mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 13:32:06 -08:00
* Added new game mode: Oathbreaker (#5678);
This commit is contained in:
parent
adb666587b
commit
07cf5201ba
17 changed files with 1100 additions and 236 deletions
|
|
@ -0,0 +1,61 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* For oathbreaker game mode
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class SignatureSpellCastOnlyWithOathbreakerEffect extends ContinuousRuleModifyingEffectImpl {
|
||||
|
||||
private final Condition condition;
|
||||
private final UUID signatureSpell;
|
||||
|
||||
public SignatureSpellCastOnlyWithOathbreakerEffect(Condition condition, UUID signatureSpell) {
|
||||
super(Duration.EndOfGame, Outcome.Detriment);
|
||||
this.condition = condition;
|
||||
this.signatureSpell = signatureSpell;
|
||||
staticText = setText();
|
||||
}
|
||||
|
||||
private SignatureSpellCastOnlyWithOathbreakerEffect(final SignatureSpellCastOnlyWithOathbreakerEffect effect) {
|
||||
super(effect);
|
||||
this.condition = effect.condition;
|
||||
this.signatureSpell = effect.signatureSpell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.CAST_SPELL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (event.getSourceId().equals(signatureSpell)) {
|
||||
return condition != null && !condition.apply(game, source);
|
||||
}
|
||||
return false; // cast not prevented by this effect
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureSpellCastOnlyWithOathbreakerEffect copy() {
|
||||
return new SignatureSpellCastOnlyWithOathbreakerEffect(this);
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
StringBuilder sb = new StringBuilder("cast this spell only ");
|
||||
if (condition != null) {
|
||||
sb.append(' ').append(condition.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* For Oathbreaker game mode
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class OathbreakerOnBattlefieldCondition implements Condition {
|
||||
|
||||
private UUID playerId;
|
||||
private FilterControlledPermanent filter;
|
||||
|
||||
public OathbreakerOnBattlefieldCondition(UUID playerId, List<UUID> oathbreakersToSearch) {
|
||||
this.playerId = playerId;
|
||||
this.filter = new FilterControlledPermanent("oathbreaker on battlefield");
|
||||
if (oathbreakersToSearch != null && !oathbreakersToSearch.isEmpty()) {
|
||||
// any commander on battlefield
|
||||
List<PermanentIdPredicate> idsList = new ArrayList<>();
|
||||
for (UUID id : oathbreakersToSearch) {
|
||||
idsList.add(new PermanentIdPredicate(id));
|
||||
}
|
||||
this.filter.add(Predicates.or(idsList));
|
||||
} else {
|
||||
// random id to disable condition
|
||||
this.filter.add(new PermanentIdPredicate(UUID.randomUUID()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// source.getSourceId() is null for commander's effects
|
||||
int permanentsOnBattlefield = game.getBattlefield().count(this.filter, source.getSourceId(), playerId, game);
|
||||
return permanentsOnBattlefield > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filter.getMessage();
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ public abstract class GameCommanderImpl extends GameImpl {
|
|||
|
||||
@Override
|
||||
protected void init(UUID choosingPlayerId) {
|
||||
// Karn Liberated calls it to restart game, all data and commanders must be re-initialized
|
||||
|
||||
// plays watcher
|
||||
state.addWatcher(new CommanderPlaysCountWatcher());
|
||||
|
|
@ -49,20 +50,19 @@ public abstract class GameCommanderImpl extends GameImpl {
|
|||
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
|
||||
Player player = getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.getSideboard().isEmpty()) { // needed for restart game of e.g. Karn Liberated
|
||||
for (UUID commanderId : player.getCommandersIds()) {
|
||||
Card commander = this.getCard(commanderId);
|
||||
if (commander != null) {
|
||||
initCommander(commander, player);
|
||||
}
|
||||
// add new commanders
|
||||
for (UUID id : player.getSideboard()) {
|
||||
Card commander = this.getCard(id);
|
||||
if (commander != null) {
|
||||
addCommander(commander, player);
|
||||
}
|
||||
} else {
|
||||
while (!player.getSideboard().isEmpty()) {
|
||||
Card commander = this.getCard(player.getSideboard().iterator().next());
|
||||
if (commander != null) {
|
||||
player.addCommanderId(commander.getId());
|
||||
initCommander(commander, player);
|
||||
}
|
||||
}
|
||||
|
||||
// init commanders
|
||||
for (UUID commanderId : player.getCommandersIds()) {
|
||||
Card commander = this.getCard(commanderId);
|
||||
if (commander != null) {
|
||||
initCommander(commander, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,17 +75,27 @@ public abstract class GameCommanderImpl extends GameImpl {
|
|||
}
|
||||
|
||||
public void initCommander(Card commander, Player player) {
|
||||
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
|
||||
commander.moveToZone(Zone.COMMAND, null, this, true);
|
||||
commander.getAbilities().setControllerId(player.getId());
|
||||
ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary));
|
||||
ability.addEffect(new CommanderCostModification(commander.getId()));
|
||||
CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), checkCommanderDamage);
|
||||
|
||||
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
|
||||
initCommanderEffects(commander, player, ability);
|
||||
CommanderInfoWatcher watcher = initCommanderWatcher(commander, checkCommanderDamage);
|
||||
getState().addWatcher(watcher);
|
||||
watcher.addCardInfoToCommander(this);
|
||||
this.getState().addAbility(ability, null);
|
||||
}
|
||||
|
||||
public CommanderInfoWatcher initCommanderWatcher(Card commander, boolean checkCommanderDamage) {
|
||||
return new CommanderInfoWatcher("Commander", commander.getId(), checkCommanderDamage);
|
||||
}
|
||||
|
||||
public void initCommanderEffects(Card commander, Player player, Ability commanderAbility) {
|
||||
// all commander effects must be independent from sourceId or controllerId
|
||||
commanderAbility.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary));
|
||||
commanderAbility.addEffect(new CommanderCostModification(commander.getId()));
|
||||
}
|
||||
|
||||
//20130711
|
||||
/*903.8. The Commander variant uses an alternate mulligan rule.
|
||||
* Each time a player takes a mulligan, rather than shuffling their entire hand of cards into their library, that player exiles any number of cards from their hand face down.
|
||||
|
|
@ -207,4 +217,8 @@ public abstract class GameCommanderImpl extends GameImpl {
|
|||
this.checkCommanderDamage = checkCommanderDamage;
|
||||
}
|
||||
|
||||
public void addCommander(Card card, Player player) {
|
||||
player.addCommanderId(card.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2871,7 +2871,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
// as commander (only commander games, look at init code in GameCommanderImpl)
|
||||
if (this instanceof GameCommanderImpl) {
|
||||
for (Card card : command) {
|
||||
player.addCommanderId(card.getId());
|
||||
((GameCommanderImpl) this).addCommander(card, player);
|
||||
// no needs in initCommander call -- it's uses on game startup (init)
|
||||
}
|
||||
} else if (!command.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl {
|
|||
ability.addEffect(new CommanderCostModification(commander.getId()));
|
||||
// Commander rule #4 was removed Jan. 18, 2016
|
||||
// ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander)));
|
||||
CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), false);
|
||||
CommanderInfoWatcher watcher = new CommanderInfoWatcher("Commander", commander.getId(), false);
|
||||
getState().addWatcher(watcher);
|
||||
watcher.addCardInfoToCommander(this);
|
||||
this.getState().addAbility(ability, null);
|
||||
|
|
|
|||
|
|
@ -26,17 +26,20 @@ public class CommanderInfoWatcher extends Watcher {
|
|||
|
||||
private final Map<UUID, Integer> damageToPlayer = new HashMap<>();
|
||||
private final boolean checkCommanderDamage;
|
||||
private final String commanderTypeName;
|
||||
|
||||
public CommanderInfoWatcher(UUID commander, boolean checkCommanderDamage) {
|
||||
public CommanderInfoWatcher(String commanderTypeName, UUID commander, boolean checkCommanderDamage) {
|
||||
super(WatcherScope.CARD);
|
||||
this.sourceId = commander;
|
||||
this.checkCommanderDamage = checkCommanderDamage;
|
||||
this.commanderTypeName = commanderTypeName;
|
||||
}
|
||||
|
||||
public CommanderInfoWatcher(final CommanderInfoWatcher watcher) {
|
||||
super(watcher);
|
||||
this.damageToPlayer.putAll(watcher.damageToPlayer);
|
||||
this.checkCommanderDamage = watcher.checkCommanderDamage;
|
||||
this.commanderTypeName = watcher.commanderTypeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -78,7 +81,7 @@ public class CommanderInfoWatcher extends Watcher {
|
|||
}
|
||||
if (object != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<b>Commander</b>");
|
||||
sb.append("<b>" + commanderTypeName + "</b>");
|
||||
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||
int playsCount = watcher.getPlaysCount(sourceId);
|
||||
if (playsCount > 0) {
|
||||
|
|
@ -89,9 +92,9 @@ public class CommanderInfoWatcher extends Watcher {
|
|||
if (checkCommanderDamage) {
|
||||
for (Map.Entry<UUID, Integer> entry : damageToPlayer.entrySet()) {
|
||||
Player damagedPlayer = game.getPlayer(entry.getKey());
|
||||
sb.append("<b>Commander</b> did ").append(entry.getValue()).append(" combat damage to player ").append(damagedPlayer.getLogName()).append('.');
|
||||
sb.append("<b>" + commanderTypeName + "</b> did ").append(entry.getValue()).append(" combat damage to player ").append(damagedPlayer.getLogName()).append('.');
|
||||
this.addInfo(object, "Commander" + entry.getKey(),
|
||||
"<b>Commander</b> did " + entry.getValue() + " combat damage to player " + damagedPlayer.getLogName() + '.', game);
|
||||
"<b>" + commanderTypeName + "</b> did " + entry.getValue() + " combat damage to player " + damagedPlayer.getLogName() + '.', game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue