mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Merge branch 'Card-Forge:master' into master
This commit is contained in:
@@ -64,7 +64,6 @@ import forge.util.Aggregates;
|
||||
import forge.util.ComparatorUtil;
|
||||
import forge.util.Expressions;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import io.sentry.Breadcrumb;
|
||||
import io.sentry.Sentry;
|
||||
|
||||
@@ -437,11 +436,11 @@ public class AiController {
|
||||
}
|
||||
|
||||
landList = CardLists.filter(landList, c -> {
|
||||
CardCollectionView battlefield = player.getCardsIn(ZoneType.Battlefield);
|
||||
if (canPlaySpellBasic(c, null) != AiPlayDecision.WillPlay) {
|
||||
return false;
|
||||
}
|
||||
String name = c.getName();
|
||||
CardCollectionView battlefield = player.getCardsIn(ZoneType.Battlefield);
|
||||
if (c.getType().isLegendary() && !name.equals("Flagstones of Trokair")) {
|
||||
if (Iterables.any(battlefield, CardPredicates.nameEquals(name))) {
|
||||
return false;
|
||||
@@ -461,15 +460,11 @@ public class AiController {
|
||||
}
|
||||
|
||||
// don't play the land if it has cycling and enough lands are available
|
||||
final FCollectionView<SpellAbility> spellAbilities = c.getSpellAbilities();
|
||||
for (final SpellAbility sa : spellAbilities) {
|
||||
if (sa.isCycling()) {
|
||||
return false;
|
||||
}
|
||||
if (c.hasKeyword(Keyword.CYCLING)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return player.canPlayLand(c);
|
||||
return Iterables.any(c.getAllPossibleAbilities(player, true), SpellAbility::isLandAbility);
|
||||
});
|
||||
return landList;
|
||||
}
|
||||
@@ -1376,30 +1371,12 @@ public class AiController {
|
||||
Card land = chooseBestLandToPlay(landsWannaPlay);
|
||||
if ((!player.canLoseLife() || player.cantLoseForZeroOrLessLife() || ComputerUtil.getDamageFromETB(player, land) < player.getLife())
|
||||
&& (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land))) {
|
||||
final List<SpellAbility> abilities = Lists.newArrayList();
|
||||
final List<SpellAbility> abilities = land.getAllPossibleAbilities(player, true);
|
||||
// skip non Land Abilities
|
||||
abilities.removeIf(sa -> !sa.isLandAbility());
|
||||
|
||||
// TODO extend this logic to evaluate MDFC with both sides land
|
||||
// this can only happen if its a MDFC land
|
||||
if (!land.isLand()) {
|
||||
land.setState(CardStateName.Modal, true);
|
||||
land.setBackSide(true);
|
||||
}
|
||||
|
||||
LandAbility la = new LandAbility(land, player, null);
|
||||
la.setCardState(land.getCurrentState());
|
||||
if (la.canPlay()) {
|
||||
abilities.add(la);
|
||||
}
|
||||
|
||||
// add mayPlay option
|
||||
for (CardPlayOption o : land.mayPlay(player)) {
|
||||
la = new LandAbility(land, player, o);
|
||||
la.setCardState(land.getCurrentState());
|
||||
if (la.canPlay()) {
|
||||
abilities.add(la);
|
||||
}
|
||||
}
|
||||
if (!abilities.isEmpty()) {
|
||||
// TODO extend this logic to evaluate MDFC with both sides land
|
||||
return abilities;
|
||||
}
|
||||
}
|
||||
@@ -1570,7 +1547,7 @@ public class AiController {
|
||||
|
||||
Iterables.removeIf(saList, spellAbility -> { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card
|
||||
// TODO allow when experimental profile?
|
||||
return spellAbility instanceof LandAbility || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard()));
|
||||
return spellAbility.isLandAbility() || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard()));
|
||||
});
|
||||
//update LivingEndPlayer
|
||||
useLivingEnd = Iterables.any(player.getZone(ZoneType.Library), CardPredicates.nameEquals("Living End"));
|
||||
|
||||
@@ -14,7 +14,6 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPayEnergy;
|
||||
import forge.game.cost.CostPutCounter;
|
||||
@@ -32,20 +31,14 @@ public class ComputerUtilAbility {
|
||||
if (!game.getStack().isEmpty() || !game.getPhaseHandler().getPhase().isMain()) {
|
||||
return null;
|
||||
}
|
||||
final CardCollection hand = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
||||
hand.addAll(player.getCardsIn(ZoneType.Exile));
|
||||
CardCollection landList = CardLists.filter(hand, Presets.LANDS);
|
||||
CardCollection landList = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
||||
|
||||
//filter out cards that can't be played
|
||||
landList = CardLists.filter(landList, c -> {
|
||||
if (!c.getSVar("NeedsToPlay").isEmpty()) {
|
||||
final String needsToPlay = c.getSVar("NeedsToPlay");
|
||||
CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), needsToPlay, c.getController(), c, null);
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (!c.hasPlayableLandFace()) {
|
||||
return false;
|
||||
}
|
||||
return player.canPlayLand(c);
|
||||
return player.canPlayLand(c, false, c.getFirstSpellAbility());
|
||||
});
|
||||
|
||||
final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
|
||||
@@ -54,7 +47,7 @@ public class ComputerUtilAbility {
|
||||
landsNotInHand.add(player.getCardsIn(ZoneType.Library).get(0));
|
||||
}
|
||||
for (final Card crd : landsNotInHand) {
|
||||
if (!(crd.isLand() || (crd.isFaceDown() && crd.getState(CardStateName.Original).getType().isLand()))) {
|
||||
if (!(crd.hasPlayableLandFace() || (crd.isFaceDown() && crd.getState(CardStateName.Original).getType().isLand()))) {
|
||||
continue;
|
||||
}
|
||||
if (!crd.mayPlay(player).isEmpty()) {
|
||||
|
||||
@@ -810,7 +810,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public boolean playChosenSpellAbility(SpellAbility sa) {
|
||||
if (sa instanceof LandAbility) {
|
||||
if (sa.isLandAbility()) {
|
||||
if (sa.canPlay()) {
|
||||
sa.resolve();
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ public class DamageDealAi extends DamageAiBase {
|
||||
final String damage = sa.getParam("NumDmg");
|
||||
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||
|
||||
if (damage.equals("X") || sourceName.equals("Crater's Claws")) {
|
||||
if (sa.getSVar(damage).equals("Count$xPaid") || sourceName.equals("Crater's Claws")) {
|
||||
if (damage.equals("X") || source.getSVar("X").equals("Count$xPaid") || sourceName.equals("Crater's Claws")) {
|
||||
if (sa.getSVar("X").equals("Count$xPaid") || sa.getSVar(damage).equals("Count$xPaid") || sourceName.equals("Crater's Claws")) {
|
||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger());
|
||||
|
||||
// Try not to waste spells like Blaze or Fireball on early targets, try to do more damage with them if possible
|
||||
@@ -723,6 +723,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
if (sa.canTarget(enemy) && sa.canAddMoreTarget()) {
|
||||
if ((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai))
|
||||
|| (isSorcerySpeed(sa, ai) && phase.is(PhaseType.MAIN2))
|
||||
|| ("BurnCreatures".equals(logic) && !enemy.getCreaturesInPlay().isEmpty())
|
||||
|| immediately) {
|
||||
boolean pingAfterAttack = "PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai);
|
||||
boolean isPWAbility = sa.isPwAbility() && sa.getPayCosts().hasSpecificCostType(CostPutCounter.class);
|
||||
|
||||
@@ -8,7 +8,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
import forge.game.spellability.Spell;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
@@ -45,7 +45,7 @@ public class DiscoverAi extends SpellAbilityAi {
|
||||
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||
Card c = (Card)params.get("Card");
|
||||
for (SpellAbility s : AbilityUtils.getBasicSpellsFromPlayEffect(c, ai)) {
|
||||
if (s instanceof LandAbility) {
|
||||
if (s.isLandAbility()) {
|
||||
// return false or we get a ClassCastException later if the AI encounters MDFC with land backside
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public class PlayAi extends SpellAbilityAi {
|
||||
if (!sa.matchesValidParam("ValidSA", s)) {
|
||||
continue;
|
||||
}
|
||||
if (s instanceof LandAbility) {
|
||||
if (s.isLandAbility()) {
|
||||
// might want to run some checks here but it's rare anyway
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,11 +26,29 @@ public class ScryAi extends SpellAbilityAi {
|
||||
*/
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
if (sa.usesTargeting()) { // It doesn't appear that Scry ever targets
|
||||
if (sa.usesTargeting()) {
|
||||
// ability is targeted
|
||||
sa.resetTargets();
|
||||
|
||||
sa.getTargets().add(ai);
|
||||
if (sa.canTarget(ai)) {
|
||||
sa.getTargets().add(ai);
|
||||
} else {
|
||||
for (Player p : ai.getAllies()) {
|
||||
if (sa.canTarget(p)) {
|
||||
sa.getTargets().add(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mandatory && !sa.isTargetNumberValid()) {
|
||||
for (Player p : ai.getOpponents()) {
|
||||
if (sa.canTarget(p)) {
|
||||
sa.getTargets().add(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mandatory || sa.isTargetNumberValid();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -132,6 +150,21 @@ public class ScryAi extends SpellAbilityAi {
|
||||
randomReturn = true;
|
||||
}
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
sa.resetTargets();
|
||||
if (sa.canTarget(ai)) {
|
||||
sa.getTargets().add(ai);
|
||||
} else {
|
||||
for (Player p : ai.getAllies()) {
|
||||
if (sa.canTarget(p)) {
|
||||
sa.getTargets().add(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
randomReturn = sa.isTargetNumberValid();
|
||||
}
|
||||
|
||||
return randomReturn;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package forge.ai.simulation;
|
||||
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -158,9 +158,9 @@ public class GameSimulator {
|
||||
}
|
||||
public Score simulateSpellAbility(SpellAbility origSa, GameStateEvaluator eval, boolean resolve) {
|
||||
SpellAbility sa;
|
||||
if (origSa instanceof LandAbility) {
|
||||
if (origSa.isLandAbility()) {
|
||||
Card hostCard = (Card) copier.find(origSa.getHostCard());
|
||||
if (!aiPlayer.playLand(hostCard, false)) {
|
||||
if (!aiPlayer.playLand(hostCard, false, origSa)) {
|
||||
System.err.println("Simulation: Couldn't play land! " + origSa);
|
||||
}
|
||||
sa = origSa;
|
||||
|
||||
@@ -260,7 +260,10 @@ public class GameStateEvaluator {
|
||||
// The value should be more than the value of having a card in hand, so if a land has an
|
||||
// activated ability but not a mana ability, it will still be played.
|
||||
for (SpellAbility m: c.getNonManaAbilities()) {
|
||||
if (!m.getPayCosts().hasTapCost()) {
|
||||
if (m.isLandAbility()) {
|
||||
// Land Ability has no extra Score
|
||||
continue;
|
||||
} if (!m.getPayCosts().hasTapCost()) {
|
||||
// probably a manland, rate it higher than a rainbow land
|
||||
value += 25;
|
||||
} else if (m.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import forge.game.card.CardPredicates;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityCondition;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -136,7 +136,7 @@ public class SpellAbilityPicker {
|
||||
|
||||
private static boolean isSorcerySpeed(SpellAbility sa, Player player) {
|
||||
// TODO: Can we use the actual rules engine for this instead of trying to do the logic ourselves?
|
||||
if (sa instanceof LandAbility) {
|
||||
if (sa.isLandAbility()) {
|
||||
return true;
|
||||
}
|
||||
if (sa.isSpell()) {
|
||||
@@ -327,16 +327,16 @@ public class SpellAbilityPicker {
|
||||
}
|
||||
|
||||
private AiPlayDecision canPlayAndPayForSim(final SpellAbility sa) {
|
||||
if (!sa.isLegalAfterStack()) {
|
||||
return AiPlayDecision.CantPlaySa;
|
||||
}
|
||||
if (!sa.checkRestrictions(sa.getHostCard(), player)) {
|
||||
return AiPlayDecision.CantPlaySa;
|
||||
}
|
||||
|
||||
if (sa instanceof LandAbility) {
|
||||
if (sa.isLandAbility()) {
|
||||
return AiPlayDecision.WillPlay;
|
||||
}
|
||||
if (!sa.isLegalAfterStack()) {
|
||||
return AiPlayDecision.CantPlaySa;
|
||||
}
|
||||
if (!sa.canPlay()) {
|
||||
return AiPlayDecision.CantPlaySa;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public enum CardRarity {
|
||||
Rare("R", "Rare"),
|
||||
MythicRare("M", "Mythic Rare"),
|
||||
Special("S", "Special"), // Timeshifted
|
||||
None("N", "None"), // Tokens
|
||||
Token("T", "Token"), // Tokens
|
||||
Unknown("?", "Unknown"); // In development
|
||||
|
||||
public static final CardRarity[] FILTER_OPTIONS = new CardRarity[] {
|
||||
|
||||
@@ -169,7 +169,7 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
||||
|
||||
@Override
|
||||
public CardRarity getRarity() {
|
||||
return CardRarity.None;
|
||||
return CardRarity.Token;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -64,7 +64,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
||||
* Keys that should not changed
|
||||
*/
|
||||
private static final ImmutableList<String> noChangeKeys = ImmutableList.<String>builder()
|
||||
.add("TokenScript", "TokenImage", "NewName", "ChooseFromList")
|
||||
.add("TokenScript", "TokenImage", "NewName" , "DefinedName", "ChooseFromList")
|
||||
.add("AddAbility").build();
|
||||
|
||||
/**
|
||||
@@ -170,7 +170,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isSecondary() {
|
||||
public boolean isSecondary() {
|
||||
return getParamOrDefault("Secondary", "False").equals("True");
|
||||
}
|
||||
|
||||
|
||||
@@ -91,10 +91,10 @@ public final class GameActionUtil {
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
if (sa.isSpell()) {
|
||||
if (sa.isSpell() || sa.isLandAbility()) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
Card newHost = ((Spell)sa).getAlternateHost(source);
|
||||
Card newHost = sa.getAlternateHost(source);
|
||||
if (newHost != null) {
|
||||
source = newHost;
|
||||
lkicheck = true;
|
||||
|
||||
@@ -2,7 +2,6 @@ package forge.game;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -93,11 +92,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
if (event.sa.getTargetRestrictions() != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
List<TargetChoices> targets = event.sa.getAllTargetChoices();
|
||||
// Include the TargetChoices from the stack instance, since the real target choices
|
||||
// are on that object at this point (see SpellAbilityStackInstance constructor).
|
||||
targets.add(event.si.getTargetChoices());
|
||||
for (TargetChoices ch : targets) {
|
||||
for (TargetChoices ch : event.sa.getAllTargetChoices()) {
|
||||
if (null != ch) {
|
||||
sb.append(ch);
|
||||
}
|
||||
|
||||
@@ -2953,7 +2953,8 @@ public class AbilityUtils {
|
||||
}
|
||||
|
||||
for (SpellAbility s : list) {
|
||||
if (s instanceof LandAbility) {
|
||||
if (s.isLandAbility()) {
|
||||
s.setActivatingPlayer(controller);
|
||||
// CR 305.3
|
||||
if (controller.getGame().getPhaseHandler().isPlayerTurn(controller) && controller.canPlayLand(tgtCard, true, s)) {
|
||||
sas.add(s);
|
||||
@@ -2981,9 +2982,7 @@ public class AbilityUtils {
|
||||
|
||||
private static void collectSpellsForPlayEffect(final List<SpellAbility> result, final CardState state, final Player controller, final boolean withAltCost) {
|
||||
if (state.getType().isLand()) {
|
||||
LandAbility la = new LandAbility(state.getCard(), controller, null);
|
||||
la.setCardState(state);
|
||||
result.add(la);
|
||||
result.add(state.getFirstSpellAbility());
|
||||
}
|
||||
final Iterable<SpellAbility> spells = state.getSpellAbilities();
|
||||
for (SpellAbility sa : spells) {
|
||||
|
||||
@@ -13,7 +13,9 @@ import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.Game;
|
||||
@@ -46,53 +48,59 @@ public class CopyPermanentEffect extends TokenEffectBase {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
boolean justOne = tgtCards.size() == 1;
|
||||
boolean addKWs = sa.hasParam("AddKeywords");
|
||||
final int numCopies = sa.hasParam("NumCopies") ?
|
||||
AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCopies"), sa) : 1;
|
||||
|
||||
sb.append(activator).append(" creates ").append(Lang.nounWithNumeralExceptOne(numCopies, "token"));
|
||||
sb.append(numCopies == 1 ? " that's a copy" : " that are copies").append(" of ");
|
||||
sb.append(Lang.joinHomogenous(tgtCards));
|
||||
|
||||
if (addKWs) {
|
||||
final List<String> keywords = Lists.newArrayList();
|
||||
keywords.addAll(Arrays.asList(sa.getParam("AddKeywords").split(" & ")));
|
||||
if (sa.getDescription().contains("except")) {
|
||||
sb.append(", except ").append(justOne ? "it has " : "they have ");
|
||||
} else {
|
||||
sb.append(". ").append(justOne ? "It gains " : "They gain ");
|
||||
}
|
||||
sb.append(Lang.joinHomogenous(keywords).toLowerCase());
|
||||
}
|
||||
|
||||
if (sa.hasParam("AddTriggers")) {
|
||||
final String oDesc = sa.getDescription();
|
||||
final String trigStg = oDesc.contains("\"") ?
|
||||
oDesc.substring(oDesc.indexOf("\""),oDesc.lastIndexOf("\"") + 1) :
|
||||
"[trigger text parsing error]";
|
||||
if (addKWs) {
|
||||
sb.append(" and ").append(trigStg);
|
||||
} else {
|
||||
sb.append(". ").append(justOne ? "It gains " : "They gain ").append(trigStg);
|
||||
}
|
||||
sb.append(activator).append(" creates ");
|
||||
if (sa.hasParam("DefinedName")) {
|
||||
sb.append(Lang.nounWithNumeralExceptOne(numCopies, sa.getParam("DefinedName") + " token"));
|
||||
} else {
|
||||
sb.append(".");
|
||||
}
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
boolean justOne = tgtCards.size() == 1;
|
||||
boolean addKWs = sa.hasParam("AddKeywords");
|
||||
|
||||
if (sa.hasParam("AtEOT")) {
|
||||
String atEOT = sa.getParam("AtEOT");
|
||||
String verb = "Sacrifice ";
|
||||
if (atEOT.startsWith("Exile")) {
|
||||
verb = "Exile ";
|
||||
sb.append(Lang.nounWithNumeralExceptOne(numCopies, "token"));
|
||||
sb.append(numCopies == 1 ? " that's a copy" : " that are copies").append(" of ");
|
||||
sb.append(Lang.joinHomogenous(tgtCards));
|
||||
|
||||
if (addKWs) {
|
||||
final List<String> keywords = Lists.newArrayList();
|
||||
keywords.addAll(Arrays.asList(sa.getParam("AddKeywords").split(" & ")));
|
||||
if (sa.getDescription().contains("except")) {
|
||||
sb.append(", except ").append(justOne ? "it has " : "they have ");
|
||||
} else {
|
||||
sb.append(". ").append(justOne ? "It gains " : "They gain ");
|
||||
}
|
||||
sb.append(Lang.joinHomogenous(keywords).toLowerCase());
|
||||
}
|
||||
sb.append(" ").append(verb).append(justOne ? "it " : "them ").append("at ");
|
||||
String when = "the beginning of the next end step.";
|
||||
if (atEOT.endsWith("Combat")) {
|
||||
when = "end of combat.";
|
||||
|
||||
if (sa.hasParam("AddTriggers")) {
|
||||
final String oDesc = sa.getDescription();
|
||||
final String trigStg = oDesc.contains("\"") ?
|
||||
oDesc.substring(oDesc.indexOf("\""),oDesc.lastIndexOf("\"") + 1) :
|
||||
"[trigger text parsing error]";
|
||||
if (addKWs) {
|
||||
sb.append(" and ").append(trigStg);
|
||||
} else {
|
||||
sb.append(". ").append(justOne ? "It gains " : "They gain ").append(trigStg);
|
||||
}
|
||||
} else {
|
||||
sb.append(".");
|
||||
}
|
||||
|
||||
if (sa.hasParam("AtEOT")) {
|
||||
String atEOT = sa.getParam("AtEOT");
|
||||
String verb = "Sacrifice ";
|
||||
if (atEOT.startsWith("Exile")) {
|
||||
verb = "Exile ";
|
||||
}
|
||||
sb.append(" ").append(verb).append(justOne ? "it " : "them ").append("at ");
|
||||
String when = "the beginning of the next end step.";
|
||||
if (atEOT.endsWith("Combat")) {
|
||||
when = "end of combat.";
|
||||
}
|
||||
sb.append(when);
|
||||
}
|
||||
sb.append(when);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
@@ -180,20 +188,21 @@ public class CopyPermanentEffect extends TokenEffectBase {
|
||||
tgtCards = choice;
|
||||
|
||||
System.err.println("Copying random permanent(s): " + tgtCards.toString());
|
||||
} else if (sa.hasParam("DefinedName")) {
|
||||
String name = sa.getParam("DefinedName");
|
||||
if (name.equals("NamedCard")) {
|
||||
if (!host.getNamedCard().isEmpty()) {
|
||||
name = host.getNamedCard();
|
||||
}
|
||||
}
|
||||
} else if (sa.hasParam("DefinedName")) {
|
||||
List<PaperCard> cards = Lists.newArrayList(StaticData.instance().getCommonCards().getUniqueCards());
|
||||
String name = sa.getParam("DefinedName");
|
||||
if (name.equals("NamedCard")) {
|
||||
if (!host.getNamedCard().isEmpty()) {
|
||||
name = host.getNamedCard();
|
||||
}
|
||||
}
|
||||
|
||||
Predicate<PaperCard> cpp = Predicates.compose(CardRulesPredicates.name(StringOp.EQUALS, name), PaperCard::getRules);
|
||||
cards = Lists.newArrayList(Iterables.filter(cards, cpp));
|
||||
Predicate<PaperCard> cpp = Predicates.compose(CardRulesPredicates.name(StringOp.EQUALS, name), PaperCard::getRules);
|
||||
cards = Lists.newArrayList(Iterables.filter(cards, cpp));
|
||||
|
||||
if (!cards.isEmpty()) {
|
||||
tgtCards.add(Card.fromPaperCard(cards.get(0), controller));
|
||||
}
|
||||
if (!cards.isEmpty()) {
|
||||
tgtCards.add(Card.fromPaperCard(cards.get(0), controller));
|
||||
}
|
||||
} else if (sa.hasParam("Choices")) {
|
||||
Player chooser = activator;
|
||||
@@ -272,31 +281,42 @@ public class CopyPermanentEffect extends TokenEffectBase {
|
||||
}
|
||||
|
||||
public static Card getProtoType(final SpellAbility sa, final Card original, final Player newOwner) {
|
||||
final Card host = sa.getHostCard();
|
||||
int id = newOwner == null ? 0 : newOwner.getGame().nextCardId();
|
||||
// need to create a physical card first, i need the original card faces
|
||||
final Card copy = CardFactory.getCard(original.getPaperCard(), newOwner, id, host.getGame());
|
||||
final Card copy;
|
||||
if (sa.hasParam("DefinedName")) {
|
||||
copy = original;
|
||||
String name = TextUtil.fastReplace(TextUtil.fastReplace(original.getName(), ",", ""), " ", "_").toLowerCase();
|
||||
String set = sa.getOriginalHost().getSetCode();
|
||||
copy.getCurrentState().setRarity(CardRarity.Token);
|
||||
copy.getCurrentState().setSetCode(set);
|
||||
copy.getCurrentState().setImageKey(ImageKeys.getTokenKey(name + "_" + set.toLowerCase()));
|
||||
} else {
|
||||
final Card host = sa.getHostCard();
|
||||
|
||||
copy.setTokenSpawningAbility(sa);
|
||||
if (original.isTransformable()) {
|
||||
// 707.8a If an effect creates a token that is a copy of a transforming permanent or a transforming double-faced card not on the battlefield,
|
||||
// the resulting token is a transforming token that has both a front face and a back face.
|
||||
// The characteristics of each face are determined by the copiable values of the same face of the permanent it is a copy of, as modified by any other copy effects that apply to that permanent.
|
||||
// If the token is a copy of a transforming permanent with its back face up, the token enters the battlefield with its back face up.
|
||||
// This rule does not apply to tokens that are created with their own set of characteristics and enter the battlefield as a copy of a transforming permanent due to a replacement effect.
|
||||
copy.setBackSide(original.isBackSide());
|
||||
if (original.isTransformed()) {
|
||||
copy.incrementTransformedTimestamp();
|
||||
int id = newOwner == null ? 0 : newOwner.getGame().nextCardId();
|
||||
// need to create a physical card first, i need the original card faces
|
||||
copy = CardFactory.getCard(original.getPaperCard(), newOwner, id, host.getGame());
|
||||
if (original.isTransformable()) {
|
||||
// 707.8a If an effect creates a token that is a copy of a transforming permanent or a transforming double-faced card not on the battlefield,
|
||||
// the resulting token is a transforming token that has both a front face and a back face.
|
||||
// The characteristics of each face are determined by the copiable values of the same face of the permanent it is a copy of, as modified by any other copy effects that apply to that permanent.
|
||||
// If the token is a copy of a transforming permanent with its back face up, the token enters the battlefield with its back face up.
|
||||
// This rule does not apply to tokens that are created with their own set of characteristics and enter the battlefield as a copy of a transforming permanent due to a replacement effect.
|
||||
copy.setBackSide(original.isBackSide());
|
||||
if (original.isTransformed()) {
|
||||
copy.incrementTransformedTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
copy.setStates(CardFactory.getCloneStates(original, copy, sa));
|
||||
// force update the now set State
|
||||
if (original.isTransformable()) {
|
||||
copy.setState(original.isTransformed() ? CardStateName.Transformed : CardStateName.Original, true, true);
|
||||
} else {
|
||||
copy.setState(copy.getCurrentStateName(), true, true);
|
||||
}
|
||||
}
|
||||
|
||||
copy.setStates(CardFactory.getCloneStates(original, copy, sa));
|
||||
// force update the now set State
|
||||
if (original.isTransformable()) {
|
||||
copy.setState(original.isTransformed() ? CardStateName.Transformed : CardStateName.Original, true, true);
|
||||
} else {
|
||||
copy.setState(copy.getCurrentStateName(), true, true);
|
||||
}
|
||||
copy.setTokenSpawningAbility(sa);
|
||||
copy.setGamePieceType(GamePieceType.TOKEN);
|
||||
|
||||
return copy;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -15,7 +13,7 @@ import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostReveal;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.PlayerZone;
|
||||
@@ -94,7 +92,7 @@ public class DiscoverEffect extends SpellAbilityEffect {
|
||||
List<SpellAbility> sas = AbilityUtils.getBasicSpellsFromPlayEffect(found, p);
|
||||
|
||||
// filter out land abilities due to MDFC or similar
|
||||
Iterables.removeIf(sas, Predicates.instanceOf(LandAbility.class));
|
||||
sas.removeIf(sp -> sp.isLandAbility());
|
||||
// the spell must also have a mana value equal to or less than the discover number
|
||||
sas.removeIf(sp -> sp.getPayCosts().getTotalMana().getCMC() > num);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.AlternativeCost;
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityPredicates;
|
||||
import forge.game.zone.Zone;
|
||||
@@ -343,7 +343,7 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
final Zone originZone = tgtCard.getZone();
|
||||
|
||||
// lands will be played
|
||||
if (tgtSA instanceof LandAbility) {
|
||||
if (tgtSA.isLandAbility()) {
|
||||
tgtSA.resolve();
|
||||
amount--;
|
||||
if (remember) {
|
||||
|
||||
@@ -57,7 +57,7 @@ public class PlayLandVariantEffect extends SpellAbilityEffect {
|
||||
PaperCard ran = Aggregates.random(cards);
|
||||
random = CardFactory.getCard(ran, activator, game);
|
||||
cards.remove(ran);
|
||||
} while (!activator.canPlayLand(random, false));
|
||||
} while (!activator.canPlayLand(random, false, random.getFirstSpellAbility()));
|
||||
|
||||
source.addCloneState(CardFactory.getCloneStates(random, source, sa), game.getNextTimestamp());
|
||||
source.updateStateForView();
|
||||
|
||||
@@ -5524,6 +5524,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return isInstant() || isSorcery() || (isAura() && !isInZone(ZoneType.Battlefield));
|
||||
}
|
||||
|
||||
public final boolean hasPlayableLandFace() { return isLand() || (isModal() && getState(CardStateName.Modal).getType().isLand()); }
|
||||
|
||||
public final boolean isLand() { return getType().isLand(); }
|
||||
public final boolean isBasicLand() { return getType().isBasicLand(); }
|
||||
public final boolean isSnow() { return getType().isSnow(); }
|
||||
@@ -7430,7 +7432,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
for (SpellAbility sa : getState(CardStateName.Modal).getSpellAbilities()) {
|
||||
//add alternative costs as additional spell abilities
|
||||
// only add Spells there
|
||||
if (sa.isSpell()) {
|
||||
if (sa.isSpell() || sa.isLandAbility()) {
|
||||
abilities.add(sa);
|
||||
abilities.addAll(GameActionUtil.getAlternativeCosts(sa, player, false));
|
||||
}
|
||||
@@ -7466,106 +7468,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
abilities.removeAll(toRemove);
|
||||
|
||||
// Land Abilities below, move them to CardFactory after MayPlayRefactor
|
||||
if (getLastKnownZone().is(ZoneType.Battlefield)) {
|
||||
return abilities;
|
||||
}
|
||||
if (getState(CardStateName.Original).getType().isLand()) {
|
||||
LandAbility la = new LandAbility(this, player, null);
|
||||
la.setCardState(oState);
|
||||
if (la.canPlay()) {
|
||||
abilities.add(la);
|
||||
}
|
||||
|
||||
Card source = this;
|
||||
boolean lkicheck = false;
|
||||
|
||||
// if Card is Facedown, need to check if MayPlay still applies
|
||||
if (isFaceDown()) {
|
||||
lkicheck = true;
|
||||
source = CardCopyService.getLKICopy(source);
|
||||
source.forceTurnFaceUp();
|
||||
}
|
||||
|
||||
if (lkicheck) {
|
||||
// double freeze tracker, so it doesn't update view
|
||||
game.getTracker().freeze();
|
||||
CardCollection preList = new CardCollection(source);
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
|
||||
}
|
||||
|
||||
// extra for MayPlay
|
||||
for (CardPlayOption o : source.mayPlay(player)) {
|
||||
la = new LandAbility(this, player, o);
|
||||
la.setCardState(oState);
|
||||
if (la.canPlay()) {
|
||||
abilities.add(la);
|
||||
}
|
||||
}
|
||||
|
||||
// reset static abilities
|
||||
if (lkicheck) {
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
// clear delayed changes, this check should not have updated the view
|
||||
game.getTracker().clearDelayed();
|
||||
// need to unfreeze tracker
|
||||
game.getTracker().unfreeze();
|
||||
}
|
||||
}
|
||||
|
||||
if (isModal() && hasState(CardStateName.Modal)) {
|
||||
CardState modal = getState(CardStateName.Modal);
|
||||
if (modal.getType().isLand()) {
|
||||
LandAbility la = new LandAbility(this, player, null);
|
||||
la.setCardState(modal);
|
||||
|
||||
Card source = CardCopyService.getLKICopy(this);
|
||||
boolean lkicheck = true;
|
||||
|
||||
// if Card is Facedown, need to check if MayPlay still applies
|
||||
if (isFaceDown()) {
|
||||
source.forceTurnFaceUp();
|
||||
}
|
||||
|
||||
// the modal state is not copied with lki, need to copy it extra
|
||||
if (!source.hasState(CardStateName.Modal)) {
|
||||
source.addAlternateState(CardStateName.Modal, false);
|
||||
source.getState(CardStateName.Modal).copyFrom(this.getState(CardStateName.Modal), true);
|
||||
}
|
||||
|
||||
source.setSplitStateToPlayAbility(la);
|
||||
|
||||
if (la.canPlay(source)) {
|
||||
abilities.add(la);
|
||||
}
|
||||
|
||||
if (lkicheck) {
|
||||
// double freeze tracker, so it doesn't update view
|
||||
game.getTracker().freeze();
|
||||
CardCollection preList = new CardCollection(source);
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
|
||||
}
|
||||
|
||||
// extra for MayPlay
|
||||
for (CardPlayOption o : source.mayPlay(player)) {
|
||||
la = new LandAbility(this, player, o);
|
||||
la.setCardState(modal);
|
||||
if (la.canPlay(source)) {
|
||||
abilities.add(la);
|
||||
}
|
||||
}
|
||||
|
||||
// reset static abilities
|
||||
if (lkicheck) {
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
// clear delayed changes, this check should not have updated the view
|
||||
game.getTracker().clearDelayed();
|
||||
// need to unfreeze tracker
|
||||
game.getTracker().unfreeze();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return abilities;
|
||||
}
|
||||
|
||||
|
||||
@@ -413,17 +413,16 @@ public class CardFactory {
|
||||
|
||||
// SpellPermanent only for Original State
|
||||
if (c.getCurrentStateName() == CardStateName.Original || c.getCurrentStateName() == CardStateName.Modal || c.getCurrentStateName().toString().startsWith("Specialize")) {
|
||||
// this is the "default" spell for permanents like creatures and artifacts
|
||||
if (c.isPermanent() && !c.isAura() && !c.isLand()) {
|
||||
if (c.isLand()) {
|
||||
SpellAbility sa = new LandAbility(c);
|
||||
sa.setCardState(c.getCurrentState());
|
||||
c.addSpellAbility(sa);
|
||||
} else if (c.isPermanent() && !c.isAura()) {
|
||||
// this is the "default" spell for permanents like creatures and artifacts
|
||||
SpellAbility sa = new SpellPermanent(c);
|
||||
|
||||
// Currently only for Modal, might react different when state is always set
|
||||
//if (c.getCurrentStateName() == CardStateName.Modal) {
|
||||
sa.setCardState(c.getCurrentState());
|
||||
//}
|
||||
sa.setCardState(c.getCurrentState());
|
||||
c.addSpellAbility(sa);
|
||||
}
|
||||
// TODO add LandAbility there when refactor MayPlay
|
||||
}
|
||||
|
||||
CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities());
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.Comparator;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.combat.CombatUtil;
|
||||
@@ -379,7 +378,7 @@ public final class CardPredicates {
|
||||
/**
|
||||
* a Predicate<Card> to get all lands.
|
||||
*/
|
||||
public static final Predicate<Card> LANDS = c -> c.isLand() || (!c.isInZone(ZoneType.Battlefield) && c.isModal() && c.getState(CardStateName.Modal).getType().isLand());
|
||||
public static final Predicate<Card> LANDS = c -> c.isLand();
|
||||
/**
|
||||
* a Predicate<Card> to get all mana-producing lands.
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,7 @@ import forge.game.event.*;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementResult;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -1064,7 +1064,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
final Zone currentZone = saHost.getZone();
|
||||
|
||||
// Need to check if Zone did change
|
||||
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa instanceof LandAbility)) {
|
||||
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa.isLandAbility())) {
|
||||
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
|
||||
final CardZoneTable triggerList = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
|
||||
|
||||
@@ -43,7 +43,7 @@ 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.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
@@ -1691,9 +1691,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
game.fireEvent(new GameEventShuffle(this));
|
||||
}
|
||||
|
||||
public final boolean playLand(final Card land, final boolean ignoreZoneAndTiming) {
|
||||
public final boolean playLand(final Card land, final boolean ignoreZoneAndTiming, SpellAbility cause) {
|
||||
// Dakkon Blackblade Avatar will use a similar effect
|
||||
if (canPlayLand(land, ignoreZoneAndTiming)) {
|
||||
if (canPlayLand(land, ignoreZoneAndTiming, cause)) {
|
||||
playLandNoCheck(land, null);
|
||||
return true;
|
||||
}
|
||||
@@ -1706,7 +1706,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
land.setController(this, 0);
|
||||
if (land.isFaceDown()) {
|
||||
land.turnFaceUp(null);
|
||||
if (cause instanceof LandAbility) {
|
||||
if (cause.isLandAbility()) {
|
||||
land.changeToState(cause.getCardStateName());
|
||||
}
|
||||
}
|
||||
@@ -1730,12 +1730,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
return c;
|
||||
}
|
||||
|
||||
public final boolean canPlayLand(final Card land) {
|
||||
return canPlayLand(land, false);
|
||||
}
|
||||
public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTiming) {
|
||||
return canPlayLand(land, ignoreZoneAndTiming, null);
|
||||
}
|
||||
public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTiming, SpellAbility landSa) {
|
||||
if (!ignoreZoneAndTiming) {
|
||||
// CR 305.3
|
||||
|
||||
@@ -21,24 +21,20 @@ import forge.card.CardStateName;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCopyService;
|
||||
import forge.game.card.CardPlayOption;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.Localizer;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class LandAbility extends Ability {
|
||||
public class LandAbility extends AbilityStatic {
|
||||
|
||||
public LandAbility(Card sourceCard, Player p, CardPlayOption mayPlay) {
|
||||
super(sourceCard, new Cost(ManaCost.NO_COST, false));
|
||||
setActivatingPlayer(p);
|
||||
setMayPlay(mayPlay);
|
||||
}
|
||||
public LandAbility(Card sourceCard) {
|
||||
this(sourceCard, sourceCard.getController(), null);
|
||||
super(sourceCard, ManaCost.NO_COST);
|
||||
|
||||
getRestrictions().setZone(ZoneType.Hand);
|
||||
}
|
||||
|
||||
public boolean canPlay(Card newHost) {
|
||||
@@ -46,11 +42,21 @@ public class LandAbility extends Ability {
|
||||
return p.canPlayLand(newHost, false, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLandAbility() { return true; }
|
||||
|
||||
@Override
|
||||
public boolean isSecondary() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlay() {
|
||||
Card land = this.getHostCard();
|
||||
final Player p = this.getActivatingPlayer();
|
||||
|
||||
if (p == null || land.isInZone(ZoneType.Battlefield)) {
|
||||
return false;
|
||||
}
|
||||
if (this.getCardState() != null && land.getCurrentStateName() != this.getCardStateName()) {
|
||||
if (!land.isLKI()) {
|
||||
land = CardCopyService.getLKICopy(land);
|
||||
@@ -113,4 +119,41 @@ public class LandAbility extends Ability {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card getAlternateHost(Card source) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
// need to be done before so it works with Vivien and Zoetic Cavern
|
||||
if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardCopyService.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.forceTurnFaceUp();
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (getCardState() != null && source.getCurrentStateName() != getCardStateName()) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardCopyService.getLKICopy(source);
|
||||
}
|
||||
CardStateName stateName = getCardState().getStateName();
|
||||
if (!source.hasState(stateName)) {
|
||||
source.addAlternateState(stateName, false);
|
||||
source.getState(stateName).copyFrom(getHostCard().getState(stateName), true);
|
||||
}
|
||||
|
||||
source.setState(stateName, false);
|
||||
if (getHostCard().isDoubleFaced()) {
|
||||
source.setBackSide(getHostCard().getRules().getSplitType().getChangedStateName().equals(stateName));
|
||||
}
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
return lkicheck ? source : null;
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
this.castFaceDown = faceDown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card getAlternateHost(Card source) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
|
||||
@@ -527,6 +527,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
public boolean isSpell() { return false; }
|
||||
public boolean isAbility() { return true; }
|
||||
public boolean isActivatedAbility() { return false; }
|
||||
public boolean isLandAbility() { return false; }
|
||||
|
||||
public boolean isTurnFaceUp() {
|
||||
return isMorphUp() || isDisguiseUp() || isManifestUp() || isCloakUp();
|
||||
@@ -2185,7 +2186,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
}
|
||||
else if (incR[0].contains("LandAbility")) {
|
||||
if (!(root instanceof LandAbility)) {
|
||||
if (!(root.isLandAbility())) {
|
||||
return testFailed;
|
||||
}
|
||||
}
|
||||
@@ -2544,7 +2545,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
if (getRestrictions().isInstantSpeed()) {
|
||||
return true;
|
||||
}
|
||||
if ((isSpell() || this instanceof LandAbility) && (isCastFromPlayEffect() || host.isInstant() || host.hasKeyword(Keyword.FLASH))) {
|
||||
if ((isSpell() || this.isLandAbility()) && (isCastFromPlayEffect() || host.isInstant() || host.hasKeyword(Keyword.FLASH))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2589,6 +2590,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return true;
|
||||
}
|
||||
|
||||
public Card getAlternateHost(Card source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasOptionalKeywordAmount(KeywordInterface kw) {
|
||||
return this.optionalKeywordAmount.contains(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId()));
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.badlogic.gdx.Version;
|
||||
import com.badlogic.gdx.backends.android.AndroidApplication;
|
||||
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
|
||||
import com.badlogic.gdx.backends.android.AndroidAudio;
|
||||
import com.badlogic.gdx.backends.android.AsynchronousAndroidAudio;
|
||||
import com.getkeepsafe.relinker.ReLinker;
|
||||
import de.cketti.fileprovider.PublicFileProvider;
|
||||
import forge.Forge;
|
||||
@@ -122,7 +123,8 @@ public class Main extends AndroidApplication {
|
||||
|
||||
@Override
|
||||
public AndroidAudio createAudio(Context context, AndroidApplicationConfiguration config) {
|
||||
return super.createAudio(context, config);
|
||||
return new AsynchronousAndroidAudio(context, config);
|
||||
//return super.createAudio(context, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package forge.ai.simulation;
|
||||
|
||||
import forge.game.spellability.LandAbility;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -84,7 +84,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertTrue(sa instanceof LandAbility);
|
||||
AssertJUnit.assertTrue(sa.isLandAbility());
|
||||
AssertJUnit.assertEquals(mountain, sa.getHostCard());
|
||||
|
||||
Plan plan = picker.getPlan();
|
||||
|
||||
@@ -324,7 +324,8 @@ public class EnemySprite extends CharacterSprite implements Steerable<Vector2> {
|
||||
if (_freeze){
|
||||
//Mob has defeated player in battle, hold still until player has a chance to move away.
|
||||
//Without this moving enemies can immediately restart battle.
|
||||
if (spriteToPlayer.len() < unfreezeRange) {
|
||||
float distance = spriteToPlayer.len();
|
||||
if (distance < unfreezeRange) {
|
||||
timer += delta;
|
||||
return Vector2.Zero;
|
||||
}
|
||||
@@ -635,7 +636,8 @@ public class EnemySprite extends CharacterSprite implements Steerable<Vector2> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean isFrozen() {
|
||||
return _freeze;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,19 @@ package forge.adventure.player;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
import com.github.tommyettinger.textra.TextraLabel;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.Forge;
|
||||
import forge.adventure.data.*;
|
||||
import forge.adventure.pointofintrest.PointOfInterestChanges;
|
||||
import forge.adventure.scene.AdventureDeckEditor;
|
||||
import forge.adventure.scene.DeckEditScene;
|
||||
import forge.adventure.stage.GameStage;
|
||||
import forge.adventure.stage.MapStage;
|
||||
import forge.adventure.stage.WorldStage;
|
||||
import forge.adventure.util.*;
|
||||
import forge.adventure.world.WorldSave;
|
||||
import forge.card.ColorSet;
|
||||
@@ -223,6 +227,10 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Boolean isFemale() {
|
||||
return isFemale;
|
||||
}
|
||||
|
||||
public float getWorldPosX() {
|
||||
return worldPosX;
|
||||
}
|
||||
@@ -594,6 +602,24 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
return HeroListData.getRaces().get(Current.player().heroRace);
|
||||
}
|
||||
|
||||
public GameStage getCurrentGameStage() {
|
||||
if (MapStage.getInstance().isInMap())
|
||||
return MapStage.getInstance();
|
||||
return WorldStage.getInstance();
|
||||
}
|
||||
public void addStatusMessage(String iconName, String message, Integer itemCount, float x, float y) {
|
||||
String symbol = itemCount == null || itemCount < 0 ? "" : " +";
|
||||
String icon = iconName == null ? "" : "[+" + iconName + "]";
|
||||
String count = itemCount == null ? "" : String.valueOf(itemCount);
|
||||
TextraLabel actor = Controls.newTextraLabel("[%95]" + icon + "[WHITE]" + symbol + count + " " + message);
|
||||
actor.setPosition(x, y);
|
||||
actor.addAction(Actions.sequence(
|
||||
Actions.parallel(Actions.moveBy(0f, 5f, 3f), Actions.fadeIn(2f)),
|
||||
Actions.hide(),
|
||||
Actions.removeActor())
|
||||
);
|
||||
getCurrentGameStage().addActor(actor);
|
||||
}
|
||||
public void addCard(PaperCard card) {
|
||||
cards.add(card);
|
||||
newCards.add(card);
|
||||
@@ -752,8 +778,11 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
}
|
||||
|
||||
public void setShards(int number) {
|
||||
shards = number;
|
||||
onShardsChangeList.emit();
|
||||
boolean changed = shards != number;
|
||||
if (changed) {
|
||||
shards = number;
|
||||
onShardsChangeList.emit();
|
||||
}
|
||||
}
|
||||
|
||||
public void addBlessing(EffectData bless) {
|
||||
|
||||
@@ -161,7 +161,7 @@ public class InventoryScene extends UIScene {
|
||||
ConsoleCommandInterpreter.getInstance().command(data.commandOnUse);
|
||||
if (data.dialogOnUse != null && data.dialogOnUse.text != null && !data.dialogOnUse.text.isEmpty()) {
|
||||
MapDialog dialog = new MapDialog(data.dialogOnUse, MapStage.getInstance(),0,null);
|
||||
MapStage.instance.showDialog();
|
||||
MapStage.getInstance().showDialog();
|
||||
dialog.activate();
|
||||
ChangeListener listen = new ChangeListener() {
|
||||
@Override
|
||||
|
||||
@@ -3,12 +3,12 @@ package forge.adventure.scene;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.github.tommyettinger.textra.TextraButton;
|
||||
import com.github.tommyettinger.textra.TextraLabel;
|
||||
import forge.Forge;
|
||||
import forge.adventure.data.DialogData;
|
||||
@@ -48,20 +48,20 @@ public class NewGameScene extends MenuScene {
|
||||
private final TextraLabel starterEditionLabel;
|
||||
private final Array<String> custom;
|
||||
private final TextraLabel colorLabel;
|
||||
private final TextraButton difficultyHelp;
|
||||
private final ImageButton difficultyHelp;
|
||||
private DialogData difficultySummary;
|
||||
private final TextraButton modeHelp;
|
||||
private final ImageButton modeHelp;
|
||||
private DialogData modeSummary;
|
||||
private final Random rand = new Random();
|
||||
|
||||
private final Array<AdventureModes> modes = new Array<>();
|
||||
|
||||
private NewGameScene() {
|
||||
|
||||
super(Forge.isLandscapeMode() ? "ui/new_game.json" : "ui/new_game_portrait.json");
|
||||
|
||||
gender = ui.findActor("gender");
|
||||
selectedName = ui.findActor("nameField");
|
||||
selectedName.setText(NameGenerator.getRandomName(gender.getCurrentIndex() > 0 ? "Female" : "Male", "Any", ""));
|
||||
generateName();
|
||||
avatarImage = ui.findActor("avatarPreview");
|
||||
mode = ui.findActor("mode");
|
||||
modeHelp = ui.findActor("modeHelp");
|
||||
@@ -127,13 +127,12 @@ public class NewGameScene extends MenuScene {
|
||||
modeNames[i] = modes.get(i).getName();
|
||||
mode.setTextList(modeNames);
|
||||
|
||||
gender.setTextList(new String[]{Forge.getLocalizer().getMessage("lblMale"), Forge.getLocalizer().getMessage("lblFemale")});
|
||||
gender.setTextList(new String[]{Forge.getLocalizer().getMessage("lblMale") + "[%120][CYAN] \u2642",
|
||||
Forge.getLocalizer().getMessage("lblFemale") + "[%120][MAGENTA] \u2640"});
|
||||
gender.addListener(new ClickListener() {
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
//gender should be either Male or Female
|
||||
String val = gender.getCurrentIndex() > 0 ? "Female" : "Male";
|
||||
selectedName.setText(NameGenerator.getRandomName(val, "Any", ""));
|
||||
nameTT = 0.8f;
|
||||
super.clicked(event, x, y);
|
||||
}
|
||||
});
|
||||
@@ -150,6 +149,13 @@ public class NewGameScene extends MenuScene {
|
||||
}
|
||||
});
|
||||
race = ui.findActor("race");
|
||||
race.addListener(new ClickListener() {
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
avatarTT = 0.7f;
|
||||
super.clicked(event, x, y);
|
||||
}
|
||||
});
|
||||
race.addListener(event -> NewGameScene.this.updateAvatar());
|
||||
race.setTextList(HeroListData.getRaces());
|
||||
difficulty = ui.findActor("difficulty");
|
||||
@@ -161,15 +167,13 @@ public class NewGameScene extends MenuScene {
|
||||
for (DifficultyData diff : Config.instance().getConfigData().difficulties) {
|
||||
if (diff.startingDifficulty)
|
||||
startingDifficulty = i;
|
||||
diffList.add(Forge.getLocalizer().getInstance().getMessageorUseDefault("lbl" + diff.name, diff.name));
|
||||
diffList.add(Forge.getLocalizer().getMessageorUseDefault("lbl" + diff.name, diff.name));
|
||||
i++;
|
||||
}
|
||||
difficulty.setTextList(diffList);
|
||||
difficulty.setCurrentIndex(startingDifficulty);
|
||||
|
||||
Random rand = new Random();
|
||||
avatarIndex = rand.nextInt();
|
||||
updateAvatar();
|
||||
generateAvatar();
|
||||
gender.setCurrentIndex(rand.nextInt());
|
||||
colorId.setCurrentIndex(rand.nextInt());
|
||||
race.setCurrentIndex(rand.nextInt());
|
||||
@@ -177,8 +181,16 @@ public class NewGameScene extends MenuScene {
|
||||
ui.onButtonPress("start", NewGameScene.this::start);
|
||||
ui.onButtonPress("leftAvatar", NewGameScene.this::leftAvatar);
|
||||
ui.onButtonPress("rightAvatar", NewGameScene.this::rightAvatar);
|
||||
difficultyHelp.addListener(new ClickListener(){ public void clicked(InputEvent e, float x, float y){ showDifficultyHelp(); }});
|
||||
modeHelp.addListener(new ClickListener(){ public void clicked(InputEvent e, float x, float y){ showModeHelp(); }});
|
||||
difficultyHelp.addListener(new ClickListener() {
|
||||
public void clicked(InputEvent e, float x, float y) {
|
||||
showDifficultyHelp();
|
||||
}
|
||||
});
|
||||
modeHelp.addListener(new ClickListener() {
|
||||
public void clicked(InputEvent e, float x, float y) {
|
||||
showModeHelp();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static NewGameScene object;
|
||||
@@ -189,6 +201,37 @@ public class NewGameScene extends MenuScene {
|
||||
return object;
|
||||
}
|
||||
|
||||
float avatarT = 1f, avatarTT = 1f;
|
||||
float nameT = 1f, nameTT = 1f;
|
||||
|
||||
@Override
|
||||
public void act(float delta) {
|
||||
super.act(delta);
|
||||
if (avatarT > avatarTT) {
|
||||
avatarTT += (delta / 0.5f);
|
||||
generateAvatar();
|
||||
} else {
|
||||
avatarTT = avatarT;
|
||||
}
|
||||
if (nameT > nameTT) {
|
||||
nameTT += (delta / 0.5f);
|
||||
generateName();
|
||||
} else {
|
||||
nameTT = nameT;
|
||||
}
|
||||
}
|
||||
|
||||
private void generateAvatar() {
|
||||
avatarIndex = rand.nextInt();
|
||||
updateAvatar();
|
||||
}
|
||||
|
||||
private void generateName() {
|
||||
//gender should be either Male or Female
|
||||
String val = gender.getCurrentIndex() > 0 ? "Female" : "Male";
|
||||
selectedName.setText(NameGenerator.getRandomName(val, "Any", ""));
|
||||
}
|
||||
|
||||
boolean started = false;
|
||||
|
||||
public boolean start() {
|
||||
@@ -196,7 +239,7 @@ public class NewGameScene extends MenuScene {
|
||||
return true;
|
||||
started = true;
|
||||
if (selectedName.getText().isEmpty()) {
|
||||
selectedName.setText(NameGenerator.getRandomName("Any", "Any", ""));
|
||||
generateName();
|
||||
}
|
||||
Runnable runnable = () -> {
|
||||
started = false;
|
||||
@@ -295,11 +338,11 @@ public class NewGameScene extends MenuScene {
|
||||
dismiss.name = "OK";
|
||||
|
||||
DialogData matchImpacts = new DialogData();
|
||||
matchImpacts.text = String.format("Difficulty: %s\nStarting Life: %d\nEnemy Health: %d%%\nGold loss on defeat: %d%%\nLife loss on defeat: %d%%", selectedDifficulty.name, selectedDifficulty.startingLife, (int)(selectedDifficulty.enemyLifeFactor * 100) , (int)(selectedDifficulty.goldLoss*100), (int)(selectedDifficulty.lifeLoss*100));
|
||||
matchImpacts.text = String.format("Difficulty: %s\nStarting Life: %d\nEnemy Health: %d%%\nGold loss on defeat: %d%%\nLife loss on defeat: %d%%", selectedDifficulty.name, selectedDifficulty.startingLife, (int) (selectedDifficulty.enemyLifeFactor * 100), (int) (selectedDifficulty.goldLoss * 100), (int) (selectedDifficulty.lifeLoss * 100));
|
||||
matchImpacts.name = "Duels";
|
||||
|
||||
DialogData economyImpacts = new DialogData();
|
||||
economyImpacts.text = String.format("Difficulty: %s\nStarting Gold: %d\nStarting Mana Shards: %d\nCard Sale Price: %d%%\nMana Shard Sale Price: %d%%\nRandom loot rate: %d%%", selectedDifficulty.name, selectedDifficulty.staringMoney, selectedDifficulty.startingShards, (int)(selectedDifficulty.sellFactor*100), (int)(selectedDifficulty.shardSellRatio*100), (int)(selectedDifficulty.rewardMaxFactor*100));
|
||||
economyImpacts.text = String.format("Difficulty: %s\nStarting Gold: %d\nStarting Mana Shards: %d\nCard Sale Price: %d%%\nMana Shard Sale Price: %d%%\nRandom loot rate: %d%%", selectedDifficulty.name, selectedDifficulty.staringMoney, selectedDifficulty.startingShards, (int) (selectedDifficulty.sellFactor * 100), (int) (selectedDifficulty.shardSellRatio * 100), (int) (selectedDifficulty.rewardMaxFactor * 100));
|
||||
economyImpacts.name = "Economy";
|
||||
|
||||
difficultySummary.options = new DialogData[3];
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.badlogic.gdx.utils.Align;
|
||||
import com.badlogic.gdx.utils.Scaling;
|
||||
import com.github.tommyettinger.textra.TextraButton;
|
||||
import com.github.tommyettinger.textra.TextraLabel;
|
||||
import com.github.tommyettinger.textra.TypingLabel;
|
||||
import forge.Forge;
|
||||
import forge.adventure.character.EnemySprite;
|
||||
import forge.adventure.data.EnemyData;
|
||||
@@ -44,7 +45,8 @@ public class PlayerStatisticScene extends UIScene {
|
||||
TextraLabel wins, totalWins, eventWins, eventMatchWins;
|
||||
TextraLabel loss, totalLoss, eventLosses, eventMatchLosses;
|
||||
TextraLabel winloss, lossWinRatio, eventLossWinRatio, eventMatchLossWinRatio;
|
||||
TextraLabel playerName, headerAchievements, headerAvatar, headerName, headerWinLoss;
|
||||
TextraLabel headerAchievements, headerAvatar, headerName, headerWinLoss;
|
||||
TypingLabel playerName;
|
||||
TextraButton back, toggleAward;
|
||||
private final Table scrollContainer, achievementContainer;
|
||||
TextraLabel blessingScroll;
|
||||
@@ -196,7 +198,9 @@ public class PlayerStatisticScene extends UIScene {
|
||||
scrollContainer.clear();
|
||||
|
||||
if (playerName != null) {
|
||||
playerName.setText(GamePlayerUtil.getGuiPlayer().getName());
|
||||
String gender = Current.player().isFemale() ? "{GRADIENT=MAGENTA;MAUVE;1;1}\u2640{ENDGRADIENT}[BLACK] " : "{GRADIENT=CYAN;BLUE;1;1}\u2642{ENDGRADIENT}[BLACK] ";
|
||||
playerName.setText(gender + GamePlayerUtil.getGuiPlayer().getName());
|
||||
playerName.skipToTheEnd();
|
||||
}
|
||||
if (avatar != null) {
|
||||
avatar.setDrawable(new TextureRegionDrawable(Current.player().avatar()));
|
||||
|
||||
@@ -8,10 +8,20 @@ import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.scenes.scene2d.*;
|
||||
import com.badlogic.gdx.scenes.scene2d.Action;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.Stage;
|
||||
import com.badlogic.gdx.scenes.scene2d.Touchable;
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.SequenceAction;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.*;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Button;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Touchpad;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
@@ -28,8 +38,19 @@ import forge.adventure.character.CharacterSprite;
|
||||
import forge.adventure.data.AdventureQuestData;
|
||||
import forge.adventure.data.ItemData;
|
||||
import forge.adventure.player.AdventurePlayer;
|
||||
import forge.adventure.scene.*;
|
||||
import forge.adventure.util.*;
|
||||
import forge.adventure.scene.DeckSelectScene;
|
||||
import forge.adventure.scene.GameScene;
|
||||
import forge.adventure.scene.InventoryScene;
|
||||
import forge.adventure.scene.MapViewScene;
|
||||
import forge.adventure.scene.QuestLogScene;
|
||||
import forge.adventure.scene.Scene;
|
||||
import forge.adventure.scene.TileMapScene;
|
||||
import forge.adventure.util.AdventureQuestController;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.adventure.util.Controls;
|
||||
import forge.adventure.util.Current;
|
||||
import forge.adventure.util.KeyBinding;
|
||||
import forge.adventure.util.UIActor;
|
||||
import forge.adventure.world.WorldSave;
|
||||
import forge.deck.Deck;
|
||||
import forge.gui.GuiBase;
|
||||
@@ -47,11 +68,11 @@ public class GameHUD extends Stage {
|
||||
static public GameHUD instance;
|
||||
private final GameStage gameStage;
|
||||
private final Image avatar, miniMapPlayer;
|
||||
private final TextraLabel lifePoints;
|
||||
private final TextraLabel money;
|
||||
private final TextraLabel shards;
|
||||
private final TypingLabel lifePoints;
|
||||
private final TypingLabel money;
|
||||
private final TypingLabel shards;
|
||||
private final TextraLabel keys;
|
||||
private TextraLabel notificationText = Controls.newTextraLabel("");
|
||||
private final TextraLabel notificationText = Controls.newTextraLabel("");
|
||||
private final Image miniMap, gamehud, mapborder, avatarborder, blank;
|
||||
private final InputEvent eventTouchDown, eventTouchUp;
|
||||
private final TextraButton deckActor, openMapActor, menuActor, logbookActor, inventoryActor, exitToWorldMapActor, bookmarkActor;
|
||||
@@ -60,7 +81,7 @@ public class GameHUD extends Stage {
|
||||
private final Console console;
|
||||
float TOUCHPAD_SCALE = 70f, referenceX;
|
||||
float opacity = 1f;
|
||||
private boolean debugMap, updatelife;
|
||||
private boolean debugMap, transluscent, hidden;
|
||||
|
||||
private final Dialog dialog;
|
||||
private boolean dialogOnlyInput;
|
||||
@@ -70,6 +91,10 @@ public class GameHUD extends Stage {
|
||||
private String lifepointsTextColor = "";
|
||||
private final ScrollPane scrollPane;
|
||||
private final ScrollPane notificationPane;
|
||||
private final Group mapGroup = new Group();
|
||||
private final Group hudGroup = new Group();
|
||||
private final Group menuGroup = new Group();
|
||||
private final Group avatarGroup = new Group();
|
||||
|
||||
private GameHUD(GameStage gameStage) {
|
||||
super(new ScalingViewport(Scaling.stretch, Scene.getIntendedWidth(), Scene.getIntendedHeight()), gameStage.getBatch());
|
||||
@@ -129,32 +154,50 @@ public class GameHUD extends Stage {
|
||||
ui.onButtonPress("exittoworldmap", this::exitToWorldMap);
|
||||
ui.onButtonPress("bookmark", this::bookmark);
|
||||
lifePoints = ui.findActor("lifePoints");
|
||||
lifePoints.skipToTheEnd();
|
||||
shards = ui.findActor("shards");
|
||||
shards.skipToTheEnd();
|
||||
money = ui.findActor("money");
|
||||
shards.setText("[%95][+Shards] 0");
|
||||
money.setText("[%95][+Gold] ");
|
||||
lifePoints.setText("[%95][+Life] 20/20");
|
||||
money.skipToTheEnd();
|
||||
shards.setText("[%95][+Shards]");
|
||||
money.setText("[%95][+Gold]");
|
||||
lifePoints.setText("[%95][+Life]");
|
||||
keys = Controls.newTextraLabel("");
|
||||
scrollPane = new ScrollPane(keys);
|
||||
scrollPane.setPosition(2, 2);
|
||||
scrollPane.setStyle(Controls.getSkin().get("translucent", ScrollPane.ScrollPaneStyle.class));
|
||||
addActor(scrollPane);
|
||||
AdventurePlayer.current().onLifeChange(() -> lifePoints.setText("[%95][+Life]" + lifepointsTextColor + " " + AdventurePlayer.current().getLife() + "/" + AdventurePlayer.current().getMaxLife()));
|
||||
AdventurePlayer.current().onShardsChange(() -> shards.setText("[%95][+Shards] " + AdventurePlayer.current().getShards()));
|
||||
AdventurePlayer.current().onLifeChange(() -> {
|
||||
String effect = "{EMERGE}";
|
||||
String effectEnd = "{ENDEMERGE}";
|
||||
String heartbeat = "";
|
||||
//colored lifepoints
|
||||
if (Current.player().getLife() >= Current.player().getMaxLife()) {
|
||||
//color green if max life
|
||||
lifepointsTextColor = "[GREEN]";
|
||||
} else if (Current.player().getLife() <= 5) {
|
||||
//color red if critical
|
||||
effect = "";
|
||||
effectEnd = "";
|
||||
heartbeat = "{HEARTBEAT=0.5;0.5}";
|
||||
lifepointsTextColor = "{ENDHEARTBEAT}[RED]";
|
||||
} else {
|
||||
lifepointsTextColor = "[WHITE]";
|
||||
}
|
||||
lifePoints.restart("[%95]" + heartbeat + "[+Life]" + lifepointsTextColor + effect + " " + AdventurePlayer.current().getLife() + effectEnd + "/" + AdventurePlayer.current().getMaxLife());
|
||||
});
|
||||
AdventurePlayer.current().onShardsChange(() -> shards.restart("[%95][+Shards]{EMERGE} " + AdventurePlayer.current().getShards() + "{ENDEMERGE}"));
|
||||
AdventurePlayer.current().onGoldChange(() -> money.restart("[%95][+Gold]{EMERGE} " + AdventurePlayer.current().getGold() + "{ENDEMERGE}"));
|
||||
AdventurePlayer.current().onEquipmentChanged(this::updateAbility);
|
||||
|
||||
WorldSave.getCurrentSave().getPlayer().onGoldChange(() -> money.setText("[%95][+Gold] " + AdventurePlayer.current().getGold()));
|
||||
addActor(ui);
|
||||
addActor(miniMapPlayer);
|
||||
console = new Console();
|
||||
console.setBounds(0, GuiBase.isAndroid() ? getHeight() : 0, getWidth(), getHeight() / 2);
|
||||
console.setVisible(false);
|
||||
ui.addActor(console);
|
||||
if (GuiBase.isAndroid()) {
|
||||
avatar.addListener(new ConsoleToggleListener());
|
||||
avatarborder.addListener(new ConsoleToggleListener());
|
||||
gamehud.addListener(new ConsoleToggleListener());
|
||||
}
|
||||
avatar.addListener(new ConsoleToggleListener());
|
||||
avatarborder.addListener(new ConsoleToggleListener());
|
||||
gamehud.addListener(new ConsoleToggleListener());
|
||||
WorldSave.getCurrentSave().onLoad(this::enter);
|
||||
|
||||
eventTouchDown = new InputEvent();
|
||||
@@ -166,11 +209,36 @@ public class GameHUD extends Stage {
|
||||
|
||||
notificationPane = new ScrollPane(notificationText);
|
||||
notificationPane.setTouchable(Touchable.childrenOnly);
|
||||
notificationPane.setBounds(5, GuiBase.isAndroid() ? getHeight() : -notificationText.getPrefHeight(), getWidth()*0.4f, 25);
|
||||
notificationPane.setBounds(5, GuiBase.isAndroid() ? getHeight() : -notificationText.getPrefHeight(), getWidth() * 0.4f, 25);
|
||||
notificationPane.setStyle(Controls.getSkin().get("paper", ScrollPane.ScrollPaneStyle.class));
|
||||
notificationPane.getColor().a = 0f;
|
||||
|
||||
ui.addActor(notificationPane);
|
||||
//MAP
|
||||
mapGroup.addActor(miniMap);
|
||||
mapGroup.addActor(mapborder);
|
||||
mapGroup.addActor(openMapActor);
|
||||
mapGroup.addActor(miniMapPlayer);
|
||||
ui.addActor(mapGroup);
|
||||
//HUD
|
||||
hudGroup.addActor(gamehud);
|
||||
hudGroup.addActor(lifePoints);
|
||||
hudGroup.addActor(shards);
|
||||
hudGroup.addActor(money);
|
||||
hudGroup.addActor(blank);
|
||||
ui.addActor(hudGroup);
|
||||
//MENU
|
||||
menuGroup.addActor(deckActor);
|
||||
menuGroup.addActor(menuActor);
|
||||
menuGroup.addActor(logbookActor);
|
||||
menuGroup.addActor(inventoryActor);
|
||||
menuGroup.addActor(exitToWorldMapActor);
|
||||
menuGroup.addActor(bookmarkActor);
|
||||
ui.addActor(menuGroup);
|
||||
//AVATAR
|
||||
avatarGroup.addActor(avatar);
|
||||
avatarGroup.addActor(avatarborder);
|
||||
ui.addActor(avatarGroup);
|
||||
}
|
||||
|
||||
private void openMap() {
|
||||
@@ -268,7 +336,6 @@ public class GameHUD extends Stage {
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
updatelife = false;
|
||||
int yPos = (int) gameStage.player.getY();
|
||||
int xPos = (int) gameStage.player.getX();
|
||||
act(Gdx.graphics.getDeltaTime()); //act the Hud
|
||||
@@ -278,32 +345,10 @@ public class GameHUD extends Stage {
|
||||
miniMapPlayer.setPosition(miniMap.getX() + xPosMini - miniMapPlayer.getWidth() / 2, miniMap.getY() + yPosMini - miniMapPlayer.getHeight() / 2);
|
||||
|
||||
miniMapPlayer.setVisible(miniMap.isVisible() &&
|
||||
!Controls.actorContainsVector(notificationPane, new Vector2(miniMapPlayer.getX(),miniMapPlayer.getY()))
|
||||
&& (!Controls.actorContainsVector(console, new Vector2(miniMapPlayer.getX(),miniMapPlayer.getY()))
|
||||
!Controls.actorContainsVector(notificationPane, new Vector2(miniMapPlayer.getX(), miniMapPlayer.getY()))
|
||||
&& (!Controls.actorContainsVector(console, new Vector2(miniMapPlayer.getX(), miniMapPlayer.getY()))
|
||||
|| !console.isVisible())); // prevent drawing on top of console or notifications
|
||||
//colored lifepoints
|
||||
if (Current.player().getLife() >= Current.player().getMaxLife()) {
|
||||
//color green if max life
|
||||
if (!lifepointsTextColor.equals("[GREEN]")) {
|
||||
lifepointsTextColor = "[GREEN]";
|
||||
updatelife = true;
|
||||
}
|
||||
} else if (Current.player().getLife() <= 5) {
|
||||
//color red if critical
|
||||
if (!lifepointsTextColor.equals("[RED]")) {
|
||||
lifepointsTextColor = "[RED]";
|
||||
updatelife = true;
|
||||
}
|
||||
} else {
|
||||
if (!lifepointsTextColor.isEmpty()) {
|
||||
lifepointsTextColor = "";
|
||||
updatelife = true;
|
||||
}
|
||||
}
|
||||
if (updatelife) {
|
||||
updatelife = false;
|
||||
lifePoints.setText("[%95][+Life]" + lifepointsTextColor + " " + AdventurePlayer.current().getLife() + "/" + AdventurePlayer.current().getMaxLife());
|
||||
}
|
||||
|
||||
if (!MapStage.getInstance().isInMap())
|
||||
updateMusic();
|
||||
else
|
||||
@@ -356,7 +401,7 @@ public class GameHUD extends Stage {
|
||||
switch (GameScene.instance().getAdventurePlayerLocation(false, false)) {
|
||||
case "capital":
|
||||
case "town":
|
||||
if(MapStage.getInstance().isInMap()) {
|
||||
if (MapStage.getInstance().isInMap()) {
|
||||
int rep = TileMapScene.instance().getPointOfInterestChanges().getMapReputation();
|
||||
String reputationText = TileMapScene.instance().rootPoint.getDisplayName() + "\nReputation: " + (rep > 0 ? "[GREEN]" : rep < 0 ? "[RED]" : "[WHITE]") + rep + "[/]";
|
||||
if (fromWorldMap) {
|
||||
@@ -405,6 +450,7 @@ public class GameHUD extends Stage {
|
||||
}
|
||||
if (MapStage.getInstance().isInMap())
|
||||
updateBookmarkActor(MapStage.getInstance().getChanges().isBookmarked());
|
||||
avatarGroup.setZIndex(ui.getChildren().size);
|
||||
}
|
||||
|
||||
void clearAbility() {
|
||||
@@ -663,11 +709,6 @@ public class GameHUD extends Stage {
|
||||
gameStage.openMenu();
|
||||
}
|
||||
|
||||
private void setVisibility(Actor actor, boolean visible) {
|
||||
if (actor != null)
|
||||
actor.setVisible(visible);
|
||||
}
|
||||
|
||||
private void setDisabled(Actor actor, boolean value, String enabled, String disabled) {
|
||||
if (actor instanceof TextraButton) {
|
||||
((TextraButton) actor).setDisabled(value);
|
||||
@@ -678,38 +719,70 @@ public class GameHUD extends Stage {
|
||||
private void setAlpha(Actor actor, boolean visible) {
|
||||
if (actor != null) {
|
||||
if (visible)
|
||||
actor.getColor().a = 1f;
|
||||
actor.addAction(Actions.alpha(1f, 0.5f));
|
||||
else
|
||||
actor.getColor().a = 0.4f;
|
||||
actor.addAction(Actions.alpha(actor == mapGroup ? 0f : 0.4f, 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
public void showHideMap(boolean visible) {
|
||||
setVisibility(miniMap, visible);
|
||||
setVisibility(mapborder, visible);
|
||||
setVisibility(openMapActor, visible);
|
||||
setVisibility(miniMapPlayer, visible);
|
||||
setVisibility(gamehud, visible);
|
||||
setVisibility(lifePoints, visible);
|
||||
setVisibility(shards, visible);
|
||||
setVisibility(money, visible);
|
||||
setVisibility(blank, visible);
|
||||
setDisabled(exitToWorldMapActor, !MapStage.getInstance().isInMap(), "[%120][+ExitToWorldMap]", "---");
|
||||
setDisabled(bookmarkActor, !MapStage.getInstance().isInMap(), "[%120][+Bookmark]", "---");
|
||||
setAlpha(avatarborder, visible);
|
||||
setAlpha(avatar, visible);
|
||||
setAlpha(deckActor, visible);
|
||||
setAlpha(menuActor, visible);
|
||||
setAlpha(logbookActor, visible);
|
||||
setAlpha(inventoryActor, visible);
|
||||
setAlpha(exitToWorldMapActor, visible);
|
||||
setAlpha(bookmarkActor, visible);
|
||||
transluscent = !visible;
|
||||
setAlpha(mapGroup, visible);
|
||||
setAlpha(hudGroup, visible);
|
||||
setAlpha(menuGroup, visible);
|
||||
setAlpha(avatarGroup, visible);
|
||||
|
||||
setDisabled(exitToWorldMapActor, !MapStage.getInstance().isInMap(), "[%120][+ExitToWorldMap]", "\uFF0F");
|
||||
setDisabled(bookmarkActor, !MapStage.getInstance().isInMap(), "[%120][+Bookmark]", "\uFF0F");
|
||||
|
||||
for (TextraButton button : abilityButtonMap) {
|
||||
setAlpha(button, visible);
|
||||
}
|
||||
opacity = visible ? 1f : 0.4f;
|
||||
}
|
||||
|
||||
public void setHUDOpacity(boolean translucent) {
|
||||
if (translucent) {
|
||||
if (!MapStage.getInstance().isInMap())
|
||||
return; //WorldStage opacity issue
|
||||
setAlpha(hudGroup, false);
|
||||
setAlpha(menuGroup, false);
|
||||
setAlpha(avatarGroup, false);
|
||||
for (TextraButton button : abilityButtonMap) {
|
||||
setAlpha(button, false);
|
||||
}
|
||||
transluscent = true;
|
||||
} else {
|
||||
setAlpha(hudGroup, true);
|
||||
setAlpha(menuGroup, true);
|
||||
setAlpha(avatarGroup, true);
|
||||
for (TextraButton button : abilityButtonMap) {
|
||||
setAlpha(button, true);
|
||||
}
|
||||
transluscent = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void showHideHUD(boolean hide) {
|
||||
if (hide) {
|
||||
hudGroup.addAction(Actions.fadeOut(0.5f));
|
||||
menuGroup.addAction(Actions.fadeOut(0.5f));
|
||||
if (!MapStage.getInstance().isInMap())
|
||||
mapGroup.addAction(Actions.fadeOut(0.5f));
|
||||
if (MapStage.getInstance().isInMap())
|
||||
avatarGroup.addAction(Actions.alpha(0.4f, 0.5f));
|
||||
hidden = true;
|
||||
} else {
|
||||
float alpha = MapStage.getInstance().isInMap() ? 0.4f : 1f;
|
||||
avatarGroup.addAction(Actions.alpha(alpha, 0.5f));
|
||||
hudGroup.addAction(Actions.alpha(alpha, 0.5f));
|
||||
menuGroup.addAction(Actions.alpha(alpha, 0.5f));
|
||||
if (!MapStage.getInstance().isInMap())
|
||||
mapGroup.addAction(Actions.fadeIn(0.5f));
|
||||
hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
void toggleConsole() {
|
||||
console.toggle();
|
||||
if (console.isVisible()) {
|
||||
@@ -810,8 +883,8 @@ public class GameHUD extends Stage {
|
||||
public boolean act(float v) {
|
||||
if (exitDungeon) {
|
||||
MapStage.getInstance().exitDungeon();
|
||||
setDisabled(exitToWorldMapActor, true, "[%120][+ExitToWorldMap]", "---");
|
||||
setDisabled(bookmarkActor, true, "[%120][+Bookmark]", "---");
|
||||
setDisabled(exitToWorldMapActor, true, "[%120][+ExitToWorldMap]", "\uFF0F");
|
||||
setDisabled(bookmarkActor, true, "[%120][+Bookmark]", "\uFF0F");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -861,9 +934,21 @@ public class GameHUD extends Stage {
|
||||
|
||||
@Override
|
||||
public boolean longPress(Actor actor, float x, float y) {
|
||||
toggleConsole();
|
||||
if (GuiBase.isAndroid())
|
||||
toggleConsole();
|
||||
return super.longPress(actor, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tap(InputEvent event, float x, float y, int count, int button) {
|
||||
if (console.isVisible())
|
||||
return;
|
||||
if (count > 1 && button == 0)
|
||||
showHideHUD(!hidden);
|
||||
else if (button == 0)
|
||||
setHUDOpacity(!transluscent);
|
||||
super.tap(event, x, y, count, button);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMusic() {
|
||||
@@ -927,13 +1012,13 @@ public class GameHUD extends Stage {
|
||||
notificationText.setWrap(false);
|
||||
notificationText.setText(text);
|
||||
notificationText.setColor(Color.BLACK);
|
||||
notificationText.setWidth(Math.min(notificationText.getPrefWidth(), Forge.isLandscapeMode()?getWidth() * 0.25f : getWidth() - 25));
|
||||
notificationText.setWidth(Math.min(notificationText.getPrefWidth(), Forge.isLandscapeMode() ? getWidth() * 0.25f : getWidth() - 25));
|
||||
notificationText.setWrap(true);
|
||||
notificationText.layout();
|
||||
|
||||
notificationPane.setSize(notificationText.getWidth() + 10, notificationText.getPrefHeight() + 20);
|
||||
|
||||
notificationPane.setPosition(5, Forge.isLandscapeMode()? -notificationPane.getHeight(): getHeight());
|
||||
notificationPane.setPosition(5, Forge.isLandscapeMode() ? -notificationPane.getHeight() : getHeight());
|
||||
|
||||
notificationPane.getColor().a = 1f;
|
||||
notificationPane.layout();
|
||||
@@ -948,22 +1033,22 @@ public class GameHUD extends Stage {
|
||||
newNotification = Actions.after(Actions.sequence(preconfigureNotification,
|
||||
Actions.moveTo(5, 0, 2f),
|
||||
Actions.delay(10f),
|
||||
Actions.alpha(0f,3f),
|
||||
Actions.sizeTo(0,0)));
|
||||
Actions.alpha(0f, 3f),
|
||||
Actions.sizeTo(0, 0)));
|
||||
} else {
|
||||
newNotification = Actions.after(Actions.sequence(preconfigureNotification,
|
||||
Actions.moveToAligned(5, getHeight(), Align.topLeft, 2f),
|
||||
Actions.delay(10f),
|
||||
Actions.alpha(0f,3f),
|
||||
Actions.sizeTo(0,0)));
|
||||
Actions.alpha(0f, 3f),
|
||||
Actions.sizeTo(0, 0)));
|
||||
}
|
||||
|
||||
notificationPane.addAction(newNotification);
|
||||
}
|
||||
|
||||
public void clearNotifications(){
|
||||
public void clearNotifications() {
|
||||
notificationText.setText("");
|
||||
notificationPane.setBounds(5, Forge.isLandscapeMode() ? -notificationText.getPrefHeight() : getHeight(), getWidth()*0.4f, 25);
|
||||
notificationPane.setBounds(5, Forge.isLandscapeMode() ? -notificationText.getPrefHeight() : getHeight(), getWidth() * 0.4f, 25);
|
||||
notificationPane.setStyle(Controls.getSkin().get("paper", ScrollPane.ScrollPaneStyle.class));
|
||||
notificationPane.getColor().a = 0f;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.github.tommyettinger.textra.TypingLabel;
|
||||
import forge.Forge;
|
||||
import forge.adventure.character.*;
|
||||
import forge.adventure.data.*;
|
||||
import forge.adventure.player.AdventurePlayer;
|
||||
import forge.adventure.pointofintrest.PointOfInterestChanges;
|
||||
import forge.adventure.scene.*;
|
||||
import forge.adventure.util.*;
|
||||
@@ -959,10 +960,11 @@ public class MapStage extends GameStage {
|
||||
} else {
|
||||
Vector2 destination = mob.getTargetVector(player, verticesNearPlayer, delta);
|
||||
|
||||
if (destination.epsilonEquals(mob.pos()) && !mob.aggro) {
|
||||
if (mob.isFrozen() || (destination.epsilonEquals(mob.pos()) && !mob.aggro)) {
|
||||
mob.setAnimation(CharacterSprite.AnimationTypes.Idle);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (destination.equals(mob.targetVector) && mob.getNavPath() != null)
|
||||
navPath = mob.getNavPath();
|
||||
|
||||
@@ -1025,20 +1027,42 @@ public class MapStage extends GameStage {
|
||||
Gdx.input.vibrate(50);
|
||||
if (Controllers.getCurrent() != null && Controllers.getCurrent().canVibrate())
|
||||
Controllers.getCurrent().startVibration(100, 1);
|
||||
startPause(0.1f, () -> { //Switch to item pickup scene.
|
||||
RewardSprite RS = (RewardSprite) actor;
|
||||
RewardScene.instance().loadRewards(RS.getRewards(), RewardScene.Type.Loot, null);
|
||||
RS.remove();
|
||||
actors.removeValue(RS, true);
|
||||
changes.deleteObject(RS.getId());
|
||||
Forge.switchScene(RewardScene.instance());
|
||||
});
|
||||
RewardSprite RS = (RewardSprite) actor;
|
||||
Array<Reward> rewards = RS.getRewards();
|
||||
|
||||
if (rewards.size == 1) {
|
||||
Reward reward = rewards.get(0);
|
||||
switch (reward.getType()) {
|
||||
case Life:
|
||||
case Shards:
|
||||
case Gold:
|
||||
String message = Forge.getLocalizer().getMessageorUseDefault("lbl" + reward.getType().name(), reward.getType().name());
|
||||
AdventurePlayer.current().addStatusMessage(reward.getType().name(), message, reward.getCount(), actor.getX(), actor.getY() + player.getHeight());
|
||||
AdventurePlayer.current().addReward(reward);
|
||||
break;
|
||||
default:
|
||||
showRewardScene(rewards);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
showRewardScene(rewards);
|
||||
}
|
||||
RS.remove();
|
||||
actors.removeValue(RS, true);
|
||||
changes.deleteObject(RS.getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showRewardScene(Array<Reward> rewards) {
|
||||
startPause(0.1f, () -> {
|
||||
RewardScene.instance().loadRewards(rewards, RewardScene.Type.Loot, null);
|
||||
Forge.switchScene(RewardScene.instance());
|
||||
});
|
||||
}
|
||||
|
||||
boolean started = false;
|
||||
public void beginDuel(EnemySprite mob) {
|
||||
if (mob == null) return;
|
||||
|
||||
@@ -139,6 +139,30 @@ public class Config {
|
||||
return configData;
|
||||
}
|
||||
|
||||
public int getBlurDivisor() {
|
||||
int val = 1;
|
||||
try {
|
||||
switch(settingsData.videomode) {
|
||||
case "720p":
|
||||
case "768p":
|
||||
val = 8;
|
||||
break;
|
||||
case "900p":
|
||||
case "1080p":
|
||||
val = 16;
|
||||
break;
|
||||
case "1440p":
|
||||
case "2160p":
|
||||
val = 32;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
import forge.adventure.util.Config;
|
||||
import forge.util.BlurUtils;
|
||||
import forge.Forge;
|
||||
import forge.Graphics;
|
||||
@@ -63,7 +64,7 @@ public class WorldSaveHeader implements java.io.Serializable, Disposable {
|
||||
Pixmap pixmap = Pixmap.createFromFrameBuffer(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
if (Forge.lastPreview != null)
|
||||
Forge.lastPreview.dispose();
|
||||
Pixmap blurred = BlurUtils.blur(pixmap, 4, 2, false, true);
|
||||
Pixmap blurred = BlurUtils.blur(pixmap, 4, 2, false, Config.instance().getBlurDivisor());
|
||||
Forge.lastPreview = new Texture(blurred);
|
||||
Pixmap scaled = new Pixmap(WorldSaveHeader.previewImageWidth, (int) (WorldSaveHeader.previewImageWidth / (Scene.getIntendedWidth() / (float) Scene.getIntendedHeight())), Pixmap.Format.RGBA8888);
|
||||
scaled.drawPixmap(pixmap,
|
||||
|
||||
@@ -270,12 +270,12 @@ public class BlurUtils {
|
||||
pixmap.getWidth(), pixmap.getHeight(), radius, iterations,
|
||||
disposePixmap);
|
||||
}
|
||||
public static Pixmap blur(Pixmap pixmap, int radius, int iterations, boolean disposePixmap, boolean crop) {
|
||||
public static Pixmap blur(Pixmap pixmap, int radius, int iterations, boolean disposePixmap, int div) {
|
||||
int x = (int)(pixmap.getWidth()*0.35f);
|
||||
int y = (int)(pixmap.getHeight()*0.35f);
|
||||
int width = pixmap.getWidth()-x;
|
||||
int height = pixmap.getHeight()-y;
|
||||
return blur(pixmap, x/2, y/2, width, height, 0, 0, width, height, radius, iterations, disposePixmap);
|
||||
return blur(pixmap, x/2, y/2, width, height, 0, 0, width/div, height/div, radius, iterations, disposePixmap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,7 +66,14 @@
|
||||
"UNH",
|
||||
"PPC1",
|
||||
"UND",
|
||||
"PUST"
|
||||
"PUST",
|
||||
"UEPHI23",
|
||||
"UEMIN23",
|
||||
"UELAS23",
|
||||
"UEIND23",
|
||||
"UEBAR23",
|
||||
"MB2",
|
||||
"UNF"
|
||||
],
|
||||
"difficulties": [
|
||||
{
|
||||
|
||||
@@ -6,7 +6,8 @@ SVar:TrigSeek:DB$ Seek | Num$ 2 | Type$ Card.nonLand
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ You can't lose the game. | CheckSVar$ YourLife | EffectZone$ Command | SVarCompare$ GE40 | Secondary$ True | Description$ You can't lose the game and your opponents can't win the game.
|
||||
S:Mode$ Continuous | Affected$ Opponent | AddKeyword$ You can't win the game. | Secondary$ True | EffectZone$ Command | CheckSVar$ YourLife | Secondary$ True | SVarCompare$ GE40 | Description$ You can't lose the game and your opponents can't win the game.
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | CheckSVar$ YourLifeCompare | SVarCompare$ EQ2 | Execute$ TrigConjure | TriggerDescription$ As long as Sorin's life total is between 20 and 40, at Sorin's upkeep, conjure a card from Sorin's Spellbook into exile with 2 time counters on it, it gains suspend.
|
||||
SVar:TrigConjure:DB$ MakeCard | Conjure$ True | AtRandom$ True | Spellbook$ Sorin; Grim Nemesis,Sorin; Imperious Bloodlord,Sorin; Lord of Innistrad,Sorin Markov,Sorin; Solemn Visitor,Sorin the Mirthless,Sorin; Vampire Lord,Sorin; Vengeful Bloodlord,Timothar; Baron of Bats,Olivia Voldaren,Patriarch's Bidding,Licia; Sanguine Tribune,Astarion; the Decadent,Strefan; Maurer Progenitor,Evelyn; the Covetous,Anje; Maid of Dishonor,Edgar Markov | WithCounters$ TIME | WithCountersAmount$ 2 | Zone$ Exile | RememberMade$ True | SubAbility$ GiveSuspend
|
||||
SVar:TrigConjure:DB$ MakeCard | Conjure$ True | AtRandom$ True | Spellbook$ Sorin; Grim Nemesis,Sorin; Imperious Bloodlord,Sorin; Lord of Innistrad,Sorin Markov,Sorin; Solemn Visitor,Sorin the Mirthless,Sorin; Vampire Lord,Sorin; Vengeful Bloodlord,Timothar; Baron of Bats,Olivia Voldaren,Patriarch's Bidding,Licia; Sanguine Tribune,Astarion; the Decadent,Strefan; Maurer Progenitor,Evelyn; the Covetous,Anje; Maid of Dishonor,Edgar Markov,Swords to Plowshares,Cruel Celebrant,Olivia's Wrath,Markov Baron,Champion of Dusk,Vein Ripper,Anguished Unmaking,Mortify,Void Rend,Terminate,Despark,Bedevil,Utter End,Ruinous Ultimatum,Sign in Blood,Reanimate,Victimize | Zone$ Exile | RememberMade$ True | SubAbility$ DBPutCounter
|
||||
SVar:DBPutCounter:DB$ PutCounter | Defined$ Remembered | CounterNum$ 3 | CounterType$ TIME | SubAbility$ GiveSuspend
|
||||
SVar:GiveSuspend:DB$ Pump | Defined$ Remembered | KW$ Suspend | PumpZone$ Exile | Duration$ Permanent | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
S:Mode$ Continuous | EffectZone$ Command | Affected$ Vampire.YouCtrl | AddPower$ 2 | AddToughness$ 2 | CheckSVar$ YourLife | SVarCompare$ LT20 | AddKeyword$ Lifelink | Description$ As long as Sorin's life total is lower than 20, Sorin's Vampires get +2/+2 and have lifelink.
|
||||
|
||||
@@ -458,6 +458,11 @@
|
||||
"down": "right_down",
|
||||
"focused": "right_f"
|
||||
},
|
||||
"roundhint": {
|
||||
"up": "unpressedround",
|
||||
"down": "pressedround",
|
||||
"focused": "unpressedround"
|
||||
},
|
||||
"item_frame": {
|
||||
"imageCheckedOver": "item_frame_selected_hover",
|
||||
"up": "item_frame",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@@ -56,7 +56,7 @@
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "lifePoints",
|
||||
"font": "default",
|
||||
"width": 64,
|
||||
@@ -65,7 +65,7 @@
|
||||
"y": 56
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "shards",
|
||||
"font": "default",
|
||||
"width": 64,
|
||||
@@ -74,7 +74,7 @@
|
||||
"y": 70
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "money",
|
||||
"font": "default",
|
||||
"width": 64,
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "lifePoints",
|
||||
"font": "default",
|
||||
"width": 64,
|
||||
@@ -65,7 +65,7 @@
|
||||
"y": 56
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "shards",
|
||||
"font": "default",
|
||||
"width": 64,
|
||||
@@ -74,7 +74,7 @@
|
||||
"y": 70
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "money",
|
||||
"font": "default",
|
||||
"width": 64,
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "lifePoints",
|
||||
"width": 48,
|
||||
"height": 3,
|
||||
@@ -64,7 +64,7 @@
|
||||
"y": 62
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "shards",
|
||||
"font": "default",
|
||||
"width": 48,
|
||||
@@ -73,7 +73,7 @@
|
||||
"y": 76
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "money",
|
||||
"font": "default",
|
||||
"width": 48,
|
||||
|
||||
@@ -65,14 +65,14 @@
|
||||
"yOffset": 8
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"type": "ImageButton",
|
||||
"name": "difficultyHelp",
|
||||
"text": "[GOLD]?",
|
||||
"style": "roundhint",
|
||||
"selectable": true,
|
||||
"width": 16,
|
||||
"height": 16,
|
||||
"width": 12,
|
||||
"height": 15,
|
||||
"x": 145,
|
||||
"yOffset": -16
|
||||
"yOffset": -17
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
@@ -93,14 +93,14 @@
|
||||
"yOffset": 8
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"type": "ImageButton",
|
||||
"name": "modeHelp",
|
||||
"text": "[GOLD]?",
|
||||
"style": "roundhint",
|
||||
"selectable": true,
|
||||
"width": 16,
|
||||
"height": 16,
|
||||
"width": 12,
|
||||
"height": 15,
|
||||
"x": 145,
|
||||
"yOffset": -16
|
||||
"yOffset": -17
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"width": 128,
|
||||
"height": 24,
|
||||
"x": 16,
|
||||
"y": 140
|
||||
"y": 148
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
@@ -65,14 +65,14 @@
|
||||
"yOffset": 8
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"type": "ImageButton",
|
||||
"name": "difficultyHelp",
|
||||
"text": "[GOLD]?",
|
||||
"style": "roundhint",
|
||||
"selectable": true,
|
||||
"width": 24,
|
||||
"height": 24,
|
||||
"x": 72,
|
||||
"yOffset": -24
|
||||
"width": 12,
|
||||
"height": 15,
|
||||
"x": 80,
|
||||
"yOffset": -17
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
@@ -93,14 +93,14 @@
|
||||
"yOffset": 8
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"type": "ImageButton",
|
||||
"name": "modeHelp",
|
||||
"text": "[GOLD]?",
|
||||
"style": "roundhint",
|
||||
"selectable": true,
|
||||
"width": 24,
|
||||
"height": 24,
|
||||
"x": 72,
|
||||
"yOffset": -24
|
||||
"width": 12,
|
||||
"height": 15,
|
||||
"x": 80,
|
||||
"yOffset": -17
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
{
|
||||
"type": "Image",
|
||||
"name": "avatar",
|
||||
"x": 321,
|
||||
"x": 384,
|
||||
"y": 28,
|
||||
"width": 64,
|
||||
"height": 64
|
||||
@@ -46,7 +46,7 @@
|
||||
"type": "Image",
|
||||
"name": "colorFrame",
|
||||
"image": "ui/colorC.png",
|
||||
"x": 305,
|
||||
"x": 368,
|
||||
"y": 45,
|
||||
"width": 64,
|
||||
"height": 64
|
||||
@@ -137,9 +137,9 @@
|
||||
"y": 224
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "playerName",
|
||||
"x": 394,
|
||||
"x": 310,
|
||||
"y": 20,
|
||||
"width": 80,
|
||||
"height": 24,
|
||||
@@ -150,7 +150,7 @@
|
||||
"name": "lifePoints",
|
||||
"width": 64,
|
||||
"height": 16,
|
||||
"x": 394,
|
||||
"x": 310,
|
||||
"y": 40
|
||||
},
|
||||
{
|
||||
@@ -158,7 +158,7 @@
|
||||
"name": "money",
|
||||
"width": 64,
|
||||
"height": 16,
|
||||
"x": 394,
|
||||
"x": 310,
|
||||
"y": 80
|
||||
},
|
||||
{
|
||||
@@ -166,7 +166,7 @@
|
||||
"name": "shards",
|
||||
"width": 64,
|
||||
"height": 16,
|
||||
"x": 394,
|
||||
"x": 310,
|
||||
"y": 60
|
||||
},
|
||||
{
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
"y": 440
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"type": "TypingLabel",
|
||||
"name": "playerName",
|
||||
"x": 98,
|
||||
"y": 4,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
Name:Anax and Cymede and Kynaios and Tiro
|
||||
ManaCost:1 W U R G
|
||||
Types:Legendary Creature Human Soldier
|
||||
PT:3/8
|
||||
K:First Strike
|
||||
K:Vigilance
|
||||
T:Mode$ SpellCast | ValidActivatingPlayer$ You | TargetsValid$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Heroic — Whenever you cast a spell that targets CARDNAME, draw a card. Each player may put a land card from their hand onto the battlefield, then each opponent who didn't draws a card.
|
||||
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ EachPlayLand
|
||||
SVar:EachPlayLand:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | DefinedPlayer$ Player | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DrawAbstainers
|
||||
SVar:DrawAbstainers:DB$ Draw | Defined$ OppNonRememberedOwner | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
Oracle:First strike, vigilance\nHeroic — Whenever you cast a spell that targets Anax and Cymede and Kynaios and Tiro, draw a card. Each player may put a land card from their hand onto the battlefield, then each opponent who didn't draws a card.
|
||||
8
forge-gui/res/cardsfolder/a/artifact_unknown_shores.txt
Normal file
8
forge-gui/res/cardsfolder/a/artifact_unknown_shores.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Artifact Unknown Shores
|
||||
ManaCost:no cost
|
||||
Types:Artifact Land
|
||||
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
|
||||
A:AB$ Mana | Cost$ 1 T | Produced$ Any | SpellDescription$ Add one mana of any color.
|
||||
AI:RemoveDeck:Random
|
||||
DeckHas:Ability$Mana.Colorless
|
||||
Oracle:{T}: Add {C}.\n{1}, {T}: Add one mana of any color.
|
||||
11
forge-gui/res/cardsfolder/a/avacyn_and_griselbrand.txt
Normal file
11
forge-gui/res/cardsfolder/a/avacyn_and_griselbrand.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Avacyn and Griselbrand
|
||||
ManaCost:4 W W B B
|
||||
Types:Legendary Creature Angel Demon
|
||||
PT:8/8
|
||||
K:Flying
|
||||
K:Vigilance
|
||||
K:Lifelink
|
||||
A:AB$ PumpAll | Cost$ PayLife<8> | ValidCards$ Creature.YouCtrl | KW$ Indestructible | SubAbility$ DBDraw | SpellDescription$ Each creature you control gains indestructible until end of turn. Then, draw a card for each creature you control.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ X
|
||||
SVar:X:Count$TypeYouCtrl.Creature
|
||||
Oracle:Flying, vigilance, lifelink\nPay 8 life: Each creature you control gains indestructible until end of turn. Then, draw a card for each creature you control.
|
||||
7
forge-gui/res/cardsfolder/b/believe_in_the_cleave.txt
Normal file
7
forge-gui/res/cardsfolder/b/believe_in_the_cleave.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Believe in the Cleave
|
||||
ManaCost:3 R
|
||||
Types:Instant
|
||||
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ CARDNAME costs {1} less to cast for each attacking creature you control.
|
||||
SVar:X:Count$Valid Creature.attacking+YouCtrl
|
||||
A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ 1 | NumDef$ 1 | KW$ Double Strike | SpellDescription$ Target creature gets +1/+1 and gains double strike until end of turn. | StackDescription$ SpellDescription
|
||||
Oracle:Believe in the Cleave costs {1} less to cast for each attacking creature you control.\nTarget creature gets +1/+1 and gains double strike until end of turn.
|
||||
11
forge-gui/res/cardsfolder/b/bram_baguette_brawler.txt
Normal file
11
forge-gui/res/cardsfolder/b/bram_baguette_brawler.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Bram, Baguette Brawler
|
||||
ManaCost:1 W
|
||||
Types:Legendary Creature Human Peasant
|
||||
PT:2/2
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.")
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_food_sac | TokenOwner$ You
|
||||
S:Mode$ Continuous | Affected$ Food.nonCreature+YouCtrl | AddType$ Equipment | AddStaticAbility$ FoodEquip | AddKeyword$ Equip:1 | Description$ Each noncreature Food you control is an Equipment with equip {1} and "Equipped creature gets +1/+1."
|
||||
SVar:FoodEquip:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | Description$ Equipped creature gets +1/+1.
|
||||
DeckHas:Ability$Token|Sacrifice & Type$Food|Artifact|Equipment
|
||||
DeckHints:Type$Food
|
||||
Oracle:When Bram, Baguette Brawler enters the battlefield, create a Food token.\nEach noncreature Food you control is an Equipment with equip {1} and "Equipped creature gets +1/+1."
|
||||
@@ -0,0 +1,14 @@
|
||||
Name:Bringer of Green Zenith's Twilight
|
||||
ManaCost:X G G
|
||||
Types:Creature Phyrexian Bringer
|
||||
PT:2/2
|
||||
S:Mode$ AlternativeCost | ValidSA$ Spell.Self | EffectZone$ All | Cost$ W U B R G | Description$ You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost. If you do, X is 5.
|
||||
K:etbCounter:P1P1:Y:no condition:CARDNAME enters the battlefield with X +1/+1 counters on it. If X was 5 or greater, it enters with twice that many counters instead.
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigChange | TriggerDescription$ When CARDNAME dies, shuffle it into its owner's library.
|
||||
SVar:TrigChange:DB$ ChangeZone | Destination$ Library | Shuffle$ True | Defined$ TriggeredNewCardLKICopy
|
||||
SVar:X:Count$xPaid
|
||||
SVar:AltCostPaid:Count$AltCost.5.X
|
||||
SVar:Y:Count$Compare AltCostPaid LT5.AltCostPaid.Z
|
||||
SVar:Z:SVar$AltCostPaid/Twice
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:You may pay {W}{U}{B}{R}{G} rather than pay this spell's mana cost. If you do, X is 5.\nBringer of Green Zenith's Twilight enters the battlefield with X +1/+1 counters on it. If X was 5 or greater, it enters with twice that many counters instead.\nWhen Bringer of Green Zenith's Twilight dies, shuffle it into its owner's library.
|
||||
12
forge-gui/res/cardsfolder/c/cat_oven.txt
Normal file
12
forge-gui/res/cardsfolder/c/cat_oven.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Cat Oven
|
||||
ManaCost:1
|
||||
Types:Artifact
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigFood | TriggerDescription$ When CARDNAME enters the battlefield, create a Food token.
|
||||
SVar:TrigFood:DB$ Token | TokenScript$ c_a_food_sac | TokenAmount$ 1
|
||||
A:AB$ Token | Cost$ T Sac<1/Food> | TokenScript$ b_1_1_cat | TokenAmount$ 1 | SubAbility$ DBDrain | SpellDescription$ Create a 1/1 black Cat creature token. Each opponent loses 1 life and you gain 1 life.
|
||||
SVar:DBDrain:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1 | SubAbility$ DBGainLife
|
||||
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1
|
||||
SVar:AIPreference:SacCost$Food.token
|
||||
DeckHas:Ability$Token|Sacrifice|LifeGain & Type$Food|Artifact|Cat
|
||||
DeckHints:Type$Food
|
||||
Oracle:When Cat Oven enters the battlefield, create a Food token.\n{T}, Sacrifice a Food: Create a 1/1 black Cat creature token. Each opponent loses 1 life and you gain 1 life.
|
||||
@@ -0,0 +1,7 @@
|
||||
Name:Chatterstorm and Awaken the Woods
|
||||
ManaCost:1 G
|
||||
Types:Sorcery
|
||||
K:Storm
|
||||
A:SP$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_forest_dryad_squirrel | TokenOwner$ You | SpellDescription$ Create a 1/1 green Forest Dryad Squirrel land creature token.
|
||||
DeckHas:Ability$Token
|
||||
Oracle:Create a 1/1 green Forest Dryad Squirrel land creature token. (It's affected by summoning sickness.)\nStorm (When you cast this spell, copy it for each spell cast before it this turn.)
|
||||
14
forge-gui/res/cardsfolder/c/cinnamon_seasoned_steed.txt
Normal file
14
forge-gui/res/cardsfolder/c/cinnamon_seasoned_steed.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Name:Cinnamon, Seasoned Steed
|
||||
ManaCost:1
|
||||
Types:Legendary Artifact Creature Food Horse
|
||||
PT:1/1
|
||||
S:Mode$ Continuous | Affected$ Creature.Food+Other+YouCtrl | AddKeyword$ Horsemanship | Description$ Other Food creatures you control have horsemanship. (They can't be blocked except by creatures with horsemanship.)
|
||||
T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | OptionalDecider$ You | TriggerDescription$ At the beginning of your combat step, you may put a +1/+1 counter on target non-creature Food you control. If you do, it becomes a 0/0 Knight creature in addition to its other types.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Food.nonCreature+YouCtrl | TgtPrompt$ Select target noncreature Food you control | CounterType$ P1P1 | CounterNum$ 1 | RememberCards$ True | SubAbility$ DBAnimate
|
||||
SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Power$ 0 | Toughness$ 0 | Types$ Creature,Knight | Duration$ Permanent | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
A:AB$ GainLife | Cost$ 2 T Sac<1/CARDNAME> | LifeAmount$ 3 | SpellDescription$ You gain 3 life.
|
||||
SVar:PlayMain1:TRUE
|
||||
DeckHas:Ability$LifeGain|Counters & Type$Knight
|
||||
DeckHints:Type$Food
|
||||
Oracle:Other Food creatures you control have horsemanship. (They can't be blocked except by creatures with horsemanship.)\nAt the beginning of your combat step, you may put a +1/+1 counter on target non-creature Food you control. If you do, it becomes a 0/0 Knight creature in addition to its other types.\n{2}, {T}, Sacrifice Cinnamon, Seasoned Steed: You gain 3 life.
|
||||
@@ -0,0 +1,7 @@
|
||||
Name:Colossal Dreadmaw and Storm Crow
|
||||
ManaCost:5 U G
|
||||
Types:Legendary Creature Dinosaur Bird
|
||||
PT:7/8
|
||||
K:Flying
|
||||
K:Trample
|
||||
Oracle:Flying, trample
|
||||
9
forge-gui/res/cardsfolder/d/delve_too_deep.txt
Normal file
9
forge-gui/res/cardsfolder/d/delve_too_deep.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Delve too Deep
|
||||
ManaCost:X B
|
||||
Types:Sorcery
|
||||
K:Delve
|
||||
A:SP$ Pump | ValidTgts$ Creature | NumAtt$ -X | NumDef$ -X | IsCurse$ True | SubAbility$ DBToken | SpellDescription$ Target creature gets -X/-X until end of turn. If X was 7 or greater, create a 7/7 black and red Demon creature token.
|
||||
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ br_7_7_demon | TokenOwner$ You | ConditionCheckSVar$ X | ConditionSVarCompare$ GE7
|
||||
SVar:X:Count$xPaid
|
||||
DeckHas:Ability$Token & Type$Demon
|
||||
Oracle:Delve\nTarget creature gets -X/-X until end of turn. If X was 7 or greater, create a 7/7 black and red Demon creature token.
|
||||
@@ -5,7 +5,7 @@ PT:5/6
|
||||
T:Mode$ ChangesZone | ValidCard$ Permanent.Lhurgoyf+YouOwn | Origin$ Library,Hand,Exile,Command,Stack | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigChangeZone | TriggerDescription$ Whenever a Lhurgoyf permanent card is put into your graveyard from anywhere other than the battlefield, put it onto the battlefield.
|
||||
SVar:TrigChangeZone:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Battlefield
|
||||
T:Mode$ DamageDoneOnce | ValidSource$ Creature.YouCtrl | TriggerZones$ Battlefield | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever one or more creatures you control deal combat damage to a player, create a Tarmogoyf token. (It's a {1}{G} Lhurgoyf creature with "Tarmogoyf's power is equal to the number of card types among cards in all graveyards and its toughness is equal to that number plus 1.")
|
||||
SVar:TrigToken:DB$ Token | TokenScript$ tarmogoyf
|
||||
SVar:TrigToken:DB$ CopyPermanent | DefinedName$ Tarmogoyf
|
||||
DeckNeeds:Type$Lhurgoyf
|
||||
DeckHas:Ability$Token & Type$Lhurgoyf
|
||||
DeckHints:Ability$Mill
|
||||
|
||||
10
forge-gui/res/cardsfolder/e/eldest_dragon_highlander.txt
Normal file
10
forge-gui/res/cardsfolder/e/eldest_dragon_highlander.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Eldest Dragon Highlander
|
||||
ManaCost:W W U U B B R R G G
|
||||
Types:Legendary Creature Elder Dragon
|
||||
PT:7/7
|
||||
K:Flying
|
||||
K:Trample
|
||||
K:Rampage:2
|
||||
A:AB$ PumpAll | Cost$ W U B R G | ValidCards$ Dragon.Elder+YouCtrl | NumAtt$ 7 | NumDef$ 7 | SpellDescription$ Elder Dragons you control get +7/+7 until end of turn.
|
||||
K:UpkeepCost:W U B R G
|
||||
Oracle:Flying, trample, rampage 2\n{W}{U}{B}{R}{G}: Elder Dragons you control get +7/+7 until end of turn.\nAt the beginning of your upkeep, sacrifice Eldest Dragon Highlander unless you pay {W}{U}{B}{R}{G}.
|
||||
9
forge-gui/res/cardsfolder/e/ensoul_ring.txt
Normal file
9
forge-gui/res/cardsfolder/e/ensoul_ring.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Ensoul Ring
|
||||
ManaCost:1 U
|
||||
Types:Enchantment Aura
|
||||
K:Enchant nonland permanent
|
||||
A:SP$ Attach | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | AILogic$ Curse
|
||||
S:Mode$ Continuous | Affected$ Permanent.EnchantedBy | SetColor$ Colorless | AddType$ Artifact | RemoveCardTypes$ True | RemoveArtifactTypes$ True | AddAbility$ SolMana | SetName$ Sol Ring | RemoveAllAbilities$ True | Description$ Enchanted permanent is a Sol Ring. (It's no longer anything else.)
|
||||
SVar:SolMana:AB$ Mana | Cost$ T | Produced$ C | Amount$ 2 | SpellDescription$ Add {C}{C}.
|
||||
SVar:NonStackingAttachEffect:True
|
||||
Oracle:Enchant nonland permanent.\nEnchanted permanent is a Sol Ring. (It's no longer anything else.)
|
||||
7
forge-gui/res/cardsfolder/f/force_of_rowan.txt
Normal file
7
forge-gui/res/cardsfolder/f/force_of_rowan.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Force of Rowan
|
||||
ManaCost:3 R R
|
||||
Types:Instant
|
||||
S:Mode$ AlternativeCost | ValidSA$ Spell.Self | EffectZone$ All | Cost$ PayLife<1> ExileFromHand<1/Card.Red+Other> | Description$ You may pay 1 life and exile a red card from your hand rather than pay this spell's mana cost.
|
||||
A:SP$ CopySpellAbility | ValidTgts$ Instant,Sorcery | TargetType$ Spell | MayChooseTarget$ True | SpellDescription$ Copy target instant or sorcery spell. You may choose new targets for the copy.
|
||||
DeckHints:Type$Instant|Sorcery
|
||||
Oracle:You may pay 1 life and exile a red card from your hand rather than pay this spell's mana cost.\nCopy target instant or sorcery spell. You may choose new targets for the copy.
|
||||
10
forge-gui/res/cardsfolder/f/forestfolk.txt
Normal file
10
forge-gui/res/cardsfolder/f/forestfolk.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Forestfolk
|
||||
ManaCost:2 G U
|
||||
Types:Creature Elf Wizard
|
||||
PT:2/2
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChange | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle.
|
||||
SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ 1 | ShuffleNonMandatory$ True
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME leaves the battlefield, draw a card.
|
||||
SVar:TrigDraw:DB$ Draw | Defined$ TriggeredCardController | NumCards$ 1
|
||||
SVar:SacMe:1
|
||||
Oracle:When Forestfolk enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle.\nWhen Forestfolk leaves the battlefield, draw a card.
|
||||
@@ -0,0 +1,19 @@
|
||||
Name:Garruk's Lost Wolf
|
||||
ManaCost:3 G
|
||||
Types:Creature Wolf
|
||||
PT:2/2
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a Huntsman Role token attached to another target creature you control. (Enchanted creature gets +1/+1 and has "{T}: Add {G}")
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ role_huntsman | TokenOwner$ You | AttachedTo$ Targeted | ValidTgts$ Creature.YouCtrl+Other | TgtPrompt$ Select another target creature you control
|
||||
DeckHas:Ability$Mill|Graveyard|Token & Type$Aura|Enchantment|Role
|
||||
AlternateMode:Adventure
|
||||
Oracle:When Garruk's Lost Wolf enters the battlefield, create a Huntsman Role token attached to another target creature you control. (Enchanted creature gets +1/+1 and has "{T}: Add {G}")
|
||||
|
||||
ALTERNATE
|
||||
|
||||
Name:Hey, Has Anyone Seen Garruk?
|
||||
ManaCost:1 G
|
||||
Types:Sorcery Adventure
|
||||
A:SP$ Mill | NumCards$ 4 | RememberMilled$ True | SubAbility$ DBChangeZone | SpellDescription$ Mill the top four cards of your library. Return a creature or planeswalker card milled this way to your hand.
|
||||
SVar:DBChangeZone:DB$ ChangeZone | Hidden$ True | Origin$ Graveyard,Exile | Mandatory$ True | Destination$ Hand | ChangeType$ Card.IsRemembered+Creature,Card.IsRemembered+Planeswalker | SelectPrompt$ Return a creature or planeswalker card milled this way to your hand | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
Oracle:Mill the top four cards of your library. Return a creature or planeswalker card milled this way to your hand.
|
||||
12
forge-gui/res/cardsfolder/g/gingerbehemoth.txt
Normal file
12
forge-gui/res/cardsfolder/g/gingerbehemoth.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Gingerbehemoth
|
||||
ManaCost:6
|
||||
Types:Artifact Creature Food Golem
|
||||
PT:6/6
|
||||
K:Vigilance
|
||||
K:Trample
|
||||
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Secondary$ True | Description$ CARDNAME costs {2} less to cast for each Food you've sacrificed this turn.
|
||||
SVar:X:PlayerCountPropertyYou$SacrificedThisTurn Food/Times.2
|
||||
A:AB$ GainLife | Cost$ 4 T Sac<1/CARDNAME> | LifeAmount$ 6 | SpellDescription$ You gain 6 life.
|
||||
DeckHas:Ability$LifeGain
|
||||
DeckHints:Type$Food
|
||||
Oracle:Gingerbehemoth costs {2} less to cast for each Food you've sacrificed this turn.\nVigilance, trample\n{4}, {T}, Sacrifice Gingerbehemoth: You gain 6 life.
|
||||
9
forge-gui/res/cardsfolder/g/glorious_dragon_kin.txt
Normal file
9
forge-gui/res/cardsfolder/g/glorious_dragon_kin.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Glorious Dragon-Kin
|
||||
ManaCost:7
|
||||
Types:Artifact Creature Dragon
|
||||
PT:6/6
|
||||
K:Flying
|
||||
K:ETBReplacement:Other:ChooseColor
|
||||
SVar:ChooseColor:DB$ ChooseColor | Defined$ You | SpellDescription$ As CARDNAME enters the battlefield, choose a color. | AILogic$ MostProminentInHumanDeck
|
||||
S:Mode$ Continuous | Affected$ Creature.Artifact+YouCtrl,Dragon.YouCtrl | AddKeyword$ Protection:Card.ChosenColor:chosenColor | Description$ Artifact creatures and Dragons you control have protection from the chosen color.
|
||||
Oracle:Flying\nAs Glorious Dragon-Kin enters the battlefield, choose a color.\nArtifact creatures and Dragons you control have protection from the chosen color.
|
||||
10
forge-gui/res/cardsfolder/h/honk.txt
Normal file
10
forge-gui/res/cardsfolder/h/honk.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:HONK!
|
||||
ManaCost:G
|
||||
Types:Instant
|
||||
A:SP$ Charm | Choices$ TheGooseIsLoose,TheGooseLaidAnEgg
|
||||
SVar:TheGooseIsLoose:DB$ Destroy | ValidTgts$ Artifact,Enchantment | TgtPrompt$ Select target artifact or enchantment | SubAbility$ DBGiveFood | SpellDescription$ The Goose is Loose — Destroy target artifact or enchantment. Its controller creates a Food token.
|
||||
SVar:DBGiveFood:DB$ Token | TokenScript$ c_a_food_sac | TokenOwner$ TargetedController | TokenAmount$ 1
|
||||
SVar:TheGooseLaidAnEgg:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBGetFood | SpellDescription$ The Goose Laid an Egg — Put a +1/+1 counter on target creature. You create a Food token.
|
||||
SVar:DBGetFood:DB$ Token | TokenScript$ c_a_food_sac | TokenAmount$ 1
|
||||
DeckHas:Ability$Token|Counters|LifeGain & Type$Food|Artifact
|
||||
Oracle:Choose one —\n• The Goose is Loose — Destroy target artifact or enchantment. Its controller creates a Food token.\n• The Goose Laid an Egg — Put a +1/+1 counter on target creature. You create a Food token.
|
||||
11
forge-gui/res/cardsfolder/h/hound_of_urabrask.txt
Normal file
11
forge-gui/res/cardsfolder/h/hound_of_urabrask.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Hound of Urabrask
|
||||
ManaCost:3 R R
|
||||
Types:Creature Phyrexian
|
||||
PT:3/3
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+counters_EQ0_OIL | TriggerZones$ Battlefield | Execute$ DBReturn | TriggerDescription$ Oildying (When this creature dies, if it had no oil counters on it, return it to the battlefield under its owner's control with an oil counter on it.)
|
||||
SVar:DBReturn:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Battlefield | WithCountersType$ OIL
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ X | AddToughness$ X | Description$ CARDNAME gets +1/+1 for each oil counter on it.
|
||||
S:Mode$ Continuous | Affected$ Card.Self+counters_GE1_OIL | AddKeyword$ Double Strike | Description$ As long as CARDNAME has an oil counter on it, it has double strike.
|
||||
SVar:X:Count$CardCounters.OIL
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Oildying (When this creature dies, if it had no oil counters on it, return it to the battlefield under its owner's control with an oil counter on it.)\nHound of Urabrask gets +1/+1 for each oil counter on it.\nAs long as Hound of Urabrask has an oil counter on it, it has double strike.
|
||||
@@ -2,7 +2,7 @@ Name:Ignite the Future
|
||||
ManaCost:3 R
|
||||
Types:Sorcery
|
||||
A:SP$ Dig | Defined$ You | DigNum$ 3 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Exile the top three cards of your library. Until the end of your next turn, you may play those cards. If this spell was cast from a graveyard, you may play cards this way without paying their mana costs.
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn | ConditionDefined$ Self | ConditionPresent$ Card.wasCastFromGraveyard | ConditionCompare$ EQ0 | SubAbility$ DBEffect2
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn | SubAbility$ DBEffect2
|
||||
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play remembered card.
|
||||
SVar:DBEffect2:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play2 | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn | ConditionDefined$ Self | ConditionPresent$ Card.wasCastFromGraveyard | ConditionCompare$ EQ1 | SubAbility$ DBCleanup
|
||||
SVar:Play2:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play remembered card without paying their mana costs.
|
||||
|
||||
10
forge-gui/res/cardsfolder/i/incisor_steed.txt
Normal file
10
forge-gui/res/cardsfolder/i/incisor_steed.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Incisor Steed
|
||||
ManaCost:1 W
|
||||
Types:Artifact Creature Phyrexian Horse
|
||||
PT:1/4
|
||||
K:Vigilance
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 3 | CheckSVar$ X | SVarCompare$ GE3 | Condition$ Metalcraft | Description$ Corrupted Metalcraft — As long as you control three or more artifacts and an opponent has three or more poison counters, CARDNAME gets +3/+0.
|
||||
SVar:X:PlayerCountOpponents$HighestCounters.Poison
|
||||
SVar:BuffedBy:Artifact
|
||||
DeckHints:Keyword$Toxic|Infect & Type$Artifact
|
||||
Oracle:Vigilance\nCorrupted Metalcraft — As long as you control three or more artifacts and an opponent has three or more poison counters, Incisor Steed gets +3/+0.
|
||||
7
forge-gui/res/cardsfolder/i/incubob.txt
Normal file
7
forge-gui/res/cardsfolder/i/incubob.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Incubob
|
||||
ManaCost:1 B
|
||||
Types:Sorcery
|
||||
K:Flashback:3 B PayLife<3>
|
||||
A:SP$ Token | TokenScript$ incubator_dark_confidant | WithCountersType$ P1P1 | WithCountersAmount$ 1 | SpellDescription$ Incubate Dark Confidant 1. (Create an Incubator Dark Confidant token with a +1/+1 counter on it and "{2}: Transform this artifact." It transforms into Dark Confidant, except it's also a Phyrexian.)
|
||||
DeckHas:Ability$Token|Counters & Type$Incubator|Artifact|Phyrexian|Human|Wizard
|
||||
Oracle:Incubate Dark Confidant 1. (Create an Incubator Dark Confidant token with a +1/+1 counter on it and "{2}: Transform this artifact." It transforms into Dark Confidant, except it's also a Phyrexian.)\nFlashback—{3}{B}, Pay 3 life.
|
||||
11
forge-gui/res/cardsfolder/i/innistrad_charm.txt
Normal file
11
forge-gui/res/cardsfolder/i/innistrad_charm.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Innistrad Charm
|
||||
ManaCost:1 B
|
||||
Types:Sorcery
|
||||
A:SP$ Charm | Choices$ CemeteryRecruitment,Duress,HumanFrailty | Defined$ You
|
||||
SVar:CemeteryRecruitment:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouCtrl | SubAbility$ DBDraw | RememberChanged$ True | SpellDescription$ Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Card.Zombie | ConditionCompare$ GE1 | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:Duress:DB$ Discard | ValidTgts$ Opponent | Mode$ RevealYouChoose | DiscardValid$ Card.nonCreature+nonLand | NumCards$ 1 | SpellDescription$ Target opponent reveals their hand. You choose a noncreature, nonland card from it. That player discards that card.
|
||||
SVar:HumanFrailty:DB$ Destroy | ValidTgts$ Creature.Human | TgtPrompt$ Choose target Human creature. | SpellDescription$ Destroy target Human creature.
|
||||
DeckHints:Type$Zombie
|
||||
Oracle:Choose one —\n• Cemetery Recruitment (Return a creature card from graveyard to hand, draw a card if it's a Zombie.)\n• Duress (Look at their hand, make them discard a noncreature, nonland.)\n• Human Frailty (Destroy target Human.)
|
||||
9
forge-gui/res/cardsfolder/i/isamaru_and_yoshimaru.txt
Normal file
9
forge-gui/res/cardsfolder/i/isamaru_and_yoshimaru.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Isamaru and Yoshimaru
|
||||
ManaCost:W
|
||||
Types:Legendary Creature Dog
|
||||
PT:2/2
|
||||
T:Mode$ ChangesZone | ValidCard$ Creature.Legendary+Other+YouCtrl,Creature.cmcEQ1+Other+YouCtrl | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigCounter | TriggerDescription$ Whenever another legendary creature or creature with mana value one enters the battlefield under your control, put a +1/+1 counter on CARDNAME.
|
||||
SVar:TrigCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1
|
||||
SVar:BuffedBy:Creature.Legendary,Creature.cmcEQ1
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Whenever another legendary creature or creature with mana value one enters the battlefield under your control, put a +1/+1 counter on Isamaru and Yoshimaru.
|
||||
8
forge-gui/res/cardsfolder/j/jeska_and_kamahl.txt
Normal file
8
forge-gui/res/cardsfolder/j/jeska_and_kamahl.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Jeska and Kamahl
|
||||
ManaCost:3 R
|
||||
Types:Legendary Creature Human Barbarian Warrior
|
||||
PT:4/1
|
||||
K:First Strike
|
||||
K:Haste
|
||||
A:AB$ DealDamage | Cost$ T | ValidTgts$ Opponent,Planeswalker,Battle | TgtPrompt$ Select target opponent, battle, or planeswalker | NumDmg$ 2 | SpellDescription$ CARDNAME deals 2 damage to target opponent, battle, or planeswalker.
|
||||
Oracle:Haste, first strike\n{T}: Jeska and Kamahl deals 2 damage to target opponent, battle, or planeswalker.
|
||||
12
forge-gui/res/cardsfolder/j/joven_and_chandler.txt
Normal file
12
forge-gui/res/cardsfolder/j/joven_and_chandler.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Joven and Chandler
|
||||
ManaCost:3 R R
|
||||
Types:Legendary Creature Human Rogue
|
||||
PT:3/3
|
||||
K:Backup:2:BackupAbilities
|
||||
SVar:BackupAbilities:DB$ Animate | Keywords$ Haste | Triggers$ DamageTrig
|
||||
SVar:DamageTrig:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | Execute$ TrigDestroy | CombatDamage$ True | TriggerDescription$ Whenever this creature deals combat damage to an opponent, destroy target artifact that player controls.
|
||||
K:Haste
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | Execute$ TrigDestroy | CombatDamage$ True | TriggerDescription$ Whenever this creature deals combat damage to an opponent, destroy target artifact that player controls.
|
||||
SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Artifact.ControlledBy TriggeredTarget | TgtPrompt$ Select target artifact damaged player controls
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Backup 2 (When this creature enters the battlefield, put two +1/+1 counters on target creature. If that's another creature, it gains the following abilities until end of turn.)\nHaste\nWhenever this creature deals combat damage to an opponent, destroy target artifact that player controls.
|
||||
14
forge-gui/res/cardsfolder/k/kamigawa_charm.txt
Normal file
14
forge-gui/res/cardsfolder/k/kamigawa_charm.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Name:Kamigawa Charm
|
||||
ManaCost:1 G G
|
||||
Types:Sorcery
|
||||
A:SP$ Charm | Choices$ DosansOldestChant,KodamasReach,TimeOfNeed | Defined$ You
|
||||
SVar:DosansOldestChant:DB$ GainLife | LifeAmount$ 6 | SubAbility$ DBDraw | SpellDescription$ You gain 6 life. Draw a card.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ 1
|
||||
SVar:KodamasReach:DB$ ChangeZone | Origin$ Library | Destination$ Library | ChangeType$ Land.Basic | ChangeNum$ 2 | RememberChanged$ True | Reveal$ True | Shuffle$ False | StackDescription$ SpellDescription | SubAbility$ DBChangeZone1 | SpellDescription$ Search your library for up to two basic land cards, reveal those cards, put one onto the battlefield tapped and the other into your hand, then shuffle.
|
||||
SVar:DBChangeZone1:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Land.IsRemembered | ChangeNum$ 1 | Mandatory$ True | NoLooking$ True | SelectPrompt$ Select a card for the battlefield | Tapped$ True | Shuffle$ False | SubAbility$ DBChangeZone2 | StackDescription$ None
|
||||
SVar:DBChangeZone2:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.IsRemembered | Mandatory$ True | NoLooking$ True | SelectPrompt$ Select a card for your hand | StackDescription$ None | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:TimeOfNeed:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Creature.Legendary | ChangeNum$ 1 | SpellDescription$ Search your library for a legendary creature card, reveal it, put it into your hand, then shuffle.
|
||||
DeckHas:Ability$LifeGain
|
||||
DeckHints:Type$Legendary
|
||||
Oracle:Choose one —\n• Dosan's Oldest Chant (Gain 6 life, draw a card.)\n• Kodama's Reach (Search for two basic lands, put one onto the battlefield tapped and one to hand.)\n• Time of Need (Search for a legend.)
|
||||
17
forge-gui/res/cardsfolder/k/kevin_questing_dragon.txt
Normal file
17
forge-gui/res/cardsfolder/k/kevin_questing_dragon.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
Name:Kevin, Questing Dragon
|
||||
ManaCost:4 R R R R
|
||||
Types:Legendary Creature Dragon
|
||||
PT:8/8
|
||||
K:Devour:2
|
||||
K:Flying
|
||||
K:Landwalk:Mountain
|
||||
K:Rampage:2
|
||||
K:Bushido:2
|
||||
K:Trample:Planeswalker
|
||||
R:Event$ Counter | ValidCard$ Card.Self | ValidSA$ Spell | Layer$ CantHappen | Description$ This spell can't be countered.
|
||||
S:Mode$ CantPreventDamage | Description$ Damage can't be prevented.
|
||||
S:Mode$ CantGainLife | ValidPlayer$ Player | Description$ Players can't gain life.
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigControl | TriggerZones$ Battlefield | TriggerDescription$ Whenever NICKNAME deals combat damage to a player, gain control of target land that player controls. Untap it.
|
||||
SVar:TrigControl:DB$ GainControl | ValidTgts$ Land.ControlledBy TriggeredTarget | TgtPrompt$ Select target land damaged player controls | SubAbility$ DBUntap
|
||||
SVar:DBUntap:DB$ Untap | Defined$ Targeted | SpellDescription$ Untap it.
|
||||
Oracle:Kevin can't be countered, devour 2\nFlying, mountainwalk, rampage 2, bushido 2, trample over planeswalkers\nDamage can't be prevented.\nPlayers can't gain life.\nWhenever Kevin deals combat damage to a player, gain control of target land that player controls. Untap it.
|
||||
15
forge-gui/res/cardsfolder/k/knowing_half_the_battle.txt
Normal file
15
forge-gui/res/cardsfolder/k/knowing_half_the_battle.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
Name:Knowing
|
||||
ManaCost:3 U U
|
||||
Types:Sorcery
|
||||
A:SP$ Draw | NumCards$ 3 | SpellDescription$ Draw three cards.
|
||||
AlternateMode:Split
|
||||
Oracle:Draw three cards.
|
||||
|
||||
ALTERNATE
|
||||
|
||||
Name:Half the Battle
|
||||
ManaCost:2 R
|
||||
Types:Sorcery
|
||||
DeckHints:Type$Battle
|
||||
A:SP$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Battle | ChangeNum$ 1 | SpellDescription$ Search your library for a Battle card, reveal it, put it into your hand, then shuffle.
|
||||
Oracle:Search your library for a Battle card, reveal it, put it into your hand, then shuffle.
|
||||
14
forge-gui/res/cardsfolder/k/koma_and_toski_compleated.txt
Normal file
14
forge-gui/res/cardsfolder/k/koma_and_toski_compleated.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Name:Koma and Toski, Compleated
|
||||
ManaCost:6 U G
|
||||
Types:Legendary Creature Phyrexian Serpent Squirrel
|
||||
PT:7/7
|
||||
R:Event$ Counter | ValidCard$ Card.Self | ValidSA$ Spell | Layer$ CantHappen | Description$ This spell can't be countered.
|
||||
T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ At the beginning of each upkeep, create a 1/1 Phyrexian Serpent Squirrel artifact creature token named Toski's Coil with "Whenever this creature deals combat damage to a player, draw a card."
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ toskis_coil | TokenOwner$ You
|
||||
A:AB$ Charm | Cost$ Sac<1/Serpent.Other;Squirrel.Other/another Serpent or Squirrel> | Choices$ DBEffect,DBPump
|
||||
SVar:DBEffect:DB$ Effect | ValidTgts$ Creature | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | StaticAbilities$ MustAttack | SubAbility$ DBConstrict | SpellDescription$ Target creature attacks this turn if able. Its activated abilities can't be activated this turn.
|
||||
SVar:MustAttack:Mode$ MustAttack | ValidCreature$ Card.IsRemembered | Description$ This creature attacks this turn if able.
|
||||
SVar:DBConstrict:DB$ Pump | Defined$ ParentTarget | KW$ HIDDEN CARDNAME's activated abilities can't be activated. | StackDescription$ None
|
||||
SVar:DBPump:DB$ Pump | Defined$ Self | KW$ Indestructible | SpellDescription$ CARDNAME gains indestructible until end of turn.
|
||||
DeckHas:Ability$Token|Sacrifice & Type$Artifact
|
||||
Oracle:This spell can't be countered.\nAt the beginning of each upkeep, create a 1/1 Phyrexian Serpent Squirrel artifact creature token named Toski's Coil with "Whenever this creature deals combat damage to a player, draw a card."\nSacrifice another Serpent or Squirrel: Choose one —\n• Target creature attacks this turn if able. Its activated abilities can't be activated this turn.\n• Koma and Toski, Compleated gains indestructible until end of turn.
|
||||
7
forge-gui/res/cardsfolder/l/leech_medic.txt
Normal file
7
forge-gui/res/cardsfolder/l/leech_medic.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Leech Medic
|
||||
ManaCost:3 W
|
||||
Types:Creature Leech Cleric
|
||||
PT:3/4
|
||||
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigConjure | TriggerDescription$ When CARDNAME enters the battlefield, create a Leeches token card and put it into your hand.
|
||||
SVar:TrigConjure:DB$ MakeCard | Name$ Leeches | TokenCard$ True | Zone$ Hand
|
||||
Oracle:When Leech Medic enters the battlefield, create a Leeches token card and put it into your hand.
|
||||
11
forge-gui/res/cardsfolder/l/life_cloud.txt
Normal file
11
forge-gui/res/cardsfolder/l/life_cloud.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Life Cloud
|
||||
ManaCost:X W W W
|
||||
Types:Sorcery
|
||||
A:SP$ GainLife | Defined$ Player | LifeAmount$ X | SubAbility$ DBDraw | SpellDescription$ Each player gains X life, draws X cards, returns X creatures from their graveyard to the battlefield, then returns X lands from their graveyard to the battlefield.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ X | Defined$ Player | SubAbility$ DBRepeatCreature
|
||||
SVar:DBRepeatCreature:DB$ RepeatEach | RepeatSubAbility$ DBReturnCreature | RepeatPlayers$ Player | SubAbility$ DBRepeatLand
|
||||
SVar:DBReturnCreature:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature.RememberedPlayerCtrl | DefinedPlayer$ Player.IsRemembered | Chooser$ Player.IsRemembered | ChangeNum$ X | Hidden$ True | Mandatory$ True
|
||||
SVar:DBRepeatLand:DB$ RepeatEach | RepeatSubAbility$ DBReturnLand | RepeatPlayers$ Player
|
||||
SVar:DBReturnLand:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Land.RememberedPlayerCtrl | DefinedPlayer$ Player.IsRemembered | Chooser$ Player.IsRemembered | ChangeNum$ X | Hidden$ True | Mandatory$ True
|
||||
SVar:X:Count$xPaid
|
||||
Oracle:Each player gains X life, draws X cards, returns X creatures from their graveyard to the battlefield, then returns X lands from their graveyard to the battlefield.
|
||||
11
forge-gui/res/cardsfolder/l/locus_cobra.txt
Normal file
11
forge-gui/res/cardsfolder/l/locus_cobra.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Locus Cobra
|
||||
ManaCost:1 G
|
||||
Types:Creature Phyrexian Snake
|
||||
PT:1/1
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigMana | TriggerDescription$ Landfall — Whenever a land enters the battlefield under your control, add one mana of any color. If it was a Locus or Sphere land, put a +1/+1 counter on CARDNAME.
|
||||
SVar:TrigMana:DB$ Mana | Produced$ Any | SubAbility$ DBPutCounter
|
||||
SVar:DBPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | ConditionDefined$ TriggeredCard | ConditionPresent$ Locus,Sphere
|
||||
SVar:BuffedBy:Locus,Sphere
|
||||
DeckHas:Ability$Counters
|
||||
DeckHints:Type$Locus|Sphere
|
||||
Oracle:Landfall — Whenever a land enters the battlefield under your control, add one mana of any color. If it was a Locus or Sphere land, put a +1/+1 counter on Locus Cobra.
|
||||
10
forge-gui/res/cardsfolder/l/luxior_and_shadowspear.txt
Normal file
10
forge-gui/res/cardsfolder/l/luxior_and_shadowspear.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Luxior and Shadowspear
|
||||
ManaCost:2
|
||||
Types:Legendary Artifact Equipment
|
||||
K:Equip:3:Planeswalker.YouCtrl:planeswalker
|
||||
K:Equip:3
|
||||
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ X | AddToughness$ X | AddKeyword$ Trample & Lifelink | Description$ Equipped creature gets +1/+1 for each counter on it, and has trample and lifelink.
|
||||
SVar:X:Equipped$CardCounters.ALL
|
||||
S:Mode$ Continuous | Affected$ Permanent.EquippedBy | RemoveType$ Planeswalker | AddType$ Creature | Description$ Equipped permanent isn't a planeswalker and is a creature in addition it its other types. (Loyalty abilities can still be activated.)
|
||||
A:AB$ AnimateAll | Cost$ 1 | ValidCards$ Permanent.OppCtrl | RemoveKeywords$ Hexproof & Indestructible | SpellDescription$ Permanents your opponents control lose hexproof and indestructible until end of turn. | StackDescription$ SpellDescription
|
||||
Oracle:Equipped creature gets +1/+1 for each counter on it, and has trample and lifelink.\n{1}: Permanents your opponents control lose hexproof and indestructible until end of turn.\nEquipped permanent isn't a planeswalker and is a creature in addition it its other types. (Loyalty abilities can still be activated.)\Equip creature or planeswalker {3}
|
||||
8
forge-gui/res/cardsfolder/m/manakin_and_millikin.txt
Normal file
8
forge-gui/res/cardsfolder/m/manakin_and_millikin.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Manakin and Millikin
|
||||
ManaCost:3
|
||||
Types:Artifact Creature Construct
|
||||
PT:1/2
|
||||
A:AB$ Mana | Cost$ T Mill<1> | Produced$ C | Amount$ 2 | SpellDescription$ Add {C}{C}.
|
||||
DeckHas:Ability$Mill
|
||||
DeckHints:Ability$Graveyard
|
||||
Oracle:{T}, Mill a card: Add {C}{C}.
|
||||
10
forge-gui/res/cardsfolder/m/more_of_that_strange_oil.txt
Normal file
10
forge-gui/res/cardsfolder/m/more_of_that_strange_oil.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:More of That Strange Oil...
|
||||
ManaCost:2 U
|
||||
Types:Instant
|
||||
A:SP$ Charm | Choices$ DBProliferate,DBCounter
|
||||
SVar:DBProliferate:DB$ Proliferate | SubAbility$ DBDraw | SpellDescription$ It's Probably Nothing — Proliferate. Draw a card.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ 1
|
||||
SVar:DBCounter:DB$ Counter | TargetType$ Spell | TgtPrompt$ Select target creature, artifact, or planeswalker spell | ValidTgts$ Creature,Artifact,Planeswalker | SubAbility$ DBScry | SpellDescription$ That Could Actually Be Dangerous — Counter target creature, artifact, or planeswalker spell. Scry 1.
|
||||
SVar:DBScry:DB$ Scry | ScryNum$ 1
|
||||
DeckHas:Ability$Proliferate
|
||||
Oracle:Choose one —\n• It's Probably Nothing — Proliferate. Draw a card.\n• That Could Actually Be Dangerous — Counter target creature, artifact, or planeswalker spell. Scry 1.
|
||||
6
forge-gui/res/cardsfolder/m/mox_poison.txt
Normal file
6
forge-gui/res/cardsfolder/m/mox_poison.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Name:Mox Poison
|
||||
ManaCost:0
|
||||
Types:Artifact
|
||||
A:AB$ Mana | Cost$ T | Produced$ Any | SubAbility$ DBPain | SpellDescription$ Add one mana of any color. You get two poison counters
|
||||
SVar:DBPain:DB$ Poison | Defined$ You | Num$ 2
|
||||
Oracle:{T}: Add one mana of any color. You get two poison counters
|
||||
@@ -0,0 +1,10 @@
|
||||
Name:Myojin of Night's Reach Grim Betrayal
|
||||
ManaCost:5 B B B
|
||||
Types:Legendary Creature Spirit
|
||||
PT:10/4
|
||||
K:etbCounter:Indestructible:1:CheckSVar$ FromHand:CARDNAME enters the battlefield with an indestructible counter on it if you cast it from your hand.
|
||||
SVar:FromHand:Count$wasCastFromYourHandByYou.1.0
|
||||
A:AB$ Discard | Cost$ SubCounter<1/Indestructible> | Defined$ Player.Opponent | Mode$ Hand | SubAbility$ DBReturn | SpellDescription$ Each opponent discards their hand. Put onto the battlefield under your control all creature cards in all graveyards that were put there from anywhere this turn.
|
||||
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ ValidGraveyard Creature.ThisTurnEntered | GainControl$ True
|
||||
DeckHas:Ability$Graveyard
|
||||
Oracle:Myojin of Night's Reach Grim Betrayal enters the battlefield with an indestructible counter on it if you cast it from your hand.\nRemove an indestructible counter from Myojin of Night's Reach Grim Betrayal: Each opponent discards their hand. Put onto the battlefield under your control all creature cards in all graveyards that were put there from anywhere this turn.
|
||||
7
forge-gui/res/cardsfolder/m/mysterious_confluence.txt
Normal file
7
forge-gui/res/cardsfolder/m/mysterious_confluence.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Mysterious Confluence
|
||||
ManaCost:5
|
||||
Types:Instant
|
||||
A:SP$ NameCard | AtRandom$ True | ChooseFromList$ Righteous Confluence,Mystic Confluence,Wretched Confluence,Fiery Confluence,Verdant Confluence,Brokers Confluence,Cabaretti Confluence,Maestros Confluence,Obscura Confluence,Riveteers Confluence | SubAbility$ DBCast | StackDescription$ SpellDescription | SpellDescription$ Choose a card at random from among Righteous Confluence, Mystic Confluence, Wretched Confluence, Fiery Confluence, Verdant Confluence, Brokers Confluence, Cabaretti Confluence, Maestros Confluence, Obscura Confluence, and Riveteers Confluence. Create a copy of that card. You may cast the copy without paying its mana cost.
|
||||
SVar:DBCast:DB$ Play | WithoutManaCost$ True | CopyFromChosenName$ True | Optional$ True | SubAbility$ DBCleanup | StackDescription$ None
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearNamedCard$ True
|
||||
Oracle:Choose a card at random from among Righteous Confluence, Mystic Confluence, Wretched Confluence, Fiery Confluence, Verdant Confluence, Brokers Confluence, Cabaretti Confluence, Maestros Confluence, Obscura Confluence, and Riveteers Confluence. Create a copy of that card. You may cast the copy without paying its mana cost.
|
||||
@@ -2,16 +2,15 @@ Name:Nahiri's Lithoforming
|
||||
ManaCost:X R R
|
||||
Types:Sorcery
|
||||
A:SP$ Sacrifice | SacValid$ Land | Amount$ X | RememberSacrificed$ True | SubAbility$ DBDraw | StackDescription$ SpellDescription | SpellDescription$ Sacrifice X lands. For each land sacrificed this way, draw a card. You may play X additional lands this turn. Lands you control enter tapped this turn.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ Y | SubAbility$ DBStoreSVar | StackDescription$ None
|
||||
SVar:DBStoreSVar:DB$ StoreSVar | SVar$ XLands | Type$ CountSVar | Expression$ X | SubAbility$ DBEffect
|
||||
SVar:DBEffect:DB$ Effect | StaticAbilities$ PlayMoreLand | ReplacementEffects$ LandETB | SubAbility$ DBCleanup
|
||||
SVar:PlayMoreLand:Mode$ Continuous | Affected$ You | AdjustLandPlays$ XLands | EffectZone$ Command | Description$ You may play X additional lands this turn.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ Y | SubAbility$ DBEffect | StackDescription$ None
|
||||
SVar:DBEffect:DB$ Effect | SetChosenNumber$ X | StaticAbilities$ PlayMoreLand | ReplacementEffects$ LandETB | SubAbility$ DBCleanup
|
||||
SVar:PlayMoreLand:Mode$ Continuous | Affected$ You | AdjustLandPlays$ Z | EffectZone$ Command | Description$ You may play X additional lands this turn.
|
||||
SVar:LandETB:Event$ Moved | ValidCard$ Land.YouCtrl | Destination$ Battlefield | ReplaceWith$ ETBTapped | ReplacementResult$ Updated | Description$ Lands you control enter tapped this turn.
|
||||
SVar:ETBTapped:DB$ Tap | ETB$ True | Defined$ ReplacedCard
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:X:Count$xPaid
|
||||
SVar:Y:Count$RememberedSize
|
||||
SVar:XLands:Number$0
|
||||
SVar:Z:Count$ChosenNumber
|
||||
DeckHas:Ability$Sacrifice
|
||||
AI:RemoveDeck:All
|
||||
Oracle:Sacrifice X lands. For each land sacrificed this way, draw a card. You may play X additional lands this turn. Lands you control enter tapped this turn.
|
||||
|
||||
15
forge-gui/res/cardsfolder/n/night_out_in_vegas.txt
Normal file
15
forge-gui/res/cardsfolder/n/night_out_in_vegas.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
Name:Night Out in Vegas
|
||||
ManaCost:2 B B
|
||||
Types:Enchantment
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, ABILITY
|
||||
SVar:TrigCharm:DB$ Charm | Choices$ Buffet,SeeAShow,PlayGames,GoToSleep | ChoiceRestriction$ ThisGame | CharmNum$ 1
|
||||
SVar:Buffet:DB$ Token | TokenScript$ c_a_food_sac | TokenAmount$ 3 | SpellDescription$ Buffet — Create three Food tokens.
|
||||
SVar:SeeAShow:DB$ Token | TokenScript$ w_2_2_performer | TokenAmount$ 2 | SpellDescription$ See a Show — Create two 2/2 white Performer creature tokens.
|
||||
SVar:PlayGames:DB$ ChangeZone | Origin$ Library | NoShuffle$ True | Destination$ Hand | ChangeType$ Card | ChangeNum$ 1 | SubAbility$ DBDiscard | Mandatory$ True | SpellDescription$ Play Games — Search your library for a card, put that card into your hand, discard a card at random, then shuffle.
|
||||
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ Random | SubAbility$ DBShuffle
|
||||
SVar:DBShuffle:DB$ Shuffle | Defined$ You
|
||||
SVar:GoToSleep:DB$ LoseLife | LifeAmount$ 15 | SubAbility$ DBSacSelf | SpellDescription$ Go to Sleep — You lose 15 life. Sacrifice CARDNAME.
|
||||
SVar:DBSacSelf:DB$ Sacrifice
|
||||
DeckHas:Ability$Token|Sacrifice|LifeGain & Type$Food|Artifact|Performer
|
||||
AI:RemoveDeck:All
|
||||
Oracle:At the beginning of your upkeep, choose one that hasn't been chosen —\n• Buffet — Create three Food tokens.\n• See a Show — Create two 2/2 white Performer creature tokens.\n• Play Games — Search your library for a card, put that card into your hand, discard a card at random, then shuffle.\n• Go to Sleep — You lose 15 life. Sacrifice Night Out in Vegas.
|
||||
11
forge-gui/res/cardsfolder/n/nim_mongoose.txt
Normal file
11
forge-gui/res/cardsfolder/n/nim_mongoose.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Nim Mongoose
|
||||
ManaCost:B
|
||||
Types:Creature Zombie Mongoose
|
||||
PT:2/1
|
||||
K:Shroud
|
||||
R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplacementResult$ Updated | ReplaceWith$ ETBTapped | Description$ CARDNAME enters the battlefield tapped.
|
||||
SVar:ETBTapped:DB$ Tap | Defined$ Self | ETB$ True
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ X | Condition$ Threshold | Description$ Threshold — CARDNAME gets +1/+0 for each artifact you control as long as seven or more cards are in your graveyard.
|
||||
SVar:X:Count$Valid Artifact.YouCtrl
|
||||
SVar:BuffedBy:Artifact
|
||||
Oracle:Shroud\nNim Mongoose enters the battlefield tapped.\nThreshold — Nim Mongoose gets +1/+0 for each artifact you control as long as seven or more cards are in your graveyard.
|
||||
9
forge-gui/res/cardsfolder/n/norin_and_feldon.txt
Normal file
9
forge-gui/res/cardsfolder/n/norin_and_feldon.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Norin and Feldon
|
||||
ManaCost:1 R
|
||||
Types:Legendary Creature Human Warrior Artificer
|
||||
PT:2/2
|
||||
A:AB$ CopyPermanent | Cost$ 2 R T | SorcerySpeed$ True | TgtZone$ Graveyard | ValidTgts$ Creature.YouOwn | TgtPrompt$ Select target creature card in your graveyard | NumCopies$ 1 | AddTypes$ Artifact | PumpKeywords$ Haste | AddSVars$ NorinExile | AddTriggers$ Norin1,Norin2 | SpellDescription$ Create a token that's a copy of target creature card in your graveyard, except it's an artifact in addition to its other types, gains haste, and has "Whenever a player casts a spell or a creature attacks, exile this token." Activate only as a sorcery.
|
||||
SVar:Norin1:Mode$ SpellCast | ValidCard$ Card | Execute$ NorinExile | TriggerZones$ Battlefield | TriggerDescription$ Whenever a player casts a spell or a creature attacks, exile this token.
|
||||
SVar:Norin2:Mode$ Attacks | ValidCard$ Creature | Execute$ NorinExile | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ Whenever a player casts a spell or a creature attacks, exile this token.
|
||||
SVar:NorinExile:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile
|
||||
Oracle:{2}{R}, {T}: Create a token that's a copy of target creature card in your graveyard, except it's an artifact in addition to its other types, gains haste, and has "Whenever a player casts a spell or a creature attacks, exile this token." Activate only as a sorcery.
|
||||
@@ -3,8 +3,8 @@ ManaCost:3 U
|
||||
Types:Legendary Creature Human Scientist
|
||||
PT:3/4
|
||||
S:Mode$ Continuous | Affected$ You | SetMaxHandSize$ Unlimited | Description$ You have no maximum hand size.
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | OptionalDecider$ You | TriggerDescription$ Sonic Booster — Whenever CARDNAME attacks, sacrifice X artifacts. When you sacrifice one or more artifacts this way, tap up to X target creatures and you draw X cards.
|
||||
SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ Sac<X/Artifact> | Execute$ TrigTap | TriggerDescription$ When you sacrifice one or more artifacts this way, tap up to X target creatures and you draw X cards.
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | OptionalDecider$ You | TriggerDescription$ Sonic Booster — Whenever CARDNAME attacks, sacrifice any number artifacts. When you sacrifice one or more artifacts this way, tap up to that many target creatures and draw that many cards.
|
||||
SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ Sac<X/Artifact> | Execute$ TrigTap | TriggerDescription$ When you sacrifice one or more artifacts this way, tap up to that many target creatures and draw that many cards.
|
||||
SVar:TrigTap:DB$ Tap | TargetMin$ 0 | TargetMax$ X | TgtPrompt$ Select up to X target creatures to tap | ValidTgts$ Creature | SubAbility$ TrigDraw
|
||||
SVar:TrigDraw:DB$ Draw | NumCards$ X
|
||||
SVar:X:Count$xPaid
|
||||
@@ -12,4 +12,4 @@ K:Doctor's companion
|
||||
SVar:HasAttackEffect:TRUE
|
||||
DeckHas:Ability$Sacrifice
|
||||
DeckNeeds:Type$Artifact
|
||||
Oracle:You have no maximum hand size.\nSonic Booster — Whenever Nyssa of Traken attacks, sacrifice X artifacts. When you sacrifice one or more artifacts this way, tap up to X target creatures and you draw X cards.\nDoctor's companion (You can have two commanders if the other is the Doctor.)
|
||||
Oracle:You have no maximum hand size.\nSonic Booster — Whenever Nyssa of Traken attacks, sacrifice any number of artifacts. When you sacrifice one or more artifacts this way, tap up to that many target creatures and draw that many cards.\nDoctor's companion (You can have two commanders if the other is the Doctor.)
|
||||
|
||||
8
forge-gui/res/cardsfolder/o/original_skullclamp.txt
Normal file
8
forge-gui/res/cardsfolder/o/original_skullclamp.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Original Skullclamp
|
||||
ManaCost:1
|
||||
Types:Artifact Equipment
|
||||
K:Equip:1
|
||||
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | Description$ Equipped creature gets +1/+1.
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.EquippedBy | Execute$ TrigDraw | TriggerDescription$ Whenever equipped creature dies, draw two cards.
|
||||
SVar:TrigDraw:DB$ Draw | NumCards$ 2
|
||||
Oracle:Equipped creature gets +1/+1.\nWhenever equipped creature dies, draw two cards.\nEquip {1}
|
||||
@@ -4,5 +4,5 @@ Types:Vanguard
|
||||
HandLifeModifier:+0/+9
|
||||
A:AB$ RepeatEach | Cost$ 3 | ActivationZone$ Command | RepeatPlayers$ Player.Opponent | RepeatSubAbility$ ArrestEach | StackDescription$ SpellDescription | SpellDescription$ For each opponent who controls a creature, create a token that's a copy of a card named Arrest and attach it to a creature that player controls chosen at random.
|
||||
SVar:ArrestEach:DB$ ChooseCard | Amount$ 1 | Choices$ Creature.RememberedPlayerCtrl | AtRandom$ True | SubAbility$ DBAttach
|
||||
SVar:DBAttach:DB$ CopyPermanent | NumCopies$ 1 | ValidSupportedCopy$ Card.namedArrest | DefinedName$ Arrest | AttachAfter$ True | AttachedTo$ ChosenCard | ConditionDefined$ ChosenCard | ConditionPresent$ Creature | ConditionCompare$ GE1
|
||||
SVar:DBAttach:DB$ CopyPermanent | NumCopies$ 1 | DefinedName$ Arrest | AttachAfter$ True | AttachedTo$ ChosenCard | ConditionDefined$ ChosenCard | ConditionPresent$ Creature | ConditionCompare$ GE1
|
||||
Oracle:Hand +0, life +9\n{3}: For each opponent who controls a creature, create a token that's a copy of a card named Arrest and attach it to a creature that player controls chosen at random.
|
||||
|
||||
7
forge-gui/res/cardsfolder/p/phila_unsealed.txt
Normal file
7
forge-gui/res/cardsfolder/p/phila_unsealed.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Phila, Unsealed
|
||||
ManaCost:4
|
||||
Types:Legendary Artifact Creature Rebel Golem
|
||||
PT:4/4
|
||||
S:Mode$ CantPutCounter | ValidPlayer$ You | CounterType$ POISON | Description$ You can't get poison counters.
|
||||
A:AB$ ChangeZone | Cost$ W U B R G | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target artifact.
|
||||
Oracle:You can't get poison counters.\n{W}{U}{B}{R}{G}: Exile target artifact.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user