From 270108309091e810f9eff96edd4d27d2aa98c0ae Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Mon, 23 Jan 2023 20:01:12 +0200 Subject: [PATCH 01/16] Fix bug - Store::Subtract adds quantity instead of subtracting it closes #751 Also add tests and a few additional methods to store. --- engine/src/resource/store.cpp | 24 +++++++++++++++++++++--- engine/src/resource/store.h | 4 ++++ engine/src/resource/tests/buy_sell.cpp | 6 ++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/engine/src/resource/store.cpp b/engine/src/resource/store.cpp index eb1f7f1ccc..0493f3920d 100644 --- a/engine/src/resource/store.cpp +++ b/engine/src/resource/store.cpp @@ -41,7 +41,7 @@ void Store::Add(int index, int quantity) { } void Store::Subtract(int index, int quantity) { - stock[index].quantity += quantity; + stock[index].quantity -= quantity; } @@ -90,14 +90,32 @@ bool Store::Buy(Customer& seller, std::string product_name, double quantity) bool Store::InStock(std::string product_name) { for(Product &in_store_product : stock) { - if(in_store_product == product_name && in_store_product.quantity > 0.0) { - return true; + if(in_store_product == product_name) { + return (in_store_product.quantity > 0.0); } } return false; } +double Store::GetStock(std::string product_name) { + for(Product &in_store_product : stock) { + if(in_store_product == product_name ) { + return true; + } + } + + return -1; +} + +bool Store::InStock(const int index) { + return (stock[index].quantity > 0.0); +} + +double Store::GetStock(const int index) { + return stock[index].quantity; +} + int Store::ProductIndex(std::string product_name) { int index = 0; diff --git a/engine/src/resource/store.h b/engine/src/resource/store.h index ab4cd08b2c..29806a38f7 100644 --- a/engine/src/resource/store.h +++ b/engine/src/resource/store.h @@ -49,6 +49,10 @@ class Store void Subtract(int index, int quantity); bool InStock(std::string product_name); + double GetStock(std::string product_name); + bool InStock(const int index); + double GetStock(const int index); + int ProductIndex(std::string product_name); // These are from the point of view of the store/called class and also affect the other party diff --git a/engine/src/resource/tests/buy_sell.cpp b/engine/src/resource/tests/buy_sell.cpp index 7a9f8fe487..552be83a4c 100644 --- a/engine/src/resource/tests/buy_sell.cpp +++ b/engine/src/resource/tests/buy_sell.cpp @@ -13,6 +13,12 @@ TEST(Store, Sanity) { std::vector stock = { bread, milk, cigarettes, gold_bar }; Store store(stock, 1000); + store.Add(0,10); + EXPECT_EQ(store.GetStock(0), 11); + + store.Subtract(0,2); + EXPECT_EQ(store.GetStock(0), 9); + Customer customer; std::vector inventory = {used_car}; customer.Stock(inventory); From 86686836c93525ec16219235de9d62624d6f80d9 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Thu, 31 Aug 2023 11:03:18 +0300 Subject: [PATCH 02/16] Refactor cloaking in the game --- engine/CMakeLists.txt | 1 + engine/src/cmd/ai/fire.cpp | 4 +- engine/src/cmd/ai/firekeyboard.cpp | 4 +- engine/src/cmd/ai/firekeyboard.h | 1 - engine/src/cmd/ai/tactics.cpp | 6 +- engine/src/cmd/armed.cpp | 2 +- engine/src/cmd/basecomputer.cpp | 6 +- engine/src/cmd/briefing.cpp | 6 +- engine/src/cmd/cloak.cpp | 135 +++++++++++++++++ engine/src/cmd/cloak.h | 143 +++++++++++++++++ engine/src/cmd/cont_terrain.cpp | 3 +- engine/src/cmd/damageable.cpp | 5 +- engine/src/cmd/drawable.cpp | 48 +++--- engine/src/cmd/drawable.h | 6 +- engine/src/cmd/jump_capable.cpp | 2 +- engine/src/cmd/unit_csv.cpp | 34 +---- engine/src/cmd/unit_generic.cpp | 219 +++++---------------------- engine/src/cmd/unit_generic.h | 28 +--- engine/src/gfx/cockpit.cpp | 6 +- engine/src/gfx/halo_system.cpp | 4 +- engine/src/gfx/mesh.cpp | 2 +- engine/src/gfx/mesh.h | 14 +- engine/src/gfx/mesh_gfx.cpp | 54 +++---- engine/src/gfx/nav/navscreen.cpp | 4 +- engine/src/python/python_unit_wrap.h | 2 +- 25 files changed, 417 insertions(+), 322 deletions(-) create mode 100644 engine/src/cmd/cloak.cpp create mode 100644 engine/src/cmd/cloak.h diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 4b9e6aca1e..5e1040760e 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -827,6 +827,7 @@ SET(LIBCMD_SOURCES src/cmd/intelligent.cpp src/cmd/energetic.cpp + src/cmd/cloak.cpp src/cmd/planetary_orbit.cpp diff --git a/engine/src/cmd/ai/fire.cpp b/engine/src/cmd/ai/fire.cpp index 095ce08610..3c6d1b8769 100644 --- a/engine/src/cmd/ai/fire.cpp +++ b/engine/src/cmd/ai/fire.cpp @@ -430,7 +430,7 @@ class ChooseTargetClass { } bool ShouldTargetUnit(Unit *un, float distance) { - if (un->CloakVisible() > .8) { + if (un->cloak.Visible()) { float rangetotarget = distance; float rel0 = parent->getRelation(un); float rel[] = { @@ -841,7 +841,7 @@ void FireAt::Execute() { bool istargetjumpableplanet = false; if ((targ = parent->Target())) { istargetjumpableplanet = isJumpablePlanet(targ); - if (targ->CloakVisible() > .8 && !targ->Destroyed()) { + if (targ->cloak.Visible() && !targ->Destroyed()) { had_target = true; if (parent->getNumMounts() > 0) { if (!istargetjumpableplanet) { diff --git a/engine/src/cmd/ai/firekeyboard.cpp b/engine/src/cmd/ai/firekeyboard.cpp index 776aaf1e35..3f03e307d3 100644 --- a/engine/src/cmd/ai/firekeyboard.cpp +++ b/engine/src/cmd/ai/firekeyboard.cpp @@ -63,7 +63,6 @@ extern bool toggle_pause(); FireKeyboard::FireKeyboard(unsigned int whichplayer, unsigned int whichjoystick) : Order(WEAPON, 0) { memset(savedTargets, 0, sizeof(void *) * NUMSAVEDTARGETS); this->autotrackingtoggle = 1; - this->cloaktoggle = true; this->whichjoystick = whichjoystick; this->whichplayer = whichplayer; gunspeed = gunrange = .0001; @@ -1779,8 +1778,7 @@ void FireKeyboard::Execute() { } if (f().cloakkey == PRESS) { f().cloakkey = DOWN; - parent->Cloak(cloaktoggle); - cloaktoggle = !cloaktoggle; + parent->cloak.Toggle(); } if (f().lockkey == PRESS) { f().lockkey = DOWN; diff --git a/engine/src/cmd/ai/firekeyboard.h b/engine/src/cmd/ai/firekeyboard.h index 94c1431cbc..3cc6ca2cff 100644 --- a/engine/src/cmd/ai/firekeyboard.h +++ b/engine/src/cmd/ai/firekeyboard.h @@ -34,7 +34,6 @@ #define NUMSAVEDTARGETS 10 class FireKeyboard : public Order { bool itts; - bool cloaktoggle; bool refresh_target; float gunspeed; float gunrange; diff --git a/engine/src/cmd/ai/tactics.cpp b/engine/src/cmd/ai/tactics.cpp index adcbcaa9cd..86884d90be 100644 --- a/engine/src/cmd/ai/tactics.cpp +++ b/engine/src/cmd/ai/tactics.cpp @@ -31,13 +31,13 @@ void CloakFor::Execute() { if (time == 0) { - parent->Cloak(enable); + parent->cloak.Activate(); } time += SIMULATION_ATOM; if (time > maxtime) { done = true; if (maxtime != 0) { - parent->Cloak(!enable); + parent->cloak.Deactivate(); } return; } @@ -48,7 +48,7 @@ CloakFor::~CloakFor() { VS_LOG_AND_FLUSH(trace, (boost::format("clk%1$x") % this)); #endif if (parent && time <= maxtime) { - parent->Cloak(!enable); + parent->cloak.Deactivate(); } } diff --git a/engine/src/cmd/armed.cpp b/engine/src/cmd/armed.cpp index 25fa01bbc3..40f99bb2b6 100644 --- a/engine/src/cmd/armed.cpp +++ b/engine/src/cmd/armed.cpp @@ -199,7 +199,7 @@ void Armed::ActivateGuns(const WeaponInfo *sz, bool ms) { void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { Unit *unit = static_cast(this); - if ((unit->cloaking >= 0 && !configuration()->weapons.can_fire_in_cloak) || + if ((unit->cloak.Active() && !configuration()->weapons.can_fire_in_cloak) || (unit->graphicOptions.InWarp && !configuration()->weapons.can_fire_in_spec)) { UnFire(); return; diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index e97d524dc8..63f243cf58 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -5565,17 +5565,17 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } //cloaking device? If we don't have one, no need to mention it ever exists, right? - if (playerUnit->cloaking != -1) { + if (playerUnit->cloak.Capable()) { if (!mode) { PRETTY_ADDU(statcolor + "Cloaking device available, energy usage: #-c", - playerUnit->cloakenergy * RSconverter * Wconv, + playerUnit->cloak.Energy() * RSconverter * Wconv, 0, "MJ/s"); } else { switch (replacement_mode) { case 0: //Replacement or new Module PRETTY_ADDU(statcolor + "Installs a cloaking device.#n# Activated energy usage: #-c", - playerUnit->cloakenergy * RSconverter * Wconv, + playerUnit->cloak.Energy() * RSconverter * Wconv, 0, "MJ/s"); break; diff --git a/engine/src/cmd/briefing.cpp b/engine/src/cmd/briefing.cpp index 436c5d733e..537368fead 100644 --- a/engine/src/cmd/briefing.cpp +++ b/engine/src/cmd/briefing.cpp @@ -116,11 +116,7 @@ void Briefing::Ship::Render(const Matrix &cam, double interpol) { Matrix camfinal; MultMatrix(camfinal, cam, final); for (unsigned int i = 0; i < meshdata.size(); i++) { - int scloak = int(cloak * ((-1) > 1)); //FIXME short fix? - if ((scloak & 0x1) == 0) { - scloak += 1; - } - meshdata[i]->Draw(1, camfinal, 1, cloak > .99 ? -1 : scloak); + meshdata[i]->Draw(1, camfinal, 1); } } diff --git a/engine/src/cmd/cloak.cpp b/engine/src/cmd/cloak.cpp new file mode 100644 index 0000000000..127969770f --- /dev/null +++ b/engine/src/cmd/cloak.cpp @@ -0,0 +1,135 @@ +/* + * cloak.cpp + * + * Copyright (C) 2001-2023 Daniel Horn, Benjaman 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 "cloak.h" +#include "unit_csv_factory.h" +#include "vegastrike.h" +#include "configuration/configuration.h" + +Cloak::Cloak() +{ + status = CloakingStatus::disabled; + + energy = 0; + rate = 100; + glass = false; + current = 0; + minimum = 0; +} + +Cloak::Cloak(std::string unit_key) +{ + if(UnitCSVFactory::GetVariable(unit_key, "Can_Cloak", false)) { + status = CloakingStatus::ready; + } else { + status = CloakingStatus::disabled; + } + + glass = UnitCSVFactory::GetVariable(unit_key, "Cloak_Glass", false); + rate = UnitCSVFactory::GetVariable(unit_key, "Cloak_Rate", 0.0); + energy = UnitCSVFactory::GetVariable(unit_key, "Cloak_Energy", 0.0); + minimum = UnitCSVFactory::GetVariable(unit_key, "Cloak_Min", 0.0); + minimum = std::min(1.0, std::max(0.0, minimum)); + current = 0; +} + +void Cloak::Update(Energetic *energetic) +{ + // Unit is not capable of cloaking or damaged or just not cloaking + if(status == CloakingStatus::disabled || + status == CloakingStatus::damaged || + status == CloakingStatus::ready) { + return; + } + + // Use warp power for cloaking (SPEC capacitor) + const static bool warp_energy_for_cloak = configuration()->warp_config.use_warp_energy_for_cloak; + double available_energy = warp_energy_for_cloak ? energetic->warpenergy : energetic->energy.Value(); + + + // Insufficient energy to cloak ship + if(available_energy < this->energy) { + status = CloakingStatus::decloaking; + } else { + // Subtract the energy used + if (warp_energy_for_cloak) { + energetic->warpenergy -= (simulation_atom_var * energy); + } else { + energetic->energy -= (simulation_atom_var * energy); + } + } + + if(status == CloakingStatus::decloaking) { + current = std::max(0.0, current - rate * simulation_atom_var); + + if(current == 0) { + status = CloakingStatus::ready; + } + } + + if(status == CloakingStatus::cloaking) { + current = std::min(1.0, current + rate * simulation_atom_var); + + if(current > minimum) { + status = CloakingStatus::cloaked; + } + } + + +} + +void Cloak::Toggle() { + // Unit is not capable of cloaking or damaged + if(status == CloakingStatus::disabled || + status == CloakingStatus::damaged) { + return; + } + + // If we're ready start cloaking + if(status == CloakingStatus::ready) { + status = CloakingStatus::cloaking; + return; + } + + // In any other case, start decloaking + status = CloakingStatus::decloaking; +} + +void Cloak::Activate() { + if(status == CloakingStatus::ready) { + status = CloakingStatus::cloaking; + } +} + +void Cloak::Deactivate() { + // Unit is not capable of cloaking or damaged or just not cloaking + if(status == CloakingStatus::disabled || + status == CloakingStatus::damaged || + status == CloakingStatus::ready) { + return; + } + + // Start decloaking + status = CloakingStatus::decloaking; +} diff --git a/engine/src/cmd/cloak.h b/engine/src/cmd/cloak.h new file mode 100644 index 0000000000..7a708a40f5 --- /dev/null +++ b/engine/src/cmd/cloak.h @@ -0,0 +1,143 @@ +/* + * cloak.h + * + * Copyright (C) 2001-2023 Daniel Horn, Benjaman 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 CLOAK_H +#define CLOAK_H + +#include + +#include "energetic.h" +#include "damageable_layer.h" + +enum class CloakingStatus { + disabled, + damaged, + ready, + cloaking, + cloaked, + decloaking +}; + +class Cloak +{ + friend class Unit; + + CloakingStatus status; + + // How much energy cloaking takes per frame + double energy; + + // How fast does this starship cloak/decloak + double rate; + + // If this unit cloaks like glass or like fading. + // Glass is alpha only. Non glass affects rgb as well. + bool glass; + + // Current cloak value. 0 is uncloaked. 1 is fully cloaked. + double current; + + // The minimum cloaking value... + double minimum; + +public: + Cloak(); + Cloak(std::string unit_key); + + void Update(Energetic *energetic); + void Toggle(); // Toggle cloak on/off + + bool Capable() const { + return (status != CloakingStatus::disabled); + } + + bool Cloaking() { + return (status == CloakingStatus::cloaking); + } + + bool Cloaked() { + return (status == CloakingStatus::cloaked); + } + + // Active is cloaking, cloaked or decloaking + bool Active() { + return (status == CloakingStatus::cloaking || + status == CloakingStatus::cloaked || + status == CloakingStatus::decloaking); + } + + bool Damaged() { + return (status == CloakingStatus::damaged); + } + + bool Ready() { + return (status == CloakingStatus::ready); + } + + bool Glass() { + return glass; + } + + double Current() { + return current; + } + + double Energy() { + return energy; + } + + //how visible the ship is from 0 to 1 + double Visible() const { + return 1-current; + } + + // TODO: more granular damage + // TODO: damageable component + void Damage() { + status = CloakingStatus::damaged; + current = 0; + } + + void Repair() { + if(status == CloakingStatus::damaged) { + status = CloakingStatus::ready; + current = 0; + } + } + + void Disable() { + status = CloakingStatus::disabled; + current = 0; + } + + void Enable() { + status = CloakingStatus::ready; + current = 0; + } + + void Activate(); + void Deactivate(); +}; + +#endif // CLOAK_H diff --git a/engine/src/cmd/cont_terrain.cpp b/engine/src/cmd/cont_terrain.cpp index d7a15c9393..fcac14c502 100644 --- a/engine/src/cmd/cont_terrain.cpp +++ b/engine/src/cmd/cont_terrain.cpp @@ -287,7 +287,8 @@ void ContinuousTerrain::Draw() { md[i].mesh->rSize() ); if (d) { - md[i].mesh->Draw(1000, md[i].mat, d, -1, (_Universe->AccessCamera()->GetNebula() != NULL) ? -1 : 0); + static Cloak dummy_cloak; + md[i].mesh->Draw(1000, md[i].mat, d, dummy_cloak, (_Universe->AccessCamera()->GetNebula() != NULL) ? -1 : 0); } } } diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index 8ab291bf7a..7847c00040 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -565,8 +565,9 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi } - // Discharge shields due to energy or SPEC - if ((in_warp && !shields_in_spec) || !unit->sufficient_energy_to_recharge_shields) { + // 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 diff --git a/engine/src/cmd/drawable.cpp b/engine/src/cmd/drawable.cpp index a4095163a5..0c357a37ea 100644 --- a/engine/src/cmd/drawable.cpp +++ b/engine/src/cmd/drawable.cpp @@ -204,15 +204,10 @@ void Drawable::Draw(const Transformation &parent, const Matrix &parentMatrix) { bool On_Screen = false; bool Unit_On_Screen = false; float Apparent_Size = 0.0f; - int cloak = unit->cloaking; Matrix wmat; if (!cam_setup_phase) { // Following stuff is only needed in actual drawing phase - if (unit->cloaking > unit->cloakmin) { - cloak = (int) (unit->cloaking - interpolation_blend_factor * unit->cloakrate * simulation_atom_var); - cloak = cloakVal(cloak, unit->cloakmin, unit->cloakrate, unit->cloakglass); - } if ((*unit->current_hull) < (*unit->max_hull)) { damagelevel = (*unit->current_hull) / (*unit->max_hull); chardamage = (255 - (unsigned char) (damagelevel * 255)); @@ -272,10 +267,9 @@ void Drawable::Draw(const Transformation &parent, const Matrix &parentMatrix) { if (frustd) { //if the radius is at least half a pixel at detail 1 (equivalent to pixradius >= 0.5 / detail) float currentFrame = meshdata[i]->getCurrentFrame(); - this->meshdata[i]->Draw(lod, wmat, d, - i == this->meshdata.size() - 1 ? -1 : cloak, + this->meshdata[i]->Draw(lod, wmat, d, unit->cloak, (camera->GetNebula() == unit->nebula && unit->nebula != NULL) ? -1 : 0, - chardamage); //cloakign and nebula + chardamage); //cloaking and nebula On_Screen = true; unsigned int numAnimFrames = 0; static const string default_animation; @@ -349,8 +343,8 @@ void Drawable::Draw(const Transformation &parent, const Matrix &parentMatrix) { return; } - DrawSubunits(On_Screen, wmat, cloak, avgscale, chardamage); - DrawHalo(On_Screen, Apparent_Size, wmat, cloak); + DrawSubunits(On_Screen, wmat, unit->cloak, avgscale, chardamage); + DrawHalo(On_Screen, Apparent_Size, wmat, unit->cloak); Sparkle(On_Screen, ctm); } @@ -412,10 +406,7 @@ void Drawable::DrawNow(const Matrix &mato, float lod) { pos = mato.p; VectorAndPositionToMatrix(mat, p, q, r, pos); } - int cloak = unit->cloaking; - if (unit->cloaking > unit->cloakmin) { - cloak = cloakVal(cloak, unit->cloakmin, unit->cloakrate, unit->cloakglass); - } + for (i = 0; i <= this->nummesh(); i++) { //NOTE LESS THAN OR EQUALS...to cover shield mesh if (this->meshdata[i] == NULL) { @@ -426,7 +417,7 @@ void Drawable::DrawNow(const Matrix &mato, float lod) { float d = GFXSphereInFrustum(TransformedPosition, this->meshdata[i]->clipRadialSize() * vlpqrScaleFactor); if (d) { //d can be used for level of detail //this->meshdata[i]->DrawNow(lod,false,mat,cloak);//cloakign and nebula - this->meshdata[i]->Draw(lod, mat, d, cloak); + this->meshdata[i]->Draw(lod, mat, d, unit->cloak); } } Unit *un; @@ -468,22 +459,20 @@ void Drawable::DrawNow(const Matrix &mato, float lod) { (d - gun->rSize() < g_game.znear) ? g_game.znear : d - gun->rSize()); ScaleMatrix(mmat, Vector(mahnt->xyscale, mahnt->xyscale, mahnt->zscale)); gun->setCurrentFrame(unit->mounts[i].ComputeAnimatedFrame(gun)); - gun->Draw(lod, mmat, d, cloak, - (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula != NULL) ? -1 + gun->Draw(lod, mmat, d, unit->cloak, (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula != NULL) ? -1 : 0, chardamage, true); //cloakign and nebula if (mahnt->type->gun1) { gun = mahnt->type->gun1; gun->setCurrentFrame(unit->mounts[i].ComputeAnimatedFrame(gun)); - gun->Draw(lod, - mmat, - d, - cloak, - (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula + gun->Draw(lod, mmat, + d, + unit->cloak, + (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula != NULL) ? -1 : 0, - chardamage, - true); //cloakign and nebula + chardamage, + true); //cloakign and nebula } } } @@ -497,7 +486,7 @@ void Drawable::DrawNow(const Matrix &mato, float lod) { if (!(unit->docked & (unit->DOCKED | unit->DOCKED_INSIDE))) { halos->Draw(mat, Scale, - cloak, + unit->cloak.Visible(), 0, unit->GetHullPercent(), velocity, @@ -707,7 +696,7 @@ void Drawable::Sparkle(bool on_screen, Matrix *ctm) { } } -void Drawable::DrawHalo(bool on_screen, float apparent_size, Matrix wmat, int cloak) { +void Drawable::DrawHalo(bool on_screen, float apparent_size, Matrix wmat, Cloak cloak) { Unit *unit = vega_dynamic_cast_ptr(this); // Units not shown don't emit a halo @@ -746,12 +735,12 @@ void Drawable::DrawHalo(bool on_screen, float apparent_size, Matrix wmat, int cl //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; - halos->Draw(wmat, Scale, cloak, nebd, hulld, velocity, + halos->Draw(wmat, Scale, unit->cloak.Visible(), nebd, hulld, velocity, linaccel, angaccel, maxaccel, cmas, unit->faction); } -void Drawable::DrawSubunits(bool on_screen, Matrix wmat, int cloak, float average_scale, unsigned char char_damage) { +void Drawable::DrawSubunits(bool on_screen, Matrix wmat, Cloak cloak, float average_scale, unsigned char char_damage) { Unit *unit = vega_dynamic_cast_ptr(this); Transformation *ct = &unit->cumulative_transformation; @@ -811,8 +800,7 @@ void Drawable::DrawSubunits(bool on_screen, Matrix wmat, int cloak, float averag if (lod > 0.5 && pixradius > 2.5) { ScaleMatrix(mat, Vector(mount->xyscale, mount->xyscale, mount->zscale)); gun->setCurrentFrame(unit->mounts[i].ComputeAnimatedFrame(gun)); - gun->Draw(lod, mat, d, cloak, - (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula != NULL) ? -1 : 0, + gun->Draw(lod, mat, d, cloak, (_Universe->AccessCamera()->GetNebula() == unit->nebula && unit->nebula != NULL) ? -1 : 0, char_damage, true); //cloakign and nebula } diff --git a/engine/src/cmd/drawable.h b/engine/src/cmd/drawable.h index 119305b1a8..dc0db5d7ee 100644 --- a/engine/src/cmd/drawable.h +++ b/engine/src/cmd/drawable.h @@ -32,6 +32,8 @@ #include #include +#include "cloak.h" + class Mesh; class Flightgroup; class Unit; @@ -134,8 +136,8 @@ class Drawable { virtual std::string drawableGetName() = 0; void Sparkle(bool on_screen, Matrix *ctm); - void DrawHalo(bool on_screen, float apparent_size, Matrix wmat, int cloak); - void DrawSubunits(bool on_screen, Matrix wmat, int cloak, float average_scale, unsigned char char_damage); + void DrawHalo(bool on_screen, float apparent_size, Matrix wmat, Cloak cloak); + void DrawSubunits(bool on_screen, Matrix wmat, Cloak cloak, float average_scale, unsigned char char_damage); //Split this mesh with into 2^level submeshes at arbitrary planes void Split(int level); diff --git a/engine/src/cmd/jump_capable.cpp b/engine/src/cmd/jump_capable.cpp index d8930247d6..571b470fb7 100644 --- a/engine/src/cmd/jump_capable.cpp +++ b/engine/src/cmd/jump_capable.cpp @@ -212,7 +212,7 @@ bool JumpCapable::AutoPilotToErrorMessage(const Unit *target, XMLSupport::parse_bool(vs_config->getVariable("physics", "teleport_autopilot", "true")); bool unsafe = false; if ((!teleport_autopilot) && (!nanspace)) { - if (Guaranteed == Mission::AUTO_NORMAL && unit->CloakVisible() > .5) { + if (Guaranteed == Mission::AUTO_NORMAL && unit->cloak.Cloaked()) { bool ignore_friendlies = true; for (un_iter i = ss->getUnitList().createIterator(); (un = *i) != NULL; ++i) { static bool canflythruplanets = diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index 91d28caa76..7c1d8bd806 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -922,28 +922,8 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ computer.radar.trackingcone = cos(UnitCSVFactory::GetVariable(unit_key, "Tracking_Cone", 180.0f) * VS_PI / 180); computer.radar.lockcone = cos(UnitCSVFactory::GetVariable(unit_key, "Lock_Cone", 180.0f) * VS_PI / 180); - cloakmin = static_cast(UnitCSVFactory::GetVariable(unit_key, "Cloak_Min", 0.0f) * 2147483136); - if (cloakmin < 0) { - cloakmin = 0; - } - - cloakglass = UnitCSVFactory::GetVariable(unit_key, "Cloak_Glass", false); - if ((cloakmin & 0x1) && !cloakglass) { - cloakmin -= 1; - } - - if ((cloakmin & 0x1) == 0 && cloakglass) { - cloakmin += 1; - } + cloak = Cloak(unit_key); - if (!UnitCSVFactory::GetVariable(unit_key, "Can_Cloak", false)) { - cloaking = -1; - } else { - cloaking = (int) (-2147483647) - 1; - } - - cloakrate = (int) (2147483136.0 * UnitCSVFactory::GetVariable(unit_key, "Cloak_Rate", 0.0f)); //short fix - cloakenergy = UnitCSVFactory::GetVariable(unit_key, "Cloak_Energy", 0.0f); repair_droid = UnitCSVFactory::GetVariable(unit_key, "Repair_Droid", 0); ecm = UnitCSVFactory::GetVariable(unit_key, "ECM_Rating", 0); @@ -1384,11 +1364,13 @@ string Unit::WriteUnitString() { unit["Tracking_Cone"] = tos(acos(computer.radar.trackingcone) * 180. / VS_PI); unit["Max_Cone"] = tos(acos(computer.radar.maxcone) * 180. / VS_PI); unit["Lock_Cone"] = tos(acos(computer.radar.lockcone) * 180. / VS_PI); - unit["Cloak_Min"] = tos(cloakmin / 2147483136.); - unit["Can_Cloak"] = tos(cloaking != -1); - unit["Cloak_Rate"] = tos(fabs(cloakrate / 2147483136.)); - unit["Cloak_Energy"] = tos(cloakenergy); - unit["Cloak_Glass"] = tos(cloakglass); + + // TODO: move to Cloak + unit["Cloak_Min"] = tos(cloak.minimum); + unit["Can_Cloak"] = tos(cloak.Capable()); + unit["Cloak_Rate"] = tos(cloak.rate); + unit["Cloak_Energy"] = tos(cloak.energy); + unit["Cloak_Glass"] = tos(cloak.glass); unit["Repair_Droid"] = tos(repair_droid); unit["ECM_Rating"] = tos(ecm > 0 ? ecm : -ecm); unit["Hud_Functionality"] = WriteHudDamage(this); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 410f02f861..4a236c2f53 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -133,7 +133,7 @@ void Unit::SetNebula(Nebula *neb) { bool Unit::InRange(const Unit *target, double &mm, bool cone, bool cap, bool lock) const { const float capship_size = configuration()->physics_config.capship_size; - if (this == target || target->CloakVisible() < .8) { + if (this == target || !target->cloak.Visible()) { return false; } if (cone && computer.radar.maxcone > -.98) { @@ -956,8 +956,11 @@ void Unit::UpdateSubunitPhysics(Unit *subunit, lastframe, uc, superunit); - //short fix - subunit->cloaking = (unsigned int) cloaking; + + // TODO: make the subunit->cloak a pointer to parent->cloak + // Also, no reason why subunits should handle their own physics but that's + // much harder to refactor + subunit->cloak.status = cloak.status; if (Destroyed()) { subunit->Target(NULL); UnFire(); //don't want to go off shooting while your body's splitting everywhere @@ -1293,14 +1296,8 @@ void Unit::DamageRandSys(float dam, const Vector &vec, float randnum, float degr if (degrees >= 90 && degrees < 120) { //DAMAGE Shield //DAMAGE cloak - if (randnum >= .95) { - this->cloaking = -1; - damages |= Damages::CLOAK_DAMAGED; - } else if (randnum >= .78) { - cloakenergy += ((1 - dam) * recharge); - damages |= Damages::CLOAK_DAMAGED; - } else if (randnum >= .7) { - cloakmin += (rand() % (32000 - cloakmin)); + if (randnum >= .7) { + this->cloak.Damage(); damages |= Damages::CLOAK_DAMAGED; } @@ -1661,25 +1658,12 @@ void Unit::SetOwner(Unit *target) { owner = target; } -void Unit::Cloak(bool loak) { - damages |= Damages::CLOAK_DAMAGED; - if (loak) { - static bool warp_energy_for_cloak = - XMLSupport::parse_bool(vs_config->getVariable("physics", "warp_energy_for_cloak", "true")); - if (cloakenergy < (warp_energy_for_cloak ? warpenergy : energy.Value())) { - cloakrate = (cloakrate >= 0) ? cloakrate : -cloakrate; - //short fix - if (cloaking < -1 && cloakrate != 0) { - //short fix - cloaking = 2147483647; - } else { - } - } +// Need this for python API. Do not delete. +void Unit::ActivateCloak(bool enable) { + if(enable) { + cloak.Activate(); } else { - cloakrate = (cloakrate >= 0) ? -cloakrate : cloakrate; - if (cloaking == cloakmin) { - ++cloaking; - } + cloak.Deactivate(); } } @@ -3344,14 +3328,14 @@ bool Unit::UpAndDownGrade(const Unit *up, tempdownmap); } } - if (cloaking != -1 && up->cloaking != -1) { + if (cloak.Capable() && up->cloak.Capable()) { if (touchme) { - cloaking = -1; + cloak.Disable(); } ++numave; ++percentage; if (gen_downgrade_list) { - AddToDowngradeMap(up->name, up->cloaking, ((char *) &this->cloaking) - ((char *) this), tempdownmap); + AddToDowngradeMap(up->name, up->cloak.current, ((char *) &this->cloak.current) - ((char *) this), tempdownmap); } } //NOTE: Afterburner type 2 (jmp) @@ -3380,16 +3364,17 @@ bool Unit::UpAndDownGrade(const Unit *up, } } } - if ((cloaking == -1 && up->cloaking != -1) || force_change_on_nothing) { + if ((!cloak.Capable() && up->cloak.Capable()) || force_change_on_nothing) { if (touchme) { - cloaking = up->cloaking; - cloakmin = up->cloakmin; - cloakrate = up->cloakrate; - cloakglass = up->cloakglass; - cloakenergy = up->cloakenergy; + cloak.Enable(); + + cloak.minimum = up->cloak.minimum; + cloak.rate = up->cloak.rate; + cloak.glass = up->cloak.glass; + cloak.energy = up->cloak.energy; } ++numave; - } else if (cloaking != -1 && up->cloaking != -1) { + } else if (cloak.Capable() && up->cloak.Capable()) { cancompletefully = false; } //NOTE: Afterburner type 2 (jmp) @@ -4259,95 +4244,7 @@ void Unit::applyTechniqueOverrides(const std::map &ove std::map Drawable::Units; -//helper func for Init -string toLowerCase(string in) { - string out; - for (unsigned int i = 0; i < in.length(); i++) { - switch (in[i]) { - case 'A': - out += 'a'; - break; - case 'B': - out += 'b'; - break; - case 'C': - out += 'c'; - break; - case 'D': - out += 'd'; - break; - case 'E': - out += 'e'; - break; - case 'F': - out += 'f'; - break; - case 'G': - out += 'g'; - break; - case 'H': - out += 'h'; - break; - case 'I': - out += 'i'; - break; - case 'J': - out += 'j'; - break; - case 'K': - out += 'k'; - break; - case 'L': - out += 'l'; - break; - case 'M': - out += 'm'; - break; - case 'N': - out += 'n'; - break; - case 'O': - out += 'o'; - break; - case 'P': - out += 'p'; - break; - case 'Q': - out += 'q'; - break; - case 'R': - out += 'r'; - break; - case 'S': - out += 's'; - break; - case 'T': - out += 't'; - break; - case 'U': - out += 'u'; - break; - case 'V': - out += 'v'; - break; - case 'W': - out += 'w'; - break; - case 'X': - out += 'x'; - break; - case 'Y': - out += 'y'; - break; - case 'Z': - out += 'z'; - break; - default: - out += in[i]; - } - } - return out; -} + unsigned int Drawable::unitCount = 0; @@ -4434,7 +4331,18 @@ void Unit::UpdatePhysics3(const Transformation &trans, if (fuel < 0) { fuel = 0; } - UpdateCloak(); + + static CloakingStatus previous_status = cloak.status; + cloak.Update(this); + + // Play once per cloaking + if(cloak.Cloaking() && previous_status != CloakingStatus::cloaking) { + previous_status = cloak.status; + playSound(SoundType::cloaking); + } else if(cloak.Cloaked() && previous_status != CloakingStatus::cloaked) { + previous_status = cloak.status; + adjustSound(SoundType::cloaking, cumulative_transformation.position, cumulative_velocity); + } // Recharge energy and shields const bool apply_difficulty_shields = configuration()->physics_config.difficulty_based_shield_recharge; @@ -4487,7 +4395,7 @@ void Unit::UpdatePhysics3(const Transformation &trans, float dist_sqr_to_target = FLT_MAX; Unit *target = Unit::Target(); bool increase_locking = false; - if (target && cloaking < 0 /*-1 or -32768*/) { + if (target && !cloak.Cloaked()) { if (target->isUnit() != Vega_UnitType::planet) { Vector TargetPos(InvTransform(cumulative_transformation_matrix, (target->Position())).Cast()); dist_sqr_to_target = TargetPos.MagnitudeSquared(); @@ -4530,7 +4438,7 @@ void Unit::UpdatePhysics3(const Transformation &trans, // TODO: simplify this if if (((false && mounts[i].status - == Mount::INACTIVE) || mounts[i].status == Mount::ACTIVE) && cloaking < 0 + == Mount::INACTIVE) || mounts[i].status == Mount::ACTIVE) && !cloak.Cloaked() && mounts[i].ammo != 0) { if (player_cockpit) { touched = true; @@ -4729,55 +4637,6 @@ void Unit::UpdatePhysics3(const Transformation &trans, } } -void Unit::UpdateCloak() { - // Use warp power for cloaking (SPEC capacitor) - const bool warp_energy_for_cloak = configuration()->warp_config.use_warp_energy_for_cloak; - - // We are not cloaked - exiting function - if (cloaking < cloakmin) { - return; - } - - // Insufficient energy to cloak ship - if (cloakenergy * simulation_atom_var > (warp_energy_for_cloak ? warpenergy : energy.Value())) { - Cloak(false); - return; - } - - // Cloaked ships don't have shields on - shield->Disable(); - - // We're cloaked - if (cloaking > cloakmin) { - adjustSound(SoundType::cloaking, cumulative_transformation.position, cumulative_velocity); - - //short fix - // TODO: figure out what they have fixed - if ((cloaking == (2147483647) - && cloakrate > 0) || (cloaking == cloakmin + 1 && cloakrate < 0)) { - playSound(SoundType::cloaking); - } - - //short fix - // TODO: figure out what they have fixed - cloaking -= (int) (cloakrate * simulation_atom_var); - if (cloaking <= cloakmin && cloakrate > 0) { - cloaking = cloakmin; - } - if (cloaking < 0 && cloakrate < 0) { - cloaking = -2147483647 - 1; - } - } - - // Calculate energy drain - if (cloakrate > 0 || cloaking == cloakmin) { - if (warp_energy_for_cloak) { - warpenergy -= (simulation_atom_var * cloakenergy); - } else { - energy -= (simulation_atom_var * cloakenergy); - } - } -} bool Unit::isPlayerShip() { return _Universe->isPlayerStarship(this) ? true : false; diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index 75aec3451f..4031de1f71 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -76,6 +76,7 @@ void UncheckUnit( class Unit*un ); #include "SharedPool.h" #include "role_bitmask.h" #include "upgradeable_unit.h" +#include "cloak.h" #include "configuration/configuration.h" #include "configuration/game_config.h" @@ -136,11 +137,14 @@ struct PlanetaryOrbitData; // TODO: move Armed to subclasses class Unit : public Armed, public Audible, public Drawable, public Damageable, public Energetic, public Intelligent, public Movable, public JumpCapable, public Carrier, public UpgradeableUnit { + protected: //How many lists are referencing us int ucref = 0; StringPool::Reference csvRow; + public: + Cloak cloak; /// Radar and related systems // TODO: take a deeper look at this much later... @@ -584,20 +588,6 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p float shieldtight = configuration()->physics_config.default_shield_tightness; public: - // TODO: move cloak to Cloakable? - ///How much energy cloaking takes per frame - float cloakenergy = 0; - ///how fast this starship decloaks/close...if negative, decloaking - int cloakrate = 100; //short fix - ///If this unit cloaks like glass or like fading - bool cloakglass = false; - - //-1 is not available... ranges between 0 32767 for "how invisible" unit currently is (32768... -32768) being visible) - // Despite the above comment, Init() would set it to -1 - int cloaking = -1; //short fix -//the minimum cloaking value... - int cloakmin = cloakglass ? 1 : 0; //short fix - // TODO: move to jump_capable? ///if the unit is a wormhole bool forcejump = false; @@ -613,16 +603,14 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p return ToLocalCoordinates((un->Position() - Position()).Cast()); } -//how visible the ship is from 0 to 1 +// how visible the ship is from 0 to 1 +// Need this function to expose it to python float CloakVisible() const { - if (cloaking < 0) { - return 1; - } - return ((float) cloaking) / 2147483647; + cloak.Visible(); } //cloaks or decloaks the starship depending on the bool - virtual void Cloak(bool cloak); + virtual void ActivateCloak(bool cloak); //deletes void Kill(bool eraseFromSave = true, bool quitting = false); diff --git a/engine/src/gfx/cockpit.cpp b/engine/src/gfx/cockpit.cpp index a8205c6ccd..fc4dcb7694 100644 --- a/engine/src/gfx/cockpit.cpp +++ b/engine/src/gfx/cockpit.cpp @@ -681,11 +681,11 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { return (float) UnitImages::NOTAPPLICABLE; } case UnitImages::CLOAK_MODAL: - if (-1 == target->cloaking) { + if (!target->cloak.Capable() || target->cloak.Damaged()) { return (float) UnitImages::NOTAPPLICABLE; - } else if (((int) (-2147483647) - 1) == target->cloaking) { + } else if (target->cloak.Ready()) { return (float) UnitImages::READY; - } else if (target->cloaking == target->cloakmin) { + } else if (target->cloak.Cloaked()) { return (float) UnitImages::ACTIVE; } else { return (float) UnitImages::SWITCHING; diff --git a/engine/src/gfx/halo_system.cpp b/engine/src/gfx/halo_system.cpp index 7e723175bb..272d31813e 100644 --- a/engine/src/gfx/halo_system.cpp +++ b/engine/src/gfx/halo_system.cpp @@ -243,7 +243,9 @@ void HaloSystem::Draw(const Matrix &trans, GFXColor(1, 1, 1, 1), GFXColor(1, 1, 1, 1), blend); - i->mesh->Draw(50000000000000.0, m, 1, alpha, nebdist, 0, false, &xtraFX); + + static Cloak dummy_cloak; + i->mesh->Draw(50000000000000.0, m, alpha, dummy_cloak, nebdist, 0, false, &xtraFX); // If damaged, and halo is back-facing if (hullpercent < .99 && ((i->trans.getR().z / i->trans.getR().Magnitude()) > 0.707)) { diff --git a/engine/src/gfx/mesh.cpp b/engine/src/gfx/mesh.cpp index 9db212e539..0ba65b852a 100644 --- a/engine/src/gfx/mesh.cpp +++ b/engine/src/gfx/mesh.cpp @@ -50,7 +50,7 @@ #include "lin_time.h" #include "mesh_xml.h" #include "gfx/technique.h" -#include +#include #include #define LOD_HYSTHERESIS_DIVIDER (20) diff --git a/engine/src/gfx/mesh.h b/engine/src/gfx/mesh.h index aec5e45876..7998181530 100644 --- a/engine/src/gfx/mesh.h +++ b/engine/src/gfx/mesh.h @@ -343,13 +343,13 @@ class Mesh { ///Draws lod pixel wide mesh at Transformation LATER void Draw(float lod, - const Matrix &m = identity_matrix, - float toofar = 1, - int cloak = -1, - float nebdist = 0, - unsigned char damage = 0, - bool renormalize_normals = false, - const MeshFX *mfx = NULL); //short fix + const Matrix &m = identity_matrix, + float toofar = 1, + Cloak cloak = Cloak(), + float nebdist = 0, + unsigned char damage = 0, + bool renormalize_normals = false, + const MeshFX *mfx = NULL); //short fix ///Draws lod pixels wide, mesh at Transformation NOW. If centered, then will center on camera and disable cull void DrawNow(float lod, bool centered, diff --git a/engine/src/gfx/mesh_gfx.cpp b/engine/src/gfx/mesh_gfx.cpp index d9f8ba0018..1ebc3d8aee 100644 --- a/engine/src/gfx/mesh_gfx.cpp +++ b/engine/src/gfx/mesh_gfx.cpp @@ -465,13 +465,13 @@ Mesh::~Mesh() { } void Mesh::Draw(float lod, - const Matrix &m, - float toofar, - int cloak, - float nebdist, - unsigned char hulldamage, - bool renormalize, - const MeshFX *mfx) //short fix + const Matrix &m, + float toofar, + Cloak cloak, + float nebdist, + unsigned char hulldamage, + bool renormalize, + const MeshFX *mfx) //short fix { Mesh *origmesh = getLOD(lod); if (origmesh->rSize() > 0) { @@ -486,6 +486,8 @@ void Mesh::Draw(float lod, c.damage = hulldamage; c.mesh_seq = ((toofar + rSize()) > g_game.zfar) ? NUM_ZBUF_SEQ : draw_sequence; + + // Cloaking and Nebula c.cloaked = MeshDrawContext::NONE; if (nebdist < 0) { c.cloaked |= MeshDrawContext::FOG; @@ -493,30 +495,30 @@ void Mesh::Draw(float lod, if (renormalize) { c.cloaked |= MeshDrawContext::RENORMALIZE; } - if (cloak >= 0) { + + // This should have gradually made the ship cloak go transparent + // for cloak.Glass but it does the same thing as the ordinary cloak. + // TODO: revisit this. + if(cloak.Active()) { c.cloaked |= MeshDrawContext::CLOAK; - if ((cloak & 0x1)) { - c.cloaked |= MeshDrawContext::GLASSCLOAK; - c.mesh_seq = MESH_SPECIAL_FX_ONLY; //draw near the end with lights + c.cloaked |= MeshDrawContext::GLASSCLOAK; + + if (cloak.Glass()) { + c.CloakFX.a = cloak.Current(); + c.mesh_seq = 2; //MESH_SPECIAL_FX_ONLY; } else { c.mesh_seq = 2; + c.CloakFX.r = (1-cloak.Current()); + c.CloakFX.g = (1-cloak.Current()); + c.CloakFX.b = (1-cloak.Current()); + c.CloakFX.a = cloak.Current(); } - if (cloak <= CLKSCALE / 2) { - c.cloaked |= MeshDrawContext::NEARINVIS; - } - float tmp = ((float) cloak) / CLKSCALE; - c.CloakFX.r = (c.cloaked & MeshDrawContext::GLASSCLOAK) ? tmp : 1; - c.CloakFX.g = (c.cloaked & MeshDrawContext::GLASSCLOAK) ? tmp : 1; - c.CloakFX.b = (c.cloaked & MeshDrawContext::GLASSCLOAK) ? tmp : 1; - c.CloakFX.a = tmp; - /* - * c.CloakNebFX.ambient[0]=((float)cloak)/CLKSCALE; - * c.CloakNebFX.ag=((float)cloak)/CLKSCALE; - * c.CloakNebFX.ab=((float)cloak)/CLKSCALE; - * c.CloakNebFX.aa=((float)cloak)/CLKSCALE; - */ - ///all else == defaults, only ambient } + + if (cloak.Cloaking()) { + c.cloaked |= MeshDrawContext::NEARINVIS; + } + //c.mat[12]=pos.i; //c.mat[13]=pos.j; //c.mat[14]=pos.k;//to translate to local_pos which is now obsolete! diff --git a/engine/src/gfx/nav/navscreen.cpp b/engine/src/gfx/nav/navscreen.cpp index 4432e329ea..5720caeb00 100644 --- a/engine/src/gfx/nav/navscreen.cpp +++ b/engine/src/gfx/nav/navscreen.cpp @@ -429,9 +429,7 @@ void NavigationSystem::Draw() { Matrix mat(p, q, r, pos); if (mesh[i]) { - mesh[i]->Draw( - FLT_MAX, // lod - mat); + mesh[i]->Draw(FLT_MAX, mat); } } Mesh::ProcessZFarMeshes(true); diff --git a/engine/src/python/python_unit_wrap.h b/engine/src/python/python_unit_wrap.h index 677b664b91..f5b60bca31 100644 --- a/engine/src/python/python_unit_wrap.h +++ b/engine/src/python/python_unit_wrap.h @@ -80,7 +80,7 @@ WRAPPED3(bool, InRange, UnitWrapper, target, bool, cone, bool, cap, false ) WRAPPED0(float, CloakVisible, false ) -voidWRAPPED1( Cloak, +voidWRAPPED1( ActivateCloak, bool, cloak ) voidWRAPPED0( RemoveFromSystem ) WRAPPED4(QVector, From e38f3fa4231444df35977db95ff5304dc8ea59ce Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Tue, 10 Oct 2023 20:30:42 +0300 Subject: [PATCH 03/16] Move cloak code from unit_csv to cloak class Add missing return. Other minor fixes. --- engine/src/cmd/cloak.cpp | 9 +++++++++ engine/src/cmd/cloak.h | 19 +++++++++++++------ engine/src/cmd/drawable.cpp | 4 ++-- engine/src/cmd/unit_csv.cpp | 7 +------ engine/src/cmd/unit_generic.cpp | 7 +++++-- engine/src/cmd/unit_generic.h | 4 ++-- engine/src/gfx/mesh_gfx.cpp | 6 +++--- engine/src/resource/resource.h | 2 +- 8 files changed, 36 insertions(+), 22 deletions(-) diff --git a/engine/src/cmd/cloak.cpp b/engine/src/cmd/cloak.cpp index 127969770f..7f20ef5c86 100644 --- a/engine/src/cmd/cloak.cpp +++ b/engine/src/cmd/cloak.cpp @@ -54,6 +54,15 @@ Cloak::Cloak(std::string unit_key) current = 0; } +void Cloak::Save(std::map& unit) +{ + unit["Cloak_Min"] = std::to_string(minimum); + unit["Can_Cloak"] = std::to_string(Capable()); + unit["Cloak_Rate"] = std::to_string(rate); + unit["Cloak_Energy"] = std::to_string(energy); + unit["Cloak_Glass"] = std::to_string(glass); +} + void Cloak::Update(Energetic *energetic) { // Unit is not capable of cloaking or damaged or just not cloaking diff --git a/engine/src/cmd/cloak.h b/engine/src/cmd/cloak.h index 7a708a40f5..967145d279 100644 --- a/engine/src/cmd/cloak.h +++ b/engine/src/cmd/cloak.h @@ -26,6 +26,7 @@ #define CLOAK_H #include +#include #include "energetic.h" #include "damageable_layer.h" @@ -64,6 +65,7 @@ class Cloak public: Cloak(); Cloak(std::string unit_key); + void Save(std::map& unit); void Update(Energetic *energetic); void Toggle(); // Toggle cloak on/off @@ -76,7 +78,7 @@ class Cloak return (status == CloakingStatus::cloaking); } - bool Cloaked() { + bool Cloaked() const { return (status == CloakingStatus::cloaked); } @@ -99,16 +101,21 @@ class Cloak return glass; } - double Current() { - return current; - } - double Energy() { return energy; } + // Is the ship visible + bool Visible() { + return !Cloaked(); + } + + double Current() const { + return current; + } + //how visible the ship is from 0 to 1 - double Visible() const { + double Visibility() const { return 1-current; } diff --git a/engine/src/cmd/drawable.cpp b/engine/src/cmd/drawable.cpp index 0c357a37ea..04e20c2678 100644 --- a/engine/src/cmd/drawable.cpp +++ b/engine/src/cmd/drawable.cpp @@ -486,7 +486,7 @@ void Drawable::DrawNow(const Matrix &mato, float lod) { if (!(unit->docked & (unit->DOCKED | unit->DOCKED_INSIDE))) { halos->Draw(mat, Scale, - unit->cloak.Visible(), + unit->cloak.Visibility(), 0, unit->GetHullPercent(), velocity, @@ -735,7 +735,7 @@ void Drawable::DrawHalo(bool on_screen, float apparent_size, Matrix wmat, Cloak //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; - halos->Draw(wmat, Scale, unit->cloak.Visible(), nebd, hulld, velocity, + halos->Draw(wmat, Scale, unit->cloak.Visibility(), nebd, hulld, velocity, linaccel, angaccel, maxaccel, cmas, unit->faction); } diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index 7c1d8bd806..77c69eeb90 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -1365,12 +1365,7 @@ string Unit::WriteUnitString() { unit["Max_Cone"] = tos(acos(computer.radar.maxcone) * 180. / VS_PI); unit["Lock_Cone"] = tos(acos(computer.radar.lockcone) * 180. / VS_PI); - // TODO: move to Cloak - unit["Cloak_Min"] = tos(cloak.minimum); - unit["Can_Cloak"] = tos(cloak.Capable()); - unit["Cloak_Rate"] = tos(cloak.rate); - unit["Cloak_Energy"] = tos(cloak.energy); - unit["Cloak_Glass"] = tos(cloak.glass); + cloak.Save(unit); unit["Repair_Droid"] = tos(repair_droid); unit["ECM_Rating"] = tos(ecm > 0 ? ecm : -ecm); unit["Hud_Functionality"] = WriteHudDamage(this); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 4a236c2f53..b0e6691335 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -133,7 +133,7 @@ void Unit::SetNebula(Nebula *neb) { bool Unit::InRange(const Unit *target, double &mm, bool cone, bool cap, bool lock) const { const float capship_size = configuration()->physics_config.capship_size; - if (this == target || !target->cloak.Visible()) { + if (this == target || target->cloak.Cloaked()) { return false; } if (cone && computer.radar.maxcone > -.98) { @@ -3335,7 +3335,10 @@ bool Unit::UpAndDownGrade(const Unit *up, ++numave; ++percentage; if (gen_downgrade_list) { - AddToDowngradeMap(up->name, up->cloak.current, ((char *) &this->cloak.current) - ((char *) this), tempdownmap); + AddToDowngradeMap(up->name, + up->cloak.current, + ((char *) &this->cloak.current) - ((char *) this), + tempdownmap); } } //NOTE: Afterburner type 2 (jmp) diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index 4031de1f71..5f5288a8ca 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -605,8 +605,8 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p // how visible the ship is from 0 to 1 // Need this function to expose it to python - float CloakVisible() const { - cloak.Visible(); + float CloakVisible() { + cloak.Visibility(); } //cloaks or decloaks the starship depending on the bool diff --git a/engine/src/gfx/mesh_gfx.cpp b/engine/src/gfx/mesh_gfx.cpp index 1ebc3d8aee..c5a8d7e633 100644 --- a/engine/src/gfx/mesh_gfx.cpp +++ b/engine/src/gfx/mesh_gfx.cpp @@ -508,9 +508,9 @@ void Mesh::Draw(float lod, c.mesh_seq = 2; //MESH_SPECIAL_FX_ONLY; } else { c.mesh_seq = 2; - c.CloakFX.r = (1-cloak.Current()); - c.CloakFX.g = (1-cloak.Current()); - c.CloakFX.b = (1-cloak.Current()); + c.CloakFX.r = cloak.Visibility(); + c.CloakFX.g = cloak.Visibility(); + c.CloakFX.b = cloak.Visibility(); c.CloakFX.a = cloak.Current(); } } diff --git a/engine/src/resource/resource.h b/engine/src/resource/resource.h index 4ef41759d1..a43c4e75e3 100644 --- a/engine/src/resource/resource.h +++ b/engine/src/resource/resource.h @@ -37,7 +37,7 @@ class Resource { T adjusted_max_value_; bool no_max_; public: - Resource(const T &value, const T &min_value = 0, const T &max_value = -1); + Resource(const T &value = 0, const T &min_value = 0, const T &max_value = -1); Resource operator+=(const T &value); Resource operator-=(const T &value); From 0dd2465309cd276a5545ab47224962f05e5af173 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Wed, 11 Oct 2023 21:23:40 +0300 Subject: [PATCH 04/16] Add missing return statement --- engine/src/cmd/unit_generic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index 5f5288a8ca..6cf18bda56 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -606,7 +606,7 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p // how visible the ship is from 0 to 1 // Need this function to expose it to python float CloakVisible() { - cloak.Visibility(); + return cloak.Visibility(); } //cloaks or decloaks the starship depending on the bool From c6633246db21030517067ce2fee1bec22b0988dc Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Tue, 17 Oct 2023 18:12:02 +0300 Subject: [PATCH 05/16] Implement radar code as a separate component Introduce the concept of components. --- engine/CMakeLists.txt | 8 ++ engine/src/cmd/ai/aggressive.cpp | 2 +- engine/src/cmd/ai/comm_ai.cpp | 4 +- engine/src/cmd/ai/fire.cpp | 6 +- engine/src/cmd/ai/firekeyboard.cpp | 10 +- engine/src/cmd/armed.cpp | 21 ++- engine/src/cmd/armed.h | 2 - engine/src/cmd/basecomputer.cpp | 83 +++++------ engine/src/cmd/computer.cpp | 54 -------- engine/src/cmd/computer.h | 51 ------- engine/src/cmd/drawable.cpp | 2 +- engine/src/cmd/unit_csv.cpp | 54 +------- engine/src/cmd/unit_generic.cpp | 122 +++++++--------- engine/src/cmd/unit_generic.h | 2 + engine/src/components/component.cpp | 45 ++++++ engine/src/components/component.h | 48 +++++++ engine/src/components/radar.cpp | 207 ++++++++++++++++++++++++++++ engine/src/components/radar.h | 117 ++++++++++++++++ engine/src/gfx/cockpit.cpp | 35 ++--- engine/src/gfx/nav/drawsystem.cpp | 2 +- engine/src/gfx/radar/sensor.cpp | 14 +- engine/src/gfx/radar/sensor.h | 2 + 22 files changed, 571 insertions(+), 320 deletions(-) create mode 100644 engine/src/components/component.cpp create mode 100644 engine/src/components/component.h create mode 100644 engine/src/components/radar.cpp create mode 100644 engine/src/components/radar.h diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 5e1040760e..7d35cb6145 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -126,6 +126,7 @@ INCLUDE_DIRECTORIES( ${Vega_Strike_SOURCE_DIR}/src/cmd ${Vega_Strike_SOURCE_DIR}/src/damage ${Vega_Strike_SOURCE_DIR}/src/resource + ${Vega_Strike_SOURCE_DIR}/src/components ${Vega_Strike_BINARY_DIR} ) ELSE (MSVC) @@ -134,6 +135,7 @@ INCLUDE_DIRECTORIES( ${Vega_Strike_SOURCE_DIR}/src/cmd ${Vega_Strike_SOURCE_DIR}/src/damage ${Vega_Strike_SOURCE_DIR}/src/resource + ${Vega_Strike_SOURCE_DIR}/src/components ${Vega_Strike_BINARY_DIR} /usr/include/harfbuzz/ ) @@ -657,6 +659,11 @@ SET(LIBRESOURCE src/resource/product.cpp ) +SET(LIBCOMPONENT + src/components/component.cpp + src/components/radar.cpp + ) + SET(LIBGUI_SOURCES src/gui/button.cpp src/gui/control.cpp @@ -1059,6 +1066,7 @@ ADD_LIBRARY(vegastrike-engine_com ${LIBCONFIG} ${LIBDAMAGE} ${LIBRESOURCE} + ${LIBCOMPONENT} ${LIBAI_SOURCES} ${LIBCMD_SOURCES} ${LIBNET_SOURCES} diff --git a/engine/src/cmd/ai/aggressive.cpp b/engine/src/cmd/ai/aggressive.cpp index 964d9a7b5e..67d66d1765 100644 --- a/engine/src/cmd/ai/aggressive.cpp +++ b/engine/src/cmd/ai/aggressive.cpp @@ -1325,7 +1325,7 @@ static Unit *ChooseNavPoint(Unit *parent, Unit **otherdest, float *lurk_on_arriv if (a != b) { int retrycount = maxrand; while (--retrycount > 0 - && (UnitUtil::getDistance(a, b) < parent->GetComputerData().radar.maxrange * 4 || a == b)) { + && (UnitUtil::getDistance(a, b) < parent->radar.GetMaxRange() * 4 || a == b)) { b = GetRandomNav(stats->navs, additionalrand[retrycount]); } if (retrycount != 0) { diff --git a/engine/src/cmd/ai/comm_ai.cpp b/engine/src/cmd/ai/comm_ai.cpp index 90aab335df..4b7a95bf11 100644 --- a/engine/src/cmd/ai/comm_ai.cpp +++ b/engine/src/cmd/ai/comm_ai.cpp @@ -364,7 +364,7 @@ Unit *CommunicatingAI::GetRandomUnit(float playaprob, float targprob) { } //FIXME FOR TESTING ONLY //return parent->Target(); - QVector where = parent->Position() + parent->GetComputerData().radar.maxrange * QVector(vsrandom.uniformInc(-1, 1), + QVector where = parent->Position() + parent->radar.GetMaxRange() * QVector(vsrandom.uniformInc(-1, 1), vsrandom.uniformInc(-1, 1), vsrandom.uniformInc(-1, 1)); Collidable wherewrapper(0, 0, where); @@ -393,7 +393,7 @@ void CommunicatingAI::RandomInitiateCommunication(float playaprob, float targpro if (target != NULL) { if (UnitUtil::getUnitSystemFile(target) == UnitUtil::getUnitSystemFile(parent) && UnitUtil::getFlightgroupName(parent) != "Base" && !isDockedAtAll(target) - && UnitUtil::getDistance(parent, target) <= target->GetComputerData().radar.maxrange) { + && UnitUtil::getDistance(parent, target) <= target->radar.GetMaxRange()) { //warning--odd hack they can talk to you if you can sense them--it's like SETI@home for (std::list::iterator i = messagequeue.begin(); i != messagequeue.end(); i++) { Unit *un = (*i)->sender.GetUnit(); diff --git a/engine/src/cmd/ai/fire.cpp b/engine/src/cmd/ai/fire.cpp index 3c6d1b8769..deaf06e95c 100644 --- a/engine/src/cmd/ai/fire.cpp +++ b/engine/src/cmd/ai/fire.cpp @@ -577,7 +577,7 @@ void FireAt::ChooseTargets(int numtargs, bool force) { "Targetting", "search_max_candidates", "64")); //Cutoff candidate count (if that many hostiles found, stop search - performance/quality tradeoff, 0=no cutoff) - UnitWithinRangeLocator > unitLocator(parent->GetComputerData().radar.maxrange, unitRad); + UnitWithinRangeLocator > unitLocator(parent->radar.GetMaxRange(), unitRad); StaticTuple maxranges{}; maxranges[0] = gunrange; @@ -629,7 +629,7 @@ void FireAt::ChooseTargets(int numtargs, bool force) { for (vector::iterator k = tbin.begin(); k != tbin.end(); ++k) { k->AssignTargets(my_target, parent->cumulative_transformation_matrix); } - parent->LockTarget(false); + parent->radar.Unlock(); if (wasnull) { if (mytarg) { nextframenumpollers[hastarg] += 2; @@ -657,7 +657,7 @@ void FireAt::ChooseTargets(int numtargs, bool force) { } } parent->Target(mytarg); - parent->LockTarget(true); + parent->radar.Lock(); SignalChosenTarget(); } diff --git a/engine/src/cmd/ai/firekeyboard.cpp b/engine/src/cmd/ai/firekeyboard.cpp index 3f03e307d3..62ecc84122 100644 --- a/engine/src/cmd/ai/firekeyboard.cpp +++ b/engine/src/cmd/ai/firekeyboard.cpp @@ -1782,7 +1782,7 @@ void FireKeyboard::Execute() { } if (f().lockkey == PRESS) { f().lockkey = DOWN; - parent->LockTarget(!parent->TargetLocked()); + parent->radar.ToggleLock(); } if (f().ECMkey == PRESS) { f().ECMkey = DOWN; @@ -1813,7 +1813,7 @@ void FireKeyboard::Execute() { f().targetskey = DOWN; ChooseTargets(parent, TargSig, false); refresh_target = true; - parent->LockTarget(true); + parent->radar.Lock(); } if (f().targetukey == PRESS) { f().targetukey = DOWN; @@ -1894,7 +1894,7 @@ void FireKeyboard::Execute() { f().rtargetskey = DOWN; ChooseTargets(parent, TargSig, true); refresh_target = true; - parent->LockTarget(true); + parent->radar.Lock(); } if (f().rtargetukey == PRESS) { f().rtargetukey = DOWN; @@ -2032,7 +2032,7 @@ void FireKeyboard::Execute() { } if (f().toggleautotracking == PRESS) { f().toggleautotracking = DOWN; - parent->GetComputerData().radar.trackingactive = !parent->GetComputerData().radar.trackingactive; + parent->radar.ToggleTracking(); } if (f().misk == PRESS || f().rmisk == PRESS) { bool forward; @@ -2058,7 +2058,7 @@ void FireKeyboard::Execute() { f().saveTargetKeys[i] = RELEASE; savedTargets[i] = parent->Target(); } - if (f().restoreTargetKeys[i] == PRESS && parent->GetComputerData().radar.canlock) { + if (f().restoreTargetKeys[i] == PRESS && parent->radar.CanLock()) { f().restoreTargetKeys[i] = RELEASE; Unit *un; for (un_iter u = _Universe->activeStarSystem()->getUnitList().createIterator(); diff --git a/engine/src/cmd/armed.cpp b/engine/src/cmd/armed.cpp index 40f99bb2b6..83826132c7 100644 --- a/engine/src/cmd/armed.cpp +++ b/engine/src/cmd/armed.cpp @@ -324,13 +324,7 @@ int Armed::LockMissile() const { return missilelock ? 1 : (dumblock ? -1 : 0); } -void Armed::LockTarget(bool myboo) { - Unit *unit = static_cast(this); - unit->computer.radar.locked = myboo; - if (myboo && unit->computer.radar.canlock == false && false == UnitUtil::isSignificant(unit->Target())) { - unit->computer.radar.locked = false; - } -} + QVector Armed::PositionITTS(const QVector &absposit, Vector velocity, float speed, bool steady_itts) const { const Unit *unit = static_cast(this); @@ -413,7 +407,7 @@ void Armed::setAverageGunSpeed() { bool Armed::TargetLocked(const Unit *checktarget) const { const Unit *unit = static_cast(this); - if (!unit->computer.radar.locked) { + if (!unit->radar.locked) { return false; } return (checktarget == NULL) || (unit->computer.target == checktarget); @@ -423,19 +417,24 @@ bool Armed::TargetTracked(const Unit *checktarget) { Unit *unit = static_cast(this); static bool must_lock_to_autotrack = XMLSupport::parse_bool( vs_config->getVariable("physics", "must_lock_to_autotrack", "true")); - bool we_do_track = unit->computer.radar.trackingactive + + bool we_do_track = unit->radar.tracking_active && (!_Universe->isPlayerStarship(unit) || TargetLocked() || !must_lock_to_autotrack); if (!we_do_track) { return false; } + if (checktarget == NULL) { return true; } + if (unit->computer.target != checktarget) { return false; } - float mycone = unit->computer.radar.trackingcone; + + float mycone = unit->radar.tracking_cone; we_do_track = CloseEnoughToAutotrack(unit, unit->computer.target.GetUnit(), mycone); + return we_do_track; } @@ -454,7 +453,7 @@ float Armed::TrackingGuns(bool &missilelock) { missilelock = false; for (int i = 0; i < getNumMounts(); ++i) { if (mounts[i].status == Mount::ACTIVE && isAutoTrackingMount(mounts[i].size)) { - trackingcone = unit->computer.radar.trackingcone; + trackingcone = unit->radar.tracking_cone; } if (mounts[i].status == Mount::ACTIVE && mounts[i].type->lock_time > 0 && mounts[i].time_to_lock <= 0) { missilelock = true; diff --git a/engine/src/cmd/armed.h b/engine/src/cmd/armed.h index 56f57af709..91534b1e13 100644 --- a/engine/src/cmd/armed.h +++ b/engine/src/cmd/armed.h @@ -61,8 +61,6 @@ class Armed { //-1 is no lock necessary 1 is locked int LockMissile() const; - void LockTarget(bool myboo); - //Finds the position from the local position if guns are aimed at it with speed QVector PositionITTS(const QVector &firingposit, Vector firingvelocity, float gunspeed, bool smooth_itts) const; diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index 63f243cf58..ea05716e0f 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -5236,15 +5236,15 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } if (!mode) { - PRETTY_ADDU(statcolor + "Tracking range: #-c", uc.radar.maxrange / 1000, 0, "km"); - if ((acos(uc.radar.maxcone) * 360 / PI) < 359) { - PRETTY_ADDU(statcolor + "Tracking cone: #-c", acos(uc.radar.maxcone) * 2, 2, "radians"); + PRETTY_ADDU(statcolor + "Tracking range: #-c", playerUnit->radar.GetMaxRange() / 1000, 0, "km"); + if ((acos(playerUnit->radar.GetMaxCone()) * 360 / PI) < 359) { + PRETTY_ADDU(statcolor + "Tracking cone: #-c", acos(playerUnit->radar.GetMaxCone()) * 2, 2, "radians"); text += expstatcolor + "#n# (planar angle: 2 pi means full space)#-c"; } else { text += "#n#" + prefix + statcolor + "Tracking cone: #-cOMNIDIRECTIONAL"; } - PRETTY_ADDU(statcolor + "Assisted targeting cone: #-c", acos(uc.radar.trackingcone) * 2, 2, "radians"); - PRETTY_ADDU(statcolor + "Missile locking cone: #-c", acos(uc.radar.lockcone) * 2, 2, "radians"); + PRETTY_ADDU(statcolor + "Assisted targeting cone: #-c", acos(playerUnit->radar.GetTrackingCone()) * 2, 2, "radians"); + PRETTY_ADDU(statcolor + "Missile locking cone: #-c", acos(playerUnit->radar.GetLockCone()) * 2, 2, "radians"); if (!subunitlevel) { //Always zero PRETTY_ADDU("Minimum target size: ",uc.radar.mintargetsize,2,"m"); text += "#n#" + prefix + statcolor + "ITTS (Intelligent Target Tracking System) support: #-c"; @@ -5255,10 +5255,10 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } text += "#n#" + prefix + statcolor + "AFHH (Advanced Flag & Hostility Heuristics) support: #-c"; std::string afhh; - if (uc.radar.UseFriendFoe()) { + if (playerUnit->radar.UseFriendFoe()) { afhh += "friendly/hostile "; } - if (uc.radar.UseThreatAssessment()) { + if (playerUnit->radar.UseThreatAssessment()) { afhh += "threat "; } if (afhh.empty()) { @@ -5268,42 +5268,43 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } text.append("#n##n##c0:1:.5#" + prefix + "[ENERGY SUBSYSTEM]#n##-c"); } else { + CRadar *radar = &playerUnit->radar; + std::string afhh; switch (replacement_mode) { case 0: //Replacement or new Module - if (MODIFIES_ALTEMPTY(replacement_mode, &uc, &buc, radar.maxrange, FLT_MAX) - || MODIFIES_ALTEMPTY(replacement_mode, &uc, &buc, radar.maxcone, VS_PI)) { - PRETTY_ADDU(statcolor + "Tracking range: #-c", uc.radar.maxrange / 1000, 0, "km"); - if ((acos(uc.radar.maxcone) * 360 / PI) < 359) { - PRETTY_ADDU(statcolor + "Tracking cone: #-c", acos(uc.radar.maxcone) * 2, 2, "radians"); - text += statcolor + " (planar angle: 2 pi means full space)#-c"; - } else { - text += "#n#" + prefix + statcolor + "Tracking cone: #-cOMNIDIRECTIONAL"; - } - PRETTY_ADDU(statcolor + "Assisted targeting cone: #-c", - acos(uc.radar.trackingcone) * 2, - 2, - "radians"); - PRETTY_ADDU(statcolor + "Missile locking cone: #-c", acos(uc.radar.lockcone) * 2, 2, "radians"); - text += "#n#" + prefix + statcolor + "ITTS (Intelligent Target Tracking System) support: #-c"; - if (uc.itts) { - text += "yes"; - } else { - text += "no"; - } - text += "#n#" + prefix + statcolor + "AFHH (Advanced Flag & Hostility Heuristics) support: #-c"; - std::string afhh; - if (uc.radar.UseFriendFoe()) { - afhh += "friendly/hostile "; - } - if (uc.radar.UseThreatAssessment()) { - afhh += "threat "; - } - if (afhh.empty()) { - afhh = "no"; - } - text += afhh; - } - break; + + PRETTY_ADDU(statcolor + "Tracking range: #-c", radar->GetMaxRange() / 1000, 0, "km"); + if ((acos(radar->GetMaxCone()) * 360 / PI) < 359) { + PRETTY_ADDU(statcolor + "Tracking cone: #-c", acos(playerUnit->radar.GetMaxCone()) * 2, 2, "radians"); + text += statcolor + " (planar angle: 2 pi means full space)#-c"; + } else { + text += "#n#" + prefix + statcolor + "Tracking cone: #-cOMNIDIRECTIONAL"; + } + PRETTY_ADDU(statcolor + "Assisted targeting cone: #-c", + acos(radar->GetTrackingCone()) * 2, + 2, + "radians"); + PRETTY_ADDU(statcolor + "Missile locking cone: #-c", acos(radar->GetLockCone()) * 2, 2, "radians"); + text += "#n#" + prefix + statcolor + "ITTS (Intelligent Target Tracking System) support: #-c"; + if (uc.itts) { + text += "yes"; + } else { + text += "no"; + } + text += "#n#" + prefix + statcolor + "AFHH (Advanced Flag & Hostility Heuristics) support: #-c"; + + if (radar->UseFriendFoe()) { + afhh += "friendly/hostile "; + } + if (radar->UseThreatAssessment()) { + afhh += "threat "; + } + if (afhh.empty()) { + afhh = "no"; + } + text += afhh; + + break; case 1: //Additive break; case 2: //multiplicative diff --git a/engine/src/cmd/computer.cpp b/engine/src/cmd/computer.cpp index 1f6b662e72..456702b82b 100644 --- a/engine/src/cmd/computer.cpp +++ b/engine/src/cmd/computer.cpp @@ -59,58 +59,4 @@ float Computer::max_ab_speed() const { return (!combat_mode) ? combat_mode_mult * max_combat_speed : max_combat_ab_speed; } -Computer::RADARLIM::RADARLIM() : - maxrange(0), - maxcone(-1), - lockcone(0), - trackingcone(0), - mintargetsize(0), - capability(Capability::IFF_NONE), - locked(false), - canlock(false), - trackingactive(true) { - maxrange = configuration()->computer_config.default_max_range; - trackingcone = configuration()->computer_config.default_tracking_cone; - lockcone = configuration()->computer_config.default_lock_cone; -} - -Computer::RADARLIM::Brand::Value Computer::RADARLIM::GetBrand() const { - switch (capability & Capability::IFF_UPPER_MASK) { - case Capability::IFF_SPHERE: - return Brand::SPHERE; - case Capability::IFF_BUBBLE: - return Brand::BUBBLE; - case Capability::IFF_PLANE: - return Brand::PLANE; - default: - assert(false); - return Brand::SPHERE; - } -} - -bool Computer::RADARLIM::UseFriendFoe() const { - // Backwardscompatibility - if (capability == 0) { - return false; - } else if ((capability == 1) || (capability == 2)) { - return true; - } - - return (capability & Capability::IFF_FRIEND_FOE); -} - -bool Computer::RADARLIM::UseObjectRecognition() const { - // Backwardscompatibility - if ((capability == 0) || (capability == 1)) { - return false; - } else if (capability == 2) { - return true; - } - - return (capability & Capability::IFF_OBJECT_RECOGNITION); -} - -bool Computer::RADARLIM::UseThreatAssessment() const { - return (capability & Capability::IFF_THREAT_ASSESSMENT); -} diff --git a/engine/src/cmd/computer.h b/engine/src/cmd/computer.h index 3ad4156c02..da2179f662 100644 --- a/engine/src/cmd/computer.h +++ b/engine/src/cmd/computer.h @@ -42,57 +42,6 @@ */ class Computer { public: - class RADARLIM { - public: - struct Brand { - enum Value { - SPHERE = 0, - BUBBLE = 1, - PLANE = 2 - }; - }; - struct Capability { - enum Value { - // For internal use - IFF_UPPER_SHIFT = 16, - IFF_LOWER_MASK = (1 << IFF_UPPER_SHIFT) - 1, - IFF_UPPER_MASK = ~IFF_LOWER_MASK, - - // The lower 16 bits - IFF_NONE = 0, - IFF_FRIEND_FOE = 1 << 0, - IFF_OBJECT_RECOGNITION = 1 << 1, - IFF_THREAT_ASSESSMENT = 1 << 2, - - // The upper 16 bits - IFF_SPHERE = Brand::SPHERE << IFF_UPPER_SHIFT, - IFF_BUBBLE = Brand::BUBBLE << IFF_UPPER_SHIFT, - IFF_PLANE = Brand::PLANE << IFF_UPPER_SHIFT - }; - }; - //the max range the radar can handle - float maxrange; - //the dot with (0,0,1) indicating the farthest to the side the radar can handle. - float maxcone; - float lockcone; - float trackingcone; - //The minimum radius of the target - float mintargetsize; - // What kind of type and capability the radar supports - int capability; - bool locked; - bool canlock; - bool trackingactive; - - Brand::Value GetBrand() const; - bool UseFriendFoe() const; - bool UseObjectRecognition() const; - bool UseThreatAssessment() const; - - RADARLIM(); - }; - - RADARLIM radar; bool ecmactive; //The nav point the unit may be heading for Vector NavPoint; diff --git a/engine/src/cmd/drawable.cpp b/engine/src/cmd/drawable.cpp index 04e20c2678..e18c0eaf0a 100644 --- a/engine/src/cmd/drawable.cpp +++ b/engine/src/cmd/drawable.cpp @@ -757,7 +757,7 @@ void Drawable::DrawSubunits(bool on_screen, Matrix wmat, Cloak cloak, float aver (isAutoTrackingMount(unit->mounts[i].size) && (unit->mounts[i].time_to_lock <= 0) && unit->TargetTracked()) ? unit->Target() : NULL, - unit->computer.radar.trackingcone); + unit->radar.GetTrackingCone()); } } diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index 77c69eeb90..5fcf330568 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -874,53 +874,8 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ computer.max_combat_ab_speed = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Speed_Governor", 0.0f) * game_speed; computer.itts = UnitCSVFactory::GetVariable(unit_key, "ITTS", true); - computer.radar.canlock = UnitCSVFactory::GetVariable(unit_key, "Can_Lock", true); - - - // The Radar_Color column in the units.csv has been changed from a - // boolean value to a string. The boolean values are supported for - // backwardscompatibility. - // When we save this setting, it is simply converted from an integer - // number to a string, and we need to support this as well. - std::string iffval = UnitCSVFactory::GetVariable(unit_key, "Radar_Color", std::string()); - - if ((iffval.empty()) || (iffval == "FALSE") || (iffval == "0")) { - computer.radar.capability = Computer::RADARLIM::Capability::IFF_NONE; - } else if ((iffval == "TRUE") || (iffval == "1")) { - computer.radar.capability = Computer::RADARLIM::Capability::IFF_SPHERE - | Computer::RADARLIM::Capability::IFF_FRIEND_FOE; - } else if (iffval == "THREAT") { - computer.radar.capability = Computer::RADARLIM::Capability::IFF_SPHERE - | Computer::RADARLIM::Capability::IFF_FRIEND_FOE - | Computer::RADARLIM::Capability::IFF_THREAT_ASSESSMENT; - } else if (iffval == "BUBBLE_THREAT") { - computer.radar.capability = Computer::RADARLIM::Capability::IFF_BUBBLE - | Computer::RADARLIM::Capability::IFF_FRIEND_FOE - | Computer::RADARLIM::Capability::IFF_OBJECT_RECOGNITION - | Computer::RADARLIM::Capability::IFF_THREAT_ASSESSMENT; - } else if (iffval == "PLANE") { - computer.radar.capability = Computer::RADARLIM::Capability::IFF_PLANE - | Computer::RADARLIM::Capability::IFF_FRIEND_FOE; - } else if (iffval == "PLANE_THREAT") { - computer.radar.capability - = Computer::RADARLIM::Capability::IFF_PLANE - | Computer::RADARLIM::Capability::IFF_FRIEND_FOE - | Computer::RADARLIM::Capability::IFF_OBJECT_RECOGNITION - | Computer::RADARLIM::Capability::IFF_THREAT_ASSESSMENT; - } else { - unsigned int value = stoi(iffval, 0); - if (value == 0) { - // Unknown value - computer.radar.capability = Computer::RADARLIM::Capability::IFF_NONE; - } else { - computer.radar.capability = value; - } - } - computer.radar.maxrange = UnitCSVFactory::GetVariable(unit_key, "Radar_Range", FLT_MAX); - computer.radar.maxcone = cos(UnitCSVFactory::GetVariable(unit_key, "Max_Cone", 180.0f) * VS_PI / 180); - computer.radar.trackingcone = cos(UnitCSVFactory::GetVariable(unit_key, "Tracking_Cone", 180.0f) * VS_PI / 180); - computer.radar.lockcone = cos(UnitCSVFactory::GetVariable(unit_key, "Lock_Cone", 180.0f) * VS_PI / 180); + radar = CRadar(unit_key, &computer); cloak = Cloak(unit_key); @@ -1358,12 +1313,7 @@ string Unit::WriteUnitString() { unit["Default_Speed_Governor"] = tos(computer.max_combat_speed / game_speed); unit["Afterburner_Speed_Governor"] = tos(computer.max_combat_ab_speed / game_speed); unit["ITTS"] = tos(computer.itts); - unit["Can_Lock"] = tos(computer.radar.canlock); - unit["Radar_Color"] = tos(computer.radar.capability); - unit["Radar_Range"] = tos(computer.radar.maxrange); - unit["Tracking_Cone"] = tos(acos(computer.radar.trackingcone) * 180. / VS_PI); - unit["Max_Cone"] = tos(acos(computer.radar.maxcone) * 180. / VS_PI); - unit["Lock_Cone"] = tos(acos(computer.radar.lockcone) * 180. / VS_PI); + cloak.Save(unit); unit["Repair_Droid"] = tos(repair_droid); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index b0e6691335..3a50e141ea 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -136,13 +136,13 @@ bool Unit::InRange(const Unit *target, double &mm, bool cone, bool cap, bool loc if (this == target || target->cloak.Cloaked()) { return false; } - if (cone && computer.radar.maxcone > -.98) { + if (cone && radar.max_cone > -.98) { QVector delta(target->Position() - Position()); mm = delta.Magnitude(); if ((!lock) || (!TargetLocked(target))) { double tempmm = mm - target->rSize(); if (tempmm > 0.0001) { - if ((ToLocalCoordinates(Vector(delta.i, delta.j, delta.k)).k / tempmm) < computer.radar.maxcone + if ((ToLocalCoordinates(Vector(delta.i, delta.j, delta.k)).k / tempmm) < radar.max_cone && cone) { return false; } @@ -152,8 +152,8 @@ bool Unit::InRange(const Unit *target, double &mm, bool cone, bool cap, bool loc mm = (target->Position() - Position()).Magnitude(); } //owner==target?! - if (((mm - rSize() - target->rSize()) > computer.radar.maxrange) - || target->rSize() < computer.radar.mintargetsize) { + if (((mm - rSize() - target->rSize()) > radar.max_range) + || target->rSize() < radar.GetMinTargetSize()) { Flightgroup *fg = target->getFlightgroup(); if ((target->rSize() < capship_size || (!cap)) && (fg == NULL ? true : fg->name != "Base")) { return target->isUnit() == Vega_UnitType::planet; @@ -1154,41 +1154,14 @@ void Unit::DamageRandSys(float dam, const Vector &vec, float randnum, float degr } else if (randnum >= .775) { computer.itts = false; //Set the computer to not have an itts } else if (randnum >= .7) { - // Gradually degrade radar capabilities - typedef Computer::RADARLIM::Capability Capability; - int &capability = computer.radar.capability; - if (capability & Capability::IFF_THREAT_ASSESSMENT) { - capability &= ~Capability::IFF_THREAT_ASSESSMENT; - } else if (capability & Capability::IFF_OBJECT_RECOGNITION) { - capability &= ~Capability::IFF_OBJECT_RECOGNITION; - } else if (capability & Capability::IFF_FRIEND_FOE) { - capability &= ~Capability::IFF_FRIEND_FOE; - } + radar.Damage(); } else if (randnum >= .5) { //THIS IS NOT YET SUPPORTED IN NETWORKING computer.target = nullptr; //set the target to NULL } else if (randnum >= .4) { limits.retro *= dam; - } else if (randnum >= .3275) { - const float maxdam = configuration()->physics_config.max_radar_cone_damage; - computer.radar.maxcone += (1 - dam); - if (computer.radar.maxcone > maxdam) { - computer.radar.maxcone = maxdam; - } - } else if (randnum >= .325) { - const float maxdam = configuration()->physics_config.max_radar_lock_cone_damage; - computer.radar.lockcone += (1 - dam); - if (computer.radar.lockcone > maxdam) { - computer.radar.lockcone = maxdam; - } - } else if (randnum >= .25) { - const float maxdam = configuration()->physics_config.max_radar_track_cone_damage; - computer.radar.trackingcone += (1 - dam); - if (computer.radar.trackingcone > maxdam) { - computer.radar.trackingcone = maxdam; - } } else if (randnum >= .175) { - computer.radar.maxrange *= dam; + radar.Damage(); } else { int which = rand() % (1 + UnitImages::NUMGAUGES + MAXVDUS); pImage->cockpit_damage[which] *= dam; @@ -1621,7 +1594,7 @@ void Unit::Target(Unit *targ) { } computer.target.SetUnit(targ); - LockTarget(false); + radar.Unlock(); } } else { if (jump.drive != -1) { @@ -3241,14 +3214,14 @@ bool Unit::UpAndDownGrade(const Unit *up, "Radar_Range|Radar_Color|ITTS|Can_Lock|Max_Cone|Lock_Cone|Tracking_Cone")) { if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Radar_Range")) { - STDUPGRADECLAMP(computer.radar.maxrange, - up->computer.radar.maxrange, - use_template_maxrange ? templ->computer.radar.maxrange : FLT_MAX, + STDUPGRADECLAMP(radar.max_range, + up->radar.max_range, + use_template_maxrange ? templ->radar.max_range : FLT_MAX, 0); } if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Radar_Color")) - STDUPGRADE(computer.radar.capability, up->computer.radar.capability, templ->computer.radar.capability, 0); + STDUPGRADE(radar.capabilities, up->radar.capabilities, templ->radar.capabilities, 0); if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "ITTS")) { computer.itts = UpgradeBoolval(computer.itts, @@ -3261,8 +3234,8 @@ bool Unit::UpAndDownGrade(const Unit *up, } if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Can_Lock")) { - computer.radar.canlock = UpgradeBoolval(computer.radar.canlock, - up->computer.radar.canlock, + radar.can_lock = UpgradeBoolval(radar.can_lock, + up->radar.can_lock, touchme, downgrade, numave, @@ -3273,41 +3246,41 @@ bool Unit::UpAndDownGrade(const Unit *up, bool ccf = cancompletefully; if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Max_Cone")) { - double myleak = 1 - computer.radar.maxcone; - double upleak = 1 - up->computer.radar.maxcone; - double templeak = 1 - (templ != NULL ? templ->computer.radar.maxcone : -1); - STDUPGRADE_SPECIFY_DEFAULTS(myleak, upleak, templeak, 0, 0, 0, false, computer.radar.maxcone); + double myleak = 1 - radar.max_cone; + double upleak = 1 - up->radar.max_cone; + double templeak = 1 - (templ != NULL ? templ->radar.max_cone : -1); + STDUPGRADE_SPECIFY_DEFAULTS(myleak, upleak, templeak, 0, 0, 0, false, radar.max_cone); if (touchme) { - computer.radar.maxcone = 1 - myleak; + radar.max_cone = 1 - myleak; } } - if (up->computer.radar.lockcone != lc) { - double myleak = 1 - computer.radar.lockcone; - double upleak = 1 - up->computer.radar.lockcone; - double templeak = 1 - (templ != NULL ? templ->computer.radar.lockcone : -1); + if (up->radar.lock_cone != lc) { + double myleak = 1 - radar.lock_cone; + double upleak = 1 - up->radar.lock_cone; + double templeak = 1 - (templ != NULL ? templ->radar.lock_cone : -1); if (templeak == 1 - lc) { templeak = 2; } if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Lock_Cone")) { - STDUPGRADE_SPECIFY_DEFAULTS(myleak, upleak, templeak, 0, 0, 0, false, computer.radar.lockcone); + STDUPGRADE_SPECIFY_DEFAULTS(myleak, upleak, templeak, 0, 0, 0, false, radar.lock_cone); if (touchme) { - computer.radar.lockcone = 1 - myleak; + radar.lock_cone = 1 - myleak; } } } - if (up->computer.radar.trackingcone != tc) { - double myleak = 1 - computer.radar.trackingcone; - double upleak = 1 - up->computer.radar.trackingcone; - double templeak = 1 - (templ != NULL ? templ->computer.radar.trackingcone : -1); + if (up->radar.tracking_cone != tc) { + double myleak = 1 - radar.tracking_cone; + double upleak = 1 - up->radar.tracking_cone; + double templeak = 1 - (templ != NULL ? templ->radar.tracking_cone : -1); if (templeak == 1 - tc) { templeak = 2; } if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Tracking_Cone")) { - STDUPGRADE_SPECIFY_DEFAULTS(myleak, upleak, templeak, 0, 0, 0, false, computer.radar.trackingcone); + STDUPGRADE_SPECIFY_DEFAULTS(myleak, upleak, templeak, 0, 0, 0, false, radar.tracking_cone); if (touchme) { - computer.radar.trackingcone = 1 - myleak; + radar.tracking_cone = 1 - myleak; } } } @@ -4403,7 +4376,7 @@ void Unit::UpdatePhysics3(const Transformation &trans, Vector TargetPos(InvTransform(cumulative_transformation_matrix, (target->Position())).Cast()); dist_sqr_to_target = TargetPos.MagnitudeSquared(); TargetPos.Normalize(); - if (TargetPos.k > computer.radar.lockcone) { + if (TargetPos.k > radar.lock_cone) { increase_locking = true; } } @@ -4505,23 +4478,26 @@ void Unit::UpdatePhysics3(const Transformation &trans, } if (mounts[i].type->type == WEAPON_TYPE::BEAM) { if (mounts[i].ref.gun) { - Unit *autotarg = - (isAutoTrackingMount(mounts[i].size) - && (mounts[i].time_to_lock <= 0) - && TargetTracked()) ? target : NULL; - float trackingcone = computer.radar.trackingcone; - if (CloseEnoughToAutotrack(this, target, trackingcone)) { + bool autoTrack = isAutoTrackingMount(mounts[i].size); + bool timeLocked = mounts[i].time_to_lock <= 0; + bool tracked = TargetTracked(); + Unit *autotarg = (autoTrack && timeLocked && tracked) ? target : nullptr; + + float tracking_cone = radar.tracking_cone; + // TODO: fix this or remove + /*if (CloseEnoughToAutotrack(this, target, tracking_cone)) { if (autotarg) { - if (computer.radar.trackingcone < trackingcone) { - trackingcone = computer.radar.trackingcone; + if (radar.tracking_cone < tracking_cone) { + tracking_cone = radar.tracking_cone; } } autotarg = target; - } + }*/ + mounts[i].ref.gun->UpdatePhysics(cumulative_transformation, cumulative_transformation_matrix, autotarg, - trackingcone, + tracking_cone, target, (HeatSink ? HeatSink : 1.0f) * mounts[i].functionality, this, @@ -4541,11 +4517,11 @@ void Unit::UpdatePhysics3(const Transformation &trans, && TargetTracked()) { autotrack = computer.itts ? 2 : 1; } - float trackingcone = computer.radar.trackingcone; - if (CloseEnoughToAutotrack(this, target, trackingcone)) { + float tracking_cone = radar.tracking_cone; + if (CloseEnoughToAutotrack(this, target, tracking_cone)) { if (autotrack) { - if (trackingcone > computer.radar.trackingcone) { - trackingcone = computer.radar.trackingcone; + if (tracking_cone > radar.tracking_cone) { + tracking_cone = radar.tracking_cone; } } autotrack = 2; @@ -4559,7 +4535,7 @@ void Unit::UpdatePhysics3(const Transformation &trans, } if (!mounts[i].PhysicsAlignedFire(this, t1, m1, cumulative_velocity, (!isSubUnit() || owner == NULL) ? this : owner, target, autotrack, - trackingcone, + tracking_cone, hint)) { const WeaponInfo *typ = mounts[i].type; energy += typ->energy_rate * (typ->type == WEAPON_TYPE::BEAM ? simulation_atom_var : 1); diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index 6cf18bda56..2147847efa 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -77,6 +77,7 @@ void UncheckUnit( class Unit*un ); #include "role_bitmask.h" #include "upgradeable_unit.h" #include "cloak.h" +#include "components/radar.h" #include "configuration/configuration.h" #include "configuration/game_config.h" @@ -480,6 +481,7 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p bool DoSightAndSound) override; Computer computer; + CRadar radar; void SwitchCombatFlightMode(); bool CombatMode(); diff --git a/engine/src/components/component.cpp b/engine/src/components/component.cpp new file mode 100644 index 0000000000..556bf81a7b --- /dev/null +++ b/engine/src/components/component.cpp @@ -0,0 +1,45 @@ +/* + * component.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "component.h" + +#include + +/*Component::Component() +{ + +}*/ + + +double random20() +{ + if(std::rand() < 0.2) { + return std::rand(); + } + + return 1.0; +} diff --git a/engine/src/components/component.h b/engine/src/components/component.h new file mode 100644 index 0000000000..58a0fd0c53 --- /dev/null +++ b/engine/src/components/component.h @@ -0,0 +1,48 @@ +/* + * component.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef COMPONENT_H +#define COMPONENT_H + + +/*class Component +{ +public: + Component(); + virtual ~Component() {} + + virtual void damage(); + virtual void fix(); + + +};*/ + +// These functions reduce functionality by a uniform distribution 0-1. +// The function name's number component is the chance of the damage occurring. +double random20(); + +#endif // COMPONENT_H diff --git a/engine/src/components/radar.cpp b/engine/src/components/radar.cpp new file mode 100644 index 0000000000..7d381c20db --- /dev/null +++ b/engine/src/components/radar.cpp @@ -0,0 +1,207 @@ +/* + * radar.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "radar.h" +#include "configuration/configuration.h" +#include "unit_generic.h" +#include "unit_util.h" +#include "unit_csv_factory.h" +#include "components/component.h" + +#include + +CRadar::CRadar(): + max_range(0), + max_cone(-1), + lock_cone(0), + tracking_cone(0), + min_target_size(0), + type(RadarType::SPHERE), + capabilities(RadarCapabilities::NONE), + locked(false), + can_lock(false), + tracking_active(true), + original(nullptr), + computer(nullptr) +{ + + max_range = configuration()->computer_config.default_max_range; + tracking_cone = configuration()->computer_config.default_tracking_cone; + lock_cone = configuration()->computer_config.default_lock_cone; +} + +CRadar::CRadar(std::string unit_key, Computer* computer) { + can_lock = UnitCSVFactory::GetVariable(unit_key, "Can_Lock", true); + + // TODO: fix this + // The Radar_Color column in the units.csv has been changed from a + // boolean value to a string. The boolean values are supported for + // backwardscompatibility. + // When we save this setting, it is simply converted from an integer + // number to a string, and we need to support this as well. + std::string iffval = UnitCSVFactory::GetVariable(unit_key, "Radar_Color", std::string()); + + if ((iffval.empty()) || (iffval == "FALSE") || (iffval == "0")) { + capabilities = RadarCapabilities::NONE; + } else if ((iffval == "TRUE") || (iffval == "1")) { + type = RadarType::SPHERE; + capabilities = RadarCapabilities::FRIEND_FOE; + } else if (iffval == "THREAT") { + type = RadarType::SPHERE; + capabilities = RadarCapabilities::FRIEND_FOE | + RadarCapabilities::THREAT_ASSESSMENT; + } else if (iffval == "BUBBLE_THREAT") { + type = RadarType::BUBBLE; + capabilities = RadarCapabilities::FRIEND_FOE | + RadarCapabilities::OBJECT_RECOGNITION | + RadarCapabilities::THREAT_ASSESSMENT; + } else if (iffval == "PLANE") { + type = RadarType::PLANE; + capabilities = RadarCapabilities::FRIEND_FOE; + } else if (iffval == "PLANE_THREAT") { + type = RadarType::PLANE; + capabilities = RadarCapabilities::FRIEND_FOE | + RadarCapabilities::OBJECT_RECOGNITION | + RadarCapabilities::THREAT_ASSESSMENT; + } else { + unsigned int value = stoi(iffval, 0); + if (value == 0) { + // Unknown value + capabilities = RadarCapabilities::NONE; + } else { + capabilities = value; + } + } + + tracking_active = true; + max_range = UnitCSVFactory::GetVariable(unit_key, "Radar_Range", FLT_MAX); + max_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Max_Cone", 180.0f) * VS_PI / 180); + tracking_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Tracking_Cone", 180.0f) * VS_PI / 180); + lock_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Lock_Cone", 180.0f) * VS_PI / 180); + original = nullptr; + this->computer = computer; +} + +void CRadar::WriteUnitString(std::map &unit) { + unit["Can_Lock"] = std::to_string(can_lock); + unit["Radar_Color"] = std::to_string(capabilities); + unit["Radar_Range"] = std::to_string(max_range); + unit["Tracking_Cone"] = std::to_string(acos(tracking_cone) * 180. / VS_PI); + unit["Max_Cone"] = std::to_string(acos(max_cone) * 180. / VS_PI); + unit["Lock_Cone"] = std::to_string(acos(lock_cone) * 180. / VS_PI); +} + +void CRadar::CRadar::Damage() +{ + std::random_device rd; // a seed source for the random number engine + std::mt19937 gen(rd()); // mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> damage_distribution(0, 6); + std::uniform_int_distribution<> size_distribution(0, 6); + + // Damage IFF capabilities + if(std::rand() < 0.2) { + // TODO: make this smarter and maybe degrade capabilities + capabilities = NONE; + } + + max_range = max_range * random20(); + max_cone = max_cone * random20(); + lock_cone = lock_cone * random20(); + tracking_cone = tracking_cone * random20(); + min_target_size = min_target_size * random20(); + + // Original cone damage + /*const float maxdam = configuration()->physics_config.max_radar_cone_damage; + radar.max_cone += (1 - dam); + if (radar.max_cone > maxdam) { + radar.max_cone = maxdam; + } + + const float maxdam = configuration()->physics_config.max_radar_lock_cone_damage; + radar.lock_cone += (1 - dam); + if (radar.lock_cone > maxdam) { + radar.lock_cone = maxdam; + } + + const float maxdam = configuration()->physics_config.max_radar_track_cone_damage; + radar.tracking_cone += (1 - dam); + if (radar.tracking_cone > maxdam) { + radar.tracking_cone = maxdam; + }*/ + +} + +void CRadar::CRadar::Repair() +{ + +} + +// This code replaces and fixes the old code in Armed::LockTarget(bool) +void CRadar::Lock() { + if(!computer) { + return; + } + + const Unit *target = computer->target.GetConstUnit(); + + if(!target) { + //std::cerr << "Target is null\n"; + return; + } + + if(!can_lock) { + std::cerr << "Can't lock\n"; + this->locked = false; + return; + } + + if(!UnitUtil::isSignificant(target)) { + std::cerr << "Target insignificant\n"; + this->locked = false; + return; + } + + std::cout << "Target locked\n"; + this->locked = true; + +} + +RadarType CRadar::GetType() const { + return type; +} + +bool CRadar::UseFriendFoe() const { + return (capabilities & RadarCapabilities::FRIEND_FOE); +} + +bool CRadar::UseObjectRecognition() const { + return (capabilities & RadarCapabilities::OBJECT_RECOGNITION); +} + +bool CRadar::UseThreatAssessment() const { + return (capabilities & RadarCapabilities::THREAT_ASSESSMENT); +} diff --git a/engine/src/components/radar.h b/engine/src/components/radar.h new file mode 100644 index 0000000000..d6ece020b7 --- /dev/null +++ b/engine/src/components/radar.h @@ -0,0 +1,117 @@ +/* + * radar.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef RADAR_H +#define RADAR_H + +#include +#include +#include +#include + +#include "computer.h" + +class Unit; + +enum class RadarType { + SPHERE, + BUBBLE, + PLANE +}; + +enum RadarCapabilities { + NONE = 0, + FRIEND_FOE = 1, + OBJECT_RECOGNITION = 2, + THREAT_ASSESSMENT = 4 +}; + +// Can't call it radar because of namespace collision +class CRadar +{ + // TODO: move floats to doubles + //the max range the radar can handle + float max_range; + + //the dot with (0,0,1) indicating the farthest to the side the radar can handle. + float max_cone; + float lock_cone; + float tracking_cone; + + //The minimum radius of the target + float min_target_size; + + // What kind of type and capability the radar supports + RadarType type; + unsigned int capabilities; + bool locked; + bool can_lock; + bool tracking_active; + + std::unique_ptr original; + + Computer *computer; + + friend class Armed; + friend class Unit; +public: + CRadar(); + CRadar(std::string unit_key, Computer* computer); + + void WriteUnitString(std::map &unit); + + void Damage(); + void Repair(); + + void Lock(); + void Unlock() { + locked = false; + } + void ToggleLock() { + locked = !locked; + } + + void Track(const bool track) { tracking_active = track; } + void ToggleTracking() { tracking_active = !tracking_active; } + + RadarType GetType() const; // Formerly GetBrand + bool UseFriendFoe() const; + bool UseObjectRecognition() const; + bool UseThreatAssessment() const; + + float GetMaxRange() const { return max_range; } + float GetMaxCone() const { return max_cone; } + float GetLockCone() const { return lock_cone; } + float GetTrackingCone() const { return tracking_cone; } + float GetMinTargetSize() const { return min_target_size; } + + bool Locked() const { return locked; } + bool CanLock() const { return can_lock; } + bool Tracking() const { return tracking_active; } +}; + +#endif // RADAR_H diff --git a/engine/src/gfx/cockpit.cpp b/engine/src/gfx/cockpit.cpp index fc4dcb7694..d36a842792 100644 --- a/engine/src/gfx/cockpit.cpp +++ b/engine/src/gfx/cockpit.cpp @@ -2702,26 +2702,29 @@ void GameCockpit::OnPauseEnd() { } void GameCockpit::updateRadar(Unit *ship) { - if (ship) { - // We may have bought a new radar brand while docked, so the actual - // radar display is instantiated when we undock. - switch (ship->GetComputerData().radar.GetBrand()) { - case Computer::RADARLIM::Brand::BUBBLE: - radarDisplay = Radar::Factory(Radar::Type::BubbleDisplay); - break; + if (!ship) { + return; + } - case Computer::RADARLIM::Brand::PLANE: - radarDisplay = Radar::Factory(Radar::Type::PlaneDisplay); - break; + // We may have bought a new radar brand while docked, so the actual + // radar display is instantiated when we undock. + RadarType type = ship->radar.GetType(); + Radar::Type::Value displayType = Radar::Type::Value::NullDisplay; - default: - radarDisplay = Radar::Factory(Radar::Type::SphereDisplay); - break; - } - // Send notification that I have undocked - radarDisplay->OnDockEnd(); + if(type == RadarType::BUBBLE) { + displayType = Radar::Type::BubbleDisplay; + } else if(type == RadarType::PLANE) { + displayType = Radar::Type::PlaneDisplay; + } else if(type == RadarType::SPHERE) { + displayType = Radar::Type::SphereDisplay; + } + + if(displayType != Radar::Type::Value::NullDisplay) { + radarDisplay = Radar::Factory(displayType); } + // Send notification that I have undocked + radarDisplay->OnDockEnd(); } void GameCockpit::SetParent(Unit *unit, const char *filename, const char *unitmodname, const QVector &startloc) { diff --git a/engine/src/gfx/nav/drawsystem.cpp b/engine/src/gfx/nav/drawsystem.cpp index 13d04729fd..fd50b9626f 100644 --- a/engine/src/gfx/nav/drawsystem.cpp +++ b/engine/src/gfx/nav/drawsystem.cpp @@ -449,7 +449,7 @@ void NavigationSystem::DrawSystem() { //JUST FOR NOW, target == current selection. later it'll be used for other shit, that will then set target. if (currentselection.GetUnit()) { (UniverseUtil::getPlayerX(UniverseUtil::getCurrentPlayer()))->Target(currentselection.GetUnit()); - (UniverseUtil::getPlayerX(UniverseUtil::getCurrentPlayer()))->LockTarget(currentselection.GetUnit()); + (UniverseUtil::getPlayerX(UniverseUtil::getCurrentPlayer()))->radar.Lock(); } } } diff --git a/engine/src/gfx/radar/sensor.cpp b/engine/src/gfx/radar/sensor.cpp index dd9c874fe1..f6eb812c1a 100644 --- a/engine/src/gfx/radar/sensor.cpp +++ b/engine/src/gfx/radar/sensor.cpp @@ -51,19 +51,19 @@ Unit *Sensor::GetPlayer() const { bool Sensor::UseFriendFoe() const { assert(player); - return player->GetComputerData().radar.UseFriendFoe(); + return player->radar.UseFriendFoe(); } bool Sensor::UseObjectRecognition() const { assert(player); - return player->GetComputerData().radar.UseObjectRecognition(); + return player->radar.UseObjectRecognition(); } bool Sensor::UseThreatAssessment() const { assert(player); - return player->GetComputerData().radar.UseThreatAssessment(); + return player->radar.UseThreatAssessment(); } float Sensor::GetCloseRange() const { @@ -73,19 +73,19 @@ float Sensor::GetCloseRange() const { float Sensor::GetMaxRange() const { assert(player); - return player->GetComputerData().radar.maxrange; + return player->radar.GetMaxRange(); } float Sensor::GetMaxCone() const { assert(player); - return player->GetComputerData().radar.maxcone; + return player->radar.GetMaxCone(); } float Sensor::GetLockCone() const { assert(player); - return player->GetComputerData().radar.lockcone; + return player->radar.GetLockCone(); } Track Sensor::CreateTrack(const Unit *target) const { @@ -154,7 +154,7 @@ class CollectRadarTracks { if (!isCurrentTarget && !draw_significant_blips && (getTopLevelOwner() == target->owner) && - (distance > player->GetComputerData().radar.maxrange)) { + (distance > player->radar.GetMaxRange())) { return true; } diff --git a/engine/src/gfx/radar/sensor.h b/engine/src/gfx/radar/sensor.h index 42b7cc6641..d6c17de359 100644 --- a/engine/src/gfx/radar/sensor.h +++ b/engine/src/gfx/radar/sensor.h @@ -36,6 +36,8 @@ class Unit; struct GFXColor; // Edit from class to struct as defined in gfxlib_struct. +// TODO: this is basically a wrapper over CRadar. Remove + namespace Radar { // Sensor is a proxy for two types of information: From 7c357ba945d6341dbead9d285ad28b40c3a92da7 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Thu, 26 Oct 2023 18:52:28 +0300 Subject: [PATCH 06/16] Fix bug in the radar where small ships are not tracked. --- engine/src/components/radar.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/engine/src/components/radar.cpp b/engine/src/components/radar.cpp index 7d381c20db..b8941a18cd 100644 --- a/engine/src/components/radar.cpp +++ b/engine/src/components/radar.cpp @@ -54,7 +54,19 @@ CRadar::CRadar(): lock_cone = configuration()->computer_config.default_lock_cone; } -CRadar::CRadar(std::string unit_key, Computer* computer) { +CRadar::CRadar(std::string unit_key, Computer* computer): + max_range(0), + max_cone(-1), + lock_cone(0), + tracking_cone(0), + min_target_size(0), + type(RadarType::SPHERE), + capabilities(RadarCapabilities::NONE), + locked(false), + can_lock(false), + tracking_active(true), + original(nullptr), + computer(nullptr) { can_lock = UnitCSVFactory::GetVariable(unit_key, "Can_Lock", true); // TODO: fix this @@ -99,9 +111,9 @@ CRadar::CRadar(std::string unit_key, Computer* computer) { tracking_active = true; max_range = UnitCSVFactory::GetVariable(unit_key, "Radar_Range", FLT_MAX); - max_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Max_Cone", 180.0f) * VS_PI / 180); + max_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Max_Cone", 180.0) * VS_PI / 180); tracking_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Tracking_Cone", 180.0f) * VS_PI / 180); - lock_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Lock_Cone", 180.0f) * VS_PI / 180); + lock_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Lock_Cone", 180.0) * VS_PI / 180); original = nullptr; this->computer = computer; } @@ -115,8 +127,9 @@ void CRadar::WriteUnitString(std::map &unit) { unit["Lock_Cone"] = std::to_string(acos(lock_cone) * 180. / VS_PI); } -void CRadar::CRadar::Damage() +void CRadar::Damage() { + return; std::random_device rd; // a seed source for the random number engine std::mt19937 gen(rd()); // mersenne_twister_engine seeded with rd() std::uniform_int_distribution<> damage_distribution(0, 6); @@ -155,7 +168,7 @@ void CRadar::CRadar::Damage() } -void CRadar::CRadar::Repair() +void CRadar::Repair() { } @@ -179,11 +192,11 @@ void CRadar::Lock() { return; } - if(!UnitUtil::isSignificant(target)) { + /*if(!UnitUtil::isSignificant(target)) { std::cerr << "Target insignificant\n"; this->locked = false; return; - } + }*/ std::cout << "Target locked\n"; this->locked = true; From 9d5551ec10dfb755baabe62a61bc670e6e15afb3 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Fri, 10 Nov 2023 22:08:29 +0200 Subject: [PATCH 07/16] Introduce lib_component Move a lot of code into individual components. --- engine/CMakeLists.txt | 16 +- engine/src/cmd/ai/aggressive.cpp | 11 +- engine/src/cmd/ai/docking.cpp | 10 +- engine/src/cmd/ai/fire.cpp | 2 +- engine/src/cmd/ai/firekeyboard.cpp | 8 +- engine/src/cmd/ai/flykeyboard.cpp | 2 +- engine/src/cmd/ai/ikarus.cpp | 2 +- engine/src/cmd/ai/warpto.cpp | 4 +- engine/src/cmd/armed.cpp | 10 +- engine/src/cmd/basecomputer.cpp | 98 +++-- engine/src/cmd/damageable.cpp | 16 +- engine/src/cmd/energetic.cpp | 382 ------------------ engine/src/cmd/energetic.h | 109 ----- engine/src/cmd/jump_capable.cpp | 20 +- engine/src/cmd/jump_capable.h | 16 +- engine/src/cmd/mount.cpp | 11 +- engine/src/cmd/movable.cpp | 130 ++---- engine/src/cmd/movable.h | 6 +- engine/src/cmd/planet.cpp | 2 +- .../cmd/script/script_call_unit_generic.cpp | 2 +- engine/src/cmd/unit_csv.cpp | 67 ++- engine/src/cmd/unit_functions_generic.cpp | 2 +- engine/src/cmd/unit_generic.cpp | 99 ++--- engine/src/cmd/unit_generic.h | 16 +- engine/src/cmd/weapon_factory.cpp | 7 +- engine/src/cmd/weapon_info.cpp | 17 +- engine/src/cmd/weapon_info.h | 5 +- engine/src/components/afterburner.cpp | 62 +++ engine/src/components/afterburner.h | 51 +++ engine/src/{cmd => components}/cloak.cpp | 33 +- engine/src/{cmd => components}/cloak.h | 7 +- engine/src/components/component.cpp | 4 +- engine/src/components/component.h | 21 +- engine/src/components/drive.cpp | 41 ++ engine/src/components/drive.h | 42 ++ engine/src/components/energy_consumer.cpp | 26 ++ engine/src/components/energy_consumer.h | 59 +++ engine/src/components/energy_container.cpp | 127 ++++++ engine/src/components/energy_container.h | 82 ++++ engine/src/components/energy_manager.cpp | 172 ++++++++ engine/src/components/energy_manager.h | 81 ++++ engine/src/components/energy_types.cpp | 46 +++ engine/src/components/energy_types.h | 74 ++++ engine/src/components/jump_drive.cpp | 47 +++ engine/src/components/jump_drive.h | 44 ++ engine/src/components/reactor.cpp | 71 ++++ engine/src/components/reactor.h | 53 +++ .../src/components/tests/balancing_tests.cpp | 154 +++++++ .../tests/energy_container_tests.cpp | 30 ++ engine/src/configuration/configuration.h | 4 +- engine/src/gfx/cockpit.cpp | 23 +- engine/src/gfx/cockpit_generic.cpp | 3 +- engine/src/gfx/cockpit_generic.h | 2 +- engine/src/python/unit_wrapper.cpp | 2 +- engine/src/python/unit_wrapper_class.h | 2 +- engine/src/resource/resource.cpp | 24 ++ engine/src/resource/resource.h | 3 + engine/src/resource/tests/resource_test.cpp | 3 + engine/src/star_system.cpp | 4 +- engine/src/star_system_jump.cpp | 4 +- 60 files changed, 1656 insertions(+), 815 deletions(-) delete mode 100644 engine/src/cmd/energetic.cpp delete mode 100644 engine/src/cmd/energetic.h create mode 100644 engine/src/components/afterburner.cpp create mode 100644 engine/src/components/afterburner.h rename engine/src/{cmd => components}/cloak.cpp (83%) rename engine/src/{cmd => components}/cloak.h (97%) create mode 100644 engine/src/components/drive.cpp create mode 100644 engine/src/components/drive.h create mode 100644 engine/src/components/energy_consumer.cpp create mode 100644 engine/src/components/energy_consumer.h create mode 100644 engine/src/components/energy_container.cpp create mode 100644 engine/src/components/energy_container.h create mode 100644 engine/src/components/energy_manager.cpp create mode 100644 engine/src/components/energy_manager.h create mode 100644 engine/src/components/energy_types.cpp create mode 100644 engine/src/components/energy_types.h create mode 100644 engine/src/components/jump_drive.cpp create mode 100644 engine/src/components/jump_drive.h create mode 100644 engine/src/components/reactor.cpp create mode 100644 engine/src/components/reactor.h create mode 100644 engine/src/components/tests/balancing_tests.cpp create mode 100644 engine/src/components/tests/energy_container_tests.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 7d35cb6145..612772461c 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -661,7 +661,17 @@ SET(LIBRESOURCE SET(LIBCOMPONENT src/components/component.cpp + src/components/energy_types.cpp + + src/components/afterburner.cpp + src/components/cloak.cpp + src/components/drive.cpp + src/components/energy_consumer.cpp + src/components/energy_container.cpp + src/components/energy_manager.cpp + src/components/jump_drive.cpp src/components/radar.cpp + src/components/reactor.cpp ) SET(LIBGUI_SOURCES @@ -833,8 +843,7 @@ SET(LIBCMD_SOURCES src/cmd/computer.cpp src/cmd/intelligent.cpp - src/cmd/energetic.cpp - src/cmd/cloak.cpp + src/cmd/planetary_orbit.cpp @@ -1628,12 +1637,15 @@ IF (USE_GTEST) src/resource/tests/buy_sell.cpp src/resource/tests/resource_test.cpp src/exit_unit_tests.cpp + src/components/tests/energy_container_tests.cpp + src/components/tests/balancing_tests.cpp ) ADD_LIBRARY(vegastrike-testing ${LIBCONFIG} ${LIBDAMAGE} ${LIBRESOURCE} + ${LIBCOMPONENT} ${LIBCMD_SOURCES} ${LIBVS_LOGGING} ) diff --git a/engine/src/cmd/ai/aggressive.cpp b/engine/src/cmd/ai/aggressive.cpp index 67d66d1765..e8c98f54bb 100644 --- a/engine/src/cmd/ai/aggressive.cpp +++ b/engine/src/cmd/ai/aggressive.cpp @@ -55,6 +55,7 @@ #include "docking.h" #include "star_system.h" #include "universe.h" +#include "jump_capable.h" extern double aggfire; @@ -1292,7 +1293,7 @@ static Unit *ChooseNavPoint(Unit *parent, Unit **otherdest, float *lurk_on_arriv int whichlist = 1; //friendly std::string fgname = UnitUtil::getFlightgroupName(parent); - bool insys = (parent->GetJumpStatus().drive == -2) || fgname.find(insysString) != std::string::npos; + bool insys = (parent->jump.drive == -2) || fgname.find(insysString) != std::string::npos; std::string::size_type whereconvoy = fgname.find(arrowString); bool convoy = (whereconvoy != std::string::npos); size_t total_size = stats->navs[0].size() + stats->navs[whichlist].size(); //friendly and neutral @@ -1640,9 +1641,9 @@ void AggressiveAI::Execute() { bool isjumpable = target ? (!target->GetDestinations().empty()) : false; if (!ProcessCurrentFgDirective(fg)) { if (isjumpable) { - if (parent->GetJumpStatus().drive < 0) { + if (parent->jump.drive < 0) { parent->ActivateJumpDrive(0); - if (parent->GetJumpStatus().drive == -2) { + if (parent->jump.drive == -2) { static bool AIjumpCheat = XMLSupport::parse_bool(vs_config->getVariable("AI", "always_have_jumpdrive_cheat", @@ -1657,7 +1658,7 @@ void AggressiveAI::Execute() { } else { parent->Target(NULL); } - } else if (parent->GetJumpStatus().drive < 0) { + } else if (parent->jump.drive < 0) { static bool AIjumpCheat = XMLSupport::parse_bool(vs_config->getVariable("AI", "jump_cheat", "true")); if (AIjumpCheat) { @@ -1733,7 +1734,7 @@ void AggressiveAI::Execute() { isjumpable = target ? (!target->GetDestinations().empty()) : false; if (!isjumpable) { - if (parent->GetJumpStatus().drive >= 0) { + if (parent->jump.drive >= 0) { parent->ActivateJumpDrive(-1); } } diff --git a/engine/src/cmd/ai/docking.cpp b/engine/src/cmd/ai/docking.cpp index de8e4e28b2..7b0b141e46 100644 --- a/engine/src/cmd/ai/docking.cpp +++ b/engine/src/cmd/ai/docking.cpp @@ -206,10 +206,7 @@ bool DockingOps::DockToTarget(Unit *utdw) { if (physicallyDock) { return parent->Dock(utdw); } else { - float maxWillingToRefill = utdw->warpCapData(); - if (maxWillingToRefill >= MinimumCapacityToRefuelOnLand) { - parent->refillWarpEnergy(); - } //BUCO! This needs its own units.csv column to see how much we refill! + utdw->energy_manager.Refill(EnergyType::SPEC); return true; } } else if (diss <= 1.2 * rad * rad) { @@ -219,10 +216,7 @@ bool DockingOps::DockToTarget(Unit *utdw) { if (physicallyDock) { return parent->Dock(utdw); } else { - float maxWillingToRefill = utdw->warpCapData(); - if (maxWillingToRefill >= MinimumCapacityToRefuelOnLand) { - parent->refillWarpEnergy(); - } //BUCO! This needs its own units.csv column to see how much we refill! + parent->energy_manager.Refill(EnergyType::SPEC); return true; } } diff --git a/engine/src/cmd/ai/fire.cpp b/engine/src/cmd/ai/fire.cpp index deaf06e95c..be49e5be6e 100644 --- a/engine/src/cmd/ai/fire.cpp +++ b/engine/src/cmd/ai/fire.cpp @@ -773,7 +773,7 @@ bool FireAt::isJumpablePlanet(Unit *targ) { bool istargetjumpableplanet = targ->isUnit() == Vega_UnitType::planet; if (istargetjumpableplanet) { istargetjumpableplanet = - (!((Planet *) targ)->GetDestinations().empty()) && (parent->GetJumpStatus().drive >= 0); + (!((Planet *) targ)->GetDestinations().empty()) && (parent->jump.drive >= 0); } return istargetjumpableplanet; } diff --git a/engine/src/cmd/ai/firekeyboard.cpp b/engine/src/cmd/ai/firekeyboard.cpp index 62ecc84122..79c2426250 100644 --- a/engine/src/cmd/ai/firekeyboard.cpp +++ b/engine/src/cmd/ai/firekeyboard.cpp @@ -2176,9 +2176,11 @@ void FireKeyboard::Execute() { cp->EjectDock(); } //use specialized ejectdock in the future } - static bool actually_arrest = XMLSupport::parse_bool(vs_config->getVariable("AI", "arrest_energy_zero", "false")); - if (actually_arrest && parent->energyRechargeData() == 0) { + + // TODO: something with this. It's very unlikely reactor will be 0 + /*static bool actually_arrest = XMLSupport::parse_bool(vs_config->getVariable("AI", "arrest_energy_zero", "false")); + if (actually_arrest && parent->energy_manager.GetReactorCapacity() == 0) { Arrested(parent); - } + }*/ } diff --git a/engine/src/cmd/ai/flykeyboard.cpp b/engine/src/cmd/ai/flykeyboard.cpp index 8846f9ad83..f3d47bf84c 100644 --- a/engine/src/cmd/ai/flykeyboard.cpp +++ b/engine/src/cmd/ai/flykeyboard.cpp @@ -453,7 +453,7 @@ void FlyByKeyboard::Execute(bool resetangvelocity) { if ((counter - last_jumped) > static_cast(jump_key_delay / SIMULATION_ATOM) || last_jumped == 0) { last_jumped = counter; parent->ActivateJumpDrive(); - if (parent->GetJumpStatus().drive >= 0) { + if (parent->jump.drive >= 0) { static soundContainer foobar; if (foobar.sound == -2) { static string str = vs_config->getVariable("cockpitaudio", "jump_engaged", "jump"); diff --git a/engine/src/cmd/ai/ikarus.cpp b/engine/src/cmd/ai/ikarus.cpp index 1c4fa38307..cba8d09751 100644 --- a/engine/src/cmd/ai/ikarus.cpp +++ b/engine/src/cmd/ai/ikarus.cpp @@ -113,7 +113,7 @@ void Ikarus::Execute() { DecideTarget(); if (!ProcessCurrentFgDirective(fg)) { Unit *target = parent->Target(); - bool isjumpable = target ? ((!target->GetDestinations().empty()) && parent->GetJumpStatus().drive >= 0) : false; + bool isjumpable = target ? ((!target->GetDestinations().empty()) && parent->jump.drive >= 0) : false; if (isjumpable) { AfterburnTurnTowards(this, parent); } else { diff --git a/engine/src/cmd/ai/warpto.cpp b/engine/src/cmd/ai/warpto.cpp index 5f3232ed24..51a13de80d 100644 --- a/engine/src/cmd/ai/warpto.cpp +++ b/engine/src/cmd/ai/warpto.cpp @@ -92,11 +92,13 @@ static void ActuallyWarpTo(Unit *parent, const QVector &tarpos, Vector tarvel, U dir *= -1; float chasedot = dir.Dot(tarvel); if (dirveldot > mindirveldot) { + // TODO: this needs to be factored for mass and consumption. + // 0.33 and 1.5 are low values for larger ships static float min_energy_to_enter_warp = XMLSupport::parse_float(vs_config->getVariable("AI", "min_energy_to_enter_warp", ".33")); static float min_warpfield_to_enter_warp = XMLSupport::parse_float(vs_config->getVariable("AI", "min_warp_to_try", "1.5")); - if ((parent->warpEnergyData() > min_energy_to_enter_warp) + if ((parent->energy_manager.Percent(EnergyType::SPEC) > min_energy_to_enter_warp) && (parent->GetMaxWarpFieldStrength(1) > min_warpfield_to_enter_warp)) { if (parent->graphicOptions.InWarp == 0) { parent->graphicOptions.InWarp = 1; //don't want the AI thrashing diff --git a/engine/src/cmd/armed.cpp b/engine/src/cmd/armed.cpp index 83826132c7..154e41c17c 100644 --- a/engine/src/cmd/armed.cpp +++ b/engine/src/cmd/armed.cpp @@ -196,6 +196,7 @@ void Armed::ActivateGuns(const WeaponInfo *sz, bool ms) { } } +// Called from firekeyboard::execute void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { Unit *unit = static_cast(this); @@ -250,14 +251,14 @@ void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { //&& ( (ROLES::EVERYTHING_ELSE&weapon_type_bitmask&i->type->role_bits) || i->type->role_bits == 0 ) ((locked_on && missile_and_want_to_fire_missiles) || gun_and_want_to_fire_guns); if ((*i).type->type == WEAPON_TYPE::BEAM) { - if ((*i).type->energy_rate * simulation_atom_var > unit->energy) { + if ((*i).type->GetConsumption() * simulation_atom_var > unit->energy_manager.GetLevel(EnergyType::Energy)) { //NOT ONLY IN non-networking mode : anyway, the server will tell everyone including us to stop if not already done (*i).UnFire(); continue; } } else //Only in non-networking mode - if (i->type->energy_rate > unit->energy) { + if (i->type->GetConsumption() > unit->energy_manager.GetLevel(EnergyType::Energy)) { if (!want_to_fire) { i->UnFire(); } @@ -277,11 +278,12 @@ void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { if (i->type->type == WEAPON_TYPE::BEAM) { if (i->ref.gun) { if ((!i->ref.gun->Dissolved()) || i->ref.gun->Ready()) { - unit->energy -= i->type->energy_rate * simulation_atom_var; + // TODO: switch to standard energy usage + unit->energy_manager.Deplete(EnergyType::Energy, i->type->GetConsumption() * simulation_atom_var); } } } else if (i->type->isMissile()) { // FIXME other than beams, only missiles are processed here? - unit->energy -= i->type->energy_rate; + unit->energy_manager.Deplete(EnergyType::Energy, i->type->GetConsumption()); } //IF WE REFRESH ENERGY FROM SERVER : Think to send the energy update to the firing client with ACK TO fireRequest //fire only 1 missile at a time diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index ea05716e0f..39f299c6b4 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -84,6 +84,18 @@ using namespace XMLSupport; // FIXME -- Shouldn't include an entire namespace, a //end for directory thing extern const char *DamagedCategory; +// TODO: find a better home for this function +// Basically max or current shield x 0.2 +float totalShieldEnergyCapacitance(Unit *unit) { + 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); +} + + int BaseComputer::dirty = 0; static GFXColor UnsaturatedColor(float r, float g, float b, float a = 1.0f) { @@ -5314,20 +5326,22 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C break; } } - const Unit::UnitJump &uj = playerUnit->GetJumpStatus(); - const Unit::UnitJump &buj = blankUnit->GetJumpStatus(); + const Unit::UnitJump &uj = playerUnit->jump; + const Unit::UnitJump &buj = blankUnit->jump; if (!mode) { - float maxshield = playerUnit->totalShieldEnergyCapacitance(); + float maxshield = totalShieldEnergyCapacitance(playerUnit); if (shields_require_power) { maxshield = 0; } - PRETTY_ADDU(statcolor + "Recharge: #-c", playerUnit->energyRechargeData() * RSconverter, 0, "MJ/s"); + PRETTY_ADDU(statcolor + "Recharge: #-c", playerUnit->energy_manager.GetReactorCapacity() * RSconverter, 0, "MJ/s"); PRETTY_ADDU(statcolor + "Weapon capacitor bank storage: #-c", - ((playerUnit->maxEnergyData() - maxshield) * RSconverter), 0, "MJ"); + // TODO: this should be converted to variable vs constant + // Also, this is a shitty calculation. Why subtract shields and not ECM or life support? + ((playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) - maxshield) * RSconverter), 0, "MJ"); //note: I found no function to get max warp energy, but since we're docked they are the same if (!subunitlevel) { PRETTY_ADDU(statcolor + "Warp capacitor bank storage: #-c", - playerUnit->warpCapData() * RSconverter * Wconv, + playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) * RSconverter * Wconv, 0, "MJ"); @@ -5350,7 +5364,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C PRETTY_ADDU(statcolor + "Delay: #-c", uj.delay, 0, "seconds"); if (uj.damage > 0) PRETTY_ADDU(statcolor + "Damage to outsystem jump drive: #-c", uj.damage * VSDM, 0, "MJ"); - if (playerUnit->warpCapData() < uj.energy) { + if (playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) < uj.energy) { text += "#n##c1:.3:.3#" + prefix + "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c"; @@ -5360,45 +5374,45 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } else { switch (replacement_mode) { case 0: //Replacement or new Module - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energyRechargeData())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) PRETTY_ADDU(statcolor + "Installs reactor with recharge rate: #-c", - playerUnit->energyRechargeData() * RSconverter, 0, "MJ/s"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, maxEnergyData())) + playerUnit->energy_manager.GetReactorCapacity() * RSconverter, 0, "MJ/s"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) PRETTY_ADDU(statcolor + "Installs main capacitor bank with storage capacity: #-c", - (playerUnit->maxEnergyData() * RSconverter), 0, "MJ"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, getWarpEnergy())) + (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) * RSconverter), 0, "MJ"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::SPEC))) PRETTY_ADDU(statcolor + "Installs warp capacitor bank with storage capacity: #-c", - playerUnit->getWarpEnergy() * RSconverter * Wconv, 0, "MJ"); + playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) * RSconverter * Wconv, 0, "MJ"); if (buj.drive != uj.drive) { text += statcolor + "#n#Allows travel via Jump Points.#n#Consult your personal info screen for ship specific energy requirements. #-c"; } break; case 1: //Additive - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energyRechargeData())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) PRETTY_ADDU(statcolor + "Increases recharge rate by #-c", - playerUnit->energyRechargeData() * RSconverter, 0, "MJ/s"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, maxEnergyData())) + playerUnit->energy_manager.GetReactorCapacity() * RSconverter, 0, "MJ/s"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) PRETTY_ADDU(statcolor + "Adds #-c", - (playerUnit->maxEnergyData() * RSconverter), + (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) * RSconverter), 0, "MJ of storage to main capacitor banks"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, getWarpEnergy())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::SPEC))) PRETTY_ADDU(statcolor + "Adds #-c", - playerUnit->getWarpEnergy() * RSconverter * Wconv, + playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) * RSconverter * Wconv, 0, "MJ of storage to warp capacitor bank"); break; case 2: //multiplicative - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energyRechargeData())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) PRETTY_ADDU(statcolor + "Increases reactor recharge rate by #-c", - 100.0 * (playerUnit->energyRechargeData() - 1), 0, "%"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, maxEnergyData())) + 100.0 * (playerUnit->energy_manager.GetReactorCapacity() - 1), 0, "%"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) PRETTY_ADDU(statcolor + "Increases main capacitor bank storage by #-c", - 100.0 * (playerUnit->maxEnergyData() - 1), 0, "%"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, getWarpEnergy())) + 100.0 * (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) - 1), 0, "%"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::SPEC))) PRETTY_ADDU(statcolor + "Increases warp capacitor bank storage by #-c", - (playerUnit->getWarpEnergy() - 1) * 100, 0, "%"); + (playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) - 1) * 100, 0, "%"); break; default: //Failure text += "Oh dear, this wasn't an upgrade. Please debug code."; @@ -5640,7 +5654,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C wi->type == WEAPON_TYPE::BEAM ? "MJ/s" : "MJ"); } PRETTY_ADDU(statcolor + " Energy usage: #-c", - wi->energy_rate * RSconverter, + wi->GetConsumption() * RSconverter, 0, wi->type == WEAPON_TYPE::BEAM ? "MJ/s" : "MJ/shot"); PRETTY_ADDU(statcolor + " Refire delay: #-c", wi->Refire(), 2, "seconds"); @@ -5673,7 +5687,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C else PRETTY_ADD(statcolor + " Rockets remaining: #-c", playerUnit->mounts[i].ammo, 0); } - totalWeaponEnergyUsage += (wi->energy_rate / wi->Refire()); + totalWeaponEnergyUsage += (wi->GetConsumption() / wi->Refire()); break; case WEAPON_TYPE::PROJECTILE: //need ammo if (wi->lock_time > 0) { @@ -5688,7 +5702,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C + " Missile Lock Type: #-c#c1:.3:.3#None.#-c Inertial Guidance Only"; } PRETTY_ADD(statcolor + " Missiles remaining: #-c", playerUnit->mounts[i].ammo, 0); - totalWeaponEnergyUsage += (wi->energy_rate / wi->Refire()); + totalWeaponEnergyUsage += (wi->GetConsumption() / wi->Refire()); break; case WEAPON_TYPE::BEAM: if (wi->damage > 0) { @@ -5700,7 +5714,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C PRETTY_ADDU(statcolor + " Beam stability: #-c", wi->stability, 2, "seconds"); if (playerUnit->mounts[i].ammo != -1) PRETTY_ADD(statcolor + " Shots remaining: #-c", playerUnit->mounts[i].ammo, 0); - totalWeaponEnergyUsage += wi->energy_rate; + totalWeaponEnergyUsage += wi->GetConsumption(); break; default: break; @@ -5708,7 +5722,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C if ((mode != 0) && (wi->type != WEAPON_TYPE::PROJECTILE) && (wi->Refire() > 0) && - ((wi->damage != 0) || (wi->phase_damage != 0) || (wi->energy_rate != 0))) { + ((wi->damage != 0) || (wi->phase_damage != 0) || (wi->GetConsumption() != 0))) { text += "#n##n#" + prefix + statcolor + " Average for continuous firing:#-c"; float shot_cycle_mul = wi->type == WEAPON_TYPE::BEAM ? wi->stability / (wi->Refire() + wi->stability) : @@ -5721,9 +5735,9 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C PRETTY_ADDU(statcolor + " Phase damage: #-c", wi->phase_damage * VSDM * shot_cycle_mul, 2, "MJ/s"); - if (wi->energy_rate != 0) + if (wi->GetConsumption() != 0) PRETTY_ADDU(statcolor + " Energy usage: #-c", - wi->energy_rate * RSconverter * shot_cycle_mul, + wi->GetConsumption() * RSconverter * shot_cycle_mul, 2, "MJ/s"); } text += "#n#"; @@ -5739,39 +5753,39 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } if (subunitlevel == 0 && mode == 0) { text += "#n##n##c0:1:.5#" + prefix + "[KEY FIGURES]#n##-c"; - float maxshield = playerUnit->totalShieldEnergyCapacitance(); + float maxshield = totalShieldEnergyCapacitance(playerUnit); if (shields_require_power) { maxshield = 0; } PRETTY_ADDU(statcolor + "Minimum time to reach full overthrust speed: #-c", playerUnit->getMass() * uc.max_ab_speed() / playerUnit->limits.afterburn, 2, "seconds"); //reactor - float avail = (playerUnit->maxEnergyData() * RSconverter - maxshield * VSDM); + float avail = (playerUnit->energy_manager.GetReactorCapacity() * RSconverter - maxshield * VSDM); int num_shields = playerUnit->shield->number_of_facets; float regeneration = playerUnit->shield->GetRegeneration(); float overhead = (shields_require_power) ? (regeneration / shieldenergycap * shield_maintenance_cost * num_shields * VSDM) : 0; - float nrt = avail / (playerUnit->energyRechargeData() * RSconverter); // TODO -overhead); + float nrt = avail / (playerUnit->energy_manager.GetReactorCapacity() * RSconverter); // TODO -overhead); PRETTY_ADDU(statcolor + "Reactor nominal replenish time: #-c", nrt, 2, "seconds"); //shield related stuff //code taken from RegenShields in unit_generic.cpp, so we're sure what we say here is correct. static float low_power_mode = XMLSupport::parse_float(vs_config->getVariable("physics", "low_power_mode_energy", "10")); - if (playerUnit->maxEnergyData() - maxshield < low_power_mode) { + if (playerUnit->energy_manager.GetReactorCapacity() - maxshield < low_power_mode) { text += "#n##c1:.3:.3#" + prefix + "WARNING: Capacitor banks are overdrawn: downgrade shield, upgrade reactor or purchase reactor capacitance!#-c"; } - if (uj.drive != -2 && playerUnit->warpCapData() < uj.energy) { + if (uj.drive != -2 && playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) < uj.energy) { text += "#n##c1:.3:.3#" + prefix + "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c"; } if (num_shields) { - if (regeneration * num_shields * VSDM / shieldenergycap > playerUnit->energyRechargeData() + if (regeneration * num_shields * VSDM / shieldenergycap > playerUnit->energy_manager.GetReactorCapacity() * RSconverter) { text += "#n##c1:1:.1#" + prefix + "WARNING: reactor recharge rate is less than combined shield recharge rate.#n#"; @@ -5780,7 +5794,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C if (shields_require_power) { text += "#n#" + prefix + statcolor + "Reactor recharge slowdown caused by shield maintenance: #-c"; float maint_draw_percent = regeneration * VSDM * 100.0 / shieldenergycap * shield_maintenance_cost - * num_shields / (playerUnit->energyRechargeData() * RSconverter); + * num_shields / (playerUnit->energy_manager.GetReactorCapacity() * RSconverter); text += (boost::format("%1$.2f") % maint_draw_percent).str(); text += " %."; if (maint_draw_percent > 60) { @@ -5799,14 +5813,14 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C float maint_draw = (shields_require_power && num_shields) ? (regeneration * VSDM / shieldenergycap * shield_maintenance_cost * num_shields) : 0; - if (totalWeaponEnergyUsage < (playerUnit->energyRechargeData() * RSconverter - maint_draw)) { + if (totalWeaponEnergyUsage < (playerUnit->energy_manager.GetReactorCapacity() * RSconverter - maint_draw)) { //waouh, impressive... text += "#n##c0:1:.2#" + prefix + "Your reactor produces more energy than your weapons can use!#-c"; } else { PRETTY_ADDU(statcolor + "Reactor energy depletion time if weapons in continuous use: #-c", - (playerUnit->maxEnergyData() + (playerUnit->energy_manager.GetReactorCapacity() * RSconverter) / (totalWeaponEnergyUsage - - ((playerUnit->energyRechargeData() * RSconverter - maint_draw))), + - ((playerUnit->energy_manager.GetReactorCapacity() * RSconverter - maint_draw))), 2, "seconds"); } diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index 7847c00040..d9622f4455 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -189,7 +189,7 @@ void Damageable::ApplyDamage(const Vector &pnt, // Additional house cleaning unit->PrimeOrders(); - unit->energy.Zero(); + unit->energy_manager = EnergyManager(); unit->Split(rand() % 3 + 1); @@ -553,12 +553,21 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi return; } - float shield_recharge = unit->constrained_charge_to_shields * simulation_atom_var; + EnergyContainer *energy = unit->energy_manager.GetContainer(EnergyType::Energy); + + // Here we store the actual charge we'll use in RegenShields + // TODO: fix this. It's a hack just to build... + double max_shield_recharge = unit->shield->GetRegeneration(); + double actual_recharge = max_shield_recharge * + energy->Powered(EnergyConsumerClassification::ShieldRegen); + + float shield_recharge = actual_recharge * simulation_atom_var; if (unit->GetNebula() != nullptr) { shield_recharge *= nebshields; } + // Adjust other (enemy) ships for difficulty if (!player_ship) { shield_recharge *= difficulty; @@ -566,8 +575,7 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi // Discharge shields due to energy or SPEC or cloak - if ((in_warp && !shields_in_spec) || !unit->sufficient_energy_to_recharge_shields || - unit->cloak.Active()) { + if ((in_warp && !shields_in_spec) || unit->cloak.Active()) { shield->Discharge(discharge_rate, min_shield_discharge); } else { // Shield regeneration diff --git a/engine/src/cmd/energetic.cpp b/engine/src/cmd/energetic.cpp deleted file mode 100644 index 2f0a4091df..0000000000 --- a/engine/src/cmd/energetic.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/** - * energetic.cpp - * - * Copyright (C) 2020-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 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 "energetic.h" - -#include "configuration/game_config.h" -#include "configuration/configuration.h" -#include "damageable.h" -#include "vegastrike.h" -#include "unit_generic.h" -#include "universe.h" -#include "resource/resource.h" - -#include - -/* This class provides all energy generation methods to unit types - - * ships, space installations, missiles, drones, etc. */ - - -Energetic::Energetic() : energy(0, 0), - recharge(0), - maxwarpenergy(0), - warpenergy(0), - constrained_charge_to_shields(0.0f), - sufficient_energy_to_recharge_shields(true), - fuel(0), - afterburnenergy(0), - afterburntype(0) { - jump.warpDriveRating = 0; - jump.energy = 100; - jump.insysenergy = configuration()->warp_config.insystem_jump_cost * jump.energy; - jump.drive = -2; - jump.delay = 5; - jump.damage = 0; -} - -void Energetic::decreaseWarpEnergy(bool insys, float time) { - if (configuration()->fuel.fuel_equals_warp) { - this->warpenergy = this->fuel; - } - this->warpenergy -= (insys ? jump.insysenergy / configuration()->warp_config.bleed_factor : jump.energy) * time; - if (this->warpenergy < 0) { - this->warpenergy = 0; - } - if (configuration()->fuel.fuel_equals_warp) { - this->fuel = this->warpenergy; - } -} - -void Energetic::DecreaseWarpEnergyInWarp() { - Unit *unit = static_cast(this); - - const bool in_warp = unit->graphicOptions.InWarp; - - if (!in_warp) { - return; - } - - //FIXME FIXME FIXME - // Roy Falk - fix what? - float bleed = jump.insysenergy / configuration()->warp_config.bleed_factor * simulation_atom_var; - if (warpenergy > bleed) { - warpenergy -= bleed; - } else { - unit->graphicOptions.InWarp = 0; - unit->graphicOptions.WarpRamping = 1; - } -} - -float Energetic::energyData() const { - float capacitance = const_cast(this)->totalShieldEnergyCapacitance(); - - if (configuration()->physics_config.max_shield_lowers_capacitance) { - if (energy.MaxValue() <= capacitance) { - return 0; - } - return (energy) / (energy.MaxValue() - capacitance); - } else { - return energy.Percent(); - } -} - -float Energetic::energyRechargeData() const { - return recharge; -} - -float Energetic::fuelData() const { - return fuel; -} - -float Energetic::getFuelUsage(bool afterburner) { - if (afterburner) { - return configuration()->fuel.afterburner_fuel_usage; - } - return configuration()->fuel.normal_fuel_usage; -} - -/** - * @brief Energetic::WCWarpIsFuelHack - in Wing Commander, warp and fuel are the same variable. - * Therefore, we need to transfer from one to the other to maintain equality - * @param transfer_warp_to_fuel - true means fuel = warpenergy - */ -// TODO: this is still an ugly hack -void Energetic::WCWarpIsFuelHack(bool transfer_warp_to_fuel) { - if (!configuration()->fuel.fuel_equals_warp) { - return; - } - - if (transfer_warp_to_fuel) { - fuel = warpenergy; - } else { - warpenergy = fuel; - } -} - -float Energetic::ExpendMomentaryFuelUsage(float magnitude) { - // TODO: have this make some kind of sense to someone other than the person who wrote the comment below. - //HACK this forces the reaction to be Li-6+D fusion with efficiency governed by the getFuelUsage function - float quantity = Energetic::getFuelUsage(false) * simulation_atom_var * magnitude * - configuration()->fuel.fmec_exit_velocity_inverse / configuration()->fuel.fuel_efficiency; - - return ExpendFuel(quantity); -} - -/** - * @brief expendFuel - reduce fuel by burning it - * @param quantity - requested quantity to use - * @return - actual quantity used - */ -float Energetic::ExpendFuel(float quantity) { - fuel -= configuration()->fuel.normal_fuel_usage * quantity; - - if (fuel < 0) { - quantity += fuel; - fuel = 0; - } - - return quantity; -} - -float Energetic::getWarpEnergy() const { - return warpenergy; -} - -void Energetic::increaseWarpEnergy(bool insys, float time) { - if (configuration()->fuel.fuel_equals_warp) { - this->warpenergy = this->fuel; - } - this->warpenergy += (insys ? jump.insysenergy : jump.energy) * time; - if (this->warpenergy > this->maxwarpenergy) { - this->warpenergy = this->maxwarpenergy; - } - if (configuration()->fuel.fuel_equals_warp) { - this->fuel = this->warpenergy; - } -} - -float Energetic::maxEnergyData() const { - return energy.MaxValue(); -} - -void Energetic::rechargeEnergy() { - if ((!configuration()->fuel.reactor_uses_fuel) || (fuel > 0)) { - energy += recharge * simulation_atom_var; - } -} - -bool Energetic::refillWarpEnergy() { - if (configuration()->fuel.fuel_equals_warp) { - this->warpenergy = this->fuel; - } - float tmp = this->maxwarpenergy; - if (tmp < this->jump.energy) { - tmp = this->jump.energy; - } - if (tmp > this->warpenergy) { - this->warpenergy = tmp; - if (configuration()->fuel.fuel_equals_warp) { - this->fuel = this->warpenergy; - } - return true; - } - return false; -} - -void Energetic::setAfterburnerEnergy(float aft) { - afterburnenergy = aft; -} - -void Energetic::setEnergyRecharge(float enrech) { - recharge = enrech; -} - -void Energetic::setFuel(float f) { - fuel = f; -} - -float Energetic::warpCapData() const { - return maxwarpenergy; -} - -float Energetic::warpEnergyData() const { - if (maxwarpenergy > 0) { - return ((float) warpenergy) / ((float) maxwarpenergy); - } - if (jump.energy > 0) { - return ((float) warpenergy) / ((float) jump.energy); - } - return 0.0f; -} - -// Basically max or current shield x 0.2 -float Energetic::totalShieldEnergyCapacitance() { - Unit *unit = static_cast(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) { - // TODO: if we run out of fuel or energy, we die from lack of air - - MaintainShields(); - ExpendEnergyToRechargeShields(); - MaintainECM(); - DecreaseWarpEnergyInWarp(); - - RechargeWarpCapacitors(player_ship); - - ExpendFuel(); -} - -void Energetic::ExpendEnergy(float usage) { - // Operator overloaded to prevent negative usage - energy -= usage; -} - -// The original code was a continuation of the comment above and simply unclear. -// I replaced it with a very simple model. -void Energetic::ExpendFuel() { - if (!configuration()->fuel.reactor_uses_fuel) { - return; - } - - const float fuel_usage = configuration()->fuel.fmec_exit_velocity_inverse * recharge * simulation_atom_var; - fuel = std::max(0.0f, fuel - fuel_usage); - - if (!FINITE(fuel)) { - VS_LOG(error, "Fuel is nan C"); - fuel = 0; - } -} - -void Energetic::MaintainECM() { - Unit *unit = static_cast(this); - - if (!unit->computer.ecmactive) { - return; - } - - float sim_atom_ecm = configuration()->fuel.ecm_energy_cost * unit->ecm * simulation_atom_var; - ExpendEnergy(sim_atom_ecm); -} - -void Energetic::MaintainShields() { - Unit *unit = static_cast(this); - - const bool in_warp = unit->graphicOptions.InWarp; - 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 > energy; - - ExpendEnergy(shield_maintenance); -} - -void Energetic::ExpendEnergyToRechargeShields() { - Unit *unit = static_cast(this); - - const bool in_warp = unit->graphicOptions.InWarp; - - // 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; - } - - float current_shield_value = unit->shield->TotalLayerValue(); - float max_shield_value = unit->shield->TotalMaxLayerValue(); - float regeneration = unit->shield->GetRegeneration(); - float 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); - float actual_charge = std::min(maximum_charge, energy.Value()); - float energy_required_to_charge = actual_charge * VSDPercent() * - simulation_atom_var; - ExpendEnergy(energy_required_to_charge); -} - -void Energetic::RechargeWarpCapacitors(const bool player_ship) { - // Will try to keep the percentage of warp and normal capacitors equal - const float transfer_capacity = 0.005f; - const float capacitor_percent = energy / energy.MaxValue(); - const float warp_capacitor_percent = warpenergy / maxwarpenergy; - const float warp_multiplier = WarpEnergyMultiplier(player_ship); - - if (warp_capacitor_percent >= 1.0f || - warp_capacitor_percent > capacitor_percent || - capacitor_percent < 0.10f) { - return; - } - - const float previous_energy = energy.Value(); - ExpendEnergy(energy.MaxValue() * transfer_capacity); - - const float actual_energy = previous_energy - energy.Value(); - warpenergy = std::min(maxwarpenergy, warpenergy + 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 deleted file mode 100644 index dcb2e71044..0000000000 --- a/engine/src/cmd/energetic.h +++ /dev/null @@ -1,109 +0,0 @@ -/** - * energetic.h - * - * Copyright (C) Daniel Horn - * Copyright (C) 2020 pyramid3d, Stephen G. Tuggy, and other Vega Strike - * contributors - * Copyright (C) 2022-2023 Stephen G. Tuggy, Benjamen R. Meyer - * - * 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_CMD_ENERGETIC_H -#define VEGA_STRIKE_ENGINE_CMD_ENERGETIC_H - -#include "resource/resource.h" - -class Energetic { -public: - Energetic(); - - void decreaseWarpEnergy(bool insys, float time); - void DecreaseWarpEnergyInWarp(); - - float energyData() const; - float energyRechargeData() const; - - float fuelData() const; - - static float getFuelUsage(bool afterburner); - void WCWarpIsFuelHack(bool transfer_warp_to_fuel); - float ExpendMomentaryFuelUsage(float magnitude); - float ExpendFuel(float quantity); - void ExpendEnergy(const bool player_ship); - void ExpendEnergy(float usage); - void ExpendEnergyToRechargeShields(); - void ExpendFuel(); - float getWarpEnergy() const; - - void increaseWarpEnergy(bool insys, float time); - - float maxEnergyData() const; - - void MaintainECM(); - void MaintainShields(); - - void rechargeEnergy(); - void RechargeWarpCapacitors(const bool player_ship); - bool refillWarpEnergy(); - - void setAfterburnerEnergy(float aft); - void setEnergyRecharge(float enrech); - void setFuel(float f); - - float totalShieldEnergyCapacitance(); - - static float VSDPercent(); - - float warpCapData() const; - float warpEnergyData() const; - - float WarpEnergyMultiplier(const bool player_ship); - - // TODO: move to StarFaring class when available - struct UnitJump { - float warpDriveRating; - float energy; //short fix - float insysenergy; //short fix - signed char drive; // disabled - unsigned char delay; - unsigned char damage; - //negative means fuel - } - jump{}; - - //current energy - Resource energy; - - //how much the energy recharges per second - float recharge; - - //maximum energy - float maxwarpenergy; //short fix - //current energy - float warpenergy; //short fix - float constrained_charge_to_shields; - bool sufficient_energy_to_recharge_shields; - - //fuel of this unit - float fuel; - float afterburnenergy; //short fix - int afterburntype; //0--energy, 1--fuel - //-1 means it is off. -2 means it doesn't exist. otherwise it's engaged to destination (positive number) -}; - -#endif //VEGA_STRIKE_ENGINE_CMD_ENERGETIC_H diff --git a/engine/src/cmd/jump_capable.cpp b/engine/src/cmd/jump_capable.cpp index 571b470fb7..7a5c64030a 100644 --- a/engine/src/cmd/jump_capable.cpp +++ b/engine/src/cmd/jump_capable.cpp @@ -98,12 +98,12 @@ std::string GenerateAutoError(Unit *me, Unit *targ) { /////////////////////////////////////////////// -JumpCapable::JumpCapable() : activeStarSystem(nullptr) { -}; +JumpCapable::JumpCapable() : activeStarSystem(nullptr), + jump_drive() {}; void JumpCapable::ActivateJumpDrive(int destination) { Unit *unit = static_cast(this); - if (((unit->docked & (unit->DOCKED | unit->DOCKED_INSIDE)) == 0) && unit->jump.drive != -2) { + if (((unit->docked & (unit->DOCKED | unit->DOCKED_INSIDE)) == 0) && jump.drive != -2) { unit->jump.drive = destination; } } @@ -138,11 +138,15 @@ bool JumpCapable::AutoPilotToErrorMessage(const Unit *target, return AutoPilotToErrorMessage(targ, ignore_energy_requirements, failuremessage, recursive_level); } } - if (unit->warpenergy < unit->jump.insysenergy) { + + // TODO: move to energymanager.canpower and + // energycontainer.canpower + if (unit->energy_manager.GetLevel(EnergyType::SPEC) < jump_drive.GetConsumption()) { if (!ignore_energy_requirements) { return false; } } + signed char Guaranteed = ComputeAutoGuarantee(unit); if (Guaranteed == Mission::AUTO_OFF) { return false; @@ -270,7 +274,9 @@ bool JumpCapable::AutoPilotToErrorMessage(const Unit *target, failuremessage = configuration()->graphics_config.hud.already_near_message; return false; } - unit->warpenergy -= totpercent * unit->jump.insysenergy; + + jump_drive.Use(); + if (unsafe == false && totpercent == 0) { end = endne; } @@ -499,10 +505,6 @@ const std::vector &JumpCapable::GetDestinations() const { return unit->pImage->destination; } -const Energetic::UnitJump &JumpCapable::GetJumpStatus() const { - const Unit *unit = static_cast(this); - return unit->jump; -} StarSystem *JumpCapable::getStarSystem() { Unit *unit = static_cast(this); diff --git a/engine/src/cmd/jump_capable.h b/engine/src/cmd/jump_capable.h index 7b238ae243..18bc8bbb43 100644 --- a/engine/src/cmd/jump_capable.h +++ b/engine/src/cmd/jump_capable.h @@ -25,7 +25,7 @@ #define VEGA_STRIKE_ENGINE_CMD_JUMP_CAPABLE_H #include "star_system.h" -#include "energetic.h" +#include "components/jump_drive.h" #include @@ -34,8 +34,19 @@ class JumpCapable { public: StarSystem *activeStarSystem; - + JumpDrive jump_drive; + public: + struct UnitJump { + float warpDriveRating; + float energy; //short fix + float insysenergy; //short fix + signed char drive; // disabled + unsigned char delay; + unsigned char damage; + //negative means fuel + } jump{}; + JumpCapable(); void ActivateJumpDrive(int destination = 0); @@ -48,7 +59,6 @@ class JumpCapable { float CourseDeviation(const Vector &OriginalCourse, const Vector &FinalCourse) const; void DeactivateJumpDrive(); const std::vector &GetDestinations() const; - const Energetic::UnitJump &GetJumpStatus() const; StarSystem *getStarSystem(); const StarSystem *getStarSystem() const; Vector GetWarpRefVelocity() const; diff --git a/engine/src/cmd/mount.cpp b/engine/src/cmd/mount.cpp index 860d3816d9..5f878ca530 100644 --- a/engine/src/cmd/mount.cpp +++ b/engine/src/cmd/mount.cpp @@ -253,7 +253,8 @@ bool Mount::PhysicsAlignedFire(Unit *caller, if (type->type == WEAPON_TYPE::BEAM || type->isMissile()) { //Missiles and beams set to processed. processed = PROCESSED; - } else if (ref.refire < type->Refire() || type->energy_rate > caller->energy) { + } else if (ref.refire < type->Refire() || + type->GetConsumption() > caller->energy_manager.GetLevel(EnergyType::Energy)) { //Wait until refire has expired and reactor has produced enough energy for the next bolt. return true; } //Not ready to refire yet. But don't stop firing. @@ -289,6 +290,8 @@ bool Mount::PhysicsAlignedFire(Unit *caller, ammo--; } } + + // TODO: why do we have energy.Deplete here and in armed.cpp??? time_to_lock = type->lock_time; switch (type->type) { case WEAPON_TYPE::UNKNOWN: @@ -299,7 +302,8 @@ bool Mount::PhysicsAlignedFire(Unit *caller, } break; case WEAPON_TYPE::BOLT: - caller->energy -= type->energy_rate; + caller->energy_manager.Deplete(EnergyType::Energy, type->GetConsumption()); + hint[Unit::UNIT_BOLT] = Bolt(type, mat, velocity, @@ -308,7 +312,8 @@ bool Mount::PhysicsAlignedFire(Unit *caller, break; case WEAPON_TYPE::BALL: { - caller->energy -= type->energy_rate; + caller->energy_manager.Deplete(EnergyType::Energy, type->GetConsumption()); + hint[Unit::UNIT_BOLT] = BoltDrawManager::GetInstance().AddBall(type, mat, velocity, owner, hint[Unit::UNIT_BOLT]); break; diff --git a/engine/src/cmd/movable.cpp b/engine/src/cmd/movable.cpp index 5c1dce7cb8..4d5891f291 100644 --- a/engine/src/cmd/movable.cpp +++ b/engine/src/cmd/movable.cpp @@ -55,7 +55,10 @@ Movable::Movable() : cumulative_transformation_matrix(identity_matrix), corner_min(Vector(FLT_MAX, FLT_MAX, FLT_MAX)), corner_max(Vector(-FLT_MAX, -FLT_MAX, -FLT_MAX)), radial_size(0), - Momentofinertia(0.01) { + Momentofinertia(0.01), + // TODO: make drive and afterburner parameters configurable somehow + drive(EnergyType::Fuel, 1.0, 1.0, 1.0, 0.1), + afterburner(EnergyType::Fuel, 3.0) { cur_sim_queue_slot = rand() % SIM_QUEUE_SIZE; const Vector default_angular_velocity(configuration()->general_config.pitch, configuration()->general_config.yaw, @@ -474,8 +477,7 @@ double Movable::GetMaxWarpFieldStrength(float rampmult) const { void Movable::FireEngines(const Vector &Direction /*unit vector... might default to "r"*/, float FuelSpeed, float FMass) { - Energetic *energetic = dynamic_cast(this); - FMass = energetic->ExpendFuel(FMass); + // Note: this calculation no longer relies on translating fuel mass to motion NetForce += Direction * ((double)FuelSpeed * (double)FMass / GetElapsedTime()); } @@ -533,12 +535,10 @@ Vector Movable::MaxTorque(const Vector &torque) { } Vector Movable::ClampTorque(const Vector &amt1) { - Energetic *energetic = dynamic_cast(this); Vector Res = amt1; - energetic->WCWarpIsFuelHack(true); - - float fuelclamp = (energetic->fuelData() <= 0) ? configuration()->fuel.no_fuel_thrust : 1; + // no_fuel_thrust = 0.4, so even with no fuel, we should keep flying + float fuelclamp = std::max(drive.Powered(), configuration()->fuel.no_fuel_thrust); if (fabs(amt1.i) > fuelclamp * limits.pitch) { Res.i = copysign(fuelclamp * limits.pitch, amt1.i); } @@ -550,23 +550,19 @@ Vector Movable::ClampTorque(const Vector &amt1) { } //1/5,000,000 m/s - energetic->ExpendMomentaryFuelUsage(Res.Magnitude()); - energetic->WCWarpIsFuelHack(false); return Res; } Vector Movable::ClampVelocity(const Vector &velocity, const bool afterburn) { - Energetic *energetic = dynamic_cast(this); - Unit *unit = static_cast(this); - - float fuelclamp = (energetic->fuelData() <= 0) ? configuration()->fuel.no_fuel_thrust : 1; - float abfuelclamp = (energetic->fuelData() <= 0 || (energetic->energy < unit->afterburnenergy * simulation_atom_var)) ? configuration()->fuel.no_fuel_afterburn : 1; - float limit = - afterburn ? (abfuelclamp - * (unit->computer.max_ab_speed() - - unit->computer.max_speed()) + (fuelclamp * unit->computer.max_speed())) : fuelclamp - * unit->computer.max_speed(); + Unit *unit = dynamic_cast(this); + + // no_fuel_thrust = 0.4, so even with no fuel, we should keep flying + float fuelclamp = std::max(drive.Powered(), configuration()->fuel.no_fuel_thrust); + float abfuelclamp = std::max(afterburner.Powered(), configuration()->fuel.no_fuel_afterburn); + + float limit = afterburn ? (abfuelclamp * (unit->computer.max_ab_speed() - unit->computer.max_speed()) + (fuelclamp * unit->computer.max_speed())) : + fuelclamp * unit->computer.max_speed(); float tmp = velocity.Magnitude(); if (tmp > fabs(limit)) { return velocity * (limit / tmp); @@ -615,35 +611,19 @@ Vector Movable::MaxThrust(const Vector &amt1) { Vector Movable::ClampThrust(const Vector &amt1, bool afterburn) { Unit *unit = static_cast(this); - const bool WCfuelhack = configuration()->fuel.fuel_equals_warp; const bool finegrainedFuelEfficiency = configuration()->fuel.variable_fuel_consumption; - if (WCfuelhack) { - if (unit->fuel > unit->warpenergy) { - unit->fuel = unit->warpenergy; - } - if (unit->fuel < unit->warpenergy) { - unit->warpenergy = unit->fuel; - } - } - float instantenergy = unit->afterburnenergy * simulation_atom_var; - if ((unit->afterburntype == 0) && unit->energy < instantenergy) { - afterburn = false; - } - if ((unit->afterburntype == 1) && unit->fuel < 0) { - unit->fuel = 0; - afterburn = false; - } - if ((unit->afterburntype == 2) && unit->warpenergy < 0) { - unit->warpenergy = 0; - afterburn = false; - } - if (3 == unit->afterburntype) { //no afterburner -- we should really make these types an enum :-/ - afterburn = false; + + // Delayed reaction + if(afterburn) { + afterburner.Use(); } + Vector Res = amt1; - float fuelclamp = (unit->fuel <= 0) ? configuration()->fuel.no_fuel_thrust : 1; - float abfuelclamp = (unit->fuel <= 0) ? configuration()->fuel.no_fuel_afterburn : 1; + // no_fuel_thrust = 0.4, so even with no fuel, we should keep flying + float fuelclamp = std::max(drive.Powered(), configuration()->fuel.no_fuel_thrust); + float abfuelclamp = std::max(afterburner.Powered(), configuration()->fuel.no_fuel_afterburn); + if (fabs(amt1.i) > fabs(fuelclamp * limits.lateral)) { Res.i = copysign(fuelclamp * limits.lateral, amt1.i); } @@ -660,53 +640,7 @@ Vector Movable::ClampThrust(const Vector &amt1, bool afterburn) { if (amt1.k < -limits.retro) { Res.k = -limits.retro; } - const float Lithium6constant = configuration()->fuel.deuterium_relative_efficiency_lithium; - //1/5,000,000 m/s - const float FMEC_exit_vel_inverse = configuration()->fuel.fmec_exit_velocity_inverse; - if (unit->afterburntype == 2) { - //Energy-consuming afterburner - //HACK this forces the reaction to be Li-6+Li-6 fusion with efficiency governed by the getFuelUsage function - unit->warpenergy -= unit->afterburnenergy * Energetic::getFuelUsage(afterburn) * simulation_atom_var * Res.Magnitude() - * FMEC_exit_vel_inverse - / Lithium6constant; - } - if (3 == unit->afterburntype || unit->afterburntype == 1) { - //fuel-burning overdrive - uses afterburner efficiency. In NO_AFTERBURNER case, "afterburn" will always be false, so can reuse code. - //HACK this forces the reaction to be Li-6+Li-6 fusion with efficiency governed by the getFuelUsage function - unit->fuel -= - ((afterburn - && finegrainedFuelEfficiency) ? unit->afterburnenergy : Energetic::getFuelUsage(afterburn)) - * simulation_atom_var * Res.Magnitude() - * FMEC_exit_vel_inverse / Lithium6constant; -#ifndef __APPLE__ - if (ISNAN(unit->fuel)) { - VS_LOG(error, "Fuel is NAN A"); - unit->fuel = 0; - } -#endif - } - if (unit->afterburntype == 0) { - //fuel-burning afterburner - uses default efficiency - appears to check for available energy? FIXME - //HACK this forces the reaction to be Li-6+Li-6 fusion with efficiency governed by the getFuelUsage function - unit->fuel -= unit->getFuelUsage(false) * simulation_atom_var * Res.Magnitude() * FMEC_exit_vel_inverse / Lithium6constant; -#ifndef __APPLE__ - if (ISNAN(unit->fuel)) { - VS_LOG(error, "Fuel is NAN B"); - unit->fuel = 0; - } -#endif - } - if ((afterburn) && (unit->afterburntype == 0)) { - unit->energy -= instantenergy; - } - if (WCfuelhack) { - if (unit->fuel > unit->warpenergy) { - unit->fuel = unit->warpenergy; - } - if (unit->fuel < unit->warpenergy) { - unit->warpenergy = unit->fuel; - } - } + return Res; } @@ -770,16 +704,10 @@ void Movable::RollTorque(float amt) { void Movable::Thrust(const Vector &amt1, bool afterburn) { Unit *unit = static_cast(this); - if (unit->afterburntype == 0) { - afterburn = afterburn && unit->energy > unit->afterburnenergy * simulation_atom_var; - } //SIMULATION_ATOM; ? - if (unit->afterburntype == 1) { - afterburn = afterburn && unit->fuel > 0; - } - if (unit->afterburntype == 2) { - afterburn = afterburn && unit->warpenergy > 0; - } - + // TODO: make sure we expand fuel in other places before deleting this. + // This will kick in after one atom. + //afterburner.Use(); + //afterburn = afterburn && afterburner.Powered(); //Unit::Thrust( amt1, afterburn ); { diff --git a/engine/src/cmd/movable.h b/engine/src/cmd/movable.h index e6c450659e..2c2d1a4820 100644 --- a/engine/src/cmd/movable.h +++ b/engine/src/cmd/movable.h @@ -29,6 +29,9 @@ #include "gfx/quaternion.h" #include "star_system.h" +#include "components/afterburner.h" +#include "components/drive.h" + #include struct Transformation; @@ -40,7 +43,8 @@ struct Quaternion; class Movable { protected: - + Drive drive; + Afterburner afterburner; public: //mass of this unit (may change with cargo) // TODO: subclass with return Mass+fuel; diff --git a/engine/src/cmd/planet.cpp b/engine/src/cmd/planet.cpp index 717b79caf7..97367b4d8f 100644 --- a/engine/src/cmd/planet.cpp +++ b/engine/src/cmd/planet.cpp @@ -462,7 +462,7 @@ void Planet::InitPlanet(QVector x, VSSprite *tmp = pImage->pHudImage; pImage->pHudImage = un->GetImageInformation().pHudImage; un->GetImageInformation().pHudImage = tmp; - maxwarpenergy = un->warpCapData(); + energy_manager.SetCapacity(EnergyType::SPEC, un->energy_manager.GetMaxLevel(EnergyType::SPEC)); if (smartplanets) { SubUnits.prepend(un); un->SetRecursiveOwner(this); diff --git a/engine/src/cmd/script/script_call_unit_generic.cpp b/engine/src/cmd/script/script_call_unit_generic.cpp index abf7094832..f79c22e20f 100644 --- a/engine/src/cmd/script/script_call_unit_generic.cpp +++ b/engine/src/cmd/script/script_call_unit_generic.cpp @@ -508,7 +508,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_manager.GetLevel(EnergyType::Energy); } 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 5fcf330568..08ff6a6698 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -400,7 +400,9 @@ static void AddSubUnits(Unit *thus, for (int a = xml.units.size() - 1; a >= 0; a--) { bool randomspawn = xml.units[a]->name.get().find("randomspawn") != string::npos; if (randomspawn) { - int chancetospawn = float_to_int(xml.units[a]->warpCapData()); + // TODO: surely we could use something more relevant than max SPEC + // to determine chance to spawn... + int chancetospawn = float_to_int(xml.units[a]->energy_manager.GetMaxLevel(EnergyType::SPEC)); if (chancetospawn > rand() % 100) { thus->SubUnits.prepend(xml.units[a]); } else { @@ -731,8 +733,17 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ pImage->CockpitCenter.k = UnitCSVFactory::GetVariable(unit_key, "CockpitZ", 0.0f) * xml.unitscale; Mass = UnitCSVFactory::GetVariable(unit_key, "Mass", 1.0f); Momentofinertia = UnitCSVFactory::GetVariable(unit_key, "Moment_Of_Inertia", 1.0f); - fuel = UnitCSVFactory::GetVariable(unit_key, "Fuel_Capacity", 0.0f); - + + + energy_manager.SetCapacity(EnergyType::Fuel, + UnitCSVFactory::GetVariable(unit_key, "Fuel_Capacity", 0.0f)); + energy_manager.Refill(EnergyType::Fuel); + + energy_manager.SetCapacity(EnergyType::Energy, + UnitCSVFactory::GetVariable(unit_key, "Primary_Capacitor", 0.0f)); + energy_manager.SetCapacity(EnergyType::SPEC, + UnitCSVFactory::GetVariable(unit_key, "Warp_Capacitor", 0.0f)); + // Hull float temp_hull = UnitCSVFactory::GetVariable(unit_key, "Hull", 0.0f); float hull_values[1] = {temp_hull}; @@ -810,15 +821,14 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ // End shield section - const bool WCfuelhack = configuration()->fuel.fuel_equals_warp; - maxwarpenergy = warpenergy = UnitCSVFactory::GetVariable(unit_key, "Warp_Capacitor", 0.0f); + graphicOptions.MinWarpMultiplier = UnitCSVFactory::GetVariable(unit_key, "Warp_Min_Multiplier", 1.0f); graphicOptions.MaxWarpMultiplier = UnitCSVFactory::GetVariable(unit_key, "Warp_Max_Multiplier", 1.0f); - double capacitor = UnitCSVFactory::GetVariable(unit_key, "Primary_Capacitor", 0.0f); - energy = Resource(capacitor, 0.0f, capacitor); - recharge = UnitCSVFactory::GetVariable(unit_key, "Reactor_Recharge", 0.0f); + + energy_manager.SetReactorCapacity(UnitCSVFactory::GetVariable(unit_key, "Reactor_Recharge", 0.0f)); + jump.drive = UnitCSVFactory::GetVariable(unit_key, "Jump_Drive_Present", false) ? -1 : -2; jump.delay = UnitCSVFactory::GetVariable(unit_key, "Jump_Drive_Delay", 0); forcejump = UnitCSVFactory::GetVariable(unit_key, "Wormhole", false); @@ -828,14 +838,19 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ ? true : false) ? 1 : 0; jump.energy = UnitCSVFactory::GetVariable(unit_key, "Outsystem_Jump_Cost", 0.0f); jump.insysenergy = UnitCSVFactory::GetVariable(unit_key, "Warp_Usage_Cost", 0.0f); - if (WCfuelhack) { - fuel = warpenergy = warpenergy + jump.energy - * 0.1f; - } //this is required to make sure we don't trigger the "globally out of fuel" if we use all warp charges -- save some afterburner for later!!! - afterburnenergy = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Usage_Cost", 32767.0f); - afterburntype = UnitCSVFactory::GetVariable(unit_key, - "Afterburner_Type", - 0); //type 1 == "use fuel", type 0 == "use reactor energy", type 2 ==(hopefully) "use jump fuel" 3: NO AFTERBURNER + // TODO: is this really an issue + // this is required to make sure we don't trigger the "globally out of fuel" if we use all warp charges -- save some afterburner for later!!! + + // Afterburner + // TODO: 1. check AB cost values in json + // 2. tweak afterburner cost for playability + // 3. check WC + int afterburner_type_number = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Type", 0); + EnergyType afterburner_type = SaveToType(afterburner_type_number); + double afterburner_usage_factor = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Usage_Cost", 3.0); + afterburner = Afterburner(afterburner_type, afterburner_usage_factor, 1.0, + Mass, simulation_atom_var); + limits.yaw = UnitCSVFactory::GetVariable(unit_key, "Maneuver_Yaw", 0.0f) * VS_PI / 180.0; limits.pitch = UnitCSVFactory::GetVariable(unit_key, "Maneuver_Pitch", 0.0f) * VS_PI / 180.0; limits.roll = UnitCSVFactory::GetVariable(unit_key, "Maneuver_Roll", 0.0f) * VS_PI / 180.0; @@ -1217,7 +1232,12 @@ string Unit::WriteUnitString() { } unit["Mass"] = tos(Mass); unit["Moment_Of_Inertia"] = tos(Momentofinertia); - unit["Fuel_Capacity"] = tos(fuel); + + // Components + unit["Fuel_Capacity"] = tos(energy_manager.GetMaxLevel(EnergyType::Fuel)); + unit["Primary_Capacitor"] = tos(energy_manager.GetMaxLevel(EnergyType::Energy)); + unit["Warp_Capacitor"] = tos(energy_manager.GetMaxLevel(EnergyType::SPEC)); + unit["Hull"] = tos(GetHullLayer().facets[0].health); unit["Spec_Interdiction"] = tos(specInterdiction); @@ -1282,18 +1302,21 @@ string Unit::WriteUnitString() { 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["Warp_Capacitor"] = tos(maxwarpenergy); + unit["Warp_Min_Multiplier"] = tos(graphicOptions.MinWarpMultiplier); unit["Warp_Max_Multiplier"] = tos(graphicOptions.MaxWarpMultiplier); - unit["Primary_Capacitor"] = tos(energy.MaxValue()); - unit["Reactor_Recharge"] = tos(recharge); + + unit["Reactor_Recharge"] = tos(energy_manager.GetReactorCapacity()); unit["Jump_Drive_Present"] = tos(jump.drive >= -1); unit["Jump_Drive_Delay"] = tos(jump.delay); unit["Wormhole"] = tos(forcejump != 0); unit["Outsystem_Jump_Cost"] = tos(jump.energy); unit["Warp_Usage_Cost"] = tos(jump.insysenergy); - unit["Afterburner_Usage_Cost"] = tos(afterburnenergy); - unit["Afterburner_Type"] = tos(afterburntype); + + // Afterburner + unit["Afterburner_Type"] = TypeToSave(afterburner.GetEnergyType()); + unit["Afterburner_Usage_Cost"] = tos(afterburner.UsageFactor()); + unit["Maneuver_Yaw"] = tos(limits.yaw * 180 / (VS_PI)); unit["Maneuver_Pitch"] = tos(limits.pitch * 180 / (VS_PI)); unit["Maneuver_Roll"] = tos(limits.roll * 180 / (VS_PI)); diff --git a/engine/src/cmd/unit_functions_generic.cpp b/engine/src/cmd/unit_functions_generic.cpp index 351b3dbd0d..6909c29db1 100644 --- a/engine/src/cmd/unit_functions_generic.cpp +++ b/engine/src/cmd/unit_functions_generic.cpp @@ -252,7 +252,7 @@ int parseMountSizes(const char *str) { void DealPossibleJumpDamage(Unit *un) { float speed = un->GetVelocity().Magnitude(); - float jump_damage = un->GetJumpStatus().damage + (rand() % 100 < 1) ? (rand() % 20) : 0; + float jump_damage = un->jump.damage + (rand() % 100 < 1) ? (rand() % 20) : 0; static float jump_damage_multiplier = XMLSupport::parse_float(vs_config->getVariable("physics", "jump_damage_multiplier", ".1")); static float max_damage = XMLSupport::parse_float(vs_config->getVariable("physics", "max_jump_damage", "100")); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 3a50e141ea..766a663365 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -68,7 +68,6 @@ #include "weapon_info.h" #include "mount_size.h" #include "turret.h" -#include "energetic.h" #include "configuration/game_config.h" #include "resource/resource.h" #include "base_util.h" @@ -986,10 +985,11 @@ float globQueryShell(QVector pos, QVector dir, float rad); extern void ActivateAnimation(Unit *jp); +// Move to jump_enabled void TurnJumpOKLightOn(Unit *un, Cockpit *cp) { if (cp) { - if (un->getWarpEnergy() >= un->GetJumpStatus().energy) { - if (un->GetJumpStatus().drive > -2) { + if (un->energy_manager.GetLevel(EnergyType::SPEC) >= un->jump.energy) { + if (un->jump.drive > -2) { cp->jumpok = 1; } } @@ -1011,16 +1011,16 @@ bool Unit::jumpReactToCollision(Unit *smalle) { return false; } //we have a drive - if ((!SPEC_interference && (smalle->GetJumpStatus().drive >= 0 + if ((!SPEC_interference && (smalle->jump.drive >= 0 && //we have power - (smalle->warpenergy >= smalle->GetJumpStatus().energy + (smalle->energy_manager.GetLevel(EnergyType::SPEC) >= smalle->jump.energy //or we're being cheap || (ai_jump_cheat && cp == nullptr) ))) || forcejump) { //or the jump is being forced? //NOW done in star_system_generic.cpp before TransferUnitToSystem smalle->warpenergy-=smalle->GetJumpStatus().energy; - int dest = smalle->GetJumpStatus().drive; + int dest = smalle->jump.drive; if (dest < 0) { dest = 0; } @@ -1039,15 +1039,15 @@ bool Unit::jumpReactToCollision(Unit *smalle) { } else { return false; } - if ((!SPEC_interference && (GetJumpStatus().drive >= 0 - && (warpenergy >= GetJumpStatus().energy || (ai_jump_cheat && cp == NULL)) + if ((!SPEC_interference && (jump.drive >= 0 + && (energy_manager.GetLevel(EnergyType::SPEC) >= jump.energy || (ai_jump_cheat && cp == NULL)) )) || smalle->forcejump) { - warpenergy -= GetJumpStatus().energy; + jump_drive.Use(); DeactivateJumpDrive(); Unit *jumppoint = smalle; _Universe->activeStarSystem()->JumpTo(this, jumppoint, - smalle->GetDestinations()[GetJumpStatus().drive + smalle->GetDestinations()[jump.drive % smalle->GetDestinations().size()]); return true; @@ -1240,13 +1240,15 @@ void Unit::DamageRandSys(float dam, const Vector &vec, float randnum, float degr ".1")); static float cargo_damage_prob = upgradevolume_damage_prob - XMLSupport::parse_float(vs_config->getVariable("physics", "cargo_damage_prob", "1")); - if (randnum >= fuel_damage_prob) { + // TODO: implement + /*if (randnum >= fuel_damage_prob) { fuel *= dam; } else if (randnum >= warpenergy_damage_prob) { warpenergy *= dam; } else if (randnum >= ab_damage_prob) { - this->afterburnenergy += ((1 - dam) * recharge); - } else if (randnum >= cargovolume_damage_prob) { + this->afterburnenergy += ((1 - dam) * recharge);*/ + //} else + if (randnum >= cargovolume_damage_prob) { CargoVolume *= dam; } else if (randnum >= upgradevolume_damage_prob) { UpgradeVolume *= dam; @@ -1301,14 +1303,14 @@ void Unit::DamageRandSys(float dam, const Vector &vec, float randnum, float degr if (dam < mindam) { dam = mindam; } - this->recharge *= dam; + // TODO: this->recharge *= dam; } else if (randnum >= .2) { static float mindam = XMLSupport::parse_float(vs_config->getVariable("physics", "min_maxenergy_shot_damage", "0.2")); if (dam < mindam) { dam = mindam; } - energy.DowngradeByPercent(dam); + // TODO: energy.DowngradeByPercent(dam); } else if (repair_droid > 0) { repair_droid--; } @@ -2018,7 +2020,8 @@ int Unit::ForceDock(Unit *utdw, unsigned int whichdockport) { UpdateMasterPartList(UniverseUtil::GetMasterPartList()); unsigned int cockpit = UnitUtil::isPlayerStarship(this); - static float MinimumCapacityToRefuelOnLand = + // TODO: reimplement SPEC refueling (why?) + /*static float MinimumCapacityToRefuelOnLand = XMLSupport::parse_float(vs_config->getVariable("physics", "MinimumWarpCapToRefuelDockeesAutomatically", "0")); @@ -2029,8 +2032,12 @@ int Unit::ForceDock(Unit *utdw, unsigned int whichdockport) { docking_fee = XMLSupport::parse_float(vs_config->getVariable("general", "fuel_docking_fee", "0")); _Universe->AccessCockpit(cockpit)->credits -= docking_fee; } - } - if ((capdata < MinimumCapacityToRefuelOnLand) && (this->faction == utdw->faction)) { + }*/ + + // This code refuels from one ship to the other but does it for spec, + // which doesn't make much sense. Probably more relevant for WC. + // TODO: we can do better. delete! + /*if ((capdata < MinimumCapacityToRefuelOnLand) && (this->faction == utdw->faction)) { if (utdw->warpEnergyData() > this->warpEnergyData() && utdw->warpEnergyData() > this->jump.energy) { this->increaseWarpEnergy(false, this->jump.energy); utdw->decreaseWarpEnergy(false, this->jump.energy); @@ -2039,7 +2046,7 @@ int Unit::ForceDock(Unit *utdw, unsigned int whichdockport) { utdw->increaseWarpEnergy(false, utdw->jump.energy); this->decreaseWarpEnergy(false, utdw->jump.energy); } - } + }*/ if (cockpit >= 0 && cockpit < _Universe->numPlayers()) { static float docking_fee = XMLSupport::parse_float(vs_config->getVariable("general", "docking_fee", "0")); if (_Universe->AccessCockpit(cockpit)->credits >= docking_fee) { @@ -2966,7 +2973,7 @@ bool Unit::UpAndDownGrade(const Unit *up, "Warp_Capacitor|Warp_Usage_Cost")) { if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Warp_Capacitor")) - STDUPGRADE(maxwarpenergy, up->maxwarpenergy, templ->maxwarpenergy, 0); + // TODO: STDUPGRADE(maxwarpenergy, up->maxwarpenergy, templ->maxwarpenergy, 0); if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Warp_Usage_Cost")) STDUPGRADE(jump.insysenergy, up->jump.insysenergy, templ->jump.insysenergy, 0); @@ -3029,9 +3036,9 @@ bool Unit::UpAndDownGrade(const Unit *up, hull->facets[0].max_health = hull->facets[0].health; } - if (!csv_cell_null_check || force_change_on_nothing - || cell_has_recursive_data(upgrade_name, up->faction, "Reactor_Recharge")) - STDUPGRADE(recharge, up->recharge, templ->recharge, 0); + //if (!csv_cell_null_check || force_change_on_nothing + // || cell_has_recursive_data(upgrade_name, up->faction, "Reactor_Recharge")) + // TODO: STDUPGRADE(recharge, up->recharge, templ->recharge, 0); static bool unittable = XMLSupport::parse_bool(vs_config->getVariable("physics", "UnitTable", "false")); //Uncommon fields (capacities... rates... etc...) if (!csv_cell_null_check || force_change_on_nothing @@ -3061,9 +3068,9 @@ bool Unit::UpAndDownGrade(const Unit *up, STDUPGRADE(ecm, up->ecm, templ->ecm, 0); //ecm is unsigned --chuck_starchaser if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Primary_Capacitor")) { - temporary_upgrade_float_variable = static_cast(energy.MaxValue()); - STDUPGRADE(temporary_upgrade_float_variable, up->energy.MaxValue(), templ->energy.MaxValue(), 0); - energy.SetMaxValue(temporary_upgrade_float_variable); + //temporary_upgrade_float_variable = static_cast(energy.MaxValue()); + // TODO: STDUPGRADE(temporary_upgrade_float_variable, up->energy.MaxValue(), templ->energy.MaxValue(), 0); + // energy.SetMaxValue(temporary_upgrade_float_variable); } } //Maneuvering stuff @@ -3095,9 +3102,9 @@ bool Unit::UpAndDownGrade(const Unit *up, if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Afterburner_Accel")) STDUPGRADE(limits.afterburn, tlimits_afterburn, templ->limits.afterburn, 0); - if (!csv_cell_null_check || force_change_on_nothing - || cell_has_recursive_data(upgrade_name, up->faction, "Fuel_Capacity")) - STDUPGRADE(fuel, up->fuel, templ->fuel, 0); + //if (!csv_cell_null_check || force_change_on_nothing + // || cell_has_recursive_data(upgrade_name, up->faction, "Fuel_Capacity")) + // TODO: STDUPGRADE(fuel, up->fuel, templ->fuel, 0); if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Default_Speed_Governor")) STDUPGRADE(computer.max_combat_speed, tmax_speed, templ->computer.max_combat_speed, 0); @@ -3317,7 +3324,7 @@ bool Unit::UpAndDownGrade(const Unit *up, //NOTE: Afterburner type 2 (jmp) //NOTE: Afterburner type 1 (gas) //NOTE: Afterburner type 0 (pwr) - if (afterburnenergy < 32767 && afterburnenergy <= up->afterburnenergy && up->afterburnenergy != 32767 + /*TODO: if (afterburnenergy < 32767 && afterburnenergy <= up->afterburnenergy && up->afterburnenergy != 32767 && up->afterburnenergy != 0) { if (touchme) { afterburnenergy = 32767, afterburntype = 0; @@ -3330,7 +3337,7 @@ bool Unit::UpAndDownGrade(const Unit *up, ((char *) &this->afterburnenergy) - ((char *) this), tempdownmap); } - } + }*/ } else { //we are upgrading! if (touchme) { @@ -3356,7 +3363,7 @@ bool Unit::UpAndDownGrade(const Unit *up, //NOTE: Afterburner type 2 (jmp) //NOTE: Afterburner type 1 (gas) //NOTE: Afterburner type 0 (pwr) - if (((afterburnenergy > up->afterburnenergy + /*if (((afterburnenergy > up->afterburnenergy || (afterburntype != up->afterburntype && up->afterburnenergy != 32767)) && up->afterburnenergy > 0) || force_change_on_nothing) { ++numave; @@ -3366,7 +3373,7 @@ bool Unit::UpAndDownGrade(const Unit *up, } else if (afterburnenergy <= up->afterburnenergy && afterburnenergy >= 0 && up->afterburnenergy > 0 && up->afterburnenergy < 32767) { cancompletefully = false; - } + }*/ if ((jump.drive == -2 && up->jump.drive >= -1) || force_change_on_nothing) { if (touchme) { jump.drive = up->jump.drive; @@ -3575,12 +3582,12 @@ int Unit::RepairUpgrade() { if (shield.recharge > maxrecharge->shield.recharge) shield.recharge = maxrecharge->shield.recharge; }*/ - if (up->energy.MaxValue() == energy.MaxValue() && up->recharge > recharge) { + /*TODO: if (up->energy.MaxValue() == energy.MaxValue() && up->recharge > recharge) { recharge = up->recharge; if (recharge > maxrecharge->recharge) { recharge = maxrecharge->recharge; } - } + }*/ } } } @@ -4233,6 +4240,9 @@ void Unit::ActTurn() { // Repair Ship Repair(); + // Power + energy_manager.Act(); + } void Unit::UpdatePhysics2(const Transformation &trans, @@ -4302,14 +4312,11 @@ void Unit::UpdatePhysics3(const Transformation &trans, bool lastframe, UnitCollection *uc, Unit *superunit) { + // This should replace all the UpdatePhysics ActTurn(); - if (fuel < 0) { - fuel = 0; - } - static CloakingStatus previous_status = cloak.status; - cloak.Update(this); + cloak.Update(); // Play once per cloaking if(cloak.Cloaking() && previous_status != CloakingStatus::cloaking) { @@ -4330,17 +4337,17 @@ void Unit::UpdatePhysics3(const Transformation &trans, difficulty_shields = g_game.difficulty; } - if (energy_before_shield) { + /*if (energy_before_shield) { rechargeEnergy(); - } + }*/ bool is_player_ship = _Universe->isPlayerStarship(this); RegenerateShields(difficulty_shields, is_player_ship); - ExpendEnergy(is_player_ship); + //ExpendEnergy(is_player_ship); - if (!energy_before_shield) { + /*if (!energy_before_shield) { rechargeEnergy(); - } + }*/ if (lastframe) { if (!(docked & (DOCKED | DOCKED_INSIDE))) { @@ -4538,7 +4545,7 @@ void Unit::UpdatePhysics3(const Transformation &trans, tracking_cone, hint)) { const WeaponInfo *typ = mounts[i].type; - energy += typ->energy_rate * (typ->type == WEAPON_TYPE::BEAM ? simulation_atom_var : 1); + // TODO: energy += typ->energy_rate * (typ->type == WEAPON_TYPE::BEAM ? simulation_atom_var : 1); } } else if (mounts[i].processed == Mount::UNFIRED || mounts[i].ref.refire > 2 * mounts[i].type->Refire()) { mounts[i].processed = Mount::UNFIRED; diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index 2147847efa..08f490d463 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -39,13 +39,15 @@ #include "movable.h" #include "computer.h" #include "intelligent.h" -#include "energetic.h" #include "carrier.h" #include "jump_capable.h" #include "mount.h" #include "damage/damage.h" +// Components +#include "components/afterburner.h" + #ifdef VS_DEBUG #define CONTAINER_DEBUG #endif @@ -76,7 +78,10 @@ void UncheckUnit( class Unit*un ); #include "SharedPool.h" #include "role_bitmask.h" #include "upgradeable_unit.h" -#include "cloak.h" + +// Components +#include "components/energy_manager.h" +#include "components/cloak.h" #include "components/radar.h" #include "configuration/configuration.h" @@ -136,7 +141,7 @@ struct PlanetaryOrbitData; */ // TODO: move Armed to subclasses -class Unit : public Armed, public Audible, public Drawable, public Damageable, public Energetic, +class Unit : public Armed, public Audible, public Drawable, public Damageable, public Intelligent, public Movable, public JumpCapable, public Carrier, public UpgradeableUnit { protected: @@ -145,6 +150,8 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p StringPool::Reference csvRow; public: + // Components + EnergyManager energy_manager; Cloak cloak; /// Radar and related systems @@ -985,6 +992,9 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p // object_a->field_a = object_b->field_b; float temporary_upgrade_float_variable; + // Temporary python functions + double fuelData() { return energy_manager.GetLevel(EnergyType::Fuel); } + double energyData() { return energy_manager.GetLevel(EnergyType::Energy); } }; Unit *findUnitInStarsystem(const void *unitDoNotDereference); diff --git a/engine/src/cmd/weapon_factory.cpp b/engine/src/cmd/weapon_factory.cpp index 3a00ae1f99..d5655f58bf 100644 --- a/engine/src/cmd/weapon_factory.cpp +++ b/engine/src/cmd/weapon_factory.cpp @@ -41,6 +41,8 @@ #include #include +#include "vs_globals.h" + namespace pt = boost::property_tree; namespace alg = boost::algorithm; @@ -130,7 +132,7 @@ void WeaponFactory::parseJSON(const std::string &weapon_text) { wi.size = getMountSize(getJSONValue(weapon, "mountsize", "Unknown_mount")); // Energy - wi.energy_rate = getJSONValue(weapon, "Energy.rate", wi.energy_rate); + wi.SetConsumption(getJSONValue(weapon, "Energy.rate", wi.GetConsumption())); wi.stability = getJSONValue(weapon, "Energy.stability", wi.stability); wi.refire_rate = getJSONValue(weapon, "Energy.refire", wi.refire_rate); wi.lock_time = getJSONValue(weapon, "Energy.locktime", wi.lock_time); @@ -226,8 +228,7 @@ void WeaponFactory::parse(ptree tree) { // Mount Size wi.size = getMountSize(inner.get(".mountsize", "Unknown_mount")); - // Energy - wi.energy_rate = inner.get("Energy..rate", wi.energy_rate); + wi.SetConsumption(inner.get("Energy..rate", wi.GetConsumption())); wi.stability = inner.get("Energy..stability", wi.stability); wi.refire_rate = inner.get("Energy..refire", wi.refire_rate); wi.lock_time = inner.get("Energy..locktime", wi.lock_time); diff --git a/engine/src/cmd/weapon_info.cpp b/engine/src/cmd/weapon_info.cpp index 0e5c729516..83893d9b54 100644 --- a/engine/src/cmd/weapon_info.cpp +++ b/engine/src/cmd/weapon_info.cpp @@ -37,14 +37,23 @@ namespace alg = boost::algorithm; Hashtable lookuptable; -WeaponInfo::WeaponInfo() { +WeaponInfo::WeaponInfo(): EnergyConsumer(EnergyType::Energy, + EnergyConsumerClassification::BallWeapon, + EnergyConsumerType::Momentary, + 0.0) { } -WeaponInfo::WeaponInfo(WEAPON_TYPE type) { +WeaponInfo::WeaponInfo(WEAPON_TYPE type) : EnergyConsumer(EnergyType::Energy, + EnergyConsumerClassification::BallWeapon, + EnergyConsumerType::Momentary, + 0.0) { this->type = type; } -WeaponInfo::WeaponInfo(const WeaponInfo &tmp) { +WeaponInfo::WeaponInfo(const WeaponInfo &tmp) : EnergyConsumer(tmp.energy_type, + tmp.classification, + tmp.consumer_type, + 0.0) { *this = tmp; } @@ -97,7 +106,7 @@ void WeaponInfo::netswap() { //size = VSSwapHostIntToLittle( size); damage = VSSwapHostFloatToLittle(damage); - energy_rate = VSSwapHostFloatToLittle(energy_rate); + consumption = VSSwapHostFloatToLittle(consumption); length = VSSwapHostFloatToLittle(length); lock_time = VSSwapHostFloatToLittle(lock_time); long_range = VSSwapHostFloatToLittle(long_range); diff --git a/engine/src/cmd/weapon_info.h b/engine/src/cmd/weapon_info.h index cdb1826143..d86d792d32 100644 --- a/engine/src/cmd/weapon_info.h +++ b/engine/src/cmd/weapon_info.h @@ -29,10 +29,11 @@ #include "weapon_type.h" #include "gfx/vec.h" #include "mount_size.h" +#include "components/energy_consumer.h" #include -struct WeaponInfo { +struct WeaponInfo : public EnergyConsumer { // Fields std::string name; WEAPON_TYPE type; @@ -40,7 +41,7 @@ struct WeaponInfo { // Make const again /*const*/ float damage = 1.8; - /*const*/ float energy_rate = 18; + /*const*/ //float energy_rate = 18; /*const*/ float length = 5; /*const*/ float lock_time = 0; /*const*/ float long_range = .5; diff --git a/engine/src/components/afterburner.cpp b/engine/src/components/afterburner.cpp new file mode 100644 index 0000000000..7ff6eb229f --- /dev/null +++ b/engine/src/components/afterburner.cpp @@ -0,0 +1,62 @@ +/* + * afterburner.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "afterburner.h" +#include "unit_csv_factory.h" + + + + + +// Note that usage_factor should be closely related to normal drive's usage factor. +// In most cases it should be 1.0 and the difference should be modelled using the afterburner_level. + +Afterburner::Afterburner(EnergyType type, double usage_factor): + EnergyConsumer(type, + EnergyConsumerClassification::Afterburner, + EnergyConsumerType::Variable, + 0.0), + usage_factor(usage_factor) {} + +Afterburner::Afterburner(EnergyType type, + double usage_factor, + double afterburner_level, + double mass, double simulation_atom_var): + EnergyConsumer(type, + EnergyConsumerClassification::Afterburner, + EnergyConsumerType::Variable, + 0.0), + usage_factor(usage_factor) { + consumption = usage_factor * afterburner_level * mass * simulation_atom_var; +} + + + +/*void Afterburner::WriteUnitString(std::map &unit) { + unit["Afterburner_Type"] = std::to_string(type); + unit["Afterburner_Usage_Cost"] = std::to_string(usage_factor); +}*/ diff --git a/engine/src/components/afterburner.h b/engine/src/components/afterburner.h new file mode 100644 index 0000000000..f31c854406 --- /dev/null +++ b/engine/src/components/afterburner.h @@ -0,0 +1,51 @@ +/* + * afterburner.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef AFTERBURNER_H +#define AFTERBURNER_H + +#include +#include + +#include "energy_container.h" +#include "energy_types.h" + + +class Afterburner : public EnergyConsumer +{ + double usage_factor; +public: + Afterburner(EnergyType type, double usage_factor); + Afterburner(EnergyType type, double usage_factor, double afterburner_level, + double mass, double simulation_atom_var); + //Afterburner(std::string unit_key); + + //void WriteUnitString(std::map &unit); + double UsageFactor() { return usage_factor; } +}; + +#endif // AFTERBURNER_H \ No newline at end of file diff --git a/engine/src/cmd/cloak.cpp b/engine/src/components/cloak.cpp similarity index 83% rename from engine/src/cmd/cloak.cpp rename to engine/src/components/cloak.cpp index 7f20ef5c86..885fc771bb 100644 --- a/engine/src/cmd/cloak.cpp +++ b/engine/src/components/cloak.cpp @@ -23,12 +23,15 @@ */ #include "cloak.h" +#include "energy_types.h" +#include "energy_consumer.h" #include "unit_csv_factory.h" #include "vegastrike.h" #include "configuration/configuration.h" -Cloak::Cloak() -{ +Cloak::Cloak() : EnergyConsumer(EnergyType::Energy, + EnergyConsumerClassification::Cloak, + EnergyConsumerType::Variable, 0.0) { status = CloakingStatus::disabled; energy = 0; @@ -38,8 +41,9 @@ Cloak::Cloak() minimum = 0; } -Cloak::Cloak(std::string unit_key) -{ +Cloak::Cloak(std::string unit_key) : EnergyConsumer(EnergyType::Energy, + EnergyConsumerClassification::Cloak, + EnergyConsumerType::Variable, 0.0) { if(UnitCSVFactory::GetVariable(unit_key, "Can_Cloak", false)) { status = CloakingStatus::ready; } else { @@ -54,8 +58,7 @@ Cloak::Cloak(std::string unit_key) current = 0; } -void Cloak::Save(std::map& unit) -{ +void Cloak::Save(std::map& unit) { unit["Cloak_Min"] = std::to_string(minimum); unit["Can_Cloak"] = std::to_string(Capable()); unit["Cloak_Rate"] = std::to_string(rate); @@ -63,7 +66,7 @@ void Cloak::Save(std::map& unit) unit["Cloak_Glass"] = std::to_string(glass); } -void Cloak::Update(Energetic *energetic) +void Cloak::Update() { // Unit is not capable of cloaking or damaged or just not cloaking if(status == CloakingStatus::disabled || @@ -72,22 +75,10 @@ void Cloak::Update(Energetic *energetic) return; } - // Use warp power for cloaking (SPEC capacitor) - const static bool warp_energy_for_cloak = configuration()->warp_config.use_warp_energy_for_cloak; - double available_energy = warp_energy_for_cloak ? energetic->warpenergy : energetic->energy.Value(); - - // Insufficient energy to cloak ship - if(available_energy < this->energy) { + if(powered < 1.0) { status = CloakingStatus::decloaking; - } else { - // Subtract the energy used - if (warp_energy_for_cloak) { - energetic->warpenergy -= (simulation_atom_var * energy); - } else { - energetic->energy -= (simulation_atom_var * energy); - } - } + } if(status == CloakingStatus::decloaking) { current = std::max(0.0, current - rate * simulation_atom_var); diff --git a/engine/src/cmd/cloak.h b/engine/src/components/cloak.h similarity index 97% rename from engine/src/cmd/cloak.h rename to engine/src/components/cloak.h index 967145d279..d3ead8c70e 100644 --- a/engine/src/cmd/cloak.h +++ b/engine/src/components/cloak.h @@ -28,7 +28,7 @@ #include #include -#include "energetic.h" +#include "energy_container.h" #include "damageable_layer.h" enum class CloakingStatus { @@ -40,7 +40,7 @@ enum class CloakingStatus { decloaking }; -class Cloak +class Cloak : EnergyConsumer { friend class Unit; @@ -65,9 +65,10 @@ class Cloak public: Cloak(); Cloak(std::string unit_key); + void Save(std::map& unit); - void Update(Energetic *energetic); + void Update(); void Toggle(); // Toggle cloak on/off bool Capable() const { diff --git a/engine/src/components/component.cpp b/engine/src/components/component.cpp index 556bf81a7b..b1f8c8b390 100644 --- a/engine/src/components/component.cpp +++ b/engine/src/components/component.cpp @@ -29,10 +29,10 @@ #include -/*Component::Component() +Component::Component() { -}*/ +} double random20() diff --git a/engine/src/components/component.h b/engine/src/components/component.h index 58a0fd0c53..87ede79ca2 100644 --- a/engine/src/components/component.h +++ b/engine/src/components/component.h @@ -29,17 +29,30 @@ #define COMPONENT_H -/*class Component +class Component { +protected: + bool installed = true; + bool enabled = true; + bool damaged = false; public: Component(); virtual ~Component() {} - virtual void damage(); - virtual void fix(); + void Install() { installed = true; } + void Uninstall() { installed = false; } + bool Installed() { return installed; } + void Enable() { enabled = true; } + void Disable() { enabled = false; } + bool Enabled() { return enabled; } -};*/ + + //virtual void damage(); + //virtual void fix(); + + +}; // These functions reduce functionality by a uniform distribution 0-1. // The function name's number component is the chance of the damage occurring. diff --git a/engine/src/components/drive.cpp b/engine/src/components/drive.cpp new file mode 100644 index 0000000000..1edb7a6f25 --- /dev/null +++ b/engine/src/components/drive.cpp @@ -0,0 +1,41 @@ +/* + * drive.cpp + * + * Copyright (C) 2001-2023 Daniel Horn, Benjaman 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 "drive.h" +#include "energy_types.h" + +// Note that usage_factor should be closely related to normal drive's usage factor. +// In most cases it should be 1.0 and the difference should be modelled using the afterburner_level. +Drive::Drive(EnergyType type, + double usage_factor, + double drive_level, + double mass, + double simulation_atom_var): + EnergyConsumer(type, + EnergyConsumerClassification::Afterburner, + EnergyConsumerType::Constant, + 0.0), + usage_factor(usage_factor) { + consumption = usage_factor * drive_level * mass * simulation_atom_var; +} \ No newline at end of file diff --git a/engine/src/components/drive.h b/engine/src/components/drive.h new file mode 100644 index 0000000000..c90afd55c2 --- /dev/null +++ b/engine/src/components/drive.h @@ -0,0 +1,42 @@ +/* + * drive.h + * + * Copyright (C) 2001-2023 Daniel Horn, Benjaman 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 DRIVE_H +#define DRIVE_H + +#include "component.h" +#include "energy_container.h" + + +class Drive : public Component, public EnergyConsumer { + double usage_factor; +public: + Drive(EnergyType type, + double usage_factor, + double drive_level, + double mass, + double simulation_atom_var); +}; + +#endif // DRIVE_H diff --git a/engine/src/components/energy_consumer.cpp b/engine/src/components/energy_consumer.cpp new file mode 100644 index 0000000000..7a585ff27f --- /dev/null +++ b/engine/src/components/energy_consumer.cpp @@ -0,0 +1,26 @@ +/* + * energy_consumer.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- \ No newline at end of file diff --git a/engine/src/components/energy_consumer.h b/engine/src/components/energy_consumer.h new file mode 100644 index 0000000000..4d4528a9a0 --- /dev/null +++ b/engine/src/components/energy_consumer.h @@ -0,0 +1,59 @@ +/* + * energy_consumer.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef ENERGYCONSUMER_H +#define ENERGYCONSUMER_H + +#include "energy_types.h" +#include "energy_consumer.h" +#include "resource.h" + +class EnergyConsumer { +protected: + EnergyType energy_type; + EnergyConsumerClassification classification; + EnergyConsumerType consumer_type; + bool in_use; + double powered; + Resource consumption; // This is when powered and for simulation_atom_var + + friend class EnergyContainer; + friend class EnergyManager; +public: + EnergyConsumer(EnergyType energy_type, EnergyConsumerClassification classification, + EnergyConsumerType consumer_type, double consumption): + energy_type(energy_type), classification(classification), consumer_type(consumer_type), in_use(false), + powered(0.0), consumption(Resource(consumption, 0.0, consumption)) {} + + double Powered() { return powered; } + void Use() { in_use = true;} + EnergyType GetEnergyType() { return energy_type; } + double GetConsumption() const { return consumption.Value(); } + void SetConsumption(const double consumption) { this->consumption = consumption; } +}; + +#endif // ENERGYCONSUMER_H diff --git a/engine/src/components/energy_container.cpp b/engine/src/components/energy_container.cpp new file mode 100644 index 0000000000..cbfc50ff0b --- /dev/null +++ b/engine/src/components/energy_container.cpp @@ -0,0 +1,127 @@ +/* + * energy_container.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "energy_container.h" + +#include + +EnergyContainer::EnergyContainer(): type(EnergyType::Fuel), + level(Resource(0.0,0.0,0.0)), + consumers(std::vector()) {} + +EnergyContainer::EnergyContainer(EnergyType type, double capacity): type(type), + level(Resource(capacity,0.0,capacity)), + consumers(std::vector()) {} + +void EnergyContainer::AddConsumer(EnergyType energy_type, + EnergyConsumerClassification classification, + EnergyConsumerType consumer_type, + double quantity) { + EnergyConsumer consumer(energy_type, classification, consumer_type, quantity); + consumers.push_back(consumer); +} + +// Return value - any surplus charge +double EnergyContainer::Charge(const double quantity) { + double old_level = level.Value(); + level += quantity; + + return quantity - level.Value() + old_level; +} + +double EnergyContainer::Deplete(const double quantity) { + double old_level = level.Value(); + level -= quantity; + return quantity + old_level - level.Value(); +} + +void EnergyContainer::SetCapacity(const double capacity, bool refill) { + if(refill) { + level = Resource(capacity,0,capacity); + } else { + level = Resource(0,0,capacity); + } +} + +double EnergyContainer::Level() const { return level.Value(); } +double EnergyContainer::MaxLevel() const { return level.MaxValue(); } +double EnergyContainer::Percent() const { + if(level.MaxValue() == 0.0) { + return 0.0; + } + + return level.Value()/level.MaxValue(); +} + +void EnergyContainer::Zero() { level = 0; } + +void EnergyContainer::Act() { + for(EnergyConsumer& consumer : consumers) { + if(consumer.consumer_type == EnergyConsumerType::Constant) { + consumer.in_use = true; + } else if(consumer.consumer_type == EnergyConsumerType::Variable) { + consumer.in_use = false; + } + + if(level < consumer.consumption) { + consumer.powered = level / consumer.consumption; + level = 0; + continue; + } + + std::cout << TypeToSave(type) << " ol: " << level; + level -= consumer.consumption; + std::cout << " nl: " << level << std::endl; + consumer.powered = 1.0; + } +} + +void EnergyContainer::Use(EnergyConsumerClassification classification) { + for(EnergyConsumer& consumer : consumers) { + consumer.in_use = true; + } +} + +bool EnergyContainer::InUse(EnergyConsumerClassification classification) { + for(EnergyConsumer& consumer : consumers) { + if(consumer.classification == classification) { + return consumer.in_use; + } + } + + return false; +} + +double EnergyContainer::Powered(EnergyConsumerClassification classification) { + for(EnergyConsumer& consumer : consumers) { + if(consumer.classification == classification) { + return consumer.powered; + } + } + + return false; +} diff --git a/engine/src/components/energy_container.h b/engine/src/components/energy_container.h new file mode 100644 index 0000000000..ee3afe0acc --- /dev/null +++ b/engine/src/components/energy_container.h @@ -0,0 +1,82 @@ +/* + * energy_container.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef ENERGYCONTAINER_H +#define ENERGYCONTAINER_H + +#include +#include + +#include "resource/resource.h" +#include "energy_types.h" +#include "energy_consumer.h" + + +/** + * @brief The EnergyContainer class models the fuel cell, capacitor and SPEC capacitor + */ +class EnergyContainer +{ + EnergyType type; + Resource level; + std::vector consumers; + + friend class EnergyManager; +public: + EnergyContainer(); + EnergyContainer(EnergyType type, double capacity); + + void AddConsumer(EnergyType energy_type, + EnergyConsumerClassification classification, + EnergyConsumerType consumer_type, + double quantity); + + void AddConsumer(EnergyConsumer consumer) { + consumers.push_back(consumer); + } + + // Return value - any surplus charge + double Charge(const double quantity); + + double Deplete(const double quantity); + + void SetCapacity(const double capacity, bool refill); + double Level() const; + double MaxLevel() const; + double Percent() const; + + void Zero(); + + void Act(); + + void Use(EnergyConsumerClassification classification); + + bool InUse(EnergyConsumerClassification classification); + double Powered(EnergyConsumerClassification classification); +}; + +#endif // ENERGYCONTAINER_H diff --git a/engine/src/components/energy_manager.cpp b/engine/src/components/energy_manager.cpp new file mode 100644 index 0000000000..a667553ac8 --- /dev/null +++ b/engine/src/components/energy_manager.cpp @@ -0,0 +1,172 @@ +/* + * energy_manager.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "energy_manager.h" +#include + +//////////////////////////////////////////////////////// + +// This section calculates the consumption of fuel and energy by different components. +// TODO: It really should move to the python side and be configurable. + +// This is calculated to provide longer range ships with approximately 60 minutes of gameplay. +// TODO: This does not calculate for WC +/*double getDriveConsumption(double factor, double mass) { + return mass * factor * simulation_atom_var; +} + +double getSPECDriveConsumption(double factor, double mass) { + return mass * factor * simulation_atom_var; +} + +double getJumpDriveConsumption(double factor, double mass) { + return mass * factor; +} + +// WC - 6 jumps +double getWCJumpDriveConsumption(double factor, double max_fuel) { + return max_fuel / factor; +} + +double getECMConsumption(double factor, double ecm_level) { + return ecm_level * factor * simulation_atom_var; +} + +double getShieldMaintenanceConsumption(double factor, int facets, double max_strength) { + return facets * max_strength * factor * simulation_atom_var; +} + +double getShieldRegenConsumption(double factor, int facets, double max_strength) { + return facets * max_strength * factor * simulation_atom_var; +} + +double getCloakConsumption(double factor, double mass) { + return mass * factor * simulation_atom_var; +}*/ + +//////////////////////////////////////////////////////// + +EnergyManager::EnergyManager() { + fuel = EnergyContainer(EnergyType::Fuel, 0.0); + energy = EnergyContainer(EnergyType::Energy, 0.0); + spec_energy = EnergyContainer(EnergyType::SPEC, 0.0); + reactor = Reactor(EnergyType::Fuel, 0.0, 0.0, 0.0, + &energy, &spec_energy, 0.1); +} + +void EnergyManager::Act() { + fuel.Act(); + double actual_reactor_usage = reactor.Generate(); + fuel.Charge(reactor.consumption - actual_reactor_usage); + energy.Act(); + spec_energy.Act(); +} + +void EnergyManager::AddConsumer(EnergyType energy_type, + EnergyConsumerClassification classification, + EnergyConsumerType consumer_type, + double consumption) { + EnergyContainer* container = GetContainer(energy_type); + EnergyConsumer consumer(energy_type, classification, consumer_type, consumption); + container->consumers.push_back(consumer); +} + +double EnergyManager::Deplete(const EnergyType type, const double quantity) { + EnergyContainer* container = GetContainer(type); + return container->Deplete(quantity); +} + +double EnergyManager::Deplete(EnergyConsumer consumer, const double quantity) { + EnergyContainer *container = GetContainer(consumer.energy_type); + if(!container) { + return 1.0; // None energy type. Infinite energy. + } + + return container->Deplete(quantity); +} + +EnergyContainer* EnergyManager::GetContainer(const EnergyType type) { + switch(type) { + case EnergyType::Fuel: + return &fuel; + case EnergyType::Energy: + return &energy; + case EnergyType::SPEC: + return &spec_energy; + default: + return nullptr; + } +} + +const EnergyContainer* EnergyManager::GetConstContainer(const EnergyType type) const { + switch(type) { + case EnergyType::Fuel: + return &fuel; + case EnergyType::Energy: + return &energy; + case EnergyType::SPEC: + return &spec_energy; + } +} + +double EnergyManager::GetLevel(const EnergyType type) const { + const EnergyContainer* container = GetConstContainer(type); + return container->level.Value(); +} + +double EnergyManager::GetMaxLevel(const EnergyType type) const { + const EnergyContainer* container = GetConstContainer(type); + return container->level.MaxValue(); +} + +void EnergyManager::SetCapacity(const EnergyType type, const double capacity) { + EnergyContainer* container = GetContainer(type); + container->SetCapacity(capacity, true); +} + +void EnergyManager::Refill(const EnergyType type) { + EnergyContainer* container = GetContainer(type); + container->level.SetToMax(); +} + +double EnergyManager::Percent(const EnergyType type) const { + const EnergyContainer* container = GetConstContainer(type); + if(!container) { + return 0.0; + } + + return container->level.Percent(); +} + +void EnergyManager::SetReactor(const double capacity, const double usage_factor, + const double reactor_level, const double simulation_atom_var) { + this->reactor.capacity = capacity; + double c = usage_factor * reactor_level * simulation_atom_var; + this->reactor.consumption = Resource(c, 0.0, c); + + fuel.AddConsumer(reactor); +} \ No newline at end of file diff --git a/engine/src/components/energy_manager.h b/engine/src/components/energy_manager.h new file mode 100644 index 0000000000..691de08b1f --- /dev/null +++ b/engine/src/components/energy_manager.h @@ -0,0 +1,81 @@ +/* + * energy_manager.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef ENERGYMANAGER_H +#define ENERGYMANAGER_H + +#include "energy_types.h" +#include "energy_container.h" +#include "reactor.h" + +/*double getDriveConsumption(double factor, double mass); // 1/36000 +double getSPECDriveConsumption(double factor, double mass); +double getJumpDriveConsumption(double factor, double mass); +double getWCJumpDriveConsumption(double factor, double max_fuel); // 1/6 +double getECMConsumption(double factor, double ecm_level); // ? +double getShieldMaintenanceConsumption(double factor, int facets, double max_strength); // 100 +double getShieldRegenConsumption(double factor, int facets, double max_strength); // 10 +double getCloakConsumption(double factor, double mass);*/ // 25 + +class EnergyManager { + EnergyContainer fuel; + EnergyContainer energy; + EnergyContainer spec_energy; + Reactor reactor; + +public: + EnergyManager(); + + void Act(); + + void AddConsumer(EnergyType energy_type, + EnergyConsumerClassification classification, + EnergyConsumerType consumer_type, + double consumption); + void AddConsumer(EnergyType energy_type, EnergyConsumer consumer) { + EnergyContainer* container = GetContainer(energy_type); + container->AddConsumer(consumer); + } + double Deplete(const EnergyType type, const double quantity); + double Deplete(EnergyConsumer consumer, const double quantity); + + EnergyContainer* GetContainer(const EnergyType type); + const EnergyContainer* GetConstContainer(const EnergyType type) const; + + double GetLevel(const EnergyType type) const; + double GetMaxLevel(const EnergyType type) const; + void SetCapacity(const EnergyType type, const double capacity); + void Refill(const EnergyType type); + double Percent(const EnergyType type) const; + double GetReactorCapacity() {return reactor.capacity; } + void SetReactorCapacity(const double capacity) { reactor.capacity = capacity; } + void SetReactor(const double capacity, const double usage_factor, + const double reactor_level, const double simulation_atom_var); +}; + + +#endif // ENERGYMANAGER_H \ No newline at end of file diff --git a/engine/src/components/energy_types.cpp b/engine/src/components/energy_types.cpp new file mode 100644 index 0000000000..446c56c3f7 --- /dev/null +++ b/engine/src/components/energy_types.cpp @@ -0,0 +1,46 @@ +/* + * energy_types.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "energy_types.h" + +EnergyType SaveToType(const int type) { + switch(type) { + case 0: return EnergyType::Energy; + case 1: return EnergyType::Fuel; + case 2: return EnergyType::SPEC; + default: return EnergyType::None; + } +} + +std::string TypeToSave(EnergyType type) { + switch(type) { + case EnergyType::Energy: return "0"; + case EnergyType::Fuel: return "1"; + case EnergyType::SPEC: return "2"; + default: return "3"; + } +} \ No newline at end of file diff --git a/engine/src/components/energy_types.h b/engine/src/components/energy_types.h new file mode 100644 index 0000000000..d5fcb29449 --- /dev/null +++ b/engine/src/components/energy_types.h @@ -0,0 +1,74 @@ +/* + * energy_types.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef ENERGYTYPES_H +#define ENERGYTYPES_H + +#include + +enum class EnergyType { + Fuel, // 1 + Energy, // 0 Capacitor + SPEC, // 2 FTL + None // 3 Free Energy +}; + +EnergyType SaveToType(const int type); + +std::string TypeToSave(EnergyType type); + + +enum class EnergyConsumerClassification { + // Fuel + Reactor, + Drive, + Afterburner, + + // Energy + LifeSupport, + Radar, + ShieldRegen, + ShieldMaintenance, + ECM, + Cloak, + BallWeapon, + BeamWeapon, + BoltWeapon, + + // SPEC + SPECDrive, + JumpDrive +}; + +enum class EnergyConsumerType { + Constant, // Life Support, Radar, etc. + Variable, // Beam Weapons, Afterburner + Momentary // Ball/Bolt Weapons, Jump Drive +}; + + +#endif // ENERGYTYPES_H \ No newline at end of file diff --git a/engine/src/components/jump_drive.cpp b/engine/src/components/jump_drive.cpp new file mode 100644 index 0000000000..cecc6b477c --- /dev/null +++ b/engine/src/components/jump_drive.cpp @@ -0,0 +1,47 @@ +/* + * jump_drive.cpp + * + * Copyright (C) 2001-2023 Daniel Horn, Benjaman 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 "jump_drive.h" + +JumpDrive::JumpDrive(): + EnergyConsumer(EnergyType::None, + EnergyConsumerClassification::JumpDrive, + EnergyConsumerType::Variable, + 0.0), + delay(0.0){} + +JumpDrive::JumpDrive(double consumption, double delay) : + EnergyConsumer(EnergyType::SPEC, + EnergyConsumerClassification::JumpDrive, + EnergyConsumerType::Variable, consumption), + delay(delay) {} + +bool JumpDrive::Ready() { + return installed && enabled; +} + +void JumpDrive::SetDestination(int destination) { + this->destination = destination; +} + diff --git a/engine/src/components/jump_drive.h b/engine/src/components/jump_drive.h new file mode 100644 index 0000000000..f85e4c9baf --- /dev/null +++ b/engine/src/components/jump_drive.h @@ -0,0 +1,44 @@ +/* + * jump_drive.h + * + * Copyright (C) 2001-2023 Daniel Horn, Benjaman 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 JUMP_DRIVE_H +#define JUMP_DRIVE_H + +#include "component.h" +#include "energy_types.h" +#include "energy_container.h" + +class JumpDrive : public Component, public EnergyConsumer { + int destination; + double delay; + +public: + JumpDrive(); + JumpDrive(double consumption, double delay); + + bool Ready(); + void SetDestination(int destination); +}; + +#endif // JUMP_DRIVE_H diff --git a/engine/src/components/reactor.cpp b/engine/src/components/reactor.cpp new file mode 100644 index 0000000000..275fee172b --- /dev/null +++ b/engine/src/components/reactor.cpp @@ -0,0 +1,71 @@ +/* + * reactor.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "reactor.h" +#include + +Reactor::Reactor(): EnergyConsumer(EnergyType::None, + EnergyConsumerClassification::Reactor, + EnergyConsumerType::Constant, + 0.0), + capacity(0.0), + energy_container(nullptr), + spec_energy_container(nullptr) {} + +Reactor::Reactor(EnergyType energy_type, double usage_factor, double reactor_level, + double capacity, + EnergyContainer *energy_container, + EnergyContainer *spec_energy_container, + double simulation_atom_var): + EnergyConsumer(energy_type, + EnergyConsumerClassification::Reactor, + EnergyConsumerType::Constant, + 0.0), + capacity(capacity), + energy_container(energy_container), + spec_energy_container(spec_energy_container) { + double c = usage_factor * reactor_level * simulation_atom_var; + this->consumption = Resource(c, 0.0, c); +} + +double Reactor::Generate() { + double actual_consumption = 0.0; + double surplus = energy_container->Charge(capacity); + surplus = spec_energy_container->Charge(surplus); + surplus = capacity; + + if(capacity == 0.0) { + // We generate nothing and so consume no fuel + actual_consumption = 0.0; + } else if(surplus == 0.0) { + actual_consumption = consumption.MaxValue(); + } else { + actual_consumption = (1 - (surplus / capacity)) * consumption.MaxValue(); + } + + return actual_consumption; +} \ No newline at end of file diff --git a/engine/src/components/reactor.h b/engine/src/components/reactor.h new file mode 100644 index 0000000000..9e736caef0 --- /dev/null +++ b/engine/src/components/reactor.h @@ -0,0 +1,53 @@ +/* + * reactor.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef REACTOR_H +#define REACTOR_H + +#include "energy_container.h" +#include "energy_consumer.h" + +class Reactor: public EnergyConsumer +{ + double capacity; + EnergyContainer *energy_container; + EnergyContainer *spec_energy_container; + + friend class EnergyManager; +public: + Reactor(); + Reactor(EnergyType energy_type, + double usage_factor, // A general factor to tweak consumption (fixed per game) + double reactor_level, // A measure of the reactor level (variable between models) + double capacity, // How much energy does the reactor generate + EnergyContainer *energy_container, + EnergyContainer *spec_energy_container, + double simulation_atom_var); + double Generate(); +}; + +#endif // REACTOR_H diff --git a/engine/src/components/tests/balancing_tests.cpp b/engine/src/components/tests/balancing_tests.cpp new file mode 100644 index 0000000000..258d5c4e0c --- /dev/null +++ b/engine/src/components/tests/balancing_tests.cpp @@ -0,0 +1,154 @@ +#include + +#include "energy_container.h" +#include "energy_manager.h" +#include "reactor.h" + + +double simulation_atom_var = 0.1; + +struct ReactorSetup { + double capacity; + double level; + double usage_factor; +}; + +struct ContainersSetup { + double fuel_capacity; + double energy_capacity; + double spec_capacity; +}; + +struct ConsumerSetup { + EnergyType energy_type; + EnergyConsumerClassification classification; + EnergyConsumerType consumer_type; + double consumption; +}; + +double reactor_capacity = 15; +double reactor_level = 1.0; + +double fuel_capacity = 3.51; // Robin +double energy_capacity = 100.0; // capacitor 1 +double spec_capacity = 200.0; // spec capacitor 1 + +double mass = 48; + +// Consumers +// Fuel +double reactor_usage_factor = 0.001; +double drive = 1; +double afterburner = 3; + +// Energy +double shield_recharge = 4; +double lifeSupport = 1; +double radar = 1; +double shieldRegen = 1; +double shieldMaintenance = 1; +double ECM = 10; +double Cloak = 10; + +// SPEC +double SPECDrive = 1; +double JumpDrive = 1; + +EnergyManager setup(ContainersSetup containers_setup, ReactorSetup reactor_setup, + double simulation_atom_var) { + EnergyManager manager = EnergyManager(); + + manager.SetCapacity(EnergyType::Fuel, containers_setup.fuel_capacity); + manager.SetCapacity(EnergyType::Energy, containers_setup.energy_capacity); + manager.SetCapacity(EnergyType::SPEC, containers_setup.spec_capacity); + manager.SetReactor(reactor_setup.capacity, reactor_setup.usage_factor, + reactor_setup.level, simulation_atom_var); + + return manager; +} + +void setupConsumer(EnergyManager& manager, ConsumerSetup setup) { + manager.AddConsumer(setup.energy_type, setup.classification, setup.consumer_type, setup.consumption); +} + +double fuelBurn(ContainersSetup containers_setup, ReactorSetup reactor_setup, + std::vector consumers_setup, int seconds, + int print_every_n = 1000) { + int run_time = seconds / simulation_atom_var; + EnergyManager manager = setup(containers_setup, reactor_setup, simulation_atom_var); + for(ConsumerSetup setup : consumers_setup) { + setupConsumer(manager, setup); + } + + for(int i = 0;i(), + seconds); + + std::cout << "NoFuelBurn percent left: " << result * 100 << std::endl; +} + +// This tests a fighter ship with level 1 equipment and steady 50MJ energy consumption +TEST(FuelBurn, RobinNaive_1) { + ContainersSetup containers_setup = {3.51, 100.0, 200.0}; + ReactorSetup reactor_setup = {15.0, reactor_usage_factor, 1.0}; + std::vector consumers_setup = { + {EnergyType::Fuel, EnergyConsumerClassification::Drive, + EnergyConsumerType::Constant, 0.0001}, + {EnergyType::Fuel, EnergyConsumerClassification::Afterburner, + EnergyConsumerType::Constant, 0.0001 * 3 * .05}, // Drive consumption x 3 but 5% of flight time + {EnergyType::Energy, EnergyConsumerClassification::LifeSupport, + EnergyConsumerType::Constant, 50.0 * simulation_atom_var} // General consumer at 50 per second + }; + + int seconds = 60 * 60; // 60 minutes gameplay + + double result = fuelBurn(containers_setup, reactor_setup, consumers_setup, + seconds); + + std::cout << "NaiveFuelBurn_1 percent left: " << result * 100 << std::endl; +} + +// This tests a fighter ship with level 1 equipment and steady 150MJ energy consumption +TEST(FuelBurn, RobinNaive_2) { + ContainersSetup containers_setup = {3.51, 300.0, 200.0}; + ReactorSetup reactor_setup = {44.0, reactor_usage_factor, 3.0}; + std::vector consumers_setup = { + {EnergyType::Fuel, EnergyConsumerClassification::Drive, + EnergyConsumerType::Constant, 0.0001}, + {EnergyType::Fuel, EnergyConsumerClassification::Afterburner, + EnergyConsumerType::Constant, 0.0001 * 3 * .05}, // Drive consumption x 3 but 5% of flight time + {EnergyType::Energy, EnergyConsumerClassification::LifeSupport, + EnergyConsumerType::Constant, 150.0 * simulation_atom_var} // General consumer at 150 per second + }; + + int seconds = 60 * 60; // 60 minutes gameplay + + double result = fuelBurn(containers_setup, reactor_setup, consumers_setup, + seconds); + + std::cout << "NaiveFuelBurn_2 percent left: " << result * 100 << std::endl; +} \ No newline at end of file diff --git a/engine/src/components/tests/energy_container_tests.cpp b/engine/src/components/tests/energy_container_tests.cpp new file mode 100644 index 0000000000..32c1dad1a7 --- /dev/null +++ b/engine/src/components/tests/energy_container_tests.cpp @@ -0,0 +1,30 @@ +#include + +#include "energy_container.h" +#include "energy_consumer.h" + +void printContainer(EnergyContainer& container) { + std::cout << "Max Level: " << container.MaxLevel(); + std::cout << " Level: " << container.Level(); + std::cout << " Percent: " << container.Percent() << std::endl; +} + +TEST(EnergyContainer, Sanity) { + EnergyContainer container = EnergyContainer(); + EXPECT_EQ(container.Level(), 0.0); + container.SetCapacity(10.0, true); + + printContainer(container); + + EnergyConsumer consumer = EnergyConsumer(EnergyType::Fuel, + EnergyConsumerClassification::Drive, + EnergyConsumerType::Constant, 1.0); + container.AddConsumer(consumer); + + int i=0; + while(container.Level() > 0 && i < 100) { + container.Act(); + printContainer(container); + i++; + } +} \ No newline at end of file diff --git a/engine/src/configuration/configuration.h b/engine/src/configuration/configuration.h index a3f82a220d..4038d02b6a 100644 --- a/engine/src/configuration/configuration.h +++ b/engine/src/configuration/configuration.h @@ -171,8 +171,8 @@ struct Fuel { float normal_fuel_usage; bool reactor_uses_fuel; float vsd_mj_yield{5.4F}; - float no_fuel_thrust{0.4F}; - float no_fuel_afterburn{0.1F}; + double no_fuel_thrust{0.4}; + double no_fuel_afterburn{0.1}; bool variable_fuel_consumption{false}; float deuterium_relative_efficiency_lithium{1.0F}; float fmec_factor{0.000000008F}; diff --git a/engine/src/gfx/cockpit.cpp b/engine/src/gfx/cockpit.cpp index d36a842792..3658373440 100644 --- a/engine/src/gfx/cockpit.cpp +++ b/engine/src/gfx/cockpit.cpp @@ -418,20 +418,21 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { return .25 * (armordat[0] + armordat[2] + armordat[4] + armordat[6]); } case UnitImages::FUEL: - if (target->fuelData() > maxfuel) { - maxfuel = target->fuelData(); - } - if (maxfuel > 0) { - return target->fuelData() / maxfuel; - } - return 0; + return target->energy_manager.Percent(EnergyType::Fuel); case UnitImages::ENERGY: - return target->energyData(); + return target->energy_manager.Percent(EnergyType::Energy); case UnitImages::WARPENERGY: { + // TODO: check how WC deals with this. + // Code below should be relevant + //if (jump.energy > 0) { + // return ((float) warpenergy) / ((float) jump.energy); + //} + const bool warpifnojump = configuration()->graphics_config.hud.display_warp_energy_if_no_jump_drive; - return (warpifnojump || target->GetJumpStatus().drive != -2) ? target->warpEnergyData() : 0; + return (warpifnojump || target->jump.drive != -2) ? + target->energy_manager.Percent(EnergyType::SPEC) : 0; } case UnitImages::HULL: if (maxhull < target->GetHull()) { @@ -727,9 +728,9 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { return (float) UnitImages::NOMINAL; } case UnitImages::CANJUMP_MODAL: - if (-2 == target->GetJumpStatus().drive) { + if (-2 == target->jump.drive) { return (float) UnitImages::NODRIVE; - } else if (target->getWarpEnergy() < target->GetJumpStatus().energy) { + } else if (target->energy_manager.GetLevel(EnergyType::SPEC) < target->jump.energy) { return (float) UnitImages::NOTENOUGHENERGY; } else if (target->graphicOptions.InWarp) { //FIXME return (float) UnitImages::OFF; diff --git a/engine/src/gfx/cockpit_generic.cpp b/engine/src/gfx/cockpit_generic.cpp index b2ed5a66e0..8d27504da5 100644 --- a/engine/src/gfx/cockpit_generic.cpp +++ b/engine/src/gfx/cockpit_generic.cpp @@ -191,7 +191,6 @@ void Cockpit::SetParent(Unit *unit, const char *filename, const char *unitmodnam if (StartArmor[7] == 0) { StartArmor[7] = 1; } - maxfuel = unit->fuelData(); maxhull = unit->GetHull(); } } @@ -538,7 +537,7 @@ bool Cockpit::Update() { "shield_energy_downpower_percent", ".66666666666666")); - bool toolittleenergy = (par->energyData() <= minEnergyForShieldDownpower); + bool toolittleenergy = false; // TODO:(par->energyData() <= minEnergyForShieldDownpower); if (toolittleenergy) { secondsWithZeroEnergy += SIMULATION_ATOM; if (secondsWithZeroEnergy > minEnergyShieldTime) { diff --git a/engine/src/gfx/cockpit_generic.h b/engine/src/gfx/cockpit_generic.h index a82fe80513..6bfe31cb81 100644 --- a/engine/src/gfx/cockpit_generic.h +++ b/engine/src/gfx/cockpit_generic.h @@ -161,7 +161,7 @@ class Cockpit { float StartArmor[9]; //short fix ///saved values to compare with current values (might need more for damage) - float maxfuel, maxhull; + float maxhull; ///this is the parent that Cockpit will read data from UnitContainer parent; diff --git a/engine/src/python/unit_wrapper.cpp b/engine/src/python/unit_wrapper.cpp index 2ea75a7c9f..39aeb4040f 100644 --- a/engine/src/python/unit_wrapper.cpp +++ b/engine/src/python/unit_wrapper.cpp @@ -272,7 +272,7 @@ static BoostPythonDictionary GatherWeaponInfo(const WeaponInfo *wi) { rv["stability"] = wi->stability; rv["longRange"] = wi->long_range; rv["lockTime"] = wi->lock_time; - rv["energyRate"] = wi->energy_rate; + rv["energyRate"] = wi->GetConsumption(); // TODO: beam weapons div by sim_atom_var rv["refire"] = wi->Refire(); rv["volume"] = wi->volume; rv["name"] = wi->name; diff --git a/engine/src/python/unit_wrapper_class.h b/engine/src/python/unit_wrapper_class.h index 633eb16d00..1c5f494fef 100644 --- a/engine/src/python/unit_wrapper_class.h +++ b/engine/src/python/unit_wrapper_class.h @@ -267,7 +267,7 @@ class UnitWrapper : public UnitContainer { { CHECKME -1; } - return unit->GetJumpStatus().drive; + return unit->jump.drive; } void ApplyDamage(Vector pnt, diff --git a/engine/src/resource/resource.cpp b/engine/src/resource/resource.cpp index 1561309634..0df1fa17fd 100644 --- a/engine/src/resource/resource.cpp +++ b/engine/src/resource/resource.cpp @@ -63,6 +63,10 @@ T Resource::Percent() const { return -1; } + if(max_value_ == 0) { // Can't calculate percent if divider is 0 + return -1; + } + return value_ / max_value_; } @@ -84,6 +88,15 @@ void Resource::Set(const T &value) { value_ = std::max(min_value_, value_); } +template +void Resource::SetToMax() { + if(no_max_) { // Can't set to max if there's no max + return; + } + + value_ = max_value_; +} + template void Resource::SetMaxValue(const T &value) { if(no_max_) { // Can't set max if there's no max @@ -140,6 +153,17 @@ void Resource::Zero() { * Overloaded operators */ +template +Resource Resource::operator=(const T &value) { + value_ = value; + if(!no_max_) { + value_ = std::min(max_value_, value_); + } + value_ = std::max(min_value_, value_); + + return *this; +} + template Resource Resource::operator+=(const T &value) { if(!no_max_) { // Only applicable if there's max diff --git a/engine/src/resource/resource.h b/engine/src/resource/resource.h index a43c4e75e3..947b97f685 100644 --- a/engine/src/resource/resource.h +++ b/engine/src/resource/resource.h @@ -39,6 +39,8 @@ class Resource { public: Resource(const T &value = 0, const T &min_value = 0, const T &max_value = -1); + + Resource operator=(const T &value); Resource operator+=(const T &value); Resource operator-=(const T &value); @@ -87,6 +89,7 @@ class Resource { T Percent() const; void ResetMaxValue(); void Set(const T &value); + void SetToMax(); void SetMaxValue(const T &value); void Upgrade(const T &value); void UpgradeByPercent(const T &value); diff --git a/engine/src/resource/tests/resource_test.cpp b/engine/src/resource/tests/resource_test.cpp index ac9bdd4801..2d56ddcca6 100644 --- a/engine/src/resource/tests/resource_test.cpp +++ b/engine/src/resource/tests/resource_test.cpp @@ -17,4 +17,7 @@ TEST(Resource, Sanity) { resource -= 12.0f; EXPECT_EQ(resource.Value(), 0.0f); + + Resource dbl_resource = Resource(0.0,0.0,0.0); + EXPECT_EQ(dbl_resource.Value(), 0.0); } diff --git a/engine/src/star_system.cpp b/engine/src/star_system.cpp index 99c68e37c9..37bce4078c 100644 --- a/engine/src/star_system.cpp +++ b/engine/src/star_system.cpp @@ -1313,7 +1313,7 @@ void StarSystem::ProcessPendingJumps() { bool dosightandsound = ((pendingjump[kk]->dest == savedStarSystem) || _Universe->isPlayerStarship(un)); _Universe->setActiveStarSystem(pendingjump[kk]->orig); if (un->TransferUnitToSystem(kk, savedStarSystem, dosightandsound)) { - un->decreaseWarpEnergy(false, 1.0f); + // TODO: un->decreaseWarpEnergy(false, 1.0f); } if (dosightandsound) { _Universe->activeStarSystem()->DoJumpingComeSightAndSound(un); @@ -1420,7 +1420,7 @@ bool StarSystem::JumpTo(Unit *un, Unit *jumppoint, const std::string &system, bo ani = _Universe->activeStarSystem()->DoJumpingLeaveSightAndSound(un); } _Universe->AccessCockpit()->OnJumpBegin(un); - pendingjump.push_back(new unorigdest(un, jumppoint, this, ss, un->GetJumpStatus().delay, ani, justloaded, + pendingjump.push_back(new unorigdest(un, jumppoint, this, ss, un->jump.delay, ani, justloaded, save_coordinates ? ComputeJumpPointArrival(un->Position(), this->getFileName(), system) : QVector(0, 0, 0))); diff --git a/engine/src/star_system_jump.cpp b/engine/src/star_system_jump.cpp index eb5e64f4a3..b61fdf140f 100644 --- a/engine/src/star_system_jump.cpp +++ b/engine/src/star_system_jump.cpp @@ -119,7 +119,7 @@ void StarSystem::DrawJumpStars() { un->Position() + r.Cast() * un->rSize() * (pendingjump[kk]->delay + .25)); JumpAnimations[k].a->SetOrientation(p, q, r); float dd = un->rSize() * game_options()->jumpgatesize - * (un->GetJumpStatus().delay - pendingjump[kk]->delay) / (float) un->GetJumpStatus().delay; + * (un->jump.delay - pendingjump[kk]->delay) / (float) un->jump.delay; JumpAnimations[k].a->SetDimensions(dd, dd); } } @@ -158,7 +158,7 @@ int StarSystem::DoJumpingLeaveSightAndSound(Unit *un) { int ani; Vector p, q, r; un->GetOrientation(p, q, r); - ani = AddJumpAnimation(un->Position() + r.Cast() * un->rSize() * (un->GetJumpStatus().delay + .25), + ani = AddJumpAnimation(un->Position() + r.Cast() * un->rSize() * (un->jump.delay + .25), 10 * un->rSize()); static int jumpleave = AUDCreateSound(game_options()->jumpleave, false); AUDPlay(jumpleave, un->LocalPosition(), un->GetVelocity(), 1); From 2c0e12b6a6c57347596047d205d53aa010b03b53 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Tue, 12 Mar 2024 09:13:25 +0200 Subject: [PATCH 08/16] Refactor resource class It's semantics, but can no longer upgrade. Rather, can be damaged. Add unit tests. Fix bug in constructor. Fix bug in operator +=. Also, rename SPEC to more generic FTL. It's a better term even if it's not accurate. --- engine/src/cmd/ai/docking.cpp | 4 +- engine/src/cmd/ai/warpto.cpp | 2 +- engine/src/cmd/basecomputer.cpp | 18 +- engine/src/cmd/jump_capable.cpp | 2 +- engine/src/cmd/planet.cpp | 2 +- engine/src/cmd/unit_csv.cpp | 6 +- engine/src/cmd/unit_generic.cpp | 6 +- engine/src/components/energy_manager.cpp | 6 +- engine/src/components/energy_types.cpp | 4 +- engine/src/components/energy_types.h | 6 +- engine/src/components/jump_drive.cpp | 2 +- .../src/components/tests/balancing_tests.cpp | 4 +- engine/src/gfx/cockpit.cpp | 4 +- engine/src/resource/resource.cpp | 114 +++++++---- engine/src/resource/resource.h | 20 +- engine/src/resource/tests/resource_test.cpp | 185 +++++++++++++++++- 16 files changed, 311 insertions(+), 74 deletions(-) diff --git a/engine/src/cmd/ai/docking.cpp b/engine/src/cmd/ai/docking.cpp index 7b0b141e46..c1357ed8bd 100644 --- a/engine/src/cmd/ai/docking.cpp +++ b/engine/src/cmd/ai/docking.cpp @@ -206,7 +206,7 @@ bool DockingOps::DockToTarget(Unit *utdw) { if (physicallyDock) { return parent->Dock(utdw); } else { - utdw->energy_manager.Refill(EnergyType::SPEC); + utdw->energy_manager.Refill(EnergyType::FTL); return true; } } else if (diss <= 1.2 * rad * rad) { @@ -216,7 +216,7 @@ bool DockingOps::DockToTarget(Unit *utdw) { if (physicallyDock) { return parent->Dock(utdw); } else { - parent->energy_manager.Refill(EnergyType::SPEC); + parent->energy_manager.Refill(EnergyType::FTL); return true; } } diff --git a/engine/src/cmd/ai/warpto.cpp b/engine/src/cmd/ai/warpto.cpp index 51a13de80d..7a5fd1cb22 100644 --- a/engine/src/cmd/ai/warpto.cpp +++ b/engine/src/cmd/ai/warpto.cpp @@ -98,7 +98,7 @@ static void ActuallyWarpTo(Unit *parent, const QVector &tarpos, Vector tarvel, U XMLSupport::parse_float(vs_config->getVariable("AI", "min_energy_to_enter_warp", ".33")); static float min_warpfield_to_enter_warp = XMLSupport::parse_float(vs_config->getVariable("AI", "min_warp_to_try", "1.5")); - if ((parent->energy_manager.Percent(EnergyType::SPEC) > min_energy_to_enter_warp) + if ((parent->energy_manager.Percent(EnergyType::FTL) > min_energy_to_enter_warp) && (parent->GetMaxWarpFieldStrength(1) > min_warpfield_to_enter_warp)) { if (parent->graphicOptions.InWarp == 0) { parent->graphicOptions.InWarp = 1; //don't want the AI thrashing diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index 39f299c6b4..8703be2847 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -5341,7 +5341,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C //note: I found no function to get max warp energy, but since we're docked they are the same if (!subunitlevel) { PRETTY_ADDU(statcolor + "Warp capacitor bank storage: #-c", - playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) * RSconverter * Wconv, + playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) * RSconverter * Wconv, 0, "MJ"); @@ -5364,7 +5364,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C PRETTY_ADDU(statcolor + "Delay: #-c", uj.delay, 0, "seconds"); if (uj.damage > 0) PRETTY_ADDU(statcolor + "Damage to outsystem jump drive: #-c", uj.damage * VSDM, 0, "MJ"); - if (playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) < uj.energy) { + if (playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) < uj.energy) { text += "#n##c1:.3:.3#" + prefix + "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c"; @@ -5380,9 +5380,9 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) PRETTY_ADDU(statcolor + "Installs main capacitor bank with storage capacity: #-c", (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) * RSconverter), 0, "MJ"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::SPEC))) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::FTL))) PRETTY_ADDU(statcolor + "Installs warp capacitor bank with storage capacity: #-c", - playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) * RSconverter * Wconv, 0, "MJ"); + playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) * RSconverter * Wconv, 0, "MJ"); if (buj.drive != uj.drive) { text += statcolor + "#n#Allows travel via Jump Points.#n#Consult your personal info screen for ship specific energy requirements. #-c"; @@ -5397,9 +5397,9 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) * RSconverter), 0, "MJ of storage to main capacitor banks"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::SPEC))) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::FTL))) PRETTY_ADDU(statcolor + "Adds #-c", - playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) * RSconverter * Wconv, + playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) * RSconverter * Wconv, 0, "MJ of storage to warp capacitor bank"); break; @@ -5410,9 +5410,9 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) PRETTY_ADDU(statcolor + "Increases main capacitor bank storage by #-c", 100.0 * (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) - 1), 0, "%"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::SPEC))) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::FTL))) PRETTY_ADDU(statcolor + "Increases warp capacitor bank storage by #-c", - (playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) - 1) * 100, 0, "%"); + (playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) - 1) * 100, 0, "%"); break; default: //Failure text += "Oh dear, this wasn't an upgrade. Please debug code."; @@ -5778,7 +5778,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C + "WARNING: Capacitor banks are overdrawn: downgrade shield, upgrade reactor or purchase reactor capacitance!#-c"; } - if (uj.drive != -2 && playerUnit->energy_manager.GetMaxLevel(EnergyType::SPEC) < uj.energy) { + if (uj.drive != -2 && playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) < uj.energy) { text += "#n##c1:.3:.3#" + prefix + "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c"; diff --git a/engine/src/cmd/jump_capable.cpp b/engine/src/cmd/jump_capable.cpp index 7a5c64030a..e08fa44460 100644 --- a/engine/src/cmd/jump_capable.cpp +++ b/engine/src/cmd/jump_capable.cpp @@ -141,7 +141,7 @@ bool JumpCapable::AutoPilotToErrorMessage(const Unit *target, // TODO: move to energymanager.canpower and // energycontainer.canpower - if (unit->energy_manager.GetLevel(EnergyType::SPEC) < jump_drive.GetConsumption()) { + if (unit->energy_manager.GetLevel(EnergyType::FTL) < jump_drive.GetConsumption()) { if (!ignore_energy_requirements) { return false; } diff --git a/engine/src/cmd/planet.cpp b/engine/src/cmd/planet.cpp index 97367b4d8f..0013301058 100644 --- a/engine/src/cmd/planet.cpp +++ b/engine/src/cmd/planet.cpp @@ -462,7 +462,7 @@ void Planet::InitPlanet(QVector x, VSSprite *tmp = pImage->pHudImage; pImage->pHudImage = un->GetImageInformation().pHudImage; un->GetImageInformation().pHudImage = tmp; - energy_manager.SetCapacity(EnergyType::SPEC, un->energy_manager.GetMaxLevel(EnergyType::SPEC)); + energy_manager.SetCapacity(EnergyType::FTL, un->energy_manager.GetMaxLevel(EnergyType::FTL)); if (smartplanets) { SubUnits.prepend(un); un->SetRecursiveOwner(this); diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index 08ff6a6698..1c600fbb63 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -402,7 +402,7 @@ static void AddSubUnits(Unit *thus, if (randomspawn) { // TODO: surely we could use something more relevant than max SPEC // to determine chance to spawn... - int chancetospawn = float_to_int(xml.units[a]->energy_manager.GetMaxLevel(EnergyType::SPEC)); + int chancetospawn = float_to_int(xml.units[a]->energy_manager.GetMaxLevel(EnergyType::FTL)); if (chancetospawn > rand() % 100) { thus->SubUnits.prepend(xml.units[a]); } else { @@ -741,7 +741,7 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ energy_manager.SetCapacity(EnergyType::Energy, UnitCSVFactory::GetVariable(unit_key, "Primary_Capacitor", 0.0f)); - energy_manager.SetCapacity(EnergyType::SPEC, + energy_manager.SetCapacity(EnergyType::FTL, UnitCSVFactory::GetVariable(unit_key, "Warp_Capacitor", 0.0f)); // Hull @@ -1236,7 +1236,7 @@ string Unit::WriteUnitString() { // Components unit["Fuel_Capacity"] = tos(energy_manager.GetMaxLevel(EnergyType::Fuel)); unit["Primary_Capacitor"] = tos(energy_manager.GetMaxLevel(EnergyType::Energy)); - unit["Warp_Capacitor"] = tos(energy_manager.GetMaxLevel(EnergyType::SPEC)); + unit["Warp_Capacitor"] = tos(energy_manager.GetMaxLevel(EnergyType::FTL)); unit["Hull"] = tos(GetHullLayer().facets[0].health); unit["Spec_Interdiction"] = tos(specInterdiction); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 766a663365..6526dfc478 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -988,7 +988,7 @@ extern void ActivateAnimation(Unit *jp); // Move to jump_enabled void TurnJumpOKLightOn(Unit *un, Cockpit *cp) { if (cp) { - if (un->energy_manager.GetLevel(EnergyType::SPEC) >= un->jump.energy) { + if (un->energy_manager.GetLevel(EnergyType::FTL) >= un->jump.energy) { if (un->jump.drive > -2) { cp->jumpok = 1; } @@ -1013,7 +1013,7 @@ bool Unit::jumpReactToCollision(Unit *smalle) { //we have a drive if ((!SPEC_interference && (smalle->jump.drive >= 0 && //we have power - (smalle->energy_manager.GetLevel(EnergyType::SPEC) >= smalle->jump.energy + (smalle->energy_manager.GetLevel(EnergyType::FTL) >= smalle->jump.energy //or we're being cheap || (ai_jump_cheat && cp == nullptr) ))) @@ -1040,7 +1040,7 @@ bool Unit::jumpReactToCollision(Unit *smalle) { return false; } if ((!SPEC_interference && (jump.drive >= 0 - && (energy_manager.GetLevel(EnergyType::SPEC) >= jump.energy || (ai_jump_cheat && cp == NULL)) + && (energy_manager.GetLevel(EnergyType::FTL) >= jump.energy || (ai_jump_cheat && cp == NULL)) )) || smalle->forcejump) { jump_drive.Use(); DeactivateJumpDrive(); diff --git a/engine/src/components/energy_manager.cpp b/engine/src/components/energy_manager.cpp index a667553ac8..33690b602d 100644 --- a/engine/src/components/energy_manager.cpp +++ b/engine/src/components/energy_manager.cpp @@ -73,7 +73,7 @@ double getCloakConsumption(double factor, double mass) { EnergyManager::EnergyManager() { fuel = EnergyContainer(EnergyType::Fuel, 0.0); energy = EnergyContainer(EnergyType::Energy, 0.0); - spec_energy = EnergyContainer(EnergyType::SPEC, 0.0); + spec_energy = EnergyContainer(EnergyType::FTL, 0.0); reactor = Reactor(EnergyType::Fuel, 0.0, 0.0, 0.0, &energy, &spec_energy, 0.1); } @@ -115,7 +115,7 @@ EnergyContainer* EnergyManager::GetContainer(const EnergyType type) { return &fuel; case EnergyType::Energy: return &energy; - case EnergyType::SPEC: + case EnergyType::FTL: return &spec_energy; default: return nullptr; @@ -128,7 +128,7 @@ const EnergyContainer* EnergyManager::GetConstContainer(const EnergyType type) c return &fuel; case EnergyType::Energy: return &energy; - case EnergyType::SPEC: + case EnergyType::FTL: return &spec_energy; } } diff --git a/engine/src/components/energy_types.cpp b/engine/src/components/energy_types.cpp index 446c56c3f7..3258872f32 100644 --- a/engine/src/components/energy_types.cpp +++ b/engine/src/components/energy_types.cpp @@ -31,7 +31,7 @@ EnergyType SaveToType(const int type) { switch(type) { case 0: return EnergyType::Energy; case 1: return EnergyType::Fuel; - case 2: return EnergyType::SPEC; + case 2: return EnergyType::FTL; default: return EnergyType::None; } } @@ -40,7 +40,7 @@ std::string TypeToSave(EnergyType type) { switch(type) { case EnergyType::Energy: return "0"; case EnergyType::Fuel: return "1"; - case EnergyType::SPEC: return "2"; + case EnergyType::FTL: return "2"; default: return "3"; } } \ No newline at end of file diff --git a/engine/src/components/energy_types.h b/engine/src/components/energy_types.h index d5fcb29449..68b53dc2ad 100644 --- a/engine/src/components/energy_types.h +++ b/engine/src/components/energy_types.h @@ -30,10 +30,14 @@ #include +/* Discussion of FTL - yes, it isn't really faster. However, +* it would be easier for a new developer or someone from WC +* to figure what it means. +*/ enum class EnergyType { Fuel, // 1 Energy, // 0 Capacitor - SPEC, // 2 FTL + FTL, // 2 FTL None // 3 Free Energy }; diff --git a/engine/src/components/jump_drive.cpp b/engine/src/components/jump_drive.cpp index cecc6b477c..490e0e4d25 100644 --- a/engine/src/components/jump_drive.cpp +++ b/engine/src/components/jump_drive.cpp @@ -32,7 +32,7 @@ JumpDrive::JumpDrive(): delay(0.0){} JumpDrive::JumpDrive(double consumption, double delay) : - EnergyConsumer(EnergyType::SPEC, + EnergyConsumer(EnergyType::FTL, EnergyConsumerClassification::JumpDrive, EnergyConsumerType::Variable, consumption), delay(delay) {} diff --git a/engine/src/components/tests/balancing_tests.cpp b/engine/src/components/tests/balancing_tests.cpp index 258d5c4e0c..6afd2b0b8e 100644 --- a/engine/src/components/tests/balancing_tests.cpp +++ b/engine/src/components/tests/balancing_tests.cpp @@ -60,7 +60,7 @@ EnergyManager setup(ContainersSetup containers_setup, ReactorSetup reactor_setup manager.SetCapacity(EnergyType::Fuel, containers_setup.fuel_capacity); manager.SetCapacity(EnergyType::Energy, containers_setup.energy_capacity); - manager.SetCapacity(EnergyType::SPEC, containers_setup.spec_capacity); + manager.SetCapacity(EnergyType::FTL, containers_setup.spec_capacity); manager.SetReactor(reactor_setup.capacity, reactor_setup.usage_factor, reactor_setup.level, simulation_atom_var); @@ -85,7 +85,7 @@ double fuelBurn(ContainersSetup containers_setup, ReactorSetup reactor_setup, std::cout << i << " R: " << manager.GetReactorCapacity() << " F: " << manager.GetLevel(EnergyType::Fuel) << " E: " << manager.GetLevel(EnergyType::Energy) << - " S: " << manager.GetLevel(EnergyType::SPEC) << std::endl; + " S: " << manager.GetLevel(EnergyType::FTL) << std::endl; } if(manager.GetLevel(EnergyType::Fuel) < 0.001) { diff --git a/engine/src/gfx/cockpit.cpp b/engine/src/gfx/cockpit.cpp index 3658373440..80907a4a5c 100644 --- a/engine/src/gfx/cockpit.cpp +++ b/engine/src/gfx/cockpit.cpp @@ -432,7 +432,7 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { const bool warpifnojump = configuration()->graphics_config.hud.display_warp_energy_if_no_jump_drive; return (warpifnojump || target->jump.drive != -2) ? - target->energy_manager.Percent(EnergyType::SPEC) : 0; + target->energy_manager.Percent(EnergyType::FTL) : 0; } case UnitImages::HULL: if (maxhull < target->GetHull()) { @@ -730,7 +730,7 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { case UnitImages::CANJUMP_MODAL: if (-2 == target->jump.drive) { return (float) UnitImages::NODRIVE; - } else if (target->energy_manager.GetLevel(EnergyType::SPEC) < target->jump.energy) { + } else if (target->energy_manager.GetLevel(EnergyType::FTL) < target->jump.energy) { return (float) UnitImages::NOTENOUGHENERGY; } else if (target->graphicOptions.InWarp) { //FIXME return (float) UnitImages::OFF; diff --git a/engine/src/resource/resource.cpp b/engine/src/resource/resource.cpp index 0df1fa17fd..4d6d92a815 100644 --- a/engine/src/resource/resource.cpp +++ b/engine/src/resource/resource.cpp @@ -33,32 +33,16 @@ Resource::Resource(const T &value, const T &min_value, const T &max_value): value_(value), min_value_(min_value), max_value_(max_value), - adjusted_max_value_(value), + adjusted_max_value_(max_value), no_max_(max_value==-1) {} /* * Methods */ -template -void Resource::Downgrade(const T &value) { - if(no_max_) { // Can't downgrade if there's no max - return; - } - - adjusted_max_value_ = std::max(min_value_, adjusted_max_value_ - value); -} - -template -void Resource::DowngradeByPercent(const T &value) { - if(no_max_) { // Can't downgrade if there's no max - return; - } - adjusted_max_value_ = std::max(min_value_, adjusted_max_value_ - (max_value_ * value)); -} template -T Resource::Percent() const { +double Resource::Percent() const { if(no_max_) { // Can't calculate percent if there's no max return -1; } @@ -106,23 +90,7 @@ void Resource::SetMaxValue(const T &value) { adjusted_max_value_ = max_value_ = value; } -template -void Resource::Upgrade(const T &value) { - if(no_max_) { // Can't upgrade max if there's no max - return; - } - adjusted_max_value_ = std::min(max_value_, adjusted_max_value_ + value); -} - -template -void Resource::UpgradeByPercent(const T &value) { - if(no_max_) { // Can't upgrade max if there's no max - return; - } - - adjusted_max_value_ = std::min(max_value_, adjusted_max_value_ + (max_value_ * value)); -} template T Resource::Value() const { @@ -149,6 +117,80 @@ void Resource::Zero() { value_ = adjusted_max_value_ = min_value_; } +// Damage & Repair +template +void Resource::Destroy() { + value_ = adjusted_max_value_ = min_value_; +} + +template +bool Resource::Destroyed() { + return adjusted_max_value_ == min_value_; +} + +template +void Resource::RandomDamage() { + const double severity = std::rand(); + + if(severity > .95) { + // Destroy system + adjusted_max_value_ = min_value_; + } else { + // Damage system + DamageByPercent(severity); + } +} + +template +void Resource::DamageByValue(const T &value) { + if(no_max_) { // Can't downgrade if there's no max + return; + } + + adjusted_max_value_ = std::max(min_value_, adjusted_max_value_ - value); + value_ = std::min(value_, adjusted_max_value_); +} + +template +void Resource::DamageByPercent(const T &value) { + if(no_max_) { // Can't downgrade if there's no max + return; + } + + adjusted_max_value_ = std::max(min_value_, adjusted_max_value_ - (max_value_ * value)); +} + +template +bool Resource::Damaged() const { + return adjusted_max_value_ < max_value_; +} + +// TODO: partial repair +template +void Resource::RepairFully() { + value_ = adjusted_max_value_ = max_value_; +} + +template +void Resource::RepairByValue(const T &value) { + if(no_max_) { // Can't upgrade max if there's no max + return; + } + + adjusted_max_value_ = std::min(max_value_, adjusted_max_value_ + value); + value_ = adjusted_max_value_; +} + +template +void Resource::RepairByPercent(const T &value) { + if(no_max_) { // Can't upgrade max if there's no max + return; + } + + adjusted_max_value_ = std::min(max_value_, adjusted_max_value_ + (max_value_ * value)); + value_ = adjusted_max_value_; +} + /* * Overloaded operators */ @@ -167,7 +209,7 @@ Resource Resource::operator=(const T &value) { template Resource Resource::operator+=(const T &value) { if(!no_max_) { // Only applicable if there's max - value_ = std::min(value_ + value, max_value_); + value_ = std::min(value_ + value, adjusted_max_value_); } else { value_ += value; } @@ -185,7 +227,7 @@ Resource Resource::operator-=(const T &value) { template Resource Resource::operator+=(T &value) { if(!no_max_) { // Only applicable if there's max - value_ = std::min(value_ + value, max_value_); + value_ = std::min(value_ + value, adjusted_max_value_); } else { value_ += value; } diff --git a/engine/src/resource/resource.h b/engine/src/resource/resource.h index 947b97f685..45da66bb57 100644 --- a/engine/src/resource/resource.h +++ b/engine/src/resource/resource.h @@ -31,6 +31,7 @@ template class Resource { +protected: T value_; T min_value_; T max_value_; @@ -83,22 +84,31 @@ class Resource { operator T() { return value_; } - void Downgrade(const T &value); - void DowngradeByPercent(const T &value); + - T Percent() const; + double Percent() const; void ResetMaxValue(); void Set(const T &value); void SetToMax(); void SetMaxValue(const T &value); - void Upgrade(const T &value); - void UpgradeByPercent(const T &value); + void Zero(); T Value() const; T MaxValue() const; T MinValue() const; T AdjustedValue() const; + + // Damage & Repair + void Destroy(); + bool Destroyed(); + void RandomDamage(); + void DamageByValue(const T &value); + void DamageByPercent(const T &value); + bool Damaged() const; + void RepairFully(); + void RepairByValue(const T &value); + void RepairByPercent(const T &value); }; template diff --git a/engine/src/resource/tests/resource_test.cpp b/engine/src/resource/tests/resource_test.cpp index 2d56ddcca6..ee6136c840 100644 --- a/engine/src/resource/tests/resource_test.cpp +++ b/engine/src/resource/tests/resource_test.cpp @@ -2,22 +2,203 @@ #include "resource.h" + +// It's surprising, but gtest doesn't provide a good way to test equal. +bool fairlyEqual(double a, double b) { + return a-b < 0.001 && b-a < 0.001; +} + + TEST(Resource, Sanity) { + Resource resource = Resource(10.0f, 0.0f, 10.0f); + EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 10.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 1.0)); + + resource.Set(5.0f); + EXPECT_EQ(resource.Value(), 5.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 10.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 0.5)); + + resource += 5.0f; + EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 10.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 1.0)); + + resource -= 2.0f; + EXPECT_EQ(resource.Value(), 8.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 10.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 0.8)); + + resource -= 12.0f; + EXPECT_EQ(resource.Value(), 0.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 10.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 0.0)); +} + +TEST(Resource, Limitless) { Resource resource = Resource(10.0f, 0.0f); EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); resource.Set(5.0f); EXPECT_EQ(resource.Value(), 5.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); resource += 5.0f; EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); resource -= 2.0f; EXPECT_EQ(resource.Value(), 8.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); resource -= 12.0f; EXPECT_EQ(resource.Value(), 0.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); + + resource += 35.0f; + EXPECT_EQ(resource.Value(), 35.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); +} + +TEST(Resource, Damage) { + Resource resource = Resource(10.0f, 0.0f, 10.0); + EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 10.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 1.0)); + + resource.DamageByValue(3.0f); + EXPECT_EQ(resource.Value(), 7.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 7.0f); + EXPECT_TRUE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 0.7)); + + resource += 12.0f; + EXPECT_EQ(resource.Value(), 7.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 7.0f); + EXPECT_TRUE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 0.7)); + + resource -= 2.0f; + EXPECT_EQ(resource.Value(), 5.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 7.0f); + EXPECT_TRUE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 0.5)); + + + resource.RepairByValue(7.0f); + EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 10.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 1.0)); + + resource.DamageByValue(3.0f); + EXPECT_EQ(resource.Value(), 7.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 7.0f); + EXPECT_TRUE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 0.7)); + + resource.RepairFully(); + EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), 10.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), 10.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), 1.0)); +} + +TEST(Resource, Limitless_Damage) { + Resource resource = Resource(10.0f, 0.0f); + EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); + + resource.DamageByValue(3.0f); + EXPECT_EQ(resource.Value(), 10.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); + + resource += 12.0f; + EXPECT_EQ(resource.Value(), 22.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); + + resource -= 2.0f; + EXPECT_EQ(resource.Value(), 20.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); + - Resource dbl_resource = Resource(0.0,0.0,0.0); - EXPECT_EQ(dbl_resource.Value(), 0.0); + resource.RepairByValue(7.0f); + EXPECT_EQ(resource.Value(), 20.0f); + EXPECT_EQ(resource.MaxValue(), -1.0f); + EXPECT_EQ(resource.MinValue(), 0.0f); + EXPECT_EQ(resource.AdjustedValue(), -1.0f); + EXPECT_FALSE(resource.Damaged()); + EXPECT_TRUE(fairlyEqual(resource.Percent(), -1.0)); } From fec840edad65337daf2c6160c24b8db4a2837a88 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Fri, 22 Mar 2024 09:27:14 +0200 Subject: [PATCH 09/16] Introduce armor hull and shield components This is an interim commit. Implement shield component fully: - Add code in base computer to allow sale of component. Also modify shield info in that file. - Some changes to shields in units.json to support this. Add Type and Facets. Issues: - Shields don't work right as power isn't fully implemented. - Color of shield component is listed as damaged. Additionally: Switch Health from float to Resource Add pointers to Resource to support DamageableUnit pointers (e.g. hull). Fix bug in resource, where an update won't change current value. Fix subtle bug in basecomputer, where all armor stats reflect 2nd facet only. --- engine/CMakeLists.txt | 4 + engine/src/cmd/basecomputer.cpp | 29 ++- engine/src/cmd/damageable.cpp | 48 ++-- engine/src/cmd/damageable.h | 21 +- engine/src/cmd/unit_csv.cpp | 103 +++----- engine/src/cmd/unit_csv_factory.h | 2 + engine/src/cmd/unit_generic.cpp | 27 +- engine/src/cmd/upgradeable_unit.cpp | 53 ++++ engine/src/cmd/upgradeable_unit.h | 35 +++ engine/src/components/armor.cpp | 150 +++++++++++ engine/src/components/armor.h | 72 ++++++ engine/src/components/component.cpp | 49 +++- engine/src/components/component.h | 60 +++-- engine/src/components/drive.cpp | 1 + engine/src/components/drive.h | 3 +- engine/src/components/hull.cpp | 90 +++++++ engine/src/components/hull.h | 67 +++++ engine/src/components/jump_drive.cpp | 6 +- engine/src/components/jump_drive.h | 3 +- engine/src/components/radar.cpp | 4 +- engine/src/components/shield.cpp | 302 +++++++++++++++++++++++ engine/src/components/shield.h | 88 +++++++ engine/src/damage/damage.h | 24 +- engine/src/damage/damageable_layer.cpp | 96 +++---- engine/src/damage/damageable_layer.h | 14 +- engine/src/damage/damageable_object.cpp | 2 +- engine/src/damage/health.cpp | 85 +++---- engine/src/damage/health.h | 52 ++-- engine/src/damage/tests/health_tests.cpp | 4 +- engine/src/damage/tests/layer_tests.cpp | 34 +++ engine/src/damage/tests/object_tests.cpp | 4 +- engine/src/gfx/cockpit.cpp | 2 +- engine/src/resource/cout_util.h | 36 +++ engine/src/resource/resource.cpp | 2 +- engine/src/resource/resource.h | 4 + engine/src/star_system.cpp | 3 +- 36 files changed, 1275 insertions(+), 304 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 create mode 100644 engine/src/resource/cout_util.h diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 612772461c..56c20176ea 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -663,6 +663,10 @@ SET(LIBCOMPONENT src/components/component.cpp src/components/energy_types.cpp + src/components/armor.cpp + src/components/hull.cpp + src/components/shield.cpp + src/components/afterburner.cpp src/components/cloak.cpp src/components/drive.cpp diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index 8703be2847..6da05478ca 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -1877,6 +1877,12 @@ bool BaseComputer::configureUpgradeCommitControls(const Cargo &item, Transaction bool CanDoSell = true; Unit *player = m_player.GetUnit(); unsigned int numc = player->numCargo(); + + static bool must_fix_first = + XMLSupport::parse_bool(vs_config->getVariable("physics", + "must_repair_to_sell", + "true")); + if (!isWeapon(item.GetCategory())) { //weapons can always be sold for (unsigned int i = 0; i < numc; ++i) { @@ -1884,16 +1890,17 @@ bool BaseComputer::configureUpgradeCommitControls(const Cargo &item, Transaction if (c->GetCategory().find("upgrades/") == 0 && !isWeapon(c->GetCategory())) { float po = UnitUtil::PercentOperational(player, c->GetName(), c->GetCategory(), false); if (po > .02 && po < .98) { - static bool must_fix_first = - XMLSupport::parse_bool(vs_config->getVariable("physics", - "must_repair_to_sell", - "true")); - CanDoSell = (emergency_downgrade_mode.length() != 0 || must_fix_first == false); } } } } + + // New component code + if(GetUpgradeType(item.GetName() + UPGRADES_SUFFIX) == UpgradeType::Shield) { + CanDoSell = (!player->shield_component.Damaged()) || !must_fix_first; + } + if (CanDoSell) { commitButton->setHidden(false); commitButton->setLabel("Sell"); @@ -5461,8 +5468,8 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C PRETTY_ADDU( substatcolor + armor_color_strings[i], (mode && replacement_mode - == 2) ? 100.0 * (playerUnit->armor->facets[armor_indices[i]].health - 1) : - playerUnit->armor->facets[2].health * VSDM, + == 2) ? 100.0 * (playerUnit->armor->facets[armor_indices[i]].health.Value() - 1) : + playerUnit->armor->facets[i].health.Value() * VSDM, 0, (2 == replacement_mode) ? "%" : "MJ"); } @@ -5547,17 +5554,17 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C shield->GetMaxHealth())) { for (int i = 0; i < num_shields; i++) { PRETTY_ADDU(substatcolor + shield_strings[i], (mode && replacement_mode == 2) ? - (100.0 * (playerUnit->shield->facets[i].max_health - 1)) : - playerUnit->shield->facets[i].max_health * VSDM, 0, + (100.0 * (playerUnit->shield->facets[i].health.MaxValue() - 1)) : + playerUnit->shield->facets[i].health.MaxValue() * VSDM, 0, (2 == replacement_mode) ? "%" : "MJ"); } } } - const float regeneration = playerUnit->shield->GetRegeneration(); + const double regeneration = playerUnit->shield->GetRegeneration(); if (!mode) { PRETTY_ADDU(statcolor + "Shield protection recharge speed: #-c", regeneration * VSDM, 0, "MJ/s"); - } else if (replacement_mode != 0 || playerUnit->shield->GetRegeneration() != blankUnit->shield->GetRegeneration()) { + } else if (replacement_mode != 0 || regeneration) { switch (replacement_mode) { case 0: //Replacement or new Module PRETTY_ADDU(statcolor + "Shield protection recharge speed set to: #-c", regeneration * VSDM, 0, "MJ/s"); diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index d9622f4455..5c1ac798dd 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -43,6 +43,24 @@ #include #include "configuration/configuration.h" +Damageable::Damageable() : DamageableObject(), + hull(&(layers[0])), + armor(&(layers[1])), + shield(&(layers[2])), + current_hull(hull->facets[0].health.ValuePtr()), + max_hull(hull->facets[0].health.MaxValuePtr()), + upgrade_hull(0), + shield_regeneration(0), + killed(false), + shield_component(shield) { + if(shield) { + shield_component.shield_ = shield; + } else { + assert(0); + } + +} + bool Damageable::ShieldUp(const Vector &pnt) const { const int shield_min = 5; @@ -469,19 +487,14 @@ 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); + return shield->facets[0].Percent(); case 4: - return ShieldData(this, 2); + return shield->facets[2].Percent(); default: return 0.0f; // We only support 2 and 4 facet shields ATM } @@ -490,9 +503,9 @@ float Damageable::FShieldData() const { float Damageable::BShieldData() const { switch (shield->number_of_facets) { case 2: - return ShieldData(this, 1); + return shield->facets[1].Percent(); case 4: - return ShieldData(this, 3); + return shield->facets[3].Percent(); default: return 0.0f; // We only support 2 and 4 facet shields ATM } @@ -503,7 +516,7 @@ float Damageable::LShieldData() const { case 2: return 0.0f; case 4: - return ShieldData(this, 0); + return shield->facets[0].Percent(); default: return 0.0f; // We only support 2 and 4 facet shields ATM } @@ -514,7 +527,7 @@ float Damageable::RShieldData() const { case 2: return 0.0f; case 4: - return ShieldData(this, 1); + return shield->facets[1].Percent(); default: return 0.0f; // We only support 2 and 4 facet shields ATM } @@ -525,7 +538,7 @@ void Damageable::ArmorData(float armor[8]) const { Damageable *damageable = const_cast(this); DamageableLayer armor_layer = damageable->GetArmorLayer(); for (int i = 0; i < 8; i++) { - armor[i] = armor_layer.facets[i].health; + armor[i] = armor_layer.facets[i].health.Value(); } } @@ -576,11 +589,14 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi // Discharge shields due to energy or SPEC or cloak if ((in_warp && !shields_in_spec) || unit->cloak.Active()) { - shield->Discharge(discharge_rate, min_shield_discharge); + shield->SetPower(0.0); } else { - // Shield regeneration - shield->Regenerate(shield_recharge); + // Figure out how to support partial power + shield->SetPower(1.0); } + + // Shield regeneration + shield->Regenerate(shield_recharge); } float Damageable::MaxShieldVal() const { diff --git a/engine/src/cmd/damageable.h b/engine/src/cmd/damageable.h index a350fbaf43..396cd14b21 100644 --- a/engine/src/cmd/damageable.h +++ b/engine/src/cmd/damageable.h @@ -29,6 +29,9 @@ #include "gfx/vec.h" #include "mount_size.h" +#include "components/shield.h" +#include + class Unit; struct GFXColor; @@ -36,13 +39,17 @@ struct GFXColor; * @brief The Damageable class TODO */ class Damageable : public DamageableObject { + friend class UpgradeableUnit; + public: + Shield shield_component; + DamageableLayer *hull; DamageableLayer *armor; DamageableLayer *shield; - float *current_hull; - float *max_hull; + double *current_hull; + double *max_hull; // These are only used for upgrade due to macros // TODO: refactor @@ -55,15 +62,7 @@ class Damageable : public DamageableObject { // Methods public: - Damageable() : hull(&layers[0]), - armor(&layers[1]), - shield(&layers[2]), - current_hull(&hull->facets[as_integer(FacetName::single)].health), - max_hull(&hull->facets[as_integer(FacetName::single)].max_health), - upgrade_hull(0), - shield_regeneration(0), - killed(false) { - } + Damageable(); protected: virtual ~Damageable() = default; diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index 1c600fbb63..86cee8e23c 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -743,11 +743,11 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ UnitCSVFactory::GetVariable(unit_key, "Primary_Capacitor", 0.0f)); energy_manager.SetCapacity(EnergyType::FTL, UnitCSVFactory::GetVariable(unit_key, "Warp_Capacitor", 0.0f)); - + // Hull float temp_hull = UnitCSVFactory::GetVariable(unit_key, "Hull", 0.0f); - float hull_values[1] = {temp_hull}; - hull->UpdateFacets(1, hull_values); + std::vector hull_values = {temp_hull}; + hull->UpdateFacets(hull_values); specInterdiction = UnitCSVFactory::GetVariable(unit_key, "Spec_Interdiction", 0.0f); @@ -756,67 +756,21 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ "Armor_Front_Bottom_Left", "Armor_Front_Bottom_Right", "Armor_Back_Top_Left", "Armor_Back_Top_Right", "Armor_Back_Bottom_Left", "Armor_Back_Bottom_Right"}; - float armor_values[8]; + std::vector armor_values; for (int i = 0; i < 8; i++) { - float tmp_armor_value = UnitCSVFactory::GetVariable(unit_key, armor_keys[i], 0.0f); - armor_values[i] = tmp_armor_value; + double tmp_armor_value = UnitCSVFactory::GetVariable(unit_key, armor_keys[i], 0.0); + armor_values.push_back(tmp_armor_value); + + if(saved_game) { + std::cout << unit_key << " : " << armor_keys[i] << " = " << tmp_armor_value << std::endl; + } } - armor->UpdateFacets(8, armor_values); + armor->UpdateFacets(armor_values); // 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); - - 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 - - int shield_count = 0; - float shield_values[4]; - std::string shield_string_values[4]; - - // TODO: this mapping should really go away - // I love macros, NOT. - shield_string_values[0] = UnitCSVFactory::GetVariable(unit_key, "Shield_Front_Top_Right", std::string()); - shield_string_values[1] = UnitCSVFactory::GetVariable(unit_key, "Shield_Back_Top_Left", std::string()); - shield_string_values[2] = UnitCSVFactory::GetVariable(unit_key, "Shield_Front_Bottom_Right", std::string()); - shield_string_values[3] = UnitCSVFactory::GetVariable(unit_key, "Shield_Front_Bottom_Left", std::string()); - - for (int i = 0; i < 4; i++) { - shield_values[i] = 0.0f; - - if (shield_string_values[i].empty()) { - continue; - } - - shield_values[i] = ::stof(shield_string_values[i]); - // Should add up to the shield type - quad or dual - shield_count++; - } - - /* - We are making the following assumptions: - 1. The CSV is correct - 2. Dual shields are 0 front and 1 rear - 3. Quad shields are front (0), rear(1), right(2) and left(3) - 4. There is no support for 8 facet shields in the game. - This has more to do with the cockpit code than anything else - 5. We map the above index to our own - */ - - if (shield_count == 4 || shield_count == 2) { - shield->number_of_facets = shield_count; - shield->UpdateFacets(shield_count, shield_values); - } + shield_component.Load("", unit_key, this); // End shield section @@ -1252,7 +1206,8 @@ string Unit::WriteUnitString() { unit["Armor_Back_Bottom_Right"] = tos(GetArmorLayer().facets[7].health); int number_of_shield_emitters = shield->number_of_facets; - { + shield_component.SaveToCSV(unit); + /*{ unit["Shield_Front_Top_Right"] = ""; unit["Shield_Front_Top_Left"] = ""; unit["Shield_Back_Top_Right"] = ""; @@ -1264,26 +1219,26 @@ string Unit::WriteUnitString() { switch (number_of_shield_emitters) { case 8: - unit["Shield_Front_Top_Left"] = tos(GetShieldLayer().facets[0].max_health); - unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[1].max_health); - unit["Shield_Front_Bottom_Left"] = tos(GetShieldLayer().facets[2].max_health); - unit["Shield_Front_Bottom_Right"] = tos(GetShieldLayer().facets[3].max_health); - unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[4].max_health); - unit["Shield_Back_Top_Right"] = tos(GetShieldLayer().facets[5].max_health); - unit["Shield_Back_Bottom_Left"] = tos(GetShieldLayer().facets[6].max_health); - unit["Shield_Back_Bottom_Right"] = tos(GetShieldLayer().facets[7].max_health); + unit["Shield_Front_Top_Left"] = tos(GetShieldLayer().facets[0].health.MaxValue()); + unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[1].health.MaxValue()); + unit["Shield_Front_Bottom_Left"] = tos(GetShieldLayer().facets[2].health.MaxValue()); + unit["Shield_Front_Bottom_Right"] = tos(GetShieldLayer().facets[3].health.MaxValue()); + unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[4].health.MaxValue()); + unit["Shield_Back_Top_Right"] = tos(GetShieldLayer().facets[5].health.MaxValue()); + unit["Shield_Back_Bottom_Left"] = tos(GetShieldLayer().facets[6].health.MaxValue()); + unit["Shield_Back_Bottom_Right"] = tos(GetShieldLayer().facets[7].health.MaxValue()); break; case 4: - unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[0].max_health); - unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[1].max_health); - unit["Shield_Front_Bottom_Right"] = tos(GetShieldLayer().facets[2].max_health); - unit["Shield_Front_Bottom_Left"] = tos(GetShieldLayer().facets[3].max_health); + unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[0].health.MaxValue()); + unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[1].health.MaxValue()); + unit["Shield_Front_Bottom_Right"] = tos(GetShieldLayer().facets[2].health.MaxValue()); + unit["Shield_Front_Bottom_Left"] = tos(GetShieldLayer().facets[3].health.MaxValue()); break; case 2: - unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[0].max_health); - unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[1].max_health); + unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[0].health.MaxValue()); + unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[1].health.MaxValue()); break; case 0: @@ -1295,7 +1250,7 @@ string Unit::WriteUnitString() { std::cout << number_of_shield_emitters << "\n"; assert(0); } - } + }*/ //TODO: lib_damage shield leak and efficiency diff --git a/engine/src/cmd/unit_csv_factory.h b/engine/src/cmd/unit_csv_factory.h index 3d6b87c8bf..7f05bc334d 100644 --- a/engine/src/cmd/unit_csv_factory.h +++ b/engine/src/cmd/unit_csv_factory.h @@ -59,6 +59,8 @@ const std::string keys[] = {"Key", "Directory", "Name", "STATUS", "Object_Type", "Explosion", "Num_Animation_Stages", "Upgrade_Storage_Volume", "Heat_Sink_Rating", "Shield_Efficiency", "Num_Chunks", "Chunk_0", "Collide_Subunits", "Spec_Interdiction", "Tractorability", + // For component upgrade + "Upgrade_Type", "Facets", // These values are not in units.csv! There are probably more but I stopped mapping. // TODO: map all missing values using the commented out code below! "FaceCamera", "Unit_Role", "Attack_Preference", "Hidden_Hold_Volume", "Equipment_Space"}; diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 6526dfc478..97d67462ef 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -1277,7 +1277,7 @@ void Unit::DamageRandSys(float dam, const Vector &vec, float randnum, float degr } // TODO: lib_damage reenable - shield->ReduceLayerCapability(dam, 0.1); + shield_component.Damage(); damages |= Damages::SHIELD_DAMAGED; return; @@ -2831,6 +2831,15 @@ bool Unit::UpAndDownGrade(const Unit *up, const Unit *downgradelimit, bool force_change_on_nothing, bool gen_downgrade_list) { + // New Code + UpgradeOperationResult result = UpgradeUnit(up->name, !downgrade, touchme); + if(result.upgradeable) { + std::cout << "Upgraded successfully\n"; + percentage = result.percent; + return result.success; + } + + // Old Code percentage = 0; static bool @@ -2988,7 +2997,7 @@ bool Unit::UpAndDownGrade(const Unit *up, } - if (!csv_cell_null_check || force_change_on_nothing + /*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 < 8; i++) { STDUPGRADE(armor->facets[i].health, @@ -2996,7 +3005,7 @@ bool Unit::UpAndDownGrade(const Unit *up, 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 @@ -3004,7 +3013,7 @@ bool Unit::UpAndDownGrade(const Unit *up, // Because of the complex macros taking partial expressions and building code from them, // this is the easiest way to refactor - float previous = shield->GetRegeneration(); + /*float previous = shield->GetRegeneration(); if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Shield_Recharge")) @@ -3015,7 +3024,7 @@ bool Unit::UpAndDownGrade(const Unit *up, if (upgradedrecharge) { shield->UpdateRegeneration(shield_regeneration); - } + }*/ // Upgrade hull health upgrade_hull = *current_hull; @@ -3032,9 +3041,9 @@ bool Unit::UpAndDownGrade(const Unit *up, STDUPGRADE(upgrade_hull, up->upgrade_hull, templ->upgrade_hull, 0); } - if ((hull->facets[0].max_health < hull->facets[0].health) && (!Destroyed())) { + /*if ((hull->facets[0].max_health < hull->facets[0].health) && (!Destroyed())) { hull->facets[0].max_health = hull->facets[0].health; - } + }*/ //if (!csv_cell_null_check || force_change_on_nothing // || cell_has_recursive_data(upgrade_name, up->faction, "Reactor_Recharge")) @@ -3165,7 +3174,7 @@ bool Unit::UpAndDownGrade(const Unit *up, bool upgradedshield = false; - if (!csv_cell_null_check || force_change_on_nothing + /*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++) { @@ -3187,7 +3196,7 @@ bool Unit::UpAndDownGrade(const Unit *up, } 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) { diff --git a/engine/src/cmd/upgradeable_unit.cpp b/engine/src/cmd/upgradeable_unit.cpp index 891b485cab..76c44b58ed 100644 --- a/engine/src/cmd/upgradeable_unit.cpp +++ b/engine/src/cmd/upgradeable_unit.cpp @@ -38,6 +38,7 @@ #include "unit_generic.h" #include "weapon_info.h" #include "vega_cast_utils.h" +#include "unit_csv_factory.h" std::vector ParseUnitUpgrades(const std::string &upgrades) { if(upgrades.size() == 0) { @@ -85,6 +86,58 @@ UpgradeableUnit::UpgradeableUnit() extern int GetModeFromName(const char *input_buffer); + +UpgradeType GetUpgradeType(const std::string upgrade_key) { + std::string upgrade_type_string = UnitCSVFactory::GetVariable(upgrade_key, "Upgrade_Type", std::string()); + std::string upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); + + if(upgrade_type_string.size() > 0) { + std::cout << "Upgrade key: " << upgrade_key << " " << upgrade_type_string << std::endl; + } + + + if(upgrade_type_string.empty()) return UpgradeType::None; + + if(upgrade_type_string == "Armor") { + + return UpgradeType::Armor; + } + //if(upgrade_type_string == "Hull") return UpgradeType::Hull; + if(upgrade_type_string == "Shield") return UpgradeType::Shield; + + return UpgradeType::None; +} + + +UpgradeOperationResult UpgradeableUnit::UpgradeUnit(const std::string upgrade_name, + bool upgrade, bool apply) { + Unit* unit = vega_dynamic_cast_ptr(this); + const std::string upgrade_key = upgrade_name + UPGRADES_SUFFIX; + const UpgradeType upgrade_type = GetUpgradeType(upgrade_key); + + UpgradeOperationResult result; + + + + switch(upgrade_type) { + // case UpgradeType::Armor: + // result.upgradeable = true; + // result.success = unit->armor.CanWillUpDowngrade(upgrade_key, upgrade, apply); + // break; + case UpgradeType::Shield: + result.upgradeable = true; + result.success = unit->shield_component.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; + default: + //std::cout << "Unhandled type for " << upgrade_name << std::endl; + break; + } + + return result; +} + +extern int GetModeFromName(const char *input_buffer); + // TODO: remove unit parameter void UpgradeableUnit::UpgradeUnit(const std::string &upgrades) { const std::string delimiter = ";"; diff --git a/engine/src/cmd/upgradeable_unit.h b/engine/src/cmd/upgradeable_unit.h index 609e1db248..d3db85c189 100644 --- a/engine/src/cmd/upgradeable_unit.h +++ b/engine/src/cmd/upgradeable_unit.h @@ -32,6 +32,37 @@ class Unit; class Mount; +const std::string UPGRADES_SUFFIX = "__upgrades"; + +// A struct to hold all results of the upgrade operation +struct UpgradeOperationResult { + double percent = 0.0; // Old part percent operational + bool success = false; // Can we upgrade/downgrade + bool upgradeable = false; // Temp variable. Until we map all types. +}; + +enum class UpgradeType { + None, + + Armor, + //Hull, // Can't upgrade the hull right now + Shield, + + Capacitor, + FTL_Capacitor, + Jump_Drive, + + Cloak, + ECM, + Radar, + + Repair_Droid + + // TODO: all the rest of the upgrades, shady or not... +}; + +UpgradeType GetUpgradeType(const std::string upgrade_key); + // TODO: make this into a subclass of unit later class UpgradeableUnit @@ -39,6 +70,10 @@ class UpgradeableUnit public: UpgradeableUnit(); virtual ~UpgradeableUnit() {} + + UpgradeOperationResult UpgradeUnit(const std::string upgrade_name, + bool upgrade, bool apply); + void UpgradeUnit(const std::string &upgrades); bool UpgradeMounts(const Unit *up, int subunitoffset, diff --git a/engine/src/components/armor.cpp b/engine/src/components/armor.cpp new file mode 100644 index 0000000000..2a70932db3 --- /dev/null +++ b/engine/src/components/armor.cpp @@ -0,0 +1,150 @@ +/* + * armor.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "armor.h" + +#include "damageable_layer.h" +#include "unit_csv_factory.h" +#include "unit_generic.h" + +const std::string armor_facets[] = { + "Moment_Of_Inertia", + "Armor_Front_Top_Right", + "Armor_Front_Top_Left", + "Armor_Front_Bottom_Right", + "Armor_Front_Bottom_Left", + "Armor_Back_Top_Right", + "Armor_Back_Top_Left", + "Armor_Back_Bottom_Right", + "Armor_Back_Bottom_Left" +}; + +Armor::Armor(DamageableLayer* armor_layer_): Component("", 0.0, 0.0, false), + armor_layer_(armor_layer_) {} + +void Armor::Load(std::string upgrade_key, std::string unit_key, + Unit *unit) { + // Component + Component::Load(upgrade_key, unit_key, unit); + + + // Damageable Layer + std::string upgrade_type_string = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); + + for(int i = 0;i < 8;i++) { + double armor_facet_current = UnitCSVFactory::GetVariable(unit_key, armor_facets[i], 0.0); + double armor_facet_max = UnitCSVFactory::GetVariable(upgrade_key, armor_facets[i], 0.0); + armor_layer_->facets[i].Update(armor_facet_max); + } +} + +std::string Armor::SaveToJSON() const { + return std::string(); +} + +std::string Armor::Describe() const { + return std::string(); +} + +bool Armor::CanDowngrade() const { + if(integral) return false; + + // Nothing to downgrade + if(upgrade_name.empty()) return false; + + // Other considerations - damaged?! + + return true; +} + +bool Armor::Downgrade() { + if(!CanDowngrade()) { + return false; + } + + this->upgrade_name.clear(); + //mass = 0; + // volume = 0; + for(int i = 0;i < 8;i++) { + armor_layer_->facets[i].Update(0.0f); + } + + return true; +} + +bool Armor::CanUpgrade(const std::string upgrade_name) const { + + if(integral) { + return false; + } + + // Will allow swapping upgrades. + // TODO: make base_computer sell previous upgrade + + // Other considerations - damaged?! + + return true; +} + +bool Armor::Upgrade(const std::string upgrade_name) { + if(!CanUpgrade(upgrade_name)) { + return false; + } + + if(!UnitCSVFactory::HasUnit(upgrade_name)) { + return false; + } + + // TODO: read all 8 facets + for(int i = 0;i < 8;i++) { + double armor = UnitCSVFactory::GetVariable(upgrade_name, + "Armor_Front_Top_Right", 0.0); + armor_layer_->facets[i].Update(armor); + } + + return true; +} + +// Handled by LibDamage +// Consider exposing this as API for python +// Currently has DealDamageToHull which serves a similar purpose +void Armor::Damage() {} + +void Armor::Repair() { + for(int i = 0;i < 8;i++) { + //armor_layer_->facets[i].health = armor_layer_->facets[i].max_health; + } +} + +bool Armor::Damaged() const { + return false;//(armor_layer_->TotalLayerValue()/armor_layer_->TotalMaxLayerValue()) < 100.0; +} + + +bool Armor::Installed() const { + return true;//armor_layer_->facets[0].max_health > 0; +} \ 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..41abd0fc45 --- /dev/null +++ b/engine/src/components/armor.h @@ -0,0 +1,72 @@ +/* + * armor.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef VEGA_STRIKE_ENGINE_COMPONENTS_ARMOR_H +#define VEGA_STRIKE_ENGINE_COMPONENTS_ARMOR_H + +#include "component.h" +#include "damage/damageable_layer.h" + +#include + +class Unit; + +// TODO: make armor plating a multiplier. +// e.g. light fighter has armor_area[8] +// Without armor, the array is all zero. +// With tungsten, you multiply each value by 1. +// With better armor material, you multiply by more. + +class Armor : public Component { + DamageableLayer* armor_layer_; + + friend class Unit; +public: + Armor(DamageableLayer* armor_layer_); + + virtual void Load(std::string upgrade_key, std::string unit_key, + Unit *unit); // Load from dictionary + virtual std::string SaveToJSON() const; // Save component as JSON + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; +}; + +#endif // VEGA_STRIKE_ENGINE_COMPONENTS_ARMOR_H \ No newline at end of file diff --git a/engine/src/components/component.cpp b/engine/src/components/component.cpp index b1f8c8b390..9bd3d1765e 100644 --- a/engine/src/components/component.cpp +++ b/engine/src/components/component.cpp @@ -26,20 +26,47 @@ // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- #include "component.h" +#include "unit_csv_factory.h" -#include +Component::Component(std::string upgrade_name, double mass, double volume, + bool integral): + upgrade_name(upgrade_name), + mass(mass), volume(volume), + integral(integral) {} -Component::Component() -{ +void Component::Load(std::string upgrade_key, std::string unit_key, + Unit *unit) { + upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); + this->upgrade_key = upgrade_key; + + mass = UnitCSVFactory::GetVariable(upgrade_key, "Mass", 0.0); + // TODO: volume = UnitCSVFactory::GetVariable(upgrade_key, "Volume", 0.0); + // TODO: bool integral = false; } - -double random20() -{ - if(std::rand() < 0.2) { - return std::rand(); - } - - return 1.0; +// TODO: convert to std::pair +bool Component::CanWillUpDowngrade(const std::string upgrade_key, + bool upgrade, bool apply) { + if(upgrade) { + if(apply) { + return Upgrade(upgrade_key); + } else { + return CanUpgrade(upgrade_key); + } + } else { + if(apply) { + return Downgrade(); + } else { + return CanDowngrade(); + } + } } + +bool Component::Downgrade() { + upgrade_name = std::string(); + upgrade_key = std::string(); + + mass = 0.0; + volume = 0.0; +} \ No newline at end of file diff --git a/engine/src/components/component.h b/engine/src/components/component.h index 87ede79ca2..65f754b9b9 100644 --- a/engine/src/components/component.h +++ b/engine/src/components/component.h @@ -28,34 +28,60 @@ #ifndef COMPONENT_H #define COMPONENT_H +#include + +/** + * LibComponent is currently tightly coupled to LibDamage and + * other various libraries in VegaStrike engine. + * Consider decoupling and subclassing every component in it. + */ + +class Unit; + +// TODO: add complete list +enum class ComponentType { + Hull, + Armor, + Shield, + Drive +}; class Component { protected: - bool installed = true; - bool enabled = true; - bool damaged = false; + std::string upgrade_name; // Isometal Armor + std::string upgrade_key; // armor03__upgrades + + double mass = 0; + double volume = 0; + + bool integral = false; // Part of the ship. Can't be upgraded/downgraded public: - Component(); - virtual ~Component() {} + Component(std::string upgrade_name, double mass, + double volume, bool integral); - void Install() { installed = true; } - void Uninstall() { installed = false; } - bool Installed() { return installed; } + virtual void Load(std::string upgrade_key, std::string unit_key, + Unit *unit); // Load from dictionary + //virtual std::string SaveToJSON() const = 0; // Save component as JSON - void Enable() { enabled = true; } - void Disable() { enabled = false; } - bool Enabled() { return enabled; } + virtual std::string Describe() const = 0; // Describe component in base_computer + // Handle the four cases of CanUpgrade/Upgrade/CanDowngrade/Downgrade + bool CanWillUpDowngrade(const std::string upgrade_key, + bool upgrade, bool apply); - //virtual void damage(); - //virtual void fix(); + virtual bool CanDowngrade() const = 0; + virtual bool Downgrade() = 0; -}; + virtual bool CanUpgrade(const std::string upgrade_name) const = 0; -// These functions reduce functionality by a uniform distribution 0-1. -// The function name's number component is the chance of the damage occurring. -double random20(); + virtual bool Upgrade(const std::string upgrade_name) = 0; + virtual void Damage() = 0; + virtual void Repair() = 0; + + virtual bool Damaged() const = 0; + virtual bool Installed() const = 0; +}; #endif // COMPONENT_H diff --git a/engine/src/components/drive.cpp b/engine/src/components/drive.cpp index 1edb7a6f25..18cdab9cac 100644 --- a/engine/src/components/drive.cpp +++ b/engine/src/components/drive.cpp @@ -32,6 +32,7 @@ Drive::Drive(EnergyType type, double drive_level, double mass, double simulation_atom_var): + //Component("", 0.0, 0.0, false), EnergyConsumer(type, EnergyConsumerClassification::Afterburner, EnergyConsumerType::Constant, diff --git a/engine/src/components/drive.h b/engine/src/components/drive.h index c90afd55c2..adedcc0e36 100644 --- a/engine/src/components/drive.h +++ b/engine/src/components/drive.h @@ -29,7 +29,8 @@ #include "energy_container.h" -class Drive : public Component, public EnergyConsumer { +class Drive : //public Component, + public EnergyConsumer { double usage_factor; public: Drive(EnergyType type, diff --git a/engine/src/components/hull.cpp b/engine/src/components/hull.cpp new file mode 100644 index 0000000000..10df66af60 --- /dev/null +++ b/engine/src/components/hull.cpp @@ -0,0 +1,90 @@ +/* + * hull.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "hull.h" + +#include "unit_csv_factory.h" + +Hull::Hull(DamageableLayer *hull_): Component("", 0.0, 0.0, true), + hull_(hull_) {} + +void Hull::Load(std::string upgrade_key, std::string unit_key, + Unit *unit) { + // Component + upgrade_name = "Hull"; + upgrade_key = ""; + + mass = UnitCSVFactory::GetVariable(unit_key, "mass", 0.0); + volume = 0; + + // Damageable Layer + double hull_current = UnitCSVFactory::GetVariable(unit_key, "hull", 1.0); + double hull_max = UnitCSVFactory::GetVariable(upgrade_key, "hull", 1.0); + hull_->facets[0].health.SetMaxValue(hull_max); +} + +std::string Hull::SaveToJSON() const { + return std::string(); +} + +std::string Hull::Describe() const { + return std::string(); +} + +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; +} + +// Handled by LibDamage +// Consider exposing this as API for python +// Currently has DealDamageToHull which serves a similar purpose +void Hull::Damage() {} + +void Hull::Repair() { + //hull_->facets[0].adjusted_health = hull_->facets[0].max_health; +} + +bool Hull::Damaged() const { + return true;//hull_->facets[0].health < hull_->facets[0].max_health; +} + + +bool Hull::Installed() const { + return true; +} \ 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..1b9f19101e --- /dev/null +++ b/engine/src/components/hull.h @@ -0,0 +1,67 @@ +/* + * hull.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef VEGA_STRIKE_ENGINE_COMPONENTS_HULL_H +#define VEGA_STRIKE_ENGINE_COMPONENTS_HULL_H + +#include "component.h" +#include "damage/damageable_layer.h" + +/** + * This is a minimum implementation of hull class. + * A hull cannot be upgraded, only repaired. + * We keep these functions to make the hull a separate component. + * It cannot be sold (downgraded) but can be repaired. + */ +class Hull : public Component { + DamageableLayer* hull_; + friend class Unit; +public: + Hull(DamageableLayer *hull_); + + virtual void Load(std::string upgrade_key, std::string unit_key, + Unit *unit); // Load from dictionary + virtual std::string SaveToJSON() const; // Save component as JSON + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; +}; + +#endif // VEGA_STRIKE_ENGINE_COMPONENTS_HULL_H \ No newline at end of file diff --git a/engine/src/components/jump_drive.cpp b/engine/src/components/jump_drive.cpp index 490e0e4d25..e369aea6ee 100644 --- a/engine/src/components/jump_drive.cpp +++ b/engine/src/components/jump_drive.cpp @@ -24,7 +24,8 @@ #include "jump_drive.h" -JumpDrive::JumpDrive(): +JumpDrive::JumpDrive(): + //Component("", 0.0, 0.0, true), EnergyConsumer(EnergyType::None, EnergyConsumerClassification::JumpDrive, EnergyConsumerType::Variable, @@ -32,13 +33,14 @@ JumpDrive::JumpDrive(): delay(0.0){} JumpDrive::JumpDrive(double consumption, double delay) : + //Component("", 0.0, 0.0, true), EnergyConsumer(EnergyType::FTL, EnergyConsumerClassification::JumpDrive, EnergyConsumerType::Variable, consumption), delay(delay) {} bool JumpDrive::Ready() { - return installed && enabled; + return true;//installed && enabled; } void JumpDrive::SetDestination(int destination) { diff --git a/engine/src/components/jump_drive.h b/engine/src/components/jump_drive.h index f85e4c9baf..cff2ff4789 100644 --- a/engine/src/components/jump_drive.h +++ b/engine/src/components/jump_drive.h @@ -29,7 +29,8 @@ #include "energy_types.h" #include "energy_container.h" -class JumpDrive : public Component, public EnergyConsumer { +class JumpDrive : //public Component, + public EnergyConsumer { int destination; double delay; diff --git a/engine/src/components/radar.cpp b/engine/src/components/radar.cpp index b8941a18cd..f42c27ff55 100644 --- a/engine/src/components/radar.cpp +++ b/engine/src/components/radar.cpp @@ -141,11 +141,11 @@ void CRadar::Damage() capabilities = NONE; } - max_range = max_range * random20(); + /*max_range = max_range * random20(); max_cone = max_cone * random20(); lock_cone = lock_cone * random20(); tracking_cone = tracking_cone * random20(); - min_target_size = min_target_size * random20(); + min_target_size = min_target_size * random20();*/ // Original cone damage /*const float maxdam = configuration()->physics_config.max_radar_cone_damage; diff --git a/engine/src/components/shield.cpp b/engine/src/components/shield.cpp new file mode 100644 index 0000000000..7793b44c3b --- /dev/null +++ b/engine/src/components/shield.cpp @@ -0,0 +1,302 @@ +/* + * shield.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "shield.h" +#include "unit_csv_factory.h" +#include "damage/damageable_layer.h" +#include "resource/cout_util.h" + +#include + +std::string shield_facets_eight[8] = { + "Shield_Front_Top_Right", + "Shield_Front_Top_Left", + "Shield_Back_Top_Right", + "Shield_Back_Top_Left", + "Shield_Front_Bottom_Right", + "Shield_Front_Bottom_Left", + "Shield_Back_Bottom_Right", + "Shield_Back_Bottom_Left" +}; + +std::string shield_facets_four[4] = { + "Shield_Front_Top_Right", + "Shield_Back_Top_Left", + "Shield_Front_Bottom_Right", + "Shield_Front_Bottom_Left" +}; + +std::string shield_facets_two[2] = { + "Shield_Front_Top_Right", + "Shield_Back_Top_Left" +}; + + + +// Note that we need to define FacetConfiguration during load +Shield::Shield(DamageableLayer* shield_): Component("", 0.0, 0.0, false), + shield_(shield_) + {} + + + +void Shield::Load(std::string upgrade_key, std::string unit_key, + Unit *unit) { + //this->upgrade_key = upgrade_key; + //upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); + //int num_facets = UnitCSVFactory::GetVariable(upgrade_key, "Facets", 0); + + printPlayerMessage(unit_key, "Old Facets", std::to_string(shield_->number_of_facets)); + printPlayerMessage(unit_key, "Old Regeneration", std::to_string(shield_->GetRegeneration())); + + // Regeneration + const double regeneration = UnitCSVFactory::GetVariable(unit_key, "Shield_Recharge", 0.0); + printPlayerMessage(unit_key, "Regeneration", std::to_string(regeneration)); + + // Get shield count + + int shield_count = 0; + std::vector shield_values; + std::string shield_string_values[4]; + + // TODO: this mapping should really go away + // I love macros, NOT. + shield_string_values[0] = UnitCSVFactory::GetVariable(unit_key, "Shield_Front_Top_Right", std::string()); + shield_string_values[1] = UnitCSVFactory::GetVariable(unit_key, "Shield_Back_Top_Left", std::string()); + shield_string_values[2] = UnitCSVFactory::GetVariable(unit_key, "Shield_Front_Bottom_Right", std::string()); + shield_string_values[3] = UnitCSVFactory::GetVariable(unit_key, "Shield_Front_Bottom_Left", std::string()); + + for (int i = 0; i < 4; i++) { + if (shield_string_values[i].empty()) { + continue; + } + + shield_values.push_back(std::stod(shield_string_values[i])); + + // Should add up to the shield type - quad or dual + shield_count++; + } + + /* + We are making the following assumptions: + 1. The CSV is correct + 2. Dual shields are 0 front and 1 rear + 3. Quad shields are front (0), rear(1), right(2) and left(3) + 4. There is no support for 8 facet shields in the game. + This has more to do with the cockpit code than anything else + 5. We map the above index to our own + */ + + shield_->number_of_facets = shield_values.size(); + shield_->UpdateFacets(shield_values); + shield_->UpdateRegeneration(regeneration); + + + // TODO: shield leakage & efficiency +} + + +void Shield::SaveToCSV(std::map& unit) const { + // TODO: lib_damage figure out if this is correctly assigned + int number_of_shield_emitters = shield_->number_of_facets; + + for(int i=0;i<8;i++) { + unit[shield_facets_eight[i]] = ""; + } + + switch (number_of_shield_emitters) { + case 8: + for(int i=0;i<8;i++) { + unit[shield_facets_eight[i]] = std::to_string(shield_->facets[i].health.MaxValue()); + } + + break; + case 4: + for(int i=0;i<4;i++) { + unit[shield_facets_four[i]] = std::to_string(shield_->facets[i].health.MaxValue()); + } + + break; + case 2: + unit[shield_facets_two[0]] = std::to_string(shield_->facets[0].health.MaxValue()); + unit[shield_facets_two[1]] = std::to_string(shield_->facets[1].health.MaxValue()); + break; + + case 0: + // No shields + break; + + default: + // This should not happen + std::cout << number_of_shield_emitters << "\n"; + assert(0); + } +} + +std::string Shield::Describe() const { + return std::string(); +} + +bool Shield::CanUpgrade(const std::string upgrade_name) const { + return !Damaged(); +} + +bool Shield::CanDowngrade() const { + return !Damaged(); +} + +bool Shield::Upgrade(const std::string upgrade_key) { + if(!CanUpgrade(upgrade_key)) { + return false; + } + + int num_facets = UnitCSVFactory::GetVariable(upgrade_key, "Facets", 0); + //FacetConfiguration new_configuration = GetFacetForIndex(num_facets); + /*if(facet_configuration != GetFacetForIndex(num_facets)) { + return false; + }*/ + + this->upgrade_key = upgrade_key; + upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); + std::cout << upgrade_key << " : " << upgrade_name << " : " << num_facets << std::endl; + + // Regeneration + double regeneration = UnitCSVFactory::GetVariable(upgrade_key, "Shield_Recharge", 0.0); + + std::vector shield_values; + if(num_facets == 2) { + for (int i = 0; i < 2; i++) { + shield_values.push_back(UnitCSVFactory::GetVariable(upgrade_key, shield_facets_two[i], 0.0)); + shield_->facets[i].regeneration.SetMaxValue(regeneration); + } + } else if(num_facets == 4) { + for (int i = 0; i < 4; i++) { + shield_values.push_back(UnitCSVFactory::GetVariable(upgrade_key, shield_facets_four[i], 0.0)); + shield_->facets[i].regeneration.SetMaxValue(regeneration); + } + } else { + return false; + } + + shield_->UpdateFacets(shield_values); + + // TODO: shield leakage + + return true; +} + +bool Shield::Downgrade() { + return false; +} + +void Shield::Damage() { + /*for(Facet& facet : facets) { + facet.RandomDamage(); + } + + regeneration.RandomDamage(); + + // This works fine as long as opacity is originally defined correctly. + // For crappy shields, need opacity.max_value_ to be <1.0. + opacity.RandomDamage(); */ +} + +void Shield::Repair() { + /*DamageableLayer::Repair(); + + regeneration.RepairFully(); + opacity.RepairFully();*/ +} + +bool Shield::Damaged() const { + /*for(const Facet& facet : facets) { + if(facet.Damaged()) { + return true; + } + } + + return regeneration.Damaged();*/ + return false; +} + +bool Shield::Installed() const { + return true; //regeneration.MaxValue() > 0; +} + + +void Shield::Disable() { + /*for (Facet facet : facets) { + facet.Set(0.0); + } + + regeneration.Set(0.0);*/ +} + +void Shield::Discharge() { + /*for (Facet &facet : facets) { + facet -= regeneration; + }*/ +} + +void Shield::Enable() { + //regeneration.Set(regeneration.AdjustedValue()); +} + +bool Shield::Enabled() const { + return true; // facets[0].Enabled(); +} + + +// This is meant to be used when colliding with an enhancement. +// It enhances the shields. +// Right now, it simply upgrades them forever. Needs further thought. +// TODO: test, this functionality works, assuming it's actually supported. +void Shield::Enhance() { + // Boost shields to 150% + /*double enhancement_factor = 1.5; + + for(Facet& facet : facets) { + facet.SetMaxValue(facet.MaxValue() * enhancement_factor); + } + + regeneration.SetMaxValue(regeneration.MaxValue() * enhancement_factor);*/ +} + +void Shield::Regenerate() { + /*for(Facet& facet : facets) { + facet += regeneration; + }*/ +} + +void Shield::AdjustStrength(const double &percent) { + //double adjusted_percent = std::max(std::min(percent, 1.0f), 0.0f); + + /*for (Facet facet : facets) { + // TODO: + //facet.(adjusted_percent); + }*/ +} diff --git a/engine/src/components/shield.h b/engine/src/components/shield.h new file mode 100644 index 0000000000..c185a538d5 --- /dev/null +++ b/engine/src/components/shield.h @@ -0,0 +1,88 @@ +/* + * shield.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef VEGA_STRIKE_ENGINE_COMPONENTS_SHIELD_H +#define VEGA_STRIKE_ENGINE_COMPONENTS_SHIELD_H + +#include +#include + +#include "component.h" +#include "damageable_layer.h" + +class Unit; + +/* This class builds on top of DamageableLayer to represent a shield. + * Shield functions (e.g. opacity, regeneration) are handled here. + * DamageableLayer and Facet do not handle these at all. + * Damage to the shield generator is handled here. + */ +class Shield : public Component { + DamageableLayer* shield_ = nullptr; + + //Resource regeneration; + + // TODO: implement opacity + //Resource opacity; + friend class Damageable; +public: + Shield(DamageableLayer* shield_); + + virtual void Load(std::string upgrade_key, std::string unit_key, + Unit *unit); // Load from dictionary + virtual void SaveToCSV(std::map& unit) const; + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_key) const; + + virtual bool Upgrade(const std::string upgrade_key); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; + + void Disable(); + void Discharge(); + void Enable(); + bool Enabled() const; + + void Enhance(); // see collision enhancement + //double GetRegeneration() const { return regeneration.Value(); } + void Regenerate(); + //void SetRegeneration(double new_value) { regeneration.SetMaxValue(new_value); } + void AdjustStrength(const double &percent); +}; + + +#endif // VEGA_STRIKE_ENGINE_COMPONENTS_SHIELD_H diff --git a/engine/src/damage/damage.h b/engine/src/damage/damage.h index 249a8955f5..248087feac 100644 --- a/engine/src/damage/damage.h +++ b/engine/src/damage/damage.h @@ -32,19 +32,19 @@ 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 + 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 //float 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) { @@ -53,15 +53,15 @@ struct Damage { }; 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 b7ab28a834..56f63ce1ef 100644 --- a/engine/src/damage/damageable_layer.cpp +++ b/engine/src/damage/damageable_layer.cpp @@ -25,6 +25,7 @@ #include #include +#include // TODO: this is a use of the code in a different library. // I'm unhappy with this, so it needs to change. @@ -106,7 +107,7 @@ void DamageableLayer::Destroy() { } } -void DamageableLayer::Disable() { +/*void DamageableLayer::Disable() { for (Health &facet : facets) { facet.Disable(); } @@ -123,11 +124,11 @@ void DamageableLayer::Discharge(float discharge_rate, float minimum_discharge) { // Used for nicer graphics when entering SPEC void DamageableLayer::GradualDisable() { for (Health &facet : facets) { - facet.ReduceLayerMaximumByOne(); + // TODO: fix this. Right now we need a new value } -} +}*/ -void DamageableLayer::Enable() { +/*void DamageableLayer::Enable() { for (Health &facet : facets) { facet.Enable(); } @@ -139,6 +140,12 @@ bool DamageableLayer::Enabled() { } return facets[0].enabled; +}*/ + +void DamageableLayer::SetPower(const double power) { + for (Health &facet : facets) { + facet.SetPower(power); + } } // TODO: test @@ -220,38 +227,12 @@ int DamageableLayer::GetFacetIndex(const CoreVector &attack_vector) { 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; - } - - static std::random_device randome_device; - static std::mt19937 gen(randome_device()); - - // TODO: this feels a bit sloppy, as we're dealing in integers here. - static std::uniform_int_distribution<> impact_distribution(1, 100); - static std::uniform_int_distribution<> facet_distribution(0, facets.size() - 1); - bool affect_regeneration = impact_distribution(gen) <= chance_to_reduce_regeneration; - int facet_index = facet_distribution(gen); - - 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; + total_value += facet.health.Value(); } return total_value; } @@ -259,7 +240,7 @@ float DamageableLayer::TotalLayerValue() { float DamageableLayer::TotalMaxLayerValue() { float total_value = 0.0f; for (const Health &facet : facets) { - total_value += facet.max_health; + total_value += facet.health.MaxValue(); } return total_value; } @@ -267,7 +248,7 @@ float DamageableLayer::TotalMaxLayerValue() { float DamageableLayer::AverageLayerValue() { float total_value = 0.0f; for (const Health &facet : facets) { - total_value += facet.health; + total_value += facet.health.Value(); } return total_value / facets.size(); } @@ -275,7 +256,7 @@ float DamageableLayer::AverageLayerValue() { float DamageableLayer::AverageMaxLayerValue() { float total_value = 0.0f; for (const Health &facet : facets) { - total_value += facet.max_health; + total_value += facet.health.MaxValue(); } return total_value / facets.size(); } @@ -313,7 +294,7 @@ float DamageableLayer::GetMaxHealth() { return 0.0f; } - return facets[0].max_health; + return facets[0].health.MaxValue(); } float DamageableLayer::GetPercent(FacetName facet_name) { @@ -329,7 +310,7 @@ float DamageableLayer::GetPercent(FacetName facet_name) { case FacetConfiguration::two: case FacetConfiguration::four: numerator = facets[as_integer(facet_name)].health; - denominator = facets[as_integer(facet_name)].max_health; + denominator = facets[as_integer(facet_name)].health.MaxValue(); return CalculatePercentage(numerator, denominator); default: @@ -374,7 +355,7 @@ float DamageableLayer::GetPercent(FacetName facet_name) { int facet_index = indices_array[indices_index][i]; Health &facet = facets[facet_index]; aggregate_health += facet.health; - aggregate_max_health += facet.max_health; + aggregate_max_health += facet.health.Value(); } percent = CalculatePercentage(aggregate_health, aggregate_max_health); @@ -398,25 +379,45 @@ float DamageableLayer::GetRegeneration() { return 0.0f; } - return facets[0].regeneration; + return facets[0].regeneration.MaxValue(); } -void DamageableLayer::UpdateFacets(const unsigned int new_size, const float new_facets[4]) { - assert(new_size == number_of_facets); +void DamageableLayer::UpdateFacets(const std::vector new_facets) { + int new_size = new_facets.size(); + // TODO: assert(new_size == number_of_facets); + + if(facets.size() != number_of_facets) { + facets.clear(); + Health health = Health(layer_index, 0, 0); + for(int i=0;i new_facets); + void UpdateRegeneration(const double &new_regeneration_value); + + void DischargeShields(); }; #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 9f0fc6ca8d..e8544d8606 100644 --- a/engine/src/damage/damageable_object.cpp +++ b/engine/src/damage/damageable_object.cpp @@ -31,7 +31,7 @@ DamageableObject::DamageableObject() { Health hull_health = Health(1, 1, 0); Health armor_health = Health(0, 0, 0); - Health shield_health = Health(0, 0, 5); + Health shield_health = Health(2, 0, 0); DamageableLayer hull_layer = DamageableLayer(0, FacetConfiguration::one, hull_health, true); DamageableLayer armor_layer = DamageableLayer(1, FacetConfiguration::eight, armor_health, false); diff --git a/engine/src/damage/health.cpp b/engine/src/damage/health.cpp index b555ca4c2d..e1398576cf 100644 --- a/engine/src/damage/health.cpp +++ b/engine/src/damage/health.cpp @@ -29,7 +29,7 @@ #include #include -void Health::AdjustPower(const float &percent) { +void Health::AdjustPower(const double &percent) { if (!regenerative) { // Not applicable for armor and hull return; @@ -40,10 +40,7 @@ void Health::AdjustPower(const float &percent) { return; } - adjusted_health = max_health * percent; - if (adjusted_health < health) { - health = adjusted_health; - } + power = percent; } void Health::DealDamage(Damage &damage, InflictedDamage &inflicted_damage) { @@ -67,11 +64,11 @@ void Health::DealDamage(Damage &damage, InflictedDamage &inflicted_damage) { * @param vulnerability - adjust for */ // TODO: type is ugly hack -void Health::DealDamageComponent(int type, float &damage, float vulnerability, InflictedDamage &inflicted_damage) { +void Health::DealDamageComponent(int type, double &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); + const float adjusted_damage = std::min(damage * vulnerability, health.Value()); // We check if there's any damage left to pass on to the next layer damage -= adjusted_damage; @@ -96,76 +93,72 @@ void Health::DealDamageComponent(int type, float &damage, float vulnerability, I } } -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; +/* This is a bit kludgy. Set power via keyboard only works when not suppressed. +* If ship is in SPEC, power will be continuously set to 0. +* Therefore, if you set power to 1/3, go to SPEC and out again, power will be +* set to full again. +*/ +void Health::SetPower(const double power) { + if (regenerative) { + this->power = power; } } -void Health::Enhance(float percent) { + +/** Enhance adds some oomph to shields. + * Originally, I thought to just make them 150% one time. + * However, this isn't really significant and it's hard to implement + * with the underlying Resource class, which checks for max values. + * Instead, this will upgrade the Max value of shields and repair them. + */ +void Health::Enhance(double 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); -} + // Sanity checks. Don't want to use enhance to downgrade + // and more than x100 is excessive. + if(percent < 1.0 || percent > 100.0) { + return; + } -void Health::ReduceLayerMaximumByOnePercent() { - float percent = adjusted_health / max_health - 0.01f; - max_health = std::max(0.0f, max_health * percent); + health.SetMaxValue(health.MaxValue() * percent); + regeneration.SetMaxValue(regeneration.MaxValue() * percent); } -void Health::ReduceRegeneration(const float &percent) { - regeneration = std::max(0.0f, regeneration - max_regeneration * percent); -} void Health::Regenerate() { - if (!enabled || destroyed || !regenerative) { + if (!regenerative) { return; } - health = std::min(adjusted_health, health + regeneration); + if(health.Percent() < power) { + health++; + } else if(health.Percent() > power) { + health--; + } } void Health::Regenerate(float recharge_rate) { - if (!enabled || destroyed || !regenerative) { + /*if (!enabled || destroyed || !regenerative) { return; } - health = std::min(adjusted_health, health + recharge_rate); + health = std::min(adjusted_health, health + recharge_rate);*/ } void Health::SetHealth(float health) { - health = std::min(max_health, health); + /* health = std::min(max_health, health); health = std::max(0.0f, health); - this->health = health; + this->health = health;*/ } -void Health::Update(float health) { - this->health = health; - max_health = health; - adjusted_health = health; +void Health::Update(float new_health) { + this->health.SetMaxValue(new_health); } diff --git a/engine/src/damage/health.h b/engine/src/damage/health.h index 8d06d16506..a9123e4dd4 100644 --- a/engine/src/damage/health.h +++ b/engine/src/damage/health.h @@ -26,6 +26,8 @@ #include "damage.h" +#include "resource/resource.h" + /** * @brief The Health struct represents the health of something. * It can be a shield, armor, hull or subsystem. @@ -40,19 +42,18 @@ * potentially without actually destroying the ship. */ struct Health { + friend class Shield; 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 + + Resource health; + Resource regeneration; + double power; // 1.0 Full, 0.66 Two thirds, 0.0 Suppressed (FTL) or turned off + bool regenerative; bool destroyed; - bool enabled; + Damage vulnerabilities; // TODO: implement "shield leaks" @@ -70,47 +71,38 @@ struct Health { 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) : + Health(int layer, float health = 1, float regeneration = 0) : layer(layer), - max_health(max_health), - adjusted_health(max_health), - health(health), - max_regeneration(regeneration), - regeneration(regeneration), + health(health, 0, health), + regeneration(regeneration, 0, regeneration), regenerative(regeneration > 0) { + power = 1.0; // Only relevant for regenerative objects (e.g. shields). + 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; + return health.Percent(); } - void AdjustPower(const float &percent); + void AdjustPower(const double &percent); void AdjustPercentage(); void DealDamage(Damage &damage, InflictedDamage &inflicted_damage); - void DealDamageComponent(int type, float &damage, float vulnerability, InflictedDamage &inflicted_damage); - void Disable(); + void DealDamageComponent(int type, double &damage, float vulnerability, InflictedDamage &inflicted_damage); 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 SetPower(const double power); + void Enhance(double percent = 1.5f); void Regenerate(); void Regenerate(float recharge_rate); void SetHealth(float health); - void Update(float health); + void Update(float new_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 index bb7b283cfb..915621deae 100644 --- a/engine/src/damage/tests/health_tests.cpp +++ b/engine/src/damage/tests/health_tests.cpp @@ -30,7 +30,7 @@ // Demonstrate some basic assertions. TEST(Shield, Sanity) { - Damage damage; + /*Damage damage; InflictedDamage inflicted_damage(3); damage.normal_damage = 10; EXPECT_EQ(damage.normal_damage, 10); @@ -101,5 +101,5 @@ TEST(Armor, Sanity) { EXPECT_FALSE(health.regenerative); EXPECT_TRUE(health.destroyed); EXPECT_FALSE(health.enabled); - EXPECT_EQ(damage.normal_damage, 20); + 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..94847ff17c 100644 --- a/engine/src/damage/tests/layer_tests.cpp +++ b/engine/src/damage/tests/layer_tests.cpp @@ -24,6 +24,7 @@ #include +#include #include "damageable_layer.h" #include "core_vector.h" @@ -52,3 +53,36 @@ TEST(Layer, Sanity) { EXPECT_EQ(layer.GetFacetIndex(CoreVector(-2, 0, -1)), 1); } + +TEST(Layer, Sanity_2) { + Health health(0, 10, 0); + EXPECT_EQ(health.health.MaxValue(), 10); + EXPECT_EQ(health.health.Value(), 10); + + DamageableLayer layer = DamageableLayer(0, FacetConfiguration::one, health, true); + std::vector layers = { layer }; + EXPECT_EQ(layer.facets[0].health.MaxValue(), 10); + EXPECT_EQ(layer.facets[0].health.Value(), 10); + + DamageableLayer* ptr = &layers[0]; + EXPECT_EQ(ptr->facets[0].health.MaxValue(), 10); + EXPECT_EQ(layer.facets[0].health.Value(), 10); + + std::vector new_health = {50}; + ptr->UpdateFacets(new_health); + + EXPECT_EQ(layer.facets[0].health.MaxValue(), 50); + EXPECT_EQ(layer.facets[0].health.Value(), 50); + + EXPECT_EQ(ptr->facets[0].health.MaxValue(), 50); + EXPECT_EQ(ptr->facets[0].health.Value(), 50); + + EXPECT_EQ(ptr, &layer); +} + +// How embarrassing +TEST(Layer, StringComparison) { + std::string player_ship = "player_ship"; + bool result = (player_ship == "player_ship"); + EXPECT_TRUE(result); +} \ No newline at end of file diff --git a/engine/src/damage/tests/object_tests.cpp b/engine/src/damage/tests/object_tests.cpp index 897a53b386..7ec94d0e07 100644 --- a/engine/src/damage/tests/object_tests.cpp +++ b/engine/src/damage/tests/object_tests.cpp @@ -29,7 +29,7 @@ // Demonstrate some basic assertions. TEST(DamageableObject, Sanity) { - CoreVector core_vector(1, 1, 1); // Left top front + /*CoreVector core_vector(1, 1, 1); // Left top front DamageableObject object; @@ -62,5 +62,5 @@ TEST(DamageableObject, Sanity) { EXPECT_EQ(inflicted_damage.phase_damage, 0); EXPECT_EQ(inflicted_damage.inflicted_damage_by_layer[0], 0); EXPECT_EQ(inflicted_damage.inflicted_damage_by_layer[1], 0); - EXPECT_EQ(inflicted_damage.inflicted_damage_by_layer[2], 10); + EXPECT_EQ(inflicted_damage.inflicted_damage_by_layer[2], 10);*/ } diff --git a/engine/src/gfx/cockpit.cpp b/engine/src/gfx/cockpit.cpp index 80907a4a5c..81f8df07c5 100644 --- a/engine/src/gfx/cockpit.cpp +++ b/engine/src/gfx/cockpit.cpp @@ -362,7 +362,7 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { // Subtracing enum SHIELDF (first shield gauge) converts the // stat parameter to the index of the shield. - if (target->GetShieldLayer().facets[shield_index].max_health > 0) { + if (target->GetShieldLayer().facets[shield_index].health.MaxValue() > 0) { return target->GetShieldLayer().facets[shield_index].Percent(); } else { return 0; diff --git a/engine/src/resource/cout_util.h b/engine/src/resource/cout_util.h new file mode 100644 index 0000000000..a436830c0a --- /dev/null +++ b/engine/src/resource/cout_util.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2001-2023 Daniel Horn, pyramid3d, 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 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_RESOURCE_COUT_UTIL_H +#define VEGA_STRIKE_ENGINE_RESOURCE_COUT_UTIL_H + +#include + +void printPlayerMessage(std::string key, + std::string message, + std::string value) { + if(key == "player_ship") { + std::cout << message << " : " << value << std::endl; + } +} + + +#endif //VEGA_STRIKE_ENGINE_RESOURCE_COUT_UTIL_H diff --git a/engine/src/resource/resource.cpp b/engine/src/resource/resource.cpp index 4d6d92a815..fb5cac318d 100644 --- a/engine/src/resource/resource.cpp +++ b/engine/src/resource/resource.cpp @@ -87,7 +87,7 @@ void Resource::SetMaxValue(const T &value) { return; } - adjusted_max_value_ = max_value_ = value; + value_ = adjusted_max_value_ = max_value_ = value; } diff --git a/engine/src/resource/resource.h b/engine/src/resource/resource.h index 45da66bb57..4ac247b44b 100644 --- a/engine/src/resource/resource.h +++ b/engine/src/resource/resource.h @@ -109,6 +109,10 @@ class Resource { void RepairFully(); void RepairByValue(const T &value); void RepairByPercent(const T &value); + + T* ValuePtr() { return &value_; } + T* AdjustedMaxValuePtr() { return &adjusted_max_value_; } + T* MaxValuePtr() { return &max_value_; } }; template diff --git a/engine/src/star_system.cpp b/engine/src/star_system.cpp index 37bce4078c..ee04faf49d 100644 --- a/engine/src/star_system.cpp +++ b/engine/src/star_system.cpp @@ -1280,8 +1280,7 @@ void StarSystem::ProcessPendingJumps() { } if (game_options()->jump_disables_shields) { // Disable and then enable so they'll start recharging - un->shield->Disable(); - un->shield->Enable(); + un->shield->DischargeShields(); } } } From f509220de8e709e4cc80620212fb2ff9d91738ba Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Sun, 24 Mar 2024 19:02:23 +0200 Subject: [PATCH 10/16] Move regeneration from health to shield --- engine/src/cmd/ai/firekeyboard.cpp | 2 +- engine/src/cmd/basecomputer.cpp | 7 +- engine/src/cmd/collision.cpp | 2 +- engine/src/cmd/damageable.cpp | 9 +- engine/src/cmd/damageable.h | 4 - engine/src/cmd/unit_csv.cpp | 10 +- engine/src/cmd/unit_generic.cpp | 1 - engine/src/cmd/upgradeable_unit.cpp | 5 - engine/src/components/armor.cpp | 25 ++-- engine/src/components/shield.cpp | 153 ++++++++++++-------- engine/src/components/shield.h | 18 +-- engine/src/damage/damageable_layer.cpp | 176 +++++------------------- engine/src/damage/damageable_layer.h | 33 ++--- engine/src/damage/facet_configuration.h | 1 + engine/src/damage/health.cpp | 91 ++---------- engine/src/damage/health.h | 44 ++---- engine/src/gfx/cockpit_generic.cpp | 2 +- engine/src/resource/resource.cpp | 9 ++ engine/src/resource/resource.h | 1 + engine/src/star_system.cpp | 2 +- 20 files changed, 218 insertions(+), 377 deletions(-) diff --git a/engine/src/cmd/ai/firekeyboard.cpp b/engine/src/cmd/ai/firekeyboard.cpp index 79c2426250..bcb41b208b 100644 --- a/engine/src/cmd/ai/firekeyboard.cpp +++ b/engine/src/cmd/ai/firekeyboard.cpp @@ -1731,7 +1731,7 @@ void FireKeyboard::Execute() { float f_result = f().shieldpowerstate; if (f_result != 1) { - parent->shield->AdjustPower(f_result); + parent->shield_component.SetPower(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 6da05478ca..f0ba00ac60 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -3911,6 +3911,9 @@ bool BaseComputer::sellUpgrade(const EventCommandId &command, Control *control) Unit *playerUnit = m_player.GetUnit(); Unit *baseUnit = m_base.GetUnit(); if (baseUnit && playerUnit) { + // New code + UpgradeOperationResult result = playerUnit->UpgradeUnit(item->GetName(), false, true); + playerUnit->SellCargo(item->GetName(), quantity, _Universe->AccessCockpit()->credits, sold, baseUnit); UnitUtil::RecomputeUnitUpgrades(playerUnit); refresh(); @@ -5561,7 +5564,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } - const double regeneration = playerUnit->shield->GetRegeneration(); + const double regeneration = playerUnit->shield_component.GetRegeneration(); if (!mode) { PRETTY_ADDU(statcolor + "Shield protection recharge speed: #-c", regeneration * VSDM, 0, "MJ/s"); } else if (replacement_mode != 0 || regeneration) { @@ -5770,7 +5773,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C float avail = (playerUnit->energy_manager.GetReactorCapacity() * RSconverter - maxshield * VSDM); int num_shields = playerUnit->shield->number_of_facets; - float regeneration = playerUnit->shield->GetRegeneration(); + float regeneration = playerUnit->shield_component.GetRegeneration(); float overhead = (shields_require_power) ? (regeneration / shieldenergycap * shield_maintenance_cost * num_shields * VSDM) : 0; diff --git a/engine/src/cmd/collision.cpp b/engine/src/cmd/collision.cpp index fec749cd64..b6c6e76647 100644 --- a/engine/src/cmd/collision.cpp +++ b/engine/src/cmd/collision.cpp @@ -126,7 +126,7 @@ void Collision::shouldApplyForceAndDealDamage(Unit *other_unit) { // TODO: someone from the "product" team needs to define the // exact behavior. Preferably after we sort the upgrade // code. - other_unit->shield->Enhance(); + other_unit->shield_component.Enhance(); /*double percent; char tempdata[sizeof(Shield)]; diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index 5c1ac798dd..b2c2e2e495 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -570,7 +570,7 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi // Here we store the actual charge we'll use in RegenShields // TODO: fix this. It's a hack just to build... - double max_shield_recharge = unit->shield->GetRegeneration(); + double max_shield_recharge = unit->shield_component.GetRegeneration(); double actual_recharge = max_shield_recharge * energy->Powered(EnergyConsumerClassification::ShieldRegen); @@ -589,14 +589,15 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi // Discharge shields due to energy or SPEC or cloak if ((in_warp && !shields_in_spec) || unit->cloak.Active()) { - shield->SetPower(0.0); + // "Damage" power + shield_component.SetPowerCap(0.0); } else { // Figure out how to support partial power - shield->SetPower(1.0); + shield_component.SetPowerCap(1.0); } // Shield regeneration - shield->Regenerate(shield_recharge); + shield_component.Regenerate(); // TODO: shield_recharge); } float Damageable::MaxShieldVal() const { diff --git a/engine/src/cmd/damageable.h b/engine/src/cmd/damageable.h index 396cd14b21..01fcbcb5b4 100644 --- a/engine/src/cmd/damageable.h +++ b/engine/src/cmd/damageable.h @@ -101,10 +101,6 @@ class Damageable : public DamageableObject { 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); } diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index 86cee8e23c..1805e9b6e6 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -760,10 +760,6 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ for (int i = 0; i < 8; i++) { double tmp_armor_value = UnitCSVFactory::GetVariable(unit_key, armor_keys[i], 0.0); armor_values.push_back(tmp_armor_value); - - if(saved_game) { - std::cout << unit_key << " : " << armor_keys[i] << " = " << tmp_armor_value << std::endl; - } } armor->UpdateFacets(armor_values); @@ -1062,6 +1058,7 @@ static string tos(int val) { return XMLSupport::tostring(val); } +// Used by Base Computer Ship Info string Unit::WriteUnitString() { const bool UNITTAB = configuration()->physics_config.unit_table; string ret = ""; @@ -1253,10 +1250,7 @@ string Unit::WriteUnitString() { }*/ - //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["Warp_Min_Multiplier"] = tos(graphicOptions.MinWarpMultiplier); unit["Warp_Max_Multiplier"] = tos(graphicOptions.MaxWarpMultiplier); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 97d67462ef..fe9729d167 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -2834,7 +2834,6 @@ bool Unit::UpAndDownGrade(const Unit *up, // New Code UpgradeOperationResult result = UpgradeUnit(up->name, !downgrade, touchme); if(result.upgradeable) { - std::cout << "Upgraded successfully\n"; percentage = result.percent; return result.success; } diff --git a/engine/src/cmd/upgradeable_unit.cpp b/engine/src/cmd/upgradeable_unit.cpp index 76c44b58ed..ec7cd5d664 100644 --- a/engine/src/cmd/upgradeable_unit.cpp +++ b/engine/src/cmd/upgradeable_unit.cpp @@ -91,11 +91,6 @@ UpgradeType GetUpgradeType(const std::string upgrade_key) { std::string upgrade_type_string = UnitCSVFactory::GetVariable(upgrade_key, "Upgrade_Type", std::string()); std::string upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); - if(upgrade_type_string.size() > 0) { - std::cout << "Upgrade key: " << upgrade_key << " " << upgrade_type_string << std::endl; - } - - if(upgrade_type_string.empty()) return UpgradeType::None; if(upgrade_type_string == "Armor") { diff --git a/engine/src/components/armor.cpp b/engine/src/components/armor.cpp index 2a70932db3..bdd5344ce3 100644 --- a/engine/src/components/armor.cpp +++ b/engine/src/components/armor.cpp @@ -55,11 +55,17 @@ void Armor::Load(std::string upgrade_key, std::string unit_key, // Damageable Layer std::string upgrade_type_string = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); + std::vector armor_values; + for(int i = 0;i < 8;i++) { - double armor_facet_current = UnitCSVFactory::GetVariable(unit_key, armor_facets[i], 0.0); - double armor_facet_max = UnitCSVFactory::GetVariable(upgrade_key, armor_facets[i], 0.0); - armor_layer_->facets[i].Update(armor_facet_max); + // TODO: implement + //double armor_facet_max = UnitCSVFactory::GetVariable(upgrade_key, armor_facets[i], 0.0); + double armor_value = UnitCSVFactory::GetVariable(unit_key, + armor_facets[i], 0.0); + armor_values.push_back(armor_value); } + + armor_layer_->UpdateFacets(armor_values); } std::string Armor::SaveToJSON() const { @@ -90,7 +96,7 @@ bool Armor::Downgrade() { //mass = 0; // volume = 0; for(int i = 0;i < 8;i++) { - armor_layer_->facets[i].Update(0.0f); + armor_layer_->facets[i].health.SetMaxValue(0.0); } return true; @@ -119,13 +125,16 @@ bool Armor::Upgrade(const std::string upgrade_name) { return false; } - // TODO: read all 8 facets + std::vector armor_values; + for(int i = 0;i < 8;i++) { - double armor = UnitCSVFactory::GetVariable(upgrade_name, - "Armor_Front_Top_Right", 0.0); - armor_layer_->facets[i].Update(armor); + double armor_value = UnitCSVFactory::GetVariable(upgrade_name, + armor_facets[i], 0.0); + armor_values.push_back(armor_value); } + armor_layer_->UpdateFacets(armor_values); + return true; } diff --git a/engine/src/components/shield.cpp b/engine/src/components/shield.cpp index 7793b44c3b..01112c687b 100644 --- a/engine/src/components/shield.cpp +++ b/engine/src/components/shield.cpp @@ -32,6 +32,8 @@ #include +const std::string SHIELD_RECHARGE = "Shield_Recharge"; + std::string shield_facets_eight[8] = { "Shield_Front_Top_Right", "Shield_Front_Top_Left", @@ -59,7 +61,9 @@ std::string shield_facets_two[2] = { // Note that we need to define FacetConfiguration during load Shield::Shield(DamageableLayer* shield_): Component("", 0.0, 0.0, false), - shield_(shield_) + shield_(shield_), + regeneration(0,0,0), + power(1.0,0.0,1.0) {} @@ -70,12 +74,8 @@ void Shield::Load(std::string upgrade_key, std::string unit_key, //upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); //int num_facets = UnitCSVFactory::GetVariable(upgrade_key, "Facets", 0); - printPlayerMessage(unit_key, "Old Facets", std::to_string(shield_->number_of_facets)); - printPlayerMessage(unit_key, "Old Regeneration", std::to_string(shield_->GetRegeneration())); - // Regeneration - const double regeneration = UnitCSVFactory::GetVariable(unit_key, "Shield_Recharge", 0.0); - printPlayerMessage(unit_key, "Regeneration", std::to_string(regeneration)); + const double regeneration = UnitCSVFactory::GetVariable(unit_key, SHIELD_RECHARGE, 0.0); // Get shield count @@ -113,8 +113,7 @@ void Shield::Load(std::string upgrade_key, std::string unit_key, shield_->number_of_facets = shield_values.size(); shield_->UpdateFacets(shield_values); - shield_->UpdateRegeneration(regeneration); - + this->regeneration.SetMaxValue(regeneration); // TODO: shield leakage & efficiency } @@ -124,6 +123,9 @@ void Shield::SaveToCSV(std::map& unit) const { // TODO: lib_damage figure out if this is correctly assigned int number_of_shield_emitters = shield_->number_of_facets; + // TODO: This won't record damage to regeneration or shield facets + unit[SHIELD_RECHARGE] = std::to_string(regeneration.MaxValue()); + for(int i=0;i<8;i++) { unit[shield_facets_eight[i]] = ""; } @@ -155,20 +157,38 @@ void Shield::SaveToCSV(std::map& unit) const { std::cout << number_of_shield_emitters << "\n"; assert(0); } + + //TODO: lib_damage shield leak and efficiency + unit["Shield_Leak"] = std::to_string(0); //tos( shield.leak/100.0 ); + unit["Shield_Efficiency"] = std::to_string(1); //tos( shield.efficiency ); } std::string Shield::Describe() const { return std::string(); } -bool Shield::CanUpgrade(const std::string upgrade_name) const { +bool Shield::CanDowngrade() const { return !Damaged(); } -bool Shield::CanDowngrade() const { +bool Shield::CanUpgrade(const std::string upgrade_name) const { return !Damaged(); } +bool Shield::Downgrade() { + if(!CanDowngrade()) { + return false; + } + + regeneration.SetMaxValue(0.0); + power.SetMaxValue(0.0); + + std::vector empty_vector; + shield_->UpdateFacets(empty_vector); + + return false; +} + bool Shield::Upgrade(const std::string upgrade_key) { if(!CanUpgrade(upgrade_key)) { return false; @@ -182,21 +202,18 @@ bool Shield::Upgrade(const std::string upgrade_key) { this->upgrade_key = upgrade_key; upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); - std::cout << upgrade_key << " : " << upgrade_name << " : " << num_facets << std::endl; // Regeneration - double regeneration = UnitCSVFactory::GetVariable(upgrade_key, "Shield_Recharge", 0.0); + regeneration.SetMaxValue(UnitCSVFactory::GetVariable(upgrade_key, SHIELD_RECHARGE, 0.0)); std::vector shield_values; if(num_facets == 2) { for (int i = 0; i < 2; i++) { shield_values.push_back(UnitCSVFactory::GetVariable(upgrade_key, shield_facets_two[i], 0.0)); - shield_->facets[i].regeneration.SetMaxValue(regeneration); } } else if(num_facets == 4) { for (int i = 0; i < 4; i++) { shield_values.push_back(UnitCSVFactory::GetVariable(upgrade_key, shield_facets_four[i], 0.0)); - shield_->facets[i].regeneration.SetMaxValue(regeneration); } } else { return false; @@ -209,94 +226,122 @@ bool Shield::Upgrade(const std::string upgrade_key) { return true; } -bool Shield::Downgrade() { - return false; -} + void Shield::Damage() { - /*for(Facet& facet : facets) { - facet.RandomDamage(); + for(Health& facet : shield_->facets) { + facet.health.RandomDamage(); } regeneration.RandomDamage(); // This works fine as long as opacity is originally defined correctly. // For crappy shields, need opacity.max_value_ to be <1.0. - opacity.RandomDamage(); */ + // TODO: opacity.RandomDamage(); } void Shield::Repair() { - /*DamageableLayer::Repair(); + for(Health& facet : shield_->facets) { + facet.health.RepairFully(); + } regeneration.RepairFully(); - opacity.RepairFully();*/ + // TODO: opacity.RepairFully(); } bool Shield::Damaged() const { - /*for(const Facet& facet : facets) { - if(facet.Damaged()) { + for(const Health& facet : shield_->facets) { + if(facet.health.Damaged()) { return true; } } - return regeneration.Damaged();*/ - return false; + return regeneration.Damaged(); } bool Shield::Installed() const { - return true; //regeneration.MaxValue() > 0; + return regeneration.MaxValue() > 0; } -void Shield::Disable() { - /*for (Facet facet : facets) { - facet.Set(0.0); - } +void Shield::AdjustPower(const double &percent) { + power.Set(percent); +} - regeneration.Set(0.0);*/ + +void Shield::Disable() { + power.Set(0.0); } +// Zeros out shields but can immediately start recharging +// Used for things like jump effects void Shield::Discharge() { - /*for (Facet &facet : facets) { - facet -= regeneration; - }*/ + for (Health &facet : shield_->facets) { + facet.health.Set(0.0); + } } void Shield::Enable() { - //regeneration.Set(regeneration.AdjustedValue()); + power.Set(1.0); } bool Shield::Enabled() const { - return true; // facets[0].Enabled(); + return power.Value() > 0.0; } -// This is meant to be used when colliding with an enhancement. -// It enhances the shields. -// Right now, it simply upgrades them forever. Needs further thought. + +/** Enhance adds some oomph to shields. + * Originally, I thought to just make them 150% one time. + * However, this isn't really significant and it's hard to implement + * with the underlying Resource class, which checks for max values. + * Instead, this will upgrade the Max value of shields and repair them. + */ // TODO: test, this functionality works, assuming it's actually supported. void Shield::Enhance() { // Boost shields to 150% - /*double enhancement_factor = 1.5; + double enhancement_factor = 1.5; - for(Facet& facet : facets) { - facet.SetMaxValue(facet.MaxValue() * enhancement_factor); + for(Health& facet : shield_->facets) { + facet.health.SetMaxValue(facet.health.MaxValue() * enhancement_factor); } - regeneration.SetMaxValue(regeneration.MaxValue() * enhancement_factor);*/ + regeneration.SetMaxValue(regeneration.MaxValue() * enhancement_factor); } -void Shield::Regenerate() { - /*for(Facet& facet : facets) { - facet += regeneration; - }*/ + + + + +/* This is a bit kludgy. Set power via keyboard only works when not suppressed. +* If ship is in SPEC, power will be continuously set to 0. +* Therefore, if you set power to 1/3, go to SPEC and out again, power will be +* set to full again. +*/ +void Shield::SetPower(const double power) { + this->power = power; } -void Shield::AdjustStrength(const double &percent) { - //double adjusted_percent = std::max(std::min(percent, 1.0f), 0.0f); +// Do we need this? +void Shield::SetPowerCap(const double power) { + this->power.SetAdjustedMaxValue(power); +} - /*for (Facet facet : facets) { - // TODO: - //facet.(adjusted_percent); - }*/ + + +double Shield::GetRegeneration() const { + return regeneration.Value(); } + +void Shield::Regenerate() { + for(Health& facet : shield_->facets) { + if(facet.health.Percent() < power) { + // If shield generator is damaged, regenerate less + facet.health += regeneration.Value(); + } else if(facet.health.Percent() > power) { + // If in SPEC or cloaked, decrease shields as fast as possible + // to prevent a scenario where damaged shields work in SPEC. + facet.health -= regeneration.MaxValue(); + } + } +} \ No newline at end of file diff --git a/engine/src/components/shield.h b/engine/src/components/shield.h index c185a538d5..b94b09bc9a 100644 --- a/engine/src/components/shield.h +++ b/engine/src/components/shield.h @@ -44,8 +44,10 @@ class Unit; class Shield : public Component { DamageableLayer* shield_ = nullptr; - //Resource regeneration; + Resource regeneration; + Resource power; // 1.0 Full, 0.66 Two thirds, 0.0 Suppressed (FTL) or turned off + // TODO: implement "shield leaks" aka // TODO: implement opacity //Resource opacity; friend class Damageable; @@ -59,11 +61,8 @@ class Shield : public Component { virtual std::string Describe() const; // Describe component in base_computer virtual bool CanDowngrade() const; - - virtual bool Downgrade(); - virtual bool CanUpgrade(const std::string upgrade_key) const; - + virtual bool Downgrade(); virtual bool Upgrade(const std::string upgrade_key); virtual void Damage(); @@ -72,16 +71,17 @@ class Shield : public Component { virtual bool Damaged() const; virtual bool Installed() const; + void AdjustPower(const double &percent); void Disable(); void Discharge(); void Enable(); bool Enabled() const; - void Enhance(); // see collision enhancement - //double GetRegeneration() const { return regeneration.Value(); } + double GetRegeneration() const; void Regenerate(); - //void SetRegeneration(double new_value) { regeneration.SetMaxValue(new_value); } - void AdjustStrength(const double &percent); + + void SetPower(const double power); + void SetPowerCap(const double power); }; diff --git a/engine/src/damage/damageable_layer.cpp b/engine/src/damage/damageable_layer.cpp index 56f63ce1ef..5e55d22f0c 100644 --- a/engine/src/damage/damageable_layer.cpp +++ b/engine/src/damage/damageable_layer.cpp @@ -52,8 +52,8 @@ DamageableLayer::DamageableLayer(int layer_index, DamageableLayer::DamageableLayer(int layer_index, FacetConfiguration configuration, - float health_array[], - float regeneration, + double health_array[], + double regeneration, bool core_layer) { int size = as_integer(configuration); @@ -84,13 +84,7 @@ DamageableLayer::DamageableLayer() : 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) { @@ -107,55 +101,6 @@ void DamageableLayer::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; - } - } -} - -// Used for nicer graphics when entering SPEC -void DamageableLayer::GradualDisable() { - for (Health &facet : facets) { - // TODO: fix this. Right now we need a new value - } -}*/ - -/*void DamageableLayer::Enable() { - for (Health &facet : facets) { - facet.Enable(); - } -} - -bool DamageableLayer::Enabled() { - if (number_of_facets == 0) { - return false; - } - - return facets[0].enabled; -}*/ - -void DamageableLayer::SetPower(const double power) { - for (Health &facet : facets) { - facet.SetPower(power); - } -} - -// 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) { if (number_of_facets == 0) { @@ -163,9 +108,9 @@ int DamageableLayer::GetFacetIndex(const CoreVector &attack_vector) { } // 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; + double k = attack_vector.k; if (configuration == FacetConfiguration::one) { return 0; @@ -176,8 +121,8 @@ int DamageableLayer::GetFacetIndex(const CoreVector &attack_vector) { return 1; } } else if (configuration == FacetConfiguration::four) { - float a = i + k; - float b = i - k; + double a = i + k; + double b = i - k; if (a >= 0 && b >= 0) { return 0; } @@ -229,67 +174,40 @@ int DamageableLayer::GetFacetIndex(const CoreVector &attack_vector) { -float DamageableLayer::TotalLayerValue() { - float total_value = 0.0f; +double DamageableLayer::TotalLayerValue() { + double total_value = 0.0f; for (const Health &facet : facets) { total_value += facet.health.Value(); } return total_value; } -float DamageableLayer::TotalMaxLayerValue() { - float total_value = 0.0f; +double DamageableLayer::TotalMaxLayerValue() { + double total_value = 0.0f; for (const Health &facet : facets) { total_value += facet.health.MaxValue(); } return total_value; } -float DamageableLayer::AverageLayerValue() { - float total_value = 0.0f; +double DamageableLayer::AverageLayerValue() { + double total_value = 0.0f; for (const Health &facet : facets) { total_value += facet.health.Value(); } return total_value / facets.size(); } -float DamageableLayer::AverageMaxLayerValue() { - float total_value = 0.0f; +double DamageableLayer::AverageMaxLayerValue() { + double total_value = 0.0f; for (const Health &facet : facets) { total_value += facet.health.MaxValue(); } return total_value / facets.size(); } -float CalculatePercentage(float numerator, float denominator) { - return numerator / denominator; - - // All these checks potentially slow down the game - // and cause the graphics to flicker - /*if(denominator < numerator) { - return 0.0; // This should really be an error - } - - if(denominator <= 0.0f || numerator <0.0f) { - return 0.0; - } - - float percent = numerator / denominator; - - if(percent > 1.0f) { - return 1.0; - } - if(percent <0.01) { - return 0.0f; - } - - // Possibly nicer alternative - //return roundf(percent * 100) / 100.0; - return percent;*/ -} - -float DamageableLayer::GetMaxHealth() { +double DamageableLayer::GetMaxHealth() { if (number_of_facets == 0) { return 0.0f; } @@ -297,12 +215,12 @@ float DamageableLayer::GetMaxHealth() { return facets[0].health.MaxValue(); } -float DamageableLayer::GetPercent(FacetName facet_name) { +double DamageableLayer::GetPercent(FacetName facet_name) { if (number_of_facets == 0) { return 0.0f; } - float numerator, denominator; + double numerator, denominator; // One, Two or Four shield configurations // Note the fallthrough switch (configuration) { @@ -311,7 +229,7 @@ float DamageableLayer::GetPercent(FacetName facet_name) { case FacetConfiguration::four: numerator = facets[as_integer(facet_name)].health; denominator = facets[as_integer(facet_name)].health.MaxValue(); - return CalculatePercentage(numerator, denominator); + return numerator / denominator; default: break; // Noop @@ -320,7 +238,7 @@ float DamageableLayer::GetPercent(FacetName facet_name) { // We handle the eight configuration outside the switch // as it is longer and more complex - float percent = 0.0f; + double percent = 0.0f; // Indices of facets for shield configuration eight static const int indices_array[4][4] = {{0, 2, 4, 6}, // left @@ -348,8 +266,8 @@ float DamageableLayer::GetPercent(FacetName facet_name) { return 0; } - float aggregate_health = 0; - float aggregate_max_health = 0; + double aggregate_health = 0; + double aggregate_max_health = 0; for (int i = 0; i < 4; i++) { int facet_index = indices_array[indices_index][i]; @@ -358,35 +276,20 @@ float DamageableLayer::GetPercent(FacetName facet_name) { aggregate_max_health += facet.health.Value(); } - percent = CalculatePercentage(aggregate_health, aggregate_max_health); + percent = aggregate_health / aggregate_max_health; return percent; } -void DamageableLayer::Regenerate(float recharge_rate) { - for (Health &facet : facets) { - facet.Regenerate(recharge_rate); - } -} -void DamageableLayer::RegenerateOrDischarge(float recharge_rate, bool velocity_discharge, float discharge_rate) { - for (Health &facet : facets) { - facet.Regenerate(recharge_rate); - } -} -float DamageableLayer::GetRegeneration() { - if (number_of_facets == 0) { - return 0.0f; - } - return facets[0].regeneration.MaxValue(); -} void DamageableLayer::UpdateFacets(const std::vector new_facets) { int new_size = new_facets.size(); // TODO: assert(new_size == number_of_facets); - if(facets.size() != number_of_facets) { + if(new_size != number_of_facets) { + number_of_facets = new_size; facets.clear(); Health health = Health(layer_index, 0, 0); for(int i=0;i new_facets) { switch (number_of_facets) { case 0: - //configuration = FacetConfiguration::zero; + configuration = FacetConfiguration::zero; break; case 1: configuration = FacetConfiguration::one; - facets[0].Update(new_facets[0]); + facets[0].health.SetMaxValue(new_facets[0]); break; case 4: configuration = FacetConfiguration::four; - facets[0].Update(new_facets[3]); - facets[1].Update(new_facets[2]); - facets[2].Update(new_facets[0]); - facets[3].Update(new_facets[1]); + facets[0].health.SetMaxValue(new_facets[3]); + facets[1].health.SetMaxValue(new_facets[2]); + facets[2].health.SetMaxValue(new_facets[0]); + facets[3].health.SetMaxValue(new_facets[1]); break; case 2: configuration = FacetConfiguration::two; for (unsigned int i = 0; i < number_of_facets; i++) { - facets[i].Update(new_facets[i]); + facets[i].health.SetMaxValue(new_facets[i]); } break; case 8: configuration = FacetConfiguration::eight; for (unsigned int i = 0; i < number_of_facets; i++) { - facets[i].Update(new_facets[i]); + facets[i].health.SetMaxValue(new_facets[i]); } break; } } -void DamageableLayer::UpdateRegeneration(const double &new_regeneration_value) { - for (Health &facet : facets) { - facet.regeneration.SetMaxValue(new_regeneration_value); - facet.regenerative = true; - facet.power = 1.0; - } -} - -void DamageableLayer::DischargeShields() { - for (Health &facet : facets) { - facet.health.Set(0.0); - } -} \ No newline at end of file diff --git a/engine/src/damage/damageable_layer.h b/engine/src/damage/damageable_layer.h index 5ae3e3e425..3d855a3fe4 100644 --- a/engine/src/damage/damageable_layer.h +++ b/engine/src/damage/damageable_layer.h @@ -47,7 +47,6 @@ struct DamageableLayer { friend class Damageable; friend struct DamageableObject; - //static float damage_component_chance = 0.03; DamageableLayer(int layer_index, FacetConfiguration configuration, Health health_template, @@ -55,8 +54,8 @@ struct DamageableLayer { DamageableLayer(int layer_index, FacetConfiguration configuration, - float health_array[], - float regeneration, + double health_array[], + double regeneration, bool core_layer); DamageableLayer(int layer_index, @@ -65,34 +64,20 @@ struct DamageableLayer { bool core_layer); DamageableLayer(); - 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 SetPower(const double power); - void Enhance(); - + int GetFacetIndex(const CoreVector &attack_vector); - float TotalLayerValue(); - float TotalMaxLayerValue(); - float AverageLayerValue(); - float AverageMaxLayerValue(); + double TotalLayerValue(); + double TotalMaxLayerValue(); + double AverageLayerValue(); + double AverageMaxLayerValue(); - float GetMaxHealth(); - float GetPercent(FacetName facet_name); + double GetMaxHealth(); + double GetPercent(FacetName facet_name); - void Regenerate(float recharge_rate); - void RegenerateOrDischarge(float recharge, bool velocity_discharge, float discharge_rate); - float GetRegeneration(); void UpdateFacets(const std::vector new_facets); - void UpdateRegeneration(const double &new_regeneration_value); - - void DischargeShields(); }; #endif //VEGA_STRIKE_ENGINE_DAMAGE_DAMAGEABLE_LAYER_H diff --git a/engine/src/damage/facet_configuration.h b/engine/src/damage/facet_configuration.h index 238b769017..153c5979f7 100644 --- a/engine/src/damage/facet_configuration.h +++ b/engine/src/damage/facet_configuration.h @@ -39,6 +39,7 @@ * That is x,y,z. This is also how both core vector and Vector class are ordered. */ enum class FacetConfiguration { + zero = 0, // Armor or shield not installed one = 1, // A single facet covering all directions two = 2, // Front and rear four = 4, // Front, rear, left and right diff --git a/engine/src/damage/health.cpp b/engine/src/damage/health.cpp index e1398576cf..4e0798efa1 100644 --- a/engine/src/damage/health.cpp +++ b/engine/src/damage/health.cpp @@ -29,19 +29,15 @@ #include #include -void Health::AdjustPower(const double &percent) { - if (!regenerative) { - // Not applicable for armor and hull - return; - } - if (percent > 1 || percent < 0) { - // valid values are between 0 and 1 - return; - } - - power = percent; -} +Health::Health(int layer, double health, double regeneration) : + layer(layer), + health(health, 0, health) { + destroyed = false; + + vulnerabilities.normal_damage = 1; + vulnerabilities.phase_damage = 1; + }; void Health::DealDamage(Damage &damage, InflictedDamage &inflicted_damage) { // If this layer is destroyed, it can no longer sustain damage @@ -64,7 +60,7 @@ void Health::DealDamage(Damage &damage, InflictedDamage &inflicted_damage) { * @param vulnerability - adjust for */ // TODO: type is ugly hack -void Health::DealDamageComponent(int type, double &damage, float vulnerability, InflictedDamage &inflicted_damage) { +void Health::DealDamageComponent(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 @@ -88,7 +84,8 @@ void Health::DealDamageComponent(int type, double &damage, float vulnerability, inflicted_damage.total_damage += adjusted_damage; inflicted_damage.inflicted_damage_by_layer.at(layer) += adjusted_damage; - if (health == 0 && !regenerative) { + // A bit hardcoded + if (layer < 2) { destroyed = true; } } @@ -98,67 +95,7 @@ void Health::Destroy() { destroyed = true; } -/* This is a bit kludgy. Set power via keyboard only works when not suppressed. -* If ship is in SPEC, power will be continuously set to 0. -* Therefore, if you set power to 1/3, go to SPEC and out again, power will be -* set to full again. -*/ -void Health::SetPower(const double power) { - if (regenerative) { - this->power = power; - } -} - -/** Enhance adds some oomph to shields. - * Originally, I thought to just make them 150% one time. - * However, this isn't really significant and it's hard to implement - * with the underlying Resource class, which checks for max values. - * Instead, this will upgrade the Max value of shields and repair them. - */ -void Health::Enhance(double percent) { - // Don't enhance armor and hull - if (!regenerative) { - return; - } - - // Sanity checks. Don't want to use enhance to downgrade - // and more than x100 is excessive. - if(percent < 1.0 || percent > 100.0) { - return; - } - - health.SetMaxValue(health.MaxValue() * percent); - regeneration.SetMaxValue(regeneration.MaxValue() * percent); -} - - -void Health::Regenerate() { - if (!regenerative) { - return; - } - - if(health.Percent() < power) { - health++; - } else if(health.Percent() > power) { - health--; - } -} - -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 new_health) { - this->health.SetMaxValue(new_health); -} +void Health::SetHealth(double new_health) { + this->health.Set(new_health); +} \ No newline at end of file diff --git a/engine/src/damage/health.h b/engine/src/damage/health.h index a9123e4dd4..228938b22c 100644 --- a/engine/src/damage/health.h +++ b/engine/src/damage/health.h @@ -44,20 +44,11 @@ struct Health { friend class Shield; public: - int layer; // The layer we're in, for recording damage Resource health; - Resource regeneration; - double power; // 1.0 Full, 0.66 Two thirds, 0.0 Suppressed (FTL) or turned off - - bool regenerative; bool destroyed; - Damage vulnerabilities; - // TODO: implement "shield leaks" - - public: /** @@ -72,37 +63,22 @@ struct Health { } effect{}; - Health(int layer, float health = 1, float regeneration = 0) : - layer(layer), - health(health, 0, health), - regeneration(regeneration, 0, regeneration), - regenerative(regeneration > 0) { - power = 1.0; // Only relevant for regenerative objects (e.g. shields). - - destroyed = false; - if (layer == 0) { - regenerative = false; - } - - vulnerabilities.normal_damage = 1; - vulnerabilities.phase_damage = 1; - }; + Health(int layer, double health = 1, double regeneration = 0); - float Percent() const { + double Percent() const { return health.Percent(); } - void AdjustPower(const double &percent); - void AdjustPercentage(); + void DealDamage(Damage &damage, InflictedDamage &inflicted_damage); - void DealDamageComponent(int type, double &damage, float vulnerability, InflictedDamage &inflicted_damage); void Destroy(); - void SetPower(const double power); - void Enhance(double percent = 1.5f); - void Regenerate(); - void Regenerate(float recharge_rate); - void SetHealth(float health); - void Update(float new_health); + void SetHealth(double new_health); + +private: + void DealDamageComponent(int type, double &damage, + double vulnerability, + InflictedDamage &inflicted_damage); + }; #endif //VEGA_STRIKE_ENGINE_DAMAGE_HEALTH_H diff --git a/engine/src/gfx/cockpit_generic.cpp b/engine/src/gfx/cockpit_generic.cpp index 8d27504da5..2eeceb4c02 100644 --- a/engine/src/gfx/cockpit_generic.cpp +++ b/engine/src/gfx/cockpit_generic.cpp @@ -545,7 +545,7 @@ bool Cockpit::Update() { // TODO: lib_damage // check the input is in the expected 0 to 1 values - par->GetShieldLayer().AdjustPower(minEnergyShieldPercent); + par->shield_component.SetPowerCap(minEnergyShieldPercent); } } else { secondsWithZeroEnergy = 0; diff --git a/engine/src/resource/resource.cpp b/engine/src/resource/resource.cpp index fb5cac318d..67c1226b7f 100644 --- a/engine/src/resource/resource.cpp +++ b/engine/src/resource/resource.cpp @@ -90,7 +90,16 @@ void Resource::SetMaxValue(const T &value) { value_ = adjusted_max_value_ = max_value_ = value; } +template +void Resource::SetAdjustedMaxValue(const T &value) { + if(no_max_) { // Can't set max if there's no max + return; + } + // Unhandled edge case - value < min value + adjusted_max_value_ = std::min(value_, max_value_); + this->value_ = std::min(this->value_, adjusted_max_value_); +} template T Resource::Value() const { diff --git a/engine/src/resource/resource.h b/engine/src/resource/resource.h index 4ac247b44b..5f812a5250 100644 --- a/engine/src/resource/resource.h +++ b/engine/src/resource/resource.h @@ -91,6 +91,7 @@ class Resource { void Set(const T &value); void SetToMax(); void SetMaxValue(const T &value); + void SetAdjustedMaxValue(const T &value); void Zero(); diff --git a/engine/src/star_system.cpp b/engine/src/star_system.cpp index ee04faf49d..1fcba4aae1 100644 --- a/engine/src/star_system.cpp +++ b/engine/src/star_system.cpp @@ -1280,7 +1280,7 @@ void StarSystem::ProcessPendingJumps() { } if (game_options()->jump_disables_shields) { // Disable and then enable so they'll start recharging - un->shield->DischargeShields(); + un->shield_component.Discharge(); } } } From a4a8a7d0356e8b3420cf8b266d1662433f092017 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Tue, 26 Mar 2024 07:38:28 +0200 Subject: [PATCH 11/16] Further work on Armor class Add armor tests Refactor Components::Load to remove dependency on Unit Fix bug in Resource class Fix bug in Hull and implement some missing code Refactor UnitCsvFactory to remove dependencies --- engine/CMakeLists.txt | 1 + engine/src/cmd/unit_csv_factory.cpp | 10 +- engine/src/cmd/unit_csv_factory.h | 5 +- engine/src/cmd/unit_generic.cpp | 3 +- engine/src/components/armor.cpp | 33 +++--- engine/src/components/armor.h | 14 ++- engine/src/components/component.cpp | 7 +- engine/src/components/component.h | 7 +- engine/src/components/hull.cpp | 5 +- engine/src/components/tests/armor_tests.cpp | 124 ++++++++++++++++++++ engine/src/resource/resource.cpp | 2 +- engine/src/universe.cpp | 3 +- 12 files changed, 181 insertions(+), 33 deletions(-) create mode 100644 engine/src/components/tests/armor_tests.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 56c20176ea..ce21acf824 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -1643,6 +1643,7 @@ IF (USE_GTEST) src/exit_unit_tests.cpp src/components/tests/energy_container_tests.cpp src/components/tests/balancing_tests.cpp + src/components/tests/armor_tests.cpp ) ADD_LIBRARY(vegastrike-testing diff --git a/engine/src/cmd/unit_csv_factory.cpp b/engine/src/cmd/unit_csv_factory.cpp index 03be6f4b4d..0f0951c393 100644 --- a/engine/src/cmd/unit_csv_factory.cpp +++ b/engine/src/cmd/unit_csv_factory.cpp @@ -93,9 +93,8 @@ std::vector ProcessLine(std::string &line) { return cells; } -void UnitCSVFactory::ParseCSV(VSFileSystem::VSFile &file, bool saved_game) { +void UnitCSVFactory::ParseCSV(std::string data, std::string root, bool saved_game) { std::vector columns; - std::string data = file.ReadFull(); std::string delimiter = "\n"; size_t pos = 0; std::string token; @@ -124,7 +123,7 @@ void UnitCSVFactory::ParseCSV(VSFileSystem::VSFile &file, bool saved_game) { } // Add root - unit_attributes["root"] = file.GetRoot(); + unit_attributes["root"] = root; std::string key = (saved_game ? "player_ship" : line[0]); @@ -151,3 +150,8 @@ std::string GetUnitKeyFromNameAndFaction(const std::string unit_name, const std: return std::string(); } + +void UnitCSVFactory::LoadUnit(std::string key, + std::map unit_map) { + UnitCSVFactory::units[key] = unit_map; +} \ No newline at end of file diff --git a/engine/src/cmd/unit_csv_factory.h b/engine/src/cmd/unit_csv_factory.h index 7f05bc334d..62c0e46dee 100644 --- a/engine/src/cmd/unit_csv_factory.h +++ b/engine/src/cmd/unit_csv_factory.h @@ -104,7 +104,7 @@ class UnitCSVFactory { friend class UnitJSONFactory; friend class UnitOptimizeFactory; public: - static void ParseCSV(VSFileSystem::VSFile &file, bool saved_game); + static void ParseCSV(std::string data, std::string root, bool saved_game); template static inline T GetVariable(std::string unit_key, std::string const &attribute_key, T default_value) = delete; @@ -126,6 +126,9 @@ class UnitCSVFactory { static std::map GetUnit(std::string key) { return UnitCSVFactory::units[key]; } + + static void LoadUnit(std::string key, + std::map unit_map); }; // Template Specialization diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index fe9729d167..46cca386cb 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -448,7 +448,8 @@ void Unit::Init(const char *filename, VSFile unitTab; VSError taberr = unitTab.OpenReadOnly(filepath + ".csv", UnitSaveFile); if (taberr <= Ok) { - UnitCSVFactory::ParseCSV(unitTab, true); + std::string data = unitTab.ReadFull(); + UnitCSVFactory::ParseCSV(data, unitTab.GetRoot(), true); unitTab.Close(); saved_game = true; } diff --git a/engine/src/components/armor.cpp b/engine/src/components/armor.cpp index bdd5344ce3..90ad82c9ae 100644 --- a/engine/src/components/armor.cpp +++ b/engine/src/components/armor.cpp @@ -29,7 +29,6 @@ #include "damageable_layer.h" #include "unit_csv_factory.h" -#include "unit_generic.h" const std::string armor_facets[] = { "Moment_Of_Inertia", @@ -44,12 +43,11 @@ const std::string armor_facets[] = { }; Armor::Armor(DamageableLayer* armor_layer_): Component("", 0.0, 0.0, false), - armor_layer_(armor_layer_) {} + armor_(armor_layer_) {} -void Armor::Load(std::string upgrade_key, std::string unit_key, - Unit *unit) { +void Armor::Load(std::string upgrade_key, std::string unit_key) { // Component - Component::Load(upgrade_key, unit_key, unit); + Component::Load(upgrade_key, unit_key); // Damageable Layer @@ -57,19 +55,26 @@ void Armor::Load(std::string upgrade_key, std::string unit_key, std::vector armor_values; + // Max values from Upgrade + for(int i = 0;i < 8;i++) { + double armor_facet_max = UnitCSVFactory::GetVariable(upgrade_key, armor_facets[i], 0.0); + armor_values.push_back(armor_facet_max); + } + + armor_->UpdateFacets(armor_values); + + // Current values from Save Game for(int i = 0;i < 8;i++) { - // TODO: implement - //double armor_facet_max = UnitCSVFactory::GetVariable(upgrade_key, armor_facets[i], 0.0); double armor_value = UnitCSVFactory::GetVariable(unit_key, armor_facets[i], 0.0); - armor_values.push_back(armor_value); + armor_->facets[i].health.Set(armor_value); } - - armor_layer_->UpdateFacets(armor_values); } -std::string Armor::SaveToJSON() const { - return std::string(); +void Armor::SaveToCSV(std::map& unit) const { + for(int i=0;i<8;i++) { + unit[armor_facets[i]] = std::to_string(armor_->facets[i].health.Value()); + } } std::string Armor::Describe() const { @@ -96,7 +101,7 @@ bool Armor::Downgrade() { //mass = 0; // volume = 0; for(int i = 0;i < 8;i++) { - armor_layer_->facets[i].health.SetMaxValue(0.0); + armor_->facets[i].health.SetMaxValue(0.0); } return true; @@ -133,7 +138,7 @@ bool Armor::Upgrade(const std::string upgrade_name) { armor_values.push_back(armor_value); } - armor_layer_->UpdateFacets(armor_values); + armor_->UpdateFacets(armor_values); return true; } diff --git a/engine/src/components/armor.h b/engine/src/components/armor.h index 41abd0fc45..6078b624de 100644 --- a/engine/src/components/armor.h +++ b/engine/src/components/armor.h @@ -32,9 +32,12 @@ #include "damage/damageable_layer.h" #include +#include class Unit; +extern const std::string armor_facets[]; + // TODO: make armor plating a multiplier. // e.g. light fighter has armor_area[8] // Without armor, the array is all zero. @@ -42,16 +45,15 @@ class Unit; // With better armor material, you multiply by more. class Armor : public Component { - DamageableLayer* armor_layer_; + DamageableLayer* armor_; friend class Unit; public: - Armor(DamageableLayer* armor_layer_); + Armor(DamageableLayer* armor_); - virtual void Load(std::string upgrade_key, std::string unit_key, - Unit *unit); // Load from dictionary - virtual std::string SaveToJSON() const; // Save component as JSON - + // Load from dictionary + virtual void Load(std::string upgrade_key, std::string unit_key); + virtual void SaveToCSV(std::map& unit) const; virtual std::string Describe() const; // Describe component in base_computer virtual bool CanDowngrade() const; diff --git a/engine/src/components/component.cpp b/engine/src/components/component.cpp index 9bd3d1765e..3645e983c4 100644 --- a/engine/src/components/component.cpp +++ b/engine/src/components/component.cpp @@ -35,8 +35,7 @@ Component::Component(std::string upgrade_name, double mass, double volume, integral(integral) {} -void Component::Load(std::string upgrade_key, std::string unit_key, - Unit *unit) { +void Component::Load(std::string upgrade_key, std::string unit_key) { upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); this->upgrade_key = upgrade_key; @@ -69,4 +68,8 @@ bool Component::Downgrade() { mass = 0.0; volume = 0.0; +} + +void Component::SetIntegral(bool integral) { + this->integral = integral; } \ No newline at end of file diff --git a/engine/src/components/component.h b/engine/src/components/component.h index 65f754b9b9..a9f15a2d0c 100644 --- a/engine/src/components/component.h +++ b/engine/src/components/component.h @@ -60,8 +60,9 @@ class Component Component(std::string upgrade_name, double mass, double volume, bool integral); - virtual void Load(std::string upgrade_key, std::string unit_key, - Unit *unit); // Load from dictionary + // Load from units dictionary + virtual void Load(std::string upgrade_key, std::string unit_key); + //virtual std::string SaveToJSON() const = 0; // Save component as JSON virtual std::string Describe() const = 0; // Describe component in base_computer @@ -83,5 +84,7 @@ class Component virtual bool Damaged() const = 0; virtual bool Installed() const = 0; + + void SetIntegral(bool integral); }; #endif // COMPONENT_H diff --git a/engine/src/components/hull.cpp b/engine/src/components/hull.cpp index 10df66af60..2121987b2b 100644 --- a/engine/src/components/hull.cpp +++ b/engine/src/components/hull.cpp @@ -45,6 +45,7 @@ void Hull::Load(std::string upgrade_key, std::string unit_key, double hull_current = UnitCSVFactory::GetVariable(unit_key, "hull", 1.0); double hull_max = UnitCSVFactory::GetVariable(upgrade_key, "hull", 1.0); hull_->facets[0].health.SetMaxValue(hull_max); + hull_->facets[0].health.Set(hull_current); } std::string Hull::SaveToJSON() const { @@ -77,11 +78,11 @@ bool Hull::Upgrade(const std::string upgrade_name) { void Hull::Damage() {} void Hull::Repair() { - //hull_->facets[0].adjusted_health = hull_->facets[0].max_health; + hull_->facets[0].health.SetToMax(); } bool Hull::Damaged() const { - return true;//hull_->facets[0].health < hull_->facets[0].max_health; + return hull_->facets[0].health.Damaged(); } diff --git a/engine/src/components/tests/armor_tests.cpp b/engine/src/components/tests/armor_tests.cpp new file mode 100644 index 0000000000..79fc324ff5 --- /dev/null +++ b/engine/src/components/tests/armor_tests.cpp @@ -0,0 +1,124 @@ +#include + +#include "armor.h" +#include "damage/damageable_layer.h" +#include "damage/health.h" +#include "damage/facet_configuration.h" +#include "unit_csv_factory.h" + +#include +#include + +const std::string DUMMY_ARMOR_KEY = "dummy_armor"; +const std::string DUMMY_UNIT_KEY = "dummy_unit"; +const std::string NAME = "Name"; +const std::string MASS = "Mass"; + + +// Utilities + +/* Discussion: use of macros +* Since we are using both DamageableLayer and Armor and one has + a pointer to the other, creating both in a function is somewhat + diffcult. + One option is pointers. + Another is macros. + However, returning std::pair is asking for + trouble, as there is potentially a copy operation and the original + DL object will go out of scope. +*/ +#define CREATE_ARMOR \ + Health health = Health(1,100.0,0.0); \ + DamageableLayer layer(1, FacetConfiguration::eight, health, false); \ + Armor armor = Armor(&layer); \ + + +void loadArmor(Armor &armor, double health, double max_health) { + // "Ship" + std::map dummy_unit_map; + for(int i=0;i<8;i++) { + dummy_unit_map[armor_facets[i]] = std::to_string(health); + } + UnitCSVFactory::LoadUnit(DUMMY_UNIT_KEY, dummy_unit_map); + + // "Armor" + std::map dummy_armor_map; + dummy_armor_map[NAME] = DUMMY_ARMOR_KEY; + dummy_armor_map[MASS] = 10.0; + + for(int i=0;i<8;i++) { + dummy_armor_map[armor_facets[i]] = std::to_string(max_health); + } + UnitCSVFactory::LoadUnit(DUMMY_ARMOR_KEY, dummy_armor_map); + + armor.Load(DUMMY_ARMOR_KEY, DUMMY_UNIT_KEY); +} + +void testArmorValues(DamageableLayer &layer, double health, double max_health) { + for(int i=0;i<8;i++) { + EXPECT_EQ(layer.facets[i].health.Value(), health); + EXPECT_EQ(layer.facets[i].health.MaxValue(), max_health); + } +} + + +// Tests +TEST(Armor, Load) { + CREATE_ARMOR + + loadArmor(armor, 25, 50); + + testArmorValues(layer, 25, 50); +} + +TEST(Armor, Save) { + Health health = Health(1,100.0,0.0); + DamageableLayer layer(1, FacetConfiguration::eight, health, false); + Armor armor = Armor(&layer); + + std::map csv; + armor.SaveToCSV(csv); + + for(int i=0;i<8;i++) { + EXPECT_EQ(std::stod(csv[armor_facets[i]]), 100); + } +} + +TEST(Armor, CanDowngrade) { + CREATE_ARMOR + loadArmor(armor, 25, 50); + + armor.SetIntegral(false); + EXPECT_TRUE(armor.CanDowngrade()); + + armor.SetIntegral(true); + EXPECT_FALSE(armor.CanDowngrade()); +} + +TEST(Armor, Downgrade) { + CREATE_ARMOR + loadArmor(armor, 25, 50); + + EXPECT_TRUE(armor.Downgrade()); + + testArmorValues(layer, 0, 0); +} + +TEST(Armor, CanUpgrade) { + CREATE_ARMOR + loadArmor(armor, 25, 50); + + EXPECT_TRUE(armor.Downgrade()); + EXPECT_TRUE(armor.CanUpgrade(DUMMY_ARMOR_KEY)); +} + +TEST(Armor, Upgrade) { + CREATE_ARMOR + loadArmor(armor, 25, 50); + + EXPECT_TRUE(armor.Downgrade()); + EXPECT_TRUE(armor.Upgrade(DUMMY_ARMOR_KEY)); + testArmorValues(layer, 50, 50); +} + +#undef CREATE_ARMOR \ No newline at end of file diff --git a/engine/src/resource/resource.cpp b/engine/src/resource/resource.cpp index 67c1226b7f..c7f8b845ad 100644 --- a/engine/src/resource/resource.cpp +++ b/engine/src/resource/resource.cpp @@ -78,7 +78,7 @@ void Resource::SetToMax() { return; } - value_ = max_value_; + value_ = adjusted_max_value_ = max_value_; } template diff --git a/engine/src/universe.cpp b/engine/src/universe.cpp index ceb935368d..c753a1a8bb 100644 --- a/engine/src/universe.cpp +++ b/engine/src/universe.cpp @@ -287,7 +287,8 @@ void InitUnitTables() { VSFileSystem::VSFile csvFile; VSFileSystem::VSError err = csvFile.OpenReadOnly("units.csv", VSFileSystem::UnitFile); if (err <= VSFileSystem::Ok) { - UnitCSVFactory::ParseCSV(csvFile, true); + std::string data = csvFile.ReadFull(); + UnitCSVFactory::ParseCSV(data, csvFile.GetRoot(), true); } else { std::cerr << "Unable to open units file. Aborting.\n"; abort(); From ab8538ad1d0bb3c6b4d89017a83a497a09b6f932 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Wed, 27 Mar 2024 12:04:00 +0200 Subject: [PATCH 12/16] Make Shield a super class of DamageableLayer Fix issue in DL::GetPercent. Not sure why it wasn't caught by the compiler before. Note: hull upgrade was disabled --- engine/src/cmd/ai/firekeyboard.cpp | 2 +- engine/src/cmd/basecomputer.cpp | 12 ++--- engine/src/cmd/collision.cpp | 2 +- engine/src/cmd/damageable.cpp | 69 +++++++++++++------------ engine/src/cmd/damageable.h | 24 ++++----- engine/src/cmd/unit_csv.cpp | 50 ++---------------- engine/src/cmd/unit_generic.cpp | 8 +-- engine/src/cmd/upgradeable_unit.cpp | 2 +- engine/src/components/shield.cpp | 40 +++++++------- engine/src/components/shield.h | 6 +-- engine/src/damage/damageable_layer.cpp | 11 ++-- engine/src/damage/damageable_layer.h | 2 +- engine/src/damage/damageable_object.cpp | 26 +++------- engine/src/damage/damageable_object.h | 6 +-- engine/src/gfx/cockpit_generic.cpp | 2 +- engine/src/star_system.cpp | 2 +- 16 files changed, 102 insertions(+), 162 deletions(-) diff --git a/engine/src/cmd/ai/firekeyboard.cpp b/engine/src/cmd/ai/firekeyboard.cpp index bcb41b208b..aa68350374 100644 --- a/engine/src/cmd/ai/firekeyboard.cpp +++ b/engine/src/cmd/ai/firekeyboard.cpp @@ -1731,7 +1731,7 @@ void FireKeyboard::Execute() { float f_result = f().shieldpowerstate; if (f_result != 1) { - parent->shield_component.SetPower(f_result); + parent->shield->SetPower(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 f0ba00ac60..f4decf6a04 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -87,10 +87,8 @@ extern const char *DamagedCategory; // TODO: find a better home for this function // Basically max or current shield x 0.2 float totalShieldEnergyCapacitance(Unit *unit) { - DamageableLayer *shield = unit->shield; - - float total_max_shield_value = shield->TotalMaxLayerValue(); - float total_current_shield_value = shield->TotalLayerValue(); + float total_max_shield_value = unit->shield->TotalMaxLayerValue(); + float total_current_shield_value = unit->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); } @@ -1898,7 +1896,7 @@ bool BaseComputer::configureUpgradeCommitControls(const Cargo &item, Transaction // New component code if(GetUpgradeType(item.GetName() + UPGRADES_SUFFIX) == UpgradeType::Shield) { - CanDoSell = (!player->shield_component.Damaged()) || !must_fix_first; + CanDoSell = (!player->shield->Damaged()) || !must_fix_first; } if (CanDoSell) { @@ -5564,7 +5562,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } - const double regeneration = playerUnit->shield_component.GetRegeneration(); + const double regeneration = playerUnit->shield->GetRegeneration(); if (!mode) { PRETTY_ADDU(statcolor + "Shield protection recharge speed: #-c", regeneration * VSDM, 0, "MJ/s"); } else if (replacement_mode != 0 || regeneration) { @@ -5773,7 +5771,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C float avail = (playerUnit->energy_manager.GetReactorCapacity() * RSconverter - maxshield * VSDM); int num_shields = playerUnit->shield->number_of_facets; - float regeneration = playerUnit->shield_component.GetRegeneration(); + float regeneration = playerUnit->shield->GetRegeneration(); float overhead = (shields_require_power) ? (regeneration / shieldenergycap * shield_maintenance_cost * num_shields * VSDM) : 0; diff --git a/engine/src/cmd/collision.cpp b/engine/src/cmd/collision.cpp index b6c6e76647..fec749cd64 100644 --- a/engine/src/cmd/collision.cpp +++ b/engine/src/cmd/collision.cpp @@ -126,7 +126,7 @@ void Collision::shouldApplyForceAndDealDamage(Unit *other_unit) { // TODO: someone from the "product" team needs to define the // exact behavior. Preferably after we sort the upgrade // code. - other_unit->shield_component.Enhance(); + 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 b2c2e2e495..0ebbba9b82 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -44,21 +44,24 @@ #include "configuration/configuration.h" Damageable::Damageable() : DamageableObject(), - hull(&(layers[0])), - armor(&(layers[1])), - shield(&(layers[2])), - current_hull(hull->facets[0].health.ValuePtr()), - max_hull(hull->facets[0].health.MaxValuePtr()), - upgrade_hull(0), - shield_regeneration(0), - killed(false), - shield_component(shield) { - if(shield) { - shield_component.shield_ = shield; - } else { - assert(0); - } - + killed(false), + shield() { + // Damageable Constructor + Health hull_health = Health(1, 1, 0); + Health armor_health = Health(0, 0, 0); + + hull_ = DamageableLayer(0, FacetConfiguration::one, hull_health, true); + armor_ = DamageableLayer(1, FacetConfiguration::eight, armor_health, false); + + hull = &hull_; + armor = &armor_; + shield = &shield_; + + layers = {hull, armor, shield}; + number_of_layers = 3; + + current_hull = hull->facets[0].health.ValuePtr(); + max_hull = hull->facets[0].health.MaxValuePtr(); } bool Damageable::ShieldUp(const Vector &pnt) const { @@ -96,8 +99,8 @@ float Damageable::DealDamageToShield(const Vector &pnt, float &damage) { 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); + shield_.DealDamage(attack_vector, dmg, inflicted_damage); + int facet_index = shield_.GetFacetIndex(attack_vector); float denominator = GetShield(facet_index) + GetHull(); if (denominator == 0) { @@ -490,44 +493,44 @@ void Damageable::Destroy() { float Damageable::FShieldData() const { - switch (shield->number_of_facets) { + switch (shield_.number_of_facets) { case 2: - return shield->facets[0].Percent(); + return shield_.facets[0].Percent(); case 4: - return shield->facets[2].Percent(); + return shield_.facets[2].Percent(); default: return 0.0f; // We only support 2 and 4 facet shields ATM } } float Damageable::BShieldData() const { - switch (shield->number_of_facets) { + switch (shield_.number_of_facets) { case 2: - return shield->facets[1].Percent(); + return shield_.facets[1].Percent(); case 4: - return shield->facets[3].Percent(); + return shield_.facets[3].Percent(); default: return 0.0f; // We only support 2 and 4 facet shields ATM } } float Damageable::LShieldData() const { - switch (shield->number_of_facets) { + switch (shield_.number_of_facets) { case 2: return 0.0f; case 4: - return shield->facets[0].Percent(); + return shield_.facets[0].Percent(); default: return 0.0f; // We only support 2 and 4 facet shields ATM } } float Damageable::RShieldData() const { - switch (shield->number_of_facets) { + switch (shield_.number_of_facets) { case 2: return 0.0f; case 4: - return shield->facets[1].Percent(); + return shield_.facets[1].Percent(); default: return 0.0f; // We only support 2 and 4 facet shields ATM } @@ -558,8 +561,8 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi Unit *unit = static_cast(this); const bool in_warp = unit->graphicOptions.InWarp; - const int shield_facets = unit->shield->number_of_facets; - const float total_max_shields = unit->shield->TotalMaxLayerValue(); + const int shield_facets = shield_.number_of_facets; + const float total_max_shields = shield_.TotalMaxLayerValue(); // No point in all this code if there are no shields. if (shield_facets < 2 || total_max_shields == 0) { @@ -570,7 +573,7 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi // Here we store the actual charge we'll use in RegenShields // TODO: fix this. It's a hack just to build... - double max_shield_recharge = unit->shield_component.GetRegeneration(); + double max_shield_recharge = shield_.GetRegeneration(); double actual_recharge = max_shield_recharge * energy->Powered(EnergyConsumerClassification::ShieldRegen); @@ -590,14 +593,14 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi // Discharge shields due to energy or SPEC or cloak if ((in_warp && !shields_in_spec) || unit->cloak.Active()) { // "Damage" power - shield_component.SetPowerCap(0.0); + shield_.SetPowerCap(0.0); } else { // Figure out how to support partial power - shield_component.SetPowerCap(1.0); + shield_.SetPowerCap(1.0); } // Shield regeneration - shield_component.Regenerate(); // TODO: shield_recharge); + shield_.Regenerate(); // TODO: shield_recharge); } float Damageable::MaxShieldVal() const { diff --git a/engine/src/cmd/damageable.h b/engine/src/cmd/damageable.h index 01fcbcb5b4..c0936e351c 100644 --- a/engine/src/cmd/damageable.h +++ b/engine/src/cmd/damageable.h @@ -39,24 +39,20 @@ struct GFXColor; * @brief The Damageable class TODO */ class Damageable : public DamageableObject { + DamageableLayer hull_; + DamageableLayer armor_; + Shield shield_; + friend class UpgradeableUnit; public: - Shield shield_component; - DamageableLayer *hull; DamageableLayer *armor; - DamageableLayer *shield; + Shield *shield; double *current_hull; double *max_hull; - // These are only used for upgrade due to macros - // TODO: refactor - float upgrade_hull; - - float shield_regeneration; - //Is dead already? bool killed; @@ -86,19 +82,19 @@ class Damageable : public DamageableObject { } const float GetShield(int facet = 0) const { - return shield->facets[facet].health; + return shield_.facets[facet].health.Value(); } DamageableLayer &GetHullLayer() { - return layers[0]; + return hull_; } DamageableLayer &GetArmorLayer() { - return layers[1]; + return armor_; } DamageableLayer &GetShieldLayer() { - return layers[2]; + return shield_; } virtual const float GetHullPercent() const { @@ -106,7 +102,7 @@ class Damageable : public DamageableObject { } virtual const float GetShieldPercent() const { - return shield->GetPercent(FacetName::left_top_front); + return shield_.GetPercent(FacetName::left_top_front); } void ArmorData(float armor[8]) const; diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index 1805e9b6e6..da9ce6e707 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -766,7 +766,7 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ // Load shield - shield_component.Load("", unit_key, this); + shield->Load("", unit_key, this); // End shield section @@ -1202,52 +1202,8 @@ string Unit::WriteUnitString() { unit["Armor_Back_Bottom_Left"] = tos(GetArmorLayer().facets[5].health); unit["Armor_Back_Bottom_Right"] = tos(GetArmorLayer().facets[7].health); - int number_of_shield_emitters = shield->number_of_facets; - shield_component.SaveToCSV(unit); - /*{ - unit["Shield_Front_Top_Right"] = ""; - unit["Shield_Front_Top_Left"] = ""; - unit["Shield_Back_Top_Right"] = ""; - unit["Shield_Back_Top_Left"] = ""; - unit["Shield_Front_Bottom_Right"] = ""; - unit["Shield_Front_Bottom_Left"] = ""; - unit["Shield_Back_Bottom_Right"] = ""; - unit["Shield_Back_Bottom_Left"] = ""; - - switch (number_of_shield_emitters) { - case 8: - unit["Shield_Front_Top_Left"] = tos(GetShieldLayer().facets[0].health.MaxValue()); - unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[1].health.MaxValue()); - unit["Shield_Front_Bottom_Left"] = tos(GetShieldLayer().facets[2].health.MaxValue()); - unit["Shield_Front_Bottom_Right"] = tos(GetShieldLayer().facets[3].health.MaxValue()); - unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[4].health.MaxValue()); - unit["Shield_Back_Top_Right"] = tos(GetShieldLayer().facets[5].health.MaxValue()); - unit["Shield_Back_Bottom_Left"] = tos(GetShieldLayer().facets[6].health.MaxValue()); - unit["Shield_Back_Bottom_Right"] = tos(GetShieldLayer().facets[7].health.MaxValue()); - - break; - case 4: - unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[0].health.MaxValue()); - unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[1].health.MaxValue()); - unit["Shield_Front_Bottom_Right"] = tos(GetShieldLayer().facets[2].health.MaxValue()); - unit["Shield_Front_Bottom_Left"] = tos(GetShieldLayer().facets[3].health.MaxValue()); - - break; - case 2: - unit["Shield_Front_Top_Right"] = tos(GetShieldLayer().facets[0].health.MaxValue()); - unit["Shield_Back_Top_Left"] = tos(GetShieldLayer().facets[1].health.MaxValue()); - break; - - case 0: - // No shields - break; - - default: - // This should not happen - std::cout << number_of_shield_emitters << "\n"; - assert(0); - } - }*/ + shield->SaveToCSV(unit); + diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 46cca386cb..8ab3e8fbdf 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -1278,7 +1278,7 @@ void Unit::DamageRandSys(float dam, const Vector &vec, float randnum, float degr } // TODO: lib_damage reenable - shield_component.Damage(); + shield->Damage(); damages |= Damages::SHIELD_DAMAGED; return; @@ -3027,9 +3027,9 @@ bool Unit::UpAndDownGrade(const Unit *up, }*/ // Upgrade hull health - upgrade_hull = *current_hull; + //TODO: upgrade_hull = *current_hull; - if (up && up->current_hull) { + /*if (up && up->current_hull) { const_cast(up)->upgrade_hull = *up->current_hull; } @@ -3039,7 +3039,7 @@ bool Unit::UpAndDownGrade(const Unit *up, if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Hull")) { STDUPGRADE(upgrade_hull, up->upgrade_hull, templ->upgrade_hull, 0); - } + }*/ /*if ((hull->facets[0].max_health < hull->facets[0].health) && (!Destroyed())) { hull->facets[0].max_health = hull->facets[0].health; diff --git a/engine/src/cmd/upgradeable_unit.cpp b/engine/src/cmd/upgradeable_unit.cpp index ec7cd5d664..bb04c04f86 100644 --- a/engine/src/cmd/upgradeable_unit.cpp +++ b/engine/src/cmd/upgradeable_unit.cpp @@ -121,7 +121,7 @@ UpgradeOperationResult UpgradeableUnit::UpgradeUnit(const std::string upgrade_na // break; case UpgradeType::Shield: result.upgradeable = true; - result.success = unit->shield_component.CanWillUpDowngrade(upgrade_key, upgrade, apply); + result.success = unit->shield->CanWillUpDowngrade(upgrade_key, upgrade, apply); break; default: //std::cout << "Unhandled type for " << upgrade_name << std::endl; diff --git a/engine/src/components/shield.cpp b/engine/src/components/shield.cpp index 01112c687b..bb47b55e37 100644 --- a/engine/src/components/shield.cpp +++ b/engine/src/components/shield.cpp @@ -60,11 +60,12 @@ std::string shield_facets_two[2] = { // Note that we need to define FacetConfiguration during load -Shield::Shield(DamageableLayer* shield_): Component("", 0.0, 0.0, false), - shield_(shield_), - regeneration(0,0,0), - power(1.0,0.0,1.0) - {} +Shield::Shield(): + Component("", 0.0, 0.0, false), + DamageableLayer(2, FacetConfiguration::zero, + Health(2, 0, 0), false), + regeneration(0,0,0), + power(1.0,0.0,1.0) {} @@ -111,8 +112,7 @@ void Shield::Load(std::string upgrade_key, std::string unit_key, 5. We map the above index to our own */ - shield_->number_of_facets = shield_values.size(); - shield_->UpdateFacets(shield_values); + UpdateFacets(shield_values); this->regeneration.SetMaxValue(regeneration); // TODO: shield leakage & efficiency @@ -121,7 +121,7 @@ void Shield::Load(std::string upgrade_key, std::string unit_key, void Shield::SaveToCSV(std::map& unit) const { // TODO: lib_damage figure out if this is correctly assigned - int number_of_shield_emitters = shield_->number_of_facets; + int number_of_shield_emitters = number_of_facets; // TODO: This won't record damage to regeneration or shield facets unit[SHIELD_RECHARGE] = std::to_string(regeneration.MaxValue()); @@ -133,19 +133,19 @@ void Shield::SaveToCSV(std::map& unit) const { switch (number_of_shield_emitters) { case 8: for(int i=0;i<8;i++) { - unit[shield_facets_eight[i]] = std::to_string(shield_->facets[i].health.MaxValue()); + unit[shield_facets_eight[i]] = std::to_string(facets[i].health.MaxValue()); } break; case 4: for(int i=0;i<4;i++) { - unit[shield_facets_four[i]] = std::to_string(shield_->facets[i].health.MaxValue()); + unit[shield_facets_four[i]] = std::to_string(facets[i].health.MaxValue()); } break; case 2: - unit[shield_facets_two[0]] = std::to_string(shield_->facets[0].health.MaxValue()); - unit[shield_facets_two[1]] = std::to_string(shield_->facets[1].health.MaxValue()); + unit[shield_facets_two[0]] = std::to_string(facets[0].health.MaxValue()); + unit[shield_facets_two[1]] = std::to_string(facets[1].health.MaxValue()); break; case 0: @@ -184,7 +184,7 @@ bool Shield::Downgrade() { power.SetMaxValue(0.0); std::vector empty_vector; - shield_->UpdateFacets(empty_vector); + UpdateFacets(empty_vector); return false; } @@ -219,7 +219,7 @@ bool Shield::Upgrade(const std::string upgrade_key) { return false; } - shield_->UpdateFacets(shield_values); + UpdateFacets(shield_values); // TODO: shield leakage @@ -229,7 +229,7 @@ bool Shield::Upgrade(const std::string upgrade_key) { void Shield::Damage() { - for(Health& facet : shield_->facets) { + for(Health& facet : facets) { facet.health.RandomDamage(); } @@ -241,7 +241,7 @@ void Shield::Damage() { } void Shield::Repair() { - for(Health& facet : shield_->facets) { + for(Health& facet : facets) { facet.health.RepairFully(); } @@ -250,7 +250,7 @@ void Shield::Repair() { } bool Shield::Damaged() const { - for(const Health& facet : shield_->facets) { + for(const Health& facet : facets) { if(facet.health.Damaged()) { return true; } @@ -276,7 +276,7 @@ void Shield::Disable() { // Zeros out shields but can immediately start recharging // Used for things like jump effects void Shield::Discharge() { - for (Health &facet : shield_->facets) { + for (Health &facet : facets) { facet.health.Set(0.0); } } @@ -302,7 +302,7 @@ void Shield::Enhance() { // Boost shields to 150% double enhancement_factor = 1.5; - for(Health& facet : shield_->facets) { + for(Health& facet : facets) { facet.health.SetMaxValue(facet.health.MaxValue() * enhancement_factor); } @@ -334,7 +334,7 @@ double Shield::GetRegeneration() const { } void Shield::Regenerate() { - for(Health& facet : shield_->facets) { + for(Health& facet : facets) { if(facet.health.Percent() < power) { // If shield generator is damaged, regenerate less facet.health += regeneration.Value(); diff --git a/engine/src/components/shield.h b/engine/src/components/shield.h index b94b09bc9a..8ce2ca1479 100644 --- a/engine/src/components/shield.h +++ b/engine/src/components/shield.h @@ -41,9 +41,7 @@ class Unit; * DamageableLayer and Facet do not handle these at all. * Damage to the shield generator is handled here. */ -class Shield : public Component { - DamageableLayer* shield_ = nullptr; - +class Shield : public Component, public DamageableLayer { Resource regeneration; Resource power; // 1.0 Full, 0.66 Two thirds, 0.0 Suppressed (FTL) or turned off @@ -52,7 +50,7 @@ class Shield : public Component { //Resource opacity; friend class Damageable; public: - Shield(DamageableLayer* shield_); + Shield(); virtual void Load(std::string upgrade_key, std::string unit_key, Unit *unit); // Load from dictionary diff --git a/engine/src/damage/damageable_layer.cpp b/engine/src/damage/damageable_layer.cpp index 5e55d22f0c..dc5a38bb4d 100644 --- a/engine/src/damage/damageable_layer.cpp +++ b/engine/src/damage/damageable_layer.cpp @@ -215,7 +215,7 @@ double DamageableLayer::GetMaxHealth() { return facets[0].health.MaxValue(); } -double DamageableLayer::GetPercent(FacetName facet_name) { +double DamageableLayer::GetPercent(FacetName facet_name) const { if (number_of_facets == 0) { return 0.0f; } @@ -227,7 +227,7 @@ double DamageableLayer::GetPercent(FacetName facet_name) { case FacetConfiguration::one: case FacetConfiguration::two: case FacetConfiguration::four: - numerator = facets[as_integer(facet_name)].health; + numerator = facets[as_integer(facet_name)].health.Value(); denominator = facets[as_integer(facet_name)].health.MaxValue(); return numerator / denominator; @@ -271,9 +271,9 @@ double DamageableLayer::GetPercent(FacetName facet_name) { for (int i = 0; i < 4; i++) { int facet_index = indices_array[indices_index][i]; - Health &facet = facets[facet_index]; - aggregate_health += facet.health; - aggregate_max_health += facet.health.Value(); + const Health &facet = facets[facet_index]; + aggregate_health += facet.health.Value(); + aggregate_max_health += facet.health.MaxValue(); } percent = aggregate_health / aggregate_max_health; @@ -297,6 +297,7 @@ void DamageableLayer::UpdateFacets(const std::vector new_facets) { } } + switch (number_of_facets) { case 0: configuration = FacetConfiguration::zero; diff --git a/engine/src/damage/damageable_layer.h b/engine/src/damage/damageable_layer.h index 3d855a3fe4..f0c1b7cb36 100644 --- a/engine/src/damage/damageable_layer.h +++ b/engine/src/damage/damageable_layer.h @@ -75,7 +75,7 @@ struct DamageableLayer { double AverageMaxLayerValue(); double GetMaxHealth(); - double GetPercent(FacetName facet_name); + double GetPercent(FacetName facet_name) const; void UpdateFacets(const std::vector new_facets); }; diff --git a/engine/src/damage/damageable_object.cpp b/engine/src/damage/damageable_object.cpp index e8544d8606..36aa8ceac6 100644 --- a/engine/src/damage/damageable_object.cpp +++ b/engine/src/damage/damageable_object.cpp @@ -29,23 +29,13 @@ #include DamageableObject::DamageableObject() { - Health hull_health = Health(1, 1, 0); - Health armor_health = Health(0, 0, 0); - Health shield_health = Health(2, 0, 0); - - DamageableLayer hull_layer = DamageableLayer(0, FacetConfiguration::one, hull_health, true); - DamageableLayer armor_layer = DamageableLayer(1, FacetConfiguration::eight, armor_health, false); - DamageableLayer shield_layer = DamageableLayer(2, FacetConfiguration::four, shield_health, false); - - layers = {hull_layer, armor_layer, shield_layer}; - number_of_layers = 3; + number_of_layers = 0; + layers = {}; } -DamageableObject::DamageableObject(std::vector layers, - std::vector components) { +DamageableObject::DamageableObject(std::vector layers) { number_of_layers = layers.size(); this->layers = layers; - this->components = components; } /*DamageableObject::DamageableObject() @@ -58,8 +48,8 @@ InflictedDamage DamageableObject::DealDamage(const CoreVector &attack_vector, Da 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)) { + layer->DealDamage(attack_vector, damage, inflicted_damage); // TODO: handle damage to components here? // Assumed the core layer has only one facet @@ -77,8 +67,8 @@ InflictedDamage DamageableObject::DealDamage(const CoreVector &attack_vector, Da } void DamageableObject::Destroy() { - for (DamageableLayer layer : layers) { - layer.Destroy(); + for (DamageableLayer* layer : layers) { + layer->Destroy(); } } @@ -91,5 +81,5 @@ bool DamageableObject::Destroyed() { return true; } - return layers[0].facets[0].destroyed; + return layers[0]->facets[0].destroyed; } diff --git a/engine/src/damage/damageable_object.h b/engine/src/damage/damageable_object.h index 592bdc4a5d..b5b74fcaec 100644 --- a/engine/src/damage/damageable_object.h +++ b/engine/src/damage/damageable_object.h @@ -33,11 +33,9 @@ */ 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(std::vector layers); DamageableObject(); InflictedDamage DealDamage(const CoreVector &attack_vector, Damage &damage); diff --git a/engine/src/gfx/cockpit_generic.cpp b/engine/src/gfx/cockpit_generic.cpp index 2eeceb4c02..86c9a56b7e 100644 --- a/engine/src/gfx/cockpit_generic.cpp +++ b/engine/src/gfx/cockpit_generic.cpp @@ -545,7 +545,7 @@ bool Cockpit::Update() { // TODO: lib_damage // check the input is in the expected 0 to 1 values - par->shield_component.SetPowerCap(minEnergyShieldPercent); + par->shield->SetPowerCap(minEnergyShieldPercent); } } else { secondsWithZeroEnergy = 0; diff --git a/engine/src/star_system.cpp b/engine/src/star_system.cpp index 1fcba4aae1..25348d38b3 100644 --- a/engine/src/star_system.cpp +++ b/engine/src/star_system.cpp @@ -1280,7 +1280,7 @@ void StarSystem::ProcessPendingJumps() { } if (game_options()->jump_disables_shields) { // Disable and then enable so they'll start recharging - un->shield_component.Discharge(); + un->shield->Discharge(); } } } From 799a73224c2ea48b1f4376b60cbfc2cdfa63dad6 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Fri, 29 Mar 2024 10:56:04 +0300 Subject: [PATCH 13/16] Make armor class a superclass of DamageableLayer Fix issues with shields in SPEC and SetAdjustedMaxValue --- engine/src/cmd/basecomputer.cpp | 5 ++- engine/src/cmd/damageable.cpp | 7 +-- engine/src/cmd/damageable.h | 5 ++- engine/src/cmd/upgradeable_unit.cpp | 13 +++--- engine/src/components/armor.cpp | 25 +++++++---- engine/src/components/armor.h | 6 +-- engine/src/components/shield.cpp | 11 ++++- engine/src/components/shield.h | 2 + engine/src/components/tests/armor_tests.cpp | 49 +++++++++------------ engine/src/damage/damageable_layer.cpp | 10 ++--- engine/src/damage/damageable_layer.h | 10 ++--- engine/src/resource/cout_util.h | 11 +++++ engine/src/resource/resource.cpp | 11 +++-- 13 files changed, 97 insertions(+), 68 deletions(-) diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index f4decf6a04..5097699c17 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -1895,8 +1895,11 @@ bool BaseComputer::configureUpgradeCommitControls(const Cargo &item, Transaction } // New component code - if(GetUpgradeType(item.GetName() + UPGRADES_SUFFIX) == UpgradeType::Shield) { + UpgradeType upgrade_type = GetUpgradeType(item.GetName() + UPGRADES_SUFFIX); + if(upgrade_type == UpgradeType::Shield) { CanDoSell = (!player->shield->Damaged()) || !must_fix_first; + } else if (upgrade_type == UpgradeType::Armor) { + CanDoSell = (!player->armor->Damaged()) || !must_fix_first; } if (CanDoSell) { diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index 0ebbba9b82..23fc3e0840 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -45,13 +45,12 @@ Damageable::Damageable() : DamageableObject(), killed(false), + armor(), shield() { // Damageable Constructor Health hull_health = Health(1, 1, 0); - Health armor_health = Health(0, 0, 0); hull_ = DamageableLayer(0, FacetConfiguration::one, hull_health, true); - armor_ = DamageableLayer(1, FacetConfiguration::eight, armor_health, false); hull = &hull_; armor = &armor_; @@ -550,6 +549,8 @@ void Damageable::leach(float damShield, float damShieldRecharge, float damEnRech // 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; @@ -600,7 +601,7 @@ void Damageable::RegenerateShields(const float difficulty, const bool player_shi } // Shield regeneration - shield_.Regenerate(); // TODO: shield_recharge); + shield_.Regenerate(); } float Damageable::MaxShieldVal() const { diff --git a/engine/src/cmd/damageable.h b/engine/src/cmd/damageable.h index c0936e351c..ecf8ec28c1 100644 --- a/engine/src/cmd/damageable.h +++ b/engine/src/cmd/damageable.h @@ -29,6 +29,7 @@ #include "gfx/vec.h" #include "mount_size.h" +#include "components/armor.h" #include "components/shield.h" #include @@ -40,14 +41,14 @@ struct GFXColor; */ class Damageable : public DamageableObject { DamageableLayer hull_; - DamageableLayer armor_; + Armor armor_; Shield shield_; friend class UpgradeableUnit; public: DamageableLayer *hull; - DamageableLayer *armor; + Armor *armor; Shield *shield; double *current_hull; diff --git a/engine/src/cmd/upgradeable_unit.cpp b/engine/src/cmd/upgradeable_unit.cpp index bb04c04f86..c06121f97f 100644 --- a/engine/src/cmd/upgradeable_unit.cpp +++ b/engine/src/cmd/upgradeable_unit.cpp @@ -94,11 +94,12 @@ UpgradeType GetUpgradeType(const std::string upgrade_key) { if(upgrade_type_string.empty()) return UpgradeType::None; if(upgrade_type_string == "Armor") { - return UpgradeType::Armor; } //if(upgrade_type_string == "Hull") return UpgradeType::Hull; - if(upgrade_type_string == "Shield") return UpgradeType::Shield; + if(upgrade_type_string == "Shield") { + return UpgradeType::Shield; + } return UpgradeType::None; } @@ -115,10 +116,10 @@ UpgradeOperationResult UpgradeableUnit::UpgradeUnit(const std::string upgrade_na switch(upgrade_type) { - // case UpgradeType::Armor: - // result.upgradeable = true; - // result.success = unit->armor.CanWillUpDowngrade(upgrade_key, upgrade, apply); - // break; + case UpgradeType::Armor: + result.upgradeable = true; + result.success = unit->armor->CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; case UpgradeType::Shield: result.upgradeable = true; result.success = unit->shield->CanWillUpDowngrade(upgrade_key, upgrade, apply); diff --git a/engine/src/components/armor.cpp b/engine/src/components/armor.cpp index 90ad82c9ae..a969fc8ac2 100644 --- a/engine/src/components/armor.cpp +++ b/engine/src/components/armor.cpp @@ -42,8 +42,9 @@ const std::string armor_facets[] = { "Armor_Back_Bottom_Left" }; -Armor::Armor(DamageableLayer* armor_layer_): Component("", 0.0, 0.0, false), - armor_(armor_layer_) {} +Armor::Armor(): Component("", 0.0, 0.0, false), + DamageableLayer(1, FacetConfiguration::eight, + Health(0,0,0), false) {} void Armor::Load(std::string upgrade_key, std::string unit_key) { // Component @@ -61,19 +62,19 @@ void Armor::Load(std::string upgrade_key, std::string unit_key) { armor_values.push_back(armor_facet_max); } - armor_->UpdateFacets(armor_values); + UpdateFacets(armor_values); // Current values from Save Game for(int i = 0;i < 8;i++) { double armor_value = UnitCSVFactory::GetVariable(unit_key, armor_facets[i], 0.0); - armor_->facets[i].health.Set(armor_value); + facets[i].health.Set(armor_value); } } void Armor::SaveToCSV(std::map& unit) const { for(int i=0;i<8;i++) { - unit[armor_facets[i]] = std::to_string(armor_->facets[i].health.Value()); + unit[armor_facets[i]] = std::to_string(facets[i].health.Value()); } } @@ -101,7 +102,7 @@ bool Armor::Downgrade() { //mass = 0; // volume = 0; for(int i = 0;i < 8;i++) { - armor_->facets[i].health.SetMaxValue(0.0); + facets[i].health.SetMaxValue(0.0); } return true; @@ -138,7 +139,7 @@ bool Armor::Upgrade(const std::string upgrade_name) { armor_values.push_back(armor_value); } - armor_->UpdateFacets(armor_values); + UpdateFacets(armor_values); return true; } @@ -155,10 +156,16 @@ void Armor::Repair() { } bool Armor::Damaged() const { - return false;//(armor_layer_->TotalLayerValue()/armor_layer_->TotalMaxLayerValue()) < 100.0; + for(const Health& facet : facets) { + if(facet.health.Damaged()) { + return true; + } + } + + return false; } bool Armor::Installed() const { - return true;//armor_layer_->facets[0].max_health > 0; + return TotalMaxLayerValue() > 0; } \ No newline at end of file diff --git a/engine/src/components/armor.h b/engine/src/components/armor.h index 6078b624de..6021bf64d7 100644 --- a/engine/src/components/armor.h +++ b/engine/src/components/armor.h @@ -44,12 +44,10 @@ extern const std::string armor_facets[]; // With tungsten, you multiply each value by 1. // With better armor material, you multiply by more. -class Armor : public Component { - DamageableLayer* armor_; - +class Armor : public Component, public DamageableLayer { friend class Unit; public: - Armor(DamageableLayer* armor_); + Armor(); // Load from dictionary virtual void Load(std::string upgrade_key, std::string unit_key); diff --git a/engine/src/components/shield.cpp b/engine/src/components/shield.cpp index bb47b55e37..7bb6b073dc 100644 --- a/engine/src/components/shield.cpp +++ b/engine/src/components/shield.cpp @@ -310,7 +310,14 @@ void Shield::Enhance() { } +double Shield::GetPower() const { + return power.Value(); +} +double Shield::GetPowerCap() const { + return power.AdjustedValue(); +} + /* This is a bit kludgy. Set power via keyboard only works when not suppressed. @@ -322,9 +329,11 @@ void Shield::SetPower(const double power) { this->power = power; } -// Do we need this? + void Shield::SetPowerCap(const double power) { this->power.SetAdjustedMaxValue(power); + // We need this as well, otherwise power will still be 0. + this->power.Set(power); } diff --git a/engine/src/components/shield.h b/engine/src/components/shield.h index 8ce2ca1479..bc83cad84c 100644 --- a/engine/src/components/shield.h +++ b/engine/src/components/shield.h @@ -78,6 +78,8 @@ class Shield : public Component, public DamageableLayer { double GetRegeneration() const; void Regenerate(); + double GetPower() const; + double GetPowerCap() const; void SetPower(const double power); void SetPowerCap(const double power); }; diff --git a/engine/src/components/tests/armor_tests.cpp b/engine/src/components/tests/armor_tests.cpp index 79fc324ff5..cdc1f1d7ea 100644 --- a/engine/src/components/tests/armor_tests.cpp +++ b/engine/src/components/tests/armor_tests.cpp @@ -17,21 +17,15 @@ const std::string MASS = "Mass"; // Utilities -/* Discussion: use of macros -* Since we are using both DamageableLayer and Armor and one has - a pointer to the other, creating both in a function is somewhat - diffcult. - One option is pointers. - Another is macros. - However, returning std::pair is asking for - trouble, as there is potentially a copy operation and the original - DL object will go out of scope. -*/ -#define CREATE_ARMOR \ - Health health = Health(1,100.0,0.0); \ - DamageableLayer layer(1, FacetConfiguration::eight, health, false); \ - Armor armor = Armor(&layer); \ +Armor createArmor(double health) { + Armor armor = Armor(); + std::vector facets; + for(int i=0;i<8;i++) { + facets.push_back(health); + } + armor.UpdateFacets(facets); +} void loadArmor(Armor &armor, double health, double max_health) { // "Ship" @@ -54,27 +48,25 @@ void loadArmor(Armor &armor, double health, double max_health) { armor.Load(DUMMY_ARMOR_KEY, DUMMY_UNIT_KEY); } -void testArmorValues(DamageableLayer &layer, double health, double max_health) { +void testArmorValues(Armor &armor, double health, double max_health) { for(int i=0;i<8;i++) { - EXPECT_EQ(layer.facets[i].health.Value(), health); - EXPECT_EQ(layer.facets[i].health.MaxValue(), max_health); + EXPECT_EQ(armor.facets[i].health.Value(), health); + EXPECT_EQ(armor.facets[i].health.MaxValue(), max_health); } } // Tests TEST(Armor, Load) { - CREATE_ARMOR + Armor armor = createArmor(1); loadArmor(armor, 25, 50); - testArmorValues(layer, 25, 50); + testArmorValues(armor, 25, 50); } TEST(Armor, Save) { - Health health = Health(1,100.0,0.0); - DamageableLayer layer(1, FacetConfiguration::eight, health, false); - Armor armor = Armor(&layer); + Armor armor = createArmor(100); std::map csv; armor.SaveToCSV(csv); @@ -85,7 +77,7 @@ TEST(Armor, Save) { } TEST(Armor, CanDowngrade) { - CREATE_ARMOR + Armor armor = createArmor(1); loadArmor(armor, 25, 50); armor.SetIntegral(false); @@ -96,16 +88,16 @@ TEST(Armor, CanDowngrade) { } TEST(Armor, Downgrade) { - CREATE_ARMOR + Armor armor = createArmor(1); loadArmor(armor, 25, 50); EXPECT_TRUE(armor.Downgrade()); - testArmorValues(layer, 0, 0); + testArmorValues(armor, 0, 0); } TEST(Armor, CanUpgrade) { - CREATE_ARMOR + Armor armor = createArmor(1); loadArmor(armor, 25, 50); EXPECT_TRUE(armor.Downgrade()); @@ -113,12 +105,11 @@ TEST(Armor, CanUpgrade) { } TEST(Armor, Upgrade) { - CREATE_ARMOR + Armor armor = createArmor(1); loadArmor(armor, 25, 50); EXPECT_TRUE(armor.Downgrade()); EXPECT_TRUE(armor.Upgrade(DUMMY_ARMOR_KEY)); - testArmorValues(layer, 50, 50); + testArmorValues(armor, 50, 50); } -#undef CREATE_ARMOR \ No newline at end of file diff --git a/engine/src/damage/damageable_layer.cpp b/engine/src/damage/damageable_layer.cpp index dc5a38bb4d..97cf6a992f 100644 --- a/engine/src/damage/damageable_layer.cpp +++ b/engine/src/damage/damageable_layer.cpp @@ -174,7 +174,7 @@ int DamageableLayer::GetFacetIndex(const CoreVector &attack_vector) { -double DamageableLayer::TotalLayerValue() { +double DamageableLayer::TotalLayerValue() const { double total_value = 0.0f; for (const Health &facet : facets) { total_value += facet.health.Value(); @@ -182,7 +182,7 @@ double DamageableLayer::TotalLayerValue() { return total_value; } -double DamageableLayer::TotalMaxLayerValue() { +double DamageableLayer::TotalMaxLayerValue() const { double total_value = 0.0f; for (const Health &facet : facets) { total_value += facet.health.MaxValue(); @@ -190,7 +190,7 @@ double DamageableLayer::TotalMaxLayerValue() { return total_value; } -double DamageableLayer::AverageLayerValue() { +double DamageableLayer::AverageLayerValue() const { double total_value = 0.0f; for (const Health &facet : facets) { total_value += facet.health.Value(); @@ -198,7 +198,7 @@ double DamageableLayer::AverageLayerValue() { return total_value / facets.size(); } -double DamageableLayer::AverageMaxLayerValue() { +double DamageableLayer::AverageMaxLayerValue() const { double total_value = 0.0f; for (const Health &facet : facets) { total_value += facet.health.MaxValue(); @@ -207,7 +207,7 @@ double DamageableLayer::AverageMaxLayerValue() { } -double DamageableLayer::GetMaxHealth() { +double DamageableLayer::GetMaxHealth() const { if (number_of_facets == 0) { return 0.0f; } diff --git a/engine/src/damage/damageable_layer.h b/engine/src/damage/damageable_layer.h index f0c1b7cb36..8c5e69ed02 100644 --- a/engine/src/damage/damageable_layer.h +++ b/engine/src/damage/damageable_layer.h @@ -69,12 +69,12 @@ struct DamageableLayer { int GetFacetIndex(const CoreVector &attack_vector); - double TotalLayerValue(); - double TotalMaxLayerValue(); - double AverageLayerValue(); - double AverageMaxLayerValue(); + double TotalLayerValue() const; + double TotalMaxLayerValue() const; + double AverageLayerValue() const; + double AverageMaxLayerValue() const; - double GetMaxHealth(); + double GetMaxHealth() const; double GetPercent(FacetName facet_name) const; void UpdateFacets(const std::vector new_facets); diff --git a/engine/src/resource/cout_util.h b/engine/src/resource/cout_util.h index a436830c0a..a285a9e2a7 100644 --- a/engine/src/resource/cout_util.h +++ b/engine/src/resource/cout_util.h @@ -32,5 +32,16 @@ void printPlayerMessage(std::string key, } } +void printOnceInAHundred(int &i, + std::string key, + std::string message, + std::string value) { + if(i==0) { + printPlayerMessage(key,message,value); + } + i++; + if(i==100) i=0; +} + #endif //VEGA_STRIKE_ENGINE_RESOURCE_COUT_UTIL_H diff --git a/engine/src/resource/resource.cpp b/engine/src/resource/resource.cpp index c7f8b845ad..9f0a7bcb89 100644 --- a/engine/src/resource/resource.cpp +++ b/engine/src/resource/resource.cpp @@ -92,13 +92,18 @@ void Resource::SetMaxValue(const T &value) { template void Resource::SetAdjustedMaxValue(const T &value) { + T v = value; + if(no_max_) { // Can't set max if there's no max return; } - // Unhandled edge case - value < min value - adjusted_max_value_ = std::min(value_, max_value_); - this->value_ = std::min(this->value_, adjusted_max_value_); + v = std::max(min_value_, v); + v = std::min(max_value_, v); + + adjusted_max_value_ = v; + + value_ = std::min(value_, adjusted_max_value_); } template From 77d3dabae55722256e6bc4d0f17668a36379c5f0 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Fri, 29 Mar 2024 18:06:04 +0300 Subject: [PATCH 14/16] Make Hull a superclass of DamageableLayer --- engine/src/cmd/damageable.cpp | 10 +++------- engine/src/cmd/damageable.h | 3 ++- engine/src/components/armor.cpp | 2 +- engine/src/components/hull.cpp | 13 +++++++------ engine/src/components/hull.h | 5 ++--- engine/src/components/shield.cpp | 2 +- engine/src/damage/damageable_layer.cpp | 5 ++--- engine/src/damage/damageable_layer.h | 1 - engine/src/damage/health.cpp | 2 +- engine/src/damage/health.h | 2 +- engine/src/damage/tests/layer_tests.cpp | 4 ++-- 11 files changed, 22 insertions(+), 27 deletions(-) diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index 23fc3e0840..5d278d98ba 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -45,13 +45,9 @@ Damageable::Damageable() : DamageableObject(), killed(false), - armor(), - shield() { - // Damageable Constructor - Health hull_health = Health(1, 1, 0); - - hull_ = DamageableLayer(0, FacetConfiguration::one, hull_health, true); - + hull_(), + armor_(), + shield_() { hull = &hull_; armor = &armor_; shield = &shield_; diff --git a/engine/src/cmd/damageable.h b/engine/src/cmd/damageable.h index ecf8ec28c1..ad2b9b5734 100644 --- a/engine/src/cmd/damageable.h +++ b/engine/src/cmd/damageable.h @@ -30,6 +30,7 @@ #include "mount_size.h" #include "components/armor.h" +#include "components/hull.h" #include "components/shield.h" #include @@ -40,7 +41,7 @@ struct GFXColor; * @brief The Damageable class TODO */ class Damageable : public DamageableObject { - DamageableLayer hull_; + Hull hull_; Armor armor_; Shield shield_; diff --git a/engine/src/components/armor.cpp b/engine/src/components/armor.cpp index a969fc8ac2..72dce4a63b 100644 --- a/engine/src/components/armor.cpp +++ b/engine/src/components/armor.cpp @@ -44,7 +44,7 @@ const std::string armor_facets[] = { Armor::Armor(): Component("", 0.0, 0.0, false), DamageableLayer(1, FacetConfiguration::eight, - Health(0,0,0), false) {} + Health(0,0), false) {} void Armor::Load(std::string upgrade_key, std::string unit_key) { // Component diff --git a/engine/src/components/hull.cpp b/engine/src/components/hull.cpp index 2121987b2b..a111efce6a 100644 --- a/engine/src/components/hull.cpp +++ b/engine/src/components/hull.cpp @@ -29,8 +29,9 @@ #include "unit_csv_factory.h" -Hull::Hull(DamageableLayer *hull_): Component("", 0.0, 0.0, true), - hull_(hull_) {} +Hull::Hull(): Component("", 0.0, 0.0, true), + DamageableLayer(0, FacetConfiguration::one, + Health(0, 1), true) {} void Hull::Load(std::string upgrade_key, std::string unit_key, Unit *unit) { @@ -44,8 +45,8 @@ void Hull::Load(std::string upgrade_key, std::string unit_key, // Damageable Layer double hull_current = UnitCSVFactory::GetVariable(unit_key, "hull", 1.0); double hull_max = UnitCSVFactory::GetVariable(upgrade_key, "hull", 1.0); - hull_->facets[0].health.SetMaxValue(hull_max); - hull_->facets[0].health.Set(hull_current); + facets[0].health.SetMaxValue(hull_max); + facets[0].health.Set(hull_current); } std::string Hull::SaveToJSON() const { @@ -78,11 +79,11 @@ bool Hull::Upgrade(const std::string upgrade_name) { void Hull::Damage() {} void Hull::Repair() { - hull_->facets[0].health.SetToMax(); + facets[0].health.SetToMax(); } bool Hull::Damaged() const { - return hull_->facets[0].health.Damaged(); + return facets[0].health.Damaged(); } diff --git a/engine/src/components/hull.h b/engine/src/components/hull.h index 1b9f19101e..694b83f394 100644 --- a/engine/src/components/hull.h +++ b/engine/src/components/hull.h @@ -37,11 +37,10 @@ * We keep these functions to make the hull a separate component. * It cannot be sold (downgraded) but can be repaired. */ -class Hull : public Component { - DamageableLayer* hull_; +class Hull : public Component, public DamageableLayer { friend class Unit; public: - Hull(DamageableLayer *hull_); + Hull(); virtual void Load(std::string upgrade_key, std::string unit_key, Unit *unit); // Load from dictionary diff --git a/engine/src/components/shield.cpp b/engine/src/components/shield.cpp index 7bb6b073dc..6d472c2e11 100644 --- a/engine/src/components/shield.cpp +++ b/engine/src/components/shield.cpp @@ -63,7 +63,7 @@ std::string shield_facets_two[2] = { Shield::Shield(): Component("", 0.0, 0.0, false), DamageableLayer(2, FacetConfiguration::zero, - Health(2, 0, 0), false), + Health(2, 0), false), regeneration(0,0,0), power(1.0,0.0,1.0) {} diff --git a/engine/src/damage/damageable_layer.cpp b/engine/src/damage/damageable_layer.cpp index 97cf6a992f..1a7698f17b 100644 --- a/engine/src/damage/damageable_layer.cpp +++ b/engine/src/damage/damageable_layer.cpp @@ -53,14 +53,13 @@ DamageableLayer::DamageableLayer(int layer_index, DamageableLayer::DamageableLayer(int layer_index, FacetConfiguration configuration, double health_array[], - double 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); + Health health(layer_index, health_array[i]); facets.push_back(health); } @@ -291,7 +290,7 @@ void DamageableLayer::UpdateFacets(const std::vector new_facets) { if(new_size != number_of_facets) { number_of_facets = new_size; facets.clear(); - Health health = Health(layer_index, 0, 0); + Health health = Health(layer_index, 0); for(int i=0;i -Health::Health(int layer, double health, double regeneration) : +Health::Health(int layer, double health) : layer(layer), health(health, 0, health) { destroyed = false; diff --git a/engine/src/damage/health.h b/engine/src/damage/health.h index 228938b22c..34c90ad685 100644 --- a/engine/src/damage/health.h +++ b/engine/src/damage/health.h @@ -63,7 +63,7 @@ struct Health { } effect{}; - Health(int layer, double health = 1, double regeneration = 0); + Health(int layer, double health = 1); double Percent() const { return health.Percent(); diff --git a/engine/src/damage/tests/layer_tests.cpp b/engine/src/damage/tests/layer_tests.cpp index 94847ff17c..2c062f9607 100644 --- a/engine/src/damage/tests/layer_tests.cpp +++ b/engine/src/damage/tests/layer_tests.cpp @@ -31,7 +31,7 @@ // Demonstrate some basic assertions. TEST(Layer, Sanity) { - Health health(10, 10, 0); + Health health(10, 10); DamageableLayer layer = DamageableLayer(0, FacetConfiguration::four, health, true); EXPECT_EQ(layer.GetFacetIndex(CoreVector(0, 0, 0)), 0); @@ -55,7 +55,7 @@ TEST(Layer, Sanity) { } TEST(Layer, Sanity_2) { - Health health(0, 10, 0); + Health health(0, 10); EXPECT_EQ(health.health.MaxValue(), 10); EXPECT_EQ(health.health.Value(), 10); From 019b5071db95de2f5dc046ee4337b69f7cd8d8f0 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Wed, 10 Apr 2024 07:46:47 +0300 Subject: [PATCH 15/16] Add fuel component Remove energy manager Make Cloak a Component subclass Implement Reactor, Shield and Armor more fully Remove Damageable::regenerateShields - now part of shield and more consistent --- engine/CMakeLists.txt | 1 + engine/src/cmd/ai/docking.cpp | 4 +- engine/src/cmd/ai/warpto.cpp | 2 +- engine/src/cmd/armed.cpp | 8 +- engine/src/cmd/basecomputer.cpp | 62 ++++---- engine/src/cmd/damageable.cpp | 53 +------ engine/src/cmd/damageable.h | 1 + engine/src/cmd/jump_capable.cpp | 2 +- engine/src/cmd/mount.cpp | 6 +- engine/src/cmd/planet.cpp | 2 +- .../cmd/script/script_call_unit_generic.cpp | 2 +- engine/src/cmd/unit_csv.cpp | 41 +++--- engine/src/cmd/unit_generic.cpp | 23 ++- engine/src/cmd/unit_generic.h | 15 +- engine/src/components/armor.cpp | 5 +- engine/src/components/cloak.cpp | 104 +++++++++++++- engine/src/components/cloak.h | 43 +++--- engine/src/components/component.h | 3 +- engine/src/components/energy_consumer.h | 2 +- engine/src/components/energy_container.cpp | 10 +- engine/src/components/energy_container.h | 5 +- engine/src/components/energy_manager.cpp | 37 +++-- engine/src/components/energy_manager.h | 23 ++- engine/src/components/energy_types.h | 3 +- engine/src/components/fuel.cpp | 92 ++++++++++++ engine/src/components/fuel.h | 63 ++++++++ engine/src/components/hull.cpp | 8 +- engine/src/components/hull.h | 2 +- engine/src/components/reactor.cpp | 135 +++++++++++++----- engine/src/components/reactor.h | 53 +++++-- engine/src/components/shield.cpp | 56 ++++++-- engine/src/components/shield.h | 32 ++++- .../src/components/tests/balancing_tests.cpp | 127 ++++++++-------- engine/src/gfx/cockpit.cpp | 8 +- engine/src/resource/cout_util.h | 10 ++ 35 files changed, 729 insertions(+), 314 deletions(-) create mode 100644 engine/src/components/fuel.cpp create mode 100644 engine/src/components/fuel.h diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index ce21acf824..ce8f2e10a0 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -673,6 +673,7 @@ SET(LIBCOMPONENT src/components/energy_consumer.cpp src/components/energy_container.cpp src/components/energy_manager.cpp + src/components/fuel.cpp src/components/jump_drive.cpp src/components/radar.cpp src/components/reactor.cpp diff --git a/engine/src/cmd/ai/docking.cpp b/engine/src/cmd/ai/docking.cpp index c1357ed8bd..987198b19c 100644 --- a/engine/src/cmd/ai/docking.cpp +++ b/engine/src/cmd/ai/docking.cpp @@ -206,7 +206,7 @@ bool DockingOps::DockToTarget(Unit *utdw) { if (physicallyDock) { return parent->Dock(utdw); } else { - utdw->energy_manager.Refill(EnergyType::FTL); + utdw->ftl_energy.Refill(); return true; } } else if (diss <= 1.2 * rad * rad) { @@ -216,7 +216,7 @@ bool DockingOps::DockToTarget(Unit *utdw) { if (physicallyDock) { return parent->Dock(utdw); } else { - parent->energy_manager.Refill(EnergyType::FTL); + parent->ftl_energy.Refill(); return true; } } diff --git a/engine/src/cmd/ai/warpto.cpp b/engine/src/cmd/ai/warpto.cpp index 7a5fd1cb22..560116d8fb 100644 --- a/engine/src/cmd/ai/warpto.cpp +++ b/engine/src/cmd/ai/warpto.cpp @@ -98,7 +98,7 @@ static void ActuallyWarpTo(Unit *parent, const QVector &tarpos, Vector tarvel, U XMLSupport::parse_float(vs_config->getVariable("AI", "min_energy_to_enter_warp", ".33")); static float min_warpfield_to_enter_warp = XMLSupport::parse_float(vs_config->getVariable("AI", "min_warp_to_try", "1.5")); - if ((parent->energy_manager.Percent(EnergyType::FTL) > min_energy_to_enter_warp) + if ((parent->ftl_energy.Percent() > min_energy_to_enter_warp) && (parent->GetMaxWarpFieldStrength(1) > min_warpfield_to_enter_warp)) { if (parent->graphicOptions.InWarp == 0) { parent->graphicOptions.InWarp = 1; //don't want the AI thrashing diff --git a/engine/src/cmd/armed.cpp b/engine/src/cmd/armed.cpp index 154e41c17c..fc9b6e8dea 100644 --- a/engine/src/cmd/armed.cpp +++ b/engine/src/cmd/armed.cpp @@ -251,14 +251,14 @@ void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { //&& ( (ROLES::EVERYTHING_ELSE&weapon_type_bitmask&i->type->role_bits) || i->type->role_bits == 0 ) ((locked_on && missile_and_want_to_fire_missiles) || gun_and_want_to_fire_guns); if ((*i).type->type == WEAPON_TYPE::BEAM) { - if ((*i).type->GetConsumption() * simulation_atom_var > unit->energy_manager.GetLevel(EnergyType::Energy)) { + if ((*i).type->GetConsumption() * simulation_atom_var > unit->energy.Level()) { //NOT ONLY IN non-networking mode : anyway, the server will tell everyone including us to stop if not already done (*i).UnFire(); continue; } } else //Only in non-networking mode - if (i->type->GetConsumption() > unit->energy_manager.GetLevel(EnergyType::Energy)) { + if (i->type->GetConsumption() > unit->energy.Level()) { if (!want_to_fire) { i->UnFire(); } @@ -279,11 +279,11 @@ void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { if (i->ref.gun) { if ((!i->ref.gun->Dissolved()) || i->ref.gun->Ready()) { // TODO: switch to standard energy usage - unit->energy_manager.Deplete(EnergyType::Energy, i->type->GetConsumption() * simulation_atom_var); + unit->energy.Deplete(i->type->GetConsumption() * simulation_atom_var); } } } else if (i->type->isMissile()) { // FIXME other than beams, only missiles are processed here? - unit->energy_manager.Deplete(EnergyType::Energy, i->type->GetConsumption()); + unit->energy.Deplete(i->type->GetConsumption()); } //IF WE REFRESH ENERGY FROM SERVER : Think to send the energy update to the firing client with ACK TO fireRequest //fire only 1 missile at a time diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index 5097699c17..4af3a99162 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -5344,15 +5344,15 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C if (shields_require_power) { maxshield = 0; } - PRETTY_ADDU(statcolor + "Recharge: #-c", playerUnit->energy_manager.GetReactorCapacity() * RSconverter, 0, "MJ/s"); + PRETTY_ADDU(statcolor + "Recharge: #-c", playerUnit->reactor.Capacity() * RSconverter, 0, "MJ/s"); PRETTY_ADDU(statcolor + "Weapon capacitor bank storage: #-c", // TODO: this should be converted to variable vs constant // Also, this is a shitty calculation. Why subtract shields and not ECM or life support? - ((playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) - maxshield) * RSconverter), 0, "MJ"); + ((playerUnit->energy.MaxLevel() - maxshield) * RSconverter), 0, "MJ"); //note: I found no function to get max warp energy, but since we're docked they are the same if (!subunitlevel) { PRETTY_ADDU(statcolor + "Warp capacitor bank storage: #-c", - playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) * RSconverter * Wconv, + playerUnit->ftl_energy.MaxLevel() * RSconverter * Wconv, 0, "MJ"); @@ -5375,7 +5375,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C PRETTY_ADDU(statcolor + "Delay: #-c", uj.delay, 0, "seconds"); if (uj.damage > 0) PRETTY_ADDU(statcolor + "Damage to outsystem jump drive: #-c", uj.damage * VSDM, 0, "MJ"); - if (playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) < uj.energy) { + if (playerUnit->ftl_energy.MaxLevel() < uj.energy) { text += "#n##c1:.3:.3#" + prefix + "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c"; @@ -5385,45 +5385,45 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } else { switch (replacement_mode) { case 0: //Replacement or new Module - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Installs reactor with recharge rate: #-c", - playerUnit->energy_manager.GetReactorCapacity() * RSconverter, 0, "MJ/s"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) + playerUnit->reactor.Capacity() * RSconverter, 0, "MJ/s"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Installs main capacitor bank with storage capacity: #-c", - (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) * RSconverter), 0, "MJ"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::FTL))) + (playerUnit->energy.MaxLevel() * RSconverter), 0, "MJ"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, ftl_energy.MaxLevel())) PRETTY_ADDU(statcolor + "Installs warp capacitor bank with storage capacity: #-c", - playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) * RSconverter * Wconv, 0, "MJ"); + playerUnit->ftl_energy.MaxLevel() * RSconverter * Wconv, 0, "MJ"); if (buj.drive != uj.drive) { text += statcolor + "#n#Allows travel via Jump Points.#n#Consult your personal info screen for ship specific energy requirements. #-c"; } break; case 1: //Additive - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Increases recharge rate by #-c", - playerUnit->energy_manager.GetReactorCapacity() * RSconverter, 0, "MJ/s"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) + playerUnit->reactor.Capacity() * RSconverter, 0, "MJ/s"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Adds #-c", - (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) * RSconverter), + (playerUnit->energy.MaxLevel() * RSconverter), 0, "MJ of storage to main capacitor banks"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::FTL))) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, ftl_energy.MaxLevel())) PRETTY_ADDU(statcolor + "Adds #-c", - playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) * RSconverter * Wconv, + playerUnit->ftl_energy.MaxLevel() * RSconverter * Wconv, 0, "MJ of storage to warp capacitor bank"); break; case 2: //multiplicative - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Increases reactor recharge rate by #-c", - 100.0 * (playerUnit->energy_manager.GetReactorCapacity() - 1), 0, "%"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetReactorCapacity())) + 100.0 * (playerUnit->reactor.Capacity() - 1), 0, "%"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Increases main capacitor bank storage by #-c", - 100.0 * (playerUnit->energy_manager.GetMaxLevel(EnergyType::Energy) - 1), 0, "%"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, energy_manager.GetMaxLevel(EnergyType::FTL))) + 100.0 * (playerUnit->energy.MaxLevel() - 1), 0, "%"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, ftl_energy.MaxLevel())) PRETTY_ADDU(statcolor + "Increases warp capacitor bank storage by #-c", - (playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) - 1) * 100, 0, "%"); + (playerUnit->ftl_energy.MaxLevel() - 1) * 100, 0, "%"); break; default: //Failure text += "Oh dear, this wasn't an upgrade. Please debug code."; @@ -5771,32 +5771,32 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C PRETTY_ADDU(statcolor + "Minimum time to reach full overthrust speed: #-c", playerUnit->getMass() * uc.max_ab_speed() / playerUnit->limits.afterburn, 2, "seconds"); //reactor - float avail = (playerUnit->energy_manager.GetReactorCapacity() * RSconverter - maxshield * VSDM); + float avail = (playerUnit->reactor.Capacity() * RSconverter - maxshield * VSDM); int num_shields = playerUnit->shield->number_of_facets; float regeneration = playerUnit->shield->GetRegeneration(); float overhead = (shields_require_power) ? (regeneration / shieldenergycap * shield_maintenance_cost * num_shields * VSDM) : 0; - float nrt = avail / (playerUnit->energy_manager.GetReactorCapacity() * RSconverter); // TODO -overhead); + float nrt = avail / (playerUnit->reactor.Capacity() * RSconverter); // TODO -overhead); PRETTY_ADDU(statcolor + "Reactor nominal replenish time: #-c", nrt, 2, "seconds"); //shield related stuff //code taken from RegenShields in unit_generic.cpp, so we're sure what we say here is correct. static float low_power_mode = XMLSupport::parse_float(vs_config->getVariable("physics", "low_power_mode_energy", "10")); - if (playerUnit->energy_manager.GetReactorCapacity() - maxshield < low_power_mode) { + if (playerUnit->reactor.Capacity() - maxshield < low_power_mode) { text += "#n##c1:.3:.3#" + prefix + "WARNING: Capacitor banks are overdrawn: downgrade shield, upgrade reactor or purchase reactor capacitance!#-c"; } - if (uj.drive != -2 && playerUnit->energy_manager.GetMaxLevel(EnergyType::FTL) < uj.energy) { + if (uj.drive != -2 && playerUnit->ftl_energy.MaxLevel() < uj.energy) { text += "#n##c1:.3:.3#" + prefix + "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c"; } if (num_shields) { - if (regeneration * num_shields * VSDM / shieldenergycap > playerUnit->energy_manager.GetReactorCapacity() + if (regeneration * num_shields * VSDM / shieldenergycap > playerUnit->reactor.Capacity() * RSconverter) { text += "#n##c1:1:.1#" + prefix + "WARNING: reactor recharge rate is less than combined shield recharge rate.#n#"; @@ -5805,7 +5805,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C if (shields_require_power) { text += "#n#" + prefix + statcolor + "Reactor recharge slowdown caused by shield maintenance: #-c"; float maint_draw_percent = regeneration * VSDM * 100.0 / shieldenergycap * shield_maintenance_cost - * num_shields / (playerUnit->energy_manager.GetReactorCapacity() * RSconverter); + * num_shields / (playerUnit->reactor.Capacity() * RSconverter); text += (boost::format("%1$.2f") % maint_draw_percent).str(); text += " %."; if (maint_draw_percent > 60) { @@ -5824,14 +5824,14 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C float maint_draw = (shields_require_power && num_shields) ? (regeneration * VSDM / shieldenergycap * shield_maintenance_cost * num_shields) : 0; - if (totalWeaponEnergyUsage < (playerUnit->energy_manager.GetReactorCapacity() * RSconverter - maint_draw)) { + if (totalWeaponEnergyUsage < (playerUnit->reactor.Capacity() * RSconverter - maint_draw)) { //waouh, impressive... text += "#n##c0:1:.2#" + prefix + "Your reactor produces more energy than your weapons can use!#-c"; } else { PRETTY_ADDU(statcolor + "Reactor energy depletion time if weapons in continuous use: #-c", - (playerUnit->energy_manager.GetReactorCapacity() + (playerUnit->reactor.Capacity() * RSconverter) / (totalWeaponEnergyUsage - - ((playerUnit->energy_manager.GetReactorCapacity() * RSconverter - maint_draw))), + - ((playerUnit->reactor.Capacity() * RSconverter - maint_draw))), 2, "seconds"); } diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index 5d278d98ba..6308598dc8 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -205,7 +205,7 @@ void Damageable::ApplyDamage(const Vector &pnt, // Additional house cleaning unit->PrimeOrders(); - unit->energy_manager = EnergyManager(); + // TODO: clear components unit->Split(rand() % 3 + 1); @@ -547,58 +547,7 @@ void Damageable::leach(float damShield, float damShieldRecharge, float damEnRech -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->graphicOptions.InWarp; - const int shield_facets = shield_.number_of_facets; - const float total_max_shields = shield_.TotalMaxLayerValue(); - - // No point in all this code if there are no shields. - if (shield_facets < 2 || total_max_shields == 0) { - return; - } - - EnergyContainer *energy = unit->energy_manager.GetContainer(EnergyType::Energy); - - // Here we store the actual charge we'll use in RegenShields - // TODO: fix this. It's a hack just to build... - double max_shield_recharge = shield_.GetRegeneration(); - double actual_recharge = max_shield_recharge * - energy->Powered(EnergyConsumerClassification::ShieldRegen); - - float shield_recharge = actual_recharge * 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->cloak.Active()) { - // "Damage" power - shield_.SetPowerCap(0.0); - } else { - // Figure out how to support partial power - shield_.SetPowerCap(1.0); - } - - // Shield regeneration - shield_.Regenerate(); -} float Damageable::MaxShieldVal() const { Damageable *damageable = const_cast(this); diff --git a/engine/src/cmd/damageable.h b/engine/src/cmd/damageable.h index ad2b9b5734..05fd5e95ec 100644 --- a/engine/src/cmd/damageable.h +++ b/engine/src/cmd/damageable.h @@ -41,6 +41,7 @@ struct GFXColor; * @brief The Damageable class TODO */ class Damageable : public DamageableObject { +protected: Hull hull_; Armor armor_; Shield shield_; diff --git a/engine/src/cmd/jump_capable.cpp b/engine/src/cmd/jump_capable.cpp index e08fa44460..fce8611e13 100644 --- a/engine/src/cmd/jump_capable.cpp +++ b/engine/src/cmd/jump_capable.cpp @@ -141,7 +141,7 @@ bool JumpCapable::AutoPilotToErrorMessage(const Unit *target, // TODO: move to energymanager.canpower and // energycontainer.canpower - if (unit->energy_manager.GetLevel(EnergyType::FTL) < jump_drive.GetConsumption()) { + if (unit->ftl_energy.Level() < jump_drive.GetConsumption()) { if (!ignore_energy_requirements) { return false; } diff --git a/engine/src/cmd/mount.cpp b/engine/src/cmd/mount.cpp index 5f878ca530..30408133fd 100644 --- a/engine/src/cmd/mount.cpp +++ b/engine/src/cmd/mount.cpp @@ -254,7 +254,7 @@ bool Mount::PhysicsAlignedFire(Unit *caller, //Missiles and beams set to processed. processed = PROCESSED; } else if (ref.refire < type->Refire() || - type->GetConsumption() > caller->energy_manager.GetLevel(EnergyType::Energy)) { + type->GetConsumption() > caller->energy.Level()) { //Wait until refire has expired and reactor has produced enough energy for the next bolt. return true; } //Not ready to refire yet. But don't stop firing. @@ -302,7 +302,7 @@ bool Mount::PhysicsAlignedFire(Unit *caller, } break; case WEAPON_TYPE::BOLT: - caller->energy_manager.Deplete(EnergyType::Energy, type->GetConsumption()); + caller->energy.Deplete(type->GetConsumption()); hint[Unit::UNIT_BOLT] = Bolt(type, mat, @@ -312,7 +312,7 @@ bool Mount::PhysicsAlignedFire(Unit *caller, break; case WEAPON_TYPE::BALL: { - caller->energy_manager.Deplete(EnergyType::Energy, type->GetConsumption()); + caller->energy.Deplete(type->GetConsumption()); hint[Unit::UNIT_BOLT] = BoltDrawManager::GetInstance().AddBall(type, mat, velocity, owner, hint[Unit::UNIT_BOLT]); diff --git a/engine/src/cmd/planet.cpp b/engine/src/cmd/planet.cpp index 0013301058..9daf89c276 100644 --- a/engine/src/cmd/planet.cpp +++ b/engine/src/cmd/planet.cpp @@ -462,7 +462,7 @@ void Planet::InitPlanet(QVector x, VSSprite *tmp = pImage->pHudImage; pImage->pHudImage = un->GetImageInformation().pHudImage; un->GetImageInformation().pHudImage = tmp; - energy_manager.SetCapacity(EnergyType::FTL, un->energy_manager.GetMaxLevel(EnergyType::FTL)); + ftl_energy.SetCapacity(un->ftl_energy.MaxLevel()); if (smartplanets) { SubUnits.prepend(un); un->SetRecursiveOwner(this); diff --git a/engine/src/cmd/script/script_call_unit_generic.cpp b/engine/src/cmd/script/script_call_unit_generic.cpp index f79c22e20f..c74cd1d352 100644 --- a/engine/src/cmd/script/script_call_unit_generic.cpp +++ b/engine/src/cmd/script/script_call_unit_generic.cpp @@ -508,7 +508,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->energy_manager.GetLevel(EnergyType::Energy); + res = my_unit->energy.Level(); } 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 da9ce6e707..9b6f920f3b 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -402,7 +402,7 @@ static void AddSubUnits(Unit *thus, if (randomspawn) { // TODO: surely we could use something more relevant than max SPEC // to determine chance to spawn... - int chancetospawn = float_to_int(xml.units[a]->energy_manager.GetMaxLevel(EnergyType::FTL)); + int chancetospawn = float_to_int(xml.units[a]->ftl_energy.MaxLevel()); if (chancetospawn > rand() % 100) { thus->SubUnits.prepend(xml.units[a]); } else { @@ -645,6 +645,8 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ string tmpstr; csvRow = unit_identifier; + // What happens if we buy a new ship? + // We treat saved_game as is_player_ship! std::string unit_key = (saved_game ? "player_ship" : unit_identifier); fullname = UnitCSVFactory::GetVariable(unit_key, "Name", std::string()); @@ -735,14 +737,9 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ Momentofinertia = UnitCSVFactory::GetVariable(unit_key, "Moment_Of_Inertia", 1.0f); - energy_manager.SetCapacity(EnergyType::Fuel, - UnitCSVFactory::GetVariable(unit_key, "Fuel_Capacity", 0.0f)); - energy_manager.Refill(EnergyType::Fuel); - - energy_manager.SetCapacity(EnergyType::Energy, - UnitCSVFactory::GetVariable(unit_key, "Primary_Capacitor", 0.0f)); - energy_manager.SetCapacity(EnergyType::FTL, - UnitCSVFactory::GetVariable(unit_key, "Warp_Capacitor", 0.0f)); + fuel.Load("", unit_key); + energy.SetCapacity(UnitCSVFactory::GetVariable(unit_key, "Primary_Capacitor", 0.0), true); + ftl_energy.SetCapacity(UnitCSVFactory::GetVariable(unit_key, "Warp_Capacitor", 0.0), true); // Hull float temp_hull = UnitCSVFactory::GetVariable(unit_key, "Hull", 0.0f); @@ -764,9 +761,17 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ armor->UpdateFacets(armor_values); + // Shield difficulty modifier (if applicable) + // Difficulty modifier to shield strength and regeneration + static const double apply_difficulty_modifier = + configuration()->physics_config.difficulty_based_shield_recharge; + double difficulty_modifier = 1.0f; + if (apply_difficulty_modifier && !isPlayerShip()) { + difficulty_modifier = g_game.difficulty; + } // Load shield - shield->Load("", unit_key, this); + shield->Load("", unit_key, this, difficulty_modifier); // End shield section @@ -776,8 +781,7 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ graphicOptions.MinWarpMultiplier = UnitCSVFactory::GetVariable(unit_key, "Warp_Min_Multiplier", 1.0f); graphicOptions.MaxWarpMultiplier = UnitCSVFactory::GetVariable(unit_key, "Warp_Max_Multiplier", 1.0f); - - energy_manager.SetReactorCapacity(UnitCSVFactory::GetVariable(unit_key, "Reactor_Recharge", 0.0f)); + reactor.Load("", unit_key); jump.drive = UnitCSVFactory::GetVariable(unit_key, "Jump_Drive_Present", false) ? -1 : -2; jump.delay = UnitCSVFactory::GetVariable(unit_key, "Jump_Drive_Delay", 0); @@ -842,7 +846,8 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ radar = CRadar(unit_key, &computer); - cloak = Cloak(unit_key); + cloak = Cloak(); + cloak.Load("", unit_key); repair_droid = UnitCSVFactory::GetVariable(unit_key, "Repair_Droid", 0); ecm = UnitCSVFactory::GetVariable(unit_key, "ECM_Rating", 0); @@ -1185,9 +1190,9 @@ string Unit::WriteUnitString() { unit["Moment_Of_Inertia"] = tos(Momentofinertia); // Components - unit["Fuel_Capacity"] = tos(energy_manager.GetMaxLevel(EnergyType::Fuel)); - unit["Primary_Capacitor"] = tos(energy_manager.GetMaxLevel(EnergyType::Energy)); - unit["Warp_Capacitor"] = tos(energy_manager.GetMaxLevel(EnergyType::FTL)); + fuel.SaveToCSV(unit); + unit["Primary_Capacitor"] = tos(energy.MaxLevel()); + unit["Warp_Capacitor"] = tos(ftl_energy.MaxLevel()); unit["Hull"] = tos(GetHullLayer().facets[0].health); unit["Spec_Interdiction"] = tos(specInterdiction); @@ -1211,7 +1216,7 @@ string Unit::WriteUnitString() { unit["Warp_Min_Multiplier"] = tos(graphicOptions.MinWarpMultiplier); unit["Warp_Max_Multiplier"] = tos(graphicOptions.MaxWarpMultiplier); - unit["Reactor_Recharge"] = tos(energy_manager.GetReactorCapacity()); + reactor.SaveToCSV(unit); unit["Jump_Drive_Present"] = tos(jump.drive >= -1); unit["Jump_Drive_Delay"] = tos(jump.delay); unit["Wormhole"] = tos(forcejump != 0); @@ -1243,7 +1248,7 @@ string Unit::WriteUnitString() { unit["ITTS"] = tos(computer.itts); - cloak.Save(unit); + cloak.SaveToCSV(unit); unit["Repair_Droid"] = tos(repair_droid); unit["ECM_Rating"] = tos(ecm > 0 ? ecm : -ecm); unit["Hud_Functionality"] = WriteHudDamage(this); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 8ab3e8fbdf..db1ca986fd 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -276,6 +276,7 @@ Unit::Unit(int dummy) : Drawable(), Damageable(), Movable() { pImage = (new UnitImages); pImage->cockpit_damage = NULL; pilot = new Pilot(FactionUtil::GetNeutralFaction()); + // TODO: delete Init(); } @@ -507,6 +508,11 @@ void Unit::Init(const char *filename, SetAniSpeed(0.05); StartAnimation(); } + + //Components + fuel.AddConsumer(reactor); + energy.AddConsumer(shield_); + energy.AddConsumer(cloak); } vector Unit::StealMeshes() { @@ -989,7 +995,7 @@ extern void ActivateAnimation(Unit *jp); // Move to jump_enabled void TurnJumpOKLightOn(Unit *un, Cockpit *cp) { if (cp) { - if (un->energy_manager.GetLevel(EnergyType::FTL) >= un->jump.energy) { + if (un->ftl_energy.Level() >= un->jump.energy) { if (un->jump.drive > -2) { cp->jumpok = 1; } @@ -1014,7 +1020,7 @@ bool Unit::jumpReactToCollision(Unit *smalle) { //we have a drive if ((!SPEC_interference && (smalle->jump.drive >= 0 && //we have power - (smalle->energy_manager.GetLevel(EnergyType::FTL) >= smalle->jump.energy + (smalle->ftl_energy.Level() >= smalle->jump.energy //or we're being cheap || (ai_jump_cheat && cp == nullptr) ))) @@ -1041,7 +1047,7 @@ bool Unit::jumpReactToCollision(Unit *smalle) { return false; } if ((!SPEC_interference && (jump.drive >= 0 - && (energy_manager.GetLevel(EnergyType::FTL) >= jump.energy || (ai_jump_cheat && cp == NULL)) + && (ftl_energy.Level() >= jump.energy || (ai_jump_cheat && cp == NULL)) )) || smalle->forcejump) { jump_drive.Use(); DeactivateJumpDrive(); @@ -4250,8 +4256,13 @@ void Unit::ActTurn() { Repair(); // Power - energy_manager.Act(); + fuel.Act(); + reactor.Generate(); + energy.Act(); + + shield_.Regenerate(graphicOptions.InWarp, isPlayerShip()); + ftl_energy.Act(); } void Unit::UpdatePhysics2(const Transformation &trans, @@ -4338,7 +4349,8 @@ void Unit::UpdatePhysics3(const Transformation &trans, // 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; + // TODO: remove + //const bool energy_before_shield = configuration()->physics_config.engine_energy_takes_priority; // Difficulty settings float difficulty_shields = 1.0f; @@ -4351,7 +4363,6 @@ void Unit::UpdatePhysics3(const Transformation &trans, }*/ bool is_player_ship = _Universe->isPlayerStarship(this); - RegenerateShields(difficulty_shields, is_player_ship); //ExpendEnergy(is_player_ship); /*if (!energy_before_shield) { diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index 08f490d463..298bd65e66 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -82,7 +82,9 @@ void UncheckUnit( class Unit*un ); // Components #include "components/energy_manager.h" #include "components/cloak.h" +#include "components/fuel.h" #include "components/radar.h" +#include "components/reactor.h" #include "configuration/configuration.h" #include "configuration/game_config.h" @@ -151,7 +153,14 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, public: // Components - EnergyManager energy_manager; + Fuel fuel = Fuel(); + EnergyContainer energy = EnergyContainer(EnergyType::Energy, 0.0); + EnergyContainer ftl_energy = EnergyContainer(EnergyType::FTL, 0.0); + + // TODO: move this to a single constructor?! + Reactor reactor = Reactor(0.0, + &energy, &ftl_energy, + sim_atom_multiplier); Cloak cloak; /// Radar and related systems @@ -993,8 +1002,8 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, float temporary_upgrade_float_variable; // Temporary python functions - double fuelData() { return energy_manager.GetLevel(EnergyType::Fuel); } - double energyData() { return energy_manager.GetLevel(EnergyType::Energy); } + double fuelData() { return fuel.Level(); } + double energyData() { return energy.Level(); } }; Unit *findUnitInStarsystem(const void *unitDoNotDereference); diff --git a/engine/src/components/armor.cpp b/engine/src/components/armor.cpp index 72dce4a63b..0f26f818ff 100644 --- a/engine/src/components/armor.cpp +++ b/engine/src/components/armor.cpp @@ -109,7 +109,6 @@ bool Armor::Downgrade() { } bool Armor::CanUpgrade(const std::string upgrade_name) const { - if(integral) { return false; } @@ -150,8 +149,8 @@ bool Armor::Upgrade(const std::string upgrade_name) { void Armor::Damage() {} void Armor::Repair() { - for(int i = 0;i < 8;i++) { - //armor_layer_->facets[i].health = armor_layer_->facets[i].max_health; + for(Health& facet : facets) { + facet.health.SetToMax(); } } diff --git a/engine/src/components/cloak.cpp b/engine/src/components/cloak.cpp index 885fc771bb..82384526ec 100644 --- a/engine/src/components/cloak.cpp +++ b/engine/src/components/cloak.cpp @@ -29,7 +29,8 @@ #include "vegastrike.h" #include "configuration/configuration.h" -Cloak::Cloak() : EnergyConsumer(EnergyType::Energy, +Cloak::Cloak() : Component(std::string(), 0.0, 0.0, false), + EnergyConsumer(EnergyType::Energy, EnergyConsumerClassification::Cloak, EnergyConsumerType::Variable, 0.0) { status = CloakingStatus::disabled; @@ -41,9 +42,8 @@ Cloak::Cloak() : EnergyConsumer(EnergyType::Energy, minimum = 0; } -Cloak::Cloak(std::string unit_key) : EnergyConsumer(EnergyType::Energy, - EnergyConsumerClassification::Cloak, - EnergyConsumerType::Variable, 0.0) { +void Cloak::Load(std::string upgrade_key, std::string unit_key) { + // TODO: load undamaged from upgrade_key if(UnitCSVFactory::GetVariable(unit_key, "Can_Cloak", false)) { status = CloakingStatus::ready; } else { @@ -58,7 +58,7 @@ Cloak::Cloak(std::string unit_key) : EnergyConsumer(EnergyType::Energy, current = 0; } -void Cloak::Save(std::map& unit) { +void Cloak::SaveToCSV(std::map& unit) const { unit["Cloak_Min"] = std::to_string(minimum); unit["Can_Cloak"] = std::to_string(Capable()); unit["Cloak_Rate"] = std::to_string(rate); @@ -66,6 +66,94 @@ void Cloak::Save(std::map& unit) { unit["Cloak_Glass"] = std::to_string(glass); } + +std::string Cloak::Describe() const { + return std::string(); +} + +bool Cloak::CanDowngrade() const { + if(integral) return false; + + // Nothing to downgrade + if(!Installed()) return false; + + // Other considerations - damaged?! + + return true; +} + +bool Cloak::Downgrade() { + if(!CanDowngrade()) { + return false; + } + + this->upgrade_name.clear(); + mass = 0; + volume = 0; + + status = CloakingStatus::disabled; + + energy = 0; + rate = 100; + glass = false; + current = 0; + minimum = 0; + + return true; +} + +bool Cloak::CanUpgrade(const std::string upgrade_name) const { + if(integral) { + return false; + } + + // Will allow swapping upgrades. + // TODO: make base_computer sell previous upgrade + + // Other considerations - damaged?! +} + +bool Cloak::Upgrade(const std::string upgrade_name) { + if(!CanUpgrade(upgrade_name)) { + return false; + } + + if(!UnitCSVFactory::HasUnit(upgrade_name)) { + return false; + } + + glass = UnitCSVFactory::GetVariable(upgrade_name, "Cloak_Glass", false); + rate = UnitCSVFactory::GetVariable(upgrade_name, "Cloak_Rate", 0.0); + energy = UnitCSVFactory::GetVariable(upgrade_name, "Cloak_Energy", 0.0); + minimum = UnitCSVFactory::GetVariable(upgrade_name, "Cloak_Min", 0.0); + minimum = std::min(1.0, std::max(0.0, minimum)); + current = 0; + + return true; +} + +// TODO: more granular damage +void Cloak::Damage() { + status = CloakingStatus::damaged; + current = 0; +} + +void Cloak::Repair() { + if(status == CloakingStatus::damaged) { + status = CloakingStatus::ready; + current = 0; + } +} + +bool Cloak::Damaged() const { + return (status == CloakingStatus::damaged); +} + +bool Cloak::Installed() const { + +} + + void Cloak::Update() { // Unit is not capable of cloaking or damaged or just not cloaking @@ -77,6 +165,7 @@ void Cloak::Update() // Insufficient energy to cloak ship if(powered < 1.0) { + std::cerr << "No power to cloak\n"; status = CloakingStatus::decloaking; } @@ -101,19 +190,22 @@ void Cloak::Update() void Cloak::Toggle() { // Unit is not capable of cloaking or damaged - if(status == CloakingStatus::disabled || + if(status == CloakingStatus::disabled || status == CloakingStatus::damaged) { + powered = 0.0; return; } // If we're ready start cloaking if(status == CloakingStatus::ready) { status = CloakingStatus::cloaking; + powered = energy; return; } // In any other case, start decloaking status = CloakingStatus::decloaking; + powered = 0.0; } void Cloak::Activate() { diff --git a/engine/src/components/cloak.h b/engine/src/components/cloak.h index d3ead8c70e..59b414171d 100644 --- a/engine/src/components/cloak.h +++ b/engine/src/components/cloak.h @@ -28,6 +28,7 @@ #include #include +#include "component.h" #include "energy_container.h" #include "damageable_layer.h" @@ -40,7 +41,7 @@ enum class CloakingStatus { decloaking }; -class Cloak : EnergyConsumer +class Cloak : public Component, public EnergyConsumer { friend class Unit; @@ -64,10 +65,28 @@ class Cloak : EnergyConsumer public: Cloak(); - Cloak(std::string unit_key); - void Save(std::map& unit); + // Virtual Functions + virtual void Load(std::string upgrade_key, std::string unit_key); + virtual void SaveToCSV(std::map& unit) const; + virtual std::string Describe() const; + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; + + // Other Functions void Update(); void Toggle(); // Toggle cloak on/off @@ -90,10 +109,6 @@ class Cloak : EnergyConsumer status == CloakingStatus::decloaking); } - bool Damaged() { - return (status == CloakingStatus::damaged); - } - bool Ready() { return (status == CloakingStatus::ready); } @@ -120,20 +135,6 @@ class Cloak : EnergyConsumer return 1-current; } - // TODO: more granular damage - // TODO: damageable component - void Damage() { - status = CloakingStatus::damaged; - current = 0; - } - - void Repair() { - if(status == CloakingStatus::damaged) { - status = CloakingStatus::ready; - current = 0; - } - } - void Disable() { status = CloakingStatus::disabled; current = 0; diff --git a/engine/src/components/component.h b/engine/src/components/component.h index a9f15a2d0c..4938954b4d 100644 --- a/engine/src/components/component.h +++ b/engine/src/components/component.h @@ -29,6 +29,7 @@ #define COMPONENT_H #include +#include /** * LibComponent is currently tightly coupled to LibDamage and @@ -63,7 +64,7 @@ class Component // Load from units dictionary virtual void Load(std::string upgrade_key, std::string unit_key); - //virtual std::string SaveToJSON() const = 0; // Save component as JSON + virtual void SaveToCSV(std::map& unit) const = 0; virtual std::string Describe() const = 0; // Describe component in base_computer diff --git a/engine/src/components/energy_consumer.h b/engine/src/components/energy_consumer.h index 4d4528a9a0..ba26deeeaf 100644 --- a/engine/src/components/energy_consumer.h +++ b/engine/src/components/energy_consumer.h @@ -38,7 +38,7 @@ class EnergyConsumer { EnergyConsumerClassification classification; EnergyConsumerType consumer_type; bool in_use; - double powered; + double powered; // 0.0-1.0 percentage powered Resource consumption; // This is when powered and for simulation_atom_var friend class EnergyContainer; diff --git a/engine/src/components/energy_container.cpp b/engine/src/components/energy_container.cpp index cbfc50ff0b..36001b1819 100644 --- a/engine/src/components/energy_container.cpp +++ b/engine/src/components/energy_container.cpp @@ -59,6 +59,10 @@ double EnergyContainer::Deplete(const double quantity) { return quantity + old_level - level.Value(); } +bool EnergyContainer::Depleted() const { + return (level.Value() < 0.01); +} + void EnergyContainer::SetCapacity(const double capacity, bool refill) { if(refill) { level = Resource(capacity,0,capacity); @@ -93,9 +97,7 @@ void EnergyContainer::Act() { continue; } - std::cout << TypeToSave(type) << " ol: " << level; level -= consumer.consumption; - std::cout << " nl: " << level << std::endl; consumer.powered = 1.0; } } @@ -125,3 +127,7 @@ double EnergyContainer::Powered(EnergyConsumerClassification classification) { return false; } + +void EnergyContainer::Refill() { + level.SetToMax(); +} \ No newline at end of file diff --git a/engine/src/components/energy_container.h b/engine/src/components/energy_container.h index ee3afe0acc..e14a64c716 100644 --- a/engine/src/components/energy_container.h +++ b/engine/src/components/energy_container.h @@ -41,6 +41,7 @@ */ class EnergyContainer { +protected: EnergyType type; Resource level; std::vector consumers; @@ -63,11 +64,13 @@ class EnergyContainer double Charge(const double quantity); double Deplete(const double quantity); + bool Depleted() const; - void SetCapacity(const double capacity, bool refill); + void SetCapacity(const double capacity, bool refill = true); double Level() const; double MaxLevel() const; double Percent() const; + void Refill(); void Zero(); diff --git a/engine/src/components/energy_manager.cpp b/engine/src/components/energy_manager.cpp index 33690b602d..5e558a1fa3 100644 --- a/engine/src/components/energy_manager.cpp +++ b/engine/src/components/energy_manager.cpp @@ -70,20 +70,19 @@ double getCloakConsumption(double factor, double mass) { //////////////////////////////////////////////////////// -EnergyManager::EnergyManager() { - fuel = EnergyContainer(EnergyType::Fuel, 0.0); - energy = EnergyContainer(EnergyType::Energy, 0.0); - spec_energy = EnergyContainer(EnergyType::FTL, 0.0); - reactor = Reactor(EnergyType::Fuel, 0.0, 0.0, 0.0, - &energy, &spec_energy, 0.1); -} +/*EnergyManager::EnergyManager(EnergyContainer *fuel, + EnergyContainer *energy, + EnergyContainer *spec_energy, + Reactor *reactor): + fuel(fuel), energy(energy), + ftl_energy(ftl_energy), reactor(reactor) {} void EnergyManager::Act() { - fuel.Act(); - double actual_reactor_usage = reactor.Generate(); - fuel.Charge(reactor.consumption - actual_reactor_usage); - energy.Act(); - spec_energy.Act(); + fuel->Act(); + double actual_reactor_usage = reactor->Generate(); + fuel->Charge(reactor->consumption - actual_reactor_usage); + energy->Act(); + ftl_energy->Act(); } void EnergyManager::AddConsumer(EnergyType energy_type, @@ -112,11 +111,11 @@ double EnergyManager::Deplete(EnergyConsumer consumer, const double quantity) { EnergyContainer* EnergyManager::GetContainer(const EnergyType type) { switch(type) { case EnergyType::Fuel: - return &fuel; + return fuel; case EnergyType::Energy: - return &energy; + return energy; case EnergyType::FTL: - return &spec_energy; + return ftl_energy; default: return nullptr; } @@ -125,11 +124,11 @@ EnergyContainer* EnergyManager::GetContainer(const EnergyType type) { const EnergyContainer* EnergyManager::GetConstContainer(const EnergyType type) const { switch(type) { case EnergyType::Fuel: - return &fuel; + return fuel; case EnergyType::Energy: - return &energy; + return energy; case EnergyType::FTL: - return &spec_energy; + return ftl_energy; } } @@ -169,4 +168,4 @@ void EnergyManager::SetReactor(const double capacity, const double usage_factor, this->reactor.consumption = Resource(c, 0.0, c); fuel.AddConsumer(reactor); -} \ No newline at end of file +}*/ diff --git a/engine/src/components/energy_manager.h b/engine/src/components/energy_manager.h index 691de08b1f..1cb5d89939 100644 --- a/engine/src/components/energy_manager.h +++ b/engine/src/components/energy_manager.h @@ -28,6 +28,7 @@ #ifndef ENERGYMANAGER_H #define ENERGYMANAGER_H +#include "component.h" #include "energy_types.h" #include "energy_container.h" #include "reactor.h" @@ -41,14 +42,16 @@ double getShieldMaintenanceConsumption(double factor, int facets, double max_str double getShieldRegenConsumption(double factor, int facets, double max_strength); // 10 double getCloakConsumption(double factor, double mass);*/ // 25 -class EnergyManager { - EnergyContainer fuel; - EnergyContainer energy; - EnergyContainer spec_energy; - Reactor reactor; +/*struct EnergyManager { + EnergyContainer *fuel; + EnergyContainer *energy; + EnergyContainer *ftl_energy; + Reactor *reactor; -public: - EnergyManager(); + EnergyManager(EnergyContainer *fuel, + EnergyContainer *energy, + EnergyContainer *ftl_energy, + Reactor *reactor); void Act(); @@ -71,11 +74,7 @@ class EnergyManager { void SetCapacity(const EnergyType type, const double capacity); void Refill(const EnergyType type); double Percent(const EnergyType type) const; - double GetReactorCapacity() {return reactor.capacity; } - void SetReactorCapacity(const double capacity) { reactor.capacity = capacity; } - void SetReactor(const double capacity, const double usage_factor, - const double reactor_level, const double simulation_atom_var); -}; +};*/ #endif // ENERGYMANAGER_H \ No newline at end of file diff --git a/engine/src/components/energy_types.h b/engine/src/components/energy_types.h index 68b53dc2ad..aba32f0024 100644 --- a/engine/src/components/energy_types.h +++ b/engine/src/components/energy_types.h @@ -55,8 +55,7 @@ enum class EnergyConsumerClassification { // Energy LifeSupport, Radar, - ShieldRegen, - ShieldMaintenance, + Shield, ECM, Cloak, BallWeapon, diff --git a/engine/src/components/fuel.cpp b/engine/src/components/fuel.cpp new file mode 100644 index 0000000000..e85cab1924 --- /dev/null +++ b/engine/src/components/fuel.cpp @@ -0,0 +1,92 @@ +/* + * fuel.cpp + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "fuel.h" + +#include "unit_csv_factory.h" + +#include + +const std::string FUEL_CAPACITY = "Fuel_Capacity"; + +Fuel::Fuel(): Component("", 0.0, 0.0, true), + EnergyContainer(EnergyType::Fuel, 0.0) {} + +void Fuel::Load(std::string upgrade_key, std::string unit_key) { + // Component + upgrade_name = "Fuel"; + upgrade_key = ""; + + // TODO: nice to have - ship mass goes down as fuel depleted + mass = 0; + volume = 0; + + double fuel_capacity = UnitCSVFactory::GetVariable(unit_key, FUEL_CAPACITY, 1.0); + SetCapacity(fuel_capacity); +} + +void Fuel::SaveToCSV(std::map& unit) const { + unit[FUEL_CAPACITY] = std::to_string(MaxLevel()); +} + +std::string Fuel::Describe() const { + return std::string(); +} + +bool Fuel::CanDowngrade() const { + return false; +} + +bool Fuel::Downgrade() { + return false; +} + +bool Fuel::CanUpgrade(const std::string upgrade_name) const { + return false; +} + +bool Fuel::Upgrade(const std::string upgrade_name) { + return false; +} + + +void Fuel::Damage() { + level.RandomDamage(); +} + +void Fuel::Repair() { + level.RepairFully(); +} + +bool Fuel::Damaged() const { + return level.Damaged(); +} + + +bool Fuel::Installed() const { + return true; +} \ No newline at end of file diff --git a/engine/src/components/fuel.h b/engine/src/components/fuel.h new file mode 100644 index 0000000000..bf01d549bf --- /dev/null +++ b/engine/src/components/fuel.h @@ -0,0 +1,63 @@ +/* + * fuel.h + * + * Copyright (c) 2001-2002 Daniel Horn + * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors + * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . + */ + +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#ifndef VEGA_STRIKE_ENGINE_COMPONENTS_FUEL_H +#define VEGA_STRIKE_ENGINE_COMPONENTS_FUEL_H + +#include "component.h" +#include "energy_container.h" + +/** + * This is a minimum implementation of fuel cell class. + * For now, a fuel cell cannot be upgraded, only repaired. + */ +class Fuel : public Component, public EnergyContainer { + friend class Unit; +public: + Fuel(); + + virtual void Load(std::string upgrade_key, std::string unit_key); // Load from dictionary + virtual void SaveToCSV(std::map& unit) const; + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; +}; + +#endif // VEGA_STRIKE_ENGINE_COMPONENTS_FUEL_H \ No newline at end of file diff --git a/engine/src/components/hull.cpp b/engine/src/components/hull.cpp index a111efce6a..4822974d39 100644 --- a/engine/src/components/hull.cpp +++ b/engine/src/components/hull.cpp @@ -29,6 +29,8 @@ #include "unit_csv_factory.h" +const std::string HULL = "Hull"; + Hull::Hull(): Component("", 0.0, 0.0, true), DamageableLayer(0, FacetConfiguration::one, Health(0, 1), true) {} @@ -49,8 +51,10 @@ void Hull::Load(std::string upgrade_key, std::string unit_key, facets[0].health.Set(hull_current); } -std::string Hull::SaveToJSON() const { - return std::string(); +void Hull::SaveToCSV(std::map& unit) const { + for(int i=0;i<8;i++) { + unit[HULL] = std::to_string(facets[0].health.Value()); + } } std::string Hull::Describe() const { diff --git a/engine/src/components/hull.h b/engine/src/components/hull.h index 694b83f394..de4fd152af 100644 --- a/engine/src/components/hull.h +++ b/engine/src/components/hull.h @@ -44,7 +44,7 @@ class Hull : public Component, public DamageableLayer { virtual void Load(std::string upgrade_key, std::string unit_key, Unit *unit); // Load from dictionary - virtual std::string SaveToJSON() const; // Save component as JSON + virtual void SaveToCSV(std::map& unit) const; virtual std::string Describe() const; // Describe component in base_computer diff --git a/engine/src/components/reactor.cpp b/engine/src/components/reactor.cpp index 275fee172b..69f9b1e4a9 100644 --- a/engine/src/components/reactor.cpp +++ b/engine/src/components/reactor.cpp @@ -26,46 +26,117 @@ // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- #include "reactor.h" + +#include "energy_manager.h" +#include "unit_csv_factory.h" + #include -Reactor::Reactor(): EnergyConsumer(EnergyType::None, - EnergyConsumerClassification::Reactor, - EnergyConsumerType::Constant, - 0.0), +const std::string REACTOR_RECHARGE = "Reactor_Recharge"; + +Reactor::Reactor(): Component("", 0.0, 0.0, false), + EnergyConsumer(EnergyType::Fuel, + EnergyConsumerClassification::Reactor, + EnergyConsumerType::Constant, 0.0), capacity(0.0), - energy_container(nullptr), - spec_energy_container(nullptr) {} + simulation_atom_var(0.1), + atom_capacity(0.0), + energy(nullptr), + ftl_energy(nullptr) {} -Reactor::Reactor(EnergyType energy_type, double usage_factor, double reactor_level, - double capacity, - EnergyContainer *energy_container, - EnergyContainer *spec_energy_container, +Reactor::Reactor(double capacity, + EnergyContainer *energy, + EnergyContainer *ftl_energy, double simulation_atom_var): - EnergyConsumer(energy_type, - EnergyConsumerClassification::Reactor, - EnergyConsumerType::Constant, - 0.0), + Component("", 0.0, 0.0, false), + EnergyConsumer(EnergyType::Fuel, + EnergyConsumerClassification::Reactor, + EnergyConsumerType::Constant, 0.0), capacity(capacity), - energy_container(energy_container), - spec_energy_container(spec_energy_container) { - double c = usage_factor * reactor_level * simulation_atom_var; - this->consumption = Resource(c, 0.0, c); + simulation_atom_var(simulation_atom_var), + atom_capacity(capacity * simulation_atom_var), + energy(energy), + ftl_energy(ftl_energy) { } -double Reactor::Generate() { - double actual_consumption = 0.0; - double surplus = energy_container->Charge(capacity); - surplus = spec_energy_container->Charge(surplus); - surplus = capacity; - - if(capacity == 0.0) { - // We generate nothing and so consume no fuel - actual_consumption = 0.0; - } else if(surplus == 0.0) { - actual_consumption = consumption.MaxValue(); - } else { - actual_consumption = (1 - (surplus / capacity)) * consumption.MaxValue(); + +void Reactor::Load(std::string upgrade_key, std::string unit_key) { + capacity = UnitCSVFactory::GetVariable(unit_key, REACTOR_RECHARGE, 0.0f); + atom_capacity = capacity * simulation_atom_var; +} + +void Reactor::SaveToCSV(std::map& unit) const { + // TODO: This won't record damage to recharge + unit[REACTOR_RECHARGE] = std::to_string(capacity.MaxValue()); +} + +std::string Reactor::Describe() const { + return std::string(); +} + +bool Reactor::CanDowngrade() const { + return !Damaged(); +} + +bool Reactor::Downgrade() { + if(!CanDowngrade()) { + return false; + } + + capacity.SetMaxValue(0.0); + + return true; +} + +bool Reactor::CanUpgrade(const std::string upgrade_name) const { + return !Damaged(); +} + +bool Reactor::Upgrade(const std::string upgrade_name) { + if(!CanUpgrade(upgrade_key)) { + return false; } - return actual_consumption; + this->upgrade_key = upgrade_key; + this->upgrade_name = upgrade_name; + + capacity = UnitCSVFactory::GetVariable(upgrade_name, REACTOR_RECHARGE, 0.0f); + atom_capacity = capacity * simulation_atom_var; + + return true; +} + +void Reactor::Damage() { + capacity.RandomDamage(); + atom_capacity = capacity * simulation_atom_var; +} + +void Reactor::Repair() { + capacity.RepairFully(); +} + +bool Reactor::Damaged() const { + return capacity.Damaged(); +} + +bool Reactor::Installed() const { + return capacity.MaxValue() > 0; +} + +void Reactor::Generate() { + // Adjust for available power + double real_capacity = atom_capacity * powered; + + double surplus = energy->Charge(real_capacity); + surplus = ftl_energy->Charge(surplus); + + double actual_consumption = atom_capacity - surplus; +} + +double Reactor::Capacity() const { + return capacity.Value(); +} + +double Reactor::MaxCapacity() const { + return capacity.MaxValue(); } \ No newline at end of file diff --git a/engine/src/components/reactor.h b/engine/src/components/reactor.h index 9e736caef0..799d2a7f96 100644 --- a/engine/src/components/reactor.h +++ b/engine/src/components/reactor.h @@ -28,26 +28,53 @@ #ifndef REACTOR_H #define REACTOR_H +#include "component.h" #include "energy_container.h" #include "energy_consumer.h" +#include "resource/resource.h" -class Reactor: public EnergyConsumer +class EnergyManager; + +class Reactor: public Component, public EnergyConsumer { - double capacity; - EnergyContainer *energy_container; - EnergyContainer *spec_energy_container; + Resource capacity; // Capacity per second + const double simulation_atom_var; // Atom definition. typically 0.1 second + double atom_capacity; // Capacity per atom + + EnergyContainer *energy; + EnergyContainer *ftl_energy; - friend class EnergyManager; public: Reactor(); - Reactor(EnergyType energy_type, - double usage_factor, // A general factor to tweak consumption (fixed per game) - double reactor_level, // A measure of the reactor level (variable between models) - double capacity, // How much energy does the reactor generate - EnergyContainer *energy_container, - EnergyContainer *spec_energy_container, - double simulation_atom_var); - double Generate(); + Reactor(double capacity, // How much energy does the reactor generate + EnergyContainer *energy, + EnergyContainer *ftl_energy, + double simulation_atom_var); + + + virtual void Load(std::string upgrade_key, std::string unit_key); + + virtual void SaveToCSV(std::map& unit) const; + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; + + void Generate(); + double Capacity() const; + double MaxCapacity() const; }; #endif // REACTOR_H diff --git a/engine/src/components/shield.cpp b/engine/src/components/shield.cpp index 6d472c2e11..b5c5a39be0 100644 --- a/engine/src/components/shield.cpp +++ b/engine/src/components/shield.cpp @@ -29,9 +29,12 @@ #include "unit_csv_factory.h" #include "damage/damageable_layer.h" #include "resource/cout_util.h" +#include "configuration/game_config.h" #include +extern float simulation_atom_var; + const std::string SHIELD_RECHARGE = "Shield_Recharge"; std::string shield_facets_eight[8] = { @@ -64,19 +67,22 @@ Shield::Shield(): Component("", 0.0, 0.0, false), DamageableLayer(2, FacetConfiguration::zero, Health(2, 0), false), + EnergyConsumer(EnergyType::Energy, + EnergyConsumerClassification::Shield, + EnergyConsumerType::Constant, 0.0), regeneration(0,0,0), power(1.0,0.0,1.0) {} void Shield::Load(std::string upgrade_key, std::string unit_key, - Unit *unit) { + Unit *unit, double difficulty) { //this->upgrade_key = upgrade_key; //upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); //int num_facets = UnitCSVFactory::GetVariable(upgrade_key, "Facets", 0); - + // Regeneration - const double regeneration = UnitCSVFactory::GetVariable(unit_key, SHIELD_RECHARGE, 0.0); + const double regeneration = UnitCSVFactory::GetVariable(unit_key, SHIELD_RECHARGE, 0.0) * difficulty; // Get shield count @@ -96,7 +102,7 @@ void Shield::Load(std::string upgrade_key, std::string unit_key, continue; } - shield_values.push_back(std::stod(shield_string_values[i])); + shield_values.push_back(std::stod(shield_string_values[i]) * difficulty); // Should add up to the shield type - quad or dual shield_count++; @@ -116,6 +122,11 @@ void Shield::Load(std::string upgrade_key, std::string unit_key, this->regeneration.SetMaxValue(regeneration); // TODO: shield leakage & efficiency + + // Power draw for maintenance and regeneration + // TODO: implement fully + double c = TotalMaxLayerValue()/10 + regeneration; + consumption.SetMaxValue(c); } @@ -186,7 +197,7 @@ bool Shield::Downgrade() { std::vector empty_vector; UpdateFacets(empty_vector); - return false; + return true; } bool Shield::Upgrade(const std::string upgrade_key) { @@ -342,15 +353,44 @@ double Shield::GetRegeneration() const { return regeneration.Value(); } -void Shield::Regenerate() { +// TODO: add nebula parameter and apply some modifier +void Shield::Regenerate(bool ftl, bool player_ship) { + // No point in all this code if there are no shields. + if(!Installed()) { + return; + } + + // No point if shields are disabled. e.g. when cloaked + if(!Enabled()) { + return; + } + + double actual_recharge = regeneration.Value() * powered * simulation_atom_var; + // TODO: adjust for nebula + + // Discharge shields due to energy or SPEC or cloak + if (ftl && !shield_in_ftl) { + if(player_ship) { + static int i = 0; + printOnceInAHundred(i, "FTL", std::to_string(actual_recharge)); + } + + // "Damage" power + SetPowerCap(0.0); + } else { + // Figure out how to support partial power + SetPowerCap(1.0); + } + + // Shield regeneration for(Health& facet : facets) { if(facet.health.Percent() < power) { // If shield generator is damaged, regenerate less - facet.health += regeneration.Value(); + facet.health += actual_recharge; } else if(facet.health.Percent() > power) { // If in SPEC or cloaked, decrease shields as fast as possible // to prevent a scenario where damaged shields work in SPEC. - facet.health -= regeneration.MaxValue(); + facet.health -= actual_recharge; } } } \ No newline at end of file diff --git a/engine/src/components/shield.h b/engine/src/components/shield.h index bc83cad84c..936b0d187e 100644 --- a/engine/src/components/shield.h +++ b/engine/src/components/shield.h @@ -33,6 +33,7 @@ #include "component.h" #include "damageable_layer.h" +#include "energy_consumer.h" class Unit; @@ -41,19 +42,44 @@ class Unit; * DamageableLayer and Facet do not handle these at all. * Damage to the shield generator is handled here. */ -class Shield : public Component, public DamageableLayer { +class Shield : public Component, public DamageableLayer, public EnergyConsumer { Resource regeneration; Resource power; // 1.0 Full, 0.66 Two thirds, 0.0 Suppressed (FTL) or turned off // TODO: implement "shield leaks" aka // TODO: implement opacity //Resource opacity; + + // TODO: + //const bool shields_in_spec = configuration()->physics_config.shields_in_spec; + //const float discharge_per_second = configuration()->physics_config.speeding_discharge; + //const float min_shield_discharge = configuration()->physics_config.min_shield_speeding_discharge; + //const float nebshields = configuration()->physics_config.nebula_shield_recharge; + + // Initialization of this should happen somewhere like main + // Note that this implementation doesn't work right for asymmetric shields. + // It would apply the same discharge value to all facets. + // Currently disabled and applies regen value instead. + static const bool shield_in_ftl = false; + //static const double percent_shield_discharge_per_second = 0.5; + //double atom_shield_discharge_per_second = 0.1; + + // TODO: Nebula shields + // Shields are supposed to recharge slower in a nebula + + // Recharge energy and shields + // Currently disabled and applies only to shield regen + // Think about this some more + // + const bool difficulty_modifier = 1.0; + + friend class Damageable; public: Shield(); virtual void Load(std::string upgrade_key, std::string unit_key, - Unit *unit); // Load from dictionary + Unit *unit, double difficulty); // Load from dictionary virtual void SaveToCSV(std::map& unit) const; virtual std::string Describe() const; // Describe component in base_computer @@ -76,7 +102,7 @@ class Shield : public Component, public DamageableLayer { bool Enabled() const; void Enhance(); // see collision enhancement double GetRegeneration() const; - void Regenerate(); + void Regenerate(bool ftl, bool player_ship); double GetPower() const; double GetPowerCap() const; diff --git a/engine/src/components/tests/balancing_tests.cpp b/engine/src/components/tests/balancing_tests.cpp index 6afd2b0b8e..6e8c95ffde 100644 --- a/engine/src/components/tests/balancing_tests.cpp +++ b/engine/src/components/tests/balancing_tests.cpp @@ -3,22 +3,26 @@ #include "energy_container.h" #include "energy_manager.h" #include "reactor.h" +#include "fuel.h" double simulation_atom_var = 0.1; -struct ReactorSetup { +struct EnergySetup { double capacity; - double level; - double usage_factor; -}; - -struct ContainersSetup { double fuel_capacity; double energy_capacity; - double spec_capacity; + double ftl_capacity; + + EnergySetup(double capacity, double fuel_capacity, + double energy_capacity, + double ftl_capacity): + capacity(capacity), fuel_capacity(fuel_capacity), + energy_capacity(energy_capacity), ftl_capacity(ftl_capacity) {} }; + + struct ConsumerSetup { EnergyType energy_type; EnergyConsumerClassification classification; @@ -27,7 +31,6 @@ struct ConsumerSetup { }; double reactor_capacity = 15; -double reactor_level = 1.0; double fuel_capacity = 3.51; // Robin double energy_capacity = 100.0; // capacitor 1 @@ -54,58 +57,66 @@ double Cloak = 10; double SPECDrive = 1; double JumpDrive = 1; -EnergyManager setup(ContainersSetup containers_setup, ReactorSetup reactor_setup, - double simulation_atom_var) { - EnergyManager manager = EnergyManager(); - - manager.SetCapacity(EnergyType::Fuel, containers_setup.fuel_capacity); - manager.SetCapacity(EnergyType::Energy, containers_setup.energy_capacity); - manager.SetCapacity(EnergyType::FTL, containers_setup.spec_capacity); - manager.SetReactor(reactor_setup.capacity, reactor_setup.usage_factor, - reactor_setup.level, simulation_atom_var); - - return manager; -} +struct EnergyManager { + Fuel fuel; + EnergyContainer energy; + EnergyContainer ftl_energy; + Reactor reactor; + + EnergyManager(EnergySetup setup, + double simulation_atom_var): + fuel(), energy(), ftl_energy(), + reactor(setup.capacity, &energy, &ftl_energy, + simulation_atom_var) { + fuel.SetCapacity(setup.fuel_capacity); + energy.SetCapacity(setup.energy_capacity); + ftl_energy.SetCapacity(setup.ftl_capacity); + } +}; -void setupConsumer(EnergyManager& manager, ConsumerSetup setup) { - manager.AddConsumer(setup.energy_type, setup.classification, setup.consumer_type, setup.consumption); -} -double fuelBurn(ContainersSetup containers_setup, ReactorSetup reactor_setup, - std::vector consumers_setup, int seconds, +double fuelBurn(EnergySetup setup, + std::vector energy_consumers, int seconds, int print_every_n = 1000) { int run_time = seconds / simulation_atom_var; - EnergyManager manager = setup(containers_setup, reactor_setup, simulation_atom_var); - for(ConsumerSetup setup : consumers_setup) { - setupConsumer(manager, setup); + EnergyManager manager = EnergyManager(setup, simulation_atom_var); + + // Add fuel consumers + + + // Add energy consumers + for(EnergyConsumer consumer : energy_consumers) { + manager.energy.AddConsumer(consumer); } for(int i = 0;i(), + double result = fuelBurn(setup, std::vector(), seconds); std::cout << "NoFuelBurn percent left: " << result * 100 << std::endl; @@ -113,42 +124,38 @@ TEST(NoFuelBurn, Robin) { // This tests a fighter ship with level 1 equipment and steady 50MJ energy consumption TEST(FuelBurn, RobinNaive_1) { - ContainersSetup containers_setup = {3.51, 100.0, 200.0}; - ReactorSetup reactor_setup = {15.0, reactor_usage_factor, 1.0}; - std::vector consumers_setup = { - {EnergyType::Fuel, EnergyConsumerClassification::Drive, - EnergyConsumerType::Constant, 0.0001}, - {EnergyType::Fuel, EnergyConsumerClassification::Afterburner, - EnergyConsumerType::Constant, 0.0001 * 3 * .05}, // Drive consumption x 3 but 5% of flight time - {EnergyType::Energy, EnergyConsumerClassification::LifeSupport, - EnergyConsumerType::Constant, 50.0 * simulation_atom_var} // General consumer at 50 per second + EnergySetup setup = {15.0, 3.51, 100.0, 200.0}; + std::vector consumers_setup = { + EnergyConsumer(EnergyType::Fuel, EnergyConsumerClassification::Drive, + EnergyConsumerType::Constant, 0.0001), + EnergyConsumer(EnergyType::Fuel, EnergyConsumerClassification::Afterburner, + EnergyConsumerType::Constant, 0.0001 * 3 * .05), // Drive consumption x 3 but 5% of flight time + EnergyConsumer(EnergyType::Energy, EnergyConsumerClassification::LifeSupport, + EnergyConsumerType::Constant, 50.0 * simulation_atom_var) // General consumer at 50 per second }; int seconds = 60 * 60; // 60 minutes gameplay - double result = fuelBurn(containers_setup, reactor_setup, consumers_setup, - seconds); + double result = fuelBurn(setup, consumers_setup, seconds); std::cout << "NaiveFuelBurn_1 percent left: " << result * 100 << std::endl; } // This tests a fighter ship with level 1 equipment and steady 150MJ energy consumption TEST(FuelBurn, RobinNaive_2) { - ContainersSetup containers_setup = {3.51, 300.0, 200.0}; - ReactorSetup reactor_setup = {44.0, reactor_usage_factor, 3.0}; - std::vector consumers_setup = { - {EnergyType::Fuel, EnergyConsumerClassification::Drive, - EnergyConsumerType::Constant, 0.0001}, - {EnergyType::Fuel, EnergyConsumerClassification::Afterburner, - EnergyConsumerType::Constant, 0.0001 * 3 * .05}, // Drive consumption x 3 but 5% of flight time - {EnergyType::Energy, EnergyConsumerClassification::LifeSupport, - EnergyConsumerType::Constant, 150.0 * simulation_atom_var} // General consumer at 150 per second + EnergySetup setup = {44.0, 3.51, 300.0, 200.0}; + std::vector consumers_setup = { + EnergyConsumer(EnergyType::Fuel, EnergyConsumerClassification::Drive, + EnergyConsumerType::Constant, 0.0001), + EnergyConsumer(EnergyType::Fuel, EnergyConsumerClassification::Afterburner, + EnergyConsumerType::Constant, 0.0001 * 3 * .05), // Drive consumption x 3 but 5% of flight time + EnergyConsumer(EnergyType::Energy, EnergyConsumerClassification::LifeSupport, + EnergyConsumerType::Constant, 150.0 * simulation_atom_var) // General consumer at 50 per second }; int seconds = 60 * 60; // 60 minutes gameplay - double result = fuelBurn(containers_setup, reactor_setup, consumers_setup, - seconds); + double result = fuelBurn(setup, consumers_setup, seconds); std::cout << "NaiveFuelBurn_2 percent left: " << result * 100 << std::endl; } \ No newline at end of file diff --git a/engine/src/gfx/cockpit.cpp b/engine/src/gfx/cockpit.cpp index 81f8df07c5..567342ac1e 100644 --- a/engine/src/gfx/cockpit.cpp +++ b/engine/src/gfx/cockpit.cpp @@ -418,10 +418,10 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { return .25 * (armordat[0] + armordat[2] + armordat[4] + armordat[6]); } case UnitImages::FUEL: - return target->energy_manager.Percent(EnergyType::Fuel); + return target->fuel.Percent(); case UnitImages::ENERGY: - return target->energy_manager.Percent(EnergyType::Energy); + return target->energy.Percent(); case UnitImages::WARPENERGY: { // TODO: check how WC deals with this. @@ -432,7 +432,7 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { const bool warpifnojump = configuration()->graphics_config.hud.display_warp_energy_if_no_jump_drive; return (warpifnojump || target->jump.drive != -2) ? - target->energy_manager.Percent(EnergyType::FTL) : 0; + target->ftl_energy.Percent() : 0; } case UnitImages::HULL: if (maxhull < target->GetHull()) { @@ -730,7 +730,7 @@ float GameCockpit::LookupUnitStat(int stat, Unit *target) { case UnitImages::CANJUMP_MODAL: if (-2 == target->jump.drive) { return (float) UnitImages::NODRIVE; - } else if (target->energy_manager.GetLevel(EnergyType::FTL) < target->jump.energy) { + } else if (target->ftl_energy.Level() < target->jump.energy) { return (float) UnitImages::NOTENOUGHENERGY; } else if (target->graphicOptions.InWarp) { //FIXME return (float) UnitImages::OFF; diff --git a/engine/src/resource/cout_util.h b/engine/src/resource/cout_util.h index a285a9e2a7..eb5142e094 100644 --- a/engine/src/resource/cout_util.h +++ b/engine/src/resource/cout_util.h @@ -43,5 +43,15 @@ void printOnceInAHundred(int &i, if(i==100) i=0; } +void printOnceInAHundred(int &i, + std::string message, + std::string value) { + if(i==0) { + printPlayerMessage("player_ship",message,value); + } + i++; + if(i==100) i=0; +} + #endif //VEGA_STRIKE_ENGINE_RESOURCE_COUT_UTIL_H From 7f53ea978e818dd05df9043585b7cf2bc7088bfb Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Thu, 18 Apr 2024 17:40:56 +0300 Subject: [PATCH 16/16] Implement afterburners and Drive Implement energy_container and consumer. Remove redundant files - energy_types, energy_manager and fuel. --- engine/CMakeLists.txt | 3 - engine/src/cmd/armed.cpp | 19 +- engine/src/cmd/damageable.cpp | 2 +- engine/src/cmd/jump_capable.cpp | 9 +- engine/src/cmd/jump_capable.h | 2 - engine/src/cmd/mount.cpp | 10 +- engine/src/cmd/mount.h | 2 +- engine/src/cmd/movable.cpp | 33 ++-- engine/src/cmd/movable.h | 4 - engine/src/cmd/unit_csv.cpp | 17 +- engine/src/cmd/unit_generic.cpp | 16 +- engine/src/cmd/unit_generic.h | 36 ++-- engine/src/cmd/upgradeable_unit.cpp | 47 ++++- engine/src/cmd/upgradeable_unit.h | 4 + engine/src/cmd/weapon_info.cpp | 15 +- engine/src/components/afterburner.cpp | 81 +++++++-- engine/src/components/afterburner.h | 52 +++++- engine/src/components/cloak.cpp | 27 ++- engine/src/components/cloak.h | 2 + engine/src/components/component.h | 4 +- engine/src/components/drive.cpp | 64 +++++-- engine/src/components/drive.h | 31 +++- engine/src/components/energy_consumer.cpp | 40 +++- engine/src/components/energy_consumer.h | 35 ++-- engine/src/components/energy_container.cpp | 154 +++++++++++++--- engine/src/components/energy_container.h | 61 ++++--- engine/src/components/energy_manager.cpp | 171 ------------------ engine/src/components/energy_manager.h | 80 -------- engine/src/components/energy_types.cpp | 46 ----- engine/src/components/energy_types.h | 77 -------- engine/src/components/fuel.cpp | 92 ---------- engine/src/components/fuel.h | 63 ------- engine/src/components/jump_drive.cpp | 63 +++++-- engine/src/components/jump_drive.h | 29 ++- engine/src/components/radar.cpp | 163 ++++++++++------- engine/src/components/radar.h | 55 ++++-- engine/src/components/reactor.cpp | 55 +++--- engine/src/components/reactor.h | 12 +- engine/src/components/shield.cpp | 17 +- engine/src/components/shield.h | 2 +- engine/src/components/tests/armor_tests.cpp | 2 + .../src/components/tests/balancing_tests.cpp | 140 +++++++------- .../tests/energy_container_tests.cpp | 14 +- engine/src/damage/tests/layer_tests.cpp | 14 +- 44 files changed, 904 insertions(+), 961 deletions(-) delete mode 100644 engine/src/components/energy_manager.cpp delete mode 100644 engine/src/components/energy_manager.h delete mode 100644 engine/src/components/energy_types.cpp delete mode 100644 engine/src/components/energy_types.h delete mode 100644 engine/src/components/fuel.cpp delete mode 100644 engine/src/components/fuel.h diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index ce8f2e10a0..f6fddfeec2 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -661,7 +661,6 @@ SET(LIBRESOURCE SET(LIBCOMPONENT src/components/component.cpp - src/components/energy_types.cpp src/components/armor.cpp src/components/hull.cpp @@ -672,8 +671,6 @@ SET(LIBCOMPONENT src/components/drive.cpp src/components/energy_consumer.cpp src/components/energy_container.cpp - src/components/energy_manager.cpp - src/components/fuel.cpp src/components/jump_drive.cpp src/components/radar.cpp src/components/reactor.cpp diff --git a/engine/src/cmd/armed.cpp b/engine/src/cmd/armed.cpp index fc9b6e8dea..60db3cb60c 100644 --- a/engine/src/cmd/armed.cpp +++ b/engine/src/cmd/armed.cpp @@ -250,15 +250,18 @@ void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { bool want_to_fire = (fire_non_autotrackers || autotracking_gun || locked_missile) && //&& ( (ROLES::EVERYTHING_ELSE&weapon_type_bitmask&i->type->role_bits) || i->type->role_bits == 0 ) ((locked_on && missile_and_want_to_fire_missiles) || gun_and_want_to_fire_guns); - if ((*i).type->type == WEAPON_TYPE::BEAM) { - if ((*i).type->GetConsumption() * simulation_atom_var > unit->energy.Level()) { + + double power = (*i).type->Consume(); + + if ((*i).type->type == WEAPON_TYPE::BEAM) { + if (power < 1.0) { //NOT ONLY IN non-networking mode : anyway, the server will tell everyone including us to stop if not already done (*i).UnFire(); continue; } } else //Only in non-networking mode - if (i->type->GetConsumption() > unit->energy.Level()) { + if (power < 1.0) { if (!want_to_fire) { i->UnFire(); } @@ -275,7 +278,11 @@ void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { //info the server sends with ack for fire //FOR NOW WE TRUST THE CLIENT SINCE THE SERVER CAN REFUSE A FIRE //if( Network==NULL || SERVER) - if (i->type->type == WEAPON_TYPE::BEAM) { + + + // Disabled all the code below. Consume() should deal with it above. + // TODO: do something here. Test? + /*if (i->type->type == WEAPON_TYPE::BEAM) { if (i->ref.gun) { if ((!i->ref.gun->Dissolved()) || i->ref.gun->Ready()) { // TODO: switch to standard energy usage @@ -284,7 +291,7 @@ void Armed::Fire(unsigned int weapon_type_bitmask, bool listen_to_owner) { } } else if (i->type->isMissile()) { // FIXME other than beams, only missiles are processed here? unit->energy.Deplete(i->type->GetConsumption()); - } + }*/ //IF WE REFRESH ENERGY FROM SERVER : Think to send the energy update to the firing client with ACK TO fireRequest //fire only 1 missile at a time if (mis) { @@ -455,7 +462,7 @@ float Armed::TrackingGuns(bool &missilelock) { missilelock = false; for (int i = 0; i < getNumMounts(); ++i) { if (mounts[i].status == Mount::ACTIVE && isAutoTrackingMount(mounts[i].size)) { - trackingcone = unit->radar.tracking_cone; + trackingcone = unit->radar.tracking_cone.Value(); } if (mounts[i].status == Mount::ACTIVE && mounts[i].type->lock_time > 0 && mounts[i].time_to_lock <= 0) { missilelock = true; diff --git a/engine/src/cmd/damageable.cpp b/engine/src/cmd/damageable.cpp index 6308598dc8..004c63cf4d 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -47,7 +47,7 @@ Damageable::Damageable() : DamageableObject(), killed(false), hull_(), armor_(), - shield_() { + shield_(nullptr) { // TODO: initialize elsewhere hull = &hull_; armor = &armor_; shield = &shield_; diff --git a/engine/src/cmd/jump_capable.cpp b/engine/src/cmd/jump_capable.cpp index fce8611e13..d79ce6d562 100644 --- a/engine/src/cmd/jump_capable.cpp +++ b/engine/src/cmd/jump_capable.cpp @@ -98,8 +98,7 @@ std::string GenerateAutoError(Unit *me, Unit *targ) { /////////////////////////////////////////////// -JumpCapable::JumpCapable() : activeStarSystem(nullptr), - jump_drive() {}; +JumpCapable::JumpCapable() : activeStarSystem(nullptr) {}; void JumpCapable::ActivateJumpDrive(int destination) { Unit *unit = static_cast(this); @@ -141,7 +140,8 @@ bool JumpCapable::AutoPilotToErrorMessage(const Unit *target, // TODO: move to energymanager.canpower and // energycontainer.canpower - if (unit->ftl_energy.Level() < jump_drive.GetConsumption()) { + double power = unit->jump_drive.Consume(); + if (power < 1.0) { if (!ignore_energy_requirements) { return false; } @@ -275,7 +275,8 @@ bool JumpCapable::AutoPilotToErrorMessage(const Unit *target, return false; } - jump_drive.Use(); + // TODO: make sure we don't consume energy before here. + // See line 143 above if (unsafe == false && totpercent == 0) { end = endne; diff --git a/engine/src/cmd/jump_capable.h b/engine/src/cmd/jump_capable.h index 18bc8bbb43..b91aaae56f 100644 --- a/engine/src/cmd/jump_capable.h +++ b/engine/src/cmd/jump_capable.h @@ -25,7 +25,6 @@ #define VEGA_STRIKE_ENGINE_CMD_JUMP_CAPABLE_H #include "star_system.h" -#include "components/jump_drive.h" #include @@ -34,7 +33,6 @@ class JumpCapable { public: StarSystem *activeStarSystem; - JumpDrive jump_drive; public: struct UnitJump { diff --git a/engine/src/cmd/mount.cpp b/engine/src/cmd/mount.cpp index 30408133fd..e647566e47 100644 --- a/engine/src/cmd/mount.cpp +++ b/engine/src/cmd/mount.cpp @@ -253,8 +253,10 @@ bool Mount::PhysicsAlignedFire(Unit *caller, if (type->type == WEAPON_TYPE::BEAM || type->isMissile()) { //Missiles and beams set to processed. processed = PROCESSED; - } else if (ref.refire < type->Refire() || - type->GetConsumption() > caller->energy.Level()) { + } else if (ref.refire < type->Refire()) { + + // TODO: something here + //type->GetConsumption() > caller->energy.Level()) { //Wait until refire has expired and reactor has produced enough energy for the next bolt. return true; } //Not ready to refire yet. But don't stop firing. @@ -302,7 +304,7 @@ bool Mount::PhysicsAlignedFire(Unit *caller, } break; case WEAPON_TYPE::BOLT: - caller->energy.Deplete(type->GetConsumption()); + //caller->energy.Deplete(type->GetConsumption()); hint[Unit::UNIT_BOLT] = Bolt(type, mat, @@ -312,7 +314,7 @@ bool Mount::PhysicsAlignedFire(Unit *caller, break; case WEAPON_TYPE::BALL: { - caller->energy.Deplete(type->GetConsumption()); + //caller->energy.Deplete(type->GetConsumption()); hint[Unit::UNIT_BOLT] = BoltDrawManager::GetInstance().AddBall(type, mat, velocity, owner, hint[Unit::UNIT_BOLT]); diff --git a/engine/src/cmd/mount.h b/engine/src/cmd/mount.h index d25d4bd9dc..69b6f5adf4 100644 --- a/engine/src/cmd/mount.h +++ b/engine/src/cmd/mount.h @@ -74,7 +74,7 @@ class Mount { status; bool bank; //bank implies whether the weapon is linked with the next mount (i.e. only one firing at a time) - const WeaponInfo *type; + WeaponInfo *type; float functionality; float maxfunctionality; int sound; diff --git a/engine/src/cmd/movable.cpp b/engine/src/cmd/movable.cpp index 4d5891f291..a60e63c8f0 100644 --- a/engine/src/cmd/movable.cpp +++ b/engine/src/cmd/movable.cpp @@ -55,10 +55,7 @@ Movable::Movable() : cumulative_transformation_matrix(identity_matrix), corner_min(Vector(FLT_MAX, FLT_MAX, FLT_MAX)), corner_max(Vector(-FLT_MAX, -FLT_MAX, -FLT_MAX)), radial_size(0), - Momentofinertia(0.01), - // TODO: make drive and afterburner parameters configurable somehow - drive(EnergyType::Fuel, 1.0, 1.0, 1.0, 0.1), - afterburner(EnergyType::Fuel, 3.0) { + Momentofinertia(0.01) { cur_sim_queue_slot = rand() % SIM_QUEUE_SIZE; const Vector default_angular_velocity(configuration()->general_config.pitch, configuration()->general_config.yaw, @@ -535,10 +532,13 @@ Vector Movable::MaxTorque(const Vector &torque) { } Vector Movable::ClampTorque(const Vector &amt1) { + Unit *unit = vega_dynamic_cast_ptr(this); + Vector Res = amt1; + double power = unit->drive.Consume(); // no_fuel_thrust = 0.4, so even with no fuel, we should keep flying - float fuelclamp = std::max(drive.Powered(), configuration()->fuel.no_fuel_thrust); + float fuelclamp = std::max(power, configuration()->fuel.no_fuel_thrust); if (fabs(amt1.i) > fuelclamp * limits.pitch) { Res.i = copysign(fuelclamp * limits.pitch, amt1.i); } @@ -555,11 +555,15 @@ Vector Movable::ClampTorque(const Vector &amt1) { Vector Movable::ClampVelocity(const Vector &velocity, const bool afterburn) { - Unit *unit = dynamic_cast(this); + Unit *unit = vega_dynamic_cast_ptr(this); + + // We're consuming energy twice. Here and in 537 + double drive_power = unit->drive.Consume(); + double ab_power = unit->afterburner.Consume(); // no_fuel_thrust = 0.4, so even with no fuel, we should keep flying - float fuelclamp = std::max(drive.Powered(), configuration()->fuel.no_fuel_thrust); - float abfuelclamp = std::max(afterburner.Powered(), configuration()->fuel.no_fuel_afterburn); + float fuelclamp = std::max(drive_power, configuration()->fuel.no_fuel_thrust); + float abfuelclamp = std::max(ab_power, configuration()->fuel.no_fuel_afterburn); float limit = afterburn ? (abfuelclamp * (unit->computer.max_ab_speed() - unit->computer.max_speed()) + (fuelclamp * unit->computer.max_speed())) : fuelclamp * unit->computer.max_speed(); @@ -612,17 +616,16 @@ Vector Movable::ClampThrust(const Vector &amt1, bool afterburn) { Unit *unit = static_cast(this); const bool finegrainedFuelEfficiency = configuration()->fuel.variable_fuel_consumption; - - // Delayed reaction - if(afterburn) { - afterburner.Use(); - } Vector Res = amt1; + // We're consuming energy thrice. Here and in 537 and 559 + double drive_power = unit->drive.Consume(); + double ab_power = unit->afterburner.Consume(); + // no_fuel_thrust = 0.4, so even with no fuel, we should keep flying - float fuelclamp = std::max(drive.Powered(), configuration()->fuel.no_fuel_thrust); - float abfuelclamp = std::max(afterburner.Powered(), configuration()->fuel.no_fuel_afterburn); + float fuelclamp = std::max(drive_power, configuration()->fuel.no_fuel_thrust); + float abfuelclamp = std::max(ab_power, configuration()->fuel.no_fuel_afterburn); if (fabs(amt1.i) > fabs(fuelclamp * limits.lateral)) { Res.i = copysign(fuelclamp * limits.lateral, amt1.i); diff --git a/engine/src/cmd/movable.h b/engine/src/cmd/movable.h index 2c2d1a4820..b6efea1e0e 100644 --- a/engine/src/cmd/movable.h +++ b/engine/src/cmd/movable.h @@ -41,10 +41,6 @@ class UnitCollection; struct Quaternion; class Movable { - -protected: - Drive drive; - Afterburner afterburner; public: //mass of this unit (may change with cargo) // TODO: subclass with return Mass+fuel; diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index 9b6f920f3b..36eaf6f5ba 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -799,11 +799,7 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ // TODO: 1. check AB cost values in json // 2. tweak afterburner cost for playability // 3. check WC - int afterburner_type_number = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Type", 0); - EnergyType afterburner_type = SaveToType(afterburner_type_number); - double afterburner_usage_factor = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Usage_Cost", 3.0); - afterburner = Afterburner(afterburner_type, afterburner_usage_factor, 1.0, - Mass, simulation_atom_var); + afterburner.Load("", unit_key); limits.yaw = UnitCSVFactory::GetVariable(unit_key, "Maneuver_Yaw", 0.0f) * VS_PI / 180.0; limits.pitch = UnitCSVFactory::GetVariable(unit_key, "Maneuver_Pitch", 0.0f) * VS_PI / 180.0; @@ -844,11 +840,9 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ UnitCSVFactory::GetVariable(unit_key, "Afterburner_Speed_Governor", 0.0f) * game_speed; computer.itts = UnitCSVFactory::GetVariable(unit_key, "ITTS", true); - radar = CRadar(unit_key, &computer); - - cloak = Cloak(); + radar.Load("", unit_key); cloak.Load("", unit_key); - + repair_droid = UnitCSVFactory::GetVariable(unit_key, "Repair_Droid", 0); ecm = UnitCSVFactory::GetVariable(unit_key, "ECM_Rating", 0); @@ -1208,7 +1202,7 @@ string Unit::WriteUnitString() { unit["Armor_Back_Bottom_Right"] = tos(GetArmorLayer().facets[7].health); shield->SaveToCSV(unit); - + radar.SaveToCSV(unit); @@ -1224,8 +1218,7 @@ string Unit::WriteUnitString() { unit["Warp_Usage_Cost"] = tos(jump.insysenergy); // Afterburner - unit["Afterburner_Type"] = TypeToSave(afterburner.GetEnergyType()); - unit["Afterburner_Usage_Cost"] = tos(afterburner.UsageFactor()); + afterburner.SaveToCSV(unit); unit["Maneuver_Yaw"] = tos(limits.yaw * 180 / (VS_PI)); unit["Maneuver_Pitch"] = tos(limits.pitch * 180 / (VS_PI)); diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index db1ca986fd..e7a5a9efe5 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -508,11 +508,6 @@ void Unit::Init(const char *filename, SetAniSpeed(0.05); StartAnimation(); } - - //Components - fuel.AddConsumer(reactor); - energy.AddConsumer(shield_); - energy.AddConsumer(cloak); } vector Unit::StealMeshes() { @@ -1049,7 +1044,8 @@ bool Unit::jumpReactToCollision(Unit *smalle) { if ((!SPEC_interference && (jump.drive >= 0 && (ftl_energy.Level() >= jump.energy || (ai_jump_cheat && cp == NULL)) )) || smalle->forcejump) { - jump_drive.Use(); + // TODO: check we're not doing this multiple times + jump_drive.Consume(); DeactivateJumpDrive(); Unit *jumppoint = smalle; @@ -3231,7 +3227,7 @@ bool Unit::UpAndDownGrade(const Unit *up, static bool use_template_maxrange = XMLSupport::parse_bool(vs_config->getVariable("physics", "use_upgrade_template_maxrange", "true")); //Radar stuff - if (!csv_cell_null_check || force_change_on_nothing + /*if (!csv_cell_null_check || force_change_on_nothing || cell_has_recursive_data(upgrade_name, up->faction, "Radar_Range|Radar_Color|ITTS|Can_Lock|Max_Cone|Lock_Cone|Tracking_Cone")) { if (!csv_cell_null_check || force_change_on_nothing @@ -3307,7 +3303,7 @@ bool Unit::UpAndDownGrade(const Unit *up, } } cancompletefully = ccf; - } + }*/ //NO CLUE FOR BELOW if (downgrade) { if (jump.drive >= -1 && up->jump.drive >= -1) { @@ -4256,13 +4252,9 @@ void Unit::ActTurn() { Repair(); // Power - fuel.Act(); reactor.Generate(); - energy.Act(); shield_.Regenerate(graphicOptions.InWarp, isPlayerShip()); - - ftl_energy.Act(); } void Unit::UpdatePhysics2(const Transformation &trans, diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index 298bd65e66..82e29e1123 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -80,11 +80,16 @@ void UncheckUnit( class Unit*un ); #include "upgradeable_unit.h" // Components -#include "components/energy_manager.h" +#include "components/energy_container.h" +#include "components/reactor.h" + +#include "components/afterburner.h" +#include "components/drive.h" +#include "components/jump_drive.h" + #include "components/cloak.h" -#include "components/fuel.h" #include "components/radar.h" -#include "components/reactor.h" + #include "configuration/configuration.h" #include "configuration/game_config.h" @@ -152,16 +157,24 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, StringPool::Reference csvRow; public: + Computer computer; // Components - Fuel fuel = Fuel(); - EnergyContainer energy = EnergyContainer(EnergyType::Energy, 0.0); - EnergyContainer ftl_energy = EnergyContainer(EnergyType::FTL, 0.0); + EnergyContainer fuel = EnergyContainer(EnergyType::Fuel); + EnergyContainer energy = EnergyContainer(EnergyType::Energy); + EnergyContainer ftl_energy = EnergyContainer(EnergyType::FTL); // TODO: move this to a single constructor?! - Reactor reactor = Reactor(0.0, - &energy, &ftl_energy, - sim_atom_multiplier); - Cloak cloak; + Reactor reactor = Reactor(&fuel, + &energy, &ftl_energy); + + Afterburner afterburner = Afterburner(&fuel); + Drive drive = Drive(&fuel); + JumpDrive jump_drive = JumpDrive(&ftl_energy); + + Cloak cloak = Cloak(&energy); + + CRadar radar = CRadar(&energy, &computer); + /// Radar and related systems // TODO: take a deeper look at this much later... @@ -496,8 +509,7 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, StarSystem *&previouslyActiveStarSystem, bool DoSightAndSound) override; - Computer computer; - CRadar radar; + void SwitchCombatFlightMode(); bool CombatMode(); diff --git a/engine/src/cmd/upgradeable_unit.cpp b/engine/src/cmd/upgradeable_unit.cpp index c06121f97f..22001dbc66 100644 --- a/engine/src/cmd/upgradeable_unit.cpp +++ b/engine/src/cmd/upgradeable_unit.cpp @@ -113,8 +113,6 @@ UpgradeOperationResult UpgradeableUnit::UpgradeUnit(const std::string upgrade_na UpgradeOperationResult result; - - switch(upgrade_type) { case UpgradeType::Armor: result.upgradeable = true; @@ -124,6 +122,51 @@ UpgradeOperationResult UpgradeableUnit::UpgradeUnit(const std::string upgrade_na result.upgradeable = true; result.success = unit->shield->CanWillUpDowngrade(upgrade_key, upgrade, apply); break; + + case UpgradeType::Capacitor: + result.upgradeable = true; + result.success = unit->energy.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; + case UpgradeType::FTL_Capacitor: + result.upgradeable = true; + result.success = unit->ftl_energy.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; + case UpgradeType::Reactor: + result.upgradeable = true; + result.success = unit->reactor.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; + + case UpgradeType::Afterburner: + result.upgradeable = true; + result.success = unit->afterburner.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; + case UpgradeType::Drive: + result.upgradeable = true; + result.success = unit->armor->CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; + case UpgradeType::Jump_Drive: + result.upgradeable = true; + result.success = unit->jump_drive.CanWillUpDowngrade(upgrade_key, upgrade, apply); + break; + + case UpgradeType::Cloak: + result.upgradeable = true; + result.success = unit->cloak.CanWillUpDowngrade(upgrade_key, upgrade, apply); + 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; diff --git a/engine/src/cmd/upgradeable_unit.h b/engine/src/cmd/upgradeable_unit.h index d3db85c189..f226a07770 100644 --- a/engine/src/cmd/upgradeable_unit.h +++ b/engine/src/cmd/upgradeable_unit.h @@ -50,6 +50,10 @@ enum class UpgradeType { Capacitor, FTL_Capacitor, + Reactor, + + Afterburner, + Drive, Jump_Drive, Cloak, diff --git a/engine/src/cmd/weapon_info.cpp b/engine/src/cmd/weapon_info.cpp index 83893d9b54..ecfa14fc66 100644 --- a/engine/src/cmd/weapon_info.cpp +++ b/engine/src/cmd/weapon_info.cpp @@ -37,23 +37,14 @@ namespace alg = boost::algorithm; Hashtable lookuptable; -WeaponInfo::WeaponInfo(): EnergyConsumer(EnergyType::Energy, - EnergyConsumerClassification::BallWeapon, - EnergyConsumerType::Momentary, - 0.0) { +WeaponInfo::WeaponInfo(): EnergyConsumer(nullptr, false) { } -WeaponInfo::WeaponInfo(WEAPON_TYPE type) : EnergyConsumer(EnergyType::Energy, - EnergyConsumerClassification::BallWeapon, - EnergyConsumerType::Momentary, - 0.0) { +WeaponInfo::WeaponInfo(WEAPON_TYPE type) : EnergyConsumer(nullptr, false) { this->type = type; } -WeaponInfo::WeaponInfo(const WeaponInfo &tmp) : EnergyConsumer(tmp.energy_type, - tmp.classification, - tmp.consumer_type, - 0.0) { +WeaponInfo::WeaponInfo(const WeaponInfo &tmp) : EnergyConsumer(nullptr, false) { *this = tmp; } diff --git a/engine/src/components/afterburner.cpp b/engine/src/components/afterburner.cpp index 7ff6eb229f..e58a6f5441 100644 --- a/engine/src/components/afterburner.cpp +++ b/engine/src/components/afterburner.cpp @@ -35,28 +35,71 @@ // Note that usage_factor should be closely related to normal drive's usage factor. // In most cases it should be 1.0 and the difference should be modelled using the afterburner_level. -Afterburner::Afterburner(EnergyType type, double usage_factor): - EnergyConsumer(type, - EnergyConsumerClassification::Afterburner, - EnergyConsumerType::Variable, - 0.0), +Afterburner::Afterburner(EnergyContainer *source, double usage_factor): + Component("", 0.0, 0.0, false), + EnergyConsumer(source, false), usage_factor(usage_factor) {} -Afterburner::Afterburner(EnergyType type, - double usage_factor, - double afterburner_level, - double mass, double simulation_atom_var): - EnergyConsumer(type, - EnergyConsumerClassification::Afterburner, - EnergyConsumerType::Variable, - 0.0), - usage_factor(usage_factor) { - consumption = usage_factor * afterburner_level * mass * simulation_atom_var; + +// Component Methods +void Afterburner::Load(std::string upgrade_key, std::string unit_key) { + type = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Type", 0); + usage_cost = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Usage_Cost", 3.0); + acceleration = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Accel", 1.0); + speed_governor = UnitCSVFactory::GetVariable(unit_key, "Afterburner_Speed_Governor", 1.0); +} + +void Afterburner::SaveToCSV(std::map& unit) const { + unit["Afterburner_Type"] = std::to_string(type); + unit["Afterburner_Usage_Cost"] = std::to_string(usage_cost); + unit["Afterburner_Accel"] = std::to_string(acceleration); + unit["Afterburner_Speed_Governor"] = std::to_string(speed_governor); } +std::string Afterburner::Describe() const { + return std::string(); +} +bool Afterburner::CanDowngrade() const { + return !Damaged(); +} -/*void Afterburner::WriteUnitString(std::map &unit) { - unit["Afterburner_Type"] = std::to_string(type); - unit["Afterburner_Usage_Cost"] = std::to_string(usage_factor); -}*/ +bool Afterburner::Downgrade() { + type = 1; + usage_cost = 1.0; + acceleration = 1.0; + speed_governor = 1.0; +} + +bool Afterburner::CanUpgrade(const std::string upgrade_name) const { + return !Damaged(); +} + +bool Afterburner::Upgrade(const std::string upgrade_name) { + if(!CanUpgrade(upgrade_key)) { + return false; + } + + type = UnitCSVFactory::GetVariable(upgrade_name, "Afterburner_Type", 1); + usage_cost = UnitCSVFactory::GetVariable(upgrade_name, "Afterburner_Usage_Cost", 1.0); + acceleration = UnitCSVFactory::GetVariable(upgrade_name, "Afterburner_Accel", 1.0); + speed_governor = UnitCSVFactory::GetVariable(upgrade_name, "Afterburner_Speed_Governor", 1.0); +} + +void Afterburner::Damage() { + // Can't damage the afterburners. + // Instead, the drive will be damaged. + return; +} + +void Afterburner::Repair() { + +} + +bool Afterburner::Damaged() const { + return false; +} + +bool Afterburner::Installed() const { + return acceleration > 1.0; +} \ No newline at end of file diff --git a/engine/src/components/afterburner.h b/engine/src/components/afterburner.h index f31c854406..8d24c82345 100644 --- a/engine/src/components/afterburner.h +++ b/engine/src/components/afterburner.h @@ -31,18 +31,56 @@ #include #include +#include "component.h" #include "energy_container.h" -#include "energy_types.h" +#include "energy_consumer.h" +// TODO: Take ship size, mass, cost, something into account. +// TODO: figure out the whole type thing. -class Afterburner : public EnergyConsumer +/* @discussion Afterburners are extra engines or a feature of existing + engines that accelrate faster and achieve a top higher speed. + Overdrive upgrade is an upgrade to improve the performance of + the afterburners. + By default, ships do not have afterburners. If they do, it must be + integrated and cannot be upgraded. +*/ +class Afterburner : public Component, public EnergyConsumer { - double usage_factor; + int type = 0; + double usage_cost = 1.0; // Multiplier + double acceleration = 1.0; // Multiplier + double speed_governor = 1.0; // Multiplier + double usage_factor = 1.0; + + double atomic_drive_usage = 1.0; + /* Discussion of fuel usage + Afterburner fuel usage is based on normal drive usage. + Therefore, we need this data here as well. + */ public: - Afterburner(EnergyType type, double usage_factor); - Afterburner(EnergyType type, double usage_factor, double afterburner_level, - double mass, double simulation_atom_var); - //Afterburner(std::string unit_key); + Afterburner(EnergyContainer *source, double usage_factor = 1.0); + + // Component + virtual void Load(std::string upgrade_key, std::string unit_key); + + virtual void SaveToCSV(std::map& unit) const; + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; //void WriteUnitString(std::map &unit); double UsageFactor() { return usage_factor; } diff --git a/engine/src/components/cloak.cpp b/engine/src/components/cloak.cpp index 82384526ec..7a917003da 100644 --- a/engine/src/components/cloak.cpp +++ b/engine/src/components/cloak.cpp @@ -23,16 +23,26 @@ */ #include "cloak.h" -#include "energy_types.h" #include "energy_consumer.h" #include "unit_csv_factory.h" #include "vegastrike.h" #include "configuration/configuration.h" -Cloak::Cloak() : Component(std::string(), 0.0, 0.0, false), - EnergyConsumer(EnergyType::Energy, - EnergyConsumerClassification::Cloak, - EnergyConsumerType::Variable, 0.0) { +Cloak::Cloak(): Component(std::string(), 0.0, 0.0, false), + EnergyConsumer(nullptr, false) { + status = CloakingStatus::disabled; + + energy = 0; + rate = 100; + glass = false; + current = 0; + minimum = 0; +} + + +Cloak::Cloak(EnergyContainer *source) : + Component(std::string(), 0.0, 0.0, false), + EnergyConsumer(source, false) { status = CloakingStatus::disabled; energy = 0; @@ -163,8 +173,10 @@ void Cloak::Update() return; } + double power = Consume(); + // Insufficient energy to cloak ship - if(powered < 1.0) { + if(power < 1.0) { std::cerr << "No power to cloak\n"; status = CloakingStatus::decloaking; } @@ -192,20 +204,17 @@ void Cloak::Toggle() { // Unit is not capable of cloaking or damaged if(status == CloakingStatus::disabled || status == CloakingStatus::damaged) { - powered = 0.0; return; } // If we're ready start cloaking if(status == CloakingStatus::ready) { status = CloakingStatus::cloaking; - powered = energy; return; } // In any other case, start decloaking status = CloakingStatus::decloaking; - powered = 0.0; } void Cloak::Activate() { diff --git a/engine/src/components/cloak.h b/engine/src/components/cloak.h index 59b414171d..47c2b7bde5 100644 --- a/engine/src/components/cloak.h +++ b/engine/src/components/cloak.h @@ -29,6 +29,7 @@ #include #include "component.h" +#include "energy_consumer.h" #include "energy_container.h" #include "damageable_layer.h" @@ -65,6 +66,7 @@ class Cloak : public Component, public EnergyConsumer public: Cloak(); + Cloak(EnergyContainer *source); // Virtual Functions virtual void Load(std::string upgrade_key, std::string unit_key); diff --git a/engine/src/components/component.h b/engine/src/components/component.h index 4938954b4d..58916fae5f 100644 --- a/engine/src/components/component.h +++ b/engine/src/components/component.h @@ -76,9 +76,9 @@ class Component virtual bool Downgrade() = 0; - virtual bool CanUpgrade(const std::string upgrade_name) const = 0; + virtual bool CanUpgrade(const std::string upgrade_key) const = 0; - virtual bool Upgrade(const std::string upgrade_name) = 0; + virtual bool Upgrade(const std::string upgrade_key) = 0; virtual void Damage() = 0; virtual void Repair() = 0; diff --git a/engine/src/components/drive.cpp b/engine/src/components/drive.cpp index 18cdab9cac..2000bb4c00 100644 --- a/engine/src/components/drive.cpp +++ b/engine/src/components/drive.cpp @@ -23,20 +23,60 @@ */ #include "drive.h" -#include "energy_types.h" // Note that usage_factor should be closely related to normal drive's usage factor. // In most cases it should be 1.0 and the difference should be modelled using the afterburner_level. -Drive::Drive(EnergyType type, - double usage_factor, - double drive_level, - double mass, - double simulation_atom_var): - //Component("", 0.0, 0.0, false), - EnergyConsumer(type, - EnergyConsumerClassification::Afterburner, - EnergyConsumerType::Constant, - 0.0), +Drive::Drive(EnergyContainer *source, + double usage_factor): + Component("", 0.0, 0.0, false), + EnergyConsumer(source, false), usage_factor(usage_factor) { - consumption = usage_factor * drive_level * mass * simulation_atom_var; + consumption = usage_factor * 1.0 * mass * simulation_atom_var; +} + + +// Component Methods +void Drive::Load(std::string upgrade_key, std::string unit_key) { +} + +void Drive::SaveToCSV(std::map& unit) const { +} + +std::string Drive::Describe() const { + return std::string(); +} + +bool Drive::CanDowngrade() const { + return !Damaged(); +} + +bool Drive::Downgrade() { + if(!CanDowngrade()) { + return false; + } +} + +bool Drive::CanUpgrade(const std::string upgrade_name) const { + return !Damaged(); +} + +bool Drive::Upgrade(const std::string upgrade_name) { + if(!CanUpgrade(upgrade_name)) { + return false; + } +} + +void Drive::Damage() { + return; +} + +void Drive::Repair() { +} + +bool Drive::Damaged() const { + return false; +} + +bool Drive::Installed() const { + return false; } \ No newline at end of file diff --git a/engine/src/components/drive.h b/engine/src/components/drive.h index adedcc0e36..f0b7eb1455 100644 --- a/engine/src/components/drive.h +++ b/engine/src/components/drive.h @@ -27,17 +27,36 @@ #include "component.h" #include "energy_container.h" +#include "energy_consumer.h" -class Drive : //public Component, +class Drive : public Component, public EnergyConsumer { double usage_factor; public: - Drive(EnergyType type, - double usage_factor, - double drive_level, - double mass, - double simulation_atom_var); + Drive(EnergyContainer *source, + double usage_factor = 1.0); + + // Component Methods + virtual void Load(std::string upgrade_key, std::string unit_key); + + virtual void SaveToCSV(std::map& unit) const; + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; }; #endif // DRIVE_H diff --git a/engine/src/components/energy_consumer.cpp b/engine/src/components/energy_consumer.cpp index 7a585ff27f..e59ba11e54 100644 --- a/engine/src/components/energy_consumer.cpp +++ b/engine/src/components/energy_consumer.cpp @@ -23,4 +23,42 @@ * along with Vega Strike. If not, see . */ -// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- \ No newline at end of file +// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- + +#include "energy_consumer.h" + +double EnergyConsumer::simulation_atom_var = 0.1; + +EnergyConsumer::EnergyConsumer(EnergyContainer *source, + bool partial, + double consumption): + source(source), + partial(partial), + consumption(consumption), + atom_consumption(consumption * simulation_atom_var) {} + + +double EnergyConsumer::Consume() { + if(!source) { + return 0.0; + } + + return source->Deplete(partial, atom_consumption); +} + +double EnergyConsumer::GetConsumption() const { + return consumption; +} + +double EnergyConsumer::GetAtomConsumption() const { + return atom_consumption; +} + +void EnergyConsumer::SetConsumption(double consumption) { + this->consumption = consumption; + atom_consumption = consumption * simulation_atom_var; +} + +void EnergyConsumer::ZeroSource() { + source->Zero(); +} \ No newline at end of file diff --git a/engine/src/components/energy_consumer.h b/engine/src/components/energy_consumer.h index ba26deeeaf..3c60c3686a 100644 --- a/engine/src/components/energy_consumer.h +++ b/engine/src/components/energy_consumer.h @@ -28,32 +28,23 @@ #ifndef ENERGYCONSUMER_H #define ENERGYCONSUMER_H -#include "energy_types.h" -#include "energy_consumer.h" -#include "resource.h" +#include "energy_container.h" class EnergyConsumer { + EnergyContainer *source; + bool partial; // Can power consumer with less energy than requested protected: - EnergyType energy_type; - EnergyConsumerClassification classification; - EnergyConsumerType consumer_type; - bool in_use; - double powered; // 0.0-1.0 percentage powered - Resource consumption; // This is when powered and for simulation_atom_var - - friend class EnergyContainer; - friend class EnergyManager; -public: - EnergyConsumer(EnergyType energy_type, EnergyConsumerClassification classification, - EnergyConsumerType consumer_type, double consumption): - energy_type(energy_type), classification(classification), consumer_type(consumer_type), in_use(false), - powered(0.0), consumption(Resource(consumption, 0.0, consumption)) {} + double consumption; // Directly converted to atomic. Mostly for book keeping. + double atom_consumption; // consumption per 0.1 seconds. - double Powered() { return powered; } - void Use() { in_use = true;} - EnergyType GetEnergyType() { return energy_type; } - double GetConsumption() const { return consumption.Value(); } - void SetConsumption(const double consumption) { this->consumption = consumption; } + static double simulation_atom_var; +public: + EnergyConsumer(EnergyContainer *source, bool partial, double consumption = 0.0); + double Consume(); + double GetConsumption() const; + double GetAtomConsumption() const; + void SetConsumption(double consumption); + void ZeroSource(); }; #endif // ENERGYCONSUMER_H diff --git a/engine/src/components/energy_container.cpp b/engine/src/components/energy_container.cpp index 36001b1819..eadefa2bc7 100644 --- a/engine/src/components/energy_container.cpp +++ b/engine/src/components/energy_container.cpp @@ -26,24 +26,19 @@ // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- #include "energy_container.h" +#include "unit_csv_factory.h" #include -EnergyContainer::EnergyContainer(): type(EnergyType::Fuel), - level(Resource(0.0,0.0,0.0)), - consumers(std::vector()) {} +const std::string FUEL_CAPACITY = "Fuel_Capacity"; +const std::string CAPACITOR = "Warp_Capacitor"; +const std::string FTL_CAPACITOR = "Primary_Capacitor"; -EnergyContainer::EnergyContainer(EnergyType type, double capacity): type(type), - level(Resource(capacity,0.0,capacity)), - consumers(std::vector()) {} +EnergyContainer::EnergyContainer(EnergyType type): + Component("", 0.0, 0.0, false), + type(type), + level(Resource(0.0,0.0,0.0)) {} -void EnergyContainer::AddConsumer(EnergyType energy_type, - EnergyConsumerClassification classification, - EnergyConsumerType consumer_type, - double quantity) { - EnergyConsumer consumer(energy_type, classification, consumer_type, quantity); - consumers.push_back(consumer); -} // Return value - any surplus charge double EnergyContainer::Charge(const double quantity) { @@ -53,14 +48,20 @@ double EnergyContainer::Charge(const double quantity) { return quantity - level.Value() + old_level; } -double EnergyContainer::Deplete(const double quantity) { +double EnergyContainer::Deplete(bool partial, const double quantity) { + // Check we have enough energy to fully charge the consumer + if(!partial && quantity > level.Value()) { + return 0.0; + } + double old_level = level.Value(); level -= quantity; - return quantity + old_level - level.Value(); + double actual_usage = old_level - level.Value(); + return actual_usage / quantity; } bool EnergyContainer::Depleted() const { - return (level.Value() < 0.01); + return (level.Value() < 0.0001); } void EnergyContainer::SetCapacity(const double capacity, bool refill) { @@ -83,7 +84,7 @@ double EnergyContainer::Percent() const { void EnergyContainer::Zero() { level = 0; } -void EnergyContainer::Act() { +/*void EnergyContainer::Act() { for(EnergyConsumer& consumer : consumers) { if(consumer.consumer_type == EnergyConsumerType::Constant) { consumer.in_use = true; @@ -100,9 +101,9 @@ void EnergyContainer::Act() { level -= consumer.consumption; consumer.powered = 1.0; } -} +}*/ -void EnergyContainer::Use(EnergyConsumerClassification classification) { +/*void EnergyContainer::Use(EnergyConsumerClassification classification) { for(EnergyConsumer& consumer : consumers) { consumer.in_use = true; } @@ -116,9 +117,9 @@ bool EnergyContainer::InUse(EnergyConsumerClassification classification) { } return false; -} +}*/ -double EnergyContainer::Powered(EnergyConsumerClassification classification) { +/*double EnergyContainer::Powered(EnergyConsumerClassification classification) { for(EnergyConsumer& consumer : consumers) { if(consumer.classification == classification) { return consumer.powered; @@ -126,8 +127,117 @@ double EnergyContainer::Powered(EnergyConsumerClassification classification) { } return false; -} +}*/ void EnergyContainer::Refill() { level.SetToMax(); +} + + +// Component Functions +void EnergyContainer::Load(std::string upgrade_key, std::string unit_key) { + // Component + upgrade_key = ""; + + // TODO: nice to have - ship mass goes down as fuel depleted + mass = 0; + volume = 0; + + double capacity = 0.0; + + switch(type) { + case EnergyType::Fuel: + upgrade_name = "Fuel"; + capacity = UnitCSVFactory::GetVariable(unit_key, FUEL_CAPACITY, 1.0); + break; + + case EnergyType::Energy: + upgrade_name = "Capacitor"; + capacity = UnitCSVFactory::GetVariable(unit_key, CAPACITOR, 1.0); + break; + + case EnergyType::FTL: + upgrade_name = "FTL_Capacitor"; + capacity = UnitCSVFactory::GetVariable(unit_key, FTL_CAPACITOR, 1.0); + break; + + case EnergyType::None: + break; + } + + SetCapacity(capacity); +} + +void EnergyContainer::SaveToCSV(std::map& unit) const { + unit[FUEL_CAPACITY] = std::to_string(MaxLevel()); +} + +std::string EnergyContainer::Describe() const { + return std::string(); +} + +bool EnergyContainer::CanDowngrade() const { + return !Damaged(); +} + +bool EnergyContainer::Downgrade() { + if(!CanDowngrade()) { + return false; + } + + level.SetMaxValue(0.0); + return true; +} + +bool EnergyContainer::CanUpgrade(const std::string upgrade_key) const { + return !Damaged(); +} + +bool EnergyContainer::Upgrade(const std::string upgrade_key) { + if(!CanUpgrade(upgrade_key)) { + return false; + } + + this->upgrade_key = upgrade_key; + upgrade_name = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); + + double capacity = 0.0; + + switch(type) { + case EnergyType::Fuel: + capacity = UnitCSVFactory::GetVariable(upgrade_key, FUEL_CAPACITY, 1.0); + break; + + case EnergyType::Energy: + capacity = UnitCSVFactory::GetVariable(upgrade_key, CAPACITOR, 1.0); + break; + + case EnergyType::FTL: + capacity = UnitCSVFactory::GetVariable(upgrade_key, FTL_CAPACITOR, 1.0); + break; + + case EnergyType::None: + break; + } + + SetCapacity(capacity); + return true; +} + + +void EnergyContainer::Damage() { + level.RandomDamage(); +} + +void EnergyContainer::Repair() { + level.RepairFully(); +} + +bool EnergyContainer::Damaged() const { + return level.Damaged(); +} + + +bool EnergyContainer::Installed() const { + return level > 0.0; } \ No newline at end of file diff --git a/engine/src/components/energy_container.h b/engine/src/components/energy_container.h index e14a64c716..14864ac903 100644 --- a/engine/src/components/energy_container.h +++ b/engine/src/components/energy_container.h @@ -32,38 +32,36 @@ #include #include "resource/resource.h" -#include "energy_types.h" -#include "energy_consumer.h" +#include "component.h" + +/* Discussion of FTL - yes, it isn't really faster. However, +* it would be easier for a new developer or someone from WC +* to figure what it means. +*/ +enum class EnergyType { + Fuel, // 1 + Energy, // 0 Capacitor + FTL, // 2 FTL + None // 3 Free Energy +}; /** * @brief The EnergyContainer class models the fuel cell, capacitor and SPEC capacitor */ -class EnergyContainer +class EnergyContainer: public Component { -protected: EnergyType type; Resource level; - std::vector consumers; - friend class EnergyManager; public: - EnergyContainer(); - EnergyContainer(EnergyType type, double capacity); - - void AddConsumer(EnergyType energy_type, - EnergyConsumerClassification classification, - EnergyConsumerType consumer_type, - double quantity); - - void AddConsumer(EnergyConsumer consumer) { - consumers.push_back(consumer); - } + EnergyContainer(EnergyType type); // Return value - any surplus charge double Charge(const double quantity); - double Deplete(const double quantity); + // Partial - can power consumer with less energy than requested + double Deplete(bool partial, const double quantity); bool Depleted() const; void SetCapacity(const double capacity, bool refill = true); @@ -74,12 +72,31 @@ class EnergyContainer void Zero(); - void Act(); - - void Use(EnergyConsumerClassification classification); + /*void Use(EnergyConsumerClassification classification); bool InUse(EnergyConsumerClassification classification); - double Powered(EnergyConsumerClassification classification); + double Powered(EnergyConsumerClassification classification);*/ + + // Component + virtual void Load(std::string upgrade_key, std::string unit_key); + + virtual void SaveToCSV(std::map& unit) const; + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; }; #endif // ENERGYCONTAINER_H diff --git a/engine/src/components/energy_manager.cpp b/engine/src/components/energy_manager.cpp deleted file mode 100644 index 5e558a1fa3..0000000000 --- a/engine/src/components/energy_manager.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * energy_manager.cpp - * - * Copyright (c) 2001-2002 Daniel Horn - * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors - * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . - */ - -// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- - -#include "energy_manager.h" -#include - -//////////////////////////////////////////////////////// - -// This section calculates the consumption of fuel and energy by different components. -// TODO: It really should move to the python side and be configurable. - -// This is calculated to provide longer range ships with approximately 60 minutes of gameplay. -// TODO: This does not calculate for WC -/*double getDriveConsumption(double factor, double mass) { - return mass * factor * simulation_atom_var; -} - -double getSPECDriveConsumption(double factor, double mass) { - return mass * factor * simulation_atom_var; -} - -double getJumpDriveConsumption(double factor, double mass) { - return mass * factor; -} - -// WC - 6 jumps -double getWCJumpDriveConsumption(double factor, double max_fuel) { - return max_fuel / factor; -} - -double getECMConsumption(double factor, double ecm_level) { - return ecm_level * factor * simulation_atom_var; -} - -double getShieldMaintenanceConsumption(double factor, int facets, double max_strength) { - return facets * max_strength * factor * simulation_atom_var; -} - -double getShieldRegenConsumption(double factor, int facets, double max_strength) { - return facets * max_strength * factor * simulation_atom_var; -} - -double getCloakConsumption(double factor, double mass) { - return mass * factor * simulation_atom_var; -}*/ - -//////////////////////////////////////////////////////// - -/*EnergyManager::EnergyManager(EnergyContainer *fuel, - EnergyContainer *energy, - EnergyContainer *spec_energy, - Reactor *reactor): - fuel(fuel), energy(energy), - ftl_energy(ftl_energy), reactor(reactor) {} - -void EnergyManager::Act() { - fuel->Act(); - double actual_reactor_usage = reactor->Generate(); - fuel->Charge(reactor->consumption - actual_reactor_usage); - energy->Act(); - ftl_energy->Act(); -} - -void EnergyManager::AddConsumer(EnergyType energy_type, - EnergyConsumerClassification classification, - EnergyConsumerType consumer_type, - double consumption) { - EnergyContainer* container = GetContainer(energy_type); - EnergyConsumer consumer(energy_type, classification, consumer_type, consumption); - container->consumers.push_back(consumer); -} - -double EnergyManager::Deplete(const EnergyType type, const double quantity) { - EnergyContainer* container = GetContainer(type); - return container->Deplete(quantity); -} - -double EnergyManager::Deplete(EnergyConsumer consumer, const double quantity) { - EnergyContainer *container = GetContainer(consumer.energy_type); - if(!container) { - return 1.0; // None energy type. Infinite energy. - } - - return container->Deplete(quantity); -} - -EnergyContainer* EnergyManager::GetContainer(const EnergyType type) { - switch(type) { - case EnergyType::Fuel: - return fuel; - case EnergyType::Energy: - return energy; - case EnergyType::FTL: - return ftl_energy; - default: - return nullptr; - } -} - -const EnergyContainer* EnergyManager::GetConstContainer(const EnergyType type) const { - switch(type) { - case EnergyType::Fuel: - return fuel; - case EnergyType::Energy: - return energy; - case EnergyType::FTL: - return ftl_energy; - } -} - -double EnergyManager::GetLevel(const EnergyType type) const { - const EnergyContainer* container = GetConstContainer(type); - return container->level.Value(); -} - -double EnergyManager::GetMaxLevel(const EnergyType type) const { - const EnergyContainer* container = GetConstContainer(type); - return container->level.MaxValue(); -} - -void EnergyManager::SetCapacity(const EnergyType type, const double capacity) { - EnergyContainer* container = GetContainer(type); - container->SetCapacity(capacity, true); -} - -void EnergyManager::Refill(const EnergyType type) { - EnergyContainer* container = GetContainer(type); - container->level.SetToMax(); -} - -double EnergyManager::Percent(const EnergyType type) const { - const EnergyContainer* container = GetConstContainer(type); - if(!container) { - return 0.0; - } - - return container->level.Percent(); -} - -void EnergyManager::SetReactor(const double capacity, const double usage_factor, - const double reactor_level, const double simulation_atom_var) { - this->reactor.capacity = capacity; - double c = usage_factor * reactor_level * simulation_atom_var; - this->reactor.consumption = Resource(c, 0.0, c); - - fuel.AddConsumer(reactor); -}*/ diff --git a/engine/src/components/energy_manager.h b/engine/src/components/energy_manager.h deleted file mode 100644 index 1cb5d89939..0000000000 --- a/engine/src/components/energy_manager.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * energy_manager.h - * - * Copyright (c) 2001-2002 Daniel Horn - * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors - * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . - */ - -// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- - -#ifndef ENERGYMANAGER_H -#define ENERGYMANAGER_H - -#include "component.h" -#include "energy_types.h" -#include "energy_container.h" -#include "reactor.h" - -/*double getDriveConsumption(double factor, double mass); // 1/36000 -double getSPECDriveConsumption(double factor, double mass); -double getJumpDriveConsumption(double factor, double mass); -double getWCJumpDriveConsumption(double factor, double max_fuel); // 1/6 -double getECMConsumption(double factor, double ecm_level); // ? -double getShieldMaintenanceConsumption(double factor, int facets, double max_strength); // 100 -double getShieldRegenConsumption(double factor, int facets, double max_strength); // 10 -double getCloakConsumption(double factor, double mass);*/ // 25 - -/*struct EnergyManager { - EnergyContainer *fuel; - EnergyContainer *energy; - EnergyContainer *ftl_energy; - Reactor *reactor; - - EnergyManager(EnergyContainer *fuel, - EnergyContainer *energy, - EnergyContainer *ftl_energy, - Reactor *reactor); - - void Act(); - - void AddConsumer(EnergyType energy_type, - EnergyConsumerClassification classification, - EnergyConsumerType consumer_type, - double consumption); - void AddConsumer(EnergyType energy_type, EnergyConsumer consumer) { - EnergyContainer* container = GetContainer(energy_type); - container->AddConsumer(consumer); - } - double Deplete(const EnergyType type, const double quantity); - double Deplete(EnergyConsumer consumer, const double quantity); - - EnergyContainer* GetContainer(const EnergyType type); - const EnergyContainer* GetConstContainer(const EnergyType type) const; - - double GetLevel(const EnergyType type) const; - double GetMaxLevel(const EnergyType type) const; - void SetCapacity(const EnergyType type, const double capacity); - void Refill(const EnergyType type); - double Percent(const EnergyType type) const; -};*/ - - -#endif // ENERGYMANAGER_H \ No newline at end of file diff --git a/engine/src/components/energy_types.cpp b/engine/src/components/energy_types.cpp deleted file mode 100644 index 3258872f32..0000000000 --- a/engine/src/components/energy_types.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * energy_types.cpp - * - * Copyright (c) 2001-2002 Daniel Horn - * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors - * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . - */ - -// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- - -#include "energy_types.h" - -EnergyType SaveToType(const int type) { - switch(type) { - case 0: return EnergyType::Energy; - case 1: return EnergyType::Fuel; - case 2: return EnergyType::FTL; - default: return EnergyType::None; - } -} - -std::string TypeToSave(EnergyType type) { - switch(type) { - case EnergyType::Energy: return "0"; - case EnergyType::Fuel: return "1"; - case EnergyType::FTL: return "2"; - default: return "3"; - } -} \ No newline at end of file diff --git a/engine/src/components/energy_types.h b/engine/src/components/energy_types.h deleted file mode 100644 index aba32f0024..0000000000 --- a/engine/src/components/energy_types.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * energy_types.h - * - * Copyright (c) 2001-2002 Daniel Horn - * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors - * Copyright (c) 2019-2023 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . - */ - -// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- - -#ifndef ENERGYTYPES_H -#define ENERGYTYPES_H - -#include - -/* Discussion of FTL - yes, it isn't really faster. However, -* it would be easier for a new developer or someone from WC -* to figure what it means. -*/ -enum class EnergyType { - Fuel, // 1 - Energy, // 0 Capacitor - FTL, // 2 FTL - None // 3 Free Energy -}; - -EnergyType SaveToType(const int type); - -std::string TypeToSave(EnergyType type); - - -enum class EnergyConsumerClassification { - // Fuel - Reactor, - Drive, - Afterburner, - - // Energy - LifeSupport, - Radar, - Shield, - ECM, - Cloak, - BallWeapon, - BeamWeapon, - BoltWeapon, - - // SPEC - SPECDrive, - JumpDrive -}; - -enum class EnergyConsumerType { - Constant, // Life Support, Radar, etc. - Variable, // Beam Weapons, Afterburner - Momentary // Ball/Bolt Weapons, Jump Drive -}; - - -#endif // ENERGYTYPES_H \ No newline at end of file diff --git a/engine/src/components/fuel.cpp b/engine/src/components/fuel.cpp deleted file mode 100644 index e85cab1924..0000000000 --- a/engine/src/components/fuel.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * fuel.cpp - * - * Copyright (c) 2001-2002 Daniel Horn - * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors - * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . - */ - -// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- - -#include "fuel.h" - -#include "unit_csv_factory.h" - -#include - -const std::string FUEL_CAPACITY = "Fuel_Capacity"; - -Fuel::Fuel(): Component("", 0.0, 0.0, true), - EnergyContainer(EnergyType::Fuel, 0.0) {} - -void Fuel::Load(std::string upgrade_key, std::string unit_key) { - // Component - upgrade_name = "Fuel"; - upgrade_key = ""; - - // TODO: nice to have - ship mass goes down as fuel depleted - mass = 0; - volume = 0; - - double fuel_capacity = UnitCSVFactory::GetVariable(unit_key, FUEL_CAPACITY, 1.0); - SetCapacity(fuel_capacity); -} - -void Fuel::SaveToCSV(std::map& unit) const { - unit[FUEL_CAPACITY] = std::to_string(MaxLevel()); -} - -std::string Fuel::Describe() const { - return std::string(); -} - -bool Fuel::CanDowngrade() const { - return false; -} - -bool Fuel::Downgrade() { - return false; -} - -bool Fuel::CanUpgrade(const std::string upgrade_name) const { - return false; -} - -bool Fuel::Upgrade(const std::string upgrade_name) { - return false; -} - - -void Fuel::Damage() { - level.RandomDamage(); -} - -void Fuel::Repair() { - level.RepairFully(); -} - -bool Fuel::Damaged() const { - return level.Damaged(); -} - - -bool Fuel::Installed() const { - return true; -} \ No newline at end of file diff --git a/engine/src/components/fuel.h b/engine/src/components/fuel.h deleted file mode 100644 index bf01d549bf..0000000000 --- a/engine/src/components/fuel.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * fuel.h - * - * Copyright (c) 2001-2002 Daniel Horn - * Copyright (c) 2002-2019 pyramid3d and other Vega Strike Contributors - * Copyright (c) 2019-2024 Stephen G. Tuggy, Benjamen R. Meyer, Roy Falk 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 . - */ - -// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- - -#ifndef VEGA_STRIKE_ENGINE_COMPONENTS_FUEL_H -#define VEGA_STRIKE_ENGINE_COMPONENTS_FUEL_H - -#include "component.h" -#include "energy_container.h" - -/** - * This is a minimum implementation of fuel cell class. - * For now, a fuel cell cannot be upgraded, only repaired. - */ -class Fuel : public Component, public EnergyContainer { - friend class Unit; -public: - Fuel(); - - virtual void Load(std::string upgrade_key, std::string unit_key); // Load from dictionary - virtual void SaveToCSV(std::map& unit) const; - - virtual std::string Describe() const; // Describe component in base_computer - - virtual bool CanDowngrade() const; - - virtual bool Downgrade(); - - virtual bool CanUpgrade(const std::string upgrade_name) const; - - virtual bool Upgrade(const std::string upgrade_name); - - virtual void Damage(); - virtual void Repair(); - - virtual bool Damaged() const; - virtual bool Installed() const; -}; - -#endif // VEGA_STRIKE_ENGINE_COMPONENTS_FUEL_H \ No newline at end of file diff --git a/engine/src/components/jump_drive.cpp b/engine/src/components/jump_drive.cpp index e369aea6ee..9f287f6f01 100644 --- a/engine/src/components/jump_drive.cpp +++ b/engine/src/components/jump_drive.cpp @@ -24,20 +24,10 @@ #include "jump_drive.h" -JumpDrive::JumpDrive(): - //Component("", 0.0, 0.0, true), - EnergyConsumer(EnergyType::None, - EnergyConsumerClassification::JumpDrive, - EnergyConsumerType::Variable, - 0.0), - delay(0.0){} - -JumpDrive::JumpDrive(double consumption, double delay) : - //Component("", 0.0, 0.0, true), - EnergyConsumer(EnergyType::FTL, - EnergyConsumerClassification::JumpDrive, - EnergyConsumerType::Variable, consumption), - delay(delay) {} +JumpDrive::JumpDrive(EnergyContainer *source): + Component("", 0.0, 0.0, true), + EnergyConsumer(source, false), + delay(0.0) {} bool JumpDrive::Ready() { return true;//installed && enabled; @@ -47,3 +37,48 @@ void JumpDrive::SetDestination(int destination) { this->destination = destination; } +// Component Methods +void JumpDrive::Load(std::string upgrade_key, std::string unit_key) { +} + +void JumpDrive::SaveToCSV(std::map& unit) const { +} + +std::string JumpDrive::Describe() const { + return std::string(); +} + +bool JumpDrive::CanDowngrade() const { + return !Damaged(); +} + +bool JumpDrive::Downgrade() { + if(!CanDowngrade()) { + return false; + } +} + +bool JumpDrive::CanUpgrade(const std::string upgrade_name) const { + return !Damaged(); +} + +bool JumpDrive::Upgrade(const std::string upgrade_name) { + if(!CanUpgrade(upgrade_name)) { + return false; + } +} + +void JumpDrive::Damage() { + return; +} + +void JumpDrive::Repair() { +} + +bool JumpDrive::Damaged() const { + return false; +} + +bool JumpDrive::Installed() const { + return false; +} \ No newline at end of file diff --git a/engine/src/components/jump_drive.h b/engine/src/components/jump_drive.h index cff2ff4789..1ebb73ca69 100644 --- a/engine/src/components/jump_drive.h +++ b/engine/src/components/jump_drive.h @@ -26,20 +26,39 @@ #define JUMP_DRIVE_H #include "component.h" -#include "energy_types.h" #include "energy_container.h" +#include "energy_consumer.h" -class JumpDrive : //public Component, - public EnergyConsumer { +class JumpDrive : public Component, public EnergyConsumer { int destination; double delay; public: - JumpDrive(); - JumpDrive(double consumption, double delay); + JumpDrive(EnergyContainer *source); bool Ready(); void SetDestination(int destination); + + // Component Methods + virtual void Load(std::string upgrade_key, std::string unit_key); + + virtual void SaveToCSV(std::map& unit) const; + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; }; #endif // JUMP_DRIVE_H diff --git a/engine/src/components/radar.cpp b/engine/src/components/radar.cpp index f42c27ff55..d20fea62e7 100644 --- a/engine/src/components/radar.cpp +++ b/engine/src/components/radar.cpp @@ -34,7 +34,9 @@ #include -CRadar::CRadar(): +CRadar::CRadar(EnergyContainer *source, Computer* computer = nullptr): + Component("", 0.0, 0.0, false), + EnergyConsumer(source, false), // TODO: support partial max_range(0), max_cone(-1), lock_cone(0), @@ -45,8 +47,9 @@ CRadar::CRadar(): locked(false), can_lock(false), tracking_active(true), + damaged_(false), original(nullptr), - computer(nullptr) + computer(computer) { max_range = configuration()->computer_config.default_max_range; @@ -54,19 +57,56 @@ CRadar::CRadar(): lock_cone = configuration()->computer_config.default_lock_cone; } -CRadar::CRadar(std::string unit_key, Computer* computer): - max_range(0), - max_cone(-1), - lock_cone(0), - tracking_cone(0), - min_target_size(0), - type(RadarType::SPHERE), - capabilities(RadarCapabilities::NONE), - locked(false), - can_lock(false), - tracking_active(true), - original(nullptr), - computer(nullptr) { + + +// This code replaces and fixes the old code in Armed::LockTarget(bool) +void CRadar::Lock() { + if(!computer) { + return; + } + + const Unit *target = computer->target.GetConstUnit(); + + if(!target) { + //std::cerr << "Target is null\n"; + return; + } + + if(!can_lock) { + std::cerr << "Can't lock\n"; + this->locked = false; + return; + } + + /*if(!UnitUtil::isSignificant(target)) { + std::cerr << "Target insignificant\n"; + this->locked = false; + return; + }*/ + + std::cout << "Target locked\n"; + this->locked = true; + +} + +RadarType CRadar::GetType() const { + return type; +} + +bool CRadar::UseFriendFoe() const { + return (capabilities & RadarCapabilities::FRIEND_FOE); +} + +bool CRadar::UseObjectRecognition() const { + return (capabilities & RadarCapabilities::OBJECT_RECOGNITION); +} + +bool CRadar::UseThreatAssessment() const { + return (capabilities & RadarCapabilities::THREAT_ASSESSMENT); +} + +// Component Methods +void CRadar::Load(std::string upgrade_key, std::string unit_key) { can_lock = UnitCSVFactory::GetVariable(unit_key, "Can_Lock", true); // TODO: fix this @@ -116,15 +156,46 @@ CRadar::CRadar(std::string unit_key, Computer* computer): lock_cone = cos(UnitCSVFactory::GetVariable(unit_key, "Lock_Cone", 180.0) * VS_PI / 180); original = nullptr; this->computer = computer; -} +} -void CRadar::WriteUnitString(std::map &unit) { +void CRadar::SaveToCSV(std::map& unit) const { unit["Can_Lock"] = std::to_string(can_lock); unit["Radar_Color"] = std::to_string(capabilities); - unit["Radar_Range"] = std::to_string(max_range); - unit["Tracking_Cone"] = std::to_string(acos(tracking_cone) * 180. / VS_PI); - unit["Max_Cone"] = std::to_string(acos(max_cone) * 180. / VS_PI); - unit["Lock_Cone"] = std::to_string(acos(lock_cone) * 180. / VS_PI); + unit["Radar_Range"] = std::to_string(max_range.Value()); + unit["Tracking_Cone"] = std::to_string(acos(tracking_cone.Value()) * 180. / VS_PI); + unit["Max_Cone"] = std::to_string(acos(max_cone.Value()) * 180. / VS_PI); + unit["Lock_Cone"] = std::to_string(acos(lock_cone.Value()) * 180. / VS_PI); +} + +std::string CRadar::Describe() const { + return std::string(); +} + +bool CRadar::CanDowngrade() const { + return !Damaged(); +} + +bool CRadar::Downgrade() { + max_range.SetMaxValue(0); + max_cone.SetMaxValue(-1); + lock_cone.SetMaxValue(0); + tracking_cone.SetMaxValue(0); + min_target_size.SetMaxValue(0); + type = RadarType::SPHERE; + capabilities = RadarCapabilities::NONE; + locked = false; + can_lock =false; + tracking_active = true; + original = nullptr; + computer = nullptr; +} + +bool CRadar::CanUpgrade(const std::string upgrade_name) const { + return !Damaged(); +} + +bool CRadar::Upgrade(const std::string upgrade_name) { + } void CRadar::Damage() @@ -165,56 +236,18 @@ void CRadar::Damage() if (radar.tracking_cone > maxdam) { radar.tracking_cone = maxdam; }*/ - + damaged_ = true; } void CRadar::Repair() { - -} - -// This code replaces and fixes the old code in Armed::LockTarget(bool) -void CRadar::Lock() { - if(!computer) { - return; - } - - const Unit *target = computer->target.GetConstUnit(); - - if(!target) { - //std::cerr << "Target is null\n"; - return; - } - - if(!can_lock) { - std::cerr << "Can't lock\n"; - this->locked = false; - return; - } - - /*if(!UnitUtil::isSignificant(target)) { - std::cerr << "Target insignificant\n"; - this->locked = false; - return; - }*/ - - std::cout << "Target locked\n"; - this->locked = true; - + damaged_ = false; } -RadarType CRadar::GetType() const { - return type; -} - -bool CRadar::UseFriendFoe() const { - return (capabilities & RadarCapabilities::FRIEND_FOE); +bool CRadar::Damaged() const { + return damaged_; } -bool CRadar::UseObjectRecognition() const { - return (capabilities & RadarCapabilities::OBJECT_RECOGNITION); -} +bool CRadar::Installed() const { -bool CRadar::UseThreatAssessment() const { - return (capabilities & RadarCapabilities::THREAT_ASSESSMENT); -} +} \ No newline at end of file diff --git a/engine/src/components/radar.h b/engine/src/components/radar.h index d6ece020b7..1942a36984 100644 --- a/engine/src/components/radar.h +++ b/engine/src/components/radar.h @@ -33,7 +33,10 @@ #include #include +#include "component.h" +#include "energy_consumer.h" #include "computer.h" +#include "resource/resource.h" class Unit; @@ -51,19 +54,19 @@ enum RadarCapabilities { }; // Can't call it radar because of namespace collision -class CRadar +class CRadar : public Component, public EnergyConsumer { // TODO: move floats to doubles //the max range the radar can handle - float max_range; + Resource max_range; //the dot with (0,0,1) indicating the farthest to the side the radar can handle. - float max_cone; - float lock_cone; - float tracking_cone; + Resource max_cone; + Resource lock_cone; + Resource tracking_cone; //The minimum radius of the target - float min_target_size; + Resource min_target_size; // What kind of type and capability the radar supports RadarType type; @@ -71,22 +74,21 @@ class CRadar bool locked; bool can_lock; bool tracking_active; + bool damaged_; std::unique_ptr original; Computer *computer; + + friend class Armed; friend class Unit; public: - CRadar(); - CRadar(std::string unit_key, Computer* computer); + CRadar(EnergyContainer *source, Computer* computer); void WriteUnitString(std::map &unit); - void Damage(); - void Repair(); - void Lock(); void Unlock() { locked = false; @@ -103,15 +105,36 @@ class CRadar bool UseObjectRecognition() const; bool UseThreatAssessment() const; - float GetMaxRange() const { return max_range; } - float GetMaxCone() const { return max_cone; } - float GetLockCone() const { return lock_cone; } - float GetTrackingCone() const { return tracking_cone; } - float GetMinTargetSize() const { return min_target_size; } + float GetMaxRange() const { return max_range.Value(); } + float GetMaxCone() const { return max_cone.Value(); } + float GetLockCone() const { return lock_cone.Value(); } + float GetTrackingCone() const { return tracking_cone.Value(); } + float GetMinTargetSize() const { return min_target_size.Value(); } bool Locked() const { return locked; } bool CanLock() const { return can_lock; } bool Tracking() const { return tracking_active; } + + // Component Methods + virtual void Load(std::string upgrade_key, std::string unit_key); + + virtual void SaveToCSV(std::map& unit) const; + + virtual std::string Describe() const; // Describe component in base_computer + + virtual bool CanDowngrade() const; + + virtual bool Downgrade(); + + virtual bool CanUpgrade(const std::string upgrade_name) const; + + virtual bool Upgrade(const std::string upgrade_name); + + virtual void Damage(); + virtual void Repair(); + + virtual bool Damaged() const; + virtual bool Installed() const; }; #endif // RADAR_H diff --git a/engine/src/components/reactor.cpp b/engine/src/components/reactor.cpp index 69f9b1e4a9..e7524423da 100644 --- a/engine/src/components/reactor.cpp +++ b/engine/src/components/reactor.cpp @@ -27,34 +27,23 @@ #include "reactor.h" -#include "energy_manager.h" #include "unit_csv_factory.h" #include const std::string REACTOR_RECHARGE = "Reactor_Recharge"; -Reactor::Reactor(): Component("", 0.0, 0.0, false), - EnergyConsumer(EnergyType::Fuel, - EnergyConsumerClassification::Reactor, - EnergyConsumerType::Constant, 0.0), - capacity(0.0), - simulation_atom_var(0.1), - atom_capacity(0.0), - energy(nullptr), - ftl_energy(nullptr) {} - -Reactor::Reactor(double capacity, + + +Reactor::Reactor(EnergyContainer *source, EnergyContainer *energy, EnergyContainer *ftl_energy, - double simulation_atom_var): + double conversion_ratio): Component("", 0.0, 0.0, false), - EnergyConsumer(EnergyType::Fuel, - EnergyConsumerClassification::Reactor, - EnergyConsumerType::Constant, 0.0), - capacity(capacity), - simulation_atom_var(simulation_atom_var), - atom_capacity(capacity * simulation_atom_var), + EnergyConsumer(source, false), + capacity(0.0, 0.0, 0.0), + atom_capacity(0.0), + conversion_ratio(conversion_ratio), energy(energy), ftl_energy(ftl_energy) { } @@ -63,6 +52,7 @@ Reactor::Reactor(double capacity, void Reactor::Load(std::string upgrade_key, std::string unit_key) { capacity = UnitCSVFactory::GetVariable(unit_key, REACTOR_RECHARGE, 0.0f); atom_capacity = capacity * simulation_atom_var; + SetConsumption(capacity * conversion_ratio); } void Reactor::SaveToCSV(std::map& unit) const { @@ -84,6 +74,8 @@ bool Reactor::Downgrade() { } capacity.SetMaxValue(0.0); + atom_capacity = 0.0; + SetConsumption(0.0); return true; } @@ -102,17 +94,19 @@ bool Reactor::Upgrade(const std::string upgrade_name) { capacity = UnitCSVFactory::GetVariable(upgrade_name, REACTOR_RECHARGE, 0.0f); atom_capacity = capacity * simulation_atom_var; + SetConsumption(capacity * conversion_ratio); return true; } void Reactor::Damage() { capacity.RandomDamage(); - atom_capacity = capacity * simulation_atom_var; + atom_capacity = capacity.Value() * simulation_atom_var; } void Reactor::Repair() { capacity.RepairFully(); + atom_capacity = capacity.Value() * simulation_atom_var; } bool Reactor::Damaged() const { @@ -124,13 +118,16 @@ bool Reactor::Installed() const { } void Reactor::Generate() { - // Adjust for available power - double real_capacity = atom_capacity * powered; + double power = Consume(); - double surplus = energy->Charge(real_capacity); - surplus = ftl_energy->Charge(surplus); - - double actual_consumption = atom_capacity - surplus; + // Zero out fuel if power is 0 + if(power < 0.0001) { + ZeroSource(); + return; + } + + double surplus = energy->Charge(atom_capacity * power); + surplus = ftl_energy->Charge(atom_capacity * surplus); } double Reactor::Capacity() const { @@ -139,4 +136,10 @@ double Reactor::Capacity() const { double Reactor::MaxCapacity() const { return capacity.MaxValue(); +} + +void Reactor::SetCapacity(double capacity) { + this->capacity.SetMaxValue(capacity); + atom_capacity = capacity * simulation_atom_var; + SetConsumption(capacity * conversion_ratio); } \ No newline at end of file diff --git a/engine/src/components/reactor.h b/engine/src/components/reactor.h index 799d2a7f96..9aebf05555 100644 --- a/engine/src/components/reactor.h +++ b/engine/src/components/reactor.h @@ -38,18 +38,17 @@ class EnergyManager; class Reactor: public Component, public EnergyConsumer { Resource capacity; // Capacity per second - const double simulation_atom_var; // Atom definition. typically 0.1 second double atom_capacity; // Capacity per atom + const double conversion_ratio; // Used to calculate fuel consumption EnergyContainer *energy; EnergyContainer *ftl_energy; public: - Reactor(); - Reactor(double capacity, // How much energy does the reactor generate - EnergyContainer *energy, - EnergyContainer *ftl_energy, - double simulation_atom_var); + Reactor(EnergyContainer *source, + EnergyContainer *energy, + EnergyContainer *ftl_energy, + double conversion_ratio = 0.0001); // < 0.01 or very short flight virtual void Load(std::string upgrade_key, std::string unit_key); @@ -75,6 +74,7 @@ class Reactor: public Component, public EnergyConsumer void Generate(); double Capacity() const; double MaxCapacity() const; + void SetCapacity(double capacity); }; #endif // REACTOR_H diff --git a/engine/src/components/shield.cpp b/engine/src/components/shield.cpp index b5c5a39be0..d3ca331bb8 100644 --- a/engine/src/components/shield.cpp +++ b/engine/src/components/shield.cpp @@ -63,13 +63,11 @@ std::string shield_facets_two[2] = { // Note that we need to define FacetConfiguration during load -Shield::Shield(): +Shield::Shield(EnergyContainer *source): Component("", 0.0, 0.0, false), DamageableLayer(2, FacetConfiguration::zero, Health(2, 0), false), - EnergyConsumer(EnergyType::Energy, - EnergyConsumerClassification::Shield, - EnergyConsumerType::Constant, 0.0), + EnergyConsumer(source, true), regeneration(0,0,0), power(1.0,0.0,1.0) {} @@ -125,8 +123,7 @@ void Shield::Load(std::string upgrade_key, std::string unit_key, // Power draw for maintenance and regeneration // TODO: implement fully - double c = TotalMaxLayerValue()/10 + regeneration; - consumption.SetMaxValue(c); + consumption = TotalMaxLayerValue()/10 + regeneration; } @@ -365,16 +362,12 @@ void Shield::Regenerate(bool ftl, bool player_ship) { return; } - double actual_recharge = regeneration.Value() * powered * simulation_atom_var; + double power = Consume(); + double actual_recharge = regeneration.Value() * power * simulation_atom_var; // TODO: adjust for nebula // Discharge shields due to energy or SPEC or cloak if (ftl && !shield_in_ftl) { - if(player_ship) { - static int i = 0; - printOnceInAHundred(i, "FTL", std::to_string(actual_recharge)); - } - // "Damage" power SetPowerCap(0.0); } else { diff --git a/engine/src/components/shield.h b/engine/src/components/shield.h index 936b0d187e..8c50032cd2 100644 --- a/engine/src/components/shield.h +++ b/engine/src/components/shield.h @@ -76,7 +76,7 @@ class Shield : public Component, public DamageableLayer, public EnergyConsumer { friend class Damageable; public: - Shield(); + Shield(EnergyContainer *source); virtual void Load(std::string upgrade_key, std::string unit_key, Unit *unit, double difficulty); // Load from dictionary diff --git a/engine/src/components/tests/armor_tests.cpp b/engine/src/components/tests/armor_tests.cpp index cdc1f1d7ea..1843f85eae 100644 --- a/engine/src/components/tests/armor_tests.cpp +++ b/engine/src/components/tests/armor_tests.cpp @@ -25,6 +25,8 @@ Armor createArmor(double health) { facets.push_back(health); } armor.UpdateFacets(facets); + + return armor; } void loadArmor(Armor &armor, double health, double max_health) { diff --git a/engine/src/components/tests/balancing_tests.cpp b/engine/src/components/tests/balancing_tests.cpp index 6e8c95ffde..0072c0283a 100644 --- a/engine/src/components/tests/balancing_tests.cpp +++ b/engine/src/components/tests/balancing_tests.cpp @@ -1,13 +1,12 @@ #include #include "energy_container.h" -#include "energy_manager.h" #include "reactor.h" -#include "fuel.h" - double simulation_atom_var = 0.1; +bool fairlyEqual(double a, double b); + struct EnergySetup { double capacity; double fuel_capacity; @@ -22,14 +21,6 @@ struct EnergySetup { }; - -struct ConsumerSetup { - EnergyType energy_type; - EnergyConsumerClassification classification; - EnergyConsumerType consumer_type; - double consumption; -}; - double reactor_capacity = 15; double fuel_capacity = 3.51; // Robin @@ -58,104 +49,129 @@ double SPECDrive = 1; double JumpDrive = 1; struct EnergyManager { - Fuel fuel; + EnergyContainer fuel; EnergyContainer energy; EnergyContainer ftl_energy; Reactor reactor; EnergyManager(EnergySetup setup, double simulation_atom_var): - fuel(), energy(), ftl_energy(), - reactor(setup.capacity, &energy, &ftl_energy, - simulation_atom_var) { + fuel(EnergyType::Fuel), energy(EnergyType::Energy), + ftl_energy(EnergyType::FTL), + reactor(&fuel, &energy, &ftl_energy) { fuel.SetCapacity(setup.fuel_capacity); energy.SetCapacity(setup.energy_capacity); ftl_energy.SetCapacity(setup.ftl_capacity); + reactor.SetCapacity(setup.capacity); + } + + void Print(int counter) { + std::cout << counter << " R: " << reactor.Capacity() << + " F: " << fuel.Level() << + " E: " << energy.Level() << + " S: " << ftl_energy.Level() << std::endl; } }; +struct FuelBurnResult { + double residue; + int iterations; + int seconds; +}; -double fuelBurn(EnergySetup setup, - std::vector energy_consumers, int seconds, +FuelBurnResult fuelBurn(EnergyManager& manager, + std::vector& consumers, + int seconds, int print_every_n = 1000) { int run_time = seconds / simulation_atom_var; - EnergyManager manager = EnergyManager(setup, simulation_atom_var); - // Add fuel consumers + manager.Print(-1); + EXPECT_FALSE(manager.fuel.Depleted()); + EXPECT_FALSE(manager.energy.Depleted()); + EXPECT_FALSE(manager.ftl_energy.Depleted()); - - // Add energy consumers - for(EnergyConsumer consumer : energy_consumers) { - manager.energy.AddConsumer(consumer); - } - - for(int i = 0;i consumers = {}; + + FuelBurnResult result = fuelBurn(manager, consumers, seconds, 1000); + std::cout << "Reactor consumption: " << manager.reactor.GetAtomConsumption() << std::endl; - double result = fuelBurn(setup, std::vector(), - seconds); + std::cout << "NoFuelBurn percent left: " << result.residue * 100 << std::endl; - std::cout << "NoFuelBurn percent left: " << result * 100 << std::endl; + // 1 / (sim_atom_var (0.1) * conversion_ration (0.001)) + EXPECT_GT(result.iterations, 99900); + EXPECT_LT(result.iterations, 100900); } -// This tests a fighter ship with level 1 equipment and steady 50MJ energy consumption +// This tests a fighter ship with level 1 equipment and steady 15MJ energy consumption +// Ship flies for 22 minutes TEST(FuelBurn, RobinNaive_1) { EnergySetup setup = {15.0, 3.51, 100.0, 200.0}; - std::vector consumers_setup = { - EnergyConsumer(EnergyType::Fuel, EnergyConsumerClassification::Drive, - EnergyConsumerType::Constant, 0.0001), - EnergyConsumer(EnergyType::Fuel, EnergyConsumerClassification::Afterburner, - EnergyConsumerType::Constant, 0.0001 * 3 * .05), // Drive consumption x 3 but 5% of flight time - EnergyConsumer(EnergyType::Energy, EnergyConsumerClassification::LifeSupport, - EnergyConsumerType::Constant, 50.0 * simulation_atom_var) // General consumer at 50 per second + EnergyManager manager = EnergyManager(setup, simulation_atom_var); + std::vector consumers = { + EnergyConsumer(&manager.fuel, false, 0.001), // Drive + EnergyConsumer(&manager.fuel, false, 0.001 * 3 * .05), // Afterburner, Drive consumption x 3 but 5% of flight time + EnergyConsumer(&manager.energy, false, 15.0) // General consumer at 15 per second }; int seconds = 60 * 60; // 60 minutes gameplay - double result = fuelBurn(setup, consumers_setup, seconds); + FuelBurnResult result = fuelBurn(manager, consumers, seconds, 1000); + EXPECT_GT(result.iterations, 12000); // More than 10 minutes - std::cout << "NaiveFuelBurn_1 percent left: " << result * 100 << std::endl; + std::cout << "RobinNaive_1 NoFuelBurn percent left: " << result.residue * 100 << std::endl; + //EXPECT_EQ(0,1); // use these to see detailed prints } -// This tests a fighter ship with level 1 equipment and steady 150MJ energy consumption +// This tests a fighter ship with level 1 equipment and steady 40MJ energy consumption +// Ship flies for 10 minutes TEST(FuelBurn, RobinNaive_2) { EnergySetup setup = {44.0, 3.51, 300.0, 200.0}; - std::vector consumers_setup = { - EnergyConsumer(EnergyType::Fuel, EnergyConsumerClassification::Drive, - EnergyConsumerType::Constant, 0.0001), - EnergyConsumer(EnergyType::Fuel, EnergyConsumerClassification::Afterburner, - EnergyConsumerType::Constant, 0.0001 * 3 * .05), // Drive consumption x 3 but 5% of flight time - EnergyConsumer(EnergyType::Energy, EnergyConsumerClassification::LifeSupport, - EnergyConsumerType::Constant, 150.0 * simulation_atom_var) // General consumer at 50 per second + EnergyManager manager = EnergyManager(setup, simulation_atom_var); + std::vector consumers = { + EnergyConsumer(&manager.fuel, false, 0.001), // Drive + EnergyConsumer(&manager.fuel, false, 0.001 * 3 * .05), // Afterburner, Drive consumption x 3 but 5% of flight time + EnergyConsumer(&manager.energy, false, 40) // General consumer at 40 per second }; int seconds = 60 * 60; // 60 minutes gameplay - double result = fuelBurn(setup, consumers_setup, seconds); + FuelBurnResult result = fuelBurn(manager, consumers, seconds, 1000); + EXPECT_GT(result.iterations, 6000); // More than 10 minutes - std::cout << "NaiveFuelBurn_2 percent left: " << result * 100 << std::endl; + std::cout << "NaiveFuelBurn_2 percent left: " << result.residue * 100 << std::endl; + //EXPECT_EQ(0,1); // use these to see detailed prints } \ No newline at end of file diff --git a/engine/src/components/tests/energy_container_tests.cpp b/engine/src/components/tests/energy_container_tests.cpp index 32c1dad1a7..d5bed7838d 100644 --- a/engine/src/components/tests/energy_container_tests.cpp +++ b/engine/src/components/tests/energy_container_tests.cpp @@ -10,20 +10,20 @@ void printContainer(EnergyContainer& container) { } TEST(EnergyContainer, Sanity) { - EnergyContainer container = EnergyContainer(); + EnergyContainer container = EnergyContainer(EnergyType::Energy); EXPECT_EQ(container.Level(), 0.0); container.SetCapacity(10.0, true); printContainer(container); - EnergyConsumer consumer = EnergyConsumer(EnergyType::Fuel, - EnergyConsumerClassification::Drive, - EnergyConsumerType::Constant, 1.0); - container.AddConsumer(consumer); + EnergyConsumer consumer = EnergyConsumer(&container, + false, + 1.0); + int i=0; - while(container.Level() > 0 && i < 100) { - container.Act(); + while(!container.Depleted() && i < 100) { + consumer.Consume(); printContainer(container); i++; } diff --git a/engine/src/damage/tests/layer_tests.cpp b/engine/src/damage/tests/layer_tests.cpp index 2c062f9607..7b5802bbd6 100644 --- a/engine/src/damage/tests/layer_tests.cpp +++ b/engine/src/damage/tests/layer_tests.cpp @@ -59,25 +59,27 @@ TEST(Layer, Sanity_2) { EXPECT_EQ(health.health.MaxValue(), 10); EXPECT_EQ(health.health.Value(), 10); + // This is copied in the next line. Do not refer to this variable. DamageableLayer layer = DamageableLayer(0, FacetConfiguration::one, health, true); + std::vector layers = { layer }; - EXPECT_EQ(layer.facets[0].health.MaxValue(), 10); - EXPECT_EQ(layer.facets[0].health.Value(), 10); + EXPECT_EQ(layers[0].facets[0].health.MaxValue(), 10); + EXPECT_EQ(layers[0].facets[0].health.Value(), 10); DamageableLayer* ptr = &layers[0]; EXPECT_EQ(ptr->facets[0].health.MaxValue(), 10); - EXPECT_EQ(layer.facets[0].health.Value(), 10); + EXPECT_EQ(layers[0].facets[0].health.Value(), 10); std::vector new_health = {50}; ptr->UpdateFacets(new_health); - EXPECT_EQ(layer.facets[0].health.MaxValue(), 50); - EXPECT_EQ(layer.facets[0].health.Value(), 50); + EXPECT_EQ(layers[0].facets[0].health.MaxValue(), 50); + EXPECT_EQ(layers[0].facets[0].health.Value(), 50); EXPECT_EQ(ptr->facets[0].health.MaxValue(), 50); EXPECT_EQ(ptr->facets[0].health.Value(), 50); - EXPECT_EQ(ptr, &layer); + EXPECT_EQ(ptr, &layers[0]); } // How embarrassing