001package com.fs.starfarer.api.impl.campaign; 002 003import java.util.HashMap; 004import java.util.List; 005import java.util.Map; 006 007import java.awt.Color; 008 009import com.fs.starfarer.api.Global; 010import com.fs.starfarer.api.campaign.BuffManagerAPI.Buff; 011import com.fs.starfarer.api.campaign.CampaignUIAPI.CoreUITradeMode; 012import com.fs.starfarer.api.campaign.FleetDataAPI; 013import com.fs.starfarer.api.campaign.econ.MarketAPI; 014import com.fs.starfarer.api.combat.HullModEffect; 015import com.fs.starfarer.api.combat.MutableShipStatsAPI; 016import com.fs.starfarer.api.combat.MutableStat; 017import com.fs.starfarer.api.combat.MutableStat.StatMod; 018import com.fs.starfarer.api.combat.ShipAPI; 019import com.fs.starfarer.api.combat.ShipAPI.HullSize; 020import com.fs.starfarer.api.combat.ShipVariantAPI; 021import com.fs.starfarer.api.fleet.FleetMemberAPI; 022import com.fs.starfarer.api.loading.HullModSpecAPI; 023import com.fs.starfarer.api.ui.TooltipMakerAPI; 024 025public class TowCable implements HullModEffect { 026 027 public static final String HULLMOD_ID = "tow_cable"; 028 029 public void init(HullModSpecAPI spec) { 030 031 } 032 033 public static class TowCableBuff implements Buff { 034 //public boolean wasApplied = false; 035 private String buffId; 036 private int frames = 0; 037 038 public TowCableBuff(String buffId) { 039 this.buffId = buffId; 040 } 041 042 public boolean isExpired() { 043 //return wasApplied; 044 return frames >= 2; 045 } 046 public String getId() { 047 return buffId; 048 } 049 public void apply(FleetMemberAPI member) { 050 // this ensures the buff lasts for exactly one frame unless wasApplied is reset (as it is later) 051 //wasApplied = true; 052 member.getStats().getMaxBurnLevel().modifyFlat(buffId, 1); 053 } 054 public void advance(float days) { 055 frames++; 056 } 057 }; 058 059 060 public TowCable() { 061 062 } 063 064 public void advanceInCampaign(FleetMemberAPI member, float amount) { 065 if (member.getFleetData() == null) return; 066 if (member.getFleetData().getFleet() == null) return; 067 if (!member.getFleetData().getFleet().isPlayerFleet()) return; 068 069 070 if (!member.getVariant().getHullMods().contains(HULLMOD_ID)) { 071 cleanUpTowCableBuffBy(member); 072 return; 073 } 074 075 if (!member.canBeDeployedForCombat()) { 076 cleanUpTowCableBuffBy(member); 077 return; 078 } 079 080 FleetDataAPI data = member.getFleetData(); 081 List<FleetMemberAPI> all = data.getMembersListCopy(); 082 083 int numCables = 0; 084 int thisCableIndex = -1; 085 for (FleetMemberAPI curr : all) { 086 if (!curr.canBeDeployedForCombat()) continue; 087 if (curr.getVariant().getHullMods().contains(HULLMOD_ID)) { 088 if (curr == member) { 089 thisCableIndex = numCables; 090 } 091 numCables++; 092 } 093 } 094 if (numCables <= 0 || thisCableIndex == -1) { 095 cleanUpTowCableBuffBy(member); 096 return; 097 } 098 099 TowCableBuff buff = getTowCableBuffBy(member, true); 100 Map<FleetMemberAPI, Integer> cables = new HashMap<FleetMemberAPI, Integer>(); 101 float towSpeed = member.getStats().getMaxBurnLevel().getModifiedValue(); 102 FleetMemberAPI thisCableTarget = null; 103 104 for (int cableIndex = 0; cableIndex < numCables; cableIndex++) { 105 FleetMemberAPI slowest = getSlowest(all, towSpeed, cables); 106 if (slowest == null) break; 107 //slowest.getStats().getMaxBurnLevel().getModifiedValue() 108 Integer bonus = cables.get(slowest); 109 if (bonus == null) bonus = new Integer(0); 110 bonus++; 111 cables.put(slowest, bonus); 112 113 if (cableIndex == thisCableIndex) { 114 thisCableTarget = slowest; 115 Buff existing = slowest.getBuffManager().getBuff(buff.getId()); 116 if (existing == buff) { 117 // make sure it's using the same buff rather than reapplying it, 118 // which would trigger a full stat recompute for the entire FLEET every frame 119 //buff.wasApplied = false; 120 buff.frames = 0; 121 //System.out.println("renewed on " + slowest); 122 } else { 123 //buff.wasApplied = false; 124 buff.frames = 0; 125 slowest.getBuffManager().addBuff(buff); 126 //System.out.println("Num: " + slowest.getBuffManager().getBuffs().size()); 127 //System.out.println("added to " + slowest); 128 } 129 break; 130 } 131 } 132 133 for (FleetMemberAPI curr : all) { 134 if (curr != thisCableTarget) { 135 curr.getBuffManager().removeBuff(buff.getId()); 136 } 137 } 138 } 139 140 private FleetMemberAPI getSlowest(List<FleetMemberAPI> all, float speedCutoff, Map<FleetMemberAPI, Integer> cables) { 141 FleetMemberAPI slowest = null; 142 float minLevel = Float.MAX_VALUE; 143 for (FleetMemberAPI curr : all) { 144 if (!isSuitable(curr)) continue; 145 146 float baseBurn = getMaxBurnWithoutCables(curr); 147 Integer bonus = cables.get(curr); 148 if (bonus == null) bonus = new Integer(0); 149 150 if (bonus >= getMaxCablesFor(curr)) continue; 151 152 float burnLevel = baseBurn + bonus; 153 154 if (burnLevel >= speedCutoff) continue; 155 156 if (burnLevel < minLevel) { 157 minLevel = burnLevel; 158 slowest = curr; 159 } 160 } 161 return slowest; 162 } 163 164 private int getMaxCablesFor(FleetMemberAPI member) { 165// switch (member.getHullSpec().getHullSize()) { 166// case CAPITAL_SHIP: return 4; 167// case CRUISER: return 3; 168// case DESTROYER: return 2; 169// case FRIGATE: return 1; 170// } 171 return 1; 172 } 173 174 private float getMaxBurnWithoutCables(FleetMemberAPI member) { 175 MutableStat burn = member.getStats().getMaxBurnLevel(); 176 float val = burn.getModifiedValue(); 177 float sub = 0; 178 for (StatMod mod : burn.getFlatMods().values()) { 179 if (mod.getSource().startsWith(HULLMOD_ID)) sub++; 180 } 181 return Math.max(0, val - sub); 182 } 183 184 private boolean isSuitable(FleetMemberAPI member) { 185 if (member.isFighterWing()) return false; 186 return true; 187 } 188 189 private void cleanUpTowCableBuffBy(FleetMemberAPI member) { 190 if (member.getFleetData() == null) return; 191 FleetDataAPI data = member.getFleetData(); 192 TowCableBuff buff = getTowCableBuffBy(member, false); 193 if (buff != null) { 194 for (FleetMemberAPI curr : data.getMembersListCopy()) { 195 curr.getBuffManager().removeBuff(buff.getId()); 196 } 197 } 198 } 199 200 /** 201 * One instance of the buff object per ship with a Tow Cable. 202 */ 203 public static final String TOW_CABLE_KEY = "TowCable_PersistentBuffs"; 204 205 @SuppressWarnings("unchecked") 206 private TowCableBuff getTowCableBuffBy(FleetMemberAPI member, boolean createIfMissing) { 207 Map<FleetMemberAPI, TowCableBuff> buffs; 208 if (Global.getSector().getPersistentData().containsKey(TOW_CABLE_KEY)) { 209 buffs = (Map<FleetMemberAPI, TowCableBuff>) Global.getSector().getPersistentData().get(TOW_CABLE_KEY); 210 } else { 211 buffs = new HashMap<FleetMemberAPI, TowCableBuff>(); 212 Global.getSector().getPersistentData().put(TOW_CABLE_KEY, buffs); 213 } 214 215 //new HashMap<FleetMemberAPI, TowCableBuff>(); 216 TowCableBuff buff = buffs.get(member); 217 if (buff == null && createIfMissing) { 218 String id = HULLMOD_ID + "_" + member.getId(); 219 buff = new TowCableBuff(id); 220 buffs.put(member, buff); 221 } 222 return buff; 223 } 224 225 226 public void advanceInCombat(ShipAPI ship, float amount) { 227 } 228 229 public void applyEffectsAfterShipCreation(ShipAPI ship, String id) { 230 } 231 public void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id) { 232 } 233 public boolean isApplicableToShip(ShipAPI ship) { 234 return true; 235 } 236 237 public String getDescriptionParam(int index, HullSize hullSize) { 238 return null; 239 } 240 241 public String getUnapplicableReason(ShipAPI ship) { 242 return null; 243 } 244 245 public boolean affectsOPCosts() { 246 return false; 247 } 248 249 public String getDescriptionParam(int index, HullSize hullSize, ShipAPI ship) { 250 return getDescriptionParam(index, hullSize); 251 } 252 253 public boolean canBeAddedOrRemovedNow(ShipAPI ship, MarketAPI marketOrNull, CoreUITradeMode mode) { 254 return true; 255 } 256 257 public String getCanNotBeInstalledNowReason(ShipAPI ship, MarketAPI marketOrNull, CoreUITradeMode mode) { 258 return null; 259 } 260 261 public boolean shouldAddDescriptionToTooltip(HullSize hullSize, ShipAPI ship, boolean isForModSpec) { 262 return true; 263 } 264 public void addPostDescriptionSection(TooltipMakerAPI tooltip, HullSize hullSize, ShipAPI ship, float width, boolean isForModSpec) { 265 266 } 267 268 public void applyEffectsToFighterSpawnedByShip(ShipAPI fighter, ShipAPI ship, String id) { 269 270 } 271 272 public Color getBorderColor() { 273 // TODO Auto-generated method stub 274 return null; 275 } 276 277 public Color getNameColor() { 278 // TODO Auto-generated method stub 279 return null; 280 } 281 282 public int getDisplaySortOrder() { 283 return 100; 284 } 285 286 public int getDisplayCategoryIndex() { 287 return -1; 288 } 289 290 public boolean hasSModEffectSection(HullSize hullSize, ShipAPI ship, boolean isForModSpec) { 291 return false; 292 } 293 294 public void addSModSection(TooltipMakerAPI tooltip, HullSize hullSize, ShipAPI ship, float width, 295 boolean isForModSpec) { 296 297 } 298 299 public void addSModEffectSection(TooltipMakerAPI tooltip, HullSize hullSize, ShipAPI ship, float width, 300 boolean isForModSpec, boolean isForBuildInList) { 301 // TODO Auto-generated method stub 302 303 } 304 305 public void addSModSection(TooltipMakerAPI tooltip, HullSize hullSize, ShipAPI ship, float width, 306 boolean isForModSpec, boolean isForBuildInList) { 307 // TODO Auto-generated method stub 308 309 } 310 311 public boolean hasSModEffect() { 312 // TODO Auto-generated method stub 313 return false; 314 } 315 316 public String getSModDescriptionParam(int index, HullSize hullSize) { 317 // TODO Auto-generated method stub 318 return null; 319 } 320 321 public String getSModDescriptionParam(int index, HullSize hullSize, ShipAPI ship) { 322 // TODO Auto-generated method stub 323 return null; 324 } 325 326 public float getTooltipWidth() { 327 return 0; 328 } 329 330 public boolean isSModEffectAPenalty() { 331 // TODO Auto-generated method stub 332 return false; 333 } 334 335 public boolean showInRefitScreenModPickerFor(ShipAPI ship) { 336 return true; 337 } 338 339 @Override 340 public void addRequiredItemSection(TooltipMakerAPI tooltip, FleetMemberAPI member, ShipVariantAPI currentVariant, 341 MarketAPI dockedAt, float width, boolean isForModSpec) { 342 // TODO Auto-generated method stub 343 344 } 345 346}