// 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; }
//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); } } } } } } }
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 = 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); } } } } } } }