changed order or actions performed on declare blockers step: declare, pay extra costs, fire event, then fire triggers

EventVisualizer.java will be able to play sounds when ai is blocking
This commit is contained in:
Maxmtg
2013-07-07 22:09:20 +00:00
parent cf27269f08
commit df3b938ed0
5 changed files with 154 additions and 137 deletions

View File

@@ -81,6 +81,16 @@ public class Combat {
return Lists.newArrayList(attackedEntities.keySet());
}
public final List<GameEntity> getDefendersControlledBy(Player who) {
List<GameEntity> res = Lists.newArrayList();
for(GameEntity ge : attackedEntities.keySet()) {
// if defender is the player himself or his cards
if (ge == who || ge instanceof Card && ((Card) ge).getController() == who)
res.add(ge);
}
return res;
}
public final List<Card> getDefendingPlaneswalkers() {
final List<Card> pwDefending = new ArrayList<Card>();
for (final GameEntity o : attackedEntities.keySet()) {
@@ -389,7 +399,7 @@ public class Combat {
// Call this method right after turn-based action of declare blockers has been performed
public final void onBlockersDeclared() {
public final void fireTriggersForUnblockedAttackers() {
for(Collection<AttackingBand> abs : attackedEntities.values()) {
for(AttackingBand ab : abs) {
Collection<Card> blockers = blockedBands.get(ab);

View File

@@ -20,9 +20,6 @@ package forge.game.combat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
@@ -959,85 +956,27 @@ public class CombatUtil {
c.getController().incrementAttackersDeclaredThisTurn();
} // checkDeclareAttackers
/**
* <p>
* checkDeclareBlockers.
* </p>
* @param game
*
* @param cl
* a {@link forge.CardList} object.
*/
public static void checkDeclareBlockers(Game game, final List<Card> cl, Combat combat) {
for (final Card c : cl) {
if (!c.getDamageHistory().getCreatureBlockedThisCombat()) {
for (final SpellAbility ab : CardFactoryUtil.getBushidoEffects(c)) {
game.getStack().add(ab);
}
// Run triggers
final HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Blocker", c);
final Card attacker = combat.getAttackersBlockedBy(c).get(0);
runParams.put("Attacker", attacker);
game.getTriggerHandler().runTrigger(TriggerType.Blocks, runParams, false);
}
c.getDamageHistory().setCreatureBlockedThisCombat(true);
} // for
public static void handleRampage(final Game game, final Card a, final List<Card> blockers) {
for (final String keyword : a.getKeyword()) {
int idx = keyword.indexOf("Rampage ");
if ( idx < 0)
continue;
} // checkDeclareBlockers
/**
* <p>
* checkBlockedAttackers.
* </p>
* @param game
*
* @param a
* a {@link forge.Card} object.
* @param b
* a {@link forge.Card} object.
*/
public static void checkBlockedAttackers(final Game game, final Card a, final List<Card> blockers) {
if (blockers.isEmpty()) {
return;
}
// Run triggers
final HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Attacker", a);
runParams.put("Blockers", blockers);
runParams.put("NumBlockers", blockers.size());
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false);
if (!a.getDamageHistory().getCreatureGotBlockedThisCombat()) {
// Bushido
for (final SpellAbility ab : CardFactoryUtil.getBushidoEffects(a)) {
game.getStack().add(ab);
}
// Rampage
final List<String> keywords = a.getKeyword();
final Pattern p = Pattern.compile("Rampage [0-9]+");
Matcher m;
for (final String keyword : keywords) {
m = p.matcher(keyword);
if (m.find()) {
final String[] k = keyword.split(" ");
final int magnitude = Integer.valueOf(k[1]);
final int numBlockers = blockers.size();
if (numBlockers > 1) {
final int magnitude = Integer.valueOf(keyword.substring(idx + "Rampage ".length()));
CombatUtil.executeRampageAbility(game, a, magnitude, numBlockers);
}
} // find
} // end Rampage
}
for (Card b : blockers) {
if (a.hasKeyword("Flanking") && !b.hasKeyword("Flanking")) {
public static void handleFlankingKeyword(final Game game, final Card attacker, final List<Card> blockers) {
for (Card blocker : blockers) {
if (attacker.hasKeyword("Flanking") && !blocker.hasKeyword("Flanking")) {
int flankingMagnitude = 0;
for (String kw : a.getKeyword()) {
for (String kw : attacker.getKeyword()) {
if (kw.equals("Flanking")) {
flankingMagnitude++;
}
@@ -1045,11 +984,11 @@ public class CombatUtil {
// Rule 702.23b: If a creature has multiple instances of flanking, each triggers separately.
for( int i = 0; i < flankingMagnitude; i++ ) {
String effect = String.format("AB$ Pump | Cost$ 0 | Defined$ CardUID_%d | NumAtt$ -1 | NumDef$ -1 | ", b.getUniqueNumber());
String desc = String.format("StackDescription$ Flanking (The blocking %s gets -1/-1 until end of turn)", b.getName());
String effect = String.format("AB$ Pump | Cost$ 0 | Defined$ CardUID_%d | NumAtt$ -1 | NumDef$ -1 | ", blocker.getUniqueNumber());
String desc = String.format("StackDescription$ Flanking (The blocking %s gets -1/-1 until end of turn)", blocker.getName());
SpellAbility ability = AbilityFactory.getAbility(effect + desc, a);
ability.setActivatingPlayer(a.getController());
SpellAbility ability = AbilityFactory.getAbility(effect + desc, attacker);
ability.setActivatingPlayer(attacker.getController());
ability.setDescription(ability.getStackDescription());
ability.setTrigger(true);
@@ -1057,11 +996,9 @@ public class CombatUtil {
}
} // flanking
b.addBlockedThisTurn(a);
a.addBlockedByThisTurn(b);
blocker.addBlockedThisTurn(attacker);
attacker.addBlockedByThisTurn(blocker);
}
a.getDamageHistory().setCreatureGotBlockedThisCombat(true);
}
/**

View File

@@ -1,9 +1,14 @@
package forge.game.event;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import forge.Card;
import forge.GameEntity;
import forge.game.player.Player;
import forge.util.Lang;
import forge.util.maps.MapOfLists;
/**
@@ -13,9 +18,11 @@ import forge.util.maps.MapOfLists;
public class GameEventBlockersDeclared extends GameEvent {
public final Map<GameEntity, MapOfLists<Card, Card>> blockers;
public final Player defendingPlayer;
public GameEventBlockersDeclared(Map<GameEntity, MapOfLists<Card, Card>> blockers) {
public GameEventBlockersDeclared(Player who, Map<GameEntity, MapOfLists<Card, Card>> blockers) {
this.blockers = blockers;
defendingPlayer = who;
}
@Override
@@ -24,4 +31,17 @@ public class GameEventBlockersDeclared extends GameEvent {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
List<Card> blockerCards = new ArrayList<Card>();
for(MapOfLists<Card, Card> vv : blockers.values()) {
for(Collection<Card> cc : vv.values()) {
blockerCards.addAll(cc);
}
}
return String.format("%s declared %d blockers: %s", defendingPlayer.getName(), blockerCards.size(), Lang.joinHomogenous(blockerCards) );
}
}

View File

@@ -24,16 +24,16 @@ import java.util.Map;
import java.util.Stack;
import org.apache.commons.lang.time.StopWatch;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.FThreads;
import forge.GameEntity;
import forge.Singletons;
import forge.CardPredicates.Presets;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost;
import forge.card.mana.ManaCost;
import forge.card.spellability.SpellAbility;
import forge.card.staticability.StaticAbility;
import forge.card.trigger.TriggerType;
import forge.game.GameAge;
@@ -522,22 +522,15 @@ public class PhaseHandler implements java.io.Serializable {
Player whoDeclaresBlockers = playerDeclaresBlockers == null || playerDeclaresBlockers.hasLost() ? p : playerDeclaresBlockers;
if ( combat.isPlayerAttacked(p) ) {
whoDeclaresBlockers.getController().declareBlockers(p, combat);
}
} else
continue;
if ( game.isGameOver() ) // they just like to close window at any moment
return;
} while(p != playerTurn);
combat.orderBlockersForDamageAssignment();
combat.orderAttackersForDamageAssignment();
combat.removeAbsentCombatants();
// Handles removing cards like Mogg Flunkies from combat if group block
// didn't occur
final List<Card> filterList = combat.getAllBlockers();
for (Card blocker : filterList) {
for (Card blocker : CardLists.filterControlledBy(combat.getAllBlockers(), p)) {
final List<Card> attackers = new ArrayList<Card>(combat.getAttackersBlockedBy(blocker));
for (Card attacker : attackers) {
boolean hasPaid = payRequiredBlockCosts(game, blocker, attacker);
@@ -547,41 +540,79 @@ public class PhaseHandler implements java.io.Serializable {
}
}
}
for (Card c : filterList) {
if (c.hasKeyword("CARDNAME can't attack or block alone.") && combat.isBlocking(c)) {
if (combat.getAllBlockers().size() < 2) {
List<Card> remainingBlockers = CardLists.filterControlledBy(combat.getAllBlockers(), p);
for (Card c : remainingBlockers) {
if ( remainingBlockers.size() < 2 && c.hasKeyword("CARDNAME can't attack or block alone.") ) {
combat.undoBlockingAssignment(c);
}
}
}
combat.onBlockersDeclared();
List<Card> list = combat.getAllBlockers();
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return !c.getDamageHistory().getCreatureBlockedThisCombat();
}
});
CombatUtil.checkDeclareBlockers(game, list, combat);
for (final Card a : combat.getAttackers()) {
CombatUtil.checkBlockedAttackers(game, a, combat.getBlockers(a));
}
// Player is done declaring blockers - redraw UI at this point
// map: defender => (many) attacker => (many) blocker
Map<GameEntity, MapOfLists<Card, Card>> blockers = new HashMap<GameEntity, MapOfLists<Card,Card>>();
for(GameEntity ge : combat.getDefenders()) {
for(GameEntity ge : combat.getDefendersControlledBy(p)) {
MapOfLists<Card, Card> protectThisDefender = new HashMapOfLists<Card, Card>(CollectionSuppliers.<Card>arrayLists());
for(Card att : combat.getAttackersOf(ge)) {
protectThisDefender.addAll(att, combat.getBlockers(att));
}
blockers.put(ge, protectThisDefender);
}
game.fireEvent(new GameEventBlockersDeclared(blockers));
game.fireEvent(new GameEventBlockersDeclared(p, blockers));
} while(p != playerTurn);
combat.orderBlockersForDamageAssignment(); // 509.2
combat.orderAttackersForDamageAssignment(); // 509.3
combat.removeAbsentCombatants();
combat.fireTriggersForUnblockedAttackers();
for (final Card c1 : combat.getAllBlockers()) {
if ( c1.getDamageHistory().getCreatureBlockedThisCombat() )
continue;
if (!c1.getDamageHistory().getCreatureBlockedThisCombat()) {
for (final SpellAbility ab : CardFactoryUtil.getBushidoEffects(c1)) {
game.getStack().add(ab);
}
// Run triggers
final HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Blocker", c1);
runParams.put("Attacker", combat.getAttackersBlockedBy(c1).get(0));
game.getTriggerHandler().runTrigger(TriggerType.Blocks, runParams, false);
}
c1.getDamageHistory().setCreatureBlockedThisCombat(true);
}
for (final Card a : combat.getAttackers()) {
List<Card> blockers = combat.getBlockers(a);
if ( blockers.isEmpty() )
continue;
// Run triggers
final HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Attacker", a);
runParams.put("Blockers", blockers);
runParams.put("NumBlockers", blockers.size());
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false);
if (!a.getDamageHistory().getCreatureGotBlockedThisCombat()) {
// Bushido
for (final SpellAbility ab : CardFactoryUtil.getBushidoEffects(a)) {
game.getStack().add(ab);
}
// Rampage
CombatUtil.handleRampage(game, a, blockers);
}
CombatUtil.handleFlankingKeyword(game, a, blockers);
a.getDamageHistory().setCreatureGotBlockedThisCombat(true);
}
}

View File

@@ -1,8 +1,11 @@
package forge.sound;
import java.util.Collection;
import forge.Card;
import forge.Singletons;
import forge.card.spellability.SpellAbility;
import forge.game.event.GameEventBlockersDeclared;
import forge.game.event.GameEventCardChangeZone;
import forge.game.event.GameEventCardDamaged;
import forge.game.event.GameEventCardDestroyed;
@@ -26,6 +29,7 @@ import forge.game.zone.ZoneType;
import forge.gui.events.IUiEventVisitor;
import forge.gui.events.UiEventAttackerDeclared;
import forge.gui.events.UiEventBlockerAssigned;
import forge.util.maps.MapOfLists;
/**
* This class is in charge of converting any forge.game.event.Event to a SoundEffectType.
@@ -55,6 +59,21 @@ public class EventVisualizer extends IGameEventVisitor.Base<SoundEffectType> imp
public SoundEffectType visit(GameEventPlayerPoisoned event) { return SoundEffectType.Poison; }
public SoundEffectType visit(GameEventShuffle event) { return SoundEffectType.Shuffle; }
public SoundEffectType visit(GameEventTokenCreated event) { return SoundEffectType.Token; }
public SoundEffectType visit(GameEventBlockersDeclared event) {
boolean isLocalHuman = event.defendingPlayer.getLobbyPlayer() == Singletons.getControl().getLobby().getGuiPlayer();
if (isLocalHuman)
return null; // already played sounds in interactive mode
for(MapOfLists<Card, Card> ab : event.blockers.values()) {
for(Collection<Card> bb : ab.values()) {
if ( !bb.isEmpty() ) {
// hasAnyBlocker = true;
return SoundEffectType.Block;
}
}
}
return null;
}
/**
* Plays the sound corresponding to the outcome of the duel.