Cypher actor @Embed (#1)

Reviewed-on: #1
Co-authored-by: Mike Bloy <mike@bloy.org>
Co-committed-by: Mike Bloy <mike@bloy.org>
This commit was merged in pull request #1.
This commit is contained in:
2026-09-26 04:47:30 +00:00
committed by mike
parent 073c341ca3
commit fda78257f9
11 changed files with 1649 additions and 3 deletions
+149
View File
@@ -0,0 +1,149 @@
import {
preloadTemplatePaths,
normalizeTemplatePaths,
MODULE_ID,
log,
} from "./module.js";
const TEMPLATE_FILES = ["actor.hbs", "actor-inline.hbs"];
const TEMPLATE_BASE = "embeds";
const TEMPLATES = normalizeTemplatePaths(TEMPLATE_FILES, TEMPLATE_BASE);
function csLocKey(name) {
return `CYPHERSYSTEM.${name[0].toUpperCase()}${name.slice(1)}`;
}
function enrich(text, options) {
return foundry.applications.ux.TextEditor.implementation.enrichHTML(
text,
options,
);
}
async function buildActorEmbedHTML(doc, content, config, options) {
// Types: community, pc, marker, npc, vehicle
// LIMITED access can see Name and Description
const noPermission = !doc.testUserPermission(
game.user,
CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED,
);
if (noPermission) {
const broken = document.createElement("span");
broken.classList.add("broken", "content-embed");
const i = document.createElement("i");
i.classList.add("fa-solid", "fa-circle-exclamation");
i.inert = true;
broken.append(
i,
_loc(`${MODULE_ID}.embed.noPermission`, { uuid: doc.uuid }),
);
return broken;
}
let icon = "";
switch (doc.type) {
case "pc":
icon = "fa-solid fa-user";
break;
case "npc":
icon = "fa-solid fa-user-secret";
break;
case "community":
icon = "fa-solid fa-tree-city";
break;
case "marker":
icon = "fa-solid fa-thumbtack";
break;
case "vehicle":
icon = "fa-solid fa-car-side";
break;
default:
icon = "fa-solid fa-person";
}
const context = {
config: { ...config },
actor: doc,
actorName: config?.label ?? doc.name,
sentence: doc.system.basic,
stats: [],
pools: [],
enriched: {},
options,
viewFullDocument: doc.testUserPermission(
game.user,
CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER,
),
icon,
user: game.user,
};
for (const basic of ["rank", "level", "tier"]) {
const value = doc.system.basic?.[basic];
if (value !== undefined && value !== null) {
context.stats.push({
name: csLocKey(basic),
value,
});
}
}
if (!config.inline) {
context.enriched.notes = await enrich(doc.system.notes, options);
context.enriched.description = await enrich(
doc.system.description,
options,
);
if (doc.type === "pc") {
context.enriched.gmNotes = await enrich(doc.system.gmNotes, options);
}
}
for (const pool of [
"health",
"infrastructure",
"might",
"speed",
"intellect",
]) {
if (doc.system.pools?.[pool]) {
context.pools.push({
name: csLocKey(pool),
value: doc.system.pools[pool],
});
}
}
let armor = doc.system.combat?.armor;
if (armor !== undefined && armor !== null) {
const ratingTotal = armor?.ratingTotal;
if (ratingTotal !== undefined && ratingTotal != null) {
armor = ratingTotal;
}
context.stats.push({
name: csLocKey("armor"),
value: armor,
noInline: true,
});
}
let damage = doc.system.combat?.damage;
if (damage !== undefined && damage !== null) {
context.stats.push({
name: csLocKey("damage"),
value: damage,
noInline: true,
});
}
const template = config.inline
? TEMPLATES["actor-inline.hbs"]
: TEMPLATES["actor.hbs"];
const el = document.createElement(config.inline ? "span" : "div");
el.classList.add(`${MODULE_ID}-embed`, "content-embed");
let innerHTML = await renderTemplate(template, context);
el.innerHTML = innerHTML;
return el;
}
function initializeEmbeds() {
CONFIG.Actor.embedHandlers.push(buildActorEmbedHTML);
}
Hooks.once("init", async () => {
log(`Initializing ${MODULE_ID}`);
initializeEmbeds();
preloadTemplatePaths(TEMPLATES);
});