001package com.fs.starfarer.api.impl.campaign.skills; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.Comparator; 007import java.util.HashSet; 008import java.util.List; 009import java.util.Set; 010 011import com.fs.starfarer.api.GameState; 012import com.fs.starfarer.api.Global; 013import com.fs.starfarer.api.campaign.AICoreOfficerPlugin; 014import com.fs.starfarer.api.campaign.CampaignFleetAPI; 015import com.fs.starfarer.api.campaign.FleetDataAPI; 016import com.fs.starfarer.api.characters.CustomSkillDescription; 017import com.fs.starfarer.api.characters.FleetTotalItem; 018import com.fs.starfarer.api.characters.MutableCharacterStatsAPI; 019import com.fs.starfarer.api.characters.MutableCharacterStatsAPI.SkillLevelAPI; 020import com.fs.starfarer.api.characters.PersonAPI; 021import com.fs.starfarer.api.characters.SkillSpecAPI; 022import com.fs.starfarer.api.combat.MutableShipStatsAPI; 023import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints; 024import com.fs.starfarer.api.combat.ShipVariantAPI; 025import com.fs.starfarer.api.fleet.FleetMemberAPI; 026import com.fs.starfarer.api.impl.campaign.ids.HullMods; 027import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; 028import com.fs.starfarer.api.impl.hullmods.Automated; 029import com.fs.starfarer.api.ui.BaseTooltipCreator; 030import com.fs.starfarer.api.ui.TooltipMakerAPI; 031import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator; 032import com.fs.starfarer.api.util.Misc; 033 034public class BaseSkillEffectDescription implements CustomSkillDescription { 035 036 public static float TOOLTIP_WIDTH = 450; 037 038 public static float FIGHTER_BAYS_THRESHOLD = 8; 039 public static float OP_THRESHOLD = 1000; 040 public static float OP_LOW_THRESHOLD = 500; 041 public static float OP_ALL_LOW_THRESHOLD = 500; 042 public static float OP_ALL_THRESHOLD = 1000; 043 public static float PHASE_OP_THRESHOLD = 150; 044 public static float MILITARIZED_OP_THRESHOLD = 50; 045 046 public static float AUTOMATED_POINTS_THRESHOLD = 200; 047 048 public static boolean USE_RECOVERY_COST = true; 049 050 //public static String RECOVERY_COST = "recovery cost"; 051 public static String RECOVERY_COST = "deployment point cost"; 052 053 static { 054 if (USE_RECOVERY_COST) { 055 // working off 60% of the default battle size of 300 for these, which is 180 056 OP_THRESHOLD = 240; 057 OP_LOW_THRESHOLD = 120; 058 OP_ALL_LOW_THRESHOLD = 120; 059 OP_ALL_THRESHOLD = 240; 060// OP_THRESHOLD = 150; 061// OP_LOW_THRESHOLD = 75; 062// OP_ALL_LOW_THRESHOLD = 50; 063 064 065 PHASE_OP_THRESHOLD = 40; 066 MILITARIZED_OP_THRESHOLD = 5; 067 AUTOMATED_POINTS_THRESHOLD = 120; 068 } 069 } 070 071 072 public static enum ThresholdBonusType { 073 OP, 074 OP_LOW, 075 OP_ALL, 076 OP_ALL_LOW, 077 MILITARIZED_OP, 078 PHASE_OP, 079 FIGHTER_BAYS, 080 AUTOMATED_POINTS, 081 } 082 083 protected Color tc; 084 protected Color dtc; 085 protected Color hc; 086 protected Color dhc; 087 protected String indent; 088 protected int alpha; 089 090 public void init(MutableCharacterStatsAPI stats, SkillSpecAPI skill) { 091 indent = BaseIntelPlugin.BULLET; 092 tc = Misc.getTextColor(); 093 hc = Misc.getHighlightColor(); 094 dhc = Misc.setAlpha(hc, 155); 095 alpha = 255; 096 float level = stats.getSkillLevel(skill.getId()); 097 if (level <= 0) { 098 tc = Misc.getGrayColor(); 099 hc = dhc; 100 alpha = 155; 101 } 102 dtc = Misc.getGrayColor(); 103 } 104 105 public void initElite(MutableCharacterStatsAPI stats, SkillSpecAPI skill) { 106 indent = BaseIntelPlugin.BULLET; 107 tc = Misc.getTextColor(); 108 hc = Misc.getHighlightColor(); 109 dhc = Misc.setAlpha(hc, 155); 110 alpha = 255; 111 float level = stats.getSkillLevel(skill.getId()); 112 if (level <= 1) { 113 tc = Misc.getGrayColor(); 114 hc = dhc; 115 alpha = 155; 116 } 117 dtc = Misc.getGrayColor(); 118 } 119 120 public void addFighterBayThresholdInfo(TooltipMakerAPI info, FleetDataAPI data) { 121 if (isInCampaign()) { 122 int bays = Math.round(getNumFighterBays(data)); 123 String baysStr = "fighter bays"; 124 if (bays == 1) baysStr = "fighter bay"; 125 //baysStr = ""; 126 info.addPara(indent + "Maximum at %s or less fighter bays in fleet, your fleet has %s " + baysStr, 127 0f, tc, hc, 128 "" + (int) FIGHTER_BAYS_THRESHOLD, 129 "" + bays); 130 } else { 131 info.addPara(indent + "Maximum at %s or less fighter bays in fleet", 132 0f, tc, hc, 133 "" + (int) FIGHTER_BAYS_THRESHOLD); 134 } 135 } 136 137 public void addOPThresholdInfo(TooltipMakerAPI info, FleetDataAPI data, MutableCharacterStatsAPI cStats) { 138 addOPThresholdInfo(info, data, cStats, OP_THRESHOLD); 139 } 140 public void addOPThresholdInfo(TooltipMakerAPI info, FleetDataAPI data, MutableCharacterStatsAPI cStats, float threshold) { 141 if (USE_RECOVERY_COST) { 142 if (isInCampaign()) { 143 float op = getTotalCombatOP(data, cStats); 144 info.addPara(indent + "Maximum at %s or less total combat ship " + RECOVERY_COST + ", your fleet's total is %s", 145 0f, tc, hc, 146 "" + (int) threshold, 147 "" + (int)Math.round(op)); 148 } else { 149 info.addPara(indent + "Maximum at %s or less total combat ship " + RECOVERY_COST + " for fleet", 150 0f, tc, hc, 151 "" + (int) threshold); 152 } 153 return; 154 } 155 if (isInCampaign()) { 156 float op = getTotalCombatOP(data, cStats); 157 String opStr = "points"; 158 if (op == 1) opStr = "point"; 159 info.addPara(indent + "Maximum at %s or less total combat ship ordnance points in fleet, your fleet has %s " + opStr, 160 0f, tc, hc, 161 "" + (int) threshold, 162 "" + (int)Math.round(op)); 163 } else { 164 info.addPara(indent + "Maximum at %s or less total combat ship ordnance points in fleet", 165 0f, tc, hc, 166 "" + (int) threshold); 167 } 168 } 169 170 public void addOPThresholdAll(TooltipMakerAPI info, FleetDataAPI data, MutableCharacterStatsAPI cStats, float threshold) { 171 if (USE_RECOVERY_COST) { 172 if (isInCampaign()) { 173 float op = getTotalOP(data, cStats); 174 info.addPara(indent + "Maximum at %s or less total " + RECOVERY_COST + ", your fleet's total is %s", 175 0f, tc, hc, 176 "" + (int) threshold, 177 "" + (int)Math.round(op)); 178 } else { 179 info.addPara(indent + "Maximum at %s or less total " + RECOVERY_COST + " for fleet", 180 0f, tc, hc, 181 "" + (int) threshold); 182 } 183 return; 184 } 185 if (isInCampaign()) { 186 float op = getTotalOP(data, cStats); 187// String opStr = "combat ship ordnance points"; 188// if (op == 1) opStr = "combat ship ordnance point"; 189 String opStr = "points"; 190 if (op == 1) opStr = "point"; 191 info.addPara(indent + "Maximum at %s or less total ordnance points in fleet, your fleet has %s " + opStr, 192 0f, tc, hc, 193 "" + (int) threshold, 194 "" + (int)Math.round(op)); 195 } else { 196 info.addPara(indent + "Maximum at %s or less total ordnance points in fleet", 197 0f, tc, hc, 198 "" + (int) threshold); 199 } 200 } 201 202 public void addPhaseOPThresholdInfo(TooltipMakerAPI info, FleetDataAPI data, MutableCharacterStatsAPI cStats) { 203 if (USE_RECOVERY_COST) { 204 if (isInCampaign()) { 205 float op = getPhaseOP(data, cStats); 206 info.addPara(indent + "Maximum at %s or less total combat phase ship " + RECOVERY_COST + ", your fleet's total is %s", 207 0f, tc, hc, 208 "" + (int) PHASE_OP_THRESHOLD, 209 "" + (int)Math.round(op)); 210 } else { 211 info.addPara(indent + "Maximum at %s or less total combat phase ship " + RECOVERY_COST + " for fleet", 212 0f, tc, hc, 213 "" + (int) PHASE_OP_THRESHOLD); 214 } 215 216 return; 217 } 218 if (isInCampaign()) { 219 float op = getPhaseOP(data, cStats); 220 String opStr = "points"; 221 if (op == 1) opStr = "point"; 222 info.addPara(indent + "Maximum at %s or less total combat phase ship ordnance points in fleet, your fleet has %s " + opStr, 223 0f, tc, hc, 224 "" + (int) PHASE_OP_THRESHOLD, 225 "" + (int)Math.round(op)); 226 } else { 227 info.addPara(indent + "Maximum at %s or less total combat phase ship ordnance points in fleet", 228 0f, tc, hc, 229 "" + (int) PHASE_OP_THRESHOLD); 230 } 231 } 232 233 public void addAutomatedThresholdInfo(TooltipMakerAPI info, FleetDataAPI data, MutableCharacterStatsAPI cStats) { 234 if (USE_RECOVERY_COST) { 235 if (isInCampaign()) { 236 float op = getAutomatedPoints(data, cStats); 237 info.addPara(indent + "Maximum at %s or less total automated ship points*, your fleet's total is %s ", 238 0f, tc, hc, 239 "" + (int) AUTOMATED_POINTS_THRESHOLD, 240 "" + (int)Math.round(op)); 241 } else { 242 info.addPara(indent + "Maximum at %s or less total automated ship points* for fleet", 243 0f, tc, hc, 244 "" + (int) AUTOMATED_POINTS_THRESHOLD); 245 } 246 return; 247 } 248 if (isInCampaign()) { 249 float op = getAutomatedPoints(data, cStats); 250 String opStr = "points"; 251 if (op == 1) opStr = "point"; 252 info.addPara(indent + "Maximum at %s or less total automated ship points* in fleet, your fleet has %s " + opStr, 253 0f, tc, hc, 254 "" + (int) AUTOMATED_POINTS_THRESHOLD, 255 "" + (int)Math.round(op)); 256 } else { 257 info.addPara(indent + "Maximum at %s or less total automated ship points* in fleet", 258 0f, tc, hc, 259 "" + (int) AUTOMATED_POINTS_THRESHOLD); 260 } 261 } 262 263 public void addMilitarizedOPThresholdInfo(TooltipMakerAPI info, FleetDataAPI data, MutableCharacterStatsAPI cStats) { 264 if (USE_RECOVERY_COST) { 265 if (isInCampaign()) { 266 float op = getMilitarizedOP(data, cStats); 267 info.addPara(indent + "Maximum at %s or less total " + RECOVERY_COST + " for ships with Militarized Subsystems, your fleet's total is %s", 268 0f, tc, hc, 269 "" + (int) MILITARIZED_OP_THRESHOLD, 270 "" + (int)Math.round(op)); 271 } else { 272 info.addPara(indent + "Maximum at %s or less total " + RECOVERY_COST + " for ships with Militarized Subsystems for fleet", 273 0f, tc, hc, 274 "" + (int) MILITARIZED_OP_THRESHOLD); 275 } 276 return; 277 } else { 278 if (isInCampaign()) { 279 float op = getMilitarizedOP(data, cStats); 280 String opStr = "points"; 281 if (op == 1) opStr = "point"; 282 info.addPara(indent + "Maximum at %s or less total ordnance points for ships with Militarized Subsystems, your fleet has %s " + opStr, 283 0f, tc, hc, 284 "" + (int) MILITARIZED_OP_THRESHOLD, 285 "" + (int)Math.round(op)); 286 } else { 287 info.addPara(indent + "Maximum at %s or less total ordnance points for ships with Militarized Subsystems", 288 0f, tc, hc, 289 "" + (int) MILITARIZED_OP_THRESHOLD); 290 } 291 } 292 } 293 294 protected float computeAndCacheThresholdBonus(MutableShipStatsAPI stats, 295 String key, float maxBonus, ThresholdBonusType type) { 296 FleetDataAPI data = getFleetData(stats); 297 MutableCharacterStatsAPI cStats = getCommanderStats(stats); 298 return computeAndCacheThresholdBonus(data, cStats, key, maxBonus, type); 299 } 300 protected float computeAndCacheThresholdBonus(FleetDataAPI data, MutableCharacterStatsAPI cStats, 301 String key, float maxBonus, ThresholdBonusType type) { 302// if (key.equals("pc_peak")) { 303// System.out.println("efwfwefwe"); 304// } 305 if (data == null) return maxBonus; 306 if (cStats.getFleet() == null) return maxBonus; 307 308 Float bonus = (Float) data.getCacheClearedOnSync().get(key); 309 if (bonus != null) return bonus; 310 311 float currValue = 0f; 312 float threshold = 1f; 313 314 if (type == ThresholdBonusType.FIGHTER_BAYS) { 315 currValue = getNumFighterBays(data); 316 threshold = FIGHTER_BAYS_THRESHOLD; 317 } else if (type == ThresholdBonusType.OP) { 318 currValue = getTotalCombatOP(data, cStats); 319 threshold = OP_THRESHOLD; 320 } else if (type == ThresholdBonusType.OP_LOW) { 321 currValue = getTotalCombatOP(data, cStats); 322 threshold = OP_LOW_THRESHOLD; 323 } else if (type == ThresholdBonusType.OP_ALL_LOW) { 324 currValue = getTotalOP(data, cStats); 325 threshold = OP_ALL_LOW_THRESHOLD; 326 } else if (type == ThresholdBonusType.OP_ALL) { 327 currValue = getTotalOP(data, cStats); 328 threshold = OP_ALL_THRESHOLD; 329 } else if (type == ThresholdBonusType.MILITARIZED_OP) { 330 currValue = getMilitarizedOP(data, cStats); 331 threshold = MILITARIZED_OP_THRESHOLD; 332 } else if (type == ThresholdBonusType.PHASE_OP) { 333 currValue = getPhaseOP(data, cStats); 334 threshold = PHASE_OP_THRESHOLD; 335 } else if (type == ThresholdBonusType.AUTOMATED_POINTS) { 336 currValue = getAutomatedPoints(data, cStats); 337 threshold = AUTOMATED_POINTS_THRESHOLD; 338 } 339 340 bonus = getThresholdBasedRoundedBonus(maxBonus, currValue, threshold); 341 342 data.getCacheClearedOnSync().put(key, bonus); 343 return bonus; 344 } 345// refitData.addOrUpdate(0, 0, indent + "Combat ship ordnance points", "" + (int)totalOP); 346// refitData.addOrUpdate(0, 1, indent + "Phase ship ordnance points", "" + (int)phaseOP); 347// refitData.addOrUpdate(0, 2, indent + "Fighter bays", "" + (int)bays); 348// float totalOP = BaseSkillEffectDescription.getTotalOP(fleet.getFleetData(), Global.getSector().getPlayerStats()); 349// float phaseOP = BaseSkillEffectDescription.getPhaseOP(fleet.getFleetData(), Global.getSector().getPlayerStats()); 350 351 public FleetTotalItem getOPTotal() { 352 final CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 353 final MutableCharacterStatsAPI stats = Global.getSector().getPlayerStats(); 354 FleetTotalItem item = new FleetTotalItem(); 355 item.label = "Total ordnance points"; 356 if (USE_RECOVERY_COST) { 357 item.label = "All ships"; 358 } 359 item.value = "" + (int) getTotalOP(fleet.getFleetData(), stats); 360 item.sortOrder = 50; 361 362 item.tooltipCreator = getTooltipCreator(new TooltipCreatorSkillEffectPlugin() { 363 public void addDescription(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 364 float opad = 10f; 365 tooltip.addPara("The total deployment points of all the ships in your fleet.", 0f); 366 } 367 public List<FleetMemberPointContrib> getContributors() { 368 return getTotalOPDetail(fleet.getFleetData(), stats); 369 } 370 }); 371 372 return item; 373 } 374 375 public FleetTotalItem getCombatOPTotal() { 376 final CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 377 final MutableCharacterStatsAPI stats = Global.getSector().getPlayerStats(); 378 FleetTotalItem item = new FleetTotalItem(); 379 item.label = "Combat ship ordnance points"; 380 if (USE_RECOVERY_COST) { 381 item.label = "Combat ships"; 382 } 383 item.value = "" + (int) BaseSkillEffectDescription.getTotalCombatOP(fleet.getFleetData(), stats); 384 item.sortOrder = 100; 385 386 item.tooltipCreator = getTooltipCreator(new TooltipCreatorSkillEffectPlugin() { 387 public void addDescription(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 388 float opad = 10f; 389 tooltip.addPara("The total deployment points of all the combat ships in your fleet.", 0f); 390 } 391 public List<FleetMemberPointContrib> getContributors() { 392 return getTotalCombatOPDetail(fleet.getFleetData(), stats); 393 } 394 }); 395 396 return item; 397 } 398 399 public FleetTotalItem getAutomatedPointsTotal() { 400 final CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 401 final MutableCharacterStatsAPI stats = Global.getSector().getPlayerStats(); 402 FleetTotalItem item = new FleetTotalItem(); 403 item.label = "Automated ships"; 404 item.value = "" + (int) BaseSkillEffectDescription.getAutomatedPoints(fleet.getFleetData(), stats); 405 item.sortOrder = 350; 406 407 item.tooltipCreator = getTooltipCreator(new TooltipCreatorSkillEffectPlugin() { 408 public void addDescription(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 409 float opad = 10f; 410 tooltip.addPara("The total deployment points of all the automated ships in your fleet, " 411 + "with additional points for ships controlled by AI cores.", 0f); 412 } 413 public List<FleetMemberPointContrib> getContributors() { 414 return getAutomatedPointsDetail(fleet.getFleetData(), stats); 415 } 416 }); 417 418 return item; 419 } 420 421 public FleetTotalItem getPhaseOPTotal() { 422 final CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 423 final MutableCharacterStatsAPI stats = Global.getSector().getPlayerStats(); 424 FleetTotalItem item = new FleetTotalItem(); 425 item.label = "Phase ship ordnance points"; 426 if (USE_RECOVERY_COST) { 427 item.label = "Phase ships"; 428 } 429 item.value = "" + (int) BaseSkillEffectDescription.getPhaseOP(fleet.getFleetData(), stats); 430 item.sortOrder = 200; 431 432 item.tooltipCreator = getTooltipCreator(new TooltipCreatorSkillEffectPlugin() { 433 public void addDescription(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 434 float opad = 10f; 435 tooltip.addPara("The total deployment points of all the non-civilian phase ships in your fleet.", 0f); 436 } 437 public List<FleetMemberPointContrib> getContributors() { 438 return getPhaseOPDetail(fleet.getFleetData(), stats); 439 } 440 }); 441 return item; 442 } 443 444 public FleetTotalItem getMilitarizedOPTotal() { 445 final CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 446 final MutableCharacterStatsAPI stats = Global.getSector().getPlayerStats(); 447 FleetTotalItem item = new FleetTotalItem(); 448 item.label = "Militarized ship ordnance points"; 449 if (USE_RECOVERY_COST) { 450 item.label = "Militarized ships"; 451 } 452 item.value = "" + (int) BaseSkillEffectDescription.getMilitarizedOP(fleet.getFleetData(), stats); 453 item.sortOrder = 300; 454 455 item.tooltipCreator = getTooltipCreator(new TooltipCreatorSkillEffectPlugin() { 456 public void addDescription(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 457 float opad = 10f; 458 tooltip.addPara("The total deployment points of all the ships in your fleet that have " 459 + "the \"Militarized Subsystems\" hullmod.", 0f); 460 } 461 public List<FleetMemberPointContrib> getContributors() { 462 return getMilitarizedOPDetail(fleet.getFleetData(), stats); 463 } 464 }); 465 466 return item; 467 } 468 469 public FleetTotalItem getFighterBaysTotal() { 470 final CampaignFleetAPI fleet = Global.getSector().getPlayerFleet(); 471 FleetTotalItem item = new FleetTotalItem(); 472 item.label = "Fighter bays"; 473 item.value = "" + (int) BaseSkillEffectDescription.getNumFighterBays(fleet.getFleetData()); 474 item.sortOrder = 400; 475 476 item.tooltipCreator = getTooltipCreator(new TooltipCreatorSkillEffectPlugin() { 477 public void addDescription(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 478 float opad = 10f; 479 tooltip.addPara("The total number of fighter bays in your fleet. Both empty and filled " 480 + "fighter bays are counted. Built-in fighter bays can be removed from a ship by " 481 + "installing the \"Converted Fighter Bay\" hullmod.", 0f); 482 } 483 public List<FleetMemberPointContrib> getContributors() { 484 return getNumFighterBaysDetail(fleet.getFleetData()); 485 } 486 }); 487 488 return item; 489 } 490 491 public interface TooltipCreatorSkillEffectPlugin { 492 void addDescription(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam); 493 List<FleetMemberPointContrib> getContributors(); 494 } 495 public TooltipCreator getTooltipCreator(final TooltipCreatorSkillEffectPlugin plugin) { 496 return new BaseTooltipCreator() { 497 public float getTooltipWidth(Object tooltipParam) { 498 return TOOLTIP_WIDTH; 499 } 500 @SuppressWarnings("unchecked") 501 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 502 float pad = 3f; 503 float opad = 10f; 504 505 plugin.addDescription(tooltip, expanded, tooltipParam); 506 507 tooltip.addPara("Certain skills will have reduced effects if this value exceeds a " 508 + "skill-specific threshold; see the specific skill for details. Mothballed " 509 + "ships are not counted towards the total.", opad); 510 511 tooltip.addPara("Skills using this value:", opad); 512 tooltip.setBulletedListMode(BaseIntelPlugin.INDENT); 513 List<SkillLevelAPI> skills = (List<SkillLevelAPI>) tooltipParam; 514 Set<SkillLevelAPI> seen = new HashSet<MutableCharacterStatsAPI.SkillLevelAPI>(); 515 516 float initPad = opad; 517 //initPad = pad; 518 for (SkillLevelAPI skill : skills) { 519 if (seen.contains(skill)) continue; 520 seen.add(skill); 521 String label = skill.getSkill().getName() + " (" + skill.getSkill().getGoverningAptitudeName() + ")"; 522 label = skill.getSkill().getGoverningAptitudeName() + " - " + skill.getSkill().getName(); 523 tooltip.addPara(label, 524 skill.getSkill().getGoverningAptitudeColor(), initPad); 525 initPad = pad; 526 } 527 tooltip.setBulletedListMode(null); 528 529 List<FleetMemberPointContrib> members = plugin.getContributors(); 530 Collections.sort(members, new Comparator<FleetMemberPointContrib>() { 531 public int compare(FleetMemberPointContrib o1, FleetMemberPointContrib o2) { 532 return o2.points - o1.points; 533 } 534 }); 535 536 tooltip.addPara("Ships contributing to this value:", opad); 537 initPad = opad; 538 //initPad = pad; 539 if (members.isEmpty()) { 540 tooltip.setBulletedListMode(BaseIntelPlugin.INDENT); 541 tooltip.addPara("None", initPad); 542 tooltip.setBulletedListMode(null); 543 } 544 float gridWidth = 450f; 545 //tooltip.beginGrid(gridWidth, 1); 546 tooltip.beginGridFlipped(gridWidth, 1, 40f, opad); 547 int i = 0; 548 int max = 20; 549 if (members.size() == max + 1) { 550 max = members.size(); 551 } 552 for (FleetMemberPointContrib member : members) { 553// String label = tooltip.shortenString(BaseIntelPlugin.INDENT + getValueLabelForMember(member.member), 554// gridWidth); 555 String label = tooltip.shortenString(getValueLabelForMember(member.member), 556 gridWidth - 60f); 557 tooltip.addToGrid(0, i++, label, "+" + member.points); 558 if (i >= max) break; 559 } 560 tooltip.addGrid(opad); 561 if (members.size() > max) { 562 tooltip.addPara("And %s other ships with a smaller contribution.", opad, 563 Misc.getHighlightColor(), "" + (members.size() - max)); 564 } 565 566 } 567 }; 568 } 569 570 public static String getValueLabelForMember(FleetMemberAPI member) { 571 String str = ""; 572 if (!member.isFighterWing()) { 573 str += member.getShipName() + ", "; 574 str += member.getHullSpec().getHullNameWithDashClass(); 575 } else { 576 str += member.getVariant().getFullDesignationWithHullName(); 577 } 578 if (member.isMothballed()) { 579 str += " (mothballed)"; 580 } 581 return str; 582 } 583 584 585 public float getThresholdBasedBonus(float maxBonus, float value, float threshold) { 586 float bonus = maxBonus * threshold / Math.max(value, threshold); 587 return bonus; 588 } 589 public float getThresholdBasedRoundedBonus(float maxBonus, float value, float threshold) { 590 float bonus = maxBonus * threshold / Math.max(value, threshold); 591 if (bonus > 0 && bonus < 1) bonus = 1; 592 if (maxBonus > 1f) { 593 if (bonus < maxBonus) { 594 bonus = Math.min(bonus, maxBonus - 1f); 595 } 596 bonus = (float) Math.round(bonus); 597 } 598 return bonus; 599 } 600 601 public static boolean isMilitarized(MutableShipStatsAPI stats) { 602 if (stats == null || stats.getFleetMember() == null) return false; 603 return isMilitarized(stats.getFleetMember()); 604 } 605 public static boolean isMilitarized(FleetMemberAPI member) { 606 if (member == null) return false; 607 MutableShipStatsAPI stats = member.getStats(); 608 return stats != null && stats.getVariant() != null && 609 stats.getVariant().hasHullMod(HullMods.MILITARIZED_SUBSYSTEMS); 610 } 611 612 public static boolean isCivilian(MutableShipStatsAPI stats) { 613 if (stats == null || stats.getFleetMember() == null) return false; 614 return isCivilian(stats.getFleetMember()); 615 } 616 617 public static boolean isCivilian(FleetMemberAPI member) { 618 if (member == null) return false; 619 MutableShipStatsAPI stats = member.getStats(); 620 return stats != null && stats.getVariant() != null && 621 ((stats.getVariant().hasHullMod(HullMods.CIVGRADE) && 622 !stats.getVariant().hasHullMod(HullMods.MILITARIZED_SUBSYSTEMS)) || 623 (!stats.getVariant().hasHullMod(HullMods.CIVGRADE) && 624 stats.getVariant().getHullSpec().getHints().contains(ShipTypeHints.CIVILIAN)));// && 625 //!stats.getVariant().getHullSpec().getHints().contains(ShipTypeHints.CARRIER); 626 } 627 628 public static boolean hasFighterBays(MutableShipStatsAPI stats) { 629 if (stats == null || stats.getFleetMember() == null) return false; 630 return hasFighterBays(stats.getFleetMember()); 631 } 632 633 public static boolean hasFighterBays(FleetMemberAPI member) { 634 if (member == null) return false; 635 MutableShipStatsAPI stats = member.getStats(); 636 return stats != null && stats.getNumFighterBays().getModifiedInt() > 0; 637 } 638 639// public static float getTotalOP(MutableShipStatsAPI stats) { 640// FleetDataAPI data = getFleetData(stats); 641// MutableCharacterStatsAPI cStats = getCommanderStats(stats); 642// if (data == null) return 0; 643// return getTotalOP(data, cStats); 644// } 645 646 protected static float getPoints(FleetMemberAPI member, MutableCharacterStatsAPI stats) { 647 if (USE_RECOVERY_COST) { 648 return member.getDeploymentPointsCost(); 649 } 650 return member.getHullSpec().getOrdnancePoints(stats); 651 } 652 653 public static class FleetMemberPointContrib { 654 public FleetMemberAPI member; 655 public int points; 656 public FleetMemberPointContrib(FleetMemberAPI member, int points) { 657 super(); 658 this.member = member; 659 this.points = points; 660 } 661 } 662 663 664 public static float getTotalOP(FleetDataAPI data, MutableCharacterStatsAPI stats) { 665 float op = 0; 666 for (FleetMemberAPI curr : data.getMembersListCopy()) { 667 if (curr.isMothballed()) continue; 668 op += getPoints(curr, stats); 669 } 670 return Math.round(op); 671 } 672 673 public static List<FleetMemberPointContrib> getTotalOPDetail(FleetDataAPI data, MutableCharacterStatsAPI stats) { 674 List<FleetMemberPointContrib> result = new ArrayList<BaseSkillEffectDescription.FleetMemberPointContrib>(); 675 for (FleetMemberAPI curr : data.getMembersListCopy()) { 676 if (curr.isMothballed()) continue; 677 int pts = (int) Math.round(getPoints(curr, stats)); 678 result.add(new FleetMemberPointContrib(curr, pts)); 679 } 680 return result; 681 } 682 683 public static float getTotalCombatOP(FleetDataAPI data, MutableCharacterStatsAPI stats) { 684 float op = 0; 685 for (FleetMemberAPI curr : data.getMembersListCopy()) { 686 if (curr.isMothballed()) continue; 687 if (isCivilian(curr)) continue; 688 op += getPoints(curr, stats); 689 } 690 return Math.round(op); 691 } 692 693 public static List<FleetMemberPointContrib> getTotalCombatOPDetail(FleetDataAPI data, MutableCharacterStatsAPI stats) { 694 List<FleetMemberPointContrib> result = new ArrayList<BaseSkillEffectDescription.FleetMemberPointContrib>(); 695 for (FleetMemberAPI curr : data.getMembersListCopy()) { 696 if (curr.isMothballed()) continue; 697 if (isCivilian(curr)) continue; 698 int pts = (int) Math.round(getPoints(curr, stats)); 699 result.add(new FleetMemberPointContrib(curr, pts)); 700 } 701 return result; 702 } 703 704 public static float getPhaseOP(FleetDataAPI data, MutableCharacterStatsAPI stats) { 705 float op = 0; 706 for (FleetMemberAPI curr : data.getMembersListCopy()) { 707 if (curr.isMothballed()) continue; 708 if (curr.isPhaseShip()) { 709 if (isCivilian(curr)) continue; 710 op += getPoints(curr, stats); 711 } 712 } 713 return Math.round(op); 714 } 715 716 public static List<FleetMemberPointContrib> getPhaseOPDetail(FleetDataAPI data, MutableCharacterStatsAPI stats) { 717 List<FleetMemberPointContrib> result = new ArrayList<BaseSkillEffectDescription.FleetMemberPointContrib>(); 718 for (FleetMemberAPI curr : data.getMembersListCopy()) { 719 if (curr.isMothballed()) continue; 720 if (curr.isPhaseShip()) { 721 if (isCivilian(curr)) continue; 722 int pts = (int) Math.round(getPoints(curr, stats)); 723 result.add(new FleetMemberPointContrib(curr, pts)); 724 } 725 } 726 return result; 727 } 728 729 public static float getMilitarizedOP(FleetDataAPI data, MutableCharacterStatsAPI stats) { 730 float op = 0; 731 for (FleetMemberAPI curr : data.getMembersListCopy()) { 732 if (curr.isMothballed()) continue; 733 if (!isMilitarized(curr)) continue; 734 op += getPoints(curr, stats); 735 } 736 return Math.round(op); 737 } 738 739 public static List<FleetMemberPointContrib> getMilitarizedOPDetail(FleetDataAPI data, MutableCharacterStatsAPI stats) { 740 List<FleetMemberPointContrib> result = new ArrayList<BaseSkillEffectDescription.FleetMemberPointContrib>(); 741 for (FleetMemberAPI curr : data.getMembersListCopy()) { 742 if (curr.isMothballed()) continue; 743 if (!isMilitarized(curr)) continue; 744 int pts = (int) Math.round(getPoints(curr, stats)); 745 result.add(new FleetMemberPointContrib(curr, pts)); 746 } 747 return result; 748 } 749 750 public static float getNumFighterBays(FleetDataAPI data) { 751 if (data == null) return FIGHTER_BAYS_THRESHOLD; 752 float bays = 0; 753 for (FleetMemberAPI curr : data.getMembersListCopy()) { 754 if (curr.isMothballed()) continue; 755 bays += getNumBaysIncludingModules(curr); 756 //bays += curr.getNumFlightDecks(); 757 } 758 return bays; 759 } 760 761 public static float getNumBaysIncludingModules(FleetMemberAPI member) { 762 float bays = 0f; 763 bays += member.getNumFlightDecks(); 764 if (member.getVariant().getModuleSlots() != null) { 765 for (String slotId : member.getVariant().getModuleSlots()) { 766 if (slotId == null) continue; 767 ShipVariantAPI variant = member.getVariant().getModuleVariant(slotId); 768 if (variant == null) continue; 769 bays += variant.getHullSpec().getFighterBays(); 770 } 771 } 772 return bays; 773 } 774 775 public static List<FleetMemberPointContrib> getNumFighterBaysDetail(FleetDataAPI data) { 776 List<FleetMemberPointContrib> result = new ArrayList<BaseSkillEffectDescription.FleetMemberPointContrib>(); 777 for (FleetMemberAPI curr : data.getMembersListCopy()) { 778 if (curr.isMothballed()) continue; 779 //int pts = curr.getNumFlightDecks(); 780 int pts = (int)Math.round(getNumBaysIncludingModules(curr)); 781 if (pts <= 0) continue; 782 result.add(new FleetMemberPointContrib(curr, pts)); 783 } 784 return result; 785 } 786 787 public static float getAutomatedPoints(FleetDataAPI data, MutableCharacterStatsAPI stats) { 788 float points = 0; 789 for (FleetMemberAPI curr : data.getMembersListCopy()) { 790 if (curr.isMothballed()) continue; 791 if (!Misc.isAutomated(curr)) continue; 792 if (Automated.isAutomatedNoPenalty(curr)) continue; 793 float mult = 1f; 794 //if (curr.getCaptain().isAICore()) { 795 points += curr.getCaptain().getMemoryWithoutUpdate().getFloat(AICoreOfficerPlugin.AUTOMATED_POINTS_VALUE); 796 mult = curr.getCaptain().getMemoryWithoutUpdate().getFloat(AICoreOfficerPlugin.AUTOMATED_POINTS_MULT); 797 if (mult == 0) mult = 1; 798 //} 799 points += Math.round(getPoints(curr, stats) * mult); 800 } 801 return Math.round(points); 802 } 803 804 public static List<FleetMemberPointContrib> getAutomatedPointsDetail(FleetDataAPI data, MutableCharacterStatsAPI stats) { 805 List<FleetMemberPointContrib> result = new ArrayList<BaseSkillEffectDescription.FleetMemberPointContrib>(); 806 for (FleetMemberAPI curr : data.getMembersListCopy()) { 807 if (curr.isMothballed()) continue; 808 if (!Misc.isAutomated(curr)) continue; 809 if (Automated.isAutomatedNoPenalty(curr)) continue; 810 float mult = 1f; 811 int pts = (int) Math.round(getPoints(curr, stats)); 812 //if (curr.getCaptain().isAICore()) { 813 pts += (int) Math.round(curr.getCaptain().getMemoryWithoutUpdate().getFloat(AICoreOfficerPlugin.AUTOMATED_POINTS_VALUE)); 814 mult = curr.getCaptain().getMemoryWithoutUpdate().getFloat(AICoreOfficerPlugin.AUTOMATED_POINTS_MULT); 815 if (mult == 0) mult = 1; 816 //} 817 result.add(new FleetMemberPointContrib(curr, Math.round(pts * mult))); 818 } 819 return result; 820 } 821 822 public static boolean isInCampaign() { 823 return Global.getCurrentState() == GameState.CAMPAIGN && 824 Global.getSector() != null && 825 Global.getSector().getPlayerFleet() != null; 826 } 827 828 public static MutableCharacterStatsAPI getCommanderStats(MutableShipStatsAPI stats) { 829 if (stats == null) { 830 if (isInCampaign()) { 831 return Global.getSector().getPlayerStats(); 832 } 833 return null; 834 } 835 836 FleetMemberAPI member = stats.getFleetMember(); 837 if (member == null) return null; 838 PersonAPI commander = member.getFleetCommanderForStats(); 839 if (commander == null) { 840 boolean orig = false; 841 if (member.getFleetData() != null) { 842 orig = member.getFleetData().isForceNoSync(); 843 member.getFleetData().setForceNoSync(true); 844 } 845 commander = member.getFleetCommander(); 846 if (member.getFleetData() != null) { 847 member.getFleetData().setForceNoSync(orig); 848 } 849 } 850 if (commander != null) { 851 return commander.getStats(); 852 } 853 return null; 854 } 855 856 public static FleetDataAPI getFleetData(MutableShipStatsAPI stats) { 857 if (stats == null) { 858 if (isInCampaign()) { 859 return Global.getSector().getPlayerFleet().getFleetData(); 860 } 861 return null; 862 } 863 FleetMemberAPI member = stats.getFleetMember(); 864 if (member == null) return null; 865 FleetDataAPI data = member.getFleetDataForStats(); 866 if (data == null) data = member.getFleetData(); 867 return data; 868 } 869 870 871 public boolean hasCustomDescription() { 872 return true; 873 } 874 875 public void createCustomDescription(MutableCharacterStatsAPI stats, SkillSpecAPI skill, 876 TooltipMakerAPI info, float width) { 877 } 878 879 public String getEffectDescription(float level) { 880 return null; 881 } 882 883 public String getEffectPerLevelDescription() { 884 return null; 885 } 886 887 public ScopeDescription getScopeDescription() { 888 return null; 889 } 890 891}