From: Erik V. <ev...@us...> - 2012-09-11 15:05:58
|
data/1835/Map.xml | 4 data/18EU/Map.xml | 17 + rails/algorithms/NetworkVertex.java | 12 - rails/game/Access.java | 278 ++++++++++++++++++++++++++++++ rails/game/MapHex.java | 329 +++++++++++++++--------------------- rails/game/MapManager.java | 122 +++++-------- rails/game/Round.java | 2 rails/game/Stop.java | 264 ++++++++++++++++------------ rails/game/Tile.java | 131 ++++++-------- rails/game/TileI.java | 11 - rails/game/TileManager.java | 2 11 files changed, 703 insertions(+), 469 deletions(-) New commits: commit a93d12400e721257f5d894be59f3e0db030b434b Author: Erik Vos <eri...@xs...> Date: Tue Sep 11 15:51:06 2012 +0200 Stop properties update diff --git a/data/1835/Map.xml b/data/1835/Map.xml index 961ff4a..2ad82bc 100644 --- a/data/1835/Map.xml +++ b/data/1835/Map.xml @@ -33,7 +33,9 @@ <Hex name="E13" tile="0"/> <Hex name="E15" tile="0" cost="50"/> <Hex name="E17" tile="0"/> - <Hex name="E19" tile="-806" city="Berlin" unlaidHomeBlocksTokens="no"/> + <Hex name="E19" tile="-806" city="Berlin" unlaidHomeBlocksTokens="no"> + <Access trainMutexID="Berlin"/> + </Hex> <Hex name="E21" tile="-143" orientation="3"/> <Hex name="F4" tile="-1"/> <Hex name="F6" tile="-1"/> diff --git a/data/18EU/Map.xml b/data/18EU/Map.xml index 3d4462c..5fa0e55 100644 --- a/data/18EU/Map.xml +++ b/data/18EU/Map.xml @@ -1,7 +1,10 @@ <Map tileOrientation="NS" letterOrientation="vertical" even="A"> <Image file="18EU/MapImage.svg" x="16" y="15" scale="0.955"/> + <Defaults> + <Access type="port" runTo="yes" runThrough="yes" loop="yes" score="minor"/> + </Defaults> <!-- Hex name="" tile="" orientation="" value="" impassable="" label="" cost="" value="" port="yes/no" --> - <Hex name="A4" port="yes" value="10" tile="-800" orientation="0"/> + <Hex name="A4" type="port" value="10" tile="-800" orientation="0"/> <Hex name="B7" value="30,50" tile="-903" pic="-939" orientation="1" city="Hamburg"> <Access runThrough="yes"/> </Hex> @@ -18,7 +21,7 @@ <Hex name="E6" tile="0" /> <Hex name="E8" tile="0" /> <Hex name="E10" label="B" tile="-3005" orientation="1" city="Berlin"> - <Access loop="no"/> + <Access loop="no" trainMutexID="Berlin"/> </Hex> <Hex name="E12" tile="-1" city="Thorn"/> <Hex name="E14" value="20,30" tile="-901" orientation="2" city="Warsaw"/> @@ -49,7 +52,7 @@ <Hex name="I10" cost="60" tile="0" /> <Hex name="I12" cost="60" tile="0" /> <Hex name="J1" label="P" tile="-3006" city="Paris"> - <Access loop="no"/> + <Access loop="no" trainMutexID="Paris"/> </Hex> <Hex name="J3" cost="60" tile="0" /> <Hex name="J5" tile="0" /> @@ -82,7 +85,7 @@ <Hex name="N7" tile="0" /> <Hex name="N9" cost="60" tile="-1" city="Salzburg"/> <Hex name="N11" label="V" tile="-3005" city="Vienna"> - <Access loop="no"/> + <Access loop="no" trainMutexID="Vienna"/> </Hex> <Hex name="N13" tile="0" /> <Hex name="O2" tile="0" /> @@ -123,11 +126,11 @@ <Hex name="T3" cost="60" tile="-1" city="Nice"/> <Hex name="T5" tile="-3008" city="Genoa"/> <Hex name="T7" tile="-1" city="Bologna"/> - <Hex name="T9" port="yes" value="10" tile="-800" orientation="3"/> - <Hex name="U2" port="yes" value="10" tile="-800" orientation="3"/> + <Hex name="T9" type="port" value="10" tile="-800" orientation="3"/> + <Hex name="U2" type="port" value="10" tile="-800" orientation="3"/> <Hex name="U4" tile="0" /> <Hex name="U6" tile="-1" city="Florence" reserved="10"/> <Hex name="U8" tile="0" /> - <Hex name="V5" port="yes" value="10" tile="-800" orientation="3"/> + <Hex name="V5" type="port" value="10" tile="-800" orientation="3"/> <Hex name="V7" label="V" value="30,50" tile="-903" orientation="4" city="Rome"/> </Map> diff --git a/rails/algorithms/NetworkVertex.java b/rails/algorithms/NetworkVertex.java index 91356af..d9b42b3 100644 --- a/rails/algorithms/NetworkVertex.java +++ b/rails/algorithms/NetworkVertex.java @@ -228,14 +228,14 @@ public final class NetworkVertex implements Comparable<NetworkVertex> { // check if it has to be removed because it is run-to only // if company == null, then no vertex gets removed if (company != null && !city.isRunToAllowedFor(company)) { - log.info("Vertex is removed"); - return false; + log.info("Vertex is removed"); + return false; } // check if it is a major or minor - if (city.getScoreType() == Stop.Score.MAJOR) { + if (city.getScoreType() == Access.ScoreType.MAJOR) { setStationType(StationType.MAJOR); - } else if (city.getScoreType() == Stop.Score.MINOR) { + } else if (city.getScoreType() == Access.ScoreType.MINOR) { setStationType(StationType.MINOR); } @@ -256,6 +256,10 @@ public final class NetworkVertex implements Comparable<NetworkVertex> { if (hex.getCityName() != null && !hex.getCityName().equals("") && station.getCityName() != null && !station.getCityName().equals("")) { cityName = hex.getCityName() + "." + station.getCityName(); + /* EV: To use the new trainMutexID, the above two lines should be changed to: + && city.getTrainMutexID() != null && !city.getTrainMutexID().equals("")) { + cityName = hex.getCityName() + "." + city.getTrainMutexID(); + */ } } diff --git a/rails/game/Access.java b/rails/game/Access.java new file mode 100644 index 0000000..894d6ad --- /dev/null +++ b/rails/game/Access.java @@ -0,0 +1,278 @@ +package rails.game; + +import rails.common.parser.ConfigurationException; +import rails.util.Util; + + +public class Access { + + /** An Access object represents a set of parameters that may affect how trains can run through stops + * and what revenue is scored. + * The final repository of an Access objects is the class Stop, where all train-run affecting aspects + * come together.<p> + * Stop objects collect all of its Access parameters from Access objects + * in other classes, as listed below, in precedence sequence:<br> + * 1. Specific MapHex Stop (defined in TileSet.xml with 'stop' value > 0)<br> + * 2. Specific Tile Station (defined in TileSet.xml with 'station' value > 0). For preprinted tiles (id<=0) only.<br> + * 3. MapHex (defined in Map.xml with 'stop' value absent or 0)<br> + * 4. Tile (defined in TileSet.xml with 'station' value absent or 0). For preprinted tiles (id<=0) only.<br> + * 5. MapManager default per stop type (defined as default in Map.xml)<br> + * 6. TileManager default per stop type (defined as default in TileSet.xml)<br> + * 7. MapManager general default (defined in Map.xml)<br> + * 8. TileManager general default (defined in TileSet.xml)<br> + * 9. Generic stop-type default (defined in the StopType enum below)<p> + * The precedence rule is, that a parameter that is not explicitly defined at a certain precedence level + * falls through to the next lower level, or ultimately to the stop-type default, as defined in this class. + * <p>For some properties, not all precedence levels apply. + */ + + /** The stop type (city, town, port, mine etc.). + * Normally the type will be derived from the tile properties. + * <p>The stop type can be configured on precedence levels 1-4. + * If still undefined after level 4, it is derived from the tile Station type. + */ + private StopType stopType = null; + + /** Run-to status of any stops on the hex (whether visible or not). CURRENTLY UNUSED. + * Indicates whether or not a single train can run from or to such stops, i.e. either enter or leave it. + * Has no meaning if no stops exist on this hex. + * <p>Values (see RunTo below for definitions): + * <br>- "yes" (default) means that trains of all companies may run to/from this station. + * <br>- "tokenOnly" means that trains may only access the station if it contains a base token + * of the operating company. Applies to the 18Scan off-map hexes. + * <br>- "no" would mean that the hex is inaccessible (like 1851 Birmingham in the early game), + * but this option is not yet useful as there is no provision yet to change this setting + * in an undoable way (no state variable). + * <p>The run-to status can be configured on all precedence levels. + */ + private RunTo runToAllowed = null; + + /** Run-through status of any stops on the hex (whether visible or not). CURRENTLY UNUSED. + * Indicates whether or not a single train can run through such stops, i.e. both enter and leave it. + * Has no meaning if no stops exist on this hex. + * <p>Values (see RunThrough below for definitions): + * <br>- "yes" (default for all except off-map hexes) means that trains of all companies + * may run through this station, unless it is completely filled with foreign base tokens. + * <br>- "tokenOnly" means that trains may only run through the station if it contains a base token + * of the operating company (applies to the 1830 PRR base). + * <br>- "no" (default for off-map hexes) means that no train may run through this hex. + * <p>The run-through status can be configured on all precedence levels. + */ + private RunThrough runThroughAllowed = null; + + /** May trains return to this tile? UNUSED, and probably obsolete. See trainMutexID */ + private Loop loopAllowed = null; + + /** + * Score type indicates whether stops on this hex count as 'major' or 'minor' stops. + * Many games don't make such a difference, but for instance 1835 and 18EU do. + * <p>The stop type can be configured on precedence levels 1-6. + */ + private ScoreType scoreType = null; + + /** An identifier of a train mutex group. Can be any non-empty string. CURRENTLY UNUSED.<p> + * One train may not access more than one stop with the same trainMutexID + * (at least, that is the intention of this item. It is not yet used). + * <p> The trainMutexID can be set on precedence levels 1-6. The default is null. + * */ + private String trainMutexID = null; + + public Access () {} + + public Access (Access copy) { + this.stopType = copy.stopType; + this.runToAllowed = copy.runToAllowed; + this.runThroughAllowed = copy.runThroughAllowed; + this.loopAllowed = copy.loopAllowed; + this.scoreType = copy.scoreType; + this.trainMutexID = copy.trainMutexID; + } + + public Access merge (Access lower) { + + if (lower != null) { + if (stopType == null) stopType = lower.stopType; + if (runToAllowed == null) runToAllowed = lower.runToAllowed; + if (runThroughAllowed == null) runThroughAllowed = lower.runThroughAllowed; + if (loopAllowed == null) loopAllowed = lower.loopAllowed; + if (scoreType == null) scoreType = lower.scoreType; + if (!Util.hasValue(trainMutexID)) trainMutexID = lower.trainMutexID; + } + + return this; + } + + public static StopType parseStopTypeString (String s, String sourceDescription) + throws ConfigurationException { + if (Util.hasValue(s)) { + try { + return StopType.valueOf(s.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ConfigurationException ("Illegal value for " + sourceDescription + +" type property: '"+s+"'", e); + } + } + return null; + } + + public static RunThrough parseRunThroughString (String s, String sourceDescription) + throws ConfigurationException { + if (Util.hasValue(s)) { + try { + return RunThrough.valueOf(s.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ConfigurationException ("Illegal value for " + sourceDescription + +" runThrough property: '"+s+"'", e); + } + } + return null; + } + + public static RunTo parseRunToString (String s, String sourceDescription) + throws ConfigurationException { + if (Util.hasValue(s)) { + try { + return RunTo.valueOf(s.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ConfigurationException ("Illegal value for " + sourceDescription + +" runTo property: '"+s+"'", e); + } + } + return null; + } + + public static Loop parseLoopString (String s, String sourceDescription) + throws ConfigurationException { + if (Util.hasValue(s)) { + try { + return Loop.valueOf(s.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ConfigurationException ("Illegal value for " + sourceDescription + +" loop property: "+s, e); + } + } + return null; + } + + public static ScoreType parseScoreTypeString (String s, String sourceDescription) + throws ConfigurationException { + if (Util.hasValue(s)) { + try { + return ScoreType.valueOf(s.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ConfigurationException ("Illegal value for " + sourceDescription + +" score property: "+s, e); + } + } + return null; + } + + public StopType getStopType() { + return stopType; + } + + public void setStopType(StopType stopType) { + this.stopType = stopType; + } + + public RunTo getRunToAllowed() { + return runToAllowed; + } + + public void setRunToAllowed(RunTo runToAllowed) { + this.runToAllowed = runToAllowed; + } + + public RunThrough getRunThroughAllowed() { + return runThroughAllowed; + } + + public void setRunThroughAllowed(RunThrough runThroughAllowed) { + this.runThroughAllowed = runThroughAllowed; + } + + public Loop getLoopAllowed() { + return loopAllowed; + } + + public void setLoopAllowed(Loop loopAllowed) { + this.loopAllowed = loopAllowed; + } + + public ScoreType getScoreType() { + return scoreType; + } + + public void setScoreType(ScoreType scoreType) { + this.scoreType = scoreType; + } + + public String getTrainMutexID() { + return trainMutexID; + } + + public void setTrainMutexID(String trainMutexID) { + this.trainMutexID = trainMutexID; + } + + // --- Enumerations --- + public enum RunTo { + YES, + NO, + TOKENONLY + } + + public enum RunThrough { + YES, + NO, + TOKENONLY + } + + public enum Loop { + YES, + NO + } + + public enum StopType { + + CITY (RunTo.YES, RunThrough.YES, Loop.YES, ScoreType.MAJOR), + TOWN (RunTo.YES, RunThrough.YES, Loop.YES, ScoreType.MINOR), + HALT (RunTo.YES, RunThrough.YES, Loop.YES, ScoreType.MINOR), + OFFMAP (RunTo.YES, RunThrough.NO, Loop.NO, ScoreType.MAJOR), + PORT (RunTo.YES, RunThrough.NO, Loop.NO, ScoreType.MINOR), + MINE (RunTo.YES, RunThrough.NO, Loop.NO, ScoreType.MINOR), + PASS (RunTo.YES, RunThrough.YES, Loop.NO, ScoreType.MAJOR); + + private RunTo defaultRunToAllowed; + private RunThrough defaultRunThroughAllowed; + private Loop defaultLoopAllowed; + private ScoreType defaultScoreType; + private Access defaultAccessInfo; + + StopType (RunTo runTo, + RunThrough runThrough, + Loop loop, + ScoreType scoreType) { + + defaultAccessInfo = new Access(); + + defaultAccessInfo.setRunToAllowed(runTo); + defaultAccessInfo.setRunThroughAllowed(runThrough); + defaultAccessInfo.setLoopAllowed(loop); + defaultAccessInfo.setScoreType(scoreType); + } + + public RunTo getDefaultRunTo() { return defaultRunToAllowed; } + public RunThrough getDefaultRunThrough() { return defaultRunThroughAllowed; } + public Loop getDefaultLoop() { return defaultLoopAllowed; } + public ScoreType getDefaultScoreType() { return defaultScoreType; } + + public Access getAccessInfoDefaults() { return defaultAccessInfo; } + + } + + public enum ScoreType { + MAJOR, + MINOR + } +} diff --git a/rails/game/MapHex.java b/rails/game/MapHex.java index f02a37a..1ca6846 100644 --- a/rails/game/MapHex.java +++ b/rails/game/MapHex.java @@ -10,11 +10,6 @@ import org.apache.log4j.Logger; import rails.algorithms.RevenueBonusTemplate; import rails.common.LocalText; import rails.common.parser.*; -import rails.game.Stop.Loop; -import rails.game.Stop.RunThrough; -import rails.game.Stop.RunTo; -import rails.game.Stop.Score; -import rails.game.Stop.Type; import rails.game.action.LayTile; import rails.game.model.ModelObject; import rails.game.move.Moveable; @@ -121,43 +116,21 @@ StationHolder, TokenHolder { /** Any open sides against which track may be laid even at board edges (1825) */ protected boolean[] openHexSides; - /** Run-through status of any stops on the hex (whether visible or not). - * Indicates whether or not a single train can run through such stops, i.e. both enter and leave it. - * Has no meaning if no stops exist on this hex. - * <p>Values (see RunThrough below for definitions): - * <br>- "yes" (default for all except off-map hexes) means that trains of all companies - * may run through this station, unless it is completely filled with foreign base tokens. - * <br>- "tokenOnly" means that trains may only run through the station if it contains a base token - * of the operating company (applies to the 1830 PRR base). - * <br>- "no" (default for off-map hexes) means that no train may run through this hex. + /** Access info array with elements: + * [0] for per-hex access parameters. Must exist.<br> + * [1]... for each stop (station; if any). Will remain null if no stop-specific access values exist. + * + * <p>Stop-specific access parameters apply to one specific hex stop only. + * Tile station numbers are defined in Tiles.xml as "city1", ... where "1" is the number. + * (station numbers always start with 1). Hex stop numbers are initially copied from the + * tile station numbers of the preprinted tile on that hex. When upgraded, Rails attempts to transfer + * hex stop numbers unchanged from one tile to the next, based upon existing track connections. + * However, such attempts will often fail when the number of stations changes during an upgrade. + * It is therefore recommended to define access properties per hex stop only for fixed (not upgradeable) + * preprinted tiles + * <p>NOTE: per-station access parameters can also be defined in TileSet.xml. */ - protected RunThrough runThroughAllowed = null; - - /** Run-to status of any stops on the hex (whether visible or not). - * Indicates whether or not a single train can run from or to such stops, i.e. either enter or leave it. - * Has no meaning if no stops exist on this hex. - * <p>Values (see RunTo below for definitions): - * <br>- "yes" (default) means that trains of all companies may run to/from this station. - * <br>- "tokenOnly" means that trains may only access the station if it contains a base token - * of the operating company. Applies to the 18Scan off-map hexes. - * <br>- "no" would mean that the hex is inaccessible (like 1851 Birmingham in the early game), - * but this option is not yet useful as there is no provision yet to change this setting - * in an undoable way (no state variable). - */ - protected RunTo runToAllowed = null; - - /** Loop: may one train touch this hex twice or more? */ - protected Loop loopAllowed = null; - - /** Type of any stops on the hex. - * Normally the type will be derived from the tile properties. - */ - protected Type stopType = null; - - /** - * Score type: do stops on this hex count as major or minor stops with respect to n+m trains? - */ - protected Score scoreType = null; + protected Access[] accessInfo; protected MapManager mapManager = null; @@ -271,59 +244,46 @@ StationHolder, TokenHolder { } // Stop properties - Tag accessTag = tag.getChild("Access"); - if (accessTag != null) { - String runThroughString = accessTag.getAttributeAsString("runThrough"); - if (Util.hasValue(runThroughString)) { - try { - runThroughAllowed = RunThrough.valueOf(runThroughString.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for MapHex" - +name+" runThrough property: "+runThroughString, e); - } - } + accessInfo = new Access[1]; + accessInfo[0] = new Access(); - String runToString = accessTag.getAttributeAsString("runTo"); - if (Util.hasValue(runToString)) { - try { - runToAllowed = RunTo.valueOf(runToString.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for MapHex " - +name+" runTo property: "+runToString, e); - } - } + /* As a shortcut, the stopType (for all stations) can also be set in the Hex tag itself. + * Any different setting in <Access> will override this shortcut setting. + */ + accessInfo[0].setStopType(Access.parseStopTypeString(tag.getAttributeAsString("type"), + "Hex " + name)); - String loopString = accessTag.getAttributeAsString("loop"); - if (Util.hasValue(loopString)) { - try { - loopAllowed = Loop.valueOf(loopString.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for MapHex " - +name+" loop property: "+loopString, e); - } - } + List<Tag> accessTags = tag.getChildren("Access"); + if (accessTags != null) { + for (Tag accessTag : accessTags) { - String typeString = accessTag.getAttributeAsString("type"); - if (Util.hasValue(typeString)) { - try { - stopType = Type.valueOf(typeString.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for MapHex " - +name+" stop type property: "+typeString, e); - } - } + int stationNumber = accessTag.getAttributeAsInteger("station", 0); - String scoreTypeString = accessTag.getAttributeAsString("score"); - if (Util.hasValue(scoreTypeString)) { - try { - scoreType = Score.valueOf(scoreTypeString.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for MapHex " - +name+" score type property: "+scoreTypeString, e); + if (stationNumber > 0) { + if (stationNumber+1 > accessInfo.length) { + accessInfo = Arrays.copyOf(accessInfo, stationNumber+1); + } + if (accessInfo[stationNumber] == null) accessInfo[stationNumber] = new Access(); + } else if (stationNumber == 0) { + // That's OK, accessInfo already exists + } else { + // Please note, that we cannot check the maximum station number yet. + throw new ConfigurationException ("Invalid <Access> stop (staion) number in hex #"+name); } + Access ai = accessInfo[stationNumber]; + ai.setStopType(Access.parseStopTypeString(accessTag.getAttributeAsString("type"), + "Hex " + name)); + ai.setRunThroughAllowed(Access.parseRunThroughString(accessTag.getAttributeAsString("runThrough"), + "Hex " + name)); + ai.setRunToAllowed(Access.parseRunToString(accessTag.getAttributeAsString("runTo"), + "Hex " + name)); + ai.setLoopAllowed(Access.parseLoopString(accessTag.getAttributeAsString("loop"), + "Hex " + name)); + ai.setScoreType(Access.parseScoreTypeString(accessTag.getAttributeAsString("score"), + "Hex " + name)); + ai.setTrainMutexID(accessTag.getAttributeAsString("trainMutexID")); } } - } public void finishConfiguration (GameManagerI gameManager) { @@ -343,15 +303,18 @@ StationHolder, TokenHolder { mStops.put(c.getNumber(), c); } - /* Superseded by new code in Stop - or do we still need it? - if (runThroughAllowed == null) { - runThroughAllowed = currentTile.getColourName().equalsIgnoreCase("red") - ? RunThrough.NO : RunThrough.YES; - } - if (runToAllowed == null) { - runToAllowed = RunTo.YES; - } + /* Make sure that the accessInfo array is minimally as long as 1 + the number of stops on the initial + * (preprinted) tile. WARNING: the length does not (yet) change during updates + * (I'm not aware of any such cases, though [EV]). + * This will almost surely cause problems if the number of stops on a hex would increase. + * It is recommended not to use stop-specific access settings in Map.xml in combination with upgradeable hexes. + * Use TileSet.xml instead. */ + int maxStationNumber = getCurrentTile().getNumStations(); + if (maxStationNumber >= accessInfo.length) { + accessInfo = Arrays.copyOf(accessInfo, maxStationNumber+1); + } + } public void addImpassableSide (int orientation) { @@ -541,7 +504,7 @@ StationHolder, TokenHolder { /** * new wrapper function for the LayTile action that calls the actual - * upgrade mehod + * upgrade method * @param action executed LayTile action */ public void upgrade(LayTile action) { @@ -558,9 +521,9 @@ StationHolder, TokenHolder { */ public void upgrade(TileI newTile, int newRotation, Map<String, Integer> relaidTokens) { - Stop newCity; + Stop newStop; String newTracks; - List<Stop> newCities; + List<Stop> newStops; if (relaidTokens == null) relaidTokens = new HashMap<String, Integer>(); @@ -574,18 +537,18 @@ StationHolder, TokenHolder { // Check for manual handling of tokens for (String compName : relaidTokens.keySet()) { - for (Stop city : stops) { - if (city.hasTokenOf(compName)) { - citiesToStations.put(city, newTile.getStations().get(relaidTokens.get(compName)-1)); + for (Stop stop : stops) { + if (stop.hasTokenOf(compName)) { + citiesToStations.put(stop, newTile.getStations().get(relaidTokens.get(compName)-1)); } } } // Scan the old cities/stations, // and assign new stations where tracks correspond - for (Stop city : stops) { - if (citiesToStations.containsKey(city)) continue; - Station oldStation = city.getRelatedStation(); + for (Stop stop : stops) { + if (citiesToStations.containsKey(stop)) continue; + Station oldStation = stop.getRelatedStation(); int[] oldTrackEnds = getTrackEndPoints(currentTile, currentTileRotation, oldStation); @@ -597,7 +560,7 @@ StationHolder, TokenHolder { for (int j = 0; j < newTrackEnds.length; j++) { if (oldTrackEnds[i] == newTrackEnds[j]) { // Match found! - citiesToStations.put(city, newStation); + citiesToStations.put(stop, newStation); continue station; } } @@ -606,29 +569,29 @@ StationHolder, TokenHolder { } // Map any unassigned cities randomly - city: for (Stop city : stops) { - if (citiesToStations.containsKey(city)) continue; + city: for (Stop stop : stops) { + if (citiesToStations.containsKey(stop)) continue; for (Station newStation : newTile.getStations()) { if (citiesToStations.values().contains(newStation)) continue; - citiesToStations.put(city, newStation); + citiesToStations.put(stop, newStation); continue city; } } // Assign the new Stations to the existing cities - for (Stop city : citiesToStations.keySet()) { - Station newStation = citiesToStations.get(city); - Station oldStation = city.getRelatedStation(); - city.setRelatedStation(newStation); - city.setSlots(newStation.getBaseSlots()); + for (Stop stop : citiesToStations.keySet()) { + Station newStation = citiesToStations.get(stop); + Station oldStation = stop.getRelatedStation(); + stop.setRelatedStation(newStation); + stop.setSlots(newStation.getBaseSlots()); newTracks = getConnectionString(newTile, newRotation, newStation.getNumber()); - city.setTrackEdges(newTracks); + stop.setTrackEdges(newTracks); log.debug("Assigned " - + city.getUniqueId() + + stop.getUniqueId() + " from " + oldStation.getId() + " " @@ -638,7 +601,7 @@ StationHolder, TokenHolder { + " to " + newStation.getId() + " " + newTracks); } - newCities = stops; + newStops = stops; } else { // If the number of stations does change, @@ -646,25 +609,25 @@ StationHolder, TokenHolder { // Build a map from old to new cities, // so that we can move tokens at the end. - newCities = new ArrayList<Stop>(4); - Map<Integer, Stop> mNewCities = new HashMap<Integer, Stop>(4); - Map<Stop, Stop> oldToNewCities = new HashMap<Stop, Stop>(); - Map<Station, Stop> newStationsToCities = + newStops = new ArrayList<Stop>(4); + Map<Integer, Stop> mNewStops = new HashMap<Integer, Stop>(4); + Map<Stop, Stop> oldToNewStops = new HashMap<Stop, Stop>(); + Map<Station, Stop> newStationsToStops = new HashMap<Station, Stop>(); // Scan the old cities/stations, // and assign new stations where tracks correspond - int newCityNumber = 0; - for (Stop oldCity : stops) { - int cityNumber = oldCity.getNumber(); - Station oldStation = oldCity.getRelatedStation(); + int newStopNumber = 0; + for (Stop oldStop : stops) { + int cityNumber = oldStop.getNumber(); + Station oldStation = oldStop.getRelatedStation(); int[] oldTrackEnds = getTrackEndPoints(currentTile, currentTileRotation, oldStation); - log.debug("Old city #" + log.debug("Old stop #" + currentTile.getId() + " city " - + oldCity.getNumber() + + oldStop.getNumber() + ": " + getConnectionString(currentTile, currentTileRotation, oldStation.getNumber())); @@ -682,26 +645,26 @@ StationHolder, TokenHolder { for (int j = 0; j < newTrackEnds.length; j++) { if (oldTrackEnds[i] == newTrackEnds[j]) { // Match found! - if (!newStationsToCities.containsKey(newStation)) { - newCity = - new Stop(this, ++newCityNumber, + if (!newStationsToStops.containsKey(newStation)) { + newStop = + new Stop(this, ++newStopNumber, newStation); - newCities.add(newCity); - mNewCities.put(cityNumber, newCity); - newStationsToCities.put(newStation, newCity); - newCity.setSlots(newStation.getBaseSlots()); + newStops.add(newStop); + mNewStops.put(cityNumber, newStop); + newStationsToStops.put(newStation, newStop); + newStop.setSlots(newStation.getBaseSlots()); } else { - newCity = - newStationsToCities.get(newStation); + newStop = + newStationsToStops.get(newStation); } - oldToNewCities.put(oldCity, newCity); + oldToNewStops.put(oldStop, newStop); newTracks = getConnectionString(newTile, newRotation, newStation.getNumber()); - newCity.setTrackEdges(newTracks); + newStop.setTrackEdges(newTracks); log.debug("Assigned from " - + oldCity.getUniqueId() + + oldStop.getUniqueId() + " #" + currentTile.getId() + "/" @@ -712,7 +675,7 @@ StationHolder, TokenHolder { + getConnectionString(currentTile, currentTileRotation, oldStation.getNumber()) - + " to " + newCity.getUniqueId() + + " to " + newStop.getUniqueId() + " #" + newTile.getId() + "/" + newRotation + " " + newStation.getId() + " " @@ -728,9 +691,9 @@ StationHolder, TokenHolder { // If an old city is not yet connected, check if was // connected to another city it has merged into (1851 Louisville) - for (Stop oldCity : stops) { - if (oldToNewCities.containsKey(oldCity)) continue; - Station oldStation = oldCity.getRelatedStation(); + for (Stop oldStop : stops) { + if (oldToNewStops.containsKey(oldStop)) continue; + Station oldStation = oldStop.getRelatedStation(); int[] oldTrackEnds = getTrackEndPoints(currentTile, currentTileRotation, oldStation); @@ -739,16 +702,16 @@ StationHolder, TokenHolder { if (oldTrackEnds[i] < 0) { int oldStationNumber = -oldTrackEnds[i]; // Find the old city that has this number - for (Stop oldCity2 : stops) { - log.debug("Old city "+oldCity2.getNumber()+" has station "+oldCity2.getRelatedStation().getNumber()); - log.debug(" and links to new city "+oldToNewCities.get(oldCity2)); - if (oldCity2.getRelatedStation().getNumber() + for (Stop oldStop2 : stops) { + log.debug("Old city "+oldStop2.getNumber()+" has station "+oldStop2.getRelatedStation().getNumber()); + log.debug(" and links to new city "+oldToNewStops.get(oldStop2)); + if (oldStop2.getRelatedStation().getNumber() == oldStationNumber - && oldToNewCities.containsKey(oldCity2)) { - newCity = oldToNewCities.get(oldCity2); - oldToNewCities.put(oldCity, newCity); + && oldToNewStops.containsKey(oldStop2)) { + newStop = oldToNewStops.get(oldStop2); + oldToNewStops.put(oldStop, newStop); log.debug("Assigned from " - + oldCity.getUniqueId() + + oldStop.getUniqueId() + " #" + currentTile.getId() + "/" @@ -759,11 +722,11 @@ StationHolder, TokenHolder { + getConnectionString(currentTile, currentTileRotation, oldStation.getNumber()) - + " to " + newCity.getUniqueId() + + " to " + newStop.getUniqueId() + " #" + newTile.getId() + "/" + newRotation + " " - + newCity.getRelatedStation().getId() + " " - + newCity.getTrackEdges()); + + newStop.getRelatedStation().getId() + " " + + newStop.getTrackEdges()); break station; @@ -777,22 +740,22 @@ StationHolder, TokenHolder { // Check if there any new stations not corresponding // to an old city. for (Station newStation : newTile.getStations()) { - if (newStationsToCities.containsKey(newStation)) continue; + if (newStationsToStops.containsKey(newStation)) continue; // Create a new city for such a station. - int cityNumber; - for (cityNumber = 1; mNewCities.containsKey(cityNumber); cityNumber++) + int stopNumber; + for (stopNumber = 1; mNewStops.containsKey(stopNumber); stopNumber++) ; - newCity = new Stop(this, ++newCityNumber, newStation); - newCities.add(newCity); - mNewCities.put(cityNumber, newCity); - newStationsToCities.put(newStation, newCity); - newCity.setSlots(newStation.getBaseSlots()); + newStop = new Stop(this, ++newStopNumber, newStation); + newStops.add(newStop); + mNewStops.put(stopNumber, newStop); + newStationsToStops.put(newStation, newStop); + newStop.setSlots(newStation.getBaseSlots()); newTracks = getConnectionString(newTile, newRotation, newStation.getNumber()); - newCity.setTrackEdges(newTracks); - log.debug("New city added " + newCity.getUniqueId() + " #" + newStop.setTrackEdges(newTracks); + log.debug("New city added " + newStop.getUniqueId() + " #" + newTile.getId() + "/" + newRotation + " " + newStation.getId() + " " + newTracks); } @@ -801,15 +764,15 @@ StationHolder, TokenHolder { Map<TokenI, TokenHolder> tokenDestinations = new HashMap<TokenI, TokenHolder>(); - for (Stop oldCity : stops) { - newCity = oldToNewCities.get(oldCity); - if (newCity != null) { - oldtoken: for (TokenI token : oldCity.getTokens()) { + for (Stop oldStop : stops) { + newStop = oldToNewStops.get(oldStop); + if (newStop != null) { + oldtoken: for (TokenI token : oldStop.getTokens()) { if (token instanceof BaseToken) { // Check if the new city already has such a token PublicCompanyI company = ((BaseToken) token).getCompany(); - for (TokenI token2 : newCity.getTokens()) { + for (TokenI token2 : newStop.getTokens()) { if (token2 instanceof BaseToken && company == ((BaseToken) token2).getCompany()) { // No duplicate tokens in one city! @@ -817,7 +780,7 @@ StationHolder, TokenHolder { log.debug("Duplicate token " + token.getUniqueId() + " moved from " - + oldCity.getName() + " to " + + oldStop.getName() + " to " + company.getName()); ReportBuffer.add(LocalText.getText( "DuplicateTokenRemoved", @@ -827,10 +790,10 @@ StationHolder, TokenHolder { } } } - tokenDestinations.put(token, newCity); + tokenDestinations.put(token, newStop); log.debug("Token " + token.getUniqueId() - + " moved from " + oldCity.getName() + " to " - + newCity.getName()); + + " moved from " + oldStop.getName() + " to " + + newStop.getName()); } if (!tokenDestinations.isEmpty()) { for (TokenI token : tokenDestinations.keySet()) { @@ -847,7 +810,7 @@ StationHolder, TokenHolder { // Replace the tile new TileMove(this, currentTile, currentTileRotation, stops, - newTile, newRotation, newCities); + newTile, newRotation, newStops); /* TODO Further consequences to be processed here, e.g. new routes etc. */ } @@ -1376,23 +1339,11 @@ StationHolder, TokenHolder { return endpoints; } - public RunThrough isRunThroughAllowed() { - return runThroughAllowed; - } - - public RunTo isRunToAllowed() { - return runToAllowed; + public Access getAccessInfo(int stationNumber) { + if (stationNumber < 0 || stationNumber >= accessInfo.length) return null; + if (accessInfo[stationNumber] == null) return new Access(); + return accessInfo[stationNumber]; } - public Loop isLoopAllowed () { - return loopAllowed; - } - public Type getStopType() { - return stopType; - } - - public Score getScoreType() { - return scoreType; - } } \ No newline at end of file diff --git a/rails/game/MapManager.java b/rails/game/MapManager.java index 6cd4307..8d07665 100644 --- a/rails/game/MapManager.java +++ b/rails/game/MapManager.java @@ -7,12 +7,7 @@ import org.apache.log4j.Logger; import rails.common.Config; import rails.common.parser.*; -import rails.game.Stop.Loop; -import rails.game.Stop.RunThrough; -import rails.game.Stop.RunTo; -import rails.game.Stop.Score; -import rails.game.Stop.Type; -import rails.util.Util; +import rails.game.Access.StopType; /** * MapManager configures the map layout from XML @@ -56,10 +51,11 @@ public class MapManager implements ConfigurableComponentI { protected static final int[] yDeltaEW = new int[] { +1, 0, -1, -1, 0, +1 }; // Stop property defaults per stop type - protected Map<Type,RunTo> runToDefaults = new HashMap<Type, RunTo>(); - protected Map<Type,RunThrough> runThroughDefaults = new HashMap<Type, RunThrough>(); - protected Map<Type,Loop> loopDefaults = new HashMap<Type, Loop>(); - protected Map<Type,Score> scoreTypeDefaults = new HashMap<Type, Score>(); + //protected Map<Type,RunTo> runToDefaults = new HashMap<Type, RunTo>(); + //protected Map<Type,RunThrough> runThroughDefaults = new HashMap<Type, RunThrough>(); + //protected Map<Type,Loop> loopDefaults = new HashMap<Type, Loop>(); + //protected Map<Type,ScoreType> scoreTypeDefaults = new HashMap<Type, ScoreType>(); + protected Map<StopType, Access> accessDefaults = new HashMap<StopType, Access>(); protected static Logger log = Logger.getLogger(MapManager.class.getPackage().getName()); @@ -164,71 +160,42 @@ public class MapManager implements ConfigurableComponentI { } // Parse default stop types - Type type; - RunTo runTo; - RunThrough runThrough; - Loop loop; - Score score; - String s; + StopType stopType; + Access accessDetail; Tag defaultsTag = tag.getChild("Defaults"); if (defaultsTag != null) { List<Tag> accessTags = defaultsTag.getChildren("Access"); for (Tag accessTag : accessTags) { - // Type - s = accessTag.getAttributeAsString("type", null); - if (Util.hasValue(s)) { - try { - type = Type.valueOf(s.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for default property type: "+s, e); - } + + // Type. Create an Access object, even if stopType is null. + stopType = Access.parseStopTypeString(accessTag.getAttributeAsString("type"), + "MapManager default"); + if (!accessDefaults.containsKey(stopType)) { + accessDefaults.put(stopType, accessDetail = new Access()); } else { - type = null; // For default defaults - } - // RunTo - s = accessTag.getAttributeAsString("runTo", null); - if (Util.hasValue(s)) { - try { - runTo = RunTo.valueOf(s.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for " - +type+" default runTo property: "+s, e); - } - runToDefaults.put(type, runTo); + accessDetail = accessDefaults.get(stopType); } + // RunThrough - s = accessTag.getAttributeAsString("runThrough", null); - if (Util.hasValue(s)) { - try { - runThrough = RunThrough.valueOf(s.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for " - +type+" default runThrough property: "+s, e); - } - runThroughDefaults.put(type, runThrough); - } + accessDetail.setRunThroughAllowed(Access.parseRunThroughString(accessTag.getAttributeAsString("runThrough"), + "MapManager default, type=" + stopType)); + + // RunTo + accessDetail.setRunToAllowed(Access.parseRunToString(accessTag.getAttributeAsString("runTo"), + "MapManager default, type=" + stopType)); + // Loop - s = accessTag.getAttributeAsString("loop", null); - if (Util.hasValue(s)) { - try { - loop = Loop.valueOf(s.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for " - +type+" default loop property: "+s, e); - } - loopDefaults.put(type, loop); - } + accessDetail.setLoopAllowed(Access.parseLoopString(accessTag.getAttributeAsString("loop"), + "MapManager default, type=" + stopType)); + // Score type (not allowed for a null stop type) - s = accessTag.getAttributeAsString("scoreType", null); - if (type != null && Util.hasValue(s)) { - try { - score = Score.valueOf(s.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new ConfigurationException ("Illegal value for " - +type+" default score type property: "+s, e); - } - scoreTypeDefaults.put(type, score); + if (stopType != null) { + accessDetail.setScoreType(Access.parseScoreTypeString(accessTag.getAttributeAsString("score"), + "MapManager default, type=" + stopType)); } + + // Train Mutex ID (to allow exclusions) + accessDetail.setTrainMutexID(accessTag.getAttributeAsString("trainMutexID")); } } } @@ -553,20 +520,29 @@ public class MapManager implements ConfigurableComponentI { return mapImageUsed; } - public RunTo getRunToDefault(Type type) { - return runToDefaults.containsKey(type) ? runToDefaults.get(type) : null; + public Access getAccessInfoDefaults(StopType stopType) { + return accessDefaults.get(stopType); + } + + /* + public RunTo getRunToDefault(StopType stopType) { + return accessDefaults.containsKey(stopType) ? accessDefaults.get(stopType).getRunToAllowed() : null; } - public RunThrough getRunThroughDefault(Type type) { - return runThroughDefaults.containsKey(type) ? runThroughDefaults.get(type) : null; + public RunThrough getRunThroughDefault(StopType stopType) { + return accessDefaults.containsKey(stopType) ? accessDefaults.get(stopType).getRunThroughAllowed() : null; } - public Loop getLoopDefault(Type type) { - return loopDefaults.containsKey(type) ? loopDefaults.get(type) : null; + public Loop getLoopDefault(StopType stopType) { + return accessDefaults.containsKey(stopType) ? accessDefaults.get(stopType).getLoopAllowed() : null; } - public Score getScoreTypeDefault(Type type) { - return scoreTypeDefaults.containsKey(type) ? scoreTypeDefaults.get(type) : null; + public ScoreType getScoreTypeDefault(StopType stopType) { + return accessDefaults.containsKey(stopType) ? accessDefaults.get(stopType).getScoreType() : null; } + public String getTrainMutexIdDefault (StopType stopType) { + return accessDefaults.containsKey(stopType) ? accessDefaults.get(stopType).getTrainMutexID() : null; + } + */ } diff --git a/rails/game/Round.java b/rails/game/Round.java index 6e113ae..b162080 100644 --- a/rails/game/Round.java +++ b/rails/game/Round.java @@ -373,7 +373,7 @@ public abstract class Round implements RoundI { } else if (capitalisationMode == PublicCompanyI.CAPITALISE_WHEN_BOUGHT) { // Cash goes directly to treasury at each buy (as in 1856 before phase 6) capFactor = 0; - } + } int price = company.getIPOPrice(); cash = capFactor * price; } else { diff --git a/rails/game/Stop.java b/rails/game/Stop.java index 401f18f..8231673 100644 --- a/rails/game/Stop.java +++ b/rails/game/Stop.java @@ -6,6 +6,11 @@ import java.util.List; import org.apache.log4j.Logger; +import rails.game.Access.Loop; +import rails.game.Access.RunThrough; +import rails.game.Access.RunTo; +import rails.game.Access.ScoreType; +import rails.game.Access.StopType; import rails.game.move.Moveable; import rails.game.state.GenericState; import rails.util.Util; @@ -37,65 +42,13 @@ public class Stop implements TokenHolder { private String trackEdges; - private Type type = null; - private RunTo runToAllowed = null; - private RunThrough runThroughAllowed = null; - private Loop loopAllowed = null; - private Score scoreType = null; - protected static Logger log = - Logger.getLogger(Stop.class.getPackage().getName()); - - public enum RunTo { - YES, - NO, - TOKENONLY - } - - public enum RunThrough { - YES, - NO, - TOKENONLY - } - - public enum Loop { - YES, - NO - } - - public enum Type { + private StopType stopType = null; - CITY (RunTo.YES, RunThrough.YES, Loop.YES, Score.MAJOR), - TOWN (RunTo.YES, RunThrough.YES, Loop.YES, Score.MINOR), - OFFMAP (RunTo.YES, RunThrough.NO, Loop.NO, Score.MAJOR); + private Access accessInfo = new Access(); - private RunTo defaultRunToAllowed; - private RunThrough defaultRunThroughAllowed; - private Loop defaultLoopAllowed; - private Score defaultScoreType; - - Type (RunTo runTo, - RunThrough runThrough, - Loop loop, - Score scoreType) { - - this.defaultRunToAllowed = runTo; - this.defaultRunThroughAllowed = runThrough; - this.defaultLoopAllowed = loop; - this.defaultScoreType = scoreType; - } - - public RunTo getDefaultRunTo() { return defaultRunToAllowed; } - public RunThrough getDefaultRunThrough() { return defaultRunThroughAllowed; } - public Loop getDefaultLoop() { return defaultLoopAllowed; } - public Score getDefaultScoreType() { return defaultScoreType; } - - } - - public enum Score { - MAJOR, - MINOR - } + protected static Logger log = + Logger.getLogger(Stop.class.getPackage().getName()); public Stop(MapHex mapHex, int number, Station station) { this.mapHex = mapHex; @@ -112,73 +65,156 @@ public class Stop implements TokenHolder { private void initStopProperties () { + boolean logdetail = false; + String loghex = "J1"; + Station station = relatedStation.get(); + int stationNumber = station.getNumber(); TileI tile = station.getTile(); MapManager mapManager = mapHex.getMapManager(); TileManager tileManager = tile.getTileManager(); - // Stop type - type = mapHex.getStopType(); - if (type == null) type = tile.getStopType(); - if (type == null) { + /* Merge the configured stop property layers, + * using the upper four precedence levels (see class Access). + */ + if (logdetail && mapHex.getName().equalsIgnoreCase(loghex)) { + log.debug("+0+ Hex="+mapHex.getName()+" tile="+tile.getId()+" city="+number + +": stopType="+accessInfo.getStopType() + +" runTo="+accessInfo.getRunToAllowed() + +" runThrough="+accessInfo.getRunThroughAllowed() + +" loop="+accessInfo.getLoopAllowed() + +" scoreType="+accessInfo.getScoreType() + +" mutex="+accessInfo.getTrainMutexID()); + } + accessInfo.merge(mapHex.getAccessInfo(stationNumber)); + if (logdetail && mapHex.getName().equalsIgnoreCase(loghex)) { + log.debug("+1+ Hex="+mapHex.getName()+" tile="+tile.getId()+" city="+number + +": stopType="+accessInfo.getStopType() + +" runTo="+accessInfo.getRunToAllowed() + +" runThrough="+accessInfo.getRunThroughAllowed() + +" loop="+accessInfo.getLoopAllowed() + +" scoreType="+accessInfo.getScoreType() + +" mutex="+accessInfo.getTrainMutexID()); + } + accessInfo.merge(tile.getAccessInfo(stationNumber)); + if (logdetail && mapHex.getName().equalsIgnoreCase(loghex)) { + log.debug("+2+ Hex="+mapHex.getName()+" tile="+tile.getId()+" city="+number + +": stopType="+accessInfo.getStopType() + +" runTo="+accessInfo.getRunToAllowed() + +" runThrough="+accessInfo.getRunThroughAllowed() + +" loop="+accessInfo.getLoopAllowed() + +" scoreType="+accessInfo.getScoreType() + +" mutex="+accessInfo.getTrainMutexID()); + } + accessInfo.merge(mapHex.getAccessInfo(0)); + if (logdetail && mapHex.getName().equalsIgnoreCase(loghex)) { + log.debug("+3+ Hex="+mapHex.getName()+" tile="+tile.getId()+" city="+number + +": stopType="+accessInfo.getStopType() + +" runTo="+accessInfo.getRunToAllowed() + +" runThrough="+accessInfo.getRunThroughAllowed() + +" loop="+accessInfo.getLoopAllowed() + +" scoreType="+accessInfo.getScoreType() + +" mutex="+accessInfo.getTrainMutexID()); + } + accessInfo.merge(tile.getAccessInfo(0)); + if (logdetail && mapHex.getName().equalsIgnoreCase(loghex)) { + log.debug("+4a Hex="+mapHex.getName()+" tile="+tile.getId()+" city="+number + +": stopType="+accessInfo.getStopType() + +" runTo="+accessInfo.getRunToAllowed() + +" runThrough="+accessInfo.getRunThroughAllowed() + +" loop="+accessInfo.getLoopAllowed() + +" scoreType="+accessInfo.getScoreType() + +" mutex="+accessInfo.getTrainMutexID()); + } + + /* Check the stop type. + * If still null at this stage, determine it from the Station properties. + */ + stopType = accessInfo.getStopType(); + if (stopType == null) { String stationType = relatedStation.get().getType(); if (stationType.equals(Station.CITY)) { - type = Type.CITY; + stopType = StopType.CITY; } else if (stationType.equals(Station.TOWN)) { - type = Type.TOWN; + stopType = StopType.TOWN; } else if (stationType.equals(Station.OFF_MAP_AREA)) { - type = Type.OFFMAP; + stopType = StopType.OFFMAP; } else if (stationType.equ... [truncated message content] |