diff --git a/src/main/java/com/jcloisterzone/engine/Engine.java b/src/main/java/com/jcloisterzone/engine/Engine.java index b706da3a..73eaae7f 100644 --- a/src/main/java/com/jcloisterzone/engine/Engine.java +++ b/src/main/java/com/jcloisterzone/engine/Engine.java @@ -148,6 +148,7 @@ private GameSetup createSetupFromMessage(GameSetupMessage setupMsg) { capabilities = addCapabilities(capabilities, setupMsg,"watchtower", WatchtowerCapability.class); capabilities = addCapabilities(capabilities, setupMsg,"robbers-son", RobbersSonCapability.class); + capabilities = addCapabilities(capabilities, setupMsg,"black-dragon", BlackDragonCapability.class); Map rules = HashMap.empty(); if (setupMsg.getElements().containsKey("farmers")) { diff --git a/src/main/java/com/jcloisterzone/engine/StateGsonBuilder.java b/src/main/java/com/jcloisterzone/engine/StateGsonBuilder.java index 738abb87..0df526fa 100644 --- a/src/main/java/com/jcloisterzone/engine/StateGsonBuilder.java +++ b/src/main/java/com/jcloisterzone/engine/StateGsonBuilder.java @@ -11,18 +11,21 @@ import com.jcloisterzone.feature.Tower; import com.jcloisterzone.figure.Follower; import com.jcloisterzone.figure.Meeple; +import com.jcloisterzone.figure.neutral.BlackDragon; import com.jcloisterzone.figure.neutral.Dragon; import com.jcloisterzone.game.capability.*; import com.jcloisterzone.game.capability.FerriesCapability.FerryToken; import com.jcloisterzone.game.capability.GoldminesCapability.GoldToken; import com.jcloisterzone.game.capability.LittleBuildingsCapability.LittleBuilding; import com.jcloisterzone.game.capability.SheepToken; +import com.jcloisterzone.game.phase.BlackDragonMovePhase; import com.jcloisterzone.game.phase.DragonMovePhase; import com.jcloisterzone.game.phase.Phase; import com.jcloisterzone.game.phase.RussianPromosTrapPhase; import com.jcloisterzone.game.state.*; import com.jcloisterzone.io.MessageParser; import io.vavr.Tuple2; +import io.vavr.Tuple3; import io.vavr.collection.*; import java.lang.reflect.Type; @@ -288,6 +291,19 @@ public JsonElement serializeNeutralFigures(GameState root, JsonSerializationCont data.add("placement", context.serialize(pos)); neutral.add("bigtop", data); } + pos = state.getBlackDragonDeployment(); + if (pos != null) { + BlackDragonCapabilityModel model = root.getCapabilityModel(BlackDragonCapability.class); + JsonObject data = new JsonObject(); + data.add("position", context.serialize(pos)); + if (root.getPhase() instanceof BlackDragonMovePhase) { + JsonArray visitedData = new JsonArray(); + model.getVisited().forEach(p -> visitedData.add(context.serialize(p))); + data.add("visited", visitedData); + data.addProperty("remaining", model.getMoves() - model.getVisited().length()); + } + neutral.add("blackdragon", data); + } return neutral; } @@ -374,6 +390,7 @@ public JsonArray serializePlayEvents(GameState root, JsonSerializationContext co JsonObject item = null; JsonArray turnEvents = null; JsonArray dragonPath = null; + JsonArray blackdragonPath = null; for (PlayEvent ev : root.getEvents()) { if (ev instanceof PlayerTurnEvent) { player = ((PlayerTurnEvent) ev).getPlayer(); @@ -387,6 +404,7 @@ public JsonArray serializePlayEvents(GameState root, JsonSerializationContext co events.add(item); // clean-up dragonPath = null; + blackdragonPath = null; continue; } if (item == null) { @@ -529,6 +547,19 @@ public JsonArray serializePlayEvents(GameState root, JsonSerializationContext co } else { dragonPath.add(context.serialize(nev.getTo())); } + } else if (nev.getNeutralFigure() instanceof BlackDragon) { + if (blackdragonPath == null) { + JsonObject data = new JsonObject(); + blackdragonPath = new JsonArray(); + blackdragonPath.add(context.serialize(nev.getFrom())); + blackdragonPath.add(context.serialize(nev.getTo())); + data.addProperty("type", "blackdragon-moved"); + data.addProperty("figure", nev.getNeutralFigure().getId()); + data.add("path", blackdragonPath); + turnEvents.add(data); + } else { + blackdragonPath.add(context.serialize(nev.getTo())); + } } else { JsonObject data = new JsonObject(); data.addProperty("type", "neutral-moved"); diff --git a/src/main/java/com/jcloisterzone/figure/neutral/BigTop.java b/src/main/java/com/jcloisterzone/figure/neutral/BigTop.java index 58342de9..a587d4a2 100644 --- a/src/main/java/com/jcloisterzone/figure/neutral/BigTop.java +++ b/src/main/java/com/jcloisterzone/figure/neutral/BigTop.java @@ -2,6 +2,7 @@ import com.jcloisterzone.Immutable; import com.jcloisterzone.board.Position; +import com.jcloisterzone.game.state.GameState; @Immutable public class BigTop extends NeutralFigure { diff --git a/src/main/java/com/jcloisterzone/figure/neutral/BlackDragon.java b/src/main/java/com/jcloisterzone/figure/neutral/BlackDragon.java new file mode 100644 index 00000000..996d979f --- /dev/null +++ b/src/main/java/com/jcloisterzone/figure/neutral/BlackDragon.java @@ -0,0 +1,15 @@ +package com.jcloisterzone.figure.neutral; + +import com.jcloisterzone.Immutable; +import com.jcloisterzone.board.Position; + +@Immutable +public class BlackDragon extends NeutralFigure { + + private static final long serialVersionUID = 1L; + + public BlackDragon(String id) { + super(id); + } + +} diff --git a/src/main/java/com/jcloisterzone/figure/neutral/Count.java b/src/main/java/com/jcloisterzone/figure/neutral/Count.java index 9dfc2659..bb193936 100644 --- a/src/main/java/com/jcloisterzone/figure/neutral/Count.java +++ b/src/main/java/com/jcloisterzone/figure/neutral/Count.java @@ -2,6 +2,7 @@ import com.jcloisterzone.Immutable; import com.jcloisterzone.board.pointer.FeaturePointer; +import com.jcloisterzone.game.state.GameState; @Immutable public class Count extends NeutralFigure { diff --git a/src/main/java/com/jcloisterzone/figure/neutral/NeutralFigure.java b/src/main/java/com/jcloisterzone/figure/neutral/NeutralFigure.java index 91ebc042..0cb13bd5 100644 --- a/src/main/java/com/jcloisterzone/figure/neutral/NeutralFigure.java +++ b/src/main/java/com/jcloisterzone/figure/neutral/NeutralFigure.java @@ -28,16 +28,4 @@ public boolean at(GameState state, Structure feature) { FeaturePointer fp = ptr.asFeaturePointer(); return feature.getPlaces().contains(fp); } - - -// @Override -// public void deploy(T at) { -// T origin = getDeployment(); -// game.replaceState(state -> { -// LinkedHashMap, BoardPointer> deployedNeutralFigures = state.getDeployedNeutralFigures(); -// return state.setDeployedNeutralFigures(deployedNeutralFigures.put(this, at)); -// }); -// game.post(new NeutralFigureMoveEvent(game.getActivePlayer(), this, origin, at)); -// } - } diff --git a/src/main/java/com/jcloisterzone/game/GameStatePhaseReducer.java b/src/main/java/com/jcloisterzone/game/GameStatePhaseReducer.java index 4677b242..0ee44e4d 100644 --- a/src/main/java/com/jcloisterzone/game/GameStatePhaseReducer.java +++ b/src/main/java/com/jcloisterzone/game/GameStatePhaseReducer.java @@ -46,6 +46,7 @@ public GameStatePhaseReducer(GameSetup setup, double initialRandom) { next = cleanUpTurnPartPhase = new CleanUpTurnPartPhase(randomGenerator, next); if (setup.contains(CornCircleCapability.class)) next = new CornCirclePhase(randomGenerator, next); + if (setup.contains(BlackDragonCapability.class)) next = new BlackDragonPlacePhase(randomGenerator, next); if (setup.contains(DragonCapability.class) && "after-scoring".equals(setup.getStringRule(Rule.DRAGON_MOVEMENT))) { next = new DragonPhase(randomGenerator, next); } diff --git a/src/main/java/com/jcloisterzone/game/capability/BlackDragonCapability.java b/src/main/java/com/jcloisterzone/game/capability/BlackDragonCapability.java new file mode 100644 index 00000000..fc2a803c --- /dev/null +++ b/src/main/java/com/jcloisterzone/game/capability/BlackDragonCapability.java @@ -0,0 +1,65 @@ +package com.jcloisterzone.game.capability; + +import com.jcloisterzone.Immutable; +import com.jcloisterzone.board.Position; +import com.jcloisterzone.board.pointer.FeaturePointer; +import com.jcloisterzone.feature.Completable; +import com.jcloisterzone.figure.Meeple; +import com.jcloisterzone.figure.neutral.BlackDragon; +import com.jcloisterzone.game.Capability; +import com.jcloisterzone.game.state.GameState; +import com.jcloisterzone.reducers.MoveNeutralFigure; +import com.jcloisterzone.reducers.UndeployMeeple; + +import io.vavr.collection.Vector; +import io.vavr.Tuple2; + + +@Immutable +public class BlackDragonCapability extends Capability { + + private static final long serialVersionUID = 1L; + + public static final Vector EMPTY_VISITED = Vector.empty(); + + @Override + public GameState onStartGame(GameState state) { + state = state.mapNeutralFigures(nf -> nf.setBlackDragon(new BlackDragon("blackdragon.1"))); + state = setModel(state, new BlackDragonCapabilityModel(EMPTY_VISITED, 0)); + return state; + } + + @Override + public boolean isMeepleDeploymentAllowed(GameState state, Position pos) { + return !pos.equals(state.getNeutralFigures().getBlackDragonDeployment()); + } + + @Override + public GameState beforeCompletableScore(GameState state, java.util.Set features) { + if (features.size() > 0) { + state = setModel(state, new BlackDragonCapabilityModel(EMPTY_VISITED, features.size())); + } + return state; + } + + public GameState moveBlackDragon(GameState state, Position pos) { + state = ( + new MoveNeutralFigure<>(state.getNeutralFigures().getBlackDragon(), pos) + ).apply(state); + + state = clearTile(state, pos); + return state; + } + + public GameState clearTile(GameState state, Position pos) { + for (Tuple2 t: state.getDeployedMeeples()) { + Meeple m = t._1; + FeaturePointer fp = t._2; + if (pos.equals(fp.getPosition()) && m.canBeEatenByDragon(state)) { + state = (new UndeployMeeple(m, true)).apply(state); + } + } + + return state; + } +} diff --git a/src/main/java/com/jcloisterzone/game/capability/BlackDragonCapabilityModel.java b/src/main/java/com/jcloisterzone/game/capability/BlackDragonCapabilityModel.java new file mode 100644 index 00000000..aacf32ec --- /dev/null +++ b/src/main/java/com/jcloisterzone/game/capability/BlackDragonCapabilityModel.java @@ -0,0 +1,31 @@ +package com.jcloisterzone.game.capability; + +import com.jcloisterzone.board.Position; +import io.vavr.collection.Vector; + +public class BlackDragonCapabilityModel { + + private final Vector visited; + private final int moves; + + public BlackDragonCapabilityModel(Vector visited, int moves) { + this.visited = visited; + this.moves = moves; + } + + public Vector getVisited() { + return visited; + } + + public BlackDragonCapabilityModel setVisited(Vector visited) { + return new BlackDragonCapabilityModel(visited, moves); + } + + public int getMoves() { + return moves; + } + + public BlackDragonCapabilityModel setMoves(int moves) { + return new BlackDragonCapabilityModel(visited, moves); + } +} diff --git a/src/main/java/com/jcloisterzone/game/phase/BlackDragonMovePhase.java b/src/main/java/com/jcloisterzone/game/phase/BlackDragonMovePhase.java new file mode 100644 index 00000000..6b081fcc --- /dev/null +++ b/src/main/java/com/jcloisterzone/game/phase/BlackDragonMovePhase.java @@ -0,0 +1,100 @@ +package com.jcloisterzone.game.phase; + +import com.jcloisterzone.action.MoveDragonAction; +import com.jcloisterzone.board.Position; +import com.jcloisterzone.board.pointer.BoardPointer; +import com.jcloisterzone.figure.neutral.BlackDragon; +import com.jcloisterzone.figure.neutral.NeutralFigure; +import com.jcloisterzone.game.capability.BlackDragonCapabilityModel; +import com.jcloisterzone.game.capability.CountCapability; +import com.jcloisterzone.game.capability.BlackDragonCapability; +import com.jcloisterzone.game.state.ActionsState; +import com.jcloisterzone.game.state.Flag; +import com.jcloisterzone.game.state.GameState; +import com.jcloisterzone.game.state.PlacedTile; +import com.jcloisterzone.io.message.MoveNeutralFigureMessage; +import com.jcloisterzone.random.RandomGenerator; +import com.jcloisterzone.reducers.MoveNeutralFigure; +import io.vavr.collection.HashSet; +import io.vavr.collection.Set; +import io.vavr.collection.Vector; + +public class BlackDragonMovePhase extends Phase { + + public BlackDragonMovePhase(RandomGenerator random, Phase defaultNext) { + super(random, defaultNext); + } + + @Override + public StepResult enter(GameState state) { + BlackDragonCapabilityModel model = state.getCapabilityModel(BlackDragonCapability.class); + if (model.getVisited().size() == model.getMoves()) { + return next(endBlackDragonMove(state)); + } + Set availMoves = getAvailBlackDragonMoves(state, model.getVisited()); + if (availMoves.isEmpty()) { + return next(endBlackDragonMove(state)); + } + BlackDragon blackdragon = state.getNeutralFigures().getBlackDragon(); + return promote(state.setPlayerActions( + new ActionsState(state.getTurnPlayer(), new MoveDragonAction(blackdragon.getId(), availMoves), false) + )); + } + + private GameState endBlackDragonMove(GameState state) { + state = state.addFlag(Flag.BLACK_DRAGON_MOVED); + state = state.setCapabilityModel(BlackDragonCapability.class, new BlackDragonCapabilityModel(BlackDragonCapability.EMPTY_VISITED, 0)); + state = clearActions(state); + return state; + } + + public Set getAvailBlackDragonMoves(GameState state, Vector visited) { + Set result = HashSet.empty(); + Position blackdragonPosition = state.getNeutralFigures().getBlackDragonDeployment(); + + if (blackdragonPosition != null) { + for (Position offset: Position.ADJACENT.values()) { + Position pos = blackdragonPosition.add(offset); + PlacedTile pt = state.getPlacedTile(pos); + + if (pt == null || CountCapability.isTileForbidden(pt.getTile())) continue; + if (visited.contains(pos)) continue; + + result = result.add(pos); + } + } + return result; + } + + @PhaseMessageHandler + public StepResult handleMoveNeutralFigure(GameState state, MoveNeutralFigureMessage msg) { + BoardPointer ptr = msg.getTo(); + NeutralFigure fig = state.getNeutralFigures().getById(msg.getFigureId()); + + if (!(fig instanceof BlackDragon)) { + throw new IllegalArgumentException("Illegal neutral figure move"); + } + + BlackDragonCapability cap = state.getCapabilities().get(BlackDragonCapability.class); + BlackDragonCapabilityModel model = state.getCapabilityModel(BlackDragonCapability.class); + + Vector visited = model.getVisited(); + Set availMoves = getAvailBlackDragonMoves(state, visited); + + final Position pos = ptr.getPosition(); + if (!availMoves.contains(pos)) { + throw new IllegalArgumentException("Invalid black dragon move."); + } + + state = ( + new MoveNeutralFigure<>((BlackDragon) fig, pos, state.getActivePlayer()) + ).apply(state); + + state = state.mapCapabilityModel(BlackDragonCapability.class, m -> { + return m.setVisited(m.getVisited().append(pos)); + }); + + state = cap.clearTile(state, pos); + return enter(state); + } +} diff --git a/src/main/java/com/jcloisterzone/game/phase/BlackDragonPhase.java b/src/main/java/com/jcloisterzone/game/phase/BlackDragonPhase.java new file mode 100644 index 00000000..2d31cc3b --- /dev/null +++ b/src/main/java/com/jcloisterzone/game/phase/BlackDragonPhase.java @@ -0,0 +1,25 @@ +package com.jcloisterzone.game.phase; + +import com.jcloisterzone.game.capability.BlackDragonCapability; +import com.jcloisterzone.game.capability.BlackDragonCapabilityModel; +import com.jcloisterzone.game.state.GameState; +import com.jcloisterzone.random.RandomGenerator; + +public class BlackDragonPhase extends Phase { + + private BlackDragonMovePhase blackDragonMovePhase; + + public BlackDragonPhase(RandomGenerator random, Phase defaultNext) { + super(random, defaultNext); + blackDragonMovePhase = new BlackDragonMovePhase(random, defaultNext); + } + + @Override + public StepResult enter(GameState state) { + BlackDragonCapabilityModel model = state.getCapabilityModel(BlackDragonCapability.class); + if (model.getMoves() > 0) { + return next(state, blackDragonMovePhase); + } + return next(state); + } +} diff --git a/src/main/java/com/jcloisterzone/game/phase/BlackDragonPlacePhase.java b/src/main/java/com/jcloisterzone/game/phase/BlackDragonPlacePhase.java new file mode 100644 index 00000000..4f17d2a9 --- /dev/null +++ b/src/main/java/com/jcloisterzone/game/phase/BlackDragonPlacePhase.java @@ -0,0 +1,50 @@ +package com.jcloisterzone.game.phase; + +import com.jcloisterzone.Player; +import com.jcloisterzone.event.ScoreEvent; +import com.jcloisterzone.game.capability.BlackDragonCapability; +import com.jcloisterzone.game.state.GameState; +import com.jcloisterzone.random.RandomGenerator; +import io.vavr.Predicates; +import io.vavr.collection.Stream; + +import java.util.HashMap; + +public class BlackDragonPlacePhase extends Phase { + + public BlackDragonPlacePhase(RandomGenerator random, Phase defaultNext) { + super(random, defaultNext); + } + + @Override + public StepResult enter(GameState state) { + BlackDragonCapability cap = state.getCapabilities().get(BlackDragonCapability.class); + + java.util.Map receivedScore = new HashMap<>(); + for (Player player : state.getPlayers().getPlayers() ) { + receivedScore.put(player, 0); + } + + Stream.ofAll(state.getCurrentTurnPartEvents()) + .filter(Predicates.instanceOf(ScoreEvent.class)) + .map(ev -> (ScoreEvent) ev) + .forEach(ev -> { + for (ScoreEvent.ReceivedPoints rp : ev.getPoints()) { + receivedScore.put(rp.getPlayer(), receivedScore.get(rp.getPlayer()) + rp.getPoints()); + } + }); + + for (Player player : state.getPlayers().getPlayers() ) { + int playerReceivedPoints = receivedScore.get(player); + if (playerReceivedPoints > 0) { + int scoreCurrent = state.getPlayers().getScore().get(player.getIndex()); + if (scoreCurrent / 50 > (scoreCurrent - playerReceivedPoints) / 50) { + state = cap.moveBlackDragon(state, state.getLastPlaced().getPosition()); + break; + } + } + } + + return next(state); + } +} diff --git a/src/main/java/com/jcloisterzone/game/phase/CleanUpTurnPartPhase.java b/src/main/java/com/jcloisterzone/game/phase/CleanUpTurnPartPhase.java index 70e10638..673aaa59 100644 --- a/src/main/java/com/jcloisterzone/game/phase/CleanUpTurnPartPhase.java +++ b/src/main/java/com/jcloisterzone/game/phase/CleanUpTurnPartPhase.java @@ -38,6 +38,7 @@ public StepResult enter(GameState state) { .remove(Flag.PORTAL_USED) .remove(Flag.NO_PHANTOM) .remove(Flag.FLYING_MACHINE_USED) + .remove(Flag.BLACK_DRAGON_MOVED) ); } diff --git a/src/main/java/com/jcloisterzone/game/phase/DragonMovePhase.java b/src/main/java/com/jcloisterzone/game/phase/DragonMovePhase.java index 7c8ce300..7e498bb1 100644 --- a/src/main/java/com/jcloisterzone/game/phase/DragonMovePhase.java +++ b/src/main/java/com/jcloisterzone/game/phase/DragonMovePhase.java @@ -67,6 +67,7 @@ public Set getAvailDragonMoves(GameState state, Vector visit BoardPointer fairyPtr = state.getNeutralFigures().getFairyDeployment(); Position fairyPosition = fairyPtr == null ? null : fairyPtr.getPosition(); Position dragonPosition = state.getNeutralFigures().getDragonDeployment(); + Position blackdragonPosition = state.getNeutralFigures().getBlackDragonDeployment(); for (Position offset: Position.ADJACENT.values()) { Position pos = dragonPosition.add(offset); @@ -75,6 +76,7 @@ public Set getAvailDragonMoves(GameState state, Vector visit if (pt == null || CountCapability.isTileForbidden(pt.getTile())) continue; if (visited.contains(pos)) continue; if (pos.equals(fairyPosition)) continue; + if (pos.equals(blackdragonPosition)) continue; result = result.add(pos); } diff --git a/src/main/java/com/jcloisterzone/game/phase/ScoringPhase.java b/src/main/java/com/jcloisterzone/game/phase/ScoringPhase.java index 6f40969d..75f17f4b 100644 --- a/src/main/java/com/jcloisterzone/game/phase/ScoringPhase.java +++ b/src/main/java/com/jcloisterzone/game/phase/ScoringPhase.java @@ -13,24 +13,29 @@ import com.jcloisterzone.game.ScoreFeatureReducer; import com.jcloisterzone.game.capability.*; import com.jcloisterzone.game.capability.TunnelCapability.Tunnel; +import com.jcloisterzone.game.state.Flag; import com.jcloisterzone.game.state.GameState; import com.jcloisterzone.game.state.PlacedTile; import com.jcloisterzone.random.RandomGenerator; +import com.jcloisterzone.Player; import com.jcloisterzone.reducers.ScoreCompletable; import com.jcloisterzone.reducers.ScoreField; import com.jcloisterzone.reducers.ScoreFieldWhenBarnIsConnected; import com.jcloisterzone.reducers.UndeployMeeples; import io.vavr.Predicates; import io.vavr.Tuple2; +import io.vavr.Tuple3; import io.vavr.collection.*; - public class ScoringPhase extends Phase { private java.util.Map completedMutable = new java.util.HashMap<>(); + private BlackDragonMovePhase blackdragonMovePhase; + public ScoringPhase(RandomGenerator random, Phase defaultNext) { super(random, defaultNext); + blackdragonMovePhase = new BlackDragonMovePhase(random, this); } private void collectCompletedOnTile(GameState state, PlacedTile tile) { @@ -99,8 +104,6 @@ public StepResult enter(GameState state) { PlacedTile lastPlaced = state.getLastPlaced(); Position pos = lastPlaced.getPosition(); - Map deployedWagonsBefore = getDeployedWagons(state); - collectCompletedOnTile(state, lastPlaced); collectCompletedOnAdjacentEdges(state, pos); // closed by abbey, city gates ... etc @@ -132,6 +135,7 @@ public StepResult enter(GameState state) { } } + Map deployedWagonsBefore = getDeployedWagons(state); for (Capability cap : state.getCapabilities().toSeq()) { state = cap.beforeCompletableScore(state, completedMutable.keySet()); @@ -182,7 +186,7 @@ public StepResult enter(GameState state) { for (Capability cap : state.getCapabilities().toSeq()) { state = cap.onTurnScoring(state, scored); } - + if (!deployedWagonsBefore.isEmpty()) { Set deployedWagonsAfter = getDeployedWagons(state).keySet(); Set returnedVagons = deployedWagonsBefore.keySet().diff(deployedWagonsAfter); diff --git a/src/main/java/com/jcloisterzone/game/state/Flag.java b/src/main/java/com/jcloisterzone/game/state/Flag.java index 5f7336c7..b73bf953 100644 --- a/src/main/java/com/jcloisterzone/game/state/Flag.java +++ b/src/main/java/com/jcloisterzone/game/state/Flag.java @@ -5,5 +5,5 @@ public enum Flag { RANSOM_PAID, BAZAAR_AUCTION, TUNNEL_PLACED, // Cleared at the turn part end - PORTAL_USED, NO_PHANTOM, FLYING_MACHINE_USED + PORTAL_USED, NO_PHANTOM, FLYING_MACHINE_USED, BLACK_DRAGON_MOVED } \ No newline at end of file diff --git a/src/main/java/com/jcloisterzone/game/state/NeutralFiguresState.java b/src/main/java/com/jcloisterzone/game/state/NeutralFiguresState.java index 0538d157..5541c6f3 100644 --- a/src/main/java/com/jcloisterzone/game/state/NeutralFiguresState.java +++ b/src/main/java/com/jcloisterzone/game/state/NeutralFiguresState.java @@ -20,15 +20,16 @@ public class NeutralFiguresState implements Serializable { private final Witch witch; private final Count count; private final BigTop bigtop; + private final BlackDragon blackdragon; private final LinkedHashMap, BoardPointer> deployedNeutralFigures; public NeutralFiguresState() { - this(null, null, null, null, null, null, LinkedHashMap.empty()); + this(null, null, null, null, null, null, null,LinkedHashMap.empty()); } public NeutralFiguresState( - Dragon dragon, Fairy fairy, Mage mage, Witch witch, Count count, BigTop bigtop, + Dragon dragon, Fairy fairy, Mage mage, Witch witch, Count count, BigTop bigtop, BlackDragon blackdragon, LinkedHashMap, BoardPointer> deployedNeutralFigures ) { this.dragon = dragon; @@ -37,35 +38,40 @@ public NeutralFiguresState( this.witch = witch; this.count = count; this.bigtop = bigtop; + this.blackdragon = blackdragon; this.deployedNeutralFigures = deployedNeutralFigures; } public NeutralFiguresState setDragon(Dragon dragon) { - return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, deployedNeutralFigures); + return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, blackdragon, deployedNeutralFigures); } public NeutralFiguresState setFairy(Fairy fairy) { - return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, deployedNeutralFigures); + return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, blackdragon, deployedNeutralFigures); } public NeutralFiguresState setMage(Mage mage) { - return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, deployedNeutralFigures); + return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, blackdragon, deployedNeutralFigures); } public NeutralFiguresState setWitch(Witch witch) { - return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, deployedNeutralFigures); + return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, blackdragon, deployedNeutralFigures); } public NeutralFiguresState setCount(Count count) { - return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, deployedNeutralFigures); + return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, blackdragon, deployedNeutralFigures); } public NeutralFiguresState setBigTop(BigTop bigtop) { - return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, deployedNeutralFigures); + return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, blackdragon, deployedNeutralFigures); + } + + public NeutralFiguresState setBlackDragon(BlackDragon blackdragon) { + return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, blackdragon, deployedNeutralFigures); } public NeutralFiguresState setDeployedNeutralFigures(LinkedHashMap, BoardPointer> deployedNeutralFigures) { - return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, deployedNeutralFigures); + return new NeutralFiguresState(dragon, fairy, mage, witch, count, bigtop, blackdragon, deployedNeutralFigures); } public NeutralFigure getById(String figureId) { @@ -75,6 +81,7 @@ public NeutralFigure getById(String figureId) { if (witch != null && figureId.equals(witch.getId())) return witch; if (count != null && figureId.equals(count.getId())) return count; if (bigtop != null && figureId.equals(bigtop.getId())) return bigtop; + if (blackdragon != null && figureId.equals(blackdragon.getId())) return blackdragon; return null; } @@ -134,4 +141,16 @@ public LinkedHashMap, BoardPointer> getDeployedNeutralFigures() return deployedNeutralFigures; } + public BlackDragon getBlackDragon() { + return blackdragon; + } + + public Position getBlackDragonDeployment() { + var bp = deployedNeutralFigures.get(blackdragon).getOrNull(); + if (bp != null) { + return bp.getPosition(); + } + return null; + } + } diff --git a/src/main/java/com/jcloisterzone/reducers/AbstractUndeploy.java b/src/main/java/com/jcloisterzone/reducers/AbstractUndeploy.java index aa6f1a84..8414968b 100644 --- a/src/main/java/com/jcloisterzone/reducers/AbstractUndeploy.java +++ b/src/main/java/com/jcloisterzone/reducers/AbstractUndeploy.java @@ -1,15 +1,20 @@ package com.jcloisterzone.reducers; import com.jcloisterzone.Player; +import com.jcloisterzone.board.pointer.BoardPointer; import com.jcloisterzone.board.pointer.FeaturePointer; import com.jcloisterzone.event.MeepleReturned; +import com.jcloisterzone.event.NeutralFigureReturned; import com.jcloisterzone.event.PlayEvent; import com.jcloisterzone.feature.Structure; import com.jcloisterzone.figure.Builder; import com.jcloisterzone.figure.Follower; import com.jcloisterzone.figure.Meeple; import com.jcloisterzone.figure.Pig; +import com.jcloisterzone.figure.neutral.NeutralFigure; import com.jcloisterzone.game.state.GameState; +import com.jcloisterzone.game.state.NeutralFiguresState; + import io.vavr.Tuple2; import io.vavr.collection.LinkedHashMap; import io.vavr.collection.Stream; @@ -20,7 +25,7 @@ protected GameState undeploy(GameState state, PlayEvent.PlayEventMeta meta, Meep LinkedHashMap deployedMeeples = state.getDeployedMeeples(); state = state.setDeployedMeeples(deployedMeeples.remove(meeple)); state = state.appendEvent( - new MeepleReturned(meta, meeple, source, forced) + new MeepleReturned(meta, meeple, source, forced) ); return state; }