Merge origin/master

This commit is contained in:
Styxo 2016-04-06 20:23:15 +02:00
commit 7ff31fb12e
92 changed files with 2894 additions and 775 deletions

View file

@ -1167,6 +1167,9 @@ public abstract class AbilityImpl implements Ability {
public MageObject getSourceObjectIfItStillExists(Game game) {
MageObject currentObject = game.getObject(getSourceId());
if (currentObject != null) {
if (sourceObject == null) {
setSourceObject(currentObject, game);
}
MageObjectReference mor = new MageObjectReference(currentObject, game);
if (mor.getZoneChangeCounter() == getSourceObjectZoneChangeCounter()) {
// source object has meanwhile not changed zone

View file

@ -156,7 +156,7 @@ public class BeginningOfEndStepTriggeredAbility extends TriggeredAbilityImpl {
private String generateConditionString() {
if (interveningIfClauseCondition != null) {
return new StringBuilder(interveningIfClauseCondition.toString()).append(", ").toString();
return "if {this} is " + interveningIfClauseCondition.toString() + ", ";
}
switch (getZone()) {
case GRAVEYARD:

View file

@ -28,9 +28,9 @@
package mage.abilities.condition.common;
import java.util.UUID;
import mage.constants.CardType;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.constants.CardType;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -38,15 +38,15 @@ import mage.game.permanent.Permanent;
*
* @author North
*/
public class EnchantedCondition implements Condition {
public class EnchantedSourceCondition implements Condition {
private int numberOfEnchantments;
public EnchantedCondition() {
public EnchantedSourceCondition() {
this(1);
}
public EnchantedCondition(int numberOfEnchantments) {
public EnchantedSourceCondition(int numberOfEnchantments) {
this.numberOfEnchantments = numberOfEnchantments;
}
@ -58,7 +58,7 @@ public class EnchantedCondition implements Condition {
for (UUID uuid : permanent.getAttachments()) {
Permanent attached = game.getBattlefield().getPermanent(uuid);
if (attached != null && attached.getCardType().contains(CardType.ENCHANTMENT)) {
if (++numberOfFoundEnchantments >= numberOfEnchantments) {
if (++numberOfFoundEnchantments >= numberOfEnchantments) {
return true;
}
}
@ -66,4 +66,9 @@ public class EnchantedCondition implements Condition {
}
return (numberOfFoundEnchantments >= numberOfEnchantments);
}
@Override
public String toString() {
return "enchanted";
}
}

View file

@ -38,9 +38,9 @@ import mage.game.permanent.Permanent;
*
* @author nantuko
*/
public class EquippedCondition implements Condition {
public class EquippedSourceCondition implements Condition {
private static final EquippedCondition fInstance = new EquippedCondition();
private static final EquippedSourceCondition fInstance = new EquippedSourceCondition();
public static Condition getInstance() {
return fInstance;
@ -59,4 +59,10 @@ public class EquippedCondition implements Condition {
}
return false;
}
@Override
public String toString() {
return "equipped";
}
}

View file

@ -67,7 +67,7 @@ public class DiscardTargetCost extends CostImpl {
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
this.cards.clear();
this.targets.clearChosen();;
this.targets.clearChosen();
Player player = game.getPlayer(controllerId);
if (player == null) {
return false;

View file

@ -32,6 +32,7 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card;
import mage.constants.CardType;
import mage.constants.Duration;
@ -90,6 +91,10 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
UUID sourceId = event.getSourceId();
UUID controllerId = event.getPlayerId();
if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()) != null) {
card = card.getSecondCardFace();
}
// Aura cards that go to battlefield face down (Manifest) don't have to select targets
if (card.isFaceDown(game)) {
return false;
@ -167,6 +172,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
}
Player targetPlayer = game.getPlayer(targetId);
if (targetCard != null || targetPermanent != null || targetPlayer != null) {
card = game.getCard(event.getTargetId());
card.removeFromZone(game, fromZone, sourceId);
card.updateZoneChangeCounter(game);
PermanentCard permanent = new PermanentCard(card, (controllingPlayer == null ? card.getOwnerId() : controllingPlayer.getId()), game);
@ -200,7 +206,12 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
if (((ZoneChangeEvent) event).getToZone().equals(Zone.BATTLEFIELD)
&& !(((ZoneChangeEvent) event).getFromZone().equals(Zone.STACK))) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")) {
if (card != null && (card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")
|| // in case of transformable enchantments
(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()) != null
&& card.getSecondCardFace() != null
&& card.getSecondCardFace().getCardType().contains(CardType.ENCHANTMENT)
&& card.getSecondCardFace().hasSubtype("Aura")))) {
return true;
}
}

View file

@ -90,6 +90,10 @@ public class DoIfCostPaid extends OneShotEffect {
return game.getPlayer(source.getControllerId());
}
public Cost getCost() {
return cost;
}
@Override
public String getText(Mode mode) {
if (!staticText.isEmpty()) {

View file

@ -31,6 +31,7 @@ import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
@ -78,20 +79,30 @@ public class ReturnToBattlefieldUnderOwnerControlTargetEffect extends OneShotEff
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Card card = null;
Cards cardsToMove = new CardsImpl();
if (fromExileZone) {
UUID exilZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
if (exilZoneId != null) {
ExileZone exileZone = game.getExile().getExileZone(exilZoneId);
if (exileZone != null && getTargetPointer().getFirst(game, source) != null) {
card = exileZone.get(getTargetPointer().getFirst(game, source), game);
if (exileZone != null) {
for (UUID cardId : getTargetPointer().getTargets(game, source)) {
Card card = exileZone.get(cardId, game);
if (card != null) {
cardsToMove.add(card);
}
}
}
}
} else {
card = game.getCard(getTargetPointer().getFirst(game, source));
for (UUID cardId : getTargetPointer().getTargets(game, source)) {
Card card = game.getCard(cardId);
if (card != null) {
cardsToMove.add(card);
}
}
}
if (card != null) {
controller.moveCards(new CardsImpl(card).getCards(game),
if (!cardsToMove.isEmpty()) {
controller.moveCards(cardsToMove.getCards(game),
Zone.BATTLEFIELD, source, game, tapped, false, true, null);
return true;
}

View file

@ -53,6 +53,8 @@ public enum PlayerAction {
MANA_AUTO_PAYMENT_OFF,
MANA_AUTO_PAYMENT_RESTRICTED_ON,
MANA_AUTO_PAYMENT_RESTRICTED_OFF,
USE_FIRST_MANA_ABILITY_ON,
USE_FIRST_MANA_ABILITY_OFF,
RESET_AUTO_SELECT_REPLACEMENT_EFFECTS,
REVOKE_PERMISSIONS_TO_SEE_HAND_CARDS,
REQUEST_PERMISSION_TO_SEE_HAND_CARDS,

View file

@ -0,0 +1,12 @@
package mage.constants;
/**
* Allows user to either tap a land for the first mode directly (shortcut)
* or have the normal method which pops up a menu
*
* @author spjspj
*/
public enum UseFirstManaAbilityMode {
NORMAL, FIRST
}

View file

@ -348,6 +348,8 @@ public interface Game extends MageItem, Serializable {
void setManaPaymentMode(UUID playerId, boolean autoPayment);
void setManaPaymentModeRestricted(UUID playerId, boolean autoPaymentRestricted);
void setUseFirstManaAbility(UUID playerId, boolean useFirstManaAbility);
void undo(UUID playerId);

View file

@ -1238,6 +1238,14 @@ public abstract class GameImpl implements Game, Serializable {
}
}
@Override
public synchronized void setUseFirstManaAbility(UUID playerId, boolean useFirstManaAbility) {
Player player = state.getPlayer(playerId);
if (player != null) {
player.getUserData().setUseFirstManaAbility(useFirstManaAbility);
}
}
@Override
public void playPriority(UUID activePlayerId, boolean resuming) {
int errorContinueCounter = 0;

View file

@ -32,8 +32,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Abilities;
@ -59,6 +63,8 @@ import mage.game.combat.CombatGroup;
import mage.game.command.Command;
import mage.game.command.CommandObject;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.events.ZoneChangeGroupEvent;
import mage.game.permanent.Battlefield;
import mage.game.permanent.Permanent;
import mage.game.stack.SpellStack;
@ -655,7 +661,9 @@ public class GameState implements Serializable, Copyable<GameState> {
if (!simultaneousEvents.isEmpty() && !getTurn().isEndTurnRequested()) {
// it can happen, that the events add new simultaneous events, so copy the list before
List<GameEvent> eventsToHandle = new ArrayList<>();
List<GameEvent> eventGroups = createEventGroups(simultaneousEvents, game);
eventsToHandle.addAll(simultaneousEvents);
eventsToHandle.addAll(eventGroups);
simultaneousEvents.clear();
for (GameEvent event : eventsToHandle) {
this.handleEvent(event, game);
@ -684,6 +692,76 @@ public class GameState implements Serializable, Copyable<GameState> {
return effects.replaceEvent(event, game);
}
public List<GameEvent> createEventGroups(List<GameEvent> events, Game game) {
class ZoneChangeData {
private final Zone fromZone;
private final Zone toZone;
private final UUID sourceId;
private final UUID playerId;
public ZoneChangeData(UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) {
this.sourceId = sourceId;
this.playerId = playerId;
this.fromZone = fromZone;
this.toZone = toZone;
}
@Override
public int hashCode() {
return (this.fromZone.ordinal() + 1) * 1
+ (this.toZone.ordinal() + 1) * 10
+ (this.sourceId != null ? this.sourceId.hashCode() : 0)
+ (this.playerId != null ? this.playerId.hashCode() : 0);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ZoneChangeData) {
ZoneChangeData data = (ZoneChangeData) obj;
return this.fromZone == data.fromZone
&& this.toZone == data.toZone
&& this.sourceId == data.sourceId
&& this.playerId == data.playerId;
}
return false;
}
}
Map<ZoneChangeData, List<GameEvent>> eventsByKey = new HashMap<>();
List<GameEvent> groupEvents = new LinkedList<>();
for (GameEvent event : events) {
if (event instanceof ZoneChangeEvent) {
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
ZoneChangeData key = new ZoneChangeData(castEvent.getSourceId(), castEvent.getPlayerId(), castEvent.getFromZone(), castEvent.getToZone());
if (eventsByKey.containsKey(key)) {
eventsByKey.get(key).add(event);
} else {
List<GameEvent> list = new LinkedList<>();
list.add(event);
eventsByKey.put(key, list);
}
}
}
for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
Set<Card> movedCards = new LinkedHashSet<>();
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext();) {
GameEvent event = it.next();
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
UUID targetId = castEvent.getTargetId();
Card card = game.getCard(targetId);
movedCards.add(card);
}
ZoneChangeData eventData = entry.getKey();
if (!movedCards.isEmpty()) {
ZoneChangeGroupEvent event = new ZoneChangeGroupEvent(movedCards, eventData.sourceId, eventData.playerId, eventData.fromZone, eventData.toZone);
groupEvents.add(event);
}
}
return groupEvents;
}
public void addCard(Card card) {
setZone(card.getId(), Zone.OUTSIDE);
for (Ability ability : card.getAbilities()) {

View file

@ -512,7 +512,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (canTransform) {
if (!replaceEvent(EventType.TRANSFORM, game)) {
setTransformed(!transformed);
fireEvent(EventType.TRANSFORMED, game);
game.applyEffects();
game.addSimultaneousEvent(GameEvent.getEvent(EventType.TRANSFORMED, getId(), getControllerId()));
return true;
}
}

View file

@ -123,7 +123,6 @@ import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.events.ZoneChangeGroupEvent;
import mage.game.match.MatchPlayer;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
@ -3249,9 +3248,6 @@ public abstract class PlayerImpl implements Player, Serializable {
default:
throw new UnsupportedOperationException("to Zone" + toZone.toString() + " not supported yet");
}
if (!successfulMovedCards.isEmpty()) {
game.fireEvent(new ZoneChangeGroupEvent(successfulMovedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone));
}
return successfulMovedCards.size() > 0;
}
@ -3267,7 +3263,6 @@ public abstract class PlayerImpl implements Player, Serializable {
if (cards.isEmpty()) {
return true;
}
game.fireEvent(new ZoneChangeGroupEvent(cards, source == null ? null : source.getSourceId(), this.getId(), null, Zone.EXILED));
boolean result = false;
for (Card card : cards) {
Zone fromZone = game.getState().getZone(card.getId());
@ -3368,7 +3363,6 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
}
game.fireEvent(new ZoneChangeGroupEvent(movedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, Zone.GRAVEYARD));
return movedCards;
}

View file

@ -22,6 +22,7 @@ public class UserData implements Serializable {
protected boolean passPriorityCast;
protected boolean passPriorityActivation;
protected boolean autoOrderTrigger;
protected boolean useFirstManaAbility;
protected String matchHistory;
protected int matchQuitRatio;
@ -31,7 +32,7 @@ public class UserData implements Serializable {
public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced,
boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps,
String flagName, boolean askMoveToGraveOrder, boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted,
boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger) {
boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger, boolean useFirstManaAbility) {
this.groupId = userGroup.getGroupId();
this.avatarId = avatarId;
this.showAbilityPickerForced = showAbilityPickerForced;
@ -45,6 +46,7 @@ public class UserData implements Serializable {
this.passPriorityCast = passPriorityCast;
this.passPriorityActivation = passPriorityActivation;
this.autoOrderTrigger = autoOrderTrigger;
this.useFirstManaAbility = useFirstManaAbility;
this.matchHistory = "";
this.matchQuitRatio = 0;
this.tourneyHistory = "";
@ -65,10 +67,11 @@ public class UserData implements Serializable {
this.passPriorityCast = userData.passPriorityCast;
this.passPriorityActivation = userData.passPriorityActivation;
this.autoOrderTrigger = userData.autoOrderTrigger;
this.useFirstManaAbility = userData.useFirstManaAbility;
}
public static UserData getDefaultUserDataView() {
return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, getDefaultFlagName(), false, true, true, false, false, false);
return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, getDefaultFlagName(), false, true, true, false, false, false, false);
}
public void setGroupId(int groupId) {
@ -175,6 +178,14 @@ public class UserData implements Serializable {
this.autoOrderTrigger = autoOrderTrigger;
}
public boolean isUseFirstManaAbility() {
return useFirstManaAbility;
}
public void setUseFirstManaAbility(boolean useFirstManaAbility) {
this.useFirstManaAbility = useFirstManaAbility;
}
public String getHistory() {
if (UserGroup.COMPUTER.equals(this.groupId)) {
return "";

View file

@ -78,11 +78,11 @@ public class CardUtil {
// Enchantment subtypes
"Aura", "Curse", "Shrine",
// Artifact subtypes
"Equipment", "Fortification", "Contraption",
"Clue", "Equipment", "Fortification", "Contraption",
// Land subtypes
"Desert", "Gate", "Lair", "Locus", "Urza's", "Mine", "Power-Plant", "Tower",
// Planeswalker subtypes
"Ajani", "Ashiok", "Bolas", "Chandra", "Dack", "Daretti", "Domri", "Elspeth", "Freyalise", "Garruk", "Gideon", "Jace",
"Ajani", "Arlinn", "Ashiok", "Bolas", "Chandra", "Dack", "Daretti", "Domri", "Elspeth", "Freyalise", "Garruk", "Gideon", "Jace",
"Karn", "Kiora", "Koth", "Liliana", "Nahiri", "Nissa", "Narset", "Nixilis", "Ral", "Sarkhan", "Sorin", "Tamiyo", "Teferi",
"Tezzeret", "Tibalt", "Ugin", "Venser", "Vraska", "Xenagos",
// Instant sorcery subtypes

View file

@ -0,0 +1,99 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.watchers.common;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
/**
*
* @author LevelX2
*/
public class CastFromGraveyardWatcher extends Watcher {
// holds which spell with witch zone change counter was cast from graveyard
private final Map<UUID, HashSet<Integer>> spellsCastFromGraveyard = new HashMap<>();
public CastFromGraveyardWatcher() {
super(CastFromGraveyardWatcher.class.getName(), WatcherScope.GAME);
}
public CastFromGraveyardWatcher(final CastFromGraveyardWatcher watcher) {
super(watcher);
}
@Override
public void watch(GameEvent event, Game game) {
/**
* This does still not handle if a spell is cast from hand and comes to
* play from other zones during the same step. But at least the state is
* reset if the game comes to a new step
*/
if (event.getType() == GameEvent.EventType.SPELL_CAST && event.getZone().equals(Zone.GRAVEYARD)) {
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null) {
HashSet<Integer> zcc = spellsCastFromGraveyard.get(spell.getSourceId());
if (zcc == null) {
zcc = new HashSet<>();
spellsCastFromGraveyard.put(spell.getSourceId(), zcc);
}
zcc.add(spell.getZoneChangeCounter(game));
}
}
}
public boolean spellWasCastFromGraveyard(UUID sourceId, int zcc) {
Set zccSet = spellsCastFromGraveyard.get(sourceId);
if (zccSet != null) {
return zccSet.contains(zcc);
}
return false;
}
@Override
public void reset() {
super.reset();
spellsCastFromGraveyard.clear();
}
@Override
public CastFromGraveyardWatcher copy() {
return new CastFromGraveyardWatcher(this);
}
}

View file

@ -0,0 +1,66 @@
package mage.watchers.common;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import static mage.game.events.GameEvent.EventType.CAST_SPELL;
import static mage.game.events.GameEvent.EventType.SPELL_CAST;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
/**
* @author jeffwadsworth
**/
public class FirstSpellCastThisTurnWatcher extends Watcher {
private final Map<UUID, UUID> playerFirstSpellCast = new HashMap<>();
private final Map<UUID, UUID> playerFirstCastSpell = new HashMap<>();
public FirstSpellCastThisTurnWatcher() {
super("FirstSpellCastThisTurn", WatcherScope.GAME);
}
public FirstSpellCastThisTurnWatcher(final FirstSpellCastThisTurnWatcher watcher) {
super(watcher);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case SPELL_CAST:
case CAST_SPELL:
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null && !playerFirstSpellCast.containsKey(spell.getControllerId())) {
if (event.getType().equals(EventType.SPELL_CAST)) {
playerFirstSpellCast.put(spell.getControllerId(), spell.getId());
} else if (event.getType().equals(EventType.CAST_SPELL)) {
playerFirstCastSpell.put(spell.getControllerId(), spell.getId());
}
}
}
}
@Override
public FirstSpellCastThisTurnWatcher copy() {
return new FirstSpellCastThisTurnWatcher(this);
}
@Override
public void reset() {
super.reset();
playerFirstSpellCast.clear();
playerFirstCastSpell.clear();
}
public UUID getIdOfFirstCastSpell(UUID playerId) {
if (playerFirstSpellCast.get(playerId) == null) {
return playerFirstCastSpell.get(playerId);
} else {
return playerFirstSpellCast.get(playerId);
}
}
}