/******************************************************************************* * Copyright (c) 2015 - 2017 * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *******************************************************************************/ package jsettlers.graphics.map.controls.original.panel.selection; import jsettlers.common.buildings.IBuilding; import jsettlers.common.buildings.IBuildingMaterial; import jsettlers.common.buildings.IBuildingOccupier; import jsettlers.common.map.partition.IStockSettings; import jsettlers.common.material.EMaterialType; import jsettlers.common.material.EPriority; import jsettlers.common.movable.ESoldierClass; import jsettlers.common.movable.IMovable; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.Hashtable; import java.util.List; /** * This class saves the state parts of the building that is displayed by the gui, to detect changes. * * @author Michael Zangl */ public class BuildingState { private final EPriority priority; private final EPriority[] supportedPriorities; private final ArrayList<StackState> stackStates = new ArrayList<>(); private final boolean construction; /** * An array: soldier class -> available places. */ private final Hashtable<ESoldierClass, ArrayList<OccupierState>> occupierStates; private final BitSet stockStates; private final int[] tradingCounts; private final boolean isSeaTrading; /** * This is the state for a building stack. * * @author Michael Zangl * */ public static class StackState { private final EMaterialType type; private final int count; private final boolean offering; /** * Create a new stack state * * @param mat * The material stack to create the state for. */ public StackState(IBuildingMaterial mat) { type = mat.getMaterialType(); count = mat.getMaterialCount(); offering = mat.isOffering(); } /** * Check if the stack state is still the same. * * @param mat * The material stack to check against. * @return <code>true</code> if the state is the same. */ public boolean isStillInState(IBuildingMaterial mat) { return mat.getMaterialType() == type && mat.getMaterialCount() == count && mat.isOffering() == offering; } /** * @return the type */ public EMaterialType getType() { return type; } /** * @return the count */ public int getCount() { return count; } /** * @return true if this is an offer stack. */ public boolean isOffering() { return offering; } } /** * Creates a new occupyer state. * * @author Michael Zangl * */ public static class OccupierState { private final IMovable movable; private final boolean comming; private OccupierState(IMovable movable) { this.movable = movable; comming = false; } private OccupierState(boolean comming) { this.comming = comming; movable = null; } /** * @return <code>true</code> if the comming image should be displayed. */ public boolean isComming() { return comming; } /** * @return <code>true</code> if the missing image should be displayed. */ public boolean isMissing() { return movable == null && !isComming(); } /** * @return The movable that is in this stack or <code>null</code> for none. */ public IMovable getMovable() { return movable; } } /** * Stores the current state of the building. * * @param building * the building */ public BuildingState(IBuilding building) { priority = building.getPriority(); supportedPriorities = building.getSupportedPriorities(); construction = building.getStateProgress() < 1; occupierStates = computeOccupierStates(building); stockStates = computeStockStates(building); tradingCounts = computeTradingCounts(building); for (IBuildingMaterial mat : building.getMaterials()) { stackStates.add(new StackState(mat)); } isSeaTrading = building instanceof IBuilding.ITrading && ((IBuilding.ITrading) building).isSeaTrading(); } private int[] computeTradingCounts(IBuilding building) { if (building instanceof IBuilding.ITrading) { IBuilding.ITrading trading = (IBuilding.ITrading) building; int[] counts = new int[EMaterialType.NUMBER_OF_DROPPABLE_MATERIALS]; for (EMaterialType m : EMaterialType.DROPPABLE_MATERIALS) { counts[m.ordinal] = trading.getRequestedTradingFor(m); } return counts; } else { return null; } } private BitSet computeStockStates(IBuilding building) { if (building instanceof IBuilding.IStock && !construction) { BitSet acceptedMaterialsSet = new BitSet(); IStockSettings stockSettings = ((IBuilding.IStock) building).getStockSettings(); for (EMaterialType materialType : EMaterialType.DROPPABLE_MATERIALS) { acceptedMaterialsSet.set(materialType.ordinal, stockSettings.isAccepted(materialType)); } return acceptedMaterialsSet; } else { return null; } } private Hashtable<ESoldierClass, ArrayList<OccupierState>> computeOccupierStates(IBuilding building) { Hashtable<ESoldierClass, ArrayList<OccupierState>> newStates = null; if (building instanceof IBuilding.IOccupied && !construction) { IBuilding.IOccupied occupied = (IBuilding.IOccupied) building; newStates = new Hashtable<>(); for (ESoldierClass soldierClass : ESoldierClass.VALUES) { newStates.put(soldierClass, new ArrayList<OccupierState>()); } for (IBuildingOccupier o : occupied.getOccupiers()) { ESoldierClass soldierClass = o.getPlace().getSoldierClass(); OccupierState state = new OccupierState(o.getMovable()); newStates.get(soldierClass).add(state); } for (ESoldierClass soldierClass : ESoldierClass.VALUES) { ArrayList<OccupierState> list = newStates.get(soldierClass); int coming = occupied.getComingSoldiers(soldierClass); for (; coming > 0; coming--) { list.add(new OccupierState(true)); } int requested = occupied.getSearchedSoldiers(soldierClass); for (; requested > 0; requested--) { list.add(new OccupierState(false)); } } } return newStates; } /** * Gets a list of priorities supported by this state. * * @return The priorities. */ public EPriority[] getSupportedPriorities() { return supportedPriorities; } public boolean isConstruction() { return construction; } public ArrayList<StackState> getStackStates() { return stackStates; } public boolean stockAcceptsMaterial(EMaterialType material) { return stockStates != null && stockStates.get(material.ordinal); } /** * Checks if we are still in the state. * * @param building * The building to check. * @return <code>true</code> if that building is in this state. */ public boolean isStillInState(IBuilding building) { return building.getPriority() == priority && Arrays.equals(supportedPriorities, building.getSupportedPriorities()) && construction == (building.getStateProgress() < 1) && hasSameStacks(building) && hasSameOccupiers(building) && hasSameStock(building) && hasSameTrading(building); } private boolean hasSameTrading(IBuilding building) { return isEqual(computeTradingCounts(building), tradingCounts); } private boolean hasSameStock(IBuilding building) { return isEqual(computeStockStates(building), stockStates); } private boolean hasSameOccupiers(IBuilding building) { return isEqual(computeOccupierStates(building), occupierStates); } private static boolean isEqual(Object o1, Object o2) { return o1 == o2 || (o1 != null && o1.equals(o2)); } private boolean hasSameStacks(IBuilding building) { List<IBuildingMaterial> materials = building.getMaterials(); if (materials.size() != stackStates.size()) { return false; } int i = 0; for (IBuildingMaterial mat : materials) { if (stackStates.get(i++).isStillInState(mat)) { return false; } } return true; } public List<OccupierState> getOccupiers(ESoldierClass soldierClass) { return Collections.unmodifiableList(occupierStates.get(soldierClass)); } public int getTradingCount(EMaterialType material) { if (tradingCounts == null) { return 0; } else { return tradingCounts[material.ordinal]; } } /** * @return <code>true</code> if we are a constructed occupied building. */ public boolean isOccupied() { return occupierStates != null && !construction; } /** * @return <code>true</code> if we are a constructed stock building. */ public boolean isStock() { return stockStates != null && !construction; } /** * @return <code>true</code> if we are a constructed trading building. */ public boolean isTrading() { return tradingCounts != null && !construction; } /** * @return <code>true</code> if we are sea trading building, <code>false</code> for land trading. */ public boolean isSeaTrading() { return isSeaTrading; } }