1 /*
2 * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3 *
4 * This file is part of Arx Libertatis.
5 *
6 * Arx Libertatis is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Arx Libertatis is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
33 <http://www.gnu.org/licenses/>.
34
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43
44 #include "script/ScriptedIOControl.h"
45
46 #include "core/Core.h"
47 #include "game/EntityManager.h"
48 #include "game/Equipment.h"
49 #include "game/Inventory.h"
50 #include "game/Item.h"
51 #include "game/Missile.h"
52 #include "game/NPC.h"
53 #include "game/Player.h"
54 #include "graphics/Math.h"
55 #include "graphics/data/Mesh.h"
56 #include "gui/Interface.h"
57 #include "io/resource/ResourcePath.h"
58 #include "physics/Collisions.h"
59 #include "scene/Interactive.h"
60 #include "script/ScriptUtils.h"
61
62 using std::string;
63
64 extern Entity * LASTSPAWNED;
65 extern long CHANGE_LEVEL_ICON;
66
67 namespace script {
68
69 namespace {
70
71 class ReplaceMeCommand : public Command {
72
73 public:
74
ReplaceMeCommand()75 ReplaceMeCommand() : Command("replaceme", AnyEntity) { }
76
execute(Context & context)77 Result execute(Context & context) {
78
79 res::path object = res::path::load(context.getWord());
80
81 DebugScript(' ' << object);
82
83 Entity * io = context.getEntity();
84
85 res::path file;
86 if(io->ioflags & IO_NPC) {
87 file = "graph/obj3d/interactive/npc" / object;
88 } else if(io->ioflags & IO_FIX) {
89 file = "graph/obj3d/interactive/fix_inter" / object;
90 } else {
91 file = "graph/obj3d/interactive/items" / object;
92 }
93
94 Anglef last_angle = io->angle;
95 Entity * ioo = AddInteractive(file);
96 if(!ioo) {
97 return Failed;
98 }
99
100 LASTSPAWNED = ioo;
101 ioo->scriptload = 1;
102 ioo->initpos = io->initpos;
103 ioo->pos = io->pos;
104 ioo->angle = io->angle;
105 ioo->move = io->move;
106 ioo->show = io->show;
107
108 if(io == DRAGINTER) {
109 Set_DragInter(ioo);
110 }
111
112 long neww = ioo->index();
113 long oldd = io->index();
114
115 if((io->ioflags & IO_ITEM) && io->_itemdata->count > 1) {
116 io->_itemdata->count--;
117 SendInitScriptEvent(ioo);
118
119 if(playerInventory.locate(io)) {
120 giveToPlayer(ioo);
121 } else {
122 CheckForInventoryReplaceMe(ioo, io);
123 }
124 } else {
125
126 for(size_t i = 0; i < MAX_SPELLS; i++) {
127 if(spells[i].exist && spells[i].caster == oldd) {
128 spells[i].caster = neww;
129 }
130 }
131
132 io->show = SHOW_FLAG_KILLED;
133
134 InventoryPos oldPos = removeFromInventories(io);
135
136 SendInitScriptEvent(ioo);
137 ioo->angle = last_angle;
138 TREATZONE_AddIO(ioo);
139
140 // check that the init script didn't put the item anywhere
141 // if we ignore this we might create duplucate references
142 bool reInsert = true; // should the new item be inserted at the old items position?
143 if(locateInInventories(ioo)) {
144 // the init script already inserted the item into an inventory
145 reInsert = false;
146 }
147 for(int i = 0; i < MAX_EQUIPED; i++) {
148 if(player.equiped[i] != 0 && ValidIONum(player.equiped[i])) {
149 if(entities[player.equiped[i]] == ioo) {
150 // the init script was sneaky and equiped the item
151 reInsert = false;
152 }
153 }
154 }
155
156 if(reInsert) {
157 if(oldPos) {
158 insertIntoInventory(ioo, oldPos);
159 } else {
160 for(int i = 0; i < MAX_EQUIPED; i++) {
161 if(player.equiped[i] != 0 && ValidIONum(player.equiped[i])) {
162 if(entities[player.equiped[i]] == io) {
163 ARX_EQUIPMENT_UnEquip(entities.player(), io, 1);
164 ARX_EQUIPMENT_Equip(entities.player(), ioo);
165 }
166 }
167 }
168 }
169 }
170
171 if(io->scriptload) {
172 delete io;
173 return AbortRefuse;
174 } else {
175 TREATZONE_RemoveIO(io);
176 }
177
178 return AbortRefuse;
179 }
180
181 return Success;
182 }
183
184 };
185
186 class CollisionCommand : public Command {
187
188 public:
189
CollisionCommand()190 CollisionCommand() : Command("collision", AnyEntity) { }
191
execute(Context & context)192 Result execute(Context & context) {
193
194 bool choice = context.getBool();
195
196 DebugScript(' ' << choice);
197
198 Entity * io = context.getEntity();
199
200 if(!choice) {
201 io->ioflags |= IO_NO_COLLISIONS;
202 return Success;
203 }
204
205 if(io->ioflags & IO_NO_COLLISIONS) {
206
207 bool colliding = false;
208 for(size_t k = 0; k < entities.size(); k++) {
209 Entity * ioo = entities[k];
210 if(ioo && IsCollidingIO(io, ioo)) {
211 Entity * oes = EVENT_SENDER;
212 EVENT_SENDER = ioo;
213 Stack_SendIOScriptEvent(io, SM_COLLISION_ERROR_DETAIL);
214 EVENT_SENDER = oes;
215 colliding = true;
216 }
217 }
218
219 if(colliding) {
220 Entity * oes = EVENT_SENDER;
221 EVENT_SENDER = NULL;
222 Stack_SendIOScriptEvent(io, SM_COLLISION_ERROR);
223 EVENT_SENDER = oes;
224 }
225 }
226
227 io->ioflags &= ~IO_NO_COLLISIONS;
228
229 return Success;
230 }
231
232 };
233
234 class SpawnCommand : public Command {
235
236 public:
237
SpawnCommand()238 SpawnCommand() : Command("spawn") { }
239
execute(Context & context)240 Result execute(Context & context) {
241
242 string type = context.getWord();
243
244 if(type == "npc" || type == "item") {
245
246 res::path file = res::path::load(context.getWord()); // object to spawn.
247 file.remove_ext();
248
249 string target = context.getWord(); // object ident for position
250 Entity * t = entities.getById(target, context.getEntity());
251 if(!t) {
252 ScriptWarning << "unknown target: npc " << file << ' ' << target;
253 return Failed;
254 }
255
256 DebugScript(" npc " << file << ' ' << target);
257
258 if(FORBID_SCRIPT_IO_CREATION) {
259 return Failed;
260 }
261
262 if(type == "npc") {
263
264 res::path path = "graph/obj3d/interactive/npc" / file;
265
266 Entity * ioo = AddNPC(path, -1, IO_IMMEDIATELOAD);
267 if(!ioo) {
268 ScriptWarning << "failed to create npc " << path;
269 return Failed;
270 }
271
272 LASTSPAWNED = ioo;
273 ioo->scriptload = 1;
274 ioo->pos = t->pos;
275
276 ioo->angle = t->angle;
277 SendInitScriptEvent(ioo);
278
279 if(t->ioflags & IO_NPC) {
280 float dist = t->physics.cyl.radius + ioo->physics.cyl.radius + 10;
281 ioo->pos.x += -EEsin(radians(t->angle.b)) * dist;
282 ioo->pos.z += EEcos(radians(t->angle.b)) * dist;
283 }
284
285 TREATZONE_AddIO(ioo);
286
287 } else {
288
289 res::path path = "graph/obj3d/interactive/items" / file;
290
291 Entity * ioo = AddItem(path);
292 if(!ioo) {
293 ScriptWarning << "failed to create item " << path;
294 return Failed;
295 }
296
297 LASTSPAWNED = ioo;
298 ioo->scriptload = 1;
299 ioo->pos = t->pos;
300 ioo->angle = t->angle;
301 SendInitScriptEvent(ioo);
302
303 TREATZONE_AddIO(ioo);
304
305 }
306
307 } else if(type == "fireball") {
308
309 Entity * io = context.getEntity();
310 if(!io) {
311 ScriptWarning << "must be npc to spawn fireballs";
312 return Failed;
313 }
314
315 GetTargetPos(io);
316 Vec3f pos = io->pos;
317
318 if(io->ioflags & IO_NPC) {
319 pos.y -= 80.f;
320 }
321
322 ARX_MISSILES_Spawn(io, MISSILE_FIREBALL, &pos, &io->target);
323
324 } else {
325 ScriptWarning << "unexpected type: " << type;
326 return Failed;
327 }
328
329 return Success;
330 }
331
332 };
333
334 class KillMeCommand : public Command {
335
336 public:
337
KillMeCommand()338 KillMeCommand() : Command("killme", AnyEntity) { }
339
execute(Context & context)340 Result execute(Context & context) {
341
342 DebugScript("");
343
344 Entity * io = context.getEntity();
345 if((io->ioflags & IO_ITEM) && io->_itemdata->count > 1) {
346 io->_itemdata->count--;
347 } else {
348 io->show = SHOW_FLAG_KILLED;
349 io->gameFlags &= ~GFLAG_ISINTREATZONE;
350 RemoveFromAllInventories(io);
351 ARX_DAMAGES_ForceDeath(io, EVENT_SENDER);
352 }
353
354 return Success;
355 }
356
357 };
358
359 class PhysicalCommand : public Command {
360
361 public:
362
PhysicalCommand()363 PhysicalCommand() : Command("physical", AnyEntity) { }
364
execute(Context & context)365 Result execute(Context & context) {
366
367 string type = context.getWord();
368
369 Entity * io = context.getEntity();
370
371 if(type == "on") {
372 io->ioflags &= ~IO_PHYSICAL_OFF;
373 DebugScript(" on");
374
375 } else if(type == "off") {
376 io->ioflags |= IO_PHYSICAL_OFF;
377 DebugScript(" off");
378
379 } else {
380
381 float fval = context.getFloat();
382
383 DebugScript(' ' << type << ' ' << fval);
384
385 if(type == "height") {
386 io->original_height = clamp(-fval, -165.f, -30.f);
387 io->physics.cyl.height = io->original_height * io->scale;
388 } else if(type == "radius") {
389 io->original_radius = clamp(fval, 10.f, 40.f);
390 io->physics.cyl.radius = io->original_radius * io->scale;
391 } else {
392 ScriptWarning << "unknown command: " << type;
393 return Failed;
394 }
395
396 }
397
398 return Success;
399 }
400
401 };
402
403 class LinkObjToMeCommand : public Command {
404
405 public:
406
LinkObjToMeCommand()407 LinkObjToMeCommand() : Command("linkobjtome", AnyEntity) { }
408
execute(Context & context)409 Result execute(Context & context) {
410
411 string name = context.getStringVar(context.getWord());
412
413 string attach = context.getWord();
414
415 DebugScript(' ' << name << ' ' << attach);
416
417 long t = entities.getById(name);
418 if(!ValidIONum(t)) {
419 ScriptWarning << "unknown target: " << name;
420 return Failed;
421 }
422
423 LinkObjToMe(context.getEntity(), entities[t], attach);
424
425 return Success;
426 }
427
428 };
429
430 class IfExistInternalCommand : public Command {
431
432 public:
433
IfExistInternalCommand()434 IfExistInternalCommand() : Command("ifexistinternal") { }
435
execute(Context & context)436 Result execute(Context & context) {
437
438 string target = context.getWord();
439
440 DebugScript(' ' << target);
441
442 long t = entities.getById(target);
443
444 if(t == -1) {
445 context.skipStatement();
446 }
447
448 return Success;
449 }
450
451 };
452
453 class IfVisibleCommand : public Command {
454
hasVisibility(Entity * io,Entity * ioo)455 static bool hasVisibility(Entity * io, Entity * ioo) {
456
457 if(distSqr(io->pos, ioo->pos) > square(20000)) {
458 return false;
459 }
460
461 float ab = MAKEANGLE(io->angle.b);
462 float aa = getAngle(io->pos.x, io->pos.z, ioo->pos.x, ioo->pos.z);
463 aa = MAKEANGLE(degrees(aa));
464
465 if((aa < ab + 90.f) && (aa > ab - 90.f)) {
466 //font
467 return true;
468 }
469
470 return false;
471 }
472
473 public:
474
IfVisibleCommand()475 IfVisibleCommand() : Command("ifvisible", AnyEntity) { }
476
execute(Context & context)477 Result execute(Context & context) {
478
479 string target = context.getWord();
480
481 DebugScript(' ' << target);
482
483 long t = entities.getById(target);
484
485 if(!ValidIONum(t) || !hasVisibility(context.getEntity(), entities[t])) {
486 context.skipStatement();
487 }
488
489 return Success;
490 }
491
492 };
493
494 class ObjectHideCommand : public Command {
495
496 public:
497
ObjectHideCommand()498 ObjectHideCommand() : Command("objecthide") { }
499
execute(Context & context)500 Result execute(Context & context) {
501
502 bool megahide = false;
503 HandleFlags("m") {
504 megahide = test_flag(flg, 'm');
505 }
506
507 string target = context.getWord();
508 Entity * t = entities.getById(target, context.getEntity());
509
510 bool hide = context.getBool();
511
512 DebugScript(' ' << options << ' ' << target << ' ' << hide);
513
514 if(!t) {
515 return Failed;
516 }
517
518 t->gameFlags &= ~GFLAG_MEGAHIDE;
519 if(hide) {
520 if(megahide) {
521 t->gameFlags |= GFLAG_MEGAHIDE;
522 t->show = SHOW_FLAG_MEGAHIDE;
523 } else {
524 t->show = SHOW_FLAG_HIDDEN;
525 }
526 } else if(t->show == SHOW_FLAG_MEGAHIDE || t->show == SHOW_FLAG_HIDDEN) {
527 t->show = SHOW_FLAG_IN_SCENE;
528 if((t->ioflags & IO_NPC) && t->_npcdata->life <= 0.f) {
529 t->animlayer[0].cur_anim = t->anims[ANIM_DIE];
530 t->animlayer[1].cur_anim = NULL;
531 t->animlayer[2].cur_anim = NULL;
532 t->animlayer[0].ctime = 9999999;
533 }
534 }
535
536 return Success;
537 }
538
539 };
540
541 class TeleportCommand : public Command {
542
543 public:
544
TeleportCommand()545 TeleportCommand() : Command("teleport") { }
546
execute(Context & context)547 Result execute(Context & context) {
548
549 bool confirm = true;
550
551 bool teleport_player = false, initpos = false;
552 HandleFlags("alnpi") {
553
554 long angle = -1;
555
556 if(flg & flag('a')) {
557 float fangle = context.getFloat();
558 angle = static_cast<long>(fangle);
559 if(!(flg & flag('l'))) {
560 player.desiredangle.b = player.angle.b = fangle;
561 }
562 }
563
564 if(flg & flag('n')) {
565 confirm = false;
566 }
567
568 if(flg & flag('l')) {
569
570 string level = context.getWord();
571 string target = context.getWord();
572
573 strcpy(TELEPORT_TO_LEVEL, level.c_str());
574 strcpy(TELEPORT_TO_POSITION, target.c_str());
575
576 if(angle == -1) {
577 TELEPORT_TO_ANGLE = static_cast<long>(player.angle.b);
578 } else {
579 TELEPORT_TO_ANGLE = angle;
580 }
581
582 CHANGE_LEVEL_ICON = confirm ? 1 : 200;
583
584 DebugScript(' ' << options << ' ' << angle << ' ' << level << ' ' << target);
585
586 return Success;
587 }
588
589 teleport_player = test_flag(flg, 'p');
590 initpos = test_flag(flg, 'i');
591 }
592
593 string target;
594 if(!initpos) {
595 target = context.getWord();
596 }
597
598 DebugScript(' ' << options << ' ' << player.angle.b << ' ' << target);
599
600 if(target == "behind") {
601 ARX_INTERACTIVE_TeleportBehindTarget(context.getEntity());
602 return Success;
603 }
604
605 Entity * io = context.getEntity();
606 if(!teleport_player && !io) {
607 ScriptWarning << "must either use -p or use in IO context";
608 return Failed;
609 }
610
611 if(!initpos) {
612
613 Entity * t = entities.getById(target, context.getEntity());
614 if(!t) {
615 ScriptWarning << "unknown target: " << target;
616 return Failed;
617 }
618
619 Vec3f pos;
620 if(!GetItemWorldPosition(t, &pos)) {
621 ScriptWarning << "could not get world position";
622 return Failed;
623 }
624
625 if(teleport_player) {
626 ARX_INTERACTIVE_Teleport(entities.player(), &pos);
627 return Success;
628 }
629
630 if(!(io->ioflags & IO_NPC) || io->_npcdata->life > 0) {
631 if(io->show != SHOW_FLAG_HIDDEN && io->show != SHOW_FLAG_MEGAHIDE) {
632 io->show = SHOW_FLAG_IN_SCENE;
633 }
634 ARX_INTERACTIVE_Teleport(io, &pos);
635 }
636
637 } else {
638
639 if(!io) {
640 ScriptWarning << "must be in IO context to teleport -i";
641 return Failed;
642 }
643
644 if(teleport_player) {
645 Vec3f pos;
646 if(GetItemWorldPosition(io, &pos)) {
647 ARX_INTERACTIVE_Teleport(entities.player(), &pos);
648 }
649 } else if(!(io->ioflags & IO_NPC) || io->_npcdata->life > 0) {
650 if(io->show != SHOW_FLAG_HIDDEN && io->show != SHOW_FLAG_MEGAHIDE) {
651 io->show = SHOW_FLAG_IN_SCENE;
652 }
653 ARX_INTERACTIVE_Teleport(io, &io->initpos);
654 }
655 }
656
657 return Success;
658 }
659
660 };
661
662 class TargetPlayerPosCommand : public Command {
663
664 public:
665
TargetPlayerPosCommand()666 TargetPlayerPosCommand() : Command("targetplayerpos", AnyEntity) { }
667
execute(Context & context)668 Result execute(Context & context) {
669
670 DebugScript("");
671
672 context.getEntity()->targetinfo = TARGET_PLAYER;
673 GetTargetPos(context.getEntity());
674
675 return Success;
676 }
677
678 };
679
680 class DestroyCommand : public Command {
681
682 public:
683
DestroyCommand()684 DestroyCommand() : Command("destroy") { }
685
execute(Context & context)686 Result execute(Context & context) {
687
688 string target = context.getStringVar(context.getWord());
689
690 DebugScript(' ' << target);
691
692 Entity * t = entities.getById(target, context.getEntity());
693 if(!t) {
694 return Success;
695 }
696
697 bool self = (t == context.getEntity());
698
699 ARX_INTERACTIVE_DestroyIO(t);
700
701 return self ? AbortAccept : Success; // Cannot process further if we destroyed the script's IO
702 }
703
704 };
705
706 class AbstractDamageCommand : public Command {
707
708 protected:
709
AbstractDamageCommand(const string & name,long ioflags=0)710 AbstractDamageCommand(const string & name, long ioflags = 0) : Command(name, ioflags) { }
711
getDamageType(Context & context)712 DamageType getDamageType(Context & context) {
713
714 DamageType type = 0;
715 HandleFlags("fmplcgewsaornu") {
716 type |= (flg & flag('f')) ? DAMAGE_TYPE_FIRE : DamageType(0);
717 type |= (flg & flag('m')) ? DAMAGE_TYPE_MAGICAL : DamageType(0);
718 type |= (flg & flag('p')) ? DAMAGE_TYPE_POISON : DamageType(0);
719 type |= (flg & flag('l')) ? DAMAGE_TYPE_LIGHTNING : DamageType(0);
720 type |= (flg & flag('c')) ? DAMAGE_TYPE_COLD : DamageType(0);
721 type |= (flg & flag('g')) ? DAMAGE_TYPE_GAS : DamageType(0);
722 type |= (flg & flag('e')) ? DAMAGE_TYPE_METAL : DamageType(0);
723 type |= (flg & flag('w')) ? DAMAGE_TYPE_WOOD : DamageType(0);
724 type |= (flg & flag('s')) ? DAMAGE_TYPE_STONE : DamageType(0);
725 type |= (flg & flag('a')) ? DAMAGE_TYPE_ACID : DamageType(0);
726 type |= (flg & flag('o')) ? DAMAGE_TYPE_ORGANIC : DamageType(0);
727 type |= (flg & flag('r')) ? DAMAGE_TYPE_DRAIN_LIFE : DamageType(0);
728 type |= (flg & flag('n')) ? DAMAGE_TYPE_DRAIN_MANA : DamageType(0);
729 type |= (flg & flag('u')) ? DAMAGE_TYPE_PUSH : DamageType(0);
730 }
731
732 return type;
733 }
734
735 };
736
737 class DoDamageCommand : public AbstractDamageCommand {
738
739 public:
740
DoDamageCommand()741 DoDamageCommand() : AbstractDamageCommand("dodamage") { }
742
execute(Context & context)743 Result execute(Context & context) {
744
745 DamageType type = getDamageType(context);
746
747 string target = context.getWord();
748
749 float damage = context.getFloat();
750
751 DebugScript(' ' << type << ' ' << target);
752
753 Entity * t = entities.getById(target, context.getEntity());
754 if(!t) {
755 ScriptWarning << "unknown target: " << target;
756 return Failed;
757 }
758
759 long self = (context.getEntity() == NULL) ? -1 : context.getEntity()->index();
760 ARX_DAMAGES_DealDamages(t->index(), damage, self, type, &t->pos);
761
762 return Success;
763 }
764
765 };
766
767 class DamagerCommand : public AbstractDamageCommand {
768
769 public:
770
DamagerCommand()771 DamagerCommand() : AbstractDamageCommand("damager", AnyEntity) { }
772
execute(Context & context)773 Result execute(Context & context) {
774
775 Entity * io = context.getEntity();
776
777 io->damager_type = getDamageType(context) | DAMAGE_TYPE_PER_SECOND;
778
779 float damages = context.getFloat();
780
781 DebugScript(' ' << io->damager_type << damages);
782
783 io->damager_damages = checked_range_cast<short>(damages);
784
785 return Success;
786 }
787
788 };
789
790 }
791
setupScriptedIOControl()792 void setupScriptedIOControl() {
793
794 ScriptEvent::registerCommand(new ReplaceMeCommand);
795 ScriptEvent::registerCommand(new CollisionCommand);
796 ScriptEvent::registerCommand(new SpawnCommand);
797 ScriptEvent::registerCommand(new KillMeCommand);
798 ScriptEvent::registerCommand(new PhysicalCommand);
799 ScriptEvent::registerCommand(new LinkObjToMeCommand);
800 ScriptEvent::registerCommand(new IfExistInternalCommand);
801 ScriptEvent::registerCommand(new IfVisibleCommand);
802 ScriptEvent::registerCommand(new ObjectHideCommand);
803 ScriptEvent::registerCommand(new TeleportCommand);
804 ScriptEvent::registerCommand(new TargetPlayerPosCommand);
805 ScriptEvent::registerCommand(new DestroyCommand);
806 ScriptEvent::registerCommand(new DoDamageCommand);
807 ScriptEvent::registerCommand(new DamagerCommand);
808
809 }
810
811 } // namespace script
812