Butcher Orgg!!!

This commit is contained in:
Tim Mocny
2020-12-30 02:35:25 +00:00
committed by Sol
parent 136c99377d
commit f700b9d43b
12 changed files with 87 additions and 21 deletions

View File

@@ -1133,6 +1133,7 @@ public class CardView extends GameEntityView {
public boolean hasDeathtouch() { return get(TrackableProperty.HasDeathtouch); }
public boolean hasDevoid() { return get(TrackableProperty.HasDevoid); }
public boolean hasDefender() { return get(TrackableProperty.HasDefender); }
public boolean hasDivideDamage() { return get(TrackableProperty.HasDivideDamage); }
public boolean hasDoubleStrike() { return get(TrackableProperty.HasDoubleStrike); }
public boolean hasFirstStrike() { return get(TrackableProperty.HasFirstStrike); }
public boolean hasFlying() { return get(TrackableProperty.HasFlying); }
@@ -1173,6 +1174,8 @@ public class CardView extends GameEntityView {
set(TrackableProperty.HasDeathtouch, c.hasKeyword(Keyword.DEATHTOUCH, state));
set(TrackableProperty.HasDevoid, c.hasKeyword(Keyword.DEVOID, state));
set(TrackableProperty.HasDefender, c.hasKeyword(Keyword.DEFENDER, state));
set(TrackableProperty.HasDivideDamage, c.hasKeyword("You may assign CARDNAME's combat damage divided as " +
"you choose among defending player and/or any number of creatures they control."));
set(TrackableProperty.HasDoubleStrike, c.hasKeyword(Keyword.DOUBLE_STRIKE, state));
set(TrackableProperty.HasFirstStrike, c.hasKeyword(Keyword.FIRST_STRIKE, state));
set(TrackableProperty.HasFlying, c.hasKeyword(Keyword.FLYING, state));

View File

@@ -20,21 +20,18 @@ package forge.game.combat;
import com.google.common.base.Function;
import com.google.common.collect.*;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameEntityCounterTable;
import forge.game.GameLogEntryType;
import forge.game.GameObjectMap;
import forge.game.*;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardDamageMap;
import forge.game.card.*;
import forge.game.keyword.Keyword;
import forge.game.player.Player;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.CardTranslation;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
import forge.util.Localizer;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
@@ -64,6 +61,7 @@ public class Combat {
private Map<Card, CardCollection> blockersOrderedForDamageAssignment = Maps.newHashMap();
private Map<GameEntity, CombatLki> lkiCache = Maps.newHashMap();
private CardDamageMap dealtDamageTo = new CardDamageMap();
private boolean dividedToPlayer = false;
// List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW)
private CardCollection combatantsThatDealtFirstStrikeDamage = new CardCollection();
@@ -373,7 +371,7 @@ public class Combat {
blocker.updateBlockingForView();
}
// remove blocked from specific attacker
// remove blocker from specific attacker
public final void removeBlockAssignment(final Card attacker, final Card blocker) {
AttackingBand band = getBandOfAttackerNotNull(attacker);
Collection<Card> cc = blockedBands.get(band);
@@ -400,6 +398,15 @@ public class Combat {
return result;
}
public final CardCollection getDefendersCreatures() {
CardCollection result = new CardCollection();
for (Card attacker : getAttackers()) {
CardCollection cc = getDefenderPlayerByAttacker(attacker).getCreaturesInPlay();
result.addAll(cc);
}
return result;
}
public final CardCollection getBlockers(final AttackingBand band) {
Collection<Card> blockers = blockedBands.get(band);
return blockers == null ? new CardCollection() : new CardCollection(blockers);
@@ -716,8 +723,26 @@ public class Combat {
continue;
}
boolean divideCombatDamageAsChoose = (getDefendersCreatures().size() > 0 &&
attacker.hasKeyword("You may assign CARDNAME's combat damage divided as you choose among " +
"defending player and/or any number of creatures they control.")
&& attacker.getController().getController().confirmAction(null, null,
Localizer.getInstance().getMessage("lblAssignCombatDamageAsChoose",
CardTranslation.getTranslatedName(attacker.getName()))));
boolean trampler = attacker.hasKeyword(Keyword.TRAMPLE);
orderedBlockers = blockersOrderedForDamageAssignment.get(attacker);
if (divideCombatDamageAsChoose) {
if (orderedBlockers == null || orderedBlockers.isEmpty()) {
orderedBlockers = getDefendersCreatures();
}
else {
for (Card c : getDefendersCreatures()) {
if (!orderedBlockers.contains(c)) {
orderedBlockers.add(c);
}
}
}
}
assignedDamage = true;
// If the Attacker is unblocked, or it's a trampler and has 0 blockers, deal damage to defender
if (orderedBlockers == null || orderedBlockers.isEmpty()) {
@@ -730,6 +755,10 @@ public class Combat {
Player assigningPlayer = getAttackingPlayer();
// Defensive Formation is very similar to Banding with Blockers
// It allows the defending player to assign damage instead of the attacking player
if (defender instanceof Card && divideCombatDamageAsChoose) {
defender = getDefenderPlayerByAttacker(attacker);
dividedToPlayer = true;
}
if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) {
assigningPlayer = (Player)defender;
}
@@ -737,7 +766,8 @@ public class Combat {
assigningPlayer = orderedBlockers.get(0).getController();
}
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(attacker, orderedBlockers, damageDealt, defender, getAttackingPlayer() != assigningPlayer);
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(attacker, orderedBlockers,
damageDealt, defender, divideCombatDamageAsChoose || getAttackingPlayer() != assigningPlayer);
for (Entry<Card, Integer> dt : map.entrySet()) {
if (dt.getKey() == null) {
if (dt.getValue() > 0)
@@ -767,7 +797,7 @@ public class Combat {
private final void addDefendingDamage(final int n, final Card source) {
final GameEntity ge = getDefenderByAttacker(source);
if (ge instanceof Card) {
if (ge instanceof Card && !dividedToPlayer) {
final Card planeswalker = (Card) ge;
planeswalker.addAssignedDamage(n, source);
return;
@@ -805,6 +835,9 @@ public class Combat {
// This function handles both Regular and First Strike combat assignment
for (final Entry<Card, Integer> entry : defendingDamageMap.entrySet()) {
GameEntity defender = getDefenderByAttacker(entry.getKey());
if (dividedToPlayer) {
defender = getDefenderPlayerByAttacker(entry.getKey());
}
if (defender instanceof Player) { // player
defender.addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo, preventMap, counterTable);
}
@@ -819,6 +852,7 @@ public class Combat {
combatants.addAll(getAttackers());
combatants.addAll(getAllBlockers());
combatants.addAll(getDefendingPlaneswalkers());
combatants.addAll(getDefendersCreatures());
for (final Card c : combatants) {
// if no assigned damage to resolve, move to next

View File

@@ -100,6 +100,7 @@ public enum TrackableProperty {
HasDeathtouch(TrackableTypes.BooleanType),
HasDevoid(TrackableTypes.BooleanType),
HasDefender(TrackableTypes.BooleanType),
HasDivideDamage(TrackableTypes.BooleanType),
HasDoubleStrike(TrackableTypes.BooleanType),
HasFirstStrike(TrackableTypes.BooleanType),
HasFlying(TrackableTypes.BooleanType),

View File

@@ -74,6 +74,7 @@ public class VAssignCombatDamage {
private final int totalDamageToAssign;
private boolean attackerHasDeathtouch = false;
private boolean attackerHasDivideDamage = false;
private boolean attackerHasTrample = false;
private boolean attackerHasInfect = false;
private boolean overrideCombatantOrder = false;
@@ -152,6 +153,7 @@ public class VAssignCombatDamage {
attackerHasDeathtouch = attacker.getCurrentState().hasDeathtouch();
attackerHasInfect = attacker.getCurrentState().hasInfect();
attackerHasTrample = defender != null && attacker.getCurrentState().hasTrample();
attackerHasDivideDamage = attacker.getCurrentState().hasDivideDamage();
overrideCombatantOrder = overrideOrder;
// Top-level UI stuff
@@ -173,7 +175,7 @@ public class VAssignCombatDamage {
// Defenders area
final JPanel pnlDefenders = new JPanel();
pnlDefenders.setOpaque(false);
int cols = attackerHasTrample ? blockers.size() + 1 : blockers.size();
int cols = ((attackerHasTrample) || (attackerHasDivideDamage && overrideCombatantOrder)) ? blockers.size() + 1 : blockers.size();
final String wrap = "wrap " + cols;
pnlDefenders.setLayout(new MigLayout("insets 0, gap 0, ax center, " + wrap));
@@ -187,7 +189,7 @@ public class VAssignCombatDamage {
addPanelForDefender(pnlDefenders, c);
}
if (attackerHasTrample) {
if ((attackerHasTrample) || (attackerHasDivideDamage && overrideCombatantOrder)) {
final DamageTarget dt = new DamageTarget(null, new FLabel.Builder().text("0").fontSize(18).fontAlign(SwingConstants.CENTER).build());
damage.put(null, dt);
defenders.add(dt);
@@ -275,10 +277,12 @@ public class VAssignCombatDamage {
source = null;
// If trying to assign to the defender, follow the normal assignment rules
// No need to check for "active" creature assignee when overiding combatant order
if ((source == null || source == defender || !overrideCombatantOrder) && isAdding &&
!VAssignCombatDamage.this.canAssignTo(source)) {
return;
// No need to check for "active" creature assignee when overriding combatant order
if (!attackerHasDivideDamage) { // Creatures with this can assign to defender
if ((source == null || source == defender || !overrideCombatantOrder) && isAdding &&
!VAssignCombatDamage.this.canAssignTo(source)) {
return;
}
}
// If lethal damage has already been assigned just act like it's 0.
@@ -316,6 +320,9 @@ public class VAssignCombatDamage {
}
private void checkDamageQueue() {
if (overrideCombatantOrder && attackerHasDivideDamage) {
return;
}
// Clear out any Damage that shouldn't be assigned to other combatants
boolean hasAliveEnemy = false;
for(DamageTarget dt : defenders) {

View File

@@ -61,6 +61,7 @@ public class VAssignCombatDamage extends FDialog {
private final int totalDamageToAssign;
private boolean attackerHasDeathtouch = false;
private boolean attackerHasDivideDamage = false;
private boolean attackerHasTrample = false;
private boolean attackerHasInfect = false;
private boolean overrideCombatantOrder = false;
@@ -102,6 +103,7 @@ public class VAssignCombatDamage extends FDialog {
totalDamageToAssign = damage0;
defender = defender0;
attackerHasDeathtouch = attacker.getCurrentState().hasDeathtouch();
attackerHasDivideDamage = attacker.getCurrentState().hasDivideDamage();
attackerHasInfect = attacker.getCurrentState().hasInfect();
attackerHasTrample = defender != null && attacker.getCurrentState().hasTrample();
overrideCombatantOrder = overrideOrder;
@@ -166,7 +168,7 @@ public class VAssignCombatDamage extends FDialog {
addDamageTarget(c);
}
if (attackerHasTrample) {
if (attackerHasTrample || (attackerHasDivideDamage && overrideCombatantOrder)) {
//add damage target for target of attack that trample damage will go through to
addDamageTarget(null);
}
@@ -298,8 +300,10 @@ public class VAssignCombatDamage extends FDialog {
// If trying to assign to the defender, follow the normal assignment rules
// No need to check for "active" creature assignee when overiding combatant order
if ((source == null || source == defender || !overrideCombatantOrder) && isAdding && !canAssignTo(source)) {
return;
if (!attackerHasDivideDamage) { // Creatures with this can assign to defender
if ((source == null || source == defender || !overrideCombatantOrder) && isAdding && !canAssignTo(source)) {
return;
}
}
// If lethal damage has already been assigned just act like it's 0.
@@ -330,6 +334,9 @@ public class VAssignCombatDamage extends FDialog {
}
private void checkDamageQueue() {
if (overrideCombatantOrder && attackerHasDivideDamage) {
return;
}
// Clear out any Damage that shouldn't be assigned to other combatants
boolean hasAliveEnemy = false;
for (DamageTarget dt : defenders) {

View File

@@ -0,0 +1,7 @@
Name:Butcher Orgg
ManaCost:4 R R R
Types:Creature Orgg
PT:6/6
K:You may assign CARDNAME's combat damage divided as you choose among defending player and/or any number of creatures they control.
AI:RemoveDeck:All
Oracle:You may assign Butcher Orggs combat damage divided as you choose among defending player and/or any number of creatures they control.

View File

@@ -1170,6 +1170,7 @@ lblDraw=Ziehen
lblTooFewCardsMainDeck=Zu wenig Karten in deinem Deck (mindestens {0}). Bitte passe dein Deck an.
lblTooManyCardsSideboard=Zu viele Karten in deinem Deck (maximal {0}). Bitte passe dein Deck an.
lblAssignCombatDamageWerentBlocked=Möchtest du den Kampfschaden deklarieren, als wäre nicht geblockt worden?
lblAssignCombatDamageAsChoose=Do you want to divide {0}''s combat damage as you choose?
lblChosenCards=Wähle Karten
lblAttacker=Angreifer
lblTriggeredby=Ausgelöst durch

View File

@@ -1170,6 +1170,7 @@ lblDraw=Draw
lblTooFewCardsMainDeck=Too few cards in your main deck (minimum {0}), please make modifications to your deck again.
lblTooManyCardsSideboard=Too many cards in your sideboard (maximum {0}), please make modifications to your deck again.
lblAssignCombatDamageWerentBlocked=Do you want to assign its combat damage as though it weren''t blocked?
lblAssignCombatDamageAsChoose=Do you want to divide {0}''s combat damage as you choose?
lblChosenCards=Chosen Cards
lblAttacker=Attacker
lblTriggeredby=Triggered by

View File

@@ -1170,6 +1170,7 @@ lblDraw=Ceder
lblTooFewCardsMainDeck=Muy pocas cartas en tu mazo principal (mínimo {0}), por favor realiza modificaciones a tu mazo de nuevo.
lblTooManyCardsSideboard=Demasiadas cartas en tu banquillo (máximo {0}), por favor realiza modificaciones a tu mazo de nuevo.
lblAssignCombatDamageWerentBlocked=¿Quieres asignar su daño de combate como si no estuviera bloqueado?
lblAssignCombatDamageAsChoose=¿Quieres dividir el daño de combate del {0} como elijas?
lblChosenCards=Cartas elegidas
lblAttacker=Atacante
lblTriggeredby=Activado por

View File

@@ -1170,6 +1170,7 @@ lblDraw=Disegnare
lblTooFewCardsMainDeck=Troppe carte nel tuo mazzo principale (minimo %s), per favore apporta nuovamente modifiche al tuo mazzo.
lblTooManyCardsSideboard=Troppe carte nel tuo sideboard (massimo %s), modifica nuovamente il tuo mazzo.
lblAssignCombatDamageWerentBlocked=Vuoi assegnare il suo danno da combattimento come se non fosse bloccato?
lblAssignCombatDamageAsChoose=Vuoi suddividere come preferisci il danno da combattimento dell''{0}?
lblChosenCards=Carte scelte
lblAttacker=aggressore
lblTriggeredby=Innescato da

View File

@@ -1170,6 +1170,7 @@ lblDraw=后手
lblTooFewCardsMainDeck=主牌中卡牌数过少(最少为{0}),请重新修改套牌。
lblTooManyCardsSideboard=备牌中卡牌数过多(最多为{0}),请重新修改套牌。
lblAssignCombatDamageWerentBlocked=是否要像没有被阻挡一样分配战斗伤害?
lblAssignCombatDamageAsChoose=你想要按你所选的分配方式对{0}造成战斗伤害吗?
lblChosenCards=选择牌
lblAttacker=进攻者
lblTriggeredby=触发者

View File

@@ -286,7 +286,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
if (defender != null && assignDamageAsIfNotBlocked(attacker)) {
map.put(null, damageDealt);
} else {
if ((attacker.hasKeyword(Keyword.TRAMPLE) && defender != null) || (blockers.size() > 1)) {
if ((attacker.hasKeyword(Keyword.TRAMPLE) && defender != null) || (blockers.size() > 1)
|| (attacker.hasKeyword("You may assign CARDNAME's combat damage divided as you choose among defending" +
" player and/or any number of creatures they control.")) && overrideOrder && blockers.size() >0) {
GameEntityViewMap<Card, CardView> gameCacheBlockers = GameEntityView.getMap(blockers);
final CardView vAttacker = CardView.get(attacker);
final GameEntityView vDefender = GameEntityView.get(defender);