ebullient / ttrpg-convert-cli

Utility to convert JSON data (for content you own) from 5etools or pf2etools into Obsidian-friendly Markdown.
https://www.ebullient.dev/projects/ttrpg-convert-cli/
Apache License 2.0
190 stars 37 forks source link

No support for `entryStatblockInline` and `entryStatblock` entry types #164

Closed revilowaldow closed 1 year ago

revilowaldow commented 1 year ago

Currently the converter does not support the entryStatblockInline and entryStatblock entry types. image

These are used to transclude or inline individual pieces of content such as tables, items or creatures into the text of books or adventures (or any other entry field).

There are 4 types of concern;

Examples;

All of these examples are featured at the bottom of the renderer demo page, where you can also see how they output: https://5etools-mirror-1.github.io/renderdemo.html

statblock

{
    "type": "statblock",
    "tag": "creature",
    "name": "Goblin", // Note lack of source key as MM is assumed as default for creatures, similar to {@creature Goblin}
    "style": "inset" // Whether this is shown in a shaded box or a transparent box, it's in a box either way
}

prop statblock

{
    "type": "statblock",
    "prop": "subclass", // tag has changed to prop and additional fields are now required
    "source": "PHB",
    "name": "Wild",
    "displayName": "Wild Magic Sorcerer", // Optional heading field for collapsed site display that would otherwise show the `name` key
    "className": "Sorcerer",
    "classSource": "PHB",
    "collapsed": true
}

statblockInline

{
    "type": "statblockInline",
    "collapsed": true,
    "dataType": "spell", // Type of data to expect
    "data": { // The raw data to render
        "name": "Fireball",
        "source": "PHB",
        "level": 3,
        "school": "V",
        "time": [
            {
                "number": 1,
                "unit": "action"
            }
        ],
        "range": {
            "type": "point",
            "distance": {
                "type": "feet",
                "amount": 150
            }
        },
        "components": {
            "v": true,
            "s": true,
            "m": "a tiny ball of bat guano and sulfur"
        },
        "duration": [
            {
                "type": "instant"
            }
        ],
        "entries": [
            "A bright streak flashes from your pointing finger to a point you choose within range and then blossoms with a low roar into an explosion of flame. Each creature in a 20-foot-radius sphere centered on that point must make a Dexterity saving throw. A target takes {@dice 8d6} fire damage on a failed save, or half as much damage on a successful one.",
            "The fire spreads around corners. It ignites flammable objects in the area that aren't being worn or carried."
        ]
    }
}

statblockInline featuring _copy

{
    "type": "statblockInline",
    "dataType": "monster",
    "dependencies": [ // Dependency field similar to `_meta`, note that 'internal copies' must also state the source in this array
        "MM"
    ],
    "data": {
        "name": "Ancient Red Dragon (Weakened)",
        "source": "MM",
        "_copy": {
            "name": "Ancient Red Dragon",
            "source": "MM"
        },
        "str": 3,
        "con": 3
    }
}
ebullient commented 1 year ago

So I haven't fully bitten this off w/ Pf2eTools, which has a similar practice.

I have questions (which I will try to do a better job of articulating this time around) =)

revilowaldow commented 1 year ago
  • Is "style" anything other than "inset"?

Nahh, just inset for now, It's helpful for when a book displays e.g. a rule as a sidebar in print, then you can make it look like that without having to wrap it in a box. The colours are the only difference: image

  • A prop statblock feels the same as the above, but for non-creatures.

Incorrect, the method above is used for everything except subclasses which need their own handling through the prop method. Exclusively the types of content in this enum can be referenced by statblock normally. And exclusively the types of content in this other enum require the prop method.

  • "type": "statblockInline" -- pf2eTools uses a lot of this, and it's mostly fine, I am just not sure how to ensure this is findable. Taking the example: should the document containing this be treated as a spell reference? Should it have spell tags added to it? Is it be cross-referenceable as a spell variant {@spell ... }?

Content created in this way should not be searchable/indexed, it is created inline and intended to be read as part of the text that surrounds it. It's typically used for examples or for reference, you can think of this as not true mechanical content, but just more fluff/explanatory text if that helps? An example might be the 'Warhammer Master' feat, it's intended as an example of a bad design and should not be used for play: This shouldn't be searchable, but is important to the surrounding text. image

  • statblockInline featuring _copy -- The obsidian statblock plugin has syntax for exactly this, if you use it (so that would be easier). I'd probably have to do a separate embedded templates for those who use the plugin and those who don't. Same questions apply here: should the document containing this be treated as a new creature? Should it have creature tags added to it? Is it be cross-referenceable as a creature variant {@creature ... }?

