mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
GUI: introduced default card hints:
* refactor: added helper emblems instead rad counter's inherent emblems (use initGameDefaultHelperEmblems to define new card hints or other fake objects); * refactor: added card hints support for emblems, planes and other command objects; * GUI: added storm counter as default card hint (use hints tool to see it, closes #12360);
This commit is contained in:
parent
83823acec7
commit
521a0f6e32
36 changed files with 234 additions and 144 deletions
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.keyword.ProtectionAbility;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -18,7 +19,6 @@ import java.util.function.Predicate;
|
|||
* interface for this.
|
||||
*
|
||||
* @param <T> The ability type this collection will hold.
|
||||
*
|
||||
* @see mage.abilities.AbilitiesImpl
|
||||
* @see mage.abilities.DelayedTriggeredAbilities
|
||||
* @see mage.abilities.SpecialActions
|
||||
|
|
@ -27,25 +27,27 @@ import java.util.function.Predicate;
|
|||
public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
||||
|
||||
/**
|
||||
* Retrieves a {@link List}<{@link String}> of ability texts for the
|
||||
* given source.
|
||||
*
|
||||
* @param source The source to retrieve ability texts.
|
||||
* @return the {@link List}<{@link String}> of ability texts.
|
||||
*
|
||||
* @see mage.cards.CardImpl#getRules()
|
||||
* @see mage.abilities.keyword.LevelAbility#getRule()
|
||||
* Return rules as part of another rules. Use it for text generation.
|
||||
*/
|
||||
List<String> getRules(String source);
|
||||
List<String> getRules();
|
||||
|
||||
List<String> getRules(String source, boolean capitalize);
|
||||
/**
|
||||
* Return rules as part of another rules. Use it for text generation.
|
||||
*/
|
||||
List<String> getRules(boolean capitalize);
|
||||
|
||||
/**
|
||||
* Return full rules with card hints. Use it for user's data like GameView
|
||||
*
|
||||
* @param game on null will ignore card hints
|
||||
*/
|
||||
List<String> getRules(Game game, MageObject object);
|
||||
|
||||
/**
|
||||
* Retrieves all activated abilities for the given {@link Zone}.
|
||||
*
|
||||
* @param zone The {@link Zone} for which abilities should be retrieved.
|
||||
* @return All abilities for the given {@link Zone}
|
||||
*
|
||||
* @see mage.cards.CardImpl#getSpellAbility()
|
||||
*/
|
||||
Abilities<ActivatedAbility> getActivatedAbilities(Zone zone);
|
||||
|
|
@ -55,7 +57,6 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
*
|
||||
* @param zone The {@link Zone} for which abilities should be retrieved.
|
||||
* @return All abilities for the given {@link Zone}
|
||||
*
|
||||
* @see mage.cards.CardImpl#getSpellAbility()
|
||||
*/
|
||||
Abilities<ActivatedAbility> getPlayableAbilities(Zone zone);
|
||||
|
|
@ -65,10 +66,9 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
* given {@link Zone}.
|
||||
*
|
||||
* @param zone The {@link Zone} to search for
|
||||
* {@link ActivatedManaAbilityImpl mana abilities}.
|
||||
* {@link ActivatedManaAbilityImpl mana abilities}.
|
||||
* @return All {@link ActivatedManaAbilityImpl mana abilities} for the given
|
||||
* {@link Zone}.
|
||||
*
|
||||
* @see mage.cards.CardImpl#getMana()
|
||||
* @see mage.players.PlayerImpl#getManaAvailable(mage.game.Game)
|
||||
* @see mage.players.PlayerImpl#getAvailableManaProducers(mage.game.Game)
|
||||
|
|
@ -80,12 +80,11 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
* Retrieves all {@link ActivatedManaAbilityImpl mana abilities} in the
|
||||
* given {@link Zone} that can be used.
|
||||
*
|
||||
* @param zone The {@link Zone} to search for
|
||||
* {@link ActivatedManaAbilityImpl mana abilities}.
|
||||
* @param zone The {@link Zone} to search for
|
||||
* {@link ActivatedManaAbilityImpl mana abilities}.
|
||||
* @param playerId The id of the player to check availability for
|
||||
* @return All {@link ActivatedManaAbilityImpl mana abilities} for the given
|
||||
* {@link Zone} that can be used.
|
||||
*
|
||||
* @see mage.cards.CardImpl#getMana()
|
||||
* @see mage.players.PlayerImpl#getManaAvailable(mage.game.Game)
|
||||
* @see mage.players.PlayerImpl#getAvailableManaProducers(mage.game.Game)
|
||||
|
|
@ -99,22 +98,16 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
* @param zone The {@link Zone} to search for {@link StaticAbility}
|
||||
* @return All {@link StaticAbility static abilities} in the given
|
||||
* {@link Zone}
|
||||
*
|
||||
* @see
|
||||
* mage.abilities.effects.ContinuousEffects#getLayeredEffects(mage.game.Game)
|
||||
* @see
|
||||
* mage.abilities.effects.ContinuousEffects#getApplicableRequirementEffects(mage.game.permanent.Permanent,
|
||||
* @see mage.abilities.effects.ContinuousEffects#getLayeredEffects(mage.game.Game)
|
||||
* @see mage.abilities.effects.ContinuousEffects#getApplicableRequirementEffects(mage.game.permanent.Permanent,
|
||||
* mage.game.Game)
|
||||
* @see
|
||||
* mage.abilities.effects.ContinuousEffects#getApplicableRestrictionEffects(mage.game.permanent.Permanent,
|
||||
* @see mage.abilities.effects.ContinuousEffects#getApplicableRestrictionEffects(mage.game.permanent.Permanent,
|
||||
* mage.game.Game)
|
||||
* @see
|
||||
* mage.abilities.effects.ContinuousEffects#getApplicableReplacementEffects(mage.game.events.GameEvent,
|
||||
* @see mage.abilities.effects.ContinuousEffects#getApplicableReplacementEffects(mage.game.events.GameEvent,
|
||||
* mage.game.Game)
|
||||
* @see mage.abilities.effects.ContinuousEffects#asThough(java.util.UUID,
|
||||
* mage.constants.AsThoughEffectType, mage.game.Game)
|
||||
* @see
|
||||
* mage.abilities.effects.ContinuousEffects#costModification(mage.abilities.Ability,
|
||||
* @see mage.abilities.effects.ContinuousEffects#costModification(mage.abilities.Ability,
|
||||
* mage.game.Game)
|
||||
*/
|
||||
Abilities<StaticAbility> getStaticAbilities(Zone zone);
|
||||
|
|
@ -131,16 +124,13 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
* {@link Zone}.
|
||||
*
|
||||
* @param zone The {@link Zone} to search for
|
||||
* {@link TriggeredAbility triggered abilities}
|
||||
* {@link TriggeredAbility triggered abilities}
|
||||
* @return All found {@link TriggeredAbility triggered abilities}.
|
||||
*
|
||||
* @see mage.cards.CardImpl#checkTriggers(mage.constants.Zone,
|
||||
* mage.game.events.GameEvent, mage.game.Game)
|
||||
* @see
|
||||
* mage.game.permanent.PermanentImpl#checkTriggers(mage.game.events.GameEvent,
|
||||
* @see mage.game.permanent.PermanentImpl#checkTriggers(mage.game.events.GameEvent,
|
||||
* mage.game.Game)
|
||||
* @see
|
||||
* mage.game.permanent.PermanentCard#checkPermanentOnlyTriggers(mage.game.events.ZoneChangeEvent,
|
||||
* @see mage.game.permanent.PermanentCard#checkPermanentOnlyTriggers(mage.game.events.ZoneChangeEvent,
|
||||
* mage.game.Game)
|
||||
*/
|
||||
Abilities<TriggeredAbility> getTriggeredAbilities(Zone zone);
|
||||
|
|
@ -149,16 +139,17 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
* Retrieves all {@link ProtectionAbility protection abilities}.
|
||||
*
|
||||
* @return All found {@link ProtectionAbility protection abilities}.
|
||||
*
|
||||
* @see mage.game.permanent.PermanentImpl#hasProtectionFrom(mage.MageObject)
|
||||
* @see mage.players.PlayerImpl#hasProtectionFrom(mage.MageObject)
|
||||
* @see mage.players.PlayerImpl#canDamage(mage.MageObject)
|
||||
*/
|
||||
Abilities<ProtectionAbility> getProtectionAbilities();
|
||||
|
||||
Abilities<Ability> getAllAbilities();
|
||||
|
||||
/**
|
||||
* TODO Method is unused, keep it around?
|
||||
*
|
||||
* <p>
|
||||
* The only implementation seems to want to use this for totally a set of
|
||||
* abilities by some arbitrary numeral value. Possibly a good method to be
|
||||
* used by the AI's?
|
||||
|
|
@ -172,7 +163,6 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
* Sets the controller of this set of abilities.
|
||||
*
|
||||
* @param controllerId
|
||||
*
|
||||
* @see mage.cards.CardImpl#setControllerId(java.util.UUID)
|
||||
* @see mage.cards.CardImpl#setOwnerId(java.util.UUID)
|
||||
* @see mage.game.permanent.PermanentImpl#changeControllerId(java.util.UUID,
|
||||
|
|
@ -185,7 +175,6 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
* Sets the source of this set of abilities.
|
||||
*
|
||||
* @param sourceId
|
||||
*
|
||||
* @see mage.cards.CardImpl#assignNewId()
|
||||
*/
|
||||
void setSourceId(UUID sourceId);
|
||||
|
|
@ -212,7 +201,7 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
|
||||
/**
|
||||
* TODO Method is unused, keep it around?
|
||||
*
|
||||
* <p>
|
||||
* Gets the ability represented by the given abilityId.
|
||||
*
|
||||
* @param abilityId
|
||||
|
|
@ -224,7 +213,7 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
* TODO The usage of this method seems redundant to that of
|
||||
* {@link #containsKey(java.util.UUID)} minus the fact that it searches for
|
||||
* exact instances instead of id's of singleton Abilities.
|
||||
*
|
||||
* <p>
|
||||
* Searches for the exact instance of the passed in ability.
|
||||
*
|
||||
* @param ability
|
||||
|
|
@ -261,10 +250,11 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
|
||||
/**
|
||||
* Returns true if one or more of the abilities are activated mana abilities with the pollDependant flag set to true.
|
||||
* @return
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasPoolDependantAbilities();
|
||||
|
||||
|
||||
/**
|
||||
* Copies this set of abilities. This copy should be new instances of all
|
||||
* the contained abilities.
|
||||
|
|
@ -275,12 +265,15 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
|
||||
String getValue();
|
||||
|
||||
@Deprecated // use permanent.removeAbility instead
|
||||
@Deprecated
|
||||
// use permanent.removeAbility instead
|
||||
boolean remove(Object o);
|
||||
|
||||
@Deprecated // use permanent.removeAbility instead
|
||||
@Deprecated
|
||||
// use permanent.removeAbility instead
|
||||
boolean removeAll(Collection<?> c);
|
||||
|
||||
@Deprecated // use permanent.removeAbility instead
|
||||
@Deprecated
|
||||
// use permanent.removeAbility instead
|
||||
boolean removeIf(Predicate<? super T> filter);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.common.ZoneChangeTriggeredAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.keyword.ProtectionAbility;
|
||||
|
|
@ -7,6 +8,7 @@ import mage.abilities.mana.ActivatedManaAbilityImpl;
|
|||
import mage.abilities.mana.ManaAbility;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.ThreadLocalStringBuilder;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
@ -44,12 +46,12 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRules(String source) {
|
||||
return getRules(source, true);
|
||||
public List<String> getRules() {
|
||||
return getRules(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRules(String source, boolean capitalize) {
|
||||
public List<String> getRules(boolean capitalize) {
|
||||
List<String> rules = new ArrayList<>();
|
||||
|
||||
for (T ability : this) {
|
||||
|
|
@ -102,6 +104,12 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
return rules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRules(Game game, MageObject object) {
|
||||
Abilities<Ability> sourceAbilities = this.getAllAbilities();
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(game, object, sourceAbilities, sourceAbilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activated Ability in the engine are broader than in the rules.
|
||||
* Notably SpellAbility & PlayLandAbility are ActivatedAbility,
|
||||
|
|
@ -193,6 +201,11 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
.collect(Collectors.toCollection(AbilitiesImpl::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<Ability> getAllAbilities() {
|
||||
return stream().collect(Collectors.toCollection(AbilitiesImpl::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControllerId(UUID controllerId) {
|
||||
for (Ability ability : this) {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class CompoundAbility extends AbilitiesImpl<Ability> {
|
|||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
List<String> rules = super.getRules(null, false);
|
||||
List<String> rules = super.getRules(false);
|
||||
for (int index = 0; index < rules.size(); index++) {
|
||||
if (index > 0) {
|
||||
if (index < rules.size() - 1) {
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ public class TriggeredAbilities extends LinkedHashMap<String, TriggeredAbility>
|
|||
public void removeAbilitiesOfNonExistingSources(Game game) {
|
||||
// e.g. Token that had triggered abilities
|
||||
entrySet().removeIf(entry -> game.getObject(entry.getValue().getSourceId()) == null
|
||||
&& game.getState().getInherentEmblems().stream().noneMatch(emblem -> emblem.getId().equals(entry.getValue().getSourceId()))
|
||||
&& game.getState().getHelperEmblems().stream().noneMatch(emblem -> emblem.getId().equals(entry.getValue().getSourceId()))
|
||||
&& game.getState().getDesignations().stream().noneMatch(designation -> designation.getId().equals(entry.getValue().getSourceId())));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1338,7 +1338,7 @@ public class ContinuousEffects implements Serializable {
|
|||
effectsMap.put(key, object.getIdName() + ": " + ability.getRule(object.getName()));
|
||||
objectsMap.put(key, object);
|
||||
} else {
|
||||
effectsMap.put(key, entry.getKey().getText(null));
|
||||
effectsMap.put(key, entry.getKey().getText(ability.getModes().getMode()));
|
||||
objectsMap.put(key, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class GetEmblemEffect extends OneShotEffect {
|
|||
public String getText() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("you get an emblem with \"");
|
||||
List<String> rules = emblem.getAbilities().getRules(null);
|
||||
List<String> rules = emblem.getAbilities().getRules();
|
||||
sb.append(rules.get(0));
|
||||
if (rules.size() == 2) {
|
||||
sb.append("\" and \"");
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import mage.game.Game;
|
|||
import mage.game.command.Emblem;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -54,6 +55,6 @@ public class GetEmblemTargetPlayerEffect extends OneShotEffect {
|
|||
return staticText;
|
||||
}
|
||||
return getTargetPointer().describeTargets(mode.getTargets(), "that player")
|
||||
+ " gets an emblem with \"" + String.join("; ", emblem.getAbilities().getRules(null)) + "\"";
|
||||
+ " gets an emblem with \"" + String.join("; ", emblem.getAbilities().getRules()) + "\"";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ public class LevelerCardBuilder {
|
|||
sb.append(power);
|
||||
sb.append('/');
|
||||
sb.append(toughness);
|
||||
List<String> abilityText = abilities.getRules("{this}");
|
||||
List<String> abilityText = abilities.getRules();
|
||||
if (!abilityText.isEmpty()) {
|
||||
sb.append("<br>");
|
||||
sb.append(abilityText.stream().collect(Collectors.joining("<br>")));
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ public abstract class AdventureCard extends CardImpl {
|
|||
public List<String> getSharedRules(Game game) {
|
||||
// rules without spellcard
|
||||
Abilities<Ability> sourceAbilities = this.getSharedAbilities(game);
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities);
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(game, this, sourceAbilities, sourceAbilities);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -230,13 +230,13 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
@Override
|
||||
public List<String> getRules() {
|
||||
Abilities<Ability> sourceAbilities = this.getAbilities();
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), sourceAbilities, sourceAbilities);
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(this, sourceAbilities, sourceAbilities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRules(Game game) {
|
||||
Abilities<Ability> sourceAbilities = this.getAbilities(game);
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities);
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(game, this, sourceAbilities, sourceAbilities);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -358,8 +358,7 @@ public abstract class ModalDoubleFacedCard extends CardImpl implements CardWithH
|
|||
// rules must show only main side (another side visible by toggle/transform button in GUI)
|
||||
// card hints from both sides
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(
|
||||
this.getId(),
|
||||
this.getName(),
|
||||
this,
|
||||
this.getInnerAbilities(true, false),
|
||||
this.getInnerAbilities(true, true)
|
||||
);
|
||||
|
|
@ -371,8 +370,7 @@ public abstract class ModalDoubleFacedCard extends CardImpl implements CardWithH
|
|||
// card hints from both sides
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(
|
||||
game,
|
||||
this.getId(),
|
||||
this.getName(),
|
||||
this,
|
||||
this.getInnerAbilities(game, true, false),
|
||||
this.getInnerAbilities(game, true, true)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -189,8 +189,7 @@ public abstract class SplitCard extends CardImpl implements CardWithHalves {
|
|||
public List<String> getRules() {
|
||||
Abilities<Ability> sourceAbilities = this.getAbilities();
|
||||
List<String> res = CardUtil.getCardRulesWithAdditionalInfo(
|
||||
this.getId(),
|
||||
this.getName(),
|
||||
this,
|
||||
sourceAbilities,
|
||||
sourceAbilities
|
||||
);
|
||||
|
|
@ -205,8 +204,7 @@ public abstract class SplitCard extends CardImpl implements CardWithHalves {
|
|||
Abilities<Ability> sourceAbilities = this.getAbilities(game);
|
||||
List<String> res = CardUtil.getCardRulesWithAdditionalInfo(
|
||||
game,
|
||||
this.getId(),
|
||||
this.getName(),
|
||||
this,
|
||||
sourceAbilities,
|
||||
sourceAbilities
|
||||
);
|
||||
|
|
|
|||
|
|
@ -119,8 +119,7 @@ public class MockSplitCard extends SplitCard implements MockableCard {
|
|||
// so a MockSplitCard must ignore it (duplicate fix)
|
||||
Abilities<Ability> sourceAbilities = this.getAbilities();
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(
|
||||
this.getId(),
|
||||
this.getName(),
|
||||
this,
|
||||
sourceAbilities,
|
||||
sourceAbilities
|
||||
);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ public enum TokenRepository {
|
|||
public static final String XMAGE_IMAGE_NAME_NIGHT = "Night";
|
||||
public static final String XMAGE_IMAGE_NAME_THE_MONARCH = "The Monarch";
|
||||
public static final String XMAGE_IMAGE_NAME_RADIATION = "Radiation";
|
||||
public static final String XMAGE_IMAGE_NAME_HELPER_EMBLEM = "Helper Emblem";
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TokenRepository.class);
|
||||
|
||||
|
|
@ -305,6 +306,10 @@ public enum TokenRepository {
|
|||
// Radiation (for trigger)
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_RADIATION, 1, "https://api.scryfall.com/cards/tpip/22/en?format=image"));
|
||||
|
||||
// Helper emblem (for global card hints)
|
||||
// use backface for it
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_HELPER_EMBLEM, 1, "https://upload.wikimedia.org/wikipedia/en/a/aa/Magic_the_gathering-card_back.jpg"));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import mage.game.command.dungeons.UndercityDungeon;
|
|||
import mage.game.command.emblems.EmblemOfCard;
|
||||
import mage.game.command.emblems.RadiationEmblem;
|
||||
import mage.game.command.emblems.TheRingEmblem;
|
||||
import mage.game.command.emblems.XmageHelperEmblem;
|
||||
import mage.game.events.*;
|
||||
import mage.game.events.TableEvent.EventType;
|
||||
import mage.game.mulligan.Mulligan;
|
||||
|
|
@ -444,7 +445,7 @@ public abstract class GameImpl implements Game {
|
|||
return designation;
|
||||
}
|
||||
}
|
||||
for (Emblem emblem : state.getInherentEmblems()) {
|
||||
for (Emblem emblem : state.getHelperEmblems()) {
|
||||
if (emblem.getId().equals(objectId)) {
|
||||
return emblem;
|
||||
}
|
||||
|
|
@ -1400,13 +1401,7 @@ public abstract class GameImpl implements Game {
|
|||
}
|
||||
}
|
||||
|
||||
// Rad counter mechanic for every player
|
||||
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
|
||||
// This is not a real emblem. Just a fake source for the
|
||||
// inherent trigger ability related to Rad counters
|
||||
// Faking a source just to display something on the stack ability.
|
||||
state.addInherentEmblem(new RadiationEmblem(), playerId);
|
||||
}
|
||||
initGameDefaultHelperEmblems();
|
||||
}
|
||||
|
||||
public void initGameDefaultWatchers() {
|
||||
|
|
@ -1447,6 +1442,22 @@ public abstract class GameImpl implements Game {
|
|||
getState().addWatcher(bloodthirstWatcher);
|
||||
}
|
||||
|
||||
public void initGameDefaultHelperEmblems() {
|
||||
|
||||
// Rad Counter's trigger source
|
||||
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
|
||||
// This is not a real emblem. Just a fake source for the
|
||||
// inherent trigger ability related to Rad counters
|
||||
// Faking a source just to display something on the stack ability.
|
||||
state.addHelperEmblem(new RadiationEmblem(), playerId);
|
||||
}
|
||||
|
||||
// global card hints for better UX
|
||||
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
|
||||
state.addHelperEmblem(new XmageHelperEmblem().withCardHint("storm counter", StormAbility.getHint()), playerId);
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendStartMessage(Player choosingPlayer, Player startingPlayer) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
if (choosingPlayer != null) {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
private boolean isPlaneChase;
|
||||
private List<String> seenPlanes = new ArrayList<>();
|
||||
private List<Designation> designations = new ArrayList<>();
|
||||
private List<Emblem> inherentEmblems = new ArrayList<>();
|
||||
private List<Emblem> helperEmblems = new ArrayList<>(); // fake emblems for inner usage like better UX
|
||||
private Exile exile;
|
||||
private Battlefield battlefield;
|
||||
private int turnNum = 1;
|
||||
|
|
@ -158,7 +158,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.isPlaneChase = state.isPlaneChase;
|
||||
this.seenPlanes.addAll(state.seenPlanes);
|
||||
this.designations.addAll(state.designations);
|
||||
this.inherentEmblems = CardUtil.deepCopyObject(state.inherentEmblems);
|
||||
this.helperEmblems = CardUtil.deepCopyObject(state.helperEmblems);
|
||||
this.exile = state.exile.copy();
|
||||
this.battlefield = state.battlefield.copy();
|
||||
this.turnNum = state.turnNum;
|
||||
|
|
@ -206,7 +206,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
exile.clear();
|
||||
command.clear();
|
||||
designations.clear();
|
||||
inherentEmblems.clear();
|
||||
helperEmblems.clear();
|
||||
seenPlanes.clear();
|
||||
isPlaneChase = false;
|
||||
revealed.clear();
|
||||
|
|
@ -248,7 +248,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.isPlaneChase = state.isPlaneChase;
|
||||
this.seenPlanes = state.seenPlanes;
|
||||
this.designations = state.designations;
|
||||
this.inherentEmblems = state.inherentEmblems;
|
||||
this.helperEmblems = state.helperEmblems;
|
||||
this.exile = state.exile;
|
||||
this.battlefield = state.battlefield;
|
||||
this.turnNum = state.turnNum;
|
||||
|
|
@ -519,12 +519,12 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
return designations;
|
||||
}
|
||||
|
||||
public List<Emblem> getInherentEmblems() {
|
||||
return inherentEmblems;
|
||||
public List<Emblem> getHelperEmblems() {
|
||||
return helperEmblems;
|
||||
}
|
||||
|
||||
public Plane getCurrentPlane() {
|
||||
if (command != null && command.size() > 0) {
|
||||
if (command != null && !command.isEmpty()) {
|
||||
for (CommandObject cobject : command) {
|
||||
if (cobject instanceof Plane) {
|
||||
return (Plane) cobject;
|
||||
|
|
@ -1184,16 +1184,13 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Inherent triggers (Rad counters) in the rules have no source.
|
||||
* However to fit better with the engine, we make a fake emblem source,
|
||||
* which is not displayed in any game zone. That allows the trigger to
|
||||
* have a source, which helps with a bunch of situation like hosting,
|
||||
* rather than having a trigger.
|
||||
* Add fake/helper emblems for hidden source of inherent triggers like Rad counters,
|
||||
* additional card hints like storm counter, etc. See GameImpl.initGameDefaultHelperEmblems
|
||||
* <p>
|
||||
* Should not be used except in very specific situations
|
||||
* It allows game and GUI find and show full card object in stack's triggers, logs and hints popup.
|
||||
*/
|
||||
public void addInherentEmblem(Emblem emblem, UUID controllerId) {
|
||||
getInherentEmblems().add(emblem);
|
||||
public void addHelperEmblem(Emblem emblem, UUID controllerId) {
|
||||
helperEmblems.add(emblem);
|
||||
emblem.setControllerId(controllerId);
|
||||
for (Ability ability : emblem.getInitAbilities()) {
|
||||
ability.setControllerId(controllerId);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import mage.constants.SubType;
|
|||
import mage.constants.SuperType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.SubTypes;
|
||||
|
||||
import java.util.Collections;
|
||||
|
|
@ -37,7 +38,7 @@ public abstract class Emblem extends CommandObjectImpl {
|
|||
private boolean copy;
|
||||
private MageObject copyFrom; // copied card INFO (used to call original adjusters)
|
||||
protected FrameStyle frameStyle;
|
||||
private Abilities<Ability> abilites = new AbilitiesImpl<>();
|
||||
private Abilities<Ability> abilities = new AbilitiesImpl<>();
|
||||
|
||||
public Emblem(String name) {
|
||||
super(name);
|
||||
|
|
@ -50,7 +51,7 @@ public abstract class Emblem extends CommandObjectImpl {
|
|||
this.sourceObject = emblem.sourceObject;
|
||||
this.copy = emblem.copy;
|
||||
this.copyFrom = emblem.copyFrom;
|
||||
this.abilites = emblem.abilites.copy();
|
||||
this.abilities = emblem.abilities.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -73,7 +74,7 @@ public abstract class Emblem extends CommandObjectImpl {
|
|||
this.setImageFileName(""); // use default
|
||||
this.setImageNumber(foundInfo.getImageNumber());
|
||||
} else {
|
||||
// how-to fix: add emblem to the tokens-database
|
||||
// how-to fix: add emblem to tokens-database.txt
|
||||
throw new IllegalArgumentException("Wrong code usage: can't find token info for the emblem: " + this.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
|
@ -98,7 +99,7 @@ public abstract class Emblem extends CommandObjectImpl {
|
|||
|
||||
public void setControllerId(UUID controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
this.abilites.setControllerId(controllerId);
|
||||
this.abilities.setControllerId(controllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -152,7 +153,7 @@ public abstract class Emblem extends CommandObjectImpl {
|
|||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities() {
|
||||
return abilites;
|
||||
return abilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -260,7 +261,7 @@ public abstract class Emblem extends CommandObjectImpl {
|
|||
}
|
||||
|
||||
public void discardEffects() {
|
||||
for (Ability ability : abilites) {
|
||||
for (Ability ability : abilities) {
|
||||
for (Effect effect : ability.getEffects()) {
|
||||
if (effect instanceof ContinuousEffect) {
|
||||
((ContinuousEffect) effect).discard();
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public class RadiationEmblem extends Emblem {
|
|||
this.setImageFileName(""); // use default
|
||||
this.setImageNumber(foundInfo.getImageNumber());
|
||||
} else {
|
||||
// how-to fix: add emblem to the tokens-database
|
||||
// how-to fix: add emblem to the tokens-database TokenRepository->loadXmageTokens
|
||||
throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_RADIATION);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
package mage.game.command.emblems;
|
||||
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.cards.FrameStyle;
|
||||
import mage.cards.repository.TokenInfo;
|
||||
import mage.cards.repository.TokenRepository;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.command.Emblem;
|
||||
|
||||
/**
|
||||
* GUI: inner xmage emblem to show additional info for players like global hints
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class XmageHelperEmblem extends Emblem {
|
||||
|
||||
public XmageHelperEmblem() {
|
||||
super("Helper Emblem");
|
||||
this.frameStyle = FrameStyle.M15_NORMAL;
|
||||
|
||||
TokenInfo foundInfo = TokenRepository.instance.findPreferredTokenInfoForXmage(TokenRepository.XMAGE_IMAGE_NAME_HELPER_EMBLEM, null);
|
||||
if (foundInfo != null) {
|
||||
this.setExpansionSetCode(foundInfo.getSetCode());
|
||||
this.setUsesVariousArt(false);
|
||||
this.setCardNumber("");
|
||||
this.setImageFileName(""); // use default
|
||||
this.setImageNumber(foundInfo.getImageNumber());
|
||||
} else {
|
||||
// how-to fix: add emblem to the tokens-database TokenRepository->loadXmageTokens
|
||||
throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_HELPER_EMBLEM);
|
||||
}
|
||||
}
|
||||
|
||||
private XmageHelperEmblem(final XmageHelperEmblem card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmageHelperEmblem copy() {
|
||||
return new XmageHelperEmblem(this);
|
||||
}
|
||||
|
||||
public XmageHelperEmblem withCardHint(String name, Hint hint) {
|
||||
this.getAbilities().add(new SimpleStaticAbility(
|
||||
Zone.ALL,
|
||||
new InfoEffect(name)).addHint(hint)
|
||||
);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -1207,9 +1207,9 @@ public final class CardUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static List<String> getCardRulesWithAdditionalInfo(UUID cardId, String cardName,
|
||||
public static List<String> getCardRulesWithAdditionalInfo(MageObject object,
|
||||
Abilities<Ability> rulesSource, Abilities<Ability> hintAbilities) {
|
||||
return getCardRulesWithAdditionalInfo(null, cardId, cardName, rulesSource, hintAbilities);
|
||||
return getCardRulesWithAdditionalInfo(null, object, rulesSource, hintAbilities);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1218,10 +1218,10 @@ public final class CardUtil {
|
|||
* @param rulesSource abilities list to show as rules
|
||||
* @param hintsSource abilities list to show as card hints only (you can add additional hints here; example: from second or transformed side)
|
||||
*/
|
||||
public static List<String> getCardRulesWithAdditionalInfo(Game game, UUID cardId, String cardName,
|
||||
public static List<String> getCardRulesWithAdditionalInfo(Game game, MageObject object,
|
||||
Abilities<Ability> rulesSource, Abilities<Ability> hintsSource) {
|
||||
try {
|
||||
List<String> rules = rulesSource.getRules(cardName);
|
||||
List<String> rules = rulesSource.getRules();
|
||||
|
||||
if (game == null || game.getPhase() == null) {
|
||||
// dynamic hints for started game only
|
||||
|
|
@ -1229,7 +1229,10 @@ public final class CardUtil {
|
|||
}
|
||||
|
||||
// additional effect's info from card.addInfo methods
|
||||
rules.addAll(game.getState().getCardState(cardId).getInfo().values());
|
||||
CardState cardState = game.getState().getCardState(object.getId());
|
||||
if (cardState != null) {
|
||||
rules.addAll(cardState.getInfo().values());
|
||||
}
|
||||
|
||||
// ability hints
|
||||
List<String> abilityHints = new ArrayList<>();
|
||||
|
|
@ -1254,7 +1257,7 @@ public final class CardUtil {
|
|||
|
||||
return rules;
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception in rules generation for card: " + cardName, e);
|
||||
logger.error("Exception in rules generation for object: " + object.getName(), e);
|
||||
}
|
||||
return RULES_ERROR_INFO;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -658,7 +658,7 @@ public final class ManaUtil {
|
|||
return getColorIdentity(
|
||||
token.getColor(),
|
||||
String.join("", token.getManaCostSymbols()),
|
||||
token.getAbilities().getRules(token.getName()),
|
||||
token.getAbilities().getRules(),
|
||||
token.getBackFace() == null ? null : token.getBackFace().getCopySourceCard()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@ package mage.util;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
// Author: alexander-novo
|
||||
// A helper class for facilitating the multi-choose dialog
|
||||
/**
|
||||
* A helper class for facilitating the multi-choose dialog
|
||||
*
|
||||
* @author alexander-novo
|
||||
*/
|
||||
public class MultiAmountMessage implements Serializable {
|
||||
public String message;
|
||||
public int min;
|
||||
|
|
@ -20,4 +23,9 @@ public class MultiAmountMessage implements Serializable {
|
|||
this.max = max;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s - from %d to %d - default %d", message, min, max, defaultValue);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue