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