As above this content should not be referenced/searchable.

I'm not sure about the statblock plugin, but the intent of this is to modify a pre-existing statblock in some way, for example I have used this to remove the legendary actions from a dragon. This means that you can read through the legendary group information once and then read through the dragons without seeing duplicate information multiple times.

Ultimately for me and many other people, the main storage location for the data is the homebrew repo, this lets me use it with the site, with Foundry VTT, Roll20 and now with Obsidian. This is how the data is formatted for use with that tool, if I was writing it exclusively for use with obsidian I might favour the extension's method.

{
    "type": "statblock",
    "tag": "legroup",       // Reference the legendary group information for this type of dragon and display it
    "source": "GHMG",
    "name": "Blightscale Dragon"
},
{
    "type": "statblockInline",
    "dataType": "monster",
    "dependencies": [
        "GHMG"
    ],
    "data": {
        "name": "Ancient Blightscale Dragon\u00A0",
        "source": "GHMG",
        "_copy": {
            "name": "Ancient Blightscale Dragon",
            "source": "GHMG"
        },
        "legendary": null       // Display the ancient dragon without the legendary actions
    }
},
{
    "type": "statblockInline",
    "dataType": "monster",
    "dependencies": [
        "GHMG"
    ],
    "data": {
        "name": "Adult Blightscale Dragon\u00A0",
        "source": "GHMG",
        "_copy": {
            "name": "Adult Blightscale Dragon",
            "source": "GHMG"
        },
        "legendary": null       // Display the adult dragon without the legendary actions
    }
},
{
    "type": "statblock",
    "tag": "creature",
    "source": "GHMG",
    "name": "Young Blightscale Dragon"  // Young dragons do not have legendary actions so we display normally
},
{
    "type": "statblock",
    "tag": "creature",
    "source": "GHMG",
    "name": "Blightscale Dragon Wyrmling"   // Wyrmlings as per young dragons
}
ebullient commented 1 year ago

As above this content should not be referenced/searchable.

I'm not sure about the statblock plugin, but the intent of this is to modify a pre-existing statblock in some way, for example I have used this to remove the legendary actions from a dragon. This means that you can read through the legendary group information once and then read through the dragons without seeing duplicate information multiple times.

Right: someone may want to add this monster variant to an encounter via initiative tracker.. which is what would trigger the indexable version (I think).



{

  "type": "statblock",

  "tag": "legroup",       // Reference the legendary group information for this type of dragon and display it

  "source": "GHMG",

  "name": "Blightscale Dragon"

},

This is easy, as a ![]() embed of the referenced type.

{

"type": "statblockInline",

"dataType": "monster",

"dependencies": [

  "GHMG"

],

"data": {

  "name": "Ancient Blightscale Dragon\u00A0",

  "source": "GHMG",

  "_copy": {

      "name": "Ancient Blightscale Dragon",

      "source": "GHMG"

  },

  "legendary": null       // Display the ancient dragon without the legendary actions

}

},

This is tricky, as it wouldn't show up as a creature that could be added to an encounter. I could try to create an encounter right here w/ modified stats, but depending on where it is in the text, there could be multiple monsters that should be included.

{ "type": "statblockInline", "dataType": "monster", "dependencies": [ "GHMG" ], "data": { "name": "Adult Blightscale Dragon\u00A0", "source": "GHMG", "_copy": { "name": "Adult Blightscale Dragon", "source": "GHMG" }, "legendary": null // Display the adult dragon without the legendary actions } },

Not sure I'm checking for dependencies as included sources, either.

revilowaldow commented 1 year ago

Again, as I've said previously, content that is modified inline is explicitly for display only. It is to aid rules comprehension, or to modify how it looks when information would be needlessly repeated by multiple inlined blocks in quick succession.

Nobody wants to add those to an encounter, you have hypothesised a situation that does not exist. They are only for display purposes. There is no need to make them referencable. The original source of the modified data remains the authoritative source.

ebullient commented 1 year ago

I frequently expand referenced content in place, to make each note standalone. I could approach this differently, and make more atomic notes, but the reused sections are not labeled/identified in a way that facilitates that.

In the main 5etools source, NPCs are effectively copies, but those are treated as unique creatures. Isn't an inline statblock similar to the standalone npc beastie for homebrew? Perhaps I should treat these as individual beasties if a bestiary entry doesn't already exist. The trick is finding them...

Have you used the initiative tracker plugin?