package forge.card.trigger;
import forge.*;
import forge.card.abilityFactory.AbilityFactory;
import forge.card.spellability.*;
import forge.gui.input.Input;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* <p>TriggerHandler class.</p>
*
* @author Forge
* @version $Id: $
*/
public class TriggerHandler {
private ArrayList<String> registeredModes = new ArrayList<String>();
private ArrayList<Trigger> registeredTriggers = new ArrayList<Trigger>();
private ArrayList<String> suppressedModes = new ArrayList<String>();
private ArrayList<Trigger> delayedTriggers = new ArrayList<Trigger>();
/**
* <p>suppressMode.</p>
*
* @param mode a {@link java.lang.String} object.
*/
public void suppressMode(String mode) {
suppressedModes.add(mode);
}
/**
* <p>clearSuppression.</p>
*
* @param mode a {@link java.lang.String} object.
*/
public void clearSuppression(String mode) {
suppressedModes.remove(mode);
}
/**
* <p>parseTrigger.</p>
*
* @param name a {@link java.lang.String} object.
* @param trigParse a {@link java.lang.String} object.
* @param host a {@link forge.Card} object.
* @return a {@link forge.card.trigger.Trigger} object.
*/
public static Trigger parseTrigger(String name, String trigParse, Card host) {
Trigger ret = TriggerHandler.parseTrigger(trigParse, host);
ret.setName(name);
return ret;
}
/**
* <p>parseTrigger.</p>
*
* @param trigParse a {@link java.lang.String} object.
* @param host a {@link forge.Card} object.
* @return a {@link forge.card.trigger.Trigger} object.
*/
public static Trigger parseTrigger(String trigParse, Card host) {
HashMap<String, String> mapParams = parseParams(trigParse);
return parseTrigger(mapParams, host);
}
/**
* <p>parseTrigger.</p>
*
* @param mapParams a {@link java.util.HashMap} object.
* @param host a {@link forge.Card} object.
* @return a {@link forge.card.trigger.Trigger} object.
*/
public static Trigger parseTrigger(HashMap<String, String> mapParams, Card host) {
Trigger ret = null;
String mode = mapParams.get("Mode");
if (mode.equals("AbilityCast")) {
ret = new Trigger_SpellAbilityCast(mapParams, host);
} else if (mode.equals("Always")) {
ret = new Trigger_Always(mapParams, host);
} else if (mode.equals("AttackerBlocked")) {
ret = new Trigger_AttackerBlocked(mapParams, host);
} else if (mode.equals("AttackerUnblocked")) {
ret = new Trigger_AttackerUnblocked(mapParams, host);
} else if (mode.equals("Attacks")) {
ret = new Trigger_Attacks(mapParams, host);
} else if (mode.equals("BecomesTarget")) {
ret = new Trigger_BecomesTarget(mapParams, host);
} else if (mode.equals("Blocks")) {
ret = new Trigger_Blocks(mapParams, host);
} else if (mode.equals("Championed")) {
ret = new Trigger_Championed(mapParams, host);
} else if (mode.equals("ChangesZone")) {
ret = new Trigger_ChangesZone(mapParams, host);
} else if (mode.equals("Clashed")) {
ret = new Trigger_Clashed(mapParams, host);
} else if (mode.equals("CounterAdded")) {
ret = new Trigger_CounterAdded(mapParams, host);
} else if (mode.equals("Cycled")) {
ret = new Trigger_Cycled(mapParams, host);
} else if (mode.equals("DamageDone")) {
ret = new Trigger_DamageDone(mapParams, host);
} else if (mode.equals("Discarded")) {
ret = new Trigger_Discarded(mapParams, host);
} else if (mode.equals("Drawn")) {
ret = new Trigger_Drawn(mapParams, host);
} else if (mode.equals("LandPlayed")) {
ret = new Trigger_LandPlayed(mapParams, host);
} else if (mode.equals("LifeGained")) {
ret = new Trigger_LifeGained(mapParams, host);
} else if (mode.equals("LifeLost")) {
ret = new Trigger_LifeLost(mapParams, host);
} else if (mode.equals("Phase")) {
ret = new Trigger_Phase(mapParams, host);
} else if (mode.equals("Sacrificed")) {
ret = new Trigger_Sacrificed(mapParams, host);
} else if (mode.equals("Shuffled")) {
ret = new Trigger_Shuffled(mapParams, host);
} else if (mode.equals("SpellAbilityCast")) {
ret = new Trigger_SpellAbilityCast(mapParams, host);
} else if (mode.equals("SpellCast")) {
ret = new Trigger_SpellAbilityCast(mapParams, host);
} else if (mode.equals("Taps")) {
ret = new Trigger_Taps(mapParams, host);
} else if (mode.equals("TapsForMana")) {
ret = new Trigger_TapsForMana(mapParams, host);
} else if (mode.equals("TurnFaceUp")) {
ret = new Trigger_TurnFaceUp(mapParams, host);
} else if (mode.equals("Unequip")) {
ret = new Trigger_Unequip(mapParams, host);
} else if (mode.equals("Untaps")) {
ret = new Trigger_Untaps(mapParams, host);
}
return ret;
}
/**
* <p>parseParams.</p>
*
* @param trigParse a {@link java.lang.String} object.
* @return a {@link java.util.HashMap} object.
*/
private static HashMap<String, String> parseParams(String trigParse) {
HashMap<String, String> mapParams = new HashMap<String, String>();
if (trigParse.length() == 0)
throw new RuntimeException("TriggerFactory : registerTrigger -- trigParse too short");
String params[] = trigParse.split("\\|");
for (int i = 0; i < params.length; i++) {
params[i] = params[i].trim();
}
for (String param : params) {
String[] splitParam = param.split("\\$");
for (int i = 0; i < splitParam.length; i++) {
splitParam[i] = splitParam[i].trim();
}
if (splitParam.length != 2) {
StringBuilder sb = new StringBuilder();
sb.append("TriggerFactory Parsing Error in registerTrigger() : Split length of ");
sb.append(param).append(" is not 2.");
throw new RuntimeException(sb.toString());
}
mapParams.put(splitParam[0], splitParam[1]);
}
return mapParams;
}
/**
* <p>registerDelayedTrigger.</p>
*
* @param trig a {@link forge.card.trigger.Trigger} object.
*/
public void registerDelayedTrigger(Trigger trig) {
delayedTriggers.add(trig);
String mode = trig.getMapParams().get("Mode");
if (!registeredModes.contains(mode))
registeredModes.add(mode);
}
/**
* <p>registerTrigger.</p>
*
* @param trig a {@link forge.card.trigger.Trigger} object.
*/
public void registerTrigger(Trigger trig) {
registeredTriggers.add(trig);
String mode = trig.getMapParams().get("Mode");
if (!registeredModes.contains(mode))
registeredModes.add(mode);
}
/**
* <p>clearRegistered.</p>
*/
public void clearRegistered() {
delayedTriggers.clear();
registeredTriggers.clear();
registeredModes.clear();
}
/**
* <p>removeRegisteredTrigger.</p>
*
* @param trig a {@link forge.card.trigger.Trigger} object.
*/
public void removeRegisteredTrigger(Trigger trig) {
for (int i = 0; i < registeredTriggers.size(); i++) {
if (registeredTriggers.get(i).equals(trig))
registeredTriggers.remove(i);
}
}
public void removeTemporaryTriggers() {
for (int i = 0; i < registeredTriggers.size(); i++) {
if (registeredTriggers.get(i).isTemporary()) {
registeredTriggers.get(i).hostCard.removeTrigger(registeredTriggers.get(i));
registeredTriggers.remove(i);
}
}
}
/**
* <p>Getter for the field <code>registeredTriggers</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<Trigger> getRegisteredTriggers() {
return registeredTriggers;
}
/**
* <p>removeAllFromCard.</p>
*
* @param crd a {@link forge.Card} object.
*/
public void removeAllFromCard(Card crd) {
for (int i = 0; i < registeredTriggers.size(); i++) {
if (registeredTriggers.get(i).getHostCard().equals(crd)) {
registeredTriggers.remove(i);
i--;
}
}
}
/**
* <p>runTrigger.</p>
*
* @param mode a {@link java.lang.String} object.
* @param runParams a {@link java.util.Map} object.
*/
public void runTrigger(String mode, Map<String, Object> runParams) {
if (suppressedModes.contains(mode) || !registeredModes.contains(mode)) {
return;
}
//AP
for (Trigger regtrig : registeredTriggers) {
if (regtrig.getHostCard().getController().equals(AllZone.getPhase().getPlayerTurn())) {
runSingleTrigger(regtrig, mode, runParams);
}
}
for (int i = 0; i < delayedTriggers.size(); i++) {
Trigger deltrig = delayedTriggers.get(i);
if (deltrig.getHostCard().getController().equals(AllZone.getPhase().getPlayerTurn())) {
if (runSingleTrigger(deltrig, mode, runParams)) {
delayedTriggers.remove(i);
i--;
}
}
}
//NAP
for (Trigger regtrig : registeredTriggers) {
if (regtrig.getHostCard().getController().equals(AllZone.getPhase().getPlayerTurn().getOpponent())) {
runSingleTrigger(regtrig, mode, runParams);
}
}
for (int i = 0; i < delayedTriggers.size(); i++) {
Trigger deltrig = delayedTriggers.get(i);
if (deltrig.getHostCard().getController().equals(AllZone.getPhase().getPlayerTurn().getOpponent())) {
if (runSingleTrigger(deltrig, mode, runParams)) {
delayedTriggers.remove(i);
i--;
}
}
}
}
//Checks if the conditions are right for a single trigger to go off, and runs it if so.
//Return true if the trigger went off, false otherwise.
/**
* <p>runSingleTrigger.</p>
*
* @param regtrig a {@link forge.card.trigger.Trigger} object.
* @param mode a {@link java.lang.String} object.
* @param runParams a {@link java.util.HashMap} object.
* @return a boolean.
*/
private boolean runSingleTrigger(final Trigger regtrig, final String mode, final Map<String, Object> runParams) {
if (!regtrig.zonesCheck()) {
return false;
}
if (!regtrig.phasesCheck()) {
return false;
}
if (!regtrig.requirementsCheck()) {
return false;
}
if (regtrig.getHostCard().isFaceDown()) {
return false;
}
if(regtrig instanceof Trigger_Always) {
if(AllZone.getStack().hasStateTrigger(regtrig.ID))
{
return false; //State triggers that are already on the stack don't trigger again.
}
}
HashMap<String, String> trigParams = regtrig.getMapParams();
final Player[] decider = new Player[1];
//final boolean isOptional = false;
if (mode.equals(trigParams.get("Mode"))) {
if (!regtrig.performTest(runParams)) {
return false;
}
// Any trigger should cause the phase not to skip
AllZone.getPhase().setSkipPhase(false);
regtrig.setRunParams(runParams);
//All tests passed, execute ability.
if (regtrig instanceof Trigger_TapsForMana) {
Ability_Mana abMana = (Ability_Mana) runParams.get("Ability_Mana");
if (null != abMana) abMana.setUndoable(false);
}
AbilityFactory AF = new AbilityFactory();
final SpellAbility[] sa = new SpellAbility[1];
Card host = AllZoneUtil.getCardState(regtrig.getHostCard());
if (host == null)
host = regtrig.getHostCard();
// This will fix the Oblivion Ring issue, but is this the right fix?
for (Object o : regtrig.getHostCard().getRemembered()) {
if (!host.getRemembered().contains(o)) {
host.addRemembered(o);
}
}
sa[0] = regtrig.getOverridingAbility();
if (sa[0] == null) {
if (!trigParams.containsKey("Execute")) {
sa[0] = new Ability(regtrig.getHostCard(), "0") {
@Override
public void resolve() {
}
};
} else {
sa[0] = AF.getAbility(host.getSVar(trigParams.get("Execute")), host);
}
}
sa[0].setTrigger(true);
sa[0].setSourceTrigger(regtrig.ID);
regtrig.setTriggeringObjects(sa[0]);
if (regtrig.getStoredTriggeredObjects() != null)
sa[0].setAllTriggeringObjects(regtrig.getStoredTriggeredObjects());
sa[0].setActivatingPlayer(host.getController());
sa[0].setStackDescription(sa[0].toString());
boolean mand = false;
if (trigParams.containsKey("OptionalDecider")) {
mand = false;
decider[0] = AbilityFactory.getDefinedPlayers(host, trigParams.get("OptionalDecider"), sa[0]).get(0);
} else {
mand = true;
SpellAbility ability = sa[0];
while(ability != null){
Target tgt = ability.getTarget();
if (tgt != null) {
tgt.setMandatory(true);
}
ability = ability.getSubAbility();
}
}
final boolean isMandatory = mand;
//Wrapper ability that checks the requirements again just before resolving, for intervening if clauses.
//Yes, it must wrap ALL SpellAbility methods in order to handle possible corner cases.
//(The trigger can have a hardcoded OverridingAbility which can make use of any of the methods)
final Ability wrapperAbility = new Ability(regtrig.getHostCard(), "0") {
@Override
public boolean isWrapper() {
return true;
}
@Override
public void setPaidHash(HashMap<String, CardList> hash) {
sa[0].setPaidHash(hash);
}
@Override
public HashMap<String, CardList> getPaidHash() {
return sa[0].getPaidHash();
}
@Override
public void setPaidList(CardList list, String str) {
sa[0].setPaidList(list, str);
}
@Override
public CardList getPaidList(String str) {
return sa[0].getPaidList(str);
}
@Override
public void addCostToHashList(Card c, String str) {
sa[0].addCostToHashList(c, str);
}
@Override
public void resetPaidHash() {
sa[0].resetPaidHash();
}
@Override
public HashMap<String, Object> getTriggeringObjects() {
return sa[0].getTriggeringObjects();
}
@Override
public void setAllTriggeringObjects(HashMap<String, Object> triggeredObjects) {
sa[0].setAllTriggeringObjects(triggeredObjects);
}
@Override
public void setTriggeringObject(String type, Object o) {
sa[0].setTriggeringObject(type, o);
}
@Override
public Object getTriggeringObject(String type) {
return sa[0].getTriggeringObject(type);
}
@Override
public boolean hasTriggeringObject(String type) {
return sa[0].hasTriggeringObject(type);
}
@Override
public void resetTriggeringObjects() {
sa[0].resetTriggeringObjects();
}
@Override
public boolean canPlay() {
return sa[0].canPlay();
}
@Override
public boolean canPlayAI() {
return sa[0].canPlayAI();
}
@Override
public void chooseTargetAI() {
sa[0].chooseTargetAI();
}
@Override
public SpellAbility copy() {
return sa[0].copy();
}
@Override
public boolean doTrigger(boolean mandatory) {
return sa[0].doTrigger(mandatory);
}
@Override
public AbilityFactory getAbilityFactory() {
return sa[0].getAbilityFactory();
}
@Override
public Player getActivatingPlayer() {
return sa[0].getActivatingPlayer();
}
@Override
public Input getAfterPayMana() {
return sa[0].getAfterPayMana();
}
@Override
public Input getAfterResolve() {
return sa[0].getAfterResolve();
}
@Override
public Input getBeforePayMana() {
return sa[0].getBeforePayMana();
}
@Override
public Command getBeforePayManaAI() {
return sa[0].getBeforePayManaAI();
}
@Override
public Command getCancelCommand() {
return sa[0].getCancelCommand();
}
@Override
public CommandArgs getChooseTargetAI() {
return sa[0].getChooseTargetAI();
}
@Override
public String getDescription() {
return sa[0].getDescription();
}
@Override
public String getMultiKickerManaCost() {
return sa[0].getMultiKickerManaCost();
}
@Override
public String getReplicateManaCost() {
return sa[0].getReplicateManaCost();
}
@Override
public SpellAbility_Restriction getRestrictions() {
return sa[0].getRestrictions();
}
@Override
public Card getSourceCard() {
return sa[0].getSourceCard();
}
@Override
public String getStackDescription() {
StringBuilder sb = new StringBuilder(regtrig.toString());
if (getTarget() != null) {
sb.append(" (Targeting ");
for (Object o : getTarget().getTargets()) {
sb.append(o.toString());
sb.append(", ");
}
if (sb.toString().endsWith(", ")) {
sb.setLength(sb.length() - 2);
} else {
sb.append("ERROR");
}
sb.append(")");
}
return sb.toString();
}
@Override
public Ability_Sub getSubAbility() {
return sa[0].getSubAbility();
}
@Override
public Target getTarget() {
return sa[0].getTarget();
}
@Override
public Card getTargetCard() {
return sa[0].getTargetCard();
}
@Override
public CardList getTargetList() {
return sa[0].getTargetList();
}
@Override
public Player getTargetPlayer() {
return sa[0].getTargetPlayer();
}
@Override
public String getXManaCost() {
return sa[0].getXManaCost();
}
@Override
public boolean isAbility() {
return sa[0].isAbility();
}
@Override
public boolean isBuyBackAbility() {
return sa[0].isBuyBackAbility();
}
@Override
public boolean isCycling() {
return sa[0].isCycling();
}
@Override
public boolean isExtrinsic() {
return sa[0].isExtrinsic();
}
@Override
public boolean isFlashBackAbility() {
return sa[0].isFlashBackAbility();
}
@Override
public boolean isIntrinsic() {
return sa[0].isIntrinsic();
}
@Override
public boolean isKickerAbility() {
return sa[0].isKickerAbility();
}
@Override
public boolean isKothThirdAbility() {
return sa[0].isKothThirdAbility();
}
@Override
public boolean isMultiKicker() {
return sa[0].isMultiKicker();
}
@Override
public boolean isReplicate() {
return sa[0].isReplicate();
}
@Override
public boolean isSpell() {
return sa[0].isSpell();
}
@Override
public boolean isTapAbility() {
return sa[0].isTapAbility();
}
@Override
public boolean isUntapAbility() {
return sa[0].isUntapAbility();
}
@Override
public boolean isXCost() {
return sa[0].isXCost();
}
@Override
public void resetOnceResolved() {
// Fixing an issue with Targeting + Paying Mana
//sa[0].resetOnceResolved();
}
@Override
public void setAbilityFactory(AbilityFactory af) {
sa[0].setAbilityFactory(af);
}
@Override
public void setActivatingPlayer(Player player) {
sa[0].setActivatingPlayer(player);
}
@Override
public void setAdditionalManaCost(String cost) {
sa[0].setAdditionalManaCost(cost);
}
@Override
public void setAfterPayMana(Input in) {
sa[0].setAfterPayMana(in);
}
@Override
public void setAfterResolve(Input in) {
sa[0].setAfterResolve(in);
}
@Override
public void setBeforePayMana(Input in) {
sa[0].setBeforePayMana(in);
}
@Override
public void setBeforePayManaAI(Command c) {
sa[0].setBeforePayManaAI(c);
}
@Override
public void setCancelCommand(Command cancelCommand) {
sa[0].setCancelCommand(cancelCommand);
}
@Override
public void setChooseTargetAI(CommandArgs c) {
sa[0].setChooseTargetAI(c);
}
@Override
public void setDescription(String s) {
sa[0].setDescription(s);
}
@Override
public void setFlashBackAbility(boolean flashBackAbility) {
sa[0].setFlashBackAbility(flashBackAbility);
}
@Override
public void setIsBuyBackAbility(boolean b) {
sa[0].setIsBuyBackAbility(b);
}
@Override
public void setIsCycling(boolean b) {
sa[0].setIsCycling(b);
}
@Override
public void setIsMultiKicker(boolean b) {
sa[0].setIsMultiKicker(b);
}
@Override
public void setIsReplicate(boolean b) {
sa[0].setIsReplicate(b);
}
@Override
public void setIsXCost(boolean b) {
sa[0].setIsXCost(b);
}
@Override
public void setKickerAbility(boolean kab) {
sa[0].setKickerAbility(kab);
}
@Override
public void setKothThirdAbility(boolean kothThirdAbility) {
sa[0].setKothThirdAbility(kothThirdAbility);
}
@Override
public void setManaCost(String cost) {
sa[0].setManaCost(cost);
}
@Override
public void setMultiKickerManaCost(String cost) {
sa[0].setMultiKickerManaCost(cost);
}
@Override
public void setReplicateManaCost(String cost) {
sa[0].setReplicateManaCost(cost);
}
@Override
public void setPayCosts(Cost abCost) {
sa[0].setPayCosts(abCost);
}
@Override
public void setRestrictions(SpellAbility_Restriction restrict) {
sa[0].setRestrictions(restrict);
}
@Override
public void setSourceCard(Card c) {
sa[0].setSourceCard(c);
}
@Override
public void setStackDescription(String s) {
sa[0].setStackDescription(s);
}
@Override
public void setSubAbility(Ability_Sub subAbility) {
sa[0].setSubAbility(subAbility);
}
@Override
public void setTarget(Target tgt) {
sa[0].setTarget(tgt);
}
@Override
public void setTargetCard(Card card) {
sa[0].setTargetCard(card);
}
@Override
public void setTargetList(CardList list) {
sa[0].setTargetList(list);
}
@Override
public void setTargetPlayer(Player p) {
sa[0].setTargetPlayer(p);
}
@Override
public void setType(String s) {
sa[0].setType(s);
}
@Override
public void setXManaCost(String cost) {
sa[0].setXManaCost(cost);
}
@Override
public boolean wasCancelled() {
return sa[0].wasCancelled();
}
@Override
public void setSourceTrigger(int ID) {
sa[0].setSourceTrigger(ID);
}
@Override
public int getSourceTrigger() {
return sa[0].getSourceTrigger();
}
////////////////////////////////////////
//THIS ONE IS ALL THAT MATTERS
////////////////////////////////////////
@Override
public void resolve() {
if(!(regtrig instanceof Trigger_Always)) //State triggers don't do the whole "Intervening If" thing.
{
if (!regtrig.requirementsCheck()) {
return;
}
}
if (decider[0] != null) {
if (decider[0].isHuman()) {
StringBuilder buildQuestion = new StringBuilder("Use triggered ability of ");
buildQuestion.append(regtrig.getHostCard().getName()).append("(").append(regtrig.getHostCard().getUniqueNumber()).append(")?");
buildQuestion.append("\r\n(");
buildQuestion.append(regtrig.getMapParams().get("TriggerDescription").replace("CARDNAME", regtrig.getHostCard().getName()));
buildQuestion.append(")");
if (!GameActionUtil.showYesNoDialog(regtrig.getHostCard(), buildQuestion.toString())) {
return;
}
} else {
// This isn't quite right, but better than canPlayAI
if (!sa[0].doTrigger(isMandatory)) {
return;
}
}
}
if (sa[0].getSourceCard().getController().isHuman()) {
//Card src = (Card)(sa[0].getSourceCard().getTriggeringObject("Card"));
//System.out.println("Trigger resolving for "+mode+". Card = "+src);
AllZone.getGameAction().playSpellAbility_NoStack(sa[0], true);
} else {
// commented out because i don't think this should be called again here
//sa[0].doTrigger(isMandatory);
ComputerUtil.playNoStack(sa[0]);
}
//Add eventual delayed trigger.
if (regtrig.getMapParams().containsKey("DelayedTrigger")) {
String SVarName = regtrig.getMapParams().get("DelayedTrigger");
Trigger deltrig = parseTrigger(regtrig.getHostCard().getSVar(SVarName), regtrig.getHostCard());
deltrig.setStoredTriggeredObjects(this.getTriggeringObjects());
registerDelayedTrigger(deltrig);
}
}
};
wrapperAbility.setTrigger(true);
wrapperAbility.setMandatory(isMandatory);
wrapperAbility.setDescription(wrapperAbility.getStackDescription());
/*
if(host.getController().isHuman())
{
AllZone.getGameAction().playSpellAbility(wrapperAbility);
}
else
{
wrapperAbility.doTrigger(isMandatory);
ComputerUtil.playStack(wrapperAbility);
}
*/
//Card src = (Card)(sa[0].getSourceCard().getTriggeringObject("Card"));
//System.out.println("Trigger going on stack for "+mode+". Card = "+src);
if(regtrig.getMapParams().containsKey("Static"))
{
if(regtrig.getMapParams().get("Static").equals("True"))
{
AllZone.getGameAction().playSpellAbility_NoStack(wrapperAbility,false);
}
else
{
AllZone.getStack().addSimultaneousStackEntry(wrapperAbility);
}
}
else
{
AllZone.getStack().addSimultaneousStackEntry(wrapperAbility);
}
return true;
}
return false;
}
}