001package com.fs.starfarer.api.impl.campaign.intel.group; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.Comparator; 007import java.util.List; 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.JumpPointAPI; 014import com.fs.starfarer.api.campaign.SectorEntityToken; 015import com.fs.starfarer.api.campaign.StarSystemAPI; 016import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.ActionType; 017import com.fs.starfarer.api.campaign.econ.MarketAPI; 018import com.fs.starfarer.api.impl.campaign.MilitaryResponseScript; 019import com.fs.starfarer.api.impl.campaign.MilitaryResponseScript.MilitaryResponseParams; 020import com.fs.starfarer.api.impl.campaign.command.WarSimScript; 021import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData; 022import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment; 023import com.fs.starfarer.api.impl.campaign.ids.Factions; 024import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 025import com.fs.starfarer.api.impl.campaign.ids.Tags; 026import com.fs.starfarer.api.impl.campaign.intel.group.GenericRaidFGI.GenericPayloadAction; 027import com.fs.starfarer.api.util.IntervalUtil; 028import com.fs.starfarer.api.util.Misc; 029 030public class FGBlockadeAction extends FGDurationAction implements GenericPayloadAction { 031 032 public static class FGBlockadeParams { 033 public StarSystemAPI where; 034 public MarketAPI specificMarket = null; 035 public boolean doNotGetSidetracked = true; 036 //public float duration = 365f; 037 public float accessibilityPenalty = 0.6f; 038 039 public String patrolText = null; 040 public String targetFaction = Factions.PLAYER; 041 } 042 043 044 protected IntervalUtil interval = new IntervalUtil(0.1f, 0.3f); 045 046 protected FGBlockadeParams params; 047 protected List<SectorEntityToken> blockadePoints = new ArrayList<SectorEntityToken>(); 048 protected SectorEntityToken primary; 049 050 protected boolean computedInitial = false; 051 protected float origDuration = 1f; 052 protected List<MilitaryResponseScript> scripts = new ArrayList<MilitaryResponseScript>(); 053 054 public FGBlockadeAction(FGBlockadeParams params, float duration) { 055 super(duration); 056 origDuration = duration; 057 this.params = params; 058 059 interval.forceIntervalElapsed(); 060 } 061 062 public Object readResolve() { 063 return this; 064 } 065 066 067 public void computeInitial() { 068 computedInitial = true; 069 070 blockadePoints = new ArrayList<SectorEntityToken>(params.where.getEntities(JumpPointAPI.class)); 071 072 final Vector2f center = new Vector2f(); 073 074 Collections.sort(blockadePoints, new Comparator<SectorEntityToken>() { 075 public int compare(SectorEntityToken o1, SectorEntityToken o2) { 076 float d1 = Misc.getDistance(center, o1.getLocation()); 077 float d2 = Misc.getDistance(center, o2.getLocation()); 078 return (int) Math.signum(d1 - d2); 079 } 080 }); 081 082 if (blockadePoints.size() == 1) { 083 primary = blockadePoints.get(0); 084 } else if (blockadePoints.size() >= 3) { 085 primary = blockadePoints.get(1); 086 } else if (blockadePoints.size() == 2) { 087 float d0 = Misc.getDistance(center, blockadePoints.get(0).getLocation()); 088 float d1 = Misc.getDistance(center, blockadePoints.get(1).getLocation()); 089 if (d0 > 3000) { 090 primary = blockadePoints.get(0); 091 } else { 092 primary = blockadePoints.get(1); 093 } 094 } 095 096 // otherwise, WasSimScript adds extra MilitaryResponseScripts for objectives and 097 // attacking fleets go there almost to the exclusion of other targets 098 for (SectorEntityToken objective : params.where.getEntitiesWithTag(Tags.OBJECTIVE)) { 099 WarSimScript.setNoFightingForObjective(objective, intel.getFaction(), 1000f); 100 } 101 102 } 103 104 105 @Override 106 public void addRouteSegment(RouteData route) { 107 RouteSegment segment = new RouteSegment(getDurDays(), params.where.getCenter()); 108 route.addSegment(segment); 109 } 110 111 @Override 112 public void notifyFleetsSpawnedMidSegment(RouteSegment segment) { 113 super.notifyFleetsSpawnedMidSegment(segment); 114 } 115 116 @Override 117 public void notifySegmentFinished(RouteSegment segment) { 118 super.notifySegmentFinished(segment); 119 120 //autoresolve(); 121 } 122 123 @Override 124 public void setActionFinished(boolean finished) { 125 if (finished && !this.finished) { 126 List<CampaignFleetAPI> fleets = intel.getFleets(); 127 for (CampaignFleetAPI fleet : fleets) { 128 Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.FLEET_BUSY, fleet.getId(), true, -1f); 129 } 130 131 if (scripts != null) { 132 for (MilitaryResponseScript s : scripts) { 133 s.forceDone(); 134 } 135 scripts.clear(); 136 } 137 138 for (SectorEntityToken objective : params.where.getEntitiesWithTag(Tags.OBJECTIVE)) { 139 WarSimScript.removeNoFightingTimeoutForObjective(objective, intel.getFaction()); 140 } 141 } 142 super.setActionFinished(finished); 143 } 144 145 146 @Override 147 public void directFleets(float amount) { 148 super.directFleets(amount); 149 if (isActionFinished()) return; 150 151 List<CampaignFleetAPI> fleets = intel.getFleets(); 152 if (fleets.isEmpty()) { 153 setActionFinished(true); 154 return; 155 } 156 157 if (!computedInitial) { 158 computeInitial(); 159 orderFleetMovements(); 160 } 161 162 if (primary == null) { 163 setActionFinished(true); 164 return; 165 } 166 167 float days = Global.getSector().getClock().convertToDays(amount); 168 169 interval.advance(days); 170 if (!interval.intervalElapsed()) return; 171 172 // doing this would cause the fleets to: 173 // 1) not hassle the player 174 // 2) not attack nearby enemies unless they bump into them 175 176// for (CampaignFleetAPI fleet : fleets) { 177// if (params.doNotGetSidetracked) { 178// boolean battleNear = false; 179// for (CampaignFleetAPI other : fleets) { 180// if (other == fleet || other.getBattle() == null) continue; 181// if (other.getContainingLocation() != fleet.getContainingLocation()); 182// float dist = Misc.getDistance(fleet, other); 183// if (dist < 1000) { 184// CampaignFleetAIAPI ai = fleet.getAI(); 185// if (ai != null && ai.wantsToJoin(other.getBattle(), other.getBattle().isPlayerInvolved())) { 186// battleNear = true; 187// break; 188// } 189// } 190// } 191// if (!battleNear) { 192// fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_FLEET_DO_NOT_GET_SIDETRACKED, true, 0.4f); 193// } 194// } 195// } 196 } 197 198 199 protected void orderFleetMovements() { 200 if (blockadePoints.isEmpty() || primary == null) { 201 return; 202 } 203 204 float fPrimary = 1f; 205 float fNonPrimary = 1f / blockadePoints.size(); 206 207 for (CampaignFleetAPI fleet : intel.getFleets()) { 208 fleet.getMemoryWithoutUpdate().unset(MemFlags.FLEET_BUSY); 209 } 210 211 212 for (SectorEntityToken target : blockadePoints) { 213 float rf = fNonPrimary; 214 if (target == primary) rf = fPrimary; 215 MilitaryResponseParams aggroParams = new MilitaryResponseParams(ActionType.HOSTILE, 216 "blockade_" + target.getId(), 217 intel.getFaction(), 218 target, 219 rf, 220 getDurDays()); 221 aggroParams.travelText = null; 222 aggroParams.actionText = params.patrolText; 223 224 MilitaryResponseScript script = new MilitaryResponseScript(aggroParams); 225 params.where.addScript(script); 226 scripts.add(script); 227 } 228 } 229 230 public FGBlockadeParams getParams() { 231 return params; 232 } 233 234 235 236 public float getSuccessFraction() { 237 float f = getElapsed() / Math.max(1f, origDuration); 238 if (f < 0f) f = 0f; 239 if (f > 1f) f = 1f; 240 return f; 241 } 242 243 public Color getSystemNameHighlightColor() { 244 return Global.getSector().getFaction(params.targetFaction).getBaseUIColor(); 245 } 246 247 public StarSystemAPI getWhere() { 248 return params.where; 249 } 250 251 public List<SectorEntityToken> getBlockadePoints() { 252 return blockadePoints; 253 } 254 255 public SectorEntityToken getPrimary() { 256 return primary; 257 } 258 259 260 261} 262 263 264 265 266 267 268 269