HeroLab to Foundry - Powered by Abyssal Engine - v1.0.1 Public Alpha - The old hlo-importer is back, this time with Starfinder 2e
This is for [SF2e]|[PF2e]
Foundry package page: https://foundryvtt.com/packages/hlo-importer-abyssal-engine
Project page: https://shinra.cc/hlo-importer-abyssal-engine
Bug tracker: https://github.com/ShinraEngineer/hlo-importer-abyssal-engine/issues
Quick context for anyone who never used the original. From 2020 through September 2022, zarmstrong's hlo-importer was the way you got a Pathfinder 2e Hero Lab Online character into Foundry without retyping the whole sheet. It was a Foundry module that used a small PHP conversion service. However, it kinda died off about three years.
I play a lot of PF2e and SF2e. I have tons of Pathfinder Society characters and now have been accumulating some Starfinder Society character that I have been remaking in Pathbuilder or directly on Foundry for the that duration (well once it stopped working). I finally got tired of it and built the replacement. The Foundry registry submission for v1.0.0 is live as of last night, so this is the first release where you can install it the normal way: paste the manifest URL into Foundry's "Install Module" dialog, or browse for it once Foundry's review queue processes it.
This is alpha-1.0.1. It works. It is also new, and not perfect by any means...
What it does
Three input paths feed the same parser, and you pick whichever fits your table:
- Element token + user token (the API path). Same flow the old importer used, except the conversion happens inside the Foundry module instead of bouncing through a third-party PHP service. You paste the two tokens, the module hits
api.herolab.onlinedirectly, and you get a Foundry actor. - Paste JSON. Drag the Hero Lab Online portfolio JSON into a textarea. Useful if you already exported a sheet, or if a GM hands you a JSON file in Discord.
- Upload JSON file. Same as above but with a file picker. Thin variant on the paste path.
All three end up in the same place: an Abyssal-Engine Import button on every PF2e and SF2e character sheet, theme-aware so it does not look broken on dark vs light sheets. Re-import overwrites in place rather than spawning a duplicate actor.
Starfinder 2e is supported on day one
The old importer was PF2e only (obviously). SF2e did not exist yet, and by the time SF2e shipped the importer was already dead. This one supports both systems from the same codebase. The parser auto-detects which system your portfolio is and routes accordingly. Operative, Technomancer, Mystic, Solarian, Envoy, Witchwarper, augmentations, batteries, expert/master role tracks - all wired.
If you only play PF2e, this is functionally a replacement for the old importer. If you play SF2e, this is the first import path that exists for you.
How matching works
This is the part that took the most work and created the most headaches, so it is worth describing. PF2e and SF2e on Foundry ship hundreds of compendium packs full of canonical items. The old importer's matching strategy was programming search match called "fuzzy name comparison", which gave you a lot of "close enough" wrong matches (the infamous "Strike with the wrong weapon profile" problem).
The new pipeline is as such:
- Deterministic pack-index lookup. At build time the module walks every compendium and indexes every item by canonical slug. At import time, parsed items get a slug derived from their HLO name and stat block, and that slug hits the index directly. No fuzzy guessing for the 90% case.
- Curated overrides. Items that cannot be slug-matched (Missed Remaster renames, pattern-collisions like "Scorching Ray" vs "Scorching Rays") live in a hand-maintained override table. The override table got a 20-agent audit pass in v0.6.13 covering 67 entries. It's not perfect and everytime I think I got it, I find a handful of new ones. HOWEVER, I believe I have achieved 90% success on import items.
- Fuzzy fallback, with a confidence threshold and an end-of-import "unmatched items" modal that lists everything the matcher could not resolve so you know exactly what to fix by hand. You can adjust this in the module settings, but I have found that 80%~85% is what has been working for me.
Runes, materials, and the precious-material weapon/armor combos all converge through a single rune-extraction pass that writes to the parent item's system.runes and system.material fields. Standalone Rune and Material items from HLO get parsed into apply-to-parent patches that merge with weapon/armor names. So a Numerian Steel Breastplate with a separate +1 Resilient + Greater Fire Resistant rune chain lands as one armor with the correct three runes attached, not four floating items.
PFS / SFS support
Two things that the old importer never had:
Faction reputation lands on the sheet. PFS factions (Envoy's Alliance, Grand Archive, Horizon Hunters, Vigilant Seal, Radiant Oath, Verdant Wheel) flow into actor.system.pfs.reputation directly. You import a PFS character and the PFS tab is populated.
Boons come in as custom homebrew feat items in the right category. Paizo publishes hundreds of PFS boons. The PF2e and SF2e compendiums between them have maybe twenty. I am not going to hold my breath that the rest of the new ones will get added, so these ones get translated from HLO's JSON ID Fields. They show up in your boons tab, they count, and they survive re-import. Most of the time.
Other small things
- The
Abyssal-Engine Importbutton is in the sheet header. - Two-phase actor create. The actor is created with the right name and class first, then items get bulk-attached. This works around a PF2e quirk where
Actor.createflipskeepId=truefor the entire batch and you lose stable IDs if you do it all at once. - Wizard silencer. If you import a PF2e Wizard, the system normally pops a
PickAThingPromptfor every grant. The module monkey-patches that prompt during the import window so you do not get fourteen modal dialogs in a row for one character. You can turn this off in settings if you prefer to see them. However, I have not tested that very thoroughly as I don't like it asking me. - SF2e grant resolver. The SF2e wizard cascade is even more strict than PF2e. The resolver pre-fills
ChoiceSetselections and writesitemGrantschains directly, bypassing the cascade entirely. - End-of-import unmatched modal. Whatever the matcher could not resolve gets listed so you can deal with it intentionally instead of finding it later.
- Conversion log export. Every import writes a diagnostic log you can ship to me when something goes wrong.
Tested against
Two characters of mine, one for each system, both verified end-to-end in Foundry V14:
- Noxora, Son of Nebula - SFS L1/L4 Operative. SF2e side, 100% clean.
- Berial Jeggare (PFS) - PFS Wizard. PF2e side, 100% clean including all PFS boons.
Plus four additional PF2e test fixtures I have been beating on throughout development (across a range of levels up to 20).
V14 (14.361) is the primary verification target. V13 compatibility is preserved but secondary - if you are still on V13, it should work, but file an issue if it does not.
Links
- Foundry package page: https://foundryvtt.com/packages/hlo-importer-abyssal-engine
- Project page: https://shinra.cc/hlo-importer-abyssal-engine
- Manifest URL: https://shinra.cc/hlo-importer-abyssal-engine/module.json
- Bug tracker: https://github.com/ShinraEngineer/hlo-importer-abyssal-engine/issues
Bug tracker: https://github.com/ShinraEngineer/hlo-importer-abyssal-engine/issues
Bug report workflow (please read this if anything imports wrong)
If you hit something the importer gets wrong, the dialog has buttons for everything I need. Run them in this order. Each step produces a file (or a chunk of text) you attach to the GitHub issue. You do not need to touch Index Dumps - that panel is a maintainer tool for auditing Foundry's compendium ID fields and is not relevant to a bug report.
- Test API Connection. (Token mode only.) Confirms the importer can reach
api.herolab.onlinewith your user token before it tries to fetch a character. Catches token expiry and HLO API outages so we do not chase a phantom bug. - Fetch and Import (or Import in paste/upload modes). Run the actual import that produces the bad result. Let it finish, do not close the dialog.
- Copy override stubs. This button only appears when the matcher could not resolve one or more items. Click it, paste the result into the issue. It tells me exactly which HLO items the matcher missed and gives me drop-in entries for the override table.
- Export pack index. Writes a JSON file describing every Foundry compendium your install can see (PF2e + SF2e + any system packs you have enabled). I need this because Foundry compendium content shifts between system versions and modules; the index pins down exactly what was available on YOUR install when the bad import happened. Attach the resulting .json to the issue.
- Download log. Writes the full conversion log (the same content you see in the dialog) to a .txt file. This is the single most important attachment. It records every parser decision, every matcher hit and miss, and the exact failure point. Attach the resulting .txt to the issue.
File the issue at https://github.com/ShinraEngineer/hlo-importer-abyssal-engine/issues. If you can, also attach the HLO portfolio JSON Download (it's above the element and user tokens) and the Foundry Character JSON Export as well, so I can compare them.
That five-step bundle gets me 95% of what I need to reproduce and fix the bug.
If something is broken, tell me. If something should exist and doesn't, tell me that too. This module exists because the previous one stopped getting maintained and a bunch of us got tired of pasting characters by hand. Every feature in it from here forward will exist because someone spoke up. PLEASE DO THIS, YOU ARE NOT ANNOYING ME, I WILL REPLY TO EVERYONE.
Especially: if you import a character and even one item lands wrong, attach the HLO portfolio JSON (or the element token) and the conversion log to a GitHub issue. I cannot fix what I have not seen fail. The fastest way to smooth the rough edges on this will be your reports!