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
178 stars 37 forks source link

5eTools Homebrew JSON Support #111

Closed revilowaldow closed 1 year ago

revilowaldow commented 1 year ago

5eTools has a well established homebrew community hosted: https://github.com/TheGiddyLimit/homebrew This includes personal small homebrews and larger format 3rd party sources converted at similar quality to the main site but not from WotC.

The data structure is slightly different in that rather than property specific files, the homebrew repository hosts standalone files with multiple properties stored together following the following top level schema where the source information is also defined: https://github.com/TheGiddyLimit/5etools-utils/blob/master/schema/brew/homebrew.json

Add support for importing homebrew files standalone, or paired with main site content.

mclearc commented 1 year ago

it would be fabulous for me if the cli could read homebrew. I don't have a much experience with this javascript stack/workflow, but if there are things I could do to help make this happen please let me know.

ebullient commented 1 year ago

what I don't know (off the top) is where the homebrew json deviates from what the site uses. When I glance at homebrew.json... the deltas don't jump out at me.

Is it just initial discovery/structure? What doesn't work if you added that json to the path for reading?

That's what would help me best.. finding places that don't align.

mclearc commented 1 year ago

For example, here's a subclass I made for the wizard.

{
  "$schema": "https://raw.githubusercontent.com/TheGiddyLimit/5etools-utils/master/schema/brew-fast/homebrew.json",
  "_meta": {
    "sources": [
      {
        "json": "Kinemancer",
        "abbreviation": "EE",
        "full": "Kinemancer",
        "authors": [
          "LichLife"
        ],
        "version": "1.0",
        "targetSchema": "1.0"
      }
    ],
    "dateAdded": 0,
    "dateLastModified": 0
  },
  "subclass": [
    {
      "name": "Kinemancer",
      "source": "Kinemancer",
      "className": "Wizard",
      "classSource": "PHB",
      "shortName": "Kinemancer",
      "subclassFeatures": [
        "Kinemancer|Wizard|PHB|Kinemancer|Kinemancer|2|Kinemancer",
        "Forceful Movement|Wizard|PHB|Kinemancer|Kinemancer|2|Kinemancer",
        "Dynamic Step|Wizard|PHB|Kinemancer|Kinemancer|2|Kinemancer",
        "Shape Force|Wizard|PHB|Kinemancer|Kinemancer|2|Kinemancer",
        "Forceful Spellcasting|Wizard|PHB|Kinemancer|Kinemancer|6|Kinemancer",
        "Field of Force|Wizard|PHB|Kinemancer|Kinemancer|10|Kinemancer",
        "Singularity|Wizard|PHB|Kinemancer|Kinemancer|14|Kinemancer"
      ]
    }
  ],
  "subclassFeature": [
    {
      "name": "Kinemancer",
      "source": "Kinemancer",
      "className": "Wizard",
      "classSource": "PHB",
      "subclassShortName": "Kinemancer",
      "subclassSource": "Kinemancer",
      "level": 2,
      "entries": [
        "Kinemancers are experts in manipulating attractive and repulsive force. They can shape and bend forces both within and without an object, causing objects to float, fall, or even orbit around them. Their powers allow them to control the battlefield, manipulating the movement of their enemies and allies alike. They also play an integral role on Ermis in settlement and construction."
      ]
    },
    {
      "name": "Forceful Movement",
      "source": "Kinemancer",
      "className": "Wizard",
      "classSource": "PHB",
      "subclassShortName": "Kinemancer",
      "subclassSource": "Kinemancer",
      "level": 2,
      "entries": [
        "Your ability to control attractive and repulsive forces manifests itself in your physical activities. Any ability check that would normally rely on Strength can use Intelligence instead. You also gain proficiency in Athletics; use your Intelligence for all proficiency checks with this skill."
      ]
    },
    {
      "name": "Dynamic Step",
      "source": "Kinemancer",
      "className": "Wizard",
      "classSource": "PHB",
      "subclassShortName": "Kinemancer",
      "subclassSource": "Kinemancer",
      "level": 2,
      "entries": [
        "You have the ability to dynamically vary the forces affecting you as you move. Until the end of your next turn, you can move through difficult terrain as if it were normal terrain, and you gain a climbing speed equal to your walking speed. Additionally, you can jump up to your Intelligence modifier (minimum of 1) times your normal jump distance. You may use this a number of times equal to your proficiency bonus before needing to take a short or long rest."
      ]
    },
    {
      "name": "Shape Force",
      "source": "Kinemancer",
      "className": "Wizard",
      "classSource": "PHB",
      "subclassShortName": "Kinemancer",
      "subclassSource": "Kinemancer",
      "level": 2,
      "entries": [
        "You can create pockets of relative safety within the effects of your kinemancy or force-related spells. When you cast a kinemancy or any force-related spell that affects other creatures that you can see, you can choose a number of them equal to 1 + the spell's level. The chosen creatures automatically succeed on their saving throws against the spell, and they take no force damage if they would normally take half damage on a successful save."
      ]
    },
    {
      "name": "Forceful Spellcasting",
      "source": "Kinemancer",
      "className": "Wizard",
      "classSource": "PHB",
      "subclassShortName": "Kinemancer",
      "subclassSource": "Kinemancer",
      "level": 6,
      "entries": [
        "All force-related spells receive a bonus to damage equal to your Intelligence modifier. Additionally, any spell attack that targets one creature can now force them to make a strength roll against your spell DC to either be pushed or pulled up to 5 feet per spell level. At level 15, this can also be applied to area-of-effect spells."
      ]
    },
    {
      "name": "Field of Force",
      "source": "Kinemancer",
      "className": "Wizard",
      "classSource": "PHB",
      "subclassShortName": "Kinemancer",
      "subclassSource": "Kinemancer",
      "level": 10,
      "entries": [
        "You gain the at-will ability to manipulate fields of force as specified by the spell Wall of Force. You also have a +2 bonus to saving throws for maintaining concentration for this spell. Once you use this ability, you can't do so again unless you expend a spell slot of any level equal to or greater than 1 (higher level slots have no extra effect), or until you finish a long rest."
      ]
    },
    {
      "name": "Singularity",
      "source": "Kinemancer",
      "className": "Wizard",
      "classSource": "PHB",
      "subclassShortName": "Kinemancer",
      "subclassSource": "Kinemancer",
      "level": 14,
      "entries": [
        "As an action, you can create a singular point of attractive force at a location you can see within 120 feet of you, and which lasts for 1 minute or until you lose concentration. The area becomes difficult terrain for creatures other than you (and any creatures you specify via Shape Force). Any creature that starts its turn within 20 feet of the singularity must make a Strength saving throw against your spell save DC. On a failed save, it takes 2d10 force damage and is pulled 10 feet towards the singularity. Additionally, its speed is halved until the start of its next turn, and ranged weapon attacks made into or out of the area are made at disadvantage. On a successful save, it takes half as much damage and isn't pulled. However, it still suffers the effects of difficult terrain and disadvantage on ranged attacks. Once you use this ability, you can't do so again until you finish a long rest."
      ]
    }
  ]
}

