/** * Damages attacking creatures by a creature that blocked several ones Damages only attackers as * blocker was damage in either {@link #singleBlockerDamage} or {@link #multiBlockerDamage}. * * <p>Handles abilities like "{this} an block any number of creatures.". * * @param first * @param game */ private void multiAttackerDamage(boolean first, Game game) { Permanent blocker = game.getPermanent(blockers.get(0)); if (blocker == null) { return; } Player player = game.getPlayer(blocker.getControllerId()); int damage = getDamageValueFromPermanent(blocker, game); if (canDamage(blocker, first)) { Map<UUID, Integer> assigned = new HashMap<>(); for (UUID attackerId : attackerOrder) { Permanent attacker = game.getPermanent(attackerId); int lethalDamage; if (blocker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { lethalDamage = 1; } else { lethalDamage = attacker.getToughness().getValue() - attacker.getDamage(); } if (lethalDamage >= damage) { assigned.put(attackerId, damage); break; } int damageAssigned = player.getAmount( lethalDamage, damage, "Assign damage to " + attacker.getLogName(), game); assigned.put(attackerId, damageAssigned); damage -= damageAssigned; } for (Map.Entry<UUID, Integer> entry : assigned.entrySet()) { Permanent attacker = game.getPermanent(entry.getKey()); attacker.markDamage(entry.getValue(), blocker.getId(), game, true, true); } } }
public void assignDamageToBlockers(boolean first, Game game) { if (attackers.size() > 0 && (!first || hasFirstOrDoubleStrike(game))) { if (blockers.isEmpty()) { unblockedDamage(first, game); } else { Permanent attacker = game.getPermanent(attackers.get(0)); if (attacker .getAbilities() .containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId())) { Player player = game.getPlayer(attacker.getControllerId()); if (player.chooseUse( Outcome.Damage, "Do you wish to assign damage for " + attacker.getLogName() + " as though it weren't blocked?", game)) { blocked = false; unblockedDamage(first, game); } } if (blockers.size() == 1) { singleBlockerDamage(first, game); } else { multiBlockerDamage(first, game); } } } }
@Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { PreventionEffectData preventionResult = preventDamageAction(event, source, game); if (preventionResult.getPreventedDamage() > 0) { Permanent redirectTo = game.getPermanent(getTargetPointer().getFirst(game, source)); if (redirectTo != null) { game.informPlayers( "Dealing " + preventionResult.getPreventedDamage() + " to " + redirectTo.getLogName() + " instead."); DamageEvent damageEvent = (DamageEvent) event; redirectTo.damage( preventionResult.getPreventedDamage(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); } discard(); // (only once) } return false; }
@Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent != null) { return sourcePermanent.getLogName() + " can't be the target of black or red spells opponents control"; } return null; }
private void singleBlockerDamage(boolean first, Game game) { // TODO: handle banding Permanent blocker = game.getPermanent(blockers.get(0)); Permanent attacker = game.getPermanent(attackers.get(0)); if (blocker != null && attacker != null) { int blockerDamage = getDamageValueFromPermanent( blocker, game); // must be set before attacker damage marking because of effects like Test of // Faith if (blocked && canDamage(attacker, first)) { int damage = getDamageValueFromPermanent(attacker, game); if (hasTrample(attacker)) { int lethalDamage; if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { lethalDamage = 1; } else { lethalDamage = blocker.getToughness().getValue() - blocker.getDamage(); } if (lethalDamage >= damage) { blocker.markDamage(damage, attacker.getId(), game, true, true); } else { Player player = game.getPlayer(attacker.getControllerId()); int damageAssigned = player.getAmount( lethalDamage, damage, "Assign damage to " + blocker.getLogName(), game); blocker.markDamage(damageAssigned, attacker.getId(), game, true, true); damage -= damageAssigned; if (damage > 0) { defenderDamage(attacker, damage, game); } } } else { blocker.markDamage(damage, attacker.getId(), game, true, true); } } if (canDamage(blocker, first)) { if (blocker.getBlocking() == 1) { // blocking several creatures handled separately attacker.markDamage(blockerDamage, blocker.getId(), game, true, true); } } } }
public boolean putOntoBattlefield( int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer) { Player controller = game.getPlayer(controllerId); if (controller == null) { return false; } lastAddedTokenIds.clear(); // moved here from CreateTokenEffect because not all cards that create tokens use // CreateTokenEffect // they use putOntoBattlefield directly // TODO: Check this setCode handling because it makes no sense if token put into play with e.g. // "Feldon of the third Path" String setCode = null; if (this.getOriginalExpansionSetCode() != null && !this.getOriginalExpansionSetCode().isEmpty()) { setCode = this.getOriginalExpansionSetCode(); } else { Card source = game.getCard(sourceId); if (source != null) { setCode = source.getExpansionSetCode(); } else { MageObject object = game.getObject(sourceId); if (object instanceof PermanentToken) { setCode = ((PermanentToken) object).getExpansionSetCode(); } } } if (!expansionSetCodeChecked) { expansionSetCodeChecked = this.updateExpansionSetCode(setCode); } GameEvent event = new GameEvent( EventType.CREATE_TOKEN, null, sourceId, controllerId, amount, this.getCardType().contains(CardType.CREATURE)); if (!game.replaceEvent(event)) { amount = event.getAmount(); List<Permanent> permanents = new ArrayList<>(); List<Permanent> permanentsEntered = new ArrayList<>(); for (int i = 0; i < amount; i++) { PermanentToken newToken = new PermanentToken( this, event.getPlayerId(), setCode, game); // use event.getPlayerId() because it can be replaced by replacement effect game.getState().addCard(newToken); permanents.add(newToken); game.getPermanentsEntering().put(newToken.getId(), newToken); newToken.setTapped(tapped); } game.setScopeRelevant(true); for (Permanent permanent : permanents) { if (permanent.entersBattlefield(sourceId, game, Zone.OUTSIDE, true)) { permanentsEntered.add(permanent); } else { game.getPermanentsEntering().remove(permanent.getId()); } } game.setScopeRelevant(false); for (Permanent permanent : permanentsEntered) { game.addPermanent(permanent); permanent.setZone(Zone.BATTLEFIELD, game); game.getPermanentsEntering().remove(permanent.getId()); this.lastAddedTokenIds.add(permanent.getId()); this.lastAddedTokenId = permanent.getId(); game.addSimultaneousEvent( new ZoneChangeEvent( permanent, permanent.getControllerId(), Zone.OUTSIDE, Zone.BATTLEFIELD)); if (attacking && game.getCombat() != null) { game.getCombat().addAttackingCreature(permanent.getId(), game, attackedPlayer); } if (!game.isSimulation()) { game.informPlayers( controller.getLogName() + " puts a " + permanent.getLogName() + " token onto the battlefield"); } } return true; } return false; }
public boolean checkBlockRestrictions(Game game, int blockersCount) { boolean blockWasLegal = true; if (attackers.isEmpty()) { return blockWasLegal; } if (blockersCount == 1) { List<UUID> toBeRemoved = new ArrayList<>(); for (UUID blockerId : getBlockers()) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null && blocker.getAbilities().containsKey(CantBlockAloneAbility.getInstance().getId())) { blockWasLegal = false; game.informPlayers(blocker.getLogName() + " can't block alone. Removing it from combat."); toBeRemoved.add(blockerId); } } for (UUID blockerId : toBeRemoved) { game.getCombat().removeBlocker(blockerId, game); } if (blockers.isEmpty()) { this.blocked = false; } } for (UUID uuid : attackers) { Permanent attacker = game.getPermanent(uuid); // Check if there are enough blockers to have a legal block if (attacker != null && this.blocked && attacker.getMinBlockedBy() > 1 && blockers.size() > 0 && blockers.size() < attacker.getMinBlockedBy()) { for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null) { blocker.setBlocking(blocker.getBlocking() - 1); } } blockers.clear(); blockerOrder.clear(); this.blocked = false; game.informPlayers( attacker.getLogName() + " can't be blocked except by " + attacker.getMinBlockedBy() + " or more creatures. Blockers discarded."); blockWasLegal = false; } // Check if there are to many blockers (maxBlockedBy = 0 means no restrictions) if (attacker != null && this.blocked && attacker.getMaxBlockedBy() > 0 && attacker.getMaxBlockedBy() < blockers.size()) { for (UUID blockerId : blockers) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null) { blocker.setBlocking(blocker.getBlocking() - 1); } } blockers.clear(); blockerOrder.clear(); this.blocked = false; game.informPlayers( new StringBuilder(attacker.getLogName()) .append(" can't be blocked by more than ") .append(attacker.getMaxBlockedBy()) .append(attacker.getMaxBlockedBy() == 1 ? " creature." : " creatures.") .append(" Blockers discarded.") .toString()); blockWasLegal = false; } } return blockWasLegal; }
private void multiBlockerDamage(boolean first, Game game) { // TODO: handle banding Permanent attacker = game.getPermanent(attackers.get(0)); if (attacker == null) { return; } Player player = game.getPlayer(attacker.getControllerId()); int damage = getDamageValueFromPermanent(attacker, game); if (canDamage(attacker, first)) { // must be set before attacker damage marking because of effects like Test of Faith Map<UUID, Integer> blockerPower = new HashMap<>(); for (UUID blockerId : blockerOrder) { Permanent blocker = game.getPermanent(blockerId); if (canDamage(blocker, first)) { if (blocker.getBlocking() == 1) { // blocking several creatures handled separately blockerPower.put(blockerId, getDamageValueFromPermanent(blocker, game)); } } } Map<UUID, Integer> assigned = new HashMap<>(); if (blocked) { for (UUID blockerId : blockerOrder) { Permanent blocker = game.getPermanent(blockerId); int lethalDamage; if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { lethalDamage = 1; } else { lethalDamage = blocker.getToughness().getValue() - blocker.getDamage(); } if (lethalDamage >= damage) { assigned.put(blockerId, damage); damage = 0; break; } int damageAssigned = player.getAmount( lethalDamage, damage, "Assign damage to " + blocker.getLogName(), game); assigned.put(blockerId, damageAssigned); damage -= damageAssigned; } if (damage > 0 && hasTrample(attacker)) { defenderDamage(attacker, damage, game); } } for (UUID blockerId : blockerOrder) { Integer power = blockerPower.get(blockerId); if (power != null) { attacker.markDamage(power.intValue(), blockerId, game, true, true); } } for (Map.Entry<UUID, Integer> entry : assigned.entrySet()) { Permanent blocker = game.getPermanent(entry.getKey()); blocker.markDamage(entry.getValue(), attacker.getId(), game, true, true); } } else { for (UUID blockerId : blockerOrder) { Permanent blocker = game.getPermanent(blockerId); if (canDamage(blocker, first)) { attacker.markDamage( getDamageValueFromPermanent(blocker, game), blocker.getId(), game, true, true); } } } }