diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index eb4fc262d..0d2b1f350 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -166,6 +166,7 @@ IF (UNIX) ${Vega_Strike_SOURCE_DIR}/src/damage ${Vega_Strike_SOURCE_DIR}/src/resource ${Vega_Strike_SOURCE_DIR}/src/components + ${Vega_Strike_SOURCE_DIR}/src/python/base_computer ${Vega_Strike_SOURCE_DIR}/src/python/config ${Vega_Strike_BINARY_DIR} ${Vega_Strike_BINARY_DIR}/src @@ -178,6 +179,7 @@ ELSE () ${Vega_Strike_SOURCE_DIR}/src/damage ${Vega_Strike_SOURCE_DIR}/src/resource ${Vega_Strike_SOURCE_DIR}/src/components + ${Vega_Strike_SOURCE_DIR}/src/python/base_computer ${Vega_Strike_SOURCE_DIR}/src/python/config ${Vega_Strike_BINARY_DIR} ${Vega_Strike_BINARY_DIR}/src @@ -770,6 +772,8 @@ SET(LIBPYTHON_SOURCES src/python/unit_method_defs.cpp src/python/unit_wrapper.cpp src/python/universe_util_export.cpp + + src/python/base_computer/ship_view.cpp ) SET(LIBSCRIPT_SOURCES @@ -1749,6 +1753,7 @@ IF (USE_GTEST) src/exit_unit_tests.cpp src/components/tests/energy_container_tests.cpp src/components/tests/balancing_tests.cpp + src/components/tests/jump_drive_tests.cpp ) ADD_LIBRARY(vegastrike-testing diff --git a/engine/src/cmd/basecomputer.cpp b/engine/src/cmd/basecomputer.cpp index 57974c631..a292b3803 100644 --- a/engine/src/cmd/basecomputer.cpp +++ b/engine/src/cmd/basecomputer.cpp @@ -62,6 +62,8 @@ using VSFileSystem::SaveFile; #include "facet_configuration.h" #include "vs_logging.h" #include "controls_factory.h" +#include "python/base_computer/ship_view.h" + //for directory thing @@ -4701,53 +4703,8 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C text += "#n##c0:1:.5#[STATS]#n##-c"; } if (!mode) { - const std::string &name = playerUnit->name; - for (nameindex = 0; (nameindex < name.size()) && name[nameindex] != '.'; nameindex++) { - } - nametemp = playerUnit->getFullname(); - if (nametemp.length()) { - nametemp[0] = toupper(nametemp[0]); - } - for (nameindex = nameindex + 1; nameindex < name.size(); nameindex++) { - model += name[nameindex]; - } - if (model == "blank") { - model = "TEMPLATE--WARNING--BUG"; - } else if (model == "") { - model = "Military Issue (equipped)"; - } else if (model == "rg") { - model = "Regional Guard Issue (equipped)"; - } else if (model == "milspec") { - model = "Military Spec."; - } else if (model == "rgspec") { - model = "Regional Guard Spec."; - } else if (model == "stock") { - model = "Stock"; - } else if (model == "begin") { - model = "Stock(Refurbished)"; - } else { - model = "Military Spec. Variant (" + model + ")"; - } - Cargo *fullname = GetMasterPartList(playerUnit->name.get().c_str()); - Cargo *milname = GetMasterPartList(nametemp.c_str()); - Cargo *blankname = GetMasterPartList((nametemp + ".blank").c_str()); - if (!subunitlevel && (fullname || milname || blankname)) { - text += "#c0:1:.5#" + prefix + "[NOTES]#n##n##-c"; - if (fullname) { - text += fullname->GetDescription(); - } else if (blankname) { - text += blankname->GetDescription(); - } else if (milname) { - text += milname->GetDescription(); - } - text += "#n#"; - } - text += "#n##c0:1:.5#" + prefix + "[GENERAL INFORMATION]#n##-c"; - - text += "#n#" + prefix + statcolor + "Class: #-c" + nametemp + statcolor + " Model: #-c" + model; - PRETTY_ADDU(statcolor + "Mass: #-c", playerUnit->getMass(), 0, "metric tons"); - //Irrelevant to player as is proportional to mass in our physics system. - //PRETTY_ADDU("Moment of inertia: ",playerUnit->GetMoment(),2,"tons.m�"); + std::map ship_map = playerUnit->UnitToMap(); + text += GetShipView(ship_map); } if (mode && replacement_mode == 2 && playerUnit->getMass() != blankUnit->getMass()) PRETTY_ADDU(statcolor + "Effective Mass reduced by: #-c", 100.0 * (1.0 - playerUnit->getMass()), 0, "%"); @@ -4760,9 +4717,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C bvol[0] = blankUnit->getEmptyCargoVolume(); bvol[1] = blankUnit->getEmptyUpgradeVolume(); for (int index = 0; index < 2; ++index) { - if (!mode) { - PRETTY_ADDU(statcolor + dvol[index] + " volume: #-c", vol[index], 0, "cubic meters"); - } else if (bvol[index] != vol[index]) { + if (mode && bvol[index] != vol[index]) { switch (replacement_mode) { case 0: //Replacement or new Module PRETTY_ADDU(statcolor + "Changes " + dvol[index] + " Volume to: #-c", @@ -4789,25 +4744,32 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } } - - - if (!mode) { - PRETTY_ADDU(statcolor + "Fuel capacity: #-c", playerUnit->fuelData(), 2, "metric tons of Lithium-6"); - } - + //following lines somewhat borken in terms of semantics for quantity of fuel + //and policy of upgrades to fuel + if (mode && blankUnit->fuelData() != playerUnit->fuelData()) { + switch (replacement_mode) { + case 0: //Replacement or new Module + break; + case 1: //Additive + PRETTY_ADDU(statcolor + "Adds #-c", + playerUnit->fuelData(), + 2, + "metric tons of Lithium-6 " /*+statcolor+"to Fuel Capacity #-c"*/ ); + break; + case 2: //multiplicative + break; + default: //Failure + text += "Oh dear, this wasn't an upgrade. Please debug code."; + break; + } + } const Computer &uc = playerUnit->ViewComputerData(); const Computer &buc = blankUnit->ViewComputerData(); - if (!mode) { - text += "#n##n#" + prefix + "#c0:1:.5#[FLIGHT CHARACTERISTICS]#n##-c"; - text += "#n#" + prefix + statcolor + "Turning response: #-c"; - } + if (playerUnit->limits.yaw == playerUnit->limits.pitch && playerUnit->limits.yaw == playerUnit->limits.roll) { prettyPrintFloat(conversionBuffer, playerUnit->limits.yaw / ((playerUnit->GetMoment() != 0) ? playerUnit->GetMoment() : 1), 0, 4); - if (!mode) { - text += conversionBuffer; - text += " radians/second^2#n#" + expstatcolor + " (yaw, pitch, roll)#-c"; - } else if (MODIFIES(replacement_mode, playerUnit, blankUnit, limits.yaw)) { + if (mode && MODIFIES(replacement_mode, playerUnit, blankUnit, limits.yaw)) { switch (replacement_mode) { case 0: //Replacement or new Module PRETTY_ADDU(statcolor + "#n#Installs maneuvering jets with turning response #-c", @@ -4829,15 +4791,9 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } } else { - if (!mode) { - float moment = (playerUnit->GetMoment() != 0) ? playerUnit->GetMoment() : 1; - PRETTY_ADDN(substatcolor + " yaw #-c", playerUnit->limits.yaw / (moment), 4); - PRETTY_ADDN(substatcolor + " pitch #-c", playerUnit->limits.pitch / (moment), 4); - PRETTY_ADDN(substatcolor + " roll #-c", playerUnit->limits.roll / (moment), 4); - text += " radians/second^2"; - } else if (MODIFIES(replacement_mode, playerUnit, blankUnit, limits.yaw) + if (mode && (MODIFIES(replacement_mode, playerUnit, blankUnit, limits.yaw) || MODIFIES(replacement_mode, playerUnit, blankUnit, limits.pitch) - || MODIFIES(replacement_mode, playerUnit, blankUnit, limits.roll)) { + || MODIFIES(replacement_mode, playerUnit, blankUnit, limits.roll))) { switch (replacement_mode) { case 0: //Replacement or new Module text += "#n#Replaces existing maneuvering system with one rated at: #-c#n#"; @@ -4867,27 +4823,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } if (!subunitlevel) { - if (!mode && (playerUnit->getMass() != 0)) { - PRETTY_ADDU(statcolor + "Fore acceleration: #-c", - playerUnit->limits.forward / (9.8 * playerUnit->getMass()), 2, "gravities"); - PRETTY_ADDU(statcolor + "Aft acceleration: #-c", - playerUnit->limits.retro / (9.8 * playerUnit->getMass()), 2, "gravities"); - if (playerUnit->limits.lateral == playerUnit->limits.vertical) { - PRETTY_ADDU(statcolor + "Orthogonal acceleration: #-c", - playerUnit->limits.vertical / (9.8 * playerUnit->getMass()), 2, "gravities"); - text += expstatcolor + "#n# (vertical and lateral axes)#-c"; - } else { - PRETTY_ADDN(statcolor + " Lateral acceleration #-c", - playerUnit->limits.lateral / (9.8 * playerUnit->getMass()), - 2); - PRETTY_ADDN(statcolor + " Vertical acceleration #-c", - playerUnit->limits.vertical / (9.8 * playerUnit->getMass()), 2); - text += " gravities"; - } - PRETTY_ADDU(statcolor + "Forward acceleration with overthrust: #-c", playerUnit->limits.afterburn - / (9.8 * playerUnit->getMass()), 2, "gravities"); - text.append("#n##n##c0:1:.5#" + prefix + "[GOVERNOR SETTINGS]#n##-c"); - } else { + if (mode) { switch (replacement_mode) { case 0: //Replacement or new Module if (MODIFIES(replacement_mode, playerUnit, blankUnit, limits.forward)) { @@ -4991,11 +4927,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } static float non_combat_mode_mult = XMLSupport::parse_float(vs_config->getVariable("physics", "combat_speed_boost", "100")); - if (!mode) { - PRETTY_ADDU(statcolor + "Max combat speed: #-c", uc.max_speed(), 0, "m/s"); - PRETTY_ADDU(statcolor + "Max overdrive combat speed: #-c", uc.max_ab_speed(), 0, "m/s"); - PRETTY_ADDU(statcolor + "Max non-combat speed: #-c", uc.max_speed() * non_combat_mode_mult, 0, "m/s"); - } else { + if (mode) { switch (replacement_mode) { case 0: //Replacement or new Module if (MODIFIES(replacement_mode, &uc, &buc, max_speed())) { @@ -5039,20 +4971,9 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } } - if (!mode) { - if (uc.max_yaw_right == uc.max_pitch_up && uc.max_yaw_right == uc.max_roll_right) { - PRETTY_ADD(statcolor + "Max turn rate: #-c", uc.max_yaw_right, 2); - text += " radians/second " + expstatcolor + "(yaw, pitch, roll)#-c"; - } else { - text += ("#n#" + prefix + statcolor + "Max turn rates:#-c"); - PRETTY_ADDU(substatcolor + " - yaw: #-c", uc.max_yaw_right, 2, "radians/second"); - PRETTY_ADDU(substatcolor + " - pitch: #-c", uc.max_pitch_up, 2, "radians/second"); - PRETTY_ADDU(substatcolor + " - roll: #-c", uc.max_roll_right, 2, "radians/second"); - } - text += "#n##n##c0:1:.5#" + prefix + "[TARGETTING SUBSYSTEM]#n##-c"; - } else if (MODIFIES(replacement_mode, &uc, &buc, max_yaw_right) + if (mode && (MODIFIES(replacement_mode, &uc, &buc, max_yaw_right) || MODIFIES(replacement_mode, &uc, &buc, max_pitch_up) - || MODIFIES(replacement_mode, &uc, &buc, max_roll_right)) { + || MODIFIES(replacement_mode, &uc, &buc, max_roll_right))) { switch (replacement_mode) { case 0: //Replacement or new Module text += ("#n#" + prefix + "Governor settings for maximum turn rates set to: "); @@ -5080,39 +5001,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C break; } } - 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"); - 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"); - 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"; - 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; - } - text.append("#n##n##c0:1:.5#" + prefix + "[ENERGY SUBSYSTEM]#n##-c"); - } else { + if (mode) { switch (replacement_mode) { case 0: //Replacement or new Module if (MODIFIES_ALTEMPTY(replacement_mode, &uc, &buc, radar.maxrange, FLT_MAX) @@ -5161,48 +5050,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C const JumpDrive &uj = playerUnit->jump_drive; const FtlDrive &ftl = playerUnit->ftl_drive; const JumpDrive &buj = blankUnit->jump_drive; - if (!mode) { - float maxshield = playerUnit->totalShieldEnergyCapacitance(); - if (shields_require_power) { - maxshield = 0; - } - 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"); - //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->ftl_energy.MaxLevel() * RSconverter * Wconv, - 0, - "MJ"); - - text += "#n##n##c0:1:.5#" + prefix + "[SPEC SUBSYSTEM]#n##-c"; - - PRETTY_ADDU(statcolor + "Active SPEC Energy Requirements: #-c", - ftl.GetConsumption() * RSconverter * Wconv, - 0, - "MJ/s"); - - text += "#n##n##c0:1:.5#" + prefix + "[JUMP SUBSYSTEM]#n##-c"; - if (!uj.Installed()) { - text += "#n##c1:.3:.3#No outsystem jump drive present#-c"; //fixed?? - } else { - PRETTY_ADDU(statcolor + "Energy cost for jumpnode travel: #-c", - uj.GetConsumption() * RSconverter * Wconv, - 0, - "MJ"); - if (uj.Delay() > 0) - PRETTY_ADDU(statcolor + "Delay: #-c", uj.Delay(), 0, "seconds"); - if (uj.Damaged()) - PRETTY_ADDU(statcolor + "Damage to outsystem jump drive: #-c", 1-uj.Percent(), 0, "%"); - if (playerUnit->ftl_energy.MaxLevel() < uj.GetAtomConsumption()) { - text += "#n##c1:.3:.3#" + prefix - + - "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c"; - } - } - } - } else { + if (mode) { switch (replacement_mode) { case 0: //Replacement or new Module if (MODIFIES(replacement_mode, playerUnit, blankUnit, reactor.Capacity())) @@ -5224,10 +5072,6 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C break; } } - if (!mode) { - text += "#n##n##c0:1:.5#" + prefix + "[DURABILITY STATISTICS]#n##-c"; - text += "#n#" + prefix + statcolor + "Armor damage resistance:#-c"; - } if (mode && MODIFIES(replacement_mode, playerUnit, blankUnit, armor->facets[as_integer(FacetName::left_top_front)].health)) { @@ -5248,7 +5092,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } // Add Armor stats - if (!mode || MODIFIES(replacement_mode, playerUnit, blankUnit, armor->facets[2].health)) { + if (mode && MODIFIES(replacement_mode, playerUnit, blankUnit, armor->facets[2].health)) { std::string armor_color_strings[8] = { " - Fore-starboard-high: #-c", " - Aft-starboard-high: #-c", @@ -5273,14 +5117,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } } - if (!mode) { - PRETTY_ADDU(statcolor + "Sustainable Hull Damage: #-c", - playerUnit->GetHull() / (playerUnit->GetHullPercent()) * VSDM, 0, "MJ"); - if (1 != playerUnit->GetHullPercent()) { - PRETTY_ADD(" Current condition: ", playerUnit->GetHullPercent() * 100, 2); - text += "% of normal"; - } - } else if (MODIFIES(replacement_mode, playerUnit, blankUnit, GetHull())) { + if (mode && MODIFIES(replacement_mode, playerUnit, blankUnit, GetHull())) { switch (replacement_mode) { case 0: //Replacement or new Module PRETTY_ADDU(statcolor + "New Sustained Hull Damage Rating: #-c", @@ -5301,14 +5138,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C // Shields const int num_shields = playerUnit->shield->number_of_facets; - if (!mode) { - if (num_shields) { - PRETTY_ADD(statcolor + "Number of shield emitter facings: #-c", num_shields, 0); - text += "#n#" + prefix + statcolor + "Shield protection rating:#-c"; - } else { - text += "#n#" + prefix + statcolor + "No shielding. #-c"; - } - } else if (replacement_mode != 0 || playerUnit->shield->GetMaxHealth() != blankUnit->shield->GetMaxHealth()) { + if (mode && (replacement_mode != 0 || playerUnit->shield->GetMaxHealth() != blankUnit->shield->GetMaxHealth())) { switch (replacement_mode) { case 0: //Replacement or new Module text += "#n#" + prefix + statcolor + "Installs shield with following protection ratings:#-c"; @@ -5347,7 +5177,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } if (shield_strings) { - if (!mode || MODIFIES(replacement_mode, playerUnit, + if (mode && MODIFIES(replacement_mode, playerUnit, blankUnit, shield->GetMaxHealth())) { for (int i = 0; i < num_shields; i++) { @@ -5360,9 +5190,7 @@ void showUnitStats(Unit *playerUnit, string &text, int subunitlevel, int mode, C } const float 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()) { + if (mode && (replacement_mode != 0 || playerUnit->shield->GetRegeneration() != blankUnit->shield->GetRegeneration())) { switch (replacement_mode) { case 0: //Replacement or new Module PRETTY_ADDU(statcolor + "Shield protection recharge speed set to: #-c", regeneration * VSDM, 0, "MJ/s"); diff --git a/engine/src/cmd/unit_csv.cpp b/engine/src/cmd/unit_csv.cpp index bb88d22a5..543734403 100644 --- a/engine/src/cmd/unit_csv.cpp +++ b/engine/src/cmd/unit_csv.cpp @@ -47,6 +47,7 @@ #include "resource/resource.h" #include "unit_csv_factory.h" #include "upgradeable_unit.h" +#include "resource/manifest.h" extern int GetModeFromName(const char *input_buffer); extern void pushMesh(std::vector &mesh, @@ -640,6 +641,13 @@ void Unit::LoadRow(std::string unit_identifier, string modification, bool saved_ string tmpstr; csvRow = unit_identifier; + // Textual Descriptions + this->unit_key = unit_identifier; + this->unit_name = UnitCSVFactory::GetVariable(unit_key, "Name", std::string()); + this->unit_description = Manifest::MPL().GetShipDescription(unit_identifier); + + // This shadows the unit variable. It also doesn't support more than one ship. + // TODO: figure this out. std::string unit_key = (saved_game ? "player_ship" : unit_identifier); fullname = UnitCSVFactory::GetVariable(unit_key, "Name", std::string()); @@ -1081,42 +1089,32 @@ CSVRow GetUnitRow(string filename, bool subu, int faction, bool readlast, bool & } void Unit::WriteUnit(const char *modifications) { - const bool UNITTAB = configuration()->physics_config.unit_table; - if (UNITTAB) { - bool bad = false; - if (!modifications) { + bool bad = false; + if (!modifications) { + bad = true; + } + if (!bad) { + if (!strlen(modifications)) { bad = true; } - if (!bad) { - if (!strlen(modifications)) { - bad = true; - } - } - if (bad) { - VS_LOG(error, - (boost::format("Cannot Write out unit file %1% %2% that has no filename") % name.get().c_str() - % csvRow.get().c_str())); - return; - } - std::string savedir = modifications; - VSFileSystem::CreateDirectoryHome(VSFileSystem::savedunitpath + "/" + savedir); - VSFileSystem::VSFile f; - VSFileSystem::VSError err = f.OpenCreateWrite(savedir + "/" + name + ".csv", VSFileSystem::UnitFile); - if (err > VSFileSystem::Ok) { - VS_LOG(error, (boost::format("!!! ERROR : Writing saved unit file : %1%") % f.GetFullPath().c_str())); - return; - } - std::string towrite = WriteUnitString(); - f.Write(towrite.c_str(), towrite.length()); - f.Close(); - } else { - if (pImage->unitwriter) { - pImage->unitwriter->Write(modifications); - } - for (un_iter ui = getSubUnits(); (*ui) != NULL; ++ui) { - (*ui)->WriteUnit(modifications); - } } + if (bad) { + VS_LOG(error, + (boost::format("Cannot Write out unit file %1% %2% that has no filename") % name.get().c_str() + % csvRow.get().c_str())); + return; + } + std::string savedir = modifications; + VSFileSystem::CreateDirectoryHome(VSFileSystem::savedunitpath + "/" + savedir); + VSFileSystem::VSFile f; + VSFileSystem::VSError err = f.OpenCreateWrite(savedir + "/" + name + ".csv", VSFileSystem::UnitFile); + if (err > VSFileSystem::Ok) { + VS_LOG(error, (boost::format("!!! ERROR : Writing saved unit file : %1%") % f.GetFullPath().c_str())); + return; + } + std::string towrite = WriteUnitString(); + f.Write(towrite.c_str(), towrite.length()); + f.Close(); } using XMLSupport::tostring; @@ -1140,24 +1138,15 @@ static string tos(int val) { return XMLSupport::tostring(val); } -string Unit::WriteUnitString() { - const bool UNITTAB = configuration()->physics_config.unit_table; - string ret = ""; - if (!UNITTAB) { - // Is this code doing something? Is it legacy? - // TODO: figure this out - if (pImage->unitwriter) { - ret = pImage->unitwriter->WriteString(); - } - for (un_iter ui = getSubUnits(); (*ui) != NULL; ++ui) { - ret = ret + ((*ui)->WriteUnitString()); - } - return ret; - } - +const std::map Unit::UnitToMap() { std::map unit = UnitCSVFactory::GetUnit(name); string val; + // Textual Descriptions + unit["Key"] = unit_key; + unit["Name"] = unit_name; + unit["Textual_Description"] = unit_description; // Used in ship view + //mutable things unit["Equipment_Space"] = XMLSupport::tostring(equipment_volume); unit["Hold_Volume"] = XMLSupport::tostring(CargoVolume); @@ -1360,7 +1349,7 @@ string Unit::WriteUnitString() { 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_Color"] = std::to_string(computer.radar.capability); unit["Radar_Range"] = tos(computer.radar.maxrange); unit["Tracking_Cone"] = tos(acos(computer.radar.trackingcone) * 180. / M_PI); unit["Max_Cone"] = tos(acos(computer.radar.maxcone) * 180. / M_PI); @@ -1399,6 +1388,10 @@ string Unit::WriteUnitString() { unit["Tractorability"] = trac; } + return unit; +} +string Unit::WriteUnitString() { + std::map unit = UnitToMap(); return writeCSV(unit); } diff --git a/engine/src/cmd/unit_functions_generic.cpp b/engine/src/cmd/unit_functions_generic.cpp index 7fd60ec62..b31f12a56 100644 --- a/engine/src/cmd/unit_functions_generic.cpp +++ b/engine/src/cmd/unit_functions_generic.cpp @@ -36,7 +36,7 @@ #include "universe.h" #include "mount_size.h" #include "damageable.h" - +#include "resource/random_utils.h" //Various functions that were used in .cpp files that are now included because of //the temple GameUnit class @@ -255,10 +255,10 @@ void DealPossibleJumpDamage(Unit *un) { static double max_damage = XMLSupport::parse_float(vs_config->getVariable("physics", "max_jump_damage", "100")); // Also damage multiplier - double chance_to_damage = ((double) rand() / (RAND_MAX)) + 1; + double chance_to_damage = randomDouble() - 0.01; // If jump drive is fully operational, there's no chance for damage - if(un->jump_drive.Operational() >= chance_to_damage) { + if(un->jump_drive.Percent() >= chance_to_damage) { return; } diff --git a/engine/src/cmd/unit_generic.cpp b/engine/src/cmd/unit_generic.cpp index 8ce0cc8fa..566acdd91 100644 --- a/engine/src/cmd/unit_generic.cpp +++ b/engine/src/cmd/unit_generic.cpp @@ -424,11 +424,6 @@ void Unit::Init(const char *filename, std::string unitModifications, Flightgroup *flightgrp, int fg_subnumber) { - // Deprecated UNITTAB and configuration()->physics_config.unit_table options. - // Game will always load units from the JSON or CSV files. - // The other option was not implemented wholly. It simply opened the file - // but didn't do anything with it. See VSFile f variable. - // TODO: something with the following line this->Unit::Init(); graphicOptions.SubUnit = SubU ? 1 : 0; diff --git a/engine/src/cmd/unit_generic.h b/engine/src/cmd/unit_generic.h index 95d3e5df2..ffc3fc5ea 100644 --- a/engine/src/cmd/unit_generic.h +++ b/engine/src/cmd/unit_generic.h @@ -145,6 +145,16 @@ struct PlanetaryOrbitData; // TODO: move Armed to subclasses class Unit : public Armed, public Audible, public Drawable, public Damageable, public Energetic, public Intelligent, public Movable, public JumpCapable, public Carrier, public UpgradeableUnit { + // We store relevant textual description here. + // We stop relying on manifest and units data once we create the unit. + // The unit description is always taken from the manifest and not saved. + // Note the game confusingly refers to units: + // - class (Llama) - should be model + // - model (stock) - should be variant, but only if actually different without upgrades + // TODO: name is duplicated down below in some memory saving measure (StringPool::Reference) + std::string unit_key; + std::string unit_name; + std::string unit_description; protected: //How many lists are referencing us @@ -539,6 +549,8 @@ class Unit : public Armed, public Audible, public Drawable, public Damageable, p public: //tries to warp as close to un as possible abiding by the distances of various enemy ships...it might not make it all the way void WriteUnit(const char *modificationname = ""); + + const std::map UnitToMap(); std::string WriteUnitString(); //Loads a unit from an xml file into a complete datastructure void LoadXML(const char *filename, const char *unitModifications = "", std::string *xmlbuffer = NULL); diff --git a/engine/src/components/tests/jump_drive_tests.cpp b/engine/src/components/tests/jump_drive_tests.cpp new file mode 100644 index 000000000..a5659c9cf --- /dev/null +++ b/engine/src/components/tests/jump_drive_tests.cpp @@ -0,0 +1,66 @@ +/* + * jump_drive_tests.cpp + * + * Copyright (C) 2001-2023 Daniel Horn, Benjamen Meyer, Roy Falk, Stephen G. Tuggy, + * and other Vega Strike contributors. + * + * https://github.com/vegastrike/Vega-Strike-Engine-Source + * + * This file is part of Vega Strike. + * + * Vega Strike is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Vega Strike is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Vega Strike. If not, see . + */ + +#include +#include + +#include "jump_drive.h" +#include "resource/random_utils.h" +#include "unit_csv_factory.h" + +static const std::string upgrades_suffix_string = "__upgrades"; +static const std::string jump_drive_string = "jump_drive"; + +static const std::map jump_drive_map = { + {"Key", "jump_drive__upgrades"}, + {"Name", "Interstellar Jump Drive"}, + {"Upgrade_Type", "Jump_Drive"}, + {"Object_Type", "Upgrade_Replacement"}, + {"Textual_Description", "\"@upgrades/jump_drive.png@Jump drive for traveling between stars\"\n"}, + {"Mass", "10"}, + {"Moment_Of_Inertia", "10"}, + {"Jump_Drive_Present", "TRUE"}, + {"Jump_Drive_Delay", "1"} +}; + + + +// Used to quickly figure out why the code wasn't working properly +TEST(JumpDrive, Damage) { + UnitCSVFactory::LoadUnit(jump_drive_string + upgrades_suffix_string, jump_drive_map); + + JumpDrive jump_drive; + + jump_drive.Load("", jump_drive_string + upgrades_suffix_string); + + jump_drive.DamageByPercent(0.1); + + // Check operational drive shouldn't get damage + double chance_to_damage = randomDouble() - 0.01; + + std::cout << chance_to_damage << std::endl; + std::cout << jump_drive.Percent() << std::endl; + + //EXPECT_FALSE(true); +} \ No newline at end of file diff --git a/engine/src/configuration/configuration.cpp b/engine/src/configuration/configuration.cpp index 26f1b3c1f..7e5f20c6d 100644 --- a/engine/src/configuration/configuration.cpp +++ b/engine/src/configuration/configuration.cpp @@ -333,7 +333,6 @@ void Configuration::OverrideDefaultsWithUserConfiguration() { physics_config.velocity_max = GetGameConfig().GetFloat("physics.velocity_max", physics_config.velocity_max); physics_config.max_player_rotation_rate = GetGameConfig().GetFloat("physics.maxplayerrot", physics_config.max_player_rotation_rate); physics_config.max_non_player_rotation_rate = GetGameConfig().GetFloat("physics.maxNPCrot", physics_config.max_non_player_rotation_rate); - physics_config.unit_table = GetGameConfig().GetBool("physics.UnitTable", physics_config.unit_table); physics_config.capship_size = GetGameConfig().GetFloat("physics.capship_size", physics_config.capship_size); physics_config.near_autotrack_cone = GetGameConfig().GetFloat("physics.near_autotrack_cone", physics_config.near_autotrack_cone); physics_config.close_enough_to_autotrack = GetGameConfig().GetFloat("physics.close_enough_to_autotrack", physics_config.close_enough_to_autotrack); diff --git a/engine/src/configuration/configuration.h b/engine/src/configuration/configuration.h index b1f520f37..83824b812 100644 --- a/engine/src/configuration/configuration.h +++ b/engine/src/configuration/configuration.h @@ -387,7 +387,6 @@ struct PhysicsConfig { float velocity_max{10000.0F}; float max_player_rotation_rate{24.0F}; float max_non_player_rotation_rate{360.0F}; - bool unit_table{false}; float capship_size{500.0F}; float near_autotrack_cone{0.9F}; float close_enough_to_autotrack{4.0F}; diff --git a/engine/src/python/base_computer/ship_view.cpp b/engine/src/python/base_computer/ship_view.cpp new file mode 100644 index 000000000..47d442869 --- /dev/null +++ b/engine/src/python/base_computer/ship_view.cpp @@ -0,0 +1,78 @@ +/* + * ship_view.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 "ship_view.h" +#include "vsfilesystem.h" + +#include +#include +#include + +static const std::string module_name = "ship_view"; + +const std::string GetShipView(const std::map&ship_stats) { + if(!boost::filesystem::exists("python/base_computer/ship_view.py")) { + return "Error: ship description not found"; + } + + PyObject* module = PyImport_ImportModule(module_name.c_str()); + + if(!module) { + PyErr_Print(); + return "Error: PyImport_ImportModule is null"; + } + + boost::python::dict dict; + for (auto const& pair : ship_stats) { + dict[pair.first] = pair.second; + } + + + PyObject* args = PyTuple_Pack(1, dict.ptr()); + if(args == nullptr) { + PyErr_Print(); + return "Error: PyTuple_Pack is null"; + } + + PyObject* function = PyObject_GetAttrString(module,"get_ship_description"); + if(!function) { + PyErr_Print(); + return "Error: PyObject_GetAttrString is null"; + } + + + PyObject* pyResult = PyObject_CallObject(function, args); + + if(!pyResult) { + PyErr_Print(); + return "Error: PyObject_CallObject is null"; + } + + std::string result = PyUnicode_AsUTF8(pyResult); + + return result; +} \ No newline at end of file diff --git a/engine/src/python/base_computer/ship_view.h b/engine/src/python/base_computer/ship_view.h new file mode 100644 index 000000000..4d6691e36 --- /dev/null +++ b/engine/src/python/base_computer/ship_view.h @@ -0,0 +1,36 @@ +/* + * ship_view.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 VEGA_STRIKE_ENGINE_PYTHON_BASE_COMPUTER_SHIP_VIEW_H +#define VEGA_STRIKE_ENGINE_PYTHON_BASE_COMPUTER_SHIP_VIEW_H + +#include +#include + +const std::string GetShipView(const std::map&ship_stats); + +#endif // VEGA_STRIKE_ENGINE_PYTHON_BASE_COMPUTER_SHIP_VIEW_H \ No newline at end of file diff --git a/engine/src/python/init.cpp b/engine/src/python/init.cpp index 1f8965406..3c93f7458 100644 --- a/engine/src/python/init.cpp +++ b/engine/src/python/init.cpp @@ -52,6 +52,8 @@ #include "python_compile.h" #include "python_class.h" #include "cmd/unit_generic.h" +#include "python/config/python_utils.h" + #if defined (_WIN32) && !defined (__CYGWIN__) #include #include @@ -170,6 +172,15 @@ void Python::init() { InitBase(); InitDirector(); +// Add relevant paths to python path + const std::string python_path_string = GetPythonPath() + + ":" + VSFileSystem::programdir + + ":" + VSFileSystem::datadir + "/python/base_computer/"; + const std::wstring python_path_wstring = std::wstring(python_path_string.begin(), + python_path_string.end()); + const wchar_t* python_path = python_path_wstring.c_str(); + Py_SetPath(python_path); + // Now we can do python things about them and initialize them Py_Initialize(); initpaths(); diff --git a/engine/src/resource/manifest.cpp b/engine/src/resource/manifest.cpp index d37632a11..53e3c896d 100644 --- a/engine/src/resource/manifest.cpp +++ b/engine/src/resource/manifest.cpp @@ -197,3 +197,13 @@ Manifest Manifest::GetMissionManifest() { return manifest; } + +const std::string Manifest::GetShipDescription(const std::string unit_key) { + for(const Cargo& cargo : _items) { + if(cargo.name == unit_key) { + return cargo.description; + } + } + + return ""; +} \ No newline at end of file diff --git a/engine/src/resource/manifest.h b/engine/src/resource/manifest.h index 4b237e95b..a231de49a 100644 --- a/engine/src/resource/manifest.h +++ b/engine/src/resource/manifest.h @@ -53,6 +53,8 @@ class Manifest { std::vector getItems() { return _items; } bool empty() { return _items.empty(); } int size() { return _items.size(); } + + const std::string GetShipDescription(const std::string unit_key); };