From 162bbde1fe114fb7d023d1a43582e8a00f735d40 Mon Sep 17 00:00:00 2001 From: Tim Evans Date: Wed, 4 Mar 2026 14:21:21 +0000 Subject: [PATCH] Initial Ringworld system publish with companion content module and starter world data --- .gitignore | 3 + README.md | 113 + assets/icons/armor.svg | 5 + assets/icons/armor/arm-bracers.svg | 7 + assets/icons/armor/armored-coat.svg | 7 + assets/icons/armor/cloth-vest.svg | 7 + assets/icons/armor/combat-harness.svg | 7 + assets/icons/armor/combat-helmet.svg | 7 + assets/icons/armor/full-combat-suit.svg | 7 + assets/icons/armor/impact-suit.svg | 7 + assets/icons/armor/leg-guards.svg | 7 + assets/icons/armor/soft-combat-vest.svg | 7 + assets/icons/gear.svg | 5 + assets/icons/gear/air-reprocessor.svg | 7 + assets/icons/gear/autodoc-portable.svg | 7 + assets/icons/gear/bioscanner.svg | 7 + assets/icons/gear/field-toolkit.svg | 7 + assets/icons/gear/holo-projector.svg | 7 + assets/icons/gear/medkit.svg | 7 + assets/icons/gear/memory-bubble-reader.svg | 7 + assets/icons/gear/micromind-unit.svg | 7 + assets/icons/gear/portable-generator.svg | 7 + assets/icons/gear/power-pack-r100.svg | 7 + assets/icons/gear/power-pack-r20.svg | 7 + assets/icons/gear/power-pack-r50.svg | 7 + assets/icons/gear/reactionless-flycycle.svg | 7 + assets/icons/gear/signal-beacon.svg | 7 + assets/icons/gear/stasis-box.svg | 7 + assets/icons/gear/survey-scanner.svg | 7 + assets/icons/gear/translator-module.svg | 7 + assets/icons/gear/vac-suit.svg | 7 + assets/icons/power-pack.svg | 6 + assets/icons/pursuit.svg | 7 + assets/icons/pursuit/artist.svg | 8 + assets/icons/pursuit/colonist.svg | 8 + assets/icons/pursuit/criminal.svg | 8 + assets/icons/pursuit/dilettante.svg | 8 + assets/icons/pursuit/engineer.svg | 8 + assets/icons/pursuit/entertainer.svg | 8 + assets/icons/pursuit/explorer.svg | 8 + assets/icons/pursuit/functionary.svg | 8 + assets/icons/pursuit/journalist-writer.svg | 8 + assets/icons/pursuit/miner.svg | 8 + assets/icons/pursuit/pilot.svg | 8 + assets/icons/pursuit/police-officer.svg | 8 + assets/icons/pursuit/politician.svg | 8 + assets/icons/pursuit/prisoner.svg | 8 + assets/icons/pursuit/scholar.svg | 8 + .../icons/pursuit/scientific-researcher.svg | 8 + assets/icons/pursuit/soldier.svg | 8 + assets/icons/pursuit/zealot.svg | 8 + assets/icons/skill.svg | 7 + assets/icons/skill/anthropology.svg | 8 + assets/icons/skill/aquatic-vehicle.svg | 8 + assets/icons/skill/archaic-melee-weapon.svg | 8 + assets/icons/skill/archaic-ranged-weapons.svg | 8 + assets/icons/skill/astronomy.svg | 8 + assets/icons/skill/athletics.svg | 8 + assets/icons/skill/atmospheric-craft.svg | 8 + assets/icons/skill/bargain.svg | 8 + assets/icons/skill/biology.svg | 8 + assets/icons/skill/botany.svg | 8 + assets/icons/skill/chemistry.svg | 8 + assets/icons/skill/computers.svg | 8 + assets/icons/skill/debate.svg | 8 + assets/icons/skill/emergency-treatment.svg | 8 + assets/icons/skill/engineering.svg | 8 + assets/icons/skill/farming.svg | 8 + assets/icons/skill/fast-talk.svg | 8 + assets/icons/skill/fine-arts.svg | 8 + assets/icons/skill/ground-vehicle.svg | 8 + assets/icons/skill/handgun-energy.svg | 8 + assets/icons/skill/handgun-projectile.svg | 8 + assets/icons/skill/heavy-weapon-energy.svg | 8 + .../icons/skill/heavy-weapon-projectile.svg | 8 + assets/icons/skill/hide.svg | 8 + assets/icons/skill/history.svg | 8 + assets/icons/skill/hyperdrive.svg | 8 + assets/icons/skill/law.svg | 8 + assets/icons/skill/listen.svg | 8 + assets/icons/skill/mathematics.svg | 8 + assets/icons/skill/musicianship.svg | 8 + assets/icons/skill/observe.svg | 8 + assets/icons/skill/orate.svg | 8 + assets/icons/skill/own-language.svg | 8 + assets/icons/skill/perform.svg | 8 + assets/icons/skill/personal-flyer.svg | 8 + assets/icons/skill/physics.svg | 8 + assets/icons/skill/planetology.svg | 8 + assets/icons/skill/psychology.svg | 8 + assets/icons/skill/reaction-drive.svg | 8 + assets/icons/skill/reactionless-drive.svg | 8 + assets/icons/skill/repair.svg | 8 + assets/icons/skill/ringworld.svg | 8 + assets/icons/skill/scent.svg | 8 + assets/icons/skill/search.svg | 8 + assets/icons/skill/second-languages.svg | 8 + assets/icons/skill/sneak.svg | 8 + assets/icons/skill/strategy.svg | 8 + assets/icons/skill/theology.svg | 8 + assets/icons/skill/track.svg | 8 + assets/icons/skill/unarmed-combat.svg | 8 + assets/icons/skill/v-sword-f-laser.svg | 8 + assets/icons/skill/weapons-system.svg | 8 + assets/icons/skill/zoology.svg | 8 + assets/icons/species.svg | 5 + assets/icons/species/bandersnatchi.svg | 7 + assets/icons/species/boat-people.svg | 7 + assets/icons/species/builder.svg | 7 + assets/icons/species/dolphin.svg | 7 + assets/icons/species/earth-human.svg | 7 + assets/icons/species/ghoul.svg | 7 + assets/icons/species/human.svg | 7 + assets/icons/species/kzinti.svg | 7 + assets/icons/species/martian.svg | 7 + assets/icons/species/pierin.svg | 7 + assets/icons/species/pierson-s-puppeteer.svg | 7 + assets/icons/species/trinoc.svg | 7 + assets/icons/weapon-melee.svg | 6 + assets/icons/weapon-ranged.svg | 7 + assets/icons/weapon/flashlight-laser.svg | 7 + assets/icons/weapon/handgun-projectile.svg | 7 + assets/icons/weapon/laser-pistol.svg | 7 + assets/icons/weapon/laser-rifle.svg | 7 + assets/icons/weapon/monofilament-knife.svg | 7 + assets/icons/weapon/rifle-projectile.svg | 7 + assets/icons/weapon/shock-baton.svg | 7 + assets/icons/weapon/slug-thrower-carbine.svg | 7 + assets/icons/weapon/stunner.svg | 7 + assets/icons/weapon/variable-sword.svg | 7 + dist/ringworld-content.zip | Bin 0 -> 2054 bytes dist/ringworld-starter-world.zip | Bin 0 -> 983 bytes dist/ringworld-system.zip | Bin 0 -> 197503 bytes lang/en.json | 5 + module/ringworld.mjs | 3335 +++++++++++++++++ module/ringworld.mjs.map | 1 + package-lock.json | 2755 ++++++++++++++ package.json | 18 + resources/hit-locations/humanoid.json | 14 + ringworld-content/README.md | 22 + ringworld-content/module.json | 33 + .../module/ringworld-content.mjs | 75 + seeds/base-skills.json | 59 + seeds/example-explorer.json | 87 + seeds/example-items.json | 74 + src/module/actor/RingworldActor.ts | 335 ++ src/module/character-tools.ts | 178 + src/module/chat.ts | 77 + src/module/constants.ts | 37 + src/module/data-base-skills.ts | 85 + src/module/hit-location-schemas.ts | 191 + src/module/impulse-tracker.ts | 756 ++++ src/module/item/RingworldItem.ts | 27 + src/module/mechanics.ts | 525 +++ src/module/sheets/actors/CreatureSheet.ts | 73 + src/module/sheets/actors/ExplorerSheet.ts | 1082 ++++++ src/module/sheets/actors/VehicleSheet.ts | 23 + src/module/sheets/items/ArmorSheet.ts | 23 + src/module/sheets/items/GearSheet.ts | 23 + src/module/sheets/items/PursuitSheet.ts | 39 + src/module/sheets/items/SkillSheet.ts | 31 + src/module/sheets/items/SpeciesSheet.ts | 23 + src/module/sheets/items/WeaponSheet.ts | 35 + src/module/skill-icons.ts | 27 + src/module/utils/dice.ts | 16 + src/module/utils/hit-locations.ts | 130 + src/module/utils/percentile.ts | 54 + src/module/world-catalog.ts | 978 +++++ src/ringworld.ts | 344 ++ src/styles/ringworld.scss | 1673 +++++++++ styles/ringworld.css | 1 + system.json | 52 + template.json | 362 ++ templates/actor/creature-sheet.hbs | 62 + templates/actor/explorer-sheet.hbs | 491 +++ templates/actor/vehicle-sheet.hbs | 17 + templates/chat/attack-card.hbs | 41 + templates/chat/skill-roll-card.hbs | 18 + templates/item/armor-sheet.hbs | 39 + templates/item/gear-sheet.hbs | 24 + templates/item/pursuit-sheet.hbs | 41 + templates/item/skill-sheet.hbs | 14 + templates/item/species-sheet.hbs | 35 + templates/item/weapon-sheet.hbs | 52 + tsconfig.json | 18 + vite.config.ts | 26 + world-data/README.md | 15 + world-data/ringworld-starter/world.json | 14 + 188 files changed, 15660 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 assets/icons/armor.svg create mode 100644 assets/icons/armor/arm-bracers.svg create mode 100644 assets/icons/armor/armored-coat.svg create mode 100644 assets/icons/armor/cloth-vest.svg create mode 100644 assets/icons/armor/combat-harness.svg create mode 100644 assets/icons/armor/combat-helmet.svg create mode 100644 assets/icons/armor/full-combat-suit.svg create mode 100644 assets/icons/armor/impact-suit.svg create mode 100644 assets/icons/armor/leg-guards.svg create mode 100644 assets/icons/armor/soft-combat-vest.svg create mode 100644 assets/icons/gear.svg create mode 100644 assets/icons/gear/air-reprocessor.svg create mode 100644 assets/icons/gear/autodoc-portable.svg create mode 100644 assets/icons/gear/bioscanner.svg create mode 100644 assets/icons/gear/field-toolkit.svg create mode 100644 assets/icons/gear/holo-projector.svg create mode 100644 assets/icons/gear/medkit.svg create mode 100644 assets/icons/gear/memory-bubble-reader.svg create mode 100644 assets/icons/gear/micromind-unit.svg create mode 100644 assets/icons/gear/portable-generator.svg create mode 100644 assets/icons/gear/power-pack-r100.svg create mode 100644 assets/icons/gear/power-pack-r20.svg create mode 100644 assets/icons/gear/power-pack-r50.svg create mode 100644 assets/icons/gear/reactionless-flycycle.svg create mode 100644 assets/icons/gear/signal-beacon.svg create mode 100644 assets/icons/gear/stasis-box.svg create mode 100644 assets/icons/gear/survey-scanner.svg create mode 100644 assets/icons/gear/translator-module.svg create mode 100644 assets/icons/gear/vac-suit.svg create mode 100644 assets/icons/power-pack.svg create mode 100644 assets/icons/pursuit.svg create mode 100644 assets/icons/pursuit/artist.svg create mode 100644 assets/icons/pursuit/colonist.svg create mode 100644 assets/icons/pursuit/criminal.svg create mode 100644 assets/icons/pursuit/dilettante.svg create mode 100644 assets/icons/pursuit/engineer.svg create mode 100644 assets/icons/pursuit/entertainer.svg create mode 100644 assets/icons/pursuit/explorer.svg create mode 100644 assets/icons/pursuit/functionary.svg create mode 100644 assets/icons/pursuit/journalist-writer.svg create mode 100644 assets/icons/pursuit/miner.svg create mode 100644 assets/icons/pursuit/pilot.svg create mode 100644 assets/icons/pursuit/police-officer.svg create mode 100644 assets/icons/pursuit/politician.svg create mode 100644 assets/icons/pursuit/prisoner.svg create mode 100644 assets/icons/pursuit/scholar.svg create mode 100644 assets/icons/pursuit/scientific-researcher.svg create mode 100644 assets/icons/pursuit/soldier.svg create mode 100644 assets/icons/pursuit/zealot.svg create mode 100644 assets/icons/skill.svg create mode 100644 assets/icons/skill/anthropology.svg create mode 100644 assets/icons/skill/aquatic-vehicle.svg create mode 100644 assets/icons/skill/archaic-melee-weapon.svg create mode 100644 assets/icons/skill/archaic-ranged-weapons.svg create mode 100644 assets/icons/skill/astronomy.svg create mode 100644 assets/icons/skill/athletics.svg create mode 100644 assets/icons/skill/atmospheric-craft.svg create mode 100644 assets/icons/skill/bargain.svg create mode 100644 assets/icons/skill/biology.svg create mode 100644 assets/icons/skill/botany.svg create mode 100644 assets/icons/skill/chemistry.svg create mode 100644 assets/icons/skill/computers.svg create mode 100644 assets/icons/skill/debate.svg create mode 100644 assets/icons/skill/emergency-treatment.svg create mode 100644 assets/icons/skill/engineering.svg create mode 100644 assets/icons/skill/farming.svg create mode 100644 assets/icons/skill/fast-talk.svg create mode 100644 assets/icons/skill/fine-arts.svg create mode 100644 assets/icons/skill/ground-vehicle.svg create mode 100644 assets/icons/skill/handgun-energy.svg create mode 100644 assets/icons/skill/handgun-projectile.svg create mode 100644 assets/icons/skill/heavy-weapon-energy.svg create mode 100644 assets/icons/skill/heavy-weapon-projectile.svg create mode 100644 assets/icons/skill/hide.svg create mode 100644 assets/icons/skill/history.svg create mode 100644 assets/icons/skill/hyperdrive.svg create mode 100644 assets/icons/skill/law.svg create mode 100644 assets/icons/skill/listen.svg create mode 100644 assets/icons/skill/mathematics.svg create mode 100644 assets/icons/skill/musicianship.svg create mode 100644 assets/icons/skill/observe.svg create mode 100644 assets/icons/skill/orate.svg create mode 100644 assets/icons/skill/own-language.svg create mode 100644 assets/icons/skill/perform.svg create mode 100644 assets/icons/skill/personal-flyer.svg create mode 100644 assets/icons/skill/physics.svg create mode 100644 assets/icons/skill/planetology.svg create mode 100644 assets/icons/skill/psychology.svg create mode 100644 assets/icons/skill/reaction-drive.svg create mode 100644 assets/icons/skill/reactionless-drive.svg create mode 100644 assets/icons/skill/repair.svg create mode 100644 assets/icons/skill/ringworld.svg create mode 100644 assets/icons/skill/scent.svg create mode 100644 assets/icons/skill/search.svg create mode 100644 assets/icons/skill/second-languages.svg create mode 100644 assets/icons/skill/sneak.svg create mode 100644 assets/icons/skill/strategy.svg create mode 100644 assets/icons/skill/theology.svg create mode 100644 assets/icons/skill/track.svg create mode 100644 assets/icons/skill/unarmed-combat.svg create mode 100644 assets/icons/skill/v-sword-f-laser.svg create mode 100644 assets/icons/skill/weapons-system.svg create mode 100644 assets/icons/skill/zoology.svg create mode 100644 assets/icons/species.svg create mode 100644 assets/icons/species/bandersnatchi.svg create mode 100644 assets/icons/species/boat-people.svg create mode 100644 assets/icons/species/builder.svg create mode 100644 assets/icons/species/dolphin.svg create mode 100644 assets/icons/species/earth-human.svg create mode 100644 assets/icons/species/ghoul.svg create mode 100644 assets/icons/species/human.svg create mode 100644 assets/icons/species/kzinti.svg create mode 100644 assets/icons/species/martian.svg create mode 100644 assets/icons/species/pierin.svg create mode 100644 assets/icons/species/pierson-s-puppeteer.svg create mode 100644 assets/icons/species/trinoc.svg create mode 100644 assets/icons/weapon-melee.svg create mode 100644 assets/icons/weapon-ranged.svg create mode 100644 assets/icons/weapon/flashlight-laser.svg create mode 100644 assets/icons/weapon/handgun-projectile.svg create mode 100644 assets/icons/weapon/laser-pistol.svg create mode 100644 assets/icons/weapon/laser-rifle.svg create mode 100644 assets/icons/weapon/monofilament-knife.svg create mode 100644 assets/icons/weapon/rifle-projectile.svg create mode 100644 assets/icons/weapon/shock-baton.svg create mode 100644 assets/icons/weapon/slug-thrower-carbine.svg create mode 100644 assets/icons/weapon/stunner.svg create mode 100644 assets/icons/weapon/variable-sword.svg create mode 100644 dist/ringworld-content.zip create mode 100644 dist/ringworld-starter-world.zip create mode 100644 dist/ringworld-system.zip create mode 100644 lang/en.json create mode 100644 module/ringworld.mjs create mode 100644 module/ringworld.mjs.map create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 resources/hit-locations/humanoid.json create mode 100644 ringworld-content/README.md create mode 100644 ringworld-content/module.json create mode 100644 ringworld-content/module/ringworld-content.mjs create mode 100644 seeds/base-skills.json create mode 100644 seeds/example-explorer.json create mode 100644 seeds/example-items.json create mode 100644 src/module/actor/RingworldActor.ts create mode 100644 src/module/character-tools.ts create mode 100644 src/module/chat.ts create mode 100644 src/module/constants.ts create mode 100644 src/module/data-base-skills.ts create mode 100644 src/module/hit-location-schemas.ts create mode 100644 src/module/impulse-tracker.ts create mode 100644 src/module/item/RingworldItem.ts create mode 100644 src/module/mechanics.ts create mode 100644 src/module/sheets/actors/CreatureSheet.ts create mode 100644 src/module/sheets/actors/ExplorerSheet.ts create mode 100644 src/module/sheets/actors/VehicleSheet.ts create mode 100644 src/module/sheets/items/ArmorSheet.ts create mode 100644 src/module/sheets/items/GearSheet.ts create mode 100644 src/module/sheets/items/PursuitSheet.ts create mode 100644 src/module/sheets/items/SkillSheet.ts create mode 100644 src/module/sheets/items/SpeciesSheet.ts create mode 100644 src/module/sheets/items/WeaponSheet.ts create mode 100644 src/module/skill-icons.ts create mode 100644 src/module/utils/dice.ts create mode 100644 src/module/utils/hit-locations.ts create mode 100644 src/module/utils/percentile.ts create mode 100644 src/module/world-catalog.ts create mode 100644 src/ringworld.ts create mode 100644 src/styles/ringworld.scss create mode 100644 styles/ringworld.css create mode 100644 system.json create mode 100644 template.json create mode 100644 templates/actor/creature-sheet.hbs create mode 100644 templates/actor/explorer-sheet.hbs create mode 100644 templates/actor/vehicle-sheet.hbs create mode 100644 templates/chat/attack-card.hbs create mode 100644 templates/chat/skill-roll-card.hbs create mode 100644 templates/item/armor-sheet.hbs create mode 100644 templates/item/gear-sheet.hbs create mode 100644 templates/item/pursuit-sheet.hbs create mode 100644 templates/item/skill-sheet.hbs create mode 100644 templates/item/species-sheet.hbs create mode 100644 templates/item/weapon-sheet.hbs create mode 100644 tsconfig.json create mode 100644 vite.config.ts create mode 100644 world-data/README.md create mode 100644 world-data/ringworld-starter/world.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e97a76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.vite/ +*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..0de04f6 --- /dev/null +++ b/README.md @@ -0,0 +1,113 @@ +# Ringworld Foundry System (v12-v13 MVP) + +Ringworld is a custom Foundry VTT game system for BRP-like percentile play with hit locations, location armor, and energy-tracked weapons. + +## Online Install (GitHub) + +Repository: +- https://github.com/slaguru666/Ringworld + +System manifest URL: +- `https://raw.githubusercontent.com/slaguru666/Ringworld/main/system.json` + +Companion content module manifest URL: +- `https://raw.githubusercontent.com/slaguru666/Ringworld/main/ringworld-content/module.json` + +Starter world data archive: +- `https://raw.githubusercontent.com/slaguru666/Ringworld/main/dist/ringworld-starter-world.zip` + +## Status + +MVP implemented: +- Actor types: `explorer`, `creature`, `vehicle` (stub) +- Item types: `skill`, `weapon`, `armor`, `gear`, `species` +- Percentile checks with `Special Success` and `Fumble` +- 1d20 hit location roll with Ringworld 7-location mapping +- Weapon workflow with target selection, hit location, armor subtraction, and damage to both location HP and general HP +- Auto conditions at location 0 or less (`prone`, `unconscious`, limb unusable flags) +- Armor overlap stacking across all equipped armor items covering a location +- Energy use from actor pool or linked power pack item +- World catalog seeding for Ringworld equipment, armor, weapons, gear, and species +- Character tools: roll all characteristics and random explorer generation + +## Install / Build + +From this project folder: + +```bash +cd /Users/timevans/FoundryVTT/foundryvtt-ringworld +npm install +npm run build +``` + +Build output: +- `module/ringworld.mjs` +- `styles/ringworld.css` + +## Foundry placement + +Foundry loads systems from `.../data/systems` (or `.../Data/systems` on case-insensitive macOS paths). + +Recommended live-dev symlink: + +```bash +ln -s /Users/timevans/FoundryVTT/foundryvtt-ringworld /Users/timevans/FoundryVTT/data/systems/ringworld +``` + +If `ringworld` already exists, remove or rename it first. + +## Using the system + +1. In Foundry, create a world using **Ringworld** system. +2. Create an **Explorer** actor. +3. In the **Skills** tab, click **Populate Ringworld Base Skills** to add all canonical Ringworld skills. +4. Add extra custom skills or weapon items from the actor sheet as needed. +5. For energy weapons: + - Create a `gear` item with `Is Power Pack` checked. + - Set weapon `Power Source` to `Power Pack Item` and select the pack. +6. Target a token, then click **Use** on a weapon. +7. Choose hit location (or random 1d20) and confirm damage application in chat. +8. Use header tools on Explorer sheet: + - **Roll Characteristics**: rolls all characteristic values from gravity profile + - **Random Character**: random species + gravity + rolled stats + starter kit + +## Catalog and Generators + +- On first GM world load, the system seeds world Items with Ringworld catalog entries. +- You can reseed manually from **Items Directory** using `Seed Ringworld Items`. +- You can create a random explorer from **Actors Directory** using `Create Random Explorer`. +- If you install the companion module **Ringworld Content**, you can import everything with one button: + - **Items Directory** -> `Import Ringworld Content` + +## Rules implemented for v1 + +- Roll-under percentile checks (`1d100 <= target`) +- Special Success: roll `<= floor(target / 5)` +- Fumble: + - target `< 50`: roll `96-100` + - target `>= 50`: roll `100` +- Hit locations (1d20): + - `01-03 Right Leg` + - `04-06 Left Leg` + - `07-10 Abdomen` + - `11-15 Chest` + - `16-17 Right Arm` + - `18-19 Left Arm` + - `20 Head` +- Locational damage also reduces general HP (same final damage) +- Armor subtracts 1:1 damage and stacks across overlapping covered armor pieces + +## Seeds + +`seeds/` contains starter data: +- `base-skills.json` full base skill list +- `example-items.json` sample skill/weapon/armor/power pack +- `example-explorer.json` sample explorer with items + +These are JSON templates for manual import or scripted pack generation. + +## Notes + +- Content is numeric/stat-driven only. No long rulebook text is embedded. +- Vehicle combat and computer subsystems remain backlog. +- `world-data/` contains a starter world shell and packaging files for server transfer. diff --git a/assets/icons/armor.svg b/assets/icons/armor.svg new file mode 100644 index 0000000..ce635f8 --- /dev/null +++ b/assets/icons/armor.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/armor/arm-bracers.svg b/assets/icons/armor/arm-bracers.svg new file mode 100644 index 0000000..dff35b7 --- /dev/null +++ b/assets/icons/armor/arm-bracers.svg @@ -0,0 +1,7 @@ + + + + + + AB + diff --git a/assets/icons/armor/armored-coat.svg b/assets/icons/armor/armored-coat.svg new file mode 100644 index 0000000..5bc44b1 --- /dev/null +++ b/assets/icons/armor/armored-coat.svg @@ -0,0 +1,7 @@ + + + + + + AC + diff --git a/assets/icons/armor/cloth-vest.svg b/assets/icons/armor/cloth-vest.svg new file mode 100644 index 0000000..a2c1ee0 --- /dev/null +++ b/assets/icons/armor/cloth-vest.svg @@ -0,0 +1,7 @@ + + + + + + CV + diff --git a/assets/icons/armor/combat-harness.svg b/assets/icons/armor/combat-harness.svg new file mode 100644 index 0000000..46809d6 --- /dev/null +++ b/assets/icons/armor/combat-harness.svg @@ -0,0 +1,7 @@ + + + + + + CH + diff --git a/assets/icons/armor/combat-helmet.svg b/assets/icons/armor/combat-helmet.svg new file mode 100644 index 0000000..e0104b0 --- /dev/null +++ b/assets/icons/armor/combat-helmet.svg @@ -0,0 +1,7 @@ + + + + + + HM + diff --git a/assets/icons/armor/full-combat-suit.svg b/assets/icons/armor/full-combat-suit.svg new file mode 100644 index 0000000..4f5a439 --- /dev/null +++ b/assets/icons/armor/full-combat-suit.svg @@ -0,0 +1,7 @@ + + + + + + FS + diff --git a/assets/icons/armor/impact-suit.svg b/assets/icons/armor/impact-suit.svg new file mode 100644 index 0000000..4fc7be8 --- /dev/null +++ b/assets/icons/armor/impact-suit.svg @@ -0,0 +1,7 @@ + + + + + + IS + diff --git a/assets/icons/armor/leg-guards.svg b/assets/icons/armor/leg-guards.svg new file mode 100644 index 0000000..071bdcf --- /dev/null +++ b/assets/icons/armor/leg-guards.svg @@ -0,0 +1,7 @@ + + + + + + LG + diff --git a/assets/icons/armor/soft-combat-vest.svg b/assets/icons/armor/soft-combat-vest.svg new file mode 100644 index 0000000..b0da820 --- /dev/null +++ b/assets/icons/armor/soft-combat-vest.svg @@ -0,0 +1,7 @@ + + + + + + SV + diff --git a/assets/icons/gear.svg b/assets/icons/gear.svg new file mode 100644 index 0000000..7d0c552 --- /dev/null +++ b/assets/icons/gear.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/gear/air-reprocessor.svg b/assets/icons/gear/air-reprocessor.svg new file mode 100644 index 0000000..f9edee7 --- /dev/null +++ b/assets/icons/gear/air-reprocessor.svg @@ -0,0 +1,7 @@ + + + + + + AR + diff --git a/assets/icons/gear/autodoc-portable.svg b/assets/icons/gear/autodoc-portable.svg new file mode 100644 index 0000000..09fa561 --- /dev/null +++ b/assets/icons/gear/autodoc-portable.svg @@ -0,0 +1,7 @@ + + + + + + AD + diff --git a/assets/icons/gear/bioscanner.svg b/assets/icons/gear/bioscanner.svg new file mode 100644 index 0000000..9f55533 --- /dev/null +++ b/assets/icons/gear/bioscanner.svg @@ -0,0 +1,7 @@ + + + + + + BS + diff --git a/assets/icons/gear/field-toolkit.svg b/assets/icons/gear/field-toolkit.svg new file mode 100644 index 0000000..e651840 --- /dev/null +++ b/assets/icons/gear/field-toolkit.svg @@ -0,0 +1,7 @@ + + + + + + FT + diff --git a/assets/icons/gear/holo-projector.svg b/assets/icons/gear/holo-projector.svg new file mode 100644 index 0000000..b847075 --- /dev/null +++ b/assets/icons/gear/holo-projector.svg @@ -0,0 +1,7 @@ + + + + + + HP + diff --git a/assets/icons/gear/medkit.svg b/assets/icons/gear/medkit.svg new file mode 100644 index 0000000..80c73e2 --- /dev/null +++ b/assets/icons/gear/medkit.svg @@ -0,0 +1,7 @@ + + + + + + MK + diff --git a/assets/icons/gear/memory-bubble-reader.svg b/assets/icons/gear/memory-bubble-reader.svg new file mode 100644 index 0000000..3cdedd1 --- /dev/null +++ b/assets/icons/gear/memory-bubble-reader.svg @@ -0,0 +1,7 @@ + + + + + + MB + diff --git a/assets/icons/gear/micromind-unit.svg b/assets/icons/gear/micromind-unit.svg new file mode 100644 index 0000000..ad8ff6f --- /dev/null +++ b/assets/icons/gear/micromind-unit.svg @@ -0,0 +1,7 @@ + + + + + + MU + diff --git a/assets/icons/gear/portable-generator.svg b/assets/icons/gear/portable-generator.svg new file mode 100644 index 0000000..445b3bf --- /dev/null +++ b/assets/icons/gear/portable-generator.svg @@ -0,0 +1,7 @@ + + + + + + PG + diff --git a/assets/icons/gear/power-pack-r100.svg b/assets/icons/gear/power-pack-r100.svg new file mode 100644 index 0000000..6e3be85 --- /dev/null +++ b/assets/icons/gear/power-pack-r100.svg @@ -0,0 +1,7 @@ + + + + + + 100 + diff --git a/assets/icons/gear/power-pack-r20.svg b/assets/icons/gear/power-pack-r20.svg new file mode 100644 index 0000000..717e8a9 --- /dev/null +++ b/assets/icons/gear/power-pack-r20.svg @@ -0,0 +1,7 @@ + + + + + + 20 + diff --git a/assets/icons/gear/power-pack-r50.svg b/assets/icons/gear/power-pack-r50.svg new file mode 100644 index 0000000..c0b4b89 --- /dev/null +++ b/assets/icons/gear/power-pack-r50.svg @@ -0,0 +1,7 @@ + + + + + + 50 + diff --git a/assets/icons/gear/reactionless-flycycle.svg b/assets/icons/gear/reactionless-flycycle.svg new file mode 100644 index 0000000..2019b5c --- /dev/null +++ b/assets/icons/gear/reactionless-flycycle.svg @@ -0,0 +1,7 @@ + + + + + + RF + diff --git a/assets/icons/gear/signal-beacon.svg b/assets/icons/gear/signal-beacon.svg new file mode 100644 index 0000000..1bffd62 --- /dev/null +++ b/assets/icons/gear/signal-beacon.svg @@ -0,0 +1,7 @@ + + + + + + SB + diff --git a/assets/icons/gear/stasis-box.svg b/assets/icons/gear/stasis-box.svg new file mode 100644 index 0000000..883c4f5 --- /dev/null +++ b/assets/icons/gear/stasis-box.svg @@ -0,0 +1,7 @@ + + + + + + ST + diff --git a/assets/icons/gear/survey-scanner.svg b/assets/icons/gear/survey-scanner.svg new file mode 100644 index 0000000..b218271 --- /dev/null +++ b/assets/icons/gear/survey-scanner.svg @@ -0,0 +1,7 @@ + + + + + + SS + diff --git a/assets/icons/gear/translator-module.svg b/assets/icons/gear/translator-module.svg new file mode 100644 index 0000000..d17b1d0 --- /dev/null +++ b/assets/icons/gear/translator-module.svg @@ -0,0 +1,7 @@ + + + + + + TM + diff --git a/assets/icons/gear/vac-suit.svg b/assets/icons/gear/vac-suit.svg new file mode 100644 index 0000000..25a2b0e --- /dev/null +++ b/assets/icons/gear/vac-suit.svg @@ -0,0 +1,7 @@ + + + + + + VS + diff --git a/assets/icons/power-pack.svg b/assets/icons/power-pack.svg new file mode 100644 index 0000000..f50277a --- /dev/null +++ b/assets/icons/power-pack.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/pursuit.svg b/assets/icons/pursuit.svg new file mode 100644 index 0000000..0370a22 --- /dev/null +++ b/assets/icons/pursuit.svg @@ -0,0 +1,7 @@ + + + + + PU + PURSUIT + diff --git a/assets/icons/pursuit/artist.svg b/assets/icons/pursuit/artist.svg new file mode 100644 index 0000000..a924eb3 --- /dev/null +++ b/assets/icons/pursuit/artist.svg @@ -0,0 +1,8 @@ + + + + + + AR + PURSUIT + diff --git a/assets/icons/pursuit/colonist.svg b/assets/icons/pursuit/colonist.svg new file mode 100644 index 0000000..e5b33b6 --- /dev/null +++ b/assets/icons/pursuit/colonist.svg @@ -0,0 +1,8 @@ + + + + + + CO + PURSUIT + diff --git a/assets/icons/pursuit/criminal.svg b/assets/icons/pursuit/criminal.svg new file mode 100644 index 0000000..c8bc9e1 --- /dev/null +++ b/assets/icons/pursuit/criminal.svg @@ -0,0 +1,8 @@ + + + + + + CR + PURSUIT + diff --git a/assets/icons/pursuit/dilettante.svg b/assets/icons/pursuit/dilettante.svg new file mode 100644 index 0000000..57e4038 --- /dev/null +++ b/assets/icons/pursuit/dilettante.svg @@ -0,0 +1,8 @@ + + + + + + DI + PURSUIT + diff --git a/assets/icons/pursuit/engineer.svg b/assets/icons/pursuit/engineer.svg new file mode 100644 index 0000000..cd51272 --- /dev/null +++ b/assets/icons/pursuit/engineer.svg @@ -0,0 +1,8 @@ + + + + + + EN + PURSUIT + diff --git a/assets/icons/pursuit/entertainer.svg b/assets/icons/pursuit/entertainer.svg new file mode 100644 index 0000000..f1b6219 --- /dev/null +++ b/assets/icons/pursuit/entertainer.svg @@ -0,0 +1,8 @@ + + + + + + EN + PURSUIT + diff --git a/assets/icons/pursuit/explorer.svg b/assets/icons/pursuit/explorer.svg new file mode 100644 index 0000000..dcad02f --- /dev/null +++ b/assets/icons/pursuit/explorer.svg @@ -0,0 +1,8 @@ + + + + + + EX + PURSUIT + diff --git a/assets/icons/pursuit/functionary.svg b/assets/icons/pursuit/functionary.svg new file mode 100644 index 0000000..c8d0ae6 --- /dev/null +++ b/assets/icons/pursuit/functionary.svg @@ -0,0 +1,8 @@ + + + + + + FU + PURSUIT + diff --git a/assets/icons/pursuit/journalist-writer.svg b/assets/icons/pursuit/journalist-writer.svg new file mode 100644 index 0000000..83dcc75 --- /dev/null +++ b/assets/icons/pursuit/journalist-writer.svg @@ -0,0 +1,8 @@ + + + + + + JW + PURSUIT + diff --git a/assets/icons/pursuit/miner.svg b/assets/icons/pursuit/miner.svg new file mode 100644 index 0000000..eb68e76 --- /dev/null +++ b/assets/icons/pursuit/miner.svg @@ -0,0 +1,8 @@ + + + + + + MI + PURSUIT + diff --git a/assets/icons/pursuit/pilot.svg b/assets/icons/pursuit/pilot.svg new file mode 100644 index 0000000..8ecbe4d --- /dev/null +++ b/assets/icons/pursuit/pilot.svg @@ -0,0 +1,8 @@ + + + + + + PI + PURSUIT + diff --git a/assets/icons/pursuit/police-officer.svg b/assets/icons/pursuit/police-officer.svg new file mode 100644 index 0000000..5c835e7 --- /dev/null +++ b/assets/icons/pursuit/police-officer.svg @@ -0,0 +1,8 @@ + + + + + + PO + PURSUIT + diff --git a/assets/icons/pursuit/politician.svg b/assets/icons/pursuit/politician.svg new file mode 100644 index 0000000..058bc65 --- /dev/null +++ b/assets/icons/pursuit/politician.svg @@ -0,0 +1,8 @@ + + + + + + PO + PURSUIT + diff --git a/assets/icons/pursuit/prisoner.svg b/assets/icons/pursuit/prisoner.svg new file mode 100644 index 0000000..77f0c2f --- /dev/null +++ b/assets/icons/pursuit/prisoner.svg @@ -0,0 +1,8 @@ + + + + + + PR + PURSUIT + diff --git a/assets/icons/pursuit/scholar.svg b/assets/icons/pursuit/scholar.svg new file mode 100644 index 0000000..c8b63c9 --- /dev/null +++ b/assets/icons/pursuit/scholar.svg @@ -0,0 +1,8 @@ + + + + + + SC + PURSUIT + diff --git a/assets/icons/pursuit/scientific-researcher.svg b/assets/icons/pursuit/scientific-researcher.svg new file mode 100644 index 0000000..93dfbab --- /dev/null +++ b/assets/icons/pursuit/scientific-researcher.svg @@ -0,0 +1,8 @@ + + + + + + SR + PURSUIT + diff --git a/assets/icons/pursuit/soldier.svg b/assets/icons/pursuit/soldier.svg new file mode 100644 index 0000000..f2583ec --- /dev/null +++ b/assets/icons/pursuit/soldier.svg @@ -0,0 +1,8 @@ + + + + + + SO + PURSUIT + diff --git a/assets/icons/pursuit/zealot.svg b/assets/icons/pursuit/zealot.svg new file mode 100644 index 0000000..b6cd63b --- /dev/null +++ b/assets/icons/pursuit/zealot.svg @@ -0,0 +1,8 @@ + + + + + + ZE + PURSUIT + diff --git a/assets/icons/skill.svg b/assets/icons/skill.svg new file mode 100644 index 0000000..c6bad04 --- /dev/null +++ b/assets/icons/skill.svg @@ -0,0 +1,7 @@ + + + + + SK + RINGWORLD + diff --git a/assets/icons/skill/anthropology.svg b/assets/icons/skill/anthropology.svg new file mode 100644 index 0000000..4ea0f6f --- /dev/null +++ b/assets/icons/skill/anthropology.svg @@ -0,0 +1,8 @@ + + + + + + AN + KNO + diff --git a/assets/icons/skill/aquatic-vehicle.svg b/assets/icons/skill/aquatic-vehicle.svg new file mode 100644 index 0000000..27f3400 --- /dev/null +++ b/assets/icons/skill/aquatic-vehicle.svg @@ -0,0 +1,8 @@ + + + + + + AV + TEC + diff --git a/assets/icons/skill/archaic-melee-weapon.svg b/assets/icons/skill/archaic-melee-weapon.svg new file mode 100644 index 0000000..695fcf3 --- /dev/null +++ b/assets/icons/skill/archaic-melee-weapon.svg @@ -0,0 +1,8 @@ + + + + + + AM + AGI + diff --git a/assets/icons/skill/archaic-ranged-weapons.svg b/assets/icons/skill/archaic-ranged-weapons.svg new file mode 100644 index 0000000..4a8c43c --- /dev/null +++ b/assets/icons/skill/archaic-ranged-weapons.svg @@ -0,0 +1,8 @@ + + + + + + AR + AGI + diff --git a/assets/icons/skill/astronomy.svg b/assets/icons/skill/astronomy.svg new file mode 100644 index 0000000..90a29ba --- /dev/null +++ b/assets/icons/skill/astronomy.svg @@ -0,0 +1,8 @@ + + + + + + AS + KNO + diff --git a/assets/icons/skill/athletics.svg b/assets/icons/skill/athletics.svg new file mode 100644 index 0000000..499a0a9 --- /dev/null +++ b/assets/icons/skill/athletics.svg @@ -0,0 +1,8 @@ + + + + + + AT + AGI + diff --git a/assets/icons/skill/atmospheric-craft.svg b/assets/icons/skill/atmospheric-craft.svg new file mode 100644 index 0000000..48cdc2c --- /dev/null +++ b/assets/icons/skill/atmospheric-craft.svg @@ -0,0 +1,8 @@ + + + + + + AC + TEC + diff --git a/assets/icons/skill/bargain.svg b/assets/icons/skill/bargain.svg new file mode 100644 index 0000000..84331bc --- /dev/null +++ b/assets/icons/skill/bargain.svg @@ -0,0 +1,8 @@ + + + + + + BA + COM + diff --git a/assets/icons/skill/biology.svg b/assets/icons/skill/biology.svg new file mode 100644 index 0000000..5ea51c0 --- /dev/null +++ b/assets/icons/skill/biology.svg @@ -0,0 +1,8 @@ + + + + + + BI + KNO + diff --git a/assets/icons/skill/botany.svg b/assets/icons/skill/botany.svg new file mode 100644 index 0000000..ea23726 --- /dev/null +++ b/assets/icons/skill/botany.svg @@ -0,0 +1,8 @@ + + + + + + BO + KNO + diff --git a/assets/icons/skill/chemistry.svg b/assets/icons/skill/chemistry.svg new file mode 100644 index 0000000..97aea39 --- /dev/null +++ b/assets/icons/skill/chemistry.svg @@ -0,0 +1,8 @@ + + + + + + CH + KNO + diff --git a/assets/icons/skill/computers.svg b/assets/icons/skill/computers.svg new file mode 100644 index 0000000..add1eee --- /dev/null +++ b/assets/icons/skill/computers.svg @@ -0,0 +1,8 @@ + + + + + + CO + KNO + diff --git a/assets/icons/skill/debate.svg b/assets/icons/skill/debate.svg new file mode 100644 index 0000000..f61846e --- /dev/null +++ b/assets/icons/skill/debate.svg @@ -0,0 +1,8 @@ + + + + + + DE + COM + diff --git a/assets/icons/skill/emergency-treatment.svg b/assets/icons/skill/emergency-treatment.svg new file mode 100644 index 0000000..f766e48 --- /dev/null +++ b/assets/icons/skill/emergency-treatment.svg @@ -0,0 +1,8 @@ + + + + + + ET + KNO + diff --git a/assets/icons/skill/engineering.svg b/assets/icons/skill/engineering.svg new file mode 100644 index 0000000..8363ef7 --- /dev/null +++ b/assets/icons/skill/engineering.svg @@ -0,0 +1,8 @@ + + + + + + EN + KNO + diff --git a/assets/icons/skill/farming.svg b/assets/icons/skill/farming.svg new file mode 100644 index 0000000..f12609e --- /dev/null +++ b/assets/icons/skill/farming.svg @@ -0,0 +1,8 @@ + + + + + + FA + KNO + diff --git a/assets/icons/skill/fast-talk.svg b/assets/icons/skill/fast-talk.svg new file mode 100644 index 0000000..a17a160 --- /dev/null +++ b/assets/icons/skill/fast-talk.svg @@ -0,0 +1,8 @@ + + + + + + FT + COM + diff --git a/assets/icons/skill/fine-arts.svg b/assets/icons/skill/fine-arts.svg new file mode 100644 index 0000000..20480f4 --- /dev/null +++ b/assets/icons/skill/fine-arts.svg @@ -0,0 +1,8 @@ + + + + + + FA + COM + diff --git a/assets/icons/skill/ground-vehicle.svg b/assets/icons/skill/ground-vehicle.svg new file mode 100644 index 0000000..269c3f2 --- /dev/null +++ b/assets/icons/skill/ground-vehicle.svg @@ -0,0 +1,8 @@ + + + + + + GV + TEC + diff --git a/assets/icons/skill/handgun-energy.svg b/assets/icons/skill/handgun-energy.svg new file mode 100644 index 0000000..3241ab5 --- /dev/null +++ b/assets/icons/skill/handgun-energy.svg @@ -0,0 +1,8 @@ + + + + + + HE + PER + diff --git a/assets/icons/skill/handgun-projectile.svg b/assets/icons/skill/handgun-projectile.svg new file mode 100644 index 0000000..395c6d9 --- /dev/null +++ b/assets/icons/skill/handgun-projectile.svg @@ -0,0 +1,8 @@ + + + + + + HP + PER + diff --git a/assets/icons/skill/heavy-weapon-energy.svg b/assets/icons/skill/heavy-weapon-energy.svg new file mode 100644 index 0000000..b7705dc --- /dev/null +++ b/assets/icons/skill/heavy-weapon-energy.svg @@ -0,0 +1,8 @@ + + + + + + HW + PER + diff --git a/assets/icons/skill/heavy-weapon-projectile.svg b/assets/icons/skill/heavy-weapon-projectile.svg new file mode 100644 index 0000000..b7705dc --- /dev/null +++ b/assets/icons/skill/heavy-weapon-projectile.svg @@ -0,0 +1,8 @@ + + + + + + HW + PER + diff --git a/assets/icons/skill/hide.svg b/assets/icons/skill/hide.svg new file mode 100644 index 0000000..e5df3e3 --- /dev/null +++ b/assets/icons/skill/hide.svg @@ -0,0 +1,8 @@ + + + + + + HI + AGI + diff --git a/assets/icons/skill/history.svg b/assets/icons/skill/history.svg new file mode 100644 index 0000000..2926be7 --- /dev/null +++ b/assets/icons/skill/history.svg @@ -0,0 +1,8 @@ + + + + + + HI + KNO + diff --git a/assets/icons/skill/hyperdrive.svg b/assets/icons/skill/hyperdrive.svg new file mode 100644 index 0000000..44939f6 --- /dev/null +++ b/assets/icons/skill/hyperdrive.svg @@ -0,0 +1,8 @@ + + + + + + HY + TEC + diff --git a/assets/icons/skill/law.svg b/assets/icons/skill/law.svg new file mode 100644 index 0000000..bdd2c3c --- /dev/null +++ b/assets/icons/skill/law.svg @@ -0,0 +1,8 @@ + + + + + + LA + KNO + diff --git a/assets/icons/skill/listen.svg b/assets/icons/skill/listen.svg new file mode 100644 index 0000000..2ac7e43 --- /dev/null +++ b/assets/icons/skill/listen.svg @@ -0,0 +1,8 @@ + + + + + + LI + PER + diff --git a/assets/icons/skill/mathematics.svg b/assets/icons/skill/mathematics.svg new file mode 100644 index 0000000..ac9d8d1 --- /dev/null +++ b/assets/icons/skill/mathematics.svg @@ -0,0 +1,8 @@ + + + + + + MA + KNO + diff --git a/assets/icons/skill/musicianship.svg b/assets/icons/skill/musicianship.svg new file mode 100644 index 0000000..a8ff2b1 --- /dev/null +++ b/assets/icons/skill/musicianship.svg @@ -0,0 +1,8 @@ + + + + + + MU + COM + diff --git a/assets/icons/skill/observe.svg b/assets/icons/skill/observe.svg new file mode 100644 index 0000000..75de7fe --- /dev/null +++ b/assets/icons/skill/observe.svg @@ -0,0 +1,8 @@ + + + + + + OB + PER + diff --git a/assets/icons/skill/orate.svg b/assets/icons/skill/orate.svg new file mode 100644 index 0000000..a93ad98 --- /dev/null +++ b/assets/icons/skill/orate.svg @@ -0,0 +1,8 @@ + + + + + + OR + COM + diff --git a/assets/icons/skill/own-language.svg b/assets/icons/skill/own-language.svg new file mode 100644 index 0000000..4b60973 --- /dev/null +++ b/assets/icons/skill/own-language.svg @@ -0,0 +1,8 @@ + + + + + + OL + COM + diff --git a/assets/icons/skill/perform.svg b/assets/icons/skill/perform.svg new file mode 100644 index 0000000..999b253 --- /dev/null +++ b/assets/icons/skill/perform.svg @@ -0,0 +1,8 @@ + + + + + + PE + COM + diff --git a/assets/icons/skill/personal-flyer.svg b/assets/icons/skill/personal-flyer.svg new file mode 100644 index 0000000..2ed1e3a --- /dev/null +++ b/assets/icons/skill/personal-flyer.svg @@ -0,0 +1,8 @@ + + + + + + PF + TEC + diff --git a/assets/icons/skill/physics.svg b/assets/icons/skill/physics.svg new file mode 100644 index 0000000..45c47bb --- /dev/null +++ b/assets/icons/skill/physics.svg @@ -0,0 +1,8 @@ + + + + + + PH + KNO + diff --git a/assets/icons/skill/planetology.svg b/assets/icons/skill/planetology.svg new file mode 100644 index 0000000..629296b --- /dev/null +++ b/assets/icons/skill/planetology.svg @@ -0,0 +1,8 @@ + + + + + + PL + KNO + diff --git a/assets/icons/skill/psychology.svg b/assets/icons/skill/psychology.svg new file mode 100644 index 0000000..92c5259 --- /dev/null +++ b/assets/icons/skill/psychology.svg @@ -0,0 +1,8 @@ + + + + + + PS + COM + diff --git a/assets/icons/skill/reaction-drive.svg b/assets/icons/skill/reaction-drive.svg new file mode 100644 index 0000000..4cc006c --- /dev/null +++ b/assets/icons/skill/reaction-drive.svg @@ -0,0 +1,8 @@ + + + + + + RD + TEC + diff --git a/assets/icons/skill/reactionless-drive.svg b/assets/icons/skill/reactionless-drive.svg new file mode 100644 index 0000000..4cc006c --- /dev/null +++ b/assets/icons/skill/reactionless-drive.svg @@ -0,0 +1,8 @@ + + + + + + RD + TEC + diff --git a/assets/icons/skill/repair.svg b/assets/icons/skill/repair.svg new file mode 100644 index 0000000..0ff162c --- /dev/null +++ b/assets/icons/skill/repair.svg @@ -0,0 +1,8 @@ + + + + + + RE + TEC + diff --git a/assets/icons/skill/ringworld.svg b/assets/icons/skill/ringworld.svg new file mode 100644 index 0000000..e5fee11 --- /dev/null +++ b/assets/icons/skill/ringworld.svg @@ -0,0 +1,8 @@ + + + + + + RI + TEC + diff --git a/assets/icons/skill/scent.svg b/assets/icons/skill/scent.svg new file mode 100644 index 0000000..664f2ba --- /dev/null +++ b/assets/icons/skill/scent.svg @@ -0,0 +1,8 @@ + + + + + + SC + PER + diff --git a/assets/icons/skill/search.svg b/assets/icons/skill/search.svg new file mode 100644 index 0000000..5ffe219 --- /dev/null +++ b/assets/icons/skill/search.svg @@ -0,0 +1,8 @@ + + + + + + SE + PER + diff --git a/assets/icons/skill/second-languages.svg b/assets/icons/skill/second-languages.svg new file mode 100644 index 0000000..2c8de58 --- /dev/null +++ b/assets/icons/skill/second-languages.svg @@ -0,0 +1,8 @@ + + + + + + SL + KNO + diff --git a/assets/icons/skill/sneak.svg b/assets/icons/skill/sneak.svg new file mode 100644 index 0000000..3b68098 --- /dev/null +++ b/assets/icons/skill/sneak.svg @@ -0,0 +1,8 @@ + + + + + + SN + AGI + diff --git a/assets/icons/skill/strategy.svg b/assets/icons/skill/strategy.svg new file mode 100644 index 0000000..dd5857d --- /dev/null +++ b/assets/icons/skill/strategy.svg @@ -0,0 +1,8 @@ + + + + + + ST + KNO + diff --git a/assets/icons/skill/theology.svg b/assets/icons/skill/theology.svg new file mode 100644 index 0000000..93b89cb --- /dev/null +++ b/assets/icons/skill/theology.svg @@ -0,0 +1,8 @@ + + + + + + TH + KNO + diff --git a/assets/icons/skill/track.svg b/assets/icons/skill/track.svg new file mode 100644 index 0000000..2c00c2a --- /dev/null +++ b/assets/icons/skill/track.svg @@ -0,0 +1,8 @@ + + + + + + TR + PER + diff --git a/assets/icons/skill/unarmed-combat.svg b/assets/icons/skill/unarmed-combat.svg new file mode 100644 index 0000000..d107789 --- /dev/null +++ b/assets/icons/skill/unarmed-combat.svg @@ -0,0 +1,8 @@ + + + + + + UC + AGI + diff --git a/assets/icons/skill/v-sword-f-laser.svg b/assets/icons/skill/v-sword-f-laser.svg new file mode 100644 index 0000000..6775be6 --- /dev/null +++ b/assets/icons/skill/v-sword-f-laser.svg @@ -0,0 +1,8 @@ + + + + + + VS + AGI + diff --git a/assets/icons/skill/weapons-system.svg b/assets/icons/skill/weapons-system.svg new file mode 100644 index 0000000..8e72c1f --- /dev/null +++ b/assets/icons/skill/weapons-system.svg @@ -0,0 +1,8 @@ + + + + + + WS + TEC + diff --git a/assets/icons/skill/zoology.svg b/assets/icons/skill/zoology.svg new file mode 100644 index 0000000..e48cc18 --- /dev/null +++ b/assets/icons/skill/zoology.svg @@ -0,0 +1,8 @@ + + + + + + ZO + KNO + diff --git a/assets/icons/species.svg b/assets/icons/species.svg new file mode 100644 index 0000000..40c8b1d --- /dev/null +++ b/assets/icons/species.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/species/bandersnatchi.svg b/assets/icons/species/bandersnatchi.svg new file mode 100644 index 0000000..3bcb3a7 --- /dev/null +++ b/assets/icons/species/bandersnatchi.svg @@ -0,0 +1,7 @@ + + + + + + BD + diff --git a/assets/icons/species/boat-people.svg b/assets/icons/species/boat-people.svg new file mode 100644 index 0000000..999bfeb --- /dev/null +++ b/assets/icons/species/boat-people.svg @@ -0,0 +1,7 @@ + + + + + + BP + diff --git a/assets/icons/species/builder.svg b/assets/icons/species/builder.svg new file mode 100644 index 0000000..4731126 --- /dev/null +++ b/assets/icons/species/builder.svg @@ -0,0 +1,7 @@ + + + + + + BL + diff --git a/assets/icons/species/dolphin.svg b/assets/icons/species/dolphin.svg new file mode 100644 index 0000000..c31d959 --- /dev/null +++ b/assets/icons/species/dolphin.svg @@ -0,0 +1,7 @@ + + + + + + DL + diff --git a/assets/icons/species/earth-human.svg b/assets/icons/species/earth-human.svg new file mode 100644 index 0000000..4d91f09 --- /dev/null +++ b/assets/icons/species/earth-human.svg @@ -0,0 +1,7 @@ + + + + + + EH + diff --git a/assets/icons/species/ghoul.svg b/assets/icons/species/ghoul.svg new file mode 100644 index 0000000..9a8cd58 --- /dev/null +++ b/assets/icons/species/ghoul.svg @@ -0,0 +1,7 @@ + + + + + + GH + diff --git a/assets/icons/species/human.svg b/assets/icons/species/human.svg new file mode 100644 index 0000000..7532b4b --- /dev/null +++ b/assets/icons/species/human.svg @@ -0,0 +1,7 @@ + + + + + + HM + diff --git a/assets/icons/species/kzinti.svg b/assets/icons/species/kzinti.svg new file mode 100644 index 0000000..49db6e2 --- /dev/null +++ b/assets/icons/species/kzinti.svg @@ -0,0 +1,7 @@ + + + + + + KZ + diff --git a/assets/icons/species/martian.svg b/assets/icons/species/martian.svg new file mode 100644 index 0000000..5cb291b --- /dev/null +++ b/assets/icons/species/martian.svg @@ -0,0 +1,7 @@ + + + + + + MR + diff --git a/assets/icons/species/pierin.svg b/assets/icons/species/pierin.svg new file mode 100644 index 0000000..41906a7 --- /dev/null +++ b/assets/icons/species/pierin.svg @@ -0,0 +1,7 @@ + + + + + + PI + diff --git a/assets/icons/species/pierson-s-puppeteer.svg b/assets/icons/species/pierson-s-puppeteer.svg new file mode 100644 index 0000000..d3e95a2 --- /dev/null +++ b/assets/icons/species/pierson-s-puppeteer.svg @@ -0,0 +1,7 @@ + + + + + + PP + diff --git a/assets/icons/species/trinoc.svg b/assets/icons/species/trinoc.svg new file mode 100644 index 0000000..8afdcf3 --- /dev/null +++ b/assets/icons/species/trinoc.svg @@ -0,0 +1,7 @@ + + + + + + TR + diff --git a/assets/icons/weapon-melee.svg b/assets/icons/weapon-melee.svg new file mode 100644 index 0000000..96474d5 --- /dev/null +++ b/assets/icons/weapon-melee.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/weapon-ranged.svg b/assets/icons/weapon-ranged.svg new file mode 100644 index 0000000..37016bf --- /dev/null +++ b/assets/icons/weapon-ranged.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/weapon/flashlight-laser.svg b/assets/icons/weapon/flashlight-laser.svg new file mode 100644 index 0000000..a2c6e2d --- /dev/null +++ b/assets/icons/weapon/flashlight-laser.svg @@ -0,0 +1,7 @@ + + + + + + FL + diff --git a/assets/icons/weapon/handgun-projectile.svg b/assets/icons/weapon/handgun-projectile.svg new file mode 100644 index 0000000..2c9399e --- /dev/null +++ b/assets/icons/weapon/handgun-projectile.svg @@ -0,0 +1,7 @@ + + + + + + HP + diff --git a/assets/icons/weapon/laser-pistol.svg b/assets/icons/weapon/laser-pistol.svg new file mode 100644 index 0000000..b584b23 --- /dev/null +++ b/assets/icons/weapon/laser-pistol.svg @@ -0,0 +1,7 @@ + + + + + + LP + diff --git a/assets/icons/weapon/laser-rifle.svg b/assets/icons/weapon/laser-rifle.svg new file mode 100644 index 0000000..ca42544 --- /dev/null +++ b/assets/icons/weapon/laser-rifle.svg @@ -0,0 +1,7 @@ + + + + + + LR + diff --git a/assets/icons/weapon/monofilament-knife.svg b/assets/icons/weapon/monofilament-knife.svg new file mode 100644 index 0000000..074bbaf --- /dev/null +++ b/assets/icons/weapon/monofilament-knife.svg @@ -0,0 +1,7 @@ + + + + + + MK + diff --git a/assets/icons/weapon/rifle-projectile.svg b/assets/icons/weapon/rifle-projectile.svg new file mode 100644 index 0000000..fb8a0d4 --- /dev/null +++ b/assets/icons/weapon/rifle-projectile.svg @@ -0,0 +1,7 @@ + + + + + + RP + diff --git a/assets/icons/weapon/shock-baton.svg b/assets/icons/weapon/shock-baton.svg new file mode 100644 index 0000000..4e4c637 --- /dev/null +++ b/assets/icons/weapon/shock-baton.svg @@ -0,0 +1,7 @@ + + + + + + SB + diff --git a/assets/icons/weapon/slug-thrower-carbine.svg b/assets/icons/weapon/slug-thrower-carbine.svg new file mode 100644 index 0000000..c5b32e6 --- /dev/null +++ b/assets/icons/weapon/slug-thrower-carbine.svg @@ -0,0 +1,7 @@ + + + + + + SC + diff --git a/assets/icons/weapon/stunner.svg b/assets/icons/weapon/stunner.svg new file mode 100644 index 0000000..a11771f --- /dev/null +++ b/assets/icons/weapon/stunner.svg @@ -0,0 +1,7 @@ + + + + + + ST + diff --git a/assets/icons/weapon/variable-sword.svg b/assets/icons/weapon/variable-sword.svg new file mode 100644 index 0000000..c3bee3e --- /dev/null +++ b/assets/icons/weapon/variable-sword.svg @@ -0,0 +1,7 @@ + + + + + + VS + diff --git a/dist/ringworld-content.zip b/dist/ringworld-content.zip new file mode 100644 index 0000000000000000000000000000000000000000..f43b2bd58df560677d47c137dc7b5ce634c65cb4 GIT binary patch literal 2054 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 literal 0 HcmV?d00001 diff --git a/dist/ringworld-system.zip b/dist/ringworld-system.zip new file mode 100644 index 0000000000000000000000000000000000000000..2f9d51dc532626fac21aa078edbe0d3c85bbe465 GIT binary patch literal 197503 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 literal 0 HcmV?d00001 diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..5126b94 --- /dev/null +++ b/lang/en.json @@ -0,0 +1,5 @@ +{ + "RINGWORLD.RollSkill": "Roll Skill", + "RINGWORLD.RollHitLocation": "Roll Hit Location", + "RINGWORLD.UseWeapon": "Use Weapon" +} diff --git a/module/ringworld.mjs b/module/ringworld.mjs new file mode 100644 index 0000000..d2e16a4 --- /dev/null +++ b/module/ringworld.mjs @@ -0,0 +1,3335 @@ +async function Tt(e) { + const t = game == null ? void 0 : game.dice3d; + if (t != null && t.showForRoll) + try { + await t.showForRoll(e, game.user, !0, null, !1); + } catch { + } +} +async function Me(e) { + const t = await new Roll(e).evaluate(); + return await Tt(t), t; +} +async function Ue(e) { + const t = await Me("1d100"); + return Dt(t.total ?? 0, e); +} +function Dt(e, t) { + const a = Math.max(0, Math.floor(Number(t) || 0)), n = Math.max(1, Math.floor(a / 5)), o = a >= 50 ? 100 : 96, s = e <= a, i = s && e <= n, l = !s && e >= o; + let r = "failure"; + return l ? r = "fumble" : i ? r = "special" : s && (r = "success"), { + roll: e, + target: a, + outcome: r, + isSuccess: s, + isSpecial: i, + isFumble: l + }; +} +function Ae(e) { + switch (e) { + case "special": + return "Special Success"; + case "success": + return "Success"; + case "fumble": + return "Fumble"; + default: + return "Failure"; + } +} +const x = "ringworld", Ce = [ + { slug: "rLeg", label: "Right Leg", min: 1, max: 3 }, + { slug: "lLeg", label: "Left Leg", min: 4, max: 6 }, + { slug: "abdomen", label: "Abdomen", min: 7, max: 10 }, + { slug: "chest", label: "Chest", min: 11, max: 15 }, + { slug: "rArm", label: "Right Arm", min: 16, max: 17 }, + { slug: "lArm", label: "Left Arm", min: 18, max: 19 }, + { slug: "head", label: "Head", min: 20, max: 20 } +], Ht = ["rArm", "lArm", "rLeg", "lLeg"], Nt = ["rLeg", "lLeg"]; +function Ot(e) { + return Ht.includes(e); +} +function it(e) { + return String(e ?? "").toLowerCase().replace(/[^a-z0-9]+/g, ""); +} +function oe(e, t) { + return e.map((a) => ({ + ...a, + hpFactor: (t == null ? void 0 : t[a.slug]) ?? (a.slug === "chest" ? 0.35 : a.slug === "rArm" || a.slug === "lArm" ? 0.25 : 0.3) + })); +} +const rt = { + id: "humanoid", + label: "Humanoid", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: oe(Ce) +}, Et = { + id: "dolphin", + label: "Dolphin", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: oe([ + { slug: "rLeg", label: "Right Fluke", min: 1, max: 3 }, + { slug: "lLeg", label: "Left Fluke", min: 4, max: 6 }, + { slug: "abdomen", label: "Lower Body", min: 7, max: 9 }, + { slug: "chest", label: "Main Body", min: 10, max: 15 }, + { slug: "rArm", label: "Right Fin", min: 16, max: 17 }, + { slug: "lArm", label: "Left Fin", min: 18, max: 19 }, + { slug: "head", label: "Head", min: 20, max: 20 } + ], { + head: 0.3, + rArm: 0.2, + lArm: 0.2, + chest: 0.4, + abdomen: 0.35, + rLeg: 0.25, + lLeg: 0.25 + }) +}, Bt = { + id: "puppeteer", + label: "Puppeteer", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: oe([ + { slug: "rLeg", label: "Right Rear Leg", min: 1, max: 3 }, + { slug: "lLeg", label: "Left Rear Leg", min: 4, max: 6 }, + { slug: "rArm", label: "Right Foreleg", min: 7, max: 9 }, + { slug: "lArm", label: "Left Foreleg", min: 10, max: 12 }, + { slug: "chest", label: "Fore Body", min: 13, max: 15 }, + { slug: "abdomen", label: "Hind Body", min: 16, max: 18 }, + { slug: "head", label: "Twin Heads", min: 19, max: 20 } + ], { + head: 0.32, + chest: 0.35, + abdomen: 0.35, + rArm: 0.28, + lArm: 0.28, + rLeg: 0.28, + lLeg: 0.28 + }) +}, Ft = { + id: "trinoc", + label: "Trinoc", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: oe([ + { slug: "rArm", label: "Right Wing/Arm", min: 1, max: 3 }, + { slug: "lArm", label: "Left Wing/Arm", min: 4, max: 6 }, + { slug: "chest", label: "Upper Torso", min: 7, max: 11 }, + { slug: "abdomen", label: "Lower Torso", min: 12, max: 14 }, + { slug: "rLeg", label: "Right Leg", min: 15, max: 17 }, + { slug: "lLeg", label: "Left Leg", min: 18, max: 19 }, + { slug: "head", label: "Head", min: 20, max: 20 } + ], { + head: 0.3, + rArm: 0.27, + lArm: 0.27, + chest: 0.36, + abdomen: 0.3, + rLeg: 0.27, + lLeg: 0.27 + }) +}, Ut = { + id: "bandersnatchi", + label: "Bandersnatchi", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: oe([ + { slug: "rLeg", label: "Right Rear Mass", min: 1, max: 2 }, + { slug: "lLeg", label: "Left Rear Mass", min: 3, max: 4 }, + { slug: "abdomen", label: "Lower Body Mass", min: 5, max: 11 }, + { slug: "chest", label: "Upper Body Mass", min: 12, max: 17 }, + { slug: "rArm", label: "Right Manipulator", min: 18, max: 18 }, + { slug: "lArm", label: "Left Manipulator", min: 19, max: 19 }, + { slug: "head", label: "Head Cluster", min: 20, max: 20 } + ], { + head: 0.28, + chest: 0.42, + abdomen: 0.42, + rArm: 0.2, + lArm: 0.2, + rLeg: 0.24, + lLeg: 0.24 + }) +}, qt = { + id: "kzinti", + label: "Kzinti", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: oe([ + { slug: "rLeg", label: "Right Hind Leg", min: 1, max: 3 }, + { slug: "lLeg", label: "Left Hind Leg", min: 4, max: 6 }, + { slug: "abdomen", label: "Abdomen", min: 7, max: 10 }, + { slug: "chest", label: "Chest", min: 11, max: 15 }, + { slug: "rArm", label: "Right Foreleg", min: 16, max: 17 }, + { slug: "lArm", label: "Left Foreleg", min: 18, max: 19 }, + { slug: "head", label: "Head", min: 20, max: 20 } + ]) +}, lt = { + humanoid: rt, + dolphin: Et, + puppeteer: Bt, + trinoc: Ft, + bandersnatchi: Ut, + kzinti: qt +}; +function Kt() { + return Object.values(lt); +} +function Vt(e) { + const t = it(e); + return t ? t.includes("dolphin") ? "dolphin" : t.includes("puppeteer") ? "puppeteer" : t.includes("trinoc") ? "trinoc" : t.includes("bandersnatch") ? "bandersnatchi" : t.includes("kzinti") ? "kzinti" : "humanoid" : "humanoid"; +} +function Ne(e) { + const t = it(e); + return lt[t] ?? rt; +} +function se(e) { + var n, o, s; + const t = String(((n = e == null ? void 0 : e.system) == null ? void 0 : n.hitLocationSchema) || "auto").trim().toLowerCase(); + if (t && t !== "auto") + return Ne(t); + const a = ((s = (o = e == null ? void 0 : e.system) == null ? void 0 : o.profile) == null ? void 0 : s.species) ?? ""; + return Ne(Vt(a)); +} +function Gt(e) { + return e ? se(e).entries : Ce; +} +function jt(e, t) { + return t.find((n) => e >= n.min && e <= n.max) ?? t[t.length - 1]; +} +async function ct(e) { + const a = (await Me("1d20")).total ?? 20, n = Gt(e); + return { + roll: a, + location: jt(a, n) + }; +} +async function _t(e) { + const t = e ? se(e) : Ne("humanoid"), a = t.entries, n = new Map(a.map((r) => [r.slug, r])), o = (r, c) => { + var u; + return ((u = n.get(r)) == null ? void 0 : u.label) ?? c; + }, s = ` +
+ +
+ +
+
+ + + + + + + +
+
+ `, i = await new Promise((r) => { + let c = !1; + const u = (g) => { + c || (c = !0, r(g)); + }; + new Dialog({ + title: "Choose Hit Location", + content: s, + buttons: { + apply: { + label: "Apply", + callback: (g) => { + const m = String(g.find("input[name='location']").val() || "random"); + u(m); + } + }, + cancel: { + label: "Cancel", + callback: () => u(null) + } + }, + default: "apply", + render: (g) => { + const m = g.find(".ringworld-hit-picker"); + m.find(".rw-hit-picker-choice").on("click", (f) => { + f.preventDefault(); + const y = f.currentTarget, b = String(y.dataset.location || "random"); + m.find(".rw-hit-picker-choice").removeClass("active"), $(y).addClass("active"), m.find("input[name='location']").val(b); + }); + }, + close: () => u(null) + }, { + classes: ["ringworld-hit-location-dialog"], + width: 500, + resizable: !1 + }).render(!0); + }); + if (!i) return null; + if (i === "random") { + const r = await ct(e); + return { + slug: r.location.slug, + label: r.location.label, + rolled: r.roll + }; + } + const l = a.find((r) => r.slug === i); + return l ? { + slug: l.slug, + label: l.label, + rolled: null + } : null; +} +const Wt = [ + "Agility", + "Communication", + "Knowledge", + "Perception", + "Technical" +], we = [ + { name: "Archaic Melee Weapon", category: "Agility", base: 5 }, + { name: "Archaic Ranged Weapons", category: "Agility", base: 3 }, + { name: "Athletics", category: "Agility", base: 15 }, + { name: "Hide", category: "Agility", base: 10 }, + { name: "Sneak", category: "Agility", base: 5 }, + { name: "Unarmed Combat", category: "Agility", base: 0 }, + { name: "V. Sword, F. Laser", category: "Agility", base: 15 }, + { name: "Bargain", category: "Communication", base: 10 }, + { name: "Debate", category: "Communication", base: 5 }, + { name: "Fast Talk", category: "Communication", base: 10 }, + { name: "Fine Arts", category: "Communication", base: 5 }, + { name: "Musicianship", category: "Communication", base: 5 }, + { name: "Orate", category: "Communication", base: 5 }, + { name: "Own Language", category: "Communication", base: 0, specialization: "INTx5 base" }, + { name: "Perform", category: "Communication", base: 5 }, + { name: "Psychology", category: "Communication", base: 0 }, + { name: "Anthropology", category: "Knowledge", base: 0 }, + { name: "Astronomy", category: "Knowledge", base: 0 }, + { name: "Biology", category: "Knowledge", base: 0 }, + { name: "Botany", category: "Knowledge", base: 0 }, + { name: "Chemistry", category: "Knowledge", base: 0 }, + { name: "Computers", category: "Knowledge", base: 0 }, + { name: "Emergency Treatment", category: "Knowledge", base: 1 }, + { name: "Engineering", category: "Knowledge", base: 0 }, + { name: "Farming", category: "Knowledge", base: 0 }, + { name: "History", category: "Knowledge", base: 5 }, + { name: "Law", category: "Knowledge", base: 0 }, + { name: "Mathematics", category: "Knowledge", base: 0 }, + { name: "Physics", category: "Knowledge", base: 0 }, + { name: "Planetology", category: "Knowledge", base: 0 }, + { name: "Second Languages", category: "Knowledge", base: 0 }, + { name: "Strategy", category: "Knowledge", base: 0 }, + { name: "Theology", category: "Knowledge", base: 0 }, + { name: "Zoology", category: "Knowledge", base: 0 }, + { name: "Handgun, Energy", category: "Perception", base: 5 }, + { name: "Handgun, Projectile", category: "Perception", base: 3 }, + { name: "Heavy Weapon, Energy", category: "Perception", base: 5 }, + { name: "Heavy Weapon, Projectile", category: "Perception", base: 3 }, + { name: "Listen", category: "Perception", base: 5 }, + { name: "Observe", category: "Perception", base: 5 }, + { name: "Scent", category: "Perception", base: 0 }, + { name: "Search", category: "Perception", base: 5 }, + { name: "Track", category: "Perception", base: 5 }, + { name: "Aquatic Vehicle", category: "Technical", base: 0 }, + { name: "Atmospheric Craft", category: "Technical", base: 0 }, + { name: "Ground Vehicle", category: "Technical", base: 0 }, + { name: "Hyperdrive", category: "Technical", base: 0 }, + { name: "Personal Flyer", category: "Technical", base: 15 }, + { name: "Reaction Drive", category: "Technical", base: 0 }, + { name: "Reactionless Drive", category: "Technical", base: 0 }, + { name: "Repair", category: "Technical", base: 0 }, + { name: "Ringworld", category: "Technical", base: 0 }, + { name: "Weapons System", category: "Technical", base: 0 } +]; +function R(e) { + return String(e || "").toLowerCase().replace(/[^a-z0-9]+/g, "").trim(); +} +function Yt() { + return new Set(we.map((e) => R(e.name))); +} +const W = "systems/ringworld/assets/icons/skill.svg"; +function zt(e) { + return String(e).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); +} +function ye(e) { + return `systems/ringworld/assets/icons/skill/${zt(e)}.svg`; +} +function $e(e) { + const t = R(e); + return we.find((a) => R(a.name) === t); +} +function Oe(e, t) { + const a = String(t || "").trim(); + return a && a !== "icons/svg/item-bag.svg" ? a : $e(e) ? ye(e) : W; +} +async function ut(e) { + var a; + const t = e.actor ? ChatMessage.getSpeaker({ actor: e.actor }) : ChatMessage.getSpeaker(); + await ChatMessage.create({ + user: (a = game.user) == null ? void 0 : a.id, + speaker: t, + content: e.content + }); +} +async function mt(e) { + var n; + const t = { + ...e, + actorImg: e.actorImg || ((n = e.actor) == null ? void 0 : n.img) || "icons/svg/mystery-man.svg", + skillImg: e.skillImg || W + }, a = await renderTemplate(`systems/${x}/templates/chat/skill-roll-card.hbs`, t); + await ut({ actor: e.actor, content: a }); +} +async function Xe(e) { + var n; + const t = { + ...e, + actorImg: e.actorImg || ((n = e.actor) == null ? void 0 : n.img) || "icons/svg/mystery-man.svg", + weaponImg: e.weaponImg || "icons/svg/sword.svg", + skillImg: e.skillImg || W, + hasTarget: e.hasTarget ?? !0 + }, a = await renderTemplate(`systems/${x}/templates/chat/attack-card.hbs`, t); + await ut({ actor: e.actor, content: a }); +} +function P(e, t = 0) { + const a = Number(e); + return Number.isFinite(a) ? a : t; +} +function Qt() { + var t, a; + const e = Array.from(((t = game.user) == null ? void 0 : t.targets) ?? []); + return e.length ? ((a = e[0]) == null ? void 0 : a.actor) ?? null : null; +} +async function Xt(e) { + var o; + const t = await ct(e), a = e ? ChatMessage.getSpeaker({ actor: e }) : ChatMessage.getSpeaker(), n = ` +
+

Hit Location

+

Roll: ${t.roll}

+

Location: ${t.location.label}

+
+ `; + await ChatMessage.create({ + user: (o = game.user) == null ? void 0 : o.id, + speaker: a, + content: n + }); +} +async function Ee(e, t, a) { + const n = await Ue(a), o = e.items.find( + (s) => s.type === "skill" && s.name.toLowerCase() === String(t).toLowerCase() + ); + await mt({ + actor: e, + actorName: e.name, + actorImg: e.img, + skillName: t, + skillImg: o == null ? void 0 : o.img, + roll: n.roll, + target: n.target, + outcomeLabel: Ae(n.outcome), + isSuccess: n.isSuccess, + isSpecial: n.isSpecial, + isFumble: n.isFumble + }); +} +function Zt(e, t) { + const a = t.system.linkedSkillId, n = a ? e.items.get(a) : null; + if ((n == null ? void 0 : n.type) === "skill") + return { name: n.name, target: P(n.system.value), img: n.img }; + const o = String(t.system.linkedSkill || "").trim(); + if (o) { + const s = e.items.find( + (i) => i.type === "skill" && i.name.toLowerCase() === o.toLowerCase() + ); + if (s) + return { name: s.name, target: P(s.system.value), img: s.img }; + } + return { + name: o || "Unlinked Skill", + target: 0 + }; +} +function Jt(e) { + return Math.max(0, Math.min(99, Math.floor(e))); +} +async function ea(e, t, a) { + var d; + const o = String(((d = t.system) == null ? void 0 : d.attackType) || "ranged") === "ranged", s = "full", i = foundry.utils.escapeHTML(String((e == null ? void 0 : e.name) || "Attacker")), l = foundry.utils.escapeHTML(String((t == null ? void 0 : t.name) || "Weapon")), r = foundry.utils.escapeHTML(String((e == null ? void 0 : e.img) || "icons/svg/mystery-man.svg")), c = foundry.utils.escapeHTML(String((t == null ? void 0 : t.img) || "icons/svg/sword.svg")), u = ` +
+
+ ${i} + ${l} +
+

${l}

+

${i} Attack Setup

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+

Ringworld rule support: unaimed ranged attacks use quarter skill, partial aim uses half skill, full aim uses full skill.

+
+ `; + return new Promise((g) => { + let m = !1; + const f = (y) => { + m || (m = !0, g(y)); + }; + new Dialog({ + title: `${t.name} - Attack Modifiers`, + content: u, + buttons: { + roll: { + label: "Roll Attack", + callback: (y) => { + var le, ce, ue, me, de, ge, pe; + const b = y[0], w = (ee) => b == null ? void 0 : b.querySelector(`[name="${ee}"]`), p = String(((le = w("aimMode")) == null ? void 0 : le.value) || s), h = p === "unaimed" || p === "partial" || p === "full" ? p : s, A = o ? { unaimed: 0.25, partial: 0.5, full: 1 }[h] : 1, S = P((ce = w("coverMod")) == null ? void 0 : ce.value, 0), I = P((ue = w("movementMod")) == null ? void 0 : ue.value, 0), M = P((me = w("rangeMod")) == null ? void 0 : me.value, 0), D = P((de = w("situationalMod")) == null ? void 0 : de.value, 0), T = P((ge = w("customMod")) == null ? void 0 : ge.value, 0), H = String(((pe = w("customLabel")) == null ? void 0 : pe.value) || "").trim(), L = S + I + M + D + T, F = a + L, X = Math.floor(F * A), J = Jt(X), N = [`Base ${a}`]; + if (S && N.push(`Cover ${S >= 0 ? "+" : ""}${S}`), I && N.push(`Movement ${I >= 0 ? "+" : ""}${I}`), M && N.push(`Range ${M >= 0 ? "+" : ""}${M}`), D && N.push(`Situational ${D >= 0 ? "+" : ""}${D}`), T && N.push(`${H || "Custom"} ${T >= 0 ? "+" : ""}${T}`), N.push(`Pre-Aim ${F}`), o) { + const ee = h === "unaimed" ? "Quarter" : h === "partial" ? "Half" : "Full"; + N.push(`Aim ${ee} x${A}`); + } + N.push(`Final ${J}`), f({ + finalTarget: J, + summary: N.join(" | "), + aimMode: h, + aimMultiplier: A, + flatModifierTotal: L + }); + } + }, + cancel: { + label: "Cancel", + callback: () => f(null) + } + }, + default: "roll", + close: () => f(null) + }, { + classes: ["ringworld-attack-dialog"], + width: 510, + resizable: !1 + }).render(!0); + }); +} +async function ta(e, t) { + var i, l, r, c, u, d; + const a = Math.max(0, P(t.system.energyUsedPerAttack)); + if (a <= 0) + return { sourceLabel: "None", remaining: 0, cost: 0, insufficient: !1 }; + if (String(t.system.powerSource || "actor") === "powerPack") { + const g = String(t.system.powerPackId || ""), m = g ? e.items.get(g) : null; + if (!m || m.type !== "gear" || !m.system.isPowerPack) + return (i = ui.notifications) == null || i.warn("Weapon is configured for a power pack but no valid power pack is linked."), { sourceLabel: "Power Pack", remaining: 0, cost: a, insufficient: !0 }; + const f = P((l = m.system.energy) == null ? void 0 : l.value); + if (f < a) + return (r = ui.notifications) == null || r.warn(`${m.name} does not have enough energy.`), { sourceLabel: m.name, remaining: f, cost: a, insufficient: !0 }; + const y = f - a; + return await e.updateEmbeddedDocuments("Item", [{ _id: m.id, "system.energy.value": y }]), { sourceLabel: m.name, remaining: y, cost: a, insufficient: !1 }; + } + const o = P((u = (c = e.system.resources) == null ? void 0 : c.energy) == null ? void 0 : u.value); + if (o < a) + return (d = ui.notifications) == null || d.warn(`${e.name} does not have enough actor energy.`), { sourceLabel: "Actor Energy", remaining: o, cost: a, insufficient: !0 }; + const s = o - a; + return await e.update({ "system.resources.energy.value": s }), { sourceLabel: "Actor Energy", remaining: s, cost: a, insufficient: !1 }; +} +function aa(e, t) { + var n, o; + if (e.type !== "armor" || !e.system.equipped || e.system.stowed || !((n = e.system.coverage) != null && n[t])) return 0; + const a = P((o = e.system.perLocation) == null ? void 0 : o[t]); + return a > 0 ? a : P(e.system.value); +} +function na(e, t) { + var o, s; + const a = P((s = (o = e.system.hitLocations) == null ? void 0 : o[t]) == null ? void 0 : s.armor), n = e.items.filter((i) => i.type === "armor").reduce((i, l) => i + aa(l, t), 0); + return a + n; +} +function oa(e, t, a, n, o) { + const s = foundry.utils.deepClone(e.system.conditions ?? {}); + return s.unusableLimbs = s.unusableLimbs ?? { rArm: !1, lArm: !1, rLeg: !1, lLeg: !1 }, a <= 0 && ((t === "head" || t === "chest") && (s.unconscious = !0, s.prone = !0), t === "abdomen" && (s.prone = !0, s.unusableLimbs.rLeg = !0, s.unusableLimbs.lLeg = !0), (t === "rArm" || t === "lArm") && (s.unusableLimbs[t] = !0), Nt.includes(t) && (s.unusableLimbs[t] = !0, s.prone = !0)), o <= 0 && (s.dying = !0), (t === "head" || t === "chest" || t === "abdomen") && a <= -n && (s.dying = !0), t === "head" && a <= -n && (s.dead = !0, s.unconscious = !0), s; +} +async function sa(e, t) { + const a = e.toggleStatusEffect; + if (typeof a == "function") + try { + await a.call(e, "prone", { active: !!t.prone }), await a.call(e, "unconscious", { active: !!t.unconscious }), await a.call(e, "dead", { active: !!t.dead }); + } catch { + } +} +async function ia(e, t, a) { + var f, y, b, w; + const n = (f = e.system.hitLocations) == null ? void 0 : f[t], o = P((y = n == null ? void 0 : n.hp) == null ? void 0 : y.value), s = Math.max(1, P((b = n == null ? void 0 : n.hp) == null ? void 0 : b.max, 1)), i = na(e, t), l = Math.max(0, a - i), r = o - l, c = Ot(t) ? Math.max(r, -s) : r, d = P((w = e.system.generalHp) == null ? void 0 : w.value) - l, g = oa(e, t, c, s, d), m = { + [`system.hitLocations.${t}.hp.value`]: c, + "system.generalHp.value": d, + "system.conditions": g + }; + return await e.update(m), await sa(e, g), { + armor: i, + finalDamage: l, + locationValue: c, + generalHpValue: d + }; +} +async function dt(e, t) { + var f; + const a = Qt(), n = !!a, o = (a == null ? void 0 : a.name) ?? "No Target", s = Zt(e, t), i = await ea(e, t, s.target); + if (!i) return; + const l = await ta(e, t); + if (l.insufficient) return; + const r = await Ue(i.finalTarget); + if (!r.isSuccess) { + const y = a ? P((f = a.system.generalHp) == null ? void 0 : f.value, 0) : 0; + await Xe({ + actor: e, + actorName: e.name, + actorImg: e.img, + targetName: o, + hasTarget: n, + weaponName: t.name, + weaponImg: t.img, + skillName: s.name, + skillImg: s.img, + attackRoll: r.roll, + attackTarget: r.target, + attackOutcomeLabel: Ae(r.outcome), + isSuccess: !1, + isSpecial: r.isSpecial, + isFumble: r.isFumble, + hitLocationLabel: "-", + hitLocationRoll: null, + rawDamage: 0, + armor: 0, + finalDamage: 0, + remainingLocationHp: 0, + remainingGeneralHp: y, + energySourceLabel: l.sourceLabel, + energyCost: l.cost, + energyRemaining: l.remaining, + notes: `Attack failed before damage was rolled. ${i.summary}` + }); + return; + } + const c = await _t(a ?? e); + if (!c) return; + const u = String(t.system.damageFormula || "0").trim() || "0", d = await Me(u), g = Math.max(0, P(d.total)), m = a ? await ia(a, c.slug, g) : { + armor: 0, + finalDamage: g, + locationValue: 0, + generalHpValue: 0 + }; + await Xe({ + actor: e, + actorName: e.name, + actorImg: e.img, + targetName: o, + hasTarget: n, + weaponName: t.name, + weaponImg: t.img, + skillName: s.name, + skillImg: s.img, + attackRoll: r.roll, + attackTarget: r.target, + attackOutcomeLabel: Ae(r.outcome), + isSuccess: r.isSuccess, + isSpecial: r.isSpecial, + isFumble: r.isFumble, + hitLocationLabel: c.label, + hitLocationRoll: c.rolled, + rawDamage: g, + armor: m.armor, + finalDamage: m.finalDamage, + remainingLocationHp: m.locationValue, + remainingGeneralHp: m.generalHpValue, + energySourceLabel: l.sourceLabel, + energyCost: l.cost, + energyRemaining: l.remaining, + notes: a ? `Location and general HP were both reduced by final damage, per Ringworld rules. ${i.summary}` : `No target selected: roll resolved with no damage application. ${i.summary}` + }); +} +function ra(e) { + var m, f, y, b, w, p, h, A; + if (e.type !== "explorer" && e.type !== "creature") return; + const t = P((f = (m = e.system) == null ? void 0 : m.characteristics) == null ? void 0 : f.con, 10), a = P((b = (y = e.system) == null ? void 0 : y.characteristics) == null ? void 0 : b.mas, 10), n = P((w = e.system.generalHp) == null ? void 0 : w.max, 0), o = P((p = e.system.generalHp) == null ? void 0 : p.value, 0), s = Math.max(1, Math.floor(t + a)); + e.system.generalHp = e.system.generalHp ?? { value: s, max: s }, e.system.generalHp.max = s; + const i = n > 0, l = i && o >= n, r = n <= 1 && o <= 1, c = i ? Math.max(0, n - o) : 0, u = !i || l || r ? s : Math.max(0, s - c); + e.system.generalHp.value = Math.min(Math.max(0, u), s); + const d = P(e.system.generalHp.value, s) >= s; + e.system.hitLocations = e.system.hitLocations ?? {}, e.system.hitLocationSchema = e.system.hitLocationSchema ?? "auto"; + const g = se(e); + for (const S of g.entries) { + const I = S.slug, M = e.system.hitLocations[I] ?? { + label: I, + hp: { value: 1, max: 1 }, + armor: 0 + }; + M.label = S.label; + const D = Math.max(0.05, P(S.hpFactor, 0.3)), T = Math.max(1, Math.ceil(s * D)), H = P((h = M.hp) == null ? void 0 : h.max, 1), L = P((A = M.hp) == null ? void 0 : A.value, H), F = H <= 1 && L <= 1, X = d && L <= 1 && T > 1; + M.hp.max = T, M.hp.value = F || X ? T : Math.min(L, T), e.system.hitLocations[I] = M; + } +} +const De = 9, _ = { + speciesDefault: "systems/ringworld/assets/icons/species.svg", + skillDefault: "systems/ringworld/assets/icons/skill.svg", + pursuitDefault: "systems/ringworld/assets/icons/pursuit.svg", + weaponMelee: "systems/ringworld/assets/icons/weapon-melee.svg", + weaponRanged: "systems/ringworld/assets/icons/weapon-ranged.svg", + armor: "systems/ringworld/assets/icons/armor.svg", + gearDefault: "systems/ringworld/assets/icons/gear.svg", + powerPackDefault: "systems/ringworld/assets/icons/power-pack.svg" +}; +function be(e) { + return String(e).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); +} +function la(e) { + return `systems/ringworld/assets/icons/species/${be(e)}.svg`; +} +function ca(e) { + return `systems/ringworld/assets/icons/weapon/${be(e)}.svg`; +} +function ua(e) { + return `systems/ringworld/assets/icons/armor/${be(e)}.svg`; +} +function ma(e) { + return `systems/ringworld/assets/icons/gear/${be(e)}.svg`; +} +function gt(e) { + return `systems/ringworld/assets/icons/pursuit/${be(e)}.svg`; +} +const Ze = { + rLeg: !0, + lLeg: !0, + abdomen: !0, + chest: !0, + rArm: !0, + lArm: !0, + head: !0 +}, Se = { + rLeg: !1, + lLeg: !1, + abdomen: !0, + chest: !0, + rArm: !1, + lArm: !1, + head: !1 +}, da = { + rLeg: !1, + lLeg: !1, + abdomen: !1, + chest: !1, + rArm: !1, + lArm: !1, + head: !0 +}, ga = { + rLeg: !1, + lLeg: !1, + abdomen: !1, + chest: !1, + rArm: !0, + lArm: !0, + head: !1 +}, pa = { + rLeg: !0, + lLeg: !0, + abdomen: !1, + chest: !1, + rArm: !1, + lArm: !1, + head: !1 +}, pt = [ + { + name: "Human", + playable: !0, + gravity: "normal", + homeworld: "Known Space", + modifiers: { str: 0, mas: 0, con: 0, int: 0, pow: 0, dex: 0, app: 0, edu: 0, age: 0 }, + notes: "Baseline human explorer profile." + }, + { + name: "Earth Human", + playable: !0, + gravity: "normal", + homeworld: "Earth", + modifiers: { str: 0, mas: 0, con: 0, int: 0, pow: 1, dex: 0, app: 0, edu: 0, age: 0 }, + notes: "Earth-line humans with stronger latent psionic potential." + }, + { + name: "Kzinti", + playable: !0, + gravity: "heavy", + homeworld: "Kzin", + modifiers: { str: 2, mas: 2, con: 1, int: -1, pow: 0, dex: 0, app: -2, edu: 0, age: 0 }, + notes: "Predatory felinoids with high physical strength." + }, + { + name: "Pierson's Puppeteer", + playable: !0, + gravity: "light", + homeworld: "Fleet of Worlds", + modifiers: { str: -2, mas: -2, con: -1, int: 2, pow: 1, dex: 1, app: 0, edu: 1, age: 0 }, + notes: "Highly intelligent, cautious engineers." + }, + { + name: "Dolphin", + playable: !0, + gravity: "normal", + homeworld: "Human Space Oceans", + modifiers: { str: -2, mas: 1, con: 1, int: 0, pow: 0, dex: 0, app: 0, edu: 0, age: 0 }, + notes: "Aquatic specialists with enhanced endurance." + }, + { + name: "Trinoc", + playable: !0, + gravity: "light", + homeworld: "Trinoc Worlds", + modifiers: { str: -1, mas: -1, con: 0, int: 1, pow: 0, dex: 1, app: 0, edu: 0, age: 0 }, + notes: "Three-eyed flyers with excellent perception." + }, + { + name: "Martian", + playable: !0, + gravity: "light", + homeworld: "Mars", + modifiers: { str: -1, mas: -1, con: -1, int: 1, pow: 0, dex: 1, app: 0, edu: 0, age: 0 }, + notes: "Light-gravity adapted intellect-focused profile." + }, + { + name: "Pierin", + playable: !0, + gravity: "normal", + homeworld: "Pierin Space", + modifiers: { str: 0, mas: 0, con: 0, int: 1, pow: 0, dex: 0, app: 1, edu: 0, age: 0 }, + notes: "Socially adept, technically curious explorers." + }, + { + name: "Boat People", + playable: !0, + gravity: "normal", + homeworld: "Ringworld", + modifiers: { str: 0, mas: 0, con: 1, int: 0, pow: 0, dex: 0, app: 0, edu: -1, age: 0 }, + notes: "Ringworld hominids adapted to river and sea cultures." + }, + { + name: "Builder", + playable: !0, + gravity: "normal", + homeworld: "Ringworld", + modifiers: { str: 1, mas: 1, con: 0, int: 0, pow: 0, dex: 0, app: -1, edu: 0, age: 0 }, + notes: "Ringworld labor stock with robust build." + }, + { + name: "Ghoul", + playable: !0, + gravity: "normal", + homeworld: "Ringworld", + modifiers: { str: 0, mas: 0, con: 1, int: -1, pow: 0, dex: 1, app: -2, edu: -1, age: 0 }, + notes: "Scavenger subspecies with strong survival instincts." + }, + { + name: "Bandersnatchi", + playable: !1, + gravity: "heavy", + homeworld: "Known Space", + modifiers: { str: 4, mas: 4, con: 3, int: 0, pow: 0, dex: -2, app: -3, edu: 0, age: 10 }, + notes: "Massive engineered beings; typically GM creature profile." + } +], fa = [ + { + name: "Variable Sword", + equipped: !0, + weight: 1.5, + linkedSkill: "V. Sword, F. Laser", + attackType: "melee", + damageFormula: "1d10+1", + range: { close: "Melee", medium: "-", long: "-" }, + energyUsedPerAttack: 1, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "High-energy blade weapon." + }, + { + name: "Monofilament Knife", + equipped: !0, + weight: 0.2, + linkedSkill: "Archaic Melee Weapon", + attackType: "melee", + damageFormula: "1d6+1", + range: { close: "Melee", medium: "-", long: "-" }, + energyUsedPerAttack: 0, + ammoUsedPerAttack: 0, + powerSource: "actor", + notes: "Fine-edge close combat blade." + }, + { + name: "Shock Baton", + equipped: !0, + weight: 1.2, + linkedSkill: "Unarmed Combat", + attackType: "melee", + damageFormula: "1d8+1", + range: { close: "Melee", medium: "-", long: "-" }, + energyUsedPerAttack: 1, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Close-quarters stun baton." + }, + { + name: "Flashlight Laser", + equipped: !0, + weight: 0.8, + linkedSkill: "V. Sword, F. Laser", + attackType: "ranged", + damageFormula: "1d10", + range: { close: "Short", medium: "Medium", long: "Long" }, + energyUsedPerAttack: 2, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Compact laser unit." + }, + { + name: "Laser Pistol", + equipped: !0, + weight: 1.4, + linkedSkill: "Handgun, Energy", + attackType: "ranged", + damageFormula: "1d10+1", + range: { close: "Short", medium: "Medium", long: "Long" }, + energyUsedPerAttack: 2, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Common energy sidearm." + }, + { + name: "Laser Rifle", + equipped: !0, + weight: 4.5, + linkedSkill: "Heavy Weapon, Energy", + attackType: "ranged", + damageFormula: "2d6+2", + range: { close: "Short", medium: "Long", long: "Extreme" }, + energyUsedPerAttack: 3, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Standard long-arm laser weapon." + }, + { + name: "Stunner", + equipped: !0, + weight: 1, + linkedSkill: "Handgun, Energy", + attackType: "ranged", + damageFormula: "1d6", + range: { close: "Short", medium: "Medium", long: "Long" }, + energyUsedPerAttack: 1, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Low-lethality disruption sidearm." + }, + { + name: "Handgun (Projectile)", + equipped: !0, + weight: 1.2, + linkedSkill: "Handgun, Projectile", + attackType: "ranged", + damageFormula: "1d10", + range: { close: "Short", medium: "Medium", long: "Long" }, + energyUsedPerAttack: 0, + ammoUsedPerAttack: 1, + powerSource: "actor", + notes: "Conventional projectile sidearm." + }, + { + name: "Rifle (Projectile)", + equipped: !0, + weight: 5, + linkedSkill: "Heavy Weapon, Projectile", + attackType: "ranged", + damageFormula: "2d6", + range: { close: "Short", medium: "Long", long: "Extreme" }, + energyUsedPerAttack: 0, + ammoUsedPerAttack: 1, + powerSource: "actor", + notes: "Conventional projectile rifle." + }, + { + name: "Slug Thrower Carbine", + equipped: !0, + weight: 3.8, + linkedSkill: "Heavy Weapon, Projectile", + attackType: "ranged", + damageFormula: "2d6+1", + range: { close: "Short", medium: "Long", long: "Extreme" }, + energyUsedPerAttack: 0, + ammoUsedPerAttack: 1, + powerSource: "actor", + notes: "Compact projectile carbine." + } +], ya = [ + { name: "Cloth Vest", equipped: !0, weight: 2, value: 1, coverage: Se, notes: "Basic torso protection." }, + { name: "Soft Combat Vest", equipped: !0, weight: 4, value: 2, coverage: Se, notes: "Flexible tactical vest." }, + { name: "Combat Harness", equipped: !0, weight: 5, value: 2, coverage: { ...Se, rArm: !0, lArm: !0 }, notes: "Torso + upper arms." }, + { name: "Armored Coat", equipped: !0, weight: 7, value: 3, coverage: { ...Se, rArm: !0, lArm: !0 }, notes: "Heavy coat with arm plating." }, + { name: "Leg Guards", equipped: !0, weight: 4, value: 2, coverage: pa, notes: "Lower-body armor." }, + { name: "Arm Bracers", equipped: !0, weight: 2, value: 2, coverage: ga, notes: "Forearm and upper arm guards." }, + { name: "Combat Helmet", equipped: !0, weight: 1.5, value: 2, coverage: da, notes: "Cranial protection." }, + { name: "Full Combat Suit", equipped: !0, weight: 12, value: 3, coverage: Ze, notes: "Full-body armored suit." }, + { name: "Impact Suit", equipped: !0, weight: 16, value: 4, coverage: Ze, notes: "High-end impact and hazard suit." } +], ha = [ + { + name: "Power Pack R20", + weight: 1, + cost: 100, + energyUse: 0, + powerSupplyNotes: "Rechargeable power pack", + isPowerPack: !0, + energy: { value: 20, max: 20 }, + notes: "Standard personal battery pack." + }, + { + name: "Power Pack R50", + weight: 2, + cost: 220, + energyUse: 0, + powerSupplyNotes: "Rechargeable power pack", + isPowerPack: !0, + energy: { value: 50, max: 50 }, + notes: "Extended personal battery pack." + }, + { + name: "Power Pack R100", + weight: 4, + cost: 400, + energyUse: 0, + powerSupplyNotes: "Rechargeable power pack", + isPowerPack: !0, + energy: { value: 100, max: 100 }, + notes: "High-capacity field battery." + }, + { name: "Portable Generator", weight: 8, cost: 700, energyUse: 0, powerSupplyNotes: "Provides charging capacity", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Portable recharge generator." }, + { name: "MicroMind Unit", weight: 0.5, cost: 900, energyUse: 1, powerSupplyNotes: "Requires external power", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Portable advanced computation module." }, + { name: "Memory Bubble Reader", weight: 0.5, cost: 450, energyUse: 1, powerSupplyNotes: "Standard battery compatible", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Reads and writes memory bubbles." }, + { name: "Autodoc (Portable)", weight: 18, cost: 3500, energyUse: 6, powerSupplyNotes: "High draw medical station", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Field medical support unit." }, + { name: "Medkit", weight: 1, cost: 120, energyUse: 0, powerSupplyNotes: "None", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Manual emergency treatment supplies." }, + { name: "Bioscanner", weight: 1, cost: 300, energyUse: 1, powerSupplyNotes: "Battery or generator", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Vitals and hazard scanning tool." }, + { name: "Survey Scanner", weight: 2, cost: 500, energyUse: 2, powerSupplyNotes: "Battery or generator", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Terrain and structure analysis scanner." }, + { name: "Translator Module", weight: 0.5, cost: 650, energyUse: 1, powerSupplyNotes: "Battery compatible", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Linguistic conversion and support." }, + { name: "Holo Projector", weight: 1.5, cost: 800, energyUse: 2, powerSupplyNotes: "Battery compatible", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Short-range holographic projection." }, + { name: "Stasis Box", weight: 12, cost: 2600, energyUse: 5, powerSupplyNotes: "Continuous power required", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Suspended animation container." }, + { name: "Reactionless Flycycle", weight: 35, cost: 4200, energyUse: 4, powerSupplyNotes: "Vehicle power core", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Single-rider reactionless vehicle." }, + { name: "Field Toolkit", weight: 2, cost: 180, energyUse: 0, powerSupplyNotes: "None", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Mechanical and electronics repair kit." }, + { name: "Vac Suit", weight: 7, cost: 1400, energyUse: 1, powerSupplyNotes: "Suit battery required", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Pressure and vacuum survival suit." }, + { name: "Air Reprocessor", weight: 3, cost: 950, energyUse: 2, powerSupplyNotes: "Continuous power", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Atmosphere recycling support unit." }, + { name: "Signal Beacon", weight: 1, cost: 140, energyUse: 1, powerSupplyNotes: "Battery compatible", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Emergency and navigation beacon." } +], he = [ + { + name: "Artist", + rollRange: "01-07", + wealthValue: 0, + occupationPointsPerYear: 20, + fixedSkills: ["Athletics", "Computers", "Fast Talk", "Fine Arts", "Hide", "Musicianship", "Observe", "Perform", "Psychology", "Scent", "Theology"], + notes: "Creative specialist pursuit." + }, + { + name: "Colonist", + rollRange: "08-27", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Atmospheric Craft", "Bargain", "Ground Vehicle", "Farming", "Handgun, Energy", "Personal Flyer", "Listen", "Heavy Weapon, Energy", "Observe", "Planetology", "Search", "Track"], + notes: "Settlement and frontier operations pursuit." + }, + { + name: "Criminal", + rollRange: "28-29", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Computers", "Fast Talk", "Hide", "Personal Flyer", "Handgun, Energy", "Handgun, Projectile", "Sneak", "Unarmed Combat"], + notes: "Underground survival pursuit." + }, + { + name: "Dilettante", + rollRange: "30", + wealthValue: 5, + occupationPointsPerYear: 15, + fixedSkills: [], + notes: "Any skills allowed; uses 15 occupation points per year." + }, + { + name: "Engineer", + rollRange: "31-36", + wealthValue: 2, + occupationPointsPerYear: 20, + fixedSkills: ["Astronomy", "Chemistry", "Computers", "Engineering", "Ground Vehicle", "Mathematics", "Personal Flyer", "Physics", "Planetology", "Reaction Drive", "Reactionless Drive", "Repair"], + notes: "Technical design and maintenance pursuit." + }, + { + name: "Entertainer", + rollRange: "37-38", + wealthValue: 2, + occupationPointsPerYear: 20, + fixedSkills: ["Athletics", "Bargain", "Own Language", "Musicianship", "Observe", "Orate", "Perform", "Psychology", "Search", "Sneak"], + notes: "Performance and social influence pursuit." + }, + { + name: "Explorer", + rollRange: "39-52", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Athletics", "Atmospheric Craft", "Bargain", "Biology", "Botany", "Emergency Treatment", "Engineering", "Ground Vehicle", "Handgun, Energy", "Heavy Weapon, Energy", "Hyperdrive", "Observe", "Planetology", "Reactionless Drive", "Repair", "Search", "Track", "Zoology"], + notes: "Survey and expedition pursuit." + }, + { + name: "Functionary", + rollRange: "53-56", + wealthValue: 3, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Debate", "Fast Talk", "History", "Law", "Observe", "Orate", "Psychology", "Strategy", "Theology"], + notes: "Administrative and governance pursuit." + }, + { + name: "Journalist/Writer", + rollRange: "57-59", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Anthropology", "Bargain", "Computers", "Debate", "History", "Fine Arts", "Own Language", "Law", "Observe", "Psychology", "Search", "Sneak", "Unarmed Combat", "Weapons System"], + notes: "Investigation and reporting pursuit." + }, + { + name: "Miner", + rollRange: "60-65", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Atmospheric Craft", "Aquatic Vehicle", "Bargain", "Climb (Custom)", "Computers", "Emergency Treatment", "Engineering", "Handgun, Energy", "Heavy Weapon, Energy", "Observe", "Personal Flyer", "Planetology", "Reaction Drive", "Reactionless Drive", "Repair", "Search"], + notes: "Resource extraction frontier pursuit." + }, + { + name: "Pilot", + rollRange: "66-68", + wealthValue: 3, + occupationPointsPerYear: 20, + fixedSkills: ["Astronomy", "Atmospheric Craft", "Computers", "Engineering", "Hyperdrive", "History", "Law", "Mathematics", "Physics", "Reaction Drive", "Reactionless Drive", "Repair", "Strategy", "Weapons System"], + notes: "Flight and spacecraft operations pursuit." + }, + { + name: "Police Officer", + rollRange: "69-76", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Atmospheric Craft", "Bargain", "Computers", "Fast Talk", "Emergency Treatment", "Handgun, Energy", "Handgun, Projectile", "Heavy Weapon, Energy", "Heavy Weapon, Projectile", "Law", "Observe", "Personal Flyer", "Psychology", "Search", "Unarmed Combat"], + notes: "Law enforcement and response pursuit." + }, + { + name: "Politician", + rollRange: "77-78", + wealthValue: 3, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Computers", "Debate", "Fast Talk", "History", "Orate", "Own Language", "Perform", "Psychology"], + optionalSkillGroups: [ + { + choose: 1, + skills: ["Engineering", "Farming", "Second Languages", "Strategy", "Theology"] + } + ], + notes: "Public office and influence pursuit." + }, + { + name: "Prisoner", + rollRange: "79-80", + wealthValue: 0, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Fast Talk", "Hide", "Observe", "Psychology", "Search", "Sneak", "Unarmed Combat"], + notes: "Carceral survival pursuit." + }, + { + name: "Scholar", + rollRange: "81-84", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: [], + optionalSkillGroups: [ + { + choose: 2, + skills: ["Anthropology", "Astronomy", "Biology", "Botany", "Chemistry", "Computers", "Engineering", "Fine Arts", "History", "Law", "Mathematics", "Own Language", "Physics", "Planetology", "Second Languages", "Theology", "Zoology"] + } + ], + notes: "Academic pursuit; pick up to two listed skills." + }, + { + name: "Scientific Researcher", + rollRange: "85-94", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: [], + optionalSkillGroups: [ + { + choose: 3, + skills: ["Anthropology", "Astronomy", "Bargain", "Biology", "Botany", "Chemistry", "Computers", "Engineering", "History", "Mathematics", "Observe", "Personal Flyer", "Physics", "Planetology", "Psychology", "Repair", "Search", "Zoology"] + } + ], + notes: "Research pursuit; pick up to three listed skills." + }, + { + name: "Soldier", + rollRange: "95-98", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Athletics", "Atmospheric Craft", "Aquatic Vehicle", "Computers", "Emergency Treatment", "Fast Talk", "Ground Vehicle", "Handgun, Energy", "Handgun, Projectile", "Heavy Weapon, Energy", "Heavy Weapon, Projectile"], + notes: "Military operations pursuit." + }, + { + name: "Zealot", + rollRange: "99-00", + wealthValue: 0, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Debate", "Fast Talk", "Fine Arts", "Handgun, Energy", "Handgun, Projectile", "Hide", "Own Language", "Law", "Strategy", "Orate", "Psychology", "Theology"], + notes: "Ideological dedication pursuit." + } +]; +function ft() { + return pt.filter((e) => e.playable); +} +function Q(e, t) { + return `${e}:${R(t)}`; +} +function wa(e) { + var s, i, l, r, c; + const t = (s = getDocumentClass("Item")) == null ? void 0 : s.TYPES; + if (Array.isArray(t)) return t.includes(e); + const a = (l = (i = game == null ? void 0 : game.system) == null ? void 0 : i.documentTypes) == null ? void 0 : l.Item; + if (Array.isArray(a)) return a.includes(e); + if (a && typeof a == "object") return Object.prototype.hasOwnProperty.call(a, e); + const n = (r = CONFIG == null ? void 0 : CONFIG.Item) == null ? void 0 : r.dataModels; + if (n && typeof n == "object" && Object.prototype.hasOwnProperty.call(n, e)) + return !0; + const o = (c = game == null ? void 0 : game.model) == null ? void 0 : c.Item; + return !!(o && typeof o == "object" && Object.prototype.hasOwnProperty.call(o, e)); +} +function ba(e) { + return { + key: Q("skill", e.name), + folderKey: "skills", + img: ye(e.name) || _.skillDefault, + data: { + name: e.name, + type: "skill", + system: { + value: e.base, + category: e.category, + specialization: e.specialization ?? "" + } + } + }; +} +function ka(e) { + const t = la(e.name) || _.speciesDefault; + return { + key: Q("species", e.name), + folderKey: "species", + img: t, + data: { + name: e.name, + type: "species", + system: { + playable: e.playable, + gravity: e.gravity, + homeworld: e.homeworld, + modifiers: e.modifiers, + notes: e.notes + } + } + }; +} +function va(e) { + const t = e.attackType === "melee" ? "weaponMelee" : "weaponRanged", a = ca(e.name) || (e.attackType === "melee" ? _.weaponMelee : _.weaponRanged); + return { + key: Q("weapon", e.name), + folderKey: t, + img: a, + data: { + name: e.name, + type: "weapon", + system: { + equipped: e.equipped ?? !0, + stowed: !1, + weight: e.weight ?? 0, + linkedSkill: e.linkedSkill, + linkedSkillId: "", + attackType: e.attackType, + damageFormula: e.damageFormula, + range: e.range, + energyUsedPerAttack: e.energyUsedPerAttack, + ammoUsedPerAttack: e.ammoUsedPerAttack, + powerSource: e.powerSource, + powerPackId: "", + notes: e.notes + } + } + }; +} +function Sa(e) { + const t = ua(e.name) || _.armor; + return { + key: Q("armor", e.name), + folderKey: "armor", + img: t, + data: { + name: e.name, + type: "armor", + system: { + equipped: e.equipped, + stowed: !1, + weight: e.weight ?? 0, + value: e.value, + coverage: e.coverage, + perLocation: { + rLeg: 0, + lLeg: 0, + abdomen: 0, + chest: 0, + rArm: 0, + lArm: 0, + head: 0 + }, + notes: e.notes + } + } + }; +} +function Aa(e) { + const t = ma(e.name) || (e.isPowerPack ? _.powerPackDefault : _.gearDefault); + return { + key: Q("gear", e.name), + folderKey: "gear", + img: t, + data: { + name: e.name, + type: "gear", + system: { + equipped: e.equipped ?? !0, + stowed: !1, + weight: e.weight, + cost: e.cost, + energyUse: e.energyUse, + powerSupplyNotes: e.powerSupplyNotes, + isPowerPack: e.isPowerPack, + energy: e.energy, + notes: e.notes + } + } + }; +} +function xa(e) { + const t = gt(e.name) || _.pursuitDefault; + return { + key: Q("pursuit", e.name), + folderKey: "pursuits", + img: t, + data: { + name: e.name, + type: "pursuit", + system: { + wealthValue: e.wealthValue, + occupationPointsPerYear: e.occupationPointsPerYear, + rollRange: e.rollRange, + fixedSkills: e.fixedSkills, + optionalSkillGroups: e.optionalSkillGroups ?? [], + notes: e.notes + } + } + }; +} +function Pa(e) { + const t = e.fixedSkills.length ? `Fixed skills: ${e.fixedSkills.join(", ")}` : "Fixed skills: (none)", a = (e.optionalSkillGroups ?? []).map((o) => `Choose ${o.choose}: ${o.skills.join(", ")}`).join(" | "), n = a ? ` Optional: ${a}.` : ""; + return `${e.notes} ${t}.${n} Wealth ${e.wealthValue}. Occupation points/year ${e.occupationPointsPerYear}.`; +} +function Ia(e) { + const t = gt(e.name) || _.pursuitDefault, a = `Pursuit: ${e.name}`; + return { + key: Q("gear", a), + folderKey: "pursuits", + img: t, + data: { + name: a, + type: "gear", + system: { + equipped: !1, + stowed: !0, + weight: 0, + cost: 0, + energyUse: 0, + powerSupplyNotes: "Pursuit reference", + isPowerPack: !1, + energy: { value: 0, max: 0 }, + notes: Pa(e) + }, + flags: { + ringworld: { + pursuitData: { + pursuitName: e.name, + rollRange: e.rollRange, + wealthValue: e.wealthValue, + occupationPointsPerYear: e.occupationPointsPerYear, + fixedSkills: e.fixedSkills, + optionalSkillGroups: e.optionalSkillGroups ?? [] + } + } + } + } + }; +} +async function G(e, t) { + const a = game.folders.find( + (n) => { + var o; + return n.type === "Item" && n.name === e && (((o = n.folder) == null ? void 0 : o.id) ?? null) === t; + } + ); + return a || Folder.create({ + name: e, + type: "Item", + folder: t, + sorting: "a" + }); +} +async function La() { + const e = await G("Ringworld Catalog", null), t = await G("Species", e.id), a = await G("Skills", e.id), n = await G("Pursuits", e.id), o = await G("Gear", e.id), s = await G("Armor", o.id), i = await G("Weapons", e.id), l = await G("Melee Weapons", i.id), r = await G("Ranged Weapons", i.id); + return { + species: t.id, + skills: a.id, + pursuits: n.id, + gear: o.id, + armor: s.id, + weaponMelee: l.id, + weaponRanged: r.id + }; +} +function Ma() { + const e = wa("pursuit"); + return [ + ...pt.map(ka), + ...we.map(ba), + ...e ? he.map(xa) : he.map(Ia), + ...fa.map(va), + ...ya.map(Sa), + ...ha.map(Aa) + ]; +} +async function Be(e) { + var i, l, r, c, u, d, g; + if (!((i = game.user) != null && i.isGM)) return { added: 0, updated: 0 }; + const t = await La(), a = Ma(), n = new Map( + game.items.map((m) => [Q(m.type, m.name), m]) + ), o = [], s = []; + for (const m of a) { + const f = t[m.folderKey], y = n.get(m.key); + if (!y) { + o.push({ + ...m.data, + img: m.img, + folder: f, + flags: { + ringworld: { + catalog: !0, + catalogKey: m.key + } + } + }); + continue; + } + const b = { _id: y.id }; + (((l = y.folder) == null ? void 0 : l.id) ?? null) !== f && (b.folder = f), String(y.img || "") !== m.img && (b.img = m.img), ((c = (r = y.flags) == null ? void 0 : r.ringworld) == null ? void 0 : c.catalog) !== !0 && (b["flags.ringworld.catalog"] = !0), ((d = (u = y.flags) == null ? void 0 : u.ringworld) == null ? void 0 : d.catalogKey) !== m.key && (b["flags.ringworld.catalogKey"] = m.key); + const w = foundry.utils.deepClone(y.system ?? {}), p = foundry.utils.mergeObject(w, m.data.system ?? {}, { + inplace: !1, + insertKeys: !0, + insertValues: !0, + overwrite: !1 + }), h = foundry.utils.diffObject(w, p); + Object.keys(h).length > 0 && (b.system = p), Object.keys(b).length > 1 && s.push(b); + } + return o.length && await Item.createDocuments(o), s.length && await Item.updateDocuments(s), e != null && e.notify && ((g = ui.notifications) == null || g.info(`Ringworld catalog synced: +${o.length} new, ${s.length} updated.`)), { added: o.length, updated: s.length }; +} +function yt() { + return game.items.filter((e) => e.type === "species" && e.system.playable); +} +function K(e) { + return Math.max(1, Math.min(99, Math.floor(e))); +} +async function j(e) { + const t = await Me(e); + return Number(t.total ?? 0); +} +async function Ca() { + let e = 10; + for (; ; ) { + const t = await j("1d6"); + if (e += t, t <= 4) break; + } + return K(e); +} +async function $a(e) { + const t = { + light: { str: "2d6+3", mas: "2d6+3", con: "2d6+6", dex: "2d6+9" }, + normal: { str: "2d6+6", mas: "2d6+6", con: "2d6+6", dex: "2d6+6" }, + heavy: { str: "2d6+9", mas: "2d6+9", con: "2d6+3", dex: "2d6+6" } + }[e]; + return { + str: await j(t.str), + mas: await j(t.mas), + con: await j(t.con), + int: await j("2d6+6"), + pow: await j("2d6+6"), + dex: await j(t.dex), + app: await j("2d6+6"), + edu: await Ca(), + age: await j("1d20+16") + }; +} +function xe(e) { + return e[Math.floor(Math.random() * e.length)]; +} +function Ra(e) { + const t = ["Ar", "Bel", "Cor", "Den", "Ela", "Far", "Gal", "Hel", "Ira", "Jun", "Kor", "Lys", "Mor", "Nyr", "Or", "Pra", "Qua", "Rin", "Sol", "Tor", "Uri", "Val", "Wen", "Xel", "Yor", "Zen"], a = ["an", "el", "is", "or", "us", "a", "en", "ion", "eth", "al", "ir", "os"]; + return `${xe(t)}${xe(a)} (${e})`; +} +function Ta(e, t) { + const a = (t == null ? void 0 : t.modifiers) ?? {}; + return { + str: K(e.str + Number(a.str || 0)), + mas: K(e.mas + Number(a.mas || 0)), + con: K(e.con + Number(a.con || 0)), + int: K(e.int + Number(a.int || 0)), + pow: K(e.pow + Number(a.pow || 0)), + dex: K(e.dex + Number(a.dex || 0)), + app: K(e.app + Number(a.app || 0)), + edu: K(e.edu + Number(a.edu || 0)), + age: K(e.age + Number(a.age || 0)) + }; +} +async function ht(e) { + var d, g, m, f; + const t = String(((g = (d = e.system) == null ? void 0 : d.profile) == null ? void 0 : g.gravity) || "normal"), a = t === "light" || t === "heavy" ? t : "normal", n = String(((f = (m = e.system) == null ? void 0 : m.profile) == null ? void 0 : f.species) || ""), s = yt().find((y) => y.name === n), i = ft().find((y) => y.name === n), l = (s == null ? void 0 : s.system) ?? i ?? null, r = await $a(a), c = Ta(r, l); + await e.update({ + "system.characteristics.str": c.str, + "system.characteristics.mas": c.mas, + "system.characteristics.con": c.con, + "system.characteristics.int": c.int, + "system.characteristics.pow": c.pow, + "system.characteristics.dex": c.dex, + "system.characteristics.app": c.app, + "system.characteristics.edu": c.edu, + "system.characteristics.age": c.age, + "system.profile.chronologicalAge": c.age, + "system.profile.physiologicalAge": c.age + }); + const u = e.items.find((y) => y.type === "skill" && y.name.toLowerCase() === "own language"); + u && await e.updateEmbeddedDocuments("Item", [{ + _id: u.id, + "system.value": Math.min(99, c.int * 5) + }]); +} +async function Da(e) { + const t = [ + ["weapon", "Laser Pistol"], + ["weapon", "Variable Sword"], + ["armor", "Soft Combat Vest"], + ["gear", "Power Pack R20"], + ["gear", "Medkit"], + ["gear", "Survey Scanner"] + ], a = game.items, n = new Set(e.items.map((s) => `${s.type}:${s.name.toLowerCase()}`)), o = []; + for (const [s, i] of t) { + if (n.has(`${s}:${String(i).toLowerCase()}`)) continue; + const l = a.find((r) => r.type === s && r.name === i); + l && o.push(l.toObject()); + } + o.length && await e.createEmbeddedDocuments("Item", o); +} +async function wt(e) { + var c; + const t = yt(), a = ft(), n = t.length ? xe(t) : null, o = n ? null : xe(a), s = (n == null ? void 0 : n.system) ?? o, i = (n == null ? void 0 : n.name) ?? (o == null ? void 0 : o.name) ?? "Human", l = String(s.gravity || "normal"), r = String(s.homeworld || "Ringworld"); + await e.update({ + name: Ra(i), + "system.profile.species": i, + "system.profile.gravity": l, + "system.profile.homeworld": r + }), await ht(e), await ((c = e.ensureBaseSkills) == null ? void 0 : c.call(e)), await Da(e); +} +async function bt() { + const t = await getDocumentClass("Actor").create({ + name: "Random Explorer", + type: "explorer" + }); + return await wt(t), t; +} +const Ha = ["str", "mas", "con", "int", "pow", "dex", "app", "edu", "age"]; +function v(e, t = 0) { + const a = Number(e); + return Number.isFinite(a) ? a : t; +} +function Na(e) { + return e != null && String(e).trim() !== ""; +} +function He(e) { + return Math.max(1, Math.min(99, Math.floor(e))); +} +function Oa(e, t) { + const a = e + t; + return a <= 12 ? "-1d6" : a <= 24 ? "-1d4" : a <= 32 ? "0" : a <= 40 ? "+1d4" : a <= 56 ? "+1d6" : a <= 72 ? "+2d6" : "+3d6"; +} +function Je(e) { + const t = v(e == null ? void 0 : e.con, 10), a = v(e == null ? void 0 : e.int, 10), n = v(e == null ? void 0 : e.pow, 10), o = v(e == null ? void 0 : e.dex, 10); + return { + health: Math.max(0, Math.min(99, t * 5)), + reasoning: Math.max(0, Math.min(99, a * 5)), + luck: Math.max(0, Math.min(99, n * 5)), + dodge: Math.max(0, Math.min(99, o * 5)) + }; +} +function Ea(e) { + const t = v(e == null ? void 0 : e.str, 10), a = v(e == null ? void 0 : e.dex, 10), n = v(e == null ? void 0 : e.app, 10), o = v(e == null ? void 0 : e.edu, 10), s = v(e == null ? void 0 : e.int, 10), i = v(e == null ? void 0 : e.pow, 10), l = v(e == null ? void 0 : e.con, 10); + return { + agility: Math.max(0, t + a), + communication: Math.max(0, s + n), + knowledge: Math.max(0, s + o), + perception: Math.max(0, i + l), + technical: Math.max(0, a + s) + }; +} +function Ba(e) { + return e <= 4 ? 7 : e <= 8 ? 6 : e <= 12 ? 5 : e <= 16 ? 4 : e <= 20 ? 3 : e <= 24 ? 2 : 1; +} +function Fa(e) { + return e === "light" ? 0.5 : e === "heavy" ? 2 : 1; +} +function Ua(e) { + var t; + return e.type !== "weapon" && e.type !== "armor" && e.type !== "gear" ? !1 : ((t = e.system) == null ? void 0 : t.stowed) !== !0; +} +function qa(e) { + var t; + return Math.max(0, v((t = e.system) == null ? void 0 : t.weight, 0)); +} +function Ka(e, t) { + var y, b, w, p; + const a = String(((y = e == null ? void 0 : e.profile) == null ? void 0 : y.gravity) || "normal"), n = Fa(a), o = t.filter((h) => h.type === "weapon" || h.type === "armor" || h.type === "gear").filter(Ua).reduce((h, A) => h + qa(A), 0), s = Math.max(0, o), i = s * n, l = Math.max(0, v((b = e == null ? void 0 : e.characteristics) == null ? void 0 : b.str, 0)), r = Math.max(0, i - l), c = r > 0 ? Math.ceil(r / 10) : 0, u = r > 0, d = Math.max(1, v((w = e == null ? void 0 : e.derived) == null ? void 0 : w.move, 3)), g = Math.max(1, v((p = e == null ? void 0 : e.derived) == null ? void 0 : p.run, d * 2)); + return { + rawLoadKg: s, + effectiveLoadKg: i, + limitKg: l, + overLimitKg: r, + actionRankingPenalty: c, + overLimit: u, + currentMove: u ? 2 : d, + currentRun: u ? 4 : g, + gravityMultiplier: n + }; +} +class Va extends Actor { + prepareDerivedData() { + if (super.prepareDerivedData(), ra(this), this.type !== "explorer" && this.type !== "creature") return; + const t = this.system ?? {}, a = t.characteristics ?? {}, n = t.profile ?? {}; + n.chronologicalAge = v(n.chronologicalAge, v(a.age, 25)), n.physiologicalAge = v(n.physiologicalAge, n.chronologicalAge), t.profile = n, t.combat = t.combat ?? {}, Na(t.combat.damageMod) || (t.combat.damageMod = Oa(v(a.str, 10), v(a.mas, 10))), t.derivedRolls = t.derivedRolls ?? {}; + const o = Je(a); + for (const l of Object.keys(o)) + t.derivedRolls[l] = v(t.derivedRolls[l], o[l]); + t.skillRootMax = t.skillRootMax ?? {}; + const s = Ea(a); + for (const [l, r] of Object.entries(s)) + t.skillRootMax[l] = r; + t.derived = t.derived ?? {}, t.derived.actionRanking = Ba(v(a.dex, 10)), t.derived.move = v(t.derived.move, 3), t.derived.run = v(t.derived.run, Math.max(1, v(t.derived.move, 3) * 2)), t.encumbrance = t.encumbrance ?? {}; + const i = Ka(t, this.items); + t.encumbrance.rawLoadKg = i.rawLoadKg, t.encumbrance.effectiveLoadKg = i.effectiveLoadKg, t.encumbrance.limitKg = i.limitKg, t.encumbrance.overLimitKg = i.overLimitKg, t.encumbrance.actionRankingPenalty = i.actionRankingPenalty, t.encumbrance.overLimit = i.overLimit, t.encumbrance.gravityMultiplier = i.gravityMultiplier, t.derived.currentMove = i.currentMove, t.derived.currentRun = i.currentRun, t.finance = t.finance ?? { money: 0, inBank: 0 }, t.finance.money = v(t.finance.money, 0), t.finance.inBank = v(t.finance.inBank, 0), t.notes = t.notes ?? { defects: "", habits: "" }, typeof t.sheetToolsUnlocked != "boolean" && (t.sheetToolsUnlocked = !1), t.characterBuilder = t.characterBuilder ?? { + pursuits: [], + allocations: {}, + baseBySkillId: {} + }, t.unconsciousLevel = v(t.unconsciousLevel, 0); + } + get skills() { + return this.items.filter((t) => t.type === "skill"); + } + get weapons() { + return this.items.filter((t) => t.type === "weapon"); + } + get armors() { + return this.items.filter((t) => t.type === "armor"); + } + get gears() { + return this.items.filter((t) => t.type === "gear"); + } + async rollSkillById(t) { + const a = this.items.get(t); + !a || a.type !== "skill" || await Ee(this, a.name, Number(a.system.value ?? 0)); + } + async rollHitLocation() { + await Xt(this); + } + async useWeaponById(t) { + const a = this.items.get(t); + !a || a.type !== "weapon" || await dt(this, a); + } + async ensureBaseSkills() { + if (this.type !== "explorer" && this.type !== "creature") return 0; + const t = new Set( + this.items.filter((n) => n.type === "skill").map((n) => R(n.name)) + ), a = we.filter((n) => !t.has(R(n.name))).map((n) => ({ + name: n.name, + type: "skill", + img: ye(n.name), + system: { + value: n.base, + category: n.category, + specialization: n.specialization ?? "" + } + })); + return a.length ? (await this.createEmbeddedDocuments("Item", a), a.length) : 0; + } + async rollCharacteristics() { + this.type === "explorer" && await ht(this); + } + async randomizeCharacter() { + this.type === "explorer" && await wt(this); + } + async applySpeciesItem(t) { + var y, b, w; + if (this.type !== "explorer" && this.type !== "creature") return; + const a = String((t == null ? void 0 : t.name) || "").trim(), n = (t == null ? void 0 : t.system) ?? {}, o = (n == null ? void 0 : n.modifiers) ?? {}, s = ((y = this.system) == null ? void 0 : y.speciesApplied) ?? {}, i = (s == null ? void 0 : s.modifiers) ?? {}, l = ((b = this.system) == null ? void 0 : b.characteristics) ?? {}, r = ((w = this.system) == null ? void 0 : w.profile) ?? {}, c = {}, u = { + str: 0, + mas: 0, + con: 0, + int: 0, + pow: 0, + dex: 0, + app: 0, + edu: 0, + age: 0 + }; + for (const p of Ha) { + const h = v(i == null ? void 0 : i[p], 0), A = v(o == null ? void 0 : o[p], 0); + u[p] = A; + const I = v(l == null ? void 0 : l[p], p === "age" ? 25 : 10) - h; + c[`system.characteristics.${p}`] = He(I + A); + } + const d = v(i == null ? void 0 : i.age, 0), g = u.age, m = v(r == null ? void 0 : r.chronologicalAge, v(l == null ? void 0 : l.age, 25)), f = v(r == null ? void 0 : r.physiologicalAge, m); + c["system.profile.chronologicalAge"] = He(m - d + g), c["system.profile.physiologicalAge"] = He(f - d + g), a && (c["system.profile.species"] = a), n != null && n.gravity && (c["system.profile.gravity"] = String(n.gravity)), (n == null ? void 0 : n.homeworld) !== void 0 && (c["system.profile.homeworld"] = String(n.homeworld || "")), c["system.speciesApplied"] = { + name: a, + modifiers: u + }, await this.update(c); + } + async rollDerivedCheck(t) { + var s, i, l; + if (this.type !== "explorer" && this.type !== "creature") return; + const a = { + health: "Health Roll", + reasoning: "Reasoning Roll", + luck: "Luck Roll", + dodge: "Dodge Roll" + }, n = Je(((s = this.system) == null ? void 0 : s.characteristics) ?? {}), o = v((l = (i = this.system) == null ? void 0 : i.derivedRolls) == null ? void 0 : l[t], n[t]); + await Ee(this, a[t], o); + } +} +class Ga extends Item { + async rollSkill() { + var a; + if (this.type !== "skill") return; + const t = this.actor; + if (!t) { + (a = ui.notifications) == null || a.warn("Skill must be owned by an Actor to roll."); + return; + } + await Ee(t, this.name, Number(this.system.value ?? 0)); + } + async useWeapon() { + var a; + if (this.type !== "weapon") return; + const t = this.actor; + if (!t) { + (a = ui.notifications) == null || a.warn("Weapon must be owned by an Actor to use."); + return; + } + await dt(t, this); + } +} +const ja = foundry.appv1.sheets.ActorSheet, Fe = { + Agility: "agility", + Communication: "communication", + Knowledge: "knowledge", + Perception: "perception", + Technical: "technical" +}, _a = { + Agility: "STR + DEX", + Communication: "INT + APP", + Knowledge: "INT + EDU", + Perception: "POW + CON", + Technical: "DEX + INT" +}, Wa = /* @__PURE__ */ new Set([ + R("Archaic Melee Weapon"), + R("Archaic Ranged Weapons") +]), Ya = R("Hyperdrive"); +function fe(e, t) { + return e.name.localeCompare(t.name); +} +function C(e, t = 0) { + const a = Number(e); + return Number.isFinite(a) ? a : t; +} +function B(e, t = 0) { + return Math.max(0, Math.floor(C(e, t))); +} +function Pe(e) { + return { + education: B(e == null ? void 0 : e.education, 0), + pursuits: B(e == null ? void 0 : e.pursuits, 0), + special: B(e == null ? void 0 : e.special, 0) + }; +} +function za(e) { + const t = String((e == null ? void 0 : e.name) || "").trim(), a = B(e == null ? void 0 : e.years, 0); + return !t || a <= 0 ? null : { name: t, years: a }; +} +function kt(e) { + const t = Array.isArray(e == null ? void 0 : e.pursuits) ? e.pursuits.map((o) => za(o)).filter((o) => !!o) : [], a = {}; + if (e != null && e.allocations && typeof e.allocations == "object") + for (const [o, s] of Object.entries(e.allocations)) { + if (!o) continue; + const i = Pe(s); + i.education + i.pursuits + i.special <= 0 || (a[o] = i); + } + const n = {}; + if (e != null && e.baseBySkillId && typeof e.baseBySkillId == "object") + for (const [o, s] of Object.entries(e.baseBySkillId)) + o && (n[o] = Math.max(0, B(s, 0))); + return { + pursuits: t, + allocations: a, + baseBySkillId: n + }; +} +function Qa(e) { + return !Wa.has(R(e)); +} +function Xa(e) { + return R(e) !== Ya; +} +function Za(e) { + const t = new Map( + he.map((n) => [R(n.name), n]) + ), a = /* @__PURE__ */ new Set(); + for (const n of e) { + const o = t.get(R(n.name)); + if (o) { + for (const s of o.fixedSkills || []) + a.add(R(s)); + for (const s of o.optionalSkillGroups || []) + for (const i of s.skills || []) + a.add(R(i)); + } + } + return a; +} +function Ja(e) { + const t = B(e == null ? void 0 : e.age, 0), a = B(e == null ? void 0 : e.edu, 0), n = B(e == null ? void 0 : e.int, 0), o = Math.max(0, (a - 10) * 20), s = Math.max(0, t - a - 6), i = s * 20, l = Math.max(0, n * 10); + return { + educationTotal: o, + pursuitsTotal: i, + specialTotal: l, + pursuitYearsTotal: s + }; +} +function Z(e, t) { + var d, g, m, f, y, b, w; + const a = Ja(((d = e.system) == null ? void 0 : d.characteristics) ?? {}), n = kt(((g = e.system) == null ? void 0 : g.characterBuilder) ?? {}), o = he.map((p) => p.name).sort((p, h) => p.localeCompare(h)), s = Za(n.pursuits), i = /* @__PURE__ */ new Map(); + let l = 0, r = 0, c = 0; + for (const p of t) { + const h = Pe(n.allocations[p.id]), A = h.education + h.pursuits + h.special, S = n.baseBySkillId[p.id], I = (m = $e(p.name)) == null ? void 0 : m.base, M = Math.max(0, C((f = p.system) == null ? void 0 : f.value, 0) - A), D = Math.max( + 0, + Number.isFinite(S) ? S : I ?? M + ), T = Fe[String(((y = p.system) == null ? void 0 : y.category) || "")] ?? "", H = T ? Math.max(0, B((w = (b = e.system) == null ? void 0 : b.skillRootMax) == null ? void 0 : w[T], 0)) : 99, L = Math.max(0, H - D), F = R(p.name), X = Qa(p.name), J = s.has(F), N = Xa(p.name); + i.set(p.id, { + allocation: h, + totalAllocated: A, + baseValue: D, + rootCap: H, + maxAllocation: L, + allowsEducation: X, + allowsPursuits: J, + allowsSpecial: N + }), l += h.education, r += h.pursuits, c += h.special; + } + const u = n.pursuits.reduce((p, h) => p + h.years, 0); + return { + state: n, + education: { + total: a.educationTotal, + spent: l, + remaining: a.educationTotal - l + }, + pursuits: { + total: a.pursuitsTotal, + spent: r, + remaining: a.pursuitsTotal - r + }, + special: { + total: a.specialTotal, + spent: c, + remaining: a.specialTotal - c + }, + pursuitYears: { + total: a.pursuitYearsTotal, + spent: u, + remaining: a.pursuitYearsTotal - u + }, + pursuitOptions: o, + pursuitAllowedSkills: s, + bySkillId: i + }; +} +function en(e, t) { + var n, o, s, i, l, r, c; + if (e.type !== "armor" || !((n = e.system) != null && n.equipped) || (o = e.system) != null && o.stowed || !((i = (s = e.system) == null ? void 0 : s.coverage) != null && i[t])) return 0; + const a = C((r = (l = e.system) == null ? void 0 : l.perLocation) == null ? void 0 : r[t], 0); + return a > 0 ? a : C((c = e.system) == null ? void 0 : c.value, 0); +} +function tn(e, t) { + var o, s, i; + const a = C((i = (s = (o = e.system) == null ? void 0 : o.hitLocations) == null ? void 0 : s[t]) == null ? void 0 : i.armor, 0), n = e.items.filter((l) => l.type === "armor").reduce((l, r) => l + en(r, t), 0); + return a + n; +} +function et(e, t, a) { + var l, r; + const n = se(e).entries.find((c) => c.slug === t), o = ((r = (l = e.system) == null ? void 0 : l.hitLocations) == null ? void 0 : r[t]) ?? {}, s = o.hp ?? {}, i = String(o.label ?? "").trim(); + return { + slug: t, + rollRange: a, + label: i || String((n == null ? void 0 : n.label) || t), + armor: C(o.armor, 0), + totalArmor: tn(e, t), + hp: { + value: C(s.value, 0), + max: C(s.max, 0) + } + }; +} +function tt(e) { + return e[Math.floor(Math.random() * e.length)]; +} +function an(e) { + return Math.max(0, Math.min(99, Math.floor(e))); +} +class nn extends ja { + isToolsUnlocked() { + var t, a; + return !!((a = (t = this.actor) == null ? void 0 : t.system) != null && a.sheetToolsUnlocked); + } + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "actor", "explorer"], + width: 930, + height: 1460, + tabs: [{ navSelector: ".tabs", contentSelector: ".sheet-body", initial: "overview" }] + }); + } + get template() { + return "systems/ringworld/templates/actor/explorer-sheet.hbs"; + } + async getData(t) { + var N, le, ce, ue, me, de, ge, pe, ee, Ve, Ge, je, _e, We, Ye, ze; + const a = await super.getData(t), n = this.actor; + (N = game.user) != null && N.isGM && n.isOwner && await ((le = n.ensureBaseSkills) == null ? void 0 : le.call(n)); + const o = n.items.filter((k) => k.type === "skill").sort(fe), s = Z(n, o), i = n.items.filter((k) => k.type === "weapon").sort(fe), l = n.items.filter((k) => k.type === "armor").sort(fe), r = n.items.filter((k) => k.type === "gear").sort(fe), c = game.items.filter((k) => k.type === "species" && k.system.playable).map((k) => k.name).sort(), u = Yt(), d = /* @__PURE__ */ new Map(); + for (const k of o) { + const O = R(k.name); + d.has(O) || d.set(O, k); + } + const g = n.system.skillRootMax ?? {}, m = Wt.map((k) => { + const O = Fe[k] ?? k.toLowerCase(), Te = we.filter((V) => V.category === k).map((V) => { + var Qe; + const U = d.get(R(V.name)), te = U ? s.bySkillId.get(U.id) : null; + return { + name: V.name, + base: V.base, + category: V.category, + specialization: V.specialization ?? "", + itemId: (U == null ? void 0 : U.id) ?? null, + icon: (U == null ? void 0 : U.img) || ye(V.name), + value: ((Qe = U == null ? void 0 : U.system) == null ? void 0 : Qe.value) ?? V.base, + missing: !U, + allocationTotal: (te == null ? void 0 : te.totalAllocated) ?? 0, + allocation: (te == null ? void 0 : te.allocation) ?? { education: 0, pursuits: 0, special: 0 } + }; + }); + return { + category: k, + rootKey: O, + rootFormula: _a[k] ?? "", + rootValue: C(g[O], 0), + rows: Te + }; + }), f = o.filter((k) => !u.has(R(k.name))).map((k) => { + const O = s.bySkillId.get(k.id); + return { + id: k.id, + name: k.name, + system: k.system, + img: Oe(k.name, k.img || W), + allocationTotal: (O == null ? void 0 : O.totalAllocated) ?? 0, + allocation: (O == null ? void 0 : O.allocation) ?? { education: 0, pursuits: 0, special: 0 } + }; + }), y = m.reduce( + (k, O) => k + O.rows.filter((Te) => Te.missing).length, + 0 + ), b = se(n), w = new Map(b.entries.map((k) => [k.slug, `${k.min}-${k.max}`])), h = b.entries.map((k) => k.slug).map( + (k) => et(n, k, w.get(k) ?? "") + ), S = b.bodyOrder.map( + (k) => et(n, k, w.get(k) ?? "") + ), I = ((ce = n.system) == null ? void 0 : ce.encumbrance) ?? {}, M = C(I.rawLoadKg, 0), D = C(I.effectiveLoadKg, M), T = C(I.limitKg, C((me = (ue = n.system) == null ? void 0 : ue.characteristics) == null ? void 0 : me.str, 0)), H = C(I.actionRankingPenalty, 0), L = C((ge = (de = n.system) == null ? void 0 : de.derived) == null ? void 0 : ge.actionRanking, 0), F = Math.max(1, L + H), X = `${M.toFixed(1)}kg on-person | ${D.toFixed(1)}kg effective / ${T.toFixed(1)}kg limit | AR +${H} (stowed ignored)`, J = `${C((ee = (pe = n.system) == null ? void 0 : pe.derived) == null ? void 0 : ee.currentMove, C((Ge = (Ve = n.system) == null ? void 0 : Ve.derived) == null ? void 0 : Ge.move, 3))}m / ${C((_e = (je = n.system) == null ? void 0 : je.derived) == null ? void 0 : _e.currentRun, C((Ye = (We = n.system) == null ? void 0 : We.derived) == null ? void 0 : Ye.run, 6))}m`; + return { + ...a, + system: n.system, + skills: o, + skillsByCategory: m, + customSkills: f, + missingBaseSkillsCount: y, + weapons: i, + armors: l, + gear: r, + speciesOptions: c, + hitLocations: h, + bodyLocations: S, + hitLocationSchemaOptions: Kt(), + currentHitLocationSchema: String(n.system.hitLocationSchema || "auto"), + activeHitLocationSchemaId: b.id, + activeHitLocationSchemaLabel: b.label, + sheetToolsUnlocked: !!((ze = n.system) != null && ze.sheetToolsUnlocked), + baseActionRanking: L, + effectiveActionRanking: F, + encumbranceSummary: X, + movementSummary: J, + builder: { + education: s.education, + pursuits: s.pursuits, + special: s.special, + pursuitYears: s.pursuitYears, + pursuitsList: s.state.pursuits, + pursuitOptions: s.pursuitOptions + } + }; + } + async updateBuilderState(t) { + await this.actor.update({ + "system.characterBuilder": { + pursuits: t.pursuits, + allocations: t.allocations, + baseBySkillId: t.baseBySkillId + } + }); + } + async syncSkillsFromBuilderState(t) { + var o, s, i, l, r, c; + const a = this.actor.items.filter((u) => u.type === "skill"), n = []; + for (const u of a) { + const d = Pe(t.allocations[u.id]), g = d.education + d.pursuits + d.special, m = Fe[String(((o = u.system) == null ? void 0 : o.category) || "")] ?? "", f = m ? Math.max(0, B((i = (s = this.actor.system) == null ? void 0 : s.skillRootMax) == null ? void 0 : i[m], 0)) : 99; + let y = t.baseBySkillId[u.id]; + if (!Number.isFinite(y)) { + const w = (l = $e(u.name)) == null ? void 0 : l.base; + y = Math.max(0, w ?? C((r = u.system) == null ? void 0 : r.value, 0) - g), t.baseBySkillId[u.id] = y; + } + const b = Math.max(0, Math.min(99, Math.min(f, y + g))); + C((c = u.system) == null ? void 0 : c.value, 0) !== b && n.push({ _id: u.id, "system.value": b }); + } + n.length && await this.actor.updateEmbeddedDocuments("Item", n); + } + async addOrUpdatePursuit(t, a) { + var c; + if (!this.isToolsUnlocked()) return; + const n = String(t || "").trim(), o = Math.max(0, Math.floor(Number(a) || 0)); + if (!n || o <= 0) { + (c = ui.notifications) == null || c.warn("Choose a pursuit and set years above 0."); + return; + } + const s = this.actor.items.filter((u) => u.type === "skill"), i = Z(this.actor, s), l = foundry.utils.deepClone(i.state), r = l.pursuits.findIndex((u) => R(u.name) === R(n)); + r >= 0 ? l.pursuits[r].years = o : l.pursuits.push({ name: n, years: o }), await this.updateBuilderState(l); + } + async removePursuit(t) { + if (!this.isToolsUnlocked()) return; + const a = this.actor.items.filter((s) => s.type === "skill"), n = Z(this.actor, a), o = foundry.utils.deepClone(n.state); + t < 0 || t >= o.pursuits.length || (o.pursuits.splice(t, 1), await this.updateBuilderState(o)); + } + async resetBuilderAllocations() { + if (!this.isToolsUnlocked()) return; + const t = this.actor.items.filter((o) => o.type === "skill"), a = Z(this.actor, t), n = foundry.utils.deepClone(a.state); + n.allocations = {}, await this.updateBuilderState(n), await this.syncSkillsFromBuilderState(n); + } + async randomizeBuilderAllocations() { + var f, y, b, w; + if (!this.isToolsUnlocked()) return; + await ((y = (f = this.actor).ensureBaseSkills) == null ? void 0 : y.call(f)); + const t = this.actor.items.filter((p) => p.type === "skill").sort(fe), a = Z(this.actor, t), n = foundry.utils.deepClone(a.state), o = Math.max(0, a.pursuitYears.total), s = he.map((p) => p.name); + if (n.pursuits = [], o > 0 && s.length > 0) { + const p = Math.min( + s.length, + Math.max(1, Math.min(3, Math.floor(o / 2) || 1)) + ), h = [...s]; + for (let S = 0; S < p && h.length; S += 1) { + const I = Math.floor(Math.random() * h.length); + n.pursuits.push({ name: h[I], years: 1 }), h.splice(I, 1); + } + let A = o - n.pursuits.length; + for (; A > 0 && n.pursuits.length; ) { + const S = tt(n.pursuits); + S.years += 1, A -= 1; + } + } + const i = kt(n); + i.allocations = {}; + const l = Z( + { ...this.actor, system: { ...this.actor.system, characterBuilder: i } }, + t + ); + for (const p of t) { + const h = l.bySkillId.get(p.id); + h && (i.baseBySkillId[p.id] = h.baseValue, i.allocations[p.id] = { education: 0, pursuits: 0, special: 0 }); + } + const r = /* @__PURE__ */ new Map(); + for (const p of t) + r.set(p.id, 0); + const c = (p) => { + const h = l.bySkillId.get(p); + return h ? Math.max(0, h.maxAllocation) : 0; + }, u = (p, h, A) => { + let S = Math.max(0, p), I = 0; + for (; S > 0 && I < 3e5; ) { + I += 1; + const M = t.filter((H) => { + const L = l.bySkillId.get(H.id); + return !L || !A(H, L) ? !1 : (r.get(H.id) ?? 0) < c(H.id); + }); + if (!M.length) break; + const D = tt(M), T = i.allocations[D.id] ?? { education: 0, pursuits: 0, special: 0 }; + T[h] += 1, i.allocations[D.id] = T, r.set(D.id, (r.get(D.id) ?? 0) + 1), S -= 1; + } + return S; + }, d = u( + Math.max(0, l.education.total), + "education", + (p, h) => h.allowsEducation + ), g = u( + Math.max(0, l.pursuits.total), + "pursuits", + (p, h) => h.allowsPursuits + ), m = u( + Math.max(0, l.special.total), + "special", + (p, h) => h.allowsSpecial + ); + for (const p of t) { + const h = Pe(i.allocations[p.id]); + h.education + h.pursuits + h.special <= 0 && delete i.allocations[p.id]; + } + await this.updateBuilderState(i), await this.syncSkillsFromBuilderState(i), d > 0 || g > 0 || m > 0 ? (b = ui.notifications) == null || b.info( + `Random build complete. Unspent points: EDU ${d}, Pursuits ${g}, Special ${m}.` + ) : (w = ui.notifications) == null || w.info("Random build complete. All available points allocated."); + } + async promptSkillAllocation(t) { + var y, b, w; + if (!this.isToolsUnlocked()) return; + const a = this.actor.items.get(t); + if (!a || a.type !== "skill") return; + const n = this.actor.items.filter((p) => p.type === "skill"), o = Z(this.actor, n), s = o.bySkillId.get(t); + if (!s) return; + const i = s.allocation, l = Math.max(0, o.education.remaining + i.education), r = Math.max(0, o.pursuits.remaining + i.pursuits), c = Math.max(0, o.special.remaining + i.special), u = Math.max(0, s.maxAllocation), d = ` +
+
+ +
+

${a.name}

+

Base ${s.baseValue}% | Root cap ${s.rootCap}% | Current ${C((y = a.system) == null ? void 0 : y.value, 0)}%

+
+
+ + + + + + + +

Max additional from root cap: ${u}. Pools: EDU ${o.education.remaining}, Pursuits ${o.pursuits.remaining}, Special ${o.special.remaining}.

+
+ `, g = await new Promise((p) => { + new Dialog({ + title: `${a.name} - Allocate Character Points`, + content: d, + buttons: { + save: { + label: "Apply", + callback: (h) => { + const A = h.find("form")[0]; + if (!A) { + p(null); + return; + } + const S = new FormData(A); + p({ + education: s.allowsEducation ? B(S.get("education"), i.education) : i.education, + pursuits: s.allowsPursuits ? B(S.get("pursuits"), i.pursuits) : i.pursuits, + special: s.allowsSpecial ? B(S.get("special"), i.special) : i.special + }); + } + }, + cancel: { + label: "Cancel", + callback: () => p(null) + } + }, + default: "save", + close: () => p(null) + }).render(!0); + }); + if (!g) return; + const m = g.education + g.pursuits + g.special; + if (m > u) { + (b = ui.notifications) == null || b.warn(`Allocation exceeds this skill's root-cap allowance (${u}).`); + return; + } + if (g.education > l || g.pursuits > r || g.special > c) { + (w = ui.notifications) == null || w.warn("Allocation exceeds remaining pool points."); + return; + } + const f = foundry.utils.deepClone(o.state); + f.baseBySkillId[a.id] = s.baseValue, m <= 0 ? delete f.allocations[a.id] : f.allocations[a.id] = g, await this.updateBuilderState(f), await this.syncSkillsFromBuilderState(f); + } + async rollCharacteristicCheck(t, a) { + var c, u; + if (!(/* @__PURE__ */ new Set(["str", "mas", "con", "int", "pow", "dex", "app", "edu", "age"])).has(t)) return; + const o = Math.max(0, B((u = (c = this.actor.system) == null ? void 0 : c.characteristics) == null ? void 0 : u[t], 0)), s = ` +
+
+ + +
+
+ + +
+
+ + +
+

Roll d100 against Characteristic x Multiplier.

+
+ `, i = await new Promise((d) => { + let g = !1; + const m = (f) => { + g || (g = !0, d(f)); + }; + new Dialog({ + title: `${a} Check`, + content: s, + buttons: { + roll: { + label: "Roll", + callback: (f) => { + var w, p; + const y = Number(((p = (w = f[0]) == null ? void 0 : w.querySelector("select[name='multiplier']")) == null ? void 0 : p.value) || 1), b = Math.max(1, Math.min(7, Math.floor(y || 1))); + m(b); + } + }, + cancel: { + label: "Cancel", + callback: () => m(null) + } + }, + default: "roll", + close: () => m(null) + }).render(!0); + }); + if (!i) return; + const l = an(o * i), r = await Ue(l); + await mt({ + actor: this.actor, + actorName: this.actor.name, + actorImg: this.actor.img, + skillName: `${a} x${i}`, + skillImg: W, + roll: r.roll, + target: r.target, + outcomeLabel: Ae(r.outcome), + isSuccess: r.isSuccess, + isSpecial: r.isSpecial, + isFumble: r.isFumble + }); + } + activateListeners(t) { + super.activateListeners(t), t.find(".toggle-sheet-lock").on("click", async (a) => { + var o; + a.preventDefault(); + const n = !this.isToolsUnlocked(); + await this.actor.update({ "system.sheetToolsUnlocked": n }), (o = ui.notifications) == null || o.info(n ? "Sheet tools unlocked." : "Sheet tools locked."); + }), t.find(".roll-characteristic").on("click", async (a) => { + a.preventDefault(); + const n = a.currentTarget, o = String(n.dataset.char || "").trim().toLowerCase(), s = String(n.dataset.label || o.toUpperCase()); + o && await this.rollCharacteristicCheck(o, s); + }), t.find(".item-create").on("click", async (a) => { + a.preventDefault(); + const n = String(a.currentTarget.dataset.type || "gear"), o = { name: `New ${n}`, type: n }; + n === "skill" && (o.img = W, o.system = { value: 0, category: "Technical", specialization: "" }), await this.actor.createEmbeddedDocuments("Item", [o]); + }), t.find(".item-edit").on("click", (a) => { + var s; + a.preventDefault(); + const n = a.currentTarget.closest(".item"), o = n ? this.actor.items.get(n.dataset.itemId ?? "") : null; + (s = o == null ? void 0 : o.sheet) == null || s.render(!0); + }), t.find(".item-delete").on("click", async (a) => { + a.preventDefault(); + const n = a.currentTarget.closest(".item"); + n != null && n.dataset.itemId && await this.actor.deleteEmbeddedDocuments("Item", [n.dataset.itemId]); + }), t.find(".item-roll-skill").on("click", async (a) => { + a.preventDefault(); + const n = a.currentTarget.closest(".item"); + n != null && n.dataset.itemId && await this.actor.rollSkillById(n.dataset.itemId); + }), t.find(".roll-derived").on("click", async (a) => { + a.preventDefault(); + const n = String(a.currentTarget.dataset.check || ""); + n && await this.actor.rollDerivedCheck(n); + }), t.find(".item-use-weapon").on("click", async (a) => { + a.preventDefault(); + const n = a.currentTarget.closest(".item"); + n != null && n.dataset.itemId && await this.actor.useWeaponById(n.dataset.itemId); + }), t.find(".item-toggle-equipped").on("click", async (a) => { + var l; + a.preventDefault(); + const n = a.currentTarget.closest(".item"); + if (!(n != null && n.dataset.itemId)) return; + const o = this.actor.items.get(n.dataset.itemId); + if (!o) return; + const s = !((l = o.system) != null && l.equipped), i = { + _id: o.id, + "system.equipped": s + }; + s && (i["system.stowed"] = !1), await this.actor.updateEmbeddedDocuments("Item", [i]); + }), t.find(".item-toggle-stowed").on("click", async (a) => { + var l; + a.preventDefault(); + const n = a.currentTarget.closest(".item"); + if (!(n != null && n.dataset.itemId)) return; + const o = this.actor.items.get(n.dataset.itemId); + if (!o) return; + const s = !((l = o.system) != null && l.stowed), i = { + _id: o.id, + "system.stowed": s + }; + s && (i["system.equipped"] = !1), await this.actor.updateEmbeddedDocuments("Item", [i]); + }), t.find(".roll-hit-location").on("click", async (a) => { + a.preventDefault(), await this.actor.rollHitLocation(); + }), t.find(".reset-conditions").on("click", async (a) => { + a.preventDefault(), await this.actor.update({ + "system.conditions.prone": !1, + "system.conditions.unconscious": !1, + "system.conditions.dead": !1, + "system.conditions.dying": !1, + "system.conditions.unusableLimbs.rArm": !1, + "system.conditions.unusableLimbs.lArm": !1, + "system.conditions.unusableLimbs.rLeg": !1, + "system.conditions.unusableLimbs.lLeg": !1 + }); + }), t.find(".populate-base-skills").on("click", async (a) => { + var o, s; + a.preventDefault(); + const n = await this.actor.ensureBaseSkills(); + n > 0 ? (o = ui.notifications) == null || o.info(`Added ${n} Ringworld base skills.`) : (s = ui.notifications) == null || s.info("All Ringworld base skills are already present."); + }), t.find(".add-base-skill").on("click", async (a) => { + a.preventDefault(); + const n = a.currentTarget, o = String(n.dataset.skillName || ""), s = String(n.dataset.skillCategory || ""), i = Number(n.dataset.skillBase || 0), l = String(n.dataset.skillSpecialization || ""); + o && await this.actor.createEmbeddedDocuments("Item", [{ + name: o, + type: "skill", + img: ye(o), + system: { value: i, category: s, specialization: l } + }]); + }), t.find(".builder-add-pursuit").on("click", async (a) => { + var i, l; + a.preventDefault(); + const n = a.currentTarget.closest(".rw-builder-pursuits"); + if (!n) return; + const o = String(((i = n.querySelector(".builder-new-pursuit")) == null ? void 0 : i.value) || ""), s = Number(((l = n.querySelector(".builder-new-years")) == null ? void 0 : l.value) || 0); + await this.addOrUpdatePursuit(o, s); + }), t.find(".builder-update-pursuit").on("click", async (a) => { + var i, l; + a.preventDefault(); + const n = a.currentTarget.closest(".builder-pursuit-row"); + if (!n) return; + const o = String(((i = n.querySelector(".builder-pursuit-name")) == null ? void 0 : i.value) || ""), s = Number(((l = n.querySelector(".builder-pursuit-years")) == null ? void 0 : l.value) || 0); + await this.addOrUpdatePursuit(o, s); + }), t.find(".builder-remove-pursuit").on("click", async (a) => { + a.preventDefault(); + const n = Number(a.currentTarget.dataset.index ?? -1); + !Number.isFinite(n) || n < 0 || await this.removePursuit(n); + }), t.find(".builder-reset-points").on("click", async (a) => { + var n; + a.preventDefault(), await this.resetBuilderAllocations(), (n = ui.notifications) == null || n.info("Character-builder allocations reset."); + }), t.find(".roll-characteristics").on("click", async (a) => { + var n, o, s; + a.preventDefault(), this.isToolsUnlocked() && (await ((o = (n = this.actor).rollCharacteristics) == null ? void 0 : o.call(n)), (s = ui.notifications) == null || s.info("Characteristics rolled.")); + }), t.find(".builder-auto-allocate").on("click", async (a) => { + var n, o; + a.preventDefault(), this.isToolsUnlocked() && (await ((o = (n = this.actor).ensureBaseSkills) == null ? void 0 : o.call(n)), await this.randomizeBuilderAllocations()); + }), t.find(".builder-skill-allocate").on("click", async (a) => { + a.preventDefault(); + const n = a.currentTarget.closest(".item"), o = String((n == null ? void 0 : n.dataset.itemId) || ""); + o && await this.promptSkillAllocation(o); + }); + } + async _onDropItemCreate(t) { + var i, l, r; + const a = Array.isArray(t) ? t : [t], n = a.filter((c) => (c == null ? void 0 : c.type) === "species"), o = a.filter((c) => (c == null ? void 0 : c.type) !== "species"); + for (const c of n) + await ((l = (i = this.actor).applySpeciesItem) == null ? void 0 : l.call(i, c)); + if (n.length > 0) { + const c = n.map((u) => String((u == null ? void 0 : u.name) || "Species")).join(", "); + (r = ui.notifications) == null || r.info(`Applied species to actor: ${c}`); + } + if (o.length === 0) return []; + const s = o.map((c) => (c == null ? void 0 : c.type) !== "skill" ? c : { + ...c, + img: Oe(String((c == null ? void 0 : c.name) || "Skill"), String((c == null ? void 0 : c.img) || "")) + }); + return super._onDropItemCreate(Array.isArray(t) ? s : s[0]); + } +} +const on = foundry.appv1.sheets.ActorSheet; +class sn extends on { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "actor", "creature"], + width: 760, + height: 700 + }); + } + get template() { + return "systems/ringworld/templates/actor/creature-sheet.hbs"; + } + async getData(t) { + const a = await super.getData(t), n = this.actor; + return { + ...a, + system: n.system, + skills: n.items.filter((o) => o.type === "skill"), + weapons: n.items.filter((o) => o.type === "weapon"), + hitLocations: se(n).entries.map((o) => { + var s; + return { + slug: o.slug, + rollRange: `${o.min}-${o.max}`, + label: o.label, + ...((s = n.system.hitLocations) == null ? void 0 : s[o.slug]) ?? {} + }; + }) + }; + } + activateListeners(t) { + super.activateListeners(t), t.find(".item-create").on("click", async (a) => { + a.preventDefault(); + const n = String(a.currentTarget.dataset.type || "skill"); + await this.actor.createEmbeddedDocuments("Item", [{ name: `New ${n}`, type: n }]); + }), t.find(".item-edit").on("click", (a) => { + var s; + const n = a.currentTarget.closest(".item"), o = n ? this.actor.items.get(n.dataset.itemId ?? "") : null; + (s = o == null ? void 0 : o.sheet) == null || s.render(!0); + }), t.find(".item-delete").on("click", async (a) => { + const n = a.currentTarget.closest(".item"); + n != null && n.dataset.itemId && await this.actor.deleteEmbeddedDocuments("Item", [n.dataset.itemId]); + }), t.find(".item-roll-skill").on("click", async (a) => { + const n = a.currentTarget.closest(".item"); + n != null && n.dataset.itemId && await this.actor.rollSkillById(n.dataset.itemId); + }), t.find(".item-use-weapon").on("click", async (a) => { + const n = a.currentTarget.closest(".item"); + n != null && n.dataset.itemId && await this.actor.useWeaponById(n.dataset.itemId); + }), t.find(".roll-hit-location").on("click", async () => { + await this.actor.rollHitLocation(); + }); + } +} +const rn = foundry.appv1.sheets.ActorSheet; +class ln extends rn { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "actor", "vehicle"], + width: 680, + height: 560 + }); + } + get template() { + return "systems/ringworld/templates/actor/vehicle-sheet.hbs"; + } + async getData(t) { + return { + ...await super.getData(t), + system: this.actor.system + }; + } +} +const cn = foundry.appv1.sheets.ItemSheet; +class un extends cn { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "skill"], + width: 520, + height: 420 + }); + } + get template() { + return "systems/ringworld/templates/item/skill-sheet.hbs"; + } + async getData(t) { + return { + ...await super.getData(t), + system: this.item.system + }; + } + activateListeners(t) { + super.activateListeners(t), t.find(".roll-skill").on("click", async (a) => { + a.preventDefault(), await this.item.rollSkill(); + }); + } +} +const mn = foundry.appv1.sheets.ItemSheet; +class dn extends mn { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "weapon"], + width: 620, + height: 620 + }); + } + get template() { + return "systems/ringworld/templates/item/weapon-sheet.hbs"; + } + async getData(t) { + const a = await super.getData(t), n = this.item.actor; + return { + ...a, + system: this.item.system, + skills: n ? n.items.filter((o) => o.type === "skill") : [], + powerPacks: n ? n.items.filter((o) => o.type === "gear" && o.system.isPowerPack) : [] + }; + } + activateListeners(t) { + super.activateListeners(t), t.find(".use-weapon").on("click", async (a) => { + a.preventDefault(), await this.item.useWeapon(); + }); + } +} +const gn = foundry.appv1.sheets.ItemSheet; +class pn extends gn { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "armor"], + width: 620, + height: 560 + }); + } + get template() { + return "systems/ringworld/templates/item/armor-sheet.hbs"; + } + async getData(t) { + return { + ...await super.getData(t), + system: this.item.system + }; + } +} +const fn = foundry.appv1.sheets.ItemSheet; +class yn extends fn { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "gear"], + width: 620, + height: 520 + }); + } + get template() { + return "systems/ringworld/templates/item/gear-sheet.hbs"; + } + async getData(t) { + return { + ...await super.getData(t), + system: this.item.system + }; + } +} +const hn = foundry.appv1.sheets.ItemSheet; +class wn extends hn { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "species"], + width: 620, + height: 560 + }); + } + get template() { + return "systems/ringworld/templates/item/species-sheet.hbs"; + } + async getData(t) { + return { + ...await super.getData(t), + system: this.item.system + }; + } +} +const bn = foundry.appv1.sheets.ItemSheet; +function at(e) { + return Array.isArray(e) ? e.map((t) => String(t)) : []; +} +class kn extends bn { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "pursuit"], + width: 680, + height: 620 + }); + } + get template() { + return "systems/ringworld/templates/item/pursuit-sheet.hbs"; + } + async getData(t) { + const a = await super.getData(t), n = this.item.system ?? {}, o = at(n.fixedSkills), s = Array.isArray(n.optionalSkillGroups) ? n.optionalSkillGroups.map((i) => ({ + choose: Number((i == null ? void 0 : i.choose) ?? 1), + skills: at(i == null ? void 0 : i.skills) + })) : []; + return { + ...a, + system: n, + fixedSkills: o, + optionalGroups: s + }; + } +} +const vt = "impulseState", qe = `${x}.impulseHudPos`, vn = [ + { value: "draw", label: "Draw / Stow Weapon" }, + { value: "aim", label: "Aim Ranged Weapon" }, + { value: "melee", label: "Melee Attack" }, + { value: "stand", label: "Stand / Recover" }, + { value: "perception", label: "Perception Skill" }, + { value: "unarmed", label: "Unarmed Combat" } +], Sn = [ + { value: "fire", label: "Fire Ranged Weapon" }, + { value: "fall", label: "Fall" }, + { value: "rise", label: "Rise (Kneeling to Stand/Run)" }, + { value: "minorCustom", label: "Other Minor Action" } +]; +function q(e, t = 0) { + const a = Number(e); + return Number.isFinite(a) ? a : t; +} +function Y(e) { + return String(e ?? "").replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """); +} +function Ke(e) { + return e <= 4 ? 7 : e <= 8 ? 6 : e <= 12 ? 5 : e <= 16 ? 4 : e <= 20 ? 3 : e <= 24 ? 2 : 1; +} +function Re(e) { + switch (e) { + case "draw": + return "Draw / Stow"; + case "aim": + return "Aim"; + case "melee": + return "Melee Attack"; + case "stand": + return "Stand / Recover"; + case "perception": + return "Perception"; + case "unarmed": + return "Unarmed Combat"; + case "fire": + return "Fire Ranged Weapon"; + case "fall": + return "Fall"; + case "rise": + return "Rise"; + case "minorCustom": + return "Minor Action"; + default: + return String(e); + } +} +function z(e) { + var t, a, n; + return Math.max(1, q((n = (a = (t = e == null ? void 0 : e.actor) == null ? void 0 : t.system) == null ? void 0 : a.characteristics) == null ? void 0 : n.dex, 10)); +} +function St(e) { + var a, n; + const t = q((n = (a = e == null ? void 0 : e.flags) == null ? void 0 : a[x]) == null ? void 0 : n.actionRanking, 0); + return t > 0 ? t : Ke(z(e)); +} +function At(e) { + var a, n, o; + const t = q((o = (n = (a = e == null ? void 0 : e.actor) == null ? void 0 : a.system) == null ? void 0 : n.encumbrance) == null ? void 0 : o.actionRankingPenalty, 0); + return Math.max(0, Math.trunc(t)); +} +async function xt(e) { + var n, o; + const t = Ke(z(e)); + return q((o = (n = e == null ? void 0 : e.flags) == null ? void 0 : n[x]) == null ? void 0 : o.actionRanking, 0) !== t && await e.update({ [`flags.${x}.actionRanking`]: t }), t; +} +function ie(e) { + const t = foundry.utils.deepClone(e.getFlag(x, vt) ?? {}), a = Array.isArray(t.queue) ? t.queue : []; + return { + currentImpulse: Math.max(1, q(t.currentImpulse, 1)), + queue: a + }; +} +async function re(e, t) { + await e.setFlag(x, vt, { + currentImpulse: Math.max(1, q(t.currentImpulse, 1)), + queue: Array.isArray(t.queue) ? t.queue : [] + }); +} +function ae(e, t) { + return [...e].sort((a, n) => { + if (a.completeImpulse !== n.completeImpulse) return a.completeImpulse - n.completeImpulse; + const o = z(t.combatants.get(a.combatantId)); + return z(t.combatants.get(n.combatantId)) - o; + }); +} +function ke(e, t) { + const a = new Set(t.combatants.map((n) => n.id)); + return e.filter((n) => a.has(n.combatantId)); +} +function An() { + try { + const e = localStorage.getItem(qe); + if (!e) return null; + const t = JSON.parse(e), a = q(t == null ? void 0 : t.left, NaN), n = q(t == null ? void 0 : t.top, NaN); + return !Number.isFinite(a) || !Number.isFinite(n) ? null : { left: a, top: n }; + } catch { + return null; + } +} +function xn(e) { + try { + localStorage.setItem( + qe, + JSON.stringify({ left: Math.round(e.left), top: Math.round(e.top) }) + ); + } catch { + } +} +function Pn() { + try { + localStorage.removeItem(qe); + } catch { + } +} +function In(e, t) { + const n = t.offsetWidth || 300, o = t.offsetHeight || 220, s = Math.max(8, window.innerWidth - n - 8), i = Math.max(8, window.innerHeight - o - 8); + return { + left: Math.min(Math.max(8, e.left), s), + top: Math.min(Math.max(8, e.top), i) + }; +} +function nt(e, t) { + const a = e.get(0); + if (!a) return; + const n = In(t, a); + e.css({ + left: `${n.left}px`, + top: `${n.top}px`, + right: "auto", + bottom: "auto" + }); +} +function Ln(e) { + const t = e.get(0); + if (!t) return; + const a = e.find(".rw-imp-mini-head"); + if (!a.length) return; + const n = An(); + n && nt(e, n); + let o = !1, s = 0, i = 0, l = 0, r = 0; + a.on("mousedown", (c) => { + var g; + const u = c; + if (u.button !== 0 || (g = u.target) != null && g.closest("button")) return; + u.preventDefault(); + const d = t.getBoundingClientRect(); + s = u.clientX, i = u.clientY, l = d.left, r = d.top, o = !0, e.addClass("dragging"), e.css({ left: `${l}px`, top: `${r}px`, right: "auto", bottom: "auto" }), $(document).on("mousemove.ringworldImpulseHudDrag", (m) => { + if (!o) return; + const f = m, y = f.clientX - s, b = f.clientY - i; + nt(e, { left: l + y, top: r + b }); + }), $(document).one("mouseup.ringworldImpulseHudDrag", () => { + o = !1, e.removeClass("dragging"), $(document).off("mousemove.ringworldImpulseHudDrag"); + const m = t.getBoundingClientRect(); + xn({ left: m.left, top: m.top }); + }); + }), a.on("dblclick", () => { + Pn(), e.css({ left: "", top: "", right: "", bottom: "" }); + }); +} +function Pt(e, t) { + return ae(ke(t, e), e).map((a) => { + const n = e.combatants.get(a.combatantId); + return { + combatantId: a.combatantId, + name: String((n == null ? void 0 : n.name) || "Unknown"), + label: Re(a.kind), + completeImpulse: a.completeImpulse, + dex: z(n) + }; + }); +} +function It(e, t = 8) { + return e.length ? e.slice(0, t).map((a) => ` +
+ ${Y(a.name)} + ${Y(a.label)} @ ${a.completeImpulse} +
+ `).join("") : '
No actions queued.
'; +} +function Lt(e, t) { + var n; + const a = !((n = game.user) != null && n.isGM); + e.find(".rw-mini-next").prop("disabled", a).on("click", async (o) => { + o.preventDefault(), await Ie(t, "next"); + }), e.find(".rw-mini-step").prop("disabled", a).on("click", async (o) => { + o.preventDefault(), await Ie(t, "step"); + }), e.find(".rw-mini-reset").prop("disabled", a).on("click", async (o) => { + o.preventDefault(), await Mt(t); + }); +} +function Mn(e) { + var s; + const t = (s = ui == null ? void 0 : ui.chat) == null ? void 0 : s.element; + if (!(t != null && t.length) || (t.find(".ringworld-impulse-chat-marker").remove(), !e)) return; + const a = ie(e), n = Pt(e, a.queue), o = $(` +
+
+ Impulse ${a.currentImpulse} + Queued ${n.length} +
+
${It(n, 5)}
+
+ + + +
+
+ `); + Lt(o, e), t.prepend(o); +} +function Cn(e) { + if ($("#ringworld-impulse-hud").remove(), !e) return; + const a = ie(e), n = Pt(e, a.queue), o = $(` +
+
+ Impulse ${a.currentImpulse} + Queued ${n.length} +
+
${It(n, 8)}
+
+ + + +
+
+ `); + Lt(o, e), $("body").append(o), Ln(o); +} +function E(e) { + Mn(e), Cn(e); +} +async function ve() { + var e, t; + return (e = game.user) != null && e.isGM ? !0 : ((t = ui.notifications) == null || t.warn("Only the GM can modify Ringworld impulse sequencing."), !1); +} +async function ot(e, t, a, n) { + var w, p, h, A; + if (!await ve()) return; + const o = e.combatants.get(t); + if (!o) return; + const s = ie(e), i = s.currentImpulse, l = await xt(o), r = At(o), c = q((p = (w = o.flags) == null ? void 0 : w[x]) == null ? void 0 : p.impulseModifier, 0), u = Math.max(1, l + r), g = Math.max(1, (n ? u : 1) + c), m = i + g, f = { + id: foundry.utils.randomID(), + actorId: ((h = o.actor) == null ? void 0 : h.id) ?? null, + combatantId: t, + kind: a, + major: n, + startImpulse: i, + completeImpulse: m + }, y = ke( + s.queue.filter((S) => S.combatantId !== t), + e + ); + y.push(f), await re(e, { + currentImpulse: i, + queue: ae(y, e) + }), await o.update({ + [`flags.${x}.pendingEventId`]: f.id, + [`flags.${x}.pendingKind`]: a, + [`flags.${x}.actionRanking`]: l, + [`flags.${x}.effectiveActionRanking`]: u + }); + const b = n ? `AR ${l} + Enc ${r}${c ? ` ${c > 0 ? "+" : ""} Imp ${c}` : ""}` : `Minor 1${c ? ` ${c > 0 ? "+" : ""} Imp ${c}` : ""}`; + (A = ui.notifications) == null || A.info( + `${o.name}: ${Re(a)} completes on impulse ${m} (${b} => ${g}).` + ), E(e); +} +async function $n(e, t) { + if (!await ve()) return; + const a = e.combatants.get(t); + if (!a) return; + const n = ie(e), o = ke( + n.queue.filter((s) => s.combatantId !== t), + e + ); + await re(e, { + currentImpulse: n.currentImpulse, + queue: ae(o, e) + }), await a.update({ + [`flags.${x}.pendingEventId`]: null, + [`flags.${x}.pendingKind`]: "", + [`flags.${x}.aimState`]: { + active: !1, + targetId: null, + sinceImpulse: null + } + }), E(e); +} +async function Rn(e, t, a) { + var i; + const n = e.combatants.get(t.combatantId); + if (!n) return; + const o = { + [`flags.${x}.pendingEventId`]: null, + [`flags.${x}.pendingKind`]: "" + }; + t.kind === "aim" && (o[`flags.${x}.aimState`] = { + active: !0, + targetId: t.targetTokenId ?? null, + sinceImpulse: a + }), await n.update(o); + const s = ` +
+

Impulse ${a} Completion

+

${Y(n.name)} completes ${Y(Re(t.kind))}.

+

DEX ${z(n)} | AR ${St(n)}

+
+ `; + await ChatMessage.create({ + user: (i = game.user) == null ? void 0 : i.id, + speaker: ChatMessage.getSpeaker({ actor: n.actor ?? void 0 }), + content: s + }); +} +async function Ie(e, t) { + var c; + if (!await ve()) return; + const a = ie(e), n = ae(ke(a.queue, e), e), o = a.currentImpulse; + let s = o + 1; + if (t === "next") { + const u = n.map((d) => d.completeImpulse).filter((d) => d >= o).sort((d, g) => d - g)[0]; + Number.isFinite(u) && (s = u); + } + const i = n.filter((u) => u.completeImpulse === s), l = n.filter((u) => u.completeImpulse !== s); + await re(e, { + currentImpulse: s, + queue: l + }); + const r = ae(i, e); + for (const u of r) + await Rn(e, u, s); + r.length || (c = ui.notifications) == null || c.info(`Impulse advanced to ${s}.`), E(e); +} +async function Mt(e) { + if (await ve()) { + await re(e, { currentImpulse: 1, queue: [] }); + for (const t of e.combatants) + await t.update({ + [`flags.${x}.pendingEventId`]: null, + [`flags.${x}.pendingKind`]: "", + [`flags.${x}.aimState`]: { + active: !1, + targetId: null, + sinceImpulse: null + }, + [`flags.${x}.actionRanking`]: Ke(z(t)), + [`flags.${x}.effectiveActionRanking`]: null, + [`flags.${x}.impulseModifier`]: 0 + }); + E(e); + } +} +function Tn(e, t) { + var u; + const a = "find" in t ? t : $(t); + if (!(a != null && a.length)) return; + a.addClass("ringworld-impulse-mode ringworld-impulse-only"); + const n = (e == null ? void 0 : e.viewed) ?? game.combat; + if (!n) return; + const o = ie(n), s = ae(ke(o.queue, n), n); + s.length !== o.queue.length && re(n, { currentImpulse: o.currentImpulse, queue: s }), a.find( + ".token-initiative, .combatant-initiative, .initiative, [data-action='rollInitiative'], [data-control='rollInitiative'], .combatant .fa-dice-d20" + ).addClass("rw-hide-initiative"), a.find(".ringworld-impulse-panel").remove(); + const i = (u = game.user) != null && u.isGM ? "" : "disabled", l = n.combatants.map((d) => { + var M, D, T, H; + const g = St(d), m = At(d), f = q((D = (M = d.flags) == null ? void 0 : M[x]) == null ? void 0 : D.impulseModifier, 0), y = Math.max(1, g + m), b = z(d), w = s.find((L) => L.combatantId === d.id), p = w ? `${Re(w.kind)} @ ${w.completeImpulse}` : "Ready", h = (H = (T = d.flags) == null ? void 0 : T[x]) == null ? void 0 : H.aimState, A = h != null && h.active ? `Aiming since ${h.sinceImpulse}` : "Not aiming", S = vn.map((L) => { + const F = w != null && w.major && (w == null ? void 0 : w.kind) === L.value ? "selected" : ""; + return ``; + }).join(""), I = Sn.map((L) => { + const F = !(w != null && w.major) && (w == null ? void 0 : w.kind) === L.value ? "selected" : ""; + return ``; + }).join(""); + return ` +
+
+ ${Y(d.name)} + DEX ${b} | AR ${g} + Enc ${m} = ${y} | Imp Mod ${f >= 0 ? `+${f}` : f} | ${Y(p)} | ${Y(A)} +
+
+ + + + + + + +
+
+ `; + }).join(""), r = $(` +
+
+
Impulse ${o.currentImpulse}
+
Queued: ${s.length}
+
+ + + +
+
+
${l}
+
+ `); + a.prepend(r); + const c = a.get(0); + if (c) + for (const d of Array.from(c.children)) + d.classList.contains("ringworld-impulse-panel") || d.classList.add("rw-hide-core-tracker"); + r.find(".rw-advance-next").on("click", async (d) => { + d.preventDefault(), await Ie(n, "next"); + }), r.find(".rw-advance-step").on("click", async (d) => { + d.preventDefault(), await Ie(n, "step"); + }), r.find(".rw-reset-sequence").on("click", async (d) => { + d.preventDefault(), await Mt(n); + }), r.find(".rw-major-start").on("click", async (d) => { + d.preventDefault(); + const g = d.currentTarget.closest(".rw-imp-row"); + if (!g) return; + const m = String(g.getAttribute("data-combatant-id") || ""), f = g.querySelector(".rw-major-kind"), y = String((f == null ? void 0 : f.value) || "draw"); + await ot(n, m, y, !0); + }), r.find(".rw-minor-start").on("click", async (d) => { + d.preventDefault(); + const g = d.currentTarget.closest(".rw-imp-row"); + if (!g) return; + const m = String(g.getAttribute("data-combatant-id") || ""), f = g.querySelector(".rw-minor-kind"), y = String((f == null ? void 0 : f.value) || "minorCustom"); + await ot(n, m, y, !1); + }), r.find(".rw-cancel-start").on("click", async (d) => { + d.preventDefault(); + const g = d.currentTarget.closest(".rw-imp-row"); + if (!g) return; + const m = String(g.getAttribute("data-combatant-id") || ""); + await $n(n, m); + }), r.find(".rw-save-mods").on("click", async (d) => { + if (d.preventDefault(), !await ve()) return; + const g = d.currentTarget.closest(".rw-imp-row"); + if (!g) return; + const m = String(g.getAttribute("data-combatant-id") || ""), f = n.combatants.get(m); + if (!f) return; + const y = g.querySelector(".rw-imp-mod"), b = Math.trunc(q(y == null ? void 0 : y.value, 0)); + await f.update({ + [`flags.${x}.impulseModifier`]: b + }); + }), E(n); +} +function Dn() { + Hooks.on("renderCombatTracker", (e, t) => { + Tn(e, t); + }), Hooks.on("createCombat", async (e) => { + await re(e, { currentImpulse: 1, queue: [] }), E(e); + }), Hooks.on("createCombatant", async (e) => { + await xt(e), E(game.combat ?? null); + }), Hooks.on("deleteCombatant", async () => { + E(game.combat ?? null); + }), Hooks.on("updateCombat", (e) => { + E(e ?? game.combat ?? null); + }), Hooks.on("deleteCombat", () => { + E(null); + }), Hooks.on("renderChatLog", () => { + E(game.combat ?? null); + }), Hooks.on("canvasReady", () => { + E(game.combat ?? null); + }), Hooks.on("updateActor", (e) => { + var n; + const t = game.combat; + if (!t) return; + t.combatants.some((o) => { + var s; + return ((s = o.actor) == null ? void 0 : s.id) === e.id; + }) && ((n = ui.combat) == null || n.render(!0), E(t)); + }), Hooks.on("updateItem", (e) => { + var o; + const t = e == null ? void 0 : e.parent; + if (!t) return; + const a = game.combat; + if (!a) return; + a.combatants.some((s) => { + var i; + return ((i = s.actor) == null ? void 0 : i.id) === t.id; + }) && ((o = ui.combat) == null || o.render(!0), E(a)); + }); +} +const st = 2; +function Ct(e) { + return String(e ?? "").trim().toLowerCase(); +} +function Hn() { + var t; + const e = /* @__PURE__ */ new Map(); + for (const a of game.items ?? []) { + if (a.type !== "weapon" && a.type !== "armor" && a.type !== "gear") continue; + const n = `${a.type}:${Ct(a.name)}`; + e.set(n, Number(((t = a.system) == null ? void 0 : t.weight) ?? 0) || 0); + } + return e; +} +async function Nn() { + var a, n, o, s; + if (!((a = game.user) != null && a.isGM)) return 0; + const e = Hn(); + let t = 0; + for (const i of game.actors ?? []) { + const l = []; + for (const r of i.items ?? []) { + if (r.type !== "weapon" && r.type !== "armor" && r.type !== "gear") continue; + const c = { _id: r.id }; + let u = !1; + if (((n = r.system) == null ? void 0 : n.equipped) === void 0 && (c["system.equipped"] = !0, u = !0), ((o = r.system) == null ? void 0 : o.weight) === void 0) { + const d = `${r.type}:${Ct(r.name)}`; + c["system.weight"] = Number(e.get(d) ?? 0), u = !0; + } + ((s = r.system) == null ? void 0 : s.stowed) === void 0 && (c["system.stowed"] = !1, u = !0), u && l.push(c); + } + l.length && (await i.updateEmbeddedDocuments("Item", l), t += l.length); + } + return t; +} +function Le(e, t, a, n) { + if (!e) return; + const o = "find" in e ? e : $(e); + if (!o.length || o.find(`.${t}`).length) return; + const s = $(``); + s.on("click", async () => { + await n(); + }); + const l = [ + o.find(".directory-footer"), + o.find("footer.directory-footer"), + o.find("footer"), + o.find(".footer-actions"), + o.find(".header-actions"), + o.find(".action-buttons") + ].find((c) => c.length); + if (l) { + l.first().append(s); + return; + } + let r = o.find(".ringworld-directory-controls"); + r.length || (r = $('
'), o.append(r)), r.first().append(s); +} +async function $t() { + await Be({ notify: !0 }); +} +async function Rt() { + var t; + const e = await bt(); + (t = e == null ? void 0 : e.sheet) == null || t.render(!0); +} +function ne() { + var a, n, o; + if (!((a = game.user) != null && a.isGM)) return; + const e = (n = ui.items) == null ? void 0 : n.element; + Le(e, "ringworld-seed-items", "Seed Ringworld Items", $t); + const t = (o = ui.actors) == null ? void 0 : o.element; + Le(t, "ringworld-random-actor", "Create Random Explorer", Rt); +} +function On(e) { + var o, s, i, l; + const t = (o = getDocumentClass("Item")) == null ? void 0 : o.TYPES; + if (Array.isArray(t)) return t.includes(e); + const a = (i = (s = game == null ? void 0 : game.system) == null ? void 0 : s.documentTypes) == null ? void 0 : i.Item; + if (Array.isArray(a)) return a.includes(e); + if (a && typeof a == "object") return Object.prototype.hasOwnProperty.call(a, e); + const n = (l = CONFIG == null ? void 0 : CONFIG.Item) == null ? void 0 : l.dataModels; + return !!(n && typeof n == "object" && Object.prototype.hasOwnProperty.call(n, e)); +} +Hooks.once("init", async () => { + var o, s, i, l, r, c, u, d; + console.log("ringworld | Initializing Ringworld system"), Dn(); + const e = (s = (o = foundry == null ? void 0 : foundry.documents) == null ? void 0 : o.collections) == null ? void 0 : s.Actors, t = (l = (i = foundry == null ? void 0 : foundry.documents) == null ? void 0 : i.collections) == null ? void 0 : l.Items, a = (c = (r = foundry == null ? void 0 : foundry.appv1) == null ? void 0 : r.sheets) == null ? void 0 : c.ActorSheet, n = (d = (u = foundry == null ? void 0 : foundry.appv1) == null ? void 0 : u.sheets) == null ? void 0 : d.ItemSheet; + if (CONFIG.ringworld = { + hitLocations: Ce + }, CONFIG.Combat.initiative = foundry.utils.mergeObject(CONFIG.Combat.initiative ?? {}, { + formula: "1d100", + decimals: 0 + }, { inplace: !1 }), CONFIG.Actor.documentClass = Va, CONFIG.Item.documentClass = Ga, !e || !t || !a || !n) { + console.error("ringworld | Foundry sheet registries were not available during init."); + return; + } + e.unregisterSheet("core", a), e.registerSheet("ringworld", nn, { + types: ["explorer"], + makeDefault: !0 + }), e.registerSheet("ringworld", sn, { + types: ["creature"], + makeDefault: !0 + }), e.registerSheet("ringworld", ln, { + types: ["vehicle"], + makeDefault: !0 + }), t.unregisterSheet("core", n), t.registerSheet("ringworld", un, { + types: ["skill"], + makeDefault: !0 + }), t.registerSheet("ringworld", dn, { + types: ["weapon"], + makeDefault: !0 + }), t.registerSheet("ringworld", pn, { + types: ["armor"], + makeDefault: !0 + }), t.registerSheet("ringworld", yn, { + types: ["gear"], + makeDefault: !0 + }), t.registerSheet("ringworld", wn, { + types: ["species"], + makeDefault: !0 + }), On("pursuit") && t.registerSheet("ringworld", kn, { + types: ["pursuit"], + makeDefault: !0 + }), game.settings.register("ringworld", "catalogSeedVersion", { + scope: "world", + config: !1, + type: Number, + default: 0 + }), game.settings.register("ringworld", "encumbranceDataMigrationVersion", { + scope: "world", + config: !1, + type: Number, + default: 0 + }), Handlebars.registerHelper("eq", (g, m) => g === m), Handlebars.registerHelper("or", (g, m) => !!(g || m)); +}); +Hooks.once("ready", async () => { + var t, a, n, o, s; + const e = "systems/ringworld/resources/hit-locations/humanoid.json"; + try { + const i = await fetch(e); + if (i.ok) { + const l = await i.json(); + CONFIG.ringworld.hitLocations = l.entries ?? Ce; + } + } catch { + console.warn(`ringworld | Could not load ${e}, using built-in table`); + } + if ((t = game.user) != null && t.isGM) { + for (const r of game.combats ?? []) { + const c = String(((n = (a = r == null ? void 0 : r.settings) == null ? void 0 : a.initiative) == null ? void 0 : n.formula) ?? "").trim(); + (!c || c.toLowerCase() === "undefined") && await r.update({ + "settings.initiative.formula": "1d100", + "settings.initiative.decimals": 0 + }); + } + if (Number(game.settings.get("ringworld", "catalogSeedVersion") ?? 0) < De && (await Be({ notify: !0 })).added >= 0 && await game.settings.set("ringworld", "catalogSeedVersion", De), Number(game.settings.get("ringworld", "encumbranceDataMigrationVersion") ?? 0) < st) { + const r = await Nn(); + await game.settings.set("ringworld", "encumbranceDataMigrationVersion", st), r > 0 && ((o = ui.notifications) == null || o.info(`Ringworld encumbrance migration updated ${r} actor items.`)); + } + } + ne(), window.setTimeout(() => ne(), 300), game.ringworld = { + ...game.ringworld ?? {}, + seedWorldCatalog: async (i) => Be(i), + createRandomExplorer: async () => bt(), + catalogVersion: De, + systemId: "ringworld", + systemVersion: String(((s = game.system) == null ? void 0 : s.version) ?? "0.0.0") + }, Hooks.callAll("ringworldReady", game.ringworld); +}); +Hooks.on("createActor", async (e, t, a) => { + var n, o; + if ((n = game.user) != null && n.isGM && a === game.user.id && !(!e || e.type !== "explorer" && e.type !== "creature")) + try { + const s = await ((o = e.ensureBaseSkills) == null ? void 0 : o.call(e)); + Number(s) > 0 && console.log(`ringworld | Added ${s} base skills to ${e.name}`); + } catch (s) { + console.error("ringworld | Failed to auto-populate base skills", s); + } +}); +Hooks.on("preCreateItem", (e) => { + if (e.type !== "skill") return; + const t = {}, a = String(e.name || "Skill"), n = String(e.img || ""); + t.img = Oe(a, n); + const o = $e(a), s = e.system ?? {}, i = Number((s == null ? void 0 : s.value) ?? NaN), l = String((s == null ? void 0 : s.category) ?? "").trim(); + t.system = { + value: Number.isFinite(i) ? i : (o == null ? void 0 : o.base) ?? 0, + category: l || ((o == null ? void 0 : o.category) ?? "Technical"), + specialization: String((s == null ? void 0 : s.specialization) ?? (o == null ? void 0 : o.specialization) ?? "") + }, t.img || (t.img = W), e.updateSource(t); +}); +Hooks.on("renderItemDirectory", (e, t) => { + var a; + (a = game.user) != null && a.isGM && (Le(t, "ringworld-seed-items", "Seed Ringworld Items", $t), ne()); +}); +Hooks.on("renderActorDirectory", (e, t) => { + var a; + (a = game.user) != null && a.isGM && (Le(t, "ringworld-random-actor", "Create Random Explorer", Rt), ne()); +}); +Hooks.on("renderSidebar", () => { + ne(); +}); +Hooks.on("changeSidebarTab", (e) => { + var a; + const t = String((e == null ? void 0 : e.tabName) ?? (e == null ? void 0 : e.id) ?? ((a = e == null ? void 0 : e.options) == null ? void 0 : a.id) ?? ""); + (t === "items" || t === "actors") && ne(); +}); +//# sourceMappingURL=ringworld.mjs.map diff --git a/module/ringworld.mjs.map b/module/ringworld.mjs.map new file mode 100644 index 0000000..858c679 --- /dev/null +++ b/module/ringworld.mjs.map @@ -0,0 +1 @@ +{"version":3,"file":"ringworld.mjs","sources":["../src/module/utils/dice.ts","../src/module/utils/percentile.ts","../src/module/constants.ts","../src/module/hit-location-schemas.ts","../src/module/utils/hit-locations.ts","../src/module/data-base-skills.ts","../src/module/skill-icons.ts","../src/module/chat.ts","../src/module/mechanics.ts","../src/module/world-catalog.ts","../src/module/character-tools.ts","../src/module/actor/RingworldActor.ts","../src/module/item/RingworldItem.ts","../src/module/sheets/actors/ExplorerSheet.ts","../src/module/sheets/actors/CreatureSheet.ts","../src/module/sheets/actors/VehicleSheet.ts","../src/module/sheets/items/SkillSheet.ts","../src/module/sheets/items/WeaponSheet.ts","../src/module/sheets/items/ArmorSheet.ts","../src/module/sheets/items/GearSheet.ts","../src/module/sheets/items/SpeciesSheet.ts","../src/module/sheets/items/PursuitSheet.ts","../src/module/impulse-tracker.ts","../src/ringworld.ts"],"sourcesContent":["export async function showDiceSoNice(roll: Roll): Promise {\n const dice3d = (game as any)?.dice3d;\n if (!dice3d?.showForRoll) return;\n\n try {\n await dice3d.showForRoll(roll, game.user, true, null, false);\n } catch (_error) {\n // Ignore Dice So Nice integration failures and continue normal flow.\n }\n}\n\nexport async function evaluateRollWithDice(formula: string): Promise {\n const roll = await new Roll(formula).evaluate();\n await showDiceSoNice(roll as any);\n return roll as any;\n}\n","import { evaluateRollWithDice } from \"./dice\";\n\nexport type PercentileOutcome = \"special\" | \"success\" | \"failure\" | \"fumble\";\n\nexport interface PercentileResult {\n roll: number;\n target: number;\n outcome: PercentileOutcome;\n isSuccess: boolean;\n isSpecial: boolean;\n isFumble: boolean;\n}\n\nexport async function rollPercentile(target: number): Promise {\n const roll = await evaluateRollWithDice(\"1d100\");\n return classifyPercentile(roll.total ?? 0, target);\n}\n\nexport function classifyPercentile(roll: number, target: number): PercentileResult {\n const cleanTarget = Math.max(0, Math.floor(Number(target) || 0));\n const specialThreshold = Math.max(1, Math.floor(cleanTarget / 5));\n const fumbleThreshold = cleanTarget >= 50 ? 100 : 96;\n\n const isSuccess = roll <= cleanTarget;\n const isSpecial = isSuccess && roll <= specialThreshold;\n const isFumble = !isSuccess && roll >= fumbleThreshold;\n\n let outcome: PercentileOutcome = \"failure\";\n if (isFumble) outcome = \"fumble\";\n else if (isSpecial) outcome = \"special\";\n else if (isSuccess) outcome = \"success\";\n\n return {\n roll,\n target: cleanTarget,\n outcome,\n isSuccess,\n isSpecial,\n isFumble\n };\n}\n\nexport function outcomeLabel(outcome: PercentileOutcome): string {\n switch (outcome) {\n case \"special\":\n return \"Special Success\";\n case \"success\":\n return \"Success\";\n case \"fumble\":\n return \"Fumble\";\n default:\n return \"Failure\";\n }\n}\n","export type HitLocationSlug = \"rLeg\" | \"lLeg\" | \"abdomen\" | \"chest\" | \"rArm\" | \"lArm\" | \"head\";\n\nexport interface HitLocationEntry {\n slug: HitLocationSlug;\n label: string;\n min: number;\n max: number;\n}\n\nexport const SYSTEM_ID = \"ringworld\";\n\nexport const HIT_LOCATION_TABLE: HitLocationEntry[] = [\n { slug: \"rLeg\", label: \"Right Leg\", min: 1, max: 3 },\n { slug: \"lLeg\", label: \"Left Leg\", min: 4, max: 6 },\n { slug: \"abdomen\", label: \"Abdomen\", min: 7, max: 10 },\n { slug: \"chest\", label: \"Chest\", min: 11, max: 15 },\n { slug: \"rArm\", label: \"Right Arm\", min: 16, max: 17 },\n { slug: \"lArm\", label: \"Left Arm\", min: 18, max: 19 },\n { slug: \"head\", label: \"Head\", min: 20, max: 20 }\n];\n\nexport const HIT_LOCATION_ORDER: HitLocationSlug[] = [\n \"rLeg\",\n \"lLeg\",\n \"abdomen\",\n \"chest\",\n \"rArm\",\n \"lArm\",\n \"head\"\n];\n\nexport const LIMB_LOCATIONS: HitLocationSlug[] = [\"rArm\", \"lArm\", \"rLeg\", \"lLeg\"];\nexport const LEG_LOCATIONS: HitLocationSlug[] = [\"rLeg\", \"lLeg\"];\n\nexport function isLimb(slug: HitLocationSlug): boolean {\n return LIMB_LOCATIONS.includes(slug);\n}\n","import { HIT_LOCATION_ORDER, HIT_LOCATION_TABLE, type HitLocationEntry, type HitLocationSlug } from \"./constants\";\n\nexport type HitLocationSchemaId = \"humanoid\" | \"dolphin\" | \"puppeteer\" | \"trinoc\" | \"bandersnatchi\" | \"kzinti\";\n\nexport interface HitLocationSchemaEntry extends HitLocationEntry {\n hpFactor: number;\n}\n\nexport interface HitLocationSchema {\n id: HitLocationSchemaId;\n label: string;\n bodyOrder: HitLocationSlug[];\n entries: HitLocationSchemaEntry[];\n}\n\nfunction normalize(value: unknown): string {\n return String(value ?? \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"\");\n}\n\nfunction withFactors(entries: HitLocationEntry[], hpFactors?: Partial>): HitLocationSchemaEntry[] {\n return entries.map((entry) => ({\n ...entry,\n hpFactor: hpFactors?.[entry.slug] ?? (entry.slug === \"chest\" ? 0.35 : (entry.slug === \"rArm\" || entry.slug === \"lArm\") ? 0.25 : 0.3)\n }));\n}\n\nconst HUMANOID_SCHEMA: HitLocationSchema = {\n id: \"humanoid\",\n label: \"Humanoid\",\n bodyOrder: [\"head\", \"lArm\", \"chest\", \"rArm\", \"abdomen\", \"lLeg\", \"rLeg\"],\n entries: withFactors(HIT_LOCATION_TABLE)\n};\n\nconst DOLPHIN_SCHEMA: HitLocationSchema = {\n id: \"dolphin\",\n label: \"Dolphin\",\n bodyOrder: [\"head\", \"lArm\", \"chest\", \"rArm\", \"abdomen\", \"lLeg\", \"rLeg\"],\n entries: withFactors([\n { slug: \"rLeg\", label: \"Right Fluke\", min: 1, max: 3 },\n { slug: \"lLeg\", label: \"Left Fluke\", min: 4, max: 6 },\n { slug: \"abdomen\", label: \"Lower Body\", min: 7, max: 9 },\n { slug: \"chest\", label: \"Main Body\", min: 10, max: 15 },\n { slug: \"rArm\", label: \"Right Fin\", min: 16, max: 17 },\n { slug: \"lArm\", label: \"Left Fin\", min: 18, max: 19 },\n { slug: \"head\", label: \"Head\", min: 20, max: 20 }\n ], {\n head: 0.3,\n rArm: 0.2,\n lArm: 0.2,\n chest: 0.4,\n abdomen: 0.35,\n rLeg: 0.25,\n lLeg: 0.25\n })\n};\n\nconst PUPPETEER_SCHEMA: HitLocationSchema = {\n id: \"puppeteer\",\n label: \"Puppeteer\",\n bodyOrder: [\"head\", \"lArm\", \"chest\", \"rArm\", \"abdomen\", \"lLeg\", \"rLeg\"],\n entries: withFactors([\n { slug: \"rLeg\", label: \"Right Rear Leg\", min: 1, max: 3 },\n { slug: \"lLeg\", label: \"Left Rear Leg\", min: 4, max: 6 },\n { slug: \"rArm\", label: \"Right Foreleg\", min: 7, max: 9 },\n { slug: \"lArm\", label: \"Left Foreleg\", min: 10, max: 12 },\n { slug: \"chest\", label: \"Fore Body\", min: 13, max: 15 },\n { slug: \"abdomen\", label: \"Hind Body\", min: 16, max: 18 },\n { slug: \"head\", label: \"Twin Heads\", min: 19, max: 20 }\n ], {\n head: 0.32,\n chest: 0.35,\n abdomen: 0.35,\n rArm: 0.28,\n lArm: 0.28,\n rLeg: 0.28,\n lLeg: 0.28\n })\n};\n\nconst TRINOC_SCHEMA: HitLocationSchema = {\n id: \"trinoc\",\n label: \"Trinoc\",\n bodyOrder: [\"head\", \"lArm\", \"chest\", \"rArm\", \"abdomen\", \"lLeg\", \"rLeg\"],\n entries: withFactors([\n { slug: \"rArm\", label: \"Right Wing/Arm\", min: 1, max: 3 },\n { slug: \"lArm\", label: \"Left Wing/Arm\", min: 4, max: 6 },\n { slug: \"chest\", label: \"Upper Torso\", min: 7, max: 11 },\n { slug: \"abdomen\", label: \"Lower Torso\", min: 12, max: 14 },\n { slug: \"rLeg\", label: \"Right Leg\", min: 15, max: 17 },\n { slug: \"lLeg\", label: \"Left Leg\", min: 18, max: 19 },\n { slug: \"head\", label: \"Head\", min: 20, max: 20 }\n ], {\n head: 0.3,\n rArm: 0.27,\n lArm: 0.27,\n chest: 0.36,\n abdomen: 0.3,\n rLeg: 0.27,\n lLeg: 0.27\n })\n};\n\nconst BANDERSNATCHI_SCHEMA: HitLocationSchema = {\n id: \"bandersnatchi\",\n label: \"Bandersnatchi\",\n bodyOrder: [\"head\", \"lArm\", \"chest\", \"rArm\", \"abdomen\", \"lLeg\", \"rLeg\"],\n entries: withFactors([\n { slug: \"rLeg\", label: \"Right Rear Mass\", min: 1, max: 2 },\n { slug: \"lLeg\", label: \"Left Rear Mass\", min: 3, max: 4 },\n { slug: \"abdomen\", label: \"Lower Body Mass\", min: 5, max: 11 },\n { slug: \"chest\", label: \"Upper Body Mass\", min: 12, max: 17 },\n { slug: \"rArm\", label: \"Right Manipulator\", min: 18, max: 18 },\n { slug: \"lArm\", label: \"Left Manipulator\", min: 19, max: 19 },\n { slug: \"head\", label: \"Head Cluster\", min: 20, max: 20 }\n ], {\n head: 0.28,\n chest: 0.42,\n abdomen: 0.42,\n rArm: 0.2,\n lArm: 0.2,\n rLeg: 0.24,\n lLeg: 0.24\n })\n};\n\nconst KZINTI_SCHEMA: HitLocationSchema = {\n id: \"kzinti\",\n label: \"Kzinti\",\n bodyOrder: [\"head\", \"lArm\", \"chest\", \"rArm\", \"abdomen\", \"lLeg\", \"rLeg\"],\n entries: withFactors([\n { slug: \"rLeg\", label: \"Right Hind Leg\", min: 1, max: 3 },\n { slug: \"lLeg\", label: \"Left Hind Leg\", min: 4, max: 6 },\n { slug: \"abdomen\", label: \"Abdomen\", min: 7, max: 10 },\n { slug: \"chest\", label: \"Chest\", min: 11, max: 15 },\n { slug: \"rArm\", label: \"Right Foreleg\", min: 16, max: 17 },\n { slug: \"lArm\", label: \"Left Foreleg\", min: 18, max: 19 },\n { slug: \"head\", label: \"Head\", min: 20, max: 20 }\n ])\n};\n\nexport const HIT_LOCATION_SCHEMAS: Record = {\n humanoid: HUMANOID_SCHEMA,\n dolphin: DOLPHIN_SCHEMA,\n puppeteer: PUPPETEER_SCHEMA,\n trinoc: TRINOC_SCHEMA,\n bandersnatchi: BANDERSNATCHI_SCHEMA,\n kzinti: KZINTI_SCHEMA\n};\n\nexport function listHitLocationSchemas(): HitLocationSchema[] {\n return Object.values(HIT_LOCATION_SCHEMAS);\n}\n\nexport function inferHitLocationSchemaId(speciesValue: unknown): HitLocationSchemaId {\n const species = normalize(speciesValue);\n if (!species) return \"humanoid\";\n if (species.includes(\"dolphin\")) return \"dolphin\";\n if (species.includes(\"puppeteer\")) return \"puppeteer\";\n if (species.includes(\"trinoc\")) return \"trinoc\";\n if (species.includes(\"bandersnatch\")) return \"bandersnatchi\";\n if (species.includes(\"kzinti\")) return \"kzinti\";\n return \"humanoid\";\n}\n\nexport function getHitLocationSchemaById(schemaId: unknown): HitLocationSchema {\n const key = normalize(schemaId) as HitLocationSchemaId;\n return HIT_LOCATION_SCHEMAS[key] ?? HUMANOID_SCHEMA;\n}\n\nexport function getActorHitLocationSchema(actor: any): HitLocationSchema {\n const mode = String(actor?.system?.hitLocationSchema || \"auto\").trim().toLowerCase();\n if (mode && mode !== \"auto\") {\n return getHitLocationSchemaById(mode);\n }\n const species = actor?.system?.profile?.species ?? \"\";\n return getHitLocationSchemaById(inferHitLocationSchemaId(species));\n}\n\nexport function getSchemaLocationEntry(schema: HitLocationSchema, slug: HitLocationSlug): HitLocationSchemaEntry {\n return schema.entries.find((entry) => entry.slug === slug) ?? withFactors(HIT_LOCATION_TABLE).find((entry) => entry.slug === slug)!;\n}\n\nexport function getSchemaOrder(schema: HitLocationSchema): HitLocationSlug[] {\n const set = new Set(schema.entries.map((entry) => entry.slug));\n const ordered = schema.bodyOrder.filter((slug) => set.has(slug));\n if (ordered.length === schema.entries.length) return ordered;\n return [...HIT_LOCATION_ORDER];\n}\n\n","import { HIT_LOCATION_TABLE, type HitLocationEntry, type HitLocationSlug } from \"../constants\";\nimport { evaluateRollWithDice } from \"./dice\";\nimport { getActorHitLocationSchema, getHitLocationSchemaById } from \"../hit-location-schemas\";\n\nexport interface HitLocationRollResult {\n roll: number;\n location: HitLocationEntry;\n}\n\nexport function getHitLocationFromRoll(roll: number): HitLocationEntry {\n const found = HIT_LOCATION_TABLE.find((entry) => roll >= entry.min && roll <= entry.max);\n return found ?? HIT_LOCATION_TABLE[HIT_LOCATION_TABLE.length - 1];\n}\n\nfunction getTableForActor(actor?: any): HitLocationEntry[] {\n if (!actor) return HIT_LOCATION_TABLE;\n const schema = getActorHitLocationSchema(actor);\n return schema.entries;\n}\n\nfunction getHitLocationFromRollForTable(roll: number, table: HitLocationEntry[]): HitLocationEntry {\n const found = table.find((entry) => roll >= entry.min && roll <= entry.max);\n return found ?? table[table.length - 1];\n}\n\nexport async function rollHitLocation(actor?: any): Promise {\n const roll = await evaluateRollWithDice(\"1d20\");\n const total = roll.total ?? 20;\n const table = getTableForActor(actor);\n return {\n roll: total,\n location: getHitLocationFromRollForTable(total, table)\n };\n}\n\nexport async function chooseOrRollHitLocation(actor?: any): Promise<{\n slug: HitLocationSlug;\n label: string;\n rolled: number | null;\n} | null> {\n const schema = actor ? getActorHitLocationSchema(actor) : getHitLocationSchemaById(\"humanoid\");\n const table = schema.entries;\n const entriesBySlug = new Map(table.map((entry) => [entry.slug, entry]));\n const labelFor = (slug: HitLocationSlug, fallback: string) => entriesBySlug.get(slug)?.label ?? fallback;\n\n const html = `\n
\n \n
\n \n
\n
\n \n \n \n \n \n \n \n
\n
\n `;\n\n const result = await new Promise((resolve) => {\n let settled = false;\n const done = (value: string | null) => {\n if (settled) return;\n settled = true;\n resolve(value);\n };\n\n const dialog = new Dialog({\n title: \"Choose Hit Location\",\n content: html,\n buttons: {\n apply: {\n label: \"Apply\",\n callback: (dlgHtml: JQuery) => {\n const value = String(dlgHtml.find(\"input[name='location']\").val() || \"random\");\n done(value);\n }\n },\n cancel: {\n label: \"Cancel\",\n callback: () => done(null)\n }\n },\n default: \"apply\",\n render: (dlgHtml: JQuery) => {\n const picker = dlgHtml.find(\".ringworld-hit-picker\");\n picker.find(\".rw-hit-picker-choice\").on(\"click\", (event) => {\n event.preventDefault();\n const button = event.currentTarget as HTMLElement;\n const location = String(button.dataset.location || \"random\");\n picker.find(\".rw-hit-picker-choice\").removeClass(\"active\");\n $(button).addClass(\"active\");\n picker.find(\"input[name='location']\").val(location);\n });\n },\n close: () => done(null)\n }, {\n classes: [\"ringworld-hit-location-dialog\"],\n width: 500,\n resizable: false\n });\n dialog.render(true);\n });\n\n if (!result) return null;\n\n if (result === \"random\") {\n const rolled = await rollHitLocation(actor);\n return {\n slug: rolled.location.slug,\n label: rolled.location.label,\n rolled: rolled.roll\n };\n }\n\n const entry = table.find((loc) => loc.slug === result);\n if (!entry) return null;\n\n return {\n slug: entry.slug,\n label: entry.label,\n rolled: null\n };\n}\n","export interface BaseSkillDef {\n name: string;\n category: \"Agility\" | \"Communication\" | \"Knowledge\" | \"Perception\" | \"Technical\";\n base: number;\n specialization?: string;\n}\n\nexport const BASE_SKILL_CATEGORIES: Array = [\n \"Agility\",\n \"Communication\",\n \"Knowledge\",\n \"Perception\",\n \"Technical\"\n];\n\nexport const RINGWORLD_BASE_SKILLS: BaseSkillDef[] = [\n { name: \"Archaic Melee Weapon\", category: \"Agility\", base: 5 },\n { name: \"Archaic Ranged Weapons\", category: \"Agility\", base: 3 },\n { name: \"Athletics\", category: \"Agility\", base: 15 },\n { name: \"Hide\", category: \"Agility\", base: 10 },\n { name: \"Sneak\", category: \"Agility\", base: 5 },\n { name: \"Unarmed Combat\", category: \"Agility\", base: 0 },\n { name: \"V. Sword, F. Laser\", category: \"Agility\", base: 15 },\n\n { name: \"Bargain\", category: \"Communication\", base: 10 },\n { name: \"Debate\", category: \"Communication\", base: 5 },\n { name: \"Fast Talk\", category: \"Communication\", base: 10 },\n { name: \"Fine Arts\", category: \"Communication\", base: 5 },\n { name: \"Musicianship\", category: \"Communication\", base: 5 },\n { name: \"Orate\", category: \"Communication\", base: 5 },\n { name: \"Own Language\", category: \"Communication\", base: 0, specialization: \"INTx5 base\" },\n { name: \"Perform\", category: \"Communication\", base: 5 },\n { name: \"Psychology\", category: \"Communication\", base: 0 },\n\n { name: \"Anthropology\", category: \"Knowledge\", base: 0 },\n { name: \"Astronomy\", category: \"Knowledge\", base: 0 },\n { name: \"Biology\", category: \"Knowledge\", base: 0 },\n { name: \"Botany\", category: \"Knowledge\", base: 0 },\n { name: \"Chemistry\", category: \"Knowledge\", base: 0 },\n { name: \"Computers\", category: \"Knowledge\", base: 0 },\n { name: \"Emergency Treatment\", category: \"Knowledge\", base: 1 },\n { name: \"Engineering\", category: \"Knowledge\", base: 0 },\n { name: \"Farming\", category: \"Knowledge\", base: 0 },\n { name: \"History\", category: \"Knowledge\", base: 5 },\n { name: \"Law\", category: \"Knowledge\", base: 0 },\n { name: \"Mathematics\", category: \"Knowledge\", base: 0 },\n { name: \"Physics\", category: \"Knowledge\", base: 0 },\n { name: \"Planetology\", category: \"Knowledge\", base: 0 },\n { name: \"Second Languages\", category: \"Knowledge\", base: 0 },\n { name: \"Strategy\", category: \"Knowledge\", base: 0 },\n { name: \"Theology\", category: \"Knowledge\", base: 0 },\n { name: \"Zoology\", category: \"Knowledge\", base: 0 },\n\n { name: \"Handgun, Energy\", category: \"Perception\", base: 5 },\n { name: \"Handgun, Projectile\", category: \"Perception\", base: 3 },\n { name: \"Heavy Weapon, Energy\", category: \"Perception\", base: 5 },\n { name: \"Heavy Weapon, Projectile\", category: \"Perception\", base: 3 },\n { name: \"Listen\", category: \"Perception\", base: 5 },\n { name: \"Observe\", category: \"Perception\", base: 5 },\n { name: \"Scent\", category: \"Perception\", base: 0 },\n { name: \"Search\", category: \"Perception\", base: 5 },\n { name: \"Track\", category: \"Perception\", base: 5 },\n\n { name: \"Aquatic Vehicle\", category: \"Technical\", base: 0 },\n { name: \"Atmospheric Craft\", category: \"Technical\", base: 0 },\n { name: \"Ground Vehicle\", category: \"Technical\", base: 0 },\n { name: \"Hyperdrive\", category: \"Technical\", base: 0 },\n { name: \"Personal Flyer\", category: \"Technical\", base: 15 },\n { name: \"Reaction Drive\", category: \"Technical\", base: 0 },\n { name: \"Reactionless Drive\", category: \"Technical\", base: 0 },\n { name: \"Repair\", category: \"Technical\", base: 0 },\n { name: \"Ringworld\", category: \"Technical\", base: 0 },\n { name: \"Weapons System\", category: \"Technical\", base: 0 }\n];\n\nexport function normalizeSkillName(name: string): string {\n return String(name || \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"\")\n .trim();\n}\n\nexport function baseSkillNameSet(): Set {\n return new Set(RINGWORLD_BASE_SKILLS.map((skill) => normalizeSkillName(skill.name)));\n}\n","import { normalizeSkillName, RINGWORLD_BASE_SKILLS } from \"./data-base-skills\";\n\nexport const DEFAULT_SKILL_ICON = \"systems/ringworld/assets/icons/skill.svg\";\n\nfunction iconSlug(name: string): string {\n return String(name)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport function skillIconPath(name: string): string {\n return `systems/ringworld/assets/icons/skill/${iconSlug(name)}.svg`;\n}\n\nexport function baseSkillByName(name: string): (typeof RINGWORLD_BASE_SKILLS)[number] | undefined {\n const key = normalizeSkillName(name);\n return RINGWORLD_BASE_SKILLS.find((skill) => normalizeSkillName(skill.name) === key);\n}\n\nexport function resolveSkillIcon(name: string, currentImg?: string): string {\n const img = String(currentImg || \"\").trim();\n if (img && img !== \"icons/svg/item-bag.svg\") return img;\n if (baseSkillByName(name)) return skillIconPath(name);\n return DEFAULT_SKILL_ICON;\n}\n\n","import { SYSTEM_ID } from \"./constants\";\nimport { DEFAULT_SKILL_ICON } from \"./skill-icons\";\n\ninterface CreateChatOptions {\n actor?: any;\n content: string;\n}\n\nasync function createChatMessage(options: CreateChatOptions): Promise {\n const speaker = options.actor ? ChatMessage.getSpeaker({ actor: options.actor }) : ChatMessage.getSpeaker();\n await ChatMessage.create({\n user: game.user?.id,\n speaker,\n content: options.content\n });\n}\n\nexport async function postSkillRollCard(data: {\n actor: any;\n actorName: string;\n actorImg?: string;\n skillName: string;\n skillImg?: string;\n roll: number;\n target: number;\n outcomeLabel: string;\n isSuccess: boolean;\n isSpecial: boolean;\n isFumble: boolean;\n}): Promise {\n const payload = {\n ...data,\n actorImg: data.actorImg || data.actor?.img || \"icons/svg/mystery-man.svg\",\n skillImg: data.skillImg || DEFAULT_SKILL_ICON\n };\n const content = await renderTemplate(`systems/${SYSTEM_ID}/templates/chat/skill-roll-card.hbs`, payload);\n await createChatMessage({ actor: data.actor, content });\n}\n\nexport async function postAttackCard(data: {\n actor: any;\n actorName: string;\n actorImg?: string;\n targetName: string;\n hasTarget?: boolean;\n weaponName: string;\n weaponImg?: string;\n skillName: string;\n skillImg?: string;\n attackRoll: number;\n attackTarget: number;\n attackOutcomeLabel: string;\n isSuccess: boolean;\n isSpecial: boolean;\n isFumble: boolean;\n hitLocationLabel: string;\n hitLocationRoll: number | null;\n rawDamage: number;\n armor: number;\n finalDamage: number;\n remainingLocationHp: number;\n remainingGeneralHp: number;\n energySourceLabel: string;\n energyCost: number;\n energyRemaining: number;\n notes: string;\n}): Promise {\n const payload = {\n ...data,\n actorImg: data.actorImg || data.actor?.img || \"icons/svg/mystery-man.svg\",\n weaponImg: data.weaponImg || \"icons/svg/sword.svg\",\n skillImg: data.skillImg || DEFAULT_SKILL_ICON,\n hasTarget: data.hasTarget ?? true\n };\n const content = await renderTemplate(`systems/${SYSTEM_ID}/templates/chat/attack-card.hbs`, payload);\n await createChatMessage({ actor: data.actor, content });\n}\n","import { outcomeLabel, rollPercentile } from \"./utils/percentile\";\nimport { chooseOrRollHitLocation, rollHitLocation } from \"./utils/hit-locations\";\nimport { LEG_LOCATIONS, SYSTEM_ID, isLimb, type HitLocationSlug } from \"./constants\";\nimport { postAttackCard, postSkillRollCard } from \"./chat\";\nimport { evaluateRollWithDice } from \"./utils/dice\";\nimport { getActorHitLocationSchema } from \"./hit-location-schemas\";\n\nfunction num(value: unknown, fallback = 0): number {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction getSingleTargetActor(): any | null {\n const targets = Array.from(game.user?.targets ?? []);\n if (!targets.length) return null;\n return (targets[0] as any)?.actor ?? null;\n}\n\nexport async function rollStandaloneHitLocation(actor?: any): Promise {\n const result = await rollHitLocation(actor);\n const speaker = actor ? ChatMessage.getSpeaker({ actor }) : ChatMessage.getSpeaker();\n const content = `\n
\n

Hit Location

\n

Roll: ${result.roll}

\n

Location: ${result.location.label}

\n
\n `;\n\n await ChatMessage.create({\n user: game.user?.id,\n speaker,\n content\n });\n}\n\nexport async function rollSkillForActor(actor: any, skillName: string, target: number): Promise {\n const result = await rollPercentile(target);\n const skillItem = actor.items.find(\n (item: any) => item.type === \"skill\" && item.name.toLowerCase() === String(skillName).toLowerCase()\n );\n\n await postSkillRollCard({\n actor,\n actorName: actor.name,\n actorImg: actor.img,\n skillName,\n skillImg: skillItem?.img,\n roll: result.roll,\n target: result.target,\n outcomeLabel: outcomeLabel(result.outcome),\n isSuccess: result.isSuccess,\n isSpecial: result.isSpecial,\n isFumble: result.isFumble\n });\n}\n\nfunction resolveLinkedSkill(actor: any, weapon: any): { name: string; target: number; img?: string } {\n const skillId = weapon.system.linkedSkillId;\n const byId = skillId ? actor.items.get(skillId) : null;\n if (byId?.type === \"skill\") {\n return { name: byId.name, target: num(byId.system.value), img: byId.img };\n }\n\n const linkedSkillName = String(weapon.system.linkedSkill || \"\").trim();\n if (linkedSkillName) {\n const byName = actor.items.find(\n (item: any) => item.type === \"skill\" && item.name.toLowerCase() === linkedSkillName.toLowerCase()\n );\n if (byName) {\n return { name: byName.name, target: num(byName.system.value), img: byName.img };\n }\n }\n\n return {\n name: linkedSkillName || \"Unlinked Skill\",\n target: 0\n };\n}\n\ntype AimMode = \"unaimed\" | \"partial\" | \"full\";\n\ninterface AttackRollModifierResult {\n finalTarget: number;\n summary: string;\n aimMode: AimMode;\n aimMultiplier: number;\n flatModifierTotal: number;\n}\n\nfunction clampPercentTarget(value: number): number {\n return Math.max(0, Math.min(99, Math.floor(value)));\n}\n\nasync function promptAttackModifiers(attacker: any, weapon: any, baseSkillTarget: number): Promise {\n const attackType = String(weapon.system?.attackType || \"ranged\");\n const isRanged = attackType === \"ranged\";\n const defaultAim: AimMode = \"full\";\n const safeActorName = foundry.utils.escapeHTML(String(attacker?.name || \"Attacker\"));\n const safeWeaponName = foundry.utils.escapeHTML(String(weapon?.name || \"Weapon\"));\n const safeActorImg = foundry.utils.escapeHTML(String(attacker?.img || \"icons/svg/mystery-man.svg\"));\n const safeWeaponImg = foundry.utils.escapeHTML(String(weapon?.img || \"icons/svg/sword.svg\"));\n\n const content = `\n
\n
\n \"${safeActorName}\"\n \"${safeWeaponName}\"\n
\n

${safeWeaponName}

\n

${safeActorName} Attack Setup

\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n

Ringworld rule support: unaimed ranged attacks use quarter skill, partial aim uses half skill, full aim uses full skill.

\n
\n `;\n\n return new Promise((resolve) => {\n let settled = false;\n const done = (value: AttackRollModifierResult | null) => {\n if (settled) return;\n settled = true;\n resolve(value);\n };\n\n new Dialog({\n title: `${weapon.name} - Attack Modifiers`,\n content,\n buttons: {\n roll: {\n label: \"Roll Attack\",\n callback: (dlgHtml: JQuery) => {\n const root = dlgHtml[0] as HTMLElement | undefined;\n const query = (name: string) => (root?.querySelector(`[name=\"${name}\"]`) as HTMLInputElement | HTMLSelectElement | null);\n const aimModeRaw = String(query(\"aimMode\")?.value || defaultAim) as AimMode;\n const aimMode: AimMode = (aimModeRaw === \"unaimed\" || aimModeRaw === \"partial\" || aimModeRaw === \"full\")\n ? aimModeRaw\n : defaultAim;\n const aimMultiplier = isRanged ? ({ unaimed: 0.25, partial: 0.5, full: 1 } as const)[aimMode] : 1;\n const coverMod = num(query(\"coverMod\")?.value, 0);\n const movementMod = num(query(\"movementMod\")?.value, 0);\n const rangeMod = num(query(\"rangeMod\")?.value, 0);\n const situationalMod = num(query(\"situationalMod\")?.value, 0);\n const customMod = num(query(\"customMod\")?.value, 0);\n const customLabel = String(query(\"customLabel\")?.value || \"\").trim();\n\n const flatModifierTotal = coverMod + movementMod + rangeMod + situationalMod + customMod;\n const preAimTarget = baseSkillTarget + flatModifierTotal;\n const adjustedByAim = Math.floor(preAimTarget * aimMultiplier);\n const finalTarget = clampPercentTarget(adjustedByAim);\n\n const parts: string[] = [`Base ${baseSkillTarget}`];\n if (coverMod) parts.push(`Cover ${coverMod >= 0 ? \"+\" : \"\"}${coverMod}`);\n if (movementMod) parts.push(`Movement ${movementMod >= 0 ? \"+\" : \"\"}${movementMod}`);\n if (rangeMod) parts.push(`Range ${rangeMod >= 0 ? \"+\" : \"\"}${rangeMod}`);\n if (situationalMod) parts.push(`Situational ${situationalMod >= 0 ? \"+\" : \"\"}${situationalMod}`);\n if (customMod) parts.push(`${customLabel || \"Custom\"} ${customMod >= 0 ? \"+\" : \"\"}${customMod}`);\n parts.push(`Pre-Aim ${preAimTarget}`);\n if (isRanged) {\n const aimLabel = aimMode === \"unaimed\" ? \"Quarter\" : aimMode === \"partial\" ? \"Half\" : \"Full\";\n parts.push(`Aim ${aimLabel} x${aimMultiplier}`);\n }\n parts.push(`Final ${finalTarget}`);\n\n done({\n finalTarget,\n summary: parts.join(\" | \"),\n aimMode,\n aimMultiplier,\n flatModifierTotal\n });\n }\n },\n cancel: {\n label: \"Cancel\",\n callback: () => done(null)\n }\n },\n default: \"roll\",\n close: () => done(null)\n }, {\n classes: [\"ringworld-attack-dialog\"],\n width: 510,\n resizable: false\n }).render(true);\n });\n}\n\nasync function consumeWeaponEnergy(actor: any, weapon: any): Promise<{\n sourceLabel: string;\n remaining: number;\n cost: number;\n insufficient: boolean;\n}> {\n const cost = Math.max(0, num(weapon.system.energyUsedPerAttack));\n if (cost <= 0) {\n return { sourceLabel: \"None\", remaining: 0, cost: 0, insufficient: false };\n }\n\n const source = String(weapon.system.powerSource || \"actor\");\n\n if (source === \"powerPack\") {\n const packId = String(weapon.system.powerPackId || \"\");\n const pack = packId ? actor.items.get(packId) : null;\n\n if (!pack || pack.type !== \"gear\" || !pack.system.isPowerPack) {\n ui.notifications?.warn(\"Weapon is configured for a power pack but no valid power pack is linked.\");\n return { sourceLabel: \"Power Pack\", remaining: 0, cost, insufficient: true };\n }\n\n const current = num(pack.system.energy?.value);\n if (current < cost) {\n ui.notifications?.warn(`${pack.name} does not have enough energy.`);\n return { sourceLabel: pack.name, remaining: current, cost, insufficient: true };\n }\n\n const next = current - cost;\n await actor.updateEmbeddedDocuments(\"Item\", [{ _id: pack.id, \"system.energy.value\": next }]);\n return { sourceLabel: pack.name, remaining: next, cost, insufficient: false };\n }\n\n const current = num(actor.system.resources?.energy?.value);\n if (current < cost) {\n ui.notifications?.warn(`${actor.name} does not have enough actor energy.`);\n return { sourceLabel: \"Actor Energy\", remaining: current, cost, insufficient: true };\n }\n\n const next = current - cost;\n await actor.update({ \"system.resources.energy.value\": next });\n return { sourceLabel: \"Actor Energy\", remaining: next, cost, insufficient: false };\n}\n\nfunction getItemArmorAtLocation(item: any, slug: HitLocationSlug): number {\n if (item.type !== \"armor\" || !item.system.equipped || item.system.stowed) return 0;\n if (!item.system.coverage?.[slug]) return 0;\n\n const specific = num(item.system.perLocation?.[slug]);\n if (specific > 0) return specific;\n return num(item.system.value);\n}\n\nfunction getTotalArmorAtLocation(targetActor: any, slug: HitLocationSlug): number {\n const locArmor = num(targetActor.system.hitLocations?.[slug]?.armor);\n const stackedItemsArmor = targetActor.items\n .filter((item: any) => item.type === \"armor\")\n .reduce((sum: number, item: any) => sum + getItemArmorAtLocation(item, slug), 0);\n\n return locArmor + stackedItemsArmor;\n}\n\nfunction buildConditionUpdates(targetActor: any, slug: HitLocationSlug, locationValue: number, locationMax: number, generalHp: number) {\n const current = foundry.utils.deepClone(targetActor.system.conditions ?? {});\n current.unusableLimbs = current.unusableLimbs ?? { rArm: false, lArm: false, rLeg: false, lLeg: false };\n\n if (locationValue <= 0) {\n if (slug === \"head\" || slug === \"chest\") {\n current.unconscious = true;\n current.prone = true;\n }\n\n if (slug === \"abdomen\") {\n current.prone = true;\n current.unusableLimbs.rLeg = true;\n current.unusableLimbs.lLeg = true;\n }\n\n if (slug === \"rArm\" || slug === \"lArm\") {\n current.unusableLimbs[slug] = true;\n }\n\n if (LEG_LOCATIONS.includes(slug)) {\n current.unusableLimbs[slug] = true;\n current.prone = true;\n }\n }\n\n if (generalHp <= 0) {\n current.dying = true;\n }\n\n if ((slug === \"head\" || slug === \"chest\" || slug === \"abdomen\") && locationValue <= -locationMax) {\n current.dying = true;\n }\n\n if (slug === \"head\" && locationValue <= -locationMax) {\n current.dead = true;\n current.unconscious = true;\n }\n\n return current;\n}\n\nasync function syncCoreStatusEffects(targetActor: any, conditions: any): Promise {\n const toggler = (targetActor as any).toggleStatusEffect;\n if (typeof toggler !== \"function\") return;\n\n try {\n await toggler.call(targetActor, \"prone\", { active: !!conditions.prone });\n await toggler.call(targetActor, \"unconscious\", { active: !!conditions.unconscious });\n await toggler.call(targetActor, \"dead\", { active: !!conditions.dead });\n } catch (_err) {\n // Some worlds lock actor-level status manipulation; system conditions still update.\n }\n}\n\nasync function applyLocationDamage(targetActor: any, slug: HitLocationSlug, rawDamage: number): Promise<{\n armor: number;\n finalDamage: number;\n locationValue: number;\n generalHpValue: number;\n}> {\n const location = targetActor.system.hitLocations?.[slug];\n const currentLoc = num(location?.hp?.value);\n const locMax = Math.max(1, num(location?.hp?.max, 1));\n const armor = getTotalArmorAtLocation(targetActor, slug);\n const finalDamage = Math.max(0, rawDamage - armor);\n\n const unclampedLoc = currentLoc - finalDamage;\n const nextLoc = isLimb(slug) ? Math.max(unclampedLoc, -locMax) : unclampedLoc;\n\n const currentGeneral = num(targetActor.system.generalHp?.value);\n const nextGeneral = currentGeneral - finalDamage;\n\n const conditions = buildConditionUpdates(targetActor, slug, nextLoc, locMax, nextGeneral);\n\n const updates: Record = {\n [`system.hitLocations.${slug}.hp.value`]: nextLoc,\n \"system.generalHp.value\": nextGeneral,\n \"system.conditions\": conditions\n };\n\n await targetActor.update(updates);\n await syncCoreStatusEffects(targetActor, conditions);\n\n return {\n armor,\n finalDamage,\n locationValue: nextLoc,\n generalHpValue: nextGeneral\n };\n}\n\nexport async function useWeaponWorkflow(attacker: any, weapon: any): Promise {\n const targetActor = getSingleTargetActor();\n const hasTarget = Boolean(targetActor);\n const targetName = targetActor?.name ?? \"No Target\";\n\n const linkedSkill = resolveLinkedSkill(attacker, weapon);\n const modifierResult = await promptAttackModifiers(attacker, weapon, linkedSkill.target);\n if (!modifierResult) return;\n\n const energy = await consumeWeaponEnergy(attacker, weapon);\n if (energy.insufficient) return;\n\n const attack = await rollPercentile(modifierResult.finalTarget);\n\n if (!attack.isSuccess) {\n const remainingGeneralHp = targetActor ? num(targetActor.system.generalHp?.value, 0) : 0;\n await postAttackCard({\n actor: attacker,\n actorName: attacker.name,\n actorImg: attacker.img,\n targetName,\n hasTarget,\n weaponName: weapon.name,\n weaponImg: weapon.img,\n skillName: linkedSkill.name,\n skillImg: linkedSkill.img,\n attackRoll: attack.roll,\n attackTarget: attack.target,\n attackOutcomeLabel: outcomeLabel(attack.outcome),\n isSuccess: false,\n isSpecial: attack.isSpecial,\n isFumble: attack.isFumble,\n hitLocationLabel: \"-\",\n hitLocationRoll: null,\n rawDamage: 0,\n armor: 0,\n finalDamage: 0,\n remainingLocationHp: 0,\n remainingGeneralHp,\n energySourceLabel: energy.sourceLabel,\n energyCost: energy.cost,\n energyRemaining: energy.remaining,\n notes: `Attack failed before damage was rolled. ${modifierResult.summary}`\n });\n return;\n }\n\n const hitSelection = await chooseOrRollHitLocation(targetActor ?? attacker);\n if (!hitSelection) return;\n\n const damageFormula = String(weapon.system.damageFormula || \"0\").trim() || \"0\";\n const damageRoll = await evaluateRollWithDice(damageFormula);\n const rawDamage = Math.max(0, num(damageRoll.total));\n\n const application = targetActor\n ? await applyLocationDamage(targetActor, hitSelection.slug, rawDamage)\n : {\n armor: 0,\n finalDamage: rawDamage,\n locationValue: 0,\n generalHpValue: 0\n };\n\n await postAttackCard({\n actor: attacker,\n actorName: attacker.name,\n actorImg: attacker.img,\n targetName,\n hasTarget,\n weaponName: weapon.name,\n weaponImg: weapon.img,\n skillName: linkedSkill.name,\n skillImg: linkedSkill.img,\n attackRoll: attack.roll,\n attackTarget: attack.target,\n attackOutcomeLabel: outcomeLabel(attack.outcome),\n isSuccess: attack.isSuccess,\n isSpecial: attack.isSpecial,\n isFumble: attack.isFumble,\n hitLocationLabel: hitSelection.label,\n hitLocationRoll: hitSelection.rolled,\n rawDamage,\n armor: application.armor,\n finalDamage: application.finalDamage,\n remainingLocationHp: application.locationValue,\n remainingGeneralHp: application.generalHpValue,\n energySourceLabel: energy.sourceLabel,\n energyCost: energy.cost,\n energyRemaining: energy.remaining,\n notes: targetActor\n ? `Location and general HP were both reduced by final damage, per Ringworld rules. ${modifierResult.summary}`\n : `No target selected: roll resolved with no damage application. ${modifierResult.summary}`\n });\n}\n\nexport function recomputeHitPointMaximums(actor: any): void {\n if (actor.type !== \"explorer\" && actor.type !== \"creature\") return;\n\n // Ringworld general HP is CON + MAS. Location HP then scales from that general HP.\n const con = num(actor.system?.characteristics?.con, 10);\n const mas = num(actor.system?.characteristics?.mas, 10);\n const existingMax = num(actor.system.generalHp?.max, 0);\n const existingValue = num(actor.system.generalHp?.value, 0);\n const generalMax = Math.max(1, Math.floor(con + mas));\n\n actor.system.generalHp = actor.system.generalHp ?? { value: generalMax, max: generalMax };\n actor.system.generalHp.max = generalMax;\n const hadExisting = existingMax > 0;\n const wasFull = hadExisting && existingValue >= existingMax;\n const looksPlaceholder = existingMax <= 1 && existingValue <= 1;\n const preservedDamage = hadExisting ? Math.max(0, existingMax - existingValue) : 0;\n const recomputedValue = (!hadExisting || wasFull || looksPlaceholder)\n ? generalMax\n : Math.max(0, generalMax - preservedDamage);\n\n actor.system.generalHp.value = Math.min(Math.max(0, recomputedValue), generalMax);\n const generalAtFull = num(actor.system.generalHp.value, generalMax) >= generalMax;\n\n actor.system.hitLocations = actor.system.hitLocations ?? {};\n actor.system.hitLocationSchema = actor.system.hitLocationSchema ?? \"auto\";\n const schema = getActorHitLocationSchema(actor);\n\n for (const entry of schema.entries) {\n const slug = entry.slug;\n const loc = actor.system.hitLocations[slug] ?? {\n label: slug,\n hp: { value: 1, max: 1 },\n armor: 0\n };\n loc.label = entry.label;\n const hpFactor = Math.max(0.05, num((entry as any).hpFactor, 0.3));\n const max = Math.max(1, Math.ceil(generalMax * hpFactor));\n\n const previousMax = num(loc.hp?.max, 1);\n const previousValue = num(loc.hp?.value, previousMax);\n const isPlaceholder = previousMax <= 1 && previousValue <= 1;\n const shouldBackfillToFull = generalAtFull && previousValue <= 1 && max > 1;\n\n loc.hp.max = max;\n loc.hp.value = (isPlaceholder || shouldBackfillToFull) ? max : Math.min(previousValue, max);\n actor.system.hitLocations[slug] = loc;\n }\n}\n\nexport function getTemplatePath(path: string): string {\n return `systems/${SYSTEM_ID}/templates/${path}`;\n}\n","import { SYSTEM_ID } from \"./constants\";\nimport { normalizeSkillName, RINGWORLD_BASE_SKILLS } from \"./data-base-skills\";\nimport { skillIconPath } from \"./skill-icons\";\n\nexport type GravityType = \"light\" | \"normal\" | \"heavy\";\n\ninterface SpeciesCatalogEntry {\n name: string;\n playable: boolean;\n gravity: GravityType;\n homeworld: string;\n modifiers: {\n str: number;\n mas: number;\n con: number;\n int: number;\n pow: number;\n dex: number;\n app: number;\n edu: number;\n age: number;\n };\n notes: string;\n}\n\ninterface WeaponCatalogEntry {\n name: string;\n equipped?: boolean;\n weight?: number;\n linkedSkill: string;\n attackType: \"melee\" | \"ranged\";\n damageFormula: string;\n range: { close: string; medium: string; long: string };\n energyUsedPerAttack: number;\n ammoUsedPerAttack: number;\n powerSource: \"actor\" | \"powerPack\";\n notes: string;\n}\n\ninterface ArmorCatalogEntry {\n name: string;\n equipped: boolean;\n weight?: number;\n value: number;\n coverage: Record;\n notes: string;\n}\n\ninterface GearCatalogEntry {\n name: string;\n equipped?: boolean;\n weight: number;\n cost: number;\n energyUse: number;\n powerSupplyNotes: string;\n isPowerPack: boolean;\n energy: { value: number; max: number };\n notes: string;\n}\n\ninterface PursuitCatalogEntry {\n name: string;\n rollRange: string;\n wealthValue: number;\n occupationPointsPerYear: number;\n fixedSkills: string[];\n optionalSkillGroups?: Array<{\n choose: number;\n skills: string[];\n }>;\n notes: string;\n}\n\ninterface CatalogSeed {\n key: string;\n data: any;\n folderKey: \"species\" | \"skills\" | \"pursuits\" | \"gear\" | \"armor\" | \"weaponMelee\" | \"weaponRanged\";\n img: string;\n}\n\nexport const RINGWORLD_CATALOG_VERSION = 9;\n\nconst ICONS = {\n speciesDefault: \"systems/ringworld/assets/icons/species.svg\",\n skillDefault: \"systems/ringworld/assets/icons/skill.svg\",\n pursuitDefault: \"systems/ringworld/assets/icons/pursuit.svg\",\n weaponMelee: \"systems/ringworld/assets/icons/weapon-melee.svg\",\n weaponRanged: \"systems/ringworld/assets/icons/weapon-ranged.svg\",\n armor: \"systems/ringworld/assets/icons/armor.svg\",\n gearDefault: \"systems/ringworld/assets/icons/gear.svg\",\n powerPackDefault: \"systems/ringworld/assets/icons/power-pack.svg\"\n};\n\nfunction iconSlug(name: string): string {\n return String(name)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nfunction speciesIconPath(name: string): string {\n return `systems/ringworld/assets/icons/species/${iconSlug(name)}.svg`;\n}\n\nfunction weaponIconPath(name: string): string {\n return `systems/ringworld/assets/icons/weapon/${iconSlug(name)}.svg`;\n}\n\nfunction armorIconPath(name: string): string {\n return `systems/ringworld/assets/icons/armor/${iconSlug(name)}.svg`;\n}\n\nfunction gearIconPath(name: string): string {\n return `systems/ringworld/assets/icons/gear/${iconSlug(name)}.svg`;\n}\n\nfunction pursuitIconPath(name: string): string {\n return `systems/ringworld/assets/icons/pursuit/${iconSlug(name)}.svg`;\n}\n\nconst allCoverage = {\n rLeg: true,\n lLeg: true,\n abdomen: true,\n chest: true,\n rArm: true,\n lArm: true,\n head: true\n};\n\nconst torsoCoverage = {\n rLeg: false,\n lLeg: false,\n abdomen: true,\n chest: true,\n rArm: false,\n lArm: false,\n head: false\n};\n\nconst headCoverage = {\n rLeg: false,\n lLeg: false,\n abdomen: false,\n chest: false,\n rArm: false,\n lArm: false,\n head: true\n};\n\nconst armCoverage = {\n rLeg: false,\n lLeg: false,\n abdomen: false,\n chest: false,\n rArm: true,\n lArm: true,\n head: false\n};\n\nconst legCoverage = {\n rLeg: true,\n lLeg: true,\n abdomen: false,\n chest: false,\n rArm: false,\n lArm: false,\n head: false\n};\n\nexport const SPECIES_CATALOG: SpeciesCatalogEntry[] = [\n {\n name: \"Human\",\n playable: true,\n gravity: \"normal\",\n homeworld: \"Known Space\",\n modifiers: { str: 0, mas: 0, con: 0, int: 0, pow: 0, dex: 0, app: 0, edu: 0, age: 0 },\n notes: \"Baseline human explorer profile.\"\n },\n {\n name: \"Earth Human\",\n playable: true,\n gravity: \"normal\",\n homeworld: \"Earth\",\n modifiers: { str: 0, mas: 0, con: 0, int: 0, pow: 1, dex: 0, app: 0, edu: 0, age: 0 },\n notes: \"Earth-line humans with stronger latent psionic potential.\"\n },\n {\n name: \"Kzinti\",\n playable: true,\n gravity: \"heavy\",\n homeworld: \"Kzin\",\n modifiers: { str: 2, mas: 2, con: 1, int: -1, pow: 0, dex: 0, app: -2, edu: 0, age: 0 },\n notes: \"Predatory felinoids with high physical strength.\"\n },\n {\n name: \"Pierson's Puppeteer\",\n playable: true,\n gravity: \"light\",\n homeworld: \"Fleet of Worlds\",\n modifiers: { str: -2, mas: -2, con: -1, int: 2, pow: 1, dex: 1, app: 0, edu: 1, age: 0 },\n notes: \"Highly intelligent, cautious engineers.\"\n },\n {\n name: \"Dolphin\",\n playable: true,\n gravity: \"normal\",\n homeworld: \"Human Space Oceans\",\n modifiers: { str: -2, mas: 1, con: 1, int: 0, pow: 0, dex: 0, app: 0, edu: 0, age: 0 },\n notes: \"Aquatic specialists with enhanced endurance.\"\n },\n {\n name: \"Trinoc\",\n playable: true,\n gravity: \"light\",\n homeworld: \"Trinoc Worlds\",\n modifiers: { str: -1, mas: -1, con: 0, int: 1, pow: 0, dex: 1, app: 0, edu: 0, age: 0 },\n notes: \"Three-eyed flyers with excellent perception.\"\n },\n {\n name: \"Martian\",\n playable: true,\n gravity: \"light\",\n homeworld: \"Mars\",\n modifiers: { str: -1, mas: -1, con: -1, int: 1, pow: 0, dex: 1, app: 0, edu: 0, age: 0 },\n notes: \"Light-gravity adapted intellect-focused profile.\"\n },\n {\n name: \"Pierin\",\n playable: true,\n gravity: \"normal\",\n homeworld: \"Pierin Space\",\n modifiers: { str: 0, mas: 0, con: 0, int: 1, pow: 0, dex: 0, app: 1, edu: 0, age: 0 },\n notes: \"Socially adept, technically curious explorers.\"\n },\n {\n name: \"Boat People\",\n playable: true,\n gravity: \"normal\",\n homeworld: \"Ringworld\",\n modifiers: { str: 0, mas: 0, con: 1, int: 0, pow: 0, dex: 0, app: 0, edu: -1, age: 0 },\n notes: \"Ringworld hominids adapted to river and sea cultures.\"\n },\n {\n name: \"Builder\",\n playable: true,\n gravity: \"normal\",\n homeworld: \"Ringworld\",\n modifiers: { str: 1, mas: 1, con: 0, int: 0, pow: 0, dex: 0, app: -1, edu: 0, age: 0 },\n notes: \"Ringworld labor stock with robust build.\"\n },\n {\n name: \"Ghoul\",\n playable: true,\n gravity: \"normal\",\n homeworld: \"Ringworld\",\n modifiers: { str: 0, mas: 0, con: 1, int: -1, pow: 0, dex: 1, app: -2, edu: -1, age: 0 },\n notes: \"Scavenger subspecies with strong survival instincts.\"\n },\n {\n name: \"Bandersnatchi\",\n playable: false,\n gravity: \"heavy\",\n homeworld: \"Known Space\",\n modifiers: { str: 4, mas: 4, con: 3, int: 0, pow: 0, dex: -2, app: -3, edu: 0, age: 10 },\n notes: \"Massive engineered beings; typically GM creature profile.\"\n }\n];\n\nexport const WEAPON_CATALOG: WeaponCatalogEntry[] = [\n {\n name: \"Variable Sword\",\n equipped: true,\n weight: 1.5,\n linkedSkill: \"V. Sword, F. Laser\",\n attackType: \"melee\",\n damageFormula: \"1d10+1\",\n range: { close: \"Melee\", medium: \"-\", long: \"-\" },\n energyUsedPerAttack: 1,\n ammoUsedPerAttack: 0,\n powerSource: \"powerPack\",\n notes: \"High-energy blade weapon.\"\n },\n {\n name: \"Monofilament Knife\",\n equipped: true,\n weight: 0.2,\n linkedSkill: \"Archaic Melee Weapon\",\n attackType: \"melee\",\n damageFormula: \"1d6+1\",\n range: { close: \"Melee\", medium: \"-\", long: \"-\" },\n energyUsedPerAttack: 0,\n ammoUsedPerAttack: 0,\n powerSource: \"actor\",\n notes: \"Fine-edge close combat blade.\"\n },\n {\n name: \"Shock Baton\",\n equipped: true,\n weight: 1.2,\n linkedSkill: \"Unarmed Combat\",\n attackType: \"melee\",\n damageFormula: \"1d8+1\",\n range: { close: \"Melee\", medium: \"-\", long: \"-\" },\n energyUsedPerAttack: 1,\n ammoUsedPerAttack: 0,\n powerSource: \"powerPack\",\n notes: \"Close-quarters stun baton.\"\n },\n {\n name: \"Flashlight Laser\",\n equipped: true,\n weight: 0.8,\n linkedSkill: \"V. Sword, F. Laser\",\n attackType: \"ranged\",\n damageFormula: \"1d10\",\n range: { close: \"Short\", medium: \"Medium\", long: \"Long\" },\n energyUsedPerAttack: 2,\n ammoUsedPerAttack: 0,\n powerSource: \"powerPack\",\n notes: \"Compact laser unit.\"\n },\n {\n name: \"Laser Pistol\",\n equipped: true,\n weight: 1.4,\n linkedSkill: \"Handgun, Energy\",\n attackType: \"ranged\",\n damageFormula: \"1d10+1\",\n range: { close: \"Short\", medium: \"Medium\", long: \"Long\" },\n energyUsedPerAttack: 2,\n ammoUsedPerAttack: 0,\n powerSource: \"powerPack\",\n notes: \"Common energy sidearm.\"\n },\n {\n name: \"Laser Rifle\",\n equipped: true,\n weight: 4.5,\n linkedSkill: \"Heavy Weapon, Energy\",\n attackType: \"ranged\",\n damageFormula: \"2d6+2\",\n range: { close: \"Short\", medium: \"Long\", long: \"Extreme\" },\n energyUsedPerAttack: 3,\n ammoUsedPerAttack: 0,\n powerSource: \"powerPack\",\n notes: \"Standard long-arm laser weapon.\"\n },\n {\n name: \"Stunner\",\n equipped: true,\n weight: 1.0,\n linkedSkill: \"Handgun, Energy\",\n attackType: \"ranged\",\n damageFormula: \"1d6\",\n range: { close: \"Short\", medium: \"Medium\", long: \"Long\" },\n energyUsedPerAttack: 1,\n ammoUsedPerAttack: 0,\n powerSource: \"powerPack\",\n notes: \"Low-lethality disruption sidearm.\"\n },\n {\n name: \"Handgun (Projectile)\",\n equipped: true,\n weight: 1.2,\n linkedSkill: \"Handgun, Projectile\",\n attackType: \"ranged\",\n damageFormula: \"1d10\",\n range: { close: \"Short\", medium: \"Medium\", long: \"Long\" },\n energyUsedPerAttack: 0,\n ammoUsedPerAttack: 1,\n powerSource: \"actor\",\n notes: \"Conventional projectile sidearm.\"\n },\n {\n name: \"Rifle (Projectile)\",\n equipped: true,\n weight: 5.0,\n linkedSkill: \"Heavy Weapon, Projectile\",\n attackType: \"ranged\",\n damageFormula: \"2d6\",\n range: { close: \"Short\", medium: \"Long\", long: \"Extreme\" },\n energyUsedPerAttack: 0,\n ammoUsedPerAttack: 1,\n powerSource: \"actor\",\n notes: \"Conventional projectile rifle.\"\n },\n {\n name: \"Slug Thrower Carbine\",\n equipped: true,\n weight: 3.8,\n linkedSkill: \"Heavy Weapon, Projectile\",\n attackType: \"ranged\",\n damageFormula: \"2d6+1\",\n range: { close: \"Short\", medium: \"Long\", long: \"Extreme\" },\n energyUsedPerAttack: 0,\n ammoUsedPerAttack: 1,\n powerSource: \"actor\",\n notes: \"Compact projectile carbine.\"\n }\n];\n\nexport const ARMOR_CATALOG: ArmorCatalogEntry[] = [\n { name: \"Cloth Vest\", equipped: true, weight: 2, value: 1, coverage: torsoCoverage, notes: \"Basic torso protection.\" },\n { name: \"Soft Combat Vest\", equipped: true, weight: 4, value: 2, coverage: torsoCoverage, notes: \"Flexible tactical vest.\" },\n { name: \"Combat Harness\", equipped: true, weight: 5, value: 2, coverage: { ...torsoCoverage, rArm: true, lArm: true }, notes: \"Torso + upper arms.\" },\n { name: \"Armored Coat\", equipped: true, weight: 7, value: 3, coverage: { ...torsoCoverage, rArm: true, lArm: true }, notes: \"Heavy coat with arm plating.\" },\n { name: \"Leg Guards\", equipped: true, weight: 4, value: 2, coverage: legCoverage, notes: \"Lower-body armor.\" },\n { name: \"Arm Bracers\", equipped: true, weight: 2, value: 2, coverage: armCoverage, notes: \"Forearm and upper arm guards.\" },\n { name: \"Combat Helmet\", equipped: true, weight: 1.5, value: 2, coverage: headCoverage, notes: \"Cranial protection.\" },\n { name: \"Full Combat Suit\", equipped: true, weight: 12, value: 3, coverage: allCoverage, notes: \"Full-body armored suit.\" },\n { name: \"Impact Suit\", equipped: true, weight: 16, value: 4, coverage: allCoverage, notes: \"High-end impact and hazard suit.\" }\n];\n\nexport const GEAR_CATALOG: GearCatalogEntry[] = [\n {\n name: \"Power Pack R20\",\n weight: 1,\n cost: 100,\n energyUse: 0,\n powerSupplyNotes: \"Rechargeable power pack\",\n isPowerPack: true,\n energy: { value: 20, max: 20 },\n notes: \"Standard personal battery pack.\"\n },\n {\n name: \"Power Pack R50\",\n weight: 2,\n cost: 220,\n energyUse: 0,\n powerSupplyNotes: \"Rechargeable power pack\",\n isPowerPack: true,\n energy: { value: 50, max: 50 },\n notes: \"Extended personal battery pack.\"\n },\n {\n name: \"Power Pack R100\",\n weight: 4,\n cost: 400,\n energyUse: 0,\n powerSupplyNotes: \"Rechargeable power pack\",\n isPowerPack: true,\n energy: { value: 100, max: 100 },\n notes: \"High-capacity field battery.\"\n },\n { name: \"Portable Generator\", weight: 8, cost: 700, energyUse: 0, powerSupplyNotes: \"Provides charging capacity\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Portable recharge generator.\" },\n { name: \"MicroMind Unit\", weight: 0.5, cost: 900, energyUse: 1, powerSupplyNotes: \"Requires external power\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Portable advanced computation module.\" },\n { name: \"Memory Bubble Reader\", weight: 0.5, cost: 450, energyUse: 1, powerSupplyNotes: \"Standard battery compatible\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Reads and writes memory bubbles.\" },\n { name: \"Autodoc (Portable)\", weight: 18, cost: 3500, energyUse: 6, powerSupplyNotes: \"High draw medical station\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Field medical support unit.\" },\n { name: \"Medkit\", weight: 1, cost: 120, energyUse: 0, powerSupplyNotes: \"None\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Manual emergency treatment supplies.\" },\n { name: \"Bioscanner\", weight: 1, cost: 300, energyUse: 1, powerSupplyNotes: \"Battery or generator\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Vitals and hazard scanning tool.\" },\n { name: \"Survey Scanner\", weight: 2, cost: 500, energyUse: 2, powerSupplyNotes: \"Battery or generator\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Terrain and structure analysis scanner.\" },\n { name: \"Translator Module\", weight: 0.5, cost: 650, energyUse: 1, powerSupplyNotes: \"Battery compatible\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Linguistic conversion and support.\" },\n { name: \"Holo Projector\", weight: 1.5, cost: 800, energyUse: 2, powerSupplyNotes: \"Battery compatible\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Short-range holographic projection.\" },\n { name: \"Stasis Box\", weight: 12, cost: 2600, energyUse: 5, powerSupplyNotes: \"Continuous power required\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Suspended animation container.\" },\n { name: \"Reactionless Flycycle\", weight: 35, cost: 4200, energyUse: 4, powerSupplyNotes: \"Vehicle power core\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Single-rider reactionless vehicle.\" },\n { name: \"Field Toolkit\", weight: 2, cost: 180, energyUse: 0, powerSupplyNotes: \"None\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Mechanical and electronics repair kit.\" },\n { name: \"Vac Suit\", weight: 7, cost: 1400, energyUse: 1, powerSupplyNotes: \"Suit battery required\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Pressure and vacuum survival suit.\" },\n { name: \"Air Reprocessor\", weight: 3, cost: 950, energyUse: 2, powerSupplyNotes: \"Continuous power\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Atmosphere recycling support unit.\" },\n { name: \"Signal Beacon\", weight: 1, cost: 140, energyUse: 1, powerSupplyNotes: \"Battery compatible\", isPowerPack: false, energy: { value: 0, max: 0 }, notes: \"Emergency and navigation beacon.\" }\n];\n\nexport const PURSUIT_CATALOG: PursuitCatalogEntry[] = [\n {\n name: \"Artist\",\n rollRange: \"01-07\",\n wealthValue: 0,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Athletics\", \"Computers\", \"Fast Talk\", \"Fine Arts\", \"Hide\", \"Musicianship\", \"Observe\", \"Perform\", \"Psychology\", \"Scent\", \"Theology\"],\n notes: \"Creative specialist pursuit.\"\n },\n {\n name: \"Colonist\",\n rollRange: \"08-27\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Atmospheric Craft\", \"Bargain\", \"Ground Vehicle\", \"Farming\", \"Handgun, Energy\", \"Personal Flyer\", \"Listen\", \"Heavy Weapon, Energy\", \"Observe\", \"Planetology\", \"Search\", \"Track\"],\n notes: \"Settlement and frontier operations pursuit.\"\n },\n {\n name: \"Criminal\",\n rollRange: \"28-29\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Bargain\", \"Computers\", \"Fast Talk\", \"Hide\", \"Personal Flyer\", \"Handgun, Energy\", \"Handgun, Projectile\", \"Sneak\", \"Unarmed Combat\"],\n notes: \"Underground survival pursuit.\"\n },\n {\n name: \"Dilettante\",\n rollRange: \"30\",\n wealthValue: 5,\n occupationPointsPerYear: 15,\n fixedSkills: [],\n notes: \"Any skills allowed; uses 15 occupation points per year.\"\n },\n {\n name: \"Engineer\",\n rollRange: \"31-36\",\n wealthValue: 2,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Astronomy\", \"Chemistry\", \"Computers\", \"Engineering\", \"Ground Vehicle\", \"Mathematics\", \"Personal Flyer\", \"Physics\", \"Planetology\", \"Reaction Drive\", \"Reactionless Drive\", \"Repair\"],\n notes: \"Technical design and maintenance pursuit.\"\n },\n {\n name: \"Entertainer\",\n rollRange: \"37-38\",\n wealthValue: 2,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Athletics\", \"Bargain\", \"Own Language\", \"Musicianship\", \"Observe\", \"Orate\", \"Perform\", \"Psychology\", \"Search\", \"Sneak\"],\n notes: \"Performance and social influence pursuit.\"\n },\n {\n name: \"Explorer\",\n rollRange: \"39-52\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Athletics\", \"Atmospheric Craft\", \"Bargain\", \"Biology\", \"Botany\", \"Emergency Treatment\", \"Engineering\", \"Ground Vehicle\", \"Handgun, Energy\", \"Heavy Weapon, Energy\", \"Hyperdrive\", \"Observe\", \"Planetology\", \"Reactionless Drive\", \"Repair\", \"Search\", \"Track\", \"Zoology\"],\n notes: \"Survey and expedition pursuit.\"\n },\n {\n name: \"Functionary\",\n rollRange: \"53-56\",\n wealthValue: 3,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Bargain\", \"Debate\", \"Fast Talk\", \"History\", \"Law\", \"Observe\", \"Orate\", \"Psychology\", \"Strategy\", \"Theology\"],\n notes: \"Administrative and governance pursuit.\"\n },\n {\n name: \"Journalist/Writer\",\n rollRange: \"57-59\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Anthropology\", \"Bargain\", \"Computers\", \"Debate\", \"History\", \"Fine Arts\", \"Own Language\", \"Law\", \"Observe\", \"Psychology\", \"Search\", \"Sneak\", \"Unarmed Combat\", \"Weapons System\"],\n notes: \"Investigation and reporting pursuit.\"\n },\n {\n name: \"Miner\",\n rollRange: \"60-65\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Atmospheric Craft\", \"Aquatic Vehicle\", \"Bargain\", \"Climb (Custom)\", \"Computers\", \"Emergency Treatment\", \"Engineering\", \"Handgun, Energy\", \"Heavy Weapon, Energy\", \"Observe\", \"Personal Flyer\", \"Planetology\", \"Reaction Drive\", \"Reactionless Drive\", \"Repair\", \"Search\"],\n notes: \"Resource extraction frontier pursuit.\"\n },\n {\n name: \"Pilot\",\n rollRange: \"66-68\",\n wealthValue: 3,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Astronomy\", \"Atmospheric Craft\", \"Computers\", \"Engineering\", \"Hyperdrive\", \"History\", \"Law\", \"Mathematics\", \"Physics\", \"Reaction Drive\", \"Reactionless Drive\", \"Repair\", \"Strategy\", \"Weapons System\"],\n notes: \"Flight and spacecraft operations pursuit.\"\n },\n {\n name: \"Police Officer\",\n rollRange: \"69-76\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Atmospheric Craft\", \"Bargain\", \"Computers\", \"Fast Talk\", \"Emergency Treatment\", \"Handgun, Energy\", \"Handgun, Projectile\", \"Heavy Weapon, Energy\", \"Heavy Weapon, Projectile\", \"Law\", \"Observe\", \"Personal Flyer\", \"Psychology\", \"Search\", \"Unarmed Combat\"],\n notes: \"Law enforcement and response pursuit.\"\n },\n {\n name: \"Politician\",\n rollRange: \"77-78\",\n wealthValue: 3,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Bargain\", \"Computers\", \"Debate\", \"Fast Talk\", \"History\", \"Orate\", \"Own Language\", \"Perform\", \"Psychology\"],\n optionalSkillGroups: [\n {\n choose: 1,\n skills: [\"Engineering\", \"Farming\", \"Second Languages\", \"Strategy\", \"Theology\"]\n }\n ],\n notes: \"Public office and influence pursuit.\"\n },\n {\n name: \"Prisoner\",\n rollRange: \"79-80\",\n wealthValue: 0,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Bargain\", \"Fast Talk\", \"Hide\", \"Observe\", \"Psychology\", \"Search\", \"Sneak\", \"Unarmed Combat\"],\n notes: \"Carceral survival pursuit.\"\n },\n {\n name: \"Scholar\",\n rollRange: \"81-84\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [],\n optionalSkillGroups: [\n {\n choose: 2,\n skills: [\"Anthropology\", \"Astronomy\", \"Biology\", \"Botany\", \"Chemistry\", \"Computers\", \"Engineering\", \"Fine Arts\", \"History\", \"Law\", \"Mathematics\", \"Own Language\", \"Physics\", \"Planetology\", \"Second Languages\", \"Theology\", \"Zoology\"]\n }\n ],\n notes: \"Academic pursuit; pick up to two listed skills.\"\n },\n {\n name: \"Scientific Researcher\",\n rollRange: \"85-94\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [],\n optionalSkillGroups: [\n {\n choose: 3,\n skills: [\"Anthropology\", \"Astronomy\", \"Bargain\", \"Biology\", \"Botany\", \"Chemistry\", \"Computers\", \"Engineering\", \"History\", \"Mathematics\", \"Observe\", \"Personal Flyer\", \"Physics\", \"Planetology\", \"Psychology\", \"Repair\", \"Search\", \"Zoology\"]\n }\n ],\n notes: \"Research pursuit; pick up to three listed skills.\"\n },\n {\n name: \"Soldier\",\n rollRange: \"95-98\",\n wealthValue: 1,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Athletics\", \"Atmospheric Craft\", \"Aquatic Vehicle\", \"Computers\", \"Emergency Treatment\", \"Fast Talk\", \"Ground Vehicle\", \"Handgun, Energy\", \"Handgun, Projectile\", \"Heavy Weapon, Energy\", \"Heavy Weapon, Projectile\"],\n notes: \"Military operations pursuit.\"\n },\n {\n name: \"Zealot\",\n rollRange: \"99-00\",\n wealthValue: 0,\n occupationPointsPerYear: 20,\n fixedSkills: [\"Bargain\", \"Debate\", \"Fast Talk\", \"Fine Arts\", \"Handgun, Energy\", \"Handgun, Projectile\", \"Hide\", \"Own Language\", \"Law\", \"Strategy\", \"Orate\", \"Psychology\", \"Theology\"],\n notes: \"Ideological dedication pursuit.\"\n }\n];\n\nexport function speciesCatalogForGeneration(): SpeciesCatalogEntry[] {\n return SPECIES_CATALOG.filter((entry) => entry.playable);\n}\n\nfunction itemKey(type: string, name: string): string {\n return `${type}:${normalizeSkillName(name)}`;\n}\n\nfunction itemTypeAvailable(type: string): boolean {\n const directTypes = (getDocumentClass(\"Item\") as any)?.TYPES;\n if (Array.isArray(directTypes)) return directTypes.includes(type);\n\n const docTypes = (game as any)?.system?.documentTypes?.Item;\n if (Array.isArray(docTypes)) return docTypes.includes(type);\n if (docTypes && typeof docTypes === \"object\") return Object.prototype.hasOwnProperty.call(docTypes, type);\n\n const configModels = (CONFIG as any)?.Item?.dataModels;\n if (configModels && typeof configModels === \"object\" && Object.prototype.hasOwnProperty.call(configModels, type)) {\n return true;\n }\n\n const gameModels = (game as any)?.model?.Item;\n if (gameModels && typeof gameModels === \"object\" && Object.prototype.hasOwnProperty.call(gameModels, type)) {\n return true;\n }\n\n return false;\n}\n\nfunction toSkillItem(entry: (typeof RINGWORLD_BASE_SKILLS)[number]): CatalogSeed {\n return {\n key: itemKey(\"skill\", entry.name),\n folderKey: \"skills\",\n img: skillIconPath(entry.name) || ICONS.skillDefault,\n data: {\n name: entry.name,\n type: \"skill\",\n system: {\n value: entry.base,\n category: entry.category,\n specialization: entry.specialization ?? \"\"\n }\n }\n };\n}\n\nfunction toSpeciesItem(entry: SpeciesCatalogEntry): CatalogSeed {\n const img = speciesIconPath(entry.name) || ICONS.speciesDefault;\n return {\n key: itemKey(\"species\", entry.name),\n folderKey: \"species\",\n img,\n data: {\n name: entry.name,\n type: \"species\",\n system: {\n playable: entry.playable,\n gravity: entry.gravity,\n homeworld: entry.homeworld,\n modifiers: entry.modifiers,\n notes: entry.notes\n }\n }\n };\n}\n\nfunction toWeaponItem(entry: WeaponCatalogEntry): CatalogSeed {\n const folderKey = entry.attackType === \"melee\" ? \"weaponMelee\" : \"weaponRanged\";\n const img = weaponIconPath(entry.name) || (entry.attackType === \"melee\" ? ICONS.weaponMelee : ICONS.weaponRanged);\n return {\n key: itemKey(\"weapon\", entry.name),\n folderKey,\n img,\n data: {\n name: entry.name,\n type: \"weapon\",\n system: {\n equipped: entry.equipped ?? true,\n stowed: false,\n weight: entry.weight ?? 0,\n linkedSkill: entry.linkedSkill,\n linkedSkillId: \"\",\n attackType: entry.attackType,\n damageFormula: entry.damageFormula,\n range: entry.range,\n energyUsedPerAttack: entry.energyUsedPerAttack,\n ammoUsedPerAttack: entry.ammoUsedPerAttack,\n powerSource: entry.powerSource,\n powerPackId: \"\",\n notes: entry.notes\n }\n }\n };\n}\n\nfunction toArmorItem(entry: ArmorCatalogEntry): CatalogSeed {\n const img = armorIconPath(entry.name) || ICONS.armor;\n return {\n key: itemKey(\"armor\", entry.name),\n folderKey: \"armor\",\n img,\n data: {\n name: entry.name,\n type: \"armor\",\n system: {\n equipped: entry.equipped,\n stowed: false,\n weight: entry.weight ?? 0,\n value: entry.value,\n coverage: entry.coverage,\n perLocation: {\n rLeg: 0,\n lLeg: 0,\n abdomen: 0,\n chest: 0,\n rArm: 0,\n lArm: 0,\n head: 0\n },\n notes: entry.notes\n }\n }\n };\n}\n\nfunction toGearItem(entry: GearCatalogEntry): CatalogSeed {\n const img = gearIconPath(entry.name) || (entry.isPowerPack ? ICONS.powerPackDefault : ICONS.gearDefault);\n return {\n key: itemKey(\"gear\", entry.name),\n folderKey: \"gear\",\n img,\n data: {\n name: entry.name,\n type: \"gear\",\n system: {\n equipped: entry.equipped ?? true,\n stowed: false,\n weight: entry.weight,\n cost: entry.cost,\n energyUse: entry.energyUse,\n powerSupplyNotes: entry.powerSupplyNotes,\n isPowerPack: entry.isPowerPack,\n energy: entry.energy,\n notes: entry.notes\n }\n }\n };\n}\n\nfunction toPursuitItem(entry: PursuitCatalogEntry): CatalogSeed {\n const img = pursuitIconPath(entry.name) || ICONS.pursuitDefault;\n return {\n key: itemKey(\"pursuit\", entry.name),\n folderKey: \"pursuits\",\n img,\n data: {\n name: entry.name,\n type: \"pursuit\",\n system: {\n wealthValue: entry.wealthValue,\n occupationPointsPerYear: entry.occupationPointsPerYear,\n rollRange: entry.rollRange,\n fixedSkills: entry.fixedSkills,\n optionalSkillGroups: entry.optionalSkillGroups ?? [],\n notes: entry.notes\n }\n }\n };\n}\n\nfunction pursuitAsGearNotes(entry: PursuitCatalogEntry): string {\n const fixed = entry.fixedSkills.length ? `Fixed skills: ${entry.fixedSkills.join(\", \")}` : \"Fixed skills: (none)\";\n const optional = (entry.optionalSkillGroups ?? []).map((group) => `Choose ${group.choose}: ${group.skills.join(\", \")}`).join(\" | \");\n const optionalText = optional ? ` Optional: ${optional}.` : \"\";\n return `${entry.notes} ${fixed}.${optionalText} Wealth ${entry.wealthValue}. Occupation points/year ${entry.occupationPointsPerYear}.`;\n}\n\nfunction toPursuitFallbackGearItem(entry: PursuitCatalogEntry): CatalogSeed {\n const img = pursuitIconPath(entry.name) || ICONS.pursuitDefault;\n const itemName = `Pursuit: ${entry.name}`;\n return {\n key: itemKey(\"gear\", itemName),\n folderKey: \"pursuits\",\n img,\n data: {\n name: itemName,\n type: \"gear\",\n system: {\n equipped: false,\n stowed: true,\n weight: 0,\n cost: 0,\n energyUse: 0,\n powerSupplyNotes: \"Pursuit reference\",\n isPowerPack: false,\n energy: { value: 0, max: 0 },\n notes: pursuitAsGearNotes(entry)\n },\n flags: {\n ringworld: {\n pursuitData: {\n pursuitName: entry.name,\n rollRange: entry.rollRange,\n wealthValue: entry.wealthValue,\n occupationPointsPerYear: entry.occupationPointsPerYear,\n fixedSkills: entry.fixedSkills,\n optionalSkillGroups: entry.optionalSkillGroups ?? []\n }\n }\n }\n }\n };\n}\n\nasync function getOrCreateItemFolder(name: string, parentId: string | null): Promise {\n const existing = game.folders.find(\n (folder: any) => folder.type === \"Item\" && folder.name === name && (folder.folder?.id ?? null) === parentId\n );\n if (existing) return existing;\n\n return Folder.create({\n name,\n type: \"Item\",\n folder: parentId,\n sorting: \"a\"\n });\n}\n\nasync function ensureCatalogFolders(): Promise> {\n const root = await getOrCreateItemFolder(\"Ringworld Catalog\", null);\n const species = await getOrCreateItemFolder(\"Species\", root.id);\n const skills = await getOrCreateItemFolder(\"Skills\", root.id);\n const pursuits = await getOrCreateItemFolder(\"Pursuits\", root.id);\n const gear = await getOrCreateItemFolder(\"Gear\", root.id);\n const armor = await getOrCreateItemFolder(\"Armor\", gear.id);\n const weapons = await getOrCreateItemFolder(\"Weapons\", root.id);\n const weaponMelee = await getOrCreateItemFolder(\"Melee Weapons\", weapons.id);\n const weaponRanged = await getOrCreateItemFolder(\"Ranged Weapons\", weapons.id);\n\n return {\n species: species.id,\n skills: skills.id,\n pursuits: pursuits.id,\n gear: gear.id,\n armor: armor.id,\n weaponMelee: weaponMelee.id,\n weaponRanged: weaponRanged.id\n };\n}\n\nfunction buildAllSeeds(): CatalogSeed[] {\n const canUsePursuitType = itemTypeAvailable(\"pursuit\");\n return [\n ...SPECIES_CATALOG.map(toSpeciesItem),\n ...RINGWORLD_BASE_SKILLS.map(toSkillItem),\n ...(canUsePursuitType ? PURSUIT_CATALOG.map(toPursuitItem) : PURSUIT_CATALOG.map(toPursuitFallbackGearItem)),\n ...WEAPON_CATALOG.map(toWeaponItem),\n ...ARMOR_CATALOG.map(toArmorItem),\n ...GEAR_CATALOG.map(toGearItem)\n ];\n}\n\nexport async function seedWorldCatalog(options?: { notify?: boolean }): Promise<{ added: number; updated: number }> {\n if (!game.user?.isGM) return { added: 0, updated: 0 };\n\n const folderIds = await ensureCatalogFolders();\n const seeds = buildAllSeeds();\n\n const existing = new Map(\n game.items.map((item: any) => [itemKey(item.type, item.name), item])\n );\n\n const createPayload: any[] = [];\n const updatePayload: any[] = [];\n\n for (const seed of seeds) {\n const folderId = folderIds[seed.folderKey];\n const existingItem = existing.get(seed.key);\n\n if (!existingItem) {\n createPayload.push({\n ...seed.data,\n img: seed.img,\n folder: folderId,\n flags: {\n ringworld: {\n catalog: true,\n catalogKey: seed.key\n }\n }\n });\n continue;\n }\n\n const update: Record = { _id: existingItem.id };\n\n if ((existingItem.folder?.id ?? null) !== folderId) {\n update.folder = folderId;\n }\n\n if (String(existingItem.img || \"\") !== seed.img) {\n update.img = seed.img;\n }\n\n if (existingItem.flags?.ringworld?.catalog !== true) {\n update[\"flags.ringworld.catalog\"] = true;\n }\n\n if (existingItem.flags?.ringworld?.catalogKey !== seed.key) {\n update[\"flags.ringworld.catalogKey\"] = seed.key;\n }\n\n const existingSystem = foundry.utils.deepClone(existingItem.system ?? {});\n const mergedSystem = foundry.utils.mergeObject(existingSystem, seed.data.system ?? {}, {\n inplace: false,\n insertKeys: true,\n insertValues: true,\n overwrite: false\n });\n const systemDiff = foundry.utils.diffObject(existingSystem, mergedSystem);\n if (Object.keys(systemDiff).length > 0) {\n update.system = mergedSystem;\n }\n\n if (Object.keys(update).length > 1) {\n updatePayload.push(update);\n }\n }\n\n if (createPayload.length) {\n await Item.createDocuments(createPayload as any[]);\n }\n\n if (updatePayload.length) {\n await Item.updateDocuments(updatePayload as any[]);\n }\n\n if (options?.notify) {\n ui.notifications?.info(`Ringworld catalog synced: +${createPayload.length} new, ${updatePayload.length} updated.`);\n }\n\n return { added: createPayload.length, updated: updatePayload.length };\n}\n\nexport function getPlayableSpeciesFromWorld(): any[] {\n return game.items.filter((item: any) => item.type === \"species\" && item.system.playable);\n}\n\nexport function ringworldSystemSettingKey(key: string): string {\n return `${SYSTEM_ID}.${key}`;\n}\n","import { speciesCatalogForGeneration, getPlayableSpeciesFromWorld } from \"./world-catalog\";\nimport { evaluateRollWithDice } from \"./utils/dice\";\n\ntype GravityType = \"light\" | \"normal\" | \"heavy\";\n\ninterface CharacteristicSet {\n str: number;\n mas: number;\n con: number;\n int: number;\n pow: number;\n dex: number;\n app: number;\n edu: number;\n age: number;\n}\n\nfunction clampScore(value: number): number {\n return Math.max(1, Math.min(99, Math.floor(value)));\n}\n\nasync function rollTotal(formula: string): Promise {\n const roll = await evaluateRollWithDice(formula);\n return Number(roll.total ?? 0);\n}\n\nasync function rollEdu(): Promise {\n let edu = 10;\n while (true) {\n const die = await rollTotal(\"1d6\");\n edu += die;\n if (die <= 4) break;\n }\n return clampScore(edu);\n}\n\nasync function rollByGravity(gravity: GravityType): Promise {\n const gravityFormulas = {\n light: { str: \"2d6+3\", mas: \"2d6+3\", con: \"2d6+6\", dex: \"2d6+9\" },\n normal: { str: \"2d6+6\", mas: \"2d6+6\", con: \"2d6+6\", dex: \"2d6+6\" },\n heavy: { str: \"2d6+9\", mas: \"2d6+9\", con: \"2d6+3\", dex: \"2d6+6\" }\n }[gravity];\n\n return {\n str: await rollTotal(gravityFormulas.str),\n mas: await rollTotal(gravityFormulas.mas),\n con: await rollTotal(gravityFormulas.con),\n int: await rollTotal(\"2d6+6\"),\n pow: await rollTotal(\"2d6+6\"),\n dex: await rollTotal(gravityFormulas.dex),\n app: await rollTotal(\"2d6+6\"),\n edu: await rollEdu(),\n age: await rollTotal(\"1d20+16\")\n };\n}\n\nfunction randomPick(arr: T[]): T {\n return arr[Math.floor(Math.random() * arr.length)];\n}\n\nfunction randomExplorerName(speciesName: string): string {\n const starts = [\"Ar\", \"Bel\", \"Cor\", \"Den\", \"Ela\", \"Far\", \"Gal\", \"Hel\", \"Ira\", \"Jun\", \"Kor\", \"Lys\", \"Mor\", \"Nyr\", \"Or\", \"Pra\", \"Qua\", \"Rin\", \"Sol\", \"Tor\", \"Uri\", \"Val\", \"Wen\", \"Xel\", \"Yor\", \"Zen\"];\n const ends = [\"an\", \"el\", \"is\", \"or\", \"us\", \"a\", \"en\", \"ion\", \"eth\", \"al\", \"ir\", \"os\"];\n return `${randomPick(starts)}${randomPick(ends)} (${speciesName})`;\n}\n\nfunction applySpeciesModifiers(base: CharacteristicSet, speciesSystem: any): CharacteristicSet {\n const mods = speciesSystem?.modifiers ?? {};\n return {\n str: clampScore(base.str + Number(mods.str || 0)),\n mas: clampScore(base.mas + Number(mods.mas || 0)),\n con: clampScore(base.con + Number(mods.con || 0)),\n int: clampScore(base.int + Number(mods.int || 0)),\n pow: clampScore(base.pow + Number(mods.pow || 0)),\n dex: clampScore(base.dex + Number(mods.dex || 0)),\n app: clampScore(base.app + Number(mods.app || 0)),\n edu: clampScore(base.edu + Number(mods.edu || 0)),\n age: clampScore(base.age + Number(mods.age || 0))\n };\n}\n\nexport async function rollCharacteristicsForActor(actor: any): Promise {\n const gravity = String(actor.system?.profile?.gravity || \"normal\") as GravityType;\n const gravityType: GravityType = gravity === \"light\" || gravity === \"heavy\" ? gravity : \"normal\";\n\n const currentSpeciesName = String(actor.system?.profile?.species || \"\");\n const worldSpecies = getPlayableSpeciesFromWorld();\n const speciesItem = worldSpecies.find((item: any) => item.name === currentSpeciesName);\n const builtIn = speciesCatalogForGeneration().find((entry) => entry.name === currentSpeciesName);\n const speciesSystem = speciesItem?.system ?? builtIn ?? null;\n\n const rolled = await rollByGravity(gravityType);\n const finalValues = applySpeciesModifiers(rolled, speciesSystem);\n\n await actor.update({\n \"system.characteristics.str\": finalValues.str,\n \"system.characteristics.mas\": finalValues.mas,\n \"system.characteristics.con\": finalValues.con,\n \"system.characteristics.int\": finalValues.int,\n \"system.characteristics.pow\": finalValues.pow,\n \"system.characteristics.dex\": finalValues.dex,\n \"system.characteristics.app\": finalValues.app,\n \"system.characteristics.edu\": finalValues.edu,\n \"system.characteristics.age\": finalValues.age,\n \"system.profile.chronologicalAge\": finalValues.age,\n \"system.profile.physiologicalAge\": finalValues.age\n });\n\n const ownLanguage = actor.items.find((item: any) => item.type === \"skill\" && item.name.toLowerCase() === \"own language\");\n if (ownLanguage) {\n await actor.updateEmbeddedDocuments(\"Item\", [{\n _id: ownLanguage.id,\n \"system.value\": Math.min(99, finalValues.int * 5)\n }]);\n }\n}\n\nexport async function applyStarterKit(actor: any): Promise {\n const starterNames = [\n [\"weapon\", \"Laser Pistol\"],\n [\"weapon\", \"Variable Sword\"],\n [\"armor\", \"Soft Combat Vest\"],\n [\"gear\", \"Power Pack R20\"],\n [\"gear\", \"Medkit\"],\n [\"gear\", \"Survey Scanner\"]\n ];\n\n const worldItems = game.items;\n const actorExisting = new Set(actor.items.map((item: any) => `${item.type}:${item.name.toLowerCase()}`));\n const createPayload: any[] = [];\n\n for (const [type, name] of starterNames) {\n if (actorExisting.has(`${type}:${String(name).toLowerCase()}`)) continue;\n const worldItem = worldItems.find((item: any) => item.type === type && item.name === name);\n if (!worldItem) continue;\n createPayload.push(worldItem.toObject());\n }\n\n if (createPayload.length) {\n await actor.createEmbeddedDocuments(\"Item\", createPayload);\n }\n}\n\nexport async function randomizeExplorerActor(actor: any): Promise {\n const worldSpecies = getPlayableSpeciesFromWorld();\n const builtInSpecies = speciesCatalogForGeneration();\n\n const pickedWorldSpecies = worldSpecies.length ? randomPick(worldSpecies) : null;\n const pickedBuiltInSpecies = !pickedWorldSpecies ? randomPick(builtInSpecies) : null;\n\n const speciesChoice = pickedWorldSpecies?.system ?? pickedBuiltInSpecies;\n const speciesName = pickedWorldSpecies?.name ?? pickedBuiltInSpecies?.name ?? \"Human\";\n\n const gravity = String(speciesChoice.gravity || \"normal\") as GravityType;\n const homeworld = String(speciesChoice.homeworld || \"Ringworld\");\n\n await actor.update({\n name: randomExplorerName(speciesName),\n \"system.profile.species\": speciesName,\n \"system.profile.gravity\": gravity,\n \"system.profile.homeworld\": homeworld\n });\n\n await rollCharacteristicsForActor(actor);\n await actor.ensureBaseSkills?.();\n await applyStarterKit(actor);\n}\n\nexport async function createRandomExplorer(): Promise {\n const ActorClass = getDocumentClass(\"Actor\");\n const actor = await ActorClass.create({\n name: \"Random Explorer\",\n type: \"explorer\"\n });\n\n await randomizeExplorerActor(actor);\n return actor;\n}\n","import { recomputeHitPointMaximums, rollSkillForActor, rollStandaloneHitLocation, useWeaponWorkflow } from \"../mechanics\";\nimport { normalizeSkillName, RINGWORLD_BASE_SKILLS } from \"../data-base-skills\";\nimport { randomizeExplorerActor, rollCharacteristicsForActor } from \"../character-tools\";\nimport { skillIconPath } from \"../skill-icons\";\n\ntype DerivedCheckKey = \"health\" | \"reasoning\" | \"luck\" | \"dodge\";\ntype SpeciesModifierKey = \"str\" | \"mas\" | \"con\" | \"int\" | \"pow\" | \"dex\" | \"app\" | \"edu\" | \"age\";\nconst SPECIES_MODIFIER_KEYS: SpeciesModifierKey[] = [\"str\", \"mas\", \"con\", \"int\", \"pow\", \"dex\", \"app\", \"edu\", \"age\"];\n\nfunction num(value: unknown, fallback = 0): number {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction hasValue(value: unknown): boolean {\n return value !== null && value !== undefined && String(value).trim() !== \"\";\n}\n\nfunction clampCharacteristic(value: number): number {\n return Math.max(1, Math.min(99, Math.floor(value)));\n}\n\nfunction computeDamageMod(str: number, mas: number): string {\n const total = str + mas;\n if (total <= 12) return \"-1d6\";\n if (total <= 24) return \"-1d4\";\n if (total <= 32) return \"0\";\n if (total <= 40) return \"+1d4\";\n if (total <= 56) return \"+1d6\";\n if (total <= 72) return \"+2d6\";\n return \"+3d6\";\n}\n\nfunction computeDerivedRolls(characteristics: any): Record {\n const con = num(characteristics?.con, 10);\n const int = num(characteristics?.int, 10);\n const pow = num(characteristics?.pow, 10);\n const dex = num(characteristics?.dex, 10);\n\n return {\n health: Math.max(0, Math.min(99, con * 5)),\n reasoning: Math.max(0, Math.min(99, int * 5)),\n luck: Math.max(0, Math.min(99, pow * 5)),\n dodge: Math.max(0, Math.min(99, dex * 5))\n };\n}\n\nfunction computeRootMaxes(characteristics: any): Record {\n const str = num(characteristics?.str, 10);\n const dex = num(characteristics?.dex, 10);\n const app = num(characteristics?.app, 10);\n const edu = num(characteristics?.edu, 10);\n const int = num(characteristics?.int, 10);\n const pow = num(characteristics?.pow, 10);\n const con = num(characteristics?.con, 10);\n\n return {\n agility: Math.max(0, str + dex),\n communication: Math.max(0, int + app),\n knowledge: Math.max(0, int + edu),\n perception: Math.max(0, pow + con),\n technical: Math.max(0, dex + int)\n };\n}\n\nfunction computeActionRanking(dex: number): number {\n if (dex <= 4) return 7;\n if (dex <= 8) return 6;\n if (dex <= 12) return 5;\n if (dex <= 16) return 4;\n if (dex <= 20) return 3;\n if (dex <= 24) return 2;\n return 1;\n}\n\nfunction gravityLoadMultiplier(gravity: string): number {\n if (gravity === \"light\") return 0.5;\n if (gravity === \"heavy\") return 2;\n return 1;\n}\n\nfunction contributesToEncumbrance(item: any): boolean {\n if (item.type !== \"weapon\" && item.type !== \"armor\" && item.type !== \"gear\") return false;\n return item.system?.stowed !== true;\n}\n\nfunction itemWeightKg(item: any): number {\n return Math.max(0, num(item.system?.weight, 0));\n}\n\nfunction computeEncumbrance(system: any, items: any[]): {\n rawLoadKg: number;\n effectiveLoadKg: number;\n limitKg: number;\n overLimitKg: number;\n actionRankingPenalty: number;\n overLimit: boolean;\n currentMove: number;\n currentRun: number;\n gravityMultiplier: number;\n} {\n const gravity = String(system?.profile?.gravity || \"normal\");\n const gravityMultiplier = gravityLoadMultiplier(gravity);\n const equippedLoadKg = items\n .filter((item) => item.type === \"weapon\" || item.type === \"armor\" || item.type === \"gear\")\n .filter(contributesToEncumbrance)\n .reduce((sum, item) => sum + itemWeightKg(item), 0);\n\n const rawLoadKg = Math.max(0, equippedLoadKg);\n const effectiveLoadKg = rawLoadKg * gravityMultiplier;\n const limitKg = Math.max(0, num(system?.characteristics?.str, 0));\n const overLimitKg = Math.max(0, effectiveLoadKg - limitKg);\n const actionRankingPenalty = overLimitKg > 0 ? Math.ceil(overLimitKg / 10) : 0;\n const overLimit = overLimitKg > 0;\n\n const baseMove = Math.max(1, num(system?.derived?.move, 3));\n const baseRun = Math.max(1, num(system?.derived?.run, baseMove * 2));\n const currentMove = overLimit ? 2 : baseMove;\n const currentRun = overLimit ? 4 : baseRun;\n\n return {\n rawLoadKg,\n effectiveLoadKg,\n limitKg,\n overLimitKg,\n actionRankingPenalty,\n overLimit,\n currentMove,\n currentRun,\n gravityMultiplier\n };\n}\n\nexport class RingworldActor extends Actor {\n prepareDerivedData(): void {\n super.prepareDerivedData();\n recomputeHitPointMaximums(this as any);\n\n if (this.type !== \"explorer\" && this.type !== \"creature\") return;\n\n const system = (this as any).system ?? {};\n const characteristics = system.characteristics ?? {};\n const profile = system.profile ?? {};\n\n profile.chronologicalAge = num(profile.chronologicalAge, num(characteristics.age, 25));\n profile.physiologicalAge = num(profile.physiologicalAge, profile.chronologicalAge);\n system.profile = profile;\n\n system.combat = system.combat ?? {};\n if (!hasValue(system.combat.damageMod)) {\n system.combat.damageMod = computeDamageMod(num(characteristics.str, 10), num(characteristics.mas, 10));\n }\n\n system.derivedRolls = system.derivedRolls ?? {};\n const derivedRollDefaults = computeDerivedRolls(characteristics);\n for (const key of Object.keys(derivedRollDefaults) as DerivedCheckKey[]) {\n system.derivedRolls[key] = num(system.derivedRolls[key], derivedRollDefaults[key]);\n }\n\n system.skillRootMax = system.skillRootMax ?? {};\n const rootMaxDefaults = computeRootMaxes(characteristics);\n for (const [key, value] of Object.entries(rootMaxDefaults)) {\n system.skillRootMax[key] = value;\n }\n\n system.derived = system.derived ?? {};\n system.derived.actionRanking = computeActionRanking(num(characteristics.dex, 10));\n system.derived.move = num(system.derived.move, 3);\n system.derived.run = num(system.derived.run, Math.max(1, num(system.derived.move, 3) * 2));\n\n system.encumbrance = system.encumbrance ?? {};\n const encumbrance = computeEncumbrance(system, this.items as any[]);\n system.encumbrance.rawLoadKg = encumbrance.rawLoadKg;\n system.encumbrance.effectiveLoadKg = encumbrance.effectiveLoadKg;\n system.encumbrance.limitKg = encumbrance.limitKg;\n system.encumbrance.overLimitKg = encumbrance.overLimitKg;\n system.encumbrance.actionRankingPenalty = encumbrance.actionRankingPenalty;\n system.encumbrance.overLimit = encumbrance.overLimit;\n system.encumbrance.gravityMultiplier = encumbrance.gravityMultiplier;\n system.derived.currentMove = encumbrance.currentMove;\n system.derived.currentRun = encumbrance.currentRun;\n\n system.finance = system.finance ?? { money: 0, inBank: 0 };\n system.finance.money = num(system.finance.money, 0);\n system.finance.inBank = num(system.finance.inBank, 0);\n\n system.notes = system.notes ?? { defects: \"\", habits: \"\" };\n if (typeof system.sheetToolsUnlocked !== \"boolean\") {\n system.sheetToolsUnlocked = false;\n }\n system.characterBuilder = system.characterBuilder ?? {\n pursuits: [],\n allocations: {},\n baseBySkillId: {}\n };\n system.unconsciousLevel = num(system.unconsciousLevel, 0);\n }\n\n get skills(): any[] {\n return this.items.filter((item) => item.type === \"skill\");\n }\n\n get weapons(): any[] {\n return this.items.filter((item) => item.type === \"weapon\");\n }\n\n get armors(): any[] {\n return this.items.filter((item) => item.type === \"armor\");\n }\n\n get gears(): any[] {\n return this.items.filter((item) => item.type === \"gear\");\n }\n\n async rollSkillById(skillId: string): Promise {\n const skill = this.items.get(skillId);\n if (!skill || skill.type !== \"skill\") return;\n await rollSkillForActor(this, skill.name, Number((skill as any).system.value ?? 0));\n }\n\n async rollHitLocation(): Promise {\n await rollStandaloneHitLocation(this);\n }\n\n async useWeaponById(weaponId: string): Promise {\n const weapon = this.items.get(weaponId);\n if (!weapon || weapon.type !== \"weapon\") return;\n await useWeaponWorkflow(this, weapon);\n }\n\n async ensureBaseSkills(): Promise {\n if (this.type !== \"explorer\" && this.type !== \"creature\") return 0;\n\n const existing = new Set(\n this.items\n .filter((item) => item.type === \"skill\")\n .map((item) => normalizeSkillName(item.name))\n );\n\n const toCreate = RINGWORLD_BASE_SKILLS\n .filter((skill) => !existing.has(normalizeSkillName(skill.name)))\n .map((skill) => ({\n name: skill.name,\n type: \"skill\",\n img: skillIconPath(skill.name),\n system: {\n value: skill.base,\n category: skill.category,\n specialization: skill.specialization ?? \"\"\n }\n }));\n\n if (!toCreate.length) return 0;\n await this.createEmbeddedDocuments(\"Item\", toCreate as any[]);\n return toCreate.length;\n }\n\n async rollCharacteristics(): Promise {\n if (this.type !== \"explorer\") return;\n await rollCharacteristicsForActor(this as any);\n }\n\n async randomizeCharacter(): Promise {\n if (this.type !== \"explorer\") return;\n await randomizeExplorerActor(this as any);\n }\n\n async applySpeciesItem(speciesData: any): Promise {\n if (this.type !== \"explorer\" && this.type !== \"creature\") return;\n\n const speciesName = String(speciesData?.name || \"\").trim();\n const speciesSystem = speciesData?.system ?? {};\n const incomingMods = speciesSystem?.modifiers ?? {};\n const appliedState = (this as any).system?.speciesApplied ?? {};\n const previousMods = appliedState?.modifiers ?? {};\n\n const currentCharacteristics = (this as any).system?.characteristics ?? {};\n const currentProfile = (this as any).system?.profile ?? {};\n const update: Record = {};\n const normalizedMods: Record = {\n str: 0,\n mas: 0,\n con: 0,\n int: 0,\n pow: 0,\n dex: 0,\n app: 0,\n edu: 0,\n age: 0\n };\n\n for (const key of SPECIES_MODIFIER_KEYS) {\n const prev = num(previousMods?.[key], 0);\n const next = num(incomingMods?.[key], 0);\n normalizedMods[key] = next;\n\n const current = num(currentCharacteristics?.[key], key === \"age\" ? 25 : 10);\n const base = current - prev;\n update[`system.characteristics.${key}`] = clampCharacteristic(base + next);\n }\n\n const agePrev = num(previousMods?.age, 0);\n const ageNext = normalizedMods.age;\n const chronoCurrent = num(currentProfile?.chronologicalAge, num(currentCharacteristics?.age, 25));\n const physCurrent = num(currentProfile?.physiologicalAge, chronoCurrent);\n update[\"system.profile.chronologicalAge\"] = clampCharacteristic((chronoCurrent - agePrev) + ageNext);\n update[\"system.profile.physiologicalAge\"] = clampCharacteristic((physCurrent - agePrev) + ageNext);\n\n if (speciesName) update[\"system.profile.species\"] = speciesName;\n if (speciesSystem?.gravity) update[\"system.profile.gravity\"] = String(speciesSystem.gravity);\n if (speciesSystem?.homeworld !== undefined) update[\"system.profile.homeworld\"] = String(speciesSystem.homeworld || \"\");\n\n update[\"system.speciesApplied\"] = {\n name: speciesName,\n modifiers: normalizedMods\n };\n\n await this.update(update);\n }\n\n async rollDerivedCheck(key: DerivedCheckKey): Promise {\n if (this.type !== \"explorer\" && this.type !== \"creature\") return;\n\n const labels: Record = {\n health: \"Health Roll\",\n reasoning: \"Reasoning Roll\",\n luck: \"Luck Roll\",\n dodge: \"Dodge Roll\"\n };\n\n const defaults = computeDerivedRolls((this as any).system?.characteristics ?? {});\n const target = num((this as any).system?.derivedRolls?.[key], defaults[key]);\n await rollSkillForActor(this, labels[key], target);\n }\n}\n","import { rollSkillForActor, useWeaponWorkflow } from \"../mechanics\";\n\nexport class RingworldItem extends Item {\n async rollSkill(): Promise {\n if (this.type !== \"skill\") return;\n\n const actor = this.actor;\n if (!actor) {\n ui.notifications?.warn(\"Skill must be owned by an Actor to roll.\");\n return;\n }\n\n await rollSkillForActor(actor, this.name, Number((this as any).system.value ?? 0));\n }\n\n async useWeapon(): Promise {\n if (this.type !== \"weapon\") return;\n\n const actor = this.actor;\n if (!actor) {\n ui.notifications?.warn(\"Weapon must be owned by an Actor to use.\");\n return;\n }\n\n await useWeaponWorkflow(actor, this);\n }\n}\n","import { type HitLocationSlug } from \"../../constants\";\nimport {\n BASE_SKILL_CATEGORIES,\n baseSkillNameSet,\n normalizeSkillName,\n RINGWORLD_BASE_SKILLS\n} from \"../../data-base-skills\";\nimport { baseSkillByName, DEFAULT_SKILL_ICON, resolveSkillIcon, skillIconPath } from \"../../skill-icons\";\nimport { getActorHitLocationSchema, listHitLocationSchemas } from \"../../hit-location-schemas\";\nimport { PURSUIT_CATALOG } from \"../../world-catalog\";\nimport { outcomeLabel, rollPercentile } from \"../../utils/percentile\";\nimport { postSkillRollCard } from \"../../chat\";\n\nconst BaseActorSheet = (foundry as any).appv1.sheets.ActorSheet;\n\nconst ROOT_KEY_BY_CATEGORY: Record = {\n Agility: \"agility\",\n Communication: \"communication\",\n Knowledge: \"knowledge\",\n Perception: \"perception\",\n Technical: \"technical\"\n};\n\nconst ROOT_FORMULA_BY_CATEGORY: Record = {\n Agility: \"STR + DEX\",\n Communication: \"INT + APP\",\n Knowledge: \"INT + EDU\",\n Perception: \"POW + CON\",\n Technical: \"DEX + INT\"\n};\n\nconst EXCLUDED_EDUCATION_SKILLS = new Set([\n normalizeSkillName(\"Archaic Melee Weapon\"),\n normalizeSkillName(\"Archaic Ranged Weapons\")\n]);\n\nconst HYPERDRIVE_KEY = normalizeSkillName(\"Hyperdrive\");\n\ninterface BuilderAllocation {\n education: number;\n pursuits: number;\n special: number;\n}\n\ninterface BuilderPursuitSelection {\n name: string;\n years: number;\n}\n\ninterface BuilderState {\n pursuits: BuilderPursuitSelection[];\n allocations: Record;\n baseBySkillId: Record;\n}\n\ninterface BuilderPool {\n total: number;\n spent: number;\n remaining: number;\n}\n\ninterface BuilderSkillMeta {\n allocation: BuilderAllocation;\n totalAllocated: number;\n baseValue: number;\n rootCap: number;\n maxAllocation: number;\n allowsEducation: boolean;\n allowsPursuits: boolean;\n allowsSpecial: boolean;\n}\n\ninterface BuilderContext {\n state: BuilderState;\n education: BuilderPool;\n pursuits: BuilderPool;\n special: BuilderPool;\n pursuitYears: BuilderPool;\n pursuitOptions: string[];\n pursuitAllowedSkills: Set;\n bySkillId: Map;\n}\n\nfunction byName(a: any, b: any): number {\n return a.name.localeCompare(b.name);\n}\n\nfunction num(value: unknown, fallback = 0): number {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction toInt(value: unknown, fallback = 0): number {\n return Math.max(0, Math.floor(num(value, fallback)));\n}\n\nfunction normalizeAllocation(raw: any): BuilderAllocation {\n return {\n education: toInt(raw?.education, 0),\n pursuits: toInt(raw?.pursuits, 0),\n special: toInt(raw?.special, 0)\n };\n}\n\nfunction normalizePursuitSelection(raw: any): BuilderPursuitSelection | null {\n const name = String(raw?.name || \"\").trim();\n const years = toInt(raw?.years, 0);\n if (!name || years <= 0) return null;\n return { name, years };\n}\n\nfunction normalizeBuilderState(raw: any): BuilderState {\n const pursuits = Array.isArray(raw?.pursuits)\n ? raw.pursuits.map((entry: any) => normalizePursuitSelection(entry)).filter((entry: any) => Boolean(entry))\n : [];\n\n const allocations: Record = {};\n if (raw?.allocations && typeof raw.allocations === \"object\") {\n for (const [skillId, allocation] of Object.entries(raw.allocations)) {\n if (!skillId) continue;\n const normalized = normalizeAllocation(allocation);\n if (normalized.education + normalized.pursuits + normalized.special <= 0) continue;\n allocations[skillId] = normalized;\n }\n }\n\n const baseBySkillId: Record = {};\n if (raw?.baseBySkillId && typeof raw.baseBySkillId === \"object\") {\n for (const [skillId, value] of Object.entries(raw.baseBySkillId)) {\n if (!skillId) continue;\n baseBySkillId[skillId] = Math.max(0, toInt(value, 0));\n }\n }\n\n return {\n pursuits,\n allocations,\n baseBySkillId\n };\n}\n\nfunction isEducationEligible(skillName: string): boolean {\n return !EXCLUDED_EDUCATION_SKILLS.has(normalizeSkillName(skillName));\n}\n\nfunction isSpecialEligible(skillName: string): boolean {\n return normalizeSkillName(skillName) !== HYPERDRIVE_KEY;\n}\n\nfunction buildPursuitAllowedSkillSet(pursuits: BuilderPursuitSelection[]): Set {\n const entriesByName = new Map(\n PURSUIT_CATALOG.map((entry) => [normalizeSkillName(entry.name), entry])\n );\n const allowed = new Set();\n\n for (const pursuit of pursuits) {\n const entry = entriesByName.get(normalizeSkillName(pursuit.name));\n if (!entry) continue;\n\n for (const skillName of entry.fixedSkills || []) {\n allowed.add(normalizeSkillName(skillName));\n }\n\n for (const group of entry.optionalSkillGroups || []) {\n for (const skillName of group.skills || []) {\n allowed.add(normalizeSkillName(skillName));\n }\n }\n }\n\n return allowed;\n}\n\nfunction computeBuilderPools(characteristics: any): {\n educationTotal: number;\n pursuitsTotal: number;\n specialTotal: number;\n pursuitYearsTotal: number;\n} {\n const age = toInt(characteristics?.age, 0);\n const edu = toInt(characteristics?.edu, 0);\n const int = toInt(characteristics?.int, 0);\n\n const educationTotal = Math.max(0, (edu - 10) * 20);\n const pursuitYearsTotal = Math.max(0, age - edu - 6);\n const pursuitsTotal = pursuitYearsTotal * 20;\n const specialTotal = Math.max(0, int * 10);\n\n return {\n educationTotal,\n pursuitsTotal,\n specialTotal,\n pursuitYearsTotal\n };\n}\n\nfunction buildBuilderContext(actor: any, skills: any[]): BuilderContext {\n const pools = computeBuilderPools(actor.system?.characteristics ?? {});\n const state = normalizeBuilderState(actor.system?.characterBuilder ?? {});\n\n const pursuitOptions = PURSUIT_CATALOG.map((entry) => entry.name).sort((a, b) => a.localeCompare(b));\n const pursuitAllowedSkills = buildPursuitAllowedSkillSet(state.pursuits);\n\n const bySkillId = new Map();\n\n let educationSpent = 0;\n let pursuitsSpent = 0;\n let specialSpent = 0;\n\n for (const skill of skills) {\n const allocation = normalizeAllocation(state.allocations[skill.id]);\n const totalAllocated = allocation.education + allocation.pursuits + allocation.special;\n\n const baseFromState = state.baseBySkillId[skill.id];\n const baseFromDefinition = baseSkillByName(skill.name)?.base;\n const inferredBase = Math.max(0, num(skill.system?.value, 0) - totalAllocated);\n const baseValue = Math.max(\n 0,\n Number.isFinite(baseFromState)\n ? baseFromState\n : (baseFromDefinition ?? inferredBase)\n );\n\n const rootKey = ROOT_KEY_BY_CATEGORY[String(skill.system?.category || \"\")] ?? \"\";\n const rootCap = rootKey ? Math.max(0, toInt(actor.system?.skillRootMax?.[rootKey], 0)) : 99;\n const maxAllocation = Math.max(0, rootCap - baseValue);\n\n const normalizedSkillName = normalizeSkillName(skill.name);\n const allowsEducation = isEducationEligible(skill.name);\n const allowsPursuits = pursuitAllowedSkills.has(normalizedSkillName);\n const allowsSpecial = isSpecialEligible(skill.name);\n\n bySkillId.set(skill.id, {\n allocation,\n totalAllocated,\n baseValue,\n rootCap,\n maxAllocation,\n allowsEducation,\n allowsPursuits,\n allowsSpecial\n });\n\n educationSpent += allocation.education;\n pursuitsSpent += allocation.pursuits;\n specialSpent += allocation.special;\n }\n\n const pursuitYearsAllocated = state.pursuits.reduce((sum, row) => sum + row.years, 0);\n\n return {\n state,\n education: {\n total: pools.educationTotal,\n spent: educationSpent,\n remaining: pools.educationTotal - educationSpent\n },\n pursuits: {\n total: pools.pursuitsTotal,\n spent: pursuitsSpent,\n remaining: pools.pursuitsTotal - pursuitsSpent\n },\n special: {\n total: pools.specialTotal,\n spent: specialSpent,\n remaining: pools.specialTotal - specialSpent\n },\n pursuitYears: {\n total: pools.pursuitYearsTotal,\n spent: pursuitYearsAllocated,\n remaining: pools.pursuitYearsTotal - pursuitYearsAllocated\n },\n pursuitOptions,\n pursuitAllowedSkills,\n bySkillId\n };\n}\n\nfunction getItemArmorAtLocation(item: any, slug: string): number {\n if (item.type !== \"armor\" || !item.system?.equipped || item.system?.stowed) return 0;\n if (!item.system?.coverage?.[slug]) return 0;\n\n const specific = num(item.system?.perLocation?.[slug], 0);\n if (specific > 0) return specific;\n return num(item.system?.value, 0);\n}\n\nfunction getTotalArmorAtLocation(actor: any, slug: string): number {\n const base = num(actor.system?.hitLocations?.[slug]?.armor, 0);\n const fromItems = actor.items\n .filter((item: any) => item.type === \"armor\")\n .reduce((sum: number, item: any) => sum + getItemArmorAtLocation(item, slug), 0);\n return base + fromItems;\n}\n\nfunction getPreparedHitLocation(actor: any, slug: string, rollRange: string): any {\n const locationConfig = getActorHitLocationSchema(actor).entries.find((entry) => entry.slug === slug);\n const source = actor.system?.hitLocations?.[slug] ?? {};\n const hp = source.hp ?? {};\n const rawLabel = String(source.label ?? \"\").trim();\n return {\n slug,\n rollRange,\n label: rawLabel || String(locationConfig?.label || slug),\n armor: num(source.armor, 0),\n totalArmor: getTotalArmorAtLocation(actor, slug),\n hp: {\n value: num(hp.value, 0),\n max: num(hp.max, 0)\n }\n };\n}\n\nfunction chooseRandom(items: T[]): T {\n return items[Math.floor(Math.random() * items.length)];\n}\n\nfunction clampPercent(value: number): number {\n return Math.max(0, Math.min(99, Math.floor(value)));\n}\n\nexport class RingworldExplorerSheet extends BaseActorSheet {\n private isToolsUnlocked(): boolean {\n return Boolean((this.actor as any)?.system?.sheetToolsUnlocked);\n }\n\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"actor\", \"explorer\"],\n width: 930,\n height: 1460,\n tabs: [{ navSelector: \".tabs\", contentSelector: \".sheet-body\", initial: \"overview\" }]\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/actor/explorer-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n const actor = this.actor as any;\n\n if (game.user?.isGM && actor.isOwner) {\n await actor.ensureBaseSkills?.();\n }\n\n const skills = actor.items.filter((item: any) => item.type === \"skill\").sort(byName);\n const builderContext = buildBuilderContext(actor, skills);\n\n const weapons = actor.items.filter((item: any) => item.type === \"weapon\").sort(byName);\n const armors = actor.items.filter((item: any) => item.type === \"armor\").sort(byName);\n const gear = actor.items.filter((item: any) => item.type === \"gear\").sort(byName);\n const speciesOptions = game.items\n .filter((item: any) => item.type === \"species\" && item.system.playable)\n .map((item: any) => item.name)\n .sort();\n const baseSet = baseSkillNameSet();\n\n const skillByNormalized = new Map();\n for (const skill of skills) {\n const key = normalizeSkillName(skill.name);\n if (!skillByNormalized.has(key)) {\n skillByNormalized.set(key, skill);\n }\n }\n\n const skillRootMax = actor.system.skillRootMax ?? {};\n const skillsByCategory = BASE_SKILL_CATEGORIES.map((category) => {\n const rootKey = ROOT_KEY_BY_CATEGORY[category] ?? category.toLowerCase();\n const rows = RINGWORLD_BASE_SKILLS\n .filter((skill) => skill.category === category)\n .map((skill) => {\n const existing = skillByNormalized.get(normalizeSkillName(skill.name));\n const meta = existing ? builderContext.bySkillId.get(existing.id) : null;\n return {\n name: skill.name,\n base: skill.base,\n category: skill.category,\n specialization: skill.specialization ?? \"\",\n itemId: existing?.id ?? null,\n icon: existing?.img || skillIconPath(skill.name),\n value: existing?.system?.value ?? skill.base,\n missing: !existing,\n allocationTotal: meta?.totalAllocated ?? 0,\n allocation: meta?.allocation ?? { education: 0, pursuits: 0, special: 0 }\n };\n });\n\n return {\n category,\n rootKey,\n rootFormula: ROOT_FORMULA_BY_CATEGORY[category] ?? \"\",\n rootValue: num(skillRootMax[rootKey], 0),\n rows\n };\n });\n\n const customSkills = skills\n .filter((item: any) => !baseSet.has(normalizeSkillName(item.name)))\n .map((item: any) => {\n const meta = builderContext.bySkillId.get(item.id);\n return {\n id: item.id,\n name: item.name,\n system: item.system,\n img: resolveSkillIcon(item.name, item.img || DEFAULT_SKILL_ICON),\n allocationTotal: meta?.totalAllocated ?? 0,\n allocation: meta?.allocation ?? { education: 0, pursuits: 0, special: 0 }\n };\n });\n\n const missingBaseSkillsCount = skillsByCategory.reduce(\n (total, group) => total + group.rows.filter((row) => row.missing).length,\n 0\n );\n\n const schema = getActorHitLocationSchema(actor);\n const rangesFromSchema = new Map(schema.entries.map((entry) => [entry.slug, `${entry.min}-${entry.max}`]));\n const order = schema.entries.map((entry) => entry.slug as HitLocationSlug);\n const hitLocations = order.map((slug) =>\n getPreparedHitLocation(actor, slug, rangesFromSchema.get(slug) ?? \"\")\n );\n\n const bodyLayoutOrder = schema.bodyOrder;\n const bodyLocations = bodyLayoutOrder.map((slug) =>\n getPreparedHitLocation(actor, slug, rangesFromSchema.get(slug) ?? \"\")\n );\n\n const enc = actor.system?.encumbrance ?? {};\n const encRaw = num(enc.rawLoadKg, 0);\n const encEffective = num(enc.effectiveLoadKg, encRaw);\n const encLimit = num(enc.limitKg, num(actor.system?.characteristics?.str, 0));\n const encPenalty = num(enc.actionRankingPenalty, 0);\n const baseActionRanking = num(actor.system?.derived?.actionRanking, 0);\n const effectiveActionRanking = Math.max(1, baseActionRanking + encPenalty);\n const encumbranceSummary = `${encRaw.toFixed(1)}kg on-person | ${encEffective.toFixed(1)}kg effective / ${encLimit.toFixed(1)}kg limit | AR +${encPenalty} (stowed ignored)`;\n const movementSummary = `${num(actor.system?.derived?.currentMove, num(actor.system?.derived?.move, 3))}m / ${num(actor.system?.derived?.currentRun, num(actor.system?.derived?.run, 6))}m`;\n\n return {\n ...data,\n system: actor.system,\n skills,\n skillsByCategory,\n customSkills,\n missingBaseSkillsCount,\n weapons,\n armors,\n gear,\n speciesOptions,\n hitLocations,\n bodyLocations,\n hitLocationSchemaOptions: listHitLocationSchemas(),\n currentHitLocationSchema: String(actor.system.hitLocationSchema || \"auto\"),\n activeHitLocationSchemaId: schema.id,\n activeHitLocationSchemaLabel: schema.label,\n sheetToolsUnlocked: Boolean(actor.system?.sheetToolsUnlocked),\n baseActionRanking,\n effectiveActionRanking,\n encumbranceSummary,\n movementSummary,\n builder: {\n education: builderContext.education,\n pursuits: builderContext.pursuits,\n special: builderContext.special,\n pursuitYears: builderContext.pursuitYears,\n pursuitsList: builderContext.state.pursuits,\n pursuitOptions: builderContext.pursuitOptions\n }\n };\n }\n\n private async updateBuilderState(state: BuilderState): Promise {\n await this.actor.update({\n \"system.characterBuilder\": {\n pursuits: state.pursuits,\n allocations: state.allocations,\n baseBySkillId: state.baseBySkillId\n }\n });\n }\n\n private async syncSkillsFromBuilderState(state: BuilderState): Promise {\n const skills = this.actor.items.filter((item: any) => item.type === \"skill\");\n const updates: any[] = [];\n\n for (const skill of skills) {\n const allocation = normalizeAllocation(state.allocations[skill.id]);\n const total = allocation.education + allocation.pursuits + allocation.special;\n\n const rootKey = ROOT_KEY_BY_CATEGORY[String(skill.system?.category || \"\")] ?? \"\";\n const rootCap = rootKey ? Math.max(0, toInt(this.actor.system?.skillRootMax?.[rootKey], 0)) : 99;\n\n let base = state.baseBySkillId[skill.id];\n if (!Number.isFinite(base)) {\n const fromBase = baseSkillByName(skill.name)?.base;\n base = Math.max(0, fromBase ?? (num(skill.system?.value, 0) - total));\n state.baseBySkillId[skill.id] = base;\n }\n\n const value = Math.max(0, Math.min(99, Math.min(rootCap, base + total)));\n if (num(skill.system?.value, 0) !== value) {\n updates.push({ _id: skill.id, \"system.value\": value });\n }\n }\n\n if (updates.length) {\n await this.actor.updateEmbeddedDocuments(\"Item\", updates);\n }\n }\n\n private async addOrUpdatePursuit(name: string, years: number): Promise {\n if (!this.isToolsUnlocked()) return;\n const trimmed = String(name || \"\").trim();\n const cleanYears = Math.max(0, Math.floor(Number(years) || 0));\n if (!trimmed || cleanYears <= 0) {\n ui.notifications?.warn(\"Choose a pursuit and set years above 0.\");\n return;\n }\n\n const skills = this.actor.items.filter((item: any) => item.type === \"skill\");\n const context = buildBuilderContext(this.actor, skills);\n const nextState = foundry.utils.deepClone(context.state) as BuilderState;\n\n const existingIndex = nextState.pursuits.findIndex((entry) => normalizeSkillName(entry.name) === normalizeSkillName(trimmed));\n if (existingIndex >= 0) {\n nextState.pursuits[existingIndex].years = cleanYears;\n } else {\n nextState.pursuits.push({ name: trimmed, years: cleanYears });\n }\n\n await this.updateBuilderState(nextState);\n }\n\n private async removePursuit(index: number): Promise {\n if (!this.isToolsUnlocked()) return;\n const skills = this.actor.items.filter((item: any) => item.type === \"skill\");\n const context = buildBuilderContext(this.actor, skills);\n const nextState = foundry.utils.deepClone(context.state) as BuilderState;\n if (index < 0 || index >= nextState.pursuits.length) return;\n\n nextState.pursuits.splice(index, 1);\n await this.updateBuilderState(nextState);\n }\n\n private async resetBuilderAllocations(): Promise {\n if (!this.isToolsUnlocked()) return;\n const skills = this.actor.items.filter((item: any) => item.type === \"skill\");\n const context = buildBuilderContext(this.actor, skills);\n const nextState = foundry.utils.deepClone(context.state) as BuilderState;\n\n nextState.allocations = {};\n await this.updateBuilderState(nextState);\n await this.syncSkillsFromBuilderState(nextState);\n }\n\n private async randomizeBuilderAllocations(): Promise {\n if (!this.isToolsUnlocked()) return;\n await (this.actor as any).ensureBaseSkills?.();\n\n const skills = this.actor.items.filter((item: any) => item.type === \"skill\").sort(byName);\n const context = buildBuilderContext(this.actor, skills);\n const nextState = foundry.utils.deepClone(context.state) as BuilderState;\n\n const totalYears = Math.max(0, context.pursuitYears.total);\n const pursuits = PURSUIT_CATALOG.map((entry) => entry.name);\n nextState.pursuits = [];\n\n if (totalYears > 0 && pursuits.length > 0) {\n const desiredCount = Math.min(\n pursuits.length,\n Math.max(1, Math.min(3, Math.floor(totalYears / 2) || 1))\n );\n\n const available = [...pursuits];\n for (let i = 0; i < desiredCount && available.length; i += 1) {\n const idx = Math.floor(Math.random() * available.length);\n nextState.pursuits.push({ name: available[idx], years: 1 });\n available.splice(idx, 1);\n }\n\n let remainingYears = totalYears - nextState.pursuits.length;\n while (remainingYears > 0 && nextState.pursuits.length) {\n const row = chooseRandom(nextState.pursuits);\n row.years += 1;\n remainingYears -= 1;\n }\n }\n\n const seededState = normalizeBuilderState(nextState);\n seededState.allocations = {};\n\n const freshContext = buildBuilderContext(\n { ...this.actor, system: { ...this.actor.system, characterBuilder: seededState } },\n skills\n );\n\n for (const skill of skills) {\n const meta = freshContext.bySkillId.get(skill.id);\n if (!meta) continue;\n seededState.baseBySkillId[skill.id] = meta.baseValue;\n seededState.allocations[skill.id] = { education: 0, pursuits: 0, special: 0 };\n }\n\n const totalBySkill = new Map();\n for (const skill of skills) {\n totalBySkill.set(skill.id, 0);\n }\n\n const maxForSkill = (skillId: string): number => {\n const meta = freshContext.bySkillId.get(skillId);\n return meta ? Math.max(0, meta.maxAllocation) : 0;\n };\n\n const allocatePool = (\n points: number,\n poolKey: keyof BuilderAllocation,\n eligible: (skill: any, meta: BuilderSkillMeta) => boolean\n ): number => {\n let remaining = Math.max(0, points);\n let guard = 0;\n\n while (remaining > 0 && guard < 300000) {\n guard += 1;\n const candidates = skills.filter((skill) => {\n const meta = freshContext.bySkillId.get(skill.id);\n if (!meta || !eligible(skill, meta)) return false;\n const allocated = totalBySkill.get(skill.id) ?? 0;\n return allocated < maxForSkill(skill.id);\n });\n\n if (!candidates.length) break;\n const picked = chooseRandom(candidates);\n const current = seededState.allocations[picked.id] ?? { education: 0, pursuits: 0, special: 0 };\n current[poolKey] += 1;\n seededState.allocations[picked.id] = current;\n totalBySkill.set(picked.id, (totalBySkill.get(picked.id) ?? 0) + 1);\n remaining -= 1;\n }\n\n return remaining;\n };\n\n const educationUnspent = allocatePool(\n Math.max(0, freshContext.education.total),\n \"education\",\n (skill, meta) => meta.allowsEducation\n );\n\n const pursuitsUnspent = allocatePool(\n Math.max(0, freshContext.pursuits.total),\n \"pursuits\",\n (skill, meta) => meta.allowsPursuits\n );\n\n const specialUnspent = allocatePool(\n Math.max(0, freshContext.special.total),\n \"special\",\n (skill, meta) => meta.allowsSpecial\n );\n\n for (const skill of skills) {\n const allocation = normalizeAllocation(seededState.allocations[skill.id]);\n if (allocation.education + allocation.pursuits + allocation.special <= 0) {\n delete seededState.allocations[skill.id];\n }\n }\n\n await this.updateBuilderState(seededState);\n await this.syncSkillsFromBuilderState(seededState);\n\n if (educationUnspent > 0 || pursuitsUnspent > 0 || specialUnspent > 0) {\n ui.notifications?.info(\n `Random build complete. Unspent points: EDU ${educationUnspent}, Pursuits ${pursuitsUnspent}, Special ${specialUnspent}.`\n );\n } else {\n ui.notifications?.info(\"Random build complete. All available points allocated.\");\n }\n }\n\n private async promptSkillAllocation(skillId: string): Promise {\n if (!this.isToolsUnlocked()) return;\n const skill = this.actor.items.get(skillId);\n if (!skill || skill.type !== \"skill\") return;\n\n const skills = this.actor.items.filter((item: any) => item.type === \"skill\");\n const context = buildBuilderContext(this.actor, skills);\n const meta = context.bySkillId.get(skillId);\n if (!meta) return;\n\n const current = meta.allocation;\n const maxEducation = Math.max(0, context.education.remaining + current.education);\n const maxPursuits = Math.max(0, context.pursuits.remaining + current.pursuits);\n const maxSpecial = Math.max(0, context.special.remaining + current.special);\n\n const maxByCap = Math.max(0, meta.maxAllocation);\n\n const content = `\n
\n
\n \"\"\n
\n

${skill.name}

\n

Base ${meta.baseValue}% | Root cap ${meta.rootCap}% | Current ${num(skill.system?.value, 0)}%

\n
\n
\n\n \n\n \n\n \n\n

Max additional from root cap: ${maxByCap}. Pools: EDU ${context.education.remaining}, Pursuits ${context.pursuits.remaining}, Special ${context.special.remaining}.

\n
\n `;\n\n const result = await new Promise((resolve) => {\n new Dialog({\n title: `${skill.name} - Allocate Character Points`,\n content,\n buttons: {\n save: {\n label: \"Apply\",\n callback: (html: JQuery) => {\n const form = html.find(\"form\")[0] as HTMLFormElement | undefined;\n if (!form) {\n resolve(null);\n return;\n }\n const data = new FormData(form);\n resolve({\n education: meta.allowsEducation ? toInt(data.get(\"education\"), current.education) : current.education,\n pursuits: meta.allowsPursuits ? toInt(data.get(\"pursuits\"), current.pursuits) : current.pursuits,\n special: meta.allowsSpecial ? toInt(data.get(\"special\"), current.special) : current.special\n });\n }\n },\n cancel: {\n label: \"Cancel\",\n callback: () => resolve(null)\n }\n },\n default: \"save\",\n close: () => resolve(null)\n }).render(true);\n });\n\n if (!result) return;\n\n const requestedTotal = result.education + result.pursuits + result.special;\n if (requestedTotal > maxByCap) {\n ui.notifications?.warn(`Allocation exceeds this skill's root-cap allowance (${maxByCap}).`);\n return;\n }\n\n if (result.education > maxEducation || result.pursuits > maxPursuits || result.special > maxSpecial) {\n ui.notifications?.warn(\"Allocation exceeds remaining pool points.\");\n return;\n }\n\n const nextState = foundry.utils.deepClone(context.state) as BuilderState;\n nextState.baseBySkillId[skill.id] = meta.baseValue;\n if (requestedTotal <= 0) {\n delete nextState.allocations[skill.id];\n } else {\n nextState.allocations[skill.id] = result;\n }\n\n await this.updateBuilderState(nextState);\n await this.syncSkillsFromBuilderState(nextState);\n }\n\n private async rollCharacteristicCheck(characterKey: string, label: string): Promise {\n const validKeys = new Set([\"str\", \"mas\", \"con\", \"int\", \"pow\", \"dex\", \"app\", \"edu\", \"age\"]);\n if (!validKeys.has(characterKey)) return;\n\n const base = Math.max(0, toInt((this.actor as any).system?.characteristics?.[characterKey], 0));\n const content = `\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n

Roll d100 against Characteristic x Multiplier.

\n
\n `;\n\n const multiplier = await new Promise((resolve) => {\n let settled = false;\n const done = (value: number | null) => {\n if (settled) return;\n settled = true;\n resolve(value);\n };\n\n new Dialog({\n title: `${label} Check`,\n content,\n buttons: {\n roll: {\n label: \"Roll\",\n callback: (dlgHtml: JQuery) => {\n const selected = Number((dlgHtml[0]?.querySelector(\"select[name='multiplier']\") as HTMLSelectElement | null)?.value || 1);\n const safeMultiplier = Math.max(1, Math.min(7, Math.floor(selected || 1)));\n done(safeMultiplier);\n }\n },\n cancel: {\n label: \"Cancel\",\n callback: () => done(null)\n }\n },\n default: \"roll\",\n close: () => done(null)\n }).render(true);\n });\n\n if (!multiplier) return;\n const target = clampPercent(base * multiplier);\n const result = await rollPercentile(target);\n\n await postSkillRollCard({\n actor: this.actor,\n actorName: this.actor.name,\n actorImg: this.actor.img,\n skillName: `${label} x${multiplier}`,\n skillImg: DEFAULT_SKILL_ICON,\n roll: result.roll,\n target: result.target,\n outcomeLabel: outcomeLabel(result.outcome),\n isSuccess: result.isSuccess,\n isSpecial: result.isSpecial,\n isFumble: result.isFumble\n });\n }\n\n activateListeners(html: JQuery): void {\n super.activateListeners(html);\n\n html.find(\".toggle-sheet-lock\").on(\"click\", async (event) => {\n event.preventDefault();\n const next = !this.isToolsUnlocked();\n await this.actor.update({ \"system.sheetToolsUnlocked\": next });\n ui.notifications?.info(next ? \"Sheet tools unlocked.\" : \"Sheet tools locked.\");\n });\n\n html.find(\".roll-characteristic\").on(\"click\", async (event) => {\n event.preventDefault();\n const button = event.currentTarget as HTMLElement;\n const key = String(button.dataset.char || \"\").trim().toLowerCase();\n const label = String(button.dataset.label || key.toUpperCase());\n if (!key) return;\n await this.rollCharacteristicCheck(key, label);\n });\n\n html.find(\".item-create\").on(\"click\", async (event) => {\n event.preventDefault();\n const type = String((event.currentTarget as HTMLElement).dataset.type || \"gear\");\n const doc: any = { name: `New ${type}`, type };\n if (type === \"skill\") {\n doc.img = DEFAULT_SKILL_ICON;\n doc.system = { value: 0, category: \"Technical\", specialization: \"\" };\n }\n await this.actor.createEmbeddedDocuments(\"Item\", [doc]);\n });\n\n html.find(\".item-edit\").on(\"click\", (event) => {\n event.preventDefault();\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n const item = li ? this.actor.items.get(li.dataset.itemId ?? \"\") : null;\n item?.sheet?.render(true);\n });\n\n html.find(\".item-delete\").on(\"click\", async (event) => {\n event.preventDefault();\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n if (!li?.dataset.itemId) return;\n await this.actor.deleteEmbeddedDocuments(\"Item\", [li.dataset.itemId]);\n });\n\n html.find(\".item-roll-skill\").on(\"click\", async (event) => {\n event.preventDefault();\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n if (!li?.dataset.itemId) return;\n await (this.actor as any).rollSkillById(li.dataset.itemId);\n });\n\n html.find(\".roll-derived\").on(\"click\", async (event) => {\n event.preventDefault();\n const key = String((event.currentTarget as HTMLElement).dataset.check || \"\");\n if (!key) return;\n await (this.actor as any).rollDerivedCheck(key);\n });\n\n html.find(\".item-use-weapon\").on(\"click\", async (event) => {\n event.preventDefault();\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n if (!li?.dataset.itemId) return;\n await (this.actor as any).useWeaponById(li.dataset.itemId);\n });\n\n html.find(\".item-toggle-equipped\").on(\"click\", async (event) => {\n event.preventDefault();\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n if (!li?.dataset.itemId) return;\n const item = this.actor.items.get(li.dataset.itemId);\n if (!item) return;\n\n const nextEquipped = !Boolean(item.system?.equipped);\n const update: Record = {\n _id: item.id,\n \"system.equipped\": nextEquipped\n };\n if (nextEquipped) {\n update[\"system.stowed\"] = false;\n }\n await this.actor.updateEmbeddedDocuments(\"Item\", [update]);\n });\n\n html.find(\".item-toggle-stowed\").on(\"click\", async (event) => {\n event.preventDefault();\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n if (!li?.dataset.itemId) return;\n const item = this.actor.items.get(li.dataset.itemId);\n if (!item) return;\n\n const nextStowed = !Boolean(item.system?.stowed);\n const update: Record = {\n _id: item.id,\n \"system.stowed\": nextStowed\n };\n if (nextStowed) {\n update[\"system.equipped\"] = false;\n }\n await this.actor.updateEmbeddedDocuments(\"Item\", [update]);\n });\n\n html.find(\".roll-hit-location\").on(\"click\", async (event) => {\n event.preventDefault();\n await (this.actor as any).rollHitLocation();\n });\n\n html.find(\".reset-conditions\").on(\"click\", async (event) => {\n event.preventDefault();\n await this.actor.update({\n \"system.conditions.prone\": false,\n \"system.conditions.unconscious\": false,\n \"system.conditions.dead\": false,\n \"system.conditions.dying\": false,\n \"system.conditions.unusableLimbs.rArm\": false,\n \"system.conditions.unusableLimbs.lArm\": false,\n \"system.conditions.unusableLimbs.rLeg\": false,\n \"system.conditions.unusableLimbs.lLeg\": false\n });\n });\n\n html.find(\".populate-base-skills\").on(\"click\", async (event) => {\n event.preventDefault();\n const added = await (this.actor as any).ensureBaseSkills();\n if (added > 0) ui.notifications?.info(`Added ${added} Ringworld base skills.`);\n else ui.notifications?.info(\"All Ringworld base skills are already present.\");\n });\n\n html.find(\".add-base-skill\").on(\"click\", async (event) => {\n event.preventDefault();\n const button = event.currentTarget as HTMLElement;\n const name = String(button.dataset.skillName || \"\");\n const category = String(button.dataset.skillCategory || \"\");\n const base = Number(button.dataset.skillBase || 0);\n const specialization = String(button.dataset.skillSpecialization || \"\");\n if (!name) return;\n\n await this.actor.createEmbeddedDocuments(\"Item\", [{\n name,\n type: \"skill\",\n img: skillIconPath(name),\n system: { value: base, category, specialization }\n }]);\n });\n\n html.find(\".builder-add-pursuit\").on(\"click\", async (event) => {\n event.preventDefault();\n const root = (event.currentTarget as HTMLElement).closest(\".rw-builder-pursuits\");\n if (!root) return;\n const name = String((root.querySelector(\".builder-new-pursuit\") as HTMLSelectElement | null)?.value || \"\");\n const years = Number((root.querySelector(\".builder-new-years\") as HTMLInputElement | null)?.value || 0);\n await this.addOrUpdatePursuit(name, years);\n });\n\n html.find(\".builder-update-pursuit\").on(\"click\", async (event) => {\n event.preventDefault();\n const row = (event.currentTarget as HTMLElement).closest(\".builder-pursuit-row\");\n if (!row) return;\n const name = String((row.querySelector(\".builder-pursuit-name\") as HTMLSelectElement | null)?.value || \"\");\n const years = Number((row.querySelector(\".builder-pursuit-years\") as HTMLInputElement | null)?.value || 0);\n await this.addOrUpdatePursuit(name, years);\n });\n\n html.find(\".builder-remove-pursuit\").on(\"click\", async (event) => {\n event.preventDefault();\n const index = Number((event.currentTarget as HTMLElement).dataset.index ?? -1);\n if (!Number.isFinite(index) || index < 0) return;\n await this.removePursuit(index);\n });\n\n html.find(\".builder-reset-points\").on(\"click\", async (event) => {\n event.preventDefault();\n await this.resetBuilderAllocations();\n ui.notifications?.info(\"Character-builder allocations reset.\");\n });\n\n html.find(\".roll-characteristics\").on(\"click\", async (event) => {\n event.preventDefault();\n if (!this.isToolsUnlocked()) return;\n await (this.actor as any).rollCharacteristics?.();\n ui.notifications?.info(\"Characteristics rolled.\");\n });\n\n html.find(\".builder-auto-allocate\").on(\"click\", async (event) => {\n event.preventDefault();\n if (!this.isToolsUnlocked()) return;\n await (this.actor as any).ensureBaseSkills?.();\n await this.randomizeBuilderAllocations();\n });\n\n html.find(\".builder-skill-allocate\").on(\"click\", async (event) => {\n event.preventDefault();\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n const skillId = String(li?.dataset.itemId || \"\");\n if (!skillId) return;\n await this.promptSkillAllocation(skillId);\n });\n }\n\n protected async _onDropItemCreate(itemData: any): Promise {\n const items = Array.isArray(itemData) ? itemData : [itemData];\n const speciesItems = items.filter((entry: any) => entry?.type === \"species\");\n const createItems = items.filter((entry: any) => entry?.type !== \"species\");\n\n for (const species of speciesItems) {\n await (this.actor as any).applySpeciesItem?.(species);\n }\n if (speciesItems.length > 0) {\n const names = speciesItems.map((entry: any) => String(entry?.name || \"Species\")).join(\", \");\n ui.notifications?.info(`Applied species to actor: ${names}`);\n }\n\n if (createItems.length === 0) return [];\n\n const patched = createItems.map((entry: any) => {\n if (entry?.type !== \"skill\") return entry;\n return {\n ...entry,\n img: resolveSkillIcon(String(entry?.name || \"Skill\"), String(entry?.img || \"\"))\n };\n });\n return super._onDropItemCreate(Array.isArray(itemData) ? patched : patched[0]);\n }\n}\n","import { getActorHitLocationSchema } from \"../../hit-location-schemas\";\n\nconst BaseActorSheet = (foundry as any).appv1.sheets.ActorSheet;\n\nexport class RingworldCreatureSheet extends BaseActorSheet {\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"actor\", \"creature\"],\n width: 760,\n height: 700\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/actor/creature-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n const actor = this.actor as any;\n\n return {\n ...data,\n system: actor.system,\n skills: actor.items.filter((item: any) => item.type === \"skill\"),\n weapons: actor.items.filter((item: any) => item.type === \"weapon\"),\n hitLocations: getActorHitLocationSchema(actor).entries.map((entry) => ({\n slug: entry.slug,\n rollRange: `${entry.min}-${entry.max}`,\n label: entry.label,\n ...(actor.system.hitLocations?.[entry.slug] ?? {})\n }))\n };\n }\n\n activateListeners(html: JQuery): void {\n super.activateListeners(html);\n\n html.find(\".item-create\").on(\"click\", async (event) => {\n event.preventDefault();\n const type = String((event.currentTarget as HTMLElement).dataset.type || \"skill\");\n await this.actor.createEmbeddedDocuments(\"Item\", [{ name: `New ${type}`, type }]);\n });\n\n html.find(\".item-edit\").on(\"click\", (event) => {\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n const item = li ? this.actor.items.get(li.dataset.itemId ?? \"\") : null;\n item?.sheet?.render(true);\n });\n\n html.find(\".item-delete\").on(\"click\", async (event) => {\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n if (!li?.dataset.itemId) return;\n await this.actor.deleteEmbeddedDocuments(\"Item\", [li.dataset.itemId]);\n });\n\n html.find(\".item-roll-skill\").on(\"click\", async (event) => {\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n if (!li?.dataset.itemId) return;\n await (this.actor as any).rollSkillById(li.dataset.itemId);\n });\n\n html.find(\".item-use-weapon\").on(\"click\", async (event) => {\n const li = (event.currentTarget as HTMLElement).closest(\".item\");\n if (!li?.dataset.itemId) return;\n await (this.actor as any).useWeaponById(li.dataset.itemId);\n });\n\n html.find(\".roll-hit-location\").on(\"click\", async () => {\n await (this.actor as any).rollHitLocation();\n });\n }\n}\n","const BaseActorSheet = (foundry as any).appv1.sheets.ActorSheet;\n\nexport class RingworldVehicleSheet extends BaseActorSheet {\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"actor\", \"vehicle\"],\n width: 680,\n height: 560\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/actor/vehicle-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n return {\n ...data,\n system: (this.actor as any).system\n };\n }\n}\n","const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet;\n\nexport class RingworldSkillSheet extends BaseItemSheet {\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"item\", \"skill\"],\n width: 520,\n height: 420\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/item/skill-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n return {\n ...data,\n system: (this.item as any).system\n };\n }\n\n activateListeners(html: JQuery): void {\n super.activateListeners(html);\n html.find(\".roll-skill\").on(\"click\", async (event) => {\n event.preventDefault();\n await (this.item as any).rollSkill();\n });\n }\n}\n","const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet;\n\nexport class RingworldWeaponSheet extends BaseItemSheet {\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"item\", \"weapon\"],\n width: 620,\n height: 620\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/item/weapon-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n const actor = this.item.actor as any;\n\n return {\n ...data,\n system: (this.item as any).system,\n skills: actor ? actor.items.filter((item: any) => item.type === \"skill\") : [],\n powerPacks: actor ? actor.items.filter((item: any) => item.type === \"gear\" && item.system.isPowerPack) : []\n };\n }\n\n activateListeners(html: JQuery): void {\n super.activateListeners(html);\n html.find(\".use-weapon\").on(\"click\", async (event) => {\n event.preventDefault();\n await (this.item as any).useWeapon();\n });\n }\n}\n","const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet;\n\nexport class RingworldArmorSheet extends BaseItemSheet {\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"item\", \"armor\"],\n width: 620,\n height: 560\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/item/armor-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n return {\n ...data,\n system: (this.item as any).system\n };\n }\n}\n","const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet;\n\nexport class RingworldGearSheet extends BaseItemSheet {\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"item\", \"gear\"],\n width: 620,\n height: 520\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/item/gear-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n return {\n ...data,\n system: (this.item as any).system\n };\n }\n}\n","const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet;\n\nexport class RingworldSpeciesSheet extends BaseItemSheet {\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"item\", \"species\"],\n width: 620,\n height: 560\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/item/species-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n return {\n ...data,\n system: (this.item as any).system\n };\n }\n}\n","const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet;\n\nfunction asArray(value: unknown): string[] {\n return Array.isArray(value) ? value.map((entry) => String(entry)) : [];\n}\n\nexport class RingworldPursuitSheet extends BaseItemSheet {\n static get defaultOptions(): any {\n return foundry.utils.mergeObject(super.defaultOptions, {\n classes: [\"ringworld\", \"sheet\", \"item\", \"pursuit\"],\n width: 680,\n height: 620\n });\n }\n\n get template(): string {\n return \"systems/ringworld/templates/item/pursuit-sheet.hbs\";\n }\n\n async getData(options?: any): Promise {\n const data = await super.getData(options);\n const system = (this.item as any).system ?? {};\n const fixedSkills = asArray(system.fixedSkills);\n const optionalGroups = Array.isArray(system.optionalSkillGroups)\n ? system.optionalSkillGroups.map((group: any) => ({\n choose: Number(group?.choose ?? 1),\n skills: asArray(group?.skills)\n }))\n : [];\n\n return {\n ...data,\n system,\n fixedSkills,\n optionalGroups\n };\n }\n}\n\n","import { SYSTEM_ID } from \"./constants\";\n\ntype MajorActionKind = \"draw\" | \"aim\" | \"melee\" | \"stand\" | \"perception\" | \"unarmed\";\ntype MinorActionKind = \"fire\" | \"fall\" | \"rise\" | \"minorCustom\";\ntype ActionKind = MajorActionKind | MinorActionKind;\n\ninterface ActionEvent {\n id: string;\n actorId: string | null;\n combatantId: string;\n kind: ActionKind;\n major: boolean;\n startImpulse: number;\n completeImpulse: number;\n itemId?: string;\n skillId?: string;\n targetTokenId?: string;\n}\n\ninterface ImpulseState {\n currentImpulse: number;\n queue: ActionEvent[];\n}\n\nconst IMPULSE_FLAG = \"impulseState\";\nconst IMPULSE_HUD_POS_KEY = `${SYSTEM_ID}.impulseHudPos`;\nconst MAJOR_ACTION_OPTIONS: { value: MajorActionKind; label: string }[] = [\n { value: \"draw\", label: \"Draw / Stow Weapon\" },\n { value: \"aim\", label: \"Aim Ranged Weapon\" },\n { value: \"melee\", label: \"Melee Attack\" },\n { value: \"stand\", label: \"Stand / Recover\" },\n { value: \"perception\", label: \"Perception Skill\" },\n { value: \"unarmed\", label: \"Unarmed Combat\" }\n];\nconst MINOR_ACTION_OPTIONS: { value: MinorActionKind; label: string }[] = [\n { value: \"fire\", label: \"Fire Ranged Weapon\" },\n { value: \"fall\", label: \"Fall\" },\n { value: \"rise\", label: \"Rise (Kneeling to Stand/Run)\" },\n { value: \"minorCustom\", label: \"Other Minor Action\" }\n];\n\nfunction num(value: unknown, fallback = 0): number {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction esc(value: unknown): string {\n return String(value ?? \"\")\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll(\"\\\"\", \""\");\n}\n\nexport function actionRankingFromDex(dex: number): number {\n if (dex <= 4) return 7;\n if (dex <= 8) return 6;\n if (dex <= 12) return 5;\n if (dex <= 16) return 4;\n if (dex <= 20) return 3;\n if (dex <= 24) return 2;\n return 1;\n}\n\nfunction actionKindLabel(kind: ActionKind): string {\n switch (kind) {\n case \"draw\":\n return \"Draw / Stow\";\n case \"aim\":\n return \"Aim\";\n case \"melee\":\n return \"Melee Attack\";\n case \"stand\":\n return \"Stand / Recover\";\n case \"perception\":\n return \"Perception\";\n case \"unarmed\":\n return \"Unarmed Combat\";\n case \"fire\":\n return \"Fire Ranged Weapon\";\n case \"fall\":\n return \"Fall\";\n case \"rise\":\n return \"Rise\";\n case \"minorCustom\":\n return \"Minor Action\";\n default:\n return String(kind);\n }\n}\n\nfunction getDexForCombatant(combatant: any): number {\n return Math.max(1, num(combatant?.actor?.system?.characteristics?.dex, 10));\n}\n\nfunction getCombatantActionRanking(combatant: any): number {\n const fromFlag = num(combatant?.flags?.[SYSTEM_ID]?.actionRanking, 0);\n if (fromFlag > 0) return fromFlag;\n return actionRankingFromDex(getDexForCombatant(combatant));\n}\n\nfunction getEncumbranceArPenalty(combatant: any): number {\n const fromActor = num(combatant?.actor?.system?.encumbrance?.actionRankingPenalty, 0);\n return Math.max(0, Math.trunc(fromActor));\n}\n\nasync function syncCombatantActionRanking(combatant: any): Promise {\n const ranking = actionRankingFromDex(getDexForCombatant(combatant));\n const existing = num(combatant?.flags?.[SYSTEM_ID]?.actionRanking, 0);\n if (existing !== ranking) {\n await combatant.update({ [`flags.${SYSTEM_ID}.actionRanking`]: ranking });\n }\n return ranking;\n}\n\nfunction getImpulseState(combat: any): ImpulseState {\n const raw = foundry.utils.deepClone(combat.getFlag(SYSTEM_ID, IMPULSE_FLAG) ?? {});\n const queue = Array.isArray(raw.queue) ? raw.queue : [];\n return {\n currentImpulse: Math.max(1, num(raw.currentImpulse, 1)),\n queue\n };\n}\n\nasync function setImpulseState(combat: any, state: ImpulseState): Promise {\n await combat.setFlag(SYSTEM_ID, IMPULSE_FLAG, {\n currentImpulse: Math.max(1, num(state.currentImpulse, 1)),\n queue: Array.isArray(state.queue) ? state.queue : []\n });\n}\n\nfunction sortQueueByCompletion(queue: ActionEvent[], combat: any): ActionEvent[] {\n return [...queue].sort((a, b) => {\n if (a.completeImpulse !== b.completeImpulse) return a.completeImpulse - b.completeImpulse;\n const aDex = getDexForCombatant(combat.combatants.get(a.combatantId));\n const bDex = getDexForCombatant(combat.combatants.get(b.combatantId));\n return bDex - aDex;\n });\n}\n\nfunction pruneQueueToCombatants(queue: ActionEvent[], combat: any): ActionEvent[] {\n const validIds = new Set(combat.combatants.map((c: any) => c.id));\n return queue.filter((entry) => validIds.has(entry.combatantId));\n}\n\nfunction loadHudPosition(): { left: number; top: number } | null {\n try {\n const raw = localStorage.getItem(IMPULSE_HUD_POS_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n const left = num(parsed?.left, NaN);\n const top = num(parsed?.top, NaN);\n if (!Number.isFinite(left) || !Number.isFinite(top)) return null;\n return { left, top };\n } catch (_err) {\n return null;\n }\n}\n\nfunction saveHudPosition(position: { left: number; top: number }): void {\n try {\n localStorage.setItem(\n IMPULSE_HUD_POS_KEY,\n JSON.stringify({ left: Math.round(position.left), top: Math.round(position.top) })\n );\n } catch (_err) {\n // Ignore storage issues.\n }\n}\n\nfunction clearHudPosition(): void {\n try {\n localStorage.removeItem(IMPULSE_HUD_POS_KEY);\n } catch (_err) {\n // Ignore storage issues.\n }\n}\n\nfunction clampHudPosition(position: { left: number; top: number }, element: HTMLElement): { left: number; top: number } {\n const margin = 8;\n const width = element.offsetWidth || 300;\n const height = element.offsetHeight || 220;\n const maxLeft = Math.max(margin, window.innerWidth - width - margin);\n const maxTop = Math.max(margin, window.innerHeight - height - margin);\n return {\n left: Math.min(Math.max(margin, position.left), maxLeft),\n top: Math.min(Math.max(margin, position.top), maxTop)\n };\n}\n\nfunction applyHudAbsolutePosition(hud: JQuery, position: { left: number; top: number }): void {\n const el = hud.get(0) as HTMLElement | undefined;\n if (!el) return;\n const clamped = clampHudPosition(position, el);\n hud.css({\n left: `${clamped.left}px`,\n top: `${clamped.top}px`,\n right: \"auto\",\n bottom: \"auto\"\n });\n}\n\nfunction makeHudDraggable(hud: JQuery): void {\n const el = hud.get(0) as HTMLElement | undefined;\n if (!el) return;\n\n const handle = hud.find(\".rw-imp-mini-head\");\n if (!handle.length) return;\n\n const saved = loadHudPosition();\n if (saved) {\n applyHudAbsolutePosition(hud, saved);\n }\n\n let dragging = false;\n let startX = 0;\n let startY = 0;\n let startLeft = 0;\n let startTop = 0;\n\n handle.on(\"mousedown\", (event) => {\n const mouseEvent = event as JQuery.MouseDownEvent;\n if (mouseEvent.button !== 0) return;\n if ((mouseEvent.target as HTMLElement)?.closest(\"button\")) return;\n mouseEvent.preventDefault();\n\n const rect = el.getBoundingClientRect();\n startX = mouseEvent.clientX;\n startY = mouseEvent.clientY;\n startLeft = rect.left;\n startTop = rect.top;\n dragging = true;\n\n hud.addClass(\"dragging\");\n hud.css({ left: `${startLeft}px`, top: `${startTop}px`, right: \"auto\", bottom: \"auto\" });\n\n $(document).on(\"mousemove.ringworldImpulseHudDrag\", (moveEvent) => {\n if (!dragging) return;\n const evt = moveEvent as JQuery.MouseMoveEvent;\n const deltaX = evt.clientX - startX;\n const deltaY = evt.clientY - startY;\n applyHudAbsolutePosition(hud, { left: startLeft + deltaX, top: startTop + deltaY });\n });\n\n $(document).one(\"mouseup.ringworldImpulseHudDrag\", () => {\n dragging = false;\n hud.removeClass(\"dragging\");\n $(document).off(\"mousemove.ringworldImpulseHudDrag\");\n const latestRect = el.getBoundingClientRect();\n saveHudPosition({ left: latestRect.left, top: latestRect.top });\n });\n });\n\n handle.on(\"dblclick\", () => {\n clearHudPosition();\n hud.css({ left: \"\", top: \"\", right: \"\", bottom: \"\" });\n });\n}\n\nfunction getQueueWithNames(combat: any, queue: ActionEvent[]): Array<{\n combatantId: string;\n name: string;\n label: string;\n completeImpulse: number;\n dex: number;\n}> {\n return sortQueueByCompletion(pruneQueueToCombatants(queue, combat), combat).map((entry) => {\n const combatant = combat.combatants.get(entry.combatantId);\n return {\n combatantId: entry.combatantId,\n name: String(combatant?.name || \"Unknown\"),\n label: actionKindLabel(entry.kind),\n completeImpulse: entry.completeImpulse,\n dex: getDexForCombatant(combatant)\n };\n });\n}\n\nfunction renderQueueRowsMarkup(rows: ReturnType, limit = 8): string {\n if (!rows.length) return `
No actions queued.
`;\n return rows.slice(0, limit).map((row) => `\n
\n ${esc(row.name)}\n ${esc(row.label)} @ ${row.completeImpulse}\n
\n `).join(\"\");\n}\n\nfunction attachOverlayControls(root: JQuery, combat: any): void {\n const disabled = game.user?.isGM ? false : true;\n root.find(\".rw-mini-next\").prop(\"disabled\", disabled).on(\"click\", async (event) => {\n event.preventDefault();\n await advanceImpulse(combat, \"next\");\n });\n root.find(\".rw-mini-step\").prop(\"disabled\", disabled).on(\"click\", async (event) => {\n event.preventDefault();\n await advanceImpulse(combat, \"step\");\n });\n root.find(\".rw-mini-reset\").prop(\"disabled\", disabled).on(\"click\", async (event) => {\n event.preventDefault();\n await resetImpulseSequence(combat);\n });\n}\n\nfunction renderChatImpulseMarker(combat: any | null): void {\n const chatEl = (ui as any)?.chat?.element as JQuery | undefined;\n if (!chatEl?.length) return;\n chatEl.find(\".ringworld-impulse-chat-marker\").remove();\n if (!combat) return;\n\n const state = getImpulseState(combat);\n const rows = getQueueWithNames(combat, state.queue);\n const marker = $(`\n
\n
\n Impulse ${state.currentImpulse}\n Queued ${rows.length}\n
\n
${renderQueueRowsMarkup(rows, 5)}
\n
\n \n \n \n
\n
\n `);\n attachOverlayControls(marker, combat);\n chatEl.prepend(marker);\n}\n\nfunction renderFloatingImpulseHud(combat: any | null): void {\n const existing = $(\"#ringworld-impulse-hud\");\n existing.remove();\n if (!combat) return;\n\n const state = getImpulseState(combat);\n const rows = getQueueWithNames(combat, state.queue);\n const hud = $(`\n
\n
\n Impulse ${state.currentImpulse}\n Queued ${rows.length}\n
\n
${renderQueueRowsMarkup(rows, 8)}
\n
\n \n \n \n
\n
\n `);\n attachOverlayControls(hud, combat);\n $(\"body\").append(hud);\n makeHudDraggable(hud);\n}\n\nfunction refreshImpulseOverlays(combat: any | null): void {\n renderChatImpulseMarker(combat);\n renderFloatingImpulseHud(combat);\n}\n\nasync function ensureGmControl(): Promise {\n if (game.user?.isGM) return true;\n ui.notifications?.warn(\"Only the GM can modify Ringworld impulse sequencing.\");\n return false;\n}\n\nasync function scheduleAction(\n combat: any,\n combatantId: string,\n kind: ActionKind,\n major: boolean\n): Promise {\n if (!(await ensureGmControl())) return;\n\n const combatant = combat.combatants.get(combatantId);\n if (!combatant) return;\n\n const state = getImpulseState(combat);\n const currentImpulse = state.currentImpulse;\n const actionRanking = await syncCombatantActionRanking(combatant);\n const encPenalty = getEncumbranceArPenalty(combatant);\n const impulseMod = num(combatant.flags?.[SYSTEM_ID]?.impulseModifier, 0);\n const effectiveAr = Math.max(1, actionRanking + encPenalty);\n const baseDuration = major ? effectiveAr : 1;\n const duration = Math.max(1, baseDuration + impulseMod);\n const completeImpulse = currentImpulse + duration;\n\n const event: ActionEvent = {\n id: foundry.utils.randomID(),\n actorId: combatant.actor?.id ?? null,\n combatantId,\n kind,\n major,\n startImpulse: currentImpulse,\n completeImpulse\n };\n\n const queueWithoutCombatant = pruneQueueToCombatants(\n state.queue.filter((entry) => entry.combatantId !== combatantId),\n combat\n );\n queueWithoutCombatant.push(event);\n\n await setImpulseState(combat, {\n currentImpulse,\n queue: sortQueueByCompletion(queueWithoutCombatant, combat)\n });\n\n await combatant.update({\n [`flags.${SYSTEM_ID}.pendingEventId`]: event.id,\n [`flags.${SYSTEM_ID}.pendingKind`]: kind,\n [`flags.${SYSTEM_ID}.actionRanking`]: actionRanking,\n [`flags.${SYSTEM_ID}.effectiveActionRanking`]: effectiveAr\n });\n\n const timingLabel = major\n ? `AR ${actionRanking} + Enc ${encPenalty}${impulseMod ? ` ${impulseMod > 0 ? \"+\" : \"\"} Imp ${impulseMod}` : \"\"}`\n : `Minor 1${impulseMod ? ` ${impulseMod > 0 ? \"+\" : \"\"} Imp ${impulseMod}` : \"\"}`;\n ui.notifications?.info(\n `${combatant.name}: ${actionKindLabel(kind)} completes on impulse ${completeImpulse} (${timingLabel} => ${duration}).`\n );\n refreshImpulseOverlays(combat);\n}\n\nasync function cancelAction(combat: any, combatantId: string): Promise {\n if (!(await ensureGmControl())) return;\n\n const combatant = combat.combatants.get(combatantId);\n if (!combatant) return;\n\n const state = getImpulseState(combat);\n const nextQueue = pruneQueueToCombatants(\n state.queue.filter((entry) => entry.combatantId !== combatantId),\n combat\n );\n\n await setImpulseState(combat, {\n currentImpulse: state.currentImpulse,\n queue: sortQueueByCompletion(nextQueue, combat)\n });\n\n await combatant.update({\n [`flags.${SYSTEM_ID}.pendingEventId`]: null,\n [`flags.${SYSTEM_ID}.pendingKind`]: \"\",\n [`flags.${SYSTEM_ID}.aimState`]: {\n active: false,\n targetId: null,\n sinceImpulse: null\n }\n });\n refreshImpulseOverlays(combat);\n}\n\nasync function resolveActionEvent(combat: any, event: ActionEvent, impulse: number): Promise {\n const combatant = combat.combatants.get(event.combatantId);\n if (!combatant) return;\n\n const updates: Record = {\n [`flags.${SYSTEM_ID}.pendingEventId`]: null,\n [`flags.${SYSTEM_ID}.pendingKind`]: \"\"\n };\n\n if (event.kind === \"aim\") {\n updates[`flags.${SYSTEM_ID}.aimState`] = {\n active: true,\n targetId: event.targetTokenId ?? null,\n sinceImpulse: impulse\n };\n }\n\n await combatant.update(updates);\n\n const content = `\n
\n

Impulse ${impulse} Completion

\n

${esc(combatant.name)} completes ${esc(actionKindLabel(event.kind))}.

\n

DEX ${getDexForCombatant(combatant)} | AR ${getCombatantActionRanking(combatant)}

\n
\n `;\n await ChatMessage.create({\n user: game.user?.id,\n speaker: ChatMessage.getSpeaker({ actor: combatant.actor ?? undefined }),\n content\n });\n}\n\nasync function advanceImpulse(combat: any, mode: \"next\" | \"step\"): Promise {\n if (!(await ensureGmControl())) return;\n\n const state = getImpulseState(combat);\n const queue = sortQueueByCompletion(pruneQueueToCombatants(state.queue, combat), combat);\n const current = state.currentImpulse;\n\n let targetImpulse = current + 1;\n if (mode === \"next\") {\n const next = queue\n .map((event) => event.completeImpulse)\n .filter((impulse) => impulse >= current)\n .sort((a, b) => a - b)[0];\n if (Number.isFinite(next)) {\n targetImpulse = next;\n }\n }\n\n const completing = queue.filter((event) => event.completeImpulse === targetImpulse);\n const remaining = queue.filter((event) => event.completeImpulse !== targetImpulse);\n\n await setImpulseState(combat, {\n currentImpulse: targetImpulse,\n queue: remaining\n });\n\n const ordered = sortQueueByCompletion(completing, combat);\n for (const event of ordered) {\n await resolveActionEvent(combat, event, targetImpulse);\n }\n\n if (!ordered.length) {\n ui.notifications?.info(`Impulse advanced to ${targetImpulse}.`);\n }\n refreshImpulseOverlays(combat);\n}\n\nasync function resetImpulseSequence(combat: any): Promise {\n if (!(await ensureGmControl())) return;\n\n await setImpulseState(combat, { currentImpulse: 1, queue: [] });\n\n for (const combatant of combat.combatants) {\n await combatant.update({\n [`flags.${SYSTEM_ID}.pendingEventId`]: null,\n [`flags.${SYSTEM_ID}.pendingKind`]: \"\",\n [`flags.${SYSTEM_ID}.aimState`]: {\n active: false,\n targetId: null,\n sinceImpulse: null\n },\n [`flags.${SYSTEM_ID}.actionRanking`]: actionRankingFromDex(getDexForCombatant(combatant)),\n [`flags.${SYSTEM_ID}.effectiveActionRanking`]: null,\n [`flags.${SYSTEM_ID}.impulseModifier`]: 0\n });\n }\n refreshImpulseOverlays(combat);\n}\n\nfunction renderImpulsePanel(app: any, htmlLike: JQuery | HTMLElement): void {\n const html = \"find\" in (htmlLike as any) ? (htmlLike as JQuery) : $(htmlLike);\n if (!html?.length) return;\n html.addClass(\"ringworld-impulse-mode ringworld-impulse-only\");\n\n const combat = app?.viewed ?? game.combat;\n if (!combat) return;\n\n const state = getImpulseState(combat);\n const queue = sortQueueByCompletion(pruneQueueToCombatants(state.queue, combat), combat);\n if (queue.length !== state.queue.length) {\n void setImpulseState(combat, { currentImpulse: state.currentImpulse, queue });\n }\n\n html\n .find(\n \".token-initiative, .combatant-initiative, .initiative, [data-action='rollInitiative'], [data-control='rollInitiative'], .combatant .fa-dice-d20\"\n )\n .addClass(\"rw-hide-initiative\");\n\n html.find(\".ringworld-impulse-panel\").remove();\n\n const disabled = game.user?.isGM ? \"\" : \"disabled\";\n const rows = combat.combatants.map((combatant: any) => {\n const ar = getCombatantActionRanking(combatant);\n const encPenalty = getEncumbranceArPenalty(combatant);\n const impMod = num(combatant.flags?.[SYSTEM_ID]?.impulseModifier, 0);\n const effAr = Math.max(1, ar + encPenalty);\n const dex = getDexForCombatant(combatant);\n const pending = queue.find((event) => event.combatantId === combatant.id);\n const pendingLabel = pending\n ? `${actionKindLabel(pending.kind)} @ ${pending.completeImpulse}`\n : \"Ready\";\n const aimState = combatant.flags?.[SYSTEM_ID]?.aimState;\n const aimLabel = aimState?.active ? `Aiming since ${aimState.sinceImpulse}` : \"Not aiming\";\n\n const options = MAJOR_ACTION_OPTIONS.map((opt) => {\n const selected = pending?.major && pending?.kind === opt.value ? \"selected\" : \"\";\n return ``;\n }).join(\"\");\n const minorOptions = MINOR_ACTION_OPTIONS.map((opt) => {\n const selected = !pending?.major && pending?.kind === opt.value ? \"selected\" : \"\";\n return ``;\n }).join(\"\");\n\n return `\n
\n
\n ${esc(combatant.name)}\n DEX ${dex} | AR ${ar} + Enc ${encPenalty} = ${effAr} | Imp Mod ${impMod >= 0 ? `+${impMod}` : impMod} | ${esc(pendingLabel)} | ${esc(aimLabel)}\n
\n
\n \n \n \n \n \n \n \n
\n
\n `;\n }).join(\"\");\n\n const panel = $(`\n
\n
\n
Impulse ${state.currentImpulse}
\n
Queued: ${queue.length}
\n
\n \n \n \n
\n
\n
${rows}
\n
\n `);\n\n html.prepend(panel);\n\n // Hide all native combat tracker blocks so only Ringworld impulse UI is shown.\n const root = html.get(0);\n if (root) {\n for (const child of Array.from(root.children)) {\n if (!(child as HTMLElement).classList.contains(\"ringworld-impulse-panel\")) {\n (child as HTMLElement).classList.add(\"rw-hide-core-tracker\");\n }\n }\n }\n\n panel.find(\".rw-advance-next\").on(\"click\", async (event) => {\n event.preventDefault();\n await advanceImpulse(combat, \"next\");\n });\n\n panel.find(\".rw-advance-step\").on(\"click\", async (event) => {\n event.preventDefault();\n await advanceImpulse(combat, \"step\");\n });\n\n panel.find(\".rw-reset-sequence\").on(\"click\", async (event) => {\n event.preventDefault();\n await resetImpulseSequence(combat);\n });\n\n panel.find(\".rw-major-start\").on(\"click\", async (event) => {\n event.preventDefault();\n const row = (event.currentTarget as HTMLElement).closest(\".rw-imp-row\");\n if (!row) return;\n const combatantId = String(row.getAttribute(\"data-combatant-id\") || \"\");\n const select = row.querySelector(\".rw-major-kind\") as HTMLSelectElement | null;\n const kind = String(select?.value || \"draw\") as MajorActionKind;\n await scheduleAction(combat, combatantId, kind, true);\n });\n\n panel.find(\".rw-minor-start\").on(\"click\", async (event) => {\n event.preventDefault();\n const row = (event.currentTarget as HTMLElement).closest(\".rw-imp-row\");\n if (!row) return;\n const combatantId = String(row.getAttribute(\"data-combatant-id\") || \"\");\n const select = row.querySelector(\".rw-minor-kind\") as HTMLSelectElement | null;\n const kind = String(select?.value || \"minorCustom\") as MinorActionKind;\n await scheduleAction(combat, combatantId, kind, false);\n });\n\n panel.find(\".rw-cancel-start\").on(\"click\", async (event) => {\n event.preventDefault();\n const row = (event.currentTarget as HTMLElement).closest(\".rw-imp-row\");\n if (!row) return;\n const combatantId = String(row.getAttribute(\"data-combatant-id\") || \"\");\n await cancelAction(combat, combatantId);\n });\n\n panel.find(\".rw-save-mods\").on(\"click\", async (event) => {\n event.preventDefault();\n if (!(await ensureGmControl())) return;\n const row = (event.currentTarget as HTMLElement).closest(\".rw-imp-row\");\n if (!row) return;\n const combatantId = String(row.getAttribute(\"data-combatant-id\") || \"\");\n const combatant = combat.combatants.get(combatantId);\n if (!combatant) return;\n\n const impModInput = row.querySelector(\".rw-imp-mod\") as HTMLInputElement | null;\n const impMod = Math.trunc(num(impModInput?.value, 0));\n await combatant.update({\n [`flags.${SYSTEM_ID}.impulseModifier`]: impMod\n });\n });\n\n refreshImpulseOverlays(combat);\n}\n\nexport function initializeImpulseTracker(): void {\n Hooks.on(\"renderCombatTracker\", (app: any, htmlLike: JQuery | HTMLElement) => {\n renderImpulsePanel(app, htmlLike);\n });\n\n Hooks.on(\"createCombat\", async (combat: any) => {\n await setImpulseState(combat, { currentImpulse: 1, queue: [] });\n refreshImpulseOverlays(combat);\n });\n\n Hooks.on(\"createCombatant\", async (combatant: any) => {\n await syncCombatantActionRanking(combatant);\n refreshImpulseOverlays(game.combat ?? null);\n });\n\n Hooks.on(\"deleteCombatant\", async () => {\n refreshImpulseOverlays(game.combat ?? null);\n });\n\n Hooks.on(\"updateCombat\", (combat: any) => {\n refreshImpulseOverlays(combat ?? game.combat ?? null);\n });\n\n Hooks.on(\"deleteCombat\", () => {\n refreshImpulseOverlays(null);\n });\n\n Hooks.on(\"renderChatLog\", () => {\n refreshImpulseOverlays(game.combat ?? null);\n });\n\n Hooks.on(\"canvasReady\", () => {\n refreshImpulseOverlays(game.combat ?? null);\n });\n\n Hooks.on(\"updateActor\", (actor: any) => {\n const combat = game.combat;\n if (!combat) return;\n const inCombat = combat.combatants.some((c: any) => c.actor?.id === actor.id);\n if (inCombat) {\n ui.combat?.render(true);\n refreshImpulseOverlays(combat);\n }\n });\n\n Hooks.on(\"updateItem\", (item: any) => {\n const actor = item?.parent;\n if (!actor) return;\n const combat = game.combat;\n if (!combat) return;\n const inCombat = combat.combatants.some((c: any) => c.actor?.id === actor.id);\n if (inCombat) {\n ui.combat?.render(true);\n refreshImpulseOverlays(combat);\n }\n });\n}\n","import \"./styles/ringworld.scss\";\n\nimport { RingworldActor } from \"./module/actor/RingworldActor\";\nimport { RingworldItem } from \"./module/item/RingworldItem\";\nimport { RingworldExplorerSheet } from \"./module/sheets/actors/ExplorerSheet\";\nimport { RingworldCreatureSheet } from \"./module/sheets/actors/CreatureSheet\";\nimport { RingworldVehicleSheet } from \"./module/sheets/actors/VehicleSheet\";\nimport { RingworldSkillSheet } from \"./module/sheets/items/SkillSheet\";\nimport { RingworldWeaponSheet } from \"./module/sheets/items/WeaponSheet\";\nimport { RingworldArmorSheet } from \"./module/sheets/items/ArmorSheet\";\nimport { RingworldGearSheet } from \"./module/sheets/items/GearSheet\";\nimport { RingworldSpeciesSheet } from \"./module/sheets/items/SpeciesSheet\";\nimport { RingworldPursuitSheet } from \"./module/sheets/items/PursuitSheet\";\nimport { HIT_LOCATION_TABLE } from \"./module/constants\";\nimport { createRandomExplorer } from \"./module/character-tools\";\nimport { RINGWORLD_CATALOG_VERSION, seedWorldCatalog } from \"./module/world-catalog\";\nimport { initializeImpulseTracker } from \"./module/impulse-tracker\";\nimport { DEFAULT_SKILL_ICON, baseSkillByName, resolveSkillIcon } from \"./module/skill-icons\";\n\nconst ENCUMBRANCE_DATA_MIGRATION_VERSION = 2;\n\nfunction normName(value: unknown): string {\n return String(value ?? \"\").trim().toLowerCase();\n}\n\nfunction worldItemWeightMap(): Map {\n const map = new Map();\n for (const item of game.items ?? []) {\n if (item.type !== \"weapon\" && item.type !== \"armor\" && item.type !== \"gear\") continue;\n const key = `${item.type}:${normName(item.name)}`;\n map.set(key, Number((item as any).system?.weight ?? 0) || 0);\n }\n return map;\n}\n\nasync function migrateActorItemEncumbranceData(): Promise {\n if (!game.user?.isGM) return 0;\n const weightMap = worldItemWeightMap();\n let touched = 0;\n\n for (const actor of game.actors ?? []) {\n const updates: any[] = [];\n\n for (const item of actor.items ?? []) {\n if (item.type !== \"weapon\" && item.type !== \"armor\" && item.type !== \"gear\") continue;\n const patch: Record = { _id: item.id };\n let changed = false;\n\n if ((item as any).system?.equipped === undefined) {\n patch[\"system.equipped\"] = true;\n changed = true;\n }\n\n if ((item as any).system?.weight === undefined) {\n const key = `${item.type}:${normName(item.name)}`;\n patch[\"system.weight\"] = Number(weightMap.get(key) ?? 0);\n changed = true;\n }\n\n if ((item as any).system?.stowed === undefined) {\n patch[\"system.stowed\"] = false;\n changed = true;\n }\n\n if (changed) {\n updates.push(patch);\n }\n }\n\n if (updates.length) {\n await actor.updateEmbeddedDocuments(\"Item\", updates);\n touched += updates.length;\n }\n }\n\n return touched;\n}\n\nfunction injectDirectoryButton(htmlLike: JQuery | HTMLElement | null | undefined, className: string, label: string, onClick: () => Promise | void): void {\n if (!htmlLike) return;\n const html = \"find\" in (htmlLike as any) ? (htmlLike as JQuery) : $(htmlLike);\n if (!html.length || html.find(`.${className}`).length) return;\n\n const button = $(``);\n button.on(\"click\", async () => {\n await onClick();\n });\n\n const containers = [\n html.find(\".directory-footer\"),\n html.find(\"footer.directory-footer\"),\n html.find(\"footer\"),\n html.find(\".footer-actions\"),\n html.find(\".header-actions\"),\n html.find(\".action-buttons\")\n ];\n\n const container = containers.find((candidate) => candidate.length);\n if (container) {\n container.first().append(button);\n return;\n }\n\n let fallback = html.find(\".ringworld-directory-controls\");\n if (!fallback.length) {\n fallback = $('
');\n html.append(fallback);\n }\n fallback.first().append(button);\n}\n\nasync function handleSeedCatalogClick(): Promise {\n await seedWorldCatalog({ notify: true });\n}\n\nasync function handleCreateRandomExplorerClick(): Promise {\n const actor = await createRandomExplorer();\n actor?.sheet?.render(true);\n}\n\nfunction ensureSidebarDirectoryButtons(): void {\n if (!game.user?.isGM) return;\n\n const itemElement = (ui as any).items?.element;\n injectDirectoryButton(itemElement, \"ringworld-seed-items\", \"Seed Ringworld Items\", handleSeedCatalogClick);\n\n const actorElement = (ui as any).actors?.element;\n injectDirectoryButton(actorElement, \"ringworld-random-actor\", \"Create Random Explorer\", handleCreateRandomExplorerClick);\n}\n\nfunction itemTypeAvailable(type: string): boolean {\n const directTypes = (getDocumentClass(\"Item\") as any)?.TYPES;\n if (Array.isArray(directTypes)) return directTypes.includes(type);\n\n const docTypes = (game as any)?.system?.documentTypes?.Item;\n if (Array.isArray(docTypes)) return docTypes.includes(type);\n if (docTypes && typeof docTypes === \"object\") return Object.prototype.hasOwnProperty.call(docTypes, type);\n\n const configModels = (CONFIG as any)?.Item?.dataModels;\n if (configModels && typeof configModels === \"object\" && Object.prototype.hasOwnProperty.call(configModels, type)) {\n return true;\n }\n return false;\n}\n\nHooks.once(\"init\", async () => {\n console.log(\"ringworld | Initializing Ringworld system\");\n initializeImpulseTracker();\n\n const ActorRegistry = (foundry as any)?.documents?.collections?.Actors;\n const ItemRegistry = (foundry as any)?.documents?.collections?.Items;\n const BaseActorSheet = (foundry as any)?.appv1?.sheets?.ActorSheet;\n const BaseItemSheet = (foundry as any)?.appv1?.sheets?.ItemSheet;\n\n (CONFIG as any).ringworld = {\n hitLocations: HIT_LOCATION_TABLE\n };\n CONFIG.Combat.initiative = foundry.utils.mergeObject(CONFIG.Combat.initiative ?? {}, {\n formula: \"1d100\",\n decimals: 0\n }, { inplace: false });\n\n CONFIG.Actor.documentClass = RingworldActor as any;\n CONFIG.Item.documentClass = RingworldItem as any;\n\n if (!ActorRegistry || !ItemRegistry || !BaseActorSheet || !BaseItemSheet) {\n console.error(\"ringworld | Foundry sheet registries were not available during init.\");\n return;\n }\n\n ActorRegistry.unregisterSheet(\"core\", BaseActorSheet);\n ActorRegistry.registerSheet(\"ringworld\", RingworldExplorerSheet as any, {\n types: [\"explorer\"],\n makeDefault: true\n });\n ActorRegistry.registerSheet(\"ringworld\", RingworldCreatureSheet as any, {\n types: [\"creature\"],\n makeDefault: true\n });\n ActorRegistry.registerSheet(\"ringworld\", RingworldVehicleSheet as any, {\n types: [\"vehicle\"],\n makeDefault: true\n });\n\n ItemRegistry.unregisterSheet(\"core\", BaseItemSheet);\n ItemRegistry.registerSheet(\"ringworld\", RingworldSkillSheet as any, {\n types: [\"skill\"],\n makeDefault: true\n });\n ItemRegistry.registerSheet(\"ringworld\", RingworldWeaponSheet as any, {\n types: [\"weapon\"],\n makeDefault: true\n });\n ItemRegistry.registerSheet(\"ringworld\", RingworldArmorSheet as any, {\n types: [\"armor\"],\n makeDefault: true\n });\n ItemRegistry.registerSheet(\"ringworld\", RingworldGearSheet as any, {\n types: [\"gear\"],\n makeDefault: true\n });\n ItemRegistry.registerSheet(\"ringworld\", RingworldSpeciesSheet as any, {\n types: [\"species\"],\n makeDefault: true\n });\n if (itemTypeAvailable(\"pursuit\")) {\n ItemRegistry.registerSheet(\"ringworld\", RingworldPursuitSheet as any, {\n types: [\"pursuit\"],\n makeDefault: true\n });\n }\n\n game.settings.register(\"ringworld\", \"catalogSeedVersion\", {\n scope: \"world\",\n config: false,\n type: Number,\n default: 0\n });\n game.settings.register(\"ringworld\", \"encumbranceDataMigrationVersion\", {\n scope: \"world\",\n config: false,\n type: Number,\n default: 0\n });\n\n Handlebars.registerHelper(\"eq\", (a: unknown, b: unknown) => a === b);\n Handlebars.registerHelper(\"or\", (a: unknown, b: unknown) => Boolean(a || b));\n});\n\nHooks.once(\"ready\", async () => {\n const path = \"systems/ringworld/resources/hit-locations/humanoid.json\";\n try {\n const response = await fetch(path);\n if (response.ok) {\n const table = await response.json();\n (CONFIG as any).ringworld.hitLocations = table.entries ?? HIT_LOCATION_TABLE;\n }\n } catch (_error) {\n console.warn(`ringworld | Could not load ${path}, using built-in table`);\n }\n\n if (game.user?.isGM) {\n for (const combat of game.combats ?? []) {\n const formula = String(combat?.settings?.initiative?.formula ?? \"\").trim();\n if (!formula || formula.toLowerCase() === \"undefined\") {\n await combat.update({\n \"settings.initiative.formula\": \"1d100\",\n \"settings.initiative.decimals\": 0\n });\n }\n }\n\n const seededVersion = Number(game.settings.get(\"ringworld\", \"catalogSeedVersion\") ?? 0);\n if (seededVersion < RINGWORLD_CATALOG_VERSION) {\n const result = await seedWorldCatalog({ notify: true });\n if (result.added >= 0) {\n await game.settings.set(\"ringworld\", \"catalogSeedVersion\", RINGWORLD_CATALOG_VERSION);\n }\n }\n\n const migratedVersion = Number(game.settings.get(\"ringworld\", \"encumbranceDataMigrationVersion\") ?? 0);\n if (migratedVersion < ENCUMBRANCE_DATA_MIGRATION_VERSION) {\n const touched = await migrateActorItemEncumbranceData();\n await game.settings.set(\"ringworld\", \"encumbranceDataMigrationVersion\", ENCUMBRANCE_DATA_MIGRATION_VERSION);\n if (touched > 0) {\n ui.notifications?.info(`Ringworld encumbrance migration updated ${touched} actor items.`);\n }\n }\n }\n\n ensureSidebarDirectoryButtons();\n window.setTimeout(() => ensureSidebarDirectoryButtons(), 300);\n\n (game as any).ringworld = {\n ...((game as any).ringworld ?? {}),\n seedWorldCatalog: async (options?: { notify?: boolean }) => seedWorldCatalog(options),\n createRandomExplorer: async () => createRandomExplorer(),\n catalogVersion: RINGWORLD_CATALOG_VERSION,\n systemId: \"ringworld\",\n systemVersion: String(game.system?.version ?? \"0.0.0\")\n };\n Hooks.callAll(\"ringworldReady\", (game as any).ringworld);\n});\n\nHooks.on(\"createActor\", async (actor: any, _options: any, userId: string) => {\n if (!game.user?.isGM) return;\n if (userId !== game.user.id) return;\n if (!actor || (actor.type !== \"explorer\" && actor.type !== \"creature\")) return;\n\n try {\n const added = await actor.ensureBaseSkills?.();\n if (Number(added) > 0) {\n console.log(`ringworld | Added ${added} base skills to ${actor.name}`);\n }\n } catch (error) {\n console.error(\"ringworld | Failed to auto-populate base skills\", error);\n }\n});\n\nHooks.on(\"preCreateItem\", (item: any) => {\n if (item.type !== \"skill\") return;\n\n const updates: Record = {};\n const name = String(item.name || \"Skill\");\n const currentImg = String(item.img || \"\");\n updates.img = resolveSkillIcon(name, currentImg);\n\n const base = baseSkillByName(name);\n const currentSystem = (item as any).system ?? {};\n const currentValue = Number(currentSystem?.value ?? NaN);\n const currentCategory = String(currentSystem?.category ?? \"\").trim();\n\n updates.system = {\n value: Number.isFinite(currentValue) ? currentValue : (base?.base ?? 0),\n category: currentCategory || (base?.category ?? \"Technical\"),\n specialization: String(currentSystem?.specialization ?? base?.specialization ?? \"\")\n };\n\n if (!updates.img) updates.img = DEFAULT_SKILL_ICON;\n item.updateSource(updates);\n});\n\nHooks.on(\"renderItemDirectory\", (_app: any, html: JQuery) => {\n if (!game.user?.isGM) return;\n injectDirectoryButton(html, \"ringworld-seed-items\", \"Seed Ringworld Items\", handleSeedCatalogClick);\n ensureSidebarDirectoryButtons();\n});\n\nHooks.on(\"renderActorDirectory\", (_app: any, html: JQuery) => {\n if (!game.user?.isGM) return;\n injectDirectoryButton(html, \"ringworld-random-actor\", \"Create Random Explorer\", handleCreateRandomExplorerClick);\n ensureSidebarDirectoryButtons();\n});\n\nHooks.on(\"renderSidebar\", () => {\n ensureSidebarDirectoryButtons();\n});\n\nHooks.on(\"changeSidebarTab\", (app: any) => {\n const tabName = String(app?.tabName ?? app?.id ?? app?.options?.id ?? \"\");\n if (tabName === \"items\" || tabName === \"actors\") {\n ensureSidebarDirectoryButtons();\n }\n});\n"],"names":["showDiceSoNice","roll","dice3d","evaluateRollWithDice","formula","rollPercentile","target","classifyPercentile","cleanTarget","specialThreshold","fumbleThreshold","isSuccess","isSpecial","isFumble","outcome","outcomeLabel","SYSTEM_ID","HIT_LOCATION_TABLE","LIMB_LOCATIONS","LEG_LOCATIONS","isLimb","slug","normalize","value","withFactors","entries","hpFactors","entry","HUMANOID_SCHEMA","DOLPHIN_SCHEMA","PUPPETEER_SCHEMA","TRINOC_SCHEMA","BANDERSNATCHI_SCHEMA","KZINTI_SCHEMA","HIT_LOCATION_SCHEMAS","listHitLocationSchemas","inferHitLocationSchemaId","speciesValue","species","getHitLocationSchemaById","schemaId","key","getActorHitLocationSchema","actor","_a","_b","_c","mode","getTableForActor","getHitLocationFromRollForTable","table","rollHitLocation","total","chooseOrRollHitLocation","schema","entriesBySlug","labelFor","fallback","html","result","resolve","settled","done","dlgHtml","picker","event","button","location","rolled","loc","BASE_SKILL_CATEGORIES","RINGWORLD_BASE_SKILLS","normalizeSkillName","name","baseSkillNameSet","skill","DEFAULT_SKILL_ICON","iconSlug","skillIconPath","baseSkillByName","resolveSkillIcon","currentImg","img","createChatMessage","options","speaker","postSkillRollCard","data","payload","content","postAttackCard","num","n","getSingleTargetActor","targets","rollStandaloneHitLocation","rollSkillForActor","skillName","skillItem","item","resolveLinkedSkill","weapon","skillId","byId","linkedSkillName","byName","clampPercentTarget","promptAttackModifiers","attacker","baseSkillTarget","isRanged","defaultAim","safeActorName","safeWeaponName","safeActorImg","safeWeaponImg","_d","_e","_f","_g","root","query","aimModeRaw","aimMode","aimMultiplier","coverMod","movementMod","rangeMod","situationalMod","customMod","customLabel","flatModifierTotal","preAimTarget","adjustedByAim","finalTarget","parts","aimLabel","consumeWeaponEnergy","cost","packId","pack","current","next","getItemArmorAtLocation","specific","getTotalArmorAtLocation","targetActor","locArmor","stackedItemsArmor","sum","buildConditionUpdates","locationValue","locationMax","generalHp","syncCoreStatusEffects","conditions","toggler","applyLocationDamage","rawDamage","currentLoc","locMax","armor","finalDamage","unclampedLoc","nextLoc","nextGeneral","updates","useWeaponWorkflow","hasTarget","targetName","linkedSkill","modifierResult","energy","attack","remainingGeneralHp","hitSelection","damageFormula","damageRoll","application","recomputeHitPointMaximums","_h","con","mas","existingMax","existingValue","generalMax","hadExisting","wasFull","looksPlaceholder","preservedDamage","recomputedValue","generalAtFull","hpFactor","max","previousMax","previousValue","isPlaceholder","shouldBackfillToFull","RINGWORLD_CATALOG_VERSION","ICONS","speciesIconPath","weaponIconPath","armorIconPath","gearIconPath","pursuitIconPath","allCoverage","torsoCoverage","headCoverage","armCoverage","legCoverage","SPECIES_CATALOG","WEAPON_CATALOG","ARMOR_CATALOG","GEAR_CATALOG","PURSUIT_CATALOG","speciesCatalogForGeneration","itemKey","type","itemTypeAvailable","directTypes","docTypes","configModels","gameModels","toSkillItem","toSpeciesItem","toWeaponItem","folderKey","toArmorItem","toGearItem","toPursuitItem","pursuitAsGearNotes","fixed","optional","group","optionalText","toPursuitFallbackGearItem","itemName","getOrCreateItemFolder","parentId","existing","folder","ensureCatalogFolders","skills","pursuits","gear","weapons","weaponMelee","weaponRanged","buildAllSeeds","canUsePursuitType","seedWorldCatalog","folderIds","seeds","createPayload","updatePayload","seed","folderId","existingItem","update","existingSystem","mergedSystem","systemDiff","getPlayableSpeciesFromWorld","clampScore","rollTotal","rollEdu","edu","die","rollByGravity","gravity","gravityFormulas","randomPick","arr","randomExplorerName","speciesName","starts","ends","applySpeciesModifiers","base","speciesSystem","mods","rollCharacteristicsForActor","gravityType","currentSpeciesName","speciesItem","builtIn","finalValues","ownLanguage","applyStarterKit","starterNames","worldItems","actorExisting","worldItem","randomizeExplorerActor","worldSpecies","builtInSpecies","pickedWorldSpecies","pickedBuiltInSpecies","speciesChoice","homeworld","createRandomExplorer","SPECIES_MODIFIER_KEYS","hasValue","clampCharacteristic","computeDamageMod","str","computeDerivedRolls","characteristics","int","pow","dex","computeRootMaxes","app","computeActionRanking","gravityLoadMultiplier","contributesToEncumbrance","itemWeightKg","computeEncumbrance","system","items","gravityMultiplier","equippedLoadKg","rawLoadKg","effectiveLoadKg","limitKg","overLimitKg","actionRankingPenalty","overLimit","baseMove","baseRun","RingworldActor","profile","derivedRollDefaults","rootMaxDefaults","encumbrance","weaponId","toCreate","speciesData","incomingMods","appliedState","previousMods","currentCharacteristics","currentProfile","normalizedMods","prev","agePrev","ageNext","chronoCurrent","physCurrent","labels","defaults","RingworldItem","BaseActorSheet","ROOT_KEY_BY_CATEGORY","ROOT_FORMULA_BY_CATEGORY","EXCLUDED_EDUCATION_SKILLS","HYPERDRIVE_KEY","a","b","toInt","normalizeAllocation","raw","normalizePursuitSelection","years","normalizeBuilderState","allocations","allocation","normalized","baseBySkillId","isEducationEligible","isSpecialEligible","buildPursuitAllowedSkillSet","entriesByName","allowed","pursuit","computeBuilderPools","age","educationTotal","pursuitYearsTotal","pursuitsTotal","specialTotal","buildBuilderContext","pools","state","pursuitOptions","pursuitAllowedSkills","bySkillId","educationSpent","pursuitsSpent","specialSpent","totalAllocated","baseFromState","baseFromDefinition","inferredBase","baseValue","rootKey","rootCap","maxAllocation","normalizedSkillName","allowsEducation","allowsPursuits","allowsSpecial","pursuitYearsAllocated","row","fromItems","getPreparedHitLocation","rollRange","locationConfig","source","hp","rawLabel","chooseRandom","clampPercent","RingworldExplorerSheet","_i","_j","_k","_l","_m","_n","_o","_p","builderContext","armors","speciesOptions","baseSet","skillByNormalized","skillRootMax","skillsByCategory","category","rows","meta","customSkills","missingBaseSkillsCount","rangesFromSchema","hitLocations","bodyLocations","enc","encRaw","encEffective","encLimit","encPenalty","baseActionRanking","effectiveActionRanking","encumbranceSummary","movementSummary","fromBase","trimmed","cleanYears","context","nextState","existingIndex","index","totalYears","desiredCount","available","i","idx","remainingYears","seededState","freshContext","totalBySkill","maxForSkill","allocatePool","points","poolKey","eligible","remaining","guard","candidates","picked","educationUnspent","pursuitsUnspent","specialUnspent","maxEducation","maxPursuits","maxSpecial","maxByCap","form","requestedTotal","characterKey","label","multiplier","selected","safeMultiplier","doc","li","nextEquipped","nextStowed","added","specialization","itemData","speciesItems","createItems","names","patched","RingworldCreatureSheet","RingworldVehicleSheet","BaseItemSheet","RingworldSkillSheet","RingworldWeaponSheet","RingworldArmorSheet","RingworldGearSheet","RingworldSpeciesSheet","asArray","RingworldPursuitSheet","fixedSkills","optionalGroups","IMPULSE_FLAG","IMPULSE_HUD_POS_KEY","MAJOR_ACTION_OPTIONS","MINOR_ACTION_OPTIONS","esc","actionRankingFromDex","actionKindLabel","kind","getDexForCombatant","combatant","getCombatantActionRanking","fromFlag","getEncumbranceArPenalty","fromActor","syncCombatantActionRanking","ranking","getImpulseState","combat","queue","setImpulseState","sortQueueByCompletion","aDex","pruneQueueToCombatants","validIds","c","loadHudPosition","parsed","left","top","saveHudPosition","position","clearHudPosition","clampHudPosition","element","width","height","maxLeft","maxTop","applyHudAbsolutePosition","hud","el","clamped","makeHudDraggable","handle","saved","dragging","startX","startY","startLeft","startTop","mouseEvent","rect","moveEvent","evt","deltaX","deltaY","latestRect","getQueueWithNames","renderQueueRowsMarkup","limit","attachOverlayControls","disabled","advanceImpulse","resetImpulseSequence","renderChatImpulseMarker","chatEl","marker","renderFloatingImpulseHud","refreshImpulseOverlays","ensureGmControl","scheduleAction","combatantId","major","currentImpulse","actionRanking","impulseMod","effectiveAr","duration","completeImpulse","queueWithoutCombatant","timingLabel","cancelAction","nextQueue","resolveActionEvent","impulse","targetImpulse","completing","ordered","renderImpulsePanel","htmlLike","ar","impMod","effAr","pending","pendingLabel","aimState","opt","minorOptions","panel","child","select","impModInput","initializeImpulseTracker","ENCUMBRANCE_DATA_MIGRATION_VERSION","normName","worldItemWeightMap","map","migrateActorItemEncumbranceData","weightMap","touched","patch","changed","injectDirectoryButton","className","onClick","container","candidate","handleSeedCatalogClick","handleCreateRandomExplorerClick","ensureSidebarDirectoryButtons","itemElement","actorElement","ActorRegistry","ItemRegistry","path","response","_options","userId","error","currentSystem","currentValue","currentCategory","_app","tabName"],"mappings":"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"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9ecddf3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2755 @@ +{ + "name": "foundryvtt-ringworld", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "foundryvtt-ringworld", + "version": "0.1.0", + "devDependencies": { + "@league-of-foundry-developers/foundry-vtt-types": "^12.331.3", + "sass": "^1.93.2", + "typescript": "^5.9.3", + "vite": "^5.4.21" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@league-of-foundry-developers/foundry-vtt-types": { + "version": "12.331.5", + "resolved": "https://registry.npmjs.org/@league-of-foundry-developers/foundry-vtt-types/-/foundry-vtt-types-12.331.5.tgz", + "integrity": "sha512-Ncyv5W5dtE8LdVxjYLfrx2ujQIuBPch5bDcnHaqKTL4wVEE8jgCE7amPkGHltFLilLTZ7Ki3veuFDURzpDIgbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pixi/basis": "^7.4.2", + "@pixi/graphics-smooth": "^1.1.0", + "@pixi/particle-emitter": "^5.0.8", + "@types/earcut": "^3.0.0", + "@types/jquery": "~3.5.22", + "@types/showdown": "~2.0.6", + "@types/simple-peer": "~9.11.1", + "@types/youtube": "~0.0.48", + "earcut": "^3.0.1", + "handlebars": "^4.7.8", + "pixi.js": "^7.4.2", + "postinstall-postinstall": "^2.1.0", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.5.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.0", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-model": "^1.21.0", + "prosemirror-schema-list": "^1.3.0", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.3.7", + "prosemirror-transform": "^1.9.0", + "prosemirror-view": "^1.33.6", + "socket.io-client": "^4.7.5", + "tinymce": "^6.8.3" + }, + "peerDependencies": { + "typescript": "^5.4" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pixi/accessibility": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/accessibility/-/accessibility-7.4.3.tgz", + "integrity": "sha512-tCr0yeWpMe0yucFvEPidy5a7gVJGpTjqGrDpSEBYT/kbScfUwcoX49RrckCCCiXDlyO4WRh9lVVuHXTvqRLIMg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/events": "7.4.3" + } + }, + "node_modules/@pixi/app": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/app/-/app-7.4.3.tgz", + "integrity": "sha512-opyWMuO0Ir8pf1DYUR++wAA6ZfNU+nIX2z95R2OD172HbcdhB4/HD7leLIIAny/LciEdMqlWEBhXK7N93YWbdg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3" + } + }, + "node_modules/@pixi/assets": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/assets/-/assets-7.4.3.tgz", + "integrity": "sha512-StvjiJBSp/j9hHkGu8AFHNvwYUazXq64WhyhytztyDMRkg/l/cL7EcttY5T0qZNWlIpccdr60LUKrWDOuMpkiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/css-font-loading-module": "^0.0.12" + }, + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/basis": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/basis/-/basis-7.4.3.tgz", + "integrity": "sha512-dnRk8fEP4Y4g7EYUXtRX+Xrgu6C/tvqOdCNvfHAazYoDQ4MT484gp/1jEEabsLcH9tFgftXu/+ZVcUbwzdd8qw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/assets": "7.4.3", + "@pixi/compressed-textures": "7.4.3", + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/color": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/color/-/color-7.4.3.tgz", + "integrity": "sha512-a6R+bXKeXMDcRmjYQoBIK+v2EYqxSX49wcjAY579EYM/WrFKS98nSees6lqVUcLKrcQh2DT9srJHX7XMny3voQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pixi/colord": "^2.9.6" + } + }, + "node_modules/@pixi/colord": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz", + "integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pixi/compressed-textures": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/compressed-textures/-/compressed-textures-7.4.3.tgz", + "integrity": "sha512-uJ3CC+lNX4HIxs6IxEESO50/0A1KxSVm6CO9UlkXzTsNj9ynmdy5BkJ1dzii7LCdqGcHIXHO01yvKuUbJBBQtw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/assets": "7.4.3", + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/constants": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/constants/-/constants-7.4.3.tgz", + "integrity": "sha512-QGmwJUNQy/vVEHzL6VGQvnwawLZ1wceZMI8HwJAT4/I2uAzbBeFDdmCS8WsTpSWLZjF/DszDc1D8BFp4pVJ5UQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pixi/core": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/core/-/core-7.4.3.tgz", + "integrity": "sha512-5YDs11faWgVVTL8VZtLU05/Fl47vaP5Tnsbf+y/WRR0VSW3KhRRGTBU1J3Gdc2xEWbJhUK07KGP7eSZpvtPVgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pixi/color": "7.4.3", + "@pixi/constants": "7.4.3", + "@pixi/extensions": "7.4.3", + "@pixi/math": "7.4.3", + "@pixi/runner": "7.4.3", + "@pixi/settings": "7.4.3", + "@pixi/ticker": "7.4.3", + "@pixi/utils": "7.4.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/pixijs" + } + }, + "node_modules/@pixi/display": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/display/-/display-7.4.3.tgz", + "integrity": "sha512-b5m2dAaoNAVdxz1oDaxl3XZ059NEOcNtGkxTOZ4EYCw/jcp9sZXkgSROHRzsGn4k+NugH7+9MP4Id2Z0kkdUhw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/events": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/events/-/events-7.4.3.tgz", + "integrity": "sha512-o3j/5Dxq6WDVS6eHfURB/cf/MP+NcsF/eC5PnbSHjXxJmDE7PoTVwLvxexm5uuvNRpFh/6/Fn0V8Vl4gV8sc8w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3" + } + }, + "node_modules/@pixi/extensions": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/extensions/-/extensions-7.4.3.tgz", + "integrity": "sha512-FhoiYkHQEDYHUE7wXhqfsTRz6KxLXjuMbSiAwnLb9uG1vAgp6q6qd6HEsf4X30YaZbLFY8a4KY6hFZWjF+4Fdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pixi/extract": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/extract/-/extract-7.4.3.tgz", + "integrity": "sha512-HNvGNrEVaeVsbcnIO1MsHpjZbTwo9nIlaOEBzDGcL6JWwzuB1RnzUke7WUCndCUt91sGUdvPnvgCvy9/NNFg3w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/filter-alpha": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-alpha/-/filter-alpha-7.4.3.tgz", + "integrity": "sha512-YFdUB1I53USQb+9TEhS849dV2KZhbnNGIoBbOSThUJfXQc4pDguIFWMagVToAQYgmZ4C4AtYfVjaSEELrMcCdA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/filter-blur": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-blur/-/filter-blur-7.4.3.tgz", + "integrity": "sha512-ZFzS9L/whdRbs5A/EUgF3yQaBcxNarmbuwaMgrfnpQ84mRczkGByqDLGToadiufyals07ufTrXBGRle9lbtEDA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/filter-color-matrix": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-color-matrix/-/filter-color-matrix-7.4.3.tgz", + "integrity": "sha512-TNu0h20SrzjUWIb5v19dAp1vPpqtG0w2XF9kIHN91bMNaf3R1jzhpWG6TtaVO9eo1IolWcEJLw38jIohyC+KNw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/filter-displacement": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-displacement/-/filter-displacement-7.4.3.tgz", + "integrity": "sha512-ax+cFA2mEnKgqf9F8qInpv09GNWzjwnASLETpwPXzWBtlAlNCeHV2tCv3+SlMdEKUkwG9sA7AmjjjC2JBUyt+Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/filter-fxaa": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-fxaa/-/filter-fxaa-7.4.3.tgz", + "integrity": "sha512-y9jhho5cCflhEsPtNqqsd+XJHsb+/ysht4rG/VHQ8Z6pScHYpbgiEpowryGq8uSMQQwx6zKNS2DPiXdiOHPZsg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/filter-noise": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-noise/-/filter-noise-7.4.3.tgz", + "integrity": "sha512-rwgSO3BKe1jW/P5CaOcfLKjfpl674aBEo/igi/3QLxA3ORhILNqWRsKkOwP8xF/ejI5NE4rMEkdv0LScbdGFhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/graphics": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/graphics/-/graphics-7.4.3.tgz", + "integrity": "sha512-wWLivD8/URb8A7X4TqCZGG39C91IE+aOuWY/z9NCz5Z6WvA/VWnsc5fLTlO+ggjGHgKF0cSucCXZfUe1wm0AOQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/sprite": "7.4.3" + } + }, + "node_modules/@pixi/graphics-smooth": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@pixi/graphics-smooth/-/graphics-smooth-1.1.1.tgz", + "integrity": "sha512-9xIFWZhHGEb6KCnyWL6TVPYG/QkF0YDM/yDU5EvjTQbaj/1cITrXtI5P3tBkB5H0DQi+8J8/QS38MjfqNEJAYQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "^7.2.0", + "@pixi/display": "^7.2.0", + "@pixi/graphics": "^7.2.0" + } + }, + "node_modules/@pixi/math": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/math/-/math-7.4.3.tgz", + "integrity": "sha512-/uJOVhR2DOZ+zgdI6Bs/CwcXT4bNRKsS+TqX3ekRIxPCwaLra+Qdm7aDxT5cTToDzdxbKL5+rwiLu3Y1egILDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pixi/mesh": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/mesh/-/mesh-7.4.3.tgz", + "integrity": "sha512-CikqFPtKvU3Zj986/MSoC8X39CWv5CEpiEW/tYp47p4tgQNDSkNWYnDiNYgb+4VX6pNsBrgX4DALLdTR17SlSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3" + } + }, + "node_modules/@pixi/mesh-extras": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/mesh-extras/-/mesh-extras-7.4.3.tgz", + "integrity": "sha512-EqpxpVZoTObyupxMSzuUsCGmWPQioW84n9EO9Ajawkk/HYA+qKFZ5viKiEThIUBYgv4Apn/7c0U3Feg7Ez4uQQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/mesh": "7.4.3" + } + }, + "node_modules/@pixi/mixin-cache-as-bitmap": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/mixin-cache-as-bitmap/-/mixin-cache-as-bitmap-7.4.3.tgz", + "integrity": "sha512-NgvDdgSgd2tfcTSc+SWF12JJjVVz5ZrkSlhX0idSp/LSako82AiFJlD2xqH9GUsEcA6sqBBlnu7nrGkPTHQdhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/sprite": "7.4.3" + } + }, + "node_modules/@pixi/mixin-get-child-by-name": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/mixin-get-child-by-name/-/mixin-get-child-by-name-7.4.3.tgz", + "integrity": "sha512-HLhDxHwafQT+CxbqQx9w9ivJIyAOg9JJ/6m4fNymVuDWeuMGcxDxBD7DukdUYIieT+RD/RlxdPEmq8YoromlFA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/display": "7.4.3" + } + }, + "node_modules/@pixi/mixin-get-global-position": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/mixin-get-global-position/-/mixin-get-global-position-7.4.3.tgz", + "integrity": "sha512-k09kvkS379EypCIWgXMY7uiXtWk1BsaJyTYlV16Co0AsmNPdFd+wUozMx1xV6rxcGiWXsxr/1k9fbETuYkcXCQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3" + } + }, + "node_modules/@pixi/particle-container": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/particle-container/-/particle-container-7.4.3.tgz", + "integrity": "sha512-0DfJF5C0XTfuI2FsLYvMKCOtqWjXWGOWfA6m4l0W/Ke/qw5zKIOEhgjPLw4qNRtOhmEfkVKJUGp66Ap/ya2YzA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/sprite": "7.4.3" + } + }, + "node_modules/@pixi/particle-emitter": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@pixi/particle-emitter/-/particle-emitter-5.0.10.tgz", + "integrity": "sha512-GF8AvL2KjVAE+n3FFRjCvE6cmn+rBCQayp9NR8rw5oJ95cx5inKx6DwNyOqSLHyPqNt/tfb9XGS+6P1Wun6yQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/constants": ">=6.0.4 <8.0.0", + "@pixi/core": ">=6.0.4 <8.0.0", + "@pixi/display": ">=6.0.4 <8.0.0", + "@pixi/math": ">=6.0.4 <8.0.0", + "@pixi/sprite": ">=6.0.4 <8.0.0", + "@pixi/ticker": ">=6.0.4 <8.0.0" + } + }, + "node_modules/@pixi/prepare": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/prepare/-/prepare-7.4.3.tgz", + "integrity": "sha512-OjJHGKXPzwP5OLKxBnTBnKMOktHynLvO0TQPqTYgNtmGQzY109mypCqM4M+s/V+uYmBo/T+sXvBahj98q/f1tA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/graphics": "7.4.3", + "@pixi/text": "7.4.3" + } + }, + "node_modules/@pixi/runner": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/runner/-/runner-7.4.3.tgz", + "integrity": "sha512-TJyfp7y23u5vvRAyYhVSa7ytq0PdKSvPLXu4G3meoFh1oxTLHH6g/RIzLuxUAThPG2z7ftthuW3qWq6dRV+dhw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pixi/settings": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/settings/-/settings-7.4.3.tgz", + "integrity": "sha512-SmGK8smc0PxRB9nr0UJioEtE9hl4gvj9OedCvZx3bxBwA3omA5BmP3CyhQfN8XJ29+o2OUL01r3zAPVol4l4lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pixi/constants": "7.4.3", + "@types/css-font-loading-module": "^0.0.12", + "ismobilejs": "^1.1.0" + } + }, + "node_modules/@pixi/sprite": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/sprite/-/sprite-7.4.3.tgz", + "integrity": "sha512-iNBrpOFF9nXDT6m2jcyYy6l/sRzklLDDck1eFHprHZwvNquY2nzRfh+RGBCecxhBcijiLJ3fsZN33fP0LDXkvw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3" + } + }, + "node_modules/@pixi/sprite-animated": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/sprite-animated/-/sprite-animated-7.4.3.tgz", + "integrity": "sha512-mw5YIec8KfO1Jv9qrDNvGoD7Dlmcgww5YlMtd+ARi7Zzo+6ziNw899LXtoaKX1+3BXdZbYNyJAx3C5r30NtwXA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/sprite": "7.4.3" + } + }, + "node_modules/@pixi/sprite-tiling": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/sprite-tiling/-/sprite-tiling-7.4.3.tgz", + "integrity": "sha512-kUa9cEcMsGXSIZoXA7LhW4oo0eWa30w0KYd7mZ0bqalBMfOcvsGZMN701Lc5lpE8URw+8yu5bnyGLbrxhWBTuw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/sprite": "7.4.3" + } + }, + "node_modules/@pixi/spritesheet": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/spritesheet/-/spritesheet-7.4.3.tgz", + "integrity": "sha512-Ce4xZzUxUSKfiROUjjVCBYNLuCcDEWKJ822bSV9rkgVHItu3q04VnEww0DXO+9K0hKv4Ukjjk8aP6Pz0LgPm7A==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/assets": "7.4.3", + "@pixi/core": "7.4.3" + } + }, + "node_modules/@pixi/text": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/text/-/text-7.4.3.tgz", + "integrity": "sha512-IAF0iu04rPg3oiL0HZsEZI44fpJxq3UZ4xTmx8l1RyhhSXiElLvvSlSH57vt/BKMQZtCs+AqEit7yn8heK2+nQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/sprite": "7.4.3" + } + }, + "node_modules/@pixi/text-bitmap": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/text-bitmap/-/text-bitmap-7.4.3.tgz", + "integrity": "sha512-TnBocJm7f5nMAYwYcsojc62uCrOYauAGH26o3pNrlqmHDRDQ7FzPOGvkYZGYFREbUycloLSRlYpSy0FB9ZdV4Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/assets": "7.4.3", + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/mesh": "7.4.3", + "@pixi/text": "7.4.3" + } + }, + "node_modules/@pixi/text-html": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/text-html/-/text-html-7.4.3.tgz", + "integrity": "sha512-nm9K9gjSZAU8ETwQZBE3kMGNdO1IzyghxoRTcJCWKhekiGDpUQhopfNhqieNZ7reVJpvhpFQWjbyaHDehndUaQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/sprite": "7.4.3", + "@pixi/text": "7.4.3" + } + }, + "node_modules/@pixi/ticker": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/ticker/-/ticker-7.4.3.tgz", + "integrity": "sha512-tHsAD0iOUb6QSGGw+c8cyRBvxsq/NlfzIFBZLEHhWZ+Bx4a0MmXup6I/yJDGmyPCYE+ctCcAfY13wKAzdiVFgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pixi/extensions": "7.4.3", + "@pixi/settings": "7.4.3", + "@pixi/utils": "7.4.3" + } + }, + "node_modules/@pixi/utils": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/utils/-/utils-7.4.3.tgz", + "integrity": "sha512-NO3Y9HAn2UKS1YdxffqsPp+kDpVm8XWvkZcS/E+rBzY9VTLnNOI7cawSRm+dacdET3a8Jad3aDKEDZ0HmAqAFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pixi/color": "7.4.3", + "@pixi/constants": "7.4.3", + "@pixi/settings": "7.4.3", + "@types/earcut": "^2.1.0", + "earcut": "^2.2.4", + "eventemitter3": "^4.0.0", + "url": "^0.11.0" + } + }, + "node_modules/@pixi/utils/node_modules/@types/earcut": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz", + "integrity": "sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pixi/utils/node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/css-font-loading-module": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.12.tgz", + "integrity": "sha512-x2tZZYkSxXqWvTDgveSynfjq/T2HyiZHXb00j/+gy19yp70PHCizM48XFdjBCWH7eHBD0R5i/pw9yMBP/BH5uA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/earcut": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-3.0.0.tgz", + "integrity": "sha512-k/9fOUGO39yd2sCjrbAJvGDEQvRwRnQIZlBz43roGwUZo5SHAmyVvSFyaVVZkicRVCaDXPKlbxrUcBuJoSWunQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jquery": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.34.tgz", + "integrity": "sha512-3m3939S3erqmTLJANS/uy0B6V7BorKx7RorcGZVjZ62dF5PAGbKEDZK1CuLtKombJkFA2T1jl8LAIIs7IV6gBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/node": { + "version": "25.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", + "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/showdown": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.6.tgz", + "integrity": "sha512-pTvD/0CIeqe4x23+YJWlX2gArHa8G0J0Oh6GKaVXV7TAeickpkkZiNOgFcFcmLQ5lB/K0qBJL1FtRYltBfbGCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/simple-peer": { + "version": "9.11.9", + "resolved": "https://registry.npmjs.org/@types/simple-peer/-/simple-peer-9.11.9.tgz", + "integrity": "sha512-6Gdl7TSS5oh9nuwKD4Pl8cSmaxWycYeZz9HLnJBNvIwWjZuGVsmHe9RwW3+9RxfhC1aIR9Z83DvaJoMw6rhkbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sizzle": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.10.tgz", + "integrity": "sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/youtube": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/youtube/-/youtube-0.0.50.tgz", + "integrity": "sha512-d4GpH4uPYp9W07kc487tiq6V/EUHl18vZWFMbQoe4Sk9LXEWzFi/BMf9x7TI4m7/j7gU3KeX8H6M8aPBgykeLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/earcut": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/engine.io-client": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz", + "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ismobilejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz", + "integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pixi.js": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-7.4.3.tgz", + "integrity": "sha512-uIWdH0EI2dVgNoqN9aFaHCmR0V65OEhMkXs2sek3c/QP2ItV6UoM+ouX9esSv3ibo20F+J5D1XwnQhUZI6wqeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pixi/accessibility": "7.4.3", + "@pixi/app": "7.4.3", + "@pixi/assets": "7.4.3", + "@pixi/compressed-textures": "7.4.3", + "@pixi/core": "7.4.3", + "@pixi/display": "7.4.3", + "@pixi/events": "7.4.3", + "@pixi/extensions": "7.4.3", + "@pixi/extract": "7.4.3", + "@pixi/filter-alpha": "7.4.3", + "@pixi/filter-blur": "7.4.3", + "@pixi/filter-color-matrix": "7.4.3", + "@pixi/filter-displacement": "7.4.3", + "@pixi/filter-fxaa": "7.4.3", + "@pixi/filter-noise": "7.4.3", + "@pixi/graphics": "7.4.3", + "@pixi/mesh": "7.4.3", + "@pixi/mesh-extras": "7.4.3", + "@pixi/mixin-cache-as-bitmap": "7.4.3", + "@pixi/mixin-get-child-by-name": "7.4.3", + "@pixi/mixin-get-global-position": "7.4.3", + "@pixi/particle-container": "7.4.3", + "@pixi/prepare": "7.4.3", + "@pixi/sprite": "7.4.3", + "@pixi/sprite-animated": "7.4.3", + "@pixi/sprite-tiling": "7.4.3", + "@pixi/spritesheet": "7.4.3", + "@pixi/text": "7.4.3", + "@pixi/text-bitmap": "7.4.3", + "@pixi/text-html": "7.4.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/pixijs" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postinstall-postinstall": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz", + "integrity": "sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT" + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.0.tgz", + "integrity": "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz", + "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.4", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", + "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", + "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", + "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz", + "integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.2.3", + "prosemirror-model": "^1.25.4", + "prosemirror-state": "^1.4.4", + "prosemirror-transform": "^1.10.5", + "prosemirror-view": "^1.41.4" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz", + "integrity": "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.6", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz", + "integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io-client": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", + "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", + "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinymce": { + "version": "6.8.6", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.8.6.tgz", + "integrity": "sha512-++XYEs8lKWvZxDCjrr8Baiw7KiikraZ5JkLMg6EdnUVNKJui0IsrAADj5MsyUeFkcEryfn2jd3p09H7REvewyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3b294c2 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "foundryvtt-ringworld", + "version": "0.1.0", + "description": "Ringworld Foundry VTT system (v12)", + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite build --watch", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@league-of-foundry-developers/foundry-vtt-types": "^12.331.3", + "sass": "^1.93.2", + "typescript": "^5.9.3", + "vite": "^5.4.21" + } +} diff --git a/resources/hit-locations/humanoid.json b/resources/hit-locations/humanoid.json new file mode 100644 index 0000000..2a965bf --- /dev/null +++ b/resources/hit-locations/humanoid.json @@ -0,0 +1,14 @@ +{ + "id": "humanoid", + "label": "Humanoid (Ringworld)", + "die": "1d20", + "entries": [ + { "slug": "rLeg", "label": "Right Leg", "min": 1, "max": 3 }, + { "slug": "lLeg", "label": "Left Leg", "min": 4, "max": 6 }, + { "slug": "abdomen", "label": "Abdomen", "min": 7, "max": 10 }, + { "slug": "chest", "label": "Chest", "min": 11, "max": 15 }, + { "slug": "rArm", "label": "Right Arm", "min": 16, "max": 17 }, + { "slug": "lArm", "label": "Left Arm", "min": 18, "max": 19 }, + { "slug": "head", "label": "Head", "min": 20, "max": 20 } + ] +} diff --git a/ringworld-content/README.md b/ringworld-content/README.md new file mode 100644 index 0000000..ac40dae --- /dev/null +++ b/ringworld-content/README.md @@ -0,0 +1,22 @@ +# Ringworld Content Module + +Companion module for the Ringworld Foundry system. + +## What it does + +- Adds a GM-only button to the Items directory: **Import Ringworld Content** +- One click imports/updates: + - skills + - species + - pursuits + - weapons + - armor + - gear + +It uses the Ringworld system API (`game.ringworld.seedWorldCatalog`) and keeps content synced. + +## Requires + +- Foundry world running the `ringworld` system +- Ringworld system version `0.1.0` or later + diff --git a/ringworld-content/module.json b/ringworld-content/module.json new file mode 100644 index 0000000..aaf44c0 --- /dev/null +++ b/ringworld-content/module.json @@ -0,0 +1,33 @@ +{ + "id": "ringworld-content", + "title": "Ringworld Content", + "description": "Companion content module for Ringworld: one-click import for skills, species, pursuits, weapons, armor, and gear.", + "version": "0.1.0", + "url": "https://github.com/slaguru666/Ringworld", + "manifest": "https://raw.githubusercontent.com/slaguru666/Ringworld/main/ringworld-content/module.json", + "download": "https://raw.githubusercontent.com/slaguru666/Ringworld/main/dist/ringworld-content.zip", + "compatibility": { + "minimum": "12", + "verified": "13.351", + "maximum": "13" + }, + "authors": [ + { + "name": "Codex" + } + ], + "esmodules": [ + "module/ringworld-content.mjs" + ], + "relationships": { + "systems": [ + { + "id": "ringworld", + "type": "system", + "compatibility": { + "minimum": "0.1.0" + } + } + ] + } +} diff --git a/ringworld-content/module/ringworld-content.mjs b/ringworld-content/module/ringworld-content.mjs new file mode 100644 index 0000000..7ec0ba9 --- /dev/null +++ b/ringworld-content/module/ringworld-content.mjs @@ -0,0 +1,75 @@ +const MODULE_ID = "ringworld-content"; + +function isRingworldWorld() { + return String(game.system?.id || "") === "ringworld"; +} + +function canShowControls() { + return isRingworldWorld() && Boolean(game.user?.isGM); +} + +function getRingworldApi() { + return game.ringworld || null; +} + +async function importRingworldContent() { + if (!canShowControls()) return; + const api = getRingworldApi(); + if (!api?.seedWorldCatalog) { + ui.notifications?.error("Ringworld API unavailable. Ensure Ringworld system is active."); + return; + } + + const result = await api.seedWorldCatalog({ notify: true }); + if (result && typeof result.added === "number") { + ui.notifications?.info(`Ringworld Content import complete: +${result.added}, updated ${result.updated || 0}.`); + } +} + +function injectDirectoryButton(htmlLike) { + if (!canShowControls()) return; + if (!htmlLike) return; + const html = ("find" in htmlLike) ? htmlLike : $(htmlLike); + if (!html?.length || html.find(".ringworld-content-import").length) return; + + const button = $( + `` + ); + button.on("click", async () => { + await importRingworldContent(); + }); + + const candidates = [ + html.find(".directory-footer"), + html.find("footer.directory-footer"), + html.find("footer"), + html.find(".footer-actions"), + html.find(".header-actions"), + html.find(".action-buttons") + ]; + const container = candidates.find((candidate) => candidate.length); + if (container) { + container.first().append(button); + return; + } + + html.append($('
').append(button)); +} + +Hooks.once("ready", () => { + if (!canShowControls()) return; + game.ringworldContent = { + importCatalog: importRingworldContent + }; +}); + +Hooks.on("renderItemDirectory", (_app, html) => { + injectDirectoryButton(html); +}); + +Hooks.on("renderSidebar", () => { + if (!canShowControls()) return; + const itemsElement = ui.items?.element; + injectDirectoryButton(itemsElement); +}); + diff --git a/seeds/base-skills.json b/seeds/base-skills.json new file mode 100644 index 0000000..3fec4fa --- /dev/null +++ b/seeds/base-skills.json @@ -0,0 +1,59 @@ +[ + { "name": "Archaic Melee Weapon", "type": "skill", "system": { "value": 5, "category": "Agility", "specialization": "" } }, + { "name": "Archaic Ranged Weapons", "type": "skill", "system": { "value": 3, "category": "Agility", "specialization": "" } }, + { "name": "Athletics", "type": "skill", "system": { "value": 15, "category": "Agility", "specialization": "" } }, + { "name": "Hide", "type": "skill", "system": { "value": 10, "category": "Agility", "specialization": "" } }, + { "name": "Sneak", "type": "skill", "system": { "value": 5, "category": "Agility", "specialization": "" } }, + { "name": "Unarmed Combat", "type": "skill", "system": { "value": 0, "category": "Agility", "specialization": "" } }, + { "name": "V. Sword, F. Laser", "type": "skill", "system": { "value": 15, "category": "Agility", "specialization": "" } }, + + { "name": "Bargain", "type": "skill", "system": { "value": 10, "category": "Communication", "specialization": "" } }, + { "name": "Debate", "type": "skill", "system": { "value": 5, "category": "Communication", "specialization": "" } }, + { "name": "Fast Talk", "type": "skill", "system": { "value": 10, "category": "Communication", "specialization": "" } }, + { "name": "Fine Arts", "type": "skill", "system": { "value": 5, "category": "Communication", "specialization": "" } }, + { "name": "Musicianship", "type": "skill", "system": { "value": 5, "category": "Communication", "specialization": "" } }, + { "name": "Orate", "type": "skill", "system": { "value": 5, "category": "Communication", "specialization": "" } }, + { "name": "Own Language", "type": "skill", "system": { "value": 0, "category": "Communication", "specialization": "INTx5 base" } }, + { "name": "Perform", "type": "skill", "system": { "value": 5, "category": "Communication", "specialization": "" } }, + { "name": "Psychology", "type": "skill", "system": { "value": 0, "category": "Communication", "specialization": "" } }, + + { "name": "Anthropology", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Astronomy", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Biology", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Botany", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Chemistry", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Computers", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Emergency Treatment", "type": "skill", "system": { "value": 1, "category": "Knowledge", "specialization": "" } }, + { "name": "Engineering", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Farming", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "History", "type": "skill", "system": { "value": 5, "category": "Knowledge", "specialization": "" } }, + { "name": "Law", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Mathematics", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Physics", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Planetology", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Second Languages", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Strategy", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Theology", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + { "name": "Zoology", "type": "skill", "system": { "value": 0, "category": "Knowledge", "specialization": "" } }, + + { "name": "Handgun, Energy", "type": "skill", "system": { "value": 5, "category": "Perception", "specialization": "" } }, + { "name": "Handgun, Projectile", "type": "skill", "system": { "value": 3, "category": "Perception", "specialization": "" } }, + { "name": "Heavy Weapon, Energy", "type": "skill", "system": { "value": 5, "category": "Perception", "specialization": "" } }, + { "name": "Heavy Weapon, Projectile", "type": "skill", "system": { "value": 3, "category": "Perception", "specialization": "" } }, + { "name": "Listen", "type": "skill", "system": { "value": 5, "category": "Perception", "specialization": "" } }, + { "name": "Observe", "type": "skill", "system": { "value": 5, "category": "Perception", "specialization": "" } }, + { "name": "Scent", "type": "skill", "system": { "value": 0, "category": "Perception", "specialization": "" } }, + { "name": "Search", "type": "skill", "system": { "value": 5, "category": "Perception", "specialization": "" } }, + { "name": "Track", "type": "skill", "system": { "value": 5, "category": "Perception", "specialization": "" } }, + + { "name": "Aquatic Vehicle", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } }, + { "name": "Atmospheric Craft", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } }, + { "name": "Ground Vehicle", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } }, + { "name": "Hyperdrive", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } }, + { "name": "Personal Flyer", "type": "skill", "system": { "value": 15, "category": "Technical", "specialization": "" } }, + { "name": "Reaction Drive", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } }, + { "name": "Reactionless Drive", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } }, + { "name": "Repair", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } }, + { "name": "Ringworld", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } }, + { "name": "Weapons System", "type": "skill", "system": { "value": 0, "category": "Technical", "specialization": "" } } +] diff --git a/seeds/example-explorer.json b/seeds/example-explorer.json new file mode 100644 index 0000000..f435c5c --- /dev/null +++ b/seeds/example-explorer.json @@ -0,0 +1,87 @@ +{ + "name": "Sample Explorer", + "type": "explorer", + "system": { + "characteristics": { + "str": 12, + "mas": 11, + "con": 13, + "int": 12, + "pow": 11, + "dex": 14, + "app": 10, + "edu": 12, + "age": 28 + }, + "derived": { + "actionRanking": 2, + "move": 3 + }, + "generalHp": { + "value": 24, + "max": 24 + }, + "resources": { + "energy": { + "value": 12, + "max": 12 + } + } + }, + "items": [ + { + "name": "Handgun, Energy", + "type": "skill", + "system": { + "value": 52, + "category": "Perception", + "specialization": "" + } + }, + { + "name": "Observe", + "type": "skill", + "system": { + "value": 45, + "category": "Perception", + "specialization": "" + } + }, + { + "name": "Laser Pistol", + "type": "weapon", + "system": { + "linkedSkill": "Handgun, Energy", + "linkedSkillId": "", + "attackType": "ranged", + "damageFormula": "1d10+1", + "range": { + "close": "Short", + "medium": "Medium", + "long": "Long" + }, + "energyUsedPerAttack": 2, + "ammoUsedPerAttack": 0, + "powerSource": "powerPack", + "powerPackId": "", + "notes": "Link to Power Pack A after import" + } + }, + { + "name": "Power Pack A", + "type": "gear", + "system": { + "weight": 1, + "cost": 100, + "energyUse": 0, + "powerSupplyNotes": "Standard power pack", + "isPowerPack": true, + "energy": { + "value": 20, + "max": 20 + }, + "notes": "" + } + } + ] +} diff --git a/seeds/example-items.json b/seeds/example-items.json new file mode 100644 index 0000000..ae06a67 --- /dev/null +++ b/seeds/example-items.json @@ -0,0 +1,74 @@ +[ + { + "name": "Handgun, Energy", + "type": "skill", + "system": { + "value": 45, + "category": "Perception", + "specialization": "" + } + }, + { + "name": "Laser Pistol", + "type": "weapon", + "system": { + "linkedSkill": "Handgun, Energy", + "linkedSkillId": "", + "attackType": "ranged", + "damageFormula": "1d10+1", + "range": { + "close": "Short", + "medium": "Medium", + "long": "Long" + }, + "energyUsedPerAttack": 2, + "ammoUsedPerAttack": 0, + "powerSource": "powerPack", + "powerPackId": "", + "notes": "Sample energy sidearm" + } + }, + { + "name": "Soft Combat Vest", + "type": "armor", + "system": { + "equipped": true, + "value": 2, + "coverage": { + "rLeg": false, + "lLeg": false, + "abdomen": true, + "chest": true, + "rArm": false, + "lArm": false, + "head": false + }, + "perLocation": { + "rLeg": 0, + "lLeg": 0, + "abdomen": 0, + "chest": 0, + "rArm": 0, + "lArm": 0, + "head": 0 + }, + "notes": "Sample armor" + } + }, + { + "name": "Power Pack A", + "type": "gear", + "system": { + "weight": 1, + "cost": 100, + "energyUse": 0, + "powerSupplyNotes": "Standard power pack", + "isPowerPack": true, + "energy": { + "value": 20, + "max": 20 + }, + "notes": "Sample pack for energy weapons" + } + } +] diff --git a/src/module/actor/RingworldActor.ts b/src/module/actor/RingworldActor.ts new file mode 100644 index 0000000..4d0e845 --- /dev/null +++ b/src/module/actor/RingworldActor.ts @@ -0,0 +1,335 @@ +import { recomputeHitPointMaximums, rollSkillForActor, rollStandaloneHitLocation, useWeaponWorkflow } from "../mechanics"; +import { normalizeSkillName, RINGWORLD_BASE_SKILLS } from "../data-base-skills"; +import { randomizeExplorerActor, rollCharacteristicsForActor } from "../character-tools"; +import { skillIconPath } from "../skill-icons"; + +type DerivedCheckKey = "health" | "reasoning" | "luck" | "dodge"; +type SpeciesModifierKey = "str" | "mas" | "con" | "int" | "pow" | "dex" | "app" | "edu" | "age"; +const SPECIES_MODIFIER_KEYS: SpeciesModifierKey[] = ["str", "mas", "con", "int", "pow", "dex", "app", "edu", "age"]; + +function num(value: unknown, fallback = 0): number { + const n = Number(value); + return Number.isFinite(n) ? n : fallback; +} + +function hasValue(value: unknown): boolean { + return value !== null && value !== undefined && String(value).trim() !== ""; +} + +function clampCharacteristic(value: number): number { + return Math.max(1, Math.min(99, Math.floor(value))); +} + +function computeDamageMod(str: number, mas: number): string { + const total = str + mas; + if (total <= 12) return "-1d6"; + if (total <= 24) return "-1d4"; + if (total <= 32) return "0"; + if (total <= 40) return "+1d4"; + if (total <= 56) return "+1d6"; + if (total <= 72) return "+2d6"; + return "+3d6"; +} + +function computeDerivedRolls(characteristics: any): Record { + const con = num(characteristics?.con, 10); + const int = num(characteristics?.int, 10); + const pow = num(characteristics?.pow, 10); + const dex = num(characteristics?.dex, 10); + + return { + health: Math.max(0, Math.min(99, con * 5)), + reasoning: Math.max(0, Math.min(99, int * 5)), + luck: Math.max(0, Math.min(99, pow * 5)), + dodge: Math.max(0, Math.min(99, dex * 5)) + }; +} + +function computeRootMaxes(characteristics: any): Record { + const str = num(characteristics?.str, 10); + const dex = num(characteristics?.dex, 10); + const app = num(characteristics?.app, 10); + const edu = num(characteristics?.edu, 10); + const int = num(characteristics?.int, 10); + const pow = num(characteristics?.pow, 10); + const con = num(characteristics?.con, 10); + + return { + agility: Math.max(0, str + dex), + communication: Math.max(0, int + app), + knowledge: Math.max(0, int + edu), + perception: Math.max(0, pow + con), + technical: Math.max(0, dex + int) + }; +} + +function computeActionRanking(dex: number): number { + if (dex <= 4) return 7; + if (dex <= 8) return 6; + if (dex <= 12) return 5; + if (dex <= 16) return 4; + if (dex <= 20) return 3; + if (dex <= 24) return 2; + return 1; +} + +function gravityLoadMultiplier(gravity: string): number { + if (gravity === "light") return 0.5; + if (gravity === "heavy") return 2; + return 1; +} + +function contributesToEncumbrance(item: any): boolean { + if (item.type !== "weapon" && item.type !== "armor" && item.type !== "gear") return false; + return item.system?.stowed !== true; +} + +function itemWeightKg(item: any): number { + return Math.max(0, num(item.system?.weight, 0)); +} + +function computeEncumbrance(system: any, items: any[]): { + rawLoadKg: number; + effectiveLoadKg: number; + limitKg: number; + overLimitKg: number; + actionRankingPenalty: number; + overLimit: boolean; + currentMove: number; + currentRun: number; + gravityMultiplier: number; +} { + const gravity = String(system?.profile?.gravity || "normal"); + const gravityMultiplier = gravityLoadMultiplier(gravity); + const equippedLoadKg = items + .filter((item) => item.type === "weapon" || item.type === "armor" || item.type === "gear") + .filter(contributesToEncumbrance) + .reduce((sum, item) => sum + itemWeightKg(item), 0); + + const rawLoadKg = Math.max(0, equippedLoadKg); + const effectiveLoadKg = rawLoadKg * gravityMultiplier; + const limitKg = Math.max(0, num(system?.characteristics?.str, 0)); + const overLimitKg = Math.max(0, effectiveLoadKg - limitKg); + const actionRankingPenalty = overLimitKg > 0 ? Math.ceil(overLimitKg / 10) : 0; + const overLimit = overLimitKg > 0; + + const baseMove = Math.max(1, num(system?.derived?.move, 3)); + const baseRun = Math.max(1, num(system?.derived?.run, baseMove * 2)); + const currentMove = overLimit ? 2 : baseMove; + const currentRun = overLimit ? 4 : baseRun; + + return { + rawLoadKg, + effectiveLoadKg, + limitKg, + overLimitKg, + actionRankingPenalty, + overLimit, + currentMove, + currentRun, + gravityMultiplier + }; +} + +export class RingworldActor extends Actor { + prepareDerivedData(): void { + super.prepareDerivedData(); + recomputeHitPointMaximums(this as any); + + if (this.type !== "explorer" && this.type !== "creature") return; + + const system = (this as any).system ?? {}; + const characteristics = system.characteristics ?? {}; + const profile = system.profile ?? {}; + + profile.chronologicalAge = num(profile.chronologicalAge, num(characteristics.age, 25)); + profile.physiologicalAge = num(profile.physiologicalAge, profile.chronologicalAge); + system.profile = profile; + + system.combat = system.combat ?? {}; + if (!hasValue(system.combat.damageMod)) { + system.combat.damageMod = computeDamageMod(num(characteristics.str, 10), num(characteristics.mas, 10)); + } + + system.derivedRolls = system.derivedRolls ?? {}; + const derivedRollDefaults = computeDerivedRolls(characteristics); + for (const key of Object.keys(derivedRollDefaults) as DerivedCheckKey[]) { + system.derivedRolls[key] = num(system.derivedRolls[key], derivedRollDefaults[key]); + } + + system.skillRootMax = system.skillRootMax ?? {}; + const rootMaxDefaults = computeRootMaxes(characteristics); + for (const [key, value] of Object.entries(rootMaxDefaults)) { + system.skillRootMax[key] = value; + } + + system.derived = system.derived ?? {}; + system.derived.actionRanking = computeActionRanking(num(characteristics.dex, 10)); + system.derived.move = num(system.derived.move, 3); + system.derived.run = num(system.derived.run, Math.max(1, num(system.derived.move, 3) * 2)); + + system.encumbrance = system.encumbrance ?? {}; + const encumbrance = computeEncumbrance(system, this.items as any[]); + system.encumbrance.rawLoadKg = encumbrance.rawLoadKg; + system.encumbrance.effectiveLoadKg = encumbrance.effectiveLoadKg; + system.encumbrance.limitKg = encumbrance.limitKg; + system.encumbrance.overLimitKg = encumbrance.overLimitKg; + system.encumbrance.actionRankingPenalty = encumbrance.actionRankingPenalty; + system.encumbrance.overLimit = encumbrance.overLimit; + system.encumbrance.gravityMultiplier = encumbrance.gravityMultiplier; + system.derived.currentMove = encumbrance.currentMove; + system.derived.currentRun = encumbrance.currentRun; + + system.finance = system.finance ?? { money: 0, inBank: 0 }; + system.finance.money = num(system.finance.money, 0); + system.finance.inBank = num(system.finance.inBank, 0); + + system.notes = system.notes ?? { defects: "", habits: "" }; + if (typeof system.sheetToolsUnlocked !== "boolean") { + system.sheetToolsUnlocked = false; + } + system.characterBuilder = system.characterBuilder ?? { + pursuits: [], + allocations: {}, + baseBySkillId: {} + }; + system.unconsciousLevel = num(system.unconsciousLevel, 0); + } + + get skills(): any[] { + return this.items.filter((item) => item.type === "skill"); + } + + get weapons(): any[] { + return this.items.filter((item) => item.type === "weapon"); + } + + get armors(): any[] { + return this.items.filter((item) => item.type === "armor"); + } + + get gears(): any[] { + return this.items.filter((item) => item.type === "gear"); + } + + async rollSkillById(skillId: string): Promise { + const skill = this.items.get(skillId); + if (!skill || skill.type !== "skill") return; + await rollSkillForActor(this, skill.name, Number((skill as any).system.value ?? 0)); + } + + async rollHitLocation(): Promise { + await rollStandaloneHitLocation(this); + } + + async useWeaponById(weaponId: string): Promise { + const weapon = this.items.get(weaponId); + if (!weapon || weapon.type !== "weapon") return; + await useWeaponWorkflow(this, weapon); + } + + async ensureBaseSkills(): Promise { + if (this.type !== "explorer" && this.type !== "creature") return 0; + + const existing = new Set( + this.items + .filter((item) => item.type === "skill") + .map((item) => normalizeSkillName(item.name)) + ); + + const toCreate = RINGWORLD_BASE_SKILLS + .filter((skill) => !existing.has(normalizeSkillName(skill.name))) + .map((skill) => ({ + name: skill.name, + type: "skill", + img: skillIconPath(skill.name), + system: { + value: skill.base, + category: skill.category, + specialization: skill.specialization ?? "" + } + })); + + if (!toCreate.length) return 0; + await this.createEmbeddedDocuments("Item", toCreate as any[]); + return toCreate.length; + } + + async rollCharacteristics(): Promise { + if (this.type !== "explorer") return; + await rollCharacteristicsForActor(this as any); + } + + async randomizeCharacter(): Promise { + if (this.type !== "explorer") return; + await randomizeExplorerActor(this as any); + } + + async applySpeciesItem(speciesData: any): Promise { + if (this.type !== "explorer" && this.type !== "creature") return; + + const speciesName = String(speciesData?.name || "").trim(); + const speciesSystem = speciesData?.system ?? {}; + const incomingMods = speciesSystem?.modifiers ?? {}; + const appliedState = (this as any).system?.speciesApplied ?? {}; + const previousMods = appliedState?.modifiers ?? {}; + + const currentCharacteristics = (this as any).system?.characteristics ?? {}; + const currentProfile = (this as any).system?.profile ?? {}; + const update: Record = {}; + const normalizedMods: Record = { + str: 0, + mas: 0, + con: 0, + int: 0, + pow: 0, + dex: 0, + app: 0, + edu: 0, + age: 0 + }; + + for (const key of SPECIES_MODIFIER_KEYS) { + const prev = num(previousMods?.[key], 0); + const next = num(incomingMods?.[key], 0); + normalizedMods[key] = next; + + const current = num(currentCharacteristics?.[key], key === "age" ? 25 : 10); + const base = current - prev; + update[`system.characteristics.${key}`] = clampCharacteristic(base + next); + } + + const agePrev = num(previousMods?.age, 0); + const ageNext = normalizedMods.age; + const chronoCurrent = num(currentProfile?.chronologicalAge, num(currentCharacteristics?.age, 25)); + const physCurrent = num(currentProfile?.physiologicalAge, chronoCurrent); + update["system.profile.chronologicalAge"] = clampCharacteristic((chronoCurrent - agePrev) + ageNext); + update["system.profile.physiologicalAge"] = clampCharacteristic((physCurrent - agePrev) + ageNext); + + if (speciesName) update["system.profile.species"] = speciesName; + if (speciesSystem?.gravity) update["system.profile.gravity"] = String(speciesSystem.gravity); + if (speciesSystem?.homeworld !== undefined) update["system.profile.homeworld"] = String(speciesSystem.homeworld || ""); + + update["system.speciesApplied"] = { + name: speciesName, + modifiers: normalizedMods + }; + + await this.update(update); + } + + async rollDerivedCheck(key: DerivedCheckKey): Promise { + if (this.type !== "explorer" && this.type !== "creature") return; + + const labels: Record = { + health: "Health Roll", + reasoning: "Reasoning Roll", + luck: "Luck Roll", + dodge: "Dodge Roll" + }; + + const defaults = computeDerivedRolls((this as any).system?.characteristics ?? {}); + const target = num((this as any).system?.derivedRolls?.[key], defaults[key]); + await rollSkillForActor(this, labels[key], target); + } +} diff --git a/src/module/character-tools.ts b/src/module/character-tools.ts new file mode 100644 index 0000000..1e6b7c7 --- /dev/null +++ b/src/module/character-tools.ts @@ -0,0 +1,178 @@ +import { speciesCatalogForGeneration, getPlayableSpeciesFromWorld } from "./world-catalog"; +import { evaluateRollWithDice } from "./utils/dice"; + +type GravityType = "light" | "normal" | "heavy"; + +interface CharacteristicSet { + str: number; + mas: number; + con: number; + int: number; + pow: number; + dex: number; + app: number; + edu: number; + age: number; +} + +function clampScore(value: number): number { + return Math.max(1, Math.min(99, Math.floor(value))); +} + +async function rollTotal(formula: string): Promise { + const roll = await evaluateRollWithDice(formula); + return Number(roll.total ?? 0); +} + +async function rollEdu(): Promise { + let edu = 10; + while (true) { + const die = await rollTotal("1d6"); + edu += die; + if (die <= 4) break; + } + return clampScore(edu); +} + +async function rollByGravity(gravity: GravityType): Promise { + const gravityFormulas = { + light: { str: "2d6+3", mas: "2d6+3", con: "2d6+6", dex: "2d6+9" }, + normal: { str: "2d6+6", mas: "2d6+6", con: "2d6+6", dex: "2d6+6" }, + heavy: { str: "2d6+9", mas: "2d6+9", con: "2d6+3", dex: "2d6+6" } + }[gravity]; + + return { + str: await rollTotal(gravityFormulas.str), + mas: await rollTotal(gravityFormulas.mas), + con: await rollTotal(gravityFormulas.con), + int: await rollTotal("2d6+6"), + pow: await rollTotal("2d6+6"), + dex: await rollTotal(gravityFormulas.dex), + app: await rollTotal("2d6+6"), + edu: await rollEdu(), + age: await rollTotal("1d20+16") + }; +} + +function randomPick(arr: T[]): T { + return arr[Math.floor(Math.random() * arr.length)]; +} + +function randomExplorerName(speciesName: string): string { + const starts = ["Ar", "Bel", "Cor", "Den", "Ela", "Far", "Gal", "Hel", "Ira", "Jun", "Kor", "Lys", "Mor", "Nyr", "Or", "Pra", "Qua", "Rin", "Sol", "Tor", "Uri", "Val", "Wen", "Xel", "Yor", "Zen"]; + const ends = ["an", "el", "is", "or", "us", "a", "en", "ion", "eth", "al", "ir", "os"]; + return `${randomPick(starts)}${randomPick(ends)} (${speciesName})`; +} + +function applySpeciesModifiers(base: CharacteristicSet, speciesSystem: any): CharacteristicSet { + const mods = speciesSystem?.modifiers ?? {}; + return { + str: clampScore(base.str + Number(mods.str || 0)), + mas: clampScore(base.mas + Number(mods.mas || 0)), + con: clampScore(base.con + Number(mods.con || 0)), + int: clampScore(base.int + Number(mods.int || 0)), + pow: clampScore(base.pow + Number(mods.pow || 0)), + dex: clampScore(base.dex + Number(mods.dex || 0)), + app: clampScore(base.app + Number(mods.app || 0)), + edu: clampScore(base.edu + Number(mods.edu || 0)), + age: clampScore(base.age + Number(mods.age || 0)) + }; +} + +export async function rollCharacteristicsForActor(actor: any): Promise { + const gravity = String(actor.system?.profile?.gravity || "normal") as GravityType; + const gravityType: GravityType = gravity === "light" || gravity === "heavy" ? gravity : "normal"; + + const currentSpeciesName = String(actor.system?.profile?.species || ""); + const worldSpecies = getPlayableSpeciesFromWorld(); + const speciesItem = worldSpecies.find((item: any) => item.name === currentSpeciesName); + const builtIn = speciesCatalogForGeneration().find((entry) => entry.name === currentSpeciesName); + const speciesSystem = speciesItem?.system ?? builtIn ?? null; + + const rolled = await rollByGravity(gravityType); + const finalValues = applySpeciesModifiers(rolled, speciesSystem); + + await actor.update({ + "system.characteristics.str": finalValues.str, + "system.characteristics.mas": finalValues.mas, + "system.characteristics.con": finalValues.con, + "system.characteristics.int": finalValues.int, + "system.characteristics.pow": finalValues.pow, + "system.characteristics.dex": finalValues.dex, + "system.characteristics.app": finalValues.app, + "system.characteristics.edu": finalValues.edu, + "system.characteristics.age": finalValues.age, + "system.profile.chronologicalAge": finalValues.age, + "system.profile.physiologicalAge": finalValues.age + }); + + const ownLanguage = actor.items.find((item: any) => item.type === "skill" && item.name.toLowerCase() === "own language"); + if (ownLanguage) { + await actor.updateEmbeddedDocuments("Item", [{ + _id: ownLanguage.id, + "system.value": Math.min(99, finalValues.int * 5) + }]); + } +} + +export async function applyStarterKit(actor: any): Promise { + const starterNames = [ + ["weapon", "Laser Pistol"], + ["weapon", "Variable Sword"], + ["armor", "Soft Combat Vest"], + ["gear", "Power Pack R20"], + ["gear", "Medkit"], + ["gear", "Survey Scanner"] + ]; + + const worldItems = game.items; + const actorExisting = new Set(actor.items.map((item: any) => `${item.type}:${item.name.toLowerCase()}`)); + const createPayload: any[] = []; + + for (const [type, name] of starterNames) { + if (actorExisting.has(`${type}:${String(name).toLowerCase()}`)) continue; + const worldItem = worldItems.find((item: any) => item.type === type && item.name === name); + if (!worldItem) continue; + createPayload.push(worldItem.toObject()); + } + + if (createPayload.length) { + await actor.createEmbeddedDocuments("Item", createPayload); + } +} + +export async function randomizeExplorerActor(actor: any): Promise { + const worldSpecies = getPlayableSpeciesFromWorld(); + const builtInSpecies = speciesCatalogForGeneration(); + + const pickedWorldSpecies = worldSpecies.length ? randomPick(worldSpecies) : null; + const pickedBuiltInSpecies = !pickedWorldSpecies ? randomPick(builtInSpecies) : null; + + const speciesChoice = pickedWorldSpecies?.system ?? pickedBuiltInSpecies; + const speciesName = pickedWorldSpecies?.name ?? pickedBuiltInSpecies?.name ?? "Human"; + + const gravity = String(speciesChoice.gravity || "normal") as GravityType; + const homeworld = String(speciesChoice.homeworld || "Ringworld"); + + await actor.update({ + name: randomExplorerName(speciesName), + "system.profile.species": speciesName, + "system.profile.gravity": gravity, + "system.profile.homeworld": homeworld + }); + + await rollCharacteristicsForActor(actor); + await actor.ensureBaseSkills?.(); + await applyStarterKit(actor); +} + +export async function createRandomExplorer(): Promise { + const ActorClass = getDocumentClass("Actor"); + const actor = await ActorClass.create({ + name: "Random Explorer", + type: "explorer" + }); + + await randomizeExplorerActor(actor); + return actor; +} diff --git a/src/module/chat.ts b/src/module/chat.ts new file mode 100644 index 0000000..0f8bc7e --- /dev/null +++ b/src/module/chat.ts @@ -0,0 +1,77 @@ +import { SYSTEM_ID } from "./constants"; +import { DEFAULT_SKILL_ICON } from "./skill-icons"; + +interface CreateChatOptions { + actor?: any; + content: string; +} + +async function createChatMessage(options: CreateChatOptions): Promise { + const speaker = options.actor ? ChatMessage.getSpeaker({ actor: options.actor }) : ChatMessage.getSpeaker(); + await ChatMessage.create({ + user: game.user?.id, + speaker, + content: options.content + }); +} + +export async function postSkillRollCard(data: { + actor: any; + actorName: string; + actorImg?: string; + skillName: string; + skillImg?: string; + roll: number; + target: number; + outcomeLabel: string; + isSuccess: boolean; + isSpecial: boolean; + isFumble: boolean; +}): Promise { + const payload = { + ...data, + actorImg: data.actorImg || data.actor?.img || "icons/svg/mystery-man.svg", + skillImg: data.skillImg || DEFAULT_SKILL_ICON + }; + const content = await renderTemplate(`systems/${SYSTEM_ID}/templates/chat/skill-roll-card.hbs`, payload); + await createChatMessage({ actor: data.actor, content }); +} + +export async function postAttackCard(data: { + actor: any; + actorName: string; + actorImg?: string; + targetName: string; + hasTarget?: boolean; + weaponName: string; + weaponImg?: string; + skillName: string; + skillImg?: string; + attackRoll: number; + attackTarget: number; + attackOutcomeLabel: string; + isSuccess: boolean; + isSpecial: boolean; + isFumble: boolean; + hitLocationLabel: string; + hitLocationRoll: number | null; + rawDamage: number; + armor: number; + finalDamage: number; + remainingLocationHp: number; + remainingGeneralHp: number; + energySourceLabel: string; + energyCost: number; + energyRemaining: number; + notes: string; +}): Promise { + const payload = { + ...data, + actorImg: data.actorImg || data.actor?.img || "icons/svg/mystery-man.svg", + weaponImg: data.weaponImg || "icons/svg/sword.svg", + skillImg: data.skillImg || DEFAULT_SKILL_ICON, + hasTarget: data.hasTarget ?? true + }; + const content = await renderTemplate(`systems/${SYSTEM_ID}/templates/chat/attack-card.hbs`, payload); + await createChatMessage({ actor: data.actor, content }); +} diff --git a/src/module/constants.ts b/src/module/constants.ts new file mode 100644 index 0000000..e70780e --- /dev/null +++ b/src/module/constants.ts @@ -0,0 +1,37 @@ +export type HitLocationSlug = "rLeg" | "lLeg" | "abdomen" | "chest" | "rArm" | "lArm" | "head"; + +export interface HitLocationEntry { + slug: HitLocationSlug; + label: string; + min: number; + max: number; +} + +export const SYSTEM_ID = "ringworld"; + +export const HIT_LOCATION_TABLE: HitLocationEntry[] = [ + { slug: "rLeg", label: "Right Leg", min: 1, max: 3 }, + { slug: "lLeg", label: "Left Leg", min: 4, max: 6 }, + { slug: "abdomen", label: "Abdomen", min: 7, max: 10 }, + { slug: "chest", label: "Chest", min: 11, max: 15 }, + { slug: "rArm", label: "Right Arm", min: 16, max: 17 }, + { slug: "lArm", label: "Left Arm", min: 18, max: 19 }, + { slug: "head", label: "Head", min: 20, max: 20 } +]; + +export const HIT_LOCATION_ORDER: HitLocationSlug[] = [ + "rLeg", + "lLeg", + "abdomen", + "chest", + "rArm", + "lArm", + "head" +]; + +export const LIMB_LOCATIONS: HitLocationSlug[] = ["rArm", "lArm", "rLeg", "lLeg"]; +export const LEG_LOCATIONS: HitLocationSlug[] = ["rLeg", "lLeg"]; + +export function isLimb(slug: HitLocationSlug): boolean { + return LIMB_LOCATIONS.includes(slug); +} diff --git a/src/module/data-base-skills.ts b/src/module/data-base-skills.ts new file mode 100644 index 0000000..08724b3 --- /dev/null +++ b/src/module/data-base-skills.ts @@ -0,0 +1,85 @@ +export interface BaseSkillDef { + name: string; + category: "Agility" | "Communication" | "Knowledge" | "Perception" | "Technical"; + base: number; + specialization?: string; +} + +export const BASE_SKILL_CATEGORIES: Array = [ + "Agility", + "Communication", + "Knowledge", + "Perception", + "Technical" +]; + +export const RINGWORLD_BASE_SKILLS: BaseSkillDef[] = [ + { name: "Archaic Melee Weapon", category: "Agility", base: 5 }, + { name: "Archaic Ranged Weapons", category: "Agility", base: 3 }, + { name: "Athletics", category: "Agility", base: 15 }, + { name: "Hide", category: "Agility", base: 10 }, + { name: "Sneak", category: "Agility", base: 5 }, + { name: "Unarmed Combat", category: "Agility", base: 0 }, + { name: "V. Sword, F. Laser", category: "Agility", base: 15 }, + + { name: "Bargain", category: "Communication", base: 10 }, + { name: "Debate", category: "Communication", base: 5 }, + { name: "Fast Talk", category: "Communication", base: 10 }, + { name: "Fine Arts", category: "Communication", base: 5 }, + { name: "Musicianship", category: "Communication", base: 5 }, + { name: "Orate", category: "Communication", base: 5 }, + { name: "Own Language", category: "Communication", base: 0, specialization: "INTx5 base" }, + { name: "Perform", category: "Communication", base: 5 }, + { name: "Psychology", category: "Communication", base: 0 }, + + { name: "Anthropology", category: "Knowledge", base: 0 }, + { name: "Astronomy", category: "Knowledge", base: 0 }, + { name: "Biology", category: "Knowledge", base: 0 }, + { name: "Botany", category: "Knowledge", base: 0 }, + { name: "Chemistry", category: "Knowledge", base: 0 }, + { name: "Computers", category: "Knowledge", base: 0 }, + { name: "Emergency Treatment", category: "Knowledge", base: 1 }, + { name: "Engineering", category: "Knowledge", base: 0 }, + { name: "Farming", category: "Knowledge", base: 0 }, + { name: "History", category: "Knowledge", base: 5 }, + { name: "Law", category: "Knowledge", base: 0 }, + { name: "Mathematics", category: "Knowledge", base: 0 }, + { name: "Physics", category: "Knowledge", base: 0 }, + { name: "Planetology", category: "Knowledge", base: 0 }, + { name: "Second Languages", category: "Knowledge", base: 0 }, + { name: "Strategy", category: "Knowledge", base: 0 }, + { name: "Theology", category: "Knowledge", base: 0 }, + { name: "Zoology", category: "Knowledge", base: 0 }, + + { name: "Handgun, Energy", category: "Perception", base: 5 }, + { name: "Handgun, Projectile", category: "Perception", base: 3 }, + { name: "Heavy Weapon, Energy", category: "Perception", base: 5 }, + { name: "Heavy Weapon, Projectile", category: "Perception", base: 3 }, + { name: "Listen", category: "Perception", base: 5 }, + { name: "Observe", category: "Perception", base: 5 }, + { name: "Scent", category: "Perception", base: 0 }, + { name: "Search", category: "Perception", base: 5 }, + { name: "Track", category: "Perception", base: 5 }, + + { name: "Aquatic Vehicle", category: "Technical", base: 0 }, + { name: "Atmospheric Craft", category: "Technical", base: 0 }, + { name: "Ground Vehicle", category: "Technical", base: 0 }, + { name: "Hyperdrive", category: "Technical", base: 0 }, + { name: "Personal Flyer", category: "Technical", base: 15 }, + { name: "Reaction Drive", category: "Technical", base: 0 }, + { name: "Reactionless Drive", category: "Technical", base: 0 }, + { name: "Repair", category: "Technical", base: 0 }, + { name: "Ringworld", category: "Technical", base: 0 }, + { name: "Weapons System", category: "Technical", base: 0 } +]; + +export function normalizeSkillName(name: string): string { + return String(name || "") + .toLowerCase() + .replace(/[^a-z0-9]+/g, "") + .trim(); +} + +export function baseSkillNameSet(): Set { + return new Set(RINGWORLD_BASE_SKILLS.map((skill) => normalizeSkillName(skill.name))); +} diff --git a/src/module/hit-location-schemas.ts b/src/module/hit-location-schemas.ts new file mode 100644 index 0000000..3ee0af8 --- /dev/null +++ b/src/module/hit-location-schemas.ts @@ -0,0 +1,191 @@ +import { HIT_LOCATION_ORDER, HIT_LOCATION_TABLE, type HitLocationEntry, type HitLocationSlug } from "./constants"; + +export type HitLocationSchemaId = "humanoid" | "dolphin" | "puppeteer" | "trinoc" | "bandersnatchi" | "kzinti"; + +export interface HitLocationSchemaEntry extends HitLocationEntry { + hpFactor: number; +} + +export interface HitLocationSchema { + id: HitLocationSchemaId; + label: string; + bodyOrder: HitLocationSlug[]; + entries: HitLocationSchemaEntry[]; +} + +function normalize(value: unknown): string { + return String(value ?? "") + .toLowerCase() + .replace(/[^a-z0-9]+/g, ""); +} + +function withFactors(entries: HitLocationEntry[], hpFactors?: Partial>): HitLocationSchemaEntry[] { + return entries.map((entry) => ({ + ...entry, + hpFactor: hpFactors?.[entry.slug] ?? (entry.slug === "chest" ? 0.35 : (entry.slug === "rArm" || entry.slug === "lArm") ? 0.25 : 0.3) + })); +} + +const HUMANOID_SCHEMA: HitLocationSchema = { + id: "humanoid", + label: "Humanoid", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: withFactors(HIT_LOCATION_TABLE) +}; + +const DOLPHIN_SCHEMA: HitLocationSchema = { + id: "dolphin", + label: "Dolphin", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: withFactors([ + { slug: "rLeg", label: "Right Fluke", min: 1, max: 3 }, + { slug: "lLeg", label: "Left Fluke", min: 4, max: 6 }, + { slug: "abdomen", label: "Lower Body", min: 7, max: 9 }, + { slug: "chest", label: "Main Body", min: 10, max: 15 }, + { slug: "rArm", label: "Right Fin", min: 16, max: 17 }, + { slug: "lArm", label: "Left Fin", min: 18, max: 19 }, + { slug: "head", label: "Head", min: 20, max: 20 } + ], { + head: 0.3, + rArm: 0.2, + lArm: 0.2, + chest: 0.4, + abdomen: 0.35, + rLeg: 0.25, + lLeg: 0.25 + }) +}; + +const PUPPETEER_SCHEMA: HitLocationSchema = { + id: "puppeteer", + label: "Puppeteer", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: withFactors([ + { slug: "rLeg", label: "Right Rear Leg", min: 1, max: 3 }, + { slug: "lLeg", label: "Left Rear Leg", min: 4, max: 6 }, + { slug: "rArm", label: "Right Foreleg", min: 7, max: 9 }, + { slug: "lArm", label: "Left Foreleg", min: 10, max: 12 }, + { slug: "chest", label: "Fore Body", min: 13, max: 15 }, + { slug: "abdomen", label: "Hind Body", min: 16, max: 18 }, + { slug: "head", label: "Twin Heads", min: 19, max: 20 } + ], { + head: 0.32, + chest: 0.35, + abdomen: 0.35, + rArm: 0.28, + lArm: 0.28, + rLeg: 0.28, + lLeg: 0.28 + }) +}; + +const TRINOC_SCHEMA: HitLocationSchema = { + id: "trinoc", + label: "Trinoc", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: withFactors([ + { slug: "rArm", label: "Right Wing/Arm", min: 1, max: 3 }, + { slug: "lArm", label: "Left Wing/Arm", min: 4, max: 6 }, + { slug: "chest", label: "Upper Torso", min: 7, max: 11 }, + { slug: "abdomen", label: "Lower Torso", min: 12, max: 14 }, + { slug: "rLeg", label: "Right Leg", min: 15, max: 17 }, + { slug: "lLeg", label: "Left Leg", min: 18, max: 19 }, + { slug: "head", label: "Head", min: 20, max: 20 } + ], { + head: 0.3, + rArm: 0.27, + lArm: 0.27, + chest: 0.36, + abdomen: 0.3, + rLeg: 0.27, + lLeg: 0.27 + }) +}; + +const BANDERSNATCHI_SCHEMA: HitLocationSchema = { + id: "bandersnatchi", + label: "Bandersnatchi", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: withFactors([ + { slug: "rLeg", label: "Right Rear Mass", min: 1, max: 2 }, + { slug: "lLeg", label: "Left Rear Mass", min: 3, max: 4 }, + { slug: "abdomen", label: "Lower Body Mass", min: 5, max: 11 }, + { slug: "chest", label: "Upper Body Mass", min: 12, max: 17 }, + { slug: "rArm", label: "Right Manipulator", min: 18, max: 18 }, + { slug: "lArm", label: "Left Manipulator", min: 19, max: 19 }, + { slug: "head", label: "Head Cluster", min: 20, max: 20 } + ], { + head: 0.28, + chest: 0.42, + abdomen: 0.42, + rArm: 0.2, + lArm: 0.2, + rLeg: 0.24, + lLeg: 0.24 + }) +}; + +const KZINTI_SCHEMA: HitLocationSchema = { + id: "kzinti", + label: "Kzinti", + bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"], + entries: withFactors([ + { slug: "rLeg", label: "Right Hind Leg", min: 1, max: 3 }, + { slug: "lLeg", label: "Left Hind Leg", min: 4, max: 6 }, + { slug: "abdomen", label: "Abdomen", min: 7, max: 10 }, + { slug: "chest", label: "Chest", min: 11, max: 15 }, + { slug: "rArm", label: "Right Foreleg", min: 16, max: 17 }, + { slug: "lArm", label: "Left Foreleg", min: 18, max: 19 }, + { slug: "head", label: "Head", min: 20, max: 20 } + ]) +}; + +export const HIT_LOCATION_SCHEMAS: Record = { + humanoid: HUMANOID_SCHEMA, + dolphin: DOLPHIN_SCHEMA, + puppeteer: PUPPETEER_SCHEMA, + trinoc: TRINOC_SCHEMA, + bandersnatchi: BANDERSNATCHI_SCHEMA, + kzinti: KZINTI_SCHEMA +}; + +export function listHitLocationSchemas(): HitLocationSchema[] { + return Object.values(HIT_LOCATION_SCHEMAS); +} + +export function inferHitLocationSchemaId(speciesValue: unknown): HitLocationSchemaId { + const species = normalize(speciesValue); + if (!species) return "humanoid"; + if (species.includes("dolphin")) return "dolphin"; + if (species.includes("puppeteer")) return "puppeteer"; + if (species.includes("trinoc")) return "trinoc"; + if (species.includes("bandersnatch")) return "bandersnatchi"; + if (species.includes("kzinti")) return "kzinti"; + return "humanoid"; +} + +export function getHitLocationSchemaById(schemaId: unknown): HitLocationSchema { + const key = normalize(schemaId) as HitLocationSchemaId; + return HIT_LOCATION_SCHEMAS[key] ?? HUMANOID_SCHEMA; +} + +export function getActorHitLocationSchema(actor: any): HitLocationSchema { + const mode = String(actor?.system?.hitLocationSchema || "auto").trim().toLowerCase(); + if (mode && mode !== "auto") { + return getHitLocationSchemaById(mode); + } + const species = actor?.system?.profile?.species ?? ""; + return getHitLocationSchemaById(inferHitLocationSchemaId(species)); +} + +export function getSchemaLocationEntry(schema: HitLocationSchema, slug: HitLocationSlug): HitLocationSchemaEntry { + return schema.entries.find((entry) => entry.slug === slug) ?? withFactors(HIT_LOCATION_TABLE).find((entry) => entry.slug === slug)!; +} + +export function getSchemaOrder(schema: HitLocationSchema): HitLocationSlug[] { + const set = new Set(schema.entries.map((entry) => entry.slug)); + const ordered = schema.bodyOrder.filter((slug) => set.has(slug)); + if (ordered.length === schema.entries.length) return ordered; + return [...HIT_LOCATION_ORDER]; +} + diff --git a/src/module/impulse-tracker.ts b/src/module/impulse-tracker.ts new file mode 100644 index 0000000..d8b86a0 --- /dev/null +++ b/src/module/impulse-tracker.ts @@ -0,0 +1,756 @@ +import { SYSTEM_ID } from "./constants"; + +type MajorActionKind = "draw" | "aim" | "melee" | "stand" | "perception" | "unarmed"; +type MinorActionKind = "fire" | "fall" | "rise" | "minorCustom"; +type ActionKind = MajorActionKind | MinorActionKind; + +interface ActionEvent { + id: string; + actorId: string | null; + combatantId: string; + kind: ActionKind; + major: boolean; + startImpulse: number; + completeImpulse: number; + itemId?: string; + skillId?: string; + targetTokenId?: string; +} + +interface ImpulseState { + currentImpulse: number; + queue: ActionEvent[]; +} + +const IMPULSE_FLAG = "impulseState"; +const IMPULSE_HUD_POS_KEY = `${SYSTEM_ID}.impulseHudPos`; +const MAJOR_ACTION_OPTIONS: { value: MajorActionKind; label: string }[] = [ + { value: "draw", label: "Draw / Stow Weapon" }, + { value: "aim", label: "Aim Ranged Weapon" }, + { value: "melee", label: "Melee Attack" }, + { value: "stand", label: "Stand / Recover" }, + { value: "perception", label: "Perception Skill" }, + { value: "unarmed", label: "Unarmed Combat" } +]; +const MINOR_ACTION_OPTIONS: { value: MinorActionKind; label: string }[] = [ + { value: "fire", label: "Fire Ranged Weapon" }, + { value: "fall", label: "Fall" }, + { value: "rise", label: "Rise (Kneeling to Stand/Run)" }, + { value: "minorCustom", label: "Other Minor Action" } +]; + +function num(value: unknown, fallback = 0): number { + const n = Number(value); + return Number.isFinite(n) ? n : fallback; +} + +function esc(value: unknown): string { + return String(value ?? "") + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll("\"", """); +} + +export function actionRankingFromDex(dex: number): number { + if (dex <= 4) return 7; + if (dex <= 8) return 6; + if (dex <= 12) return 5; + if (dex <= 16) return 4; + if (dex <= 20) return 3; + if (dex <= 24) return 2; + return 1; +} + +function actionKindLabel(kind: ActionKind): string { + switch (kind) { + case "draw": + return "Draw / Stow"; + case "aim": + return "Aim"; + case "melee": + return "Melee Attack"; + case "stand": + return "Stand / Recover"; + case "perception": + return "Perception"; + case "unarmed": + return "Unarmed Combat"; + case "fire": + return "Fire Ranged Weapon"; + case "fall": + return "Fall"; + case "rise": + return "Rise"; + case "minorCustom": + return "Minor Action"; + default: + return String(kind); + } +} + +function getDexForCombatant(combatant: any): number { + return Math.max(1, num(combatant?.actor?.system?.characteristics?.dex, 10)); +} + +function getCombatantActionRanking(combatant: any): number { + const fromFlag = num(combatant?.flags?.[SYSTEM_ID]?.actionRanking, 0); + if (fromFlag > 0) return fromFlag; + return actionRankingFromDex(getDexForCombatant(combatant)); +} + +function getEncumbranceArPenalty(combatant: any): number { + const fromActor = num(combatant?.actor?.system?.encumbrance?.actionRankingPenalty, 0); + return Math.max(0, Math.trunc(fromActor)); +} + +async function syncCombatantActionRanking(combatant: any): Promise { + const ranking = actionRankingFromDex(getDexForCombatant(combatant)); + const existing = num(combatant?.flags?.[SYSTEM_ID]?.actionRanking, 0); + if (existing !== ranking) { + await combatant.update({ [`flags.${SYSTEM_ID}.actionRanking`]: ranking }); + } + return ranking; +} + +function getImpulseState(combat: any): ImpulseState { + const raw = foundry.utils.deepClone(combat.getFlag(SYSTEM_ID, IMPULSE_FLAG) ?? {}); + const queue = Array.isArray(raw.queue) ? raw.queue : []; + return { + currentImpulse: Math.max(1, num(raw.currentImpulse, 1)), + queue + }; +} + +async function setImpulseState(combat: any, state: ImpulseState): Promise { + await combat.setFlag(SYSTEM_ID, IMPULSE_FLAG, { + currentImpulse: Math.max(1, num(state.currentImpulse, 1)), + queue: Array.isArray(state.queue) ? state.queue : [] + }); +} + +function sortQueueByCompletion(queue: ActionEvent[], combat: any): ActionEvent[] { + return [...queue].sort((a, b) => { + if (a.completeImpulse !== b.completeImpulse) return a.completeImpulse - b.completeImpulse; + const aDex = getDexForCombatant(combat.combatants.get(a.combatantId)); + const bDex = getDexForCombatant(combat.combatants.get(b.combatantId)); + return bDex - aDex; + }); +} + +function pruneQueueToCombatants(queue: ActionEvent[], combat: any): ActionEvent[] { + const validIds = new Set(combat.combatants.map((c: any) => c.id)); + return queue.filter((entry) => validIds.has(entry.combatantId)); +} + +function loadHudPosition(): { left: number; top: number } | null { + try { + const raw = localStorage.getItem(IMPULSE_HUD_POS_KEY); + if (!raw) return null; + const parsed = JSON.parse(raw); + const left = num(parsed?.left, NaN); + const top = num(parsed?.top, NaN); + if (!Number.isFinite(left) || !Number.isFinite(top)) return null; + return { left, top }; + } catch (_err) { + return null; + } +} + +function saveHudPosition(position: { left: number; top: number }): void { + try { + localStorage.setItem( + IMPULSE_HUD_POS_KEY, + JSON.stringify({ left: Math.round(position.left), top: Math.round(position.top) }) + ); + } catch (_err) { + // Ignore storage issues. + } +} + +function clearHudPosition(): void { + try { + localStorage.removeItem(IMPULSE_HUD_POS_KEY); + } catch (_err) { + // Ignore storage issues. + } +} + +function clampHudPosition(position: { left: number; top: number }, element: HTMLElement): { left: number; top: number } { + const margin = 8; + const width = element.offsetWidth || 300; + const height = element.offsetHeight || 220; + const maxLeft = Math.max(margin, window.innerWidth - width - margin); + const maxTop = Math.max(margin, window.innerHeight - height - margin); + return { + left: Math.min(Math.max(margin, position.left), maxLeft), + top: Math.min(Math.max(margin, position.top), maxTop) + }; +} + +function applyHudAbsolutePosition(hud: JQuery, position: { left: number; top: number }): void { + const el = hud.get(0) as HTMLElement | undefined; + if (!el) return; + const clamped = clampHudPosition(position, el); + hud.css({ + left: `${clamped.left}px`, + top: `${clamped.top}px`, + right: "auto", + bottom: "auto" + }); +} + +function makeHudDraggable(hud: JQuery): void { + const el = hud.get(0) as HTMLElement | undefined; + if (!el) return; + + const handle = hud.find(".rw-imp-mini-head"); + if (!handle.length) return; + + const saved = loadHudPosition(); + if (saved) { + applyHudAbsolutePosition(hud, saved); + } + + let dragging = false; + let startX = 0; + let startY = 0; + let startLeft = 0; + let startTop = 0; + + handle.on("mousedown", (event) => { + const mouseEvent = event as JQuery.MouseDownEvent; + if (mouseEvent.button !== 0) return; + if ((mouseEvent.target as HTMLElement)?.closest("button")) return; + mouseEvent.preventDefault(); + + const rect = el.getBoundingClientRect(); + startX = mouseEvent.clientX; + startY = mouseEvent.clientY; + startLeft = rect.left; + startTop = rect.top; + dragging = true; + + hud.addClass("dragging"); + hud.css({ left: `${startLeft}px`, top: `${startTop}px`, right: "auto", bottom: "auto" }); + + $(document).on("mousemove.ringworldImpulseHudDrag", (moveEvent) => { + if (!dragging) return; + const evt = moveEvent as JQuery.MouseMoveEvent; + const deltaX = evt.clientX - startX; + const deltaY = evt.clientY - startY; + applyHudAbsolutePosition(hud, { left: startLeft + deltaX, top: startTop + deltaY }); + }); + + $(document).one("mouseup.ringworldImpulseHudDrag", () => { + dragging = false; + hud.removeClass("dragging"); + $(document).off("mousemove.ringworldImpulseHudDrag"); + const latestRect = el.getBoundingClientRect(); + saveHudPosition({ left: latestRect.left, top: latestRect.top }); + }); + }); + + handle.on("dblclick", () => { + clearHudPosition(); + hud.css({ left: "", top: "", right: "", bottom: "" }); + }); +} + +function getQueueWithNames(combat: any, queue: ActionEvent[]): Array<{ + combatantId: string; + name: string; + label: string; + completeImpulse: number; + dex: number; +}> { + return sortQueueByCompletion(pruneQueueToCombatants(queue, combat), combat).map((entry) => { + const combatant = combat.combatants.get(entry.combatantId); + return { + combatantId: entry.combatantId, + name: String(combatant?.name || "Unknown"), + label: actionKindLabel(entry.kind), + completeImpulse: entry.completeImpulse, + dex: getDexForCombatant(combatant) + }; + }); +} + +function renderQueueRowsMarkup(rows: ReturnType, limit = 8): string { + if (!rows.length) return `
No actions queued.
`; + return rows.slice(0, limit).map((row) => ` +
+ ${esc(row.name)} + ${esc(row.label)} @ ${row.completeImpulse} +
+ `).join(""); +} + +function attachOverlayControls(root: JQuery, combat: any): void { + const disabled = game.user?.isGM ? false : true; + root.find(".rw-mini-next").prop("disabled", disabled).on("click", async (event) => { + event.preventDefault(); + await advanceImpulse(combat, "next"); + }); + root.find(".rw-mini-step").prop("disabled", disabled).on("click", async (event) => { + event.preventDefault(); + await advanceImpulse(combat, "step"); + }); + root.find(".rw-mini-reset").prop("disabled", disabled).on("click", async (event) => { + event.preventDefault(); + await resetImpulseSequence(combat); + }); +} + +function renderChatImpulseMarker(combat: any | null): void { + const chatEl = (ui as any)?.chat?.element as JQuery | undefined; + if (!chatEl?.length) return; + chatEl.find(".ringworld-impulse-chat-marker").remove(); + if (!combat) return; + + const state = getImpulseState(combat); + const rows = getQueueWithNames(combat, state.queue); + const marker = $(` +
+
+ Impulse ${state.currentImpulse} + Queued ${rows.length} +
+
${renderQueueRowsMarkup(rows, 5)}
+
+ + + +
+
+ `); + attachOverlayControls(marker, combat); + chatEl.prepend(marker); +} + +function renderFloatingImpulseHud(combat: any | null): void { + const existing = $("#ringworld-impulse-hud"); + existing.remove(); + if (!combat) return; + + const state = getImpulseState(combat); + const rows = getQueueWithNames(combat, state.queue); + const hud = $(` +
+
+ Impulse ${state.currentImpulse} + Queued ${rows.length} +
+
${renderQueueRowsMarkup(rows, 8)}
+
+ + + +
+
+ `); + attachOverlayControls(hud, combat); + $("body").append(hud); + makeHudDraggable(hud); +} + +function refreshImpulseOverlays(combat: any | null): void { + renderChatImpulseMarker(combat); + renderFloatingImpulseHud(combat); +} + +async function ensureGmControl(): Promise { + if (game.user?.isGM) return true; + ui.notifications?.warn("Only the GM can modify Ringworld impulse sequencing."); + return false; +} + +async function scheduleAction( + combat: any, + combatantId: string, + kind: ActionKind, + major: boolean +): Promise { + if (!(await ensureGmControl())) return; + + const combatant = combat.combatants.get(combatantId); + if (!combatant) return; + + const state = getImpulseState(combat); + const currentImpulse = state.currentImpulse; + const actionRanking = await syncCombatantActionRanking(combatant); + const encPenalty = getEncumbranceArPenalty(combatant); + const impulseMod = num(combatant.flags?.[SYSTEM_ID]?.impulseModifier, 0); + const effectiveAr = Math.max(1, actionRanking + encPenalty); + const baseDuration = major ? effectiveAr : 1; + const duration = Math.max(1, baseDuration + impulseMod); + const completeImpulse = currentImpulse + duration; + + const event: ActionEvent = { + id: foundry.utils.randomID(), + actorId: combatant.actor?.id ?? null, + combatantId, + kind, + major, + startImpulse: currentImpulse, + completeImpulse + }; + + const queueWithoutCombatant = pruneQueueToCombatants( + state.queue.filter((entry) => entry.combatantId !== combatantId), + combat + ); + queueWithoutCombatant.push(event); + + await setImpulseState(combat, { + currentImpulse, + queue: sortQueueByCompletion(queueWithoutCombatant, combat) + }); + + await combatant.update({ + [`flags.${SYSTEM_ID}.pendingEventId`]: event.id, + [`flags.${SYSTEM_ID}.pendingKind`]: kind, + [`flags.${SYSTEM_ID}.actionRanking`]: actionRanking, + [`flags.${SYSTEM_ID}.effectiveActionRanking`]: effectiveAr + }); + + const timingLabel = major + ? `AR ${actionRanking} + Enc ${encPenalty}${impulseMod ? ` ${impulseMod > 0 ? "+" : ""} Imp ${impulseMod}` : ""}` + : `Minor 1${impulseMod ? ` ${impulseMod > 0 ? "+" : ""} Imp ${impulseMod}` : ""}`; + ui.notifications?.info( + `${combatant.name}: ${actionKindLabel(kind)} completes on impulse ${completeImpulse} (${timingLabel} => ${duration}).` + ); + refreshImpulseOverlays(combat); +} + +async function cancelAction(combat: any, combatantId: string): Promise { + if (!(await ensureGmControl())) return; + + const combatant = combat.combatants.get(combatantId); + if (!combatant) return; + + const state = getImpulseState(combat); + const nextQueue = pruneQueueToCombatants( + state.queue.filter((entry) => entry.combatantId !== combatantId), + combat + ); + + await setImpulseState(combat, { + currentImpulse: state.currentImpulse, + queue: sortQueueByCompletion(nextQueue, combat) + }); + + await combatant.update({ + [`flags.${SYSTEM_ID}.pendingEventId`]: null, + [`flags.${SYSTEM_ID}.pendingKind`]: "", + [`flags.${SYSTEM_ID}.aimState`]: { + active: false, + targetId: null, + sinceImpulse: null + } + }); + refreshImpulseOverlays(combat); +} + +async function resolveActionEvent(combat: any, event: ActionEvent, impulse: number): Promise { + const combatant = combat.combatants.get(event.combatantId); + if (!combatant) return; + + const updates: Record = { + [`flags.${SYSTEM_ID}.pendingEventId`]: null, + [`flags.${SYSTEM_ID}.pendingKind`]: "" + }; + + if (event.kind === "aim") { + updates[`flags.${SYSTEM_ID}.aimState`] = { + active: true, + targetId: event.targetTokenId ?? null, + sinceImpulse: impulse + }; + } + + await combatant.update(updates); + + const content = ` +
+

Impulse ${impulse} Completion

+

${esc(combatant.name)} completes ${esc(actionKindLabel(event.kind))}.

+

DEX ${getDexForCombatant(combatant)} | AR ${getCombatantActionRanking(combatant)}

+
+ `; + await ChatMessage.create({ + user: game.user?.id, + speaker: ChatMessage.getSpeaker({ actor: combatant.actor ?? undefined }), + content + }); +} + +async function advanceImpulse(combat: any, mode: "next" | "step"): Promise { + if (!(await ensureGmControl())) return; + + const state = getImpulseState(combat); + const queue = sortQueueByCompletion(pruneQueueToCombatants(state.queue, combat), combat); + const current = state.currentImpulse; + + let targetImpulse = current + 1; + if (mode === "next") { + const next = queue + .map((event) => event.completeImpulse) + .filter((impulse) => impulse >= current) + .sort((a, b) => a - b)[0]; + if (Number.isFinite(next)) { + targetImpulse = next; + } + } + + const completing = queue.filter((event) => event.completeImpulse === targetImpulse); + const remaining = queue.filter((event) => event.completeImpulse !== targetImpulse); + + await setImpulseState(combat, { + currentImpulse: targetImpulse, + queue: remaining + }); + + const ordered = sortQueueByCompletion(completing, combat); + for (const event of ordered) { + await resolveActionEvent(combat, event, targetImpulse); + } + + if (!ordered.length) { + ui.notifications?.info(`Impulse advanced to ${targetImpulse}.`); + } + refreshImpulseOverlays(combat); +} + +async function resetImpulseSequence(combat: any): Promise { + if (!(await ensureGmControl())) return; + + await setImpulseState(combat, { currentImpulse: 1, queue: [] }); + + for (const combatant of combat.combatants) { + await combatant.update({ + [`flags.${SYSTEM_ID}.pendingEventId`]: null, + [`flags.${SYSTEM_ID}.pendingKind`]: "", + [`flags.${SYSTEM_ID}.aimState`]: { + active: false, + targetId: null, + sinceImpulse: null + }, + [`flags.${SYSTEM_ID}.actionRanking`]: actionRankingFromDex(getDexForCombatant(combatant)), + [`flags.${SYSTEM_ID}.effectiveActionRanking`]: null, + [`flags.${SYSTEM_ID}.impulseModifier`]: 0 + }); + } + refreshImpulseOverlays(combat); +} + +function renderImpulsePanel(app: any, htmlLike: JQuery | HTMLElement): void { + const html = "find" in (htmlLike as any) ? (htmlLike as JQuery) : $(htmlLike); + if (!html?.length) return; + html.addClass("ringworld-impulse-mode ringworld-impulse-only"); + + const combat = app?.viewed ?? game.combat; + if (!combat) return; + + const state = getImpulseState(combat); + const queue = sortQueueByCompletion(pruneQueueToCombatants(state.queue, combat), combat); + if (queue.length !== state.queue.length) { + void setImpulseState(combat, { currentImpulse: state.currentImpulse, queue }); + } + + html + .find( + ".token-initiative, .combatant-initiative, .initiative, [data-action='rollInitiative'], [data-control='rollInitiative'], .combatant .fa-dice-d20" + ) + .addClass("rw-hide-initiative"); + + html.find(".ringworld-impulse-panel").remove(); + + const disabled = game.user?.isGM ? "" : "disabled"; + const rows = combat.combatants.map((combatant: any) => { + const ar = getCombatantActionRanking(combatant); + const encPenalty = getEncumbranceArPenalty(combatant); + const impMod = num(combatant.flags?.[SYSTEM_ID]?.impulseModifier, 0); + const effAr = Math.max(1, ar + encPenalty); + const dex = getDexForCombatant(combatant); + const pending = queue.find((event) => event.combatantId === combatant.id); + const pendingLabel = pending + ? `${actionKindLabel(pending.kind)} @ ${pending.completeImpulse}` + : "Ready"; + const aimState = combatant.flags?.[SYSTEM_ID]?.aimState; + const aimLabel = aimState?.active ? `Aiming since ${aimState.sinceImpulse}` : "Not aiming"; + + const options = MAJOR_ACTION_OPTIONS.map((opt) => { + const selected = pending?.major && pending?.kind === opt.value ? "selected" : ""; + return ``; + }).join(""); + const minorOptions = MINOR_ACTION_OPTIONS.map((opt) => { + const selected = !pending?.major && pending?.kind === opt.value ? "selected" : ""; + return ``; + }).join(""); + + return ` +
+
+ ${esc(combatant.name)} + DEX ${dex} | AR ${ar} + Enc ${encPenalty} = ${effAr} | Imp Mod ${impMod >= 0 ? `+${impMod}` : impMod} | ${esc(pendingLabel)} | ${esc(aimLabel)} +
+
+ + + + + + + +
+
+ `; + }).join(""); + + const panel = $(` +
+
+
Impulse ${state.currentImpulse}
+
Queued: ${queue.length}
+
+ + + +
+
+
${rows}
+
+ `); + + html.prepend(panel); + + // Hide all native combat tracker blocks so only Ringworld impulse UI is shown. + const root = html.get(0); + if (root) { + for (const child of Array.from(root.children)) { + if (!(child as HTMLElement).classList.contains("ringworld-impulse-panel")) { + (child as HTMLElement).classList.add("rw-hide-core-tracker"); + } + } + } + + panel.find(".rw-advance-next").on("click", async (event) => { + event.preventDefault(); + await advanceImpulse(combat, "next"); + }); + + panel.find(".rw-advance-step").on("click", async (event) => { + event.preventDefault(); + await advanceImpulse(combat, "step"); + }); + + panel.find(".rw-reset-sequence").on("click", async (event) => { + event.preventDefault(); + await resetImpulseSequence(combat); + }); + + panel.find(".rw-major-start").on("click", async (event) => { + event.preventDefault(); + const row = (event.currentTarget as HTMLElement).closest(".rw-imp-row"); + if (!row) return; + const combatantId = String(row.getAttribute("data-combatant-id") || ""); + const select = row.querySelector(".rw-major-kind") as HTMLSelectElement | null; + const kind = String(select?.value || "draw") as MajorActionKind; + await scheduleAction(combat, combatantId, kind, true); + }); + + panel.find(".rw-minor-start").on("click", async (event) => { + event.preventDefault(); + const row = (event.currentTarget as HTMLElement).closest(".rw-imp-row"); + if (!row) return; + const combatantId = String(row.getAttribute("data-combatant-id") || ""); + const select = row.querySelector(".rw-minor-kind") as HTMLSelectElement | null; + const kind = String(select?.value || "minorCustom") as MinorActionKind; + await scheduleAction(combat, combatantId, kind, false); + }); + + panel.find(".rw-cancel-start").on("click", async (event) => { + event.preventDefault(); + const row = (event.currentTarget as HTMLElement).closest(".rw-imp-row"); + if (!row) return; + const combatantId = String(row.getAttribute("data-combatant-id") || ""); + await cancelAction(combat, combatantId); + }); + + panel.find(".rw-save-mods").on("click", async (event) => { + event.preventDefault(); + if (!(await ensureGmControl())) return; + const row = (event.currentTarget as HTMLElement).closest(".rw-imp-row"); + if (!row) return; + const combatantId = String(row.getAttribute("data-combatant-id") || ""); + const combatant = combat.combatants.get(combatantId); + if (!combatant) return; + + const impModInput = row.querySelector(".rw-imp-mod") as HTMLInputElement | null; + const impMod = Math.trunc(num(impModInput?.value, 0)); + await combatant.update({ + [`flags.${SYSTEM_ID}.impulseModifier`]: impMod + }); + }); + + refreshImpulseOverlays(combat); +} + +export function initializeImpulseTracker(): void { + Hooks.on("renderCombatTracker", (app: any, htmlLike: JQuery | HTMLElement) => { + renderImpulsePanel(app, htmlLike); + }); + + Hooks.on("createCombat", async (combat: any) => { + await setImpulseState(combat, { currentImpulse: 1, queue: [] }); + refreshImpulseOverlays(combat); + }); + + Hooks.on("createCombatant", async (combatant: any) => { + await syncCombatantActionRanking(combatant); + refreshImpulseOverlays(game.combat ?? null); + }); + + Hooks.on("deleteCombatant", async () => { + refreshImpulseOverlays(game.combat ?? null); + }); + + Hooks.on("updateCombat", (combat: any) => { + refreshImpulseOverlays(combat ?? game.combat ?? null); + }); + + Hooks.on("deleteCombat", () => { + refreshImpulseOverlays(null); + }); + + Hooks.on("renderChatLog", () => { + refreshImpulseOverlays(game.combat ?? null); + }); + + Hooks.on("canvasReady", () => { + refreshImpulseOverlays(game.combat ?? null); + }); + + Hooks.on("updateActor", (actor: any) => { + const combat = game.combat; + if (!combat) return; + const inCombat = combat.combatants.some((c: any) => c.actor?.id === actor.id); + if (inCombat) { + ui.combat?.render(true); + refreshImpulseOverlays(combat); + } + }); + + Hooks.on("updateItem", (item: any) => { + const actor = item?.parent; + if (!actor) return; + const combat = game.combat; + if (!combat) return; + const inCombat = combat.combatants.some((c: any) => c.actor?.id === actor.id); + if (inCombat) { + ui.combat?.render(true); + refreshImpulseOverlays(combat); + } + }); +} diff --git a/src/module/item/RingworldItem.ts b/src/module/item/RingworldItem.ts new file mode 100644 index 0000000..e65855e --- /dev/null +++ b/src/module/item/RingworldItem.ts @@ -0,0 +1,27 @@ +import { rollSkillForActor, useWeaponWorkflow } from "../mechanics"; + +export class RingworldItem extends Item { + async rollSkill(): Promise { + if (this.type !== "skill") return; + + const actor = this.actor; + if (!actor) { + ui.notifications?.warn("Skill must be owned by an Actor to roll."); + return; + } + + await rollSkillForActor(actor, this.name, Number((this as any).system.value ?? 0)); + } + + async useWeapon(): Promise { + if (this.type !== "weapon") return; + + const actor = this.actor; + if (!actor) { + ui.notifications?.warn("Weapon must be owned by an Actor to use."); + return; + } + + await useWeaponWorkflow(actor, this); + } +} diff --git a/src/module/mechanics.ts b/src/module/mechanics.ts new file mode 100644 index 0000000..d63cc8d --- /dev/null +++ b/src/module/mechanics.ts @@ -0,0 +1,525 @@ +import { outcomeLabel, rollPercentile } from "./utils/percentile"; +import { chooseOrRollHitLocation, rollHitLocation } from "./utils/hit-locations"; +import { LEG_LOCATIONS, SYSTEM_ID, isLimb, type HitLocationSlug } from "./constants"; +import { postAttackCard, postSkillRollCard } from "./chat"; +import { evaluateRollWithDice } from "./utils/dice"; +import { getActorHitLocationSchema } from "./hit-location-schemas"; + +function num(value: unknown, fallback = 0): number { + const n = Number(value); + return Number.isFinite(n) ? n : fallback; +} + +function getSingleTargetActor(): any | null { + const targets = Array.from(game.user?.targets ?? []); + if (!targets.length) return null; + return (targets[0] as any)?.actor ?? null; +} + +export async function rollStandaloneHitLocation(actor?: any): Promise { + const result = await rollHitLocation(actor); + const speaker = actor ? ChatMessage.getSpeaker({ actor }) : ChatMessage.getSpeaker(); + const content = ` +
+

Hit Location

+

Roll: ${result.roll}

+

Location: ${result.location.label}

+
+ `; + + await ChatMessage.create({ + user: game.user?.id, + speaker, + content + }); +} + +export async function rollSkillForActor(actor: any, skillName: string, target: number): Promise { + const result = await rollPercentile(target); + const skillItem = actor.items.find( + (item: any) => item.type === "skill" && item.name.toLowerCase() === String(skillName).toLowerCase() + ); + + await postSkillRollCard({ + actor, + actorName: actor.name, + actorImg: actor.img, + skillName, + skillImg: skillItem?.img, + roll: result.roll, + target: result.target, + outcomeLabel: outcomeLabel(result.outcome), + isSuccess: result.isSuccess, + isSpecial: result.isSpecial, + isFumble: result.isFumble + }); +} + +function resolveLinkedSkill(actor: any, weapon: any): { name: string; target: number; img?: string } { + const skillId = weapon.system.linkedSkillId; + const byId = skillId ? actor.items.get(skillId) : null; + if (byId?.type === "skill") { + return { name: byId.name, target: num(byId.system.value), img: byId.img }; + } + + const linkedSkillName = String(weapon.system.linkedSkill || "").trim(); + if (linkedSkillName) { + const byName = actor.items.find( + (item: any) => item.type === "skill" && item.name.toLowerCase() === linkedSkillName.toLowerCase() + ); + if (byName) { + return { name: byName.name, target: num(byName.system.value), img: byName.img }; + } + } + + return { + name: linkedSkillName || "Unlinked Skill", + target: 0 + }; +} + +type AimMode = "unaimed" | "partial" | "full"; + +interface AttackRollModifierResult { + finalTarget: number; + summary: string; + aimMode: AimMode; + aimMultiplier: number; + flatModifierTotal: number; +} + +function clampPercentTarget(value: number): number { + return Math.max(0, Math.min(99, Math.floor(value))); +} + +async function promptAttackModifiers(attacker: any, weapon: any, baseSkillTarget: number): Promise { + const attackType = String(weapon.system?.attackType || "ranged"); + const isRanged = attackType === "ranged"; + const defaultAim: AimMode = "full"; + const safeActorName = foundry.utils.escapeHTML(String(attacker?.name || "Attacker")); + const safeWeaponName = foundry.utils.escapeHTML(String(weapon?.name || "Weapon")); + const safeActorImg = foundry.utils.escapeHTML(String(attacker?.img || "icons/svg/mystery-man.svg")); + const safeWeaponImg = foundry.utils.escapeHTML(String(weapon?.img || "icons/svg/sword.svg")); + + const content = ` +
+
+ ${safeActorName} + ${safeWeaponName} +
+

${safeWeaponName}

+

${safeActorName} Attack Setup

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+

Ringworld rule support: unaimed ranged attacks use quarter skill, partial aim uses half skill, full aim uses full skill.

+
+ `; + + return new Promise((resolve) => { + let settled = false; + const done = (value: AttackRollModifierResult | null) => { + if (settled) return; + settled = true; + resolve(value); + }; + + new Dialog({ + title: `${weapon.name} - Attack Modifiers`, + content, + buttons: { + roll: { + label: "Roll Attack", + callback: (dlgHtml: JQuery) => { + const root = dlgHtml[0] as HTMLElement | undefined; + const query = (name: string) => (root?.querySelector(`[name="${name}"]`) as HTMLInputElement | HTMLSelectElement | null); + const aimModeRaw = String(query("aimMode")?.value || defaultAim) as AimMode; + const aimMode: AimMode = (aimModeRaw === "unaimed" || aimModeRaw === "partial" || aimModeRaw === "full") + ? aimModeRaw + : defaultAim; + const aimMultiplier = isRanged ? ({ unaimed: 0.25, partial: 0.5, full: 1 } as const)[aimMode] : 1; + const coverMod = num(query("coverMod")?.value, 0); + const movementMod = num(query("movementMod")?.value, 0); + const rangeMod = num(query("rangeMod")?.value, 0); + const situationalMod = num(query("situationalMod")?.value, 0); + const customMod = num(query("customMod")?.value, 0); + const customLabel = String(query("customLabel")?.value || "").trim(); + + const flatModifierTotal = coverMod + movementMod + rangeMod + situationalMod + customMod; + const preAimTarget = baseSkillTarget + flatModifierTotal; + const adjustedByAim = Math.floor(preAimTarget * aimMultiplier); + const finalTarget = clampPercentTarget(adjustedByAim); + + const parts: string[] = [`Base ${baseSkillTarget}`]; + if (coverMod) parts.push(`Cover ${coverMod >= 0 ? "+" : ""}${coverMod}`); + if (movementMod) parts.push(`Movement ${movementMod >= 0 ? "+" : ""}${movementMod}`); + if (rangeMod) parts.push(`Range ${rangeMod >= 0 ? "+" : ""}${rangeMod}`); + if (situationalMod) parts.push(`Situational ${situationalMod >= 0 ? "+" : ""}${situationalMod}`); + if (customMod) parts.push(`${customLabel || "Custom"} ${customMod >= 0 ? "+" : ""}${customMod}`); + parts.push(`Pre-Aim ${preAimTarget}`); + if (isRanged) { + const aimLabel = aimMode === "unaimed" ? "Quarter" : aimMode === "partial" ? "Half" : "Full"; + parts.push(`Aim ${aimLabel} x${aimMultiplier}`); + } + parts.push(`Final ${finalTarget}`); + + done({ + finalTarget, + summary: parts.join(" | "), + aimMode, + aimMultiplier, + flatModifierTotal + }); + } + }, + cancel: { + label: "Cancel", + callback: () => done(null) + } + }, + default: "roll", + close: () => done(null) + }, { + classes: ["ringworld-attack-dialog"], + width: 510, + resizable: false + }).render(true); + }); +} + +async function consumeWeaponEnergy(actor: any, weapon: any): Promise<{ + sourceLabel: string; + remaining: number; + cost: number; + insufficient: boolean; +}> { + const cost = Math.max(0, num(weapon.system.energyUsedPerAttack)); + if (cost <= 0) { + return { sourceLabel: "None", remaining: 0, cost: 0, insufficient: false }; + } + + const source = String(weapon.system.powerSource || "actor"); + + if (source === "powerPack") { + const packId = String(weapon.system.powerPackId || ""); + const pack = packId ? actor.items.get(packId) : null; + + if (!pack || pack.type !== "gear" || !pack.system.isPowerPack) { + ui.notifications?.warn("Weapon is configured for a power pack but no valid power pack is linked."); + return { sourceLabel: "Power Pack", remaining: 0, cost, insufficient: true }; + } + + const current = num(pack.system.energy?.value); + if (current < cost) { + ui.notifications?.warn(`${pack.name} does not have enough energy.`); + return { sourceLabel: pack.name, remaining: current, cost, insufficient: true }; + } + + const next = current - cost; + await actor.updateEmbeddedDocuments("Item", [{ _id: pack.id, "system.energy.value": next }]); + return { sourceLabel: pack.name, remaining: next, cost, insufficient: false }; + } + + const current = num(actor.system.resources?.energy?.value); + if (current < cost) { + ui.notifications?.warn(`${actor.name} does not have enough actor energy.`); + return { sourceLabel: "Actor Energy", remaining: current, cost, insufficient: true }; + } + + const next = current - cost; + await actor.update({ "system.resources.energy.value": next }); + return { sourceLabel: "Actor Energy", remaining: next, cost, insufficient: false }; +} + +function getItemArmorAtLocation(item: any, slug: HitLocationSlug): number { + if (item.type !== "armor" || !item.system.equipped || item.system.stowed) return 0; + if (!item.system.coverage?.[slug]) return 0; + + const specific = num(item.system.perLocation?.[slug]); + if (specific > 0) return specific; + return num(item.system.value); +} + +function getTotalArmorAtLocation(targetActor: any, slug: HitLocationSlug): number { + const locArmor = num(targetActor.system.hitLocations?.[slug]?.armor); + const stackedItemsArmor = targetActor.items + .filter((item: any) => item.type === "armor") + .reduce((sum: number, item: any) => sum + getItemArmorAtLocation(item, slug), 0); + + return locArmor + stackedItemsArmor; +} + +function buildConditionUpdates(targetActor: any, slug: HitLocationSlug, locationValue: number, locationMax: number, generalHp: number) { + const current = foundry.utils.deepClone(targetActor.system.conditions ?? {}); + current.unusableLimbs = current.unusableLimbs ?? { rArm: false, lArm: false, rLeg: false, lLeg: false }; + + if (locationValue <= 0) { + if (slug === "head" || slug === "chest") { + current.unconscious = true; + current.prone = true; + } + + if (slug === "abdomen") { + current.prone = true; + current.unusableLimbs.rLeg = true; + current.unusableLimbs.lLeg = true; + } + + if (slug === "rArm" || slug === "lArm") { + current.unusableLimbs[slug] = true; + } + + if (LEG_LOCATIONS.includes(slug)) { + current.unusableLimbs[slug] = true; + current.prone = true; + } + } + + if (generalHp <= 0) { + current.dying = true; + } + + if ((slug === "head" || slug === "chest" || slug === "abdomen") && locationValue <= -locationMax) { + current.dying = true; + } + + if (slug === "head" && locationValue <= -locationMax) { + current.dead = true; + current.unconscious = true; + } + + return current; +} + +async function syncCoreStatusEffects(targetActor: any, conditions: any): Promise { + const toggler = (targetActor as any).toggleStatusEffect; + if (typeof toggler !== "function") return; + + try { + await toggler.call(targetActor, "prone", { active: !!conditions.prone }); + await toggler.call(targetActor, "unconscious", { active: !!conditions.unconscious }); + await toggler.call(targetActor, "dead", { active: !!conditions.dead }); + } catch (_err) { + // Some worlds lock actor-level status manipulation; system conditions still update. + } +} + +async function applyLocationDamage(targetActor: any, slug: HitLocationSlug, rawDamage: number): Promise<{ + armor: number; + finalDamage: number; + locationValue: number; + generalHpValue: number; +}> { + const location = targetActor.system.hitLocations?.[slug]; + const currentLoc = num(location?.hp?.value); + const locMax = Math.max(1, num(location?.hp?.max, 1)); + const armor = getTotalArmorAtLocation(targetActor, slug); + const finalDamage = Math.max(0, rawDamage - armor); + + const unclampedLoc = currentLoc - finalDamage; + const nextLoc = isLimb(slug) ? Math.max(unclampedLoc, -locMax) : unclampedLoc; + + const currentGeneral = num(targetActor.system.generalHp?.value); + const nextGeneral = currentGeneral - finalDamage; + + const conditions = buildConditionUpdates(targetActor, slug, nextLoc, locMax, nextGeneral); + + const updates: Record = { + [`system.hitLocations.${slug}.hp.value`]: nextLoc, + "system.generalHp.value": nextGeneral, + "system.conditions": conditions + }; + + await targetActor.update(updates); + await syncCoreStatusEffects(targetActor, conditions); + + return { + armor, + finalDamage, + locationValue: nextLoc, + generalHpValue: nextGeneral + }; +} + +export async function useWeaponWorkflow(attacker: any, weapon: any): Promise { + const targetActor = getSingleTargetActor(); + const hasTarget = Boolean(targetActor); + const targetName = targetActor?.name ?? "No Target"; + + const linkedSkill = resolveLinkedSkill(attacker, weapon); + const modifierResult = await promptAttackModifiers(attacker, weapon, linkedSkill.target); + if (!modifierResult) return; + + const energy = await consumeWeaponEnergy(attacker, weapon); + if (energy.insufficient) return; + + const attack = await rollPercentile(modifierResult.finalTarget); + + if (!attack.isSuccess) { + const remainingGeneralHp = targetActor ? num(targetActor.system.generalHp?.value, 0) : 0; + await postAttackCard({ + actor: attacker, + actorName: attacker.name, + actorImg: attacker.img, + targetName, + hasTarget, + weaponName: weapon.name, + weaponImg: weapon.img, + skillName: linkedSkill.name, + skillImg: linkedSkill.img, + attackRoll: attack.roll, + attackTarget: attack.target, + attackOutcomeLabel: outcomeLabel(attack.outcome), + isSuccess: false, + isSpecial: attack.isSpecial, + isFumble: attack.isFumble, + hitLocationLabel: "-", + hitLocationRoll: null, + rawDamage: 0, + armor: 0, + finalDamage: 0, + remainingLocationHp: 0, + remainingGeneralHp, + energySourceLabel: energy.sourceLabel, + energyCost: energy.cost, + energyRemaining: energy.remaining, + notes: `Attack failed before damage was rolled. ${modifierResult.summary}` + }); + return; + } + + const hitSelection = await chooseOrRollHitLocation(targetActor ?? attacker); + if (!hitSelection) return; + + const damageFormula = String(weapon.system.damageFormula || "0").trim() || "0"; + const damageRoll = await evaluateRollWithDice(damageFormula); + const rawDamage = Math.max(0, num(damageRoll.total)); + + const application = targetActor + ? await applyLocationDamage(targetActor, hitSelection.slug, rawDamage) + : { + armor: 0, + finalDamage: rawDamage, + locationValue: 0, + generalHpValue: 0 + }; + + await postAttackCard({ + actor: attacker, + actorName: attacker.name, + actorImg: attacker.img, + targetName, + hasTarget, + weaponName: weapon.name, + weaponImg: weapon.img, + skillName: linkedSkill.name, + skillImg: linkedSkill.img, + attackRoll: attack.roll, + attackTarget: attack.target, + attackOutcomeLabel: outcomeLabel(attack.outcome), + isSuccess: attack.isSuccess, + isSpecial: attack.isSpecial, + isFumble: attack.isFumble, + hitLocationLabel: hitSelection.label, + hitLocationRoll: hitSelection.rolled, + rawDamage, + armor: application.armor, + finalDamage: application.finalDamage, + remainingLocationHp: application.locationValue, + remainingGeneralHp: application.generalHpValue, + energySourceLabel: energy.sourceLabel, + energyCost: energy.cost, + energyRemaining: energy.remaining, + notes: targetActor + ? `Location and general HP were both reduced by final damage, per Ringworld rules. ${modifierResult.summary}` + : `No target selected: roll resolved with no damage application. ${modifierResult.summary}` + }); +} + +export function recomputeHitPointMaximums(actor: any): void { + if (actor.type !== "explorer" && actor.type !== "creature") return; + + // Ringworld general HP is CON + MAS. Location HP then scales from that general HP. + const con = num(actor.system?.characteristics?.con, 10); + const mas = num(actor.system?.characteristics?.mas, 10); + const existingMax = num(actor.system.generalHp?.max, 0); + const existingValue = num(actor.system.generalHp?.value, 0); + const generalMax = Math.max(1, Math.floor(con + mas)); + + actor.system.generalHp = actor.system.generalHp ?? { value: generalMax, max: generalMax }; + actor.system.generalHp.max = generalMax; + const hadExisting = existingMax > 0; + const wasFull = hadExisting && existingValue >= existingMax; + const looksPlaceholder = existingMax <= 1 && existingValue <= 1; + const preservedDamage = hadExisting ? Math.max(0, existingMax - existingValue) : 0; + const recomputedValue = (!hadExisting || wasFull || looksPlaceholder) + ? generalMax + : Math.max(0, generalMax - preservedDamage); + + actor.system.generalHp.value = Math.min(Math.max(0, recomputedValue), generalMax); + const generalAtFull = num(actor.system.generalHp.value, generalMax) >= generalMax; + + actor.system.hitLocations = actor.system.hitLocations ?? {}; + actor.system.hitLocationSchema = actor.system.hitLocationSchema ?? "auto"; + const schema = getActorHitLocationSchema(actor); + + for (const entry of schema.entries) { + const slug = entry.slug; + const loc = actor.system.hitLocations[slug] ?? { + label: slug, + hp: { value: 1, max: 1 }, + armor: 0 + }; + loc.label = entry.label; + const hpFactor = Math.max(0.05, num((entry as any).hpFactor, 0.3)); + const max = Math.max(1, Math.ceil(generalMax * hpFactor)); + + const previousMax = num(loc.hp?.max, 1); + const previousValue = num(loc.hp?.value, previousMax); + const isPlaceholder = previousMax <= 1 && previousValue <= 1; + const shouldBackfillToFull = generalAtFull && previousValue <= 1 && max > 1; + + loc.hp.max = max; + loc.hp.value = (isPlaceholder || shouldBackfillToFull) ? max : Math.min(previousValue, max); + actor.system.hitLocations[slug] = loc; + } +} + +export function getTemplatePath(path: string): string { + return `systems/${SYSTEM_ID}/templates/${path}`; +} diff --git a/src/module/sheets/actors/CreatureSheet.ts b/src/module/sheets/actors/CreatureSheet.ts new file mode 100644 index 0000000..2e8f230 --- /dev/null +++ b/src/module/sheets/actors/CreatureSheet.ts @@ -0,0 +1,73 @@ +import { getActorHitLocationSchema } from "../../hit-location-schemas"; + +const BaseActorSheet = (foundry as any).appv1.sheets.ActorSheet; + +export class RingworldCreatureSheet extends BaseActorSheet { + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "actor", "creature"], + width: 760, + height: 700 + }); + } + + get template(): string { + return "systems/ringworld/templates/actor/creature-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + const actor = this.actor as any; + + return { + ...data, + system: actor.system, + skills: actor.items.filter((item: any) => item.type === "skill"), + weapons: actor.items.filter((item: any) => item.type === "weapon"), + hitLocations: getActorHitLocationSchema(actor).entries.map((entry) => ({ + slug: entry.slug, + rollRange: `${entry.min}-${entry.max}`, + label: entry.label, + ...(actor.system.hitLocations?.[entry.slug] ?? {}) + })) + }; + } + + activateListeners(html: JQuery): void { + super.activateListeners(html); + + html.find(".item-create").on("click", async (event) => { + event.preventDefault(); + const type = String((event.currentTarget as HTMLElement).dataset.type || "skill"); + await this.actor.createEmbeddedDocuments("Item", [{ name: `New ${type}`, type }]); + }); + + html.find(".item-edit").on("click", (event) => { + const li = (event.currentTarget as HTMLElement).closest(".item"); + const item = li ? this.actor.items.get(li.dataset.itemId ?? "") : null; + item?.sheet?.render(true); + }); + + html.find(".item-delete").on("click", async (event) => { + const li = (event.currentTarget as HTMLElement).closest(".item"); + if (!li?.dataset.itemId) return; + await this.actor.deleteEmbeddedDocuments("Item", [li.dataset.itemId]); + }); + + html.find(".item-roll-skill").on("click", async (event) => { + const li = (event.currentTarget as HTMLElement).closest(".item"); + if (!li?.dataset.itemId) return; + await (this.actor as any).rollSkillById(li.dataset.itemId); + }); + + html.find(".item-use-weapon").on("click", async (event) => { + const li = (event.currentTarget as HTMLElement).closest(".item"); + if (!li?.dataset.itemId) return; + await (this.actor as any).useWeaponById(li.dataset.itemId); + }); + + html.find(".roll-hit-location").on("click", async () => { + await (this.actor as any).rollHitLocation(); + }); + } +} diff --git a/src/module/sheets/actors/ExplorerSheet.ts b/src/module/sheets/actors/ExplorerSheet.ts new file mode 100644 index 0000000..fd00bba --- /dev/null +++ b/src/module/sheets/actors/ExplorerSheet.ts @@ -0,0 +1,1082 @@ +import { type HitLocationSlug } from "../../constants"; +import { + BASE_SKILL_CATEGORIES, + baseSkillNameSet, + normalizeSkillName, + RINGWORLD_BASE_SKILLS +} from "../../data-base-skills"; +import { baseSkillByName, DEFAULT_SKILL_ICON, resolveSkillIcon, skillIconPath } from "../../skill-icons"; +import { getActorHitLocationSchema, listHitLocationSchemas } from "../../hit-location-schemas"; +import { PURSUIT_CATALOG } from "../../world-catalog"; +import { outcomeLabel, rollPercentile } from "../../utils/percentile"; +import { postSkillRollCard } from "../../chat"; + +const BaseActorSheet = (foundry as any).appv1.sheets.ActorSheet; + +const ROOT_KEY_BY_CATEGORY: Record = { + Agility: "agility", + Communication: "communication", + Knowledge: "knowledge", + Perception: "perception", + Technical: "technical" +}; + +const ROOT_FORMULA_BY_CATEGORY: Record = { + Agility: "STR + DEX", + Communication: "INT + APP", + Knowledge: "INT + EDU", + Perception: "POW + CON", + Technical: "DEX + INT" +}; + +const EXCLUDED_EDUCATION_SKILLS = new Set([ + normalizeSkillName("Archaic Melee Weapon"), + normalizeSkillName("Archaic Ranged Weapons") +]); + +const HYPERDRIVE_KEY = normalizeSkillName("Hyperdrive"); + +interface BuilderAllocation { + education: number; + pursuits: number; + special: number; +} + +interface BuilderPursuitSelection { + name: string; + years: number; +} + +interface BuilderState { + pursuits: BuilderPursuitSelection[]; + allocations: Record; + baseBySkillId: Record; +} + +interface BuilderPool { + total: number; + spent: number; + remaining: number; +} + +interface BuilderSkillMeta { + allocation: BuilderAllocation; + totalAllocated: number; + baseValue: number; + rootCap: number; + maxAllocation: number; + allowsEducation: boolean; + allowsPursuits: boolean; + allowsSpecial: boolean; +} + +interface BuilderContext { + state: BuilderState; + education: BuilderPool; + pursuits: BuilderPool; + special: BuilderPool; + pursuitYears: BuilderPool; + pursuitOptions: string[]; + pursuitAllowedSkills: Set; + bySkillId: Map; +} + +function byName(a: any, b: any): number { + return a.name.localeCompare(b.name); +} + +function num(value: unknown, fallback = 0): number { + const n = Number(value); + return Number.isFinite(n) ? n : fallback; +} + +function toInt(value: unknown, fallback = 0): number { + return Math.max(0, Math.floor(num(value, fallback))); +} + +function normalizeAllocation(raw: any): BuilderAllocation { + return { + education: toInt(raw?.education, 0), + pursuits: toInt(raw?.pursuits, 0), + special: toInt(raw?.special, 0) + }; +} + +function normalizePursuitSelection(raw: any): BuilderPursuitSelection | null { + const name = String(raw?.name || "").trim(); + const years = toInt(raw?.years, 0); + if (!name || years <= 0) return null; + return { name, years }; +} + +function normalizeBuilderState(raw: any): BuilderState { + const pursuits = Array.isArray(raw?.pursuits) + ? raw.pursuits.map((entry: any) => normalizePursuitSelection(entry)).filter((entry: any) => Boolean(entry)) + : []; + + const allocations: Record = {}; + if (raw?.allocations && typeof raw.allocations === "object") { + for (const [skillId, allocation] of Object.entries(raw.allocations)) { + if (!skillId) continue; + const normalized = normalizeAllocation(allocation); + if (normalized.education + normalized.pursuits + normalized.special <= 0) continue; + allocations[skillId] = normalized; + } + } + + const baseBySkillId: Record = {}; + if (raw?.baseBySkillId && typeof raw.baseBySkillId === "object") { + for (const [skillId, value] of Object.entries(raw.baseBySkillId)) { + if (!skillId) continue; + baseBySkillId[skillId] = Math.max(0, toInt(value, 0)); + } + } + + return { + pursuits, + allocations, + baseBySkillId + }; +} + +function isEducationEligible(skillName: string): boolean { + return !EXCLUDED_EDUCATION_SKILLS.has(normalizeSkillName(skillName)); +} + +function isSpecialEligible(skillName: string): boolean { + return normalizeSkillName(skillName) !== HYPERDRIVE_KEY; +} + +function buildPursuitAllowedSkillSet(pursuits: BuilderPursuitSelection[]): Set { + const entriesByName = new Map( + PURSUIT_CATALOG.map((entry) => [normalizeSkillName(entry.name), entry]) + ); + const allowed = new Set(); + + for (const pursuit of pursuits) { + const entry = entriesByName.get(normalizeSkillName(pursuit.name)); + if (!entry) continue; + + for (const skillName of entry.fixedSkills || []) { + allowed.add(normalizeSkillName(skillName)); + } + + for (const group of entry.optionalSkillGroups || []) { + for (const skillName of group.skills || []) { + allowed.add(normalizeSkillName(skillName)); + } + } + } + + return allowed; +} + +function computeBuilderPools(characteristics: any): { + educationTotal: number; + pursuitsTotal: number; + specialTotal: number; + pursuitYearsTotal: number; +} { + const age = toInt(characteristics?.age, 0); + const edu = toInt(characteristics?.edu, 0); + const int = toInt(characteristics?.int, 0); + + const educationTotal = Math.max(0, (edu - 10) * 20); + const pursuitYearsTotal = Math.max(0, age - edu - 6); + const pursuitsTotal = pursuitYearsTotal * 20; + const specialTotal = Math.max(0, int * 10); + + return { + educationTotal, + pursuitsTotal, + specialTotal, + pursuitYearsTotal + }; +} + +function buildBuilderContext(actor: any, skills: any[]): BuilderContext { + const pools = computeBuilderPools(actor.system?.characteristics ?? {}); + const state = normalizeBuilderState(actor.system?.characterBuilder ?? {}); + + const pursuitOptions = PURSUIT_CATALOG.map((entry) => entry.name).sort((a, b) => a.localeCompare(b)); + const pursuitAllowedSkills = buildPursuitAllowedSkillSet(state.pursuits); + + const bySkillId = new Map(); + + let educationSpent = 0; + let pursuitsSpent = 0; + let specialSpent = 0; + + for (const skill of skills) { + const allocation = normalizeAllocation(state.allocations[skill.id]); + const totalAllocated = allocation.education + allocation.pursuits + allocation.special; + + const baseFromState = state.baseBySkillId[skill.id]; + const baseFromDefinition = baseSkillByName(skill.name)?.base; + const inferredBase = Math.max(0, num(skill.system?.value, 0) - totalAllocated); + const baseValue = Math.max( + 0, + Number.isFinite(baseFromState) + ? baseFromState + : (baseFromDefinition ?? inferredBase) + ); + + const rootKey = ROOT_KEY_BY_CATEGORY[String(skill.system?.category || "")] ?? ""; + const rootCap = rootKey ? Math.max(0, toInt(actor.system?.skillRootMax?.[rootKey], 0)) : 99; + const maxAllocation = Math.max(0, rootCap - baseValue); + + const normalizedSkillName = normalizeSkillName(skill.name); + const allowsEducation = isEducationEligible(skill.name); + const allowsPursuits = pursuitAllowedSkills.has(normalizedSkillName); + const allowsSpecial = isSpecialEligible(skill.name); + + bySkillId.set(skill.id, { + allocation, + totalAllocated, + baseValue, + rootCap, + maxAllocation, + allowsEducation, + allowsPursuits, + allowsSpecial + }); + + educationSpent += allocation.education; + pursuitsSpent += allocation.pursuits; + specialSpent += allocation.special; + } + + const pursuitYearsAllocated = state.pursuits.reduce((sum, row) => sum + row.years, 0); + + return { + state, + education: { + total: pools.educationTotal, + spent: educationSpent, + remaining: pools.educationTotal - educationSpent + }, + pursuits: { + total: pools.pursuitsTotal, + spent: pursuitsSpent, + remaining: pools.pursuitsTotal - pursuitsSpent + }, + special: { + total: pools.specialTotal, + spent: specialSpent, + remaining: pools.specialTotal - specialSpent + }, + pursuitYears: { + total: pools.pursuitYearsTotal, + spent: pursuitYearsAllocated, + remaining: pools.pursuitYearsTotal - pursuitYearsAllocated + }, + pursuitOptions, + pursuitAllowedSkills, + bySkillId + }; +} + +function getItemArmorAtLocation(item: any, slug: string): number { + if (item.type !== "armor" || !item.system?.equipped || item.system?.stowed) return 0; + if (!item.system?.coverage?.[slug]) return 0; + + const specific = num(item.system?.perLocation?.[slug], 0); + if (specific > 0) return specific; + return num(item.system?.value, 0); +} + +function getTotalArmorAtLocation(actor: any, slug: string): number { + const base = num(actor.system?.hitLocations?.[slug]?.armor, 0); + const fromItems = actor.items + .filter((item: any) => item.type === "armor") + .reduce((sum: number, item: any) => sum + getItemArmorAtLocation(item, slug), 0); + return base + fromItems; +} + +function getPreparedHitLocation(actor: any, slug: string, rollRange: string): any { + const locationConfig = getActorHitLocationSchema(actor).entries.find((entry) => entry.slug === slug); + const source = actor.system?.hitLocations?.[slug] ?? {}; + const hp = source.hp ?? {}; + const rawLabel = String(source.label ?? "").trim(); + return { + slug, + rollRange, + label: rawLabel || String(locationConfig?.label || slug), + armor: num(source.armor, 0), + totalArmor: getTotalArmorAtLocation(actor, slug), + hp: { + value: num(hp.value, 0), + max: num(hp.max, 0) + } + }; +} + +function chooseRandom(items: T[]): T { + return items[Math.floor(Math.random() * items.length)]; +} + +function clampPercent(value: number): number { + return Math.max(0, Math.min(99, Math.floor(value))); +} + +export class RingworldExplorerSheet extends BaseActorSheet { + private isToolsUnlocked(): boolean { + return Boolean((this.actor as any)?.system?.sheetToolsUnlocked); + } + + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "actor", "explorer"], + width: 930, + height: 1460, + tabs: [{ navSelector: ".tabs", contentSelector: ".sheet-body", initial: "overview" }] + }); + } + + get template(): string { + return "systems/ringworld/templates/actor/explorer-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + const actor = this.actor as any; + + if (game.user?.isGM && actor.isOwner) { + await actor.ensureBaseSkills?.(); + } + + const skills = actor.items.filter((item: any) => item.type === "skill").sort(byName); + const builderContext = buildBuilderContext(actor, skills); + + const weapons = actor.items.filter((item: any) => item.type === "weapon").sort(byName); + const armors = actor.items.filter((item: any) => item.type === "armor").sort(byName); + const gear = actor.items.filter((item: any) => item.type === "gear").sort(byName); + const speciesOptions = game.items + .filter((item: any) => item.type === "species" && item.system.playable) + .map((item: any) => item.name) + .sort(); + const baseSet = baseSkillNameSet(); + + const skillByNormalized = new Map(); + for (const skill of skills) { + const key = normalizeSkillName(skill.name); + if (!skillByNormalized.has(key)) { + skillByNormalized.set(key, skill); + } + } + + const skillRootMax = actor.system.skillRootMax ?? {}; + const skillsByCategory = BASE_SKILL_CATEGORIES.map((category) => { + const rootKey = ROOT_KEY_BY_CATEGORY[category] ?? category.toLowerCase(); + const rows = RINGWORLD_BASE_SKILLS + .filter((skill) => skill.category === category) + .map((skill) => { + const existing = skillByNormalized.get(normalizeSkillName(skill.name)); + const meta = existing ? builderContext.bySkillId.get(existing.id) : null; + return { + name: skill.name, + base: skill.base, + category: skill.category, + specialization: skill.specialization ?? "", + itemId: existing?.id ?? null, + icon: existing?.img || skillIconPath(skill.name), + value: existing?.system?.value ?? skill.base, + missing: !existing, + allocationTotal: meta?.totalAllocated ?? 0, + allocation: meta?.allocation ?? { education: 0, pursuits: 0, special: 0 } + }; + }); + + return { + category, + rootKey, + rootFormula: ROOT_FORMULA_BY_CATEGORY[category] ?? "", + rootValue: num(skillRootMax[rootKey], 0), + rows + }; + }); + + const customSkills = skills + .filter((item: any) => !baseSet.has(normalizeSkillName(item.name))) + .map((item: any) => { + const meta = builderContext.bySkillId.get(item.id); + return { + id: item.id, + name: item.name, + system: item.system, + img: resolveSkillIcon(item.name, item.img || DEFAULT_SKILL_ICON), + allocationTotal: meta?.totalAllocated ?? 0, + allocation: meta?.allocation ?? { education: 0, pursuits: 0, special: 0 } + }; + }); + + const missingBaseSkillsCount = skillsByCategory.reduce( + (total, group) => total + group.rows.filter((row) => row.missing).length, + 0 + ); + + const schema = getActorHitLocationSchema(actor); + const rangesFromSchema = new Map(schema.entries.map((entry) => [entry.slug, `${entry.min}-${entry.max}`])); + const order = schema.entries.map((entry) => entry.slug as HitLocationSlug); + const hitLocations = order.map((slug) => + getPreparedHitLocation(actor, slug, rangesFromSchema.get(slug) ?? "") + ); + + const bodyLayoutOrder = schema.bodyOrder; + const bodyLocations = bodyLayoutOrder.map((slug) => + getPreparedHitLocation(actor, slug, rangesFromSchema.get(slug) ?? "") + ); + + const enc = actor.system?.encumbrance ?? {}; + const encRaw = num(enc.rawLoadKg, 0); + const encEffective = num(enc.effectiveLoadKg, encRaw); + const encLimit = num(enc.limitKg, num(actor.system?.characteristics?.str, 0)); + const encPenalty = num(enc.actionRankingPenalty, 0); + const baseActionRanking = num(actor.system?.derived?.actionRanking, 0); + const effectiveActionRanking = Math.max(1, baseActionRanking + encPenalty); + const encumbranceSummary = `${encRaw.toFixed(1)}kg on-person | ${encEffective.toFixed(1)}kg effective / ${encLimit.toFixed(1)}kg limit | AR +${encPenalty} (stowed ignored)`; + const movementSummary = `${num(actor.system?.derived?.currentMove, num(actor.system?.derived?.move, 3))}m / ${num(actor.system?.derived?.currentRun, num(actor.system?.derived?.run, 6))}m`; + + return { + ...data, + system: actor.system, + skills, + skillsByCategory, + customSkills, + missingBaseSkillsCount, + weapons, + armors, + gear, + speciesOptions, + hitLocations, + bodyLocations, + hitLocationSchemaOptions: listHitLocationSchemas(), + currentHitLocationSchema: String(actor.system.hitLocationSchema || "auto"), + activeHitLocationSchemaId: schema.id, + activeHitLocationSchemaLabel: schema.label, + sheetToolsUnlocked: Boolean(actor.system?.sheetToolsUnlocked), + baseActionRanking, + effectiveActionRanking, + encumbranceSummary, + movementSummary, + builder: { + education: builderContext.education, + pursuits: builderContext.pursuits, + special: builderContext.special, + pursuitYears: builderContext.pursuitYears, + pursuitsList: builderContext.state.pursuits, + pursuitOptions: builderContext.pursuitOptions + } + }; + } + + private async updateBuilderState(state: BuilderState): Promise { + await this.actor.update({ + "system.characterBuilder": { + pursuits: state.pursuits, + allocations: state.allocations, + baseBySkillId: state.baseBySkillId + } + }); + } + + private async syncSkillsFromBuilderState(state: BuilderState): Promise { + const skills = this.actor.items.filter((item: any) => item.type === "skill"); + const updates: any[] = []; + + for (const skill of skills) { + const allocation = normalizeAllocation(state.allocations[skill.id]); + const total = allocation.education + allocation.pursuits + allocation.special; + + const rootKey = ROOT_KEY_BY_CATEGORY[String(skill.system?.category || "")] ?? ""; + const rootCap = rootKey ? Math.max(0, toInt(this.actor.system?.skillRootMax?.[rootKey], 0)) : 99; + + let base = state.baseBySkillId[skill.id]; + if (!Number.isFinite(base)) { + const fromBase = baseSkillByName(skill.name)?.base; + base = Math.max(0, fromBase ?? (num(skill.system?.value, 0) - total)); + state.baseBySkillId[skill.id] = base; + } + + const value = Math.max(0, Math.min(99, Math.min(rootCap, base + total))); + if (num(skill.system?.value, 0) !== value) { + updates.push({ _id: skill.id, "system.value": value }); + } + } + + if (updates.length) { + await this.actor.updateEmbeddedDocuments("Item", updates); + } + } + + private async addOrUpdatePursuit(name: string, years: number): Promise { + if (!this.isToolsUnlocked()) return; + const trimmed = String(name || "").trim(); + const cleanYears = Math.max(0, Math.floor(Number(years) || 0)); + if (!trimmed || cleanYears <= 0) { + ui.notifications?.warn("Choose a pursuit and set years above 0."); + return; + } + + const skills = this.actor.items.filter((item: any) => item.type === "skill"); + const context = buildBuilderContext(this.actor, skills); + const nextState = foundry.utils.deepClone(context.state) as BuilderState; + + const existingIndex = nextState.pursuits.findIndex((entry) => normalizeSkillName(entry.name) === normalizeSkillName(trimmed)); + if (existingIndex >= 0) { + nextState.pursuits[existingIndex].years = cleanYears; + } else { + nextState.pursuits.push({ name: trimmed, years: cleanYears }); + } + + await this.updateBuilderState(nextState); + } + + private async removePursuit(index: number): Promise { + if (!this.isToolsUnlocked()) return; + const skills = this.actor.items.filter((item: any) => item.type === "skill"); + const context = buildBuilderContext(this.actor, skills); + const nextState = foundry.utils.deepClone(context.state) as BuilderState; + if (index < 0 || index >= nextState.pursuits.length) return; + + nextState.pursuits.splice(index, 1); + await this.updateBuilderState(nextState); + } + + private async resetBuilderAllocations(): Promise { + if (!this.isToolsUnlocked()) return; + const skills = this.actor.items.filter((item: any) => item.type === "skill"); + const context = buildBuilderContext(this.actor, skills); + const nextState = foundry.utils.deepClone(context.state) as BuilderState; + + nextState.allocations = {}; + await this.updateBuilderState(nextState); + await this.syncSkillsFromBuilderState(nextState); + } + + private async randomizeBuilderAllocations(): Promise { + if (!this.isToolsUnlocked()) return; + await (this.actor as any).ensureBaseSkills?.(); + + const skills = this.actor.items.filter((item: any) => item.type === "skill").sort(byName); + const context = buildBuilderContext(this.actor, skills); + const nextState = foundry.utils.deepClone(context.state) as BuilderState; + + const totalYears = Math.max(0, context.pursuitYears.total); + const pursuits = PURSUIT_CATALOG.map((entry) => entry.name); + nextState.pursuits = []; + + if (totalYears > 0 && pursuits.length > 0) { + const desiredCount = Math.min( + pursuits.length, + Math.max(1, Math.min(3, Math.floor(totalYears / 2) || 1)) + ); + + const available = [...pursuits]; + for (let i = 0; i < desiredCount && available.length; i += 1) { + const idx = Math.floor(Math.random() * available.length); + nextState.pursuits.push({ name: available[idx], years: 1 }); + available.splice(idx, 1); + } + + let remainingYears = totalYears - nextState.pursuits.length; + while (remainingYears > 0 && nextState.pursuits.length) { + const row = chooseRandom(nextState.pursuits); + row.years += 1; + remainingYears -= 1; + } + } + + const seededState = normalizeBuilderState(nextState); + seededState.allocations = {}; + + const freshContext = buildBuilderContext( + { ...this.actor, system: { ...this.actor.system, characterBuilder: seededState } }, + skills + ); + + for (const skill of skills) { + const meta = freshContext.bySkillId.get(skill.id); + if (!meta) continue; + seededState.baseBySkillId[skill.id] = meta.baseValue; + seededState.allocations[skill.id] = { education: 0, pursuits: 0, special: 0 }; + } + + const totalBySkill = new Map(); + for (const skill of skills) { + totalBySkill.set(skill.id, 0); + } + + const maxForSkill = (skillId: string): number => { + const meta = freshContext.bySkillId.get(skillId); + return meta ? Math.max(0, meta.maxAllocation) : 0; + }; + + const allocatePool = ( + points: number, + poolKey: keyof BuilderAllocation, + eligible: (skill: any, meta: BuilderSkillMeta) => boolean + ): number => { + let remaining = Math.max(0, points); + let guard = 0; + + while (remaining > 0 && guard < 300000) { + guard += 1; + const candidates = skills.filter((skill) => { + const meta = freshContext.bySkillId.get(skill.id); + if (!meta || !eligible(skill, meta)) return false; + const allocated = totalBySkill.get(skill.id) ?? 0; + return allocated < maxForSkill(skill.id); + }); + + if (!candidates.length) break; + const picked = chooseRandom(candidates); + const current = seededState.allocations[picked.id] ?? { education: 0, pursuits: 0, special: 0 }; + current[poolKey] += 1; + seededState.allocations[picked.id] = current; + totalBySkill.set(picked.id, (totalBySkill.get(picked.id) ?? 0) + 1); + remaining -= 1; + } + + return remaining; + }; + + const educationUnspent = allocatePool( + Math.max(0, freshContext.education.total), + "education", + (skill, meta) => meta.allowsEducation + ); + + const pursuitsUnspent = allocatePool( + Math.max(0, freshContext.pursuits.total), + "pursuits", + (skill, meta) => meta.allowsPursuits + ); + + const specialUnspent = allocatePool( + Math.max(0, freshContext.special.total), + "special", + (skill, meta) => meta.allowsSpecial + ); + + for (const skill of skills) { + const allocation = normalizeAllocation(seededState.allocations[skill.id]); + if (allocation.education + allocation.pursuits + allocation.special <= 0) { + delete seededState.allocations[skill.id]; + } + } + + await this.updateBuilderState(seededState); + await this.syncSkillsFromBuilderState(seededState); + + if (educationUnspent > 0 || pursuitsUnspent > 0 || specialUnspent > 0) { + ui.notifications?.info( + `Random build complete. Unspent points: EDU ${educationUnspent}, Pursuits ${pursuitsUnspent}, Special ${specialUnspent}.` + ); + } else { + ui.notifications?.info("Random build complete. All available points allocated."); + } + } + + private async promptSkillAllocation(skillId: string): Promise { + if (!this.isToolsUnlocked()) return; + const skill = this.actor.items.get(skillId); + if (!skill || skill.type !== "skill") return; + + const skills = this.actor.items.filter((item: any) => item.type === "skill"); + const context = buildBuilderContext(this.actor, skills); + const meta = context.bySkillId.get(skillId); + if (!meta) return; + + const current = meta.allocation; + const maxEducation = Math.max(0, context.education.remaining + current.education); + const maxPursuits = Math.max(0, context.pursuits.remaining + current.pursuits); + const maxSpecial = Math.max(0, context.special.remaining + current.special); + + const maxByCap = Math.max(0, meta.maxAllocation); + + const content = ` +
+
+ +
+

${skill.name}

+

Base ${meta.baseValue}% | Root cap ${meta.rootCap}% | Current ${num(skill.system?.value, 0)}%

+
+
+ + + + + + + +

Max additional from root cap: ${maxByCap}. Pools: EDU ${context.education.remaining}, Pursuits ${context.pursuits.remaining}, Special ${context.special.remaining}.

+
+ `; + + const result = await new Promise((resolve) => { + new Dialog({ + title: `${skill.name} - Allocate Character Points`, + content, + buttons: { + save: { + label: "Apply", + callback: (html: JQuery) => { + const form = html.find("form")[0] as HTMLFormElement | undefined; + if (!form) { + resolve(null); + return; + } + const data = new FormData(form); + resolve({ + education: meta.allowsEducation ? toInt(data.get("education"), current.education) : current.education, + pursuits: meta.allowsPursuits ? toInt(data.get("pursuits"), current.pursuits) : current.pursuits, + special: meta.allowsSpecial ? toInt(data.get("special"), current.special) : current.special + }); + } + }, + cancel: { + label: "Cancel", + callback: () => resolve(null) + } + }, + default: "save", + close: () => resolve(null) + }).render(true); + }); + + if (!result) return; + + const requestedTotal = result.education + result.pursuits + result.special; + if (requestedTotal > maxByCap) { + ui.notifications?.warn(`Allocation exceeds this skill's root-cap allowance (${maxByCap}).`); + return; + } + + if (result.education > maxEducation || result.pursuits > maxPursuits || result.special > maxSpecial) { + ui.notifications?.warn("Allocation exceeds remaining pool points."); + return; + } + + const nextState = foundry.utils.deepClone(context.state) as BuilderState; + nextState.baseBySkillId[skill.id] = meta.baseValue; + if (requestedTotal <= 0) { + delete nextState.allocations[skill.id]; + } else { + nextState.allocations[skill.id] = result; + } + + await this.updateBuilderState(nextState); + await this.syncSkillsFromBuilderState(nextState); + } + + private async rollCharacteristicCheck(characterKey: string, label: string): Promise { + const validKeys = new Set(["str", "mas", "con", "int", "pow", "dex", "app", "edu", "age"]); + if (!validKeys.has(characterKey)) return; + + const base = Math.max(0, toInt((this.actor as any).system?.characteristics?.[characterKey], 0)); + const content = ` +
+
+ + +
+
+ + +
+
+ + +
+

Roll d100 against Characteristic x Multiplier.

+
+ `; + + const multiplier = await new Promise((resolve) => { + let settled = false; + const done = (value: number | null) => { + if (settled) return; + settled = true; + resolve(value); + }; + + new Dialog({ + title: `${label} Check`, + content, + buttons: { + roll: { + label: "Roll", + callback: (dlgHtml: JQuery) => { + const selected = Number((dlgHtml[0]?.querySelector("select[name='multiplier']") as HTMLSelectElement | null)?.value || 1); + const safeMultiplier = Math.max(1, Math.min(7, Math.floor(selected || 1))); + done(safeMultiplier); + } + }, + cancel: { + label: "Cancel", + callback: () => done(null) + } + }, + default: "roll", + close: () => done(null) + }).render(true); + }); + + if (!multiplier) return; + const target = clampPercent(base * multiplier); + const result = await rollPercentile(target); + + await postSkillRollCard({ + actor: this.actor, + actorName: this.actor.name, + actorImg: this.actor.img, + skillName: `${label} x${multiplier}`, + skillImg: DEFAULT_SKILL_ICON, + roll: result.roll, + target: result.target, + outcomeLabel: outcomeLabel(result.outcome), + isSuccess: result.isSuccess, + isSpecial: result.isSpecial, + isFumble: result.isFumble + }); + } + + activateListeners(html: JQuery): void { + super.activateListeners(html); + + html.find(".toggle-sheet-lock").on("click", async (event) => { + event.preventDefault(); + const next = !this.isToolsUnlocked(); + await this.actor.update({ "system.sheetToolsUnlocked": next }); + ui.notifications?.info(next ? "Sheet tools unlocked." : "Sheet tools locked."); + }); + + html.find(".roll-characteristic").on("click", async (event) => { + event.preventDefault(); + const button = event.currentTarget as HTMLElement; + const key = String(button.dataset.char || "").trim().toLowerCase(); + const label = String(button.dataset.label || key.toUpperCase()); + if (!key) return; + await this.rollCharacteristicCheck(key, label); + }); + + html.find(".item-create").on("click", async (event) => { + event.preventDefault(); + const type = String((event.currentTarget as HTMLElement).dataset.type || "gear"); + const doc: any = { name: `New ${type}`, type }; + if (type === "skill") { + doc.img = DEFAULT_SKILL_ICON; + doc.system = { value: 0, category: "Technical", specialization: "" }; + } + await this.actor.createEmbeddedDocuments("Item", [doc]); + }); + + html.find(".item-edit").on("click", (event) => { + event.preventDefault(); + const li = (event.currentTarget as HTMLElement).closest(".item"); + const item = li ? this.actor.items.get(li.dataset.itemId ?? "") : null; + item?.sheet?.render(true); + }); + + html.find(".item-delete").on("click", async (event) => { + event.preventDefault(); + const li = (event.currentTarget as HTMLElement).closest(".item"); + if (!li?.dataset.itemId) return; + await this.actor.deleteEmbeddedDocuments("Item", [li.dataset.itemId]); + }); + + html.find(".item-roll-skill").on("click", async (event) => { + event.preventDefault(); + const li = (event.currentTarget as HTMLElement).closest(".item"); + if (!li?.dataset.itemId) return; + await (this.actor as any).rollSkillById(li.dataset.itemId); + }); + + html.find(".roll-derived").on("click", async (event) => { + event.preventDefault(); + const key = String((event.currentTarget as HTMLElement).dataset.check || ""); + if (!key) return; + await (this.actor as any).rollDerivedCheck(key); + }); + + html.find(".item-use-weapon").on("click", async (event) => { + event.preventDefault(); + const li = (event.currentTarget as HTMLElement).closest(".item"); + if (!li?.dataset.itemId) return; + await (this.actor as any).useWeaponById(li.dataset.itemId); + }); + + html.find(".item-toggle-equipped").on("click", async (event) => { + event.preventDefault(); + const li = (event.currentTarget as HTMLElement).closest(".item"); + if (!li?.dataset.itemId) return; + const item = this.actor.items.get(li.dataset.itemId); + if (!item) return; + + const nextEquipped = !Boolean(item.system?.equipped); + const update: Record = { + _id: item.id, + "system.equipped": nextEquipped + }; + if (nextEquipped) { + update["system.stowed"] = false; + } + await this.actor.updateEmbeddedDocuments("Item", [update]); + }); + + html.find(".item-toggle-stowed").on("click", async (event) => { + event.preventDefault(); + const li = (event.currentTarget as HTMLElement).closest(".item"); + if (!li?.dataset.itemId) return; + const item = this.actor.items.get(li.dataset.itemId); + if (!item) return; + + const nextStowed = !Boolean(item.system?.stowed); + const update: Record = { + _id: item.id, + "system.stowed": nextStowed + }; + if (nextStowed) { + update["system.equipped"] = false; + } + await this.actor.updateEmbeddedDocuments("Item", [update]); + }); + + html.find(".roll-hit-location").on("click", async (event) => { + event.preventDefault(); + await (this.actor as any).rollHitLocation(); + }); + + html.find(".reset-conditions").on("click", async (event) => { + event.preventDefault(); + await this.actor.update({ + "system.conditions.prone": false, + "system.conditions.unconscious": false, + "system.conditions.dead": false, + "system.conditions.dying": false, + "system.conditions.unusableLimbs.rArm": false, + "system.conditions.unusableLimbs.lArm": false, + "system.conditions.unusableLimbs.rLeg": false, + "system.conditions.unusableLimbs.lLeg": false + }); + }); + + html.find(".populate-base-skills").on("click", async (event) => { + event.preventDefault(); + const added = await (this.actor as any).ensureBaseSkills(); + if (added > 0) ui.notifications?.info(`Added ${added} Ringworld base skills.`); + else ui.notifications?.info("All Ringworld base skills are already present."); + }); + + html.find(".add-base-skill").on("click", async (event) => { + event.preventDefault(); + const button = event.currentTarget as HTMLElement; + const name = String(button.dataset.skillName || ""); + const category = String(button.dataset.skillCategory || ""); + const base = Number(button.dataset.skillBase || 0); + const specialization = String(button.dataset.skillSpecialization || ""); + if (!name) return; + + await this.actor.createEmbeddedDocuments("Item", [{ + name, + type: "skill", + img: skillIconPath(name), + system: { value: base, category, specialization } + }]); + }); + + html.find(".builder-add-pursuit").on("click", async (event) => { + event.preventDefault(); + const root = (event.currentTarget as HTMLElement).closest(".rw-builder-pursuits"); + if (!root) return; + const name = String((root.querySelector(".builder-new-pursuit") as HTMLSelectElement | null)?.value || ""); + const years = Number((root.querySelector(".builder-new-years") as HTMLInputElement | null)?.value || 0); + await this.addOrUpdatePursuit(name, years); + }); + + html.find(".builder-update-pursuit").on("click", async (event) => { + event.preventDefault(); + const row = (event.currentTarget as HTMLElement).closest(".builder-pursuit-row"); + if (!row) return; + const name = String((row.querySelector(".builder-pursuit-name") as HTMLSelectElement | null)?.value || ""); + const years = Number((row.querySelector(".builder-pursuit-years") as HTMLInputElement | null)?.value || 0); + await this.addOrUpdatePursuit(name, years); + }); + + html.find(".builder-remove-pursuit").on("click", async (event) => { + event.preventDefault(); + const index = Number((event.currentTarget as HTMLElement).dataset.index ?? -1); + if (!Number.isFinite(index) || index < 0) return; + await this.removePursuit(index); + }); + + html.find(".builder-reset-points").on("click", async (event) => { + event.preventDefault(); + await this.resetBuilderAllocations(); + ui.notifications?.info("Character-builder allocations reset."); + }); + + html.find(".roll-characteristics").on("click", async (event) => { + event.preventDefault(); + if (!this.isToolsUnlocked()) return; + await (this.actor as any).rollCharacteristics?.(); + ui.notifications?.info("Characteristics rolled."); + }); + + html.find(".builder-auto-allocate").on("click", async (event) => { + event.preventDefault(); + if (!this.isToolsUnlocked()) return; + await (this.actor as any).ensureBaseSkills?.(); + await this.randomizeBuilderAllocations(); + }); + + html.find(".builder-skill-allocate").on("click", async (event) => { + event.preventDefault(); + const li = (event.currentTarget as HTMLElement).closest(".item"); + const skillId = String(li?.dataset.itemId || ""); + if (!skillId) return; + await this.promptSkillAllocation(skillId); + }); + } + + protected async _onDropItemCreate(itemData: any): Promise { + const items = Array.isArray(itemData) ? itemData : [itemData]; + const speciesItems = items.filter((entry: any) => entry?.type === "species"); + const createItems = items.filter((entry: any) => entry?.type !== "species"); + + for (const species of speciesItems) { + await (this.actor as any).applySpeciesItem?.(species); + } + if (speciesItems.length > 0) { + const names = speciesItems.map((entry: any) => String(entry?.name || "Species")).join(", "); + ui.notifications?.info(`Applied species to actor: ${names}`); + } + + if (createItems.length === 0) return []; + + const patched = createItems.map((entry: any) => { + if (entry?.type !== "skill") return entry; + return { + ...entry, + img: resolveSkillIcon(String(entry?.name || "Skill"), String(entry?.img || "")) + }; + }); + return super._onDropItemCreate(Array.isArray(itemData) ? patched : patched[0]); + } +} diff --git a/src/module/sheets/actors/VehicleSheet.ts b/src/module/sheets/actors/VehicleSheet.ts new file mode 100644 index 0000000..369d0a8 --- /dev/null +++ b/src/module/sheets/actors/VehicleSheet.ts @@ -0,0 +1,23 @@ +const BaseActorSheet = (foundry as any).appv1.sheets.ActorSheet; + +export class RingworldVehicleSheet extends BaseActorSheet { + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "actor", "vehicle"], + width: 680, + height: 560 + }); + } + + get template(): string { + return "systems/ringworld/templates/actor/vehicle-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + return { + ...data, + system: (this.actor as any).system + }; + } +} diff --git a/src/module/sheets/items/ArmorSheet.ts b/src/module/sheets/items/ArmorSheet.ts new file mode 100644 index 0000000..17c6c97 --- /dev/null +++ b/src/module/sheets/items/ArmorSheet.ts @@ -0,0 +1,23 @@ +const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet; + +export class RingworldArmorSheet extends BaseItemSheet { + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "armor"], + width: 620, + height: 560 + }); + } + + get template(): string { + return "systems/ringworld/templates/item/armor-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + return { + ...data, + system: (this.item as any).system + }; + } +} diff --git a/src/module/sheets/items/GearSheet.ts b/src/module/sheets/items/GearSheet.ts new file mode 100644 index 0000000..ac5e329 --- /dev/null +++ b/src/module/sheets/items/GearSheet.ts @@ -0,0 +1,23 @@ +const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet; + +export class RingworldGearSheet extends BaseItemSheet { + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "gear"], + width: 620, + height: 520 + }); + } + + get template(): string { + return "systems/ringworld/templates/item/gear-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + return { + ...data, + system: (this.item as any).system + }; + } +} diff --git a/src/module/sheets/items/PursuitSheet.ts b/src/module/sheets/items/PursuitSheet.ts new file mode 100644 index 0000000..b7e1f54 --- /dev/null +++ b/src/module/sheets/items/PursuitSheet.ts @@ -0,0 +1,39 @@ +const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet; + +function asArray(value: unknown): string[] { + return Array.isArray(value) ? value.map((entry) => String(entry)) : []; +} + +export class RingworldPursuitSheet extends BaseItemSheet { + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "pursuit"], + width: 680, + height: 620 + }); + } + + get template(): string { + return "systems/ringworld/templates/item/pursuit-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + const system = (this.item as any).system ?? {}; + const fixedSkills = asArray(system.fixedSkills); + const optionalGroups = Array.isArray(system.optionalSkillGroups) + ? system.optionalSkillGroups.map((group: any) => ({ + choose: Number(group?.choose ?? 1), + skills: asArray(group?.skills) + })) + : []; + + return { + ...data, + system, + fixedSkills, + optionalGroups + }; + } +} + diff --git a/src/module/sheets/items/SkillSheet.ts b/src/module/sheets/items/SkillSheet.ts new file mode 100644 index 0000000..8e802d9 --- /dev/null +++ b/src/module/sheets/items/SkillSheet.ts @@ -0,0 +1,31 @@ +const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet; + +export class RingworldSkillSheet extends BaseItemSheet { + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "skill"], + width: 520, + height: 420 + }); + } + + get template(): string { + return "systems/ringworld/templates/item/skill-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + return { + ...data, + system: (this.item as any).system + }; + } + + activateListeners(html: JQuery): void { + super.activateListeners(html); + html.find(".roll-skill").on("click", async (event) => { + event.preventDefault(); + await (this.item as any).rollSkill(); + }); + } +} diff --git a/src/module/sheets/items/SpeciesSheet.ts b/src/module/sheets/items/SpeciesSheet.ts new file mode 100644 index 0000000..48d69dc --- /dev/null +++ b/src/module/sheets/items/SpeciesSheet.ts @@ -0,0 +1,23 @@ +const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet; + +export class RingworldSpeciesSheet extends BaseItemSheet { + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "species"], + width: 620, + height: 560 + }); + } + + get template(): string { + return "systems/ringworld/templates/item/species-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + return { + ...data, + system: (this.item as any).system + }; + } +} diff --git a/src/module/sheets/items/WeaponSheet.ts b/src/module/sheets/items/WeaponSheet.ts new file mode 100644 index 0000000..e41634b --- /dev/null +++ b/src/module/sheets/items/WeaponSheet.ts @@ -0,0 +1,35 @@ +const BaseItemSheet = (foundry as any).appv1.sheets.ItemSheet; + +export class RingworldWeaponSheet extends BaseItemSheet { + static get defaultOptions(): any { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["ringworld", "sheet", "item", "weapon"], + width: 620, + height: 620 + }); + } + + get template(): string { + return "systems/ringworld/templates/item/weapon-sheet.hbs"; + } + + async getData(options?: any): Promise { + const data = await super.getData(options); + const actor = this.item.actor as any; + + return { + ...data, + system: (this.item as any).system, + skills: actor ? actor.items.filter((item: any) => item.type === "skill") : [], + powerPacks: actor ? actor.items.filter((item: any) => item.type === "gear" && item.system.isPowerPack) : [] + }; + } + + activateListeners(html: JQuery): void { + super.activateListeners(html); + html.find(".use-weapon").on("click", async (event) => { + event.preventDefault(); + await (this.item as any).useWeapon(); + }); + } +} diff --git a/src/module/skill-icons.ts b/src/module/skill-icons.ts new file mode 100644 index 0000000..9c347bf --- /dev/null +++ b/src/module/skill-icons.ts @@ -0,0 +1,27 @@ +import { normalizeSkillName, RINGWORLD_BASE_SKILLS } from "./data-base-skills"; + +export const DEFAULT_SKILL_ICON = "systems/ringworld/assets/icons/skill.svg"; + +function iconSlug(name: string): string { + return String(name) + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); +} + +export function skillIconPath(name: string): string { + return `systems/ringworld/assets/icons/skill/${iconSlug(name)}.svg`; +} + +export function baseSkillByName(name: string): (typeof RINGWORLD_BASE_SKILLS)[number] | undefined { + const key = normalizeSkillName(name); + return RINGWORLD_BASE_SKILLS.find((skill) => normalizeSkillName(skill.name) === key); +} + +export function resolveSkillIcon(name: string, currentImg?: string): string { + const img = String(currentImg || "").trim(); + if (img && img !== "icons/svg/item-bag.svg") return img; + if (baseSkillByName(name)) return skillIconPath(name); + return DEFAULT_SKILL_ICON; +} + diff --git a/src/module/utils/dice.ts b/src/module/utils/dice.ts new file mode 100644 index 0000000..4a7f8e5 --- /dev/null +++ b/src/module/utils/dice.ts @@ -0,0 +1,16 @@ +export async function showDiceSoNice(roll: Roll): Promise { + const dice3d = (game as any)?.dice3d; + if (!dice3d?.showForRoll) return; + + try { + await dice3d.showForRoll(roll, game.user, true, null, false); + } catch (_error) { + // Ignore Dice So Nice integration failures and continue normal flow. + } +} + +export async function evaluateRollWithDice(formula: string): Promise { + const roll = await new Roll(formula).evaluate(); + await showDiceSoNice(roll as any); + return roll as any; +} diff --git a/src/module/utils/hit-locations.ts b/src/module/utils/hit-locations.ts new file mode 100644 index 0000000..c749194 --- /dev/null +++ b/src/module/utils/hit-locations.ts @@ -0,0 +1,130 @@ +import { HIT_LOCATION_TABLE, type HitLocationEntry, type HitLocationSlug } from "../constants"; +import { evaluateRollWithDice } from "./dice"; +import { getActorHitLocationSchema, getHitLocationSchemaById } from "../hit-location-schemas"; + +export interface HitLocationRollResult { + roll: number; + location: HitLocationEntry; +} + +export function getHitLocationFromRoll(roll: number): HitLocationEntry { + const found = HIT_LOCATION_TABLE.find((entry) => roll >= entry.min && roll <= entry.max); + return found ?? HIT_LOCATION_TABLE[HIT_LOCATION_TABLE.length - 1]; +} + +function getTableForActor(actor?: any): HitLocationEntry[] { + if (!actor) return HIT_LOCATION_TABLE; + const schema = getActorHitLocationSchema(actor); + return schema.entries; +} + +function getHitLocationFromRollForTable(roll: number, table: HitLocationEntry[]): HitLocationEntry { + const found = table.find((entry) => roll >= entry.min && roll <= entry.max); + return found ?? table[table.length - 1]; +} + +export async function rollHitLocation(actor?: any): Promise { + const roll = await evaluateRollWithDice("1d20"); + const total = roll.total ?? 20; + const table = getTableForActor(actor); + return { + roll: total, + location: getHitLocationFromRollForTable(total, table) + }; +} + +export async function chooseOrRollHitLocation(actor?: any): Promise<{ + slug: HitLocationSlug; + label: string; + rolled: number | null; +} | null> { + const schema = actor ? getActorHitLocationSchema(actor) : getHitLocationSchemaById("humanoid"); + const table = schema.entries; + const entriesBySlug = new Map(table.map((entry) => [entry.slug, entry])); + const labelFor = (slug: HitLocationSlug, fallback: string) => entriesBySlug.get(slug)?.label ?? fallback; + + const html = ` +
+ +
+ +
+
+ + + + + + + +
+
+ `; + + const result = await new Promise((resolve) => { + let settled = false; + const done = (value: string | null) => { + if (settled) return; + settled = true; + resolve(value); + }; + + const dialog = new Dialog({ + title: "Choose Hit Location", + content: html, + buttons: { + apply: { + label: "Apply", + callback: (dlgHtml: JQuery) => { + const value = String(dlgHtml.find("input[name='location']").val() || "random"); + done(value); + } + }, + cancel: { + label: "Cancel", + callback: () => done(null) + } + }, + default: "apply", + render: (dlgHtml: JQuery) => { + const picker = dlgHtml.find(".ringworld-hit-picker"); + picker.find(".rw-hit-picker-choice").on("click", (event) => { + event.preventDefault(); + const button = event.currentTarget as HTMLElement; + const location = String(button.dataset.location || "random"); + picker.find(".rw-hit-picker-choice").removeClass("active"); + $(button).addClass("active"); + picker.find("input[name='location']").val(location); + }); + }, + close: () => done(null) + }, { + classes: ["ringworld-hit-location-dialog"], + width: 500, + resizable: false + }); + dialog.render(true); + }); + + if (!result) return null; + + if (result === "random") { + const rolled = await rollHitLocation(actor); + return { + slug: rolled.location.slug, + label: rolled.location.label, + rolled: rolled.roll + }; + } + + const entry = table.find((loc) => loc.slug === result); + if (!entry) return null; + + return { + slug: entry.slug, + label: entry.label, + rolled: null + }; +} diff --git a/src/module/utils/percentile.ts b/src/module/utils/percentile.ts new file mode 100644 index 0000000..b5062ab --- /dev/null +++ b/src/module/utils/percentile.ts @@ -0,0 +1,54 @@ +import { evaluateRollWithDice } from "./dice"; + +export type PercentileOutcome = "special" | "success" | "failure" | "fumble"; + +export interface PercentileResult { + roll: number; + target: number; + outcome: PercentileOutcome; + isSuccess: boolean; + isSpecial: boolean; + isFumble: boolean; +} + +export async function rollPercentile(target: number): Promise { + const roll = await evaluateRollWithDice("1d100"); + return classifyPercentile(roll.total ?? 0, target); +} + +export function classifyPercentile(roll: number, target: number): PercentileResult { + const cleanTarget = Math.max(0, Math.floor(Number(target) || 0)); + const specialThreshold = Math.max(1, Math.floor(cleanTarget / 5)); + const fumbleThreshold = cleanTarget >= 50 ? 100 : 96; + + const isSuccess = roll <= cleanTarget; + const isSpecial = isSuccess && roll <= specialThreshold; + const isFumble = !isSuccess && roll >= fumbleThreshold; + + let outcome: PercentileOutcome = "failure"; + if (isFumble) outcome = "fumble"; + else if (isSpecial) outcome = "special"; + else if (isSuccess) outcome = "success"; + + return { + roll, + target: cleanTarget, + outcome, + isSuccess, + isSpecial, + isFumble + }; +} + +export function outcomeLabel(outcome: PercentileOutcome): string { + switch (outcome) { + case "special": + return "Special Success"; + case "success": + return "Success"; + case "fumble": + return "Fumble"; + default: + return "Failure"; + } +} diff --git a/src/module/world-catalog.ts b/src/module/world-catalog.ts new file mode 100644 index 0000000..bbe2310 --- /dev/null +++ b/src/module/world-catalog.ts @@ -0,0 +1,978 @@ +import { SYSTEM_ID } from "./constants"; +import { normalizeSkillName, RINGWORLD_BASE_SKILLS } from "./data-base-skills"; +import { skillIconPath } from "./skill-icons"; + +export type GravityType = "light" | "normal" | "heavy"; + +interface SpeciesCatalogEntry { + name: string; + playable: boolean; + gravity: GravityType; + homeworld: string; + modifiers: { + str: number; + mas: number; + con: number; + int: number; + pow: number; + dex: number; + app: number; + edu: number; + age: number; + }; + notes: string; +} + +interface WeaponCatalogEntry { + name: string; + equipped?: boolean; + weight?: number; + linkedSkill: string; + attackType: "melee" | "ranged"; + damageFormula: string; + range: { close: string; medium: string; long: string }; + energyUsedPerAttack: number; + ammoUsedPerAttack: number; + powerSource: "actor" | "powerPack"; + notes: string; +} + +interface ArmorCatalogEntry { + name: string; + equipped: boolean; + weight?: number; + value: number; + coverage: Record; + notes: string; +} + +interface GearCatalogEntry { + name: string; + equipped?: boolean; + weight: number; + cost: number; + energyUse: number; + powerSupplyNotes: string; + isPowerPack: boolean; + energy: { value: number; max: number }; + notes: string; +} + +interface PursuitCatalogEntry { + name: string; + rollRange: string; + wealthValue: number; + occupationPointsPerYear: number; + fixedSkills: string[]; + optionalSkillGroups?: Array<{ + choose: number; + skills: string[]; + }>; + notes: string; +} + +interface CatalogSeed { + key: string; + data: any; + folderKey: "species" | "skills" | "pursuits" | "gear" | "armor" | "weaponMelee" | "weaponRanged"; + img: string; +} + +export const RINGWORLD_CATALOG_VERSION = 9; + +const ICONS = { + speciesDefault: "systems/ringworld/assets/icons/species.svg", + skillDefault: "systems/ringworld/assets/icons/skill.svg", + pursuitDefault: "systems/ringworld/assets/icons/pursuit.svg", + weaponMelee: "systems/ringworld/assets/icons/weapon-melee.svg", + weaponRanged: "systems/ringworld/assets/icons/weapon-ranged.svg", + armor: "systems/ringworld/assets/icons/armor.svg", + gearDefault: "systems/ringworld/assets/icons/gear.svg", + powerPackDefault: "systems/ringworld/assets/icons/power-pack.svg" +}; + +function iconSlug(name: string): string { + return String(name) + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); +} + +function speciesIconPath(name: string): string { + return `systems/ringworld/assets/icons/species/${iconSlug(name)}.svg`; +} + +function weaponIconPath(name: string): string { + return `systems/ringworld/assets/icons/weapon/${iconSlug(name)}.svg`; +} + +function armorIconPath(name: string): string { + return `systems/ringworld/assets/icons/armor/${iconSlug(name)}.svg`; +} + +function gearIconPath(name: string): string { + return `systems/ringworld/assets/icons/gear/${iconSlug(name)}.svg`; +} + +function pursuitIconPath(name: string): string { + return `systems/ringworld/assets/icons/pursuit/${iconSlug(name)}.svg`; +} + +const allCoverage = { + rLeg: true, + lLeg: true, + abdomen: true, + chest: true, + rArm: true, + lArm: true, + head: true +}; + +const torsoCoverage = { + rLeg: false, + lLeg: false, + abdomen: true, + chest: true, + rArm: false, + lArm: false, + head: false +}; + +const headCoverage = { + rLeg: false, + lLeg: false, + abdomen: false, + chest: false, + rArm: false, + lArm: false, + head: true +}; + +const armCoverage = { + rLeg: false, + lLeg: false, + abdomen: false, + chest: false, + rArm: true, + lArm: true, + head: false +}; + +const legCoverage = { + rLeg: true, + lLeg: true, + abdomen: false, + chest: false, + rArm: false, + lArm: false, + head: false +}; + +export const SPECIES_CATALOG: SpeciesCatalogEntry[] = [ + { + name: "Human", + playable: true, + gravity: "normal", + homeworld: "Known Space", + modifiers: { str: 0, mas: 0, con: 0, int: 0, pow: 0, dex: 0, app: 0, edu: 0, age: 0 }, + notes: "Baseline human explorer profile." + }, + { + name: "Earth Human", + playable: true, + gravity: "normal", + homeworld: "Earth", + modifiers: { str: 0, mas: 0, con: 0, int: 0, pow: 1, dex: 0, app: 0, edu: 0, age: 0 }, + notes: "Earth-line humans with stronger latent psionic potential." + }, + { + name: "Kzinti", + playable: true, + gravity: "heavy", + homeworld: "Kzin", + modifiers: { str: 2, mas: 2, con: 1, int: -1, pow: 0, dex: 0, app: -2, edu: 0, age: 0 }, + notes: "Predatory felinoids with high physical strength." + }, + { + name: "Pierson's Puppeteer", + playable: true, + gravity: "light", + homeworld: "Fleet of Worlds", + modifiers: { str: -2, mas: -2, con: -1, int: 2, pow: 1, dex: 1, app: 0, edu: 1, age: 0 }, + notes: "Highly intelligent, cautious engineers." + }, + { + name: "Dolphin", + playable: true, + gravity: "normal", + homeworld: "Human Space Oceans", + modifiers: { str: -2, mas: 1, con: 1, int: 0, pow: 0, dex: 0, app: 0, edu: 0, age: 0 }, + notes: "Aquatic specialists with enhanced endurance." + }, + { + name: "Trinoc", + playable: true, + gravity: "light", + homeworld: "Trinoc Worlds", + modifiers: { str: -1, mas: -1, con: 0, int: 1, pow: 0, dex: 1, app: 0, edu: 0, age: 0 }, + notes: "Three-eyed flyers with excellent perception." + }, + { + name: "Martian", + playable: true, + gravity: "light", + homeworld: "Mars", + modifiers: { str: -1, mas: -1, con: -1, int: 1, pow: 0, dex: 1, app: 0, edu: 0, age: 0 }, + notes: "Light-gravity adapted intellect-focused profile." + }, + { + name: "Pierin", + playable: true, + gravity: "normal", + homeworld: "Pierin Space", + modifiers: { str: 0, mas: 0, con: 0, int: 1, pow: 0, dex: 0, app: 1, edu: 0, age: 0 }, + notes: "Socially adept, technically curious explorers." + }, + { + name: "Boat People", + playable: true, + gravity: "normal", + homeworld: "Ringworld", + modifiers: { str: 0, mas: 0, con: 1, int: 0, pow: 0, dex: 0, app: 0, edu: -1, age: 0 }, + notes: "Ringworld hominids adapted to river and sea cultures." + }, + { + name: "Builder", + playable: true, + gravity: "normal", + homeworld: "Ringworld", + modifiers: { str: 1, mas: 1, con: 0, int: 0, pow: 0, dex: 0, app: -1, edu: 0, age: 0 }, + notes: "Ringworld labor stock with robust build." + }, + { + name: "Ghoul", + playable: true, + gravity: "normal", + homeworld: "Ringworld", + modifiers: { str: 0, mas: 0, con: 1, int: -1, pow: 0, dex: 1, app: -2, edu: -1, age: 0 }, + notes: "Scavenger subspecies with strong survival instincts." + }, + { + name: "Bandersnatchi", + playable: false, + gravity: "heavy", + homeworld: "Known Space", + modifiers: { str: 4, mas: 4, con: 3, int: 0, pow: 0, dex: -2, app: -3, edu: 0, age: 10 }, + notes: "Massive engineered beings; typically GM creature profile." + } +]; + +export const WEAPON_CATALOG: WeaponCatalogEntry[] = [ + { + name: "Variable Sword", + equipped: true, + weight: 1.5, + linkedSkill: "V. Sword, F. Laser", + attackType: "melee", + damageFormula: "1d10+1", + range: { close: "Melee", medium: "-", long: "-" }, + energyUsedPerAttack: 1, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "High-energy blade weapon." + }, + { + name: "Monofilament Knife", + equipped: true, + weight: 0.2, + linkedSkill: "Archaic Melee Weapon", + attackType: "melee", + damageFormula: "1d6+1", + range: { close: "Melee", medium: "-", long: "-" }, + energyUsedPerAttack: 0, + ammoUsedPerAttack: 0, + powerSource: "actor", + notes: "Fine-edge close combat blade." + }, + { + name: "Shock Baton", + equipped: true, + weight: 1.2, + linkedSkill: "Unarmed Combat", + attackType: "melee", + damageFormula: "1d8+1", + range: { close: "Melee", medium: "-", long: "-" }, + energyUsedPerAttack: 1, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Close-quarters stun baton." + }, + { + name: "Flashlight Laser", + equipped: true, + weight: 0.8, + linkedSkill: "V. Sword, F. Laser", + attackType: "ranged", + damageFormula: "1d10", + range: { close: "Short", medium: "Medium", long: "Long" }, + energyUsedPerAttack: 2, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Compact laser unit." + }, + { + name: "Laser Pistol", + equipped: true, + weight: 1.4, + linkedSkill: "Handgun, Energy", + attackType: "ranged", + damageFormula: "1d10+1", + range: { close: "Short", medium: "Medium", long: "Long" }, + energyUsedPerAttack: 2, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Common energy sidearm." + }, + { + name: "Laser Rifle", + equipped: true, + weight: 4.5, + linkedSkill: "Heavy Weapon, Energy", + attackType: "ranged", + damageFormula: "2d6+2", + range: { close: "Short", medium: "Long", long: "Extreme" }, + energyUsedPerAttack: 3, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Standard long-arm laser weapon." + }, + { + name: "Stunner", + equipped: true, + weight: 1.0, + linkedSkill: "Handgun, Energy", + attackType: "ranged", + damageFormula: "1d6", + range: { close: "Short", medium: "Medium", long: "Long" }, + energyUsedPerAttack: 1, + ammoUsedPerAttack: 0, + powerSource: "powerPack", + notes: "Low-lethality disruption sidearm." + }, + { + name: "Handgun (Projectile)", + equipped: true, + weight: 1.2, + linkedSkill: "Handgun, Projectile", + attackType: "ranged", + damageFormula: "1d10", + range: { close: "Short", medium: "Medium", long: "Long" }, + energyUsedPerAttack: 0, + ammoUsedPerAttack: 1, + powerSource: "actor", + notes: "Conventional projectile sidearm." + }, + { + name: "Rifle (Projectile)", + equipped: true, + weight: 5.0, + linkedSkill: "Heavy Weapon, Projectile", + attackType: "ranged", + damageFormula: "2d6", + range: { close: "Short", medium: "Long", long: "Extreme" }, + energyUsedPerAttack: 0, + ammoUsedPerAttack: 1, + powerSource: "actor", + notes: "Conventional projectile rifle." + }, + { + name: "Slug Thrower Carbine", + equipped: true, + weight: 3.8, + linkedSkill: "Heavy Weapon, Projectile", + attackType: "ranged", + damageFormula: "2d6+1", + range: { close: "Short", medium: "Long", long: "Extreme" }, + energyUsedPerAttack: 0, + ammoUsedPerAttack: 1, + powerSource: "actor", + notes: "Compact projectile carbine." + } +]; + +export const ARMOR_CATALOG: ArmorCatalogEntry[] = [ + { name: "Cloth Vest", equipped: true, weight: 2, value: 1, coverage: torsoCoverage, notes: "Basic torso protection." }, + { name: "Soft Combat Vest", equipped: true, weight: 4, value: 2, coverage: torsoCoverage, notes: "Flexible tactical vest." }, + { name: "Combat Harness", equipped: true, weight: 5, value: 2, coverage: { ...torsoCoverage, rArm: true, lArm: true }, notes: "Torso + upper arms." }, + { name: "Armored Coat", equipped: true, weight: 7, value: 3, coverage: { ...torsoCoverage, rArm: true, lArm: true }, notes: "Heavy coat with arm plating." }, + { name: "Leg Guards", equipped: true, weight: 4, value: 2, coverage: legCoverage, notes: "Lower-body armor." }, + { name: "Arm Bracers", equipped: true, weight: 2, value: 2, coverage: armCoverage, notes: "Forearm and upper arm guards." }, + { name: "Combat Helmet", equipped: true, weight: 1.5, value: 2, coverage: headCoverage, notes: "Cranial protection." }, + { name: "Full Combat Suit", equipped: true, weight: 12, value: 3, coverage: allCoverage, notes: "Full-body armored suit." }, + { name: "Impact Suit", equipped: true, weight: 16, value: 4, coverage: allCoverage, notes: "High-end impact and hazard suit." } +]; + +export const GEAR_CATALOG: GearCatalogEntry[] = [ + { + name: "Power Pack R20", + weight: 1, + cost: 100, + energyUse: 0, + powerSupplyNotes: "Rechargeable power pack", + isPowerPack: true, + energy: { value: 20, max: 20 }, + notes: "Standard personal battery pack." + }, + { + name: "Power Pack R50", + weight: 2, + cost: 220, + energyUse: 0, + powerSupplyNotes: "Rechargeable power pack", + isPowerPack: true, + energy: { value: 50, max: 50 }, + notes: "Extended personal battery pack." + }, + { + name: "Power Pack R100", + weight: 4, + cost: 400, + energyUse: 0, + powerSupplyNotes: "Rechargeable power pack", + isPowerPack: true, + energy: { value: 100, max: 100 }, + notes: "High-capacity field battery." + }, + { name: "Portable Generator", weight: 8, cost: 700, energyUse: 0, powerSupplyNotes: "Provides charging capacity", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Portable recharge generator." }, + { name: "MicroMind Unit", weight: 0.5, cost: 900, energyUse: 1, powerSupplyNotes: "Requires external power", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Portable advanced computation module." }, + { name: "Memory Bubble Reader", weight: 0.5, cost: 450, energyUse: 1, powerSupplyNotes: "Standard battery compatible", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Reads and writes memory bubbles." }, + { name: "Autodoc (Portable)", weight: 18, cost: 3500, energyUse: 6, powerSupplyNotes: "High draw medical station", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Field medical support unit." }, + { name: "Medkit", weight: 1, cost: 120, energyUse: 0, powerSupplyNotes: "None", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Manual emergency treatment supplies." }, + { name: "Bioscanner", weight: 1, cost: 300, energyUse: 1, powerSupplyNotes: "Battery or generator", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Vitals and hazard scanning tool." }, + { name: "Survey Scanner", weight: 2, cost: 500, energyUse: 2, powerSupplyNotes: "Battery or generator", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Terrain and structure analysis scanner." }, + { name: "Translator Module", weight: 0.5, cost: 650, energyUse: 1, powerSupplyNotes: "Battery compatible", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Linguistic conversion and support." }, + { name: "Holo Projector", weight: 1.5, cost: 800, energyUse: 2, powerSupplyNotes: "Battery compatible", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Short-range holographic projection." }, + { name: "Stasis Box", weight: 12, cost: 2600, energyUse: 5, powerSupplyNotes: "Continuous power required", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Suspended animation container." }, + { name: "Reactionless Flycycle", weight: 35, cost: 4200, energyUse: 4, powerSupplyNotes: "Vehicle power core", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Single-rider reactionless vehicle." }, + { name: "Field Toolkit", weight: 2, cost: 180, energyUse: 0, powerSupplyNotes: "None", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Mechanical and electronics repair kit." }, + { name: "Vac Suit", weight: 7, cost: 1400, energyUse: 1, powerSupplyNotes: "Suit battery required", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Pressure and vacuum survival suit." }, + { name: "Air Reprocessor", weight: 3, cost: 950, energyUse: 2, powerSupplyNotes: "Continuous power", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Atmosphere recycling support unit." }, + { name: "Signal Beacon", weight: 1, cost: 140, energyUse: 1, powerSupplyNotes: "Battery compatible", isPowerPack: false, energy: { value: 0, max: 0 }, notes: "Emergency and navigation beacon." } +]; + +export const PURSUIT_CATALOG: PursuitCatalogEntry[] = [ + { + name: "Artist", + rollRange: "01-07", + wealthValue: 0, + occupationPointsPerYear: 20, + fixedSkills: ["Athletics", "Computers", "Fast Talk", "Fine Arts", "Hide", "Musicianship", "Observe", "Perform", "Psychology", "Scent", "Theology"], + notes: "Creative specialist pursuit." + }, + { + name: "Colonist", + rollRange: "08-27", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Atmospheric Craft", "Bargain", "Ground Vehicle", "Farming", "Handgun, Energy", "Personal Flyer", "Listen", "Heavy Weapon, Energy", "Observe", "Planetology", "Search", "Track"], + notes: "Settlement and frontier operations pursuit." + }, + { + name: "Criminal", + rollRange: "28-29", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Computers", "Fast Talk", "Hide", "Personal Flyer", "Handgun, Energy", "Handgun, Projectile", "Sneak", "Unarmed Combat"], + notes: "Underground survival pursuit." + }, + { + name: "Dilettante", + rollRange: "30", + wealthValue: 5, + occupationPointsPerYear: 15, + fixedSkills: [], + notes: "Any skills allowed; uses 15 occupation points per year." + }, + { + name: "Engineer", + rollRange: "31-36", + wealthValue: 2, + occupationPointsPerYear: 20, + fixedSkills: ["Astronomy", "Chemistry", "Computers", "Engineering", "Ground Vehicle", "Mathematics", "Personal Flyer", "Physics", "Planetology", "Reaction Drive", "Reactionless Drive", "Repair"], + notes: "Technical design and maintenance pursuit." + }, + { + name: "Entertainer", + rollRange: "37-38", + wealthValue: 2, + occupationPointsPerYear: 20, + fixedSkills: ["Athletics", "Bargain", "Own Language", "Musicianship", "Observe", "Orate", "Perform", "Psychology", "Search", "Sneak"], + notes: "Performance and social influence pursuit." + }, + { + name: "Explorer", + rollRange: "39-52", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Athletics", "Atmospheric Craft", "Bargain", "Biology", "Botany", "Emergency Treatment", "Engineering", "Ground Vehicle", "Handgun, Energy", "Heavy Weapon, Energy", "Hyperdrive", "Observe", "Planetology", "Reactionless Drive", "Repair", "Search", "Track", "Zoology"], + notes: "Survey and expedition pursuit." + }, + { + name: "Functionary", + rollRange: "53-56", + wealthValue: 3, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Debate", "Fast Talk", "History", "Law", "Observe", "Orate", "Psychology", "Strategy", "Theology"], + notes: "Administrative and governance pursuit." + }, + { + name: "Journalist/Writer", + rollRange: "57-59", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Anthropology", "Bargain", "Computers", "Debate", "History", "Fine Arts", "Own Language", "Law", "Observe", "Psychology", "Search", "Sneak", "Unarmed Combat", "Weapons System"], + notes: "Investigation and reporting pursuit." + }, + { + name: "Miner", + rollRange: "60-65", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Atmospheric Craft", "Aquatic Vehicle", "Bargain", "Climb (Custom)", "Computers", "Emergency Treatment", "Engineering", "Handgun, Energy", "Heavy Weapon, Energy", "Observe", "Personal Flyer", "Planetology", "Reaction Drive", "Reactionless Drive", "Repair", "Search"], + notes: "Resource extraction frontier pursuit." + }, + { + name: "Pilot", + rollRange: "66-68", + wealthValue: 3, + occupationPointsPerYear: 20, + fixedSkills: ["Astronomy", "Atmospheric Craft", "Computers", "Engineering", "Hyperdrive", "History", "Law", "Mathematics", "Physics", "Reaction Drive", "Reactionless Drive", "Repair", "Strategy", "Weapons System"], + notes: "Flight and spacecraft operations pursuit." + }, + { + name: "Police Officer", + rollRange: "69-76", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Atmospheric Craft", "Bargain", "Computers", "Fast Talk", "Emergency Treatment", "Handgun, Energy", "Handgun, Projectile", "Heavy Weapon, Energy", "Heavy Weapon, Projectile", "Law", "Observe", "Personal Flyer", "Psychology", "Search", "Unarmed Combat"], + notes: "Law enforcement and response pursuit." + }, + { + name: "Politician", + rollRange: "77-78", + wealthValue: 3, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Computers", "Debate", "Fast Talk", "History", "Orate", "Own Language", "Perform", "Psychology"], + optionalSkillGroups: [ + { + choose: 1, + skills: ["Engineering", "Farming", "Second Languages", "Strategy", "Theology"] + } + ], + notes: "Public office and influence pursuit." + }, + { + name: "Prisoner", + rollRange: "79-80", + wealthValue: 0, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Fast Talk", "Hide", "Observe", "Psychology", "Search", "Sneak", "Unarmed Combat"], + notes: "Carceral survival pursuit." + }, + { + name: "Scholar", + rollRange: "81-84", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: [], + optionalSkillGroups: [ + { + choose: 2, + skills: ["Anthropology", "Astronomy", "Biology", "Botany", "Chemistry", "Computers", "Engineering", "Fine Arts", "History", "Law", "Mathematics", "Own Language", "Physics", "Planetology", "Second Languages", "Theology", "Zoology"] + } + ], + notes: "Academic pursuit; pick up to two listed skills." + }, + { + name: "Scientific Researcher", + rollRange: "85-94", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: [], + optionalSkillGroups: [ + { + choose: 3, + skills: ["Anthropology", "Astronomy", "Bargain", "Biology", "Botany", "Chemistry", "Computers", "Engineering", "History", "Mathematics", "Observe", "Personal Flyer", "Physics", "Planetology", "Psychology", "Repair", "Search", "Zoology"] + } + ], + notes: "Research pursuit; pick up to three listed skills." + }, + { + name: "Soldier", + rollRange: "95-98", + wealthValue: 1, + occupationPointsPerYear: 20, + fixedSkills: ["Athletics", "Atmospheric Craft", "Aquatic Vehicle", "Computers", "Emergency Treatment", "Fast Talk", "Ground Vehicle", "Handgun, Energy", "Handgun, Projectile", "Heavy Weapon, Energy", "Heavy Weapon, Projectile"], + notes: "Military operations pursuit." + }, + { + name: "Zealot", + rollRange: "99-00", + wealthValue: 0, + occupationPointsPerYear: 20, + fixedSkills: ["Bargain", "Debate", "Fast Talk", "Fine Arts", "Handgun, Energy", "Handgun, Projectile", "Hide", "Own Language", "Law", "Strategy", "Orate", "Psychology", "Theology"], + notes: "Ideological dedication pursuit." + } +]; + +export function speciesCatalogForGeneration(): SpeciesCatalogEntry[] { + return SPECIES_CATALOG.filter((entry) => entry.playable); +} + +function itemKey(type: string, name: string): string { + return `${type}:${normalizeSkillName(name)}`; +} + +function itemTypeAvailable(type: string): boolean { + const directTypes = (getDocumentClass("Item") as any)?.TYPES; + if (Array.isArray(directTypes)) return directTypes.includes(type); + + const docTypes = (game as any)?.system?.documentTypes?.Item; + if (Array.isArray(docTypes)) return docTypes.includes(type); + if (docTypes && typeof docTypes === "object") return Object.prototype.hasOwnProperty.call(docTypes, type); + + const configModels = (CONFIG as any)?.Item?.dataModels; + if (configModels && typeof configModels === "object" && Object.prototype.hasOwnProperty.call(configModels, type)) { + return true; + } + + const gameModels = (game as any)?.model?.Item; + if (gameModels && typeof gameModels === "object" && Object.prototype.hasOwnProperty.call(gameModels, type)) { + return true; + } + + return false; +} + +function toSkillItem(entry: (typeof RINGWORLD_BASE_SKILLS)[number]): CatalogSeed { + return { + key: itemKey("skill", entry.name), + folderKey: "skills", + img: skillIconPath(entry.name) || ICONS.skillDefault, + data: { + name: entry.name, + type: "skill", + system: { + value: entry.base, + category: entry.category, + specialization: entry.specialization ?? "" + } + } + }; +} + +function toSpeciesItem(entry: SpeciesCatalogEntry): CatalogSeed { + const img = speciesIconPath(entry.name) || ICONS.speciesDefault; + return { + key: itemKey("species", entry.name), + folderKey: "species", + img, + data: { + name: entry.name, + type: "species", + system: { + playable: entry.playable, + gravity: entry.gravity, + homeworld: entry.homeworld, + modifiers: entry.modifiers, + notes: entry.notes + } + } + }; +} + +function toWeaponItem(entry: WeaponCatalogEntry): CatalogSeed { + const folderKey = entry.attackType === "melee" ? "weaponMelee" : "weaponRanged"; + const img = weaponIconPath(entry.name) || (entry.attackType === "melee" ? ICONS.weaponMelee : ICONS.weaponRanged); + return { + key: itemKey("weapon", entry.name), + folderKey, + img, + data: { + name: entry.name, + type: "weapon", + system: { + equipped: entry.equipped ?? true, + stowed: false, + weight: entry.weight ?? 0, + linkedSkill: entry.linkedSkill, + linkedSkillId: "", + attackType: entry.attackType, + damageFormula: entry.damageFormula, + range: entry.range, + energyUsedPerAttack: entry.energyUsedPerAttack, + ammoUsedPerAttack: entry.ammoUsedPerAttack, + powerSource: entry.powerSource, + powerPackId: "", + notes: entry.notes + } + } + }; +} + +function toArmorItem(entry: ArmorCatalogEntry): CatalogSeed { + const img = armorIconPath(entry.name) || ICONS.armor; + return { + key: itemKey("armor", entry.name), + folderKey: "armor", + img, + data: { + name: entry.name, + type: "armor", + system: { + equipped: entry.equipped, + stowed: false, + weight: entry.weight ?? 0, + value: entry.value, + coverage: entry.coverage, + perLocation: { + rLeg: 0, + lLeg: 0, + abdomen: 0, + chest: 0, + rArm: 0, + lArm: 0, + head: 0 + }, + notes: entry.notes + } + } + }; +} + +function toGearItem(entry: GearCatalogEntry): CatalogSeed { + const img = gearIconPath(entry.name) || (entry.isPowerPack ? ICONS.powerPackDefault : ICONS.gearDefault); + return { + key: itemKey("gear", entry.name), + folderKey: "gear", + img, + data: { + name: entry.name, + type: "gear", + system: { + equipped: entry.equipped ?? true, + stowed: false, + weight: entry.weight, + cost: entry.cost, + energyUse: entry.energyUse, + powerSupplyNotes: entry.powerSupplyNotes, + isPowerPack: entry.isPowerPack, + energy: entry.energy, + notes: entry.notes + } + } + }; +} + +function toPursuitItem(entry: PursuitCatalogEntry): CatalogSeed { + const img = pursuitIconPath(entry.name) || ICONS.pursuitDefault; + return { + key: itemKey("pursuit", entry.name), + folderKey: "pursuits", + img, + data: { + name: entry.name, + type: "pursuit", + system: { + wealthValue: entry.wealthValue, + occupationPointsPerYear: entry.occupationPointsPerYear, + rollRange: entry.rollRange, + fixedSkills: entry.fixedSkills, + optionalSkillGroups: entry.optionalSkillGroups ?? [], + notes: entry.notes + } + } + }; +} + +function pursuitAsGearNotes(entry: PursuitCatalogEntry): string { + const fixed = entry.fixedSkills.length ? `Fixed skills: ${entry.fixedSkills.join(", ")}` : "Fixed skills: (none)"; + const optional = (entry.optionalSkillGroups ?? []).map((group) => `Choose ${group.choose}: ${group.skills.join(", ")}`).join(" | "); + const optionalText = optional ? ` Optional: ${optional}.` : ""; + return `${entry.notes} ${fixed}.${optionalText} Wealth ${entry.wealthValue}. Occupation points/year ${entry.occupationPointsPerYear}.`; +} + +function toPursuitFallbackGearItem(entry: PursuitCatalogEntry): CatalogSeed { + const img = pursuitIconPath(entry.name) || ICONS.pursuitDefault; + const itemName = `Pursuit: ${entry.name}`; + return { + key: itemKey("gear", itemName), + folderKey: "pursuits", + img, + data: { + name: itemName, + type: "gear", + system: { + equipped: false, + stowed: true, + weight: 0, + cost: 0, + energyUse: 0, + powerSupplyNotes: "Pursuit reference", + isPowerPack: false, + energy: { value: 0, max: 0 }, + notes: pursuitAsGearNotes(entry) + }, + flags: { + ringworld: { + pursuitData: { + pursuitName: entry.name, + rollRange: entry.rollRange, + wealthValue: entry.wealthValue, + occupationPointsPerYear: entry.occupationPointsPerYear, + fixedSkills: entry.fixedSkills, + optionalSkillGroups: entry.optionalSkillGroups ?? [] + } + } + } + } + }; +} + +async function getOrCreateItemFolder(name: string, parentId: string | null): Promise { + const existing = game.folders.find( + (folder: any) => folder.type === "Item" && folder.name === name && (folder.folder?.id ?? null) === parentId + ); + if (existing) return existing; + + return Folder.create({ + name, + type: "Item", + folder: parentId, + sorting: "a" + }); +} + +async function ensureCatalogFolders(): Promise> { + const root = await getOrCreateItemFolder("Ringworld Catalog", null); + const species = await getOrCreateItemFolder("Species", root.id); + const skills = await getOrCreateItemFolder("Skills", root.id); + const pursuits = await getOrCreateItemFolder("Pursuits", root.id); + const gear = await getOrCreateItemFolder("Gear", root.id); + const armor = await getOrCreateItemFolder("Armor", gear.id); + const weapons = await getOrCreateItemFolder("Weapons", root.id); + const weaponMelee = await getOrCreateItemFolder("Melee Weapons", weapons.id); + const weaponRanged = await getOrCreateItemFolder("Ranged Weapons", weapons.id); + + return { + species: species.id, + skills: skills.id, + pursuits: pursuits.id, + gear: gear.id, + armor: armor.id, + weaponMelee: weaponMelee.id, + weaponRanged: weaponRanged.id + }; +} + +function buildAllSeeds(): CatalogSeed[] { + const canUsePursuitType = itemTypeAvailable("pursuit"); + return [ + ...SPECIES_CATALOG.map(toSpeciesItem), + ...RINGWORLD_BASE_SKILLS.map(toSkillItem), + ...(canUsePursuitType ? PURSUIT_CATALOG.map(toPursuitItem) : PURSUIT_CATALOG.map(toPursuitFallbackGearItem)), + ...WEAPON_CATALOG.map(toWeaponItem), + ...ARMOR_CATALOG.map(toArmorItem), + ...GEAR_CATALOG.map(toGearItem) + ]; +} + +export async function seedWorldCatalog(options?: { notify?: boolean }): Promise<{ added: number; updated: number }> { + if (!game.user?.isGM) return { added: 0, updated: 0 }; + + const folderIds = await ensureCatalogFolders(); + const seeds = buildAllSeeds(); + + const existing = new Map( + game.items.map((item: any) => [itemKey(item.type, item.name), item]) + ); + + const createPayload: any[] = []; + const updatePayload: any[] = []; + + for (const seed of seeds) { + const folderId = folderIds[seed.folderKey]; + const existingItem = existing.get(seed.key); + + if (!existingItem) { + createPayload.push({ + ...seed.data, + img: seed.img, + folder: folderId, + flags: { + ringworld: { + catalog: true, + catalogKey: seed.key + } + } + }); + continue; + } + + const update: Record = { _id: existingItem.id }; + + if ((existingItem.folder?.id ?? null) !== folderId) { + update.folder = folderId; + } + + if (String(existingItem.img || "") !== seed.img) { + update.img = seed.img; + } + + if (existingItem.flags?.ringworld?.catalog !== true) { + update["flags.ringworld.catalog"] = true; + } + + if (existingItem.flags?.ringworld?.catalogKey !== seed.key) { + update["flags.ringworld.catalogKey"] = seed.key; + } + + const existingSystem = foundry.utils.deepClone(existingItem.system ?? {}); + const mergedSystem = foundry.utils.mergeObject(existingSystem, seed.data.system ?? {}, { + inplace: false, + insertKeys: true, + insertValues: true, + overwrite: false + }); + const systemDiff = foundry.utils.diffObject(existingSystem, mergedSystem); + if (Object.keys(systemDiff).length > 0) { + update.system = mergedSystem; + } + + if (Object.keys(update).length > 1) { + updatePayload.push(update); + } + } + + if (createPayload.length) { + await Item.createDocuments(createPayload as any[]); + } + + if (updatePayload.length) { + await Item.updateDocuments(updatePayload as any[]); + } + + if (options?.notify) { + ui.notifications?.info(`Ringworld catalog synced: +${createPayload.length} new, ${updatePayload.length} updated.`); + } + + return { added: createPayload.length, updated: updatePayload.length }; +} + +export function getPlayableSpeciesFromWorld(): any[] { + return game.items.filter((item: any) => item.type === "species" && item.system.playable); +} + +export function ringworldSystemSettingKey(key: string): string { + return `${SYSTEM_ID}.${key}`; +} diff --git a/src/ringworld.ts b/src/ringworld.ts new file mode 100644 index 0000000..9f4b56e --- /dev/null +++ b/src/ringworld.ts @@ -0,0 +1,344 @@ +import "./styles/ringworld.scss"; + +import { RingworldActor } from "./module/actor/RingworldActor"; +import { RingworldItem } from "./module/item/RingworldItem"; +import { RingworldExplorerSheet } from "./module/sheets/actors/ExplorerSheet"; +import { RingworldCreatureSheet } from "./module/sheets/actors/CreatureSheet"; +import { RingworldVehicleSheet } from "./module/sheets/actors/VehicleSheet"; +import { RingworldSkillSheet } from "./module/sheets/items/SkillSheet"; +import { RingworldWeaponSheet } from "./module/sheets/items/WeaponSheet"; +import { RingworldArmorSheet } from "./module/sheets/items/ArmorSheet"; +import { RingworldGearSheet } from "./module/sheets/items/GearSheet"; +import { RingworldSpeciesSheet } from "./module/sheets/items/SpeciesSheet"; +import { RingworldPursuitSheet } from "./module/sheets/items/PursuitSheet"; +import { HIT_LOCATION_TABLE } from "./module/constants"; +import { createRandomExplorer } from "./module/character-tools"; +import { RINGWORLD_CATALOG_VERSION, seedWorldCatalog } from "./module/world-catalog"; +import { initializeImpulseTracker } from "./module/impulse-tracker"; +import { DEFAULT_SKILL_ICON, baseSkillByName, resolveSkillIcon } from "./module/skill-icons"; + +const ENCUMBRANCE_DATA_MIGRATION_VERSION = 2; + +function normName(value: unknown): string { + return String(value ?? "").trim().toLowerCase(); +} + +function worldItemWeightMap(): Map { + const map = new Map(); + for (const item of game.items ?? []) { + if (item.type !== "weapon" && item.type !== "armor" && item.type !== "gear") continue; + const key = `${item.type}:${normName(item.name)}`; + map.set(key, Number((item as any).system?.weight ?? 0) || 0); + } + return map; +} + +async function migrateActorItemEncumbranceData(): Promise { + if (!game.user?.isGM) return 0; + const weightMap = worldItemWeightMap(); + let touched = 0; + + for (const actor of game.actors ?? []) { + const updates: any[] = []; + + for (const item of actor.items ?? []) { + if (item.type !== "weapon" && item.type !== "armor" && item.type !== "gear") continue; + const patch: Record = { _id: item.id }; + let changed = false; + + if ((item as any).system?.equipped === undefined) { + patch["system.equipped"] = true; + changed = true; + } + + if ((item as any).system?.weight === undefined) { + const key = `${item.type}:${normName(item.name)}`; + patch["system.weight"] = Number(weightMap.get(key) ?? 0); + changed = true; + } + + if ((item as any).system?.stowed === undefined) { + patch["system.stowed"] = false; + changed = true; + } + + if (changed) { + updates.push(patch); + } + } + + if (updates.length) { + await actor.updateEmbeddedDocuments("Item", updates); + touched += updates.length; + } + } + + return touched; +} + +function injectDirectoryButton(htmlLike: JQuery | HTMLElement | null | undefined, className: string, label: string, onClick: () => Promise | void): void { + if (!htmlLike) return; + const html = "find" in (htmlLike as any) ? (htmlLike as JQuery) : $(htmlLike); + if (!html.length || html.find(`.${className}`).length) return; + + const button = $(``); + button.on("click", async () => { + await onClick(); + }); + + const containers = [ + html.find(".directory-footer"), + html.find("footer.directory-footer"), + html.find("footer"), + html.find(".footer-actions"), + html.find(".header-actions"), + html.find(".action-buttons") + ]; + + const container = containers.find((candidate) => candidate.length); + if (container) { + container.first().append(button); + return; + } + + let fallback = html.find(".ringworld-directory-controls"); + if (!fallback.length) { + fallback = $('
'); + html.append(fallback); + } + fallback.first().append(button); +} + +async function handleSeedCatalogClick(): Promise { + await seedWorldCatalog({ notify: true }); +} + +async function handleCreateRandomExplorerClick(): Promise { + const actor = await createRandomExplorer(); + actor?.sheet?.render(true); +} + +function ensureSidebarDirectoryButtons(): void { + if (!game.user?.isGM) return; + + const itemElement = (ui as any).items?.element; + injectDirectoryButton(itemElement, "ringworld-seed-items", "Seed Ringworld Items", handleSeedCatalogClick); + + const actorElement = (ui as any).actors?.element; + injectDirectoryButton(actorElement, "ringworld-random-actor", "Create Random Explorer", handleCreateRandomExplorerClick); +} + +function itemTypeAvailable(type: string): boolean { + const directTypes = (getDocumentClass("Item") as any)?.TYPES; + if (Array.isArray(directTypes)) return directTypes.includes(type); + + const docTypes = (game as any)?.system?.documentTypes?.Item; + if (Array.isArray(docTypes)) return docTypes.includes(type); + if (docTypes && typeof docTypes === "object") return Object.prototype.hasOwnProperty.call(docTypes, type); + + const configModels = (CONFIG as any)?.Item?.dataModels; + if (configModels && typeof configModels === "object" && Object.prototype.hasOwnProperty.call(configModels, type)) { + return true; + } + return false; +} + +Hooks.once("init", async () => { + console.log("ringworld | Initializing Ringworld system"); + initializeImpulseTracker(); + + const ActorRegistry = (foundry as any)?.documents?.collections?.Actors; + const ItemRegistry = (foundry as any)?.documents?.collections?.Items; + const BaseActorSheet = (foundry as any)?.appv1?.sheets?.ActorSheet; + const BaseItemSheet = (foundry as any)?.appv1?.sheets?.ItemSheet; + + (CONFIG as any).ringworld = { + hitLocations: HIT_LOCATION_TABLE + }; + CONFIG.Combat.initiative = foundry.utils.mergeObject(CONFIG.Combat.initiative ?? {}, { + formula: "1d100", + decimals: 0 + }, { inplace: false }); + + CONFIG.Actor.documentClass = RingworldActor as any; + CONFIG.Item.documentClass = RingworldItem as any; + + if (!ActorRegistry || !ItemRegistry || !BaseActorSheet || !BaseItemSheet) { + console.error("ringworld | Foundry sheet registries were not available during init."); + return; + } + + ActorRegistry.unregisterSheet("core", BaseActorSheet); + ActorRegistry.registerSheet("ringworld", RingworldExplorerSheet as any, { + types: ["explorer"], + makeDefault: true + }); + ActorRegistry.registerSheet("ringworld", RingworldCreatureSheet as any, { + types: ["creature"], + makeDefault: true + }); + ActorRegistry.registerSheet("ringworld", RingworldVehicleSheet as any, { + types: ["vehicle"], + makeDefault: true + }); + + ItemRegistry.unregisterSheet("core", BaseItemSheet); + ItemRegistry.registerSheet("ringworld", RingworldSkillSheet as any, { + types: ["skill"], + makeDefault: true + }); + ItemRegistry.registerSheet("ringworld", RingworldWeaponSheet as any, { + types: ["weapon"], + makeDefault: true + }); + ItemRegistry.registerSheet("ringworld", RingworldArmorSheet as any, { + types: ["armor"], + makeDefault: true + }); + ItemRegistry.registerSheet("ringworld", RingworldGearSheet as any, { + types: ["gear"], + makeDefault: true + }); + ItemRegistry.registerSheet("ringworld", RingworldSpeciesSheet as any, { + types: ["species"], + makeDefault: true + }); + if (itemTypeAvailable("pursuit")) { + ItemRegistry.registerSheet("ringworld", RingworldPursuitSheet as any, { + types: ["pursuit"], + makeDefault: true + }); + } + + game.settings.register("ringworld", "catalogSeedVersion", { + scope: "world", + config: false, + type: Number, + default: 0 + }); + game.settings.register("ringworld", "encumbranceDataMigrationVersion", { + scope: "world", + config: false, + type: Number, + default: 0 + }); + + Handlebars.registerHelper("eq", (a: unknown, b: unknown) => a === b); + Handlebars.registerHelper("or", (a: unknown, b: unknown) => Boolean(a || b)); +}); + +Hooks.once("ready", async () => { + const path = "systems/ringworld/resources/hit-locations/humanoid.json"; + try { + const response = await fetch(path); + if (response.ok) { + const table = await response.json(); + (CONFIG as any).ringworld.hitLocations = table.entries ?? HIT_LOCATION_TABLE; + } + } catch (_error) { + console.warn(`ringworld | Could not load ${path}, using built-in table`); + } + + if (game.user?.isGM) { + for (const combat of game.combats ?? []) { + const formula = String(combat?.settings?.initiative?.formula ?? "").trim(); + if (!formula || formula.toLowerCase() === "undefined") { + await combat.update({ + "settings.initiative.formula": "1d100", + "settings.initiative.decimals": 0 + }); + } + } + + const seededVersion = Number(game.settings.get("ringworld", "catalogSeedVersion") ?? 0); + if (seededVersion < RINGWORLD_CATALOG_VERSION) { + const result = await seedWorldCatalog({ notify: true }); + if (result.added >= 0) { + await game.settings.set("ringworld", "catalogSeedVersion", RINGWORLD_CATALOG_VERSION); + } + } + + const migratedVersion = Number(game.settings.get("ringworld", "encumbranceDataMigrationVersion") ?? 0); + if (migratedVersion < ENCUMBRANCE_DATA_MIGRATION_VERSION) { + const touched = await migrateActorItemEncumbranceData(); + await game.settings.set("ringworld", "encumbranceDataMigrationVersion", ENCUMBRANCE_DATA_MIGRATION_VERSION); + if (touched > 0) { + ui.notifications?.info(`Ringworld encumbrance migration updated ${touched} actor items.`); + } + } + } + + ensureSidebarDirectoryButtons(); + window.setTimeout(() => ensureSidebarDirectoryButtons(), 300); + + (game as any).ringworld = { + ...((game as any).ringworld ?? {}), + seedWorldCatalog: async (options?: { notify?: boolean }) => seedWorldCatalog(options), + createRandomExplorer: async () => createRandomExplorer(), + catalogVersion: RINGWORLD_CATALOG_VERSION, + systemId: "ringworld", + systemVersion: String(game.system?.version ?? "0.0.0") + }; + Hooks.callAll("ringworldReady", (game as any).ringworld); +}); + +Hooks.on("createActor", async (actor: any, _options: any, userId: string) => { + if (!game.user?.isGM) return; + if (userId !== game.user.id) return; + if (!actor || (actor.type !== "explorer" && actor.type !== "creature")) return; + + try { + const added = await actor.ensureBaseSkills?.(); + if (Number(added) > 0) { + console.log(`ringworld | Added ${added} base skills to ${actor.name}`); + } + } catch (error) { + console.error("ringworld | Failed to auto-populate base skills", error); + } +}); + +Hooks.on("preCreateItem", (item: any) => { + if (item.type !== "skill") return; + + const updates: Record = {}; + const name = String(item.name || "Skill"); + const currentImg = String(item.img || ""); + updates.img = resolveSkillIcon(name, currentImg); + + const base = baseSkillByName(name); + const currentSystem = (item as any).system ?? {}; + const currentValue = Number(currentSystem?.value ?? NaN); + const currentCategory = String(currentSystem?.category ?? "").trim(); + + updates.system = { + value: Number.isFinite(currentValue) ? currentValue : (base?.base ?? 0), + category: currentCategory || (base?.category ?? "Technical"), + specialization: String(currentSystem?.specialization ?? base?.specialization ?? "") + }; + + if (!updates.img) updates.img = DEFAULT_SKILL_ICON; + item.updateSource(updates); +}); + +Hooks.on("renderItemDirectory", (_app: any, html: JQuery) => { + if (!game.user?.isGM) return; + injectDirectoryButton(html, "ringworld-seed-items", "Seed Ringworld Items", handleSeedCatalogClick); + ensureSidebarDirectoryButtons(); +}); + +Hooks.on("renderActorDirectory", (_app: any, html: JQuery) => { + if (!game.user?.isGM) return; + injectDirectoryButton(html, "ringworld-random-actor", "Create Random Explorer", handleCreateRandomExplorerClick); + ensureSidebarDirectoryButtons(); +}); + +Hooks.on("renderSidebar", () => { + ensureSidebarDirectoryButtons(); +}); + +Hooks.on("changeSidebarTab", (app: any) => { + const tabName = String(app?.tabName ?? app?.id ?? app?.options?.id ?? ""); + if (tabName === "items" || tabName === "actors") { + ensureSidebarDirectoryButtons(); + } +}); diff --git a/src/styles/ringworld.scss b/src/styles/ringworld.scss new file mode 100644 index 0000000..add13d0 --- /dev/null +++ b/src/styles/ringworld.scss @@ -0,0 +1,1673 @@ +.ringworld-sheet { + color: #1b2a3a; + background: linear-gradient(165deg, #f4f7fb 0%, #e8eff8 55%, #f8fbff 100%); + font-size: 12.5px; + line-height: 1.3; + font-family: "Rajdhani", "Orbitron", "Eurostile", "Bank Gothic", "Segoe UI", sans-serif; + + input, + select, + textarea, + button { + border: 1px solid #91a6bf; + border-radius: 4px; + background: #ffffff; + color: #1b2a3a; + } + + input, + select, + textarea { + padding: 0.18rem 0.3rem; + } + + button { + cursor: pointer; + padding: 0.2rem 0.45rem; + background: linear-gradient(180deg, #f8fbff 0%, #dfe9f6 100%); + border-color: #6f8eb0; + } + + .sheet-header { + display: grid; + gap: 0.3rem; + margin-bottom: 0.3rem; + } + + .panel { + border: 1px solid #8fa8c2; + border-radius: 6px; + background: rgba(255, 255, 255, 0.78); + padding: 0.28rem; + margin-bottom: 0.2rem; + } + + .panel-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 0.4rem; + margin-bottom: 0.35rem; + } + + .toolbar { + display: flex; + align-items: center; + gap: 0.35rem; + margin-bottom: 0.35rem; + } + + .tabs { + display: flex; + gap: 0.35rem; + margin-bottom: 0.3rem; + + .item { + border: 1px solid #7d97b3; + border-radius: 4px; + padding: 0.2rem 0.5rem; + background: rgba(229, 238, 249, 0.85); + font-weight: 600; + font-size: 0.84rem; + } + } + + .char-grid, + .condition-grid, + .location-list { + display: grid; + gap: 0.3rem; + } + + .char-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .resource-row { + display: flex; + align-items: center; + gap: 0.35rem; + flex-wrap: wrap; + } + + .item-list { + list-style: none; + margin: 0; + padding: 0; + + .item { + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) auto; + align-items: center; + gap: 0.35rem; + margin-bottom: 0.22rem; + padding: 0.2rem 0.3rem; + border: 1px solid #a0b4ca; + border-radius: 4px; + background: rgba(250, 253, 255, 0.92); + + > div { + min-width: 0; + } + + > div:not(.actions) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } + + .actions { + display: flex; + justify-content: flex-end; + gap: 0.2rem; + + button { + font-size: 0.74rem; + padding: 0.15rem 0.35rem; + min-width: 1.4rem; + } + } + + .location-row { + display: grid; + grid-template-columns: 1.3fr 1fr auto 1fr; + align-items: center; + gap: 0.3rem; + } + + .inline-pair { + display: inline-flex; + align-items: center; + gap: 0.2rem; + } + + .skills-badge { + margin-left: auto; + border: 1px solid #7b98b7; + border-radius: 999px; + background: #edf4fe; + padding: 0.1rem 0.45rem; + font-size: 0.78rem; + } + + .rw-header h1 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.03em; + color: #2d3e54; + font-size: 1.72rem; + font-weight: 700; + } + + .rw-title-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.4rem; + } + + .rw-lock-status { + display: inline-block; + border: 1px solid #7f97b3; + border-radius: 999px; + background: rgba(233, 242, 252, 0.9); + color: #2b4057; + font-size: 0.73rem; + font-weight: 700; + padding: 0.1rem 0.45rem; + } + + .rw-profile { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 0.16rem; + + label { + display: grid; + gap: 0.06rem; + font-size: 0.75rem; + font-weight: 600; + } + + .rw-encumbrance-field { + grid-column: 1 / -1; + + input { + min-height: 1.55rem; + font-size: 0.88rem; + font-weight: 700; + color: #1f3348; + border-width: 2px; + border-color: #6486ac; + background: linear-gradient(180deg, #f9fcff 0%, #e5effc 100%); + } + } + } + + .rw-upper-grid { + display: grid; + grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr); + gap: 0.24rem; + align-items: start; + } + + .rw-left-stack, + .rw-right-stack { + display: grid; + gap: 0.2rem; + } + + .rw-quick { + display: grid; + gap: 0.2rem; + max-width: none; + width: 100%; + margin-bottom: 0; + } + + .rw-overview-list { + max-height: 10.9rem; + overflow-y: auto; + overflow-x: hidden; + padding-right: 0.08rem; + + .item { + min-height: 1.7rem; + margin-bottom: 0.16rem; + padding: 0.14rem 0.2rem; + gap: 0.18rem; + font-size: 0.73rem; + font-weight: 600; + + > div:not(.actions) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .item:nth-child(odd) { + background: rgba(228, 239, 251, 0.96); + } + } + + .item-title { + display: inline-flex; + align-items: center; + gap: 0.22rem; + min-width: 0; + + span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .item-icon { + width: 1rem; + height: 1rem; + border-radius: 2px; + flex: 0 0 auto; + object-fit: cover; + } + + .rw-overview-weapons .rw-overview-list { + max-height: 11.1rem; + } + + .rw-overview-content { + display: grid; + grid-template-columns: minmax(0, 1.26fr) minmax(0, 0.74fr); + gap: 0.24rem; + align-items: start; + } + + .rw-overview-side { + display: grid; + gap: 0.24rem; + } + + .rw-attribute-board { + display: grid; + gap: 0.08rem; + border: 1px solid #92a7c0; + border-radius: 6px; + background: rgba(255, 255, 255, 0.68); + padding: 0.16rem 0.18rem; + } + + .attr-row { + display: grid; + grid-template-columns: minmax(82px, 0.44fr) minmax(0, 1.56fr); + gap: 0.06rem; + align-items: center; + padding: 0.03rem 0.08rem; + border-bottom: 1px dotted #8da4bf; + + &:last-child { + border-bottom: none; + } + } + + .rw-attribute-board .attr-row:nth-child(odd) { + background: rgba(228, 239, 251, 0.6); + border-radius: 4px; + } + + .lhs, + .rhs { + display: flex; + align-items: center; + gap: 0.1rem; + min-width: 0; + font-size: 0.8rem; + font-weight: 600; + color: #26384d; + } + + .lhs { + display: grid; + grid-template-columns: 2.6rem 2.9rem; + align-items: center; + column-gap: 0.12rem; + } + + .stat-roll-btn { + width: 2.5rem; + min-width: 2.5rem; + height: 1.35rem; + padding: 0; + font-size: 0.95rem; + font-weight: 800; + letter-spacing: 0.01em; + border-color: #6f8eb0; + } + + .lhs input { + width: 2.9rem; + text-align: center; + font-size: 0.8rem; + justify-self: start; + } + + .rhs > span:first-child { + min-width: 3.8rem; + } + + .rhs { + display: grid; + grid-template-columns: minmax(0, 0.95fr) minmax(7.8rem, 13.2rem); + align-items: center; + column-gap: 0.14rem; + } + + .rhs input[type="text"] { + width: 100%; + min-width: 0; + } + + .rhs input[type="number"] { + width: 2.8rem; + text-align: center; + } + + .rhs .inline-pair { + margin-left: 0; + justify-self: end; + } + + .attr-row button.roll-derived { + margin-left: 0.1rem; + } + + .rw-tools { + display: flex; + flex-wrap: wrap; + gap: 0.18rem; + align-items: center; + max-width: 100%; + + label { + display: inline-flex; + align-items: center; + gap: 0.2rem; + font-size: 0.78rem; + font-weight: 600; + } + + input { + width: 3.8rem; + text-align: center; + } + + .reset-conditions { + flex: 1; + min-width: 11rem; + } + } + + .rw-notes { + p { + margin: 0; + color: #3c5169; + font-size: 0.82rem; + } + } + + .skills-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.28rem; + } + + .skills-grid .skill-category:nth-child(5) { + grid-column: 2; + } + + .rw-character-builder { + display: grid; + gap: 0.28rem; + margin-bottom: 0.35rem; + } + + .rw-builder-actions { + display: inline-flex; + gap: 0.2rem; + flex-wrap: wrap; + } + + .rw-builder-pools { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.16rem; + font-size: 0.78rem; + + > div { + border: 1px solid #9ab0c8; + border-radius: 4px; + background: rgba(240, 247, 255, 0.86); + padding: 0.16rem 0.24rem; + } + } + + .rw-builder-pursuits { + display: grid; + gap: 0.16rem; + + h4 { + margin: 0; + font-size: 0.76rem; + text-transform: uppercase; + letter-spacing: 0.02em; + color: #2f455d; + } + } + + .builder-add-row, + .builder-pursuit-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 4.1rem auto auto; + gap: 0.16rem; + align-items: center; + } + + .builder-add-row { + grid-template-columns: minmax(0, 1fr) 4.1rem auto; + } + + .builder-pursuit-list { + list-style: none; + padding: 0; + margin: 0; + display: grid; + gap: 0.14rem; + } + + .rw-builder-note { + margin: 0; + font-size: 0.75rem; + color: #324962; + } + + .skill-category { + margin: 0; + padding: 0.28rem; + + h3 { + margin: 0 0 0.15rem; + font-size: 0.88rem; + text-transform: uppercase; + letter-spacing: 0.02em; + color: #26364a; + } + + .root-max { + display: inline-grid; + grid-template-columns: auto 3.4rem; + align-items: center; + gap: 0.18rem; + margin-bottom: 0.18rem; + font-size: 0.72rem; + font-weight: 600; + } + + .root-max input[readonly] { + background: #edf3fb; + color: #2b4057; + } + } + + .compact-skill-list { + .skill-row { + grid-template-columns: minmax(0, 1fr) auto auto auto; + min-height: 1.42rem; + margin-bottom: 0.14rem; + padding: 0.1rem 0.16rem; + border-radius: 3px; + } + + .skill-name { + display: inline-flex; + align-items: center; + gap: 0.22rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.77rem; + line-height: 1.2; + padding-right: 0.1rem; + + span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .skill-base, + .skill-value { + min-width: 2.4rem; + text-align: right; + font-size: 0.76rem; + font-variant-numeric: tabular-nums; + } + + .alloc-chip { + display: inline-block; + margin-left: 0.18rem; + padding: 0.04rem 0.2rem; + border-radius: 999px; + border: 1px solid #6f8eae; + background: #ecf4fd; + color: #2a445f; + font-size: 0.62rem; + font-weight: 700; + line-height: 1; + vertical-align: middle; + } + } + + .skill-actions { + gap: 0.12rem; + } + + .skill-columns-head { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto auto; + gap: 0.14rem; + padding: 0.02rem 0.18rem 0.14rem; + margin-bottom: 0.1rem; + border-bottom: 1px dashed #9ab0c8; + font-size: 0.66rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.02em; + color: #375069; + } + + .skill-icon { + width: 0.92rem; + height: 0.92rem; + border-radius: 2px; + border: 1px solid #87a2bf; + object-fit: cover; + background: #0a0d12; + flex: 0 0 auto; + } + + .icon-btn { + width: 1.34rem; + height: 1.34rem; + min-width: 1.34rem; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + font-size: 0.7rem; + line-height: 1; + + i { + pointer-events: none; + } + } + + .missing-skill { + border-color: #bf9863; + background: #fff8ec; + } + + .additional-skills summary { + cursor: pointer; + font-weight: 600; + } + + .rw-hit-body { + display: grid; + grid-template-columns: repeat(3, 74px); + grid-template-areas: + ". head ." + "lArm chest rArm" + ". abdomen ." + "lLeg . rLeg"; + gap: 0.12rem; + align-items: center; + justify-content: start; + width: 100%; + } + + .rw-hit-body.schema-dolphin { + grid-template-areas: + ". head ." + "lArm chest rArm" + ". abdomen ." + ". lLeg ." + ". rLeg ."; + } + + .rw-hit-body.schema-puppeteer { + grid-template-areas: + ". head ." + "lArm chest rArm" + "lLeg abdomen rLeg"; + } + + .rw-hit-body.schema-trinoc { + grid-template-areas: + ". head ." + "lArm chest rArm" + ". abdomen ." + "lLeg . rLeg"; + } + + .rw-hit-body.schema-bandersnatchi { + grid-template-areas: + ". head ." + "lArm chest rArm" + "lLeg abdomen rLeg"; + } + + .rw-hit-toolbar { + display: grid; + grid-template-columns: auto; + align-items: center; + gap: 0.1rem; + margin-bottom: 0.08rem; + } + + .rw-hit-toolbar .roll-hit-location { + justify-self: start; + } + + .rw-hit-main .rw-hit-body { + justify-content: center; + } + + .body-loc { + border: 1px solid #8ea7c2; + border-radius: 6px; + background: rgba(245, 250, 255, 0.95); + padding: 0.12rem 0.14rem; + display: grid; + gap: 0.05rem; + min-height: 68px; + + h4 { + margin: 0; + font-size: 0.63rem; + text-transform: uppercase; + color: #2c4158; + } + + label { + display: flex; + justify-content: space-between; + align-items: center; + gap: 0.2rem; + font-size: 0.6rem; + font-weight: 600; + } + + input { + width: 1.84rem; + text-align: center; + font-size: 0.6rem; + padding: 0.05rem 0.08rem; + } + } + + .body-head { + grid-area: head; + min-height: 64px; + } + + .body-lArm { + grid-area: lArm; + min-height: 70px; + } + + .body-rArm { + grid-area: rArm; + min-height: 70px; + } + + .body-chest { + grid-area: chest; + min-height: 70px; + } + + .body-abdomen { + grid-area: abdomen; + min-height: 70px; + } + + .body-lLeg { + grid-area: lLeg; + min-height: 68px; + } + + .body-rLeg { + grid-area: rLeg; + min-height: 68px; + } + + .rw-right-lower { + display: grid; + grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); + gap: 0.2rem; + align-items: start; + } + + .rw-portrait-panel { + display: grid; + gap: 0.1rem; + align-content: start; + margin-bottom: 0; + } + + .rw-portrait-img { + width: 100%; + max-width: none; + aspect-ratio: 4 / 3; + min-height: 7.4rem; + margin: 0 auto; + border: 1px solid #88a4c1; + border-radius: 6px; + object-fit: cover; + background: linear-gradient(160deg, #e1eaf5 0%, #f6fbff 100%); + cursor: pointer; + } + + .loc-roll { + font-size: 0.56rem; + color: #476482; + font-weight: 600; + } + + .loc-ap-total { + font-size: 0.56rem; + font-weight: 700; + color: #2e4762; + } + + .rw-hit-layout { + display: block; + } + + .rw-hit-summary { + border: 1px solid #9bb0c8; + border-radius: 6px; + background: rgba(248, 252, 255, 0.95); + padding: 0.14rem 0.18rem; + margin-bottom: 0; + + h4 { + margin: 0 0 0.14rem; + font-size: 0.74rem; + text-transform: uppercase; + color: #2d4259; + } + } + + .rw-hit-summary-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: baseline; + gap: 0.25rem; + border-bottom: 1px dotted #9eb1c8; + padding: 0.03rem 0; + font-size: 0.74rem; + font-weight: 600; + + &:last-of-type { + border-bottom: none; + } + } + + .rw-hit-summary-row:nth-child(odd) { + background: rgba(228, 239, 251, 0.6); + border-radius: 3px; + padding-left: 0.14rem; + padding-right: 0.14rem; + } + + .rw-hit-label { + color: #243a52; + } + + .rw-hit-values { + text-align: right; + white-space: nowrap; + } + + .rw-hit-unconscious { + display: grid; + gap: 0.1rem; + margin-top: 0.12rem; + font-size: 0.73rem; + font-weight: 600; + + input { + width: 4rem; + height: 1.25rem; + text-align: center; + } + } + + .rw-hit-summary-below { + margin-top: 0.05rem; + width: fit-content; + min-width: min(100%, 24rem); + max-width: 100%; + } + + .rw-weapon-list .item { + grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.42fr) minmax(0, 0.95fr) minmax(0, 0.58fr) minmax(0, 0.95fr) auto; + } + + .rw-armor-list .item { + grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.42fr) minmax(0, 0.62fr) minmax(0, 0.72fr) auto; + } + + .rw-gear-list .item { + grid-template-columns: minmax(0, 1.36fr) minmax(0, 0.62fr) minmax(0, 1.02fr) auto; + } + + .rw-overview-weapons .rw-weapon-list .item { + grid-template-columns: minmax(0, 1.26fr) minmax(0, 1.02fr) minmax(0, 0.58fr) minmax(0, 0.96fr) auto; + } + + .rw-overview-armor .rw-armor-list .item { + grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.68fr) minmax(0, 0.78fr) auto; + } + + .rw-overview-gear .rw-gear-list .item { + grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.24fr) auto; + } + + @media (max-width: 1250px) { + .rw-upper-grid, + .rw-overview-content { + grid-template-columns: 1fr; + } + + .rw-right-lower { + grid-template-columns: 1fr; + } + } + + @media (max-width: 1100px) { + .rw-profile { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .rw-upper-grid, + .rw-overview-content { + grid-template-columns: 1fr; + } + + .attr-row { + grid-template-columns: 1fr; + gap: 0.2rem; + } + + .rhs .inline-pair { + margin-left: 0; + } + + .skills-grid { + grid-template-columns: 1fr; + } + + .skills-grid .skill-category:nth-child(5) { + grid-column: auto; + } + + .rw-builder-pools { + grid-template-columns: 1fr; + } + + .builder-add-row, + .builder-pursuit-row { + grid-template-columns: 1fr; + } + } +} + +.rw-builder-alloc-form { + display: grid; + gap: 0.4rem; + + .rw-builder-alloc-head { + display: grid; + grid-template-columns: 2.4rem minmax(0, 1fr); + gap: 0.45rem; + align-items: center; + } + + .rw-builder-alloc-head img { + width: 2.2rem; + height: 2.2rem; + border-radius: 4px; + border: 1px solid #7f97b3; + object-fit: cover; + background: #0b0f15; + } + + .rw-builder-alloc-head h3 { + margin: 0; + font-size: 0.95rem; + } + + .rw-builder-alloc-head p { + margin: 0.08rem 0 0; + font-size: 0.74rem; + color: #38516b; + } + + label { + display: grid; + gap: 0.12rem; + font-size: 0.8rem; + font-weight: 600; + } + + input[type="number"] { + width: 100%; + } + + .hint { + margin: 0; + font-size: 0.74rem; + color: #38516b; + } +} + +.rw-stat-roll-form { + display: grid; + gap: 0.42rem; + + .form-group { + display: grid; + gap: 0.12rem; + } + + label { + font-size: 0.78rem; + font-weight: 700; + } + + .hint { + margin: 0; + font-size: 0.74rem; + color: #375069; + } +} + +.directory .directory-footer .ringworld-seed-items, +.directory .directory-footer .ringworld-random-actor { + margin-top: 0.35rem; + width: 100%; +} + +.ringworld-directory-controls { + display: grid; + gap: 0.3rem; + margin-top: 0.35rem; +} + +.ringworld-directory-button { + width: 100%; + padding: 0.3rem 0.5rem; + border: 1px solid #7f9cba; + border-radius: 4px; + background: #eef5fd; + color: #24364b; +} + +.ringworld-chat-card { + border: 1px solid #9aa7b4; + border-radius: 8px; + background: linear-gradient(155deg, #f7fbff 0%, #e7f0fb 62%, #edf5ff 100%); + padding: 0.5rem 0.58rem; + font-family: "Rajdhani", "Orbitron", "Eurostile", "Bank Gothic", "Segoe UI", sans-serif; + box-shadow: inset 0 0 0 1px rgba(128, 168, 210, 0.28); + + h3 { + margin: 0; + line-height: 1.1; + font-size: 1.08rem; + color: #1f3550; + letter-spacing: 0.02em; + text-transform: uppercase; + } + + p { + margin: 0.22rem 0 0; + font-size: 0.95rem; + color: #223548; + } + + .rw-chat-header { + display: grid; + grid-template-columns: 2.25rem 1fr; + gap: 0.4rem; + align-items: center; + margin-bottom: 0.24rem; + } + + .rw-chat-actor-img { + width: 2.25rem; + height: 2.25rem; + border-radius: 6px; + object-fit: cover; + border: 1px solid #8ea3bc; + background: #f0f5fc; + } + + .rw-chat-header-text { + min-width: 0; + } + + .rw-chat-subtitle { + display: inline-flex; + align-items: center; + gap: 0.22rem; + font-weight: 700; + color: #2b425a; + letter-spacing: 0.02em; + } + + .rw-chat-tags { + display: grid; + gap: 0.18rem; + margin: 0.18rem 0 0.08rem; + } + + .rw-chat-tag { + display: inline-flex; + align-items: center; + gap: 0.22rem; + min-width: 0; + font-size: 0.9rem; + background: rgba(228, 239, 251, 0.78); + border: 1px solid #9fb4cb; + border-radius: 4px; + padding: 0.1rem 0.24rem; + } + + .rw-chat-tag-img { + width: 1rem; + height: 1rem; + border-radius: 3px; + object-fit: cover; + border: 1px solid #8ea3bc; + background: #f0f5fc; + flex: 0 0 auto; + } + + .rw-chat-outcome { + font-weight: 800; + margin-top: 0.06rem; + font-size: 2rem; + line-height: 0.94; + letter-spacing: 0.02em; + text-transform: uppercase; + } + + .rw-chat-notes { + font-size: 0.85rem; + color: #3a4e63; + margin-top: 0.34rem; + border-top: 1px dotted #90a6be; + padding-top: 0.26rem; + } + + .outcome.success, + .outcome.special { + color: #187047; + } + + .outcome.failure, + .outcome.fumble { + color: #b32929; + } + + .rw-chat-outcome.success, + .rw-chat-outcome.special { + color: #0d844d; + text-shadow: 0 0 8px rgba(13, 132, 77, 0.28); + } + + .rw-chat-outcome.failure, + .rw-chat-outcome.fumble { + color: #b01f1f; + text-shadow: 0 0 8px rgba(176, 31, 31, 0.22); + } + + .rw-chat-hitline { + margin-top: 0.28rem; + padding: 0.14rem 0.22rem; + border: 1px solid #7ca2cb; + border-radius: 5px; + background: rgba(226, 240, 255, 0.72); + } + + .rw-chat-hit-label { + font-size: 0.92rem; + font-weight: 700; + color: #2f4a66; + margin-right: 0.2rem; + } + + .rw-chat-hit-value { + font-size: 1.38rem; + font-weight: 800; + color: #284a8a; + letter-spacing: 0.02em; + text-transform: uppercase; + } + + .rw-chat-hit-roll { + font-size: 0.84rem; + color: #3a5775; + font-weight: 600; + } + + .rw-chat-damage-line { + margin-top: 0.18rem; + padding: 0.16rem 0.22rem; + border: 1px solid #ad8b57; + border-radius: 5px; + background: rgba(255, 244, 222, 0.82); + display: flex; + align-items: baseline; + gap: 0.26rem; + flex-wrap: wrap; + } + + .rw-chat-damage-label { + font-size: 0.92rem; + font-weight: 700; + color: #4f3d24; + } + + .rw-chat-damage-value { + font-size: 1.45rem; + line-height: 0.95; + font-weight: 900; + color: #8f2d1f; + letter-spacing: 0.01em; + min-width: 1.25rem; + } + + .rw-chat-damage-breakdown { + font-size: 0.85rem; + color: #665239; + font-weight: 600; + } +} + +.ringworld-attack-dialog { + font-family: "Rajdhani", "Orbitron", "Eurostile", "Bank Gothic", "Segoe UI", sans-serif; + + .window-header { + background: linear-gradient(155deg, #263950 0%, #1d2f46 100%); + color: #e6f2ff; + } + + .window-content { + background: linear-gradient(165deg, #f8fcff 0%, #e8f1fb 60%, #f4f9ff 100%); + padding: 0.42rem 0.48rem; + } + + .dialog-content { + margin-bottom: 0.35rem; + } + + .dialog-buttons { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.28rem; + } + + .dialog-buttons .dialog-button { + min-height: 2rem; + font-weight: 700; + letter-spacing: 0.02em; + border-radius: 5px; + border: 1px solid #6f8eb0; + background: linear-gradient(180deg, #f8fbff 0%, #dce8f6 100%); + color: #21374c; + } +} + +.ringworld-attack-modifiers { + display: grid; + gap: 0.3rem; + font-family: "Rajdhani", "Orbitron", "Eurostile", "Bank Gothic", "Segoe UI", sans-serif; + + .rw-attack-dialog-head { + display: grid; + grid-template-columns: 2.05rem 2.05rem 1fr; + gap: 0.34rem; + align-items: center; + padding: 0.22rem 0.26rem; + border: 1px solid #9ab1ca; + border-radius: 6px; + background: linear-gradient(155deg, #edf4fd 0%, #e0ebf8 100%); + } + + .rw-attack-dialog-avatar, + .rw-attack-dialog-weapon { + width: 2.05rem; + height: 2.05rem; + border-radius: 5px; + object-fit: cover; + border: 1px solid #87a2bf; + background: #f1f6fd; + } + + .rw-attack-dialog-title-block h3 { + margin: 0; + font-size: 0.95rem; + line-height: 1.05; + letter-spacing: 0.02em; + text-transform: uppercase; + color: #243d57; + } + + .rw-attack-dialog-title-block p { + margin: 0.08rem 0 0; + font-size: 0.78rem; + color: #3a5673; + font-weight: 600; + } + + .form-group { + display: grid; + grid-template-columns: 9.4rem minmax(0, 1fr); + align-items: center; + column-gap: 0.35rem; + margin: 0; + } + + .form-group > label { + margin: 0; + font-size: 0.88rem; + font-weight: 700; + color: #20364d; + } + + .form-group input, + .form-group select { + height: 1.9rem; + margin: 0; + } + + .notes { + margin: 0.1rem 0 0; + font-size: 0.8rem; + color: #34506d; + border-top: 1px dotted #91a7bf; + padding-top: 0.2rem; + } +} + +.ringworld-hit-location-dialog { + font-family: "Rajdhani", "Orbitron", "Eurostile", "Bank Gothic", "Segoe UI", sans-serif; + + .window-header { + background: linear-gradient(155deg, #263950 0%, #1d2f46 100%); + color: #e6f2ff; + } + + .window-content { + background: linear-gradient(165deg, #f8fcff 0%, #e8f1fb 60%, #f4f9ff 100%); + padding: 0.42rem 0.48rem; + } + + .dialog-buttons { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.24rem; + } +} + +.ringworld-hit-picker { + display: grid; + gap: 0.34rem; + + .rw-hit-picker-top { + display: grid; + } + + .rw-hit-picker-body { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + grid-template-areas: + ". head ." + "lArm chest rArm" + ". abdomen ." + "lLeg . rLeg"; + gap: 0.18rem; + align-items: stretch; + } + + .rw-hit-picker-choice { + border: 1px solid #7f9fbe; + border-radius: 6px; + background: linear-gradient(180deg, #f6fbff 0%, #e0ebf8 100%); + color: #1f3650; + font-size: 0.86rem; + font-weight: 700; + min-height: 2rem; + padding: 0.14rem 0.24rem; + text-transform: uppercase; + letter-spacing: 0.02em; + } + + .rw-hit-picker-choice.active { + border-color: #3b6ea3; + background: linear-gradient(180deg, #d6ebff 0%, #bedcfa 100%); + box-shadow: 0 0 0 2px rgba(59, 110, 163, 0.22) inset; + } + + .rw-loc-head { grid-area: head; } + .rw-loc-l-arm { grid-area: lArm; min-height: 2.5rem; } + .rw-loc-r-arm { grid-area: rArm; min-height: 2.5rem; } + .rw-loc-chest { grid-area: chest; min-height: 2.5rem; } + .rw-loc-abdomen { grid-area: abdomen; min-height: 2.5rem; } + .rw-loc-l-leg { grid-area: lLeg; min-height: 2.45rem; } + .rw-loc-r-leg { grid-area: rLeg; min-height: 2.45rem; } + + &.schema-dolphin .rw-hit-picker-body { + grid-template-areas: + ". head ." + "lArm chest rArm" + ". abdomen ." + ". lLeg ." + ". rLeg ."; + } + + &.schema-puppeteer .rw-hit-picker-body, + &.schema-bandersnatchi .rw-hit-picker-body { + grid-template-areas: + ". head ." + "lArm chest rArm" + "lLeg abdomen rLeg"; + } +} + +#combat-tracker, +#combat { + &.ringworld-impulse-only { + > :not(.ringworld-impulse-panel) { + display: none !important; + } + + > #combat-tracker, + > .combat-tracker, + > .directory-list, + > .combatants { + display: none !important; + } + + .rw-hide-core-tracker { + display: none !important; + } + } + + &.ringworld-impulse-mode { + .rw-hide-initiative, + .token-initiative, + .combatant-initiative, + .initiative, + [data-action="rollInitiative"], + [data-control="rollInitiative"], + .combatant .fa-dice-d20 { + display: none !important; + } + } + + .ringworld-impulse-panel { + margin: 0 0 0.35rem; + border: 1px solid #6e88a6; + border-radius: 6px; + background: #e6eef8; + padding: 0.3rem; + font-size: 0.72rem; + color: #22364b; + } + + .rw-impulse-top { + display: grid; + grid-template-columns: auto auto 1fr; + align-items: center; + gap: 0.25rem; + margin-bottom: 0.28rem; + } + + .rw-impulse-clock strong { + font-size: 0.88rem; + } + + .rw-impulse-controls { + display: flex; + justify-content: flex-end; + gap: 0.18rem; + } + + .rw-impulse-controls button { + border: 1px solid #7895b6; + border-radius: 4px; + background: #f2f7fd; + color: #1f3348; + padding: 0.12rem 0.25rem; + font-size: 0.68rem; + line-height: 1.1; + } + + .rw-imp-list { + display: grid; + gap: 0.2rem; + max-height: 15rem; + overflow-y: auto; + padding-right: 0.08rem; + } + + .rw-imp-row { + border: 1px solid #8ba3bf; + border-radius: 4px; + background: #f7fbff; + padding: 0.18rem; + display: grid; + gap: 0.16rem; + } + + .rw-imp-row:nth-child(odd) { + background: #edf5ff; + } + + .rw-imp-row-head { + display: grid; + gap: 0.08rem; + } + + .rw-imp-name { + font-weight: 700; + line-height: 1.1; + } + + .rw-imp-meta { + font-size: 0.65rem; + color: #39506a; + line-height: 1.1; + } + + .rw-imp-row-controls { + display: flex; + flex-wrap: wrap; + gap: 0.15rem; + align-items: center; + } + + .rw-imp-row-controls select { + border: 1px solid #7f9dbc; + border-radius: 4px; + background: #ffffff; + color: #1f3348; + min-width: 0; + padding: 0.08rem 0.14rem; + font-size: 0.68rem; + flex: 1 1 9.5rem; + } + + .rw-imp-row-controls button { + border: 1px solid #7895b6; + border-radius: 4px; + background: #f2f7fd; + color: #1f3348; + padding: 0.12rem 0.26rem; + font-size: 0.66rem; + line-height: 1.1; + min-width: 2.8rem; + flex: 0 0 auto; + } + + .rw-imp-row-controls label { + display: inline-flex; + align-items: center; + gap: 0.12rem; + font-size: 0.63rem; + font-weight: 700; + color: #2b425b; + white-space: nowrap; + } + + .rw-imp-row-controls input[type="number"] { + width: 2.8rem; + border: 1px solid #7f9dbc; + border-radius: 4px; + background: #fff; + color: #1f3348; + padding: 0.08rem 0.12rem; + font-size: 0.66rem; + line-height: 1.1; + text-align: center; + } + + .rw-save-mods { + min-width: 4.2rem !important; + } +} + +.ringworld-impulse-chat-marker, +.ringworld-impulse-hud { + border: 1px solid #7694b6; + border-radius: 8px; + background: linear-gradient(165deg, rgba(238, 246, 255, 0.98) 0%, rgba(225, 238, 252, 0.96) 100%); + box-shadow: 0 6px 16px rgba(19, 39, 62, 0.18); + color: #1d344c; + font-family: "Rajdhani", "Orbitron", "Eurostile", "Bank Gothic", "Segoe UI", sans-serif; +} + +.ringworld-impulse-hud { + position: fixed; + left: 78px; + bottom: 18px; + width: 300px; + z-index: 72; + padding: 0.34rem 0.38rem; +} + +.ringworld-impulse-chat-marker { + margin: 0.25rem 0.25rem 0.35rem; + padding: 0.28rem 0.3rem; +} + +.rw-imp-mini-head { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: 0.3rem; + font-size: 0.88rem; + margin-bottom: 0.22rem; + cursor: move; + user-select: none; + + strong { + font-size: 1.04rem; + letter-spacing: 0.02em; + } +} + +.ringworld-impulse-hud.dragging { + opacity: 0.93; +} + +.rw-imp-mini-list { + display: grid; + gap: 0.14rem; + max-height: 10.2rem; + overflow-y: auto; + margin-bottom: 0.26rem; + padding-right: 0.08rem; +} + +.rw-imp-mini-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 0.25rem; + align-items: center; + border: 1px solid #9bb1ca; + border-radius: 4px; + background: rgba(248, 252, 255, 0.98); + padding: 0.1rem 0.16rem; + font-size: 0.76rem; +} + +.rw-imp-mini-row:nth-child(odd) { + background: rgba(228, 239, 251, 0.85); +} + +.rw-imp-mini-name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 700; +} + +.rw-imp-mini-action { + white-space: nowrap; + font-weight: 600; + color: #304a65; +} + +.rw-imp-mini-empty { + border: 1px dashed #92abc7; + border-radius: 4px; + background: rgba(247, 252, 255, 0.85); + padding: 0.2rem 0.24rem; + font-size: 0.75rem; + color: #3d5673; +} + +.rw-imp-mini-controls { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.16rem; + + button { + border: 1px solid #7694b6; + border-radius: 4px; + background: linear-gradient(180deg, #f8fbff 0%, #dce8f6 100%); + color: #21374c; + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.01em; + padding: 0.12rem 0.2rem; + } +} diff --git a/styles/ringworld.css b/styles/ringworld.css new file mode 100644 index 0000000..aa4087e --- /dev/null +++ b/styles/ringworld.css @@ -0,0 +1 @@ +.ringworld-sheet{color:#1b2a3a;background:linear-gradient(165deg,#f4f7fb,#e8eff8 55%,#f8fbff);font-size:12.5px;line-height:1.3;font-family:Rajdhani,Orbitron,Eurostile,Bank Gothic,Segoe UI,sans-serif}.ringworld-sheet input,.ringworld-sheet select,.ringworld-sheet textarea,.ringworld-sheet button{border:1px solid #91a6bf;border-radius:4px;background:#fff;color:#1b2a3a}.ringworld-sheet input,.ringworld-sheet select,.ringworld-sheet textarea{padding:.18rem .3rem}.ringworld-sheet button{cursor:pointer;padding:.2rem .45rem;background:linear-gradient(180deg,#f8fbff,#dfe9f6);border-color:#6f8eb0}.ringworld-sheet .sheet-header{display:grid;gap:.3rem;margin-bottom:.3rem}.ringworld-sheet .panel{border:1px solid #8fa8c2;border-radius:6px;background:#ffffffc7;padding:.28rem;margin-bottom:.2rem}.ringworld-sheet .panel-header{display:flex;justify-content:space-between;align-items:center;gap:.4rem;margin-bottom:.35rem}.ringworld-sheet .toolbar{display:flex;align-items:center;gap:.35rem;margin-bottom:.35rem}.ringworld-sheet .tabs{display:flex;gap:.35rem;margin-bottom:.3rem}.ringworld-sheet .tabs .item{border:1px solid #7d97b3;border-radius:4px;padding:.2rem .5rem;background:#e5eef9d9;font-weight:600;font-size:.84rem}.ringworld-sheet .char-grid,.ringworld-sheet .condition-grid,.ringworld-sheet .location-list{display:grid;gap:.3rem}.ringworld-sheet .char-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.ringworld-sheet .resource-row{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap}.ringworld-sheet .item-list{list-style:none;margin:0;padding:0}.ringworld-sheet .item-list .item{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr) minmax(0,1fr) auto;align-items:center;gap:.35rem;margin-bottom:.22rem;padding:.2rem .3rem;border:1px solid #a0b4ca;border-radius:4px;background:#fafdffeb}.ringworld-sheet .item-list .item>div{min-width:0}.ringworld-sheet .item-list .item>div:not(.actions){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ringworld-sheet .actions{display:flex;justify-content:flex-end;gap:.2rem}.ringworld-sheet .actions button{font-size:.74rem;padding:.15rem .35rem;min-width:1.4rem}.ringworld-sheet .location-row{display:grid;grid-template-columns:1.3fr 1fr auto 1fr;align-items:center;gap:.3rem}.ringworld-sheet .inline-pair{display:inline-flex;align-items:center;gap:.2rem}.ringworld-sheet .skills-badge{margin-left:auto;border:1px solid #7b98b7;border-radius:999px;background:#edf4fe;padding:.1rem .45rem;font-size:.78rem}.ringworld-sheet .rw-header h1{margin:0;text-transform:uppercase;letter-spacing:.03em;color:#2d3e54;font-size:1.72rem;font-weight:700}.ringworld-sheet .rw-title-row{display:flex;align-items:center;justify-content:space-between;gap:.4rem}.ringworld-sheet .rw-lock-status{display:inline-block;border:1px solid #7f97b3;border-radius:999px;background:#e9f2fce6;color:#2b4057;font-size:.73rem;font-weight:700;padding:.1rem .45rem}.ringworld-sheet .rw-profile{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.16rem}.ringworld-sheet .rw-profile label{display:grid;gap:.06rem;font-size:.75rem;font-weight:600}.ringworld-sheet .rw-profile .rw-encumbrance-field{grid-column:1/-1}.ringworld-sheet .rw-profile .rw-encumbrance-field input{min-height:1.55rem;font-size:.88rem;font-weight:700;color:#1f3348;border-width:2px;border-color:#6486ac;background:linear-gradient(180deg,#f9fcff,#e5effc)}.ringworld-sheet .rw-upper-grid{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr);gap:.24rem;align-items:start}.ringworld-sheet .rw-left-stack,.ringworld-sheet .rw-right-stack{display:grid;gap:.2rem}.ringworld-sheet .rw-quick{display:grid;gap:.2rem;max-width:none;width:100%;margin-bottom:0}.ringworld-sheet .rw-overview-list{max-height:10.9rem;overflow-y:auto;overflow-x:hidden;padding-right:.08rem}.ringworld-sheet .rw-overview-list .item{min-height:1.7rem;margin-bottom:.16rem;padding:.14rem .2rem;gap:.18rem;font-size:.73rem;font-weight:600}.ringworld-sheet .rw-overview-list .item>div:not(.actions){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ringworld-sheet .rw-overview-list .item:nth-child(odd){background:#e4effbf5}.ringworld-sheet .item-title{display:inline-flex;align-items:center;gap:.22rem;min-width:0}.ringworld-sheet .item-title span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ringworld-sheet .item-icon{width:1rem;height:1rem;border-radius:2px;flex:0 0 auto;object-fit:cover}.ringworld-sheet .rw-overview-weapons .rw-overview-list{max-height:11.1rem}.ringworld-sheet .rw-overview-content{display:grid;grid-template-columns:minmax(0,1.26fr) minmax(0,.74fr);gap:.24rem;align-items:start}.ringworld-sheet .rw-overview-side{display:grid;gap:.24rem}.ringworld-sheet .rw-attribute-board{display:grid;gap:.08rem;border:1px solid #92a7c0;border-radius:6px;background:#ffffffad;padding:.16rem .18rem}.ringworld-sheet .attr-row{display:grid;grid-template-columns:minmax(82px,.44fr) minmax(0,1.56fr);gap:.06rem;align-items:center;padding:.03rem .08rem;border-bottom:1px dotted #8da4bf}.ringworld-sheet .attr-row:last-child{border-bottom:none}.ringworld-sheet .rw-attribute-board .attr-row:nth-child(odd){background:#e4effb99;border-radius:4px}.ringworld-sheet .lhs,.ringworld-sheet .rhs{display:flex;align-items:center;gap:.1rem;min-width:0;font-size:.8rem;font-weight:600;color:#26384d}.ringworld-sheet .lhs{display:grid;grid-template-columns:2.6rem 2.9rem;align-items:center;column-gap:.12rem}.ringworld-sheet .stat-roll-btn{width:2.5rem;min-width:2.5rem;height:1.35rem;padding:0;font-size:.95rem;font-weight:800;letter-spacing:.01em;border-color:#6f8eb0}.ringworld-sheet .lhs input{width:2.9rem;text-align:center;font-size:.8rem;justify-self:start}.ringworld-sheet .rhs>span:first-child{min-width:3.8rem}.ringworld-sheet .rhs{display:grid;grid-template-columns:minmax(0,.95fr) minmax(7.8rem,13.2rem);align-items:center;column-gap:.14rem}.ringworld-sheet .rhs input[type=text]{width:100%;min-width:0}.ringworld-sheet .rhs input[type=number]{width:2.8rem;text-align:center}.ringworld-sheet .rhs .inline-pair{margin-left:0;justify-self:end}.ringworld-sheet .attr-row button.roll-derived{margin-left:.1rem}.ringworld-sheet .rw-tools{display:flex;flex-wrap:wrap;gap:.18rem;align-items:center;max-width:100%}.ringworld-sheet .rw-tools label{display:inline-flex;align-items:center;gap:.2rem;font-size:.78rem;font-weight:600}.ringworld-sheet .rw-tools input{width:3.8rem;text-align:center}.ringworld-sheet .rw-tools .reset-conditions{flex:1;min-width:11rem}.ringworld-sheet .rw-notes p{margin:0;color:#3c5169;font-size:.82rem}.ringworld-sheet .skills-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.28rem}.ringworld-sheet .skills-grid .skill-category:nth-child(5){grid-column:2}.ringworld-sheet .rw-character-builder{display:grid;gap:.28rem;margin-bottom:.35rem}.ringworld-sheet .rw-builder-actions{display:inline-flex;gap:.2rem;flex-wrap:wrap}.ringworld-sheet .rw-builder-pools{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.16rem;font-size:.78rem}.ringworld-sheet .rw-builder-pools>div{border:1px solid #9ab0c8;border-radius:4px;background:#f0f7ffdb;padding:.16rem .24rem}.ringworld-sheet .rw-builder-pursuits{display:grid;gap:.16rem}.ringworld-sheet .rw-builder-pursuits h4{margin:0;font-size:.76rem;text-transform:uppercase;letter-spacing:.02em;color:#2f455d}.ringworld-sheet .builder-add-row,.ringworld-sheet .builder-pursuit-row{display:grid;grid-template-columns:minmax(0,1fr) 4.1rem auto auto;gap:.16rem;align-items:center}.ringworld-sheet .builder-add-row{grid-template-columns:minmax(0,1fr) 4.1rem auto}.ringworld-sheet .builder-pursuit-list{list-style:none;padding:0;margin:0;display:grid;gap:.14rem}.ringworld-sheet .rw-builder-note{margin:0;font-size:.75rem;color:#324962}.ringworld-sheet .skill-category{margin:0;padding:.28rem}.ringworld-sheet .skill-category h3{margin:0 0 .15rem;font-size:.88rem;text-transform:uppercase;letter-spacing:.02em;color:#26364a}.ringworld-sheet .skill-category .root-max{display:inline-grid;grid-template-columns:auto 3.4rem;align-items:center;gap:.18rem;margin-bottom:.18rem;font-size:.72rem;font-weight:600}.ringworld-sheet .skill-category .root-max input[readonly]{background:#edf3fb;color:#2b4057}.ringworld-sheet .compact-skill-list .skill-row{grid-template-columns:minmax(0,1fr) auto auto auto;min-height:1.42rem;margin-bottom:.14rem;padding:.1rem .16rem;border-radius:3px}.ringworld-sheet .compact-skill-list .skill-name{display:inline-flex;align-items:center;gap:.22rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.77rem;line-height:1.2;padding-right:.1rem}.ringworld-sheet .compact-skill-list .skill-name span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ringworld-sheet .compact-skill-list .skill-base,.ringworld-sheet .compact-skill-list .skill-value{min-width:2.4rem;text-align:right;font-size:.76rem;font-variant-numeric:tabular-nums}.ringworld-sheet .compact-skill-list .alloc-chip{display:inline-block;margin-left:.18rem;padding:.04rem .2rem;border-radius:999px;border:1px solid #6f8eae;background:#ecf4fd;color:#2a445f;font-size:.62rem;font-weight:700;line-height:1;vertical-align:middle}.ringworld-sheet .skill-actions{gap:.12rem}.ringworld-sheet .skill-columns-head{display:grid;grid-template-columns:minmax(0,1fr) auto auto auto;gap:.14rem;padding:.02rem .18rem .14rem;margin-bottom:.1rem;border-bottom:1px dashed #9ab0c8;font-size:.66rem;font-weight:700;text-transform:uppercase;letter-spacing:.02em;color:#375069}.ringworld-sheet .skill-icon{width:.92rem;height:.92rem;border-radius:2px;border:1px solid #87a2bf;object-fit:cover;background:#0a0d12;flex:0 0 auto}.ringworld-sheet .icon-btn{width:1.34rem;height:1.34rem;min-width:1.34rem;display:inline-flex;align-items:center;justify-content:center;padding:0;font-size:.7rem;line-height:1}.ringworld-sheet .icon-btn i{pointer-events:none}.ringworld-sheet .missing-skill{border-color:#bf9863;background:#fff8ec}.ringworld-sheet .additional-skills summary{cursor:pointer;font-weight:600}.ringworld-sheet .rw-hit-body{display:grid;grid-template-columns:repeat(3,74px);grid-template-areas:". head ." "lArm chest rArm" ". abdomen ." "lLeg . rLeg";gap:.12rem;align-items:center;justify-content:start;width:100%}.ringworld-sheet .rw-hit-body.schema-dolphin{grid-template-areas:". head ." "lArm chest rArm" ". abdomen ." ". lLeg ." ". rLeg ."}.ringworld-sheet .rw-hit-body.schema-puppeteer{grid-template-areas:". head ." "lArm chest rArm" "lLeg abdomen rLeg"}.ringworld-sheet .rw-hit-body.schema-trinoc{grid-template-areas:". head ." "lArm chest rArm" ". abdomen ." "lLeg . rLeg"}.ringworld-sheet .rw-hit-body.schema-bandersnatchi{grid-template-areas:". head ." "lArm chest rArm" "lLeg abdomen rLeg"}.ringworld-sheet .rw-hit-toolbar{display:grid;grid-template-columns:auto;align-items:center;gap:.1rem;margin-bottom:.08rem}.ringworld-sheet .rw-hit-toolbar .roll-hit-location{justify-self:start}.ringworld-sheet .rw-hit-main .rw-hit-body{justify-content:center}.ringworld-sheet .body-loc{border:1px solid #8ea7c2;border-radius:6px;background:#f5fafff2;padding:.12rem .14rem;display:grid;gap:.05rem;min-height:68px}.ringworld-sheet .body-loc h4{margin:0;font-size:.63rem;text-transform:uppercase;color:#2c4158}.ringworld-sheet .body-loc label{display:flex;justify-content:space-between;align-items:center;gap:.2rem;font-size:.6rem;font-weight:600}.ringworld-sheet .body-loc input{width:1.84rem;text-align:center;font-size:.6rem;padding:.05rem .08rem}.ringworld-sheet .body-head{grid-area:head;min-height:64px}.ringworld-sheet .body-lArm{grid-area:lArm;min-height:70px}.ringworld-sheet .body-rArm{grid-area:rArm;min-height:70px}.ringworld-sheet .body-chest{grid-area:chest;min-height:70px}.ringworld-sheet .body-abdomen{grid-area:abdomen;min-height:70px}.ringworld-sheet .body-lLeg{grid-area:lLeg;min-height:68px}.ringworld-sheet .body-rLeg{grid-area:rLeg;min-height:68px}.ringworld-sheet .rw-right-lower{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);gap:.2rem;align-items:start}.ringworld-sheet .rw-portrait-panel{display:grid;gap:.1rem;align-content:start;margin-bottom:0}.ringworld-sheet .rw-portrait-img{width:100%;max-width:none;aspect-ratio:4/3;min-height:7.4rem;margin:0 auto;border:1px solid #88a4c1;border-radius:6px;object-fit:cover;background:linear-gradient(160deg,#e1eaf5,#f6fbff);cursor:pointer}.ringworld-sheet .loc-roll{font-size:.56rem;color:#476482;font-weight:600}.ringworld-sheet .loc-ap-total{font-size:.56rem;font-weight:700;color:#2e4762}.ringworld-sheet .rw-hit-layout{display:block}.ringworld-sheet .rw-hit-summary{border:1px solid #9bb0c8;border-radius:6px;background:#f8fcfff2;padding:.14rem .18rem;margin-bottom:0}.ringworld-sheet .rw-hit-summary h4{margin:0 0 .14rem;font-size:.74rem;text-transform:uppercase;color:#2d4259}.ringworld-sheet .rw-hit-summary-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:baseline;gap:.25rem;border-bottom:1px dotted #9eb1c8;padding:.03rem 0;font-size:.74rem;font-weight:600}.ringworld-sheet .rw-hit-summary-row:last-of-type{border-bottom:none}.ringworld-sheet .rw-hit-summary-row:nth-child(odd){background:#e4effb99;border-radius:3px;padding-left:.14rem;padding-right:.14rem}.ringworld-sheet .rw-hit-label{color:#243a52}.ringworld-sheet .rw-hit-values{text-align:right;white-space:nowrap}.ringworld-sheet .rw-hit-unconscious{display:grid;gap:.1rem;margin-top:.12rem;font-size:.73rem;font-weight:600}.ringworld-sheet .rw-hit-unconscious input{width:4rem;height:1.25rem;text-align:center}.ringworld-sheet .rw-hit-summary-below{margin-top:.05rem;width:fit-content;min-width:min(100%,24rem);max-width:100%}.ringworld-sheet .rw-weapon-list .item{grid-template-columns:minmax(0,1.2fr) minmax(0,.42fr) minmax(0,.95fr) minmax(0,.58fr) minmax(0,.95fr) auto}.ringworld-sheet .rw-armor-list .item{grid-template-columns:minmax(0,1.1fr) minmax(0,.42fr) minmax(0,.62fr) minmax(0,.72fr) auto}.ringworld-sheet .rw-gear-list .item{grid-template-columns:minmax(0,1.36fr) minmax(0,.62fr) minmax(0,1.02fr) auto}.ringworld-sheet .rw-overview-weapons .rw-weapon-list .item{grid-template-columns:minmax(0,1.26fr) minmax(0,1.02fr) minmax(0,.58fr) minmax(0,.96fr) auto}.ringworld-sheet .rw-overview-armor .rw-armor-list .item{grid-template-columns:minmax(0,1.2fr) minmax(0,.68fr) minmax(0,.78fr) auto}.ringworld-sheet .rw-overview-gear .rw-gear-list .item{grid-template-columns:minmax(0,1.2fr) minmax(0,1.24fr) auto}@media (max-width: 1250px){.ringworld-sheet .rw-upper-grid,.ringworld-sheet .rw-overview-content,.ringworld-sheet .rw-right-lower{grid-template-columns:1fr}}@media (max-width: 1100px){.ringworld-sheet .rw-profile{grid-template-columns:repeat(2,minmax(0,1fr))}.ringworld-sheet .rw-upper-grid,.ringworld-sheet .rw-overview-content{grid-template-columns:1fr}.ringworld-sheet .attr-row{grid-template-columns:1fr;gap:.2rem}.ringworld-sheet .rhs .inline-pair{margin-left:0}.ringworld-sheet .skills-grid{grid-template-columns:1fr}.ringworld-sheet .skills-grid .skill-category:nth-child(5){grid-column:auto}.ringworld-sheet .rw-builder-pools,.ringworld-sheet .builder-add-row,.ringworld-sheet .builder-pursuit-row{grid-template-columns:1fr}}.rw-builder-alloc-form{display:grid;gap:.4rem}.rw-builder-alloc-form .rw-builder-alloc-head{display:grid;grid-template-columns:2.4rem minmax(0,1fr);gap:.45rem;align-items:center}.rw-builder-alloc-form .rw-builder-alloc-head img{width:2.2rem;height:2.2rem;border-radius:4px;border:1px solid #7f97b3;object-fit:cover;background:#0b0f15}.rw-builder-alloc-form .rw-builder-alloc-head h3{margin:0;font-size:.95rem}.rw-builder-alloc-form .rw-builder-alloc-head p{margin:.08rem 0 0;font-size:.74rem;color:#38516b}.rw-builder-alloc-form label{display:grid;gap:.12rem;font-size:.8rem;font-weight:600}.rw-builder-alloc-form input[type=number]{width:100%}.rw-builder-alloc-form .hint{margin:0;font-size:.74rem;color:#38516b}.rw-stat-roll-form{display:grid;gap:.42rem}.rw-stat-roll-form .form-group{display:grid;gap:.12rem}.rw-stat-roll-form label{font-size:.78rem;font-weight:700}.rw-stat-roll-form .hint{margin:0;font-size:.74rem;color:#375069}.directory .directory-footer .ringworld-seed-items,.directory .directory-footer .ringworld-random-actor{margin-top:.35rem;width:100%}.ringworld-directory-controls{display:grid;gap:.3rem;margin-top:.35rem}.ringworld-directory-button{width:100%;padding:.3rem .5rem;border:1px solid #7f9cba;border-radius:4px;background:#eef5fd;color:#24364b}.ringworld-chat-card{border:1px solid #9aa7b4;border-radius:8px;background:linear-gradient(155deg,#f7fbff,#e7f0fb 62%,#edf5ff);padding:.5rem .58rem;font-family:Rajdhani,Orbitron,Eurostile,Bank Gothic,Segoe UI,sans-serif;box-shadow:inset 0 0 0 1px #80a8d247}.ringworld-chat-card h3{margin:0;line-height:1.1;font-size:1.08rem;color:#1f3550;letter-spacing:.02em;text-transform:uppercase}.ringworld-chat-card p{margin:.22rem 0 0;font-size:.95rem;color:#223548}.ringworld-chat-card .rw-chat-header{display:grid;grid-template-columns:2.25rem 1fr;gap:.4rem;align-items:center;margin-bottom:.24rem}.ringworld-chat-card .rw-chat-actor-img{width:2.25rem;height:2.25rem;border-radius:6px;object-fit:cover;border:1px solid #8ea3bc;background:#f0f5fc}.ringworld-chat-card .rw-chat-header-text{min-width:0}.ringworld-chat-card .rw-chat-subtitle{display:inline-flex;align-items:center;gap:.22rem;font-weight:700;color:#2b425a;letter-spacing:.02em}.ringworld-chat-card .rw-chat-tags{display:grid;gap:.18rem;margin:.18rem 0 .08rem}.ringworld-chat-card .rw-chat-tag{display:inline-flex;align-items:center;gap:.22rem;min-width:0;font-size:.9rem;background:#e4effbc7;border:1px solid #9fb4cb;border-radius:4px;padding:.1rem .24rem}.ringworld-chat-card .rw-chat-tag-img{width:1rem;height:1rem;border-radius:3px;object-fit:cover;border:1px solid #8ea3bc;background:#f0f5fc;flex:0 0 auto}.ringworld-chat-card .rw-chat-outcome{font-weight:800;margin-top:.06rem;font-size:2rem;line-height:.94;letter-spacing:.02em;text-transform:uppercase}.ringworld-chat-card .rw-chat-notes{font-size:.85rem;color:#3a4e63;margin-top:.34rem;border-top:1px dotted #90a6be;padding-top:.26rem}.ringworld-chat-card .outcome.success,.ringworld-chat-card .outcome.special{color:#187047}.ringworld-chat-card .outcome.failure,.ringworld-chat-card .outcome.fumble{color:#b32929}.ringworld-chat-card .rw-chat-outcome.success,.ringworld-chat-card .rw-chat-outcome.special{color:#0d844d;text-shadow:0 0 8px rgba(13,132,77,.28)}.ringworld-chat-card .rw-chat-outcome.failure,.ringworld-chat-card .rw-chat-outcome.fumble{color:#b01f1f;text-shadow:0 0 8px rgba(176,31,31,.22)}.ringworld-chat-card .rw-chat-hitline{margin-top:.28rem;padding:.14rem .22rem;border:1px solid #7ca2cb;border-radius:5px;background:#e2f0ffb8}.ringworld-chat-card .rw-chat-hit-label{font-size:.92rem;font-weight:700;color:#2f4a66;margin-right:.2rem}.ringworld-chat-card .rw-chat-hit-value{font-size:1.38rem;font-weight:800;color:#284a8a;letter-spacing:.02em;text-transform:uppercase}.ringworld-chat-card .rw-chat-hit-roll{font-size:.84rem;color:#3a5775;font-weight:600}.ringworld-chat-card .rw-chat-damage-line{margin-top:.18rem;padding:.16rem .22rem;border:1px solid #ad8b57;border-radius:5px;background:#fff4ded1;display:flex;align-items:baseline;gap:.26rem;flex-wrap:wrap}.ringworld-chat-card .rw-chat-damage-label{font-size:.92rem;font-weight:700;color:#4f3d24}.ringworld-chat-card .rw-chat-damage-value{font-size:1.45rem;line-height:.95;font-weight:900;color:#8f2d1f;letter-spacing:.01em;min-width:1.25rem}.ringworld-chat-card .rw-chat-damage-breakdown{font-size:.85rem;color:#665239;font-weight:600}.ringworld-attack-dialog{font-family:Rajdhani,Orbitron,Eurostile,Bank Gothic,Segoe UI,sans-serif}.ringworld-attack-dialog .window-header{background:linear-gradient(155deg,#263950,#1d2f46);color:#e6f2ff}.ringworld-attack-dialog .window-content{background:linear-gradient(165deg,#f8fcff,#e8f1fb 60%,#f4f9ff);padding:.42rem .48rem}.ringworld-attack-dialog .dialog-content{margin-bottom:.35rem}.ringworld-attack-dialog .dialog-buttons{display:grid;grid-template-columns:1fr 1fr;gap:.28rem}.ringworld-attack-dialog .dialog-buttons .dialog-button{min-height:2rem;font-weight:700;letter-spacing:.02em;border-radius:5px;border:1px solid #6f8eb0;background:linear-gradient(180deg,#f8fbff,#dce8f6);color:#21374c}.ringworld-attack-modifiers{display:grid;gap:.3rem;font-family:Rajdhani,Orbitron,Eurostile,Bank Gothic,Segoe UI,sans-serif}.ringworld-attack-modifiers .rw-attack-dialog-head{display:grid;grid-template-columns:2.05rem 2.05rem 1fr;gap:.34rem;align-items:center;padding:.22rem .26rem;border:1px solid #9ab1ca;border-radius:6px;background:linear-gradient(155deg,#edf4fd,#e0ebf8)}.ringworld-attack-modifiers .rw-attack-dialog-avatar,.ringworld-attack-modifiers .rw-attack-dialog-weapon{width:2.05rem;height:2.05rem;border-radius:5px;object-fit:cover;border:1px solid #87a2bf;background:#f1f6fd}.ringworld-attack-modifiers .rw-attack-dialog-title-block h3{margin:0;font-size:.95rem;line-height:1.05;letter-spacing:.02em;text-transform:uppercase;color:#243d57}.ringworld-attack-modifiers .rw-attack-dialog-title-block p{margin:.08rem 0 0;font-size:.78rem;color:#3a5673;font-weight:600}.ringworld-attack-modifiers .form-group{display:grid;grid-template-columns:9.4rem minmax(0,1fr);align-items:center;column-gap:.35rem;margin:0}.ringworld-attack-modifiers .form-group>label{margin:0;font-size:.88rem;font-weight:700;color:#20364d}.ringworld-attack-modifiers .form-group input,.ringworld-attack-modifiers .form-group select{height:1.9rem;margin:0}.ringworld-attack-modifiers .notes{margin:.1rem 0 0;font-size:.8rem;color:#34506d;border-top:1px dotted #91a7bf;padding-top:.2rem}.ringworld-hit-location-dialog{font-family:Rajdhani,Orbitron,Eurostile,Bank Gothic,Segoe UI,sans-serif}.ringworld-hit-location-dialog .window-header{background:linear-gradient(155deg,#263950,#1d2f46);color:#e6f2ff}.ringworld-hit-location-dialog .window-content{background:linear-gradient(165deg,#f8fcff,#e8f1fb 60%,#f4f9ff);padding:.42rem .48rem}.ringworld-hit-location-dialog .dialog-buttons{display:grid;grid-template-columns:1fr 1fr;gap:.24rem}.ringworld-hit-picker{display:grid;gap:.34rem}.ringworld-hit-picker .rw-hit-picker-top{display:grid}.ringworld-hit-picker .rw-hit-picker-body{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-template-areas:". head ." "lArm chest rArm" ". abdomen ." "lLeg . rLeg";gap:.18rem;align-items:stretch}.ringworld-hit-picker .rw-hit-picker-choice{border:1px solid #7f9fbe;border-radius:6px;background:linear-gradient(180deg,#f6fbff,#e0ebf8);color:#1f3650;font-size:.86rem;font-weight:700;min-height:2rem;padding:.14rem .24rem;text-transform:uppercase;letter-spacing:.02em}.ringworld-hit-picker .rw-hit-picker-choice.active{border-color:#3b6ea3;background:linear-gradient(180deg,#d6ebff,#bedcfa);box-shadow:0 0 0 2px #3b6ea338 inset}.ringworld-hit-picker .rw-loc-head{grid-area:head}.ringworld-hit-picker .rw-loc-l-arm{grid-area:lArm;min-height:2.5rem}.ringworld-hit-picker .rw-loc-r-arm{grid-area:rArm;min-height:2.5rem}.ringworld-hit-picker .rw-loc-chest{grid-area:chest;min-height:2.5rem}.ringworld-hit-picker .rw-loc-abdomen{grid-area:abdomen;min-height:2.5rem}.ringworld-hit-picker .rw-loc-l-leg{grid-area:lLeg;min-height:2.45rem}.ringworld-hit-picker .rw-loc-r-leg{grid-area:rLeg;min-height:2.45rem}.ringworld-hit-picker.schema-dolphin .rw-hit-picker-body{grid-template-areas:". head ." "lArm chest rArm" ". abdomen ." ". lLeg ." ". rLeg ."}.ringworld-hit-picker.schema-puppeteer .rw-hit-picker-body,.ringworld-hit-picker.schema-bandersnatchi .rw-hit-picker-body{grid-template-areas:". head ." "lArm chest rArm" "lLeg abdomen rLeg"}#combat-tracker.ringworld-impulse-only>:not(.ringworld-impulse-panel),#combat.ringworld-impulse-only>:not(.ringworld-impulse-panel){display:none!important}#combat-tracker.ringworld-impulse-only>#combat-tracker,#combat-tracker.ringworld-impulse-only>.combat-tracker,#combat-tracker.ringworld-impulse-only>.directory-list,#combat-tracker.ringworld-impulse-only>.combatants,#combat.ringworld-impulse-only>#combat-tracker,#combat.ringworld-impulse-only>.combat-tracker,#combat.ringworld-impulse-only>.directory-list,#combat.ringworld-impulse-only>.combatants{display:none!important}#combat-tracker.ringworld-impulse-only .rw-hide-core-tracker,#combat.ringworld-impulse-only .rw-hide-core-tracker,#combat-tracker.ringworld-impulse-mode .rw-hide-initiative,#combat-tracker.ringworld-impulse-mode .token-initiative,#combat-tracker.ringworld-impulse-mode .combatant-initiative,#combat-tracker.ringworld-impulse-mode .initiative,#combat-tracker.ringworld-impulse-mode [data-action=rollInitiative],#combat-tracker.ringworld-impulse-mode [data-control=rollInitiative],#combat-tracker.ringworld-impulse-mode .combatant .fa-dice-d20,#combat.ringworld-impulse-mode .rw-hide-initiative,#combat.ringworld-impulse-mode .token-initiative,#combat.ringworld-impulse-mode .combatant-initiative,#combat.ringworld-impulse-mode .initiative,#combat.ringworld-impulse-mode [data-action=rollInitiative],#combat.ringworld-impulse-mode [data-control=rollInitiative],#combat.ringworld-impulse-mode .combatant .fa-dice-d20{display:none!important}#combat-tracker .ringworld-impulse-panel,#combat .ringworld-impulse-panel{margin:0 0 .35rem;border:1px solid #6e88a6;border-radius:6px;background:#e6eef8;padding:.3rem;font-size:.72rem;color:#22364b}#combat-tracker .rw-impulse-top,#combat .rw-impulse-top{display:grid;grid-template-columns:auto auto 1fr;align-items:center;gap:.25rem;margin-bottom:.28rem}#combat-tracker .rw-impulse-clock strong,#combat .rw-impulse-clock strong{font-size:.88rem}#combat-tracker .rw-impulse-controls,#combat .rw-impulse-controls{display:flex;justify-content:flex-end;gap:.18rem}#combat-tracker .rw-impulse-controls button,#combat .rw-impulse-controls button{border:1px solid #7895b6;border-radius:4px;background:#f2f7fd;color:#1f3348;padding:.12rem .25rem;font-size:.68rem;line-height:1.1}#combat-tracker .rw-imp-list,#combat .rw-imp-list{display:grid;gap:.2rem;max-height:15rem;overflow-y:auto;padding-right:.08rem}#combat-tracker .rw-imp-row,#combat .rw-imp-row{border:1px solid #8ba3bf;border-radius:4px;background:#f7fbff;padding:.18rem;display:grid;gap:.16rem}#combat-tracker .rw-imp-row:nth-child(odd),#combat .rw-imp-row:nth-child(odd){background:#edf5ff}#combat-tracker .rw-imp-row-head,#combat .rw-imp-row-head{display:grid;gap:.08rem}#combat-tracker .rw-imp-name,#combat .rw-imp-name{font-weight:700;line-height:1.1}#combat-tracker .rw-imp-meta,#combat .rw-imp-meta{font-size:.65rem;color:#39506a;line-height:1.1}#combat-tracker .rw-imp-row-controls,#combat .rw-imp-row-controls{display:flex;flex-wrap:wrap;gap:.15rem;align-items:center}#combat-tracker .rw-imp-row-controls select,#combat .rw-imp-row-controls select{border:1px solid #7f9dbc;border-radius:4px;background:#fff;color:#1f3348;min-width:0;padding:.08rem .14rem;font-size:.68rem;flex:1 1 9.5rem}#combat-tracker .rw-imp-row-controls button,#combat .rw-imp-row-controls button{border:1px solid #7895b6;border-radius:4px;background:#f2f7fd;color:#1f3348;padding:.12rem .26rem;font-size:.66rem;line-height:1.1;min-width:2.8rem;flex:0 0 auto}#combat-tracker .rw-imp-row-controls label,#combat .rw-imp-row-controls label{display:inline-flex;align-items:center;gap:.12rem;font-size:.63rem;font-weight:700;color:#2b425b;white-space:nowrap}#combat-tracker .rw-imp-row-controls input[type=number],#combat .rw-imp-row-controls input[type=number]{width:2.8rem;border:1px solid #7f9dbc;border-radius:4px;background:#fff;color:#1f3348;padding:.08rem .12rem;font-size:.66rem;line-height:1.1;text-align:center}#combat-tracker .rw-save-mods,#combat .rw-save-mods{min-width:4.2rem!important}.ringworld-impulse-chat-marker,.ringworld-impulse-hud{border:1px solid #7694b6;border-radius:8px;background:linear-gradient(165deg,#eef6fffa,#e1eefcf5);box-shadow:0 6px 16px #13273e2e;color:#1d344c;font-family:Rajdhani,Orbitron,Eurostile,Bank Gothic,Segoe UI,sans-serif}.ringworld-impulse-hud{position:fixed;left:78px;bottom:18px;width:300px;z-index:72;padding:.34rem .38rem}.ringworld-impulse-chat-marker{margin:.25rem .25rem .35rem;padding:.28rem .3rem}.rw-imp-mini-head{display:flex;justify-content:space-between;align-items:baseline;gap:.3rem;font-size:.88rem;margin-bottom:.22rem;cursor:move;-webkit-user-select:none;user-select:none}.rw-imp-mini-head strong{font-size:1.04rem;letter-spacing:.02em}.ringworld-impulse-hud.dragging{opacity:.93}.rw-imp-mini-list{display:grid;gap:.14rem;max-height:10.2rem;overflow-y:auto;margin-bottom:.26rem;padding-right:.08rem}.rw-imp-mini-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.25rem;align-items:center;border:1px solid #9bb1ca;border-radius:4px;background:#f8fcfffa;padding:.1rem .16rem;font-size:.76rem}.rw-imp-mini-row:nth-child(odd){background:#e4effbd9}.rw-imp-mini-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:700}.rw-imp-mini-action{white-space:nowrap;font-weight:600;color:#304a65}.rw-imp-mini-empty{border:1px dashed #92abc7;border-radius:4px;background:#f7fcffd9;padding:.2rem .24rem;font-size:.75rem;color:#3d5673}.rw-imp-mini-controls{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.16rem}.rw-imp-mini-controls button{border:1px solid #7694b6;border-radius:4px;background:linear-gradient(180deg,#f8fbff,#dce8f6);color:#21374c;font-size:.72rem;font-weight:700;letter-spacing:.01em;padding:.12rem .2rem} diff --git a/system.json b/system.json new file mode 100644 index 0000000..1dd5536 --- /dev/null +++ b/system.json @@ -0,0 +1,52 @@ +{ + "id": "ringworld", + "title": "Ringworld", + "description": "Ringworld RPG-compatible percentile system (BRP-like)", + "version": "0.1.0", + "url": "https://github.com/slaguru666/Ringworld", + "manifest": "https://raw.githubusercontent.com/slaguru666/Ringworld/main/system.json", + "download": "https://raw.githubusercontent.com/slaguru666/Ringworld/main/dist/ringworld-system.zip", + "compatibility": { + "minimum": "12", + "verified": "13.351", + "maximum": "13" + }, + "authors": [ + { + "name": "Codex" + } + ], + "esmodules": [ + "module/ringworld.mjs" + ], + "styles": [ + "styles/ringworld.css" + ], + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json" + } + ], + "documentTypes": { + "Actor": { + "explorer": {}, + "creature": {}, + "vehicle": {} + }, + "Item": { + "skill": {}, + "weapon": {}, + "armor": {}, + "gear": {}, + "species": {}, + "pursuit": {} + } + }, + "grid": { + "units": "m" + }, + "primaryTokenAttribute": "system.generalHp.value", + "secondaryTokenAttribute": "system.resources.energy.value" +} diff --git a/template.json b/template.json new file mode 100644 index 0000000..8dc5cd2 --- /dev/null +++ b/template.json @@ -0,0 +1,362 @@ +{ + "Actor": { + "explorer": { + "characteristics": { + "str": 10, + "mas": 10, + "con": 10, + "int": 10, + "pow": 10, + "dex": 10, + "app": 10, + "edu": 10, + "age": 25 + }, + "profile": { + "player": "", + "gender": "", + "chronologicalAge": 25, + "physiologicalAge": 25, + "species": "Human", + "gravity": "normal", + "homeworld": "", + "creditRating": 0 + }, + "derived": { + "actionRanking": 1, + "move": 3, + "run": 6, + "currentMove": 3, + "currentRun": 6 + }, + "encumbrance": { + "rawLoadKg": 0, + "effectiveLoadKg": 0, + "limitKg": 10, + "overLimitKg": 0, + "actionRankingPenalty": 0, + "overLimit": false, + "gravityMultiplier": 1 + }, + "combat": { + "damageMod": "0" + }, + "derivedRolls": { + "health": 50, + "reasoning": 50, + "luck": 50, + "dodge": 50 + }, + "skillRootMax": { + "agility": 50, + "communication": 50, + "knowledge": 50, + "perception": 50, + "technical": 50 + }, + "finance": { + "money": 0, + "inBank": 0 + }, + "notes": { + "defects": "", + "habits": "" + }, + "sheetToolsUnlocked": false, + "characterBuilder": { + "pursuits": [], + "allocations": {}, + "baseBySkillId": {} + }, + "unconsciousLevel": 0, + "generalHp": { + "value": 20, + "max": 20 + }, + "resources": { + "energy": { + "value": 20, + "max": 20 + } + }, + "conditions": { + "prone": false, + "unconscious": false, + "dead": false, + "dying": false, + "unusableLimbs": { + "rArm": false, + "lArm": false, + "rLeg": false, + "lLeg": false + } + }, + "hitLocationSchema": "auto", + "hitLocations": { + "rLeg": { + "label": "Right Leg", + "hp": { "value": 6, "max": 6 }, + "armor": 0 + }, + "lLeg": { + "label": "Left Leg", + "hp": { "value": 6, "max": 6 }, + "armor": 0 + }, + "abdomen": { + "label": "Abdomen", + "hp": { "value": 6, "max": 6 }, + "armor": 0 + }, + "chest": { + "label": "Chest", + "hp": { "value": 7, "max": 7 }, + "armor": 0 + }, + "rArm": { + "label": "Right Arm", + "hp": { "value": 5, "max": 5 }, + "armor": 0 + }, + "lArm": { + "label": "Left Arm", + "hp": { "value": 5, "max": 5 }, + "armor": 0 + }, + "head": { + "label": "Head", + "hp": { "value": 6, "max": 6 }, + "armor": 0 + } + } + }, + "creature": { + "characteristics": { + "str": 10, + "mas": 10, + "con": 10, + "int": 10, + "pow": 10, + "dex": 10, + "app": 10, + "edu": 10, + "age": 10 + }, + "profile": { + "player": "", + "gender": "", + "chronologicalAge": 10, + "physiologicalAge": 10, + "species": "", + "gravity": "normal", + "homeworld": "", + "creditRating": 0 + }, + "derived": { + "actionRanking": 1, + "move": 3, + "run": 6, + "currentMove": 3, + "currentRun": 6 + }, + "encumbrance": { + "rawLoadKg": 0, + "effectiveLoadKg": 0, + "limitKg": 10, + "overLimitKg": 0, + "actionRankingPenalty": 0, + "overLimit": false, + "gravityMultiplier": 1 + }, + "combat": { + "damageMod": "0" + }, + "derivedRolls": { + "health": 50, + "reasoning": 50, + "luck": 50, + "dodge": 50 + }, + "skillRootMax": { + "agility": 50, + "communication": 50, + "knowledge": 50, + "perception": 50, + "technical": 50 + }, + "finance": { + "money": 0, + "inBank": 0 + }, + "notes": { + "defects": "", + "habits": "" + }, + "unconsciousLevel": 0, + "generalHp": { + "value": 20, + "max": 20 + }, + "resources": { + "energy": { + "value": 0, + "max": 0 + } + }, + "conditions": { + "prone": false, + "unconscious": false, + "dead": false, + "dying": false, + "unusableLimbs": { + "rArm": false, + "lArm": false, + "rLeg": false, + "lLeg": false + } + }, + "hitLocationSchema": "auto", + "hitLocations": { + "rLeg": { + "label": "Right Leg", + "hp": { "value": 6, "max": 6 }, + "armor": 0 + }, + "lLeg": { + "label": "Left Leg", + "hp": { "value": 6, "max": 6 }, + "armor": 0 + }, + "abdomen": { + "label": "Abdomen", + "hp": { "value": 6, "max": 6 }, + "armor": 0 + }, + "chest": { + "label": "Chest", + "hp": { "value": 7, "max": 7 }, + "armor": 0 + }, + "rArm": { + "label": "Right Arm", + "hp": { "value": 5, "max": 5 }, + "armor": 0 + }, + "lArm": { + "label": "Left Arm", + "hp": { "value": 5, "max": 5 }, + "armor": 0 + }, + "head": { + "label": "Head", + "hp": { "value": 6, "max": 6 }, + "armor": 0 + } + } + }, + "vehicle": { + "stats": { + "hp": { + "value": 30, + "max": 30 + }, + "armor": 0, + "speed": 0, + "power": { + "value": 0, + "max": 0 + } + } + } + }, + "Item": { + "skill": { + "value": 0, + "category": "", + "specialization": "" + }, + "weapon": { + "equipped": true, + "stowed": false, + "weight": 0, + "linkedSkill": "", + "linkedSkillId": "", + "attackType": "ranged", + "damageFormula": "1d6", + "range": { + "close": "", + "medium": "", + "long": "" + }, + "energyUsedPerAttack": 0, + "ammoUsedPerAttack": 0, + "powerSource": "actor", + "powerPackId": "", + "notes": "" + }, + "armor": { + "equipped": true, + "stowed": false, + "weight": 0, + "value": 0, + "coverage": { + "rLeg": false, + "lLeg": false, + "abdomen": false, + "chest": false, + "rArm": false, + "lArm": false, + "head": false + }, + "perLocation": { + "rLeg": 0, + "lLeg": 0, + "abdomen": 0, + "chest": 0, + "rArm": 0, + "lArm": 0, + "head": 0 + }, + "notes": "" + }, + "gear": { + "equipped": true, + "stowed": false, + "weight": 0, + "cost": 0, + "energyUse": 0, + "powerSupplyNotes": "", + "isPowerPack": false, + "energy": { + "value": 0, + "max": 0 + }, + "notes": "" + }, + "species": { + "playable": true, + "gravity": "normal", + "homeworld": "", + "modifiers": { + "str": 0, + "mas": 0, + "con": 0, + "int": 0, + "pow": 0, + "dex": 0, + "app": 0, + "edu": 0, + "age": 0 + }, + "notes": "" + }, + "pursuit": { + "wealthValue": 0, + "occupationPointsPerYear": 20, + "rollRange": "", + "fixedSkills": [], + "optionalSkillGroups": [], + "notes": "" + } + } +} diff --git a/templates/actor/creature-sheet.hbs b/templates/actor/creature-sheet.hbs new file mode 100644 index 0000000..b279af0 --- /dev/null +++ b/templates/actor/creature-sheet.hbs @@ -0,0 +1,62 @@ +
+
+ +
+ + + / {{system.generalHp.max}} + +
+
+ +
+

Hit Locations

+
+ {{#each hitLocations as |loc|}} +
+ {{loc.label}} (d20 {{loc.rollRange}}) + + / {{loc.hp.max}} + +
+ {{/each}} +
+
+ +
+
+ + +
+ +

Skills

+
    + {{#each skills as |item|}} +
  1. +
    {{item.name}}
    +
    {{item.system.value}}%
    +
    + + + +
    +
  2. + {{/each}} +
+ +

Weapons

+
    + {{#each weapons as |item|}} +
  1. +
    {{item.name}}
    +
    {{item.system.damageFormula}}
    +
    + + + +
    +
  2. + {{/each}} +
+
+
diff --git a/templates/actor/explorer-sheet.hbs b/templates/actor/explorer-sheet.hbs new file mode 100644 index 0000000..3300777 --- /dev/null +++ b/templates/actor/explorer-sheet.hbs @@ -0,0 +1,491 @@ +
diff --git a/templates/actor/vehicle-sheet.hbs b/templates/actor/vehicle-sheet.hbs new file mode 100644 index 0000000..6fe9a64 --- /dev/null +++ b/templates/actor/vehicle-sheet.hbs @@ -0,0 +1,17 @@ +
+
+ +
+ +
+

Vehicle Stats (v1 stub)

+
+ + + + + + +
+
+
diff --git a/templates/chat/attack-card.hbs b/templates/chat/attack-card.hbs new file mode 100644 index 0000000..be1c752 --- /dev/null +++ b/templates/chat/attack-card.hbs @@ -0,0 +1,41 @@ +
+
+ {{actorName}} +
+

{{actorName}} attacks {{targetName}}

+

{{attackOutcomeLabel}}

+
+
+ +
+ + {{#if weaponImg}}{{weaponName}}{{/if}} + Weapon: {{weaponName}} + + + {{#if skillImg}}{{skillName}}{{/if}} + Skill: {{skillName}} + +
+ +

Attack: {{attackRoll}} vs {{attackTarget}}

+ + {{#if isSuccess}} +

+ Hit Location + {{hitLocationLabel}}{{#if hitLocationRoll}} (rolled {{hitLocationRoll}}){{/if}} +

+

+ Total Damage + {{finalDamage}} + ({{rawDamage}} - {{armor}}) +

+ {{#if hasTarget}} +

Remaining Location HP: {{remainingLocationHp}}

+

Remaining General HP: {{remainingGeneralHp}}

+ {{/if}} + {{/if}} + +

Energy: {{energySourceLabel}} spent {{energyCost}}, remaining {{energyRemaining}}

+

{{notes}}

+
diff --git a/templates/chat/skill-roll-card.hbs b/templates/chat/skill-roll-card.hbs new file mode 100644 index 0000000..f0cd178 --- /dev/null +++ b/templates/chat/skill-roll-card.hbs @@ -0,0 +1,18 @@ +
+
+ {{actorName}} +
+

{{actorName}}

+

+ {{#if skillImg}}{{skillName}}{{/if}} + {{skillName}} +

+
+
+

Roll: {{roll}} vs Target: {{target}}

+

+ {{outcomeLabel}} +

+ {{#if isSpecial}}

Special Success.

{{/if}} + {{#if isFumble}}

Fumble.

{{/if}} +
diff --git a/templates/item/armor-sheet.hbs b/templates/item/armor-sheet.hbs new file mode 100644 index 0000000..7620581 --- /dev/null +++ b/templates/item/armor-sheet.hbs @@ -0,0 +1,39 @@ +
+
+ +
+ +
+
+ + + + +
+ +

Coverage

+
+ + + + + + + +
+ +

Per-Location Override (0 = use base value)

+
+ + + + + + + +
+ + + +
+
diff --git a/templates/item/gear-sheet.hbs b/templates/item/gear-sheet.hbs new file mode 100644 index 0000000..f4b911d --- /dev/null +++ b/templates/item/gear-sheet.hbs @@ -0,0 +1,24 @@ +
+
+ +
+ +
+
+ + + + + + + + +
+ + + + + + +
+
diff --git a/templates/item/pursuit-sheet.hbs b/templates/item/pursuit-sheet.hbs new file mode 100644 index 0000000..3f3bdcc --- /dev/null +++ b/templates/item/pursuit-sheet.hbs @@ -0,0 +1,41 @@ +
+
+ +
+ +
+
+ + + +
+ +

Fixed Skills

+
    + {{#each fixedSkills as |skill|}} +
  1. +
    {{skill}}
    +
  2. + {{/each}} +
+ + {{#if optionalGroups.length}} +

Optional Skill Groups

+ {{#each optionalGroups as |group|}} +
+

Choose {{group.choose}}

+
    + {{#each group.skills as |skill|}} +
  1. +
    {{skill}}
    +
  2. + {{/each}} +
+
+ {{/each}} + {{/if}} + + + +
+
diff --git a/templates/item/skill-sheet.hbs b/templates/item/skill-sheet.hbs new file mode 100644 index 0000000..0c2b7b2 --- /dev/null +++ b/templates/item/skill-sheet.hbs @@ -0,0 +1,14 @@ +
+
+ + +
+ +
+
+ + + +
+
+
diff --git a/templates/item/species-sheet.hbs b/templates/item/species-sheet.hbs new file mode 100644 index 0000000..8b9ea64 --- /dev/null +++ b/templates/item/species-sheet.hbs @@ -0,0 +1,35 @@ +
+
+ +
+ +
+
+ + + +
+ +

Characteristic Modifiers

+
+ + + + + + + + + +
+ + + +
+
diff --git a/templates/item/weapon-sheet.hbs b/templates/item/weapon-sheet.hbs new file mode 100644 index 0000000..47d2093 --- /dev/null +++ b/templates/item/weapon-sheet.hbs @@ -0,0 +1,52 @@ +
+
+ + +
+ +
+
+ + + + + + + + + + + + + + +
+ + + +
+
diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8ff5192 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": false, + "skipLibCheck": true, + "allowJs": false, + "noEmit": true, + "types": [ + "@league-of-foundry-developers/foundry-vtt-types" + ] + }, + "include": [ + "src/**/*.ts", + "vite.config.ts" + ] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..40d809e --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,26 @@ +import { resolve } from "node:path"; +import { defineConfig } from "vite"; + +export default defineConfig({ + base: "", + build: { + emptyOutDir: false, + outDir: ".", + sourcemap: true, + lib: { + entry: resolve(__dirname, "src/ringworld.ts"), + formats: ["es"], + fileName: () => "module/ringworld.mjs" + }, + rollupOptions: { + output: { + assetFileNames: (assetInfo) => { + if ((assetInfo.name ?? "").endsWith(".css")) { + return "styles/ringworld.css"; + } + return "assets/[name][extname]"; + } + } + } + } +}); diff --git a/world-data/README.md b/world-data/README.md new file mode 100644 index 0000000..970f95e --- /dev/null +++ b/world-data/README.md @@ -0,0 +1,15 @@ +# Ringworld World Data + +This folder contains a starter world shell: + +- `ringworld-starter/world.json` + +## Usage + +1. Copy `ringworld-starter` into your Foundry `Data/worlds/` folder. +2. Start Foundry and launch the world. +3. Enable the `ringworld-content` module. +4. In Items directory, click **Import Ringworld Content**. + +The world starts empty by design and is meant to be populated using the system/module tools. + diff --git a/world-data/ringworld-starter/world.json b/world-data/ringworld-starter/world.json new file mode 100644 index 0000000..52f9864 --- /dev/null +++ b/world-data/ringworld-starter/world.json @@ -0,0 +1,14 @@ +{ + "title": "Ringworld Starter", + "id": "ringworld-starter", + "system": "ringworld", + "coreVersion": "13.351", + "compatibility": { + "minimum": "12", + "verified": "13" + }, + "systemVersion": "0.1.0", + "description": "Starter world shell for Ringworld. Use Ringworld Content module import button to populate catalog content.", + "lastPlayed": "", + "flags": {} +}
+
+
+

Ringworld Explorer Sheet

+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
Damage Mod
+
+
+
+
+ General Hit Points + + + / + + +
+
+
+
+
+ Health Roll + + + % + + +
+
+
+
+
+ Reasoning Roll + + + % + + +
+
+
+
+
+ Luck Roll + + + % + + +
+
+
+
+
+ Dodge Roll + + + % + + +
+
+
+
+
Defects
+
+
+
+
Habits
+
+
+ +
+ + +
+
+
+ +
+
+
+

Hit Locations

+
+ +
+ +
+ +
+
+ {{#each bodyLocations as |loc|}} +
+

{{loc.label}}

+
d20 {{loc.rollRange}}
+ +
AP Total: {{loc.totalArmor}}
+ +
+ {{/each}} +
+
+
+ +
+
+
+

Portrait

+
+ {{actor.name}} +
+ + +
+
+
+
+ +
+ +
+
+
+
+
+

Weapons

+ +
+
    + {{#each weapons as |item|}} +
  1. +
    {{item.name}}
    +
    {{item.system.linkedSkill}}
    +
    {{item.system.damageFormula}}
    +
    {{item.system.range.close}} / {{item.system.range.medium}} / {{item.system.range.long}}
    +
    + + + +
    +
  2. + {{/each}} +
+
+ +
+
+
+

Armor

+ +
+
    + {{#each armors as |item|}} +
  1. +
    {{item.name}}
    +
    Base AP {{item.system.value}}
    +
    {{#if item.system.stowed}}Stowed{{else}}{{#if item.system.equipped}}Equipped{{else}}Carried{{/if}}{{/if}}
    +
    + + +
    +
  2. + {{/each}} +
+
+
+
+
+ +
+
+ + + Missing: {{missingBaseSkillsCount}} +
+ +
+
+

Character Builder

+ {{#if sheetToolsUnlocked}} +
+ + + +
+ {{else}} + Tools Locked + {{/if}} +
+ +
+
Education (EDU-10)*20: {{builder.education.total}} | Spent {{builder.education.spent}} | Remaining {{builder.education.remaining}}
+
Pursuits (Age-EDU-6)*20: {{builder.pursuits.total}} | Spent {{builder.pursuits.spent}} | Remaining {{builder.pursuits.remaining}}
+
Special Interests INT*10: {{builder.special.total}} | Spent {{builder.special.spent}} | Remaining {{builder.special.remaining}}
+
Pursuit Years: {{builder.pursuitYears.total}} | Allocated {{builder.pursuitYears.spent}} | Remaining {{builder.pursuitYears.remaining}}
+
+ + {{#if sheetToolsUnlocked}} +
+

Pursuit Selections (controls pursuit-only skill spending)

+
+ + + +
+ +
    + {{#each builder.pursuitsList as |entry idx|}} +
  1. + + + + +
  2. + {{/each}} +
+
+ +

Use Allocate Builder Points on each skill row to spend points by pool. Overspending and invalid pool usage are blocked.

+ {{/if}} +
+ +
+ {{#each skillsByCategory as |group|}} +
+

{{group.category}}

+ +
+ Skill + Base % + Current % + Actions +
+ +
    + {{#each group.rows as |row|}} + {{#if row.itemId}} +
  1. +
    + + {{row.name}} +
    +
    {{row.base}}%
    +
    {{row.value}}% {{#if row.allocationTotal}}+{{row.allocationTotal}}{{/if}}
    +
    + {{#if ../../sheetToolsUnlocked}} + + {{/if}} + + + +
    +
  2. + {{else}} +
  3. +
    + + {{row.name}} +
    +
    {{row.base}}%
    +
    -
    +
    + +
    +
  4. + {{/if}} + {{/each}} +
+
+ {{/each}} +
+ +
+ Additional Skills ({{customSkills.length}}) +
+ Skill + Base % + Current % + Actions +
+
    + {{#each customSkills as |item|}} +
  1. +
    {{item.name}}
    +
    -
    +
    {{item.system.value}}% {{#if item.allocationTotal}}+{{item.allocationTotal}}{{/if}}
    +
    + {{#if ../sheetToolsUnlocked}} + + {{/if}} + + + +
    +
  2. + {{/each}} +
+
+
+ +
+
+
+

Weapons

+ +
+
    + {{#each weapons as |item|}} +
  1. +
    {{item.name}}
    +
    {{item.system.weight}}kg
    +
    {{#if item.system.stowed}}Stowed{{else}}{{#if item.system.equipped}}Equipped{{else}}Carried{{/if}}{{/if}} | {{item.system.linkedSkill}}
    +
    {{item.system.damageFormula}}
    +
    {{item.system.range.close}} / {{item.system.range.medium}} / {{item.system.range.long}}
    +
    + + + + + +
    +
  2. + {{/each}} +
+
+
+ +
+
+
+

Armor

+ +
+
    + {{#each armors as |item|}} +
  1. +
    {{item.name}}
    +
    {{item.system.weight}}kg
    +
    Base AP {{item.system.value}}
    +
    {{#if item.system.stowed}}Stowed{{else}}{{#if item.system.equipped}}Equipped{{else}}Carried{{/if}}{{/if}}
    +
    + + + + +
    +
  2. + {{/each}} +
+
+ +
+
+

Equipment

+ +
+
    + {{#each gear as |item|}} +
  1. +
    {{item.name}}
    +
    Weight {{item.system.weight}}
    +
    {{#if item.system.stowed}}Stowed{{else}}{{#if item.system.equipped}}Equipped{{else}}Carried{{/if}}{{/if}} | {{#if item.system.isPowerPack}}Power Pack {{item.system.energy.value}}/{{item.system.energy.max}}{{else}}Gear{{/if}}
    +
    + + + + +
    +
  2. + {{/each}} +
+
+
+
+