From 65ac8621de31cbfae1922f75653f9100525642ff Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Tue, 7 May 2024 16:34:44 +0300 Subject: [PATCH 1/3] Add Component, Reactor, EConsumer and EContainer classes Add unit tests. Remove UnitCsvFactory reliance on VSFileSystem for better separation of concerns. Game doesn't build because unit tests require VSFileSystem and that pulls in half the game with it. As this is imported from the original lib component branch by file, some stuff here is for future commits. --- engine/CMakeLists.txt | 14 ++ engine/src/cmd/unit_csv_factory.cpp | 10 +- engine/src/cmd/unit_csv_factory.h | 7 +- engine/src/cmd/unit_generic.cpp | 3 +- engine/src/components/component.cpp | 76 +++++++ engine/src/components/component.h | 92 ++++++++ engine/src/components/energy_consumer.cpp | 68 ++++++ engine/src/components/energy_consumer.h | 52 +++++ engine/src/components/energy_container.cpp | 199 ++++++++++++++++++ engine/src/components/energy_container.h | 97 +++++++++ engine/src/components/reactor.cpp | 145 +++++++++++++ engine/src/components/reactor.h | 80 +++++++ .../src/components/tests/balancing_tests.cpp | 177 ++++++++++++++++ .../tests/energy_container_tests.cpp | 30 +++ engine/src/universe.cpp | 3 +- 15 files changed, 1047 insertions(+), 6 deletions(-) create mode 100644 engine/src/components/component.cpp create mode 100644 engine/src/components/component.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/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 45ff7d4d96..63f3fb91de 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -165,6 +165,7 @@ IF (UNIX) ${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} ${Vega_Strike_BINARY_DIR}/src /usr/include/harfbuzz/ @@ -175,6 +176,7 @@ ELSE () ${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} ${Vega_Strike_BINARY_DIR}/src ) @@ -714,6 +716,14 @@ SET(LIBRESOURCE src/cmd/json.cpp ) +SET(LIBCOMPONENT + src/components/component.cpp + + src/components/energy_consumer.cpp + src/components/energy_container.cpp + src/components/reactor.cpp + ) + SET(LIBGUI_SOURCES src/gui/button.cpp src/gui/control.cpp @@ -1123,6 +1133,7 @@ ADD_LIBRARY(vegastrike-engine_com ${LIBCONFIG} ${LIBDAMAGE} ${LIBRESOURCE} + ${LIBCOMPONENT} ${LIBAI_SOURCES} ${LIBCMD_SOURCES} ${LIBNET_SOURCES} @@ -1709,12 +1720,15 @@ IF (USE_GTEST) src/resource/tests/manifest_tests.cpp src/resource/tests/random_tests.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/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 3d6b87c8bf..62c0e46dee 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"}; @@ -102,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; @@ -124,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 3c0498b1d9..23c6d462a3 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -454,7 +454,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/component.cpp b/engine/src/components/component.cpp new file mode 100644 index 0000000000..c5d22eb4e7 --- /dev/null +++ b/engine/src/components/component.cpp @@ -0,0 +1,76 @@ +/* + * 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 "unit_csv_factory.h" + +Component::Component(double mass, double volume, bool integral): + unit_key(""), + upgrade_name(""), + mass(mass), volume(volume), + integral(integral) {} + + +void Component::Load(std::string upgrade_key, std::string unit_key) { + this->unit_key = unit_key; + 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; +} + +// 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; +} + +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 new file mode 100644 index 0000000000..870af22149 --- /dev/null +++ b/engine/src/components/component.h @@ -0,0 +1,92 @@ +/* + * 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 + +#include +#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: + std::string unit_key; // Areus.blank + 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(double mass, + double volume, bool integral); + + // Load from units dictionary + virtual void Load(std::string upgrade_key, std::string unit_key); + + virtual void SaveToCSV(std::map& unit) const = 0; + + 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 bool CanDowngrade() const = 0; + + virtual bool Downgrade() = 0; + + virtual bool CanUpgrade(const std::string upgrade_key) const = 0; + + virtual bool Upgrade(const std::string upgrade_key) = 0; + + virtual void Damage() = 0; + virtual void Repair() = 0; + + virtual bool Damaged() const = 0; + virtual bool Installed() const = 0; + + void SetIntegral(bool integral); +}; +#endif // COMPONENT_H diff --git a/engine/src/components/energy_consumer.cpp b/engine/src/components/energy_consumer.cpp new file mode 100644 index 0000000000..8fd618acd4 --- /dev/null +++ b/engine/src/components/energy_consumer.cpp @@ -0,0 +1,68 @@ +/* + * 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 -*- + +#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::SetSource(EnergyContainer* source) { + this->source = source; +} + +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 new file mode 100644 index 0000000000..34dd4c8b40 --- /dev/null +++ b/engine/src/components/energy_consumer.h @@ -0,0 +1,52 @@ +/* + * 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_container.h" + +class EnergyConsumer { + EnergyContainer *source; + bool partial; // Can power consumer with less energy than requested +protected: + double consumption; // Directly converted to atomic. Mostly for book keeping. + double atom_consumption; // consumption per 0.1 seconds. + + 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 SetSource(EnergyContainer* source); + void ZeroSource(); + +}; + +#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..b3c692a903 --- /dev/null +++ b/engine/src/components/energy_container.cpp @@ -0,0 +1,199 @@ +/* + * 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 "unit_csv_factory.h" + +#include + +const std::string FUEL_CAPACITY = "Fuel_Capacity"; +const std::string CAPACITOR = "Warp_Capacitor"; +const std::string FTL_CAPACITOR = "Primary_Capacitor"; + +EnergyContainer::EnergyContainer(EnergyType type): + Component(0.0, 0.0, false), + type(type), + level(Resource(0.0,0.0,0.0)) {} + + +// 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(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; + double actual_usage = old_level - level.Value(); + return actual_usage / quantity; +} + +bool EnergyContainer::Depleted() const { + return (level.Value() < 0.0001); +} + +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::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 new file mode 100644 index 0000000000..eecfbd719c --- /dev/null +++ b/engine/src/components/energy_container.h @@ -0,0 +1,97 @@ +/* + * 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 "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: public Component +{ + EnergyType type; + Resource level; + +public: + EnergyContainer(EnergyType type); + + // Return value - any surplus charge + double Charge(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); + double Level() const; + double MaxLevel() const; + double Percent() const; + void Refill(); + + void Zero(); + + // 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/reactor.cpp b/engine/src/components/reactor.cpp new file mode 100644 index 0000000000..fe2ea5bd5a --- /dev/null +++ b/engine/src/components/reactor.cpp @@ -0,0 +1,145 @@ +/* + * 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 "unit_csv_factory.h" + +#include + +const std::string REACTOR_RECHARGE = "Reactor_Recharge"; + + + +Reactor::Reactor(EnergyContainer *source, + EnergyContainer *energy, + EnergyContainer *ftl_energy, + double conversion_ratio): + Component(0.0, 0.0, false), + EnergyConsumer(source, false), + capacity(0.0, 0.0, 0.0), + atom_capacity(0.0), + conversion_ratio(conversion_ratio), + energy(energy), + ftl_energy(ftl_energy) { +} + + +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 { + // 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); + atom_capacity = 0.0; + SetConsumption(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; + } + + 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; + SetConsumption(capacity * conversion_ratio); + + return true; +} + +void Reactor::Damage() { + capacity.RandomDamage(); + atom_capacity = capacity.Value() * simulation_atom_var; +} + +void Reactor::Repair() { + capacity.RepairFully(); + atom_capacity = capacity.Value() * simulation_atom_var; +} + +bool Reactor::Damaged() const { + return capacity.Damaged(); +} + +bool Reactor::Installed() const { + return capacity.MaxValue() > 0; +} + +void Reactor::Generate() { + double power = Consume(); + + // 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 { + return capacity.Value(); +} + +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 new file mode 100644 index 0000000000..9aebf05555 --- /dev/null +++ b/engine/src/components/reactor.h @@ -0,0 +1,80 @@ +/* + * 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 "component.h" +#include "energy_container.h" +#include "energy_consumer.h" +#include "resource/resource.h" + +class EnergyManager; + +class Reactor: public Component, public EnergyConsumer +{ + Resource capacity; // Capacity per second + double atom_capacity; // Capacity per atom + const double conversion_ratio; // Used to calculate fuel consumption + + EnergyContainer *energy; + EnergyContainer *ftl_energy; + +public: + 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); + + 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; + void SetCapacity(double capacity); +}; + +#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..0072c0283a --- /dev/null +++ b/engine/src/components/tests/balancing_tests.cpp @@ -0,0 +1,177 @@ +#include + +#include "energy_container.h" +#include "reactor.h" + +double simulation_atom_var = 0.1; + +bool fairlyEqual(double a, double b); + +struct EnergySetup { + double capacity; + double fuel_capacity; + double energy_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) {} +}; + + +double reactor_capacity = 15; + +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; + +struct EnergyManager { + EnergyContainer fuel; + EnergyContainer energy; + EnergyContainer ftl_energy; + Reactor reactor; + + EnergyManager(EnergySetup setup, + double 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; +}; + +FuelBurnResult fuelBurn(EnergyManager& manager, + std::vector& consumers, + int seconds, + int print_every_n = 1000) { + int run_time = seconds / simulation_atom_var; + + manager.Print(-1); + EXPECT_FALSE(manager.fuel.Depleted()); + EXPECT_FALSE(manager.energy.Depleted()); + EXPECT_FALSE(manager.ftl_energy.Depleted()); + + int i = 0; + for(;i consumers = {}; + + FuelBurnResult result = fuelBurn(manager, consumers, seconds, 1000); + std::cout << "Reactor consumption: " << manager.reactor.GetAtomConsumption() << std::endl; + + std::cout << "NoFuelBurn percent left: " << result.residue * 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 15MJ energy consumption +// Ship flies for 22 minutes +TEST(FuelBurn, RobinNaive_1) { + EnergySetup setup = {15.0, 3.51, 100.0, 200.0}; + 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 + + FuelBurnResult result = fuelBurn(manager, consumers, seconds, 1000); + EXPECT_GT(result.iterations, 12000); // More than 10 minutes + + 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 40MJ energy consumption +// Ship flies for 10 minutes +TEST(FuelBurn, RobinNaive_2) { + EnergySetup setup = {44.0, 3.51, 300.0, 200.0}; + 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 + + FuelBurnResult result = fuelBurn(manager, consumers, seconds, 1000); + EXPECT_GT(result.iterations, 6000); // More than 10 minutes + + 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 new file mode 100644 index 0000000000..d5bed7838d --- /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(EnergyType::Energy); + EXPECT_EQ(container.Level(), 0.0); + container.SetCapacity(10.0, true); + + printContainer(container); + + EnergyConsumer consumer = EnergyConsumer(&container, + false, + 1.0); + + + int i=0; + while(!container.Depleted() && i < 100) { + consumer.Consume(); + printContainer(container); + i++; + } +} \ No newline at end of file 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 b1dcb7cebb9bfbf6052dd8048ffe558b959add1b Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Wed, 8 May 2024 08:43:04 +0300 Subject: [PATCH 2/3] Fix error C7555 in CICD on windows use of designated initializers requires at least '/std:c++20' --- engine/src/components/tests/balancing_tests.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/engine/src/components/tests/balancing_tests.cpp b/engine/src/components/tests/balancing_tests.cpp index 0072c0283a..081ab49f79 100644 --- a/engine/src/components/tests/balancing_tests.cpp +++ b/engine/src/components/tests/balancing_tests.cpp @@ -77,6 +77,9 @@ struct FuelBurnResult { double residue; int iterations; int seconds; + + FuelBurnResult(double residue, int iterations, int seconds): + residue(residue), iterations(iterations), seconds(seconds) {} }; FuelBurnResult fuelBurn(EnergyManager& manager, @@ -107,11 +110,7 @@ FuelBurnResult fuelBurn(EnergyManager& manager, } } - FuelBurnResult result { - .residue = manager.fuel.Percent(), - .iterations = i, - .seconds = i/60 - }; + FuelBurnResult result(manager.fuel.Percent(), i, i/60); return result; }; From 2ed44a29fa5c850009b5f1035f772431feb34343 Mon Sep 17 00:00:00 2001 From: Roy Falk Date: Sun, 12 May 2024 07:50:53 +0300 Subject: [PATCH 3/3] Fix bug - Implement Downgrade in Component Reverse order of visibility in EnergyConsumer. Make visibility modifiers explicit in EnergyContainer and Reactor. --- engine/src/components/component.cpp | 2 ++ engine/src/components/component.h | 2 +- engine/src/components/energy_consumer.h | 6 ++++-- engine/src/components/energy_container.h | 1 + engine/src/components/reactor.h | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/engine/src/components/component.cpp b/engine/src/components/component.cpp index c5d22eb4e7..54b9364098 100644 --- a/engine/src/components/component.cpp +++ b/engine/src/components/component.cpp @@ -69,6 +69,8 @@ bool Component::Downgrade() { mass = 0.0; volume = 0.0; + + return true; } void Component::SetIntegral(bool integral) { diff --git a/engine/src/components/component.h b/engine/src/components/component.h index 870af22149..28b17f32f1 100644 --- a/engine/src/components/component.h +++ b/engine/src/components/component.h @@ -75,7 +75,7 @@ class Component virtual bool CanDowngrade() const = 0; - virtual bool Downgrade() = 0; + virtual bool Downgrade(); virtual bool CanUpgrade(const std::string upgrade_key) const = 0; diff --git a/engine/src/components/energy_consumer.h b/engine/src/components/energy_consumer.h index 34dd4c8b40..cc1eee1097 100644 --- a/engine/src/components/energy_consumer.h +++ b/engine/src/components/energy_consumer.h @@ -31,13 +31,15 @@ #include "energy_container.h" class EnergyConsumer { - EnergyContainer *source; - bool partial; // Can power consumer with less energy than requested protected: double consumption; // Directly converted to atomic. Mostly for book keeping. double atom_consumption; // consumption per 0.1 seconds. static double simulation_atom_var; + +private: + EnergyContainer *source; + bool partial; // Can power consumer with less energy than requested public: EnergyConsumer(EnergyContainer *source, bool partial, double consumption = 0.0); double Consume(); diff --git a/engine/src/components/energy_container.h b/engine/src/components/energy_container.h index eecfbd719c..db5008451d 100644 --- a/engine/src/components/energy_container.h +++ b/engine/src/components/energy_container.h @@ -51,6 +51,7 @@ enum class EnergyType { */ class EnergyContainer: public Component { +private: EnergyType type; Resource level; diff --git a/engine/src/components/reactor.h b/engine/src/components/reactor.h index 9aebf05555..2176021ab4 100644 --- a/engine/src/components/reactor.h +++ b/engine/src/components/reactor.h @@ -37,6 +37,7 @@ class EnergyManager; class Reactor: public Component, public EnergyConsumer { +private: Resource capacity; // Capacity per second double atom_capacity; // Capacity per atom const double conversion_ratio; // Used to calculate fuel consumption