Reworking effects that allow controlling combat (WIP) (#8159)

* reworked effects that allow controlling combat

* [AFC] Implemented Berserker's Frenzy

* [AFC] updated Berserker's Frenzy to roll correctly
This commit is contained in:
Evan Kranzler 2021-10-10 10:25:10 -04:00 committed by GitHub
parent f5177097cd
commit f7e821be2f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 366 additions and 492 deletions

View file

@ -27,6 +27,7 @@ public class RollDieWithResultTableEffect extends OneShotEffect {
private final String prefixText;
private final List<TableEntry> resultsTable = new ArrayList<>();
private final DynamicValue modifier;
private final int toIgnore;
public RollDieWithResultTableEffect() {
this(20);
@ -37,14 +38,15 @@ public class RollDieWithResultTableEffect extends OneShotEffect {
}
public RollDieWithResultTableEffect(int sides, String prefixText) {
this(sides, prefixText, StaticValue.get(0));
this(sides, prefixText, StaticValue.get(0), 0);
}
public RollDieWithResultTableEffect(int sides, String prefixText, DynamicValue modifier) {
public RollDieWithResultTableEffect(int sides, String prefixText, DynamicValue modifier, int toIgnore) {
super(Outcome.Benefit);
this.sides = sides;
this.prefixText = prefixText;
this.modifier = modifier;
this.toIgnore = toIgnore;
}
protected RollDieWithResultTableEffect(final RollDieWithResultTableEffect effect) {
@ -55,6 +57,7 @@ public class RollDieWithResultTableEffect extends OneShotEffect {
this.resultsTable.add(tableEntry.copy());
}
this.modifier = effect.modifier.copy();
this.toIgnore = effect.toIgnore;
}
@Override
@ -68,7 +71,9 @@ public class RollDieWithResultTableEffect extends OneShotEffect {
if (player == null) {
return false;
}
int result = player.rollDice(outcome, source, game, sides) + modifier.calculate(game, source, this);
int result = player.rollDice(
outcome, source, game, sides, 1 + toIgnore, toIgnore
).get(0) + modifier.calculate(game, source, this);
this.applyResult(result, game, source);
return true;
}

View file

@ -0,0 +1,80 @@
package mage.abilities.effects.common.combat;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.watchers.common.ControlCombatRedundancyWatcher;
/**
* @author L_J, TheElk801
*/
public class ChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl {
public ChooseBlockersEffect(Duration duration) {
super(duration, Outcome.Benefit, false, false);
}
private ChooseBlockersEffect(final ChooseBlockersEffect effect) {
super(effect);
}
@Override
public ChooseBlockersEffect copy() {
return new ChooseBlockersEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS;
}
@Override
public void init(Ability source, Game game) {
super.init(source, game);
ControlCombatRedundancyWatcher.addBlockingController(source.getControllerId(), this.duration, game);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (!ControlCombatRedundancyWatcher.checkBlockingController(source.getControllerId(), game)) {
game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply");
return false;
}
Player blockController = game.getPlayer(source.getControllerId());
if (blockController != null) {
game.getCombat().selectBlockers(blockController, source, game);
return true;
}
return false;
}
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
StringBuilder sb = new StringBuilder("you choose which creatures block this ");
switch (duration) {
case EndOfTurn:
sb.append("turn");
break;
case EndOfCombat:
sb.append("combat");
break;
default:
throw new IllegalArgumentException("duration type not supported");
}
sb.append(" and how those creatures block");
return sb.toString();
}
}

View file

@ -1,42 +0,0 @@
package mage.watchers.common;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.watchers.Watcher;
/**
* @author L_J
*/
public class ChooseBlockersRedundancyWatcher extends Watcher { // workaround for solving timestamp issues regarding "you choose which creatures block and how those creatures block" effects
public int copyCount = 0;
public int copyCountApply = 0;
public ChooseBlockersRedundancyWatcher() {
super(WatcherScope.GAME);
}
@Override
public void reset() {
super.reset();
copyCount = 0;
copyCountApply = 0;
}
@Override
public void watch(GameEvent event, Game game) {
}
public void increment() {
copyCount++;
copyCountApply = copyCount;
}
public void decrement() {
if (copyCountApply > 0) {
copyCountApply--;
}
}
}

View file

@ -0,0 +1,78 @@
package mage.watchers.common;
import mage.constants.Duration;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.watchers.Watcher;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author L_J
*/
public class ControlCombatRedundancyWatcher extends Watcher { // workaround for solving timestamp issues regarding "you choose which creatures block and how those creatures block" effects
private static final class PlayerDuration {
private final Duration duration;
private final UUID playerId;
private PlayerDuration(Duration duration, UUID playerId) {
this.duration = duration;
this.playerId = playerId;
}
private boolean isCombat() {
return duration == Duration.EndOfCombat;
}
private boolean isPlayer(UUID playerId) {
return playerId.equals(this.playerId);
}
}
private final List<PlayerDuration> attackingControllers = new ArrayList<>();
private final List<PlayerDuration> blockingControllers = new ArrayList<>();
public ControlCombatRedundancyWatcher() {
super(WatcherScope.GAME);
}
@Override
public void reset() {
super.reset();
attackingControllers.clear();
blockingControllers.clear();
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.END_COMBAT_STEP_POST) {
attackingControllers.removeIf(PlayerDuration::isCombat);
blockingControllers.removeIf(PlayerDuration::isCombat);
}
}
public static void addAttackingController(UUID playerId, Duration duration, Game game) {
ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class);
watcher.attackingControllers.add(0, new PlayerDuration(duration, playerId));
}
public static void addBlockingController(UUID playerId, Duration duration, Game game) {
ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class);
watcher.blockingControllers.add(0, new PlayerDuration(duration, playerId));
}
public static boolean checkAttackingController(UUID playerId, Game game) {
ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class);
return !watcher.attackingControllers.isEmpty() && watcher.attackingControllers.get(0).isPlayer(playerId);
}
public static boolean checkBlockingController(UUID playerId, Game game) {
ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class);
return !watcher.blockingControllers.isEmpty() && watcher.blockingControllers.get(0).isPlayer(playerId);
}
}