float ARX_DAMAGES_DealDamages(EntityHandle target, float dmg, EntityHandle source, DamageType flags, Vec3f * pos) { if(!ValidIONum(target) || !ValidIONum(source)) return 0; Entity * io_target = entities[target]; Entity * io_source = entities[source]; float damagesdone; if(flags & DAMAGE_TYPE_PER_SECOND) { dmg = dmg * framedelay * ( 1.0f / 1000 ); } if(target == 0) { if(flags & DAMAGE_TYPE_POISON) { if(rnd() * 100.f > player.m_misc.resistPoison) { damagesdone = dmg; player.poison += damagesdone; } else { damagesdone = 0; } } else { 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); } } 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 * ( 1.0f / 2 )); if((flags & DAMAGE_TYPE_MAGICAL) && !(flags & (DAMAGE_TYPE_FIRE | DAMAGE_TYPE_COLD))) { damagesdone -= player.m_miscFull.resistMagic * ( 1.0f / 100 ) * damagesdone; damagesdone = std::max(0.0f, 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; } } else { 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, true, pos); } } 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 * ( 1.0f / 2 )); if((flags & DAMAGE_TYPE_MAGICAL) && !(flags & (DAMAGE_TYPE_FIRE | DAMAGE_TYPE_COLD))) { damagesdone -= io_target->_npcdata->resist_magic * ( 1.0f / 100 ) * damagesdone; damagesdone = std::max(0.0f, damagesdone); } return damagesdone; } } return 0; }
// source = -1 no source but valid pos // source = 0 player // source > 0 IO static void ARX_DAMAGES_UpdateDamage(DamageHandle j, float tim) { DAMAGE_INFO & damage = damages[j]; if(!damage.exist) { return; } if(damage.params.flags & DAMAGE_FLAG_FOLLOW_SOURCE) { if(damage.params.source == PlayerEntityHandle) { damage.params.pos = player.pos; } else if (ValidIONum(damage.params.source)) { damage.params.pos = entities[damage.params.source]->pos; } } float dmg; if(damage.params.flags & DAMAGE_NOT_FRAME_DEPENDANT) { dmg = damage.params.damages; } else if(damage.params.duration == -1) { dmg = damage.params.damages; } else { float FD = (float)framedelay; if(tim > damage.start_time + damage.params.duration) { FD -= damage.start_time + damage.params.duration - tim; } dmg = damage.params.damages * FD * ( 1.0f / 1000 ); } bool validsource = ValidIONum(damage.params.source); float divradius = 1.f / damage.params.radius; // checking for IO damages for(size_t i = 0; i < entities.size(); i++) { const EntityHandle handle = EntityHandle(i); Entity * io = entities[handle]; if(io && (io->gameFlags & GFLAG_ISINTREATZONE) && (io->show == SHOW_FLAG_IN_SCENE) && (damage.params.source != long(i) || (damage.params.source == long(i) && !(damage.params.flags & DAMAGE_FLAG_DONT_HURT_SOURCE))) ){ if(io->ioflags & IO_NPC) { if( i != 0 && damage.params.source != PlayerEntityHandle && validsource && HaveCommonGroup(io, entities[damage.params.source]) ) { continue; } Sphere sphere; sphere.origin = damage.params.pos; sphere.radius = damage.params.radius - 10.f; if(CheckIOInSphere(sphere, EntityHandle(i), true)) { Vec3f sub = io->pos + Vec3f(0.f, -60.f, 0.f); float dist = fdist(damage.params.pos, sub); if(damage.params.type & DAMAGE_TYPE_FIELD) { if(float(arxtime) > io->collide_door_time + 500) { EVENT_SENDER = NULL; io->collide_door_time = (unsigned long)(arxtime); char param[64]; param[0] = 0; if(damage.params.type & DAMAGE_TYPE_FIRE) strcpy(param, "fire"); if(damage.params.type & DAMAGE_TYPE_COLD) strcpy(param, "cold"); SendIOScriptEvent(io, SM_COLLIDE_FIELD, param); } } switch(damage.params.area) { case DAMAGE_AREA: { float ratio = (damage.params.radius - dist) * divradius; ratio = glm::clamp(ratio, 0.f, 1.f); dmg = dmg * ratio + 1.f; } break; case DAMAGE_AREAHALF: { float ratio = (damage.params.radius - (dist * ( 1.0f / 2 ))) * divradius; ratio = glm::clamp(ratio, 0.f, 1.f); dmg = dmg * ratio + 1.f; } break; case DAMAGE_FULL: break; } if(dmg <= 0.f) continue; if( (damage.params.flags & DAMAGE_FLAG_ADD_VISUAL_FX) && (entities[handle]->ioflags & IO_NPC) && (entities[handle]->_npcdata->lifePool.current > 0.f) ) { ARX_DAMAGES_AddVisual(&damage, &sub, dmg, entities[handle]); } if(damage.params.type & DAMAGE_TYPE_DRAIN_MANA) { float manadrained; if(i == 0) { manadrained = std::min(dmg, player.manaPool.current); player.manaPool.current -= manadrained; } else { manadrained = dmg; if(io && io->_npcdata) { manadrained = std::min(dmg, io->_npcdata->manaPool.current); io->_npcdata->manaPool.current -= manadrained; } } if (damage.params.source == PlayerEntityHandle) { player.manaPool.current = std::min(player.manaPool.current + manadrained, player.Full_maxmana); } else { if(ValidIONum(damage.params.source) && (entities[damage.params.source]->_npcdata)) { entities[damage.params.source]->_npcdata->manaPool.current = std::min(entities[damage.params.source]->_npcdata->manaPool.current + manadrained, entities[damage.params.source]->_npcdata->manaPool.max); } } } else { float damagesdone; // TODO copy-paste if(i == 0) { if(damage.params.type & DAMAGE_TYPE_POISON) { if(rnd() * 100.f > player.m_misc.resistPoison) { // Failed Saving Throw damagesdone = dmg; player.poison += damagesdone; } else { damagesdone = 0; } } else { if( (damage.params.type & DAMAGE_TYPE_MAGICAL) && !(damage.params.type & DAMAGE_TYPE_FIRE) && !(damage.params.type & DAMAGE_TYPE_COLD) ) { dmg -= player.m_miscFull.resistMagic * ( 1.0f / 100 ) * dmg; dmg = std::max(0.0f, dmg); } if(damage.params.type & DAMAGE_TYPE_FIRE) { dmg = ARX_SPELLS_ApplyFireProtection(entities.player(), dmg); ARX_DAMAGES_IgnitIO(entities.player(), dmg); } if(damage.params.type & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(entities.player(), dmg); } damagesdone = ARX_DAMAGES_DamagePlayer(dmg, damage.params.type, damage.params.source); } } else { if( (entities[handle]->ioflags & IO_NPC) && (damage.params.type & DAMAGE_TYPE_POISON) ) { if(rnd() * 100.f > entities[handle]->_npcdata->resist_poison) { // Failed Saving Throw damagesdone = dmg; entities[handle]->_npcdata->poisonned += damagesdone; } else { damagesdone = 0; } } else { if(damage.params.type & DAMAGE_TYPE_FIRE) { dmg = ARX_SPELLS_ApplyFireProtection(entities[handle], dmg); ARX_DAMAGES_IgnitIO(entities[handle], dmg); } if( (damage.params.type & DAMAGE_TYPE_MAGICAL) && !(damage.params.type & DAMAGE_TYPE_FIRE) && !(damage.params.type & DAMAGE_TYPE_COLD) ) { dmg -= entities[handle]->_npcdata->resist_magic * ( 1.0f / 100 ) * dmg; dmg = std::max(0.0f, dmg); } if(damage.params.type & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(entities[handle], dmg); } damagesdone = ARX_DAMAGES_DamageNPC(entities[handle], dmg, damage.params.source, true, &damage.params.pos); } if(damagesdone > 0 && (damage.params.flags & DAMAGE_SPAWN_BLOOD)) { ARX_PARTICLES_Spawn_Blood(&damage.params.pos, damagesdone, damage.params.source); } } if(damage.params.type & DAMAGE_TYPE_DRAIN_LIFE) { if(ValidIONum(damage.params.source)) ARX_DAMAGES_HealInter(entities[damage.params.source], damagesdone); } } } } else if((io->ioflags & IO_FIX) && !(damage.params.type & DAMAGE_TYPE_NO_FIX)) { Sphere sphere; sphere.origin = damage.params.pos; sphere.radius = damage.params.radius + 15.f; if(CheckIOInSphere(sphere, EntityHandle(i))) { ARX_DAMAGES_DamageFIX(io, dmg, damage.params.source, true); } } } } if(damage.params.duration == -1) damage.exist = false; else if(tim > damage.start_time + damage.params.duration) damage.exist = false; }
bool DoSphericDamage(const Vec3f & pos, float dmg, float radius, DamageArea flags, DamageType typ, EntityHandle numsource) { bool damagesdone = false; if(radius <= 0.f) return damagesdone; float rad = 1.f / radius; bool validsource = ValidIONum(numsource); for(size_t i = 0; i < entities.size(); i++) { const EntityHandle handle = EntityHandle(i); Entity * ioo = entities[handle]; if(!ioo || long(i) == numsource || !ioo->obj) continue; if ((i != 0) && (numsource != PlayerEntityHandle) && validsource && (HaveCommonGroup(ioo, entities[numsource]))) continue; if((ioo->ioflags & IO_CAMERA) || (ioo->ioflags & IO_MARKER)) continue; long count = 0; long count2 = 0; float mindist = std::numeric_limits<float>::max(); for(size_t k = 0; k < ioo->obj->vertexlist.size(); k += 1) { if(ioo->obj->vertexlist.size() < 120) { for(size_t kk = 0; kk < ioo->obj->vertexlist.size(); kk += 1) { if(kk != k) { Vec3f posi = (entities[handle]->obj->vertexlist3[k].v + entities[handle]->obj->vertexlist3[kk].v) * 0.5f; float dist = fdist(pos, posi); if(dist <= radius) { count2++; if(dist < mindist) mindist = dist; } } } } { float dist = fdist(pos, entities[handle]->obj->vertexlist3[k].v); if(dist <= radius) { count++; if(dist < mindist) mindist = dist; } } } float ratio = ((float)count / ((float)ioo->obj->vertexlist.size() * ( 1.0f / 2 ))); if(count2 > count) ratio = ((float)count2 / ((float)ioo->obj->vertexlist.size() * ( 1.0f / 2 ))); 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 * ( 1.0f / 2 )) * rad; break; case DAMAGE_FULL: break; } if(i == 0) { if(typ & DAMAGE_TYPE_FIRE) { dmg = ARX_SPELLS_ApplyFireProtection(ioo, dmg); ARX_DAMAGES_IgnitIO(entities.player(), dmg); } if(typ & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(ioo, dmg); } ARX_DAMAGES_DamagePlayer(dmg, typ, numsource); ARX_DAMAGES_DamagePlayerEquipment(dmg); } 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, true, &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(entities[handle], dmg); } if(typ & DAMAGE_TYPE_COLD) { dmg = ARX_SPELLS_ApplyColdProtection(ioo, dmg * ratio); } if(entities[handle]->ioflags & IO_FIX) ARX_DAMAGES_DamageFIX(entities[handle], dmg * ratio, numsource, true); if(dmg > 0.2f) damagesdone = true; } } } if (typ & DAMAGE_TYPE_FIRE) CheckForIgnition(pos, radius, 1); return damagesdone; }
//TODO Move somewhere else void Cedric_ApplyLightingFirstPartRefactor(Entity *io) { if(!io) return; io->special_color = Color3f::white; float poisonpercent = 0.f; float trappercent = 0.f; float secretpercent = 0.f; if((io->ioflags & IO_NPC) && io->_npcdata->poisonned > 0.f) { poisonpercent = io->_npcdata->poisonned * ( 1.0f / 20 ); if(poisonpercent > 1.f) poisonpercent = 1.f; } if((io->ioflags & IO_ITEM) && io->poisonous > 0.f && io->poisonous_count) { poisonpercent = (float)io->poisonous * (1.0f / 20); if(poisonpercent > 1.f) poisonpercent = 1.f; } if((io->ioflags & IO_FIX) && io->_fixdata->trapvalue > -1) { trappercent = player.TRAP_DETECT - (float)io->_fixdata->trapvalue; if(trappercent > 0.f) { trappercent = 0.6f + trappercent * ( 1.0f / 100 ); trappercent = clamp(trappercent, 0.6f, 1.f); } } if((io->ioflags & IO_FIX) && io->secretvalue > -1) { secretpercent = player.TRAP_SECRET - (float)io->secretvalue; if(secretpercent > 0.f) { secretpercent = 0.6f + secretpercent * ( 1.0f / 100 ); secretpercent = clamp(secretpercent, 0.6f, 1.f); } } if(poisonpercent > 0.f) { io->special_color = Color3f::green; } if(trappercent > 0.f) { io->special_color = Color3f(trappercent, 1.f - trappercent, 1.f - trappercent); } if(secretpercent > 0.f) { io->special_color = Color3f(1.f - secretpercent, 1.f - secretpercent, secretpercent); } if(io->ioflags & IO_FREEZESCRIPT) { io->special_color = Color3f::blue; } if(io->sfx_flag & SFX_TYPE_YLSIDE_DEATH) { if(io->show == SHOW_FLAG_TELEPORTING) { float fTime = io->sfx_time + framedelay; io->sfx_time = checked_range_cast<unsigned long>(fTime); if (io->sfx_time >= (unsigned long)(arxtime)) io->sfx_time = (unsigned long)(arxtime); } else { float elapsed = float(arxtime) - io->sfx_time; if(elapsed > 0.f) { if(elapsed < 3000.f) { // 5 seconds to red float ratio = elapsed * (1.0f / 3000); io->special_color = Color3f(1.f, 1.f - ratio, 1.f - ratio); AddRandomSmoke(io, 1); } else if(elapsed < 6000.f) { // 5 seconds to White float ratio = (elapsed - 3000.f) * (1.0f / 3000); io->special_color = Color3f(1.f, ratio, ratio); AddRandomSmoke(io, 2); } else { // SFX finish io->sfx_time = 0; if(io->ioflags & IO_NPC) { MakePlayerAppearsFX(io); AddRandomSmoke(io, 50); Color3f rgb = io->_npcdata->blood_color.to<float>(); EERIE_SPHERE sp; sp.origin = io->pos; sp.radius = 200.f; long count = 6; while(count--) { SpawnGroundSplat(&sp, &rgb, rnd() * 30.f + 30.f, 1); sp.origin.y -= rnd() * 150.f; ARX_PARTICLES_Spawn_Splat(sp.origin, 200.f, io->_npcdata->blood_color); sp.origin.x = io->pos.x + rnd() * 200.f - 100.f; sp.origin.y = io->pos.y + rnd() * 20.f - 10.f; sp.origin.z = io->pos.z + rnd() * 200.f - 100.f; sp.radius = rnd() * 100.f + 100.f; } long nn = GetFreeDynLight(); if(nn >= 0) { DynLight[nn].exist = 1; DynLight[nn].intensity = 0.7f + 2.f * rnd(); DynLight[nn].fallend = 600.f; DynLight[nn].fallstart = 400.f; DynLight[nn].rgb.r = 1.0f; DynLight[nn].rgb.g = 0.8f; DynLight[nn].rgb.b = .0f; DynLight[nn].pos.x = io->pos.x; DynLight[nn].pos.y = io->pos.y - 80.f; DynLight[nn].pos.z = io->pos.z; DynLight[nn].duration = 600; } if(io->sfx_flag & SFX_TYPE_INCINERATE) { io->sfx_flag &= ~SFX_TYPE_INCINERATE; io->sfx_flag &= ~SFX_TYPE_YLSIDE_DEATH; long num = ARX_SPELLS_GetSpellOn(io, SPELL_INCINERATE); if(num < 0) num = ARX_SPELLS_GetSpellOn(io, SPELL_MASS_INCINERATE); if(num >= 0) { spells[num].tolive = 0; float damages = 20 * spells[num].caster_level; damages = ARX_SPELLS_ApplyFireProtection(io, damages); if (ValidIONum(spells[num].caster)) ARX_DAMAGES_DamageNPC(io, damages, spells[num].caster, 1, &entities[spells[num].caster]->pos); else ARX_DAMAGES_DamageNPC(io, damages, spells[num].caster, 1, &io->pos); ARX_SOUND_PlaySFX(SND_SPELL_FIRE_HIT, &io->pos); } } else { io->sfx_flag &= ~SFX_TYPE_YLSIDE_DEATH; ARX_INTERACTIVE_DestroyIOdelayed(io); } } } } } } }
//TODO Move somewhere else void Cedric_ApplyLightingFirstPartRefactor(Entity *io) { if(!io) return; io->special_color = Color3f::white; float poisonpercent = 0.f; float trappercent = 0.f; float secretpercent = 0.f; if((io->ioflags & IO_NPC) && io->_npcdata->poisonned > 0.f) { poisonpercent = io->_npcdata->poisonned * ( 1.0f / 20 ); if(poisonpercent > 1.f) poisonpercent = 1.f; } if((io->ioflags & IO_ITEM) && io->poisonous > 0.f && io->poisonous_count) { poisonpercent = io->poisonous * (1.0f / 20); if(poisonpercent > 1.f) poisonpercent = 1.f; } if((io->ioflags & IO_FIX) && io->_fixdata->trapvalue > -1) { trappercent = player.TRAP_DETECT - io->_fixdata->trapvalue; if(trappercent > 0.f) { trappercent = 0.6f + trappercent * ( 1.0f / 100 ); trappercent = glm::clamp(trappercent, 0.6f, 1.f); } } if((io->ioflags & IO_FIX) && io->secretvalue > -1) { secretpercent = player.TRAP_SECRET - io->secretvalue; if(secretpercent > 0.f) { secretpercent = 0.6f + secretpercent * ( 1.0f / 100 ); secretpercent = glm::clamp(secretpercent, 0.6f, 1.f); } } if(poisonpercent > 0.f) { io->special_color = Color3f::green; } if(trappercent > 0.f) { io->special_color = Color3f(trappercent, 1.f - trappercent, 1.f - trappercent); } if(secretpercent > 0.f) { io->special_color = Color3f(1.f - secretpercent, 1.f - secretpercent, secretpercent); } if(io->ioflags & IO_FREEZESCRIPT) { io->special_color = Color3f::blue; } if(io->sfx_flag & SFX_TYPE_YLSIDE_DEATH) { if(io->show == SHOW_FLAG_TELEPORTING) { io->sfx_time = io->sfx_time + ArxDurationMs(g_framedelay); if (io->sfx_time >= arxtime.now()) io->sfx_time = arxtime.now(); } else { const ArxDuration elapsed = arxtime.now() - io->sfx_time; if(elapsed > ArxDuration_ZERO) { if(elapsed < ArxDurationMs(3000)) { // 5 seconds to red float ratio = toMs(elapsed) * (1.0f / 3000); io->special_color = Color3f(1.f, 1.f - ratio, 1.f - ratio); io->highlightColor += Color3f(std::max(ratio - 0.5f, 0.f), 0.f, 0.f) * 255; AddRandomSmoke(io, 1); } else if(elapsed < ArxDurationMs(6000)) { // 5 seconds to White float ratio = toMs(elapsed) * (1.0f / 3000); io->special_color = Color3f::red; io->highlightColor += Color3f(std::max(ratio - 0.5f, 0.f), 0.f, 0.f) * 255; AddRandomSmoke(io, 2); } else { // SFX finish io->sfx_time = ArxInstant_ZERO; if(io->ioflags & IO_NPC) { MakePlayerAppearsFX(io); AddRandomSmoke(io, 50); Color3f rgb = io->_npcdata->blood_color.to<float>(); Sphere sp = Sphere(io->pos, 200.f); long count = 6; while(count--) { Sphere splatSphere = Sphere(sp.origin, Random::getf(30.f, 60.f)); PolyBoomAddSplat(splatSphere, rgb, 1); sp.origin.y -= Random::getf(0.f, 150.f); ARX_PARTICLES_Spawn_Splat(sp.origin, 200.f, io->_npcdata->blood_color); sp.origin = io->pos + randomVec3f() * Vec3f(200.f, 20.f,200.f) - Vec3f(100.f, 10.f, 100.f); sp.radius = Random::getf(100.f, 200.f); } EERIE_LIGHT * light = dynLightCreate(); if(light) { light->intensity = Random::getf(0.7f, 2.7f); light->fallend = 600.f; light->fallstart = 400.f; light->rgb = Color3f(1.0f, 0.8f, 0.f); light->pos = io->pos + Vec3f(0.f, -80.f, 0.f); light->duration = ArxDurationMs(600); } if(io->sfx_flag & SFX_TYPE_INCINERATE) { io->sfx_flag &= ~SFX_TYPE_INCINERATE; io->sfx_flag &= ~SFX_TYPE_YLSIDE_DEATH; SpellBase * spell = spells.getSpellOnTarget(io->index(), SPELL_INCINERATE); if(!spell) spell = spells.getSpellOnTarget(io->index(), SPELL_MASS_INCINERATE); if(spell) { spells.endSpell(spell); float damages = 20 * spell->m_level; damages = ARX_SPELLS_ApplyFireProtection(io, damages); if (ValidIONum(spell->m_caster)) ARX_DAMAGES_DamageNPC(io, damages, spell->m_caster, true, &entities[spell->m_caster]->pos); else ARX_DAMAGES_DamageNPC(io, damages, spell->m_caster, true, &io->pos); ARX_SOUND_PlaySFX(SND_SPELL_FIRE_HIT, &io->pos); } } else { io->sfx_flag &= ~SFX_TYPE_YLSIDE_DEATH; ARX_INTERACTIVE_DestroyIOdelayed(io); } } } } } } }
float ARX_EQUIPMENT_ComputeDamages(Entity * io_source, Entity * io_target, float ratioaim, Vec3f * position) { EVENT_SENDER = io_source; SendIOScriptEvent(io_target, SM_AGGRESSION); if(!io_source || !io_target) return 0.f; if(!(io_target->ioflags & IO_NPC)) { if(io_target->ioflags & IO_FIX) { if (io_source == entities.player()) ARX_DAMAGES_DamageFIX(io_target, player.m_miscFull.damages, PlayerEntityHandle, false); else if (io_source->ioflags & IO_NPC) ARX_DAMAGES_DamageFIX(io_target, io_source->_npcdata->damages, io_source->index(), false); else ARX_DAMAGES_DamageFIX(io_target, 1, io_source->index(), false); } return 0.f; } float attack, ac, damages; float backstab = 1.f; std::string _wmat = "bare"; const std::string * wmat = &_wmat; std::string _amat = "flesh"; const std::string * amat = &_amat; bool critical = false; if(io_source == entities.player()) { if(ValidIONum(player.equiped[EQUIP_SLOT_WEAPON])) { Entity * io = entities[player.equiped[EQUIP_SLOT_WEAPON]]; if(io && !io->weaponmaterial.empty()) { wmat = &io->weaponmaterial; } } attack = player.m_miscFull.damages; if(Random::getf(0.f, 100.f) <= player.m_miscFull.criticalHit) { if(SendIOScriptEvent(io_source, SM_CRITICAL) != REFUSE) critical = true; } else critical = false; damages = attack * ratioaim; if(io_target->_npcdata->npcflags & NPCFLAG_BACKSTAB) { if(Random::getf(0.f, 100.f) <= player.m_skillFull.stealth * ( 1.0f / 2 )) { if(SendIOScriptEvent(io_source, SM_BACKSTAB) != REFUSE) backstab = 1.5f; } } } else { if(!(io_source->ioflags & IO_NPC)) // no NPC source... return 0.f; if(!io_source->weaponmaterial.empty()) { wmat = &io_source->weaponmaterial; } if(io_source->_npcdata->weapon) { Entity * iow = io_source->_npcdata->weapon; if(!iow->weaponmaterial.empty()) { wmat = &iow->weaponmaterial; } } attack = io_source->_npcdata->tohit; damages = io_source->_npcdata->damages * ratioaim * Random::getf(0.5f, 1.0f); SpellBase * spell = spells.getSpellOnTarget(io_source->index(), SPELL_CURSE); if(spell) { damages *= (1 - spell->m_level * 0.05f); } if(Random::getf(0.f, 100) <= io_source->_npcdata->critical) { if(SendIOScriptEvent(io_source, SM_CRITICAL) != REFUSE) critical = true; } else critical = false; if(Random::getf(0.f, 100.f) <= (float)io_source->_npcdata->backstab_skill) { if(SendIOScriptEvent(io_source, SM_BACKSTAB) != REFUSE) backstab = 1.5f; } } float absorb; if(io_target == entities.player()) { ac = player.m_miscFull.armorClass; absorb = player.m_skillFull.defense * ( 1.0f / 2 ); } else { ac = ARX_INTERACTIVE_GetArmorClass(io_target); absorb = io_target->_npcdata->absorb; SpellBase * spell = spells.getSpellOnTarget(io_target->index(), SPELL_CURSE); if(spell) { float modif = (1 - spell->m_level * 0.05f); ac *= modif; absorb *= modif; } } if(!io_target->armormaterial.empty()) { amat = &io_target->armormaterial; } if(io_target == entities.player()) { if(ValidIONum(player.equiped[EQUIP_SLOT_ARMOR])) { Entity * io = entities[player.equiped[EQUIP_SLOT_ARMOR]]; if(io && !io->armormaterial.empty()) { amat = &io->armormaterial; } } } float dmgs = damages * backstab; dmgs -= dmgs * absorb * 0.01f; Vec3f pos = io_target->pos; float power = std::min(1.f, dmgs * 0.05f) * 0.1f + 0.9f; ARX_SOUND_PlayCollision(*amat, *wmat, power, 1.f, pos, io_source); float chance = 100.f - (ac - attack); if(Random::getf(0.f, 100.f) > chance) { return 0.f; } ARX_SOUND_PlayCollision("flesh", *wmat, power, 1.f, pos, io_source); if(dmgs > 0.f) { if(critical) { dmgs *= 1.5f; } if(io_target == entities.player()) { // TODO should this be player.pos - player.baseOffset() = player.basePosition()? Vec3f ppos = io_source->pos - (player.pos + player.baseOffset()); ppos = glm::normalize(ppos); // Push the player PUSH_PLAYER_FORCE += ppos * -dmgs * Vec3f(1.0f / 11, 1.0f / 30, 1.0f / 11); ARX_DAMAGES_DamagePlayer(dmgs, 0, io_source->index()); ARX_DAMAGES_DamagePlayerEquipment(dmgs); } else { Vec3f ppos = io_source->pos - io_target->pos; ppos = glm::normalize(ppos); // Push the NPC io_target->forcedmove += ppos * -dmgs; Vec3f * pos = position ? position : &io_target->pos; ARX_DAMAGES_DamageNPC(io_target, dmgs, io_source->index(), false, pos); } } return dmgs; }
void ARX_THROWN_OBJECT_Manage(unsigned long time_offset) { for(size_t i = 0; i < MAX_THROWN_OBJECTS; i++) { ARX_THROWN_OBJECT *thrownObj = &Thrown[i]; if(!(thrownObj->flags & ATO_EXIST)) continue; { // Is Object Visible & Near ? EERIE_BKG_INFO * bkgData = getFastBackgroundData(thrownObj->position.x, thrownObj->position.z); if(!bkgData || !bkgData->treat) { continue; } // Now render object ! if(!thrownObj->obj) continue; TransformInfo t(thrownObj->position, thrownObj->quat); DrawEERIEInter_ModelTransform(thrownObj->obj, t); if((thrownObj->flags & ATO_FIERY) && (thrownObj->flags & ATO_MOVING) && !(thrownObj->flags & ATO_UNDERWATER)) { LightHandle id = GetFreeDynLight(); if(lightHandleIsValid(id) && framedelay > 0) { EERIE_LIGHT * light = lightHandleGet(id); light->intensity = 1.f; light->fallstart = 100.f; light->fallend = 240.f; light->rgb = Color3f(1.f, .8f, .6f) - randomColor3f() * Color3f(.2f, .2f, .2f); light->pos = thrownObj->position; light->ex_flaresize = 40.f; light->extras |= EXTRAS_FLARE; light->duration = static_cast<long>(framedelay * 0.5f); } float p = 3.f; while(p > 0.f) { p -= 0.5f; if(thrownObj->obj) { long notok = 10; std::vector<EERIE_FACE>::iterator it; while(notok-- > 0) { it = Random::getIterator(thrownObj->obj->facelist); arx_assert(it != thrownObj->obj->facelist.end()); if(it->facetype & POLY_HIDE) continue; notok = -1; } if(notok < 0) { Vec3f pos = thrownObj->obj->vertexlist3[it->vid[0]].v; createFireParticles(pos, 2, 180); } } } } if(thrownObj->pRuban) { thrownObj->pRuban->SetNextPosition(thrownObj->position); thrownObj->pRuban->Update(time_offset); } Vec3f original_pos; if(thrownObj->flags & ATO_MOVING) { long need_kill = 0; float mod = (float)time_offset * thrownObj->velocity; original_pos = thrownObj->position; thrownObj->position.x += thrownObj->vector.x * mod; float gmod = 1.f - thrownObj->velocity; gmod = glm::clamp(gmod, 0.f, 1.f); thrownObj->position.y += thrownObj->vector.y * mod + (time_offset * gmod); thrownObj->position.z += thrownObj->vector.z * mod; CheckForIgnition(Sphere(original_pos, 10.f), 0, 2); Vec3f wpos = thrownObj->position; wpos.y += 20.f; EERIEPOLY * ep = EEIsUnderWater(wpos); if(thrownObj->flags & ATO_UNDERWATER) { if(!ep) { thrownObj->flags &= ~ATO_UNDERWATER; ARX_SOUND_PlaySFX(SND_PLOUF, &thrownObj->position); } } else if(ep) { thrownObj->flags |= ATO_UNDERWATER; ARX_SOUND_PlaySFX(SND_PLOUF, &thrownObj->position); } // Check for collision MUST be done after DRAWING !!!! long nbact = thrownObj->obj->actionlist.size(); for(long j = 0; j < nbact; j++) { float rad = GetHitValue(thrownObj->obj->actionlist[j].name); if(rad == -1) continue; rad *= .5f; const Vec3f v0 = thrownObj->obj->vertexlist3[thrownObj->obj->actionlist[j].idx].v; Vec3f dest = original_pos + thrownObj->vector * 95.f; Vec3f orgn = original_pos - thrownObj->vector * 25.f; EERIEPOLY * ep = CheckArrowPolyCollision(orgn, dest); if(ep) { ARX_PARTICLES_Spawn_Spark(v0, 14, 0); CheckExp(i); if(ValidIONum(thrownObj->source)) ARX_NPC_SpawnAudibleSound(v0, entities[thrownObj->source]); thrownObj->flags &= ~ATO_MOVING; thrownObj->velocity = 0.f; std::string bkg_material = "earth"; if(ep && ep->tex && !ep->tex->m_texName.empty()) bkg_material = GetMaterialString(ep->tex->m_texName); if(ValidIONum(thrownObj->source)) { char weapon_material[64] = "dagger"; ARX_SOUND_PlayCollision(weapon_material, bkg_material, 1.f, 1.f, v0, entities[thrownObj->source]); } thrownObj->position = original_pos; j = 200; } else if(IsPointInField(v0)) { ARX_PARTICLES_Spawn_Spark(v0, 24, 0); CheckExp(i); if (ValidIONum(thrownObj->source)) ARX_NPC_SpawnAudibleSound(v0, entities[thrownObj->source]); thrownObj->flags &= ~ATO_MOVING; thrownObj->velocity = 0.f; if(ValidIONum(thrownObj->source)) { char weapon_material[64] = "dagger"; char bkg_material[64] = "earth"; ARX_SOUND_PlayCollision(weapon_material, bkg_material, 1.f, 1.f, v0, entities[thrownObj->source]); } thrownObj->position = original_pos; j = 200; need_kill = 1; } else { for(float precision = 0.5f; precision <= 6.f; precision += 0.5f) { Sphere sphere; sphere.origin = v0 + thrownObj->vector * precision * 4.5f; sphere.radius = rad + 3.f; std::vector<EntityHandle> sphereContent; if(CheckEverythingInSphere(sphere, thrownObj->source, EntityHandle::Invalid, sphereContent)) { for(size_t jj = 0; jj < sphereContent.size(); jj++) { if(ValidIONum(sphereContent[jj]) && sphereContent[jj] != thrownObj->source) { Entity * target = entities[sphereContent[jj]]; if(target->ioflags & IO_NPC) { Vec3f pos; Color color = Color::none; long hitpoint = -1; float curdist = 999999.f; for(size_t ii = 0 ; ii < target->obj->facelist.size() ; ii++) { if(target->obj->facelist[ii].facetype & POLY_HIDE) continue; short vid = target->obj->facelist[ii].vid[0]; float d = glm::distance(sphere.origin, target->obj->vertexlist3[vid].v); if(d < curdist) { hitpoint = target->obj->facelist[ii].vid[0]; curdist = d; } } if(hitpoint >= 0) { color = target->_npcdata->blood_color; pos = target->obj->vertexlist3[hitpoint].v; } if(thrownObj->source == 0) { float damages = ARX_THROWN_ComputeDamages(i, thrownObj->source, sphereContent[jj]); if(damages > 0.f) { arx_assert(hitpoint >= 0); if(target->ioflags & IO_NPC) { target->_npcdata->SPLAT_TOT_NB = 0; ARX_PARTICLES_Spawn_Blood2(original_pos, damages, color, target); } ARX_PARTICLES_Spawn_Blood2(pos, damages, color, target); ARX_DAMAGES_DamageNPC(target, damages, thrownObj->source, false, &pos); if(Random::getf(0.f, 100.f) > target->_npcdata->resist_poison) { target->_npcdata->poisonned += thrownObj->poisonous; } CheckExp(i); } else { ARX_PARTICLES_Spawn_Spark(v0, 14, 0); ARX_NPC_SpawnAudibleSound(v0, entities[thrownObj->source]); } } } else { // not NPC if(target->ioflags & IO_FIX) { if(ValidIONum(thrownObj->source)) ARX_DAMAGES_DamageFIX(target, 0.1f, thrownObj->source, false); } ARX_PARTICLES_Spawn_Spark(v0, 14, 0); if(ValidIONum(thrownObj->source)) ARX_NPC_SpawnAudibleSound(v0, entities[thrownObj->source]); CheckExp(i); } // Need to deal damages ! thrownObj->flags &= ~ATO_MOVING; thrownObj->velocity = 0.f; need_kill = 1; precision = 500.f; j = 200; } } } } } } if(need_kill) ARX_THROWN_OBJECT_Kill(i); } } } }