Merge remote-tracking branch 'core/master'

This commit is contained in:
Anthony Calosa
2022-02-19 21:25:25 +08:00
9 changed files with 78 additions and 31 deletions

View File

@@ -27,6 +27,7 @@ import java.util.Set;
import forge.util.*;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
@@ -1265,7 +1266,24 @@ public class GameAction {
noRegCreats.add(c);
checkAgain = true;
} else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) {
for (final Integer dmg : c.getReceivedDamageFromThisTurn().values()) {
// merge entries with same source
List<Integer> dmgList = Lists.newArrayList();
List<Pair<Card, Integer>> remainingDamaged = Lists.newArrayList(c.getReceivedDamageFromThisTurn());
while (!remainingDamaged.isEmpty()) {
Pair <Card, Integer> damaged = remainingDamaged.get(0);
int sum = damaged.getRight();
remainingDamaged.remove(damaged);
for (Pair<Card, Integer> other : Lists.newArrayList(remainingDamaged)) {
if (other.getLeft().equalsWithTimestamp(damaged.getLeft())) {
sum += other.getRight();
// once it got counted keep it out
remainingDamaged.remove(other);
}
}
dmgList.add(sum);
}
for (final Integer dmg : dmgList) {
if (c.getLethal() <= dmg.intValue() || c.hasBeenDealtDeathtouchDamage()) {
if (desCreats == null) {
desCreats = new CardCollection();

View File

@@ -255,14 +255,14 @@ public class DiscardEffect extends SpellAbilityEffect {
Player chooser = p;
if (mode.endsWith("YouChoose")) {
chooser = source.getController();
} else if (mode.endsWith("TgtChoose")) {
} else if (mode.equals("RevealTgtChoose")) {
chooser = firstTarget;
}
if (mode.startsWith("Reveal")) {
game.getAction().reveal(dPHand, p);
}
if (mode.startsWith("Look")) {
if (mode.startsWith("Look") && p != chooser) {
game.getAction().revealTo(dPHand, chooser);
}

View File

@@ -174,8 +174,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private final Set<Object> rememberedObjects = Sets.newLinkedHashSet();
private Map<Player, String> flipResult;
private Map<GameEntity, Integer> receivedDamageFromThisTurn = Maps.newHashMap();
private List<Pair<Card, Integer>> receivedDamageFromThisTurn = Lists.newArrayList();
private Map<Player, Integer> receivedDamageFromPlayerThisTurn = Maps.newHashMap();
private final Map<Card, Integer> assignedDamageMap = Maps.newTreeMap();
private boolean isCommander = false;
@@ -5154,46 +5155,48 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
damageHistory = history;
}
public final Map<GameEntity, Integer> getReceivedDamageFromThisTurn() {
public final List<Pair<Card, Integer>> getReceivedDamageFromThisTurn() {
return receivedDamageFromThisTurn;
}
public final void setReceivedDamageFromThisTurn(final Map<GameEntity, Integer> receivedDamageList) {
receivedDamageFromThisTurn = Maps.newHashMap(receivedDamageList);
public final void setReceivedDamageFromThisTurn(final List<Pair<Card, Integer>> receivedDamageList) {
receivedDamageFromThisTurn = Lists.newArrayList(receivedDamageList);
}
public final Map<Player, Integer> getReceivedDamageFromPlayerThisTurn() {
return receivedDamageFromPlayerThisTurn;
}
public final void setReceivedDamageFromPlayerThisTurn(final Map<Player, Integer> receivedDamageMap) {
receivedDamageFromPlayerThisTurn = Maps.newHashMap(receivedDamageMap);
}
public int getReceivedDamageByPlayerThisTurn(final Player p) {
if (receivedDamageFromThisTurn.containsKey(p)) {
return receivedDamageFromThisTurn.get(p);
if (receivedDamageFromPlayerThisTurn.containsKey(p)) {
return receivedDamageFromPlayerThisTurn.get(p);
}
return 0;
}
public final void addReceivedDamageFromThisTurn(final Card c, final int damage) {
public final void addReceivedDamageFromThisTurn(Card c, final int damage) {
int currentDamage = 0;
if (receivedDamageFromThisTurn.containsKey(c)) {
currentDamage = receivedDamageFromThisTurn.get(c);
}
receivedDamageFromThisTurn.put(c, damage+currentDamage);
// because Aegar cares about the past state we need to keep all LKI instances
receivedDamageFromThisTurn.add(Pair.of(c.isLKI() ? c : CardUtil.getLKICopy(c), damage));
Player p = c.getController();
if (p != null) {
currentDamage = 0;
if (receivedDamageFromThisTurn.containsKey(p)) {
currentDamage = receivedDamageFromThisTurn.get(p);
if (receivedDamageFromPlayerThisTurn.containsKey(p)) {
currentDamage = receivedDamageFromPlayerThisTurn.get(p);
}
receivedDamageFromThisTurn.put(p, damage+currentDamage);
receivedDamageFromPlayerThisTurn.put(p, damage+currentDamage);
}
}
public final void resetReceivedDamageFromThisTurn() {
receivedDamageFromThisTurn.clear();
receivedDamageFromPlayerThisTurn.clear();
}
public final int getTotalDamageReceivedThisTurn() {
int total = 0;
for (Entry<GameEntity, Integer> e : receivedDamageFromThisTurn.entrySet()) {
if (e.getKey() instanceof Player) {
total += e.getValue();
}
for (Integer i : receivedDamageFromPlayerThisTurn.values()) {
total += i;
}
return total;
}

View File

@@ -32,6 +32,7 @@ import forge.util.TextUtil;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Collections;
import java.util.List;
@@ -633,18 +634,21 @@ public class CardProperty {
return false;
}
} else if (property.startsWith("DamagedBy")) {
List<Card> damaged = Lists.newArrayList();
for (Pair<Card, Integer> pair : card.getReceivedDamageFromThisTurn()) {
damaged.add(pair.getLeft());
}
if (property.endsWith("Source") || property.equals("DamagedBy")) {
if (!card.getReceivedDamageFromThisTurn().containsKey(source)) {
if (!damaged.contains(source)) {
return false;
}
} else {
String prop = property.substring("DamagedBy".length());
final Iterable<Card> list = Iterables.filter(card.getReceivedDamageFromThisTurn().keySet(), Card.class);
boolean found = Iterables.any(list, CardPredicates.restriction(prop, sourceController, source, spellAbility));
boolean found = Iterables.any(damaged, CardPredicates.restriction(prop, sourceController, source, spellAbility));
if (!found) {
for (Card d : AbilityUtils.getDefinedCards(source, prop, spellAbility)) {
if (card.getReceivedDamageFromThisTurn().containsKey(d)) {
if (damaged.contains(d)) {
found = true;
break;
}
@@ -1177,7 +1181,7 @@ public class CardProperty {
return false;
}
} else if (property.startsWith("wasDealtDamageThisTurn")) {
if ((card.getReceivedDamageFromThisTurn().keySet()).isEmpty()) {
if (card.getReceivedDamageFromPlayerThisTurn().isEmpty()) {
return false;
}
} else if (property.startsWith("dealtDamageThisTurn")) {

View File

@@ -250,6 +250,7 @@ public final class CardUtil {
newCopy.setPhasedOut(in.isPhasedOut());
newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn());
newCopy.setReceivedDamageFromPlayerThisTurn(in.getReceivedDamageFromPlayerThisTurn());
newCopy.setDamageHistory(in.getDamageHistory());
for (Card c : in.getBlockedThisTurn()) {
newCopy.addBlockedThisTurn(c);

View File

@@ -0,0 +1,7 @@
Name:Disruption Protocol
ManaCost:U U
Types:Instant
K:AlternateAdditionalCost:tapXType<1/Artifact>:1
A:SP$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | SpellDescription$ Counter target spell.
DeckHints:Type$Artifact
Oracle:As an additional cost to cast this spell, tap an untapped artifact you control or pay {1}.\nCounter target spell.

View File

@@ -0,0 +1,6 @@
Name:Futurist Sentinel
ManaCost:3 U
Types:Artifact Vehicle
PT:6/6
K:Crew:3
Oracle:Crew 3 (Tap any number of creatures you control with total power 3 or more: This Vehicle becomes an artifact creature until end of turn.)

View File

@@ -0,0 +1,8 @@
Name:Moonfolk Puzzlemaker
ManaCost:2 U
Types:Artifact Creature Moonfolk Wizard
PT:1/4
K:Flying
T:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigSry | TriggerDescription$ Whenever CARDNAME becomes tapped, scry 1.
SVar:TrigSry:DB$ Scry | ScryNum$ 1
Oracle:Flying\nWhenever Moonfolk Puzzlemaker becomes tapped, scry 1.

View File

@@ -7,15 +7,15 @@ SVar:DBExile:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZo
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile | StackDescription$ SpellDescription | SpellDescription$ You may play that card this turn. Activate only as a sorcery.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play the exiled card this turn.
T:Mode$ SpellCast | ValidCard$ Card.IsImprinted | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigTransform | TriggerDescription$ Whenever you play a card exiled with CARDNAME, transform it.
T:Mode$ LandPlayed | ValidCard$ Land.IsImprinted+YouCtrl | Execute$ TrigTransform | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ Whenever you play a card exiled with CARDNAME, transform it.
T:Mode$ SpellCast | ValidCard$ Card.IsImprinted | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigTransform | TriggerDescription$ When you play a card exiled with CARDNAME, transform it.
T:Mode$ LandPlayed | ValidCard$ Land.IsImprinted+YouCtrl | Execute$ TrigTransform | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ When you play a card exiled with CARDNAME, transform it.
SVar:TrigTransform:DB$ SetState | Defined$ Self | Mode$ Transform
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBClear | Static$ True
SVar:DBClear:DB$ Cleanup | ClearImprinted$ True
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
AlternateMode:DoubleFaced
Oracle:{T}: Voltaic Visionary deals 2 damage to you. Exile the top card of your library. You may play that card this turn. Activate only as a sorcery.\nWhenever you play a card exiled with Voltaic Visionary, transform it.
Oracle:{T}: Voltaic Visionary deals 2 damage to you. Exile the top card of your library. You may play that card this turn. Activate only as a sorcery.\nWhen you play a card exiled with Voltaic Visionary, transform it.
ALTERNATE