mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
Merge branch 'partnerWith' into 'master'
Partner with See merge request core-developers/forge!596
This commit is contained in:
@@ -152,7 +152,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
return doReturnCommanderLogic(sa, aiPlayer);
|
||||
}
|
||||
|
||||
if ("IfNotBuffed".equals(sa.getParam("AILogic"))) {
|
||||
if ("Always".equals(sa.getParam("AILogic"))) {
|
||||
return true;
|
||||
} else if ("IfNotBuffed".equals(sa.getParam("AILogic"))) {
|
||||
if (ComputerUtilCard.isUselessCreature(aiPlayer, sa.getHostCard())) {
|
||||
return true; // debuffed by opponent's auras to the level that it becomes useless
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
private CardAiHints aiHints;
|
||||
private ColorSet colorIdentity;
|
||||
private String meldWith;
|
||||
private String partnerWith;
|
||||
|
||||
private CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
|
||||
splitType = altMode;
|
||||
@@ -48,6 +49,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
otherPart = faces[1];
|
||||
aiHints = cah;
|
||||
meldWith = "";
|
||||
partnerWith = "";
|
||||
|
||||
//calculate color identity
|
||||
byte colMask = calculateColorIdentity(mainPart);
|
||||
@@ -68,6 +70,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
aiHints = newRules.aiHints;
|
||||
colorIdentity = newRules.colorIdentity;
|
||||
meldWith = newRules.meldWith;
|
||||
partnerWith = newRules.partnerWith;
|
||||
}
|
||||
|
||||
private static byte calculateColorIdentity(final ICardFace face) {
|
||||
@@ -204,7 +207,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
}
|
||||
|
||||
public boolean canBePartnerCommander() {
|
||||
return canBeCommander() && Iterables.contains(mainPart.getKeywords(), "Partner");
|
||||
return canBeCommander() && (hasKeyword("Partner") || !this.partnerWith.isEmpty());
|
||||
}
|
||||
|
||||
public boolean canBeBrawlCommander() {
|
||||
@@ -216,6 +219,10 @@ public final class CardRules implements ICardCharacteristics {
|
||||
return meldWith;
|
||||
}
|
||||
|
||||
public String getParterWith() {
|
||||
return partnerWith;
|
||||
}
|
||||
|
||||
// vanguard card fields, they don't use sides.
|
||||
private int deltaHand;
|
||||
private int deltaLife;
|
||||
@@ -262,6 +269,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
private int curFace = 0;
|
||||
private CardSplitType altMode = CardSplitType.None;
|
||||
private String meldWith = "";
|
||||
private String partnerWith = "";
|
||||
private String handLife = null;
|
||||
private String normalizedName = "";
|
||||
|
||||
@@ -291,6 +299,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
this.hints = null;
|
||||
this.has = null;
|
||||
this.meldWith = "";
|
||||
this.partnerWith = "";
|
||||
this.normalizedName = "";
|
||||
}
|
||||
|
||||
@@ -307,6 +316,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
|
||||
result.setNormalizedName(this.normalizedName);
|
||||
result.meldWith = this.meldWith;
|
||||
result.partnerWith = this.partnerWith;
|
||||
result.setDlUrls(pictureUrl);
|
||||
if (StringUtils.isNotBlank(handLife))
|
||||
result.setVanguardProperties(handLife);
|
||||
@@ -382,6 +392,9 @@ public final class CardRules implements ICardCharacteristics {
|
||||
case 'K':
|
||||
if ("K".equals(key)) {
|
||||
this.faces[this.curFace].addKeyword(value);
|
||||
if (value.startsWith("Partner:")) {
|
||||
this.partnerWith = value.split(":")[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -533,4 +546,8 @@ public final class CardRules implements ICardCharacteristics {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean hasKeyword(final String k) {
|
||||
return Iterables.contains(mainPart.getKeywords(), k);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.util.ComparableOp;
|
||||
import forge.util.PredicateString;
|
||||
import forge.util.PredicateString.StringOp;
|
||||
|
||||
/**
|
||||
* Filtering conditions specific for CardRules class, defined here along with
|
||||
@@ -558,6 +557,19 @@ public final class CardRulesPredicates {
|
||||
}
|
||||
};
|
||||
|
||||
public static final Predicate<CardRules> CAN_BE_COMMANDER = new Predicate<CardRules>() {
|
||||
@Override
|
||||
public boolean apply(final CardRules subject) {
|
||||
return subject.canBeCommander();
|
||||
}
|
||||
};
|
||||
public static final Predicate<CardRules> CAN_BE_PARTNER_COMMANDER = new Predicate<CardRules>() {
|
||||
@Override
|
||||
public boolean apply(final CardRules subject) {
|
||||
return subject.canBePartnerCommander();
|
||||
}
|
||||
};
|
||||
|
||||
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker);
|
||||
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardType.CoreType.Instant);
|
||||
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardType.CoreType.Sorcery);
|
||||
@@ -570,13 +582,10 @@ public final class CardRulesPredicates {
|
||||
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
|
||||
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
||||
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(Presets.IS_CREATURE, Presets.IS_LAND));
|
||||
public static final Predicate<CardRules> CAN_BE_COMMANDER = Predicates.or(CardRulesPredicates.rules(StringOp.CONTAINS_IC, "can be your commander"),
|
||||
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
|
||||
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
|
||||
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
|
||||
|
||||
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Predicate<CardRules> IS_NONCREATURE_SPELL_FOR_GENERATOR = com.google.common.base.Predicates
|
||||
.or(Presets.IS_SORCERY, Presets.IS_INSTANT, Presets.IS_PLANESWALKER, Presets.IS_ENCHANTMENT,
|
||||
Predicates.and(Presets.IS_ARTIFACT, Predicates.not(Presets.IS_CREATURE)));
|
||||
|
||||
@@ -248,41 +248,33 @@ public enum DeckFormat {
|
||||
return "too many commanders";
|
||||
}
|
||||
|
||||
// Bring values up to 100
|
||||
min++;
|
||||
max++;
|
||||
|
||||
byte cmdCI = 0;
|
||||
Boolean hasPartner = null;
|
||||
for (PaperCard pc : commanders) {
|
||||
// For each commander decrement size by 1 (99 for 1, 98 for 2)
|
||||
min--;
|
||||
max--;
|
||||
|
||||
if (!isLegalCommander(pc.getRules())) {
|
||||
return "has an illegal commander";
|
||||
}
|
||||
|
||||
if (hasPartner != null && !hasPartner) {
|
||||
return "has an illegal commander partnership";
|
||||
}
|
||||
|
||||
boolean isPartner = false;
|
||||
for(String s : pc.getRules().getMainPart().getKeywords()) {
|
||||
if (s.equals("Partner")) {
|
||||
isPartner = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasPartner == null) {
|
||||
hasPartner = isPartner;
|
||||
} else if (!isPartner) {
|
||||
return "has an illegal commander partnership";
|
||||
}
|
||||
|
||||
cmdCI |= pc.getRules().getColorIdentity().getColor();
|
||||
}
|
||||
|
||||
// special check for Partner
|
||||
if (commanders.size() == 2) {
|
||||
// two commander = 98 cards
|
||||
min--;
|
||||
max--;
|
||||
|
||||
PaperCard a = commanders.get(0);
|
||||
PaperCard b = commanders.get(1);
|
||||
|
||||
if (a.getRules().hasKeyword("Partner") && b.getRules().hasKeyword("Partner")) {
|
||||
// normal partner commander
|
||||
} else if (a.getName().equals(b.getRules().getParterWith())
|
||||
&& b.getName().equals(a.getRules().getParterWith())) {
|
||||
// paired partner commander
|
||||
} else {
|
||||
return "has an illegal commander partnership";
|
||||
}
|
||||
}
|
||||
|
||||
final List<PaperCard> erroneousCI = new ArrayList<PaperCard>();
|
||||
|
||||
Set<String> basicLandNames = new HashSet<>();
|
||||
@@ -521,6 +513,8 @@ public enum DeckFormat {
|
||||
for (final PaperCard p : commanders) {
|
||||
cmdCI |= p.getRules().getColorIdentity().getColor();
|
||||
}
|
||||
return Predicates.compose(Predicates.or(CardRulesPredicates.hasColorIdentity(cmdCI), CardRulesPredicates.hasKeyword("Partner")), PaperCard.FN_GET_RULES);
|
||||
// TODO : check commander what kind of Partner it needs
|
||||
return Predicates.compose(Predicates.or(CardRulesPredicates.hasColorIdentity(cmdCI),
|
||||
CardRulesPredicates.Presets.CAN_BE_PARTNER_COMMANDER), PaperCard.FN_GET_RULES);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1561,6 +1561,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|| keyword.equals("Totem armor") || keyword.equals("Battle cry")
|
||||
|| keyword.equals("Devoid")){
|
||||
sbLong.append(keyword + " (" + inst.getReminderText() + ")");
|
||||
} else if (keyword.startsWith("Partner:")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append("Partner with " + k[1] + " (" + inst.getReminderText() + ")");
|
||||
} else if (keyword.startsWith("Modular") || keyword.startsWith("Bloodthirst")
|
||||
|| keyword.startsWith("Fabricate") || keyword.startsWith("Soulshift")
|
||||
|| keyword.startsWith("Crew") || keyword.startsWith("Tribute") || keyword.startsWith("Absorb")
|
||||
|
||||
@@ -2711,6 +2711,23 @@ public class CardFactoryUtil {
|
||||
card.setSVar("MyriadCleanup", dbString4);
|
||||
|
||||
inst.addTrigger(parsedTrigger);
|
||||
} else if (keyword.startsWith("Partner:")) {
|
||||
// Partner With
|
||||
final String[] k = keyword.split(":");
|
||||
final String trigStr = "Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield " +
|
||||
"| ValidCard$ Card.Self | Secondary$ True " +
|
||||
"| TriggerDescription$ Partner with " + k[1] + " (" + inst.getReminderText() + ")";
|
||||
// replace , for ; in the ChangeZone
|
||||
k[1] = k[1].replace(",", ";");
|
||||
|
||||
final String effect = "DB$ ChangeZone | ValidTgts$ Player | TgtPrompt$ Select target player" +
|
||||
" | Origin$ Library | Destination$ Hand | ChangeType$ Card.named" + k[1] +
|
||||
" | ChangeNum$ 1 | Hidden$ True | Chooser$ Targeted | Optional$ Targeted | AILogic$ Always";
|
||||
|
||||
final Trigger trigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
||||
trigger.setOverridingAbility(AbilityFactory.getAbility(effect, card));
|
||||
|
||||
inst.addTrigger(trigger);
|
||||
} else if (keyword.equals("Persist")) {
|
||||
final String trigStr = "Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | OncePerEffect$ True " +
|
||||
" | ValidCard$ Card.Self+counters_EQ0_M1M1 | Secondary$ True" +
|
||||
|
||||
@@ -100,7 +100,7 @@ public enum Keyword {
|
||||
NINJUTSU(KeywordWithCost.class, false, "%s, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking."),
|
||||
OUTLAST(KeywordWithCost.class, false, "%s, {T}: Put a +1/+1 counter on this creature. Outlast only as a sorcery."),
|
||||
OFFERING(KeywordWithType.class, false, "You may cast this card any time you could cast an instant by sacrificing a %1$s and paying the difference in mana costs between this and the sacrificed %1$s. Mana cost includes color."),
|
||||
PARTNER(SimpleKeyword.class, true, "You can have two commanders if both have partner."),
|
||||
PARTNER(Partner.class, true, "You can have two commanders if both have partner."),
|
||||
PERSIST(SimpleKeyword.class, true, "When this creature dies, if it had no -1/-1 counters on it, return it to the battlefield under its owner's control with a -1/-1 counter on it."),
|
||||
PHASING(SimpleKeyword.class, true, "This phases in or out before you untap during each of your untap steps. While it's phased out, it's treated as though it doesn't exist."),
|
||||
POISONOUS(KeywordWithAmount.class, false, "Whenever this creature deals combat damage to a player, that player gets {%d:poison counter}."),
|
||||
|
||||
35
forge-game/src/main/java/forge/game/keyword/Partner.java
Normal file
35
forge-game/src/main/java/forge/game/keyword/Partner.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package forge.game.keyword;
|
||||
|
||||
public class Partner extends SimpleKeyword {
|
||||
|
||||
private String with = null;
|
||||
|
||||
public Partner() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parse(String details) {
|
||||
if (!details.isEmpty()) {
|
||||
if (details.contains(":")) {
|
||||
with = details.split(":")[1];
|
||||
} else {
|
||||
with = details;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatReminderText(String reminderText) {
|
||||
if (with == null) {
|
||||
return reminderText;
|
||||
} else {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("When this creature enters the battlefield, target player may put ");
|
||||
sb.append(with);
|
||||
sb.append(" into their hand from their library, then shuffle.");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
forge-gui/res/cardsfolder/upcoming/khorvath_brightflame.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/khorvath_brightflame.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Khorvath Brightflame
|
||||
ManaCost:5 R
|
||||
Types:Legendary Creature Dragon
|
||||
PT:3/4
|
||||
K:Partner:Sylvia Brightspear:Sylvia
|
||||
K:Flying
|
||||
K:Haste
|
||||
S:Mode$ Continuous | Affected$ Knight.YourTeamCtrl | AddKeyword$ Flying & Haste | Description$ Knights your team controls have flying and haste.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/khorvath_brightflame.jpg
|
||||
Oracle:Partner with Sylvia Brightspear (When this creature enters the battlefield, target player may put Sylvia into their hand from their library, then shuffle.)\nFlying, haste\nKnights your team controls have flying and haste.
|
||||
@@ -0,0 +1,9 @@
|
||||
Name:Sylvia Brightspear
|
||||
ManaCost:2 W
|
||||
Types:Legendary Creature Human Knight
|
||||
PT:2/2
|
||||
K:Partner:Khorvath Brightflame:Khorvath
|
||||
K:Double Strike
|
||||
S:Mode$ Continuous | Affected$ Dragon.YourTeamCtrl | AddKeyword$ Double Strike | Description$ Dragons your team controls have double strike.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/sylvia_brightspear.jpg
|
||||
Oracle:Partner with Khorvath Brightflame (When this creature enters the battlefield, target player may put Khorvath into their hand from their library, then shuffle.)\nDouble strike\nDragons your team controls have double strike.
|
||||
Reference in New Issue
Block a user