Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 8 additions & 22 deletions forge-ai/src/main/java/forge/ai/SpecialCardAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -1136,27 +1136,13 @@ public static Card considerCardToGet(final Player ai, final SpellAbility sa) {
public static class MairsilThePretender {
// Scan the fetch list for a card with at least one activated ability.
// TODO: can be improved to a full consider(sa, ai) logic which would scan the graveyard first and hand last
public static Card considerCardFromList(final CardCollection fetchList) {
for (Card c : CardLists.filter(fetchList, CardPredicates.ARTIFACTS.or(CardPredicates.CREATURES))) {
for (SpellAbility ab : c.getSpellAbilities()) {
if (ab.isActivatedAbility()) {
Player controller = c.getController();
boolean wasCaged = false;
for (Card caged : CardLists.filter(controller.getCardsIn(ZoneType.Exile),
CardPredicates.hasCounter(CounterEnumType.CAGE))) {
if (c.getName().equals(caged.getName())) {
wasCaged = true;
break;
}
}

if (!wasCaged) {
return c;
}
}
}
}
return null;
public static Card considerCardFromList(final CardCollection fetchList, SpellAbility sa) {
CardCollectionView caged = CardLists.filter(sa.getActivatingPlayer().getCardsIn(ZoneType.Exile),
CardPredicates.hasCounter(CounterType.getType("CAGE")));
return fetchList.stream().filter(CardPredicates.ARTIFACTS.or(CardPredicates.CREATURES))
.filter(c -> c.getSpellAbilities().stream().anyMatch(SpellAbility::isActivatedAbility))
.filter(c -> caged.stream().noneMatch(CardPredicates.sharesNameWith(c)))
.findFirst().orElse(null);
}
}

Expand Down Expand Up @@ -1765,7 +1751,7 @@ public static AiAbilityDecision consider(final Player ai, final SpellAbility sa)

AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
int lifeInDanger = aic.getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD);
int numCtrs = sa.getHostCard().getCounters(CounterEnumType.BURDEN);
int numCtrs = sa.getHostCard().getCounters(CounterType.getType("BURDEN"));

if (ai.getLife() > numCtrs + 1 && ai.getLife() > lifeInDanger
&& ai.getMaxHandSize() >= ai.getCardsIn(ZoneType.Hand).size() + numCtrs + 1) {
Expand Down
2 changes: 1 addition & 1 deletion forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -1500,7 +1500,7 @@ public static Card chooseCardToHiddenOriginChangeZone(ZoneType destination, List
} else if ("BestCard".equals(logic)) {
return ComputerUtilCard.getBestAI(fetchList); // generally also means the most expensive one or close to it
} else if ("Mairsil".equals(logic)) {
return SpecialCardAi.MairsilThePretender.considerCardFromList(fetchList);
return SpecialCardAi.MairsilThePretender.considerCardFromList(fetchList, sa);
} else if ("SurvivalOfTheFittest".equals(logic)) {
return SpecialCardAi.SurvivalOfTheFittest.considerCardToGet(decider, sa);
} else if ("MazesEnd".equals(logic)) {
Expand Down
10 changes: 6 additions & 4 deletions forge-ai/src/main/java/forge/ai/ability/LegendaryRuleAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;

Expand Down Expand Up @@ -41,21 +41,23 @@ public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options,
} else {
// AI decision making - should AI compare damage and debuffs?
}
CounterType ice = CounterType.getType("ICE");
CounterType ki = CounterType.getType("KI");

// TODO: Can this be made more generic somehow?
if (firstOption.getName().equals("Dark Depths")) {
Card best = firstOption;
for (Card c : options) {
if (c.getCounters(CounterEnumType.ICE) < best.getCounters(CounterEnumType.ICE)) {
if (c.getCounters(ice) < best.getCounters(ice)) {
best = c;
}
}
return best;
} else if (firstOption.getCounters(CounterEnumType.KI) > 0) {
} else if (firstOption.getCounters(ki) > 0) {
// Extra Rule for KI counter
Card best = firstOption;
for (Card c : options) {
if (c.getCounters(CounterEnumType.KI) > best.getCounters(CounterEnumType.KI)) {
if (c.getCounters(ki) > best.getCounters(ki)) {
best = c;
}
}
Expand Down
3 changes: 2 additions & 1 deletion forge-ai/src/main/java/forge/ai/ability/SetStateAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,9 @@ private boolean isSafeToTransformIntoLegendary(Player aiPlayer, Card source) {

final Card othercard = aiPlayer.getCardsIn(ZoneType.Battlefield, other.getName()).getFirst();

CounterType ki = CounterType.getType("KI");
// for legendary KI counter creatures
if (othercard.getCounters(CounterEnumType.KI) >= source.getCounters(CounterEnumType.KI)) {
if (othercard.getCounters(ki) >= source.getCounters(ki)) {
// if the other legendary is useless try to replace it
return ComputerUtilCard.isUselessCreature(aiPlayer, othercard);
}
Expand Down
6 changes: 0 additions & 6 deletions forge-game/src/main/java/forge/game/card/CounterEnumType.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ public enum CounterEnumType implements CounterType {

BRICK("BRICK", 226, 192, 164),

BURDEN("BURDEN", 135, 62, 35),

CAGE("CAGE", 155, 155, 155),

CARRION("CRRON", 255, 163, 222),

CELL ("CELL", 90, 10, 95),
Expand Down Expand Up @@ -251,8 +247,6 @@ public enum CounterEnumType implements CounterType {

JUDGMENT("JUDGM", 249, 220, 52),

KI("KI", 190, 189, 255),

KICK("KICK", 255, 255, 240),

KNOWLEDGE("KNOWL", 0, 115, 255),
Expand Down
74 changes: 74 additions & 0 deletions forge-game/src/main/java/forge/game/card/CounterListType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package forge.game.card;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.Maps;


public record CounterListType(String name, String desc, int red, int green, int blue) implements CounterType {
private static Map<String, CounterListType> sMap = Maps.newLinkedHashMap();

public static CounterListType get(String s) {
return sMap.get(s);
}

public static Collection<CounterListType> getValues() {
return sMap.values();
}

public static void add(String name, String desc, int red, int green, int blue) {
sMap.put(desc, new CounterListType(StringUtils.capitalize(name), desc, red, green, blue));
}

public static final void parseTypes(List<String> content) {
for (String line : content) {
if (line.startsWith("#") || line.isEmpty()) {
continue;
}
// Name=Description,red,green,blue
String k[] = line.split("=");
String l[] = k[1].split(",");
add(k[0], l[0], Integer.valueOf(l[1]), Integer.valueOf(l[2]), Integer.valueOf(l[3]));
}
}

@Override
public String getName() {
return name;
}

@Override
public String getCounterOnCardDisplayName() {
return desc;
}

@Override
public boolean is(CounterEnumType eType) {
return false;
}

@Override
public boolean isKeywordCounter() {
return false;
}

@Override
public int getRed() {
return this.red;
}

@Override
public int getGreen() {
return this.green;
}

@Override
public int getBlue() {
return this.blue;
}

}
5 changes: 5 additions & 0 deletions forge-game/src/main/java/forge/game/card/CounterType.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@ static CounterType getType(String name) {
try {
return CounterEnumType.getType(name);
} catch (final IllegalArgumentException ex) {
CounterType result = CounterListType.get(name);
if (result != null) {
return result;
}
return CounterKeywordType.get(name);
}
}
static List<CounterType> getValues() {
List<CounterType> result = Lists.newArrayList();
result.addAll(List.of(CounterEnumType.values()));
result.addAll(CounterListType.getValues());
result.addAll(CounterKeywordType.getValues());
return result;
}
Expand Down
3 changes: 3 additions & 0 deletions forge-gui/res/lists/CounterLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BURDEN=BURDEN,135,62,35
CAGE=CAGE,155,155,155
KI=KI,190,189,255
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public final class ForgeConstants {
public static final String SETLOOKUP_DIR = RES_DIR + "setlookup" + PATH_SEPARATOR;
public static final String KEYWORD_LIST_FILE = LISTS_DIR + "NonStackingKWList.txt";
public static final String TYPE_LIST_FILE = LISTS_DIR + "TypeLists.txt";
public static final String COUNTER_LIST_FILE = LISTS_DIR + "CounterLists.txt";
public static final String SPECIAL_CARD_ACHIEVEMENT_LIST_FILE = LISTS_DIR + "special-card-achievements.txt";
public static final String PLANESWALKER_ACHIEVEMENT_LIST_FILE = LISTS_DIR + "planeswalker-achievements.txt";
public static final String ALTWIN_ACHIEVEMENT_LIST_FILE = LISTS_DIR + "altwin-achievements.txt";
Expand Down
6 changes: 6 additions & 0 deletions forge-gui/src/main/java/forge/model/FModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import forge.game.GameFormat;
import forge.game.GameType;
import forge.game.card.CardUtil;
import forge.game.card.CounterListType;
import forge.game.spellability.Spell;
import forge.gamemodes.gauntlet.GauntletData;
import forge.gamemodes.limited.GauntletMini;
Expand Down Expand Up @@ -342,6 +343,7 @@ public static ItemPool<PaperCard> getContraptionPool() {
}

private static boolean keywordsLoaded = false;
private static boolean countersLoaded = false;

/**
* Load dynamic gamedata.
Expand All @@ -357,6 +359,10 @@ public static void loadDynamicGamedata() {

CardType.Constant.LOADED.set();
}
if (!countersLoaded) {
CounterListType.parseTypes(FileUtil.readFile(ForgeConstants.COUNTER_LIST_FILE));
countersLoaded = true;
}

if (!keywordsLoaded) {
final List<String> nskwListFile = FileUtil.readFile(ForgeConstants.KEYWORD_LIST_FILE);
Expand Down
Loading