Starsector API
Loading...
Searching...
No Matches
NeuralLinkScript.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.skills;
2
3import java.util.ArrayList;
4import java.util.LinkedHashMap;
5import java.util.List;
6import java.util.Map;
7
8import java.awt.Color;
9
10import org.lwjgl.input.Mouse;
11
12import com.fs.starfarer.api.Global;
13import com.fs.starfarer.api.characters.PersonAPI;
14import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
15import com.fs.starfarer.api.combat.CombatEngineAPI;
16import com.fs.starfarer.api.combat.CombatFleetManagerAPI;
17import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
18import com.fs.starfarer.api.combat.ShipAIPlugin;
19import com.fs.starfarer.api.combat.ShipAPI;
20import com.fs.starfarer.api.combat.ShipCommand;
21import com.fs.starfarer.api.combat.ViewportAPI;
22import com.fs.starfarer.api.combat.WeaponGroupAPI;
23import com.fs.starfarer.api.impl.campaign.ids.Stats;
24import com.fs.starfarer.api.impl.hullmods.NeuralInterface;
25import com.fs.starfarer.api.input.InputEventAPI;
26import com.fs.starfarer.api.loading.WeaponGroupSpec;
27import com.fs.starfarer.api.mission.FleetSide;
28
30 public static String TRANSFER_CONTROL = "SHIP_TOGGLE_XPAN_MODE";
31
32 public static float INSTANT_TRANSFER_DP = 50;
33 public static float TRANSFER_SECONDS_PER_DP = 0.125f;
34 public static float TRANSFER_MAX_SECONDS = 5f;
35 public static float TRANSFER_MIN_SECONDS_IF_NOT_INSTANT = 1f;
36
37 // doesn't really work out - disables shields etc, and you also can't just
38 // switch to see how the ship is doing without really disrupting it
39 public static boolean ALLOW_ENGINE_CONTROL_DURING_TRANSFER = false;
40
41 public static final Object KEY_STATUS = new Object();
42 public static final Object KEY_STATUS2 = new Object();
43
44 public static final String TRANSFER_COMPLETE_KEY = "neural_transfer_complete_key";
45
46
47 public static class SavedShipControlState {
48 public ShipAPI ship;
49 public Map<WeaponGroupAPI, Boolean> autofiring = new LinkedHashMap<WeaponGroupAPI, Boolean>();
50 public WeaponGroupAPI selected;
51 }
52
53
54
57 this.engine = engine;
58 }
59
60 protected ShipAPI prevPlayerShip = null;
61 protected int skipFrames = 0;
62
63 protected List<ShipAPI> linked = new ArrayList<ShipAPI>();
64
65 protected float untilTransfer;
66 protected int lastShownTime = 0;
67
68 protected SavedShipControlState prevState;
69 protected SavedShipControlState savedState;
70
72
73 public void saveControlState(ShipAPI ship) {
75 savedState = new SavedShipControlState();
76 savedState.ship = ship;
77 for (WeaponGroupAPI group : ship.getWeaponGroupsCopy()) {
78 savedState.autofiring.put(group, group.isAutofiring());
79 }
80 savedState.selected = ship.getSelectedGroupAPI();
81 }
82 public void restoreControlState(ShipAPI ship) {
83 if (ship == null) {
84 return;
85 }
86
87 if (prevState == null || prevState.ship != ship) {
88 List<WeaponGroupAPI> groups = ship.getWeaponGroupsCopy();
89 int index = 0;
90 for (WeaponGroupSpec groupSpec : ship.getVariant().getWeaponGroups()) {
91 if (index >= groups.size()) break;
92
93 boolean auto = groupSpec.isAutofireOnByDefault();
94 WeaponGroupAPI group = groups.get(index);
95 if (group != null) {
96 if (auto) {
97 group.toggleOn();
98 } else {
99 group.toggleOff();
100 }
101 }
102 index++;
103 }
104 if (groups.size() >= 1) {
105 ship.giveCommand(ShipCommand.SELECT_GROUP, null, 0);
106 }
107 return;
108 }
109
110
111 for (WeaponGroupAPI group : ship.getWeaponGroupsCopy()) {
112 Boolean auto = prevState.autofiring.get(group);
113 if (auto == null) auto = false;
114 if (auto) {
115 group.toggleOn();
116 } else {
117 group.toggleOff();
118 }
119 }
120 int index = ship.getWeaponGroupsCopy().indexOf(prevState.selected);
121 if (index > 0) {
122 ship.giveCommand(ShipCommand.SELECT_GROUP, null, index);
123 }
124 }
125
126 public void advance(float amount, List<InputEventAPI> events) {
127 if (engine == null) return;
128 if (engine.isPaused()) return;
129
130
131 ShipAPI playerShip = engine.getPlayerShip();
132 if (playerShip == null) {
133 return;
134 }
135
136 if (!playerShip.isAlive()) {
137 untilTransfer = 0f;
138 lastShownTime = 0;
139 }
140
141 if (untilTransfer > 0) {
142 float timeMult = playerShip.getMutableStats().getTimeMult().getModifiedValue();
143 untilTransfer -= amount * timeMult;
144
146// Global.getSoundPlayer().applyLowPassFilter(1f - (1f - spec.getFilterGain()) * level * mult,
147// 1f - (1f - spec.getFilterGainHF()) * level * mult);
148 //engine.getCombatUI().setShipInfoFanOutBrightness(0f);
150 if (untilTransfer <= 0) {
151 untilTransfer = 0;
152 Global.getSoundPlayer().playUISound("ui_neural_transfer_complete", 1f, 1f);
153 playerShip.setCustomData(TRANSFER_COMPLETE_KEY, true);
156 boolean autopilot = engine.getCombatUI().isAutopilotOn();
157 if (autopilot) {
158 if (playerShip.getAI() == null) { // somehow?
160 DeployedFleetMemberAPI member = manager.getDeployedFleetMember(playerShip);
161 playerShip.setShipAI(Global.getSettings().pickShipAIPlugin(member == null ? null : member.getMember(), playerShip));
162 }
163 } else {
164 playerShip.setShipAI(null);
165 restoreControlState(playerShip);
166 }
167 } else {
170 }
171 }
172
173 // if the player changed flagships:
174 // skip a few frames to make sure the status ends up on top of the status list
175 if (playerShip != prevPlayerShip) {
176 prevPlayerShip = playerShip;
177 skipFrames = 30;
178 }
179
180 if (skipFrames > 0) {
181 skipFrames--;
182 return;
183 }
184
186 }
187
201
203 ShipAPI playerShip = engine.getPlayerShip();
204 if (playerShip == null) return;
205 float timeMult = playerShip.getMutableStats().getTimeMult().getModifiedValue();
206 Color color = new Color(0,121,216,255);
207
208// feels kind of annoying
209// if (untilTransfer <= 0) {
210// engine.addFloatingTextAlways(playerShip.getLocation(), "Transfer complete",
211// getFloatySize(playerShip), color, playerShip, 5f * timeMult, 2f, 1f/timeMult, 0f, 0f,
212// 1f);
213// return;
214// }
215
216 int show = (int) Math.ceil(untilTransfer);
217 if (show != lastShownTime) {
218 if (show > 0) {
219 engine.addFloatingTextAlways(playerShip.getLocation(), "Neural transfer in " + show,
220 getFloatySize(playerShip), color, playerShip, 4f * timeMult, 0.8f/timeMult, 1f/timeMult, 0f, 0f,
221 1f);
222 }
223 lastShownTime = show;
224 //Global.getSoundPlayer().playUISound("ui_hold_fire_on", 2f, 0.25f);
225 }
226 }
227
228 public boolean canLink(ShipAPI ship) {
229 // below line: debug, work for every ship
230 //if (ship.isAlive() && !ship.isShuttlePod()) return true;
231
232 ShipAPI playerShip = engine.getPlayerShip();
233 // transferred command to officer'ed ship, can't link
234 if (ship == playerShip && ship.getOriginalCaptain() != null &&
236 return false;
237 }
238
241 return false;
242 }
243 }
244
245 boolean aliveOrDisabledButNonPhysical = ship.isAlive();
247 if (ship == playerShip && ship != physicalLocation) {
248 aliveOrDisabledButNonPhysical = true;
249 }
250
251 return aliveOrDisabledButNonPhysical && !ship.isShuttlePod() &&
253 //ship.getVariant().hasHullMod(HullMods.NEURAL_INTERFACE);
254 }
255
256 public void updateLinkState() {
257 ShipAPI playerShip = engine.getPlayerShip();
258 if (playerShip == null) return;
259
261 for (ShipAPI ship : new ArrayList<ShipAPI>(linked)) {
262 if (!ship.isAlive()) {
263 if (ship == playerShip && ship != physicalLocation) {
264 continue;
265 }
266
267 if (ship != playerShip) {
268 PersonAPI orig = ship.getOriginalCaptain();
269 if (orig.isPlayer()) {
270 orig = Global.getFactory().createPerson();
273 }
274 }
275 ship.setCaptain(orig);
276 }
277 linked.remove(ship);
278 }
279 }
280
281 boolean physicallyPresent = linked.contains(physicalLocation);
282 if (!linked.contains(playerShip) || !physicallyPresent ||
283 !canLink(playerShip)) {
284 for (ShipAPI ship : linked) {
285 PersonAPI orig = ship.getOriginalCaptain();
286 if (orig.isPlayer()) {
287 orig = Global.getFactory().createPerson();
290 }
291 }
292 if (ship.getCaptain() != orig) {
293 ship.setCaptain(orig);
294 if (ship.getFleetMember() != null) {
295 ship.getFleetMember().setCaptain(ship.getOriginalCaptain());
296 }
297 }
298 }
299 linked.clear();
300 if (canLink(playerShip)) {
301 linked.add(playerShip);
302 }
303 }
304
305 if (linked.isEmpty()) return;
306
307
309 List<DeployedFleetMemberAPI> members = manager.getDeployedCopyDFM();
310
311 if (physicallyPresent) {
312 for (DeployedFleetMemberAPI dfm : members) {
313 if (linked.size() >= 2) break;
314
315 if (dfm.isFighterWing()) continue;
316 if (dfm.isAlly()) continue;
317
318 ShipAPI ship = dfm.getShip();
319 if (linked.contains(ship)) continue;
320 if (!ship.getCaptain().isDefault() && ship != playerShip &&
321 !ship.getCaptain().isPlayer()) {
322 // this last for when the player deploys a ship with NI in the simulator and then
323 // deploys their actual flagship - so, the player ship doesn't *actually* have the player in it
324 // but the other ship does
325 continue;
326 }
327
328 // transferred command to an officer'ed ship, no link
329 if (ship == playerShip && ship.getOriginalCaptain() != null &&
330 !ship.getOriginalCaptain().isDefault() &&
331 !ship.getOriginalCaptain().isPlayer()) {
332 continue;
333 }
334 if (ship.controlsLocked()) continue;
335
336 if (canLink(ship)) {
337 linked.add(ship);
338 }
339 }
340 }
341
342 PersonAPI player = playerPerson;
343 if (player == null) {
344 player = playerShip.getCaptain();
345 if (!player.isDefault() && playerPerson == null) {
346 playerPerson = player;
347 }
348 }
349
350 for (ShipAPI ship : linked) {
351 if (ship.getCaptain() != player) {
352 ship.setCaptain(player);
353 if (ship.getFleetMember() != null) {
354 ship.getFleetMember().setCaptain(player);
355 }
356 }
357 }
358
359 if (linked.contains(playerShip)) {
360 ShipAPI other = null;
361 for (ShipAPI ship : linked) {
362 if (ship != playerShip) {
363 other = ship;
364 break;
365 }
366 }
367
368 String title = "Neural System Reset";
369 //String title = "System Reset on Transfer";
370 String icon = Global.getSettings().getSpriteName("ui", "icon_neural_link");
372// Float timeout = (Float) Global.getCombatEngine().getCustomData().get(key);
373// if (timeout == null) timeout = 0f;
374 Float timeout = null;
375 if (other != null) timeout = (Float) other.getCustomData().get(key);
376 if (timeout == null) timeout = 0f;
377 if (other == null) {
378 engine.maintainStatusForPlayerShip(KEY_STATUS2, icon, title, "No signal", true);
379 } else if (timeout <= 0) {
380 engine.maintainStatusForPlayerShip(KEY_STATUS2, icon, title, "Ready on transfer", false);
381 } else {
382 int show = (int) Math.ceil(timeout);
383 engine.maintainStatusForPlayerShip(KEY_STATUS2, icon, title, "Ready in " + show + " seconds", true);
384 }
385 }
386
387 for (ShipAPI ship : linked) {
388 if (ship != playerShip) {
389
390 if (untilTransfer <= 0f) {
391 String title = "Neural Link Active";
392 //String data = ship.getName() + ", " + ship.getHullSpec().getHullNameWithDashClass();
393 //String data = ship.getName() + ", " + ship.getHullSpec().getHullName();
394 String data = "Target: " + ship.getHullSpec().getHullNameWithDashClass();
395 String icon = Global.getSettings().getSpriteName("ui", "icon_neural_link");
396 engine.maintainStatusForPlayerShip(KEY_STATUS, icon, title, data, false);
397 } else {
398 int show = (int) Math.ceil(untilTransfer);
399 if (show > 0) {
400 String title = "Neural Transfer";
401 String data = "Link in " + show + " seconds";
402 String icon = Global.getSettings().getSpriteName("ui", "icon_neural_link");
403 engine.maintainStatusForPlayerShip(KEY_STATUS, icon, title, data, true);
404 }
405 }
406
407 break;
408 }
409 }
410
411 if (linked.size() <= 1 && linked.contains(playerShip)) {
412 String title = "Neural Link Inactive";
413 String data = "No signal";
414 if (!physicallyPresent) {
415 data = "requires physical transfer";
416 }
417 String icon = Global.getSettings().getSpriteName("ui", "icon_neural_link");
418 engine.maintainStatusForPlayerShip(KEY_STATUS, icon, title, data, true);
419 }
420 }
421
422
423 public void processInputPreCoreControls(float amount, List<InputEventAPI> events) {
424 if (engine == null || engine.getCombatUI() == null || engine.getCombatUI().isShowingCommandUI()) return;
425
426 ShipAPI playerShip = engine.getPlayerShip();
427 if (playerShip == null) return;
428 if (!linked.contains(playerShip) || linked.size() < 2) return;
429 //if (untilTransfer > 0) return;
430
431 for (InputEventAPI event : events) {
432 if (event.isConsumed()) continue;
433
434 if (event.isControlDownEvent(TRANSFER_CONTROL)) {
435 if (untilTransfer <= 0) {
436 for (ShipAPI ship : linked) {
437 if (ship != playerShip) {
439 lastShownTime = 0;
440 doTransfer(ship);
441 }
442 }
443 }
444 event.consume();
445 return;
446 }
447 }
448 }
449
450
451 public void doTransfer(ShipAPI ship) {
452 if (ship == null) return;
453 ShipAPI playerShip = engine.getPlayerShip();
454 if (playerShip == null) return;
455 if (!linked.contains(playerShip)) return;
456 if (!linked.contains(ship)) return;
457
458 if (untilTransfer <= 0) {
459 Global.getSoundPlayer().playUISound("ui_neural_transfer_complete", 1f, 1f);
463 } else {
464 Global.getSoundPlayer().playUISound("ui_neural_transfer_begin", 1f, 1f);
465 }
466
467 // I have it on good authority that this looks bad.
468 // "[it looks like] some vague energy field effect, then like Troi would have a headache and some dubious writing would occur"
469// float animDur = Math.max(untilTransfer, 0.5f);
470// engine.addLayeredRenderingPlugin(new NeuralTransferVisual(playerShip, ship, animDur));
471
472 Mouse.setCursorPosition((int)Global.getSettings().getScreenWidthPixels()/2,
474
475 saveControlState(playerShip);
476
477 ShipAIPlugin playerShipAI = playerShip.getShipAI(); // non-null if autopilot is on
478 ShipAIPlugin prevTargetAI = ship.getShipAI();
480 if (ship.getFleetMember() != null) {
481 ship.getFleetMember().setCaptain(playerShip.getCaptain());
482 }
483
484 if (playerShipAI != null) {
485 playerShip.setShipAI(playerShipAI); // not the player ship anymore, the old ship
486 }
487
488 boolean autopilot = engine.getCombatUI().isAutopilotOn();
489
490 if (untilTransfer > 0) {
492 ship.setShipAI(prevTargetAI);
493 }
494 //engine.getCombatUI().setDisablePlayerShipControlOneFrame(true);
497 } else if (autopilot) {
499 DeployedFleetMemberAPI member = manager.getDeployedFleetMember(ship);
500 ship.setShipAI(Global.getSettings().pickShipAIPlugin(member == null ? null : member.getMember(), ship));
501 }
502
503 if (untilTransfer <= 0) {
505 if (!autopilot) {
507 }
508 }
509 }
510
511
512 public float getTransferTime() {
513 float total = 0f;
514 boolean instant = false;
515 for (ShipAPI ship : linked) {
516 if (ship.getFleetMember() == null) continue;
517 total += ship.getFleetMember().getDeploymentPointsCost();
518 if (ship == Global.getCombatEngine().getPlayerShip() &&
520 instant = true;
521 }
522 }
523
524 total = Math.round(total);
525 if (instant) total = 0f;
526
527 //INSTANT_TRANSFER_DP = 0f;
528
529 if (total <= INSTANT_TRANSFER_DP) return 0f;
530
531 float time = (total - INSTANT_TRANSFER_DP) * TRANSFER_SECONDS_PER_DP;
534 } else if (time > TRANSFER_MAX_SECONDS) {
536 }
537 time = (float) Math.ceil(time);
538
539 return time;
540 }
541
542
543 public void renderInUICoords(ViewportAPI viewport) {
544 }
545
546 public void renderInWorldCoords(ViewportAPI viewport) {
547 }
548
549 public static float getFloatySize(ShipAPI ship) {
550 switch (ship.getHullSize()) {
551 case FIGHTER: return 15f;
552 case FRIGATE: return 17f;
553 case DESTROYER: return 21f;
554 case CRUISER: return 24f;
555 case CAPITAL_SHIP: return 27f;
556 }
557 return 10f;
558 }
559}
static SettingsAPI getSettings()
Definition Global.java:57
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:49
static FactoryAPI getFactory()
Definition Global.java:41
static CombatEngineAPI getCombatEngine()
Definition Global.java:69
static SectorAPI getSector()
Definition Global.java:65
float computeEffective(float baseValue)
static final String INSTANT_NEURAL_TRANSFER_FROM
Definition Stats.java:21
void processInputPreCoreControls(float amount, List< InputEventAPI > events)
void advance(float amount, List< InputEventAPI > events)
String getSpriteName(String category, String id)
ShipAIPlugin pickShipAIPlugin(FleetMemberAPI member, ShipAPI ship)
void applyLowPassFilter(float gain, float gainHF)
SoundAPI playUISound(String id, float pitch, float volume)
MutableCharacterStatsAPI getPlayerStats()
void setPersonality(String personality)
void maintainStatusForPlayerShip(Object key, String spriteName, String title, String data, boolean isDebuff)
CombatFleetManagerAPI getFleetManager(FleetSide side)
void addFloatingTextAlways(Vector2f loc, String text, float size, Color color, CombatEntityAPI attachedTo, float flashFrequency, float flashDuration, float durInPlace, float durFloatingUp, float durFadingOut, float baseAlpha)
void setCustomData(String key, Object data)
List< DeployedFleetMemberAPI > getDeployedCopyDFM()
DeployedFleetMemberAPI getDeployedFleetMember(ShipAPI ship)
void setDisablePlayerShipControlOneFrame(boolean disablePlayerShipControlOneFrame)
MutableShipStatsAPI getMutableStats()
void setShipAI(ShipAIPlugin ai)
List< WeaponGroupAPI > getWeaponGroupsCopy()
void blockCommandForOneFrame(ShipCommand command)
void giveCommand(ShipCommand command, Object param, int groupNumber)
List< WeaponGroupSpec > getWeaponGroups()
void setCaptain(PersonAPI commander)