1package com.fs.starfarer.api.impl.combat;
3import java.util.ArrayList;
4import java.util.Collection;
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;
46 if (member ==
null || member.
getStats() ==
null)
return;
48 float maxCRBasedOnLevel = 0.7f;
53 float penalty = 0.5f * (1f - cf);
67 List<CRStatusItemData> list =
new ArrayList<CRStatusItemData>();
80 String title =
"Combat Readiness " + Math.round(cr * 100f) +
"%";
85 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[9], icon, title,
86 "Shields offline",
true);
91 boolean hasWings =
false;
93 if (bay.getWing() !=
null) {
99 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[10], icon, title,
100 "Fighter bays offline",
true);
106 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[8], icon, title,
112 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[7], icon, title,
118 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[0], icon, title,
119 "malfunction risk: " + malfStr,
true);
123 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[2], icon, title,
124 "missiles not fully loaded",
true);
129 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[3], icon, title,
130 "degraded performance",
true);
133 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[4], icon, title,
134 "improved performance",
false);
137 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[8], icon, title,
138 "standard performance",
false);
146 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[5], icon,
"peak active performance",
150 CRStatusItemData itemData =
new CRStatusItemData(
statusKeys[6], icon,
"combat stresses",
151 "degrading readiness",
true);
163 private float getEngineMalfuctionPercent(MutableShipStatsAPI stats,
float cr) {
166 private float getCriticalMalfuctionPercent(MutableShipStatsAPI stats,
float cr) {
169 private float getShieldMalfuctionPercent(MutableShipStatsAPI stats,
float cr) {
174 private float getMovementChangePercent(MutableShipStatsAPI stats,
float cr) {
178 float movementChange = 0f;
182 float f = (d - cr) / d;
185 float f = (cr - i) / (1f - i);
188 return movementChange;
191 private float getDamageTakenChangePercent(MutableShipStatsAPI stats,
float cr) {
192 float damageTakenChange = 0f;
196 float f = (d - cr) / d;
199 float f = (cr - i) / (1f - i);
202 return damageTakenChange;
205 private float getRefitTimeChangePercent(MutableShipStatsAPI stats,
float cr) {
206 float refitTimeChange = 0f;
210 float f = (d - cr) / d;
213 float f = (cr - i) / (1f - i);
216 return refitTimeChange;
219 private float getDamageChangePercent(MutableShipStatsAPI stats,
float cr) {
220 float damageChange = 0f;
224 float f = (d - cr) / d;
227 float f = (cr - i) / (1f - i);
249 private float getAimAccuracy(
float cr) {
250 return cr * 1.5f - 0.5f;
254 String
id =
"cr_effect";
256 boolean fighter = hullSize == HullSize.FIGHTER;
304 float movementChange = getMovementChangePercent(stats, cr);
305 float damageTakenChange = getDamageTakenChangePercent(stats, cr);
306 float damageChange = getDamageChangePercent(stats, cr);
307 float refitTimeChange = getRefitTimeChangePercent(stats, cr);
309 if (refitTimeChange != 0) {
315 if (movementChange != 0) {
329 if (damageTakenChange != 0) {
339 if (damageChange != 0) {
349 float aimAccuracy = getAimAccuracy(cr);
356 if (weapon.getType() == WeaponType.MISSILE) {
358 if (ammo < 0) ammo = 0;
359 weapon.setAmmo(Math.round(ammo));
367 float severity = (c - cr) / (c);
370 float criticalMult = 1f;
372 criticalMult *= mod.getValue();
374 severity *= criticalMult;
432 CREffectDescriptionForTooltip result =
new CREffectDescriptionForTooltip();
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;
443 String crStr = (int)(cr * 100f) +
"%";
446 str = String.format(
"The %s is not ready for combat and can not be deployed in battle.", shipOrWing);
448 str = String.format(
"The %s suffers from degraded performance and runs the risk of permanent and damaging malfunctions if deployed.", shipOrWing);
450 str = String.format(
"The %s suffers from degraded performance and runs the risk of weapon and engine malfunctions during combat.", shipOrWing);
452 str = String.format(
"The %s suffers from degraded performance during combat.", shipOrWing);
454 str = String.format(
"The %s has standard combat performance.", shipOrWing);
456 str = String.format(
"The %s benefits from improved combat performance.", shipOrWing);
463 boolean canReplaceFighters =
false;
467 if (curr.isMothballed())
continue;
468 if (curr.getNumFlightDecks() > 0) {
469 canReplaceFighters =
true;
474 if (canReplaceFighters) {
475 details.add(
new CREffectDetail(
"",
"", CREffectDetailType.NEUTRAL));
477 String numStr =
"" + (int) Math.ceil((
float)((
int) (cr * 100f)) / (costPer * 100f));
479 str +=
" " + numStr +
" fighter chassis are ready to replace combat losses.";
480 result.getHighlights().add(numStr);
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).";
489 result.setString(str);
495 String malfStr =
"None";
497 malfStr =
"Critical";
498 }
else if (cr < 0.3f) {
507 List<CREffectDetail> result =
new ArrayList<CREffectDetail>();
509 int engine = (int) getEngineMalfuctionPercent(member.
getStats(), cr);
510 int weapon = (int) getWeaponMalfuctionPercent(member.
getStats(), cr);
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));
517 float acc = getAimAccuracy(cr);
519 String malfStr = getMalfunctionString(member.
getStats(), cr);
522 CREffectDetailType accType;
524 accString =
"Very poor";
525 accType = CREffectDetailType.PENALTY;
526 }
else if (acc < 0.25f) {
528 accType = CREffectDetailType.PENALTY;
529 }
else if (acc < 0.67) {
530 accString =
"Standard";
531 accType = CREffectDetailType.NEUTRAL;
533 accString =
"Excellent";
534 accType = CREffectDetailType.BONUS;
537 String speedStr = speed +
"%";
539 speedStr =
"+" + speedStr;
541 String damageStr = damage +
"%";
543 damageStr =
"+" + damageStr;
545 String rofStr = damageDealt +
"%";
546 if (damageDealt >= 0) {
547 rofStr =
"+" + rofStr;
550 String refitStr = refit +
"%";
552 refitStr =
"+" + refitStr;
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)));
560 result.add(
new CREffectDetail(
"Fighter refit time", refitStr, getTypeFor(refit,
true)));
563 result.add(
new CREffectDetail(
"Autofire accuracy", accString, accType));
567 CREffectDetailType malfType = CREffectDetailType.NEUTRAL;
568 if (getWeaponMalfuctionPercent(member.
getStats(), cr) > 0) {
569 malfType = CREffectDetailType.PENALTY;
572 result.add(
new CREffectDetail(
"Malfunction risk", malfStr, malfType));
575 boolean hasMissiles =
false;
576 for (String slotId : slots) {
578 if (w.
getType() == WeaponType.MISSILE) {
585 if (missileFraction < 0) missileFraction = 0;
586 String missileStr = (int)(missileFraction * 100f) +
"%";
588 if (missileFraction < 1f) {
589 result.add(
new CREffectDetail(
"Missile magazines", missileStr, missileFraction < 1 ? CREffectDetailType.PENALTY : CREffectDetailType.NEUTRAL));
596 private CREffectDetailType getTypeFor(
int val,
boolean invert) {
598 if (val < 0)
return CREffectDetailType.BONUS;
599 else if (val > 0)
return CREffectDetailType.PENALTY;
600 return CREffectDetailType.NEUTRAL;
602 if (val > 0)
return CREffectDetailType.BONUS;
603 else if (val < 0)
return CREffectDetailType.PENALTY;
604 return CREffectDetailType.NEUTRAL;
636 if (fractionIfDisabled > 0.66f) {
644 WeaponType type = ((
WeaponAPI)module).getType();
645 if (type == WeaponType.DECORATIVE || type == WeaponType.LAUNCH_BAY || type == WeaponType.SYSTEM) {
653 List<Object> usableWeapons =
new ArrayList<Object>();
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);
664 usableWeapons.remove(module);
666 return usableWeapons.size() >= 1;
static SettingsAPI getSettings()
static CombatEngineAPI getCombatEngine()
void modifyFlat(String source, float value)
void modifyPercent(String source, float value)
void unmodifyFlat(String source)
HashMap< String, StatMod > getMultMods()
float computeEffective(float baseValue)
static final String CR_MALFUNCION_RANGE
static float NO_SYSTEM_THRESHOLD
CREffectDescriptionForTooltip getCREffectDescription(float cr, String shipOrWing, FleetMemberAPI member)
static float SHIELD_MALFUNCTION_START
static boolean isOkToPermanentlyDisableStatic(ShipAPI ship, Object module)
void applyCRToStats(float cr, MutableShipStatsAPI stats, HullSize hullSize)
static float IMPROVE_START
List< CRStatusItemData > getCRStatusDataForShip(ShipAPI ship)
static float CRITICAL_MALFUNCTION_START
static float MAX_DAMAGE_TAKEN_CHANGE
static float MAX_SHIELD_MALFUNCTION_CHANCE
float getShieldMalfunctionThreshold(MutableShipStatsAPI stats)
void applyCRToShip(float cr, ShipAPI ship)
static float MAX_DAMAGE_CHANGE
List< CREffectDetail > getCREffectDetails(float cr, FleetMemberAPI member)
float getMissileAmmoReductionThreshold(MutableShipStatsAPI stats)
float getMissileLoadedFraction(MutableShipStatsAPI stats, float cr)
static float MAX_CRITICAL_MALFUNCTION_CHANCE
static Object[] statusKeys
static float MAX_ENGINE_MALFUNCTION_CHANCE
static float DEGRADE_START
static float MISSILE_AMMO_REDUCTION_START
void applyMaxCRCrewModifiers(FleetMemberAPI member)
boolean isOkToPermanentlyDisable(ShipAPI ship, Object module)
float getDegradeThreshold(MutableShipStatsAPI stats)
float getImproveThreshold(MutableShipStatsAPI stats)
static float MAX_MOVEMENT_CHANGE
float getMalfunctionThreshold(MutableShipStatsAPI stats)
static float MAX_REFIT_CHANGE
float getCriticalMalfunctionThreshold(MutableShipStatsAPI stats)
static float MAX_WEAPON_MALFUNCTION_CHANCE
static float MALFUNCTION_START
String getSpriteName(String category, String id)
float getFloat(String key)
List< FleetMemberAPI > getMembersListCopy()
void addPlugin(EveryFrameCombatPlugin plugin)
MutableStat getMissileWeaponDamageMult()
MutableStat getShieldMalfunctionChance()
MutableStat getShieldMalfunctionFluxLevel()
MutableStat getMaxCombatReadiness()
DynamicStatsAPI getDynamic()
MutableStat getFighterRefitTimeMult()
StatBonus getCRPerDeploymentPercent()
StatBonus getPeakCRDuration()
MutableStat getHullDamageTakenMult()
MutableStat getEngineMalfunctionChance()
MutableStat getArmorDamageTakenMult()
MutableStat getWeaponMalfunctionChance()
MutableStat getDeceleration()
MutableStat getBallisticWeaponDamageMult()
MutableStat getAutofireAimAccuracy()
MutableStat getTurnAcceleration()
MutableStat getCriticalMalfunctionChance()
MutableStat getMaxTurnRate()
MutableStat getEnergyWeaponDamageMult()
MutableStat getMaxSpeed()
CombatEntityAPI getEntity()
MutableStat getAcceleration()
MutableStat getShieldDamageTakenMult()
void setCRAtDeployment(float cr)
void setShipSystemDisabled(boolean systemDisabled)
void setDefenseDisabled(boolean defenseDisabled)
ShipSystemAPI getPhaseCloak()
float getTimeDeployedForCRReduction()
ShipHullSpecAPI getHullSpec()
MutableShipStatsAPI getMutableStats()
float getCRAtDeployment()
List< WeaponAPI > getAllWeapons()
float getEngineFractionPermanentlyDisabled()
List< FighterLaunchBayAPI > getLaunchBaysCopy()
ShipSystemAPI getSystem()
boolean losesCRDuringCombat()
Collection< String > getFittedWeaponSlots()
ShipHullSpecAPI getHullSpec()
WeaponSpecAPI getWeaponSpec(String slotId)
ShipVariantAPI getVariant()
FleetDataAPI getFleetData()
MutableShipStatsAPI getStats()
MutableStat getStat(String id)