package games.strategy.engine.data; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class GameSequence extends GameDataComponent implements Iterable<GameStep> { private static final long serialVersionUID = 6354618406598578287L; private final List<GameStep> m_steps = new ArrayList<>(); private int m_currentIndex; private int m_round = 1; private int m_roundOffset = 0; private transient Object m_currentStepMutex = new Object(); public GameSequence(final GameData data) { super(data); } /** * Only used when we are trying to export the data to a savegame, * and we need to change the round and step to something other than the current round and step * (because we are creating a savegame at a certain point in history, for example). */ public synchronized void setRoundAndStep(final int currentRound, final String stepDisplayName, final PlayerID player) { m_round = currentRound; boolean found = false; for (int i = 0; i < m_steps.size(); i++) { final GameStep step = m_steps.get(i); if (step != null && step.getDisplayName().equalsIgnoreCase(stepDisplayName)) { if ((player == null && step.getPlayerID() == null) || (player != null && player.equals(step.getPlayerID()))) { m_currentIndex = i; found = true; break; } } } if (!found) { m_currentIndex = 0; System.err.println("Step Not Found (" + stepDisplayName + ":" + player.getName() + "), will instead use: " + m_steps.get(m_currentIndex)); } } public void addStep(final GameStep step) { m_steps.add(step); } /** * Removes the first instance of step. */ protected void remove(final GameStep step) { if (!m_steps.contains(step)) { throw new IllegalArgumentException("Step does not exist"); } m_steps.remove(step); } protected void removeStep(final int index) { m_steps.remove(index); } public void removeAllSteps() { m_steps.clear(); m_round = 1; } public int getRound() { return m_round + m_roundOffset; } public int getRoundOffset() { return m_roundOffset; } public void setRoundOffset(final int roundOffset) { m_roundOffset = roundOffset; } public int getStepIndex() { return m_currentIndex; } void setStepIndex(final int newIndex) { if ((newIndex < 0) || (newIndex >= m_steps.size())) { throw new IllegalArgumentException("New index out of range: " + newIndex); } m_currentIndex = newIndex; } /** * @return boolean whether the round has changed. */ public boolean next() { synchronized (m_currentStepMutex) { m_currentIndex++; if (m_currentIndex >= m_steps.size()) { m_currentIndex = 0; m_round++; return true; } return false; } } /** * Only tests to see if we are on the last step. * Used for finding if we need to make a new round or not. * Does not change any data or fields. */ public boolean testWeAreOnLastStep() { synchronized (m_currentStepMutex) { return m_currentIndex + 1 >= m_steps.size(); } } public GameStep getStep() { synchronized (m_currentStepMutex) { // since we can now delete game steps mid game, it is a good idea to test if our index is out of range if (m_currentIndex < 0) { m_currentIndex = 0; } if (m_currentIndex >= m_steps.size()) { next(); } return getStep(m_currentIndex); } } public GameStep getStep(final int index) { if ((index < 0) || (index >= m_steps.size())) { throw new IllegalArgumentException("Attempt to access invalid state: " + index); } return m_steps.get(index); } @Override public Iterator<GameStep> iterator() { return m_steps.iterator(); } public int size() { return m_steps.size(); } /** make sure transient lock object is initialized on deserialization. */ private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); if (m_currentStepMutex == null) { m_currentStepMutex = new Object(); } } }