mirror of
https://github.com/magefree/mage.git
synced 2025-12-27 05:52:06 -08:00
Introduce new batch event for life lost for a specific player (#13071)
* Introduce new batch event for life lost for a specific player closes #12202, fix #10805 * implement [DSC] Valgavoth, Harrower of Souls * text fixes
This commit is contained in:
parent
95e986dee7
commit
d6cf207a8b
28 changed files with 693 additions and 707 deletions
|
|
@ -28,12 +28,12 @@ public class GainLoseLifeYourTurnTriggeredAbility extends TriggeredAbilityImpl {
|
|||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.GAINED_LIFE
|
||||
|| event.getType() == GameEvent.EventType.LOST_LIFE;
|
||||
|| event.getType() == GameEvent.EventType.LOST_LIFE_BATCH_FOR_ONE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return isControlledBy(game.getActivePlayerId())
|
||||
&& isControlledBy(event.getPlayerId());
|
||||
&& isControlledBy(event.getTargetId());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.TargetController;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.watchers.common.LifeLostThisTurnWatcher;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class LoseLifeFirstTimeEachTurnTriggeredAbility extends LoseLifeTriggeredAbility {
|
||||
|
||||
public LoseLifeFirstTimeEachTurnTriggeredAbility(Effect effect) {
|
||||
this(effect, TargetController.YOU, false, false);
|
||||
}
|
||||
|
||||
public LoseLifeFirstTimeEachTurnTriggeredAbility(Effect effect, TargetController targetController, boolean optional, boolean setTargetPointer) {
|
||||
super(effect, targetController, optional, setTargetPointer);
|
||||
addWatcher(new LifeLostThisTurnWatcher());
|
||||
}
|
||||
|
||||
protected LoseLifeFirstTimeEachTurnTriggeredAbility(final LoseLifeFirstTimeEachTurnTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoseLifeFirstTimeEachTurnTriggeredAbility copy() {
|
||||
return new LoseLifeFirstTimeEachTurnTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
LifeLostThisTurnWatcher watcher = game.getState().getWatcher(LifeLostThisTurnWatcher.class);
|
||||
return watcher != null
|
||||
&& watcher.timesLostLifeThisTurn(event.getTargetId()) <= 1
|
||||
&& super.checkTrigger(event, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateTriggerPhrase() {
|
||||
switch (targetController) {
|
||||
case YOU:
|
||||
return "Whenever you lose life for the first time each turn, ";
|
||||
case OPPONENT:
|
||||
return "Whenever an opponent loses life for the first time each turn, ";
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong code usage: not supported targetController: " + targetController);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.dynamicvalue.common.SavedLifeLossValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.LifeLostEvent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class LoseLifeTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<LifeLostEvent> {
|
||||
|
||||
protected final TargetController targetController;
|
||||
private final boolean setTargetPointer;
|
||||
|
||||
public LoseLifeTriggeredAbility(Effect effect) {
|
||||
this(effect, TargetController.YOU, false, false);
|
||||
}
|
||||
|
||||
public LoseLifeTriggeredAbility(Effect effect, TargetController targetController, boolean optional, boolean setTargetPointer) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.targetController = targetController;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
setTriggerPhrase(generateTriggerPhrase());
|
||||
}
|
||||
|
||||
protected LoseLifeTriggeredAbility(final LoseLifeTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.targetController = ability.targetController;
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoseLifeTriggeredAbility copy() {
|
||||
return new LoseLifeTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.LOST_LIFE_BATCH_FOR_ONE_PLAYER;
|
||||
}
|
||||
|
||||
private boolean filterPlayer(UUID playerId, Game game) {
|
||||
switch (targetController) {
|
||||
case YOU:
|
||||
return isControlledBy(playerId);
|
||||
case OPPONENT:
|
||||
return game.getOpponents(getControllerId()).contains(playerId);
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong code usage: not supported targetController: " + targetController);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!filterPlayer(event.getTargetId(), game)) {
|
||||
return false;
|
||||
}
|
||||
// if target id matches, all events in the batch are relevant
|
||||
this.getEffects().setValue(SavedLifeLossValue.getValueKey(), event.getAmount());
|
||||
if (setTargetPointer) {
|
||||
this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected String generateTriggerPhrase() {
|
||||
switch (targetController) {
|
||||
case YOU:
|
||||
return "Whenever you lose life, ";
|
||||
case OPPONENT:
|
||||
return "Whenever an opponent loses life, ";
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong code usage: not supported targetController: " + targetController);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public enum SavedLifeLossValue implements DynamicValue {
|
||||
MANY("many"),
|
||||
MUCH("much");
|
||||
|
||||
private final String message;
|
||||
|
||||
private static final String key = "SavedLifeLoss";
|
||||
|
||||
/**
|
||||
* value key used to store the amount of life lost
|
||||
*/
|
||||
public static String getValueKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
SavedLifeLossValue(String message) {
|
||||
this.message = "that " + message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return (Integer) effect.getValue(getValueKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedLifeLossValue copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -975,12 +975,17 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
// Combine multiple life loss events in the single event (batch)
|
||||
// see GameEvent.LOST_LIFE_BATCH
|
||||
|
||||
// existing batch
|
||||
// existing batchs
|
||||
boolean isLifeLostBatchUsed = false;
|
||||
boolean isSingleBatchUsed = false;
|
||||
for (GameEvent event : simultaneousEvents) {
|
||||
if (event instanceof LifeLostBatchEvent) {
|
||||
((LifeLostBatchEvent) event).addEvent(lifeLossEvent);
|
||||
isLifeLostBatchUsed = true;
|
||||
} else if (event instanceof LifeLostBatchForOnePlayerEvent
|
||||
&& event.getTargetId().equals(lifeLossEvent.getTargetId())) {
|
||||
((LifeLostBatchForOnePlayerEvent) event).addEvent(lifeLossEvent);
|
||||
isSingleBatchUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -988,6 +993,9 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
if (!isLifeLostBatchUsed) {
|
||||
addSimultaneousEvent(new LifeLostBatchEvent(lifeLossEvent), game);
|
||||
}
|
||||
if (!isSingleBatchUsed) {
|
||||
addSimultaneousEvent(new LifeLostBatchForOnePlayerEvent(lifeLossEvent), game);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSimultaneousTappedToBatch(TappedEvent tappedEvent, Game game) {
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ public class GameEvent implements Serializable {
|
|||
DAMAGE_CAUSES_LIFE_LOSS,
|
||||
PLAYER_LIFE_CHANGE,
|
||||
GAIN_LIFE, GAINED_LIFE,
|
||||
LOSE_LIFE, LOST_LIFE,
|
||||
/* LOSE_LIFE + LOST_LIFE
|
||||
targetId the id of the player loosing life
|
||||
sourceId sourceId of the ability which caused the lose
|
||||
|
|
@ -174,10 +173,17 @@ public class GameEvent implements Serializable {
|
|||
amount amount of life loss
|
||||
flag true = from combat damage - other from non combat damage
|
||||
*/
|
||||
LOST_LIFE_BATCH(true),
|
||||
|
||||
LOSE_LIFE, LOST_LIFE,
|
||||
/* LOST_LIFE_BATCH_FOR_ONE_PLAYER
|
||||
combines all life lost events for a player to a single batch (event)
|
||||
*/
|
||||
LOST_LIFE_BATCH_FOR_ONE_PLAYER(true),
|
||||
/* LOST_LIFE_BATCH
|
||||
combines all player life lost events to a single batch (event)
|
||||
*/
|
||||
LOST_LIFE_BATCH(true),
|
||||
|
||||
PLAY_LAND, LAND_PLAYED,
|
||||
CREATURE_CHAMPIONED,
|
||||
/* CREATURE_CHAMPIONED
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package mage.game.events;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class LifeLostBatchForOnePlayerEvent extends BatchEvent<LifeLostEvent> {
|
||||
|
||||
public LifeLostBatchForOnePlayerEvent(LifeLostEvent firstEvent) {
|
||||
super(EventType.LOST_LIFE_BATCH_FOR_ONE_PLAYER, true, false, false, firstEvent);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package mage.watchers.common;
|
||||
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.util.CardUtil;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class LifeLostThisTurnWatcher extends Watcher {
|
||||
|
||||
// player -> number of times (not amount!) that player lost life this turn.
|
||||
private final Map<UUID, Integer> playersLostLife = new HashMap<>();
|
||||
|
||||
public LifeLostThisTurnWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.LOST_LIFE_BATCH_FOR_ONE_PLAYER) {
|
||||
playersLostLife.compute(event.getTargetId(), CardUtil::setOrIncrementValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
playersLostLife.clear();
|
||||
}
|
||||
|
||||
public int timesLostLifeThisTurn(UUID playerId) {
|
||||
return playersLostLife.getOrDefault(playerId, 0);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue