1 /*
2  * Copyright 2011-2012 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 // Code: Cyril Meynier
44 //
45 // Copyright (c) 1999 ARKANE Studios SA. All rights reserved
46 
47 #include "game/Damage.h"
48 
49 #include <cstring>
50 #include <cstdio>
51 #include <algorithm>
52 #include <limits>
53 #include <string>
54 #include <vector>
55 
56 #include "ai/Paths.h"
57 
58 #include "core/GameTime.h"
59 #include "core/Core.h"
60 
61 #include "game/EntityManager.h"
62 #include "game/Equipment.h"
63 #include "game/Inventory.h"
64 #include "game/Item.h"
65 #include "game/NPC.h"
66 #include "game/Player.h"
67 #include "game/Spells.h"
68 
69 #include "gui/Speech.h"
70 #include "gui/Interface.h"
71 
72 #include "graphics/BaseGraphicsTypes.h"
73 #include "graphics/Color.h"
74 #include "graphics/Draw.h"
75 #include "graphics/GraphicsTypes.h"
76 #include "graphics/Math.h"
77 #include "graphics/Renderer.h"
78 #include "graphics/Vertex.h"
79 #include "graphics/data/Mesh.h"
80 #include "graphics/particle/ParticleEffects.h"
81 
82 #include "io/resource/ResourcePath.h"
83 
84 #include "math/Random.h"
85 
86 #include "physics/Collisions.h"
87 
88 #include "scene/GameSound.h"
89 #include "scene/Light.h"
90 #include "scene/Interactive.h"
91 
92 #include "script/Script.h"
93 
94 class TextureContainer;
95 
96 using std::min;
97 using std::max;
98 
99 extern long REFUSE_GAME_RETURN;
100 
101 DAMAGE_INFO	damages[MAX_DAMAGES];
102 extern Vec3f PUSH_PLAYER_FORCE;
103 
104 float Blood_Pos = 0.f;
105 long Blood_Duration = 0;
ARX_DAMAGES_IgnitIO(Entity * io,float dmg)106 static void ARX_DAMAGES_IgnitIO(Entity * io, float dmg)
107 {
108 	if ((!io)
109 	        ||	(io->ioflags & IO_INVULNERABILITY))
110 		return;
111 
112 	if ((io->ignition <= 0.f) && (io->ignition + dmg > 1.f))
113 	{
114 		SendIOScriptEvent(io, SM_ENTERZONE, "cook_s");
115 	}
116 
117 	if (io->ioflags & IO_FIX) io->ignition += dmg * ( 1.0f / 10 );
118 	else if (io->ioflags & IO_ITEM) io->ignition += dmg * ( 1.0f / 8 );
119 	else if (io->ioflags & IO_NPC) io->ignition += dmg * ( 1.0f / 4 );
120 }
121 
ARX_DAMAGE_Reset_Blood_Info()122 void ARX_DAMAGE_Reset_Blood_Info()
123 {
124 	Blood_Pos = 0.f;
125 	Blood_Duration = 0;
126 }
127 
ARX_DAMAGE_Show_Hit_Blood()128 void ARX_DAMAGE_Show_Hit_Blood()
129 {
130 	Color color;
131 	static float Last_Blood_Pos = 0.f;
132 	static long duration;
133 
134 	if (Blood_Pos > 2.f) // end of blood flash
135 	{
136 		Blood_Pos = 0.f;
137 		duration = 0;
138 	}
139 	else if (Blood_Pos > 1.f)
140 	{
141 		GRenderer->SetBlendFunc(Renderer::BlendZero, Renderer::BlendSrcColor);
142 		GRenderer->SetRenderState(Renderer::AlphaBlending, true);
143 		GRenderer->SetRenderState(Renderer::DepthWrite, false);
144 
145 		if (player.poison > 1.f)
146 			color = Color3f(Blood_Pos - 1.f, 1.f, Blood_Pos - 1.f).to<u8>();
147 		else
148 			color = Color3f(1.f, Blood_Pos - 1.f, Blood_Pos - 1.f).to<u8>();
149 
150 		EERIEDrawBitmap(0.f, 0.f, (float)DANAESIZX, (float)DANAESIZY, 0.00009f, NULL, color);
151 		GRenderer->SetRenderState(Renderer::DepthWrite, true);
152 		GRenderer->SetRenderState(Renderer::AlphaBlending, false);
153 	}
154 	else if (Blood_Pos > 0.f)
155 	{
156 		GRenderer->SetBlendFunc(Renderer::BlendZero, Renderer::BlendSrcColor);
157 		GRenderer->SetRenderState(Renderer::AlphaBlending, true);
158 		GRenderer->SetRenderState(Renderer::DepthWrite, false);
159 
160 		if (player.poison > 1.f)
161 			color = Color3f(1.f - Blood_Pos, 1.f, 1.f - Blood_Pos).to<u8>();
162 		else
163 			color = Color3f(1.f, 1.f - Blood_Pos, 1.f - Blood_Pos).to<u8>();
164 
165 		EERIEDrawBitmap(0.f, 0.f, (float)DANAESIZX, (float)DANAESIZY, 0.00009f, NULL, color);
166 		GRenderer->SetRenderState(Renderer::AlphaBlending, false);
167 		GRenderer->SetRenderState(Renderer::DepthWrite, true);
168 	}
169 
170 	if (Blood_Pos > 0.f)
171 	{
172 		if (Blood_Pos > 1.f)
173 		{
174 			if ((Last_Blood_Pos <= 1.f))
175 			{
176 				Blood_Pos = 1.0001f;
177 				duration = 0;
178 			}
179 
180 			if (duration > Blood_Duration)
181 				Blood_Pos += (float)FrameDiff * ( 1.0f / 300 );
182 
183 			duration += static_cast<long>(FrameDiff);
184 		}
185 		else Blood_Pos += (float)FrameDiff * ( 1.0f / 40 );
186 	}
187 
188 	Last_Blood_Pos = Blood_Pos;
189 }
190 
191 //*************************************************************************************
192 //*************************************************************************************
ARX_DAMAGES_DamagePlayer(float dmg,DamageType type,long source)193 float ARX_DAMAGES_DamagePlayer(float dmg, DamageType type, long source) {
194 	if (player.playerflags & PLAYERFLAGS_INVULNERABILITY)
195 		return 0;
196 
197 	float damagesdone = 0.f;
198 
199 	if (player.life == 0.f) return damagesdone;
200 
201 	if (dmg > player.life) damagesdone = dmg;
202 	else damagesdone = player.life;
203 
204 	entities.player()->dmg_sum += dmg;
205 
206 	if (float(arxtime) > entities.player()->ouch_time + 500)
207 	{
208 		Entity * oes = EVENT_SENDER;
209 
210 		if (ValidIONum(source))
211 			EVENT_SENDER = entities[source];
212 		else
213 			EVENT_SENDER = NULL;
214 
215 		entities.player()->ouch_time = (unsigned long)(arxtime);
216 		char tex[32];
217 		sprintf(tex, "%5.2f", entities.player()->dmg_sum);
218 		SendIOScriptEvent( entities.player(), SM_OUCH, tex );
219 		EVENT_SENDER = oes;
220 		float power = entities.player()->dmg_sum / player.maxlife * 220.f;
221 		AddQuakeFX(power * 3.5f, 500 + power * 3, rnd() * 100.f + power + 200, 0);
222 		entities.player()->dmg_sum = 0.f;
223 	}
224 
225 	if (dmg > 0.f)
226 	{
227 		if (ValidIONum(source))
228 		{
229 			Entity * pio = NULL;
230 
231 			if (entities[source]->ioflags & IO_NPC)
232 			{
233 				pio = entities[source]->_npcdata->weapon;
234 
235 				if ((pio) && ((pio->poisonous == 0) || (pio->poisonous_count == 0)))
236 					pio = NULL;
237 			}
238 
239 			if (!pio) pio = entities[source];
240 
241 			if (pio && pio->poisonous && (pio->poisonous_count != 0))
242 			{
243 				if (rnd() * 100.f > player.resist_poison)
244 				{
245 					player.poison += pio->poisonous;
246 				}
247 
248 				if (pio->poisonous_count != -1)
249 					pio->poisonous_count--;
250 			}
251 		}
252 
253 		long alive;
254 
255 		if (player.life > 0) alive = 1;
256 		else alive = 0;
257 
258 		if (!BLOCK_PLAYER_CONTROLS)
259 			player.life -= dmg;
260 
261 		if (player.life <= 0.f)
262 		{
263 			player.life = 0.f;
264 
265 			if (alive)
266 			{
267 				REFUSE_GAME_RETURN = 1;
268 				ARX_PLAYER_BecomesDead();
269 
270 				if ((type & DAMAGE_TYPE_FIRE) || (type & DAMAGE_TYPE_FAKEFIRE))
271 				{
272 					ARX_SOUND_PlayInterface(SND_PLAYER_DEATH_BY_FIRE);
273 				}
274 
275 				SendIOScriptEvent(entities.player(), SM_DIE);
276 
277 				for(size_t i = 1; i < entities.size(); i++) {
278 					Entity * ioo = entities[i];
279 					if(ioo && (ioo->ioflags & IO_NPC)) {
280 						if(ioo->targetinfo == TARGET_PLAYER) {
281 							EVENT_SENDER = entities.player();
282 							std::string killer;
283 							if(source == 0) {
284 								killer = "player";
285 							} else if(source <= -1) {
286 								killer = "none";
287 							} else if(ValidIONum(source)) {
288 								killer = entities[source]->long_name();
289 							}
290 							SendIOScriptEvent(entities[i], SM_NULL, killer, "target_death");
291 						}
292 					}
293 				}
294 			}
295 		}
296 
297 		if (player.maxlife <= 0.f) return damagesdone;
298 
299 		float t = dmg / player.maxlife;
300 
301 		if (Blood_Pos == 0.f) {
302 			Blood_Pos = 0.000001f;
303 			Blood_Duration = 100 + (t * 200.f);
304 		} else {
305 			long temp = t * 800.f;
306 			Blood_Duration += temp;
307 		}
308 	}
309 
310 	// revient les barres
311 	ResetPlayerInterface();
312 
313 	return damagesdone;
314 }
315 
ARX_DAMAGES_HealPlayer(float dmg)316 void ARX_DAMAGES_HealPlayer(float dmg)
317 {
318 	if (player.life == 0.f) return;
319 
320 	if (dmg > 0.f)
321 	{
322 		if (!BLOCK_PLAYER_CONTROLS)
323 			player.life += dmg;
324 
325 		if (player.life > player.Full_maxlife) player.life = player.Full_maxlife;
326 	}
327 }
ARX_DAMAGES_HealInter(Entity * io,float dmg)328 void ARX_DAMAGES_HealInter(Entity * io, float dmg)
329 {
330 	if (io == NULL) return;
331 
332 	if (!(io->ioflags & IO_NPC)) return;
333 
334 	if (io->_npcdata->life <= 0.f) return;
335 
336 	if (io == entities.player()) ARX_DAMAGES_HealPlayer(dmg);
337 
338 	if (dmg > 0.f)
339 	{
340 		io->_npcdata->life += dmg;
341 
342 		if (io->_npcdata->life > io->_npcdata->maxlife) io->_npcdata->life = io->_npcdata->maxlife;
343 	}
344 }
ARX_DAMAGES_HealManaPlayer(float dmg)345 void ARX_DAMAGES_HealManaPlayer(float dmg)
346 {
347 	if (player.life == 0.f) return;
348 
349 	if (dmg > 0.f)
350 	{
351 		player.mana += dmg;
352 
353 		if (player.mana > player.Full_maxmana) player.mana = player.Full_maxmana;
354 	}
355 }
ARX_DAMAGES_HealManaInter(Entity * io,float dmg)356 void ARX_DAMAGES_HealManaInter(Entity * io, float dmg)
357 {
358 	if (io == NULL) return;
359 
360 	if (!(io->ioflags & IO_NPC)) return;
361 
362 	if (io == entities.player()) ARX_DAMAGES_HealManaPlayer(dmg);
363 
364 	if (io->_npcdata->life <= 0.f) return;
365 
366 	if (dmg > 0.f)
367 	{
368 		io->_npcdata->mana += dmg;
369 
370 		if (io->_npcdata->mana > io->_npcdata->maxmana) io->_npcdata->mana = io->_npcdata->maxmana;
371 	}
372 }
ARX_DAMAGES_DrainMana(Entity * io,float dmg)373 float ARX_DAMAGES_DrainMana(Entity * io, float dmg)
374 {
375 	if (io == NULL) return 0;
376 
377 	if (!(io->ioflags & IO_NPC)) return 0;
378 
379 	if (io == entities.player())
380 	{
381 		if (player.playerflags & PLAYERFLAGS_NO_MANA_DRAIN)
382 			return 0;
383 
384 		if (player.mana >= dmg)
385 		{
386 			player.mana -= dmg;
387 			return dmg;
388 		}
389 
390 		float d = player.mana;
391 		player.mana = 0;
392 		return d;
393 	}
394 
395 	if (io->_npcdata->mana >= dmg)
396 	{
397 		io->_npcdata->mana -= dmg;
398 		return dmg;
399 	}
400 
401 	float d = io->_npcdata->mana;
402 	io->_npcdata->mana = 0;
403 	return d;
404 }
405 //*************************************************************************************
406 //*************************************************************************************
ARX_DAMAGES_DamageFIX(Entity * io,float dmg,long source,long flags)407 void ARX_DAMAGES_DamageFIX(Entity * io, float dmg, long source, long flags)
408 {
409 	if ((!io)
410 	        ||	(!io->show)
411 	        ||	(!(io->ioflags & IO_FIX))
412 	        ||	(io->ioflags & IO_INVULNERABILITY)
413 	        ||	(!io->script.data))
414 		return;
415 
416 	io->dmg_sum += dmg;
417 
418 	if (ValidIONum(source))
419 		EVENT_SENDER = entities[source];
420 	else
421 		EVENT_SENDER = NULL;
422 
423 	if (float(arxtime) > io->ouch_time + 500)
424 	{
425 		io->ouch_time = (unsigned long)(arxtime);
426 		char tex[32];
427 		sprintf(tex, "%5.2f", io->dmg_sum);
428 		SendIOScriptEvent(io, SM_OUCH, tex);
429 		io->dmg_sum = 0.f;
430 	}
431 
432 	if (rnd() * 100.f > io->durability) io->durability -= dmg * ( 1.0f / 2 ); //1.f;
433 
434 	if (io->durability <= 0.f)
435 	{
436 		io->durability = 0.f;
437 		SendIOScriptEvent(io, SM_BREAK);
438 	}
439 	else
440 	{
441 		char dmm[32];
442 
443 		if (EVENT_SENDER == entities.player())
444 		{
445 			if (flags & 1)
446 			{
447 				sprintf(dmm, "%f spell", dmg);
448 			}
449 			else switch	(ARX_EQUIPMENT_GetPlayerWeaponType())
450 				{
451 					case WEAPON_BARE:
452 						sprintf(dmm, "%f bare", dmg);
453 						break;
454 					case WEAPON_DAGGER:
455 						sprintf(dmm, "%f dagger", dmg);
456 						break;
457 					case WEAPON_1H:
458 						sprintf(dmm, "%f 1h", dmg);
459 						break;
460 					case WEAPON_2H:
461 						sprintf(dmm, "%f 2h", dmg);
462 						break;
463 					case WEAPON_BOW:
464 						sprintf(dmm, "%f arrow", dmg);
465 						break;
466 					default:
467 						sprintf(dmm, "%f", dmg);
468 						break;
469 				}
470 		}
471 		else sprintf(dmm, "%f", dmg);
472 
473 		if (SendIOScriptEvent(io, SM_HIT, dmm) != ACCEPT) return;
474 	}
475 }
476 
477 extern Entity * FlyingOverIO;
478 extern MASTER_CAMERA_STRUCT MasterCamera;
479 
ARX_DAMAGES_ForceDeath(Entity * io_dead,Entity * io_killer)480 void ARX_DAMAGES_ForceDeath(Entity * io_dead, Entity * io_killer) {
481 
482 	if(io_dead->mainevent == "dead") {
483 		return;
484 	}
485 
486 	Entity * old_sender = EVENT_SENDER;
487 	EVENT_SENDER = io_killer;
488 
489 	if (io_dead == DRAGINTER)
490 		Set_DragInter(NULL);
491 
492 	if (io_dead == FlyingOverIO)
493 		FlyingOverIO = NULL;
494 
495 	if ((MasterCamera.exist & 1) && (MasterCamera.io == io_dead))
496 		MasterCamera.exist = 0;
497 
498 	if ((MasterCamera.exist & 2) && (MasterCamera.want_io == io_dead))
499 		MasterCamera.exist = 0;
500 
501 	//Kill all speeches
502 	if (ValidDynLight(io_dead->dynlight))
503 		DynLight[io_dead->dynlight].exist = 0;
504 
505 	io_dead->dynlight = -1;
506 
507 	if (ValidDynLight(io_dead->halo.dynlight))
508 		DynLight[io_dead->halo.dynlight].exist = 0;
509 
510 	io_dead->halo.dynlight = -1;
511 	ARX_NPC_Behaviour_Reset(io_dead);
512 
513 	ARX_SPEECH_ReleaseIOSpeech(io_dead);
514 
515 	//Kill all Timers...
516 	ARX_SCRIPT_Timer_Clear_By_IO(io_dead);
517 
518 	if(io_dead->mainevent != "dead") {
519 		if(SendIOScriptEvent(io_dead, SM_DIE) != REFUSE && ValidIOAddress(io_dead)) {
520 			io_dead->infracolor = Color3f::blue;
521 		}
522 	}
523 
524 	if (!ValidIOAddress(io_dead))
525 		return;
526 
527 	ARX_SCRIPT_SetMainEvent(io_dead, "dead");
528 
529 	if(fartherThan(io_dead->pos, ACTIVECAM->pos, 3200.f)) {
530 		io_dead->animlayer[0].ctime = 9999999;
531 		io_dead->lastanimtime = 0;
532 	}
533 
534 	std::string killer;
535 
536 	if (io_dead->ioflags & IO_NPC)
537 		io_dead->_npcdata->weaponinhand = 0;
538 
539 	ARX_INTERACTIVE_DestroyDynamicInfo(io_dead);
540 
541 	if (io_killer == entities.player()) killer = "player";
542 	else
543 	{
544 		if (io_killer)
545 			killer = io_killer->long_name();
546 	}
547 
548 	for(size_t i = 1; i < entities.size(); i++) {
549 		Entity * ioo = entities[i];
550 
551 		if (ioo == io_dead)
552 			continue;
553 
554 		if ((ioo) && (ioo->ioflags & IO_NPC))
555 		{
556 			if (ValidIONum(ioo->targetinfo))
557 				if (entities[ioo->targetinfo] == io_dead)
558 				{
559 					EVENT_SENDER = io_dead;
560 					Stack_SendIOScriptEvent(entities[i], SM_NULL, killer, "target_death");
561 					ioo->targetinfo = TARGET_NONE;
562 					ioo->_npcdata->reachedtarget = 0;
563 				}
564 
565 			if (ValidIONum(ioo->_npcdata->pathfind.truetarget))
566 				if (entities[ioo->_npcdata->pathfind.truetarget] == io_dead)
567 				{
568 					EVENT_SENDER = io_dead;
569 					Stack_SendIOScriptEvent(entities[i], SM_NULL, killer, "target_death");
570 					ioo->_npcdata->pathfind.truetarget = TARGET_NONE;
571 					ioo->_npcdata->reachedtarget = 0;
572 				}
573 		}
574 	}
575 
576 	IO_UnlinkAllLinkedObjects(io_dead);
577 	io_dead->animlayer[1].cur_anim = NULL;
578 	io_dead->animlayer[2].cur_anim = NULL;
579 	io_dead->animlayer[3].cur_anim = NULL;
580 
581 	if (io_dead->ioflags & IO_NPC)
582 	{
583 		io_dead->_npcdata->life = 0;
584 
585 		if(io_dead->_npcdata->weapon) {
586 			Entity * ioo = io_dead->_npcdata->weapon;
587 			if(ValidIOAddress(ioo)) {
588 				ioo->show = SHOW_FLAG_IN_SCENE;
589 				ioo->ioflags |= IO_NO_NPC_COLLIDE;
590 				ioo->pos = ioo->obj->vertexlist3[ioo->obj->origin].v;
591 				ioo->velocity = Vec3f(0.f, 13.f, 0.f);
592 				ioo->stopped = 0;
593 			}
594 		}
595 	}
596 
597 	EVENT_SENDER = old_sender;
598 }
599 
ARX_DAMAGES_PushIO(Entity * io_target,long source,float power)600 void ARX_DAMAGES_PushIO(Entity * io_target, long source, float power)
601 {
602 	if ((power > 0.f)
603 	        &&	(ValidIONum(source)))
604 	{
605 		power *= ( 1.0f / 20 );
606 		Entity * io = entities[source];
607 		Vec3f vect = io_target->pos - io->pos;
608 		fnormalize(vect);
609 		vect *= power;
610 		if(io_target == entities.player()) {
611 			PUSH_PLAYER_FORCE = vect; // TODO why not +=?
612 		} else {
613 			io_target->move += vect;
614 		}
615 	}
616 }
617 
ARX_DAMAGES_DealDamages(long target,float dmg,long source,DamageType flags,Vec3f * pos)618 float ARX_DAMAGES_DealDamages(long target, float dmg, long source, DamageType flags, Vec3f * pos)
619 {
620 	if ((!ValidIONum(target))
621 	        ||	(!ValidIONum(source)))
622 		return 0;
623 
624 	Entity * io_target = entities[target];
625 	Entity * io_source = entities[source];
626 	float damagesdone;
627 
628 	if (flags & DAMAGE_TYPE_PER_SECOND)
629 	{
630 		dmg = dmg * framedelay * ( 1.0f / 1000 );
631 	}
632 
633 	if (target == 0)
634 	{
635 		if (flags & DAMAGE_TYPE_POISON)
636 		{
637 			if (rnd() * 100.f > player.resist_poison)
638 			{
639 				damagesdone = dmg;
640 				player.poison += damagesdone;
641 			}
642 			else damagesdone = 0;
643 
644 			goto dodamage;
645 		}
646 
647 		if (flags & DAMAGE_TYPE_DRAIN_MANA)
648 			damagesdone = ARX_DAMAGES_DrainMana(io_target, dmg);
649 		else
650 		{
651 			ARX_DAMAGES_DamagePlayerEquipment(dmg);
652 			damagesdone = ARX_DAMAGES_DamagePlayer(dmg, flags, source);
653 		}
654 
655 	dodamage:
656 		;
657 
658 		if (flags & DAMAGE_TYPE_FIRE)
659 		{
660 			ARX_DAMAGES_IgnitIO(io_target, damagesdone);
661 		}
662 
663 		if (flags & DAMAGE_TYPE_DRAIN_LIFE)
664 		{
665 			ARX_DAMAGES_HealInter(io_source, damagesdone);
666 		}
667 
668 		if (flags & DAMAGE_TYPE_DRAIN_MANA)
669 		{
670 			ARX_DAMAGES_HealManaInter(io_source, damagesdone);
671 		}
672 
673 		if (flags & DAMAGE_TYPE_PUSH)
674 		{
675 			ARX_DAMAGES_PushIO(io_target, source, damagesdone * ( 1.0f / 2 ));
676 		}
677 
678 		if ((flags & DAMAGE_TYPE_MAGICAL)
679 		        && !(flags & (DAMAGE_TYPE_FIRE | DAMAGE_TYPE_COLD)))
680 		{
681 			damagesdone -= player.Full_resist_magic * ( 1.0f / 100 ) * damagesdone;
682 			damagesdone = max(0.0f, damagesdone);
683 		}
684 
685 		return damagesdone;
686 	}
687 	else
688 	{
689 		if (io_target->ioflags & IO_NPC)
690 		{
691 			if (flags & DAMAGE_TYPE_POISON)
692 			{
693 				if (rnd() * 100.f > io_target->_npcdata->resist_poison)
694 				{
695 					damagesdone = dmg;
696 					io_target->_npcdata->poisonned += damagesdone;
697 				}
698 				else damagesdone = 0;
699 
700 				goto dodamage2;
701 			}
702 
703 			if (flags & DAMAGE_TYPE_FIRE)
704 			{
705 				if (rnd() * 100.f <= io_target->_npcdata->resist_fire)
706 					dmg = 0;
707 
708 				ARX_DAMAGES_IgnitIO(io_target, dmg);
709 			}
710 
711 			if (flags & DAMAGE_TYPE_DRAIN_MANA)
712 			{
713 				damagesdone = ARX_DAMAGES_DrainMana(io_target, dmg);
714 			}
715 			else damagesdone = ARX_DAMAGES_DamageNPC(io_target, dmg, source, 1, pos);
716 
717 		dodamage2:
718 			;
719 
720 			if (flags & DAMAGE_TYPE_DRAIN_LIFE)
721 			{
722 				ARX_DAMAGES_HealInter(io_source, damagesdone);
723 			}
724 
725 			if (flags & DAMAGE_TYPE_DRAIN_MANA)
726 			{
727 				ARX_DAMAGES_HealManaInter(io_source, damagesdone);
728 			}
729 
730 			if (flags & DAMAGE_TYPE_PUSH)
731 			{
732 				ARX_DAMAGES_PushIO(io_target, source, damagesdone * ( 1.0f / 2 ));
733 			}
734 
735 			if ((flags & DAMAGE_TYPE_MAGICAL)
736 			        && !(flags & (DAMAGE_TYPE_FIRE | DAMAGE_TYPE_COLD)))
737 			{
738 				damagesdone -= io_target->_npcdata->resist_magic * ( 1.0f / 100 ) * damagesdone;
739 				damagesdone = max(0.0f, damagesdone);
740 			}
741 
742 			return damagesdone;
743 		}
744 	}
745 
746 	return 0;
747 }
748 
749 extern bool bHitFlash;
750 extern float fHitFlash;
751 extern unsigned long ulHitFlash;
752 
753 //*************************************************************************************
754 // flags & 1 == spell damage
755 //*************************************************************************************
ARX_DAMAGES_DamageNPC(Entity * io,float dmg,long source,long flags,Vec3f * pos)756 float ARX_DAMAGES_DamageNPC(Entity * io, float dmg, long source, long flags, Vec3f * pos) {
757 
758 	if ((!io)
759 	        ||	(!io->show)
760 	        ||	(io->ioflags & IO_INVULNERABILITY)
761 	        ||	(!(io->ioflags & IO_NPC)))
762 		return 0.f;
763 
764 	float damagesdone = 0.f;
765 
766 	if (io->_npcdata->life <= 0.f)
767 	{
768 		if ((source != 0)
769 		        ||	((source == 0) &&	(player.equiped[EQUIP_SLOT_WEAPON] > 0)))
770 		{
771 			if ((dmg >= io->_npcdata->maxlife * 0.4f) && pos)
772 				ARX_NPC_TryToCutSomething(io, pos);
773 
774 			return damagesdone;
775 		}
776 
777 		return damagesdone;
778 	}
779 
780 	io->dmg_sum += dmg;
781 
782 	if (float(arxtime) > io->ouch_time + 500)
783 	{
784 		if (ValidIONum(source))
785 		{
786 			EVENT_SENDER = entities[source];
787 
788 		}
789 		else
790 			EVENT_SENDER = NULL;
791 
792 		io->ouch_time = (unsigned long)(arxtime);
793 		char tex[32];
794 
795 		if (EVENT_SENDER && (EVENT_SENDER->summoner == 0))
796 		{
797 			EVENT_SENDER = entities.player();
798 			sprintf(tex, "%5.2f summoned", io->dmg_sum);
799 		}
800 		else
801 			sprintf(tex, "%5.2f", io->dmg_sum);
802 
803 		SendIOScriptEvent(io, SM_OUCH, tex);
804 		io->dmg_sum = 0.f;
805 		long n = ARX_SPELLS_GetSpellOn(io, SPELL_CONFUSE);
806 
807 		if (n >= 0)
808 		{
809 			spells[n].tolive = 0;
810 		}
811 	}
812 
813 	if (dmg >= 0.f)
814 	{
815 		if (ValidIONum(source))
816 		{
817 			Entity * pio = NULL;
818 
819 			if (source == 0)
820 			{
821 				if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
822 				        &&	ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
823 				{
824 					pio = entities[player.equiped[EQUIP_SLOT_WEAPON]];
825 
826 					if((pio && (pio->poisonous == 0 || pio->poisonous_count == 0)) || (flags & 1)) {
827 						pio = NULL;
828 					}
829 				}
830 			} else {
831 				if(entities[source]->ioflags & IO_NPC) {
832 					pio = entities[source]->_npcdata->weapon;
833 					if(pio && (pio->poisonous == 0 || pio->poisonous_count == 0)) {
834 						pio = NULL;
835 					}
836 				}
837 			}
838 
839 			if (!pio) pio = entities[source];
840 
841 			if (pio && pio->poisonous && (pio->poisonous_count != 0))
842 			{
843 				if (rnd() * 100.f > io->_npcdata->resist_poison)
844 				{
845 					io->_npcdata->poisonned += pio->poisonous;
846 				}
847 
848 				if (pio->poisonous_count != -1)
849 					pio->poisonous_count--;
850 			}
851 		}
852 
853 		if (io->script.data != NULL)
854 		{
855 			if (source >= 0)
856 			{
857 				if (ValidIONum(source))
858 					EVENT_SENDER = entities[source];
859 				else
860 					EVENT_SENDER = NULL;
861 
862 				char dmm[256];
863 
864 				if (EVENT_SENDER == entities.player())
865 				{
866 					if (flags & 1)
867 					{
868 						sprintf(dmm, "%f spell", dmg);
869 					}
870 					else switch	(ARX_EQUIPMENT_GetPlayerWeaponType())
871 						{
872 							case WEAPON_BARE:
873 								sprintf(dmm, "%f bare", dmg);
874 								break;
875 							case WEAPON_DAGGER:
876 								sprintf(dmm, "%f dagger", dmg);
877 								break;
878 							case WEAPON_1H:
879 								sprintf(dmm, "%f 1h", dmg);
880 								break;
881 							case WEAPON_2H:
882 								sprintf(dmm, "%f 2h", dmg);
883 								break;
884 							case WEAPON_BOW:
885 								sprintf(dmm, "%f arrow", dmg);
886 								break;
887 							default:
888 								sprintf(dmm, "%f", dmg);
889 								break;
890 						}
891 				}
892 				else sprintf(dmm, "%f", dmg);
893 
894 				if ((EVENT_SENDER)
895 				        &&	(EVENT_SENDER->summoner == 0))
896 				{
897 					EVENT_SENDER = entities.player();
898 					sprintf(dmm, "%f summoned", dmg);
899 				}
900 
901 				if (SendIOScriptEvent(io, SM_HIT, dmm) != ACCEPT) return damagesdone;
902 
903 			}
904 		}
905 
906 		damagesdone = min(dmg, io->_npcdata->life);
907 		io->_npcdata->life -= dmg;
908 
909 		bHitFlash = true;
910 
911 		if (io->_npcdata->life <= 0)
912 		{
913 			fHitFlash = 0;
914 		}
915 		else
916 		{
917 			fHitFlash = io->_npcdata->life / io->_npcdata->maxlife;
918 		}
919 
920 		ulHitFlash = 0;
921 
922 		if (io->_npcdata->life <= 0.f)
923 		{
924 			io->_npcdata->life = 0.f;
925 
926 			if ((source != 0)
927 			        ||	((source == 0) &&	(player.equiped[EQUIP_SLOT_WEAPON] > 0)))
928 			{
929 				if ((dmg >= io->_npcdata->maxlife * ( 1.0f / 2 )) && pos)
930 					ARX_NPC_TryToCutSomething(io, pos);
931 			}
932 
933 			if (ValidIONum(source))
934 			{
935 				long xp = io->_npcdata->xpvalue;
936 				ARX_DAMAGES_ForceDeath(io, entities[source]);
937 
938 				if (source == 0)
939 					ARX_PLAYER_Modify_XP(xp);
940 			}
941 			else ARX_DAMAGES_ForceDeath(io, NULL);
942 		}
943 	}
944 
945 	return damagesdone;
946 }
947 
948 //*************************************************************************************
949 //*************************************************************************************
ARX_DAMAGES_Reset()950 void ARX_DAMAGES_Reset()
951 {
952 	memset(damages, 0, sizeof(DAMAGE_INFO)*MAX_DAMAGES);
953 }
954 
955 //*************************************************************************************
956 //*************************************************************************************
ARX_DAMAGES_GetFree()957 long ARX_DAMAGES_GetFree()
958 {
959 	for (size_t i = 0; i < MAX_DAMAGES; i++)
960 	{
961 		if (!damages[i].exist)
962 		{
963 			damages[i].radius = 100.f;
964 			damages[i].start_time = (unsigned long)(arxtime);
965 			damages[i].duration = 1000;
966 			damages[i].area = DAMAGE_AREA;
967 			damages[i].flags = 0;
968 			damages[i].type = 0;
969 			damages[i].special = 0;
970 			damages[i].special_ID = 0;
971 			damages[i].lastupd = 0;
972 			damages[i].active = 1;
973 
974 			for (long j = 0; j < 10; j++)
975 				damages[i].except[j] = -1;
976 
977 			return i;
978 		}
979 	}
980 
981 	return -1;
982 }
InExceptList(long dmg,long num)983 long InExceptList(long dmg, long num)
984 {
985 	for (long j = 0; j < 10; j++)
986 		if (damages[dmg].except[j] == num) return 1;
987 
988 	return 0;
989 }
990 
ARX_DAMAGES_AddVisual(DAMAGE_INFO * di,Vec3f * pos,float dmg,Entity * io)991 void ARX_DAMAGES_AddVisual(DAMAGE_INFO * di, Vec3f * pos, float dmg, Entity * io) {
992 
993 	if(!(di->type & DAMAGE_TYPE_FAKEFIRE)) {
994 		return;
995 	}
996 
997 	long num = -1;
998 	if(io) {
999 		num = Random::get(0, io->obj->vertexlist.size() / 4 - 1) * 4 + 1;
1000 	}
1001 
1002 	unsigned long tim = (unsigned long)(arxtime);
1003 	if(di->lastupd + 200 < tim) {
1004 		di->lastupd = tim;
1005 		if(di->type & DAMAGE_TYPE_MAGICAL) {
1006 			ARX_SOUND_PlaySFX(SND_SPELL_MAGICAL_HIT, pos, 0.8F + 0.4F * rnd());
1007 		} else {
1008 			ARX_SOUND_PlaySFX(SND_SPELL_FIRE_HIT, pos, 0.8F + 0.4F * rnd());
1009 		}
1010 	}
1011 
1012 	for(long k = 0 ; k < 14 ; k++) {
1013 
1014 		PARTICLE_DEF * pd = createParticle();
1015 		if(!pd) {
1016 			break;
1017 		}
1018 
1019 		if(io) {
1020 			arx_assert(num >= 0);
1021 			pd->ov = io->obj->vertexlist3[num].v + randomVec(-5.f, 5.f);
1022 		} else {
1023 			pd->ov = *pos + randomVec(-50.f, 50.f);
1024 		}
1025 		pd->siz = clamp(dmg, 5.f, 15.f);
1026 		pd->scale = Vec3f::repeat(-10.f);
1027 		pd->special = ROTATING | MODULATE_ROTATION | FIRE_TO_SMOKE;
1028 		pd->tolive = Random::get(500, 900);
1029 		pd->move = Vec3f(1.f - 2.f * rnd(), 2.f - 16.f * rnd(), 1.f - 2.f * rnd());
1030 		if(di->type & DAMAGE_TYPE_MAGICAL) {
1031 			pd->rgb = Color3f(0.3f, 0.3f, 0.8f);
1032 		} else {
1033 			pd->rgb = Color3f::gray(0.5f);
1034 		}
1035 		pd->tc = TC_fire2;
1036 		pd->fparam = 0.1f - rnd() * 0.2f;
1037 	}
1038 }
1039 
1040 // source = -1 no source but valid pos
1041 // source = 0  player
1042 // source > 0  IO
ARX_DAMAGES_UpdateDamage(long j,float tim)1043 void ARX_DAMAGES_UpdateDamage(long j, float tim) {
1044 
1045 	Vec3f sub;
1046 
1047 	if (damages[j].exist)
1048 	{
1049 		if (!damages[j].active) return;
1050 
1051 		if(damages[j].flags & DAMAGE_FLAG_FOLLOW_SOURCE) {
1052 			if(damages[j].source == 0) {
1053 				damages[j].pos = player.pos;
1054 			} else if (ValidIONum(damages[j].source)) {
1055 				damages[j].pos = entities[damages[j].source]->pos;
1056 			}
1057 		}
1058 
1059 		float dmg;
1060 		if (damages[j].flags & DAMAGE_NOT_FRAME_DEPENDANT)
1061 			dmg = damages[j].damages;
1062 		else if (damages[j].duration == -1) dmg = damages[j].damages;
1063 		else
1064 		{
1065 			float FD = (float)FrameDiff;
1066 
1067 			if (tim > damages[j].start_time + damages[j].duration)
1068 				FD -= damages[j].start_time + damages[j].duration - tim;
1069 
1070 			dmg = damages[j].damages * FD * ( 1.0f / 1000 );
1071 		}
1072 
1073 		long validsource = ValidIONum(damages[j].source);
1074 		float divradius = 1.f / damages[j].radius;
1075 
1076 		// checking for IO damages
1077 		for(size_t i = 0; i < entities.size(); i++) {
1078 			Entity * io = entities[i];
1079 
1080 			if ((io)
1081 			        &&	(io->gameFlags & GFLAG_ISINTREATZONE)
1082 			        &&	(io->show == SHOW_FLAG_IN_SCENE)
1083 			        &&	(!InExceptList(j, i))
1084 			        && ( (damages[j].source != long(i))
1085 			            || ((damages[j].source == long(i))
1086 			                && (!(damages[j].flags & DAMAGE_FLAG_DONT_HURT_SOURCE))))
1087 			   )
1088 			{
1089 				if (io->ioflags & IO_NPC)
1090 				{
1091 					if ((i != 0) && (damages[j].source != 0)
1092 					        && validsource && (HaveCommonGroup(io, entities[damages[j].source])))
1093 						continue;
1094 
1095 					EERIE_SPHERE sphere;
1096 					sphere.origin = damages[j].pos;
1097 					sphere.radius = damages[j].radius - 10.f;
1098 
1099 					if (CheckIOInSphere(&sphere, i, true))
1100 					{
1101 						sub.x = io->pos.x;
1102 						sub.y = io->pos.y - 60.f;
1103 						sub.z = io->pos.z;
1104 						float dist = fdist(damages[j].pos, sub);
1105 
1106 						if (damages[j].type & DAMAGE_TYPE_FIELD)
1107 						{
1108 							if (float(arxtime) > io->collide_door_time + 500)
1109 							{
1110 								EVENT_SENDER = NULL;
1111 								io->collide_door_time = (unsigned long)(arxtime);
1112 								char param[64];
1113 								param[0] = 0;
1114 
1115 								if (damages[j].type & DAMAGE_TYPE_FIRE)
1116 									strcpy(param, "fire");
1117 
1118 								if (damages[j].type & DAMAGE_TYPE_COLD)
1119 									strcpy(param, "cold");
1120 
1121 								SendIOScriptEvent(io, SM_COLLIDE_FIELD, param);
1122 
1123 							}
1124 						}
1125 
1126 						switch (damages[j].area)
1127 						{
1128 							case DAMAGE_AREA:
1129 							{
1130 								float ratio = (damages[j].radius - dist) * divradius;
1131 
1132 								if (ratio > 1.f) ratio = 1.f;
1133 								else if (ratio < 0.f) ratio = 0.f;
1134 
1135 								dmg = dmg * ratio + 1.f;
1136 							}
1137 							break;
1138 							case DAMAGE_AREAHALF:
1139 							{
1140 								float ratio = (damages[j].radius - (dist * ( 1.0f / 2 ))) * divradius;
1141 
1142 								if (ratio > 1.f) ratio = 1.f;
1143 								else if (ratio < 0.f) ratio = 0.f;
1144 
1145 								dmg = dmg * ratio + 1.f;
1146 							}
1147 							break;
1148 							case DAMAGE_FULL: break;
1149 						}
1150 
1151 						if (dmg <= 0.f) continue;
1152 
1153 						if (damages[j].flags & DAMAGE_FLAG_ADD_VISUAL_FX)
1154 						{
1155 							if ((entities[i]->ioflags & IO_NPC)
1156 							        &&	(entities[i]->_npcdata->life > 0.f))
1157 								ARX_DAMAGES_AddVisual(&damages[j], &sub, dmg, entities[i]);
1158 						}
1159 
1160 						if (damages[j].type & DAMAGE_TYPE_DRAIN_MANA)
1161 						{
1162 							float manadrained;
1163 
1164 							if (i == 0)
1165 							{
1166 								manadrained = min(dmg, player.mana);
1167 								player.mana -= manadrained;
1168 							}
1169 							else
1170 							{
1171 								manadrained = dmg;
1172 
1173 								if ((io) && (io->_npcdata))
1174 								{
1175 									manadrained = min(dmg, io->_npcdata->mana);
1176 									io->_npcdata->mana -= manadrained;
1177 								}
1178 							}
1179 
1180 							if (damages[j].source == 0)
1181 							{
1182 								player.mana = min(player.mana + manadrained, player.Full_maxmana);
1183 							}
1184 							else
1185 							{
1186 								if (ValidIONum(damages[j].source) && (entities[damages[j].source]->_npcdata))
1187 								{
1188 									entities[damages[j].source]->_npcdata->mana = min(entities[damages[j].source]->_npcdata->mana + manadrained, entities[damages[j].source]->_npcdata->maxmana);
1189 								}
1190 							}
1191 						}
1192 						else
1193 						{
1194 							float damagesdone;
1195 
1196 							if (i == 0)
1197 							{
1198 
1199 								if (damages[j].type & DAMAGE_TYPE_POISON)
1200 								{
1201 									if (rnd() * 100.f > player.resist_poison)
1202 									{
1203 										// Failed Saving Throw
1204 										damagesdone = dmg;
1205 										player.poison += damagesdone;
1206 
1207 									}
1208 									else damagesdone = 0;
1209 								}
1210 								else
1211 								{
1212 									if ((damages[j].type & DAMAGE_TYPE_MAGICAL)
1213 									        &&	(!(damages[j].type & DAMAGE_TYPE_FIRE))
1214 									        &&	(!(damages[j].type & DAMAGE_TYPE_COLD))
1215 									   )
1216 									{
1217 										dmg -= player.Full_resist_magic * ( 1.0f / 100 ) * dmg;
1218 										dmg = max(0.0f, dmg);
1219 									}
1220 
1221 									if (damages[j].type & DAMAGE_TYPE_FIRE)
1222 									{
1223 										dmg = ARX_SPELLS_ApplyFireProtection(entities.player(), dmg);
1224 										ARX_DAMAGES_IgnitIO(entities.player(), dmg);
1225 									}
1226 
1227 									if (damages[j].type & DAMAGE_TYPE_COLD)
1228 									{
1229 										dmg = ARX_SPELLS_ApplyColdProtection(entities.player(), dmg);
1230 									}
1231 
1232 									damagesdone = ARX_DAMAGES_DamagePlayer(dmg, damages[j].type, damages[j].source);
1233 								}
1234 							}
1235 							else
1236 							{
1237 								if ((entities[i]->ioflags & IO_NPC)
1238 								        && (damages[j].type & DAMAGE_TYPE_POISON))
1239 								{
1240 									if (rnd() * 100.f > entities[i]->_npcdata->resist_poison)
1241 									{
1242 										// Failed Saving Throw
1243 										damagesdone = dmg;
1244 										entities[i]->_npcdata->poisonned += damagesdone;
1245 									}
1246 									else damagesdone = 0;
1247 								}
1248 								else
1249 								{
1250 									if (damages[j].type & DAMAGE_TYPE_FIRE)
1251 									{
1252 										dmg = ARX_SPELLS_ApplyFireProtection(entities[i], dmg);
1253 										ARX_DAMAGES_IgnitIO(entities[i], dmg);
1254 									}
1255 
1256 									if ((damages[j].type & DAMAGE_TYPE_MAGICAL)
1257 									        && (!(damages[j].type & DAMAGE_TYPE_FIRE))
1258 									        && (!(damages[j].type & DAMAGE_TYPE_COLD))
1259 									   )
1260 									{
1261 										dmg -= entities[i]->_npcdata->resist_magic * ( 1.0f / 100 ) * dmg;
1262 										dmg = max(0.0f, dmg);
1263 									}
1264 
1265 									if (damages[j].type & DAMAGE_TYPE_COLD)
1266 									{
1267 										dmg = ARX_SPELLS_ApplyColdProtection(entities[i], dmg);
1268 									}
1269 
1270 									damagesdone = ARX_DAMAGES_DamageNPC(entities[i], dmg, damages[j].source, 1, &damages[j].pos);
1271 								}
1272 
1273 								if ((damagesdone > 0) && (damages[j].flags & DAMAGE_SPAWN_BLOOD))
1274 								{
1275 									ARX_PARTICLES_Spawn_Blood(&damages[j].pos, damagesdone, damages[j].source);
1276 								}
1277 							}
1278 
1279 							if (damages[j].type & DAMAGE_TYPE_DRAIN_LIFE)
1280 							{
1281 								if (ValidIONum(damages[j].source))
1282 									ARX_DAMAGES_HealInter(entities[damages[j].source], damagesdone);
1283 							}
1284 						}
1285 					}
1286 
1287 				}
1288 				else if ((io->ioflags & IO_FIX)
1289 				         &&	(!(damages[j].type & DAMAGE_TYPE_NO_FIX)))
1290 				{
1291 					EERIE_SPHERE sphere;
1292 					sphere.origin = damages[j].pos;
1293 					sphere.radius = damages[j].radius + 15.f;
1294 
1295 					if (CheckIOInSphere(&sphere, i))
1296 					{
1297 						ARX_DAMAGES_DamageFIX(io, dmg, damages[j].source, 1);
1298 					}
1299 				}
1300 			}
1301 		}
1302 
1303 		if (damages[j].duration == -1) damages[j].exist = false;
1304 		else if (tim > damages[j].start_time + damages[j].duration)
1305 			damages[j].exist = false;
1306 	}
1307 }
ARX_DAMAGES_UpdateAll()1308 void ARX_DAMAGES_UpdateAll()
1309 {
1310 	for (size_t j = 0; j < MAX_DAMAGES; j++)
1311 		ARX_DAMAGES_UpdateDamage(j, arxtime);
1312 }
SphereInIO(Entity * io,Vec3f * pos,float radius)1313 bool SphereInIO(Entity * io, Vec3f * pos, float radius)
1314 {
1315 	if (io == NULL) return false;
1316 
1317 	if (io->obj == NULL) return false;
1318 
1319 	long step;
1320 
1321 	if (io->obj->vertexlist.size() < 150) step = 1;
1322 	else if (io->obj->vertexlist.size() < 300) step = 2;
1323 	else if (io->obj->vertexlist.size() < 600) step = 4;
1324 	else if (io->obj->vertexlist.size() < 1200) step = 6;
1325 	else step = 7;
1326 
1327 	for(size_t i = 0; i < io->obj->vertexlist.size(); i += step) {
1328 		if(!fartherThan(*pos, io->obj->vertexlist3[i].v, radius)) {
1329 			return true;
1330 		}
1331 	}
1332 
1333 	return false;
1334 }
ARX_DAMAGES_TryToDoDamage(Vec3f * pos,float dmg,float radius,long source)1335 bool ARX_DAMAGES_TryToDoDamage(Vec3f * pos, float dmg, float radius, long source)
1336 {
1337 	bool ret = false;
1338 
1339 	for(size_t i = 0; i < entities.size(); i++) {
1340 		Entity * io = entities[i];
1341 
1342 		if (io != NULL)
1343 
1344 			if (entities[i]->gameFlags & GFLAG_ISINTREATZONE)
1345 				if (io->show == SHOW_FLAG_IN_SCENE)
1346 					if (source != long(i))
1347 					{
1348 						float threshold;
1349 						float rad = radius + 5.f;
1350 
1351 						if (io->ioflags & IO_FIX)
1352 						{
1353 							threshold = square(510);
1354 							rad += 10.f;
1355 						}
1356 						else if (io->ioflags & IO_NPC)
1357 							threshold = square(250);
1358 						else threshold = square(350);
1359 
1360 						if (distSqr(*pos, io->pos) < threshold)
1361 							if (SphereInIO(io, pos, rad))
1362 							{
1363 								if (io->ioflags & IO_NPC)
1364 								{
1365 									if (ValidIONum(source))
1366 										ARX_EQUIPMENT_ComputeDamages(entities[source], io, 1.f);
1367 
1368 									ret = true;
1369 								}
1370 
1371 								if (io->ioflags & IO_FIX)
1372 								{
1373 									ARX_DAMAGES_DamageFIX(io, dmg, source, 0);
1374 									ret = true;
1375 								}
1376 							}
1377 					}
1378 	}
1379 
1380 	return ret;
1381 }
1382 
CheckForIgnition(Vec3f * pos,float radius,bool mode,long flag)1383 void CheckForIgnition(Vec3f * pos, float radius, bool mode, long flag) {
1384 
1385 	if (!(flag & 1))
1386 		for (size_t i = 0; i < MAX_LIGHTS; i++)
1387 		{
1388 			EERIE_LIGHT * el = GLight[i];
1389 
1390 			if (el == NULL) continue;
1391 
1392 			if ((el->extras & EXTRAS_EXTINGUISHABLE)
1393 			        &&
1394 			        ((el->extras & EXTRAS_SEMIDYNAMIC)
1395 			         || (el->extras & EXTRAS_SPAWNFIRE)
1396 			         || (el->extras & EXTRAS_SPAWNSMOKE)))
1397 			{
1398 				if ((el->extras & EXTRAS_FIREPLACE) && (flag & 2))
1399 					continue;
1400 
1401 				if(distSqr(*pos, el->pos) <= square(radius)) {
1402 					if(mode) {
1403 						if (!(el->extras & EXTRAS_NO_IGNIT))
1404 							el->status = 1;
1405 					}
1406 					else
1407 					{
1408 						el->status = 0;
1409 					}
1410 				}
1411 
1412 			}
1413 		}
1414 
1415 	for(size_t i = 0; i < entities.size(); i++) {
1416 		Entity * io = entities[i];
1417 
1418 		if ((io)
1419 		        &&	(io->show == 1)
1420 		        &&	(io->obj)
1421 		        &&  !(io->ioflags & IO_UNDERWATER)
1422 		        &&	(io->obj->fastaccess.fire >= 0)
1423 		   )
1424 		{
1425 
1426 			if(distSqr(*pos, io->obj->vertexlist3[io->obj->fastaccess.fire].v) < square(radius)) {
1427 
1428 				if ((mode) && (io->ignition <= 0) && (io->obj->fastaccess.fire >= 0))
1429 				{
1430 					io->ignition = 1;
1431 				}
1432 				else if ((!mode) && (io->ignition > 0))
1433 				{
1434 					if (io->obj->fastaccess.fire >= 0)
1435 					{
1436 						io->ignition = 0;
1437 
1438 						if (ValidDynLight(io->ignit_light))
1439 							DynLight[io->ignit_light].exist = 0;
1440 
1441 						io->ignit_light = -1;
1442 
1443 						if (io->ignit_sound != audio::INVALID_ID)
1444 						{
1445 							ARX_SOUND_Stop(io->ignit_sound);
1446 							io->ignit_sound = audio::INVALID_ID;
1447 						}
1448 					}
1449 					else if (!(flag & 2))
1450 						io->ignition = 0.00001f;
1451 				}
1452 			}
1453 		}
1454 	}
1455 }
1456 
1457 //*************************************************************************************
1458 //*************************************************************************************
DoSphericDamage(Vec3f * pos,float dmg,float radius,DamageArea flags,DamageType typ,long numsource)1459 bool DoSphericDamage(Vec3f * pos, float dmg, float radius, DamageArea flags, DamageType typ, long numsource)
1460 {
1461 	bool damagesdone = false;
1462 	Vec3f sub;
1463 	sub.x = player.pos.x;
1464 	sub.y = player.pos.y + 90.f;
1465 	sub.z = player.pos.z;
1466 
1467 	if (radius <= 0.f) return damagesdone;
1468 
1469 	float rad = 1.f / radius;
1470 	long validsource = ValidIONum(numsource);
1471 
1472 	for(size_t i = 0; i < entities.size(); i++) {
1473 		Entity * ioo = entities[i];
1474 
1475 		if ((ioo) && (long(i) != numsource) && (ioo->obj))
1476 		{
1477 			if ((i != 0) && (numsource != 0)
1478 			        && validsource && (HaveCommonGroup(ioo, entities[numsource])))
1479 				continue;
1480 
1481 			if ((ioo->ioflags & IO_CAMERA) || (ioo->ioflags & IO_MARKER)) continue;
1482 
1483 			long count = 0;
1484 			long count2 = 0;
1485 			float mindist = std::numeric_limits<float>::max();
1486 
1487 			for (size_t k = 0; k < ioo->obj->vertexlist.size(); k += 1)
1488 			{
1489 				if (ioo->obj->vertexlist.size() < 120)
1490 				{
1491 					for (size_t kk = 0; kk < ioo->obj->vertexlist.size(); kk += 1)
1492 					{
1493 						if (kk != k)
1494 						{
1495 							Vec3f posi = (entities[i]->obj->vertexlist3[k].v
1496 							              + entities[i]->obj->vertexlist3[kk].v) * 0.5f;
1497 							float dist = fdist(*pos, posi);
1498 							if(dist <= radius) {
1499 								count2++;
1500 								if (dist < mindist) mindist = dist;
1501 							}
1502 						}
1503 					}
1504 				}
1505 
1506 				{
1507 					float dist = fdist(*pos, entities[i]->obj->vertexlist3[k].v);
1508 
1509 					if (dist <= radius)
1510 					{
1511 						count++;
1512 
1513 						if (dist < mindist) mindist = dist;
1514 					}
1515 				}
1516 			}
1517 
1518 			float ratio = ((float)count / ((float)ioo->obj->vertexlist.size() * ( 1.0f / 2 )));
1519 
1520 			if (count2 > count)
1521 				ratio = ((float)count2 / ((float)ioo->obj->vertexlist.size() * ( 1.0f / 2 )));
1522 
1523 			if (ratio > 2.f) ratio = 2.f;
1524 
1525 			if (ioo->ioflags & IO_NPC)
1526 			{
1527 				if (mindist <= radius + 30.f)
1528 				{
1529 					switch (flags)
1530 					{
1531 						case DAMAGE_AREA:
1532 							dmg = dmg * (radius + 30 - mindist) * rad;
1533 							break;
1534 						case DAMAGE_AREAHALF:
1535 							dmg = dmg * (radius + 30 - mindist * ( 1.0f / 2 )) * rad;
1536 							break;
1537 						case DAMAGE_FULL: break;
1538 					}
1539 
1540 					if (i == 0)
1541 					{
1542 						if (typ & DAMAGE_TYPE_FIRE)
1543 						{
1544 							dmg = ARX_SPELLS_ApplyFireProtection(ioo, dmg);
1545 							ARX_DAMAGES_IgnitIO(entities.player(), dmg);
1546 						}
1547 
1548 						if (typ & DAMAGE_TYPE_COLD)
1549 						{
1550 							dmg = ARX_SPELLS_ApplyColdProtection(ioo, dmg);
1551 						}
1552 
1553 						ARX_DAMAGES_DamagePlayer(dmg, typ, numsource);
1554 						ARX_DAMAGES_DamagePlayerEquipment(dmg);
1555 					}
1556 					else
1557 					{
1558 						if (typ & DAMAGE_TYPE_FIRE)
1559 						{
1560 							dmg = ARX_SPELLS_ApplyFireProtection(ioo, dmg * ratio);
1561 							ARX_DAMAGES_IgnitIO(ioo, dmg);
1562 						}
1563 
1564 						if (typ & DAMAGE_TYPE_COLD)
1565 						{
1566 							dmg = ARX_SPELLS_ApplyColdProtection(ioo, dmg * ratio);
1567 						}
1568 
1569 						ARX_DAMAGES_DamageNPC(ioo, dmg * ratio, numsource, 1, pos);
1570 					}
1571 
1572 					if (dmg > 1) damagesdone = true;
1573 				}
1574 			}
1575 			else
1576 			{
1577 				if (mindist <= radius + 30.f)
1578 				{
1579 					if (typ & DAMAGE_TYPE_FIRE)
1580 					{
1581 						dmg = ARX_SPELLS_ApplyFireProtection(ioo, dmg * ratio);
1582 						ARX_DAMAGES_IgnitIO(entities[i], dmg);
1583 					}
1584 
1585 					if (typ & DAMAGE_TYPE_COLD)
1586 					{
1587 						dmg = ARX_SPELLS_ApplyColdProtection(ioo, dmg * ratio);
1588 					}
1589 
1590 					if (entities[i]->ioflags & IO_FIX)
1591 						ARX_DAMAGES_DamageFIX(entities[i], dmg * ratio, numsource, 1);
1592 
1593 					if (dmg > 0.2f) damagesdone = true;
1594 				}
1595 			}
1596 		}
1597 	}
1598 
1599 	if (typ & DAMAGE_TYPE_FIRE)
1600 		CheckForIgnition(pos, radius, 1);
1601 
1602 	return damagesdone;
1603 }
ARX_DAMAGES_DurabilityRestore(Entity * io,float percent)1604 void ARX_DAMAGES_DurabilityRestore(Entity * io, float percent)
1605 {
1606 	if (!io) return;
1607 
1608 	if (io->durability <= 0) return;
1609 
1610 	if (io->durability == io->max_durability) return;
1611 
1612 	if (percent >= 100.f)
1613 	{
1614 		io->durability = io->max_durability;
1615 	}
1616 	else
1617 	{
1618 		float ratio			= percent * ( 1.0f / 100 );
1619 		float to_restore	= (io->max_durability - io->durability) * ratio;
1620 		float v				= rnd() * 100.f - percent;
1621 
1622 		if (v <= 0.f)
1623 		{
1624 			float mloss = 1.f;
1625 
1626 			if(io->ioflags & IO_ITEM) {
1627 				io->_itemdata->price -= checked_range_cast<long>(io->_itemdata->price / io->max_durability);
1628 			}
1629 
1630 			io->max_durability -= mloss;
1631 		}
1632 		else
1633 		{
1634 			if (v > 50.f)
1635 				v = 50.f;
1636 
1637 			v *= ( 1.0f / 100 );
1638 			float mloss = io->max_durability * v;
1639 
1640 			if (io->ioflags & IO_ITEM)
1641 			{
1642 				io->_itemdata->price -= static_cast<long>(io->_itemdata->price * v);
1643 			}
1644 
1645 			io->max_durability -= mloss;
1646 		}
1647 
1648 		io->durability += to_restore;
1649 
1650 		if (io->durability > io->max_durability) io->durability = io->max_durability;
1651 
1652 		if (io->max_durability <= 0.f)
1653 			ARX_DAMAGES_DurabilityLoss(io, 100);
1654 	}
1655 
1656 }
ARX_DAMAGES_DurabilityCheck(Entity * io,float ratio)1657 void ARX_DAMAGES_DurabilityCheck(Entity * io, float ratio)
1658 {
1659 	if (!io) return;
1660 
1661 	if (rnd() * 100.f > io->durability)
1662 	{
1663 		ARX_DAMAGES_DurabilityLoss(io, ratio);
1664 	}
1665 }
ARX_DAMAGES_DurabilityLoss(Entity * io,float loss)1666 void ARX_DAMAGES_DurabilityLoss(Entity * io, float loss)
1667 {
1668 	if (!io) return;
1669 
1670 	io->durability -= loss;
1671 
1672 	if (io->durability <= 0)
1673 	{
1674 		SendIOScriptEvent(io, SM_BREAK);
1675 	}
1676 }
ARX_DAMAGES_DamagePlayerEquipment(float damages)1677 void ARX_DAMAGES_DamagePlayerEquipment(float damages)
1678 {
1679 	float ratio = damages * ( 1.0f / 20 );
1680 
1681 	if (ratio > 1.f) ratio = 1.f;
1682 
1683 	for (long i = 0; i < MAX_EQUIPED; i++)
1684 	{
1685 		if (player.equiped[i] != 0)
1686 		{
1687 			Entity * todamage = entities[player.equiped[i]];
1688 			ARX_DAMAGES_DurabilityCheck(todamage, ratio);
1689 		}
1690 	}
1691 }
ARX_DAMAGES_ComputeRepairPrice(Entity * torepair,Entity * blacksmith)1692 float ARX_DAMAGES_ComputeRepairPrice(Entity * torepair, Entity * blacksmith)
1693 {
1694 	if ((!torepair) || (!blacksmith)) return -1.f;
1695 
1696 	if (!(torepair->ioflags & IO_ITEM)) return -1.f;
1697 
1698 	if (torepair->max_durability <= 0.f) return -1.f;
1699 
1700 	if (torepair->durability == torepair->max_durability) return -1.f;
1701 
1702 	float ratio = (torepair->max_durability - torepair->durability) / torepair->max_durability;
1703 	float price = torepair->_itemdata->price * ratio;
1704 
1705 	if (blacksmith->shop_multiply != 0.f)
1706 		price *= blacksmith->shop_multiply;
1707 
1708 	if ((price > 0.f) &&
1709 	        (price < 1.f)) price = 1.f;
1710 
1711 	return price;
1712 }
1713