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

lightning storm updates #5436

Merged
merged 19 commits into from
Oct 29, 2024
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
2 changes: 2 additions & 0 deletions megamek/i18n/megamek/common/options/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ GameOptionsInfo.option.hidden_units.displayableName=Hidden Units
GameOptionsInfo.option.hidden_units.description=If checked, players may deploy units hidden.
GameOptionsInfo.option.black_ice.displayableName=Black Ice
GameOptionsInfo.option.black_ice.description=If checked, Black Ice may form on pavement if temperatures are below -30C. TO:AR p38\nDoes not affect Black Ice forming due to Ice Storms. TO:AR p58
GameOptionsInfo.option.lightning_storm_targets_units.displayableName=Lightning Storm targets units
GameOptionsInfo.option.lightning_storm_targets_units.description=If unchecked, targets random hex on the board
GameOptionsInfo.option.double_blind.displayableName=TacOps Double blind
GameOptionsInfo.option.double_blind.description=If checked, enemy units will only be visible if they are in line of sight of one or more of your units, or within sensor range. \nUnchecked by default.
GameOptionsInfo.option.single_blind_bots.displayableName=(Unofficial) Default bots to single blind, when using TacOps Double blind
Expand Down
3 changes: 3 additions & 0 deletions megamek/i18n/megamek/common/report-messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,9 @@
5606=player must choose <data> bombs to be destroyed.
5607=bot loses <data> bombs.
5608=<data> (<data>) Internal Bomb Bay + Cargo critical: Damage exceeds remaining bombs; all bombs destroyed.
5620=<newline>Damage from lightning storm<newline>-------------------
5621=lightning strike in hex <data>
5622= and adjacent hex <data>

#6000's -- Damage Related
6005=\ no effect.
Expand Down
7 changes: 5 additions & 2 deletions megamek/src/megamek/common/Sensor.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,15 @@ public int adjustRange(int range, Game game, LosEffects los) {
return 0;
}

