package rabbitescape.engine; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import rabbitescape.engine.ChangeDescription.State; import rabbitescape.engine.util.LookupItem2D; import rabbitescape.engine.util.Position; import rabbitescape.engine.util.Util; import rabbitescape.engine.util.WaterUtil; public class WaterRegion extends Thing implements LookupItem2D { /** * The list of directions that this region is connected in. Note that this * does not mean that water can necessarily flow that way, because the cell * in that direction may have no water region, or it may have a region that * is not connected to here (e.g. two adjacent left ramps). */ private Set<CellularDirection> connections; /** The amount of water that can stay here without being under pressure. */ public int capacity; /** The amount of water stored here. */ private int contents; /** The water being transferred from here this tick. */ private Map<CellularDirection, Integer> flow = new HashMap<>(); /** Does this region need updating? */ public final boolean outsideWorld; public WaterRegion( int x, int y, Set<CellularDirection> connections, int capacity ) { this( x, y, connections, capacity, false ); } public WaterRegion( int x, int y, Set<CellularDirection> connections, int capacity, boolean outsideWorld ) { this( x, y, connections, capacity, 0, outsideWorld ); } public WaterRegion( int x, int y, Set<CellularDirection> connections, int capacity, int contents, boolean outsideWorld ) { super( x, y, State.WATER_REGION_EMPTY ); this.connections = connections; this.capacity = capacity; this.contents = contents; this.outsideWorld = outsideWorld; } @Override public Position getPosition() { return new Position( x, y ); } public int getContents() { return contents; } public void setContents( int contents ) { this.contents = contents; if ( contents == 0 ) { state = State.WATER_REGION_EMPTY; } else if ( contents < WaterUtil.MAX_CAPACITY ) { state = State.WATER_REGION_HALF; } else { state = State.WATER_REGION; } } public void addContents( int delta ) { setContents( contents + delta ); } /** * Is this region connected in the specified direction? Note that this does * not mean that water can necessarily flow that way, because the cell in * that direction may have no water region, or it may have a region that is * not connected to here (e.g. two adjacent left ramps). */ public boolean isConnected( CellularDirection direction ) { return connections.contains( direction ); } public Iterator<CellularDirection> getConnectionsIterator() { return connections.iterator(); } /** * Get the amount of water being transferred from here in a given direction * this tick. * * @param direction * The direction of interest. * @return The amount of water. */ public int getFlow( CellularDirection direction ) { if ( flow.containsKey( direction ) ) { return flow.get( direction ); } return 0; } @Override public void calcNewState( World world ) { if ( outsideWorld || contents <= 0 ) { return; } Map<CellularDirection, WaterRegion> neighbourhood = WaterUtil .findNeighbourhood( this, world.waterTable ); flow = WaterUtil.calculateFlow( neighbourhood ); } @Override public void step( World world ) { if ( flow.size() > 0 ) { Map<CellularDirection, WaterRegion> neighbourhood = WaterUtil .findNeighbourhood( this, world.waterTable ); for ( Entry<CellularDirection, Integer> entry : flow.entrySet() ) { CellularDirection direction = entry.getKey(); if ( entry.getValue() > 0 ) { if ( neighbourhood.keySet().contains( direction ) ) { WaterRegion neighbour = neighbourhood.get( direction ); if ( !neighbour.outsideWorld ) { neighbour.addContents( entry.getValue() ); } setContents( contents - entry.getValue() ); } else { System.out.println( "Something went wrong when calculating water moving " + direction + " from " + this ); } } } flow = new HashMap<>(); } } @Override public Map<String, String> saveState() { Map<String, String> ret = new HashMap<String, String>(); ret.put( "WaterRegion.connections", Util.join( ",", connections ) ); BehaviourState.addToStateIfNotDefault( ret, "WaterRegion.capacity", String.valueOf( capacity ), "0" ); BehaviourState.addToStateIfNotDefault( ret, "WaterRegion.contents", String.valueOf( contents ), "0" ); List<String> flowBits = new ArrayList<>(); for ( Entry<CellularDirection, Integer> entry : flow.entrySet() ) { flowBits.add( entry.getKey().toString() ); flowBits.add( entry.getValue().toString() ); } ret.put( "WaterRegion.flow", Util.join( ",", flowBits ) ); return ret; } @Override public void restoreFromState( Map<String, String> state ) { connections = new HashSet<>(); for ( String connection : Util.split( state.get( "WaterRegion.connections" ), "," ) ) { connections.add( CellularDirection.valueOf( connection ) ); } capacity = BehaviourState.restoreFromState( state, "WaterRegion.capacity", 0 ); contents = BehaviourState.restoreFromState( state, "WaterRegion.contents", 0 ); flow = new HashMap<>(); String[] flowBits = Util.split( state.get( "WaterRegion.flow" ), "," ); for ( int i : Util.range( flowBits.length / 2 )) { CellularDirection direction = CellularDirection.valueOf( flowBits[i * 2] ); Integer amount = Integer.valueOf( flowBits[i * 2 + 1] ); flow.put( direction, amount ); } } @Override public int hashCode() { int hash = x; hash = 31 * hash + y; hash = 31 * hash + connections.hashCode(); hash = 31 * hash + capacity; hash = 31 * hash + contents; hash = 31 * hash + flow.hashCode(); return hash; } @Override public boolean equals( Object obj ) { if ( !( obj instanceof WaterRegion ) ) { return false; } if ( obj == this ) { return true; } WaterRegion other = ( WaterRegion ) obj; return x == other.x && y == other.y && connections.equals( other.connections ) && capacity == other.capacity && contents == other.contents && flow.equals( other.flow ); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append( "WaterRegion: " ) .append( x ).append( ", " ) .append( y ).append( ", " ) .append( connections ).append( ", " ) .append( capacity ).append( ", " ) .append( contents ).append( ", " ) .append( flow ); return sb.toString(); } @Override public String overlayText() { return 0 == contents ? "" : "~" + contents; } }