I think you can safely ignore the "schema" portion, as that is largely for error checking. The formatting follows the rules from here: https://wiki.tercept.net/en/Homebrew/TableOfReference/Subclass.

How would I go about testing this as an additional source? I assume I'd have to define it as a source somewhere and then refer to it in my config?

ebullient commented 1 year ago

yes. Just include it on the command line (along w/ any other sources), and I think add EE to your source list..

AH!! the part that I would be missing is processing the _meta/sources (the glue that would allow an opt-in for that source).

So. as a test... if you include all sources, AND add this file to the command line alongside everything else... this subclass should show up. is that true?

mclearc commented 1 year ago

I tried the following with both "EE" as a source and "Kinemancer" as a source. In both cases I got "🔸 Source Kinemancer is unknown". The tool didn't return anything at all for the first, and it returned the following (in a file called wizard-kinemancer-kinemancer.md) for the second:

---
obsidianUIMode: preview
cssclass: json5e-class
tags:
- compendium/src/5e/kinemancer
- class/wizard/kinemancer
aliases: ["Kinemancer"]
---
# Kinemancer
*Wizard: Arcane Tradition*  
*Source: Kinemancer*  

## Class Features
ebullient commented 1 year ago

Above was full content? I think I know why.

Did it work if you just included that file and used ALL sources? I would expect a wizard-kinemancer-kinemancer.md file (possibly) for that case. Should also have more content..

(structure should match, but source mapping is missing)

mclearc commented 1 year ago

Using:

java -jar /Users/roambot/bin/ttrpg-convert-cli/target/ttrpg-convert-cli-199-SNAPSHOT-runner.jar \
-s ALL \
-o '/Users/roambot/Documents/DocLibrary/Games/Dungeons & Dragons/D&D 5th Edition/5e-cli-compendium.nosync/DnD-cli-out' \
'/Users/roambot/Documents/DocLibrary/Games/Dungeons & Dragons/D&D 5th Edition/5etools-mirror.nosync/data' '/Users/roambot/fvtt-dir/Data/5e-homebrew/kinemancer.json'

I still got the incomplete md file.

revilowaldow commented 1 year ago

what I don't know (off the top) is where the homebrew json deviates from what the site uses. When I glance at homebrew.json... the deltas don't jump out at me. That's what would help me best.. finding places that don't align.

Whereas the site groups files by content type, homebrew keeps all materials relating to a source (dependencies aside) in a single file. The difference between the actual data can be found here: https://github.com/TheGiddyLimit/5etools-utils/tree/master/schema-template There's a 'proto-schema' with some custom syntax that gets built out to the versions in: https://github.com/TheGiddyLimit/5etools-utils/tree/master/schema (Don;t worry about the -fast ones, that's unimportant.)

Searching for the schema differences using the stuff detailed in this readme would let you identify differences in the content itself, but 95% of getting this working is making whatever your handling is for files accept standalone homebrew files. https://github.com/TheGiddyLimit/5etools-utils/blob/master/schema-template/README.md

ebullient commented 1 year ago

I'll try w/ your json and any other favorites you want to feed me. ;)

revilowaldow commented 1 year ago

See the "third party high quality conversion" link in the first comment of this issue for a great example. Lots of dependencies and copying etc declared in the _meta.

ebullient commented 1 year ago

y.. I surf meta for copies etc (on the 5e side.. I've not gone down that rathole yet for pf2e). just not for homebrew source mapping, etc.

ebullient commented 1 year ago

Broadly resolved by ceb0551021209118202db91e594157d377dfe4b1.

Whatever we find that isn't quite right should be a new issue..

revilowaldow commented 1 year ago

Apologies, what would an example command be to process just the content of a single homebrew file?

mclearc commented 1 year ago

@ebullient you are an absolute legend! 🎊 🥳

ebullient commented 1 year ago

Add either of the linked json in your original post to the command line (as you would any the book or adventure json that aren't included in the general scoop):

The id you need to use to include that content is the json id in _meta/sources.

An approximate example (buried in a test) is here: https://github.com/ebullient/ttrpg-convert-cli/blob/af036863fc04e7b942e8df349e4962c2e0a59ac4/src/test/java/dev/ebullient/convert/RpgDataConvertTest.java#L151

Note many of those parameters can be put in your config json, too. I'm just lazy and didn't want to make another config file.