From 5f3570d9b46855432f6c4681144687aaf5fec1a6 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 20 Aug 2024 09:04:37 +0200 Subject: [PATCH 01/14] add parsing of princess settings for scenarios --- megamek/docs/Scenarios/ScenarioV2 HowTo.mms | 35 ++++++ .../megamek/client/ui/swing/MegaMekGUI.java | 11 +- .../common/jacksonadapters/BotParser.java | 100 ++++++++++++++++++ .../src/megamek/common/scenario/Scenario.java | 9 ++ .../megamek/common/scenario/ScenarioV2.java | 12 +++ 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 megamek/src/megamek/common/jacksonadapters/BotParser.java diff --git a/megamek/docs/Scenarios/ScenarioV2 HowTo.mms b/megamek/docs/Scenarios/ScenarioV2 HowTo.mms index 9f753f46eac..a29520a90b9 100644 --- a/megamek/docs/Scenarios/ScenarioV2 HowTo.mms +++ b/megamek/docs/Scenarios/ScenarioV2 HowTo.mms @@ -291,6 +291,36 @@ factions: - name: "Player B" home: "E" + + # Bots can be given specific settings + bot: + # Optional: currently the only type is princess + type: princess + # Princess settings always are 0...10, see BehaviorSettings.java + # Optional: how worried about enemy damage am I? + selfpreservation: 5 + # Optional: how much do I want to avoid failed Piloting Rolls? + fallshame: 5 + # Optional: how close to I want to get to my enemies? + hyperaggression: 5 + # Optional: how close do I want to stick to my teammates? + herdmentality: 5 + # Optional: how quickly will I try to escape once damaged? + bravery: 5 + # Optional: which edge am I trying to reach? This is used for the "flee" status + destination: none + # Optional: to which edge will my units flee when crippled? + retreat: nearest + + # Optional: flee = will try to reach the destinationEdge even when not crippled + # forcedwithdrawal = follow the Forced Withdrawal rules + status: [ flee, forcedwithdrawal ] + + # private boolean goHome = false; // Should I immediately proceed to my home board edge? + # private final Set strategicBuildingTargets = new HashSet<>(); // What (besides enemy units) do I want to blow up? + # private final Set priorityUnitTargets = new HashSet<>(); // What units do I especially want to blow up? + + units: - fullname: Schrek PPC Carrier type: TW_UNIT @@ -316,6 +346,11 @@ factions: piloting: 4 gunnery: 3 +events: + - type: princesssettings + destination: south + status: flee + # ############################################### diff --git a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java index 05cb90186eb..d7169d53ed7 100644 --- a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java +++ b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java @@ -24,6 +24,7 @@ import megamek.client.IClient; import megamek.client.SBFClient; import megamek.client.bot.BotClient; +import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.bot.princess.Princess; import megamek.client.bot.ui.swing.BotGUI; import megamek.client.ui.Messages; @@ -46,6 +47,7 @@ import megamek.codeUtilities.StringUtility; import megamek.common.*; import megamek.common.annotations.Nullable; +import megamek.common.jacksonadapters.BotParser; import megamek.common.options.IBasicOption; import megamek.common.options.IOption; import megamek.common.preference.IPreferenceChangeListener; @@ -53,6 +55,7 @@ import megamek.common.preference.PreferenceManager; import megamek.common.scenario.Scenario; import megamek.common.scenario.ScenarioLoader; +import megamek.common.util.AddBotUtil; import megamek.server.sbf.SBFGameManager; import megamek.common.util.EmailService; import megamek.common.util.ImageUtil; @@ -855,8 +858,12 @@ void scenario() { if (scenario.getGameType() == GameType.TW) { for (int x = 0; x < pa.length; x++) { if (playerTypes[x] == ScenarioDialog.T_BOT) { - LogManager.getLogger().info("Adding bot " + pa[x].getName() + " as Princess"); - BotClient c = new Princess(pa[x].getName(), MMConstants.LOCALHOST, port); + LogManager.getLogger().info("Adding bot {} as Princess", pa[x].getName()); + Princess c = new Princess(pa[x].getName(), MMConstants.LOCALHOST, port); + if (scenario.hasBotInfo(pa[x].getName()) + && scenario.getBotInfo(pa[x].getName()) instanceof BotParser.PrincessRecord princessRecord) { + c.setBehaviorSettings(princessRecord.behaviorSettings()); + } c.getGame().addGameListener(new BotGUI(frame, c)); c.connect(); } diff --git a/megamek/src/megamek/common/jacksonadapters/BotParser.java b/megamek/src/megamek/common/jacksonadapters/BotParser.java new file mode 100644 index 00000000000..83a84f3405b --- /dev/null +++ b/megamek/src/megamek/common/jacksonadapters/BotParser.java @@ -0,0 +1,100 @@ +/* + * 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 . + */ +package megamek.common.jacksonadapters; + +import com.fasterxml.jackson.databind.JsonNode; +import megamek.client.bot.princess.BehaviorSettings; +import megamek.client.bot.princess.BehaviorSettingsFactory; +import megamek.client.bot.princess.CardinalEdge; +import megamek.client.ui.swing.ScenarioDialog; + +import java.util.List; + +public final class BotParser { + + public interface BotInfo { + int type(); + } + + public record PrincessRecord(BehaviorSettings behaviorSettings) implements BotInfo { + + @Override + public int type() { + return ScenarioDialog.T_BOT; + } + } + + private static final String TYPE = "type"; + private static final String BOT_PRINCESS = "princess"; + private static final String PRINCESS_SELF_PRESERVATION = "selfpreservation"; + private static final String PRINCESS_FALL_SHAME = "fallshame"; + private static final String PRINCESS_AGGRESSION = "hyperaggression"; + private static final String PRINCESS_HERDING = "herdmentality"; + private static final String PRINCESS_BRAVERY = "bravery"; + private static final String PRINCESS_DESTINATION = "destination"; + private static final String PRINCESS_RETREAT = "retreat"; + private static final String PRINCESS_FLEE = "flee"; + private static final String PRINCESS_FORCED_WITHDRAW = "forcedwithdraw"; + private static final String STATUS = "status"; + + public static BotInfo parse(JsonNode node) { + if (!node.has(TYPE) || node.get(TYPE).asText().equals(BOT_PRINCESS)) { + BehaviorSettings behavior = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; + assert behavior != null; + if (node.has(PRINCESS_SELF_PRESERVATION)) { + behavior.setSelfPreservationIndex(node.get(PRINCESS_SELF_PRESERVATION).asInt(5)); + } + if (node.has(PRINCESS_FALL_SHAME)) { + behavior.setFallShameIndex(node.get(PRINCESS_FALL_SHAME).asInt(5)); + } + if (node.has(PRINCESS_AGGRESSION)) { + behavior.setHyperAggressionIndex(node.get(PRINCESS_AGGRESSION).asInt(5)); + } + if (node.has(PRINCESS_HERDING)) { + behavior.setHerdMentalityIndex(node.get(PRINCESS_HERDING).asInt(5)); + } + if (node.has(PRINCESS_BRAVERY)) { + behavior.setBraveryIndex(node.get(PRINCESS_BRAVERY).asInt(5)); + } + if (node.has(PRINCESS_DESTINATION)) { + CardinalEdge edge = CardinalEdge.parseFromString( + node.get(PRINCESS_DESTINATION).asText("NONE").toUpperCase()); + behavior.setDestinationEdge(edge); + } + if (node.has(PRINCESS_RETREAT)) { + CardinalEdge edge = CardinalEdge.parseFromString( + node.get(PRINCESS_RETREAT).asText("NONE").toUpperCase()); + behavior.setRetreatEdge(edge); + } + List statusStrings = TriggerDeserializer.parseArrayOrSingleNode(node.get(STATUS), + PRINCESS_FLEE, PRINCESS_FORCED_WITHDRAW); + if (statusStrings.contains(PRINCESS_FLEE)) { + behavior.setAutoFlee(true); + } + if (statusStrings.contains(PRINCESS_FORCED_WITHDRAW)) { + behavior.setForcedWithdrawal(true); + } + return new PrincessRecord(behavior); + } else { + throw new IllegalArgumentException("Invalid bot type"); + } + } + + private BotParser() { } +} diff --git a/megamek/src/megamek/common/scenario/Scenario.java b/megamek/src/megamek/common/scenario/Scenario.java index 9ea4e685c75..0a64638e10c 100644 --- a/megamek/src/megamek/common/scenario/Scenario.java +++ b/megamek/src/megamek/common/scenario/Scenario.java @@ -20,6 +20,7 @@ import megamek.common.GameType; import megamek.common.IGame; +import megamek.common.jacksonadapters.BotParser; import megamek.server.IGameManager; import java.io.IOException; @@ -109,4 +110,12 @@ default int findIndex(String[] sa, String s) { } return -1; } + + default boolean hasBotInfo(String playerName) { + return getBotInfo(playerName) != null; + } + + default BotParser.BotInfo getBotInfo(String playerName) { + return null; + } } \ No newline at end of file diff --git a/megamek/src/megamek/common/scenario/ScenarioV2.java b/megamek/src/megamek/common/scenario/ScenarioV2.java index 5365171a913..b1ed432093e 100644 --- a/megamek/src/megamek/common/scenario/ScenarioV2.java +++ b/megamek/src/megamek/common/scenario/ScenarioV2.java @@ -58,9 +58,11 @@ public class ScenarioV2 implements Scenario { private static final String END = "end"; private static final String TRIGGER = "trigger"; private static final String VICTORY = "victory"; + private static final String BOT = "bot"; private final JsonNode node; private final File scenariofile; + private final Map botInfo = new HashMap<>(); private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); @@ -110,6 +112,11 @@ public boolean hasFixedPlanetaryConditions() { return !node.has(PARAM_PLANETCOND_FIXED) || node.get(PARAM_PLANETCOND_FIXED).booleanValue(); } + @Override + public BotParser.BotInfo getBotInfo(String playerName) { + return botInfo.get(playerName); + } + @Override public IGame createGame() throws IOException, ScenarioLoaderException { LogManager.getLogger().info("Loading scenario from {}", scenariofile); @@ -268,6 +275,11 @@ private List readPlayers(IGame game) throws ScenarioLoaderException, IOE teamId = playerNode.has(PARAM_TEAM) ? playerNode.get(PARAM_TEAM).intValue() : teamId + 1; player.setTeam(Math.min(teamId, Player.TEAM_NAMES.length - 1)); + // Bot type + if (playerNode.has(BOT)) { + botInfo.put(player.getName(), BotParser.parse(playerNode.get(BOT))); + } + //TODO minefields // Carryables From f8f7495fa58f3c7afbfee6b3a740584cee017b62 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 20 Aug 2024 09:05:02 +0200 Subject: [PATCH 02/14] add Lowering The Boom reverse scenario, princess fleeing --- .../LoweringTheBoom/LoweringTheBoom.mms | 9 + .../LoweringTheBoomReverse.mms | 364 ++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms index dd60051451b..682b4353288 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms @@ -21,6 +21,7 @@ MMSVersion: 2 name: Lowering the Boom planet: Castor description: > + Playing as the Marik side -- Lyran intelligence has found illegal atomic weapons on the Marik world of Castor. Katrina Steiner has authorized an attack to remove the weapons and provide the Kell Hounds with action. @@ -144,6 +145,14 @@ factions: deploy: edge: S + bot: + # Optional: which edge am I trying to reach? This is used for the "flee" status + destination: SOUTH + + # Optional: flee = will try to reach the destinationEdge even when not crippled + # forcedwithdrawal = follow the Forced Withdrawal rules + status: flee + victory: - modify: onlyatend trigger: diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms new file mode 100644 index 00000000000..c80bed8e499 --- /dev/null +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms @@ -0,0 +1,364 @@ +# +# 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 . +# +# Based on Battlecorps Scenario 3011, Lowering the Boom, originally published in FASA's "Kell Hounds" sourcebook +MMSVersion: 2 +name: Lowering the Boom +planet: Castor +description: > + Playing as the Kell Hound side -- + Lyran intelligence has found illegal atomic weapons on the Marik world of Castor. Katrina Steiner + has authorized an attack to remove the weapons and provide the Kell Hounds with action. + +map: + boardrows: 2 + boards: + - file: Map Set 2/16x17 BattleTech.board + - file: Map Set 2/16x17 Lake Area.board + modify: rotate + postprocess: + - type: convertterrain + terrain: woods + newlevel: 2 + - type: convertterrain + terrain: water + newlevel: 3 + +factions: + + - name: Kell Hounds, First Battalion + camo: MERC - 1st Kell Hounds.gif + deploy: + edge: S + + victory: + - modify: onlyatend + trigger: + type: fledunits + modify: atend + units: [ 101, 102, 103, 104, 105, 106 ] + atmost: 2 + + units: + - fullname: Wolverine WVR-6R + id: 201 + deploymentround: 2 + crew: + name: Maj. Salome Ward + piloting: 4 + gunnery: 3 + + - fullname: Shadow Hawk SHD-2H + id: 202 + crew: + name: Lee Kennedy + piloting: 4 + gunnery: 4 + + - fullname: Dervish DV-6M + id: 203 + deploymentround: 2 + ammo: + LT: + - slot: 3 + shots: 4 + RT: + - slot: 3 + shots: 4 + crew: + name: Brian Martell + piloting: 4 + gunnery: 4 + + - fullname: Trebuchet TBT-5N + id: 204 + deploymentround: 2 + crew: + name: Judith Nesmith + piloting: 4 + gunnery: 4 + + - fullname: Phoenix Hawk PXH-1 + id: 205 + crew: + name: Nathan Mack + piloting: 4 + gunnery: 4 + + - fullname: Phoenix Hawk PXH-1 + id: 206 + crew: + name: Stuart O'Grady + piloting: 4 + gunnery: 4 + + - fullname: Jenner JR7-D + id: 207 + crew: + name: Sarah Jette + piloting: 4 + gunnery: 4 + + - name: Thirtieth Marik Militia + camo: Free Worlds League/Marik Militia/Marik Militia.jpg + deploy: N + + bot: + destination: north + status: flee + + victory: + - trigger: + type: fledunits + modify: atend + units: [ 101, 102, 103, 104, 105, 106 ] + atleast: 4 + modify: onlyatend + + units: + - fullname: Thunderbolt TDR-5S + id: 101 + at: [ 12, 29 ] + remaining: + armor: + LT: 2 + CT: 15 + ammo: + CT: + - slot: 11 + shots: 5 + - slot: 12 + shots: 5 + crew: + name: Col. Oliver Nage + portrait: Male/MechWarrior/MW_M_15.png + piloting: 5 + gunnery: 5 + + - fullname: Griffin GRF-1N + id: 102 + at: [ 9, 32 ] + remaining: + armor: + LT: 0 + CT: 15 + internal: + LT: 10 + crits: + RT: 3 + crew: + name: Maj. Abraham Morrison + portrait: Male/MechWarrior/MW_M_13.png + piloting: 4 + gunnery: 4 + + - fullname: Hunchback HBK-4G + id: 103 + at: [ 16, 29 ] + remaining: + armor: + HD: 5 + RL: 10 + ammo: + LT: + - slot: 1 + shots: 3 + - slot: 2 + shots: 3 + crew: + name: Lt. Alicia Devon + piloting: 4 + gunnery: 4 + + - fullname: Centurion CN9-A + id: 104 + at: [ 14, 31 ] + remaining: + armor: + CT: 12 + ammo: + LT: + - slot: 4 + shots: 4 + - slot: 5 + shots: 4 + crew: + name: Sgt. Jonathan Taylor + piloting: 4 + gunnery: 4 + + - fullname: Hermes II HER-2S + id: 105 + at: [ 5, 30 ] + crew: + name: Samantha Blaustein + piloting: 4 + gunnery: 4 + + - fullname: Javelin JVN-10N + id: 106 + at: [ 2, 29 ] + crew: + name: Deborah Ryan + piloting: 4 + gunnery: 4 + + +messages: + - header: Situation + text: | + # Situation + ## Castor + ## Free Worlds League + ## 7 June 3011 + + The idea for a Steiner raid on the Marik world of Castor originated with Cranston Snord, a well-known + eccentric and one of Katrina Steiner's long-serving mercenary commanders. Snord's sources had informed + him of a priceless collection of Fabergé Eggs located on Castor, and he already envisioned them as part + of his private museum on Clinton. + + Upon learning of Snord's plans, Morgan and Patrick Kell saw an opportunity to put their newly formed + unit to the test in battle. With Lyran intelligence uncovering a cache of illegal atomic weapons on + Castor, Katrina Steiner sanctioned the raid. The operation aimed to eliminate the weapons, secure the + eggs for Snord, and provide combat experience for the Kell Hounds. + + Colonel Nage recognized the battle was lost and retreated with his command company, aiming to reach the + atomic weapons and unleash them on the mercenaries. Colonel Kell dispatched Salome Ward and her + Relentless Wolves to intercept him. Ward's company caught up with Nage just as he was nearing the depot. + She launched an attack as the Second Battalion's LAMs bombed the depot. + + *This scenario is based on Battlecorps Scenario 3011, "Lowering the Boom", originally published in + the "Kell Hounds" sourcebook, FASA 01652.* + image: loweringboom_splash.png + trigger: + type: gamestart + + - header: Defender's Task + text: | + ## Defender's Task + + In this scenario, it is your task to save Col. Oliver Nage and his elements of 1st Company, 1st Battalion + from the attacking Kell Hounds. Try to escape by retreating off the north edge of the map with + as many Meks as possible. + + Be careful! Some of your Meks have already sustained damage. + + *Technical note: you can currently retreat off any edge of the battlefield and it will count for victory. + If you do this, Princess will be sad.* + image: loweringboom_map.png + trigger: + type: and + triggers: + - type: phasestart + phase: movement + - type: round + round: 1 + + - header: One Unit Safe + text: Congratulations, one of your Meks has safely left the battlefield! + trigger: + type: fledunits + modify: once + units: [ 101, 102, 103, 104, 105, 106 ] + count: 1 + + - header: Another Unit Safe + text: Three of your Meks have safely left the battlefield! This game is already considered a draw. + trigger: + type: fledunits + modify: once + units: [ 101, 102, 103, 104, 105, 106 ] + atleast: 3 + + - header: Decisive Defeat + text: | + ## Decisive Defeat + + Not more than one Marik Mek managed to evade the Kell Hounds forces. The Kell Hounds + have won a decivise victory. + + Ward's Wolves inflicted significant losses on the Marik Meks. Witnessing the smoke rising + from the depot and his units sustaining severe damage, Nage decided to flee, with the relentless + pursuit of the Wolves close behind. + + image: loweringboom_splash.png + trigger: + type: fledunits + modify: atend + units: [ 101, 102, 103, 104, 105, 106 ] + atmost: 1 + + - header: Marginal Defeat + text: | + ## Marginal Defeat + + Only two Marik Meks managed to evade the Kell Hounds forces. + + Ward's Wolves inflicted significant losses on the Marik Meks. Witnessing the smoke rising + from the depot and his units sustaining severe damage, Nage decided to flee, with the relentless + pursuit of the Wolves close behind. + image: loweringboom_splash.png + trigger: + type: fledunits + modify: atend + units: [ 101, 102, 103, 104, 105, 106 ] + count: 2 + + - header: Decisive Marik Victory + text: At least five Marik Meks managed to evade the Kell Hounds forces. The FWL + has won a decivise victory. + + Ward's Wolves failed to inflict significant losses on the Marik Meks. With the atomic weapons supply + depot bombarded, Nage decided to withdraw. + image: loweringboom_splash.png + trigger: + type: fledunits + modify: atend + units: [ 101, 102, 103, 104, 105, 106 ] + atleast: 5 + + - header: A Draw! + text: Three Marik Meks managed to evade the Kell Hounds forces. This result is considered + a draw. + + Both sides suffered significant losses. With the atomic weapons supply + depot bombarded, Col. Nage decided to withdraw. + image: loweringboom_splash.png + trigger: + type: fledunits + modify: atend + units: [ 101, 102, 103, 104, 105, 106 ] + count: 3 + + - header: Marginal Marik Victory + text: Four Marik Meks managed to evade the Kell Hounds forces. The FWL + has won a marginal victory. + + Ward's Wolves failed to inflict significant losses on the Marik Meks. With the atomic weapons supply + depot bombarded, Col. Nage decided to withdraw. + image: loweringboom_splash.png + trigger: + type: fledunits + modify: atend + units: [ 101, 102, 103, 104, 105, 106 ] + count: 4 + +end: + - trigger: + type: battlefieldcontrol + From c0b79ca8a7dca9241da2be18e50f537c8ff943d6 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 20 Aug 2024 09:05:23 +0200 Subject: [PATCH 03/14] add princess settings change event (incomplete) --- .../jacksonadapters/MessageDeserializer.java | 10 +-- .../PrincessSettingsEventDeserializer.java | 79 +++++++++++++++++++ .../scriptedevent/PrincessSettingsEvent.java | 70 ++++++++++++++++ 3 files changed, 153 insertions(+), 6 deletions(-) create mode 100644 megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java create mode 100644 megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java diff --git a/megamek/src/megamek/common/jacksonadapters/MessageDeserializer.java b/megamek/src/megamek/common/jacksonadapters/MessageDeserializer.java index 5474bf54313..1231d16b03d 100644 --- a/megamek/src/megamek/common/jacksonadapters/MessageDeserializer.java +++ b/megamek/src/megamek/common/jacksonadapters/MessageDeserializer.java @@ -57,13 +57,11 @@ public MessageTriggeredActiveEvent deserialize(JsonParser jp, DeserializationCon } /** - * Parses the given map: or maps: node to return a list of one or more boards (the list should - * ideally never be empty, an exception being thrown instead). Board files are tried first - * in the given basePath; if not found there, MM's data/boards/ is tried instead. + * Parses the given messages: node to return a list of one or more message events. - * @param messageNode a map: or maps: node from a YAML definition file - * @param basePath a path to search board files in (e.g. scenario path) - * @return a list of parsed boards + * @param messageNode a messages: node + * @param basePath a path to search image files in (e.g. scenario path) + * @return a list of parsed message events * @throws IllegalArgumentException for illegal node combinations and other errors */ public static MessageTriggeredActiveEvent parse(JsonNode messageNode, File basePath) { diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java new file mode 100644 index 00000000000..c9d5b34a8cb --- /dev/null +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java @@ -0,0 +1,79 @@ +/* + * 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 . + */ +package megamek.common.jacksonadapters; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import megamek.client.bot.princess.BehaviorSettings; +import megamek.client.ui.MMMarkdownRenderer; +import megamek.server.scriptedevent.PrincessSettingsEvent; +import megamek.server.trigger.Trigger; +import org.apache.logging.log4j.LogManager; + +import java.awt.*; +import java.io.File; +import java.io.IOException; + +import static megamek.common.jacksonadapters.MMUReader.requireFields; + +public class PrincessSettingsEventDeserializer extends StdDeserializer { + + private static final String TEXT = "text"; + private static final String HEADER = "header"; + private static final String TRIGGER = "trigger"; + private static final String IMAGE = "image"; + + public PrincessSettingsEventDeserializer() { + this(null); + } + + public PrincessSettingsEventDeserializer(Class vc) { + super(vc); + } + + @Override + public PrincessSettingsEvent deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + return parse(jp.getCodec().readTree(jp)); + } + + /** + * Parses the given messages: node to return a list of one or more message events. + + * @param eventNode a messages: node + * @return a list of parsed message events + * @throws IllegalArgumentException for illegal node combinations and other errors + */ + public static PrincessSettingsEvent parse(JsonNode eventNode) { + // TODO: how to give incomplete settings to change only one setting + // TODO: unify parsing with BotParser + // TODO: update Lowering the Boom reverse messages + requireFields("MessageScriptedEvent", eventNode, TEXT, HEADER, TRIGGER); + + String header = eventNode.get(HEADER).textValue(); + String text = eventNode.get(TEXT).textValue(); + // By default, expect this to be markdown and render to HTML; this preserves line breaks and paragraphs + text = MMMarkdownRenderer.getRenderedHtml(text); + Trigger trigger = TriggerDeserializer.parseNode(eventNode.get(TRIGGER)); + + + return new PrincessSettingsEvent(trigger, header, new BehaviorSettings()); + } +} diff --git a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java new file mode 100644 index 00000000000..262f1d9847e --- /dev/null +++ b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java @@ -0,0 +1,70 @@ +/* + * 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 . + */ +package megamek.server.scriptedevent; + +import megamek.client.bot.princess.BehaviorSettings; +import megamek.common.Player; +import megamek.common.net.enums.PacketCommand; +import megamek.common.net.packets.Packet; +import megamek.server.GameManager; +import megamek.server.IGameManager; +import megamek.server.trigger.Trigger; +import org.apache.logging.log4j.LogManager; + +public class PrincessSettingsEvent implements TriggeredActiveEvent { + + private final Trigger trigger; + private final String playerName; + private final BehaviorSettings settings; + + /** + * Creates a scripted even that changes the Princess settings for the bot player with the given name to + * the given settings. + * + * @param trigger The trigger that activates this event + * @param playerName The Princess player to change + * @param settings The new settings for Princess to use + */ + public PrincessSettingsEvent(Trigger trigger, String playerName, BehaviorSettings settings) { + this.trigger = trigger; + this.playerName = playerName; + this.settings = settings; + } + + @Override + public void process(IGameManager gameManager) { + if (gameManager instanceof GameManager gm) { + for (Player player : gm.getGame().getPlayersList()) { + if (player.getName().equals(playerName) && player.isBot()) { + gm.send(player.getId(), new Packet(PacketCommand.PRINCESS_SETTINGS, settings)); + return; + } + } + // only reached when no bot player of the name was found + LogManager.getLogger().warn("PrincessSettingsEvent found no bot player with the right name"); + } else { + LogManager.getLogger().error("PrincessSettingsEvent is only available in TW games"); + } + } + + @Override + public Trigger trigger() { + return trigger; + } +} From f00207276cf2c06ca5c0ee69bf479ad82de60357 Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 31 Aug 2024 16:39:46 +0200 Subject: [PATCH 04/14] add PrincessSettingsBuilder --- .../data/scenarios/Test Setups/BotvBot.mms | 6 + .../bot/princess/BehaviorSettingsFactory.java | 2 +- .../common/jacksonadapters/BotParser.java | 89 +++++---- .../PrincessSettingsBuilder.java | 183 ++++++++++++++++++ .../PrincessSettingsEventDeserializer.java | 2 +- .../scriptedevent/PrincessSettingsEvent.java | 12 +- 6 files changed, 248 insertions(+), 46 deletions(-) create mode 100644 megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java diff --git a/megamek/data/scenarios/Test Setups/BotvBot.mms b/megamek/data/scenarios/Test Setups/BotvBot.mms index 0cec0c7ec90..4fe0c892419 100644 --- a/megamek/data/scenarios/Test Setups/BotvBot.mms +++ b/megamek/data/scenarios/Test Setups/BotvBot.mms @@ -7,6 +7,12 @@ factions: - name: Observer - name: Legion of Vega + + bot: + herdmentality: 10 + selfpreservation: 10 + retreat: SOUTH + units: - fullname: Atlas AS7-D - fullname: Locust LCT-1M diff --git a/megamek/src/megamek/client/bot/princess/BehaviorSettingsFactory.java b/megamek/src/megamek/client/bot/princess/BehaviorSettingsFactory.java index a467d311476..c52fc5b20d1 100644 --- a/megamek/src/megamek/client/bot/princess/BehaviorSettingsFactory.java +++ b/megamek/src/megamek/client/bot/princess/BehaviorSettingsFactory.java @@ -420,7 +420,7 @@ private BehaviorSettings buildDefaultBehavior() { return defaultBehavior; } catch (Exception e) { logger.error(e, "Default Behavior Exception"); - return null; + return new BehaviorSettings(); } } } diff --git a/megamek/src/megamek/common/jacksonadapters/BotParser.java b/megamek/src/megamek/common/jacksonadapters/BotParser.java index 83a84f3405b..37ded53da81 100644 --- a/megamek/src/megamek/common/jacksonadapters/BotParser.java +++ b/megamek/src/megamek/common/jacksonadapters/BotParser.java @@ -18,7 +18,10 @@ */ package megamek.common.jacksonadapters; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import megamek.client.bot.princess.BehaviorSettings; import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.bot.princess.CardinalEdge; @@ -28,6 +31,8 @@ public final class BotParser { + private static final ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory()); + public interface BotInfo { int type(); } @@ -54,46 +59,52 @@ public int type() { private static final String STATUS = "status"; public static BotInfo parse(JsonNode node) { - if (!node.has(TYPE) || node.get(TYPE).asText().equals(BOT_PRINCESS)) { - BehaviorSettings behavior = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; - assert behavior != null; - if (node.has(PRINCESS_SELF_PRESERVATION)) { - behavior.setSelfPreservationIndex(node.get(PRINCESS_SELF_PRESERVATION).asInt(5)); - } - if (node.has(PRINCESS_FALL_SHAME)) { - behavior.setFallShameIndex(node.get(PRINCESS_FALL_SHAME).asInt(5)); - } - if (node.has(PRINCESS_AGGRESSION)) { - behavior.setHyperAggressionIndex(node.get(PRINCESS_AGGRESSION).asInt(5)); - } - if (node.has(PRINCESS_HERDING)) { - behavior.setHerdMentalityIndex(node.get(PRINCESS_HERDING).asInt(5)); - } - if (node.has(PRINCESS_BRAVERY)) { - behavior.setBraveryIndex(node.get(PRINCESS_BRAVERY).asInt(5)); - } - if (node.has(PRINCESS_DESTINATION)) { - CardinalEdge edge = CardinalEdge.parseFromString( - node.get(PRINCESS_DESTINATION).asText("NONE").toUpperCase()); - behavior.setDestinationEdge(edge); - } - if (node.has(PRINCESS_RETREAT)) { - CardinalEdge edge = CardinalEdge.parseFromString( - node.get(PRINCESS_RETREAT).asText("NONE").toUpperCase()); - behavior.setRetreatEdge(edge); - } - List statusStrings = TriggerDeserializer.parseArrayOrSingleNode(node.get(STATUS), - PRINCESS_FLEE, PRINCESS_FORCED_WITHDRAW); - if (statusStrings.contains(PRINCESS_FLEE)) { - behavior.setAutoFlee(true); - } - if (statusStrings.contains(PRINCESS_FORCED_WITHDRAW)) { - behavior.setForcedWithdrawal(true); - } - return new PrincessRecord(behavior); - } else { - throw new IllegalArgumentException("Invalid bot type"); + try { + PrincessSettingsBuilder builder = YAML_MAPPER.treeToValue(node, PrincessSettingsBuilder.class); + return new PrincessRecord(builder.build()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); } +// if (!node.has(TYPE) || node.get(TYPE).asText().equals(BOT_PRINCESS)) { +// BehaviorSettings behavior = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; +// assert behavior != null; +// if (node.has(PRINCESS_SELF_PRESERVATION)) { +// behavior.setSelfPreservationIndex(node.get(PRINCESS_SELF_PRESERVATION).asInt(5)); +// } +// if (node.has(PRINCESS_FALL_SHAME)) { +// behavior.setFallShameIndex(node.get(PRINCESS_FALL_SHAME).asInt(5)); +// } +// if (node.has(PRINCESS_AGGRESSION)) { +// behavior.setHyperAggressionIndex(node.get(PRINCESS_AGGRESSION).asInt(5)); +// } +// if (node.has(PRINCESS_HERDING)) { +// behavior.setHerdMentalityIndex(node.get(PRINCESS_HERDING).asInt(5)); +// } +// if (node.has(PRINCESS_BRAVERY)) { +// behavior.setBraveryIndex(node.get(PRINCESS_BRAVERY).asInt(5)); +// } +// if (node.has(PRINCESS_DESTINATION)) { +// CardinalEdge edge = CardinalEdge.parseFromString( +// node.get(PRINCESS_DESTINATION).asText("NONE").toUpperCase()); +// behavior.setDestinationEdge(edge); +// } +// if (node.has(PRINCESS_RETREAT)) { +// CardinalEdge edge = CardinalEdge.parseFromString( +// node.get(PRINCESS_RETREAT).asText("NONE").toUpperCase()); +// behavior.setRetreatEdge(edge); +// } +// List statusStrings = TriggerDeserializer.parseArrayOrSingleNode(node.get(STATUS), +// PRINCESS_FLEE, PRINCESS_FORCED_WITHDRAW); +// if (statusStrings.contains(PRINCESS_FLEE)) { +// behavior.setAutoFlee(true); +// } +// if (statusStrings.contains(PRINCESS_FORCED_WITHDRAW)) { +// behavior.setForcedWithdrawal(true); +// } +// return new PrincessRecord(behavior); +// } else { +// throw new IllegalArgumentException("Invalid bot type"); +// } } private BotParser() { } diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java new file mode 100644 index 00000000000..ba64690c042 --- /dev/null +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package megamek.common.jacksonadapters; + +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import megamek.client.bot.princess.BehaviorSettings; +import megamek.client.bot.princess.BehaviorSettingsFactory; +import megamek.client.bot.princess.CardinalEdge; +import megamek.client.bot.princess.PrincessException; +import megamek.common.annotations.Nullable; +import megamek.logging.MMLogger; + +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*; + +@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, setterVisibility = NONE) +public class PrincessSettingsBuilder { + + private static final MMLogger LOGGER = MMLogger.create(PrincessSettingsBuilder.class); + + private static final String PRINCESS_SELF_PRESERVATION = "selfpreservation"; + private static final String PRINCESS_FALL_SHAME = "fallshame"; + private static final String PRINCESS_AGGRESSION = "hyperaggression"; + private static final String PRINCESS_HERDING = "herdmentality"; + private static final String PRINCESS_DESTINATION = "destination"; + private static final String PRINCESS_RETREAT = "retreat"; + private static final String PRINCESS_FLEE = "flee"; + private static final String PRINCESS_FORCED_WITHDRAW = "forcedwithdraw"; + + @JsonAlias(PRINCESS_SELF_PRESERVATION) + private int selfPreservation = -1; + + @JsonAlias(PRINCESS_FALL_SHAME) + private int fallShame = -1; + + @JsonAlias(PRINCESS_AGGRESSION) + private int hyperAgression = -1; + + @JsonAlias(PRINCESS_HERDING) + private int herdMentality = -1; + + private int bravery = -1; + + private boolean hasFleeValue = false; + + @JsonAlias(PRINCESS_FLEE) + private boolean doFlee; + + @JsonAlias(PRINCESS_DESTINATION) + private CardinalEdge fleeDestinationEdge; + + private boolean hasForcedWithdrawValue = false; + + @JsonAlias(PRINCESS_FORCED_WITHDRAW) + private boolean useForcedWithdraw; + + @JsonAlias(PRINCESS_RETREAT) + private CardinalEdge withdrawEdge; + + private String description = null; + + + public PrincessSettingsBuilder selfPreservation(int selfPreservation) { + this.selfPreservation = selfPreservation; + return this; + } + + public PrincessSettingsBuilder fallShame(int fallShame) { + this.fallShame = fallShame; + return this; + } + + public PrincessSettingsBuilder hyperAgression(int hyperAgression) { + this.hyperAgression = hyperAgression; + return this; + } + + public PrincessSettingsBuilder herdMentality(int herdMentality) { + this.herdMentality = herdMentality; + return this; + } + + public PrincessSettingsBuilder bravery(int bravery) { + this.bravery = bravery; + return this; + } + + public PrincessSettingsBuilder flee(boolean flee) { + doFlee = flee; + hasFleeValue = true; + return this; + } + + public PrincessSettingsBuilder useWithdraw(boolean useWithdraw) { + useForcedWithdraw = useWithdraw; + hasForcedWithdrawValue = true; + return this; + } + + public PrincessSettingsBuilder description(String description) { + if ((description == null) || description.isBlank()) { + throw new IllegalArgumentException("Description cannot be null or empty"); + } + this.description = description; + return this; + } + + + /** + * Returns new BehaviorSettings based on the given settings. Settings that are present in this builder + * overwrite the previous settings, others are untouched. + * + * @param previousSettings Settings to base the new settings on + * @return New BehaviorSettings that incorporate the settings of this builder + */ + public BehaviorSettings build(@Nullable BehaviorSettings previousSettings) { + BehaviorSettings settings = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; + if (previousSettings != null) { + try { + settings = previousSettings.getCopy(); + } catch (PrincessException ex) { + LOGGER.error("Could not obtain copy of given princess settings", ex); + // stay with the default settings + } + } + if (selfPreservation != -1) { + settings.setSelfPreservationIndex(selfPreservation); + } + if (fallShame != -1) { + settings.setFallShameIndex(fallShame); + } + if (hyperAgression != -1) { + settings.setHyperAggressionIndex(hyperAgression); + } + if (herdMentality != -1) { + settings.setHerdMentalityIndex(herdMentality); + } + if (bravery != -1) { + settings.setBraveryIndex(bravery); + } + if (hasFleeValue) { + settings.setAutoFlee(doFlee); + } + if (hasForcedWithdrawValue) { + settings.setForcedWithdrawal(useForcedWithdraw); + } + if ((description != null) && !description.isBlank()) { + try { + settings.setDescription(description); + } catch (PrincessException ex) { + // ignore, description has been made sure to not be empty + } + } + + return settings; + } + + /** + * Returns new BehaviorSettings based on the Princess default settings. Settings that are present in this + * builder overwrite the default settings, others are untouched. + * + * @return New BehaviorSettings that incorporate the settings of this builder + */ + public BehaviorSettings build() { + return build(null); + } +} diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java index c9d5b34a8cb..5a7ee449b55 100644 --- a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java @@ -74,6 +74,6 @@ public static PrincessSettingsEvent parse(JsonNode eventNode) { Trigger trigger = TriggerDeserializer.parseNode(eventNode.get(TRIGGER)); - return new PrincessSettingsEvent(trigger, header, new BehaviorSettings()); + return new PrincessSettingsEvent(trigger, header, new PrincessSettingsBuilder()); } } diff --git a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java index 262f1d9847e..39981ca33cb 100644 --- a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java +++ b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java @@ -20,6 +20,7 @@ import megamek.client.bot.princess.BehaviorSettings; import megamek.common.Player; +import megamek.common.jacksonadapters.PrincessSettingsBuilder; import megamek.common.net.enums.PacketCommand; import megamek.common.net.packets.Packet; import megamek.server.GameManager; @@ -31,7 +32,7 @@ public class PrincessSettingsEvent implements TriggeredActiveEvent { private final Trigger trigger; private final String playerName; - private final BehaviorSettings settings; + private final PrincessSettingsBuilder settingsBuilder; /** * Creates a scripted even that changes the Princess settings for the bot player with the given name to @@ -39,12 +40,12 @@ public class PrincessSettingsEvent implements TriggeredActiveEvent { * * @param trigger The trigger that activates this event * @param playerName The Princess player to change - * @param settings The new settings for Princess to use + * @param settingsBuilder The new settings for Princess to use */ - public PrincessSettingsEvent(Trigger trigger, String playerName, BehaviorSettings settings) { + public PrincessSettingsEvent(Trigger trigger, String playerName, PrincessSettingsBuilder settingsBuilder) { this.trigger = trigger; this.playerName = playerName; - this.settings = settings; + this.settingsBuilder = settingsBuilder; } @Override @@ -52,7 +53,8 @@ public void process(IGameManager gameManager) { if (gameManager instanceof GameManager gm) { for (Player player : gm.getGame().getPlayersList()) { if (player.getName().equals(playerName) && player.isBot()) { - gm.send(player.getId(), new Packet(PacketCommand.PRINCESS_SETTINGS, settings)); +// gm.getGame().getBotSettings() +// gm.send(player.getId(), new Packet(PacketCommand.PRINCESS_SETTINGS, settings)); return; } } From 934bbd61e5c6358dc741d93a1f76095c03668a74 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 1 Sep 2024 10:09:00 +0200 Subject: [PATCH 05/14] read bot settings in builder class --- .../data/scenarios/Test Setups/BotvBot.mms | 7 +- .../client/bot/princess/BehaviorSettings.java | 1 + .../common/jacksonadapters/BotParser.java | 67 +------------------ .../PrincessSettingsBuilder.java | 25 ++++--- 4 files changed, 25 insertions(+), 75 deletions(-) diff --git a/megamek/data/scenarios/Test Setups/BotvBot.mms b/megamek/data/scenarios/Test Setups/BotvBot.mms index 4fe0c892419..a6010d917dd 100644 --- a/megamek/data/scenarios/Test Setups/BotvBot.mms +++ b/megamek/data/scenarios/Test Setups/BotvBot.mms @@ -11,11 +11,12 @@ factions: bot: herdmentality: 10 selfpreservation: 10 - retreat: SOUTH + flee: true + destination: SOUTH units: - - fullname: Atlas AS7-D - - fullname: Locust LCT-1M + - fullname: Atlas AS7-D + - fullname: Locust LCT-1M - name: 2nd Air Cavalry, Federated Suns units: diff --git a/megamek/src/megamek/client/bot/princess/BehaviorSettings.java b/megamek/src/megamek/client/bot/princess/BehaviorSettings.java index 01a04c488bf..3222cbad069 100644 --- a/megamek/src/megamek/client/bot/princess/BehaviorSettings.java +++ b/megamek/src/megamek/client/bot/princess/BehaviorSettings.java @@ -842,6 +842,7 @@ public String toLog() { out.append("\n\t Destination Edge: ").append(getDestinationEdge()); out.append("\n\t Retreat Edge: ").append(getRetreatEdge()); out.append("\n\t Forced Withdrawal: ").append(isForcedWithdrawal()); + out.append("\n\t Flee: ").append(autoFlee); out.append("\n\t Self Preservation: ").append(getSelfPreservationIndex()).append(":") .append(getSelfPreservationValue(getSelfPreservationIndex())); out.append("\n\t Hyper Aggression: ").append(getHyperAggressionIndex()).append(":") diff --git a/megamek/src/megamek/common/jacksonadapters/BotParser.java b/megamek/src/megamek/common/jacksonadapters/BotParser.java index 37ded53da81..26d8df5b640 100644 --- a/megamek/src/megamek/common/jacksonadapters/BotParser.java +++ b/megamek/src/megamek/common/jacksonadapters/BotParser.java @@ -23,12 +23,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import megamek.client.bot.princess.BehaviorSettings; -import megamek.client.bot.princess.BehaviorSettingsFactory; -import megamek.client.bot.princess.CardinalEdge; import megamek.client.ui.swing.ScenarioDialog; -import java.util.List; - public final class BotParser { private static final ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory()); @@ -45,66 +41,9 @@ public int type() { } } - private static final String TYPE = "type"; - private static final String BOT_PRINCESS = "princess"; - private static final String PRINCESS_SELF_PRESERVATION = "selfpreservation"; - private static final String PRINCESS_FALL_SHAME = "fallshame"; - private static final String PRINCESS_AGGRESSION = "hyperaggression"; - private static final String PRINCESS_HERDING = "herdmentality"; - private static final String PRINCESS_BRAVERY = "bravery"; - private static final String PRINCESS_DESTINATION = "destination"; - private static final String PRINCESS_RETREAT = "retreat"; - private static final String PRINCESS_FLEE = "flee"; - private static final String PRINCESS_FORCED_WITHDRAW = "forcedwithdraw"; - private static final String STATUS = "status"; - - public static BotInfo parse(JsonNode node) { - try { - PrincessSettingsBuilder builder = YAML_MAPPER.treeToValue(node, PrincessSettingsBuilder.class); - return new PrincessRecord(builder.build()); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } -// if (!node.has(TYPE) || node.get(TYPE).asText().equals(BOT_PRINCESS)) { -// BehaviorSettings behavior = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; -// assert behavior != null; -// if (node.has(PRINCESS_SELF_PRESERVATION)) { -// behavior.setSelfPreservationIndex(node.get(PRINCESS_SELF_PRESERVATION).asInt(5)); -// } -// if (node.has(PRINCESS_FALL_SHAME)) { -// behavior.setFallShameIndex(node.get(PRINCESS_FALL_SHAME).asInt(5)); -// } -// if (node.has(PRINCESS_AGGRESSION)) { -// behavior.setHyperAggressionIndex(node.get(PRINCESS_AGGRESSION).asInt(5)); -// } -// if (node.has(PRINCESS_HERDING)) { -// behavior.setHerdMentalityIndex(node.get(PRINCESS_HERDING).asInt(5)); -// } -// if (node.has(PRINCESS_BRAVERY)) { -// behavior.setBraveryIndex(node.get(PRINCESS_BRAVERY).asInt(5)); -// } -// if (node.has(PRINCESS_DESTINATION)) { -// CardinalEdge edge = CardinalEdge.parseFromString( -// node.get(PRINCESS_DESTINATION).asText("NONE").toUpperCase()); -// behavior.setDestinationEdge(edge); -// } -// if (node.has(PRINCESS_RETREAT)) { -// CardinalEdge edge = CardinalEdge.parseFromString( -// node.get(PRINCESS_RETREAT).asText("NONE").toUpperCase()); -// behavior.setRetreatEdge(edge); -// } -// List statusStrings = TriggerDeserializer.parseArrayOrSingleNode(node.get(STATUS), -// PRINCESS_FLEE, PRINCESS_FORCED_WITHDRAW); -// if (statusStrings.contains(PRINCESS_FLEE)) { -// behavior.setAutoFlee(true); -// } -// if (statusStrings.contains(PRINCESS_FORCED_WITHDRAW)) { -// behavior.setForcedWithdrawal(true); -// } -// return new PrincessRecord(behavior); -// } else { -// throw new IllegalArgumentException("Invalid bot type"); -// } + public static BotInfo parse(JsonNode node) throws JsonProcessingException { + PrincessSettingsBuilder builder = YAML_MAPPER.treeToValue(node, PrincessSettingsBuilder.class); + return new PrincessRecord(builder.build()); } private BotParser() { } diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java index ba64690c042..f95725eb62c 100644 --- a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonSetter; import megamek.client.bot.princess.BehaviorSettings; import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.bot.princess.CardinalEdge; @@ -75,7 +76,6 @@ public class PrincessSettingsBuilder { private String description = null; - public PrincessSettingsBuilder selfPreservation(int selfPreservation) { this.selfPreservation = selfPreservation; return this; @@ -101,12 +101,14 @@ public PrincessSettingsBuilder bravery(int bravery) { return this; } + @JsonSetter("doFlee") public PrincessSettingsBuilder flee(boolean flee) { doFlee = flee; hasFleeValue = true; return this; } + @JsonSetter("useForcedWithdraw") public PrincessSettingsBuilder useWithdraw(boolean useWithdraw) { useForcedWithdraw = useWithdraw; hasForcedWithdrawValue = true; @@ -121,7 +123,6 @@ public PrincessSettingsBuilder description(String description) { return this; } - /** * Returns new BehaviorSettings based on the given settings. Settings that are present in this builder * overwrite the previous settings, others are untouched. @@ -157,15 +158,23 @@ public BehaviorSettings build(@Nullable BehaviorSettings previousSettings) { if (hasFleeValue) { settings.setAutoFlee(doFlee); } + if (fleeDestinationEdge != null) { + settings.setDestinationEdge(fleeDestinationEdge); + } if (hasForcedWithdrawValue) { settings.setForcedWithdrawal(useForcedWithdraw); } - if ((description != null) && !description.isBlank()) { - try { - settings.setDescription(description); - } catch (PrincessException ex) { - // ignore, description has been made sure to not be empty - } + if (withdrawEdge != null) { + settings.setRetreatEdge(withdrawEdge); + } + + if ((description == null) || description.isBlank()) { + description = "(No description)"; + } + try { + settings.setDescription(description); + } catch (PrincessException ex) { + // ignore, description has been made sure to not be empty } return settings; From 7c4b70dad8fe634ec780aea41bb94ace9e2e1ba2 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 1 Sep 2024 11:53:41 +0200 Subject: [PATCH 06/14] updates --- .../data/scenarios/Test Setups/BotvBot.mms | 4 +--- megamek/docs/Scenarios/ScenarioV2 HowTo.mms | 16 +++++++------- .../PrincessSettingsBuilder.java | 12 +++++++++++ .../scriptedevent/PrincessSettingsEvent.java | 21 +++++++++---------- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/megamek/data/scenarios/Test Setups/BotvBot.mms b/megamek/data/scenarios/Test Setups/BotvBot.mms index a6010d917dd..4d240b49419 100644 --- a/megamek/data/scenarios/Test Setups/BotvBot.mms +++ b/megamek/data/scenarios/Test Setups/BotvBot.mms @@ -9,10 +9,8 @@ factions: - name: Legion of Vega bot: - herdmentality: 10 - selfpreservation: 10 flee: true - destination: SOUTH + destination: south units: - fullname: Atlas AS7-D diff --git a/megamek/docs/Scenarios/ScenarioV2 HowTo.mms b/megamek/docs/Scenarios/ScenarioV2 HowTo.mms index a29520a90b9..2ae21c7caf5 100644 --- a/megamek/docs/Scenarios/ScenarioV2 HowTo.mms +++ b/megamek/docs/Scenarios/ScenarioV2 HowTo.mms @@ -307,14 +307,14 @@ factions: herdmentality: 5 # Optional: how quickly will I try to escape once damaged? bravery: 5 - # Optional: which edge am I trying to reach? This is used for the "flee" status - destination: none - # Optional: to which edge will my units flee when crippled? + # Optional: use forced Withdrawal, this is true by default + forcedwithdrawal: true + # Optional: the edge to retreat to, nearest by default; use south, north, west, east, nearest retreat: nearest - - # Optional: flee = will try to reach the destinationEdge even when not crippled - # forcedwithdrawal = follow the Forced Withdrawal rules - status: [ flee, forcedwithdrawal ] + # Optional: flee = true will try to reach the destination edge even when not crippled + flee: true + # Optional: the edge to flee to; use south, north, west, east, nearest + destination: none # private boolean goHome = false; // Should I immediately proceed to my home board edge? # private final Set strategicBuildingTargets = new HashSet<>(); // What (besides enemy units) do I want to blow up? @@ -349,7 +349,7 @@ factions: events: - type: princesssettings destination: south - status: flee + flee: true diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java index f95725eb62c..477aace7c0c 100644 --- a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java @@ -115,6 +115,18 @@ public PrincessSettingsBuilder useWithdraw(boolean useWithdraw) { return this; } + @JsonSetter("fleeDestinationEdge") + public PrincessSettingsBuilder setFleeEdge(String edge) { + fleeDestinationEdge = CardinalEdge.valueOf(edge.toUpperCase()); + return this; + } + + @JsonSetter("withdrawEdge") + public PrincessSettingsBuilder withdrawEdge(String edge) { + withdrawEdge = CardinalEdge.valueOf(edge.toUpperCase()); + return this; + } + public PrincessSettingsBuilder description(String description) { if ((description == null) || description.isBlank()) { throw new IllegalArgumentException("Description cannot be null or empty"); diff --git a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java index 39981ca33cb..48f1f164c72 100644 --- a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java +++ b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java @@ -18,8 +18,6 @@ */ package megamek.server.scriptedevent; -import megamek.client.bot.princess.BehaviorSettings; -import megamek.common.Player; import megamek.common.jacksonadapters.PrincessSettingsBuilder; import megamek.common.net.enums.PacketCommand; import megamek.common.net.packets.Packet; @@ -27,6 +25,7 @@ import megamek.server.IGameManager; import megamek.server.trigger.Trigger; import org.apache.logging.log4j.LogManager; +import megamek.common.Game; public class PrincessSettingsEvent implements TriggeredActiveEvent { @@ -36,11 +35,13 @@ public class PrincessSettingsEvent implements TriggeredActiveEvent { /** * Creates a scripted even that changes the Princess settings for the bot player with the given name to - * the given settings. + * the given settings. This event only works in TW games and will do nothing if no Princess settings are + * previously registered under the player name. * * @param trigger The trigger that activates this event * @param playerName The Princess player to change * @param settingsBuilder The new settings for Princess to use + * @see Game#getBotSettings() */ public PrincessSettingsEvent(Trigger trigger, String playerName, PrincessSettingsBuilder settingsBuilder) { this.trigger = trigger; @@ -51,15 +52,13 @@ public PrincessSettingsEvent(Trigger trigger, String playerName, PrincessSetting @Override public void process(IGameManager gameManager) { if (gameManager instanceof GameManager gm) { - for (Player player : gm.getGame().getPlayersList()) { - if (player.getName().equals(playerName) && player.isBot()) { -// gm.getGame().getBotSettings() -// gm.send(player.getId(), new Packet(PacketCommand.PRINCESS_SETTINGS, settings)); - return; - } + if (gm.getGame().getBotSettings().containsKey(playerName)) { + // apply this only if there is already a Princess with settings registered for the player name + gm.getGame().getBotSettings().compute(playerName, (k, oldSettings) -> settingsBuilder.build(oldSettings)); +//TODO gm.send(player.getId(), new Packet(PacketCommand.PRINCESS_SETTINGS, settings)); + } else { + LogManager.getLogger().warn("PrincessSettingsEvent found no bot player with the right name"); } - // only reached when no bot player of the name was found - LogManager.getLogger().warn("PrincessSettingsEvent found no bot player with the right name"); } else { LogManager.getLogger().error("PrincessSettingsEvent is only available in TW games"); } From eb78c9cae5bb2b367c1f4ec621d17b68958fd08c Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 2 Sep 2024 17:05:06 +0200 Subject: [PATCH 07/14] updates, additional packet command, event update --- .../data/scenarios/Test Setups/BotvBot.mms | 45 ++++++++++++------ megamek/docs/Scenarios/ScenarioV2 HowTo.mms | 7 ++- .../common/jacksonadapters/BotParser.java | 2 +- .../GeneralEventDeserializer.java | 46 +++++++++++++++++++ .../PrincessSettingsBuilder.java | 5 +- .../PrincessSettingsEventDeserializer.java | 29 +++--------- .../common/net/enums/PacketCommand.java | 8 ++++ .../megamek/common/scenario/ScenarioV2.java | 20 +++++++- .../scriptedevent/PrincessSettingsEvent.java | 41 +++++++++++++---- 9 files changed, 152 insertions(+), 51 deletions(-) create mode 100644 megamek/src/megamek/common/jacksonadapters/GeneralEventDeserializer.java diff --git a/megamek/data/scenarios/Test Setups/BotvBot.mms b/megamek/data/scenarios/Test Setups/BotvBot.mms index 4d240b49419..36ac91fe3ea 100644 --- a/megamek/data/scenarios/Test Setups/BotvBot.mms +++ b/megamek/data/scenarios/Test Setups/BotvBot.mms @@ -4,21 +4,40 @@ description: A test fight bot vs bot map: AGoAC Maps/16x17 Grassland 2.board factions: -- name: Observer + - name: Observer -- name: Legion of Vega + - name: Legion of Vega + units: + - fullname: Atlas AS7-D + id: 101 + - fullname: Locust LCT-1M + id: 102 + deploy: N - bot: - flee: true - destination: south + - name: 2nd Air Cavalry, Federated Suns + units: + - fullname: Atlas AS7-D + id: 201 + - fullname: Locust LCT-1M + id: 202 + deploy: S - units: - - fullname: Atlas AS7-D - - fullname: Locust LCT-1M - -- name: 2nd Air Cavalry, Federated Suns - units: - - fullname: Atlas AS7-D - - fullname: Locust LCT-1M +events: + - type: princesssettings + event: + trigger: + type: killedunit + unit: 102 + flee: true + destination: north + player: Legion of Vega + - type: message + event: + trigger: + type: killedunit + unit: 102 + modify: once + header: oops + text: Locust dead. I'm running to the north! diff --git a/megamek/docs/Scenarios/ScenarioV2 HowTo.mms b/megamek/docs/Scenarios/ScenarioV2 HowTo.mms index 2ae21c7caf5..aee8179b80c 100644 --- a/megamek/docs/Scenarios/ScenarioV2 HowTo.mms +++ b/megamek/docs/Scenarios/ScenarioV2 HowTo.mms @@ -348,6 +348,9 @@ factions: events: - type: princesssettings + trigger: + - type: unitkilled + unit: 103 destination: south flee: true @@ -510,7 +513,7 @@ trigger: # here, the unit 201 must be killed AND it must be the start of the movement phase for this AND trigger # to activate - type: killedunit - units: 201 + unit: 201 - type: phasestart phase: movement @@ -522,7 +525,7 @@ trigger: # here, the unit 201 must be killed OR it must be the start of the movement phase for this OR trigger # to activate - type: killedunit - units: 201 + unit: 201 - type: phasestart phase: movement diff --git a/megamek/src/megamek/common/jacksonadapters/BotParser.java b/megamek/src/megamek/common/jacksonadapters/BotParser.java index 26d8df5b640..c78e3fb984c 100644 --- a/megamek/src/megamek/common/jacksonadapters/BotParser.java +++ b/megamek/src/megamek/common/jacksonadapters/BotParser.java @@ -27,7 +27,7 @@ public final class BotParser { - private static final ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory()); + static final ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory()); public interface BotInfo { int type(); diff --git a/megamek/src/megamek/common/jacksonadapters/GeneralEventDeserializer.java b/megamek/src/megamek/common/jacksonadapters/GeneralEventDeserializer.java new file mode 100644 index 00000000000..95785d26f19 --- /dev/null +++ b/megamek/src/megamek/common/jacksonadapters/GeneralEventDeserializer.java @@ -0,0 +1,46 @@ +/* + * 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 . + */ +package megamek.common.jacksonadapters; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import megamek.server.scriptedevent.TriggeredActiveEvent; + +import java.io.File; + +public class GeneralEventDeserializer { + + private static final String TYPE = "type"; + private static final String EVENT = "event"; + private static final String PRINCESS_SETTINGS = "princesssettings"; + private static final String MESSAGE = "message"; + + + public static TriggeredActiveEvent parse(JsonNode node, File basePath) throws JsonProcessingException { + String type = node.get(TYPE).asText(); + JsonNode eventNode = node.get(EVENT); + return switch (type) { + case PRINCESS_SETTINGS -> PrincessSettingsEventDeserializer.parse(eventNode); + case MESSAGE -> MessageDeserializer.parse(eventNode, basePath); + default -> throw new IllegalArgumentException("Unknown event type: " + type); + }; + } + + private GeneralEventDeserializer() { } +} diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java index 477aace7c0c..2b824070f21 100644 --- a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java @@ -18,9 +18,7 @@ */ package megamek.common.jacksonadapters; -import com.fasterxml.jackson.annotation.JsonAlias; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.*; import megamek.client.bot.princess.BehaviorSettings; import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.bot.princess.CardinalEdge; @@ -31,6 +29,7 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*; @JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, setterVisibility = NONE) +@JsonIgnoreProperties(ignoreUnknown = true) public class PrincessSettingsBuilder { private static final MMLogger LOGGER = MMLogger.create(PrincessSettingsBuilder.class); diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java index 5a7ee449b55..9b77ff3f73e 100644 --- a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsEventDeserializer.java @@ -19,27 +19,21 @@ package megamek.common.jacksonadapters; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import megamek.client.bot.princess.BehaviorSettings; -import megamek.client.ui.MMMarkdownRenderer; import megamek.server.scriptedevent.PrincessSettingsEvent; import megamek.server.trigger.Trigger; -import org.apache.logging.log4j.LogManager; -import java.awt.*; -import java.io.File; import java.io.IOException; -import static megamek.common.jacksonadapters.MMUReader.requireFields; +import static megamek.common.jacksonadapters.BotParser.YAML_MAPPER; public class PrincessSettingsEventDeserializer extends StdDeserializer { - private static final String TEXT = "text"; - private static final String HEADER = "header"; + private static final String PLAYER_NAME = "player"; private static final String TRIGGER = "trigger"; - private static final String IMAGE = "image"; public PrincessSettingsEventDeserializer() { this(null); @@ -61,19 +55,10 @@ public PrincessSettingsEvent deserialize(JsonParser jp, DeserializationContext c * @return a list of parsed message events * @throws IllegalArgumentException for illegal node combinations and other errors */ - public static PrincessSettingsEvent parse(JsonNode eventNode) { - // TODO: how to give incomplete settings to change only one setting - // TODO: unify parsing with BotParser - // TODO: update Lowering the Boom reverse messages - requireFields("MessageScriptedEvent", eventNode, TEXT, HEADER, TRIGGER); - - String header = eventNode.get(HEADER).textValue(); - String text = eventNode.get(TEXT).textValue(); - // By default, expect this to be markdown and render to HTML; this preserves line breaks and paragraphs - text = MMMarkdownRenderer.getRenderedHtml(text); + public static PrincessSettingsEvent parse(JsonNode eventNode) throws JsonProcessingException { Trigger trigger = TriggerDeserializer.parseNode(eventNode.get(TRIGGER)); - - - return new PrincessSettingsEvent(trigger, header, new PrincessSettingsBuilder()); + String playerName = eventNode.get(PLAYER_NAME).asText(); + PrincessSettingsBuilder builder = YAML_MAPPER.treeToValue(eventNode, PrincessSettingsBuilder.class); + return new PrincessSettingsEvent(trigger, playerName, builder); } } diff --git a/megamek/src/megamek/common/net/enums/PacketCommand.java b/megamek/src/megamek/common/net/enums/PacketCommand.java index 836a2d7e513..1b249ec1d58 100644 --- a/megamek/src/megamek/common/net/enums/PacketCommand.java +++ b/megamek/src/megamek/common/net/enums/PacketCommand.java @@ -31,8 +31,16 @@ public enum PacketCommand { PLAYER_REMOVE, PLAYER_UPDATE, PLAYER_TEAM_CHANGE, + + /** + * A packet replacing a Client's knowledge of all bot settings (S -> C) or updating the Server on a single + * bot's settings (C -> S). Does not invoke any actual changes to bots. + */ PRINCESS_SETTINGS, + /** A Server to Client packet instructing a Princess Client to replace its settings. */ + CHANGE_PRINCESS_SETTINGS, + /** A packet setting a Client's ready status (S -> C) or updating the Server on the Client's status (C -> S). */ PLAYER_READY, diff --git a/megamek/src/megamek/common/scenario/ScenarioV2.java b/megamek/src/megamek/common/scenario/ScenarioV2.java index b1ed432093e..a98909658b8 100644 --- a/megamek/src/megamek/common/scenario/ScenarioV2.java +++ b/megamek/src/megamek/common/scenario/ScenarioV2.java @@ -59,6 +59,7 @@ public class ScenarioV2 implements Scenario { private static final String TRIGGER = "trigger"; private static final String VICTORY = "victory"; private static final String BOT = "bot"; + private static final String EVENTS = "events"; private final JsonNode node; private final File scenariofile; @@ -126,6 +127,7 @@ public IGame createGame() throws IOException, ScenarioLoaderException { parsePlayers(game); parseMessages(game); parseGameEndEvents(game); + parseGeneralEvents(game); game.setupTeams(); @@ -157,6 +159,23 @@ private void parsePlanetaryConditions(PlanetaryConditionsUsing plGame) throws Js } } + private void parseGeneralEvents(IGame game) { + if (node.has(EVENTS)) { + node.get(EVENTS).iterator().forEachRemaining(n -> { + try { + parseGeneralEvent(game, n); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); + } + } + + private void parseGeneralEvent(IGame game, JsonNode node) throws JsonProcessingException { + game.addScriptedEvent(GeneralEventDeserializer.parse(node, scenarioDirectory())); + } + + private void parseGameEndEvents(IGame game) { if (node.has(END)) { node.get(END).iterator().forEachRemaining(n -> parseGameEndEvent(game, n)); @@ -165,7 +184,6 @@ private void parseGameEndEvents(IGame game) { private void parseGameEndEvent(IGame game, JsonNode node) { game.addScriptedEvent(new GameEndTriggeredEvent(TriggerDeserializer.parseNode(node.get(TRIGGER)))); - } private void parseMessages(IGame game) { diff --git a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java index 48f1f164c72..b441f55db0f 100644 --- a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java +++ b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java @@ -18,6 +18,8 @@ */ package megamek.server.scriptedevent; +import megamek.client.bot.princess.BehaviorSettings; +import megamek.common.Player; import megamek.common.jacksonadapters.PrincessSettingsBuilder; import megamek.common.net.enums.PacketCommand; import megamek.common.net.packets.Packet; @@ -51,17 +53,38 @@ public PrincessSettingsEvent(Trigger trigger, String playerName, PrincessSetting @Override public void process(IGameManager gameManager) { - if (gameManager instanceof GameManager gm) { - if (gm.getGame().getBotSettings().containsKey(playerName)) { - // apply this only if there is already a Princess with settings registered for the player name - gm.getGame().getBotSettings().compute(playerName, (k, oldSettings) -> settingsBuilder.build(oldSettings)); -//TODO gm.send(player.getId(), new Packet(PacketCommand.PRINCESS_SETTINGS, settings)); - } else { - LogManager.getLogger().warn("PrincessSettingsEvent found no bot player with the right name"); - } - } else { + if (!validateData(gameManager)) { + return; + } + + GameManager gm = (GameManager) gameManager; + int id = findPlayerId(playerName, gm); + BehaviorSettings newSettings = settingsBuilder.build(gm.getGame().getBotSettings().get(playerName)); + gm.getGame().getBotSettings().put(playerName, newSettings); + gm.send(id, new Packet(PacketCommand.CHANGE_PRINCESS_SETTINGS, newSettings)); + } + + private boolean validateData(IGameManager gameManager) { + if (!(gameManager instanceof GameManager gm)) { LogManager.getLogger().error("PrincessSettingsEvent is only available in TW games"); + return false; + } else if (findPlayerId(playerName, gameManager) == Player.PLAYER_NONE) { + LogManager.getLogger().warn("No player ID for the player name {}", playerName); + return false; + } else if (!gm.getGame().getBotSettings().containsKey(playerName)) { + LogManager.getLogger().warn("No bot settings known for the player name {}", playerName); + return false; + } + return true; + } + + private int findPlayerId(String playerName, IGameManager gameManager) { + for (Player player : gameManager.getGame().getPlayersList()) { + if (playerName.equals(player.getName())) { + return player.getId(); + } } + return Player.PLAYER_NONE; } @Override From 30b093cec7aaacd77e9a06aeb88f4b761475acd6 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 24 Sep 2024 22:53:50 +0200 Subject: [PATCH 08/14] bot settings updates --- .../LoweringTheBoom/LoweringTheBoom.mms | 15 +++++----- .../LoweringTheBoomReverse.mms | 26 ++++++++++-------- .../loweringboom_map_reverse.png | Bin 0 -> 109460 bytes megamek/docs/Scenarios/ScenarioV2 HowTo.mms | 4 +-- .../megamek/client/ui/swing/MegaMekGUI.java | 3 -- .../PrincessSettingsBuilder.java | 23 +++++++++++++--- .../megamek/common/scenario/ScenarioV2.java | 7 +---- .../scriptedevent/PrincessSettingsEvent.java | 6 ++-- 8 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 megamek/data/scenarios/Kell Hounds/LoweringTheBoom/loweringboom_map_reverse.png diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms index 156f4f2b564..e2d25bb37f7 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms @@ -18,7 +18,7 @@ # # Based on Battlecorps Scenario 3011, Lowering the Boom, originally published in FASA's "Kell Hounds" sourcebook MMSVersion: 2 -name: Lowering the Boom +name: Lowering the Boom [Flee] planet: Castor description: > Playing as the Marik side -- @@ -146,12 +146,13 @@ factions: edge: S bot: - # Optional: which edge am I trying to reach? This is used for the "flee" status - destination: SOUTH - - # Optional: flee = will try to reach the destinationEdge even when not crippled - # forcedwithdrawal = follow the Forced Withdrawal rules - status: flee + # must be pretty berserk to hunt down the Mariks in this scenario + selfpreservation: 2 + fallshame: 2 + hyperaggression: 9 + herdmentality: 3 + bravery: 8 + forcedwithdrawal: false victory: - modify: onlyatend diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms index c80bed8e499..d832e572b54 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms @@ -18,7 +18,7 @@ # # Based on Battlecorps Scenario 3011, Lowering the Boom, originally published in FASA's "Kell Hounds" sourcebook MMSVersion: 2 -name: Lowering the Boom +name: Lowering the Boom [Hunt] planet: Castor description: > Playing as the Kell Hound side -- @@ -114,13 +114,20 @@ factions: piloting: 4 gunnery: 4 + # OPFOR ---------------------------- - name: Thirtieth Marik Militia camo: Free Worlds League/Marik Militia/Marik Militia.jpg deploy: N bot: - destination: north - status: flee + # try to get away + selfpreservation: 8 + fallshame: 8 + hyperaggression: 4 + herdmentality: 1 + bravery: 3 + fleeto: north + flee: true victory: - trigger: @@ -252,15 +259,12 @@ messages: text: | ## Defender's Task - In this scenario, it is your task to save Col. Oliver Nage and his elements of 1st Company, 1st Battalion - from the attacking Kell Hounds. Try to escape by retreating off the north edge of the map with - as many Meks as possible. + In this scenario, it is your task to stop Col. Oliver Nage and his elements of 1st Company, 1st Battalion + from reaching the weapons depot which lies beyond the north edge of the map. They will try to reach and + escape from this edge with as many Meks as possible. - Be careful! Some of your Meks have already sustained damage. - - *Technical note: you can currently retreat off any edge of the battlefield and it will count for victory. - If you do this, Princess will be sad.* - image: loweringboom_map.png + Some of their Meks have already sustained damage. + image: loweringboom_map_reverse.png trigger: type: and triggers: diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/loweringboom_map_reverse.png b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/loweringboom_map_reverse.png new file mode 100644 index 0000000000000000000000000000000000000000..e1961a2380c2dace506b75eaedac29c1472adf52 GIT binary patch literal 109460 zcmV*xKt8{TP)Q;t9;P?(3Bx~hvkcav<96lqBuBvCT$uwP`!Aw|gY_$T-;@QZ9m z*bWa7wqI;3EQ^#$*%U32(JZo?-R!Eav1QSXk7=qYC=DhzWO-oqx-jU6eb?EzuhZ}77iNfX+UQRooVtSaPdRo;L`rw6BWIr5 ze;w}~S!Qsyt)qp!gx6jTJpc0kYW(021K<3Gh|(HwBRW?ot;oyBpZ#Ir<*z=yFa9DQ za_C5Z=MyI)yD!KcZ`}UE-v90V`7W>B@AW(0@bT}p&)3^OeEj>}mvdEk|=gVCCRP}d%OL^K2iZ~g$j`fc^VU;H~i3B2?( zptUAMp=uoBL5$!CyzwW(H-7%Xb>4eB@Z!s$mBvM(X&iV^nb7#a>wm2H_Akl<>zsc- z@a*%Tw843(8;695$`n--=@x2VALxtc1EcHjrB-K3)^@a>38;5=jx@v@sp`e z<0962B!DVlIE>8p^}g!dxE45i6r`==tb-uvLfF^{%+Kzx&dt@pp+kaFn%V}u^QlfL z+`1V!u%P#S)BpDW_6`8IZ8dT4ALBi~zW4p{9$&xPTjJgR*+aeWw!gQ3iT69ld%Wjk zugCkJ%f4TacU-%VeZP;t{;}8Y_-1?mDo+vhlXZ-?95Zv8#s)ft2Bokz(o`0cE1E`8 z77wm78GCwtg>|V;R0?MUO=ZbS&19tL^&VWOt~_N&a4zoDu}z?^EM?cQu_nyU?E}49 zkI97KBb~w!L~u6M>GTY%H-yCl4?>-uZ+sU-3o$^MYk~;2@ieug+c#Xj1j~o-zj6HN zd!DcTJj8aJEOU)R@QtUc9K9LC#dC1v*!}Cg_I=M+z5~GvB2Z=;CxWXTb?xcTWPJG3 z$cfYUuk+>)J+FKVtP`|A(Vma19Zi$!yz@rj?DOjB4e}QmQSTQ?KM*P3?e)(IMehq~ z-xnR<`z%K1nZ$J6(NGwkq_R1Q)lme z^I!fbaQa!Ht|LkTZ5{8S>}b4?C=+?_4LJM4z3W`R95{aJ<8_>e&UPIgc>Blj!k6#8 zh}^s$c;?u>&PU;WM0Dhh9|$jh?cV3JelxIqNT{2LQUX4tI-#xOB1q)5KNY_6t$WuQ zZUhz%K+}L$5PifUgqZ3$2T0(@-xI#}bN8+@83kr$!CKG)F{C;Xp(q9Kz{kLk{z&-N zFWkFMJq`4FU|l+&(4LPVA_Z$C5uLi#T zmju^Dq5|(?;+2gMqF`%}(W#F0pylp$UjISh8$TyBbpQeHNqn#p0)nj_#t2#|>L!wz zyVrT^$APbYOQ@=VD8ajk$rb9hItrsLob1EKhHizlezX@X}<6YVybe7I%b`A`Y#swmvq^%=@G71A}q4jB_8?6N*Z5^F9Jdw!E zyr4tG`UlhrktpeWK;vCRw4k*@>A>uqAR*HDB+4p*5Cu_!(t-h8h=dRc0;5|2Bnghn z2%;5P9{K6(k*|IyUEAOJKi<6yLfsE9{Rt8FL-N=ALD&1?uD?jU^`j!|r-=FqIo^CN z@WM+%zo+p6b>-3mU|JL#Bf%z7F(N4M>2?i12$l6nD>&8`GJ#HMcI!mYnMNt_F%oS= zX$S~9csd<}3s5&nP!l6mO&}LVr_?x!G?ga;+Nib;x79HrP5nLqKk)kkEHsEYP0YM3kjdZn;bqtO5*d`(C%qSv(5G0*Xo+yw~Lsn?&Cg5B^ zBIr`MaW!yYQ9r$ro>WBr%&|dy6eRsgIy=6IDEhJR(2o&8-}buz{>H!k0@p6VnP-LZ zBqA|AQVWoEG|r~Ixwf9z1oB+t{T)JK?NC}My9RM!eT0zeD8O_ywxvXx#(|Awc|wbw zToZK?3TVP&v43SQ55+C09L~9h9BG=%daREgnDnTnyg~rv9sG*u#Ocd01oxJ zpZ}A0?}=hRZG`=J`0Fnwx_+dNkbNTSr-=FqJH}gq`2!l;1bmE$kK|oLh(c3)VoX#* zh%xOOKHb>cf@37&UHW=nq{ZCF2p)=_!MBK)2)Tg}cI#{ld__kSV@P!Zi|fEyq96?_I%>*`RuI|fudA+58ee7LY}L1 zE!sLoFQ=|N)(3QqWCna_X^plHdj&@q(4sJT(jp>hG0u7!b>(qBqKRa>t%DFkNP?pX zc_DZoaTbhz%3(jzi2Cs&=`RK!{mHoKd!y*P@z8hg)!sjXmV1bH+h6|c|JDnwi0Apr zchZ6xLPTPsKQg0ee87khoF_^W=JpDXP3`cW`x=XjB4x93Wc`` zj!7sKN$qr^5_*Usk#IUw7_Ders^gtcf}`WELZJu*pSa=}BQc~4qmAs;$rL!7Rq>hihbwUy@)gHmoWD2c?n>RhL{=o+y>nyl8H+&bA{V)joK0VHNMYIpgOF!s6 zUjW|vaUkobi24aUZml?$j%un&LUm|?5Kx&$dBHl@3XXt+=mnMS5ej_-f#{O}7F;CH zHBDoaK$T}GpE%d;IwC^c77CSu5E6CL)=@e=hUb||;$jS_%pgAE+#bP^R7dX-9KBD1 z+V=T)8^|+F5KsifXp|$J&$dvwBRIA~VQ7WI?K(<`AuVQ2(~xBbof)wH;{qS8z}IeI zK17t3G~kkHq8GAEKq&|w=RJ6^-tW{2F>TB$QyA{36G0hG^dve&6hw#f0sQ?0$K5(e zyI6}N_(+x~p;)Ojt%wcS5cZr;5*)P_G=ggd*-l^6Ov2Nf=t)D=M~=_L&l5@ig+S4F zgO5UNJTo&n z@6S^^t%Qxv8|H?zFomFppD>y5Nk)TpHtN#p*yWGg;3jw*g&tB z;bKVC%ubz%Z-qH+1nUJFM{kf*O#?23)J~VDzG&?NoqmS%+k&H@jY1?6h=eAg?8Zh+ z2=w|n8yl9(AKhTS95b`n$CnFalF{w$5eh{xMuD_~qf0&8>$g0I4=-}^`J)t_9zF)L zJfkQJK78vvZhUl+{%nWFy2PVz*HMw++oua68|#6iM-OoPxg+Fd7asyyk&)*)AN=H9 zZeF=cf2O3eZbxv;v?A^iE_JSGsz@e*qRf~~Ju$|Fl5=4?4HN=p*HATn=gA`pj=ObC zsaU@$eDC+)eK0Tm7!me;`RgYfU4K@{+Wdq6>>E$Ft0(BVa?x}0tY%s#!BC7QG=b^R z;suH#4VqY&ggMcv$=T$FXhG$Qx^aXibvk_b{yI0V4Ozdo!m&e|zH%6AQFBKK!O+-% zYCANo+k+3rcaQ zfhW#AgSRpHZa_lqF?nz_bwrn%tCu#ZhmM8Wg3TM(DLOeJ3U1nvb?12I__ItmH>k!F zT;R4k%4n>M=t6P%!X|DKsOC#HuU{iCQyn{LCa4 zGagxTr4XGs^Pms+ep@S-Dn~hg>51fn(@RlJEQoG-+KP(jr4>a z=D+{PKYU~){V+Y!pEn=8JXiY2~ zf}vBU#bH~xG(jg`v|YeWmKGExAhk=>g(P84R2osAG?7l%U>iq>k?10(9dq|KqC%n; zoKL*4G-(7ch){MlN+}wfM8~qNv)${h3PTJLZvzI!a9HuthwE&v)wrgiFv761mNflKcR&wUAg_y7Fq z!y)Eled0|b9)G6ZT`W3`V>(=DaY9_f!5o?ozQF#tVrrr z4y^!YGNGzHqT`OB)-;|X*La;;$R_nVraPoRCv2^2d??u*Rt(1xUk*6>OpY>8)ecFN zbgEN(vP{v?3XmEC5Ii&r2oWKjPjmsT1aF|OUCXmZq6kf$1VC#eibVf{v~_mKV*--8 z9<<#zTtPZ9MKhp7LDfBeHw2}wVEXUAuU zqVMva$AzLF6N0|;y+8lU&vEderfCwoB}SB12&t1ISdZ3eTso3?+cL{=(WjeS3!58L zZr!X{TirqzLyE3q@u(ui9Q-T`OUo=Sc9~x+Q@2M*+-sI;Y~|5e8bWa{pjGPCZR#Xk z5+h0!N(7ZB+9J!+*tJNSayy$k=X*QXqq@6a0*iPH}ub!mto8H8Hlsu82jTbz3KMXK>u5-B}onL>P)wo3^zIb+>8A5Cx3>?vs5QnAPQ$2La^jUF|FP1VO~>-r&H$i@|=zY7M991o*f}848vO!CaarxHzn_M>C7F(6e<}EH6hupsun2& zuH{Cvj=|NbEl;#UIY(gxe473bfS30lQ8gi|>rtxkw@K%U_4PH%1<%>l)*#pwpS-%!?<;JDpZ=EKo}LSHJT| zT5ikfSGO;=_A) zd@>~cdGpbq2u0s@BJtkH_)!t`o$vj%zx^EJ5iBojsya|tzV+Tn{ms?}M5XS9%jdVa zdF2Y#q(%zO3ooBy<=mLnYnx2GYM+WMosOZJ1mxD3g?SJ!ochJ*87%kl)}=ZhUT5jx zm~J;Pzf=&ilX&S7B+~C1icC|xKvQ{8+o-ZVA5r+=i9V*qH72hR?<1k{G)*9Cq2JZy zMpM~&P?BaDB>+t5cqP`5^DTy^3lMuy|;e_dj@(Lc`hT4s!J5T-t23P~@7Z zBDU==SzDWO^}-gXP9EU+iN#hZ1Usq82a5!ibL0FQbb2L|slzuOj7n3F0@g-MCfvL^ z;l`yQXU;5f^ymUvnGqs1U{P;a~h;@7>2k`tc*|`=aXybwNBNy8c{v>raTR_u~y>{{DaR z6j47Z#|Ljk`n^OEGRfI*QTI^5&mk#%(FaYa>jr zV6fOjWhK^n+}4N%W65hM%Z$9LXeI?8zPHAOw{IcUkZvC7l#21DBcB_h%RZe_6C#Yq z4yAnIs`@M{rczLZNCG^VD*vOsW$_h}M%AIe5XvKy;)&=a{Gq zWqsn!3*o!NEs7!%(Fu(c>1xL-s|^V(#z3ra&x1Qr?zAaq($aCgJ&14*sfE7WSfcJs( z@2_*^qYbL5B`+dWo`7bw0d`z7ojAHiQVJ2 z0&9gU7d_W6dHVAi2TvPLyr9z%MmvZh88l5u{Ad)U_GxO+M=~vFlC#^6;K;5}7(;+4 zh)N4h)1(PLB1q#CFWp4))>Fera5iz?m9xC`)(uQwfSlTSrZ~Jy{oFHsII(KEs4C(c z*E#mx9@8z$`JZla>|~bR2C7FQxqlBNyU|`n?x1UlS+sbb?Q;R zxjI2Y;PAm&W(Eb$MkgGuyl-#TPq-rx=A*M3PHKlBfffNh*5BOsn4Ka zrk2WFC#Pvqxainey~6QRXIWjn4iZwG_OmHj%eT8gW)w
#DJ(xNpXDws|@%g-p@ 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

