diff --git a/src/main/java/com/jcloisterzone/XMLUtils.java b/src/main/java/com/jcloisterzone/XMLUtils.java index 39419499..9c757126 100644 --- a/src/main/java/com/jcloisterzone/XMLUtils.java +++ b/src/main/java/com/jcloisterzone/XMLUtils.java @@ -103,4 +103,15 @@ public static Integer attributeIntValue(Element e, String attr, Integer defaultV } return Integer.parseInt(e.getAttribute(attr)); } + + public static String attributeStringValue(Element e, String attr) { + return attributeStringValue(e, attr, null); + } + + public static String attributeStringValue(Element e, String attr, String defaultValue) { + if (!e.hasAttribute(attr)) { + return defaultValue; + } + return e.getAttribute(attr); + } } diff --git a/src/main/java/com/jcloisterzone/engine/Engine.java b/src/main/java/com/jcloisterzone/engine/Engine.java index b706da3a..1e470a25 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,"families", FamiliesCapability.class); Map rules = HashMap.empty(); if (setupMsg.getElements().containsKey("farmers")) { diff --git a/src/main/java/com/jcloisterzone/feature/modifier/StringNonMergingModifier.java b/src/main/java/com/jcloisterzone/feature/modifier/StringNonMergingModifier.java new file mode 100644 index 00000000..8528b268 --- /dev/null +++ b/src/main/java/com/jcloisterzone/feature/modifier/StringNonMergingModifier.java @@ -0,0 +1,24 @@ +package com.jcloisterzone.feature.modifier; + +import com.jcloisterzone.game.setup.SetupQuery; + +public class StringNonMergingModifier extends FeatureModifier { + + public StringNonMergingModifier(String selector, SetupQuery enabledBy) { + super(selector, enabledBy); + } + + @Override + public String mergeValues(String a, String b) { + if (a != null) { + return (b != null && a.equals(b)) ? a : null; + } else { + return (b != null) ? b : null; + } + } + + @Override + public String valueOf(String attr) { + return attr; + } +} diff --git a/src/main/java/com/jcloisterzone/game/capability/FamiliesCapability.java b/src/main/java/com/jcloisterzone/game/capability/FamiliesCapability.java new file mode 100644 index 00000000..3c37069c --- /dev/null +++ b/src/main/java/com/jcloisterzone/game/capability/FamiliesCapability.java @@ -0,0 +1,65 @@ +package com.jcloisterzone.game.capability; + +import com.jcloisterzone.board.*; +import com.jcloisterzone.feature.City; +import com.jcloisterzone.feature.Feature; +import com.jcloisterzone.feature.Structure; +import com.jcloisterzone.feature.modifier.StringNonMergingNonEmptyModifier; +import com.jcloisterzone.game.Capability; +import com.jcloisterzone.game.state.GameState; +import com.jcloisterzone.game.state.PlacedTile; +import com.jcloisterzone.reducers.PlaceTile; +import io.vavr.collection.List; + +import static com.jcloisterzone.XMLUtils.attributeIntValue; +import static com.jcloisterzone.XMLUtils.attributeStringValue; + +import org.w3c.dom.Element; + +public class FamiliesCapability extends Capability { + + public static final StringNonMergingNonEmptyModifier FAMILY = new StringNonMergingNonEmptyModifier("family", null); + + @Override + public Feature initFeature(GameState state, String tileId, Feature feature, Element xml) { + if (feature instanceof City) { + if (attributeIntValue(xml, "pennants", 0) > 0 ) { + String family = "blue"; + if (xml.hasAttribute("family")) { + family = xml.getAttribute("family"); + } + feature = ((City) feature).putModifier(FAMILY, family); + } + } + return feature; + } + + @Override + public boolean isTilePlacementAllowed(GameState state, Tile tile, PlacementOption placement) { + Position pos = placement.getPosition(); + Rotation rot = placement.getRotation(); + + state = (new PlaceTile(tile, pos, rot)).apply(state); + + List cities = state.getTileFeatures2(pos, Structure.class) + .filter(fp -> City.class.isInstance(fp._2)) + .map(t -> (City) t._2) + .toList(); + + if (cities.size()==0) { + // No city + return true; + } + + for(City city : cities) { + if (city.getModifier(state, City.PENNANTS, 0) > 0) { + if (city.getModifier(state, FAMILY, null).equals("")) { + // Family is null, not allowed placement, because families joined due to StringNonMergingModifier = null + return false; + } + } + } + + return true; + } +}