Skip to content

Commit

Permalink
Fix cheating class level 3 by copying level 2 activation (#6643)
Browse files Browse the repository at this point in the history
  • Loading branch information
tool4ever authored Nov 29, 2024
1 parent 4b1ca24 commit 947d190
Show file tree
Hide file tree
Showing 16 changed files with 28 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
Expand All @@ -18,8 +19,13 @@ public class ClassLevelUpEffect extends SpellAbilityEffect {
public void resolve(SpellAbility sa) {
final Card host = sa.getHostCard();
final Game game = host.getGame();
final int level = host.getClassLevel() + 1;
host.setClassLevel(level);
int level = host.getClassLevel();

if (AbilityUtils.calculateAmount(host, sa.getRestrictions().getClassLevel(), sa) != level) {
return;
}

host.setClassLevel(++level);

// need to run static ability to get Trigger online
game.getAction().checkStaticAbilities();
Expand All @@ -28,7 +34,6 @@ public void resolve(SpellAbility sa) {
game.getTriggerHandler().clearActiveTriggers(host, null);
game.getTriggerHandler().registerActiveTrigger(host, false);

// Run ClassLevelGained trigger
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(host);
runParams.put(AbilityKey.ClassLevel, level);
game.getTriggerHandler().runTrigger(TriggerType.ClassLevelGained, runParams, false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package forge.game.ability.effects;

import com.google.common.base.Functions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.game.ability.AbilityKey;
Expand All @@ -12,6 +13,7 @@
import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.util.Aggregates;
import forge.util.Lang;
import forge.util.Localizer;
import forge.util.MyRandom;
Expand Down Expand Up @@ -98,8 +100,6 @@ private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount,
}
}

int total = 0;
int countMaxRolls = 0;
List<Integer> naturalRolls = (rollsResult == null ? new ArrayList<>() : rollsResult);

for (int i = 0; i < amount; i++) {
Expand All @@ -108,7 +108,6 @@ private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount,
player.getGame().fireEvent(new GameEventRollDie());
player.roll();
naturalRolls.add(roll);
total += roll;
}

naturalRolls.sort(null);
Expand All @@ -117,7 +116,6 @@ private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount,
// Ignore lowest rolls
if (ignore > 0) {
for (int i = ignore - 1; i >= 0; --i) {
total -= naturalRolls.get(i);
ignored.add(naturalRolls.get(i));
naturalRolls.remove(i);
}
Expand All @@ -126,12 +124,15 @@ private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount,
for (Player chooser : ignoreChosenMap.keySet()) {
for (int ig = 0; ig < ignoreChosenMap.get(chooser); ig++) {
Integer ign = chooser.getController().chooseRollToIgnore(naturalRolls);
total -= ign;
ignored.add(ign);
naturalRolls.remove(ign);
}
}

if (sa.hasParam("UseHighestRoll")) {
naturalRolls.subList(0, naturalRolls.size() - 1).clear();
}

//Notify of results
if (amount > 0) {
StringBuilder sb = new StringBuilder();
Expand All @@ -150,6 +151,7 @@ private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount,
int oddResults = 0;
int evenResults = 0;
int differentResults = 0;
int countMaxRolls = 0;
for (Integer i : naturalRolls) {
final int modifiedRoll = i + modifier;
if (!rolls.contains(modifiedRoll)) {
Expand Down Expand Up @@ -177,9 +179,7 @@ private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount,
sa.setSVar("MaxRolls", Integer.toString(countMaxRolls));
}
}
total += modifier;

// Run triggers
int rollNum = 1;
for (Integer roll : rolls) {
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(player);
Expand All @@ -197,7 +197,7 @@ private static int rollDiceForPlayer(SpellAbility sa, Player player, int amount,
runParams.put(AbilityKey.RolledToVisitAttractions, toVisitAttractions);
player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDieOnce, runParams, false);

return total;
return Aggregates.sum(rolls, Functions.identity());
}

private static void resolveSub(SpellAbility sa, int num) {
Expand Down Expand Up @@ -232,9 +232,7 @@ private int rollDice(SpellAbility sa, Player player, int amount, int sides) {
List<Integer> rolls = new ArrayList<>();
int total = rollDiceForPlayer(sa, player, amount, sides, ignore, modifier, rolls, sa.hasParam("ToVisitYourAttractions"));

if (sa.hasParam("UseHighestRoll")) {
total = Collections.max(rolls);
} else if (sa.hasParam("UseDifferenceBetweenRolls")) {
if (sa.hasParam("UseDifferenceBetweenRolls")) {
total = Collections.max(rolls) - Collections.min(rolls);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public class RunChaosEffect extends SpellAbilityEffect {

@Override
public void resolve(SpellAbility sa) {

List<SpellAbility> validSA = Lists.newArrayList();
for (final Card c : getTargetCards(sa)) {
for (Trigger t : c.getTriggers()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ public void resolve(SpellAbility sa) {
}

for (final Card c : Aggregates.random(pool, seekNum)) {

Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
Expand All @@ -85,9 +84,9 @@ public void resolve(SpellAbility sa) {
if (resultZone.equals(ZoneType.Hand)) { // if it went to hand as planned, consider it "sought"
soughtCards.add(movedCard);
}

}
}

if (notify.length() != 0) {
game.getAction().notifyOfValue(sa, source, notify.toString(), null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ public void resolve(SpellAbility sa) {

game.getAction().moveToCommand(controller.getActiveScheme(), sa);

// Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Scheme, controller.getActiveScheme());
game.getTriggerHandler().runTrigger(TriggerType.SetInMotion, runParams, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ public void resolve(SpellAbility sa) {
final Card source = sa.getHostCard();
final Game game = source.getGame();
final Player activator = sa.getActivatingPlayer();

CardCollection list;

if (sa.hasParam("Choices")) {
Player chooser = activator;
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoose") + " ";
Expand All @@ -43,7 +43,7 @@ public void resolve(SpellAbility sa) {
} else {
list = getTargetCards(sa);
}

for (Card c : list) {
Map<String, Object> params = Maps.newHashMap();
params.put("Object", c);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ private void ventureIntoDungeon(SpellAbility sa, Player player, Map<AbilityKey,
// TODO: Currently play the Add Counter sound, but maybe add soundeffect for marker?
game.fireEvent(new GameEventCardCounters(dungeon, CounterType.getType("LEVEL"), 0, 1));

// Run RoomEntered trigger
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(dungeon);
runParams.put(AbilityKey.RoomName, nextRoom);
game.getTriggerHandler().runTrigger(TriggerType.RoomEntered, runParams, false);
Expand Down
10 changes: 0 additions & 10 deletions forge-game/src/main/java/forge/game/cost/CostAdjustment.java
Original file line number Diff line number Diff line change
Expand Up @@ -568,16 +568,6 @@ private static boolean checkRequirement(final SpellAbility sa, final StaticAbili
}
}
}
case "NonManaAbility" -> {
if (!sa.isActivatedAbility() || sa.isManaAbility() || sa.isReplacementAbility()) {
return false;
}
}
case "MorphDown" -> {
if (!sa.isSpell() || !sa.isCastFaceDown()) {
return false;
}
}
case "Foretell" -> {
if (!sa.isForetelling()) {
return false;
Expand Down
1 change: 0 additions & 1 deletion forge-game/src/main/java/forge/game/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -2293,7 +2293,6 @@ public final void addSacrificedThisTurn(final Card cpy, final SpellAbility sourc

sacrificedThisTurn.add(cpy);

// Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
// use a copy that preserves last known information about the card (e.g. for Savra, Queen of the Golgari + Painter's Servant)
runParams.put(AbilityKey.Card, cpy);
Expand Down
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/a/anointed_peacekeeper.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ SVar:DBLook:DB$ RevealHand | Defined$ ChosenPlayer | Look$ True | SubAbility$ DB
SVar:DBNameCard:DB$ NameCard | Defined$ You | SubAbility$ DBClear
SVar:DBClear:DB$ Cleanup | ClearChosenPlayer$ True
S:Mode$ RaiseCost | EffectZone$ Battlefield | ValidCard$ Card.NamedCard | Type$ Spell | Amount$ 2 | Activator$ Opponent | Description$ Spells your opponents cast with the chosen name cost {2} more to cast.
S:Mode$ RaiseCost | EffectZone$ Battlefield | ValidCard$ Card.NamedCard | Type$ NonManaAbility | Amount$ 2 | Description$ Activated abilities of sources with the chosen name cost {2} more to activate unless they're mana abilities.
S:Mode$ RaiseCost | EffectZone$ Battlefield | ValidCard$ Card.NamedCard | ValidSpell$ Activated.nonManaAbility | Amount$ 2 | Description$ Activated abilities of sources with the chosen name cost {2} more to activate unless they're mana abilities.
AI:RemoveDeck:Random
Oracle:Vigilance\nAs Anointed Peacekeeper enters, look at an opponent's hand, then choose any card name.\nSpells your opponents cast with the chosen name cost {2} more to cast.\nActivated abilities of sources with the chosen name cost {2} more to activate unless they're mana abilities.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/c/carrionette.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ Name:Carrionette
ManaCost:1 B
Types:Creature Skeleton
PT:1/1
A:AB$ ChangeZone | Cost$ 2 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | ThisDefinedAndTgts$ Self | Origin$ Battlefield | Destination$ Exile | UnlessCost$ 2 | UnlessPayer$ TargetedController | ActivationZone$ Graveyard | SpellDescription$ Exile CARDNAME and target creature unless that creature's controller pays {2}. Activate only if CARDNAME is in your graveyard.
A:AB$ ChangeZone | Cost$ 2 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | ThisDefinedAndTgts$ Self | Origin$ Battlefield,Graveyard | Destination$ Exile | UnlessCost$ 2 | UnlessPayer$ TargetedController | ActivationZone$ Graveyard | SpellDescription$ Exile CARDNAME and target creature unless that creature's controller pays {2}. Activate only if CARDNAME is in your graveyard.
Oracle:{2}{B}{B}: Exile Carrionette and target creature unless that creature's controller pays {2}. Activate only if Carrionette is in your graveyard.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/d/dream_chisel.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Name:Dream Chisel
ManaCost:2
Types:Artifact
S:Mode$ ReduceCost | ValidCard$ Creature | Type$ MorphDown | Activator$ You | Amount$ 1 | Description$ Face-down creature spells you cast cost {1} less to cast.
S:Mode$ ReduceCost | ValidCard$ Creature | ValidSpell$ Spell.isCastFaceDown | Activator$ You | Amount$ 1 | Description$ Face-down creature spells you cast cost {1} less to cast.
AI:RemoveDeck:Random
Oracle:Face-down creature spells you cast cost {1} less to cast.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/i/iron_mastiff.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Name:Iron Mastiff
ManaCost:4
Types:Artifact Creature Dog
PT:4/4
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DBTrigRollDice | TriggerDescription$ Whenever CARDNAME attacks, roll a d20 for each player being attacked and ignore all but the highest roll.
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DBTrigRollDice | TriggerDescription$ Whenever CARDNAME attacks, roll a d20 for each player being attacked and ignore all but the highest roll. ABILITY
SVar:DBTrigRollDice:DB$ RollDice | Sides$ 20 | Amount$ NbAttackedPlayers | UseHighestRoll$ True | ResultSubAbilities$ 1-9:DBDamageToController,10-19:DBDamageToDefending,20:DBDamageToOpponents
SVar:NbAttackedPlayers:PlayerCountOpponents$HasPropertyDefending
SVar:DBDamageToController:DB$ DealDamage | Defined$ You | NumDmg$ X | SpellDescription$ 1—9 VERT CARDNAME deals damage equal to its power to you.
Expand Down
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/o/obscuring_aether.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Name:Obscuring Aether
ManaCost:G
Types:Enchantment
S:Mode$ ReduceCost | ValidCard$ Creature | Type$ MorphDown | Activator$ You | Amount$ 1 | Description$ Face-down creature spells you cast cost {1} less to cast.
S:Mode$ ReduceCost | ValidCard$ Creature | ValidSpell$ Spell.isCastFaceDown | Activator$ You | Amount$ 1 | Description$ Face-down creature spells you cast cost {1} less to cast.
A:AB$ SetState | Cost$ 1 G | Defined$ Self | Mode$ TurnFaceDown | SpellDescription$ Turn CARDNAME face down. (It becomes a 2/2 creature.)
AI:RemoveDeck:All
DeckHints:Keyword$Morph|Megamorph
Expand Down
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/s/suppression_field.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Name:Suppression Field
ManaCost:1 W
Types:Enchantment
S:Mode$ RaiseCost | ValidCard$ Card | Type$ NonManaAbility | Amount$ 2 | Description$ Activated abilities cost {2} more to activate unless they're mana abilities.
S:Mode$ RaiseCost | ValidCard$ Card | ValidSpell$ Activated.nonManaAbility | Amount$ 2 | Description$ Activated abilities cost {2} more to activate unless they're mana abilities.
AI:RemoveDeck:Random
Oracle:Activated abilities cost {2} more to activate unless they're mana abilities.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/z/zirda_the_dawnwaker.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ ManaCost:1 RW RW
Types:Legendary Creature Elemental Fox
PT:3/3
K:Companion:Permanent.hasActivatedAbility,Instant,Sorcery:Each permanent card in your starting deck has an activated ability.
S:Mode$ ReduceCost | ValidCard$ Card | Activator$ You | Type$ NonManaAbility | Amount$ 2 | MinMana$ 1 | Description$ Abilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.
S:Mode$ ReduceCost | ValidCard$ Card | Activator$ You | ValidSpell$ Activated.nonManaAbility | Amount$ 2 | MinMana$ 1 | Description$ Abilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.
A:AB$ Pump | Cost$ 1 T | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN CARDNAME can't block. | IsCurse$ True | SpellDescription$ Target creature can't block this turn.
Oracle:Companion — Each permanent card in your starting deck has an activated ability. (If this card is your chosen companion, you may put it into your hand from outside the game for {3} any time you could cast a sorcery.)\nAbilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.\n{1}, {T}: Target creature can't block this turn.

0 comments on commit 947d190

Please sign in to comment.