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