diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 4b9e6aca1e..f6fddfeec2 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,23 @@ SET(LIBRESOURCE src/resource/product.cpp ) +SET(LIBCOMPONENT + src/components/component.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 + src/components/energy_consumer.cpp + src/components/energy_container.cpp + src/components/jump_drive.cpp + src/components/radar.cpp + src/components/reactor.cpp + ) + SET(LIBGUI_SOURCES src/gui/button.cpp src/gui/control.cpp @@ -826,7 +845,7 @@ SET(LIBCMD_SOURCES src/cmd/computer.cpp src/cmd/intelligent.cpp - src/cmd/energetic.cpp + src/cmd/planetary_orbit.cpp @@ -1058,6 +1077,7 @@ ADD_LIBRARY(vegastrike-engine_com ${LIBCONFIG} ${LIBDAMAGE} ${LIBRESOURCE} + ${LIBCOMPONENT} ${LIBAI_SOURCES} ${LIBCMD_SOURCES} ${LIBNET_SOURCES} @@ -1619,12 +1639,16 @@ 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 + src/components/tests/armor_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 964d9a7b5e..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 @@ -1325,7 +1326,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) { @@ -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/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/docking.cpp b/engine/src/cmd/ai/docking.cpp index de8e4e28b2..987198b19c 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->ftl_energy.Refill(); 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->ftl_energy.Refill(); return true; } } diff --git a/engine/src/cmd/ai/fire.cpp b/engine/src/cmd/ai/fire.cpp index 095ce08610..be49e5be6e 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[] = { @@ -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(); } @@ -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; } @@ -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..aa68350374 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; @@ -1732,7 +1731,7 @@ void FireKeyboard::Execute() { float f_result = f().shieldpowerstate; if (f_result != 1) { - parent->shield->AdjustPower(f_result); + parent->shield->SetPower(f_result); } if (f().firekey == PRESS || f().jfirekey == PRESS || j().firekey == DOWN || j().jfirekey == DOWN) { if (!_Universe->AccessCockpit()->CanDrawNavSystem()) { @@ -1779,12 +1778,11 @@ 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; - parent->LockTarget(!parent->TargetLocked()); + parent->radar.ToggleLock(); } if (f().ECMkey == PRESS) { f().ECMkey = DOWN; @@ -1815,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; @@ -1896,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; @@ -2034,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; @@ -2060,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(); @@ -2178,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/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/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/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/ai/warpto.cpp b/engine/src/cmd/ai/warpto.cpp index 5f3232ed24..560116d8fb 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->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 25fa01bbc3..60db3cb60c 100644 --- a/engine/src/cmd/armed.cpp +++ b/engine/src/cmd/armed.cpp @@ -196,10 +196,11 @@ 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); - 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; @@ -249,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->energy_rate * simulation_atom_var > unit->energy) { + + 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->energy_rate > unit->energy) { + if (power < 1.0) { if (!want_to_fire) { i->UnFire(); } @@ -274,15 +278,20 @@ 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()) { - unit->energy -= i->type->energy_rate * simulation_atom_var; + // TODO: switch to standard energy usage + 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 -= i->type->energy_rate; - } + 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) { @@ -324,13 +333,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 +416,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 +426,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 +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->computer.radar.trackingcone; + 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/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 e97d524dc8..4af3a99162 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -84,6 +84,16 @@ 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) { + 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); +} + + int BaseComputer::dirty = 0; static GFXColor UnsaturatedColor(float r, float g, float b, float a = 1.0f) { @@ -1865,6 +1875,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) { @@ -1872,16 +1888,20 @@ 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 + 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) { commitButton->setHidden(false); commitButton->setLabel("Sell"); @@ -3892,6 +3912,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(); @@ -5236,15 +5259,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 +5278,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 +5291,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 @@ -5313,20 +5337,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->reactor.Capacity() * 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.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->warpCapData() * RSconverter * Wconv, + playerUnit->ftl_energy.MaxLevel() * RSconverter * Wconv, 0, "MJ"); @@ -5349,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->warpCapData() < 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"; @@ -5359,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, energyRechargeData())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Installs reactor with recharge rate: #-c", - playerUnit->energyRechargeData() * RSconverter, 0, "MJ/s"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, maxEnergyData())) + 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->maxEnergyData() * RSconverter), 0, "MJ"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, getWarpEnergy())) + (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->getWarpEnergy() * 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, energyRechargeData())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Increases recharge rate by #-c", - playerUnit->energyRechargeData() * RSconverter, 0, "MJ/s"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, maxEnergyData())) + playerUnit->reactor.Capacity() * RSconverter, 0, "MJ/s"); + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) PRETTY_ADDU(statcolor + "Adds #-c", - (playerUnit->maxEnergyData() * RSconverter), + (playerUnit->energy.MaxLevel() * RSconverter), 0, "MJ of storage to main capacitor banks"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, getWarpEnergy())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, ftl_energy.MaxLevel())) PRETTY_ADDU(statcolor + "Adds #-c", - playerUnit->getWarpEnergy() * 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, energyRechargeData())) + if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) 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->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->maxEnergyData() - 1), 0, "%"); - if (MODIFIES(replacement_mode, playerUnit, blankUnit, getWarpEnergy())) + 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->getWarpEnergy() - 1) * 100, 0, "%"); + (playerUnit->ftl_energy.MaxLevel() - 1) * 100, 0, "%"); break; default: //Failure text += "Oh dear, this wasn't an upgrade. Please debug code."; @@ -5446,8 +5472,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"); } @@ -5532,17 +5558,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"); @@ -5565,17 +5591,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; @@ -5639,7 +5665,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"); @@ -5672,7 +5698,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) { @@ -5687,7 +5713,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) { @@ -5699,7 +5725,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; @@ -5707,7 +5733,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) : @@ -5720,9 +5746,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#"; @@ -5738,39 +5764,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->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->energyRechargeData() * 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->maxEnergyData() - 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->warpCapData() < 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->energyRechargeData() + 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#"; @@ -5779,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->energyRechargeData() * RSconverter); + * num_shields / (playerUnit->reactor.Capacity() * RSconverter); text += (boost::format("%1$.2f") % maint_draw_percent).str(); text += " %."; if (maint_draw_percent > 60) { @@ -5798,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->energyRechargeData() * 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->maxEnergyData() + (playerUnit->reactor.Capacity() * RSconverter) / (totalWeaponEnergyUsage - - ((playerUnit->energyRechargeData() * RSconverter - maint_draw))), + - ((playerUnit->reactor.Capacity() * RSconverter - maint_draw))), 2, "seconds"); } 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/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/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..004c63cf4d 100644 --- a/engine/src/cmd/damageable.cpp +++ b/engine/src/cmd/damageable.cpp @@ -43,6 +43,22 @@ #include #include "configuration/configuration.h" +Damageable::Damageable() : DamageableObject(), + killed(false), + hull_(), + armor_(), + shield_(nullptr) { // TODO: initialize elsewhere + 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 { const int shield_min = 5; @@ -78,8 +94,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) { @@ -189,7 +205,7 @@ void Damageable::ApplyDamage(const Vector &pnt, // Additional house cleaning unit->PrimeOrders(); - unit->energy.Zero(); + // TODO: clear components unit->Split(rand() % 3 + 1); @@ -469,52 +485,47 @@ 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) { + 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 } } float Damageable::BShieldData() const { - switch (shield->number_of_facets) { + 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 } } float Damageable::LShieldData() const { - switch (shield->number_of_facets) { + switch (shield_.number_of_facets) { 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 } } float Damageable::RShieldData() const { - switch (shield->number_of_facets) { + switch (shield_.number_of_facets) { 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 +536,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(); } } @@ -534,45 +545,9 @@ 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; - //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 = unit->shield->number_of_facets; - const float total_max_shields = unit->shield->TotalMaxLayerValue(); - - // No point in all this code if there are no shields. - if (shield_facets < 2 || total_max_shields == 0) { - return; - } - float shield_recharge = unit->constrained_charge_to_shields * simulation_atom_var; - if (unit->GetNebula() != nullptr) { - shield_recharge *= nebshields; - } - - // Adjust other (enemy) ships for difficulty - if (!player_ship) { - shield_recharge *= difficulty; - } - - - // Discharge shields due to energy or SPEC - if ((in_warp && !shields_in_spec) || !unit->sufficient_energy_to_recharge_shields) { - shield->Discharge(discharge_rate, min_shield_discharge); - } else { - // Shield regeneration - shield->Regenerate(shield_recharge); - } -} float Damageable::MaxShieldVal() const { Damageable *damageable = const_cast(this); diff --git a/engine/src/cmd/damageable.h b/engine/src/cmd/damageable.h index a350fbaf43..05fd5e95ec 100644 --- a/engine/src/cmd/damageable.h +++ b/engine/src/cmd/damageable.h @@ -29,6 +29,11 @@ #include "gfx/vec.h" #include "mount_size.h" +#include "components/armor.h" +#include "components/hull.h" +#include "components/shield.h" +#include + class Unit; struct GFXColor; @@ -36,34 +41,27 @@ struct GFXColor; * @brief The Damageable class TODO */ class Damageable : public DamageableObject { +protected: + Hull hull_; + Armor armor_; + Shield shield_; + + friend class UpgradeableUnit; + public: DamageableLayer *hull; - DamageableLayer *armor; - DamageableLayer *shield; - - float *current_hull; - float *max_hull; + Armor *armor; + Shield *shield; - // These are only used for upgrade due to macros - // TODO: refactor - float upgrade_hull; - - float shield_regeneration; + double *current_hull; + double *max_hull; //Is dead already? bool killed; // 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; @@ -87,23 +85,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]; - } - - const float GetShieldRegeneration() const { - return shield->facets[as_integer(FacetName::left_top_front)].regeneration; + return shield_; } virtual const float GetHullPercent() const { @@ -111,7 +105,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/drawable.cpp b/engine/src/cmd/drawable.cpp index a4095163a5..e18c0eaf0a 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.Visibility(), 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.Visibility(), 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; @@ -768,7 +757,7 @@ void Drawable::DrawSubunits(bool on_screen, Matrix wmat, int cloak, float averag (isAutoTrackingMount(unit->mounts[i].size) && (unit->mounts[i].time_to_lock <= 0) && unit->TargetTracked()) ? unit->Target() : NULL, - unit->computer.radar.trackingcone); + unit->radar.GetTrackingCone()); } } @@ -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/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 d8930247d6..d79ce6d562 100644 --- a/engine/src/cmd/jump_capable.cpp +++ b/engine/src/cmd/jump_capable.cpp @@ -98,12 +98,11 @@ std::string GenerateAutoError(Unit *me, Unit *targ) { /////////////////////////////////////////////// -JumpCapable::JumpCapable() : activeStarSystem(nullptr) { -}; +JumpCapable::JumpCapable() : activeStarSystem(nullptr) {}; 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 +137,16 @@ 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 + double power = unit->jump_drive.Consume(); + if (power < 1.0) { if (!ignore_energy_requirements) { return false; } } + signed char Guaranteed = ComputeAutoGuarantee(unit); if (Guaranteed == Mission::AUTO_OFF) { return false; @@ -212,7 +216,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 = @@ -270,7 +274,10 @@ bool JumpCapable::AutoPilotToErrorMessage(const Unit *target, failuremessage = configuration()->graphics_config.hud.already_near_message; return false; } - unit->warpenergy -= totpercent * unit->jump.insysenergy; + + // TODO: make sure we don't consume energy before here. + // See line 143 above + if (unsafe == false && totpercent == 0) { end = endne; } @@ -499,10 +506,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..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 "energetic.h" #include @@ -34,8 +33,18 @@ class JumpCapable { public: StarSystem *activeStarSystem; - + 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 +57,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..e647566e47 100644 --- a/engine/src/cmd/mount.cpp +++ b/engine/src/cmd/mount.cpp @@ -253,7 +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->energy_rate > caller->energy) { + } 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. @@ -289,6 +292,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 +304,8 @@ bool Mount::PhysicsAlignedFire(Unit *caller, } break; case WEAPON_TYPE::BOLT: - caller->energy -= type->energy_rate; + //caller->energy.Deplete(type->GetConsumption()); + hint[Unit::UNIT_BOLT] = Bolt(type, mat, velocity, @@ -308,7 +314,8 @@ bool Mount::PhysicsAlignedFire(Unit *caller, break; case WEAPON_TYPE::BALL: { - caller->energy -= type->energy_rate; + //caller->energy.Deplete(type->GetConsumption()); + hint[Unit::UNIT_BOLT] = BoltDrawManager::GetInstance().AddBall(type, mat, velocity, owner, hint[Unit::UNIT_BOLT]); break; 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 5c1dce7cb8..a60e63c8f0 100644 --- a/engine/src/cmd/movable.cpp +++ b/engine/src/cmd/movable.cpp @@ -55,7 +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) { + Momentofinertia(0.01) { cur_sim_queue_slot = rand() % SIM_QUEUE_SIZE; const Vector default_angular_velocity(configuration()->general_config.pitch, configuration()->general_config.yaw, @@ -474,8 +474,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 +532,13 @@ Vector Movable::MaxTorque(const Vector &torque) { } Vector Movable::ClampTorque(const Vector &amt1) { - Energetic *energetic = dynamic_cast(this); - Vector Res = amt1; + Unit *unit = vega_dynamic_cast_ptr(this); - energetic->WCWarpIsFuelHack(true); + Vector Res = amt1; - float fuelclamp = (energetic->fuelData() <= 0) ? configuration()->fuel.no_fuel_thrust : 1; + double power = unit->drive.Consume(); + // no_fuel_thrust = 0.4, so even with no fuel, we should keep flying + 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); } @@ -550,23 +550,23 @@ 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 = 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_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(); float tmp = velocity.Magnitude(); if (tmp > fabs(limit)) { return velocity * (limit / tmp); @@ -615,35 +615,18 @@ 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; - } + 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; + // 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_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); } @@ -660,53 +643,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 +707,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..b6efea1e0e 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; @@ -38,9 +41,6 @@ class UnitCollection; struct Quaternion; class Movable { - -protected: - 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..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; - maxwarpenergy = un->warpCapData(); + 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 abf7094832..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->energyData(); + 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 91d28caa76..36eaf6f5ba 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]->ftl_energy.MaxLevel()); if (chancetospawn > rand() % 100) { thus->SubUnits.prepend(xml.units[a]); } else { @@ -643,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()); @@ -731,12 +735,16 @@ 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); + + + 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); - 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); @@ -745,80 +753,36 @@ 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); } - 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++; + // 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; } - /* - 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); - } + // Load shield + shield->Load("", unit_key, this, difficulty_modifier); // 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); + 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); forcejump = UnitCSVFactory::GetVariable(unit_key, "Wormhole", false); @@ -828,14 +792,15 @@ 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 + 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; limits.roll = UnitCSVFactory::GetVariable(unit_key, "Maneuver_Roll", 0.0f) * VS_PI / 180.0; @@ -874,76 +839,10 @@ 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); - 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; - } - - 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); + 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); @@ -1158,6 +1057,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 = ""; @@ -1282,7 +1182,12 @@ string Unit::WriteUnitString() { } unit["Mass"] = tos(Mass); unit["Moment_Of_Inertia"] = tos(Momentofinertia); - unit["Fuel_Capacity"] = tos(fuel); + + // Components + 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); @@ -1296,69 +1201,25 @@ 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; - { - 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].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); - - 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); - - 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); - break; - - case 0: - // No shields - break; - - default: - // This should not happen - std::cout << number_of_shield_emitters << "\n"; - assert(0); - } - } + shield->SaveToCSV(unit); + radar.SaveToCSV(unit); - //TODO: lib_damage shield leak and efficiency - unit["Shield_Leak"] = tos(0); //tos( shield.leak/100.0 ); - unit["Shield_Efficiency"] = tos(1); //tos( shield.efficiency ); - unit["Shield_Recharge"] = tos(shield->GetRegeneration()); //tos( shield.recharge ); - unit["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); + + reactor.SaveToCSV(unit); 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 + afterburner.SaveToCSV(unit); + 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)); @@ -1378,17 +1239,9 @@ 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); - 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); + + + 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_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_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 410f02f861..e7a5a9efe5 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" @@ -133,16 +132,16 @@ 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.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 +151,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; @@ -277,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(); } @@ -449,7 +449,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; } @@ -956,8 +957,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 @@ -983,10 +987,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->ftl_energy.Level() >= un->jump.energy) { + if (un->jump.drive > -2) { cp->jumpok = 1; } } @@ -1008,16 +1013,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->ftl_energy.Level() >= 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; } @@ -1036,15 +1041,16 @@ 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 + && (ftl_energy.Level() >= jump.energy || (ai_jump_cheat && cp == NULL)) )) || smalle->forcejump) { - warpenergy -= GetJumpStatus().energy; + // TODO: check we're not doing this multiple times + jump_drive.Consume(); DeactivateJumpDrive(); Unit *jumppoint = smalle; _Universe->activeStarSystem()->JumpTo(this, jumppoint, - smalle->GetDestinations()[GetJumpStatus().drive + smalle->GetDestinations()[jump.drive % smalle->GetDestinations().size()]); return true; @@ -1151,41 +1157,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; @@ -1264,13 +1243,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; @@ -1293,19 +1274,13 @@ 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; } // TODO: lib_damage reenable - shield->ReduceLayerCapability(dam, 0.1); + shield->Damage(); damages |= Damages::SHIELD_DAMAGED; return; @@ -1331,14 +1306,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--; } @@ -1624,7 +1599,7 @@ void Unit::Target(Unit *targ) { } computer.target.SetUnit(targ); - LockTarget(false); + radar.Unlock(); } } else { if (jump.drive != -1) { @@ -1661,25 +1636,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(); } } @@ -2061,7 +2023,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")); @@ -2072,8 +2035,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); @@ -2082,7 +2049,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) { @@ -2867,6 +2834,14 @@ 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) { + percentage = result.percent; + return result.success; + } + + // Old Code percentage = 0; static bool @@ -3009,7 +2984,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); @@ -3024,7 +2999,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, @@ -3032,7 +3007,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 @@ -3040,7 +3015,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")) @@ -3051,12 +3026,12 @@ bool Unit::UpAndDownGrade(const Unit *up, if (upgradedrecharge) { shield->UpdateRegeneration(shield_regeneration); - } + }*/ // 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; } @@ -3066,15 +3041,15 @@ 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())) { + /*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")) - 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 @@ -3104,9 +3079,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 @@ -3138,9 +3113,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); @@ -3201,7 +3176,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++) { @@ -3223,7 +3198,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) { @@ -3252,19 +3227,19 @@ 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 || 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, @@ -3277,8 +3252,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, @@ -3289,46 +3264,46 @@ 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; } } } cancompletefully = ccf; - } + }*/ //NO CLUE FOR BELOW if (downgrade) { if (jump.drive >= -1 && up->jump.drive >= -1) { @@ -3344,20 +3319,23 @@ 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) //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; @@ -3370,7 +3348,7 @@ bool Unit::UpAndDownGrade(const Unit *up, ((char *) &this->afterburnenergy) - ((char *) this), tempdownmap); } - } + }*/ } else { //we are upgrading! if (touchme) { @@ -3380,22 +3358,23 @@ 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) //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; @@ -3405,7 +3384,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; @@ -3614,12 +3593,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; } - } + }*/ } } } @@ -4259,95 +4238,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; @@ -4360,6 +4251,10 @@ void Unit::ActTurn() { // Repair Ship Repair(); + // Power + reactor.Generate(); + + shield_.Regenerate(graphicOptions.InWarp, isPlayerShip()); } void Unit::UpdatePhysics2(const Transformation &trans, @@ -4429,16 +4324,25 @@ 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(); + + // 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); } - UpdateCloak(); // 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; @@ -4446,17 +4350,16 @@ 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))) { @@ -4487,12 +4390,12 @@ 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(); TargetPos.Normalize(); - if (TargetPos.k > computer.radar.lockcone) { + if (TargetPos.k > radar.lock_cone) { increase_locking = true; } } @@ -4530,7 +4433,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; @@ -4594,23 +4497,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, @@ -4630,11 +4536,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; @@ -4648,10 +4554,10 @@ 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); + // 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; @@ -4729,55 +4635,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..82e29e1123 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 @@ -77,6 +79,18 @@ void UncheckUnit( class Unit*un ); #include "role_bitmask.h" #include "upgradeable_unit.h" +// Components +#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/radar.h" + + #include "configuration/configuration.h" #include "configuration/game_config.h" @@ -134,13 +148,33 @@ 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: //How many lists are referencing us int ucref = 0; StringPool::Reference csvRow; + public: + Computer computer; + // Components + 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(&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... @@ -475,7 +509,7 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p StarSystem *&previouslyActiveStarSystem, bool DoSightAndSound) override; - Computer computer; + void SwitchCombatFlightMode(); bool CombatMode(); @@ -584,20 +618,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 +633,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 - float CloakVisible() const { - if (cloaking < 0) { - return 1; - } - return ((float) cloaking) / 2147483647; +// how visible the ship is from 0 to 1 +// Need this function to expose it to python + float CloakVisible() { + return cloak.Visibility(); } //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); @@ -995,6 +1013,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 fuel.Level(); } + double energyData() { return energy.Level(); } }; Unit *findUnitInStarsystem(const void *unitDoNotDereference); diff --git a/engine/src/cmd/upgradeable_unit.cpp b/engine/src/cmd/upgradeable_unit.cpp index 891b485cab..22001dbc66 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,97 @@ 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.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->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; + } + + 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..f226a07770 100644 --- a/engine/src/cmd/upgradeable_unit.h +++ b/engine/src/cmd/upgradeable_unit.h @@ -32,6 +32,41 @@ 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, + Reactor, + + Afterburner, + Drive, + 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 +74,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/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..ecfa14fc66 100644 --- a/engine/src/cmd/weapon_info.cpp +++ b/engine/src/cmd/weapon_info.cpp @@ -37,14 +37,14 @@ namespace alg = boost::algorithm; Hashtable lookuptable; -WeaponInfo::WeaponInfo() { +WeaponInfo::WeaponInfo(): EnergyConsumer(nullptr, false) { } -WeaponInfo::WeaponInfo(WEAPON_TYPE type) { +WeaponInfo::WeaponInfo(WEAPON_TYPE type) : EnergyConsumer(nullptr, false) { this->type = type; } -WeaponInfo::WeaponInfo(const WeaponInfo &tmp) { +WeaponInfo::WeaponInfo(const WeaponInfo &tmp) : EnergyConsumer(nullptr, false) { *this = tmp; } @@ -97,7 +97,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..e58a6f5441 --- /dev/null +++ b/engine/src/components/afterburner.cpp @@ -0,0 +1,105 @@ +/* + * 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(EnergyContainer *source, double usage_factor): + Component("", 0.0, 0.0, false), + EnergyConsumer(source, false), + usage_factor(usage_factor) {} + + +// 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(); +} + +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 new file mode 100644 index 0000000000..8d24c82345 --- /dev/null +++ b/engine/src/components/afterburner.h @@ -0,0 +1,89 @@ +/* + * 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 "component.h" +#include "energy_container.h" +#include "energy_consumer.h" + +// TODO: Take ship size, mass, cost, something into account. +// TODO: figure out the whole type thing. + +/* @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 +{ + 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(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; } +}; + +#endif // AFTERBURNER_H \ No newline at end of file diff --git a/engine/src/components/armor.cpp b/engine/src/components/armor.cpp new file mode 100644 index 0000000000..0f26f818ff --- /dev/null +++ b/engine/src/components/armor.cpp @@ -0,0 +1,170 @@ +/* + * 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" + +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(): Component("", 0.0, 0.0, false), + DamageableLayer(1, FacetConfiguration::eight, + Health(0,0), false) {} + +void Armor::Load(std::string upgrade_key, std::string unit_key) { + // Component + Component::Load(upgrade_key, unit_key); + + + // Damageable Layer + std::string upgrade_type_string = UnitCSVFactory::GetVariable(upgrade_key, "Name", std::string()); + + 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); + } + + 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); + 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(facets[i].health.Value()); + } +} + +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++) { + facets[i].health.SetMaxValue(0.0); + } + + 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; + } + + std::vector armor_values; + + for(int i = 0;i < 8;i++) { + double armor_value = UnitCSVFactory::GetVariable(upgrade_name, + armor_facets[i], 0.0); + armor_values.push_back(armor_value); + } + + UpdateFacets(armor_values); + + 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(Health& facet : facets) { + facet.health.SetToMax(); + } +} + +bool Armor::Damaged() const { + for(const Health& facet : facets) { + if(facet.health.Damaged()) { + return true; + } + } + + return false; +} + + +bool Armor::Installed() const { + return TotalMaxLayerValue() > 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..6021bf64d7 --- /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 +#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. +// With tungsten, you multiply each value by 1. +// With better armor material, you multiply by more. + +class Armor : public Component, public DamageableLayer { + friend class Unit; +public: + Armor(); + + // 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; + + 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/cloak.cpp b/engine/src/components/cloak.cpp new file mode 100644 index 0000000000..7a917003da --- /dev/null +++ b/engine/src/components/cloak.cpp @@ -0,0 +1,236 @@ +/* + * 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 "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(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; + rate = 100; + glass = false; + current = 0; + minimum = 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 { + 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::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); + unit["Cloak_Energy"] = std::to_string(energy); + 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 + if(status == CloakingStatus::disabled || + status == CloakingStatus::damaged || + status == CloakingStatus::ready) { + return; + } + + double power = Consume(); + + // Insufficient energy to cloak ship + if(power < 1.0) { + std::cerr << "No power to cloak\n"; + status = CloakingStatus::decloaking; + } + + 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/components/cloak.h b/engine/src/components/cloak.h new file mode 100644 index 0000000000..47c2b7bde5 --- /dev/null +++ b/engine/src/components/cloak.h @@ -0,0 +1,154 @@ +/* + * 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 + +#include "component.h" +#include "energy_consumer.h" +#include "energy_container.h" +#include "damageable_layer.h" + +enum class CloakingStatus { + disabled, + damaged, + ready, + cloaking, + cloaked, + decloaking +}; + +class Cloak : public Component, public EnergyConsumer +{ + 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(EnergyContainer *source); + + // 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 + + bool Capable() const { + return (status != CloakingStatus::disabled); + } + + bool Cloaking() { + return (status == CloakingStatus::cloaking); + } + + bool Cloaked() const { + return (status == CloakingStatus::cloaked); + } + + // Active is cloaking, cloaked or decloaking + bool Active() { + return (status == CloakingStatus::cloaking || + status == CloakingStatus::cloaked || + status == CloakingStatus::decloaking); + } + + bool Ready() { + return (status == CloakingStatus::ready); + } + + bool Glass() { + return glass; + } + + 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 Visibility() const { + return 1-current; + } + + 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/components/component.cpp b/engine/src/components/component.cpp new file mode 100644 index 0000000000..3645e983c4 --- /dev/null +++ b/engine/src/components/component.cpp @@ -0,0 +1,75 @@ +/* + * 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(std::string upgrade_name, double mass, double volume, + bool integral): + upgrade_name(upgrade_name), + mass(mass), volume(volume), + integral(integral) {} + + +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; + + 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..58916fae5f --- /dev/null +++ b/engine/src/components/component.h @@ -0,0 +1,91 @@ +/* + * 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 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(std::string upgrade_name, 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/drive.cpp b/engine/src/components/drive.cpp new file mode 100644 index 0000000000..2000bb4c00 --- /dev/null +++ b/engine/src/components/drive.cpp @@ -0,0 +1,82 @@ +/* + * 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" + +// 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(EnergyContainer *source, + double usage_factor): + Component("", 0.0, 0.0, false), + EnergyConsumer(source, false), + usage_factor(usage_factor) { + 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 new file mode 100644 index 0000000000..f0b7eb1455 --- /dev/null +++ b/engine/src/components/drive.h @@ -0,0 +1,62 @@ +/* + * 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" +#include "energy_consumer.h" + + +class Drive : public Component, + public EnergyConsumer { + double usage_factor; +public: + 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 new file mode 100644 index 0000000000..e59ba11e54 --- /dev/null +++ b/engine/src/components/energy_consumer.cpp @@ -0,0 +1,64 @@ +/* + * 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::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..3c60c3686a --- /dev/null +++ b/engine/src/components/energy_consumer.h @@ -0,0 +1,50 @@ +/* + * 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 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..eadefa2bc7 --- /dev/null +++ b/engine/src/components/energy_container.cpp @@ -0,0 +1,243 @@ +/* + * 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::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; + } + + level -= consumer.consumption; + 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; +}*/ + +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..14864ac903 --- /dev/null +++ b/engine/src/components/energy_container.h @@ -0,0 +1,102 @@ +/* + * 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(); + + /*void Use(EnergyConsumerClassification classification); + + bool InUse(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/hull.cpp b/engine/src/components/hull.cpp new file mode 100644 index 0000000000..4822974d39 --- /dev/null +++ b/engine/src/components/hull.cpp @@ -0,0 +1,96 @@ +/* + * 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" + +const std::string 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) { + // 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); + facets[0].health.SetMaxValue(hull_max); + facets[0].health.Set(hull_current); +} + +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 { + 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() { + facets[0].health.SetToMax(); +} + +bool Hull::Damaged() const { + return facets[0].health.Damaged(); +} + + +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..de4fd152af --- /dev/null +++ b/engine/src/components/hull.h @@ -0,0 +1,66 @@ +/* + * 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, public DamageableLayer { + friend class Unit; +public: + Hull(); + + 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_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 new file mode 100644 index 0000000000..9f287f6f01 --- /dev/null +++ b/engine/src/components/jump_drive.cpp @@ -0,0 +1,84 @@ +/* + * 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(EnergyContainer *source): + Component("", 0.0, 0.0, true), + EnergyConsumer(source, false), + delay(0.0) {} + +bool JumpDrive::Ready() { + return true;//installed && enabled; +} + +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 new file mode 100644 index 0000000000..1ebb73ca69 --- /dev/null +++ b/engine/src/components/jump_drive.h @@ -0,0 +1,64 @@ +/* + * 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_container.h" +#include "energy_consumer.h" + +class JumpDrive : public Component, public EnergyConsumer { + int destination; + double delay; + +public: + 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 new file mode 100644 index 0000000000..d20fea62e7 --- /dev/null +++ b/engine/src/components/radar.cpp @@ -0,0 +1,253 @@ +/* + * 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(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), + tracking_cone(0), + min_target_size(0), + type(RadarType::SPHERE), + capabilities(RadarCapabilities::NONE), + locked(false), + can_lock(false), + tracking_active(true), + damaged_(false), + original(nullptr), + computer(computer) +{ + + max_range = configuration()->computer_config.default_max_range; + tracking_cone = configuration()->computer_config.default_tracking_cone; + lock_cone = configuration()->computer_config.default_lock_cone; +} + + + +// 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 + // 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.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.0) * VS_PI / 180); + original = nullptr; + this->computer = computer; +} + +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.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() +{ + 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); + 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; + }*/ + damaged_ = true; +} + +void CRadar::Repair() +{ + damaged_ = false; +} + +bool CRadar::Damaged() const { + return damaged_; +} + +bool CRadar::Installed() const { + +} \ No newline at end of file diff --git a/engine/src/components/radar.h b/engine/src/components/radar.h new file mode 100644 index 0000000000..1942a36984 --- /dev/null +++ b/engine/src/components/radar.h @@ -0,0 +1,140 @@ +/* + * 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 "component.h" +#include "energy_consumer.h" +#include "computer.h" +#include "resource/resource.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 : public Component, public EnergyConsumer +{ + // TODO: move floats to doubles + //the max range the radar can handle + Resource max_range; + + //the dot with (0,0,1) indicating the farthest to the side the radar can handle. + Resource max_cone; + Resource lock_cone; + Resource tracking_cone; + + //The minimum radius of the target + Resource 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; + bool damaged_; + + std::unique_ptr original; + + Computer *computer; + + + + friend class Armed; + friend class Unit; +public: + CRadar(EnergyContainer *source, Computer* computer); + + void WriteUnitString(std::map &unit); + + 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.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 new file mode 100644 index 0000000000..e7524423da --- /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/shield.cpp b/engine/src/components/shield.cpp new file mode 100644 index 0000000000..d3ca331bb8 --- /dev/null +++ b/engine/src/components/shield.cpp @@ -0,0 +1,389 @@ +/* + * 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 "configuration/game_config.h" + +#include + +extern float simulation_atom_var; + +const std::string SHIELD_RECHARGE = "Shield_Recharge"; + +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(EnergyContainer *source): + Component("", 0.0, 0.0, false), + DamageableLayer(2, FacetConfiguration::zero, + Health(2, 0), false), + EnergyConsumer(source, true), + regeneration(0,0,0), + power(1.0,0.0,1.0) {} + + + +void Shield::Load(std::string upgrade_key, std::string unit_key, + 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) * difficulty; + + // 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]) * difficulty); + + // 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 + */ + + UpdateFacets(shield_values); + this->regeneration.SetMaxValue(regeneration); + + // TODO: shield leakage & efficiency + + // Power draw for maintenance and regeneration + // TODO: implement fully + consumption = TotalMaxLayerValue()/10 + regeneration; +} + + +void Shield::SaveToCSV(std::map& unit) const { + // TODO: lib_damage figure out if this is correctly assigned + 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()); + + 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(facets[i].health.MaxValue()); + } + + break; + case 4: + for(int i=0;i<4;i++) { + unit[shield_facets_four[i]] = std::to_string(facets[i].health.MaxValue()); + } + + break; + case 2: + 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: + // No shields + break; + + default: + // This should not happen + 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::CanDowngrade() const { + return !Damaged(); +} + +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; + UpdateFacets(empty_vector); + + return true; +} + +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()); + + // Regeneration + 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)); + } + } 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)); + } + } else { + return false; + } + + UpdateFacets(shield_values); + + // TODO: shield leakage + + return true; +} + + + +void Shield::Damage() { + for(Health& facet : 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. + // TODO: opacity.RandomDamage(); +} + +void Shield::Repair() { + for(Health& facet : facets) { + facet.health.RepairFully(); + } + + regeneration.RepairFully(); + // TODO: opacity.RepairFully(); +} + +bool Shield::Damaged() const { + for(const Health& facet : facets) { + if(facet.health.Damaged()) { + return true; + } + } + + return regeneration.Damaged(); +} + +bool Shield::Installed() const { + return regeneration.MaxValue() > 0; +} + + +void Shield::AdjustPower(const double &percent) { + power.Set(percent); +} + + +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 (Health &facet : facets) { + facet.health.Set(0.0); + } +} + +void Shield::Enable() { + power.Set(1.0); +} + +bool Shield::Enabled() const { + return power.Value() > 0.0; +} + + + +/** 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; + + for(Health& facet : facets) { + facet.health.SetMaxValue(facet.health.MaxValue() * enhancement_factor); + } + + regeneration.SetMaxValue(regeneration.MaxValue() * enhancement_factor); +} + + +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. +* 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::SetPowerCap(const double power) { + this->power.SetAdjustedMaxValue(power); + // We need this as well, otherwise power will still be 0. + this->power.Set(power); +} + + + +double Shield::GetRegeneration() const { + return regeneration.Value(); +} + +// 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 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) { + // "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 += 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 -= actual_recharge; + } + } +} \ No newline at end of file diff --git a/engine/src/components/shield.h b/engine/src/components/shield.h new file mode 100644 index 0000000000..8c50032cd2 --- /dev/null +++ b/engine/src/components/shield.h @@ -0,0 +1,114 @@ +/* + * 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" +#include "energy_consumer.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, 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(EnergyContainer *source); + + virtual void Load(std::string upgrade_key, std::string unit_key, + Unit *unit, double difficulty); // 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 CanUpgrade(const std::string upgrade_key) const; + virtual bool Downgrade(); + virtual bool Upgrade(const std::string upgrade_key); + + virtual void Damage(); + virtual void Repair(); + + 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; + void Regenerate(bool ftl, bool player_ship); + + double GetPower() const; + double GetPowerCap() const; + void SetPower(const double power); + void SetPowerCap(const double power); +}; + + +#endif // VEGA_STRIKE_ENGINE_COMPONENTS_SHIELD_H diff --git a/engine/src/components/tests/armor_tests.cpp b/engine/src/components/tests/armor_tests.cpp new file mode 100644 index 0000000000..1843f85eae --- /dev/null +++ b/engine/src/components/tests/armor_tests.cpp @@ -0,0 +1,117 @@ +#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 + +Armor createArmor(double health) { + Armor armor = Armor(); + + std::vector facets; + for(int i=0;i<8;i++) { + facets.push_back(health); + } + armor.UpdateFacets(facets); + + return armor; +} + +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(Armor &armor, double health, double max_health) { + for(int i=0;i<8;i++) { + EXPECT_EQ(armor.facets[i].health.Value(), health); + EXPECT_EQ(armor.facets[i].health.MaxValue(), max_health); + } +} + + +// Tests +TEST(Armor, Load) { + Armor armor = createArmor(1); + + loadArmor(armor, 25, 50); + + testArmorValues(armor, 25, 50); +} + +TEST(Armor, Save) { + Armor armor = createArmor(100); + + 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) { + Armor armor = createArmor(1); + loadArmor(armor, 25, 50); + + armor.SetIntegral(false); + EXPECT_TRUE(armor.CanDowngrade()); + + armor.SetIntegral(true); + EXPECT_FALSE(armor.CanDowngrade()); +} + +TEST(Armor, Downgrade) { + Armor armor = createArmor(1); + loadArmor(armor, 25, 50); + + EXPECT_TRUE(armor.Downgrade()); + + testArmorValues(armor, 0, 0); +} + +TEST(Armor, CanUpgrade) { + Armor armor = createArmor(1); + loadArmor(armor, 25, 50); + + EXPECT_TRUE(armor.Downgrade()); + EXPECT_TRUE(armor.CanUpgrade(DUMMY_ARMOR_KEY)); +} + +TEST(Armor, Upgrade) { + Armor armor = createArmor(1); + loadArmor(armor, 25, 50); + + EXPECT_TRUE(armor.Downgrade()); + EXPECT_TRUE(armor.Upgrade(DUMMY_ARMOR_KEY)); + testArmorValues(armor, 50, 50); +} + 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/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/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..1a7698f17b 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. @@ -51,15 +52,14 @@ DamageableLayer::DamageableLayer(int layer_index, DamageableLayer::DamageableLayer(int layer_index, FacetConfiguration configuration, - float health_array[], - float regeneration, + double health_array[], 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); } @@ -83,13 +83,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) { @@ -106,49 +100,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) { - facet.ReduceLayerMaximumByOne(); - } -} - -void DamageableLayer::Enable() { - for (Health &facet : facets) { - facet.Enable(); - } -} - -bool DamageableLayer::Enabled() { - if (number_of_facets == 0) { - return false; - } - - return facets[0].enabled; -} - -// 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) { @@ -156,9 +107,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; @@ -169,8 +120,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; } @@ -220,117 +171,64 @@ 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; +double DamageableLayer::TotalLayerValue() const { + double total_value = 0.0f; for (const Health &facet : facets) { - total_value += facet.health; + total_value += facet.health.Value(); } return total_value; } -float DamageableLayer::TotalMaxLayerValue() { - float total_value = 0.0f; +double DamageableLayer::TotalMaxLayerValue() const { + double total_value = 0.0f; for (const Health &facet : facets) { - total_value += facet.max_health; + total_value += facet.health.MaxValue(); } return total_value; } -float DamageableLayer::AverageLayerValue() { - float total_value = 0.0f; +double DamageableLayer::AverageLayerValue() const { + double total_value = 0.0f; for (const Health &facet : facets) { - total_value += facet.health; + total_value += facet.health.Value(); } return total_value / facets.size(); } -float DamageableLayer::AverageMaxLayerValue() { - float total_value = 0.0f; +double DamageableLayer::AverageMaxLayerValue() const { + double total_value = 0.0f; for (const Health &facet : facets) { - total_value += facet.max_health; + 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() const { if (number_of_facets == 0) { return 0.0f; } - return facets[0].max_health; + return facets[0].health.MaxValue(); } -float DamageableLayer::GetPercent(FacetName facet_name) { +double DamageableLayer::GetPercent(FacetName facet_name) const { 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) { case FacetConfiguration::one: case FacetConfiguration::two: case FacetConfiguration::four: - numerator = facets[as_integer(facet_name)].health; - denominator = facets[as_integer(facet_name)].max_health; - return CalculatePercentage(numerator, denominator); + numerator = facets[as_integer(facet_name)].health.Value(); + denominator = facets[as_integer(facet_name)].health.MaxValue(); + return numerator / denominator; default: break; // Noop @@ -339,7 +237,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 @@ -367,68 +265,66 @@ 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]; - Health &facet = facets[facet_index]; - aggregate_health += facet.health; - aggregate_max_health += facet.max_health; + const Health &facet = facets[facet_index]; + aggregate_health += facet.health.Value(); + aggregate_max_health += facet.health.MaxValue(); } - 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; -} -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(new_size != number_of_facets) { + number_of_facets = new_size; + facets.clear(); + Health health = Health(layer_index, 0); + for(int i=0;i new_facets); }; #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..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(0, 0, 5); - - 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/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 b555ca4c2d..93cd638e99 100644 --- a/engine/src/damage/health.cpp +++ b/engine/src/damage/health.cpp @@ -29,22 +29,15 @@ #include #include -void Health::AdjustPower(const float &percent) { - if (!regenerative) { - // Not applicable for armor and hull - return; - } - - if (percent > 1 || percent < 0) { - // valid values are between 0 and 1 - return; - } - adjusted_health = max_health * percent; - if (adjusted_health < health) { - health = adjusted_health; - } -} +Health::Health(int layer, double health) : + 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 @@ -67,11 +60,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, double vulnerability, InflictedDamage &inflicted_damage) { // Here we adjust for specialized weapons such as shield bypassing and shield leeching // which only damage the shield. // We also cap the actual damage at the current health - const 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; @@ -91,81 +84,18 @@ void Health::DealDamageComponent(int type, float &damage, float vulnerability, I 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; } } -void Health::Disable() { - if (regenerative && enabled) { - enabled = false; - health = 0.0f; - } -} - void Health::Destroy() { health = 0; destroyed = true; } -void Health::Enable() { - if (regenerative && !enabled) { - enabled = true; - } -} - -void Health::Enhance(float percent) { - // Don't enhance armor and hull - if (!regenerative) { - return; - } - - health = max_health * percent; -} - -void Health::ReduceLayerMaximum(const float &percent) { - adjusted_health = std::max(0.0f, max_health * (1 - percent)); - health = std::min(health, max_health); -} - -void Health::ReduceLayerMaximumByOne() { - adjusted_health = std::max(0.0f, adjusted_health - 1); - health = std::min(health, adjusted_health); -} - -void Health::ReduceLayerMaximumByOnePercent() { - float percent = adjusted_health / max_health - 0.01f; - max_health = std::max(0.0f, max_health * percent); -} - -void Health::ReduceRegeneration(const float &percent) { - regeneration = std::max(0.0f, regeneration - max_regeneration * percent); -} -void Health::Regenerate() { - if (!enabled || destroyed || !regenerative) { - return; - } - - health = std::min(adjusted_health, health + regeneration); -} - -void Health::Regenerate(float recharge_rate) { - if (!enabled || destroyed || !regenerative) { - return; - } - - health = std::min(adjusted_health, health + recharge_rate); -} - -void Health::SetHealth(float health) { - health = std::min(max_health, health); - health = std::max(0.0f, health); - this->health = health; -} - -void Health::Update(float health) { - this->health = health; - max_health = health; - adjusted_health = health; -} +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 8d06d16506..34c90ad685 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,23 +42,13 @@ * 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 - bool regenerative; + + Resource health; bool destroyed; - bool enabled; Damage vulnerabilities; - // TODO: implement "shield leaks" - - public: /** @@ -70,47 +62,23 @@ 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) : - layer(layer), - max_health(max_health), - adjusted_health(max_health), - health(health), - max_regeneration(regeneration), - regeneration(regeneration), - regenerative(regeneration > 0) { - destroyed = false; - if (layer == 0) { - regenerative = false; - } - enabled = regenerative; // Only relevant for regenerative objects (e.g. shields). - vulnerabilities.normal_damage = 1; - vulnerabilities.phase_damage = 1; - }; + Health(int layer, double health = 1); - float Percent() const { - return max_health != 0 ? health / max_health : 0.0f; + double Percent() const { + return health.Percent(); } - void AdjustPower(const float &percent); - void AdjustPercentage(); + void DealDamage(Damage &damage, InflictedDamage &inflicted_damage); - void DealDamageComponent(int type, float &damage, float vulnerability, InflictedDamage &inflicted_damage); - void Disable(); void Destroy(); - void Enable(); - void Enhance(float percent = 1.5f); - void ReduceLayerMaximum(const float &percent); - void ReduceLayerMaximumByOne(); - void ReduceLayerMaximumByOnePercent(); - void ReduceRegeneration(const float &percent); - void Regenerate(); - void Regenerate(float recharge_rate); - void SetHealth(float health); - void Update(float health); + 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/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..7b5802bbd6 100644 --- a/engine/src/damage/tests/layer_tests.cpp +++ b/engine/src/damage/tests/layer_tests.cpp @@ -24,13 +24,14 @@ #include +#include #include "damageable_layer.h" #include "core_vector.h" // 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); @@ -52,3 +53,38 @@ TEST(Layer, Sanity) { EXPECT_EQ(layer.GetFacetIndex(CoreVector(-2, 0, -1)), 1); } + +TEST(Layer, Sanity_2) { + Health health(0, 10); + 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(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(layers[0].facets[0].health.Value(), 10); + + std::vector new_health = {50}; + ptr->UpdateFacets(new_health); + + 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, &layers[0]); +} + +// 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 a8205c6ccd..567342ac1e 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; @@ -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->fuel.Percent(); case UnitImages::ENERGY: - return target->energyData(); + return target->energy.Percent(); 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->ftl_energy.Percent() : 0; } case UnitImages::HULL: if (maxhull < target->GetHull()) { @@ -681,11 +682,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; @@ -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->ftl_energy.Level() < target->jump.energy) { return (float) UnitImages::NOTENOUGHENERGY; } else if (target->graphicOptions.InWarp) { //FIXME return (float) UnitImages::OFF; @@ -2702,26 +2703,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/cockpit_generic.cpp b/engine/src/gfx/cockpit_generic.cpp index b2ed5a66e0..86c9a56b7e 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) { @@ -546,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->SetPowerCap(minEnergyShieldPercent); } } else { secondsWithZeroEnergy = 0; 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/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..c5a8d7e633 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 = cloak.Visibility(); + c.CloakFX.g = cloak.Visibility(); + c.CloakFX.b = cloak.Visibility(); + 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/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/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/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: 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, 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/cout_util.h b/engine/src/resource/cout_util.h new file mode 100644 index 0000000000..eb5142e094 --- /dev/null +++ b/engine/src/resource/cout_util.h @@ -0,0 +1,57 @@ +/* + * 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; + } +} + +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; +} + +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 diff --git a/engine/src/resource/resource.cpp b/engine/src/resource/resource.cpp index 1561309634..9f0a7bcb89 100644 --- a/engine/src/resource/resource.cpp +++ b/engine/src/resource/resource.cpp @@ -33,33 +33,21 @@ 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; +double Resource::Percent() const { + if(no_max_) { // Can't calculate percent if there's no max + return -1; } - adjusted_max_value_ = std::max(min_value_, adjusted_max_value_ - (max_value_ * value)); -} - -template -T Resource::Percent() const { - if(no_max_) { // Can't calculate percent if there's no max + if(max_value_ == 0) { // Can't calculate percent if divider is 0 return -1; } @@ -85,30 +73,37 @@ void Resource::Set(const T &value) { } template -void Resource::SetMaxValue(const T &value) { - if(no_max_) { // Can't set max if there's no max +void Resource::SetToMax() { + if(no_max_) { // Can't set to max if there's no max return; } - adjusted_max_value_ = max_value_ = value; + value_ = adjusted_max_value_ = max_value_; } template -void Resource::Upgrade(const T &value) { - if(no_max_) { // Can't upgrade max if there's no max +void Resource::SetMaxValue(const T &value) { + if(no_max_) { // Can't set max if there's no max return; } - adjusted_max_value_ = std::min(max_value_, adjusted_max_value_ + value); + value_ = adjusted_max_value_ = max_value_ = value; } template -void Resource::UpgradeByPercent(const T &value) { - if(no_max_) { // Can't upgrade max if there's no max +void Resource::SetAdjustedMaxValue(const T &value) { + T v = value; + + if(no_max_) { // Can't set max if there's no max return; } - adjusted_max_value_ = std::min(max_value_, adjusted_max_value_ + (max_value_ * 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 @@ -136,14 +131,99 @@ 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 */ +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 - value_ = std::min(value_ + value, max_value_); + value_ = std::min(value_ + value, adjusted_max_value_); } else { value_ += value; } @@ -161,7 +241,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 4ef41759d1..5f812a5250 100644 --- a/engine/src/resource/resource.h +++ b/engine/src/resource/resource.h @@ -31,14 +31,17 @@ template class Resource { +protected: T value_; T min_value_; T max_value_; 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); Resource operator-=(const T &value); @@ -81,21 +84,36 @@ 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 SetAdjustedMaxValue(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); + + T* ValuePtr() { return &value_; } + T* AdjustedMaxValuePtr() { return &adjusted_max_value_; } + T* MaxValuePtr() { return &max_value_; } }; template diff --git a/engine/src/resource/tests/resource_test.cpp b/engine/src/resource/tests/resource_test.cpp index ac9bdd4801..ee6136c840 100644 --- a/engine/src/resource/tests/resource_test.cpp +++ b/engine/src/resource/tests/resource_test.cpp @@ -2,19 +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.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)); } diff --git a/engine/src/star_system.cpp b/engine/src/star_system.cpp index 99c68e37c9..25348d38b3 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->Discharge(); } } } @@ -1313,7 +1312,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 +1419,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); 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();