Skip to content

Commit

Permalink
🐛 common monster templates (_trait -> _templates)
Browse files Browse the repository at this point in the history
  • Loading branch information
ebullient committed Jun 25, 2024
1 parent 64c87a5 commit ecc64a6
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 45 deletions.
8 changes: 8 additions & 0 deletions src/main/java/dev/ebullient/convert/tools/JsonNodeReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ default boolean existsIn(JsonNode source) {
return source.has(this.nodeName());
}

default boolean nestedExistsIn(JsonNodeReader field, JsonNode source) {
if (source == null || source.isNull()) {
return false;
}
JsonNode parent = field.getFrom(source);
return this.existsIn(parent);
}

default boolean isArrayIn(JsonNode source) {
if (source == null || !source.has(this.nodeName())) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,7 @@ enum MonsterFields implements JsonNodeReader {
spellcasting,
spells,
summonedBySpellLevel,
trait,
type,
will,
choose,
Expand Down
100 changes: 55 additions & 45 deletions src/main/java/dev/ebullient/convert/tools/dnd5e/JsonSourceCopier.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,46 +223,50 @@ JsonNode mergeNodes(Tools5eIndexType type, String originKey, JsonNode copyFrom,
// edit in place: if you don't, lower-level copies will keep being revisted.
ObjectNode target = (ObjectNode) copyTo;

JsonNode _copy = MetaFields._copy.getFrom(copyTo);
JsonNode _mod = _copy == null ? null : normalizeMods(MetaFields._mod.getFrom(_copy));
JsonNode _trait = _copy == null ? null : MetaFields._trait.getFrom(_copy);
JsonNode _copy = MetaFields._copy.getFromOrEmptyObjectNode(copyTo);
if (MetaFields._mod.existsIn(_copy)) {
normalizeMods(_copy);
}

JsonNode templateApplyRoot = null;
// fetch and apply any external template
// append them to existing copy mods where available
ArrayNode templates = MetaFields._templates.readArrayFrom(_copy);
for (JsonNode _template : templates) {

if (_trait != null) {
// fetch and apply external template mods
String templateKey = Tools5eIndexType.monsterTemplate.createKey(_trait);
JsonNode template = index.getOriginNoFallback(templateKey);
if (template == null) {
tui().warn("Unable to find trait for " + templateKey);
} else {
template = copyNode(template); // copy fast
String templateKey = Tools5eIndexType.monsterTemplate.createKey(_template);
JsonNode templateNode = index.getOriginNoFallback(templateKey);

JsonNode apply = MetaFields.apply.getFrom(template);
templateApplyRoot = MetaFields._root.getFrom(apply);
if (templateNode == null) {
tui().warn("Unable to find traits for " + templateKey);
continue;
} else {
if (!MetaFields._mod.nestedExistsIn(MetaFields.apply, templateNode)) {
// if template.apply._mod doesn't exist, skip this
continue;
}

JsonNode templateMods = normalizeMods(MetaFields._mod.getFrom(apply));
if (templateMods != null) {
if (_mod == null) {
_mod = templateMods;
} else {
ObjectNode _modRw = (ObjectNode) _mod;
for (Entry<String, JsonNode> e : iterableFields(templateMods)) {
if (_modRw.has(e.getKey())) {
appendToArray(_modRw.withArray(e.getKey()), e.getValue());
} else {
_modRw.set(e.getKey(), e.getValue());
}
JsonNode template = copyNode(templateNode); // copy fast
JsonNode templateApply = MetaFields.apply.getFrom(template);
normalizeMods(templateApply);

JsonNode templateApplyMods = MetaFields._mod.getFrom(templateApply);
if (MetaFields._mod.existsIn(_copy)) {
ObjectNode copyMods = (ObjectNode) MetaFields._mod.getFrom(_copy);
for (Entry<String, JsonNode> e : iterableFields(templateApplyMods)) {
if (copyMods.has(e.getKey())) {
appendToArray(copyMods.withArray(e.getKey()), e.getValue());
} else {
copyMods.set(e.getKey(), e.getValue());
}
}
} else {
MetaFields._mod.setIn(_copy, templateApplyMods);
}
}
MetaFields._trait.removeFrom(_copy);
}
MetaFields._templates.removeFrom(_copy);

JsonNode _preserve = _copy == null
? mapper().createObjectNode()
: MetaFields._preserve.getFrom(_copy);
JsonNode _preserve = MetaFields._preserve.getFromOrEmptyObjectNode(_copy);

// Copy required values from...
for (Entry<String, JsonNode> from : iterableFields(copyFrom)) {
Expand All @@ -288,9 +292,13 @@ JsonNode mergeNodes(Tools5eIndexType type, String originKey, JsonNode copyFrom,
}
}

// Apply template _root properties
// apply any root template properties after doing base copy
List<String> copyToRootProps = streamOfFieldNames(copyTo).toList();
if (templateApplyRoot != null) {
for (JsonNode template : templates) {
if (!MetaFields._root.nestedExistsIn(MetaFields.apply, template)) {
continue;
}
JsonNode templateApplyRoot = MetaFields._root.getFrom(MetaFields.apply.getFrom(template));
for (Entry<String, JsonNode> from : iterableFields(templateApplyRoot)) {
String k = from.getKey();
if (!copyToRootProps.contains(k)) {
Expand All @@ -301,13 +309,16 @@ JsonNode mergeNodes(Tools5eIndexType type, String originKey, JsonNode copyFrom,
}

// Apply mods
if (_mod != null) {
for (Entry<String, JsonNode> entry : iterableFields(_mod)) {
if (MetaFields._mod.existsIn(_copy)) {
// pre-convert any dynamic text
JsonNode copyMetaMod = MetaFields._mod.getFrom(_copy);
for (Entry<String, JsonNode> entry : iterableFields(copyMetaMod)) {
// use the copyTo value as the attribute source for resolving dynamic text
entry.setValue(resolveDynamicText(originKey, entry.getValue(), copyTo));
}

for (Entry<String, JsonNode> entry : iterableFields(_mod)) {
// Now iterate and apply mod rules
for (Entry<String, JsonNode> entry : iterableFields(copyMetaMod)) {
String prop = entry.getKey();
JsonNode modInfos = entry.getValue();
if ("*".equals(prop)) {
Expand Down Expand Up @@ -408,17 +419,16 @@ private JsonNode resolveDynamicText(String originKey, JsonNode value, JsonNode t
return value;
}

private JsonNode normalizeMods(JsonNode copyMeta) {
if (copyMeta == null || !copyMeta.isObject()) {
return copyMeta;
}
for (String name : iterableFieldNames(copyMeta)) {
JsonNode mod = copyMeta.get(name);
if (!mod.isArray()) {
((ObjectNode) copyMeta).set(name, mapper().createArrayNode().add(mod));
private void normalizeMods(JsonNode copyMeta) {
if (MetaFields._mod.existsIn(copyMeta)) {
ObjectNode mods = (ObjectNode) MetaFields._mod.getFrom(copyMeta);
for (String name : iterableFieldNames(mods)) {
JsonNode mod = mods.get(name);
if (!mod.isArray()) {
mods.set(name, mapper().createArrayNode().add(mod));
}
}
}
return copyMeta;
}

private void doMod(String originKey, ObjectNode target, JsonNode copyFrom, JsonNode modInfos, List<String> props) {
Expand Down Expand Up @@ -1231,7 +1241,7 @@ enum MetaFields implements JsonNodeReader {
_mod,
_preserve,
_root,
_trait,
_templates,
alias,
apply,
data,
Expand Down
17 changes: 17 additions & 0 deletions src/test/java/dev/ebullient/convert/tools/dnd5e/JsonDataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import dev.ebullient.convert.TestUtils;
import dev.ebullient.convert.tools.dnd5e.CommonDataTests.TestInput;
import dev.ebullient.convert.tools.dnd5e.Json2QuteMonster.MonsterFields;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
Expand Down Expand Up @@ -208,6 +209,22 @@ public void testMagicVariants() {
@Test
public void testMonsterList() {
commonTests.testMonsterList(outputPath);

if (!TestUtils.PATH_5E_TOOLS_DATA.toFile().exists()) {
return;
}

JsonNode x;

x = commonTests.index.getOrigin("monster|reduced-threat aboleth|tftyp");
JsonNode hp = MonsterFields.hp.getFrom(x);
assertThat(hp).isNotNull();
assertThat(MonsterFields.average.getFrom(hp).toString())
.describedAs("Reduced Threat monsters should have a template with stat modifications applied")
.isEqualTo("67.0");
assertThat(MonsterFields.trait.getFrom(x).toPrettyString())
.describedAs("Reduced Threat monsters should have a template with stat modifications applied")
.contains("Reduced Threat");
}

@Test
Expand Down

0 comments on commit ecc64a6

Please sign in to comment.