* Added Dream Halls and Curse of chain (and what was neccessary to get Dream Hall costs to work).

This commit is contained in:
LevelX2 2014-07-27 11:22:22 +02:00
parent f86ef6f625
commit c8d76cdaaf
18 changed files with 299 additions and 32 deletions

View file

@ -224,7 +224,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
if (target instanceof TargetCardInHand) {
List<Card> cards = new ArrayList<>();
for (UUID cardId: ((TargetCardInHand)target).possibleTargets(this.getId(), hand, game)) {
for (UUID cardId: ((TargetCardInHand)target).possibleTargets(sourceId, this.getId(), game)) {
Card card = game.getCard(cardId);
if (card != null) {
cards.add(card);

View file

@ -269,8 +269,9 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
@Override
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) {
if (this.isHuman())
if (this.isHuman()) {
return chooseRandom(target, game);
}
return super.choose(outcome, target, sourceId, game, options);
}
@ -303,8 +304,9 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
@Override
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
if (cards.isEmpty())
if (cards.isEmpty()) {
return !target.isRequired(source);
}
Card card = cards.getRandom(game);
target.addTarget(card.getId(), source, game);
return true;
@ -313,8 +315,9 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
@Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
Set<UUID> possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game);
if (possibleTargets.isEmpty())
if (possibleTargets.isEmpty()) {
return !target.isRequired(source);
}
if (!target.isRequired(source)) {
if (rnd.nextInt(possibleTargets.size() + 1) == 0) {
return false;
@ -341,8 +344,9 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
@Override
public boolean chooseUse(Outcome outcome, String message, Game game) {
if (this.isHuman())
if (this.isHuman()) {
return rnd.nextBoolean();
}
return super.chooseUse(outcome, message, game);
}
@ -370,15 +374,17 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
@Override
public int chooseEffect(List<String> rEffects, Game game) {
if (this.isHuman())
if (this.isHuman()) {
return rnd.nextInt(rEffects.size());
}
return super.chooseEffect(rEffects, game);
}
@Override
public TriggeredAbility chooseTriggeredAbility(List<TriggeredAbility> abilities, Game game) {
if (this.isHuman())
if (this.isHuman()) {
return abilities.get(rnd.nextInt(abilities.size()));
}
return super.chooseTriggeredAbility(abilities, game);
}
@ -387,8 +393,9 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
if (this.isHuman()) {
Iterator<Mode> it = modes.values().iterator();
Mode mode = it.next();
if (modes.size() == 1)
if (modes.size() == 1) {
return mode;
}
int modeNum = rnd.nextInt(modes.values().size());
for (int i = 0; i < modeNum; i++) {
mode = it.next();

View file

@ -221,7 +221,7 @@ public class HumanPlayer extends PlayerImpl {
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) {
updateGameStatePriority("choose(5)", game);
while (!abort) {
Set<UUID> cards = target.possibleTargets(null, playerId, game);
Set<UUID> cards = target.possibleTargets(sourceId, playerId, game);
if (cards == null || cards.isEmpty()) {
return false;
}
@ -232,6 +232,9 @@ public class HumanPlayer extends PlayerImpl {
game.fireSelectTargetEvent(playerId, target.getMessage(), cards, required, options);
waitForResponse(game);
if (response.getUUID() != null) {
if (!cards.contains(response.getUUID())) {
continue;
}
if (target instanceof TargetPermanent) {
if (((TargetPermanent)target).canTarget(playerId, response.getUUID(), sourceId, game, false)) {
target.add(response.getUUID(), game);

View file

@ -55,7 +55,7 @@ public class FistOfSuns extends CardImpl {
this.expansionSetCode = "5DN";
// You may pay {W}{U}{B}{R}{G} rather than pay the mana cost for spells that you cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MirrorGalleryRuleEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new FistOfSunsRuleEffect()));
}
public FistOfSuns(final FistOfSuns card) {
@ -69,22 +69,22 @@ public class FistOfSuns extends CardImpl {
}
class MirrorGalleryRuleEffect extends ContinuousEffectImpl {
class FistOfSunsRuleEffect extends ContinuousEffectImpl {
static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new ManaCostsImpl("{W}{U}{B}{R}{G}"));
public MirrorGalleryRuleEffect() {
public FistOfSunsRuleEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "You may pay {W}{U}{B}{R}{G} rather than pay the mana cost for spells that you cast";
}
public MirrorGalleryRuleEffect(final MirrorGalleryRuleEffect effect) {
public FistOfSunsRuleEffect(final FistOfSunsRuleEffect effect) {
super(effect);
}
@Override
public MirrorGalleryRuleEffect copy() {
return new MirrorGalleryRuleEffect(this);
public FistOfSunsRuleEffect copy() {
return new FistOfSunsRuleEffect(this);
}
@Override

View file

@ -0,0 +1,77 @@
/*
* 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.sets.shadowmoor;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.TapEnchantedEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author LevelX2
*/
public class CurseOfChains extends CardImpl {
public CurseOfChains(UUID ownerId) {
super(ownerId, 139, "Curse of Chains", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W/U}");
this.expansionSetCode = "SHM";
this.subtype.add("Aura");
this.color.setBlue(true);
this.color.setWhite(true);
// Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.Tap));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
// At the beginning of each upkeep, tap enchanted creature.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TapEnchantedEffect(), TargetController.ANY, false));
}
public CurseOfChains(final CurseOfChains card) {
super(card);
}
@Override
public CurseOfChains copy() {
return new CurseOfChains(this);
}
}

View file

@ -0,0 +1,120 @@
/*
* 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.sets.stronghold;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.common.DiscardCardCost;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.SubLayer;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.AnotherCardPredicate;
import mage.filter.predicate.mageobject.SharesColorWithSourcePredicate;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author LevelX2
*/
public class DreamHalls extends CardImpl {
public DreamHalls(UUID ownerId) {
super(ownerId, 28, "Dream Halls", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}");
this.expansionSetCode = "STH";
this.color.setBlue(true);
// Rather than pay the mana cost for a spell, its controller may discard a card that shares a color with that spell.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DreamHallsEffect()));
}
public DreamHalls(final DreamHalls card) {
super(card);
}
@Override
public DreamHalls copy() {
return new DreamHalls(this);
}
}
class DreamHallsEffect extends ContinuousEffectImpl {
private static final FilterCard filter = new FilterCard("a card that shares a color with that spell");
static {
filter.add(new AnotherCardPredicate());
filter.add(new SharesColorWithSourcePredicate());
}
static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new DiscardCardCost(filter));
public DreamHallsEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "Rather than pay the mana cost for a spell, its controller may discard a card that shares a color with that spell";
}
public DreamHallsEffect(final DreamHallsEffect effect) {
super(effect);
}
@Override
public DreamHallsEffect copy() {
return new DreamHallsEffect(this);
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
controller.getAlternativeSourceCosts().add(alternativeCastingCostAbility);
return true;
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.RulesEffects;
}
}

View file

@ -106,7 +106,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
Player player = game.getPlayer(ability.getControllerId());
if (player != null) {
if (alternateCosts.canPay(ability.getSourceId(), ability.getControllerId(), game) &&
player.chooseUse(Outcome.Detriment, alternateCosts.isEmpty() ? "Cast without paying its mana cost?":"Pay alternative costs?", game)) {
player.chooseUse(Outcome.Detriment, alternateCosts.isEmpty() ? "Cast without paying its mana cost?":"Pay alternative costs? (" + alternateCosts.getText() +")", game)) {
ability.getManaCostsToPay().clear();
ability.getCosts().clear();
for (Cost cost : alternateCosts) {
@ -121,7 +121,11 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
}
}
}
} else {
return false;
}
} else {
return false;
}
}
return isActivated(ability, game);

View file

@ -41,10 +41,18 @@ public class DiscardCardCost extends DiscardTargetCost {
}
public DiscardCardCost(boolean randomDiscard) {
super(new TargetCardInHand(new FilterCard(randomDiscard ?"a card at random":"a card")), randomDiscard);
this(new FilterCard(randomDiscard ?"a card at random":"a card"), randomDiscard);
}
public DiscardCardCost(DiscardCardCost cost) {
public DiscardCardCost(FilterCard filter) {
this(filter, false);
}
public DiscardCardCost(FilterCard filter, boolean randomDiscard) {
super(new TargetCardInHand(filter), randomDiscard);
}
public DiscardCardCost(final DiscardCardCost cost) {
super(cost);
}

View file

@ -87,7 +87,7 @@ public class DiscardTargetCost extends CostImpl {
return false;
}
this.cards.add(card.copy());
paid |= player.discard(card, null, game);
paid |= player.discard(card, ability, game);
}
}
}
@ -96,7 +96,7 @@ public class DiscardTargetCost extends CostImpl {
@Override
public boolean canPay(UUID sourceId, UUID controllerId, Game game) {
return targets.canChoose(controllerId, game);
return targets.canChoose(sourceId, controllerId, game);
}
@Override

View file

@ -80,7 +80,7 @@ public class CastCardFromOutsideTheGameEffect extends OneShotEffect {
return false;
}
Set<Card> filtered = cards.getCards(filterCard, game);
Set<Card> filtered = cards.getCards(filterCard, source.getSourceId(), source.getControllerId(), game);
if (filtered.isEmpty()) {
game.informPlayer(player, "You have no " + filterCard.getMessage() + " outside the game.");
return false;

View file

@ -45,7 +45,7 @@ import mage.players.Player;
public class ReturnToHandFromGraveyardAllEffect extends OneShotEffect {
private FilterCard filter;
private final FilterCard filter;
public ReturnToHandFromGraveyardAllEffect(FilterCard filter) {
super(Outcome.ReturnToHand);
@ -65,7 +65,7 @@ public class ReturnToHandFromGraveyardAllEffect extends OneShotEffect {
for (UUID playerId : controller.getInRange()) {
Player player = game.getPlayer(playerId);
if (player != null) {
for (Card card :player.getGraveyard().getCards(filter, game)) {
for (Card card :player.getGraveyard().getCards(filter, source.getSourceId(), source.getControllerId(), game)) {
card.moveToZone(Zone.HAND, playerId, game, false);
}
}

View file

@ -45,6 +45,7 @@ public interface Cards extends Set<UUID>, Serializable {
void addAll(List<Card> createCards);
Set<Card> getCards(Game game);
Set<Card> getCards(FilterCard filter, Game game);
Set<Card> getCards(FilterCard filter, UUID sourceId, UUID playerId, Game game);
Collection<Card> getUniqueCards(Game game);
Card getRandom(Game game);
int count(FilterCard filter, Game game);

View file

@ -156,6 +156,18 @@ public class CardsImpl extends LinkedHashSet<UUID> implements Cards, Serializabl
return result;
}
@Override
public Set<Card> getCards(FilterCard filter, UUID sourceId, UUID playerId, Game game) {
Set<Card> cards = new LinkedHashSet<>();
for (UUID card: this) {
boolean match = filter.match(game.getCard(card), sourceId, playerId, game);
if (match) {
cards.add(game.getCard(card));
}
}
return cards;
}
@Override
public Set<Card> getCards(FilterCard filter, Game game) {
Set<Card> cards = new LinkedHashSet<>();

View file

@ -0,0 +1,35 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.filter.predicate.mageobject;
import mage.MageObject;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
/**
*
* @author LevelX2
*/
public class SharesColorWithSourcePredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<MageObject>> {
@Override
public boolean apply(ObjectSourcePlayer<MageObject> input, Game game) {
MageObject sourceObject = game.getObject(input.getSourceId());
if (sourceObject != null) {
return input.getObject().getColor().shares(sourceObject.getColor());
}
return false;
}
@Override
public String toString() {
return "shares a color";
}
}

View file

@ -92,7 +92,7 @@ public class TargetCard extends TargetObject {
if (player != null) {
switch (zone) {
case HAND:
for (Card card : player.getHand().getCards(filter, game)) {
for (Card card : player.getHand().getCards(filter, sourceId, sourceControllerId, game)) {
if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) {
possibleTargets++;
if (possibleTargets >= this.minNumberOfTargets) {
@ -102,7 +102,7 @@ public class TargetCard extends TargetObject {
}
break;
case GRAVEYARD:
for (Card card : player.getGraveyard().getCards(filter, game)) {
for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) {
if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) {
possibleTargets++;
if (possibleTargets >= this.minNumberOfTargets) {
@ -161,14 +161,14 @@ public class TargetCard extends TargetObject {
if (player != null) {
switch (zone) {
case HAND:
for (Card card : player.getHand().getCards(filter, game)) {
for (Card card : player.getHand().getCards(filter, sourceId, sourceControllerId, game)) {
if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) {
possibleTargets.add(card.getId());
}
}
break;
case GRAVEYARD:
for (Card card : player.getGraveyard().getCards(filter, game)) {
for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) {
if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) {
possibleTargets.add(card.getId());
}

View file

@ -77,7 +77,7 @@ public class Targets extends ArrayList<Target> {
public boolean choose(Outcome outcome, UUID playerId, UUID sourceId, Game game) {
if (this.size() > 0) {
if (!canChoose(playerId, game)) {
if (!canChoose(sourceId, playerId, game)) {
return false;
}
while (!isChosen()) {

View file

@ -86,7 +86,7 @@ public class TargetCardInHand extends TargetCard {
Set<UUID> possibleTargets = new HashSet<>();
Player player = game.getPlayer(playerId);
if (player != null) {
for (Card card : player.getHand().getCards(filter, game)) {
for (Card card : player.getHand().getCards(filter, sourceId, playerId, game)) {
if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, playerId))) {
possibleTargets.add(card.getId());
}
@ -100,7 +100,7 @@ public class TargetCardInHand extends TargetCard {
int possibleTargets = 0;
Player player = game.getPlayer(sourceControllerId);
if (player != null) {
for (Card card : player.getHand().getCards(filter, game)) {
for (Card card : player.getHand().getCards(filter, sourceId, sourceControllerId, game)) {
if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) {
possibleTargets++;
if (possibleTargets >= this.minNumberOfTargets) {

View file

@ -94,7 +94,7 @@ public class TargetCardInYourGraveyard extends TargetCard {
public Set<UUID> possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) {
Set<UUID> possibleTargets = new HashSet<>();
Player player = game.getPlayer(sourceControllerId);
for (Card card : player.getGraveyard().getCards(filter, game)) {
for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) {
if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) {
possibleTargets.add(card.getId());
}
@ -135,7 +135,7 @@ public class TargetCardInYourGraveyard extends TargetCard {
return true;
}
int possibleTargets = 0;
for (Card card : player.getGraveyard().getCards(filter, game)) {
for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) {
if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) {
possibleTargets++;
if (possibleTargets >= this.minNumberOfTargets) {