From 8e5975e3e9b61b75b239d5781a16962ba0f6e5f2 Mon Sep 17 00:00:00 2001 From: Agetian Date: Thu, 28 Sep 2017 17:31:04 +0000 Subject: [PATCH] - Added Celestial Convergence. - Added new effect AF GameDrawn that creates an intentional draw situation by game effect. - Divine Intervention now uses this effect. git-svn-id: http://svn.slightlymagic.net/forge/trunk@35816 269b9781-a132-4a9b-9d4e-f004f1b56b58 --- .../src/main/java/forge/game/Match.java | 3 ++ .../main/java/forge/game/ability/ApiType.java | 4 +-- .../game/ability/effects/GameDrawEffect.java | 27 +++++++++++++++ .../java/forge/game/card/CardFactoryUtil.java | 33 +++++++++++++++++++ .../forge/game/player/GameLossReason.java | 4 ++- .../main/java/forge/game/player/Player.java | 4 +++ .../java/forge/game/player/PlayerOutcome.java | 6 +++- .../cardsfolder/c/celestial_convergence.txt | 14 ++++++++ .../res/cardsfolder/d/divine_intervention.txt | 2 +- 9 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/ability/effects/GameDrawEffect.java create mode 100644 forge-gui/res/cardsfolder/c/celestial_convergence.txt diff --git a/forge-game/src/main/java/forge/game/Match.java b/forge-game/src/main/java/forge/game/Match.java index f50f63f4..e17e2223 100644 --- a/forge-game/src/main/java/forge/game/Match.java +++ b/forge-game/src/main/java/forge/game/Match.java @@ -143,6 +143,9 @@ public class Match { public Multiset getGamesWon() { final Multiset won = HashMultiset.create(players.size()); for (final GameOutcome go : gamesPlayedRo) { + if (go.getWinningPlayer() == null) { + return won; + } won.add(go.getWinningPlayer().getRegisteredPlayer()); } return won; diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index ba311a9a..1dc40982 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -1,13 +1,12 @@ package forge.game.ability; +import com.google.common.collect.Maps; import forge.game.ability.effects.*; import forge.util.ReflectionUtil; import java.util.Map; -import com.google.common.collect.Maps; - /** * TODO: Write javadoc for this type. * @@ -76,6 +75,7 @@ public enum ApiType { GainControl (ControlGainEffect.class), GainLife (LifeGainEffect.class), GainOwnership (OwnershipGainEffect.class), + GameDrawn (GameDrawEffect.class), GenericChoice (ChooseGenericEffect.class), Goad (GoadEffect.class), Haunt (HauntEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/GameDrawEffect.java b/forge-game/src/main/java/forge/game/ability/effects/GameDrawEffect.java new file mode 100644 index 00000000..6368c1ee --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/GameDrawEffect.java @@ -0,0 +1,27 @@ +package forge.game.ability.effects; + +import forge.game.GameEndReason; +import forge.game.ability.SpellAbilityEffect; +import forge.game.player.Player; +import forge.game.spellability.SpellAbility; + +public class GameDrawEffect extends SpellAbilityEffect { + + /* (non-Javadoc) + * @see forge.card.abilityfactory.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility) + */ + @Override + protected String getStackDescription(SpellAbility sa) { + return "The game is a draw."; + } + + @Override + public void resolve(SpellAbility sa) { + for (Player p : sa.getHostCard().getGame().getPlayers()) { + p.intentionalDraw(); + } + sa.getHostCard().getGame().setGameOver(GameEndReason.Draw); + } + +} + diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 146704f2..54134a80 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -467,6 +467,39 @@ public class CardFactoryUtil { return doXMath(n, m, source); } + if (l[0].startsWith("TiedForHighestLife")) { + int maxLife = Integer.MIN_VALUE; + for (final Player player : players) { + int highestTotal = playerXProperty(player, "LifeTotal", source); + if (highestTotal > maxLife) { + maxLife = highestTotal; + } + } + int numTied = 0; + for (final Player player : players) { + if (player.getLife() == maxLife) { + numTied++; + } + } + return doXMath(numTied, m, source); + } + + if (l[0].startsWith("TiedForLowestLife")) { + int minLife = Integer.MAX_VALUE; + for (final Player player : players) { + int lowestTotal = playerXProperty(player, "LifeTotal", source); + if (lowestTotal < minLife) { + minLife = lowestTotal; + } + } + int numTied = 0; + for (final Player player : players) { + if (player.getLife() == minLife) { + numTied++; + } + } + return doXMath(numTied, m, source); + } final String[] sq; sq = l[0].split("\\."); diff --git a/forge-game/src/main/java/forge/game/player/GameLossReason.java b/forge-game/src/main/java/forge/game/player/GameLossReason.java index abf59117..63194753 100644 --- a/forge-game/src/main/java/forge/game/player/GameLossReason.java +++ b/forge-game/src/main/java/forge/game/player/GameLossReason.java @@ -36,7 +36,9 @@ public enum GameLossReason { CommanderDamage, - OpponentWon + OpponentWon, + + IntentionalDraw // not a real "game loss" as such /* * DoorToNothingness, // Door To Nothingness's ability activated diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index fdb4914a..8d2e6172 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1807,6 +1807,10 @@ public class Player extends GameEntity implements Comparable { setOutcome(PlayerOutcome.concede()); } + public final void intentionalDraw() { + setOutcome(PlayerOutcome.draw()); + } + public final boolean cantLose() { if (getOutcome() != null && getOutcome().lossState == GameLossReason.Conceded) { return false; diff --git a/forge-game/src/main/java/forge/game/player/PlayerOutcome.java b/forge-game/src/main/java/forge/game/player/PlayerOutcome.java index 5ebaa56c..dbb8c6a9 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerOutcome.java +++ b/forge-game/src/main/java/forge/game/player/PlayerOutcome.java @@ -17,13 +17,16 @@ public class PlayerOutcome { /** * TODO: Write javadoc for this method. - * @param sourceName * @return */ public static PlayerOutcome win() { return new PlayerOutcome(null, null, null); } + public static PlayerOutcome draw() { + return new PlayerOutcome(null, GameLossReason.IntentionalDraw, null); + } + public static PlayerOutcome altWin(String sourceName) { return new PlayerOutcome(sourceName, null, null); } @@ -69,6 +72,7 @@ public class PlayerOutcome { case OpponentWon: return "lost because an opponent has won by spell '" + loseConditionSpell + "'"; case SpellEffect: return "lost due to effect of spell '" + loseConditionSpell + "'"; case CommanderDamage: return "lost due to accumulation of 21 damage from generals"; + case IntentionalDraw: return "accepted that the game is a draw"; } return "lost for unknown reason (this is a bug)"; } diff --git a/forge-gui/res/cardsfolder/c/celestial_convergence.txt b/forge-gui/res/cardsfolder/c/celestial_convergence.txt new file mode 100644 index 00000000..5a5343e7 --- /dev/null +++ b/forge-gui/res/cardsfolder/c/celestial_convergence.txt @@ -0,0 +1,14 @@ +Name:Celestial Convergence +ManaCost:2 W W +Types:Enchantment +K:etbCounter:OMEN:7 +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | ActiveZones$ Battlefield | Execute$ TrigRemoveCtr | TriggerDescription$ At the beginning of your upkeep, remove an omen counter from CARDNAME. If there are no omen counters on CARDNAME, the player with the highest life total wins the game. If two or more players are tied for highest life total, the game is a draw. +SVar:TrigRemoveCtr:DB$ RemoveCounter | Defined$ Self | CounterType$ OMEN | CounterNum$ 1 | SubAbility$ WinnerDecided +SVar:WinnerDecided:DB$ WinsGame | Defined$ Player.LifeEquals_X | References$ X,NumHighestLife | ConditionPresent$ Card.Self+counters_EQ0_OMEN | ConditionCheckSVar$ NumHighestLife | ConditionSVarCompare$ LT2 | SubAbility$ GameIsADraw +SVar:GameIsADraw:DB$ GameDrawn | ConditionPresent$ Card.Self+counters_EQ0_OMEN | References$ NumHighestLife | ConditionCheckSVar$ NumHighestLife | ConditionSVarCompare$ GE2 +SVar:Picture:http://www.wizards.com/global/images/magic/general/celestial_convergence.jpg +SVar:X:PlayerCountPlayers$HighestLifeTotal +SVar:NumHighestLife:PlayerCountPlayers$TiedForHighestLife +SVar:RemRandomDeck:True +SVar:RemAIDeck:True +Oracle:Celestial Convergence enters the battlefield with seven omen counters on it.\nAt the beginning of your upkeep, remove an omen counter from Celestial Convergence. If there are no omen counters on Celestial Convergence, the player with the highest life total wins the game. If two or more players are tied for highest life total, the game is a draw. diff --git a/forge-gui/res/cardsfolder/d/divine_intervention.txt b/forge-gui/res/cardsfolder/d/divine_intervention.txt index 6c44258a..cc3dafd3 100644 --- a/forge-gui/res/cardsfolder/d/divine_intervention.txt +++ b/forge-gui/res/cardsfolder/d/divine_intervention.txt @@ -4,7 +4,7 @@ Types:Enchantment K:etbCounter:INTERVENTION:2 T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | ActiveZones$ Battlefield | Execute$ TrigRemoveCtr | TriggerDescription$ At the beginning of your upkeep, remove an intervention counter from CARDNAME. When you remove the last intervention counter from CARDNAME, the game is a draw. SVar:TrigRemoveCtr:DB$ RemoveCounter | Defined$ Self | CounterType$ INTERVENTION | CounterNum$ 1 | SubAbility$ GameIsADraw -SVar:GameIsADraw:DB$ LosesGame | Defined$ Player | ConditionPresent$ Card.Self+counters_EQ0_INTERVENTION +SVar:GameIsADraw:DB$ GameDrawn | ConditionPresent$ Card.Self+counters_EQ0_INTERVENTION SVar:Picture:http://www.wizards.com/global/images/magic/general/divine_intervention.jpg SVar:RemRandomDeck:True SVar:RemAIDeck:True