[OTJ] Implementing "commit crime" mechanic (#11859)

* [OTJ] Implement Oko the Ringleader

* [OTJ] Implement Duelist of the Mind

* update implementation of crime mechanic to match new info

* [OTJ] Implement Marauding Sphinx

* [OTJ] Implement Hardbristle Bandit

* [OTJ] Implement Intimidation Campaign

* [OTJ] Implement Freestrider Lookout

* add initial test

* add more tests

* apply requested changes

* applied requested changes

* fix verify failure
This commit is contained in:
Evan Kranzler 2024-03-28 11:19:27 -04:00 committed by GitHub
parent 6d7f42e5d7
commit fa0f9f3d00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 896 additions and 2 deletions

View file

@ -0,0 +1,117 @@
package mage.abilities.common;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Controllable;
import mage.game.Game;
import mage.game.Ownerable;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
* @author TheElk801
*/
public class CommittedCrimeTriggeredAbility extends TriggeredAbilityImpl {
public CommittedCrimeTriggeredAbility(Effect effect) {
this(effect, false);
}
public CommittedCrimeTriggeredAbility(Effect effect, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
}
private CommittedCrimeTriggeredAbility(final CommittedCrimeTriggeredAbility ability) {
super(ability);
}
@Override
public CommittedCrimeTriggeredAbility copy() {
return new CommittedCrimeTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
switch (event.getType()) {
case SPELL_CAST:
case ACTIVATED_ABILITY:
case TRIGGERED_ABILITY:
return true;
default:
return false;
}
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return isControlledBy(getCriminal(event, game));
}
public static UUID getCriminal(GameEvent event, Game game) {
UUID controllerId;
Ability ability;
switch (event.getType()) {
case SPELL_CAST:
Spell spell = game.getSpell(event.getTargetId());
if (spell == null) {
return null;
}
controllerId = spell.getControllerId();
ability = spell.getSpellAbility();
break;
case ACTIVATED_ABILITY:
case TRIGGERED_ABILITY:
StackObject stackObject = game.getStack().getStackObject(event.getTargetId());
if (stackObject == null) {
return null;
}
controllerId = stackObject.getControllerId();
ability = stackObject.getStackAbility();
break;
default:
return null;
}
if (controllerId == null || ability == null) {
return null;
}
Set<UUID> opponents = game.getOpponents(controllerId);
Set<UUID> targets = CardUtil.getAllSelectedTargets(ability, game);
// an opponent
if (targets
.stream()
.anyMatch(opponents::contains)
// an opponent's permanent
|| targets
.stream()
.map(game::getPermanent)
.filter(Objects::nonNull)
.map(Controllable::getControllerId)
.anyMatch(opponents::contains)
// an opponent's spell or ability
|| targets
.stream()
.map(game.getStack()::getStackObject)
.filter(Objects::nonNull)
.map(Controllable::getControllerId)
.anyMatch(opponents::contains)
// a card in an opponent's graveyard
|| targets
.stream()
.filter(uuid -> Zone.GRAVEYARD.match(game.getState().getZone(uuid)))
.map(game::getCard)
.filter(Objects::nonNull)
.map(Ownerable::getOwnerId)
.anyMatch(opponents::contains)) {
return controllerId;
}
return null;
}
}

View file

@ -0,0 +1,32 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.abilities.hint.ConditionHint;
import mage.abilities.hint.Hint;
import mage.game.Game;
import mage.watchers.common.CommittedCrimeWatcher;
/**
* requires CommittedCrimeWatcher
*
* @author TheElk801
*/
public enum CommittedCrimeCondition implements Condition {
instance;
private static final Hint hint = new ConditionHint(instance, "You committed a crime this turn");
public static Hint getHint() {
return hint;
}
@Override
public boolean apply(Game game, Ability source) {
return CommittedCrimeWatcher.checkCriminality(game, source);
}
@Override
public String toString() {
return "you've committed a crime this turn";
}
}

View file

@ -0,0 +1,28 @@
package mage.game.permanent.token;
import mage.MageInt;
import mage.constants.CardType;
import mage.constants.SubType;
/**
* @author TheElk801
*/
public final class ElkToken extends TokenImpl {
public ElkToken() {
super("Elk Token", "3/3 green Elk creature token");
cardType.add(CardType.CREATURE);
color.setGreen(true);
subtype.add(SubType.ELK);
power = new MageInt(3);
toughness = new MageInt(3);
}
private ElkToken(final ElkToken token) {
super(token);
}
public ElkToken copy() {
return new ElkToken(this);
}
}

View file

@ -0,0 +1,53 @@
package mage.watchers.common;
import mage.abilities.Ability;
import mage.abilities.common.CommittedCrimeTriggeredAbility;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.watchers.Watcher;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
/**
* @author TheElk801
*/
public class CommittedCrimeWatcher extends Watcher {
// players who committed a crime this turn
private final Set<UUID> criminals = new HashSet<>();
public CommittedCrimeWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case SPELL_CAST:
case ACTIVATED_ABILITY:
case TRIGGERED_ABILITY:
break;
default:
return;
}
Optional.ofNullable(CommittedCrimeTriggeredAbility.getCriminal(event, game)).ifPresent(criminals::add);
}
@Override
public void reset() {
super.reset();
criminals.clear();
}
public static boolean checkCriminality(Game game, Ability source) {
return game
.getState()
.getWatcher(CommittedCrimeWatcher.class)
.criminals
.contains(source.getControllerId());
}
}