mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
PIP: Radiation mechanic implementation (#4204)
* PIP: Radiation mechanic implementation * remove OVERRIDE * remove unused property
This commit is contained in:
@@ -338,14 +338,14 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix) {
|
||||
public void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix, boolean addSuffix) {
|
||||
for (Card c : cards) {
|
||||
AiCardMemory.rememberCard(player, c, AiCardMemory.MemorySet.REVEALED_CARDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal(List<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix) {
|
||||
public void reveal(List<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix, boolean addSuffix) {
|
||||
for (CardView cv : cards) {
|
||||
AiCardMemory.rememberCard(player, player.getGame().findByView(cv), AiCardMemory.MemorySet.REVEALED_CARDS);
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.PumpAll, PumpAllAi.class)
|
||||
.put(ApiType.PutCounter, CountersPutAi.class)
|
||||
.put(ApiType.PutCounterAll, CountersPutAllAi.class)
|
||||
.put(ApiType.Radiation, AlwaysPlayAi.class)
|
||||
.put(ApiType.RearrangeTopOfLibrary, RearrangeTopOfLibraryAi.class)
|
||||
.put(ApiType.Regenerate, RegenerateAi.class)
|
||||
.put(ApiType.RegenerateAll, RegenerateAllAi.class)
|
||||
|
||||
@@ -1926,15 +1926,15 @@ public class GameAction {
|
||||
revealTo(cards, to, null);
|
||||
}
|
||||
public void revealTo(final CardCollectionView cards, final Player to, String messagePrefix) {
|
||||
revealTo(cards, Collections.singleton(to), messagePrefix);
|
||||
revealTo(cards, Collections.singleton(to), messagePrefix, true);
|
||||
}
|
||||
public void revealTo(final Card card, final Iterable<Player> to) {
|
||||
revealTo(new CardCollection(card), to);
|
||||
}
|
||||
public void revealTo(final CardCollectionView cards, final Iterable<Player> to) {
|
||||
revealTo(cards, to, null);
|
||||
revealTo(cards, to, null, true);
|
||||
}
|
||||
public void revealTo(final CardCollectionView cards, final Iterable<Player> to, String messagePrefix) {
|
||||
public void revealTo(final CardCollectionView cards, final Iterable<Player> to, String messagePrefix, boolean addSuffix) {
|
||||
if (cards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1942,7 +1942,7 @@ public class GameAction {
|
||||
final ZoneType zone = cards.getFirst().getZone().getZoneType();
|
||||
final Player owner = cards.getFirst().getOwner();
|
||||
for (final Player p : to) {
|
||||
p.getController().reveal(cards, zone, owner, messagePrefix);
|
||||
p.getController().reveal(cards, zone, owner, messagePrefix, addSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1953,18 +1953,25 @@ public class GameAction {
|
||||
reveal(cards, cardOwner, dontRevealToOwner, null);
|
||||
}
|
||||
public void reveal(CardCollectionView cards, Player cardOwner, boolean dontRevealToOwner, String messagePrefix) {
|
||||
reveal(cards, cardOwner, dontRevealToOwner, messagePrefix, true);
|
||||
}
|
||||
public void reveal(CardCollectionView cards, Player cardOwner, boolean dontRevealToOwner, String messagePrefix, boolean msgAddSuffix) {
|
||||
Card firstCard = Iterables.getFirst(cards, null);
|
||||
if (firstCard == null) {
|
||||
return;
|
||||
}
|
||||
reveal(cards, game.getZoneOf(firstCard).getZoneType(), cardOwner, dontRevealToOwner, messagePrefix);
|
||||
reveal(cards, game.getZoneOf(firstCard).getZoneType(), cardOwner, dontRevealToOwner, messagePrefix, msgAddSuffix);
|
||||
}
|
||||
|
||||
public void reveal(CardCollectionView cards, ZoneType zt, Player cardOwner, boolean dontRevealToOwner, String messagePrefix) {
|
||||
reveal(cards, zt, cardOwner, dontRevealToOwner, messagePrefix, true);
|
||||
}
|
||||
public void reveal(CardCollectionView cards, ZoneType zt, Player cardOwner, boolean dontRevealToOwner, String messagePrefix, boolean msgAddSuffix) {
|
||||
for (Player p : game.getPlayers()) {
|
||||
if (dontRevealToOwner && cardOwner == p) {
|
||||
continue;
|
||||
}
|
||||
p.getController().reveal(cards, zt, cardOwner, messagePrefix);
|
||||
p.getController().reveal(cards, zt, cardOwner, messagePrefix, msgAddSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import forge.LobbyPlayer;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.event.*;
|
||||
import forge.game.event.GameEventCardDamaged.DamageType;
|
||||
import forge.game.player.Player;
|
||||
@@ -224,6 +225,21 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventPlayerRadiation ev) {
|
||||
String message;
|
||||
final int change = ev.change;
|
||||
String radCtr = CounterEnumType.RAD.getName().toLowerCase() + " " +
|
||||
Localizer.getInstance().getMessage("lblCounter").toLowerCase();
|
||||
if (change >= 0) message = localizer.getMessage("lblLogPlayerRadiation",
|
||||
ev.receiver.toString(), Lang.nounWithNumeralExceptOne(String.valueOf(change), radCtr),
|
||||
ev.source.toString());
|
||||
else message = localizer.getMessage("lblLogPlayerRadRemove",
|
||||
ev.receiver.toString(), Lang.nounWithNumeralExceptOne(String.valueOf(Math.abs(change)), radCtr),
|
||||
ev.source.toString());
|
||||
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(final GameEventAttackersDeclared ev) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -139,6 +139,7 @@ public enum ApiType {
|
||||
PumpAll (PumpAllEffect.class),
|
||||
PutCounter (CountersPutEffect.class),
|
||||
PutCounterAll (CountersPutAllEffect.class),
|
||||
Radiation (RadiationEffect.class),
|
||||
RearrangeTopOfLibrary (RearrangeTopOfLibraryEffect.class),
|
||||
Regenerate (RegenerateEffect.class),
|
||||
RegenerateAll (RegenerateAllEffect.class),
|
||||
|
||||
@@ -98,6 +98,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean revealTitle = (sa.hasParam("RevealTitle"));
|
||||
for (Player p : tgtPlayers) {
|
||||
if (!p.isInGame()) {
|
||||
p = getNewChooser(sa, activator, p);
|
||||
@@ -267,14 +268,15 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("Reveal") && !sa.hasParam("SecretlyChoose")) {
|
||||
game.getAction().reveal(chosen, p, dontRevealToOwner, sa.hasParam("RevealTitle") ?
|
||||
sa.getParam("RevealTitle") : Localizer.getInstance().getMessage("lblChosenCards") + " ");
|
||||
game.getAction().reveal(chosen, p, dontRevealToOwner, revealTitle ? sa.getParam("RevealTitle") :
|
||||
Localizer.getInstance().getMessage("lblChosenCards") + " ", !revealTitle);
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("Reveal") && sa.hasParam("SecretlyChoose")) {
|
||||
for (final Player p : tgtPlayers) {
|
||||
game.getAction().reveal(chosen, p, true, sa.hasParam("RevealTitle") ?
|
||||
sa.getParam("RevealTitle") : Localizer.getInstance().getMessage("lblChosenCards") + " ");
|
||||
game.getAction().reveal(chosen, p, true, revealTitle ?
|
||||
sa.getParam("RevealTitle") : Localizer.getInstance().getMessage("lblChosenCards") + " ",
|
||||
!revealTitle);
|
||||
}
|
||||
}
|
||||
host.setChosenCards(chosen);
|
||||
|
||||
@@ -88,7 +88,6 @@ public class ClashEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
final StringBuilder reveal = new StringBuilder();
|
||||
reveal.append("OVERRIDE "); //will return substring with the original message parsed here..
|
||||
Card pCard = null;
|
||||
Card oCard = null;
|
||||
final CardCollection toReveal = new CardCollection();
|
||||
@@ -122,7 +121,7 @@ public class ClashEffect extends SpellAbilityEffect {
|
||||
reveal.append(winner + " " + Localizer.getInstance().getMessage("lblWinsClash") + ".");
|
||||
}
|
||||
|
||||
player.getGame().getAction().revealTo(toReveal, player.getGame().getPlayers(), reveal.toString());
|
||||
player.getGame().getAction().revealTo(toReveal, player.getGame().getPlayers(), reveal.toString(), false);
|
||||
|
||||
clashMoveToTopOrBottom(player, pCard, sa);
|
||||
clashMoveToTopOrBottom(opponent, oCard, sa);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameLogEntryType;
|
||||
import forge.game.ability.AbilityKey;
|
||||
@@ -18,6 +16,8 @@ import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class MillEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
@@ -47,6 +47,8 @@ public class MillEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
|
||||
String toZoneStr = destination.equals(ZoneType.Graveyard) ? "" : " (" +
|
||||
Localizer.getInstance().getMessage("lblMilledToZone", destination.getTranslatedName()) + ")";
|
||||
if (sa.hasParam("Optional")) {
|
||||
String d = destination.equals(ZoneType.Graveyard) ? "" : " (" + destination.getTranslatedName() + ")";
|
||||
final String prompt = TextUtil.concatWithSpace(Localizer.getInstance().
|
||||
@@ -61,11 +63,12 @@ public class MillEffect extends SpellAbilityEffect {
|
||||
// graveyard to figure out which ones were milled.
|
||||
if (!facedown && reveal) { // do not reveal when exiling face down
|
||||
if (showRevealDialog) {
|
||||
game.getAction().reveal(milled, p, false);
|
||||
final String message = Localizer.getInstance().getMessage("lblMilledCards");
|
||||
final boolean addSuffix = !toZoneStr.equals("");
|
||||
game.getAction().reveal(milled, destination, p, false, message, addSuffix);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(p).append(" milled ").append(milled).append(" to ").append(destination);
|
||||
p.getGame().getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString());
|
||||
p.getGame().getGameLog().add(GameLogEntryType.ZONE_CHANGE, p + " milled " +
|
||||
Lang.joinHomogenous(milled) + toZoneStr + ".");
|
||||
}
|
||||
if (destination.equals(ZoneType.Exile)) {
|
||||
for (final Card c : milled) {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.event.GameEventPlayerRadiation;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class RadiationEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final int toAdd = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Add", "0"), sa);
|
||||
final int toRem = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Remove", "0"), sa);
|
||||
final Map<Player, Integer> list = Maps.newHashMap();
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
|
||||
for (final Player p : getTargetPlayers(sa)) {
|
||||
if (!p.isInGame()) continue;
|
||||
|
||||
list.put(p, p.getCounters(CounterEnumType.RAD));
|
||||
if (toAdd >= 1) p.addRadCounters(toAdd, host, table);
|
||||
else if (toRem >= 1) p.removeRadCounters(toRem, host);
|
||||
}
|
||||
table.replaceCounterEffect(game, sa, true);
|
||||
for (final Player p : list.keySet()) {
|
||||
int oldCount = list.get(p);
|
||||
int newCount = p.getCounters(CounterEnumType.RAD);
|
||||
if (newCount > 0 && !p.hasRadiationEffect()) p.createRadiationEffect(host.getSetCode());
|
||||
if (oldCount < newCount) game.fireEvent(new GameEventPlayerRadiation(p, host, newCount - oldCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,9 @@ public class RevealEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (sa.hasParam("RevealToAll") || sa.hasParam("Random")) {
|
||||
game.getAction().reveal(revealed, p, false, sa.getParamOrDefault("RevealTitle", ""));
|
||||
boolean revealTitle = sa.hasParam("RevealTitle");
|
||||
game.getAction().reveal(revealed, p, false,
|
||||
revealTitle ? sa.getParam("RevealTitle") : "", !revealTitle);
|
||||
} else {
|
||||
game.getAction().reveal(revealed, p);
|
||||
}
|
||||
|
||||
@@ -427,6 +427,8 @@ public enum CounterEnumType {
|
||||
|
||||
POISON("POISN"),
|
||||
|
||||
RAD("RAD"),
|
||||
|
||||
TICKET("TICKET"),
|
||||
|
||||
// Keyword Counters
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package forge.game.event;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
|
||||
public class GameEventPlayerRadiation extends GameEvent {
|
||||
public final Player receiver;
|
||||
public final Card source;
|
||||
public final int change;
|
||||
|
||||
public GameEventPlayerRadiation(Player recv, Card src, int chng) {
|
||||
receiver = recv;
|
||||
source = src;
|
||||
change = chng;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public interface IGameEventVisitor<T> {
|
||||
T visit(GameEventPlayerDamaged gameEventPlayerDamaged);
|
||||
T visit(GameEventPlayerCounters event);
|
||||
T visit(GameEventPlayerPoisoned event);
|
||||
T visit(GameEventPlayerRadiation event);
|
||||
T visit(GameEventPlayerPriority event);
|
||||
T visit(GameEventPlayerShardsChanged event);
|
||||
T visit(GameEventPlayerStatsChanged event);
|
||||
@@ -90,6 +91,7 @@ public interface IGameEventVisitor<T> {
|
||||
public T visit(GameEventPlayerControl event) { return null; }
|
||||
public T visit(GameEventPlayerCounters event) { return null; }
|
||||
public T visit(GameEventPlayerPoisoned event) { return null; }
|
||||
public T visit(GameEventPlayerRadiation event) { return null; }
|
||||
public T visit(GameEventPlayerPriority event) { return null; }
|
||||
public T visit(GameEventPlayerShardsChanged event) { return null; }
|
||||
public T visit(GameEventPlayerStatsChanged event) { return null; }
|
||||
|
||||
@@ -17,43 +17,22 @@
|
||||
*/
|
||||
package forge.game.phase;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.GameStage;
|
||||
import forge.game.GameType;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import forge.game.*;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.effects.AddTurnEffect;
|
||||
import forge.game.ability.effects.SkipPhaseEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.CostEnlist;
|
||||
import forge.game.cost.CostExert;
|
||||
import forge.game.event.GameEventAttackersDeclared;
|
||||
import forge.game.event.GameEventBlockersDeclared;
|
||||
import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.event.GameEventCombatEnded;
|
||||
import forge.game.event.GameEventGameRestarted;
|
||||
import forge.game.event.GameEventPlayerPriority;
|
||||
import forge.game.event.GameEventPlayerStatsChanged;
|
||||
import forge.game.event.GameEventTokenStateUpdate;
|
||||
import forge.game.event.GameEventTurnBegan;
|
||||
import forge.game.event.GameEventTurnEnded;
|
||||
import forge.game.event.GameEventTurnPhase;
|
||||
import forge.game.event.*;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementResult;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
@@ -64,9 +43,14 @@ import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.CollectionSuppliers;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.maps.HashMapOfLists;
|
||||
import forge.util.maps.MapOfLists;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
@@ -299,6 +283,9 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
if (playerTurn.isArchenemy()) {
|
||||
playerTurn.setSchemeInMotion();
|
||||
}
|
||||
if (playerTurn.hasRadiationEffect()) {
|
||||
handleRadiation();
|
||||
}
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
// all Saga get Lore counter at the begin of pre combat
|
||||
for (Card c : playerTurn.getCardsIn(ZoneType.Battlefield)) {
|
||||
@@ -541,6 +528,36 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRadiation() {
|
||||
int numRad = playerTurn.getCounters(CounterEnumType.RAD);
|
||||
if (numRad == 0) playerTurn.removeRadiationEffect();
|
||||
else {
|
||||
final CardZoneTable table = new CardZoneTable();
|
||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||
moveParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield());
|
||||
moveParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard());
|
||||
final SpellAbility sa = new SpellAbility.EmptySa(playerTurn.getRadiationEffect(), playerTurn);
|
||||
final CardCollectionView milled = playerTurn.mill(numRad, ZoneType.Graveyard, sa,
|
||||
table, moveParams);
|
||||
game.getAction().reveal(milled, playerTurn, false,
|
||||
Localizer.getInstance().getMessage("lblMilledCards", playerTurn), false);
|
||||
game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, playerTurn + " milled " +
|
||||
Lang.joinHomogenous(milled) + ".");
|
||||
table.triggerChangesZoneAll(game, sa);
|
||||
int n = CardLists.filter(milled, Predicates.not(CardPredicates.Presets.LANDS)).size();
|
||||
final Map<Player, Integer> lossMap = Maps.newHashMap();
|
||||
final int lost = playerTurn.loseLife(n, false, false);
|
||||
if (lost > 0) {
|
||||
lossMap.put(playerTurn, lost);
|
||||
}
|
||||
if (!lossMap.isEmpty()) { // Run triggers if any player actually lost life
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPIMap(lossMap);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.LifeLostAll, runParams, false);
|
||||
}
|
||||
playerTurn.removeRadCounters(n, playerTurn.getRadiationEffect());
|
||||
}
|
||||
}
|
||||
|
||||
private void declareAttackersTurnBasedAction() {
|
||||
final Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost()
|
||||
? playerTurn
|
||||
|
||||
@@ -17,39 +17,9 @@
|
||||
*/
|
||||
package forge.game.player;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import forge.game.event.*;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Table;
|
||||
import com.google.common.collect.TreeBasedTable;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import forge.ImageKeys;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.card.CardStateName;
|
||||
@@ -58,36 +28,17 @@ import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.GameLogEntryType;
|
||||
import forge.game.GameStage;
|
||||
import forge.game.GameType;
|
||||
import forge.game.*;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.ability.effects.DetachedCardEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.keyword.Companion;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordCollection;
|
||||
import forge.game.event.*;
|
||||
import forge.game.keyword.*;
|
||||
import forge.game.keyword.KeywordCollection.KeywordCollectionView;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.keyword.KeywordsChange;
|
||||
import forge.game.mana.ManaPool;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
@@ -95,16 +46,10 @@ import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementResult;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.LandAbility;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.staticability.StaticAbilityCantBeCast;
|
||||
import forge.game.staticability.StaticAbilityCantDiscard;
|
||||
import forge.game.staticability.StaticAbilityCantBecomeMonarch;
|
||||
import forge.game.staticability.StaticAbilityCantDraw;
|
||||
import forge.game.staticability.StaticAbilityCantGainLosePayLife;
|
||||
import forge.game.staticability.StaticAbilityCantPutCounter;
|
||||
import forge.game.staticability.StaticAbilityCantTarget;
|
||||
import forge.game.staticability.StaticAbilityCantSetSchemesInMotion;
|
||||
import forge.game.staticability.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -114,13 +59,14 @@ import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.*;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -240,6 +186,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
private Card monarchEffect;
|
||||
private Card initiativeEffect;
|
||||
private Card blessingEffect;
|
||||
|
||||
private Card radiationEffect;
|
||||
private Card keywordEffect;
|
||||
|
||||
private Map<Long, Integer> additionalVotes = Maps.newHashMap();
|
||||
@@ -986,6 +934,18 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
getGame().fireEvent(new GameEventPlayerCounters(this, null, 0, 0));
|
||||
}
|
||||
|
||||
public final void addRadCounters(final int num, final Card source, GameEntityCounterTable table) {
|
||||
addCounter(CounterEnumType.RAD, num, source.getController(), table);
|
||||
}
|
||||
public final void removeRadCounters(final int num, final Card source) {
|
||||
int oldRad = getCounters(CounterEnumType.RAD);
|
||||
if (oldRad != 0) subtractCounter(CounterEnumType.RAD, num);
|
||||
|
||||
int newRad = getCounters(CounterEnumType.RAD);
|
||||
if (newRad == 0) removeRadiationEffect();
|
||||
if (oldRad != newRad) game.fireEvent(new GameEventPlayerRadiation(this, source, newRad - oldRad));
|
||||
}
|
||||
|
||||
// TODO Merge These calls into the primary counter calls
|
||||
public final int getPoisonCounters() {
|
||||
return getCounters(CounterEnumType.POISON);
|
||||
@@ -3462,6 +3422,42 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
}
|
||||
}
|
||||
|
||||
public final Card getRadiationEffect() {
|
||||
return radiationEffect;
|
||||
}
|
||||
public void createRadiationEffect(String setCode) {
|
||||
final PlayerZone com = getZone(ZoneType.Command);
|
||||
if (radiationEffect == null) {
|
||||
radiationEffect = new Card(game.nextCardId(), null, game);
|
||||
radiationEffect.setOwner(this);
|
||||
radiationEffect.setImmutable(true);
|
||||
radiationEffect.setImageKey("t:radiation");
|
||||
radiationEffect.setName("Radiation");
|
||||
radiationEffect.setSetCode(setCode);
|
||||
String desc = "Mode$ Continuous | Affected$ Card.Self | Description$ At the beginning of your precombat " +
|
||||
"main phase, if you have any rad counters, mill that many cards. For each nonland card milled " +
|
||||
"this way, you lose 1 life and a rad counter.";
|
||||
StaticAbility st = StaticAbility.create(desc, radiationEffect, radiationEffect.getCurrentState(), true);
|
||||
radiationEffect.addStaticAbility(st);
|
||||
radiationEffect.updateStateForView();
|
||||
}
|
||||
com.add(radiationEffect);
|
||||
this.updateZoneForView(com);
|
||||
}
|
||||
|
||||
public void removeRadiationEffect() {
|
||||
final PlayerZone com = getZone(ZoneType.Command);
|
||||
if (radiationEffect != null) {
|
||||
com.remove(radiationEffect);
|
||||
radiationEffect = null;
|
||||
this.updateZoneForView(com);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasRadiationEffect() {
|
||||
return radiationEffect != null;
|
||||
}
|
||||
|
||||
public void updateKeywordCardAbilityText() {
|
||||
if (getKeywordCard() == null)
|
||||
return;
|
||||
@@ -3735,7 +3731,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
lki.setZone(c.getZone());
|
||||
revealCards.add(lki);
|
||||
}
|
||||
game.getAction().revealTo(revealCards, otherPlayers, Localizer.getInstance().getMessage("lblRevealFaceDownCards"));
|
||||
game.getAction().revealTo(revealCards, otherPlayers, Localizer.getInstance().getMessage("lblRevealFaceDownCards"), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,8 +163,14 @@ public abstract class PlayerController {
|
||||
public final void reveal(CardCollectionView cards, ZoneType zone, Player owner) {
|
||||
reveal(cards, zone, owner, null);
|
||||
}
|
||||
public abstract void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix);
|
||||
public abstract void reveal(List<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix);
|
||||
public final void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix) {
|
||||
reveal(cards, zone, owner, null, true);
|
||||
}
|
||||
public abstract void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix, boolean addMsgSuffix);
|
||||
public final void reveal(List<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix) {
|
||||
reveal(cards, zone, owner, null, true);
|
||||
}
|
||||
public abstract void reveal(List<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix, boolean addMsgSuffix);
|
||||
|
||||
/** Shows message to player to reveal chosen cardName, creatureType, number etc. AI must analyze API to understand what that is */
|
||||
public abstract void notifyOfValue(SpellAbility saSource, GameObject realtedTarget, String value);
|
||||
|
||||
@@ -57,6 +57,7 @@ public class TriggerMilledAll extends Trigger {
|
||||
}
|
||||
|
||||
sa.setTriggeringObject(AbilityKey.Cards, cards);
|
||||
sa.setTriggeringObject(AbilityKey.Amount, cards.size());
|
||||
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ public class VField implements IVDoc<CField> {
|
||||
private final FLabel lblEnergy = new FLabel.Builder().fontAlign(SwingConstants.CENTER).fontStyle(Font.BOLD).icon(FSkin.getImage(FSkinProp.IMG_ENERGY)).iconInBackground().build();
|
||||
private final FLabel lblExperience = new FLabel.Builder().fontAlign(SwingConstants.CENTER).fontStyle(Font.BOLD).icon(FSkin.getImage(FSkinProp.IMG_EXPERIENCE)).iconInBackground().build();
|
||||
private final FLabel lblTicket = new FLabel.Builder().fontAlign(SwingConstants.CENTER).fontStyle(Font.BOLD).icon(FSkin.getImage(FSkinProp.IMG_TICKET)).iconInBackground().build();
|
||||
private final FLabel lblRad = new FLabel.Builder().fontAlign(SwingConstants.CENTER).fontStyle(Font.BOLD).icon(FSkin.getImage(FSkinProp.IMG_RAD)).iconInBackground().build();
|
||||
|
||||
private final PhaseIndicator phaseIndicator = new PhaseIndicator();
|
||||
|
||||
@@ -115,6 +116,7 @@ public class VField implements IVDoc<CField> {
|
||||
lblEnergy.setFocusable(false);
|
||||
lblExperience.setFocusable(false);
|
||||
lblTicket.setFocusable(false);
|
||||
lblRad.setFocusable(false);
|
||||
|
||||
avatarArea.setOpaque(false);
|
||||
avatarArea.setBackground(FSkin.getColor(FSkin.Colors.CLR_HOVER));
|
||||
@@ -236,6 +238,24 @@ public class VField implements IVDoc<CField> {
|
||||
avatarArea.add(lblLife, "w 100%!, h 20px!, wrap");
|
||||
}
|
||||
|
||||
private void addLblRad() {
|
||||
if (lblRad.isShowing() || lblExperience.isShowing() || lblEnergy.isShowing() || lblPoison.isShowing()) {
|
||||
return;
|
||||
}
|
||||
avatarArea.remove(lblLife);
|
||||
lblLife.setIcon(FSkin.getImage(FSkinProp.ICO_QUEST_LIFE));
|
||||
avatarArea.add(lblLife, "w 50%!, h 20px!, split 2");
|
||||
avatarArea.add(lblRad, "w 50%!, h 20px!, wrap");
|
||||
}
|
||||
|
||||
private void removeLblRad() {
|
||||
if (!lblRad.isShowing()) {
|
||||
return;
|
||||
}
|
||||
avatarArea.remove(lblRad);
|
||||
avatarArea.remove(lblLife);
|
||||
avatarArea.add(lblLife, "w 100%!, h 20px!, wrap");
|
||||
}
|
||||
|
||||
private void addLblExperience() {
|
||||
if (lblExperience.isShowing() || lblEnergy.isShowing() || lblPoison.isShowing()) {
|
||||
@@ -307,11 +327,13 @@ public class VField implements IVDoc<CField> {
|
||||
final int poison = player.getCounters(CounterEnumType.POISON);
|
||||
final int energy = player.getCounters(CounterEnumType.ENERGY);
|
||||
final int experience = player.getCounters(CounterEnumType.EXPERIENCE);
|
||||
final int rad = player.getCounters(CounterEnumType.RAD);
|
||||
final int ticket = player.getCounters(CounterEnumType.TICKET);
|
||||
|
||||
if (poison > 0) {
|
||||
removeLblEnergy();
|
||||
removeLblExperience();
|
||||
removeLblRad();
|
||||
removeLblTicket();
|
||||
addLblPoison();
|
||||
lblPoison.setText(String.valueOf(poison));
|
||||
@@ -326,6 +348,7 @@ public class VField implements IVDoc<CField> {
|
||||
|
||||
if (energy > 0) {
|
||||
removeLblExperience();
|
||||
removeLblRad();
|
||||
removeLblTicket();
|
||||
if (poison == 0) {
|
||||
addLblEnergy();
|
||||
@@ -336,6 +359,7 @@ public class VField implements IVDoc<CField> {
|
||||
}
|
||||
|
||||
if (experience > 0) {
|
||||
removeLblRad();
|
||||
removeLblTicket();
|
||||
if (poison == 0 && energy == 0) {
|
||||
addLblExperience();
|
||||
@@ -345,8 +369,18 @@ public class VField implements IVDoc<CField> {
|
||||
removeLblExperience();
|
||||
}
|
||||
|
||||
if (ticket > 0) {
|
||||
if (rad > 0) {
|
||||
removeLblTicket();
|
||||
if (poison == 0 && energy == 0 && experience == 0) {
|
||||
addLblRad();
|
||||
lblRad.setText(String.valueOf(rad));
|
||||
}
|
||||
} else {
|
||||
removeLblRad();
|
||||
}
|
||||
|
||||
if (ticket > 0) {
|
||||
if (poison == 0 && energy == 0 && experience == 0 && rad == 0) {
|
||||
addLblTicket();
|
||||
lblTicket.setText(String.valueOf(ticket));
|
||||
}
|
||||
|
||||
@@ -239,12 +239,12 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix) {
|
||||
public void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix, boolean addSuffix) {
|
||||
//nothing needs to be done here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal(List<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix) {
|
||||
public void reveal(List<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix, boolean addSuffix) {
|
||||
//nothing needs to be done here
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ public enum FSkinImage implements FImage {
|
||||
COUNTERS_MULTI (FSkinProp.IMG_COUNTERS_MULTI, SourceFile.ICONS),
|
||||
ENERGY (FSkinProp.IMG_ENERGY, SourceFile.ICONS),
|
||||
TICKET (FSkinProp.IMG_TICKET, SourceFile.ICONS),
|
||||
RAD (FSkinProp.IMG_RAD, SourceFile.ICONS),
|
||||
|
||||
//Dock Icons
|
||||
SHORTCUTS (FSkinProp.ICO_SHORTCUTS, SourceFile.ICONS),
|
||||
|
||||
@@ -472,6 +472,7 @@ public class VPlayerPanel extends FContainer {
|
||||
private int energyCounters = player.getCounters(CounterEnumType.ENERGY);
|
||||
private int experienceCounters = player.getCounters(CounterEnumType.EXPERIENCE);
|
||||
private int ticketCounters = player.getCounters(CounterEnumType.TICKET);
|
||||
private int radCounters = player.getCounters(CounterEnumType.RAD);
|
||||
private int manaShards = player.getNumManaShards();
|
||||
private String lifeStr = String.valueOf(life);
|
||||
|
||||
@@ -525,7 +526,7 @@ public class VPlayerPanel extends FContainer {
|
||||
adjustHeight = 1;
|
||||
float divider = Gdx.app.getGraphics().getHeight() > 900 ? 1.2f : 2f;
|
||||
if(Forge.altPlayerLayout && !Forge.altZoneTabs && Forge.isLandscapeMode()) {
|
||||
if (poisonCounters == 0 && energyCounters == 0 && experienceCounters == 0 && ticketCounters ==0 && manaShards == 0) {
|
||||
if (poisonCounters == 0 && energyCounters == 0 && experienceCounters == 0 && ticketCounters == 0 && radCounters == 0 && manaShards == 0) {
|
||||
g.fillRect(Color.DARK_GRAY, 0, 0, INFO2_FONT.getBounds(lifeStr).width+1, INFO2_FONT.getBounds(lifeStr).height+1);
|
||||
g.drawText(lifeStr, INFO2_FONT, getInfoForeColor().getColor(), 0, 0, getWidth(), getHeight(), false, Align.left, false);
|
||||
} else {
|
||||
@@ -554,6 +555,12 @@ public class VPlayerPanel extends FContainer {
|
||||
g.drawText(String.valueOf(experienceCounters), INFO_FONT, getInfoForeColor().getColor(), textStart, (halfHeight*mod)+2, textWidth, halfHeight, false, Align.left, false);
|
||||
mod+=1;
|
||||
}
|
||||
if (radCounters > 0) {
|
||||
g.fillRect(Color.DARK_GRAY, 0, (halfHeight*mod)+2, INFO_FONT.getBounds(String.valueOf(radCounters)).width+halfHeight+1, INFO_FONT.getBounds(String.valueOf(radCounters)).height+1);
|
||||
g.drawImage(FSkinImage.RAD, 0, (halfHeight*mod)+2, halfHeight, halfHeight);
|
||||
g.drawText(String.valueOf(radCounters), INFO_FONT, getInfoForeColor().getColor(), textStart, (halfHeight*mod)+2, textWidth, halfHeight, false, Align.left, false);
|
||||
mod+=1;
|
||||
}
|
||||
if (ticketCounters > 0) {
|
||||
g.fillRect(Color.DARK_GRAY, 0, (halfHeight*mod)+2, INFO_FONT.getBounds(String.valueOf(ticketCounters)).width+halfHeight+1, INFO_FONT.getBounds(String.valueOf(ticketCounters)).height+1);
|
||||
g.drawImage(FSkinImage.TICKET, 0, (halfHeight*mod)+2, halfHeight, halfHeight);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Death Ring
|
||||
Types:Artifact
|
||||
A:AB$ RepeatEach | Cost$ PayShards<2> | ActivationZone$ Command | SorcerySpeed$ True | IsPresent$ Creature.YouCtrl | RepeatPlayers$ Player | RepeatSubAbility$ DBChooseRandom | SubAbility$ DBPutCounter | SpellDescription$ For each player, put a -1/-1 counter on a random creature with the lowest toughness that player controls. Then if your creature has power less than one, sacrifice it.
|
||||
SVar:DBChooseRandom:DB$ ChooseCard | AtRandom$ True | Choices$ Creature.leastToughnessControlledByRememberedPlayer | RevealTitle$ OVERRIDE Randomly chosen creature: | Reveal$ True | RememberChosen$ True
|
||||
SVar:DBChooseRandom:DB$ ChooseCard | AtRandom$ True | Choices$ Creature.leastToughnessControlledByRememberedPlayer | RevealTitle$ Randomly chosen creature: | Reveal$ True | RememberChosen$ True
|
||||
SVar:DBPutCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ M1M1 | SubAbility$ ConditionalSac | StackDescription$ None | SpellDescription$ Activate only if you control a creature and only as a sorcery.
|
||||
SVar:ConditionalSac:DB$ SacrificeAll | Defined$ Remembered.powerLT1+YouCtrl | SubAbility$ DBCleanup | SpellDescription$ If your creature has power less than one, sacrifice it.
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name:Call to the Void
|
||||
ManaCost:4 B
|
||||
Types:Sorcery
|
||||
A:SP$ ChooseCard | Defined$ Player | Choices$ Creature | SecretlyChoose$ True | Amount$ 1 | ControlAndNot$ True | ChoiceTitle$ Secretly choose a creature | Reveal$ True | RevealTitle$ OVERRIDE Chosen creatures. They will be destroyed. | SubAbility$ DBDestroyChosen | Mandatory$ True
|
||||
A:SP$ ChooseCard | Defined$ Player | Choices$ Creature | SecretlyChoose$ True | Amount$ 1 | ControlAndNot$ True | ChoiceTitle$ Secretly choose a creature | Reveal$ True | RevealTitle$ Chosen creatures. They will be destroyed. | SubAbility$ DBDestroyChosen | Mandatory$ True
|
||||
SVar:DBDestroyChosen:DB$ DestroyAll | ValidCards$ Creature.ChosenCard
|
||||
Oracle:Each player secretly chooses a creature they control and a creature they don't control. Then those choices are revealed. Destroy each creature chosen this way.
|
||||
|
||||
@@ -5,7 +5,7 @@ PT:6/4
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ At the beginning of your upkeep, choose target nonland permanent you control and up to two target nonland permanents you don't control. Destroy one of them at random.
|
||||
SVar:TrigPump:DB$ Pump | ValidTgts$ Permanent.nonLand+YouCtrl | TgtPrompt$ Select target nonland permanent you control | Mandatory$ True | IsCurse$ True | SubAbility$ DBPump
|
||||
SVar:DBPump:DB$ Pump | ValidTgts$ Permanent.nonLand+YouDontCtrl | TgtPrompt$ Select up to two target nonland permanents you don't control | TargetMin$ 0 | TargetMax$ 2 | IsCurse$ True | SubAbility$ DBChooseRandom
|
||||
SVar:DBChooseRandom:DB$ ChooseCard | AtRandom$ True | Reveal$ True | RevealTitle$ OVERRIDE Randomly chosen permanent: | DefinedCards$ TargetedCard | SubAbility$ DBDestroy
|
||||
SVar:DBChooseRandom:DB$ ChooseCard | AtRandom$ True | Reveal$ True | RevealTitle$ Randomly chosen permanent: | DefinedCards$ TargetedCard | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ ChosenCard | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
||||
Oracle:At the beginning of your upkeep, choose target nonland permanent you control and up to two target nonland permanents you don't control. Destroy one of them at random.
|
||||
|
||||
@@ -14,7 +14,7 @@ SVar:TrigRepeatEach:DB$ RepeatEach | UseImprinted$ True | RepeatCards$ Card.Note
|
||||
SVar:DBCopyRandom:DB$ NameCard | Defined$ You | AtRandom$ True | ValidCards$ Card.ManaCost=Imprinted | SubAbility$ DBMake
|
||||
SVar:DBMake:DB$ MakeCard | Name$ ChosenName | Zone$ None | RememberMade$ True | SubAbility$ DBClearNamed
|
||||
SVar:DBClearNamed:DB$ Cleanup | ClearNamedCard$ True
|
||||
SVar:DBReveal:DB$ Reveal | Defined$ You | RevealDefined$ Remembered | RevealToAll$ True | RevealTitle$ OVERRIDE Cards created by Maelstrom Archangel Avatar | SubAbility$ DBChoose
|
||||
SVar:DBReveal:DB$ Reveal | Defined$ You | RevealDefined$ Remembered | RevealToAll$ True | RevealTitle$ Cards created by Maelstrom Archangel Avatar | SubAbility$ DBChoose
|
||||
SVar:DBChoose:DB$ ChooseCard | UnlessCost$ 3 | UnlessPayer$ You | UnlessSwitched$ True | Choices$ Card.IsRemembered | ChoiceZone$ None | SubAbility$ DBCopyPerm | SpellDescription$ You may pay {3}. If you do, choose one of those copies. If a copy of a permanent card is chosen, you may create a token that's a copy of that card. If a copy of an instant or sorcery card is chosen, you may cast the copy without paying its mana cost.
|
||||
SVar:DBCopyPerm:DB$ CopyPermanent | Optional$ True | Defined$ ChosenCard | ConditionDefined$ ChosenCard | ConditionPresent$ Permanent | ConditionCompare$ GE1 | SubAbility$ DBCastSp
|
||||
SVar:DBCastSp:DB$ Play | Defined$ ChosenCard | ZoneRegardless$ True | Optional$ True | WithoutManaCost$ True | ValidSA$ Spell | ConditionDefined$ ChosenCard | ConditionPresent$ Permanent | ConditionCompare$ EQ0 | SubAbility$ DBCleanup
|
||||
|
||||
12
forge-gui/res/cardsfolder/upcoming/feral_ghoul.txt
Normal file
12
forge-gui/res/cardsfolder/upcoming/feral_ghoul.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Feral Ghoul
|
||||
ManaCost:2 B
|
||||
Types:Creature Zombie Mutant
|
||||
PT:2/2
|
||||
K:Menace
|
||||
T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever another creature you control dies, put a +1/+1 counter on CARDNAME.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigRadiation | TriggerDescription$ When CARDNAME dies, each opponent gets a number of rad counters equal to its power.
|
||||
SVar:TrigRadiation:DB$ Radiation | Defined$ Opponent | Add$ TriggeredCard$CardPower
|
||||
DeckHas:Ability$Counters
|
||||
DeckHints:Ability$Sacrifice
|
||||
Oracle:Menace\nWhenever another creature you control dies, put a +1/+1 counter on Feral Ghoul.\nWhen Feral Ghoul dies, each opponent gets a number of rad counters equal to its power.
|
||||
13
forge-gui/res/cardsfolder/upcoming/the_wise_mothman.txt
Normal file
13
forge-gui/res/cardsfolder/upcoming/the_wise_mothman.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Name:The Wise Mothman
|
||||
ManaCost:1 B G U
|
||||
Types:Legendary Creature Insect Mutant
|
||||
PT:3/3
|
||||
K:Flying
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRadiation | TriggerDescription$ Whenever CARDNAME enters the battlefield or attacks, each player gets a rad counter.
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigRadiation | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ Whenever CARDNAME enters the battlefield attacks, each player gets a rad counter.
|
||||
SVar:TrigRadiation:DB$ Radiation | Defined$ Player | Add$ 1
|
||||
T:Mode$ MilledAll | ValidPlayer$ Player | ValidCard$ Card.nonLand | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever one or more nonland cards are milled, put a +1/+1 counter on each of up to X target creatures, where X is the number of nonland cards milled this way.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | CounterNum$ 1 | CounterType$ P1P1 | TargetMin$ 0 | TargetMax$ TriggerCount$Amount | ValidTgts$ Creature | TgtPrompt$ Select up to X target creatures
|
||||
DeckHints:Ability$Mill
|
||||
DeckHas:Ability$Counters|Mill
|
||||
Oracle:Flying\nWhenever The Wise Mothman enters the battlefield or attacks, each player gets a rad counter.\nWhenever one or more nonland cards are milled, put a +1/+1 counter on each of up to X target creatures, where X is the number of nonland cards milled this way.
|
||||
@@ -1507,6 +1507,8 @@ lblCombat=Kampf
|
||||
lblNonCombat=Nicht-Kampf
|
||||
lblLogSourceDealsNDamageOfTypeToDest={0} verursacht {1} {2} Schaden bei {3}{4}.
|
||||
lblLogPlayerReceivesNPosionCounterFrom={0} hat {1} Giftmarken von {2} erhalten.
|
||||
lblLogPlayerRadiation={0} erhält {1} von {2}.
|
||||
lblLogPlayerRadRemove={0} entfernt {1} aufgrund von {2}.
|
||||
lblLogPlayerAssignedAttackerToAttackTarget={0} läßt {1} {2} angreifen.
|
||||
lblLogPlayerDidntBlockAttacker={0} hat {1} nicht geblockt.
|
||||
lblLogPlayerAssignedBlockerToBlockAttacker={0} läßt {1} {2} blocken.
|
||||
@@ -2035,6 +2037,8 @@ lblChooseCardToManifest=Wähle zu manifestierende Karte
|
||||
lblChooseCardToMeld=Wähle Karte zum Verschmelzen mit
|
||||
#MillEffect.java
|
||||
lblDoYouWantToMill=Willst du {0} millen?{1}
|
||||
lblMilledToZone=(zu {0})
|
||||
lblMilledCards=Karten {0} gefräst
|
||||
#MultiplePilesEffect.java
|
||||
lblChooseCardsInTargetPile=Wähle Karten in Stapel {0}?
|
||||
#MutateEffect.java
|
||||
|
||||
@@ -1512,6 +1512,8 @@ lblCombat=combat
|
||||
lblNonCombat=non-combat
|
||||
lblLogSourceDealsNDamageOfTypeToDest={0} deals {1} {2} damage to {3}{4}.
|
||||
lblLogPlayerReceivesNPosionCounterFrom={0} receives {1} poison counter from {2}
|
||||
lblLogPlayerRadiation={0} gets {1} from {2}.
|
||||
lblLogPlayerRadRemove={0} removes {1} due to {2}.
|
||||
lblLogPlayerAssignedAttackerToAttackTarget={0} assigned {1} to attack {2}.
|
||||
lblLogPlayerDidntBlockAttacker={0} didn''t block {1}.
|
||||
lblLogPlayerAssignedBlockerToBlockAttacker={0} assigned {1} to block {2}.
|
||||
@@ -2040,6 +2042,8 @@ lblChooseCardToManifest=Choose cards to manifest
|
||||
lblChooseCardToMeld=Choose card to meld with
|
||||
#MillEffect.java
|
||||
lblDoYouWantToMill=Do you want to mill {0}?{1}
|
||||
lblMilledToZone=(to {0})
|
||||
lblMilledCards=Cards {0} milled
|
||||
#MultiplePilesEffect.java
|
||||
lblChooseCardsInTargetPile=Choose cards in Pile {0}?
|
||||
#MutateEffect.java
|
||||
|
||||
@@ -1508,6 +1508,8 @@ lblCombat=combate
|
||||
lblNonCombat=no combate
|
||||
lblLogSourceDealsNDamageOfTypeToDest={0} hace {1} punto(s) de daño de {2} a {3}{4}.
|
||||
lblLogPlayerReceivesNPosionCounterFrom={0} recibe {1} contador de veneno de {2}
|
||||
lblLogPlayerRadiation={0} obtiene {1} de {2}.
|
||||
lblLogPlayerRadRemove={0} elimina {1} debido a {2}.
|
||||
lblLogPlayerAssignedAttackerToAttackTarget={0} asigna a {1} para atacar a {2}.
|
||||
lblLogPlayerDidntBlockAttacker={0} no bloquea a {1}.
|
||||
lblLogPlayerAssignedBlockerToBlockAttacker={0} asigna {1} para bloquear a {2}.
|
||||
@@ -2036,6 +2038,8 @@ lblChooseCardToManifest=Elige las cartas para manifestar
|
||||
lblChooseCardToMeld=Elige una carta para fundirla con
|
||||
#MillEffect.java
|
||||
lblDoYouWantToMill=¿Quieres miller {0}?{1}
|
||||
lblMilledToZone=(a {0})
|
||||
lblMilledCards=Tarjetas {0} fresadas
|
||||
#MultiplePilesEffect.java
|
||||
lblChooseCardsInTargetPile=¿Elegir las cartas en la Pila {0}?
|
||||
#MutateEffect.java
|
||||
|
||||
@@ -1511,6 +1511,8 @@ lblCombat=combat
|
||||
lblNonCombat=sans combat
|
||||
lblLogSourceDealsNDamageOfTypeToDest={0} inflige {1} {2} dégâts à {3}{4}.
|
||||
lblLogPlayerReceivesNPosionCounterFrom={0} reçoit {1} marqueur de poison de {2}
|
||||
lblLogPlayerRadiation={0} obtient {1} de {2}.
|
||||
lblLogPlayerRadRemove={0} supprime {1} en raison de {2}.
|
||||
lblLogPlayerAssignedAttackerToAttackTarget={0} a assigné {1} pour attaquer {2}.
|
||||
lblLogPlayerDidntBlockAttacker={0} n''a pas bloqué {1}.
|
||||
lblLogPlayerAssignedBlockerToBlockAttacker={0} a assigné {1} au bloc {2}.
|
||||
@@ -2040,6 +2042,8 @@ lblChooseCardToManifest=Choisir les cartes à manifester
|
||||
lblChooseCardToMeld=Choisissez la carte avec laquelle fusionner
|
||||
#MillEffect.java
|
||||
lblDoYouWantToMill=Voulez-vous meuler {0}?{1}
|
||||
lblMilledToZone=(à {0})
|
||||
lblMilledCards=Cartes {0} fraisées
|
||||
#MultiplePilesEffect.java
|
||||
lblChooseCardsInTargetPile=Choisir les cartes dans la pile {0} ?
|
||||
#MutateEffect.java
|
||||
|
||||
@@ -1508,6 +1508,8 @@ lblCombat=da combattimento
|
||||
lblNonCombat=non da combattimento
|
||||
lblLogSourceDealsNDamageOfTypeToDest={0} infligge {1} {2} danno/i a {3}{4}.
|
||||
lblLogPlayerReceivesNPosionCounterFrom={0} riceve {1} segnalino/i veleno da {2}
|
||||
lblLogPlayerRadiation={0} ottiene {1} da {2}.
|
||||
lblLogPlayerRadRemove={0} rimuove {1} a causa di {2}.
|
||||
lblLogPlayerAssignedAttackerToAttackTarget={0} ha dichiarato di attaccare {2} con {1}.
|
||||
lblLogPlayerDidntBlockAttacker={0} non blocca {1}.
|
||||
lblLogPlayerAssignedBlockerToBlockAttacker={0} ha dichiarato di bloccare {2} con {1}.
|
||||
@@ -2036,6 +2038,8 @@ lblChooseCardToManifest=Scegli le carte da manifestare
|
||||
lblChooseCardToMeld=Scegli le carte da combinare
|
||||
#MillEffect.java
|
||||
lblDoYouWantToMill=Vuoi macinare {0}?{1}
|
||||
lblMilledToZone=(a {0})
|
||||
lblMilledCards=Carte {0} macinate
|
||||
#MultiplePilesEffect.java
|
||||
lblChooseCardsInTargetPile=Scegli le carte nella Pila {0}?
|
||||
#MutateEffect.java
|
||||
|
||||
@@ -1509,6 +1509,8 @@ lblCombat=戦闘
|
||||
lblNonCombat=非戦闘
|
||||
lblLogSourceDealsNDamageOfTypeToDest={0}は {3}{4}に対して{1} {2}点のダメージを与えました。
|
||||
lblLogPlayerReceivesNPosionCounterFrom={0}は {2}から {1}個毒カウンターを貰いました。
|
||||
lblLogPlayerRadiation={0}{2}から{1}を得ます。
|
||||
lblLogPlayerRadRemove={0}は、{2}による{1}を削除します。
|
||||
lblLogPlayerAssignedAttackerToAttackTarget={0}は {1}を {2}の攻撃に指定しました。
|
||||
lblLogPlayerDidntBlockAttacker={0}が {1}をブロックしていません。
|
||||
lblLogPlayerAssignedBlockerToBlockAttacker={0}は {1}を {2}のブロックに指定しました。
|
||||
@@ -2035,6 +2037,8 @@ lblChooseCardToManifest=予示するカードを選ぶ
|
||||
lblChooseCardToMeld=合体するカードを選ぶ
|
||||
#MillEffect.java
|
||||
lblDoYouWantToMill={0}を製粉したいですか?{1}
|
||||
lblMilledToZone=({0} まで)
|
||||
lblMilledCards=カード{0}ミリング
|
||||
#MultiplePilesEffect.java
|
||||
lblChooseCardsInTargetPile={0}番目の束からカードを選びますか?
|
||||
#MutateEffect.java
|
||||
|
||||
@@ -1544,6 +1544,8 @@ lblCombat=combate
|
||||
lblNonCombat=não-combate
|
||||
lblLogSourceDealsNDamageOfTypeToDest={0} causa {1} {2} de dano a {3}{4}.
|
||||
lblLogPlayerReceivesNPosionCounterFrom={0} recebe {1} contador de veneno de {2}
|
||||
lblLogPlayerRadiation={0} obtém {1} de {2}.
|
||||
lblLogPlayerRadRemove={0} remove {1} devido a {2}.
|
||||
lblLogPlayerAssignedAttackerToAttackTarget={0} atribuiu {1} para atacar {2}.
|
||||
lblLogPlayerDidntBlockAttacker={0} não bloqueou {1}.
|
||||
lblLogPlayerAssignedBlockerToBlockAttacker={0} atribuiu {1} para bloquear {2}.
|
||||
@@ -2097,6 +2099,8 @@ lblChooseCardToManifest=Escolha cartas para manifestar
|
||||
lblChooseCardToMeld=Escolha a carta para fundir com
|
||||
#MillEffect.java
|
||||
lblDoYouWantToMill=Você quer moer {0}?{1}
|
||||
lblMilledToZone=(para {0})
|
||||
lblMilledCards=Cartões {0} fresados
|
||||
#MultiplePilesEffect.java
|
||||
lblChooseCardsInTargetPile=Escolha cartas na pilha {0}?
|
||||
#MutateEffect.java
|
||||
|
||||
@@ -1512,6 +1512,8 @@ lblCombat=战斗
|
||||
lblNonCombat=非战斗
|
||||
lblLogSourceDealsNDamageOfTypeToDest={0}对{3}造成{1}点{2}伤害{4}。
|
||||
lblLogPlayerReceivesNPosionCounterFrom={0}获得来自{2}的{1}个中毒指示物
|
||||
lblLogPlayerRadiation={0}从{2}那里得到{1}。
|
||||
lblLogPlayerRadRemove={0}消除了由于{2}而产生的{1}。
|
||||
lblLogPlayerAssignedAttackerToAttackTarget={0}已分配{1}进攻{2}。
|
||||
lblLogPlayerDidntBlockAttacker={0}没有阻挡{1}.
|
||||
lblLogPlayerAssignedBlockerToBlockAttacker={0}已分配{1}对{2}进行阻挡。
|
||||
@@ -2040,6 +2042,8 @@ lblChooseCardToManifest=选择要显化的牌
|
||||
lblChooseCardToMeld=选择要融合的牌
|
||||
#MillEffect.java
|
||||
lblDoYouWantToMill=您想铣削{0}吗?{1}
|
||||
lblMilledToZone=(到{0})
|
||||
lblMilledCards=卡{0}铣削
|
||||
#MultiplePilesEffect.java
|
||||
lblChooseCardsInTargetPile=选择堆{0}中的牌?
|
||||
#MutateEffect.java
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
@@ -505,6 +505,11 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
return processPlayer(event.receiver, livesUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventPlayerRadiation event) {
|
||||
return processPlayer(event.receiver, livesUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventPlayerDamaged event) {
|
||||
return processEvent();
|
||||
|
||||
@@ -90,6 +90,7 @@ public enum FSkinProp {
|
||||
|
||||
IMG_ZONE_POISON (new int[] {320, 80, 40, 40}, PropType.IMAGE),
|
||||
IMG_TICKET (new int[] {360, 80, 40, 40}, PropType.IMAGE),
|
||||
IMG_RAD (new int[] {360, 120, 40, 40}, PropType.IMAGE),
|
||||
|
||||
//mana images
|
||||
IMG_MANA_B (new int[] {166, 2, 80, 80}, PropType.MANAICONS),
|
||||
|
||||
@@ -939,22 +939,20 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal(final CardCollectionView cards, final ZoneType zone, final Player owner, String message) {
|
||||
reveal(cards, zone, PlayerView.get(owner), message);
|
||||
public void reveal(final CardCollectionView cards, final ZoneType zone, final Player owner, String message, boolean addSuffix) {
|
||||
reveal(cards, zone, PlayerView.get(owner), message, addSuffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal(final List<CardView> cards, final ZoneType zone, final PlayerView owner, String message) {
|
||||
reveal(getCardList(cards), zone, owner, message);
|
||||
public void reveal(final List<CardView> cards, final ZoneType zone, final PlayerView owner, String message, boolean addSuffix) {
|
||||
reveal(getCardList(cards), zone, owner, message, addSuffix);
|
||||
}
|
||||
|
||||
protected void reveal(final CardCollectionView cards, final ZoneType zone, final PlayerView owner, String message) {
|
||||
protected void reveal(final CardCollectionView cards, final ZoneType zone, final PlayerView owner, String message, boolean addSuffix) {
|
||||
if (StringUtils.isBlank(message)) {
|
||||
message = localizer.getMessage("lblLookCardInPlayerZone", "{player's}", zone.getTranslatedName().toLowerCase());
|
||||
} else if (message.startsWith("OVERRIDE")) {
|
||||
message = message.substring(9);
|
||||
} else {
|
||||
message += " " + localizer.getMessage("lblPlayerZone", "{player's}", zone.getTranslatedName().toLowerCase());
|
||||
if (addSuffix) message += " " + localizer.getMessage("lblPlayerZone", "{player's}", zone.getTranslatedName().toLowerCase());
|
||||
}
|
||||
final String fm = MessageUtil.formatMessage(message, getLocalPlayerView(), owner);
|
||||
if (!cards.isEmpty()) {
|
||||
|
||||
Reference in New Issue
Block a user