Merge pull request #2979 from Northmoc/chaos

MOC: Chaos Ensues!!
This commit is contained in:
Anthony Calosa
2023-04-22 08:50:45 +08:00
committed by GitHub
109 changed files with 506 additions and 223 deletions

View File

@@ -3580,6 +3580,18 @@ public class AbilityUtils {
}
return doXMath(amount, m, source, ctb);
}
if (value.startsWith("PlaneswalkedToThisTurn")) {
int found = 0;
String name = value.split(" ")[1];
List<Card> pwTo = player.getPlaneswalkedToThisTurn();
for (Card c : pwTo) {
if (c.getName().equals(name)) {
found++;
break;
}
}
return doXMath(found, m, source, ctb);
}
return doXMath(0, m, source, ctb);
}

View File

@@ -27,6 +27,7 @@ public enum ApiType {
BecomeMonarch (BecomeMonarchEffect.class),
BecomesBlocked (BecomesBlockedEffect.class),
BidLife (BidLifeEffect.class),
BlankLine (BlankLineEffect.class),
Block (BlockEffect.class),
Bond (BondEffect.class),
Branch (BranchEffect.class),
@@ -37,6 +38,7 @@ public enum ApiType {
ChangeX (ChangeXEffect.class),
ChangeZone (ChangeZoneEffect.class),
ChangeZoneAll (ChangeZoneAllEffect.class),
ChaosEnsues (ChaosEnsuesEffect.class),
Charm (CharmEffect.class),
ChooseCard (ChooseCardEffect.class),
ChooseColor (ChooseColorEffect.class),

View File

@@ -0,0 +1,16 @@
package forge.game.ability.effects;
import forge.game.ability.SpellAbilityEffect;
import forge.game.spellability.SpellAbility;
public class BlankLineEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
return "\r\n";
}
@Override
public void resolve(SpellAbility sa) {
// this "effect" just allows spacing to look better for certain card displays
}
}

View File

@@ -0,0 +1,74 @@
package forge.game.ability.effects;
import com.google.common.collect.Lists;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import java.util.*;
public class ChaosEnsuesEffect extends SpellAbilityEffect {
/** 311.7. Each plane card has a triggered ability that triggers “Whenever chaos ensues.” These are called
chaos abilities. Each one is indicated by a chaos symbol to the left of the ability, though the symbol
itself has no special rules meaning. This ability triggers if the chaos symbol is rolled on the planar
die (see rule 901.9b), if a resolving spell or ability says that chaos ensues, or if a resolving spell or
ability states that chaos ensues for a particular object. In the last case, the chaos ability can trigger
even if that plane card is still in the planar deck but revealed. A chaos ability is controlled by the
current planar controller. **/
@Override
public void resolve(SpellAbility sa) {
final Card host = sa.getHostCard();
final Player activator = sa.getActivatingPlayer();
final Game game = activator.getGame();
if (game.getActivePlanes() == null) { // not a planechase game, nothing happens
return;
}
Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(activator);
Map<Integer, EnumSet<ZoneType>> tweakedTrigs = new HashMap<>();
List<Card> affected = Lists.newArrayList();
if (sa.hasParam("Defined")) {
for (final Card c : AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa)) {
for (Trigger t : c.getTriggers()) {
if (t.getMode() == TriggerType.ChaosEnsues) { // also allow current zone for any Defined
//String zones = t.getParam("TriggerZones");
//t.putParam("TriggerZones", zones + "," + c.getZone().getZoneType().toString());
EnumSet<ZoneType> zones = (EnumSet<ZoneType>) t.getActiveZone();
tweakedTrigs.put(t.getId(), zones);
zones.add(c.getZone().getZoneType());
t.setActiveZone(zones);
affected.add(c);
game.getTriggerHandler().registerOneTrigger(t);
}
}
}
runParams.put(AbilityKey.Affected, affected);
if (affected.isEmpty()) { // if no Defined has chaos ability, don't trigger non Defined
return;
}
}
game.getTriggerHandler().runTrigger(TriggerType.ChaosEnsues, runParams,false);
for (Map.Entry<Integer, EnumSet<ZoneType>> e : tweakedTrigs.entrySet()) {
for (Card c : affected) {
for (Trigger t : c.getTriggers()) {
if (t.getId() == e.getKey()) {
EnumSet<ZoneType> zones = e.getValue();
t.setActiveZone(zones);
}
}
}
}
}
}

