Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Game Systems #5445

Merged
merged 9 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1444,7 +1444,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 @@ -751,7 +751,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 Expand Up @@ -9483,7 +9482,7 @@
*
* @return an int
*/
public int getDeployRound() {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
Deployable.getDeployRound
; it is advisable to add an Override annotation.
return deployRound;
}

Expand All @@ -9500,7 +9499,7 @@
/**
* Checks to see if an entity has been deployed
*/
public boolean isDeployed() {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
Deployable.isDeployed
; it is advisable to add an Override annotation.
return deployed;
}

Expand Down
Loading
Loading