mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Merge remote-tracking branch 'upstream/master' into display_localization_cardname
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.33-SNAPSHOT</version>
|
||||
<version>1.6.34-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-game</artifactId>
|
||||
|
||||
@@ -914,4 +914,17 @@ public class Game {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Player getControlVote() {
|
||||
Player result = null;
|
||||
long maxValue = 0;
|
||||
for (Player p : getPlayers()) {
|
||||
Long v = p.getHighestControlVote();
|
||||
if (v != null && v > maxValue) {
|
||||
maxValue = v;
|
||||
result = p;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ public class GameAction {
|
||||
// the LKI needs to be the Card itself,
|
||||
// or it might not updated correctly
|
||||
// TODO be reworked when ZoneTrigger Update is done
|
||||
if (toBattlefield) {
|
||||
if (toBattlefield || zoneTo.is(ZoneType.Stack)) {
|
||||
lastKnownInfo = c;
|
||||
}
|
||||
|
||||
@@ -547,6 +547,13 @@ public class GameAction {
|
||||
c.setCastSA(null);
|
||||
} else if (zoneTo.is(ZoneType.Stack)) {
|
||||
c.setCastFrom(zoneFrom.getZoneType());
|
||||
if (cause != null && cause.isSpell() && c.equals(cause.getHostCard()) && !c.isCopiedSpell()) {
|
||||
cause.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
cause.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
c.setCastSA(cause);
|
||||
} else {
|
||||
c.setCastSA(null);
|
||||
}
|
||||
} else if (!(zoneTo.is(ZoneType.Battlefield) && zoneFrom.is(ZoneType.Stack))) {
|
||||
c.setCastFrom(null);
|
||||
c.setCastSA(null);
|
||||
@@ -565,16 +572,31 @@ public class GameAction {
|
||||
public final void controllerChangeZoneCorrection(final Card c) {
|
||||
System.out.println("Correcting zone for " + c.toString());
|
||||
final Zone oldBattlefield = game.getZoneOf(c);
|
||||
if (oldBattlefield == null || oldBattlefield.getZoneType() == ZoneType.Stack) {
|
||||
|
||||
if (oldBattlefield == null || oldBattlefield.is(ZoneType.Stack)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Player original = oldBattlefield.getPlayer();
|
||||
final PlayerZone newBattlefield = c.getController().getZone(oldBattlefield.getZoneType());
|
||||
final Player controller = c.getController();
|
||||
if (original == null || controller == null || original.equals(controller)) {
|
||||
return;
|
||||
}
|
||||
final PlayerZone newBattlefield = controller.getZone(oldBattlefield.getZoneType());
|
||||
|
||||
if (newBattlefield == null || oldBattlefield.equals(newBattlefield)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 702.94e A paired creature becomes unpaired if any of the following occur:
|
||||
// another player gains control of it or the creature it’s paired with
|
||||
if (c.isPaired()) {
|
||||
Card partner = c.getPairedWith();
|
||||
c.setPairedWith(null);
|
||||
partner.setPairedWith(null);
|
||||
partner.updateStateForView();
|
||||
}
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
for (Player p : game.getPlayers()) {
|
||||
((PlayerZoneBattlefield) p.getZone(ZoneType.Battlefield)).setTriggers(false);
|
||||
|
||||
@@ -22,9 +22,10 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostParser;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
@@ -33,9 +34,15 @@ import forge.game.cost.Cost;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -78,68 +85,9 @@ public final class GameActionUtil {
|
||||
if (sa.isSpell() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
// need to be done before so it works with Vivien and Zoetic Cavern
|
||||
if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.turnFaceUp(false, false);
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (sa.isBestow() && !source.isBestowed() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.animateBestow(false);
|
||||
lkicheck = true;
|
||||
} else if (sa.isCastFaceDown()) {
|
||||
// need a copy of the card to turn facedown without trigger anything
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
source.turnFaceDownNoUpdate();
|
||||
lkicheck = true;
|
||||
} else if (sa.isAdventure()) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.Adventure, false);
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
lkicheck = true;
|
||||
} else if (source.isSplitCard() && (sa.isLeftSplit() || sa.isRightSplit())) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
if (sa.isLeftSplit()) {
|
||||
if (!source.hasState(CardStateName.LeftSplit)) {
|
||||
source.addAlternateState(CardStateName.LeftSplit, false);
|
||||
source.getState(CardStateName.LeftSplit).copyFrom(
|
||||
sa.getHostCard().getState(CardStateName.LeftSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.LeftSplit, false);
|
||||
}
|
||||
|
||||
if (sa.isRightSplit()) {
|
||||
if (!source.hasState(CardStateName.RightSplit)) {
|
||||
source.addAlternateState(CardStateName.RightSplit, false);
|
||||
source.getState(CardStateName.RightSplit).copyFrom(
|
||||
sa.getHostCard().getState(CardStateName.RightSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.RightSplit, false);
|
||||
}
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
Card newHost = ((Spell)sa).getAlternateHost(source);
|
||||
if (newHost != null) {
|
||||
source = newHost;
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
@@ -218,6 +166,7 @@ public final class GameActionUtil {
|
||||
final Cost escapeCost = new Cost(k[1], true);
|
||||
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(escapeCost);
|
||||
newSA.setActivatingPlayer(activator);
|
||||
|
||||
newSA.getMapParams().put("PrecostDesc", "Escape—");
|
||||
newSA.getMapParams().put("CostDesc", escapeCost.toString());
|
||||
@@ -276,6 +225,7 @@ public final class GameActionUtil {
|
||||
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
|
||||
// set the cost to this directly to buypass non mana cost
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>");
|
||||
newSA.setActivatingPlayer(activator);
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.getMapParams().put("CostDesc", ManaCostParser.parse("0"));
|
||||
// makes new SpellDescription
|
||||
@@ -421,10 +371,11 @@ public final class GameActionUtil {
|
||||
}
|
||||
SpellAbility result = null;
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final PlayerController pc = activator.getController();
|
||||
|
||||
host.getGame().getAction().checkStaticAbilities(false);
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
|
||||
boolean reset = false;
|
||||
|
||||
@@ -487,7 +438,60 @@ public final class GameActionUtil {
|
||||
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
||||
|
||||
if (v > 0) {
|
||||
host.addReplacementEffect(CardFactoryUtil.makeEtbCounter("etbCounter:P1P1:" + v, host, false));
|
||||
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(c.getName() + "'s Effect");
|
||||
eff.addType("Effect");
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||
eff.setOwner(activator);
|
||||
|
||||
eff.setImageKey(c.getImageKey());
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
eff.setImmutable(true);
|
||||
// try to get the SpellAbility from the mana ability
|
||||
//eff.setEffectSource((SpellAbility)null);
|
||||
|
||||
eff.addRemembered(host);
|
||||
|
||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ P1P1 | ETB$ True | CounterNum$ " + v;
|
||||
|
||||
SpellAbility saAb = AbilityFactory.getAbility(abStr, c);
|
||||
|
||||
CardFactoryUtil.setupETBReplacementAbility(saAb);
|
||||
|
||||
String desc = "It enters the battlefield with ";
|
||||
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
|
||||
desc += " on it.";
|
||||
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||
re.setLayer(ReplacementLayer.Other);
|
||||
re.setOverridingAbility(saAb);
|
||||
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
// Forgot Trigger
|
||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
eff.addTrigger(parsedTrigger);
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
|
||||
if (result == null) {
|
||||
result = sa.copy();
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -37,7 +37,7 @@ import com.google.common.collect.Maps;
|
||||
* <p>
|
||||
* StaticEffect class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -72,7 +72,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* setTimestamp TODO Write javadoc for this method.
|
||||
*
|
||||
*
|
||||
* @param t
|
||||
* a long
|
||||
*/
|
||||
@@ -82,7 +82,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* getTimestamp. TODO Write javadoc for this method.
|
||||
*
|
||||
*
|
||||
* @return a long
|
||||
*/
|
||||
public final long getTimestamp() {
|
||||
@@ -93,7 +93,7 @@ public class StaticEffect {
|
||||
* <p>
|
||||
* Getter for the field <code>source</code>.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link forge.game.card.Card} object.
|
||||
*/
|
||||
public final Card getSource() {
|
||||
@@ -104,7 +104,7 @@ public class StaticEffect {
|
||||
* <p>
|
||||
* Getter for the field <code>affectedCards</code>.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
public final CardCollectionView getAffectedCards() {
|
||||
@@ -115,7 +115,7 @@ public class StaticEffect {
|
||||
* <p>
|
||||
* Setter for the field <code>affectedCards</code>.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param list
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
@@ -125,7 +125,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* Gets the affected players.
|
||||
*
|
||||
*
|
||||
* @return the affected players
|
||||
*/
|
||||
public final List<Player> getAffectedPlayers() {
|
||||
@@ -134,7 +134,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* Sets the affected players.
|
||||
*
|
||||
*
|
||||
* @param list
|
||||
* the new affected players
|
||||
*/
|
||||
@@ -144,7 +144,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* setParams. TODO Write javadoc for this method.
|
||||
*
|
||||
*
|
||||
* @param params
|
||||
* a HashMap
|
||||
*/
|
||||
@@ -154,7 +154,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* Gets the params.
|
||||
*
|
||||
*
|
||||
* @return the params
|
||||
*/
|
||||
public final Map<String, String> getParams() {
|
||||
@@ -171,13 +171,12 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* Undo everything that was changed by this effect.
|
||||
*
|
||||
*
|
||||
* @return a {@link CardCollectionView} of all affected cards.
|
||||
*/
|
||||
final CardCollectionView remove() {
|
||||
final CardCollectionView affectedCards = getAffectedCards();
|
||||
final List<Player> affectedPlayers = getAffectedPlayers();
|
||||
//final Map<String, String> params = getParams();
|
||||
|
||||
String changeColorWordsTo = null;
|
||||
|
||||
@@ -245,6 +244,10 @@ public class StaticEffect {
|
||||
|
||||
p.removeMaxLandPlays(getTimestamp());
|
||||
p.removeMaxLandPlaysInfinite(getTimestamp());
|
||||
|
||||
p.removeControlVote(getTimestamp());
|
||||
p.removeAdditionalVote(getTimestamp());
|
||||
p.removeAdditionalOptionalVote(getTimestamp());
|
||||
}
|
||||
|
||||
// modify the affected card
|
||||
|
||||
@@ -1318,9 +1318,16 @@ public class AbilityUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
Player pl = sa.getActivatingPlayer();
|
||||
final Game game = pl.getGame();
|
||||
|
||||
if (sa.isTrigger() && sa.getParent() == null && sa.getPayCosts() != null) {
|
||||
// when trigger cost are paid before the effect does resolve, need to clean the trigger
|
||||
game.getTriggerHandler().resetActiveTriggers();
|
||||
}
|
||||
|
||||
// do blessing there before condition checks
|
||||
if (sa.isSpell() && sa.isBlessing() && !sa.getHostCard().isPermanent()) {
|
||||
Player pl = sa.getActivatingPlayer();
|
||||
if (pl != null && pl.getZone(ZoneType.Battlefield).size() >= 10) {
|
||||
pl.setBlessing(true);
|
||||
}
|
||||
@@ -1335,7 +1342,7 @@ public class AbilityUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityUtils.resolveApiAbility(sa, sa.getActivatingPlayer().getGame());
|
||||
AbilityUtils.resolveApiAbility(sa, game);
|
||||
}
|
||||
|
||||
private static void resolveSubAbilities(final SpellAbility sa, final Game game) {
|
||||
|
||||
@@ -62,7 +62,9 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("ChooseAnObject")) {
|
||||
Card c = p.getController().chooseSingleEntityForEffect(attachments, sa, sa.getParam("ChooseAnObject"));
|
||||
attachments.clear();
|
||||
attachments.add(c);
|
||||
if (c != null) {
|
||||
attachments.add(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
attachments = new CardCollection(source);
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.util.List;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -18,7 +17,6 @@ import forge.game.combat.Combat;
|
||||
import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.Ability;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.collect.FCollectionView;
|
||||
@@ -26,9 +24,7 @@ import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
public class ControlGainEffect extends SpellAbilityEffect {
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.abilityfactory.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
@@ -67,15 +63,17 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
if (null == c || c.hasKeyword("Other players can't gain control of CARDNAME.")) {
|
||||
return;
|
||||
}
|
||||
final Game game = host.getGame();
|
||||
if (c.isInPlay()) {
|
||||
c.removeTempController(tStamp);
|
||||
|
||||
game.getAction().controllerChangeZoneCorrection(c);
|
||||
|
||||
if (tapOnLose) {
|
||||
c.tap();
|
||||
}
|
||||
} // if
|
||||
host.removeGainControlTargets(c);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,11 +82,9 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
|
||||
final boolean bUntap = sa.hasParam("Untap");
|
||||
final boolean bTapOnLose = sa.hasParam("TapOnLose");
|
||||
final boolean bNoRegen = sa.hasParam("NoRegen");
|
||||
final boolean remember = sa.hasParam("RememberControlled");
|
||||
final boolean forget = sa.hasParam("ForgetControlled");
|
||||
final boolean attacking = sa.hasParam("Attacking");
|
||||
final List<String> destroyOn = sa.hasParam("DestroyTgt") ? Arrays.asList(sa.getParam("DestroyTgt").split(",")) : null;
|
||||
final List<String> keywords = sa.hasParam("AddKWs") ? Arrays.asList(sa.getParam("AddKWs").split(" & ")) : null;
|
||||
final List<String> lose = sa.hasParam("LoseControl") ? Arrays.asList(sa.getParam("LoseControl").split(",")) : null;
|
||||
|
||||
@@ -189,18 +185,6 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyOn != null) {
|
||||
if (destroyOn.contains("LeavesPlay")) {
|
||||
sa.getHostCard().addLeavesPlayCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
if (destroyOn.contains("Untap")) {
|
||||
sa.getHostCard().addUntapCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
if (destroyOn.contains("LoseControl")) {
|
||||
sa.getHostCard().addChangeControllerCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
}
|
||||
|
||||
if (keywords != null) {
|
||||
// Add keywords only until end of turn
|
||||
final GameCommand untilKeywordEOT = new GameCommand() {
|
||||
@@ -241,43 +225,6 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
} // end foreach target
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getDestroyCommand.
|
||||
* </p>
|
||||
*
|
||||
* @param i
|
||||
* a int.
|
||||
* @return a {@link forge.GameCommand} object.
|
||||
*/
|
||||
private static GameCommand getDestroyCommand(final Card c, final Card hostCard, final boolean bNoRegen) {
|
||||
final GameCommand destroy = new GameCommand() {
|
||||
private static final long serialVersionUID = 878543373519872418L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Game game = hostCard.getGame();
|
||||
final Ability ability = new Ability(hostCard, ManaCost.ZERO) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
game.getAction().destroy(c, null, !bNoRegen, null);
|
||||
}
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(hostCard).append(" - destroy ").append(c.getName()).append(".");
|
||||
if (bNoRegen) {
|
||||
sb.append(" It can't be regenerated.");
|
||||
}
|
||||
ability.setStackDescription(sb.toString());
|
||||
ability.setTrigger(true);
|
||||
|
||||
game.getStack().addSimultaneousStackEntry(ability);
|
||||
}
|
||||
|
||||
};
|
||||
return destroy;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getLoseControlCommand.
|
||||
|
||||
@@ -35,61 +35,77 @@ import java.util.List;
|
||||
|
||||
public class CountersPutEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Card card = sa.getHostCard();
|
||||
final boolean dividedAsYouChoose = sa.hasParam("DividedAsYouChoose");
|
||||
protected String getStackDescription(SpellAbility spellAbility) {
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
final Card card = spellAbility.getHostCard();
|
||||
final boolean dividedAsYouChoose = spellAbility.hasParam("DividedAsYouChoose");
|
||||
|
||||
|
||||
final int amount = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("CounterNum", "1"), sa);
|
||||
if (sa.hasParam("Bolster")) {
|
||||
sb.append("Bolster ").append(amount);
|
||||
return sb.toString();
|
||||
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility);
|
||||
if (spellAbility.hasParam("Bolster")) {
|
||||
stringBuilder.append("Bolster ").append(amount);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
if (dividedAsYouChoose) {
|
||||
sb.append("Distribute ");
|
||||
stringBuilder.append("Distribute ");
|
||||
} else {
|
||||
sb.append("Put ");
|
||||
stringBuilder.append("Put ");
|
||||
}
|
||||
if (sa.hasParam("UpTo")) {
|
||||
sb.append("up to ");
|
||||
if (spellAbility.hasParam("UpTo")) {
|
||||
stringBuilder.append("up to ");
|
||||
}
|
||||
|
||||
sb.append(amount).append(" ");
|
||||
stringBuilder.append(amount).append(" ");
|
||||
|
||||
String type = sa.getParam("CounterType");
|
||||
String type = spellAbility.getParam("CounterType");
|
||||
if (type.equals("ExistingCounter")) {
|
||||
sb.append("of an existing counter");
|
||||
stringBuilder.append("of an existing counter");
|
||||
} else {
|
||||
|
||||
sb.append( CounterType.valueOf(type).getName()).append(" counter");
|
||||
stringBuilder.append(CounterType.valueOf(type).getName()).append(" counter");
|
||||
}
|
||||
|
||||
if (amount != 1) {
|
||||
sb.append("s");
|
||||
stringBuilder.append("s");
|
||||
}
|
||||
|
||||
if (dividedAsYouChoose) {
|
||||
sb.append(" among ");
|
||||
stringBuilder.append(" among ");
|
||||
} else {
|
||||
sb.append(" on ");
|
||||
stringBuilder.append(" on ");
|
||||
}
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
|
||||
final Iterator<Card> it = tgtCards.iterator();
|
||||
while (it.hasNext()) {
|
||||
final Card tgtC = it.next();
|
||||
if (tgtC.isFaceDown()) {
|
||||
sb.append("Morph");
|
||||
} else {
|
||||
sb.append(tgtC);
|
||||
// if use targeting we show all targets and corresponding counters
|
||||
if(spellAbility.usesTargeting()) {
|
||||
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
|
||||
for(int i = 0; i < targetCards.size(); i++) {
|
||||
Card targetCard = targetCards.get(i);
|
||||
stringBuilder.append(targetCard).append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" counter)");
|
||||
|
||||
if(i == targetCards.size() - 2) {
|
||||
stringBuilder.append(" and ");
|
||||
}
|
||||
else if(i + 1 < targetCards.size()) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
|
||||
final Iterator<Card> it = targetCards.iterator();
|
||||
while (it.hasNext()) {
|
||||
final Card targetCard = it.next();
|
||||
if (targetCard.isFaceDown()) {
|
||||
stringBuilder.append("Morph");
|
||||
} else {
|
||||
stringBuilder.append(targetCard);
|
||||
}
|
||||
|
||||
if (it.hasNext()) {
|
||||
sb.append(", ");
|
||||
if (it.hasNext()) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(".");
|
||||
stringBuilder.append(".");
|
||||
|
||||
return sb.toString();
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,7 +172,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
if (existingCounter) {
|
||||
final List<CounterType> choices = Lists.newArrayList();
|
||||
if (obj instanceof GameEntity) {
|
||||
GameEntity entity = (GameEntity)obj;
|
||||
GameEntity entity = (GameEntity) obj;
|
||||
// get types of counters
|
||||
for (CounterType ct : entity.getCounters().keySet()) {
|
||||
if (entity.canReceiveCounters(ct)) {
|
||||
@@ -166,7 +182,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (eachExistingCounter) {
|
||||
for(CounterType ct : choices) {
|
||||
for (CounterType ct : choices) {
|
||||
if (obj instanceof Player) {
|
||||
((Player) obj).addCounter(ct, counterAmount, placer, true, table);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.game.Game;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardDamageMap;
|
||||
@@ -25,45 +26,86 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
* @see forge.game.ability.SpellAbilityEffect#getStackDescription(forge.game.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
protected String getStackDescription(SpellAbility spellAbility) {
|
||||
// when damageStackDescription is called, just build exactly what is happening
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final String damage = sa.getParam("NumDmg");
|
||||
final int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
final String damage = spellAbility.getParam("NumDmg");
|
||||
final int dmg = AbilityUtils.calculateAmount(spellAbility.getHostCard(), damage, spellAbility);
|
||||
|
||||
List<GameObject> tgts = getTargets(sa);
|
||||
if (tgts.isEmpty())
|
||||
List<GameObject> targets = SpellAbilityEffect.getTargets(spellAbility);
|
||||
if (targets.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DamageSource"), sa);
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(spellAbility.getHostCard(), spellAbility.getParam("DamageSource"), spellAbility);
|
||||
|
||||
if (!definedSources.isEmpty() && definedSources.get(0) != sa.getHostCard()) {
|
||||
sb.append(definedSources.get(0).toString()).append(" deals");
|
||||
if (!definedSources.isEmpty() && definedSources.get(0) != spellAbility.getHostCard()) {
|
||||
stringBuilder.append(definedSources.get(0).toString()).append(" deals");
|
||||
} else {
|
||||
sb.append("Deals");
|
||||
stringBuilder.append("Deals");
|
||||
}
|
||||
|
||||
sb.append(" ").append(dmg).append(" damage ");
|
||||
stringBuilder.append(" ").append(dmg).append(" damage ");
|
||||
|
||||
if (sa.hasParam("DivideEvenly")) {
|
||||
sb.append("divided evenly (rounded down) ");
|
||||
} else if (sa.hasParam("DividedAsYouChoose")) {
|
||||
sb.append("divided as you choose ");
|
||||
// if use targeting we show all targets and corresponding damage
|
||||
if (spellAbility.usesTargeting()) {
|
||||
if (spellAbility.hasParam("DivideEvenly")) {
|
||||
stringBuilder.append("divided evenly (rounded down) to\n");
|
||||
} else if (spellAbility.hasParam("DividedAsYouChoose")) {
|
||||
stringBuilder.append("divided to\n");
|
||||
}
|
||||
|
||||
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
|
||||
final List<Player> players = SpellAbilityEffect.getTargetPlayers(spellAbility);
|
||||
|
||||
int targetCount = targetCards.size() + players.size();
|
||||
|
||||
// target cards
|
||||
for (int i = 0; i < targetCards.size(); i++) {
|
||||
Card targetCard = targetCards.get(i);
|
||||
stringBuilder.append(targetCard).append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" damage)");
|
||||
|
||||
if (i == targetCount - 2) {
|
||||
stringBuilder.append(" and ");
|
||||
} else if (i + 1 < targetCount) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
// target players
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
|
||||
Player targetPlayer = players.get(i);
|
||||
stringBuilder.append(targetPlayer).append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetPlayer)).append(" damage)");
|
||||
|
||||
if (i == players.size() - 2) {
|
||||
stringBuilder.append(" and ");
|
||||
} else if (i + 1 < players.size()) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (spellAbility.hasParam("DivideEvenly")) {
|
||||
stringBuilder.append("divided evenly (rounded down) ");
|
||||
} else if (spellAbility.hasParam("DividedAsYouChoose")) {
|
||||
stringBuilder.append("divided as you choose ");
|
||||
}
|
||||
stringBuilder.append("to ").append(Lang.joinHomogenous(targets));
|
||||
}
|
||||
sb.append("to ").append(Lang.joinHomogenous(tgts));
|
||||
|
||||
if (sa.hasParam("Radiance")) {
|
||||
sb.append(" and each other ").append(sa.getParam("ValidTgts"))
|
||||
if (spellAbility.hasParam("Radiance")) {
|
||||
stringBuilder.append(" and each other ").append(spellAbility.getParam("ValidTgts"))
|
||||
.append(" that shares a color with ");
|
||||
if (tgts.size() > 1) {
|
||||
sb.append("them");
|
||||
if (targets.size() > 1) {
|
||||
stringBuilder.append("them");
|
||||
} else {
|
||||
sb.append("it");
|
||||
stringBuilder.append("it");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(". ");
|
||||
return sb.toString();
|
||||
stringBuilder.append(".");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -153,7 +195,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
// Do we have a way of doing this in a better fashion?
|
||||
for (GameObject obj : tgts) {
|
||||
if (obj instanceof Card) {
|
||||
assigneeCards.add((Card)obj);
|
||||
assigneeCards.add((Card) obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +237,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
||||
c.setDamage(0);
|
||||
c.setHasBeenDealtDeathtouchDamage(false);
|
||||
c.clearAssignedDamage();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
c.addDamage(dmg, sourceLKI, false, noPrevention, damageMap, preventMap, counterTable, sa);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class DebuffEffect extends SpellAbilityEffect {
|
||||
for (final Card tgtC : getTargetCards(sa)) {
|
||||
final List<String> addedKW = Lists.newArrayList();
|
||||
final List<String> removedKW = Lists.newArrayList();
|
||||
if (tgtC.isInPlay() && tgtC.canBeTargetedBy(sa)) {
|
||||
if (tgtC.isInPlay() && (!sa.usesTargeting() || tgtC.canBeTargetedBy(sa))) {
|
||||
if (sa.hasParam("AllSuffixKeywords")) {
|
||||
String suffix = sa.getParam("AllSuffixKeywords");
|
||||
for (final KeywordInterface kw : tgtC.getKeywords()) {
|
||||
|
||||
@@ -1,34 +1,62 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class MustBlockEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final Game game = activator.getGame();
|
||||
|
||||
List<Card> tgtCards = Lists.newArrayList();
|
||||
if (sa.hasParam("Choices")) {
|
||||
Player chooser = activator;
|
||||
if (sa.hasParam("Chooser")) {
|
||||
final String choose = sa.getParam("Chooser");
|
||||
chooser = AbilityUtils.getDefinedPlayers(sa.getHostCard(), choose, sa).get(0);
|
||||
}
|
||||
|
||||
CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
||||
if (!choices.isEmpty()) {
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") +" ";
|
||||
|
||||
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||
|
||||
if (choosen != null) {
|
||||
tgtCards.add(choosen);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tgtCards = getTargetCards(sa);
|
||||
}
|
||||
|
||||
List<Card> tgtCards = getTargetCards(sa);
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
final boolean mustBlockAll = sa.hasParam("BlockAllDefined");
|
||||
|
||||
List<Card> cards;
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
||||
} else {
|
||||
cards = new ArrayList<>();
|
||||
cards.add(host);
|
||||
cards = Lists.newArrayList(host);
|
||||
}
|
||||
|
||||
for (final Card c : tgtCards) {
|
||||
if ((tgt == null) || c.canBeTargetedBy(sa)) {
|
||||
if ((!sa.usesTargeting()) || c.canBeTargetedBy(sa)) {
|
||||
if (mustBlockAll) {
|
||||
c.addMustBlockCards(cards);
|
||||
} else {
|
||||
@@ -48,8 +76,6 @@ public class MustBlockEffect extends SpellAbilityEffect {
|
||||
|
||||
// end standard pre-
|
||||
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
|
||||
String attacker = null;
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
||||
@@ -58,10 +84,13 @@ public class MustBlockEffect extends SpellAbilityEffect {
|
||||
attacker = host.toString();
|
||||
}
|
||||
|
||||
for (final Card c : tgtCards) {
|
||||
sb.append(c).append(" must block ").append(attacker).append(" if able.");
|
||||
if (sa.hasParam("Choices")) {
|
||||
sb.append("Choosen creature ").append(" must block ").append(attacker).append(" if able.");
|
||||
} else {
|
||||
for (final Card c : getTargetCards(sa)) {
|
||||
sb.append(c).append(" must block ").append(attacker).append(" if able.");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -9,9 +10,9 @@ import forge.game.ability.AbilityKey;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
@@ -19,10 +20,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -67,33 +65,29 @@ public class VoteEffect extends SpellAbilityEffect {
|
||||
return;
|
||||
}
|
||||
|
||||
// starting with the activator
|
||||
int pSize = tgtPlayers.size();
|
||||
Player activator = sa.getActivatingPlayer();
|
||||
while (tgtPlayers.contains(activator) && !activator.equals(Iterables.getFirst(tgtPlayers, null))) {
|
||||
tgtPlayers.add(pSize - 1, tgtPlayers.remove(0));
|
||||
|
||||
// starting with the activator
|
||||
int aidx = tgtPlayers.indexOf(activator);
|
||||
if (aidx != -1) {
|
||||
Collections.rotate(tgtPlayers, -aidx);
|
||||
}
|
||||
|
||||
ListMultimap<Object, Player> votes = ArrayListMultimap.create();
|
||||
|
||||
Player voter = null;
|
||||
|
||||
PlayerCollection voters = game.getPlayers().filter(PlayerPredicates.hasKeyword("You choose how each player votes this turn."));
|
||||
|
||||
if (voters.size() > 1) {
|
||||
List<Card> illusions = CardLists.filter(voters.getCardsIn(ZoneType.Command), CardPredicates.nameEquals("Illusion of Choice Effect"));
|
||||
voter = Collections.max(illusions, CardPredicates.compareByTimestamp()).getController();
|
||||
} else if (voters.size() == 1) {
|
||||
voter = voters.get(0);
|
||||
}
|
||||
Player voter = game.getControlVote();
|
||||
|
||||
for (final Player p : tgtPlayers) {
|
||||
int voteAmount = p.getKeywords().getAmount("You get an additional vote.") + 1;
|
||||
int optionalVotes = p.getKeywords().getAmount("You may vote an additional time.");
|
||||
voteAmount += p.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyAdditionalVotesDoYouWant"), 0, optionalVotes);
|
||||
int voteAmount = p.getAdditionalVotesAmount() + 1;
|
||||
int optionalVotes = p.getAdditionalOptionalVotesAmount();
|
||||
Player realVoter = voter == null ? p : voter;
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Voter", realVoter);
|
||||
voteAmount += p.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyAdditionalVotesDoYouWant"), 0, optionalVotes, params);
|
||||
|
||||
for (int i = 0; i < voteAmount; i++) {
|
||||
Object result = realVoter.getController().vote(sa, host + Localizer.getInstance().getMessage("lblVote") + ":", voteType, votes);
|
||||
Object result = realVoter.getController().vote(sa, host + Localizer.getInstance().getMessage("lblVote") + ":", voteType, votes, p);
|
||||
|
||||
votes.put(result, p);
|
||||
host.getGame().getAction().nofityOfValue(sa, p, result + "\r\n" + Localizer.getInstance().getMessage("lblCurrentVote") + ":" + votes, p);
|
||||
@@ -104,34 +98,49 @@ public class VoteEffect extends SpellAbilityEffect {
|
||||
runParams.put(AbilityKey.AllVotes, votes);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Vote, runParams, false);
|
||||
|
||||
List<String> subAbs = Lists.newArrayList();
|
||||
final List<Object> mostVotes = getMostVotes(votes);
|
||||
if (sa.hasParam("Tied") && mostVotes.size() > 1) {
|
||||
subAbs.add(sa.getParam("Tied"));
|
||||
} else if (sa.hasParam("VoteSubAbility")) {
|
||||
for (final Object o : mostVotes) {
|
||||
host.addRemembered(o);
|
||||
}
|
||||
subAbs.add(sa.getParam("VoteSubAbility"));
|
||||
} else {
|
||||
for (Object type : mostVotes) {
|
||||
subAbs.add(sa.getParam("Vote" + type.toString()));
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("StoreVoteNum")) {
|
||||
for (final Object type : voteType) {
|
||||
host.setSVar("VoteNum" + type, "Number$" + votes.get(type).size());
|
||||
}
|
||||
} else {
|
||||
for (final String subAb : subAbs) {
|
||||
final SpellAbility action = AbilityFactory.getAbility(host.getSVar(subAb), host);
|
||||
if (sa.hasParam("EachVote")) {
|
||||
for (Map.Entry<Object, Collection<Player>> e : votes.asMap().entrySet()) {
|
||||
final SpellAbility action = AbilityFactory.getAbility(host, sa.getParam("Vote" + e.getKey().toString()));
|
||||
|
||||
action.setActivatingPlayer(sa.getActivatingPlayer());
|
||||
((AbilitySub) action).setParent(sa);
|
||||
AbilityUtils.resolve(action);
|
||||
|
||||
for (Player p : e.getValue()) {
|
||||
host.addRemembered(p);
|
||||
AbilityUtils.resolve(action);
|
||||
host.removeRemembered(p);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<String> subAbs = Lists.newArrayList();
|
||||
final List<Object> mostVotes = getMostVotes(votes);
|
||||
if (sa.hasParam("Tied") && mostVotes.size() > 1) {
|
||||
subAbs.add(sa.getParam("Tied"));
|
||||
} else if (sa.hasParam("VoteSubAbility")) {
|
||||
for (final Object o : mostVotes) {
|
||||
host.addRemembered(o);
|
||||
}
|
||||
subAbs.add(sa.getParam("VoteSubAbility"));
|
||||
} else {
|
||||
for (Object type : mostVotes) {
|
||||
subAbs.add(sa.getParam("Vote" + type.toString()));
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("StoreVoteNum")) {
|
||||
for (final Object type : voteType) {
|
||||
host.setSVar("VoteNum" + type, "Number$" + votes.get(type).size());
|
||||
}
|
||||
} else {
|
||||
for (final String subAb : subAbs) {
|
||||
final SpellAbility action = AbilityFactory.getAbility(host, subAb);
|
||||
action.setActivatingPlayer(sa.getActivatingPlayer());
|
||||
((AbilitySub) action).setParent(sa);
|
||||
AbilityUtils.resolve(action);
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("VoteSubAbility")) {
|
||||
host.clearRemembered();
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("VoteSubAbility")) {
|
||||
host.clearRemembered();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3938,7 +3938,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
keywordsGrantedByTextChanges.add(newKw);
|
||||
}
|
||||
}
|
||||
addChangedCardKeywordsInternal(addKeywords, removeKeywords, false, false, timestamp, true);
|
||||
if (!addKeywords.isEmpty() || !removeKeywords.isEmpty()) {
|
||||
addChangedCardKeywordsInternal(addKeywords, removeKeywords, false, false, timestamp, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateKeywordsOnRemoveChangedText(final KeywordsChange k) {
|
||||
@@ -6353,6 +6355,10 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
removeSVar("PayX"); // Temporary AI X announcement variable
|
||||
removeSVar("IsCastFromPlayEffect"); // Temporary SVar indicating that the spell is cast indirectly via AF Play
|
||||
setSunburstValue(0); // Sunburst
|
||||
setXManaCostPaid(0);
|
||||
setXManaCostPaidByColor(null);
|
||||
setKickerMagnitude(0);
|
||||
setPseudoMultiKickerMagnitude(0);
|
||||
}
|
||||
|
||||
public final int getFinalChapterNr() {
|
||||
|
||||
@@ -20,7 +20,6 @@ package forge.game.card;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -3010,24 +3009,43 @@ public class CardFactoryUtil {
|
||||
|
||||
inst.addTrigger(parsedTrigger);
|
||||
} else if (keyword.startsWith("Saga")) {
|
||||
// Saga there doesn't need Max value anymore?
|
||||
final String[] k = keyword.split(":");
|
||||
final String[] abs = k[2].split(",");
|
||||
final List<String> abs = Arrays.asList(k[2].split(","));
|
||||
if (abs.size() != Integer.valueOf(k[1])) {
|
||||
throw new RuntimeException("Saga max differ from Ability amount");
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
for (String ab : abs) {
|
||||
SpellAbility sa = AbilityFactory.getAbility(card, ab);
|
||||
sa.setChapter(i);
|
||||
int idx = 0;
|
||||
int skipId = 0;
|
||||
for(String ab : abs) {
|
||||
idx += 1;
|
||||
if (idx <= skipId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO better logic for Roman numbers
|
||||
// In the Description try to merge Chapter trigger with the Same Effect
|
||||
String trigStr = "Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield"
|
||||
+ "| CounterType$ LORE | CounterAmount$ EQ" + i
|
||||
+ "| TriggerDescription$ " + Strings.repeat("I", i) + " - " + sa.getDescription();
|
||||
final Trigger t = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
||||
t.setOverridingAbility(sa);
|
||||
inst.addTrigger(t);
|
||||
++i;
|
||||
skipId = idx + abs.subList(idx - 1, abs.size()).lastIndexOf(ab);
|
||||
StringBuilder desc = new StringBuilder();
|
||||
for (int i = idx; i <= skipId; i++) {
|
||||
if (i != idx) {
|
||||
desc.append(", ");
|
||||
}
|
||||
desc.append(TextUtil.toRoman(i));
|
||||
}
|
||||
|
||||
for (int i = idx; i <= skipId; i++) {
|
||||
SpellAbility sa = AbilityFactory.getAbility(card, ab);
|
||||
sa.setChapter(i);
|
||||
|
||||
StringBuilder trigStr = new StringBuilder("Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield");
|
||||
trigStr.append("| CounterType$ LORE | CounterAmount$ EQ").append(i);
|
||||
if (i != idx) {
|
||||
trigStr.append(" | Secondary$ True");
|
||||
}
|
||||
trigStr.append("| TriggerDescription$ ").append(desc).append(" — ").append(sa.getDescription());
|
||||
final Trigger t = TriggerHandler.parseTrigger(trigStr.toString(), card, intrinsic);
|
||||
t.setOverridingAbility(sa);
|
||||
inst.addTrigger(t);
|
||||
}
|
||||
}
|
||||
} else if (keyword.equals("Soulbond")) {
|
||||
// Setup ETB trigger for card with Soulbond keyword
|
||||
|
||||
@@ -1518,27 +1518,38 @@ public class CardProperty {
|
||||
} else if (property.startsWith("blockedByThisTurn")) {
|
||||
return !card.getBlockedByThisTurn().isEmpty();
|
||||
} else if (property.startsWith("blockedValidThisTurn ")) {
|
||||
if (card.getBlockedThisTurn() == null) {
|
||||
CardCollectionView blocked = card.getBlockedThisTurn();
|
||||
if (blocked == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String valid = property.split(" ")[1];
|
||||
for(Card c : card.getBlockedThisTurn()) {
|
||||
for(Card c : blocked) {
|
||||
if (c.isValid(valid, card.getController(), source, spellAbility)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) {
|
||||
if (blocked.contains(c)) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return false;
|
||||
} else if (property.startsWith("blockedByValidThisTurn ")) {
|
||||
if (card.getBlockedByThisTurn() == null) {
|
||||
CardCollectionView blocked = card.getBlockedByThisTurn();
|
||||
if (blocked == null) {
|
||||
return false;
|
||||
}
|
||||
String valid = property.split(" ")[1];
|
||||
for(Card c : card.getBlockedByThisTurn()) {
|
||||
for(Card c : blocked) {
|
||||
if (c.isValid(valid, card.getController(), source, spellAbility)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) {
|
||||
if (blocked.contains(c)) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return false;
|
||||
} else if (property.startsWith("blockedBySourceThisTurn")) {
|
||||
return source.getBlockedByThisTurn().contains(card);
|
||||
@@ -1779,4 +1790,4 @@ public class CardProperty {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,27 +203,22 @@ public class CardView extends GameEntityView {
|
||||
void updateCommander(Card c) {
|
||||
boolean isCommander = c.isCommander();
|
||||
set(TrackableProperty.IsCommander, isCommander);
|
||||
if (c.getGame().getRules().hasAppliedVariant(GameType.Oathbreaker)) {
|
||||
//store alternate type for oathbreaker or signature spell for display in card text
|
||||
if (isCommander) {
|
||||
if (isCommander) {
|
||||
if (c.getGame().getRules().hasAppliedVariant(GameType.Oathbreaker)) {
|
||||
//store alternate type for oathbreaker or signature spell for display in card text
|
||||
if (c.getPaperCard().getRules().canBeSignatureSpell()) {
|
||||
set(TrackableProperty.CommanderAltType, "Signature Spell");
|
||||
}
|
||||
else {
|
||||
set(TrackableProperty.CommanderAltType, "Oathbreaker");
|
||||
}
|
||||
}
|
||||
else {
|
||||
set(TrackableProperty.CommanderAltType, null);
|
||||
} else {
|
||||
set(TrackableProperty.CommanderAltType, "Commander");
|
||||
}
|
||||
}
|
||||
}
|
||||
public String getCommanderType() {
|
||||
String type = get(TrackableProperty.CommanderAltType);
|
||||
if (type == null) {
|
||||
type = "Commander";
|
||||
}
|
||||
return type;
|
||||
return get(TrackableProperty.CommanderAltType);
|
||||
}
|
||||
|
||||
public Map<CounterType, Integer> getCounters() {
|
||||
|
||||
@@ -882,6 +882,10 @@ public class Combat {
|
||||
return true; // is blocking something at the moment
|
||||
}
|
||||
|
||||
if (!blocker.isLKI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombatLki lki = lkiCache.get(blocker);
|
||||
return null != lki && !lki.isAttacker; // was blocking something anyway
|
||||
}
|
||||
@@ -892,7 +896,11 @@ public class Combat {
|
||||
if (blockers != null && blockers.contains(blocker)) {
|
||||
return true; // is blocking the attacker's band at the moment
|
||||
}
|
||||
|
||||
|
||||
if (!blocker.isLKI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombatLki lki = lkiCache.get(blocker);
|
||||
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
}
|
||||
}
|
||||
if (mana.addsCounters(sa)) {
|
||||
mana.getManaAbility().createETBCounters(host);
|
||||
mana.getManaAbility().createETBCounters(host, this.owner);
|
||||
}
|
||||
if (mana.triggersWhenSpent()) {
|
||||
mana.getManaAbility().addTriggersWhenSpent(sa, host);
|
||||
|
||||
@@ -162,6 +162,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
private Card blessingEffect = null;
|
||||
private Card keywordEffect = null;
|
||||
|
||||
private Map<Long, Integer> additionalVotes = Maps.newHashMap();
|
||||
private Map<Long, Integer> additionalOptionalVotes = Maps.newHashMap();
|
||||
private SortedSet<Long> controlVotes = Sets.newTreeSet();
|
||||
|
||||
private final AchievementTracker achievementTracker = new AchievementTracker();
|
||||
private final PlayerView view;
|
||||
|
||||
@@ -2706,6 +2710,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
public void incCommanderCast(Card commander) {
|
||||
commanderCast.put(commander, getCommanderCast(commander) + 1);
|
||||
getView().updateCommanderCast(this, commander);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
|
||||
public int getTotalCommanderCast() {
|
||||
@@ -3065,4 +3071,86 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
this.updateZoneForView(com);
|
||||
return keywordEffect;
|
||||
}
|
||||
|
||||
public void addAdditionalVote(long timestamp, int value) {
|
||||
additionalVotes.put(timestamp, value);
|
||||
getView().updateAdditionalVote(this);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
|
||||
public void removeAdditionalVote(long timestamp) {
|
||||
if (additionalVotes.remove(timestamp) != null) {
|
||||
getView().updateAdditionalVote(this);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
}
|
||||
|
||||
public int getAdditionalVotesAmount() {
|
||||
int value = 0;
|
||||
for (Integer i : additionalVotes.values()) {
|
||||
value += i;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void addAdditionalOptionalVote(long timestamp, int value) {
|
||||
additionalOptionalVotes.put(timestamp, value);
|
||||
getView().updateOptionalAdditionalVote(this);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
public void removeAdditionalOptionalVote(long timestamp) {
|
||||
if (additionalOptionalVotes.remove(timestamp) != null) {
|
||||
getView().updateOptionalAdditionalVote(this);
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
||||
}
|
||||
}
|
||||
|
||||
public int getAdditionalOptionalVotesAmount() {
|
||||
int value = 0;
|
||||
for (Integer i : additionalOptionalVotes.values()) {
|
||||
value += i;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean addControlVote(long timestamp) {
|
||||
if (controlVotes.add(timestamp)) {
|
||||
updateControlVote();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public boolean removeControlVote(long timestamp) {
|
||||
if (controlVotes.remove(timestamp)) {
|
||||
updateControlVote();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateControlVote() {
|
||||
// need to update all players because it can't know
|
||||
Player control = getGame().getControlVote();
|
||||
for (Player pl : getGame().getPlayers()) {
|
||||
pl.getView().updateControlVote(pl.equals(control));
|
||||
getGame().fireEvent(new GameEventPlayerStatsChanged(pl, false));
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Long> getControlVote() {
|
||||
return controlVotes;
|
||||
}
|
||||
|
||||
public void setControlVote(Set<Long> value) {
|
||||
controlVotes.clear();
|
||||
controlVotes.addAll(value);
|
||||
updateControlVote();
|
||||
}
|
||||
|
||||
public Long getHighestControlVote() {
|
||||
if (controlVotes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return controlVotes.last();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ public abstract class PlayerController {
|
||||
return chooseSomeType(kindOfType, sa, validTypes, invalidTypes, false);
|
||||
}
|
||||
|
||||
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes);
|
||||
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer);
|
||||
public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question);
|
||||
|
||||
public abstract CardCollectionView getCardsToMulligan(Player firstPlayer);
|
||||
|
||||
@@ -7,7 +7,6 @@ import forge.card.CardType;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.card.CounterType;
|
||||
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
@@ -107,8 +106,7 @@ public class PlayerView extends GameEntityView {
|
||||
}
|
||||
|
||||
public boolean isOpponentOf(final PlayerView other) {
|
||||
FCollectionView<PlayerView> opponents = getOpponents();
|
||||
return opponents != null && opponents.contains(other);
|
||||
return getOpponents().contains(other);
|
||||
}
|
||||
|
||||
public final String getCommanderInfo(CardView v) {
|
||||
@@ -117,14 +115,15 @@ public class PlayerView extends GameEntityView {
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
Iterable<PlayerView> opponents = getOpponents();
|
||||
if (opponents == null) {
|
||||
opponents = Collections.emptyList();
|
||||
}
|
||||
for (final PlayerView p : Iterables.concat(Collections.singleton(this), opponents)) {
|
||||
|
||||
sb.append(Localizer.getInstance().getMessage("lblCommanderCastCard", String.valueOf(getCommanderCast(v))));
|
||||
sb.append("\n");
|
||||
|
||||
for (final PlayerView p : Iterables.concat(Collections.singleton(this), getOpponents())) {
|
||||
final int damage = p.getCommanderDamage(v);
|
||||
if (damage > 0) {
|
||||
sb.append(Localizer.getInstance().getMessage("lblCommanderDealNDamageToPlayer", p.toString(), CardTranslation.getTranslatedName(v.getName()), String.valueOf(damage)));
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -144,7 +143,11 @@ public class PlayerView extends GameEntityView {
|
||||
}
|
||||
|
||||
final List<String> info = Lists.newArrayListWithExpectedSize(opponents.size());
|
||||
info.add(TextUtil.concatWithSpace("Commanders:", Lang.joinHomogenous(commanders)));
|
||||
|
||||
info.add("Commanders:");
|
||||
for (final CardView v : commanders) {
|
||||
info.add(Localizer.getInstance().getMessage("lblCommanderCastPlayer", CardTranslation.getTranslatedName(v.getName()), String.valueOf(getCommanderCast(v))));
|
||||
}
|
||||
|
||||
// own commanders
|
||||
for (final CardView v : commanders) {
|
||||
@@ -268,6 +271,27 @@ public class PlayerView extends GameEntityView {
|
||||
set(TrackableProperty.NumDrawnThisTurn, p.getNumDrawnThisTurn());
|
||||
}
|
||||
|
||||
public int getAdditionalVote() {
|
||||
return get(TrackableProperty.AdditionalVote);
|
||||
}
|
||||
public void updateAdditionalVote(Player p) {
|
||||
set(TrackableProperty.AdditionalVote, p.getAdditionalVotesAmount());
|
||||
}
|
||||
|
||||
public int getOptionalAdditionalVote() {
|
||||
return get(TrackableProperty.OptionalAdditionalVote);
|
||||
}
|
||||
public void updateOptionalAdditionalVote(Player p) {
|
||||
set(TrackableProperty.OptionalAdditionalVote, p.getAdditionalOptionalVotesAmount());
|
||||
}
|
||||
|
||||
public boolean getControlVote() {
|
||||
return get(TrackableProperty.ControlVotes);
|
||||
}
|
||||
public void updateControlVote(boolean val) {
|
||||
set(TrackableProperty.ControlVotes, val);
|
||||
}
|
||||
|
||||
public ImmutableMultiset<String> getKeywords() {
|
||||
return get(TrackableProperty.Keywords);
|
||||
}
|
||||
@@ -300,13 +324,29 @@ public class PlayerView extends GameEntityView {
|
||||
return damage == null ? 0 : damage.intValue();
|
||||
}
|
||||
void updateCommanderDamage(Player p) {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
Map<Integer, Integer> map = Maps.newHashMap();
|
||||
for (Entry<Card, Integer> entry : p.getCommanderDamage()) {
|
||||
map.put(entry.getKey().getId(), entry.getValue());
|
||||
}
|
||||
set(TrackableProperty.CommanderDamage, map);
|
||||
}
|
||||
|
||||
public int getCommanderCast(CardView commander) {
|
||||
Map<Integer, Integer> map = get(TrackableProperty.CommanderCast);
|
||||
if (map == null) { return 0; }
|
||||
Integer damage = map.get(commander.getId());
|
||||
return damage == null ? 0 : damage.intValue();
|
||||
}
|
||||
|
||||
void updateCommanderCast(Player p, Card c) {
|
||||
Map<Integer, Integer> map = get(TrackableProperty.CommanderCast);
|
||||
if (map == null) {
|
||||
map = Maps.newHashMap();
|
||||
}
|
||||
map.put(c.getId(), p.getCommanderCast(c));
|
||||
set(TrackableProperty.CommanderCast, map);
|
||||
}
|
||||
|
||||
public PlayerView getMindSlaveMaster() {
|
||||
return get(TrackableProperty.MindSlaveMaster);
|
||||
}
|
||||
@@ -478,6 +518,19 @@ public class PlayerView extends GameEntityView {
|
||||
details.add(Localizer.getInstance().getMessage("lblCardDrawnThisTurnHas", String.valueOf(getNumDrawnThisTurn())));
|
||||
details.add(Localizer.getInstance().getMessage("lblDamagepreventionHas", String.valueOf(getPreventNextDamage())));
|
||||
|
||||
int v = getAdditionalVote();
|
||||
if (v > 0) {
|
||||
details.add(Localizer.getInstance().getMessage("lblAdditionalVotes", String.valueOf(v)));
|
||||
}
|
||||
v = getOptionalAdditionalVote();
|
||||
if (v > 0) {
|
||||
details.add(Localizer.getInstance().getMessage("lblOptionalAdditionalVotes", String.valueOf(v)));
|
||||
}
|
||||
|
||||
if (getControlVote()) {
|
||||
details.add(Localizer.getInstance().getMessage("lblControlsVote"));
|
||||
}
|
||||
|
||||
if (getIsExtraTurn()) {
|
||||
details.add(Localizer.getInstance().getMessage("lblIsExtraTurn"));
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -19,9 +19,11 @@ package forge.game.spellability;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
@@ -34,6 +36,8 @@ import forge.game.replacement.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -46,7 +50,7 @@ import java.util.regex.Pattern;
|
||||
* <p>
|
||||
* Abstract AbilityMana class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -78,7 +82,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* Constructor for AbilityMana.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sourceCard
|
||||
* a {@link forge.game.card.Card} object.
|
||||
*/
|
||||
@@ -111,7 +115,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* produceMana.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param produced
|
||||
* a {@link java.lang.String} object.
|
||||
* @param player
|
||||
@@ -169,7 +173,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* cannotCounterPaidWith.
|
||||
* </p>
|
||||
* @param saBeingPaid
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public boolean cannotCounterPaidWith(SpellAbility saBeingPaid) {
|
||||
@@ -186,7 +190,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* addKeywords.
|
||||
* </p>
|
||||
* @param saBeingPaid
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public boolean addKeywords(SpellAbility saBeingPaid) {
|
||||
@@ -205,7 +209,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* getKeywords.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String getKeywords() {
|
||||
@@ -217,7 +221,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* addsCounters.
|
||||
* </p>
|
||||
* @param saBeingPaid
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public boolean addsCounters(SpellAbility saBeingPaid) {
|
||||
@@ -227,10 +231,26 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
/**
|
||||
* createETBCounters
|
||||
*/
|
||||
public void createETBCounters(Card c) {
|
||||
public void createETBCounters(Card c, Player controller) {
|
||||
String[] parse = this.addsCounters.split("_");
|
||||
// Convert random SVars if there are other cards with this effect
|
||||
if (c.isValid(parse[0], c.getController(), c, null)) {
|
||||
final Game game = this.sourceCard.getGame();
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(sourceCard.getName() + "'s Effect");
|
||||
eff.addType("Effect");
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||
eff.setOwner(controller);
|
||||
|
||||
eff.setImageKey(sourceCard.getImageKey());
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
eff.setImmutable(true);
|
||||
// try to get the SpellAbility from the mana ability
|
||||
//eff.setEffectSource((SpellAbility)null);
|
||||
|
||||
eff.addRemembered(c);
|
||||
|
||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1]
|
||||
+ " | ETB$ True | CounterNum$ " + parse[2];
|
||||
|
||||
@@ -240,15 +260,37 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
}
|
||||
CardFactoryUtil.setupETBReplacementAbility(sa);
|
||||
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield "
|
||||
+ " | Secondary$ True | Description$ CARDNAME"
|
||||
+ " enters the battlefield with " + CounterType.valueOf(parse[1]).getName() + " counters.";
|
||||
String desc = "It enters the battlefield with ";
|
||||
desc += Lang.nounWithNumeral(parse[2], CounterType.valueOf(parse[1]).getName() + " counter");
|
||||
desc += " on it.";
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, c, false);
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||
re.setLayer(ReplacementLayer.Other);
|
||||
re.setOverridingAbility(sa);
|
||||
|
||||
c.addReplacementEffect(re);
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
// Forgot Trigger
|
||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
eff.addTrigger(parsedTrigger);
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +311,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* getManaRestrictions.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String getManaRestrictions() {
|
||||
@@ -280,7 +322,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* meetsManaRestrictions.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @return a boolean.
|
||||
@@ -296,7 +338,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
if (restriction.equals("nonSpell")) {
|
||||
return !sa.isSpell();
|
||||
}
|
||||
|
||||
|
||||
if (restriction.equals("CumulativeUpkeep")) {
|
||||
if (sa.isCumulativeupkeep()) {
|
||||
return true;
|
||||
@@ -349,7 +391,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* mana.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public final String mana() {
|
||||
@@ -438,7 +480,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* canProduce.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param s
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
@@ -468,7 +510,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* isBasic.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isBasic() {
|
||||
@@ -541,7 +583,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
public Card getSourceCard() {
|
||||
return sourceCard;
|
||||
}
|
||||
|
||||
|
||||
public void setSourceCard(final Card host) {
|
||||
sourceCard = host;
|
||||
}
|
||||
|
||||
@@ -75,12 +75,12 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
@Override
|
||||
public boolean canPlay() {
|
||||
Card card = this.getHostCard();
|
||||
if (card.isInZone(ZoneType.Battlefield)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the original cost and the face down info for a later check since the LKI copy will overwrite them
|
||||
ManaCost origCost = card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost();
|
||||
boolean wasFaceDownInstant = card.isFaceDown()
|
||||
&& !card.getLastKnownZone().is(ZoneType.Battlefield)
|
||||
&& card.getState(CardStateName.Original).getType().isInstant();
|
||||
|
||||
Player activator = this.getActivatingPlayer();
|
||||
if (activator == null) {
|
||||
@@ -95,61 +95,29 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isInstant = card.isInstant();
|
||||
// special case for split cards
|
||||
if (card.isSplitCard()) {
|
||||
CardStateName name = isLeftSplit() ? CardStateName.LeftSplit : CardStateName.RightSplit;
|
||||
isInstant = card.getState(name).getType().isInstant();
|
||||
} else if (isAdventure()) {
|
||||
if (card.hasState(CardStateName.Adventure)) {
|
||||
isInstant = card.getState(CardStateName.Adventure).getType().isInstant();
|
||||
}
|
||||
}
|
||||
|
||||
boolean lkicheck = false;
|
||||
boolean flash = false;
|
||||
|
||||
// do performanceMode only for cases where the activator is different than controller
|
||||
if (!Spell.performanceMode && activator != null && !card.getController().equals(activator)
|
||||
&& !card.isInZone(ZoneType.Battlefield)) {
|
||||
if (!Spell.performanceMode && activator != null && !card.getController().equals(activator)) {
|
||||
// always make a lki copy in this case?
|
||||
card = CardUtil.getLKICopy(card);
|
||||
card.setController(activator, 0);
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (isBestow() && !card.isBestowed() && !card.isInZone(ZoneType.Battlefield)) {
|
||||
// Rule 601.3: cast Bestow with Flash
|
||||
// for the check the card does need to be animated
|
||||
// otherwise the StaticAbility will not found them
|
||||
if (!card.isLKI()) {
|
||||
card = CardUtil.getLKICopy(card);
|
||||
}
|
||||
card.animateBestow(false);
|
||||
lkicheck = true;
|
||||
} else if (isCastFaceDown()) {
|
||||
// need a copy of the card to turn facedown without trigger anything
|
||||
if (!card.isLKI()) {
|
||||
card = CardUtil.getLKICopy(card);
|
||||
}
|
||||
card.turnFaceDownNoUpdate();
|
||||
lkicheck = true;
|
||||
} else if (isAdventure()) {
|
||||
if (!card.isLKI()) {
|
||||
card = CardUtil.getLKICopy(card);
|
||||
}
|
||||
|
||||
card.setState(CardStateName.Adventure, false);
|
||||
Card lkiHost = getAlternateHost(card);
|
||||
if (lkiHost != null) {
|
||||
card = lkiHost;
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
|
||||
if (lkicheck) {
|
||||
game.getTracker().freeze(); //prevent views flickering during while updating for state-based effects
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(card), new CardCollection(card));
|
||||
}
|
||||
|
||||
flash = card.withFlash(activator);
|
||||
boolean isInstant = card.isInstant();
|
||||
boolean flash = card.withFlash(activator);
|
||||
|
||||
// reset static abilities
|
||||
if (lkicheck) {
|
||||
@@ -160,8 +128,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
}
|
||||
|
||||
if (!(isInstant || activator.canCastSorcery() || flash || getRestrictions().isInstantSpeed()
|
||||
|| hasSVar("IsCastFromPlayEffect")
|
||||
|| wasFaceDownInstant)) {
|
||||
|| hasSVar("IsCastFromPlayEffect"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -235,4 +202,74 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
||||
this.castFaceDown = faceDown;
|
||||
}
|
||||
|
||||
public Card getAlternateHost(Card source) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
// need to be done before so it works with Vivien and Zoetic Cavern
|
||||
if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.turnFaceUp(false, false);
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (isBestow() && !source.isBestowed()) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.animateBestow(false);
|
||||
lkicheck = true;
|
||||
} else if (isCastFaceDown()) {
|
||||
// need a copy of the card to turn facedown without trigger anything
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
source.turnFaceDownNoUpdate();
|
||||
lkicheck = true;
|
||||
} else if (isAdventure()) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.Adventure, false);
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
lkicheck = true;
|
||||
} else if (source.isSplitCard() && (isLeftSplit() || isRightSplit())) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
if (isLeftSplit()) {
|
||||
if (!source.hasState(CardStateName.LeftSplit)) {
|
||||
source.addAlternateState(CardStateName.LeftSplit, false);
|
||||
source.getState(CardStateName.LeftSplit).copyFrom(
|
||||
getHostCard().getState(CardStateName.LeftSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.LeftSplit, false);
|
||||
}
|
||||
|
||||
if (isRightSplit()) {
|
||||
if (!source.hasState(CardStateName.RightSplit)) {
|
||||
source.addAlternateState(CardStateName.RightSplit, false);
|
||||
source.getState(CardStateName.RightSplit).copyFrom(
|
||||
getHostCard().getState(CardStateName.RightSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.RightSplit, false);
|
||||
}
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
return lkicheck ? source : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1299,6 +1299,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
String announce = getParam("Announce");
|
||||
if (StringUtils.isBlank(announce)) {
|
||||
mapParams.put("Announce", variable);
|
||||
originalMapParams.put("Announce", variable);
|
||||
return;
|
||||
}
|
||||
String[] announcedOnes = TextUtil.split(announce, ',');
|
||||
@@ -1308,6 +1309,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
}
|
||||
mapParams.put("Announce", announce + ";" + variable);
|
||||
originalMapParams.put("Announce", announce + ";" + variable);
|
||||
}
|
||||
|
||||
public boolean isXCost() {
|
||||
|
||||
@@ -181,15 +181,9 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
layers.add(StaticAbilityLayer.MODIFYPT);
|
||||
}
|
||||
|
||||
if (hasParam("AddHiddenKeyword")) {
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
if (hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")) {
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
if (hasParam("AdjustLandPlays")) {
|
||||
if (hasParam("AddHiddenKeyword")
|
||||
|| hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")
|
||||
|| hasParam("AdjustLandPlays") || hasParam("ControlVote") || hasParam("AdditionalVote") || hasParam("AdditionalOptionalVote")) {
|
||||
layers.add(StaticAbilityLayer.RULES);
|
||||
}
|
||||
|
||||
|
||||
@@ -513,6 +513,20 @@ public final class StaticAbilityContinuous {
|
||||
}
|
||||
}
|
||||
|
||||
if (params.containsKey("ControlVote")) {
|
||||
p.addControlVote(se.getTimestamp());
|
||||
}
|
||||
if (params.containsKey("AdditionalVote")) {
|
||||
String mhs = params.get("AdditionalVote");
|
||||
int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb);
|
||||
p.addAdditionalVote(se.getTimestamp(), add);
|
||||
}
|
||||
if (params.containsKey("AdditionalOptionalVote")) {
|
||||
String mhs = params.get("AdditionalOptionalVote");
|
||||
int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb);
|
||||
p.addAdditionalOptionalVote(se.getTimestamp(), add);
|
||||
}
|
||||
|
||||
if (params.containsKey("RaiseMaxHandSize")) {
|
||||
String rmhs = params.get("RaiseMaxHandSize");
|
||||
int rmax = AbilityUtils.calculateAmount(hostCard, rmhs, stAb);
|
||||
|
||||
@@ -274,7 +274,7 @@ public class TriggerHandler {
|
||||
}
|
||||
|
||||
private void runStateTrigger(final Map<AbilityKey, Object> runParams) {
|
||||
for (final Trigger t: activeTriggers) {
|
||||
for (final Trigger t: Lists.newArrayList(activeTriggers)) {
|
||||
if (canRunTrigger(t, TriggerType.Always, runParams)) {
|
||||
runSingleTrigger(t, runParams);
|
||||
}
|
||||
@@ -542,13 +542,20 @@ public class TriggerHandler {
|
||||
}
|
||||
|
||||
sa = AbilityFactory.getAbility(host, name);
|
||||
// need to set as Overriding Abiltiy so it can be copied better
|
||||
regtrig.setOverridingAbility(sa);
|
||||
}
|
||||
sa.setActivatingPlayer(host.getController());
|
||||
|
||||
if (regtrig.isIntrinsic()) {
|
||||
sa.setIntrinsic(true);
|
||||
sa.changeText();
|
||||
}
|
||||
} else {
|
||||
// need to copy the SA because of TriggeringObjects
|
||||
sa = sa.copy();
|
||||
sa = sa.copy(host, host.getController(), false);
|
||||
}
|
||||
|
||||
sa.setHostCard(host);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
|
||||
@@ -560,9 +567,7 @@ public class TriggerHandler {
|
||||
sa.setTriggeringObjects(regtrig.getStoredTriggeredObjects());
|
||||
}
|
||||
|
||||
if (sa.getActivatingPlayer() == null) { // overriding delayed trigger should have set activator
|
||||
sa.setActivatingPlayer(host.getController());
|
||||
} else if (sa.getDeltrigActivatingPlayer() != null) {
|
||||
if (sa.getDeltrigActivatingPlayer() != null) {
|
||||
// make sure that the original delayed trigger activator is restored
|
||||
// (may have been overwritten by the AI simulation routines, e.g. Rainbow Vale)
|
||||
sa.setActivatingPlayer(sa.getDeltrigActivatingPlayer());
|
||||
@@ -577,11 +582,6 @@ public class TriggerHandler {
|
||||
host.addRemembered(sa.getActivatingPlayer());
|
||||
}
|
||||
|
||||
if (regtrig.isIntrinsic() && regtrig.getOverridingAbility() == null) {
|
||||
sa.setIntrinsic(true);
|
||||
sa.changeText();
|
||||
}
|
||||
|
||||
sa.setStackDescription(sa.toString());
|
||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||
if (!CharmEffect.makeChoices(sa)) {
|
||||
|
||||
@@ -134,8 +134,12 @@ public enum TrackableProperty {
|
||||
HasUnlimitedLandPlay(TrackableTypes.BooleanType),
|
||||
NumLandThisTurn(TrackableTypes.IntegerType),
|
||||
NumDrawnThisTurn(TrackableTypes.IntegerType),
|
||||
AdditionalVote(TrackableTypes.IntegerType),
|
||||
OptionalAdditionalVote(TrackableTypes.IntegerType),
|
||||
ControlVotes(TrackableTypes.BooleanType),
|
||||
Keywords(TrackableTypes.KeywordCollectionViewType, FreezeMode.IgnoresFreeze),
|
||||
Commander(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze),
|
||||
CommanderCast(TrackableTypes.IntegerMapType),
|
||||
CommanderDamage(TrackableTypes.IntegerMapType),
|
||||
MindSlaveMaster(TrackableTypes.PlayerViewType),
|
||||
Ante(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze),
|
||||
|
||||
@@ -100,17 +100,21 @@ public class TrackableTypes {
|
||||
if (newCollection != null) {
|
||||
//swap in objects in old collection for objects in new collection
|
||||
for (int i = 0; i < newCollection.size(); i++) {
|
||||
T newObj = newCollection.get(i);
|
||||
if (newObj != null) {
|
||||
T existingObj = from.getTracker().getObj(itemType, newObj.getId());
|
||||
if (existingObj != null) { //if object exists already, update its changed properties
|
||||
existingObj.copyChangedProps(newObj);
|
||||
newCollection.remove(i);
|
||||
newCollection.add(i, existingObj);
|
||||
}
|
||||
else { //if object is new, cache in object lookup
|
||||
from.getTracker().putObj(itemType, newObj.getId(), newObj);
|
||||
try {
|
||||
T newObj = newCollection.get(i);
|
||||
if (newObj != null) {
|
||||
T existingObj = from.getTracker().getObj(itemType, newObj.getId());
|
||||
if (existingObj != null) { //if object exists already, update its changed properties
|
||||
existingObj.copyChangedProps(newObj);
|
||||
newCollection.remove(i);
|
||||
newCollection.add(i, existingObj);
|
||||
}
|
||||
else { //if object is new, cache in object lookup
|
||||
from.getTracker().putObj(itemType, newObj.getId(), newObj);
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
System.err.println("got an IndexOutOfBoundsException, trying to continue ...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user