From ca3df7f9cd051afdd405bb695a5538ac8bfc1626 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Tue, 18 Feb 2025 05:38:25 +0200 Subject: [PATCH] Implement hull, armor and shield components (#1016) * Remove 8 facet support for armor and shield Remove extra text in ship_view from the C++ end of things (basecomputer) * Implement hull, armor and shield components - Delete Health class. Very similar to Resource. - Move functions to components - e.g. Percent, Get, - Move shield functionality like AdjustPower from DamageableLayer to Shield class - Disable enhance shields from collision. This should probably be handled by a python script. - Delete many functions that are now unused. - Delete x4 sparkling missile - Delete planet.cpp fawg weird hull value - Delete incorrect GetHullPercent by planet. For now it can be damaged and destroyed. - Move some python API from macros to actual code. - Fix issue in energy_container, where Capacitor and FTL Capacitor were not saved. - Add two shield factors in configuration. Currently not read from file. * Fix bug in units.json and ships.json reading. If file doesn't open, game crashes. Polish the code a little. * Tweak the shield modifiers to make the game work better. This stops the shields from eating all the reactor capacity. * Make upgrade view take unit stats into account. Upgrades installed on ship now display damage. Fix bug #1019. Convert Radar_Range to int before saving - prevent exponent format for upgrade view. Fix issue in armor and shield, where Resource was initiated without max value. Use proper string constructor instead. Fix issue in armor, hull and shield, where PercentOperational did not use actual libdamage damage. Fix issue in EnergyContainer applied modifier in the wrong direction. Also, use Serialize to save all three values. Fix issue in Reactor, where Serialize was not used. * Add support for new lib component style upgrade repair Add integral components to ship - hull, afterburner, drive and FTL drive. Fix incorrect reporting of operational as 0-100 and not 0-1. Add missing functions in several components - Damaged, Repair. Fix several bugs in EnergyContainer, including applying fuel_factor to energy and ftl_energy. Correct issue with Reactor incorrectly apply reactor_factor when saving. Fix issue in armor and shield, where the value is read incorrectly. Delete unused code from energetic. * Fix several issues: - Component repairs cost money again - Some sanity checks for eject cargo - Integral components have non-zero volume and mass. Prevent all sorts of nan errors in code. - General repair and refuel no longer repairs integral components. - Shield no longer reports shield strength as percent operational. Damaged/Fixing shields is now also reported correctly. * Fix issue of repairs not costing money. Possible fix for multiple integral components. --- engine/CMakeLists.txt | 6 +- engine/src/cmd/ai/aggressive.cpp | 43 +- engine/src/cmd/ai/firekeyboard.cpp | 8 +- engine/src/cmd/basecomputer.cpp | 23 +- engine/src/cmd/carrier.cpp | 15 + engine/src/cmd/collision.cpp | 6 +- engine/src/cmd/damageable.cpp | 201 +++------ engine/src/cmd/damageable.h | 82 +--- engine/src/cmd/drawable.cpp | 22 +- engine/src/cmd/energetic.cpp | 141 +----- engine/src/cmd/energetic.h | 10 - engine/src/cmd/missile.cpp | 4 - engine/src/cmd/music.cpp | 5 - engine/src/cmd/music.h | 1 - engine/src/cmd/planet.cpp | 5 +- engine/src/cmd/planet.h | 4 - .../cmd/script/script_call_unit_generic.cpp | 12 +- engine/src/cmd/unit_csv.cpp | 244 ++--------- engine/src/cmd/unit_generic.cpp | 173 ++------ engine/src/cmd/unit_generic.h | 23 +- engine/src/cmd/unit_util_generic.cpp | 42 +- engine/src/cmd/upgradeable_unit.cpp | 115 ++++- engine/src/cmd/upgradeable_unit.h | 1 + engine/src/components/afterburner.cpp | 6 +- engine/src/components/armor.cpp | 163 +++++++ engine/src/components/armor.h | 64 +++ engine/src/components/component.cpp | 6 +- engine/src/components/drive.cpp | 6 +- engine/src/components/energy_container.cpp | 17 +- engine/src/components/hull.cpp | 100 +++++ engine/src/components/hull.h | 62 +++ engine/src/components/reactor.cpp | 9 +- engine/src/components/shield.cpp | 402 ++++++++++++++++++ engine/src/components/shield.h | 75 ++++ engine/src/configuration/configuration.h | 3 + engine/src/damage/damage.h | 27 +- engine/src/damage/damageable_layer.cpp | 319 ++++---------- engine/src/damage/damageable_layer.h | 86 ++-- engine/src/damage/damageable_object.cpp | 42 +- engine/src/damage/damageable_object.h | 9 +- engine/src/damage/facet_configuration.h | 33 +- engine/src/damage/health.cpp | 171 -------- engine/src/damage/health.h | 116 ----- engine/src/damage/tests/health_tests.cpp | 105 ----- engine/src/damage/tests/layer_tests.cpp | 3 +- engine/src/damage/tests/object_tests.cpp | 29 +- engine/src/gfx/cockpit.cpp | 65 ++- engine/src/gfx/cockpit.h | 3 +- engine/src/gfx/cockpit_generic.cpp | 26 +- engine/src/gfx/cockpit_generic.h | 6 - engine/src/gfx/cockpit_gfx.cpp | 27 +- engine/src/gfx/vdu.cpp | 48 +-- engine/src/python/define_odd_unit_functions.h | 10 + engine/src/python/python_unit_wrap.h | 19 +- engine/src/python/unit_wrapper_class.h | 47 ++ engine/src/star_system.cpp | 5 +- engine/src/universe.cpp | 6 +- 57 files changed, 1564 insertions(+), 1737 deletions(-) create mode 100644 engine/src/components/armor.cpp create mode 100644 engine/src/components/armor.h create mode 100644 engine/src/components/hull.cpp create mode 100644 engine/src/components/hull.h create mode 100644 engine/src/components/shield.cpp create mode 100644 engine/src/components/shield.h delete mode 100644 engine/src/damage/health.cpp delete mode 100644 engine/src/damage/health.h delete mode 100644 engine/src/damage/tests/health_tests.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 85e2b14901..eb6ee8a565 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -460,7 +460,6 @@ SET(LIBCONFIG SET(LIBDAMAGE src/damage/damageable_layer.cpp src/damage/damageable_object.cpp - src/damage/health.cpp ) SET(LIBRESOURCE @@ -486,6 +485,10 @@ SET(LIBCOMPONENT src/components/energy_container.cpp src/components/reactor.cpp + src/components/armor.cpp + src/components/hull.cpp + src/components/shield.cpp + src/components/afterburner.cpp src/components/afterburner_upgrade.cpp src/components/cloak.cpp @@ -1479,7 +1482,6 @@ IF (USE_GTEST) src/cmd/tests/csv_tests.cpp src/cmd/tests/json_tests.cpp src/configuration/tests/configuration_tests.cpp - src/damage/tests/health_tests.cpp src/damage/tests/layer_tests.cpp src/damage/tests/object_tests.cpp src/resource/tests/buy_sell.cpp diff --git a/engine/src/cmd/ai/aggressive.cpp b/engine/src/cmd/ai/aggressive.cpp index 1ea679ec4a..c9e2e572e3 100644 --- a/engine/src/cmd/ai/aggressive.cpp +++ b/engine/src/cmd/ai/aggressive.cpp @@ -260,16 +260,16 @@ void AggressiveAI::SetParent(Unit *parent1) { last_directive = "b"; //prevent escort race condition //INIT stored stuff - Fshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->FShieldData(); + Fshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::front); Fshield_rate_old = 0.0; Fshield_prev_time = UniverseUtil::GetGameTime(); - Bshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->BShieldData(); + Bshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::back); Bshield_rate_old = 0.0; Bshield_prev_time = UniverseUtil::GetGameTime(); - Lshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->LShieldData(); + Lshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::left); Lshield_rate_old = 0.0; Lshield_prev_time = UniverseUtil::GetGameTime(); - Rshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->RShieldData(); + Rshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::right); Rshield_rate_old = 0.0; Rshield_prev_time = UniverseUtil::GetGameTime(); Farmour_prev = 1.0; @@ -284,7 +284,7 @@ void AggressiveAI::SetParent(Unit *parent1) { Rarmour_prev = 1.0; Rarmour_rate_old = 0.0; Rarmour_prev_time = UniverseUtil::GetGameTime(); - Hull_prev = parent->GetHullPercent(); + Hull_prev = parent->hull.Percent(); Hull_rate_old = 0.0; Hull_prev_time = UniverseUtil::GetGameTime(); } @@ -333,30 +333,31 @@ bool AggressiveAI::ProcessLogicItem(const AIEvents::AIEvresult &item) { value = parent->GetComputerData().threatlevel; break; case FSHIELD: - value = parent->ftl_drive.Enabled() ? 1 : parent->FShieldData(); + value = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::front); break; case BSHIELD: - value = parent->ftl_drive.Enabled() ? 1 : parent->BShieldData(); + value = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::back); break; case HULL: { - value = parent->GetHullPercent(); + value = parent->hull.Percent(); break; } case LSHIELD: - value = parent->ftl_drive.Enabled() ? 1 : parent->LShieldData(); + value = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::left); break; case RSHIELD: - value = parent->ftl_drive.Enabled() ? 1 : parent->RShieldData(); + value = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::right); break; case FSHIELD_HEAL_RATE: { + // TODO: refactor this. We don't need to repeat this code double delta_t = UniverseUtil::GetGameTime() - Fshield_prev_time; if (delta_t > 0.5) { //0.5 = reaction time limit for hit rate - double delta_v = parent->ftl_drive.Enabled() ? 1 : parent->FShieldData() - Fshield_prev; + double delta_v = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::front) - Fshield_prev; value = delta_v / delta_t; Fshield_rate_old = value; Fshield_prev_time = UniverseUtil::GetGameTime(); - Fshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->FShieldData(); + Fshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::front); } else { value = Fshield_rate_old; } @@ -366,11 +367,11 @@ bool AggressiveAI::ProcessLogicItem(const AIEvents::AIEvresult &item) { double delta_t = UniverseUtil::GetGameTime() - Bshield_prev_time; if (delta_t > 0.5) { //0.5 = reaction time limit for hit rate - double delta_v = parent->ftl_drive.Enabled() ? 1 : parent->BShieldData() - Bshield_prev; + double delta_v = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::back) - Bshield_prev; value = delta_v / delta_t; Bshield_rate_old = value; Bshield_prev_time = UniverseUtil::GetGameTime(); - Bshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->BShieldData(); + Bshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::back); } else { value = Bshield_rate_old; } @@ -380,11 +381,11 @@ bool AggressiveAI::ProcessLogicItem(const AIEvents::AIEvresult &item) { double delta_t = UniverseUtil::GetGameTime() - Lshield_prev_time; if (delta_t > 0.5) { //0.5 = reaction time limit for hit rate - double delta_v = parent->ftl_drive.Enabled() ? 1 : parent->LShieldData() - Lshield_prev; + double delta_v = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::left) - Lshield_prev; value = delta_v / delta_t; Lshield_rate_old = value; Lshield_prev_time = UniverseUtil::GetGameTime(); - Lshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->LShieldData(); + Lshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::left); } else { value = Lshield_rate_old; } @@ -394,11 +395,11 @@ bool AggressiveAI::ProcessLogicItem(const AIEvents::AIEvresult &item) { double delta_t = UniverseUtil::GetGameTime() - Rshield_prev_time; if (delta_t > 0.5) { //0.5 = reaction time limit for hit rate - double delta_v = parent->ftl_drive.Enabled() ? 1 : parent->RShieldData() - Rshield_prev; + double delta_v = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::right) - Rshield_prev; value = delta_v / delta_t; Rshield_rate_old = value; Rshield_prev_time = UniverseUtil::GetGameTime(); - Rshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->RShieldData(); + Rshield_prev = parent->ftl_drive.Enabled() ? 1 : parent->shield.Percent(Shield::right); } else { value = Rshield_rate_old; } @@ -420,11 +421,11 @@ bool AggressiveAI::ProcessLogicItem(const AIEvents::AIEvresult &item) { double delta_t = UniverseUtil::GetGameTime() - Hull_prev_time; if (delta_t > 0.5) { //0.5 = reaction time limit for hit rate - double delta_v = parent->GetHullPercent() - Hull_prev; + double delta_v = parent->hull.Percent() - Hull_prev; value = delta_v / delta_t; Hull_rate_old = value; Hull_prev_time = UniverseUtil::GetGameTime(); - Hull_prev = parent->GetHullPercent(); + Hull_prev = parent->hull.Percent(); } else { value = Hull_rate_old; } @@ -1190,7 +1191,7 @@ void AggressiveAI::ReCommandWing(Flightgroup *fg) { //computer won't override capital orders if (nullptr != (lead = fg->leader.GetUnit())) { if (float ( rand())/RAND_MAX < simulation_atom_var / time_to_recommand_wing) { - if (parent->Threat() && (parent->FShieldData() < .2 || parent->RShieldData() < .2)) { + if (parent->Threat() && (parent->shield.Percent() < .2)) { fg->directive = string("h"); LeadMe(parent, "h", "I need help here!", false); if (verbose_debug) { diff --git a/engine/src/cmd/ai/firekeyboard.cpp b/engine/src/cmd/ai/firekeyboard.cpp index cb72144a65..96c82c539f 100644 --- a/engine/src/cmd/ai/firekeyboard.cpp +++ b/engine/src/cmd/ai/firekeyboard.cpp @@ -1194,7 +1194,7 @@ bool ChooseTargets(Unit *me, bool (*typeofunit)(Unit *, Unit *), bool reverse) { int cur = 0; while (1) { while (veciter != vec.end()) { - if (((*veciter) != me) && ((*veciter)->GetHull() >= 0) && typeofunit(me, (*veciter))) { + if (((*veciter) != me) && ((*veciter)->hull.Get() >= 0) && typeofunit(me, (*veciter))) { me->Target(*veciter); if ((*veciter) != NULL) { @@ -1541,7 +1541,7 @@ void FireKeyboard::ProcessCommMessage(class CommunicationMessage &c) { } //wait till later bool reallydospeech = false; - if (un && un->GetHull() > 0) { + if (un && !un->Destroyed()) { reallydospeech = true; for (list::iterator i = resp.begin(); i != resp.end(); i++) { if ((*i).sender.GetUnit() == un) { @@ -1715,7 +1715,7 @@ void FireKeyboard::Execute() { if (targ) { double mm = 0.0; ShouldFire(targ); - if (targ->GetHull() < 0) { + if (targ->Destroyed()) { parent->Target(NULL); ForceChangeTarget(parent); refresh_target = true; @@ -1732,7 +1732,7 @@ void FireKeyboard::Execute() { float f_result = f().shieldpowerstate; if (f_result != 1) { - parent->shield->AdjustPower(f_result); + parent->shield.AdjustPower(f_result); } if (f().firekey == PRESS || f().jfirekey == PRESS || j().firekey == DOWN || j().jfirekey == DOWN) { if (!_Universe->AccessCockpit()->CanDrawNavSystem()) { diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index 655e260d22..0f211b96de 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -245,7 +245,7 @@ string buildShipDescription(Cargo &item, string &descriptiontexture); string buildCargoDescription(const Cargo &item, BaseComputer &computer, float price); //put in buffer a pretty prepresentation of the POSITIVE float f (ie 4,732.17) void prettyPrintFloat(char *buffer, float f, int digitsBefore, int digitsAfter, int bufferLen = 128); -string buildUpgradeDescription(Cargo &item); +string buildUpgradeDescription(Cargo &item, std::map ship_map); int basecargoassets(Unit *base, string cargoname); //"Basic Repair" item that is added to Buy UPGRADE mode. @@ -1922,7 +1922,7 @@ void BaseComputer::updateTransactionControlsForSelection(TransactionList *tlist) } descString += tempString; if (item.GetDescription() == "" || item.GetDescription()[0] != '#') { - item.SetDescription(buildUpgradeDescription(item)); + item.SetDescription(buildUpgradeDescription(item, std::map())); } break; case BUY_SHIP: @@ -1989,8 +1989,8 @@ void BaseComputer::updateTransactionControlsForSelection(TransactionList *tlist) //******************************************************************************************** { - double percent_working = m_player.GetUnit() ? UnitUtil::PercentOperational( - m_player.GetUnit(), item.GetName(), item.GetCategory(), false) : 0.0; + double percent_working = UnitUtil::PercentOperational( + m_player.GetUnit(), item.GetName(), item.GetCategory(), false); if (percent_working < 1) { //IF DAMAGED tempString = (boost::format("Damaged and Used value: #b#%1$.2f#-b, purchased for %2$.2f#n1.5#") @@ -1998,10 +1998,11 @@ void BaseComputer::updateTransactionControlsForSelection(TransactionList *tlist) % item.GetPrice()) .str(); descString += tempString; - + double repair_price = RepairPrice(percent_working, baseUnit->PriceCargo(item.GetName())); + tempString = (boost::format("Percent Working: #b#%1$.2f#-b, Repair Cost: %2$.2f#n1.5#") % (percent_working * 100) - % RepairPrice(percent_working, baseUnit->PriceCargo(item.GetName()))) + % repair_price) .str(); descString += tempString; } else { @@ -2017,7 +2018,8 @@ void BaseComputer::updateTransactionControlsForSelection(TransactionList *tlist) } //******************************************************************************************** if (item.GetDescription() == "" || item.GetDescription()[0] != '#') { - item.SetDescription(buildUpgradeDescription(item)); + std::map ship_map = m_player.GetUnit()->UnitToMap(); + item.SetDescription(buildUpgradeDescription(item, ship_map)); } break; } @@ -3781,6 +3783,7 @@ bool BaseComputer::fixUpgrade(const EventCommandId &command, Control *control) { Cargo *item = selectedItem(); Unit *playerUnit = m_player.GetUnit(); Unit *baseUnit = m_base.GetUnit(); + if (baseUnit && playerUnit && item) { float *credits = NULL; Cockpit *cp = _Universe->isPlayerStarship(playerUnit); @@ -3940,11 +3943,11 @@ string buildShipDescription(Cargo &item, std::string &texturedescription) { } //UNDER CONSTRUCTION -string buildUpgradeDescription(Cargo &item) { +string buildUpgradeDescription(Cargo &item, std::map ship_map) { const std::string key = item.GetName() + "__upgrades"; - PyObject* args = PyTuple_Pack(1, PyUnicode_FromString(key.c_str())); + ship_map["upgrade_key"] = key; const std::string text = GetString("get_upgrade_info", "upgrade_view", - "upgrade_view.py", args); + "upgrade_view.py", ship_map); return text; } diff --git a/engine/src/cmd/carrier.cpp b/engine/src/cmd/carrier.cpp index 7e6112a651..4f0d6cc479 100644 --- a/engine/src/cmd/carrier.cpp +++ b/engine/src/cmd/carrier.cpp @@ -186,6 +186,21 @@ void Carrier::EjectCargo(unsigned int index) { if (index < numCargo()) { tmp = &GetCargo(index); } + + // Some sanity checks for tmp + // Can't eject an upgrade, unless ship is destroyed + if(tmp->GetInstalled() && !unit->hull.Destroyed()) { + return; + } + + // Can't eject cargo from the hidden hold unless ship is destroyed. + // TODO: implement + + // Make sure ejected mass isn't 0. This causes game to mishandle + if(tmp->GetMass() == 0) { + tmp->SetMass(0.01); + } + static float cargotime = XMLSupport::parse_float(vs_config->getVariable("physics", "cargo_live_time", "600")); if (tmp) { string tmpcontent = tmp->name; diff --git a/engine/src/cmd/collision.cpp b/engine/src/cmd/collision.cpp index fec749cd64..f71b7f2494 100644 --- a/engine/src/cmd/collision.cpp +++ b/engine/src/cmd/collision.cpp @@ -120,13 +120,11 @@ void Collision::shouldApplyForceAndDealDamage(Unit *other_unit) { return; } - // I've changed the behavior of enhancements for now. - // Instead of upgrading the shields, the simply max them out - // at 150%. + // disabled for now. // TODO: someone from the "product" team needs to define the // exact behavior. Preferably after we sort the upgrade // code. - other_unit->shield->Enhance(); + /*double percent; char tempdata[sizeof(Shield)]; diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index cc1703bb91..b01877c3b4 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -39,34 +39,24 @@ #include "pilot.h" #include "ai/comm_ai.h" #include "gfx/mesh.h" +#include "vega_cast_utils.h" #include #include "configuration/configuration.h" -bool Damageable::ShieldUp(const Vector &pnt) const { - const int shield_min = 5; - - // TODO: think about this. I have no idea why a nebula needs shields -// static float nebshields = XMLSupport::parse_float( vs_config->getVariable( "physics", "nebula_shield_recharge", ".5" ) ); -// if (nebula != NULL || nebshields > 0) -// return false; - - CoreVector attack_vector(pnt.i, pnt.j, pnt.k); - - DamageableLayer shield = const_cast(this)->GetShieldLayer(); - int facet_index = shield.GetFacetIndex(attack_vector); - return shield.facets[facet_index].health > shield_min; -} float Damageable::DealDamageToHull(const Vector &pnt, float damage) { + Unit *unit = vega_dynamic_cast_ptr(this); + Damage dmg(0, damage); // Bypass shield with phase damage CoreVector attack_vector(pnt.i, pnt.j, pnt.k); InflictedDamage inflicted_damage(3); - armor->DealDamage(attack_vector, dmg, inflicted_damage); - hull->DealDamage(attack_vector, dmg, inflicted_damage); - int facet_index = armor->GetFacetIndex(attack_vector); + unit->armor.DealDamage(attack_vector, dmg, inflicted_damage); + unit->hull.DealDamage(attack_vector, dmg, inflicted_damage); + int facet_index = unit->armor.GetFacetIndex(attack_vector); - float denominator = GetArmor(facet_index) + GetHull(); + float denominator = unit->armor.facets[facet_index].Value() + + unit->hull.Get(); if (denominator == 0) { return 0; } @@ -75,13 +65,15 @@ float Damageable::DealDamageToHull(const Vector &pnt, float damage) { } float Damageable::DealDamageToShield(const Vector &pnt, float &damage) { + Unit *unit = vega_dynamic_cast_ptr(this); Damage dmg(damage); CoreVector attack_vector(pnt.i, pnt.j, pnt.k); InflictedDamage inflicted_damage(3); - shield->DealDamage(attack_vector, dmg, inflicted_damage); - int facet_index = shield->GetFacetIndex(attack_vector); + unit->shield.DealDamage(attack_vector, dmg, inflicted_damage); + int facet_index = unit->shield.GetFacetIndex(attack_vector); - float denominator = GetShield(facet_index) + GetHull(); + float denominator = unit->shield.facets[facet_index].Value() + + unit->hull.Get(); if (denominator == 0) { return 0; } @@ -98,11 +90,10 @@ void Damageable::ApplyDamage(const Vector &pnt, Unit *affected_unit, const GFXColor &color, void *ownerDoNotDereference) { + Unit *unit = vega_dynamic_cast_ptr(this); + InflictedDamage inflicted_damage(3); - const Damageable *const_damagable = static_cast(this); - Unit *unit = static_cast(this); - //We also do the following lock on client side in order not to display shield hits const bool no_dock_damage = configuration()->physics_config.no_damage_to_docked_ships; const bool apply_difficulty_enemy_damage = configuration()->physics_config.difficulty_based_enemy_damage; @@ -114,7 +105,7 @@ void Damageable::ApplyDamage(const Vector &pnt, } // Stop processing for destroyed units - if (Destroyed()) { + if (unit->Destroyed()) { return; } @@ -129,11 +120,7 @@ void Damageable::ApplyDamage(const Vector &pnt, Vector localpnt(InvTransform(unit->cumulative_transformation_matrix, pnt)); Vector localnorm(unit->ToLocalCoordinates(normal)); CoreVector attack_vector(localpnt.i, localpnt.j, localpnt.k); - float previous_hull_percent = GetHullPercent(); - - /*float hullpercent = GetHullPercent(); - bool mykilled = Destroyed(); - bool armor_damage = false;*/ + float previous_hull_percent = unit->hull.Percent(); inflicted_damage = DealDamage(attack_vector, damage); @@ -170,7 +157,7 @@ void Damageable::ApplyDamage(const Vector &pnt, } } - if (Destroyed()) { + if (unit->Destroyed()) { unit->ClearMounts(); if (shooter_is_player) { @@ -245,7 +232,7 @@ void Damageable::ApplyDamage(const Vector &pnt, // Light shields if hit if (inflicted_damage.inflicted_damage_by_layer[2] > 0) { - unit->LightShields(pnt, normal, GetShieldPercent(), color); + unit->LightShields(pnt, normal, unit->shield.Percent(), color); } // Apply damage to meshes @@ -255,7 +242,7 @@ void Damageable::ApplyDamage(const Vector &pnt, for (unsigned int i = 0; i < unit->nummesh(); ++i) { // TODO: figure out how to adjust looks for armor damage - float hull_damage_percent = static_cast(const_damagable->GetHullPercent()); + float hull_damage_percent = unit->hull.Percent(); unit->meshdata[i]->AddDamageFX(pnt, unit->shieldtight ? unit->shieldtight * normal : Vector(0, 0, 0), hull_damage_percent, color); } @@ -286,7 +273,7 @@ void Damageable::ApplyDamage(const Vector &pnt, // Only happens if we crossed the threshold in this attack if (previous_hull_percent >= configuration()->ai.hull_percent_for_comm && - GetHullPercent() < configuration()->ai.hull_percent_for_comm && + unit->hull.Percent() < configuration()->ai.hull_percent_for_comm && (shooter_is_player || shot_at_is_player)) { Unit *computer_ai = nullptr; Unit *player = nullptr; @@ -310,7 +297,7 @@ void Damageable::ApplyDamage(const Vector &pnt, if (shooter_is_player && configuration()->ai.assist_friend_in_need) { AllUnitsCloseAndEngage(player, computer_ai->faction); } - if (GetHullPercent() > 0 || !shooter_cockpit) { + if (unit->hull.Percent() > 0 || !shooter_cockpit) { CommunicationMessage c(computer_ai, player, anim, gender); c.SetCurrentState(shooter_cockpit ? c.fsm->GetDamagedNode() : c.fsm->GetDealtDamageNode(), anim, @@ -346,13 +333,18 @@ void Damageable::ApplyDamage(const Vector &pnt, } // TODO: get rid of extern -extern bool DestroySystem(float hull, float maxhull, float numhits); -extern bool DestroyPlayerSystem(float hull, float maxhull, float numhits); +extern bool DestroySystem(float hull_percent, float numhits); +extern bool DestroyPlayerSystem(float hull_percent, float numhits); extern float rand01(); extern const Unit *loadUnitByCache(std::string name, int faction); void Damageable::DamageRandomSystem(InflictedDamage inflicted_damage, bool player, Vector attack_vector) { - Unit *unit = static_cast(this); + Unit *unit = vega_dynamic_cast_ptr(this); + + // Ship destroyed. No point in further work + if(unit->Destroyed()) { + return; + } bool hull_damage = inflicted_damage.inflicted_damage_by_layer[0] > 0; bool armor_damage = inflicted_damage.inflicted_damage_by_layer[0] > 0; @@ -363,22 +355,26 @@ void Damageable::DamageRandomSystem(InflictedDamage inflicted_damage, bool playe } bool damage_system; - double current_hull = layers[0].facets[0].health; - double max_hull = layers[0].facets[0].max_health; if (player) { - damage_system = DestroyPlayerSystem(current_hull, max_hull, 1); + damage_system = DestroyPlayerSystem(unit->hull.Percent(), 1); } else { - damage_system = DestroySystem(current_hull, max_hull, 1); + damage_system = DestroySystem(unit->hull.Percent(), 1); } if (!damage_system) { return; } - unit->DamageRandSys(configuration()->physics_config.indiscriminate_system_destruction * rand01() + - (1 - configuration()->physics_config.indiscriminate_system_destruction) * (1 - ((current_hull) > 0 ? hull_damage / (current_hull) : 1.0f)), - attack_vector); + // A brief explanation + // indiscriminate is a fraction (25% by default). + // Damage is calculated as 0.25 * rand + 0.75 * (hull_damage)/(current_hull) + // Therefore, + double indiscriminate_system_destruction = configuration()->physics_config.indiscriminate_system_destruction; + double random_damage_factor = indiscriminate_system_destruction * rand01(); + double hull_damage_modifier = 1 - indiscriminate_system_destruction; + double hull_damage_factor = hull_damage_modifier * (1 - hull_damage / unit->hull.Get()); + unit->DamageRandSys(random_damage_factor + hull_damage_factor, attack_vector); } void Damageable::DamageCargo(InflictedDamage inflicted_damage) { @@ -405,7 +401,7 @@ void Damageable::DamageCargo(InflictedDamage inflicted_damage) { return; } - Unit *unit = static_cast(this); + Unit *unit = vega_dynamic_cast_ptr(this); // If nothing to damage, exit if (unit->numCargo() == 0) { @@ -413,9 +409,7 @@ void Damageable::DamageCargo(InflictedDamage inflicted_damage) { } // Is the hit unit, lucky or not - double current_hull = layers[0].facets[0].health; - double max_hull = layers[0].facets[0].max_health; - if (DestroySystem(current_hull, max_hull, unit->numCargo())) { + if (DestroySystem(unit->hull.Percent(), unit->numCargo())) { return; } @@ -459,9 +453,9 @@ extern void DestroyMount(Mount *); // TODO: a lot of this should be handled by RAII void Damageable::Destroy() { - Unit *unit = static_cast(this); + Unit *unit = vega_dynamic_cast_ptr(this); - DamageableObject::Destroy(); + unit->hull.Destroy(); if (!unit->killed) { for (int beamcount = 0; beamcount < unit->getNumMounts(); ++beamcount) { @@ -474,119 +468,20 @@ void Damageable::Destroy() { } } -// We only support 2 and 4 facet shields ATM -// This doesn't have proper checks -float ShieldData(const Damageable *unit, int facet_index) { - return (unit->shield->facets[facet_index].health) / - (unit->shield->facets[facet_index].max_health); -} - -float Damageable::FShieldData() const { - switch (shield->number_of_facets) { - case 2: - return ShieldData(this, 0); - case 4: - return ShieldData(this, 2); - default: - return 0.0f; // We only support 2 and 4 facet shields ATM - } -} - -float Damageable::BShieldData() const { - switch (shield->number_of_facets) { - case 2: - return ShieldData(this, 1); - case 4: - return ShieldData(this, 3); - default: - return 0.0f; // We only support 2 and 4 facet shields ATM - } -} -float Damageable::LShieldData() const { - switch (shield->number_of_facets) { - case 2: - return 0.0f; - case 4: - return ShieldData(this, 0); - default: - return 0.0f; // We only support 2 and 4 facet shields ATM - } -} -float Damageable::RShieldData() const { - switch (shield->number_of_facets) { - case 2: - return 0.0f; - case 4: - return ShieldData(this, 1); - default: - return 0.0f; // We only support 2 and 4 facet shields ATM - } -} -//short fix -void Damageable::ArmorData(float armor[8]) const { - Damageable *damageable = const_cast(this); - DamageableLayer armor_layer = damageable->GetArmorLayer(); - for (int i = 0; i < armor_layer.number_of_facets; ++i) { - armor[i] = armor_layer.facets[i].health; - } -} // TODO: fix typo void Damageable::leach(float damShield, float damShieldRecharge, float damEnRecharge) { // TODO: restore this lib_damage } -void Damageable::RegenerateShields(const float difficulty, const bool player_ship) { - const bool shields_in_spec = configuration()->physics_config.shields_in_spec; - const float discharge_per_second = configuration()->physics_config.speeding_discharge; - //approx - const float discharge_rate = (1 - (1 - discharge_per_second) * simulation_atom_var); - const float min_shield_discharge = configuration()->physics_config.min_shield_speeding_discharge; - const float nebshields = configuration()->physics_config.nebula_shield_recharge; - - Unit *unit = static_cast(this); - - const bool in_warp = unit->ftl_drive.Enabled(); - const int shield_facets = unit->shield->number_of_facets; - const float total_max_shields = unit->shield->TotalMaxLayerValue(); - - // No point in all this code if there are no shields. - if (shield_facets < 2 || total_max_shields == 0) { - return; - } - - float shield_recharge = unit->constrained_charge_to_shields * simulation_atom_var; - - if (unit->GetNebula() != nullptr) { - shield_recharge *= nebshields; - } - - // Adjust other (enemy) ships for difficulty - if (!player_ship) { - shield_recharge *= difficulty; - } - - - // Discharge shields due to energy or SPEC or cloak - if ((in_warp && !shields_in_spec) || !unit->sufficient_energy_to_recharge_shields || - unit->cloak.Active()) { - shield->Discharge(discharge_rate, min_shield_discharge); - } else { - // Shield regeneration - shield->Regenerate(shield_recharge); - } -} - -float Damageable::MaxShieldVal() const { - Damageable *damageable = const_cast(this); - return static_cast(damageable->GetShieldLayer()).AverageMaxLayerValue(); -} bool Damageable::flickerDamage() { - float damagelevel = GetHullPercent(); + Unit *unit = vega_dynamic_cast_ptr(this); + + float damagelevel = unit->hull.Percent(); if (damagelevel == 0) { return false; } diff --git a/engine/src/cmd/damageable.h b/engine/src/cmd/damageable.h index dca58b7676..8d57ed167a 100644 --- a/engine/src/cmd/damageable.h +++ b/engine/src/cmd/damageable.h @@ -32,33 +32,20 @@ class Unit; struct GFXColor; +// TODO: most of what's left in this class should go up to lib damage. +// The rest can be refactored elsewhere and the class deleted. + /** * @brief The Damageable class TODO */ class Damageable : public DamageableObject { public: - DamageableLayer *hull; - DamageableLayer *armor; - DamageableLayer *shield; - - // These are only used for upgrade due to macros - // TODO: refactor - float upgrade_hull; - - float shield_regeneration; - //Is dead already? bool killed; // Methods public: - Damageable() : hull(&layers[0]), - armor(&layers[1]), - shield(&layers[2]), - upgrade_hull(0), - shield_regeneration(0), - killed(false) { - } + Damageable() : killed(false) {} protected: virtual ~Damageable() = default; @@ -68,51 +55,6 @@ class Damageable : public DamageableObject { Damageable &operator=(const Damageable &) = delete; public: - // We follow the existing convention of GetX for the actual health value - // because we are constrained by existing python interfaces, which cannot - // be easily changed. - // TODO: convert all calls to *current_hull - const float GetHull() const { - return layers[0].facets[0].health; - } - - // TODO: check for valid index - const float GetArmor(int facet = 0) const { - return armor->facets[facet].health; - } - - const float GetShield(int facet = 0) const { - return shield->facets[facet].health; - } - - DamageableLayer &GetHullLayer() { - return layers[0]; - } - - DamageableLayer &GetArmorLayer() { - return layers[1]; - } - - DamageableLayer &GetShieldLayer() { - return layers[2]; - } - - const float GetShieldRegeneration() const { - return shield->facets[as_integer(FacetName::left_top_front)].regeneration; - } - - virtual const float GetHullPercent() const { - return hull->GetPercent(FacetName::single); - } - - virtual const float GetShieldPercent() const { - return shield->GetPercent(FacetName::left_top_front); - } - - void ArmorData(float armor[8]) const; - - // Is the shield up from this position - bool ShieldUp(const Vector &) const; // Currently available in case someone wants to call from python // but there isn't an interface @@ -131,27 +73,13 @@ class Damageable : public DamageableObject { void DamageRandomSystem(InflictedDamage inflicted_damage, bool player, Vector attack_vector); void DamageCargo(InflictedDamage inflicted_damage); void Destroy(); //explodes then deletes - - float FShieldData() const; - float RShieldData() const; - float LShieldData() const; - float BShieldData() const; - //short fix - // void ArmorData( float armor[8] ) const; - //Gets the current status of the hull - // float GetHull() const - // { - // return hull; - // } + //reduces shields to X percentage and reduces shield recharge to Y percentage void leach(float XshieldPercent, float YrechargePercent, float ZenergyPercent); - float MaxShieldVal() const; - void RegenerateShields(const float difficulty, const bool player_ship); - bool flickerDamage(); }; diff --git a/engine/src/cmd/drawable.cpp b/engine/src/cmd/drawable.cpp index 59f601802b..cbe82d66df 100644 --- a/engine/src/cmd/drawable.cpp +++ b/engine/src/cmd/drawable.cpp @@ -207,12 +207,9 @@ void Drawable::Draw(const Transformation &parent, const Matrix &parentMatrix) { Matrix wmat; if (!cam_setup_phase) { - double current_hull = unit->layers[0].facets[0].health; - double max_hull = unit->layers[0].facets[0].max_health; - // Following stuff is only needed in actual drawing phase - if ((current_hull) < (max_hull)) { - damagelevel = (current_hull) / (max_hull); + if (unit->hull.Damaged()) { + damagelevel = unit->hull.Percent(); chardamage = (255 - (unsigned char) (damagelevel * 255)); } avgscale = sqrt((ctm->getP().MagnitudeSquared() + ctm->getR().MagnitudeSquared()) * 0.5); @@ -390,8 +387,8 @@ void Drawable::DrawNow(const Matrix &mato, float lod) { } float damagelevel = 1.0; unsigned char chardamage = 0; - if (unit->layers[0].facets[0].health < unit->layers[0].facets[0].max_health) { - damagelevel = (unit->layers[0].facets[0].Percent()); + if (unit->hull.Damaged()) { + damagelevel = unit->hull.Percent(); chardamage = (255 - (unsigned char) (damagelevel * 255)); } #ifdef VARIABLE_LENGTH_PQR @@ -491,7 +488,7 @@ void Drawable::DrawNow(const Matrix &mato, float lod) { Scale, unit->cloak.Visibility(), 0, - unit->GetHullPercent(), + unit->hull.Percent(), velocity, linaccel, angaccel, @@ -654,7 +651,7 @@ void Drawable::Sparkle(bool on_screen, Matrix *ctm) { } // Destroyed units (dying?) don't sparkle - if (unit->GetHull() <= 0) { + if (unit->Destroyed()) { return; } @@ -664,7 +661,7 @@ void Drawable::Sparkle(bool on_screen, Matrix *ctm) { } // Undamaged units don't sparkle - float damage_level = unit->layers[0].facets[0].Percent(); + float damage_level = unit->hull.Percent(); if (damage_level >= .99) { return; } @@ -733,11 +730,10 @@ void Drawable::DrawHalo(bool on_screen, float apparent_size, Matrix wmat, Cloak } Vector Scale(1, 1, 1); //Now, HaloSystem handles that - float damage_level = unit->layers[0].facets[0].Percent(); //WARNING: cmas is not a valid maximum speed for the upcoming multi-direction thrusters, //nor is maxaccel. Instead, each halo should have its own limits specified in units.csv float nebd = (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula != nullptr) ? -1 : 0; - float hulld = unit->GetHull() > 0 ? damage_level : 1.0; + float hulld = unit->Destroyed() ? 1 : unit->hull.Percent(); halos->Draw(wmat, Scale, unit->cloak.Visibility(), nebd, hulld, velocity, linaccel, angaccel, maxaccel, cmas, unit->faction); @@ -930,7 +926,7 @@ void Drawable::Split(int level) { tempmeshes.push_back(old[k]); } unit->SubUnits.prepend(splitsub = new Unit(tempmeshes, true, unit->faction)); - splitsub->layers[0].facets[0] = Health(0, 1000); + splitsub->hull.Set(1000.0); splitsub->name = "debris"; splitsub->setMass(game_options()->debris_mass * splitsub->getMass() / level); splitsub->pImage->timeexplode = .1; diff --git a/engine/src/cmd/energetic.cpp b/engine/src/cmd/energetic.cpp index 15c1fd683d..a67c311c14 100644 --- a/engine/src/cmd/energetic.cpp +++ b/engine/src/cmd/energetic.cpp @@ -71,66 +71,14 @@ float Energetic::getFuelUsage(bool afterburner) { } - - - - - -float Energetic::maxEnergyData() const { - const Unit *unit = vega_dynamic_cast_ptr(this); - return unit->energy.MaxLevel(); -} - -void Energetic::rechargeEnergy() { - Unit *unit = vega_dynamic_cast_ptr(this); - if ((!configuration()->fuel.reactor_uses_fuel) || (!unit->fuel.Depleted())) { - unit->energy.Charge(unit->reactor.Capacity() * simulation_atom_var); - } -} - - void Energetic::setEnergyRecharge(float enrech) { Unit *unit = vega_dynamic_cast_ptr(this); unit->reactor.SetCapacity(enrech); } -// Basically max or current shield x 0.2 -float Energetic::totalShieldEnergyCapacitance() const { - const Unit *unit = vega_dynamic_cast_ptr(this); - DamageableLayer *shield = unit->shield; - - float total_max_shield_value = shield->TotalMaxLayerValue(); - float total_current_shield_value = shield->TotalLayerValue(); - - return configuration()->physics_config.shield_energy_capacitance * (configuration()->physics_config.use_max_shield_energy_usage ? total_max_shield_value : total_current_shield_value); -} - -// The original code was in unit_generic:5476 RegenShields and was simply -// incomprehensible. After several days, I've written a similar version. -// However, someone who understands the previous code can refactor this easily -// or better yet, write plugable consumption models. -//GAHHH reactor in units of 100MJ, shields in units of VSD=5.4MJ to make 1MJ of shield use 1/shieldenergycap MJ -void Energetic::ExpendEnergy(const bool player_ship) { - Unit *unit = vega_dynamic_cast_ptr(this); - - // TODO: if we run out of fuel or energy, we die from lack of air - - MaintainShields(); - ExpendEnergyToRechargeShields(); - MaintainECM(); - DecreaseWarpEnergyInWarp(); - unit->reactor.Generate(); - unit->drive.Consume(); -} - -void Energetic::ExpendEnergy(float usage) { - Unit *unit = vega_dynamic_cast_ptr(this); - // Operator overloaded to prevent negative usage - unit->energy.Deplete(true, usage); -} void Energetic::MaintainECM() { @@ -141,96 +89,9 @@ void Energetic::MaintainECM() { } float sim_atom_ecm = configuration()->fuel.ecm_energy_cost * unit->ecm * simulation_atom_var; - ExpendEnergy(sim_atom_ecm); -} - -void Energetic::MaintainShields() { - Unit *unit = vega_dynamic_cast_ptr(this); - - const bool in_warp = unit->ftl_drive.Enabled(); - const int shield_facets = unit->shield->number_of_facets; - - if (in_warp && !configuration()->physics_config.shields_in_spec) { - return; - } - - if (unit->shield->TotalMaxLayerValue() == 0) { - return; - } - - // TODO: lib_damage restore efficiency by replacing with shield->efficiency - const float efficiency = 1; - - const float shield_maintenance = unit->shield->GetRegeneration() * VSDPercent() * - efficiency / configuration()->physics_config.shield_energy_capacitance * shield_facets * - configuration()->physics_config.shield_maintenance_charge * simulation_atom_var; - - sufficient_energy_to_recharge_shields = shield_maintenance > unit->energy.Level(); - - ExpendEnergy(shield_maintenance); -} - -void Energetic::ExpendEnergyToRechargeShields() { - Unit *unit = vega_dynamic_cast_ptr(this); - - const bool in_warp = unit->ftl_drive.Enabled(); - - // TODO: add has_shields function instead of check below - if (unit->shield->TotalMaxLayerValue() == 0) { - return; - } - - if (in_warp && !configuration()->physics_config.shields_in_spec) { - return; - } - - double current_shield_value = unit->shield->TotalLayerValue(); - double max_shield_value = unit->shield->TotalMaxLayerValue(); - double regeneration = unit->shield->GetRegeneration(); - double maximum_charge = std::min(max_shield_value - current_shield_value, regeneration); - - // Here we store the actual charge we'll use in RegenShields - constrained_charge_to_shields = maximum_charge; - sufficient_energy_to_recharge_shields = (constrained_charge_to_shields > 0); - double actual_charge = std::min(maximum_charge, unit->energy.Level()); - double energy_required_to_charge = actual_charge * VSDPercent() * - simulation_atom_var; - ExpendEnergy((float)energy_required_to_charge); + unit->energy.Deplete(false, sim_atom_ecm); } -void Energetic::RechargeWarpCapacitors(const bool player_ship) { - Unit *unit = vega_dynamic_cast_ptr(this); - - // Will try to keep the percentage of warp and normal capacitors equal - const double transfer_capacity = 0.005f; - const double capacitor_percent = unit->energy.Percent(); - const double warp_capacitor_percent = unit->ftl_energy.Percent(); - const double warp_multiplier = WarpEnergyMultiplier(player_ship); - - if (warp_capacitor_percent >= 1.0f || - warp_capacitor_percent > capacitor_percent || - capacitor_percent < 0.10f) { - return; - } - - const double previous_energy = unit->energy.Level(); - ExpendEnergy((float)(unit->energy.MaxLevel() * transfer_capacity)); - - const double actual_energy = previous_energy - unit->energy.Level(); - unit->ftl_energy.SetLevel(std::min(unit->ftl_energy.MaxLevel(), unit->ftl_energy.Level() + actual_energy * warp_multiplier)); -} - -float Energetic::WarpEnergyMultiplier(const bool player_ship) { - Unit *unit = static_cast(this); - bool player = player_ship; - - // We also apply player multiplier to wing members - Flightgroup *flight_group = unit->getFlightgroup(); - if (flight_group && !player_ship) { - player = _Universe->isPlayerStarship(flight_group->leader.GetUnit()) != nullptr; - } - return player ? configuration()->warp_config.player_warp_energy_multiplier : configuration()->warp_config.warp_energy_multiplier; -} float Energetic::VSDPercent() { return configuration()->fuel.vsd_mj_yield / 100; diff --git a/engine/src/cmd/energetic.h b/engine/src/cmd/energetic.h index d7f462b444..27e8627709 100644 --- a/engine/src/cmd/energetic.h +++ b/engine/src/cmd/energetic.h @@ -37,26 +37,16 @@ class Energetic { static float getFuelUsage(bool afterburner); - void ExpendEnergy(const bool player_ship); - void ExpendEnergy(float usage); - void ExpendEnergyToRechargeShields(); - float maxEnergyData() const; - void MaintainECM(); - void MaintainShields(); - void rechargeEnergy(); void RechargeWarpCapacitors(const bool player_ship); void setEnergyRecharge(float enrech); - float totalShieldEnergyCapacitance() const; - static float VSDPercent(); - float WarpEnergyMultiplier(const bool player_ship); float constrained_charge_to_shields; diff --git a/engine/src/cmd/missile.cpp b/engine/src/cmd/missile.cpp index b0d9f66271..73de038747 100644 --- a/engine/src/cmd/missile.cpp +++ b/engine/src/cmd/missile.cpp @@ -187,10 +187,6 @@ Missile::Missile(const char *filename, discharged(false), retarget(-1), had_target(false) { - // TODO: why would a sparkling missile be four times as hard to kill??? - if (configuration()->graphics_config.missile_sparkle) { - layers[0].facets[0] = Health(0, layers[0].facets[0].max_health * 4); - } } void Missile::Discharge() { diff --git a/engine/src/cmd/music.cpp b/engine/src/cmd/music.cpp index ae62ed9bd6..65f3744818 100644 --- a/engine/src/cmd/music.cpp +++ b/engine/src/cmd/music.cpp @@ -118,11 +118,6 @@ Music::Music(Unit *parent) : random(false), p(parent), song(-1), thread_initiali return; } lastlist = PEACELIST; - if (parent) { - maxhull = parent->GetHull(); - } else { - maxhull = 1; - } int i; const char *listvars[MAXLIST] = {"battleplaylist", "peaceplaylist", "panicplaylist", "victoryplaylist", "lossplaylist"}; diff --git a/engine/src/cmd/music.h b/engine/src/cmd/music.h index 9eff91d358..35e5bcd454 100644 --- a/engine/src/cmd/music.h +++ b/engine/src/cmd/music.h @@ -44,7 +44,6 @@ extern DWORD WINAPI readerThread( PVOID input ); class Music { bool random; - float maxhull; int socketr; int socketw; UnitContainer p; diff --git a/engine/src/cmd/planet.cpp b/engine/src/cmd/planet.cpp index 2472184778..f84d125952 100644 --- a/engine/src/cmd/planet.cpp +++ b/engine/src/cmd/planet.cpp @@ -427,7 +427,7 @@ void Planet::InitPlanet(QVector x, const float densityOfRock = configuration()->physics_config.density_of_rock; const float densityOfJumpPoint = configuration()->physics_config.density_of_jump_point; //static float massofplanet = XMLSupport::parse_float(vs_config->getVariable("physics","mass_of_planet","10000000")); - layers[0].facets[0] = Health(0, (4.0 / 3.0) * M_PI * radius * radius * radius * (notJumppoint ? densityOfRock : densityOfJumpPoint)); + hull.Set((4.0 / 3.0) * M_PI * radius * radius * radius * (notJumppoint ? densityOfRock : densityOfJumpPoint)); this->Mass = (4.0 / 3.0) * M_PI * radius * radius * radius * (notJumppoint ? densityOfRock : (densityOfJumpPoint / 100000)); SetAI(new PlanetaryOrbit(this, vely, pos, x, y, orbitcent, parent)); //behavior @@ -573,9 +573,6 @@ void Planet::AddFog(const std::vector &v, bool opticalillusi } fawg->setFaceCamera(); getSubUnits().preinsert(fawg); - - // TODO: this is very weird. Why are we doing this? - // fawg->hull /= fawg->GetHullPercent(); #ifdef MESHONLY meshdata.push_back( shield ); #endif diff --git a/engine/src/cmd/planet.h b/engine/src/cmd/planet.h index b5b00faef0..043ed4362d 100644 --- a/engine/src/cmd/planet.h +++ b/engine/src/cmd/planet.h @@ -272,10 +272,6 @@ class Planet : public Unit { return lights; } - virtual const float GetHullPercent() const { - return 1.0f; - } - virtual const float GetShieldPercent() const { return 0.0f; } diff --git a/engine/src/cmd/script/script_call_unit_generic.cpp b/engine/src/cmd/script/script_call_unit_generic.cpp index a4beef853d..8af9b1db64 100644 --- a/engine/src/cmd/script/script_call_unit_generic.cpp +++ b/engine/src/cmd/script/script_call_unit_generic.cpp @@ -454,7 +454,7 @@ varInst *Mission::call_unit(missionNode *node, int mode) { } else if (method_id == CMT_UNIT_getFShieldData) { float res = 0.0; if (mode == SCRIPT_RUN) { - res = my_unit->FShieldData(); + res = my_unit->shield.Percent(Shield::front); } viret = newVarInst(VI_TEMP); viret->type = VAR_FLOAT; @@ -462,7 +462,7 @@ varInst *Mission::call_unit(missionNode *node, int mode) { } else if (method_id == CMT_UNIT_getRShieldData) { float res = 0.0; if (mode == SCRIPT_RUN) { - res = my_unit->RShieldData(); + res = my_unit->shield.Percent(Shield::right); } viret = newVarInst(VI_TEMP); viret->type = VAR_FLOAT; @@ -470,7 +470,7 @@ varInst *Mission::call_unit(missionNode *node, int mode) { } else if (method_id == CMT_UNIT_getLShieldData) { float res = 0.0; if (mode == SCRIPT_RUN) { - res = my_unit->LShieldData(); + res = my_unit->shield.Percent(Shield::left); } viret = newVarInst(VI_TEMP); viret->type = VAR_FLOAT; @@ -478,7 +478,7 @@ varInst *Mission::call_unit(missionNode *node, int mode) { } else if (method_id == CMT_UNIT_getBShieldData) { float res = 0.0; if (mode == SCRIPT_RUN) { - res = my_unit->BShieldData(); + res = my_unit->shield.Percent(Shield::back); } viret = newVarInst(VI_TEMP); viret->type = VAR_FLOAT; @@ -486,7 +486,7 @@ varInst *Mission::call_unit(missionNode *node, int mode) { } else if (method_id == CMT_UNIT_getEnergyData) { float res = 0.0; if (mode == SCRIPT_RUN) { - res = my_unit->energyData(); + res = my_unit->energy.Percent(); } viret = newVarInst(VI_TEMP); viret->type = VAR_FLOAT; @@ -494,7 +494,7 @@ varInst *Mission::call_unit(missionNode *node, int mode) { } else if (method_id == CMT_UNIT_getHullData) { float res = 0.0; if (mode == SCRIPT_RUN) { - res = my_unit->layers[0].facets[0].health; + res = my_unit->hull.Percent(); } viret = newVarInst(VI_TEMP); viret->type = VAR_FLOAT; diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index f707bbddc6..a04a369a20 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -726,195 +726,12 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ // Hull - float temp_hull = UnitCSVFactory::GetVariable(unit_key, "Hull", 0.0f); - float hull_values[1] = {temp_hull}; - hull->UpdateFacets(1, hull_values); + hull.Load(unit_key); + armor.Load(unit_key); + shield.Load(unit_key); specInterdiction = UnitCSVFactory::GetVariable(unit_key, "Spec_Interdiction", 0.0f); - - // Armor - // We support 3 options: - // 1. Minimized armor = x (single value). - // 2. New detailed armor (Front, back, left, right). - // 3. Old detailed (Front-left-top, ...). 8 facets converted to 4. - const std::string armor_single_value_string = UnitCSVFactory::GetVariable(unit_key, "armor", std::string()); - - if(armor_single_value_string != "") { - // Minimized - const double armor_single_value = std::stod(armor_single_value_string, 0); - armor->UpdateFacets(armor_single_value); - } else { - // Try new - std::string armor_keys[] = {"armor_front", "armor_back", - "armor_left", "armor_right"}; - bool new_form = true; - for (int i = 0; i < 4; i++) { - const std::string armor_string_value = UnitCSVFactory::GetVariable(unit_key, armor_keys[i], std::string()); - if(armor_string_value.empty()) { - new_form = false; - break; - } - - double armor_value = std::stod(armor_string_value); - armor->facets[i].health = armor_value; - armor->facets[i].max_health = armor_value; - armor->facets[i].adjusted_health = armor_value; - } - - // Fallback to old - if(!new_form) { - const std::string armor_keys[] = {"Armor_Front_Top_Left", - "Armor_Front_Top_Right", - "Armor_Front_Bottom_Left", - "Armor_Front_Bottom_Right", - "Armor_Back_Top_Left", - "Armor_Back_Top_Right", - "Armor_Back_Bottom_Left", - "Armor_Back_Bottom_Right"}; - - double old_armor_values[8]; - float new_armor_values[4]; - for (int i = 0; i < 8; i++) { - old_armor_values[i] = UnitCSVFactory::GetVariable(unit_key, armor_keys[i], 0.0f); - } - - // Conversion is tricky because new values are a square and old values are - // a two layered diamond. - new_armor_values[0] = (old_armor_values[0] + old_armor_values[1] + old_armor_values[2] + old_armor_values[3])/2; - new_armor_values[1] = (old_armor_values[4] + old_armor_values[5] + old_armor_values[5] + old_armor_values[5])/2; - new_armor_values[2] = (old_armor_values[0] + old_armor_values[2] + old_armor_values[4] + old_armor_values[6])/2; - new_armor_values[3] = (old_armor_values[1] + old_armor_values[3] + old_armor_values[5] + old_armor_values[7])/2; - armor->UpdateFacets(4,new_armor_values); - } - } - - // shield - - // We support 3 options: - // 1. Minimized shield_strength = x (single value). - // 2. New detailed shield (Front, back, left, right). - // 3. Old detailed (Front-left-top, ...). 4/2 facets converted to 4/2. - - // Some basic shield variables - // TODO: lib_damage figure out how leak and efficiency work - //char leak = static_cast(UnitCSVFactory::GetVariable(unit_key, "Shield_Leak", 0.0f) * 100); - - float regeneration = UnitCSVFactory::GetVariable(unit_key, "Shield_Recharge", 0.0f); - - // This is necessary for upgrading shields, as it's done with an ugly macro in - // unit_generic STDUPGRADE - shield_regeneration = regeneration; - shield->UpdateRegeneration(regeneration); - //float efficiency = UnitCSVFactory::GetVariable(unit_key, "Shield_Efficiency", 1.0f ); - - // Get shield count - std::map shield_sections{}; - std::vector shield_string_values{}; - - const std::string shield_strength_string = UnitCSVFactory::GetVariable(unit_key, "shield_strength", std::string()); - const std::string shield_facets_string = UnitCSVFactory::GetVariable(unit_key, "shield_facets", std::string()); - - if(!shield_facets_string.empty()) { - try { - int shield_facets = std::stoi(shield_facets_string); - shield->number_of_facets = shield_facets; - } catch (std::invalid_argument const& ex) { - VS_LOG(error, (boost::format("%1%: %2% trying to convert shield_facets_string '%3%' to int") % __FUNCTION__ % ex.what() % shield_facets_string)); - shield->number_of_facets = 1; - } catch (std::out_of_range const& ex) { - VS_LOG(error, (boost::format("%1%: %2% trying to convert shield_facets_string '%3%' to int") % __FUNCTION__ % ex.what() % shield_facets_string)); - shield->number_of_facets = 1; - } - } - - if(!shield_strength_string.empty()) { - try { - int shield_strength = std::stoi(shield_strength_string); - shield->UpdateFacets(static_cast(shield_strength)); - } catch (std::invalid_argument const& ex) { - VS_LOG(error, (boost::format("%1%: %2% trying to convert shield_strength_string '%3%' to int") % __FUNCTION__ % ex.what() % shield_strength_string)); - shield->UpdateFacets(0.0F); - } catch (std::out_of_range const& ex) { - VS_LOG(error, (boost::format("%1%: %2% trying to convert shield_strength_string '%3%' to int") % __FUNCTION__ % ex.what() % shield_strength_string)); - shield->UpdateFacets(0.0F); - } - } else if(!shield_facets_string.empty()) { - // Try new longform - std::vector shield_values{}; - std::string shield_keys[] = {"shield_front", "shield_back", - "shield_left", "shield_right"}; - - for (int i = 0; i < shield->number_of_facets; ++i) { - const std::string shield_string_value = UnitCSVFactory::GetVariable(unit_key, shield_keys[i], std::string()); - if (shield_string_value.empty()) { - shield_sections[shield_keys[i]] = 0.0F; - shield_values.emplace_back(0.0F); - } else { - try { - float tmp = static_cast(std::stoi(shield_string_value)); - shield_sections[shield_keys[i]] = tmp; - shield_values.emplace_back(tmp); - } catch (const std::invalid_argument& ex) { - VS_LOG(error, (boost::format("%1%: Unable to convert shield value '%2%' to a number: %3%") % __FUNCTION__ % shield_string_value % ex.what())); - shield_sections[shield_keys[i]] = 0.0F; - shield_values.emplace_back(0.0F); - } catch (std::out_of_range const& ex) { - VS_LOG(error, (boost::format("%1%: Unable to convert shield value '%2%' to a number: %3%") % __FUNCTION__ % shield_string_value % ex.what())); - shield_sections[shield_keys[i]] = 0.0F; - shield_values.emplace_back(0.0F); - } - } - } - - if (shield->number_of_facets == 4 || shield->number_of_facets == 2) { - shield->UpdateFacets(shield->number_of_facets, shield_values.data()); - } - } else { - // Fallback to old shield_keys - int shield_count = 0; - std::vector tmp_keys{"Shield_Front_Top_Right", "Shield_Back_Top_Left", "Shield_Front_Bottom_Right", "Shield_Front_Bottom_Left"}; - std::vector shield_values{}; - - try { - for (auto &key : tmp_keys) { - std::string tmp_string_value = UnitCSVFactory::GetVariable(unit_key, key, std::string()); - float tmp_float_value = std::stof(tmp_string_value); - shield_sections[key] = tmp_float_value; - shield_values.emplace_back(tmp_float_value); - shield_string_values.emplace_back(tmp_string_value); - ++shield_count; - } - } catch (std::exception const& ex) { - shield_count = 0; - shield_string_values.clear(); - shield_values.clear(); - shield_sections.clear(); - tmp_keys.clear(); - tmp_keys.emplace_back("shield_front"); - tmp_keys.emplace_back("shield_back"); - tmp_keys.emplace_back("shield_left"); - tmp_keys.emplace_back("shield_right"); - try { - for (auto &key : tmp_keys) { - std::string tmp_string_value = UnitCSVFactory::GetVariable(unit_key, key, std::string()); - float tmp_float_value = std::stof(tmp_string_value); - shield_sections[key] = tmp_float_value; - shield_values.emplace_back(tmp_float_value); - shield_string_values.emplace_back(tmp_string_value); - ++shield_count; - } - } catch (std::exception const& ex) { - VS_LOG(error, (boost::format("%1%: %2% trying to parse shield facets") % __FUNCTION__ % ex.what())); - } - } - - if (shield_count == 4 || shield_count == 2) { - shield->number_of_facets = shield_count; - shield->UpdateFacets(shield_count, shield_values.data()); - } - } - // Energy // TODO: The following code has a bug. // It will set the max of the component as the current value loaded from the @@ -1138,6 +955,19 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ // From drawable this->num_chunks = UnitCSVFactory::GetVariable(unit_key, "Num_Chunks", 0); + + // Add integral components + // Make this a factor of unit price + if(!saved_game) { + const std::string integral_components = + "{hull;upgrades/integral;12000;1;0.1;0.1;1;1;@cargo/hull_patches.image@The ship's hull.;0;1}\ + {afterburner;upgrades/integral;2000;1;0.1;0.1;1;1;@upgrades/afterburner_generic.image@Engine overdrive. Increases thrust at the expense of decreased fuel efficiency.;0;1}\ + {drive;upgrades/integral;6000;1;0.1;0.1;1;1;@upgrades/afterburner_generic.image@The ship's engine.;0;1}\ + {ftl_drive;upgrades/integral;4500;1;0.1;0.1;1;1;@upgrades/jump_drive.image@The ship's faster than light engine.;0;1}"; + + AddCarg(this, integral_components); + } + } void Unit::WriteUnit(const char *modifications) { @@ -1199,17 +1029,26 @@ const std::map Unit::UnitToMap() { string val; // Textual Descriptions - unit["Key"] = unit_key; + unit["Key"] = unit_key; unit["Name"] = unit_name; unit["Textual_Description"] = unit_description; // Used in ship view + // Take some immutable stats directly from the original unit + const std::string immutable_stats[] = {"Directory", "STATUS", "Combat_Role", "Hud_image", + "Unit_Scale", "CockpitZ", "Mesh", "Prohibited_Upgrades", + "Light"}; + + for(const auto& stat : immutable_stats) { + unit[stat] = UnitCSVFactory::GetVariable(unit_key, stat, std::string()); + } + //mutable things unit["Equipment_Space"] = XMLSupport::tostring(equipment_volume); unit["Hold_Volume"] = XMLSupport::tostring(CargoVolume); unit["Hidden_Hold_Volume"] = XMLSupport::tostring(HiddenCargoVolume); unit["Upgrade_Storage_Volume"] = XMLSupport::tostring(UpgradeVolume); string mountstr; - double unitScale = stof(unit["Unit_Scale"], 1); + double unitScale = UnitCSVFactory::GetVariable(unit_key, "Unit_Scale", 1.0f); { //mounts for (unsigned int j = 0; j < mounts.size(); ++j) { @@ -1308,31 +1147,12 @@ const std::map Unit::UnitToMap() { unit["Cargo"] = carg; } unit["Mass"] = tos(Mass); - unit["Hull"] = tos(GetHullLayer().facets[0].health); - unit["Spec_Interdiction"] = tos(specInterdiction); - - // TODO: lib_damage figure out if this is correctly assigned - unit["armor_front"] = tos(GetArmorLayer().facets[0].health); - unit["armor_back"] = tos(GetArmorLayer().facets[1].health); - unit["armor_left"] = tos(GetArmorLayer().facets[2].health); - unit["armor_right"] = tos(GetArmorLayer().facets[3].health); - - unit["shield_facets"] = std::to_string(shield->number_of_facets); - switch (shield->number_of_facets) { - case 4: - unit["shield_left"] = tos(GetShieldLayer().facets[2].max_health); - unit["shield_right"] = tos(GetShieldLayer().facets[3].max_health); - // Fallthrough - case 2: - unit["shield_front"] = tos(GetShieldLayer().facets[0].max_health); - unit["shield_back"] = tos(GetShieldLayer().facets[1].max_health); - } + hull.SaveToCSV(unit); + armor.SaveToCSV(unit); + shield.SaveToCSV(unit); - //TODO: lib_damage shield leak and efficiency - unit["Shield_Leak"] = tos(0); //tos( shield.leak/100.0 ); - unit["Shield_Efficiency"] = tos(1); //tos( shield.efficiency ); - unit["Shield_Recharge"] = tos(shield->GetRegeneration()); //tos( shield.recharge ); + unit["Spec_Interdiction"] = tos(specInterdiction); unit["Warp_Min_Multiplier"] = tos(graphicOptions.MinWarpMultiplier); unit["Warp_Max_Multiplier"] = tos(graphicOptions.MaxWarpMultiplier); @@ -1355,7 +1175,7 @@ const std::map Unit::UnitToMap() { unit["ITTS"] = tos(computer.itts); unit["Can_Lock"] = tos(computer.radar.canlock); unit["Radar_Color"] = std::to_string(computer.radar.capability); - unit["Radar_Range"] = tos(computer.radar.maxrange); + unit["Radar_Range"] = tos(int(computer.radar.maxrange)); unit["Tracking_Cone"] = tos(acos(computer.radar.trackingcone) * 180. / M_PI); unit["Max_Cone"] = tos(acos(computer.radar.maxcone) * 180. / M_PI); unit["Lock_Cone"] = tos(acos(computer.radar.lockcone) * 180. / M_PI); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index a9383a4ec0..13f033186a 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -414,6 +414,10 @@ void Unit::Init() { pImage->cockpit_damage[damageiterator] = 1; } } + + AddLayer(&hull); + AddLayer(&armor); + AddLayer(&shield); } using namespace VSFileSystem; @@ -509,6 +513,16 @@ void Unit::Init(const char *filename, SetAniSpeed(0.05); StartAnimation(); } + + AddLayer(&hull); + AddLayer(&armor); + AddLayer(&shield); +} + + + +bool Unit::GettingDestroyed() const { + return hull.Destroyed(); } vector Unit::StealMeshes() { @@ -1287,7 +1301,7 @@ void Unit::DamageRandSys(float dam, const Vector &vec) { } // TODO: lib_damage reenable - shield->ReduceLayerCapability(dam, 0.1); + shield.Damage(); damages |= Damages::SHIELD_DAMAGED; return; @@ -1513,23 +1527,23 @@ const Unit *loadUnitByCache(std::string name, int faction) { return temprate; } -bool DestroySystem(float hull, float maxhull, float numhits) { +bool DestroySystem(float hull_percent, float numhits) { static float damage_chance = XMLSupport::parse_float(vs_config->getVariable("physics", "damage_chance", ".005")); static float guaranteed_chance = XMLSupport::parse_float(vs_config->getVariable("physics", "definite_damage_chance", ".1")); - float chance = 1 - (damage_chance * (guaranteed_chance + (maxhull - hull) / maxhull)); + float chance = 1 - (damage_chance * (guaranteed_chance + hull_percent)); if (numhits > 1) { chance = pow(chance, numhits); } return rand01() > chance; } -bool DestroyPlayerSystem(float hull, float maxhull, float numhits) { +bool DestroyPlayerSystem(float hull_percent, float numhits) { static float damage_chance = XMLSupport::parse_float(vs_config->getVariable("physics", "damage_player_chance", ".5")); static float guaranteed_chance = XMLSupport::parse_float(vs_config->getVariable("physics", "definite_damage_chance", ".1")); - float chance = 1 - (damage_chance * (guaranteed_chance + (maxhull - hull) / maxhull)); + float chance = 1 - (damage_chance * (guaranteed_chance + hull_percent)); if (numhits > 1) { chance = pow(chance, numhits); } @@ -1691,7 +1705,7 @@ bool Unit::Explode(bool drawit, float timeit) { this->pImage->pExplosion->SetOrientation(p, q, r); if (this->isUnit() != Vega_UnitType::missile) { _Universe->activeStarSystem()->AddMissileToQueue(new MissileEffect(this->Position(), - this->MaxShieldVal(), + this->shield.AverageMaxLayerValue(), 0, this->ExplosionRadius() * game_options()->explosion_damage_center, @@ -2004,7 +2018,7 @@ void rechargeShip(Unit *unit, unsigned int cockpit) { unit->fuel.Refill(); unit->energy.Refill(); unit->ftl_energy.Refill(); - unit->shield->FullyCharge(); + unit->shield.FullyCharge(); if (cockpit < 0 || cockpit >= _Universe->numPlayers()) { return; @@ -2937,36 +2951,6 @@ bool Unit::UpAndDownGrade(const Unit *up, 1); } } - - - if (!csv_cell_null_check || force_change_on_nothing - || cell_has_recursive_data(upgrade_name, up->faction, "Armor_Front_Top_Right")) { - for (int i = 0; i < armor->number_of_facets; ++i) { - STDUPGRADE(armor->facets[i].health, - up->armor->facets[i].health, - templ->armor->facets[i].health, 0); - armor->facets[i].max_health = armor->facets[i].health; - } - } - - // TODO: lib_damage all of this should be implemented better elsewhere - // Probably in DamageableFactory - // Upgrade shield regeneration - - // Because of the complex macros taking partial expressions and building code from them, - // this is the easiest way to refactor - float previous = shield->GetRegeneration(); - - if (!csv_cell_null_check || force_change_on_nothing - || cell_has_recursive_data(upgrade_name, up->faction, "Shield_Recharge")) - STDUPGRADE(shield_regeneration, - up->shield_regeneration, - templ->shield_regeneration, 0); - bool upgradedrecharge = (previous != shield_regeneration); - if (upgradedrecharge) { - - shield->UpdateRegeneration(shield_regeneration); - } /*if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Reactor_Recharge")) @@ -3037,46 +3021,9 @@ bool Unit::UpAndDownGrade(const Unit *up, } - if (!csv_cell_null_check || force_change_on_nothing - || cell_has_recursive_data(upgrade_name, up->faction, "Shield_Front_Top_Right")) { - if (shield->number_of_facets == up->shield->number_of_facets) { - for (unsigned int i = 0; i < shield->number_of_facets; i++) { - float previous_max = shield->facets[i].max_health; - STDUPGRADE(shield->facets[i].max_health, - up->shield->facets[i].max_health, - templ->shield->facets[i].max_health, 0); - - if (shield->facets[i].max_health != previous_max) { - shield->facets[i].max_health = shield->facets[i].max_health; - shield->facets[i].adjusted_health = shield->facets[i].max_health; - shield->facets[i].health = shield->facets[i].max_health; - } - } - } else if (up->FShieldData() > 0 || up->RShieldData() > 0 || up->LShieldData() > 0 || up->BShieldData() > 0) { - cancompletefully = false; - } - } - - // TODO: lib_damage. Disabled this until we restore efficiency and leak - /*if (upgradedshield || upgradedrecharge) { - if (up->shield.efficiency) { - shield.efficiency = up->shield.efficiency; - if (templ) - if (shield.efficiency > templ->shield.efficiency) - shield.efficiency = templ->shield.efficiency; - } - } - if ( !csv_cell_null_check || force_change_on_nothing - || cell_has_recursive_data( upgrade_name, up->faction, "Shield_Leak" ) ) { - double myleak = 100-shield.leak; - double upleak = 100-up->shield.leak; - double templeak = 100-(templ != NULL ? templ->shield.leak : 0); - bool ccf = cancompletefully; - STDUPGRADE_SPECIFY_DEFAULTS( myleak, upleak, templeak, 0, 100, 100, false, shield.leak ); - if (touchme && myleak <= 100 && myleak >= 0) shield.leak = (char) 100-myleak; - cancompletefully = ccf; - }*/ + + //DO NOT CHANGE see unit_customize.cpp static float lc = XMLSupport::parse_float(vs_config->getVariable("physics", "lock_cone", ".8")); //DO NOT CHANGE! see unit.cpp:258 @@ -3339,16 +3286,7 @@ int Unit::RepairUpgrade() { pct = 1; success += 1; } - if (SPECDriveFunctionality < 1) { - SPECDriveFunctionality = 1; - pct = 1; - success += 1; - } - if (SPECDriveFunctionalityMax < 1) { - SPECDriveFunctionalityMax = 1; - pct = 1; - success += 1; - } + if (CommFunctionality < 1) { CommFunctionality = 1; pct = 1; @@ -3370,13 +3308,6 @@ int Unit::RepairUpgrade() { success += 1; } - // Repair components - afterburner.Repair(); - afterburner_upgrade.Repair(); - drive.Repair(); - drive_upgrade.Repair(); - ftl_drive.Repair(); - damages = Damages::NO_DAMAGE; bool ret = success && pct > 0; static bool ComponentBasedUpgrades = @@ -3420,6 +3351,20 @@ extern bool isWeapon(std::string name); bool Unit::RepairUpgradeCargo(Cargo *item, Unit *baseUnit, float *credits) { assert((item != NULL) | !"Unit::RepairUpgradeCargo got a null item."); //added by chuck_starchaser double itemPrice = baseUnit ? baseUnit->PriceCargo(item->GetName()) : item->GetPrice(); + + // This needs to happen before we repair the part, obviously. + double percent_working = UnitUtil::PercentOperational(this, item->GetName(), "upgrades/", false); + + // New repair + if(RepairUnit(item->GetName())) { + double repair_price = RepairPrice(percent_working, itemPrice); + if (credits) { + // Pay for repair + (*credits) -= repair_price; + } + return true; + } + if (isWeapon(item->GetCategory())) { const Unit *upgrade = getUnitFromUpgradeName(item->GetName(), this->faction); if (upgrade->getNumMounts()) { @@ -4141,28 +4086,17 @@ void Unit::UpdatePhysics3(const Transformation &trans, adjustSound(SoundType::cloaking, cumulative_transformation.position, cumulative_velocity); } - // Recharge energy and shields - const bool apply_difficulty_shields = configuration()->physics_config.difficulty_based_shield_recharge; - const bool energy_before_shield = configuration()->physics_config.engine_energy_takes_priority; - - // Difficulty settings - float difficulty_shields = 1.0f; - if (apply_difficulty_shields) { - difficulty_shields = g_game.difficulty; - } - - if (energy_before_shield) { - rechargeEnergy(); - } - + + bool is_player_ship = _Universe->isPlayerStarship(this); - RegenerateShields(difficulty_shields, is_player_ship); - ExpendEnergy(is_player_ship); - if (!energy_before_shield) { - rechargeEnergy(); - } + reactor.Generate(); + drive.Consume(); + shield.Regenerate(is_player_ship); + MaintainECM(); + DecreaseWarpEnergyInWarp(); + if (lastframe) { if (!(docked & (DOCKED | DOCKED_INSIDE))) { //the AIscript should take care @@ -4649,18 +4583,3 @@ bool Unit::TransferUnitToSystem(unsigned int kk, StarSystem *&savedStarSystem, b } return ret; } - -// TODO: move this to energy or just simplify -// Why single out shields from other constant drains? -float Unit::energyData() const { - float capacitance = totalShieldEnergyCapacitance(); - - if (configuration()->physics_config.max_shield_lowers_capacitance) { - if (energy.MaxLevel() <= capacitance) { - return 0; - } - return (energy.Level()) / (energy.MaxLevel() - capacitance); - } else { - return energy.Percent(); - } -} diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index dc48ce9d59..6f8894489d 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -92,6 +92,9 @@ void UncheckUnit( class Unit*un ); #include "components/drive_upgrade.h" #include "components/ftl_drive.h" #include "components/jump_drive.h" +#include "components/armor.h" +#include "components/hull.h" +#include "components/shield.h" extern char *GetUnitDir(const char *filename); @@ -175,12 +178,17 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p Afterburner afterburner; AfterburnerUpgrade afterburner_upgrade = AfterburnerUpgrade(&afterburner); - Cloak cloak; + Cloak cloak = Cloak(); Drive drive; DriveUpgrade drive_upgrade = DriveUpgrade(&drive); FtlDrive ftl_drive = FtlDrive(&ftl_energy); JumpDrive jump_drive = JumpDrive(&ftl_energy); + Armor armor; + Hull hull; + Shield shield = Shield(&energy, &ftl_drive, &cloak); + + /// Radar and related systems // TODO: take a deeper look at this much later... //how likely to fool missiles @@ -272,6 +280,11 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p //table can be NULL, but setting it appropriately may increase performance void LoadRow(std::string unit_identifier, std::string unitMod, bool saved_game); + // Units take some time to blow up + // When they're done, their destructor is called ;) + bool GettingDestroyed() const; + + // TODO: implement enum class as type safe bitmask... // http://blog.bitwigglers.org/using-enum-classes-as-type-safe-bitmasks/ enum Damages { @@ -711,9 +724,6 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p void SetGlowVisible(bool isvis); - - - //Applies damage to the local area given by pnt float ApplyLocalDamage(const Vector &pnt, const Vector &normal, @@ -1028,11 +1038,6 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p // MACRO_FUNCTION(field_a, object_a, object_b) // object_a->field_a = object_b->field_b; float temporary_upgrade_float_variable; - - - // Python Interfaces - float fuelData() const { return fuel.Level(); } - float energyData() const; }; Unit *findUnitInStarsystem(const void *unitDoNotDereference); diff --git a/engine/src/cmd/unit_util_generic.cpp b/engine/src/cmd/unit_util_generic.cpp index d45291747a..9e313dbbd9 100644 --- a/engine/src/cmd/unit_util_generic.cpp +++ b/engine/src/cmd/unit_util_generic.cpp @@ -453,9 +453,8 @@ void SetHull(Unit *my_unit, float newhull) { if (!my_unit) { return; } - // TODO: replace with a proper API in both lib_damage and damageable - // damageable SetHull linked to SetHealth which checks validity - my_unit->hull->facets[0].health = newhull; + + my_unit->hull.Set(newhull); } float getCredits(const Unit *my_unit) { @@ -909,16 +908,40 @@ float PercentOperational(Unit *un, std::string name, std::string category, bool } const std::string upgrade_category = UnitCSVFactory::GetVariable(unit_key, "Upgrade_Type", std::string()); + if(upgrade_category == "Hull") { + return un->hull.PercentOperational(); + } + + if(upgrade_category == "Armor") { + return un->armor.PercentOperational(); + } + + if(upgrade_category == "Shield") { + return un->shield.PercentOperational(); + } + + if(upgrade_category == "Afterburner") { + return un->afterburner.PercentOperational(); + } + + if(upgrade_category == "Drive") { + return un->drive.PercentOperational(); + } + + if(upgrade_category == "FTL_Drive") { + return un->ftl_drive.PercentOperational(); + } + if(upgrade_category == "Reactor") { return un->reactor.PercentOperational(); } if(upgrade_category == "Capacitor") { - return un->reactor.PercentOperational(); + return un->energy.PercentOperational(); } if(upgrade_category == "FTL_Capacitor") { - return un->reactor.PercentOperational(); + return un->ftl_energy.PercentOperational(); } if(upgrade_category == "Jump_Drive") { @@ -959,15 +982,6 @@ float PercentOperational(Unit *un, std::string name, std::string category, bool } } } else if (name.find("add_") != 0 && name.find("mult_") != 0) { - float armor[8]; - upgrade->ArmorData(armor); - if (upgrade->layers[0].facets[0].health > 1 || armor[0] || armor[1] || armor[2] || armor[3] || armor[4] || armor[5] - || armor[6] - || armor[7]) { - if (countHullAndArmorAsFull) { - return 1.0f; - } - } double percent = 0; if (un->canUpgrade(upgrade, -1, -1, 0, true, percent, makeTemplateUpgrade(un->name, un->faction), false)) { if (percent > 0 && percent < 1) { diff --git a/engine/src/cmd/upgradeable_unit.cpp b/engine/src/cmd/upgradeable_unit.cpp index a619e1ef03..f3567b9453 100644 --- a/engine/src/cmd/upgradeable_unit.cpp +++ b/engine/src/cmd/upgradeable_unit.cpp @@ -128,14 +128,14 @@ UpgradeOperationResult UpgradeableUnit::UpgradeUnit(const std::string upgrade_na UpgradeOperationResult result; switch(component_type) { - /*case ComponentType::Armor: + case ComponentType::Armor: result.upgradeable = true; - result.success = unit->armor->CanWillUpDowngrade(upgrade_key, upgrade, apply); + result.success = unit->armor.CanWillUpDowngrade(upgrade_key, upgrade, apply); break; case ComponentType::Shield: result.upgradeable = true; - result.success = unit->shield->CanWillUpDowngrade(upgrade_key, upgrade, apply); - break;*/ + result.success = unit->shield.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; case ComponentType::Capacitor: result.upgradeable = true; @@ -198,6 +198,113 @@ UpgradeOperationResult UpgradeableUnit::UpgradeUnit(const std::string upgrade_na return result; } +bool UpgradeableUnit::RepairUnit(const std::string upgrade_name) { + Unit* unit = vega_dynamic_cast_ptr(this); + const std::string upgrade_key = upgrade_name + UPGRADES_SUFFIX; + const ComponentType component_type = GetComponentTypeFromName(upgrade_name); + + switch(component_type) { + case ComponentType::Hull: + if(unit->hull.Damaged()) { + unit->hull.Repair(); + return true; + } + break; + case ComponentType::Armor: + if(unit->armor.Damaged()) { + unit->armor.Repair(); + return true; + } + break; + case ComponentType::Shield: + if(unit->shield.Damaged()) { + unit->shield.Repair(); + return true; + } + break; + case ComponentType::Capacitor: + if(unit->energy.Damaged()) { + unit->energy.Repair(); + return true; + } + break; + case ComponentType::FtlCapacitor: + if(unit->ftl_energy.Damaged()) { + unit->ftl_energy.Repair(); + return true; + } + break; + case ComponentType::Reactor: + if(unit->reactor.Damaged()) { + unit->reactor.Repair(); + return true; + } + break; + case ComponentType::Afterburner: + if(unit->afterburner.Damaged()) { + unit->afterburner.Repair(); + return true; + } + break; + case ComponentType::AfterburnerUpgrade: + if(unit->afterburner_upgrade.Damaged()) { + unit->afterburner_upgrade.Repair(); + return true; + } + break; + case ComponentType::Drive: + if(unit->drive.Damaged()) { + unit->drive.Repair(); + return true; + } + break; + case ComponentType::DriveUpgrade: + if(unit->drive_upgrade.Damaged()) { + unit->drive_upgrade.Repair(); + return true; + } + break; + case ComponentType::FtlDrive: + if(unit->ftl_drive.Damaged()) { + unit->ftl_drive.Repair(); + return true; + } + break; + case ComponentType::JumpDrive: + if(unit->jump_drive.Damaged()) { + unit->jump_drive.Repair(); + return true; + } + break; + case ComponentType::Cloak: + if(unit->cloak.Damaged()) { + unit->cloak.Repair(); + return true; + } + break; + + + /*case UpgradeType::ECM: + result.upgradeable = true; + result.success = unit->ecm.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; + case UpgradeType::Radar: + result.upgradeable = true; + result.success = unit->radar.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break;*/ + + /*case UpgradeType::Repair_Droid: + result.upgradeable = true; + result.success = unit->repair_droid.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break;*/ + + default: + //std::cout << "Unhandled type for " << upgrade_name << std::endl; + break; + } + + return false; +} // TODO: remove unit parameter void UpgradeableUnit::UpgradeUnit(const std::string &upgrades) { diff --git a/engine/src/cmd/upgradeable_unit.h b/engine/src/cmd/upgradeable_unit.h index ad862e32f3..46802dbd9e 100644 --- a/engine/src/cmd/upgradeable_unit.h +++ b/engine/src/cmd/upgradeable_unit.h @@ -52,6 +52,7 @@ class UpgradeableUnit virtual ~UpgradeableUnit() {} UpgradeOperationResult UpgradeUnit(const std::string upgrade_name, bool upgrade, bool apply); + bool RepairUnit(const std::string upgrade_name); void UpgradeUnit(const std::string &upgrades); bool UpgradeMounts(const Unit *up, int subunitoffset, diff --git a/engine/src/components/afterburner.cpp b/engine/src/components/afterburner.cpp index 6aa3086316..e95db04dbe 100644 --- a/engine/src/components/afterburner.cpp +++ b/engine/src/components/afterburner.cpp @@ -48,7 +48,7 @@ void Afterburner::Load(std::string unit_key) { SetConsumption(consumption); // We calculate percent operational as a simple average - operational = (thrust.Percent() + speed.Percent()) / 2 * 100; + operational = (thrust.Percent() + speed.Percent()) / 2; } void Afterburner::SaveToCSV(std::map& unit) const { @@ -82,14 +82,14 @@ void Afterburner::Damage() { speed.RandomDamage(); // We calculate percent operational as a simple average - operational = (thrust.Percent() + speed.Percent()) / 2 * 100; + operational = (thrust.Percent() + speed.Percent()) / 2; } void Afterburner::DamageByPercent(double percent) { thrust.DamageByPercent(percent); speed.DamageByPercent(percent); - operational = (thrust.Percent() + speed.Percent()) / 2 * 100; + operational = (thrust.Percent() + speed.Percent()) / 2; } void Afterburner::Repair() { diff --git a/engine/src/components/armor.cpp b/engine/src/components/armor.cpp new file mode 100644 index 0000000000..b18fbbab75 --- /dev/null +++ b/engine/src/components/armor.cpp @@ -0,0 +1,163 @@ +/* + * armor.cpp + * + * Copyright (C) 2001-2023 Daniel Horn, Benjamen Meyer, Roy Falk, Stephen G. Tuggy, + * and other Vega Strike contributors. + * + * https://github.com/vegastrike/Vega-Strike-Engine-Source + * + * This file is part of Vega Strike. + * + * Vega Strike 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. + * + * Vega Strike 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 Vega Strike. If not, see . + */ + +#include "armor.h" +#include "damage.h" +#include "unit_csv_factory.h" + +int Armor::front = 0; +int Armor::back = 1; +int Armor::left = 2; +int Armor::right = 3; + +static const Damage normal_and_phase_damage = Damage(1.0,1.0); + +Armor::Armor() : + Component(), + DamageableLayer(1, FacetConfiguration::four, 1.0, normal_and_phase_damage, false) { + type = ComponentType::Armor; +} + + +// Component Methods +void Armor::Load(std::string unit_key) { + Component::Load(unit_key); + + facets.clear(); + + // Armor + // We support 3 options: + // 1. Minimized armor = x (single value). + // 2. New detailed armor (Front, back, left, right). + // 3. Old detailed (Front-left-top, ...). 8 facets converted to 4. + const std::string armor_single_value_string = UnitCSVFactory::GetVariable(unit_key, "armor", std::string()); + + if(armor_single_value_string != "") { + // Minimized + Resource armor_single_value = Resource(armor_single_value_string); + facets = std::vector>(number_of_facets, + armor_single_value); + } else { + // Try new + std::string armor_keys[] = {"armor_front", "armor_back", + "armor_left", "armor_right"}; + bool new_form = true; + for (int i = 0; i < 4; i++) { + const std::string armor_string_value = UnitCSVFactory::GetVariable(unit_key, armor_keys[i], std::string()); + if(armor_string_value.empty()) { + new_form = false; + break; + } + + Resource armor_single_value = Resource(armor_string_value); + facets.push_back(armor_single_value); + } + + // Fallback to old + if(!new_form) { + const std::string armor_keys[] = {"Armor_Front_Top_Left", + "Armor_Front_Top_Right", + "Armor_Front_Bottom_Left", + "Armor_Front_Bottom_Right", + "Armor_Back_Top_Left", + "Armor_Back_Top_Right", + "Armor_Back_Bottom_Left", + "Armor_Back_Bottom_Right"}; + + double old_armor_values[8]; + for (int i = 0; i < 8; i++) { + old_armor_values[i] = UnitCSVFactory::GetVariable(unit_key, armor_keys[i], 0.0); + } + + // Conversion is tricky because new values are a square and old values are + // a two layered diamond. + int index_combinations[4][4] = {{0,1,2,3}, {4,5,6,7}, {0,2,4,6}, {1,3,5,7}}; + for(const auto& facet_indices : index_combinations) { + double facet_strength = 0.0; + for(const auto& facet_index : facet_indices) { + facet_strength += old_armor_values[facet_index]; + } + facet_strength /= 2; + facets.push_back(Resource(facet_strength, 0, facet_strength)); + } + } + } +} + +void Armor::SaveToCSV(std::map& unit) const { + unit["armor_front"] = facets[0].Serialize(); + unit["armor_back"] = facets[1].Serialize(); + unit["armor_left"] = facets[2].Serialize(); + unit["armor_right"] = facets[3].Serialize(); +} + +bool Armor::CanDowngrade() const { + return installed && !integral; +} + +bool Armor::Downgrade() { + if(!CanDowngrade()) { + return false; + } + + // Component + Component::Downgrade(); + + facets.clear(); + return true; +} + +bool Armor::CanUpgrade(const std::string upgrade_key) const { + return !integral; +} + +bool Armor::Upgrade(const std::string upgrade_key) { + Component::Upgrade(upgrade_key); + + // We only support single value armor + facets.clear(); + const double armor_single_value = UnitCSVFactory::GetVariable(upgrade_key, "armor", 0.0); + + if(armor_single_value > 0.0) { + for (unsigned int i = 0; i < number_of_facets; i++) { + facets.push_back(Resource(armor_single_value,0,armor_single_value)); + } + } + + return true; +} + +double Armor::PercentOperational() const { + return Percent(); +} + +bool Armor::Damaged() const { + return Percent() < 1; +} + +void Armor::Repair() { + for(auto& facet : facets) { + facet.RepairFully(); + } +} \ No newline at end of file diff --git a/engine/src/components/armor.h b/engine/src/components/armor.h new file mode 100644 index 0000000000..47262fd11b --- /dev/null +++ b/engine/src/components/armor.h @@ -0,0 +1,64 @@ +/* + * armor.h + * + * Copyright (C) 2001-2023 Daniel Horn, Benjamen Meyer, Roy Falk, Stephen G. Tuggy, + * and other Vega Strike contributors. + * + * https://github.com/vegastrike/Vega-Strike-Engine-Source + * + * This file is part of Vega Strike. + * + * Vega Strike 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. + * + * Vega Strike 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 Vega Strike. If not, see . + */ + +#ifndef VEGA_STRIKE_ENGINE_COMPONENTS_ARMOR_H +#define VEGA_STRIKE_ENGINE_COMPONENTS_ARMOR_H + +#include "component.h" +#include "damage/damageable_layer.h" + +/** Armor component + * + * Always present and always implemented as a 4 facet DL. + * If no armor, each facet is 0. +*/ +class Armor : public Component, public DamageableLayer { +public: + static int front; + static int back; + static int left; + static int right; + + Armor(); + + // Component Methods + void Load(std::string unit_key) override; + + void SaveToCSV(std::map& unit) const override; + + bool CanDowngrade() const override; + + bool Downgrade() override; + + bool CanUpgrade(const std::string upgrade_key) const override; + + bool Upgrade(const std::string upgrade_key) override; + + double PercentOperational() const override; + + bool Damaged() const override; + void Repair() override; +}; + +#endif // VEGA_STRIKE_ENGINE_COMPONENTS_ARMOR_H diff --git a/engine/src/components/component.cpp b/engine/src/components/component.cpp index ed8a35fe5a..d7632dfdca 100644 --- a/engine/src/components/component.cpp +++ b/engine/src/components/component.cpp @@ -41,7 +41,7 @@ Component::Component(double mass, double volume, bool installed, bool integral): description(""), price(0.0), mass(mass), volume(volume), - operational(Resource(100.0,0.0,100.0)), + operational(Resource(1.0,0.0,1.0)), installed(installed), integral(integral) {} @@ -126,7 +126,7 @@ void Component::Destroy() { } bool Component::Damaged() const { - return operational.Value() < operational.MaxValue(); + return operational.Value() < 1.0; } bool Component::Destroyed() const { @@ -142,7 +142,7 @@ bool Component::Operational() const { } double Component::PercentOperational() const { - return operational.Percent(); + return operational.Value(); } void Component::SetIntegral(bool integral) { diff --git a/engine/src/components/drive.cpp b/engine/src/components/drive.cpp index 615d124db7..653a9fbd51 100644 --- a/engine/src/components/drive.cpp +++ b/engine/src/components/drive.cpp @@ -108,7 +108,7 @@ void Drive::Load(std::string unit_key) { lateral.Percent() + vertical.Percent() + forward.Percent() + retro.Percent() + speed.Percent() + max_yaw_left.Percent() + max_yaw_right.Percent() + max_pitch_down.Percent() + - max_pitch_up.Percent() + max_roll_left.Percent() + max_roll_right.Percent()) / 14 * 100; + max_pitch_up.Percent() + max_roll_left.Percent() + max_roll_right.Percent()) / 14; } @@ -182,7 +182,7 @@ void Drive::Damage() { lateral.Percent() + vertical.Percent() + forward.Percent() + retro.Percent() + speed.Percent() + max_yaw_left.Percent() + max_yaw_right.Percent() + max_pitch_down.Percent() + - max_pitch_up.Percent() + max_roll_left.Percent() + max_roll_right.Percent()) / 14 * 100; + max_pitch_up.Percent() + max_roll_left.Percent() + max_roll_right.Percent()) / 14; } void Drive::DamageByPercent(double percent) { @@ -208,7 +208,7 @@ void Drive::DamageByPercent(double percent) { lateral.Percent() + vertical.Percent() + forward.Percent() + retro.Percent() + speed.Percent() + max_yaw_left.Percent() + max_yaw_right.Percent() + max_pitch_down.Percent() + - max_pitch_up.Percent() + max_roll_left.Percent() + max_roll_right.Percent()) / 14 * 100; + max_pitch_up.Percent() + max_roll_left.Percent() + max_roll_right.Percent()) / 14; } void Drive::Repair() { diff --git a/engine/src/components/energy_container.cpp b/engine/src/components/energy_container.cpp index 3a995e2e82..5c3b146f43 100644 --- a/engine/src/components/energy_container.cpp +++ b/engine/src/components/energy_container.cpp @@ -101,7 +101,7 @@ void EnergyContainer::Zero() { level = 0; } void EnergyContainer::Refill() { - level.SetToMax(); + level.Set(level.AdjustedValue()); } @@ -129,20 +129,22 @@ void EnergyContainer::Load(std::string unit_key) { std::cerr << "Illegal container type in EnergyContainer::Load" << std::flush; abort(); } + + operational = level.AdjustedValue() / level.MaxValue(); } void EnergyContainer::SaveToCSV(std::map& unit) const { switch(type) { case ComponentType::Fuel: - unit[FUEL_CAPACITY] = std::to_string(MaxLevel() / configuration()->fuel.fuel_factor); + unit[FUEL_CAPACITY] = level.Serialize(configuration()->fuel.fuel_factor); break; case ComponentType::Capacitor: - unit[FUEL_CAPACITY] = std::to_string(MaxLevel() / configuration()->fuel.energy_factor); + unit[CAPACITOR] = level.Serialize(configuration()->fuel.energy_factor); break; case ComponentType::FtlCapacitor: - unit[FUEL_CAPACITY] = std::to_string(MaxLevel() / configuration()->fuel.ftl_energy_factor); + unit[FTL_CAPACITOR] = level.Serialize(configuration()->fuel.ftl_energy_factor); break; default: // This really can't happen @@ -205,15 +207,18 @@ bool EnergyContainer::Upgrade(const std::string upgrade_key) { void EnergyContainer::Damage() { - level.RandomDamage(); + level.RandomDamage(); + operational = level.AdjustedValue() / level.MaxValue(); } void EnergyContainer::DamageByPercent(double percent) { - level.DamageByPercent(percent); + level.DamageByPercent(percent); + operational = level.AdjustedValue() / level.MaxValue(); } void EnergyContainer::Repair() { level.RepairFully(); + operational = 1.0; } bool EnergyContainer::Damaged() const { diff --git a/engine/src/components/hull.cpp b/engine/src/components/hull.cpp new file mode 100644 index 0000000000..850b4b281f --- /dev/null +++ b/engine/src/components/hull.cpp @@ -0,0 +1,100 @@ +/* + * hull.cpp + * + * Copyright (C) 2001-2023 Daniel Horn, Benjamen Meyer, Roy Falk, Stephen G. Tuggy, + * and other Vega Strike contributors. + * + * https://github.com/vegastrike/Vega-Strike-Engine-Source + * + * This file is part of Vega Strike. + * + * Vega Strike 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. + * + * Vega Strike 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 Vega Strike. If not, see . + */ + +#include "hull.h" + +#include "unit_csv_factory.h" + +static const Damage normal_and_phase_damage = Damage(1.0,1.0); + + +Hull::Hull() : + Component(), + DamageableLayer(0, FacetConfiguration::one, 0.0, normal_and_phase_damage, true) { + type = ComponentType::Hull; +} + + +// Component Methods +void Hull::Load(std::string unit_key) { + Component::Load(unit_key); + + Resource temp_hull = Resource(UnitCSVFactory::GetVariable(unit_key, "Hull", std::string())); + facets.clear(); + facets.push_back(temp_hull); +} + +void Hull::SaveToCSV(std::map& unit) const { + unit["Hull"] = facets[0].Serialize(); +} + +bool Hull::CanDowngrade() const { + return false; +} + +bool Hull::Downgrade() { + return false; +} + +bool Hull::CanUpgrade(const std::string upgrade_name) const { + return false; +} + +bool Hull::Upgrade(const std::string upgrade_name) { + return false; +} + +double Hull::Percent() const { + return facets[0].Percent(); +} + +void Hull::Destroy() { + facets[0] = 0; +} + +double Hull::Get() { + return facets[0].Value(); +} + +double Hull::GetMax() { + return facets[0].MaxValue(); +} + +void Hull::Set(double value) { + facets[0] = Resource(value, 0, value); +} + +double Hull::PercentOperational() const { + return Percent(); +} + +bool Hull::Damaged() const { + return Percent() < 1; +} + +void Hull::Repair() { + for(auto& facet : facets) { + facet.RepairFully(); + } +} \ No newline at end of file diff --git a/engine/src/components/hull.h b/engine/src/components/hull.h new file mode 100644 index 0000000000..3a70f50e44 --- /dev/null +++ b/engine/src/components/hull.h @@ -0,0 +1,62 @@ +/* + * hull.h + * + * Copyright (C) 2001-2023 Daniel Horn, Benjamen Meyer, Roy Falk, Stephen G. Tuggy, + * and other Vega Strike contributors. + * + * https://github.com/vegastrike/Vega-Strike-Engine-Source + * + * This file is part of Vega Strike. + * + * Vega Strike 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. + * + * Vega Strike 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 Vega Strike. If not, see . + */ + +#ifndef VEGA_STRIKE_ENGINE_COMPONENTS_HULL_H +#define VEGA_STRIKE_ENGINE_COMPONENTS_HULL_H + +#include "component.h" +#include "damage/damageable_layer.h" + +/** Hull component */ +class Hull : public Component, public DamageableLayer { +public: + Hull(); + + // Component Methods + void Load(std::string unit_key) override; + + void SaveToCSV(std::map& unit) const; + + bool CanDowngrade() const override; + + bool Downgrade() override; + + bool CanUpgrade(const std::string upgrade_name) const override; + + bool Upgrade(const std::string upgrade_name) override; + + double PercentOperational() const override; + + double Percent() const; + bool Damaged() const override; + void Repair() override; + + void Destroy() override; + + double Get(); + double GetMax(); + void Set(double value); +}; + +#endif // VEGA_STRIKE_ENGINE_COMPONENTS_HULL_H diff --git a/engine/src/components/reactor.cpp b/engine/src/components/reactor.cpp index 6a2644d691..d062bd17a1 100644 --- a/engine/src/components/reactor.cpp +++ b/engine/src/components/reactor.cpp @@ -58,11 +58,12 @@ void Reactor::Load(std::string unit_key) { atom_capacity = capacity * simulation_atom_var; SetConsumption(capacity * conversion_ratio); + operational = capacity.Percent(); } void Reactor::SaveToCSV(std::map& unit) const { // TODO: This won't record damage to recharge - unit[REACTOR_RECHARGE] = std::to_string(capacity.MaxValue() / configuration()->fuel.reactor_factor); + unit[REACTOR_RECHARGE] = capacity.Serialize(configuration()->fuel.reactor_factor); } @@ -105,16 +106,19 @@ bool Reactor::Upgrade(const std::string upgrade_key) { void Reactor::Damage() { capacity.RandomDamage(); atom_capacity = capacity.Value() * simulation_atom_var; + operational = capacity.AdjustedValue() / capacity.MaxValue(); } void Reactor::DamageByPercent(double percent) { capacity.DamageByPercent(percent); - atom_capacity = capacity.Value() * simulation_atom_var; + atom_capacity = capacity.Value() * simulation_atom_var; + operational = capacity.AdjustedValue() / capacity.MaxValue(); } void Reactor::Repair() { capacity.RepairFully(); atom_capacity = capacity.Value() * simulation_atom_var; + operational = 1.0; } bool Reactor::Damaged() const { @@ -150,4 +154,5 @@ void Reactor::SetCapacity(double capacity) { this->capacity.SetMaxValue(capacity * configuration()->fuel.reactor_factor); atom_capacity = capacity * simulation_atom_var; SetConsumption(capacity * conversion_ratio); + operational = 1.0; } \ No newline at end of file diff --git a/engine/src/components/shield.cpp b/engine/src/components/shield.cpp new file mode 100644 index 0000000000..39adde946b --- /dev/null +++ b/engine/src/components/shield.cpp @@ -0,0 +1,402 @@ +/* + * shield.cpp + * + * Copyright (C) 2001-2023 Daniel Horn, Benjamen Meyer, Roy Falk, Stephen G. Tuggy, + * and other Vega Strike contributors. + * + * https://github.com/vegastrike/Vega-Strike-Engine-Source + * + * This file is part of Vega Strike. + * + * Vega Strike 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. + * + * Vega Strike 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 Vega Strike. If not, see . + */ + +#include "shield.h" +#include "ftl_drive.h" +#include "cloak.h" +#include "unit_csv_factory.h" +#include "configuration/configuration.h" +#include "vs_logging.h" +#include "damage/damage.h" + +#include + +int Shield::front = 0; +int Shield::back = 1; +int Shield::left = 2; +int Shield::right = 3; + +static const Damage normal_damage = Damage(1.0,0.0); + +Shield::Shield(EnergyContainer *source, FtlDrive *ftl_drive, Cloak *cloak, FacetConfiguration configuration) : + Component(), + EnergyConsumer(source, true, 0), + DamageableLayer(2, configuration, 0.0, normal_damage, false), + ftl_drive(ftl_drive), cloak(cloak), + regeneration(Resource(0.0,0.0,0.0)), + max_power(Resource(1.0,0.0,1.0)) { + type = ComponentType::Shield; +} + + +// Component Methods +void Shield::Load(std::string unit_key) { + Component::Load(unit_key); + + // Load shield + // Some basic shield variables + // TODO: lib_damage figure out how leak and efficiency work + //char leak = static_cast(UnitCSVFactory::GetVariable(unit_key, "Shield_Leak", 0.0f) * 100); + //double efficiency = UnitCSVFactory::GetVariable(unit_key, "Shield_Efficiency", 1.0f ); + + const std::string regen_string = UnitCSVFactory::GetVariable(unit_key, "Shield_Recharge", std::string()); + regeneration = Resource(regen_string); + + // We support 3 options: + // 1. Minimized shield_strength = x (single value). + // 2. New detailed shield (Front, back, left, right). + // 3. Old detailed (Front-left-top, ...). 4/2 facets converted to 4/2. + + // Get shield count + std::map shield_sections{}; + std::vector shield_string_values{}; + + const std::string shield_strength_string = UnitCSVFactory::GetVariable(unit_key, "shield", std::string()); + const std::string shield_facets_string = UnitCSVFactory::GetVariable(unit_key, "shield_facets", std::string()); + + if(!shield_facets_string.empty()) { + try { + number_of_facets = std::stoi(shield_facets_string); + } catch (std::invalid_argument const& ex) { + VS_LOG(error, (boost::format("%1%: %2% trying to convert shield_facets_string '%3%' to int") % __FUNCTION__ % ex.what() % shield_facets_string)); + number_of_facets = 1; + } catch (std::out_of_range const& ex) { + VS_LOG(error, (boost::format("%1%: %2% trying to convert shield_facets_string '%3%' to int") % __FUNCTION__ % ex.what() % shield_facets_string)); + number_of_facets = 1; + } + } + + // Single value (short form) + if(!shield_strength_string.empty()) { + try { + Resource facet_strength = Resource(shield_strength_string); + facets = std::vector>(number_of_facets, + facet_strength); + + CalculatePercentOperational(); + return; + } catch (std::invalid_argument const& ex) { + VS_LOG(error, (boost::format("%1%: %2% trying to convert shield_strength_string '%3%' to int") % __FUNCTION__ % ex.what() % shield_strength_string)); + } catch (std::out_of_range const& ex) { + VS_LOG(error, (boost::format("%1%: %2% trying to convert shield_strength_string '%3%' to int") % __FUNCTION__ % ex.what() % shield_strength_string)); + } + } + + // Try new longform + if(!shield_facets_string.empty() && + (number_of_facets == 4 || number_of_facets == 2)) { + std::vector> shield_values{}; + std::string shield_keys[] = {"shield_front", "shield_back", + "shield_left", "shield_right"}; + + // Using old form for loop to support 2/4 shields. + for (int i = 0; i < number_of_facets; ++i) { + // TODO: this can be taken out to a separate utility function, shared to hull, armor and shield + // also below in old form. + // Maybe. If we read numbers instead of strings... no need. + const std::string shield_string_value = UnitCSVFactory::GetVariable(unit_key, shield_keys[i], std::string()); + if (shield_string_value.empty()) { + shield_values.push_back(Resource(0.0)); + continue; + } + + try { + Resource facet_strength = Resource(shield_string_value); + shield_values.push_back(facet_strength); + continue; + } catch (const std::invalid_argument& ex) { + VS_LOG(error, (boost::format("%1%: Unable to convert shield value '%2%' to a number: %3%") % __FUNCTION__ % shield_string_value % ex.what())); + } catch (std::out_of_range const& ex) { + VS_LOG(error, (boost::format("%1%: Unable to convert shield value '%2%' to a number: %3%") % __FUNCTION__ % shield_string_value % ex.what())); + } + shield_values.push_back(Resource(0.0)); + } + + facets = shield_values; + CalculatePercentOperational(); + return; + } + + // Fallback to old shield_keys + int shield_count = 0; + std::string shield_keys[] = {"Shield_Front_Top_Right", "Shield_Back_Top_Left", "Shield_Front_Bottom_Right", "Shield_Front_Bottom_Left"}; + std::vector> shield_values{}; + + try { + for (auto &key : shield_keys) { + std::string string_value = UnitCSVFactory::GetVariable(unit_key, key, std::string()); + + // If no shields present, we should break and go directly to end + // of function + if(string_value.empty()) { + break; + } + + double value = std::stod(string_value); + shield_values.push_back(Resource(value, 0.0, value)); + ++shield_count; + } + + if (shield_count == 4 || shield_count == 2) { + number_of_facets = shield_count; + facets = shield_values; + CalculatePercentOperational(); + return; + } + } catch (std::exception const& ex) { + VS_LOG(error, (boost::format("%1%: %2% Unable to parse shield facets for %3%") % __FUNCTION__ % ex.what() % unit_key)); + } + + // This should already be set, but good practice to do it anyway. + number_of_facets = 0; + facets.clear(); + operational = 0.0; +} + + + +void Shield::SaveToCSV(std::map& unit) const { + // We always save in long form. + unit["shield_facets"] = std::to_string(number_of_facets); + unit["shield_front"] = facets[0].Serialize(); + unit["shield_back"] = facets[1].Serialize(); + unit["shield_left"] = facets[2].Serialize(); + unit["shield_right"] = facets[3].Serialize(); + + //TODO: lib_damage shield leak and efficiency + unit["Shield_Leak"] = std::to_string(0); + unit["Shield_Efficiency"] = std::to_string(1); + unit["Shield_Recharge"] = regeneration.Serialize(); +} + +bool Shield::CanDowngrade() const { + return installed && !integral; +} + +bool Shield::Downgrade() { + if(!CanDowngrade()) { + return false; + } + + // Component + Component::Downgrade(); + + facets.clear(); + return true; +} + +bool Shield::CanUpgrade(const std::string upgrade_key) const { + const int facets = UnitCSVFactory::GetVariable(upgrade_key, "shield_facets", 0.0); + return facets == number_of_facets && !integral; +} + +bool Shield::Upgrade(const std::string upgrade_key) { + Component::Upgrade(upgrade_key); + + // We only support single value shield + const double regen = UnitCSVFactory::GetVariable(upgrade_key, "Shield_Recharge", 0.0); + if(regen > 0) { + regeneration = Resource(regen, 0.0, regen); + } else { + return false; + } + + facets.clear(); + const double shield_single_value = UnitCSVFactory::GetVariable(upgrade_key, "shield", 0.0); + + if(shield_single_value > 0.0) { + for (unsigned int i = 0; i < number_of_facets; i++) { + facets.push_back(Resource(shield_single_value,0,shield_single_value)); + } + } + + return true; +} + +double Shield::PercentOperational() const { + return operational.Value(); +} + +void Shield::CalculatePercentOperational() { + double percent = regeneration.Percent(); + + for (Resource &facet : facets) { + percent += facet.Percent(); + } + + // A simple average of regeneration and facets + // 4 facet shields assign less importance to regeneration + operational = percent / (number_of_facets + 1); +} + +void Shield::Damage() { + regeneration.RandomDamage(); + double percent = regeneration.Percent(); + + for (Resource &facet : facets) { + facet.RandomDamage(); + percent += facet.Percent(); + } + + // A simple average of regeneration and facets + // 4 facet shields assign less importance to regeneration + operational = percent / (number_of_facets + 1); +} + +void Shield::DamageByPercent(double percent) { + regeneration.DamageByPercent(percent); + double sum_of_percents = regeneration.Percent(); + + for (Resource &facet : facets) { + facet.DamageByPercent(percent); + sum_of_percents += facet.Percent(); + } + + // A simple average of regeneration and facets + // 4 facet shields assign less importance to regeneration + operational = sum_of_percents / (number_of_facets + 1); +} + +void Shield::Repair() { + regeneration.RepairFully(); + + for (Resource &facet : facets) { + facet.RepairFully(); + } + + operational = 1.0; +} + +bool Shield::Damaged() const { + return Percent() < 1; +} + + +void Shield::Regenerate(const bool player_ship) { + //const bool apply_difficulty_shields = configuration()->physics_config.difficulty_based_shield_recharge; + + // Discharge numbers for nicer, gradual discharge + //const float discharge_per_second = configuration()->physics_config.speeding_discharge; + //approx + //const float discharge_rate = (1 - (1 - discharge_per_second) * simulation_atom_var); + //const float min_shield_discharge = configuration()->physics_config.min_shield_speeding_discharge; + + // Some basic sanity checks first + // No point in all this code if there are no shields. + if (number_of_facets < 2 || TotalMaxLayerValue() == 0) { + return; + } + + // No shields in SPEC + if (ftl_drive->Enabled() && !configuration()->physics_config.shields_in_spec) { + Decrease(); + return; + } + + // No shields while cloaked + if (cloak->Active()) { + Decrease(); + return; + } + + /* A discussion of consumption + * First, you set the consumption for 1 second (not atom_var) + * Then you consume and get back actual consumption + * Finally, you adjust whatever used it to the value in question + */ + + // Shield Maintenance + // TODO: lib_damage restore efficiency by replacing with shield->efficiency + //const double efficiency = 1; + + const double shield_maintenance_cost = TotalMaxLayerValue() * configuration()->fuel.shield_maintenance_factor; + SetConsumption(shield_maintenance_cost); + const double actual_maintenance_percent = Consume(); + if(Percent() > actual_maintenance_percent) { + Decrease(); + return; + } + + // Manually throttle shield strength + if(Percent() > max_power) { + Decrease(); + return; + } + + // Shield Regeneration + if(TotalLayerValue() == TotalMaxLayerValue()) { + // Fully charged. No need for more action or energy consumption + return; + } + + const double shield_regeneration_cost = regeneration.AdjustedValue() * configuration()->fuel.shield_regeneration_factor; + SetConsumption(shield_regeneration_cost); + const double actual_regeneration_percent = Consume(); + double regen = actual_regeneration_percent * regeneration.AdjustedValue() * simulation_atom_var; + + for (Resource &facet : facets) { + facet += regen; + } + + // Difficulty settings + // TODO: enable + //double difficulty_shields = 1.0; + // if (apply_difficulty_shields) { + // difficulty_shields = g_game.difficulty; + // } + + /*if (unit->GetNebula() != nullptr) { + shield_recharge *= nebshields; + } + + // Adjust other (enemy) ships for difficulty + if (!player_ship) { + shield_recharge *= difficulty; + }*/ + + //const float nebshields = configuration()->physics_config.nebula_shield_recharge; +} + +void Shield::AdjustPower(double percent) { + max_power = percent; +} + + +void Shield::FullyCharge() { + for (Resource &facet : facets) { + facet.Set(facet.AdjustedValue()); + } +} + +void Shield::Decrease() { + for(auto& facet : facets) { + facet--; + } +} + +void Shield::Zero() { + for(auto& facet : facets) { + facet = 0; + } +} \ No newline at end of file diff --git a/engine/src/components/shield.h b/engine/src/components/shield.h new file mode 100644 index 0000000000..1af3ee6a6d --- /dev/null +++ b/engine/src/components/shield.h @@ -0,0 +1,75 @@ +/* + * shield.h + * + * Copyright (C) 2001-2023 Daniel Horn, Benjamen Meyer, Roy Falk, Stephen G. Tuggy, + * and other Vega Strike contributors. + * + * https://github.com/vegastrike/Vega-Strike-Engine-Source + * + * This file is part of Vega Strike. + * + * Vega Strike 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. + * + * Vega Strike 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 Vega Strike. If not, see . + */ + +#ifndef VEGA_STRIKE_ENGINE_COMPONENTS_SHIELD_H +#define VEGA_STRIKE_ENGINE_COMPONENTS_SHIELD_H + +#include "component.h" +#include "energy_consumer.h" +#include "damage/damageable_layer.h" + +class FtlDrive; +class Cloak; + +/** Shield component */ +class Shield : public Component, public EnergyConsumer, public DamageableLayer { + const FtlDrive *ftl_drive; + const Cloak *cloak; + Resource regeneration; // The current capability of a potentially damaged part + Resource max_power; // Used to manually throttle shield +public: + static int front; + static int back; + static int left; + static int right; + + Shield(EnergyContainer *source, FtlDrive *ftl_drive, Cloak *cloak, FacetConfiguration configuration = FacetConfiguration::four); + + // Component Methods + void Load(std::string unit_key) override; + void SaveToCSV(std::map& unit) const override; + + bool CanDowngrade() const override; + bool Downgrade() override; + bool CanUpgrade(const std::string upgrade_key) const override; + bool Upgrade(const std::string upgrade_key) override; + + double PercentOperational() const override; + void CalculatePercentOperational(); + + void Damage(); + void DamageByPercent(double percent); + void Repair() override; + bool Damaged() const override; + + void Regenerate(const bool player_ship); + + // Manipulate shield strength + void AdjustPower(double percent); + void FullyCharge(); + void Decrease(); + void Zero(); +}; + +#endif // VEGA_STRIKE_ENGINE_COMPONENTS_SHIELD_H diff --git a/engine/src/configuration/configuration.h b/engine/src/configuration/configuration.h index 76374186a4..ca12d6b0bd 100644 --- a/engine/src/configuration/configuration.h +++ b/engine/src/configuration/configuration.h @@ -189,6 +189,9 @@ struct Fuel { double ftl_drive_factor{0.1}; double jump_drive_factor{1.0}; + double shield_maintenance_factor{0.01}; + double shield_regeneration_factor{0.1}; + // 0 infinite, 1 fuel, 2 energy, 3 ftl_energy, 4 disabled EnergyConsumerSource drive_source{EnergyConsumerSource::Fuel}; EnergyConsumerSource reactor_source{EnergyConsumerSource::Fuel}; diff --git a/engine/src/damage/damage.h b/engine/src/damage/damage.h index 249a8955f5..d4b775286c 100644 --- a/engine/src/damage/damage.h +++ b/engine/src/damage/damage.h @@ -32,36 +32,37 @@ struct Damage { // TODO: generalize this later // TODO: add shield leach - float normal_damage; // Go through shield, armor and finally hull and subsystems - float phase_damage; // Bypass shields - float propulsion_damage; // Disables the ship's drive - //float blast_effect; // Add torque to the ship + double normal_damage; // Go through shield, armor and finally hull and subsystems + double phase_damage; // Bypass shields + double propulsion_damage; // Disables the ship's drive + //double blast_effect; // Add torque to the ship bool Spent() { return (normal_damage == 0 && phase_damage == 0 && propulsion_damage == 0);// && blast_effect == 0); } - Damage(float normal_damage = 0, - float phase_damage = 0, - float propulsion_damage = 0, - float blast_effect = 0) : + Damage(double normal_damage = 0, + double phase_damage = 0, + double propulsion_damage = 0, + double blast_effect = 0) : normal_damage(normal_damage), phase_damage(phase_damage), propulsion_damage(propulsion_damage) { } + //blast_effect(blast_effect), }; struct InflictedDamage { - float total_damage; // Total inflicted damage - float normal_damage; // Go through shield, armor and finally hull and subsystems - float phase_damage; // Bypass shields - float propulsion_damage; // Disables the ship's drive + double total_damage; // Total inflicted damage + double normal_damage; // Go through shield, armor and finally hull and subsystems + double phase_damage; // Bypass shields + double propulsion_damage; // Disables the ship's drive // This array stores the damage inflicted to each layer // By default, it is hard-coded to three layers: // shield, armor and hull. But this makes this implementation inflexible. - std::vector inflicted_damage_by_layer; + std::vector inflicted_damage_by_layer; InflictedDamage(int number_of_layers = 3) { total_damage = 0.0; diff --git a/engine/src/damage/damageable_layer.cpp b/engine/src/damage/damageable_layer.cpp index c5b3446fdd..9c70d003ac 100644 --- a/engine/src/damage/damageable_layer.cpp +++ b/engine/src/damage/damageable_layer.cpp @@ -31,147 +31,101 @@ #include "random_utils.h" -DamageableLayer::DamageableLayer(int layer_index, - FacetConfiguration configuration, - Health health_template, - bool core_layer) { - health_template.layer = layer_index; - int size = as_integer(configuration); - - std::vector facets; - for (int i = 0; i < size; i++) { - facets.push_back(health_template); - } - this->layer_index = layer_index; - this->configuration = configuration; - this->number_of_facets = size; - this->facets = facets; - this->core_layer = core_layer; -} - -DamageableLayer::DamageableLayer(int layer_index, - FacetConfiguration configuration, - float health_array[], - float regeneration, - bool core_layer) { - int size = as_integer(configuration); - std::vector facets; - for (int i = 0; i < size; i++) { - Health health(layer_index, health_array[i], regeneration); +DamageableLayer::DamageableLayer(int layer, + FacetConfiguration configuration, + double health_template, + Damage vulnerabilities, + bool core_layer, double regeneration): + layer(layer), + vulnerabilities(vulnerabilities), + core_layer(core_layer), + number_of_facets(as_integer(configuration)) { + Resource health = Resource(health_template, 0.0, health_template); + for (int i = 0; i < number_of_facets; i++) { facets.push_back(health); } - - this->layer_index = layer_index; - this->configuration = configuration; - this->number_of_facets = size; - this->facets = facets; - this->core_layer = core_layer; } -DamageableLayer::DamageableLayer(int layer_index, int number_of_facets, std::vector &facets, bool core_layer) : - layer_index(layer_index), - number_of_facets(number_of_facets), - facets(facets), - core_layer(core_layer) { -} - -DamageableLayer::DamageableLayer() : - layer_index(0), - number_of_facets(0), - core_layer(false) { -} -void DamageableLayer::AdjustPower(const float &percent) { - float adjusted_percent = std::max(std::min(percent, 1.0f), 0.0f); - - for (Health &facet : facets) { - facet.AdjustPower(adjusted_percent); - } -} void DamageableLayer::DealDamage(const CoreVector &attack_vector, Damage &damage, InflictedDamage &inflicted_damage) { if (number_of_facets == 0) { return; } - int impacted_facet_index = GetFacetIndex(attack_vector); - facets[impacted_facet_index].DealDamage(damage, inflicted_damage); -} - -void DamageableLayer::Destroy() { - for (Health &facet : facets) { - facet.Destroy(); - } -} - -void DamageableLayer::Disable() { - for (Health &facet : facets) { - facet.Disable(); - } -} - -void DamageableLayer::Discharge(float discharge_rate, float minimum_discharge) { - for (Health &facet : facets) { - if (facet.health > minimum_discharge * facet.max_health) { - facet.health *= discharge_rate; - } + if (number_of_facets != facets.size()) { + return; } -} -// Used for nicer graphics when entering SPEC -void DamageableLayer::GradualDisable() { - for (Health &facet : facets) { - facet.ReduceLayerMaximumByOne(); - } -} + int impacted_facet_index = GetFacetIndex(attack_vector); + + DealDamageComponent(impacted_facet_index, 0, damage.normal_damage, vulnerabilities.normal_damage, inflicted_damage); + DealDamageComponent(impacted_facet_index, 1, damage.phase_damage, vulnerabilities.phase_damage, inflicted_damage); -void DamageableLayer::Enable() { - for (Health &facet : facets) { - facet.Enable(); - } + // TODO: implement other types of damage } -bool DamageableLayer::Enabled() { - if (number_of_facets == 0) { - return false; +/** + * @brief DamageableLayer::DealDamageComponent - deal a component of damage (normal, phased) and not damage + * a component. + * @param health - to subtract from + * @param damage - to inflict + * @param vulnerability - adjust for + */ +// TODO: type is ugly hack +void DamageableLayer::DealDamageComponent(int impacted_facet_index, int type, + double &damage, double vulnerability, + InflictedDamage &inflicted_damage) { + // Here we adjust for specialized weapons such as shield bypassing and shield leeching + // which only damage the shield. + // We also cap the actual damage at the current health + const double adjusted_damage = std::min(damage * vulnerability, facets[impacted_facet_index].Value()); + + // Damage the current health + facets[impacted_facet_index].DamageByValue(adjusted_damage); + + // Adjust damage left to pass on to the next layer + damage -= adjusted_damage; + + // Record damage + switch (type) { + case 0: + inflicted_damage.normal_damage += adjusted_damage; + break; + case 1: + inflicted_damage.phase_damage += adjusted_damage; } - return facets[0].enabled; + inflicted_damage.total_damage += adjusted_damage; + inflicted_damage.inflicted_damage_by_layer.at(layer) += adjusted_damage; } -// TODO: test -// Boost shields to 150% -void DamageableLayer::Enhance() { - for (Health &facet : facets) { - // Don't enhance armor and hull - facet.Enhance(); - } -} -int DamageableLayer::GetFacetIndex(const CoreVector &attack_vector) { +int DamageableLayer::GetFacetIndex(const CoreVector &attack_vector) const { if (number_of_facets == 0) { return -1; } // Convenience Variables - float i = attack_vector.i; - float j = attack_vector.j; - float k = attack_vector.k; + double i = attack_vector.i; + // double j = attack_vector.j; not used + double k = attack_vector.k; + double a = i + k; + double b = i - k; - if (configuration == FacetConfiguration::one) { + switch (number_of_facets) { + case(1): return 0; - } else if (configuration == FacetConfiguration::two) { + case(2): if (k >= 0) { return 0; } else { return 1; } - } else if (configuration == FacetConfiguration::four) { - float a = i + k; - float b = i - k; + case(4): if (a >= 0 && b >= 0) { return 0; } @@ -187,155 +141,70 @@ int DamageableLayer::GetFacetIndex(const CoreVector &attack_vector) { if (a < 0 && b < 0) { return 1; } - + default: return 0; - } - - // The default - return 0; } -/** This is one of the few functions in libdamage to implement a non-generic - * model. There are too many models for damaging components and we have to settle - * for one. */ -void DamageableLayer::ReduceLayerCapability(const float &percent, - const float &chance_to_reduce_regeneration) { - if (number_of_facets == 0) { - return; - } - - bool affect_regeneration = randomDouble() <= chance_to_reduce_regeneration; - int facet_index = randomInt(number_of_facets); - if (affect_regeneration) { - // Reduce regeneration - facets[facet_index].ReduceRegeneration(percent); - } else { - // Reduce max health - facets[facet_index].ReduceLayerMaximum(percent); - } -} - -float DamageableLayer::TotalLayerValue() { - float total_value = 0.0f; - for (const Health &facet : facets) { - total_value += facet.health; +double DamageableLayer::TotalLayerValue() const { + double total_value = 0.0f; + for (const Resource &facet : facets) { + total_value += facet.Value(); } return total_value; } -float DamageableLayer::TotalMaxLayerValue() { - float total_value = 0.0f; - for (const Health &facet : facets) { - total_value += facet.max_health; +double DamageableLayer::TotalMaxLayerValue() const { + double total_value = 0.0f; + for (const Resource &facet : facets) { + total_value += facet.MaxValue(); } return total_value; } -float DamageableLayer::AverageLayerValue() { - float total_value = 0.0f; - for (const Health &facet : facets) { - total_value += facet.health; +double DamageableLayer::AverageLayerValue() const { + double total_value = 0.0f; + for (const Resource &facet : facets) { + total_value += facet.Value(); } - return total_value / facets.size(); + return total_value / number_of_facets; } -float DamageableLayer::AverageMaxLayerValue() { - float total_value = 0.0f; - for (const Health &facet : facets) { - total_value += facet.max_health; +double DamageableLayer::AverageMaxLayerValue() const { + double total_value = 0.0f; + for (const Resource &facet : facets) { + total_value += facet.MaxValue(); } - return total_value / facets.size(); -} - -float CalculatePercentage(float numerator, float denominator) { - return numerator / denominator; + return total_value / number_of_facets; } -float DamageableLayer::GetMaxHealth() { - if (number_of_facets == 0) { - return 0.0f; - } - - return facets[0].max_health; -} - -float DamageableLayer::GetPercent(FacetName facet_name) { - if (number_of_facets == 0) { - return 0.0f; - } - - float numerator, denominator; - // One, Two or Four shield configurations - // Note the fallthrough - switch (configuration) { - case FacetConfiguration::one: - case FacetConfiguration::two: - case FacetConfiguration::four: - numerator = facets[as_integer(facet_name)].health; - denominator = facets[as_integer(facet_name)].max_health; - return CalculatePercentage(numerator, denominator); - - default: - return 0.0; +double DamageableLayer::Percent() const { + if(number_of_facets == 0) { + return 0; } -} -void DamageableLayer::FullyCharge() { - for (Health &facet : facets) { - facet.SetHealth(facet.adjusted_health); + double percents = 0; + for(const Resource& facet:facets){ + percents += facet.Percent(); } -} -void DamageableLayer::Regenerate(float recharge_rate) { - for (Health &facet : facets) { - facet.Regenerate(recharge_rate); - } + return percents/number_of_facets; } -void DamageableLayer::RegenerateOrDischarge(float recharge_rate, bool velocity_discharge, float discharge_rate) { - for (Health &facet : facets) { - facet.Regenerate(recharge_rate); +double DamageableLayer::Percent(int facet) const { + if(number_of_facets <= facet || facets.size() <= facet) { + return 0; } -} -float DamageableLayer::GetRegeneration() { - if (number_of_facets == 0) { - return 0.0f; + if(facets[facet].MaxValue() == 0) { + return 0; } - return facets[0].regeneration; -} - -void DamageableLayer::UpdateFacets(const float new_facet_strength) { - for (unsigned int i = 0; i < number_of_facets; i++) { - facets[i].Update(new_facet_strength); - } + return facets[facet].Percent(); } -void DamageableLayer::UpdateFacets(const unsigned int new_size, const float new_facets[4]) { - assert(new_size == number_of_facets); - switch (number_of_facets) { - case 1: - facets[0].Update(new_facets[0]); - break; - - case 4: - facets[2].Update(new_facets[0]); - facets[3].Update(new_facets[1]); - case 2: // Note the fallthrough - facets[0].Update(new_facets[3]); - facets[1].Update(new_facets[2]); - } -} - -void DamageableLayer::UpdateRegeneration(const float &new_regeneration_value) { - for (Health &facet : facets) { - facet.regeneration = new_regeneration_value; - facet.max_regeneration = new_regeneration_value; - facet.regenerative = true; - facet.enabled = true; - } -} +int DamageableLayer::Layer() const { + return layer; +} \ No newline at end of file diff --git a/engine/src/damage/damageable_layer.h b/engine/src/damage/damageable_layer.h index 8d4313b875..1cd84d1267 100644 --- a/engine/src/damage/damageable_layer.h +++ b/engine/src/damage/damageable_layer.h @@ -26,75 +26,53 @@ #include "facet_configuration.h" #include "core_vector.h" -#include "health.h" +#include "damage.h" +#include "resource/resource.h" /** * @brief The DamageableLayer class represents an object to be damaged. * This can be shields, armor, hull or subsystem. - * A layer can be regenerative (e.g. shields) or not (pretty much everything else). + * A layer can be regenerative (e.g. shields) or not (armor/hull). */ -struct DamageableLayer { - - int layer_index; - FacetConfiguration configuration; - - unsigned int number_of_facets; // How many facets. e.g. dual shield (front and back). - std::vector facets; // The facets container +class DamageableLayer { +protected: + int layer; // The layer we're in, for recording damage + + Damage vulnerabilities; + // TODO: implement "shield leaks" bool core_layer; // Damage to the core layer has a chance of also - // damaging internal components such as propulsion. + // damaging internal components such as propulsion. + + int number_of_facets; // How many facets. e.g. dual shield (front and rear). + std::vector> facets; // The facets container friend class Damageable; friend struct DamageableObject; - //static float damage_component_chance = 0.03; - DamageableLayer(int layer_index, - FacetConfiguration configuration, - Health health_template, - bool core_layer); - - DamageableLayer(int layer_index, +public: + DamageableLayer(int layer, FacetConfiguration configuration, - float health_array[], - float regeneration, - bool core_layer); + double health_template, + Damage vulnerabilities = Damage(1.0,1.0), + bool core_layer = false, + double regeneration = 0.0); - DamageableLayer(int layer_index, - int number_of_facets, - std::vector &facets, - bool core_layer); - DamageableLayer(); + DamageableLayer() = delete; - void AdjustPower(const float &percent); void DealDamage(const CoreVector &attack_vector, Damage &damage, InflictedDamage &inflicted_damage); - void Destroy(); - void Disable(); - void GradualDisable(); - void Discharge(float discharge_rate, float minimum_discharge); - void Enable(); - bool Enabled(); - void Enhance(); - - int GetFacetIndex(const CoreVector &attack_vector); - - void ReduceLayerCapability(const float &percent, - const float &chance_to_reduce_regeneration); - - float TotalLayerValue(); - float TotalMaxLayerValue(); - float AverageLayerValue(); - float AverageMaxLayerValue(); - - float GetMaxHealth(); - float GetPercent(FacetName facet_name); - - void FullyCharge(); - void Regenerate(float recharge_rate); - void RegenerateOrDischarge(float recharge, bool velocity_discharge, float discharge_rate); - float GetRegeneration(); - void UpdateFacets(const float new_facet_strength); - void UpdateFacets(const unsigned int size, const float new_facets[4]); - void UpdateRegeneration(const float &new_regeneration_value); + void DealDamageComponent(int impacted_facet_index, int type, double &damage, double vulnerability, InflictedDamage &inflicted_damage); + + int GetFacetIndex(const CoreVector &attack_vector) const; + + double TotalLayerValue() const; + double TotalMaxLayerValue() const; + double AverageLayerValue() const; + double AverageMaxLayerValue() const; + double Percent() const; + double Percent(int facet) const; + + int Layer() const; }; #endif //VEGA_STRIKE_ENGINE_DAMAGE_DAMAGEABLE_LAYER_H diff --git a/engine/src/damage/damageable_object.cpp b/engine/src/damage/damageable_object.cpp index ef2aac62eb..09f867885f 100644 --- a/engine/src/damage/damageable_object.cpp +++ b/engine/src/damage/damageable_object.cpp @@ -28,38 +28,30 @@ #include #include -DamageableObject::DamageableObject() { - Health hull_health = Health(1, 1, 0); - Health armor_health = Health(0, 0, 0); - Health shield_health = Health(0, 0, 5); - DamageableLayer hull_layer = DamageableLayer(0, FacetConfiguration::one, hull_health, true); - DamageableLayer armor_layer = DamageableLayer(1, FacetConfiguration::four, armor_health, false); - DamageableLayer shield_layer = DamageableLayer(2, FacetConfiguration::four, shield_health, false); +DamageableObject::DamageableObject(): + number_of_layers(0) {} - layers = {hull_layer, armor_layer, shield_layer}; - number_of_layers = 3; -} -DamageableObject::DamageableObject(std::vector layers, - std::vector components) { - number_of_layers = layers.size(); - this->layers = layers; - this->components = components; -} +DamageableObject::DamageableObject(std::vector layers): + number_of_layers(3), layers(layers) {} -/*DamageableObject::DamageableObject() -{ - layers.push_back(DamageableLayer()); -}*/ +void DamageableObject::AddLayer(DamageableLayer* layer) { + number_of_layers++; + layers.push_back(layer); +} InflictedDamage DamageableObject::DealDamage(const CoreVector &attack_vector, Damage &damage) { InflictedDamage inflicted_damage(3); // Currently hard-coded default is 3! // Higher index layers are outer layers. We therefore need to reverse the order. - for (DamageableLayer &layer : boost::adaptors::reverse(layers)) { - layer.DealDamage(attack_vector, damage, inflicted_damage); + for (DamageableLayer* layer : boost::adaptors::reverse(layers)) { + if(!layer) { + continue; + } + + layer->DealDamage(attack_vector, damage, inflicted_damage); // TODO: handle damage to components here? // Assumed the core layer has only one facet @@ -77,9 +69,7 @@ InflictedDamage DamageableObject::DealDamage(const CoreVector &attack_vector, Da } void DamageableObject::Destroy() { - for (DamageableLayer layer : layers) { - layer.Destroy(); - } + layers[0]->facets[0] = 0; } // We make a lot of assumptions here: @@ -91,5 +81,5 @@ bool DamageableObject::Destroyed() { return true; } - return layers[0].facets[0].destroyed; + return layers[0]->facets[0] == 0; } diff --git a/engine/src/damage/damageable_object.h b/engine/src/damage/damageable_object.h index 592bdc4a5d..11e6a9d576 100644 --- a/engine/src/damage/damageable_object.h +++ b/engine/src/damage/damageable_object.h @@ -33,13 +33,12 @@ */ struct DamageableObject { int number_of_layers; - std::vector layers; // Typically shield/armor/hull - std::vector components; // Propoulsion, life support, + std::vector layers; // Typically shield/armor/hull - DamageableObject(std::vector layers, - std::vector components); DamageableObject(); - + DamageableObject(std::vector layers); + + void AddLayer(DamageableLayer* layer); InflictedDamage DealDamage(const CoreVector &attack_vector, Damage &damage); void Destroy(); diff --git a/engine/src/damage/facet_configuration.h b/engine/src/damage/facet_configuration.h index e9470ffaa3..d77760c005 100644 --- a/engine/src/damage/facet_configuration.h +++ b/engine/src/damage/facet_configuration.h @@ -30,13 +30,8 @@ * @brief The FacetConfiguration enum represents how many facets a layer has and it what configuration. * @details The use of enum class enforces the correctness of the parameter without checking. * You can't pass 17 for example. - * I debated keeping the existing dual/quad but as it turns out, dual is Greek and Quad is latin. - * It is also easier to understand five and six than penta and sexta (No! Hexa! Mixed again!). - * This can also be extended to other facet configurations, such as pyramid - * (left, right, top, bottom and back). - * - * For consistency's sake, always start left to right, top to bottom and finally front to back. - * That is x,y,z. This is also how both core vector and Vector class are ordered. + * The game originally supported 8 facets. + * However, I disabled all facets except 1,2 and 4 due to complexity. */ enum class FacetConfiguration { one = 1, // A single facet covering all directions @@ -44,29 +39,5 @@ enum class FacetConfiguration { four = 4, // Front, back, left and right }; -enum class FacetName { - single = 0, - - two_front = 0, - two_rear = 1, - - four_left = 0, - four_right = 1, - four_front = 2, - four_rear = 3, - - left_top_front = 0, - right_top_front = 1, - left_bottom_front = 2, - right_bottom_front = 3, - left_top_rear = 4, - right_top_rear = 5, - left_bottom_rear = 6, - right_bottom_rear = 7 -}; - -// Same code as mount_size.h! -// auto as_integer(Enumeration const value) - #endif //VEGA_STRIKE_ENGINE_DAMAGE_FACET_CONFIGURATION_H diff --git a/engine/src/damage/health.cpp b/engine/src/damage/health.cpp deleted file mode 100644 index b555ca4c2d..0000000000 --- a/engine/src/damage/health.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * health.cpp - * - * Copyright (C) 2021-2022 Daniel Horn, Roy Falk, Stephen G. Tuggy, - * and other Vega Strike contributors - * - * https://github.com/vegastrike/Vega-Strike-Engine-Source - * - * This file is part of Vega Strike. - * - * Vega Strike 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 2 of the License, or - * (at your option) any later version. - * - * Vega Strike 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 Vega Strike. If not, see . - */ - - -#include "health.h" - -#include -#include -#include - -void Health::AdjustPower(const float &percent) { - if (!regenerative) { - // Not applicable for armor and hull - return; - } - - if (percent > 1 || percent < 0) { - // valid values are between 0 and 1 - return; - } - - adjusted_health = max_health * percent; - if (adjusted_health < health) { - health = adjusted_health; - } -} - -void Health::DealDamage(Damage &damage, InflictedDamage &inflicted_damage) { - // If this layer is destroyed, it can no longer sustain damage - if (destroyed) { - return; - } - - DealDamageComponent(0, damage.normal_damage, vulnerabilities.normal_damage, inflicted_damage); - DealDamageComponent(1, damage.phase_damage, vulnerabilities.phase_damage, inflicted_damage); - - // TODO: implement other types of damage -} - - -/** - * @brief Health::DealDamageComponent - deal a component of damage (normal, phased) and not damage - * a component. - * @param health - to subtract from - * @param damage - to inflict - * @param vulnerability - adjust for - */ -// TODO: type is ugly hack -void Health::DealDamageComponent(int type, float &damage, float vulnerability, InflictedDamage &inflicted_damage) { - // Here we adjust for specialized weapons such as shield bypassing and shield leeching - // which only damage the shield. - // We also cap the actual damage at the current health - const float adjusted_damage = std::min(damage * vulnerability, health); - - // We check if there's any damage left to pass on to the next layer - damage -= adjusted_damage; - - // Damage the current health - health -= adjusted_damage; - - // Record damage - switch (type) { - case 0: - inflicted_damage.normal_damage += adjusted_damage; - break; - case 1: - inflicted_damage.phase_damage += adjusted_damage; - } - - inflicted_damage.total_damage += adjusted_damage; - inflicted_damage.inflicted_damage_by_layer.at(layer) += adjusted_damage; - - if (health == 0 && !regenerative) { - destroyed = true; - } -} - -void Health::Disable() { - if (regenerative && enabled) { - enabled = false; - health = 0.0f; - } -} - -void Health::Destroy() { - health = 0; - destroyed = true; -} - -void Health::Enable() { - if (regenerative && !enabled) { - enabled = true; - } -} - -void Health::Enhance(float percent) { - // Don't enhance armor and hull - if (!regenerative) { - return; - } - - health = max_health * percent; -} - -void Health::ReduceLayerMaximum(const float &percent) { - adjusted_health = std::max(0.0f, max_health * (1 - percent)); - health = std::min(health, max_health); -} - -void Health::ReduceLayerMaximumByOne() { - adjusted_health = std::max(0.0f, adjusted_health - 1); - health = std::min(health, adjusted_health); -} - -void Health::ReduceLayerMaximumByOnePercent() { - float percent = adjusted_health / max_health - 0.01f; - max_health = std::max(0.0f, max_health * percent); -} - -void Health::ReduceRegeneration(const float &percent) { - regeneration = std::max(0.0f, regeneration - max_regeneration * percent); -} - -void Health::Regenerate() { - if (!enabled || destroyed || !regenerative) { - return; - } - - health = std::min(adjusted_health, health + regeneration); -} - -void Health::Regenerate(float recharge_rate) { - if (!enabled || destroyed || !regenerative) { - return; - } - - health = std::min(adjusted_health, health + recharge_rate); -} - -void Health::SetHealth(float health) { - health = std::min(max_health, health); - health = std::max(0.0f, health); - this->health = health; -} - -void Health::Update(float health) { - this->health = health; - max_health = health; - adjusted_health = health; -} diff --git a/engine/src/damage/health.h b/engine/src/damage/health.h deleted file mode 100644 index 8d06d16506..0000000000 --- a/engine/src/damage/health.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * health.h - * - * Copyright (C) 2021-2023 Daniel Horn, Roy Falk, Stephen G. Tuggy, Benjamen R. Meyer, - * and other Vega Strike contributors - * - * https://github.com/vegastrike/Vega-Strike-Engine-Source - * - * This file is part of Vega Strike. - * - * Vega Strike 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 2 of the License, or - * (at your option) any later version. - * - * Vega Strike 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 Vega Strike. If not, see . - */ -#ifndef VEGA_STRIKE_ENGINE_DAMAGE_HEALTH_H -#define VEGA_STRIKE_ENGINE_DAMAGE_HEALTH_H - -#include "damage.h" - -/** - * @brief The Health struct represents the health of something. - * It can be a shield, armor, hull or subsystem. - * @details - * More specifically, it represents the health of DamageableFacet. - * If the health of a non-regenerative facet is zero, it is destroyed. - * If this facet is the sole facet of a DamageableLayer, it is destroyed. - * If this DamageableLayer is mortal, the whole DamageableObject is destroyed. - * Therefore, destroying the hull of a ship destroys it. - * But, destroying the reactor or life-support of a ship also disabled it, - * killing everyone inside, - * potentially without actually destroying the ship. - */ -struct Health { -public: - int layer; // The layer we're in, for recording damage - float max_health; // The absolute maximum, for a new, undamaged part - float adjusted_health; // The current max, for a potentially damaged part - // or max health for shields when there's not enough power - // for them - // or shields are declining in SPEC - float health; - float max_regeneration; // The absolute maximum, for a new, undamaged part - float regeneration; // The current capability of a potentially damaged part - bool regenerative; - bool destroyed; - bool enabled; - Damage vulnerabilities; - // TODO: implement "shield leaks" - - -public: - - /** - * @brief The Effect enum specifies what happens when the health of a specific object is zero. - */ - enum class Effect { - none, // Nothing happens - isolated, // The facet/layer/subsystem is destroyed - disabling, // The whole DamageableObject is disabled but not destroyed. - // It can then be tractored to another ship and repaired or sold. - destroying // The DamageableObject is destroyed, potentially leaving debris behind - } effect{}; - - Health(int layer, float health = 1, float regeneration = 0) : - Health(layer, health, health, regeneration) { - } - - Health(int layer, float max_health, float health, float regeneration) : - layer(layer), - max_health(max_health), - adjusted_health(max_health), - health(health), - max_regeneration(regeneration), - regeneration(regeneration), - regenerative(regeneration > 0) { - destroyed = false; - if (layer == 0) { - regenerative = false; - } - enabled = regenerative; // Only relevant for regenerative objects (e.g. shields). - vulnerabilities.normal_damage = 1; - vulnerabilities.phase_damage = 1; - }; - - float Percent() const { - return max_health != 0 ? health / max_health : 0.0f; - } - - void AdjustPower(const float &percent); - void AdjustPercentage(); - void DealDamage(Damage &damage, InflictedDamage &inflicted_damage); - void DealDamageComponent(int type, float &damage, float vulnerability, InflictedDamage &inflicted_damage); - void Disable(); - void Destroy(); - void Enable(); - void Enhance(float percent = 1.5f); - void ReduceLayerMaximum(const float &percent); - void ReduceLayerMaximumByOne(); - void ReduceLayerMaximumByOnePercent(); - void ReduceRegeneration(const float &percent); - void Regenerate(); - void Regenerate(float recharge_rate); - void SetHealth(float health); - void Update(float health); -}; - -#endif //VEGA_STRIKE_ENGINE_DAMAGE_HEALTH_H diff --git a/engine/src/damage/tests/health_tests.cpp b/engine/src/damage/tests/health_tests.cpp deleted file mode 100644 index bb7b283cfb..0000000000 --- a/engine/src/damage/tests/health_tests.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * health_tests.cpp - * - * Copyright (C) 2021-2022 Daniel Horn, Roy Falk, Stephen G. Tuggy, - * and other Vega Strike contributors - * - * https://github.com/vegastrike/Vega-Strike-Engine-Source - * - * This file is part of Vega Strike. - * - * Vega Strike 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 2 of the License, or - * (at your option) any later version. - * - * Vega Strike 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 Vega Strike. If not, see . - */ - - -#include - -#include "health.h" - - -// Demonstrate some basic assertions. -TEST(Shield, Sanity) { - Damage damage; - InflictedDamage inflicted_damage(3); - damage.normal_damage = 10; - EXPECT_EQ(damage.normal_damage, 10); - - Health health(2, 100, 100, 25); - - EXPECT_EQ(health.health, 100); - EXPECT_EQ(health.max_health, 100); - EXPECT_EQ(health.regeneration, 25); - EXPECT_TRUE(health.regenerative); - EXPECT_FALSE(health.destroyed); - EXPECT_TRUE(health.enabled); - - health.DealDamage(damage, inflicted_damage); - EXPECT_EQ(health.health, 90); - EXPECT_EQ(damage.normal_damage, 0); - - health.Disable(); - health.Regenerate(1.0f); - EXPECT_EQ(health.health, 0); - - health.Enable(); - health.Regenerate(health.regeneration); - EXPECT_EQ(health.health, 25); - - damage.normal_damage = 110; - health.DealDamage(damage, inflicted_damage); - EXPECT_EQ(health.health, 0); - EXPECT_TRUE(health.regenerative); - EXPECT_FALSE(health.destroyed); - EXPECT_TRUE(health.enabled); - EXPECT_EQ(damage.normal_damage, 85); -} - -TEST(Armor, Sanity) { - Damage damage; - InflictedDamage inflicted_damage(3); - damage.normal_damage = 10; - EXPECT_EQ(damage.normal_damage, 10); - - Health health(2, 100, 100, 0); - health.vulnerabilities.normal_damage = 1; - - EXPECT_EQ(health.health, 100); - EXPECT_EQ(health.max_health, 100); - EXPECT_EQ(health.regeneration, 0); - EXPECT_FALSE(health.regenerative); - EXPECT_FALSE(health.destroyed); - EXPECT_FALSE(health.enabled); - - health.DealDamage(damage, inflicted_damage); - EXPECT_EQ(health.health, 90); - EXPECT_EQ(damage.normal_damage, 0); - - health.Disable(); - health.Regenerate(1.0f); - EXPECT_EQ(health.health, 90); - EXPECT_FALSE(health.enabled); - - health.Enable(); - health.Regenerate(1.0f); - EXPECT_EQ(health.health, 90); - EXPECT_FALSE(health.enabled); - - damage.normal_damage = 110; - health.DealDamage(damage, inflicted_damage); - EXPECT_EQ(health.health, 0); - EXPECT_FALSE(health.regenerative); - EXPECT_TRUE(health.destroyed); - EXPECT_FALSE(health.enabled); - EXPECT_EQ(damage.normal_damage, 20); -} diff --git a/engine/src/damage/tests/layer_tests.cpp b/engine/src/damage/tests/layer_tests.cpp index c66237a948..d3ec08f584 100644 --- a/engine/src/damage/tests/layer_tests.cpp +++ b/engine/src/damage/tests/layer_tests.cpp @@ -30,8 +30,7 @@ // Demonstrate some basic assertions. TEST(Layer, Sanity) { - Health health(10, 10, 0); - DamageableLayer layer = DamageableLayer(0, FacetConfiguration::four, health, true); + DamageableLayer layer = DamageableLayer(0, FacetConfiguration::four, 10, Damage(1.0,1.0), true); EXPECT_EQ(layer.GetFacetIndex(CoreVector(0, 0, 0)), 0); diff --git a/engine/src/damage/tests/object_tests.cpp b/engine/src/damage/tests/object_tests.cpp index 76774af99b..7cc13952d4 100644 --- a/engine/src/damage/tests/object_tests.cpp +++ b/engine/src/damage/tests/object_tests.cpp @@ -33,29 +33,26 @@ TEST(DamageableObject, Sanity) { DamageableObject object; - Health hull_health(0, 50, 0.0f); - Health armor_health(1, 100, 0.0f); - Health shield_health(2, 150, 10.0f); + DamageableLayer hull = DamageableLayer(0, FacetConfiguration::one, 50, Damage(1.0,1.0), true); + DamageableLayer armor = DamageableLayer(1, FacetConfiguration::four, 100, Damage(1.0,1.0), false); + DamageableLayer shield = DamageableLayer(2, FacetConfiguration::four, 200, Damage(1.0,0.0), false); - DamageableLayer hull = DamageableLayer(0, FacetConfiguration::one, hull_health, true); - DamageableLayer armor = DamageableLayer(1, FacetConfiguration::four, armor_health, false); - DamageableLayer shield = DamageableLayer(2, FacetConfiguration::four, shield_health, false); + EXPECT_EQ(hull.Layer(), 0); + EXPECT_EQ(armor.Layer(), 1); + EXPECT_EQ(shield.Layer(), 2); - EXPECT_EQ(hull.layer_index, 0); - EXPECT_EQ(armor.layer_index, 1); - EXPECT_EQ(shield.layer_index, 2); - - object.layers[0] = hull; - object.layers[1] = armor; - object.layers[2] = shield; + object.AddLayer(&hull); + object.AddLayer(&armor); + object.AddLayer(&shield); Damage damage; damage.normal_damage = 10; + damage.phase_damage = 0; InflictedDamage inflicted_damage = object.DealDamage(core_vector, damage); - EXPECT_EQ(object.layers[0].facets[0].health, 50); - EXPECT_EQ(object.layers[1].facets[0].health, 100); - EXPECT_EQ(object.layers[2].facets[0].health, 140); + EXPECT_EQ(object.layers[0]->Percent(0), 1.0); + EXPECT_EQ(object.layers[1]->Percent(0), 1.0); + EXPECT_EQ(object.layers[2]->Percent(0), 0.95); EXPECT_EQ(damage.normal_damage, 0); EXPECT_EQ(inflicted_damage.total_damage, 10); EXPECT_EQ(inflicted_damage.normal_damage, 10); diff --git a/engine/src/gfx/cockpit.cpp b/engine/src/gfx/cockpit.cpp index ce85c32d66..b76ec857b8 100644 --- a/engine/src/gfx/cockpit.cpp +++ b/engine/src/gfx/cockpit.cpp @@ -312,22 +312,7 @@ void GameCockpit::AutoLanding() { } -float GameCockpit::LookupTargetStat(int stat, Unit *target) { - switch (stat) { - case UnitImages::TARGETSHIELDF: - return target->FShieldData(); - - case UnitImages::TARGETSHIELDR: - return target->RShieldData(); - case UnitImages::TARGETSHIELDL: - return target->LShieldData(); - - case UnitImages::TARGETSHIELDB: - return target->BShieldData(); - } - return 1; -} float GameCockpit::LookupUnitStat(int stat, Unit *target) { static float game_speed = XMLSupport::parse_float(vs_config->getVariable("physics", "game_speed", "1")); @@ -347,50 +332,54 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { switch (stat) { case UnitImages::SHIELDF: - return target->FShieldData(); - - case UnitImages::SHIELDR: - return target->RShieldData(); + return target->shield.Percent(Shield::front); + + case UnitImages::SHIELDB: + return target->shield.Percent(Shield::back); case UnitImages::SHIELDL: - return target->LShieldData(); + return target->shield.Percent(Shield::left); - case UnitImages::SHIELDB: - return target->BShieldData(); + case UnitImages::SHIELDR: + return target->shield.Percent(Shield::right); case UnitImages::ARMORF: - return target->armor->facets[1].Percent(); - case UnitImages::ARMORR: - return target->armor->facets[0].Percent(); - case UnitImages::ARMORL: - return target->armor->facets[1].Percent(); + return target->armor.Percent(Armor::front); + case UnitImages::ARMORB: - return target->armor->facets[0].Percent(); + return target->armor.Percent(Armor::back); + + case UnitImages::ARMORL: + return target->armor.Percent(Armor::left); + + case UnitImages::ARMORR: + return target->armor.Percent(Armor::right); + case UnitImages::ARMOR4: - return target->armor->facets[2].Percent(); + return target->armor.Percent(Armor::left); case UnitImages::ARMOR5: - return target->armor->facets[2].Percent(); + return target->armor.Percent(Armor::left); case UnitImages::ARMOR6: - return target->armor->facets[3].Percent(); + return target->armor.Percent(Armor::right); case UnitImages::ARMOR7: - return target->armor->facets[3].Percent(); + return target->armor.Percent(Armor::right); case UnitImages::FUEL: return target->fuel.Percent(); case UnitImages::ENERGY: - return target->energyData(); + return target->energy.Percent(); case UnitImages::WARPENERGY: { const bool warpifnojump = configuration()->graphics_config.hud.display_warp_energy_if_no_jump_drive; return (warpifnojump || target->jump_drive.Installed()) ? target->ftl_energy.Percent() : 0; } case UnitImages::HULL: - return target->hull->facets[0].Percent(); + return target->hull.Percent(); case UnitImages::EJECT: { int go = - ((target->hull->facets[0].Percent() < .25) - && (target->BShieldData() < .25 || target->FShieldData() < .25)) ? 1 : 0; + ((target->hull.Percent() < .25) + && (target->shield.Percent() < .25)) ? 1 : 0; static int overload = 0; if (overload != go) { if (go == 0) { @@ -1866,7 +1855,7 @@ void GameCockpit::Draw() { ////////////////////////////////////////////////////////////////////////// } GFXColor4f(1, 1, 1, 1); - if (un->GetHull() >= 0) { + if (!un->Destroyed()) { die = false; } if (un->Threat() != NULL) { @@ -1934,7 +1923,7 @@ void GameCockpit::Draw() { static float dietime = 0; if (die) { if (un) { - if (un->GetHull() >= 0) { + if (!un->Destroyed()) { die = false; } } diff --git a/engine/src/gfx/cockpit.h b/engine/src/gfx/cockpit.h index 097c0ba0bc..b2d4591418 100644 --- a/engine/src/gfx/cockpit.h +++ b/engine/src/gfx/cockpit.h @@ -187,8 +187,7 @@ class GameCockpit : public Cockpit { void RestoreViewPort(); GameCockpit(const char *file, Unit *parent, const std::string &pilotname); ~GameCockpit(); - ///Looks up a particular Gauge stat on target unit - float LookupTargetStat(int stat, Unit *target); + ///Looks up a particular Gauge stat on unit float LookupUnitStat(int stat, Unit *target); ///Loads cockpit info...just as constructor diff --git a/engine/src/gfx/cockpit_generic.cpp b/engine/src/gfx/cockpit_generic.cpp index f72272ed6f..74c8e00238 100644 --- a/engine/src/gfx/cockpit_generic.cpp +++ b/engine/src/gfx/cockpit_generic.cpp @@ -214,7 +214,6 @@ Cockpit::Cockpit(const char *file, Unit *parent, const std::string &pilot_name) //int i; partial_number_of_attackers = -1; number_of_attackers = 0; - secondsWithZeroEnergy = 0; fg = NULL; jumpok = 0; TimeOfLastCollision = -200; @@ -500,30 +499,7 @@ bool Cockpit::Update() { } UpdAutoPilot(); Unit *par = GetParent(); - if (par != NULL) { - static float minEnergyForShieldDownpower = - XMLSupport::parse_float(vs_config->getVariable("physics", "shield_energy_downpower", "-.125")); - static float minEnergyShieldTime = - XMLSupport::parse_float(vs_config->getVariable("physics", "shield_energy_downpower_time", "5")); - static float minEnergyShieldPercent = - XMLSupport::parse_float(vs_config->getVariable("physics", - "shield_energy_downpower_percent", - ".66666666666666")); - - bool toolittleenergy = (par->energyData() <= minEnergyForShieldDownpower); - if (toolittleenergy) { - secondsWithZeroEnergy += SIMULATION_ATOM; - if (secondsWithZeroEnergy > minEnergyShieldTime) { - secondsWithZeroEnergy = 0; - - // TODO: lib_damage - // check the input is in the expected 0 to 1 values - par->GetShieldLayer().AdjustPower(minEnergyShieldPercent); - } - } else { - secondsWithZeroEnergy = 0; - } - } + if (turretcontrol.size() > _Universe->CurrentCockpit()) { if (turretcontrol[_Universe->CurrentCockpit()]) { turretcontrol[_Universe->CurrentCockpit()] = 0; diff --git a/engine/src/gfx/cockpit_generic.h b/engine/src/gfx/cockpit_generic.h index 33bedd062f..a108e86291 100644 --- a/engine/src/gfx/cockpit_generic.h +++ b/engine/src/gfx/cockpit_generic.h @@ -219,7 +219,6 @@ class Cockpit { std::vector sounds; public: - double secondsWithZeroEnergy; int number_of_attackers; unsigned int retry_dock; double TimeOfLastCollision; @@ -314,11 +313,6 @@ class Cockpit { Cockpit(const char *file, Unit *parent, const std::string &pilotname); virtual ~Cockpit(); - ///Looks up a particular Gauge stat on target unit - virtual float LookupTargetStat(int stat, Unit *target) { - return 1; - } - ///Loads cockpit info...just as constructor virtual void Init(const char *file, bool defaultCockpit = false); void recreate(const std::string &pilotname); diff --git a/engine/src/gfx/cockpit_gfx.cpp b/engine/src/gfx/cockpit_gfx.cpp index 5859de85be..7c9b81e2d6 100644 --- a/engine/src/gfx/cockpit_gfx.cpp +++ b/engine/src/gfx/cockpit_gfx.cpp @@ -893,21 +893,24 @@ void DrawTargetGauges( Unit *target, Gauge *gauges[] ) } -float LookupTargetStat( int stat, Unit *target ) -{ - switch (stat) - { - case UnitImages< void >::TARGETSHIELDF: - return target->FShieldData(); +float LookupTargetStat( int stat, Unit *target ) { + if(!target) { + return 0; + } - case UnitImages< void >::TARGETSHIELDR: - return target->RShieldData(); + switch (stat) { + case UnitImages< void >::TARGETSHIELDF: + return target->shield.Percent(Shield::front); + case UnitImages< void >::TARGETSHIELDB: + return target->shield.Percent(Shield::back); + case UnitImages< void >::TARGETSHIELDL: - return target->LShieldData(); + return target->shield.Percent(Shield::left); - case UnitImages< void >::TARGETSHIELDB: - return target->BShieldData(); + case UnitImages< void >::TARGETSHIELDR: + return target->shield.Percent(Shield::right); } - return 1; + + return 0; } diff --git a/engine/src/gfx/vdu.cpp b/engine/src/gfx/vdu.cpp index ea332af917..d0defae76d 100644 --- a/engine/src/gfx/vdu.cpp +++ b/engine/src/gfx/vdu.cpp @@ -498,16 +498,18 @@ void VDU::DrawVDUShield(Unit *parent) { h = fabs(h * .6); w = fabs(w * .6); + + double hull_percent = parent->hull.Percent(); //static bool invert_friendly_shields = // XMLSupport::parse_bool( vs_config->getVariable( "graphics", "hud", "invert_friendly_shields", "false" ) ); //DrawShieldArmor(parent,StartArmor,x,y,w,h,invert_friendly_shields); - GFXColor4f(1, parent->GetHullPercent(), parent->GetHullPercent(), 1); + GFXColor4f(1, hull_percent, hull_percent, 1); GFXEnable(TEXTURE0); - GFXColor4f(1, parent->GetHullPercent(), parent->GetHullPercent(), 1); + GFXColor4f(1, hull_percent, hull_percent, 1); const bool invert_friendly_sprite = configuration()->graphics_config.hud.invert_friendly_sprite; - DrawHUDSprite(this, parent->getHudImage(), .25, x, y, w, h, parent->GetHullPercent(), - parent->GetHullPercent(), parent->GetHullPercent(), parent->GetHullPercent(), - parent->GetHullPercent(), true, invert_friendly_sprite); + DrawHUDSprite(this, parent->getHudImage(), .25, x, y, w, h, hull_percent, + hull_percent, hull_percent, hull_percent, + hull_percent, true, invert_friendly_sprite); } // TODO: make into function @@ -633,12 +635,12 @@ void VDU::DrawTarget(GameCockpit *cp, Unit *parent, Unit *target) { GFXEnable(TEXTURE0); const bool invert_target_sprite = configuration()->graphics_config.hud.invert_target_sprite; - float armor_up = target->armor->facets[0].Percent(); - float armor_down = target->armor->facets[1].Percent(); - float armor_left = target->armor->facets[2].Percent(); - float armor_right = target->armor->facets[3].Percent(); + float armor_up = target->armor.Percent(Armor::front); + float armor_down = target->armor.Percent(Armor::back); + float armor_left = target->armor.Percent(Armor::right); + float armor_right = target->armor.Percent(Armor::left); if (target->isUnit() == Vega_UnitType::planet) { - armor_up = armor_down = armor_left = armor_right = target->GetHullPercent(); + armor_up = armor_down = armor_left = armor_right = target->hull.Percent(); } DrawHUDSprite(this, @@ -659,7 +661,7 @@ void VDU::DrawTarget(GameCockpit *cp, Unit *parent, Unit *target) { armor_right, armor_left, armor_down, - target->GetHullPercent(), + target->hull.Percent(), true, invert_target_sprite); @@ -740,10 +742,10 @@ void VDU::DrawTarget(GameCockpit *cp, Unit *parent, Unit *target) { static bool builtin_shields = XMLSupport::parse_bool(vs_config->getVariable("graphics", "vdu_builtin_shields", "false")); if (builtin_shields) { - DrawShield(target->shield->facets[0].Percent(), - target->shield->facets[3].Percent(), - target->shield->facets[2].Percent(), - target->shield->facets[1].Percent(), x, y, w, h, false, + DrawShield(target->shield.Percent(Shield::front), + target->shield.Percent(Shield::right), + target->shield.Percent(Shield::left), + target->shield.Percent(Shield::back), x, y, w, h, false, GFXColor(ishieldcolor[0], ishieldcolor[1], ishieldcolor[2], ishieldcolor[3]), GFXColor(mshieldcolor[0], mshieldcolor[1], mshieldcolor[2], mshieldcolor[3]), GFXColor(oshieldcolor[0], oshieldcolor[1], oshieldcolor[2], oshieldcolor[3])); @@ -1185,18 +1187,16 @@ void VDU::DrawDamage(Unit *parent) { float x, y, w, h; //float th; //char st[1024]; - double health_percent = parent->layers[0].facets[0].Percent(); - GFXColor4f(1, health_percent, health_percent, 1); + double hull_percent = parent->hull.Percent(); + GFXColor4f(1, hull_percent, hull_percent, 1); GFXEnable(TEXTURE0); - float armor[8]; - parent->ArmorData(armor); const bool draw_damage_sprite = configuration()->graphics_config.hud.draw_damage_sprite; DrawHUDSprite(this, draw_damage_sprite ? parent->getHudImage() : nullptr, .6, x, y, w, h, - parent->layers[1].facets[0].Percent(), - parent->layers[1].facets[3].Percent(), - parent->layers[1].facets[2].Percent(), - parent->layers[1].facets[1].Percent(), - health_percent, true, false); + parent->armor.Percent(Armor::front), + parent->armor.Percent(Armor::right), + parent->armor.Percent(Armor::left), + parent->armor.Percent(Armor::back), + hull_percent, true, false); GFXDisable(TEXTURE0); //Unit *thr = parent->Threat(); parent->Threat(); diff --git a/engine/src/python/define_odd_unit_functions.h b/engine/src/python/define_odd_unit_functions.h index f14449c5cf..5aa4d4ac9b 100644 --- a/engine/src/python/define_odd_unit_functions.h +++ b/engine/src/python/define_odd_unit_functions.h @@ -44,4 +44,14 @@ void DefineOddUnitFunctions(boost::python::class_builder &Class) { PYTHON_DEFINE_METHOD(Class, &UnitWrapper::GetJumpStatus, "GetJumpStatus"); PYTHON_DEFINE_METHOD(Class, &UnitWrapper::ApplyDamage, "ApplyDamage"); PYTHON_DEFINE_METHOD(Class, &UnitWrapper::GetMountInfo, "GetMountInfo"); + + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::FShieldData, "FShieldData"); + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::BShieldData, "BShieldData"); + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::LShieldData, "LShieldData"); + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::RShieldData, "RShieldData"); + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::fuelData, "fuelData"); + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::energyData, "energyData"); + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::GetHull, "GetHull"); + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::GetHullPercent, "GetHullPercent"); + PYTHON_DEFINE_METHOD(Class, &UnitWrapper::rSize, "rSize"); } diff --git a/engine/src/python/python_unit_wrap.h b/engine/src/python/python_unit_wrap.h index f5b60bca31..8e6d9e578c 100644 --- a/engine/src/python/python_unit_wrap.h +++ b/engine/src/python/python_unit_wrap.h @@ -111,24 +111,7 @@ unsigned int, Missile, bool, OnlyCollideWithTarget ) voidWRAPPED0( UnFire ) WRAPPED0(float, computeLockingPercent, 0 ) //how locked are we -WRAPPED0(float, FShieldData, -0 ) -WRAPPED0(float, RShieldData, -0 ) -WRAPPED0(float, LShieldData, -0 ) -WRAPPED0(float, BShieldData, -0 ) -WRAPPED0(float, fuelData, -0 ) -WRAPPED0(float, energyData, -0 ) -WRAPPED0(float, GetHull, -0 ) -WRAPPED0(float, GetHullPercent, -0 ) -WRAPPED0(float, rSize, -0 ) + #endif // ((WHICH_UNIT_EXPORT_NUMBER == 3) || (WHICH_UNIT_EXPORT_NUMBER == 2)) #if (WHICH_UNIT_EXPORT_NUMBER == 1) || (WHICH_UNIT_EXPORT_NUMBER == 3) diff --git a/engine/src/python/unit_wrapper_class.h b/engine/src/python/unit_wrapper_class.h index e6f3d990d7..fa5dcfaa59 100644 --- a/engine/src/python/unit_wrapper_class.h +++ b/engine/src/python/unit_wrapper_class.h @@ -298,6 +298,53 @@ class UnitWrapper : public UnitContainer { Damage damage(amt); unit->ApplyDamage(pnt, normal, damage, unit, GFXColor(r, g, b, a), dealer.GetUnit()); } + + double FShieldData() { + {CHECKME 0.0;} + return me->shield.Percent(Shield::front); + } + + double BShieldData() { + {CHECKME 0.0;} + return me->shield.Percent(Shield::back); + } + + double LShieldData() { + {CHECKME 0.0;} + return me->shield.Percent(Shield::left); + } + + double RShieldData() { + {CHECKME 0.0;} + return me->shield.Percent(Shield::right); + } + + float fuelData() { + {CHECKME 0.0;} + return me->fuel.Level(); + } + + float energyData() { + {CHECKME 0.0;} + return me->energy.Percent(); + } + + float GetHull() { + {CHECKME 0.0;} + return me->hull.Get(); + } + + float GetHullPercent() { + {CHECKME 0.0;} + return me->hull.Percent(); + } + + float rSize() { + {CHECKME 0.0;} + return me->rSize(); + } + + /////////////////////////////MANUAL WRAP////// /* * WRAPPED1(bool,TransferUnitToSystem,class StarSystem *,NewSystem,false) diff --git a/engine/src/star_system.cpp b/engine/src/star_system.cpp index 0dfa1459c2..2353fe1376 100644 --- a/engine/src/star_system.cpp +++ b/engine/src/star_system.cpp @@ -1284,9 +1284,8 @@ void StarSystem::ProcessPendingJumps() { un->SetVelocity(Vector(0, 0, 0)); } if (game_options()->jump_disables_shields) { - // Disable and then enable so they'll start recharging - un->shield->Disable(); - un->shield->Enable(); + // Zero shield. They'll start recharging from zero. + un->shield.Zero(); } } } diff --git a/engine/src/universe.cpp b/engine/src/universe.cpp index 9ec562448c..25f1e82fad 100644 --- a/engine/src/universe.cpp +++ b/engine/src/universe.cpp @@ -281,19 +281,17 @@ void InitUnitTables() { VSFileSystem::VSError err = jsonFile.OpenReadOnly("units.json", VSFileSystem::UnitFile); if (err <= VSFileSystem::Ok) { UnitJSONFactory::ParseJSON(jsonFile); + jsonFile.Close(); } - jsonFile.Close(); // Really New Init VSFileSystem::VSFile newJsonFile; err = newJsonFile.OpenReadOnly("ships.json", VSFileSystem::UnitFile); if (err <= VSFileSystem::Ok) { UnitOptimizeFactory::ParseJSON(newJsonFile); - + newJsonFile.Close(); } - - newJsonFile.Close(); } void CleanupUnitTables() {