Starsector API
Loading...
Searching...
No Matches
InterdictionPulseAbility.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.abilities;
2
3import java.awt.Color;
4
5import com.fs.starfarer.api.EveryFrameScript;
6import com.fs.starfarer.api.Global;
7import com.fs.starfarer.api.campaign.BattleAPI;
8import com.fs.starfarer.api.campaign.CampaignFleetAPI;
9import com.fs.starfarer.api.campaign.SectorEntityToken.VisibilityLevel;
10import com.fs.starfarer.api.campaign.ai.FleetAIFlags;
11import com.fs.starfarer.api.campaign.ai.ModularFleetAIAPI;
12import com.fs.starfarer.api.campaign.econ.MarketAPI;
13import com.fs.starfarer.api.campaign.rules.MemoryAPI;
14import com.fs.starfarer.api.characters.AbilityPlugin;
15import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
16import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
17import com.fs.starfarer.api.impl.campaign.ids.Abilities;
18import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
19import com.fs.starfarer.api.impl.campaign.ids.Pings;
20import com.fs.starfarer.api.loading.CampaignPingSpec;
21import com.fs.starfarer.api.ui.LabelAPI;
22import com.fs.starfarer.api.ui.TooltipMakerAPI;
23import com.fs.starfarer.api.util.Misc;
24
26
27 public static class IPReactionScript implements EveryFrameScript {
28 float delay;
29 boolean done;
30 CampaignFleetAPI other;
31 CampaignFleetAPI fleet;
32 float activationDays;
39 public IPReactionScript(CampaignFleetAPI fleet, CampaignFleetAPI other, float activationDays) {
40 this.fleet = fleet;
41 this.other = other;
42 this.activationDays = activationDays;
43 delay = 0.3f + 0.3f * (float) Math.random();
44 //delay = 0f;
45 }
46 public void advance(float amount) {
47 if (done) return;
48
49 delay -= amount;
50 if (delay > 0) return;
51
52 VisibilityLevel level = fleet.getVisibilityLevelTo(other);
53 if (level == VisibilityLevel.NONE || level == VisibilityLevel.SENSOR_CONTACT) {
54 done = true;
55 return;
56 }
57
58 if (!(other.getAI() instanceof ModularFleetAIAPI)) {
59 done = true;
60 return;
61 }
63
64
65 float dist = Misc.getDistance(fleet.getLocation(), other.getLocation());
66 float speed = Math.max(1f, other.getTravelSpeed());
67 float eta = dist / speed;
68
69 float rushTime = activationDays * Global.getSector().getClock().getSecondsPerDay();
70 rushTime += 0.5f + 0.5f * (float) Math.random();
71
74
75 if (eta < rushTime && pursueTarget == fleet) {
76 done = true;
77 return;
78 }
79
80 float range = InterdictionPulseAbility.getRange(fleet);
81 float getAwayTime = 1f + (range - dist) / speed;
83 if (getAwayTime > rushTime && sb != null && sb.isUsable() && (float) Math.random() > 0.67f) {
84 sb.activate();
85 done = true;
86 return;
87 }
88
89 //float avoidRange = Math.min(dist, getRange(other));
90 float avoidRange = getRange(other) + 100f;
92 fleet.getLocation(), avoidRange, avoidRange + 50f, activationDays + 0.01f);
93
95 //fleet.getLocation(), dist, dist + 50f, activationDays + 0.01f);
96 Misc.getPointAtRadius(fleet.getLocation(), avoidRange * 0.5f), avoidRange, avoidRange * 1.5f + 50f, activationDays + 0.05f);
97
98 done = true;
99 }
100
101 public boolean isDone() {
102 return done;
103 }
104 public boolean runWhilePaused() {
105 return false;
106 }
107 }
108
109 public static final float MAX_EFFECT = 1f;
110 //public static final float RANGE = 1000f;
111 public static final float BASE_RANGE = 500f;
112 public static final float BASE_SECONDS = 6f;
113 public static final float STRENGTH_PER_SECOND = 200f;
114
115 //public static final float CR_COST_MULT = 0.5f;
116 public static final float DETECTABILITY_PERCENT = 100f;
117
118// public String getSpriteName() {
119// return Global.getSettings().getSpriteName("abilities", Abilities.EMERGENCY_BURN);
120// }
121
122
123 public static float getRange(CampaignFleetAPI fleet) {
124 float max = Global.getSettings().getMaxSensorRange();
125 return Math.min(max, BASE_RANGE + fleet.getSensorRangeMod().computeEffective(fleet.getSensorStrength()) / 2f);
126 }
127
128 @Override
129 protected String getActivationText() {
130 //return Misc.ucFirst(spec.getName().toLowerCase());
131 return "Interdiction pulse";
132 }
133
134
135 protected Boolean primed = null;
136 protected Float elapsed = null;
137 protected Integer numFired = null;
138
139 @Override
140 protected void activateImpl() {
141 CampaignFleetAPI fleet = getFleet();
142 if (fleet == null) return;
143
145
146 float range = getRange(fleet);
147 for (CampaignFleetAPI other : fleet.getContainingLocation().getFleets()) {
148 if (other == fleet) continue;
149
150 float dist = Misc.getDistance(fleet.getLocation(), other.getLocation());
151 if (dist > range + 500f) continue;
152
153 other.addScript(new IPReactionScript(fleet, other, getActivationDays()));
154 }
155
156 primed = true;
157
158 }
159
160 protected void showRangePing(float amount) {
161 CampaignFleetAPI fleet = getFleet();
162 if (fleet == null) return;
163
164 VisibilityLevel vis = fleet.getVisibilityLevelToPlayerFleet();
165 if (vis == VisibilityLevel.NONE || vis == VisibilityLevel.SENSOR_CONTACT) return;
166
167
168 boolean fire = false;
169 if (elapsed == null) {
170 elapsed = 0f;
171 numFired = 0;
172 fire = true;
173 }
174 elapsed += amount;
175 if (elapsed > 0.5f && numFired < 4) {
176 elapsed -= 0.5f;
177 fire = true;
178 }
179
180 if (fire) {
181 numFired++;
182
183 float range = getRange(fleet);
184 CampaignPingSpec custom = new CampaignPingSpec();
185 custom.setUseFactionColor(true);
186 custom.setWidth(7);
187 custom.setMinRange(range - 100f);
188 custom.setRange(200);
189 custom.setDuration(2f);
190 custom.setAlphaMult(0.25f);
191 custom.setInFraction(0.2f);
192 custom.setNum(1);
193
194 Global.getSector().addPing(fleet, custom);
195 }
196
197 }
198
199 @Override
200 protected void applyEffect(float amount, float level) {
201 CampaignFleetAPI fleet = getFleet();
202 if (fleet == null) return;
203
205
206 //System.out.println("Level: " + level);
207
208 if (level > 0 && level < 1 && amount > 0) {
209 showRangePing(amount);
210// float activateSeconds = getActivationDays() * Global.getSector().getClock().getSecondsPerDay();
211// float speed = fleet.getVelocity().length();
212// float acc = Math.max(speed, 200f)/activateSeconds + fleet.getAcceleration();
213// float ds = acc * amount;
214// if (ds > speed) ds = speed;
215// Vector2f dv = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(fleet.getVelocity()));
216// dv.scale(ds);
217// fleet.setVelocity(fleet.getVelocity().x - dv.x, fleet.getVelocity().y - dv.y);
218 fleet.goSlowOneFrame();
219 return;
220 }
221
222 float range = getRange(fleet);
223
224 boolean playedHit = !(entity.isInCurrentLocation() && entity.isVisibleToPlayerFleet());
225 if (level == 1 && primed != null) {
226
229 }
231
232 CampaignPingSpec custom = new CampaignPingSpec();
233 custom.setUseFactionColor(true);
234 custom.setWidth(15);
235 custom.setRange(range * 1.3f);
236 custom.setDuration(0.5f);
237 custom.setAlphaMult(1f);
238 custom.setInFraction(0.1f);
239 custom.setNum(1);
240 Global.getSector().addPing(fleet, custom);
241
242
243 for (CampaignFleetAPI other : fleet.getContainingLocation().getFleets()) {
244 if (other == fleet) continue;
245 if (other.getFaction() == fleet.getFaction()) continue;
246 if (other.isInHyperspaceTransition()) continue;
247
248 float dist = Misc.getDistance(fleet.getLocation(), other.getLocation());
249 if (dist > range) continue;
250
251
252 float interdictSeconds = getInterdictSeconds(fleet, other);
253 if (interdictSeconds > 0 && interdictSeconds < 1f) interdictSeconds = 1f;
254
255 VisibilityLevel vis = other.getVisibilityLevelToPlayerFleet();
256 if (vis == VisibilityLevel.COMPOSITION_AND_FACTION_DETAILS ||
257 vis == VisibilityLevel.COMPOSITION_DETAILS ||
258 (vis == VisibilityLevel.SENSOR_CONTACT && fleet.isPlayerFleet())) {
259 if (interdictSeconds <= 0) {
260 other.addFloatingText("Interdict avoided!" , fleet.getFaction().getBaseUIColor(), 1f, true);
261 continue;
262 } else {
263 other.addFloatingText("Interdict! (" + (int) Math.round(interdictSeconds) + "s)" , fleet.getFaction().getBaseUIColor(), 1f, true);
264 }
265 }
266
267 float interdictDays = interdictSeconds / Global.getSector().getClock().getSecondsPerDay();
268
269 for (AbilityPlugin ability : other.getAbilities().values()) {
270 if (!ability.getSpec().hasTag(Abilities.TAG_BURN + "+") &&
271 !ability.getSpec().hasTag(Abilities.TAG_DISABLED_BY_INTERDICT) &&
272 !ability.getId().equals(Abilities.INTERDICTION_PULSE)) continue;
273
274 float origCooldown = ability.getCooldownLeft();
275 float extra = 0;
276 if (ability.isActiveOrInProgress()) {
277 extra += ability.getSpec().getDeactivationCooldown() * ability.getProgressFraction();
278 ability.deactivate();
279
280 }
281
282 if (!ability.getSpec().hasTag(Abilities.TAG_BURN + "+")) continue;
283
284 float cooldown = interdictDays;
285 //cooldown = Math.max(cooldown, origCooldown);
286 cooldown += origCooldown;
287 cooldown += extra;
288 float max = Math.max(ability.getSpec().getDeactivationCooldown(), 2f);
289 if (cooldown > max) cooldown = max;
290 ability.setCooldownLeft(cooldown);
291 }
292
293 if (fleet.isPlayerFleet() && other.knowsWhoPlayerIs() && fleet.getFaction() != other.getFaction()) {
295 new RepActionEnvelope(RepActions.INTERDICTED, null, null, false),
296 other.getFaction().getId());
297 }
298
299 if (!playedHit) {
300 Global.getSoundPlayer().playSound("world_interdict_hit", 1f, 1f, other.getLocation(), other.getVelocity());
301 //playedHit = true;
302 }
303 }
304
305 primed = null;
306 elapsed = null;
307 numFired = null;
308 }
309
310 }
311
312 public static float getInterdictSeconds(CampaignFleetAPI fleet, CampaignFleetAPI other) {
313 float offense = fleet.getSensorRangeMod().computeEffective(fleet.getSensorStrength());
314 float defense = other.getSensorRangeMod().computeEffective(other.getSensorStrength());
315 float diff = offense - defense;
316
317 float extra = diff / STRENGTH_PER_SECOND;
318
319 float total = BASE_SECONDS + extra;
320 if (total < 0f) total = 0f;
321 return total;// / Global.getSector().getClock().getSecondsPerDay();
322 }
323
324
325// public static float getEffectMagnitude(CampaignFleetAPI fleet, CampaignFleetAPI other) {
326// float burn = Misc.getBurnLevelForSpeed(other.getVelocity().length());
327//
328// Vector2f velDir = Misc.normalise(new Vector2f(other.getVelocity()));
329// Vector2f toFleet = Misc.normalise(Vector2f.sub(fleet.getLocation(), other.getLocation(), new Vector2f()));
330// float dot = Vector2f.dot(velDir, toFleet);
331// if (dot <= 0.05f || burn <= 1f) return 0f;
332//
333// float effect = dot;
334// if (effect < 0) effect = 0;
335// if (effect > 1) effect = 1;
336//
337// //effect *= Math.min(1f, burn / 10f);
338//
339// //return effect;
340// return Math.max(0.1f, effect);
341// }
342
343 @Override
344 protected void deactivateImpl() {
345 cleanupImpl();
346 }
347
348 @Override
349 protected void cleanupImpl() {
350 CampaignFleetAPI fleet = getFleet();
351 if (fleet == null) return;
352
354 //fleet.getStats().getSensorRangeMod().unmodify(getModId());
355 //fleet.getStats().getFleetwideMaxBurnMod().unmodify(getModId());
356 //fleet.getStats().getAccelerationMult().unmodify(getModId());
357 //fleet.getCommanderStats().getDynamic().getStat(Stats.NAVIGATION_PENALTY_MULT).unmodify(getModId());
358
359 primed = null;
360 }
361
362
363 @Override
364 public boolean isUsable() {
365 return super.isUsable() &&
366 getFleet() != null;// &&
367 //getNonReadyShips().isEmpty();
368 }
369
370// protected List<FleetMemberAPI> getNonReadyShips() {
371// List<FleetMemberAPI> result = new ArrayList<FleetMemberAPI>();
372// CampaignFleetAPI fleet = getFleet();
373// if (fleet == null) return result;
374//
375// float crCostFleetMult = fleet.getStats().getDynamic().getValue(Stats.EMERGENCY_BURN_CR_MULT);
376// for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
377// //if (member.isMothballed()) continue;
378// float crLoss = member.getDeployCost() * CR_COST_MULT * crCostFleetMult;
379// if (Math.round(member.getRepairTracker().getCR() * 100) < Math.round(crLoss * 100)) {
380// result.add(member);
381// }
382// }
383// return result;
384// }
385
386// protected float computeSupplyCost() {
387// CampaignFleetAPI fleet = getFleet();
388// if (fleet == null) return 0f;
389//
390// float crCostFleetMult = fleet.getStats().getDynamic().getValue(Stats.EMERGENCY_BURN_CR_MULT);
391//
392// float cost = 0f;
393// for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
394// cost += member.getDeploymentPointsCost() * CR_COST_MULT * crCostFleetMult;
395// }
396// return cost;
397// }
398
399
400 @Override
401 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded) {
402 CampaignFleetAPI fleet = getFleet();
403 if (fleet == null) return;
404
405 Color gray = Misc.getGrayColor();
406 Color highlight = Misc.getHighlightColor();
407 Color fuel = Global.getSettings().getColor("progressBarFuelColor");
408 Color bad = Misc.getNegativeHighlightColor();
409
411 LabelAPI title = tooltip.addTitle("Interdiction Pulse");
412 } else {
413 tooltip.addSpacer(-10f);
414 }
415
416 float pad = 10f;
417
418 int range = (int) getRange(fleet);
419
420
421 tooltip.addPara("Slows* the fleet and uses its active sensor network to charge and release a powerful energy pulse that " +
422 "can disrupt the drive fields of nearby fleets.", pad);
423
425 Color hc = highlight;
427 tooltip.addPara("The disruption interrupts any movement-related abilities (such as %s or %s) " +
428 "and prevents their use for some time afterwards. Also interrupts charging interdiction pulses.", pad,
429 hc, "Sustained Burn", "Emergency Burn");
430
431 tooltip.addPara("The disruption lasts for %s seconds, modified by %s second for " +
432 "every %s points of difference in the fleets' sensor strengths.", pad, highlight,
433 "" + (int) BASE_SECONDS,
434 "" + (int) 1,
435 "" + (int) STRENGTH_PER_SECOND);
436
437 tooltip.addPara("Base range of %s* units, increased by half your fleet's sensor strength, " +
438 "for a total of %s units. While the pulse is charging, the range at which the fleet can be detected will " +
439 "gradually increase by up to %s.", pad, highlight,
440 "" + (int) BASE_RANGE,
441 "" + range,
442 "" + (int) DETECTABILITY_PERCENT + "%");
443
444 tooltip.addPara("A successful interdict is considered a hostile act, though not on the same level as " +
445 "open warfare.", pad);
446
447 tooltip.addPara("*2000 units = 1 map grid cell", gray, pad);
448 tooltip.addPara("*A fleet is considered slow-moving at a burn level of half that of its slowest ship.", gray, pad);
449 addIncompatibleToTooltip(tooltip, expanded);
450 }
451
452 public boolean hasTooltip() {
453 return true;
454 }
455
456
457 @Override
458 public void fleetLeftBattle(BattleAPI battle, boolean engagedInHostilities) {
459 if (engagedInHostilities) {
460 deactivate();
461 }
462 }
463
464 @Override
465 public void fleetOpenedMarket(MarketAPI market) {
466 deactivate();
467 }
468
469}
470
471
472
473
474
static SettingsAPI getSettings()
Definition Global.java:57
static boolean CODEX_TOOLTIP_MODE
Definition Global.java:15
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:49
static SectorAPI getSector()
Definition Global.java:65
void modifyPercent(String source, float value)
float computeEffective(float baseValue)
void addIncompatibleToTooltip(TooltipMakerAPI tooltip, boolean expanded)
static float getInterdictSeconds(CampaignFleetAPI fleet, CampaignFleetAPI other)
void fleetLeftBattle(BattleAPI battle, boolean engagedInHostilities)
static final String GLOBAL_INTERDICTION_PULSE_JUST_USED_IN_CURRENT_LOCATION
Definition MemFlags.java:53
static Color getNegativeHighlightColor()
Definition Misc.java:802
static Color getBasePlayerColor()
Definition Misc.java:833
static Color getGrayColor()
Definition Misc.java:826
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static Color getHighlightColor()
Definition Misc.java:792
static Color getTooltipTitleAndLightHighlightColor()
Definition Misc.java:799
static Vector2f getPointAtRadius(Vector2f from, float r)
Definition Misc.java:697
SoundAPI playSound(String id, float pitch, float volume, Vector2f loc, Vector2f vel)
List< CampaignFleetAPI > getFleets()
ReputationAdjustmentResult adjustPlayerReputation(Object action, String factionId)
EveryFrameScript addPing(SectorEntityToken entity, String pingType)
VisibilityLevel getVisibilityLevelTo(SectorEntityToken other)
void avoidLocation(LocationAPI containingLocation, Vector2f loc, float minRange, float maxRange, float duration)
void set(String key, Object value)
CampaignFleetAPI getFleet(String key)
LabelAPI addPara(String format, float pad, Color hl, String... highlights)
UIComponentAPI addSpacer(float height)