Merge branch 'master' into Attractions

This commit is contained in:
Jetz
2024-06-23 11:23:47 -04:00
426 changed files with 921 additions and 108 deletions

View File

@@ -485,7 +485,7 @@ public class DamageDealAi extends DamageAiBase {
return false; return false;
} }
return damageChoosingTargets(ai, saMe, tgt, dmg, false, immediately); return damageChoosingTargets(ai, saMe, tgt, dmg, saMe.isMandatory(), immediately);
} }
/** /**

View File

@@ -174,6 +174,21 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
return cp != null && !cp.isEmpty(); return cp != null && !cp.isEmpty();
} }
public PaperCard removeCardName(String name) {
PaperCard paperCard;
for (Entry<DeckSection, CardPool> kv : parts.entrySet()) {
CardPool pool = kv.getValue();
for (Entry<PaperCard, Integer> pc : pool) {
if (pc.getKey().getName().equalsIgnoreCase(name)) {
paperCard = pc.getKey();
pool.remove(paperCard);
return paperCard;
}
}
}
return null;
}
// will return new if it was absent // will return new if it was absent
public CardPool getOrCreate(DeckSection deckSection) { public CardPool getOrCreate(DeckSection deckSection) {
CardPool p = get(deckSection); CardPool p = get(deckSection);

View File

@@ -22,10 +22,7 @@ package forge.game;
*/ */
public enum GlobalRuleChange { public enum GlobalRuleChange {
attackerChoosesBlockers ("The attacking player chooses how each creature blocks each combat."), attackerChoosesBlockers ("The attacking player chooses how each creature blocks each combat.");
onlyOneBlocker ("No more than one creature can block each combat."),
onlyOneBlockerPerOpponent ("Each opponent can't block with more than one creature."),
onlyTwoBlockers ("No more than two creatures can block each combat.");
private final String ruleText; private final String ruleText;

View File

@@ -2133,6 +2133,9 @@ public class CardProperty {
List<String> nameList = Lists.newArrayList(names.split(";")); List<String> nameList = Lists.newArrayList(names.split(";"));
return nameList.contains(card.getName()); return nameList.contains(card.getName());
} else if (property.equals("NotedGuessPhantasm")) {
String names = sourceController.getDraftNotes().get("Spire Phantasm");
return names != null && !names.isEmpty();
} else if (property.equals("NotedTypes")) { } else if (property.equals("NotedTypes")) {
// Should Paliano Vanguard be hardcoded here or part of the property? // Should Paliano Vanguard be hardcoded here or part of the property?
String types = sourceController.getDraftNotes().get("Paliano Vanguard"); String types = sourceController.getDraftNotes().get("Paliano Vanguard");

View File

@@ -24,7 +24,6 @@ import com.google.common.collect.Lists;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.GlobalRuleChange;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.card.*; import forge.game.card.*;
import forge.game.cost.Cost; import forge.game.cost.Cost;
@@ -36,6 +35,7 @@ import forge.game.player.Player;
import forge.game.player.PlayerController.ManaPaymentPurpose; import forge.game.player.PlayerController.ManaPaymentPurpose;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityBlockRestrict;
import forge.game.staticability.StaticAbilityCantAttackBlock; import forge.game.staticability.StaticAbilityCantAttackBlock;
import forge.game.staticability.StaticAbilityMustBlock; import forge.game.staticability.StaticAbilityMustBlock;
import forge.game.trigger.TriggerType; import forge.game.trigger.TriggerType;
@@ -457,21 +457,11 @@ public class CombatUtil {
if (!canBlockMoreCreatures(blocker, combat.getAttackersBlockedBy(blocker))) { if (!canBlockMoreCreatures(blocker, combat.getAttackersBlockedBy(blocker))) {
return false; return false;
} }
final Game game = blocker.getGame();
final int blockers = combat.getAllBlockers().size();
if (blockers > 1 && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.onlyTwoBlockers)) {
return false;
}
if (blockers > 0 && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.onlyOneBlocker)) {
return false;
}
CardCollection allOtherBlockers = combat.getAllBlockers(); CardCollection allOtherBlockers = combat.getAllBlockers();
allOtherBlockers.remove(blocker); allOtherBlockers.remove(blocker);
final int blockersFromOnePlayer = CardLists.count(allOtherBlockers, CardPredicates.isController(blocker.getController())); final int blockersFromOnePlayer = CardLists.count(allOtherBlockers, CardPredicates.isController(blocker.getController()));
if (blockersFromOnePlayer > 0 && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.onlyOneBlockerPerOpponent)) { if (blockersFromOnePlayer >= StaticAbilityBlockRestrict.blockRestrictNum(blocker.getController())) {
return false; return false;
} }

View File

@@ -2134,12 +2134,8 @@ public class Player extends GameEntity implements Comparable<Player> {
return lost; return lost;
} }
public final boolean hasProwl(final Set<String> types) { public final boolean hasProwl(final SpellAbility sa) {
StringBuilder sb = new StringBuilder(); return !game.getDamageDoneThisTurn(true, true, "Card.YouCtrl+sharesCreatureTypeWith", "Player", sa.getHostCard(), this, sa).isEmpty();
for (String type : types) {
sb.append("Card.YouCtrl+").append(type).append(",");
}
return !game.getDamageDoneThisTurn(true, true, sb.toString(), "Player", null, this, null).isEmpty();
} }
public final boolean hasFreerunning() { public final boolean hasFreerunning() {

View File

@@ -440,7 +440,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
} }
} }
if (sa.isProwl()) { if (sa.isProwl()) {
if (!activator.hasProwl(c.getType().getCreatureTypes())) { if (!activator.hasProwl(sa)) {
return false; return false;
} }
} }

