From 41dd3f5e1d0ab85f725b337d7b64b7c5451a9c13 Mon Sep 17 00:00:00 2001 From: Ontheheavens <92992377+Ontheheavens@users.noreply.github.com> Date: Tue, 22 Nov 2022 15:27:33 +0400 Subject: [PATCH] Update 1.0.4 1. Fixed NullPointerException in mission refit screens where mousing over forge hullmods caused crash. 2. Civilian grade check now allows Archean Order Civilian-grade hullmod. 3. Added a setting to disable civilian-grade requirement. 4. Corrected hullmod descriptions with regards to "available" carge space requirement, while in code it was base cargo space stat derived from ShipHullSpecAPI. (Changing code to check for modified cargo space is unfeasible due to the nature of refit screen, which clones ship instances, therefore leading to hard-to-fix wonky behaviour.) 5. Added a setting to disable cargo space requirement. (Adding a setting for disabling hullsize requirement requires extensive rewrite, not something I'm willing to do.) 6. Enabled Fuel Centrifuge installation on Prometheus-class hulls. 7. Moved campaign burn abilities incompatibility from tags to code and added a setting to disable it. Move slowly restriction and sensor profile penalty settings are split and toggled off separately. 8. Added status lines in ability tooltip that tell whether conversion is working or halted due to insufficient inputs. --- 1.0.4 Changelog.txt | 27 ++ data/campaign/abilities.csv | 4 +- data/config/settings.json | 27 ++ data/hullmods/hull_mods.csv | 18 +- data/world/factions/hegemony.faction | 10 + data/world/factions/independent.faction | 10 + data/world/factions/persean_league.faction | 10 + data/world/factions/pirates.faction | 10 + data/world/factions/sindrian_diktat.faction | 10 + data/world/factions/tritachyon.faction | 10 + forge_production_settings.ini | 24 +- graphics/hullmods/fuel_centrifuge_module.png | Bin 0 -> 4787 bytes .../hullmods/machinery_assembly_module.png | Bin 0 -> 5951 bytes graphics/hullmods/ore_refinery_module.png | Bin 0 -> 4008 bytes .../hullmods/supply_manufacture_module.png | Bin 0 -> 6200 bytes graphics/icons/abilities/forge_production.png | Bin 0 -> 7891 bytes .../icons/intel/forge_production_report.png | Bin 0 -> 5254 bytes jars/forge_production.jar | Bin 0 -> 67389 bytes mod_info.json | 20 +- .../data/forge/abilities/ForgeProduction.java | 271 ++++++++++++++++ .../conversion/ForgeAssemblingLogic.java | 79 +++++ .../conversion/ForgeCentrifugingLogic.java | 83 +++++ .../conversion/ForgeManufacturingLogic.java | 79 +++++ .../conversion/ForgeRefiningLogic.java | 120 +++++++ .../support/ForgeConversionGeneral.java | 87 +++++ .../support/ForgeConversionVariables.java | 57 ++++ .../conversion/support/ForgeTypes.java | 30 ++ .../tooltip/ForgeProductionTooltip.java | 132 ++++++++ .../tooltip/ForgeTooltipBreakdown.java | 296 ++++++++++++++++++ .../abilities/tooltip/ForgeTooltipItems.java | 138 ++++++++ .../abilities/tooltip/ForgeTooltipShips.java | 115 +++++++ .../forge/campaign/ForgeConditionChecker.java | 194 ++++++++++++ .../forge/campaign/ForgeProductionReport.java | 138 ++++++++ .../forge/hullmods/ForgeAssemblyModule.java | 186 +++++++++++ .../forge/hullmods/ForgeCentrifugeModule.java | 191 +++++++++++ .../hullmods/ForgeManufactureModule.java | 186 +++++++++++ .../forge/hullmods/ForgeRefineryModule.java | 188 +++++++++++ .../support/ForgeHullmodsGeneral.java | 129 ++++++++ .../support/ForgeHullmodsTooltip.java | 156 +++++++++ .../data/forge/plugins/ForgeDataSupplier.java | 81 +++++ source/data/forge/plugins/ForgeModPlugin.java | 27 ++ source/data/forge/plugins/ForgeSettings.java | 66 ++++ 42 files changed, 3181 insertions(+), 28 deletions(-) create mode 100644 1.0.4 Changelog.txt create mode 100644 data/config/settings.json create mode 100644 data/world/factions/hegemony.faction create mode 100644 data/world/factions/independent.faction create mode 100644 data/world/factions/persean_league.faction create mode 100644 data/world/factions/pirates.faction create mode 100644 data/world/factions/sindrian_diktat.faction create mode 100644 data/world/factions/tritachyon.faction create mode 100644 graphics/hullmods/fuel_centrifuge_module.png create mode 100644 graphics/hullmods/machinery_assembly_module.png create mode 100644 graphics/hullmods/ore_refinery_module.png create mode 100644 graphics/hullmods/supply_manufacture_module.png create mode 100644 graphics/icons/abilities/forge_production.png create mode 100644 graphics/icons/intel/forge_production_report.png create mode 100644 jars/forge_production.jar create mode 100644 source/data/forge/abilities/ForgeProduction.java create mode 100644 source/data/forge/abilities/conversion/ForgeAssemblingLogic.java create mode 100644 source/data/forge/abilities/conversion/ForgeCentrifugingLogic.java create mode 100644 source/data/forge/abilities/conversion/ForgeManufacturingLogic.java create mode 100644 source/data/forge/abilities/conversion/ForgeRefiningLogic.java create mode 100644 source/data/forge/abilities/conversion/support/ForgeConversionGeneral.java create mode 100644 source/data/forge/abilities/conversion/support/ForgeConversionVariables.java create mode 100644 source/data/forge/abilities/conversion/support/ForgeTypes.java create mode 100644 source/data/forge/abilities/tooltip/ForgeProductionTooltip.java create mode 100644 source/data/forge/abilities/tooltip/ForgeTooltipBreakdown.java create mode 100644 source/data/forge/abilities/tooltip/ForgeTooltipItems.java create mode 100644 source/data/forge/abilities/tooltip/ForgeTooltipShips.java create mode 100644 source/data/forge/campaign/ForgeConditionChecker.java create mode 100644 source/data/forge/campaign/ForgeProductionReport.java create mode 100644 source/data/forge/hullmods/ForgeAssemblyModule.java create mode 100644 source/data/forge/hullmods/ForgeCentrifugeModule.java create mode 100644 source/data/forge/hullmods/ForgeManufactureModule.java create mode 100644 source/data/forge/hullmods/ForgeRefineryModule.java create mode 100644 source/data/forge/hullmods/support/ForgeHullmodsGeneral.java create mode 100644 source/data/forge/hullmods/support/ForgeHullmodsTooltip.java create mode 100644 source/data/forge/plugins/ForgeDataSupplier.java create mode 100644 source/data/forge/plugins/ForgeModPlugin.java create mode 100644 source/data/forge/plugins/ForgeSettings.java diff --git a/1.0.4 Changelog.txt b/1.0.4 Changelog.txt new file mode 100644 index 0000000..2a40267 --- /dev/null +++ b/1.0.4 Changelog.txt @@ -0,0 +1,27 @@ +1. Fixed NullPointerException in mission refit screens where mousing over forge hullmods caused crash. + +2. Civilian grade check now allows Archean Order Civilian-grade hullmod. + +3. Added a setting to disable civilian-grade requirement. + +4. Corrected hullmod descriptions with regards to "available" carge space requirement, + while in code it was base cargo space stat derived from ShipHullSpecAPI. + + (Changing code to check for modified cargo space is unfeasible due to the nature of refit screen, + which clones ship instances, therefore leading to hard-to-fix wonky behaviour.) + +5. Added a setting to disable cargo space requirement. + + (Adding a setting for disabling hullsize requirement requires extensive rewrite, not something I'm willing to do.) + +6. Enabled Fuel Centrifuge installation on Prometheus-class hulls. + +7. Moved campaign burn abilities incompatibility from tags to code and added a setting to disable it. + Move slowly restriction and sensor profile penalty settings are split and toggled off separately. + +8. Added status lines in ability tooltip that tell whether conversion is working or halted due to insufficient inputs. + + *** + + Important: I do NOT guarantee that your save will be compatible with this update or that there will be no crashes. + It is working fine on my end, but no promises. \ No newline at end of file diff --git a/data/campaign/abilities.csv b/data/campaign/abilities.csv index ec77a18..8ab6e62 100644 --- a/data/campaign/abilities.csv +++ b/data/campaign/abilities.csv @@ -1,2 +1,2 @@ -name,id,type,tags,activationDays,activationCooldown,durationDays,deactivationDays,deactivationCooldown,unlockedAtStart,defaultForAIFleet,musicSuppression,uiOn,uiOff,uiLoop,worldOn,worldOff,worldLoop,icon,plugin,ai,desc,sortOrder -"Forge Production",forge_production,TOGGLE,"burn-, stealth-,sensors+",0.5,,,0.25,1,TRUE,,,ui_neutrino_detector_on,ui_neutrino_detector_off,,system_temporalshell_off,,system_temporalshell_loop,graphics/icons/abilities/forge_production.png,data.forge.abilities.ForgeProduction,,"Control forge production of your fleet.",868 +name,id,type,tags,activationDays,activationCooldown,durationDays,deactivationDays,deactivationCooldown,unlockedAtStart,defaultForAIFleet,musicSuppression,uiOn,uiOff,uiLoop,worldOn,worldOff,worldLoop,icon,plugin,ai,desc,sortOrder +"Forge Production",forge_production,TOGGLE,,0.5,,,0.25,1,TRUE,,,ui_neutrino_detector_on,ui_neutrino_detector_off,,system_temporalshell_off,,system_temporalshell_loop,graphics/icons/abilities/forge_production.png,data.forge.abilities.ForgeProduction,,"Control forge production of your fleet.",868 diff --git a/data/config/settings.json b/data/config/settings.json new file mode 100644 index 0000000..509b294 --- /dev/null +++ b/data/config/settings.json @@ -0,0 +1,27 @@ +{ + "designTypeColors": { + + "Forge":[161, 152, 146, 255] + + }, + + "graphics": { + + "intel": { + + "forge_production_report": "graphics/icons/intel/forge_production_report.png", + + }, + + "hullmods": { + + "ore_refinery_module": "graphics/hullmods/ore_refinery_module.png", + "fuel_centrifuge_module": "graphics/hullmods/fuel_centrifuge_module.png", + "supply_manufacture_module": "graphics/hullmods/supply_manufacture_module.png", + "machinery_assembly_module": "graphics/hullmods/machinery_assembly_module.png", + + } + + } + +} \ No newline at end of file diff --git a/data/hullmods/hull_mods.csv b/data/hullmods/hull_mods.csv index a082297..b7ed766 100644 --- a/data/hullmods/hull_mods.csv +++ b/data/hullmods/hull_mods.csv @@ -1,17 +1,9 @@ -name,id,tier,rarity,tech/manufacturer,tags,uiTags,"base value",unlocked,hidden,hiddenEverywhere,cost_frigate,cost_dest,cost_cruiser,cost_capital,script,desc,short,sprite +name,id,tier,rarity,tech/manufacturer,tags,uiTags,"base value",unlocked,hidden,hiddenEverywhere,cost_frigate,cost_dest,cost_cruiser,cost_capital,script,desc,short,sprite "Ore Refinery",forge_refinery_module,2,"0.8 -",Forge,"req_spaceport, special","Special, Requires Dock",120000,FALSE,FALSE,FALSE,25,25,25,30,data.forge.hullmods.ForgeRefineryModule,"Refits %s/%s available cargo space, depending on hull size, into ore refinery, allowing for production of metal and transplutonics from their raw ores. It requires additional %s/%s crew and %s/%s supplies per month, depending on hull size. - -Installable only on cruisers or capital ships with civilian-grade hull.","Allows refining of ore.",graphics/hullmods/ore_refinery_module.png +",Forge,"req_spaceport, special","Special, Requires Dock",120000,FALSE,FALSE,FALSE,25,25,25,30,data.forge.hullmods.ForgeRefineryModule,"Refits %s/%s base cargo space, depending on hull size, into ore refinery, allowing for production of metal and transplutonics from their raw ores. It requires additional %s/%s crew and %s/%s supplies per month, depending on hull size.","Allows refining of ore.",graphics/hullmods/ore_refinery_module.png "Fuel Centrifuge",forge_centrifuge_module,2,"0.6 -",Forge,"req_spaceport, special","Special, Requires Dock",140000,FALSE,FALSE,FALSE,25,25,25,30,data.forge.hullmods.ForgeCentrifugeModule,"Refits %s/%s available cargo space, depending on hull size, into fuel centrifuge, allowing for production of fuel from volatiles. It requires additional %s/%s crew and %s/%s supplies per month, depending on hull size. - -Installable only on cruisers or capital ships with civilian-grade hull.","Allows centrifuging of volatiles.",graphics/hullmods/fuel_centrifuge_module.png +",Forge,"req_spaceport, special","Special, Requires Dock",140000,FALSE,FALSE,FALSE,25,25,25,30,data.forge.hullmods.ForgeCentrifugeModule,"Refits %s/%s base cargo space, depending on hull size, into fuel centrifuge, allowing for production of fuel from volatiles. It requires additional %s/%s crew and %s/%s supplies per month, depending on hull size.","Allows centrifuging of volatiles.",graphics/hullmods/fuel_centrifuge_module.png "Supply Manufacture",forge_manufacture_module,3,"0.4 -",Forge,"req_spaceport, special","Special, Requires Dock",160000,FALSE,FALSE,FALSE,25,25,25,30,data.forge.hullmods.ForgeManufactureModule,"Refits %s/%s available cargo space, depending on hull size, into supply manufacture, allowing for production of supplies from metal and transplutonics. It requires additional %s/%s crew and %s/%s supplies per month, depending on hull size. - -Installable only on cruisers or capital ships with civilian-grade hull.","Allows manufacturing of supplies",graphics/hullmods/supply_manufacture_module.png +",Forge,"req_spaceport, special","Special, Requires Dock",160000,FALSE,FALSE,FALSE,25,25,25,30,data.forge.hullmods.ForgeManufactureModule,"Refits %s/%s base cargo space, depending on hull size, into supply manufacture, allowing for production of supplies from metal and transplutonics. It requires additional %s/%s crew and %s/%s supplies per month, depending on hull size.","Allows manufacturing of supplies",graphics/hullmods/supply_manufacture_module.png "Machinery Assembly",forge_assembly_module,3,"0.2 -",Forge,"req_spaceport, special","Special, Requires Dock",180000,FALSE,FALSE,FALSE,25,25,25,30,data.forge.hullmods.ForgeAssemblyModule,"Refits %s/%s available cargo space, depending on hull size, into machinery assembly, allowing for production of heavy machinery from metal and transplutonics. It requires additional %s/%s crew and %s/%s supplies per month, depending on hull size. - -Installable only on cruisers or capital ships with civilian-grade hull.","Allows assembling of heavy machinery",graphics/hullmods/machinery_assembly_module.png +",Forge,"req_spaceport, special","Special, Requires Dock",180000,FALSE,FALSE,FALSE,25,25,25,30,data.forge.hullmods.ForgeAssemblyModule,"Refits %s/%s base cargo space, depending on hull size, into machinery assembly, allowing for production of heavy machinery from metal and transplutonics. It requires additional %s/%s crew and %s/%s supplies per month, depending on hull size.","Allows assembling of heavy machinery",graphics/hullmods/machinery_assembly_module.png diff --git a/data/world/factions/hegemony.faction b/data/world/factions/hegemony.faction new file mode 100644 index 0000000..cf2e4f1 --- /dev/null +++ b/data/world/factions/hegemony.faction @@ -0,0 +1,10 @@ +{ + "knownHullMods":{ + "hullMods":[ + "forge_refinery_module", + "forge_centrifuge_module", + "forge_manufacture_module", + "forge_assembly_module" + ], + }, +}, \ No newline at end of file diff --git a/data/world/factions/independent.faction b/data/world/factions/independent.faction new file mode 100644 index 0000000..cf2e4f1 --- /dev/null +++ b/data/world/factions/independent.faction @@ -0,0 +1,10 @@ +{ + "knownHullMods":{ + "hullMods":[ + "forge_refinery_module", + "forge_centrifuge_module", + "forge_manufacture_module", + "forge_assembly_module" + ], + }, +}, \ No newline at end of file diff --git a/data/world/factions/persean_league.faction b/data/world/factions/persean_league.faction new file mode 100644 index 0000000..cf2e4f1 --- /dev/null +++ b/data/world/factions/persean_league.faction @@ -0,0 +1,10 @@ +{ + "knownHullMods":{ + "hullMods":[ + "forge_refinery_module", + "forge_centrifuge_module", + "forge_manufacture_module", + "forge_assembly_module" + ], + }, +}, \ No newline at end of file diff --git a/data/world/factions/pirates.faction b/data/world/factions/pirates.faction new file mode 100644 index 0000000..cf2e4f1 --- /dev/null +++ b/data/world/factions/pirates.faction @@ -0,0 +1,10 @@ +{ + "knownHullMods":{ + "hullMods":[ + "forge_refinery_module", + "forge_centrifuge_module", + "forge_manufacture_module", + "forge_assembly_module" + ], + }, +}, \ No newline at end of file diff --git a/data/world/factions/sindrian_diktat.faction b/data/world/factions/sindrian_diktat.faction new file mode 100644 index 0000000..cf2e4f1 --- /dev/null +++ b/data/world/factions/sindrian_diktat.faction @@ -0,0 +1,10 @@ +{ + "knownHullMods":{ + "hullMods":[ + "forge_refinery_module", + "forge_centrifuge_module", + "forge_manufacture_module", + "forge_assembly_module" + ], + }, +}, \ No newline at end of file diff --git a/data/world/factions/tritachyon.faction b/data/world/factions/tritachyon.faction new file mode 100644 index 0000000..cf2e4f1 --- /dev/null +++ b/data/world/factions/tritachyon.faction @@ -0,0 +1,10 @@ +{ + "knownHullMods":{ + "hullMods":[ + "forge_refinery_module", + "forge_centrifuge_module", + "forge_manufacture_module", + "forge_assembly_module" + ], + }, +}, \ No newline at end of file diff --git a/forge_production_settings.ini b/forge_production_settings.ini index 830064d..5c78b1c 100644 --- a/forge_production_settings.ini +++ b/forge_production_settings.ini @@ -1,12 +1,23 @@ { - # Exercise caution when modifying settings variables: balance is fragile. + ####### Installation Settings ####### + + "forge_enable_cargo_requirement": true, + "forge_enable_civgrade_requirement": true, ####### Miscellaneous Settings ####### - # Slow-moving and detected-at-range penalties when forge production is on. + # Slow-moving penalty when forge production is on. + + "forge_enable_slow_move_penalty": true, + + # Detected-at-range penalty when forge production is on. + + "forge_enable_sensor_penalty": true, - "forge_enable_ability_penalties": true, + # Burn abilities are Sustained Burn and Emergency Burn. + + "forge_enable_burn_abilities_incompatibility": true, # Percentage. @@ -21,11 +32,16 @@ "forge_play_sound_notification": true, "forge_show_notification": true, + # Determines style of production report. + # True is inverted grid, while false is basic grid. Default is false. + + "forge_inverted_grid_notification": false, + # In days. Production is still calculated daily. # Results are gathered and cleared when intel is created. # Recommended to set only to values >= 1, default = 3. - "forge_notification_interval": 1, # (Integer) + "forge_notification_interval": 3, # (Integer) ####### Ship Capacity Settings ####### diff --git a/graphics/hullmods/fuel_centrifuge_module.png b/graphics/hullmods/fuel_centrifuge_module.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7961b490271eb80d3cf712724a9d4234a6f58e GIT binary patch literal 4787 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 aZL7_i3r}oE{?+wwY1~0elMWF=71!Pn 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 J4zkme{}0`HME?K) literal 0 HcmV?d00001 diff --git a/graphics/icons/abilities/forge_production.png b/graphics/icons/abilities/forge_production.png new file mode 100644 index 0000000000000000000000000000000000000000..976e4984303505bbf92fcecd415d4a0bfbb52e7a GIT binary patch literal 7891 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 DSf>QC literal 0 HcmV?d00001 diff --git a/graphics/icons/intel/forge_production_report.png b/graphics/icons/intel/forge_production_report.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3ee677c45666d3a90f703f1a6bfaeff5788ef6 GIT binary patch literal 5254 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

Ioc)M}N|rj@OHV{L-q}=_igM%2&>T3d(0Gi^uM2 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 P;ExzA5YXz6pa1%QrtTKf literal 0 HcmV?d00001 diff --git a/mod_info.json b/mod_info.json index 5cadb40..a8ebe1e 100644 --- a/mod_info.json +++ b/mod_info.json @@ -1,10 +1,12 @@ -{ - "id":"forge_production", - "name":"Forge Production", - "author":"Ontheheavens", - "version":"1.0.1", - "description":"Hullmods that allow ships to produce commodities with ability toggle.", - "gameVersion":"0.95a-RC15", - "jars":["jars/forge_production.jar"], - "modPlugin":"data.forge.plugins.ForgeModPlugin", +{ + "id":"forge_production", + "name":"Forge Production", + "author":"Ontheheavens", + "version":"1.0.4", + "description":"Hullmods that allow ships to produce commodities with ability toggle.", + "gameVersion": "0.95.1a-RC6", + "jars":["jars/forge_production.jar"], + "modPlugin":"data.forge.plugins.ForgeModPlugin", + "totalConversion": "false", + "utility": "false", } \ No newline at end of file diff --git a/source/data/forge/abilities/ForgeProduction.java b/source/data/forge/abilities/ForgeProduction.java new file mode 100644 index 0000000..54ca523 --- /dev/null +++ b/source/data/forge/abilities/ForgeProduction.java @@ -0,0 +1,271 @@ +package data.forge.abilities; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import com.fs.starfarer.api.characters.AbilityPlugin; +import com.fs.starfarer.api.impl.campaign.ids.Commodities; +import data.forge.abilities.conversion.*; +import data.forge.abilities.conversion.support.ForgeConversionGeneral; +import data.forge.abilities.tooltip.ForgeProductionTooltip; +import data.forge.campaign.ForgeConditionChecker; +import data.forge.plugins.ForgeSettings; +import org.lwjgl.util.vector.Vector2f; +import com.fs.starfarer.api.Global; +import com.fs.starfarer.api.util.Misc; +import com.fs.starfarer.api.util.IntervalUtil; +import com.fs.starfarer.api.ui.LabelAPI; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.impl.campaign.abilities.BaseToggleAbility; + +import data.forge.campaign.ForgeProductionReport; + +import static data.forge.campaign.ForgeConditionChecker.*; +import static data.forge.plugins.ForgeSettings.*; +import static data.forge.abilities.conversion.support.ForgeConversionVariables.*; + +public class ForgeProduction extends BaseToggleAbility { + + private static final Vector2f ZERO = new Vector2f(); + + private int notificationCounter = 0; + + private final IntervalUtil productionInterval = new IntervalUtil(1f,1f); + + protected void activateImpl() { + interruptIncompatible(); + } + + @Override + protected String getActivationText() { + return "Commencing forge production"; + } + + protected void applyEffect(float amount, float level) { + + CampaignFleetAPI fleet = getFleet(); + + if (fleet == null) return; + if (!isActive()) return; + + if (ENABLE_DETECT_AT_RANGE_PENALTY) { + fleet.getStats().getDetectedRangeMod().modifyPercent(getModId(), ForgeSettings.SENSOR_PROFILE_INCREASE, "Forge production"); + } + + if (ENABLE_SLOW_MOVE_PENALTY) { + fleet.goSlowOneFrame(); + } + + float days = Global.getSector().getClock().convertToDays(amount); + + productionInterval.advance(days); + + if (productionInterval.intervalElapsed()) { + + ForgeRefiningLogic.startRefining(fleet); + + ForgeCentrifugingLogic.startCentrifuging(fleet); + + ForgeManufacturingLogic.startManufacturing(fleet); + + ForgeAssemblingLogic.startAssembling(fleet); + + if (goodsWereForged()) { + + fleet.addFloatingText("Forged goods", Misc.setAlpha(Misc.getTextColor(), 255), 0.5f); + + if(PLAY_SOUND_NOTIFICATION) { + Global.getSoundPlayer().playSound("ui_cargo_raremetals", 1f, 1f, fleet.getLocation(), ZERO); + } + + clearGoodsStatus(); + } + + if (SHOW_NOTIFICATION && goodsProducedReport()) { + notificationCounter += 1; + } + + boolean notificationCounterElapsed = (notificationCounter >= NOTIFICATION_INTERVAL); + + if (SHOW_NOTIFICATION && (notificationCounterElapsed)) { + ForgeProductionReport intel = new ForgeProductionReport(); + Global.getSector().getIntelManager().addIntel(intel); + notificationCounter = 0; + } + + } + + if (!checkUsableForMachinery()) { + deactivate(); + } + disableIncompatible(); + } + + protected void deactivateImpl() { cleanupImpl(); } + + @Override + protected void cleanupImpl() { + CampaignFleetAPI fleet = getFleet(); + if (fleet == null) return; + + if (ENABLE_DETECT_AT_RANGE_PENALTY) { + fleet.getStats().getDetectedRangeMod().unmodify(getModId()); + } + } + + private boolean checkUsableForMachinery() { + boolean hasMachinery = ForgeConditionChecker.getPlayerCargo(Commodities.HEAVY_MACHINERY) > 0; + return (hasActiveForgeShips() && hasMachinery); + } + + @Override + public boolean isUsable() { + if (!isActivateCooldown && + getProgressFraction() > 0 && getProgressFraction() < 1 && + getDeactivationDays() > 0) return false; + + if (!checkUsableForMachinery()) + return false; + + return super.isUsable(); + } + + @Override + public boolean showActiveIndicator() { return isActive(); } + + @Override + public boolean hasTooltip() { return true; } + + @Override + public float getTooltipWidth() { + return 390f; + } + + @Override + public void createTooltip(TooltipMakerAPI tooltip, boolean expanded) { + + Color gray = Misc.getGrayColor(); + Color highlight = Misc.getHighlightColor(); + + String status = " (off)"; + if (turnedOn) { + status = " (on)"; + } + + LabelAPI title = tooltip.addTitle(this.spec.getName() + status); + title.highlightLast(status); + title.setHighlightColor(highlight); + + String percentageCR = "10%"; + String percentageMachinery = "10%"; + + float pad = 6f; + tooltip.addPara("Control forge production of your fleet and monitor fleet forging capacity.", pad); + if (!expanded) { + tooltip.addPara("Your fleet must have ships with active forge modules to produce goods. " + + "Ships that are mothballed, do not receive repairs or have less than %s CR are unable to use their forge modules. " + + "Production also uses heavy machinery, which must be present but is not consumed, and can break accidentally. " + + "At least %s of total needed machinery must be available to produce goods.", pad, highlight, percentageCR, percentageMachinery); + } + + if (!hasActiveForgeShips() && expanded) { + ForgeProductionTooltip.addCannotForgeNoActiveShips(tooltip); + } + + if (hasActiveForgeShips() && ForgeConversionGeneral.getMachineryAvailability() < 0.1f && expanded) { + ForgeProductionTooltip.addCannotForgeNoMachinery(tooltip); + } + + if (expanded) { + ForgeProductionTooltip.addExpandedInfo(tooltip, this.isActive()); + } + + if (ENABLE_DETECT_AT_RANGE_PENALTY && !expanded) { + tooltip.addPara("Increases the range at which the fleet can be detected by %s.", + pad, highlight, (int) ForgeSettings.SENSOR_PROFILE_INCREASE + "%"); + } + + if (ENABLE_SLOW_MOVE_PENALTY && !expanded) { + tooltip.addPara("Causes the fleet to %s.", + pad, highlight, "move slowly"); + + tooltip.addPara("*A fleet is considered slow-moving at a burn level of half that of its slowest ship.", gray, pad); + } + + if (!hasActiveForgeShips() && !expanded) { + ForgeProductionTooltip.addCannotForgeNoActiveShips(tooltip); + } + + if (hasActiveForgeShips() && ForgeConversionGeneral.getMachineryAvailability() < 0.1f && !expanded) { + ForgeProductionTooltip.addCannotForgeNoMachinery(tooltip); + } + + addIncompatibleToTooltip(tooltip, expanded); + } + + @Override + protected void addIncompatibleToTooltip(TooltipMakerAPI tooltip, boolean expanded) { + addIncompatibleToTooltip(tooltip, "Incompatible with the following abilities:", + "Expand tooltip to view production specifics and conflicting abilities", + expanded); + } + + @Override + protected boolean isCompatible(AbilityPlugin other) { + if(!ENABLE_BURN_ABILITIES_INCOMPATIBILITY) + return true; + if (other.getId().equals("sustained_burn") || other.getId().equals("emergency_burn")) { + return false; + } + return true; + } + + @Override + public List getInterruptedList() { + List result = new ArrayList<>(); + CampaignFleetAPI fleet = getFleet(); + if (fleet == null) return result; + for (AbilityPlugin curr : fleet.getAbilities().values()) { + if (curr == this) continue; + if (!isCompatible(curr)) { + result.add(curr); + } + } + Collections.sort(result, new Comparator() { + public int compare(AbilityPlugin o1, AbilityPlugin o2) { + + return o1.getSpec().getSortOrder() - o2.getSpec().getSortOrder(); + } + }); + return result; + } + + @Override + protected void interruptIncompatible() { + CampaignFleetAPI fleet = getFleet(); + if (fleet == null) return; + for (AbilityPlugin curr : fleet.getAbilities().values()) { + if (curr == this) continue; + if (!isCompatible(curr) && curr.isActive()) { + curr.deactivate(); + } + } + } + + @Override + protected void disableIncompatible() { + CampaignFleetAPI fleet = getFleet(); + if (fleet == null) return; + for (AbilityPlugin curr : fleet.getAbilities().values()) { + if (curr == this) continue; + if (!isCompatible(curr)) { + curr.forceDisable(); + } + } + } + +} \ No newline at end of file diff --git a/source/data/forge/abilities/conversion/ForgeAssemblingLogic.java b/source/data/forge/abilities/conversion/ForgeAssemblingLogic.java new file mode 100644 index 0000000..6b2425c --- /dev/null +++ b/source/data/forge/abilities/conversion/ForgeAssemblingLogic.java @@ -0,0 +1,79 @@ +package data.forge.abilities.conversion; + +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.impl.campaign.ids.Commodities; +import data.forge.abilities.conversion.support.ForgeConversionGeneral; +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; + +import static data.forge.abilities.conversion.support.ForgeConversionVariables.*; +import static data.forge.plugins.ForgeSettings.*; + +public class ForgeAssemblingLogic { + + public static void startAssembling(CampaignFleetAPI fleet) { + + if (ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.ASSEMBLY) <= 1) return; + if (!ForgeConditionChecker.hasMinimumMachinery()) return; + + if (ForgeConditionChecker.getPlayerCargo(Commodities.METALS) > METAL_TO_ASSEMBLE && + ForgeConditionChecker.getPlayerCargo(Commodities.RARE_METALS) > TRANSPLUTONICS_TO_ASSEMBLE) { + assembleMachinery(fleet); + } + + if (heavyMachineryWasAssembled) { + ForgeConversionGeneral.applyForgingCRMalus(fleet, ForgeTypes.ASSEMBLY); + } + + } + + public static void assembleMachinery(CampaignFleetAPI fleet) { + + int machineryAssemblingCycles = ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.ASSEMBLING, ForgeTypes.ASSEMBLY); + + int assemblingCapacity = ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.ASSEMBLY); + + int metalAssemblingCycles = (int) Math.floor(ForgeConditionChecker.getPlayerCargo(Commodities.METALS) / METAL_TO_ASSEMBLE); + int transplutonicsAssemblingCycles = (int) Math.floor(ForgeConditionChecker.getPlayerCargo(Commodities.RARE_METALS) / TRANSPLUTONICS_TO_ASSEMBLE); + + int assemblingCycles = (int) Math.floor(Math.min(assemblingCapacity, + Math.min(machineryAssemblingCycles, Math.min(metalAssemblingCycles, transplutonicsAssemblingCycles)))); + + boolean willHeavyMachineryBreakdown = ForgeConditionChecker.getMachineryBreakdownChance(); + int MachineryInAssembling = (int) Math.ceil((assemblingCapacity * HEAVY_MACHINERY_ASSEMBLING_USAGE)); + int machineryBroken = 0; + + for (int machineryInCheck = 0; machineryInCheck < MachineryInAssembling; machineryInCheck++) { + if (Math.random() < (BREAKDOWN_SEVERITY * ForgeConditionChecker.getForgingQuality())) + machineryBroken++; + } + + int dailyMetalSpent = (int) Math.ceil(METAL_TO_ASSEMBLE * assemblingCycles); + int dailyTransplutonicsSpent = (int) Math.ceil(TRANSPLUTONICS_TO_ASSEMBLE * assemblingCycles); + int dailyHeavyMachineryProduced = (int) Math.floor((HEAVY_MACHINERY_PRODUCED + ForgeConditionChecker.getNanoforgeAssemblingBonus()) * assemblingCycles); + int dailyHeavyMachineryBroken = machineryBroken; + + fleet.getCargo().removeCommodity(Commodities.METALS, dailyMetalSpent); + fleet.getCargo().removeCommodity(Commodities.RARE_METALS, dailyTransplutonicsSpent); + fleet.getCargo().addCommodity(Commodities.HEAVY_MACHINERY, dailyHeavyMachineryProduced); + if (willHeavyMachineryBreakdown) { + fleet.getCargo().removeCommodity(Commodities.HEAVY_MACHINERY, dailyHeavyMachineryBroken); + } + + totalHeavyMachineryProduction += dailyHeavyMachineryProduced; + if (willHeavyMachineryBreakdown) { + totalMachineryBreakage += dailyHeavyMachineryBroken; + } + + if (willHeavyMachineryBreakdown && dailyHeavyMachineryBroken > 0) { + breakdownReport = true; + } + + if (dailyHeavyMachineryProduced > 0) { + heavyMachineryWasAssembled = true; + heavyMachineryAssemblingReport = true; + } + + } + +} diff --git a/source/data/forge/abilities/conversion/ForgeCentrifugingLogic.java b/source/data/forge/abilities/conversion/ForgeCentrifugingLogic.java new file mode 100644 index 0000000..3d04e48 --- /dev/null +++ b/source/data/forge/abilities/conversion/ForgeCentrifugingLogic.java @@ -0,0 +1,83 @@ +package data.forge.abilities.conversion; + +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.impl.campaign.ids.Commodities; + +import data.forge.abilities.conversion.support.ForgeConversionGeneral; +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; + +import static data.forge.plugins.ForgeSettings.*; +import static data.forge.abilities.conversion.support.ForgeConversionVariables.*; + +public class ForgeCentrifugingLogic { + + public static void startCentrifuging(CampaignFleetAPI fleet) { + + if (ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.CENTRIFUGE) <= 1) return; + if (!ForgeConditionChecker.hasMinimumMachinery()) return; + if (getPossibleCentrifugingCycles(fleet) < 1) return; + + if (ForgeConditionChecker.getPlayerCargo(Commodities.VOLATILES) > VOLATILES_TO_CENTRIFUGE) { + centrifugeFuel(fleet); + } + + if (fuelWasCentrifuged) { + ForgeConversionGeneral.applyForgingCRMalus(fleet, ForgeTypes.CENTRIFUGE); + } + + } + + public static int getPossibleCentrifugingCycles(CampaignFleetAPI fleet) { + + return (int) Math.floor(fleet.getCargo().getFreeFuelSpace() / (FUEL_PRODUCED + ForgeConditionChecker.getSynchrotronCoreBonus())); + + } + + public static void centrifugeFuel(CampaignFleetAPI fleet) { + + int machineryCentrifugingCycles = ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.CENTRIFUGING, ForgeTypes.CENTRIFUGE); + + int centrifugingCapacity = ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.CENTRIFUGE); + + int volatilesCentrifugingCycles = (int) Math.floor(ForgeConditionChecker.getPlayerCargo(Commodities.VOLATILES) / VOLATILES_TO_CENTRIFUGE); + + int initialCentrifugingCycles = Math.min(centrifugingCapacity, Math.min(machineryCentrifugingCycles, volatilesCentrifugingCycles)); + + int centrifugingCycles = (int) Math.floor(Math.min(initialCentrifugingCycles, getPossibleCentrifugingCycles(fleet))); + + boolean willHeavyMachineryBreakdown = ForgeConditionChecker.getMachineryBreakdownChance(); + int machineryInCentrifuging = (int) Math.ceil((centrifugingCycles * HEAVY_MACHINERY_CENTRIFUGING_USAGE)); + int machineryBroken = 0; + + for (int machineryInCheck = 0; machineryInCheck < machineryInCentrifuging; machineryInCheck++) { + if (Math.random()<(BREAKDOWN_SEVERITY * ForgeConditionChecker.getForgingQuality())) machineryBroken++; + } + + int dailyVolatilesSpent = (int) Math.ceil(VOLATILES_TO_CENTRIFUGE * centrifugingCycles); + int dailyFuelProduced = (int) Math.floor((FUEL_PRODUCED + ForgeConditionChecker.getSynchrotronCoreBonus()) * centrifugingCycles); + int dailyHeavyMachineryBroken = machineryBroken; + + fleet.getCargo().removeCommodity(Commodities.VOLATILES, dailyVolatilesSpent); + fleet.getCargo().addCommodity(Commodities.FUEL, dailyFuelProduced); + if(willHeavyMachineryBreakdown) { + fleet.getCargo().removeCommodity(Commodities.HEAVY_MACHINERY, dailyHeavyMachineryBroken); + } + + totalFuelProduction += dailyFuelProduced; + if(willHeavyMachineryBreakdown) { + totalMachineryBreakage += dailyHeavyMachineryBroken; + } + + if (willHeavyMachineryBreakdown && dailyHeavyMachineryBroken > 0) { + breakdownReport = true; + } + + if (centrifugingCycles >= 1 && dailyFuelProduced > 0) { + fuelWasCentrifuged = true; + fuelCentrifugingReport = true; + } + + } + +} diff --git a/source/data/forge/abilities/conversion/ForgeManufacturingLogic.java b/source/data/forge/abilities/conversion/ForgeManufacturingLogic.java new file mode 100644 index 0000000..498a4d0 --- /dev/null +++ b/source/data/forge/abilities/conversion/ForgeManufacturingLogic.java @@ -0,0 +1,79 @@ +package data.forge.abilities.conversion; + +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.impl.campaign.ids.Commodities; +import data.forge.abilities.conversion.support.ForgeConversionGeneral; +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; + +import static data.forge.abilities.conversion.support.ForgeConversionVariables.*; +import static data.forge.plugins.ForgeSettings.*; + +public class ForgeManufacturingLogic { + + public static void startManufacturing(CampaignFleetAPI fleet) { + + if (ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.MANUFACTURE) <= 1) return; + if (!ForgeConditionChecker.hasMinimumMachinery()) return; + + if (ForgeConditionChecker.getPlayerCargo(Commodities.METALS) > METAL_TO_MANUFACTURE && + ForgeConditionChecker.getPlayerCargo(Commodities.RARE_METALS) > TRANSPLUTONICS_TO_MANUFACTURE) { + manufactureSupplies(fleet); + } + + if (suppliesWereManufactured) { + ForgeConversionGeneral.applyForgingCRMalus(fleet, ForgeTypes.MANUFACTURE); + } + + } + + public static void manufactureSupplies(CampaignFleetAPI fleet) { + + int machineryManufacturingCycles = ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.MANUFACTURING, ForgeTypes.MANUFACTURE); + + int manufacturingCapacity = ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.MANUFACTURE); + + int metalManufacturingCycles = (int) Math.floor(ForgeConditionChecker.getPlayerCargo(Commodities.METALS) / METAL_TO_MANUFACTURE); + int transplutonicsManufacturingCycles = (int) Math.floor(ForgeConditionChecker.getPlayerCargo(Commodities.RARE_METALS) / TRANSPLUTONICS_TO_MANUFACTURE); + + int manufacturingCycles = (int) Math.floor(Math.min(manufacturingCapacity, + Math.min(machineryManufacturingCycles, Math.min(metalManufacturingCycles, transplutonicsManufacturingCycles)))); + + boolean willHeavyMachineryBreakdown = ForgeConditionChecker.getMachineryBreakdownChance(); + int MachineryInManufacturing = (int) Math.ceil((manufacturingCycles * HEAVY_MACHINERY_MANUFACTURING_USAGE)); + int machineryBroken = 0; + + for (int machineryInCheck = 0; machineryInCheck < MachineryInManufacturing; machineryInCheck++) { + if (Math.random() < (BREAKDOWN_SEVERITY * ForgeConditionChecker.getForgingQuality())) + machineryBroken++; + } + + int dailyMetalSpent = (int) Math.ceil(METAL_TO_MANUFACTURE * manufacturingCycles); + int dailyTransplutonicsSpent = (int) Math.ceil(TRANSPLUTONICS_TO_MANUFACTURE * manufacturingCycles); + int dailySuppliesProduced = (int) Math.floor((SUPPLIES_PRODUCED + ForgeConditionChecker.getNanoforgeManufacturingBonus()) * manufacturingCycles); + int dailyHeavyMachineryBroken = machineryBroken; + + fleet.getCargo().removeCommodity(Commodities.METALS, dailyMetalSpent); + fleet.getCargo().removeCommodity(Commodities.RARE_METALS, dailyTransplutonicsSpent); + fleet.getCargo().addCommodity(Commodities.SUPPLIES, dailySuppliesProduced); + if (willHeavyMachineryBreakdown) { + fleet.getCargo().removeCommodity(Commodities.HEAVY_MACHINERY, dailyHeavyMachineryBroken); + } + + totalSuppliesProduction += dailySuppliesProduced; + if (willHeavyMachineryBreakdown) { + totalMachineryBreakage += dailyHeavyMachineryBroken; + } + + if (willHeavyMachineryBreakdown && dailyHeavyMachineryBroken > 0) { + breakdownReport = true; + } + + if (dailySuppliesProduced > 0) { + suppliesWereManufactured = true; + suppliesManufacturingReport = true; + } + + } + +} diff --git a/source/data/forge/abilities/conversion/ForgeRefiningLogic.java b/source/data/forge/abilities/conversion/ForgeRefiningLogic.java new file mode 100644 index 0000000..8b1972c --- /dev/null +++ b/source/data/forge/abilities/conversion/ForgeRefiningLogic.java @@ -0,0 +1,120 @@ +package data.forge.abilities.conversion; + +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.impl.campaign.ids.Commodities; + +import data.forge.abilities.conversion.support.ForgeConversionGeneral; +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; +import static data.forge.plugins.ForgeSettings.*; +import static data.forge.abilities.conversion.support.ForgeConversionVariables.*; + +public class ForgeRefiningLogic { + + public static void startRefining(CampaignFleetAPI fleet) { + + if (ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.REFINERY) <= 1) return; + if (!ForgeConditionChecker.hasMinimumMachinery()) return; + + if (ForgeConditionChecker.getPlayerCargo(Commodities.ORE) > ORE_TO_REFINE) { + refineOre(fleet); + } + + if (ForgeConditionChecker.getPlayerCargo(Commodities.RARE_ORE) > TRANSPLUTONIC_ORE_TO_REFINE) { + refineTransplutonicOre(fleet); + } + + if (oreWasRefined || transplutonicsWereRefined) { + ForgeConversionGeneral.applyForgingCRMalus(fleet, ForgeTypes.REFINERY); + } + + } + + public static void refineOre(CampaignFleetAPI fleet) { + + int machineryRefiningCycles = ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.REFINING, ForgeTypes.REFINERY); + + int refiningCapacity = ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.REFINERY); + + int oreRefiningCycles = (int) Math.floor(ForgeConditionChecker.getPlayerCargo(Commodities.ORE)/ ORE_TO_REFINE); + + int refiningCycles = (int) Math.floor(Math.min(refiningCapacity, Math.min(machineryRefiningCycles, oreRefiningCycles))); + + boolean willHeavyMachineryBreakdown = ForgeConditionChecker.getMachineryBreakdownChance(); + int MachineryInRefining = (int) Math.ceil((refiningCycles * HEAVY_MACHINERY_REFINING_USAGE)); + int machineryBroken = 0; + + for (int machineryInCheck = 0; machineryInCheck < MachineryInRefining; machineryInCheck++) { + if (Math.random()<(BREAKDOWN_SEVERITY * ForgeConditionChecker.getForgingQuality())) machineryBroken++; + } + + int dailyOreSpent = (int) Math.ceil(ORE_TO_REFINE * refiningCycles); + int dailyMetalProduced = (int) Math.floor((METAL_PRODUCED + ForgeConditionChecker.getCatalyticCoreBonus()) * refiningCycles); + int dailyHeavyMachineryBroken = machineryBroken; + + fleet.getCargo().removeCommodity(Commodities.ORE, dailyOreSpent); + fleet.getCargo().addCommodity(Commodities.METALS, dailyMetalProduced); + if(willHeavyMachineryBreakdown) { + fleet.getCargo().removeCommodity(Commodities.HEAVY_MACHINERY, dailyHeavyMachineryBroken); + } + + totalMetalProduction += dailyMetalProduced; + if(willHeavyMachineryBreakdown) { + totalMachineryBreakage += dailyHeavyMachineryBroken; + } + + if (willHeavyMachineryBreakdown && dailyHeavyMachineryBroken > 0) { + breakdownReport = true; + } + + if (dailyMetalProduced > 0) { + oreWasRefined = true; + oreRefiningReport = true; + } + + } + + public static void refineTransplutonicOre(CampaignFleetAPI fleet) { + + int machineryRefiningCycles = ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.REFINING, ForgeTypes.REFINERY); + + int refiningCapacity = ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.REFINERY); + + int transplutonicOreRefiningCycles = (int) Math.floor(ForgeConditionChecker.getPlayerCargo(Commodities.RARE_ORE) / TRANSPLUTONIC_ORE_TO_REFINE); + + int refiningCycles = (int) Math.floor(Math.min(refiningCapacity, Math.min(machineryRefiningCycles, transplutonicOreRefiningCycles))); + + boolean willHeavyMachineryBreakdown = ForgeConditionChecker.getMachineryBreakdownChance(); + int machineryInRefining = (int) Math.ceil((refiningCycles * HEAVY_MACHINERY_REFINING_USAGE)); + int machineryBroken = 0; + + for (int machineryInCheck = 0; machineryInCheck < machineryInRefining; machineryInCheck++) { + if (Math.random()<(BREAKDOWN_SEVERITY * ForgeConditionChecker.getForgingQuality())) machineryBroken++; + } + + int dailyTransplutonicOreSpent = (int) Math.ceil(TRANSPLUTONIC_ORE_TO_REFINE * refiningCycles); + int dailyTransplutonicsProduced = (int) Math.floor((TRANSPLUTONICS_PRODUCED + ForgeConditionChecker.getCatalyticCoreBonus()) * refiningCycles); + int dailyHeavyMachineryBroken = machineryBroken; + + fleet.getCargo().removeCommodity(Commodities.RARE_ORE, dailyTransplutonicOreSpent); + fleet.getCargo().addCommodity(Commodities.RARE_METALS, dailyTransplutonicsProduced); + if(willHeavyMachineryBreakdown) { + fleet.getCargo().removeCommodity(Commodities.HEAVY_MACHINERY, dailyHeavyMachineryBroken); + } + + totalTransplutonicsProduction += dailyTransplutonicsProduced; + if(willHeavyMachineryBreakdown) { + totalMachineryBreakage += dailyHeavyMachineryBroken; + } + + if (willHeavyMachineryBreakdown && dailyHeavyMachineryBroken > 0) { + breakdownReport = true; + } + + if (dailyTransplutonicsProduced > 0) { + transplutonicsWereRefined = true; + transplutonicsRefiningReport = true; + } + } + +} diff --git a/source/data/forge/abilities/conversion/support/ForgeConversionGeneral.java b/source/data/forge/abilities/conversion/support/ForgeConversionGeneral.java new file mode 100644 index 0000000..6b28e01 --- /dev/null +++ b/source/data/forge/abilities/conversion/support/ForgeConversionGeneral.java @@ -0,0 +1,87 @@ +package data.forge.abilities.conversion.support; + +import com.fs.starfarer.api.Global; +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.fleet.FleetMemberAPI; +import com.fs.starfarer.api.impl.campaign.ids.Commodities; +import data.forge.campaign.ForgeConditionChecker; +import data.forge.plugins.ForgeSettings; + +import static data.forge.hullmods.support.ForgeHullmodsGeneral.shipSizeEffect; +import static data.forge.plugins.ForgeSettings.CR_PRODUCTION_DECAY; + +public class ForgeConversionGeneral { + + public static int getForgingCapacityWithCR(CampaignFleetAPI fleet, String forgeType) { + if (Global.getSector().getPlayerFleet() == null) { return 0; } + int totalForgingCapacity = 0; + for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { + if (!ForgeConditionChecker.isOperational(member)) + continue; + int shipBaseCapacity = shipSizeEffect.get(member.getVariant().getHullSize()); + if (member.getVariant().hasHullMod(forgeType)) + totalForgingCapacity += (int) Math.floor(shipBaseCapacity * + (member.getRepairTracker().getCR() / 0.7f)); + + } + return totalForgingCapacity; + } + + public static void applyForgingCRMalus(CampaignFleetAPI fleet, String forgeType) { + for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { + if ((!ForgeConditionChecker.isOperational(member))) + continue; + if (member.getVariant().hasHullMod(forgeType)) + member.getRepairTracker().applyCREvent(-((CR_PRODUCTION_DECAY * ForgeConditionChecker.getForgingQuality()) * + (member.getRepairTracker().getCR() / 0.7f)), "Forged goods"); + } + } + + public static float getTotalFleetMachineryRequirement(CampaignFleetAPI fleet) { + + float requirementRefining = (ForgeSettings.HEAVY_MACHINERY_REFINING_USAGE * getForgingCapacityWithCR(fleet, ForgeTypes.REFINERY)); + float requirementCentrifuging = (ForgeSettings.HEAVY_MACHINERY_CENTRIFUGING_USAGE * getForgingCapacityWithCR(fleet, ForgeTypes.CENTRIFUGE)); + float requirementManufacturing = (ForgeSettings.HEAVY_MACHINERY_MANUFACTURING_USAGE * getForgingCapacityWithCR(fleet, ForgeTypes.MANUFACTURE)); + float requirementAssembling = (ForgeSettings.HEAVY_MACHINERY_ASSEMBLING_USAGE * getForgingCapacityWithCR(fleet, ForgeTypes.ASSEMBLY)); + + return (requirementRefining + requirementCentrifuging + requirementManufacturing + requirementAssembling); + } + + public static float getMachineryAvailability() { + + if (Global.getSector().getPlayerFleet() == null) { + return 0f; + } + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + int machineryInCargo = (int) Math.floor(ForgeConditionChecker.getPlayerCargo(Commodities.HEAVY_MACHINERY)); + float machineryRequiredTotal = getTotalFleetMachineryRequirement(fleet); + + float machineryAvailability = machineryInCargo / machineryRequiredTotal; + + if (machineryAvailability > 1f) { + machineryAvailability = 1f; + } + + return machineryAvailability; + } + + public static float getConversionMachineryUsageTotal(CampaignFleetAPI fleet, ForgeTypes.Conversion type, String forgeType) { + + float requirementForConversion = (ForgeTypes.MACHINERY_USAGE.get(type) * getForgingCapacityWithCR(fleet, forgeType)); + float machineryAvailableTotal = getTotalFleetMachineryRequirement(fleet) * getMachineryAvailability(); + float machineryAvailableForOtherConversions = machineryAvailableTotal - (requirementForConversion * getMachineryAvailability()); + + return machineryAvailableTotal - machineryAvailableForOtherConversions; + } + + public static int getMachineryUsageCycles(ForgeTypes.Conversion type, String forgeType) { + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + float machineryAvailable = ForgeConversionGeneral.getConversionMachineryUsageTotal(fleet, type, forgeType); + + return (int) Math.floor(machineryAvailable / ForgeTypes.MACHINERY_USAGE.get(type)); + } + +} diff --git a/source/data/forge/abilities/conversion/support/ForgeConversionVariables.java b/source/data/forge/abilities/conversion/support/ForgeConversionVariables.java new file mode 100644 index 0000000..eaf0bc0 --- /dev/null +++ b/source/data/forge/abilities/conversion/support/ForgeConversionVariables.java @@ -0,0 +1,57 @@ +package data.forge.abilities.conversion.support; + +public class ForgeConversionVariables { + + // Here: Production report variables + + public static float totalMetalProduction = 0f; + public static float totalTransplutonicsProduction = 0f; + public static float totalFuelProduction = 0f; + public static float totalSuppliesProduction = 0f; + public static float totalHeavyMachineryProduction = 0f; + + public static float totalMachineryBreakage = 0f; + + public static boolean breakdownReport = false; + + // Here: Daily floating/sound notification variables + + public static boolean oreWasRefined = false; + public static boolean transplutonicsWereRefined = false; + public static boolean fuelWasCentrifuged = false; + public static boolean suppliesWereManufactured = false; + public static boolean heavyMachineryWasAssembled = false; + + public static boolean goodsWereForged () { + return oreWasRefined || + transplutonicsWereRefined || + fuelWasCentrifuged || + suppliesWereManufactured || + heavyMachineryWasAssembled; + } + + public static void clearGoodsStatus () { + oreWasRefined = false; + transplutonicsWereRefined = false; + fuelWasCentrifuged = false; + suppliesWereManufactured = false; + heavyMachineryWasAssembled = false; + } + + // Here: Detailed intel report variables + + public static boolean oreRefiningReport = false; + public static boolean transplutonicsRefiningReport = false; + public static boolean fuelCentrifugingReport = false; + public static boolean suppliesManufacturingReport = false; + public static boolean heavyMachineryAssemblingReport = false; + + public static boolean goodsProducedReport () { + return oreRefiningReport || + transplutonicsRefiningReport || + fuelCentrifugingReport || + suppliesManufacturingReport || + heavyMachineryAssemblingReport; + } + +} diff --git a/source/data/forge/abilities/conversion/support/ForgeTypes.java b/source/data/forge/abilities/conversion/support/ForgeTypes.java new file mode 100644 index 0000000..b2117e2 --- /dev/null +++ b/source/data/forge/abilities/conversion/support/ForgeTypes.java @@ -0,0 +1,30 @@ +package data.forge.abilities.conversion.support; + +import data.forge.plugins.ForgeSettings; + +import java.util.HashMap; +import java.util.Map; + +public class ForgeTypes { + + public static final String REFINERY = "forge_refinery_module"; + public static final String CENTRIFUGE = "forge_centrifuge_module"; + public static final String MANUFACTURE = "forge_manufacture_module"; + public static final String ASSEMBLY = "forge_assembly_module"; + + public enum Conversion { + REFINING, + CENTRIFUGING, + MANUFACTURING, + ASSEMBLING, + } + + public static final Map MACHINERY_USAGE = new HashMap<>(); + static { + MACHINERY_USAGE.put(Conversion.REFINING, ForgeSettings.HEAVY_MACHINERY_REFINING_USAGE); + MACHINERY_USAGE.put(Conversion.CENTRIFUGING, ForgeSettings.HEAVY_MACHINERY_CENTRIFUGING_USAGE); + MACHINERY_USAGE.put(Conversion.MANUFACTURING, ForgeSettings.HEAVY_MACHINERY_MANUFACTURING_USAGE); + MACHINERY_USAGE.put(Conversion.ASSEMBLING, ForgeSettings.HEAVY_MACHINERY_ASSEMBLING_USAGE); + } + +} diff --git a/source/data/forge/abilities/tooltip/ForgeProductionTooltip.java b/source/data/forge/abilities/tooltip/ForgeProductionTooltip.java new file mode 100644 index 0000000..5df75d4 --- /dev/null +++ b/source/data/forge/abilities/tooltip/ForgeProductionTooltip.java @@ -0,0 +1,132 @@ +package data.forge.abilities.tooltip; + +import java.awt.*; +import java.util.List; +import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Collections; + +import com.fs.starfarer.api.Global; +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.fleet.FleetMemberAPI; +import com.fs.starfarer.api.impl.campaign.ids.Commodities; +import com.fs.starfarer.api.impl.campaign.ids.Items; +import com.fs.starfarer.api.ui.Alignment; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.util.Misc; + +import data.forge.campaign.ForgeConditionChecker; + +import static data.forge.campaign.ForgeConditionChecker.*; + +public class ForgeProductionTooltip { + + public static void addCannotForgeNoActiveShips(TooltipMakerAPI tooltip) { + float pad = 6f; + Color negativeHighlight = Misc.getNegativeHighlightColor(); + tooltip.addPara("Your fleet currently cannot conduct forging operations: no active forge ships.", negativeHighlight, pad); + } + + public static void addCannotForgeNoMachinery(TooltipMakerAPI tooltip) { + float pad = 6f; + Color negativeHighlight = Misc.getNegativeHighlightColor(); + if (ForgeConditionChecker.getPlayerCargo(Commodities.HEAVY_MACHINERY) > 0) { + tooltip.addPara("Production halted: insufficient machinery.", negativeHighlight, pad); + } else { + tooltip.addPara("Your fleet currently cannot conduct forging operations: no machinery available.", negativeHighlight, pad); + } + } + + public static void addExpandedInfo(TooltipMakerAPI tooltip, boolean isActive) { + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + if (hasActiveForgeShips()) { + ForgeTooltipShips.addOperationalShipsBreakdown(tooltip); + } + + if (hasInactiveForgeShips()) { + ForgeTooltipShips.addInactiveShipsBreakdown(tooltip); + } + + if (ForgeConditionChecker.hasAnyForgingCapacity(fleet) && ForgeConditionChecker.getPlayerCargo(Commodities.HEAVY_MACHINERY) >= 1) { + ForgeTooltipBreakdown.addProductionBreakdown(tooltip, isActive); + } + + if (ForgeConditionChecker.hasAnySpecialItem()) { + ForgeProductionTooltip.addSpecialItemsBreakdown(tooltip); + } + + } + + public static void addSpecialItemsBreakdown(TooltipMakerAPI tooltip) { + + float pad = 10f; + tooltip.addSectionHeading("Special effects", Alignment.MID, pad); + tooltip.addSpacer(4f); + + if (ForgeConditionChecker.hasSpecialItem(Items.CORRUPTED_NANOFORGE)) { + ForgeTooltipItems.addCorruptedNanoforgeNote(tooltip); + } + + if (ForgeConditionChecker.hasSpecialItem(Items.PRISTINE_NANOFORGE)) { + ForgeTooltipItems.addPristineNanoforgeNote(tooltip); + } + + if (ForgeConditionChecker.hasSpecialItem(Items.CATALYTIC_CORE)) { + ForgeTooltipItems.addCatalyticCoreNote(tooltip); + } + + if (ForgeConditionChecker.hasSpecialItem(Items.SYNCHROTRON)) { + ForgeTooltipItems.addSynchrotronCoreNote(tooltip); + } + + } + + protected static List getOperationalForgeShipsList() { + return getForgeShipsWithActivity(true); + } + + protected static List getInactiveForgeShipsList() { + return getForgeShipsWithActivity(false); + } + + protected static List getForgeShipsWithActivity(boolean isActiveCheck) { + List forgeHullMods = new ArrayList<>();{ + forgeHullMods.add("forge_refinery_module"); + forgeHullMods.add("forge_centrifuge_module"); + forgeHullMods.add("forge_manufacture_module"); + forgeHullMods.add("forge_assembly_module");} + List membersWithHullMod = new LinkedList<>(); + CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); + for (FleetMemberAPI forgeShip : playerFleet.getFleetData().getMembersListCopy()) { + if (isActiveCheck ^ isOperational(forgeShip)) { + continue;} + if (!Collections.disjoint(forgeShip.getVariant().getHullMods(), forgeHullMods)) + membersWithHullMod.add(forgeShip); + } + return membersWithHullMod; + } + + protected static String getForgeHullmodOfForgeShip(FleetMemberAPI member) { + + if (member.getVariant().hasHullMod("forge_refinery_module")) { + return "Refinery"; + } + + if (member.getVariant().hasHullMod("forge_centrifuge_module")) { + return "Centrifuge"; + } + + if (member.getVariant().hasHullMod("forge_manufacture_module")) { + return "Manufacture"; + } + + if (member.getVariant().hasHullMod("forge_assembly_module")) { + return "Assembly"; + } + + return null; + } + +} diff --git a/source/data/forge/abilities/tooltip/ForgeTooltipBreakdown.java b/source/data/forge/abilities/tooltip/ForgeTooltipBreakdown.java new file mode 100644 index 0000000..bf3828e --- /dev/null +++ b/source/data/forge/abilities/tooltip/ForgeTooltipBreakdown.java @@ -0,0 +1,296 @@ +package data.forge.abilities.tooltip; + +import com.fs.starfarer.api.Global; +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.impl.campaign.ids.Commodities; +import com.fs.starfarer.api.ui.Alignment; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.util.Misc; +import data.forge.abilities.conversion.support.ForgeConversionGeneral; +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; + +import java.awt.*; + +import static data.forge.abilities.conversion.support.ForgeConversionGeneral.getForgingCapacityWithCR; +import static data.forge.abilities.conversion.support.ForgeTypes.*; +import static data.forge.plugins.ForgeSettings.*; + +public class ForgeTooltipBreakdown { + + public static void addProductionBreakdown(TooltipMakerAPI tooltip, boolean isActive) { + + float pad = 10f; + + tooltip.addSectionHeading("Production details", Alignment.MID, pad); + tooltip.addSpacer(3f); + + if (ForgeConditionChecker.hasMinimumMachinery()) { + addMachineryRequirementLine(tooltip, isActive); + + tooltip.addSpacer(3f); + + addProductionValues(tooltip, isActive); + } + + if (!ForgeConditionChecker.hasMinimumMachinery()) { + addInsufficientMachineryLine(tooltip); + } + + } + + private static void addProductionValues(TooltipMakerAPI tooltip, boolean isActive) { + + Color textColor = Misc.getTextColor(); + Color highlightColor = Misc.getHighlightColor(); + Color negativeHighlight = Misc.getNegativeHighlightColor(); + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + float pad = 10f; + + int gridSize = 0; + float gridWidth = 185f; + tooltip.beginGridFlipped(gridWidth, 2, 45f, pad); + + String tanksAreFullFormat = " Halted: tanks full"; + if (!isActive) { + tanksAreFullFormat = " Standby: tanks full"; + } + + String noInputsFormat = " Halted: no inputs"; + if (!isActive) { + noInputsFormat = " Standby: no inputs"; + } + String lowInputsFormat = " Working: low inputs"; + if (!isActive) { + lowInputsFormat = "Standby: low inputs"; + } + String workingFormat = " Working"; + if (!isActive) { + workingFormat = " Standby"; + } + + if (getForgingCapacityWithCR(fleet, REFINERY) >= 1) { + + int machineryAvailable = (int) Math.floor(ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.REFINING, ForgeTypes.REFINERY)); + + int maxMetalProductionCapacity = ((int) Math.floor(Math.min(getForgingCapacityWithCR(fleet, REFINERY), machineryAvailable) * + (METAL_PRODUCED + ForgeConditionChecker.getCatalyticCoreBonus()))); + String metalLabel = "Metal / day"; + + tooltip.addToGrid(0, gridSize++, metalLabel, "+" + Misc.getWithDGS(maxMetalProductionCapacity), highlightColor); + + boolean noOre = ForgeConditionChecker.getPlayerCargo(Commodities.ORE) < ORE_TO_REFINE; + + if (noOre) { + tooltip.setGridLabelColor(negativeHighlight); + tooltip.addToGrid(1, 0, noInputsFormat, "", negativeHighlight); + } else { + tooltip.setGridLabelColor(highlightColor); + tooltip.addToGrid(1, 0, workingFormat, "", highlightColor); + } + tooltip.setGridLabelColor(textColor); + + int maxTransplutonicsProductionCapacity = (int) Math.floor(Math.min(getForgingCapacityWithCR(fleet, REFINERY), machineryAvailable) * + (TRANSPLUTONICS_PRODUCED + ForgeConditionChecker.getCatalyticCoreBonus())); + String transplutonicsLabel = "Transplutonics / day"; + + tooltip.addToGrid(0, gridSize++, transplutonicsLabel, "+" + Misc.getWithDGS(maxTransplutonicsProductionCapacity), highlightColor); + + boolean noTransplutonicOre = ForgeConditionChecker.getPlayerCargo(Commodities.RARE_ORE) < TRANSPLUTONIC_ORE_TO_REFINE; + + if (noTransplutonicOre) { + tooltip.setGridLabelColor(negativeHighlight); + tooltip.addToGrid(1, gridSize - 1, noInputsFormat, "", negativeHighlight); + } else { + tooltip.setGridLabelColor(highlightColor); + tooltip.addToGrid(1, gridSize - 1, workingFormat, "", highlightColor); + } + tooltip.setGridLabelColor(textColor); + + } + + if (getForgingCapacityWithCR(fleet, CENTRIFUGE) >= 1) { + + int machineryAvailable = (int) Math.floor(ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.CENTRIFUGING, ForgeTypes.CENTRIFUGE)); + + int maxFuelProductionCapacity = (int) Math.floor(Math.min(getForgingCapacityWithCR(fleet, CENTRIFUGE), machineryAvailable) * + (FUEL_PRODUCED + ForgeConditionChecker.getSynchrotronCoreBonus())); + + String fuelLabel = "Fuel / day"; + + tooltip.addToGrid(0, gridSize++, fuelLabel, "+" + Misc.getWithDGS(maxFuelProductionCapacity), highlightColor); + + if (ForgeConditionChecker.areFuelTanksFull(fleet)) { + tooltip.setGridLabelColor(negativeHighlight); + tooltip.addToGrid(1, gridSize - 1, tanksAreFullFormat, "", negativeHighlight); + tooltip.setGridLabelColor(textColor); + } else if (ForgeConditionChecker.getPlayerCargo(Commodities.VOLATILES) < VOLATILES_TO_CENTRIFUGE) { + tooltip.setGridLabelColor(negativeHighlight); + tooltip.addToGrid(1, gridSize - 1, noInputsFormat, "", negativeHighlight); + tooltip.setGridLabelColor(textColor); + } else { + tooltip.setGridLabelColor(highlightColor); + tooltip.addToGrid(1, gridSize - 1, workingFormat, "", highlightColor); + tooltip.setGridLabelColor(textColor); + } + + } + + if (getForgingCapacityWithCR(fleet, MANUFACTURE) >= 1) { + + int machineryAvailable = (int) Math.floor(ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.MANUFACTURING, ForgeTypes.MANUFACTURE)); + + int maxSuppliesProductionCapacity = (int) Math.floor(Math.min(getForgingCapacityWithCR(fleet, MANUFACTURE), machineryAvailable) * + (SUPPLIES_PRODUCED + ForgeConditionChecker.getNanoforgeManufacturingBonus())); + String suppliesLabel = "Supplies / day"; + + tooltip.addToGrid(0, gridSize++, suppliesLabel, "+" + Misc.getWithDGS(maxSuppliesProductionCapacity), highlightColor); + + boolean noMetal = ForgeConditionChecker.getPlayerCargo(Commodities.METALS) < METAL_TO_MANUFACTURE; + boolean noTransplutonics = ForgeConditionChecker.getPlayerCargo(Commodities.RARE_METALS) < TRANSPLUTONICS_TO_MANUFACTURE; + boolean noInputs = noMetal || noTransplutonics; + + boolean hasRefining = getForgingCapacityWithCR(fleet, REFINERY) >= 1; + boolean hasOre = ForgeConditionChecker.getPlayerCargo(Commodities.ORE) > ORE_TO_REFINE; + boolean hasTransplutonicOre = ForgeConditionChecker.getPlayerCargo(Commodities.RARE_ORE) > TRANSPLUTONIC_ORE_TO_REFINE; + boolean hasRawOres = hasOre && hasTransplutonicOre; + boolean hasOresAndRefining = hasRefining && hasRawOres; + boolean refiningOreOperational = hasRefining && hasOre; + boolean refiningTransplutonicsOperational = hasRefining && hasTransplutonicOre; + boolean hasOneRawAndOneProcessed = (!noMetal && refiningTransplutonicsOperational) + || (!noTransplutonics && refiningOreOperational); + + if (noInputs && !hasOresAndRefining && !hasOneRawAndOneProcessed) { + tooltip.setGridLabelColor(negativeHighlight); + tooltip.addToGrid(1, gridSize - 1, noInputsFormat, "", negativeHighlight); + } else if (noInputs || hasOneRawAndOneProcessed) { + tooltip.setGridLabelColor(highlightColor); + tooltip.addToGrid(1, gridSize - 1, lowInputsFormat, "", highlightColor); + } else { + tooltip.setGridLabelColor(highlightColor); + tooltip.addToGrid(1, gridSize - 1, workingFormat, "", highlightColor); + } + tooltip.setGridLabelColor(textColor); + + } + + if (getForgingCapacityWithCR(fleet, ASSEMBLY) > 0) { + + int machineryAvailable = (int) Math.floor(ForgeConversionGeneral.getMachineryUsageCycles(ForgeTypes.Conversion.ASSEMBLING, ForgeTypes.ASSEMBLY)); + + int maxHeavyMachineryProductionCapacity = (int) Math.floor(Math.min(getForgingCapacityWithCR(fleet, ASSEMBLY), machineryAvailable) * + (HEAVY_MACHINERY_PRODUCED + ForgeConditionChecker.getNanoforgeAssemblingBonus())); + String machineryLabel = "Machinery / day"; + + tooltip.addToGrid(0, gridSize++, machineryLabel, "+" + Misc.getWithDGS(maxHeavyMachineryProductionCapacity), highlightColor); + + boolean noMetal = ForgeConditionChecker.getPlayerCargo(Commodities.METALS) < METAL_TO_ASSEMBLE; + boolean noTransplutonics = ForgeConditionChecker.getPlayerCargo(Commodities.RARE_METALS) < TRANSPLUTONICS_TO_ASSEMBLE; + boolean noInputs = noMetal || noTransplutonics; + + boolean hasRefining = getForgingCapacityWithCR(fleet, REFINERY) >= 1; + boolean hasOre = ForgeConditionChecker.getPlayerCargo(Commodities.ORE) > ORE_TO_REFINE; + boolean hasTransplutonicOre = ForgeConditionChecker.getPlayerCargo(Commodities.RARE_ORE) > TRANSPLUTONIC_ORE_TO_REFINE; + boolean hasRawOres = hasOre && hasTransplutonicOre; + boolean hasOresAndRefining = hasRefining && hasRawOres; + boolean refiningOreOperational = hasRefining && hasOre; + boolean refiningTransplutonicsOperational = hasRefining && hasTransplutonicOre; + boolean hasOneRawAndOneProcessed = (!noMetal && refiningTransplutonicsOperational) + || (!noTransplutonics && refiningOreOperational); + + if (noInputs && !hasOresAndRefining && !hasOneRawAndOneProcessed) { + tooltip.setGridLabelColor(negativeHighlight); + tooltip.addToGrid(1, gridSize - 1, noInputsFormat, "", negativeHighlight); + } else if (noInputs || hasOneRawAndOneProcessed) { + tooltip.setGridLabelColor(highlightColor); + tooltip.addToGrid(1, gridSize - 1, lowInputsFormat, "", highlightColor); + } else { + tooltip.setGridLabelColor(highlightColor); + tooltip.addToGrid(1, gridSize - 1, workingFormat, "", highlightColor); + } + tooltip.setGridLabelColor(textColor); + + } + + tooltip.addGrid(0); + + } + + public static void addMachineryRequirementLine(TooltipMakerAPI tooltip, boolean isActive) { + + Color highlightColor = Misc.getHighlightColor(); + Color negativeHighlight = Misc.getNegativeHighlightColor(); + + float pad = 5f; + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + Color [] positiveHighlights = {highlightColor}; + Color [] negativeHighlights = {negativeHighlight, highlightColor, highlightColor}; + + int maxMachineryNeededFinal = (int) Math.ceil(ForgeConversionGeneral.getTotalFleetMachineryRequirement(fleet)); + int heavyMachineryAvailable = (int) fleet.getCargo().getCommodityQuantity(Commodities.HEAVY_MACHINERY); + + int machineryPercentage = (int) (ForgeConversionGeneral.getMachineryAvailability() * 100f); + String machineryUsagePercentage = machineryPercentage + "%"; + + String machineryNeeded = Misc.getWithDGS(maxMachineryNeededFinal); + String machineryAvailable = Misc.getWithDGS(heavyMachineryAvailable); + + String usingOrCanUse = "Using"; + if (!isActive) { + usingOrCanUse = "Can use"; + } + + String demandMetFormat = " all %s needed heavy machinery:"; + String demandNotMetFormat = " %s (out of %s needed, %s ratio) machinery:"; + + tooltip.setBulletedListMode(" "); + + if (heavyMachineryAvailable >= maxMachineryNeededFinal ) { + tooltip.addPara(usingOrCanUse + demandMetFormat, pad, positiveHighlights, machineryNeeded); + } + else { + tooltip.addPara(usingOrCanUse + demandNotMetFormat, pad, negativeHighlights, machineryAvailable, machineryNeeded, machineryUsagePercentage); + } + + tooltip.setBulletedListMode(null); + + } + + public static void addInsufficientMachineryLine(TooltipMakerAPI tooltip) { + + Color highlightColor = Misc.getHighlightColor(); + Color negativeHighlight = Misc.getNegativeHighlightColor(); + + float pad = 5f; + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + Color [] tooFewMachineryHighlights = {negativeHighlight, negativeHighlight, highlightColor, negativeHighlight}; + + int maxMachineryNeededFinal = (int) Math.ceil(ForgeConversionGeneral.getTotalFleetMachineryRequirement(fleet)); + int heavyMachineryAvailable = (int) fleet.getCargo().getCommodityQuantity(Commodities.HEAVY_MACHINERY); + + int machineryPercentage = (int) (ForgeConversionGeneral.getMachineryAvailability() * 100f); + String machineryUsagePercentage = machineryPercentage + "%"; + + String machineryNeeded = Misc.getWithDGS(maxMachineryNeededFinal); + String machineryAvailable = Misc.getWithDGS(heavyMachineryAvailable); + String tooFewMachinery = "Production halted:"; + + String tooFewMachineryFormat = "%s only %s (out of %s needed, %s ratio) machinery is available."; + + tooltip.setBulletedListMode(" "); + + if (ForgeConversionGeneral.getMachineryAvailability() > 0 && ForgeConditionChecker.getPlayerCargo(Commodities.HEAVY_MACHINERY) >= 1 ) { + tooltip.addPara(tooFewMachineryFormat, pad, tooFewMachineryHighlights, tooFewMachinery, machineryAvailable, machineryNeeded, machineryUsagePercentage); + } + + tooltip.setBulletedListMode(null); + + } + +} diff --git a/source/data/forge/abilities/tooltip/ForgeTooltipItems.java b/source/data/forge/abilities/tooltip/ForgeTooltipItems.java new file mode 100644 index 0000000..de1d749 --- /dev/null +++ b/source/data/forge/abilities/tooltip/ForgeTooltipItems.java @@ -0,0 +1,138 @@ +package data.forge.abilities.tooltip; + +import com.fs.starfarer.api.Global; +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.util.Misc; + +import java.awt.*; + +import static data.forge.abilities.conversion.support.ForgeConversionGeneral.getForgingCapacityWithCR; +import static data.forge.abilities.conversion.support.ForgeTypes.*; +import static data.forge.abilities.conversion.support.ForgeTypes.CENTRIFUGE; +import static data.forge.plugins.ForgeSettings.*; +import static data.forge.plugins.ForgeSettings.CATALYTIC_CORE_REFINING_BONUS; + +public class ForgeTooltipItems { + + public static void addCorruptedNanoforgeNote(TooltipMakerAPI tooltip) { + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + Color textColor = Misc.getTextColor(); + Color highlightColor = Misc.getHighlightColor(); + float pad = 5f; + + int manufacturingCapacity = (int) Math.ceil(getForgingCapacityWithCR(fleet, MANUFACTURE)); + int assemblingCapacity = (int) Math.ceil(getForgingCapacityWithCR(fleet, ASSEMBLY)); + String manufacturingBonus = Misc.getWithDGS((int) Math.ceil(CORRUPTED_NANOFORGE_MANUFACTURING_BONUS * manufacturingCapacity)); + String assemblingBonus = Misc.getWithDGS((int) Math.ceil(CORRUPTED_NANOFORGE_ASSEMBLING_BONUS * assemblingCapacity)); + String qualityBonus = ((int)(100-(CORRUPTED_NANOFORGE_QUALITY_BONUS*100)) + "%"); + String nanoforge = "Corrupted Nanoforge"; + String formatNoCapacity = "%s increases output of supplies, increases output of heavy machinery, and reduces " + + "breakdowns and CR loss by %s."; + String formatManufacturingCapacity = "%s increases output of supplies by %s, increases output of heavy machinery, and reduces " + + "breakdowns and CR loss by %s."; + String formatAssemblingCapacity = "%s increases output of supplies, increases output of heavy machinery by %s, and reduces " + + "breakdowns and CR loss by %s."; + String formatFullCapacity = "%s increases output of supplies by %s, increases output of heavy machinery by %s, and reduces " + + "breakdowns and CR loss by %s."; + + if (manufacturingCapacity >= 1 && assemblingCapacity < 1) { + tooltip.addPara(formatManufacturingCapacity, pad, textColor, highlightColor, + nanoforge, manufacturingBonus, qualityBonus);} + if (assemblingCapacity >= 1 && manufacturingCapacity < 1) { + tooltip.addPara(formatAssemblingCapacity, pad, textColor, highlightColor, + nanoforge, assemblingBonus, qualityBonus);} + if (manufacturingCapacity >= 1 && assemblingCapacity >= 1) { + tooltip.addPara(formatFullCapacity, pad, textColor, highlightColor, + nanoforge, manufacturingBonus, assemblingBonus, qualityBonus);} + if (manufacturingCapacity < 1 && assemblingCapacity < 1) { + tooltip.addPara(formatNoCapacity, pad, textColor, highlightColor, + nanoforge, qualityBonus);} + + } + + public static void addPristineNanoforgeNote(TooltipMakerAPI tooltip) { + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + Color textColor = Misc.getTextColor(); + Color highlightColor = Misc.getHighlightColor(); + float pad = 5f; + + int manufacturingCapacity = (int) Math.ceil(getForgingCapacityWithCR(fleet, MANUFACTURE)); + int assemblingCapacity = (int) Math.ceil(getForgingCapacityWithCR(fleet, ASSEMBLY)); + String manufacturingBonus = Misc.getWithDGS((int) Math.ceil(PRISTINE_NANOFORGE_MANUFACTURING_BONUS * manufacturingCapacity)); + String assemblingBonus = Misc.getWithDGS((int) Math.ceil(PRISTINE_NANOFORGE_ASSEMBLING_BONUS * assemblingCapacity)); + String qualityBonus = ((int)(100-(PRISTINE_NANOFORGE_QUALITY_BONUS*100)) + "%"); + String nanoforge = "Pristine Nanoforge"; + String formatNoCapacity = "%s increases output of supplies, increases output of heavy machinery, and reduces " + + "breakdowns and CR loss by %s."; + String formatManufacturingCapacity = "%s increases output of supplies by %s, increases output of heavy machinery, and reduces " + + "breakdowns and CR loss by %s."; + String formatAssemblingCapacity = "%s increases output of supplies, increases output of heavy machinery by %s, and reduces " + + "breakdowns and CR loss by %s."; + String formatFullCapacity = "%s increases output of supplies by %s, increases output of heavy machinery by %s, and reduces " + + "breakdowns and CR loss by %s."; + + if (manufacturingCapacity >= 1 && assemblingCapacity < 1) { + tooltip.addPara(formatManufacturingCapacity, pad, textColor, highlightColor, + nanoforge, manufacturingBonus, qualityBonus);} + if (assemblingCapacity >= 1 && manufacturingCapacity < 1) { + tooltip.addPara(formatAssemblingCapacity, pad, textColor, highlightColor, + nanoforge, assemblingBonus, qualityBonus);} + if (manufacturingCapacity >= 1 && assemblingCapacity >= 1) { + tooltip.addPara(formatFullCapacity, pad, textColor, highlightColor, + nanoforge, manufacturingBonus, assemblingBonus, qualityBonus);} + if (manufacturingCapacity < 1 && assemblingCapacity < 1) { + tooltip.addPara(formatNoCapacity, pad, textColor, highlightColor, + nanoforge, qualityBonus);} + + } + + public static void addCatalyticCoreNote(TooltipMakerAPI tooltip) { + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + Color textColor = Misc.getTextColor(); + Color highlightColor = Misc.getHighlightColor(); + float pad = 5f; + + int fleetCapacity = (int) Math.ceil(getForgingCapacityWithCR(fleet, REFINERY)); + String refiningBonus = Misc.getWithDGS((int) Math.ceil(CATALYTIC_CORE_REFINING_BONUS * fleetCapacity)); + String specialItem = "Catalytic Core"; + + String formatNoCapacity = specialItem + " increases output of metals and transplutonics."; + String formatCapacity = specialItem + " increases output of metals and transplutonics by " + refiningBonus + "."; + + if (fleetCapacity > 0 ) { + tooltip.addPara(formatCapacity, pad, textColor, highlightColor, specialItem, refiningBonus);} + else { + tooltip.addPara(formatNoCapacity, pad, textColor, highlightColor, specialItem);} + + } + + public static void addSynchrotronCoreNote(TooltipMakerAPI tooltip) { + + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + + Color textColor = Misc.getTextColor(); + Color highlightColor = Misc.getHighlightColor(); + float pad = 5f; + + int fleetCapacity = (int) Math.ceil(getForgingCapacityWithCR(fleet, CENTRIFUGE)); + String centrifugingBonus = Misc.getWithDGS((int) Math.ceil(CATALYTIC_CORE_REFINING_BONUS * fleetCapacity)); + String specialItem = "Synchrotron Core"; + + String formatNoCapacity = specialItem + " increases output of fuel."; + String formatCapacity = specialItem + " increases output of fuel by " + centrifugingBonus + "."; + + if (fleetCapacity > 0 ) { + tooltip.addPara(formatCapacity, pad, textColor, highlightColor, specialItem, centrifugingBonus);} + else { + tooltip.addPara(formatNoCapacity, pad, textColor, highlightColor, specialItem);} + + } + +} diff --git a/source/data/forge/abilities/tooltip/ForgeTooltipShips.java b/source/data/forge/abilities/tooltip/ForgeTooltipShips.java new file mode 100644 index 0000000..121322b --- /dev/null +++ b/source/data/forge/abilities/tooltip/ForgeTooltipShips.java @@ -0,0 +1,115 @@ +package data.forge.abilities.tooltip; + +import data.forge.abilities.ForgeProduction; +import data.forge.plugins.ForgeSettings; + +import com.fs.starfarer.api.fleet.FleetMemberAPI; +import com.fs.starfarer.api.ui.Alignment; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.util.Misc; + +import java.awt.*; +import java.util.List; + +import static data.forge.plugins.ForgeSettings.SHIP_LIST_SIZE; + +public class ForgeTooltipShips { + public static void addOperationalShipsBreakdown(TooltipMakerAPI tooltip) { + + Color textColor = Misc.getTextColor(); + Color highlightColor = Misc.getHighlightColor(); + + String indent = " "; + float pad = 10f; + + tooltip.addSectionHeading("Operational ships", Alignment.MID, pad); + tooltip.addSpacer(6f); + + java.util.List forgeShips = ForgeProductionTooltip.getOperationalForgeShipsList(); + + int counter = 1; + int rowNumber = 0; + int displayThatMany = SHIP_LIST_SIZE; + int shipsOverTooltipCap = forgeShips.size() - displayThatMany; + + tooltip.beginGrid(380f, 1, textColor); + + for (FleetMemberAPI member : forgeShips) { + + if (counter > displayThatMany) { + break; + } + + String shipName = member.getShipName(); + String shipNameShort = tooltip.shortenString(shipName, 175); + String shipClass = member.getHullSpec().getHullNameWithDashClass(); + String forgeModule = ForgeProductionTooltip.getForgeHullmodOfForgeShip(member); + + String label = indent + shipNameShort + ", " + shipClass; + + tooltip.addToGrid(0, rowNumber++, label, forgeModule, highlightColor); + + counter++; + + } + + tooltip.addGrid(1); + + if ((counter > displayThatMany) && !(shipsOverTooltipCap == 0)) { + String shipsOverCapString = String.valueOf(shipsOverTooltipCap); + String format = " ...and %s more."; + tooltip.addPara(format, 3, textColor, highlightColor, shipsOverCapString); + } + + } + + + public static void addInactiveShipsBreakdown(TooltipMakerAPI tooltip) { + + Color negativeHighlightColor = Misc.getNegativeHighlightColor(); + Color grayColor = Misc.getGrayColor(); + Color highlightColor = Misc.getHighlightColor(); + + String indent = " "; + float pad = 10f; + + tooltip.addSectionHeading("Inactive ships", Alignment.MID, pad); + tooltip.addSpacer(6f); + + List forgeShips = ForgeProductionTooltip.getInactiveForgeShipsList(); + + int counter = 1; + int rowNumber = 0; + int displayThatMany = SHIP_LIST_SIZE; + int shipsOverTooltipCap = forgeShips.size() - displayThatMany; + + tooltip.beginGrid(380f, 1, grayColor); + + for (FleetMemberAPI member : forgeShips) { + + if (counter > displayThatMany) { + break; + } + + String shipName = member.getShipName(); + String shipNameShort = tooltip.shortenString(shipName, 175); + String shipClass = member.getHullSpec().getHullNameWithDashClass(); + String forgeModule = ForgeProductionTooltip.getForgeHullmodOfForgeShip(member); + + String label = indent + shipNameShort + ", " + shipClass; + + tooltip.addToGrid(0, rowNumber++, label, forgeModule, negativeHighlightColor); + counter++; + + } + + tooltip.addGrid(3); + + if ((counter > displayThatMany) && !(shipsOverTooltipCap == 0)) { + String shipsOverCapString = String.valueOf(shipsOverTooltipCap); + String format = " ...and %s more."; + tooltip.addPara(format, 3, grayColor, highlightColor, shipsOverCapString); + } + } + +} diff --git a/source/data/forge/campaign/ForgeConditionChecker.java b/source/data/forge/campaign/ForgeConditionChecker.java new file mode 100644 index 0000000..18dc288 --- /dev/null +++ b/source/data/forge/campaign/ForgeConditionChecker.java @@ -0,0 +1,194 @@ +package data.forge.campaign; + +import java.util.List; +import java.util.ArrayList; + +import com.fs.starfarer.api.Global; +import com.fs.starfarer.api.campaign.CampaignFleetAPI; +import com.fs.starfarer.api.campaign.CargoAPI; +import com.fs.starfarer.api.campaign.SpecialItemData; +import com.fs.starfarer.api.combat.ShipAPI; +import com.fs.starfarer.api.fleet.FleetMemberAPI; +import com.fs.starfarer.api.impl.campaign.ids.Items; + +import data.forge.abilities.conversion.support.ForgeConversionGeneral; +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.hullmods.support.ForgeHullmodsGeneral; +import data.forge.plugins.ForgeSettings; + +import static data.forge.plugins.ForgeSettings.BASE_BREAKDOWN_CHANCE; +import static data.forge.plugins.ForgeSettings.FUEL_PRODUCED; + +public class ForgeConditionChecker { + + public static boolean getMachineryBreakdownChance() { + return (Math.random()<(BASE_BREAKDOWN_CHANCE * ForgeConditionChecker.getForgingQuality())); + } + + public static boolean areFuelTanksFull(CampaignFleetAPI fleet) { + return fleet.getCargo().getFreeFuelSpace() < (FUEL_PRODUCED + ForgeConditionChecker.getSynchrotronCoreBonus()); + } + + public static boolean hasMinimumMachinery() { + return ForgeConversionGeneral.getMachineryAvailability() >= 0.1f; + } + + public static int getPlayerCargo(String commodity) { + if (Global.getSector().getPlayerFleet() == null) { + return 0; + } + CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); + return Math.round(fleet.getCargo().getCommodityQuantity(commodity)); + } + + public static boolean hasSpecialItem(String specialItemType) { + SpecialItemData specialItem = new SpecialItemData(specialItemType, null); + CargoAPI cargo = Global.getSector().getPlayerFleet().getCargo(); + int specialItemQuantity = (int) Math.ceil(cargo.getQuantity(CargoAPI.CargoItemType.SPECIAL, specialItem)); + return specialItemQuantity > 0; + } + + public static boolean hasAnySpecialItem() { + List specialItems = new ArrayList<>(); + { + specialItems.add(Items.CORRUPTED_NANOFORGE); + specialItems.add(Items.PRISTINE_NANOFORGE); + specialItems.add(Items.CATALYTIC_CORE); + specialItems.add(Items.SYNCHROTRON); + } + for(String specialItem : specialItems) { + if(hasSpecialItem(specialItem)) { + return true; + } + } + return false; + } + + public static float getForgingQuality() { + if (hasSpecialItem(Items.PRISTINE_NANOFORGE)) { + return ForgeSettings.PRISTINE_NANOFORGE_QUALITY_BONUS; + } + if (hasSpecialItem(Items.CORRUPTED_NANOFORGE)) { + return ForgeSettings.CORRUPTED_NANOFORGE_QUALITY_BONUS; + } + return 1f; + } + + public static float getCatalyticCoreBonus() { + if (hasSpecialItem(Items.CATALYTIC_CORE)) { + return ForgeSettings.CATALYTIC_CORE_REFINING_BONUS; + } + return 0f; + } + + public static float getSynchrotronCoreBonus() { + if (hasSpecialItem(Items.SYNCHROTRON)) { + return ForgeSettings.SYNCHROTRON_CORE_CENTRIFUGING_BONUS; + } + return 0f; + } + + public static float getNanoforgeManufacturingBonus() { + if (hasSpecialItem(Items.PRISTINE_NANOFORGE)) { + return ForgeSettings.PRISTINE_NANOFORGE_MANUFACTURING_BONUS; + } + if (hasSpecialItem(Items.CORRUPTED_NANOFORGE)) { + return ForgeSettings.CORRUPTED_NANOFORGE_MANUFACTURING_BONUS; + } + return 0f; + } + + public static float getNanoforgeAssemblingBonus() { + if (hasSpecialItem(Items.PRISTINE_NANOFORGE)) { + return ForgeSettings.PRISTINE_NANOFORGE_ASSEMBLING_BONUS; + } + if (hasSpecialItem(Items.CORRUPTED_NANOFORGE)) { + return ForgeSettings.CORRUPTED_NANOFORGE_ASSEMBLING_BONUS; + } + return 0f; + } + + public static boolean hasAnyForgingCapacity (CampaignFleetAPI fleet) { + return ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.REFINERY) >= 1 || + ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.CENTRIFUGE) >= 1 || + ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.MANUFACTURE) >= 1 || + ForgeConversionGeneral.getForgingCapacityWithCR(fleet, ForgeTypes.ASSEMBLY) >= 1; + } + + + public static boolean hasActiveForgeShips() { + CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); + if (playerFleet == null) { + return false; + } + for (FleetMemberAPI member : playerFleet.getMembersWithFightersCopy()) { + if (!isValidHullsize(member) || !isOperational(member)) { + continue; + } + if (isForgeShip(member)) { + return true; + } + } + return false; + } + + public static boolean hasInactiveForgeShips() { + CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); + if (playerFleet == null) { + return false; + } + for (FleetMemberAPI member : playerFleet.getMembersWithFightersCopy()) { + if (!isValidHullsize(member)) { + continue; + } + if (isForgeShip(member) && !isOperational(member)) { + return true; + } + } + return false; + } + + public static boolean isOperational(FleetMemberAPI member) { + return !member.getRepairTracker().isMothballed() && + !member.getRepairTracker().isSuspendRepairs() && + hasMinimumCR(member); + } + + public static boolean isOperational(ShipAPI ship) { + return !ship.getFleetMember().getRepairTracker().isMothballed() && + !ship.getFleetMember().getRepairTracker().isSuspendRepairs() && + hasMinimumCR(ship); + } + + public static boolean hasMinimumCR(FleetMemberAPI member) { + return member.getRepairTracker().getCR() > 0.1f; + } + + public static boolean hasMinimumCR(ShipAPI ship) { + return ship.getFleetMember().getRepairTracker().getCR() > 0.1f; + } + + public static boolean isValidHullsize(FleetMemberAPI member) { + return isSize(member, ShipAPI.HullSize.CRUISER) || + isSize(member, ShipAPI.HullSize.CAPITAL_SHIP); + } + + public static boolean isValidHullsize(ShipAPI ship) { + return (ship.getHullSize() == ShipAPI.HullSize.CRUISER || + ship.getHullSize() == ShipAPI.HullSize.CAPITAL_SHIP); + } + + public static boolean isForgeShip(FleetMemberAPI member) { + for (String forgeHullmod : ForgeHullmodsGeneral.ALL_FORGE_HULLMODS) { + if (member.getVariant().getHullMods().contains(forgeHullmod)) { + return true; + } + } + return false; + } + + public static boolean isSize(FleetMemberAPI member, ShipAPI.HullSize hullSize) { + return member.getHullSpec().getHullSize().equals(hullSize); + } + +} diff --git a/source/data/forge/campaign/ForgeProductionReport.java b/source/data/forge/campaign/ForgeProductionReport.java new file mode 100644 index 0000000..5a26657 --- /dev/null +++ b/source/data/forge/campaign/ForgeProductionReport.java @@ -0,0 +1,138 @@ +package data.forge.campaign; + +import java.awt.*; + +import com.fs.starfarer.api.Global; +import com.fs.starfarer.api.util.Misc; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; + +import data.forge.abilities.conversion.support.ForgeConversionVariables; +import data.forge.plugins.ForgeSettings; + +import static data.forge.plugins.ForgeSettings.NOTIFICATION_INTERVAL; + +public class ForgeProductionReport extends BaseIntelPlugin { + + @Override + public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) { + + Color titleColor = getTitleColor(mode); + + if (NOTIFICATION_INTERVAL > 1) { + info.addPara("Forge Production - " + NOTIFICATION_INTERVAL + " Days Report", titleColor, 0f); + } else if (NOTIFICATION_INTERVAL == 1) { + info.addPara("Forge Production - Daily Report", titleColor, 0f); + } + else { + info.addPara("Forge Production - Report", titleColor, 0f); + } + + info.addSpacer(2.5f); + + addBulletPoints(info, mode); + + clearTemporaryVariables(); + + Global.getSector().getIntelManager().removeIntel(this); + + } + + @Override public String getIcon() { + + return Global.getSettings().getSpriteName("intel", "forge_production_report"); + + } + + protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) { + + Color highlightColor = Misc.getHighlightColor(); + Color negativeHighlight = Misc.getNegativeHighlightColor(); + + float pad = 10f; + int gridSize = 0; + float gridWidth = 190f; + + String metalValue = "+" + Misc.getWithDGS((int) (ForgeConversionVariables.totalMetalProduction)); + String transplutonicsValue = "+" + Misc.getWithDGS((int) (ForgeConversionVariables.totalTransplutonicsProduction)); + String fuelValue = "+" + Misc.getWithDGS((int) (ForgeConversionVariables.totalFuelProduction)); + String suppliesValue = "+" + Misc.getWithDGS((int) (ForgeConversionVariables.totalSuppliesProduction)); + String heavyMachineryValue = "+" + Misc.getWithDGS((int) (ForgeConversionVariables.totalHeavyMachineryProduction)); + + String machineryBreakdownsValue = "-" + Misc.getWithDGS((int) (ForgeConversionVariables.totalMachineryBreakage)); + + String indent = " - "; + + String metalProductionLine = indent + "Metal:"; + String transplutonicsProductionLine = indent + "Transplutonics:"; + String fuelProductionLine = indent + "Fuel:"; + String suppliesProductionLine = indent + "Supplies:"; + String heavyMachineryProductionLine = indent + "Machinery:"; + String machineryBreakdownsLine = indent + "Breakdowns:"; + + if (ForgeSettings.INVERTED_GRID_NOTIFICATION) { + gridWidth = 220f; + metalProductionLine = "Metal produced"; + transplutonicsProductionLine = "Transplutonics produced"; + fuelProductionLine = "Fuel produced"; + suppliesProductionLine = "Supplies produced"; + heavyMachineryProductionLine = "Machinery produced"; + machineryBreakdownsLine = "Machinery broken"; + } + + info.setGridRowHeight(12.5f); + + if (!ForgeSettings.INVERTED_GRID_NOTIFICATION) { + info.beginGrid(gridWidth, 1); + } + + if (ForgeSettings.INVERTED_GRID_NOTIFICATION) { + info.beginGridFlipped(gridWidth, 1, 40f, pad); + } + + if (ForgeConversionVariables.oreRefiningReport) { + info.addToGrid(0, gridSize++, metalProductionLine, metalValue, highlightColor); + } + + if (ForgeConversionVariables.transplutonicsRefiningReport) { + info.addToGrid(0, gridSize++, transplutonicsProductionLine, transplutonicsValue, highlightColor); + } + + if (ForgeConversionVariables.fuelCentrifugingReport) { + info.addToGrid(0, gridSize++, fuelProductionLine, fuelValue, highlightColor); + } + + if (ForgeConversionVariables.suppliesManufacturingReport) { + info.addToGrid(0, gridSize++, suppliesProductionLine, suppliesValue, highlightColor); + } + + if (ForgeConversionVariables.heavyMachineryAssemblingReport) { + info.addToGrid(0, gridSize++, heavyMachineryProductionLine, heavyMachineryValue, highlightColor); + } + + if (ForgeConversionVariables.breakdownReport) { + info.addToGrid(0, gridSize++, machineryBreakdownsLine, machineryBreakdownsValue, negativeHighlight); + } + info.addGrid(2f); + info.resetGridRowHeight(); + } + + public void clearTemporaryVariables() { + + ForgeConversionVariables.totalMetalProduction = 0f; + ForgeConversionVariables.totalTransplutonicsProduction = 0f; + ForgeConversionVariables.totalFuelProduction = 0f; + ForgeConversionVariables.totalSuppliesProduction = 0f; + ForgeConversionVariables.totalHeavyMachineryProduction = 0f; + ForgeConversionVariables.totalMachineryBreakage = 0f; + + ForgeConversionVariables.oreRefiningReport = false; + ForgeConversionVariables.transplutonicsRefiningReport = false; + ForgeConversionVariables.fuelCentrifugingReport = false; + ForgeConversionVariables.suppliesManufacturingReport = false; + ForgeConversionVariables.heavyMachineryAssemblingReport = false; + ForgeConversionVariables.breakdownReport = false; + + } + +} diff --git a/source/data/forge/hullmods/ForgeAssemblyModule.java b/source/data/forge/hullmods/ForgeAssemblyModule.java new file mode 100644 index 0000000..0e92126 --- /dev/null +++ b/source/data/forge/hullmods/ForgeAssemblyModule.java @@ -0,0 +1,186 @@ +package data.forge.hullmods; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +import com.fs.starfarer.api.combat.BaseHullMod; +import com.fs.starfarer.api.combat.MutableShipStatsAPI; +import com.fs.starfarer.api.combat.ShipAPI; +import com.fs.starfarer.api.ui.Alignment; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.util.Misc; + +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; +import data.forge.hullmods.support.ForgeHullmodsTooltip; +import data.forge.plugins.ForgeSettings; + +import static data.forge.hullmods.support.ForgeHullmodsGeneral.*; + +public class ForgeAssemblyModule extends BaseHullMod { + + private static final Set OTHER_FORGE_HULLMODS = new HashSet<>(); + static + { + OTHER_FORGE_HULLMODS.add("forge_refinery_module"); + OTHER_FORGE_HULLMODS.add("forge_centrifuge_module"); + OTHER_FORGE_HULLMODS.add("forge_manufacture_module"); + } + + @Override + public void applyEffectsBeforeShipCreation(ShipAPI.HullSize hullSize, MutableShipStatsAPI stats, String id) { + + if (stats.getVariant().getSMods().contains("forge_assembly_module") || + stats.getVariant().getHullSpec().isBuiltInMod("forge_assembly_module")) { + + float cargoMalus = shipCargoMalus.get(hullSize); + stats.getCargoMod().modifyFlat(id, -cargoMalus); + + float nullifiedMalus = 0f; + stats.getSuppliesPerMonth().modifyFlat(id, nullifiedMalus); + stats.getMinCrewMod().modifyFlat(id, nullifiedMalus); + + } + else { + + float cargoMalus = shipCargoMalus.get(hullSize); + stats.getCargoMod().modifyFlat(id, -cargoMalus); + + float maintenanceIncrease = shipMaintenanceIncrease.get(hullSize); + stats.getSuppliesPerMonth().modifyFlat(id, maintenanceIncrease); + + float skeletonCrew = skeletonCrewRequirement.get(hullSize); + stats.getMinCrewMod().modifyFlat(id, skeletonCrew); + + } + } + + public String getDescriptionParam(int index, ShipAPI.HullSize hullSize) { + if (index == 0) return "" + (shipCargoMalus.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 1) return "" + (shipCargoMalus.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + if (index == 2) return "" + (skeletonCrewRequirement.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 3) return "" + (skeletonCrewRequirement.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + if (index == 4) return "" + (shipMaintenanceIncrease.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 5) return "" + (shipMaintenanceIncrease.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + return null; + } + + + public void addPostDescriptionSection(TooltipMakerAPI tooltip, ShipAPI.HullSize hullSize, ShipAPI ship, float width, boolean isForModSpec) { + + ForgeHullmodsTooltip.addRequirementsLines(tooltip); + + if (ship != null) + { + float pad = 10f; + tooltip.addSectionHeading("Details", Alignment.MID, pad); + + Color highlightColor = Misc.getHighlightColor(); + + boolean isOperational = ForgeConditionChecker.isOperational(ship); + boolean hasMachinery = ForgeConditionChecker.hasMinimumMachinery(); + boolean isInstalled = ship.getVariant().hasHullMod(ForgeTypes.ASSEMBLY); + boolean hasCapacity = getShipCapacityWithCR(ship, hullSize) >= 1; + + int shipModifier = getShipCapacityMod(ship,hullSize, ForgeTypes.Conversion.ASSEMBLING); + + if (!isOperational || !isInstalled) { + shipModifier = shipSizeEffect.get(hullSize); + } + + ForgeHullmodsTooltip.addShipStateNote(tooltip, ship, hullSize, ForgeTypes.ASSEMBLY); + + tooltip.setBulletedListMode(" • "); + + //Here: First line + + Color [] firstLineHighlights = { + highlightColor, highlightColor, highlightColor + }; + + float heavyMachineryValue = ForgeSettings.HEAVY_MACHINERY_PRODUCED * shipModifier; + String heavyMachinery = String.valueOf((int)Math.floor(heavyMachineryValue)); + if (heavyMachineryValue < 1) { + heavyMachinery = String.valueOf((int)Math.ceil(heavyMachineryValue)); + } + + String metal = String.valueOf((int)Math.ceil(ForgeSettings.METAL_TO_ASSEMBLE * shipModifier)); + String transplutonics = String.valueOf((int)Math.ceil(ForgeSettings.TRANSPLUTONICS_TO_ASSEMBLE * shipModifier)); + + String firstLine = "Assembles %s heavy machinery from %s metal and %s transplutonics."; + String firstLineNoCapacity = "Assembles heavy machinery from metal and transplutonics."; + + if (!isOperational) { + tooltip.addPara(firstLine, 4f, firstLineHighlights, heavyMachinery, metal, transplutonics); + } + if (isOperational && hasCapacity && hasMachinery) { + tooltip.addPara(firstLine, 4f, firstLineHighlights, heavyMachinery, metal, transplutonics); + } + if (isOperational && (!hasCapacity || !hasMachinery)) { + tooltip.addPara(firstLineNoCapacity, 4f); + } + + //Here: Second line + + ForgeHullmodsTooltip.addMachineryLine(tooltip, hullSize, ship, ForgeTypes.Conversion.ASSEMBLING, ForgeTypes.ASSEMBLY); + + //Here: Third line + + ForgeHullmodsTooltip.addCRNoteLine(tooltip); + + //Here: Fourth line + + ForgeHullmodsTooltip.addInactiveLine(tooltip); + + tooltip.setBulletedListMode(null); + + //Here: S-Mod lines + + ForgeHullmodsTooltip.addSModLine(tooltip, ship, isForModSpec, ForgeTypes.ASSEMBLY) ; + + } + + } + + @Override + public boolean isApplicableToShip(ShipAPI ship) { + + boolean hasForgeModule = false; + + for (String forgeHullmod : OTHER_FORGE_HULLMODS) { + if(ship.getVariant().getHullMods().contains(forgeHullmod)) { + hasForgeModule = true; + } + } + + return (isForgeHullmodInstallValid(ship) && !hasForgeModule); + + } + + public String getUnapplicableReason(ShipAPI ship) { + if (ship != null && !ForgeConditionChecker.isValidHullsize(ship)) { + return REASON_WRONG_HULLSIZE; + } + if (ship != null && !isCivGrade(ship) && ForgeSettings.ENABLE_CIVGRADE_REQUIREMENT) { + return REASON_NOT_CIVGRADE; + } + if (ship != null && !isNotModule(ship)) { + return REASON_IS_MODULE; + } + if (ship != null && !hasCargoCapacity(ship) && ForgeSettings.ENABLE_CARGO_REQUIREMENT) { + return REASON_NO_CARGO_SPACE; + } + boolean hasForgeModule = false; + for (String forgeHullmod : OTHER_FORGE_HULLMODS) { + if (ship != null && ship.getVariant().getHullMods().contains(forgeHullmod)) { + hasForgeModule = true; + } + } + if (hasForgeModule) { + return REASON_ALREADY_HAS; + } + return null; + } + +} diff --git a/source/data/forge/hullmods/ForgeCentrifugeModule.java b/source/data/forge/hullmods/ForgeCentrifugeModule.java new file mode 100644 index 0000000..2e10113 --- /dev/null +++ b/source/data/forge/hullmods/ForgeCentrifugeModule.java @@ -0,0 +1,191 @@ +package data.forge.hullmods; + +import java.awt.*; +import java.util.Set; +import java.util.HashSet; + +import com.fs.starfarer.api.combat.BaseHullMod; +import com.fs.starfarer.api.combat.MutableShipStatsAPI; +import com.fs.starfarer.api.combat.ShipAPI; +import com.fs.starfarer.api.ui.Alignment; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.util.Misc; + +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; +import data.forge.hullmods.support.ForgeHullmodsTooltip; +import data.forge.plugins.ForgeSettings; + +import static data.forge.hullmods.support.ForgeHullmodsGeneral.*; + +public class ForgeCentrifugeModule extends BaseHullMod { + + private static final Set OTHER_FORGE_HULLMODS = new HashSet<>(); + static + { + OTHER_FORGE_HULLMODS.add("forge_refinery_module"); + OTHER_FORGE_HULLMODS.add("forge_manufacture_module"); + OTHER_FORGE_HULLMODS.add("forge_assembly_module"); + } + + @Override + public void applyEffectsBeforeShipCreation(ShipAPI.HullSize hullSize, MutableShipStatsAPI stats, String id) { + + if (stats.getVariant().getSMods().contains("forge_centrifuge_module") || + stats.getVariant().getHullSpec().isBuiltInMod("forge_centrifuge_module")) { + + float cargoMalus = shipCargoMalus.get(hullSize); + stats.getCargoMod().modifyFlat(id, -cargoMalus); + + float nullifiedMalus = 0f; + stats.getSuppliesPerMonth().modifyFlat(id, nullifiedMalus); + stats.getMinCrewMod().modifyFlat(id, nullifiedMalus); + + } + else { + + float cargoMalus = shipCargoMalus.get(hullSize); + stats.getCargoMod().modifyFlat(id, -cargoMalus); + + float maintenanceIncrease = shipMaintenanceIncrease.get(hullSize); + stats.getSuppliesPerMonth().modifyFlat(id, maintenanceIncrease); + + float skeletonCrew = skeletonCrewRequirement.get(hullSize); + stats.getMinCrewMod().modifyFlat(id, skeletonCrew); + + } + } + + public String getDescriptionParam(int index, ShipAPI.HullSize hullSize) { + if (index == 0) return "" + (shipCargoMalus.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 1) return "" + (shipCargoMalus.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + if (index == 2) return "" + (skeletonCrewRequirement.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 3) return "" + (skeletonCrewRequirement.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + if (index == 4) return "" + (shipMaintenanceIncrease.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 5) return "" + (shipMaintenanceIncrease.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + return null; + } + + + public void addPostDescriptionSection(TooltipMakerAPI tooltip, ShipAPI.HullSize hullSize, ShipAPI ship, float width, boolean isForModSpec) { + + ForgeHullmodsTooltip.addRequirementsLines(tooltip); + + if (ship != null) + { + float pad = 10f; + tooltip.addSectionHeading("Details", Alignment.MID, pad); + + Color highlightColor = Misc.getHighlightColor(); + + boolean isOperational = ForgeConditionChecker.isOperational(ship); + boolean hasMachinery = ForgeConditionChecker.hasMinimumMachinery(); + boolean isInstalled = ship.getVariant().hasHullMod(ForgeTypes.CENTRIFUGE); + boolean hasCapacity = getShipCapacityWithCR(ship, hullSize) >= 1; + + int shipModifier = getShipCapacityMod(ship,hullSize, ForgeTypes.Conversion.CENTRIFUGING); + + if (!isOperational || !isInstalled) { + shipModifier = shipSizeEffect.get(hullSize); + } + + ForgeHullmodsTooltip.addShipStateNote(tooltip, ship, hullSize, ForgeTypes.CENTRIFUGE); + + tooltip.setBulletedListMode(" • "); + + //Here: First line + + Color [] firstLineHighlights = { + highlightColor, highlightColor, highlightColor, highlightColor + }; + + String volatiles = String.valueOf((int)Math.ceil(ForgeSettings.VOLATILES_TO_CENTRIFUGE * shipModifier)); + String fuel = String.valueOf((int)Math.floor(ForgeSettings.FUEL_PRODUCED * shipModifier)); + + String firstLine = "Centrifuges %s fuel from %s volatiles."; + String firstLineNoCapacity = "Centrifuges fuel from volatiles."; + + if (!isOperational) { + tooltip.addPara(firstLine, 4f, firstLineHighlights, fuel, volatiles); + } + if (isOperational && hasCapacity && hasMachinery) { + tooltip.addPara(firstLine, 4f, firstLineHighlights, fuel, volatiles); + } + if (isOperational && (!hasCapacity || !hasMachinery)) { + tooltip.addPara(firstLineNoCapacity, 4f); + } + + //Here: Second line + + ForgeHullmodsTooltip.addMachineryLine(tooltip, hullSize, ship, ForgeTypes.Conversion.CENTRIFUGING, ForgeTypes.CENTRIFUGE); + + //Here: Third line + + ForgeHullmodsTooltip.addCRNoteLine(tooltip); + + //Here: Fourth line + + ForgeHullmodsTooltip.addInactiveLine(tooltip); + + tooltip.setBulletedListMode(null); + + //Here: S-Mod lines + + ForgeHullmodsTooltip.addSModLine(tooltip, ship, isForModSpec, ForgeTypes.CENTRIFUGE) ; + + //Here: Prometheus line + + if (isHullPrometheus(ship) && !isInstalled) { + String prometheusInstall = "Installable on Prometheus-class hulls due to existence of built-in facilities."; + tooltip.addPara(prometheusInstall, Misc.getPositiveHighlightColor(), 2f); + } + } + } + + private boolean isHullPrometheus(ShipAPI ship) { + return ship.getHullSpec().getBaseHullId().equals("prometheus"); + } + + @Override + public boolean isApplicableToShip(ShipAPI ship) { + + boolean hasForgeModule = false; + + for (String forgeHullmod : OTHER_FORGE_HULLMODS) { + if(ship.getVariant().getHullMods().contains(forgeHullmod)) { + hasForgeModule = true; + } + } + + if (isHullPrometheus(ship)) { + return !hasForgeModule; + } + return (isForgeHullmodInstallValid(ship) && !hasForgeModule); + } + + public String getUnapplicableReason(ShipAPI ship) { + if (ship != null && !ForgeConditionChecker.isValidHullsize(ship)) { + return REASON_WRONG_HULLSIZE; + } + if (ship != null && !isCivGrade(ship) && ForgeSettings.ENABLE_CIVGRADE_REQUIREMENT) { + return REASON_NOT_CIVGRADE; + } + if (ship != null && !isNotModule(ship)) { + return REASON_IS_MODULE; + } + if (ship != null && !hasCargoCapacity(ship) && ForgeSettings.ENABLE_CARGO_REQUIREMENT) { + return REASON_NO_CARGO_SPACE; + } + boolean hasForgeModule = false; + for (String forgeHullmod : OTHER_FORGE_HULLMODS) { + if (ship != null && ship.getVariant().getHullMods().contains(forgeHullmod)) { + hasForgeModule = true; + } + } + if (hasForgeModule) { + return REASON_ALREADY_HAS; + } + return null; + } + +} diff --git a/source/data/forge/hullmods/ForgeManufactureModule.java b/source/data/forge/hullmods/ForgeManufactureModule.java new file mode 100644 index 0000000..8002f05 --- /dev/null +++ b/source/data/forge/hullmods/ForgeManufactureModule.java @@ -0,0 +1,186 @@ +package data.forge.hullmods; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +import com.fs.starfarer.api.combat.BaseHullMod; +import com.fs.starfarer.api.combat.MutableShipStatsAPI; +import com.fs.starfarer.api.combat.ShipAPI; +import com.fs.starfarer.api.ui.Alignment; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.util.Misc; + +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; +import data.forge.hullmods.support.ForgeHullmodsTooltip; +import data.forge.plugins.ForgeSettings; + +import static data.forge.hullmods.support.ForgeHullmodsGeneral.*; + +public class ForgeManufactureModule extends BaseHullMod { + + private static final Set OTHER_FORGE_HULLMODS = new HashSet<>(); + static + { + OTHER_FORGE_HULLMODS.add("forge_refinery_module"); + OTHER_FORGE_HULLMODS.add("forge_centrifuge_module"); + OTHER_FORGE_HULLMODS.add("forge_assembly_module"); + } + + @Override + public void applyEffectsBeforeShipCreation(ShipAPI.HullSize hullSize, MutableShipStatsAPI stats, String id) { + + if (stats.getVariant().getSMods().contains("forge_manufacture_module") || + stats.getVariant().getHullSpec().isBuiltInMod("forge_manufacture_module")) { + + float cargoMalus = shipCargoMalus.get(hullSize); + stats.getCargoMod().modifyFlat(id, -cargoMalus); + + float nullifiedMalus = 0f; + stats.getSuppliesPerMonth().modifyFlat(id, -nullifiedMalus); + stats.getMinCrewMod().modifyFlat(id, -nullifiedMalus); + + } + else { + + float cargoMalus = shipCargoMalus.get(hullSize); + stats.getCargoMod().modifyFlat(id, -cargoMalus); + + float maintenanceIncrease = shipMaintenanceIncrease.get(hullSize); + stats.getSuppliesPerMonth().modifyFlat(id, maintenanceIncrease); + + float skeletonCrew = skeletonCrewRequirement.get(hullSize); + stats.getMinCrewMod().modifyFlat(id, skeletonCrew); + + } + } + + public String getDescriptionParam(int index, ShipAPI.HullSize hullSize) { + if (index == 0) return "" + (shipCargoMalus.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 1) return "" + (shipCargoMalus.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + if (index == 2) return "" + (skeletonCrewRequirement.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 3) return "" + (skeletonCrewRequirement.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + if (index == 4) return "" + (shipMaintenanceIncrease.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 5) return "" + (shipMaintenanceIncrease.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + return null; + } + + + public void addPostDescriptionSection(TooltipMakerAPI tooltip, ShipAPI.HullSize hullSize, ShipAPI ship, float width, boolean isForModSpec) { + + ForgeHullmodsTooltip.addRequirementsLines(tooltip); + + if (ship != null) + { + float pad = 10f; + tooltip.addSectionHeading("Details", Alignment.MID, pad); + + Color highlightColor = Misc.getHighlightColor(); + + boolean isOperational = ForgeConditionChecker.isOperational(ship); + boolean hasMachinery = ForgeConditionChecker.hasMinimumMachinery(); + boolean isInstalled = ship.getVariant().hasHullMod(ForgeTypes.MANUFACTURE); + boolean hasCapacity = getShipCapacityWithCR(ship, hullSize) >= 1; + + int shipModifier = getShipCapacityMod(ship,hullSize, ForgeTypes.Conversion.MANUFACTURING); + + if (!isOperational || !isInstalled) { + shipModifier = shipSizeEffect.get(hullSize); + } + + ForgeHullmodsTooltip.addShipStateNote(tooltip, ship, hullSize, ForgeTypes.MANUFACTURE); + + tooltip.setBulletedListMode(" • "); + + //Here: First line + + Color [] firstLineHighlights = { + highlightColor, highlightColor, highlightColor + }; + + float suppliesValue = ForgeSettings.SUPPLIES_PRODUCED * shipModifier; + String supplies = String.valueOf((int)Math.floor(suppliesValue)); + if (suppliesValue < 1) { + supplies = String.valueOf((int)Math.ceil(suppliesValue)); + } + + String metal = String.valueOf((int)Math.ceil(ForgeSettings.METAL_TO_MANUFACTURE * shipModifier)); + String transplutonics = String.valueOf((int)Math.ceil(ForgeSettings.TRANSPLUTONICS_TO_MANUFACTURE * shipModifier)); + + String firstLine = "Manufactures %s supplies from %s metal and %s transplutonics."; + String firstLineNoCapacity = "Manufactures supplies from metal and transplutonics."; + + if (!isOperational) { + tooltip.addPara(firstLine, 4f, firstLineHighlights, supplies, metal, transplutonics); + } + if (isOperational && hasCapacity && hasMachinery) { + tooltip.addPara(firstLine, 4f, firstLineHighlights, supplies, metal, transplutonics); + } + if (isOperational && (!hasCapacity || !hasMachinery)) { + tooltip.addPara(firstLineNoCapacity, 4f); + } + + //Here: Second line + + ForgeHullmodsTooltip.addMachineryLine(tooltip, hullSize, ship, ForgeTypes.Conversion.MANUFACTURING, ForgeTypes.MANUFACTURE); + + //Here: Third line + + ForgeHullmodsTooltip.addCRNoteLine(tooltip); + + //Here: Fourth line + + ForgeHullmodsTooltip.addInactiveLine(tooltip); + + tooltip.setBulletedListMode(null); + + //Here: S-Mod lines + + ForgeHullmodsTooltip.addSModLine(tooltip, ship, isForModSpec, ForgeTypes.MANUFACTURE) ; + + } + + } + + @Override + public boolean isApplicableToShip(ShipAPI ship) { + + boolean hasForgeModule = false; + + for (String forgeHullmod : OTHER_FORGE_HULLMODS) { + if(ship.getVariant().getHullMods().contains(forgeHullmod)) { + hasForgeModule = true; + } + } + + return (isForgeHullmodInstallValid(ship) && !hasForgeModule); + + } + + public String getUnapplicableReason(ShipAPI ship) { + if (ship != null && !ForgeConditionChecker.isValidHullsize(ship)) { + return REASON_WRONG_HULLSIZE; + } + if (ship != null && !isCivGrade(ship) && ForgeSettings.ENABLE_CIVGRADE_REQUIREMENT) { + return REASON_NOT_CIVGRADE; + } + if (ship != null && !isNotModule(ship)) { + return REASON_IS_MODULE; + } + if (ship != null && !hasCargoCapacity(ship) && ForgeSettings.ENABLE_CARGO_REQUIREMENT) { + return REASON_NO_CARGO_SPACE; + } + boolean hasForgeModule = false; + for (String forgeHullmod : OTHER_FORGE_HULLMODS) { + if (ship != null && ship.getVariant().getHullMods().contains(forgeHullmod)) { + hasForgeModule = true; + } + } + if (hasForgeModule) { + return REASON_ALREADY_HAS; + } + return null; + } + +} diff --git a/source/data/forge/hullmods/ForgeRefineryModule.java b/source/data/forge/hullmods/ForgeRefineryModule.java new file mode 100644 index 0000000..6501232 --- /dev/null +++ b/source/data/forge/hullmods/ForgeRefineryModule.java @@ -0,0 +1,188 @@ +package data.forge.hullmods; + +import java.awt.*; +import java.util.Set; +import java.util.HashSet; + +import com.fs.starfarer.api.combat.BaseHullMod; +import com.fs.starfarer.api.combat.MutableShipStatsAPI; +import com.fs.starfarer.api.combat.ShipAPI; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.ui.Alignment; +import com.fs.starfarer.api.util.Misc; + +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; +import data.forge.hullmods.support.ForgeHullmodsTooltip; +import data.forge.plugins.ForgeSettings; + +import static data.forge.hullmods.support.ForgeHullmodsGeneral.*; + +public class ForgeRefineryModule extends BaseHullMod { + + private static final Set OTHER_FORGE_HULLMODS = new HashSet<>(); + static + { + OTHER_FORGE_HULLMODS.add("forge_centrifuge_module"); + OTHER_FORGE_HULLMODS.add("forge_manufacture_module"); + OTHER_FORGE_HULLMODS.add("forge_assembly_module"); + } + + @Override + public void applyEffectsBeforeShipCreation(ShipAPI.HullSize hullSize, MutableShipStatsAPI stats, String id) { + + if (stats.getVariant().getSMods().contains("forge_refinery_module") || + stats.getVariant().getHullSpec().isBuiltInMod("forge_refinery_module")) { + + float cargoMalus = shipCargoMalus.get(hullSize); + stats.getCargoMod().modifyFlat(id, -cargoMalus); + + float nullifiedMalus = 0f; + stats.getSuppliesPerMonth().modifyFlat(id, -nullifiedMalus); + stats.getMinCrewMod().modifyFlat(id, -nullifiedMalus); + + } + else { + + float cargoMalus = shipCargoMalus.get(hullSize); + stats.getCargoMod().modifyFlat(id, -cargoMalus); + + float maintenanceIncrease = shipMaintenanceIncrease.get(hullSize); + stats.getSuppliesPerMonth().modifyFlat(id, maintenanceIncrease); + + float skeletonCrew = skeletonCrewRequirement.get(hullSize); + stats.getMinCrewMod().modifyFlat(id, skeletonCrew); + + } + } + + public String getDescriptionParam(int index, ShipAPI.HullSize hullSize) { + if (index == 0) return "" + (shipCargoMalus.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 1) return "" + (shipCargoMalus.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + if (index == 2) return "" + (skeletonCrewRequirement.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 3) return "" + (skeletonCrewRequirement.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + if (index == 4) return "" + (shipMaintenanceIncrease.get(ShipAPI.HullSize.CRUISER)).intValue(); + if (index == 5) return "" + (shipMaintenanceIncrease.get(ShipAPI.HullSize.CAPITAL_SHIP)).intValue(); + return null; + } + + public void addPostDescriptionSection(TooltipMakerAPI tooltip, ShipAPI.HullSize hullSize, ShipAPI ship, float width, boolean isForModSpec) { + + ForgeHullmodsTooltip.addRequirementsLines(tooltip); + + if (ship != null) + { + float pad = 10f; + tooltip.addSectionHeading("Details", Alignment.MID, pad); + + Color highlightColor = Misc.getHighlightColor(); + + boolean isOperational = ForgeConditionChecker.isOperational(ship); + boolean hasMachinery = ForgeConditionChecker.hasMinimumMachinery(); + boolean isInstalled = ship.getVariant().hasHullMod(ForgeTypes.REFINERY); + boolean hasCapacity = getShipCapacityWithCR(ship, hullSize) >= 1; + + int shipModifier = getShipCapacityMod(ship,hullSize, ForgeTypes.Conversion.REFINING); + + if (!isOperational || !isInstalled) { + shipModifier = shipSizeEffect.get(hullSize); + } + + ForgeHullmodsTooltip.addShipStateNote(tooltip, ship, hullSize, ForgeTypes.REFINERY); + + tooltip.setBulletedListMode(" • "); + + // Here: First line + + Color [] firstLineHighlights = { + highlightColor, highlightColor, highlightColor, highlightColor + }; + + String ore = String.valueOf((int)Math.ceil(ForgeSettings.ORE_TO_REFINE * shipModifier)); + String metal = String.valueOf((int)Math.floor(ForgeSettings.METAL_PRODUCED * shipModifier)); + String transplutonicOre = String.valueOf((int)Math.ceil(ForgeSettings.TRANSPLUTONIC_ORE_TO_REFINE * shipModifier)); + + float transplutonicsValue = ForgeSettings.TRANSPLUTONICS_PRODUCED * shipModifier; + String transplutonics = String.valueOf((int)Math.floor(transplutonicsValue)); + if (transplutonicsValue < 1) { + transplutonics = String.valueOf((int)Math.ceil(transplutonicsValue)); + } + + String firstLine = "Refines %s metal from %s ore and %s transplutonics from %s transplutonic ore."; + String firstLineNoCapacity = "Refines metal from ore and transplutonics from transplutonic ore."; + + if (!isOperational) { + tooltip.addPara(firstLine, 4f, firstLineHighlights, + metal, ore, transplutonics, transplutonicOre); + } + if (isOperational && hasCapacity && hasMachinery) { + tooltip.addPara(firstLine, 4f, firstLineHighlights, + metal, ore, transplutonics, transplutonicOre); + } + if (isOperational && (!hasCapacity || !hasMachinery)) { + tooltip.addPara(firstLineNoCapacity, 4f); + } + + // Here: Second line + + ForgeHullmodsTooltip.addMachineryLine(tooltip, hullSize, ship, ForgeTypes.Conversion.REFINING, ForgeTypes.REFINERY); + + // Here: Third line + + ForgeHullmodsTooltip.addCRNoteLine(tooltip); + + // Here: Fourth line + + ForgeHullmodsTooltip.addInactiveLine(tooltip); + + tooltip.setBulletedListMode(null); + + // Here: S-Mod lines + + ForgeHullmodsTooltip.addSModLine(tooltip, ship, isForModSpec, ForgeTypes.REFINERY) ; + + } + + } + + @Override + public boolean isApplicableToShip(ShipAPI ship) { + + boolean hasForgeModule = false; + + for (String forgeHullmod : OTHER_FORGE_HULLMODS) { + if(ship.getVariant().getHullMods().contains(forgeHullmod)) { + hasForgeModule = true; + } + } + + return (isForgeHullmodInstallValid(ship) && !hasForgeModule); + + } + + public String getUnapplicableReason(ShipAPI ship) { + if (ship != null && !ForgeConditionChecker.isValidHullsize(ship)) { + return REASON_WRONG_HULLSIZE; + } + if (ship != null && !isCivGrade(ship) && ForgeSettings.ENABLE_CIVGRADE_REQUIREMENT) { + return REASON_NOT_CIVGRADE; + } + if (ship != null && !isNotModule(ship)) { + return REASON_IS_MODULE; + } + if (ship != null && !hasCargoCapacity(ship) && ForgeSettings.ENABLE_CARGO_REQUIREMENT) { + return REASON_NO_CARGO_SPACE; + } + boolean hasForgeModule = false; + for (String forgeHullmod : OTHER_FORGE_HULLMODS) { + if (ship != null && ship.getVariant().getHullMods().contains(forgeHullmod)) { + hasForgeModule = true; + } + } + if (hasForgeModule) { + return REASON_ALREADY_HAS; + } + return null; + } + +} diff --git a/source/data/forge/hullmods/support/ForgeHullmodsGeneral.java b/source/data/forge/hullmods/support/ForgeHullmodsGeneral.java new file mode 100644 index 0000000..de5272c --- /dev/null +++ b/source/data/forge/hullmods/support/ForgeHullmodsGeneral.java @@ -0,0 +1,129 @@ +package data.forge.hullmods.support; + +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; + +import com.fs.starfarer.api.combat.ShipAPI; +import com.fs.starfarer.api.impl.campaign.ids.HullMods; + +import data.forge.abilities.conversion.support.ForgeConversionGeneral; +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; +import data.forge.plugins.ForgeSettings; + +public class ForgeHullmodsGeneral { + + public static final String REASON_WRONG_HULLSIZE = "Can only be installed on cruisers and capital ships"; + public static final String REASON_NOT_CIVGRADE = "Can only be installed on civilian-grade hulls"; + public static final String REASON_IS_MODULE = "Can not be installed on modules"; + public static final String REASON_NO_CARGO_SPACE = "Can not be installed on ships with insufficient cargo space"; + public static final String REASON_ALREADY_HAS = "Can only install one Forge module per ship"; + + public static final Set ALL_FORGE_HULLMODS = new HashSet<>(); + static + { + ALL_FORGE_HULLMODS.add("forge_refinery_module"); + ALL_FORGE_HULLMODS.add("forge_centrifuge_module"); + ALL_FORGE_HULLMODS.add("forge_manufacture_module"); + ALL_FORGE_HULLMODS.add("forge_assembly_module"); + } + + public static Map shipSizeEffect = new HashMap<>(); + static { + + shipSizeEffect.put(ShipAPI.HullSize.DEFAULT, 0); + shipSizeEffect.put(ShipAPI.HullSize.FIGHTER, 0); + shipSizeEffect.put(ShipAPI.HullSize.FRIGATE, 0); + shipSizeEffect.put(ShipAPI.HullSize.DESTROYER, 0); + shipSizeEffect.put(ShipAPI.HullSize.CRUISER, ForgeSettings.CAPACITY_CRUISER); + shipSizeEffect.put(ShipAPI.HullSize.CAPITAL_SHIP, ForgeSettings.CAPACITY_CAPITAL); + + } + + public static final Map shipCargoMalus = new HashMap<>(); + static { + shipCargoMalus.put(ShipAPI.HullSize.DEFAULT, 0f); + shipCargoMalus.put(ShipAPI.HullSize.FIGHTER, 0f); + shipCargoMalus.put(ShipAPI.HullSize.FRIGATE, 0f); + shipCargoMalus.put(ShipAPI.HullSize.DESTROYER, 0f); + shipCargoMalus.put(ShipAPI.HullSize.CRUISER, 200f); + shipCargoMalus.put(ShipAPI.HullSize.CAPITAL_SHIP, 400f); + } + + public static final Map shipMaintenanceIncrease = new HashMap<>(); + static { + shipMaintenanceIncrease.put(ShipAPI.HullSize.DEFAULT, 0f); + shipMaintenanceIncrease.put(ShipAPI.HullSize.FIGHTER, 0f); + shipMaintenanceIncrease.put(ShipAPI.HullSize.FRIGATE, 0f); + shipMaintenanceIncrease.put(ShipAPI.HullSize.DESTROYER, 0f); + shipMaintenanceIncrease.put(ShipAPI.HullSize.CRUISER, 2f); + shipMaintenanceIncrease.put(ShipAPI.HullSize.CAPITAL_SHIP, 4f); + } + + public static final Map skeletonCrewRequirement = new HashMap<>(); + static { + skeletonCrewRequirement.put(ShipAPI.HullSize.DEFAULT, 0f); + skeletonCrewRequirement.put(ShipAPI.HullSize.FIGHTER, 0f); + skeletonCrewRequirement.put(ShipAPI.HullSize.FRIGATE, 0f); + skeletonCrewRequirement.put(ShipAPI.HullSize.DESTROYER, 0f); + skeletonCrewRequirement.put(ShipAPI.HullSize.CRUISER, 20f); + skeletonCrewRequirement.put(ShipAPI.HullSize.CAPITAL_SHIP, 40f); + } + + public static boolean isForgeHullmodInstallValid(ShipAPI ship) { + return (ForgeConditionChecker.isValidHullsize(ship) && + isCivGrade(ship) && + isNotModule(ship) && + hasCargoCapacity(ship)); + } + + public static boolean isCivGrade(ShipAPI ship) { + if (!ForgeSettings.ENABLE_CIVGRADE_REQUIREMENT) { + return true; + } + boolean hasCivHullmod = ship.getVariant().hasHullMod(HullMods.CIVGRADE); + boolean hasArcheanCivHullmod = ship.getVariant().hasHullMod("archeus_civgrade"); + return (hasCivHullmod || hasArcheanCivHullmod); + } + + public static boolean isNotModule(ShipAPI ship) { + return !ship.isStationModule(); + } + + public static boolean hasCargoCapacity(ShipAPI ship) { + if (!ForgeSettings.ENABLE_CARGO_REQUIREMENT) { + return true; + } + return (ship.getVariant().getHullSpec().getCargo() > shipCargoMalus.get(ship.getHullSize())); + } + + public static int getShipCapacityMod (ShipAPI ship, ShipAPI.HullSize hullSize, ForgeTypes.Conversion type) { + int shipSize = shipSizeEffect.get(hullSize); + float getCurrentCR = (ship.getFleetMember().getRepairTracker().getCR() / 0.7f); + return (int) Math.floor(Math.min((int) Math.floor(shipSize * getCurrentCR), getShipMachineryCycles(ship, hullSize, type))); + } + + public static int getShipCapacityWithCR (ShipAPI ship, ShipAPI.HullSize hullSize) { + int shipSize = shipSizeEffect.get(hullSize); + float getCurrentCR = (ship.getFleetMember().getRepairTracker().getCR() / 0.7f); + return (int) Math.floor(shipSize * getCurrentCR); + } + + public static int getShipMachineryMod (ShipAPI ship, ShipAPI.HullSize hullSize) { + int shipSize = shipSizeEffect.get(hullSize); + float getCurrentCR = (ship.getFleetMember().getRepairTracker().getCR() / 0.7f); + return (int) Math.ceil((int) Math.floor(shipSize * getCurrentCR) * ForgeConversionGeneral.getMachineryAvailability()); + } + + public static int getShipMachineryCycles (ShipAPI ship, ShipAPI.HullSize hullSize, ForgeTypes.Conversion type) { + int shipSize = shipSizeEffect.get(hullSize); + float getCurrentCR = (ship.getFleetMember().getRepairTracker().getCR() / 0.7f); + int shipCapacity = (int) Math.floor(shipSize * getCurrentCR); + float machineryAvailable = (shipCapacity * ForgeTypes.MACHINERY_USAGE.get(type)) * ForgeConversionGeneral.getMachineryAvailability(); + float machineryCycles = machineryAvailable / ForgeTypes.MACHINERY_USAGE.get(type); + return (int) Math.floor(machineryCycles); + } + +} diff --git a/source/data/forge/hullmods/support/ForgeHullmodsTooltip.java b/source/data/forge/hullmods/support/ForgeHullmodsTooltip.java new file mode 100644 index 0000000..b65f699 --- /dev/null +++ b/source/data/forge/hullmods/support/ForgeHullmodsTooltip.java @@ -0,0 +1,156 @@ +package data.forge.hullmods.support; + +import java.awt.*; + +import com.fs.starfarer.api.combat.ShipAPI; +import com.fs.starfarer.api.ui.TooltipMakerAPI; +import com.fs.starfarer.api.util.Misc; + +import data.forge.abilities.conversion.support.ForgeTypes; +import data.forge.campaign.ForgeConditionChecker; +import data.forge.plugins.ForgeSettings; + +import static data.forge.hullmods.support.ForgeHullmodsGeneral.getShipCapacityWithCR; + +public class ForgeHullmodsTooltip { + + public static void addRequirementsLines(TooltipMakerAPI tooltip) { + String hullsizeRequirement; + if (ForgeSettings.ENABLE_CIVGRADE_REQUIREMENT) { + hullsizeRequirement = "Installable only on cruisers or capital ships with civilian-grade hull."; + } else { + hullsizeRequirement = "Installable only on cruisers or capital ships."; + } + tooltip.addPara(hullsizeRequirement, 6f); + + if (!ForgeSettings.ENABLE_CARGO_REQUIREMENT) { + String cargoRequirementNullified = "Cargo requirements nullified."; + tooltip.addPara(cargoRequirementNullified, Misc.getPositiveHighlightColor(), 2f); + } + } + + public static void addShipStateNote(TooltipMakerAPI tooltip, ShipAPI ship, ShipAPI.HullSize hullSize, String forgeType) { + + boolean isInstalled = ship.getVariant().hasHullMod(forgeType); + boolean isOperational = ForgeConditionChecker.isOperational(ship); + boolean hasMachinery = ForgeConditionChecker.hasMinimumMachinery(); + boolean hasCapacity = getShipCapacityWithCR(ship, hullSize) >= 1; + + String indent = " "; + + Color stateHighlightColor = Misc.getHighlightColor(); + Color stateTextColor = Misc.getTextColor(); + + String stateHighlight = "Not installed:" ; + if (isInstalled && !isOperational) { + stateHighlight = "Module inactive:" ; + } + if (isInstalled && isOperational) { + stateHighlight = "Module active:" ; + } + + String shipStateLine = "%s showing default values." ; + if (isInstalled && !isOperational) { + shipStateLine = "%s showing default values." ; + } + if (isInstalled && isOperational) { + shipStateLine = "%s showing current values." ; + } + if (isInstalled && isOperational && !hasMachinery && hasCapacity) { + stateTextColor = Misc.getNegativeHighlightColor(); + shipStateLine = "%s insufficient machinery." ; + } + if (isInstalled && isOperational && hasMachinery && !hasCapacity) { + stateTextColor = Misc.getNegativeHighlightColor(); + shipStateLine = "%s low CR." ; + } + if (isInstalled && isOperational && !hasMachinery && !hasCapacity) { + stateTextColor = Misc.getNegativeHighlightColor(); + shipStateLine = "%s low CR." ; + } + + tooltip.setBulletedListMode(indent); + tooltip.addPara(shipStateLine, 10f, stateTextColor, stateHighlightColor, stateHighlight); + tooltip.setBulletedListMode(null); + + } + + public static void addMachineryLine(TooltipMakerAPI tooltip, ShipAPI.HullSize hullSize, ShipAPI ship, + ForgeTypes.Conversion type, String forgeType) { + + Color highlightColor = Misc.getHighlightColor(); + + Color [] secondLineHighlights = { + highlightColor, highlightColor + }; + + boolean isOperational = ForgeConditionChecker.isOperational(ship); + boolean hasMachinery = ForgeConditionChecker.hasMinimumMachinery(); + boolean isInstalled = ship.getVariant().hasHullMod(forgeType); + boolean hasCapacity = ForgeHullmodsGeneral.getShipCapacityMod(ship, hullSize, type) >= 1; + + int shipMachineryModifier = ForgeHullmodsGeneral.getShipMachineryMod(ship,hullSize); + + if (!isOperational || !isInstalled) { + shipMachineryModifier = ForgeHullmodsGeneral.shipSizeEffect.get(hullSize); + } + + float heavyMachineryUsageValue = ForgeTypes.MACHINERY_USAGE.get(type) * shipMachineryModifier; + String heavyMachineryUsage = String.valueOf((int)Math.floor(heavyMachineryUsageValue)); + if (heavyMachineryUsageValue < 1) { + heavyMachineryUsage = String.valueOf((int)Math.ceil(heavyMachineryUsageValue)); + } + + String breakdownChance = ((int)(ForgeSettings.BASE_BREAKDOWN_CHANCE * 100) + "%"); + + String secondLine = "Uses %s heavy machinery, with %s chance of breakdown." ; + String secondLineNoCapacity = "Uses heavy machinery, with %s chance of breakdown." ; + + if (!isOperational) { + tooltip.addPara(secondLine, 2f, secondLineHighlights, heavyMachineryUsage, breakdownChance); + } + if (isOperational && hasCapacity && hasMachinery) { + tooltip.addPara(secondLine, 2f, secondLineHighlights, heavyMachineryUsage, breakdownChance); + } + if (isOperational && (!hasCapacity || !hasMachinery)) { + tooltip.addPara(secondLineNoCapacity, 2f, highlightColor, breakdownChance); + } + + } + + public static void addCRNoteLine(TooltipMakerAPI tooltip) { + + Color [] thirdLineHighlights = {Misc.getHighlightColor()}; + + String crDecrease = ((int)(( ForgeSettings.CR_PRODUCTION_DECAY)*100) + "%"); + String CRNoteLine = "Refining drains %s CR per day. Ships with low CR have lowered output and lose less CR." ; + + tooltip.addPara(CRNoteLine, 2f, thirdLineHighlights, crDecrease); + + } + + public static void addInactiveLine(TooltipMakerAPI tooltip) { + + Color [] fourthLineHighlights = {Misc.getHighlightColor()}; + String CR = "10%"; + String InactiveLine = "Forge module cannot be activated if ship is mothballed, does not receive repairs or has less than %s CR."; + + tooltip.addPara(InactiveLine, 2f, fourthLineHighlights, CR); + + } + + public static void addSModLine(TooltipMakerAPI tooltip, ShipAPI ship, boolean isForModSpec, String forgeType) { + + if (isForModSpec) { + tooltip.addPara("If this hullmod is built in, additional crew requirement and maintenance cost increase are nullified.", Misc.getGrayColor(), 10f); + } else if (ship.getVariant().getSMods().contains(forgeType)) { + tooltip.addPara("S-mod Bonus: Additional crew requirement and maintenance cost increase are nullified.", Misc.getPositiveHighlightColor(), 10f); + } else if (ship.getHullSpec().isBuiltInMod(forgeType)) { + tooltip.addPara("Built-in Bonus: Additional crew requirement and maintenance cost increase are nullified.", Misc.getPositiveHighlightColor(), 10f); + } else { + tooltip.addPara("If this hullmod is built in, additional crew requirement and maintenance cost increase are nullified.", Misc.getGrayColor(), 10f); + } + + } + +} diff --git a/source/data/forge/plugins/ForgeDataSupplier.java b/source/data/forge/plugins/ForgeDataSupplier.java new file mode 100644 index 0000000..7cf14c9 --- /dev/null +++ b/source/data/forge/plugins/ForgeDataSupplier.java @@ -0,0 +1,81 @@ +package data.forge.plugins; + +import java.io.IOException; + +import org.json.JSONException; +import org.json.JSONObject; + +import com.fs.starfarer.api.Global; + +public class ForgeDataSupplier { + + public static void loadSettings(String fileName) throws IOException, JSONException { + + JSONObject settings = Global.getSettings().loadJSON(fileName); + + ForgeSettings.ENABLE_CARGO_REQUIREMENT = settings.getBoolean("forge_enable_cargo_requirement"); + ForgeSettings.ENABLE_CIVGRADE_REQUIREMENT = settings.getBoolean("forge_enable_civgrade_requirement"); + + // Here: Miscellaneous Settings + ForgeSettings.ENABLE_SLOW_MOVE_PENALTY = settings.getBoolean("forge_enable_slow_move_penalty"); + ForgeSettings.ENABLE_DETECT_AT_RANGE_PENALTY = settings.getBoolean("forge_enable_sensor_penalty"); + + ForgeSettings.ENABLE_BURN_ABILITIES_INCOMPATIBILITY = settings.getBoolean("forge_enable_burn_abilities_incompatibility"); + + ForgeSettings.SENSOR_PROFILE_INCREASE = settings.getInt("forge_sensor_profile_increase"); + ForgeSettings.SHIP_LIST_SIZE = settings.getInt("forge_ship_list_size"); + + // Here: Notification Settings + ForgeSettings.PLAY_SOUND_NOTIFICATION = settings.getBoolean("forge_play_sound_notification"); + ForgeSettings.SHOW_NOTIFICATION = settings.getBoolean("forge_show_notification"); + ForgeSettings.INVERTED_GRID_NOTIFICATION = settings.getBoolean("forge_inverted_grid_notification"); + ForgeSettings.NOTIFICATION_INTERVAL = settings.getInt("forge_notification_interval"); + + // Here: Capacity Settings + ForgeSettings.CAPACITY_CRUISER = settings.getInt("forge_capacity_cruiser"); + ForgeSettings.CAPACITY_CAPITAL = settings.getInt("forge_capacity_capital"); + + // Here: Refining Settings + ForgeSettings.ORE_TO_REFINE = Float.parseFloat(settings.getString("forge_ore_to_refine")); + ForgeSettings.METAL_PRODUCED = Float.parseFloat(settings.getString("forge_metal_produced")); + ForgeSettings.TRANSPLUTONIC_ORE_TO_REFINE = Float.parseFloat(settings.getString("forge_transplutonic_ore_to_refine")); + ForgeSettings.TRANSPLUTONICS_PRODUCED = Float.parseFloat(settings.getString("forge_transplutonics_produced")); + ForgeSettings.HEAVY_MACHINERY_REFINING_USAGE = Float.parseFloat(settings.getString("forge_heavy_machinery_refining_usage")); + + // Here: Centrifuging Settings + ForgeSettings.VOLATILES_TO_CENTRIFUGE = Float.parseFloat(settings.getString("forge_volatiles_to_centrifuge")); + ForgeSettings.FUEL_PRODUCED = Float.parseFloat(settings.getString("forge_fuel_produced")); + ForgeSettings.HEAVY_MACHINERY_CENTRIFUGING_USAGE = Float.parseFloat(settings.getString("forge_heavy_machinery_centrifuging_usage")); + + // Here: Manufacturing Settings + ForgeSettings.METAL_TO_MANUFACTURE = Float.parseFloat(settings.getString("forge_metal_to_manufacture")); + ForgeSettings.TRANSPLUTONICS_TO_MANUFACTURE = Float.parseFloat(settings.getString("forge_transplutonics_to_manufacture")); + ForgeSettings.SUPPLIES_PRODUCED = Float.parseFloat(settings.getString("forge_supplies_produced")); + ForgeSettings.HEAVY_MACHINERY_MANUFACTURING_USAGE = Float.parseFloat(settings.getString("forge_heavy_machinery_manufacturing_usage")); + + // Here: Assembling Settings + ForgeSettings.METAL_TO_ASSEMBLE = Float.parseFloat(settings.getString("forge_metal_to_assemble")); + ForgeSettings.TRANSPLUTONICS_TO_ASSEMBLE = Float.parseFloat(settings.getString("forge_transplutonics_to_assemble")); + ForgeSettings.HEAVY_MACHINERY_PRODUCED = Float.parseFloat(settings.getString("forge_heavy_machinery_produced")); + ForgeSettings.HEAVY_MACHINERY_ASSEMBLING_USAGE = Float.parseFloat(settings.getString("forge_heavy_machinery_assembling_usage")); + + // Here: Machinery Breakdown Settings + ForgeSettings.BASE_BREAKDOWN_CHANCE = Float.parseFloat(settings.getString("forge_base_heavy_machinery_breakdown_chance")); + ForgeSettings.BREAKDOWN_SEVERITY = Float.parseFloat(settings.getString("forge_heavy_machinery_breakdown_severity")); + + // Here: CR Settings + ForgeSettings.CR_PRODUCTION_DECAY = Float.parseFloat(settings.getString("forge_combat_readiness_decay_when_producing")); + + // Here: Special Item Settings + ForgeSettings.CORRUPTED_NANOFORGE_QUALITY_BONUS = Float.parseFloat(settings.getString("forge_corrupted_nanoforge_quality_bonus")); + ForgeSettings.PRISTINE_NANOFORGE_QUALITY_BONUS = Float.parseFloat(settings.getString("forge_pristine_nanoforge_quality_bonus")); + ForgeSettings.CATALYTIC_CORE_REFINING_BONUS = Float.parseFloat(settings.getString("forge_catalytic_core_refining_bonus")); + ForgeSettings.SYNCHROTRON_CORE_CENTRIFUGING_BONUS = Float.parseFloat(settings.getString("forge_synchrotron_core_centrifuging_bonus")); + ForgeSettings.CORRUPTED_NANOFORGE_MANUFACTURING_BONUS = Float.parseFloat(settings.getString("forge_corrupted_nanoforge_manufacturing_bonus")); + ForgeSettings.PRISTINE_NANOFORGE_MANUFACTURING_BONUS = Float.parseFloat(settings.getString("forge_pristine_nanoforge_manufacturing_bonus")); + ForgeSettings.CORRUPTED_NANOFORGE_ASSEMBLING_BONUS = Float.parseFloat(settings.getString("forge_corrupted_nanoforge_assembling_bonus")); + ForgeSettings.PRISTINE_NANOFORGE_ASSEMBLING_BONUS = Float.parseFloat(settings.getString("forge_pristine_nanoforge_assembling_bonus")); + + } + +} diff --git a/source/data/forge/plugins/ForgeModPlugin.java b/source/data/forge/plugins/ForgeModPlugin.java new file mode 100644 index 0000000..f21d55f --- /dev/null +++ b/source/data/forge/plugins/ForgeModPlugin.java @@ -0,0 +1,27 @@ +package data.forge.plugins; + +import java.io.IOException; +import org.json.JSONException; + +import com.fs.starfarer.api.BaseModPlugin; +import com.fs.starfarer.api.Global; + +public class ForgeModPlugin extends BaseModPlugin { + + public static final String FORGE_SETTINGS = "forge_production_settings.ini"; + public static final String FORGE_PRODUCTION_ABILITY = "forge_production"; + + @Override + public void onApplicationLoad() throws JSONException, IOException { + ForgeDataSupplier.loadSettings(FORGE_SETTINGS); + } + + @Override + public void onGameLoad(boolean newGame) { + + if(!Global.getSector().getPlayerFleet().hasAbility(FORGE_PRODUCTION_ABILITY)) { + Global.getSector().getCharacterData().addAbility(FORGE_PRODUCTION_ABILITY); + } + } + +} diff --git a/source/data/forge/plugins/ForgeSettings.java b/source/data/forge/plugins/ForgeSettings.java new file mode 100644 index 0000000..46543a5 --- /dev/null +++ b/source/data/forge/plugins/ForgeSettings.java @@ -0,0 +1,66 @@ +package data.forge.plugins; + +public class ForgeSettings { + public static boolean ENABLE_CARGO_REQUIREMENT = true; + public static boolean ENABLE_CIVGRADE_REQUIREMENT = true; + + // Here: Miscellaneous Settings + + public static boolean ENABLE_SLOW_MOVE_PENALTY = true; + public static boolean ENABLE_DETECT_AT_RANGE_PENALTY = true; + public static boolean ENABLE_BURN_ABILITIES_INCOMPATIBILITY = true; + public static float SENSOR_PROFILE_INCREASE = 100; + public static int SHIP_LIST_SIZE = 5; + + // Here: Notification Settings + public static boolean PLAY_SOUND_NOTIFICATION = true; + public static boolean SHOW_NOTIFICATION = true; + public static boolean INVERTED_GRID_NOTIFICATION = false; + public static int NOTIFICATION_INTERVAL = 3; + + // Here: Capacity Settings + public static int CAPACITY_CRUISER = 40; + public static int CAPACITY_CAPITAL = 60; + + // Here: Refining Settings + public static float ORE_TO_REFINE = 2f; + public static float METAL_PRODUCED = 1.2f; + public static float TRANSPLUTONIC_ORE_TO_REFINE = 1f; + public static float TRANSPLUTONICS_PRODUCED = 0.6f; + public static float HEAVY_MACHINERY_REFINING_USAGE = 0.6f; + + // Here: Centrifuging Settings + public static float VOLATILES_TO_CENTRIFUGE = 0.1f; + public static float FUEL_PRODUCED = 1.2f; + public static float HEAVY_MACHINERY_CENTRIFUGING_USAGE = 0.8f; + + // Here: Manufacturing Settings + public static float METAL_TO_MANUFACTURE = 0.8f; + public static float TRANSPLUTONICS_TO_MANUFACTURE = 0.2f; + public static float SUPPLIES_PRODUCED = 0.4f; + public static float HEAVY_MACHINERY_MANUFACTURING_USAGE = 1f; + + // Here: Assembling Settings + public static float METAL_TO_ASSEMBLE = 1.2f; + public static float TRANSPLUTONICS_TO_ASSEMBLE = 0.1f; + public static float HEAVY_MACHINERY_PRODUCED = 0.2f; + public static float HEAVY_MACHINERY_ASSEMBLING_USAGE = 1.6f; + + // Here: Machinery Breakdown Settings + public static float BASE_BREAKDOWN_CHANCE = 0.3f; + public static float BREAKDOWN_SEVERITY = 0.05f; + + // Here: CR Settings + public static float CR_PRODUCTION_DECAY = 0.02f; + + // Here: Special Item Settings + public static float CORRUPTED_NANOFORGE_QUALITY_BONUS = 0.8f; + public static float PRISTINE_NANOFORGE_QUALITY_BONUS = 0.5f; + public static float CATALYTIC_CORE_REFINING_BONUS = 0.2f; + public static float SYNCHROTRON_CORE_CENTRIFUGING_BONUS = 0.4f; + public static float CORRUPTED_NANOFORGE_MANUFACTURING_BONUS = 0.1f; + public static float PRISTINE_NANOFORGE_MANUFACTURING_BONUS = 0.2f; + public static float CORRUPTED_NANOFORGE_ASSEMBLING_BONUS = 0.05f; + public static float PRISTINE_NANOFORGE_ASSEMBLING_BONUS = 0.1f; + +}