mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 09:48:02 +00:00
Butcher Orgg!!!
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
7
forge-gui/res/cardsfolder/b/butcher_orgg.txt
Normal file
7
forge-gui/res/cardsfolder/b/butcher_orgg.txt
Normal 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 Orgg’s combat damage divided as you choose among defending player and/or any number of creatures they control.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1170,6 +1170,7 @@ lblDraw=后手
|
||||
lblTooFewCardsMainDeck=主牌中卡牌数过少(最少为{0}),请重新修改套牌。
|
||||
lblTooManyCardsSideboard=备牌中卡牌数过多(最多为{0}),请重新修改套牌。
|
||||
lblAssignCombatDamageWerentBlocked=是否要像没有被阻挡一样分配战斗伤害?
|
||||
lblAssignCombatDamageAsChoose=你想要按你所选的分配方式对{0}造成战斗伤害吗?
|
||||
lblChosenCards=选择牌
|
||||
lblAttacker=进攻者
|
||||
lblTriggeredby=触发者
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user