void BattleHazard::applyEffect(GameState &state) { auto tile = tileObject->getOwningTile(); auto set = tile->ownedObjects; for (auto &obj : set) { if (tile->ownedObjects.find(obj) == tile->ownedObjects.end()) { continue; } if (obj->getType() == TileObject::Type::Ground || obj->getType() == TileObject::Type::Feature || obj->getType() == TileObject::Type::LeftWall || obj->getType() == TileObject::Type::RightWall) { auto mp = std::static_pointer_cast<TileObjectBattleMapPart>(obj)->getOwner(); switch (damageType->effectType) { case DamageType::EffectType::Fire: if (mp->applyBurning(state, age)) { // Map part burned and provided fuel for our fire, keep the fire raging if (power < 0 && age > 10) { power = -power; } } break; default: switch (damageType->blockType) { case DamageType::BlockType::Gas: case DamageType::BlockType::Psionic: break; default: mp->applyDamage(state, power, damageType); break; } break; } } else if (obj->getType() == TileObject::Type::Item) { if (damageType->effectType == DamageType::EffectType::Fire) { // It was observed that armor resists fire damage deal to it // It also appears that damage is applied gradually at a rate of around 1 damage per // second // In tests, marsec armor (20% modifier) was hurt by fire but X-Com armor (10% // modifier) was not // If we apply damage once per turn, we apply 4 at once. Since we round down, 4 * // 20% will be rounded to 0 // while it should be 1. So we add 1 here auto i = std::static_pointer_cast<TileObjectBattleItem>(obj)->getItem(); i->applyDamage(state, 2 * TICKS_PER_HAZARD_UPDATE / TICKS_PER_SECOND + 1, damageType); } } else if (obj->getType() == TileObject::Type::Unit) { StateRef<BattleUnit> u = { &state, std::static_pointer_cast<TileObjectBattleUnit>(obj)->getUnit()->id}; // Determine direction of hit Vec3<float> velocity = -position; velocity -= Vec3<float>{0.5f, 0.5f, 0.5f}; velocity += u->position; if (velocity.x == 0.0f && velocity.y == 0.0f) { velocity.z = 1.0f; } // Determine wether to hit head, legs or torso auto cposition = u->position; // Hit torso if (sqrtf(velocity.x * velocity.x + velocity.y * velocity.y) > std::abs(velocity.z)) { cposition.z += (float)u->getCurrentHeight() / 2.0f / 40.0f; } // Hit head else if (velocity.z < 0) { cposition.z += (float)u->getCurrentHeight() / 40.0f; } else { // Legs are defeault already } // Apply u->applyDamage(state, power, damageType, u->determineBodyPartHit(damageType, cposition, velocity), DamageSource::Hazard); } } }
void BattleHazard::applyEffect(GameState &state) { auto tile = tileObject->getOwningTile(); auto set = tile->ownedObjects; for (auto obj : set) { if (tile->ownedObjects.find(obj) == tile->ownedObjects.end()) { continue; } if (obj->getType() == TileObject::Type::Ground || obj->getType() == TileObject::Type::Feature || obj->getType() == TileObject::Type::LeftWall || obj->getType() == TileObject::Type::RightWall) { auto mp = std::static_pointer_cast<TileObjectBattleMapPart>(obj)->getOwner(); switch (damageType->effectType) { case DamageType::EffectType::Fire: LogWarning("Set map part on fire!"); break; default: mp->applyDamage(state, power, damageType); break; } } else if (obj->getType() == TileObject::Type::Unit) { StateRef<BattleUnit> u = { &state, std::static_pointer_cast<TileObjectBattleUnit>(obj)->getUnit()->id}; switch (damageType->effectType) { case DamageType::EffectType::Fire: LogWarning("Set unit on fire!"); break; default: { // Determine direction of hit Vec3<float> velocity = -position; velocity -= Vec3<float>{0.5f, 0.5f, 0.5f}; velocity += u->position; if (velocity.x == 0.0f && velocity.y == 0.0f) { velocity.z = 1.0f; } // Determine wether to hit head, legs or torso auto cposition = u->position; // Hit torso if (sqrtf(velocity.x * velocity.x + velocity.y * velocity.y) > std::abs(velocity.z)) { cposition.z += (float)u->getCurrentHeight() / 2.0f / 40.0f; } // Hit head else if (velocity.z < 0) { cposition.z += (float)u->getCurrentHeight() / 40.0f; } else { // Legs are defeault already } // Apply u->applyDamage(state, power, damageType, u->determineBodyPartHit(damageType, cposition, velocity)); } break; } } } }
void BattleExplosion::damage(GameState &state, const TileMap &map, Vec3<int> pos, int damage) { auto tile = map.getTile(pos); // Explosions with no hazard spawn smoke with half ttl if (!damageType->hazardType) { StateRef<DamageType> dtSmoke = {&state, "DAMAGETYPE_SMOKE"}; state.current_battle->placeHazard(state, ownerOrganisation, ownerUnit, dtSmoke, pos, dtSmoke->hazardType->getLifetime(state), damage, 2, false); } // Explosions with no custom explosion doodad spawn hazards when dealing damage else if (!damageType->explosionDoodad) { state.current_battle->placeHazard(state, ownerOrganisation, ownerUnit, damageType, pos, damageType->hazardType->getLifetime(state), damage, 1, false); } // Gas does no direct damage if (damageType->doesImpactDamage()) { auto set = tile->ownedObjects; for (auto &obj : set) { if (tile->ownedObjects.find(obj) == tile->ownedObjects.end()) { continue; } if (obj->getType() == TileObject::Type::Ground || obj->getType() == TileObject::Type::Feature || obj->getType() == TileObject::Type::LeftWall || obj->getType() == TileObject::Type::RightWall) { auto mp = std::static_pointer_cast<TileObjectBattleMapPart>(obj)->getOwner(); switch (damageType->effectType) { case DamageType::EffectType::Fire: // Nothing, map parts are not damaged by fire at explosion time break; default: mp->applyDamage(state, damage, damageType); break; } } else if (obj->getType() == TileObject::Type::Unit) { StateRef<BattleUnit> u = { &state, std::static_pointer_cast<TileObjectBattleUnit>(obj)->getUnit()->id}; if (affectedUnits.find(u) != affectedUnits.end()) { continue; } affectedUnits.insert(u); // Determine direction of hit Vec3<float> velocity = -position; velocity -= Vec3<float>{0.5f, 0.5f, 0.5f}; velocity += u->position; if (velocity.x == 0.0f && velocity.y == 0.0f) { velocity.z = 1.0f; } // Determine wether to hit head, legs or torso auto cposition = u->position; // Hit torso if coming from the side, not from above or below if (sqrtf(velocity.x * velocity.x + velocity.y * velocity.y) > std::abs(velocity.z)) { cposition.z += (float)u->getCurrentHeight() / 2.0f / 40.0f; } // Hit head if coming from above else if (velocity.z < 0) { cposition.z += (float)u->getCurrentHeight() / 40.0f; } // Hit legs if coming from below else { // Legs are defeault already } // Apply u->applyDamage(state, damage, damageType, u->determineBodyPartHit(damageType, cposition, velocity), DamageSource::Impact, ownerUnit); } else if (obj->getType() == TileObject::Type::Item) { // Special effects do not damage items, fire damages items differently and not on // explosion impact if (damageType->effectType == DamageType::EffectType::None) { auto i = std::static_pointer_cast<TileObjectBattleItem>(obj)->getItem(); i->applyDamage(state, damage, damageType); } } } } }