Merge remote-tracking branch 'upstream/master'

This commit is contained in:
CCTV-1
2020-03-08 16:11:00 +08:00
76 changed files with 4333 additions and 411 deletions

View File

@@ -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;
}
@@ -565,16 +565,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 its 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);

View File

@@ -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);

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -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 ...");
}
}
}