//TO:AR 6th ed. p 190
if ((type != TYPE_MEK_SEISMIC) && (type != TYPE_VEE_SEISMIC)) {
PlanetaryConditions conditions = game.getPlanetaryConditions();
if (conditions.getEMI().isEMI()) {
if (conditions.isEMI()) {
range -= 4;
}
// TODO: add lightning
if (conditions.getWeather().isLightningStorm()) {
range -= 1;
}
}

if ((type == TYPE_MEK_RADAR) || (type == TYPE_VEE_RADAR)
Expand Down
2 changes: 1 addition & 1 deletion megamek/src/megamek/common/actions/WeaponAttackAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -5560,7 +5560,7 @@ public static ToHitData processAttackerSPAs(ToHitData toHit, Entity ae, Targetab
toHit.addModifier(-1, Messages.getString("WeaponAttackAction.RainSpec"));
}

if (conditions.getWeather().isModerateRainOrHeavyRainOrGustingRainOrDownpour()) {
if (conditions.getWeather().isModerateRainOrHeavyRainOrGustingRainOrDownpourOrLightningStorm()) {
toHit.addModifier(-1, Messages.getString("WeaponAttackAction.RainSpec"));
}
}
Expand Down
1 change: 1 addition & 0 deletions megamek/src/megamek/common/options/GameOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public synchronized void initialize() {
addOption(advancedRules, OptionsConstants.ADVANCED_MINEFIELDS, false);
addOption(advancedRules, OptionsConstants.ADVANCED_HIDDEN_UNITS, true);
addOption(advancedRules, OptionsConstants.ADVANCED_BLACK_ICE, false);
addOption(advancedRules, OptionsConstants.ADVANCED_LIGHTNING_STORM_TARGETS_UNITS, false);
addOption(advancedRules, OptionsConstants.ADVANCED_DOUBLE_BLIND, false);
addOption(advancedRules, OptionsConstants.ADVANCED_TACOPS_SENSORS, false);
addOption(advancedRules, OptionsConstants.ADVANCED_SUPRESS_ALL_DB_MESSAGES, false);
Expand Down
1 change: 1 addition & 0 deletions megamek/src/megamek/common/options/OptionsConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ public class OptionsConstants {
public static final String ADVANCED_MINEFIELDS = "minefields";
public static final String ADVANCED_HIDDEN_UNITS = "hidden_units";
public static final String ADVANCED_BLACK_ICE= "black_ice";
public static final String ADVANCED_LIGHTNING_STORM_TARGETS_UNITS= "lightning_storm_targets_units";
public static final String ADVANCED_DOUBLE_BLIND = "double_blind";
public static final String ADVANCED_SINGLE_BLIND_BOTS = "single_blind_bots";
public static final String ADVANCED_TACOPS_SENSORS = "tacops_sensors";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ public int getWeatherHitPenalty(Entity en) {
if (getWeather().isLightRainOrLightSnow()
&& en.isConventionalInfantry()) {
return 1;
} else if (getWeather().isModerateRainOrHeavyRainOrGustingRainOrModerateSnowOrSnowFlurriesOrHeavySnowOrSleet()) {
} else if (getWeather().isModerateRainOrHeavyRainOrGustingRainOrModerateSnowOrSnowFlurriesOrHeavySnowOrSleetOrLightningStorm()) {
return 1;
} else if(getWeather().isDownpour()) {
return 2;
Expand Down Expand Up @@ -403,7 +403,7 @@ && getWind().isStrongerThan(Wind.STORM)) {
public int getIgniteModifiers() {
int mod = 0;

if (getWeather().isLightRainOrModerateRain() ) {
if (getWeather().isLightRainOrModerateRainOrLightningStorm() ) {
mod += 1;
}

Expand Down Expand Up @@ -449,6 +449,7 @@ public boolean putOutFire() {
case MOD_RAIN:
case MOD_SNOW:
case SNOW_FLURRIES:
case LIGHTNING_STORM:
roll = roll + 2;
break;
case HEAVY_RAIN:
Expand Down Expand Up @@ -752,7 +753,7 @@ && getWind().isStrongerThan(Wind.LIGHT_GALE)) {
} else {
otherRange = 8;
}
} else if (getWeather().isModerateRainOrModerateSnow()) {
} else if (getWeather().isModerateRainOrModerateSnowOrLightningStorm()) {
if (isMekOrVee || isLowAltitudeAero) {
otherRange = 20;
} else if (isAero) {
Expand Down Expand Up @@ -885,6 +886,7 @@ public static Wind setWindFromWeather(Weather weather, Wind wind) {
switch (weather) {
case ICE_STORM:
case SNOW_FLURRIES:
case LIGHTNING_STORM:
return Wind.MOD_GALE;
case GUSTING_RAIN:
return Wind.STRONG_GALE;
Expand Down
30 changes: 20 additions & 10 deletions megamek/src/megamek/common/planetaryconditions/Weather.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,25 +143,33 @@ public boolean isHeavyRainOrGustingRain() {
}


public boolean isLightRainOrModerateRain() {
public boolean isLightRainOrModerateRainOrLightningStorm() {
return isLightRain()
|| isModerateRain();
|| isModerateRain()
|| isLightningStorm();
}

public boolean isModerateSnowOrSnowFlurries() {
return isModerateSnow()
|| isSnowFlurries();
}

public boolean isModerateRainOrModerateSnow() {
public boolean isModerateRainOrLightningStorm() {
return isModerateRain()
|| isLightningStorm();
}

public boolean isModerateRainOrModerateSnowOrLightningStorm() {
return isModerateRain()
|| isModerateSnow();
|| isModerateSnow()
|| isLightningStorm();
}

public boolean isDownpourOrHeavySnowOrIceStorm() {
public boolean isDownpourOrHeavySnowOrIceStormOrLightningStorm() {
return isDownpour()
|| isHeavySnow()
|| isIceStorm();
|| isIceStorm()
|| isLightningStorm();
}

public boolean isSnowFlurriesOrSleetOrIceStorm() {
Expand Down Expand Up @@ -220,11 +228,12 @@ public boolean isModerateSnowOrHeavySnowOrSnowFlurriesOrSleet() {
|| isSleet();
}

public boolean isModerateRainOrHeavyRainOrGustingRainOrDownpour() {
public boolean isModerateRainOrHeavyRainOrGustingRainOrDownpourOrLightningStorm() {
return isModerateRain()
|| isHeavyRain()
|| isGustingRain()
|| isDownpour();
|| isDownpour()
|| isLightningStorm();
}

public boolean isGustingRainOrSnowFlurriesOrIceStormOrLightningStorm() {
Expand Down Expand Up @@ -260,14 +269,15 @@ public boolean isHeavyRainOrGustingRainOrDownpourOrLightSnowOrModerateSnowOrSnow
|| isSnowFlurries();
}

public boolean isModerateRainOrHeavyRainOrGustingRainOrModerateSnowOrSnowFlurriesOrHeavySnowOrSleet() {
public boolean isModerateRainOrHeavyRainOrGustingRainOrModerateSnowOrSnowFlurriesOrHeavySnowOrSleetOrLightningStorm() {
return isModerateRain()
|| isHeavyRain()
|| isGustingRain()
|| isModerateSnow()
|| isSnowFlurries()
|| isHeavySnow()
|| isSleet();
|| isSleet()
|| isLightningStorm();
}

public boolean isAnyRain() {
Expand Down
2 changes: 1 addition & 1 deletion megamek/src/megamek/common/util/BoardUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@ public static void addWeatherConditions(Board board, Weather weatherCond, Wind w
Hex hex = board.getHex(c);

//moderate rain - mud in clear hexes, depth 0 water, and dirt roads (not implemented yet)
if (weatherCond.isModerateRain()) {
if (weatherCond.isModerateRainOrLightningStorm()) {
if ((hex.terrainsPresent() == 0) || (hex.containsTerrain(Terrains.WATER) && (hex.depth() == 0))) {
hex.addTerrain(new Terrain(Terrains.MUD, 1));
if (hex.containsTerrain(Terrains.WATER)) {
Expand Down
113 changes: 112 additions & 1 deletion megamek/src/megamek/server/totalwarfare/TWGameManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -14948,6 +14948,16 @@ private PilotingRollData getKickPushPSR(Entity psrEntity, Entity attacker,
return psr;
}

void resolveWeather() {
PlanetaryConditions conditions = game.getPlanetaryConditions();
if (conditions.isBlowingSandActive()) {
addReport(resolveBlowingSandDamage());
}
if (conditions.getWeather().isLightningStorm()) {
addReport(resolveLightningStormDamage());
}
}

/**
* Each mek sinks the amount of heat appropriate to its current heat
* capacity.
Expand Down Expand Up @@ -29704,6 +29714,7 @@ public static PilotingRollData getEjectModifiers(Game game, Entity entity, int c
}
Hex targetHex = game.getBoard().getHex(targetCoords);
// Terrain modifiers should only apply if the unit is on the ground...
// TO:AR 6th ed p165
if (!entity.isSpaceborne() && !entity.isAirborne()) {
if (targetHex != null) {
if ((targetHex.terrainLevel(Terrains.WATER) > 0)
Expand Down Expand Up @@ -29741,6 +29752,7 @@ public static PilotingRollData getEjectModifiers(Game game, Entity entity, int c
// battle, but it shouldn't
// That's a fix for another day, probably when I get around to space terrain and
// 'weather'
// TO:AR 6th ed p165
if (conditions.getGravity() == 0) {
rollTarget.addModifier(3, "Zero-G");
} else if (conditions.getGravity() < 0.8) {
Expand All @@ -29752,6 +29764,7 @@ public static PilotingRollData getEjectModifiers(Game game, Entity entity, int c
// Vacuum shouldn't apply to ASF ejection since they're designed for it, but the
// rules don't specify
// High and low pressures make more sense to apply to all
// TO:AR 6th ed p165
if (conditions.getAtmosphere().isVacuum()) {
rollTarget.addModifier(3, "Vacuum");
} else if (conditions.getAtmosphere().isVeryHigh()) {
Expand All @@ -29761,11 +29774,13 @@ public static PilotingRollData getEjectModifiers(Game game, Entity entity, int c
}
}

if (conditions.getWeather().isDownpourOrHeavySnowOrIceStorm()
// TO:AR 6th ed p165
if (conditions.getWeather().isDownpourOrHeavySnowOrIceStormOrLightningStorm()
|| conditions.getWind().isStrongGale()) {
rollTarget.addModifier(2, "Bad Weather");
}

// TO:AR 6th ed p165
if (conditions.getWind().isStrongerThan(Wind.STRONG_GALE)
|| (conditions.getWeather().isHeavySnow() && conditions.getWind().isStrongGale())) {
rollTarget.addModifier(3, "Really Bad Weather");
Expand Down Expand Up @@ -31253,6 +31268,102 @@ public List<SmokeCloud> getSmokeCloudList() {
return game.getSmokeCloudList();
}

/**
* Check to see if Lightning Storm caused damage
* TO:AR 6th ed. p. 57
* */
private Vector<Report> resolveLightningStormDamage() {
Vector<Report> vFullReport = new Vector<>();
Roll rollStrike = Compute.rollD6(1);

if (rollStrike.getIntValue() > 4) {
Report.addNewline(vFullReport);
vFullReport.add(new Report(5620, Report.PUBLIC));

Roll rollNumber = Compute.rollD6(1);
int numberOfStrikes = Math.max(1, rollNumber.getIntValue() / 2);

for (int i = 0; i < numberOfStrikes; i++) {
Roll rollType = Compute.rollD6(1);
int damage;
switch (rollType.getIntValue()) {
case 1:
case 2:
case 3:
damage = 5;
break;
case 4:
case 5:
damage = 10;
break;
default:
damage = 15;
}

Coords coords;

if (game.getOptions().booleanOption(OptionsConstants.ADVANCED_LIGHTNING_STORM_TARGETS_UNITS)) {
List<Entity> entities = game.getEntitiesVector().stream()
.filter(e -> e.getPosition() != null)
.toList();
int index = Compute.randomInt(entities.size());
coords = entities.get(index).getPosition();
} else {
int x = Compute.randomInt(game.getBoard().getWidth());
int y = Compute.randomInt(game.getBoard().getHeight());
coords = new Coords(x, y);
}

Report r;
r = new Report(5621);
r.add(coords.getBoardNum());
vFullReport.add(r);

vFullReport.addAll(lightningStormDamage(coords, damage));

if (rollType.getIntValue() == 6) {
for (Coords locationAdjacent : coords.allAdjacent()) {
r = new Report(5622);
r.add(locationAdjacent.getBoardNum());
vFullReport.add(r);

vFullReport.addAll(lightningStormDamage(locationAdjacent, 5));
}
}
}
}

return vFullReport;
}

private Vector<Report> lightningStormDamage(Coords coords, int damage) {
Vector<Report> vFullReport = new Vector<>();
Vector<Report> newReports = tryClearHex(coords, damage, Entity.NONE);
vFullReport.addAll(newReports);

Building bldg = game.getBoard().getBuildingAt(coords);

if (bldg != null) {
Vector<Report> buildingReport = damageBuilding(bldg, damage, coords);
vFullReport.addAll(buildingReport);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getPosition can return null, better turn this comparison around

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, should the lightning storm disregard offboard units?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not gun emplacements?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default it only targets a on board hexes.
adding off board hexes would make hitting anything very rare, since you can have units sheets away from the main board.

if you use the option to target units, it will target off board units also if they have a posiition.

for gun emplacements I was thinking the building takes the damage. that is what it looks like it does for other damage types.

image
image

List<Entity> hitEntities = game.getEntitiesVector().stream()
.filter(e -> coords.equals(e.getPosition())
kuronekochomusuke marked this conversation as resolved.
Show resolved Hide resolved
&& !(e instanceof GunEmplacement))
.toList();

for (Entity entity : hitEntities) {
ToHitData toHit = new ToHitData();
toHit.setSideTable(ToHitData.SIDE_RANDOM);
HitData hit = entity.rollHitLocation(ToHitData.HIT_NORMAL, toHit.getSideTable());
Vector<Report> entityReport = damageEntity(entity, hit, damage);
vFullReport.addAll(entityReport);
}

return vFullReport;
}

/**
* Check to see if blowing sand caused damage to airborne VTOL/WIGEs
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,7 @@ void managePhase() {
gameManager.resetEntityPhase(phase);
gameManager.clearReports();
gameManager.resolveHeat();
PlanetaryConditions conditions = gameManager.getGame().getPlanetaryConditions();
if (conditions.isBlowingSandActive()) {
gameManager.addReport(gameManager.resolveBlowingSandDamage());
}
gameManager.resolveWeather();
gameManager.addReport(gameManager.resolveControlRolls());
gameManager.addReport(gameManager.checkForTraitors());
// write End Phase header
Expand Down