mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Merge branch 'sorrows_path_general_jarkeld' into 'master'
Add Sorrow's Path and General Jarkeld See merge request core-developers/forge!3685
This commit is contained in:
@@ -154,6 +154,7 @@ public enum ApiType {
|
||||
StoreSVar (StoreSVarEffect.class),
|
||||
StoreMap (StoreMapEffect.class),
|
||||
Surveil (SurveilEffect.class),
|
||||
SwitchBlock (SwitchBlockEffect.class),
|
||||
Tap (TapEffect.class),
|
||||
TapAll (TapAllEffect.class),
|
||||
TapOrUntap (TapOrUntapEffect.class),
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
|
||||
public class SwitchBlockEffect extends SpellAbilityEffect {
|
||||
|
||||
private void runTriggers(final Game game, final Card attacker, final Card blocker) {
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
runParams.put(AbilityKey.Attacker, attacker);
|
||||
runParams.put(AbilityKey.Blocker, blocker);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlockedByCreature, runParams, false);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Blocks, runParams, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final Combat combat = game.getPhaseHandler().getCombat();
|
||||
boolean isTargetingAttacker = false;
|
||||
|
||||
List<Card> attackers = new ArrayList<>();
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
final String definedAttacker = sa.getParam("DefinedAttacker");
|
||||
if (definedAttacker.equals("Targeted")) {
|
||||
isTargetingAttacker = true;
|
||||
}
|
||||
for (final Card attacker : AbilityUtils.getDefinedCards(host, definedAttacker, sa)) {
|
||||
if (combat.isAttacking(attacker))
|
||||
attackers.add(attacker);
|
||||
}
|
||||
}
|
||||
|
||||
List<Card> blockers = new ArrayList<>();
|
||||
if (sa.hasParam("DefinedBlocker")) {
|
||||
final String definedBlocker = sa.getParam("DefinedBlocker");
|
||||
if (definedBlocker.equals("Targeted")) {
|
||||
isTargetingAttacker = false;
|
||||
}
|
||||
for (final Card blocker : AbilityUtils.getDefinedCards(host, definedBlocker, sa)) {
|
||||
if (combat.isBlocking(blocker))
|
||||
blockers.add(blocker);
|
||||
}
|
||||
}
|
||||
|
||||
if (attackers.isEmpty() || blockers.isEmpty()) return;
|
||||
|
||||
// Check if blockers can be switched, then switch them.
|
||||
boolean isReblock = sa.hasParam("RemoveFromCombat");
|
||||
if (isTargetingAttacker) { // For General Jarkeld
|
||||
// If targeting attackers but only one remains, this fizzles.
|
||||
if (attackers.size() == 1) return;
|
||||
|
||||
final Card attacker1 = attackers.get(0);
|
||||
final Card attacker2 = attackers.get(1);
|
||||
|
||||
// If both attackers are in the same attacking band, no need to switch
|
||||
if (combat.getBandOfAttacker(attacker1) == combat.getBandOfAttacker(attacker2)) return;
|
||||
|
||||
for (final Card blocker : blockers) {
|
||||
if (combat.isBlocking(blocker, attacker1) && !CombatUtil.canBlock(attacker2, blocker) ||
|
||||
combat.isBlocking(blocker, attacker2) && !CombatUtil.canBlock(attacker1, blocker)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch blockers
|
||||
int blockingStates[] = new int[blockers.size()];
|
||||
// Remove all blockers first
|
||||
for (int i = 0; i < blockers.size(); i++) {
|
||||
final Card blocker = blockers.get(i);
|
||||
final boolean blocking1 = combat.isBlocking(blocker, attacker1);
|
||||
final boolean blocking2 = combat.isBlocking(blocker, attacker2);
|
||||
blockingStates[i] = (blocking1 ? 1 : 0) + (blocking2 ? 2 : 0);
|
||||
combat.removeFromCombat(blocker);
|
||||
}
|
||||
// Unregister so it won't ask for damage assignment order when adding each blocker
|
||||
combat.unregisterAttacker(attacker1, combat.getBandOfAttacker(attacker1));
|
||||
combat.unregisterAttacker(attacker2, combat.getBandOfAttacker(attacker2));
|
||||
// Add blockers back to block the other attacker
|
||||
for (int i = 0; i < blockers.size(); i++) {
|
||||
final Card blocker = blockers.get(i);
|
||||
if ((blockingStates[i] & 1) == 1) {
|
||||
combat.addBlocker(attacker2, blocker);
|
||||
if (isReblock) {
|
||||
runTriggers(game, attacker2, blocker);
|
||||
}
|
||||
}
|
||||
if((blockingStates[i] & 2) == 2) {
|
||||
combat.addBlocker(attacker1, blocker);
|
||||
if (isReblock) {
|
||||
runTriggers(game, attacker1, blocker);
|
||||
}
|
||||
}
|
||||
blocker.updateBlockingForView();
|
||||
}
|
||||
// 509.6
|
||||
combat.orderBlockersForDamageAssignment(attacker1, combat.getBlockers(attacker1));
|
||||
combat.orderBlockersForDamageAssignment(attacker2, combat.getBlockers(attacker2));
|
||||
for (final Card blocker : blockers) {
|
||||
combat.orderAttackersForDamageAssignment(blocker);
|
||||
}
|
||||
} else { // For Sorrow's Path
|
||||
// If targeting blockers but only one remains, this fizzles.
|
||||
if (blockers.size() == 1) return;
|
||||
|
||||
final Card blocker1 = blockers.get(0);
|
||||
final Card blocker2 = blockers.get(1);
|
||||
|
||||
// If one blocker is currently blocking more creatures than the other blocker could possibly block, can't switch
|
||||
if (combat.getAttackersBlockedBy(blocker1).size() > blocker2.canBlockAdditional() + 1 ||
|
||||
combat.getAttackersBlockedBy(blocker2).size() > blocker1.canBlockAdditional() + 1) {
|
||||
return;
|
||||
} else {
|
||||
for (final Card attacker : attackers) {
|
||||
if (combat.isBlocking(blocker1, attacker) && !CombatUtil.canBlock(attacker, blocker2) ||
|
||||
combat.isBlocking(blocker2, attacker) && !CombatUtil.canBlock(attacker, blocker1)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Switch blockers
|
||||
int blockingStates[] = new int[attackers.size()];
|
||||
// Remove all blockers first
|
||||
for (int i = 0; i < attackers.size(); i++) {
|
||||
final Card attacker = attackers.get(i);
|
||||
final boolean blocking1 = combat.isBlocking(blocker1, attacker);
|
||||
final boolean blocking2 = combat.isBlocking(blocker2, attacker);
|
||||
blockingStates[i] = (blocking1 ? 1 : 0) + (blocking2 ? 2 : 0);
|
||||
}
|
||||
combat.removeFromCombat(blocker1);
|
||||
combat.removeFromCombat(blocker2);
|
||||
// Add blockers back to block the other attacker
|
||||
for (int i = 0; i < attackers.size(); i++) {
|
||||
final Card attacker = attackers.get(i);
|
||||
if ((blockingStates[i] & 1) == 1) {
|
||||
combat.addBlocker(attacker, blocker2);
|
||||
if (isReblock) {
|
||||
runTriggers(game, attacker, blocker2);
|
||||
}
|
||||
}
|
||||
if ((blockingStates[i] & 2) == 2) {
|
||||
combat.addBlocker(attacker, blocker1);
|
||||
if (isReblock) {
|
||||
runTriggers(game, attacker, blocker1);
|
||||
}
|
||||
}
|
||||
}
|
||||
combat.orderAttackersForDamageAssignment(blocker1);
|
||||
combat.orderAttackersForDamageAssignment(blocker2);
|
||||
}
|
||||
|
||||
game.updateCombatForView();
|
||||
game.fireEvent(new GameEventCombatChanged());
|
||||
}
|
||||
}
|
||||
8
forge-gui/res/cardsfolder/g/general_jarkeld.txt
Normal file
8
forge-gui/res/cardsfolder/g/general_jarkeld.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:General Jarkeld
|
||||
ManaCost:3 W
|
||||
Types:Legendary Creature Human Soldier
|
||||
PT:1/2
|
||||
A:AB$ SwitchBlock | Cost$ T | ActivationPhases$ Declare Blockers | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Creature.attacking+blocked | DefinedAttacker$ Targeted | DefinedBlocker$ Valid Creature.blockingTargeted | TgtPrompt$ Choose two target blocked attacking creatures | SpellDescription$ Choose two target blocked attacking creatures. If each of those creatures could be blocked by all creatures that the other is blocked by, each creature that's blocking exactly one of those attacking creatures stops blocking it and is blocking the other attacking creature. Activate this ability only during the declare blockers step. | StackDescription$ SpellDescription
|
||||
AI:RemoveDeck:Random
|
||||
AI:RemoveDeck:All
|
||||
Oracle:{T}: Choose two target blocked attacking creatures. If each of those creatures could be blocked by all creatures that the other is blocked by, each creature that's blocking exactly one of those attacking creatures stops blocking it and is blocking the other attacking creature. Activate this ability only during the declare blockers step.
|
||||
9
forge-gui/res/cardsfolder/s/sorrows_path.txt
Normal file
9
forge-gui/res/cardsfolder/s/sorrows_path.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Sorrow's Path
|
||||
ManaCost:no cost
|
||||
Types:Land
|
||||
A:AB$ SwitchBlock | Cost$ T | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Creature.blocking+OppCtrl | TargetsFromSingleZone$ True | DefinedAttacker$ Valid Creature.blockedByValidThisTurn Targeted | DefinedBlocker$ Targeted | RemoveFromCombat$ True | TgtPrompt$ Choose two target blocking creatures an opponent controls | SpellDescription$ Choose two target blocking creatures an opponent controls. If each of those creatures could block all creatures that the other is blocking, remove both of them from combat. Each one then blocks all creatures the other was blocking. | StackDescription$ SpellDescription
|
||||
T:Mode$ Taps | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDamage | TriggerDescription$ Whenever CARDNAME becomes tapped, it deals 2 damage to you and each creature you control.
|
||||
SVar:TrigDamage:DB$ DamageAll | ValidCards$ Creature.YouCtrl | ValidPlayers$ You | NumDmg$ 2
|
||||
AI:RemoveDeck:Random
|
||||
AI:RemoveDeck:All
|
||||
Oracle:{T}: Choose two target blocking creatures an opponent controls. If each of those creatures could block all creatures that the other is blocking, remove both of them from combat. Each one then blocks all creatures the other was blocking.\nWhenever Sorrow's Path becomes tapped, it deals 2 damage to you and each creature you control.
|
||||
Reference in New Issue
Block a user