Migrated Site cards in set 18 to hjson

This commit is contained in:
MarcinSc
2024-05-27 14:25:14 +07:00
parent 6fb2c2e757
commit ef40d20619
11 changed files with 103 additions and 461 deletions

View File

@@ -1,223 +0,0 @@
import com.gempukku.lotro.common.CardType;
import com.gempukku.lotro.common.Keyword;
import com.gempukku.lotro.common.PossessionClass;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.LotroCardBlueprint;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.packs.SetDefinition;
import org.hjson.JsonArray;
import org.hjson.JsonObject;
import org.hjson.Stringify;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
public class CreateCardStubs {
private static final String[] _packageNames =
new String[]{
"", ".dwarven", ".dunland", ".elven", ".fallenRealms", ".gandalf", ".gollum", ".gondor", ".isengard", ".men", ".orc",
".raider", ".rohan", ".moria", ".wraith", ".sauron", ".shire", ".site", ".uruk_hai",
//Additional Hobbit Draft packages
".esgaroth", ".gundabad", ".smaug", ".spider", ".troll"
};
public static void main(String[] args) {
final String property = System.getProperty("user.dir");
String projectRoot = new File(property).getAbsolutePath();
File path = new File(projectRoot);
int[] sets = {3};//, 4, 5, 6, 7, 8, 10, 11, 12, 13, 15, 17, 18, 31};
for (int set : sets) {
produceForSet(path, set);
}
}
private static void produceForSet(File projectRoot, int set) {
File setPath = new File(projectRoot, "/gemp-lotr-cards/src/main/resources/cards-stub/set" + set);
File cardsPath = new File(projectRoot, "/gemp-lotr-cards/src/main/resources/cards");
File cardsMapping = new File(projectRoot, "/gemp-lotr-cards/src/main/resources/blueprintMapping.txt");
File setConfig = new File(projectRoot, "/gemp-lotr-cards/src/main/resources/setConfig.hjson");
File rarities = new File(projectRoot, "/gemp-lotr-cards/src/main/resources/rarities");
LotroCardBlueprintLibrary library = new LotroCardBlueprintLibrary(cardsPath, cardsMapping, setConfig, rarities);
Map<String, Map<String, LotroCardBlueprint>> cardsByFileName = new HashMap<>();
SetDefinition setDefinition = library.getSetDefinitions().get("" + set);
if (setDefinition.NeedsLoading()) {
final Set<String> allCards = setDefinition.getAllCards();
for (String blueprintId : allCards) {
blueprintId = stripBlueprintModifiers(blueprintId);
try {
// Ensure it's loaded
LotroCardBlueprint cardBlueprint = getBlueprint(blueprintId);
String key = getKey(cardBlueprint);
Map<String, LotroCardBlueprint> list = cardsByFileName.computeIfAbsent(key, k -> new LinkedHashMap<>());
list.put(blueprintId, cardBlueprint);
} catch (CardNotFoundException exp) {
// Ignore card that is not defined
}
}
}
for (Map.Entry<String, Map<String, LotroCardBlueprint>> cardsOfType : cardsByFileName.entrySet()) {
String type = cardsOfType.getKey();
File typePath = new File(setPath, "set" + set + "-" + type + ".hjson");
if (!typePath.exists()) {
Map<String, JsonObject> cardJsons = new TreeMap<>();
Map<String, LotroCardBlueprint> cards = cardsOfType.getValue();
for (Map.Entry<String, LotroCardBlueprint> cardEntry : cards.entrySet()) {
String blueprintId = cardEntry.getKey();
LotroCardBlueprint card = cardEntry.getValue();
JsonObject cardJson = new JsonObject();
cardJson.add("title", card.getTitle());
if (card.getSubtitle() != null)
cardJson.add("subtitle", card.getSubtitle());
if (card.isUnique())
cardJson.add("unique", true);
if (card.getSide() != null)
cardJson.add("side", card.getSide().name().toLowerCase());
if (card.getCulture() != null)
cardJson.add("culture", card.getCulture().getHumanReadable());
cardJson.add("twilight", card.getTwilightCost());
cardJson.add("type", card.getCardType().name().toLowerCase());
if (card.getRace() != null)
cardJson.add("race", card.getRace().getHumanReadable());
if (card.getPossessionClasses() != null) {
JsonArray array = new JsonArray();
for (PossessionClass possessionClass : card.getPossessionClasses()) {
array.add(possessionClass.getHumanReadable());
}
if (array.size() == 1)
cardJson.add("itemclass", array.get(0).asString());
else
cardJson.add("itemclass", array);
}
if (card.getSignet() != null)
cardJson.add("signet", card.getSignet().name().toLowerCase());
if (card.getStrength() != 0)
cardJson.add("strength", card.getStrength());
if (card.getVitality() != 0)
cardJson.add("vitality", card.getVitality());
if (card.getResistance() != 0)
cardJson.add("resistance", card.getResistance());
if (card.getCardType() == CardType.ALLY) {
if (card.getAllyHomeSiteBlock() != null) {
String home = card.getAllyHomeSiteBlock().getHumanReadable();
for (int allyHomeSiteNumber : card.getAllyHomeSiteNumbers()) {
home += "," + allyHomeSiteNumber;
}
cardJson.add("allyHome", home);
}
}
if (card.getCardType() == CardType.SITE)
cardJson.add("block", card.getSiteBlock().getHumanReadable());
if (card.getSiteNumber() != 0)
cardJson.add("site", card.getSiteNumber());
if (card.getCardType() == CardType.SITE)
cardJson.add("direction", card.getSiteDirection().name().toLowerCase());
// Keywords handling
JsonArray keywords = new JsonArray();
for (Keyword keyword : Keyword.values()) {
if (card.hasKeyword(keyword)) {
int count = card.getKeywordCount(keyword);
if (count > 1)
keywords.add(keyword.getHumanReadable() + "+" + count);
else
keywords.add(keyword.getHumanReadable());
}
}
if (keywords.size() == 1)
cardJson.add("keyword", keywords.get(0).asString());
if (keywords.size() > 1)
cardJson.add("keyword", keywords);
cardJsons.put(blueprintId, cardJson);
}
typePath.getParentFile().mkdirs();
//Write JSON file
try (FileWriter file = new FileWriter(typePath)) {
file.write("{\n");
for (Map.Entry<String, JsonObject> cardJson : cardJsons.entrySet()) {
file.write(cardJson.getKey() + ": ");
file.write(cardJson.getValue().toString(Stringify.HJSON));
file.write("\n");
}
file.write("}\n");
file.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static String getKey(LotroCardBlueprint cardBlueprint) {
if (cardBlueprint.getCulture() != null)
return cardBlueprint.getCulture().getHumanReadable().toLowerCase();
if (cardBlueprint.getCardType() == CardType.SITE)
return "site";
return "ring";
}
public static String stripBlueprintModifiers(String blueprintId) {
if (blueprintId.endsWith("*"))
blueprintId = blueprintId.substring(0, blueprintId.length() - 1);
if (blueprintId.endsWith("T"))
blueprintId = blueprintId.substring(0, blueprintId.length() - 1);
return blueprintId;
}
private static LotroCardBlueprint getBlueprint(String blueprintId) throws CardNotFoundException {
String[] blueprintParts = blueprintId.split("_");
String setNumber = blueprintParts[0];
String cardNumber = blueprintParts[1];
for (String packageName : _packageNames) {
LotroCardBlueprint blueprint = null;
try {
blueprint = tryLoadingFromPackage(packageName, setNumber, cardNumber);
} catch (IllegalAccessException | InstantiationException | NoSuchMethodException e) {
throw new CardNotFoundException(blueprintId);
}
if (blueprint != null)
return blueprint;
}
throw new CardNotFoundException(blueprintId);
}
private static LotroCardBlueprint tryLoadingFromPackage(String packageName, String setNumber, String cardNumber) throws IllegalAccessException, InstantiationException, NoSuchMethodException {
try {
Class clazz = Class.forName("com.gempukku.lotro.cards.set" + setNumber + packageName + ".Card" + setNumber + "_" + normalizeId(cardNumber));
return (LotroCardBlueprint) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException | InvocationTargetException e) {
// Ignore
return null;
}
}
private static String normalizeId(String blueprintPart) {
int id = Integer.parseInt(blueprintPart);
if (id < 10)
return "00" + id;
else if (id < 100)
return "0" + id;
else
return String.valueOf(id);
}
}

View File

@@ -1,41 +0,0 @@
package com.gempukku.lotro.cards.set18.site;
import com.gempukku.lotro.logic.cardtype.AbstractShadowsSite;
import com.gempukku.lotro.logic.timing.TriggerConditions;
import com.gempukku.lotro.common.Keyword;
import com.gempukku.lotro.filters.Filters;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.GameUtils;
import com.gempukku.lotro.logic.actions.RequiredTriggerAction;
import com.gempukku.lotro.logic.effects.ChooseAndWoundCharactersEffect;
import com.gempukku.lotro.logic.timing.EffectResult;
import java.util.Collections;
import java.util.List;
/**
* Set: Treachery & Deceit
* Twilight Cost: 1
* Type: Site
* Game Text: Mountain. When the fellowship moves to this site, each player wounds two of his characters.
*/
public class Card18_134 extends AbstractShadowsSite {
public Card18_134() {
super("Doorway to Doom", 1, Direction.RIGHT);
addKeyword(Keyword.MOUNTAIN);
}
@Override
public List<RequiredTriggerAction> getRequiredAfterTriggers(LotroGame game, EffectResult effectResult, PhysicalCard self) {
if (TriggerConditions.movesTo(game, effectResult, self)) {
RequiredTriggerAction action = new RequiredTriggerAction(self);
for (String playerId : GameUtils.getAllPlayers(game)) {
action.appendEffect(
new ChooseAndWoundCharactersEffect(action, playerId, 2, 2, Filters.owner(playerId), Filters.character));
}
return Collections.singletonList(action);
}
return null;
}
}

View File

@@ -1,30 +0,0 @@
package com.gempukku.lotro.cards.set18.site;
import com.gempukku.lotro.common.Keyword;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.cardtype.AbstractShadowsSite;
import com.gempukku.lotro.logic.modifiers.Modifier;
import com.gempukku.lotro.logic.modifiers.ModifierFlag;
import com.gempukku.lotro.logic.modifiers.SpecialFlagModifier;
import java.util.Collections;
import java.util.List;
/**
* Set: Treachery & Deceit
* Twilight Cost: 2
* Type: Site
* Game Text: Mountain. Each card that is about to be placed into the discard pile is removed from the game instead.
*/
public class Card18_135 extends AbstractShadowsSite {
public Card18_135() {
super("Foot of Mount Doom", 2, Direction.LEFT);
addKeyword(Keyword.MOUNTAIN);
}
@Override
public List<? extends Modifier> getInPlayModifiers(LotroGame game, PhysicalCard self) {
return Collections.singletonList(new SpecialFlagModifier(self, ModifierFlag.REMOVE_CARDS_GOING_TO_DISCARD));
}
}

View File

@@ -1,38 +0,0 @@
package com.gempukku.lotro.cards.set18.site;
import com.gempukku.lotro.logic.cardtype.AbstractShadowsSite;
import com.gempukku.lotro.logic.timing.TriggerConditions;
import com.gempukku.lotro.common.Keyword;
import com.gempukku.lotro.filters.Filters;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.actions.RequiredTriggerAction;
import com.gempukku.lotro.logic.effects.HealCharactersEffect;
import com.gempukku.lotro.logic.timing.EffectResult;
import java.util.Collections;
import java.util.List;
/**
* Set: Treachery & Deceit
* Twilight Cost: 2
* Type: Site
* Game Text: River. When the fellowship moves to this site, each player heals each of his or her characters.
*/
public class Card18_136 extends AbstractShadowsSite {
public Card18_136() {
super("Mithlond", 2, Direction.LEFT);
addKeyword(Keyword.RIVER);
}
@Override
public List<RequiredTriggerAction> getRequiredAfterTriggers(LotroGame game, EffectResult effectResult, PhysicalCard self) {
if (TriggerConditions.movesTo(game, effectResult, self)) {
RequiredTriggerAction action = new RequiredTriggerAction(self);
action.appendEffect(
new HealCharactersEffect(self, null, Filters.character));
return Collections.singletonList(action);
}
return null;
}
}

View File

@@ -1,44 +0,0 @@
package com.gempukku.lotro.cards.set18.site;
import com.gempukku.lotro.logic.cardtype.AbstractShadowsSite;
import com.gempukku.lotro.logic.timing.TriggerConditions;
import com.gempukku.lotro.logic.effects.OptionalEffect;
import com.gempukku.lotro.logic.effects.ReinforceTokenEffect;
import com.gempukku.lotro.common.Keyword;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.GameUtils;
import com.gempukku.lotro.logic.actions.RequiredTriggerAction;
import com.gempukku.lotro.logic.timing.EffectResult;
import java.util.Collections;
import java.util.List;
/**
* Set: Treachery & Deceit
* Twilight Cost: 1
* Type: Site
* Game Text: Battleground. Plains. When the fellowship moves to this site, each player may reinforce a culture token.
*/
public class Card18_137 extends AbstractShadowsSite {
public Card18_137() {
super("Morannon Plains", 1, Direction.RIGHT);
addKeyword(Keyword.BATTLEGROUND);
addKeyword(Keyword.PLAINS);
}
@Override
public List<RequiredTriggerAction> getRequiredAfterTriggers(LotroGame game, EffectResult effectResult, PhysicalCard self) {
if (TriggerConditions.movesTo(game, effectResult, self)) {
RequiredTriggerAction action = new RequiredTriggerAction(self);
for (String playerId : GameUtils.getAllPlayers(game)) {
action.appendEffect(
new OptionalEffect(
action, playerId,
new ReinforceTokenEffect(self, playerId, null)));
}
return Collections.singletonList(action);
}
return null;
}
}

View File

@@ -1,17 +0,0 @@
package com.gempukku.lotro.cards.set18.site;
import com.gempukku.lotro.logic.cardtype.AbstractShadowsSite;
import com.gempukku.lotro.common.Keyword;
/**
* Set: Treachery & Deceit
* Twilight Cost: 4
* Type: Site
* Game Text: River.
*/
public class Card18_138 extends AbstractShadowsSite {
public Card18_138() {
super("Sirannon Ruins", 4, Direction.RIGHT);
addKeyword(Keyword.RIVER);
}
}

View File

@@ -1,34 +0,0 @@
package com.gempukku.lotro.cards.set18.site;
import com.gempukku.lotro.filters.Filters;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.cardtype.AbstractShadowsSite;
import com.gempukku.lotro.logic.modifiers.*;
import java.util.LinkedList;
import java.util.List;
/**
* Set: Treachery & Deceit
* Twilight Cost: 1
* Type: Site
* Game Text: Wounds cannot be prevented or healed. Burdens cannot be removed.
*/
public class Card18_139 extends AbstractShadowsSite {
public Card18_139() {
super("Steward's Tomb", 1, Direction.RIGHT);
}
@Override
public List<? extends Modifier> getInPlayModifiers(LotroGame game, PhysicalCard self) {
List<Modifier> modifiers = new LinkedList<>();
modifiers.add(
new SpecialFlagModifier(self, ModifierFlag.CANT_PREVENT_WOUNDS));
modifiers.add(
new CantHealModifier(self, Filters.any));
modifiers.add(
new CantRemoveBurdensModifier(self, null, Filters.any));
return modifiers;
}
}

View File

@@ -1,30 +0,0 @@
package com.gempukku.lotro.cards.set18.site;
import com.gempukku.lotro.common.CardType;
import com.gempukku.lotro.common.Keyword;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.cardtype.AbstractShadowsSite;
import com.gempukku.lotro.logic.modifiers.CantBeTransferredModifier;
import com.gempukku.lotro.logic.modifiers.Modifier;
import java.util.Collections;
import java.util.List;
/**
* Set: Treachery & Deceit
* Twilight Cost: 0
* Type: Site
* Game Text: Dwelling. While the fellowship is at this site, followers cannot be transfered to a character.
*/
public class Card18_140 extends AbstractShadowsSite {
public Card18_140() {
super("Streets of Bree", 0, Direction.LEFT);
addKeyword(Keyword.DWELLING);
}
@Override
public List<? extends Modifier> getInPlayModifiers(LotroGame game, PhysicalCard self) {
return Collections.singletonList(new CantBeTransferredModifier(self, CardType.FOLLOWER));
}
}

View File

@@ -23,6 +23,27 @@
direction: Right
keywords: Mountain
effects: [
{
type: trigger
trigger: {
type: movesTo
filter: self
}
effect: [
{
type: wound
player: free people
filter: choose(side(free people),character)
count: 2
}
{
type: wound
player: shadow
filter: choose(side(shadow),character)
count: 2
}
]
}
]
gametext: <b>Mountain</b>. When the fellowship moves to this site, each player wounds two of his or her characters.
lore: ""
@@ -58,6 +79,12 @@
direction: Left
keywords: Mountain
effects: [
{
type: modifier
modifier: {
type: removeCardsGoingToDiscard
}
}
]
gametext: <b>Mountain</b>. Each card that is about to be placed into the discard pile is removed from the game instead.
lore: ""
@@ -93,6 +120,25 @@
direction: Left
keywords: River
effects: [
{
type: trigger
trigger: {
type: movesTo
filter: self
}
effect: [
{
type: heal
player: free people
filter: all(side(free people),character)
}
{
type: heal
player: shadow
filter: all(side(shadow),character)
}
]
}
]
gametext: <b>River</b>. When the fellowship moves to this site, each player heals each of his or her characters.
lore: ""
@@ -131,6 +177,33 @@
Plains
]
effects: [
{
type: trigger
trigger: {
type: movesTo
filter: self
}
effect: [
{
type: optional
text: Would you like to reinforce a culture token
player: free people
effect: {
type: reinforceTokens
player: free people
}
}
{
type: optional
text: Would you like to reinforce a culture token
player: shadow
effect: {
type: reinforceTokens
player: shadow
}
}
]
}
]
gametext: <b>Battleground</b>. <b>Plains</b>. When the fellowship moves to this site, each player may reinforce a culture token.
lore: ""
@@ -165,8 +238,6 @@
block: Shadows
direction: Right
keywords: River
effects: [
]
gametext: <b>River</b>.
lore: ""
promotext: ""
@@ -200,6 +271,25 @@
block: Shadows
direction: Right
effects: [
{
type: modifier
modifier: {
type: cantPreventWounds
}
}
{
type: modifier
modifier: {
type: cantHeal
filter: any
}
}
{
type: modifier
modifier: {
type: cantRemoveBurdens
}
}
]
gametext: Wounds cannot be prevented or healed. Burdens cannot be removed.
lore: ""
@@ -235,6 +325,13 @@
direction: Left
keywords: Dwelling
effects: [
{
type: modifier
modifier: {
type: cantBeTransferred
filter: follower
}
}
]
gametext: <b>Dwelling</b>. While the fellowship is at this site, followers cannot be transferred to a character.
lore: ""

View File

@@ -29,13 +29,14 @@ import java.util.Map;
public class ReinforceTokens implements EffectAppenderProducer {
@Override
public EffectAppender createEffectAppender(JSONObject effectObject, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
FieldUtils.validateAllowedFields(effectObject, "culture", "filter", "memorize", "times");
FieldUtils.validateAllowedFields(effectObject, "culture", "filter", "memorize", "times", "player");
final Culture culture = FieldUtils.getEnum(Culture.class, effectObject.get("culture"), "culture");
final Token token = Token.findTokenForCulture(culture);
final String filter = FieldUtils.getString(effectObject.get("filter"), "filter", "self");
final String memory = FieldUtils.getString(effectObject.get("memorize"), "memorize", "_temp");
final ValueSource valueSource = ValueResolver.resolveEvaluator(effectObject.get("times"), 1, environment);
String player = FieldUtils.getString(effectObject.get("player"), "player", "you");
Filter tokenFilter = token != null ? Filters.hasToken(token, 1) : Filters.hasAnyCultureTokens(1);
@@ -43,7 +44,7 @@ public class ReinforceTokens implements EffectAppenderProducer {
result.addEffectAppender(
CardResolver.resolveCard(filter, (actionContext) -> tokenFilter,
memory, "you", "Choose card to reinforce tokens on", environment));
memory, player, "Choose card to reinforce tokens on", environment));
result.addEffectAppender(
new DelayedAppender() {
@Override

View File

@@ -82,6 +82,7 @@ public class ModifierSourceFactory {
modifierProducers.put("nomorethanoneminionmaybeassignedtoeachskirmish", new NoMorethanOneMinionMayBeAssignedToEachSkirmish());
modifierProducers.put("opponentmaynotdiscard", new OpponentMayNotDiscard());
modifierProducers.put("removeallkeywords", new RemoveAllKeywords());
modifierProducers.put("removecardsgoingtodiscard", new AddModifierFlag(ModifierFlag.REMOVE_CARDS_GOING_TO_DISCARD));
modifierProducers.put("removekeyword", new RemoveKeyword());
modifierProducers.put("removetext", new RemoveText());
modifierProducers.put("ringbearercanttakethreatwounds", new AddModifierFlag(ModifierFlag.RING_BEARER_CANT_TAKE_THREAT_WOUNDS));