diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties
index 9a906e0d485..1295c966fbb 100644
--- a/megamek/i18n/megamek/client/messages.properties
+++ b/megamek/i18n/megamek/client/messages.properties
@@ -285,6 +285,7 @@ BoardSelectionDialog.UpdateSize=Update Size
BoardSelectionDialog.Updating=Updating...
BoardSelectionDialog.ViewGameBoard=Preview Game Map... *
BoardSelectionDialog.ViewGameBoardTooltip=Shows a preview of the game's map.
* Note: For generated maps and surprise maps the preview is only a sample
and will not be the same as the actual map used.
+BoardSelectionDialog.ViewGameBoardButton=Refresh Preview
#Game board tooltips
BoardView1.ACTIVATING=ACTIVATING
@@ -772,6 +773,8 @@ ChatLounge.notDone=Not Done
ChatLounge.PartialRepairs=Partial Repairs
ChatLounge.Player=Player
ChatLounge.Players=Players
+ChatLounge.Player0=Player0
+ChatLounge.Blind=Blind
ChatLounge.quickView=Unit Quick View
ChatLounge.OverlapDeploy.title=Must choose exclusive deployment zone
ChatLounge.OverlapDeploy.msg=When using double blind, each player needs to have an exclusive deployment zone.
@@ -1430,6 +1433,12 @@ CustomMechDialog.labDeploymentOffset=Deployment Zone Offset:
CustomMechDialog.labDeploymentOffsetTip=Deployment Zone Offset, in hexes from corresponding map edge
CustomMechDialog.labDeploymentWidth=Deployment Zone Width:
CustomMechDialog.labDeploymentWidthTip=Deployment Zone width, in hexes
+CustomMechDialog.labDeploymentAnyNW=Deployment Any NW corner:
+CustomMechDialog.labDeploymentAnySE=Deployment Any SE corner:
+CustomMechDialog.BtnDeploymentUseRuler=Use ruler coords
+CustomMechDialog.BtnDeploymentUseRulerTip=Set the Any NW and SE corners based on the corners of the NW and SE corners the square defined by the map preview ruler
+CustomMechDialog.BtnDeploymentApply=Apply
+CustomMechDialog.BtnDeploymentApplyTip=Apply player setting
CustomMechDialog.labDeployShutdown=Shutdown
CustomMechDialog.labDeployProne=Prone
CustomMechDialog.labDeployHullDown=Hull Down
diff --git a/megamek/i18n/megamek/common/options/messages.properties b/megamek/i18n/megamek/common/options/messages.properties
index 34c9c5d8874..894be944f70 100644
--- a/megamek/i18n/megamek/common/options/messages.properties
+++ b/megamek/i18n/megamek/common/options/messages.properties
@@ -33,6 +33,10 @@ GameOptionsInfo.option.dumping_from_round.displayableName=first round for ammo d
GameOptionsInfo.option.dumping_from_round.description=Number of the round that has to be at least reached before allowing ammo dumps.
GameOptionsInfo.option.set_arty_player_homeedge.displayableName=Automatically set artillery home edge
GameOptionsInfo.option.set_arty_player_homeedge.description=If checked, all of the players' artillery units will have their home edge set to the deployment edge of the player, NW and NE are North, SW and SE are South. \nUnchecked by default.
+GameOptionsInfo.option.set_default_team_1.displayableName=Default non-bot players to team 1, useful in coop games
+GameOptionsInfo.option.set_default_team_1.description=When this option is unchecked each player is assigned new team
+GameOptionsInfo.option.set_player_deployment_to_player0.displayableName=Non-bot player entities with \"Use Owners*\" deployment set, use Player 0\'s settings, instead of current player\'s settings
+GameOptionsInfo.option.set_player_deployment_to_player0.description=When this option is unchecked use the standard player deployments settings
GameOptionsInfo.option.restrict_game_commands.displayableName=Restrict sensitive commands to non-Observers
GameOptionsInfo.option.restrict_game_commands.description=If checked, commands such as /reset and /kick cannot be used by Observers while others are playing. \nUnchecked by default.
GameOptionsInfo.option.disable_local_save.displayableName=Disable local saves when using double blind
diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java
index 6b3615e82b1..f51744f904c 100644
--- a/megamek/src/megamek/client/ui/swing/ClientGUI.java
+++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java
@@ -585,7 +585,7 @@ public void windowClosing(WindowEvent e) {
Ruler.color1 = GUIP.getRulerColor1();
Ruler.color2 = GUIP.getRulerColor2();
- ruler = new Ruler(frame, client, bv);
+ ruler = new Ruler(frame, client, bv, client.getGame());
ruler.setLocation(GUIP.getRulerPosX(), GUIP.getRulerPosY());
ruler.setSize(GUIP.getRulerSizeHeight(), GUIP.getRulerSizeWidth());
UIUtil.updateWindowBounds(ruler);
@@ -680,7 +680,7 @@ private void showOptions() {
}
public void customizePlayer() {
- PlayerSettingsDialog psd = new PlayerSettingsDialog(this, client);
+ PlayerSettingsDialog psd = new PlayerSettingsDialog(this, client, bv);
psd.setVisible(true);
}
diff --git a/megamek/src/megamek/client/ui/swing/CustomMechDialog.java b/megamek/src/megamek/client/ui/swing/CustomMechDialog.java
index 030b086fbfc..0cb6df18afd 100644
--- a/megamek/src/megamek/client/ui/swing/CustomMechDialog.java
+++ b/megamek/src/megamek/client/ui/swing/CustomMechDialog.java
@@ -85,6 +85,11 @@ public class CustomMechDialog extends AbstractButtonDialog implements ActionList
private final JFormattedTextField txtDeploymentOffset = new JFormattedTextField(formatterFactory);
private final JFormattedTextField txtDeploymentWidth = new JFormattedTextField(formatterFactory);
+ private JSpinner spinStartingAnyNWx;
+ private JSpinner spinStartingAnyNWy;
+ private JSpinner spinStartingAnySEx;
+ private JSpinner spinStartingAnySEy;
+
private final JLabel labDeployShutdown = new JLabel(
Messages.getString("CustomMechDialog.labDeployShutdown"), SwingConstants.RIGHT);
private final JCheckBox chDeployShutdown = new JCheckBox();
@@ -473,6 +478,17 @@ private void refreshDeployment() {
txtDeploymentOffset.setText(Integer.toString(entity.getStartingOffset(false)));
txtDeploymentWidth.setText(Integer.toString(entity.getStartingWidth(false)));
+ int bh = clientgui.getClient().getMapSettings().getBoardHeight();
+ int bw = clientgui.getClient().getMapSettings().getBoardWidth();
+ int x = Math.min(entity.getStartingAnyNWx(false) + 1, bw);
+ spinStartingAnyNWx.setValue(x);
+ int y = Math.min(entity.getStartingAnyNWy(false) + 1, bh);
+ spinStartingAnyNWy.setValue(y);
+ x = Math.min(entity.getStartingAnySEx(false) + 1, bw);
+ spinStartingAnySEy.setValue(x);
+ y = Math.min(entity.getStartingAnySEy(false) + 1, bh);
+ spinStartingAnySEy.setValue(y);
+
boolean enableDeploymentZoneControls = choDeploymentZone.isEnabled() && (choDeploymentZone.getSelectedIndex() > 0);
txtDeploymentOffset.setEnabled(enableDeploymentZoneControls);
txtDeploymentWidth.setEnabled(enableDeploymentZoneControls);
@@ -881,6 +897,15 @@ protected void okAction() {
entity.setStartingOffset(Integer.parseInt(txtDeploymentOffset.getText()));
entity.setStartingWidth(Integer.parseInt(txtDeploymentWidth.getText()));
+ int x = Math.min((Integer) spinStartingAnyNWx.getValue(), (Integer) spinStartingAnySEx.getValue());
+ int y = Math.min((Integer) spinStartingAnyNWy.getValue(), (Integer) spinStartingAnySEy.getValue());
+ entity.setStartingAnyNWx(x - 1);
+ entity.setStartingAnyNWy(y - 1);
+ x = Math.max((Integer) spinStartingAnyNWx.getValue(), (Integer) spinStartingAnySEx.getValue());
+ y = Math.max((Integer) spinStartingAnyNWy.getValue(), (Integer) spinStartingAnySEy.getValue());
+ entity.setStartingAnySEx(x - 1);
+ entity.setStartingAnySEy(y - 1);
+
// Should the entity begin the game shutdown?
if (chDeployShutdown.isSelected() && gameOptions().booleanOption(OptionsConstants.RPG_BEGIN_SHUTDOWN)) {
entity.performManualShutdown();
@@ -1200,6 +1225,32 @@ protected Container createCenterPane() {
panDeploy.add(labDeploymentWidth, GBC.std());
panDeploy.add(txtDeploymentWidth, GBC.eol());
+ int bh = clientgui.getClient().getMapSettings().getBoardHeight();
+ int bw = clientgui.getClient().getMapSettings().getBoardWidth();
+
+ panDeploy.add(new JLabel(Messages.getString("CustomMechDialog.labDeploymentAnyNW")), GBC.std());
+ int x = Math.min(entity.getStartingAnyNWx(false) + 1, bw);
+ SpinnerNumberModel mStartingAnyNWx = new SpinnerNumberModel(x, 0,bw, 1);
+ spinStartingAnyNWx = new JSpinner(mStartingAnyNWx);
+ spinStartingAnyNWx.setValue(x);
+ panDeploy.add(spinStartingAnyNWx, GBC.std());
+ int y = Math.min(entity.getStartingAnyNWy(false) + 1, bh);
+ SpinnerNumberModel mStartingAnyNWy = new SpinnerNumberModel(y, 0, bh, 1);
+ spinStartingAnyNWy = new JSpinner(mStartingAnyNWy);
+ spinStartingAnyNWy.setValue(y);
+ panDeploy.add(spinStartingAnyNWy, GBC.eol());
+ panDeploy.add(new JLabel(Messages.getString("CustomMechDialog.labDeploymentAnySE")), GBC.std());
+ x = Math.min(entity.getStartingAnySEx(false) + 1, bw);
+ SpinnerNumberModel mStartingAnySEx = new SpinnerNumberModel(x, 0, bw, 1);
+ spinStartingAnySEx = new JSpinner(mStartingAnySEx);
+ spinStartingAnySEx.setValue(x);
+ panDeploy.add(spinStartingAnySEx, GBC.std());
+ y = Math.min(entity.getStartingAnySEy(false) + 1, bh);
+ SpinnerNumberModel mStartingAnySEy = new SpinnerNumberModel(y, -0, bh, 1);
+ spinStartingAnySEy = new JSpinner(mStartingAnySEy);
+ spinStartingAnySEy.setValue(y);
+ panDeploy.add(spinStartingAnySEy, GBC.eol());
+
numFormatter.setMinimum(0);
numFormatter.setCommitsOnValidEdit(true);
diff --git a/megamek/src/megamek/client/ui/swing/Ruler.java b/megamek/src/megamek/client/ui/swing/Ruler.java
index b9a81e7b02f..2fa7d3983ff 100644
--- a/megamek/src/megamek/client/ui/swing/Ruler.java
+++ b/megamek/src/megamek/client/ui/swing/Ruler.java
@@ -43,6 +43,7 @@ public class Ruler extends JDialog implements BoardViewListener, IPreferenceChan
private int distance;
private Client client;
private BoardView bv;
+ private Game game;
private boolean flip;
private JPanel buttonPanel;
@@ -69,7 +70,7 @@ public class Ruler extends JDialog implements BoardViewListener, IPreferenceChan
private JCheckBox cboIsMech2 =
new JCheckBox(Messages.getString("Ruler.isMech"));
- public Ruler(JFrame f, Client c, BoardView b) {
+ public Ruler(JFrame f, Client c, BoardView b, Game g) {
super(f, Messages.getString("Ruler.title"), false);
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
@@ -81,6 +82,7 @@ public Ruler(JFrame f, Client c, BoardView b) {
bv = b;
client = c;
+ game = g;
b.addBoardViewListener(this);
try {
@@ -273,7 +275,7 @@ private void addPoint(Coords c) {
int absHeight = Integer.MIN_VALUE;
boolean isMech = false;
boolean entFound = false;
- for (Entity ent : client.getGame().getEntitiesVector(c)) {
+ for (Entity ent : game.getEntitiesVector(c)) {
int trAbsheight = ent.relHeight();
if (trAbsheight > absHeight) {
absHeight = trAbsheight;
@@ -316,20 +318,20 @@ private void setText() {
// leave at default value
}
- if (!client.getGame().getBoard().contains(start) || !client.getGame().getBoard().contains(end)) {
+ if (!game.getBoard().contains(start) || !game.getBoard().contains(end)) {
return;
}
String toHit1 = "", toHit2 = "";
ToHitData thd;
if (flip) {
- thd = LosEffects.calculateLos(client.getGame(),
+ thd = LosEffects.calculateLos(game,
buildAttackInfo(start, end, h1, h2, cboIsMech1.isSelected(),
- cboIsMech2.isSelected())).losModifiers(client.getGame());
+ cboIsMech2.isSelected())).losModifiers(game);
} else {
- thd = LosEffects.calculateLos(client.getGame(),
+ thd = LosEffects.calculateLos(game,
buildAttackInfo(end, start, h2, h1, cboIsMech2.isSelected(),
- cboIsMech1.isSelected())).losModifiers(client.getGame());
+ cboIsMech1.isSelected())).losModifiers(game);
}
if (thd.getValue() != TargetRoll.IMPOSSIBLE) {
toHit1 = thd.getValue() + " = ";
@@ -337,13 +339,13 @@ private void setText() {
toHit1 += thd.getDesc();
if (flip) {
- thd = LosEffects.calculateLos(client.getGame(),
+ thd = LosEffects.calculateLos(game,
buildAttackInfo(end, start, h2, h1, cboIsMech2.isSelected(),
- cboIsMech1.isSelected())).losModifiers(client.getGame());
+ cboIsMech1.isSelected())).losModifiers(game);
} else {
- thd = LosEffects.calculateLos(client.getGame(),
+ thd = LosEffects.calculateLos(game,
buildAttackInfo(start, end, h1, h2, cboIsMech1.isSelected(),
- cboIsMech2.isSelected())).losModifiers(client.getGame());
+ cboIsMech2.isSelected())).losModifiers(game);
}
if (thd.getValue() != TargetRoll.IMPOSSIBLE) {
toHit2 = thd.getValue() + " = ";
@@ -377,8 +379,8 @@ private LosEffects.AttackInfo buildAttackInfo(Coords c1, Coords c2, int h1,
ai.targetHeight = h2;
ai.attackerIsMech = attackerIsMech;
ai.targetIsMech = targetIsMech;
- ai.attackAbsHeight = client.getGame().getBoard().getHex(c1).floor() + h1;
- ai.targetAbsHeight = client.getGame().getBoard().getHex(c2).floor() + h2;
+ ai.attackAbsHeight = game.getBoard().getHex(c1).floor() + h1;
+ ai.targetAbsHeight = game.getBoard().getHex(c2).floor() + h2;
return ai;
}
diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java
index cb803585f47..bebb1bed4aa 100644
--- a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java
+++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java
@@ -1138,7 +1138,8 @@ public synchronized void paintComponent(Graphics g) {
drawDeployment(g);
}
- if (game.getPhase().isSetArtilleryAutohitHexes() && showAllDeployment) {
+ if ((game.getPhase().isSetArtilleryAutohitHexes() && showAllDeployment)
+ || (game.getPhase().isLounge())) {
drawAllDeployment(g);
}
@@ -1850,20 +1851,30 @@ private void drawAllDeployment(Graphics g) {
int drawWidth = (view.width / (int) (HEX_WC * scale)) + 3;
int drawHeight = (view.height / (int) (HEX_H * scale)) + 3;
+ List players = game.getPlayersList();
+ final GameOptions gOpts = game.getOptions();
+
+ if (gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ players = players.stream().filter(p -> p.isBot() || p.getId() == 0).collect(Collectors.toList());
+ }
+
+ if (game.getPhase().isLounge() && !localPlayer.isGameMaster()
+ && (gOpts.booleanOption(OptionsConstants.BASE_BLIND_DROP)
+ || gOpts.booleanOption(OptionsConstants.BASE_REAL_BLIND_DROP)) ) {
+ players = players.stream().filter(p -> !p.isEnemyOf(localPlayer)).collect(Collectors.toList());
+ }
+
Board board = game.getBoard();
// loop through the hexes
for (int i = 0; i < drawHeight; i++) {
for (int j = 0; j < drawWidth; j++) {
Coords c = new Coords(j + drawX, i + drawY);
- Enumeration allP = game.getPlayers();
- Player cp;
int pCount = 0;
int bThickness = 1 + 10 / game.getNoOfPlayers();
// loop through all players
- while (allP.hasMoreElements()) {
- cp = allP.nextElement();
- if (board.isLegalDeployment(c, cp)) {
- Color bC = cp.getColour().getColour();
+ for (Player player : players) {
+ if (board.isLegalDeployment(c, player)) {
+ Color bC = player.getColour().getColour();
drawHexBorder(g, getHexLocation(c), bC, (bThickness + 2) * pCount, bThickness);
pCount++;
}
@@ -3147,6 +3158,14 @@ public void drawRuler(Coords s, Coords e, Color sc, Color ec) {
repaint();
}
+ public Coords getRulerStart() {
+ return rulerStart;
+ }
+
+ public Coords getRulerEnd() {
+ return rulerEnd;
+ }
+
/**
* @return the coords at the specified point
*/
diff --git a/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java b/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java
index a988bd06549..44e60009d43 100644
--- a/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java
+++ b/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java
@@ -44,6 +44,7 @@
import megamek.client.ui.swing.widget.SkinSpecification;
import megamek.common.*;
import megamek.common.annotations.Nullable;
+import megamek.common.enums.GamePhase;
import megamek.common.event.*;
import megamek.common.force.Force;
import megamek.common.force.Forces;
@@ -753,11 +754,27 @@ public void componentShown(ComponentEvent e) {
boardPreviewW.setLocationRelativeTo(clientgui.frame);
try {
+ boardPreviewGame.setPhase(GamePhase.LOUNGE);
previewBV = new BoardView(boardPreviewGame, null, null);
previewBV.setDisplayInvalidHexInfo(false);
previewBV.setUseLOSTool(false);
- boardPreviewW.add(previewBV.getComponent(true));
+ JButton bpButton = new JButton(Messages.getString("BoardSelectionDialog.ViewGameBoardButton"));
+ bpButton.addActionListener(e -> previewGameBoard());
+ JPanel bpPanel = new JPanel();
+ bpPanel.setLayout(new BoxLayout(bpPanel, BoxLayout.PAGE_AXIS));
+ bpPanel.add(bpButton);
+ bpPanel.add(previewBV.getComponent(true));
+ boardPreviewW.add(bpPanel);
boardPreviewW.setSize(clientgui.frame.getWidth() / 2, clientgui.frame.getHeight() / 2);
+
+ Ruler.color1 = GUIP.getRulerColor1();
+ Ruler.color2 = GUIP.getRulerColor2();
+ Ruler ruler = new Ruler(clientgui.frame, client(), previewBV, boardPreviewGame);
+ ruler.setLocation(GUIP.getRulerPosX(), GUIP.getRulerPosY());
+ ruler.setSize(GUIP.getRulerSizeHeight(), GUIP.getRulerSizeWidth());
+ ruler.setAlwaysOnTop(true);
+ UIUtil.updateWindowBounds(ruler);
+
// Most boards will be far too large on the standard zoom
previewBV.zoomOut();
previewBV.zoomOut();
@@ -1138,6 +1155,16 @@ private void markServerSideBoard(BufferedImage image) {
public void previewGameBoard() {
Board newBoard = getPossibleGameBoard(false);
boardPreviewGame.setBoard(newBoard);
+ previewBV.setLocalPlayer(client().getLocalPlayer());
+ final GameOptions gOpts = game().getOptions();
+ boardPreviewGame.setOptions(gOpts);
+
+ for (Player player : game().getPlayersList()) {
+ boardPreviewGame.removePlayer(player.getId());
+ }
+ for (Player player : game().getPlayersList()) {
+ boardPreviewGame.setPlayer(player.getId(), player);
+ }
boardPreviewW.setVisible(true);
}
@@ -1251,7 +1278,8 @@ private void refreshMekTable() {
boolean localUnit = entity.getOwner().equals(localPlayer());
boolean teamUnit = !entity.getOwner().isEnemyOf(localPlayer());
boolean realBlindDrop = opts.booleanOption(OptionsConstants.BASE_REAL_BLIND_DROP);
- if (localUnit || teamUnit || !realBlindDrop) {
+ boolean localGM = localPlayer().isGameMaster();
+ if (localUnit || teamUnit || !realBlindDrop || localGM) {
mekModel.addUnit(entity);
}
}
@@ -1486,8 +1514,9 @@ void disembarkAll(Collection entities) {
* own units (and bots) though.
*/
boolean isEditable(Entity entity) {
- return clientgui.getLocalBots().containsKey(entity.getOwner().getName())
- || (entity.getOwnerId() == localPlayer().getId());
+ boolean localGM = clientgui.getClient().getLocalPlayer().isGameMaster();
+ return localGM || (clientgui.getLocalBots().containsKey(entity.getOwner().getName())
+ || (entity.getOwnerId() == localPlayer().getId()));
}
/**
@@ -1528,38 +1557,10 @@ public void configPlayer() {
return;
}
- PlayerSettingsDialog psd = new PlayerSettingsDialog(clientgui, c);
- if (psd.showDialog().isConfirmed()) {
- Player player = c.getLocalPlayer();
- player.setConstantInitBonus(psd.getInit());
- player.setNbrMFConventional(psd.getCnvMines());
- player.setNbrMFVibra(psd.getVibMines());
- player.setNbrMFActive(psd.getActMines());
- player.setNbrMFInferno(psd.getInfMines());
- psd.getSkillGenerationOptionsPanel().updateClient();
- player.setEmail(psd.getEmail());
-
- // The deployment position
- int startPos = psd.getStartPos();
- final GameOptions gOpts = clientgui.getClient().getGame().getOptions();
-
- player.setStartingPos(startPos);
- player.setStartOffset(psd.getStartOffset());
- player.setStartWidth(psd.getStartWidth());
- c.sendPlayerInfo();
-
- // If the gameoption set_arty_player_homeedge is set, adjust the player's offboard
- // arty units to be behind the newly selected home edge.
- OffBoardDirection direction = OffBoardDirection.translateStartPosition(startPos);
- if (direction != OffBoardDirection.NONE &&
- gOpts.booleanOption(OptionsConstants.BASE_SET_ARTY_PLAYER_HOMEEDGE)) {
- for (Entity entity: c.getGame().getPlayerEntities(c.getLocalPlayer(), false)) {
- if (entity.getOffBoardDirection() != OffBoardDirection.NONE) {
- entity.setOffBoard(entity.getOffBoardDistance(), direction);
- }
- }
- }
- }
+ PlayerSettingsDialog psd = new PlayerSettingsDialog(clientgui, c, previewBV);
+ psd.setModal(false);
+ psd.setAlwaysOnTop(true);
+ psd.showDialog();
}
/**
diff --git a/megamek/src/megamek/client/ui/swing/lobby/LobbyActions.java b/megamek/src/megamek/client/ui/swing/lobby/LobbyActions.java
index d1771161181..f8b8b69ddf3 100644
--- a/megamek/src/megamek/client/ui/swing/lobby/LobbyActions.java
+++ b/megamek/src/megamek/client/ui/swing/lobby/LobbyActions.java
@@ -354,9 +354,11 @@ public void customizeMech(Entity entity) {
if (editable) {
c = client().localBots.get(entity.getOwner().getName());
} else {
- editable |= entity.getOwnerId() == localPlayer().getId();
+ boolean localGM = localPlayer().isGameMaster();
+ editable |= localGM || entity.getOwnerId() == localPlayer().getId();
c = client();
}
+
// When we customize a single entity's C3 network setting,
// **ALL** members of the network may get changed.
Entity c3master = entity.getC3Master();
@@ -1192,7 +1194,9 @@ private Client correctSender(Force force) {
* player is allowed to change everything.
*/
boolean isEditable(Entity entity) {
- return client().localBots.containsKey(entity.getOwner().getName())
+ boolean localGM = client().getLocalPlayer().isGameMaster();
+ return localGM
+ || client().localBots.containsKey(entity.getOwner().getName())
|| (entity.getOwnerId() == localPlayer().getId())
|| (entity.partOfForce() && isSelfOrLocalBot(game().getForces().getOwner(entity.getForceId())))
|| (entity.partOfForce() && isEditable(game().getForces().getForce(entity)));
@@ -1222,7 +1226,8 @@ boolean isEditable(Collection entities) {
* of the entities are not on his team.
*/
boolean canSeeAll(Collection entities) {
- if (!isBlindDrop(game()) && !isRealBlindDrop(game())) {
+ boolean localGM = client().getLocalPlayer().isGameMaster();
+ if (localGM || (!isBlindDrop(game()) && !isRealBlindDrop(game()))) {
return true;
}
return entities.stream().noneMatch(this::isLocalEnemy);
diff --git a/megamek/src/megamek/client/ui/swing/lobby/LobbyMekCellFormatter.java b/megamek/src/megamek/client/ui/swing/lobby/LobbyMekCellFormatter.java
index 13d901b7a08..ccb1729196c 100644
--- a/megamek/src/megamek/client/ui/swing/lobby/LobbyMekCellFormatter.java
+++ b/megamek/src/megamek/client/ui/swing/lobby/LobbyMekCellFormatter.java
@@ -85,7 +85,8 @@ static String formatUnitFull(Entity entity, ChatLounge lobby, boolean forceView)
GameOptions options = game.getOptions();
Player localPlayer = client.getLocalPlayer();
Player owner = entity.getOwner();
- boolean hideEntity = owner.isEnemyOf(localPlayer)
+ boolean localGM = localPlayer.isGameMaster();
+ boolean hideEntity = !localGM && owner.isEnemyOf(localPlayer)
&& options.booleanOption(OptionsConstants.BASE_BLIND_DROP);
if (hideEntity) {
result.append(DOT_SPACER);
@@ -262,6 +263,16 @@ static String formatUnitFull(Entity entity, ChatLounge lobby, boolean forceView)
}
String msg_start = Messages.getString("ChatLounge.Start");
result.append(" " + msg_start + ":" + IStartingPositions.START_LOCATION_NAMES[sp]);
+ if (sp == 0) {
+ int NWx = entity.getStartingAnyNWx() + 1;
+ int NWy = entity.getStartingAnyNWy() + 1;
+ int SEx = entity.getStartingAnySEx() + 1;
+ int SEy = entity.getStartingAnySEy() + 1;
+ int hexes = (1 + SEx - NWx) * (1 + SEy - NWy);
+ if ((NWx + NWy + SEx + SEy) > 0) {
+ result.append(" (" + NWx + ", " + NWy + ")-(" + SEx + ", " + SEy + ") (" + hexes + ")");
+ }
+ }
int so = entity.getStartingOffset(true);
int sw = entity.getStartingWidth(true);
if ((so != 0) || (sw != 3)) {
@@ -483,7 +494,8 @@ static String formatUnitCompact(Entity entity, ChatLounge lobby, boolean forceVi
GameOptions options = game.getOptions();
Player localPlayer = client.getLocalPlayer();
Player owner = entity.getOwner();
- boolean hideEntity = owner.isEnemyOf(localPlayer)
+ boolean localGM = localPlayer.isGameMaster();
+ boolean hideEntity = !localGM && owner.isEnemyOf(localPlayer)
&& options.booleanOption(OptionsConstants.BASE_BLIND_DROP);
if (hideEntity) {
String value = " ";
diff --git a/megamek/src/megamek/client/ui/swing/lobby/LobbyUtility.java b/megamek/src/megamek/client/ui/swing/lobby/LobbyUtility.java
index e56f521369a..a41c8223ff2 100644
--- a/megamek/src/megamek/client/ui/swing/lobby/LobbyUtility.java
+++ b/megamek/src/megamek/client/ui/swing/lobby/LobbyUtility.java
@@ -73,11 +73,22 @@ static boolean isValidStartPos(Game game, Player player, int pos) {
if (!isExclusiveDeployment(game)) {
return true;
} else {
+ final GameOptions gOpts = game.getOptions();
+ List players = game.getPlayersList();
+
+ if (gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0) && !player.isBot() && player.getId() != 0) {
+ return true;
+ }
+
+ if (gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ players = players.stream().filter(p -> p.isBot() || p.getId() == 0).collect(Collectors.toList());
+ }
+
if (isTeamsShareVision(game)) {
- return game.getPlayersVector().stream().filter(p -> p.isEnemyOf(player))
+ return players.stream().filter(p -> p.isEnemyOf(player))
.noneMatch(p -> startPosOverlap(pos, p.getStartingPos()));
} else {
- return game.getPlayersVector().stream().filter(p -> !p.equals(player))
+ return players.stream().filter(p -> !p.equals(player))
.noneMatch(p -> startPosOverlap(pos, p.getStartingPos()));
}
}
diff --git a/megamek/src/megamek/client/ui/swing/lobby/MekTableModel.java b/megamek/src/megamek/client/ui/swing/lobby/MekTableModel.java
index c0a1cc2f9a8..46e0a251a16 100644
--- a/megamek/src/megamek/client/ui/swing/lobby/MekTableModel.java
+++ b/megamek/src/megamek/client/ui/swing/lobby/MekTableModel.java
@@ -104,7 +104,8 @@ public Object getValueAt(int row, int col) {
if (col == COLS.BV.ordinal()) {
boolean isEnemy = clientGui.getClient().getLocalPlayer().isEnemyOf(ownerOf(entity));
boolean isBlindDrop = clientGui.getClient().getGame().getOptions().booleanOption(OptionsConstants.BASE_BLIND_DROP);
- boolean hideEntity = isEnemy && isBlindDrop;
+ boolean localGM = clientGui.getClient().getLocalPlayer().isGameMaster();
+ boolean hideEntity = !localGM && isEnemy && isBlindDrop;
float size = chatLounge.isCompact() ? 0 : 0.2f;
return hideEntity ? "" : guiScaledFontHTML(size) + NumberFormat.getIntegerInstance().format(bv.get(row));
@@ -176,8 +177,10 @@ private void addCellData(InGameObject entity) {
// Note that units of a player's bots are obscured because they could be added from
// a MekHQ AtB campaign. Thus, the player can still configure them and so can identify
// the obscured units but has to actively decide to do it.
- boolean hideEntity = clientGui.getClient().getLocalPlayer().isEnemyOf(owner)
+ boolean localGM = clientGui.getClient().getLocalPlayer().isGameMaster();
+ boolean hideEntity = !localGM && clientGui.getClient().getLocalPlayer().isEnemyOf(owner)
&& clientGui.getClient().getGame().getOptions().booleanOption(OptionsConstants.BASE_BLIND_DROP);
+
if (hideEntity) {
unitTooltips.add(null);
pilotTooltips.add(null);
@@ -295,7 +298,8 @@ public Component getTableCellRendererComponent(final JTable table,
}
Player owner = ownerOf(entity);
- boolean showAsUnknown = clientGui.getClient().getLocalPlayer().isEnemyOf(owner)
+ boolean localGM = clientGui.getClient().getLocalPlayer().isGameMaster();
+ boolean showAsUnknown = !localGM && clientGui.getClient().getLocalPlayer().isEnemyOf(owner)
&& clientGui.getClient().getGame().getOptions().booleanOption(OptionsConstants.BASE_BLIND_DROP);
int size = UIUtil.scaleForGUI(MEKTABLE_IMGHEIGHT);
diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java
index f7374d79502..cf13f5ba4f8 100644
--- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java
+++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java
@@ -31,9 +31,13 @@
import megamek.client.ui.panels.SkillGenerationOptionsPanel;
import megamek.client.ui.swing.ClientGUI;
import megamek.client.ui.swing.GUIPreferences;
+import megamek.client.ui.swing.boardview.BoardView;
import megamek.client.ui.swing.util.UIUtil;
+import megamek.common.Entity;
import megamek.common.IStartingPositions;
+import megamek.common.OffBoardDirection;
import megamek.common.Player;
+import megamek.common.options.GameOptions;
import megamek.common.options.OptionsConstants;
import javax.swing.*;
@@ -59,10 +63,11 @@
*/
public class PlayerSettingsDialog extends AbstractButtonDialog {
- public PlayerSettingsDialog(ClientGUI cg, Client cl) {
+ public PlayerSettingsDialog(ClientGUI cg, Client cl, BoardView bv) {
super(cg.frame, "PlayerSettingsDialog", "PlayerSettingsDialog.title");
client = cl;
clientgui = cg;
+ this.bv = bv;
currentPlayerStartPos = cl.getLocalPlayer().getStartingPos();
if (currentPlayerStartPos > 10) {
currentPlayerStartPos -= 10;
@@ -80,6 +85,11 @@ protected void finalizeInitialization() throws Exception {
super.finalizeInitialization();
}
+ @Override
+ protected void okAction() {
+ apply();
+ }
+
/** Returns the chosen initiative modifier. */
public int getInit() {
return parseField(fldInit);
@@ -120,6 +130,22 @@ public int getStartPos() {
return currentPlayerStartPos;
}
+ public int getStartingAnyNWx() {
+ return Math.min((Integer) spinStartingAnyNWx.getValue(), (Integer) spinStartingAnySEx.getValue()) - 1;
+ }
+
+ public int getStartingAnyNWy() {
+ return Math.min((Integer) spinStartingAnyNWy.getValue(), (Integer) spinStartingAnySEy.getValue()) - 1;
+ }
+
+ public int getStartingAnySEx() {
+ return Math.max((Integer) spinStartingAnyNWx.getValue(), (Integer) spinStartingAnySEx.getValue()) - 1;
+ }
+
+ public int getStartingAnySEy() {
+ return Math.max((Integer) spinStartingAnyNWy.getValue(), (Integer) spinStartingAnySEy.getValue()) - 1;
+ }
+
/**
* @return the current {@link SkillGenerationOptionsPanel}
*/
@@ -136,6 +162,7 @@ public String getEmail() {
private final Client client;
private final ClientGUI clientgui;
+ private final BoardView bv;
// Initiative Section
private final JLabel labInit = new TipLabel(Messages.getString("PlayerSettingsDialog.initMod"), SwingConstants.RIGHT);
@@ -167,6 +194,10 @@ public String getEmail() {
private final DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory(numFormatter);
private final JFormattedTextField txtOffset = new JFormattedTextField(formatterFactory, 0);
private final JFormattedTextField txtWidth = new JFormattedTextField(formatterFactory, 3);
+ private JSpinner spinStartingAnyNWx;
+ private JSpinner spinStartingAnyNWy;
+ private JSpinner spinStartingAnySEx;
+ private JSpinner spinStartingAnySEy;
// Bot Settings Section
private final JButton butBotSettings = new JButton(Messages.getString("PlayerSettingsDialog.botSettings"));
@@ -248,10 +279,76 @@ private JPanel deploymentParametersPanel() {
result.add(txtOffset, GBC.eol());
result.add(lblWidth, GBC.std());
result.add(txtWidth, GBC.eol());
-
+
+ result.add(new JLabel(Messages.getString("CustomMechDialog.labDeploymentAnyNW")), GBC.std());
+ result.add(spinStartingAnyNWx, GBC.std());
+ result.add(spinStartingAnyNWy, GBC.eol());
+ result.add(new JLabel(Messages.getString("CustomMechDialog.labDeploymentAnySE")), GBC.std());
+ result.add(spinStartingAnySEx, GBC.std());
+ result.add(spinStartingAnySEy, GBC.eol());
+
+ JButton btnUseRuler = new JButton(Messages.getString("CustomMechDialog.BtnDeploymentUseRuler"));
+ btnUseRuler.setToolTipText(Messages.getString("CustomMechDialog.BtnDeploymentUseRulerTip"));
+ btnUseRuler.addActionListener(e -> useRuler());
+ result.add(btnUseRuler, GBC.std());
+ JButton btnApply = new JButton(Messages.getString("CustomMechDialog.BtnDeploymentApply"));
+ btnApply.setToolTipText(Messages.getString("CustomMechDialog.BtnDeploymentApplyTip"));
+ btnApply.addActionListener(e -> apply());
+ result.add(btnApply, GBC.eol());
+
return result;
}
-
+
+
+ private void useRuler() {
+ if (bv.getRulerStart() != null && bv.getRulerEnd() != null) {
+ int x = Math.min(bv.getRulerStart().getX(), bv.getRulerEnd().getX());
+ spinStartingAnyNWx.setValue(x + 1);
+ int y = Math.min(bv.getRulerStart().getY(), bv.getRulerEnd().getY());
+ spinStartingAnyNWy.setValue(y + 1);
+ x = Math.max(bv.getRulerStart().getX(), bv.getRulerEnd().getX());
+ spinStartingAnySEx.setValue(x + 1);
+ y = Math.max(bv.getRulerStart().getY(), bv.getRulerEnd().getY());
+ spinStartingAnySEy.setValue(y + 1);
+ }
+ }
+
+ private void apply() {
+ Player player = client.getLocalPlayer();
+
+ player.setConstantInitBonus(getInit());
+ player.setNbrMFConventional(getCnvMines());
+ player.setNbrMFVibra(getVibMines());
+ player.setNbrMFActive(getActMines());
+ player.setNbrMFInferno(getInfMines());
+ getSkillGenerationOptionsPanel().updateClient();
+ player.setEmail(getEmail());
+
+ final GameOptions gOpts = clientgui.getClient().getGame().getOptions();
+
+ // If the gameoption set_arty_player_homeedge is set, adjust the player's offboard
+ // arty units to be behind the newly selected home edge.
+ OffBoardDirection direction = OffBoardDirection.translateStartPosition(getStartPos());
+ if (direction != OffBoardDirection.NONE &&
+ gOpts.booleanOption(OptionsConstants.BASE_SET_ARTY_PLAYER_HOMEEDGE)) {
+ for (Entity entity: client.getGame().getPlayerEntities(client.getLocalPlayer(), false)) {
+ if (entity.getOffBoardDirection() != OffBoardDirection.NONE) {
+ entity.setOffBoard(entity.getOffBoardDistance(), direction);
+ }
+ }
+ }
+
+ // The deployment position
+ player.setStartingPos(getStartPos());
+ player.setStartOffset(getStartOffset());
+ player.setStartWidth(getStartWidth());
+ player.setStartingAnyNWx(getStartingAnyNWx());
+ player.setStartingAnyNWy(getStartingAnyNWy());
+ player.setStartingAnySEx(getStartingAnySEx());
+ player.setStartingAnySEy(getStartingAnySEy());
+ client.sendPlayerInfo();
+ }
+
private JPanel initiativeSection() {
JPanel result = new OptionPanel("PlayerSettingsDialog.header.initMod");
Content panContent = new Content(new GridLayout(1, 2, 10, 5));
@@ -309,7 +406,27 @@ private void setupValues() {
fldEmail.setText(player.getEmail());
txtWidth.setText(Integer.toString(player.getStartWidth()));
txtOffset.setText(Integer.toString(player.getStartOffset()));
-
+
+ int bh = clientgui.getClient().getMapSettings().getBoardHeight();
+ int bw = clientgui.getClient().getMapSettings().getBoardWidth();
+
+ SpinnerNumberModel mStartingAnyNWx = new SpinnerNumberModel(0, 0,bw, 1);
+ spinStartingAnyNWx = new JSpinner(mStartingAnyNWx);
+ SpinnerNumberModel mStartingAnyNWy = new SpinnerNumberModel(0, 0, bh, 1);
+ spinStartingAnyNWy = new JSpinner(mStartingAnyNWy);
+ SpinnerNumberModel mStartingAnySEx = new SpinnerNumberModel(0, 0, bw, 1);
+ spinStartingAnySEx = new JSpinner(mStartingAnySEx);
+ SpinnerNumberModel mStartingAnySEy = new SpinnerNumberModel(0, -0, bh, 1);
+ spinStartingAnySEy = new JSpinner(mStartingAnySEy);
+
+ int x = Math.min(player.getStartingAnyNWx() + 1, bw);
+ spinStartingAnyNWx.setValue(x);
+ int y = Math.min(player.getStartingAnyNWy() + 1, bh);
+ spinStartingAnyNWy.setValue(y);
+ x = Math.min(player.getStartingAnySEx() + 1, bw);
+ spinStartingAnySEx.setValue(x);
+ y = Math.min(player.getStartingAnySEy() + 1, bh);
+ spinStartingAnySEy.setValue(y);
}
private void setupStartGrid() {
diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerTable.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerTable.java
index 0b320781159..78ad59fddac 100644
--- a/megamek/src/megamek/client/ui/swing/lobby/PlayerTable.java
+++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerTable.java
@@ -25,6 +25,7 @@
import megamek.client.ui.swing.util.UIUtil;
import megamek.common.IStartingPositions;
import megamek.common.Player;
+import megamek.common.options.GameOptions;
import megamek.common.options.OptionsConstants;
import javax.swing.*;
@@ -195,8 +196,28 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
result.append(guiScaledFontHTML());
String msg_start = Messages.getString("ChatLounge.Start");
- if ((player.getStartingPos() >= 0) && (player.getStartingPos() <= IStartingPositions.START_LOCATION_NAMES.length)) {
+
+ final GameOptions gOpts = lobby.game().getOptions();
+ if (gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0) && !player.isBot() && player.getId() != 0) {
+ result.append(msg_start + ": " + Messages.getString("ChatLounge.Player0"));
+ } else if ((!lobby.client().getLocalPlayer().isGameMaster()
+ && (isEnemy)
+ && (gOpts.booleanOption(OptionsConstants.BASE_BLIND_DROP)
+ || gOpts.booleanOption(OptionsConstants.BASE_REAL_BLIND_DROP)))) {
+ result.append(msg_start + ": " + Messages.getString("ChatLounge.Blind"));
+ } else if ((player.getStartingPos() >= 0)
+ && (player.getStartingPos() <= IStartingPositions.START_LOCATION_NAMES.length)) {
result.append(msg_start + ": " + IStartingPositions.START_LOCATION_NAMES[player.getStartingPos()]);
+
+ if (player.getStartingPos() == 0) {
+ int NWx = player.getStartingAnyNWx() + 1;
+ int NWy = player.getStartingAnyNWy() + 1;
+ int SEx = player.getStartingAnySEx() + 1;
+ int SEy = player.getStartingAnySEy() + 1;
+ if ((NWx + NWy + SEx + SEy) > 0) {
+ result.append(" (" + NWx + ", " + NWy + ")-(" + SEx + ", " + SEy + ")");
+ }
+ }
int so = player.getStartOffset();
int sw = player.getStartWidth();
if ((so != 0) || (sw != 3)) {
diff --git a/megamek/src/megamek/common/Board.java b/megamek/src/megamek/common/Board.java
index ecd7e834b65..c5a7b194769 100644
--- a/megamek/src/megamek/common/Board.java
+++ b/megamek/src/megamek/common/Board.java
@@ -846,7 +846,7 @@ public static boolean isValid(String board) {
* Can the given player deploy at these coordinates?
*/
public boolean isLegalDeployment(Coords c, Player p) {
- return isLegalDeployment(c, p.getStartingPos(), p.getStartWidth(), p.getStartOffset());
+ return isLegalDeployment(c, p.getStartingPos(), p.getStartWidth(), p.getStartOffset(), p.getStartingAnyNWx(), p.getStartingAnyNWy(), p.getStartingAnySEx(), p.getStartingAnySEy());
}
/**
@@ -857,13 +857,13 @@ public boolean isLegalDeployment(Coords c, Entity e) {
return false;
}
- return isLegalDeployment(c, e.getStartingPos(), e.getStartingWidth(), e.getStartingOffset());
+ return isLegalDeployment(c, e.getStartingPos(), e.getStartingWidth(), e.getStartingOffset(), e.getStartingAnyNWx(), e.getStartingAnyNWy(), e.getStartingAnySEx(), e.getStartingAnySEy());
}
/**
* Can an object be deployed at these coordinates, given a starting zone, width of starting zone and offset from edge of board?
*/
- public boolean isLegalDeployment(Coords c, int zoneType, int startingWidth, int startingOffset) {
+ public boolean isLegalDeployment(Coords c, int zoneType, int startingWidth, int startingOffset, int startingAnyNWx, int startingAnyNWy, int startingAnySEx, int startingAnySEy) {
if ((c == null) || !contains(c)) {
return false;
}
@@ -876,7 +876,10 @@ public boolean isLegalDeployment(Coords c, int zoneType, int startingWidth, int
switch (zoneType) {
case START_ANY:
- return true;
+ return (((startingAnyNWx == Entity.STARTING_ANY_NONE) || (c.getX() >= startingAnyNWx))
+ && ((startingAnySEx == Entity.STARTING_ANY_NONE) || (c.getX() <= startingAnySEx))
+ && ((startingAnyNWy == Entity.STARTING_ANY_NONE) || (c.getY() >= startingAnyNWy))
+ && ((startingAnySEy == Entity.STARTING_ANY_NONE) || (c.getY() <= startingAnySEy)));
case START_NW:
return ((c.getX() < (minx + nLimit)) && (c.getX() >= minx) && (c.getY() >= miny) && (c.getY() < (height / 2)))
|| ((c.getY() < (miny + nLimit)) && (c.getY() >= miny) && (c.getX() >= minx) && (c.getX() < (width / 2)));
diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java
index 7adba24108f..a3d5a2334d7 100644
--- a/megamek/src/megamek/common/Entity.java
+++ b/megamek/src/megamek/common/Entity.java
@@ -210,6 +210,12 @@ public abstract class Entity extends TurnOrdered implements Transporter, Targeta
private int startingOffset = 0;
private int startingWidth = 3;
+ public static final int STARTING_ANY_NONE = -1;
+ private int startingAnyNWx = STARTING_ANY_NONE;
+ private int startingAnyNWy = STARTING_ANY_NONE;
+ private int startingAnySEx = STARTING_ANY_NONE;
+ private int startingAnySEy = STARTING_ANY_NONE;
+
/**
* The pilot of the entity. Even infantry has a 'pilot'.
*/
@@ -12890,7 +12896,12 @@ public int getStartingPos() {
public int getStartingPos(boolean inheritFromOwner) {
if (inheritFromOwner && startingPos == Board.START_NONE) {
- return getOwner().getStartingPos();
+ final GameOptions gOpts = getGame().getOptions();
+ if (!getOwner().isBot() && gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ return game.getPlayer(0).getStartingPos();
+ } else {
+ return getOwner().getStartingPos();
+ }
}
return startingPos;
}
@@ -15288,7 +15299,12 @@ public int getStartingOffset(boolean inheritFromOwner) {
// if we are given permission to use the owner's settings
// and have specified entity-specific settings, use the owner's settings
if (inheritFromOwner && (startingPos == Board.START_NONE)) {
- return getOwner().getStartOffset();
+ final GameOptions gOpts = getGame().getOptions();
+ if (!getOwner().isBot() && gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ return game.getPlayer(0).getStartOffset();
+ } else {
+ return getOwner().getStartOffset();
+ }
}
return startingOffset;
@@ -15306,7 +15322,12 @@ public int getStartingWidth(boolean inheritFromOwner) {
// if we are given permission to use the owner's settings
// and have specified entity-specific settings, use the owner's settings
if (inheritFromOwner && (startingPos == Board.START_NONE)) {
- return getOwner().getStartWidth();
+ final GameOptions gOpts = getGame().getOptions();
+ if (!getOwner().isBot() && gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ return game.getPlayer(0).getStartWidth();
+ } else {
+ return getOwner().getStartWidth();
+ }
}
return startingWidth;
@@ -15316,6 +15337,98 @@ public void setStartingWidth(int startingWidth) {
this.startingWidth = startingWidth;
}
+ public int getStartingAnyNWx() {
+ return getStartingAnyNWx(true);
+ }
+
+ public int getStartingAnyNWx(boolean inheritFromOwner) {
+ // if we are given permission to use the owner's settings
+ // and have specified entity-specific settings, use the owner's settings
+ if (inheritFromOwner && (startingPos == Board.START_NONE)) {
+ final GameOptions gOpts = getGame().getOptions();
+ if (!getOwner().isBot() && gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ return game.getPlayer(0).getStartingAnyNWx();
+ } else {
+ return getOwner().getStartingAnyNWx();
+ }
+ }
+
+ return startingAnyNWx;
+ }
+
+ public void setStartingAnyNWx(int i) {
+ this.startingAnyNWx = i;
+ }
+
+ public int getStartingAnyNWy() {
+ return getStartingAnyNWy(true);
+ }
+
+ public int getStartingAnyNWy(boolean inheritFromOwner) {
+ // if we are given permission to use the owner's settings
+ // and have specified entity-specific settings, use the owner's settings
+ if (inheritFromOwner && (startingPos == Board.START_NONE)) {
+ final GameOptions gOpts = getGame().getOptions();
+ if (!getOwner().isBot() && gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ return game.getPlayer(0).getStartingAnyNWy();
+ } else {
+ return getOwner().getStartingAnyNWy();
+ }
+ }
+
+ return startingAnyNWy;
+ }
+
+ public void setStartingAnyNWy(int i) {
+ this.startingAnyNWy = i;
+ }
+
+ public int getStartingAnySEx() {
+ return getStartingAnySEx(true);
+ }
+
+ public int getStartingAnySEx(boolean inheritFromOwner) {
+ // if we are given permission to use the owner's settings
+ // and have specified entity-specific settings, use the owner's settings
+ if (inheritFromOwner && (startingPos == Board.START_NONE)) {
+ final GameOptions gOpts = getGame().getOptions();
+ if (!getOwner().isBot() && gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ return game.getPlayer(0).getStartingAnySEx();
+ } else {
+ return getOwner().getStartingAnySEx();
+ }
+ }
+
+ return startingAnySEx;
+ }
+
+ public void setStartingAnySEx(int i) {
+ this.startingAnySEx = i;
+ }
+
+ public int getStartingAnySEy() {
+ return getStartingAnySEy(true);
+ }
+
+ public int getStartingAnySEy(boolean inheritFromOwner) {
+ // if we are given permission to use the owner's settings
+ // and have specified entity-specific settings, use the owner's settings
+ if (inheritFromOwner && (startingPos == Board.START_NONE)) {
+ final GameOptions gOpts = getGame().getOptions();
+ if (!getOwner().isBot() && gOpts.booleanOption(OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0)) {
+ return game.getPlayer(0).getStartingAnySEy();
+ } else {
+ return getOwner().getStartingAnySEy();
+ }
+ }
+
+ return startingAnySEy;
+ }
+
+ public void setStartingAnySEy(int i) {
+ this.startingAnySEy = i;
+ }
+
public int getBloodStalkerTarget() {
return bloodStalkerTarget;
}
diff --git a/megamek/src/megamek/common/EntityListFile.java b/megamek/src/megamek/common/EntityListFile.java
index 84f5c11649e..331c27ea563 100644
--- a/megamek/src/megamek/common/EntityListFile.java
+++ b/megamek/src/megamek/common/EntityListFile.java
@@ -719,6 +719,14 @@ private static void writeEntityList(Writer output, ArrayList list) throw
output.write(String.valueOf(entity.getStartingWidth(false)));
output.write("\" deploymentZoneOffset=\"");
output.write(String.valueOf(entity.getStartingOffset(false)));
+ output.write("\" " + MULParser.DEPLOYMENT_ZONE_ANY_NWX + "=\"");
+ output.write(String.valueOf(entity.getStartingAnyNWx(false)));
+ output.write("\" " + MULParser.DEPLOYMENT_ZONE_ANY_NWY + "=\"");
+ output.write(String.valueOf(entity.getStartingAnyNWy(false)));
+ output.write("\" " + MULParser.DEPLOYMENT_ZONE_ANY_SEX + "=\"");
+ output.write(String.valueOf(entity.getStartingAnySEx(false)));
+ output.write("\" " + MULParser.DEPLOYMENT_ZONE_ANY_SEY + "=\"");
+ output.write(String.valueOf(entity.getStartingAnySEy(false)));
output.write("\" neverDeployed=\"");
output.write(String.valueOf(entity.wasNeverDeployed()));
if (entity.isAero()) {
diff --git a/megamek/src/megamek/common/MULParser.java b/megamek/src/megamek/common/MULParser.java
index a18781f04b0..37d5595d371 100644
--- a/megamek/src/megamek/common/MULParser.java
+++ b/megamek/src/megamek/common/MULParser.java
@@ -144,6 +144,10 @@ public class MULParser {
private static final String DEPLOYMENT_ZONE = "deploymentZone";
private static final String DEPLOYMENT_ZONE_WIDTH = "deploymentZoneWidth";
private static final String DEPLOYMENT_ZONE_OFFSET = "deploymentZoneOffset";
+ public static final String DEPLOYMENT_ZONE_ANY_NWX = "deploymentZoneAnyNWx";
+ public static final String DEPLOYMENT_ZONE_ANY_NWY = "deploymentZoneAnyNWy";
+ public static final String DEPLOYMENT_ZONE_ANY_SEX = "deploymentZoneAnySEx";
+ public static final String DEPLOYMENT_ZONE_ANY_SEY = "deploymentZoneAnySEy";
private static final String NEVER_DEPLOYED = "neverDeployed";
private static final String VELOCITY = "velocity";
public static final String ALTITUDE = "altitude";
@@ -710,6 +714,35 @@ private void parseEntityAttributes(Entity entity, Element entityTag) {
entity.setStartingOffset(0);
}
+ // deployment zone Any
+ try {
+ int deployZoneAnyNWx = Integer.parseInt(entityTag.getAttribute(DEPLOYMENT_ZONE_ANY_NWX));
+ entity.setStartingAnyNWx(deployZoneAnyNWx);
+ } catch (Exception e) {
+ entity.setStartingAnyNWx(Entity.STARTING_ANY_NONE);
+ }
+
+ try {
+ int deployZoneAnyNWy = Integer.parseInt(entityTag.getAttribute(DEPLOYMENT_ZONE_ANY_NWY));
+ entity.setStartingAnyNWy(deployZoneAnyNWy);
+ } catch (Exception e) {
+ entity.setStartingAnyNWy(Entity.STARTING_ANY_NONE);
+ }
+
+ try {
+ int deployZoneAnySEx = Integer.parseInt(entityTag.getAttribute(DEPLOYMENT_ZONE_ANY_SEX));
+ entity.setStartingAnySEx(deployZoneAnySEx);
+ } catch (Exception e) {
+ entity.setStartingAnySEx(Entity.STARTING_ANY_NONE);
+ }
+
+ try {
+ int deployZoneAnySEy = Integer.parseInt(entityTag.getAttribute(DEPLOYMENT_ZONE_ANY_SEY));
+ entity.setStartingAnySEy(deployZoneAnySEy);
+ } catch (Exception e) {
+ entity.setStartingAnySEy(Entity.STARTING_ANY_NONE);
+ }
+
// Was never deployed
try {
String ndeploy = entityTag.getAttribute(NEVER_DEPLOYED);
diff --git a/megamek/src/megamek/common/Player.java b/megamek/src/megamek/common/Player.java
index f88228a2c98..7a6a30f646e 100644
--- a/megamek/src/megamek/common/Player.java
+++ b/megamek/src/megamek/common/Player.java
@@ -56,6 +56,10 @@ public final class Player extends TurnOrdered {
private int startingPos = Board.START_ANY;
private int startOffset = 0;
private int startWidth = 3;
+ private int startingAnyNWx = Entity.STARTING_ANY_NONE;
+ private int startingAnyNWy = Entity.STARTING_ANY_NONE;
+ private int startingAnySEx = Entity.STARTING_ANY_NONE;
+ private int startingAnySEy = Entity.STARTING_ANY_NONE;
// number of minefields
private int numMfConv = 0;
@@ -384,6 +388,38 @@ public void setStartWidth(int startWidth) {
this.startWidth = startWidth;
}
+ public int getStartingAnyNWx() {
+ return startingAnyNWx;
+ }
+
+ public void setStartingAnyNWx(int i) {
+ this.startingAnyNWx = i;
+ }
+
+ public int getStartingAnyNWy() {
+ return startingAnyNWy;
+ }
+
+ public void setStartingAnyNWy(int i) {
+ this.startingAnyNWy = i;
+ }
+
+ public int getStartingAnySEx() {
+ return startingAnySEx;
+ }
+
+ public void setStartingAnySEx(int i) {
+ this.startingAnySEx = i;
+ }
+
+ public int getStartingAnySEy() {
+ return startingAnySEy;
+ }
+
+ public void setStartingAnySEy(int i) {
+ this.startingAnySEy = i;
+ }
+
/**
* Set deployment zone to edge of board for reinforcements
*/
@@ -748,6 +784,11 @@ public Player copy() {
copy.startOffset = startOffset;
copy.startWidth = startWidth;
+ copy.startingAnyNWx = startingAnyNWx;
+ copy.startingAnyNWy = startingAnyNWy;
+ copy.startingAnySEx = startingAnySEx;
+ copy.startingAnySEy = startingAnySEy;
+
copy.numMfConv = numMfConv;
copy.numMfCmd = numMfCmd;
copy.numMfVibra = numMfVibra;
diff --git a/megamek/src/megamek/common/options/GameOptions.java b/megamek/src/megamek/common/options/GameOptions.java
index 7c9d5e9d97b..63936a5a375 100755
--- a/megamek/src/megamek/common/options/GameOptions.java
+++ b/megamek/src/megamek/common/options/GameOptions.java
@@ -61,7 +61,9 @@ public synchronized void initialize() {
addOption(base, OptionsConstants.BASE_REAL_BLIND_DROP, false);
addOption(base, OptionsConstants.BASE_LOBBY_AMMO_DUMP, false);
addOption(base, OptionsConstants.BASE_DUMPING_FROM_ROUND, 1);
- addOption(base, OptionsConstants.BASE_SET_ARTY_PLAYER_HOMEEDGE, false);
+ addOption(base, OptionsConstants.BASE_SET_ARTY_PLAYER_HOMEEDGE, false);
+ addOption(base, OptionsConstants.BASE_SET_DEFAULT_TEAM_1, false);
+ addOption(base, OptionsConstants.BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0, false);
addOption(base, OptionsConstants.BASE_RESTRICT_GAME_COMMANDS, false);
addOption(base, OptionsConstants.BASE_DISABLE_LOCAL_SAVE, false);
addOption(base, OptionsConstants.BASE_BRIDGECF, 0);
diff --git a/megamek/src/megamek/common/options/OptionsConstants.java b/megamek/src/megamek/common/options/OptionsConstants.java
index 02d11bd3fce..127433e1a93 100644
--- a/megamek/src/megamek/common/options/OptionsConstants.java
+++ b/megamek/src/megamek/common/options/OptionsConstants.java
@@ -281,6 +281,8 @@ public class OptionsConstants {
public static final String BASE_LOBBY_AMMO_DUMP = "lobby_ammo_dump";
public static final String BASE_DUMPING_FROM_ROUND = "dumping_from_round";
public static final String BASE_SET_ARTY_PLAYER_HOMEEDGE = "set_arty_player_homeedge";
+ public static final String BASE_SET_DEFAULT_TEAM_1 = "set_default_team_1";
+ public static final String BASE_SET_PLAYER_DEPLOYMENT_TO_PLAYER0 = "set_player_deployment_to_player0";
public static final String BASE_RESTRICT_GAME_COMMANDS = "restrict_game_commands";
public static final String BASE_DISABLE_LOCAL_SAVE = "disable_local_save";
public static final String BASE_BRIDGECF = "bridgeCF";
diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java
index 1a566c6e304..d5d8910c1ea 100644
--- a/megamek/src/megamek/server/GameManager.java
+++ b/megamek/src/megamek/server/GameManager.java
@@ -482,7 +482,13 @@ public void saveGame(String sFile) {
public void disconnect(Player player) {
// in the lounge, just remove all entities for that player
if (getGame().getPhase().isLounge()) {
- removeAllEntitiesOwnedBy(player);
+ List gms = game.getPlayersList().stream().filter(p -> p.isGameMaster()).collect(Collectors.toList());
+
+ if (gms.size() > 0) {
+ transferAllEnititiesOwnedBy(player, gms.get(0));
+ } else {
+ removeAllEntitiesOwnedBy(player);
+ }
}
// if a player has active entities, he becomes a ghost
@@ -540,6 +546,16 @@ public void checkForObservers() {
}
}
+ private void transferAllEnititiesOwnedBy(Player pFrom, Player pTo) {
+ for (Entity entity : game.getEntitiesVector().stream().filter(e -> e.getOwner().equals(pFrom)).collect(Collectors.toList())) {
+ entity.setOwner(pTo);
+ }
+ game.getForces().correct();
+ ServerLobbyHelper.correctLoading(game);
+ ServerLobbyHelper.correctC3Connections(game);
+ send(createFullEntitiesPacket());
+ }
+
@Override
public void removeAllEntitiesOwnedBy(Player player) {
int pid = player.getId();
diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java
index 0fd46b61af3..23d452d2ff0 100644
--- a/megamek/src/megamek/server/Server.java
+++ b/megamek/src/megamek/server/Server.java
@@ -32,6 +32,8 @@
import megamek.common.net.factories.ConnectionFactory;
import megamek.common.net.listeners.ConnectionListener;
import megamek.common.net.packets.Packet;
+import megamek.common.options.GameOptions;
+import megamek.common.options.OptionsConstants;
import megamek.common.preference.PreferenceManager;
import megamek.common.util.EmailService;
import megamek.common.util.SerializationHelper;
@@ -542,6 +544,10 @@ private void receivePlayerInfo(Packet packet, int connId) {
gamePlayer.setStartingPos(player.getStartingPos());
gamePlayer.setStartWidth(player.getStartWidth());
gamePlayer.setStartOffset(player.getStartOffset());
+ gamePlayer.setStartingAnyNWx(player.getStartingAnyNWx());
+ gamePlayer.setStartingAnyNWy(player.getStartingAnyNWy());
+ gamePlayer.setStartingAnySEx(player.getStartingAnySEx());
+ gamePlayer.setStartingAnySEy(player.getStartingAnySEy());
gamePlayer.setTeam(player.getTeam());
gamePlayer.setCamouflage(player.getCamouflage().clone());
gamePlayer.setNbrMFConventional(player.getNbrMFConventional());
@@ -751,12 +757,18 @@ private Player addNewPlayer(int connId, String name, boolean isBot) {
int team = Player.TEAM_UNASSIGNED;
if (getGame().getPhase().isLounge()) {
team = Player.TEAM_NONE;
- for (Player p : getGame().getPlayersVector()) {
- if (p.getTeam() > team) {
- team = p.getTeam();
+ final GameOptions gOpts = getGame().getOptions();
+ if (isBot || !gOpts.booleanOption(OptionsConstants.BASE_SET_DEFAULT_TEAM_1)) {
+ for (Player p : getGame().getPlayersList()) {
+ if (p.getTeam() > team) {
+ team = p.getTeam();
+ }
}
+ team++;
+ } else {
+ team = 1;
}
- team++;
+
}
Player newPlayer = new Player(connId, name);
newPlayer.setBot(isBot);