Starsector API
Loading...
Searching...
No Matches
CRPluginImpl.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat;
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.List;
6
7import com.fs.starfarer.api.Global;
8import com.fs.starfarer.api.campaign.FleetDataAPI;
9import com.fs.starfarer.api.combat.CombatReadinessPlugin;
10import com.fs.starfarer.api.combat.FighterLaunchBayAPI;
11import com.fs.starfarer.api.combat.MutableShipStatsAPI;
12import com.fs.starfarer.api.combat.MutableStat.StatMod;
13import com.fs.starfarer.api.combat.ShipAPI;
14import com.fs.starfarer.api.combat.ShipAPI.HullSize;
15import com.fs.starfarer.api.combat.ShipEngineControllerAPI.ShipEngineAPI;
16import com.fs.starfarer.api.combat.WeaponAPI;
17import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
18import com.fs.starfarer.api.fleet.FleetMemberAPI;
19import com.fs.starfarer.api.impl.campaign.ids.Stats;
20import com.fs.starfarer.api.loading.WeaponSpecAPI;
21
22public class CRPluginImpl implements CombatReadinessPlugin {
23
24 public static float NO_SYSTEM_THRESHOLD = 0.0f;
25
26 public static float IMPROVE_START = 0.7f;
27 public static float DEGRADE_START = 0.5f;
28 public static float SHIELD_MALFUNCTION_START = 0.1f;
29 public static float CRITICAL_MALFUNCTION_START = 0.2f;
30 public static float MALFUNCTION_START = 0.4f;
31 public static float MISSILE_AMMO_REDUCTION_START = 0.4f;
32
33 public static float MAX_MOVEMENT_CHANGE = 10f; // percent
34 public static float MAX_DAMAGE_TAKEN_CHANGE = 10f; // percent
35 //public static float MAX_ROF_CHANGE = 25f; // percent
36 public static float MAX_DAMAGE_CHANGE = 10f; // percent
37 public static float MAX_REFIT_CHANGE = 10f; // percent
38
39 public static float MAX_SHIELD_MALFUNCTION_CHANCE = 5f; // percent
40 public static float MAX_CRITICAL_MALFUNCTION_CHANCE = 25f; // percent
41 public static float MAX_ENGINE_MALFUNCTION_CHANCE = 7.5f; // percent
42 public static float MAX_WEAPON_MALFUNCTION_CHANCE = 10f; // percent
43
44
45 public void applyMaxCRCrewModifiers(FleetMemberAPI member) {
46 if (member == null || member.getStats() == null) return;
47 //float maxCRBasedOnLevel = (40f + member.getCrewFraction() * 10f) / 100f;
48 float maxCRBasedOnLevel = 0.7f;
49 member.getStats().getMaxCombatReadiness().modifyFlat("crew skill bonus", maxCRBasedOnLevel, "Basic maintenance");
50
51 float cf = member.getCrewFraction();
52 if (cf < 1) {
53 float penalty = 0.5f * (1f - cf);
54 //float penalty = (1f - cf);
55 member.getStats().getMaxCombatReadiness().modifyFlat("crew understrength", -penalty, "Crew understrength");
56 } else {
57 member.getStats().getMaxCombatReadiness().unmodifyFlat("crew understrength");
58 }
59 }
60
61
62
63 public List<CRStatusItemData> getCRStatusDataForShip(ShipAPI ship) {
64 float startingCR = ship.getCRAtDeployment();
65 float cr = ship.getCurrentCR();
66
67 List<CRStatusItemData> list = new ArrayList<CRStatusItemData>();
68
69 String icon = null;
70
71 if (cr > getImproveThreshold(ship.getMutableStats())) {
72 icon = Global.getSettings().getSpriteName("ui", "icon_tactical_cr_bonus");
73 } else if (cr < getDegradeThreshold(ship.getMutableStats())) {
74 icon = Global.getSettings().getSpriteName("ui", "icon_tactical_cr_penalty");
75 } else {
76 icon = Global.getSettings().getSpriteName("ui", "icon_tactical_cr_neutral");
77 //return list;
78 }
79
80 String title = "Combat Readiness " + Math.round(cr * 100f) + "%";
81
82 String malfStr = getMalfunctionString(ship.getMutableStats(), cr);
83
84 if (cr <= NO_SYSTEM_THRESHOLD && ship.getShield() != null) {
85 CRStatusItemData itemData = new CRStatusItemData(statusKeys[9], icon, title,
86 "Shields offline", true);
87 list.add(itemData);
88 }
89
90 if (cr <= NO_SYSTEM_THRESHOLD) {
91 boolean hasWings = false;
92 for (FighterLaunchBayAPI bay : ship.getLaunchBaysCopy()) {
93 if (bay.getWing() != null) {
94 hasWings = true;
95 break;
96 }
97 }
98 if (hasWings) {
99 CRStatusItemData itemData = new CRStatusItemData(statusKeys[10], icon, title,
100 "Fighter bays offline", true);
101 list.add(itemData);
102 }
103 }
104
105 if (cr <= NO_SYSTEM_THRESHOLD && ship.getPhaseCloak() != null) {
106 CRStatusItemData itemData = new CRStatusItemData(statusKeys[8], icon, title,
107 ship.getPhaseCloak().getDisplayName() + " offline", true);
108 list.add(itemData);
109 }
110
111 if (cr <= NO_SYSTEM_THRESHOLD && ship.getSystem() != null) {
112 CRStatusItemData itemData = new CRStatusItemData(statusKeys[7], icon, title,
113 ship.getSystem().getDisplayName() + " offline", true);
114 list.add(itemData);
115 }
116
117 if (cr < getMalfunctionThreshold(ship.getMutableStats())) {
118 CRStatusItemData itemData = new CRStatusItemData(statusKeys[0], icon, title,
119 "malfunction risk: " + malfStr, true);
120 list.add(itemData);
121 }
122 if (startingCR < getMissileAmmoReductionThreshold(ship.getMutableStats())) {
123 CRStatusItemData itemData = new CRStatusItemData(statusKeys[2], icon, title,
124 "missiles not fully loaded", true);
125 list.add(itemData);
126 }
127
128 if (cr < getDegradeThreshold(ship.getMutableStats())) {
129 CRStatusItemData itemData = new CRStatusItemData(statusKeys[3], icon, title,
130 "degraded performance", true);
131 list.add(itemData);
132 } else if (cr > getImproveThreshold(ship.getMutableStats())) {
133 CRStatusItemData itemData = new CRStatusItemData(statusKeys[4], icon, title,
134 "improved performance", false);
135 list.add(itemData);
136 } else {
137 CRStatusItemData itemData = new CRStatusItemData(statusKeys[8], icon, title,
138 "standard performance", false);
139 list.add(itemData);
140 }
141
142 if (ship.losesCRDuringCombat() && cr > 0) {
143 //float noLossTime = ship.getHullSpec().getNoCRLossTime();
144 float noLossTime = ship.getMutableStats().getPeakCRDuration().computeEffective(ship.getHullSpec().getNoCRLossTime());
145 if (noLossTime > ship.getTimeDeployedForCRReduction()) {
146 CRStatusItemData itemData = new CRStatusItemData(statusKeys[5], icon, "peak active performance",
147 "remaining time: " + (int) (noLossTime - ship.getTimeDeployedForCRReduction()) + " sec", false);
148 list.add(itemData);
149 } else {
150 CRStatusItemData itemData = new CRStatusItemData(statusKeys[6], icon, "combat stresses",
151 "degrading readiness", true);
152 list.add(itemData);
153 }
154 }
155
156 return list;
157 }
158
159
160 private float getWeaponMalfuctionPercent(MutableShipStatsAPI stats, float cr) {
162 }
163 private float getEngineMalfuctionPercent(MutableShipStatsAPI stats, float cr) {
165 }
166 private float getCriticalMalfuctionPercent(MutableShipStatsAPI stats, float cr) {
168 }
169 private float getShieldMalfuctionPercent(MutableShipStatsAPI stats, float cr) {
171 }
172
173
174 private float getMovementChangePercent(MutableShipStatsAPI stats, float cr) {
175// if (cr > 0) {
176// System.out.println("wefwefe");
177// }
178 float movementChange = 0f;
179 float d = getDegradeThreshold(stats);
180 float i = getImproveThreshold(stats);
181 if (cr < d) {
182 float f = (d - cr) / d;
183 movementChange = -1f * f * MAX_MOVEMENT_CHANGE;
184 } else if (cr > i) {
185 float f = (cr - i) / (1f - i);
186 movementChange = 1f * f * MAX_MOVEMENT_CHANGE;
187 }
188 return movementChange;
189 }
190
191 private float getDamageTakenChangePercent(MutableShipStatsAPI stats, float cr) {
192 float damageTakenChange = 0f;
193 float d = getDegradeThreshold(stats);
194 float i = getImproveThreshold(stats);
195 if (cr < d) {
196 float f = (d - cr) / d;
197 damageTakenChange = 1f * f * MAX_DAMAGE_TAKEN_CHANGE;
198 } else if (cr > i) {
199 float f = (cr - i) / (1f - i);
200 damageTakenChange = -1f * f * MAX_DAMAGE_TAKEN_CHANGE;
201 }
202 return damageTakenChange;
203 }
204
205 private float getRefitTimeChangePercent(MutableShipStatsAPI stats, float cr) {
206 float refitTimeChange = 0f;
207 float d = getDegradeThreshold(stats);
208 float i = getImproveThreshold(stats);
209 if (cr < d) {
210 float f = (d - cr) / d;
211 refitTimeChange = 1f * f * MAX_REFIT_CHANGE;
212 } else if (cr > i) {
213 float f = (cr - i) / (1f - i);
214 refitTimeChange = -1f * f * MAX_REFIT_CHANGE;
215 }
216 return refitTimeChange;
217 }
218
219 private float getDamageChangePercent(MutableShipStatsAPI stats, float cr) {
220 float damageChange = 0f;
221 float d = getDegradeThreshold(stats);
222 float i = getImproveThreshold(stats);
223 if (cr < d) {
224 float f = (d - cr) / d;
225 damageChange = -1f * f * MAX_DAMAGE_CHANGE;
226 } else if (cr > i) {
227 float f = (cr - i) / (1f - i);
228 damageChange = 1f * f * MAX_DAMAGE_CHANGE;
229 }
230 return damageChange;
231 }
232// private float getRateOfFireChangePercent(float cr) {
233// float rateOfFireChange = 0f;
234// if (cr < DEGRADE_START) {
235// float f = (DEGRADE_START - cr) / DEGRADE_START;
236// rateOfFireChange = -1f * f * MAX_ROF_CHANGE;
237// } else if (cr > IMPROVE_START) {
238// float f = (cr - IMPROVE_START) / (1f - IMPROVE_START);
239// rateOfFireChange = 1f * f * MAX_ROF_CHANGE;
240// }
241// return rateOfFireChange;
242// }
243
249 private float getAimAccuracy(float cr) {
250 return cr * 1.5f - 0.5f;
251 }
252
253 public void applyCRToStats(float cr, MutableShipStatsAPI stats, HullSize hullSize) {
254 String id = "cr_effect";
255 //System.out.println("CR: " + cr);
256 boolean fighter = hullSize == HullSize.FIGHTER;
257
258// if (hullSize == HullSize.CAPITAL_SHIP) {
259// System.out.println("Applying CR value of " + cr + " to stats " + stats + "(" + hullSize.name() + ")");
260// //new RuntimeException().printStackTrace();
261// }
262
263 if (!fighter) {
264 if (cr < getMalfunctionThreshold(stats)) {
265 stats.getWeaponMalfunctionChance().modifyFlat(id, 0.01f * getWeaponMalfuctionPercent(stats, cr));
266 stats.getEngineMalfunctionChance().modifyFlat(id, 0.01f * getEngineMalfuctionPercent(stats, cr));
267 } else {
268 stats.getWeaponMalfunctionChance().unmodify(id);
269 stats.getEngineMalfunctionChance().unmodify(id);
270 }
271 }
272
273 if (!fighter) {
274 if (cr < getCriticalMalfunctionThreshold(stats)) {
275 stats.getCriticalMalfunctionChance().modifyFlat(id, 0.01f * getCriticalMalfuctionPercent(stats, cr));
276 } else {
277 stats.getCriticalMalfunctionChance().unmodify(id);
278 }
279 }
280
281 if (!fighter) {
282 if (cr < getShieldMalfunctionThreshold(stats)) {
283 stats.getShieldMalfunctionChance().modifyFlat(id, 0.01f * getShieldMalfuctionPercent(stats, cr));
284 stats.getShieldMalfunctionFluxLevel().modifyFlat(id, 0.75f);
285 } else {
286 stats.getShieldMalfunctionChance().unmodify(id);
287 stats.getShieldMalfunctionFluxLevel().unmodify(id);
288 }
289 }
290
291 if (!fighter) {
292 if (stats.getEntity() instanceof ShipAPI) {
293 ShipAPI ship = (ShipAPI)stats.getEntity();
294 if (cr <= NO_SYSTEM_THRESHOLD) {
295 ship.setShipSystemDisabled(true);
296 ship.setDefenseDisabled(true);
297 } else {
298 ship.setShipSystemDisabled(false);
299 ship.setDefenseDisabled(false);
300 }
301 }
302 }
303
304 float movementChange = getMovementChangePercent(stats, cr);
305 float damageTakenChange = getDamageTakenChangePercent(stats, cr);
306 float damageChange = getDamageChangePercent(stats, cr);
307 float refitTimeChange = getRefitTimeChangePercent(stats, cr);
308
309 if (refitTimeChange != 0) {
310 stats.getFighterRefitTimeMult().modifyPercent(id, refitTimeChange);
311 } else {
312 stats.getFighterRefitTimeMult().unmodify(id);
313 }
314
315 if (movementChange != 0) {
316 stats.getMaxSpeed().modifyPercent(id, movementChange);
317 stats.getAcceleration().modifyPercent(id, movementChange);
318 stats.getDeceleration().modifyPercent(id, movementChange);
319 stats.getTurnAcceleration().modifyPercent(id, movementChange);
320 stats.getMaxTurnRate().modifyPercent(id, movementChange);
321 } else {
322 stats.getMaxSpeed().unmodify(id);
323 stats.getAcceleration().unmodify(id);
324 stats.getDeceleration().unmodify(id);
325 stats.getTurnAcceleration().unmodify(id);
326 stats.getMaxTurnRate().unmodify(id);
327 }
328
329 if (damageTakenChange != 0) {
330 stats.getArmorDamageTakenMult().modifyPercent(id, damageTakenChange);
331 stats.getHullDamageTakenMult().modifyPercent(id, damageTakenChange);
332 stats.getShieldDamageTakenMult().modifyPercent(id, damageTakenChange);
333 } else {
334 stats.getArmorDamageTakenMult().unmodify(id);
335 stats.getHullDamageTakenMult().unmodify(id);
336 stats.getShieldDamageTakenMult().unmodify(id);
337 }
338
339 if (damageChange != 0) {
340 stats.getBallisticWeaponDamageMult().modifyPercent(id, damageChange);
341 stats.getEnergyWeaponDamageMult().modifyPercent(id, damageChange);
342 stats.getMissileWeaponDamageMult().modifyPercent(id, damageChange);
343 } else {
344 stats.getBallisticWeaponDamageMult().unmodify(id);
345 stats.getEnergyWeaponDamageMult().unmodify(id);
346 stats.getMissileWeaponDamageMult().unmodify(id);
347 }
348
349 float aimAccuracy = getAimAccuracy(cr);
350 stats.getAutofireAimAccuracy().modifyFlat(id, aimAccuracy);
351 }
352
353 public void applyCRToShip(float cr, ShipAPI ship) {
354 if (!ship.isFighter() && cr < getMissileAmmoReductionThreshold(ship.getMutableStats())) {
355 for (WeaponAPI weapon : ship.getAllWeapons()) {
356 if (weapon.getType() == WeaponType.MISSILE) {
357 float ammo = (float) weapon.getMaxAmmo() * getMissileLoadedFraction(ship.getMutableStats(), cr);
358 if (ammo < 0) ammo = 0;
359 weapon.setAmmo(Math.round(ammo));
360 }
361 }
362 }
363 ship.setCRAtDeployment(cr);
364
365 float c = getCriticalMalfunctionThreshold(ship.getMutableStats());
366 if (cr < c && !ship.controlsLocked() && !ship.isFighter()) {
367 float severity = (c - cr) / (c);
368 if (Global.getCombatEngine() != null) { // can be null if coming from refit on a fresh app start in campaign
369
370 float criticalMult = 1f;
371 for (StatMod mod : ship.getMutableStats().getCriticalMalfunctionChance().getMultMods().values()) {
372 criticalMult *= mod.getValue();
373 }
374 severity *= criticalMult;
375 Global.getCombatEngine().addPlugin(new LowCRShipDamageSequence(ship, severity));
376 }
377 }
378 }
379
380 public float getMissileLoadedFraction(MutableShipStatsAPI stats, float cr) {
381 if (true) return 1f;
382 float test = Global.getSettings().getFloat("noDeployCRPercent") * 0.01f;
383 float f = (cr - test) / (getMissileAmmoReductionThreshold(stats) - test);
384 if (f > 1) f = 1;
385 if (f < 0) f = 0;
386 return f;
387 }
388
389
390 public float getMalfunctionThreshold(MutableShipStatsAPI stats) {
391 float mult = 1f;
392 if (stats != null) mult *= stats.getDynamic().getStat(Stats.CR_MALFUNCION_RANGE).getModifiedValue();
393 return MALFUNCTION_START * mult - 0.001f;
394 }
395 public float getCriticalMalfunctionThreshold(MutableShipStatsAPI stats) {
396 float mult = 1f;
397 if (stats != null) mult *= stats.getDynamic().getStat(Stats.CR_MALFUNCION_RANGE).getModifiedValue();
398 return CRITICAL_MALFUNCTION_START * mult - 0.001f;
399 }
400
401 public float getShieldMalfunctionThreshold(MutableShipStatsAPI stats) {
402 float mult = 1f;
403 if (stats != null) mult *= stats.getDynamic().getStat(Stats.CR_MALFUNCION_RANGE).getModifiedValue();
404 return SHIELD_MALFUNCTION_START * mult;
405 }
406
407 public float getMissileAmmoReductionThreshold(MutableShipStatsAPI stats) {
408 float mult = 1f;
409 if (stats != null) mult *= stats.getDynamic().getStat(Stats.CR_MALFUNCION_RANGE).getModifiedValue();
410 return MISSILE_AMMO_REDUCTION_START * mult - 0.001f;
411 }
412
413 public float getDegradeThreshold(MutableShipStatsAPI stats) {
414 float mult = 1f;
415 if (stats != null) mult *= stats.getDynamic().getStat(Stats.CR_MALFUNCION_RANGE).getModifiedValue();
416 return DEGRADE_START * mult;
417 }
418
419 public float getImproveThreshold(MutableShipStatsAPI stats) {
420 float mult = 1f;
421 //if (stats != null) mult *= stats.getDynamic().getStat(Stats.CR_MALFUNCION_RANGE).getModifiedValue();
422 return IMPROVE_START * mult;
423 }
424
425
431 public CREffectDescriptionForTooltip getCREffectDescription(float cr, String shipOrWing, FleetMemberAPI member) {
432 CREffectDescriptionForTooltip result = new CREffectDescriptionForTooltip();
433
434 List<CREffectDetail> details = getCREffectDetails(cr, member);
435 boolean hasPositive = false;
436 boolean hasNegative = false;
437 for (CREffectDetail detail : details) {
438 if (detail.getType() == CREffectDetailType.BONUS) hasPositive = true;
439 if (detail.getType() == CREffectDetailType.PENALTY) hasNegative = true;
440 }
441
442 float noDeploy = Global.getSettings().getFloat("noDeployCRPercent") * 0.01f;
443 String crStr = (int)(cr * 100f) + "%";
444 String str;
445 if (cr < noDeploy) {
446 str = String.format("The %s is not ready for combat and can not be deployed in battle.", shipOrWing);
447 } else if (cr < getCriticalMalfunctionThreshold(member.getStats())) {
448 str = String.format("The %s suffers from degraded performance and runs the risk of permanent and damaging malfunctions if deployed.", shipOrWing);
449 } else if (cr < getMalfunctionThreshold(member.getStats())) {
450 str = String.format("The %s suffers from degraded performance and runs the risk of weapon and engine malfunctions during combat.", shipOrWing);
451 } else if (cr < getDegradeThreshold(member.getStats()) && hasNegative) {
452 str = String.format("The %s suffers from degraded performance during combat.", shipOrWing);
453 } else if (cr < getImproveThreshold(member.getStats()) || !hasPositive) {
454 str = String.format("The %s has standard combat performance.", shipOrWing);
455 } else {
456 str = String.format("The %s benefits from improved combat performance.", shipOrWing);
457 }
458
459 //result.getHighlights().add(crStr);
460
461
462 if (member.isFighterWing()) {
463 boolean canReplaceFighters = false;
464 FleetDataAPI data = member.getFleetData();
465 if (data != null) {
466 for (FleetMemberAPI curr : data.getMembersListCopy()) {
467 if (curr.isMothballed()) continue;
468 if (curr.getNumFlightDecks() > 0) {
469 canReplaceFighters = true;
470 break;
471 }
472 }
473 }
474 if (canReplaceFighters) {
475 details.add(new CREffectDetail("", "", CREffectDetailType.NEUTRAL));
476 float costPer = member.getStats().getCRPerDeploymentPercent().computeEffective(member.getVariant().getHullSpec().getCRToDeploy()) / 100f;
477 String numStr = "" + (int) Math.ceil((float)((int) (cr * 100f)) / (costPer * 100f));
478
479 str += " " + numStr + " fighter chassis are ready to replace combat losses.";
480 result.getHighlights().add(numStr);
481
482 } else {
483 details.add(new CREffectDetail("Replacement chassis", "None", CREffectDetailType.PENALTY));
484 str += " " + "Fighter losses can not be replaced due to the lack of a ship with proper facilities (i.e. a flight deck).";
485 }
486 }
487
488
489 result.setString(str);
490
491 return result;
492 }
493
494 private String getMalfunctionString(MutableShipStatsAPI stats, float cr) {
495 String malfStr = "None";
496 if (cr < getCriticalMalfunctionThreshold(stats)) {
497 malfStr = "Critical";
498 } else if (cr < 0.3f) {
499 malfStr = "Serious";
500 } else if (cr < getMalfunctionThreshold(stats)) {
501 malfStr = "Low";
502 }
503 return malfStr;
504 }
505
506 public List<CREffectDetail> getCREffectDetails(float cr, FleetMemberAPI member) {
507 List<CREffectDetail> result = new ArrayList<CREffectDetail>();
508
509 int engine = (int) getEngineMalfuctionPercent(member.getStats(), cr);
510 int weapon = (int) getWeaponMalfuctionPercent(member.getStats(), cr);
511
512 int speed = (int) Math.round(getMovementChangePercent(member.getStats(), cr));
513 int damage = (int) Math.round(getDamageTakenChangePercent(member.getStats(), cr));
514 int damageDealt = (int) Math.round(getDamageChangePercent(member.getStats(), cr));
515 int refit = (int) Math.round(getRefitTimeChangePercent(member.getStats(), cr));
516
517 float acc = getAimAccuracy(cr);
518
519 String malfStr = getMalfunctionString(member.getStats(), cr);
520
521 String accString;
522 CREffectDetailType accType;
523 if (acc < 0) {
524 accString = "Very poor";
525 accType = CREffectDetailType.PENALTY;
526 } else if (acc < 0.25f) {
527 accString = "Poor";
528 accType = CREffectDetailType.PENALTY;
529 } else if (acc < 0.67) {
530 accString = "Standard";
531 accType = CREffectDetailType.NEUTRAL;
532 } else {
533 accString = "Excellent";
534 accType = CREffectDetailType.BONUS;
535 }
536
537 String speedStr = speed + "%";
538 if (speed >= 0) {
539 speedStr = "+" + speedStr;
540 }
541 String damageStr = damage + "%";
542 if (damage >= 0) {
543 damageStr = "+" + damageStr;
544 }
545 String rofStr = damageDealt + "%";
546 if (damageDealt >= 0) {
547 rofStr = "+" + rofStr;
548 }
549
550 String refitStr = refit + "%";
551 if (refit >= 0) {
552 refitStr = "+" + refitStr;
553 }
554
555 result.add(new CREffectDetail("Speed & maneuverability", speedStr, getTypeFor(speed, false)));
556 result.add(new CREffectDetail("Damage taken", damageStr, getTypeFor(damage, true)));
557 result.add(new CREffectDetail("Damage dealt", rofStr, getTypeFor(damageDealt, false)));
558
559 if (member.getNumFlightDecks() > 0) {
560 result.add(new CREffectDetail("Fighter refit time", refitStr, getTypeFor(refit, true)));
561 }
562
563 result.add(new CREffectDetail("Autofire accuracy", accString, accType));
564
565
566
567 CREffectDetailType malfType = CREffectDetailType.NEUTRAL;
568 if (getWeaponMalfuctionPercent(member.getStats(), cr) > 0) {
569 malfType = CREffectDetailType.PENALTY;
570 }
571
572 result.add(new CREffectDetail("Malfunction risk", malfStr, malfType));
573
574 Collection<String> slots = member.getVariant().getFittedWeaponSlots();
575 boolean hasMissiles = false;
576 for (String slotId : slots) {
577 WeaponSpecAPI w = member.getVariant().getWeaponSpec(slotId);
578 if (w.getType() == WeaponType.MISSILE) {
579 hasMissiles = true;
580 break;
581 }
582 }
583 if (hasMissiles) {
584 float missileFraction = getMissileLoadedFraction(member.getStats(), cr);
585 if (missileFraction < 0) missileFraction = 0;
586 String missileStr = (int)(missileFraction * 100f) + "%";
587
588 if (missileFraction < 1f) {
589 result.add(new CREffectDetail("Missile magazines", missileStr, missileFraction < 1 ? CREffectDetailType.PENALTY : CREffectDetailType.NEUTRAL));
590 }
591 }
592
593 return result;
594 }
595
596 private CREffectDetailType getTypeFor(int val, boolean invert) {
597 if (invert) {
598 if (val < 0) return CREffectDetailType.BONUS;
599 else if (val > 0) return CREffectDetailType.PENALTY;
600 return CREffectDetailType.NEUTRAL;
601 } else {
602 if (val > 0) return CREffectDetailType.BONUS;
603 else if (val < 0) return CREffectDetailType.PENALTY;
604 return CREffectDetailType.NEUTRAL;
605 }
606 }
607
608 protected static Object [] statusKeys = new Object [] {
609 new Object(),
610 new Object(),
611 new Object(),
612 new Object(),
613 new Object(),
614 new Object(),
615 new Object(),
616 new Object(),
617 new Object(),
618 new Object(),
619 new Object(),
620 new Object(),
621 new Object(),
622 new Object(),
623 new Object(),
624 new Object(),
625 };
626
627
628
629 public boolean isOkToPermanentlyDisable(ShipAPI ship, Object module) {
630 return isOkToPermanentlyDisableStatic(ship, module);
631 }
632
633 public static boolean isOkToPermanentlyDisableStatic(ShipAPI ship, Object module) {
634 if (module instanceof ShipEngineAPI) {
635 float fractionIfDisabled = ((ShipEngineAPI) module).getContribution() + ship.getEngineFractionPermanentlyDisabled();
636 if (fractionIfDisabled > 0.66f) {
637 return false;
638 } else {
639 return true;
640 }
641 }
642
643 if (module instanceof WeaponAPI) {
644 WeaponType type = ((WeaponAPI)module).getType();
645 if (type == WeaponType.DECORATIVE || type == WeaponType.LAUNCH_BAY || type == WeaponType.SYSTEM) {
646 return false;
647 }
648
649 if (ship.getCurrentCR() <= 0) {
650 return true;
651 }
652
653 List<Object> usableWeapons = new ArrayList<Object>();
654 for (WeaponAPI weapon : ship.getAllWeapons()) {
655 if (weapon.isPermanentlyDisabled()) continue;
656 if (weapon.isDecorative()) continue;
657 if (weapon.getSlot().isSystemSlot()) continue;
658 if (weapon.getSlot().isDecorative()) continue;
659 if (weapon.getSlot().isStationModule()) continue;
660 if (weapon.getAmmo() > 0 && (weapon.getMaxAmmo() > 20 || weapon.getSpec().getAmmoPerSecond() > 0)) {
661 usableWeapons.add(weapon);
662 }
663 }
664 usableWeapons.remove(module);
665
666 return usableWeapons.size() >= 1;
667 }
668 return false;
669 }
670}
671
672
static SettingsAPI getSettings()
Definition Global.java:51
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
CREffectDescriptionForTooltip getCREffectDescription(float cr, String shipOrWing, FleetMemberAPI member)
static boolean isOkToPermanentlyDisableStatic(ShipAPI ship, Object module)
void applyCRToStats(float cr, MutableShipStatsAPI stats, HullSize hullSize)
List< CRStatusItemData > getCRStatusDataForShip(ShipAPI ship)
float getShieldMalfunctionThreshold(MutableShipStatsAPI stats)
List< CREffectDetail > getCREffectDetails(float cr, FleetMemberAPI member)
float getMissileAmmoReductionThreshold(MutableShipStatsAPI stats)
float getMissileLoadedFraction(MutableShipStatsAPI stats, float cr)
void applyMaxCRCrewModifiers(FleetMemberAPI member)
boolean isOkToPermanentlyDisable(ShipAPI ship, Object module)
float getDegradeThreshold(MutableShipStatsAPI stats)
float getImproveThreshold(MutableShipStatsAPI stats)
float getMalfunctionThreshold(MutableShipStatsAPI stats)
float getCriticalMalfunctionThreshold(MutableShipStatsAPI stats)
String getSpriteName(String category, String id)