diff --git a/README.md b/README.md
index 0de04f6..c15e658 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
-# Ringworld Foundry System (v12-v13 MVP)
+# Ringworld Foundry System (v13-v14)
-Ringworld is a custom Foundry VTT game system for BRP-like percentile play with hit locations, location armor, and energy-tracked weapons.
+Ringworld is a custom Foundry VTT game system for the classic 1984 Chaosium percentile RPG: BRP-style roll-under checks, seven hit locations with location armor, energy- and ammunition-tracked weapons, and the Ringworld impulse combat clock.
+
+Version 0.5.0 is a full rework: Foundry v13/v14 compatibility, a uniform "Ringworld '84" visual theme (warm space-black, bronze structure, single ring-arc amber accent — inspired by Larry Niven's Ringworld and the original boxed set), combat automation for the system's quirks, and a seeded GM Reference journal.
## Online Install (GitHub)
@@ -16,26 +18,50 @@ Companion content module manifest URL:
Starter world data archive:
- `https://raw.githubusercontent.com/slaguru666/Ringworld/main/dist/ringworld-starter-world.zip`
+## What's in 0.5.0
+
+Visual:
+- Complete "Ringworld '84" retheme driven by CSS custom properties: one palette across actor sheets, item sheets, dialogs, chat cards, the hit-location picker, and the impulse HUD. No neon.
+- Legible dark dialogs, amber active tabs, monospace numerals, uppercase display headers.
+
+Combat automation:
+- Special Success rolls damage dice twice (world setting `Automate Special Success Damage`).
+- Fumbles flag a GM adjudication note on the chat card.
+- Defender Dodge prompt before damage: a successful dodge evades a normal hit; only a special dodge evades a special hit (world setting `Prompt Defender Dodge`).
+- Ammunition is consumed per attack (`Ammo Loaded / Ammo Max` on weapons), with a Reload button and ammo badge on sheet weapon rows. Empty weapons block the attack.
+- Energy weapons auto-link to the best carried power pack when none is linked, instead of failing the attack.
+
+Correctness fixes:
+- Hit point damage now persists reliably: maximums are always derived (CON + MAS), current values are source-authoritative, and a one-time migration preserves what tables saw under the old logic. (Previously damage could silently heal back to full on unlinked tokens and fresh actors.)
+- Damage Mod and derived rolls (Health/Reasoning/Luck/Dodge) always recompute from characteristics and can no longer go stale; they display read-only.
+
+GM experience:
+- A **Ringworld GM Reference** journal is seeded into every world: percentile outcomes, the impulse clock, the attack workflow, wound effects, the 1984 quirks (MAS, skill pools, gravity encumbrance, power packs), and a quick-start checklist.
+- The creature sheet is now a full monster sheet: characteristics with multiplier checks, derived rolls, body-schema hit-location grid with stacked AP totals, weapons with ammo/reload, armor, skills, conditions reset, and portrait picker.
+
## Status
-MVP implemented:
+Implemented:
- Actor types: `explorer`, `creature`, `vehicle` (stub)
-- Item types: `skill`, `weapon`, `armor`, `gear`, `species`
+- Item types: `skill`, `weapon`, `armor`, `gear`, `species`, `pursuit`
- 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
+- 1d20 hit location roll with per-species body schemas (humanoid, dolphin, puppeteer, trinoc, bandersnatchi, kzinti)
+- Weapon workflow: aim states, modifiers, ammo/energy consumption, defender dodge, hit location, armor stacking, damage to location + general HP
+- Auto conditions at location 0 or less (`prone`, `unconscious`, limb unusable flags), synced to token status icons
+- Impulse combat tracker (Action Ranking clock) with floating HUD
+- World catalog seeding: species, 55 base skills, 18 pursuits, weapons, armor, gear
+- Character tools: gravity-based characteristic rolls, random explorer generation, point-buy character builder
+
+Backlog:
+- Vehicle combat and computer subsystems
+- Compendium packs (content currently ships via the idempotent world seeder)
+- Localization (UI strings are hardcoded English)
## Install / Build
From this project folder:
```bash
-cd /Users/timevans/FoundryVTT/foundryvtt-ringworld
npm install
npm run build
```
@@ -44,58 +70,36 @@ 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:
+Install into a local Foundry data directory (macOS example):
```bash
-ln -s /Users/timevans/FoundryVTT/foundryvtt-ringworld /Users/timevans/FoundryVTT/data/systems/ringworld
+rsync -a --delete \
+ --exclude node_modules --exclude .git --exclude src --exclude dist \
+ --exclude world-data --exclude seeds --exclude ringworld-content \
+ ./ "$HOME/Library/Application Support/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
+1. In Foundry, create a world using the **Ringworld** system. The catalog and GM Reference journal seed automatically on first GM load.
+2. Read the **Ringworld GM Reference** journal for the rules the system automates.
+3. Create an **Explorer** actor (or use **Create Random Explorer** in the Actors directory).
+4. Target a token, then click **Use** on a weapon and follow the prompts.
+5. For projectile weapons, set `Ammo Max` on the weapon and use the Reload button on the sheet.
+6. Check **System Settings** for the automation toggles.
-## 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
+## Rules implemented
- 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`
+- Special Success: roll `<= floor(target / 5)` — damage dice doubled when automated
+- Fumble: target `< 50`: roll `96-100`; target `>= 50`: roll `100`
+- Ranged aim states: unaimed quarter skill, partial aim half, full aim full skill
+- Dodge: DEX x5; success evades a normal hit, special dodge required against a special hit
+- Hit locations (humanoid 1d20): `01-03 R Leg | 04-06 L Leg | 07-10 Abdomen | 11-15 Chest | 16-17 R Arm | 18-19 L Arm | 20 Head`
- Locational damage also reduces general HP (same final damage)
-- Armor subtracts 1:1 damage and stacks across overlapping covered armor pieces
+- Armor subtracts 1:1 and stacks across overlapping covered pieces
+- General HP = CON + MAS; damage modifier from STR + MAS
+- Encumbrance vs STR with gravity multipliers (light x0.5, heavy x2)
## Seeds
@@ -104,10 +108,8 @@ If `ringworld` already exists, remove or rename it first.
- `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.
+- `npm run typecheck` reports pre-existing errors from the v12 types package against v13/v14 APIs; the vite build is the authoritative check.
diff --git a/dist/ringworld-content.zip b/dist/ringworld-content.zip
index f43b2bd..e0063d8 100644
Binary files a/dist/ringworld-content.zip and b/dist/ringworld-content.zip differ
diff --git a/dist/ringworld-starter-world.zip b/dist/ringworld-starter-world.zip
index 47dcd8b..3e16206 100644
Binary files a/dist/ringworld-starter-world.zip and b/dist/ringworld-starter-world.zip differ
diff --git a/dist/ringworld-system.zip b/dist/ringworld-system.zip
index 2f9d51d..5ddce11 100644
Binary files a/dist/ringworld-system.zip and b/dist/ringworld-system.zip differ
diff --git a/module/ringworld.mjs b/module/ringworld.mjs
index d2e16a4..941f1e7 100644
--- a/module/ringworld.mjs
+++ b/module/ringworld.mjs
@@ -1,4 +1,4 @@
-async function Tt(e) {
+async function Bt(e) {
const t = game == null ? void 0 : game.dice3d;
if (t != null && t.showForRoll)
try {
@@ -6,27 +6,27 @@ async function Tt(e) {
} catch {
}
}
-async function Me(e) {
+async function ye(e) {
const t = await new Roll(e).evaluate();
- return await Tt(t), t;
+ return await Bt(t), t;
}
-async function Ue(e) {
- const t = await Me("1d100");
- return Dt(t.total ?? 0, e);
+async function Ce(e) {
+ const t = await ye("1d100");
+ return Ut(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"), {
+function Ut(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, r = !s && e >= o;
+ let l = "failure";
+ return r ? l = "fumble" : i ? l = "special" : s && (l = "success"), {
roll: e,
target: a,
- outcome: r,
+ outcome: l,
isSuccess: s,
isSpecial: i,
- isFumble: l
+ isFumble: r
};
}
-function Ae(e) {
+function he(e) {
switch (e) {
case "special":
return "Special Success";
@@ -38,7 +38,7 @@ function Ae(e) {
return "Failure";
}
}
-const x = "ringworld", Ce = [
+const P = "ringworld", Re = [
{ 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 },
@@ -46,29 +46,29 @@ const x = "ringworld", Ce = [
{ 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);
+], qt = ["rArm", "lArm", "rLeg", "lLeg"], Gt = ["rLeg", "lLeg"];
+function Vt(e) {
+ return qt.includes(e);
}
-function it(e) {
+function ut(e) {
return String(e ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
}
-function oe(e, t) {
+function ne(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 = {
+const mt = {
id: "humanoid",
label: "Humanoid",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
- entries: oe(Ce)
-}, Et = {
+ entries: ne(Re)
+}, Kt = {
id: "dolphin",
label: "Dolphin",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
- entries: oe([
+ entries: ne([
{ 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 },
@@ -85,11 +85,11 @@ const rt = {
rLeg: 0.25,
lLeg: 0.25
})
-}, Bt = {
+}, _t = {
id: "puppeteer",
label: "Puppeteer",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
- entries: oe([
+ entries: ne([
{ 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 },
@@ -106,11 +106,11 @@ const rt = {
rLeg: 0.28,
lLeg: 0.28
})
-}, Ft = {
+}, jt = {
id: "trinoc",
label: "Trinoc",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
- entries: oe([
+ entries: ne([
{ 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 },
@@ -127,11 +127,11 @@ const rt = {
rLeg: 0.27,
lLeg: 0.27
})
-}, Ut = {
+}, Wt = {
id: "bandersnatchi",
label: "Bandersnatchi",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
- entries: oe([
+ entries: ne([
{ 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 },
@@ -148,11 +148,11 @@ const rt = {
rLeg: 0.24,
lLeg: 0.24
})
-}, qt = {
+}, Yt = {
id: "kzinti",
label: "Kzinti",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
- entries: oe([
+ entries: ne([
{ 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 },
@@ -161,50 +161,50 @@ const rt = {
{ 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
+}, dt = {
+ humanoid: mt,
+ dolphin: Kt,
+ puppeteer: _t,
+ trinoc: jt,
+ bandersnatchi: Wt,
+ kzinti: Yt
};
-function Kt() {
- return Object.values(lt);
+function gt() {
+ return Object.values(dt);
}
-function Vt(e) {
- const t = it(e);
+function zt(e) {
+ const t = ut(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 Ee(e) {
+ const t = ut(e);
+ return dt[t] ?? mt;
}
-function se(e) {
+function oe(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);
+ return Ee(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));
+ return Ee(zt(a));
}
-function Gt(e) {
- return e ? se(e).entries : Ce;
+function Qt(e) {
+ return e ? oe(e).entries : Re;
}
-function jt(e, t) {
+function Xt(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);
+async function pt(e) {
+ const a = (await ye("1d20")).total ?? 20, n = Qt(e);
return {
roll: a,
- location: jt(a, n)
+ location: Xt(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) => {
+async function Zt(e) {
+ const t = e ? oe(e) : Ee("humanoid"), a = t.entries, n = new Map(a.map((l) => [l.slug, l])), o = (l, c) => {
var u;
- return ((u = n.get(r)) == null ? void 0 : u.label) ?? c;
+ return ((u = n.get(l)) == null ? void 0 : u.label) ?? c;
}, s = `
Hit Location
Roll: ${t.roll}
@@ -411,11 +411,11 @@ async function Xt(e) {
content: n
});
}
-async function Ee(e, t, a) {
- const n = await Ue(a), o = e.items.find(
+async function Be(e, t, a) {
+ const n = await Ce(a), o = e.items.find(
(s) => s.type === "skill" && s.name.toLowerCase() === String(t).toLowerCase()
);
- await mt({
+ await ht({
actor: e,
actorName: e.name,
actorImg: e.img,
@@ -423,41 +423,41 @@ async function Ee(e, t, a) {
skillImg: o == null ? void 0 : o.img,
roll: n.roll,
target: n.target,
- outcomeLabel: Ae(n.outcome),
+ outcomeLabel: he(n.outcome),
isSuccess: n.isSuccess,
isSpecial: n.isSpecial,
isFumble: n.isFumble
});
}
-function Zt(e, t) {
+function oa(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 };
+ return { name: n.name, target: A(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: s.name, target: A(s.system.value), img: s.img };
}
return {
name: o || "Unlinked Skill",
target: 0
};
}
-function Jt(e) {
+function yt(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 = `
+async function sa(e, t, a) {
+ var m;
+ const o = String(((m = t.system) == null ? void 0 : m.attackType) || "ranged") === "ranged", s = "full", i = foundry.utils.escapeHTML(String((e == null ? void 0 : e.name) || "Attacker")), r = foundry.utils.escapeHTML(String((t == null ? void 0 : t.name) || "Weapon")), l = 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 = `
`;
return new Promise((g) => {
- let m = !1;
- const f = (y) => {
- m || (m = !0, g(y));
+ let f = !1;
+ const d = (y) => {
+ f || (f = !0, g(y));
};
new Dialog({
title: `${t.name} - Attack Modifiers`,
@@ -513,20 +513,164 @@ async function ea(e, t, a) {
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}`);
+ const b = y[0], h = (J) => b == null ? void 0 : b.querySelector(`[name="${J}"]`), p = String(((le = h("aimMode")) == null ? void 0 : le.value) || s), w = p === "unaimed" || p === "partial" || p === "full" ? p : s, v = o ? { unaimed: 0.25, partial: 0.5, full: 1 }[w] : 1, x = A((ce = h("coverMod")) == null ? void 0 : ce.value, 0), I = A((ue = h("movementMod")) == null ? void 0 : ue.value, 0), T = A((me = h("rangeMod")) == null ? void 0 : me.value, 0), R = A((de = h("situationalMod")) == null ? void 0 : de.value, 0), D = A((ge = h("customMod")) == null ? void 0 : ge.value, 0), N = String(((pe = h("customLabel")) == null ? void 0 : pe.value) || "").trim(), C = x + I + T + R + D, U = a + C, re = Math.floor(U * v), Z = yt(re), H = [`Base ${a}`];
+ if (x && H.push(`Cover ${x >= 0 ? "+" : ""}${x}`), I && H.push(`Movement ${I >= 0 ? "+" : ""}${I}`), T && H.push(`Range ${T >= 0 ? "+" : ""}${T}`), R && H.push(`Situational ${R >= 0 ? "+" : ""}${R}`), D && H.push(`${N || "Custom"} ${D >= 0 ? "+" : ""}${D}`), H.push(`Pre-Aim ${U}`), o) {
+ const J = w === "unaimed" ? "Quarter" : w === "partial" ? "Half" : "Full";
+ H.push(`Aim ${J} x${v}`);
}
- N.push(`Final ${J}`), f({
- finalTarget: J,
- summary: N.join(" | "),
- aimMode: h,
- aimMultiplier: A,
- flatModifierTotal: L
+ H.push(`Final ${Z}`), d({
+ finalTarget: Z,
+ summary: H.join(" | "),
+ aimMode: w,
+ aimMultiplier: v,
+ flatModifierTotal: C
});
}
},
+ cancel: {
+ label: "Cancel",
+ callback: () => d(null)
+ }
+ },
+ default: "roll",
+ close: () => d(null)
+ }, {
+ classes: ["ringworld-attack-dialog"],
+ width: 510,
+ resizable: !1
+ }).render(!0);
+ });
+}
+async function ia(e, t) {
+ var i, r, l;
+ const a = Math.max(0, A(t.system.ammoUsedPerAttack));
+ if (a <= 0)
+ return { used: !1, remaining: 0, cost: 0, insufficient: !1 };
+ const n = A((i = t.system.ammo) == null ? void 0 : i.value);
+ if (A((r = t.system.ammo) == null ? void 0 : r.max) <= 0 && n <= 0)
+ return { used: !1, remaining: 0, cost: a, insufficient: !1 };
+ if (n < a)
+ return (l = ui.notifications) == null || l.warn(`${t.name} is out of ammunition. Reload before attacking.`), { used: !0, remaining: n, cost: a, insufficient: !0 };
+ const s = n - a;
+ return await e.updateEmbeddedDocuments("Item", [{ _id: t.id, "system.ammo.value": s }]), { used: !0, remaining: s, cost: a, insufficient: !1 };
+}
+async function ra(e, t) {
+ var n, o, s;
+ const a = A((n = t.system.ammo) == null ? void 0 : n.max);
+ if (a <= 0) {
+ (o = ui.notifications) == null || o.info(`${t.name} has no ammunition capacity configured (set Ammo Max on the weapon).`);
+ return;
+ }
+ await e.updateEmbeddedDocuments("Item", [{ _id: t.id, "system.ammo.value": a }]), (s = ui.notifications) == null || s.info(`${t.name} reloaded (${a}/${a}).`);
+}
+async function la(e, t) {
+ var i, r, l, c, u, m, g;
+ const a = Math.max(0, A(t.system.energyUsedPerAttack));
+ if (a <= 0)
+ return { sourceLabel: "None", remaining: 0, cost: 0, insufficient: !1 };
+ if (String(t.system.powerSource || "actor") === "powerPack") {
+ const f = String(t.system.powerPackId || "");
+ let d = f ? e.items.get(f) : null;
+ if ((!d || d.type !== "gear" || !d.system.isPowerPack) && (d = e.items.find(
+ (h) => {
+ var p;
+ return h.type === "gear" && h.system.isPowerPack && !h.system.stowed && A((p = h.system.energy) == null ? void 0 : p.value) >= a;
+ }
+ ) ?? null, d && (await e.updateEmbeddedDocuments("Item", [{ _id: t.id, "system.powerPackId": d.id }]), (i = ui.notifications) == null || i.info(`${t.name} linked to ${d.name}.`))), !d || d.type !== "gear" || !d.system.isPowerPack)
+ return (r = ui.notifications) == null || r.warn("Weapon is configured for a power pack but no valid power pack is carried."), { sourceLabel: "Power Pack", remaining: 0, cost: a, insufficient: !0 };
+ const y = A((l = d.system.energy) == null ? void 0 : l.value);
+ if (y < a)
+ return (c = ui.notifications) == null || c.warn(`${d.name} does not have enough energy.`), { sourceLabel: d.name, remaining: y, cost: a, insufficient: !0 };
+ const b = y - a;
+ return await e.updateEmbeddedDocuments("Item", [{ _id: d.id, "system.energy.value": b }]), { sourceLabel: d.name, remaining: b, cost: a, insufficient: !1 };
+ }
+ const o = A((m = (u = e.system.resources) == null ? void 0 : u.energy) == null ? void 0 : m.value);
+ if (o < a)
+ return (g = ui.notifications) == null || g.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 ca(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 = A((o = e.system.perLocation) == null ? void 0 : o[t]);
+ return a > 0 ? a : A(e.system.value);
+}
+function ua(e, t) {
+ var o, s;
+ const a = A((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, r) => i + ca(r, t), 0);
+ return a + n;
+}
+function ma(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), Gt.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 da(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 ga(e, t, a) {
+ var d, y, b, h;
+ const n = (d = e.system.hitLocations) == null ? void 0 : d[t], o = A((y = n == null ? void 0 : n.hp) == null ? void 0 : y.value), s = Math.max(1, A((b = n == null ? void 0 : n.hp) == null ? void 0 : b.max, 1)), i = ua(e, t), r = Math.max(0, a - i), l = o - r, c = Vt(t) ? Math.max(l, -s) : l, m = A((h = e.system.generalHp) == null ? void 0 : h.value) - r, g = ma(e, t, c, s, m), f = {
+ [`system.hitLocations.${t}.hp.value`]: c,
+ "system.generalHp.value": m,
+ "system.conditions": g
+ };
+ return await e.update(f), await da(e, g), {
+ armor: i,
+ finalDamage: r,
+ locationValue: c,
+ generalHpValue: m
+ };
+}
+async function wt(e, 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, A((u = (c = e.system) == null ? void 0 : c.characteristics) == null ? void 0 : u[t], 0)), s = `
+
+ `, i = await new Promise((m) => {
+ let g = !1;
+ const f = (d) => {
+ g || (g = !0, m(d));
+ };
+ new Dialog({
+ title: `${a} Check`,
+ content: s,
+ buttons: {
+ roll: {
+ label: "Roll",
+ callback: (d) => {
+ var h, p;
+ const y = Number(((p = (h = d[0]) == null ? void 0 : h.querySelector("select[name='multiplier']")) == null ? void 0 : p.value) || 1), b = Math.max(1, Math.min(7, Math.floor(y || 1)));
+ f(b);
+ }
+ },
cancel: {
label: "Cancel",
callback: () => f(null)
@@ -535,80 +679,80 @@ async function ea(e, t, a) {
default: "roll",
close: () => f(null)
}, {
- classes: ["ringworld-attack-dialog"],
- width: 510,
- resizable: !1
+ classes: ["ringworld-attack-dialog"]
}).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);
+ const r = yt(o * i), l = await Ce(r);
+ await ht({
+ actor: e,
+ actorName: e.name,
+ actorImg: e.img,
+ skillName: `${a} x${i}`,
+ roll: l.roll,
+ target: l.target,
+ outcomeLabel: he(l.outcome),
+ isSuccess: l.isSuccess,
+ isSpecial: l.isSpecial,
+ isFumble: l.isFumble
+ });
+}
+function Je(e, t) {
+ try {
+ return !!game.settings.get(P, e);
+ } catch {
+ return t;
+ }
+}
+async function pa(e, t) {
+ var r, l;
+ const a = A((l = (r = e.system) == null ? void 0 : r.derivedRolls) == null ? void 0 : l.dodge, 0), n = foundry.utils.escapeHTML(String(e.name || "Target"));
+ if (!await new Promise((c) => {
+ let u = !1;
+ const m = (g) => {
+ u || (u = !0, c(g));
+ };
+ new Dialog({
+ title: `${e.name} - Dodge Attempt`,
+ content: `
+
+ `,
+ buttons: {
+ dodge: { label: `Dodge (${a}%)`, callback: () => m(!0) },
+ none: { label: "No Dodge", callback: () => m(!1) }
+ },
+ default: "dodge",
+ close: () => m(!1)
+ }, {
+ classes: ["ringworld-attack-dialog"],
+ width: 420
+ }).render(!0);
+ })) return null;
+ const s = await Ce(a), i = s.isSuccess && (!t || s.isSpecial);
+ return {
+ attempted: !0,
+ roll: s.roll,
+ target: s.target,
+ success: s.isSuccess,
+ special: s.isSpecial,
+ negated: i
+ };
+}
+async function bt(e, t) {
+ var p, w;
+ const a = aa(), n = !!a, o = (a == null ? void 0 : a.name) ?? "No Target", s = oa(e, t), i = await sa(e, t, s.target);
+ if (!i) return;
+ const r = await ia(e, t);
+ if (r.insufficient) return;
+ const l = await la(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({
+ const c = await Ce(i.finalTarget);
+ if (!c.isSuccess) {
+ const v = a ? A((p = a.system.generalHp) == null ? void 0 : p.value, 0) : 0;
+ await Ne({
actor: e,
actorName: e.name,
actorImg: e.img,
@@ -618,35 +762,81 @@ async function dt(e, t) {
weaponImg: t.img,
skillName: s.name,
skillImg: s.img,
- attackRoll: r.roll,
- attackTarget: r.target,
- attackOutcomeLabel: Ae(r.outcome),
+ attackRoll: c.roll,
+ attackTarget: c.target,
+ attackOutcomeLabel: he(c.outcome),
isSuccess: !1,
- isSpecial: r.isSpecial,
- isFumble: r.isFumble,
+ isSpecial: c.isSpecial,
+ isFumble: c.isFumble,
hitLocationLabel: "-",
hitLocationRoll: null,
rawDamage: 0,
armor: 0,
finalDamage: 0,
remainingLocationHp: 0,
- remainingGeneralHp: y,
+ remainingGeneralHp: v,
energySourceLabel: l.sourceLabel,
energyCost: l.cost,
energyRemaining: l.remaining,
- notes: `Attack failed before damage was rolled. ${i.summary}`
+ ammoUsed: r.used,
+ ammoCost: r.cost,
+ ammoRemaining: r.remaining,
+ dodge: null,
+ notes: c.isFumble ? `Fumble! The GM should adjudicate a mishap (dropped or damaged weapon, hit an ally, drained power). ${i.summary}` : `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) : {
+ let u = null;
+ if (a && Je("promptDefenderDodge", !0) && (u = await pa(a, c.isSpecial)), u != null && u.negated) {
+ await Ne({
+ 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: c.roll,
+ attackTarget: c.target,
+ attackOutcomeLabel: he(c.outcome),
+ isSuccess: !1,
+ isSpecial: c.isSpecial,
+ isFumble: !1,
+ hitLocationLabel: "-",
+ hitLocationRoll: null,
+ rawDamage: 0,
+ armor: 0,
+ finalDamage: 0,
+ remainingLocationHp: 0,
+ remainingGeneralHp: A((w = a.system.generalHp) == null ? void 0 : w.value, 0),
+ energySourceLabel: l.sourceLabel,
+ energyCost: l.cost,
+ energyRemaining: l.remaining,
+ ammoUsed: r.used,
+ ammoCost: r.cost,
+ ammoRemaining: r.remaining,
+ dodge: u,
+ notes: `${o} dodged the attack. ${i.summary}`
+ });
+ return;
+ }
+ const m = await Zt(a ?? e);
+ if (!m) return;
+ const g = String(t.system.damageFormula || "0").trim() || "0", f = Je("automateSpecialDamage", !0), d = c.isSpecial && f, y = await ye(g);
+ let b = Math.max(0, A(y.total));
+ if (d) {
+ const v = await ye(g);
+ b += Math.max(0, A(v.total));
+ }
+ const h = a ? await ga(a, m.slug, b) : {
armor: 0,
- finalDamage: g,
+ finalDamage: b,
locationValue: 0,
generalHpValue: 0
};
- await Xe({
+ await Ne({
actor: e,
actorName: e.name,
actorImg: e.img,
@@ -656,47 +846,164 @@ async function dt(e, t) {
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,
+ attackRoll: c.roll,
+ attackTarget: c.target,
+ attackOutcomeLabel: he(c.outcome),
+ isSuccess: c.isSuccess,
+ isSpecial: c.isSpecial,
+ isFumble: c.isFumble,
+ hitLocationLabel: m.label,
+ hitLocationRoll: m.rolled,
+ rawDamage: b,
+ armor: h.armor,
+ finalDamage: h.finalDamage,
+ remainingLocationHp: h.locationValue,
+ remainingGeneralHp: h.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}`
+ ammoUsed: r.used,
+ ammoCost: r.cost,
+ ammoRemaining: r.remaining,
+ dodge: u,
+ notes: [
+ d ? "Special success: damage dice rolled twice." : "",
+ a ? "Location and general HP were both reduced by final damage, per Ringworld rules." : "No target selected: roll resolved with no damage application.",
+ i.summary
+ ].filter(Boolean).join(" ")
});
}
-function ra(e) {
- var m, f, y, b, w, p, h, A;
+function fa(e) {
+ var r, l, c, u, m, g, f, d, y;
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 },
+ const t = A((l = (r = e.system) == null ? void 0 : r.characteristics) == null ? void 0 : l.con, 10), a = A((u = (c = e.system) == null ? void 0 : c.characteristics) == null ? void 0 : u.mas, 10), n = Math.max(1, Math.floor(t + a)), o = (e._source ?? ((m = e.toObject) == null ? void 0 : m.call(e, !1)) ?? {}).system ?? {}, s = (g = o.generalHp) == null ? void 0 : g.value;
+ e.system.generalHp = e.system.generalHp ?? {}, e.system.generalHp.max = n, e.system.generalHp.value = s == null ? n : Math.min(A(s, n), n), e.system.hitLocations = e.system.hitLocations ?? {}, e.system.hitLocationSchema = e.system.hitLocationSchema ?? "auto";
+ const i = oe(e);
+ for (const b of i.entries) {
+ const h = b.slug, p = e.system.hitLocations[h] ?? {
+ label: h,
+ hp: {},
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;
+ p.label = b.label;
+ const w = Math.max(0.05, A(b.hpFactor, 0.3)), v = Math.max(1, Math.ceil(n * w)), x = (y = (d = (f = o.hitLocations) == null ? void 0 : f[h]) == null ? void 0 : d.hp) == null ? void 0 : y.value;
+ p.hp = p.hp ?? {}, p.hp.max = v, p.hp.value = x == null ? v : Math.min(A(x, v), v), e.system.hitLocations[h] = p;
}
}
-const De = 9, _ = {
+const et = "Ringworld GM Reference";
+function ha() {
+ return [
+ {
+ name: "Percentile Outcomes",
+ content: `
+
Every check is a d100 roll against a target percentage. The system classifies results automatically:
+
+ - Success — roll ≤ target.
+ - Special Success — roll ≤ one fifth of the target (minimum 1). Attacks roll their damage dice twice when Automate Special Success Damage is enabled.
+ - Failure — roll > target.
+ - Fumble — roll of 96–100 when the target is below 50, or a natural 100 otherwise. The GM adjudicates a mishap: dropped or damaged weapon, hit an ally, drained power pack.
+
+
Characteristic checks use Characteristic × Multiplier (usually ×5): click any characteristic button on a sheet to pick the multiplier.
+ `
+ },
+ {
+ name: "Combat Sequence (Impulses)",
+ content: `
+
Ringworld does not use simple initiative. Combat rounds are split into impulses and each combatant acts on a rhythm set by their Action Ranking (AR), which is derived from DEX (lower is faster) and worsened by encumbrance.
+
+ - Add combatants to the Combat Tracker as normal. The Ringworld impulse panel replaces the core tracker list.
+ - Each combatant schedules an action: a major action (attack, full move, complex task) completes after AR impulses; a minor action (drop prone, shout, draw) completes after 1 impulse.
+ - Advance the clock with Next Completion or +1. When a combatant's action completes, resolve it, then let them schedule the next one.
+
+
The floating impulse HUD can be dragged anywhere on screen and mirrors the tracker for quick play.
+ `
+ },
+ {
+ name: "Attack Workflow",
+ content: `
+
Target a token, then press the weapon's Use button on the attacker's sheet. The system walks through the 1984 rules in order:
+
+ - Aim state (ranged only): unaimed fires at quarter skill, partially aimed at half, fully aimed at full skill. Cover, movement, range and situational modifiers are applied before the aim multiplier.
+ - Ammunition and energy are spent. Projectile weapons with an ammo capacity are blocked when empty — use the Reload button on the weapon row. Energy weapons draw from the actor pool or a linked power pack.
+ - Dodge: on a hit, the defender is offered a Dodge roll (DEX ×5). A successful dodge evades a normal hit; only a special dodge evades a special hit. Disable with the Prompt Defender Dodge setting.
+ - Hit location is rolled on a d20 (or picked manually). Species use their own body schema — puppeteers, dolphins, trinocs, bandersnatchi and kzinti map differently.
+ - Damage is rolled, stacked armor at that location is subtracted, and the result reduces both the location HP and the general HP.
+
+ `
+ },
+ {
+ name: "Wounds & Conditions",
+ content: `
+
When a hit location reaches 0 HP or less, conditions are applied automatically:
+
+ - Head or Chest at 0 — unconscious and prone.
+ - Abdomen at 0 — prone, both legs unusable.
+ - Arm at 0 — that arm is unusable.
+ - Leg at 0 — that leg is unusable, and the character falls prone.
+ - General HP at 0 — dying.
+ - Head, chest or abdomen at negative full value — dying; a head at negative full value is death outright.
+
+
Limbs cannot go below their negative maximum. Use Reset Conditions on a sheet after treatment or recovery. Prone, unconscious and dead sync to token status icons.
+ `
+ },
+ {
+ name: "Running the 1984 Rules",
+ content: `
+
Quirks of the original boxed set that the system encodes for you:
+
+ - General HP = CON + MAS. MAS (mass) replaces the familiar SIZ. Location HP scale from general HP by body schema.
+ - Damage modifier comes from STR + MAS and is recomputed automatically when characteristics change.
+ - Skill pools: Education points = (EDU − 10) × 20, Pursuit points = (Age − EDU − 6) × 20, Special points = INT × 10. The character builder on the Skills tab tracks all three.
+ - Skill category caps: each category is capped by a characteristic pair (e.g. Agility by STR + DEX). The builder enforces the caps.
+ - Gravity matters: carried weight is halved in light gravity and doubled in heavy gravity before comparing to the STR limit; over-limit explorers slow to Move 2 and suffer an Action Ranking penalty.
+ - Energy economy: most Ringworld tech runs on power packs (R20/R50/R100). Weapons can draw from a linked pack — keep spares, and remember fumbles can drain them.
+
+ `
+ },
+ {
+ name: "GM Quick Start",
+ content: `
+
To get a table playing quickly:
+
+ - Seed the catalog (Items sidebar button) — species, 55 base skills, pursuits, weapons, armor and gear from the boxed set appear in organised folders. This runs automatically for new worlds.
+ - Create Random Explorer (Actors sidebar button) — rolls characteristics by species and gravity, adds base skills and a starter kit.
+ - For bespoke characters, unlock the sheet tools (padlock, top right of the sheet) and use the Character Builder on the Skills tab: pick pursuits, then allocate the three point pools.
+ - Creatures get the same automation — hit locations, dodge, armor stacking — via the creature sheet, with a body schema selector for non-humanoids.
+ - Check System Settings for the automation toggles (special success damage, defender dodge prompts).
+
+ `
+ }
+ ];
+}
+async function ya() {
+ var n, o;
+ if (!((n = game.user) != null && n.isGM)) return !1;
+ const e = game.journal;
+ if ((e == null ? void 0 : e.find((s) => {
+ var i;
+ return ((i = s.getFlag) == null ? void 0 : i.call(s, P, "gmReference")) === !0;
+ })) ?? ((o = e == null ? void 0 : e.getName) == null ? void 0 : o.call(e, et))) return !1;
+ const a = ha().map((s, i) => ({
+ name: s.name,
+ type: "text",
+ sort: (i + 1) * 1e5,
+ text: {
+ format: 1,
+ content: s.content.trim()
+ }
+ }));
+ return await getDocumentClass("JournalEntry").create({
+ name: et,
+ pages: a,
+ flags: {
+ [P]: {
+ gmReference: !0
+ }
+ },
+ ownership: { default: 0 }
+ }), !0;
+}
+const He = 10, j = {
speciesDefault: "systems/ringworld/assets/icons/species.svg",
skillDefault: "systems/ringworld/assets/icons/skill.svg",
pursuitDefault: "systems/ringworld/assets/icons/pursuit.svg",
@@ -706,25 +1013,25 @@ const De = 9, _ = {
gearDefault: "systems/ringworld/assets/icons/gear.svg",
powerPackDefault: "systems/ringworld/assets/icons/power-pack.svg"
};
-function be(e) {
+function ve(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 wa(e) {
+ return `systems/ringworld/assets/icons/species/${ve(e)}.svg`;
}
-function ca(e) {
- return `systems/ringworld/assets/icons/weapon/${be(e)}.svg`;
+function ba(e) {
+ return `systems/ringworld/assets/icons/weapon/${ve(e)}.svg`;
}
-function ua(e) {
- return `systems/ringworld/assets/icons/armor/${be(e)}.svg`;
+function ka(e) {
+ return `systems/ringworld/assets/icons/armor/${ve(e)}.svg`;
}
-function ma(e) {
- return `systems/ringworld/assets/icons/gear/${be(e)}.svg`;
+function va(e) {
+ return `systems/ringworld/assets/icons/gear/${ve(e)}.svg`;
}
-function gt(e) {
- return `systems/ringworld/assets/icons/pursuit/${be(e)}.svg`;
+function kt(e) {
+ return `systems/ringworld/assets/icons/pursuit/${ve(e)}.svg`;
}
-const Ze = {
+const tt = {
rLeg: !0,
lLeg: !0,
abdomen: !0,
@@ -732,7 +1039,7 @@ const Ze = {
rArm: !0,
lArm: !0,
head: !0
-}, Se = {
+}, xe = {
rLeg: !1,
lLeg: !1,
abdomen: !0,
@@ -740,7 +1047,7 @@ const Ze = {
rArm: !1,
lArm: !1,
head: !1
-}, da = {
+}, Sa = {
rLeg: !1,
lLeg: !1,
abdomen: !1,
@@ -748,7 +1055,7 @@ const Ze = {
rArm: !1,
lArm: !1,
head: !0
-}, ga = {
+}, Aa = {
rLeg: !1,
lLeg: !1,
abdomen: !1,
@@ -756,7 +1063,7 @@ const Ze = {
rArm: !0,
lArm: !0,
head: !1
-}, pa = {
+}, xa = {
rLeg: !0,
lLeg: !0,
abdomen: !1,
@@ -764,7 +1071,7 @@ const Ze = {
rArm: !1,
lArm: !1,
head: !1
-}, pt = [
+}, vt = [
{
name: "Human",
playable: !0,
@@ -861,7 +1168,7 @@ const Ze = {
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 = [
+], Pa = [
{
name: "Variable Sword",
equipped: !0,
@@ -992,17 +1299,17 @@ const Ze = {
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 = [
+], La = [
+ { name: "Cloth Vest", equipped: !0, weight: 2, value: 1, coverage: xe, notes: "Basic torso protection." },
+ { name: "Soft Combat Vest", equipped: !0, weight: 4, value: 2, coverage: xe, notes: "Flexible tactical vest." },
+ { name: "Combat Harness", equipped: !0, weight: 5, value: 2, coverage: { ...xe, rArm: !0, lArm: !0 }, notes: "Torso + upper arms." },
+ { name: "Armored Coat", equipped: !0, weight: 7, value: 3, coverage: { ...xe, rArm: !0, lArm: !0 }, notes: "Heavy coat with arm plating." },
+ { name: "Leg Guards", equipped: !0, weight: 4, value: 2, coverage: xa, notes: "Lower-body armor." },
+ { name: "Arm Bracers", equipped: !0, weight: 2, value: 2, coverage: Aa, notes: "Forearm and upper arm guards." },
+ { name: "Combat Helmet", equipped: !0, weight: 1.5, value: 2, coverage: Sa, notes: "Cranial protection." },
+ { name: "Full Combat Suit", equipped: !0, weight: 12, value: 3, coverage: tt, notes: "Full-body armored suit." },
+ { name: "Impact Suit", equipped: !0, weight: 16, value: 4, coverage: tt, notes: "High-end impact and hazard suit." }
+], Ia = [
{
name: "Power Pack R20",
weight: 1,
@@ -1048,7 +1355,7 @@ const Ze = {
{ 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 = [
+], be = [
{
name: "Artist",
rollRange: "01-07",
@@ -1212,30 +1519,30 @@ const Ze = {
notes: "Ideological dedication pursuit."
}
];
-function ft() {
- return pt.filter((e) => e.playable);
+function St() {
+ return vt.filter((e) => e.playable);
}
-function Q(e, t) {
- return `${e}:${R(t)}`;
+function z(e, t) {
+ return `${e}:${M(t)}`;
}
-function wa(e) {
- var s, i, l, r, c;
+function Ma(e) {
+ var s, i, r, l, 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;
+ const a = (r = (i = game == null ? void 0 : game.system) == null ? void 0 : i.documentTypes) == null ? void 0 : r.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;
+ const n = (l = CONFIG == null ? void 0 : CONFIG.Item) == null ? void 0 : l.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) {
+function Ca(e) {
return {
- key: Q("skill", e.name),
+ key: z("skill", e.name),
folderKey: "skills",
- img: ye(e.name) || _.skillDefault,
+ img: we(e.name) || j.skillDefault,
data: {
name: e.name,
type: "skill",
@@ -1247,10 +1554,10 @@ function ba(e) {
}
};
}
-function ka(e) {
- const t = la(e.name) || _.speciesDefault;
+function Ra(e) {
+ const t = wa(e.name) || j.speciesDefault;
return {
- key: Q("species", e.name),
+ key: z("species", e.name),
folderKey: "species",
img: t,
data: {
@@ -1266,10 +1573,10 @@ function ka(e) {
}
};
}
-function va(e) {
- const t = e.attackType === "melee" ? "weaponMelee" : "weaponRanged", a = ca(e.name) || (e.attackType === "melee" ? _.weaponMelee : _.weaponRanged);
+function $a(e) {
+ const t = e.attackType === "melee" ? "weaponMelee" : "weaponRanged", a = ba(e.name) || (e.attackType === "melee" ? j.weaponMelee : j.weaponRanged);
return {
- key: Q("weapon", e.name),
+ key: z("weapon", e.name),
folderKey: t,
img: a,
data: {
@@ -1293,10 +1600,10 @@ function va(e) {
}
};
}
-function Sa(e) {
- const t = ua(e.name) || _.armor;
+function Ta(e) {
+ const t = ka(e.name) || j.armor;
return {
- key: Q("armor", e.name),
+ key: z("armor", e.name),
folderKey: "armor",
img: t,
data: {
@@ -1322,10 +1629,10 @@ function Sa(e) {
}
};
}
-function Aa(e) {
- const t = ma(e.name) || (e.isPowerPack ? _.powerPackDefault : _.gearDefault);
+function Da(e) {
+ const t = va(e.name) || (e.isPowerPack ? j.powerPackDefault : j.gearDefault);
return {
- key: Q("gear", e.name),
+ key: z("gear", e.name),
folderKey: "gear",
img: t,
data: {
@@ -1345,10 +1652,10 @@ function Aa(e) {
}
};
}
-function xa(e) {
- const t = gt(e.name) || _.pursuitDefault;
+function Na(e) {
+ const t = kt(e.name) || j.pursuitDefault;
return {
- key: Q("pursuit", e.name),
+ key: z("pursuit", e.name),
folderKey: "pursuits",
img: t,
data: {
@@ -1365,14 +1672,14 @@ function xa(e) {
}
};
}
-function Pa(e) {
+function Ha(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}`;
+function Oa(e) {
+ const t = kt(e.name) || j.pursuitDefault, a = `Pursuit: ${e.name}`;
return {
- key: Q("gear", a),
+ key: z("gear", a),
folderKey: "pursuits",
img: t,
data: {
@@ -1387,7 +1694,7 @@ function Ia(e) {
powerSupplyNotes: "Pursuit reference",
isPowerPack: !1,
energy: { value: 0, max: 0 },
- notes: Pa(e)
+ notes: Ha(e)
},
flags: {
ringworld: {
@@ -1404,7 +1711,7 @@ function Ia(e) {
}
};
}
-async function G(e, t) {
+async function K(e, t) {
const a = game.folders.find(
(n) => {
var o;
@@ -1418,123 +1725,129 @@ async function G(e, 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);
+async function Ea() {
+ const e = await K("Ringworld Catalog", null), t = await K("Species", e.id), a = await K("Skills", e.id), n = await K("Pursuits", e.id), o = await K("Gear", e.id), s = await K("Armor", o.id), i = await K("Weapons", e.id), r = await K("Melee Weapons", i.id), l = await K("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
+ weaponMelee: r.id,
+ weaponRanged: l.id
};
}
-function Ma() {
- const e = wa("pursuit");
+function Fa() {
+ const e = Ma("pursuit");
return [
- ...pt.map(ka),
- ...we.map(ba),
- ...e ? he.map(xa) : he.map(Ia),
- ...fa.map(va),
- ...ya.map(Sa),
- ...ha.map(Aa)
+ ...vt.map(Ra),
+ ...ke.map(Ca),
+ ...e ? be.map(Na) : be.map(Oa),
+ ...Pa.map($a),
+ ...La.map(Ta),
+ ...Ia.map(Da)
];
}
-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])
+async function Ue(e) {
+ var r, l, c, u, m, g, f;
+ if (!((r = game.user) != null && r.isGM)) return { added: 0, updated: 0 };
+ const t = await Ea(), a = Fa(), n = new Map(
+ game.items.map((d) => [z(d.type, d.name), d])
), o = [], s = [];
- for (const m of a) {
- const f = t[m.folderKey], y = n.get(m.key);
- if (!y) {
+ for (const d of a) {
+ const y = t[d.folderKey], b = n.get(d.key);
+ if (!b) {
o.push({
- ...m.data,
- img: m.img,
- folder: f,
+ ...d.data,
+ img: d.img,
+ folder: y,
flags: {
ringworld: {
catalog: !0,
- catalogKey: m.key
+ catalogKey: d.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 ?? {}, {
+ const h = { _id: b.id };
+ (((l = b.folder) == null ? void 0 : l.id) ?? null) !== y && (h.folder = y), String(b.img || "") !== d.img && (h.img = d.img), ((u = (c = b.flags) == null ? void 0 : c.ringworld) == null ? void 0 : u.catalog) !== !0 && (h["flags.ringworld.catalog"] = !0), ((g = (m = b.flags) == null ? void 0 : m.ringworld) == null ? void 0 : g.catalogKey) !== d.key && (h["flags.ringworld.catalogKey"] = d.key);
+ const p = foundry.utils.deepClone(b.system ?? {}), w = foundry.utils.mergeObject(p, d.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);
+ }), v = foundry.utils.diffObject(p, w);
+ Object.keys(v).length > 0 && (h.system = w), Object.keys(h).length > 1 && s.push(h);
}
- 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 };
+ o.length && await Item.createDocuments(o), s.length && await Item.updateDocuments(s);
+ const i = await ya();
+ if (e != null && e.notify) {
+ const d = i ? " GM Reference journal added." : "";
+ (f = ui.notifications) == null || f.info(`Ringworld catalog synced: +${o.length} new, ${s.length} updated.${d}`);
+ }
+ return { added: o.length, updated: s.length };
}
-function yt() {
+function At() {
return game.items.filter((e) => e.type === "species" && e.system.playable);
}
-function K(e) {
+function G(e) {
return Math.max(1, Math.min(99, Math.floor(e)));
}
-async function j(e) {
- const t = await Me(e);
+async function _(e) {
+ const t = await ye(e);
return Number(t.total ?? 0);
}
-async function Ca() {
+async function Ba() {
let e = 10;
for (; ; ) {
- const t = await j("1d6");
+ const t = await _("1d6");
if (e += t, t <= 4) break;
}
- return K(e);
+ return G(e);
}
-async function $a(e) {
+async function Ua(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")
+ str: await _(t.str),
+ mas: await _(t.mas),
+ con: await _(t.con),
+ int: await _("2d6+6"),
+ pow: await _("2d6+6"),
+ dex: await _(t.dex),
+ app: await _("2d6+6"),
+ edu: await Ba(),
+ age: await _("1d20+16")
};
}
-function xe(e) {
+function Pe(e) {
return e[Math.floor(Math.random() * e.length)];
}
-function Ra(e) {
+function qa(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})`;
+ return `${Pe(t)}${Pe(a)} (${e})`;
}
-function Ta(e, t) {
+function Ga(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))
+ str: G(e.str + Number(a.str || 0)),
+ mas: G(e.mas + Number(a.mas || 0)),
+ con: G(e.con + Number(a.con || 0)),
+ int: G(e.int + Number(a.int || 0)),
+ pow: G(e.pow + Number(a.pow || 0)),
+ dex: G(e.dex + Number(a.dex || 0)),
+ app: G(e.app + Number(a.app || 0)),
+ edu: G(e.edu + Number(a.edu || 0)),
+ age: G(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);
+async function xt(e) {
+ var m, g, f, d;
+ const t = String(((g = (m = e.system) == null ? void 0 : m.profile) == null ? void 0 : g.gravity) || "normal"), a = t === "light" || t === "heavy" ? t : "normal", n = String(((d = (f = e.system) == null ? void 0 : f.profile) == null ? void 0 : d.species) || ""), s = At().find((y) => y.name === n), i = St().find((y) => y.name === n), r = (s == null ? void 0 : s.system) ?? i ?? null, l = await Ua(a), c = Ga(l, r);
await e.update({
"system.characteristics.str": c.str,
"system.characteristics.mas": c.mas,
@@ -1554,7 +1867,7 @@ async function ht(e) {
"system.value": Math.min(99, c.int * 5)
}]);
}
-async function Da(e) {
+async function Va(e) {
const t = [
["weapon", "Laser Pistol"],
["weapon", "Variable Sword"],
@@ -1565,45 +1878,42 @@ async function Da(e) {
], 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());
+ const r = a.find((l) => l.type === s && l.name === i);
+ r && o.push(r.toObject());
}
o.length && await e.createEmbeddedDocuments("Item", o);
}
-async function wt(e) {
+async function Pt(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");
+ const t = At(), a = St(), n = t.length ? Pe(t) : null, o = n ? null : Pe(a), s = (n == null ? void 0 : n.system) ?? o, i = (n == null ? void 0 : n.name) ?? (o == null ? void 0 : o.name) ?? "Human", r = String(s.gravity || "normal"), l = String(s.homeworld || "Ringworld");
await e.update({
- name: Ra(i),
+ name: qa(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);
+ "system.profile.gravity": r,
+ "system.profile.homeworld": l
+ }), await xt(e), await ((c = e.ensureBaseSkills) == null ? void 0 : c.call(e)), await Va(e);
}
-async function bt() {
+async function Lt() {
const t = await getDocumentClass("Actor").create({
name: "Random Explorer",
type: "explorer"
});
- return await wt(t), t;
+ return await Pt(t), t;
}
-const Ha = ["str", "mas", "con", "int", "pow", "dex", "app", "edu", "age"];
-function v(e, t = 0) {
+const Ka = ["str", "mas", "con", "int", "pow", "dex", "app", "edu", "age"];
+function S(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) {
+function Oe(e) {
return Math.max(1, Math.min(99, Math.floor(e)));
}
-function Oa(e, t) {
+function _a(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);
+function at(e) {
+ const t = S(e == null ? void 0 : e.con, 10), a = S(e == null ? void 0 : e.int, 10), n = S(e == null ? void 0 : e.pow, 10), o = S(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)),
@@ -1611,64 +1921,64 @@ function Je(e) {
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);
+function ja(e) {
+ const t = S(e == null ? void 0 : e.str, 10), a = S(e == null ? void 0 : e.dex, 10), n = S(e == null ? void 0 : e.app, 10), o = S(e == null ? void 0 : e.edu, 10), s = S(e == null ? void 0 : e.int, 10), i = S(e == null ? void 0 : e.pow, 10), r = S(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),
+ perception: Math.max(0, i + r),
technical: Math.max(0, a + s)
};
}
-function Ba(e) {
+function Wa(e) {
return e <= 4 ? 7 : e <= 8 ? 6 : e <= 12 ? 5 : e <= 16 ? 4 : e <= 20 ? 3 : e <= 24 ? 2 : 1;
}
-function Fa(e) {
+function Ya(e) {
return e === "light" ? 0.5 : e === "heavy" ? 2 : 1;
}
-function Ua(e) {
+function za(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) {
+function Qa(e) {
var t;
- return Math.max(0, v((t = e.system) == null ? void 0 : t.weight, 0));
+ return Math.max(0, S((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));
+function Xa(e, t) {
+ var y, b, h, p;
+ const a = String(((y = e == null ? void 0 : e.profile) == null ? void 0 : y.gravity) || "normal"), n = Ya(a), o = t.filter((w) => w.type === "weapon" || w.type === "armor" || w.type === "gear").filter(za).reduce((w, v) => w + Qa(v), 0), s = Math.max(0, o), i = s * n, r = Math.max(0, S((b = e == null ? void 0 : e.characteristics) == null ? void 0 : b.str, 0)), l = Math.max(0, i - r), c = l > 0 ? Math.ceil(l / 10) : 0, u = l > 0, m = Math.max(1, S((h = e == null ? void 0 : e.derived) == null ? void 0 : h.move, 3)), g = Math.max(1, S((p = e == null ? void 0 : e.derived) == null ? void 0 : p.run, m * 2));
return {
rawLoadKg: s,
effectiveLoadKg: i,
- limitKg: l,
- overLimitKg: r,
+ limitKg: r,
+ overLimitKg: l,
actionRankingPenalty: c,
overLimit: u,
- currentMove: u ? 2 : d,
+ currentMove: u ? 2 : m,
currentRun: u ? 4 : g,
gravityMultiplier: n
};
}
-class Va extends Actor {
+class Za extends Actor {
prepareDerivedData() {
- if (super.prepareDerivedData(), ra(this), this.type !== "explorer" && this.type !== "creature") return;
+ if (super.prepareDerivedData(), fa(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]);
+ n.chronologicalAge = S(n.chronologicalAge, S(a.age, 25)), n.physiologicalAge = S(n.physiologicalAge, n.chronologicalAge), t.profile = n, t.combat = t.combat ?? {}, t.combat.damageMod = _a(S(a.str, 10), S(a.mas, 10)), t.derivedRolls = t.derivedRolls ?? {};
+ const o = at(a);
+ for (const r of Object.keys(o))
+ t.derivedRolls[r] = o[r];
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 ?? {
+ const s = ja(a);
+ for (const [r, l] of Object.entries(s))
+ t.skillRootMax[r] = l;
+ t.derived = t.derived ?? {}, t.derived.actionRanking = Wa(S(a.dex, 10)), t.derived.move = S(t.derived.move, 3), t.derived.run = S(t.derived.run, Math.max(1, S(t.derived.move, 3) * 2)), t.encumbrance = t.encumbrance ?? {};
+ const i = Xa(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 = S(t.finance.money, 0), t.finance.inBank = S(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);
+ }, t.unconsciousLevel = S(t.unconsciousLevel, 0);
}
get skills() {
return this.items.filter((t) => t.type === "skill");
@@ -1684,23 +1994,27 @@ class Va extends Actor {
}
async rollSkillById(t) {
const a = this.items.get(t);
- !a || a.type !== "skill" || await Ee(this, a.name, Number(a.system.value ?? 0));
+ !a || a.type !== "skill" || await Be(this, a.name, Number(a.system.value ?? 0));
}
async rollHitLocation() {
- await Xt(this);
+ await na(this);
}
async useWeaponById(t) {
const a = this.items.get(t);
- !a || a.type !== "weapon" || await dt(this, a);
+ !a || a.type !== "weapon" || await bt(this, a);
+ }
+ async reloadWeaponById(t) {
+ const a = this.items.get(t);
+ !a || a.type !== "weapon" || await ra(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) => ({
+ this.items.filter((n) => n.type === "skill").map((n) => M(n.name))
+ ), a = ke.filter((n) => !t.has(M(n.name))).map((n) => ({
name: n.name,
type: "skill",
- img: ye(n.name),
+ img: we(n.name),
system: {
value: n.base,
category: n.category,
@@ -1710,15 +2024,15 @@ class Va extends Actor {
return a.length ? (await this.createEmbeddedDocuments("Item", a), a.length) : 0;
}
async rollCharacteristics() {
- this.type === "explorer" && await ht(this);
+ this.type === "explorer" && await xt(this);
}
async randomizeCharacter() {
- this.type === "explorer" && await wt(this);
+ this.type === "explorer" && await Pt(this);
}
async applySpeciesItem(t) {
- var y, b, w;
+ var y, b, h;
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 = {
+ 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) ?? {}, r = ((b = this.system) == null ? void 0 : b.characteristics) ?? {}, l = ((h = this.system) == null ? void 0 : h.profile) ?? {}, c = {}, u = {
str: 0,
mas: 0,
con: 0,
@@ -1729,31 +2043,31 @@ class Va extends Actor {
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);
+ for (const p of Ka) {
+ const w = S(i == null ? void 0 : i[p], 0), v = S(o == null ? void 0 : o[p], 0);
+ u[p] = v;
+ const I = S(r == null ? void 0 : r[p], p === "age" ? 25 : 10) - w;
+ c[`system.characteristics.${p}`] = Oe(I + v);
}
- 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"] = {
+ const m = S(i == null ? void 0 : i.age, 0), g = u.age, f = S(l == null ? void 0 : l.chronologicalAge, S(r == null ? void 0 : r.age, 25)), d = S(l == null ? void 0 : l.physiologicalAge, f);
+ c["system.profile.chronologicalAge"] = Oe(f - m + g), c["system.profile.physiologicalAge"] = Oe(d - m + 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;
+ var s, i, r;
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);
+ }, n = at(((s = this.system) == null ? void 0 : s.characteristics) ?? {}), o = S((r = (i = this.system) == null ? void 0 : i.derivedRolls) == null ? void 0 : r[t], n[t]);
+ await Be(this, a[t], o);
}
}
-class Ga extends Item {
+class Ja extends Item {
async rollSkill() {
var a;
if (this.type !== "skill") return;
@@ -1762,7 +2076,7 @@ class Ga extends Item {
(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));
+ await Be(t, this.name, Number(this.system.value ?? 0));
}
async useWeapon() {
var a;
@@ -1772,127 +2086,127 @@ class Ga extends Item {
(a = ui.notifications) == null || a.warn("Weapon must be owned by an Actor to use.");
return;
}
- await dt(t, this);
+ await bt(t, this);
}
}
-const ja = foundry.appv1.sheets.ActorSheet, Fe = {
+const en = foundry.appv1.sheets.ActorSheet, qe = {
Agility: "agility",
Communication: "communication",
Knowledge: "knowledge",
Perception: "perception",
Technical: "technical"
-}, _a = {
+}, tn = {
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");
+}, an = /* @__PURE__ */ new Set([
+ M("Archaic Melee Weapon"),
+ M("Archaic Ranged Weapons")
+]), nn = M("Hyperdrive");
function fe(e, t) {
return e.name.localeCompare(t.name);
}
-function C(e, t = 0) {
+function L(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 F(e, t = 0) {
+ return Math.max(0, Math.floor(L(e, t)));
}
-function Pe(e) {
+function Le(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)
+ education: F(e == null ? void 0 : e.education, 0),
+ pursuits: F(e == null ? void 0 : e.pursuits, 0),
+ special: F(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);
+function on(e) {
+ const t = String((e == null ? void 0 : e.name) || "").trim(), a = F(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 = {};
+function It(e) {
+ const t = Array.isArray(e == null ? void 0 : e.pursuits) ? e.pursuits.map((o) => on(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);
+ const i = Le(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)));
+ o && (n[o] = Math.max(0, F(s, 0)));
return {
pursuits: t,
allocations: a,
baseBySkillId: n
};
}
-function Qa(e) {
- return !Wa.has(R(e));
+function sn(e) {
+ return !an.has(M(e));
}
-function Xa(e) {
- return R(e) !== Ya;
+function rn(e) {
+ return M(e) !== nn;
}
-function Za(e) {
+function ln(e) {
const t = new Map(
- he.map((n) => [R(n.name), n])
+ be.map((n) => [M(n.name), n])
), a = /* @__PURE__ */ new Set();
for (const n of e) {
- const o = t.get(R(n.name));
+ const o = t.get(M(n.name));
if (o) {
for (const s of o.fixedSkills || [])
- a.add(R(s));
+ a.add(M(s));
for (const s of o.optionalSkillGroups || [])
for (const i of s.skills || [])
- a.add(R(i));
+ a.add(M(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);
+function cn(e) {
+ const t = F(e == null ? void 0 : e.age, 0), a = F(e == null ? void 0 : e.edu, 0), n = F(e == null ? void 0 : e.int, 0), o = Math.max(0, (a - 10) * 20), s = Math.max(0, t - a - 6), i = s * 20, r = Math.max(0, n * 10);
return {
educationTotal: o,
pursuitsTotal: i,
- specialTotal: l,
+ specialTotal: r,
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;
+function Q(e, t) {
+ var m, g, f, d, y, b, h;
+ const a = cn(((m = e.system) == null ? void 0 : m.characteristics) ?? {}), n = It(((g = e.system) == null ? void 0 : g.characterBuilder) ?? {}), o = be.map((p) => p.name).sort((p, w) => p.localeCompare(w)), s = ln(n.pursuits), i = /* @__PURE__ */ new Map();
+ let r = 0, l = 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(
+ const w = Le(n.allocations[p.id]), v = w.education + w.pursuits + w.special, x = n.baseBySkillId[p.id], I = (f = $e(p.name)) == null ? void 0 : f.base, T = Math.max(0, L((d = p.system) == null ? void 0 : d.value, 0) - v), R = 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);
+ Number.isFinite(x) ? x : I ?? T
+ ), D = qe[String(((y = p.system) == null ? void 0 : y.category) || "")] ?? "", N = D ? Math.max(0, F((h = (b = e.system) == null ? void 0 : b.skillRootMax) == null ? void 0 : h[D], 0)) : 99, C = Math.max(0, N - R), U = M(p.name), re = sn(p.name), Z = s.has(U), H = rn(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;
+ allocation: w,
+ totalAllocated: v,
+ baseValue: R,
+ rootCap: N,
+ maxAllocation: C,
+ allowsEducation: re,
+ allowsPursuits: Z,
+ allowsSpecial: H
+ }), r += w.education, l += w.pursuits, c += w.special;
}
- const u = n.pursuits.reduce((p, h) => p + h.years, 0);
+ const u = n.pursuits.reduce((p, w) => p + w.years, 0);
return {
state: n,
education: {
total: a.educationTotal,
- spent: l,
- remaining: a.educationTotal - l
+ spent: r,
+ remaining: a.educationTotal - r
},
pursuits: {
total: a.pursuitsTotal,
- spent: r,
- remaining: a.pursuitsTotal - r
+ spent: l,
+ remaining: a.pursuitsTotal - l
},
special: {
total: a.specialTotal,
@@ -1909,39 +2223,36 @@ function Z(e, t) {
bySkillId: i
};
}
-function en(e, t) {
- var n, o, s, i, l, r, c;
+function un(e, t) {
+ var n, o, s, i, r, l, 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);
+ const a = L((l = (r = e.system) == null ? void 0 : r.perLocation) == null ? void 0 : l[t], 0);
+ return a > 0 ? a : L((c = e.system) == null ? void 0 : c.value, 0);
}
-function tn(e, t) {
+function mn(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);
+ const a = L((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((r) => r.type === "armor").reduce((r, l) => r + un(l, 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();
+function nt(e, t, a) {
+ var r, l;
+ const n = oe(e).entries.find((c) => c.slug === t), o = ((l = (r = e.system) == null ? void 0 : r.hitLocations) == null ? void 0 : l[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),
+ armor: L(o.armor, 0),
+ totalArmor: mn(e, t),
hp: {
- value: C(s.value, 0),
- max: C(s.max, 0)
+ value: L(s.value, 0),
+ max: L(s.max, 0)
}
};
}
-function tt(e) {
+function ot(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 {
+class dn extends en {
isToolsUnlocked() {
var t, a;
return !!((a = (t = this.actor) == null ? void 0 : t.system) != null && a.sheetToolsUnlocked);
@@ -1958,78 +2269,78 @@ class nn extends ja {
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;
+ var H, le, ce, ue, me, de, ge, pe, J, _e, je, We, Ye, ze, Qe, Xe;
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();
+ (H = game.user) != null && H.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 = Q(n, o), i = n.items.filter((k) => k.type === "weapon").sort(fe), r = n.items.filter((k) => k.type === "armor").sort(fe), l = 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 = ea(), m = /* @__PURE__ */ new Map();
for (const k of o) {
- const O = R(k.name);
- d.has(O) || d.set(O, k);
+ const O = M(k.name);
+ m.has(O) || m.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;
+ const g = n.system.skillRootMax ?? {}, f = Jt.map((k) => {
+ const O = qe[k] ?? k.toLowerCase(), De = ke.filter((V) => V.category === k).map((V) => {
+ var Ze;
+ const B = m.get(M(V.name)), ee = B ? s.bySkillId.get(B.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 }
+ itemId: (B == null ? void 0 : B.id) ?? null,
+ icon: (B == null ? void 0 : B.img) || we(V.name),
+ value: ((Ze = B == null ? void 0 : B.system) == null ? void 0 : Ze.value) ?? V.base,
+ missing: !B,
+ allocationTotal: (ee == null ? void 0 : ee.totalAllocated) ?? 0,
+ allocation: (ee == null ? void 0 : ee.allocation) ?? { education: 0, pursuits: 0, special: 0 }
};
});
return {
category: k,
rootKey: O,
- rootFormula: _a[k] ?? "",
- rootValue: C(g[O], 0),
- rows: Te
+ rootFormula: tn[k] ?? "",
+ rootValue: L(g[O], 0),
+ rows: De
};
- }), f = o.filter((k) => !u.has(R(k.name))).map((k) => {
+ }), d = o.filter((k) => !u.has(M(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),
+ img: Fe(k.name, k.img || X),
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,
+ }), y = f.reduce(
+ (k, O) => k + O.rows.filter((De) => De.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`;
+ ), b = oe(n), h = new Map(b.entries.map((k) => [k.slug, `${k.min}-${k.max}`])), w = b.entries.map((k) => k.slug).map(
+ (k) => nt(n, k, h.get(k) ?? "")
+ ), x = b.bodyOrder.map(
+ (k) => nt(n, k, h.get(k) ?? "")
+ ), I = ((ce = n.system) == null ? void 0 : ce.encumbrance) ?? {}, T = L(I.rawLoadKg, 0), R = L(I.effectiveLoadKg, T), D = L(I.limitKg, L((me = (ue = n.system) == null ? void 0 : ue.characteristics) == null ? void 0 : me.str, 0)), N = L(I.actionRankingPenalty, 0), C = L((ge = (de = n.system) == null ? void 0 : de.derived) == null ? void 0 : ge.actionRanking, 0), U = Math.max(1, C + N), re = `${T.toFixed(1)}kg on-person | ${R.toFixed(1)}kg effective / ${D.toFixed(1)}kg limit | AR +${N} (stowed ignored)`, Z = `${L((J = (pe = n.system) == null ? void 0 : pe.derived) == null ? void 0 : J.currentMove, L((je = (_e = n.system) == null ? void 0 : _e.derived) == null ? void 0 : je.move, 3))}m / ${L((Ye = (We = n.system) == null ? void 0 : We.derived) == null ? void 0 : Ye.currentRun, L((Qe = (ze = n.system) == null ? void 0 : ze.derived) == null ? void 0 : Qe.run, 6))}m`;
return {
...a,
system: n.system,
skills: o,
- skillsByCategory: m,
- customSkills: f,
+ skillsByCategory: f,
+ customSkills: d,
missingBaseSkillsCount: y,
weapons: i,
- armors: l,
- gear: r,
+ armors: r,
+ gear: l,
speciesOptions: c,
- hitLocations: h,
- bodyLocations: S,
- hitLocationSchemaOptions: Kt(),
+ hitLocations: w,
+ bodyLocations: x,
+ hitLocationSchemaOptions: gt(),
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,
+ sheetToolsUnlocked: !!((Xe = n.system) != null && Xe.sheetToolsUnlocked),
+ baseActionRanking: C,
+ effectiveActionRanking: U,
+ encumbranceSummary: re,
+ movementSummary: Z,
builder: {
education: s.education,
pursuits: s.pursuits,
@@ -2050,17 +2361,17 @@ class nn extends ja {
});
}
async syncSkillsFromBuilderState(t) {
- var o, s, i, l, r, c;
+ var o, s, i, r, l, 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;
+ const m = Le(t.allocations[u.id]), g = m.education + m.pursuits + m.special, f = qe[String(((o = u.system) == null ? void 0 : o.category) || "")] ?? "", d = f ? Math.max(0, F((i = (s = this.actor.system) == null ? void 0 : s.skillRootMax) == null ? void 0 : i[f], 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 h = (r = $e(u.name)) == null ? void 0 : r.base;
+ y = Math.max(0, h ?? L((l = u.system) == null ? void 0 : l.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 });
+ const b = Math.max(0, Math.min(99, Math.min(d, y + g)));
+ L((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);
}
@@ -2072,112 +2383,112 @@ class nn extends ja {
(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);
+ const s = this.actor.items.filter((u) => u.type === "skill"), i = Q(this.actor, s), r = foundry.utils.deepClone(i.state), l = r.pursuits.findIndex((u) => M(u.name) === M(n));
+ l >= 0 ? r.pursuits[l].years = o : r.pursuits.push({ name: n, years: o }), await this.updateBuilderState(r);
}
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);
+ const a = this.actor.items.filter((s) => s.type === "skill"), n = Q(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);
+ const t = this.actor.items.filter((o) => o.type === "skill"), a = Q(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;
+ var d, y, b, h;
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);
+ await ((y = (d = this.actor).ensureBaseSkills) == null ? void 0 : y.call(d));
+ const t = this.actor.items.filter((p) => p.type === "skill").sort(fe), a = Q(this.actor, t), n = foundry.utils.deepClone(a.state), o = Math.max(0, a.pursuitYears.total), s = be.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);
+ ), w = [...s];
+ for (let x = 0; x < p && w.length; x += 1) {
+ const I = Math.floor(Math.random() * w.length);
+ n.pursuits.push({ name: w[I], years: 1 }), w.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;
+ let v = o - n.pursuits.length;
+ for (; v > 0 && n.pursuits.length; ) {
+ const x = ot(n.pursuits);
+ x.years += 1, v -= 1;
}
}
- const i = kt(n);
+ const i = It(n);
i.allocations = {};
- const l = Z(
+ const r = Q(
{ ...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 w = r.bySkillId.get(p.id);
+ w && (i.baseBySkillId[p.id] = w.baseValue, i.allocations[p.id] = { education: 0, pursuits: 0, special: 0 });
}
- const r = /* @__PURE__ */ new Map();
+ const l = /* @__PURE__ */ new Map();
for (const p of t)
- r.set(p.id, 0);
+ l.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; ) {
+ const w = r.bySkillId.get(p);
+ return w ? Math.max(0, w.maxAllocation) : 0;
+ }, u = (p, w, v) => {
+ let x = Math.max(0, p), I = 0;
+ for (; x > 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);
+ const T = t.filter((N) => {
+ const C = r.bySkillId.get(N.id);
+ return !C || !v(N, C) ? !1 : (l.get(N.id) ?? 0) < c(N.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;
+ if (!T.length) break;
+ const R = ot(T), D = i.allocations[R.id] ?? { education: 0, pursuits: 0, special: 0 };
+ D[w] += 1, i.allocations[R.id] = D, l.set(R.id, (l.get(R.id) ?? 0) + 1), x -= 1;
}
- return S;
- }, d = u(
- Math.max(0, l.education.total),
+ return x;
+ }, m = u(
+ Math.max(0, r.education.total),
"education",
- (p, h) => h.allowsEducation
+ (p, w) => w.allowsEducation
), g = u(
- Math.max(0, l.pursuits.total),
+ Math.max(0, r.pursuits.total),
"pursuits",
- (p, h) => h.allowsPursuits
- ), m = u(
- Math.max(0, l.special.total),
+ (p, w) => w.allowsPursuits
+ ), f = u(
+ Math.max(0, r.special.total),
"special",
- (p, h) => h.allowsSpecial
+ (p, w) => w.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];
+ const w = Le(i.allocations[p.id]);
+ w.education + w.pursuits + w.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.");
+ await this.updateBuilderState(i), await this.syncSkillsFromBuilderState(i), m > 0 || g > 0 || f > 0 ? (b = ui.notifications) == null || b.info(
+ `Random build complete. Unspent points: EDU ${m}, Pursuits ${g}, Special ${f}.`
+ ) : (h = ui.notifications) == null || h.info("Random build complete. All available points allocated.");
}
async promptSkillAllocation(t) {
- var y, b, w;
+ var y, b, h;
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);
+ const n = this.actor.items.filter((p) => p.type === "skill"), o = Q(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 = `
+ const i = s.allocation, r = Math.max(0, o.education.remaining + i.education), l = Math.max(0, o.pursuits.remaining + i.pursuits), c = Math.max(0, o.special.remaining + i.special), u = Math.max(0, s.maxAllocation), m = `