Files
Ringworld/module/ringworld.mjs
T

3336 lines
126 KiB
JavaScript

async function Tt(e) {
const t = game == null ? void 0 : game.dice3d;
if (t != null && t.showForRoll)
try {
await t.showForRoll(e, game.user, !0, null, !1);
} catch {
}
}
async function Me(e) {
const t = await new Roll(e).evaluate();
return await Tt(t), t;
}
async function Ue(e) {
const t = await Me("1d100");
return Dt(t.total ?? 0, e);
}
function Dt(e, t) {
const a = Math.max(0, Math.floor(Number(t) || 0)), n = Math.max(1, Math.floor(a / 5)), o = a >= 50 ? 100 : 96, s = e <= a, i = s && e <= n, l = !s && e >= o;
let r = "failure";
return l ? r = "fumble" : i ? r = "special" : s && (r = "success"), {
roll: e,
target: a,
outcome: r,
isSuccess: s,
isSpecial: i,
isFumble: l
};
}
function Ae(e) {
switch (e) {
case "special":
return "Special Success";
case "success":
return "Success";
case "fumble":
return "Fumble";
default:
return "Failure";
}
}
const x = "ringworld", Ce = [
{ slug: "rLeg", label: "Right Leg", min: 1, max: 3 },
{ slug: "lLeg", label: "Left Leg", min: 4, max: 6 },
{ slug: "abdomen", label: "Abdomen", min: 7, max: 10 },
{ slug: "chest", label: "Chest", min: 11, max: 15 },
{ slug: "rArm", label: "Right Arm", min: 16, max: 17 },
{ slug: "lArm", label: "Left Arm", min: 18, max: 19 },
{ slug: "head", label: "Head", min: 20, max: 20 }
], Ht = ["rArm", "lArm", "rLeg", "lLeg"], Nt = ["rLeg", "lLeg"];
function Ot(e) {
return Ht.includes(e);
}
function it(e) {
return String(e ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
}
function oe(e, t) {
return e.map((a) => ({
...a,
hpFactor: (t == null ? void 0 : t[a.slug]) ?? (a.slug === "chest" ? 0.35 : a.slug === "rArm" || a.slug === "lArm" ? 0.25 : 0.3)
}));
}
const rt = {
id: "humanoid",
label: "Humanoid",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
entries: oe(Ce)
}, Et = {
id: "dolphin",
label: "Dolphin",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
entries: oe([
{ slug: "rLeg", label: "Right Fluke", min: 1, max: 3 },
{ slug: "lLeg", label: "Left Fluke", min: 4, max: 6 },
{ slug: "abdomen", label: "Lower Body", min: 7, max: 9 },
{ slug: "chest", label: "Main Body", min: 10, max: 15 },
{ slug: "rArm", label: "Right Fin", min: 16, max: 17 },
{ slug: "lArm", label: "Left Fin", min: 18, max: 19 },
{ slug: "head", label: "Head", min: 20, max: 20 }
], {
head: 0.3,
rArm: 0.2,
lArm: 0.2,
chest: 0.4,
abdomen: 0.35,
rLeg: 0.25,
lLeg: 0.25
})
}, Bt = {
id: "puppeteer",
label: "Puppeteer",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
entries: oe([
{ slug: "rLeg", label: "Right Rear Leg", min: 1, max: 3 },
{ slug: "lLeg", label: "Left Rear Leg", min: 4, max: 6 },
{ slug: "rArm", label: "Right Foreleg", min: 7, max: 9 },
{ slug: "lArm", label: "Left Foreleg", min: 10, max: 12 },
{ slug: "chest", label: "Fore Body", min: 13, max: 15 },
{ slug: "abdomen", label: "Hind Body", min: 16, max: 18 },
{ slug: "head", label: "Twin Heads", min: 19, max: 20 }
], {
head: 0.32,
chest: 0.35,
abdomen: 0.35,
rArm: 0.28,
lArm: 0.28,
rLeg: 0.28,
lLeg: 0.28
})
}, Ft = {
id: "trinoc",
label: "Trinoc",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
entries: oe([
{ slug: "rArm", label: "Right Wing/Arm", min: 1, max: 3 },
{ slug: "lArm", label: "Left Wing/Arm", min: 4, max: 6 },
{ slug: "chest", label: "Upper Torso", min: 7, max: 11 },
{ slug: "abdomen", label: "Lower Torso", min: 12, max: 14 },
{ slug: "rLeg", label: "Right Leg", min: 15, max: 17 },
{ slug: "lLeg", label: "Left Leg", min: 18, max: 19 },
{ slug: "head", label: "Head", min: 20, max: 20 }
], {
head: 0.3,
rArm: 0.27,
lArm: 0.27,
chest: 0.36,
abdomen: 0.3,
rLeg: 0.27,
lLeg: 0.27
})
}, Ut = {
id: "bandersnatchi",
label: "Bandersnatchi",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
entries: oe([
{ slug: "rLeg", label: "Right Rear Mass", min: 1, max: 2 },
{ slug: "lLeg", label: "Left Rear Mass", min: 3, max: 4 },
{ slug: "abdomen", label: "Lower Body Mass", min: 5, max: 11 },
{ slug: "chest", label: "Upper Body Mass", min: 12, max: 17 },
{ slug: "rArm", label: "Right Manipulator", min: 18, max: 18 },
{ slug: "lArm", label: "Left Manipulator", min: 19, max: 19 },
{ slug: "head", label: "Head Cluster", min: 20, max: 20 }
], {
head: 0.28,
chest: 0.42,
abdomen: 0.42,
rArm: 0.2,
lArm: 0.2,
rLeg: 0.24,
lLeg: 0.24
})
}, qt = {
id: "kzinti",
label: "Kzinti",
bodyOrder: ["head", "lArm", "chest", "rArm", "abdomen", "lLeg", "rLeg"],
entries: oe([
{ slug: "rLeg", label: "Right Hind Leg", min: 1, max: 3 },
{ slug: "lLeg", label: "Left Hind Leg", min: 4, max: 6 },
{ slug: "abdomen", label: "Abdomen", min: 7, max: 10 },
{ slug: "chest", label: "Chest", min: 11, max: 15 },
{ slug: "rArm", label: "Right Foreleg", min: 16, max: 17 },
{ slug: "lArm", label: "Left Foreleg", min: 18, max: 19 },
{ slug: "head", label: "Head", min: 20, max: 20 }
])
}, lt = {
humanoid: rt,
dolphin: Et,
puppeteer: Bt,
trinoc: Ft,
bandersnatchi: Ut,
kzinti: qt
};
function Kt() {
return Object.values(lt);
}
function Vt(e) {
const t = it(e);
return t ? t.includes("dolphin") ? "dolphin" : t.includes("puppeteer") ? "puppeteer" : t.includes("trinoc") ? "trinoc" : t.includes("bandersnatch") ? "bandersnatchi" : t.includes("kzinti") ? "kzinti" : "humanoid" : "humanoid";
}
function Ne(e) {
const t = it(e);
return lt[t] ?? rt;
}
function se(e) {
var n, o, s;
const t = String(((n = e == null ? void 0 : e.system) == null ? void 0 : n.hitLocationSchema) || "auto").trim().toLowerCase();
if (t && t !== "auto")
return Ne(t);
const a = ((s = (o = e == null ? void 0 : e.system) == null ? void 0 : o.profile) == null ? void 0 : s.species) ?? "";
return Ne(Vt(a));
}
function Gt(e) {
return e ? se(e).entries : Ce;
}
function jt(e, t) {
return t.find((n) => e >= n.min && e <= n.max) ?? t[t.length - 1];
}
async function ct(e) {
const a = (await Me("1d20")).total ?? 20, n = Gt(e);
return {
roll: a,
location: jt(a, n)
};
}
async function _t(e) {
const t = e ? se(e) : Ne("humanoid"), a = t.entries, n = new Map(a.map((r) => [r.slug, r])), o = (r, c) => {
var u;
return ((u = n.get(r)) == null ? void 0 : u.label) ?? c;
}, s = `
<form class="ringworld-hit-picker schema-${t.id}">
<input type="hidden" name="location" value="random" />
<div class="rw-hit-picker-top">
<button type="button" class="rw-hit-picker-choice active" data-location="random">
Roll Random (1d20)
</button>
</div>
<div class="rw-hit-picker-body">
<button type="button" class="rw-hit-picker-choice rw-loc-head" data-location="head">${o("head", "Head")}</button>
<button type="button" class="rw-hit-picker-choice rw-loc-l-arm" data-location="lArm">${o("lArm", "Left Arm")}</button>
<button type="button" class="rw-hit-picker-choice rw-loc-chest" data-location="chest">${o("chest", "Chest")}</button>
<button type="button" class="rw-hit-picker-choice rw-loc-r-arm" data-location="rArm">${o("rArm", "Right Arm")}</button>
<button type="button" class="rw-hit-picker-choice rw-loc-abdomen" data-location="abdomen">${o("abdomen", "Abdomen")}</button>
<button type="button" class="rw-hit-picker-choice rw-loc-l-leg" data-location="lLeg">${o("lLeg", "Left Leg")}</button>
<button type="button" class="rw-hit-picker-choice rw-loc-r-leg" data-location="rLeg">${o("rLeg", "Right Leg")}</button>
</div>
</form>
`, i = await new Promise((r) => {
let c = !1;
const u = (g) => {
c || (c = !0, r(g));
};
new Dialog({
title: "Choose Hit Location",
content: s,
buttons: {
apply: {
label: "Apply",
callback: (g) => {
const m = String(g.find("input[name='location']").val() || "random");
u(m);
}
},
cancel: {
label: "Cancel",
callback: () => u(null)
}
},
default: "apply",
render: (g) => {
const m = g.find(".ringworld-hit-picker");
m.find(".rw-hit-picker-choice").on("click", (f) => {
f.preventDefault();
const y = f.currentTarget, b = String(y.dataset.location || "random");
m.find(".rw-hit-picker-choice").removeClass("active"), $(y).addClass("active"), m.find("input[name='location']").val(b);
});
},
close: () => u(null)
}, {
classes: ["ringworld-hit-location-dialog"],
width: 500,
resizable: !1
}).render(!0);
});
if (!i) return null;
if (i === "random") {
const r = await ct(e);
return {
slug: r.location.slug,
label: r.location.label,
rolled: r.roll
};
}
const l = a.find((r) => r.slug === i);
return l ? {
slug: l.slug,
label: l.label,
rolled: null
} : null;
}
const Wt = [
"Agility",
"Communication",
"Knowledge",
"Perception",
"Technical"
], we = [
{ name: "Archaic Melee Weapon", category: "Agility", base: 5 },
{ name: "Archaic Ranged Weapons", category: "Agility", base: 3 },
{ name: "Athletics", category: "Agility", base: 15 },
{ name: "Hide", category: "Agility", base: 10 },
{ name: "Sneak", category: "Agility", base: 5 },
{ name: "Unarmed Combat", category: "Agility", base: 0 },
{ name: "V. Sword, F. Laser", category: "Agility", base: 15 },
{ name: "Bargain", category: "Communication", base: 10 },
{ name: "Debate", category: "Communication", base: 5 },
{ name: "Fast Talk", category: "Communication", base: 10 },
{ name: "Fine Arts", category: "Communication", base: 5 },
{ name: "Musicianship", category: "Communication", base: 5 },
{ name: "Orate", category: "Communication", base: 5 },
{ name: "Own Language", category: "Communication", base: 0, specialization: "INTx5 base" },
{ name: "Perform", category: "Communication", base: 5 },
{ name: "Psychology", category: "Communication", base: 0 },
{ name: "Anthropology", category: "Knowledge", base: 0 },
{ name: "Astronomy", category: "Knowledge", base: 0 },
{ name: "Biology", category: "Knowledge", base: 0 },
{ name: "Botany", category: "Knowledge", base: 0 },
{ name: "Chemistry", category: "Knowledge", base: 0 },
{ name: "Computers", category: "Knowledge", base: 0 },
{ name: "Emergency Treatment", category: "Knowledge", base: 1 },
{ name: "Engineering", category: "Knowledge", base: 0 },
{ name: "Farming", category: "Knowledge", base: 0 },
{ name: "History", category: "Knowledge", base: 5 },
{ name: "Law", category: "Knowledge", base: 0 },
{ name: "Mathematics", category: "Knowledge", base: 0 },
{ name: "Physics", category: "Knowledge", base: 0 },
{ name: "Planetology", category: "Knowledge", base: 0 },
{ name: "Second Languages", category: "Knowledge", base: 0 },
{ name: "Strategy", category: "Knowledge", base: 0 },
{ name: "Theology", category: "Knowledge", base: 0 },
{ name: "Zoology", category: "Knowledge", base: 0 },
{ name: "Handgun, Energy", category: "Perception", base: 5 },
{ name: "Handgun, Projectile", category: "Perception", base: 3 },
{ name: "Heavy Weapon, Energy", category: "Perception", base: 5 },
{ name: "Heavy Weapon, Projectile", category: "Perception", base: 3 },
{ name: "Listen", category: "Perception", base: 5 },
{ name: "Observe", category: "Perception", base: 5 },
{ name: "Scent", category: "Perception", base: 0 },
{ name: "Search", category: "Perception", base: 5 },
{ name: "Track", category: "Perception", base: 5 },
{ name: "Aquatic Vehicle", category: "Technical", base: 0 },
{ name: "Atmospheric Craft", category: "Technical", base: 0 },
{ name: "Ground Vehicle", category: "Technical", base: 0 },
{ name: "Hyperdrive", category: "Technical", base: 0 },
{ name: "Personal Flyer", category: "Technical", base: 15 },
{ name: "Reaction Drive", category: "Technical", base: 0 },
{ name: "Reactionless Drive", category: "Technical", base: 0 },
{ name: "Repair", category: "Technical", base: 0 },
{ name: "Ringworld", category: "Technical", base: 0 },
{ name: "Weapons System", category: "Technical", base: 0 }
];
function R(e) {
return String(e || "").toLowerCase().replace(/[^a-z0-9]+/g, "").trim();
}
function Yt() {
return new Set(we.map((e) => R(e.name)));
}
const W = "systems/ringworld/assets/icons/skill.svg";
function zt(e) {
return String(e).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
}
function ye(e) {
return `systems/ringworld/assets/icons/skill/${zt(e)}.svg`;
}
function $e(e) {
const t = R(e);
return we.find((a) => R(a.name) === t);
}
function Oe(e, t) {
const a = String(t || "").trim();
return a && a !== "icons/svg/item-bag.svg" ? a : $e(e) ? ye(e) : W;
}
async function ut(e) {
var a;
const t = e.actor ? ChatMessage.getSpeaker({ actor: e.actor }) : ChatMessage.getSpeaker();
await ChatMessage.create({
user: (a = game.user) == null ? void 0 : a.id,
speaker: t,
content: e.content
});
}
async function mt(e) {
var n;
const t = {
...e,
actorImg: e.actorImg || ((n = e.actor) == null ? void 0 : n.img) || "icons/svg/mystery-man.svg",
skillImg: e.skillImg || W
}, a = await renderTemplate(`systems/${x}/templates/chat/skill-roll-card.hbs`, t);
await ut({ actor: e.actor, content: a });
}
async function Xe(e) {
var n;
const t = {
...e,
actorImg: e.actorImg || ((n = e.actor) == null ? void 0 : n.img) || "icons/svg/mystery-man.svg",
weaponImg: e.weaponImg || "icons/svg/sword.svg",
skillImg: e.skillImg || W,
hasTarget: e.hasTarget ?? !0
}, a = await renderTemplate(`systems/${x}/templates/chat/attack-card.hbs`, t);
await ut({ actor: e.actor, content: a });
}
function P(e, t = 0) {
const a = Number(e);
return Number.isFinite(a) ? a : t;
}
function Qt() {
var t, a;
const e = Array.from(((t = game.user) == null ? void 0 : t.targets) ?? []);
return e.length ? ((a = e[0]) == null ? void 0 : a.actor) ?? null : null;
}
async function Xt(e) {
var o;
const t = await ct(e), a = e ? ChatMessage.getSpeaker({ actor: e }) : ChatMessage.getSpeaker(), n = `
<div class="ringworld-chat-card">
<h3>Hit Location</h3>
<p><strong>Roll:</strong> ${t.roll}</p>
<p><strong>Location:</strong> ${t.location.label}</p>
</div>
`;
await ChatMessage.create({
user: (o = game.user) == null ? void 0 : o.id,
speaker: a,
content: n
});
}
async function Ee(e, t, a) {
const n = await Ue(a), o = e.items.find(
(s) => s.type === "skill" && s.name.toLowerCase() === String(t).toLowerCase()
);
await mt({
actor: e,
actorName: e.name,
actorImg: e.img,
skillName: t,
skillImg: o == null ? void 0 : o.img,
roll: n.roll,
target: n.target,
outcomeLabel: Ae(n.outcome),
isSuccess: n.isSuccess,
isSpecial: n.isSpecial,
isFumble: n.isFumble
});
}
function Zt(e, t) {
const a = t.system.linkedSkillId, n = a ? e.items.get(a) : null;
if ((n == null ? void 0 : n.type) === "skill")
return { name: n.name, target: P(n.system.value), img: n.img };
const o = String(t.system.linkedSkill || "").trim();
if (o) {
const s = e.items.find(
(i) => i.type === "skill" && i.name.toLowerCase() === o.toLowerCase()
);
if (s)
return { name: s.name, target: P(s.system.value), img: s.img };
}
return {
name: o || "Unlinked Skill",
target: 0
};
}
function Jt(e) {
return Math.max(0, Math.min(99, Math.floor(e)));
}
async function ea(e, t, a) {
var d;
const o = String(((d = t.system) == null ? void 0 : d.attackType) || "ranged") === "ranged", s = "full", i = foundry.utils.escapeHTML(String((e == null ? void 0 : e.name) || "Attacker")), l = foundry.utils.escapeHTML(String((t == null ? void 0 : t.name) || "Weapon")), r = foundry.utils.escapeHTML(String((e == null ? void 0 : e.img) || "icons/svg/mystery-man.svg")), c = foundry.utils.escapeHTML(String((t == null ? void 0 : t.img) || "icons/svg/sword.svg")), u = `
<form class="ringworld-attack-modifiers">
<header class="rw-attack-dialog-head">
<img class="rw-attack-dialog-avatar" src="${r}" alt="${i}" />
<img class="rw-attack-dialog-weapon" src="${c}" alt="${l}" />
<div class="rw-attack-dialog-title-block">
<h3>${l}</h3>
<p>${i} Attack Setup</p>
</div>
</header>
<div class="form-group">
<label>Base Skill</label>
<input type="number" name="baseSkill" value="${a}" disabled />
</div>
<div class="form-group">
<label>Aim State</label>
<select name="aimMode" ${o ? "" : "disabled"}>
<option value="unaimed" >Unaimed (Quarter Skill)</option>
<option value="partial" >Partially Aimed (Half Skill)</option>
<option value="full" selected>Fully Aimed (Full Skill)</option>
</select>
</div>
<div class="form-group">
<label>Cover Modifier</label>
<input type="number" name="coverMod" value="0" />
</div>
<div class="form-group">
<label>Movement Modifier</label>
<input type="number" name="movementMod" value="0" />
</div>
<div class="form-group">
<label>Range Modifier</label>
<input type="number" name="rangeMod" value="0" />
</div>
<div class="form-group">
<label>Situational Modifier</label>
<input type="number" name="situationalMod" value="0" />
</div>
<div class="form-group">
<label>Custom Modifier Label</label>
<input type="text" name="customLabel" value="" />
</div>
<div class="form-group">
<label>Custom Modifier Value</label>
<input type="number" name="customMod" value="0" />
</div>
<p class="notes">Ringworld rule support: unaimed ranged attacks use quarter skill, partial aim uses half skill, full aim uses full skill.</p>
</form>
`;
return new Promise((g) => {
let m = !1;
const f = (y) => {
m || (m = !0, g(y));
};
new Dialog({
title: `${t.name} - Attack Modifiers`,
content: u,
buttons: {
roll: {
label: "Roll Attack",
callback: (y) => {
var le, ce, ue, me, de, ge, pe;
const b = y[0], w = (ee) => b == null ? void 0 : b.querySelector(`[name="${ee}"]`), p = String(((le = w("aimMode")) == null ? void 0 : le.value) || s), h = p === "unaimed" || p === "partial" || p === "full" ? p : s, A = o ? { unaimed: 0.25, partial: 0.5, full: 1 }[h] : 1, S = P((ce = w("coverMod")) == null ? void 0 : ce.value, 0), I = P((ue = w("movementMod")) == null ? void 0 : ue.value, 0), M = P((me = w("rangeMod")) == null ? void 0 : me.value, 0), D = P((de = w("situationalMod")) == null ? void 0 : de.value, 0), T = P((ge = w("customMod")) == null ? void 0 : ge.value, 0), H = String(((pe = w("customLabel")) == null ? void 0 : pe.value) || "").trim(), L = S + I + M + D + T, F = a + L, X = Math.floor(F * A), J = Jt(X), N = [`Base ${a}`];
if (S && N.push(`Cover ${S >= 0 ? "+" : ""}${S}`), I && N.push(`Movement ${I >= 0 ? "+" : ""}${I}`), M && N.push(`Range ${M >= 0 ? "+" : ""}${M}`), D && N.push(`Situational ${D >= 0 ? "+" : ""}${D}`), T && N.push(`${H || "Custom"} ${T >= 0 ? "+" : ""}${T}`), N.push(`Pre-Aim ${F}`), o) {
const ee = h === "unaimed" ? "Quarter" : h === "partial" ? "Half" : "Full";
N.push(`Aim ${ee} x${A}`);
}
N.push(`Final ${J}`), f({
finalTarget: J,
summary: N.join(" | "),
aimMode: h,
aimMultiplier: A,
flatModifierTotal: L
});
}
},
cancel: {
label: "Cancel",
callback: () => f(null)
}
},
default: "roll",
close: () => f(null)
}, {
classes: ["ringworld-attack-dialog"],
width: 510,
resizable: !1
}).render(!0);
});
}
async function ta(e, t) {
var i, l, r, c, u, d;
const a = Math.max(0, P(t.system.energyUsedPerAttack));
if (a <= 0)
return { sourceLabel: "None", remaining: 0, cost: 0, insufficient: !1 };
if (String(t.system.powerSource || "actor") === "powerPack") {
const g = String(t.system.powerPackId || ""), m = g ? e.items.get(g) : null;
if (!m || m.type !== "gear" || !m.system.isPowerPack)
return (i = ui.notifications) == null || i.warn("Weapon is configured for a power pack but no valid power pack is linked."), { sourceLabel: "Power Pack", remaining: 0, cost: a, insufficient: !0 };
const f = P((l = m.system.energy) == null ? void 0 : l.value);
if (f < a)
return (r = ui.notifications) == null || r.warn(`${m.name} does not have enough energy.`), { sourceLabel: m.name, remaining: f, cost: a, insufficient: !0 };
const y = f - a;
return await e.updateEmbeddedDocuments("Item", [{ _id: m.id, "system.energy.value": y }]), { sourceLabel: m.name, remaining: y, cost: a, insufficient: !1 };
}
const o = P((u = (c = e.system.resources) == null ? void 0 : c.energy) == null ? void 0 : u.value);
if (o < a)
return (d = ui.notifications) == null || d.warn(`${e.name} does not have enough actor energy.`), { sourceLabel: "Actor Energy", remaining: o, cost: a, insufficient: !0 };
const s = o - a;
return await e.update({ "system.resources.energy.value": s }), { sourceLabel: "Actor Energy", remaining: s, cost: a, insufficient: !1 };
}
function aa(e, t) {
var n, o;
if (e.type !== "armor" || !e.system.equipped || e.system.stowed || !((n = e.system.coverage) != null && n[t])) return 0;
const a = P((o = e.system.perLocation) == null ? void 0 : o[t]);
return a > 0 ? a : P(e.system.value);
}
function na(e, t) {
var o, s;
const a = P((s = (o = e.system.hitLocations) == null ? void 0 : o[t]) == null ? void 0 : s.armor), n = e.items.filter((i) => i.type === "armor").reduce((i, l) => i + aa(l, t), 0);
return a + n;
}
function oa(e, t, a, n, o) {
const s = foundry.utils.deepClone(e.system.conditions ?? {});
return s.unusableLimbs = s.unusableLimbs ?? { rArm: !1, lArm: !1, rLeg: !1, lLeg: !1 }, a <= 0 && ((t === "head" || t === "chest") && (s.unconscious = !0, s.prone = !0), t === "abdomen" && (s.prone = !0, s.unusableLimbs.rLeg = !0, s.unusableLimbs.lLeg = !0), (t === "rArm" || t === "lArm") && (s.unusableLimbs[t] = !0), Nt.includes(t) && (s.unusableLimbs[t] = !0, s.prone = !0)), o <= 0 && (s.dying = !0), (t === "head" || t === "chest" || t === "abdomen") && a <= -n && (s.dying = !0), t === "head" && a <= -n && (s.dead = !0, s.unconscious = !0), s;
}
async function sa(e, t) {
const a = e.toggleStatusEffect;
if (typeof a == "function")
try {
await a.call(e, "prone", { active: !!t.prone }), await a.call(e, "unconscious", { active: !!t.unconscious }), await a.call(e, "dead", { active: !!t.dead });
} catch {
}
}
async function ia(e, t, a) {
var f, y, b, w;
const n = (f = e.system.hitLocations) == null ? void 0 : f[t], o = P((y = n == null ? void 0 : n.hp) == null ? void 0 : y.value), s = Math.max(1, P((b = n == null ? void 0 : n.hp) == null ? void 0 : b.max, 1)), i = na(e, t), l = Math.max(0, a - i), r = o - l, c = Ot(t) ? Math.max(r, -s) : r, d = P((w = e.system.generalHp) == null ? void 0 : w.value) - l, g = oa(e, t, c, s, d), m = {
[`system.hitLocations.${t}.hp.value`]: c,
"system.generalHp.value": d,
"system.conditions": g
};
return await e.update(m), await sa(e, g), {
armor: i,
finalDamage: l,
locationValue: c,
generalHpValue: d
};
}
async function dt(e, t) {
var f;
const a = Qt(), n = !!a, o = (a == null ? void 0 : a.name) ?? "No Target", s = Zt(e, t), i = await ea(e, t, s.target);
if (!i) return;
const l = await ta(e, t);
if (l.insufficient) return;
const r = await Ue(i.finalTarget);
if (!r.isSuccess) {
const y = a ? P((f = a.system.generalHp) == null ? void 0 : f.value, 0) : 0;
await Xe({
actor: e,
actorName: e.name,
actorImg: e.img,
targetName: o,
hasTarget: n,
weaponName: t.name,
weaponImg: t.img,
skillName: s.name,
skillImg: s.img,
attackRoll: r.roll,
attackTarget: r.target,
attackOutcomeLabel: Ae(r.outcome),
isSuccess: !1,
isSpecial: r.isSpecial,
isFumble: r.isFumble,
hitLocationLabel: "-",
hitLocationRoll: null,
rawDamage: 0,
armor: 0,
finalDamage: 0,
remainingLocationHp: 0,
remainingGeneralHp: y,
energySourceLabel: l.sourceLabel,
energyCost: l.cost,
energyRemaining: l.remaining,
notes: `Attack failed before damage was rolled. ${i.summary}`
});
return;
}
const c = await _t(a ?? e);
if (!c) return;
const u = String(t.system.damageFormula || "0").trim() || "0", d = await Me(u), g = Math.max(0, P(d.total)), m = a ? await ia(a, c.slug, g) : {
armor: 0,
finalDamage: g,
locationValue: 0,
generalHpValue: 0
};
await Xe({
actor: e,
actorName: e.name,
actorImg: e.img,
targetName: o,
hasTarget: n,
weaponName: t.name,
weaponImg: t.img,
skillName: s.name,
skillImg: s.img,
attackRoll: r.roll,
attackTarget: r.target,
attackOutcomeLabel: Ae(r.outcome),
isSuccess: r.isSuccess,
isSpecial: r.isSpecial,
isFumble: r.isFumble,
hitLocationLabel: c.label,
hitLocationRoll: c.rolled,
rawDamage: g,
armor: m.armor,
finalDamage: m.finalDamage,
remainingLocationHp: m.locationValue,
remainingGeneralHp: m.generalHpValue,
energySourceLabel: l.sourceLabel,
energyCost: l.cost,
energyRemaining: l.remaining,
notes: a ? `Location and general HP were both reduced by final damage, per Ringworld rules. ${i.summary}` : `No target selected: roll resolved with no damage application. ${i.summary}`
});
}
function ra(e) {
var m, f, y, b, w, p, h, A;
if (e.type !== "explorer" && e.type !== "creature") return;
const t = P((f = (m = e.system) == null ? void 0 : m.characteristics) == null ? void 0 : f.con, 10), a = P((b = (y = e.system) == null ? void 0 : y.characteristics) == null ? void 0 : b.mas, 10), n = P((w = e.system.generalHp) == null ? void 0 : w.max, 0), o = P((p = e.system.generalHp) == null ? void 0 : p.value, 0), s = Math.max(1, Math.floor(t + a));
e.system.generalHp = e.system.generalHp ?? { value: s, max: s }, e.system.generalHp.max = s;
const i = n > 0, l = i && o >= n, r = n <= 1 && o <= 1, c = i ? Math.max(0, n - o) : 0, u = !i || l || r ? s : Math.max(0, s - c);
e.system.generalHp.value = Math.min(Math.max(0, u), s);
const d = P(e.system.generalHp.value, s) >= s;
e.system.hitLocations = e.system.hitLocations ?? {}, e.system.hitLocationSchema = e.system.hitLocationSchema ?? "auto";
const g = se(e);
for (const S of g.entries) {
const I = S.slug, M = e.system.hitLocations[I] ?? {
label: I,
hp: { value: 1, max: 1 },
armor: 0
};
M.label = S.label;
const D = Math.max(0.05, P(S.hpFactor, 0.3)), T = Math.max(1, Math.ceil(s * D)), H = P((h = M.hp) == null ? void 0 : h.max, 1), L = P((A = M.hp) == null ? void 0 : A.value, H), F = H <= 1 && L <= 1, X = d && L <= 1 && T > 1;
M.hp.max = T, M.hp.value = F || X ? T : Math.min(L, T), e.system.hitLocations[I] = M;
}
}
const De = 9, _ = {
speciesDefault: "systems/ringworld/assets/icons/species.svg",
skillDefault: "systems/ringworld/assets/icons/skill.svg",
pursuitDefault: "systems/ringworld/assets/icons/pursuit.svg",
weaponMelee: "systems/ringworld/assets/icons/weapon-melee.svg",
weaponRanged: "systems/ringworld/assets/icons/weapon-ranged.svg",
armor: "systems/ringworld/assets/icons/armor.svg",
gearDefault: "systems/ringworld/assets/icons/gear.svg",
powerPackDefault: "systems/ringworld/assets/icons/power-pack.svg"
};
function be(e) {
return String(e).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
}
function la(e) {
return `systems/ringworld/assets/icons/species/${be(e)}.svg`;
}
function ca(e) {
return `systems/ringworld/assets/icons/weapon/${be(e)}.svg`;
}
function ua(e) {
return `systems/ringworld/assets/icons/armor/${be(e)}.svg`;
}
function ma(e) {
return `systems/ringworld/assets/icons/gear/${be(e)}.svg`;
}
function gt(e) {
return `systems/ringworld/assets/icons/pursuit/${be(e)}.svg`;
}
const Ze = {
rLeg: !0,
lLeg: !0,
abdomen: !0,
chest: !0,
rArm: !0,
lArm: !0,
head: !0
}, Se = {
rLeg: !1,
lLeg: !1,
abdomen: !0,
chest: !0,
rArm: !1,
lArm: !1,
head: !1
}, da = {
rLeg: !1,
lLeg: !1,
abdomen: !1,
chest: !1,
rArm: !1,
lArm: !1,
head: !0
}, ga = {
rLeg: !1,
lLeg: !1,
abdomen: !1,
chest: !1,
rArm: !0,
lArm: !0,
head: !1
}, pa = {
rLeg: !0,
lLeg: !0,
abdomen: !1,
chest: !1,
rArm: !1,
lArm: !1,
head: !1
}, pt = [
{
name: "Human",
playable: !0,
gravity: "normal",
homeworld: "Known Space",
modifiers: { str: 0, mas: 0, con: 0, int: 0, pow: 0, dex: 0, app: 0, edu: 0, age: 0 },
notes: "Baseline human explorer profile."
},
{
name: "Earth Human",
playable: !0,
gravity: "normal",
homeworld: "Earth",
modifiers: { str: 0, mas: 0, con: 0, int: 0, pow: 1, dex: 0, app: 0, edu: 0, age: 0 },
notes: "Earth-line humans with stronger latent psionic potential."
},
{
name: "Kzinti",
playable: !0,
gravity: "heavy",
homeworld: "Kzin",
modifiers: { str: 2, mas: 2, con: 1, int: -1, pow: 0, dex: 0, app: -2, edu: 0, age: 0 },
notes: "Predatory felinoids with high physical strength."
},
{
name: "Pierson's Puppeteer",
playable: !0,
gravity: "light",
homeworld: "Fleet of Worlds",
modifiers: { str: -2, mas: -2, con: -1, int: 2, pow: 1, dex: 1, app: 0, edu: 1, age: 0 },
notes: "Highly intelligent, cautious engineers."
},
{
name: "Dolphin",
playable: !0,
gravity: "normal",
homeworld: "Human Space Oceans",
modifiers: { str: -2, mas: 1, con: 1, int: 0, pow: 0, dex: 0, app: 0, edu: 0, age: 0 },
notes: "Aquatic specialists with enhanced endurance."
},
{
name: "Trinoc",
playable: !0,
gravity: "light",
homeworld: "Trinoc Worlds",
modifiers: { str: -1, mas: -1, con: 0, int: 1, pow: 0, dex: 1, app: 0, edu: 0, age: 0 },
notes: "Three-eyed flyers with excellent perception."
},
{
name: "Martian",
playable: !0,
gravity: "light",
homeworld: "Mars",
modifiers: { str: -1, mas: -1, con: -1, int: 1, pow: 0, dex: 1, app: 0, edu: 0, age: 0 },
notes: "Light-gravity adapted intellect-focused profile."
},
{
name: "Pierin",
playable: !0,
gravity: "normal",
homeworld: "Pierin Space",
modifiers: { str: 0, mas: 0, con: 0, int: 1, pow: 0, dex: 0, app: 1, edu: 0, age: 0 },
notes: "Socially adept, technically curious explorers."
},
{
name: "Boat People",
playable: !0,
gravity: "normal",
homeworld: "Ringworld",
modifiers: { str: 0, mas: 0, con: 1, int: 0, pow: 0, dex: 0, app: 0, edu: -1, age: 0 },
notes: "Ringworld hominids adapted to river and sea cultures."
},
{
name: "Builder",
playable: !0,
gravity: "normal",
homeworld: "Ringworld",
modifiers: { str: 1, mas: 1, con: 0, int: 0, pow: 0, dex: 0, app: -1, edu: 0, age: 0 },
notes: "Ringworld labor stock with robust build."
},
{
name: "Ghoul",
playable: !0,
gravity: "normal",
homeworld: "Ringworld",
modifiers: { str: 0, mas: 0, con: 1, int: -1, pow: 0, dex: 1, app: -2, edu: -1, age: 0 },
notes: "Scavenger subspecies with strong survival instincts."
},
{
name: "Bandersnatchi",
playable: !1,
gravity: "heavy",
homeworld: "Known Space",
modifiers: { str: 4, mas: 4, con: 3, int: 0, pow: 0, dex: -2, app: -3, edu: 0, age: 10 },
notes: "Massive engineered beings; typically GM creature profile."
}
], fa = [
{
name: "Variable Sword",
equipped: !0,
weight: 1.5,
linkedSkill: "V. Sword, F. Laser",
attackType: "melee",
damageFormula: "1d10+1",
range: { close: "Melee", medium: "-", long: "-" },
energyUsedPerAttack: 1,
ammoUsedPerAttack: 0,
powerSource: "powerPack",
notes: "High-energy blade weapon."
},
{
name: "Monofilament Knife",
equipped: !0,
weight: 0.2,
linkedSkill: "Archaic Melee Weapon",
attackType: "melee",
damageFormula: "1d6+1",
range: { close: "Melee", medium: "-", long: "-" },
energyUsedPerAttack: 0,
ammoUsedPerAttack: 0,
powerSource: "actor",
notes: "Fine-edge close combat blade."
},
{
name: "Shock Baton",
equipped: !0,
weight: 1.2,
linkedSkill: "Unarmed Combat",
attackType: "melee",
damageFormula: "1d8+1",
range: { close: "Melee", medium: "-", long: "-" },
energyUsedPerAttack: 1,
ammoUsedPerAttack: 0,
powerSource: "powerPack",
notes: "Close-quarters stun baton."
},
{
name: "Flashlight Laser",
equipped: !0,
weight: 0.8,
linkedSkill: "V. Sword, F. Laser",
attackType: "ranged",
damageFormula: "1d10",
range: { close: "Short", medium: "Medium", long: "Long" },
energyUsedPerAttack: 2,
ammoUsedPerAttack: 0,
powerSource: "powerPack",
notes: "Compact laser unit."
},
{
name: "Laser Pistol",
equipped: !0,
weight: 1.4,
linkedSkill: "Handgun, Energy",
attackType: "ranged",
damageFormula: "1d10+1",
range: { close: "Short", medium: "Medium", long: "Long" },
energyUsedPerAttack: 2,
ammoUsedPerAttack: 0,
powerSource: "powerPack",
notes: "Common energy sidearm."
},
{
name: "Laser Rifle",
equipped: !0,
weight: 4.5,
linkedSkill: "Heavy Weapon, Energy",
attackType: "ranged",
damageFormula: "2d6+2",
range: { close: "Short", medium: "Long", long: "Extreme" },
energyUsedPerAttack: 3,
ammoUsedPerAttack: 0,
powerSource: "powerPack",
notes: "Standard long-arm laser weapon."
},
{
name: "Stunner",
equipped: !0,
weight: 1,
linkedSkill: "Handgun, Energy",
attackType: "ranged",
damageFormula: "1d6",
range: { close: "Short", medium: "Medium", long: "Long" },
energyUsedPerAttack: 1,
ammoUsedPerAttack: 0,
powerSource: "powerPack",
notes: "Low-lethality disruption sidearm."
},
{
name: "Handgun (Projectile)",
equipped: !0,
weight: 1.2,
linkedSkill: "Handgun, Projectile",
attackType: "ranged",
damageFormula: "1d10",
range: { close: "Short", medium: "Medium", long: "Long" },
energyUsedPerAttack: 0,
ammoUsedPerAttack: 1,
powerSource: "actor",
notes: "Conventional projectile sidearm."
},
{
name: "Rifle (Projectile)",
equipped: !0,
weight: 5,
linkedSkill: "Heavy Weapon, Projectile",
attackType: "ranged",
damageFormula: "2d6",
range: { close: "Short", medium: "Long", long: "Extreme" },
energyUsedPerAttack: 0,
ammoUsedPerAttack: 1,
powerSource: "actor",
notes: "Conventional projectile rifle."
},
{
name: "Slug Thrower Carbine",
equipped: !0,
weight: 3.8,
linkedSkill: "Heavy Weapon, Projectile",
attackType: "ranged",
damageFormula: "2d6+1",
range: { close: "Short", medium: "Long", long: "Extreme" },
energyUsedPerAttack: 0,
ammoUsedPerAttack: 1,
powerSource: "actor",
notes: "Compact projectile carbine."
}
], ya = [
{ name: "Cloth Vest", equipped: !0, weight: 2, value: 1, coverage: Se, notes: "Basic torso protection." },
{ name: "Soft Combat Vest", equipped: !0, weight: 4, value: 2, coverage: Se, notes: "Flexible tactical vest." },
{ name: "Combat Harness", equipped: !0, weight: 5, value: 2, coverage: { ...Se, rArm: !0, lArm: !0 }, notes: "Torso + upper arms." },
{ name: "Armored Coat", equipped: !0, weight: 7, value: 3, coverage: { ...Se, rArm: !0, lArm: !0 }, notes: "Heavy coat with arm plating." },
{ name: "Leg Guards", equipped: !0, weight: 4, value: 2, coverage: pa, notes: "Lower-body armor." },
{ name: "Arm Bracers", equipped: !0, weight: 2, value: 2, coverage: ga, notes: "Forearm and upper arm guards." },
{ name: "Combat Helmet", equipped: !0, weight: 1.5, value: 2, coverage: da, notes: "Cranial protection." },
{ name: "Full Combat Suit", equipped: !0, weight: 12, value: 3, coverage: Ze, notes: "Full-body armored suit." },
{ name: "Impact Suit", equipped: !0, weight: 16, value: 4, coverage: Ze, notes: "High-end impact and hazard suit." }
], ha = [
{
name: "Power Pack R20",
weight: 1,
cost: 100,
energyUse: 0,
powerSupplyNotes: "Rechargeable power pack",
isPowerPack: !0,
energy: { value: 20, max: 20 },
notes: "Standard personal battery pack."
},
{
name: "Power Pack R50",
weight: 2,
cost: 220,
energyUse: 0,
powerSupplyNotes: "Rechargeable power pack",
isPowerPack: !0,
energy: { value: 50, max: 50 },
notes: "Extended personal battery pack."
},
{
name: "Power Pack R100",
weight: 4,
cost: 400,
energyUse: 0,
powerSupplyNotes: "Rechargeable power pack",
isPowerPack: !0,
energy: { value: 100, max: 100 },
notes: "High-capacity field battery."
},
{ name: "Portable Generator", weight: 8, cost: 700, energyUse: 0, powerSupplyNotes: "Provides charging capacity", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Portable recharge generator." },
{ name: "MicroMind Unit", weight: 0.5, cost: 900, energyUse: 1, powerSupplyNotes: "Requires external power", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Portable advanced computation module." },
{ name: "Memory Bubble Reader", weight: 0.5, cost: 450, energyUse: 1, powerSupplyNotes: "Standard battery compatible", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Reads and writes memory bubbles." },
{ name: "Autodoc (Portable)", weight: 18, cost: 3500, energyUse: 6, powerSupplyNotes: "High draw medical station", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Field medical support unit." },
{ name: "Medkit", weight: 1, cost: 120, energyUse: 0, powerSupplyNotes: "None", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Manual emergency treatment supplies." },
{ name: "Bioscanner", weight: 1, cost: 300, energyUse: 1, powerSupplyNotes: "Battery or generator", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Vitals and hazard scanning tool." },
{ name: "Survey Scanner", weight: 2, cost: 500, energyUse: 2, powerSupplyNotes: "Battery or generator", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Terrain and structure analysis scanner." },
{ name: "Translator Module", weight: 0.5, cost: 650, energyUse: 1, powerSupplyNotes: "Battery compatible", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Linguistic conversion and support." },
{ name: "Holo Projector", weight: 1.5, cost: 800, energyUse: 2, powerSupplyNotes: "Battery compatible", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Short-range holographic projection." },
{ name: "Stasis Box", weight: 12, cost: 2600, energyUse: 5, powerSupplyNotes: "Continuous power required", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Suspended animation container." },
{ name: "Reactionless Flycycle", weight: 35, cost: 4200, energyUse: 4, powerSupplyNotes: "Vehicle power core", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Single-rider reactionless vehicle." },
{ name: "Field Toolkit", weight: 2, cost: 180, energyUse: 0, powerSupplyNotes: "None", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Mechanical and electronics repair kit." },
{ name: "Vac Suit", weight: 7, cost: 1400, energyUse: 1, powerSupplyNotes: "Suit battery required", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Pressure and vacuum survival suit." },
{ name: "Air Reprocessor", weight: 3, cost: 950, energyUse: 2, powerSupplyNotes: "Continuous power", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Atmosphere recycling support unit." },
{ name: "Signal Beacon", weight: 1, cost: 140, energyUse: 1, powerSupplyNotes: "Battery compatible", isPowerPack: !1, energy: { value: 0, max: 0 }, notes: "Emergency and navigation beacon." }
], he = [
{
name: "Artist",
rollRange: "01-07",
wealthValue: 0,
occupationPointsPerYear: 20,
fixedSkills: ["Athletics", "Computers", "Fast Talk", "Fine Arts", "Hide", "Musicianship", "Observe", "Perform", "Psychology", "Scent", "Theology"],
notes: "Creative specialist pursuit."
},
{
name: "Colonist",
rollRange: "08-27",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: ["Atmospheric Craft", "Bargain", "Ground Vehicle", "Farming", "Handgun, Energy", "Personal Flyer", "Listen", "Heavy Weapon, Energy", "Observe", "Planetology", "Search", "Track"],
notes: "Settlement and frontier operations pursuit."
},
{
name: "Criminal",
rollRange: "28-29",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: ["Bargain", "Computers", "Fast Talk", "Hide", "Personal Flyer", "Handgun, Energy", "Handgun, Projectile", "Sneak", "Unarmed Combat"],
notes: "Underground survival pursuit."
},
{
name: "Dilettante",
rollRange: "30",
wealthValue: 5,
occupationPointsPerYear: 15,
fixedSkills: [],
notes: "Any skills allowed; uses 15 occupation points per year."
},
{
name: "Engineer",
rollRange: "31-36",
wealthValue: 2,
occupationPointsPerYear: 20,
fixedSkills: ["Astronomy", "Chemistry", "Computers", "Engineering", "Ground Vehicle", "Mathematics", "Personal Flyer", "Physics", "Planetology", "Reaction Drive", "Reactionless Drive", "Repair"],
notes: "Technical design and maintenance pursuit."
},
{
name: "Entertainer",
rollRange: "37-38",
wealthValue: 2,
occupationPointsPerYear: 20,
fixedSkills: ["Athletics", "Bargain", "Own Language", "Musicianship", "Observe", "Orate", "Perform", "Psychology", "Search", "Sneak"],
notes: "Performance and social influence pursuit."
},
{
name: "Explorer",
rollRange: "39-52",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: ["Athletics", "Atmospheric Craft", "Bargain", "Biology", "Botany", "Emergency Treatment", "Engineering", "Ground Vehicle", "Handgun, Energy", "Heavy Weapon, Energy", "Hyperdrive", "Observe", "Planetology", "Reactionless Drive", "Repair", "Search", "Track", "Zoology"],
notes: "Survey and expedition pursuit."
},
{
name: "Functionary",
rollRange: "53-56",
wealthValue: 3,
occupationPointsPerYear: 20,
fixedSkills: ["Bargain", "Debate", "Fast Talk", "History", "Law", "Observe", "Orate", "Psychology", "Strategy", "Theology"],
notes: "Administrative and governance pursuit."
},
{
name: "Journalist/Writer",
rollRange: "57-59",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: ["Anthropology", "Bargain", "Computers", "Debate", "History", "Fine Arts", "Own Language", "Law", "Observe", "Psychology", "Search", "Sneak", "Unarmed Combat", "Weapons System"],
notes: "Investigation and reporting pursuit."
},
{
name: "Miner",
rollRange: "60-65",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: ["Atmospheric Craft", "Aquatic Vehicle", "Bargain", "Climb (Custom)", "Computers", "Emergency Treatment", "Engineering", "Handgun, Energy", "Heavy Weapon, Energy", "Observe", "Personal Flyer", "Planetology", "Reaction Drive", "Reactionless Drive", "Repair", "Search"],
notes: "Resource extraction frontier pursuit."
},
{
name: "Pilot",
rollRange: "66-68",
wealthValue: 3,
occupationPointsPerYear: 20,
fixedSkills: ["Astronomy", "Atmospheric Craft", "Computers", "Engineering", "Hyperdrive", "History", "Law", "Mathematics", "Physics", "Reaction Drive", "Reactionless Drive", "Repair", "Strategy", "Weapons System"],
notes: "Flight and spacecraft operations pursuit."
},
{
name: "Police Officer",
rollRange: "69-76",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: ["Atmospheric Craft", "Bargain", "Computers", "Fast Talk", "Emergency Treatment", "Handgun, Energy", "Handgun, Projectile", "Heavy Weapon, Energy", "Heavy Weapon, Projectile", "Law", "Observe", "Personal Flyer", "Psychology", "Search", "Unarmed Combat"],
notes: "Law enforcement and response pursuit."
},
{
name: "Politician",
rollRange: "77-78",
wealthValue: 3,
occupationPointsPerYear: 20,
fixedSkills: ["Bargain", "Computers", "Debate", "Fast Talk", "History", "Orate", "Own Language", "Perform", "Psychology"],
optionalSkillGroups: [
{
choose: 1,
skills: ["Engineering", "Farming", "Second Languages", "Strategy", "Theology"]
}
],
notes: "Public office and influence pursuit."
},
{
name: "Prisoner",
rollRange: "79-80",
wealthValue: 0,
occupationPointsPerYear: 20,
fixedSkills: ["Bargain", "Fast Talk", "Hide", "Observe", "Psychology", "Search", "Sneak", "Unarmed Combat"],
notes: "Carceral survival pursuit."
},
{
name: "Scholar",
rollRange: "81-84",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: [],
optionalSkillGroups: [
{
choose: 2,
skills: ["Anthropology", "Astronomy", "Biology", "Botany", "Chemistry", "Computers", "Engineering", "Fine Arts", "History", "Law", "Mathematics", "Own Language", "Physics", "Planetology", "Second Languages", "Theology", "Zoology"]
}
],
notes: "Academic pursuit; pick up to two listed skills."
},
{
name: "Scientific Researcher",
rollRange: "85-94",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: [],
optionalSkillGroups: [
{
choose: 3,
skills: ["Anthropology", "Astronomy", "Bargain", "Biology", "Botany", "Chemistry", "Computers", "Engineering", "History", "Mathematics", "Observe", "Personal Flyer", "Physics", "Planetology", "Psychology", "Repair", "Search", "Zoology"]
}
],
notes: "Research pursuit; pick up to three listed skills."
},
{
name: "Soldier",
rollRange: "95-98",
wealthValue: 1,
occupationPointsPerYear: 20,
fixedSkills: ["Athletics", "Atmospheric Craft", "Aquatic Vehicle", "Computers", "Emergency Treatment", "Fast Talk", "Ground Vehicle", "Handgun, Energy", "Handgun, Projectile", "Heavy Weapon, Energy", "Heavy Weapon, Projectile"],
notes: "Military operations pursuit."
},
{
name: "Zealot",
rollRange: "99-00",
wealthValue: 0,
occupationPointsPerYear: 20,
fixedSkills: ["Bargain", "Debate", "Fast Talk", "Fine Arts", "Handgun, Energy", "Handgun, Projectile", "Hide", "Own Language", "Law", "Strategy", "Orate", "Psychology", "Theology"],
notes: "Ideological dedication pursuit."
}
];
function ft() {
return pt.filter((e) => e.playable);
}
function Q(e, t) {
return `${e}:${R(t)}`;
}
function wa(e) {
var s, i, l, r, c;
const t = (s = getDocumentClass("Item")) == null ? void 0 : s.TYPES;
if (Array.isArray(t)) return t.includes(e);
const a = (l = (i = game == null ? void 0 : game.system) == null ? void 0 : i.documentTypes) == null ? void 0 : l.Item;
if (Array.isArray(a)) return a.includes(e);
if (a && typeof a == "object") return Object.prototype.hasOwnProperty.call(a, e);
const n = (r = CONFIG == null ? void 0 : CONFIG.Item) == null ? void 0 : r.dataModels;
if (n && typeof n == "object" && Object.prototype.hasOwnProperty.call(n, e))
return !0;
const o = (c = game == null ? void 0 : game.model) == null ? void 0 : c.Item;
return !!(o && typeof o == "object" && Object.prototype.hasOwnProperty.call(o, e));
}
function ba(e) {
return {
key: Q("skill", e.name),
folderKey: "skills",
img: ye(e.name) || _.skillDefault,
data: {
name: e.name,
type: "skill",
system: {
value: e.base,
category: e.category,
specialization: e.specialization ?? ""
}
}
};
}
function ka(e) {
const t = la(e.name) || _.speciesDefault;
return {
key: Q("species", e.name),
folderKey: "species",
img: t,
data: {
name: e.name,
type: "species",
system: {
playable: e.playable,
gravity: e.gravity,
homeworld: e.homeworld,
modifiers: e.modifiers,
notes: e.notes
}
}
};
}
function va(e) {
const t = e.attackType === "melee" ? "weaponMelee" : "weaponRanged", a = ca(e.name) || (e.attackType === "melee" ? _.weaponMelee : _.weaponRanged);
return {
key: Q("weapon", e.name),
folderKey: t,
img: a,
data: {
name: e.name,
type: "weapon",
system: {
equipped: e.equipped ?? !0,
stowed: !1,
weight: e.weight ?? 0,
linkedSkill: e.linkedSkill,
linkedSkillId: "",
attackType: e.attackType,
damageFormula: e.damageFormula,
range: e.range,
energyUsedPerAttack: e.energyUsedPerAttack,
ammoUsedPerAttack: e.ammoUsedPerAttack,
powerSource: e.powerSource,
powerPackId: "",
notes: e.notes
}
}
};
}
function Sa(e) {
const t = ua(e.name) || _.armor;
return {
key: Q("armor", e.name),
folderKey: "armor",
img: t,
data: {
name: e.name,
type: "armor",
system: {
equipped: e.equipped,
stowed: !1,
weight: e.weight ?? 0,
value: e.value,
coverage: e.coverage,
perLocation: {
rLeg: 0,
lLeg: 0,
abdomen: 0,
chest: 0,
rArm: 0,
lArm: 0,
head: 0
},
notes: e.notes
}
}
};
}
function Aa(e) {
const t = ma(e.name) || (e.isPowerPack ? _.powerPackDefault : _.gearDefault);
return {
key: Q("gear", e.name),
folderKey: "gear",
img: t,
data: {
name: e.name,
type: "gear",
system: {
equipped: e.equipped ?? !0,
stowed: !1,
weight: e.weight,
cost: e.cost,
energyUse: e.energyUse,
powerSupplyNotes: e.powerSupplyNotes,
isPowerPack: e.isPowerPack,
energy: e.energy,
notes: e.notes
}
}
};
}
function xa(e) {
const t = gt(e.name) || _.pursuitDefault;
return {
key: Q("pursuit", e.name),
folderKey: "pursuits",
img: t,
data: {
name: e.name,
type: "pursuit",
system: {
wealthValue: e.wealthValue,
occupationPointsPerYear: e.occupationPointsPerYear,
rollRange: e.rollRange,
fixedSkills: e.fixedSkills,
optionalSkillGroups: e.optionalSkillGroups ?? [],
notes: e.notes
}
}
};
}
function Pa(e) {
const t = e.fixedSkills.length ? `Fixed skills: ${e.fixedSkills.join(", ")}` : "Fixed skills: (none)", a = (e.optionalSkillGroups ?? []).map((o) => `Choose ${o.choose}: ${o.skills.join(", ")}`).join(" | "), n = a ? ` Optional: ${a}.` : "";
return `${e.notes} ${t}.${n} Wealth ${e.wealthValue}. Occupation points/year ${e.occupationPointsPerYear}.`;
}
function Ia(e) {
const t = gt(e.name) || _.pursuitDefault, a = `Pursuit: ${e.name}`;
return {
key: Q("gear", a),
folderKey: "pursuits",
img: t,
data: {
name: a,
type: "gear",
system: {
equipped: !1,
stowed: !0,
weight: 0,
cost: 0,
energyUse: 0,
powerSupplyNotes: "Pursuit reference",
isPowerPack: !1,
energy: { value: 0, max: 0 },
notes: Pa(e)
},
flags: {
ringworld: {
pursuitData: {
pursuitName: e.name,
rollRange: e.rollRange,
wealthValue: e.wealthValue,
occupationPointsPerYear: e.occupationPointsPerYear,
fixedSkills: e.fixedSkills,
optionalSkillGroups: e.optionalSkillGroups ?? []
}
}
}
}
};
}
async function G(e, t) {
const a = game.folders.find(
(n) => {
var o;
return n.type === "Item" && n.name === e && (((o = n.folder) == null ? void 0 : o.id) ?? null) === t;
}
);
return a || Folder.create({
name: e,
type: "Item",
folder: t,
sorting: "a"
});
}
async function La() {
const e = await G("Ringworld Catalog", null), t = await G("Species", e.id), a = await G("Skills", e.id), n = await G("Pursuits", e.id), o = await G("Gear", e.id), s = await G("Armor", o.id), i = await G("Weapons", e.id), l = await G("Melee Weapons", i.id), r = await G("Ranged Weapons", i.id);
return {
species: t.id,
skills: a.id,
pursuits: n.id,
gear: o.id,
armor: s.id,
weaponMelee: l.id,
weaponRanged: r.id
};
}
function Ma() {
const e = wa("pursuit");
return [
...pt.map(ka),
...we.map(ba),
...e ? he.map(xa) : he.map(Ia),
...fa.map(va),
...ya.map(Sa),
...ha.map(Aa)
];
}
async function Be(e) {
var i, l, r, c, u, d, g;
if (!((i = game.user) != null && i.isGM)) return { added: 0, updated: 0 };
const t = await La(), a = Ma(), n = new Map(
game.items.map((m) => [Q(m.type, m.name), m])
), o = [], s = [];
for (const m of a) {
const f = t[m.folderKey], y = n.get(m.key);
if (!y) {
o.push({
...m.data,
img: m.img,
folder: f,
flags: {
ringworld: {
catalog: !0,
catalogKey: m.key
}
}
});
continue;
}
const b = { _id: y.id };
(((l = y.folder) == null ? void 0 : l.id) ?? null) !== f && (b.folder = f), String(y.img || "") !== m.img && (b.img = m.img), ((c = (r = y.flags) == null ? void 0 : r.ringworld) == null ? void 0 : c.catalog) !== !0 && (b["flags.ringworld.catalog"] = !0), ((d = (u = y.flags) == null ? void 0 : u.ringworld) == null ? void 0 : d.catalogKey) !== m.key && (b["flags.ringworld.catalogKey"] = m.key);
const w = foundry.utils.deepClone(y.system ?? {}), p = foundry.utils.mergeObject(w, m.data.system ?? {}, {
inplace: !1,
insertKeys: !0,
insertValues: !0,
overwrite: !1
}), h = foundry.utils.diffObject(w, p);
Object.keys(h).length > 0 && (b.system = p), Object.keys(b).length > 1 && s.push(b);
}
return o.length && await Item.createDocuments(o), s.length && await Item.updateDocuments(s), e != null && e.notify && ((g = ui.notifications) == null || g.info(`Ringworld catalog synced: +${o.length} new, ${s.length} updated.`)), { added: o.length, updated: s.length };
}
function yt() {
return game.items.filter((e) => e.type === "species" && e.system.playable);
}
function K(e) {
return Math.max(1, Math.min(99, Math.floor(e)));
}
async function j(e) {
const t = await Me(e);
return Number(t.total ?? 0);
}
async function Ca() {
let e = 10;
for (; ; ) {
const t = await j("1d6");
if (e += t, t <= 4) break;
}
return K(e);
}
async function $a(e) {
const t = {
light: { str: "2d6+3", mas: "2d6+3", con: "2d6+6", dex: "2d6+9" },
normal: { str: "2d6+6", mas: "2d6+6", con: "2d6+6", dex: "2d6+6" },
heavy: { str: "2d6+9", mas: "2d6+9", con: "2d6+3", dex: "2d6+6" }
}[e];
return {
str: await j(t.str),
mas: await j(t.mas),
con: await j(t.con),
int: await j("2d6+6"),
pow: await j("2d6+6"),
dex: await j(t.dex),
app: await j("2d6+6"),
edu: await Ca(),
age: await j("1d20+16")
};
}
function xe(e) {
return e[Math.floor(Math.random() * e.length)];
}
function Ra(e) {
const t = ["Ar", "Bel", "Cor", "Den", "Ela", "Far", "Gal", "Hel", "Ira", "Jun", "Kor", "Lys", "Mor", "Nyr", "Or", "Pra", "Qua", "Rin", "Sol", "Tor", "Uri", "Val", "Wen", "Xel", "Yor", "Zen"], a = ["an", "el", "is", "or", "us", "a", "en", "ion", "eth", "al", "ir", "os"];
return `${xe(t)}${xe(a)} (${e})`;
}
function Ta(e, t) {
const a = (t == null ? void 0 : t.modifiers) ?? {};
return {
str: K(e.str + Number(a.str || 0)),
mas: K(e.mas + Number(a.mas || 0)),
con: K(e.con + Number(a.con || 0)),
int: K(e.int + Number(a.int || 0)),
pow: K(e.pow + Number(a.pow || 0)),
dex: K(e.dex + Number(a.dex || 0)),
app: K(e.app + Number(a.app || 0)),
edu: K(e.edu + Number(a.edu || 0)),
age: K(e.age + Number(a.age || 0))
};
}
async function ht(e) {
var d, g, m, f;
const t = String(((g = (d = e.system) == null ? void 0 : d.profile) == null ? void 0 : g.gravity) || "normal"), a = t === "light" || t === "heavy" ? t : "normal", n = String(((f = (m = e.system) == null ? void 0 : m.profile) == null ? void 0 : f.species) || ""), s = yt().find((y) => y.name === n), i = ft().find((y) => y.name === n), l = (s == null ? void 0 : s.system) ?? i ?? null, r = await $a(a), c = Ta(r, l);
await e.update({
"system.characteristics.str": c.str,
"system.characteristics.mas": c.mas,
"system.characteristics.con": c.con,
"system.characteristics.int": c.int,
"system.characteristics.pow": c.pow,
"system.characteristics.dex": c.dex,
"system.characteristics.app": c.app,
"system.characteristics.edu": c.edu,
"system.characteristics.age": c.age,
"system.profile.chronologicalAge": c.age,
"system.profile.physiologicalAge": c.age
});
const u = e.items.find((y) => y.type === "skill" && y.name.toLowerCase() === "own language");
u && await e.updateEmbeddedDocuments("Item", [{
_id: u.id,
"system.value": Math.min(99, c.int * 5)
}]);
}
async function Da(e) {
const t = [
["weapon", "Laser Pistol"],
["weapon", "Variable Sword"],
["armor", "Soft Combat Vest"],
["gear", "Power Pack R20"],
["gear", "Medkit"],
["gear", "Survey Scanner"]
], a = game.items, n = new Set(e.items.map((s) => `${s.type}:${s.name.toLowerCase()}`)), o = [];
for (const [s, i] of t) {
if (n.has(`${s}:${String(i).toLowerCase()}`)) continue;
const l = a.find((r) => r.type === s && r.name === i);
l && o.push(l.toObject());
}
o.length && await e.createEmbeddedDocuments("Item", o);
}
async function wt(e) {
var c;
const t = yt(), a = ft(), n = t.length ? xe(t) : null, o = n ? null : xe(a), s = (n == null ? void 0 : n.system) ?? o, i = (n == null ? void 0 : n.name) ?? (o == null ? void 0 : o.name) ?? "Human", l = String(s.gravity || "normal"), r = String(s.homeworld || "Ringworld");
await e.update({
name: Ra(i),
"system.profile.species": i,
"system.profile.gravity": l,
"system.profile.homeworld": r
}), await ht(e), await ((c = e.ensureBaseSkills) == null ? void 0 : c.call(e)), await Da(e);
}
async function bt() {
const t = await getDocumentClass("Actor").create({
name: "Random Explorer",
type: "explorer"
});
return await wt(t), t;
}
const Ha = ["str", "mas", "con", "int", "pow", "dex", "app", "edu", "age"];
function v(e, t = 0) {
const a = Number(e);
return Number.isFinite(a) ? a : t;
}
function Na(e) {
return e != null && String(e).trim() !== "";
}
function He(e) {
return Math.max(1, Math.min(99, Math.floor(e)));
}
function Oa(e, t) {
const a = e + t;
return a <= 12 ? "-1d6" : a <= 24 ? "-1d4" : a <= 32 ? "0" : a <= 40 ? "+1d4" : a <= 56 ? "+1d6" : a <= 72 ? "+2d6" : "+3d6";
}
function Je(e) {
const t = v(e == null ? void 0 : e.con, 10), a = v(e == null ? void 0 : e.int, 10), n = v(e == null ? void 0 : e.pow, 10), o = v(e == null ? void 0 : e.dex, 10);
return {
health: Math.max(0, Math.min(99, t * 5)),
reasoning: Math.max(0, Math.min(99, a * 5)),
luck: Math.max(0, Math.min(99, n * 5)),
dodge: Math.max(0, Math.min(99, o * 5))
};
}
function Ea(e) {
const t = v(e == null ? void 0 : e.str, 10), a = v(e == null ? void 0 : e.dex, 10), n = v(e == null ? void 0 : e.app, 10), o = v(e == null ? void 0 : e.edu, 10), s = v(e == null ? void 0 : e.int, 10), i = v(e == null ? void 0 : e.pow, 10), l = v(e == null ? void 0 : e.con, 10);
return {
agility: Math.max(0, t + a),
communication: Math.max(0, s + n),
knowledge: Math.max(0, s + o),
perception: Math.max(0, i + l),
technical: Math.max(0, a + s)
};
}
function Ba(e) {
return e <= 4 ? 7 : e <= 8 ? 6 : e <= 12 ? 5 : e <= 16 ? 4 : e <= 20 ? 3 : e <= 24 ? 2 : 1;
}
function Fa(e) {
return e === "light" ? 0.5 : e === "heavy" ? 2 : 1;
}
function Ua(e) {
var t;
return e.type !== "weapon" && e.type !== "armor" && e.type !== "gear" ? !1 : ((t = e.system) == null ? void 0 : t.stowed) !== !0;
}
function qa(e) {
var t;
return Math.max(0, v((t = e.system) == null ? void 0 : t.weight, 0));
}
function Ka(e, t) {
var y, b, w, p;
const a = String(((y = e == null ? void 0 : e.profile) == null ? void 0 : y.gravity) || "normal"), n = Fa(a), o = t.filter((h) => h.type === "weapon" || h.type === "armor" || h.type === "gear").filter(Ua).reduce((h, A) => h + qa(A), 0), s = Math.max(0, o), i = s * n, l = Math.max(0, v((b = e == null ? void 0 : e.characteristics) == null ? void 0 : b.str, 0)), r = Math.max(0, i - l), c = r > 0 ? Math.ceil(r / 10) : 0, u = r > 0, d = Math.max(1, v((w = e == null ? void 0 : e.derived) == null ? void 0 : w.move, 3)), g = Math.max(1, v((p = e == null ? void 0 : e.derived) == null ? void 0 : p.run, d * 2));
return {
rawLoadKg: s,
effectiveLoadKg: i,
limitKg: l,
overLimitKg: r,
actionRankingPenalty: c,
overLimit: u,
currentMove: u ? 2 : d,
currentRun: u ? 4 : g,
gravityMultiplier: n
};
}
class Va extends Actor {
prepareDerivedData() {
if (super.prepareDerivedData(), ra(this), this.type !== "explorer" && this.type !== "creature") return;
const t = this.system ?? {}, a = t.characteristics ?? {}, n = t.profile ?? {};
n.chronologicalAge = v(n.chronologicalAge, v(a.age, 25)), n.physiologicalAge = v(n.physiologicalAge, n.chronologicalAge), t.profile = n, t.combat = t.combat ?? {}, Na(t.combat.damageMod) || (t.combat.damageMod = Oa(v(a.str, 10), v(a.mas, 10))), t.derivedRolls = t.derivedRolls ?? {};
const o = Je(a);
for (const l of Object.keys(o))
t.derivedRolls[l] = v(t.derivedRolls[l], o[l]);
t.skillRootMax = t.skillRootMax ?? {};
const s = Ea(a);
for (const [l, r] of Object.entries(s))
t.skillRootMax[l] = r;
t.derived = t.derived ?? {}, t.derived.actionRanking = Ba(v(a.dex, 10)), t.derived.move = v(t.derived.move, 3), t.derived.run = v(t.derived.run, Math.max(1, v(t.derived.move, 3) * 2)), t.encumbrance = t.encumbrance ?? {};
const i = Ka(t, this.items);
t.encumbrance.rawLoadKg = i.rawLoadKg, t.encumbrance.effectiveLoadKg = i.effectiveLoadKg, t.encumbrance.limitKg = i.limitKg, t.encumbrance.overLimitKg = i.overLimitKg, t.encumbrance.actionRankingPenalty = i.actionRankingPenalty, t.encumbrance.overLimit = i.overLimit, t.encumbrance.gravityMultiplier = i.gravityMultiplier, t.derived.currentMove = i.currentMove, t.derived.currentRun = i.currentRun, t.finance = t.finance ?? { money: 0, inBank: 0 }, t.finance.money = v(t.finance.money, 0), t.finance.inBank = v(t.finance.inBank, 0), t.notes = t.notes ?? { defects: "", habits: "" }, typeof t.sheetToolsUnlocked != "boolean" && (t.sheetToolsUnlocked = !1), t.characterBuilder = t.characterBuilder ?? {
pursuits: [],
allocations: {},
baseBySkillId: {}
}, t.unconsciousLevel = v(t.unconsciousLevel, 0);
}
get skills() {
return this.items.filter((t) => t.type === "skill");
}
get weapons() {
return this.items.filter((t) => t.type === "weapon");
}
get armors() {
return this.items.filter((t) => t.type === "armor");
}
get gears() {
return this.items.filter((t) => t.type === "gear");
}
async rollSkillById(t) {
const a = this.items.get(t);
!a || a.type !== "skill" || await Ee(this, a.name, Number(a.system.value ?? 0));
}
async rollHitLocation() {
await Xt(this);
}
async useWeaponById(t) {
const a = this.items.get(t);
!a || a.type !== "weapon" || await dt(this, a);
}
async ensureBaseSkills() {
if (this.type !== "explorer" && this.type !== "creature") return 0;
const t = new Set(
this.items.filter((n) => n.type === "skill").map((n) => R(n.name))
), a = we.filter((n) => !t.has(R(n.name))).map((n) => ({
name: n.name,
type: "skill",
img: ye(n.name),
system: {
value: n.base,
category: n.category,
specialization: n.specialization ?? ""
}
}));
return a.length ? (await this.createEmbeddedDocuments("Item", a), a.length) : 0;
}
async rollCharacteristics() {
this.type === "explorer" && await ht(this);
}
async randomizeCharacter() {
this.type === "explorer" && await wt(this);
}
async applySpeciesItem(t) {
var y, b, w;
if (this.type !== "explorer" && this.type !== "creature") return;
const a = String((t == null ? void 0 : t.name) || "").trim(), n = (t == null ? void 0 : t.system) ?? {}, o = (n == null ? void 0 : n.modifiers) ?? {}, s = ((y = this.system) == null ? void 0 : y.speciesApplied) ?? {}, i = (s == null ? void 0 : s.modifiers) ?? {}, l = ((b = this.system) == null ? void 0 : b.characteristics) ?? {}, r = ((w = this.system) == null ? void 0 : w.profile) ?? {}, c = {}, u = {
str: 0,
mas: 0,
con: 0,
int: 0,
pow: 0,
dex: 0,
app: 0,
edu: 0,
age: 0
};
for (const p of Ha) {
const h = v(i == null ? void 0 : i[p], 0), A = v(o == null ? void 0 : o[p], 0);
u[p] = A;
const I = v(l == null ? void 0 : l[p], p === "age" ? 25 : 10) - h;
c[`system.characteristics.${p}`] = He(I + A);
}
const d = v(i == null ? void 0 : i.age, 0), g = u.age, m = v(r == null ? void 0 : r.chronologicalAge, v(l == null ? void 0 : l.age, 25)), f = v(r == null ? void 0 : r.physiologicalAge, m);
c["system.profile.chronologicalAge"] = He(m - d + g), c["system.profile.physiologicalAge"] = He(f - d + g), a && (c["system.profile.species"] = a), n != null && n.gravity && (c["system.profile.gravity"] = String(n.gravity)), (n == null ? void 0 : n.homeworld) !== void 0 && (c["system.profile.homeworld"] = String(n.homeworld || "")), c["system.speciesApplied"] = {
name: a,
modifiers: u
}, await this.update(c);
}
async rollDerivedCheck(t) {
var s, i, l;
if (this.type !== "explorer" && this.type !== "creature") return;
const a = {
health: "Health Roll",
reasoning: "Reasoning Roll",
luck: "Luck Roll",
dodge: "Dodge Roll"
}, n = Je(((s = this.system) == null ? void 0 : s.characteristics) ?? {}), o = v((l = (i = this.system) == null ? void 0 : i.derivedRolls) == null ? void 0 : l[t], n[t]);
await Ee(this, a[t], o);
}
}
class Ga extends Item {
async rollSkill() {
var a;
if (this.type !== "skill") return;
const t = this.actor;
if (!t) {
(a = ui.notifications) == null || a.warn("Skill must be owned by an Actor to roll.");
return;
}
await Ee(t, this.name, Number(this.system.value ?? 0));
}
async useWeapon() {
var a;
if (this.type !== "weapon") return;
const t = this.actor;
if (!t) {
(a = ui.notifications) == null || a.warn("Weapon must be owned by an Actor to use.");
return;
}
await dt(t, this);
}
}
const ja = foundry.appv1.sheets.ActorSheet, Fe = {
Agility: "agility",
Communication: "communication",
Knowledge: "knowledge",
Perception: "perception",
Technical: "technical"
}, _a = {
Agility: "STR + DEX",
Communication: "INT + APP",
Knowledge: "INT + EDU",
Perception: "POW + CON",
Technical: "DEX + INT"
}, Wa = /* @__PURE__ */ new Set([
R("Archaic Melee Weapon"),
R("Archaic Ranged Weapons")
]), Ya = R("Hyperdrive");
function fe(e, t) {
return e.name.localeCompare(t.name);
}
function C(e, t = 0) {
const a = Number(e);
return Number.isFinite(a) ? a : t;
}
function B(e, t = 0) {
return Math.max(0, Math.floor(C(e, t)));
}
function Pe(e) {
return {
education: B(e == null ? void 0 : e.education, 0),
pursuits: B(e == null ? void 0 : e.pursuits, 0),
special: B(e == null ? void 0 : e.special, 0)
};
}
function za(e) {
const t = String((e == null ? void 0 : e.name) || "").trim(), a = B(e == null ? void 0 : e.years, 0);
return !t || a <= 0 ? null : { name: t, years: a };
}
function kt(e) {
const t = Array.isArray(e == null ? void 0 : e.pursuits) ? e.pursuits.map((o) => za(o)).filter((o) => !!o) : [], a = {};
if (e != null && e.allocations && typeof e.allocations == "object")
for (const [o, s] of Object.entries(e.allocations)) {
if (!o) continue;
const i = Pe(s);
i.education + i.pursuits + i.special <= 0 || (a[o] = i);
}
const n = {};
if (e != null && e.baseBySkillId && typeof e.baseBySkillId == "object")
for (const [o, s] of Object.entries(e.baseBySkillId))
o && (n[o] = Math.max(0, B(s, 0)));
return {
pursuits: t,
allocations: a,
baseBySkillId: n
};
}
function Qa(e) {
return !Wa.has(R(e));
}
function Xa(e) {
return R(e) !== Ya;
}
function Za(e) {
const t = new Map(
he.map((n) => [R(n.name), n])
), a = /* @__PURE__ */ new Set();
for (const n of e) {
const o = t.get(R(n.name));
if (o) {
for (const s of o.fixedSkills || [])
a.add(R(s));
for (const s of o.optionalSkillGroups || [])
for (const i of s.skills || [])
a.add(R(i));
}
}
return a;
}
function Ja(e) {
const t = B(e == null ? void 0 : e.age, 0), a = B(e == null ? void 0 : e.edu, 0), n = B(e == null ? void 0 : e.int, 0), o = Math.max(0, (a - 10) * 20), s = Math.max(0, t - a - 6), i = s * 20, l = Math.max(0, n * 10);
return {
educationTotal: o,
pursuitsTotal: i,
specialTotal: l,
pursuitYearsTotal: s
};
}
function Z(e, t) {
var d, g, m, f, y, b, w;
const a = Ja(((d = e.system) == null ? void 0 : d.characteristics) ?? {}), n = kt(((g = e.system) == null ? void 0 : g.characterBuilder) ?? {}), o = he.map((p) => p.name).sort((p, h) => p.localeCompare(h)), s = Za(n.pursuits), i = /* @__PURE__ */ new Map();
let l = 0, r = 0, c = 0;
for (const p of t) {
const h = Pe(n.allocations[p.id]), A = h.education + h.pursuits + h.special, S = n.baseBySkillId[p.id], I = (m = $e(p.name)) == null ? void 0 : m.base, M = Math.max(0, C((f = p.system) == null ? void 0 : f.value, 0) - A), D = Math.max(
0,
Number.isFinite(S) ? S : I ?? M
), T = Fe[String(((y = p.system) == null ? void 0 : y.category) || "")] ?? "", H = T ? Math.max(0, B((w = (b = e.system) == null ? void 0 : b.skillRootMax) == null ? void 0 : w[T], 0)) : 99, L = Math.max(0, H - D), F = R(p.name), X = Qa(p.name), J = s.has(F), N = Xa(p.name);
i.set(p.id, {
allocation: h,
totalAllocated: A,
baseValue: D,
rootCap: H,
maxAllocation: L,
allowsEducation: X,
allowsPursuits: J,
allowsSpecial: N
}), l += h.education, r += h.pursuits, c += h.special;
}
const u = n.pursuits.reduce((p, h) => p + h.years, 0);
return {
state: n,
education: {
total: a.educationTotal,
spent: l,
remaining: a.educationTotal - l
},
pursuits: {
total: a.pursuitsTotal,
spent: r,
remaining: a.pursuitsTotal - r
},
special: {
total: a.specialTotal,
spent: c,
remaining: a.specialTotal - c
},
pursuitYears: {
total: a.pursuitYearsTotal,
spent: u,
remaining: a.pursuitYearsTotal - u
},
pursuitOptions: o,
pursuitAllowedSkills: s,
bySkillId: i
};
}
function en(e, t) {
var n, o, s, i, l, r, c;
if (e.type !== "armor" || !((n = e.system) != null && n.equipped) || (o = e.system) != null && o.stowed || !((i = (s = e.system) == null ? void 0 : s.coverage) != null && i[t])) return 0;
const a = C((r = (l = e.system) == null ? void 0 : l.perLocation) == null ? void 0 : r[t], 0);
return a > 0 ? a : C((c = e.system) == null ? void 0 : c.value, 0);
}
function tn(e, t) {
var o, s, i;
const a = C((i = (s = (o = e.system) == null ? void 0 : o.hitLocations) == null ? void 0 : s[t]) == null ? void 0 : i.armor, 0), n = e.items.filter((l) => l.type === "armor").reduce((l, r) => l + en(r, t), 0);
return a + n;
}
function et(e, t, a) {
var l, r;
const n = se(e).entries.find((c) => c.slug === t), o = ((r = (l = e.system) == null ? void 0 : l.hitLocations) == null ? void 0 : r[t]) ?? {}, s = o.hp ?? {}, i = String(o.label ?? "").trim();
return {
slug: t,
rollRange: a,
label: i || String((n == null ? void 0 : n.label) || t),
armor: C(o.armor, 0),
totalArmor: tn(e, t),
hp: {
value: C(s.value, 0),
max: C(s.max, 0)
}
};
}
function tt(e) {
return e[Math.floor(Math.random() * e.length)];
}
function an(e) {
return Math.max(0, Math.min(99, Math.floor(e)));
}
class nn extends ja {
isToolsUnlocked() {
var t, a;
return !!((a = (t = this.actor) == null ? void 0 : t.system) != null && a.sheetToolsUnlocked);
}
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "actor", "explorer"],
width: 930,
height: 1460,
tabs: [{ navSelector: ".tabs", contentSelector: ".sheet-body", initial: "overview" }]
});
}
get template() {
return "systems/ringworld/templates/actor/explorer-sheet.hbs";
}
async getData(t) {
var N, le, ce, ue, me, de, ge, pe, ee, Ve, Ge, je, _e, We, Ye, ze;
const a = await super.getData(t), n = this.actor;
(N = game.user) != null && N.isGM && n.isOwner && await ((le = n.ensureBaseSkills) == null ? void 0 : le.call(n));
const o = n.items.filter((k) => k.type === "skill").sort(fe), s = Z(n, o), i = n.items.filter((k) => k.type === "weapon").sort(fe), l = n.items.filter((k) => k.type === "armor").sort(fe), r = n.items.filter((k) => k.type === "gear").sort(fe), c = game.items.filter((k) => k.type === "species" && k.system.playable).map((k) => k.name).sort(), u = Yt(), d = /* @__PURE__ */ new Map();
for (const k of o) {
const O = R(k.name);
d.has(O) || d.set(O, k);
}
const g = n.system.skillRootMax ?? {}, m = Wt.map((k) => {
const O = Fe[k] ?? k.toLowerCase(), Te = we.filter((V) => V.category === k).map((V) => {
var Qe;
const U = d.get(R(V.name)), te = U ? s.bySkillId.get(U.id) : null;
return {
name: V.name,
base: V.base,
category: V.category,
specialization: V.specialization ?? "",
itemId: (U == null ? void 0 : U.id) ?? null,
icon: (U == null ? void 0 : U.img) || ye(V.name),
value: ((Qe = U == null ? void 0 : U.system) == null ? void 0 : Qe.value) ?? V.base,
missing: !U,
allocationTotal: (te == null ? void 0 : te.totalAllocated) ?? 0,
allocation: (te == null ? void 0 : te.allocation) ?? { education: 0, pursuits: 0, special: 0 }
};
});
return {
category: k,
rootKey: O,
rootFormula: _a[k] ?? "",
rootValue: C(g[O], 0),
rows: Te
};
}), f = o.filter((k) => !u.has(R(k.name))).map((k) => {
const O = s.bySkillId.get(k.id);
return {
id: k.id,
name: k.name,
system: k.system,
img: Oe(k.name, k.img || W),
allocationTotal: (O == null ? void 0 : O.totalAllocated) ?? 0,
allocation: (O == null ? void 0 : O.allocation) ?? { education: 0, pursuits: 0, special: 0 }
};
}), y = m.reduce(
(k, O) => k + O.rows.filter((Te) => Te.missing).length,
0
), b = se(n), w = new Map(b.entries.map((k) => [k.slug, `${k.min}-${k.max}`])), h = b.entries.map((k) => k.slug).map(
(k) => et(n, k, w.get(k) ?? "")
), S = b.bodyOrder.map(
(k) => et(n, k, w.get(k) ?? "")
), I = ((ce = n.system) == null ? void 0 : ce.encumbrance) ?? {}, M = C(I.rawLoadKg, 0), D = C(I.effectiveLoadKg, M), T = C(I.limitKg, C((me = (ue = n.system) == null ? void 0 : ue.characteristics) == null ? void 0 : me.str, 0)), H = C(I.actionRankingPenalty, 0), L = C((ge = (de = n.system) == null ? void 0 : de.derived) == null ? void 0 : ge.actionRanking, 0), F = Math.max(1, L + H), X = `${M.toFixed(1)}kg on-person | ${D.toFixed(1)}kg effective / ${T.toFixed(1)}kg limit | AR +${H} (stowed ignored)`, J = `${C((ee = (pe = n.system) == null ? void 0 : pe.derived) == null ? void 0 : ee.currentMove, C((Ge = (Ve = n.system) == null ? void 0 : Ve.derived) == null ? void 0 : Ge.move, 3))}m / ${C((_e = (je = n.system) == null ? void 0 : je.derived) == null ? void 0 : _e.currentRun, C((Ye = (We = n.system) == null ? void 0 : We.derived) == null ? void 0 : Ye.run, 6))}m`;
return {
...a,
system: n.system,
skills: o,
skillsByCategory: m,
customSkills: f,
missingBaseSkillsCount: y,
weapons: i,
armors: l,
gear: r,
speciesOptions: c,
hitLocations: h,
bodyLocations: S,
hitLocationSchemaOptions: Kt(),
currentHitLocationSchema: String(n.system.hitLocationSchema || "auto"),
activeHitLocationSchemaId: b.id,
activeHitLocationSchemaLabel: b.label,
sheetToolsUnlocked: !!((ze = n.system) != null && ze.sheetToolsUnlocked),
baseActionRanking: L,
effectiveActionRanking: F,
encumbranceSummary: X,
movementSummary: J,
builder: {
education: s.education,
pursuits: s.pursuits,
special: s.special,
pursuitYears: s.pursuitYears,
pursuitsList: s.state.pursuits,
pursuitOptions: s.pursuitOptions
}
};
}
async updateBuilderState(t) {
await this.actor.update({
"system.characterBuilder": {
pursuits: t.pursuits,
allocations: t.allocations,
baseBySkillId: t.baseBySkillId
}
});
}
async syncSkillsFromBuilderState(t) {
var o, s, i, l, r, c;
const a = this.actor.items.filter((u) => u.type === "skill"), n = [];
for (const u of a) {
const d = Pe(t.allocations[u.id]), g = d.education + d.pursuits + d.special, m = Fe[String(((o = u.system) == null ? void 0 : o.category) || "")] ?? "", f = m ? Math.max(0, B((i = (s = this.actor.system) == null ? void 0 : s.skillRootMax) == null ? void 0 : i[m], 0)) : 99;
let y = t.baseBySkillId[u.id];
if (!Number.isFinite(y)) {
const w = (l = $e(u.name)) == null ? void 0 : l.base;
y = Math.max(0, w ?? C((r = u.system) == null ? void 0 : r.value, 0) - g), t.baseBySkillId[u.id] = y;
}
const b = Math.max(0, Math.min(99, Math.min(f, y + g)));
C((c = u.system) == null ? void 0 : c.value, 0) !== b && n.push({ _id: u.id, "system.value": b });
}
n.length && await this.actor.updateEmbeddedDocuments("Item", n);
}
async addOrUpdatePursuit(t, a) {
var c;
if (!this.isToolsUnlocked()) return;
const n = String(t || "").trim(), o = Math.max(0, Math.floor(Number(a) || 0));
if (!n || o <= 0) {
(c = ui.notifications) == null || c.warn("Choose a pursuit and set years above 0.");
return;
}
const s = this.actor.items.filter((u) => u.type === "skill"), i = Z(this.actor, s), l = foundry.utils.deepClone(i.state), r = l.pursuits.findIndex((u) => R(u.name) === R(n));
r >= 0 ? l.pursuits[r].years = o : l.pursuits.push({ name: n, years: o }), await this.updateBuilderState(l);
}
async removePursuit(t) {
if (!this.isToolsUnlocked()) return;
const a = this.actor.items.filter((s) => s.type === "skill"), n = Z(this.actor, a), o = foundry.utils.deepClone(n.state);
t < 0 || t >= o.pursuits.length || (o.pursuits.splice(t, 1), await this.updateBuilderState(o));
}
async resetBuilderAllocations() {
if (!this.isToolsUnlocked()) return;
const t = this.actor.items.filter((o) => o.type === "skill"), a = Z(this.actor, t), n = foundry.utils.deepClone(a.state);
n.allocations = {}, await this.updateBuilderState(n), await this.syncSkillsFromBuilderState(n);
}
async randomizeBuilderAllocations() {
var f, y, b, w;
if (!this.isToolsUnlocked()) return;
await ((y = (f = this.actor).ensureBaseSkills) == null ? void 0 : y.call(f));
const t = this.actor.items.filter((p) => p.type === "skill").sort(fe), a = Z(this.actor, t), n = foundry.utils.deepClone(a.state), o = Math.max(0, a.pursuitYears.total), s = he.map((p) => p.name);
if (n.pursuits = [], o > 0 && s.length > 0) {
const p = Math.min(
s.length,
Math.max(1, Math.min(3, Math.floor(o / 2) || 1))
), h = [...s];
for (let S = 0; S < p && h.length; S += 1) {
const I = Math.floor(Math.random() * h.length);
n.pursuits.push({ name: h[I], years: 1 }), h.splice(I, 1);
}
let A = o - n.pursuits.length;
for (; A > 0 && n.pursuits.length; ) {
const S = tt(n.pursuits);
S.years += 1, A -= 1;
}
}
const i = kt(n);
i.allocations = {};
const l = Z(
{ ...this.actor, system: { ...this.actor.system, characterBuilder: i } },
t
);
for (const p of t) {
const h = l.bySkillId.get(p.id);
h && (i.baseBySkillId[p.id] = h.baseValue, i.allocations[p.id] = { education: 0, pursuits: 0, special: 0 });
}
const r = /* @__PURE__ */ new Map();
for (const p of t)
r.set(p.id, 0);
const c = (p) => {
const h = l.bySkillId.get(p);
return h ? Math.max(0, h.maxAllocation) : 0;
}, u = (p, h, A) => {
let S = Math.max(0, p), I = 0;
for (; S > 0 && I < 3e5; ) {
I += 1;
const M = t.filter((H) => {
const L = l.bySkillId.get(H.id);
return !L || !A(H, L) ? !1 : (r.get(H.id) ?? 0) < c(H.id);
});
if (!M.length) break;
const D = tt(M), T = i.allocations[D.id] ?? { education: 0, pursuits: 0, special: 0 };
T[h] += 1, i.allocations[D.id] = T, r.set(D.id, (r.get(D.id) ?? 0) + 1), S -= 1;
}
return S;
}, d = u(
Math.max(0, l.education.total),
"education",
(p, h) => h.allowsEducation
), g = u(
Math.max(0, l.pursuits.total),
"pursuits",
(p, h) => h.allowsPursuits
), m = u(
Math.max(0, l.special.total),
"special",
(p, h) => h.allowsSpecial
);
for (const p of t) {
const h = Pe(i.allocations[p.id]);
h.education + h.pursuits + h.special <= 0 && delete i.allocations[p.id];
}
await this.updateBuilderState(i), await this.syncSkillsFromBuilderState(i), d > 0 || g > 0 || m > 0 ? (b = ui.notifications) == null || b.info(
`Random build complete. Unspent points: EDU ${d}, Pursuits ${g}, Special ${m}.`
) : (w = ui.notifications) == null || w.info("Random build complete. All available points allocated.");
}
async promptSkillAllocation(t) {
var y, b, w;
if (!this.isToolsUnlocked()) return;
const a = this.actor.items.get(t);
if (!a || a.type !== "skill") return;
const n = this.actor.items.filter((p) => p.type === "skill"), o = Z(this.actor, n), s = o.bySkillId.get(t);
if (!s) return;
const i = s.allocation, l = Math.max(0, o.education.remaining + i.education), r = Math.max(0, o.pursuits.remaining + i.pursuits), c = Math.max(0, o.special.remaining + i.special), u = Math.max(0, s.maxAllocation), d = `
<form class="rw-builder-alloc-form">
<header class="rw-builder-alloc-head">
<img src="${a.img || W}" alt="" />
<div>
<h3>${a.name}</h3>
<p>Base ${s.baseValue}% | Root cap ${s.rootCap}% | Current ${C((y = a.system) == null ? void 0 : y.value, 0)}%</p>
</div>
</header>
<label>Education ${s.allowsEducation ? "" : "(Not eligible)"}
<input name="education" type="number" min="0" max="${l}" value="${i.education}" ${s.allowsEducation ? "" : "disabled"} />
</label>
<label>Pursuits ${s.allowsPursuits ? "" : "(Not in selected pursuits)"}
<input name="pursuits" type="number" min="0" max="${r}" value="${i.pursuits}" ${s.allowsPursuits ? "" : "disabled"} />
</label>
<label>Special Interests ${s.allowsSpecial ? "" : "(Hyperdrive excluded)"}
<input name="special" type="number" min="0" max="${c}" value="${i.special}" ${s.allowsSpecial ? "" : "disabled"} />
</label>
<p class="hint">Max additional from root cap: ${u}. Pools: EDU ${o.education.remaining}, Pursuits ${o.pursuits.remaining}, Special ${o.special.remaining}.</p>
</form>
`, g = await new Promise((p) => {
new Dialog({
title: `${a.name} - Allocate Character Points`,
content: d,
buttons: {
save: {
label: "Apply",
callback: (h) => {
const A = h.find("form")[0];
if (!A) {
p(null);
return;
}
const S = new FormData(A);
p({
education: s.allowsEducation ? B(S.get("education"), i.education) : i.education,
pursuits: s.allowsPursuits ? B(S.get("pursuits"), i.pursuits) : i.pursuits,
special: s.allowsSpecial ? B(S.get("special"), i.special) : i.special
});
}
},
cancel: {
label: "Cancel",
callback: () => p(null)
}
},
default: "save",
close: () => p(null)
}).render(!0);
});
if (!g) return;
const m = g.education + g.pursuits + g.special;
if (m > u) {
(b = ui.notifications) == null || b.warn(`Allocation exceeds this skill's root-cap allowance (${u}).`);
return;
}
if (g.education > l || g.pursuits > r || g.special > c) {
(w = ui.notifications) == null || w.warn("Allocation exceeds remaining pool points.");
return;
}
const f = foundry.utils.deepClone(o.state);
f.baseBySkillId[a.id] = s.baseValue, m <= 0 ? delete f.allocations[a.id] : f.allocations[a.id] = g, await this.updateBuilderState(f), await this.syncSkillsFromBuilderState(f);
}
async rollCharacteristicCheck(t, a) {
var c, u;
if (!(/* @__PURE__ */ new Set(["str", "mas", "con", "int", "pow", "dex", "app", "edu", "age"])).has(t)) return;
const o = Math.max(0, B((u = (c = this.actor.system) == null ? void 0 : c.characteristics) == null ? void 0 : u[t], 0)), s = `
<form class="rw-stat-roll-form">
<div class="form-group">
<label>Characteristic</label>
<input type="text" value="${foundry.utils.escapeHTML(a)}" disabled />
</div>
<div class="form-group">
<label>Base Value</label>
<input type="number" value="${o}" disabled />
</div>
<div class="form-group">
<label>Multiplier</label>
<select name="multiplier">
<option value="1">x1</option>
<option value="2">x2</option>
<option value="3">x3</option>
<option value="4">x4</option>
<option value="5">x5</option>
<option value="6">x6</option>
<option value="7">x7</option>
</select>
</div>
<p class="hint">Roll d100 against Characteristic x Multiplier.</p>
</form>
`, i = await new Promise((d) => {
let g = !1;
const m = (f) => {
g || (g = !0, d(f));
};
new Dialog({
title: `${a} Check`,
content: s,
buttons: {
roll: {
label: "Roll",
callback: (f) => {
var w, p;
const y = Number(((p = (w = f[0]) == null ? void 0 : w.querySelector("select[name='multiplier']")) == null ? void 0 : p.value) || 1), b = Math.max(1, Math.min(7, Math.floor(y || 1)));
m(b);
}
},
cancel: {
label: "Cancel",
callback: () => m(null)
}
},
default: "roll",
close: () => m(null)
}).render(!0);
});
if (!i) return;
const l = an(o * i), r = await Ue(l);
await mt({
actor: this.actor,
actorName: this.actor.name,
actorImg: this.actor.img,
skillName: `${a} x${i}`,
skillImg: W,
roll: r.roll,
target: r.target,
outcomeLabel: Ae(r.outcome),
isSuccess: r.isSuccess,
isSpecial: r.isSpecial,
isFumble: r.isFumble
});
}
activateListeners(t) {
super.activateListeners(t), t.find(".toggle-sheet-lock").on("click", async (a) => {
var o;
a.preventDefault();
const n = !this.isToolsUnlocked();
await this.actor.update({ "system.sheetToolsUnlocked": n }), (o = ui.notifications) == null || o.info(n ? "Sheet tools unlocked." : "Sheet tools locked.");
}), t.find(".roll-characteristic").on("click", async (a) => {
a.preventDefault();
const n = a.currentTarget, o = String(n.dataset.char || "").trim().toLowerCase(), s = String(n.dataset.label || o.toUpperCase());
o && await this.rollCharacteristicCheck(o, s);
}), t.find(".item-create").on("click", async (a) => {
a.preventDefault();
const n = String(a.currentTarget.dataset.type || "gear"), o = { name: `New ${n}`, type: n };
n === "skill" && (o.img = W, o.system = { value: 0, category: "Technical", specialization: "" }), await this.actor.createEmbeddedDocuments("Item", [o]);
}), t.find(".item-edit").on("click", (a) => {
var s;
a.preventDefault();
const n = a.currentTarget.closest(".item"), o = n ? this.actor.items.get(n.dataset.itemId ?? "") : null;
(s = o == null ? void 0 : o.sheet) == null || s.render(!0);
}), t.find(".item-delete").on("click", async (a) => {
a.preventDefault();
const n = a.currentTarget.closest(".item");
n != null && n.dataset.itemId && await this.actor.deleteEmbeddedDocuments("Item", [n.dataset.itemId]);
}), t.find(".item-roll-skill").on("click", async (a) => {
a.preventDefault();
const n = a.currentTarget.closest(".item");
n != null && n.dataset.itemId && await this.actor.rollSkillById(n.dataset.itemId);
}), t.find(".roll-derived").on("click", async (a) => {
a.preventDefault();
const n = String(a.currentTarget.dataset.check || "");
n && await this.actor.rollDerivedCheck(n);
}), t.find(".item-use-weapon").on("click", async (a) => {
a.preventDefault();
const n = a.currentTarget.closest(".item");
n != null && n.dataset.itemId && await this.actor.useWeaponById(n.dataset.itemId);
}), t.find(".item-toggle-equipped").on("click", async (a) => {
var l;
a.preventDefault();
const n = a.currentTarget.closest(".item");
if (!(n != null && n.dataset.itemId)) return;
const o = this.actor.items.get(n.dataset.itemId);
if (!o) return;
const s = !((l = o.system) != null && l.equipped), i = {
_id: o.id,
"system.equipped": s
};
s && (i["system.stowed"] = !1), await this.actor.updateEmbeddedDocuments("Item", [i]);
}), t.find(".item-toggle-stowed").on("click", async (a) => {
var l;
a.preventDefault();
const n = a.currentTarget.closest(".item");
if (!(n != null && n.dataset.itemId)) return;
const o = this.actor.items.get(n.dataset.itemId);
if (!o) return;
const s = !((l = o.system) != null && l.stowed), i = {
_id: o.id,
"system.stowed": s
};
s && (i["system.equipped"] = !1), await this.actor.updateEmbeddedDocuments("Item", [i]);
}), t.find(".roll-hit-location").on("click", async (a) => {
a.preventDefault(), await this.actor.rollHitLocation();
}), t.find(".reset-conditions").on("click", async (a) => {
a.preventDefault(), await this.actor.update({
"system.conditions.prone": !1,
"system.conditions.unconscious": !1,
"system.conditions.dead": !1,
"system.conditions.dying": !1,
"system.conditions.unusableLimbs.rArm": !1,
"system.conditions.unusableLimbs.lArm": !1,
"system.conditions.unusableLimbs.rLeg": !1,
"system.conditions.unusableLimbs.lLeg": !1
});
}), t.find(".populate-base-skills").on("click", async (a) => {
var o, s;
a.preventDefault();
const n = await this.actor.ensureBaseSkills();
n > 0 ? (o = ui.notifications) == null || o.info(`Added ${n} Ringworld base skills.`) : (s = ui.notifications) == null || s.info("All Ringworld base skills are already present.");
}), t.find(".add-base-skill").on("click", async (a) => {
a.preventDefault();
const n = a.currentTarget, o = String(n.dataset.skillName || ""), s = String(n.dataset.skillCategory || ""), i = Number(n.dataset.skillBase || 0), l = String(n.dataset.skillSpecialization || "");
o && await this.actor.createEmbeddedDocuments("Item", [{
name: o,
type: "skill",
img: ye(o),
system: { value: i, category: s, specialization: l }
}]);
}), t.find(".builder-add-pursuit").on("click", async (a) => {
var i, l;
a.preventDefault();
const n = a.currentTarget.closest(".rw-builder-pursuits");
if (!n) return;
const o = String(((i = n.querySelector(".builder-new-pursuit")) == null ? void 0 : i.value) || ""), s = Number(((l = n.querySelector(".builder-new-years")) == null ? void 0 : l.value) || 0);
await this.addOrUpdatePursuit(o, s);
}), t.find(".builder-update-pursuit").on("click", async (a) => {
var i, l;
a.preventDefault();
const n = a.currentTarget.closest(".builder-pursuit-row");
if (!n) return;
const o = String(((i = n.querySelector(".builder-pursuit-name")) == null ? void 0 : i.value) || ""), s = Number(((l = n.querySelector(".builder-pursuit-years")) == null ? void 0 : l.value) || 0);
await this.addOrUpdatePursuit(o, s);
}), t.find(".builder-remove-pursuit").on("click", async (a) => {
a.preventDefault();
const n = Number(a.currentTarget.dataset.index ?? -1);
!Number.isFinite(n) || n < 0 || await this.removePursuit(n);
}), t.find(".builder-reset-points").on("click", async (a) => {
var n;
a.preventDefault(), await this.resetBuilderAllocations(), (n = ui.notifications) == null || n.info("Character-builder allocations reset.");
}), t.find(".roll-characteristics").on("click", async (a) => {
var n, o, s;
a.preventDefault(), this.isToolsUnlocked() && (await ((o = (n = this.actor).rollCharacteristics) == null ? void 0 : o.call(n)), (s = ui.notifications) == null || s.info("Characteristics rolled."));
}), t.find(".builder-auto-allocate").on("click", async (a) => {
var n, o;
a.preventDefault(), this.isToolsUnlocked() && (await ((o = (n = this.actor).ensureBaseSkills) == null ? void 0 : o.call(n)), await this.randomizeBuilderAllocations());
}), t.find(".builder-skill-allocate").on("click", async (a) => {
a.preventDefault();
const n = a.currentTarget.closest(".item"), o = String((n == null ? void 0 : n.dataset.itemId) || "");
o && await this.promptSkillAllocation(o);
});
}
async _onDropItemCreate(t) {
var i, l, r;
const a = Array.isArray(t) ? t : [t], n = a.filter((c) => (c == null ? void 0 : c.type) === "species"), o = a.filter((c) => (c == null ? void 0 : c.type) !== "species");
for (const c of n)
await ((l = (i = this.actor).applySpeciesItem) == null ? void 0 : l.call(i, c));
if (n.length > 0) {
const c = n.map((u) => String((u == null ? void 0 : u.name) || "Species")).join(", ");
(r = ui.notifications) == null || r.info(`Applied species to actor: ${c}`);
}
if (o.length === 0) return [];
const s = o.map((c) => (c == null ? void 0 : c.type) !== "skill" ? c : {
...c,
img: Oe(String((c == null ? void 0 : c.name) || "Skill"), String((c == null ? void 0 : c.img) || ""))
});
return super._onDropItemCreate(Array.isArray(t) ? s : s[0]);
}
}
const on = foundry.appv1.sheets.ActorSheet;
class sn extends on {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "actor", "creature"],
width: 760,
height: 700
});
}
get template() {
return "systems/ringworld/templates/actor/creature-sheet.hbs";
}
async getData(t) {
const a = await super.getData(t), n = this.actor;
return {
...a,
system: n.system,
skills: n.items.filter((o) => o.type === "skill"),
weapons: n.items.filter((o) => o.type === "weapon"),
hitLocations: se(n).entries.map((o) => {
var s;
return {
slug: o.slug,
rollRange: `${o.min}-${o.max}`,
label: o.label,
...((s = n.system.hitLocations) == null ? void 0 : s[o.slug]) ?? {}
};
})
};
}
activateListeners(t) {
super.activateListeners(t), t.find(".item-create").on("click", async (a) => {
a.preventDefault();
const n = String(a.currentTarget.dataset.type || "skill");
await this.actor.createEmbeddedDocuments("Item", [{ name: `New ${n}`, type: n }]);
}), t.find(".item-edit").on("click", (a) => {
var s;
const n = a.currentTarget.closest(".item"), o = n ? this.actor.items.get(n.dataset.itemId ?? "") : null;
(s = o == null ? void 0 : o.sheet) == null || s.render(!0);
}), t.find(".item-delete").on("click", async (a) => {
const n = a.currentTarget.closest(".item");
n != null && n.dataset.itemId && await this.actor.deleteEmbeddedDocuments("Item", [n.dataset.itemId]);
}), t.find(".item-roll-skill").on("click", async (a) => {
const n = a.currentTarget.closest(".item");
n != null && n.dataset.itemId && await this.actor.rollSkillById(n.dataset.itemId);
}), t.find(".item-use-weapon").on("click", async (a) => {
const n = a.currentTarget.closest(".item");
n != null && n.dataset.itemId && await this.actor.useWeaponById(n.dataset.itemId);
}), t.find(".roll-hit-location").on("click", async () => {
await this.actor.rollHitLocation();
});
}
}
const rn = foundry.appv1.sheets.ActorSheet;
class ln extends rn {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "actor", "vehicle"],
width: 680,
height: 560
});
}
get template() {
return "systems/ringworld/templates/actor/vehicle-sheet.hbs";
}
async getData(t) {
return {
...await super.getData(t),
system: this.actor.system
};
}
}
const cn = foundry.appv1.sheets.ItemSheet;
class un extends cn {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "item", "skill"],
width: 520,
height: 420
});
}
get template() {
return "systems/ringworld/templates/item/skill-sheet.hbs";
}
async getData(t) {
return {
...await super.getData(t),
system: this.item.system
};
}
activateListeners(t) {
super.activateListeners(t), t.find(".roll-skill").on("click", async (a) => {
a.preventDefault(), await this.item.rollSkill();
});
}
}
const mn = foundry.appv1.sheets.ItemSheet;
class dn extends mn {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "item", "weapon"],
width: 620,
height: 620
});
}
get template() {
return "systems/ringworld/templates/item/weapon-sheet.hbs";
}
async getData(t) {
const a = await super.getData(t), n = this.item.actor;
return {
...a,
system: this.item.system,
skills: n ? n.items.filter((o) => o.type === "skill") : [],
powerPacks: n ? n.items.filter((o) => o.type === "gear" && o.system.isPowerPack) : []
};
}
activateListeners(t) {
super.activateListeners(t), t.find(".use-weapon").on("click", async (a) => {
a.preventDefault(), await this.item.useWeapon();
});
}
}
const gn = foundry.appv1.sheets.ItemSheet;
class pn extends gn {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "item", "armor"],
width: 620,
height: 560
});
}
get template() {
return "systems/ringworld/templates/item/armor-sheet.hbs";
}
async getData(t) {
return {
...await super.getData(t),
system: this.item.system
};
}
}
const fn = foundry.appv1.sheets.ItemSheet;
class yn extends fn {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "item", "gear"],
width: 620,
height: 520
});
}
get template() {
return "systems/ringworld/templates/item/gear-sheet.hbs";
}
async getData(t) {
return {
...await super.getData(t),
system: this.item.system
};
}
}
const hn = foundry.appv1.sheets.ItemSheet;
class wn extends hn {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "item", "species"],
width: 620,
height: 560
});
}
get template() {
return "systems/ringworld/templates/item/species-sheet.hbs";
}
async getData(t) {
return {
...await super.getData(t),
system: this.item.system
};
}
}
const bn = foundry.appv1.sheets.ItemSheet;
function at(e) {
return Array.isArray(e) ? e.map((t) => String(t)) : [];
}
class kn extends bn {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ringworld", "sheet", "item", "pursuit"],
width: 680,
height: 620
});
}
get template() {
return "systems/ringworld/templates/item/pursuit-sheet.hbs";
}
async getData(t) {
const a = await super.getData(t), n = this.item.system ?? {}, o = at(n.fixedSkills), s = Array.isArray(n.optionalSkillGroups) ? n.optionalSkillGroups.map((i) => ({
choose: Number((i == null ? void 0 : i.choose) ?? 1),
skills: at(i == null ? void 0 : i.skills)
})) : [];
return {
...a,
system: n,
fixedSkills: o,
optionalGroups: s
};
}
}
const vt = "impulseState", qe = `${x}.impulseHudPos`, vn = [
{ value: "draw", label: "Draw / Stow Weapon" },
{ value: "aim", label: "Aim Ranged Weapon" },
{ value: "melee", label: "Melee Attack" },
{ value: "stand", label: "Stand / Recover" },
{ value: "perception", label: "Perception Skill" },
{ value: "unarmed", label: "Unarmed Combat" }
], Sn = [
{ value: "fire", label: "Fire Ranged Weapon" },
{ value: "fall", label: "Fall" },
{ value: "rise", label: "Rise (Kneeling to Stand/Run)" },
{ value: "minorCustom", label: "Other Minor Action" }
];
function q(e, t = 0) {
const a = Number(e);
return Number.isFinite(a) ? a : t;
}
function Y(e) {
return String(e ?? "").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
}
function Ke(e) {
return e <= 4 ? 7 : e <= 8 ? 6 : e <= 12 ? 5 : e <= 16 ? 4 : e <= 20 ? 3 : e <= 24 ? 2 : 1;
}
function Re(e) {
switch (e) {
case "draw":
return "Draw / Stow";
case "aim":
return "Aim";
case "melee":
return "Melee Attack";
case "stand":
return "Stand / Recover";
case "perception":
return "Perception";
case "unarmed":
return "Unarmed Combat";
case "fire":
return "Fire Ranged Weapon";
case "fall":
return "Fall";
case "rise":
return "Rise";
case "minorCustom":
return "Minor Action";
default:
return String(e);
}
}
function z(e) {
var t, a, n;
return Math.max(1, q((n = (a = (t = e == null ? void 0 : e.actor) == null ? void 0 : t.system) == null ? void 0 : a.characteristics) == null ? void 0 : n.dex, 10));
}
function St(e) {
var a, n;
const t = q((n = (a = e == null ? void 0 : e.flags) == null ? void 0 : a[x]) == null ? void 0 : n.actionRanking, 0);
return t > 0 ? t : Ke(z(e));
}
function At(e) {
var a, n, o;
const t = q((o = (n = (a = e == null ? void 0 : e.actor) == null ? void 0 : a.system) == null ? void 0 : n.encumbrance) == null ? void 0 : o.actionRankingPenalty, 0);
return Math.max(0, Math.trunc(t));
}
async function xt(e) {
var n, o;
const t = Ke(z(e));
return q((o = (n = e == null ? void 0 : e.flags) == null ? void 0 : n[x]) == null ? void 0 : o.actionRanking, 0) !== t && await e.update({ [`flags.${x}.actionRanking`]: t }), t;
}
function ie(e) {
const t = foundry.utils.deepClone(e.getFlag(x, vt) ?? {}), a = Array.isArray(t.queue) ? t.queue : [];
return {
currentImpulse: Math.max(1, q(t.currentImpulse, 1)),
queue: a
};
}
async function re(e, t) {
await e.setFlag(x, vt, {
currentImpulse: Math.max(1, q(t.currentImpulse, 1)),
queue: Array.isArray(t.queue) ? t.queue : []
});
}
function ae(e, t) {
return [...e].sort((a, n) => {
if (a.completeImpulse !== n.completeImpulse) return a.completeImpulse - n.completeImpulse;
const o = z(t.combatants.get(a.combatantId));
return z(t.combatants.get(n.combatantId)) - o;
});
}
function ke(e, t) {
const a = new Set(t.combatants.map((n) => n.id));
return e.filter((n) => a.has(n.combatantId));
}
function An() {
try {
const e = localStorage.getItem(qe);
if (!e) return null;
const t = JSON.parse(e), a = q(t == null ? void 0 : t.left, NaN), n = q(t == null ? void 0 : t.top, NaN);
return !Number.isFinite(a) || !Number.isFinite(n) ? null : { left: a, top: n };
} catch {
return null;
}
}
function xn(e) {
try {
localStorage.setItem(
qe,
JSON.stringify({ left: Math.round(e.left), top: Math.round(e.top) })
);
} catch {
}
}
function Pn() {
try {
localStorage.removeItem(qe);
} catch {
}
}
function In(e, t) {
const n = t.offsetWidth || 300, o = t.offsetHeight || 220, s = Math.max(8, window.innerWidth - n - 8), i = Math.max(8, window.innerHeight - o - 8);
return {
left: Math.min(Math.max(8, e.left), s),
top: Math.min(Math.max(8, e.top), i)
};
}
function nt(e, t) {
const a = e.get(0);
if (!a) return;
const n = In(t, a);
e.css({
left: `${n.left}px`,
top: `${n.top}px`,
right: "auto",
bottom: "auto"
});
}
function Ln(e) {
const t = e.get(0);
if (!t) return;
const a = e.find(".rw-imp-mini-head");
if (!a.length) return;
const n = An();
n && nt(e, n);
let o = !1, s = 0, i = 0, l = 0, r = 0;
a.on("mousedown", (c) => {
var g;
const u = c;
if (u.button !== 0 || (g = u.target) != null && g.closest("button")) return;
u.preventDefault();
const d = t.getBoundingClientRect();
s = u.clientX, i = u.clientY, l = d.left, r = d.top, o = !0, e.addClass("dragging"), e.css({ left: `${l}px`, top: `${r}px`, right: "auto", bottom: "auto" }), $(document).on("mousemove.ringworldImpulseHudDrag", (m) => {
if (!o) return;
const f = m, y = f.clientX - s, b = f.clientY - i;
nt(e, { left: l + y, top: r + b });
}), $(document).one("mouseup.ringworldImpulseHudDrag", () => {
o = !1, e.removeClass("dragging"), $(document).off("mousemove.ringworldImpulseHudDrag");
const m = t.getBoundingClientRect();
xn({ left: m.left, top: m.top });
});
}), a.on("dblclick", () => {
Pn(), e.css({ left: "", top: "", right: "", bottom: "" });
});
}
function Pt(e, t) {
return ae(ke(t, e), e).map((a) => {
const n = e.combatants.get(a.combatantId);
return {
combatantId: a.combatantId,
name: String((n == null ? void 0 : n.name) || "Unknown"),
label: Re(a.kind),
completeImpulse: a.completeImpulse,
dex: z(n)
};
});
}
function It(e, t = 8) {
return e.length ? e.slice(0, t).map((a) => `
<div class="rw-imp-mini-row" data-combatant-id="${Y(a.combatantId)}">
<span class="rw-imp-mini-name">${Y(a.name)}</span>
<span class="rw-imp-mini-action">${Y(a.label)} @ ${a.completeImpulse}</span>
</div>
`).join("") : '<div class="rw-imp-mini-empty">No actions queued.</div>';
}
function Lt(e, t) {
var n;
const a = !((n = game.user) != null && n.isGM);
e.find(".rw-mini-next").prop("disabled", a).on("click", async (o) => {
o.preventDefault(), await Ie(t, "next");
}), e.find(".rw-mini-step").prop("disabled", a).on("click", async (o) => {
o.preventDefault(), await Ie(t, "step");
}), e.find(".rw-mini-reset").prop("disabled", a).on("click", async (o) => {
o.preventDefault(), await Mt(t);
});
}
function Mn(e) {
var s;
const t = (s = ui == null ? void 0 : ui.chat) == null ? void 0 : s.element;
if (!(t != null && t.length) || (t.find(".ringworld-impulse-chat-marker").remove(), !e)) return;
const a = ie(e), n = Pt(e, a.queue), o = $(`
<section class="ringworld-impulse-chat-marker">
<div class="rw-imp-mini-head">
<strong>Impulse ${a.currentImpulse}</strong>
<span>Queued ${n.length}</span>
</div>
<div class="rw-imp-mini-list">${It(n, 5)}</div>
<div class="rw-imp-mini-controls">
<button type="button" class="rw-mini-next">Next</button>
<button type="button" class="rw-mini-step">+1</button>
<button type="button" class="rw-mini-reset">Reset</button>
</div>
</section>
`);
Lt(o, e), t.prepend(o);
}
function Cn(e) {
if ($("#ringworld-impulse-hud").remove(), !e) return;
const a = ie(e), n = Pt(e, a.queue), o = $(`
<section id="ringworld-impulse-hud" class="ringworld-impulse-hud">
<div class="rw-imp-mini-head">
<strong>Impulse ${a.currentImpulse}</strong>
<span>Queued ${n.length}</span>
</div>
<div class="rw-imp-mini-list">${It(n, 8)}</div>
<div class="rw-imp-mini-controls">
<button type="button" class="rw-mini-next">Next Completion</button>
<button type="button" class="rw-mini-step">+1 Impulse</button>
<button type="button" class="rw-mini-reset">Break / Reset</button>
</div>
</section>
`);
Lt(o, e), $("body").append(o), Ln(o);
}
function E(e) {
Mn(e), Cn(e);
}
async function ve() {
var e, t;
return (e = game.user) != null && e.isGM ? !0 : ((t = ui.notifications) == null || t.warn("Only the GM can modify Ringworld impulse sequencing."), !1);
}
async function ot(e, t, a, n) {
var w, p, h, A;
if (!await ve()) return;
const o = e.combatants.get(t);
if (!o) return;
const s = ie(e), i = s.currentImpulse, l = await xt(o), r = At(o), c = q((p = (w = o.flags) == null ? void 0 : w[x]) == null ? void 0 : p.impulseModifier, 0), u = Math.max(1, l + r), g = Math.max(1, (n ? u : 1) + c), m = i + g, f = {
id: foundry.utils.randomID(),
actorId: ((h = o.actor) == null ? void 0 : h.id) ?? null,
combatantId: t,
kind: a,
major: n,
startImpulse: i,
completeImpulse: m
}, y = ke(
s.queue.filter((S) => S.combatantId !== t),
e
);
y.push(f), await re(e, {
currentImpulse: i,
queue: ae(y, e)
}), await o.update({
[`flags.${x}.pendingEventId`]: f.id,
[`flags.${x}.pendingKind`]: a,
[`flags.${x}.actionRanking`]: l,
[`flags.${x}.effectiveActionRanking`]: u
});
const b = n ? `AR ${l} + Enc ${r}${c ? ` ${c > 0 ? "+" : ""} Imp ${c}` : ""}` : `Minor 1${c ? ` ${c > 0 ? "+" : ""} Imp ${c}` : ""}`;
(A = ui.notifications) == null || A.info(
`${o.name}: ${Re(a)} completes on impulse ${m} (${b} => ${g}).`
), E(e);
}
async function $n(e, t) {
if (!await ve()) return;
const a = e.combatants.get(t);
if (!a) return;
const n = ie(e), o = ke(
n.queue.filter((s) => s.combatantId !== t),
e
);
await re(e, {
currentImpulse: n.currentImpulse,
queue: ae(o, e)
}), await a.update({
[`flags.${x}.pendingEventId`]: null,
[`flags.${x}.pendingKind`]: "",
[`flags.${x}.aimState`]: {
active: !1,
targetId: null,
sinceImpulse: null
}
}), E(e);
}
async function Rn(e, t, a) {
var i;
const n = e.combatants.get(t.combatantId);
if (!n) return;
const o = {
[`flags.${x}.pendingEventId`]: null,
[`flags.${x}.pendingKind`]: ""
};
t.kind === "aim" && (o[`flags.${x}.aimState`] = {
active: !0,
targetId: t.targetTokenId ?? null,
sinceImpulse: a
}), await n.update(o);
const s = `
<div class="ringworld-chat-card">
<h3>Impulse ${a} Completion</h3>
<p><strong>${Y(n.name)}</strong> completes <strong>${Y(Re(t.kind))}</strong>.</p>
<p><small>DEX ${z(n)} | AR ${St(n)}</small></p>
</div>
`;
await ChatMessage.create({
user: (i = game.user) == null ? void 0 : i.id,
speaker: ChatMessage.getSpeaker({ actor: n.actor ?? void 0 }),
content: s
});
}
async function Ie(e, t) {
var c;
if (!await ve()) return;
const a = ie(e), n = ae(ke(a.queue, e), e), o = a.currentImpulse;
let s = o + 1;
if (t === "next") {
const u = n.map((d) => d.completeImpulse).filter((d) => d >= o).sort((d, g) => d - g)[0];
Number.isFinite(u) && (s = u);
}
const i = n.filter((u) => u.completeImpulse === s), l = n.filter((u) => u.completeImpulse !== s);
await re(e, {
currentImpulse: s,
queue: l
});
const r = ae(i, e);
for (const u of r)
await Rn(e, u, s);
r.length || (c = ui.notifications) == null || c.info(`Impulse advanced to ${s}.`), E(e);
}
async function Mt(e) {
if (await ve()) {
await re(e, { currentImpulse: 1, queue: [] });
for (const t of e.combatants)
await t.update({
[`flags.${x}.pendingEventId`]: null,
[`flags.${x}.pendingKind`]: "",
[`flags.${x}.aimState`]: {
active: !1,
targetId: null,
sinceImpulse: null
},
[`flags.${x}.actionRanking`]: Ke(z(t)),
[`flags.${x}.effectiveActionRanking`]: null,
[`flags.${x}.impulseModifier`]: 0
});
E(e);
}
}
function Tn(e, t) {
var u;
const a = "find" in t ? t : $(t);
if (!(a != null && a.length)) return;
a.addClass("ringworld-impulse-mode ringworld-impulse-only");
const n = (e == null ? void 0 : e.viewed) ?? game.combat;
if (!n) return;
const o = ie(n), s = ae(ke(o.queue, n), n);
s.length !== o.queue.length && re(n, { currentImpulse: o.currentImpulse, queue: s }), a.find(
".token-initiative, .combatant-initiative, .initiative, [data-action='rollInitiative'], [data-control='rollInitiative'], .combatant .fa-dice-d20"
).addClass("rw-hide-initiative"), a.find(".ringworld-impulse-panel").remove();
const i = (u = game.user) != null && u.isGM ? "" : "disabled", l = n.combatants.map((d) => {
var M, D, T, H;
const g = St(d), m = At(d), f = q((D = (M = d.flags) == null ? void 0 : M[x]) == null ? void 0 : D.impulseModifier, 0), y = Math.max(1, g + m), b = z(d), w = s.find((L) => L.combatantId === d.id), p = w ? `${Re(w.kind)} @ ${w.completeImpulse}` : "Ready", h = (H = (T = d.flags) == null ? void 0 : T[x]) == null ? void 0 : H.aimState, A = h != null && h.active ? `Aiming since ${h.sinceImpulse}` : "Not aiming", S = vn.map((L) => {
const F = w != null && w.major && (w == null ? void 0 : w.kind) === L.value ? "selected" : "";
return `<option value="${L.value}" ${F}>${L.label}</option>`;
}).join(""), I = Sn.map((L) => {
const F = !(w != null && w.major) && (w == null ? void 0 : w.kind) === L.value ? "selected" : "";
return `<option value="${L.value}" ${F}>${L.label}</option>`;
}).join("");
return `
<div class="rw-imp-row" data-combatant-id="${d.id}">
<div class="rw-imp-row-head">
<span class="rw-imp-name">${Y(d.name)}</span>
<span class="rw-imp-meta">DEX ${b} | AR ${g} + Enc ${m} = ${y} | Imp Mod ${f >= 0 ? `+${f}` : f} | ${Y(p)} | ${Y(A)}</span>
</div>
<div class="rw-imp-row-controls">
<select class="rw-major-kind" ${i}>${S}</select>
<select class="rw-minor-kind" ${i}>${I}</select>
<button type="button" class="rw-major-start" ${i}>Major</button>
<button type="button" class="rw-minor-start" ${i}>Minor</button>
<button type="button" class="rw-cancel-start" ${i}>Cancel</button>
<label>Imp Mod <input type="number" class="rw-imp-mod" value="${f}" ${i} /></label>
<button type="button" class="rw-save-mods" ${i}>Save Mods</button>
</div>
</div>
`;
}).join(""), r = $(`
<section class="ringworld-impulse-panel">
<div class="rw-impulse-top">
<div class="rw-impulse-clock">Impulse <strong>${o.currentImpulse}</strong></div>
<div class="rw-impulse-queue">Queued: ${s.length}</div>
<div class="rw-impulse-controls">
<button type="button" class="rw-advance-next" ${i}>Next Completion</button>
<button type="button" class="rw-advance-step" ${i}>+1 Impulse</button>
<button type="button" class="rw-reset-sequence" ${i}>Break / Reset</button>
</div>
</div>
<div class="rw-imp-list">${l}</div>
</section>
`);
a.prepend(r);
const c = a.get(0);
if (c)
for (const d of Array.from(c.children))
d.classList.contains("ringworld-impulse-panel") || d.classList.add("rw-hide-core-tracker");
r.find(".rw-advance-next").on("click", async (d) => {
d.preventDefault(), await Ie(n, "next");
}), r.find(".rw-advance-step").on("click", async (d) => {
d.preventDefault(), await Ie(n, "step");
}), r.find(".rw-reset-sequence").on("click", async (d) => {
d.preventDefault(), await Mt(n);
}), r.find(".rw-major-start").on("click", async (d) => {
d.preventDefault();
const g = d.currentTarget.closest(".rw-imp-row");
if (!g) return;
const m = String(g.getAttribute("data-combatant-id") || ""), f = g.querySelector(".rw-major-kind"), y = String((f == null ? void 0 : f.value) || "draw");
await ot(n, m, y, !0);
}), r.find(".rw-minor-start").on("click", async (d) => {
d.preventDefault();
const g = d.currentTarget.closest(".rw-imp-row");
if (!g) return;
const m = String(g.getAttribute("data-combatant-id") || ""), f = g.querySelector(".rw-minor-kind"), y = String((f == null ? void 0 : f.value) || "minorCustom");
await ot(n, m, y, !1);
}), r.find(".rw-cancel-start").on("click", async (d) => {
d.preventDefault();
const g = d.currentTarget.closest(".rw-imp-row");
if (!g) return;
const m = String(g.getAttribute("data-combatant-id") || "");
await $n(n, m);
}), r.find(".rw-save-mods").on("click", async (d) => {
if (d.preventDefault(), !await ve()) return;
const g = d.currentTarget.closest(".rw-imp-row");
if (!g) return;
const m = String(g.getAttribute("data-combatant-id") || ""), f = n.combatants.get(m);
if (!f) return;
const y = g.querySelector(".rw-imp-mod"), b = Math.trunc(q(y == null ? void 0 : y.value, 0));
await f.update({
[`flags.${x}.impulseModifier`]: b
});
}), E(n);
}
function Dn() {
Hooks.on("renderCombatTracker", (e, t) => {
Tn(e, t);
}), Hooks.on("createCombat", async (e) => {
await re(e, { currentImpulse: 1, queue: [] }), E(e);
}), Hooks.on("createCombatant", async (e) => {
await xt(e), E(game.combat ?? null);
}), Hooks.on("deleteCombatant", async () => {
E(game.combat ?? null);
}), Hooks.on("updateCombat", (e) => {
E(e ?? game.combat ?? null);
}), Hooks.on("deleteCombat", () => {
E(null);
}), Hooks.on("renderChatLog", () => {
E(game.combat ?? null);
}), Hooks.on("canvasReady", () => {
E(game.combat ?? null);
}), Hooks.on("updateActor", (e) => {
var n;
const t = game.combat;
if (!t) return;
t.combatants.some((o) => {
var s;
return ((s = o.actor) == null ? void 0 : s.id) === e.id;
}) && ((n = ui.combat) == null || n.render(!0), E(t));
}), Hooks.on("updateItem", (e) => {
var o;
const t = e == null ? void 0 : e.parent;
if (!t) return;
const a = game.combat;
if (!a) return;
a.combatants.some((s) => {
var i;
return ((i = s.actor) == null ? void 0 : i.id) === t.id;
}) && ((o = ui.combat) == null || o.render(!0), E(a));
});
}
const st = 2;
function Ct(e) {
return String(e ?? "").trim().toLowerCase();
}
function Hn() {
var t;
const e = /* @__PURE__ */ new Map();
for (const a of game.items ?? []) {
if (a.type !== "weapon" && a.type !== "armor" && a.type !== "gear") continue;
const n = `${a.type}:${Ct(a.name)}`;
e.set(n, Number(((t = a.system) == null ? void 0 : t.weight) ?? 0) || 0);
}
return e;
}
async function Nn() {
var a, n, o, s;
if (!((a = game.user) != null && a.isGM)) return 0;
const e = Hn();
let t = 0;
for (const i of game.actors ?? []) {
const l = [];
for (const r of i.items ?? []) {
if (r.type !== "weapon" && r.type !== "armor" && r.type !== "gear") continue;
const c = { _id: r.id };
let u = !1;
if (((n = r.system) == null ? void 0 : n.equipped) === void 0 && (c["system.equipped"] = !0, u = !0), ((o = r.system) == null ? void 0 : o.weight) === void 0) {
const d = `${r.type}:${Ct(r.name)}`;
c["system.weight"] = Number(e.get(d) ?? 0), u = !0;
}
((s = r.system) == null ? void 0 : s.stowed) === void 0 && (c["system.stowed"] = !1, u = !0), u && l.push(c);
}
l.length && (await i.updateEmbeddedDocuments("Item", l), t += l.length);
}
return t;
}
function Le(e, t, a, n) {
if (!e) return;
const o = "find" in e ? e : $(e);
if (!o.length || o.find(`.${t}`).length) return;
const s = $(`<button type="button" class="ringworld-directory-button ${t}">${a}</button>`);
s.on("click", async () => {
await n();
});
const l = [
o.find(".directory-footer"),
o.find("footer.directory-footer"),
o.find("footer"),
o.find(".footer-actions"),
o.find(".header-actions"),
o.find(".action-buttons")
].find((c) => c.length);
if (l) {
l.first().append(s);
return;
}
let r = o.find(".ringworld-directory-controls");
r.length || (r = $('<div class="ringworld-directory-controls"></div>'), o.append(r)), r.first().append(s);
}
async function $t() {
await Be({ notify: !0 });
}
async function Rt() {
var t;
const e = await bt();
(t = e == null ? void 0 : e.sheet) == null || t.render(!0);
}
function ne() {
var a, n, o;
if (!((a = game.user) != null && a.isGM)) return;
const e = (n = ui.items) == null ? void 0 : n.element;
Le(e, "ringworld-seed-items", "Seed Ringworld Items", $t);
const t = (o = ui.actors) == null ? void 0 : o.element;
Le(t, "ringworld-random-actor", "Create Random Explorer", Rt);
}
function On(e) {
var o, s, i, l;
const t = (o = getDocumentClass("Item")) == null ? void 0 : o.TYPES;
if (Array.isArray(t)) return t.includes(e);
const a = (i = (s = game == null ? void 0 : game.system) == null ? void 0 : s.documentTypes) == null ? void 0 : i.Item;
if (Array.isArray(a)) return a.includes(e);
if (a && typeof a == "object") return Object.prototype.hasOwnProperty.call(a, e);
const n = (l = CONFIG == null ? void 0 : CONFIG.Item) == null ? void 0 : l.dataModels;
return !!(n && typeof n == "object" && Object.prototype.hasOwnProperty.call(n, e));
}
Hooks.once("init", async () => {
var o, s, i, l, r, c, u, d;
console.log("ringworld | Initializing Ringworld system"), Dn();
const e = (s = (o = foundry == null ? void 0 : foundry.documents) == null ? void 0 : o.collections) == null ? void 0 : s.Actors, t = (l = (i = foundry == null ? void 0 : foundry.documents) == null ? void 0 : i.collections) == null ? void 0 : l.Items, a = (c = (r = foundry == null ? void 0 : foundry.appv1) == null ? void 0 : r.sheets) == null ? void 0 : c.ActorSheet, n = (d = (u = foundry == null ? void 0 : foundry.appv1) == null ? void 0 : u.sheets) == null ? void 0 : d.ItemSheet;
if (CONFIG.ringworld = {
hitLocations: Ce
}, CONFIG.Combat.initiative = foundry.utils.mergeObject(CONFIG.Combat.initiative ?? {}, {
formula: "1d100",
decimals: 0
}, { inplace: !1 }), CONFIG.Actor.documentClass = Va, CONFIG.Item.documentClass = Ga, !e || !t || !a || !n) {
console.error("ringworld | Foundry sheet registries were not available during init.");
return;
}
e.unregisterSheet("core", a), e.registerSheet("ringworld", nn, {
types: ["explorer"],
makeDefault: !0
}), e.registerSheet("ringworld", sn, {
types: ["creature"],
makeDefault: !0
}), e.registerSheet("ringworld", ln, {
types: ["vehicle"],
makeDefault: !0
}), t.unregisterSheet("core", n), t.registerSheet("ringworld", un, {
types: ["skill"],
makeDefault: !0
}), t.registerSheet("ringworld", dn, {
types: ["weapon"],
makeDefault: !0
}), t.registerSheet("ringworld", pn, {
types: ["armor"],
makeDefault: !0
}), t.registerSheet("ringworld", yn, {
types: ["gear"],
makeDefault: !0
}), t.registerSheet("ringworld", wn, {
types: ["species"],
makeDefault: !0
}), On("pursuit") && t.registerSheet("ringworld", kn, {
types: ["pursuit"],
makeDefault: !0
}), game.settings.register("ringworld", "catalogSeedVersion", {
scope: "world",
config: !1,
type: Number,
default: 0
}), game.settings.register("ringworld", "encumbranceDataMigrationVersion", {
scope: "world",
config: !1,
type: Number,
default: 0
}), Handlebars.registerHelper("eq", (g, m) => g === m), Handlebars.registerHelper("or", (g, m) => !!(g || m));
});
Hooks.once("ready", async () => {
var t, a, n, o, s;
const e = "systems/ringworld/resources/hit-locations/humanoid.json";
try {
const i = await fetch(e);
if (i.ok) {
const l = await i.json();
CONFIG.ringworld.hitLocations = l.entries ?? Ce;
}
} catch {
console.warn(`ringworld | Could not load ${e}, using built-in table`);
}
if ((t = game.user) != null && t.isGM) {
for (const r of game.combats ?? []) {
const c = String(((n = (a = r == null ? void 0 : r.settings) == null ? void 0 : a.initiative) == null ? void 0 : n.formula) ?? "").trim();
(!c || c.toLowerCase() === "undefined") && await r.update({
"settings.initiative.formula": "1d100",
"settings.initiative.decimals": 0
});
}
if (Number(game.settings.get("ringworld", "catalogSeedVersion") ?? 0) < De && (await Be({ notify: !0 })).added >= 0 && await game.settings.set("ringworld", "catalogSeedVersion", De), Number(game.settings.get("ringworld", "encumbranceDataMigrationVersion") ?? 0) < st) {
const r = await Nn();
await game.settings.set("ringworld", "encumbranceDataMigrationVersion", st), r > 0 && ((o = ui.notifications) == null || o.info(`Ringworld encumbrance migration updated ${r} actor items.`));
}
}
ne(), window.setTimeout(() => ne(), 300), game.ringworld = {
...game.ringworld ?? {},
seedWorldCatalog: async (i) => Be(i),
createRandomExplorer: async () => bt(),
catalogVersion: De,
systemId: "ringworld",
systemVersion: String(((s = game.system) == null ? void 0 : s.version) ?? "0.0.0")
}, Hooks.callAll("ringworldReady", game.ringworld);
});
Hooks.on("createActor", async (e, t, a) => {
var n, o;
if ((n = game.user) != null && n.isGM && a === game.user.id && !(!e || e.type !== "explorer" && e.type !== "creature"))
try {
const s = await ((o = e.ensureBaseSkills) == null ? void 0 : o.call(e));
Number(s) > 0 && console.log(`ringworld | Added ${s} base skills to ${e.name}`);
} catch (s) {
console.error("ringworld | Failed to auto-populate base skills", s);
}
});
Hooks.on("preCreateItem", (e) => {
if (e.type !== "skill") return;
const t = {}, a = String(e.name || "Skill"), n = String(e.img || "");
t.img = Oe(a, n);
const o = $e(a), s = e.system ?? {}, i = Number((s == null ? void 0 : s.value) ?? NaN), l = String((s == null ? void 0 : s.category) ?? "").trim();
t.system = {
value: Number.isFinite(i) ? i : (o == null ? void 0 : o.base) ?? 0,
category: l || ((o == null ? void 0 : o.category) ?? "Technical"),
specialization: String((s == null ? void 0 : s.specialization) ?? (o == null ? void 0 : o.specialization) ?? "")
}, t.img || (t.img = W), e.updateSource(t);
});
Hooks.on("renderItemDirectory", (e, t) => {
var a;
(a = game.user) != null && a.isGM && (Le(t, "ringworld-seed-items", "Seed Ringworld Items", $t), ne());
});
Hooks.on("renderActorDirectory", (e, t) => {
var a;
(a = game.user) != null && a.isGM && (Le(t, "ringworld-random-actor", "Create Random Explorer", Rt), ne());
});
Hooks.on("renderSidebar", () => {
ne();
});
Hooks.on("changeSidebarTab", (e) => {
var a;
const t = String((e == null ? void 0 : e.tabName) ?? (e == null ? void 0 : e.id) ?? ((a = e == null ? void 0 : e.options) == null ? void 0 : a.id) ?? "");
(t === "items" || t === "actors") && ne();
});
//# sourceMappingURL=ringworld.mjs.map