/* =========================================================================== ARX FATALIS GPL Source Code Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company. This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code'). Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see . In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ ////////////////////////////////////////////////////////////////////////////////////// // @@ @@@ @@@ @@ @@@@@ // // @@@ @@@@@@ @@@ @@ @@@@ @@@ @@@ // // @@@ @@@@@@@ @@@ @@@@ @@@@ @@ @@@@ // // @@@ @@ @@@@ @@@ @@@@@ @@@@@@ @@@ @@@ // // @@@@@ @@ @@@@ @@@ @@@@@ @@@@@@@ @@@ @ @@@ // // @@@@@ @@ @@@@ @@@@@@@@ @@@@ @@@ @@@@@ @@ @@@@@@@ // // @@ @@@ @@ @@@@ @@@@@@@ @@@ @@@ @@@@@@ @@ @@@@ // // @@@ @@@ @@@ @@@@ @@@@@ @@@@@@@@@ @@@@@@@ @@@ @@@@ // // @@@ @@@@ @@@@@@@ @@@@@@ @@@ @@@@ @@@ @@@ @@@ @@@@ // // @@@@@@@@ @@@@@ @@@@@@@@@@ @@@ @@@ @@@ @@@ @@@ @@@@@ // // @@@ @@@@ @@@@ @@@ @@@@@@@ @@@ @@@ @@@@ @@@ @@@@ @@@@@ // //@@@ @@@@ @@@@@ @@@ @@@@@@ @@ @@@ @@@@ @@@@@@@ @@@@@ @@@@@ // //@@@ @@@@@ @@@@@ @@@@ @@@ @@ @@ @@@@ @@@@@@@ @@@@@@@@@ // //@@@ @@@@ @@@@@@@ @@@@ @@ @@ @@@@ @@@@@ @@@@@ // //@@@ @@@@ @@@@@@@ @@@@ @@ @@ @@@@ @@@@@ @@ // //@@@ @@@ @@@ @@@@@ @@ @@@ // // @@@ @@@ @@ @@ STUDIOS // ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // ARX_Damages ////////////////////////////////////////////////////////////////////////////////////// // // Description: // ARX Damages management // // Updates: (date) (person) (update) // // Code: Cyril Meynier // // Copyright (c) 1999 ARKANE Studios SA. All rights reserved ////////////////////////////////////////////////////////////////////////////////////// #include #include #include "ARX_Damages.h" #include "HERMESMain.h" #include "EERIEpoly.h" #include "EERIELinkedObj.h" #include "EERIELight.h" #include "EERIEDRAW.h" #include "EERIEPoly.h" #include "ARX_Player.h" #include "ARX_NPC.h" #include "ARX_Sound.h" #include "ARX_Speech.h" #include "ARX_Collisions.h" #include "ARX_Particles.h" #include "ARX_Equipment.h" #include "ARX_Interface.h" #include "ARX_Paths.h" #include "ARX_script.h" #include "ARX_time.h" #define _CRTDBG_MAP_ALLOC #include extern long REFUSE_GAME_RETURN; typedef struct { long exist; float x; float y; float size; TextureContainer * tc; long duration; unsigned long starttime; } SCREEN_SPLATS; #define MAX_SCREEN_SPLATS 12 DAMAGE_INFO damages[MAX_DAMAGES]; extern long ParticleCount; extern EERIE_3D PUSH_PLAYER_FORCE; float Blood_Pos = 0.f; long Blood_Duration = 0; SCREEN_SPLATS ssplat[MAX_SCREEN_SPLATS]; void ARX_DAMAGES_IgnitIO(INTERACTIVE_OBJ * io, float dmg) { if ((!io) || (io->ioflags & IO_INVULNERABILITY)) return; if ((io->ignition <= 0.f) && (io->ignition + dmg > 1.f)) { SendIOScriptEvent(io, SM_ENTERZONE, "COOK_S", NULL); } if (io->ioflags & IO_FIX) io->ignition += dmg * DIV10; else if (io->ioflags & IO_ITEM) io->ignition += dmg * DIV8; else if (io->ioflags & IO_NPC) io->ignition += dmg * DIV4; } void ARX_DAMAGES_SCREEN_SPLATS_Init() { for (long i = 0; i < MAX_SCREEN_SPLATS; i++) ssplat[i].exist = 0; } extern TextureContainer * bloodsplat[6]; void ARX_DAMAGES_SCREEN_SPLATS_Add(EERIE_3D * pos, float dmgs) { return; long j = ARX_PARTICLES_GetFree(); if ((j != -1) && (!ARXPausedTimer)) { D3DCOLOR col = inter.iobj[0]->_npcdata->blood_color; D3DTLVERTEX in, out; in.sx = pos->x; in.sy = pos->y; in.sz = pos->z; EERIETreatPoint(&in, &out); if (out.sx < 0) out.sx = 0; else if (out.sx > DANAESIZX) out.sx = ARX_CLEAN_WARN_CAST_D3DVALUE(DANAESIZX); if (out.sy < 0) out.sy = 0; else if (out.sy > DANAESIZY) out.sy = ARX_CLEAN_WARN_CAST_D3DVALUE(DANAESIZY); float power; power = (dmgs * DIV60) + 0.9f; float r, g, b; r = (float)((long)((col >> 16) & 255)) * DIV255; g = (float)((long)((col >> 8) & 255)) * DIV255; b = (float)((long)((col) & 255)) * DIV255; ParticleCount++; PARTICLE_DEF * pd = &particle[j]; pd->special = PARTICLE_SUB2 | SUBSTRACT; pd->exist = TRUE; pd->zdec = 0; pd->ov.x = out.sx; pd->ov.y = out.sy; pd->ov.z = 0.0000001f; pd->move.x = 0.f; pd->move.y = 0.f; pd->move.z = 0.f; pd->scale.x = 1.8f; pd->scale.y = 1.8f; pd->scale.z = 1.f; pd->timcreation = lARXTime; pd->tolive = 1800 + (unsigned long)(rnd() * 400.f); long num; F2L((float)(rnd() * 6.f), &num); if (num < 0) num = 0; else if (num > 5) num = 5; pd->tc = bloodsplat[num]; pd->r = r; pd->g = g; pd->b = b; pd->siz = 3.5f * power * 40 * Xratio; pd->type = PARTICLE_2D; } } void ARX_DAMAGE_Reset_Blood_Info() { Blood_Pos = 0.f; Blood_Duration = 0; } void ARX_DAMAGE_Show_Hit_Blood(LPDIRECT3DDEVICE7 pd3dDevice) { D3DCOLOR color; static float Last_Blood_Pos = 0.f; static long duration; if (Blood_Pos > 2.f) // end of blood flash { Blood_Pos = 0.f; duration = 0; } else if (Blood_Pos > 1.f) { pd3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ZERO); pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_SRCCOLOR); SETALPHABLEND(pd3dDevice, TRUE); SETZWRITE(pd3dDevice, FALSE); if (player.poison > 1.f) color = D3DRGB(Blood_Pos - 1.f, 1.f, Blood_Pos - 1.f); else color = D3DRGB(1.f, Blood_Pos - 1.f, Blood_Pos - 1.f); EERIEDrawBitmap(pd3dDevice, 0.f, 0.f, (float)DANAESIZX, (float)DANAESIZY, 0.00009f, NULL, color); SETZWRITE(pd3dDevice, TRUE); SETALPHABLEND(pd3dDevice, FALSE); } else if (Blood_Pos > 0.f) { pd3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ZERO); pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_SRCCOLOR); SETALPHABLEND(pd3dDevice, TRUE); SETZWRITE(pd3dDevice, FALSE); if (player.poison > 1.f) color = D3DRGB(1.f - Blood_Pos, 1.f, 1.f - Blood_Pos); else color = D3DRGB(1.f, 1.f - Blood_Pos, 1.f - Blood_Pos); EERIEDrawBitmap(pd3dDevice, 0.f, 0.f, (float)DANAESIZX, (float)DANAESIZY, 0.00009f, NULL, color); SETALPHABLEND(pd3dDevice, FALSE); SETZWRITE(pd3dDevice, TRUE); } if (Blood_Pos > 0.f) { if (Blood_Pos > 1.f) { if ((Last_Blood_Pos <= 1.f)) { Blood_Pos = 1.0001f; duration = 0; } if (duration > Blood_Duration) Blood_Pos += (float)FrameDiff * DIV300; duration += ARX_CLEAN_WARN_CAST_LONG(FrameDiff); } else Blood_Pos += (float)FrameDiff * DIV40; } Last_Blood_Pos = Blood_Pos; } //************************************************************************************* //************************************************************************************* float ARX_DAMAGES_DamagePlayer(float dmg, long type, long source, EERIE_3D * pos) { if (player.playerflags & PLAYERFLAGS_INVULNERABILITY) return 0; float damagesdone = 0.f; if (player.life == 0.f) return damagesdone; if (dmg > player.life) damagesdone = dmg; else damagesdone = player.life; inter.iobj[0]->dmg_sum += dmg; if (ARXTime > inter.iobj[0]->ouch_time + 500) { INTERACTIVE_OBJ * oes = EVENT_SENDER; if (ValidIONum(source)) EVENT_SENDER = inter.iobj[source]; else EVENT_SENDER = NULL; inter.iobj[0]->ouch_time = ARXTimeUL(); char tex[32]; sprintf(tex, "%5.2f", inter.iobj[0]->dmg_sum); SendIOScriptEvent(inter.iobj[0], SM_OUCH, tex, NULL); EVENT_SENDER = oes; float power = inter.iobj[0]->dmg_sum / player.maxlife * 220.f; AddQuakeFX(power * 3.5f, 500 + power * 3, rnd() * 100.f + power + 200, 0); inter.iobj[0]->dmg_sum = 0.f; } if (dmg > 0.f) { if (ValidIONum(source)) { INTERACTIVE_OBJ * pio = NULL; if (inter.iobj[source]->ioflags & IO_NPC) { pio = (INTERACTIVE_OBJ *)inter.iobj[source]->_npcdata->weapon; if ((pio) && ((pio->poisonous == 0) || (pio->poisonous_count == 0))) pio = NULL; } if (!pio) pio = inter.iobj[source]; if (pio && pio->poisonous && (pio->poisonous_count != 0)) { if (rnd() * 100.f > player.resist_poison) { player.poison += pio->poisonous; } if (pio->poisonous_count != -1) pio->poisonous_count--; } } long alive; if (player.life > 0) alive = 1; else alive = 0; if (!BLOCK_PLAYER_CONTROLS) player.life -= dmg; if (player.life <= 0.f) { player.life = 0.f; if (alive) { REFUSE_GAME_RETURN = 1; ARX_PLAYER_BecomesDead(); if ((type & DAMAGE_TYPE_FIRE) || (type & DAMAGE_TYPE_FAKEFIRE)) { ARX_SOUND_PlayInterface(SND_PLAYER_DEATH_BY_FIRE); } SendIOScriptEvent(inter.iobj[0], SM_DIE, "", NULL); for (long i = 1; i < inter.nbmax; i++) { INTERACTIVE_OBJ * ioo = inter.iobj[i]; if ((ioo) && (ioo->ioflags & IO_NPC)) { if ((ioo->targetinfo == 0) || (ioo->targetinfo == TARGET_PLAYER)) { EVENT_SENDER = inter.iobj[0]; char killer[256]; memset(killer, 0, 256); if (source == 0) strcpy(killer, "PLAYER"); else if (source <= -1) strcpy(killer, "NONE"); else if (ValidIONum(source) && (inter.iobj[source]->filename) && (inter.iobj[source]->filename[0] != 0)) { char temp[256]; strcpy(temp, GetName(inter.iobj[source]->filename)); sprintf(killer, "%s_%04d", temp, inter.iobj[source]->ident); } SendIOScriptEvent(inter.iobj[i], 0, killer, "TARGET_DEATH"); } } } } } if (player.maxlife <= 0.f) return damagesdone; float t = dmg / player.maxlife; if (Blood_Pos == 0.f) { Blood_Pos = 0.000001f; F2L(100 + (float)(t * 200.f), &Blood_Duration); } else { long temp; F2L((float)(t * 800.f), &temp); Blood_Duration += temp; } } // revient les barres ResetPlayerInterface(); return damagesdone; } void ARX_DAMAGES_HealPlayer(float dmg) { if (player.life == 0.f) return; if (dmg > 0.f) { if (!BLOCK_PLAYER_CONTROLS) player.life += dmg; if (player.life > player.Full_maxlife) player.life = player.Full_maxlife; } } void ARX_DAMAGES_HealInter(INTERACTIVE_OBJ * io, float dmg) { if (io == NULL) return; if (!(io->ioflags & IO_NPC)) return; if (io->_npcdata->life <= 0.f) return; if (io == inter.iobj[0]) ARX_DAMAGES_HealPlayer(dmg); if (dmg > 0.f) { io->_npcdata->life += dmg; if (io->_npcdata->life > io->_npcdata->maxlife) io->_npcdata->life = io->_npcdata->maxlife; } } void ARX_DAMAGES_HealManaPlayer(float dmg) { if (player.life == 0.f) return; if (dmg > 0.f) { player.mana += dmg; if (player.mana > player.Full_maxmana) player.mana = player.Full_maxmana; } } void ARX_DAMAGES_HealManaInter(INTERACTIVE_OBJ * io, float dmg) { if (io == NULL) return; if (!(io->ioflags & IO_NPC)) return; if (io == inter.iobj[0]) ARX_DAMAGES_HealManaPlayer(dmg); if (io->_npcdata->life <= 0.f) return; if (dmg > 0.f) { io->_npcdata->mana += dmg; if (io->_npcdata->mana > io->_npcdata->maxmana) io->_npcdata->mana = io->_npcdata->maxmana; } } float ARX_DAMAGES_DrainMana(INTERACTIVE_OBJ * io, float dmg) { if (io == NULL) return 0; if (!(io->ioflags & IO_NPC)) return 0; if (io == inter.iobj[0]) { if (player.playerflags & PLAYERFLAGS_NO_MANA_DRAIN) return 0; if (player.mana >= dmg) { player.mana -= dmg; return dmg; } float d = player.mana; player.mana = 0; return d; } if (io->_npcdata->mana >= dmg) { io->_npcdata->mana -= dmg; return dmg; } float d = io->_npcdata->mana; io->_npcdata->mana = 0; return d; } //************************************************************************************* //************************************************************************************* void ARX_DAMAGES_DamageFIX(INTERACTIVE_OBJ * io, float dmg, long source, long flags, EERIE_3D * pos) { if ((!io) || (!io->show) || (!(io->ioflags & IO_FIX)) || (io->ioflags & IO_INVULNERABILITY) || (!io->script.data)) return; io->dmg_sum += dmg; if (ValidIONum(source)) EVENT_SENDER = inter.iobj[source]; else EVENT_SENDER = NULL; if (ARXTime > io->ouch_time + 500) { io->ouch_time = ARXTimeUL(); char tex[32]; sprintf(tex, "%5.2f", io->dmg_sum); SendIOScriptEvent(io, SM_OUCH, tex, NULL); io->dmg_sum = 0.f; } if (rnd() * 100.f > io->durability) io->durability -= dmg * DIV2; //1.f; if (io->durability <= 0.f) { io->durability = 0.f; SendIOScriptEvent(io, SM_BREAK, ""); } else { char dmm[32]; if (EVENT_SENDER == inter.iobj[0]) { if (flags & 1) { sprintf(dmm, "%f SPELL", dmg); } else switch (ARX_EQUIPMENT_GetPlayerWeaponType()) { case WEAPON_BARE: sprintf(dmm, "%f BARE", dmg); break; case WEAPON_DAGGER: sprintf(dmm, "%f DAGGER", dmg); break; case WEAPON_1H: sprintf(dmm, "%f 1H", dmg); break; case WEAPON_2H: sprintf(dmm, "%f 2H", dmg); break; case WEAPON_BOW: sprintf(dmm, "%f ARROW", dmg); break; default: sprintf(dmm, "%f", dmg); break; } } else sprintf(dmm, "%f", dmg); if (SendIOScriptEvent(io, SM_HIT, dmm) != ACCEPT) return; } } extern INTERACTIVE_OBJ * FlyingOverIO; extern MASTER_CAMERA_STRUCT MasterCamera; void ARX_DAMAGES_ForceDeath(INTERACTIVE_OBJ * io_dead, INTERACTIVE_OBJ * io_killer) { if (!stricmp(io_dead->mainevent, "DEAD")) return; INTERACTIVE_OBJ * old_sender = EVENT_SENDER; EVENT_SENDER = io_killer; if (io_dead == DRAGINTER) Set_DragInter(NULL); if (io_dead == FlyingOverIO) FlyingOverIO = NULL; if ((MasterCamera.exist & 1) && (MasterCamera.io == io_dead)) MasterCamera.exist = 0; if ((MasterCamera.exist & 2) && (MasterCamera.want_io == io_dead)) MasterCamera.exist = 0; //Kill all speeches if (ValidDynLight(io_dead->dynlight)) DynLight[io_dead->dynlight].exist = 0; io_dead->dynlight = -1; if (ValidDynLight(io_dead->halo.dynlight)) DynLight[io_dead->halo.dynlight].exist = 0; io_dead->halo.dynlight = -1; ARX_NPC_Behaviour_Reset(io_dead); ARX_SPEECH_ReleaseIOSpeech(io_dead); //Kill all Timers... ARX_SCRIPT_Timer_Clear_By_IO(io_dead); if (stricmp(io_dead->mainevent, "DEAD")) NotifyIOEvent(io_dead, SM_DIE, ""); if (!ValidIOAddress(io_dead)) return; ARX_SCRIPT_SetMainEvent(io_dead, "DEAD"); if (EEDistance3D(&io_dead->pos, &ACTIVECAM->pos) > 3200) { io_dead->animlayer[0].ctime = 9999999; io_dead->lastanimtime = 0; } char killer[256]; killer[0] = 0; if (io_dead->ioflags & IO_NPC) io_dead->_npcdata->weaponinhand = 0; ARX_INTERACTIVE_DestroyDynamicInfo(io_dead); if (io_killer == inter.iobj[0]) strcpy(killer, "PLAYER"); else { if (io_killer) { char temp[256]; strcpy(temp, GetName(io_killer->filename)); sprintf(killer, "%s_%04d", temp, io_killer->ident); } } for (long i = 1; i < inter.nbmax; i++) { INTERACTIVE_OBJ * ioo = inter.iobj[i]; if (ioo == io_dead) continue; if ((ioo) && (ioo->ioflags & IO_NPC)) { if (ValidIONum(ioo->targetinfo)) if (inter.iobj[ioo->targetinfo] == io_dead) { EVENT_SENDER = io_dead; Stack_SendIOScriptEvent(inter.iobj[i], 0, killer, "TARGET_DEATH"); ioo->targetinfo = TARGET_NONE; ioo->_npcdata->reachedtarget = 0; } if (ValidIONum(ioo->_npcdata->pathfind.truetarget)) if (inter.iobj[ioo->_npcdata->pathfind.truetarget] == io_dead) { EVENT_SENDER = io_dead; Stack_SendIOScriptEvent(inter.iobj[i], 0, killer, "TARGET_DEATH"); ioo->_npcdata->pathfind.truetarget = TARGET_NONE; ioo->_npcdata->reachedtarget = 0; } } } IO_UnlinkAllLinkedObjects(io_dead); io_dead->animlayer[1].cur_anim = NULL; io_dead->animlayer[2].cur_anim = NULL; io_dead->animlayer[3].cur_anim = NULL; if (io_dead->ioflags & IO_NPC) { io_dead->_npcdata->life = 0; if (io_dead->_npcdata->weapon != NULL) { INTERACTIVE_OBJ * ioo = (INTERACTIVE_OBJ *)io_dead->_npcdata->weapon; if (ValidIOAddress(ioo)) { ioo->show = SHOW_FLAG_IN_SCENE; ioo->ioflags |= IO_NO_NPC_COLLIDE; ioo->pos.x = ioo->obj->vertexlist3[ioo->obj->origin].v.x; ioo->pos.y = ioo->obj->vertexlist3[ioo->obj->origin].v.y; ioo->pos.z = ioo->obj->vertexlist3[ioo->obj->origin].v.z; ioo->velocity.x = 0.f; ioo->velocity.y = 13.f; ioo->velocity.z = 0.f; ioo->stopped = 0; } } } EVENT_SENDER = old_sender; } void ARX_DAMAGES_PushIO(INTERACTIVE_OBJ * io_target, long source, float power) { if ((power > 0.f) && (ValidIONum(source))) { power *= DIV20; INTERACTIVE_OBJ * io = inter.iobj[source]; EERIE_3D vect; vect.x = io_target->pos.x - io->pos.x; vect.y = io_target->pos.y - io->pos.y; vect.z = io_target->pos.z - io->pos.z; Vector_Normalize(&vect); vect.x *= power; vect.y *= power; vect.z *= power; if (io_target == inter.iobj[0]) { PUSH_PLAYER_FORCE.x = vect.x; PUSH_PLAYER_FORCE.y = vect.y; PUSH_PLAYER_FORCE.z = vect.z; } else { io_target->move.x += vect.x; io_target->move.y += vect.y; io_target->move.z += vect.z; } } } float ARX_DAMAGES_DealDamages(long target, float dmg, long source, long flags, EERIE_3D * pos) { if ((!ValidIONum(target)) || (!ValidIONum(source))) return 0; INTERACTIVE_OBJ * io_target = inter.iobj[target]; INTERACTIVE_OBJ * io_source = inter.iobj[source]; float damagesdone; if (flags & DAMAGE_TYPE_PER_SECOND) { dmg = dmg * _framedelay * DIV1000; } if (target == 0) { if (flags & DAMAGE_TYPE_POISON) { if (rnd() * 100.f > player.resist_poison) { damagesdone = dmg; player.poison += damagesdone; } else damagesdone = 0; goto dodamage; } if (flags & DAMAGE_TYPE_DRAIN_MANA) damagesdone = ARX_DAMAGES_DrainMana(io_target, dmg); else { ARX_DAMAGES_DamagePlayerEquipment(dmg); damagesdone = ARX_DAMAGES_DamagePlayer(dmg, flags, source, pos); } dodamage: ; if (flags & DAMAGE_TYPE_FIRE) { ARX_DAMAGES_IgnitIO(io_target, damagesdone); } if (flags & DAMAGE_TYPE_DRAIN_LIFE) { ARX_DAMAGES_HealInter(io_source, damagesdone); } if (flags & DAMAGE_TYPE_DRAIN_MANA) { ARX_DAMAGES_HealManaInter(io_source, damagesdone); } if (flags & DAMAGE_TYPE_PUSH) { ARX_DAMAGES_PushIO(io_target, source, damagesdone * DIV2); } if ((flags & DAMAGE_TYPE_MAGICAL) && !(flags & (DAMAGE_TYPE_FIRE | DAMAGE_TYPE_COLD))) { damagesdone -= player.Full_resist_magic * DIV100 * damagesdone; damagesdone = __max(0, damagesdone); } return damagesdone; } else { if (io_target->ioflags & IO_NPC) { if (flags & DAMAGE_TYPE_POISON) { if (rnd() * 100.f > io_target->_npcdata->resist_poison) { damagesdone = dmg; io_target->_npcdata->poisonned += damagesdone; } else damagesdone = 0; goto dodamage2; } if (flags & DAMAGE_TYPE_FIRE) { if (rnd() * 100.f <= io_target->_npcdata->resist_fire) dmg = 0; ARX_DAMAGES_IgnitIO(io_target, dmg); } if (flags & DAMAGE_TYPE_DRAIN_MANA) { damagesdone = ARX_DAMAGES_DrainMana(io_target, dmg); } else damagesdone = ARX_DAMAGES_DamageNPC(io_target, dmg, source, 1, pos); dodamage2: ; if (flags & DAMAGE_TYPE_DRAIN_LIFE) { ARX_DAMAGES_HealInter(io_source, damagesdone); } if (flags & DAMAGE_TYPE_DRAIN_MANA) { ARX_DAMAGES_HealManaInter(io_source, damagesdone); } if (flags & DAMAGE_TYPE_PUSH) { ARX_DAMAGES_PushIO(io_target, source, damagesdone * DIV2); } if ((flags & DAMAGE_TYPE_MAGICAL) && !(flags & (DAMAGE_TYPE_FIRE | DAMAGE_TYPE_COLD))) { damagesdone -= io_target->_npcdata->resist_magic * DIV100 * damagesdone; damagesdone = __max(0, damagesdone); } return damagesdone; } } return 0; } extern bool bHitFlash; extern float fHitFlash; extern unsigned long ulHitFlash; //************************************************************************************* // flags & 1 == spell damage //************************************************************************************* float ARX_DAMAGES_DamageNPC(INTERACTIVE_OBJ * io, float dmg, long source, long flags, EERIE_3D * pos) //,INTERACTIVE_OBJ * source) { if ((!io) || (!io->show) || (io->ioflags & IO_INVULNERABILITY) || (!(io->ioflags & IO_NPC))) return 0.f; float damagesdone = 0.f; if (io->_npcdata->life <= 0.f) { if ((source != 0) || ((source == 0) && (player.equiped[EQUIP_SLOT_WEAPON] > 0))) { if ((dmg >= io->_npcdata->maxlife * 0.4f) && pos) ARX_NPC_TryToCutSomething(io, pos); return damagesdone; } return damagesdone; } io->dmg_sum += dmg; if (ARXTime > io->ouch_time + 500) { if (ValidIONum(source)) { EVENT_SENDER = inter.iobj[source]; } else EVENT_SENDER = NULL; io->ouch_time = ARXTimeUL(); char tex[32]; if (EVENT_SENDER && (EVENT_SENDER->summoner == 0)) { EVENT_SENDER = inter.iobj[0]; sprintf(tex, "%5.2f SUMMONED", io->dmg_sum); } else sprintf(tex, "%5.2f", io->dmg_sum); SendIOScriptEvent(io, SM_OUCH, tex, NULL); io->dmg_sum = 0.f; long n = ARX_SPELLS_GetSpellOn(io, SPELL_CONFUSE); if (n >= 0) { spells[n].tolive = 0; } } if (dmg >= 0.f) { if (ValidIONum(source)) { INTERACTIVE_OBJ * pio = NULL; if (source == 0) { if ((player.equiped[EQUIP_SLOT_WEAPON] != 0) && ValidIONum(player.equiped[EQUIP_SLOT_WEAPON])) { pio = inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]]; if ((pio) && ((pio->poisonous == 0) || (pio->poisonous_count == 0)) || (flags & 1)) pio = NULL; } } else { if (inter.iobj[source]->ioflags & IO_NPC) { pio = (INTERACTIVE_OBJ *)inter.iobj[source]->_npcdata->weapon; if ((pio) && ((pio->poisonous == 0) || (pio->poisonous_count == 0))) pio = NULL; } } if (!pio) pio = inter.iobj[source]; if (pio && pio->poisonous && (pio->poisonous_count != 0)) { if (rnd() * 100.f > io->_npcdata->resist_poison) { io->_npcdata->poisonned += pio->poisonous; } if (pio->poisonous_count != -1) pio->poisonous_count--; } } if (io->script.data != NULL) { if (source >= 0) { if (ValidIONum(source)) EVENT_SENDER = inter.iobj[source]; else EVENT_SENDER = NULL; char dmm[256]; if (EVENT_SENDER == inter.iobj[0]) { if (flags & 1) { sprintf(dmm, "%f SPELL", dmg); } else switch (ARX_EQUIPMENT_GetPlayerWeaponType()) { case WEAPON_BARE: sprintf(dmm, "%f BARE", dmg); break; case WEAPON_DAGGER: sprintf(dmm, "%f DAGGER", dmg); break; case WEAPON_1H: sprintf(dmm, "%f 1H", dmg); break; case WEAPON_2H: sprintf(dmm, "%f 2H", dmg); break; case WEAPON_BOW: sprintf(dmm, "%f ARROW", dmg); break; default: sprintf(dmm, "%f", dmg); break; } } else sprintf(dmm, "%f", dmg); if ((EVENT_SENDER) && (EVENT_SENDER->summoner == 0)) { EVENT_SENDER = inter.iobj[0]; sprintf(dmm, "%f SUMMONED", dmg); } if (SendIOScriptEvent(io, SM_HIT, dmm) != ACCEPT) return damagesdone; } } damagesdone = __min(dmg, io->_npcdata->life); io->_npcdata->life -= dmg; bHitFlash = true; if (io->_npcdata->life <= 0) { fHitFlash = 0; } else { fHitFlash = io->_npcdata->life / io->_npcdata->maxlife; } ulHitFlash = 0; if (io->_npcdata->life <= 0.f) { io->_npcdata->life = 0.f; if ((source != 0) || ((source == 0) && (player.equiped[EQUIP_SLOT_WEAPON] > 0))) { if ((dmg >= io->_npcdata->maxlife * DIV2) && pos) ARX_NPC_TryToCutSomething(io, pos); } if (ValidIONum(source)) { long xp = io->_npcdata->xpvalue; ARX_DAMAGES_ForceDeath(io, inter.iobj[source]); if (source == 0) ARX_PLAYER_Modify_XP(xp); } else ARX_DAMAGES_ForceDeath(io, NULL); } } return damagesdone; } //************************************************************************************* //************************************************************************************* void ARX_DAMAGES_Reset() { memset(damages, 0, sizeof(DAMAGE_INFO)*MAX_DAMAGES); } //************************************************************************************* //************************************************************************************* long ARX_DAMAGES_GetFree() { for (long i = 0; i < MAX_DAMAGES; i++) { if (!damages[i].exist) { damages[i].radius = 100.f; damages[i].start_time = ARXTimeUL(); damages[i].duration = 1000; damages[i].area = 0; damages[i].flags = 0; damages[i].type = 0; damages[i].special = 0; damages[i].special_ID = 0; damages[i].lastupd = 0; damages[i].active = 1; for (long j = 0; j < 10; j++) damages[i].except[j] = -1; return i; } } return -1; } long InExceptList(long dmg, long num) { for (long j = 0; j < 10; j++) if (damages[dmg].except[j] == num) return 1; return 0; } //************************************************************************************* //************************************************************************************* void ARX_DAMAGES_AddVisual(DAMAGE_INFO * di, EERIE_3D * pos, float dmg, INTERACTIVE_OBJ * io) { if (di->type & DAMAGE_TYPE_FAKEFIRE) { long num = -1; if (io != NULL) num = ((long)(float)(rnd() * (io->obj->nbvertex / 4 - 1))) * 4 + 1; unsigned long tim = ARXTimeUL(); if (di->lastupd + 200 < tim) { di->lastupd = tim; if (di->type & DAMAGE_TYPE_MAGICAL) ARX_SOUND_PlaySFX(SND_SPELL_MAGICAL_HIT, pos, 0.8F + 0.4F * rnd()); else ARX_SOUND_PlaySFX(SND_SPELL_FIRE_HIT, pos, 0.8F + 0.4F * rnd()); } for (long k = 0 ; k < 14 ; k++) { long j = ARX_PARTICLES_GetFree(); if ((j != -1) && (!ARXPausedTimer)) { ParticleCount++; particle[j].exist = TRUE; particle[j].zdec = 0; if (io != NULL) { ARX_CHECK_NOT_NEG(num); particle[j].ov.x = io->obj->vertexlist3[num].v.x + rnd() * 10.f - 5.f;; particle[j].ov.y = io->obj->vertexlist3[num].v.y + rnd() * 10.f - 5.f;; particle[j].ov.z = io->obj->vertexlist3[num].v.z + rnd() * 10.f - 5.f;; // } } else { particle[j].ov.x = pos->x + rnd() * 100.f - 50.f; particle[j].ov.y = pos->y + rnd() * 100.f - 50.f; particle[j].ov.z = pos->z + rnd() * 100.f - 50.f; } particle[j].siz = dmg; if (particle[j].siz < 5.f) particle[j].siz = 5.f; else if (particle[j].siz > 15.f) particle[j].siz = 15.f; particle[j].scale.x = -10.f; particle[j].scale.y = -10.f; particle[j].scale.z = -10.f; particle[j].timcreation = lARXTime; particle[j].special |= ROTATING | MODULATE_ROTATION; particle[j].special |= FIRE_TO_SMOKE; particle[j].tolive = 500 + (unsigned long)(rnd() * 400.f); if (di->type & DAMAGE_TYPE_MAGICAL) { particle[j].move.x = 1.f - 2.f * rnd(); particle[j].move.y = 2.f - 16.f * rnd(); particle[j].move.z = 1.f - 2.f * rnd(); particle[j].r = 0.3f; particle[j].g = 0.3f; particle[j].b = 0.8f; } else { particle[j].move.x = 1.f - 2.f * rnd(); particle[j].move.y = 2.f - 16.f * rnd(); particle[j].move.z = 1.f - 2.f * rnd(); particle[j].r = 0.5f; particle[j].g = 0.5f; particle[j].b = 0.5f; } particle[j].tc = TC_fire2; particle[j].fparam = 0.1f - rnd() * 0.2f; } } } } //************************************************************************************* // source = -1 no source but valid pos // source = 0 player // source > 0 IO //************************************************************************************* void ARX_DAMAGES_UpdateDamage(long j, float tim) { float dmg, dist; EERIE_3D sub; if (damages[j].exist) { if (!damages[j].active) return; if (damages[j].flags & DAMAGE_FLAG_FOLLOW_SOURCE) { if (damages[j].source == 0) { damages[j].pos.x = player.pos.x; damages[j].pos.y = player.pos.y; damages[j].pos.z = player.pos.z; } else { if (ValidIONum(damages[j].source)) { damages[j].pos.x = inter.iobj[damages[j].source]->pos.x; damages[j].pos.y = inter.iobj[damages[j].source]->pos.y; damages[j].pos.z = inter.iobj[damages[j].source]->pos.z; } } } if (damages[j].flags & DAMAGE_NOT_FRAME_DEPENDANT) dmg = damages[j].damages; else if (damages[j].duration == -1) dmg = damages[j].damages; else { float FD = (float)FrameDiff; if (tim > damages[j].start_time + damages[j].duration) FD -= damages[j].start_time + damages[j].duration - tim; dmg = damages[j].damages * FD * DIV1000; } long validsource = ValidIONum(damages[j].source); float divradius = 1.f / damages[j].radius; // checking for IO damages for (long i = 0; i < inter.nbmax; i++) { INTERACTIVE_OBJ * io = inter.iobj[i]; if ((io) && (io->GameFlags & GFLAG_ISINTREATZONE) && (io->show == SHOW_FLAG_IN_SCENE) && (!InExceptList(j, i)) && ( (damages[j].source != i) || ((damages[j].source == i) && (!(damages[j].flags & DAMAGE_FLAG_DONT_HURT_SOURCE)))) ) { if (io->ioflags & IO_NPC) { if ((i != 0) && (damages[j].source != 0) && validsource && (HaveCommonGroup(io, inter.iobj[damages[j].source]))) continue; EERIE_SPHERE sphere; sphere.origin.x = damages[j].pos.x; sphere.origin.y = damages[j].pos.y; sphere.origin.z = damages[j].pos.z; sphere.radius = damages[j].radius - 10.f; if (CheckIOInSphere(&sphere, i, IIS_NO_NOCOL)) { sub.x = io->pos.x; sub.y = io->pos.y - 60.f; sub.z = io->pos.z; dist = EEDistance3D(&damages[j].pos, &sub); if (damages[j].type & DAMAGE_TYPE_FIELD) { if (ARXTime > io->collide_door_time + 500) { EVENT_SENDER = NULL; io->collide_door_time = ARXTimeUL(); char param[64]; param[0] = 0; if (damages[j].type & DAMAGE_TYPE_FIRE) strcpy(param, "FIRE"); if (damages[j].type & DAMAGE_TYPE_COLD) strcpy(param, "COLD"); SendIOScriptEvent(io, SM_COLLIDE_FIELD, param, NULL); } } switch (damages[j].area) { case DAMAGE_AREA: { float ratio = (damages[j].radius - dist) * divradius; if (ratio > 1.f) ratio = 1.f; else if (ratio < 0.f) ratio = 0.f; dmg = dmg * ratio + 1.f; } break; case DAMAGE_AREAHALF: { float ratio = (damages[j].radius - (dist * DIV2)) * divradius; if (ratio > 1.f) ratio = 1.f; else if (ratio < 0.f) ratio = 0.f; dmg = dmg * ratio + 1.f; } break; } if (dmg <= 0.f) continue; if (damages[j].flags & DAMAGE_FLAG_ADD_VISUAL_FX) { if ((inter.iobj[i]->ioflags & IO_NPC) && (inter.iobj[i]->_npcdata->life > 0.f)) ARX_DAMAGES_AddVisual(&damages[j], &sub, dmg, inter.iobj[i]); } if (damages[j].type & DAMAGE_TYPE_DRAIN_MANA) { float manadrained; if (i == 0) { manadrained = __min(dmg, player.mana); player.mana -= manadrained; } else { manadrained = dmg; if ((io) && (io->_npcdata)) { manadrained = __min(dmg, io->_npcdata->mana); io->_npcdata->mana -= manadrained; } } if (damages[j].source == 0) { player.mana = __min(player.mana + manadrained, player.Full_maxmana); } else { if (ValidIONum(damages[j].source) && (inter.iobj[damages[j].source]->_npcdata)) { inter.iobj[damages[j].source]->_npcdata->mana = __min(inter.iobj[damages[j].source]->_npcdata->mana + manadrained, inter.iobj[damages[j].source]->_npcdata->maxmana); } } } else { float damagesdone; if (i == 0) { if (damages[j].type & DAMAGE_TYPE_POISON) { if (rnd() * 100.f > player.resist_poison) { // Failed Saving Throw damagesdone = dmg; player.poison += damagesdone; } else damagesdone = 0; } else { if ((damages[j].type & DAMAGE_TYPE_MAGICAL) && (!(damages[j].type & DAMAGE_TYPE_FIRE)) && (!(damages[j].type & DAMAGE_TYPE_COLD)) ) { dmg -= player.Full_resist_magic * DIV100 * dmg; dmg = __max(0, dmg); } if (damages[j].type & DAMAGE_TYPE_FIRE) { dmg = ARX_SPELLS_ApplyFireProtection(inter.iobj[0], dmg); ARX_DAMAGES_IgnitIO(inter.iobj[0], dmg); } if (damages[j].type & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(inter.iobj[0], dmg); } damagesdone = ARX_DAMAGES_DamagePlayer(dmg, damages[j].type, damages[j].source, &damages[j].pos); } } else { if ((inter.iobj[i]->ioflags & IO_NPC) && (damages[j].type & DAMAGE_TYPE_POISON)) { if (rnd() * 100.f > inter.iobj[i]->_npcdata->resist_poison) { // Failed Saving Throw damagesdone = dmg; inter.iobj[i]->_npcdata->poisonned += damagesdone; } else damagesdone = 0; } else { if (damages[j].type & DAMAGE_TYPE_FIRE) { dmg = ARX_SPELLS_ApplyFireProtection(inter.iobj[i], dmg); ARX_DAMAGES_IgnitIO(inter.iobj[i], dmg); } if ((damages[j].type & DAMAGE_TYPE_MAGICAL) && (!(damages[j].type & DAMAGE_TYPE_FIRE)) && (!(damages[j].type & DAMAGE_TYPE_COLD)) ) { dmg -= inter.iobj[i]->_npcdata->resist_magic * DIV100 * dmg; dmg = __max(0, dmg); } if (damages[j].type & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(inter.iobj[i], dmg); } damagesdone = ARX_DAMAGES_DamageNPC(inter.iobj[i], dmg, damages[j].source, 1, &damages[j].pos); } if ((damagesdone > 0) && (damages[j].flags & DAMAGE_SPAWN_BLOOD)) { EERIE_3D vector; vector.x = damages[j].pos.x - inter.iobj[i]->pos.x; vector.y = (damages[j].pos.y - inter.iobj[i]->pos.y) * DIV2; vector.z = damages[j].pos.z - inter.iobj[i]->pos.z; float t = 1.f / TRUEVector_Magnitude(&vector); vector.x *= t; vector.y *= t; vector.z *= t; ARX_PARTICLES_Spawn_Blood(&damages[j].pos, &vector, damagesdone, damages[j].source); } } if (damages[j].type & DAMAGE_TYPE_DRAIN_LIFE) { if (ValidIONum(damages[j].source)) ARX_DAMAGES_HealInter(inter.iobj[damages[j].source], damagesdone); } } } } else if ((io->ioflags & IO_FIX) && (!(damages[j].type & DAMAGE_TYPE_NO_FIX))) { EERIE_SPHERE sphere; sphere.origin.x = damages[j].pos.x; sphere.origin.y = damages[j].pos.y; sphere.origin.z = damages[j].pos.z; sphere.radius = damages[j].radius + 15.f; if (CheckIOInSphere(&sphere, i)) { ARX_DAMAGES_DamageFIX(io, dmg, damages[j].source, 1); } } } } if (damages[j].duration == -1) damages[j].exist = FALSE; else if (tim > damages[j].start_time + damages[j].duration) damages[j].exist = FALSE; } } void ARX_DAMAGES_UpdateAll() { for (long j = 0; j < MAX_DAMAGES; j++) ARX_DAMAGES_UpdateDamage(j, ARXTime); } BOOL SphereInIO(INTERACTIVE_OBJ * io, EERIE_3D * pos, float radius) { if (io == NULL) return FALSE; if (io->obj == NULL) return FALSE; long step; if (io->obj->nbvertex < 150) step = 1; else if (io->obj->nbvertex < 300) step = 2; else if (io->obj->nbvertex < 600) step = 4; else if (io->obj->nbvertex < 1200) step = 6; else step = 7; for (long i = 0; i < io->obj->nbvertex; i += step) { if (EEDistance3D(pos, &io->obj->vertexlist3[i].v) <= radius) { return TRUE; } } return FALSE; } BOOL ARX_DAMAGES_TryToDoDamage(EERIE_3D * pos, float dmg, float radius, long source) { BOOL ret = FALSE; for (long i = 0; i < inter.nbmax; i++) { INTERACTIVE_OBJ * io = inter.iobj[i]; if (io != NULL) if (inter.iobj[i]->GameFlags & GFLAG_ISINTREATZONE) if (io->show == SHOW_FLAG_IN_SCENE) if (source != i) { float threshold; float rad = radius + 5.f; if (io->ioflags & IO_FIX) { threshold = 510; rad += 10.f; } else if (io->ioflags & IO_NPC) threshold = 250; else threshold = 350; if (EEDistance3D(pos, &io->pos) < threshold) if (SphereInIO(io, pos, rad)) { if (io->ioflags & IO_NPC) { if (ValidIONum(source)) ARX_EQUIPMENT_ComputeDamages(inter.iobj[source], NULL, io, 1.f); ret = TRUE; } if (io->ioflags & IO_FIX) { ARX_DAMAGES_DamageFIX(io, dmg, source, 0); ret = TRUE; } } } } return ret; } // mode=1 ON mode=0 OFF // flag & 1 no lights; // flag & 2 Only affects small sources //************************************************************************************* //************************************************************************************* void CheckForIgnition(EERIE_3D * pos, float radius, long mode, long flag) { long i; float dist; if (!(flag & 1)) for (i = 0; i < MAX_LIGHTS; i++) { EERIE_LIGHT * el = GLight[i]; if (el == NULL) continue; if ((el->extras & EXTRAS_EXTINGUISHABLE) && ((el->extras & EXTRAS_SEMIDYNAMIC) || (el->extras & EXTRAS_SPAWNFIRE) || (el->extras & EXTRAS_SPAWNSMOKE))) { if ((el->extras & EXTRAS_FIREPLACE) && (flag & 2)) continue; dist = EEDistance3D(pos, &el->pos); if (dist <= radius) { if (mode) { if (!(el->extras & EXTRAS_NO_IGNIT)) el->status = 1; } else { el->status = 0; } } } } for (i = 0; i < inter.nbmax; i++) { INTERACTIVE_OBJ * io = inter.iobj[i]; if ((io) && (io->show == 1) && (io->obj) && !(io->ioflags & IO_UNDERWATER) && (io->obj->fastaccess.fire >= 0) ) { float dist = EEDistance3D(pos, &io->obj->vertexlist3[io->obj->fastaccess.fire].v); if (dist < radius) { if ((mode) && (io->ignition <= 0) && (io->obj->fastaccess.fire >= 0)) { io->ignition = 1; } else if ((!mode) && (io->ignition > 0)) { if (io->obj->fastaccess.fire >= 0) { io->ignition = 0; if (ValidDynLight(io->ignit_light)) DynLight[io->ignit_light].exist = 0; io->ignit_light = -1; if (io->ignit_sound != ARX_SOUND_INVALID_RESOURCE) { ARX_SOUND_Stop(io->ignit_sound); io->ignit_sound = ARX_SOUND_INVALID_RESOURCE; } } else if (!(flag & 2)) io->ignition = 0.00001f; } } } } } void PushPlayer(EERIE_3D * pos, float intensity) { } //************************************************************************************* //************************************************************************************* BOOL DoSphericDamage(EERIE_3D * pos, float dmg, float radius, long flags, long typ, long numsource) { BOOL damagesdone = FALSE; EERIE_3D sub; sub.x = player.pos.x; sub.y = player.pos.y + 90.f; sub.z = player.pos.z; float dist; dist = EEDistance3D(pos, &sub); if (radius <= 0.f) return damagesdone; float rad = 1.f / radius; long validsource = ValidIONum(numsource); for (long i = 0; i < inter.nbmax; i++) { INTERACTIVE_OBJ * ioo = inter.iobj[i]; if ((ioo) && (i != numsource) && (ioo->obj)) { if ((i != 0) && (numsource != 0) && validsource && (HaveCommonGroup(ioo, inter.iobj[numsource]))) continue; if ((ioo->ioflags & IO_CAMERA) || (ioo->ioflags & IO_MARKER)) continue; long count = 0; long count2 = 0; float mindist = FLT_MAX; for (long k = 0; k < ioo->obj->nbvertex; k += 1) { if (ioo->obj->nbvertex < 120) { for (long kk = 0; kk < ioo->obj->nbvertex; kk += 1) { if (kk != k) { EERIE_3D posi; posi.x = (inter.iobj[i]->obj->vertexlist3[k].v.x + inter.iobj[i]->obj->vertexlist3[kk].v.x) * DIV2; posi.y = (inter.iobj[i]->obj->vertexlist3[k].v.y + inter.iobj[i]->obj->vertexlist3[kk].v.y) * DIV2; posi.z = (inter.iobj[i]->obj->vertexlist3[k].v.z + inter.iobj[i]->obj->vertexlist3[kk].v.z) * DIV2; dist = EEDistance3D(pos, &posi); if (dist <= radius) { count2++; if (dist < mindist) mindist = dist; } } } } { dist = EEDistance3D(pos, &inter.iobj[i]->obj->vertexlist3[k].v); if (dist <= radius) { count++; if (dist < mindist) mindist = dist; } } } float ratio = ((float)count / ((float)ioo->obj->nbvertex * DIV2)); if (count2 > count) ratio = ((float)count2 / ((float)ioo->obj->nbvertex * DIV2)); if (ratio > 2.f) ratio = 2.f; if (ioo->ioflags & IO_NPC) { if (mindist <= radius + 30.f) { switch (flags) { case DAMAGE_AREA: dmg = dmg * (radius + 30 - mindist) * rad; break; case DAMAGE_AREAHALF: dmg = dmg * (radius + 30 - mindist * DIV2) * rad; break; } if (i == 0) { if (typ & DAMAGE_TYPE_FIRE) { dmg = ARX_SPELLS_ApplyFireProtection(ioo, dmg); ARX_DAMAGES_IgnitIO(inter.iobj[0], dmg); } if (typ & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(ioo, dmg); } ARX_DAMAGES_DamagePlayer(dmg, typ, numsource, pos); ARX_DAMAGES_DamagePlayerEquipment(dmg); EERIE_3D vector; float div = 1.f / dist; vector.x = (sub.x - pos->x) * div; vector.y = (sub.y - pos->y) * div; vector.z = (sub.z - pos->z) * div; PushPlayer(&vector, (radius - dist) / radius); } else { if (typ & DAMAGE_TYPE_FIRE) { dmg = ARX_SPELLS_ApplyFireProtection(ioo, dmg * ratio); ARX_DAMAGES_IgnitIO(ioo, dmg); } if (typ & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(ioo, dmg * ratio); } ARX_DAMAGES_DamageNPC(ioo, dmg * ratio, numsource, 1, pos); } if (dmg > 1) damagesdone = TRUE; } } else { if (mindist <= radius + 30.f) { if (typ & DAMAGE_TYPE_FIRE) { dmg = ARX_SPELLS_ApplyFireProtection(ioo, dmg * ratio); ARX_DAMAGES_IgnitIO(inter.iobj[i], dmg); } if (typ & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(ioo, dmg * ratio); } if (inter.iobj[i]->ioflags & IO_FIX) ARX_DAMAGES_DamageFIX(inter.iobj[i], dmg * ratio, numsource, 1); if (dmg > 0.2f) damagesdone = TRUE; } } } } if (typ & DAMAGE_TYPE_FIRE) CheckForIgnition(pos, radius, 1); return damagesdone; } void ARX_DAMAGES_DurabilityRestore(INTERACTIVE_OBJ * io, float percent) { if (!io) return; if (io->durability <= 0) return; if (io->durability == io->max_durability) return; if (percent >= 100.f) { io->durability = io->max_durability; } else { float ratio = percent * DIV100; float to_restore = (io->max_durability - io->durability) * ratio; float v = rnd() * 100.f - percent; if (v <= 0.f) { float mloss = 1.f; if (io->ioflags & IO_ITEM) { ARX_CHECK_LONG(io->_itemdata->price / io->max_durability); io->_itemdata->price -= ARX_CLEAN_WARN_CAST_LONG(io->_itemdata->price / io->max_durability); } io->max_durability -= mloss; } else { if (v > 50.f) v = 50.f; v *= DIV100; float mloss = io->max_durability * v; if (io->ioflags & IO_ITEM) { io->_itemdata->price -= ARX_CLEAN_WARN_CAST_LONG(io->_itemdata->price * v); } io->max_durability -= mloss; } io->durability += to_restore; if (io->durability > io->max_durability) io->durability = io->max_durability; if (io->max_durability <= 0.f) ARX_DAMAGES_DurabilityLoss(io, 100); } } void ARX_DAMAGES_DurabilityCheck(INTERACTIVE_OBJ * io, float ratio) { if (!io) return; if (rnd() * 100.f > io->durability) { ARX_DAMAGES_DurabilityLoss(io, ratio); } } void ARX_DAMAGES_DurabilityLoss(INTERACTIVE_OBJ * io, float loss) { if (!io) return; io->durability -= loss; if (io->durability <= 0) { SendIOScriptEvent(io, SM_BREAK, "", NULL); } } void ARX_DAMAGES_DamagePlayerEquipment(float damages) { float ratio = damages * DIV20; if (ratio > 1.f) ratio = 1.f; for (long i = 0; i < MAX_EQUIPED; i++) { if (player.equiped[i] != 0) { INTERACTIVE_OBJ * todamage = inter.iobj[player.equiped[i]]; ARX_DAMAGES_DurabilityCheck(todamage, ratio); } } } float ARX_DAMAGES_ComputeRepairPrice(INTERACTIVE_OBJ * torepair, INTERACTIVE_OBJ * blacksmith) { if ((!torepair) || (!blacksmith)) return -1.f; if (!(torepair->ioflags & IO_ITEM)) return -1.f; if (torepair->max_durability <= 0.f) return -1.f; if (torepair->durability == torepair->max_durability) return -1.f; float ratio = (torepair->max_durability - torepair->durability) / torepair->max_durability; float price = torepair->_itemdata->price * ratio; if (blacksmith->shop_multiply != 0.f) price *= blacksmith->shop_multiply; if ((price > 0.f) && (price < 1.f)) price = 1.f; return price; }