mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Add TimesMutated counter. Refactor how mutate get abilities
This commit is contained in:
@@ -3,8 +3,8 @@ package forge.game.ability.effects;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
|
|
||||||
|
import forge.card.CardStateName;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
@@ -13,41 +13,9 @@ import forge.game.card.*;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.util.Lang;
|
|
||||||
|
|
||||||
public class MutateEffect extends SpellAbilityEffect {
|
public class MutateEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
private void migrateTopCard(final Card host, final Card target) {
|
|
||||||
// Copy all status from target card and migrate all counters
|
|
||||||
// Also update all reference of target card to new top card
|
|
||||||
|
|
||||||
// TODO: find out all necessary status that should be copied
|
|
||||||
host.setTapped(target.isTapped());
|
|
||||||
host.setSickness(target.isFirstTurnControlled());
|
|
||||||
host.setFlipped(target.isFlipped());
|
|
||||||
host.setDamage(target.getDamage());
|
|
||||||
host.setMonstrous(target.isMonstrous());
|
|
||||||
host.setRenowned(target.isRenowned());
|
|
||||||
|
|
||||||
// Migrate counters
|
|
||||||
Map<CounterType, Integer> counters = target.getCounters();
|
|
||||||
if (!counters.isEmpty()) {
|
|
||||||
host.setCounters(Maps.newHashMap(counters));
|
|
||||||
}
|
|
||||||
target.clearCounters();
|
|
||||||
|
|
||||||
// Migrate attached cards
|
|
||||||
CardCollectionView attached = target.getAttachedCards();
|
|
||||||
for (final Card c : attached) {
|
|
||||||
c.setEntityAttachedTo(host);
|
|
||||||
}
|
|
||||||
target.setAttachedCards(null);
|
|
||||||
host.setAttachedCards(attached);
|
|
||||||
|
|
||||||
// TODO: move all remembered, imprinted objects to new top card
|
|
||||||
// and possibly many other needs to be migrated.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Player p = sa.getActivatingPlayer();
|
final Player p = sa.getActivatingPlayer();
|
||||||
@@ -89,33 +57,34 @@ public class MutateEffect extends SpellAbilityEffect {
|
|||||||
host.setMergedToCard(target);
|
host.setMergedToCard(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now the top card always have all abilities from bottom cards
|
|
||||||
|
// First remove current mutated states
|
||||||
|
if (target.getMutatedTimestamp() != -1) {
|
||||||
|
target.removeCloneState(target.getMutatedTimestamp());
|
||||||
|
target.setMutatedTimestamp(-1);
|
||||||
|
}
|
||||||
|
// Now add all abilities from bottom cards
|
||||||
final Long ts = game.getNextTimestamp();
|
final Long ts = game.getNextTimestamp();
|
||||||
|
if (topCard.getCurrentStateName() != CardStateName.FaceDown) {
|
||||||
|
final CardCloneStates mutatedStates = CardFactory.getMutatedCloneStates(topCard, sa);
|
||||||
|
topCard.addCloneState(mutatedStates, ts);
|
||||||
|
topCard.setMutatedTimestamp(ts);
|
||||||
|
}
|
||||||
if (topCard == target) {
|
if (topCard == target) {
|
||||||
final CardCloneStates cloneStates = CardFactory.getCloneStates(target, target, sa);
|
|
||||||
final CardState targetState = cloneStates.get(target.getCurrentStateName());
|
|
||||||
final CardState newState = host.getCurrentState();
|
|
||||||
targetState.addAbilitiesFrom(newState, false);
|
|
||||||
target.addCloneState(cloneStates, ts);
|
|
||||||
// Re-register triggers for target card
|
// Re-register triggers for target card
|
||||||
game.getTriggerHandler().clearActiveTriggers(target, null);
|
game.getTriggerHandler().clearActiveTriggers(target, null);
|
||||||
game.getTriggerHandler().registerActiveTrigger(target, false);
|
game.getTriggerHandler().registerActiveTrigger(target, false);
|
||||||
} else {
|
|
||||||
final CardCloneStates cloneStates = CardFactory.getCloneStates(host, host, sa);
|
|
||||||
final CardState newState = cloneStates.get(host.getCurrentStateName());
|
|
||||||
final CardState targetState = target.getCurrentState();
|
|
||||||
newState.addAbilitiesFrom(targetState, false);
|
|
||||||
host.addCloneState(cloneStates, ts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
game.getAction().moveToPlay(host, p, sa);
|
game.getAction().moveToPlay(host, p, sa);
|
||||||
|
|
||||||
if (topCard == host) {
|
if (topCard == host) {
|
||||||
migrateTopCard(host, target);
|
CardFactory.migrateTopCard(host, target);
|
||||||
} else {
|
} else {
|
||||||
host.setTapped(target.isTapped());
|
host.setTapped(target.isTapped());
|
||||||
host.setFlipped(target.isFlipped());
|
host.setFlipped(target.isFlipped());
|
||||||
}
|
}
|
||||||
|
topCard.setTimesMutated(topCard.getTimesMutated() + 1);
|
||||||
|
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Mutates, AbilityKey.mapFromCard(topCard), false);
|
game.getTriggerHandler().runTrigger(TriggerType.Mutates, AbilityKey.mapFromCard(topCard), false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,6 +185,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
|
|
||||||
private long bestowTimestamp = -1;
|
private long bestowTimestamp = -1;
|
||||||
private long transformedTimestamp = 0;
|
private long transformedTimestamp = 0;
|
||||||
|
private long mutatedTimestamp = -1;
|
||||||
|
private int timesMutated = 0;
|
||||||
private boolean tributed = false;
|
private boolean tributed = false;
|
||||||
private boolean embalmed = false;
|
private boolean embalmed = false;
|
||||||
private boolean eternalized = false;
|
private boolean eternalized = false;
|
||||||
@@ -822,7 +824,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCloned() {
|
public boolean isCloned() {
|
||||||
return !clonedStates.isEmpty();
|
return !clonedStates.isEmpty() && clonedStates.lastEntry().getKey() != mutatedTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final CardCollectionView getDevouredCards() {
|
public final CardCollectionView getDevouredCards() {
|
||||||
@@ -1029,6 +1031,24 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return !getMergedCards().isEmpty() || getMergedToCard() != null;
|
return !getMergedCards().isEmpty() || getMergedToCard() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean isMutated() {
|
||||||
|
return mutatedTimestamp != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long getMutatedTimestamp() {
|
||||||
|
return mutatedTimestamp;
|
||||||
|
}
|
||||||
|
public final void setMutatedTimestamp(final long t) {
|
||||||
|
mutatedTimestamp = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getTimesMutated() {
|
||||||
|
return timesMutated;
|
||||||
|
}
|
||||||
|
public final void setTimesMutated(final int t) {
|
||||||
|
timesMutated = t;
|
||||||
|
}
|
||||||
|
|
||||||
public final String getFlipResult(final Player flipper) {
|
public final String getFlipResult(final Player flipper) {
|
||||||
if (flipResult == null) {
|
if (flipResult == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ package forge.game.card;
|
|||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.ImageKeys;
|
import forge.ImageKeys;
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.card.*;
|
import forge.card.*;
|
||||||
@@ -820,4 +822,50 @@ public class CardFactory {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CardCloneStates getMutatedCloneStates(final Card card, final CardTraitBase sa) {
|
||||||
|
final CardStateName state = card.getCurrentStateName();
|
||||||
|
final CardState ret = new CardState(card, state);
|
||||||
|
ret.copyFrom(card.getState(state, true), false);
|
||||||
|
|
||||||
|
for (final Card c : card.getMergedCards()) {
|
||||||
|
ret.addAbilitiesFrom(c.getCurrentState(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
final CardCloneStates result = new CardCloneStates(card, sa);
|
||||||
|
result.put(state, ret);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void migrateTopCard(final Card host, final Card target) {
|
||||||
|
// Copy all status from target card and migrate all counters
|
||||||
|
// Also update all reference of target card to new top card
|
||||||
|
|
||||||
|
// TODO: find out all necessary status that should be copied
|
||||||
|
host.setTapped(target.isTapped());
|
||||||
|
host.setSickness(target.isFirstTurnControlled());
|
||||||
|
host.setFlipped(target.isFlipped());
|
||||||
|
host.setDamage(target.getDamage());
|
||||||
|
host.setTimesMutated(target.getTimesMutated());
|
||||||
|
host.setMonstrous(target.isMonstrous());
|
||||||
|
host.setRenowned(target.isRenowned());
|
||||||
|
|
||||||
|
// Migrate counters
|
||||||
|
Map<CounterType, Integer> counters = target.getCounters();
|
||||||
|
if (!counters.isEmpty()) {
|
||||||
|
host.setCounters(Maps.newHashMap(counters));
|
||||||
|
}
|
||||||
|
target.clearCounters();
|
||||||
|
|
||||||
|
// Migrate attached cards
|
||||||
|
CardCollectionView attached = target.getAttachedCards();
|
||||||
|
for (final Card c : attached) {
|
||||||
|
c.setEntityAttachedTo(host);
|
||||||
|
}
|
||||||
|
target.setAttachedCards(null);
|
||||||
|
host.setAttachedCards(attached);
|
||||||
|
|
||||||
|
// TODO: move all remembered, imprinted objects to new top card
|
||||||
|
// and possibly many other needs to be migrated.
|
||||||
|
}
|
||||||
|
|
||||||
} // end class AbstractCardFactory
|
} // end class AbstractCardFactory
|
||||||
|
|||||||
@@ -1319,6 +1319,9 @@ public class CardFactoryUtil {
|
|||||||
if (sq[0].contains("TimesPseudokicked")) {
|
if (sq[0].contains("TimesPseudokicked")) {
|
||||||
return doXMath(c.getPseudoKickerMagnitude(), m, c);
|
return doXMath(c.getPseudoKickerMagnitude(), m, c);
|
||||||
}
|
}
|
||||||
|
if (sq[0].contains("TimesMutated")) {
|
||||||
|
return doXMath(c.getTimesMutated(), m, c);
|
||||||
|
}
|
||||||
|
|
||||||
// Count$IfCastInOwnMainPhase.<numMain>.<numNotMain> // 7/10
|
// Count$IfCastInOwnMainPhase.<numMain>.<numNotMain> // 7/10
|
||||||
if (sq[0].contains("IfCastInOwnMainPhase")) {
|
if (sq[0].contains("IfCastInOwnMainPhase")) {
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -68,7 +68,7 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
private Map<String, String> sVars = Maps.newTreeMap();
|
private Map<String, String> sVars = Maps.newTreeMap();
|
||||||
|
|
||||||
private KeywordCollection cachedKeywords = new KeywordCollection();
|
private KeywordCollection cachedKeywords = new KeywordCollection();
|
||||||
|
|
||||||
private CardRarity rarity = CardRarity.Unknown;
|
private CardRarity rarity = CardRarity.Unknown;
|
||||||
private String setCode = CardEdition.UNKNOWN.getCode();
|
private String setCode = CardEdition.UNKNOWN.getCode();
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
view.updateType(this);
|
view.updateType(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setCreatureTypes(Collection<String> ctypes) {
|
public final void setCreatureTypes(Collection<String> ctypes) {
|
||||||
if (type.setCreatureTypes(ctypes)) {
|
if (type.setCreatureTypes(ctypes)) {
|
||||||
view.updateType(this);
|
view.updateType(this);
|
||||||
@@ -595,17 +595,11 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
staticAbilities.clear();
|
|
||||||
for (StaticAbility sa : source.staticAbilities) {
|
for (StaticAbility sa : source.staticAbilities) {
|
||||||
if (sa.isIntrinsic()) {
|
if (sa.isIntrinsic()) {
|
||||||
staticAbilities.add(sa.copy(card, lki));
|
staticAbilities.add(sa.copy(card, lki));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not sure if this is needed
|
|
||||||
if (lki && source.loyaltyRep != null) {
|
|
||||||
this.loyaltyRep = source.loyaltyRep.copy(card, lki);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardState copy(final Card host, CardStateName name, final boolean lki) {
|
public CardState copy(final Card host, CardStateName name, final boolean lki) {
|
||||||
@@ -625,11 +619,11 @@ public class CardState extends GameObject implements IHasSVars {
|
|||||||
public String getSetCode() {
|
public String getSetCode() {
|
||||||
return setCode;
|
return setCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardTypeView getTypeWithChanges() {
|
public CardTypeView getTypeWithChanges() {
|
||||||
return getType().getTypeWithChanges(card.getChangedCardTypes());
|
return getType().getTypeWithChanges(card.getChangedCardTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSetCode(String setCode0) {
|
public void setSetCode(String setCode0) {
|
||||||
setCode = setCode0;
|
setCode = setCode0;
|
||||||
view.updateSetCode(this);
|
view.updateSetCode(this);
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -26,7 +26,7 @@ import forge.game.player.Player;
|
|||||||
* <p>
|
* <p>
|
||||||
* PlayerZoneComesIntoPlay class.
|
* PlayerZoneComesIntoPlay class.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
|
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
@@ -62,7 +62,7 @@ public class PlayerZoneBattlefield extends PlayerZone {
|
|||||||
|
|
||||||
super.add(c, position, latestState);
|
super.add(c, position, latestState);
|
||||||
|
|
||||||
if (trigger && !c.isMerged()) {
|
if (trigger) {
|
||||||
c.setSickness(true); // summoning sickness
|
c.setSickness(true); // summoning sickness
|
||||||
c.runComesIntoPlayCommands();
|
c.runComesIntoPlayCommands();
|
||||||
}
|
}
|
||||||
|
|||||||
11
forge-gui/res/cardsfolder/i/insatiable_hemophage.txt
Normal file
11
forge-gui/res/cardsfolder/i/insatiable_hemophage.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Name:Insatiable Hemophage
|
||||||
|
ManaCost:3 B
|
||||||
|
Types:Creature Nightmare
|
||||||
|
PT:3/3
|
||||||
|
K:Mutate:2 B
|
||||||
|
K:Deathtouch
|
||||||
|
T:Mode$ Mutates | ValidCard$ Card.Self | Execute$ TrigLoseLife | TriggerDescription$ Whenever this creature mutates, each opponent loses X life and you gain X life, where X is the number of times this creature has mutated.
|
||||||
|
SVar:TrigLoseLife:DB$ LoseLife | Defined$ Opponent | LifeAmount$ X | References$ X | SubAbility$ DBGainLife
|
||||||
|
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | References$ X
|
||||||
|
SVar:X:Count$TimesMutated
|
||||||
|
Oracle:Mutate {2}{B} (If you cast this spell for its mutate cost, put it over or under target non-Human creature you own. They mutate into the creature on top plus all abilities from under it.)\nDeathtouch\nWhenever this creature mutates, each opponent loses X life and you gain X life, where X is the number of times this creature has mutated.
|
||||||
Reference in New Issue
Block a user