View File

@@ -13,8 +13,14 @@ public class PlaneswalkEffect extends SpellAbilityEffect {
public void resolve(SpellAbility sa) {
Game game = sa.getActivatingPlayer().getGame();
for (Player p : game.getPlayers()) {
p.leaveCurrentPlane();
if (game.getActivePlanes() == null) { // not a planechase game, nothing happens
return;
}
if (!sa.hasParam("DontPlaneswalkAway")) {
for (Player p : game.getPlayers()) {
p.leaveCurrentPlane();
}
}
if (sa.hasParam("Defined")) {
CardCollectionView destinations = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);

View File

@@ -1,12 +1,6 @@
package forge.game.ability.effects;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import forge.game.PlanarDice;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -16,17 +10,17 @@ import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.trigger.WrappedAbility;
import java.util.List;
public class RunChaosEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
Map<AbilityKey, Object> map = AbilityKey.mapFromPlayer(sa.getActivatingPlayer());
map.put(AbilityKey.Result, PlanarDice.Chaos);
List<SpellAbility> validSA = Lists.newArrayList();
for (final Card c : getTargetCards(sa)) {
for (Trigger t : c.getTriggers()) {
if (TriggerType.PlanarDice.equals(t.getMode()) && t.performTest(map)) {
if (t.getMode() == TriggerType.ChaosEnsues) {
SpellAbility triggerSA = t.ensureAbility().copy(sa.getActivatingPlayer());
Player decider = sa.getActivatingPlayer();
@@ -44,5 +38,4 @@ public class RunChaosEffect extends SpellAbilityEffect {
}
sa.getActivatingPlayer().getController().orderAndPlaySimultaneousSa(validSA);
}
}

View File

@@ -6,6 +6,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import forge.util.Lang;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ArrayListMultimap;
@@ -35,13 +36,13 @@ public class VoteEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(final SpellAbility sa) {
final StringBuilder sb = new StringBuilder();
sb.append(StringUtils.join(getDefinedPlayersOrTargeted(sa), ", "));
sb.append(" vote ");
sb.append(Lang.joinHomogenous(getDefinedPlayersOrTargeted(sa))).append(" vote ");
if (sa.hasParam("VoteType")) {
sb.append(StringUtils.join(sa.getParam("VoteType").split(","), " or "));
sb.append("for ").append(StringUtils.join(sa.getParam("VoteType").split(","), " or "));
} else if (sa.hasParam("VoteMessage")) {
sb.append(sa.getParam("VoteMessage"));
}
sb.append(".");
return sb.toString();
}

View File

@@ -350,9 +350,17 @@ public class CardFactory {
planesWalkTrigger.setOverridingAbility(AbilityFactory.getAbility(rolledWalk, card));
card.addTrigger(planesWalkTrigger);
String chaosTrig = "Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Static$ True";
String rolledChaos = "DB$ ChaosEnsues";
Trigger chaosTrigger = TriggerHandler.parseTrigger(chaosTrig, card, true);
chaosTrigger.setOverridingAbility(AbilityFactory.getAbility(rolledChaos, card));
card.addTrigger(chaosTrigger);
String specialA = "ST$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | Activator$ Player | SpecialAction$ True" +
" | ActivationZone$ Command | SpellDescription$ Roll the planar dice. X is equal to the number of " +
"times you have previously taken this action this turn.";
"times you have previously taken this action this turn. | CostDesc$ {X}: ";
SpellAbility planarRoll = AbilityFactory.getAbility(specialA, card);
planarRoll.setSVar("X", "Count$PlanarDiceSpecialActionThisTurn");

View File

@@ -209,6 +209,7 @@ public class Player extends GameEntity implements Comparable<Player> {
private Map<Card, Card> maingameCardsMap = Maps.newHashMap();
private CardCollection currentPlanes = new CardCollection();
private CardCollection planeswalkedToThisTurn = new CardCollection();
private PlayerStatistics stats = new PlayerStatistics();
private PlayerController controller;
@@ -1918,6 +1919,10 @@ public class Player extends GameEntity implements Comparable<Player> {
completedDungeons.clear();
}
public final List<Card> getPlaneswalkedToThisTurn() {
return planeswalkedToThisTurn;
}
public final void altWinBySpellEffect(final String sourceName) {
if (cantWin()) {
System.out.println("Tried to win, but currently can't.");
@@ -2458,6 +2463,7 @@ public class Player extends GameEntity implements Comparable<Player> {
setNumManaConversion(0);
damageReceivedThisTurn.clear();
planeswalkedToThisTurn.clear();
// set last turn nr
if (game.getPhaseHandler().isPlayerTurn(this)) {
@@ -2617,7 +2623,7 @@ public class Player extends GameEntity implements Comparable<Player> {
* Then runs triggers.
*/
public void planeswalkTo(SpellAbility sa, final CardCollectionView destinations) {
System.out.println(getName() + ": planeswalk to " + destinations.toString());
System.out.println(getName() + " planeswalks to " + destinations.toString());
currentPlanes.addAll(destinations);
game.getView().updatePlanarPlayer(getView());
@@ -2625,8 +2631,9 @@ public class Player extends GameEntity implements Comparable<Player> {
moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield());
moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard());
for (Card c : currentPlanes) {
for (Card c : destinations) {
game.getAction().moveTo(ZoneType.Command, c, sa, moveParams);
planeswalkedToThisTurn.add(c);
//getZone(ZoneType.PlanarDeck).remove(c);
//getZone(ZoneType.Command).add(c);
}
@@ -2634,7 +2641,7 @@ public class Player extends GameEntity implements Comparable<Player> {
game.setActivePlanes(currentPlanes);
//Run PlaneswalkedTo triggers here.
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Cards, currentPlanes);
runParams.put(AbilityKey.Cards, destinations);
game.getTriggerHandler().runTrigger(TriggerType.PlaneswalkedTo, runParams, false);
view.updateCurrentPlaneName(currentPlanes.toString().replaceAll(" \\(.*","").replace("[",""));
}

View File

@@ -0,0 +1,63 @@
package forge.game.trigger;
import forge.game.GameObject;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import java.util.Map;
public class TriggerChaosEnsues extends Trigger {
/**
* <p>
* Constructor for Trigger_ChaosEnsues
* </p>
*
* @param params
* a {@link java.util.HashMap} object.
* @param host
* a {@link forge.game.card.Card} object.
* @param intrinsic
* the intrinsic
*/
public TriggerChaosEnsues(final Map<String, String> params, final Card host, final boolean intrinsic) {
super(params, host, intrinsic);
}
/* (non-Javadoc)
* @see forge.card.trigger.Trigger#performTest(java.util.Map)
*/
@Override
public boolean performTest(Map<AbilityKey, Object> runParams) {
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
return false;
}
if (runParams.containsKey(AbilityKey.Affected)) {
final Object o = runParams.get(AbilityKey.Affected);
if (o instanceof GameObject) {
final GameObject c = (GameObject) o;
if (!c.equals(this.getHostCard())) {
return false;
}
} else if (o instanceof Iterable<?>) {
for (Object o2 : (Iterable<?>) o) {
if (!o2.equals(this.getHostCard())) {
return false;
}
}
}
}
return true;
}
@Override
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player);
}
@Override
public String getImportantStackObjects(SpellAbility sa) {
return "";
}
}

View File

@@ -39,6 +39,7 @@ public enum TriggerType {
ChangesController(TriggerChangesController.class),
ChangesZone(TriggerChangesZone.class),
ChangesZoneAll(TriggerChangesZoneAll.class),
ChaosEnsues(TriggerChaosEnsues.class),
Clashed(TriggerClashed.class),
ClassLevelGained(TriggerClassLevelGained.class),
ConjureAll(TriggerConjureAll.class),