I 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%0AhX;Ku(c*SjA*cftOQBfG8pn1`hCs2pXTrk0vu% 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%0DLJ-l<- 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 literal 0 HcmV?d00001 diff --git a/megamek/docs/Scenarios/ScenarioV2 HowTo.mms b/megamek/docs/Scenarios/ScenarioV2 HowTo.mms index 08aef7884c6..cbd682474f0 100644 --- a/megamek/docs/Scenarios/ScenarioV2 HowTo.mms +++ b/megamek/docs/Scenarios/ScenarioV2 HowTo.mms @@ -310,11 +310,11 @@ factions: # Optional: use forced Withdrawal, this is true by default forcedwithdrawal: true # Optional: the edge to retreat to, nearest by default; use south, north, west, east, nearest - retreat: nearest + withdrawto: nearest # Optional: flee = true will try to reach the destination edge even when not crippled flee: true # Optional: the edge to flee to; use south, north, west, east, nearest - destination: none + fleeto: none # private boolean goHome = false; // Should I immediately proceed to my home board edge? # private final Set strategicBuildingTargets = new HashSet<>(); // What (besides enemy units) do I want to blow up? diff --git a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java index 77860bb3d65..72e1f4c0853 100644 --- a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java +++ b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java @@ -63,7 +63,6 @@ import megamek.client.IClient; import megamek.client.SBFClient; import megamek.client.bot.BotClient; -import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.bot.princess.Princess; import megamek.client.bot.ui.swing.BotGUI; import megamek.client.ui.Messages; @@ -94,7 +93,6 @@ import megamek.common.preference.PreferenceManager; import megamek.common.scenario.Scenario; import megamek.common.scenario.ScenarioLoader; -import megamek.common.util.AddBotUtil; import megamek.server.sbf.SBFGameManager; import megamek.common.util.EmailService; import megamek.common.util.ImageUtil; @@ -102,7 +100,6 @@ import megamek.logging.MMLogger; import megamek.server.IGameManager; import megamek.server.Server; -import megamek.server.sbf.SBFGameManager; import megamek.server.totalwarfare.TWGameManager; import megamek.utilities.xml.MMXMLUtility; diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java index 2b824070f21..290e711984e 100644 --- a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java @@ -28,6 +28,17 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*; +/** + * This class is a builder for settings for a Princess bot (BehaviorSettings) and also supports parsing + * such settings from a yaml MM scenario definition. The difference to BehaviorSettings itself is + * that this builder can work with incomplete settings, such as only a change to herdmentality or + * only a change to auto-flee. When building the final BehaviorSettings, either default values or + * the values of a given other BehaviorSettings are used where the builder has no value. + * + * @see BehaviorSettings + * @see PrincessSettingsBuilder#build() + * @see PrincessSettingsBuilder#build(BehaviorSettings) + */ @JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, setterVisibility = NONE) @JsonIgnoreProperties(ignoreUnknown = true) public class PrincessSettingsBuilder { @@ -38,8 +49,8 @@ public class PrincessSettingsBuilder { private static final String PRINCESS_FALL_SHAME = "fallshame"; private static final String PRINCESS_AGGRESSION = "hyperaggression"; private static final String PRINCESS_HERDING = "herdmentality"; - private static final String PRINCESS_DESTINATION = "destination"; - private static final String PRINCESS_RETREAT = "retreat"; + private static final String PRINCESS_DESTINATION = "fleeto"; + private static final String PRINCESS_RETREAT = "withdrawto"; private static final String PRINCESS_FLEE = "flee"; private static final String PRINCESS_FORCED_WITHDRAW = "forcedwithdraw"; @@ -75,21 +86,25 @@ public class PrincessSettingsBuilder { private String description = null; + @SuppressWarnings("unused") public PrincessSettingsBuilder selfPreservation(int selfPreservation) { this.selfPreservation = selfPreservation; return this; } + @SuppressWarnings("unused") public PrincessSettingsBuilder fallShame(int fallShame) { this.fallShame = fallShame; return this; } + @SuppressWarnings("unused") public PrincessSettingsBuilder hyperAgression(int hyperAgression) { this.hyperAgression = hyperAgression; return this; } + @SuppressWarnings("unused") public PrincessSettingsBuilder herdMentality(int herdMentality) { this.herdMentality = herdMentality; return this; @@ -136,7 +151,7 @@ public PrincessSettingsBuilder description(String description) { /** * Returns new BehaviorSettings based on the given settings. Settings that are present in this builder - * overwrite the previous settings, others are untouched. + * overwrite the previous settings, otherwise the previous settings are left unchanged. * * @param previousSettings Settings to base the new settings on * @return New BehaviorSettings that incorporate the settings of this builder @@ -193,7 +208,7 @@ public BehaviorSettings build(@Nullable BehaviorSettings previousSettings) { /** * Returns new BehaviorSettings based on the Princess default settings. Settings that are present in this - * builder overwrite the default settings, others are untouched. + * builder overwrite the default settings, others are left at their default values. * * @return New BehaviorSettings that incorporate the settings of this builder */ diff --git a/megamek/src/megamek/common/scenario/ScenarioV2.java b/megamek/src/megamek/common/scenario/ScenarioV2.java index d6f962b506c..bd7017dec6f 100644 --- a/megamek/src/megamek/common/scenario/ScenarioV2.java +++ b/megamek/src/megamek/common/scenario/ScenarioV2.java @@ -41,12 +41,7 @@ import megamek.common.force.Forces; import megamek.common.icons.Camouflage; import megamek.common.icons.FileCamouflage; -import megamek.common.jacksonadapters.BoardDeserializer; -import megamek.common.jacksonadapters.CarryableDeserializer; -import megamek.common.jacksonadapters.MMUReader; -import megamek.common.jacksonadapters.MessageDeserializer; -import megamek.common.jacksonadapters.TriggerDeserializer; -import megamek.common.jacksonadapters.VictoryDeserializer; +import megamek.common.jacksonadapters.*; import megamek.common.planetaryconditions.PlanetaryConditions; import megamek.common.strategicBattleSystems.SBFGame; import megamek.logging.MMLogger; diff --git a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java index b441f55db0f..239db07e2e2 100644 --- a/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java +++ b/megamek/src/megamek/server/scriptedevent/PrincessSettingsEvent.java @@ -23,7 +23,7 @@ import megamek.common.jacksonadapters.PrincessSettingsBuilder; import megamek.common.net.enums.PacketCommand; import megamek.common.net.packets.Packet; -import megamek.server.GameManager; +import megamek.server.totalwarfare.TWGameManager; import megamek.server.IGameManager; import megamek.server.trigger.Trigger; import org.apache.logging.log4j.LogManager; @@ -57,7 +57,7 @@ public void process(IGameManager gameManager) { return; } - GameManager gm = (GameManager) gameManager; + TWGameManager gm = (TWGameManager) gameManager; int id = findPlayerId(playerName, gm); BehaviorSettings newSettings = settingsBuilder.build(gm.getGame().getBotSettings().get(playerName)); gm.getGame().getBotSettings().put(playerName, newSettings); @@ -65,7 +65,7 @@ public void process(IGameManager gameManager) { } private boolean validateData(IGameManager gameManager) { - if (!(gameManager instanceof GameManager gm)) { + if (!(gameManager instanceof TWGameManager gm)) { LogManager.getLogger().error("PrincessSettingsEvent is only available in TW games"); return false; } else if (findPlayerId(playerName, gameManager) == Player.PLAYER_NONE) { From c85b041172e1b0b7a9d9321bd554b0397798b939 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 6 Oct 2024 14:40:53 +0200 Subject: [PATCH 09/14] support markdown formatting for scenario description --- .../LoweringTheBoom/LoweringTheBoom.mms | 5 ++-- .../LoweringTheBoomReverse.mms | 8 ++++-- .../megamek/client/ui/MMMarkdownRenderer.java | 2 +- .../ui/swing/scenario/ScenarioInfoPanel.java | 28 ++++++++++++------- .../ui/swing/util/FlatLafStyleBuilder.java | 4 ++- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms index 94cc1eec97e..2c82afbb38d 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms @@ -20,10 +20,11 @@ MMSVersion: 2 name: Lowering the Boom [Flee] planet: Castor -description: > - Playing as the Marik side -- +description: | Lyran intelligence has found illegal atomic weapons on the Marik world of Castor. Katrina Steiner has authorized an attack to remove the weapons and provide the Kell Hounds with action. + + *Goal: Playing as the Marik force, escape the Kell Hounds.* map: boardrows: 2 diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms index d832e572b54..330fc42b2f2 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms @@ -20,11 +20,12 @@ MMSVersion: 2 name: Lowering the Boom [Hunt] planet: Castor -description: > - Playing as the Kell Hound side -- - Lyran intelligence has found illegal atomic weapons on the Marik world of Castor. Katrina Steiner +description: | + Lyran intelligence has found illegal atomic weapons on the Marik world of Castor. Katrina Steiner has authorized an attack to remove the weapons and provide the Kell Hounds with action. + *Goal: Playing as the Kell Hounds force, catch the Marik forces before they can flee.* + map: boardrows: 2 boards: @@ -126,6 +127,7 @@ factions: hyperaggression: 4 herdmentality: 1 bravery: 3 + # Princess respects the edge she is set to flee from fleeto: north flee: true diff --git a/megamek/src/megamek/client/ui/MMMarkdownRenderer.java b/megamek/src/megamek/client/ui/MMMarkdownRenderer.java index 59e5c02a47e..8ce521e386b 100644 --- a/megamek/src/megamek/client/ui/MMMarkdownRenderer.java +++ b/megamek/src/megamek/client/ui/MMMarkdownRenderer.java @@ -46,7 +46,7 @@ private MMMarkdownRenderer() { } /** - * This method renders markdown-flavored text as HTML. + * This method renders markdown-flavored text as HTML. The result does not include HTML or BODY tags. * * @param input - a String possible containing markdown markup (and html) to be rendered * @return a string rendered to html diff --git a/megamek/src/megamek/client/ui/swing/scenario/ScenarioInfoPanel.java b/megamek/src/megamek/client/ui/swing/scenario/ScenarioInfoPanel.java index 47a144138bc..18d0028be6d 100644 --- a/megamek/src/megamek/client/ui/swing/scenario/ScenarioInfoPanel.java +++ b/megamek/src/megamek/client/ui/swing/scenario/ScenarioInfoPanel.java @@ -18,12 +18,12 @@ */ package megamek.client.ui.swing.scenario; +import megamek.client.ui.MMMarkdownRenderer; import megamek.client.ui.swing.util.*; import megamek.common.scenario.ScenarioV1; import megamek.common.scenario.Scenario; import javax.swing.*; -import javax.swing.border.EmptyBorder; import java.awt.*; /** @@ -36,7 +36,7 @@ public class ScenarioInfoPanel extends JPanel { static final int BASE_MINIMUM_HEIGHT = 100; private final JLabel lblTitle = new JLabel(); - private final DescriptionLabel textDescription2 = new DescriptionLabel(); + private final JTextPane textDescription2 = new DescriptionPane(); public ScenarioInfoPanel() { setBorder(BorderFactory.createCompoundBorder( @@ -52,25 +52,33 @@ public ScenarioInfoPanel() { add(lblTitle); add(Box.createVerticalStrut(10)); add(new DashedSeparator(UIUtil.uiLightGreen(), 0.9f, 2f)); - add(Box.createVerticalStrut(10)); - new FlatLafStyleBuilder().font(FontHandler.notoFont()).apply(textDescription2); - textDescription2.setAlignmentX(0.5f); - textDescription2.setVerticalAlignment(SwingConstants.TOP); - textDescription2.setBorder(new EmptyBorder(0, 10, 0, 10)); add(textDescription2); } protected void updateFromPreset(final Scenario preset) { lblTitle.setText(preset.getName()); - textDescription2.setText("" + preset.getDescription()); + String description = MMMarkdownRenderer.getRenderedHtml(preset.getDescription()); + // bring the paragraph top margin in line (and scale it) + int margin = UIUtil.scaleForGUI(8); + textDescription2.setText("%s".formatted(margin, description)); } - private static class DescriptionLabel extends JLabel { + private static class DescriptionPane extends JTextPane { + + public DescriptionPane() { + setEditable(false); + setContentType("text/html"); + setCaretPosition(0); + setAlignmentX(0.5f); + setBackground(null); + setMargin(new Insets(0, 10, 0, 10)); + } + @Override public Dimension getMaximumSize() { // Fixed sizes do not get scaled by FlatLaf - return new Dimension(UIUtil.scaleForGUI(BASE_MINIMUM_WIDTH), UIUtil.scaleForGUI(BASE_MINIMUM_HEIGHT)); + return UIUtil.scaleForGUI(BASE_MINIMUM_WIDTH, BASE_MINIMUM_HEIGHT); } @Override diff --git a/megamek/src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java b/megamek/src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java index b7a4739cf62..3849c392e5c 100644 --- a/megamek/src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java +++ b/megamek/src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java @@ -30,6 +30,8 @@ */ public final class FlatLafStyleBuilder { + public static final String FLATLAF_STYLE_KEY = "FlatLaf.style"; + private String fontName = ""; private double size = 1; private boolean bold = false; @@ -106,6 +108,6 @@ public void apply(JComponent component) { if ((fontName != null) && !fontName.isBlank()) { styleText += " \"" + fontName + "\""; } - component.putClientProperty("FlatLaf.style", styleText); + component.putClientProperty(FLATLAF_STYLE_KEY, styleText); } } From 9cea6f41a11f4094b7420128aa3bc4a418a139c6 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 30 Oct 2024 10:37:02 +0100 Subject: [PATCH 10/14] allow markdown in scenario description, including unit lists fix --- .../LoweringTheBoom/LoweringTheBoom.mms | 159 ++---------------- .../LoweringTheBoomReverse.mms | 146 +--------------- .../LoweringTheBoom_units_kell.mmu | 78 +++++++++ .../LoweringTheBoom_units_marik.mmu | 106 ++++++++++++ .../ui/swing/scenario/ScenarioInfoPanel.java | 4 +- .../common/jacksonadapters/MMUReader.java | 43 ++--- 6 files changed, 225 insertions(+), 311 deletions(-) create mode 100644 megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_kell.mmu create mode 100644 megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_marik.mmu diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms index 2c82afbb38d..78d69c94ab0 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms @@ -41,6 +41,8 @@ map: newlevel: 3 factions: +- name: obser + - name: Thirtieth Marik Militia camo: Free Worlds League/Marik Militia/Marik Militia.jpg deploy: N @@ -56,93 +58,19 @@ factions: fleefrom: border: north + bot: + # try to get away + selfpreservation: 8 + fallshame: 8 + hyperaggression: 4 + herdmentality: 1 + bravery: 3 + # Princess respects the edge she is set to flee from + fleeto: north + flee: true + units: - - fullname: Thunderbolt TDR-5S - id: 101 - at: [ 12, 29 ] - remaining: - armor: - LT: 2 - CT: 15 - ammo: - CT: - - slot: 11 - shots: 5 - - slot: 12 - shots: 5 - crew: - name: Col. Oliver Nage - portrait: Male/MekWarrior/MW_M_15.png - piloting: 5 - gunnery: 5 - - - fullname: Griffin GRF-1N - id: 102 - at: [ 9, 32 ] - remaining: - armor: - LT: 0 - CT: 15 - internal: - LT: 10 - crits: - RT: 3 - crew: - name: Maj. Abraham Morrison - portrait: Male/MekWarrior/MW_M_13.png - piloting: 4 - gunnery: 4 - - - fullname: Hunchback HBK-4G - id: 103 - at: [ 16, 29 ] - remaining: - armor: - HD: 5 - RL: 10 - ammo: - LT: - - slot: 1 - shots: 3 - - slot: 2 - shots: 3 - crew: - name: Lt. Alicia Devon - piloting: 4 - gunnery: 4 - - - fullname: Centurion CN9-A - id: 104 - at: [ 14, 31 ] - remaining: - armor: - CT: 12 - ammo: - LT: - - slot: 4 - shots: 4 - - slot: 5 - shots: 4 - crew: - name: Sgt. Jonathan Taylor - piloting: 4 - gunnery: 4 - - - fullname: Hermes II HER-2S - id: 105 - at: [ 5, 30 ] - crew: - name: Samantha Blaustein - piloting: 4 - gunnery: 4 - - - fullname: Javelin JVN-10N - id: 106 - at: [ 2, 29 ] - crew: - name: Deborah Ryan - piloting: 4 - gunnery: 4 + include: LoweringTheBoom_units_marik.mmu - name: Kell Hounds, First Battalion camo: MERC - 1st Kell Hounds.gif @@ -167,64 +95,7 @@ factions: atmost: 2 units: - - fullname: Wolverine WVR-6R - id: 201 - deploymentround: 2 - crew: - name: Maj. Salome Ward - piloting: 4 - gunnery: 3 - - - fullname: Shadow Hawk SHD-2H - id: 202 - crew: - name: Lee Kennedy - piloting: 4 - gunnery: 4 - - - fullname: Dervish DV-6M - id: 203 - deploymentround: 2 - ammo: - LT: - - slot: 3 - shots: 4 - RT: - - slot: 3 - shots: 4 - crew: - name: Brian Martell - piloting: 4 - gunnery: 4 - - - fullname: Trebuchet TBT-5N - id: 204 - deploymentround: 2 - crew: - name: Judith Nesmith - piloting: 4 - gunnery: 4 - - - fullname: Phoenix Hawk PXH-1 - id: 205 - crew: - name: Nathan Mack - piloting: 4 - gunnery: 4 - - - fullname: Phoenix Hawk PXH-1 - id: 206 - crew: - name: Stuart O'Grady - piloting: 4 - gunnery: 4 - - - fullname: Jenner JR7-D - id: 207 - crew: - name: Sarah Jette - piloting: 4 - gunnery: 4 + include: LoweringTheBoom_units_kell.mmu messages: - header: Situation diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms index 330fc42b2f2..73385650c2c 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoomReverse.mms @@ -56,64 +56,7 @@ factions: atmost: 2 units: - - fullname: Wolverine WVR-6R - id: 201 - deploymentround: 2 - crew: - name: Maj. Salome Ward - piloting: 4 - gunnery: 3 - - - fullname: Shadow Hawk SHD-2H - id: 202 - crew: - name: Lee Kennedy - piloting: 4 - gunnery: 4 - - - fullname: Dervish DV-6M - id: 203 - deploymentround: 2 - ammo: - LT: - - slot: 3 - shots: 4 - RT: - - slot: 3 - shots: 4 - crew: - name: Brian Martell - piloting: 4 - gunnery: 4 - - - fullname: Trebuchet TBT-5N - id: 204 - deploymentround: 2 - crew: - name: Judith Nesmith - piloting: 4 - gunnery: 4 - - - fullname: Phoenix Hawk PXH-1 - id: 205 - crew: - name: Nathan Mack - piloting: 4 - gunnery: 4 - - - fullname: Phoenix Hawk PXH-1 - id: 206 - crew: - name: Stuart O'Grady - piloting: 4 - gunnery: 4 - - - fullname: Jenner JR7-D - id: 207 - crew: - name: Sarah Jette - piloting: 4 - gunnery: 4 + include: LoweringTheBoom_units_kell.mmu # OPFOR ---------------------------- - name: Thirtieth Marik Militia @@ -140,92 +83,7 @@ factions: modify: onlyatend units: - - fullname: Thunderbolt TDR-5S - id: 101 - at: [ 12, 29 ] - remaining: - armor: - LT: 2 - CT: 15 - ammo: - CT: - - slot: 11 - shots: 5 - - slot: 12 - shots: 5 - crew: - name: Col. Oliver Nage - portrait: Male/MechWarrior/MW_M_15.png - piloting: 5 - gunnery: 5 - - - fullname: Griffin GRF-1N - id: 102 - at: [ 9, 32 ] - remaining: - armor: - LT: 0 - CT: 15 - internal: - LT: 10 - crits: - RT: 3 - crew: - name: Maj. Abraham Morrison - portrait: Male/MechWarrior/MW_M_13.png - piloting: 4 - gunnery: 4 - - - fullname: Hunchback HBK-4G - id: 103 - at: [ 16, 29 ] - remaining: - armor: - HD: 5 - RL: 10 - ammo: - LT: - - slot: 1 - shots: 3 - - slot: 2 - shots: 3 - crew: - name: Lt. Alicia Devon - piloting: 4 - gunnery: 4 - - - fullname: Centurion CN9-A - id: 104 - at: [ 14, 31 ] - remaining: - armor: - CT: 12 - ammo: - LT: - - slot: 4 - shots: 4 - - slot: 5 - shots: 4 - crew: - name: Sgt. Jonathan Taylor - piloting: 4 - gunnery: 4 - - - fullname: Hermes II HER-2S - id: 105 - at: [ 5, 30 ] - crew: - name: Samantha Blaustein - piloting: 4 - gunnery: 4 - - - fullname: Javelin JVN-10N - id: 106 - at: [ 2, 29 ] - crew: - name: Deborah Ryan - piloting: 4 - gunnery: 4 + include: LoweringTheBoom_units_marik.mmu messages: diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_kell.mmu b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_kell.mmu new file mode 100644 index 00000000000..cac926ab014 --- /dev/null +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_kell.mmu @@ -0,0 +1,78 @@ +# +# 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 . +# +# Based on Battlecorps Scenario 3011, Lowering the Boom, originally published in FASA's "Kell Hounds" sourcebook + +- fullname: Wolverine WVR-6R + id: 201 + deploymentround: 2 + crew: + name: Maj. Salome Ward + piloting: 4 + gunnery: 3 + +- fullname: Shadow Hawk SHD-2H + id: 202 + crew: + name: Lee Kennedy + piloting: 4 + gunnery: 4 + +- fullname: Dervish DV-6M + id: 203 + deploymentround: 2 + ammo: + LT: + - slot: 3 + shots: 4 + RT: + - slot: 3 + shots: 4 + crew: + name: Brian Martell + piloting: 4 + gunnery: 4 + +- fullname: Trebuchet TBT-5N + id: 204 + deploymentround: 2 + crew: + name: Judith Nesmith + piloting: 4 + gunnery: 4 + +- fullname: Phoenix Hawk PXH-1 + id: 205 + crew: + name: Nathan Mack + piloting: 4 + gunnery: 4 + +- fullname: Phoenix Hawk PXH-1 + id: 206 + crew: + name: Stuart O'Grady + piloting: 4 + gunnery: 4 + +- fullname: Jenner JR7-D + id: 207 + crew: + name: Sarah Jette + piloting: 4 + gunnery: 4 \ No newline at end of file diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_marik.mmu b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_marik.mmu new file mode 100644 index 00000000000..0d9663bf078 --- /dev/null +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_marik.mmu @@ -0,0 +1,106 @@ +# +# 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 . +# +# Based on Battlecorps Scenario 3011, Lowering the Boom, originally published in FASA's "Kell Hounds" sourcebook + +- fullname: Thunderbolt TDR-5S + id: 101 + at: [ 12, 29 ] + remaining: + armor: + LT: 2 + CT: 15 + ammo: + CT: + - slot: 11 + shots: 5 + - slot: 12 + shots: 5 + crew: + name: Col. Oliver Nage + portrait: Male/MekWarrior/MW_M_15.png + piloting: 5 + gunnery: 5 + +- fullname: Griffin GRF-1N + id: 102 + at: [ 9, 32 ] + remaining: + armor: + LT: 0 + CT: 15 + internal: + LT: 10 + crits: + RT: 3 + crew: + name: Maj. Abraham Morrison + portrait: Male/MekWarrior/MW_M_13.png + piloting: 4 + gunnery: 4 + +- fullname: Hunchback HBK-4G + id: 103 + at: [ 16, 29 ] + remaining: + armor: + HD: 5 + RL: 10 + ammo: + LT: + - slot: 1 + shots: 3 + - slot: 2 + shots: 3 + crew: + name: Lt. Alicia Devon + piloting: 4 + gunnery: 4 + +- fullname: Centurion CN9-A + id: 104 + at: [ 14, 31 ] + remaining: + armor: + CT: 12 + ammo: + LT: + - slot: 4 + shots: 4 + - slot: 5 + shots: 4 + crew: + name: Sgt. Jonathan Taylor + piloting: 4 + gunnery: 4 + +- fullname: Hermes II HER-2S + id: 105 + at: [ 5, 30 ] + crew: + name: Samantha Blaustein + piloting: 4 + gunnery: 4 + +- fullname: Javelin JVN-10N + id: 106 + at: [ 2, 29 ] + crew: + name: Deborah Ryan + piloting: 4 + gunnery: 4 diff --git a/megamek/src/megamek/client/ui/swing/scenario/ScenarioInfoPanel.java b/megamek/src/megamek/client/ui/swing/scenario/ScenarioInfoPanel.java index 18d0028be6d..b5ba101754d 100644 --- a/megamek/src/megamek/client/ui/swing/scenario/ScenarioInfoPanel.java +++ b/megamek/src/megamek/client/ui/swing/scenario/ScenarioInfoPanel.java @@ -20,15 +20,13 @@ import megamek.client.ui.MMMarkdownRenderer; import megamek.client.ui.swing.util.*; -import megamek.common.scenario.ScenarioV1; import megamek.common.scenario.Scenario; import javax.swing.*; import java.awt.*; /** - * This panel displays a single {@link ScenarioV1} object in a well-formatted manner for display in the - * {@link ScenarioChooser}. + * This panel displays a single {@link Scenario} object in a well-formatted manner for display in the {@link ScenarioChooser}. */ public class ScenarioInfoPanel extends JPanel { diff --git a/megamek/src/megamek/common/jacksonadapters/MMUReader.java b/megamek/src/megamek/common/jacksonadapters/MMUReader.java index 5c69b90edcc..db34fdbaa94 100644 --- a/megamek/src/megamek/common/jacksonadapters/MMUReader.java +++ b/megamek/src/megamek/common/jacksonadapters/MMUReader.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Optional; /** * This class has methods for reading objects such as SBF Formations, Units and AlphaStrikeElements @@ -44,7 +43,6 @@ public final class MMUReader { static final String SBF_FORMATION = "SBFFormation"; static final String SBF_UNIT = "SBFUnit"; static final String AS_ELEMENT = "ASElement"; - static final String TW_UNIT = "TWUnit"; static final String SKILL = "skill"; static final String SIZE = "size"; static final String ROLE = "role"; @@ -112,25 +110,34 @@ public List read(JsonNode node, Class objectType) throws IOException List result = new ArrayList<>(); if (node.isArray()) { for (Iterator it = node.elements(); it.hasNext(); ) { - JsonNode arrayNode = it.next(); - parseNode(arrayNode, objectType).ifPresent(result::add); + result.addAll(parseNode(it.next(), objectType)); } + } else if (node.isObject()) { + result.addAll(parseNode(node, objectType)); } else { - parseNode(node, objectType).ifPresent(result::add); + throw new IllegalArgumentException("Can't process file, neither array nor single unit"); } return result; } - private Optional parseNode(JsonNode node, Class objectType) throws IOException { + private List parseNode(JsonNode node, Class objectType) throws IOException { if (node.has(INCLUDE)) { JsonNode node2 = yamlMapper.readTree(new File(currentDirectory, node.get(INCLUDE).textValue())); // add the included nodes to the present node as if they had been written directly in the original file - node2.fieldNames().forEachRemaining(n -> ((ObjectNode) node).set(n, node2.get(n))); + // node can be an array or a single element (object node) + // the included file can have an array or a single element + if (node2.isObject()) { + ((ObjectNode) node).setAll((ObjectNode) node2); + } else if (node2.isArray()) { + return read(node2, objectType); + } else { + throw new IllegalArgumentException("Can't process include file, neither array nor single unit"); + } } if (objectType != null) { // When the object type such as AlphaStrikeElement.class is given, read it directly as such an object - return Optional.of(yamlMapper.treeToValue(node, objectType)); + return List.of(yamlMapper.treeToValue(node, objectType)); } else { // When no type is provided by the method call, the type must be given with the "type:" keyword @@ -138,18 +145,14 @@ private Optional parseNode(JsonNode node, Class objectType) throws IO throw new IOException("Missing type info in MMU file!"); } try { - switch (node.get(TYPE).textValue()) { - case SBF_FORMATION: - return Optional.of(yamlMapper.treeToValue(node, SBFFormation.class)); - case AS_ELEMENT: - return Optional.of(yamlMapper.treeToValue(node, AlphaStrikeElement.class)); - case SBF_UNIT: - return Optional.of(yamlMapper.treeToValue(node, SBFUnit.class)); - default: - return Optional.empty(); - } + return switch (node.get(TYPE).textValue()) { + case SBF_FORMATION -> List.of(yamlMapper.treeToValue(node, SBFFormation.class)); + case AS_ELEMENT -> List.of(yamlMapper.treeToValue(node, AlphaStrikeElement.class)); + case SBF_UNIT -> List.of(yamlMapper.treeToValue(node, SBFUnit.class)); + default -> List.of(); + }; } catch (JsonProcessingException e) { - return Optional.empty(); + return List.of(); } } } @@ -194,4 +197,4 @@ public static void disallowCombinedFields(String objectType, JsonNode node, Stri } } } -} \ No newline at end of file +} From f5b74c9bbf5f770a9ffbdc776cb6a0f9d4e99ba0 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 30 Oct 2024 13:15:21 +0100 Subject: [PATCH 11/14] trigger fix, princess parser fix --- .../common/jacksonadapters/PrincessSettingsBuilder.java | 5 ++--- .../megamek/server/trigger/BattlefieldControlTrigger.java | 8 ++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java index 290e711984e..c76ceb1d541 100644 --- a/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java +++ b/megamek/src/megamek/common/jacksonadapters/PrincessSettingsBuilder.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.annotation.*; import megamek.client.bot.princess.BehaviorSettings; -import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.bot.princess.CardinalEdge; import megamek.client.bot.princess.PrincessException; import megamek.common.annotations.Nullable; @@ -71,7 +70,7 @@ public class PrincessSettingsBuilder { private boolean hasFleeValue = false; @JsonAlias(PRINCESS_FLEE) - private boolean doFlee; + private boolean doFlee = false; @JsonAlias(PRINCESS_DESTINATION) private CardinalEdge fleeDestinationEdge; @@ -157,7 +156,7 @@ public PrincessSettingsBuilder description(String description) { * @return New BehaviorSettings that incorporate the settings of this builder */ public BehaviorSettings build(@Nullable BehaviorSettings previousSettings) { - BehaviorSettings settings = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; + BehaviorSettings settings = new BehaviorSettings(); if (previousSettings != null) { try { settings = previousSettings.getCopy(); diff --git a/megamek/src/megamek/server/trigger/BattlefieldControlTrigger.java b/megamek/src/megamek/server/trigger/BattlefieldControlTrigger.java index 507d0305d6f..f8d1e2c5cad 100644 --- a/megamek/src/megamek/server/trigger/BattlefieldControlTrigger.java +++ b/megamek/src/megamek/server/trigger/BattlefieldControlTrigger.java @@ -18,11 +18,7 @@ */ package megamek.server.trigger; -import megamek.common.Game; -import megamek.common.GunEmplacement; -import megamek.common.IGame; -import megamek.common.MekWarrior; -import megamek.common.TeleMissile; +import megamek.common.*; import megamek.logging.MMLogger; /** @@ -40,7 +36,7 @@ public boolean isTriggered(IGame game, TriggerSituation event) { return twGame.getEntitiesVector().stream() .filter(e -> !e.isOffBoard()) .filter(e -> e.getPosition() != null) - .filter(e -> !(e instanceof MekWarrior)) + .filter(e -> !(e instanceof EjectedCrew)) .filter(e -> !(e instanceof TeleMissile)) .filter(e -> !(e instanceof GunEmplacement)) .map(unit -> game.getPlayer(unit.getOwnerId()).getTeam()) From 0558318243df2f1b3a52afe58c630ae7eb1ce65d Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 30 Oct 2024 13:19:04 +0100 Subject: [PATCH 12/14] merge fix --- .../src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java b/megamek/src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java index f3c13c01da5..557427c2be2 100644 --- a/megamek/src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java +++ b/megamek/src/megamek/client/ui/swing/util/FlatLafStyleBuilder.java @@ -31,8 +31,6 @@ */ public final class FlatLafStyleBuilder { - public static final String FLATLAF_STYLE_KEY = "FlatLaf.style"; - private String fontName = ""; private double size = 1; private boolean bold = false; From fa87fc0592933b36e736cbc5800910b74825f695 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 30 Oct 2024 13:33:58 +0100 Subject: [PATCH 13/14] pilot portraits in LoweringTheBoom --- .../LoweringTheBoom/LoweringTheBoom_units_kell.mmu | 7 +++++++ .../LoweringTheBoom/LoweringTheBoom_units_marik.mmu | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_kell.mmu b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_kell.mmu index cac926ab014..e7e876b7b96 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_kell.mmu +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_kell.mmu @@ -23,6 +23,7 @@ deploymentround: 2 crew: name: Maj. Salome Ward + portrait: Female/MekWarrior/MW_F_34.png piloting: 4 gunnery: 3 @@ -30,6 +31,7 @@ id: 202 crew: name: Lee Kennedy + portrait: Male/MekWarrior/MW_M_20.png piloting: 4 gunnery: 4 @@ -45,6 +47,7 @@ shots: 4 crew: name: Brian Martell + portrait: Male/MekWarrior/MW_M_90.png piloting: 4 gunnery: 4 @@ -53,6 +56,7 @@ deploymentround: 2 crew: name: Judith Nesmith + portrait: Female/MekWarrior/MW_F_4.png piloting: 4 gunnery: 4 @@ -60,6 +64,7 @@ id: 205 crew: name: Nathan Mack + portrait: Male/MekWarrior/MW_M_64.png piloting: 4 gunnery: 4 @@ -67,6 +72,7 @@ id: 206 crew: name: Stuart O'Grady + portrait: Male/MekWarrior/MW_M_16.png piloting: 4 gunnery: 4 @@ -74,5 +80,6 @@ id: 207 crew: name: Sarah Jette + portrait: Female/MekWarrior/MW_F_45.png piloting: 4 gunnery: 4 \ No newline at end of file diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_marik.mmu b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_marik.mmu index 0d9663bf078..9ffd12760fb 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_marik.mmu +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom_units_marik.mmu @@ -69,6 +69,7 @@ shots: 3 crew: name: Lt. Alicia Devon + portrait: Female/MekWarrior/MW_F_35.png piloting: 4 gunnery: 4 @@ -86,6 +87,7 @@ shots: 4 crew: name: Sgt. Jonathan Taylor + portrait: Male/MekWarrior/MW_M_78.png piloting: 4 gunnery: 4 @@ -94,6 +96,7 @@ at: [ 5, 30 ] crew: name: Samantha Blaustein + portrait: Female/MekWarrior/MW_F_25.png piloting: 4 gunnery: 4 @@ -102,5 +105,6 @@ at: [ 2, 29 ] crew: name: Deborah Ryan + portrait: Female/MekWarrior/MW_F_83.png piloting: 4 gunnery: 4 From a6fee3dc3de468813b6c683481ea2716b9e41866 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 30 Oct 2024 15:57:13 +0100 Subject: [PATCH 14/14] scenario fix --- .../scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms | 1 - 1 file changed, 1 deletion(-) diff --git a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms index 78d69c94ab0..57dbf21ec52 100644 --- a/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms +++ b/megamek/data/scenarios/Kell Hounds/LoweringTheBoom/LoweringTheBoom.mms @@ -41,7 +41,6 @@ map: newlevel: 3 factions: -- name: obser - name: Thirtieth Marik Militia camo: Free Worlds League/Marik Militia/Marik Militia.jpg