Starsector API
Loading...
Searching...
No Matches
ConvertedHangar.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.hullmods;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.LinkedHashSet;
6import java.util.List;
7import java.util.Set;
8
9import com.fs.starfarer.api.Global;
10import com.fs.starfarer.api.combat.BaseHullMod;
11import com.fs.starfarer.api.combat.MutableShipStatsAPI;
12import com.fs.starfarer.api.combat.ShipAPI;
13import com.fs.starfarer.api.combat.ShipAPI.HullSize;
14import com.fs.starfarer.api.graphics.SpriteAPI;
15import com.fs.starfarer.api.impl.campaign.ids.HullMods;
16import com.fs.starfarer.api.impl.campaign.ids.Stats;
17import com.fs.starfarer.api.impl.campaign.ids.Strings;
18import com.fs.starfarer.api.loading.FighterWingSpecAPI;
19import com.fs.starfarer.api.ui.Alignment;
20import com.fs.starfarer.api.ui.TooltipMakerAPI;
21import com.fs.starfarer.api.util.Misc;
22
23public class ConvertedHangar extends BaseHullMod {
24
25 public static float FIGHTER_OP_PER_DP = 5;
26 public static int MIN_DP = 1;
27 public static float REPLACEMENT_TIME_MULT = 1.5f;
28 public static int CREW_REQ = 20;
29
30 //public static float EXTRA_REARM_TIME = 5f;
31 public static float REARM_TIME_FRACTION = 0.4f;
32
33 public static float SMOD_CRUISER = 10f;
34 public static float SMOD_CAPITAL = 25f;
35
36 public static float CR_THRESHOLD_UNINSTALLABLE = 70;
37
38
39
40 //public static final int CARGO_REQ = 80;
41// public static final int ALL_FIGHTER_COST_PERCENT = 50;
42// public static final int BOMBER_COST_PERCENT = 100;
43
44// private static Map mag = new HashMap();
45// static {
46// mag.put(HullSize.FRIGATE, 0f);
47// mag.put(HullSize.DESTROYER, 75f);
48// mag.put(HullSize.CRUISER, 50f);
49// mag.put(HullSize.CAPITAL_SHIP, 25f);
50// }
51
52 public static int computeDPModifier(float fighterOPCost) {
53 int mod = (int) Math.ceil(fighterOPCost / FIGHTER_OP_PER_DP);
54 if (mod < MIN_DP) mod = MIN_DP;
55 return mod;
56 }
57
58 public static float getFighterOPCost(MutableShipStatsAPI stats) {
59 float cost = 0;
60 for (String wingId : getFighterWings(stats)) {
61 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(wingId);
62 cost += spec.getOpCost(stats);
63 }
64 return cost;
65 }
66
67 public static List<String> getFighterWings(MutableShipStatsAPI stats) {
68 if (stats.getVariant() != null) {
69 return stats.getVariant().getFittedWings();
70 }
71 return new ArrayList<String>();
72// if (stats.getEntity() instanceof ShipAPI) {
73// ShipAPI ship = (ShipAPI) stats.getEntity();
74// } else {
75// FleetMemberAPI member = stats.getFleetMember();
76// }
77 }
78
79 public float computeCRMult(float suppliesPerDep, float dpMod) {
80 return 1f + dpMod / suppliesPerDep;
81 }
82
83 public void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id) {
84 //stats.getFighterRefitTimeMult().modifyPercent(id, ((Float) mag.get(hullSize)));
85 float numBays = 1f;
86 numBays += stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_MOD).computeEffective(0f);
87 stats.getNumFighterBays().modifyFlat(id, numBays);
88
89 boolean sMod = isSMod(stats);
90 if (sMod) {
91 float bonus = 0f;
92 if (hullSize == HullSize.CRUISER) bonus = SMOD_CRUISER;
93 else if (hullSize == HullSize.CAPITAL_SHIP) bonus = SMOD_CAPITAL;
94 if (bonus != 0) {
95 stats.getDynamic().getStat(Stats.REPLACEMENT_RATE_INCREASE_MULT).modifyPercent(id, bonus);
96 }
97 }
98
99 boolean crewIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_CREW_INCREASE).computeEffective(0f) <= 0;
100 boolean rearmIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_REARM_INCREASE).computeEffective(0f) <= 0;
101 boolean dpIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_DP_INCREASE).computeEffective(0f) <= 0;
102 boolean refitPenalty = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_REFIT_PENALTY).computeEffective(0f) <= 0;
103
104 if (refitPenalty) {
105 stats.getFighterRefitTimeMult().modifyMult(id, REPLACEMENT_TIME_MULT);
106 stats.getDynamic().getStat(Stats.REPLACEMENT_RATE_DECREASE_MULT).modifyMult(id, 1f / REPLACEMENT_TIME_MULT);
107 stats.getDynamic().getStat(Stats.REPLACEMENT_RATE_INCREASE_MULT).modifyMult(id, 1f / REPLACEMENT_TIME_MULT);
108 }
109
110 if (rearmIncrease) {
111 //stats.getDynamic().getMod(Stats.FIGHTER_REARM_TIME_EXTRA_FLAT_MOD).modifyFlat(id, EXTRA_REARM_TIME);
112 stats.getDynamic().getMod(Stats.FIGHTER_REARM_TIME_EXTRA_FRACTION_OF_BASE_REFIT_TIME_MOD).modifyFlat(id, REARM_TIME_FRACTION);
113 }
114
115 if (dpIncrease) {
116 float dpMod = computeDPModifier(getFighterOPCost(stats));
117 if (dpMod > 0) {
118 stats.getDynamic().getMod(Stats.DEPLOYMENT_POINTS_MOD).modifyFlat(id, dpMod);
119
120 if (stats.getFleetMember() != null) {
121 float perDep = stats.getFleetMember().getHullSpec().getSuppliesToRecover();
122 float mult = computeCRMult(perDep, dpMod);
123 stats.getCRPerDeploymentPercent().modifyMult(id, mult);
124 }
125
126 stats.getSuppliesToRecover().modifyFlat(id, dpMod);
127 }
128 }
129
130 if (crewIncrease) {
131 stats.getMinCrewMod().modifyFlat(id, CREW_REQ);
132 }
133
134
135// boolean costIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_COST_INCREASE).computeEffective(0f) <= 0;
136// //costIncrease = false;
137// if (costIncrease) {
138// stats.getMinCrewMod().modifyFlat(id, CREW_REQ);
139// //stats.getDynamic().getMod(Stats.ALL_FIGHTER_COST_MOD).modifyPercent(id, ALL_FIGHTER_COST_PERCENT);
140// stats.getDynamic().getMod(Stats.BOMBER_COST_MOD).modifyPercent(id, BOMBER_COST_PERCENT);
141// stats.getDynamic().getMod(Stats.FIGHTER_COST_MOD).modifyPercent(id, ALL_FIGHTER_COST_PERCENT);
142// stats.getDynamic().getMod(Stats.INTERCEPTOR_COST_MOD).modifyPercent(id, ALL_FIGHTER_COST_PERCENT);
143// stats.getDynamic().getMod(Stats.SUPPORT_COST_MOD).modifyPercent(id, ALL_FIGHTER_COST_PERCENT);
144// }
145 //stats.getCargoMod().modifyFlat(id, -CARGO_REQ);
146 }
147
148 public boolean isApplicableToShip(ShipAPI ship) {
149 //if (ship.getMutableStats().getCargoMod().computeEffective(ship.getHullSpec().getCargo()) < CARGO_REQ) return false;
150 if (ship != null && ship.getHullSpec().getCRToDeploy() > CR_THRESHOLD_UNINSTALLABLE) {
151 return false;
152 }
153 return ship != null && !ship.isFrigate() && ship.getHullSpec().getFighterBays() <= 0 &&
154 //ship.getNumFighterBays() <= 0 &&
155 !ship.getVariant().hasHullMod(HullMods.CONVERTED_BAY) &&
156 !ship.getHullSpec().isPhase();
157 }
158
159 public String getUnapplicableReason(ShipAPI ship) {
160 if (ship != null && ship.getHullSpec().getCRToDeploy() > CR_THRESHOLD_UNINSTALLABLE) {
161 return "Ship's combat readiness lost per deployment is too high";
162 }
163 if (ship != null && ship.isFrigate()) return "Can not be installed on a frigate";
164 if (ship != null && ship.getHullSpec().getFighterBays() > 0) return "Ship has standard fighter bays";
165 if (ship != null && ship.getVariant().hasHullMod(HullMods.CONVERTED_BAY)) return "Ship has fighter bays";
166 //if (ship != null && ship.getNumFighterBays() > 0) return "Ship has fighter bays";
167 return "Can not be installed on a phase ship";
168 }
169
170 public void applyEffectsToFighterSpawnedByShip(ShipAPI fighter, ShipAPI ship, String id) {
171 //setFighterSkin(fighter, ship);
172// boolean statsPenalty = ship.getMutableStats().getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_PERFORMANCE_PENALTY).computeEffective(0f) <= 0;
173// boolean sMod = isSMod(ship);
174// if (statsPenalty && !sMod) {
175// new DefectiveManufactory().applyEffectsToFighterSpawnedByShip(fighter, ship, id);
176// }
177 }
178
179
180 public static void setFighterSkin(ShipAPI fighter, ShipAPI carrier) {
181 SpriteAPI sprite = getFighterSkin(fighter, carrier);
182 if (sprite != null) {
183 fighter.setSprite(sprite);
184 }
185 }
186
187 public static SpriteAPI getFighterSkin(ShipAPI fighter, ShipAPI carrier) {
188 if (carrier.getHullStyleId().equals(fighter.getHullStyleId())) {
189 return null;
190 }
191 String cat = null;
192 SpriteAPI skin = null;
193 if (carrier.getOwner() == 0 || carrier.getOriginalOwner() == 0) {
194 cat = "fighterSkinsPlayerOnly";
195 skin = getFighterSkin(cat, fighter, carrier);
196 }
197 if (skin != null) return skin;
198
199 cat = "fighterSkinsPlayerAndNPC";
200 skin = getFighterSkin(cat, fighter, carrier);
201 return skin;
202 }
203
204
205 public static SpriteAPI getFighterSkin(String cat, ShipAPI fighter, ShipAPI carrier) {
206
207 String exclude = "fighterSkinsExcludeFromSharing";
208 String id = fighter.getHullSpec().getHullId();
209 String style = carrier.getHullStyleId();
210
211 List<String> skins = Global.getSettings().getSpriteKeys(cat);
212 Set<String> noSharing = new LinkedHashSet<String>(Global.getSettings().getSpriteKeys(exclude));
213
214 List<SpriteAPI> matching = new ArrayList<SpriteAPI>();
215 for (String key : skins) {
216 if (key.equals(id + "_" + style)) {
217 return Global.getSettings().getSprite(cat, key);
218 }
219 if (key.startsWith(id) && !noSharing.contains(key)) {
220 matching.add(Global.getSettings().getSprite(cat, key));
221 }
222 }
223
224 if (!matching.isEmpty()) {
225 SpriteAPI best = null;
226 float minDist = Float.MAX_VALUE;
227
228 for (SpriteAPI curr : matching) {
229 float dist = Misc.getColorDist(carrier.getSpriteAPI().getAverageBrightColor(), curr.getAverageBrightColor());
230 if (dist < minDist) {
231 best = curr;
232 minDist = dist;
233 }
234 }
235 return best;
236 }
237 return null;
238 }
239
240
241// public String getDescriptionParam(int index, HullSize hullSize, ShipAPI ship) {
242// if (index == 2) return "" + CREW_REQ;
243// if (index == 3) return "" + BOMBER_COST_PERCENT + "%";
244// if (index == 4) return "" + ALL_FIGHTER_COST_PERCENT + "%";
245// return new DefectiveManufactory().getDescriptionParam(index, hullSize, ship);
251// //if (index == 0) return "" + ((Float) mag.get(hullSize)).intValue();
252// //return null;
253// }
254
255// @Override
256// public boolean affectsOPCosts() {
257// return true;
258// }
259
260 @Override
261 public boolean shouldAddDescriptionToTooltip(HullSize hullSize, ShipAPI ship, boolean isForModSpec) {
262 return false;
263 }
264
265 @Override
266 public void addPostDescriptionSection(TooltipMakerAPI tooltip, HullSize hullSize, final ShipAPI ship, float width, boolean isForModSpec) {
267 float pad = 3f;
268 float opad = 10f;
269 Color h = Misc.getHighlightColor();
270 Color bad = Misc.getNegativeHighlightColor();
271
272
273 tooltip.addPara("Converts the ship's standard shuttle hangar to house a fighter bay. "
274 + "The improvised flight deck, its crew, and the related machinery all function "
275 + "at a pace below that of a dedicated carrier.", opad);
276
277
278// tooltip.addPara("Increases fighter refit time by %s, "
279// + "and the fighter replacement rate both decays and recovers %s more slowly. "
280// + "In addition, bombers returning to rearm take %s seconds longer to relaunch. "
281// + "Increases the minimum crew by %s to account for pilots and flight crews.", opad, h,
282// "" + Misc.getRoundedValueMaxOneAfterDecimal(REPLACEMENT_TIME_MULT) + Strings.X,
283// "" + Misc.getRoundedValueMaxOneAfterDecimal(REPLACEMENT_TIME_MULT) + Strings.X,
284// "" + (int) EXTRA_REARM_TIME,
285// "" + (int) CREW_REQ);
286 tooltip.addPara("Increases fighter refit time by %s, "
287 + "and the fighter replacement rate both decays and recovers %s more slowly. "
288 + "In addition, bombers returning to rearm (or fighters returning for repairs) "
289 + "take %s of their base refit time to relaunch, "
290 + "where normally it takes under a second. "
291 + "", opad, h,
292 "" + Misc.getRoundedValueMaxOneAfterDecimal(REPLACEMENT_TIME_MULT) + Strings.X,
293 "" + Misc.getRoundedValueMaxOneAfterDecimal(REPLACEMENT_TIME_MULT) + Strings.X,
294 "" + (int) Math.round(REARM_TIME_FRACTION * 100f) + "%");
295
296
297 tooltip.addPara("Increases the minimum crew by %s to account for pilots and flight crews. "
298 + "Increases the ship's deployment points and supply cost to recover "
299 + "from deployment by %s for every %s ordnance points spent on "
300 + "fighters, or by at least %s point. This comes with a proportional increase "
301 + "in combat readiness lost per deployment.", opad, h,
302 "" + (int) CREW_REQ,
303 "1",
304 "" + (int) + FIGHTER_OP_PER_DP,
305 "" + (int) + MIN_DP);
306
307 if (isForModSpec || ship == null || ship.getMutableStats() == null) return;
308
309 MutableShipStatsAPI stats = ship.getMutableStats();
310 boolean crewIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_CREW_INCREASE).computeEffective(0f) <= 0;
311 boolean rearmIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_REARM_INCREASE).computeEffective(0f) <= 0;
312 boolean dpIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_DP_INCREASE).computeEffective(0f) <= 0;
313 boolean refitPenalty = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_REFIT_PENALTY).computeEffective(0f) <= 0;
314
315 int dpMod = computeDPModifier(getFighterOPCost(stats));
316 if (dpMod > 0) {
317 //tooltip.addSectionHeading("Fighter wings", Alignment.MID, opad);
318 //tooltip.addPara("%s points for the currently installed fighter wing.", opad, h, "+" + dpMod);
319 if (dpIncrease) {
320// float perDep = stats.getFleetMember().getHullSpec().getCRToDeploy();
321// 1f + dpMod / perDep);
322 tooltip.addPara("Deployment cost: %s", opad, h, "+" + dpMod);
323 }
324
325 float numW = 160f;
326 float sizeW = width - numW - 10f;
327
328 if (!getFighterWings(stats).isEmpty() && rearmIncrease) {
329 tooltip.beginTable(Misc.getBasePlayerColor(), Misc.getDarkPlayerColor(), Misc.getBrightPlayerColor(),
330 20f, true, true,
331 new Object [] {"Wing", sizeW, "Seconds to relaunch", numW});
332
333 for (String wingId : getFighterWings(stats)) {
334 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(wingId);
335 float refitPortion = spec.getRefitTime() *
336 ship.getMutableStats().getDynamic().getValue(Stats.FIGHTER_REARM_TIME_EXTRA_FRACTION_OF_BASE_REFIT_TIME_MOD, 0f);
337
338 Color c = Misc.getTextColor();
339 //c = Misc.getHighlightColor();
340 tooltip.addRow(Alignment.MID, c, spec.getWingName(),
341 Alignment.MID, h, Misc.getRoundedValueOneAfterDecimalIfNotWhole(refitPortion)
342 );
343 }
344 tooltip.addTable("", 0, opad);
345 }
346
347 }
348
349// boolean crewIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_CREW_INCREASE).computeEffective(0f) <= 0;
350// boolean rearmIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_REARM_INCREASE).computeEffective(0f) <= 0;
351// boolean dpIncrease = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_DP_INCREASE).computeEffective(0f) <= 0;
352// boolean refitPenalty = stats.getDynamic().getMod(Stats.CONVERTED_HANGAR_NO_REFIT_PENALTY).computeEffective(0f) <= 0;
353
354 List<String> negated = new ArrayList<String>();
355 if (!refitPenalty) negated.add("refit time and rate recovery modifiers");
356 if (!rearmIncrease) negated.add("relaunch delay");
357 if (!crewIncrease) negated.add("increased crew requirement");
358 if (!dpIncrease) negated.add("deployment cost increase");
359
360 if (!negated.isEmpty()) {
361 Color c = Misc.getPositiveHighlightColor();
362 String isOrAre = "is";
363 if (negated.size() > 1) isOrAre = "are";
364 if (negated.size() >= 4) isOrAre += " all";
365 tooltip.addPara("The " + Misc.getAndJoined(negated) + " " + isOrAre + " negated on this ship.", c, opad);
366 }
367
368 //tooltip.setBgAlpha(0.9f);
369
370
371 }
372
373 @Override
374 public String getSModDescriptionParam(int index, HullSize hullSize, ShipAPI ship) {
375 if (index == 0) return "" + (int) SMOD_CRUISER + "%";
376 if (index == 1) return "" + (int) SMOD_CAPITAL + "%";
377 return null;
378 }
379
380}
381
382
383
static SettingsAPI getSettings()
Definition Global.java:51
void applyEffectsToFighterSpawnedByShip(ShipAPI fighter, ShipAPI ship, String id)
static SpriteAPI getFighterSkin(ShipAPI fighter, ShipAPI carrier)
String getSModDescriptionParam(int index, HullSize hullSize, ShipAPI ship)
void addPostDescriptionSection(TooltipMakerAPI tooltip, HullSize hullSize, final ShipAPI ship, float width, boolean isForModSpec)
static void setFighterSkin(ShipAPI fighter, ShipAPI carrier)
static SpriteAPI getFighterSkin(String cat, ShipAPI fighter, ShipAPI carrier)
boolean shouldAddDescriptionToTooltip(HullSize hullSize, ShipAPI ship, boolean isForModSpec)
static List< String > getFighterWings(MutableShipStatsAPI stats)
float computeCRMult(float suppliesPerDep, float dpMod)
static float getFighterOPCost(MutableShipStatsAPI stats)
void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id)
List< String > getSpriteKeys(String category)
FighterWingSpecAPI getFighterWingSpec(String wingId)
SpriteAPI getSprite(String filename)