001package com.fs.starfarer.api.impl.campaign; 002 003import java.util.List; 004 005import com.fs.starfarer.api.EveryFrameScript; 006import com.fs.starfarer.api.Global; 007import com.fs.starfarer.api.campaign.CampaignFleetAPI; 008import com.fs.starfarer.api.campaign.FactionAPI; 009import com.fs.starfarer.api.campaign.FleetAssignment; 010import com.fs.starfarer.api.campaign.SectorEntityToken; 011import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.ActionType; 012import com.fs.starfarer.api.campaign.ai.FleetAssignmentDataAPI; 013import com.fs.starfarer.api.campaign.ai.ModularFleetAIAPI; 014import com.fs.starfarer.api.campaign.rules.MemoryAPI; 015import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 016import com.fs.starfarer.api.util.IntervalUtil; 017import com.fs.starfarer.api.util.Misc; 018 019public class MilitaryResponseScript implements EveryFrameScript { 020 021 public static String RESPONSE_ASSIGNMENT = "response"; // custom value added to assignments so we know which to clear 022 023 public static class MilitaryResponseParams { 024 public ActionType type; 025 public String responseReason; 026 public FactionAPI faction; 027 028 public SectorEntityToken actor; 029 public SectorEntityToken target; 030 public float responseFraction; 031 public float responseDuration; 032 public String travelText; 033 public String actionText; 034 035 public MilitaryResponseParams(ActionType type, String responseReason, 036 FactionAPI faction, SectorEntityToken target, 037 float responseFraction, float responseDuration) { 038 this.type = type; 039 this.responseReason = responseReason; 040 this.faction = faction; 041 this.target = target; 042 this.responseFraction = responseFraction; 043 this.responseDuration = responseDuration; 044 } 045 046 047 } 048 049 050 protected IntervalUtil tracker = new IntervalUtil(0.05f, 0.15f); 051 protected MilitaryResponseParams params; 052 protected float elapsed; 053 054 public MilitaryResponseScript(MilitaryResponseParams params) { 055 this.params = params; 056 addToResponseTotal(); 057 initiateResponse(); 058 } 059 060 public void advance(float amount) { 061 float days = Global.getSector().getClock().convertToDays(amount); 062 tracker.advance(days); 063 064 elapsed += days; 065 066 067// if (params != null) { 068// System.out.println("MRS: " + params.responseReason); 069// } else { 070// System.out.println("NULL MRS params"); 071// } 072 073 if (tracker.intervalElapsed()) { 074 initiateResponse(); 075 } 076 } 077 078 079 public void initiateResponse() { 080 if (params.target.getContainingLocation() == null) return; 081// if (params.faction.getId().equals(Factions.PIRATES) && params.target.isInCurrentLocation()) { 082// System.out.println("wefwefwe"); 083// } 084 List<CampaignFleetAPI> fleets = params.target.getContainingLocation().getFleets(); 085 for (CampaignFleetAPI fleet : fleets) { 086 seeIfFleetShouldRespond(fleet); 087 } 088 } 089 090 protected boolean isTemporarilyNotResponding(CampaignFleetAPI fleet) { 091 if (fleet.getBattle() != null) return true; 092 093 if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.FLEET_BUSY)) return true; 094 FleetAssignmentDataAPI curr = fleet.getCurrentAssignment(); 095 if (curr != null && curr.getAssignment() == FleetAssignment.STANDING_DOWN) return true; 096 097 MemoryAPI memory = fleet.getMemoryWithoutUpdate(); 098 if (memory.getBoolean(MemFlags.FLEET_MILITARY_RESPONSE)) return true; 099 100 return false; 101 } 102 103 protected void seeIfFleetShouldRespond(CampaignFleetAPI fleet) { 104// if (fleet.getContainingLocation() == Global.getSector().getCurrentLocation()) { 105// System.out.println("fwefwef"); 106// } 107 108 if (!couldRespond(fleet)) return; 109 110 if (isTemporarilyNotResponding(fleet)) return; 111 112 List<CampaignFleetAPI> fleets = params.target.getContainingLocation().getFleets(); 113 float potentialFP = 0; 114 float respondingFP = 0f; 115 116 float closestDist = Float.MAX_VALUE; 117 CampaignFleetAPI closestNonResponder = null; 118 119 for (CampaignFleetAPI other : fleets) { 120 if (!couldRespond(other)) continue; 121 122 float fp = other.getFleetPoints(); 123 124 potentialFP += fp; 125 boolean responding = isResponding(other); 126 if (responding) { 127 respondingFP += fp; 128 } 129 130 //if (other == fleet) continue; 131 132 if (!responding && !isTemporarilyNotResponding(other)) { 133 float distOther = Misc.getDistance(params.target, other); 134 if (distOther < closestDist) { 135 closestDist = distOther; 136 closestNonResponder = other; 137 } 138 } 139 } 140 141 float fraction = params.responseFraction / getResponseTotal(); 142 143 //float dist = Misc.getDistance(params.target, fleet); 144 if (potentialFP > 0 && 145 respondingFP / potentialFP < fraction && 146 closestNonResponder == fleet) { 147 148 respond(fleet); 149 } 150 } 151 152 protected void respond(CampaignFleetAPI fleet) { 153 unrespond(fleet); 154 155// if (fleet.getContainingLocation() != null && fleet.getContainingLocation().getName().startsWith("Corvus")) { 156// System.out.println("fwefwe"); 157// } 158 //fleet.getAssignmentsCopy().get(0) 159 Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), 160 MemFlags.FLEET_MILITARY_RESPONSE, params.responseReason, true, (1.5f + (float) Math.random()) * 0.2f); 161 162 fleet.addAssignmentAtStart(FleetAssignment.PATROL_SYSTEM, params.target, 3f, params.actionText, null); 163 FleetAssignmentDataAPI curr = fleet.getCurrentAssignment(); 164 if (curr != null) { 165 curr.setCustom(RESPONSE_ASSIGNMENT); 166 } 167 168 float dist = Misc.getDistance(params.target, fleet); 169 if (dist > 2000f) { 170 fleet.addAssignmentAtStart(FleetAssignment.GO_TO_LOCATION, params.target, 3f, params.travelText, null); 171 //fleet.addAssignmentAtStart(FleetAssignment.DELIVER_CREW, params.target, 3f, params.travelText, null); 172 curr = fleet.getCurrentAssignment(); 173 if (curr != null) { 174 curr.setCustom(RESPONSE_ASSIGNMENT); 175 } 176 } 177 178 //Global.getSector().addPing(fleet, Pings.DANGER); 179 } 180 181 protected void unrespond(CampaignFleetAPI fleet) { 182 Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), 183 MemFlags.FLEET_MILITARY_RESPONSE, params.responseReason, false, 0f); 184 boolean firstOrbitPassive = true; 185 for (FleetAssignmentDataAPI curr : fleet.getAI().getAssignmentsCopy()) { 186 if (RESPONSE_ASSIGNMENT.equals(curr.getCustom())) { 187 fleet.getAI().removeAssignment(curr); 188 } else if (curr.getAssignment() == FleetAssignment.ORBIT_PASSIVE && firstOrbitPassive) { 189 // "preparing for patrol" or some such, very likely - don't want to go back to that 190 // after the response is done 191 fleet.getAI().removeAssignment(curr); 192 firstOrbitPassive = false; 193 } 194 } 195 } 196 197 protected boolean isResponding(CampaignFleetAPI fleet) { 198 return Misc.flagHasReason(fleet.getMemoryWithoutUpdate(), MemFlags.FLEET_MILITARY_RESPONSE, params.responseReason); 199 } 200 201 protected boolean couldRespond(CampaignFleetAPI fleet) { 202 if (fleet.getFaction() != params.faction) return false; 203 if (fleet.getAI() == null) return false; 204 if (fleet.isPlayerFleet()) return false; 205 if (fleet.isStationMode()) return false; 206 207 // don't check for this here as it would skew proportiions of what's assigned where if a fleet is busy for a bit 208 //if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.FLEET_BUSY)) return false; 209 210 if (fleet.getAI() instanceof ModularFleetAIAPI) { 211 ModularFleetAIAPI ai = (ModularFleetAIAPI) fleet.getAI(); 212 if (ai.getAssignmentModule().areAssignmentsFrozen()) return false; 213 } 214 215 if (fleet.getCurrentAssignment() != null && 216 fleet.getCurrentAssignment().getAssignment() == FleetAssignment.GO_TO_LOCATION_AND_DESPAWN) { 217 return false; 218 } 219 220 MemoryAPI memory = fleet.getMemoryWithoutUpdate(); 221 222 boolean patrol = memory.getBoolean(MemFlags.MEMORY_KEY_PATROL_FLEET); 223 boolean warFleet = memory.getBoolean(MemFlags.MEMORY_KEY_WAR_FLEET); 224 boolean pirate = memory.getBoolean(MemFlags.MEMORY_KEY_PIRATE); 225 boolean noMilitary = memory.getBoolean(MemFlags.FLEET_NO_MILITARY_RESPONSE); 226 if (!(patrol || warFleet || pirate) || noMilitary) return false; 227 228 return true; 229 } 230 231 protected String getResponseTotalKey() { 232 return "$mrs_" + params.responseReason; 233 } 234 235 protected void addToResponseTotal() { 236 MemoryAPI memory = params.faction.getMemoryWithoutUpdate(); 237 String key = getResponseTotalKey(); 238 239 float curr = memory.getFloat(key); 240 memory.set(key, curr + params.responseFraction, 60f); 241 } 242 243 protected void removeFromResponseTotal() { 244 MemoryAPI memory = params.faction.getMemoryWithoutUpdate(); 245 String key = getResponseTotalKey(); 246 247 float curr = memory.getFloat(key); 248 if (curr > params.responseFraction) { 249 memory.set(key, Math.max(0, curr - params.responseFraction), 60f); 250 } else { 251 memory.unset(key); 252 } 253 } 254 255 protected float getResponseTotal() { 256 MemoryAPI memory = params.faction.getMemoryWithoutUpdate(); 257 String key = getResponseTotalKey(); 258 259 float curr = memory.getFloat(key); 260 if (curr < params.responseFraction) curr = params.responseFraction; 261 if (curr < 1) curr = 1; 262 return curr; 263 } 264 265 public void forceDone() { 266 if (params != null) { 267 elapsed = params.responseDuration; 268 } 269 } 270 271 public boolean isDone() { 272 if (params == null || elapsed >= params.responseDuration) { 273 removeFromResponseTotal(); 274 params = null; 275 return true; 276 } 277 return false; 278 } 279 280 public boolean runWhilePaused() { 281 return false; 282 } 283 284 public MilitaryResponseParams getParams() { 285 return params; 286 } 287 288 public float getElapsed() { 289 return elapsed; 290 } 291 292 public void setElapsed(float elapsed) { 293 this.elapsed = elapsed; 294 } 295 296 297 298}