/** *------------------------------------------------------------------------------ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lostkingdomsfrontier.pfrpg.encounter; import java.util.ArrayList; import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.lostkingdomsfrontier.pfrpg.dice.Dice; /** * An encounter agent processes the execution of encounters * Basic Usage: * <ol> * <li>Instantiate the encounter and add all the participants via joinEncounter</li> * <li>Make all combatants aware that are not surprised via makeCombatantAware</li> * <li>Begin the encounter by invoking initialize()</li> * <li>For a fully automated resolution of a single round, use executeRound()</li> * <li>To step incrementally through each combatant in initiative order, use advanceToNextCombatant * and executeActionSet.</li> * </ol> * @author bebopjmm * * TODO Currently cannot handle concept of DELAYS or READY Actions, particularly with * respect to impact on turn order. */ public class EncounterAgent { static final Log LOG = LogFactory.getLog(EncounterAgent.class); /** * */ Dice dice = Dice.getInstance(); TurnProcessor turnEngine = new TurnProcessor(); Encounter currentEncounter; /** * Flag if the encounter is currently active and in progress */ boolean isActive = false; /** * Counter of the current round of this encounter. */ int currentRound = 0; Iterator<Combatant> turnOrder; Combatant activeCombatant = null; ArrayList<EncounterListener> listeners = new ArrayList<EncounterListener>(); /** * @return the currentEncounter */ public Encounter getCurrentEncounter() { return currentEncounter; } /** * @param currentEncounter the currentEncounter to set */ public void setCurrentEncounter(Encounter currentEncounter) { this.currentEncounter = currentEncounter; } /** * @return the currentRound */ public int getCurrentRound() { return currentRound; } public synchronized void subscribe(EncounterListener listener) { if (!listeners.contains(listener)) { listeners.add(listener); } } public synchronized void unsubscribe(EncounterListener listener) { if (listeners.contains(listener)) { listeners.remove(listener); } } public synchronized void initialize() { // Don't initialize an encounter that is already active. if (isActive) { LOG.warn("Ignoring attempt to initiate encounter that is already active, id = " + currentEncounter.getName()); return; } isActive = true; currentRound = 0; turnEngine = new TurnProcessor(); turnEngine.setEncounter(this.currentEncounter); advanceRound(); } /** * This method conducts a single round of the encounter. */ public synchronized void executeRound() { if (!isActive) { LOG.warn("Cannot execute an Encounter that has not been initialized."); return; } // TODO: Launch threads for all the combatants // Process rounds until the round is complete currentRound++; LOG.info("Beginnning combat sequence for round: " + currentRound); Combatant[] combatants; LOG.info("Round = " + currentRound); combatants = currentEncounter.getInitiativeOrder(); for (Combatant combatant : combatants) { LOG.info("Next up is " + combatant.getActor().getName()); // retrieve and execute actions selected by the combatant turnEngine.process(combatant.getActionSet()); } } public void executeActionSet() { // retrieve and execute actions selected by the combatant turnEngine.process(activeCombatant.getActionSet()); } public Combatant advanceToNextCombatant() { if (!isActive) { LOG.warn("Attempt to advanceToNextCombatant before Encounter has been initiallized."); return null; } if (!turnOrder.hasNext()) { advanceRound(); } activeCombatant = turnOrder.next(); return activeCombatant; } void advanceRound() { synchronized (listeners) { if (LOG.isDebugEnabled()) { LOG.debug("Notifying subscribers, total = " + listeners.size()); } for (EncounterListener listener : listeners) { listener.endOfRound(currentRound); } } currentRound++; // turnOrder = initiativeOrder.descendingIterator(); turnOrder = currentEncounter.initiativeOrder.iterator(); } }