View File

@@ -0,0 +1,39 @@
package forge.game.staticability;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
public class StaticAbilityBlockRestrict {
static String MODE = "BlockRestrict";
static public int blockRestrictNum(Player defender) {
final Game game = defender.getGame();
int num = Integer.MAX_VALUE;
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final StaticAbility stAb : ca.getStaticAbilities()) {
if (!stAb.checkConditions(MODE)) {
continue;
}
if (blockRestrict(stAb, defender)) {
int stNum = AbilityUtils.calculateAmount(stAb.getHostCard(),
stAb.getParamOrDefault("MaxBlockers", "1"), stAb);
if (stNum < num) {
num = stNum;
}
}
}
}
return num;
}
static public boolean blockRestrict(StaticAbility stAb, Player defender) {
if (!stAb.matchesValidParam("ValidDefender", defender)) {
return false;
}
return true;
}
}

View File

@@ -116,12 +116,21 @@ public class CEditorDraftingProcess extends ACEditorBase<PaperCard, DeckGroup> i
// can only draft one at a time, regardless of the requested quantity // can only draft one at a time, regardless of the requested quantity
PaperCard card = items.iterator().next().getKey(); PaperCard card = items.iterator().next().getKey();
if (boosterDraft.getHumanPlayer().shouldSkipThisPick()) {
System.out.println(card + " not drafted because we're skipping this pick");
showPackToDraft();
return;
}
// Verify if card is in the activate pack? // Verify if card is in the activate pack?
this.getDeckManager().addItem(card, 1); this.getDeckManager().addItem(card, 1);
// get next booster pack // get next booster pack if we aren't picking again from this pack
this.boosterDraft.setChoice(card); this.boosterDraft.setChoice(card);
showPackToDraft();
}
protected void showPackToDraft() {
boolean nextChoice = this.boosterDraft.hasNextChoice(); boolean nextChoice = this.boosterDraft.hasNextChoice();
ItemPool<PaperCard> pool = null; ItemPool<PaperCard> pool = null;
if (nextChoice) { if (nextChoice) {

View File

@@ -58,10 +58,15 @@ public class BoosterDraftTest implements IBoosterDraft {
return result; return result;
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*
* @return
*/
@Override @Override
public void setChoice(final PaperCard c) { public boolean setChoice(final PaperCard c) {
System.out.println(c.getName()); System.out.println(c.getName());
return false;
} }
@Override @Override

View File

@@ -132,3 +132,4 @@ Alchemy: Ixalan, 3/6/LCI, YLCI
Ravnica Remastered, 3/6/RAV, RVR Ravnica Remastered, 3/6/RAV, RVR
Murders at Karlov Manor, 3/6/MKM, MKM Murders at Karlov Manor, 3/6/MKM, MKM
Outlaws of Thunder Junction, 3/6/OTJ, OTJ Outlaws of Thunder Junction, 3/6/OTJ, OTJ
Modern Horizons 3, 3/6/MH3, MH3

View File

@@ -0,0 +1,7 @@
Name:Agent of Acquisitions
ManaCost:2
Types:Artifact Creature Construct
PT:2/1
Draft:Draft CARDNAME face up.
Draft:Instead of drafting a card from a booster pack, you may draft each card in that booster pack, one at a time. If you do, turn CARDNAME face down and you cant draft cards for the rest of this draft round. (You may look at booster packs passed to you.)
Oracle:Draft Agent of Acquisitions face up.\nInstead of drafting a card from a booster pack, you may draft each card in that booster pack, one at a time. If you do, turn Agent of Acquisitions face down and you cant draft cards for the rest of this draft round. (You may look at booster packs passed to you.)

View File

@@ -4,6 +4,6 @@ Types:Creature Human
PT:3/1 PT:3/1
K:Squad:2 K:Squad:2
K:CARDNAME can't block. K:CARDNAME can't block.
A:AB$ Pump | PrecostDesc$ Endurant — | Cost$ PayLife<3> | KW$ Indestructible | SpellDescription$ CARDNAME gains indestructible until end of turn. A:AB$ Pump | PrecostDesc$ Endurant — | Cost$ PayLife<3> | KW$ Indestructible | Defined$ Self | SpellDescription$ CARDNAME gains indestructible until end of turn.
DeckHas:Ability$Token DeckHas:Ability$Token
Oracle:Squad {2} (As an additional cost to cast this spell, you may pay {2} any number of times. When this creature enters the battlefield, create that many tokens that are copies of it.)\nArco-Flagellant can't block.\nEndurant — Pay 3 life: Arco-Flagellant gains indestructible until end of turn. Oracle:Squad {2} (As an additional cost to cast this spell, you may pay {2} any number of times. When this creature enters the battlefield, create that many tokens that are copies of it.)\nArco-Flagellant can't block.\nEndurant — Pay 3 life: Arco-Flagellant gains indestructible until end of turn.

View File

@@ -2,7 +2,7 @@ Name:Astral Arena
ManaCost:no cost ManaCost:no cost
Types:Plane Kolbahan Types:Plane Kolbahan
S:Mode$ AttackRestrict | EffectZone$ Command | MaxAttackers$ 1 | Description$ No more than one creature can attack each combat. S:Mode$ AttackRestrict | EffectZone$ Command | MaxAttackers$ 1 | Description$ No more than one creature can attack each combat.
S:Mode$ Continuous | EffectZone$ Command | GlobalRule$ No more than one creature can block each combat. | Description$ No more than one creature can block each combat. S:Mode$ BlockRestrict | EffectZone$ Command | MaxBlockers$ 1 | Description$ No more than one creature can block each combat.
T:Mode$ ChaosEnsues | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever chaos ensues, CARDNAME deals 2 damage to each creature. T:Mode$ ChaosEnsues | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever chaos ensues, CARDNAME deals 2 damage to each creature.
SVar:RolledChaos:DB$ DamageAll | NumDmg$ 2 | ValidCards$ Creature SVar:RolledChaos:DB$ DamageAll | NumDmg$ 2 | ValidCards$ Creature
SVar:AIRollPlanarDieParams:Mode$ Random | MinTurn$ 5 SVar:AIRollPlanarDieParams:Mode$ Random | MinTurn$ 5

View File

@@ -4,7 +4,7 @@ Types:Creature Elemental Knight
PT:5/6 PT:5/6
K:Reach K:Reach
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters the battlefield, reveal the top five cards of your library. Put a land card from among them onto the battlefield and the rest into your graveyard. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters the battlefield, reveal the top five cards of your library. Put a land card from among them onto the battlefield and the rest into your graveyard.
SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | ChangeValid$ Land | Optional$ True | DestinationZone$ Battlefield | DestinationZone2$ Graveyard SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | ChangeValid$ Land | DestinationZone$ Battlefield | DestinationZone2$ Graveyard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, put another target card from your graveyard on top of your library. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, put another target card from your graveyard on top of your library.
SVar:TrigExile:AB$ ChangeZone | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Origin$ Graveyard | Destination$ Library | ValidTgts$ Card.YouOwn+Other | TgtPrompt$ Select another target card from your graveyard | AITgts$ Card.Other SVar:TrigExile:AB$ ChangeZone | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Origin$ Graveyard | Destination$ Library | ValidTgts$ Card.YouOwn+Other | TgtPrompt$ Select another target card from your graveyard | AITgts$ Card.Other
DeckHas:Ability$Graveyard DeckHas:Ability$Graveyard

View File

@@ -2,7 +2,7 @@ Name:Caverns of Despair
ManaCost:2 R R ManaCost:2 R R
Types:World Enchantment Types:World Enchantment
S:Mode$ AttackRestrict | MaxAttackers$ 2 | Description$ No more than two creatures can attack each combat. S:Mode$ AttackRestrict | MaxAttackers$ 2 | Description$ No more than two creatures can attack each combat.
S:Mode$ Continuous | GlobalRule$ No more than two creatures can block each combat. | Description$ No more than two creatures can block each combat. S:Mode$ BlockRestrict | MaxBlockers$ 2 | Description$ No more than two creatures can block each combat.
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
AI:RemoveDeck:Random AI:RemoveDeck:Random
DeckHints:Type$Planeswalker|Artifact|Enchantment|Aura|Equipment DeckHints:Type$Planeswalker|Artifact|Enchantment|Aura|Equipment

View File

@@ -0,0 +1,7 @@
Name:Cogwork Librarian
ManaCost:4
Types:Artifact Creature Construct
PT:3/3
Draft:Draft CARDNAME face up.
Draft:As you draft a card, you may draft an additional card from that booster pack. If you do, put CARDNAME into that booster pack.
Oracle:Draft Cogwork Librarian face up.\nAs you draft a card, you may draft an additional card from that booster pack. If you do, put Cogwork Librarian into that booster pack.

View File

@@ -2,6 +2,6 @@ Name:Dueling Grounds
ManaCost:1 G W ManaCost:1 G W
Types:Enchantment Types:Enchantment
S:Mode$ AttackRestrict | MaxAttackers$ 1 | Description$ No more than one creature can attack each combat. S:Mode$ AttackRestrict | MaxAttackers$ 1 | Description$ No more than one creature can attack each combat.
S:Mode$ Continuous | GlobalRule$ No more than one creature can block each combat. | Description$ No more than one creature can block each combat. S:Mode$ BlockRestrict | MaxBlockers$ 1 | Description$ No more than one creature can block each combat.
AI:RemoveDeck:Random AI:RemoveDeck:Random
Oracle:No more than one creature can attack each combat.\nNo more than one creature can block each combat. Oracle:No more than one creature can attack each combat.\nNo more than one creature can block each combat.

Some files were not shown because too many files have changed in this diff Show More