mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -30,6 +30,7 @@ public enum ApiType {
|
||||
Block (BlockEffect.class),
|
||||
Bond (BondEffect.class),
|
||||
Branch (BranchEffect.class),
|
||||
Camouflage (CamouflageEffect.class),
|
||||
ChangeCombatants (ChangeCombatantsEffect.class),
|
||||
ChangeTargets (ChangeTargetsEffect.class),
|
||||
ChangeText (ChangeTextEffect.class),
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class CamouflageEffect extends SpellAbilityEffect {
|
||||
|
||||
private void randomizeBlockers(SpellAbility sa, Combat combat, Player declarer, Player defender, List<Card> attackers, List<CardCollection> blockerPiles) {
|
||||
CardLists.shuffle(attackers);
|
||||
for (int i = 0; i < attackers.size(); i++) {
|
||||
final Card attacker = attackers.get(i);
|
||||
CardCollection blockers = blockerPiles.get(i);
|
||||
|
||||
// Remove all illegal blockers first
|
||||
for (int j = blockers.size() - 1; j >= 0; j--) {
|
||||
final Card blocker = blockers.get(j);
|
||||
if (!CombatUtil.canBlock(attacker, blocker, combat)) {
|
||||
blockers.remove(j);
|
||||
}
|
||||
}
|
||||
|
||||
if (attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.") &&
|
||||
blockers.size() < defender.getCreaturesInPlay().size() ||
|
||||
blockers.size() < CombatUtil.needsBlockers(attacker)) {
|
||||
// If not enough remaining creatures to block, don't add them as blocker
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attacker.hasKeyword("CantBeBlockedByAmount GT1") && blockers.size() > 1) {
|
||||
// If no more than one creature can block, order the player to choose one to block
|
||||
Card chosen = declarer.getController().chooseCardsForEffect(blockers, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseBlockerForAttacker", attacker.toString()), 1, 1, false, null).get(0);
|
||||
combat.addBlocker(attacker, chosen);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add all remaning blockers normally
|
||||
for (final Card blocker : blockers) {
|
||||
combat.addBlocker(attacker, blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
Card hostCard = sa.getHostCard();
|
||||
Player declarer = getDefinedPlayersOrTargeted(sa).get(0);
|
||||
Player defender = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("Defender"), sa).get(0);
|
||||
Combat combat = hostCard.getGame().getCombat();
|
||||
List<Card> attackers = combat.getAttackers();
|
||||
List<CardCollection> blockerPiles = new ArrayList<>();
|
||||
|
||||
if (declarer.isAI()) {
|
||||
// For AI player, just let it declare blockers normally, then randomize it later.
|
||||
declarer.getController().declareBlockers(defender, combat);
|
||||
// Remove all blockers first
|
||||
for (final Card attacker : attackers) {
|
||||
CardCollection blockers = combat.getBlockers(attacker);
|
||||
blockerPiles.add(blockers);
|
||||
for (final Card blocker : blockers) {
|
||||
combat.removeFromCombat(blocker);
|
||||
}
|
||||
}
|
||||
} else { // Human player
|
||||
CardCollection pool = new CardCollection(defender.getCreaturesInPlay());
|
||||
// remove all blockers that can't block
|
||||
for (final Card blocker : pool) {
|
||||
if (!CombatUtil.canBlock(blocker)) {
|
||||
pool.remove(blocker);
|
||||
}
|
||||
}
|
||||
List<Integer> blockedSoFar = new ArrayList<>(Collections.nCopies(pool.size(), 0));
|
||||
|
||||
for (int i = 0; i < attackers.size(); i++) {
|
||||
int size = pool.size();
|
||||
CardCollection blockers = new CardCollection(declarer.getController().chooseCardsForEffect(
|
||||
pool, sa, Localizer.getInstance().getMessage("lblChooseBlockersForPile", String.valueOf(i + 1)), 0, size, false, null));
|
||||
blockerPiles.add(blockers);
|
||||
// Remove chosen creatures, unless it can block additional attackers
|
||||
for (final Card blocker : blockers) {
|
||||
int index = pool.indexOf(blocker);
|
||||
Integer blockedCount = blockedSoFar.get(index) + 1;
|
||||
if (!blocker.canBlockAny() && blocker.canBlockAdditional() < blockedCount) {
|
||||
pool.remove(index);
|
||||
blockedSoFar.remove(index);
|
||||
} else {
|
||||
blockedSoFar.set(index, blockedCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
randomizeBlockers(sa, combat, declarer, defender, attackers, blockerPiles);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -349,6 +349,9 @@ public class DiscardEffect extends SpellAbilityEffect {
|
||||
runParams.put(AbilityKey.Cause, sa);
|
||||
runParams.put(AbilityKey.FirstTime, firstDiscard);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.DiscardedAll, runParams, false);
|
||||
if (sa.hasParam("RememberDiscardingPlayers")) {
|
||||
source.addRemembered(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1247,17 +1247,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
currentState.addTrigger(t);
|
||||
return t;
|
||||
}
|
||||
@Deprecated
|
||||
public final void removeTrigger(final Trigger t) {
|
||||
currentState.removeTrigger(t);
|
||||
}
|
||||
@Deprecated
|
||||
public final void removeTrigger(final Trigger t, final CardStateName state) {
|
||||
getState(state).removeTrigger(t);
|
||||
}
|
||||
public final void clearTriggersNew() {
|
||||
currentState.clearTriggers();
|
||||
}
|
||||
|
||||
public final boolean hasTrigger(final Trigger t) {
|
||||
return currentState.hasTrigger(t);
|
||||
|
||||
@@ -653,7 +653,14 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
if (combat.isPlayerAttacked(p)) {
|
||||
if (CombatUtil.canBlock(p, combat)) {
|
||||
whoDeclaresBlockers.getController().declareBlockers(p, combat);
|
||||
// Replacement effects (for Camouflage)
|
||||
final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromAffected(p);
|
||||
repRunParams.put(AbilityKey.Player, whoDeclaresBlockers);
|
||||
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.DeclareBlocker, repRunParams);
|
||||
if (repres == ReplacementResult.NotReplaced) {
|
||||
// If not replaced, run normal declare blockers
|
||||
whoDeclaresBlockers.getController().declareBlockers(p, combat);
|
||||
}
|
||||
}
|
||||
}
|
||||
else { continue; }
|
||||
|
||||
@@ -421,6 +421,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
public final Player getWeakestOpponent() {
|
||||
return getOpponents().min(PlayerPredicates.compareByLife());
|
||||
}
|
||||
public final Player getStrongestOpponent() {
|
||||
return getOpponents().max(PlayerPredicates.compareByLife());
|
||||
}
|
||||
|
||||
public boolean isOpponentOf(Player other) {
|
||||
return other != this && other != null && (other.teamNumber < 0 || other.teamNumber != teamNumber);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package forge.game.replacement;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class ReplaceDeclareBlocker extends ReplacementEffect {
|
||||
|
||||
public ReplaceDeclareBlocker(final Map<String, String> mapParams, final Card host, final boolean intrinsic) {
|
||||
super(mapParams, host, intrinsic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Affected))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject(AbilityKey.DefendingPlayer, runParams.get(AbilityKey.Affected));
|
||||
// Here the Player is the one who would declare blockers (may be changed by some Card's effect)
|
||||
sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Player));
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ public enum ReplacementType {
|
||||
CreateToken(ReplaceToken.class),
|
||||
DamageDone(ReplaceDamage.class),
|
||||
DealtDamage(ReplaceDealtDamage.class),
|
||||
DeclareBlocker(ReplaceDeclareBlocker.class),
|
||||
Destroy(ReplaceDestroy.class),
|
||||
Discard(ReplaceDiscard.class),
|
||||
Draw(ReplaceDraw.class),
|
||||
|
||||
Reference in New Issue
Block a user