mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Room: First Spell Part (#6044)
* Room: First Spell ---- Co-authored-by: tool4ever <therealtoolkit@hotmail.com> Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.59> Co-authored-by: TRT <> Co-authored-by: Anthony Calosa <anthonycalosa@gmail.com>
This commit is contained in:
@@ -190,7 +190,12 @@ public class GameAction {
|
||||
// Make sure the card returns from the battlefield as the original card with two halves
|
||||
resetToOriginal = true;
|
||||
}
|
||||
} else if (!zoneTo.is(ZoneType.Stack)) {
|
||||
} else if (zoneTo.is(ZoneType.Battlefield) && c.isRoom()) {
|
||||
if (c.getCastSA() == null) {
|
||||
// need to set as empty room
|
||||
c.updateRooms();
|
||||
}
|
||||
} else if (!zoneTo.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Battlefield)) {
|
||||
// For regular splits, recreate the original state unless the card is going to stack as one half
|
||||
resetToOriginal = true;
|
||||
}
|
||||
@@ -604,6 +609,9 @@ public class GameAction {
|
||||
// CR 603.6b
|
||||
if (toBattlefield) {
|
||||
zoneTo.saveLKI(copied, lastKnownInfo);
|
||||
if (copied.isRoom() && copied.getCastSA() != null) {
|
||||
copied.unlockRoom(copied.getCastSA().getActivatingPlayer(), copied.getCastSA().getCardStateName());
|
||||
}
|
||||
}
|
||||
|
||||
// only now that the LKI preserved it
|
||||
|
||||
@@ -30,6 +30,7 @@ public enum AbilityKey {
|
||||
Blockers("Blockers"),
|
||||
CanReveal("CanReveal"),
|
||||
Card("Card"),
|
||||
CardState("CardState"),
|
||||
Cards("Cards"),
|
||||
CardsFiltered("CardsFiltered"),
|
||||
CardLKI("CardLKI"),
|
||||
|
||||
@@ -194,6 +194,7 @@ public enum ApiType {
|
||||
TwoPiles (TwoPilesEffect.class),
|
||||
Unattach (UnattachEffect.class),
|
||||
UnattachAll (UnattachAllEffect.class),
|
||||
UnlockDoor (UnlockDoorEffect.class),
|
||||
Untap (UntapEffect.class),
|
||||
UntapAll (UntapAllEffect.class),
|
||||
Venture (VentureEffect.class),
|
||||
|
||||
@@ -144,6 +144,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
|
||||
final long ts = game.getNextTimestamp();
|
||||
tgtCard.addCloneState(CardFactory.getCloneStates(cardToCopy, tgtCard, sa), ts);
|
||||
tgtCard.updateRooms();
|
||||
|
||||
// set ETB tapped of clone
|
||||
if (sa.hasParam("IntoPlayTapped")) {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class UnlockDoorEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
|
||||
CardCollection list;
|
||||
|
||||
if (sa.hasParam("Choices")) {
|
||||
Player chooser = activator;
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoose") + " ";
|
||||
|
||||
CardCollection choices = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("Choices"), activator, source, sa);
|
||||
|
||||
Card c = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, Maps.newHashMap());
|
||||
if (c == null) {
|
||||
return;
|
||||
}
|
||||
list = new CardCollection(c);
|
||||
} else {
|
||||
list = getTargetCards(sa);
|
||||
}
|
||||
|
||||
for (Card c : list) {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Object", c);
|
||||
switch (sa.getParamOrDefault("Mode", "ThisDoor")) {
|
||||
case "ThisDoor":
|
||||
c.unlockRoom(activator, sa.getCardStateName());
|
||||
break;
|
||||
case "Unlock":
|
||||
List<CardState> states = c.getLockedRooms().stream().map(stateName -> c.getState(stateName)).collect(Collectors.toList());
|
||||
|
||||
// need to choose Room Name
|
||||
CardState chosen = activator.getController().chooseSingleCardState(sa, states, "Choose Room to unlock", params);
|
||||
if (chosen == null) {
|
||||
continue;
|
||||
}
|
||||
c.unlockRoom(activator, chosen.getStateName());
|
||||
break;
|
||||
case "LockOrUnlock":
|
||||
switch (c.getLockedRooms().size()) {
|
||||
case 0:
|
||||
// no locked, all unlocked, can only lock door
|
||||
List<CardState> unlockStates = c.getUnlockedRooms().stream().map(stateName -> c.getState(stateName)).collect(Collectors.toList());
|
||||
CardState chosenUnlock = activator.getController().chooseSingleCardState(sa, unlockStates, "Choose Room to lock", params);
|
||||
if (chosenUnlock == null) {
|
||||
continue;
|
||||
}
|
||||
c.lockRoom(activator, chosenUnlock.getStateName());
|
||||
break;
|
||||
case 1:
|
||||
// TODO check for Lock vs Unlock first?
|
||||
List<CardState> bothStates = Lists.newArrayList();
|
||||
bothStates.add(c.getState(CardStateName.LeftSplit));
|
||||
bothStates.add(c.getState(CardStateName.RightSplit));
|
||||
CardState chosenBoth = activator.getController().chooseSingleCardState(sa, bothStates, "Choose Room to lock or unlock", params);
|
||||
if (chosenBoth == null) {
|
||||
continue;
|
||||
}
|
||||
if (c.getLockedRooms().contains(chosenBoth.getStateName())) {
|
||||
c.unlockRoom(activator, chosenBoth.getStateName());
|
||||
} else {
|
||||
c.lockRoom(activator, chosenBoth.getStateName());
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
List<CardState> lockStates = c.getLockedRooms().stream().map(stateName -> c.getState(stateName)).collect(Collectors.toList());
|
||||
|
||||
// need to choose Room Name
|
||||
CardState chosenLock = activator.getController().chooseSingleCardState(sa, lockStates, "Choose Room to unlock", params);
|
||||
if (chosenLock == null) {
|
||||
continue;
|
||||
}
|
||||
c.unlockRoom(activator, chosenLock.getStateName());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -215,6 +215,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
|
||||
private boolean plotted;
|
||||
|
||||
private Set<CardStateName> unlockedRooms = EnumSet.noneOf(CardStateName.class);
|
||||
private Map<CardStateName, SpellAbility> unlockAbilities = Maps.newEnumMap(CardStateName.class);
|
||||
|
||||
private boolean specialized;
|
||||
|
||||
private int timesCrewedThisTurn = 0;
|
||||
@@ -444,6 +447,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
if (state == CardStateName.FaceDown) {
|
||||
return getFaceDownState();
|
||||
}
|
||||
if (state == CardStateName.EmptyRoom) {
|
||||
return getEmptyRoomState();
|
||||
}
|
||||
CardCloneStates clStates = getLastClonedState();
|
||||
if (clStates == null) {
|
||||
return getOriginalState(state);
|
||||
@@ -452,7 +458,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
|
||||
public boolean hasState(final CardStateName state) {
|
||||
if (state == CardStateName.FaceDown) {
|
||||
if (state == CardStateName.FaceDown || state == CardStateName.EmptyRoom) {
|
||||
return true;
|
||||
}
|
||||
CardCloneStates clStates = getLastClonedState();
|
||||
@@ -466,6 +472,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
if (state == CardStateName.FaceDown) {
|
||||
return getFaceDownState();
|
||||
}
|
||||
if (state == CardStateName.EmptyRoom) {
|
||||
return getEmptyRoomState();
|
||||
}
|
||||
return states.get(state);
|
||||
}
|
||||
|
||||
@@ -492,7 +501,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
boolean needsTransformAnimation = transform || rollback;
|
||||
// faceDown has higher priority over clone states
|
||||
// while text change states doesn't apply while the card is faceDown
|
||||
if (state != CardStateName.FaceDown) {
|
||||
if (state != CardStateName.FaceDown && state != CardStateName.EmptyRoom) {
|
||||
CardCloneStates cloneStates = getLastClonedState();
|
||||
if (cloneStates != null) {
|
||||
if (!cloneStates.containsKey(state)) {
|
||||
@@ -934,6 +943,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
return alt ? StaticData.instance().getCommonCards().getName(name, true) : name;
|
||||
}
|
||||
|
||||
public final boolean hasNameOverwrite() {
|
||||
return changedCardNames.values().stream().anyMatch(CardChangedName::isOverwrite);
|
||||
}
|
||||
|
||||
public final boolean hasNonLegendaryCreatureNames() {
|
||||
boolean result = false;
|
||||
for (CardChangedName change : this.changedCardNames.values()) {
|
||||
@@ -1045,7 +1058,12 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
|
||||
public final boolean isSplitCard() {
|
||||
return getRules() != null && getRules().getSplitType() == CardSplitType.Split;
|
||||
// Normal Split Cards, these need to return true before Split States are added
|
||||
if (getRules() != null && getRules().getSplitType() == CardSplitType.Split) {
|
||||
return true;
|
||||
};
|
||||
// in case or clones or copies
|
||||
return hasState(CardStateName.LeftSplit);
|
||||
}
|
||||
|
||||
public final boolean isAdventureCard() {
|
||||
@@ -1970,7 +1988,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
public final Integer getChosenNumber() {
|
||||
return chosenNumber;
|
||||
}
|
||||
|
||||
|
||||
public final void setChosenNumber(final int i) { setChosenNumber(i, false); }
|
||||
public final void setChosenNumber(final int i, final boolean secret) {
|
||||
chosenNumber = i;
|
||||
@@ -3126,7 +3144,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
} else if (keyword.startsWith("Entwine") || keyword.startsWith("Madness")
|
||||
|| keyword.startsWith("Miracle") || keyword.startsWith("Recover")
|
||||
|| keyword.startsWith("Escape") || keyword.startsWith("Foretell:")
|
||||
|| keyword.startsWith("Disturb") || keyword.startsWith("Overload")
|
||||
|| keyword.startsWith("Disturb") || keyword.startsWith("Overload")
|
||||
|| keyword.startsWith("Plot")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost cost = new Cost(k[1], false);
|
||||
@@ -5575,6 +5593,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
|
||||
public final boolean isOutlaw() { return getType().isOutlaw(); }
|
||||
|
||||
public final boolean isRoom() { return getType().hasSubtype("Room"); }
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final int compareTo(final Card that) {
|
||||
@@ -5985,9 +6005,21 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
boolean shares = getName(true).equals(name);
|
||||
|
||||
// Split cards has extra logic to check if it does share a name with
|
||||
if (isSplitCard()) {
|
||||
shares |= name.equals(getState(CardStateName.LeftSplit).getName());
|
||||
shares |= name.equals(getState(CardStateName.RightSplit).getName());
|
||||
if (!shares && !hasNameOverwrite()) {
|
||||
if (isInPlay()) {
|
||||
// split cards in play are only rooms
|
||||
for (String door : getUnlockedRoomNames()) {
|
||||
shares |= name.equals(door);
|
||||
}
|
||||
} else { // not on the battlefield
|
||||
if (hasState(CardStateName.LeftSplit)) {
|
||||
shares |= name.equals(getState(CardStateName.LeftSplit).getName());
|
||||
}
|
||||
if (hasState(CardStateName.RightSplit)) {
|
||||
shares |= name.equals(getState(CardStateName.RightSplit).getName());
|
||||
}
|
||||
}
|
||||
// TODO does it need extra check for stack?
|
||||
}
|
||||
|
||||
if (!shares && hasNonLegendaryCreatureNames()) {
|
||||
@@ -6635,7 +6667,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
if (plotted == true && !isLKI()) {
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(this);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.BecomesPlotted, runParams, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7474,6 +7506,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
}
|
||||
|
||||
if (isInPlay() && !isPhasedOut() && player.canCastSorcery()) {
|
||||
if (getCurrentStateName() == CardStateName.RightSplit || getCurrentStateName() == CardStateName.EmptyRoom) {
|
||||
abilities.add(getUnlockAbility(CardStateName.LeftSplit));
|
||||
}
|
||||
if (getCurrentStateName() == CardStateName.LeftSplit || getCurrentStateName() == CardStateName.EmptyRoom) {
|
||||
abilities.add(getUnlockAbility(CardStateName.RightSplit));
|
||||
}
|
||||
}
|
||||
|
||||
if (isInPlay() && isFaceDown() && oState.getType().isCreature() && oState.getManaCost() != null && !oState.getManaCost().isNoCost())
|
||||
{
|
||||
if (isManifested()) {
|
||||
@@ -8075,4 +8116,107 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
return StaticAbilityWitherDamage.isWitherDamage(this);
|
||||
}
|
||||
|
||||
public Set<CardStateName> getUnlockedRooms() {
|
||||
return this.unlockedRooms;
|
||||
}
|
||||
public void setUnlockedRooms(Set<CardStateName> set) {
|
||||
this.unlockedRooms = set;
|
||||
}
|
||||
|
||||
public List<String> getUnlockedRoomNames() {
|
||||
List<String> result = Lists.newArrayList();
|
||||
for (CardStateName stateName : unlockedRooms) {
|
||||
if (this.hasState(stateName)) {
|
||||
result.add(this.getState(stateName).getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Set<CardStateName> getLockedRooms() {
|
||||
Set<CardStateName> result = Sets.newHashSet(CardStateName.LeftSplit, CardStateName.RightSplit);
|
||||
result.removeAll(this.unlockedRooms);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<String> getLockedRoomNames() {
|
||||
List<String> result = Lists.newArrayList();
|
||||
for (CardStateName stateName : getLockedRooms()) {
|
||||
if (this.hasState(stateName)) {
|
||||
result.add(this.getState(stateName).getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean unlockRoom(Player p, CardStateName stateName) {
|
||||
if (unlockedRooms.contains(stateName) || (stateName != CardStateName.LeftSplit && stateName != CardStateName.RightSplit)) {
|
||||
return false;
|
||||
}
|
||||
unlockedRooms.add(stateName);
|
||||
|
||||
updateRooms();
|
||||
|
||||
Map<AbilityKey, Object> unlockParams = AbilityKey.mapFromPlayer(p);
|
||||
unlockParams.put(AbilityKey.Card, this);
|
||||
unlockParams.put(AbilityKey.CardState, getState(stateName));
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.UnlockDoor, unlockParams, true);
|
||||
|
||||
// fully unlock
|
||||
if (unlockedRooms.size() > 1) {
|
||||
Map<AbilityKey, Object> fullyUnlockParams = AbilityKey.mapFromPlayer(p);
|
||||
fullyUnlockParams.put(AbilityKey.Card, this);
|
||||
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.FullyUnlock, fullyUnlockParams, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean lockRoom(Player p, CardStateName stateName) {
|
||||
if (!unlockedRooms.contains(stateName) || (stateName != CardStateName.LeftSplit && stateName != CardStateName.RightSplit)) {
|
||||
return false;
|
||||
}
|
||||
unlockedRooms.remove(stateName);
|
||||
|
||||
updateRooms();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void updateRooms() {
|
||||
if (!this.isRoom()) {
|
||||
return;
|
||||
}
|
||||
if (this.isFaceDown()) {
|
||||
return;
|
||||
}
|
||||
if (unlockedRooms.isEmpty()) {
|
||||
this.setState(CardStateName.EmptyRoom, true);
|
||||
} else if (unlockedRooms.size() > 1) {
|
||||
this.setState(CardStateName.Original, true);
|
||||
} else { // we already know the set is only one
|
||||
for (CardStateName name : unlockedRooms) {
|
||||
this.setState(name, true);
|
||||
}
|
||||
}
|
||||
// update trigger after state change
|
||||
getGame().getTriggerHandler().clearActiveTriggers(this, null);
|
||||
getGame().getTriggerHandler().registerActiveTrigger(this, false);
|
||||
}
|
||||
|
||||
public CardState getEmptyRoomState() {
|
||||
if (!states.containsKey(CardStateName.EmptyRoom)) {
|
||||
states.put(CardStateName.EmptyRoom, CardUtil.getEmptyRoomCharacteristic(this));
|
||||
}
|
||||
return states.get(CardStateName.EmptyRoom);
|
||||
}
|
||||
|
||||
public SpellAbility getUnlockAbility(CardStateName state) {
|
||||
if (!unlockAbilities.containsKey(state)) {
|
||||
unlockAbilities.put(state, CardFactoryUtil.abilityUnlockRoom(getState(state)));
|
||||
}
|
||||
return unlockAbilities.get(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,6 +258,16 @@ public class CardFactory {
|
||||
final CardState original = card.getState(CardStateName.Original);
|
||||
original.addNonManaAbilities(card.getCurrentState().getNonManaAbilities());
|
||||
original.addIntrinsicKeywords(card.getCurrentState().getIntrinsicKeywords()); // Copy 'Fuse' to original side
|
||||
for (Trigger t : card.getCurrentState().getTriggers()) {
|
||||
if (t.isIntrinsic()) {
|
||||
original.addTrigger(t.copy(card, false));
|
||||
}
|
||||
}
|
||||
for (StaticAbility st : card.getCurrentState().getStaticAbilities()) {
|
||||
if (st.isIntrinsic()) {
|
||||
original.addStaticAbility(st.copy(card, false));
|
||||
}
|
||||
}
|
||||
original.getSVars().putAll(card.getCurrentState().getSVars()); // Unfortunately need to copy these to (Effect looks for sVars on execute)
|
||||
} else if (state != CardStateName.Original) {
|
||||
CardFactoryUtil.setupKeywordedAbilities(card);
|
||||
@@ -415,7 +425,11 @@ public class CardFactory {
|
||||
c.setAttractionLights(face.getAttractionLights());
|
||||
|
||||
// SpellPermanent only for Original State
|
||||
if (c.getCurrentStateName() == CardStateName.Original || c.getCurrentStateName() == CardStateName.Modal || c.getCurrentStateName().toString().startsWith("Specialize")) {
|
||||
if (c.getCurrentStateName() == CardStateName.Original ||
|
||||
c.getCurrentStateName() == CardStateName.LeftSplit ||
|
||||
c.getCurrentStateName() == CardStateName.RightSplit ||
|
||||
c.getCurrentStateName() == CardStateName.Modal ||
|
||||
c.getCurrentStateName().toString().startsWith("Specialize")) {
|
||||
if (c.isLand()) {
|
||||
SpellAbility sa = new LandAbility(c);
|
||||
sa.setCardState(c.getCurrentState());
|
||||
|
||||
@@ -119,6 +119,12 @@ public class CardFactoryUtil {
|
||||
return morphDown;
|
||||
}
|
||||
|
||||
public static SpellAbility abilityUnlockRoom(CardState cardState) {
|
||||
String unlockStr = "ST$ UnlockDoor | Cost$ " + cardState.getManaCost().getShortString() + " | Unlock$ True | SpellDescription$ Unlock " + cardState.getName();
|
||||
|
||||
return AbilityFactory.getAbility(unlockStr, cardState);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* abilityMorphUp.
|
||||
|
||||
@@ -215,6 +215,24 @@ public final class CardUtil {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static CardState getEmptyRoomCharacteristic(Card c) {
|
||||
return getEmptyRoomCharacteristic(c, CardStateName.EmptyRoom);
|
||||
}
|
||||
public static CardState getEmptyRoomCharacteristic(Card c, CardStateName state) {
|
||||
final CardType type = new CardType(false);
|
||||
type.add("Enchantment");
|
||||
type.add("Room");
|
||||
final CardState ret = new CardState(c, state);
|
||||
|
||||
ret.setName("");
|
||||
ret.setType(type);
|
||||
|
||||
// find new image key for empty room
|
||||
ret.setImageKey(c.getImageKey());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// a nice entry point with minimum parameters
|
||||
public static Set<String> getReflectableManaColors(final SpellAbility sa) {
|
||||
return getReflectableManaColors(sa, sa, Sets.newHashSet(), new CardCollection());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.game.card;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import forge.ImageKeys;
|
||||
import forge.StaticData;
|
||||
@@ -40,6 +41,14 @@ public class CardView extends GameEntityView {
|
||||
return s == null ? null : s.getView();
|
||||
}
|
||||
|
||||
public static Map<CardStateView, CardState> getStateMap(Iterable<CardState> states) {
|
||||
Map<CardStateView, CardState> stateViewCache = Maps.newLinkedHashMap();
|
||||
for (CardState state : states) {
|
||||
stateViewCache.put(state.getView(), state);
|
||||
}
|
||||
return stateViewCache;
|
||||
}
|
||||
|
||||
public CardView getBackup() {
|
||||
if (get(TrackableProperty.PaperCardBackup) == null)
|
||||
return null;
|
||||
@@ -795,7 +804,7 @@ public class CardView extends GameEntityView {
|
||||
sb.append(getOwner().getCommanderInfo(this)).append("\r\n");
|
||||
}
|
||||
|
||||
if (isSplitCard() && !isFaceDown() && getZone() != ZoneType.Stack) {
|
||||
if (isSplitCard() && !isFaceDown() && getZone() != ZoneType.Stack && getZone() != ZoneType.Battlefield) {
|
||||
sb.append("(").append(getLeftSplitState().getName()).append(") ");
|
||||
sb.append(getLeftSplitState().getAbilityText());
|
||||
sb.append("\r\n\r\n").append("(").append(getRightSplitState().getName()).append(") ");
|
||||
@@ -1183,6 +1192,11 @@ public class CardView extends GameEntityView {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getId(), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (getName() + " (" + getDisplayId() + ")").trim();
|
||||
|
||||
@@ -64,6 +64,8 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -3954,4 +3956,12 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
Map.Entry<Long, Player> e = declaresBlockers.lastEntry();
|
||||
return e == null ? null : e.getValue();
|
||||
}
|
||||
|
||||
public List<String> getUnlockedDoors() {
|
||||
return StreamSupport.stream(getCardsIn(ZoneType.Battlefield).spliterator(), false)
|
||||
.filter(Card::isRoom)
|
||||
.map(Card::getUnlockedRoomNames)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +243,8 @@ public abstract class PlayerController {
|
||||
public abstract byte chooseColorAllowColorless(String message, Card c, ColorSet colors);
|
||||
|
||||
public abstract ICardFace chooseSingleCardFace(SpellAbility sa, String message, Predicate<ICardFace> cpp, String name);
|
||||
public abstract ICardFace chooseSingleCardFace(SpellAbility sa, List<ICardFace> faces, String message);
|
||||
public abstract CardState chooseSingleCardState(SpellAbility sa, List<CardState> states, String message, Map<String, Object> params);
|
||||
public abstract List<String> chooseColors(String message, SpellAbility sa, int min, int max, List<String> options);
|
||||
|
||||
public abstract CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, String prompt, Map<String, Object> params);
|
||||
|
||||
@@ -144,6 +144,7 @@ public enum TriggerType {
|
||||
TurnBegin(TriggerTurnBegin.class),
|
||||
TurnFaceUp(TriggerTurnFaceUp.class),
|
||||
Unattach(TriggerUnattach.class),
|
||||
UnlockDoor(TriggerUnlockDoor.class),
|
||||
UntapAll(TriggerUntapAll.class),
|
||||
Untaps(TriggerUntaps.class),
|
||||
VisitAttraction(TriggerVisitAttraction.class),
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package forge.game.trigger;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class TriggerUnlockDoor extends Trigger {
|
||||
|
||||
public TriggerUnlockDoor(final Map<String, String> params, final Card host, final boolean intrinsic) {
|
||||
super(params, host, intrinsic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasParam("ThisDoor")) {
|
||||
CardState state = (CardState) runParams.get(AbilityKey.CardState);
|
||||
// This Card
|
||||
if (!getHostCard().equals(state.getCard())) {
|
||||
return false;
|
||||
}
|
||||
// This Face
|
||||
if (!getCardStateName().equals(state.getStateName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImportantStackObjects(SpellAbility sa) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(Localizer.getInstance().getMessage("lblPlayer")).append(": ").append(sa.getTriggeringObject(AbilityKey.Player));
|
||||
sb.append(", ").append(Localizer.getInstance().getMessage("lblCard")).append(": ").append(sa.getTriggeringObject(AbilityKey.Card));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public abstract class TrackableObject implements IIdentifiable, Serializable {
|
||||
@Override
|
||||
public final boolean equals(final Object o) {
|
||||
if (o == null) { return false; }
|
||||
return o.hashCode() == id && o.getClass().equals(getClass());
|
||||
return o.hashCode() == hashCode() && o.getClass().equals(getClass());
|
||||
}
|
||||
|
||||
// don't know if this is really needed, but don't know a better way
|
||||
|
||||
Reference in New Issue
Block a user