1package com.fs.starfarer.api.impl.campaign.intel.raid;
4import java.util.ArrayList;
6import java.util.Random;
9import org.lwjgl.util.vector.Vector2f;
11import com.fs.starfarer.api.Global;
12import com.fs.starfarer.api.campaign.CampaignFleetAPI;
13import com.fs.starfarer.api.campaign.FactionAPI;
14import com.fs.starfarer.api.campaign.SectorEntityToken;
15import com.fs.starfarer.api.campaign.StarSystemAPI;
16import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin;
17import com.fs.starfarer.api.campaign.econ.MarketAPI;
18import com.fs.starfarer.api.impl.campaign.DebugFlags;
19import com.fs.starfarer.api.impl.campaign.command.WarSimScript;
20import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
21import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
22import com.fs.starfarer.api.impl.campaign.fleets.RouteLocationCalculator;
23import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData;
24import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
25import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner;
26import com.fs.starfarer.api.impl.campaign.ids.Factions;
27import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
28import com.fs.starfarer.api.impl.campaign.ids.Ranks;
29import com.fs.starfarer.api.impl.campaign.ids.Tags;
30import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
31import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseAssignmentAI.FleetActionDelegate;
32import com.fs.starfarer.api.impl.campaign.procgen.themes.RouteFleetAssignmentAI;
33import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD;
34import com.fs.starfarer.api.ui.Alignment;
35import com.fs.starfarer.api.ui.LabelAPI;
36import com.fs.starfarer.api.ui.SectorMapAPI;
37import com.fs.starfarer.api.ui.TooltipMakerAPI;
38import com.fs.starfarer.api.util.Misc;
46 public static enum RaidStageStatus {
52 public static interface RaidDelegate {
53 void notifyRaidEnded(
RaidIntel raid, RaidStageStatus status);
56 public static interface RaidStage {
57 RaidStageStatus getStatus();
58 void advance(
float amount);
60 float getExtraDaysUsed();
61 void showStageInfo(TooltipMakerAPI info);
68 protected List<RaidStage>
stages =
new ArrayList<RaidStage>();
70 protected String
id = Misc.genUID();
71 protected String
sid =
"raid_" +
id;
114 return stages.indexOf(stage);
122 for (RaidStage stage :
stages) {
130 for (RaidStage stage :
stages) {
140 for (RaidStage stage :
stages) {
169 return super.canMakeVisibleToPlayer(playerInRelayRange);
202 super.advanceImpl(amount);
219 stage.advance(amount);
221 RaidStageStatus status = stage.getStatus();
222 if (status == RaidStageStatus.SUCCESS) {
229 }
else if (status == RaidStageStatus.FAILURE) {
263 super.notifyEnding();
266 RaidStageStatus status = RaidStageStatus.SUCCESS;
268 status = RaidStageStatus.FAILURE;
270 delegate.notifyRaidEnded(
this, status);
278 for (RaidStage stage :
stages) {
288 eta += Math.max(0f, stage.getMaxDays() - stage.getElapsed());
290 eta += Math.max(0f, 20f - stage.getElapsed());
292 float travelDays = RouteLocationCalculator.getTravelDays(
getAssembleStage().gatheringPoint,
system.getHyperspaceAnchor());
293 eta += Math.max(0f, travelDays - stage.getElapsed());
301 Color h = Misc.getHighlightColor();
302 Color g = Misc.getGrayColor();
307 if (mode == ListInfoMode.IN_DESC) initPad = opad;
316 info.addPara(
"Faction: " +
faction.getDisplayName(), initPad, tc,
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())) {
332 if (target !=
null) {
333 FactionAPI other = target.getFaction();
334 info.addPara(
"Target: " + other.getDisplayName(), initPad, tc,
335 other.getBaseUIColor(), other.getDisplayName());
340 info.addPara(
"Arrived in-system", tc, initPad);
343 info.addPara(
"Colonies in the " +
system.getNameWithLowercaseType() +
" have been raided",
346 info.addPara(
"The raid on the " +
system.getNameWithLowercaseType() +
" has failed",
351 info.addPara(
system.getNameWithLowercaseType(),
357 info.addPara(
"Estimated %s " + days +
" until arrival",
358 initPad, tc, h,
"" + (
int)Math.round(eta));
370 info.setParaSmallInsignia();
372 info.setParaFontDefault();
375 info.addPara(
getName(), c, 0f);
376 info.setParaFontDefault();
382 if (as ==
null)
return null;
389 Color h = Misc.getHighlightColor();
390 Color g = Misc.getGrayColor();
391 Color tc = Misc.getTextColor();
398 String has =
faction.getDisplayNameHasOrHave();
399 String is =
faction.getDisplayNameIsOrAre();
406 raidStr = Misc.getAdjustedStrength(raidStr, source);
424 String fleets =
"fleets";
425 if (numFleets == 1) fleets =
" large fleet, or several smaller ones";
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);
435 List<MarketAPI> targets =
new ArrayList<MarketAPI>();
436 for (MarketAPI market : Misc.getMarketsInLocation(
system)) {
437 if (market.getFaction().isHostileTo(
faction)) {
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) {
456 if (targets.isEmpty()) {
457 info.addPara(
"There are no colonies for the raid to target in the system.", opad);
459 boolean showSafe =
false;
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);
464 info.addPara(
"The raiding forces are evenly matched with fleets defending the system.", opad);
467 info.addPara(
"The raiding forces are superior to the fleets defending the system.", opad);
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);
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) {
492 info.addSectionHeading(
"Status",
493 faction.getBaseUIColor(),
faction.getDarkUIColor(), Alignment.MID, opad);
495 for (RaidStage stage :
stages) {
496 stage.showStageInfo(info);
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);
524 String base = Misc.ucFirst(
getFaction().getPersonNamePrefix()) +
" Raid";
527 return base +
" - Failed";
529 for (RaidStage stage :
stages) {
531 return base +
" - Successful";
534 return base +
" - Over";
544 for (RaidStage stage :
stages) {
568 return system.getHyperspaceAnchor();
577 if (
system ==
null||
system == from.getContainingLocation())
return null;
579 List<ArrowData> result =
new ArrayList<ArrowData>();
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;
589 ArrowData arrow =
new ArrowData(entityFrom,
system.getCenter());
612 Random random = route.getRandom();
614 MarketAPI market = route.getMarket();
615 CampaignFleetAPI fleet =
createFleet(market.getFactionId(), route, market,
null, random);
617 if (fleet ==
null || fleet.isEmpty())
return null;
621 market.getContainingLocation().addEntity(fleet);
622 fleet.setFacing((
float) Math.random() * 360f);
624 fleet.setLocation(market.getPrimaryEntity().getLocation().x, market.getPrimaryEntity().getLocation().x);
633 FleetActionDelegate
delegate =
null;
634 if (action instanceof FleetActionDelegate) {
635 delegate = (FleetActionDelegate) action;
640 public CampaignFleetAPI
createFleet(String factionId, RouteData route, MarketAPI market, Vector2f locInHyper, Random random) {
641 if (random ==
null) random =
new Random();
643 OptionalFleetData extra = route.getExtra();
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;
652 FleetParamsV3 params =
new FleetParamsV3(
656 route ==
null ?
null : route.getQualityOverride(),
671 params.timestamp = route.getTimestamp();
673 params.random = random;
674 CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params);
676 if (fleet ==
null || fleet.isEmpty())
return null;
678 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_WAR_FLEET,
true);
679 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_RAIDER,
true);
681 if (fleet.getFaction().getCustomBoolean(Factions.CUSTOM_PIRATE_BEHAVIOR)) {
682 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PIRATE,
true);
685 String postId = Ranks.POST_PATROL_COMMANDER;
686 String rankId = Ranks.SPACE_COMMANDER;
688 fleet.getCommander().setPostId(postId);
689 fleet.getCommander().setRankId(rankId);
702 float raidStr = Misc.getAdjustedFP(raidFP, source);
710 CampaignFleetAPI fleet = route.getActiveFleet();
712 float mult = Misc.getAdjustedFP(1f, route.getMarket());
713 if (mult < 1) mult = 1f;
714 raidStr += fleet.getFleetPoints() / mult;
716 raidStr += route.getExtra().fp;
722 float raidFP = raidStr;
732 if (num < 1) num = 1;
739 if (num < 1) num = 1;
750 float raidStr = Misc.getAdjustedStrength(raidFP, source);
759 MarketAPI target, FactionAPI targetFaction,
760 boolean withGround,
boolean withBombard,
761 String raid, String raids) {
762 Color h = Misc.getHighlightColor();
772 float defensiveStr =
defenderStr + WarSimScript.getStationStrength(targetFaction,
system, target.getPrimaryEntity());
774 float assumedRaidGroundStr = raidFP * Misc.FP_TO_GROUND_RAID_STR_APPROX_MULT;
775 float re = MarketCMD.getRaidEffectiveness(target, assumedRaidGroundStr);
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";
789 spaceStr =
"superior";
790 if (!withGround && !withBombard) {
791 if (outcomeDesc ==
null) outcomeDesc =
"The " + raid +
" is likely to be successful";
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";
803 groundStr =
"superior";
804 if (outcomeDesc ==
null) outcomeDesc =
"The " + raid +
" is likely to be successful";
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;
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";
821 groundStr =
"superior";
822 if (outcomeDesc ==
null) outcomeDesc =
"The bombardment is likely to be successful";
825 info.addPara(
"Compared to the defenses, the " + raids +
" space forces are %s. " +
826 "" + outcomeDesc +
".", opad, h, spaceStr, groundStr);
829 info.addPara(
"Compared to the defenses of " + target.getName() +
", " +
830 "the " + raids +
" space forces are %s." +
831 " " + outcomeDesc +
".", opad, h, spaceStr, groundStr);
838 return IntelSortTier.TIER_2;
840 return super.getSortTier();
static SectorAPI getSector()
static boolean SEND_UPDATES_WHEN_NO_COMM
boolean isSendingUpdate()
void unindent(TooltipMakerAPI info)
static String getSoundStandardUpdate()
void sendUpdateIfPlayerHasIntel(Object listInfoParam, TextPanelAPI textPanel)
Object getListInfoParam()
Color getBulletColorForMode(ListInfoMode mode)
static String getSoundColonyThreat()
static String getDaysString(float days)
void bullet(TooltipMakerAPI info)
static String getSoundMajorPosting()
Color getTitleColor(ListInfoMode mode)
static void addMarketToList(TooltipMakerAPI info, MarketAPI market, float pad)
abstract boolean isPlayerTargeted()
float getLargeSize(boolean limitToSpawnFP)
List< MarketAPI > getSources()
SectorEntityToken gatheringPoint
RaidStageStatus getStatus()
List< RouteData > getRoutes()
IntelSortTier getSortTier()
void createSmallDescription(TooltipMakerAPI info, float width, float height)
CampaignFleetAPI spawnFleet(RouteData route)
String getSmallDescriptionTitle()
static Object UPDATE_RETURNING
SectorEntityToken getMapLocation(SectorMapAPI map)
RaidIntel(StarSystemAPI system, FactionAPI faction, RaidDelegate delegate)
void addStage(RaidStage stage)
List< ArrowData > getArrowData(SectorMapAPI map)
AssembleStage getAssembleStage()
boolean shouldRepeat(RouteData route)
CampaignFleetAPI createFleet(String factionId, RouteData route, MarketAPI market, Vector2f locInHyper, Random random)
Set< String > getIntelTags(SectorMapAPI map)
void addStandardStrengthComparisons(TooltipMakerAPI info, MarketAPI target, FactionAPI targetFaction, boolean withGround, boolean withBombard, String raid, String raids)
void failedAtStage(RaidStage stage)
ActionStage getActionStage()
boolean isPlayerTargeted()
static Object UPDATE_FAILED
String getCommMessageSound()
void forceFail(boolean withUpdate)
void reportAboutToBeDespawnedByRouteManager(RouteData route)
FactionAPI getFactionForUIColors()
boolean canMakeVisibleToPlayer(boolean playerInRelayRange)
static Object ENTERED_SYSTEM_UPDATE
OrganizeStage getOrganizeStage()
void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode)
String getRouteSourceId()
int getStageIndex(RaidStage stage)
void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode)
StarSystemAPI getSystem()
float getRaidFPAdjusted()
void sendEnteredSystemUpdate()
RouteFleetAssignmentAI createAssignmentAI(CampaignFleetAPI fleet, RouteData route)
MarketAPI getFirstSource()
void advanceImpl(float amount)
void setExtraDays(float extraDays)
boolean shouldSendUpdate()
boolean shouldCancelRouteAfterDelayCheck(RouteData route)