001package com.fs.starfarer.api.impl.campaign.intel.raid; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.List; 006import java.util.Random; 007import java.util.Set; 008 009import org.lwjgl.util.vector.Vector2f; 010 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.campaign.CampaignFleetAPI; 013import com.fs.starfarer.api.campaign.FactionAPI; 014import com.fs.starfarer.api.campaign.SectorEntityToken; 015import com.fs.starfarer.api.campaign.StarSystemAPI; 016import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin; 017import com.fs.starfarer.api.campaign.econ.MarketAPI; 018import com.fs.starfarer.api.impl.campaign.DebugFlags; 019import com.fs.starfarer.api.impl.campaign.command.WarSimScript; 020import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3; 021import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3; 022import com.fs.starfarer.api.impl.campaign.fleets.RouteLocationCalculator; 023import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData; 024import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData; 025import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner; 026import com.fs.starfarer.api.impl.campaign.ids.Factions; 027import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 028import com.fs.starfarer.api.impl.campaign.ids.Ranks; 029import com.fs.starfarer.api.impl.campaign.ids.Tags; 030import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; 031import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseAssignmentAI.FleetActionDelegate; 032import com.fs.starfarer.api.impl.campaign.procgen.themes.RouteFleetAssignmentAI; 033import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD; 034import com.fs.starfarer.api.ui.Alignment; 035import com.fs.starfarer.api.ui.LabelAPI; 036import com.fs.starfarer.api.ui.SectorMapAPI; 037import com.fs.starfarer.api.ui.TooltipMakerAPI; 038import com.fs.starfarer.api.util.Misc; 039 040public class RaidIntel extends BaseIntelPlugin implements RouteFleetSpawner { 041 042 public static Object UPDATE_FAILED = new Object(); 043 public static Object UPDATE_RETURNING = new Object(); 044 public static Object ENTERED_SYSTEM_UPDATE = new Object(); 045 046 public static enum RaidStageStatus { 047 ONGOING, 048 SUCCESS, 049 FAILURE, 050 } 051 052 public static interface RaidDelegate { 053 void notifyRaidEnded(RaidIntel raid, RaidStageStatus status); 054 } 055 056 public static interface RaidStage { 057 RaidStageStatus getStatus(); 058 void advance(float amount); 059 void notifyStarted(); 060 float getExtraDaysUsed(); 061 void showStageInfo(TooltipMakerAPI info); 062 float getElapsed(); 063 float getMaxDays(); 064 } 065 066 protected int currentStage = 0; 067 protected int failStage = -1; 068 protected List<RaidStage> stages = new ArrayList<RaidStage>(); 069 070 protected String id = Misc.genUID(); 071 protected String sid = "raid_" + id; 072 073 protected float extraDays = 60f; 074 protected StarSystemAPI system; 075 protected FactionAPI faction; 076 protected float defenderStr = 0f; 077 protected RaidDelegate delegate; 078 079 public RaidIntel(StarSystemAPI system, FactionAPI faction, RaidDelegate delegate) { 080 081 //RAID_DEBUG = false; 082 083 this.system = system; 084 this.faction = faction; 085 this.delegate = delegate; 086 087 Global.getSector().addScript(this); 088// Global.getSector().getIntelManager().addIntel(this); 089 090 091// List<MarketAPI> targets = new ArrayList<MarketAPI>(); 092// for (MarketAPI market : Misc.getMarketsInLocation(system)) { 093// if (market.getFaction().isHostileTo(getFaction())) { 094// targets.add(market); 095// } 096// } 097 defenderStr = WarSimScript.getEnemyStrength(getFaction(), system); 098 } 099 100 public void sendEnteredSystemUpdate() { 101 //sendUpdateIfPlayerHasIntel(ENTERED_SYSTEM_UPDATE, false); 102 } 103 104 public StarSystemAPI getSystem() { 105 return system; 106 } 107 108 109 public int getCurrentStage() { 110 return currentStage; 111 } 112 113 public int getStageIndex(RaidStage stage) { 114 return stages.indexOf(stage); 115 } 116 117 public int getFailStage() { 118 return failStage; 119 } 120 121 public OrganizeStage getOrganizeStage() { 122 for (RaidStage stage : stages) { 123 if (stage instanceof OrganizeStage) { 124 return (OrganizeStage) stage; 125 } 126 } 127 return null; 128 } 129 public AssembleStage getAssembleStage() { 130 for (RaidStage stage : stages) { 131 if (stage instanceof AssembleStage) { 132 return (AssembleStage) stage; 133 } 134 } 135 return null; 136 //return (AssembleStage) stages.get(0); 137 } 138 139 public ActionStage getActionStage() { 140 for (RaidStage stage : stages) { 141 if (stage instanceof ActionStage) { 142 return (ActionStage) stage; 143 } 144 } 145 return null; 146 //return (AssembleStage) stages.get(0); 147 } 148 149 public void addStage(RaidStage stage) { 150 stages.add(stage); 151 } 152 153 public String getRouteSourceId() { 154 return sid; 155 } 156 157 public float getExtraDays() { 158 return extraDays; 159 } 160 161 public void setExtraDays(float extraDays) { 162 this.extraDays = extraDays; 163 } 164 165 166 167 @Override 168 public boolean canMakeVisibleToPlayer(boolean playerInRelayRange) { 169 return super.canMakeVisibleToPlayer(playerInRelayRange); 170 } 171 172 public boolean shouldSendUpdate() { 173 if (DebugFlags.SEND_UPDATES_WHEN_NO_COMM || Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay()) { 174 return true; 175 } 176 if (system != null && system == Global.getSector().getCurrentLocation()) { 177 return true; 178 } 179 180 return isPlayerTargeted(); 181 } 182 183 public boolean isPlayerTargeted() { 184 ActionStage action = getActionStage(); 185 if (action != null && action.isPlayerTargeted()) return true; 186 return false; 187 } 188 189 public String getCommMessageSound() { 190 if (isPlayerTargeted() && !isSendingUpdate()) { 191 return getSoundColonyThreat(); 192 } 193 194 if (isSendingUpdate()) { 195 return getSoundStandardUpdate(); 196 } 197 return getSoundMajorPosting(); 198 } 199 200 @Override 201 protected void advanceImpl(float amount) { 202 super.advanceImpl(amount); 203 204 if (currentStage >= stages.size()) { 205 endAfterDelay(); 206 // do we really need an update after the raiders have returned to their source colonies? 207 // as far as the player is concerned, the raid is really over when they head back, not when they get back 208 209 // actually, the return stage finishes immediately since its updateStatus() sets to SUCCESS right away 210 // so this update gets sent right after the action stage succeeds 211 if (shouldSendUpdate()) { 212 sendUpdateIfPlayerHasIntel(UPDATE_RETURNING, false); 213 } 214 return; 215 } 216 217 RaidStage stage = stages.get(currentStage); 218 219 stage.advance(amount); 220 221 RaidStageStatus status = stage.getStatus(); 222 if (status == RaidStageStatus.SUCCESS) { 223 currentStage++; 224 setExtraDays(Math.max(0, getExtraDays() - stage.getExtraDaysUsed())); 225 if (currentStage < stages.size()) { 226 stages.get(currentStage).notifyStarted(); 227 } 228 return; 229 } else if (status == RaidStageStatus.FAILURE) { 230 failedAtStage(stage); 231 failStage = currentStage; 232 endAfterDelay(); 233 if (shouldSendUpdate()) { 234 sendUpdateIfPlayerHasIntel(UPDATE_FAILED, false); 235 } 236 } 237 } 238 239 public void forceFail(boolean withUpdate) { 240 int index = currentStage; 241 if (index >= stages.size()) index = stages.size() - 1; 242 failedAtStage(stages.get(index)); 243 failStage = currentStage; 244 endAfterDelay(); 245 if (withUpdate && shouldSendUpdate()) { 246 sendUpdateIfPlayerHasIntel(UPDATE_FAILED, false); 247 } 248 } 249 250 protected void failedAtStage(RaidStage stage) { 251 252 } 253 254 255 @Override 256 protected void notifyEnded() { 257 super.notifyEnded(); 258 Global.getSector().removeScript(this); 259 } 260 261 @Override 262 protected void notifyEnding() { 263 super.notifyEnding(); 264 265 if (delegate != null) { 266 RaidStageStatus status = RaidStageStatus.SUCCESS; 267 if (failStage >= 0) { 268 status = RaidStageStatus.FAILURE; 269 } 270 delegate.notifyRaidEnded(this, status); 271 } 272 } 273 274 275 public float getETA() { 276 int curr = getCurrentStage(); 277 float eta = 0f; 278 for (RaidStage stage : stages) { 279 if (stage instanceof ActionStage) { 280 break; 281 } 282 //RouteLocationCalculator.getTravelDays(((TravelStage)stage).from, ((TravelStage)stage).to) 283 int index = getStageIndex(stage); 284 if (index < curr) { 285 continue; 286 } 287 if (stage instanceof OrganizeStage) { 288 eta += Math.max(0f, stage.getMaxDays() - stage.getElapsed()); 289 } else if (stage instanceof AssembleStage) { 290 eta += Math.max(0f, 20f - stage.getElapsed()); 291 } else if (stage instanceof TravelStage) { 292 float travelDays = RouteLocationCalculator.getTravelDays(getAssembleStage().gatheringPoint, system.getHyperspaceAnchor()); 293 eta += Math.max(0f, travelDays - stage.getElapsed()); 294 } 295 } 296 return eta; 297 } 298 299 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) { 300 301 Color h = Misc.getHighlightColor(); 302 Color g = Misc.getGrayColor(); 303 float pad = 3f; 304 float opad = 10f; 305 306 float initPad = pad; 307 if (mode == ListInfoMode.IN_DESC) initPad = opad; 308 309 Color tc = getBulletColorForMode(mode); 310 311 bullet(info); 312 boolean isUpdate = getListInfoParam() != null; 313 314 float eta = getETA(); 315 316 info.addPara("Faction: " + faction.getDisplayName(), initPad, tc, 317 faction.getBaseUIColor(), faction.getDisplayName()); 318 initPad = 0f; 319 320 int max = 0; 321 MarketAPI target = null; 322 for (MarketAPI other : Misc.getMarketsInLocation(system)) { 323 if (!other.getFaction().isHostileTo(faction)) continue; 324 int size = other.getSize(); 325 if (size > max || (size == max && other.getFaction().isPlayerFaction())) { 326 max = size; 327 target = other; 328 } 329 } 330 331 332 if (target != null) { 333 FactionAPI other = target.getFaction(); 334 info.addPara("Target: " + other.getDisplayName(), initPad, tc, 335 other.getBaseUIColor(), other.getDisplayName()); 336 } 337 338 if (isUpdate) { 339 if (getListInfoParam() == ENTERED_SYSTEM_UPDATE) { 340 info.addPara("Arrived in-system", tc, initPad); 341 } else { 342 if (failStage < 0) { 343 info.addPara("Colonies in the " + system.getNameWithLowercaseType() + " have been raided", 344 tc, initPad); 345 } else { 346 info.addPara("The raid on the " + system.getNameWithLowercaseType() + " has failed", 347 tc, initPad); 348 } 349 } 350 } else { 351 info.addPara(system.getNameWithLowercaseType(), 352 tc, initPad); 353 } 354 initPad = 0f; 355 if (eta > 1 && failStage < 0 && getListInfoParam() != ENTERED_SYSTEM_UPDATE) { 356 String days = getDaysString(eta); 357 info.addPara("Estimated %s " + days + " until arrival", 358 initPad, tc, h, "" + (int)Math.round(eta)); 359 initPad = 0f; 360 } 361 362 unindent(info); 363 } 364 365 @Override 366 public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) { 367 Color c = getTitleColor(mode); 368 369 if (isPlayerTargeted() && false) { 370 info.setParaSmallInsignia(); 371 } else { 372 info.setParaFontDefault(); 373 } 374 375 info.addPara(getName(), c, 0f); 376 info.setParaFontDefault(); 377 addBulletPoints(info, mode); 378 } 379 380 protected MarketAPI getFirstSource() { 381 AssembleStage as = getAssembleStage(); 382 if (as == null) return null; 383 if (as.getSources() == null || as.getSources().isEmpty()) return null; 384 return as.getSources().get(0); 385 } 386 387 @Override 388 public void createSmallDescription(TooltipMakerAPI info, float width, float height) { 389 Color h = Misc.getHighlightColor(); 390 Color g = Misc.getGrayColor(); 391 Color tc = Misc.getTextColor(); 392 float pad = 3f; 393 float opad = 10f; 394 395 info.addImage(getFactionForUIColors().getLogo(), width, 128, opad); 396 397 FactionAPI faction = getFaction(); 398 String has = faction.getDisplayNameHasOrHave(); 399 String is = faction.getDisplayNameIsOrAre(); 400 401 AssembleStage as = getAssembleStage(); 402 MarketAPI source = getFirstSource(); 403 404 //float raidStr = as.getSpawnFP(); 405 float raidStr = as.getOrigSpawnFP(); 406 raidStr = Misc.getAdjustedStrength(raidStr, source); 407 408// String strDesc = ""; 409// // fp, multiplied by roughly 0.25 to 4, depending on quality, colony size, doctrine 410// if (raidStr < 150) { 411// strDesc = "very weak"; 412// } else if (raidStr < 300) { 413// strDesc = "somewhat weak"; 414// } else if (raidStr < 500) { 415// strDesc = "fairly strong"; 416// } else if (raidStr < 1000) { 417// strDesc = "strong"; 418// } else { 419// strDesc = "very strong"; 420// } 421 422 String strDesc = getRaidStrDesc(); 423 int numFleets = (int) getOrigNumFleets(); 424 String fleets = "fleets"; 425 if (numFleets == 1) fleets = " large fleet, or several smaller ones"; 426 427 LabelAPI label = info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " " + is + 428 " conducting a raid of the " + system.getName() + ". The raiding forces are " + 429 "projected to be " + strDesc + 430 " and likely comprised of " + numFleets + " " + fleets + ".", 431 opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle()); 432 label.setHighlight(faction.getDisplayNameWithArticleWithoutArticle(), strDesc, "" + numFleets); 433 label.setHighlightColors(faction.getBaseUIColor(), h, h); 434 435 List<MarketAPI> targets = new ArrayList<MarketAPI>(); 436 for (MarketAPI market : Misc.getMarketsInLocation(system)) { 437 if (market.getFaction().isHostileTo(faction)) { 438 targets.add(market); 439 } 440 } 441 442 defenderStr = WarSimScript.getEnemyStrength(getFaction(), system); 443 444 List<MarketAPI> safe = new ArrayList<MarketAPI>(); 445 List<MarketAPI> unsafe = new ArrayList<MarketAPI>(); 446 for (MarketAPI market : targets) { 447 float defensiveStr = defenderStr + WarSimScript.getStationStrength(market.getFaction(), system, market.getPrimaryEntity()); 448 if (defensiveStr > raidStr * 1.25f) { 449 safe.add(market); 450 } else { 451 unsafe.add(market); 452 } 453 } 454 455 if (!isEnding()) { 456 if (targets.isEmpty()) { 457 info.addPara("There are no colonies for the raid to target in the system.", opad); 458 } else { 459 boolean showSafe = false; 460 if (raidStr < defenderStr * 0.75f) { 461 info.addPara("The raiding forces should be outmatched by fleets defending the system. In the absence of " + 462 "other factors, the raid is unlikely to find success.", opad); 463 } else if (raidStr < defenderStr * 1.25f) { 464 info.addPara("The raiding forces are evenly matched with fleets defending the system.", opad); 465 showSafe = true; 466 } else { 467 info.addPara("The raiding forces are superior to the fleets defending the system.", opad); 468 showSafe = true; 469 } 470 if (showSafe) { 471 if (safe.size() == targets.size()) { 472 info.addPara("However, all colonies should be safe from the raid, " + 473 "owing to their orbital defenses.", opad); 474 } else { 475 info.addPara("Considering orbital defenses (if any), the following colonies are " + 476 "at risk from the raid:", opad); 477 float initPad = opad; 478 for (MarketAPI market : unsafe) { 479 addMarketToList(info, market, initPad, tc); 480 initPad = 0f; 481 } 482 483 // info.addPara("Unless the raid is stopped, these colonies " + 484 // "may suffer " + 485 // "reduced stability, infrastructure damage, and a possible loss of stockpiled resources.", opad); 486 487 } 488 } 489 } 490 } 491 492 info.addSectionHeading("Status", 493 faction.getBaseUIColor(), faction.getDarkUIColor(), Alignment.MID, opad); 494 495 for (RaidStage stage : stages) { 496 stage.showStageInfo(info); 497 if (getStageIndex(stage) == failStage) break; 498 } 499 } 500 501 502 503 @Override 504 public String getIcon() { 505 return faction.getCrest(); 506 } 507 508 @Override 509 public Set<String> getIntelTags(SectorMapAPI map) { 510 Set<String> tags = super.getIntelTags(map); 511 tags.add(Tags.INTEL_MILITARY); 512 if (!Misc.getMarketsInLocation(system, Factions.PLAYER).isEmpty()) { 513 tags.add(Tags.INTEL_COLONIES); 514 } 515 tags.add(getFaction().getId()); 516 return tags; 517 } 518 519 public String getSortString() { 520 return "Raid"; 521 } 522 523 public String getName() { 524 String base = Misc.ucFirst(getFaction().getPersonNamePrefix()) + " Raid"; 525 if (isEnding()) { 526 if (isSendingUpdate() && failStage >= 0) { 527 return base + " - Failed"; 528 } 529 for (RaidStage stage : stages) { 530 if (stage instanceof ActionStage && stage.getStatus() == RaidStageStatus.SUCCESS) { 531 return base + " - Successful"; 532 } 533 } 534 return base + " - Over"; 535 } 536 return base; 537 } 538 539 public boolean isFailed() { 540 return failStage >= 0; 541 } 542 543 public boolean isSucceeded() { 544 for (RaidStage stage : stages) { 545 if (stage instanceof ActionStage && stage.getStatus() == RaidStageStatus.SUCCESS) { 546 return true; 547 } 548 } 549 return false; 550 } 551 552 553 @Override 554 public FactionAPI getFactionForUIColors() { 555 return getFaction(); 556 } 557 558 public FactionAPI getFaction() { 559 return faction; 560 } 561 562 public String getSmallDescriptionTitle() { 563 return getName(); 564 } 565 566 @Override 567 public SectorEntityToken getMapLocation(SectorMapAPI map) { 568 return system.getHyperspaceAnchor(); 569 } 570 571 public List<ArrowData> getArrowData(SectorMapAPI map) { 572 AssembleStage as = getAssembleStage(); 573 if (as == null || !as.isSourceKnown()) return null; 574 575 576 SectorEntityToken from = as.gatheringPoint; 577 if (system == null|| system == from.getContainingLocation()) return null; 578 579 List<ArrowData> result = new ArrayList<ArrowData>(); 580 581 SectorEntityToken entityFrom = from; 582 if (map != null && delegate instanceof IntelInfoPlugin && delegate != this) { 583 SectorEntityToken iconEntity = map.getIntelIconEntity((IntelInfoPlugin)delegate); 584 if (iconEntity != null) { 585 entityFrom = iconEntity; 586 } 587 } 588 589 ArrowData arrow = new ArrowData(entityFrom, system.getCenter()); 590 arrow.color = getFactionForUIColors().getBaseUIColor(); 591 arrow.width = 20f; 592 result.add(arrow); 593 594 return result; 595 } 596 597 public boolean shouldCancelRouteAfterDelayCheck(RouteData route) { 598 return false; 599 } 600 601 public boolean shouldRepeat(RouteData route) { 602 return false; 603 } 604 605 public void reportAboutToBeDespawnedByRouteManager(RouteData route) { 606 } 607 608 609 610 public CampaignFleetAPI spawnFleet(RouteData route) { 611 612 Random random = route.getRandom(); 613 614 MarketAPI market = route.getMarket(); 615 CampaignFleetAPI fleet = createFleet(market.getFactionId(), route, market, null, random); 616 617 if (fleet == null || fleet.isEmpty()) return null; 618 619 //fleet.addEventListener(this); 620 621 market.getContainingLocation().addEntity(fleet); 622 fleet.setFacing((float) Math.random() * 360f); 623 // this will get overridden by the patrol assignment AI, depending on route-time elapsed etc 624 fleet.setLocation(market.getPrimaryEntity().getLocation().x, market.getPrimaryEntity().getLocation().x); 625 626 fleet.addScript(createAssignmentAI(fleet, route)); 627 628 return fleet; 629 } 630 631 public RouteFleetAssignmentAI createAssignmentAI(CampaignFleetAPI fleet, RouteData route) { 632 ActionStage action = getActionStage(); 633 FleetActionDelegate delegate = null; 634 if (action instanceof FleetActionDelegate) { 635 delegate = (FleetActionDelegate) action; 636 } 637 return new RaidAssignmentAI(fleet, route, delegate); 638 } 639 640 public CampaignFleetAPI createFleet(String factionId, RouteData route, MarketAPI market, Vector2f locInHyper, Random random) { 641 if (random == null) random = new Random(); 642 643 OptionalFleetData extra = route.getExtra(); 644 645 float combat = extra.fp; 646 float tanker = extra.fp * (0.1f + random.nextFloat() * 0.05f); 647 float transport = extra.fp * (0.1f + random.nextFloat() * 0.05f); 648 float freighter = 0f; 649 combat -= tanker; 650 combat -= transport; 651 652 FleetParamsV3 params = new FleetParamsV3( 653 market, 654 locInHyper, 655 factionId, 656 route == null ? null : route.getQualityOverride(), 657 extra.fleetType, 658 combat, // combatPts 659 freighter, // freighterPts 660 tanker, // tankerPts 661 transport, // transportPts 662 0f, // linerPts 663 0f, // utilityPts 664 0f // qualityMod, won't get used since routes mostly have quality override set 665 ); 666 //params.ignoreMarketFleetSizeMult = true; // already accounted for in extra.fp 667// if (DebugFlags.RAID_DEBUG) { 668// params.qualityOverride = 1f; 669// } 670 if (route != null) { 671 params.timestamp = route.getTimestamp(); 672 } 673 params.random = random; 674 CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params); 675 676 if (fleet == null || fleet.isEmpty()) return null; 677 678 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_WAR_FLEET, true); 679 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_RAIDER, true); 680 681 if (fleet.getFaction().getCustomBoolean(Factions.CUSTOM_PIRATE_BEHAVIOR)) { 682 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PIRATE, true); 683 } 684 685 String postId = Ranks.POST_PATROL_COMMANDER; 686 String rankId = Ranks.SPACE_COMMANDER; 687 688 fleet.getCommander().setPostId(postId); 689 fleet.getCommander().setRankId(rankId); 690 691 return fleet; 692 } 693 694 695 public float getRaidFPAdjusted() { 696 //AssembleStage as = getAssembleStage(); 697 //MarketAPI source = as.getSources().get(0); 698 MarketAPI source = getFirstSource(); 699 float raidFP = getRaidFP(); 700 701 // fp, multiplied by roughly 0.25 to 4, depending on quality, colony size, doctrine 702 float raidStr = Misc.getAdjustedFP(raidFP, source); 703 return raidStr; 704 } 705 706 public float getRaidFP() { 707 AssembleStage as = getAssembleStage(); 708 float raidStr = 0f; 709 for (RouteData route : as.getRoutes()) { 710 CampaignFleetAPI fleet = route.getActiveFleet(); 711 if (fleet != null) { 712 float mult = Misc.getAdjustedFP(1f, route.getMarket()); 713 if (mult < 1) mult = 1f; 714 raidStr += fleet.getFleetPoints() / mult; 715 } else { 716 raidStr += route.getExtra().fp; 717 } 718 } 719 if (raidStr <= 0 || as.getSpawnFP() > 0) { 720 raidStr = Math.max(as.getOrigSpawnFP(), raidStr); 721 } 722 float raidFP = raidStr; 723 return raidFP; 724 } 725 726 public float getNumFleets() { 727 AssembleStage as = getAssembleStage(); 728 float num = as.getRoutes().size(); 729 if (as.getSpawnFP() > 0) { 730 num = Math.max(num, as.getOrigSpawnFP() / as.getLargeSize(false)); 731 } 732 if (num < 1) num = 1; 733 return num; 734 } 735 736 public float getOrigNumFleets() { 737 AssembleStage as = getAssembleStage(); 738 float num = (float) Math.ceil(as.getOrigSpawnFP() / as.getLargeSize(false)); 739 if (num < 1) num = 1; 740 return num; 741 } 742 743 public float getRaidStr() { 744// AssembleStage as = getAssembleStage(); 745// MarketAPI source = as.getSources().get(0); 746 MarketAPI source = getFirstSource(); 747 float raidFP = getRaidFP(); 748 749 // fp, multiplied by roughly 0.25 to 4, depending on quality, colony size, doctrine 750 float raidStr = Misc.getAdjustedStrength(raidFP, source); 751 return raidStr; 752 } 753 754 protected String getRaidStrDesc() { 755 return Misc.getStrengthDesc(getRaidStr()); 756 } 757 758 public void addStandardStrengthComparisons(TooltipMakerAPI info, 759 MarketAPI target, FactionAPI targetFaction, 760 boolean withGround, boolean withBombard, 761 String raid, String raids) { 762 Color h = Misc.getHighlightColor(); 763 float opad = 10f; 764 765 //AssembleStage as = getAssembleStage(); 766 //MarketAPI source = as.getSources().get(0); 767 float raidFP = getRaidFPAdjusted() / getNumFleets(); 768 float raidStr = getRaidStr(); 769 770 //float defenderStr = WarSimScript.getEnemyStrength(getFaction(), system); 771 float defenderStr = WarSimScript.getFactionStrength(targetFaction, system); 772 float defensiveStr = defenderStr + WarSimScript.getStationStrength(targetFaction, system, target.getPrimaryEntity()); 773 774 float assumedRaidGroundStr = raidFP * Misc.FP_TO_GROUND_RAID_STR_APPROX_MULT; 775 float re = MarketCMD.getRaidEffectiveness(target, assumedRaidGroundStr); 776 777 String spaceStr = ""; 778 String groundStr = ""; 779 String outcomeDesc = null; 780 boolean even = false; 781 if (raidStr < defensiveStr * 0.75f) { 782 spaceStr = "outmatched"; 783 if (outcomeDesc == null) outcomeDesc = "The " + raid + " is likely to be defeated in orbit"; 784 } else if (raidStr < defensiveStr * 1.25f) { 785 spaceStr = "evenly matched"; 786 if (outcomeDesc == null) outcomeDesc = "The " + raids + " outcome is uncertain"; 787 even = true; 788 } else { 789 spaceStr = "superior"; 790 if (!withGround && !withBombard) { 791 if (outcomeDesc == null) outcomeDesc = "The " + raid + " is likely to be successful"; 792 } 793 } 794 795 if (withGround) { 796 if (re < 0.33f) { 797 groundStr = "outmatched"; 798 if (outcomeDesc == null || even) outcomeDesc = "The " + raid + " is likely to be largely repelled by the ground defences"; 799 } else if (re < 0.66f) { 800 groundStr = "evenly matched"; 801 if (outcomeDesc == null) outcomeDesc = "The " + raids + " outcome is uncertain"; 802 } else { 803 groundStr = "superior"; 804 if (outcomeDesc == null) outcomeDesc = "The " + raid + " is likely to be successful"; 805 } 806 //info.addPara("Compared to the defenses of " + target.getName() + ", the " + raids + " space forces are %s " + 807 info.addPara("Compared to the defenses, the " + raids + " space forces are %s " + 808 "and its ground forces are %s." + 809 " " + outcomeDesc + ".", opad, h, spaceStr, groundStr); 810 } else if (withBombard) { 811 float required = MarketCMD.getBombardmentCost(target, null); 812 float available = raidFP * Misc.FP_TO_BOMBARD_COST_APPROX_MULT; 813 814 if (required * .67 > available) { 815 groundStr = "outmatched"; 816 if (outcomeDesc == null) outcomeDesc = "The bombardment is likely to be countered by the ground defences"; 817 } else if (required * 1.33f > available) { 818 groundStr = "evenly matched"; 819 if (outcomeDesc == null) outcomeDesc = "The bombardment's outcome is uncertain"; 820 } else { 821 groundStr = "superior"; 822 if (outcomeDesc == null) outcomeDesc = "The bombardment is likely to be successful"; 823 } 824 //info.addPara("Compared to the defenses of " + target.getName() + ", the " + raids + " space forces are %s " + 825 info.addPara("Compared to the defenses, the " + raids + " space forces are %s. " + 826 "" + outcomeDesc + ".", opad, h, spaceStr, groundStr); 827 828 } else { 829 info.addPara("Compared to the defenses of " + target.getName() + ", " + 830 "the " + raids + " space forces are %s." + 831 " " + outcomeDesc + ".", opad, h, spaceStr, groundStr); 832 } 833 } 834 835 @Override 836 public IntelSortTier getSortTier() { 837 if (isPlayerTargeted() && false) { 838 return IntelSortTier.TIER_2; 839 } 840 return super.getSortTier(); 841 } 842} 843 844 845 846 847 848 849