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