Merge branch '1372-used-memory-grows-quadratic-in-the-number-of-triggered-spells' into 'master'

Resolve "Used memory grows quadratic in the number of triggered spells"

Closes #1372

See merge request core-developers/forge!2756
This commit is contained in:
Michael Kamensky
2020-04-25 17:36:50 +00:00
11 changed files with 49 additions and 80 deletions

View File

@@ -1352,7 +1352,7 @@ public class ComputerUtil {
if (valid.contains("Creature.YouCtrl")
|| valid.contains("Other+YouCtrl") ) {
final SpellAbility sa = t.getTriggeredSA();
final SpellAbility sa = t.getOverridingAbility();
if (sa != null && sa.getApi() == ApiType.Pump && sa.hasParam("KW")
&& sa.getParam("KW").contains("Haste")) {
return true;

View File

@@ -894,6 +894,9 @@ public class Game {
public void clearCaches() {
spabCache.clear();
cardCache.clear();
lastStateBattlefield.clear();
lastStateGraveyard.clear();
//playerCache.clear();
}

View File

@@ -1056,23 +1056,6 @@ public class Card extends GameEntity implements Comparable<Card> {
}
}
public final Object getTriggeringObject(final AbilityKey typeIn) {
Object triggered = null;
if (!currentState.getTriggers().isEmpty()) {
for (final Trigger t : currentState.getTriggers()) {
final SpellAbility sa = t.getTriggeredSA();
if (sa == null) {
continue;
}
triggered = sa.hasTriggeringObject(typeIn) ? sa.getTriggeringObject(typeIn) : null;
if (triggered != null) {
break;
}
}
}
return triggered;
}
public final int getSunburstValue() {
return sunburstValue;
}

View File

@@ -680,7 +680,6 @@ public class CardFactory {
wrapperAbility.setTrigger(true);
wrapperAbility.setMandatory(sa.isMandatory());
wrapperAbility.setDescription(wrapperAbility.getStackDescription());
t.setTriggeredSA(wrapperAbility);
return wrapperAbility;
}

View File

@@ -34,7 +34,6 @@ import forge.game.GameEntity;
import forge.game.GameEntityCounterTable;
import forge.game.GameLogEntryType;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.CardPredicates.Presets;
@@ -954,11 +953,6 @@ public class CardFactoryUtil {
return doXMath(c.getRegeneratedThisTurn(), m, c);
}
// TriggeringObjects
if (sq[0].startsWith("Triggered")) {
return doXMath(xCount((Card) c.getTriggeringObject(AbilityKey.Card), sq[0].substring(9)), m, c);
}
if (sq[0].contains("YourStartingLife")) {
return doXMath(cc.getStartingLife(), m, c);
}

View File

@@ -6,6 +6,7 @@ import forge.card.MagicColor;
import forge.game.Direction;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.card.CardPredicates.Presets;
@@ -15,7 +16,6 @@ import forge.game.keyword.Keyword;
import forge.game.player.Player;
import forge.game.spellability.OptionalCost;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.Trigger;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.util.Expressions;
@@ -392,30 +392,20 @@ public class CardProperty {
}
} else if (property.startsWith("AttachedTo")) {
final String restriction = property.split("AttachedTo ")[1];
if (restriction.equals("Targeted")) {
if (!source.getCurrentState().getTriggers().isEmpty()) {
for (final Trigger t : source.getCurrentState().getTriggers()) {
final SpellAbility sa = t.getTriggeredSA();
final CardCollectionView cards = AbilityUtils.getDefinedCards(source, "Targeted", sa);
for (final Card c : cards) {
if (card.getEquipping() != c && !c.equals(card.getEntityAttachedTo())) {
if (card.getEntityAttachedTo() == null) {
return false;
}
if (!card.getEntityAttachedTo().isValid(restriction, sourceController, source, spellAbility)) {
boolean found = false;
for (final GameObject o : AbilityUtils.getDefinedObjects(source, restriction, spellAbility)) {
if (o.equals(card.getEntityAttachedTo())) {
found = true;
break;
}
}
} else {
for (final SpellAbility sa : source.getCurrentState().getNonManaAbilities()) {
final CardCollectionView cards = AbilityUtils.getDefinedCards(source, "Targeted", sa);
for (final Card c : cards) {
if (card.getEquipping() == c || c.equals(card.getEntityAttachedTo())) { // handle multiple targets
return true;
}
}
}
return false;
}
} else {
if ((card.getEntityAttachedTo() == null || !card.getEntityAttachedTo().isValid(restriction, sourceController, source, spellAbility))) {
if (!found) {
return false;
}
}
@@ -856,15 +846,6 @@ public class CardProperty {
}
}
return false;
case "TriggeredCard":
final Object triggeringObject = source.getTriggeringObject(AbilityKey.fromString(restriction.substring("Triggered".length())));
if (!(triggeringObject instanceof Card)) {
return false;
}
if (card.sharesCardTypeWith((Card) triggeringObject)) {
return true;
}
return false;
case "EachTopLibrary":
final CardCollection cards = new CardCollection();
for (Player p : game.getPlayers()) {

View File

@@ -1996,4 +1996,24 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public void setXManaCostPaid(final Integer n) {
xManaCostPaid = n;
}
public void removeFromGame() {
if (getHostCard() == null) {
return;
}
getHostCard().getGame().removeSpellAbility(this);
if (subAbility != null) {
subAbility.removeFromGame();
}
for (AbilitySub sa : additionalAbilities.values()) {
sa.removeFromGame();
}
for (List<AbilitySub> list : additionalAbilityLists.values()) {
for (AbilitySub sa : list) {
sa.removeFromGame();
}
}
}
}

View File

@@ -29,7 +29,6 @@ import forge.game.card.CardState;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.Ability;
import forge.game.spellability.OptionalCost;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
@@ -472,27 +471,6 @@ public abstract class Trigger extends TriggerReplacementBase {
this.id = id;
}
private Ability triggeredSA;
/**
* Gets the triggered sa.
*
* @return the triggered sa
*/
public final Ability getTriggeredSA() {
return this.triggeredSA;
}
/**
* Sets the triggered sa.
*
* @param sa
* the triggered sa to set
*/
public void setTriggeredSA(final Ability sa) {
this.triggeredSA = sa;
}
public void addRemembered(Object o) {
this.triggerRemembered.add(o);
}

View File

@@ -628,7 +628,6 @@ public class TriggerHandler {
else {
game.getStack().addSimultaneousStackEntry(wrapperAbility);
}
regtrig.setTriggeredSA(wrapperAbility);
regtrig.triggerRun();

View File

@@ -563,4 +563,10 @@ public class WrappedAbility extends Ability {
public void setXManaCostPaid(final Integer n) {
sa.setXManaCostPaid(n);
}
@Override
public void removeFromGame() {
super.removeFromGame();
getHostCard().getGame().removeSpellAbility(this.getWrappedAbility());
}
}

View File

@@ -638,7 +638,13 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
frozenStack.remove(si);
game.updateStackForView();
SpellAbility sa = si.getSpellAbility(true);
sa.setLastStateBattlefield(CardCollection.EMPTY);
sa.setLastStateGraveyard(CardCollection.EMPTY);
game.fireEvent(new GameEventSpellRemovedFromStack(sa));
if (sa.isTrigger()) {
sa.removeFromGame();
}
}
public final void remove(final Card c) {