001package com.fs.starfarer.api.impl.campaign.skills; 002 003import java.util.LinkedHashSet; 004import java.util.Random; 005 006import com.fs.starfarer.api.EveryFrameScript; 007import com.fs.starfarer.api.Global; 008import com.fs.starfarer.api.campaign.CampaignFleetAPI; 009import com.fs.starfarer.api.campaign.comm.CommMessageAPI.MessageClickAction; 010import com.fs.starfarer.api.combat.ShipHullSpecAPI; 011import com.fs.starfarer.api.combat.ShipVariantAPI; 012import com.fs.starfarer.api.fleet.FleetMemberAPI; 013import com.fs.starfarer.api.impl.campaign.DModManager; 014import com.fs.starfarer.api.impl.campaign.ids.Skills; 015import com.fs.starfarer.api.impl.campaign.ids.Tags; 016import com.fs.starfarer.api.impl.campaign.intel.MessageIntel; 017import com.fs.starfarer.api.loading.HullModSpecAPI; 018import com.fs.starfarer.api.util.IntervalUtil; 019import com.fs.starfarer.api.util.Misc; 020import com.fs.starfarer.api.util.WeightedRandomPicker; 021 022/** 023 * Used for the Hull Restoration skill, but keeping the name for save compatibility. 024 * 025 * @author Alex 026 * 027 * Copyright 2021 Fractal Softworks, LLC 028 */ 029public class FieldRepairsScript implements EveryFrameScript { 030 031 public static int MONTHS_PER_DMOD_REMOVAL = 1; 032 033 public static float RATE_DP_MAX = 40f; 034 public static float RATE_DP_MIN = 4f; 035 public static float MAX_RATE_MULT = 3f; 036 037 public static boolean REMOVE_DMOD_FROM_NEW_SHIPS = true; 038 public static float MIN_NEW_REMOVE_PROB = 0.2f; 039 public static float NEW_REMOVE_PROB_PER_DMOD = 0.2f; 040 041 protected IntervalUtil tracker = new IntervalUtil(10f, 20f); 042 protected IntervalUtil tracker2 = new IntervalUtil(3f, 5f); 043 044 protected FleetMemberAPI pickedNew = null; 045 protected String dmodNew = null; 046 protected Random newRandom = new Random(Misc.genRandomSeed()); 047 protected LinkedHashSet<String> seen = new LinkedHashSet<String>(); 048 049 protected FleetMemberAPI picked = null; 050 protected String dmod = null; 051 052 Object readResolve() { 053 if (seen == null) { 054 seen = new LinkedHashSet<String>(); 055 } 056 if (tracker2 == null) { 057 tracker2 = new IntervalUtil(3f, 5f); 058 } 059 if (newRandom == null) { 060 newRandom = new Random(Misc.genRandomSeed()); 061 } 062 return this; 063 } 064 065 public void advance(float amount) { 066 067// System.out.println(RecoverAPlanetkiller.getNexus().getContainingLocation().getName() + 068// " " + RecoverAPlanetkiller.getNexus().getContainingLocation().getLocation()); 069 070 CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 071 if (fleet == null) return; 072 073 if (Global.getSector().getPlayerStats().getSkillLevel(Skills.HULL_RESTORATION) <= 0) { 074 picked = null; 075 dmod = null; 076 return; 077 } 078 079 float days = Global.getSector().getClock().convertToDays(amount); 080 float rateMult = 1f / (float) MONTHS_PER_DMOD_REMOVAL; 081 //days *= 100f; 082 if (picked != null) { 083 float dp = picked.getDeploymentPointsCost(); 084 float f = (dp - RATE_DP_MIN) / (RATE_DP_MAX - RATE_DP_MIN); 085 f = 1 - f; 086 if (f > 1f) f = 1f; 087 if (f < 0f) f = 0f; 088 089 rateMult *= 1f + (MAX_RATE_MULT - 1f) * f; 090 } 091 tracker.advance(days * rateMult * 0.5f); // * 0.5f since the tracker interval averages 15 days 092 if (tracker.intervalElapsed()) { 093 // if picked ship is no longer present in the fleet when it's time to remove the d-mod, 094 // don't remove a d-mod at all 095 if (picked == null || dmod == null) { 096 pickNext(); 097 } else { 098 if (fleet.getFleetData().getMembersListCopy().contains(picked) && 099 DModManager.getNumNonBuiltInDMods(picked.getVariant()) > 0) { 100 101 DModManager.removeDMod(picked.getVariant(), dmod); 102 103 HullModSpecAPI spec = DModManager.getMod(dmod); 104 MessageIntel intel = new MessageIntel(picked.getShipName() + " - repaired " + spec.getDisplayName(), Misc.getBasePlayerColor()); 105 intel.setIcon(Global.getSettings().getSpriteName("intel", "repairs_finished")); 106 Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.REFIT_TAB, picked); 107 108 int dmods = DModManager.getNumNonBuiltInDMods(picked.getVariant()); 109 if (dmods <= 0) { 110 restoreToNonDHull(picked.getVariant()); 111 } 112 } 113 picked = null; 114 pickNext(); 115 } 116 } 117 118 tracker2.advance(days); 119 if (tracker2.intervalElapsed() && REMOVE_DMOD_FROM_NEW_SHIPS) { 120 if (pickedNew == null || dmodNew == null) { 121 pickNextNew(); 122 } else { 123 seen.add(pickedNew.getId()); 124 125 float numDmods = DModManager.getNumNonBuiltInDMods(pickedNew.getVariant()); 126 if (fleet.getFleetData().getMembersListCopy().contains(pickedNew) && numDmods > 0) { 127 float probRemove = MIN_NEW_REMOVE_PROB + numDmods * NEW_REMOVE_PROB_PER_DMOD; 128 if (newRandom.nextFloat() < probRemove) { 129 DModManager.removeDMod(pickedNew.getVariant(), dmodNew); 130 131 HullModSpecAPI spec = DModManager.getMod(dmodNew); 132 MessageIntel intel = new MessageIntel(pickedNew.getShipName() + " - repaired " + spec.getDisplayName(), Misc.getBasePlayerColor()); 133 intel.setIcon(Global.getSettings().getSpriteName("intel", "repairs_finished")); 134 Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.REFIT_TAB, pickedNew); 135 136 int dmods = DModManager.getNumNonBuiltInDMods(pickedNew.getVariant()); 137 if (dmods <= 0) { 138 restoreToNonDHull(pickedNew.getVariant()); 139 } 140 } 141 } 142 pickedNew = null; 143 pickNextNew(); 144 } 145 } 146 } 147 148 public void pickNext() { 149 picked = null; 150 dmod = null; 151 152 CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 153 WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>(); 154 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 155 if (member.getVariant().isStockVariant()) continue; 156 if (member.isMothballed()) continue; 157 if (member.getHullSpec().hasTag(Tags.HULL_UNRESTORABLE) || 158 member.getVariant().hasTag(Tags.VARIANT_UNRESTORABLE)) continue; 159 int dmods = DModManager.getNumNonBuiltInDMods(member.getVariant()); 160 if (dmods > 0) { 161 picker.add(member, 1); 162 } 163 } 164 picked = picker.pick(); 165 166 if (picked != null) { 167 ShipVariantAPI variant = picked.getVariant(); 168 WeightedRandomPicker<String> modPicker = new WeightedRandomPicker<String>(); 169 for (String id : variant.getHullMods()) { 170 if (DModManager.getMod(id).hasTag(Tags.HULLMOD_DMOD)) { 171 if (variant.getHullSpec().getBuiltInMods().contains(id)) continue; 172 modPicker.add(id); 173 } 174 } 175 dmod = modPicker.pick(); 176 if (dmod == null) { 177 picked = null; 178 } 179 } 180 } 181 182 public void pickNextNew() { 183 pickedNew = null; 184 dmodNew = null; 185 186 CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 187 WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>(); 188 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 189 if (member.getVariant().isStockVariant() || member.isMothballed() || 190 member.getHullSpec().hasTag(Tags.HULL_UNRESTORABLE) || 191 member.getVariant().hasTag(Tags.VARIANT_UNRESTORABLE)) { 192 seen.add(member.getId()); 193 continue; 194 } 195 if (seen.contains(member.getId())) continue; 196 int dmods = DModManager.getNumNonBuiltInDMods(member.getVariant()); 197 if (dmods > 0) { 198 picker.add(member, 1); 199 } else { 200 seen.add(member.getId()); 201 } 202 } 203 pickedNew = picker.pick(); 204 205 if (pickedNew != null) { 206 ShipVariantAPI variant = pickedNew.getVariant(); 207 WeightedRandomPicker<String> modPicker = new WeightedRandomPicker<String>(); 208 for (String id : variant.getHullMods()) { 209 if (DModManager.getMod(id).hasTag(Tags.HULLMOD_DMOD)) { 210 if (variant.getHullSpec().getBuiltInMods().contains(id)) continue; 211 modPicker.add(id); 212 } 213 } 214 dmodNew = modPicker.pick(); 215 if (dmodNew == null) { 216 pickedNew = null; 217 } 218 } 219 } 220 221 public boolean isDone() { 222 return false; 223 } 224 225 public boolean runWhilePaused() { 226 return false; 227 } 228 229 230 public static ShipHullSpecAPI getBaseNonDHullFor(ShipVariantAPI v) { 231 ShipHullSpecAPI base = v.getHullSpec().getDParentHull(); 232 233 // so that a skin with dmods can be "restored" - i.e. just dmods suppressed w/o changing to 234 // actual base skin 235 //if (!v.getHullSpec().isDHull()) base = v.getHullSpec(); 236 if (!v.getHullSpec().isDefaultDHull() && !v.getHullSpec().isRestoreToBase()) base = v.getHullSpec(); 237 238 if (base == null && v.getHullSpec().isRestoreToBase()) { 239 base = v.getHullSpec().getBaseHull(); 240 } 241 return base; 242 } 243 244 public static void restoreToNonDHull(ShipVariantAPI v) { 245 ShipHullSpecAPI base = getBaseNonDHullFor(v); 246 247 if (base != null) { 248 //v.clearPermaMods(); 249 v.setHullSpecAPI(base); 250 } 251 } 252} 253 254