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

allow fleeing at end of movement #6230

Merged
merged 13 commits into from
Nov 29, 2024
6 changes: 4 additions & 2 deletions megamek/i18n/megamek/common/report-messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@
1511= to maximise range.

2000=<newline><B>Movement Phase</B><newline>-------------------
2005=<newline><data> (<data>) flees the battlefield.
2005=<newline><data> (<data>) flees the battlefield to the <data>.
2010=It carries <data> (<data>) with it.
2015=It takes <data> (<data>) with it.
2016=It carries <data> with it.
2017=<newline> flee failed.
2020=<newline><data> ejects from a <data> (<data>).
2025=<newline><data> (<data>) is abandoned by its crew.
2026=<newline><data> (<data>) is given the order to abandon ship.
Expand Down Expand Up @@ -159,7 +160,7 @@
2216=collides!
2217=<<span class='miss'><B>misses</B></span>.
2220=<data> (<data>) takes <data> damage from the collision.
2230=<newline><span class='info'>*** <data> (<data>) has been forced from the field. ***</span>
2230=<newline><span class='info'>*** <data> (<data>) has been forced from the field to the <data>. ***</span>
2235=<data> (<data>) is displaced into hex <data>.
2240=<data> (<data>) is displaced into hex <data>, violating stacking with <data> (<data>).
2245=<data> attempts to clear a(n) <data> mine in hex <data>:
Expand Down Expand Up @@ -1037,6 +1038,7 @@
7075=The following units never entered the field of battle:
7076=All fighters in squadron destroyed
7080=<newline>The following units are in retreat:
7081=fled to the <data>.
7085=<newline>Graveyard contains:
7090=<newline>The following utterly destroyed units are not available for salvage:
7095=<newline>Detailed unit status saved to entitystatus.txt
Expand Down
4 changes: 2 additions & 2 deletions megamek/src/megamek/client/bot/princess/Princess.java
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ protected void calculateTargetingOffBoardTurn() {
Entity entityToFire = getGame().getFirstEntity(getMyTurn());

// if we're crippled, off-board and can do so, disengage
if (entityToFire.isOffBoard() && entityToFire.canFlee() && entityToFire.isCrippled(true)) {
if (entityToFire.isOffBoard() && entityToFire.canFlee(entityToFire.getPosition()) && entityToFire.isCrippled(true)) {
Vector<EntityAction> disengageVector = new Vector<>();
disengageVector.add(new DisengageAction(entityToFire.getId()));
sendAttackData(entityToFire.getId(), disengageVector);
Expand Down Expand Up @@ -2138,7 +2138,7 @@ boolean canShootWhileFallingBack(Entity entity) {
boolean mustFleeBoard(final Entity entity) {
if (!isFallingBack(entity)) {
return false;
} else if (!entity.canFlee()) {
} else if (!entity.canFlee(entity.getPosition())) {
return false;
} else if (0 < getPathRanker(entity).distanceToHomeEdge(entity.getPosition(),
getHomeEdge(entity), getGame())) {
Expand Down
26 changes: 23 additions & 3 deletions megamek/src/megamek/client/ui/swing/MovementDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,8 @@ private void updateButtons() {
(ce instanceof Tank)
&& (ce.getSwarmAttackerId() != Entity.NONE));

setFleeEnabled(ce.canFlee());
updateFleeButton();

if (gOpts.booleanOption(OptionsConstants.ADVGRNDMOV_VEHICLES_CAN_EJECT) && (ce instanceof Tank)) {
// Vehicle don't have ejection systems so crews abandon, and must enter a valid
// hex.
Expand Down Expand Up @@ -968,9 +969,28 @@ private void updateMove(boolean redrawMovement) {
if (redrawMovement) {
clientgui.getBoardView().drawMovementData(ce(), cmd);
}

updateFleeButton();
updateDonePanel();
}

private void updateFleeButton() {
boolean fleeStart = (cmd.getLastStep() == null)
&& ce().canFlee(ce().getPosition());
boolean jumpMoveRemaining = (cmd.getLastStep() != null)
&& cmd.getLastStep().isJumping()
&& (cmd.getMpUsed() < ce().getJumpMP());
boolean runMoveRemaining = (cmd.getLastStep() != null)
&& !cmd.getLastStep().isJumping()
&& (cmd.getMpUsed() < ce().getRunMP());
boolean moveRemaining = jumpMoveRemaining || runMoveRemaining;
boolean fleeEnd = (cmd.getLastStep() != null)
&& (cmd.getLastStepMovementType() != EntityMovementType.MOVE_ILLEGAL)
&& moveRemaining
&& clientgui.getClient().getGame().canFleeFrom(ce(), cmd.getLastStep().getPosition());
setFleeEnabled(fleeStart || fleeEnd);
}

private void updateAeroButtons() {
if (ce() != null && ce().isAero()) {
getBtn(MoveCommand.MOVE_THRUST).setEnabled(true);
Expand Down Expand Up @@ -1966,6 +1986,7 @@ public synchronized void hexMoused(BoardViewEvent b) {
// else clear movement
clear();
}

return;
}
// if not valid, tell why
Expand Down Expand Up @@ -4893,10 +4914,9 @@ public synchronized void actionPerformed(ActionEvent ev) {
&& clientgui.doYesNoDialog(
Messages.getString("MovementDisplay.EscapeDialog.title"),
Messages.getString("MovementDisplay.EscapeDialog.message"))) {

clear();
addStepToMovePath(MoveStepType.FLEE);
ready();
clear();
} else if (actionCmd.equals(MoveCommand.MOVE_FLY_OFF.getCmd())
&& clientgui.doYesNoDialog(
Messages.getString("MovementDisplay.FlyOffDialog.title"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ private void beginMyTurn() {
if (GUIP.getAutoSelectNextUnit()) {
selectEntity(clientgui.getClient().getFirstEntityNum());
}
setDisengageEnabled((ce() != null) && attacks.isEmpty() && ce().canFlee());
setDisengageEnabled((ce() != null) && attacks.isEmpty() && ce().canFlee(ce().getPosition()));

GameTurn turn = clientgui.getClient().getMyTurn();
// There's special processing for triggering AP Pods.
Expand Down Expand Up @@ -754,7 +754,7 @@ private void clearAttacks() {
// restore any other movement to default
ce().setSecondaryFacing(ce().getFacing());
ce().setArmsFlipped(false);
setDisengageEnabled(ce().isOffBoard() && ce().canFlee());
setDisengageEnabled(ce().isOffBoard() && ce().canFlee(ce().getPosition()));
}

/**
Expand All @@ -776,7 +776,7 @@ private void removeLastFiring() {
WeaponAttackAction waa = (WeaponAttackAction) o;
ce().getEquipment(waa.getWeaponId()).setUsedThisRound(false);
removeAttack(o);
setDisengageEnabled(attacks.isEmpty() && ce().isOffBoard() && ce().canFlee());
setDisengageEnabled(attacks.isEmpty() && ce().isOffBoard() && ce().canFlee(ce().getPosition()));
clientgui.getUnitDisplay().wPan.displayMek(ce());
clientgui.getClient().getGame().removeAction(o);
clientgui.getBoardView().refreshAttacks();
Expand Down
3 changes: 1 addition & 2 deletions megamek/src/megamek/common/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -9546,8 +9546,7 @@ public short getUnitNumber() {
* Returns whether an entity can flee from its current position. Currently
* returns true if the entity is on the edge of the board.
*/
public boolean canFlee() {
Coords pos = getPosition();
public boolean canFlee(Coords pos) {
return ((getWalkMP() > 0) || (this instanceof Infantry))
&& !isProne()
&& !isStuck()
Expand Down
2 changes: 1 addition & 1 deletion megamek/src/megamek/common/GunEmplacement.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ public boolean canCharge() {
}

@Override
public boolean canFlee() {
public boolean canFlee(Coords pos) {
return false;
}

Expand Down
4 changes: 2 additions & 2 deletions megamek/src/megamek/common/MoveStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -2214,7 +2214,7 @@ && getClearance() < prev.getClearance()) { // landing
}

// check to see if it's trying to flee and can legally do so.
if ((type == MoveStepType.FLEE) && entity.canFlee()) {
if ((type == MoveStepType.FLEE) && entity.canFlee(curPos)) {
movementType = EntityMovementType.MOVE_LEGAL;
}

Expand Down Expand Up @@ -3365,7 +3365,7 @@ public boolean isMovementPossible(Game game, Coords src, int srcEl, CachedEntity
}

// If you want to flee, and you can flee, flee.
if ((type == MoveStepType.FLEE) && entity.canFlee()) {
if ((type == MoveStepType.FLEE) && entity.canFlee(dest)) {
return true;
}

Expand Down
17 changes: 11 additions & 6 deletions megamek/src/megamek/server/totalwarfare/MovePathHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,6 @@ class MovePathHandler extends AbstractTWRuleHandler {
}

void processMovement() {
// check for fleeing
if (md.contains(MovePath.MoveStepType.FLEE)) {
addReport(gameManager.processLeaveMap(md, false, -1));
return;
}

if (md.contains(MovePath.MoveStepType.EJECT)) {
if (entity.isLargeCraft() && !entity.isCarcass()) {
r = new Report(2026);
Expand Down Expand Up @@ -1200,6 +1194,17 @@ && getGame().getOptions().booleanOption(OptionsConstants.ADVAERORULES_FUEL_CONSU
((Mek) entity).setShouldDieAtEndOfTurnBecauseOfWater(false);
}
}

// check for fleeing
if (md.contains(MovePath.MoveStepType.FLEE)) {
if (entity.canFlee(entity.getPosition())) {
addReport(gameManager.processLeaveMap(md, false, -1));
} else {
r = new Report(2017, Report.PUBLIC);
r.indent();
addReport(r);
}
}
}

/**
Expand Down
110 changes: 82 additions & 28 deletions megamek/src/megamek/server/totalwarfare/TWGameManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,29 @@ void prepareVictoryReport() {
while (retreat.hasMoreElements()) {
Entity entity = retreat.nextElement();
addReport(entity.victoryReport());

String fleeDirection;
switch (entity.getStartingPos(false)) {
case Board.START_N:
fleeDirection = "North";
break;
case Board.START_E:
fleeDirection = "Ease";
break;
case Board.START_S:
fleeDirection = "South";
break;
case Board.START_W:
fleeDirection = "West";
break;
default:
fleeDirection = "Edge";
}

r = new Report(7081, Report.PUBLIC);
r.indent();
r.add(fleeDirection);
addReport(r);
}
}
// List destroyed units
Expand Down Expand Up @@ -5283,6 +5306,50 @@ Vector<Report> processCrash(Entity entity, int vel, Coords c) {
return vReport;
}

private OffBoardDirection calculateEdge(Coords coords) {
OffBoardDirection fleeDirection;

if (coords.getY() <= 0) {
fleeDirection = OffBoardDirection.NORTH;
} else if (coords.getY() >= (getGame().getBoard().getHeight() - 1)) {
fleeDirection = OffBoardDirection.SOUTH;
} else if (coords.getX() <= 0) {
fleeDirection = OffBoardDirection.WEST;
} else {
fleeDirection = OffBoardDirection.EAST;
}

return fleeDirection;
}

private String setRetreatEdge(Entity entity, OffBoardDirection fleeDirection) {
String result;

switch (fleeDirection) {
case NORTH:
entity.setStartingPos(Board.START_N);
result = "North";
break;
case EAST:
entity.setStartingPos(Board.START_E);
result = "East";
break;
case SOUTH:
entity.setStartingPos(Board.START_S);
result = "South";
break;
case WEST:
entity.setStartingPos(Board.START_W);
result = "West";
break;
default:
entity.setStartingPos(Board.START_EDGE);
result = "Edge";
}

return result;
}

/**
* Process any flee movement actions, including flying off the map
*
Expand All @@ -5305,20 +5372,15 @@ public Vector<Report> processLeaveMap(MovePath movePath, boolean flewOff, int re
r = new Report(9370, Report.PUBLIC);
}
r.addDesc(entity);

OffBoardDirection fleeDirection = calculateEdge(movePath.getFinalCoords());
String retreatEdge = setRetreatEdge(entity, fleeDirection);
r.add(retreatEdge);
addReport(r);
OffBoardDirection fleeDirection;
if (movePath.getFinalCoords().getY() <= 0) {
fleeDirection = OffBoardDirection.NORTH;
} else if (movePath.getFinalCoords().getY() >= (getGame().getBoard().getHeight() - 1)) {
fleeDirection = OffBoardDirection.SOUTH;
} else if (movePath.getFinalCoords().getX() <= 0) {
fleeDirection = OffBoardDirection.WEST;
} else {
fleeDirection = OffBoardDirection.EAST;
}

if (returnable > -1) {
entityUpdate(entity.getId());

if (returnable > -1) {
entity.setDeployed(false);
entity.setDeployRound(1 + game.getRoundCount() + returnable);
entity.setPosition(null);
Expand All @@ -5331,23 +5393,7 @@ public Vector<Report> processLeaveMap(MovePath movePath, boolean flewOff, int re
// fly off again instantly.
((IAero) entity).setOutControl(false);
}
switch (fleeDirection) {
case WEST:
entity.setStartingPos(Board.START_W);
break;
case NORTH:
entity.setStartingPos(Board.START_N);
break;
case EAST:
entity.setStartingPos(Board.START_E);
break;
case SOUTH:
entity.setStartingPos(Board.START_S);
break;
default:
entity.setStartingPos(Board.START_EDGE);
}
entityUpdate(entity.getId());

return vReport;
} else {
ServerHelper.clearBloodStalkers(game, entity.getId(), this);
Expand Down Expand Up @@ -8620,6 +8666,7 @@ Vector<Report> doEntityDisplacement(Entity entity, Coords src,
Coords dest, PilotingRollData roll) {
Vector<Report> vPhaseReport = new Vector<>();
Report r;

if (!game.getBoard().contains(dest)) {
// set position anyway, for pushes moving through, stuff like that
entity.setPosition(dest);
Expand All @@ -8633,12 +8680,17 @@ Vector<Report> doEntityDisplacement(Entity entity, Coords src,
} else if (turnsRemoved > 0) {
send(packetHelper.createTurnListPacket());
}

OffBoardDirection fleeDirection = calculateEdge(src);
String retreatEdge = setRetreatEdge(entity, fleeDirection);

game.removeEntity(entity.getId(), IEntityRemovalConditions.REMOVE_PUSHED);
send(createRemoveEntityPacket(entity.getId(), IEntityRemovalConditions.REMOVE_PUSHED));
// entity forced from the field
r = new Report(2230);
r.subject = entity.getId();
r.addDesc(entity);
r.add(retreatEdge);
vPhaseReport.add(r);
// TODO : remove passengers and swarmers.
}
Expand Down Expand Up @@ -8777,6 +8829,7 @@ else if ((waterDepth > 0)
doSkillCheckInPlace(entity, waterRoll);
}
}

// Update the entity's position on the client.
entityUpdate(entity.getId());

Expand Down Expand Up @@ -8888,6 +8941,7 @@ else if ((waterDepth > 0)
entityUpdate(violation.getId());
}
}

return vPhaseReport;
}

Expand Down
Loading