Skip to content

Commit

Permalink
Merge pull request #5445 from SJuliez/SBF-step
Browse files Browse the repository at this point in the history
Game Systems
  • Loading branch information
HammerGS authored May 16, 2024
2 parents 5b4d3ae + 0a53aba commit 2d8b33f
Show file tree
Hide file tree
Showing 14 changed files with 1,055 additions and 215 deletions.
2 changes: 1 addition & 1 deletion megamek/src/megamek/client/ui/swing/FiringDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,7 @@ protected void removeTempAttacks() {
*/
protected void removeLastFiring() {
if (!attacks.isEmpty()) {
Object o = attacks.lastElement();
EntityAction o = attacks.lastElement();
if (o instanceof WeaponAttackAction) {
WeaponAttackAction waa = (WeaponAttackAction) o;
ce().getEquipment(waa.getWeaponId()).setUsedThisRound(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ private void removeTempAttacks() {
*/
private void removeLastFiring() {
if (!attacks.isEmpty()) {
Object o = attacks.lastElement();
EntityAction o = attacks.lastElement();
if (o instanceof WeaponAttackAction) {
WeaponAttackAction waa = (WeaponAttackAction) o;
ce().getEquipment(waa.getWeaponId()).setUsedThisRound(false);
Expand Down
129 changes: 120 additions & 9 deletions megamek/src/megamek/common/AbstractGame.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,38 @@
*/
package megamek.common;

import megamek.common.actions.EntityAction;
import megamek.common.event.GameBoardNewEvent;
import megamek.common.event.GameEvent;
import megamek.common.event.GameListener;
import megamek.common.event.GameNewActionEvent;
import megamek.common.force.Forces;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
* This is a base class to derive all types of Game (TW, AS, BF, SBF...) from. Any such game will have players, units
* (InGameObjects) and Forces (even if empty); the base class manages these.
*/
public abstract class AbstractGame implements IGame {

/**
* The players present in the game mapped to their id as key.
*/
/** The players present in the game mapped to their id as key */
protected final ConcurrentHashMap<Integer, Player> players = new ConcurrentHashMap<>();

/**
* The InGameObjects (units such as Entity and others) present in the game mapped to their id as key.
*/
/** The InGameObjects (units such as Entity and others) present in the game mapped to their id as key */
protected final ConcurrentHashMap<Integer, InGameObject> inGameObjects = new ConcurrentHashMap<>();

/**
* The teams present in the game.
*/
/** The teams present in the game */
protected final CopyOnWriteArrayList<Team> teams = new CopyOnWriteArrayList<>();

protected transient Vector<GameListener> gameListeners = new Vector<>();

/** The currently pending actions such as attacks */
protected final List<EntityAction> pendingActions = new ArrayList<>();

/**
* This Map holds all game boards together with a unique ID for each.
* For the "legacy" Game that currently only allows a single board, that board always uses ID 0.
Expand All @@ -64,6 +64,13 @@ public abstract class AbstractGame implements IGame {
*/
protected Forces forces = new Forces(this);

/**
* This map links deployment rounds to lists of Deployables that deploy in respective rounds. It only contains
* units/objects that are not yet deployed or will redeploy (returning Aeros, units going from one board to
* another if implemented). For those, the list is updated every round.
*/
private final Map<Integer, List<Deployable>> deploymentTable = new HashMap<>();

protected int currentRound = 0;

@Override
Expand Down Expand Up @@ -199,4 +206,108 @@ public int getCurrentRound() {
public void setCurrentRound(int currentRound) {
this.currentRound = currentRound;
}

/**
* Returns true when the current game phase should be played, meaning it is played in the current type
* of game and there are possible actions in it in the present game state.
* The result may be different in other rounds.
*
* @return True when the current phase should be skipped entirely in this round
* @see #shouldSkipCurrentPhase()
*/
public abstract boolean isCurrentPhasePlayable();

/**
* Returns true when the current game phase should be skipped, either because it is not played at
* all in the current type of game or because the present game state dictates that there can be no
* actions in it. The result may be different in other rounds. This is the opposite of
* {@link #isCurrentPhasePlayable()}.
*
* @return True when the current phase should be skipped entirely in this round
* @see #isCurrentPhasePlayable()
*/
public boolean shouldSkipCurrentPhase() {
return !isCurrentPhasePlayable();
}

/**
* Empties the list of pending EntityActions completely.
* @see #getActionsVector()
*/
public void clearActions() {
pendingActions.clear();
}

/**
* Removes all pending EntityActions by the InGameObject (Entity, unit) of the given ID from the list
* of pending actions.
*/
public void removeActionsFor(int id) {
pendingActions.removeIf(action -> action.getEntityId() == id);
}

/**
* Remove the given EntityAction from the list of pending actions.
*/
public void removeAction(EntityAction action) {
pendingActions.remove(action);
}

/**
* Returns the pending EntityActions. Do not use to modify the actions; Arlith said: I will be
* angry. &gt;:[
*/
public List<EntityAction> getActionsVector() {
return Collections.unmodifiableList(pendingActions);
}

/**
* Adds the specified action to the list of pending EntityActions for this phase and fires a GameNewActionEvent.
*/
public void addAction(EntityAction action) {
pendingActions.add(action);
fireGameEvent(new GameNewActionEvent(this, action));
}

public void setupRoundDeployment() {
deploymentTable.clear();
for (Deployable unit : deployableInGameObjects()) {
if (!unit.isDeployed()) {
deploymentTable.computeIfAbsent(unit.getDeployRound(), k -> new ArrayList<>()).add(unit);
}
}
}

protected List<Deployable> deployableInGameObjects() {
return inGameObjects.values().stream()
.filter(unit -> unit instanceof Deployable)
.map(unit -> (Deployable) unit)
.collect(Collectors.toList());
}

public int lastDeploymentRound() {
return deploymentTable.isEmpty() ? -1 : Collections.max(deploymentTable.keySet());
}

public boolean isDeploymentComplete() {
return lastDeploymentRound() < currentRound;
}

/**
* Check to see if we should deploy this round
*/
public boolean shouldDeployThisRound() {
return shouldDeployForRound(currentRound);
}

public boolean shouldDeployForRound(int round) {
return deploymentTable.containsKey(round);
}

/**
* Clear this round from this list of entities to deploy
*/
public void clearDeploymentThisRound() {
deploymentTable.remove(currentRound);
}
}
40 changes: 40 additions & 0 deletions megamek/src/megamek/common/Deployable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MegaMek.
*
* MegaMek is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MegaMek is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MegaMek. If not, see <http://www.gnu.org/licenses/>.
*/
package megamek.common;

/**
* This interface is implemented by those units (by InGameObjects) that can be deployed either
* offboard or on a board. There are InGameObjects that are only targets (HexTarget) and may thus not
* actually be deployable. All Deployable objects could theoretically be listed in the lobby's unit list.
*/
public interface Deployable {

/**
* Returns true when this unit/object is deployed, i.e. it has arrived in the game and may
* perform actions or be targeted by actions. Usually that means it has a fixed position on a board.
* Offboard units also count as undeployed as long as they cannot perform actions and as deployed when they
* can.
*/
boolean isDeployed();

/**
* Returns the round that this unit/object is to be deployed on the board or offboard.
*/
int getDeployRound();
}
3 changes: 1 addition & 2 deletions megamek/src/megamek/common/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import megamek.common.force.Force;
import megamek.common.icons.Camouflage;
import megamek.common.jacksonadapters.EntityDeserializer;
import megamek.common.jacksonadapters.SBFUnitDeserializer;
import megamek.common.options.*;
import megamek.common.planetaryconditions.Atmosphere;
import megamek.common.planetaryconditions.PlanetaryConditions;
Expand Down Expand Up @@ -61,7 +60,7 @@
*/
@JsonDeserialize(using = EntityDeserializer.class)
public abstract class Entity extends TurnOrdered implements Transporter, Targetable, RoundUpdated,
PhaseUpdated, ITechnology, ForceAssignable, CombatRole {
PhaseUpdated, ITechnology, ForceAssignable, CombatRole, Deployable {
private static final long serialVersionUID = 1430806396279853295L;

public static final int DOES_NOT_TRACK_HEAT = 999;
Expand Down
Loading

0 comments on commit 2d8b33f

Please sign in to comment.