diff --git a/examples/config/config.5e.json b/examples/config/config.5e.json index ed6ad7f0b..162034c7c 100644 --- a/examples/config/config.5e.json +++ b/examples/config/config.5e.json @@ -1,47 +1,47 @@ -{ - "sources" : { - "toolsRoot" : "local/5etools/data", - "reference" : [ - "DMG" - ], - "adventure" : [ - "LMoP" - ], - "book" : [ - "PHB" - ], - "homebrew" : [ - "homebrew/collection/Kobold Press; Deep Magic 14 Elemental Magic.json" - ] - }, - "paths" : { - "compendium" : "/compendium/", - "rules" : "/compendium/rules/" - }, - "include" : [ - "race|changeling|mpmm" - ], - "includeGroup" : [ - "familiars" - ], - "exclude" : [ - "monster|expert|dc", - "monster|expert|sdw", - "monster|expert|slw" - ], - "excludePattern" : [ - "race\\|.*\\|dmg" - ], - "reprintBehavior" : "newest", - "template" : { - "background" : "examples/templates/tools5e/images-background2md.txt" - }, - "useDiceRoller" : true, - "yamlStatblocks" : true, - "tagPrefix" : "ttrpg-cli", - "images" : { - "internalRoot" : "local/path/for/remote/images", - "copyInternal" : true, - "copyExternal" : true - } +{ + "sources" : { + "toolsRoot" : "local/5etools/data", + "reference" : [ + "DMG" + ], + "adventure" : [ + "LMoP" + ], + "book" : [ + "PHB" + ], + "homebrew" : [ + "homebrew/collection/Kobold Press; Deep Magic 14 Elemental Magic.json" + ] + }, + "paths" : { + "compendium" : "/compendium/", + "rules" : "/compendium/rules/" + }, + "include" : [ + "race|changeling|mpmm" + ], + "includeGroup" : [ + "familiars" + ], + "exclude" : [ + "monster|expert|dc", + "monster|expert|sdw", + "monster|expert|slw" + ], + "excludePattern" : [ + "race\\|.*\\|dmg" + ], + "reprintBehavior" : "newest", + "template" : { + "background" : "examples/templates/tools5e/images-background2md.txt" + }, + "useDiceRoller" : true, + "yamlStatblocks" : true, + "tagPrefix" : "ttrpg-cli", + "images" : { + "internalRoot" : "local/path/for/remote/images", + "copyInternal" : true, + "copyExternal" : true + } } \ No newline at end of file diff --git a/examples/config/config.pf2e.json b/examples/config/config.pf2e.json index f6a82e75e..cdeccf927 100644 --- a/examples/config/config.pf2e.json +++ b/examples/config/config.pf2e.json @@ -1,32 +1,32 @@ -{ - "sources" : { - "reference" : [ - "CRB", - "GMG" - ], - "book" : [ - "crb", - "gmg" - ] - }, - "paths" : { - "compendium" : "compendium/", - "rules" : "compendium/rules/" - }, - "include" : [ - "ability|buck|b1" - ], - "exclude" : [ - "background|insurgent|apg" - ], - "excludePattern" : [ - "background\\|.*\\|lowg" - ], - "reprintBehavior" : "newest", - "template" : { - "ability" : "../path/to/ability2md.txt" - }, - "useDiceRoller" : true, - "tagPrefix" : "ttrpg-cli", - "images" : { } +{ + "sources" : { + "reference" : [ + "CRB", + "GMG" + ], + "book" : [ + "crb", + "gmg" + ] + }, + "paths" : { + "compendium" : "compendium/", + "rules" : "compendium/rules/" + }, + "include" : [ + "ability|buck|b1" + ], + "exclude" : [ + "background|insurgent|apg" + ], + "excludePattern" : [ + "background\\|.*\\|lowg" + ], + "reprintBehavior" : "newest", + "template" : { + "ability" : "../path/to/ability2md.txt" + }, + "useDiceRoller" : true, + "tagPrefix" : "ttrpg-cli", + "images" : { } } \ No newline at end of file diff --git a/examples/config/config.schema.json b/examples/config/config.schema.json index 4b2daa621..6e11c401c 100644 --- a/examples/config/config.schema.json +++ b/examples/config/config.schema.json @@ -1,146 +1,146 @@ -{ - "$schema" : "https://json-schema.org/draft/2020-12/schema", - "$defs" : { - "Map(String,String)" : { - "type" : "object" - } - }, - "type" : "object", - "properties" : { - "exclude" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "excludePattern" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "from" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "fullSource" : { - "type" : "object", - "properties" : { - "adventure" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "book" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "homebrew" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - }, - "images" : { - "type" : "object", - "properties" : { - "copyExternal" : { - "type" : "boolean" - }, - "copyInternal" : { - "type" : "boolean" - }, - "fallbackPaths" : { - "$ref" : "#/$defs/Map(String,String)" - }, - "internalRoot" : { - "type" : "string" - } - } - }, - "include" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "includeGroup" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "includePattern" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "paths" : { - "type" : "object", - "properties" : { - "compendium" : { - "type" : "string" - }, - "rules" : { - "type" : "string" - } - } - }, - "reprintBehavior" : { - "type" : "string", - "enum" : [ "newest", "edition", "all" ] - }, - "sources" : { - "type" : "object", - "properties" : { - "adventure" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "book" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "homebrew" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "reference" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "toolsRoot" : { - "type" : "string" - } - } - }, - "tagPrefix" : { - "type" : "string" - }, - "template" : { - "$ref" : "#/$defs/Map(String,String)" - }, - "useDiceRoller" : { - "type" : "boolean" - }, - "yamlStatblocks" : { - "type" : "boolean" - } - } +{ + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "$defs" : { + "Map(String,String)" : { + "type" : "object" + } + }, + "type" : "object", + "properties" : { + "exclude" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "excludePattern" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "from" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "fullSource" : { + "type" : "object", + "properties" : { + "adventure" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "book" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "homebrew" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "images" : { + "type" : "object", + "properties" : { + "copyExternal" : { + "type" : "boolean" + }, + "copyInternal" : { + "type" : "boolean" + }, + "fallbackPaths" : { + "$ref" : "#/$defs/Map(String,String)" + }, + "internalRoot" : { + "type" : "string" + } + } + }, + "include" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "includeGroup" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "includePattern" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "paths" : { + "type" : "object", + "properties" : { + "compendium" : { + "type" : "string" + }, + "rules" : { + "type" : "string" + } + } + }, + "reprintBehavior" : { + "type" : "string", + "enum" : [ "newest", "edition", "all" ] + }, + "sources" : { + "type" : "object", + "properties" : { + "adventure" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "book" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "homebrew" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "reference" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "toolsRoot" : { + "type" : "string" + } + } + }, + "tagPrefix" : { + "type" : "string" + }, + "template" : { + "$ref" : "#/$defs/Map(String,String)" + }, + "useDiceRoller" : { + "type" : "boolean" + }, + "yamlStatblocks" : { + "type" : "boolean" + } + } } \ No newline at end of file diff --git a/src/main/java/dev/ebullient/convert/StringUtil.java b/src/main/java/dev/ebullient/convert/StringUtil.java index 0481bd4df..165d5a46f 100644 --- a/src/main/java/dev/ebullient/convert/StringUtil.java +++ b/src/main/java/dev/ebullient/convert/StringUtil.java @@ -25,16 +25,24 @@ */ public class StringUtil { + /** Return the number with prefixed with the appropriate sign (+ or -), or an empty string if null. */ + public static String formatAsModifier(Integer n) { + if (n == null) { + return ""; + } + return (n < 0 ? "-" : "+") + Math.abs(n); + } + /** * Return {@code formatString} formatted with {@code o} as the first parameter. * If {@code o} is null, then return an empty string. */ - public static String format(String formatString, Object val) { - return val == null || (val instanceof String && ((String) val).isBlank()) ? "" : formatString.formatted(val); + public static String formatIfPresent(String formatString, Object val) { + return val == null || val.toString().isBlank() ? "" : formatString.formatted(val); } - public static String valueOrDefault(String value, String fallback) { - return value == null || value.isEmpty() ? fallback : value; + public static String valueOrDefault(Object value, String fallback) { + return value == null || value.toString().isEmpty() ? fallback : value.toString(); } public static String valueOrDefault(String[] parts, int index, String fallback) { @@ -82,33 +90,6 @@ public static String flatJoin(String joiner, Collection... lists) { return join(joiner, Arrays.stream(lists).flatMap(Collection::stream).toList()); } - /** - * Like {@link #joinWithPrefix(String, String, Collection)} but accept vararg inputs. This is mostly to get around - * being unable to pass null values to {@code List.of}. - * - * @see #joinWithPrefix(String, String, Collection) - * @see #join(String, Object, Object...) - */ - public static String joinWithPrefix(String joiner, String prefix, Object o1, Object... rest) { - List args = new ArrayList<>(); - args.add(o1); - args.addAll(Arrays.asList(rest)); - return joinWithPrefix(joiner, prefix, args); - } - - /** - * Like {@link #join(String, Collection)} but add a prefix to the resulting string if it's non-empty. - * - * @see #join(String, Collection) - */ - public static String joinWithPrefix(String joiner, String prefix, Collection list) { - String s = join(joiner, list); - if (s.isEmpty()) { - return ""; - } - return isPresent(prefix) ? prefix + s : s; - } - /** * {@link #joinConjunct(String, String, List)} with a {@code ", "} joiner. * diff --git a/src/main/java/dev/ebullient/convert/VersionProvider.java b/src/main/java/dev/ebullient/convert/VersionProvider.java index b556649c6..4761a38a5 100644 --- a/src/main/java/dev/ebullient/convert/VersionProvider.java +++ b/src/main/java/dev/ebullient/convert/VersionProvider.java @@ -13,12 +13,13 @@ public String[] getVersion() { Properties properties = new Properties(); try { properties.load(TtrpgConfig.class.getResourceAsStream("/git.properties")); - return new String[] { - "${COMMAND-FULL-NAME} version " + properties.getProperty("git.build.version"), - "Git commit: " + properties.get("git.commit.id.abbrev") - }; - } catch (IOException e) { + } catch (IOException | NullPointerException e) { return new String[] { "${COMMAND-FULL-NAME} version unknown " }; } + + return new String[] { + "${COMMAND-FULL-NAME} version " + properties.getProperty("git.build.version"), + "Git commit: " + properties.get("git.commit.id.abbrev") + }; } } diff --git a/src/main/java/dev/ebullient/convert/tools/JsonNodeReader.java b/src/main/java/dev/ebullient/convert/tools/JsonNodeReader.java index 9877497e6..e40748cca 100644 --- a/src/main/java/dev/ebullient/convert/tools/JsonNodeReader.java +++ b/src/main/java/dev/ebullient/convert/tools/JsonNodeReader.java @@ -48,18 +48,6 @@ default void appendUnlessEmptyFrom(JsonNode x, List text, JsonTextConver } } - default String bonusOrNull(JsonNode x) { - JsonNode value = getFrom(x); - if (value == null) { - return null; - } - if (!value.isNumber()) { - throw new IllegalArgumentException("bonusOrNull can only work with numbers: " + value); - } - int n = value.asInt(); - return (n >= 0 ? "+" : "") + n; - } - /** * Return the boolean value of the field in the node: * - if the field is a boolean, return the value @@ -364,15 +352,20 @@ default String transformTextFrom(JsonNode source, String delimiter, JsonTextConv * Parse this field from {@code source} as potentially-nested array of entries, and return a list of strings. This * calls {@link JsonTextConverter#appendToText(List, JsonNode, String)} to recursively parse the input. */ - default List transformListFrom(JsonNode source, JsonTextConverter convert) { + default List transformListFrom(JsonNode source, JsonTextConverter convert, String heading) { if (!isArrayIn(source)) { return List.of(); } List inner = new ArrayList<>(); - convert.appendToText(inner, getFrom(source), null); + convert.appendToText(inner, getFrom(source), heading); return inner; } + /** @see #transformListFrom(JsonNode, JsonTextConverter, String) */ + default List transformListFrom(JsonNode source, JsonTextConverter convert) { + return transformListFrom(source, convert, null); + } + /** Returns the enum value of {@code enumClass} that this field in {@code source} contains, or null. */ default > E getEnumValueFrom(JsonNode source, Class enumClass) { String value = getTextOrNull(source); diff --git a/src/main/java/dev/ebullient/convert/tools/JsonTextConverter.java b/src/main/java/dev/ebullient/convert/tools/JsonTextConverter.java index f06d226a9..2522a3f44 100644 --- a/src/main/java/dev/ebullient/convert/tools/JsonTextConverter.java +++ b/src/main/java/dev/ebullient/convert/tools/JsonTextConverter.java @@ -495,9 +495,9 @@ default List removePreamble(List content) { * @param resource QuteBase containing required template resource data * @param admonition Type of embedded/encapsulating admonition */ - default String renderEmbeddedTemplate(QuteBase resource, String admonition) { + default String renderEmbeddedTemplate(QuteUtil resource, String admonition, String... prepend) { List inner = new ArrayList<>(); - renderEmbeddedTemplate(inner, resource, admonition, List.of()); + renderEmbeddedTemplate(inner, resource, admonition, prepend); return String.join("\n", inner); } @@ -509,11 +509,11 @@ default String renderEmbeddedTemplate(QuteBase resource, String admonition) { * @param admonition Type of embedded/encapsulating admonition * @param prepend Text to prepend at beginning of admonition (e.g. title) */ - default void renderEmbeddedTemplate(List text, QuteBase resource, String admonition, List prepend) { - boolean pushed = parseState().push(resource.sources()); + default void renderEmbeddedTemplate(List text, QuteUtil resource, String admonition, String... prepend) { + Boolean pushed = (resource instanceof QuteBase) ? parseState().push(((QuteBase)resource).sources()) : null; try { String rendered = tui().renderEmbedded(resource); - List inner = new ArrayList<>(prepend); + List inner = new ArrayList<>(Arrays.asList(prepend)); inner.addAll(removePreamble(new ArrayList<>(List.of(rendered.split("\n"))))); maybeAddBlankLine(text); @@ -524,41 +524,10 @@ default void renderEmbeddedTemplate(List text, QuteBase resource, String } text.addAll(inner); } finally { - parseState().pop(pushed); - } - } - - /** - * Return the rendered contents of an (always) inline template. - * - * @param resource QuteUtil containing required template resource data - * @param admonition Type of inline admonition - */ - default String renderInlineTemplate(QuteUtil resource, String admonition) { - List inner = new ArrayList<>(); - renderInlineTemplate(inner, resource, admonition); - return String.join("\n", inner); - } - - /** - * Add rendered contents of an (always) inline template - * to collected text - * - * @param text List of text content should be added to - * @param resource QuteUtil containing required template resource data - * @param admonition Type of inline admonition - */ - default void renderInlineTemplate(List text, QuteUtil resource, String admonition) { - String rendered = tui().renderEmbedded(resource); - List inner = removePreamble(new ArrayList<>(List.of(rendered.split("\n")))); - - maybeAddBlankLine(text); - if (admonition != null) { - wrapAdmonition(inner, "inline-" + admonition); - } else { - balanceBackticks(inner); + if (pushed != null) { + parseState().pop(pushed); + } } - text.addAll(inner); } /** Wrap {@code inner} in an admonition with the name {@code admonition}. */ @@ -622,7 +591,12 @@ default int[] outerAdmonitionIndices(List inner) { return new int[] { firstLineIdx, lastLineIdx }; } - String replaceText(String s); + default String replaceText(String input) { + return replaceTokens(input, this::replaceTokenText); + } + + /** Replace specific tokens (e.g. "@"-tags) in the input string. */ + String replaceTokenText(String input, boolean nested); default String replaceText(JsonNode input) { if (input == null) { diff --git a/src/main/java/dev/ebullient/convert/tools/dnd5e/JsonSource.java b/src/main/java/dev/ebullient/convert/tools/dnd5e/JsonSource.java index b03115721..7d638202a 100644 --- a/src/main/java/dev/ebullient/convert/tools/dnd5e/JsonSource.java +++ b/src/main/java/dev/ebullient/convert/tools/dnd5e/JsonSource.java @@ -605,11 +605,10 @@ default void appendStatblockInline(List text, JsonNode entry, String hea .withTargetFile(embedFileName) .withTargetPath(relativePath)); } else { - List prepend = new ArrayList<>(List.of( - "title: " + name, - "collapse: closed", - existingNode == null ? "" : "%% See " + type.linkify(this, data) + " %%")); - renderEmbeddedTemplate(text, qs, type.name(), prepend); + renderEmbeddedTemplate(text, qs, type.name(), + "title: " + name, + "collapse: closed", + existingNode == null ? "" : "%% See " + type.linkify(this, data) + " %%"); } } } diff --git a/src/main/java/dev/ebullient/convert/tools/dnd5e/JsonTextReplacement.java b/src/main/java/dev/ebullient/convert/tools/dnd5e/JsonTextReplacement.java index 64541760b..dc8be4dbf 100644 --- a/src/main/java/dev/ebullient/convert/tools/dnd5e/JsonTextReplacement.java +++ b/src/main/java/dev/ebullient/convert/tools/dnd5e/JsonTextReplacement.java @@ -125,10 +125,6 @@ default String joinAndReplace(ArrayNode array) { return String.join(", ", list); } - default String replaceText(String input) { - return replaceTokens(input, (s, b) -> this._replaceTokenText(s, b)); - } - default String tableHeader(String x) { if (x.contains("dice")) { // don't do the usual dice formatting in a column header @@ -179,7 +175,8 @@ default String replacePromptStrings(String s) { }); } - default String _replaceTokenText(String input, boolean nested) { + @Override + default String replaceTokenText(String input, boolean nested) { String result = input; // render.js this._renderString_renderTag diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAbility.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAbility.java index d31bd8624..3727f366e 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAbility.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAbility.java @@ -3,22 +3,23 @@ import static dev.ebullient.convert.StringUtil.join; import static dev.ebullient.convert.StringUtil.parenthesize; -import java.util.Set; - import com.fasterxml.jackson.databind.JsonNode; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.QuteAbility; public class Json2QuteAbility extends Json2QuteBase { - public Json2QuteAbility(Pf2eIndex index, Pf2eIndexType type, JsonNode rootNode) { - super(index, type, rootNode); + private final boolean isEmbedded; + + public Json2QuteAbility(Pf2eIndex index, JsonNode rootNode, boolean isEmbedded) { + super(index, Pf2eIndexType.ability, rootNode, + isEmbedded ? null : Pf2eSources.findOrTemporary(Pf2eIndexType.ability, rootNode)); + this.isEmbedded = isEmbedded; } @Override protected QuteAbility buildQuteNote() { - return Pf2eAbility.createAbility(rootNode, this, sources); + return Pf2eAbility.createAbility(this); } public enum Pf2eAbility implements Pf2eJsonNodeReader { @@ -63,33 +64,29 @@ public enum Pf2eAbility implements Pf2eJsonNodeReader { /** Nestable entries for the ability effect. */ entries; - private static QuteAbility createAbility(JsonNode node, JsonSource convert, Pf2eSources sources) { - Tags tags = new Tags(); - Set traits = convert.collectTraitsFrom(node, tags); - - return new QuteAbility(sources, + private static QuteAbility createAbility(Json2QuteAbility convert) { + JsonNode node = convert.rootNode; + return new QuteAbility(convert.sources, name.getTextFrom(node).map(convert::replaceText).orElse("Activate"), generic.getLinkFrom(node, convert), - entries.transformTextFrom(node, "\n", convert), - tags, - traits, + convert.entries, + convert.tags, + convert.traits, activity.getActivityFrom(node, convert), range.getRangeFrom(node, convert), - components.getActivationComponentsFrom(node, traits, convert), + components.getActivationComponentsFrom(node, convert.traits, convert), requirements.replaceTextFrom(node, convert), prerequisites.replaceTextFrom(node, convert), cost.replaceTextFrom(node, convert) // remove trailing period - .replaceFirst("^(.*)\\.$", "\1"), - trigger.replaceTextFrom(node, convert), + .replaceFirst("(^[.])\\.$", "$1"), + trigger.replaceTextFrom(node, convert) + // remove trailing period + .replaceFirst("(^[.])\\.$", "$1"), frequency.getFrequencyFrom(node, convert), special.transformTextFrom(node, "\n", convert), note.replaceTextFrom(node, convert), - sources == null, convert); - } - - public static QuteAbility createEmbeddedAbility(JsonNode node, JsonSource convert) { - return createAbility(node, convert, null); + convert.isEmbedded, convert); } public String getLinkFrom(JsonNode node, JsonSource convert) { diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAction.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAction.java index 3ad2b2f4f..4ad8944b2 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAction.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAction.java @@ -19,11 +19,7 @@ public Json2QuteAction(Pf2eIndex index, JsonNode node) { @Override protected QuteAction buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); - - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - appendToText(text, Pf2eAction.info.getFrom(rootNode), null); + entries.addAll(Pf2eAction.info.transformListFrom(rootNode, this)); ActionType actionType = Pf2eAction.actionType.fieldFromTo(rootNode, ActionType.class, tui()); @@ -33,12 +29,11 @@ protected QuteAction buildQuteResource() { actionType.addTags(this, tags); } - return new QuteAction( - getSources(), text, tags, + return new QuteAction(sources, entries, tags, Pf2eAction.cost.transformTextFrom(rootNode, ", ", this), Pf2eAction.trigger.transformTextFrom(rootNode, ", ", this), Field.alias.replaceTextFromList(rootNode, this), - collectTraitsFrom(rootNode, tags), + traits, Pf2eAction.prerequisites.transformTextFrom(rootNode, ", ", this), Field.requirements.replaceTextFrom(rootNode, this), Pf2eAction.frequency.getFrequencyFrom(rootNode, this), diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAffliction.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAffliction.java index 3d2b73765..066cccba9 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAffliction.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteAffliction.java @@ -4,7 +4,6 @@ import static dev.ebullient.convert.StringUtil.toTitleCase; import java.util.ArrayList; -import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -12,22 +11,23 @@ import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; - import com.fasterxml.jackson.databind.JsonNode; import dev.ebullient.convert.StringUtil; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.QuteAffliction; public class Json2QuteAffliction extends Json2QuteBase { - public Json2QuteAffliction(Pf2eIndex index, Pf2eIndexType type, JsonNode rootNode) { - super(index, type, rootNode); + private final boolean isEmbedded; + + public Json2QuteAffliction(Pf2eIndex index, Pf2eIndexType type, JsonNode rootNode, boolean isEmbedded) { + super(index, type, rootNode, isEmbedded ? null : Pf2eSources.findOrTemporary(type, rootNode)); + this.isEmbedded = isEmbedded; } @Override protected QuteAffliction buildQuteNote() { - return Pf2eAffliction.createAffliction(rootNode, this, getSources()); + return Pf2eAffliction.createAffliction(this); } public enum Pf2eAffliction implements Pf2eJsonNodeReader { @@ -96,24 +96,20 @@ public enum Pf2eAffliction implements Pf2eJsonNodeReader { * ], * */ - private static QuteAffliction createAffliction( - JsonNode node, JsonSource convert, Pf2eSources sources) { - boolean isEmbedded = sources == null; + private static QuteAffliction createAffliction(Json2QuteAffliction convert) { + JsonNode node = convert.rootNode; // Sometimes the affliction data is nested as an entry within the parent node. Optional nestedAfflictionNode = Optional.ofNullable(getNestedAffliction(node)); - if (!isEmbedded && nestedAfflictionNode.isEmpty()) { + if (!convert.isEmbedded && nestedAfflictionNode.isEmpty()) { // For standalone notes, we should always have a nested affliction node. convert.tui().errorf("Unable to extract affliction entry from %s", node.toPrettyString()); return null; } JsonNode dataNode = nestedAfflictionNode.orElse(node); - Tags tags = new Tags(sources); - Collection traits = convert.collectTraitsFrom(node, tags); - Optional afflictionLevel = level.intFrom(node).map(Objects::toString); - afflictionLevel.ifPresent(lv -> tags.add("affliction", "level", lv)); + afflictionLevel.ifPresent(lv -> convert.tags.add("affliction", "level", lv)); String temptedCurseText = temptedCurse.transformTextFrom(node, "\n", convert); Optional afflictionType = type.getTextFrom(node) @@ -121,18 +117,18 @@ private static QuteAffliction createAffliction( .filter(StringUtil::isPresent); afflictionType.ifPresent(type -> { if (isPresent(temptedCurseText)) { - tags.add("affliction", type, "tempted"); + convert.tags.add("affliction", type, "tempted"); } else { - tags.add("affliction", type); + convert.tags.add("affliction", type); } }); Optional afflictionName = name.getTextFrom(node); return new QuteAffliction( - sources, + convert.sources, // Standalone notes must have a valid affliction name so that we can name the file - isEmbedded ? afflictionName.orElse("") : afflictionName.orElseThrow(), + convert.isEmbedded ? afflictionName.orElse("") : afflictionName.orElseThrow(), // Any entries which were alongside the nested affliction block nestedAfflictionNode.isEmpty() ? List.of() @@ -142,8 +138,8 @@ private static QuteAffliction createAffliction( ArrayList::new, (acc, n) -> convert.appendToText(acc, n, "##"), ArrayList::addAll), - tags, - traits, + convert.tags, + convert.traits, Field.alias.replaceTextFromList(dataNode, convert), // Level may be e.g. "varies" afflictionLevel.or(() -> level.getTextFrom(node)) @@ -177,14 +173,10 @@ private static QuteAffliction createAffliction( entry.transformTextFrom(e.getValue(), "\n", convert, e.getKey())), (x, y) -> y, LinkedHashMap::new)), - isEmbedded, + convert.isEmbedded, convert); } - static QuteAffliction createInlineAffliction(JsonNode node, JsonSource convert) { - return createAffliction(node, convert, null); - } - /** Try to extract the affliction node from the entries. Returns null if we couldn't extract one. */ private static JsonNode getNestedAffliction(JsonNode node) { if (!entries.isArrayIn(node)) { diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteArchetype.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteArchetype.java index 4590a25fb..cffc2418b 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteArchetype.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteArchetype.java @@ -6,10 +6,8 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; - import com.fasterxml.jackson.databind.JsonNode; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.QuteArchetype; import dev.ebullient.convert.tools.pf2e.qute.QuteFeat; @@ -21,18 +19,12 @@ public Json2QuteArchetype(Pf2eIndex index, JsonNode rootNode) { @Override protected QuteArchetype buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); - - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - List benefits = ArchetypeField.benefits.getListOfStrings(rootNode, tui()); benefits.forEach(b -> tags.add("archetype", "benefit", b)); int dedicationLevel = ArchetypeField.dedicationLevel.intOrDefault(rootNode, 2); - return new QuteArchetype(sources, text, tags, - collectTraitsFrom(rootNode, tags), + return new QuteArchetype(sources, entries, tags, traits, dedicationLevel, benefits, getFeatures(dedicationLevel)); @@ -120,12 +112,9 @@ QuteFeat findFeat(String levelKey) { } String render(QuteFeat quteFeat, boolean archetypeFeat) { - List inner = new ArrayList<>(); - renderEmbeddedTemplate(inner, quteFeat, "feat", List.of( - String.format("title: %s, Feat %s", quteFeat.getName(), quteFeat.level + (archetypeFeat ? "*" : "")), - "collapse: closed")); - - return String.join("\n", inner); + return renderEmbeddedTemplate(quteFeat, "feat", + "title: %s, Feat %s".formatted(quteFeat.getName(), quteFeat.level + (archetypeFeat ? "*" : "")), + "collapse: closed"); } enum ArchetypeField implements Pf2eJsonNodeReader { diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteBackground.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteBackground.java index 761ce44f5..1a1aa61a5 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteBackground.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteBackground.java @@ -1,11 +1,7 @@ package dev.ebullient.convert.tools.pf2e; -import java.util.ArrayList; -import java.util.List; - import com.fasterxml.jackson.databind.JsonNode; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.QuteBackground; public class Json2QuteBackground extends Json2QuteBase { @@ -16,11 +12,6 @@ public Json2QuteBackground(Pf2eIndex index, JsonNode rootNode) { @Override protected QuteBackground buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); - - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - Pf2eBackground.boosts.getListOfStrings(rootNode, tui()) .stream() .filter(b -> !b.equalsIgnoreCase("Free")) @@ -32,7 +23,7 @@ protected QuteBackground buildQuteResource() { Pf2eBackground.feat.getListOfStrings(rootNode, tui()) .forEach(s -> tags.add("background", "feat", s)); - return new QuteBackground(sources, text, tags); + return new QuteBackground(sources, entries, tags); } enum Pf2eBackground implements Pf2eJsonNodeReader { diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteBase.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteBase.java index fc5b2e2d6..7fe17696f 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteBase.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteBase.java @@ -1,15 +1,22 @@ package dev.ebullient.convert.tools.pf2e; +import java.util.ArrayList; +import java.util.List; import com.fasterxml.jackson.databind.JsonNode; +import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.Pf2eQuteBase; import dev.ebullient.convert.tools.pf2e.qute.Pf2eQuteNote; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataTraits; public abstract class Json2QuteBase implements JsonSource { protected final Pf2eIndex index; protected final Pf2eIndexType type; protected final JsonNode rootNode; protected final Pf2eSources sources; + protected final Tags tags; + protected final QuteDataTraits traits; + protected final List entries; public Json2QuteBase(Pf2eIndex index, Pf2eIndexType type, JsonNode rootNode) { this(index, type, rootNode, Pf2eSources.findOrTemporary(type, rootNode)); @@ -20,6 +27,9 @@ public Json2QuteBase(Pf2eIndex index, Pf2eIndexType type, JsonNode rootNode, Pf2 this.type = type; this.rootNode = rootNode; this.sources = sources; + this.tags = new Tags(sources); + this.traits = getTraits(rootNode).addToTags(tags); + this.entries = new ArrayList<>(SourceField.entries.transformListFrom(rootNode, this, "##")); } @Override diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteCreature.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteCreature.java index 97f14bcfa..918638f97 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteCreature.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteCreature.java @@ -2,18 +2,16 @@ import static dev.ebullient.convert.StringUtil.join; -import java.util.Collection; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; - import com.fasterxml.jackson.databind.JsonNode; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.QuteCreature; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataRef; public class Json2QuteCreature extends Json2QuteBase { @@ -23,7 +21,7 @@ public Json2QuteCreature(Pf2eIndex index, JsonNode rootNode) { @Override protected QuteCreature buildQuteResource() { - return Pf2eCreature.create(rootNode, this); + return Pf2eCreature.create(this); } /** @@ -59,14 +57,14 @@ protected QuteCreature buildQuteResource() { enum Pf2eCreature implements Pf2eJsonNodeReader { abilities, abilityMods, - alignment, + alignment, // unused in the data but defined in the schema alias, attacks, defenses, description, entries, hasImages, - inflicts, // not actually present in any of the entries + inflicts, // unused in the data but defined in the schema isNpc, items, languages, @@ -83,15 +81,9 @@ enum Pf2eCreature implements Pf2eJsonNodeReader { std, traits; - private static QuteCreature create(JsonNode node, JsonSource convert) { - Tags tags = new Tags(convert.getSources()); - Collection traits = convert.collectTraitsFrom(node, tags); - traits.addAll(alignment.getAlignmentsFrom(node, convert)); - - return new QuteCreature(convert.getSources(), - entries.transformTextFrom(node, "\n", convert, "##"), - tags, - traits, + private static QuteCreature create(Json2QuteCreature convert) { + JsonNode node = convert.rootNode; + return new QuteCreature(convert.sources, convert.entries, convert.tags, convert.traits, alias.replaceTextFromList(node, convert), description.replaceTextFrom(node, convert), level.intOrNull(node), @@ -290,7 +282,8 @@ private static QuteCreature.CreatureSpellReference getSpellReference(JsonNode no String spellName = name.getTextOrThrow(node); return new QuteCreature.CreatureSpellReference( spellName, - convert.linkify(Pf2eIndexType.spell, join("|", spellName, source.getTextOrNull(node))), + QuteDataRef.fromMarkdownLink( + convert.linkify(Pf2eIndexType.spell, join("|", spellName, source.getTextOrNull(node)))), amount.getTextFrom(node) .filter(s -> s.equalsIgnoreCase("at will")) .map(unused -> 0) diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteDeity.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteDeity.java index d63967031..4ec4d3e73 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteDeity.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteDeity.java @@ -6,21 +6,18 @@ import static dev.ebullient.convert.StringUtil.toOrdinal; import static dev.ebullient.convert.StringUtil.toTitleCase; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; import java.util.stream.Stream; - import com.fasterxml.jackson.databind.JsonNode; import dev.ebullient.convert.StringUtil; import dev.ebullient.convert.io.Tui; import dev.ebullient.convert.qute.NamedText; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.Pf2eJsonNodeReader.Pf2eAttack; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataActivity.Activity; import dev.ebullient.convert.tools.pf2e.qute.QuteDeity; import dev.ebullient.convert.tools.pf2e.qute.QuteInlineAttack; import dev.ebullient.convert.tools.pf2e.qute.QuteInlineAttack.AttackRangeType; @@ -34,30 +31,25 @@ public Json2QuteDeity(Pf2eIndex index, JsonNode rootNode) { @Override protected QuteDeity buildQuteResource() { - List text = new ArrayList<>(); - Tags tags = new Tags(sources); - Pf2eDeity.domains.getListOfStrings(rootNode, tui()).forEach(d -> tags.add("domain", d, "deity")); Pf2eDeity.alternateDomains.getListOfStrings(rootNode, tui()).forEach(d -> tags.add("domain", d, "deity")); String category = Pf2eDeity.category.getTextOrDefault(rootNode, "Deity"); tags.add("deity", category); - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - JsonNode alignNode = Pf2eDeity.alignment.getFrom(rootNode); - - return new QuteDeity(sources, text, tags, + // TODO handle "entry" field in alignment + return new QuteDeity(sources, entries, tags, Field.alias.replaceTextFromList(rootNode, this), category, join(", ", Pf2eDeity.pantheon.linkifyListFrom(rootNode, Pf2eIndexType.deity, this)), - join(", ", Pf2eDeity.alignment.getAlignmentsFrom(alignNode, this)), - join(", ", Pf2eDeity.followerAlignment.getAlignmentsFrom(alignNode, this)), + join(", ", Pf2eDeity.alignment.linkifyListFrom(alignNode, Pf2eIndexType.trait, this)), + join(", ", Pf2eDeity.followerAlignment.linkifyListFrom(alignNode, Pf2eIndexType.trait, this)), Pf2eDeity.areasOfConcern.transformTextFrom(rootNode, ", ", this), commandmentToString(Pf2eDeity.edict.replaceTextFromList(rootNode, this)), commandmentToString(Pf2eDeity.anathema.replaceTextFromList(rootNode, this)), buildCleric(), - buildAvatar(tags), + buildAvatar(), buildIntercession()); } @@ -114,7 +106,7 @@ QuteDeity.QuteDeityCleric buildCleric() { return cleric; } - QuteDeity.QuteDivineAvatar buildAvatar(Tags tags) { + QuteDeity.QuteDivineAvatar buildAvatar() { JsonNode avatarNode = Pf2eDeity.avatar.getFrom(rootNode); if (avatarNode == null) { return null; @@ -154,7 +146,7 @@ QuteDeity.QuteDivineAvatar buildAvatar(Tags tags) { avatar.attacks = Stream.concat( Pf2eDeity.melee.streamFrom(avatarNode).map(n -> Map.entry(n, AttackRangeType.MELEE)), Pf2eDeity.ranged.streamFrom(avatarNode).map(n -> Map.entry(n, AttackRangeType.RANGED))) - .map(e -> buildAvatarAttack(e.getKey(), tags, e.getValue())) + .map(e -> buildAvatarAttack(e.getKey(), e.getValue())) .toList(); avatar.ability = Pf2eDeity.ability.streamFrom(avatarNode) .map(this::buildAvatarAbility) @@ -169,21 +161,19 @@ private NamedText buildAvatarAbility(JsonNode abilityNode) { SourceField.entries.transformTextFrom(abilityNode, "; ", this)); } - private QuteInlineAttack buildAvatarAttack(JsonNode actionNode, Tags tags, AttackRangeType rangeType) { - Collection traits = collectTraitsFrom(actionNode, tags); - traits.addAll(Pf2eDeity.preciousMetal.getListOfStrings(actionNode, tui())); - Pf2eDeity.traitNote.getTextFrom(actionNode).ifPresent(traits::add); - + private QuteInlineAttack buildAvatarAttack(JsonNode actionNode, AttackRangeType rangeType) { return new QuteInlineAttack( Pf2eAttack.name.getTextOrDefault(actionNode, "attack"), - Pf2eActivity.single.toQuteActivity(this, null), + Pf2eActivity.toQuteActivity(this, Activity.single, null), rangeType, Json2QuteItem.Pf2eWeaponData.getDamageString(actionNode, this), Stream.of(Json2QuteItem.Pf2eWeaponData.damageType, Json2QuteItem.Pf2eWeaponData.damageType2) .map(field -> field.getTextOrEmpty(actionNode)) .filter(StringUtil::isPresent) .toList(), - traits, + getTraits(actionNode).addToTags(tags) + .addTraits(Pf2eDeity.preciousMetal.getListOfStrings(actionNode, tui())) + .addTrait(Pf2eDeity.traitNote.getTextOrEmpty(actionNode)), Pf2eDeity.note.replaceTextFrom(actionNode, this), this); } diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteFeat.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteFeat.java index 6b44546b6..01b6bcf5d 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteFeat.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteFeat.java @@ -1,12 +1,10 @@ package dev.ebullient.convert.tools.pf2e; -import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; - import com.fasterxml.jackson.databind.JsonNode; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.QuteFeat; public class Json2QuteFeat extends Json2QuteBase { @@ -17,18 +15,12 @@ public Json2QuteFeat(Pf2eIndex index, JsonNode rootNode) { @Override protected QuteFeat buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); - - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - List leadsTo = Pf2eFeat.leadsTo.getListOfStrings(rootNode, tui()) .stream() .map(x -> linkify(Pf2eIndexType.feat, x)) .collect(Collectors.toList()); - return new QuteFeat(sources, text, tags, - collectTraitsFrom(rootNode, tags), + return new QuteFeat(sources, entries, tags, traits, Field.alias.replaceTextFromList(rootNode, this), Pf2eFeat.level.getTextOrDefault(rootNode, "1"), Pf2eFeat.access.transformTextFrom(rootNode, ", ", this), @@ -44,21 +36,16 @@ protected QuteFeat buildQuteResource() { public QuteFeat buildArchetype(String archetypeName, String dedicationLevel) { String featLevel = Pf2eFeat.level.getTextOrDefault(rootNode, "1"); - List text = new ArrayList<>(); - Tags tags = new Tags(); String note = null; - if (dedicationLevel != featLevel) { + if (!Objects.equals(dedicationLevel, featLevel)) { note = String.format( "> [!pf2-note] This version of %s is intended for use with the %s Archetype. Its level has been changed accordingly.", index.linkify(this.type, String.join("|", List.of(sources.getName(), sources.primarySource()))), archetypeName); } - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - - return new QuteFeat(sources, text, tags, - collectTraitsFrom(rootNode, tags), + return new QuteFeat(sources, entries, tags, traits, List.of(), dedicationLevel, Pf2eFeat.access.transformTextFrom(rootNode, ", ", this), diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteHazard.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteHazard.java index 07fa0e798..e00910f5e 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteHazard.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteHazard.java @@ -1,13 +1,8 @@ package dev.ebullient.convert.tools.pf2e; -import java.util.ArrayList; -import java.util.List; - import com.fasterxml.jackson.databind.JsonNode; import dev.ebullient.convert.tools.JsonTextConverter; -import dev.ebullient.convert.tools.Tags; -import dev.ebullient.convert.tools.pf2e.Json2QuteAbility.Pf2eAbility; import dev.ebullient.convert.tools.pf2e.qute.QuteDataGenericStat; import dev.ebullient.convert.tools.pf2e.qute.QuteHazard; @@ -19,13 +14,9 @@ public Json2QuteHazard(Pf2eIndex index, JsonNode rootNode) { @Override protected QuteHazard buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); - - appendToText(text, Pf2eHazard.description.getFrom(rootNode), "##"); + entries.addAll(Pf2eHazard.description.transformListFrom(rootNode, this, "##")); - return new QuteHazard(sources, text, tags, - collectTraitsFrom(rootNode, tags), + return new QuteHazard(sources, entries, tags, traits, Pf2eHazard.level.getTextOrDefault(rootNode, "0"), Pf2eHazard.disable.transformTextFrom(rootNode, "\n", index), Pf2eHazard.reset.transformTextFrom(rootNode, "\n", index), @@ -33,7 +24,7 @@ protected QuteHazard buildQuteResource() { Pf2eHazard.defenses.getDefensesFrom(rootNode, this), Pf2eHazard.attacks.getAttacksFrom(rootNode, this), Pf2eHazard.abilities.streamFrom(rootNode) - .map(n -> Pf2eAbility.createEmbeddedAbility(n, this)) + .map(n -> new Json2QuteAbility(index, n, true).buildQuteNote()) .toList(), Pf2eHazard.actions.getAbilityOrAfflictionsFrom(rootNode, this), Pf2eHazard.stealth.getObjectFrom(rootNode) diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteItem.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteItem.java index 39750df07..90d6bb363 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteItem.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteItem.java @@ -8,9 +8,7 @@ import java.util.Collection; import java.util.List; import java.util.Map.Entry; -import java.util.Set; import java.util.stream.Collectors; - import com.fasterxml.jackson.databind.JsonNode; import dev.ebullient.convert.qute.NamedText; @@ -35,18 +33,12 @@ public Json2QuteItem(Pf2eIndex index, JsonNode rootNode) { @Override protected Pf2eQuteBase buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); - List aliases = new ArrayList<>(Field.alias.replaceTextFromList(rootNode, this)); - Set traits = collectTraitsFrom(rootNode, tags); - - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - String duration = Pf2eItem.duration.existsIn(rootNode) ? SourceField.entry.getTextOrEmpty(Pf2eItem.duration.getFrom(rootNode)) : null; - return new QuteItem(sources, text, tags, traits, aliases, + return new QuteItem(sources, entries, tags, traits, + Field.alias.replaceTextFromList(rootNode, this), buildActivate(), getPrice(rootNode), join(", ", Pf2eItem.ammunition.linkifyListFrom(rootNode, Pf2eIndexType.item, this)), @@ -54,15 +46,15 @@ protected Pf2eQuteBase buildQuteResource() { Pf2eItem.onset.transformTextFrom(rootNode, ", ", this), replaceText(Pf2eItem.access.getTextOrEmpty(rootNode)), duration, - getCategory(tags), + getCategory(), linkify(Pf2eIndexType.group, getGroup()), Pf2eItem.hands.getTextOrEmpty(rootNode), keysToList(List.of(Pf2eItem.usage, Pf2eItem.bulk)), - getContract(tags), + getContract(), getShieldData(), - getArmorData(), - getWeaponData(tags), - getVariants(tags), + Pf2eItem.armorData.getArmorFrom(rootNode), + getWeaponData(), + getVariants(), Pf2eItem.craftReq.transformTextFrom(rootNode, "; ", this)); } @@ -115,28 +107,7 @@ private QuteItemShieldData getShieldData() { penalty(Pf2eItem.speedPen.getTextOrEmpty(shieldNode), " ft.")); } - private QuteItemArmorData getArmorData() { - JsonNode armorDataNode = Pf2eItem.armorData.getFrom(rootNode); - if (armorDataNode == null) { - return null; - } - - QuteItemArmorData armorData = new QuteItemArmorData(); - Pf2eItem.ac.intFrom(armorDataNode).ifPresent(ac -> armorData.ac = new QuteDataArmorClass(ac)); - armorData.dexCap = Pf2eItem.dexCap.bonusOrNull(armorDataNode); - - armorData.strength = Pf2eItem.str.getTextOrDefault(armorDataNode, "—"); - - String checkPen = Pf2eItem.checkPen.getTextOrDefault(armorDataNode, null); - armorData.checkPenalty = penalty(checkPen, ""); - - String speedPen = Pf2eItem.speedPen.getTextOrDefault(armorDataNode, null); - armorData.speedPenalty = penalty(speedPen, " ft."); - - return armorData; - } - - private List getWeaponData(Tags tags) { + private List getWeaponData() { JsonNode weaponDataNode = Pf2eItem.weaponData.getFrom(rootNode); if (weaponDataNode == null) { return null; @@ -152,7 +123,7 @@ private List getWeaponData(Tags tags) { return weaponDataList; } - private List getVariants(Tags tags) { + private List getVariants() { JsonNode variantsNode = Pf2eItem.variants.getFrom(rootNode); if (variantsNode == null) @@ -176,7 +147,7 @@ private List getVariants(Tags tags) { return variantList; } - private Collection getContract(Tags tags) { + private Collection getContract() { JsonNode contractNode = Pf2eItem.contract.getFrom(rootNode); if (contractNode == null) { return null; @@ -241,7 +212,7 @@ private String getGroup() { return Pf2eWeaponData.group.getTextOrEmpty(rootNode); } - String getCategory(Tags tags) { + String getCategory() { String category = Pf2eItem.category.getTextOrEmpty(rootNode); String subcategory = Pf2eItem.subCategory.getTextOrEmpty(rootNode); if (category == null) { @@ -302,6 +273,18 @@ enum Pf2eItem implements Pf2eJsonNodeReader { String properName() { return toTitleCase(this.nodeName()); } + + private QuteItemArmorData getArmorFrom(JsonNode source) { + return getObjectFrom(source) + .map(node -> new QuteItemArmorData( + ac.intFrom(node).map(QuteDataArmorClass::new).orElseThrow(), + dexCap.intOrNull(node), + str.intOrNull(node), + checkPen.intFrom(node).map(n -> -Math.abs(n)).orElse(0), + speedPen.intFrom(node).map(n -> -Math.abs(n)).orElse(0) + )) + .orElse(null); + } } enum Pf2eItemVariant implements Pf2eJsonNodeReader { @@ -326,7 +309,7 @@ public static QuteItemWeaponData buildWeaponData(JsonNode source, JsonSource convert, Tags tags) { QuteItemWeaponData weaponData = new QuteItemWeaponData(); - weaponData.traits = convert.collectTraitsFrom(source, tags); + weaponData.traits = convert.getTraits(source).addToTags(tags); weaponData.type = SourceField.type.getTextOrEmpty(source); weaponData.damage = getDamageString(source, convert); diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteRitual.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteRitual.java index 5a0a13729..3b51a52b1 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteRitual.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteRitual.java @@ -2,13 +2,10 @@ import static dev.ebullient.convert.StringUtil.joinConjunct; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; - import com.fasterxml.jackson.databind.JsonNode; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.Pf2eQuteBase; import dev.ebullient.convert.tools.pf2e.qute.QuteDataRange; import dev.ebullient.convert.tools.pf2e.qute.QuteRitual; @@ -26,27 +23,22 @@ public Json2QuteRitual(Pf2eIndex index, JsonNode rootNode) { @Override protected Pf2eQuteBase buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); - - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - String level = Pf2eSpell.level.getTextOrDefault(rootNode, "1"); tags.add(RITUAL_TAG, level); - return new QuteRitual(sources, text, tags, + return new QuteRitual(sources, entries, tags, level, "Ritual", - collectTraitsFrom(rootNode, tags), + traits, Field.alias.replaceTextFromList(rootNode, this), getQuteRitualCast(), getQuteRitualChecks(), - getQuteRitualSpellTarget(tags), + getQuteRitualSpellTarget(), Field.requirements.transformTextFrom(rootNode, ", ", this), null, getHeightenedCast()); } - QuteSpellTarget getQuteRitualSpellTarget(Tags tags) { + QuteSpellTarget getQuteRitualSpellTarget() { String targets = replaceText(Pf2eSpell.targets.getTextOrEmpty(rootNode)); QuteDataRange range = Pf2eSpell.range.getRangeFrom(rootNode, this); SpellArea area = Pf2eSpell.area.fieldFromTo(rootNode, SpellArea.class, tui()); diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteSpell.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteSpell.java index 6a8b4d646..349d879d5 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteSpell.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteSpell.java @@ -11,15 +11,14 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.stream.Stream; - import com.fasterxml.jackson.databind.JsonNode; import dev.ebullient.convert.qute.NamedText; import dev.ebullient.convert.tools.JsonNodeReader.FieldValue; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.Pf2eQuteBase; import dev.ebullient.convert.tools.pf2e.qute.QuteDataDuration; import dev.ebullient.convert.tools.pf2e.qute.QuteDataRange; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataRef; import dev.ebullient.convert.tools.pf2e.qute.QuteDataTimedDuration; import dev.ebullient.convert.tools.pf2e.qute.QuteSpell; import dev.ebullient.convert.tools.pf2e.qute.QuteSpell.QuteSpellAmp; @@ -41,13 +40,6 @@ protected Json2QuteSpell(Pf2eIndex index, Pf2eIndexType type, JsonNode rootNode) @Override protected Pf2eQuteBase buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); - - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - - Collection traits = collectTraitsFrom(rootNode, tags); - boolean focus = Pf2eSpell.focus.booleanOrDefault(rootNode, false); String level = Pf2eSpell.level.getTextOrDefault(rootNode, "1"); String type = "spell"; @@ -89,9 +81,9 @@ protected Pf2eQuteBase buildQuteResource() { List components = Pf2eSpell.components.getComponentsFrom(rootNode, this); // Add additional traits according to present components components.stream().map(Pf2eSpellComponent::getAddedTrait) - .distinct().map(this::linkifyTrait).forEach(traits::add); + .distinct().map(this::linkifyTrait).map(QuteDataRef::fromMarkdownLink).forEach(traits::add); - return new QuteSpell(sources, text, tags, + return new QuteSpell(sources, entries, tags, level, toTitleCase(type), traits, Field.alias.replaceTextFromList(rootNode, this), @@ -100,7 +92,7 @@ level, toTitleCase(type), Pf2eSpell.cost.transformTextFrom(rootNode, ", ", this), Pf2eSpell.trigger.transformTextFrom(rootNode, ", ", this), Pf2eSpell.requirements.transformTextFrom(rootNode, ", ", this), - getQuteSpellTarget(tags), + getQuteSpellTarget(), Pf2eSpell.savingThrow.getSpellSaveFrom(rootNode, this), Pf2eSpell.duration.getSpellDurationFrom(rootNode, this), Pf2eSpell.domains.linkifyListFrom(rootNode, Pf2eIndexType.domain, this), @@ -111,7 +103,7 @@ level, toTitleCase(type), getAmpEffects()); } - QuteSpellTarget getQuteSpellTarget(Tags tags) { + QuteSpellTarget getQuteSpellTarget() { String targets = Pf2eSpell.targets.replaceTextFrom(rootNode, this); SpellArea area = Pf2eSpell.area.fieldFromTo(rootNode, SpellArea.class, tui()); QuteDataRange range = Pf2eSpell.range.getRangeFrom(rootNode, this); diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteTable.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteTable.java index eaac32255..87446a3bc 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteTable.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteTable.java @@ -4,11 +4,9 @@ import java.util.ArrayList; import java.util.List; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.Pf2eQuteNote; public class Json2QuteTable extends Json2QuteBase { @@ -19,9 +17,7 @@ public Json2QuteTable(Pf2eIndex index, JsonNode rootNode) { @Override protected Pf2eQuteNote buildQuteNote() { - Tags tags = new Tags(sources); List text = new ArrayList<>(); - ((ObjectNode) rootNode).put(SourceField.type.name(), "table"); appendToText(text, rootNode, null); diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteTrait.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteTrait.java index f867a587e..4a7a2a955 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteTrait.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Json2QuteTrait.java @@ -2,10 +2,8 @@ import java.util.ArrayList; import java.util.List; - import com.fasterxml.jackson.databind.JsonNode; -import dev.ebullient.convert.tools.Tags; import dev.ebullient.convert.tools.pf2e.qute.Pf2eQuteNote; import dev.ebullient.convert.tools.pf2e.qute.QuteTrait; import dev.ebullient.convert.tools.pf2e.qute.QuteTraitIndex; @@ -18,8 +16,6 @@ public Json2QuteTrait(Pf2eIndex index, JsonNode rootNode) { @Override protected QuteTrait buildQuteResource() { - Tags tags = new Tags(sources); - List text = new ArrayList<>(); List categories = new ArrayList<>(); Field.categories.getListOfStrings(rootNode, tui()).forEach(c -> { @@ -28,7 +24,7 @@ protected QuteTrait buildQuteResource() { JsonNode implied = TraitField.implies.getFrom(rootNode); if (implied != null) { implied.fieldNames().forEachRemaining(n -> { - if ("spell".equals(n.toLowerCase())) { + if ("spell".equalsIgnoreCase(n)) { String school = implied.get(n).get("_fSchool").asText(); tags.add("trait", "category", "spell", school); categories.add(String.format("%s (%s)", c, school)); @@ -41,9 +37,7 @@ protected QuteTrait buildQuteResource() { } }); - appendToText(text, SourceField.entries.getFrom(rootNode), "##"); - - return new QuteTrait(sources, text, tags, List.of(), categories); + return new QuteTrait(sources, entries, tags, List.of(), categories); } static Pf2eQuteNote buildIndex(Pf2eIndex index) { diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/JsonSource.java b/src/main/java/dev/ebullient/convert/tools/pf2e/JsonSource.java index 79ac45ff8..00aeb0c9f 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/JsonSource.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/JsonSource.java @@ -3,12 +3,8 @@ import static dev.ebullient.convert.StringUtil.join; import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Set; -import java.util.TreeSet; import java.util.stream.Collectors; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -17,31 +13,12 @@ import dev.ebullient.convert.io.Tui; import dev.ebullient.convert.qute.QuteUtil; import dev.ebullient.convert.tools.JsonNodeReader.FieldValue; -import dev.ebullient.convert.tools.Tags; -import dev.ebullient.convert.tools.pf2e.Json2QuteAbility.Pf2eAbility; -import dev.ebullient.convert.tools.pf2e.Json2QuteAffliction.Pf2eAffliction; import dev.ebullient.convert.tools.pf2e.Json2QuteItem.Pf2eItem; import dev.ebullient.convert.tools.pf2e.qute.Pf2eQuteBase; import dev.ebullient.convert.tools.pf2e.qute.QuteDataActivity; public interface JsonSource extends JsonTextReplacement { - /** - * Collect and linkify traits from the specified node. - * - * @param tags The tags to populate while collecting traits. If null, then don't populate any tags. - * - * @return an empty or sorted/linkified list of traits (never null) - */ - default Set collectTraitsFrom(JsonNode sourceNode, Tags tags) { - return Field.traits.getListOfStrings(sourceNode, tui()).stream() - .peek(tags == null ? t -> { - } : t -> tags.add("trait", t)) - .sorted() - .map(s -> linkify(Pf2eIndexType.trait, s)) - .collect(Collectors.toCollection(TreeSet::new)); - } - /** * External (and recursive) entry point for content parsing. * @@ -115,8 +92,10 @@ default void appendObjectToText(List text, JsonNode node, String heading case quote -> appendQuote(text, node); // special inline types - case ability -> appendRenderable(text, Pf2eAbility.createEmbeddedAbility(node, this)); - case affliction -> appendAffliction(text, node); + case ability -> appendRenderable(text, + new Json2QuteAbility(index(), node, true).buildQuteNote()); + case affliction -> appendRenderable(text, + new Json2QuteAffliction(index(), Pf2eIndexType.affliction, node, true).buildQuteNote()); case attack -> appendRenderable(text, Pf2eJsonNodeReader.Pf2eAttack.getAttack(node, this)); case data -> embedData(text, node); case lvlEffect -> appendLevelEffect(text, node); @@ -299,11 +278,6 @@ default void appendSuccessDegree(List text, JsonNode node) { + x)); } - /** Internal */ - default void appendAffliction(List text, JsonNode node) { - appendRenderable(text, Pf2eAffliction.createInlineAffliction(node, this)); - } - /** Internal */ private void appendRenderable(List text, QuteUtil.Renderable renderable) { text.addAll(List.of(renderable.render().split("\n"))); @@ -555,14 +529,14 @@ default void embedData(List text, JsonNode dataNode) { // (This might be the case anyway, but we know it probably is the case with these). // So try to get the renderable embedded object first, and then add the collapsed // tag to the outermost admonition. - QuteUtil.Renderable renderable = switch (dataType) { - case ability -> Pf2eAbility.createEmbeddedAbility(data, this); - case affliction, curse, disease -> Pf2eAffliction.createInlineAffliction(data, this); + Json2QuteBase json2Renderable = switch (dataType) { + case ability -> new Json2QuteAbility(index(), data, true); + case affliction, curse, disease -> new Json2QuteAffliction(index(), dataType, data, true); default -> null; }; - if (renderable != null) { + if (json2Renderable != null) { List renderedData = new ArrayList<>(); - appendRenderable(renderedData, renderable); + appendRenderable(renderedData, (QuteUtil.Renderable) json2Renderable.buildQuteNote()); // Make the outermost admonition collapsed, if there is one int[] adIndices = outerAdmonitionIndices(renderedData); if (adIndices != null) { @@ -578,8 +552,8 @@ default void embedData(List text, JsonNode dataNode) { Pf2eQuteBase converted = dataType.convertJson2QuteBase(index(), data); if (converted != null) { renderEmbeddedTemplate(text, converted, tag, - List.of(String.format("title: %s", converted.title()), - "collapse: closed")); + "title: %s".formatted(converted.title()), + "collapse: closed"); } else { tui().errorf("Unable to process data for %s: %s", tag, dataNode.toString()); } @@ -608,9 +582,7 @@ default List embedGenericData(String tag, JsonNode data) { text.add("title: " + title); // Add traits - Tags tags = new Tags(); - Collection traits = collectTraitsFrom(data, tags); - text.add(join(" ", traits) + " "); + text.add(join(" ", getTraits(data)) + " "); maybeAddBlankLine(text); // Add rendered sections diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/JsonTextReplacement.java b/src/main/java/dev/ebullient/convert/tools/pf2e/JsonTextReplacement.java index 24dc0c4b2..bd67d3149 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/JsonTextReplacement.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/JsonTextReplacement.java @@ -3,12 +3,13 @@ import static dev.ebullient.convert.StringUtil.join; import static dev.ebullient.convert.StringUtil.toAnchorTag; import static dev.ebullient.convert.StringUtil.toTitleCase; +import static dev.ebullient.convert.tools.pf2e.Pf2eActivity.linkifyActivity; import java.util.ArrayList; import java.util.List; import java.util.regex.MatchResult; import java.util.regex.Pattern; - +import java.util.stream.Collector; import com.fasterxml.jackson.databind.JsonNode; import dev.ebullient.convert.config.CompendiumConfig; @@ -17,9 +18,11 @@ import dev.ebullient.convert.tools.JsonNodeReader; import dev.ebullient.convert.tools.JsonNodeReader.FieldValue; import dev.ebullient.convert.tools.JsonTextConverter; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataActivity.Activity; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataRef; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataTraits; public interface JsonTextReplacement extends JsonTextConverter { - enum Field implements Pf2eJsonNodeReader { alias, auto, @@ -66,11 +69,8 @@ default CompendiumConfig cfg() { return index().cfg(); } - default String replaceText(String input) { - return replaceTokens(input, (s, b) -> this._replaceTokenText(s, b)); - } - - default String _replaceTokenText(String input, boolean nested) { + @Override + default String replaceTokenText(String input, boolean nested) { if (input == null || input.isEmpty()) { return input; } @@ -193,31 +193,29 @@ default String replaceFootnoteReference(MatchResult match) { } default String replaceActionAs(MatchResult match) { - final Pf2eActivity type; - switch (match.group(1).toLowerCase()) { - case "1": - case "a": - type = Pf2eActivity.single; - break; - case "2": - case "d": - type = Pf2eActivity.two; - break; - case "3": - case "t": - type = Pf2eActivity.three; - break; - case "f": - type = Pf2eActivity.free; - break; - case "r": - type = Pf2eActivity.reaction; - break; - default: - type = Pf2eActivity.varies; - break; - } - return type.linkify(index().rulesVaultRoot()); + Activity type = switch (match.group(1).toLowerCase()) { + case "1", "a" -> Activity.single; + case "2", "d" -> Activity.two; + case "3", "t" -> Activity.three; + case "f" -> Activity.free; + case "r" -> Activity.reaction; + default -> Activity.varies; + }; + return linkifyActivity(type, index().rulesVaultRoot()); + } + + /** + * Collect and linkify traits from the specified node. + * + * @return a {@link QuteDataTraits} which may be empty (never null) + */ + default QuteDataTraits getTraits(JsonNode sourceNode) { + return Field.traits.getListOfStrings(sourceNode, tui()).stream() + .map(s -> QuteDataRef.fromMarkdownLink(linkify(Pf2eIndexType.trait, s))) + .collect(Collector.of(QuteDataTraits::new, QuteDataTraits::add, (a, b) -> { + a.addAll(b); + return b; + })); } default String linkifyRuneItem(MatchResult match) { @@ -361,6 +359,8 @@ default String linkifyTrait(String match) { default String linkifyTrait(JsonNode traitNode, String linkText) { if (traitNode != null) { String source = SourceField.source.getTextOrEmpty(traitNode); + // Some traits are surrounded in square brackets. Strip this out to avoid messing up link rendering. + linkText = linkText.replaceFirst("^\\[(.*)]$", "$1"); return "[%s](%s/%s%s.md \"%s\")".formatted( linkText, diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eActivity.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eActivity.java index 5b5512920..8f1e68781 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eActivity.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eActivity.java @@ -7,86 +7,55 @@ import dev.ebullient.convert.io.Tui; import dev.ebullient.convert.tools.pf2e.qute.QuteDataActivity; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataActivity.Activity; -public enum Pf2eActivity { - single("Single Action", ">", "single_action.svg"), - two("Two-Action", ">>", "two_actions.svg"), - three("Three-Action", ">>>", "three_actions.svg"), - free("Free Action", "F", "delay.svg"), - reaction("Reaction", "R", "reaction.svg"), - varies("Varies", "V", "load.svg"), - timed("Duration or Frequency", "⏲", "hour-glass.svg"); - +public class Pf2eActivity { final static String DOC_PATH = "core-rulebook/chapter-9-playing-the-game.md#Actions"; - final String longName; - final String markdownName; - final String textGlyph; - final String glyph; - final String targetFileName; - - Pf2eActivity(String longName, String textGlyph, String glyph) { - this.longName = longName; - this.markdownName = longName.replace(" ", "%20"); - this.textGlyph = textGlyph; - this.glyph = glyph; - - int x = glyph.lastIndexOf('.'); - this.targetFileName = Tui.slugify(glyph.substring(0, x)) + glyph.substring(x); - } - - public static Pf2eActivity toActivity(String unit, int number) { - switch (unit) { - case "single": - case "action": - switch (number) { - case 1: - return single; - case 2: - return two; - case 3: - return three; - } - break; - case "free": - return free; - case "reaction": - return reaction; - case "varies": - return varies; - case "timed": - return timed; + public static void addImageRef(Activity activity, JsonSource convert) { + String glyph = switch (activity) { + case single -> "single_action"; + case two -> "two_actions"; + case three -> "three_actions"; + case free -> "delay"; + case reaction -> "reaction"; + case varies -> "load"; + case timed -> "hour-glass"; + }; + String targetFileName = Tui.slugify(glyph) + ".svg"; + Pf2eSources.buildStreamImageRef( + convert.index(), glyph + ".svg", Path.of("img", targetFileName), activity.longName); + } + + public static String linkifyActivity(Activity activity, String rulesRoot) { + return "[%s](%s \"%s\")".formatted(activity.textGlyph, rulesRoot + DOC_PATH, activity.longName); + } + + public static QuteDataActivity toQuteActivity(JsonSource convert, String unit, int number, String text) { + Activity activity = switch (unit) { + case "single", "action" -> switch (number) { + case 1 -> Activity.single; + case 2 -> Activity.two; + case 3 -> Activity.three; + default -> null; + }; + case "free" -> Activity.free; + case "reaction" -> Activity.reaction; + case "varies" -> Activity.varies; + case "timed" -> Activity.timed; + default -> null; + }; + if (activity == null) { + return null; } - return null; - } - - public String getLongName() { - return this.longName; - } - - public String getTextGlyph() { - return this.textGlyph; - } - - public String getGlyph() { - return this.glyph; - } - - public String linkify(String rulesRoot) { - return String.format("[%s](%s \"%s\")", - this.textGlyph, getRulesPath(rulesRoot), longName); - } - - public String getRulesPath(String rulesRoot) { - return String.format("%s%s", rulesRoot, DOC_PATH); + return toQuteActivity(convert, activity, text); } - public QuteDataActivity toQuteActivity(JsonSource convert, String text) { - Path relativeTarget = Path.of("img", targetFileName); + public static QuteDataActivity toQuteActivity(JsonSource convert, Activity activity, String text) { + addImageRef(activity, convert); return new QuteDataActivity( - this != timed && isPresent(text) ? join(" ", getLongName(), text) : text, - Pf2eSources.buildStreamImageRef(convert.index(), glyph, relativeTarget, longName), - textGlyph, - this.getRulesPath(convert.index().rulesVaultRoot())); + activity, + convert.index().rulesVaultRoot() + DOC_PATH, + activity != Activity.timed && isPresent(text) ? join(" ", activity.longName, text) : text); } } diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eJsonNodeReader.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eJsonNodeReader.java index a4db54aad..8fc1e3a7d 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eJsonNodeReader.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eJsonNodeReader.java @@ -7,26 +7,24 @@ import static java.util.Objects.requireNonNullElse; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; - import com.fasterxml.jackson.databind.JsonNode; import dev.ebullient.convert.StringUtil; import dev.ebullient.convert.tools.JsonNodeReader; -import dev.ebullient.convert.tools.pf2e.Json2QuteAbility.Pf2eAbility; -import dev.ebullient.convert.tools.pf2e.Json2QuteAffliction.Pf2eAffliction; import dev.ebullient.convert.tools.pf2e.JsonSource.AppendTypeValue; import dev.ebullient.convert.tools.pf2e.qute.QuteAbilityOrAffliction; import dev.ebullient.convert.tools.pf2e.qute.QuteDataActivity; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataActivity.Activity; import dev.ebullient.convert.tools.pf2e.qute.QuteDataArmorClass; import dev.ebullient.convert.tools.pf2e.qute.QuteDataDefenses; import dev.ebullient.convert.tools.pf2e.qute.QuteDataDuration; @@ -35,6 +33,7 @@ import dev.ebullient.convert.tools.pf2e.qute.QuteDataGenericStat.QuteDataNamedBonus; import dev.ebullient.convert.tools.pf2e.qute.QuteDataHpHardnessBt; import dev.ebullient.convert.tools.pf2e.qute.QuteDataRange; +import dev.ebullient.convert.tools.pf2e.qute.QuteDataRef; import dev.ebullient.convert.tools.pf2e.qute.QuteDataSpeed; import dev.ebullient.convert.tools.pf2e.qute.QuteDataTimedDuration; import dev.ebullient.convert.tools.pf2e.qute.QuteInlineAttack; @@ -42,16 +41,6 @@ /** A utility class which extends {@link JsonNodeReader} with PF2e-specific functionality. */ public interface Pf2eJsonNodeReader extends JsonNodeReader { - /** - * Return alignments as a list of formatted strings from this field in the given node. - * Returns an empty list if we couldn't get alignments. - */ - default List getAlignmentsFrom(JsonNode alignNode, JsonSource convert) { - return streamFrom(alignNode) - .map(JsonNode::asText) - .map(a -> a.length() > 2 ? a : convert.linkifyTrait(a.toUpperCase())) - .toList(); - } /** Return a {@link QuteDataSpeed} read from this field of the {@code source} node, or null. */ default QuteDataSpeed getSpeedFrom(JsonNode source, JsonSource convert) { @@ -106,7 +95,7 @@ default List getAttacksFrom(JsonNode source, JsonSource conver * traits from these activation components to {@code traits}. Return an empty list if we couldn't get activation * components. */ - default List getActivationComponentsFrom(JsonNode source, Set traits, JsonSource convert) { + default List getActivationComponentsFrom(JsonNode source, Collection traits, JsonSource convert) { List rawComponents = getListOfStrings(source, convert.tui()).stream() .map(s -> s.replaceFirst("^\\((%s)\\)$", "\1")) // remove parens .toList(); @@ -124,6 +113,7 @@ default List getActivationComponentsFrom(JsonNode source, Set tr return Stream.of(); }).distinct() .map(convert::linkifyTrait) + .map(QuteDataRef::fromMarkdownLink) .forEach(traits::add); return rawComponents.stream().map(convert::replaceText).toList(); @@ -133,14 +123,15 @@ default List getActivationComponentsFrom(JsonNode source, Set tr default List getAbilityOrAfflictionsFrom(JsonNode source, JsonSource convert) { return streamFrom(source) .map(n -> switch (requireNonNullElse(AppendTypeValue.getBlockType(n), AppendTypeValue.ability)) { - case affliction -> (QuteAbilityOrAffliction) Pf2eAffliction.createInlineAffliction(n, convert); - case ability -> Pf2eAbility.createEmbeddedAbility(n, convert); + case affliction -> new Json2QuteAffliction(convert.index(), Pf2eIndexType.affliction, n, true); + case ability -> new Json2QuteAbility(convert.index(), n, true); default -> { convert.tui().debugf("Unexpected block type in %s", source.toPrettyString()); yield null; } }) .filter(Objects::nonNull) + .map(json2Qute -> (QuteAbilityOrAffliction) json2Qute.buildNote()) .toList(); } @@ -518,26 +509,21 @@ enum Pf2eNumberUnitEntry implements Pf2eJsonNodeReader { */ private static QuteDataActivity getActivity(JsonNode node, JsonSource convert) { String actionType = unit.getTextOrNull(node); - Pf2eActivity activity = switch (actionType) { - case "single", "action", "free", "reaction" -> - Pf2eActivity.toActivity(actionType, number.intOrThrow(node)); - case "varies" -> Pf2eActivity.varies; - case "day", "minute", "hour", "round" -> Pf2eActivity.timed; - default -> null; - }; - - if (activity == null) { - throw new IllegalArgumentException("Can't parse activity from: %s".formatted(node)); - } String extra = entry.getTextFrom(node) - .filter(s -> !s.toLowerCase().contains("varies")) - .filter(Predicate.not(String::isBlank)) - .map(convert::replaceText).map(StringUtil::parenthesize) - .orElse(""); - - return activity.toQuteActivity( - convert, activity == Pf2eActivity.timed ? join(" ", number.intOrThrow(node), actionType, extra) : extra); + .filter(s -> !s.toLowerCase().contains("varies")) + .filter(Predicate.not(String::isBlank)) + .map(convert::replaceText).map(StringUtil::parenthesize) + .orElse(""); + + return switch (actionType) { + case "single", "action", "free", "reaction", "varies" -> + Pf2eActivity.toQuteActivity(convert, actionType, number.intOrDefault(node, 0), extra); + case "day", "minute", "hour", "round" -> + Pf2eActivity.toQuteActivity( + convert, Activity.timed, join(" ", number.intOrThrow(node), actionType, extra)); + default -> throw new IllegalArgumentException("Can't parse activity from: %s".formatted(node)); + }; } /** @@ -565,9 +551,8 @@ private static QuteDataDuration getDuration(JsonNode node, JsonSource convert) { return null; } // The activity is more specific unless we have a custom display. Otherwise, fall back to the timed duration - return Optional.ofNullable(Pf2eActivity.toActivity(unitText, timedDuration.value())) - .map(a -> (QuteDataDuration) a.toQuteActivity(convert, null)) - .orElse(timedDuration); + return requireNonNullElse( + Pf2eActivity.toQuteActivity(convert, unitText, timedDuration.value(), null), timedDuration); } /** @@ -677,12 +662,12 @@ public static QuteInlineAttack getAttack(JsonNode node, JsonSource convert) { return new QuteInlineAttack( name.replaceTextFrom(node, convert), Optional.ofNullable(activity.getActivityFrom(node, convert)) - .orElse(Pf2eActivity.single.toQuteActivity(convert, "")), + .orElse(Pf2eActivity.toQuteActivity(convert, Activity.single, "")), QuteInlineAttack.AttackRangeType.valueOf(range.getTextOrDefault(node, "Melee").toUpperCase()), attack.intOrNull(node), formattedDamage, types.replaceTextFromList(node, convert), - convert.collectTraitsFrom(node, null), + convert.getTraits(node), hasMultilineEffect ? List.of() : attackEffects, hasMultilineEffect ? String.join("\n", attackEffects) : null, noMAP.booleanOrDefault(node, false) ? List.of() : List.of("no multiple attack penalty"), @@ -729,10 +714,11 @@ public static QuteDataNamedBonus getNamedBonus( return new QuteDataNamedBonus( displayName, std.intOrThrow(source), - convert.streamPropsExcluding(source, std, note) + convert.streamPropsExcluding(source, std, note, abilities, notes) .collect(Collectors.toMap(e -> convert.replaceText(e.getKey()), e -> e.getValue().asInt())), - note.getTextFrom(source).map(convert::replaceText).map(List::of) - .orElse((abilities.existsIn(source) ? abilities : notes).replaceTextFromList(source, convert))); + Stream.of(abilities, note, notes) + .flatMap(field -> field.replaceTextFromList(source, convert).stream()) + .toList()); } } diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eMarkdown.java b/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eMarkdown.java index 569eda6b9..613ef1f3f 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eMarkdown.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/Pf2eMarkdown.java @@ -107,9 +107,9 @@ private Pf2eMarkdown writeNotesAndTables(List types) { } switch (type) { - case ability -> rules.add(new Json2QuteAbility(index, type, node).buildNote()); + case ability -> rules.add(new Json2QuteAbility(index, node, false).buildNote()); case affliction, curse, disease -> - compendium.add(new Json2QuteAffliction(index, type, node).buildNote()); + compendium.add(new Json2QuteAffliction(index, type, node, false).buildNote()); case book -> { index.tui().progressf("book %s", e.getKey()); JsonNode data = index.getIncludedNode(key.replace("book|", "data|")); diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAbility.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAbility.java index b26591dd3..0279d2927 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAbility.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAbility.java @@ -29,10 +29,10 @@ public final class QuteAbility extends Pf2eQuteNote implements QuteUtil.Renderab /** A formatted string which is a link to the base ability that this ability references. Embedded only. */ public final String reference; /** - * Collection of trait links. Use `{#for}` or `{#each}` to iterate over the collection. - * See [traitList](#traitlist) or [bareTraitList](#baretraitlist). + * Collection of trait links as {@link QuteDataRef}. Use `{#for}` or `{#each}` to iterate over the collection. + * See {@link QuteAbility#getBareTraitList()}. */ - public final Collection traits; + public final Collection traits; /** {@link QuteDataRange}. The targeting range for this ability. */ public final QuteDataRange range; /** List of formatted strings. Activation components for this ability, e.g. command, envision */ @@ -65,8 +65,8 @@ public final class QuteAbility extends Pf2eQuteNote implements QuteUtil.Renderab // Internal only. private final JsonSource _converter; - public QuteAbility(Pf2eSources sources, String name, String reference, String text, Tags tags, - Collection traits, QuteDataActivity activity, QuteDataRange range, + public QuteAbility(Pf2eSources sources, String name, String reference, List text, Tags tags, + Collection traits, QuteDataActivity activity, QuteDataRange range, List components, String requirements, String prerequisites, String cost, String trigger, QuteDataFrequency frequency, String special, String note, boolean embedded, JsonSource converter) { @@ -128,7 +128,7 @@ public String getBareTraitList() { return ""; } return traits.stream() - .map(x -> x.replaceAll(" \".*\"", "")) + .map(ref -> new QuteDataRef(ref.displayText(), ref.notePath(), null).toString()) .collect(Collectors.joining(", ")); } diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAction.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAction.java index 7397fe053..f95538f8d 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAction.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAction.java @@ -20,8 +20,8 @@ public class QuteAction extends Pf2eQuteBase { public final String trigger; /** Aliases for this note */ public final List aliases; - /** Collection of traits (decorated links) */ - public final Collection traits; + /** Collection of traits (collection of {@link QuteDataRef}) */ + public final Collection traits; /** Situational requirements for performing this action */ public final String requirements; /** Prerequisite trait or characteristic for performing this action */ @@ -39,7 +39,7 @@ public class QuteAction extends Pf2eQuteBase { public final QuteDataActivity activity; public QuteAction(Pf2eSources sources, List text, Tags tags, - String cost, String trigger, List aliases, Collection traits, + String cost, String trigger, List aliases, Collection traits, String prerequisites, String requirements, QuteDataFrequency frequency, QuteDataActivity activity, ActionType actionType) { super(sources, text, tags); diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAffliction.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAffliction.java index 25068da6e..35d192f10 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAffliction.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteAffliction.java @@ -22,8 +22,8 @@ @TemplateData public final class QuteAffliction extends Pf2eQuteNote implements QuteUtil.Renderable, QuteAbilityOrAffliction { - /** Collection of traits (decorated links) */ - public final Collection traits; + /** Collection of traits ({@link QuteDataRef}) */ + public final Collection traits; /** Integer from 1 to 10. Level of the affliction. */ public final String level; /** Aliases for this note. Only populated if not embedded. */ @@ -58,7 +58,7 @@ public final class QuteAffliction extends Pf2eQuteNote implements QuteUtil.Rende public QuteAffliction( Pf2eSources sources, String name, List text, Tags tags, - Collection traits, List aliases, String level, + Collection traits, List aliases, String level, String category, String maxDuration, String onset, QuteAfflictionSave savingThrow, String effect, String temptedCurse, List notes, Map stages, boolean isEmbedded, JsonTextConverter _converter) { @@ -96,7 +96,7 @@ public String template() { @Override public String render() { - return _converter.renderInlineTemplate(this, null); + return _converter.renderEmbeddedTemplate(this, null); } @Override diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteArchetype.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteArchetype.java index 53ec98b96..cdcd377ec 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteArchetype.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteArchetype.java @@ -15,15 +15,15 @@ @TemplateData public class QuteArchetype extends Pf2eQuteBase { - /** Collection of traits (decorated links) */ - public final Collection traits; + /** Collection of traits (collection of {@link QuteDataRef}) */ + public final Collection traits; public final int dedicationLevel; public final List benefits; public final List feats; public QuteArchetype(Pf2eSources sources, List text, Tags tags, - Collection traits, int dedicationLevel, List benefits, List feats) { + Collection traits, int dedicationLevel, List benefits, List feats) { super(sources, text, tags); this.traits = traits; diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteCreature.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteCreature.java index b24c918eb..6c931d926 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteCreature.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteCreature.java @@ -1,7 +1,7 @@ package dev.ebullient.convert.tools.pf2e.qute; import static dev.ebullient.convert.StringUtil.flatJoin; -import static dev.ebullient.convert.StringUtil.format; +import static dev.ebullient.convert.StringUtil.formatIfPresent; import static dev.ebullient.convert.StringUtil.join; import static dev.ebullient.convert.StringUtil.parenthesize; import static dev.ebullient.convert.StringUtil.pluralize; @@ -11,9 +11,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import dev.ebullient.convert.StringUtil; import dev.ebullient.convert.io.JavadocVerbatim; import dev.ebullient.convert.qute.QuteUtil; import dev.ebullient.convert.tools.Tags; @@ -30,8 +28,8 @@ public class QuteCreature extends Pf2eQuteBase { /** Aliases for this note (optional) */ public final List aliases; - /** Collection of traits (decorated links, optional) */ - public final Collection traits; + /** Collection of traits (collection of {@link QuteDataRef}) */ + public final Collection traits; /** Short creature description (optional) */ public final String description; /** Creature level (number, optional) */ @@ -70,8 +68,8 @@ public class QuteCreature extends Pf2eQuteBase { public final List ritualCasting; public QuteCreature( - Pf2eSources sources, String text, Tags tags, - Collection traits, List aliases, + Pf2eSources sources, List text, Tags tags, + Collection traits, List aliases, String description, Integer level, Integer perception, QuteDataDefenses defenses, CreatureLanguages languages, CreatureSkills skills, List senses, Map abilityMods, @@ -252,8 +250,8 @@ public String name() { @JavadocVerbatim public String formattedStats() { return join(", ", - format("DC %d", dc), - format("attack %+d", attackBonus), + formatIfPresent("DC %d", dc), + formatIfPresent("attack %+d", attackBonus), focusPoints == null ? "" : focusPoints + " Focus " + pluralize("Point", focusPoints)); } } @@ -301,9 +299,9 @@ public String rank() { @Override public String toString() { return join(" ", - format("**%s**", rank()), + formatIfPresent("**%s**", rank()), join(", ", spells), - format("(%d slots)", slots)); + formatIfPresent("(%d slots)", slots)); } } @@ -315,7 +313,7 @@ public String toString() { * ``` * * @param name The name of the spell - * @param link A formatted link to the spell's note, or just the spell's name if we couldn't get a link. + * @param spellRef A {@link QuteDataRef} to the spell's note, or null if we couldn't find a note * @param amount The number of casts available for this spell. A value of 0 represents an at will spell. Use * {@link QuteCreature.CreatureSpellReference#formattedAmount()} to get this as a formatted string. * @param notes Any notes associated with this spell, e.g. "at will only" @@ -323,22 +321,23 @@ public String toString() { @TemplateData public record CreatureSpellReference( String name, - String link, + QuteDataRef spellRef, Integer amount, - List notes) { + List notes) implements QuteDataGenericStat { + + @Override + public Integer value() { + return amount; + } /** The number of casts as a formatted string, e.g. "(at will)" or "(×2)". Empty when the amount is 1. */ public String formattedAmount() { return amount == 1 ? "" : parenthesize(amount == 0 ? "at will" : "×" + amount); } - public String formattedNotes() { - return notes.stream().map(StringUtil::parenthesize).collect(Collectors.joining(" ")); - } - @Override public String toString() { - return join(" ", link, formattedAmount(), formattedNotes()); + return join(" ", spellRef == null ? name : spellRef, formattedAmount(), formattedNotes()); } } } diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataActivity.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataActivity.java index f89ad9a53..94fa2d200 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataActivity.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataActivity.java @@ -2,7 +2,6 @@ import static dev.ebullient.convert.StringUtil.join; -import dev.ebullient.convert.qute.ImageRef; import dev.ebullient.convert.qute.QuteUtil; import io.quarkus.qute.TemplateData; @@ -10,25 +9,45 @@ * Pf2eTools activity attributes. This attribute will render itself as a formatted link: * *
- *     [textGlyph](rulesPath "glyph.title")<optional text>
+ *     [textGlyph](rulesPath "action name")<optional text>
  * 
* - * @param text The text associated with the action - may be null. - * @param glyph icon/image representing this activity as a {@link dev.ebullient.convert.qute.ImageRef ImageRef} - * @param textGlyph A textual representation of the glyph, used as the link text - * @param rulesPath The path which leads to an explanation of this particular activity + * @param activity The type of activity, as a {@link QuteDataActivity.Activity} + * @param actionRef A {@link QuteDataRef} to the rules for this particular action type + * @param note Text associated with this activity */ @TemplateData -public record QuteDataActivity(String text, ImageRef glyph, String textGlyph, - String rulesPath) implements QuteUtil, QuteDataDuration { +public record QuteDataActivity(Activity activity, QuteDataRef actionRef, String note) implements QuteUtil, QuteDataDuration { + + public QuteDataActivity(Activity activity, String rulesPath, String note) { + this(activity, new QuteDataRef(activity.textGlyph, rulesPath, activity.longName), note); + } /** Return the text associated with the action. */ - @Override public String text() { - return isPresent(text) ? text : glyph.title; + return isPresent(note) ? note : activity.longName; } + @Override public String toString() { - return join(" ", "[%s](%s \"%s\")".formatted(textGlyph, rulesPath, glyph.title), text); + return join(" ", actionRef.toString(), note); + } + + public enum Activity { + single("Single Action", ">"), + two("Two-Action", ">>"), + three("Three-Action", ">>>"), + free("Free Action", "F"), + reaction("Reaction", "R"), + varies("Varies", "V"), + timed("Duration or Frequency", "⏲"); + + public final String longName; + public final String textGlyph; + + Activity(String longName, String textGlyph) { + this.longName = longName; + this.textGlyph = textGlyph; + } } } diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataArmorClass.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataArmorClass.java index 7e72e158d..0511e9b12 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataArmorClass.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataArmorClass.java @@ -47,6 +47,11 @@ private String formattedAlternates(boolean asBonus) { formatMap(alternateValues, (k, v) -> parenthesize(join(" ", valFormat.formatted(v), k)))); } + @Override + public String formattedNotes() { + return flatJoin(", ", List.of(formattedAlternates(false)), notes, abilities); + } + @Override public String bonus() { return join(" ", QuteDataGenericStat.super.bonus(), formattedAlternates(true)); @@ -54,6 +59,6 @@ public String bonus() { @Override public String toString() { - return flatJoin(" ", List.of("**AC**", value, formattedAlternates(false)), notes, abilities); + return join(" ", value, formattedNotes()); } } diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataDefenses.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataDefenses.java index 5fd5abd1d..5080e16ea 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataDefenses.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataDefenses.java @@ -1,13 +1,14 @@ package dev.ebullient.convert.tools.pf2e.qute; +import static dev.ebullient.convert.StringUtil.formatIfPresent; import static dev.ebullient.convert.StringUtil.formatMap; import static dev.ebullient.convert.StringUtil.join; -import static dev.ebullient.convert.StringUtil.joinWithPrefix; import static dev.ebullient.convert.StringUtil.joiningNonEmpty; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; import java.util.stream.Stream; import dev.ebullient.convert.qute.QuteUtil; @@ -50,20 +51,37 @@ public record QuteDataDefenses( Map resistances, Map weaknesses) implements QuteUtil { + @SuppressWarnings("unused") // for template use + public String additionalHp() { + return additionalHpHardnessBt.entrySet().stream() + .filter(e -> e.getValue().hp() != null) + .map(e -> "__%s HP__ %s".formatted(e.getKey(), e.getValue().hp())) + .collect(Collectors.joining("; ")); + } + + @SuppressWarnings("unused") // for template use + public String additionalHardness() { + return additionalHpHardnessBt.entrySet().stream() + .filter(e -> e.getValue().hardness() != null) + .map(e -> "__%s Hardness__ %s".formatted(e.getKey(), e.getValue().hardness())) + .collect(Collectors.joining("; ")); + } + @Override public String toString() { return join("\n", // - **AC** 21; **Fort** +15, **Ref** +12, **Will** +10 - joinWithPrefix("; ", "- ", ac, savingThrows), + formatIfPresent("- %s", + join("; ", formatIfPresent("**AC** %s", ac), savingThrows)), // - **Hardness** 18, **HP (BT)** 10; **Immunities** critical hits; **Resistances** fire 5 - joinWithPrefix("; ", "- ", + formatIfPresent("- %s", join("; ", hpHardnessBt, join("; ", formatMap(additionalHpHardnessBt, (k, v) -> v.toStringWithName(k))), - joinWithPrefix(", ", "**Immunities** ", immunities), + formatIfPresent("**Immunities** %s", join(", ", immunities)), formatMap(resistances, (k, v) -> join(" ", k, v)).stream().sorted() .collect(joiningNonEmpty(", ", "**Resistances** ")), formatMap(weaknesses, (k, v) -> join(" ", k, v)).stream().sorted() - .collect(joiningNonEmpty(", ", "**Weaknesses** ")))); + .collect(joiningNonEmpty(", ", "**Weaknesses** "))))); } /** diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataRef.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataRef.java new file mode 100644 index 000000000..d689a82b7 --- /dev/null +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataRef.java @@ -0,0 +1,68 @@ +package dev.ebullient.convert.tools.pf2e.qute; + +import static dev.ebullient.convert.StringUtil.formatIfPresent; +import static dev.ebullient.convert.StringUtil.isPresent; + +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A reference to another note. This will render itself as a formatted link. + * + * @param displayText The display text for the link + * @param notePath The path to the note that this link references. Null if we couldn't find a note to reference. + * @param title The hover title to use for the reference (optional) + */ +public record QuteDataRef(String displayText, String notePath, String title) implements Comparable { + + private static final Pattern MARKDOWN_LINK_PAT = Pattern.compile("^\\[(?.+)]\\((?.*?)(?: \"(?.*)\")?\\)$"); + + public QuteDataRef(String displayText) { + this(displayText, null, null); + } + + public static QuteDataRef fromMarkdownLink(String link) { + if (!isPresent(link)) { + return null; + } + Matcher matcher = MARKDOWN_LINK_PAT.matcher(link); + return matcher.matches() + ? new QuteDataRef(matcher.group("display"), matcher.group("path"), matcher.group("title")) + : new QuteDataRef(link); + } + + /** Return this reference as a Markdown link, without the title attribute. */ + public String withoutTitle() { + return notePath != null ? "[%s](%s)".formatted(displayText, notePath) : displayText; + } + + @Override + public String toString() { + return notePath != null + ? "[%s](%s%s)".formatted(displayText, notePath, formatIfPresent(" \"%s\"", title)) + : displayText; + } + + @Override + public int compareTo(QuteDataRef o) { + if (!displayText.equals(o.displayText)) { + return displayText.compareTo(o.displayText); + } else if (!Objects.equals(notePath, o.notePath)) { + if (notePath == null) { + return -1; + } else if (o.notePath == null) { + return 1; + } + return notePath.compareTo(o.notePath); + } else if (!Objects.equals(title, o.title)) { + if (title == null) { + return -1; + } else if (o.title == null) { + return 1; + } + return title.compareTo(o.title); + } + return 0; + } +} diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataTraits.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataTraits.java new file mode 100644 index 000000000..e207e20c2 --- /dev/null +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteDataTraits.java @@ -0,0 +1,142 @@ +package dev.ebullient.convert.tools.pf2e.qute; + +import static dev.ebullient.convert.StringUtil.isPresent; +import static dev.ebullient.convert.StringUtil.join; +import static dev.ebullient.convert.StringUtil.joiningNonEmpty; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import dev.ebullient.convert.tools.Tags; + +/** A collection of traits stored as {@link QuteDataRef}s to the trait note. */ +public class QuteDataTraits implements Collection<QuteDataRef> { + private final Set<QuteDataRef> refs = new TreeSet<>(); + + public QuteDataTraits() {} + + /** The first trait which has this category, as a {@link QuteDataRef}. Case-sensitive. */ + public QuteDataRef getFirst(String category) { + return refs.stream().filter(ref -> ref.title() != null && ref.title().contains(category)).findFirst().orElse(null); + } + + /** Return these traits as a comma-delimited string without any extra formatting (eg no title attributes). */ + public String formattedWithoutTitles() { + return refs.stream().map(QuteDataRef::withoutTitle).collect(joiningNonEmpty(", ")); + } + + /** Return traits without any size, alignment, or rarity traits. */ + public QuteDataTraits getGenericTraits() { + return refs.stream() + .filter(ref -> !isPresent(ref.title()) || + !(ref.title().contains("Alignment") || ref.title().contains("Size") || ref.title().contains("Rarity"))) + .collect(Collectors.toCollection(QuteDataTraits::new)); + } + + public QuteDataTraits addToTags(Tags tags) { + for (QuteDataRef ref : this) { + tags.add("trait", ref.displayText()); + } + return this; + } + + public QuteDataTraits addTrait(String trait) { + if (isPresent(trait)) { + refs.add(new QuteDataRef(trait)); + } + return this; + } + + public QuteDataTraits addTraits(Collection<String> c) { + c.forEach(this::addTrait); + return this; + } + + @Override + public String toString() { + return join(", ", refs); + } + + @Override + public int size() { + return refs.size(); + } + + @Override + public boolean isEmpty() { + return refs.isEmpty(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof QuteDataRef) { + return refs.contains(o); + } else if (o instanceof String) { + return refs.stream().anyMatch(ref -> ref.displayText().equals(o)); + } + return false; + } + + @Override + public boolean remove(Object o) { + if (o instanceof QuteDataRef) { + return refs.remove(o); + } else if (o instanceof String) { + Optional<QuteDataRef> ref = refs.stream().filter(r -> r.displayText().equals(o)).findAny(); + if (ref.isEmpty()) { + return false; + } + return refs.remove(ref); + } + return false; + } + + @Override + public boolean add(QuteDataRef quteDataRef) { + return refs.add(quteDataRef); + } + + @Override + public Iterator<QuteDataRef> iterator() { + return refs.iterator(); + } + + @Override + public Object[] toArray() { + return refs.toArray(); + } + + @Override + public <T> T[] toArray(T[] a) { + return refs.toArray(a); + } + + @Override + public boolean containsAll(Collection<?> c) { + return c.stream().allMatch(this::contains); + } + + @Override + public boolean addAll(Collection<? extends QuteDataRef> c) { + return c.stream().allMatch(this::add); + } + + @Override + public boolean removeAll(Collection<?> c) { + return c.stream().allMatch(this::remove); + } + + @Override + public boolean retainAll(Collection<?> c) { + return refs.removeIf(ref -> !c.contains(ref) && !c.contains(ref.displayText())); + } + + @Override + public void clear() { + refs.clear(); + } +} diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteFeat.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteFeat.java index fc9f64fe0..1285281ee 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteFeat.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteFeat.java @@ -22,8 +22,8 @@ @TemplateData public class QuteFeat extends Pf2eQuteBase { - /** Collection of traits (decorated links) */ - public final Collection<String> traits; + /** Collection of traits (collection of {@link QuteDataRef}) */ + public final Collection<QuteDataRef> traits; /** Aliases for this note */ public final List<String> aliases; @@ -51,7 +51,7 @@ public class QuteFeat extends Pf2eQuteBase { public final boolean embedded; public QuteFeat(Pf2eSources sources, List<String> text, Tags tags, - Collection<String> traits, List<String> aliases, + Collection<QuteDataRef> traits, List<String> aliases, String level, String access, QuteDataFrequency frequency, QuteDataActivity activity, String trigger, String cost, String requirements, String prerequisites, String special, String note, List<String> leadsTo, boolean embedded) { diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteHazard.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteHazard.java index a869db30c..0035c1def 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteHazard.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteHazard.java @@ -26,8 +26,8 @@ @TemplateData public class QuteHazard extends Pf2eQuteBase { - /** Collection of traits (decorated links) */ - public final Collection<String> traits; + /** Collection of traits (collection of {@link QuteDataRef}) */ + public final Collection<QuteDataRef> traits; public final String level; public final String disable; @@ -83,7 +83,7 @@ public class QuteHazard extends Pf2eQuteBase { public final QuteDataGenericStat perception; public QuteHazard(Pf2eSources sources, List<String> text, Tags tags, - Collection<String> traits, String level, String disable, + Collection<QuteDataRef> traits, String level, String disable, String reset, String routine, QuteDataDefenses defenses, List<QuteInlineAttack> attacks, List<QuteAbility> abilities, List<QuteAbilityOrAffliction> actions, QuteHazardStealth stealth, QuteDataGenericStat perception) { @@ -102,7 +102,7 @@ public QuteHazard(Pf2eSources sources, List<String> text, Tags tags, } public String getComplexity() { - if (traits == null || traits.stream().noneMatch(t -> t.contains("complex"))) { + if (traits == null || traits.stream().noneMatch(ref -> ref.displayText().equalsIgnoreCase("complex"))) { return "Simple"; } return "Complex"; diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteInlineAttack.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteInlineAttack.java index 071c97558..be6796816 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteInlineAttack.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteInlineAttack.java @@ -1,7 +1,5 @@ package dev.ebullient.convert.tools.pf2e.qute; -import static dev.ebullient.convert.StringUtil.join; -import static dev.ebullient.convert.StringUtil.parenthesize; import static dev.ebullient.convert.StringUtil.toTitleCase; import java.util.Collection; @@ -46,8 +44,8 @@ public final class QuteInlineAttack implements QuteDataGenericStat, QuteUtil.Ren */ public final Collection<String> damageTypes; - /** Any traits associated with the attack (collection of decorated links) */ - public final Collection<String> traits; + /** Traits associated with the attack as a {@link QuteDataTraits} */ + public final QuteDataTraits traits; /** * Any additional effects associated with the attack e.g. grab (list of strings). Effects listed here @@ -66,7 +64,7 @@ public final class QuteInlineAttack implements QuteDataGenericStat, QuteUtil.Ren public QuteInlineAttack( String name, QuteDataActivity activity, AttackRangeType rangeType, Integer attackBonus, String damage, - Collection<String> damageTypes, Collection<String> traits, List<String> effects, String multilineEffect, + Collection<String> damageTypes, QuteDataTraits traits, List<String> effects, String multilineEffect, List<String> notes, JsonTextConverter<?> converter) { this.name = name; this.activity = activity; @@ -83,7 +81,7 @@ public QuteInlineAttack( public QuteInlineAttack( String name, QuteDataActivity activity, AttackRangeType rangeType, String damage, - Collection<String> damageTypes, Collection<String> traits, String note, + Collection<String> damageTypes, QuteDataTraits traits, String note, JsonTextConverter<?> converter) { this( name, activity, rangeType, null, @@ -108,7 +106,7 @@ public String template() { @Override public String render() { - return _converter.renderInlineTemplate(this, null); + return _converter.renderEmbeddedTemplate(this, null); } @Override @@ -116,11 +114,6 @@ public String toString() { return render(); } - /** Return traits formatted as a single string, e.g. {@code (agile, trip, finesse)} */ - public String formattedTraits() { - return parenthesize(join(", ", traits)); - } - public enum AttackRangeType { RANGED, MELEE; diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteItem.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteItem.java index e599d4fe6..5fb9bf53d 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteItem.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteItem.java @@ -1,5 +1,9 @@ package dev.ebullient.convert.tools.pf2e.qute; +import static dev.ebullient.convert.StringUtil.formatAsModifier; +import static dev.ebullient.convert.StringUtil.join; +import static dev.ebullient.convert.StringUtil.valueOrDefault; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -19,8 +23,8 @@ @TemplateData public class QuteItem extends Pf2eQuteBase { - /** Collection of traits (decorated links) */ - public final Collection<String> traits; + /** Collection of traits (collection of {@link QuteDataRef}) */ + public final Collection<QuteDataRef> traits; /** Aliases for this note */ public final List<String> aliases; /** @@ -66,7 +70,7 @@ public class QuteItem extends Pf2eQuteBase { public final List<QuteItemVariant> variants; public QuteItem(Pf2eSources sources, List<String> text, Tags tags, - Collection<String> traits, List<String> aliases, QuteItemActivate activate, + Collection<QuteDataRef> traits, List<String> aliases, QuteItemActivate activate, String price, String ammunition, String level, String onset, String access, String duration, String category, String group, String hands, Collection<NamedText> usage, Collection<NamedText> contract, @@ -167,41 +171,38 @@ public String toString() { } /** - * Pf2eTools item armor attributes + * Armor statistics + * <blockquote> + * <b>AC Bonus</b> +2; <b>Dex Cap</b> +0; <b>Check Penalty</b> -3; <b>Speed Penalty</b> -10 ft; <b>Strength</b> 14 + * </blockquote> * * This data object provides a default mechanism for creating * a marked up string based on the attributes that are present. * To use it, reference it directly: `{resource.armor}`. + * + * @param acBonus AC bonus granted by the armor as a {@link QuteDataArmorClass}. Never null. + * @param dexCap The dex modifier cap that applies while wearing this armor, or null. + * @param strengthReq The strength requirement to reduce some penalties while wearing the armor, or null. + * @param checkPenalty The penalty to Strength-and-Dex-based skill checks that apply if the strength requirement is not met. + * Integer, always negative or 0. + * @param speedPenalty The penalty to speed that applies when wearing this armor. Integer, always negative or 0. */ @TemplateData - public static class QuteItemArmorData implements QuteUtil { - /** {@link dev.ebullient.convert.tools.pf2e.qute.QuteDataArmorClass Item armor class details} */ - public QuteDataArmorClass ac; - /** Formatted string. Dex cap */ - public String dexCap; - /** Formatted string. Armor strength */ - public String strength; - /** Formatted string. Check penalty */ - public String checkPenalty; - /** Formatted string. Speed penalty */ - public String speedPenalty; - + public record QuteItemArmorData( + QuteDataArmorClass acBonus, + Integer dexCap, + Integer strengthReq, + int checkPenalty, + int speedPenalty + ) implements QuteUtil { + @Override public String toString() { - List<String> parts = new ArrayList<>(); - parts.add("**AC Bonus** " + ac.bonus()); - if (isPresent(dexCap)) { - parts.add("**Dex Cap** " + dexCap); - } - if (isPresent(strength)) { - parts.add("**Strength** " + strength); - } - if (isPresent(checkPenalty)) { - parts.add("**Check Penalty** " + checkPenalty); - } - if (isPresent(speedPenalty)) { - parts.add("**Speed Penalty** " + speedPenalty); - } - return "- " + String.join("; ", parts); + return join("; ", + "**AC Bonus** " + acBonus.bonus(), + "**Dex Cap** " + valueOrDefault(formatAsModifier(dexCap), "—"), + "**Check Penalty** " + (checkPenalty != 0 ? formatAsModifier(checkPenalty) : "—"), + "**Speed Penalty** " + (speedPenalty != 0 ? (formatAsModifier(speedPenalty) + " ft.") : "—"), + "**Strength** " + valueOrDefault(strengthReq, "—")); } } @@ -231,8 +232,8 @@ public String toString() { public static class QuteItemWeaponData implements QuteUtil { /** Formatted string. Weapon type */ public String type; - /** Formatted string. List of traits (links) */ - public Collection<String> traits; + /** Traits as a {@link QuteDataTraits} */ + public QuteDataTraits traits; public Collection<NamedText> ranged; public String damage; public String group; diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteRitual.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteRitual.java index 562aff6b8..4a57d0fd1 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteRitual.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteRitual.java @@ -23,8 +23,8 @@ public class QuteRitual extends Pf2eQuteBase { public final String level; /** Type: Ritual (usually) */ public final String ritualType; - /** Collection of traits (decorated links) */ - public final Collection<String> traits; + /** Collection of traits (collection of {@link QuteDataRef}) */ + public final Collection<QuteDataRef> traits; /** Aliases for this note */ public final List<String> aliases; @@ -44,7 +44,7 @@ public class QuteRitual extends Pf2eQuteBase { public final Collection<NamedText> heightened; public QuteRitual(Pf2eSources sources, List<String> text, Tags tags, - String level, String ritualType, Collection<String> traits, List<String> aliases, + String level, String ritualType, Collection<QuteDataRef> traits, List<String> aliases, QuteRitualCasting casting, QuteRitualChecks checks, QuteSpellTarget targeting, String requirements, String duration, Collection<NamedText> heightened) { super(sources, text, tags); diff --git a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteSpell.java b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteSpell.java index c53d30dc1..b0c2465ba 100644 --- a/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteSpell.java +++ b/src/main/java/dev/ebullient/convert/tools/pf2e/qute/QuteSpell.java @@ -26,8 +26,8 @@ public class QuteSpell extends Pf2eQuteBase { public final String level; /** Type: spell, cantrip, or focus */ public final String spellType; - /** Collection of traits (decorated links) */ - public final Collection<String> traits; + /** Collection of traits (collection of {@link QuteDataRef}) */ + public final Collection<QuteDataRef> traits; /** Aliases for this note */ public final List<String> aliases; /** @@ -71,7 +71,7 @@ public class QuteSpell extends Pf2eQuteBase { public final Collection<NamedText> heightened; public QuteSpell(Pf2eSources sources, List<String> text, Tags tags, - String level, String spellType, Collection<String> traits, List<String> aliases, + String level, String spellType, Collection<QuteDataRef> traits, List<String> aliases, QuteDataDuration castDuration, List<String> components, String cost, String trigger, String requirements, QuteSpellTarget targeting, QuteSpellSave save, QuteSpellDuration duration, List<String> domains, List<String> traditions, List<String> spellLists, diff --git a/src/main/resources/templates/toolsPf2e/inline-ability2md.txt b/src/main/resources/templates/toolsPf2e/inline-ability2md.txt index 1394f6edf..15d46b3db 100644 --- a/src/main/resources/templates/toolsPf2e/inline-ability2md.txt +++ b/src/main/resources/templates/toolsPf2e/inline-ability2md.txt @@ -1,6 +1,7 @@ +{#let componentsTraits = (resource.components.join(", ") + (resource.components && resource.traits ? " " : "") + (resource.traits ? (str:format("(%s)", resource.traits.formattedWithoutTitles)) : ""))} ```ad-embed-ability {#if resource.hasDetails } -title: **{#if resource.reference}{resource.reference}{#else}{resource.name}{/if}** {resource.activity}{resource.components.join(", ").prefixSpace}{#if resource.traits} ({resource.bareTraitList}){/if} +title: **{#if resource.reference}{resource.reference}{#else}{resource.name}{/if}** {resource.activity}{componentsTraits.prefixSpace} {#if resource.note} > [!pf2-note] > {resource.note} diff --git a/src/main/resources/templates/toolsPf2e/inline-attack2md.txt b/src/main/resources/templates/toolsPf2e/inline-attack2md.txt index 1c5ad5b56..a5d9b4a77 100644 --- a/src/main/resources/templates/toolsPf2e/inline-attack2md.txt +++ b/src/main/resources/templates/toolsPf2e/inline-attack2md.txt @@ -1,7 +1,7 @@ {#let r=resource} {#if r.multilineEffect} ```ad-inline-attack -title: {r.rangeType} {r.activity} {r.name.capitalized}{r.bonus.prefixSpace}{r.formattedTraits.prefixSpace} +title: {r.rangeType} {r.activity} {r.name.capitalized}{r.bonus.prefixSpace}{#if r.traits} ({r.traits}){/if} {#if r.damage} **Damage** {r.damage} {/if}{#if r.multilineEffect} @@ -9,5 +9,5 @@ title: {r.rangeType} {r.activity} {r.name.capitalized}{r.bonus.prefixSpace}{r.fo {/if} ``` {#else} -- **{r.rangeType}** {r.activity} {r.name}{r.bonus.prefixSpace}{r.formattedTraits.prefixSpace}{#if r.damage}, **Damage** {r.damage}{/if} +- **{r.rangeType}** {r.activity} {r.name}{r.bonus.prefixSpace}{#if r.traits} ({r.traits}){/if}{#if r.damage}, **Damage** {r.damage}{/if} {/if} diff --git a/src/main/resources/templates/toolsPf2e/item2md.txt b/src/main/resources/templates/toolsPf2e/item2md.txt index 48b04cbd7..d6dc04531 100644 --- a/src/main/resources/templates/toolsPf2e/item2md.txt +++ b/src/main/resources/templates/toolsPf2e/item2md.txt @@ -34,7 +34,7 @@ aliases: ["{resource.name}"{#each resource.aliases}, "{it}"{/each}] {/if}{#if resource.shield } {resource.shield} {/if}{#if resource.armor } -{resource.armor} +- {resource.armor} {/if}{#if resource.weapons } {#each resource.weapons}{it} {/each}{/if}{#if resource.hands } diff --git a/src/test/java/dev/ebullient/convert/tools/TokenizerTest.java b/src/test/java/dev/ebullient/convert/tools/TokenizerTest.java index dbb1b3a3f..d108b2b54 100644 --- a/src/test/java/dev/ebullient/convert/tools/TokenizerTest.java +++ b/src/test/java/dev/ebullient/convert/tools/TokenizerTest.java @@ -42,8 +42,8 @@ public String linkify(IndexType type, String s) { } @Override - public String replaceText(String s) { - throw new UnsupportedOperationException("Unimplemented method 'replaceText'"); + public String replaceTokenText(String s, boolean nested) { + throw new UnsupportedOperationException("Unimplemented method 'replaceTokenText'"); } @Override