create-lt-adventure 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -0
- package/addons/github-workflow/addon.json +5 -0
- package/addons/github-workflow/main.yml +48 -0
- package/addons/github-workflow/setup.ts +112 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +283 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/options.d.ts +21 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +66 -0
- package/dist/options.js.map +1 -0
- package/package.json +66 -0
- package/templates/vite/.env.example +2 -0
- package/templates/vite/README.md +0 -0
- package/templates/vite/assets/journals/Read-Aloud_Corner.webp +0 -0
- package/templates/vite/assets/journals/Sidebar_Frame_Brown.webp +0 -0
- package/templates/vite/assets/journals/Sidebar_Frame_Green.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-000.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-001.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-002.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-003.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-004.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-005.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-006.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-007.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-008.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-009.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-010.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-011.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-012.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-013.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-014.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-015.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-016.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-017.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-018.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-019.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-020.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-021.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-022.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-023.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-024.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-025.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-026.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-027.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-028.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-029.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-030.webp +0 -0
- package/templates/vite/assets/journals/borders/panel-border-031.webp +0 -0
- package/templates/vite/assets/journals/fonts/Candara.ttf +0 -0
- package/templates/vite/assets/journals/fonts/CoinageCapsKrugerGray.ttf +0 -0
- package/templates/vite/assets/journals/fonts/GeizerModifiedO.otf +0 -0
- package/templates/vite/assets/journals/fonts/Kings Caslon/Kings_Caslon_Bold.otf +0 -0
- package/templates/vite/assets/journals/fonts/Kings Caslon/Kings_Caslon_BoldItalic.otf +0 -0
- package/templates/vite/assets/journals/fonts/Kings Caslon/Kings_Caslon_Italic.otf +0 -0
- package/templates/vite/assets/journals/fonts/Kings Caslon/Kings_Caslon_Regular.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/ModestoText_Bold.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/ModestoText_BoldItalic.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/ModestoText_Light.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/ModestoText_LightItalic.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/ModestoText_Medium.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/ModestoText_MediumItalic.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/Modesto_Condensed_Bold.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/Modesto_Condensed_Light.otf +0 -0
- package/templates/vite/assets/journals/fonts/Modesto/Modesto_Condensed_Regular.otf +0 -0
- package/templates/vite/assets/journals/headers/bg-header-length-01.svg +61 -0
- package/templates/vite/assets/journals/headers/bg-header-length-02.svg +59 -0
- package/templates/vite/assets/journals/headers/bg-header-length-03.svg +66 -0
- package/templates/vite/assets/journals/headers/bg-header-length-04.svg +58 -0
- package/templates/vite/assets/journals/headers/bg-header-length-05.svg +75 -0
- package/templates/vite/assets/journals/headers/bg-header-length-06.svg +73 -0
- package/templates/vite/assets/journals/headers/bg-header-length-07.svg +69 -0
- package/templates/vite/assets/journals/masks/flipped/mask-brush-02.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-brush-03.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-brush-04.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-brush-05.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-brush-06.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-hard-01.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-hard-02.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-hard-03.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-hard-04.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-hard-05.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-hard-06.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-soft-01.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-soft-02.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-soft-03.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-soft-04.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-soft-05.webp +0 -0
- package/templates/vite/assets/journals/masks/flipped/mask-soft-06.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-brush-02.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-brush-03.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-brush-04.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-brush-05.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-brush-06.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-hard-01.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-hard-02.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-hard-03.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-hard-04.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-hard-05.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-hard-06.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-soft-01.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-soft-02.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-soft-03.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-soft-04.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-soft-05.webp +0 -0
- package/templates/vite/assets/journals/masks/mask-soft-06.webp +0 -0
- package/templates/vite/assets/setup.webp +0 -0
- package/templates/vite/bun.lock +983 -0
- package/templates/vite/lang/en.json +1 -0
- package/templates/vite/module.json +36 -0
- package/templates/vite/package.json +29 -0
- package/templates/vite/scripts/extractPacks.mjs +50 -0
- package/templates/vite/scripts/onCreate.mjs +14 -0
- package/templates/vite/scripts/symlink.mjs +79 -0
- package/templates/vite/src/adventureSheet/index.js +498 -0
- package/templates/vite/src/index.js +5 -0
- package/templates/vite/src/misc/fonts.js +50 -0
- package/templates/vite/src/misc/prosemirror.js +47 -0
- package/templates/vite/src/module.css +284 -0
- package/templates/vite/vite.config.ts +110 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "module-template",
|
|
3
|
+
"title": "Module Template",
|
|
4
|
+
"description": "",
|
|
5
|
+
"authors": [
|
|
6
|
+
{
|
|
7
|
+
"name": "Vauxs",
|
|
8
|
+
"discord": "vauxs",
|
|
9
|
+
"twitter": "@ThatVauxs",
|
|
10
|
+
"email": "mrvauxs@gmail.com",
|
|
11
|
+
"ko-fi": "MrVauxs",
|
|
12
|
+
"patreon": "MrVauxs"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"version": "dev",
|
|
16
|
+
"compatibility": {
|
|
17
|
+
"minimum": "13",
|
|
18
|
+
"verified": "13"
|
|
19
|
+
},
|
|
20
|
+
"esmodules": [],
|
|
21
|
+
"styles": [],
|
|
22
|
+
"relationships": {},
|
|
23
|
+
"packs": [],
|
|
24
|
+
"packFolders": [],
|
|
25
|
+
"flags": {
|
|
26
|
+
"canUpload": true,
|
|
27
|
+
"hotReload": {
|
|
28
|
+
"extensions": ["css", "json"],
|
|
29
|
+
"paths": ["styles"]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"url": "",
|
|
33
|
+
"bugs": "",
|
|
34
|
+
"manifest": "",
|
|
35
|
+
"download": ""
|
|
36
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-new-module",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"private": true,
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "vite build",
|
|
8
|
+
"dev": "vite",
|
|
9
|
+
"extract": "node scripts/extractPacks.mjs",
|
|
10
|
+
"symlink": "node scripts/symlink.mjs"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@clack/prompts": "^1.0.0-alpha.9",
|
|
14
|
+
"@foundryvtt/foundryvtt-cli": "^3.0.2",
|
|
15
|
+
"@types/jquery": "^3.5.33",
|
|
16
|
+
"dotenv": "^17.2.3",
|
|
17
|
+
"foundry-pf2e": "github:7H3LaughingMan/foundry-pf2e",
|
|
18
|
+
"foundryvtt-sync": "https://github.com/MrVauxs/FoundryVTT-Sync",
|
|
19
|
+
"kolorist": "^1.8.0",
|
|
20
|
+
"postcss-preset-env": "^10.6.0",
|
|
21
|
+
"postcss-replace": "^2.0.1",
|
|
22
|
+
"typescript": "^5.9.3",
|
|
23
|
+
"vite": "^7.3.0"
|
|
24
|
+
},
|
|
25
|
+
"browserslist": [
|
|
26
|
+
">5%",
|
|
27
|
+
"not IE 11"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import * as p from "@clack/prompts";
|
|
7
|
+
import { extractPack } from "@foundryvtt/foundryvtt-cli";
|
|
8
|
+
import { yellow } from "kolorist";
|
|
9
|
+
// import moduleJSON from "../module.json" with { type: "json" };
|
|
10
|
+
|
|
11
|
+
const foundryDataDir = "packs/";
|
|
12
|
+
const jsonDataDir = "data/";
|
|
13
|
+
|
|
14
|
+
p.intro(`Extracting ${foundryDataDir} into ${jsonDataDir}...`)
|
|
15
|
+
|
|
16
|
+
const outDir = path.resolve(process.cwd());
|
|
17
|
+
const packsCompiled = path.resolve(outDir, foundryDataDir);
|
|
18
|
+
if (!existsSync(packsCompiled)) {
|
|
19
|
+
p.log.warn("Packs directory does not exist in the build!")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const packFolders = await fs.readdir(packsCompiled);
|
|
23
|
+
|
|
24
|
+
if (packFolders.length === 0) p.log.info("No packs to extract!")
|
|
25
|
+
|
|
26
|
+
await p.tasks(
|
|
27
|
+
packFolders.map(pack => ({
|
|
28
|
+
title: `Extracting ${pack}...`,
|
|
29
|
+
task: async () => {
|
|
30
|
+
if (!existsSync(`${jsonDataDir}/${pack}`)) {
|
|
31
|
+
await fs.mkdir(`${jsonDataDir}/${pack}`);
|
|
32
|
+
}
|
|
33
|
+
await extractPack(
|
|
34
|
+
path.resolve(packsCompiled, pack),
|
|
35
|
+
`${jsonDataDir}/${pack}`,
|
|
36
|
+
{
|
|
37
|
+
expandAdventures: true,
|
|
38
|
+
omitVolatile: true,
|
|
39
|
+
folders: false,
|
|
40
|
+
clean: true,
|
|
41
|
+
log: false
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
return `Extracted ${yellow(pack)}!`
|
|
45
|
+
}
|
|
46
|
+
})),
|
|
47
|
+
{}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
p.outro("Finished!");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const mod = (await Bun.file("../module.json").json());
|
|
2
|
+
const pack = (await Bun.file("../package.json").json());
|
|
3
|
+
|
|
4
|
+
// Module
|
|
5
|
+
mod.esmodules = [`dist/${mod.id}.js`];
|
|
6
|
+
mod.styles = [`dist/${mod.id}.css`];
|
|
7
|
+
|
|
8
|
+
// Package
|
|
9
|
+
pack.name = mod.id;
|
|
10
|
+
|
|
11
|
+
await Bun.write("../module.json", JSON.stringify(mod, null, "\t"));
|
|
12
|
+
await Bun.write("../package.json", JSON.stringify(pack, null, "\t"));
|
|
13
|
+
|
|
14
|
+
export { };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { lstatSync, rmSync, symlinkSync, unlinkSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
import moduleJSON from "../module.json" with { type: "json" };
|
|
7
|
+
import { yellow } from "kolorist";
|
|
8
|
+
|
|
9
|
+
p.intro(`${moduleJSON.id} symlink script`)
|
|
10
|
+
|
|
11
|
+
// Store config in user's home directory
|
|
12
|
+
const configPath = resolve(homedir(), ".foundry-symlink-config.json");
|
|
13
|
+
|
|
14
|
+
// Load last known path from config
|
|
15
|
+
let lastPath = null;
|
|
16
|
+
try {
|
|
17
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
18
|
+
lastPath = config.dataPath;
|
|
19
|
+
} catch {
|
|
20
|
+
// Config doesn't exist yet, that's fine
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const windowsInstructions = process.platform === "win32" ? " Start with a drive letter (\"C:\\\")." : "";
|
|
24
|
+
const lastFolder = lastPath ? `(last: ${lastPath})` : "";
|
|
25
|
+
const promptPath = await p.text({
|
|
26
|
+
message: `Enter the full path to your Foundry data folder.${windowsInstructions}`,
|
|
27
|
+
placeholder: lastPath,
|
|
28
|
+
initialValue: lastPath,
|
|
29
|
+
validate(val) {
|
|
30
|
+
const value = val.replace(/\W*$/, "").trim();
|
|
31
|
+
if (!value || !/\bData$/.test(value)) {
|
|
32
|
+
return (`"${value}" does not contain ${yellow('/Data')}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
let dataPath = promptPath.replace(/\W*$/, "").trim();
|
|
38
|
+
|
|
39
|
+
if (dataPath !== lastPath) {
|
|
40
|
+
// Ask if user wants to save the path
|
|
41
|
+
const shouldSave = await p.confirm({
|
|
42
|
+
initialValue: true,
|
|
43
|
+
message: `Save "${dataPath}" for future use?`,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (shouldSave) {
|
|
47
|
+
writeFileSync(configPath, JSON.stringify({ dataPath }, null, 2));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const symlinkPath = resolve(dataPath, "modules", moduleJSON.id);
|
|
52
|
+
const symlinkStats = lstatSync(symlinkPath, { throwIfNoEntry: false });
|
|
53
|
+
if (symlinkStats) {
|
|
54
|
+
const atPath = symlinkStats.isDirectory() ? "folder" : symlinkStats.isSymbolicLink() ? "symlink" : "file";
|
|
55
|
+
const proceed = await p.confirm({
|
|
56
|
+
initialValue: false,
|
|
57
|
+
message: `A "${moduleJSON.id}" ${atPath} already exists in the "modules" subfolder. Replace with new symlink?`,
|
|
58
|
+
});
|
|
59
|
+
if (!proceed) {
|
|
60
|
+
p.cancel("Aborting.");
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
if (symlinkStats?.isDirectory()) {
|
|
67
|
+
rmSync(symlinkPath, { recursive: true, force: true });
|
|
68
|
+
} else if (symlinkStats) {
|
|
69
|
+
unlinkSync(symlinkPath);
|
|
70
|
+
}
|
|
71
|
+
symlinkSync(resolve(process.cwd()), symlinkPath);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (error instanceof Error) {
|
|
74
|
+
console.error(`An error was encountered trying to create a symlink: ${error.message}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
p.outro(`Symlink successfully created at "${symlinkPath}"!`);
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
import moduleJSON from "moduleJSON";
|
|
2
|
+
|
|
3
|
+
const affectedDocuments = ["journal"];
|
|
4
|
+
|
|
5
|
+
const backgrounds = [
|
|
6
|
+
{ maxWidth: 130, image: "bg-header-length-01.svg" },
|
|
7
|
+
{ maxWidth: 150, image: "bg-header-length-02.svg" },
|
|
8
|
+
{ maxWidth: 210, image: "bg-header-length-03.svg" },
|
|
9
|
+
{ maxWidth: 250, image: "bg-header-length-04.svg" },
|
|
10
|
+
{ maxWidth: 300, image: "bg-header-length-05.svg" },
|
|
11
|
+
{ maxWidth: 400, image: "bg-header-length-06.svg" },
|
|
12
|
+
{ maxWidth: Infinity, image: "bg-header-length-07.svg" },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const DnDSheet = CONFIG.JournalEntry.sheetClasses.base['dnd5e.JournalEntrySheet5e'].cls;
|
|
16
|
+
|
|
17
|
+
export class LootTavernSheet extends DnDSheet {
|
|
18
|
+
static DEFAULT_OPTIONS = {
|
|
19
|
+
classes: [`${moduleJSON.id}-journal`],
|
|
20
|
+
actions: {
|
|
21
|
+
"openLootDevTools": this.openLootDevTools
|
|
22
|
+
},
|
|
23
|
+
window: {
|
|
24
|
+
controls: [
|
|
25
|
+
{
|
|
26
|
+
"icon": "fa-solid fa-wrench",
|
|
27
|
+
"label": "Loot Tavern Dev Tools",
|
|
28
|
+
"action": "openLootDevTools",
|
|
29
|
+
visible: () => moduleJSON.version === "dev",
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static openLootDevTools(event) {
|
|
36
|
+
event.stopPropagation(); // Don't trigger other events
|
|
37
|
+
if (event.detail > 1) return; // Ignore repeated clicks
|
|
38
|
+
const docSheetConfigWidth = foundry.applications.apps.DocumentSheetConfig.DEFAULT_OPTIONS.position.width;
|
|
39
|
+
const fields = foundry.applications.fields;
|
|
40
|
+
|
|
41
|
+
const placeholder = `modules/${moduleJSON.id}/assets/setup.webp`
|
|
42
|
+
|
|
43
|
+
const headersInput = fields.createCheckboxInput({ name: "helianaHeaders", value: this.document.getFlag(moduleJSON.id, "helianaHeaders"), placeholder })
|
|
44
|
+
const selectGroup = fields.createFormGroup({
|
|
45
|
+
input: headersInput,
|
|
46
|
+
label: "Heliana-styled Headers",
|
|
47
|
+
hint: undefined
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const imageInput = fields.createTextInput({ name: "image", value: this.document.getFlag(moduleJSON.id, "image"), placeholder })
|
|
51
|
+
const imageGroup = fields.createFormGroup({
|
|
52
|
+
input: imageInput,
|
|
53
|
+
label: "Main Image",
|
|
54
|
+
hint: undefined
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const sidebarImageInput = fields.createTextInput({ name: "sidebarImage", value: this.document.getFlag(moduleJSON.id, "sidebarImage"), placeholder })
|
|
58
|
+
const sidebarImageGroup = fields.createFormGroup({
|
|
59
|
+
input: sidebarImageInput,
|
|
60
|
+
label: "Sidebar Background Image",
|
|
61
|
+
hint: undefined
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const borderNumberInput = fields.createSelectInput({
|
|
65
|
+
options: Array(31).fill().map((x, i) => ({ label: i, value: i })),
|
|
66
|
+
name: 'borderNumber',
|
|
67
|
+
value: this.document.getFlag(moduleJSON.id, "borderNumber") || 26,
|
|
68
|
+
})
|
|
69
|
+
const borderNumberGroup = fields.createFormGroup({
|
|
70
|
+
input: borderNumberInput,
|
|
71
|
+
label: "Border Select",
|
|
72
|
+
hint: "Pick a style of border you want."
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const formContent = [
|
|
76
|
+
selectGroup,
|
|
77
|
+
imageGroup,
|
|
78
|
+
sidebarImageGroup,
|
|
79
|
+
borderNumberGroup
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
const updateFlags = (flags) => this.document.update({ [`flags.${moduleJSON.id}`]: flags });
|
|
83
|
+
|
|
84
|
+
const dialog = new foundry.applications.api.DialogV2({
|
|
85
|
+
id: `${this.id}-dev-menu`,
|
|
86
|
+
classes: [],
|
|
87
|
+
window: {
|
|
88
|
+
title: `${this.document.name} Dev Config`,
|
|
89
|
+
resizable: true,
|
|
90
|
+
},
|
|
91
|
+
position: {
|
|
92
|
+
width: 550,
|
|
93
|
+
top: this.position.top + 40,
|
|
94
|
+
left: this.position.left + ((this.position.width - docSheetConfigWidth) / 2)
|
|
95
|
+
},
|
|
96
|
+
// ../../assets/Art/ReignOfIron-Landscape-Vertical.webp
|
|
97
|
+
content: formContent.map(x => x.outerHTML).join(""),
|
|
98
|
+
buttons: [
|
|
99
|
+
{
|
|
100
|
+
action: "submit",
|
|
101
|
+
label: "Submit",
|
|
102
|
+
default: true,
|
|
103
|
+
callback: async function (event, target) {
|
|
104
|
+
const flags = Object.fromEntries(Object.values(target.form.elements).map(x => [x.name, x.checked || x.value]).filter(x => x[0]))
|
|
105
|
+
|
|
106
|
+
await updateFlags(flags)
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
dialog.render(true);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async render(force, options) {
|
|
115
|
+
const doc = this.document;
|
|
116
|
+
const modFlags = doc.flags?.[moduleJSON.id] ?? {};
|
|
117
|
+
await super.render(force, options);
|
|
118
|
+
|
|
119
|
+
const html = this.element;
|
|
120
|
+
|
|
121
|
+
// Sidebar Background
|
|
122
|
+
const sidebarImg = this.document.getFlag(moduleJSON.id, "sidebarImage");
|
|
123
|
+
if (sidebarImg) html.style.setProperty("--sidebarImage", `url("/${sidebarImg}")`);
|
|
124
|
+
|
|
125
|
+
const borderNumber = this.document.getFlag(moduleJSON.id, "borderNumber");
|
|
126
|
+
if (!isNaN(borderNumber)) html.style.setProperty("--urlBorderImage", `url("/modules/${moduleJSON.id}/assets/journals/borders/panel-border-${("00" + Number(borderNumber)).slice(-3)}.webp")`);
|
|
127
|
+
|
|
128
|
+
let borderRadius = "7px";
|
|
129
|
+
let borderSizeMod = "0px";
|
|
130
|
+
let borderCut = "32";
|
|
131
|
+
let borderOutset = "32";
|
|
132
|
+
switch (Number(borderNumber)) {
|
|
133
|
+
case 0: {
|
|
134
|
+
borderRadius = `7px`;
|
|
135
|
+
borderSizeMod = `25px`;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case 1: {
|
|
139
|
+
borderRadius = `15px`;
|
|
140
|
+
borderSizeMod = `0px`;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
case 2: {
|
|
144
|
+
borderRadius = `15px`;
|
|
145
|
+
borderSizeMod = `0px`;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case 3: {
|
|
149
|
+
borderRadius = `0px`;
|
|
150
|
+
borderSizeMod = `35px`;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 4: {
|
|
154
|
+
borderRadius = `6px`;
|
|
155
|
+
borderSizeMod = `20px`;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case 5: {
|
|
159
|
+
borderRadius = `0px`;
|
|
160
|
+
borderSizeMod = `0px`;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
case 6: {
|
|
164
|
+
borderRadius = `10px`;
|
|
165
|
+
borderSizeMod = `0px`;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case 7: {
|
|
169
|
+
borderRadius = `15px`;
|
|
170
|
+
borderSizeMod = `0px`;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case 8:
|
|
174
|
+
case 9: {
|
|
175
|
+
borderRadius = `10px`;
|
|
176
|
+
borderSizeMod = `12px`;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
case 10: {
|
|
180
|
+
borderRadius = `16px`;
|
|
181
|
+
borderSizeMod = `5px`;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case 11:
|
|
185
|
+
case 12: {
|
|
186
|
+
borderRadius = `16px`;
|
|
187
|
+
borderSizeMod = `5px`;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case 13: {
|
|
191
|
+
borderRadius = `12px`;
|
|
192
|
+
borderSizeMod = `5px`;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case 14: {
|
|
196
|
+
borderRadius = `10px`;
|
|
197
|
+
borderSizeMod = `20px`;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
case 15: {
|
|
201
|
+
borderRadius = `0px`;
|
|
202
|
+
borderSizeMod = `5px`;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
case 16: {
|
|
206
|
+
borderRadius = `8px`;
|
|
207
|
+
borderSizeMod = `10px`;
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
case 17: {
|
|
211
|
+
borderRadius = `11px`;
|
|
212
|
+
borderSizeMod = `10px`;
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case 18:
|
|
216
|
+
case 19: {
|
|
217
|
+
borderRadius = `16px`;
|
|
218
|
+
borderSizeMod = `0px`;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case 20: {
|
|
222
|
+
borderRadius = `0px`;
|
|
223
|
+
borderSizeMod = `10px`;
|
|
224
|
+
borderCut = `40`
|
|
225
|
+
borderOutset = `46`
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case 21: {
|
|
229
|
+
borderRadius = `25px`;
|
|
230
|
+
borderSizeMod = `0px`;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case 22: {
|
|
234
|
+
borderRadius = `10px`;
|
|
235
|
+
borderSizeMod = `6px`;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
case 23: {
|
|
239
|
+
borderRadius = `24px`;
|
|
240
|
+
borderSizeMod = `0px`;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case 24: {
|
|
244
|
+
borderRadius = `0px`;
|
|
245
|
+
borderSizeMod = `50px`;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
case 25: {
|
|
249
|
+
borderRadius = `27px`;
|
|
250
|
+
borderSizeMod = `0px`;
|
|
251
|
+
borderCut = `40`
|
|
252
|
+
borderOutset = `32`
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
case 26: {
|
|
256
|
+
borderRadius = `30px`;
|
|
257
|
+
borderSizeMod = `0px`;
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
case 27: {
|
|
261
|
+
borderRadius = `0px`;
|
|
262
|
+
borderSizeMod = `8px`;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case 28: {
|
|
266
|
+
borderRadius = `15px`;
|
|
267
|
+
borderSizeMod = `8px`;
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
case 29: {
|
|
271
|
+
borderRadius = `15px`;
|
|
272
|
+
borderSizeMod = `8px`;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case 30: {
|
|
276
|
+
borderRadius = `15px`;
|
|
277
|
+
borderSizeMod = `0px`;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
default: {
|
|
281
|
+
console.log("wtf??", borderNumber)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
html.style.setProperty("--ltBorderRadius", borderRadius);
|
|
286
|
+
html.style.setProperty("--ltBorderSizeMod", borderSizeMod);
|
|
287
|
+
html.style.setProperty("--ltBorderCut", borderCut);
|
|
288
|
+
html.style.setProperty("--ltBorderOutset", borderOutset);
|
|
289
|
+
|
|
290
|
+
if (this.mode === 2) {
|
|
291
|
+
const imgPath = this.document.flags[moduleJSON.id]?.image;
|
|
292
|
+
if (!imgPath) return;
|
|
293
|
+
const scrollable = html.querySelector(".journal-entry-content .scrollable");
|
|
294
|
+
|
|
295
|
+
// Create and insert scenery element
|
|
296
|
+
const sceneryDiv = document.createElement("div");
|
|
297
|
+
sceneryDiv.className = "scenery mask upside-down";
|
|
298
|
+
sceneryDiv.innerHTML = `<img src="/${imgPath}">`;
|
|
299
|
+
scrollable.prepend(sceneryDiv);
|
|
300
|
+
|
|
301
|
+
const scenery = scrollable.querySelector(".scenery");
|
|
302
|
+
|
|
303
|
+
scrollable.addEventListener("scroll", ({ target }) => {
|
|
304
|
+
const opacity = Math.max(10, 100 - target.scrollTop / 2);
|
|
305
|
+
scenery.style.opacity = `${opacity}%`;
|
|
306
|
+
scenery.style.pointerEvents = opacity < 50 ? "none" : "auto";
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
const header = html.querySelector("header.journal-header");
|
|
311
|
+
header.remove();
|
|
312
|
+
|
|
313
|
+
const title = document.createElement('header');
|
|
314
|
+
title.textContent = this.document.name;
|
|
315
|
+
scenery.appendChild(title);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Level Select
|
|
319
|
+
const levelSelect = html.querySelector("#level-select");
|
|
320
|
+
|
|
321
|
+
if (levelSelect) {
|
|
322
|
+
// Add radio inputs to each level
|
|
323
|
+
const levelOptions = levelSelect.querySelectorAll("[data-level]");
|
|
324
|
+
levelOptions.forEach((option) => {
|
|
325
|
+
const input = document.createElement("input");
|
|
326
|
+
input.type = "radio";
|
|
327
|
+
input.name = "level-selector";
|
|
328
|
+
option.prepend(input);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Handle radio button clicks
|
|
332
|
+
const radioInputs = levelSelect.querySelectorAll("input[type='radio']");
|
|
333
|
+
radioInputs.forEach((radio) => {
|
|
334
|
+
radio.addEventListener("click", function () {
|
|
335
|
+
const radioElement = this;
|
|
336
|
+
|
|
337
|
+
if (radioElement.checked) {
|
|
338
|
+
// Uncheck all radios in the group
|
|
339
|
+
const groupName = radioElement.name;
|
|
340
|
+
const allRadios = levelSelect.querySelectorAll(`input[type='radio'][name='${groupName}']`);
|
|
341
|
+
allRadios.forEach((r) => {
|
|
342
|
+
(r).checked = false;
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Check the clicked radio
|
|
346
|
+
radioElement.checked = true;
|
|
347
|
+
|
|
348
|
+
// Get level and update
|
|
349
|
+
const parent = radioElement.parentElement;
|
|
350
|
+
const level = parseInt(parent.dataset.level || "0", 10);
|
|
351
|
+
|
|
352
|
+
ui.notifications.info(`Setting the adventure level to ${level}!`);
|
|
353
|
+
doc.update({ flags: { [moduleJSON.id]: { level } } });
|
|
354
|
+
|
|
355
|
+
// Determine changes based on level
|
|
356
|
+
let changes = [11, 3, 3, "1d6"];
|
|
357
|
+
switch (level) {
|
|
358
|
+
case 1:
|
|
359
|
+
case 2:
|
|
360
|
+
changes = [11, 3, 3, "1d6"];
|
|
361
|
+
break;
|
|
362
|
+
case 3:
|
|
363
|
+
case 4:
|
|
364
|
+
changes = [12, 4, 3, "1d6"];
|
|
365
|
+
break;
|
|
366
|
+
case 5:
|
|
367
|
+
case 6:
|
|
368
|
+
changes = [13, 5, 5, "2d4"];
|
|
369
|
+
break;
|
|
370
|
+
case 7:
|
|
371
|
+
case 8:
|
|
372
|
+
changes = [14, 6, 7, "2d6"];
|
|
373
|
+
break;
|
|
374
|
+
case 9:
|
|
375
|
+
case 10:
|
|
376
|
+
case 11:
|
|
377
|
+
changes = [15, 7, 10, "3d6"];
|
|
378
|
+
break;
|
|
379
|
+
case 12:
|
|
380
|
+
case 13:
|
|
381
|
+
case 14:
|
|
382
|
+
changes = [16, 8, 14, "4d6"];
|
|
383
|
+
break;
|
|
384
|
+
case 15:
|
|
385
|
+
case 16:
|
|
386
|
+
case 17:
|
|
387
|
+
changes = [17, 9, 21, "6d6"];
|
|
388
|
+
break;
|
|
389
|
+
case 18:
|
|
390
|
+
case 19:
|
|
391
|
+
case 20:
|
|
392
|
+
changes = [18, 10, 28, "8d6"];
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Update all flags
|
|
397
|
+
doc.update({
|
|
398
|
+
flags: {
|
|
399
|
+
[moduleJSON.id]: {
|
|
400
|
+
level,
|
|
401
|
+
dc: changes[0],
|
|
402
|
+
mod: changes[1],
|
|
403
|
+
damage: changes[2],
|
|
404
|
+
damageDice: changes[3],
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
} else {
|
|
409
|
+
(this).checked = false;
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Set checked state for previously chosen level
|
|
415
|
+
const chosenLevel = modFlags.level;
|
|
416
|
+
if (chosenLevel) {
|
|
417
|
+
const chosenOption = levelSelect.querySelector(`[data-level="${chosenLevel}"]`);
|
|
418
|
+
if (chosenOption) {
|
|
419
|
+
const chosenInput = chosenOption.querySelector("input");
|
|
420
|
+
chosenInput.checked = true;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async _renderPageViews(context, options) {
|
|
427
|
+
const rendered = await super._renderPageViews(context, options);
|
|
428
|
+
this.applyHelianaHeaders()
|
|
429
|
+
return rendered;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
applyHelianaHeaders(target = this.element) {
|
|
433
|
+
const headers = target.querySelectorAll("h2");
|
|
434
|
+
headers.forEach((h2) => {
|
|
435
|
+
const parent = h2.parentElement;
|
|
436
|
+
if (!parent) return;
|
|
437
|
+
parent.classList.add("heliana-style-bg");
|
|
438
|
+
|
|
439
|
+
// Find appropriate background based on header width
|
|
440
|
+
const background = backgrounds.find((bg) => h2.offsetWidth <= bg.maxWidth);
|
|
441
|
+
|
|
442
|
+
if (background) {
|
|
443
|
+
parent.style.backgroundImage = `url("/modules/${moduleJSON.id}/assets/journals/headers/${background.image}")`;
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async deleteSelf() {
|
|
449
|
+
await this.close({ force: true });
|
|
450
|
+
// @ts-expect-error Intentional for purposes of HMR
|
|
451
|
+
this.document._sheet = null;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function registerSheet(sheet) {
|
|
456
|
+
foundry.applications.apps.DocumentSheetConfig.registerSheet(JournalEntry, moduleJSON.id, sheet, {
|
|
457
|
+
label: `${moduleJSON.title} Sheet`,
|
|
458
|
+
canBeDefault: false,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function unregisterSheet(sheet) {
|
|
463
|
+
foundry.applications.apps.DocumentSheetConfig.unregisterSheet(JournalEntry, moduleJSON.id, sheet);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Assuming we are inside a Hooks.on("ready")
|
|
467
|
+
registerSheet(LootTavernSheet)
|
|
468
|
+
|
|
469
|
+
if (import.meta.hot) {
|
|
470
|
+
import.meta.hot.accept(async (newModule) => {
|
|
471
|
+
if (!newModule) return;
|
|
472
|
+
let reopenedDocuments = [];
|
|
473
|
+
|
|
474
|
+
unregisterSheet(LootTavernSheet);
|
|
475
|
+
|
|
476
|
+
for (const type of affectedDocuments) {
|
|
477
|
+
for (const doc of game[type].contents) {
|
|
478
|
+
// @ts-expect-error Custom function for LootTavernSheets
|
|
479
|
+
if (doc.sheet.deleteSelf) {
|
|
480
|
+
// @ts-expect-error Custom function for LootTavernSheets
|
|
481
|
+
await doc.sheet.deleteSelf();
|
|
482
|
+
reopenedDocuments.push(doc.uuid);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
registerSheet(newModule.LootTavernSheet);
|
|
488
|
+
console.log(`Registered new ${newModule.LootTavernSheet.name} sheet.`)
|
|
489
|
+
|
|
490
|
+
reopenedDocuments.forEach(async (uuid) => {
|
|
491
|
+
const doc = await fromUuid(uuid);
|
|
492
|
+
if (!doc) return;
|
|
493
|
+
|
|
494
|
+
doc?.sheet?.render(true);
|
|
495
|
+
});
|
|
496
|
+
reopenedDocuments = [];
|
|
497
|
+
});
|
|
498
|
+
}
|