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

Fix 3917 jump vehicles with motive damage #4940

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions megamek/src/megamek/client/ui/swing/MovementDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ public void performAction() {
final Entity ce = ce();
boolean isAero = ce.isAero();
// first check if jumping is available at all
if (!isAero && !ce.isImmobile() && (ce.getJumpMP() > 0)
if (!isAero && !ce.isImmobileForJump() && (ce.getJumpMP() > 0)
&& !(ce.isStuck() && !ce.canUnstickByJumping())) {
if (gear != MovementDisplay.GEAR_JUMP) {
if (!((cmd.getLastStep() != null)
Expand Down Expand Up @@ -813,7 +813,7 @@ private void updateButtons() {

setWalkEnabled(!ce.isImmobile() && ((ce.getWalkMP() > 0) || (ce.getRunMP() > 0))
&& !ce.isStuck());
setJumpEnabled(!isAero && !ce.isImmobile() && !ce.isProne()
setJumpEnabled(!isAero && !ce.isImmobileForJump() && !ce.isProne()
// Conventional infantry also uses jump MP for VTOL and UMU MP
&& ((ce.getJumpMP() > 0) && (!ce.isConventionalInfantry() || ce.getMovementMode().isJumpInfantry()))
&& !(ce.isStuck() && !ce.canUnstickByJumping()));
Expand Down
8 changes: 7 additions & 1 deletion megamek/src/megamek/common/Compute.java
Original file line number Diff line number Diff line change
Expand Up @@ -1072,7 +1072,13 @@ public static ToHitData getImmobileMod(Targetable target, int aimingAt, AimingMo
if ((target instanceof Mech) && (aimingAt == Mech.LOC_HEAD) && aimingMode.isImmobile()) {
return new ToHitData(3, "aiming at head");
}
return new ToHitData(-4, "target immobile");
ToHitData immobileTHD = new ToHitData(-4, "target immobile");
if(target instanceof Tank) {
// An "immobilized" but jumping CV is not actually immobile for targeting purposes
// (See issue #3917)
return ((Tank)target).moved == EntityMovementType.MOVE_JUMP ? null : immobileTHD;
NickAragua marked this conversation as resolved.
Show resolved Hide resolved
}
return immobileTHD;
}
return null;
}
Expand Down
4 changes: 4 additions & 0 deletions megamek/src/megamek/common/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,10 @@ public boolean isImmobile() {
return isImmobile(true);
}

public boolean isImmobileForJump() {
return isImmobile();
}

/**
* Is this entity shut down, or if applicable is the crew unconscious?
* @param checkCrew If true, consider the fitness of the crew when determining
Expand Down
5 changes: 5 additions & 0 deletions megamek/src/megamek/common/MoveStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -3250,6 +3250,11 @@ public boolean isMovementPossible(Game game, Coords src, int srcEl, CachedEntity
return true;
}

// Motive hit has immobilized CV, but it still wants to (and can) jump: okay!
if (movementType == EntityMovementType.MOVE_JUMP && (entity instanceof Tank) && !entity.isImmobileForJump()) {
return true;
}

// super-easy, but not any more
if (entity.isImmobile() && !entity.isBracing()) {
return false;
Expand Down
17 changes: 17 additions & 0 deletions megamek/src/megamek/common/Tank.java
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,23 @@ public boolean isPermanentlyImmobilized(boolean checkCrew) {
return super.isPermanentlyImmobilized(checkCrew) || isMovementHit();
}

/**
* Per https://bg.battletech.com/forums/index.php/topic,78336.msg1869386.html#msg1869386
* CVs with working engines and Jump Jets should still have the option to jump during the movement
* phase, even if reduced to 0 MP by motive hits, or rolling 12 on the Motive System Damage table.
*/
@Override
public boolean isImmobileForJump() {
// *Can* jump unless 0 Jump MP, or 1+ Jump MP but engine is critted, or crew unconscious/dead.
boolean jumpImmobile = (
super.isImmobile(true) ||
super.isPermanentlyImmobilized(true) ||
(getJumpMP() == 0) ||
(isEngineHit())
);
return jumpImmobile;
}

@Override
public boolean hasCommandConsoleBonus() {
if (!hasWorkingMisc(MiscType.F_COMMAND_CONSOLE) || isCommanderHit() || isUsingConsoleCommander()) {
Expand Down
4 changes: 2 additions & 2 deletions megamek/src/megamek/server/GameManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -33657,7 +33657,7 @@ private Vector<Report> vehicleMotiveDamage(Tank te, int modifier, boolean noRoll
default:
break;
}
// Apply vehicle effectiveness...except for jumps.
// Apply vehicle effectiveness...except for hits from jumps.
if (game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_TACOPS_VEHICLE_EFFECTIVE)
&& !jumpDamage) {
modifier = Math.max(modifier - 1, 0);
Expand Down Expand Up @@ -33722,7 +33722,7 @@ private Vector<Report> vehicleMotiveDamage(Tank te, int modifier, boolean noRoll
// unsure how to *report* any outcomes from there. Note that these treat
// being reduced to 0 MP and being actually immobilized as the same thing,
// which for these particular purposes may or may not be the intent of
// the rules in all cases.
// the rules in all cases (for instance, motive-immobilized CVs can still jump).
// Immobile hovercraft on water sink...
if (!te.isOffBoard() && (te.getMovementMode() == EntityMovementMode.HOVER
&& (te.isMovementHitPending() || (te.getWalkMP() <= 0))
Expand Down
70 changes: 65 additions & 5 deletions megamek/unittests/megamek/common/EntityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
import java.io.File;
import java.util.ArrayList;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -73,14 +72,14 @@ public void testCalculateBattleValue() {
actual = testEntity.calculateBattleValue(true, true);
assertEquals(expected, actual);
}

@Test
public void testCalculateWeight() {
File f;
File f;
MechFileParser mfp;
Entity e;
int expectedWeight, computedWeight;

// Test 1/1
try {
f = new File("data/mechfiles/mechs/3050U/Exterminator EXT-4A.mtf");
Expand All @@ -93,4 +92,65 @@ public void testCalculateWeight() {
fail(ex.getMessage());
}
}

/**
* Verify new Tank method .isImmobilizedForJump() returns correct values in
* various states. Note: vehicles cannot lose individual Jump Jets via crits,
* so this is not tested.
*/
@Test
public void testIsImmobilizedForJump() {
File f;
MechFileParser mfp;
Entity e;

// Test 1/1
try {
f = new File("data/mechfiles/vehicles/3050U/Kanga Medium Hovertank.blk");
mfp = new MechFileParser(f);
e = mfp.getEntity();
Tank t = (Tank) e;
Crew c = t.getCrew();

// 1 Crew condition
// 1.a Killed crew should prevent jumping; live crew should allow jumping
c.setDead(true);
assertTrue(t.isImmobileForJump());
c.resetGameState();
assertFalse(t.isImmobileForJump());

// 1.b Unconscious crew should prevent jumping; conscious crew should allow jumping
c.setUnconscious(true);
assertTrue(t.isImmobileForJump());
c.resetGameState();
assertFalse(t.isImmobileForJump());

// 1.c Stunned crew should _not_ prevent jumping
t.setStunnedTurns(1);
assertFalse(t.isImmobileForJump());
t.setStunnedTurns(0);

// 2. Engine condition
// 2.a Engine hit should prevent jumping; fixing engine should enable jumping
t.engineHit();
assertTrue(t.isImmobileForJump());
t.engineFix();
assertFalse(t.isImmobileForJump());

// 2.b Shutdown should prevent jumping; restarting should enable jumping
t.setShutDown(true);
assertTrue(t.isImmobileForJump());
t.setShutDown(false);
assertFalse(t.isImmobileForJump());

// 3. Immobilization due to massive damage motive hit / reducing MP to 0 should
// _not_ prevent jumping
t.setMotiveDamage(t.getOriginalWalkMP());
assertFalse(t.isImmobileForJump());
t.setMotiveDamage(0);

} catch (Exception ex) {
fail(ex.getMessage());
}
}
}