Skip to content

Commit

Permalink
feat: added multiple GM commands, including kill, change planetary co…
Browse files Browse the repository at this point in the history
…nditions, orbital bombardment, etc.
  • Loading branch information
Scoppio committed Nov 8, 2024
1 parent f7d8a48 commit b797123
Show file tree
Hide file tree
Showing 11 changed files with 464 additions and 109 deletions.
4 changes: 4 additions & 0 deletions megamek/i18n/megamek/common/report-messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
1241=Blue Shield Field Dampener <span class='warning'>fails</span>!
1242=no effect

#1300s - Orbital Bombardment related
1300=<newline><newline><newline>An orbital bombardment hit hex <data>!!!
1301=<span class='info'>End of orbital bombardment resolution</span>

# 1500s - ammo handling related
1500=<data> (<data>) selected <data> <data>
1501= due to <span class='warning'>out of ammo!</span>
Expand Down
1 change: 1 addition & 0 deletions megamek/src/megamek/common/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,7 @@ public void setLastPhase(GamePhase lastPhase) {

/**
* @return an enumeration of all the entities in the game.
* @deprecated Use {@link #inGameTWEntities()} instead.
*/
@Deprecated
public Iterator<Entity> getEntities() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*
* @author Luana Scoppio
*/
public class ChangeOwnershipCommand extends ServerCommand {
public class ChangeOwnershipCommand extends ServerCommand implements IsGM {

private final TWGameManager gameManager;

Expand All @@ -49,8 +49,7 @@ public ChangeOwnershipCommand(Server server, TWGameManager gameManager) {
@Override
public void run(int connId, String[] args) {
try {
var currentPlayer = server.getGame().getPlayer(connId);
if (!currentPlayer.isGameMaster()) {
if (!isGM(connId)) {
server.sendServerChat(connId, "You are not a Game Master.");
return;
}
Expand All @@ -73,4 +72,8 @@ public void run(int connId, String[] args) {
}
}

@Override
public TWGameManager getGameManager() {
return gameManager;
}
}
132 changes: 58 additions & 74 deletions megamek/src/megamek/server/commands/ChangeWeatherCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,51 @@
import megamek.server.Server;
import megamek.server.totalwarfare.TWGameManager;


import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;

/**
* @author Luana Scoppio
*/
public class ChangeWeatherCommand extends ServerCommand {

private final TWGameManager gameManager;

private static final String HELP_TEXT = "GM changes (weather) planetary conditions. The parameters are optional and unordered " +
"and the effects are applied at the beginning of the next turn. The square brackets means that argument is optional. " +
"Usage format: /weather [fog=0-2] [wind=0-6] [winddir=0-6] [light=0-6] [atmo=0-5] [blowsand=0-1] [weather=0-14] " +
"light= 0: daylight, 1: dusk, 2: full moon, 3: glare, 4: moonless night, 5: solar flare, 6: pitch black " +
"fog= 0: none, 1: light, 2: heavy " +
"wind= 0: calm, 1: light gale, 2: moderate gale, 3: strong gale, 4: storm, 5: tornado F1-F3, 6: tornado F4 " +
"winddir= 0: south, 1: southwest, 2: northwest, 3: north, 4: northeast, 5: southeast, 6: random " +
"atmo= 0: vacuum, 1: trace, 2: thin, 3: standard, 4: high, 5: very high " +
"blowsand= 0: no, 1: yes " +
"weather= 0: clear, 1: light rain, 2: moderate rain, 3: heavy rain, 4: gusting rain, 5: downpour, 6: light snow " +
"7: moderate snow, 8: snow flurries, 9: heavy snow, 10: sleet, 11: ice storm, 12: light hail, 13: heavy hail " +
"14: lightning storm";

/** Creates new ChangeWeatherCommand */
public ChangeWeatherCommand(Server server, TWGameManager gameManager) {
super(server, "weather", "GM changes (weather) planetary conditions. The parameters are optional and unordered " +
"and the effects are applied at the beggining of the next turn. The square brackets means that argument is optional. " +
"Usage format: /weather [fog=0-2] [wind=0-6] [winddir=0-6] [light=0-6] [atmo=0-5] [blowsand=0-1] [weather=0-14] " +
"light= 0: daylight, 1: dusk, 2: full moon, 3: glare, 4: moonless night, 5: solar flare, 6: pitch black " +
"fog= 0: none, 1: light, 2: heavy " +
"wind= 0: calm, 1: light gale, 2: moderate gale, 3: strong gale, 4: storm, 5: tornado F1-F3, 6: tornado F4 " +
"winddir= 0: south, 1: southwest, 2: northwest, 3: north, 4: northeast, 5: southeast, 6: random " +
"atmo= 0: vacuum, 1: trace, 2: thin, 3: standard, 4: high, 5: very high " +
"blowsand= 0: no, 1: yes " +
"weather= 0: clear, 1: light rain, 2: moderate rain, 3: heavy rain, 4: gusting rain, 5: downpour, 6: light snow " +
"7: moderate snow, 8: snow flurries, 9: heavy snow, 10: sleet, 11: ice storm, 12: light hail, 13: heavy hail " +
"14: lightning storm");
super(server, "weather", HELP_TEXT);
this.gameManager = gameManager;
}

private void updatePlanetaryCondition(String arg, String prefix, int connId, int maxLength, Consumer<Integer> setter,
Function<Integer, String> successMessage, Function<Integer, String> errorMessage) {
var value = Integer.parseInt(arg.substring(prefix.length()));

Check notice

Code scanning / CodeQL

Missing catch of NumberFormatException Note

Potential uncaught 'java.lang.NumberFormatException'.
if (value >= 0 && value < maxLength) {
setter.accept(value);
server.sendServerChat(connId, successMessage.apply(value));
} else {
server.sendServerChat(connId, errorMessage.apply(maxLength));
}
}

private record Condition(int maxLength, Consumer<Integer> setter, Function<Integer, String> successMessage, Function<Integer, String> errorMessage) {}

/**
* Run this command with the arguments supplied
*/
Expand All @@ -56,75 +77,38 @@ public void run(int connId, String[] args) {
return;
}

// Check argument integrity.
var planetaryConditions = gameManager.getGame().getPlanetaryConditions();

if (args.length > 1) {
// Check command
var planetaryConditions = gameManager.getGame().getPlanetaryConditions();

for (var arg : args) {
if (arg.startsWith("fog=")) {
var fog = Integer.parseInt(arg.substring(4));
if (fog >= 0 && fog < Fog.values().length) {
planetaryConditions.setFog(Fog.values()[fog]);
server.sendServerChat(connId, "The fog has changed.");
} else {
server.sendServerChat(connId, "Invalid fog value. Must be between 0 and " + Fog.values().length);
}
} else if (arg.startsWith("wind=")) {
var wind = Integer.parseInt(arg.substring(5));
if (wind >= 0 && wind < Wind.values().length) {
planetaryConditions.setWind(Wind.values()[wind]);
server.sendServerChat(connId, "The wind strength has changed.");
} else {
server.sendServerChat(connId, "Invalid wind value. Must be between 0 and " + Wind.values().length);
}
} else if (arg.startsWith("winddir=")) {
var windDir = Integer.parseInt(arg.substring(8));
if (windDir >= 0 && windDir < WindDirection.values().length) {
planetaryConditions.setWindDirection(WindDirection.values()[windDir]);
server.sendServerChat(connId, "The wind direction has changed.");
} else {
server.sendServerChat(connId, "Invalid wind direction value. Must be between 0 and " + WindDirection.values().length);
}
} else if (arg.startsWith("light=")) {
var light = Integer.parseInt(arg.substring(6));
if (light >= 0 && light < Light.values().length) {
planetaryConditions.setLight(Light.values()[light]);
server.sendServerChat(connId, "The light has changed.");
} else {
server.sendServerChat(connId, "Invalid light value. Must be between 0 and " + Light.values().length);
}
} else if (arg.startsWith("atmo=")) {
var atmo = Integer.parseInt(arg.substring(5));
if (atmo >= 0 && atmo < Atmosphere.values().length) {
planetaryConditions.setAtmosphere(Atmosphere.values()[atmo]);
server.sendServerChat(connId, atmo == 0 ? "The air has vanished, put your vac suits!" : "The air is changing.");
} else {
server.sendServerChat(connId, "Invalid atmosphere value. Must be between 0 and " + Atmosphere.values().length);
}
} else if (arg.startsWith("blowsand=")) {
var blowSand = Integer.parseInt(arg.substring(9));
if (blowSand >= 0 && blowSand < BlowingSand.values().length) {
planetaryConditions.setBlowingSand(BlowingSand.values()[blowSand]);
server.sendServerChat(connId, blowSand == 1 ? "Sand started blowing." : "The sand has settled.");
} else {
server.sendServerChat(connId, "Invalid blowsand value. Must be between 0 and " + BlowingSand.values().length);
}
} else if (arg.startsWith("weather=")) {
var weather = Integer.parseInt(arg.substring(8));
if (weather >= 0 && weather < Weather.values().length) {
planetaryConditions.setWeather(Weather.values()[weather]);
server.sendServerChat(connId, "The weather has changed.");
} else {
server.sendServerChat(connId, "Invalid weather value. Must be between 0 and " + Weather.values().length);
Map<String, Condition> conditions = Map.of(
"fog=", new Condition(Fog.values().length, value -> planetaryConditions.setFog(Fog.values()[value]),
value -> "The fog has changed.", maxLength -> "Invalid fog value. Must be between 0 and " + (maxLength - 1)),
"wind=", new Condition(Wind.values().length, value -> planetaryConditions.setWind(Wind.values()[value]),
value -> "The wind strength has changed.", maxLength -> "Invalid wind value. Must be between 0 and " + (maxLength - 1)),
"winddir=", new Condition(WindDirection.values().length, value -> planetaryConditions.setWindDirection(WindDirection.values()[value]),
value -> "The wind direction has changed.", maxLength -> "Invalid wind direction value. Must be between 0 and " + (maxLength - 1)),
"light=", new Condition(Light.values().length, value -> planetaryConditions.setLight(Light.values()[value]),
value -> "The light has changed.", maxLength -> "Invalid light value. Must be between 0 and " + (maxLength - 1)),
"atmo=", new Condition(Atmosphere.values().length, value -> planetaryConditions.setAtmosphere(Atmosphere.values()[value]),
value -> value == 0 ? "The air has vanished, put your vac suits!" : "The air is changing.", maxLength -> "Invalid atmosphere value. Must be between 0 and " + (maxLength - 1)),
"blowsand=", new Condition(BlowingSand.values().length, value -> planetaryConditions.setBlowingSand(BlowingSand.values()[value]),
value -> value == 1 ? "Sand started blowing." : "The sand has settled.", maxLength -> "Invalid blowsand value. Must be between 0 and " + (maxLength - 1)),
"weather=", new Condition(Weather.values().length, value -> planetaryConditions.setWeather(Weather.values()[value]),
value -> "The weather has changed.", maxLength -> "Invalid weather value. Must be between 0 and " + (maxLength - 1))
);

Stream.of(args)
.forEach(arg -> conditions.forEach((prefix, condition) -> {
if (arg.startsWith(prefix)) {
updatePlanetaryCondition(arg, prefix, connId, condition.maxLength, condition.setter, condition.successMessage, condition.errorMessage);
}
}
}
}));

gameManager.getGame().setPlanetaryConditions(planetaryConditions);
} else {
// Error out; it's not a valid call.
server.sendServerChat(connId, "weather command failed.");
server.sendServerChat(connId, "weather command failed. " + HELP_TEXT);
}
}
}
13 changes: 13 additions & 0 deletions megamek/src/megamek/server/commands/IsGM.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package megamek.server.commands;

import megamek.server.totalwarfare.TWGameManager;

public interface IsGM {

TWGameManager getGameManager();

default boolean isGM(int connId) {
return getGameManager().getGame().getPlayer(connId).getGameMaster();
}

}
9 changes: 7 additions & 2 deletions megamek/src/megamek/server/commands/KillCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/**
* @author Luana Scoppio
*/
public class KillCommand extends ServerCommand {
public class KillCommand extends ServerCommand implements IsGM {

private final TWGameManager gameManager;

Expand All @@ -49,7 +49,7 @@ public void run(int connId, String[] args) {
server.sendServerChat(connId, "Command-line kill is not enabled in this game.");
return;
}
if (!server.getPlayer(connId).getGameMaster()) {
if (!isGM(connId)) {
server.sendServerChat(connId, "You are not a Game Master.");
return;
}
Expand All @@ -74,4 +74,9 @@ public void run(int connId, String[] args) {
server.sendServerChat(connId, "Kill command failed (1). Proper format is \"/kill <id>\" where id is the units ID");
}
}

@Override
public TWGameManager getGameManager() {
return gameManager;
}
}
103 changes: 103 additions & 0 deletions megamek/src/megamek/server/commands/OrbitalBombardmentCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MegaMek.
*
* MegaMek is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MegaMek is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MegaMek. If not, see <http://www.gnu.org/licenses/>.
*/
package megamek.server.commands;

import megamek.common.options.OptionsConstants;
import megamek.server.Server;
import megamek.server.props.OrbitalBombardment;
import megamek.server.totalwarfare.TWGameManager;

/**
* @author Luana Scoppio
*/
public class OrbitalBombardmentCommand extends ServerCommand implements IsGM {

private final TWGameManager gameManager;

/** Creates new NukeCommand */
public OrbitalBombardmentCommand(Server server, TWGameManager gameManager) {
super(server, "ob", "GM Drops a bomb onto the board doing of 100 damage with a 3 hex radius, to be exploded at" +
"the end of the next weapons attack phase." +
"Allowed formats:"+
"/bomb <x> <y> and" +
"/bomb <x> <y> [factor=10] [radius=4]" +
"the damage at impact point is 10 times the factor, default is 10. " +
"and hex x, y is x=column number and y=row number (hex 0923 would be x=9 and y=23), the explosion blast radius default " +
"is equal to 4, it automatically applies a linear damage dropoff each hex away from the center." +
" All parameters in square brackets may be ommited. " +
" Example: /ob 10 10 factor=12 ");
this.gameManager = gameManager;
}

/**
* Run this command with the arguments supplied
*/
@Override
public void run(int connId, String[] args) {

// Check to make sure nuking is allowed by game options!
if (!isGM(connId)) {
server.sendServerChat(connId, "You are not a Game Master.");
return;
}

if (args.length >= 3) {
var orbitalBombardmentBuilder = new OrbitalBombardment.Builder();
try {
int[] position = new int[2];
for (int i = 1; i < 3; i++) {
position[i-1] = Integer.parseInt(args[i]) - 1;
}
// is the hex on the board?
if (!gameManager.getGame().getBoard().contains(position[0], position[1])) {
server.sendServerChat(connId, "Specified hex is not on the board.");
return;
}

orbitalBombardmentBuilder
.x(position[0])
.y(position[1]);

if (args.length > 3) {
for (int i = 3; i < args.length; i++) {
String[] keyValue = args[i].split("=");
if (keyValue[0].equals("factor")) {
orbitalBombardmentBuilder.damageFactor(Integer.parseInt(keyValue[1]));
} else if (keyValue[0].equals("radius")) {
orbitalBombardmentBuilder.radius(Integer.parseInt(keyValue[1]));
}
}
}

gameManager.addScheduledOrbitalBombardment(orbitalBombardmentBuilder.build());
server.sendServerChat(connId, "This isn't a shooting star! Take cover!");
} catch (Exception e) {
server.sendServerChat(connId, "Orbital bombardment command failed (2). Proper format is \"/ob <x> <y> [factor=10] [radius=4]\" where hex x, y is x=column number and y=row number (hex 0923 would be x=9 and y=23)");
}
} else {
// Error out; it's not a valid call.
server.sendServerChat(connId, "Orbital bombardment command failed (1). Proper format is \"/ob <x> <y> [factor=10] [radius=4]\" where hex x, y is x=column number and y=row number (hex 0923 would be x=9 and y=23)");
}
}

@Override
public TWGameManager getGameManager() {
return gameManager;
}
}
9 changes: 7 additions & 2 deletions megamek/src/megamek/server/commands/RemoveSmokeCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
/**
* @author Luana Scoppio
*/
public class RemoveSmokeCommand extends ServerCommand {
public class RemoveSmokeCommand extends ServerCommand implements IsGM {

private final TWGameManager gameManager;

Expand All @@ -42,7 +42,7 @@ public RemoveSmokeCommand(Server server, TWGameManager gameManager) {
*/
@Override
public void run(int connId, String[] args) {
if (!server.getPlayer(connId).getGameMaster()) {
if (!isGM(connId)) {
server.sendServerChat(connId, "You are not a Game Master.");
return;
}
Expand All @@ -57,4 +57,9 @@ public void run(int connId, String[] args) {
server.sendServerChat(connId, "nosmoke command failed (1).");
}
}

@Override
public TWGameManager getGameManager() {
return gameManager;
}
}
Loading

0 comments on commit b797123

Please sign in to comment.