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 "<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) { function int_or_null(inp) {
if (inp == 'None') { if (inp == 'None') {
return null; return null;
@ -118,6 +108,8 @@ function replaceAll(str, search, replace) {
return str.split(search).join(replace); return str.split(search).join(replace);
} }
function UnsupportedComponentError() {}
function build_card(component, pack_code, cycle_prefix, copies) { function build_card(component, pack_code, cycle_prefix, copies) {
function substitute_tags(str) { function substitute_tags(str) {
str = str.trim(); str = str.trim();
@ -129,108 +121,233 @@ function build_card(component, pack_code, cycle_prefix, copies) {
return str; 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 = { return {
code: String(code), name: substitute_tags(String(component.getName())),
deck_limit: 2, // TODO: could be derived? pack_code: String(pack_code),
flavor: substitute_tags(String(component.settings.get('Flavor'))), quantity: copies,
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')),
};
const raw_health = component.settings.get('Stamina'); // "Game Text" tab
if (raw_health && raw_health != 'None' && raw_health != '-') { traits: substitute_tags(String(component.settings.get('Traits'))),
card_data.health = int_or_null(raw_health); text: substitute_tags(String(
} component.settings.get('Keywords') + '\n' + component.settings.get('Rules'))),
const raw_sanity = component.settings.get('Sanity'); flavor: substitute_tags(String(component.settings.get('Flavor'))),
if (raw_sanity && raw_sanity != 'None' && raw_sanity != '-') { // TODO: "Victory" field
card_data.sanity = int_or_null(raw_sanity);
// "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 = { function is_unique() {
Agility: 0, if (component.settings.getBoolean('Unique')) {
Intellect: 0, return { is_unique: true };
Combat: 0, } else {
Willpower: 0, return {};
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];
} }
} }
const raw_cost = component.settings.get('ResourceCost'); function health_and_sanity() {
if (raw_cost) { const card_data = {};
card_data.cost = int_or_null(raw_cost); const raw_health = component.settings.get('Stamina');
} if (raw_health != 'None' && raw_health != '-') {
card_data.health = int_or_null(raw_health);
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));
} }
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'); function skill_icons() {
if (subtitle && subtitle != '') { const card_data = {};
card_data.subname = String(subtitle); 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'); function faction() {
if (faction) { const faction = component.settings.get('CardClass');
if (faction == 'Weakness') { if (faction == 'Weakness') {
card_data.subtype_code = "weakness"; return {
} faction_code: "neutral",
else if (faction == 'Basic Weakness') { subtype_code: "weakness",
card_data.subtype_code = "basicweakness"; };
} } else if (faction == 'BasicWeakness') {
else { return {
card_data.faction_code = String(faction).toLowerCase(); faction_code: "neutral",
subtype_code: "basicweakness",
};
} else {
const card_data = { faction_code: String(faction).toLowerCase() };
const faction2 = component.settings.get('CardClass2'); const faction2 = component.settings.get('CardClass2');
if (faction2 && faction2 != 'None') { if (faction2 && faction2 != 'None') {
card_data.faction2_code = String(faction2).toLowerCase(); card_data.faction2_code = String(faction2).toLowerCase();
} }
return card_data;
} }
} }
if (card_types[component.getFrontTemplateKey()] == 'enemy') { function player_card_common() {
// TODO: "weakness" or "basicweakness" return Object.assign(
card_data.subtype_code = "basicweakness"; {
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 // TODO: parse out some keywords into their own fields
// order by keys println(String(component.getFrontTemplateKey()));
const ordered_card_data = Object.keys(card_data).sort().reduce( switch (String(component.getFrontTemplateKey())) {
function(obj, key) { case "AHLCG-Event-Default":
obj[key] = card_data[key]; return order_by_keys(Object.assign(
return obj; { type_code: "event" },
}, common_data(),
{} player_card_common(),
); cost()
));
return ordered_card_data; 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) { function exportCard(component, file) {
@ -332,23 +449,32 @@ function run() {
const cards = []; const cards = [];
for (let child of children) { for (let child of children) {
try { try {
let component = ResourceKit.getGameComponentFromFile(child.file); let component = ResourceKit.getGameComponentFromFile(child.file);
if (component.getFrontTemplateKey() in card_types) { let copies = copyCount(copies_list, child.baseName);
printf("Generating JSON/PNG for '%s'...\n", child); let card_data;
let copies = copyCount(copies_list, child.baseName); try {
let card_data = build_card(component, pack_code, cycle_prefix, copies); card_data = build_card(component, pack_code, cycle_prefix, copies);
cards.push(card_data); } 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 export_dir = new File(deck_task.file, 'export');
let target_file = new File(export_dir, card_data.code + '.' + FORMAT); let target_file = new File(export_dir, card_data.code + '.' + FORMAT);
if (!target_file.exists() || child.file.lastModified() > target_file.lastModified()) { if (!target_file.exists() || child.file.lastModified() > target_file.lastModified()) {
printf("Image for '%s' is out of date, rebuilding...\n", child); printf("Image for '%s' is out of date, rebuilding...\n", child);
export_dir.mkdir(); export_dir.mkdir();
exportCard(component, target_file); exportCard(component, target_file);
} }
}
} catch (ex) { } catch (ex) {
println(ex); println(ex);
println('Error while processing ' + child.name + ', skipping file'); println('Error while processing ' + child.name + ', skipping file');