diff --git a/scripts/api/entity/shiptemplatebasedobject.lua b/scripts/api/entity/shiptemplatebasedobject.lua index a4c7caf866..ac36194443 100644 --- a/scripts/api/entity/shiptemplatebasedobject.lua +++ b/scripts/api/entity/shiptemplatebasedobject.lua @@ -21,8 +21,27 @@ function Entity:setTemplate(template_name) error("Failed to find template: " .. tostring(template_name), 2) end -- print("Setting template:" .. template_name) + + -- Convert old hull component setters to health + hull for backward compatibility + if template.hull and type(template.hull) == "table" then + -- Create health component from hull data + local hull_data = template.hull + comp.health = { + current = hull_data.current or 100.0, + max = hull_data.max or 100.0, + allow_destruction = hull_data.allow_destruction, + damaged_by_energy = hull_data.damaged_by_energy, + damaged_by_kinetic = hull_data.damaged_by_kinetic, + damaged_by_emp = hull_data.damaged_by_emp, + on_destruction = hull_data.on_destruction, + on_taking_damage = hull_data.on_taking_damage + } + -- Create hull as empty marker component + comp.hull = {} + end + for key, value in next, template, nil do - if string.sub(key, 1, 2) ~= "__" then + if string.sub(key, 1, 2) ~= "__" and key ~= "hull" then comp[key] = value end end @@ -91,47 +110,35 @@ end function Entity:getTypeName() return self.components.typename.type_name end ---- Returns this STBO's hull points. ---- Example: stbo:getHull() +--- DEPRECATED: Returns this entity's health points. +--- Example: entity:getHull() function Entity:getHull() - if self.components.hull then return self.components.hull.current end - return 0 + return self:getHealth() end ---- Returns this STBO's maximum limit of hull points. ---- Example: stbo:getHullMax() +--- DEPRECATED: Returns this entity's maximum limit of health points. +--- Example: entity:getHullMax() function Entity:getHullMax() - if self.components.hull then return self.components.hull.max end - return 0 + return self:getHealthMax() end ---- Sets this STBO's hull points. +--- Adds the Hull marker component to the entity. +--- DEPRECATED: If passed an amount, also sets this entity's current health value. --- If set to a value larger than the maximum, this sets the value to the limit. --- If set to a value less than 0, this sets the value to 0. ---- Note that setting this value to 0 doesn't immediately destroy the STBO. ---- Example: stbo:setHull(100) -- sets the hull point limit to either 100, or the limit if less than 100 +--- Note that setting this value to 0 doesn't immediately destroy the entity. +--- Example: entity:setHull(100) -- sets the hull point limit to either 100, or the limit if less than 100 function Entity:setHull(amount) - if self.components.hull then self.components.hull.current = amount end + self.components.hull = {} + self:setHealth(amount) return self end ---- Sets this STBO's maximum limit of hull points. ---- Note that SpaceStations can't repair their own hull, so this only changes the percentage of remaining hull. ---- Example: stbo:setHullMax(100) -- sets the hull point limit to 100 +--- Adds the Hull marker component to the entity. +--- DEPRECATED: If passed an amount, also sets this entity's maximum limit of health points. +--- Example: entity:setHullMax(100) -- sets the hull point limit to 100 function Entity:setHullMax(amount) - if self.components.hull then self.components.hull.max = amount end + self.components.hull = {} + self:setHealthMax(amount) return self end ---- Defines whether this STBO can be destroyed by damage. ---- Defaults to true. ---- Example: stbo:setCanBeDestroyed(false) -- prevents the STBO from being destroyed by damage -function Entity:setCanBeDestroyed(allow_destroy) - if self.components.hull then self.components.hull.allow_destruction = allow_destroy end - return self -end ---- Returns whether the STBO can be destroyed by damage. ---- Example: stbo:getCanBeDestroyed() -function Entity:getCanBeDestroyed() - if self.components.hull then return self.components.hull.allow_destruction end - return false -end --- Returns the shield points for this STBO's shield segment with the given index. --- Shield segments are 0-indexed. --- Example for a ship with two shield segments: @@ -283,12 +290,12 @@ end --- [DEPRECATED] --- Use ShipTemplateBasedObject:getShieldLevel() with an index value. function Entity:getFrontShield() - return self.getShieldLevel(0) + return self:getShieldLevel(0) end --- [DEPRECATED] --- Use ShipTemplateBasedObject:setShieldsMax(). function Entity:getFrontShieldMax() - return self.getShieldMax(0) + return self:getShieldMax(0) end --- [DEPRECATED] --- Use ShipTemplateBasedObject:setShieldLevel() with an index value. @@ -305,12 +312,12 @@ end --- [DEPRECATED] --- Use ShipTemplateBasedObject:getShieldLevel() with an index value. function Entity:getRearShield() - return self.getShieldLevel(1) + return self:getShieldLevel(1) end --- [DEPRECATED] --- Use ShipTemplateBasedObject:setShieldsMax(). function Entity:getRearShieldMax() - return self.getShieldMax(1) + return self:getShieldMax(1) end --- [DEPRECATED] --- Use ShipTemplateBasedObject:setShieldLevel() with an index value. diff --git a/scripts/api/entity/spaceobject.lua b/scripts/api/entity/spaceobject.lua index d8c77cf404..c0d1b55fb2 100644 --- a/scripts/api/entity/spaceobject.lua +++ b/scripts/api/entity/spaceobject.lua @@ -458,12 +458,52 @@ function Entity:isScannedByFaction(faction_name) end return false end +--- Returns this entity's health points. +--- Example: entity:getHealth() +function Entity:getHealth() + if self.components.health then return self.components.health.current end + return 0 +end +--- Returns this entity's maximum limit of health points. +--- Example: entity:getHealthMax() +function Entity:getHealthMax() + if self.components.health then return self.components.health.max end + return 0 +end +--- Sets this entity's health points. +--- If set to a value larger than the maximum, this sets the value to the limit. +--- If set to a value less than 0, this sets the value to 0. +--- Note that setting this value to 0 doesn't immediately destroy the entity. +--- Example: entity:setHealth(100) -- sets the health point limit to either 100, or the limit if less than 100 +function Entity:setHealth(amount) + if self.components.health then self.components.health.current = amount end + return self +end +--- Sets this entity's maximum limit of health points. +--- Example: entity:setHealthMax(100) -- sets the health point limit to 100 +function Entity:setHealthMax(amount) + if self.components.health then self.components.health.max = amount end + return self +end +--- Defines whether this entity can be destroyed by damage. +--- Defaults to true. +--- Example: entity:setCanBeDestroyed(false) -- prevents the entity from being destroyed by damage +function Entity:setCanBeDestroyed(allow_destroy) + if self.components.health then self.components.health.allow_destruction = allow_destroy end + return self +end +--- Returns whether the entity can be destroyed by damage. +--- Example: entity:getCanBeDestroyed() +function Entity:getCanBeDestroyed() + if self.components.health then return self.components.health.allow_destruction end + return false +end --- Defines a function to call when this SpaceObject is destroyed by any means. --- Example: --- -- Prints to the console window or logging file when this SpaceObject is destroyed --- obj:onDestroyed(function() print("Object destroyed!") end) function Entity:onDestroyed(callback) - --TODO: Cases where we do not have hull - if self.components.hull then self.components.hull.on_destruction = callback end + --TODO: Cases where we do not have health + if self.components.health then self.components.health.on_destruction = callback end return self end diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index d487207de5..1c08125c58 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -6,6 +6,7 @@ #include "components/impulse.h" #include "components/warpdrive.h" #include "components/jumpdrive.h" +#include "components/health.h" #include "components/hull.h" #include "components/beamweapon.h" #include "components/missiletubes.h" @@ -527,8 +528,8 @@ void ShipAI::runOrders() } if (bay->flags & DockingBay::Repair) { - auto hull = owner.getComponent(); - if (hull && hull->current < hull->max) + auto health = owner.getComponent(); + if (health && health->current < health->max) allow_undock = false; } } @@ -837,6 +838,7 @@ sp::ecs::Entity ShipAI::findBestTarget(glm::vec2 position, float radius) auto owner_position = ot->getPosition(); for(auto entity : sp::CollisionSystem::queryArea(position - glm::vec2(radius, radius), position + glm::vec2(radius, radius))) { + // Seek only entities with Hull components. if (!entity.hasComponent() || Faction::getRelation(owner, entity) != FactionRelation::Enemy || entity == target) continue; if (RadarBlockSystem::isRadarBlockedFrom(owner_position, entity, short_range)) diff --git a/src/components/health.h b/src/components/health.h new file mode 100644 index 0000000000..9739be6bdd --- /dev/null +++ b/src/components/health.h @@ -0,0 +1,36 @@ +#pragma once + +#include "script/callback.h" +#include "systems/damage.h" + +// Component for entities that can take damage and be destroyed. +// This component tracks current and max health values for all damageable +// entities, and implements damage and destruction callbacks. +// Entities are typically destroyed if daamaged while at zero health. +// Destructability can be disabled to prevent player ship destruction in LARP +// scenarios or tutorials. +// +// For UI display purposes, player-facing interfaces by default display the +// health of entities ONLY if they have both Health and Hull components. +// Entities that have only Health can be targeted but don't show health values +// in player UI. +class Health +{ +public: + float current = 100.0f; + float max = 100.0f; + bool allow_destruction = true; + int damaged_by_flags = (1 << int(DamageType::Energy)) | (1 << int(DamageType::Kinetic)); + float damage_indicator = 0.0f; // Visual damage flash timer (1.5s) + + sp::script::Callback on_destruction; + sp::script::Callback on_taking_damage; +}; + +// Component indicates that an entity lacks a hull or health value, but an +// area damage effect (i.e. explosion) in the area still destroys it. +class DestroyedByAreaDamage +{ +public: + int damaged_by_flags = (1 << int(DamageType::Energy)) | (1 << int(DamageType::Kinetic)); +}; diff --git a/src/components/hull.h b/src/components/hull.h index 78e4830a12..41b9bc80df 100644 --- a/src/components/hull.h +++ b/src/components/hull.h @@ -1,27 +1,16 @@ #pragma once -#include "script/callback.h" -#include "systems/damage.h" - - -// Component to indicate that this entity has a hull and can get hull damage. -// Usually entities are destroyed once they reach zero hull. But you can disable this to prevent player ship destruction in LARP scenarios or tutorials. +// Marker component to indicate that this entity has a hull and can take hull +// damage. Entity health, presented as hull integrity, is tracked in the Health +// component. +// If an entity has the Hull component, it gains certain ship-like properties: +// - Automatic health regeneration from DockingBay entities with the +// DockingBay::Repair flag (as hull repairs) +// - Can be targeted by AI +// - Can be selected on Relay and Science radars +// - With Health component, display of health values as hull percentage/points +// in user interfaces class Hull { public: - float current = 100.0f; - float max = 100.0f; - bool allow_destruction = true; - int damaged_by_flags = (1 << int(DamageType::Energy)) | (1 << int(DamageType::Kinetic)); - float damage_indicator = 0.0f; - - sp::script::Callback on_destruction; - sp::script::Callback on_taking_damage; }; - -// Not having actual hull, but an explosion in the area will destroy this entity. -class DestroyedByAreaDamage -{ -public: - int damaged_by_flags = (1 << int(DamageType::Energy)) | (1 << int(DamageType::Kinetic)); -}; \ No newline at end of file diff --git a/src/hardware/hardwareController.cpp b/src/hardware/hardwareController.cpp index 8ad4637994..18a311ba20 100644 --- a/src/hardware/hardwareController.cpp +++ b/src/hardware/hardwareController.cpp @@ -5,6 +5,7 @@ #include "playerInfo.h" #include "ecs/query.h" +#include "components/health.h" #include "components/hull.h" #include "components/shields.h" #include "components/reactor.h" @@ -377,7 +378,7 @@ bool HardwareController::getVariableValue(string variable_name, float& value) value = bool(ship) ? 1.0f : 0.0f; return true; } - SHIP_VARIABLE("Hull", Hull, 100.0f * c->current / c->max); + SHIP_VARIABLE("Hull", Health, 100.0f * c->current / c->max); SHIP_VARIABLE("FrontShield", Shields, c->entries.size() > 0 ? c->entries[0].percentage() : 0.0f); SHIP_VARIABLE("RearShield", Shields, c->entries.size() > 1 ? c->entries[1].percentage() : 0.0f); SHIP_VARIABLE("Shield0", Shields, c->entries.size() > 0 ? c->entries[0].percentage() : 0.0f); diff --git a/src/multiplayer/health.cpp b/src/multiplayer/health.cpp new file mode 100644 index 0000000000..712cc5deb6 --- /dev/null +++ b/src/multiplayer/health.cpp @@ -0,0 +1,8 @@ +#include "multiplayer/health.h" +#include "multiplayer.h" + +BASIC_REPLICATION_IMPL(HealthReplication, Health) + BASIC_REPLICATION_FIELD(current); + BASIC_REPLICATION_FIELD(max); + BASIC_REPLICATION_FIELD(damage_indicator); +} diff --git a/src/multiplayer/health.h b/src/multiplayer/health.h new file mode 100644 index 0000000000..172dc9aa75 --- /dev/null +++ b/src/multiplayer/health.h @@ -0,0 +1,6 @@ +#pragma once + +#include "multiplayer/basic.h" +#include "components/health.h" + +BASIC_REPLICATION_CLASS(HealthReplication, Health); diff --git a/src/multiplayer/hull.cpp b/src/multiplayer/hull.cpp index 001dd7f4bb..f734829c28 100644 --- a/src/multiplayer/hull.cpp +++ b/src/multiplayer/hull.cpp @@ -2,8 +2,5 @@ #include "multiplayer.h" -BASIC_REPLICATION_IMPL(HullReplication, Hull) - BASIC_REPLICATION_FIELD(current); - BASIC_REPLICATION_FIELD(max); - BASIC_REPLICATION_FIELD(damage_indicator); -} \ No newline at end of file +// Hull is now a marker component with no fields to replicate +EMPTY_REPLICATION_IMPL(HullReplication, Hull) \ No newline at end of file diff --git a/src/playerInfo.cpp b/src/playerInfo.cpp index 5cf2d857f5..99bb277c01 100644 --- a/src/playerInfo.cpp +++ b/src/playerInfo.cpp @@ -31,6 +31,7 @@ #include "components/collision.h" #include "components/impulse.h" +#include "components/health.h" #include "components/hull.h" #include "components/customshipfunction.h" #include "components/shiplog.h" @@ -953,8 +954,9 @@ void PlayerInfo::onReceiveClientCommand(int32_t client_id, sp::io::DataBuffer& p trace.max_size = 10.0; trace.color = {96, 192, 128, 255}; trace.flags = RadarTrace::LongRange; - auto& hull = p.addComponent(); - hull.current = hull.max = 1; + auto& health = p.addComponent(); + health.current = health.max = 1; + p.addComponent(); p.addComponent(); auto model = "SensorBuoy/SensorBuoyMKI.model"; auto idx = irandom(1, 3); diff --git a/src/screenComponents/indicatorOverlays.cpp b/src/screenComponents/indicatorOverlays.cpp index 3d347bf723..066676d8ba 100644 --- a/src/screenComponents/indicatorOverlays.cpp +++ b/src/screenComponents/indicatorOverlays.cpp @@ -8,6 +8,7 @@ #include "components/warpdrive.h" #include "components/jumpdrive.h" #include "components/shields.h" +#include "components/health.h" #include "components/hull.h" #include "multiplayer_server.h" #include "i18n.h" @@ -80,8 +81,8 @@ void GuiIndicatorOverlays::onDraw(sp::RenderTarget& renderer) shield_low_warning_overlay->setAlpha(0); } - if (auto hull = my_spaceship.getComponent()) - hull_hit_overlay->setAlpha(128 * (hull->damage_indicator / 1.5f)); + if (auto health = my_spaceship.getComponent()) + hull_hit_overlay->setAlpha(128 * (health->damage_indicator / 1.5f)); }else{ shield_hit_overlay->setAlpha(0); shield_low_warning_overlay->setAlpha(0); diff --git a/src/screenComponents/infoDisplay.cpp b/src/screenComponents/infoDisplay.cpp index de593fe88a..81c7aafea7 100644 --- a/src/screenComponents/infoDisplay.cpp +++ b/src/screenComponents/infoDisplay.cpp @@ -5,6 +5,7 @@ #include "components/reactor.h" #include "components/collision.h" #include "components/shields.h" +#include "components/health.h" #include "components/hull.h" #include "components/coolant.h" #include "playerInfo.h" @@ -97,10 +98,12 @@ HullInfoDisplay::HullInfoDisplay(GuiContainer* owner, const string& id, float di void HullInfoDisplay::onUpdate() { - if (auto hull = my_spaceship.getComponent()) + // Show Health as Hull, and only if the entity also has Hull. + auto health = my_spaceship.getComponent(); + if (health && my_spaceship.hasComponent()) { - setValue(toNearbyIntString(100.0f * hull->current / hull->max) + "%"); - if (hull->current < hull->max / 4.0f) + setValue(toNearbyIntString(100.0f * health->current / health->max) + "%"); + if (health->current < health->max / 4.0f) setBackColor(glm::u8vec4(255, 0, 0, 255)); else setBackColor(glm::u8vec4{255,255,255,255}); diff --git a/src/screenComponents/targetsContainer.cpp b/src/screenComponents/targetsContainer.cpp index d5c0608764..9f69b6c8b1 100644 --- a/src/screenComponents/targetsContainer.cpp +++ b/src/screenComponents/targetsContainer.cpp @@ -1,12 +1,14 @@ #include "targetsContainer.h" #include "playerInfo.h" +#include "ecs/query.h" + #include "systems/collision.h" -#include "components/hull.h" #include "components/collision.h" +#include "components/comms.h" +#include "components/health.h" +#include "components/hull.h" #include "components/scanning.h" #include "components/radar.h" -#include "ecs/query.h" - TargetsContainer::TargetsContainer() { @@ -199,21 +201,21 @@ bool TargetsContainer::isValidTarget(sp::ecs::Entity entity, ESelectionType sele { if (entity == my_spaceship) return false; - switch(selection_type) + switch (selection_type) { - case Selectable: + case Selectable: // Used by Relay if (entity.hasComponent()) return true; - if (entity.getComponent()) return true; - if (entity.getComponent()) return true; + if (entity.hasComponent()) return true; + if (entity.hasComponent()) return true; break; - case Targetable: - if (entity.hasComponent()) return true; + case Targetable: // Used by Weapons + if (entity.hasComponent()) return true; break; - case Scannable: + case Scannable: // Used by Science if (entity.hasComponent()) return true; - if (entity.getComponent()) return true; - if (entity.getComponent()) return true; - if (entity.getComponent()) return true; + if (entity.hasComponent()) return true; + if (entity.hasComponent()) return true; + if (entity.hasComponent()) return true; break; } return false; diff --git a/src/screenComponents/targetsContainer.h b/src/screenComponents/targetsContainer.h index 3e792e1037..4cd9954700 100644 --- a/src/screenComponents/targetsContainer.h +++ b/src/screenComponents/targetsContainer.h @@ -41,4 +41,4 @@ class TargetsContainer bool isValidTarget(sp::ecs::Entity entity, ESelectionType selection_type); }; -#endif//TARGETS_CONTAINER_H +#endif//TARGETS_CONTAINER_H \ No newline at end of file diff --git a/src/screens/crew6/relayScreen.cpp b/src/screens/crew6/relayScreen.cpp index f44502c8c1..0e5caf49fd 100644 --- a/src/screens/crew6/relayScreen.cpp +++ b/src/screens/crew6/relayScreen.cpp @@ -29,11 +29,24 @@ //TODO: This function does not belong here. static bool canHack(sp::ecs::Entity entity) { + if (!my_spaceship) return false; + if (my_spaceship == entity || !my_spaceship.hasComponent()) return false; auto scanstate = entity.getComponent(); - //TODO: Check if there are actually hackable systems. if (scanstate && scanstate->getStateFor(my_spaceship) == ScanState::State::NotScanned) return true; - return Faction::getRelation(entity, my_spaceship) != FactionRelation::Friendly; + + // Check for hackable ShipSystems. + bool has_hackable_systems = false; + for (int n = 0; n < static_cast(ShipSystem::Type::COUNT); n++) + { + auto sys = ShipSystem::get(entity, ShipSystem::Type(n)); + if (sys && sys->can_be_hacked) has_hackable_systems = true; + }; + + if (Faction::getRelation(entity, my_spaceship) == FactionRelation::Friendly) + return false; + else + return has_hackable_systems; } RelayScreen::RelayScreen(GuiContainer* owner, bool allow_comms) diff --git a/src/screens/crew6/scienceScreen.cpp b/src/screens/crew6/scienceScreen.cpp index 0e3dfeeb11..4014b5e35b 100644 --- a/src/screens/crew6/scienceScreen.cpp +++ b/src/screens/crew6/scienceScreen.cpp @@ -8,6 +8,7 @@ #include "components/beamweapon.h" #include "components/shields.h" +#include "components/health.h" #include "components/hull.h" #include "components/collision.h" #include "components/radar.h" @@ -419,8 +420,12 @@ void ScienceScreen::onDraw(sp::RenderTarget& renderer) } info_shields->setValue(str); } - if (auto hull = target.getComponent()) - info_hull->setValue(int(ceil(hull->current))); + // Show health only if entity has both Health and Hull. + if (auto health = target.getComponent()) + { + if (target.hasComponent()) + info_hull->setValue(static_cast(ceil(health->current))); + } } // On a full scan, show tactical and systems data (if any), and its diff --git a/src/screens/extra/damcon.cpp b/src/screens/extra/damcon.cpp index 719fc06d0d..ad5c341c42 100644 --- a/src/screens/extra/damcon.cpp +++ b/src/screens/extra/damcon.cpp @@ -4,6 +4,7 @@ #include "screenComponents/shieldFreqencySelect.h" #include "screenComponents/shipInternalView.h" #include "screenComponents/customShipFunctions.h" +#include "components/health.h" #include "components/hull.h" #include "i18n.h" @@ -36,10 +37,12 @@ void DamageControlScreen::onDraw(sp::RenderTarget& renderer) if (my_spaceship) { - auto hull = my_spaceship.getComponent(); - if (hull) { - hull_display->setValue(string(int(100 * hull->current / hull->max)) + "%"); - if (hull->current < hull->max / 4.0f) + auto health = my_spaceship.getComponent(); + auto hull = my_spaceship.hasComponent(); + if (health && hull) + { + hull_display->setValue(string(int(100 * health->current / health->max)) + "%"); + if (health->current < health->max / 4.0f) hull_display->setBackColor(glm::u8vec4(255, 0, 0, 255)); else hull_display->setBackColor(glm::u8vec4{255,255,255,255}); diff --git a/src/screens/gm/gameMasterScreen.cpp b/src/screens/gm/gameMasterScreen.cpp index 3bacb1e662..2ebd4e5884 100644 --- a/src/screens/gm/gameMasterScreen.cpp +++ b/src/screens/gm/gameMasterScreen.cpp @@ -11,6 +11,7 @@ #include "components/faction.h" #include "components/collision.h" #include "components/gravity.h" +#include "components/health.h" #include "components/hull.h" #include "components/comms.h" #include "components/player.h" @@ -39,8 +40,8 @@ static std::unordered_map getGMInfo(sp::ecs::Entity entity) result[trMark("gm_info", "CallSign")] = cs->callsign; if (auto tn = entity.getComponent()) result[trMark("gm_info", "Type")] = tn->localized; - if (auto hull = entity.getComponent()) - result[trMark("gm_info", "Hull")] = string(hull->current) + "/" + string(hull->max); + if (auto health = entity.getComponent()) + result[trMark("gm_info", "Health")] = string(health->current) + "/" + string(health->max); //for(int n=0; n()) - info[trMark("gm_info", "Hull")] = string(int(100.0f * hull->current / hull->max)) + "%"; + if (auto health = target.getComponent()) + info[trMark("gm_info", "Health")] = string(int(100.0f * health->current / health->max)) + "%"; for (auto i = info.begin(); i != info.end(); i++) selection_info[i->first] = i->second; diff --git a/src/script/components.cpp b/src/script/components.cpp index 2d64e25e64..789ca09793 100644 --- a/src/script/components.cpp +++ b/src/script/components.cpp @@ -16,6 +16,7 @@ #include "components/name.h" #include "components/moveto.h" #include "components/lifetime.h" +#include "components/health.h" #include "components/hull.h" #include "components/shields.h" #include "components/docking.h" @@ -362,15 +363,17 @@ void initComponentScriptBindings() sp::script::ComponentHandler::name("allow_radar_link"); BIND_MEMBER(AllowRadarLink, owner); + sp::script::ComponentHandler::name("health"); + BIND_MEMBER(Health, current); + BIND_MEMBER(Health, max); + BIND_MEMBER(Health, allow_destruction); + BIND_MEMBER(Health, on_destruction); + BIND_MEMBER(Health, on_taking_damage); + BIND_MEMBER_FLAG(Health, damaged_by_flags, "damaged_by_energy", (1 << int(DamageType::Energy))); + BIND_MEMBER_FLAG(Health, damaged_by_flags, "damaged_by_kinetic", (1 << int(DamageType::Kinetic))); + BIND_MEMBER_FLAG(Health, damaged_by_flags, "damaged_by_emp", (1 << int(DamageType::EMP))); + sp::script::ComponentHandler::name("hull"); - BIND_MEMBER(Hull, current); - BIND_MEMBER(Hull, max); - BIND_MEMBER(Hull, allow_destruction); - BIND_MEMBER(Hull, on_destruction); - BIND_MEMBER(Hull, on_taking_damage); - BIND_MEMBER_FLAG(Hull, damaged_by_flags, "damaged_by_energy", (1 << int(DamageType::Energy))); - BIND_MEMBER_FLAG(Hull, damaged_by_flags, "damaged_by_kinetic", (1 << int(DamageType::Kinetic))); - BIND_MEMBER_FLAG(Hull, damaged_by_flags, "damaged_by_emp", (1 << int(DamageType::EMP))); sp::script::ComponentHandler::name("shields"); BIND_MEMBER_NAMED(Shields, front_system.health, "front_health"); diff --git a/src/script/enum.h b/src/script/enum.h index 4744438c16..57456311f8 100644 --- a/src/script/enum.h +++ b/src/script/enum.h @@ -1,7 +1,7 @@ #pragma once #include "script/environment.h" -#include "components/hull.h" +#include "components/health.h" #include "components/collision.h" #include "components/faction.h" #include "components/ai.h" diff --git a/src/systems/damage.cpp b/src/systems/damage.cpp index ef26fc71c1..8db52837e9 100644 --- a/src/systems/damage.cpp +++ b/src/systems/damage.cpp @@ -2,7 +2,7 @@ #include "systems/collision.h" #include "ecs/query.h" #include "components/collision.h" -#include "components/hull.h" +#include "components/health.h" #include "components/shields.h" #include "components/beamweapon.h" #include "components/radar.h" @@ -15,9 +15,9 @@ void DamageSystem::update(float delta) { - for(auto [entity, hull] : sp::ecs::Query()) { - if (hull.damage_indicator > 0.0f) - hull.damage_indicator -= delta; + for(auto [entity, health] : sp::ecs::Query()) { + if (health.damage_indicator > 0.0f) + health.damage_indicator -= delta; } } @@ -82,7 +82,7 @@ void DamageSystem::applyDamage(sp::ecs::Entity entity, float amount, const Damag if (amount > 0.0f) { - takeHullDamage(entity, amount, info); + takeHealthDamage(entity, amount, info); if (auto dbad = entity.getComponent()) { if (dbad->damaged_by_flags & (1 << int(info.type))) { entity.destroy(); @@ -91,39 +91,44 @@ void DamageSystem::applyDamage(sp::ecs::Entity entity, float amount, const Damag } } -void DamageSystem::takeHullDamage(sp::ecs::Entity entity, float amount, const DamageInfo& info) +void DamageSystem::takeHealthDamage(sp::ecs::Entity entity, float amount, const DamageInfo& info) { - auto hull = entity.getComponent(); - if (!hull) + auto health = entity.getComponent(); + if (!health) return; - if (!(hull->damaged_by_flags & (1 << int(info.type)))) + if (!(health->damaged_by_flags & (1 << int(info.type)))) return; // If taking non-EMP damage, light up the hull damage overlay. - hull->damage_indicator = 1.5f; + health->damage_indicator = 1.5f; - if (gameGlobalInfo->use_system_damage && hull) + if (gameGlobalInfo->use_system_damage && health) { if (auto sys = ShipSystem::get(entity, info.system_target)) { //Target specific system - float system_damage = (amount / hull->max) * 2.0f; - if (info.type == DamageType::Energy) - system_damage *= 3.0f; //Beam weapons do more system damage, as they penetrate the hull easier. - sys->health -= system_damage; - if (sys->health < -1.0f) - sys->health = -1.0f; - - for(int n=0; n<2; n++) + if (health->max > 0.0f) { - auto random_system = ShipSystem::Type(irandom(0, ShipSystem::COUNT - 1)); - //Damage the system compared to the amount of hull damage you would do. If we have less hull strength you get more system damage. - float system_damage = (amount / hull->max) * 1.0f; - sys = ShipSystem::get(entity, random_system); - if (sys) { - sys->health -= system_damage; - if (sys->health < -1.0f) - sys->health = -1.0f; + float system_damage = (amount / health->max) * 2.0f; + // Beam weapons penetrate the hull more easily and deal more + // system damage. + if (info.type == DamageType::Energy) + system_damage *= 3.0f; + sys->health -= system_damage; + if (sys->health < -1.0f) + sys->health = -1.0f; + + for(int n=0; n<2; n++) + { + auto random_system = ShipSystem::Type(irandom(0, ShipSystem::COUNT - 1)); + //Damage the system compared to the amount of hull damage you would do. If we have less hull strength you get more system damage. + float system_damage = (amount / health->max) * 1.0f; + sys = ShipSystem::get(entity, random_system); + if (sys) { + sys->health -= system_damage; + if (sys->health < -1.0f) + sys->health = -1.0f; + } } } @@ -132,40 +137,45 @@ void DamageSystem::takeHullDamage(sp::ecs::Entity entity, float amount, const Da else amount *= 0.5f; }else{ - //Damage the system compared to the amount of hull damage you would do. If we have less hull strength you get more system damage. - float system_damage = (amount / hull->max) * 3.0f; - if (info.type == DamageType::Energy) - system_damage *= 2.5f; //Beam weapons do more system damage, as they penetrate the hull easier. - - auto random_system = ShipSystem::Type(irandom(0, ShipSystem::COUNT - 1)); - sys = ShipSystem::get(entity, random_system); - if (sys) { - sys->health -= system_damage; - if (sys->health < -1.0f) - sys->health = -1.0f; + // Damage the system compared to the amount of hull damage dealt. If + // we have less hull strength, we take more system damage. + if (health->max > 0.0f) + { + float system_damage = (amount / health->max) * 3.0f; + // Beam weapons penetrate the hull more easily and deal more + // system damage. + if (info.type == DamageType::Energy) + system_damage *= 2.5f; + auto random_system = ShipSystem::Type(irandom(0, ShipSystem::COUNT - 1)); + sys = ShipSystem::get(entity, random_system); + if (sys) { + sys->health -= system_damage; + if (sys->health < -1.0f) + sys->health = -1.0f; + } } } } - hull->current -= amount; - if (hull->current <= 0.0f && !hull->allow_destruction) + health->current -= amount; + if (health->current <= 0.0f && !health->allow_destruction) { - hull->current = 1; + health->current = 1; } - if (hull->current <= 0.0f) + if (health->current <= 0.0f) { destroyedByDamage(entity, info); return; } - if (hull->on_taking_damage) + if (health->on_taking_damage) { if (info.instigator) { - LuaConsole::checkResult(hull->on_taking_damage.call(entity, info.instigator)); + LuaConsole::checkResult(health->on_taking_damage.call(entity, info.instigator)); } else { - LuaConsole::checkResult(hull->on_taking_damage.call(entity)); + LuaConsole::checkResult(health->on_taking_damage.call(entity)); } } } @@ -181,13 +191,12 @@ void DamageSystem::destroyedByDamage(sp::ecs::Entity entity, const DamageInfo& i } } + auto health = entity.getComponent(); if (info.instigator) { float points = 0; - auto hull = entity.getComponent(); - if (hull) - points += hull->max * 0.1f; + if (health) points += health->max * 0.1f; auto shields = entity.getComponent(); if (shields && !shields->entries.empty()) { @@ -195,21 +204,20 @@ void DamageSystem::destroyedByDamage(sp::ecs::Entity entity, const DamageInfo& i points += shield.max * 0.1f; points /= shields->entries.size(); } - + if (Faction::getRelation(info.instigator, entity) == FactionRelation::Enemy) Faction::getInfo(info.instigator).reputation_points += points; else Faction::getInfo(info.instigator).reputation_points = std::max(Faction::getInfo(info.instigator).reputation_points - points, 0.0f); } - auto hull = entity.getComponent(); - if (hull->on_destruction) + if (health && health->on_destruction) { if (info.instigator) { - LuaConsole::checkResult(hull->on_destruction.call(entity, info.instigator)); + LuaConsole::checkResult(health->on_destruction.call(entity, info.instigator)); } else { - LuaConsole::checkResult(hull->on_destruction.call(entity)); + LuaConsole::checkResult(health->on_destruction.call(entity)); } } diff --git a/src/systems/damage.h b/src/systems/damage.h index a20aaf9c85..487ef9c6f1 100644 --- a/src/systems/damage.h +++ b/src/systems/damage.h @@ -40,6 +40,6 @@ class DamageSystem : public sp::ecs::System static void applyDamage(sp::ecs::Entity entity, float amount, const DamageInfo& info); private: - static void takeHullDamage(sp::ecs::Entity entity, float amount, const DamageInfo& info); + static void takeHealthDamage(sp::ecs::Entity entity, float amount, const DamageInfo& info); static void destroyedByDamage(sp::ecs::Entity entity, const DamageInfo& info); }; diff --git a/src/systems/docking.cpp b/src/systems/docking.cpp index 8785b3ebc8..db29a277c2 100644 --- a/src/systems/docking.cpp +++ b/src/systems/docking.cpp @@ -4,6 +4,7 @@ #include "components/impulse.h" #include "components/maneuveringthrusters.h" #include "components/reactor.h" +#include "components/health.h" #include "components/hull.h" #include "components/warpdrive.h" #include "components/jumpdrive.h" @@ -61,14 +62,17 @@ void DockingSystem::update(float delta) } auto bay = docking_port.target.getComponent(); - if (bay && (bay->flags & DockingBay::Repair)) //Check if what we are docked to allows hull repairs, and if so, do it. + // Check if what we are docked to allows hull repairs, and if + // so, do it. + if (bay && (bay->flags & DockingBay::Repair)) { - auto hull = entity.getComponent(); - if (hull && hull->current < hull->max) + const auto hull = entity.hasComponent(); + auto health = entity.getComponent(); + if (hull && health && health->current < health->max) { - hull->current += delta; - if (hull->current > hull->max) - hull->current = hull->max; + health->current += delta; + if (health->current > health->max) + health->current = health->max; } } diff --git a/src/systems/energysystem.cpp b/src/systems/energysystem.cpp index 5121d76e28..682c30a8e5 100644 --- a/src/systems/energysystem.cpp +++ b/src/systems/energysystem.cpp @@ -2,6 +2,7 @@ #include "ecs/query.h" #include "components/coolant.h" #include "components/reactor.h" +#include "components/health.h" #include "components/hull.h" #include "components/collision.h" #include "components/rendering.h" @@ -55,8 +56,8 @@ void EnergySystem::update(float delta) // destroying the ship and damaging a 0.5U radius. if (reactor.health < -0.9f && reactor.heat_level == 1.0f && reactor.overload_explode && game_server) { - auto hull = entity.getComponent(); - if (hull && hull->allow_destruction) { + auto health = entity.getComponent(); + if (health && health->allow_destruction) { auto transform = entity.getComponent(); if (transform) { auto e = sp::ecs::Entity::create(); diff --git a/src/systems/gravity.cpp b/src/systems/gravity.cpp index ba25da5d5a..a859ec3b80 100644 --- a/src/systems/gravity.cpp +++ b/src/systems/gravity.cpp @@ -1,6 +1,7 @@ #include "systems/gravity.h" #include "components/gravity.h" #include "components/collision.h" +#include "components/health.h" #include "components/hull.h" #include "systems/collision.h" #include "systems/damage.h" @@ -60,7 +61,8 @@ void GravitySystem::update(float delta) DamageSystem::applyDamage(target, 100000.0, info); //try to destroy the object by inflicting a huge amount of damage if (target) { - if (!target.hasComponent() || target.getComponent()->allow_destruction) + auto health = target.getComponent(); + if (!health || health->allow_destruction) target.destroy(); return; } diff --git a/src/systems/missilesystem.cpp b/src/systems/missilesystem.cpp index c7f64b2e22..6c30616baa 100644 --- a/src/systems/missilesystem.cpp +++ b/src/systems/missilesystem.cpp @@ -4,7 +4,7 @@ #include "components/missiletubes.h" #include "components/missile.h" #include "components/lifetime.h" -#include "components/hull.h" +#include "components/health.h" #include "components/radar.h" #include "components/docking.h" #include "components/warpdrive.h" @@ -137,15 +137,15 @@ void MissileSystem::collision(sp::ecs::Entity a, sp::ecs::Entity b, float force) if (!game_server) return; auto deot = a.getComponent(); if (deot && deot->trigger_holdoff_delay <= 0.0f) { - auto hull = b.getComponent(); - if (!hull) return; + auto health = b.getComponent(); + if (!health) return; deot->triggered = true; } auto eot = a.getComponent(); if (!eot) return; if (eot->owner == b) return; - auto hull = b.getComponent(); - if (!hull) return; + auto health = b.getComponent(); + if (!health) return; explode(a, b, *eot); } diff --git a/src/threatLevelEstimate.cpp b/src/threatLevelEstimate.cpp index fb81739735..a5b4ebd15c 100644 --- a/src/threatLevelEstimate.cpp +++ b/src/threatLevelEstimate.cpp @@ -1,6 +1,7 @@ #include "gameGlobalInfo.h" #include "threatLevelEstimate.h" #include "ecs/query.h" +#include "components/health.h" #include "components/hull.h" #include "components/collision.h" #include "components/shields.h" @@ -52,9 +53,10 @@ float ThreatLevelEstimate::getThreatFor(sp::ecs::Entity ship) float threat = 0.0; - auto hull = ship.getComponent(); - if (hull) - threat += hull->max - hull->current; + auto health = ship.getComponent(); + bool hull = ship.hasComponent(); + if (hull && health) + threat += health->max - health->current; auto shields = ship.getComponent(); if (shields) { if (shields->active) @@ -81,10 +83,11 @@ float ThreatLevelEstimate::getThreatFor(sp::ecs::Entity ship) } bool is_being_attacked = false; - hull = entity.getComponent(); + health = entity.getComponent(); + hull = entity.hasComponent(); float score = 200.0f; - if (hull) - score += hull->max; + if (hull && health) + score += health->max; auto shields = entity.getComponent(); if (shields) { for(auto& shield : shields->entries) {