Refactor JSON card generation to allow for more card types

Rather than trying all settings and using what exists, declare what
should exist (via calling appropriate functions), per card type
This commit is contained in:
Adam Goldsmith 2021-10-09 22:16:24 -04:00
parent e96795030a
commit 8e3ab9e43e
1 changed files with 230 additions and 104 deletions

View File

@ -88,16 +88,6 @@ const tag_replacements = {
"<svs>": "", // Small vertical spacer
};
// TODO: handle investigator cards
const card_types = {
"AHLCG-Event-Default": "event",
"AHLCG-Skill-Default": "skill",
"AHLCG-Asset-Default": "asset",
// TODO: actually handle enemy weaknesses
"AHLCG-WeaknessEnemy-Default": "enemy",
"AHLCG-WeaknessTreachery-Default": "treachery",
};
function int_or_null(inp) {
if (inp == 'None') {
return null;
@ -118,6 +108,8 @@ function replaceAll(str, search, replace) {
return str.split(search).join(replace);
}
function UnsupportedComponentError() {}
function build_card(component, pack_code, cycle_prefix, copies) {
function substitute_tags(str) {
str = str.trim();
@ -129,108 +121,233 @@ function build_card(component, pack_code, cycle_prefix, copies) {
return str;
}
const code = cycle_prefix + leftPad(String(component.settings.get('CollectionNumber')), 3, '0');
function common_data() {
const code = cycle_prefix + leftPad(String(component.settings.get('CollectionNumber')), 3, '0');
const card_data = {
code: String(code),
deck_limit: 2, // TODO: could be derived?
flavor: substitute_tags(String(component.settings.get('Flavor'))),
illustrator: String(component.settings.get('Artist')),
is_unique: component.settings.getBoolean('Unique'),
name: substitute_tags(String(component.getName())),
pack_code: String(pack_code),
position: int_or_null(component.settings.get('CollectionNumber')),
quantity: copies,
//restrictions: null, // TODO
// TODO: should also handle "Victory" field
text: substitute_tags(String(
component.settings.get('Keywords') + '\n' + component.settings.get('Rules'))),
traits: substitute_tags(String(component.settings.get('Traits'))),
type_code: card_types[component.getFrontTemplateKey()],
xp: int_or_null(component.settings.get('Level')),
};
return {
name: substitute_tags(String(component.getName())),
pack_code: String(pack_code),
quantity: copies,
const raw_health = component.settings.get('Stamina');
if (raw_health && raw_health != 'None' && raw_health != '-') {
card_data.health = int_or_null(raw_health);
}
const raw_sanity = component.settings.get('Sanity');
if (raw_sanity && raw_sanity != 'None' && raw_sanity != '-') {
card_data.sanity = int_or_null(raw_sanity);
// "Game Text" tab
traits: substitute_tags(String(component.settings.get('Traits'))),
text: substitute_tags(String(
component.settings.get('Keywords') + '\n' + component.settings.get('Rules'))),
flavor: substitute_tags(String(component.settings.get('Flavor'))),
// TODO: "Victory" field
// "Collection" tab
code: String(code),
position: int_or_null(component.settings.get('CollectionNumber')),
// "Portraits" tab
illustrator: String(component.settings.get('Artist')),
//restrictions: null, // TODO
};
}
const skills = {
Agility: 0,
Intellect: 0,
Combat: 0,
Willpower: 0,
Wild: 0,
};
for (let i = 1; i<=6; i++) {
let skill_icon = component.settings.get('Skill' + i);
if (skill_icon in skills) {
skills[skill_icon] += 1;
}
}
for (let skill in skills) {
if (skills[skill] > 0) {
card_data["skill_" + skill.toLowerCase()] = skills[skill];
function is_unique() {
if (component.settings.getBoolean('Unique')) {
return { is_unique: true };
} else {
return {};
}
}
const raw_cost = component.settings.get('ResourceCost');
if (raw_cost) {
card_data.cost = int_or_null(raw_cost);
}
const raw_slot = component.settings.get('Slot');
if (raw_slot && raw_slot != 'None') {
card_data.slot = renameSlot(String(raw_slot));
const raw_slot2 = component.settings.get('Slot2');
if (raw_slot2 && raw_slot2 != 'None') {
card_data.slot += '. ' + renameSlot(String(raw_slot2));
function health_and_sanity() {
const card_data = {};
const raw_health = component.settings.get('Stamina');
if (raw_health != 'None' && raw_health != '-') {
card_data.health = int_or_null(raw_health);
}
const raw_sanity = component.settings.get('Sanity');
if (raw_sanity != 'None' && raw_sanity != '-') {
card_data.sanity = int_or_null(raw_sanity);
}
return card_data;
}
const subtitle = component.settings.get('Subtitle');
if (subtitle && subtitle != '') {
card_data.subname = String(subtitle);
function skill_icons() {
const card_data = {};
const skills = {
Agility: 0,
Intellect: 0,
Combat: 0,
Willpower: 0,
Wild: 0,
};
for (let i = 1; i <= 6; i++) {
let skill_icon = component.settings.get('Skill' + i);
if (skill_icon in skills) {
skills[skill_icon] += 1;
}
}
for (let skill in skills) {
if (skills[skill] > 0) {
card_data["skill_" + skill.toLowerCase()] = skills[skill];
}
}
return card_data;
}
const faction = component.settings.get('CardClass');
if (faction) {
function faction() {
const faction = component.settings.get('CardClass');
if (faction == 'Weakness') {
card_data.subtype_code = "weakness";
}
else if (faction == 'Basic Weakness') {
card_data.subtype_code = "basicweakness";
}
else {
card_data.faction_code = String(faction).toLowerCase();
return {
faction_code: "neutral",
subtype_code: "weakness",
};
} else if (faction == 'BasicWeakness') {
return {
faction_code: "neutral",
subtype_code: "basicweakness",
};
} else {
const card_data = { faction_code: String(faction).toLowerCase() };
const faction2 = component.settings.get('CardClass2');
if (faction2 && faction2 != 'None') {
card_data.faction2_code = String(faction2).toLowerCase();
}
return card_data;
}
}
if (card_types[component.getFrontTemplateKey()] == 'enemy') {
// TODO: "weakness" or "basicweakness"
card_data.subtype_code = "basicweakness";
function player_card_common() {
return Object.assign(
{
deck_limit: 2, // TODO: could be derived?
xp: int_or_null(component.settings.get('Level')),
},
skill_icons(),
faction()
);
}
function cost() {
return { cost: int_or_null(component.settings.get('ResourceCost')) };
}
function slots() {
const card_data = {};
const raw_slot = component.settings.get('Slot');
if (raw_slot != 'None') {
card_data.slot = renameSlot(String(raw_slot));
const raw_slot2 = component.settings.get('Slot2');
if (raw_slot2 != 'None') {
card_data.slot += '. ' + renameSlot(String(raw_slot2));
}
}
return card_data;
}
function subtitle() {
const subtitle = component.settings.get('Subtitle');
if (subtitle != '') {
return { subname: String(subtitle) };
} else {
return {};
}
}
function treachery_subtype() {
switch (String(component.settings.get('Subtype'))) {
// TODO: should "StoryWeakness" be different?
case 'StoryWeakness':
case 'Weakness':
return { subtype_code: "weakness" };
case 'BasicWeakness':
return { subtype_code: "basicweakness" };
default:
throw "Unknown Treachery Subtype:" + String(component.settings.get('Subtype'));
}
}
function enemy() {
let subtype;
switch (component.settings.get('Subtype')) {
case "Basic Weakness":
subtype = "basicweakness";
break;
case "Weakness":
// TODO: should these be different?
case "Investigator Weakness":
case "Story Weakness":
subtype = "weakness";
break;
}
return {
subtype_code: subtype,
// TODO: "per investigator" health
health: int_or_null(component.settings.get('Health')),
horror: int_or_null(component.settings.get('Horror')),
attack: int_or_null(component.settings.get('Attack')),
damage: int_or_null(component.settings.get('Damage')),
damage: int_or_null(component.settings.get('Damage')),
evade: int_or_null(component.settings.get('Evade')),
};
}
function order_by_keys(card_data) {
return Object.keys(card_data).sort().reduce(
function(obj, key) {
obj[key] = card_data[key];
return obj;
},
{}
);
}
// TODO: parse out some keywords into their own fields
// order by keys
const ordered_card_data = Object.keys(card_data).sort().reduce(
function(obj, key) {
obj[key] = card_data[key];
return obj;
},
{}
);
return ordered_card_data;
println(String(component.getFrontTemplateKey()));
switch (String(component.getFrontTemplateKey())) {
case "AHLCG-Event-Default":
return order_by_keys(Object.assign(
{ type_code: "event" },
common_data(),
player_card_common(),
cost()
));
case "AHLCG-Skill-Default":
return order_by_keys(Object.assign(
{ type_code: "skill" },
common_data(),
player_card_common()
));
case "AHLCG-Asset-Default":
println("asdf");
return order_by_keys(Object.assign(
{ type_code: "asset" },
common_data(),
player_card_common(),
cost(),
subtitle(),
is_unique(),
health_and_sanity(),
slots()
));
case "AHLCG-WeaknessEnemy-Default":
return order_by_keys(Object.assign(
{ type_code: "enemy", },
common_data(),
is_unique(),
enemy()
));
case "AHLCG-WeaknessTreachery-Default":
return order_by_keys(Object.assign(
{ type_code: "treachery", },
common_data(),
treachery_subtype()
));
default:
println("asdfasdfasdf");
throw new UnsupportedComponentError();
}
}
function exportCard(component, file) {
@ -332,23 +449,32 @@ function run() {
const cards = [];
for (let child of children) {
try {
let component = ResourceKit.getGameComponentFromFile(child.file);
if (component.getFrontTemplateKey() in card_types) {
printf("Generating JSON/PNG for '%s'...\n", child);
let copies = copyCount(copies_list, child.baseName);
let card_data = build_card(component, pack_code, cycle_prefix, copies);
cards.push(card_data);
for (let child of children) {
try {
let component = ResourceKit.getGameComponentFromFile(child.file);
let copies = copyCount(copies_list, child.baseName);
let card_data;
try {
card_data = build_card(component, pack_code, cycle_prefix, copies);
} catch (ex) {
println(ex);
if (ex instanceof UnsupportedComponentError) {
println("Skipping unsupported component: " + component.getName());
continue;
} else {
throw ex;
}
}
printf("Generating JSON/PNG for '%s'...\n", child);
cards.push(card_data);
let export_dir = new File(deck_task.file, 'export');
let target_file = new File(export_dir, card_data.code + '.' + FORMAT);
if (!target_file.exists() || child.file.lastModified() > target_file.lastModified()) {
printf("Image for '%s' is out of date, rebuilding...\n", child);
export_dir.mkdir();
exportCard(component, target_file);
}
}
let export_dir = new File(deck_task.file, 'export');
let target_file = new File(export_dir, card_data.code + '.' + FORMAT);
if (!target_file.exists() || child.file.lastModified() > target_file.lastModified()) {
printf("Image for '%s' is out of date, rebuilding...\n", child);
export_dir.mkdir();
exportCard(component, target_file);
}
} catch (ex) {
println(ex);
println('Error while processing ' + child.name + ', skipping file');