From d374da0481a7c9f7b48b4a4ad74bb1b6319d0c4a Mon Sep 17 00:00:00 2001 From: Chris H Date: Sat, 30 Dec 2017 21:55:20 -0500 Subject: [PATCH] Refactor TokenEffect to be easier to add in TokenScripts --- .../game/ability/effects/TokenEffect.java | 372 +++++++++--------- .../java/forge/game/card/token/TokenInfo.java | 6 +- 2 files changed, 196 insertions(+), 182 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java index c236500e02c..4e1dd62ef07 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java @@ -18,10 +18,12 @@ package forge.game.ability.effects; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import forge.card.MagicColor; import forge.game.card.token.TokenInfo; -import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterables; @@ -184,6 +186,7 @@ public class TokenEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { final Card host = sa.getHostCard(); + final Game game = host.getGame(); final SpellAbility root = sa.getRootAbility(); readParameters(sa); @@ -194,30 +197,11 @@ public class TokenEffect extends SpellAbilityEffect { cause = (SpellAbility)root.getReplacingObject("Cause"); } - String cost = ""; - - // Construct original colors - String originalColorDesc = ""; - for (final String col : this.tokenOriginalColors) { - if (col.equalsIgnoreCase("White")) { - originalColorDesc += "W "; - } else if (col.equalsIgnoreCase("Blue")) { - originalColorDesc += "U "; - } else if (col.equalsIgnoreCase("Black")) { - originalColorDesc += "B "; - } else if (col.equalsIgnoreCase("Red")) { - originalColorDesc += "R "; - } else if (col.equalsIgnoreCase("Green")) { - originalColorDesc += "G "; - } else if (col.equalsIgnoreCase("Colorless")) { - originalColorDesc = "C"; - } - } + String originalColorDesc = parseColorForImage(); final List imageNames = Lists.newArrayListWithCapacity(1); if (this.tokenImage.equals("")) { - imageNames.add(PaperToken.makeTokenFileName(TextUtil.fastReplace(originalColorDesc, - " ", ""), tokenPower, tokenToughness, tokenOriginalName)); + imageNames.add(PaperToken.makeTokenFileName(originalColorDesc, tokenPower, tokenToughness, tokenOriginalName)); } else { imageNames.add(0, this.tokenImage); } @@ -225,34 +209,7 @@ public class TokenEffect extends SpellAbilityEffect { imageNames.addAll(Arrays.asList(this.tokenAltImages)); } - final String[] substitutedColors = Arrays.copyOf(this.tokenColors, this.tokenColors.length); - for (int i = 0; i < substitutedColors.length; i++) { - if (substitutedColors[i].equals("ChosenColor")) { - // this currently only supports 1 chosen color - substitutedColors[i] = host.getChosenColor(); - } - } - String colorDesc = ""; - for (final String col : substitutedColors) { - if (col.equalsIgnoreCase("White")) { - colorDesc += "W "; - } else if (col.equalsIgnoreCase("Blue")) { - colorDesc += "U "; - } else if (col.equalsIgnoreCase("Black")) { - colorDesc += "B "; - } else if (col.equalsIgnoreCase("Red")) { - colorDesc += "R "; - } else if (col.equalsIgnoreCase("Green")) { - colorDesc += "G "; - } else if (col.equalsIgnoreCase("Colorless")) { - colorDesc = "C"; - } - } - for (final char c : colorDesc.toCharArray()) { - cost += c + ' '; - } - - cost = colorDesc.replace('C', '1').trim(); + String cost = determineTokenColor(host); final int finalPower = AbilityUtils.calculateAmount(host, this.tokenPower, sa); final int finalToughness = AbilityUtils.calculateAmount(host, this.tokenToughness, sa); @@ -269,146 +226,203 @@ public class TokenEffect extends SpellAbilityEffect { final boolean remember = sa.hasParam("RememberTokens"); final boolean imprint = sa.hasParam("ImprintTokens"); final List allTokens = Lists.newArrayList(); + + boolean combatChanged = false; + boolean inCombat = game.getPhaseHandler().inCombat(); + for (final Player controller : AbilityUtils.getDefinedPlayers(host, this.tokenOwner, sa)) { - final Game game = controller.getGame(); - for (int i = 0; i < finalAmount; i++) { - final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size())); - final TokenInfo tokenInfo = new TokenInfo(substitutedName, imageName, - cost, substitutedTypes, this.tokenKeywords, finalPower, finalToughness); - final List tokens = tokenInfo.makeTokenWithMultiplier(controller, cause != null); - - // Grant rule changes - if (this.tokenHiddenKeywords != null) { - for (final String s : this.tokenHiddenKeywords) { - for (final Card c : tokens) { - c.addHiddenExtrinsicKeyword(s); - } - } + final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size())); + final TokenInfo tokenInfo = new TokenInfo(substitutedName, imageName, + cost, substitutedTypes, this.tokenKeywords, finalPower, finalToughness); + final List tokens = tokenInfo.makeTokenWithMultiplier(controller, finalAmount, cause != null); + + grantHiddenKeywords(tokens); + grantAbilities(tokens, root); + grantTriggers(tokens, root); + grantSvars(tokens, root); + grantStatics(tokens, root); + + for (Card tok : tokens) { + if (this.tokenTapped) { + tok.setTapped(true); + } + // Should this be catching the Card that's returned? + Card c = game.getAction().moveToPlay(tok, sa); + + if (sa.hasParam("AtEOTTrig")) { + addSelfTrigger(sa, sa.getParam("AtEOTTrig"), c); } - // Grant SVars - if (this.tokenSVars != null) { - for (final String s : this.tokenSVars) { - String actualSVar = AbilityUtils.getSVar(root, s); - String name = s; - if (actualSVar.startsWith("SVar")) { - actualSVar = actualSVar.split("SVar:")[1]; - name = actualSVar.split(":")[0]; - actualSVar = actualSVar.split(":")[1]; - } - for (final Card c : tokens) { - c.setSVar(name, actualSVar); - } - } + if (inCombat) { + combatChanged = addTokenToCombat(game, c, controller, sa, host) || combatChanged; } - // Grant abilities - if (this.tokenAbilities != null) { - for (final String s : this.tokenAbilities) { - final String actualAbility = AbilityUtils.getSVar(root, s); - for (final Card c : tokens) { - final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c); - // Set intrinsic, so that effects like Clone will copy these. - grantedAbility.setIntrinsic(true); - c.addSpellAbility(grantedAbility); - } + c.updateStateForView(); + + if (remember) { + game.getCardState(sa.getHostCard()).addRemembered(c); + } + if (imprint) { + game.getCardState(sa.getHostCard()).addImprintedCard(c); + } + if (sa.hasParam("RememberSource")) { + game.getCardState(c).addRemembered(host); + } + if (sa.hasParam("TokenRemembered")) { + final Card token = game.getCardState(c); + final String remembered = sa.getParam("TokenRemembered"); + for (final Object o : AbilityUtils.getDefinedObjects(host, remembered, sa)) { + token.addRemembered(o); } } - - // Grant triggers - if (this.tokenTriggers != null) { - for (final String s : this.tokenTriggers) { - final String actualTrigger = AbilityUtils.getSVar(root, s); - for (final Card c : tokens) { - final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, true); - final String ability = AbilityUtils.getSVar(root, parsedTrigger.getMapParams().get("Execute")); - parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(ability, c)); - c.addTrigger(parsedTrigger); - } - } - } - - // Grant static abilities - if (this.tokenStaticAbilities != null) { - for (final String s : this.tokenStaticAbilities) { - final String actualAbility = AbilityUtils.getSVar(root, s); - for (final Card c : tokens) { - c.addStaticAbility(actualAbility); - } - } - } - - for (Card tok : tokens) { - if (this.tokenTapped) { - tok.setTapped(true); - } - game.getAction().moveToPlay(tok, sa); - } - game.fireEvent(new GameEventTokenCreated()); - - boolean combatChanged = false; - for (final Card c : tokens) { - if (sa.hasParam("AtEOTTrig")) { - addSelfTrigger(sa, sa.getParam("AtEOTTrig"), c); - } - // To show the Abilities and Trigger on the Token - c.updateStateForView(); - if (this.tokenAttacking && game.getPhaseHandler().inCombat()) { - final Combat combat = game.getCombat(); - - // into battlefield attacking only should work if you are the attacking player - if (combat.getAttackingPlayer().equals(controller)) { - final FCollectionView defs = combat.getDefenders(); - final GameEntity defender = controller.getController().chooseSingleEntityForEffect(defs, sa, "Choose which defender to attack with " + c, false); - combat.addAttacker(c, defender); - combatChanged = true; - } - } - if (this.tokenBlocking != null && game.getPhaseHandler().inCombat()) { - final Combat combat = game.getPhaseHandler().getCombat(); - final Card attacker = Iterables.getFirst(AbilityUtils.getDefinedCards(host, this.tokenBlocking, sa), null); - if (attacker != null) { - final boolean wasBlocked = combat.isBlocked(attacker); - combat.addBlocker(attacker, c); - combat.orderAttackersForDamageAssignment(c); - - // Run triggers for new blocker and add it to damage assignment order - if (!wasBlocked) { - combat.setBlocked(attacker, true); - combat.addBlockerToDamageAssignmentOrder(attacker, c); - } - combatChanged = true; - } - } - if (remember) { - game.getCardState(sa.getHostCard()).addRemembered(c); - } - if (imprint) { - game.getCardState(sa.getHostCard()).addImprintedCard(c); - } - if (sa.hasParam("RememberSource")) { - game.getCardState(c).addRemembered(host); - } - if (sa.hasParam("TokenRemembered")) { - final Card token = game.getCardState(c); - final String remembered = sa.getParam("TokenRemembered"); - for (final Object o : AbilityUtils.getDefinedObjects(host, remembered, sa)) { - token.addRemembered(o); - } - } - } - if (tokenName.equals("Clue")) { // investigate trigger - controller.addInvestigatedThisTurn(); - } - if (combatChanged) { - game.updateCombatForView(); - game.fireEvent(new GameEventCombatChanged()); - } - allTokens.addAll(tokens); + allTokens.add(c); } + if (tokenName.equals("Clue")) { // investigate trigger + controller.addInvestigatedThisTurn(); + } + } + + game.fireEvent(new GameEventTokenCreated()); + + if (combatChanged) { + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); } if (sa.hasParam("AtEOT")) { registerDelayedTrigger(sa, sa.getParam("AtEOT"), allTokens); } } + + private String parseColorForImage() { + String originalColorDesc = ""; + for (final String col : this.tokenOriginalColors) { + originalColorDesc += MagicColor.toShortString(col); + if (originalColorDesc.equals("C")) { + return originalColorDesc; + } + } + return originalColorDesc; + } + + private String determineTokenColor(Card host) { + Set colorSet = new HashSet<>(); + final String[] substitutedColors = Arrays.copyOf(this.tokenColors, this.tokenColors.length); + for (int i = 0; i < substitutedColors.length; i++) { + if (substitutedColors[i].equals("ChosenColor")) { + // this currently only supports 1 chosen color + substitutedColors[i] = host.getChosenColor(); + } + } + + StringBuilder sb = new StringBuilder(); + for (final String col : substitutedColors) { + String str = MagicColor.toShortString(col); + if (str.equals("C")) { + return "1"; + } + + sb.append(str).append(" "); + } + + return sb.toString().trim(); + } + + private void grantHiddenKeywords(List tokens) { + // Grant rule changes + if (this.tokenHiddenKeywords != null) { + for (final String s : this.tokenHiddenKeywords) { + for (final Card c : tokens) { + c.addHiddenExtrinsicKeyword(s); + } + } + } + } + + private void grantAbilities(List tokens, SpellAbility root) { + if (this.tokenAbilities != null) { + for (final String s : this.tokenAbilities) { + final String actualAbility = AbilityUtils.getSVar(root, s); + for (final Card c : tokens) { + final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c); + // Set intrinsic, so that effects like Clone will copy these. + grantedAbility.setIntrinsic(true); + c.addSpellAbility(grantedAbility); + } + } + } + } + + private void grantTriggers(List tokens, SpellAbility root) { + if (this.tokenTriggers != null) { + for (final String s : this.tokenTriggers) { + final String actualTrigger = AbilityUtils.getSVar(root, s); + for (final Card c : tokens) { + final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, true); + final String ability = AbilityUtils.getSVar(root, parsedTrigger.getMapParams().get("Execute")); + parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(ability, c)); + c.addTrigger(parsedTrigger); + } + } + } + } + + private void grantSvars(List tokens, SpellAbility root) { + if (this.tokenSVars != null) { + for (final String s : this.tokenSVars) { + String actualSVar = AbilityUtils.getSVar(root, s); + String name = s; + if (actualSVar.startsWith("SVar")) { + actualSVar = actualSVar.split("SVar:")[1]; + name = actualSVar.split(":")[0]; + actualSVar = actualSVar.split(":")[1]; + } + for (final Card c : tokens) { + c.setSVar(name, actualSVar); + } + } + } + } + + private void grantStatics(List tokens, SpellAbility root) { + if (this.tokenStaticAbilities != null) { + for (final String s : this.tokenStaticAbilities) { + final String actualAbility = AbilityUtils.getSVar(root, s); + for (final Card c : tokens) { + c.addStaticAbility(actualAbility); + } + } + } + } + + private boolean addTokenToCombat(Game game, Card c, Player controller, SpellAbility sa, Card host) { + boolean combatChanged = false; + if (this.tokenAttacking) { + final Combat combat = game.getCombat(); + + // into battlefield attacking only should work if you are the attacking player + if (combat.getAttackingPlayer().equals(controller)) { + final FCollectionView defs = combat.getDefenders(); + final GameEntity defender = controller.getController().chooseSingleEntityForEffect(defs, sa, "Choose which defender to attack with " + c, false); + combat.addAttacker(c, defender); + combatChanged = true; + } + } + if (this.tokenBlocking != null) { + final Combat combat = game.getPhaseHandler().getCombat(); + final Card attacker = Iterables.getFirst(AbilityUtils.getDefinedCards(host, this.tokenBlocking, sa), null); + if (attacker != null) { + final boolean wasBlocked = combat.isBlocked(attacker); + combat.addBlocker(attacker, c); + combat.orderAttackersForDamageAssignment(c); + + // Run triggers for new blocker and add it to damage assignment order + if (!wasBlocked) { + combat.setBlocked(attacker, true); + combat.addBlockerToDamageAssignmentOrder(attacker, c); + } + combatChanged = true; + } + } + return combatChanged; + } } diff --git a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java index 6a24f644d63..258788efd51 100644 --- a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java +++ b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java @@ -133,7 +133,7 @@ public class TokenInfo { return sb.toString(); } - public List makeTokenWithMultiplier(final Player controller, final boolean applyMultiplier) { + public List makeTokenWithMultiplier(final Player controller, int amount, final boolean applyMultiplier) { final List list = Lists.newArrayList(); final Game game = controller.getGame(); @@ -158,8 +158,8 @@ public class TokenInfo { return list; } - for (int i = 0; i < multiplier; i++) { - list.add(makeOneToken(player)); + for (int i = 0; i < multiplier * amount; i++) { + list.add(makeOneToken(controller)); } return list; }