Skip to content

Commit

Permalink
Robber's Son fan expansion support
Browse files Browse the repository at this point in the history
  • Loading branch information
farin committed Dec 4, 2021
1 parent 2663082 commit a79e330
Show file tree
Hide file tree
Showing 11 changed files with 62 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/jcloisterzone/board/TileBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class TileBuilder {

private static final FeatureModifier[] MONASTERY_MODIFIERS = new FeatureModifier[] { Monastery.SPECIAL_MONASTERY, Monastery.SHRINE, Monastery.CHURCH };
private static final FeatureModifier[] CITY_MODIFIERS = new FeatureModifier[] { City.PENNANTS, City.CATHEDRAL, City.PRINCESS, City.BESIEGED, City.DARMSTADTIUM, City.POINTS_MODIFIER };
private static final FeatureModifier[] ROAD_MODIFIERS = new FeatureModifier[] { Road.INN, Road.LABYRINTH };
private static final FeatureModifier[] ROAD_MODIFIERS = new FeatureModifier[] { Road.INN, Road.LABYRINTH, Road.ROBBERS_SON };

private java.util.List<FeatureModifier> externalModifiers;
private java.util.Map<String, java.util.List<FeatureModifier>> modifiersByType;
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/jcloisterzone/engine/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ private GameSetup createSetupFromMessage(GameSetupMessage setupMsg) {
capabilities = addCapabilities(capabilities, setupMsg,"russian-trap", RussianPromosTrapCapability.class);
capabilities = addCapabilities(capabilities, setupMsg,"watchtower", WatchtowerCapability.class);

capabilities = addCapabilities(capabilities, setupMsg,"robbers-son", RobbersSonCapability.class);

Map<Rule, Object> rules = HashMap.empty();
if (setupMsg.getElements().containsKey("farmers")) {
rules = rules.put(Rule.FARMERS,true);
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/jcloisterzone/feature/Road.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ public class Road extends CompletableFeature<Road> implements ModifiedFeature<Ro

private static final long serialVersionUID = 1L;

// public static IntegerAddModifier WELLS = new IntegerAddModifier("road[wells]", new GameElementQuery("well"));
public static BooleanAnyModifier INN = new BooleanAnyModifier("road[inn]", new GameElementQuery("inn"));
public static BooleanAnyModifier LABYRINTH = new BooleanAnyModifier("road[labyrinth]", new RuleQuery(Rule.LABYRINTH_VARIANT, "advanced"));
public static final BooleanAnyModifier INN = new BooleanAnyModifier("road[inn]", new GameElementQuery("inn"));
public static final BooleanAnyModifier LABYRINTH = new BooleanAnyModifier("road[labyrinth]", new RuleQuery(Rule.LABYRINTH_VARIANT, "advanced"));
public static final BooleanAnyModifier ROBBERS_SON = new BooleanAnyModifier("road[robbers-son]", new GameElementQuery("robbers-son"));

private final Map<FeatureModifier<?>, Object> modifiers;
private final Set<FeaturePointer> openTunnelEnds;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public abstract class FeatureModifier<T> {

public FeatureModifier(String selector, SetupQuery enabledBy) {
this.selector = selector;
this.name = selector.replaceAll("\\w+\\[(\\w+)\\]", "$1");
this.name = selector.replaceAll("\\w+\\[([-\\w]+)\\]", "$1");
this.enabledBy = enabledBy;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class PrincessCapability extends Capability<Void> {

@Override
public GameState onActionPhaseEntered(GameState state) {
if (state.getFlags().contains(Flag.PRINCESS_USED)) {
if (state.getFlags().contains(Flag.NO_PHANTOM)) {
return state;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.jcloisterzone.game.capability;

import com.jcloisterzone.action.ReturnMeepleAction;
import com.jcloisterzone.board.pointer.MeeplePointer;
import com.jcloisterzone.feature.Road;
import com.jcloisterzone.feature.Scoreable;
import com.jcloisterzone.game.Capability;
import com.jcloisterzone.game.state.Flag;
import com.jcloisterzone.game.state.GameState;
import com.jcloisterzone.game.state.PlacedTile;
import com.jcloisterzone.io.message.ReturnMeepleMessage.ReturnMeepleSource;
import io.vavr.collection.Set;

public class RobbersSonCapability extends Capability<Void> {

private static final long serialVersionUID = 1L;

@Override
public GameState onActionPhaseEntered(GameState state) {
if (state.getFlags().contains(Flag.NO_PHANTOM)) {
return state;
}

PlacedTile lastTile = state.getLastPlaced();
Set<MeeplePointer> options = state.getTileFeatures2(lastTile.getPosition(), Scoreable.class)
.filter(t -> {
if (t._2 instanceof Road) {
Road part = (Road) lastTile.getInitialFeaturePartOf(t._1.getLocation())._2;
return part.hasModifier(state, Road.ROBBERS_SON);
} else {
return false;
}
})
.flatMap(featureTuple -> {
Road roadWithReturn = (Road) featureTuple._2;
return roadWithReturn.getFollowers2(state).map(MeeplePointer::new);
})
.toSet();

if (options.isEmpty()) {
return state;
}

return state.appendAction(new ReturnMeepleAction(options, ReturnMeepleSource.ROBBERS_SON));
}
}
7 changes: 4 additions & 3 deletions src/main/java/com/jcloisterzone/game/phase/ActionPhase.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,14 @@ public StepResult handleReturnMeeple(GameState state, ReturnMeepleMessage msg) {

switch (msg.getSource()) {
case PRINCESS:
case ROBBERS_SON:
ReturnMeepleAction princessAction = (ReturnMeepleAction) state.getPlayerActions()
.getActions().find(a -> a instanceof ReturnMeepleAction && ((ReturnMeepleAction) a).getSource() == ReturnMeepleSource.PRINCESS)
.getActions().find(a -> a instanceof ReturnMeepleAction && ((ReturnMeepleAction) a).getSource() == msg.getSource())
.getOrElseThrow(() -> new IllegalArgumentException("Return meeple is not allowed"));
if (princessAction.getOptions().contains(ptr)) {
state = state.addFlag(Flag.PRINCESS_USED);
state = state.addFlag(Flag.NO_PHANTOM);
} else {
throw new IllegalArgumentException("Pointer doesn't match princess action");
throw new IllegalArgumentException("Pointer doesn't match return action");
}
break;
case FESTIVAL:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public StepResult enter(GameState state) {
if (!state.getFlags().isEmpty()) {
state = state.setFlags(state.getFlags()
.remove(Flag.PORTAL_USED)
.remove(Flag.PRINCESS_USED)
.remove(Flag.NO_PHANTOM)
.remove(Flag.FLYING_MACHINE_USED)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public PhantomPhase(RandomGenerator random, Phase defaultNext) {

@Override
public StepResult enter(GameState state) {
if (state.getFlags().contains(Flag.PRINCESS_USED) || state.isPassed()) {
if (state.getFlags().contains(Flag.NO_PHANTOM) || state.isPassed()) {
// The placement of a princess tile with removal of a knight from the city cannot be used as a first
// "follower move" and be followed by placement of the phantom (e.g. into the now-vacated city).
// As per the rules for the princess, "if a knight is removed from the city, the player may not deploy or
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/jcloisterzone/game/state/Flag.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ public enum Flag {
RANSOM_PAID, BAZAAR_AUCTION, TUNNEL_PLACED,

// Cleared at the turn part end
PORTAL_USED, PRINCESS_USED, FLYING_MACHINE_USED
PORTAL_USED, NO_PHANTOM, FLYING_MACHINE_USED
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
public class ReturnMeepleMessage extends AbstractMessage implements ReplayableMessage {

public enum ReturnMeepleSource {
PRINCESS, SIEGE_ESCAPE, FESTIVAL, CORN_CIRCLE, ABBOT_RETURN, TRAP
PRINCESS, SIEGE_ESCAPE, FESTIVAL, CORN_CIRCLE, ABBOT_RETURN, TRAP, ROBBERS_SON
}

private MeeplePointer pointer;
Expand Down

0 comments on commit a79e330

Please sign in to comment.