Skip to content

Commit

Permalink
Merge pull request MegaMek#5138 from Sleet01/Fix_4952_Princess_does_n…
Browse files Browse the repository at this point in the history
…ot_select_alt_ammo

Fix 4952: Princess does not select alt ammo
  • Loading branch information
HammerGS authored Feb 16, 2024
2 parents e4c6b2a + 4dab296 commit f940340
Show file tree
Hide file tree
Showing 44 changed files with 2,073 additions and 684 deletions.
15 changes: 14 additions & 1 deletion megamek/i18n/megamek/common/report-messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@
1241=Blue Shield Field Dampener <font color='C00000'>fails</font>!
1242=no effect

# 1500s - ammo handling related
1500=<data> (<data>) selected <data> <data>
1501= due to <font color='C00000'>out of ammo!</font>
1502= due to better to-hit chance.
1503= due to better damage potential.
1504= due to special effects.
1505= due to expectation of TAG support.
1506= due to having no better option.
1507= randomly.
1508= from attached ammo carrier.
1509= to maximise critical chances.
1510= to maximise heat on target.
1511= to maximise range.

2000=<newline><B>Movement Phase</B><newline>-------------------
2005=<newline><data> (<data>) flees the battlefield.
Expand Down Expand Up @@ -728,7 +741,7 @@
5560=<data> takes over as <data> of <data> (<data>).
5600=<newline>Bomb Bay Explosions<newline>-------------------
5601=<data> (<data>) took ground fire damage while dropping internal bay bombs.
5602=<data> (<data>) must roll under <data>+ to avoid bomb bay explosion, rolls <data>.
5602=<data> (<data>) Checking for bomb bay explosion on <data>+, rolls <data>.
5603=\ <font color='C00000'><data> (<data>) takes <data> damage when <data> remaining bombs explode!</font>
5604=\ <font color='008000'><data> (<data>) avoids bomb bay explosion; <data> internal bombs remaining.</font>
5605=\ <font color='C00000'><data> (<data>) suffers a critical Cargo hit; <msg:5606,5607></font>
Expand Down
4 changes: 2 additions & 2 deletions megamek/src/megamek/client/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,8 @@ public void sendSystemModeChange(int nEntity, int nSystem, int nMode) {
/**
* Send mode-change data to the server
*/
public void sendAmmoChange(int nEntity, int nWeapon, int nAmmo) {
send(new Packet(PacketCommand.ENTITY_AMMOCHANGE, nEntity, nWeapon, nAmmo));
public void sendAmmoChange(int nEntity, int nWeapon, int nAmmo, int reason) {
send(new Packet(PacketCommand.ENTITY_AMMOCHANGE, nEntity, nWeapon, nAmmo, reason));
}

/**
Expand Down
1 change: 1 addition & 0 deletions megamek/src/megamek/client/bot/BotClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ private synchronized boolean calculateMyTurnWorker() {
sendArtyAutoHitHexes(autoHitHexes);
} else if (game.getPhase().isTargeting() || game.getPhase().isOffboard()) {
// Princess implements arty targeting
// TODO: TAG should be handled separately.
calculateTargetingOffBoardTurn();
} else if (game.getPhase().isPremovement() || game.getPhase().isPrefiring()) {
calculatePrephaseTurn();
Expand Down
161 changes: 121 additions & 40 deletions megamek/src/megamek/client/bot/princess/AeroPathUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,46 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import megamek.common.IAero;
import megamek.common.MovePath;
import megamek.common.UnitType;
import megamek.client.commands.ClientCommand;
import megamek.common.*;
import megamek.common.MovePath.MoveStepType;
import org.apache.logging.log4j.LogManager;

/**
* Helper class that contains functionality relating mostly to aero unit paths.
* @author NickAragua
*/
public class AeroPathUtil {
public static List<List<MoveStepType>> TURNS;

static {
// put together a pre-defined array of turns. Indexes correspond to the directional values found in Coords.java
TURNS = new ArrayList<>();
TURNS.add(new ArrayList<>()); // "no turns"

TURNS.add(new ArrayList<>());
TURNS.get(1).add(MoveStepType.TURN_RIGHT);

TURNS.add(new ArrayList<>());
TURNS.get(2).add(MoveStepType.TURN_RIGHT);
TURNS.get(2).add(MoveStepType.TURN_RIGHT);

TURNS.add(new ArrayList<>());
TURNS.get(3).add(MoveStepType.TURN_RIGHT);
TURNS.get(3).add(MoveStepType.TURN_RIGHT);
TURNS.get(3).add(MoveStepType.TURN_RIGHT);

TURNS.add(new ArrayList<>());
TURNS.get(4).add(MoveStepType.TURN_LEFT);
TURNS.get(4).add(MoveStepType.TURN_LEFT);

TURNS.add(new ArrayList<>());
TURNS.get(5).add(MoveStepType.TURN_LEFT);
}

/**
* Determines if the aircraft undertaking the given path will stall at the end of the turn.
* Only relevant for aerodyne units
Expand All @@ -63,13 +64,13 @@ public static boolean willStall(MovePath movePath) {
if ((movePath.getFinalVelocity() == 0) && isAirborne && !isSpheroid) {
return true;
}
if (isSpheroid && (movePath.getFinalNDown() == 0)

if (isSpheroid && (movePath.getFinalNDown() == 0)
&& (movePath.getMpUsed() == 0)
&& !movePath.contains(MoveStepType.VLAND)) {
return true;
}

return false;
}

Expand Down Expand Up @@ -110,45 +111,45 @@ public static boolean isSafePathOffBoard(MovePath movePath) {
*/
public static Collection<MovePath> generateValidAccelerations(MovePath startingPath, int lowerBound, int upperBound) {
Collection<MovePath> paths = new ArrayList<>();

// sanity check: if we've already done something else with the path, there's no acceleration to be done
if (startingPath.length() > 0) {
return paths;
}

int currentVelocity = startingPath.getFinalVelocity();

// we go from the lower bound to the current velocity and generate paths with the required number of DECs to get to
// the desired velocity
for (int desiredVelocity = lowerBound; desiredVelocity < currentVelocity; desiredVelocity++) {
MovePath path = startingPath.clone();
for (int deltaVelocity = 0; deltaVelocity < currentVelocity - desiredVelocity; deltaVelocity++) {
path.addStep(MoveStepType.DEC);
}

paths.add(path);
}

// If the unaltered starting path is within acceptable velocity bounds, it's also a valid "acceleration".
if (startingPath.getFinalVelocity() <= upperBound &&
startingPath.getFinalVelocity() >= lowerBound) {
paths.add(startingPath.clone());
}

// we go from the current velocity to the upper bound and generate paths with the required number of DECs to get to
// the desired velocity
for (int desiredVelocity = currentVelocity; desiredVelocity < upperBound; desiredVelocity++) {
MovePath path = startingPath.clone();
for (int deltaVelocity = 0; deltaVelocity < upperBound - desiredVelocity; deltaVelocity++) {
path.addStep(MoveStepType.ACC);
}

paths.add(path);
}

return paths;
}

/**
* Helper function to calculate the maximum thrust we should use for a particular aircraft
* We limit ourselves to the lowest of "safe thrust" and "structural integrity", as anything further is unsafe, meaning it requires a PSR.
Expand All @@ -159,83 +160,163 @@ public static int calculateMaxSafeThrust(IAero aero) {
int maxThrust = Math.min(aero.getCurrentThrust(), aero.getSI()); // we should only thrust up to our SI
return maxThrust;
}

/**
* Given a move path, generate all possible increases and decreases in elevation.
* @param path The move path to process.
* @return Collection of generated paths.
*/
public static List<MovePath> generateValidAltitudeChanges(MovePath path) {
List<MovePath> paths = new ArrayList<>();

// clone path add UP
// if path uses more MP than entity has available or altitude higher than 10, stop
for (int altChange = 0; ; altChange++) {
int altChangeCost = altChange * 2;

// if we are going to attempt to change altitude but won't actually be able to, break out.
if ((path.getFinalAltitude() + altChange > 10) ||
path.getMpUsed() + altChangeCost > path.getEntity().getRunMP()) {
break;
}

MovePath childPath = path.clone();

for (int numSteps = 0; numSteps < altChange; numSteps++) {
childPath.addStep(MoveStepType.UP);
}

if ((childPath.getFinalAltitude() > 10) ||
childPath.getMpUsed() > path.getEntity().getRunMP()) {
break;
}

paths.add(childPath);
}

// clone path add DOWN
// if the path is already at minimum altitude, skip this
// if path uses more MP than entity has available or altitude lower than 1, stop
if (path.getFinalAltitude() > 1) {
for (int altChange = 1; ; altChange++) {
MovePath childPath = path.clone();

for (int numSteps = 0; numSteps < altChange; numSteps++) {
childPath.addStep(MoveStepType.DOWN);
}
// going down doesn't use MP, but if we drop down more than 2 altitude it causes a massive

// going down doesn't use MP, but if we drop down more than 2 altitude it causes a massive
// difficulty PSR, which is just not worth it.
if ((childPath.getFinalAltitude() < 1) ||
childPath.length() > 2) {
break;
}

paths.add(childPath);
}
}

return paths;
}

/**
* Given a move path, generates all possible rotations from it,
* without any regard to legality. Mostly because it's intended for spheroid
* without any regard to legality. Mostly because it's intended for spheroid
* dropships in atmosphere, which can rotate as much as they want.
*/
public static List<MovePath> generateValidRotations(MovePath path) {
List<MovePath> childPaths = new ArrayList<>();

for (int x = 1; x < TURNS.size(); x++) {
MovePath childPath = path.clone();

for (MoveStepType turn : TURNS.get(x)) {
childPath.addStep(turn);
}

childPaths.add(childPath);
}

return childPaths;
}

public static int getSpheroidDir(Game game, Entity mover) {
boolean debug = LogManager.getLogger().isDebugEnabled();
final StringBuilder msg = (debug)
? new StringBuilder("Deciding where to point ")
.append(mover.getDisplayName()).append("...")
: null;

// Face the center of the board
int dir = mover.getPosition().direction(game.getBoard().getCenter());
if (debug) {
msg.append("\nMap center is to the ").append(ClientCommand.getDirection(dir));
}

int enemyDir = dir;

// Get all enemies, find centroid, face that.
final Coords centroid;
ArrayList<Entity> enemies = new ArrayList<>();
Iterator<Entity> eIt = game.getAllEnemyEntities(mover);
while (eIt.hasNext()) {
enemies.add(eIt.next());
}
if (!enemies.isEmpty()) {
// Calc center of allies _of the enemy_
centroid = PathRanker.calcAllyCenter(enemies.get(0).getId(), enemies, game);
enemyDir = mover.getPosition().direction(centroid);

if (debug) {
msg.append("\nEnemies are over in ").append(ClientCommand.getDirection(enemyDir));
}
}

// Then determine if we need to protect part of the ship
if (mover.getDamageLevel() == Entity.DMG_NONE) {
dir = enemyDir;
if (debug) {
msg.append("\nBeing hale and hearty, we will aim toward the ")
.append(ClientCommand.getDirection(dir));
}
} else {
int leastArmor = 9999999;
int leastLoc = Dropship.LOC_NONE;
for (int i = Dropship.LOC_NOSE; i<= Dropship.LOC_FUSELAGE; i++) {
if (mover.getArmor(i) < leastArmor) {
leastArmor = mover.getArmor(i);
leastLoc = i;
}
}
if (leastLoc != Dropship.LOC_NONE) {
// Turn away, turn away, turn away, from enemy!
switch (leastLoc) {
case Dropship.LOC_NOSE:
// Turn 180, face away from enemy.
dir = (enemyDir + 3) % 6;
case Dropship.LOC_LWING:
// Turn left two hex sides.
dir = (enemyDir + 4) % 6;
case Dropship.LOC_RWING:
// Turn rigth two hex sides.
dir = (enemyDir + 2) % 6;
case Dropship.LOC_AFT:
case Dropship.LOC_WINGS:
case Dropship.LOC_FUSELAGE:
// Default is nose to enemy. If this should be different, suggest using NOSE case.
dir = enemyDir;
}
if (debug) {
msg.append("\nWe've taken a lot of damage to our ").append(mover.getLocationAbbr(leastLoc));
msg.append("\nTurning to the ").append(ClientCommand.getDirection(dir))
.append(" to protect ourselves!");
}
}
}

if (debug) {
LogManager.getLogger().debug(msg.toString());
}
return dir;
}
}
Loading

0 comments on commit f940340

Please sign in to comment.