mirror of
https://github.com/lexi-lambda/shattered-plans.git
synced 2024-11-25 07:48:04 +03:00
Refactor tactical overview calculation into TacticalAnalysis class
This commit is contained in:
parent
f7e1dc1b91
commit
17d675bdb4
@ -3214,17 +3214,17 @@ public final class GameUI {
|
||||
this.gameSession.gameView.stopCombatAnimations();
|
||||
this.gameSession.recalculateSystemState();
|
||||
if (this.gameSession.localPlayer != null) {
|
||||
final StatusPanelState var2 = (StatusPanelState) this.statusPanel.state;
|
||||
if (var2.icon.isEmpty() && this.gameSession.placementMode != PlacementMode.BUILD_FLEET) {
|
||||
final StatusPanelState statusState = (StatusPanelState) this.statusPanel.state;
|
||||
if (statusState.icon.isEmpty() && this.gameSession.placementMode != PlacementMode.BUILD_FLEET) {
|
||||
if (this.gameSession.gameState.gameOptions.unifiedTerritories) {
|
||||
if (this.gameSession.localPlayer.combinedForce.fleetsAvailableToBuild > 0) {
|
||||
this.activateFleetPlacement(this.gameSession.localPlayer.combinedForce, true);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
for (final ContiguousForce var3 : this.gameSession.localPlayer.contiguousForces) {
|
||||
if (var3.fleetsAvailableToBuild > 0) {
|
||||
this.activateFleetPlacement(var3, true);
|
||||
for (final ContiguousForce force : this.gameSession.localPlayer.contiguousForces) {
|
||||
if (force.fleetsAvailableToBuild > 0) {
|
||||
this.activateFleetPlacement(force, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -3233,7 +3233,7 @@ public final class GameUI {
|
||||
|
||||
this.gameSession.endTurn();
|
||||
this.endTurnButton.activate();
|
||||
var2.icon.setSprite(null);
|
||||
statusState.icon.setSprite(null);
|
||||
if (this.gameSession.playersWaitingOn == 1) {
|
||||
this.setActionHint(StringConstants.TEXT_WAITING_FOR_PLAYER);
|
||||
} else {
|
||||
|
@ -45,16 +45,13 @@ public abstract class AbstractGameView {
|
||||
public boolean isAnimatingViewport;
|
||||
public StarSystem targetedSystem;
|
||||
public MoveFleetsOrder selectedFleetOrder;
|
||||
protected int[] possibleSystemCollapseStages;
|
||||
protected Random random;
|
||||
protected int animationTick;
|
||||
public SystemHighlight[] highlightedSystems;
|
||||
protected int[] systemDrawX;
|
||||
protected List<CombatEngagementAnimationState> combatEngagements;
|
||||
protected int[] clonedRemainingGarrisons;
|
||||
protected boolean[] willOwnSystem;
|
||||
protected int _n;
|
||||
protected int[] systemCollapseStages;
|
||||
private int largestFleetMovement;
|
||||
protected int largestFleetBuildQuantity;
|
||||
protected List<MoveFleetsAnimationState> fleetMovements;
|
||||
@ -66,7 +63,6 @@ public abstract class AbstractGameView {
|
||||
protected Player[] clonedSystemOwners;
|
||||
protected Collection<ProjectOrder> projectOrders;
|
||||
protected List<MoveFleetsAnimationState> combatRetreats;
|
||||
protected boolean[] canOwnSystem;
|
||||
protected int[] systemDrawY;
|
||||
protected Player[] systemOwners;
|
||||
@MagicConstant(valuesFromClass = GameView.AnimationPhase.class)
|
||||
@ -243,16 +239,6 @@ public abstract class AbstractGameView {
|
||||
}
|
||||
}
|
||||
|
||||
public final void setTacticalOverlay(final boolean[] canOwnSystem,
|
||||
final boolean[] willOwnSystem,
|
||||
final int[] collapseStages,
|
||||
final int[] possibleCollapseStages) {
|
||||
this.possibleSystemCollapseStages = possibleCollapseStages;
|
||||
this.systemCollapseStages = collapseStages;
|
||||
this.canOwnSystem = canOwnSystem;
|
||||
this.willOwnSystem = willOwnSystem;
|
||||
}
|
||||
|
||||
private void addBuildTannhauserEvent(final Player player, final StarSystem source, final StarSystem target) {
|
||||
final int phase = this.nextTannhauserPhase(source, target);
|
||||
this.buildEvents.add(new BuildEvent(target, player, GameState.ResourceType.EXOTICS, phase));
|
||||
|
@ -65,6 +65,7 @@ public final class ClientGameSession extends GameSession {
|
||||
private Player[] systemOwners;
|
||||
private ContiguousForce[] systemForces;
|
||||
private int[] remainingGarrisons;
|
||||
private TacticalAnalysis tacticalAnalysis;
|
||||
|
||||
public boolean desynced;
|
||||
public int turnNumberWhenJoined;
|
||||
@ -93,15 +94,6 @@ public final class ClientGameSession extends GameSession {
|
||||
private TickTimer recentlyPlayedBuildSfxQueue;
|
||||
private int recentlyPlayedBuildSfxCounter;
|
||||
|
||||
public boolean[] systemsWillOwn;
|
||||
private boolean[] systemsCanOwn;
|
||||
private int[] possibleCollapseStages;
|
||||
private int[] minGarrisonsAtTurnEnd;
|
||||
private int[] guaranteedCollapseStages;
|
||||
private int[] maxGarrisonsAtTurnEnd;
|
||||
private int[] safeGarrisonsToHold;
|
||||
private int[] minGarrisonsToHold;
|
||||
|
||||
public ClientGameSession(final boolean isMultiplayer,
|
||||
final boolean isTutorial,
|
||||
final int turnLengthIndex,
|
||||
@ -202,17 +194,10 @@ public final class ClientGameSession extends GameSession {
|
||||
}
|
||||
}
|
||||
|
||||
final int systemCount = this.gameState.map.systems.length;
|
||||
this.systemsCanOwn = new boolean[systemCount];
|
||||
this.systemsWillOwn = new boolean[systemCount];
|
||||
this.guaranteedCollapseStages = new int[systemCount];
|
||||
this.possibleCollapseStages = new int[systemCount];
|
||||
this.minGarrisonsAtTurnEnd = new int[systemCount];
|
||||
this.safeGarrisonsToHold = new int[systemCount];
|
||||
this.maxGarrisonsAtTurnEnd = new int[systemCount];
|
||||
this.minGarrisonsToHold = new int[systemCount];
|
||||
this.gameState.recalculatePlayerFleetProduction();
|
||||
this.gameView.setTacticalOverlay(this.systemsCanOwn, this.systemsWillOwn, this.guaranteedCollapseStages, this.possibleCollapseStages);
|
||||
|
||||
this.tacticalAnalysis = new TacticalAnalysis(this.gameState.map.systems.length);
|
||||
this.gameView.setTacticalAnalysis(this.tacticalAnalysis);
|
||||
|
||||
if (this.localPlayerIndex < 0) {
|
||||
isAutoPlaying = false;
|
||||
@ -300,115 +285,12 @@ public final class ClientGameSession extends GameSession {
|
||||
ShatteredPlansClient.saveProfile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the “hatching” overlay used to display which systems are
|
||||
* expected or in danger of being captured or lost.
|
||||
*/
|
||||
private void recalculateTacticalOverlay() {
|
||||
for (final StarSystem system : this.gameState.map.systems) {
|
||||
this.guaranteedCollapseStages[system.index] = 0;
|
||||
this.possibleCollapseStages[system.index] = 0;
|
||||
if (system.owner == this.localPlayer) {
|
||||
this.systemsCanOwn[system.index] = true;
|
||||
this.maxGarrisonsAtTurnEnd[system.index] = system.remainingGarrison;
|
||||
this.systemsWillOwn[system.index] = true;
|
||||
this.minGarrisonsAtTurnEnd[system.index] = system.remainingGarrison;
|
||||
} else {
|
||||
this.systemsCanOwn[system.index] = false;
|
||||
this.maxGarrisonsAtTurnEnd[system.index] = 0;
|
||||
this.systemsWillOwn[system.index] = false;
|
||||
this.minGarrisonsAtTurnEnd[system.index] = 0;
|
||||
}
|
||||
private void recalculateTacticalAnalysis() {
|
||||
this.tacticalAnalysis.analyze(this.gameState, this.localPlayer);
|
||||
}
|
||||
|
||||
for (final MoveFleetsOrder incomingOrder : system.incomingOrders) {
|
||||
if (incomingOrder.player == this.localPlayer) {
|
||||
this.maxGarrisonsAtTurnEnd[system.index] += incomingOrder.quantity;
|
||||
this.systemsCanOwn[system.index] = true;
|
||||
if (incomingOrder.target.owner == this.localPlayer || incomingOrder.target.garrison == 0) {
|
||||
this.minGarrisonsAtTurnEnd[system.index] += incomingOrder.quantity;
|
||||
this.systemsWillOwn[system.index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.localPlayer != null) {
|
||||
for (final StarSystem neighbor : system.neighbors) {
|
||||
if (neighbor.owner != null && neighbor.owner != this.localPlayer
|
||||
&& (system.owner != this.localPlayer || !neighbor.owner.allies[this.localPlayer.index])
|
||||
&& !this.isStellarBombTarget(this.localPlayer, neighbor)) {
|
||||
this.systemsWillOwn[system.index] = false;
|
||||
this.minGarrisonsAtTurnEnd[system.index] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.gameState.gameOptions.noChainCollapsing) {
|
||||
for (final StarSystem system : this.gameState.map.systems) {
|
||||
this.minGarrisonsToHold[system.index] = 0;
|
||||
this.safeGarrisonsToHold[system.index] = 0;
|
||||
}
|
||||
} else if (this.gameState.gameOptions.simpleGarrisoning) {
|
||||
for (final StarSystem system : this.gameState.map.systems) {
|
||||
this.minGarrisonsToHold[system.index] = 1;
|
||||
this.safeGarrisonsToHold[system.index] = 1;
|
||||
}
|
||||
} else {
|
||||
for (final StarSystem system : this.gameState.map.systems) {
|
||||
this.minGarrisonsToHold[system.index] = (int) Arrays.stream(system.neighbors).filter(neighbor -> !this.systemsCanOwn[neighbor.index]).count();
|
||||
this.safeGarrisonsToHold[system.index] = (int) Arrays.stream(system.neighbors).filter(neighbor -> !this.systemsWillOwn[neighbor.index]).count();
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.gameState.gameOptions.noChainCollapsing) {
|
||||
boolean goAgain = true;
|
||||
while (goAgain) {
|
||||
goAgain = false;
|
||||
|
||||
for (final StarSystem system : this.gameState.map.systems) {
|
||||
final int index = system.index;
|
||||
if (this.systemsCanOwn[index] && this.minGarrisonsToHold[index] > this.maxGarrisonsAtTurnEnd[index]) {
|
||||
goAgain = true;
|
||||
this.systemsCanOwn[index] = false;
|
||||
final int nextStage = this.guaranteedCollapseStages[index] + 1;
|
||||
|
||||
for (final StarSystem neighbor : system.neighbors) {
|
||||
if (this.gameState.gameOptions.simpleGarrisoning) {
|
||||
this.minGarrisonsToHold[neighbor.index] = 1;
|
||||
} else {
|
||||
this.minGarrisonsToHold[neighbor.index]++;
|
||||
}
|
||||
|
||||
if (this.guaranteedCollapseStages[neighbor.index] > nextStage || this.systemsCanOwn[neighbor.index]) {
|
||||
this.guaranteedCollapseStages[neighbor.index] = nextStage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.systemsWillOwn[index] && this.safeGarrisonsToHold[index] > this.minGarrisonsAtTurnEnd[index]) {
|
||||
goAgain = true;
|
||||
this.systemsWillOwn[index] = false;
|
||||
this.minGarrisonsAtTurnEnd[index] = 0;
|
||||
final int nextStage = this.possibleCollapseStages[index] + 1;
|
||||
|
||||
for (final StarSystem neighbor : system.neighbors) {
|
||||
if (this.gameState.gameOptions.simpleGarrisoning) {
|
||||
this.safeGarrisonsToHold[neighbor.index] = 1;
|
||||
} else {
|
||||
this.safeGarrisonsToHold[neighbor.index]++;
|
||||
}
|
||||
|
||||
if (nextStage < this.possibleCollapseStages[neighbor.index] || this.systemsWillOwn[neighbor.index]) {
|
||||
this.possibleCollapseStages[neighbor.index] = nextStage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.gameView.setTacticalOverlay(this.systemsCanOwn, this.systemsWillOwn, this.guaranteedCollapseStages, this.possibleCollapseStages);
|
||||
public boolean isSystemOwnershipGuaranteed(final StarSystem system) {
|
||||
return this.tacticalAnalysis.isOwnershipGuaranteed(system);
|
||||
}
|
||||
|
||||
private void readTurnOrdersAndUpdate(final CipheredBuffer packet, final int len) {
|
||||
@ -427,22 +309,18 @@ public final class ClientGameSession extends GameSession {
|
||||
order.source.remainingGarrison -= order.quantity;
|
||||
}
|
||||
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
}
|
||||
|
||||
// only used by the tutorial
|
||||
public void setMap(final Map map) {
|
||||
this.gameState.setMap(map);
|
||||
|
||||
this.tacticalAnalysis = new TacticalAnalysis(map.systems.length);
|
||||
this.recalculateTacticalAnalysis();
|
||||
|
||||
this.gameView.setMap(map);
|
||||
this.maxGarrisonsAtTurnEnd = new int[map.systems.length];
|
||||
this.minGarrisonsToHold = new int[map.systems.length];
|
||||
this.systemsCanOwn = new boolean[map.systems.length];
|
||||
this.guaranteedCollapseStages = new int[map.systems.length];
|
||||
this.minGarrisonsAtTurnEnd = new int[map.systems.length];
|
||||
this.safeGarrisonsToHold = new int[map.systems.length];
|
||||
this.systemsWillOwn = new boolean[map.systems.length];
|
||||
this.possibleCollapseStages = new int[map.systems.length];
|
||||
this.recalculateTacticalOverlay();
|
||||
this.gameView.setTacticalAnalysis(this.tacticalAnalysis);
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
@ -1412,7 +1290,7 @@ public final class ClientGameSession extends GameSession {
|
||||
this.gameView.stopCombatAnimations();
|
||||
this.recalculateSystemState();
|
||||
this.handleBuildProject(GameState.ResourceType.ENERGY, target);
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
this.ui.setPlacementMode(PlacementMode.NONE);
|
||||
this.ui.markProjectPending(GameState.ResourceType.ENERGY);
|
||||
}
|
||||
@ -1481,7 +1359,7 @@ public final class ClientGameSession extends GameSession {
|
||||
this.gameState.projectOrders.remove(order);
|
||||
this.ui.handleProjectOrderCanceled(order.type);
|
||||
if (order.type == GameState.ResourceType.ENERGY) {
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1534,7 +1412,7 @@ public final class ClientGameSession extends GameSession {
|
||||
this.cancelOrdersToAttackPlayer(offerer);
|
||||
}
|
||||
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
super.handlePactAccepted(offerer, offeree);
|
||||
}
|
||||
|
||||
@ -1590,15 +1468,6 @@ public final class ClientGameSession extends GameSession {
|
||||
|
||||
for (final StarSystem system : this.gameState.map.systems) {
|
||||
system.remainingGarrison = system.garrison;
|
||||
if (system.owner == this.localPlayer) {
|
||||
this.maxGarrisonsAtTurnEnd[system.index] = system.garrison;
|
||||
this.minGarrisonsAtTurnEnd[system.index] = system.garrison;
|
||||
} else {
|
||||
this.systemsCanOwn[system.index] = false;
|
||||
this.systemsWillOwn[system.index] = false;
|
||||
this.maxGarrisonsAtTurnEnd[system.index] = 0;
|
||||
this.minGarrisonsAtTurnEnd[system.index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.systemOwners == null || this.systemOwners.length < this.gameState.map.systems.length) {
|
||||
@ -1615,13 +1484,12 @@ public final class ClientGameSession extends GameSession {
|
||||
}
|
||||
|
||||
this.gameView.assignSystemState(this.remainingGarrisons, this.systemForces, this.systemOwners, false);
|
||||
this.gameView.setTacticalOverlay(this.systemsCanOwn, this.systemsWillOwn, this.guaranteedCollapseStages, this.possibleCollapseStages);
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
if (this.isMultiplayer || this.ais[this.localPlayerIndex] == null) {
|
||||
if (isAutoPlaying && !this.desynced && this.localPlayerIsAlive) {
|
||||
this.ais[this.localPlayerIndex].makeDesiredPactOffers();
|
||||
this.ais[this.localPlayerIndex].planTurnOrders();
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
|
||||
this.unsentMoveOrders.addAll(this.gameState.moveOrders);
|
||||
this.unsentBuildOrders.addAll(this.gameState.buildOrders);
|
||||
@ -1650,16 +1518,11 @@ public final class ClientGameSession extends GameSession {
|
||||
JagexApplet.clientError(var6, "AI has errored in single player game");
|
||||
}
|
||||
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
this.advanceTurnSinglePlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStellarBombTarget(final Player player, final StarSystem system) {
|
||||
return this.gameState.projectOrders.stream().anyMatch(order ->
|
||||
order.player == player && order.type == GameState.ResourceType.ENERGY && order.target == system);
|
||||
}
|
||||
|
||||
private void resendAllTurnOrders() {
|
||||
C2SPacket.Type.ALL_TURN_ORDERS.write(C2SPacket.buffer);
|
||||
C2SPacket.buffer.withLengthShort(() -> {
|
||||
@ -1681,7 +1544,7 @@ public final class ClientGameSession extends GameSession {
|
||||
orders.projectOrders.forEach(this::addOrder);
|
||||
orders.buildOrders.forEach(this::addOrder);
|
||||
orders.moveOrders.forEach(this::addOrder);
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
this.ui.updateAvailableFleetCounters();
|
||||
}
|
||||
|
||||
@ -1707,8 +1570,6 @@ public final class ClientGameSession extends GameSession {
|
||||
|
||||
order.system.remainingGarrison += order.quantity;
|
||||
this.remainingGarrisons[order.system.index] += order.quantity;
|
||||
this.maxGarrisonsAtTurnEnd[order.system.index] += order.quantity;
|
||||
this.minGarrisonsAtTurnEnd[order.system.index] += order.quantity;
|
||||
force.fleetsAvailableToBuild -= order.quantity;
|
||||
}
|
||||
|
||||
@ -1762,7 +1623,7 @@ public final class ClientGameSession extends GameSession {
|
||||
this.gameState.moveOrders.remove(order);
|
||||
}
|
||||
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
if (this.isMultiplayer) {
|
||||
this.unsentMoveOrders.remove(order);
|
||||
this.unsentMoveOrders.add(order);
|
||||
@ -1819,12 +1680,12 @@ public final class ClientGameSession extends GameSession {
|
||||
this.gameView.highlightedSystems[system.index] = SystemHighlight.GRAY;
|
||||
}
|
||||
}
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
}
|
||||
|
||||
private void handleMoveFleets(final StarSystem source, final StarSystem target, final int quantity) {
|
||||
this.addOrder(new MoveFleetsOrder(source, target, quantity));
|
||||
this.recalculateTacticalOverlay();
|
||||
this.recalculateTacticalAnalysis();
|
||||
}
|
||||
|
||||
private void handleBuildProject(@MagicConstant(valuesFromClass = GameState.ResourceType.class) final int type, final StarSystem target) {
|
||||
|
@ -74,6 +74,7 @@ public final class GameView extends AbstractGameView {
|
||||
public boolean _Gb;
|
||||
public boolean _Ab;
|
||||
public MoveFleetsOrder _rb;
|
||||
protected TacticalAnalysis tacticalAnalysis;
|
||||
private Sprite[] _Mb;
|
||||
private ArgbSprite _K;
|
||||
private double _wb;
|
||||
@ -3055,9 +3056,9 @@ public final class GameView extends AbstractGameView {
|
||||
final boolean isAttacked = system.incomingOrders.stream()
|
||||
.anyMatch(order -> order.player == this.localPlayer);
|
||||
if (isAttacked && this.localPlayer != null) {
|
||||
if (!this.canOwnSystem[system.index]) {
|
||||
if (!this.tacticalAnalysis.isOwnershipPossible(system)) {
|
||||
drawSystemHatching(this.systemHexes[system.index], Drawing.RED, 96);
|
||||
} else if (this.willOwnSystem[system.index]) {
|
||||
} else if (this.tacticalAnalysis.isOwnershipGuaranteed(system)) {
|
||||
drawSystemHatching(this.systemHexes[system.index], Drawing.GREEN, 64);
|
||||
} else {
|
||||
drawSystemHatching(this.systemHexes[system.index], Drawing.YELLOW, 64);
|
||||
@ -3079,10 +3080,10 @@ public final class GameView extends AbstractGameView {
|
||||
}
|
||||
|
||||
if (owner == this.localPlayer && this.localPlayer != null) {
|
||||
if (!this.canOwnSystem[system.index]) {
|
||||
drawSystemHatching(var6, Drawing.RED, this.systemCollapseStages[system.index] == 0 ? 192 : 96);
|
||||
} else if (!this.willOwnSystem[system.index]) {
|
||||
drawSystemHatching(var6, Drawing.YELLOW, this.possibleSystemCollapseStages[system.index] == 0 ? 128 : 64);
|
||||
if (!this.tacticalAnalysis.isOwnershipPossible(system)) {
|
||||
drawSystemHatching(var6, Drawing.RED, this.tacticalAnalysis.getEarliestGuaranteedCollapseWave(system) == 0 ? 192 : 96);
|
||||
} else if (!this.tacticalAnalysis.isOwnershipGuaranteed(system)) {
|
||||
drawSystemHatching(var6, Drawing.YELLOW, this.tacticalAnalysis.getEarliestPossibleCollapseWave(system) == 0 ? 128 : 64);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3701,4 +3702,8 @@ public final class GameView extends AbstractGameView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void setTacticalAnalysis(final TacticalAnalysis tacticalAnalysis) {
|
||||
this.tacticalAnalysis = tacticalAnalysis;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,218 @@
|
||||
package funorb.shatteredplans.client.game;
|
||||
|
||||
import funorb.shatteredplans.game.GameState;
|
||||
import funorb.shatteredplans.game.MoveFleetsOrder;
|
||||
import funorb.shatteredplans.game.Player;
|
||||
import funorb.shatteredplans.map.StarSystem;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class performs some simple analyses of the current map from the
|
||||
* perspective of the local player. The resulting information is used to drive
|
||||
* the “tactical overlay” in the user interface, which renders the information
|
||||
* using cross-hatching of various colors.
|
||||
*/
|
||||
public final class TacticalAnalysis {
|
||||
/**
|
||||
* {@code true} for systems owned by the local player that could be attacked
|
||||
* by a hostile force on this turn (and therefore could be lost regardless of
|
||||
* its garrison at the start of combat on this turn).
|
||||
*/
|
||||
private final boolean[] systemThreatened;
|
||||
|
||||
/**
|
||||
* {@code true} for systems that the local player is <i>guaranteed</i> to own
|
||||
* after this turn. Implies {@link #systemCanOwn}.
|
||||
*/
|
||||
private final boolean[] systemWillOwn;
|
||||
|
||||
/**
|
||||
* {@code true} for systems that the local play <i>may</i> own after this
|
||||
* turn. Implied by {@link #systemWillOwn}.
|
||||
*/
|
||||
private final boolean[] systemCanOwn;
|
||||
|
||||
/**
|
||||
* The minimum number of fleets (owned by the local player) that could be
|
||||
* garrisoned in each system after this turn, assuming the worst possible
|
||||
* combat outcomes.
|
||||
*/
|
||||
private final int[] minGarrisonAtTurnEnd;
|
||||
|
||||
/**
|
||||
* The maximum number of fleets (owned by the local player) that could be
|
||||
* garrisoned in each system after this turn, assuming the best possible
|
||||
* combat outcomes.
|
||||
*/
|
||||
private final int[] maxGarrisonAtTurnEnd;
|
||||
|
||||
/**
|
||||
* The absolute minimum number of fleets needed to be garrisoned in each
|
||||
* system at the end of this turn to have any chance of holding it, assuming
|
||||
* the best possible combat outcomes in neighboring systems.
|
||||
*/
|
||||
private final int[] minGarrisonToHold;
|
||||
|
||||
/**
|
||||
* The minimum number of fleets needed to be garrisoned in each system at the
|
||||
* end of this turn to <i>guarantee</i> it will be held, assuming the worst
|
||||
* possible combat outcomes in neighboring systems.
|
||||
*/
|
||||
private final int[] safeGarrisonToHold;
|
||||
|
||||
/**
|
||||
* Tracks the index of the earliest “wave” in which each system <i>might</i>
|
||||
* collapse. For systems that cannot be lost (see {@link #systemWillOwn}),
|
||||
* the value is arbitrary.
|
||||
*/
|
||||
private final int[] possibleCollapseWave;
|
||||
|
||||
/**
|
||||
* Tracks the index of the earliest “wave” in which each system is
|
||||
* <i>guaranteed</i> to collapse. For systems that might not be lost (see
|
||||
* {@link #systemCanOwn}), the value is arbitrary.
|
||||
*/
|
||||
private final int[] guaranteedCollapseWave;
|
||||
|
||||
public TacticalAnalysis(final int systemCount) {
|
||||
this.systemThreatened = new boolean[systemCount];
|
||||
this.systemCanOwn = new boolean[systemCount];
|
||||
this.systemWillOwn = new boolean[systemCount];
|
||||
this.guaranteedCollapseWave = new int[systemCount];
|
||||
this.possibleCollapseWave = new int[systemCount];
|
||||
this.minGarrisonAtTurnEnd = new int[systemCount];
|
||||
this.safeGarrisonToHold = new int[systemCount];
|
||||
this.maxGarrisonAtTurnEnd = new int[systemCount];
|
||||
this.minGarrisonToHold = new int[systemCount];
|
||||
}
|
||||
|
||||
public boolean isThreatened(final @NotNull StarSystem system) {
|
||||
return this.systemThreatened[system.index];
|
||||
}
|
||||
|
||||
public boolean isOwnershipGuaranteed(final @NotNull StarSystem system) {
|
||||
return this.systemWillOwn[system.index];
|
||||
}
|
||||
|
||||
public boolean isOwnershipPossible(final @NotNull StarSystem system) {
|
||||
return this.systemCanOwn[system.index];
|
||||
}
|
||||
|
||||
public int getEarliestPossibleCollapseWave(final @NotNull StarSystem system) {
|
||||
return this.possibleCollapseWave[system.index];
|
||||
}
|
||||
|
||||
public int getEarliestGuaranteedCollapseWave(final @NotNull StarSystem system) {
|
||||
return this.guaranteedCollapseWave[system.index];
|
||||
}
|
||||
|
||||
public void analyze(final @NotNull GameState gameState, final @Nullable Player localPlayer) {
|
||||
for (final StarSystem system : gameState.map.systems) {
|
||||
this.systemThreatened[system.index] = false;
|
||||
this.guaranteedCollapseWave[system.index] = 0;
|
||||
this.possibleCollapseWave[system.index] = 0;
|
||||
if (system.owner == localPlayer) {
|
||||
this.systemCanOwn[system.index] = true;
|
||||
this.maxGarrisonAtTurnEnd[system.index] = system.remainingGarrison;
|
||||
this.systemWillOwn[system.index] = true;
|
||||
this.minGarrisonAtTurnEnd[system.index] = system.remainingGarrison;
|
||||
} else {
|
||||
this.systemCanOwn[system.index] = false;
|
||||
this.maxGarrisonAtTurnEnd[system.index] = 0;
|
||||
this.systemWillOwn[system.index] = false;
|
||||
this.minGarrisonAtTurnEnd[system.index] = 0;
|
||||
}
|
||||
|
||||
for (final MoveFleetsOrder incomingOrder : system.incomingOrders) {
|
||||
if (incomingOrder.player == localPlayer) {
|
||||
this.maxGarrisonAtTurnEnd[system.index] += incomingOrder.quantity;
|
||||
this.systemCanOwn[system.index] = true;
|
||||
if (incomingOrder.target.owner == localPlayer || incomingOrder.target.garrison == 0) {
|
||||
this.minGarrisonAtTurnEnd[system.index] += incomingOrder.quantity;
|
||||
this.systemWillOwn[system.index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (localPlayer != null) {
|
||||
for (final StarSystem neighbor : system.neighbors) {
|
||||
if (neighbor.owner != null && neighbor.owner != localPlayer
|
||||
&& (system.owner != localPlayer || !neighbor.owner.allies[localPlayer.index])
|
||||
&& !gameState.isStellarBombTarget(localPlayer, neighbor)) {
|
||||
this.systemThreatened[system.index] = true;
|
||||
this.systemWillOwn[system.index] = false;
|
||||
this.minGarrisonAtTurnEnd[system.index] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gameState.gameOptions.noChainCollapsing) {
|
||||
for (final StarSystem system : gameState.map.systems) {
|
||||
this.minGarrisonToHold[system.index] = 0;
|
||||
this.safeGarrisonToHold[system.index] = 0;
|
||||
}
|
||||
} else if (gameState.gameOptions.simpleGarrisoning) {
|
||||
for (final StarSystem system : gameState.map.systems) {
|
||||
this.minGarrisonToHold[system.index] = 1;
|
||||
this.safeGarrisonToHold[system.index] = 1;
|
||||
}
|
||||
} else {
|
||||
for (final StarSystem system : gameState.map.systems) {
|
||||
this.minGarrisonToHold[system.index] = (int) Arrays.stream(system.neighbors).filter(neighbor -> !this.systemCanOwn[neighbor.index]).count();
|
||||
this.safeGarrisonToHold[system.index] = (int) Arrays.stream(system.neighbors).filter(neighbor -> !this.systemWillOwn[neighbor.index]).count();
|
||||
}
|
||||
}
|
||||
|
||||
if (!gameState.gameOptions.noChainCollapsing) {
|
||||
boolean goAgain = true;
|
||||
while (goAgain) {
|
||||
goAgain = false;
|
||||
|
||||
for (final StarSystem system : gameState.map.systems) {
|
||||
final int index = system.index;
|
||||
if (this.systemCanOwn[index] && this.minGarrisonToHold[index] > this.maxGarrisonAtTurnEnd[index]) {
|
||||
goAgain = true;
|
||||
this.systemCanOwn[index] = false;
|
||||
final int nextStage = this.guaranteedCollapseWave[index] + 1;
|
||||
|
||||
for (final StarSystem neighbor : system.neighbors) {
|
||||
if (gameState.gameOptions.simpleGarrisoning) {
|
||||
this.minGarrisonToHold[neighbor.index] = 1;
|
||||
} else {
|
||||
this.minGarrisonToHold[neighbor.index]++;
|
||||
}
|
||||
|
||||
if (this.guaranteedCollapseWave[neighbor.index] > nextStage || this.systemCanOwn[neighbor.index]) {
|
||||
this.guaranteedCollapseWave[neighbor.index] = nextStage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.systemWillOwn[index] && this.safeGarrisonToHold[index] > this.minGarrisonAtTurnEnd[index]) {
|
||||
goAgain = true;
|
||||
this.systemWillOwn[index] = false;
|
||||
this.minGarrisonAtTurnEnd[index] = 0;
|
||||
final int nextStage = this.possibleCollapseWave[index] + 1;
|
||||
|
||||
for (final StarSystem neighbor : system.neighbors) {
|
||||
if (gameState.gameOptions.simpleGarrisoning) {
|
||||
this.safeGarrisonToHold[neighbor.index] = 1;
|
||||
} else {
|
||||
this.safeGarrisonToHold[neighbor.index]++;
|
||||
}
|
||||
|
||||
if (nextStage < this.possibleCollapseWave[neighbor.index] || this.systemWillOwn[neighbor.index]) {
|
||||
this.possibleCollapseWave[neighbor.index] = nextStage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -262,7 +262,7 @@ public final class TutorialState {
|
||||
|
||||
for (var3 = 0; var2.length > var3; ++var3) {
|
||||
var4 = var2[var3];
|
||||
if (var4.owner == localPlayer && !session.systemsWillOwn[var4.index]) {
|
||||
if (var4.owner == localPlayer && !session.isSystemOwnershipGuaranteed(var4)) {
|
||||
session.gameView.a021(92, var4, 300.0F);
|
||||
return;
|
||||
}
|
||||
|
@ -1333,6 +1333,11 @@ public final class GameState {
|
||||
return kills;
|
||||
}
|
||||
|
||||
public boolean isStellarBombTarget(final Player player, final StarSystem system) {
|
||||
return this.projectOrders.stream().anyMatch(order ->
|
||||
order.player == player && order.type == ResourceType.ENERGY && order.target == system);
|
||||
}
|
||||
|
||||
public void recalculateFleetProduction() {
|
||||
for (final Player player : this.players) {
|
||||
this.recalculateFleetProduction(player);
|
||||
|
Loading…
Reference in New Issue
Block a user