mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 04:08:01 +00:00
*Added Haunt keyword
*Added Absolver Thrull Belfry Spirit Benediction of Moons Blind Hunter Cry of Contrition Exhumer Thrull Graven Dominator Orzhov Euthanist Seize the Soul
This commit is contained in:
@@ -184,6 +184,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
private Map<Counters, Integer> counters = new TreeMap<Counters, Integer>();
|
||||
private Map<String, String> sVars = new TreeMap<String, String>();
|
||||
private static String[] storableSVars = {"ChosenX"};
|
||||
|
||||
private ArrayList<Card> hauntedBy = new ArrayList<Card>();
|
||||
private Card haunting = null;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -1615,7 +1618,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
sbLong.append(" (When this enters the battlefield, sacrifice it unless you exile another ");
|
||||
sbLong.append(types);
|
||||
sbLong.append(" you control. When this leaves the battlefield, that card returns to the battlefield.)\r\n");
|
||||
} else if (keyword.get(i).endsWith(".")) {
|
||||
} else if (keyword.get(i).endsWith(".") && !keyword.get(i).startsWith("Haunt")) {
|
||||
sbLong.append(keyword.get(i).toString()).append("\r\n");
|
||||
} else if (keyword.get(i).contains("At the beginning of your upkeep, ")
|
||||
&& keyword.get(i).contains(" unless you pay"))
|
||||
@@ -1645,6 +1648,17 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
sbLong.append(" +1/+1 counters on it. When it's put into a graveyard, you may put its +1/+1 counters on target artifact creature.)");
|
||||
} else if (keyword.get(i).startsWith("MayEffectFromOpeningHand")) {
|
||||
continue;
|
||||
} else if (keyword.get(i).contains("Haunt")) {
|
||||
sb.append("\r\nHaunt (");
|
||||
if(isCreature()) {
|
||||
sb.append("When this creature dies, exile it haunting target creature.");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("When this spell card is put into a graveyard after resolving, exile it haunting target creature.");
|
||||
}
|
||||
sb.append(")");
|
||||
continue;
|
||||
} else {
|
||||
if (i != 0 && sb.length() != 0) {
|
||||
sb.append(", ");
|
||||
@@ -1760,10 +1774,31 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
sb.append(")\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(String keyw : kw) {
|
||||
if(keyw.startsWith("Haunt")) {
|
||||
if (sb.toString().endsWith("\r\n\r\n")) {
|
||||
sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n") + 3);
|
||||
}
|
||||
sb.append("Haunt (");
|
||||
if(isCreature()) {
|
||||
sb.append("When this creature dies, exile it haunting target creature.");
|
||||
}
|
||||
else {
|
||||
sb.append("When this spell card is put into a graveyard after resolving, exile it haunting target creature.");
|
||||
}
|
||||
sb.append(")\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(haunting != null) {
|
||||
sb.append("Haunting: ").append(haunting);
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
while (sb.toString().endsWith("\r\n")) {
|
||||
sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n") + 3);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString().replaceAll("CARDNAME", getName());
|
||||
}
|
||||
@@ -1870,6 +1905,20 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
sb.append("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(hauntedBy.size() != 0) {
|
||||
sb.append("Haunted by: ");
|
||||
for(Card c : hauntedBy) {
|
||||
sb.append(c).append(",");
|
||||
}
|
||||
sb.deleteCharAt(sb.length()-1);
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
if(haunting != null) {
|
||||
sb.append("Haunting: ").append(haunting);
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
/*
|
||||
sb.append("\r\nOwner: ").append(owner).append("\r\n");
|
||||
@@ -4957,6 +5006,8 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
if (!equippedBy.contains(source)) return false;
|
||||
} else if (Property.startsWith("Equipped")) {
|
||||
if (!equipping.contains(source)) return false;
|
||||
} else if (Property.startsWith("HauntedBy")) {
|
||||
if (!hauntedBy.contains(source)) return false;
|
||||
} else if (Property.startsWith("Above")){ // "Are Above" Source
|
||||
CardList list = this.getOwner().getCardsIn(Zone.Graveyard);
|
||||
if (!list.getAbove(source, this))
|
||||
@@ -6129,5 +6180,28 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
public final void setFoil(final int f) {
|
||||
sVars.put("Foil", Integer.toString(f));
|
||||
}
|
||||
|
||||
public final void addHauntedBy(final Card c) {
|
||||
hauntedBy.add(c);
|
||||
if(c != null) {
|
||||
c.setHaunting(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final ArrayList<Card> getHauntedBy() {
|
||||
return hauntedBy;
|
||||
}
|
||||
|
||||
public final void removeHauntedBy(final Card c) {
|
||||
hauntedBy.remove(c);
|
||||
}
|
||||
|
||||
public final Card getHaunting() {
|
||||
return haunting;
|
||||
}
|
||||
|
||||
public final void setHaunting(final Card c) {
|
||||
haunting = c;
|
||||
}
|
||||
|
||||
} //end Card class
|
||||
|
||||
@@ -692,6 +692,10 @@ public final class CardUtil {
|
||||
res.setChangedCardTypes(c.getChangedCardTypes());
|
||||
res.setNewPT(c.getNewPT());
|
||||
res.setReceivedDamageFromThisTurn(c.getReceivedDamageFromThisTurn());
|
||||
res.setHaunting(c.getHaunting());
|
||||
for(Card haunter : c.getHauntedBy()) {
|
||||
res.addHauntedBy(haunter);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -820,7 +820,7 @@ public class MagicStack extends MyObservable {
|
||||
SpellAbility sa = AllZone.getStack().pop();
|
||||
|
||||
AllZone.getPhase().resetPriority(); // ActivePlayer gains priority first after Resolve
|
||||
Card source = sa.getSourceCard();
|
||||
final Card source = sa.getSourceCard();
|
||||
|
||||
if (hasFizzled(sa, source)) { //Fizzle
|
||||
// TODO: Spell fizzles, what's the best way to alert player?
|
||||
@@ -833,6 +833,67 @@ public class MagicStack extends MyObservable {
|
||||
sa.resolve();
|
||||
finishResolving(sa, false);
|
||||
}
|
||||
|
||||
if(source.hasStartOfKeyword("Haunt") && !source.isCreature() && AllZone.getZoneOf(source).is(Constant.Zone.Graveyard)) {
|
||||
CardList creats = AllZoneUtil.getCreaturesInPlay();
|
||||
if(creats.size() != 0)
|
||||
{
|
||||
final Ability haunterDies_Work = new Ability(source,"0") {
|
||||
@Override
|
||||
public void resolve() {
|
||||
AllZone.getGameAction().exile(source);
|
||||
getTargetCard().addHauntedBy(source);
|
||||
}
|
||||
};
|
||||
haunterDies_Work.setDescription("");
|
||||
|
||||
final Input target = new Input() {
|
||||
private static final long serialVersionUID = 1981791992623774490L;
|
||||
|
||||
@Override
|
||||
public void showMessage() {
|
||||
AllZone.getDisplay().showMessage("Choose target creature to haunt.");
|
||||
ButtonUtil.disableAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectCard(final Card c, final PlayerZone zone) {
|
||||
if(!zone.is(Constant.Zone.Battlefield)) {
|
||||
return;
|
||||
}
|
||||
if(CardFactoryUtil.canTarget(source,c))
|
||||
{
|
||||
haunterDies_Work.setTargetCard(c);
|
||||
add(haunterDies_Work);
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
AllZone.getDisplay().showMessage("Cannot target this card (Shroud? Protection?).");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(source.getController().isHuman())
|
||||
{
|
||||
AllZone.getInputControl().setInput(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
//AI choosing what to haunt
|
||||
CardList oppCreats = creats.getController(AllZone.getHumanPlayer());
|
||||
if(oppCreats.size() != 0)
|
||||
{
|
||||
haunterDies_Work.setTargetCard(CardFactoryUtil.AI_getWorstCreature(oppCreats));
|
||||
}
|
||||
else
|
||||
{
|
||||
haunterDies_Work.setTargetCard(CardFactoryUtil.AI_getWorstCreature(creats));
|
||||
}
|
||||
add(haunterDies_Work);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package forge.card.cardFactory;
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import forge.*;
|
||||
import forge.Constant.Zone;
|
||||
import forge.card.abilityFactory.AbilityFactory;
|
||||
import forge.card.cost.Cost;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.spellability.*;
|
||||
@@ -4770,6 +4771,137 @@ public class CardFactoryUtil {
|
||||
card.addSpellAbility(altCostSA);
|
||||
}
|
||||
}
|
||||
|
||||
if(card.hasStartOfKeyword("Haunt")) {
|
||||
int hauntPos = card.getKeywordPosition("Haunt");
|
||||
String[] splitKeyword = card.getKeyword().get(hauntPos).split(":");
|
||||
String hauntSVarName = splitKeyword[1];
|
||||
String abilityDescription = splitKeyword[2];
|
||||
String hauntAbilityDescription = abilityDescription.substring(0,1).toLowerCase() + abilityDescription.substring(1);
|
||||
String hauntDescription;
|
||||
if(card.isCreature())
|
||||
{
|
||||
hauntDescription = "When " + card.getName() + " enters the battlefield or the creature it haunts dies, " + hauntAbilityDescription;
|
||||
}
|
||||
else
|
||||
{
|
||||
hauntDescription = "When the creature " + card.getName() + " haunts dies, " + hauntAbilityDescription;
|
||||
}
|
||||
|
||||
|
||||
card.getKeyword().remove(hauntPos);
|
||||
|
||||
//First, create trigger that runs when the haunter dies (if it's a creature)
|
||||
Trigger haunterDies = forge.card.trigger.TriggerHandler.parseTrigger("Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Static$ True | Secondary$ True | TriggerDescription$ Blank", card, true);
|
||||
|
||||
final Ability haunterDies_Work = new Ability(card,"0") {
|
||||
@Override
|
||||
public void resolve() {
|
||||
getTargetCard().addHauntedBy(card);
|
||||
AllZone.getGameAction().exile(card);
|
||||
}
|
||||
};
|
||||
haunterDies_Work.setDescription(hauntDescription);
|
||||
|
||||
final Input target = new Input() {
|
||||
private static final long serialVersionUID = 1981791992623774490L;
|
||||
|
||||
@Override
|
||||
public void showMessage() {
|
||||
AllZone.getDisplay().showMessage("Choose target creature to haunt.");
|
||||
ButtonUtil.disableAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectCard(final Card c, final PlayerZone zone) {
|
||||
if(!zone.is(Constant.Zone.Battlefield)) {
|
||||
return;
|
||||
}
|
||||
if(canTarget(card,c))
|
||||
{
|
||||
haunterDies_Work.setTargetCard(c);
|
||||
AllZone.getStack().add(haunterDies_Work);
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
AllZone.getDisplay().showMessage("Cannot target this card (Shroud? Protection?).");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ability haunterDies_Setup = new Ability(card,"0") {
|
||||
@Override
|
||||
public void resolve() {
|
||||
CardList creats = AllZoneUtil.getCreaturesInPlay();
|
||||
|
||||
if(creats.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//need to do it this way because I don't know quite how to make TriggerHandler respect BeforePayMana.
|
||||
if(card.getController().isHuman())
|
||||
{
|
||||
AllZone.getInputControl().setInput(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
//AI choosing what to haunt
|
||||
CardList oppCreats = creats.getController(AllZone.getHumanPlayer());
|
||||
if(oppCreats.size() != 0)
|
||||
{
|
||||
haunterDies_Work.setTargetCard(CardFactoryUtil.AI_getWorstCreature(oppCreats));
|
||||
}
|
||||
else
|
||||
{
|
||||
haunterDies_Work.setTargetCard(CardFactoryUtil.AI_getWorstCreature(creats));
|
||||
}
|
||||
AllZone.getStack().add(haunterDies_Work);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
haunterDies.setOverridingAbility(haunterDies_Setup);
|
||||
|
||||
//Second, create the trigger that runs when the haunted creature dies
|
||||
Trigger hauntedDies = forge.card.trigger.TriggerHandler.parseTrigger("Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.HauntedBy | Execute$ " + hauntSVarName + " | TriggerDescription$ " + hauntDescription, card, true);
|
||||
|
||||
//Third, create the trigger that runs when the haunting creature enters the battlefield
|
||||
Trigger haunterETB = forge.card.trigger.TriggerHandler.parseTrigger("Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ " + hauntSVarName + " | Secondary$ True | TriggerDescription$ " + hauntDescription, card, true);
|
||||
|
||||
//Fourth, create a trigger that removes the haunting status if the haunter leaves the exile
|
||||
Trigger haunterUnExiled = forge.card.trigger.TriggerHandler.parseTrigger("Mode$ ChangesZone | Origin$ Exile | ValidCard$ Card.Self | Static$ True | Secondary$ True | TriggerDescription$ Blank", card, true);
|
||||
|
||||
Ability haunterUnExiled_Work = new Ability(card,"0") {
|
||||
@Override
|
||||
public void resolve() {
|
||||
if(card.getHaunting() != null)
|
||||
{
|
||||
card.getHaunting().removeHauntedBy(card);
|
||||
card.setHaunting(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
haunterUnExiled.setOverridingAbility(haunterUnExiled_Work);
|
||||
|
||||
//Fifth, add all triggers and abilities to the card.
|
||||
if(card.isCreature()) {
|
||||
card.addTrigger(haunterETB);
|
||||
card.addTrigger(haunterDies);
|
||||
}
|
||||
else {
|
||||
AbilityFactory af = new AbilityFactory();
|
||||
String abString = card.getSVar(hauntSVarName).replace("AB$", "SP$").replace("Cost$ 0", "Cost$ " + card.getManaCost()) + " | SpellDescription$ " + abilityDescription;
|
||||
|
||||
SpellAbility sa = af.getAbility(abString, card);
|
||||
card.addSpellAbility(sa);
|
||||
}
|
||||
|
||||
card.addTrigger(hauntedDies);
|
||||
card.addTrigger(haunterUnExiled);
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user