Skip to content

Commit

Permalink
Add ScenarioV2 language for defining carryable objects and test setup…
Browse files Browse the repository at this point in the history
… scenario
  • Loading branch information
SJuliez committed Jul 28, 2024
1 parent d56d89b commit dc1c3a9
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 0 deletions.
23 changes: 23 additions & 0 deletions megamek/data/scenarios/Examples/ExampleV2.mms
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,29 @@ factions:
piloting: 4
gunnery: 3

# Carryable objects. These currently have no real owner, but if they are not pre-deployed, the present
# player will deploy them. When pre-deployed (they have a position), the owner is irrelevant.
objects:

# All objects require a name and weight. Currently the type is automatically Briefcase, later new
# types may be incoming; currently no ID, might make sense
- name: Black Briefcase With Codes
# weight in tons, need not be integer
weight: 1
# pre-deployed at a position
at: [ 2, 3 ]
# currently the only available status: invulnerable
# ideas:
# forbidden_owner (may only be picked up by enemies) - this would require them to have an owner
# respawn (respawns if destroyed or removed from the map)
status: invulnerable

- name: Cargo Crate
weight: 5

- name: Ambassador Hisho
weight: 0.08

- name: "Player B"
home: "E"
units:
Expand Down
49 changes: 49 additions & 0 deletions megamek/data/scenarios/Test Setups/Carryables.mms
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
MMSVersion: 2
name: Test Setup for Carryables
planet: None
description: A few units on a small map with a few carryable objects for testing; most objects predeployed, some to deploy
map: buildingsnobasement/dropport2.board

factions:
- name: Test Player

units:
- fullname: Locust LCT-1M
at: [ 9, 10 ]

- fullname: Hunchback HBK-4G
at: [ 10, 10 ]

- fullname: Charger CGR-1A1
at: [ 11, 10 ]

- fullname: Atlas AS7-D
at: [ 10, 11 ]

objects:
- name: Test Paperweight (invulnerable)
at: [ 14, 11 ]
weight: 0.02
status: invulnerable

- name: Crate (can be damaged)
weight: 1
at: [ 3, 11 ]

- name: This is medium weight (invulnerable)
weight: 50
at: [ 2, 2 ]
status: invulnerable

- name: This is massive (can be damaged)
weight: 150
at: [ 8, 15 ]

- name: Deploy this (can be damaged)
weight: 20

- name: Deploy this 2 (invulnerable)
weight: 3
status: invulnerable


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

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import megamek.common.*;
import megamek.common.annotations.Nullable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static megamek.common.jacksonadapters.MMUReader.requireFields;

public class CarryableDeserializer extends StdDeserializer<CarryableDeserializer.CarryableInfo> {

private static final String NAME = "name";
private static final String WEIGHT = "weight";
private static final String AT = "at";
private static final String X = "x";
private static final String Y = "y";
private static final String STATUS = "status";
private static final String INVULNERABLE = "invulnerable";

public CarryableDeserializer() {
this(null);
}

public CarryableDeserializer(Class<?> vc) {
super(vc);
}

/**
* This is a temporary record for parsed info about a Carryable object (which do not have a position field)
*/
@JsonRootName(value = "Carryable")
@JsonDeserialize(using = CarryableDeserializer.class)
public record CarryableInfo(ICarryable carryable, Coords position) { }

@Override
public CarryableInfo deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
requireFields("Carryable", node, NAME, WEIGHT);

Briefcase briefcase = new Briefcase();

briefcase.setName(node.get(NAME).textValue());
briefcase.setTonnage(node.get(WEIGHT).doubleValue());
assignStatus(briefcase, node);
Coords position = readPosition(briefcase, node);
return new CarryableInfo(briefcase, position);
}

private @Nullable Coords readPosition(Briefcase briefcase, JsonNode node) {
try {
if (node.has(AT)) {
List<Integer> xyList = new ArrayList<>();
node.get(AT).elements().forEachRemaining(n -> xyList.add(n.asInt()));
return new Coords(xyList.get(0), xyList.get(1));

} else if (node.has(X) || node.has(Y)) {
requireFields("Carryable", node, X, Y);
return new Coords(node.get(X).asInt(), node.get(Y).asInt());
}
} catch (Exception e) {
throw new IllegalArgumentException("Illegal position information for carryable " + briefcase, e);
}
return null;
}

private void assignStatus(Briefcase briefcase, JsonNode node) {
if (node.has(STATUS)) {
JsonNode statusNode = node.get(STATUS);
if (statusNode.isContainerNode() && statusNode.isArray()) {
statusNode.iterator().forEachRemaining(n -> parseStatus(briefcase, n.textValue()));
} else if (statusNode.isTextual()) {
parseStatus(briefcase, statusNode.asText());
}
}
}

private void parseStatus(Briefcase briefcase, String statusString) {
switch (statusString) {
case INVULNERABLE:
briefcase.setInvulnerable(true);
break;
default:
throw new IllegalArgumentException("Unknown status " + statusString);
}
}
}
19 changes: 19 additions & 0 deletions megamek/src/megamek/common/scenario/ScenarioV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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.options.SBFRuleOptions;
import megamek.common.planetaryconditions.PlanetaryConditions;
Expand All @@ -48,6 +49,7 @@ public class ScenarioV2 implements Scenario {
private static final String MAPS = "maps";
private static final String UNITS = "units";
private static final String OPTIONS = "options";
private static final String OBJECTS = "objects";

private final JsonNode node;
private final File scenariofile;
Expand Down Expand Up @@ -216,6 +218,23 @@ private List<Player> readPlayers(IGame game) throws ScenarioLoaderException, IOE

//TODO minefields

// Carryables
if (playerNode.has(OBJECTS) && (game instanceof AbstractGame)) {
JsonNode carryablesNode = playerNode.get(OBJECTS);
List<CarryableDeserializer.CarryableInfo> carryables = new MMUReader(scenariofile)
.read(carryablesNode, CarryableDeserializer.CarryableInfo.class).stream()
.filter(o -> o instanceof CarryableDeserializer.CarryableInfo)
.map(o -> (CarryableDeserializer.CarryableInfo) o)
.toList();
for (CarryableDeserializer.CarryableInfo carryableInfo : carryables) {
if (carryableInfo.position() == null) {
player.getGroundObjectsToPlace().add(carryableInfo.carryable());
} else {
((AbstractGame) game).placeGroundObject(carryableInfo.position(), carryableInfo.carryable());
}
}
}

JsonNode unitsNode = playerNode.get(UNITS);
if (game instanceof Game) {
List<Entity> units = new MMUReader(scenariofile).read(unitsNode, Entity.class).stream()
Expand Down

0 comments on commit dc1c3a9

Please sign in to comment.