mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28: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 com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityKey;
|
||||
@@ -13,41 +13,9 @@ import forge.game.card.*;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.util.Lang;
|
||||
|
||||
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
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Player p = sa.getActivatingPlayer();
|
||||
@@ -89,33 +57,34 @@ public class MutateEffect extends SpellAbilityEffect {
|
||||
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();
|
||||
if (topCard.getCurrentStateName() != CardStateName.FaceDown) {
|
||||
final CardCloneStates mutatedStates = CardFactory.getMutatedCloneStates(topCard, sa);
|
||||
topCard.addCloneState(mutatedStates, ts);
|
||||
topCard.setMutatedTimestamp(ts);
|
||||
}
|
||||
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
|
||||
game.getTriggerHandler().clearActiveTriggers(target, null);
|
||||
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);
|
||||
|
||||
if (topCard == host) {
|
||||
migrateTopCard(host, target);
|
||||
CardFactory.migrateTopCard(host, target);
|
||||
} else {
|
||||
host.setTapped(target.isTapped());
|
||||
host.setFlipped(target.isFlipped());
|
||||
}
|
||||
topCard.setTimesMutated(topCard.getTimesMutated() + 1);
|
||||
|
||||
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 transformedTimestamp = 0;
|
||||
private long mutatedTimestamp = -1;
|
||||
private int timesMutated = 0;
|
||||
private boolean tributed = false;
|
||||
private boolean embalmed = false;
|
||||
private boolean eternalized = false;
|
||||
@@ -822,7 +824,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
|
||||
public boolean isCloned() {
|
||||
return !clonedStates.isEmpty();
|
||||
return !clonedStates.isEmpty() && clonedStates.lastEntry().getKey() != mutatedTimestamp;
|
||||
}
|
||||
|
||||
public final CardCollectionView getDevouredCards() {
|
||||
@@ -1029,6 +1031,24 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
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) {
|
||||
if (flipResult == null) {
|
||||
return null;
|
||||
|
||||
@@ -20,6 +20,8 @@ package forge.game.card;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.StaticData;
|
||||
import forge.card.*;
|
||||
@@ -820,4 +822,50 @@ public class CardFactory {
|
||||
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
|
||||
|
||||
@@ -1319,6 +1319,9 @@ public class CardFactoryUtil {
|
||||
if (sq[0].contains("TimesPseudokicked")) {
|
||||
return doXMath(c.getPseudoKickerMagnitude(), m, c);
|
||||
}
|
||||
if (sq[0].contains("TimesMutated")) {
|
||||
return doXMath(c.getTimesMutated(), m, c);
|
||||
}
|
||||
|
||||
// Count$IfCastInOwnMainPhase.<numMain>.<numNotMain> // 7/10
|
||||
if (sq[0].contains("IfCastInOwnMainPhase")) {
|
||||
|
||||
@@ -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/>.
|
||||
*/
|
||||
@@ -68,7 +68,7 @@ public class CardState extends GameObject implements IHasSVars {
|
||||
private Map<String, String> sVars = Maps.newTreeMap();
|
||||
|
||||
private KeywordCollection cachedKeywords = new KeywordCollection();
|
||||
|
||||
|
||||
private CardRarity rarity = CardRarity.Unknown;
|
||||
private String setCode = CardEdition.UNKNOWN.getCode();
|
||||
|
||||
@@ -138,7 +138,7 @@ public class CardState extends GameObject implements IHasSVars {
|
||||
view.updateType(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final void setCreatureTypes(Collection<String> ctypes) {
|
||||
if (type.setCreatureTypes(ctypes)) {
|
||||
view.updateType(this);
|
||||
@@ -595,17 +595,11 @@ public class CardState extends GameObject implements IHasSVars {
|
||||
}
|
||||
}
|
||||
|
||||
staticAbilities.clear();
|
||||
for (StaticAbility sa : source.staticAbilities) {
|
||||
if (sa.isIntrinsic()) {
|
||||
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) {
|
||||
@@ -625,11 +619,11 @@ public class CardState extends GameObject implements IHasSVars {
|
||||
public String getSetCode() {
|
||||
return setCode;
|
||||
}
|
||||
|
||||
|
||||
public CardTypeView getTypeWithChanges() {
|
||||
return getType().getTypeWithChanges(card.getChangedCardTypes());
|
||||
}
|
||||
|
||||
|
||||
public void setSetCode(String setCode0) {
|
||||
setCode = setCode0;
|
||||
view.updateSetCode(this);
|
||||
|
||||
@@ -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/>.
|
||||
*/
|
||||
@@ -26,7 +26,7 @@ import forge.game.player.Player;
|
||||
* <p>
|
||||
* PlayerZoneComesIntoPlay class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
|
||||
* @version $Id$
|
||||
@@ -62,7 +62,7 @@ public class PlayerZoneBattlefield extends PlayerZone {
|
||||
|
||||
super.add(c, position, latestState);
|
||||
|
||||
if (trigger && !c.isMerged()) {
|
||||
if (trigger) {
|
||||
c.setSickness(true); // summoning sickness
|
||||
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