mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 05:22:02 -08:00
* AI: improved support of "as though mana" abilities (now computer can choose correct mana ability to pay, example: Draugr Necromancer);
* Dev: added card's LKI support of multi part cards (mdf/split/adventure); * Dev: improved support of adding/removing counters from mdf cards; * Draugr Necromancer - fixed support of mdf/split/adventure cards (#7620);
This commit is contained in:
parent
adc945748b
commit
dda69cd009
12 changed files with 227 additions and 65 deletions
|
|
@ -1,27 +1,30 @@
|
|||
|
||||
package mage.abilities.effects;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.ManaType;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPoolItem;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public interface AsThoughManaEffect extends AsThoughEffect {
|
||||
|
||||
// return a mana type that can be used to pay a mana cost instead of the normally needed mana type
|
||||
/**
|
||||
* Return a mana type that can be used to pay a mana cost instead of the normally needed mana type
|
||||
* <p>
|
||||
* Usage instructions:
|
||||
* - on non applied effect: must return null (example: basic object applied, by you need to check a mana source too, see Draugr Necromancer);
|
||||
* - on applied effect: must return compatible manaType with ManaPoolItem (e.g. return mana.getFirstAvailable)
|
||||
*
|
||||
* @param manaType type of mana with which the player wants to pay the cost
|
||||
* @param mana mana pool item to pay from the cost
|
||||
* @param manaType type of mana with which the player wants to pay the cost
|
||||
* @param mana mana pool item to pay from the cost
|
||||
* @param affectedControllerId
|
||||
* @param source
|
||||
* @param game
|
||||
* @return
|
||||
* @return null on non applied effect
|
||||
*/
|
||||
ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game);
|
||||
|
||||
|
|
|
|||
|
|
@ -612,6 +612,23 @@ public class ContinuousEffects implements Serializable {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fit paying mana type with current mana pool (if asThoughMana affected)
|
||||
* <p>
|
||||
* Example:
|
||||
* - you need to pay {R} as cost
|
||||
* - asThoughMana effect allows to use {G} as any color;
|
||||
* - asThoughMana effect must change/fit paying mana type from {R} to {G}
|
||||
* - after that you can pay {G} as cost
|
||||
*
|
||||
* @param manaType paying mana type
|
||||
* @param mana checking pool item
|
||||
* @param objectId paying ability's source object
|
||||
* @param affectedAbility paying ability
|
||||
* @param controllerId controller who pay
|
||||
* @param game
|
||||
* @return corrected paying mana type (same if no asThough effects and different on applied asThough effect)
|
||||
*/
|
||||
public ManaType asThoughMana(ManaType manaType, ManaPoolItem mana, UUID objectId, Ability affectedAbility, UUID controllerId, Game game) {
|
||||
// First check existing only effects
|
||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(AsThoughEffectType.SPEND_ONLY_MANA, game);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import mage.abilities.*;
|
|||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.constants.*;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.Counters;
|
||||
import mage.game.Game;
|
||||
import mage.game.GameState;
|
||||
|
|
@ -123,6 +124,16 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
|
|||
return state.getCardState(leftHalfCard.getId()).getCounters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List<UUID> appliedEffects, boolean isEffect) {
|
||||
return leftHalfCard.addCounters(counter, playerAddingCounters, source, game, appliedEffects, isEffect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCounters(String name, int amount, Ability source, Game game) {
|
||||
leftHalfCard.removeCounters(name, amount, source, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
|
||||
switch (ability.getSpellAbilityType()) {
|
||||
|
|
|
|||
|
|
@ -2985,39 +2985,25 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
if (object instanceof Permanent || object instanceof StackObject) {
|
||||
MageObject copy = object.copy();
|
||||
|
||||
Map<UUID, MageObject> lkiMap = lki.get(zone);
|
||||
if (lkiMap != null) {
|
||||
lkiMap.put(objectId, copy);
|
||||
} else {
|
||||
Map<UUID, MageObject> newMap = new HashMap<>();
|
||||
newMap.put(objectId, copy);
|
||||
lki.put(zone, newMap);
|
||||
}
|
||||
Map<UUID, MageObject> lkiMap = lki.computeIfAbsent(zone, k -> new HashMap<>());
|
||||
lkiMap.put(objectId, copy);
|
||||
|
||||
// remembers if a object was in a zone during the resolution of an effect
|
||||
// e.g. Wrath destroys all and you the question is is the replacement effect to apply because the source was also moved by the same effect
|
||||
// because it happens all at the same time the replacement effect has still to be applied
|
||||
Set<UUID> idSet = shortLivingLKI.computeIfAbsent(zone, k -> new HashSet<>());
|
||||
idSet.add(objectId);
|
||||
if (object instanceof Permanent) {
|
||||
Map<Integer, MageObject> lkiExtendedMap = lkiExtended.get(objectId);
|
||||
if (lkiExtendedMap != null) {
|
||||
lkiExtendedMap.put(object.getZoneChangeCounter(this), copy);
|
||||
} else {
|
||||
lkiExtendedMap = new HashMap<>();
|
||||
lkiExtendedMap.put(object.getZoneChangeCounter(this), copy);
|
||||
lkiExtended.put(objectId, lkiExtendedMap);
|
||||
}
|
||||
Map<Integer, MageObject> lkiExtendedMap = lkiExtended.computeIfAbsent(objectId, k -> new HashMap<>());
|
||||
lkiExtendedMap.put(object.getZoneChangeCounter(this), copy);
|
||||
}
|
||||
} else if (zone.isPublicZone()) {
|
||||
// Remember card state in this public zone (mainly removed/gained abilities)
|
||||
Map<UUID, CardState> lkiMap = lkiCardState.get(zone);
|
||||
if (lkiMap != null) {
|
||||
lkiMap.put(objectId, getState().getCardState(objectId));
|
||||
} else {
|
||||
Map<UUID, CardState> newMap = new HashMap<>();
|
||||
newMap.put(objectId, getState().getCardState(objectId).copy());
|
||||
lkiCardState.put(zone, newMap);
|
||||
}
|
||||
// Must save all card parts (mdf, split)
|
||||
CardUtil.getObjectParts(object).forEach(partId -> {
|
||||
Map<UUID, CardState> lkiMap = lkiCardState.computeIfAbsent(zone, k -> new HashMap<>());
|
||||
lkiMap.put(partId, getState().getCardState(partId).copy());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -339,15 +339,28 @@ public class ManaPool implements Serializable {
|
|||
Mana mana = manaToAdd.copy();
|
||||
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source, playerId, mana))) {
|
||||
if (mana instanceof ConditionalMana) {
|
||||
ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game),
|
||||
((ConditionalMana) mana).getManaProducerOriginalId() != null
|
||||
? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId());
|
||||
ConditionalMana conditionalMana = (ConditionalMana) mana;
|
||||
ManaPoolItem item = new ManaPoolItem(
|
||||
conditionalMana,
|
||||
source.getSourceObject(game),
|
||||
conditionalMana.getManaProducerOriginalId() != null ? conditionalMana.getManaProducerOriginalId() : source.getOriginalId()
|
||||
);
|
||||
if (emptyOnTurnsEnd) {
|
||||
item.setDuration(Duration.EndOfTurn);
|
||||
}
|
||||
this.manaItems.add(item);
|
||||
} else {
|
||||
ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getGeneric() + mana.getColorless(), source.getSourceObject(game), source.getOriginalId(), mana.getFlag());
|
||||
ManaPoolItem item = new ManaPoolItem(
|
||||
mana.getRed(),
|
||||
mana.getGreen(),
|
||||
mana.getBlue(),
|
||||
mana.getWhite(),
|
||||
mana.getBlack(),
|
||||
mana.getGeneric() + mana.getColorless(),
|
||||
source.getSourceObject(game),
|
||||
source.getOriginalId(),
|
||||
mana.getFlag()
|
||||
);
|
||||
if (emptyOnTurnsEnd) {
|
||||
item.setDuration(Duration.EndOfTurn);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import mage.cards.ModalDoubleFacesCard;
|
|||
import mage.cards.SplitCard;
|
||||
import mage.choices.Choice;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.constants.ManaType;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
|
@ -699,6 +700,23 @@ public final class ManaUtil {
|
|||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all used mana types in mana cost (wubrg + colorless)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static List<ManaType> getManaTypesInCost(ManaCost cost) {
|
||||
List<ManaType> res = new ArrayList<>();
|
||||
for (Mana mana : cost.getManaOptions()) {
|
||||
if (mana.getWhite() > 0) res.add(ManaType.WHITE);
|
||||
if (mana.getBlue() > 0) res.add(ManaType.BLUE);
|
||||
if (mana.getBlack() > 0) res.add(ManaType.BLACK);
|
||||
if (mana.getRed() > 0) res.add(ManaType.RED);
|
||||
if (mana.getGreen() > 0) res.add(ManaType.GREEN);
|
||||
if (mana.getColorless() > 0 || mana.getGeneric() > 0 || mana.getAny() > 0) res.add(ManaType.COLORLESS);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue