├── src ├── system │ ├── _index.scss │ └── core │ │ ├── foundry-overrides.js │ │ ├── hud.ts │ │ ├── fonts.js │ │ └── styles │ │ └── variables.scss ├── legacy-styles │ ├── utils │ │ ├── _index.scss │ │ ├── _functions.scss │ │ └── _tab-button.scss │ ├── system │ │ ├── _index.scss │ │ ├── _foundry-overrides.scss │ │ └── _fonts.scss │ ├── sheets │ │ ├── _index.scss │ │ ├── tab │ │ │ ├── _bio.scss │ │ │ ├── _index.scss │ │ │ ├── _stronghold-building.scss │ │ │ ├── _stronghold-hireling.scss │ │ │ ├── _stronghold-gear.scss │ │ │ ├── _talent.scss │ │ │ ├── _main.scss │ │ │ └── _monster-combat.scss │ │ ├── _stronghold.scss │ │ ├── _monster.scss │ │ └── _character.scss │ └── components │ │ ├── _index.scss │ │ ├── _sheet-config.scss │ │ ├── _table-config.scss │ │ ├── _util-classes.scss │ │ ├── _chat.scss │ │ └── _character-generator.scss ├── utils │ ├── safe-parse.js │ ├── localize-string.js │ ├── object-search.js │ ├── semver-compare.js │ └── sheet-config.js ├── journal │ ├── _index.scss │ ├── styles │ │ ├── _module-styles.scss │ │ └── _enrichers.scss │ └── adventure-sites │ │ └── styles │ │ └── _adventure-sites.scss ├── tests │ └── foundry-scripts │ │ ├── helpers │ │ ├── logger.js │ │ └── crud-test.js │ │ ├── index.js │ │ └── scripts │ │ ├── actors.js │ │ └── items.js ├── forbidden-lands.scss ├── types │ └── foundry-year-zero-roller.d.ts ├── item │ ├── building │ │ └── building-sheet.js │ ├── hireling │ │ └── hireling-sheet.js │ ├── raw-material │ │ └── raw-material-sheet.js │ ├── gear │ │ └── gear-sheet.js │ ├── spell │ │ └── spell-sheet.js │ ├── critical-injury │ │ └── critical-injury-sheet.js │ ├── talent │ │ └── talent-sheet.js │ ├── monster-attack │ │ └── monster-attack-sheet.js │ ├── armor │ │ └── armor-sheet.js │ └── weapon │ │ └── weapon-sheet.js ├── tsconfig.json ├── components │ ├── roll-engine │ │ └── dice-labels.js │ └── message-system.js ├── actor │ ├── party │ │ └── components │ │ │ ├── info-dialog.js │ │ │ ├── helpers.js │ │ │ └── character-picker-dialog.js │ └── stronghold │ │ └── stronghold-sheet.js ├── changelog │ └── _index.scss └── global.ts ├── .vscode ├── extensions.json ├── settings.json └── i18n-ally-custom-framework.yml ├── bun.lockb ├── assets ├── skull.png ├── sword.png ├── fbl-sun.webp ├── dice │ ├── base-1.png │ ├── base-2.png │ ├── base-3.png │ ├── base-4.png │ ├── base-5.png │ ├── base-6.png │ ├── d10-1.png │ ├── d10-10.png │ ├── d10-2.png │ ├── d10-3.png │ ├── d10-4.png │ ├── d10-5.png │ ├── d10-6.png │ ├── d10-7.png │ ├── d10-8.png │ ├── d10-9.png │ ├── d12-1.png │ ├── d12-10.png │ ├── d12-11.png │ ├── d12-12.png │ ├── d12-2.png │ ├── d12-3.png │ ├── d12-4.png │ ├── d12-5.png │ ├── d12-6.png │ ├── d12-7.png │ ├── d12-8.png │ ├── d12-9.png │ ├── d8-1.png │ ├── d8-2.png │ ├── d8-3.png │ ├── d8-4.png │ ├── d8-5.png │ ├── d8-6.png │ ├── d8-7.png │ ├── d8-8.png │ ├── gear-1.png │ ├── gear-2.png │ ├── gear-3.png │ ├── gear-4.png │ ├── gear-5.png │ ├── gear-6.png │ ├── neg-1.png │ ├── neg-2.png │ ├── neg-3.png │ ├── neg-4.png │ ├── neg-5.png │ ├── neg-6.png │ ├── skill-1.png │ ├── skill-2.png │ ├── skill-3.png │ ├── skill-4.png │ ├── skill-5.png │ └── skill-6.png ├── dsn │ ├── d8 │ │ ├── d8-1.png │ │ ├── d8-2.png │ │ ├── d8-3.png │ │ ├── d8-4.png │ │ ├── d8-5.png │ │ ├── d8-6.png │ │ ├── d8-7.png │ │ ├── d8-8.png │ │ ├── d8-1-bump.png │ │ ├── d8-2-bump.png │ │ ├── d8-3-bump.png │ │ ├── d8-4-bump.png │ │ ├── d8-5-bump.png │ │ ├── d8-6-bump.png │ │ ├── d8-7-bump.png │ │ └── d8-8-bump.png │ ├── d10 │ │ ├── d10-1.png │ │ ├── d10-2.png │ │ ├── d10-3.png │ │ ├── d10-4.png │ │ ├── d10-5.png │ │ ├── d10-6.png │ │ ├── d10-7.png │ │ ├── d10-8.png │ │ ├── d10-9.png │ │ ├── d10-10.png │ │ ├── d10-1-bump.png │ │ ├── d10-2-bump.png │ │ ├── d10-3-bump.png │ │ ├── d10-4-bump.png │ │ ├── d10-5-bump.png │ │ ├── d10-6-bump.png │ │ ├── d10-7-bump.png │ │ ├── d10-8-bump.png │ │ ├── d10-9-bump.png │ │ └── d10-10-bump.png │ ├── d12 │ │ ├── d12-1.png │ │ ├── d12-2.png │ │ ├── d12-3.png │ │ ├── d12-4.png │ │ ├── d12-5.png │ │ ├── d12-6.png │ │ ├── d12-7.png │ │ ├── d12-8.png │ │ ├── d12-9.png │ │ ├── d12-10.png │ │ ├── d12-11.png │ │ ├── d12-12.png │ │ ├── d12-1-bump.png │ │ ├── d12-2-bump.png │ │ ├── d12-3-bump.png │ │ ├── d12-4-bump.png │ │ ├── d12-5-bump.png │ │ ├── d12-6-bump.png │ │ ├── d12-7-bump.png │ │ ├── d12-8-bump.png │ │ ├── d12-9-bump.png │ │ ├── d12-10-bump.png │ │ ├── d12-11-bump.png │ │ └── d12-12-bump.png │ └── d6 │ │ ├── d6-1-bump.png │ │ ├── d6-2-bump.png │ │ ├── d6-3-bump.png │ │ ├── d6-4-bump.png │ │ ├── d6-5-bump.png │ │ ├── d6-6-bump.png │ │ ├── d6-1-black.png │ │ ├── d6-1-white.png │ │ ├── d6-2-black.png │ │ ├── d6-2-white.png │ │ ├── d6-3-black.png │ │ ├── d6-3-white.png │ │ ├── d6-4-black.png │ │ ├── d6-4-white.png │ │ ├── d6-5-black.png │ │ ├── d6-5-white.png │ │ ├── d6-6-black.png │ │ ├── d6-6-white.png │ │ ├── d6-1-skill-black.png │ │ ├── d6-1-skill-bump.png │ │ └── d6-1-skill-white.png ├── splash-page.jpg ├── fbl-monster.webp ├── fbl-portrait.webp ├── rolling-dices.png ├── assorted │ ├── arrows.webp │ ├── food.webp │ ├── water.webp │ └── torches.webp ├── fbl-character.webp ├── fbl-stronghold.webp ├── journal-art │ ├── endstop.webp │ ├── top_border.webp │ ├── attack-border.webp │ ├── ravens │ │ ├── raven1.webp │ │ ├── raven2.webp │ │ ├── raven3.webp │ │ ├── raven4.webp │ │ ├── raven6.webp │ │ └── raven7.webp │ ├── small-border.webp │ ├── title-border.webp │ ├── box-border-large.webp │ ├── pentagram-border.webp │ ├── box-border-large-dark.webp │ └── box-border-top-bottom.webp └── datasets │ └── macros │ └── macros.json ├── fonts ├── skullz.ttf ├── imfe-dpit.otf ├── imfe-dprm.otf ├── imfe-gpit.otf ├── imfe-gprm.otf ├── imfe-tlrm.otf ├── imfe-dwpscr.ttf ├── swordlings.ttf ├── author-medium.otf ├── author-semibold.otf ├── rpgawesome-webfont.eot ├── rpgawesome-webfont.ttf ├── rpgawesome-webfont.woff ├── author-medium-italic.otf └── author-semibold-italic.otf ├── __fixtures__ ├── .gitignore ├── source │ └── pwtest.zip └── FoundryVTT │ └── Config │ └── options.json ├── lefthook.yml ├── templates ├── components │ ├── character-generator │ │ └── list-component.hbs │ ├── sheet-config.hbs │ ├── roll-engine │ │ ├── infos.hbs │ │ ├── tooltip.hbs │ │ ├── roll.hbs │ │ ├── dialog.hbs │ │ └── spell-dialog.hbs │ ├── tables-config.hbs │ └── sheet-config-modal.hbs ├── actor │ ├── party │ │ ├── components │ │ │ ├── character-picker-dialog.hbs │ │ │ ├── member-component.hbs │ │ │ └── action-component.hbs │ │ ├── sheet-tabs │ │ │ ├── travel-tab.hbs │ │ │ └── main-tab.hbs │ │ └── party-sheet.hbs │ ├── stronghold │ │ ├── sheet-tabs │ │ │ ├── hireling-tab.hbs │ │ │ └── building-tab.hbs │ │ └── stronghold-sheet.hbs │ ├── character │ │ ├── sheet-tabs │ │ │ ├── bio-tab.hbs │ │ │ └── talent-tab.hbs │ │ ├── character-limited-sheet.hbs │ │ └── npc-sheet.hbs │ └── monster │ │ ├── monster-sheet.hbs │ │ └── sheet-tabs │ │ └── combat-tab.hbs └── item │ ├── armor │ ├── main-tab.hbs │ └── armor-sheet.hbs │ ├── hireling │ └── hireling-sheet.hbs │ ├── _shared-template-tabs │ ├── supply-tab.hbs │ └── artifact-tab.hbs │ ├── talent │ └── talent-sheet.hbs │ ├── critical-injury │ └── critical-injury-sheet.hbs │ ├── gear │ ├── main-tab.hbs │ └── gear-sheet.hbs │ ├── raw-material │ └── raw-material-sheet.hbs │ ├── spell │ └── spell-sheet.hbs │ ├── building │ └── building-sheet.hbs │ ├── monster-attack │ └── monster-attack-sheet.hbs │ └── weapon │ └── weapon-sheet.hbs ├── .editorconfig ├── devbox.json ├── tools ├── get-template-paths.ts ├── foundry.ts ├── sort-lang-files.ts ├── args-parser.ts ├── version.ts └── release.ts ├── .changeset ├── config.json └── README.md ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.yml │ └── bug_report.yml └── workflows │ ├── pr.yml │ ├── release.yml │ └── publish.yml ├── tsconfig.json ├── .gitignore ├── e2e └── foundry.spec.ts ├── biome.json ├── playwright.config.ts ├── esbuild.config.ts ├── devbox.lock ├── tests └── utils-tests │ └── semver-compare.test.ts └── package.json /src/system/_index.scss: -------------------------------------------------------------------------------- 1 | @forward "core/styles"; 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["lokalise.i18n-ally"] 3 | } 4 | -------------------------------------------------------------------------------- /src/legacy-styles/utils/_index.scss: -------------------------------------------------------------------------------- 1 | @forward "tab-button"; 2 | @forward "functions"; 3 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/bun.lockb -------------------------------------------------------------------------------- /src/legacy-styles/system/_index.scss: -------------------------------------------------------------------------------- 1 | @forward "fonts"; 2 | @forward "main"; 3 | @forward "foundry-overrides"; -------------------------------------------------------------------------------- /assets/skull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/skull.png -------------------------------------------------------------------------------- /assets/sword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/sword.png -------------------------------------------------------------------------------- /fonts/skullz.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/skullz.ttf -------------------------------------------------------------------------------- /assets/fbl-sun.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/fbl-sun.webp -------------------------------------------------------------------------------- /fonts/imfe-dpit.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/imfe-dpit.otf -------------------------------------------------------------------------------- /fonts/imfe-dprm.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/imfe-dprm.otf -------------------------------------------------------------------------------- /fonts/imfe-gpit.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/imfe-gpit.otf -------------------------------------------------------------------------------- /fonts/imfe-gprm.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/imfe-gprm.otf -------------------------------------------------------------------------------- /fonts/imfe-tlrm.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/imfe-tlrm.otf -------------------------------------------------------------------------------- /__fixtures__/.gitignore: -------------------------------------------------------------------------------- 1 | # __fixtures__ 2 | /FoundryVTT/** 3 | 4 | !/FoundryVTT/Config/ 5 | !/FoundryVTT/Config/options.json -------------------------------------------------------------------------------- /assets/dice/base-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/base-1.png -------------------------------------------------------------------------------- /assets/dice/base-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/base-2.png -------------------------------------------------------------------------------- /assets/dice/base-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/base-3.png -------------------------------------------------------------------------------- /assets/dice/base-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/base-4.png -------------------------------------------------------------------------------- /assets/dice/base-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/base-5.png -------------------------------------------------------------------------------- /assets/dice/base-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/base-6.png -------------------------------------------------------------------------------- /assets/dice/d10-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-1.png -------------------------------------------------------------------------------- /assets/dice/d10-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-10.png -------------------------------------------------------------------------------- /assets/dice/d10-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-2.png -------------------------------------------------------------------------------- /assets/dice/d10-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-3.png -------------------------------------------------------------------------------- /assets/dice/d10-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-4.png -------------------------------------------------------------------------------- /assets/dice/d10-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-5.png -------------------------------------------------------------------------------- /assets/dice/d10-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-6.png -------------------------------------------------------------------------------- /assets/dice/d10-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-7.png -------------------------------------------------------------------------------- /assets/dice/d10-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-8.png -------------------------------------------------------------------------------- /assets/dice/d10-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d10-9.png -------------------------------------------------------------------------------- /assets/dice/d12-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-1.png -------------------------------------------------------------------------------- /assets/dice/d12-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-10.png -------------------------------------------------------------------------------- /assets/dice/d12-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-11.png -------------------------------------------------------------------------------- /assets/dice/d12-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-12.png -------------------------------------------------------------------------------- /assets/dice/d12-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-2.png -------------------------------------------------------------------------------- /assets/dice/d12-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-3.png -------------------------------------------------------------------------------- /assets/dice/d12-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-4.png -------------------------------------------------------------------------------- /assets/dice/d12-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-5.png -------------------------------------------------------------------------------- /assets/dice/d12-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-6.png -------------------------------------------------------------------------------- /assets/dice/d12-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-7.png -------------------------------------------------------------------------------- /assets/dice/d12-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-8.png -------------------------------------------------------------------------------- /assets/dice/d12-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d12-9.png -------------------------------------------------------------------------------- /assets/dice/d8-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d8-1.png -------------------------------------------------------------------------------- /assets/dice/d8-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d8-2.png -------------------------------------------------------------------------------- /assets/dice/d8-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d8-3.png -------------------------------------------------------------------------------- /assets/dice/d8-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d8-4.png -------------------------------------------------------------------------------- /assets/dice/d8-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d8-5.png -------------------------------------------------------------------------------- /assets/dice/d8-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d8-6.png -------------------------------------------------------------------------------- /assets/dice/d8-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d8-7.png -------------------------------------------------------------------------------- /assets/dice/d8-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/d8-8.png -------------------------------------------------------------------------------- /assets/dice/gear-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/gear-1.png -------------------------------------------------------------------------------- /assets/dice/gear-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/gear-2.png -------------------------------------------------------------------------------- /assets/dice/gear-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/gear-3.png -------------------------------------------------------------------------------- /assets/dice/gear-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/gear-4.png -------------------------------------------------------------------------------- /assets/dice/gear-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/gear-5.png -------------------------------------------------------------------------------- /assets/dice/gear-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/gear-6.png -------------------------------------------------------------------------------- /assets/dice/neg-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/neg-1.png -------------------------------------------------------------------------------- /assets/dice/neg-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/neg-2.png -------------------------------------------------------------------------------- /assets/dice/neg-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/neg-3.png -------------------------------------------------------------------------------- /assets/dice/neg-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/neg-4.png -------------------------------------------------------------------------------- /assets/dice/neg-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/neg-5.png -------------------------------------------------------------------------------- /assets/dice/neg-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/neg-6.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-1.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-2.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-3.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-4.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-5.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-6.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-7.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-8.png -------------------------------------------------------------------------------- /assets/splash-page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/splash-page.jpg -------------------------------------------------------------------------------- /fonts/imfe-dwpscr.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/imfe-dwpscr.ttf -------------------------------------------------------------------------------- /fonts/swordlings.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/swordlings.ttf -------------------------------------------------------------------------------- /assets/dice/skill-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/skill-1.png -------------------------------------------------------------------------------- /assets/dice/skill-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/skill-2.png -------------------------------------------------------------------------------- /assets/dice/skill-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/skill-3.png -------------------------------------------------------------------------------- /assets/dice/skill-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/skill-4.png -------------------------------------------------------------------------------- /assets/dice/skill-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/skill-5.png -------------------------------------------------------------------------------- /assets/dice/skill-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dice/skill-6.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-1.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-2.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-3.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-4.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-5.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-6.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-7.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-8.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-9.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-1.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-2.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-3.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-4.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-5.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-6.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-7.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-8.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-9.png -------------------------------------------------------------------------------- /assets/fbl-monster.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/fbl-monster.webp -------------------------------------------------------------------------------- /assets/fbl-portrait.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/fbl-portrait.webp -------------------------------------------------------------------------------- /assets/rolling-dices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/rolling-dices.png -------------------------------------------------------------------------------- /fonts/author-medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/author-medium.otf -------------------------------------------------------------------------------- /assets/assorted/arrows.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/assorted/arrows.webp -------------------------------------------------------------------------------- /assets/assorted/food.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/assorted/food.webp -------------------------------------------------------------------------------- /assets/assorted/water.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/assorted/water.webp -------------------------------------------------------------------------------- /assets/dsn/d10/d10-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-10.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-10.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-11.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-12.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-1-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-1-bump.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-2-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-2-bump.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-3-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-3-bump.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-4-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-4-bump.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-5-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-5-bump.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-6-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-6-bump.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-1-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-1-bump.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-2-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-2-bump.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-3-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-3-bump.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-4-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-4-bump.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-5-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-5-bump.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-6-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-6-bump.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-7-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-7-bump.png -------------------------------------------------------------------------------- /assets/dsn/d8/d8-8-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d8/d8-8-bump.png -------------------------------------------------------------------------------- /assets/fbl-character.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/fbl-character.webp -------------------------------------------------------------------------------- /assets/fbl-stronghold.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/fbl-stronghold.webp -------------------------------------------------------------------------------- /fonts/author-semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/author-semibold.otf -------------------------------------------------------------------------------- /assets/assorted/torches.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/assorted/torches.webp -------------------------------------------------------------------------------- /assets/dsn/d10/d10-1-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-1-bump.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-2-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-2-bump.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-3-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-3-bump.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-4-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-4-bump.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-5-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-5-bump.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-6-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-6-bump.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-7-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-7-bump.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-8-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-8-bump.png -------------------------------------------------------------------------------- /assets/dsn/d10/d10-9-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-9-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-1-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-1-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-2-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-2-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-3-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-3-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-4-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-4-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-5-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-5-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-6-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-6-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-7-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-7-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-8-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-8-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-9-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-9-bump.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-1-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-1-black.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-1-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-1-white.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-2-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-2-black.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-2-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-2-white.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-3-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-3-black.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-3-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-3-white.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-4-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-4-black.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-4-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-4-white.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-5-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-5-black.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-5-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-5-white.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-6-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-6-black.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-6-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-6-white.png -------------------------------------------------------------------------------- /fonts/rpgawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/rpgawesome-webfont.eot -------------------------------------------------------------------------------- /fonts/rpgawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/rpgawesome-webfont.ttf -------------------------------------------------------------------------------- /fonts/rpgawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/rpgawesome-webfont.woff -------------------------------------------------------------------------------- /__fixtures__/source/pwtest.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/__fixtures__/source/pwtest.zip -------------------------------------------------------------------------------- /assets/dsn/d10/d10-10-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d10/d10-10-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-10-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-10-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-11-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-11-bump.png -------------------------------------------------------------------------------- /assets/dsn/d12/d12-12-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d12/d12-12-bump.png -------------------------------------------------------------------------------- /assets/journal-art/endstop.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/endstop.webp -------------------------------------------------------------------------------- /fonts/author-medium-italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/author-medium-italic.otf -------------------------------------------------------------------------------- /fonts/author-semibold-italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/fonts/author-semibold-italic.otf -------------------------------------------------------------------------------- /assets/dsn/d6/d6-1-skill-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-1-skill-black.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-1-skill-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-1-skill-bump.png -------------------------------------------------------------------------------- /assets/dsn/d6/d6-1-skill-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/dsn/d6/d6-1-skill-white.png -------------------------------------------------------------------------------- /assets/journal-art/top_border.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/top_border.webp -------------------------------------------------------------------------------- /assets/journal-art/attack-border.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/attack-border.webp -------------------------------------------------------------------------------- /assets/journal-art/ravens/raven1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/ravens/raven1.webp -------------------------------------------------------------------------------- /assets/journal-art/ravens/raven2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/ravens/raven2.webp -------------------------------------------------------------------------------- /assets/journal-art/ravens/raven3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/ravens/raven3.webp -------------------------------------------------------------------------------- /assets/journal-art/ravens/raven4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/ravens/raven4.webp -------------------------------------------------------------------------------- /assets/journal-art/ravens/raven6.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/ravens/raven6.webp -------------------------------------------------------------------------------- /assets/journal-art/ravens/raven7.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/ravens/raven7.webp -------------------------------------------------------------------------------- /assets/journal-art/small-border.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/small-border.webp -------------------------------------------------------------------------------- /assets/journal-art/title-border.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/title-border.webp -------------------------------------------------------------------------------- /assets/journal-art/box-border-large.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/box-border-large.webp -------------------------------------------------------------------------------- /assets/journal-art/pentagram-border.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/pentagram-border.webp -------------------------------------------------------------------------------- /src/utils/safe-parse.js: -------------------------------------------------------------------------------- 1 | export default function safeParseJSON(json) { 2 | try { 3 | return JSON.parse(json); 4 | } catch (e) { 5 | return null; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /assets/journal-art/box-border-large-dark.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/box-border-large-dark.webp -------------------------------------------------------------------------------- /assets/journal-art/box-border-top-bottom.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/HEAD/assets/journal-art/box-border-top-bottom.webp -------------------------------------------------------------------------------- /src/journal/_index.scss: -------------------------------------------------------------------------------- 1 | @forward "styles/journal"; 2 | @forward "styles/module-styles"; 3 | @forward "styles/enrichers"; 4 | @forward "adventure-sites/styles/adventure-sites"; 5 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/_index.scss: -------------------------------------------------------------------------------- 1 | @forward "character"; 2 | @forward "item"; 3 | @forward "monster"; 4 | @forward "stronghold"; 5 | @forward "party-sheet"; 6 | @forward "tab"; 7 | -------------------------------------------------------------------------------- /src/tests/foundry-scripts/helpers/logger.js: -------------------------------------------------------------------------------- 1 | export default class Logger { 2 | static init() { 3 | Hooks.once("error", (error) => { 4 | console.log(error); 5 | }); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/tests/foundry-scripts/index.js: -------------------------------------------------------------------------------- 1 | import * as actors from "./scripts/actors"; 2 | import * as items from "./scripts/items"; 3 | 4 | export default { 5 | actors, 6 | items, 7 | }; 8 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | commit-msg: 2 | commands: 3 | commitlint: 4 | run: bunx devmoji -e --lint 5 | 6 | pre-push: 7 | commands: 8 | test: 9 | run: bun run test 10 | -------------------------------------------------------------------------------- /src/system/core/foundry-overrides.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars, no-shadow */ 2 | export default async function FoundryOverrides() { 3 | return new Promise((resolve) => resolve()); 4 | } 5 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/tab/_bio.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .character .bio-tab { 2 | height: 100%; 3 | overflow-y: auto; 4 | } 5 | 6 | .forbidden-lands .character .bio-tab .section { 7 | margin-bottom: 1rem; 8 | } 9 | -------------------------------------------------------------------------------- /src/legacy-styles/components/_index.scss: -------------------------------------------------------------------------------- 1 | @forward "chat"; 2 | @forward "dialog"; 3 | @forward "character-generator"; 4 | @forward "table-config"; 5 | @forward "sheet-config"; 6 | @forward "roll-items"; 7 | @forward "util-classes"; 8 | -------------------------------------------------------------------------------- /src/legacy-styles/utils/_functions.scss: -------------------------------------------------------------------------------- 1 | @function fromTo($pref: h, $from: 1, $to: 6) { 2 | @if $from == $to { 3 | @return "#{$pref}#{$from}"; 4 | } @else { 5 | @return "#{$pref}#{$from}," + fromTo($pref, $from + 1, $to); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/forbidden-lands.scss: -------------------------------------------------------------------------------- 1 | @import "./legacy-styles/system"; 2 | @import "./legacy-styles/utils"; 3 | @import "./legacy-styles/components"; 4 | @import "./legacy-styles/sheets"; 5 | 6 | @import "system"; 7 | @import "changelog"; 8 | @import "journal"; -------------------------------------------------------------------------------- /src/legacy-styles/sheets/tab/_index.scss: -------------------------------------------------------------------------------- 1 | @forward "bio"; 2 | @forward "combat"; 3 | @forward "gear"; 4 | @forward "main"; 5 | @forward "monster-combat"; 6 | @forward "stronghold-building"; 7 | @forward "stronghold-gear"; 8 | @forward "stronghold-hireling"; 9 | @forward "talent"; 10 | -------------------------------------------------------------------------------- /templates/components/character-generator/list-component.hbs: -------------------------------------------------------------------------------- 1 |
2 |
{{localize title}}
3 | 8 |
9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = tab 7 | indent_size = 4 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | 11 | [*.scss] 12 | indent_size = 2 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | indent_style = space 17 | -------------------------------------------------------------------------------- /src/types/foundry-year-zero-roller.d.ts: -------------------------------------------------------------------------------- 1 | declare module "foundry-year-zero-roller" { 2 | export class YearZeroRollManager { 3 | static register( 4 | system: string, 5 | templates: { 6 | "ROLL.chatTemplate": string; 7 | "ROLL.tooltipTemplate": string; 8 | "ROLL.infosTemplate": string; 9 | }, 10 | ): void; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /devbox.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.11.0/.schema/devbox.schema.json", 3 | "packages": ["bun@latest"], 4 | "shell": { 5 | "init_hook": [ 6 | "echo 'Welcome to devbox!' > /dev/null" 7 | ], 8 | "scripts": { 9 | "test": [ 10 | "echo \"Error: no test specified\" && exit 1" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tools/get-template-paths.ts: -------------------------------------------------------------------------------- 1 | import { globby } from "globby"; 2 | import { posix, sep } from "node:path"; 3 | 4 | export default (async () => { 5 | const paths = await globby("**/*.hbs", { cwd: "./templates" }); 6 | return paths.map((templatePath) => { 7 | templatePath = templatePath.split(sep).join(posix.sep); 8 | return `systems/forbidden-lands/templates/${templatePath}`; 9 | }); 10 | })(); 11 | -------------------------------------------------------------------------------- /tools/foundry.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "bun"; 2 | import { data } from "foundryconfig.json" with { type: "json" }; 3 | 4 | const __dirname = new URL("../", import.meta.url).pathname; 5 | 6 | async function run() { 7 | const { foundryBinary } = data; 8 | const flags = [`--dataPath=/${__dirname}/__fixtures__/FoundryVTT`]; 9 | 10 | await $`node ${foundryBinary} ${flags.join(" ")}`; 11 | } 12 | 13 | run(); 14 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "fvtt-fria-ligan/forbidden-lands-foundry-vtt" } 6 | ], 7 | "commit": false, 8 | "fixed": [], 9 | "linked": [], 10 | "access": "restricted", 11 | "baseBranch": "main", 12 | "updateInternalDependencies": "patch", 13 | "ignore": [] 14 | } 15 | -------------------------------------------------------------------------------- /src/item/building/building-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | export class ForbiddenLandsBuildingSheet extends ForbiddenLandsItemSheet { 3 | static get defaultOptions() { 4 | return foundry.utils.mergeObject(super.defaultOptions, { 5 | ...super.defaultOptions, 6 | template: 7 | "systems/forbidden-lands/templates/item/building/building-sheet.hbs", 8 | }); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/item/hireling/hireling-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | export class ForbiddenLandsHirelingSheet extends ForbiddenLandsItemSheet { 3 | static get defaultOptions() { 4 | return foundry.utils.mergeObject(super.defaultOptions, { 5 | ...super.defaultOptions, 6 | template: 7 | "systems/forbidden-lands/templates/item/hireling/hireling-sheet.hbs", 8 | }); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "i18n-ally.localesPaths": ["lang"], 3 | "i18n-ally.keystyle": "flat", 4 | "i18n-ally.extract.autoDetect": true, 5 | "typescript.tsdk": "node_modules/typescript/lib", 6 | "[javascript]": { 7 | "editor.defaultFormatter": "biomejs.biome" 8 | }, 9 | "[json]": { 10 | "editor.defaultFormatter": "biomejs.biome" 11 | }, 12 | "[typescript]": { 13 | "editor.defaultFormatter": "biomejs.biome" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/item/raw-material/raw-material-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | export class ForbiddenLandsRawMaterialSheet extends ForbiddenLandsItemSheet { 3 | static get defaultOptions() { 4 | return foundry.utils.mergeObject(super.defaultOptions, { 5 | ...super.defaultOptions, 6 | template: 7 | "systems/forbidden-lands/templates/item/raw-material/raw-material-sheet.hbs", 8 | }); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/localize-string.js: -------------------------------------------------------------------------------- 1 | export default function localizeString(string) { 2 | if (!string) console.log("No string passed"); 3 | try { 4 | const dict = CONFIG.fbl.i18n; 5 | let localeString = dict[string]; 6 | if (!localeString) localeString = string; 7 | 8 | return game.i18n.localize(localeString); 9 | } catch (error) { 10 | console.log(`Failed to localize string: ${string}. Error: `, error); 11 | 12 | return string; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord Chat 4 | url: https://discord.gg/foundryvtt 5 | about: "Look for the #free-league channel to find a community dedicated to the Free League games." 6 | - name: Questions & Discussions 7 | url: https://github.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/discussions 8 | about: Use the discussions board if you have ideas, or more complex usage questions. 9 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/tab/_stronghold-building.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .stronghold { 2 | .buildings { 3 | flex-basis: 50%; 4 | 5 | .building { 6 | align-items: center; 7 | padding-block: 4px; 8 | 9 | .name { 10 | flex-basis: 75%; 11 | } 12 | 13 | .quantity { 14 | flex-basis: 15%; 15 | text-align: center; 16 | } 17 | 18 | .button { 19 | flex-basis: 10%; 20 | font-size: var(--font-size-12); 21 | text-align: right; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/system/core/hud.ts: -------------------------------------------------------------------------------- 1 | export class ForbiddenLandsTokenHUD extends foundry.applications.hud.TokenHUD { 2 | _getStatusEffectChoices() { 3 | const actor = this.object.document.actor; 4 | 5 | const data = super._getStatusEffectChoices(); 6 | 7 | if (actor?.type === "character") return data; 8 | 9 | for (const [key, effect] of Object.entries(data)) { 10 | if (effect && CONFIG.fbl.conditions.includes(effect?.id)) 11 | delete data[key]; 12 | } 13 | 14 | return data; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/legacy-styles/utils/_tab-button.scss: -------------------------------------------------------------------------------- 1 | @mixin tab-button($add) { 2 | #{$add}:before { 3 | position: absolute; 4 | font-family: swordlings; 5 | text-transform: lowercase; 6 | content: "x"; 7 | font-size: var(--font-size-16); 8 | left: -6px; 9 | top: 0.55rem; 10 | } 11 | #{$add}:after { 12 | position: absolute; 13 | font-family: skullz; 14 | text-transform: lowercase; 15 | content: "l"; 16 | font-size: var(--font-size-24); 17 | top: 0.3rem; 18 | right: -5px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "strict": true, 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleResolution": "Bundler", 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true, 10 | "allowJs": true, 11 | "lib": ["ESNext"], 12 | "types": ["bun-types"], 13 | "baseUrl": "./", 14 | "paths": { 15 | "@utils/*": ["src/utils/*"] 16 | } 17 | }, 18 | "include": ["./**/*.ts"], 19 | "exclude": ["node_modules", "src", "__fixtures__"] 20 | } 21 | -------------------------------------------------------------------------------- /templates/components/sheet-config.hbs: -------------------------------------------------------------------------------- 1 | {{#*inline "fields" config}} 2 | {{#each config as |field|}} 3 |
4 |
5 | 12 |
13 | {{/each}} 14 | {{/inline}} 15 | 16 |
17 | {{> fields config=config}} 18 | 19 |
20 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/tab/_stronghold-hireling.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .stronghold .hirelings { 2 | flex-basis: 50%; 3 | 4 | .hireling { 5 | align-items: center; 6 | padding-block: 4px; 7 | 8 | .name { 9 | flex-basis: 55%; 10 | } 11 | 12 | .quantity { 13 | flex-basis: 15%; 14 | text-align: center; 15 | } 16 | 17 | .salary { 18 | flex-basis: 20%; 19 | text-align: right; 20 | } 21 | 22 | .button { 23 | flex-basis: 10%; 24 | font-size: var(--font-size-12); 25 | text-align: right; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /templates/actor/party/components/character-picker-dialog.hbs: -------------------------------------------------------------------------------- 1 |
2 |

{{title}}

3 |
    4 | {{#each actors}} 5 |
  1. 6 | 7 | 8 | {{name}} 9 | 10 |
  2. 11 | {{/each}} 12 |
13 |
14 | -------------------------------------------------------------------------------- /tools/sort-lang-files.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | 3 | const dir = fs.readdirSync("./static/lang"); 4 | 5 | const files = dir.filter((file) => file.endsWith(".json")); 6 | 7 | for (const file of files) { 8 | const contents = fs.readFileSync(`./static/lang/${file}`, "utf8"); 9 | const json = JSON.parse(contents); 10 | const entries = Object.entries(json); 11 | const sorted = entries.sort((a, b) => a[0].localeCompare(b[0])); 12 | const obj = Object.fromEntries(sorted); 13 | fs.writeFileSync(`./static/lang/${file}`, JSON.stringify(obj, null, "\t")); 14 | } 15 | -------------------------------------------------------------------------------- /src/legacy-styles/components/_sheet-config.scss: -------------------------------------------------------------------------------- 1 | .sheet-config { 2 | form { 3 | display: flex; 4 | flex-direction: column; 5 | gap: 16px; 6 | } 7 | 8 | label { 9 | display: flex; 10 | flex-direction: row; 11 | font-weight: 600; 12 | gap: 8px; 13 | 14 | div { 15 | display: flex; 16 | flex-direction: column; 17 | 18 | .description { 19 | font-size: var(--font-size-12); 20 | font-weight: 400; 21 | color: var(--color-text-dark-5); 22 | font-style: italic; 23 | } 24 | } 25 | } 26 | 27 | hr { 28 | width: 100%; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "lib": ["ESNext", "DOM"], 5 | "types": ["jquery", "showdown"], 6 | "skipLibCheck": true, 7 | "moduleResolution": "bundler", 8 | "baseUrl": ".", 9 | "paths": { 10 | "$*": ["./*"], 11 | "@utils/*": ["./utils/*"], 12 | "@actor/*": ["./actor/*"], 13 | "@item/*": ["./item/*"], 14 | "@journal/*": ["./journal/*"], 15 | "@components/*": ["./components/*"], 16 | "@system/*": ["./system/*"] 17 | } 18 | }, 19 | "include": ["./", "../types"], 20 | "exclude": [] 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/object-search.js: -------------------------------------------------------------------------------- 1 | export function objectSearch(object, string) { 2 | if (!validateObject(object) || !validateString(string)) 3 | return console.error("Invalid arguments"); 4 | const result = Object.entries(object).find( 5 | (entries) => entries[1] === string, 6 | ); 7 | return result ? result[0] : null; 8 | } 9 | 10 | function validateObject(object) { 11 | return ( 12 | !!object && typeof object === "object" && !foundry.utils.isEmpty(object) 13 | ); 14 | } 15 | 16 | function validateString(string) { 17 | return !!string && typeof string === "string"; 18 | } 19 | -------------------------------------------------------------------------------- /templates/actor/party/sheet-tabs/travel-tab.hbs: -------------------------------------------------------------------------------- 1 |
2 |
{{localize "FLPS.PARTY.HINT_ASSIGN"}}
3 |
4 | 5 |
6 | 13 |
14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | /__MACOSX 3 | 4 | # IDE 5 | /.idea 6 | /.vs 7 | *.code-workspace 8 | .history 9 | 10 | # Node Modules 11 | /node_modules 12 | npm-debug.log 13 | 14 | # Local configuration 15 | foundryconfig.json 16 | 17 | # Distribution files 18 | /dist 19 | /forbidden-lands.js 20 | /forbidden-lands.css 21 | 22 | # ESLint 23 | /.eslintcache 24 | 25 | # Junit results 26 | junit.xml 27 | 28 | # filetypes 29 | .DS_Store 30 | 31 | # NPM files 32 | package-lock.json 33 | 34 | # Playwright 35 | /test-results/ 36 | /playwright-report/ 37 | /blob-report/ 38 | /playwright/.cache/ 39 | 40 | # Devbox 41 | /.devbox 42 | -------------------------------------------------------------------------------- /src/tests/foundry-scripts/scripts/actors.js: -------------------------------------------------------------------------------- 1 | import crudTest from "../helpers/crud-test"; 2 | const CLSName = "Actor"; 3 | export const individual = { 4 | character: () => crudTest(CLSName, "character", "Character1"), 5 | npc: () => crudTest(CLSName, "character", "NPC1", "npc"), 6 | monster: () => crudTest(CLSName, "monster", "Monster1"), 7 | party: () => crudTest(CLSName, "party", "Party1"), 8 | stronghold: () => crudTest(CLSName, "stronghold", "Stronghold1"), 9 | }; 10 | // biome-ignore lint/complexity/noForEach: 11 | export const all = () => Object.values(individual).forEach((actor) => actor()); 12 | -------------------------------------------------------------------------------- /src/components/roll-engine/dice-labels.js: -------------------------------------------------------------------------------- 1 | export function registerYZURLabels() { 2 | CONFIG.YZUR.Icons.getLabel = (type, result) => 3 | `${result}`; 4 | } 5 | 6 | export class ForbiddenLandsD6 extends foundry.dice.terms.Die { 7 | constructor(termData) { 8 | termData.faces = 6; 9 | super(termData); 10 | } 11 | 12 | static DENOMINATION = 6; 13 | 14 | static getResultLabel(result) { 15 | return `${result}`; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tools/args-parser.ts: -------------------------------------------------------------------------------- 1 | export const args = (() => { 2 | const args = process.argv.slice(2); 3 | if (args.length === 0) return {}; 4 | return args.reduce>( 5 | (acc, arg, index, arr) => { 6 | if (arg.startsWith("--")) { 7 | let [key, value] = arg.split("=") as [string, string | boolean]; 8 | if (!value) { 9 | if (index + 1 < arr.length && !arr[index + 1].startsWith("--")) { 10 | value = arr.splice(index + 1, 1).join(); 11 | } else value = true; 12 | } 13 | acc[key.replace("--", "")] = value; 14 | } 15 | 16 | return acc; 17 | }, 18 | {}, 19 | ); 20 | })(); 21 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR Check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | concurrency: ${{ github.workflow }}-${{ github.ref }} 10 | 11 | jobs: 12 | test: 13 | name: "Pull Request Linting" 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | ref: ${{ github.event.pull_request.head.sha }} 21 | - uses: oven-sh/setup-bun@v1 22 | - name: "Install Dependencies" 23 | run: bun install 24 | - name: "Run Linter" 25 | run: bun run test 26 | 27 | -------------------------------------------------------------------------------- /src/tests/foundry-scripts/helpers/crud-test.js: -------------------------------------------------------------------------------- 1 | import Logger from "./logger"; 2 | export default async function ( 3 | documentClassName, 4 | documentType, 5 | documentName, 6 | subtype = "", 7 | ) { 8 | try { 9 | Logger.init(); 10 | const cls = getDocumentClass(documentClassName); 11 | const created = await cls.__proto__.create({ 12 | type: documentType, 13 | documentName, 14 | name: documentName, 15 | ["system.subtype.type"]: subtype, 16 | }); 17 | await created.sheet.render(true); 18 | await created.update({ name: `${documentName}-updated` }); 19 | await created.delete(); 20 | } catch (error) { 21 | Logger.error(error); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/legacy-styles/components/_table-config.scss: -------------------------------------------------------------------------------- 1 | .tables-config { 2 | form { 3 | display: grid; 4 | grid-template-columns: repeat(2, auto); 5 | gap: 16px; 6 | } 7 | 8 | fieldset { 9 | display: grid; 10 | grid-template-columns: 1fr; 11 | gap: 16px; 12 | 13 | &.encounter { 14 | grid-template-columns: 1fr 1fr; 15 | grid-row: span 2; 16 | } 17 | } 18 | 19 | button[type="submit"] { 20 | grid-column: span 2; 21 | } 22 | 23 | legend { 24 | font-weight: 600; 25 | font-size: var(--font-size-18); 26 | } 27 | 28 | label { 29 | display: flex; 30 | flex-direction: column; 31 | gap: 8px; 32 | max-width: 250px; 33 | 34 | font-weight: 600; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /templates/actor/party/components/member-component.hbs: -------------------------------------------------------------------------------- 1 |
  • 3 | {{#if deleteButton }} 4 |
    5 | 7 | 8 |
    9 | {{/if}} 10 | {{partyMember.name}} 16 |
  • -------------------------------------------------------------------------------- /src/item/gear/gear-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | 3 | export class ForbiddenLandsGearSheet extends ForbiddenLandsItemSheet { 4 | static get defaultOptions() { 5 | return foundry.utils.mergeObject(super.defaultOptions, { 6 | ...super.defaultOptions, 7 | template: "systems/forbidden-lands/templates/item/gear/gear-sheet.hbs", 8 | tabs: [ 9 | { 10 | navSelector: ".sheet-tabs", 11 | contentSelector: ".sheet-body", 12 | initial: "main", 13 | }, 14 | ], 15 | }); 16 | } 17 | 18 | async getData(options) { 19 | const data = await super.getData(options); 20 | data.encumbranceValues = CONFIG.fbl.encumbrance; 21 | return data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/item/spell/spell-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | export class ForbiddenLandsSpellSheet extends ForbiddenLandsItemSheet { 3 | static get defaultOptions() { 4 | return foundry.utils.mergeObject(super.defaultOptions, { 5 | ...super.defaultOptions, 6 | template: "systems/forbidden-lands/templates/item/spell/spell-sheet.hbs", 7 | }); 8 | } 9 | 10 | async getData(options = {}) { 11 | const data = await super.getData(options); 12 | 13 | data.spellTypes = [ 14 | { value: "SPELL.SPELL", label: "SPELL.SPELL" }, 15 | { value: "SPELL.POWER_WORD", label: "SPELL.POWER_WORD" }, 16 | { value: "SPELL.RITUAL", label: "SPELL.RITUAL" }, 17 | ]; 18 | 19 | return data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/item/critical-injury/critical-injury-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | export class ForbiddenLandsCriticalInjurySheet extends ForbiddenLandsItemSheet { 3 | static get defaultOptions() { 4 | return foundry.utils.mergeObject(super.defaultOptions, { 5 | ...super.defaultOptions, 6 | template: 7 | "systems/forbidden-lands/templates/item/critical-injury/critical-injury-sheet.hbs", 8 | width: 400, 9 | height: 310, 10 | }); 11 | } 12 | 13 | async getData(options = {}) { 14 | const data = await super.getData(options); 15 | 16 | data.lethalOptions = [ 17 | { value: "no", label: "LETHAL.NO" }, 18 | { value: "yes", label: "LETHAL.YES" }, 19 | ]; 20 | 21 | return data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/_stronghold.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .stronghold { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | width: 100%; 6 | .bio { 7 | padding: 8px; 8 | } 9 | } 10 | 11 | .forbidden-lands .stronghold .bio input { 12 | flex-grow: 1; 13 | margin: 0 8px; 14 | } 15 | 16 | .forbidden-lands .stronghold .bio .bio-row { 17 | align-items: baseline; 18 | display: flex; 19 | flex-direction: row; 20 | justify-content: space-between; 21 | margin: 5px 0; 22 | } 23 | 24 | .forbidden-lands .stronghold .bio .name { 25 | display: flex; 26 | flex-direction: row; 27 | } 28 | 29 | .forbidden-lands .stronghold .bio .location input { 30 | width: 15em; 31 | } 32 | 33 | .forbidden-lands .stronghold .bio .defense-rating input { 34 | width: 2em; 35 | } 36 | -------------------------------------------------------------------------------- /__fixtures__/FoundryVTT/Config/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "compressStatic": true, 3 | "fullscreen": false, 4 | "hostname": null, 5 | "language": "en.core", 6 | "localHostname": null, 7 | "port": 30000, 8 | "protocol": null, 9 | "proxyPort": null, 10 | "proxySSL": false, 11 | "routePrefix": null, 12 | "updateChannel": "stable", 13 | "upnp": false, 14 | "upnpLeaseDuration": null, 15 | "awsConfig": null, 16 | "compressSocket": true, 17 | "cssTheme": "foundry", 18 | "deleteNEDB": false, 19 | "hotReload": false, 20 | "passwordSalt": null, 21 | "sslCert": null, 22 | "sslKey": null, 23 | "world": null, 24 | "serviceConfig": null, 25 | "telemetry": false, 26 | "demo": { 27 | "worldName": "pwtest", 28 | "sourceZip": "../../source/pwtest.zip", 29 | "resetSeconds": 3600 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /templates/components/roll-engine/infos.hbs: -------------------------------------------------------------------------------- 1 |
    2 | {{#if roll.pushed}} 3 | {{#if (and roll.attributeTrauma attributeLabel)}} 4 |
    5 | 6 | {{roll.attributeTrauma}} 7 | 8 | 9 |  {{localize "ROLL.APPLIED_TRAUMA"}} {{localize attributeLabel}} 10 | 11 |
    12 | {{/if}} 13 | 14 | {{#if (and roll.gearDamage gears)}} 15 | {{#each gears as |gear|}} 16 | {{#if gear.value}} 17 |
    18 | 19 | {{gear.value}} 20 | 21 | 22 |  {{localize "ROLL.APPLIED_TRAUMA"}} {{localize gear.name}} 23 | 24 |
    25 | {{/if}} 26 | {{/each}} 27 | {{/if}} 28 | {{/if}} 29 |
    30 | -------------------------------------------------------------------------------- /src/actor/party/components/info-dialog.js: -------------------------------------------------------------------------------- 1 | export class InfoDialog { 2 | /** 3 | * Display informational message. 4 | * 5 | * @param {string} title 6 | * @param {string} message 7 | * @param {Function} onClose 8 | */ 9 | static show(title, message, onClose) { 10 | const d = new Dialog({ 11 | title: title, 12 | content: this.buildDivHtmlDialog(message), 13 | buttons: { 14 | ok: { 15 | icon: '', 16 | label: "OK", 17 | callback: onClose, 18 | }, 19 | }, 20 | default: "ok", 21 | close: onClose, 22 | }); 23 | d.render(true); 24 | } 25 | 26 | /** 27 | * @param {string} divContent 28 | */ 29 | static buildDivHtmlDialog(divContent) { 30 | return `
    ${divContent}
    `; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/item/talent/talent-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | 3 | export class ForbiddenLandsTalentSheet extends ForbiddenLandsItemSheet { 4 | static get defaultOptions() { 5 | return foundry.utils.mergeObject(super.defaultOptions, { 6 | ...super.defaultOptions, 7 | template: 8 | "systems/forbidden-lands/templates/item/talent/talent-sheet.hbs", 9 | }); 10 | } 11 | 12 | async getData(options = {}) { 13 | const data = await super.getData(options); 14 | 15 | data.talentTypes = [ 16 | { value: "general", label: "TALENT.GENERAL" }, 17 | { value: "kin", label: "TALENT.KIN" }, 18 | { value: "profession", label: "TALENT.PROFESSION" }, 19 | { value: "monster", label: "TALENT.MONSTER" }, 20 | { value: "weakness", label: "TALENT.WEAKNESS" }, 21 | ]; 22 | 23 | return data; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /e2e/foundry.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "@playwright/test"; 2 | 3 | const URL = "http://127.0.0.1:30000"; 4 | 5 | test("Launches Forbidden Lands", async ({ page }) => { 6 | await page.goto(URL); 7 | 8 | // Login 9 | const userSelect = page.locator("select[name='userid']"); 10 | await userSelect.selectOption({ label: "Gamemaster" }); 11 | 12 | const submitButton = page.locator("button[name='join']"); 13 | await submitButton.click(); 14 | 15 | // Check title 16 | const title = await page.title(); 17 | expect(title).toBe("PWTEST"); 18 | 19 | // Find the chat box and do a test roll 20 | const chatBox = page.locator("textarea#chat-message"); 21 | await chatBox.fill("/r 10"); 22 | await chatBox.press("Enter"); 23 | 24 | const message = page.locator("div.message-content").last(); 25 | await expect(message).toContainText("10"); 26 | }); 27 | -------------------------------------------------------------------------------- /src/system/core/fonts.js: -------------------------------------------------------------------------------- 1 | export function registerFonts() { 2 | CONFIG.fontDefinitions.Author = { 3 | editor: true, 4 | fonts: [ 5 | { 6 | urls: ["systems/forbidden-lands/fonts/author-semibold.otf"], 7 | weight: 600, 8 | }, 9 | { 10 | urls: ["systems/forbidden-lands/fonts/author-semibold-italic.otf"], 11 | weight: 600, 12 | style: "italic", 13 | }, 14 | { urls: ["systems/forbidden-lands/fonts/author-medium.otf"] }, 15 | { 16 | urls: ["systems/forbidden-lands/fonts/author-medium-italic.otf"], 17 | style: "italic", 18 | }, 19 | ], 20 | }; 21 | CONFIG.fontDefinitions["IM Fell Great Primer"] = { 22 | editor: true, 23 | fonts: [ 24 | { urls: ["systems/forbidden-lands/fonts/imfe-gprm.otf"] }, 25 | { 26 | urls: ["systems/forbidden-lands/fonts/imfe-gpit.otf"], 27 | style: "italic", 28 | }, 29 | ], 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /tools/version.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "bun"; 2 | // Use Bun when Bun supports node:fs/promises fully 3 | import { writeFile } from "node:fs/promises"; 4 | 5 | // Version package 6 | await $`bunx changeset version`; 7 | 8 | // Import versioned package.json 9 | const { 10 | default: { version }, 11 | } = await import("../package.json", { 12 | with: { type: "json" }, 13 | }); 14 | 15 | // Import system.json 16 | const { default: manifest } = await import("../system.json", { 17 | with: { type: "json" }, 18 | }); 19 | 20 | // Update and Write system.json 21 | manifest.version = version; 22 | manifest.download = manifest.download.replace(/v\d+\.\d+\.\d+/, `v${version}`); 23 | await writeFile("system.json", `${JSON.stringify(manifest, null, "\t")}\n`); 24 | 25 | // Format system.json 26 | await $`bunx biome format --write .`; 27 | await $`git add -A`; 28 | await $`git commit -m "chore: Version Packages"`; 29 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/_monster.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .monster { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | width: 100%; 6 | 7 | .bio { 8 | padding: 8px; 9 | 10 | .bio-row { 11 | display: flex; 12 | flex-direction: column; 13 | align-items: flex-end; 14 | gap: 8px; 15 | 16 | label { 17 | display: flex; 18 | gap: 4px; 19 | } 20 | 21 | input[type="number"] { 22 | width: 50px; 23 | } 24 | input[name="name"] { 25 | width: 235px; 26 | } 27 | 28 | .armor { 29 | & > div { 30 | display: flex; 31 | gap: 4px; 32 | } 33 | 34 | .roll-armor { 35 | white-space: nowrap; 36 | padding: 0; 37 | margin: 0; 38 | } 39 | } 40 | 41 | .description { 42 | width: 300px; 43 | } 44 | } 45 | } 46 | 47 | .attributes { 48 | grid-column-end: span 2; 49 | } 50 | 51 | .conditions { 52 | display: none; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/tests/foundry-scripts/scripts/items.js: -------------------------------------------------------------------------------- 1 | import crudTest from "../helpers/crud-test"; 2 | const CLSName = "Item"; 3 | export const individual = { 4 | armor: () => crudTest(CLSName, "armor", "Armor1"), 5 | weapon: () => crudTest(CLSName, "weapon", "Weapon1"), 6 | spell: () => crudTest(CLSName, "spell", "Spell1"), 7 | talent: () => crudTest(CLSName, "talent", "Talent1"), 8 | gear: () => crudTest(CLSName, "gear", "Gear1"), 9 | rawMaterial: () => crudTest(CLSName, "rawMaterial", "RawMaterial1"), 10 | hireling: () => crudTest(CLSName, "hireling", "Hireling1"), 11 | building: () => crudTest(CLSName, "building", "Building1"), 12 | criticalInjury: () => crudTest(CLSName, "criticalInjury", "CriticalInjury1"), 13 | monsterAttack: () => crudTest(CLSName, "monsterAttack", "MonsterAttack1"), 14 | }; 15 | // biome-ignore lint/complexity/noForEach: 16 | export const all = () => Object.values(individual).forEach((item) => item()); 17 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/tab/_stronghold-gear.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .stronghold .gears { 2 | padding-bottom: 8px; 3 | 4 | .items .header { 5 | margin: 0; 6 | width: 100%; 7 | } 8 | 9 | .items div+.header { 10 | margin-top: 8px; 11 | } 12 | 13 | .gear { 14 | align-items: center; 15 | } 16 | 17 | .name { 18 | align-items: center; 19 | flex-basis: 42%; 20 | display: flex; 21 | } 22 | 23 | .header .name, 24 | .name .name, 25 | .bonus, 26 | .quantity, 27 | .weight, 28 | .button { 29 | margin-left: 4px; 30 | } 31 | 32 | .bonus, 33 | .weight, 34 | .quantity { 35 | flex-basis: 15%; 36 | text-align: center; 37 | } 38 | 39 | input.quantity { 40 | flex-basis: 8%; 41 | margin-right: 3%; 42 | margin-left: 5%; 43 | } 44 | 45 | .bonus.broken { 46 | color: var(--color-theme-accent); 47 | } 48 | 49 | .button { 50 | flex-basis: 10%; 51 | font-size: var(--font-size-12); 52 | text-align: right; 53 | } 54 | } -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "organizeImports": { 4 | "enabled": false 5 | }, 6 | "formatter": { 7 | "ignore": [ 8 | "*devbox*", 9 | "./forbidden-lands.js", 10 | "./forbidden-lands.css", 11 | "./foundryconfig.json", 12 | "./types/foundry/*", 13 | "__fixtures__/*", 14 | "playwright-report/*", 15 | ".vscode/*" 16 | ] 17 | }, 18 | "linter": { 19 | "enabled": true, 20 | "ignore": [ 21 | "*devbox*", 22 | "./forbidden-lands.js", 23 | "./forbidden-lands.css", 24 | "./foundryconfig.json", 25 | "./types/foundry/*", 26 | "__fixtures__/*", 27 | "playwright-report/*", 28 | ".vscode/*" 29 | ], 30 | "rules": { 31 | "recommended": true, 32 | "style": { 33 | "noParameterAssign": "off" 34 | }, 35 | "complexity": { 36 | "noStaticOnlyClass": "off", 37 | "noThisInStatic": "off", 38 | "useLiteralKeys": "off" 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /templates/item/armor/main-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 7 |
    8 |
    9 | 10 | 13 |
    14 |
    15 | 16 | {{#if editable}} 17 | {{editor system.enrichedFeatures target="system.features" owner=owner button=true editable=true}} 18 | {{else}} 19 |
    20 | {{{system.enrichedFeatures}}} 21 |
    22 | {{/if}} 23 |
    24 |
    25 |
    26 | {{> systems/forbidden-lands/templates/components/modifiers-component.hbs }} 27 |
    -------------------------------------------------------------------------------- /templates/actor/party/party-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | {{localize "FLPS.TAB.MAIN"}} 5 | {{localize "FLPS.TAB.TRAVEL"}} 6 | {{localize "TAB.NOTE"}} 7 |
    8 | 9 |
    10 |
    11 | {{> systems/forbidden-lands/templates/actor/party/sheet-tabs/main-tab.hbs}} 12 |
    13 |
    14 | {{> systems/forbidden-lands/templates/actor/party/sheet-tabs/travel-tab.hbs}} 15 |
    16 |
    17 | {{editor system.description target="system.description" button=true owner=owner editable=true}} 18 |
    19 |
    20 |
    21 |
    22 | -------------------------------------------------------------------------------- /tools/release.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "bun"; 2 | 3 | import rootConfig from "../package.json" with { type: "json" }; 4 | 5 | const { version } = rootConfig; 6 | const tag = `v${version}`; 7 | const releaseLine = `v${version.split(".")[0]}`; 8 | 9 | const { exitCode, stderr } = 10 | await $`git ls-remote --exit-code origin --tags refs/tags/${tag}` 11 | .nothrow() 12 | .quiet(); 13 | 14 | if (exitCode === 0) { 15 | console.log( 16 | `Action is not being published because version ${tag} is already published`, 17 | ); 18 | process.exit(0); 19 | } 20 | 21 | if (exitCode !== 2) { 22 | throw new Error(`git ls-remote exited with ${exitCode}:\n${stderr}`); 23 | } 24 | 25 | await $`git checkout --detach`; 26 | await $`git add --force forbidden-lands.js forbidden-lands.css`; 27 | await $`git commit -m "chore(release): ${tag}"`; 28 | await $`git tag ${tag} -m ${tag}`.then(() => console.log(`New tag: ${tag}`)); 29 | 30 | await $`git push --force --follow-tags origin HEAD:refs/heads/${releaseLine}`; 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | version: 12 | name: Version 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: oven-sh/setup-bun@v1 17 | with: 18 | bun-version: canary 19 | - name: Install Dependencies 20 | run: bun install 21 | - name: Build 22 | run: bun run build 23 | - name: Create Release or Version 24 | uses: changesets/action@cdfd21f8180220750990b6a7924b2a990b40c87f 25 | with: 26 | commit: 'chore(release): Version Package' 27 | title: 'Version Package' 28 | version: bun run version 29 | publish: bun run release 30 | createGithubReleases: true 31 | createGithubReleaseAsDraft: true 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /templates/actor/party/components/action-component.hbs: -------------------------------------------------------------------------------- 1 |
    3 |
    4 | {{#if action.journalLink}} 5 | {{localize action.name}} 9 | {{else}} 10 | {{localize action.name}} 11 | {{/if}} 12 |
    13 |
    14 | {{#each action.buttons as |button|}} 15 | 17 | {{/each}} 18 |
    19 |
    20 |
      21 | {{#each assignedCharacters as |partyMember|}} 22 | {{> systems/forbidden-lands/templates/actor/party/components/member-component.hbs partyMember=partyMember}} 23 | {{/each}} 24 |
    25 |
    26 |
    -------------------------------------------------------------------------------- /templates/item/hireling/hireling-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 |
    8 | 9 | 10 |
    11 |
    12 | 13 | 14 |
    15 |
    16 | 17 | 18 |
    19 |
    20 |
    21 |
    22 | 23 | {{editor system.description target="system.description" owner=owner button=true editable=true}} 24 |
    25 |
    -------------------------------------------------------------------------------- /templates/components/tables-config.hbs: -------------------------------------------------------------------------------- 1 | {{#*inline "tables" config scope allTables}} 2 | {{#each config as |table|}} 3 | 9 | {{/each}} 10 | {{/inline}} 11 | 12 |
    13 |
    14 | {{localize "CONFIG.TABLE_CONFIG.MISHAPS"}} 15 | {{> tables config=mishapTables scope="mishap" allTables=tables}} 16 |
    17 |
    18 | {{localize "CONFIG.TABLE_CONFIG.RANDOM_ENCOUNTERS"}} 19 | {{> tables config=encounterTables scope="encounter" allTables=tables}} 20 |
    21 |
    22 | {{localize "CONFIG.TABLE_CONFIG.OTHER"}} 23 | {{> tables config=otherTables scope="other" allTables=tables}} 24 |
    25 | 26 |
    27 | -------------------------------------------------------------------------------- /templates/item/_shared-template-tabs/supply-tab.hbs: -------------------------------------------------------------------------------- 1 | {{#if (or showCostField showSupplyField)}} 2 |
    3 | {{#if showCostField}} 4 | 5 | 6 | {{/if}} 7 | {{#if showSupplyField}} 8 | 9 | 10 | {{/if}} 11 |
    12 | {{/if}} 13 | {{#if showCraftingFields}} 14 |
    15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
    27 | {{/if}} 28 | -------------------------------------------------------------------------------- /src/legacy-styles/system/_foundry-overrides.scss: -------------------------------------------------------------------------------- 1 | /* -------------- */ 2 | /* Font Awesome */ 3 | /* Overrides */ 4 | /* -------------- */ 5 | 6 | .fa-suitcase { 7 | font-family: RPGAwesome; 8 | font-size: var(--font-size-16); 9 | transform: translateY(1px); 10 | 11 | &:before { 12 | content: "\ea72"; 13 | } 14 | } 15 | 16 | .fa-comments { 17 | font-family: RPGAwesome; 18 | font-size: var(--font-size-16); 19 | transform: translateY(1px); 20 | 21 | &:before { 22 | content: "\ea8f"; 23 | } 24 | } 25 | 26 | .fa-fist-raised { 27 | font-family: Swordlings; 28 | transform: translate(1.3px, -0.6px); 29 | font-size: var(--font-size-16); 30 | 31 | &:before { 32 | content: "x"; 33 | } 34 | } 35 | 36 | /* ------------- */ 37 | 38 | 39 | #pause { 40 | background: none; 41 | 42 | &>img { 43 | width: 200px; 44 | height: 200px; 45 | top: -50px; 46 | left: calc(50% - 100px); 47 | opacity: 0.75; 48 | } 49 | 50 | &>figcaption { 51 | font-family: "IM Fell DW Pica SC"; 52 | font-size: var(--font-size-24); 53 | text-shadow: 0px 3px 5px rgba(0, 0, 0, 1); 54 | } 55 | } -------------------------------------------------------------------------------- /src/changelog/_index.scss: -------------------------------------------------------------------------------- 1 | .app.fbl.changelog { 2 | .dialog-content { 3 | font-family: var(--font-alternative); 4 | padding-inline: 16px; 5 | line-height: 165%; 6 | } 7 | 8 | h1, 9 | h2, 10 | h3 { 11 | font-family: var(--font-header-sc); 12 | border: none; 13 | } 14 | 15 | h1 { 16 | text-align: center; 17 | } 18 | 19 | ul { 20 | list-style-type: "\2725 "; 21 | } 22 | 23 | code { 24 | background: var(--color-border-light-1); 25 | padding: 0.1em 0.2em; 26 | border-radius: 0.25em; 27 | display: inline; 28 | user-select: all; 29 | } 30 | 31 | details { 32 | margin-bottom: 8px; 33 | 34 | summary>i { 35 | transition: transform 0.2s ease-in-out; 36 | } 37 | 38 | &[open] summary { 39 | margin-bottom: 20px; 40 | 41 | &>i { 42 | transform: rotate(90deg); 43 | } 44 | } 45 | } 46 | 47 | summary { 48 | display: flex; 49 | align-items: center; 50 | gap: 8px; 51 | 52 | h2 { 53 | margin: 0px; 54 | 55 | span { 56 | font-size: var(--font-size-16); 57 | } 58 | } 59 | 60 | &::marker { 61 | content: none; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/journal/styles/_module-styles.scss: -------------------------------------------------------------------------------- 1 | :where(.fbl-box) { 2 | border-style: solid; 3 | border-image: url("./assets/journal-art/box-border-top-bottom.webp") 60 0 round; 4 | border-image-width: 20px 0; 5 | font-family: var(--font-primary); 6 | line-height: 135%; 7 | padding: 25px 0; 8 | margin: 1rem; 9 | 10 | &.info-right, 11 | &.fbl-journal-info-box { 12 | width: 320px; 13 | float: right; 14 | } 15 | 16 | &.info-left { 17 | width: 320px; 18 | float: left; 19 | } 20 | 21 | h3, 22 | h4 { 23 | font-family: var(--font-primary); 24 | text-transform: unset; 25 | font-weight: 600; 26 | font-size: var(--font-size-18); 27 | margin: 0; 28 | } 29 | 30 | table { 31 | th { 32 | font-size: var(--font-size-14); 33 | } 34 | 35 | td { 36 | text-align: center; 37 | } 38 | } 39 | 40 | .inline-heading { 41 | font-family: var(--font-primary); 42 | } 43 | } 44 | 45 | .fbl-italic { 46 | font-style: italic; 47 | } 48 | 49 | .fbl-italic.fbl-centered, 50 | .italic-intro { 51 | line-height: 200%; 52 | margin: 2rem 3rem; 53 | font-size: var(--font-size-18); 54 | font-weight: 600; 55 | } 56 | -------------------------------------------------------------------------------- /src/item/monster-attack/monster-attack-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | export class ForbiddenLandsMonsterAttackSheet extends ForbiddenLandsItemSheet { 3 | static get defaultOptions() { 4 | return foundry.utils.mergeObject(super.defaultOptions, { 5 | ...super.defaultOptions, 6 | template: 7 | "systems/forbidden-lands/templates/item/monster-attack/monster-attack-sheet.hbs", 8 | }); 9 | } 10 | 11 | async getData(options = {}) { 12 | const data = await super.getData(options); 13 | 14 | data.rangeOptions = [ 15 | { value: "arm", label: "RANGE.ARM" }, 16 | { value: "near", label: "RANGE.NEAR" }, 17 | { value: "short", label: "RANGE.SHORT" }, 18 | { value: "long", label: "RANGE.LONG" }, 19 | { value: "distant", label: "RANGE.DISTANT" }, 20 | ]; 21 | 22 | data.damageTypeOptions = [ 23 | { value: "stab", label: "ATTACK.STAB" }, 24 | { value: "slash", label: "ATTACK.SLASH" }, 25 | { value: "blunt", label: "ATTACK.BLUNT" }, 26 | { value: "fear", label: "ATTACK.FEAR" }, 27 | { value: "other", label: "ATTACK.OTHER" }, 28 | ]; 29 | 30 | return data; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /templates/item/talent/talent-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 | 8 |
    9 | 10 |
    11 | 12 | 15 | {{#if ranks}} 16 | 17 |
    18 | 19 |
    20 | {{/if}} 21 |
    22 |
    23 |
    24 |
    25 | 26 | {{editor system.description target="system.description" owner=owner button=true editable=true}} 27 |
    28 | {{> systems/forbidden-lands/templates/components/modifiers-component.hbs }} 29 |
    30 |
    31 | -------------------------------------------------------------------------------- /src/legacy-styles/components/_util-classes.scss: -------------------------------------------------------------------------------- 1 | .skullz { 2 | font-family: skullz; 3 | font-size: 1.3em; 4 | line-height: 1em; 5 | height: 1em; 6 | transform: translateY(0.2em); 7 | margin: 0 0.2rem 0 0; 8 | display: block; 9 | font-weight: 400; 10 | } 11 | 12 | .sword { 13 | font-family: swordlings; 14 | font-size: 0.8em; 15 | margin: 0 0.2rem; 16 | display: block; 17 | font-weight: 600; 18 | } 19 | 20 | .special-number { 21 | line-height: 1.2rem; 22 | display: block; 23 | } 24 | 25 | .fbl-button { 26 | font-weight: 600; 27 | border: 2px solid var(--color-border); 28 | box-shadow: 0 0 5px transparent; 29 | width: max(2em, 60%); 30 | display: block; 31 | margin: 0 auto; 32 | border-radius: 6px; 33 | transition: 34 | transform 80ms ease-in-out, 35 | box-shadow 80ms ease-in-out; 36 | 37 | &:hover, 38 | &:focus { 39 | box-shadow: 0 0 5px var(--color-theme-accent); 40 | border: 2px solid var(--color-theme-accent); 41 | } 42 | 43 | &:active { 44 | transform: scale(0.96); 45 | } 46 | } 47 | 48 | .chat-flavor { 49 | font-size: var(--font-size-14); 50 | display: block; 51 | text-align: center; 52 | font-style: italic; 53 | font-weight: bold; 54 | } -------------------------------------------------------------------------------- /templates/components/roll-engine/tooltip.hbs: -------------------------------------------------------------------------------- 1 |
    2 | {{#each parts}} 3 |
    4 |
    5 |
    6 | {{#if this.flavor}}{{this.flavor}}{{else}}{{this.formula}}{{/if}} 7 | x{{this.total}} {{#unless (or (eq this.type 8 | "skill") (eq this.type "neg"))}}{{#if this.banes}} | l 9 | {{this.banes}} 10 | {{/if}}{{/unless}} 11 |
    12 |
      13 | {{#if ../pushed}} {{#each this.rolls as | lineRolls |}} 14 |
      15 | {{#each lineRolls}} {{#if this}} 16 | {{{this.result}}} 17 | {{else}} 18 |   19 | {{/if}} {{/each}} 20 |
      21 | {{/each}} {{else}} {{#each this.rolls}} 22 |
    1. {{{this.result}}}
    2. 23 | {{/each}} {{/if}} 24 |
    25 |
    26 |
    27 | {{/each}} 28 |
    29 | -------------------------------------------------------------------------------- /templates/item/critical-injury/critical-injury-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 | 8 | 9 | 10 | 13 | {{#if lethal}} 14 | 15 | 16 | {{/if}} 17 | 18 | 19 |
    20 |
    21 |
    22 |
    23 | 24 | {{editor system.effect target="system.effect" owner=owner button=true editable=true}} 25 |
    26 | {{> systems/forbidden-lands/templates/components/modifiers-component.hbs }} 27 |
    28 |
    29 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | testDir: "./e2e", 5 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 6 | forbidOnly: !!process.env.CI, 7 | /* Retry on CI only */ 8 | retries: process.env.CI ? 2 : 0, 9 | /* Opt out of parallel tests on CI. */ 10 | workers: process.env.CI ? 1 : undefined, 11 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 12 | reporter: "html", 13 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 14 | use: { 15 | /* Base URL to use in actions like `await page.goto('/')`. */ 16 | // baseURL: 'http://127.0.0.1:3000', 17 | 18 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 19 | trace: "on-first-retry", 20 | }, 21 | 22 | /* Configure projects for major browsers */ 23 | projects: [ 24 | { 25 | name: "chromium", 26 | use: { ...devices["Desktop Chrome"] }, 27 | }, 28 | ], 29 | 30 | /* Run your local dev server before starting the tests */ 31 | webServer: { 32 | command: "npm run foundry", 33 | url: "http://127.0.0.1:30000", 34 | reuseExistingServer: !process.env.CI, 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /src/system/core/styles/variables.scss: -------------------------------------------------------------------------------- 1 | body { 2 | // Theme Colors 3 | --color-theme-text: #222020; 4 | --color-theme-background: #fff; 5 | --color-theme-contrast: var(--color-theme-gray-200); 6 | --color-theme-accent: #a00404; 7 | --color-theme-gray-200: #dddddd; 8 | --color-theme-gray-400: #999999; 9 | --color-theme-gray-600: #707070; 10 | --color-theme-gray-800: #333333; 11 | --color-theme-alt-200: #ffb994; 12 | --color-theme-alt-600: #36b5a7; 13 | --color-theme-alt-800: #005d67; 14 | --color-theme-alt-900: #433a3f; 15 | --color-theme-alt-1000: #bcb192; 16 | 17 | --font-header: "IM Fell DW Pica", serif; 18 | --font-header-sc: "IM Fell DW Pica SC", serif; 19 | --font-primary: Branding, Author, MavenPro, Ubuntu, sans-serif; 20 | --font-alternative: "IM Fell Great Primer", serif; 21 | 22 | // Foundry Fonts 23 | --font-body: var(--font-primary); 24 | 25 | // Shadow Colors 26 | --color-shadow-primary: var(--color-theme-alt-600); 27 | --color-shadow-highlight: var(--color-theme-alt-800); 28 | 29 | // Border Colors 30 | --color-border-highlight: var(--color-theme-alt-600); 31 | --color-border-highlight-alt: var(--color-theme-alt-800); 32 | 33 | // Hyperlink 34 | --color-text-hyperlink: var(--color-theme-accent); 35 | } 36 | 37 | body.theme-dark { 38 | --color-theme-background: var(--color-theme-alt-1000); 39 | } -------------------------------------------------------------------------------- /src/item/armor/armor-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | 3 | export class ForbiddenLandsArmorSheet extends ForbiddenLandsItemSheet { 4 | static get defaultOptions() { 5 | return foundry.utils.mergeObject(super.defaultOptions, { 6 | ...super.defaultOptions, 7 | template: "systems/forbidden-lands/templates/item/armor/armor-sheet.hbs", 8 | tabs: [ 9 | { 10 | navSelector: ".sheet-tabs", 11 | contentSelector: ".sheet-body", 12 | initial: "main", 13 | }, 14 | ], 15 | }); 16 | } 17 | 18 | async getData(options = {}) { 19 | const data = await super.getData(options); 20 | 21 | data.weightOptions = [ 22 | { value: "none", label: "WEIGHT.NONE" }, 23 | { value: "tiny", label: "WEIGHT.TINY" }, 24 | { value: "light", label: "WEIGHT.LIGHT" }, 25 | { value: "regular", label: "WEIGHT.REGULAR" }, 26 | { value: "heavy", label: "WEIGHT.HEAVY" }, 27 | ]; 28 | 29 | data.partOptions = [ 30 | { value: "body", label: "ARMOR.BODY" }, 31 | { value: "head", label: "ARMOR.HELMET" }, 32 | { value: "other", label: "ARMOR.OTHER" }, 33 | ]; 34 | 35 | data.system.enrichedFeatures = 36 | await foundry.applications.ux.TextEditor.enrichHTML( 37 | data.system.features ?? "", 38 | { 39 | async: true, 40 | secrets: game.user.isGM, 41 | relativeTo: this.item, 42 | }, 43 | ); 44 | 45 | return data; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /templates/item/gear/main-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 20 |
    21 | {{#if showEffectField}} 22 |
    23 | 24 | {{#if isGM}} 25 | 26 | 27 | 28 | {{/if}} 29 | {{editor system.effect target="system.effect" owner=owner button=true editable=true}} 30 |
    31 | {{/if}} 32 |
    33 |
    34 | {{> systems/forbidden-lands/templates/components/modifiers-component.hbs }} 35 |
    36 | -------------------------------------------------------------------------------- /templates/item/raw-material/raw-material-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 |
    8 | 9 | 10 |
    11 |
    12 |
    13 | 14 | 15 |
    16 |
    17 | 18 | 19 |
    20 |
    21 |
    22 |
    23 |
    24 | 25 | 26 |
    27 |
    28 | 29 | 30 |
    31 |
    32 | 33 | {{editor system.tools target="system.tools" owner=owner button=true editable=true}} 34 |
    35 |
    -------------------------------------------------------------------------------- /templates/actor/stronghold/sheet-tabs/hireling-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | {{localize "HIRELING.NAME"}} 5 | {{localize "HIRELING.SALARY"}} 6 | {{localize "HIRELING.NUMBER"}} 7 | 8 |
    9 |
    10 | {{#each items as |item|}} 11 | {{#if item.isHireling}} 12 |
    13 |
    14 |
    15 | {{item.name}} 16 |
    {{{item.system.description}}}
    17 |
    18 |
    19 |
    {{item.system.salary}}
    20 | 21 |
    22 | 23 | 24 | 25 |
    26 |
    27 | {{/if}} 28 | {{/each}} 29 |
    30 |
    31 |
    -------------------------------------------------------------------------------- /src/actor/party/components/helpers.js: -------------------------------------------------------------------------------- 1 | import { InfoDialog } from "./info-dialog.js"; 2 | 3 | export class Helpers { 4 | static getCharacterDiceRoller(character) { 5 | character = character instanceof Actor ? character : game.user.character; 6 | if (!character) return; 7 | 8 | let charSheetClass = () => {}; 9 | for (const chName in CONFIG.Actor.sheetClasses.character) { 10 | if (chName === "forbidden-lands.ForbiddenLandsCharacterSheet") { 11 | charSheetClass = CONFIG.Actor.sheetClasses.character[chName].cls; 12 | break; 13 | } 14 | } 15 | let charSheet; 16 | for (const key in character.apps) { 17 | if (character.apps[key] instanceof charSheetClass) { 18 | charSheet = character.apps[key]; 19 | break; 20 | } 21 | } 22 | if (!charSheet) { 23 | InfoDialog.show( 24 | game.i18n.localize("FLPS.UI.ATTENTION"), 25 | game.i18n.localize("FLPS.UI.ERROR.OPEN_SHEET"), 26 | ); 27 | return null; 28 | } 29 | 30 | return charSheet.diceRoller; 31 | } 32 | 33 | static getOwnedCharacters(characterIds) { 34 | characterIds = 35 | typeof characterIds !== "object" && characterIds !== "" 36 | ? [characterIds] 37 | : characterIds; 38 | const characters = []; 39 | for (let i = 0; i < characterIds.length; i++) { 40 | const actor = game.actors.get(characterIds[i]); 41 | if (actor?.isOwner) characters.push(game.actors.get(characterIds[i])); 42 | } 43 | 44 | return characters; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /templates/actor/character/sheet-tabs/bio-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | 11 |

    12 | {{editor system.bio.pride.value target="system.bio.pride.value" owner=owner button=true editable=true}} 13 |
    14 |
    15 |

    16 | {{localize "HEADER.DARK_SECRET"}} 17 |

    18 | {{editor 19 | system.bio.darkSecret.value 20 | target="system.bio.darkSecret.value" 21 | owner=owner 22 | button=true 23 | editable=true 24 | }} 25 |
    26 |
    27 |

    28 | {{localize "HEADER.FACE"}} 29 |

    30 | {{editor system.bio.face.value target="system.bio.face.value" owner=owner button=true editable=true}} 31 |
    32 |
    33 |

    34 | {{localize "HEADER.BODY"}} 35 |

    36 | {{editor system.bio.body.value target="system.bio.body.value" owner=owner button=true editable=true}} 37 |
    38 |
    39 |

    40 | {{localize "HEADER.CLOTHING"}} 41 |

    42 | {{editor system.bio.clothing.value target="system.bio.clothing.value" owner=owner button=true editable=true}} 43 |
    44 |
    -------------------------------------------------------------------------------- /templates/actor/stronghold/sheet-tabs/building-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 | {{localize "BUILDING.NAME"}} 6 | {{localize "BUILDING.NUMBER"}} 7 | 10 |
    11 |
    12 | {{#each items as |item|}} 13 | {{#if item.isBuilding}} 14 |
    15 |
    16 |
    17 | {{item.name}} 18 |
    {{{item.system.effect}}}
    19 |
    20 |
    21 | 27 |
    28 | 29 | 32 | 35 |
    36 |
    37 | {{/if}} 38 | {{/each}} 39 |
    40 |
    41 |
    42 |
    -------------------------------------------------------------------------------- /esbuild.config.ts: -------------------------------------------------------------------------------- 1 | import { context } from "esbuild"; 2 | import { sassPlugin } from "esbuild-sass-plugin"; 3 | import { existsSync } from "node:fs"; 4 | import { rm } from "node:fs/promises"; 5 | import { args } from "./tools/args-parser.js"; 6 | import templatePathsPromise from "./tools/get-template-paths.js"; 7 | 8 | const templatePaths = await templatePathsPromise; 9 | 10 | const development = Object.hasOwn(args, "development"); 11 | console.log( 12 | `Building Forbidden Lands for ${development ? "development" : "production"}...`, 13 | ); 14 | 15 | if (existsSync("./forbidden-lands.js")) await rm("./forbidden-lands.js"); 16 | if (existsSync("./forbidden-lands.css")) await rm("./forbidden-lands.css"); 17 | 18 | const ctx = await context({ 19 | bundle: true, 20 | entryPoints: ["./src/forbidden-lands.ts", "./src/forbidden-lands.scss"], 21 | outdir: "./", 22 | format: "iife", 23 | logLevel: "info", 24 | sourcemap: development ? "inline" : false, 25 | ignoreAnnotations: development, 26 | minifyWhitespace: true, 27 | minifySyntax: true, 28 | drop: development ? [] : ["console", "debugger"], 29 | define: { 30 | GLOBALPATHS: JSON.stringify(templatePaths), 31 | }, 32 | plugins: [ 33 | sassPlugin({ 34 | logger: { 35 | warn: () => "", 36 | }, 37 | }), 38 | { 39 | name: "external-files", 40 | setup(inBuild) { 41 | inBuild.onResolve( 42 | { filter: /(\.\/assets|\.\/fonts|\/systems)/ }, 43 | () => { 44 | return { external: true }; 45 | }, 46 | ); 47 | }, 48 | }, 49 | ], 50 | }); 51 | 52 | ctx.rebuild(); 53 | 54 | if (development) ctx.watch(); 55 | else ctx.dispose(); 56 | -------------------------------------------------------------------------------- /templates/actor/character/character-limited-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 | 7 |
    8 | 9 | 10 | 15 | 18 | 23 | 26 | 27 | 28 | 33 | 36 | 41 | 44 | 49 | 52 | 53 |
    11 | 14 | 16 | 17 | 19 | 22 | 24 | ??? 25 |
    29 | 32 | 34 | ??? 35 | 37 | 40 | 42 | ??? 43 | 45 | 48 | 50 | ??? 51 |
    54 |
    55 |
    56 |
    57 |
    58 | 59 | {{! prettier-ignore }} 60 | {{editor system.bio.note.value target="system.bio.note.value"}} 61 |
    62 |
    63 |
    64 |
    65 | -------------------------------------------------------------------------------- /templates/item/spell/spell-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 | 8 |
    9 | 10 |
    11 | 12 |
    13 | 14 |
    15 | 16 |
    17 | 20 |
    21 |
    22 |
    23 |
    24 |
    25 | 26 | 27 |
    28 |
    29 | 30 | 31 |
    32 |
    33 | 34 | 35 |
    36 |
    37 | 38 | {{editor system.description target="system.description" owner=owner button=true editable=true}} 39 |
    40 |
    41 |
    42 | -------------------------------------------------------------------------------- /src/legacy-styles/system/_fonts.scss: -------------------------------------------------------------------------------- 1 | $ra-font-path: "./fonts"; 2 | @import "~rpg-awesome/scss/rpg-awesome.scss"; 3 | 4 | @font-face { 5 | font-family: "IM Fell DW Pica"; 6 | src: url("./fonts/imfe-dprm.otf"); 7 | } 8 | 9 | @font-face { 10 | font-family: "IM Fell DW Pica"; 11 | src: url("./fonts/imfe-dpit.otf"); 12 | font-style: italic; 13 | } 14 | 15 | @font-face { 16 | font-family: "IM Fell DW Pica SC"; 17 | src: url("./fonts/imfe-dwpscr.ttf"); 18 | } 19 | 20 | @font-face { 21 | font-family: "IM Fell Three Line Pica"; 22 | src: url("./fonts/imfe-tlrm.otf"); 23 | } 24 | 25 | @font-face { 26 | font-family: "IM Fell Great Primer"; 27 | src: url("./fonts/imfe-gprm.otf"); 28 | } 29 | 30 | @font-face { 31 | font-family: "IM Fell Great Primer"; 32 | src: url("./fonts/imfe-gpit.otf"); 33 | font-style: italic; 34 | } 35 | 36 | @font-face { 37 | font-family: "Author"; 38 | size-adjust: 110%; 39 | src: url("./fonts/author-medium.otf"); 40 | } 41 | 42 | @font-face { 43 | font-family: "Author"; 44 | font-weight: bold; 45 | size-adjust: 110%; 46 | src: url("./fonts/author-semibold.otf"); 47 | } 48 | 49 | @font-face { 50 | font-family: "Author"; 51 | font-style: italic; 52 | size-adjust: 110%; 53 | src: url("./fonts/author-medium-italic.otf"); 54 | } 55 | 56 | @font-face { 57 | font-family: "Author"; 58 | font-style: italic; 59 | font-weight: bold; 60 | size-adjust: 110%; 61 | src: url("./fonts/author-semibold-italic.otf"); 62 | } 63 | 64 | @font-face { 65 | font-family: "skullz"; 66 | src: url("./fonts/skullz.ttf"); 67 | } 68 | 69 | @font-face { 70 | font-family: "Swordlings"; 71 | src: url("./fonts/swordlings.ttf"); 72 | } 73 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/tab/_talent.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .talent-tab { 2 | display: grid; 3 | gap: 8px; 4 | grid-template-columns: 1fr 1fr; 5 | height: 100%; 6 | } 7 | 8 | .forbidden-lands .character .talents { 9 | display: flex; 10 | flex-direction: column; 11 | overflow: hidden; 12 | padding-bottom: 8px; 13 | } 14 | 15 | .forbidden-lands .character .talents .talent { 16 | align-items: center; 17 | } 18 | 19 | .forbidden-lands .character .talents .talent .name { 20 | align-items: center; 21 | flex-basis: 65%; 22 | display: flex; 23 | } 24 | 25 | .forbidden-lands .character .talents .talent .name .name { 26 | margin-left: 4px; 27 | } 28 | 29 | .forbidden-lands .character .talents .talent .rank { 30 | flex-basis: 15%; 31 | text-align: center; 32 | } 33 | 34 | .forbidden-lands .character .talents .talent .button { 35 | flex-basis: 20%; 36 | font-size: var(--font-size-12); 37 | text-align: right; 38 | } 39 | 40 | .forbidden-lands .character .spells { 41 | display: flex; 42 | flex-direction: column; 43 | overflow: hidden; 44 | padding-bottom: 8px; 45 | } 46 | 47 | .forbidden-lands .character .spells .spell { 48 | align-items: center; 49 | } 50 | 51 | .forbidden-lands .character .spells .spell .name { 52 | align-items: center; 53 | flex-basis: 65%; 54 | display: flex; 55 | } 56 | 57 | .forbidden-lands .character .spells .spell .name .roll-spell { 58 | font-weight: normal; 59 | } 60 | 61 | .forbidden-lands .character .spells .spell .rank { 62 | flex-basis: 15%; 63 | text-align: center; 64 | } 65 | 66 | .forbidden-lands .character .spells .spell .button { 67 | flex-basis: 20%; 68 | font-size: var(--font-size-12); 69 | text-align: right; 70 | } 71 | -------------------------------------------------------------------------------- /src/utils/semver-compare.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Takes up to three semver strings and an options object. 3 | * @param {String} min 4 | * @param {String} curr 5 | * @param {String} max 6 | * @param {Object} opt Properties: eqMin, eqMax, gEqMin, lEqMax 7 | * @returns {Boolean} Comparison result 8 | */ 9 | export default function semverComp(min, curr, max, opt = {}) { 10 | if ((!min && !max) || !curr) 11 | throw new Error( 12 | `Missing Comparators. min ${min}; curr ${curr}; max ${max}`, 13 | ); 14 | 15 | // Type converting from String to Number. 16 | min = min && coerceNum(min); 17 | curr = curr && coerceNum(curr); 18 | max = max && coerceNum(max); 19 | 20 | if (min && max && opt.eqMin && opt.eqMax) return min === curr && curr === max; 21 | if (min && opt.eqMin) return min === curr; 22 | if (max && opt.eqMax) return max === curr; 23 | 24 | // Type converting from Number to Boolean. 25 | if (min) { 26 | if (opt.gEqMin) min = min <= curr; 27 | else min = min < curr; 28 | } else { 29 | min = true; 30 | } 31 | 32 | if (max) { 33 | if (opt.lEqMax) max = curr <= max; 34 | else max = curr < max; 35 | } else { 36 | max = true; 37 | } 38 | 39 | if (min && max) return true; 40 | return false; 41 | } 42 | 43 | function coerceNum(string) { 44 | if (typeof string !== "string") 45 | throw new Error(`Wrong term passed ${string}`); 46 | const array = Array.from(string.split(".", 3), (v) => Number.parseInt(v)); 47 | if (array.some((v) => Number.isNaN(v)) || array.length !== 3) 48 | throw new Error(`Invalid SemVer string: ${string}`); 49 | array[0] = array[0] * 1000000; 50 | array[1] = array[1] * 1000; 51 | return array.reduce((sum, val) => sum + val, 0); 52 | } 53 | -------------------------------------------------------------------------------- /src/item/weapon/weapon-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsItemSheet } from "@item/item-sheet"; 2 | export class ForbiddenLandsWeaponSheet extends ForbiddenLandsItemSheet { 3 | static get defaultOptions() { 4 | return foundry.utils.mergeObject(super.defaultOptions, { 5 | ...super.defaultOptions, 6 | template: 7 | "systems/forbidden-lands/templates/item/weapon/weapon-sheet.hbs", 8 | tabs: [ 9 | { 10 | navSelector: ".sheet-tabs", 11 | contentSelector: ".sheet-body", 12 | initial: "main", 13 | }, 14 | ], 15 | }); 16 | } 17 | 18 | async getData(options = {}) { 19 | const data = await super.getData(options); 20 | 21 | data.weightOptions = [ 22 | { value: "none", label: "WEIGHT.NONE" }, 23 | { value: "tiny", label: "WEIGHT.TINY" }, 24 | { value: "light", label: "WEIGHT.LIGHT" }, 25 | { value: "regular", label: "WEIGHT.REGULAR" }, 26 | { value: "heavy", label: "WEIGHT.HEAVY" }, 27 | ]; 28 | 29 | data.categoryOptions = [ 30 | { value: "melee", label: "WEAPON.MELEE" }, 31 | { value: "ranged", label: "WEAPON.RANGED" }, 32 | ]; 33 | 34 | data.gripOptions = [ 35 | { value: "1h", label: "WEAPON.1H" }, 36 | { value: "2h", label: "WEAPON.2H" }, 37 | ]; 38 | 39 | data.rangeOptions = [ 40 | { value: "arm", label: "RANGE.ARM" }, 41 | { value: "near", label: "RANGE.NEAR" }, 42 | { value: "short", label: "RANGE.SHORT" }, 43 | { value: "long", label: "RANGE.LONG" }, 44 | { value: "distant", label: "RANGE.DISTANT" }, 45 | ]; 46 | 47 | data.ammoOptions = [ 48 | { value: "other", label: "WEAPON.AMMO_OTHER" }, 49 | { value: "arrows", label: "WEAPON.AMMO_ARROWS" }, 50 | ]; 51 | 52 | return data; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /templates/actor/party/sheet-tabs/main-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 |
    8 |
    9 | {{localize "FLPS.PARTY.NAME"}} 10 | 11 |
    12 |
    13 | {{#if isGm}} 14 | 16 | {{/if}} 17 |
    18 |
    19 |
    20 |
    21 |

    {{localize "FLPS.PARTY.MEMBERS" }}

    22 |
    {{localize "FLPS.PARTY.HINT_ADD"}}
    23 |
      24 | {{#each partyMembers as | partyMember |}} 25 | {{> systems/forbidden-lands/templates/actor/party/components/member-component.hbs partyMember=partyMember deleteButton=true}} 26 | {{/each}} 27 |
    28 |
    29 | {{#if isGm}} 30 |
    31 |

    {{localize "FLPS.PARTY.RANDOM_ENCOUNTERS"}}

    32 |
      33 | {{#each encounterTables as | value |}} 34 |
    • 35 | 37 |
    • 38 | {{/each}} 39 |
    40 |
    41 | 43 |
    44 | {{/if}} 45 |
    46 |
    -------------------------------------------------------------------------------- /templates/item/building/building-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 |
    8 | 9 | 10 |
    11 |
    12 | 13 | 14 |
    15 |
    16 | 17 | 18 |
    19 |
    20 |
    21 |
    22 | 23 | 24 |
    25 |
    26 | 27 | 28 |
    29 |
    30 | 31 | 32 |
    33 |
    34 | 35 | {{editor system.effect target="system.effect" owner=owner button=true editable=true}} 36 |
    37 |
    38 | 39 | {{editor system.description target="system.description" owner=owner button=true editable=true}} 40 |
    41 |
    -------------------------------------------------------------------------------- /.vscode/i18n-ally-custom-framework.yml: -------------------------------------------------------------------------------- 1 | # Author: FloRad from SWADE game system 2 | # https://gitlab.com/peginc/swade 3 | 4 | # .vscode/i18n-ally-custom-framework.yml 5 | 6 | # An array of strings which contain Language Ids defined by VS Code 7 | # You can check avaliable language ids here: https://code.visualstudio.com/docs/languages/overview#_language-id 8 | languageIds: 9 | - javascript 10 | - typescript 11 | - handlebars 12 | 13 | # An array of RegExes to find the key usage. **The key should be captured in the first match group**. 14 | # You should unescape RegEx strings in order to fit in the YAML file 15 | # To help with this, you can use https://www.freeformatter.com/json-escape.html 16 | usageMatchRegex: 17 | # The following example shows how to detect `t("your.i18n.keys")` 18 | # the `{key}` will be placed by a proper keypath matching regex, 19 | # you can ignore it and use your own matching rules as well 20 | - "[^\\w\\d]game\\.i18n\\.localize\\(['\"`]({key})['\"`]\\)" 21 | - "[^\\w\\d]game\\.i18n\\.format\\(['\"`]({key})['\"`]" 22 | - "[^\\w\\d]localize\\(['\"`]({key})['\"`]" 23 | - "[^\\w\\d]ui\\.notifications\\.notify\\(['\"`]({key})['\"`],\\s*.*,\\s{\\s*.*\\slocalize: true\\s*.*}" 24 | - "[^\\w\\d]ui\\.notifications\\.warn\\(['\"`]({key})['\"`],\\s{\\s*.*\\slocalize: true\\s*.*}" 25 | - "[^\\w\\d]ui\\.notifications\\.error\\(['\"`]({key})['\"`],\\s{\\s*.*\\slocalize: true\\s*.*}" 26 | - "[^\\w\\d]ui\\.notifications\\.info\\(['\"`]({key})['\"`],\\s{\\s*.*\\slocalize: true\\s*.*}" 27 | - "\\{\\{\\s*localize\\s+[\"']({key})['\"]\\}\\}" 28 | - "\\{\\{[\\w\\.\\s\\=]*\\(localize\\s+[\"']({key})['\"]\\)[\\w\\.\\s\\=]*\\}\\}" 29 | - "name:\\s+[\"'](SETTINGS.{key})[\"']" 30 | - "hint:\\s+[\"'](SETTINGS.{key})[\"']" 31 | 32 | # If set to true, only enables this custom framework (will disable all built-in frameworks) 33 | monopoly: true 34 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/_character.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .character { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | width: 100%; 6 | } 7 | 8 | .forbidden-lands .character .bio label { 9 | font-family: var(--font-alternative); 10 | margin-left: 8px; 11 | } 12 | 13 | .forbidden-lands .character .bio table { 14 | width: auto; 15 | background: none; 16 | border: none; 17 | margin: 5px 0; 18 | } 19 | 20 | .forbidden-lands .character .bio table td { 21 | padding: 0.25em; 22 | text-align: right; 23 | 24 | input { 25 | max-width: 50px; 26 | 27 | &.kin, 28 | &.profession { 29 | text-align: left; 30 | max-width: 120px; 31 | } 32 | 33 | &.name { 34 | text-align: left; 35 | max-width: 225px; 36 | } 37 | } 38 | } 39 | 40 | .forbidden-lands .character .bio { 41 | .willpower.skulls { 42 | justify-content: flex-start; 43 | align-items: flex-end; 44 | white-space: nowrap; 45 | height: 1.75em; 46 | 47 | .change-willpower>i.skull { 48 | font-size: var(--font-size-16); 49 | } 50 | } 51 | } 52 | 53 | .forbidden-lands .character .bio .avatar .forbidden-lands .character .bio .avatar img .forbidden-lands .character .roll { 54 | background: transparent; 55 | border: 0 none; 56 | cursor: pointer; 57 | font-family: inherit; 58 | font-weight: bold; 59 | } 60 | 61 | .forbidden-lands .character .roll:hover { 62 | box-shadow: none; 63 | text-shadow: 0 0 8px var(--color-shadow-primary); 64 | } 65 | 66 | .forbidden-lands .character .push { 67 | background: transparent; 68 | border: 0 none; 69 | cursor: pointer; 70 | font-family: inherit; 71 | font-weight: bold; 72 | } 73 | 74 | .forbidden-lands .character .push:hover { 75 | box-shadow: none; 76 | text-shadow: 0 0 8px var(--color-theme-accent); 77 | } 78 | 79 | .forbidden-lands .monster h2, 80 | .forbidden-lands .character h2 { 81 | padding: 8px 4px 4px; 82 | } 83 | 84 | .forbidden-lands .roll-consumable { 85 | text-transform: uppercase; 86 | } -------------------------------------------------------------------------------- /templates/components/sheet-config-modal.hbs: -------------------------------------------------------------------------------- 1 |
    2 | {{#if (eq document.type "character")}} 3 |
    4 | Character Configuration 5 |
    6 | 7 |
    8 | 11 |
    12 |

    {{localize "CONFIG.SHEET.CHARACTER_SUBTYPE_DESC"}}

    13 |
    14 |
    15 | 16 |
    17 | 18 |
    19 |

    {{localize "FLPS.SETTINGS.ALLOW_PUSH_HINT"}}

    20 |
    21 |
    22 | {{/if}} 23 | 24 |
    25 | Document 26 |
    27 | 28 |
    29 | 33 |
    34 |

    {{localize "SHEETS.DocumentSheetHint"}}

    35 |
    36 |
    37 | 38 |
    39 | Defaults 40 |
    41 | 42 |
    43 | 47 |
    48 |

    {{localize "SHEETS.TypeSheetHint"}}

    49 |
    50 |
    51 |
    52 | -------------------------------------------------------------------------------- /templates/item/_shared-template-tabs/artifact-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 | {{#unless (eq type "gear")}} 3 | {{#if showEffectField}} 4 |
    5 | 6 | {{#if isGM}} 7 | 8 | 9 | 10 | {{/if}} 11 | {{editor system.effect target="system.effect" owner=owner button=true editable=true}} 12 |
    13 | {{/if}} 14 | {{/unless}} 15 | {{#if showDescriptionField}} 16 |
    17 | 18 | {{#if isGM}} 19 | 20 | 21 | 22 | {{/if}} 23 | {{editor system.description target="system.description" owner=owner button=true editable=true}} 24 |
    25 | {{/if}} 26 | {{#if showDrawbackField}} 27 |
    28 | 29 | {{#if isGM}} 30 | 31 | 32 | 33 | {{/if}} 34 | {{editor system.drawback target="system.drawback" owner=owner button=true editable=true}} 35 |
    36 | {{/if}} 37 | {{#if showAppearanceField}} 38 |
    39 | 40 | {{#if isGM}} 41 | 42 | 43 | 44 | {{/if}} 45 | {{editor system.appearance target="system.appearance" owner=owner button=true editable=true}} 46 |
    47 | {{/if}} 48 |
    49 | -------------------------------------------------------------------------------- /templates/item/monster-attack/monster-attack-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 | 8 | 9 | 10 |
    11 | {{#unless system.usingStrength}} 12 | 13 | 20 | {{else}} 21 | 32 | {{/unless}} 33 | 34 |
    35 | 36 | 39 | 40 | 41 | 42 | 45 |
    46 |
    47 |
    48 |
    49 | 50 | {{editor system.description target="system.description" owner=owner button=true editable=true}} 51 |
    52 |
    53 |
    54 | -------------------------------------------------------------------------------- /devbox.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfile_version": "1", 3 | "packages": { 4 | "bun@latest": { 5 | "last_modified": "2024-07-17T09:45:27Z", 6 | "resolved": "github:NixOS/nixpkgs/5e73714b16ca222dcb2fc3ea2618fd7ba698da65#bun", 7 | "source": "devbox-search", 8 | "version": "1.1.20", 9 | "systems": { 10 | "aarch64-darwin": { 11 | "outputs": [ 12 | { 13 | "name": "out", 14 | "path": "/nix/store/sjhdi5ixjsqyldxfmdzcazk1r3asa7c5-bun-1.1.20", 15 | "default": true 16 | } 17 | ], 18 | "store_path": "/nix/store/sjhdi5ixjsqyldxfmdzcazk1r3asa7c5-bun-1.1.20" 19 | }, 20 | "aarch64-linux": { 21 | "outputs": [ 22 | { 23 | "name": "out", 24 | "path": "/nix/store/1cg8hfz38pxcxqvk3ms1v9hr52bh9yrm-bun-1.1.20", 25 | "default": true 26 | } 27 | ], 28 | "store_path": "/nix/store/1cg8hfz38pxcxqvk3ms1v9hr52bh9yrm-bun-1.1.20" 29 | }, 30 | "x86_64-darwin": { 31 | "outputs": [ 32 | { 33 | "name": "out", 34 | "path": "/nix/store/2mnl4ry24y9q261caia8yn46h47ckpz3-bun-1.1.20", 35 | "default": true 36 | } 37 | ], 38 | "store_path": "/nix/store/2mnl4ry24y9q261caia8yn46h47ckpz3-bun-1.1.20" 39 | }, 40 | "x86_64-linux": { 41 | "outputs": [ 42 | { 43 | "name": "out", 44 | "path": "/nix/store/6a8kqmk0jda5a3kyka1c55dh9h5ry5ii-bun-1.1.20", 45 | "default": true 46 | } 47 | ], 48 | "store_path": "/nix/store/6a8kqmk0jda5a3kyka1c55dh9h5ry5ii-bun-1.1.20" 49 | } 50 | } 51 | }, 52 | "github:NixOS/nixpkgs/nixpkgs-unstable": { 53 | "last_modified": "2025-05-28T04:23:31Z", 54 | "resolved": "github:NixOS/nixpkgs/3d1f29646e4b57ed468d60f9d286cde23a8d1707?lastModified=1748406211&narHash=sha256-B3BsCRbc%2Bx%2Fd0WiG1f%2BqfSLUy%2BoiIfih54kalWBi%2B%2FM%3D" 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/actor/party/components/character-picker-dialog.js: -------------------------------------------------------------------------------- 1 | const noop = () => {}; 2 | 3 | export class CharacterPickerDialog extends Application { 4 | constructor(actors, title, callback, onCancel = noop) { 5 | super(); 6 | this.actors = actors; 7 | this.titleText = title; 8 | this.callback = callback; 9 | this.onCancel = onCancel; 10 | } 11 | 12 | /** 13 | * Launch the character picker dialog. 14 | */ 15 | static async show(title, characters, onSelect = noop, onCancel = noop) { 16 | const actors = characters.map((c) => 17 | c instanceof Actor ? c : game.actors.get(c), 18 | ); 19 | const dialog = new CharacterPickerDialog(actors, title, onSelect, onCancel); 20 | dialog.render(true); 21 | } 22 | 23 | /** 24 | * Foundry VTT options for this Application. 25 | */ 26 | static get defaultOptions() { 27 | return mergeObject(super.defaultOptions, { 28 | id: "character-picker-dialog", 29 | classes: ["forbidden-lands", "dialog"], 30 | template: 31 | "systems/forbidden-lands/templates/actor/party/components/character-picker-dialog.hbs", 32 | width: 400, 33 | height: "auto", 34 | resizable: false, 35 | }); 36 | } 37 | 38 | /** 39 | * Sets the dialog title. 40 | */ 41 | get title() { 42 | return this.titleText; 43 | } 44 | 45 | /** 46 | * Supplies data to the template. 47 | */ 48 | getData() { 49 | return { 50 | title: this.titleText, 51 | actors: this.actors, 52 | }; 53 | } 54 | 55 | /** 56 | * Handles user interaction with actor selections. 57 | */ 58 | activateListeners(html) { 59 | super.activateListeners(html); 60 | for (const el of html[0].querySelectorAll(".select-actor")) { 61 | el.addEventListener("click", async (event) => { 62 | event.preventDefault(); 63 | event.stopPropagation(); 64 | const uuid = el.dataset.uuid; 65 | const actor = await fromUuid(uuid); 66 | this.callback(actor); 67 | this.close(); 68 | }); 69 | } 70 | } 71 | 72 | /** 73 | * When closed without selection. 74 | */ 75 | close(options) { 76 | this.onCancel?.(); 77 | return super.close(options); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/legacy-styles/sheets/tab/_main.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .main-tab { 2 | display: grid; 3 | gap: 8px; 4 | grid-template-columns: 1fr 1fr; 5 | height: 100%; 6 | overflow-y: auto; 7 | font-size: var(--font-size-16); 8 | } 9 | 10 | .forbidden-lands .attributes { 11 | align-items: center; 12 | display: grid; 13 | gap: 4px 8px; 14 | grid-template-columns: auto 1fr auto; 15 | padding: 0 8px 8px; 16 | } 17 | 18 | .forbidden-lands .attributes h2 { 19 | grid-column: 1/-1; 20 | } 21 | 22 | .forbidden-lands .attributes .name { 23 | font-family: var(--font-alternative); 24 | text-align: left; 25 | } 26 | 27 | .forbidden-lands .attributes .skulls { 28 | text-align: left; 29 | } 30 | 31 | .forbidden-lands .attributes .value { 32 | font-family: var(--font-alternative); 33 | margin-right: 5px; 34 | } 35 | 36 | .forbidden-lands .attributes .value input { 37 | width: 2em; 38 | } 39 | 40 | .forbidden-lands .attributes .broken { 41 | color: var(--color-theme-accent); 42 | } 43 | 44 | .forbidden-lands .character .conditions-grid { 45 | display: grid; 46 | height: 100%; 47 | grid-template-columns: repeat(2, 1fr); 48 | gap: 40% 20%; 49 | place-content: center; 50 | padding: 5% 10%; 51 | } 52 | 53 | .forbidden-lands .conditions-grid a.condition { 54 | font-family: var(--font-alternative); 55 | font-size: var(--font-size-16); 56 | font-weight: bold; 57 | display: inline-grid; 58 | grid: inherit; 59 | gap: 4px; 60 | place-content: baseline; 61 | } 62 | 63 | .forbidden-lands .character .condition i { 64 | font-size: var(--font-size-18); 65 | justify-self: end; 66 | } 67 | 68 | .forbidden-lands .conditions .condition.active { 69 | color: var(--color-theme-accent); 70 | } 71 | 72 | .forbidden-lands .skills { 73 | align-items: baseline; 74 | display: grid; 75 | flex-basis: calc(50% - 4px); 76 | flex-grow: 1; 77 | gap: 4px 8px; 78 | grid-template-columns: 1fr auto; 79 | padding: 0 8px 8px; 80 | } 81 | 82 | .forbidden-lands .skills h2 { 83 | grid-column: 1/-1; 84 | } 85 | 86 | .forbidden-lands .skills .name { 87 | font-family: var(--font-alternative); 88 | flex-basis: 50%; 89 | } 90 | 91 | .forbidden-lands .skills input { 92 | width: 2em; 93 | margin-right: 2px; 94 | } -------------------------------------------------------------------------------- /src/global.ts: -------------------------------------------------------------------------------- 1 | import type EnJSON from "../lang/en.json"; 2 | import type CONFIG_FBL from "$system/core/config"; 3 | import type { FBLRollHandler } from "$components/roll-engine/engine"; 4 | 5 | interface GameFBL 6 | extends Game< 7 | Actor, 8 | Actors>, 9 | ChatMessage, 10 | Combat, 11 | Item, 12 | Macro, 13 | Scene, 14 | User 15 | > { 16 | fbl: { 17 | config: typeof CONFIG_FBL; 18 | roll: typeof FBLRollHandler.createRoll; 19 | }; 20 | } 21 | 22 | type ConfiguredConfig = Config< 23 | AmbientLightDocument, 24 | ActiveEffect, 25 | Actor, 26 | ActorDelta, 27 | ChatLog, 28 | ChatMessage, 29 | Combat, 30 | Combatant, 31 | CombatTracker, 32 | CompendiumDirectory, 33 | Hotbar, 34 | Item, 35 | Macro, 36 | MeasuredTemplateDocument, 37 | TileDocument, 38 | TokenDocument, 39 | WallDocument, 40 | Scene, 41 | User, 42 | EffectsCanvasGroup 43 | >; 44 | 45 | declare global { 46 | interface ConfigFBL extends ConfiguredConfig { 47 | debug: ConfiguredConfig["debug"] & { 48 | ruleElement: boolean; 49 | }; 50 | fbl: typeof CONFIG_FBL; 51 | tests: typeof import("./tests/foundry-scripts").default; 52 | time: { 53 | roundTime: number; 54 | }; 55 | } 56 | 57 | const CONFIG: ConfigFBL; 58 | 59 | namespace globalThis { 60 | var game: GameFBL; 61 | 62 | var ui: FoundryUI< 63 | ActorDirectory>, 64 | ItemDirectory>, 65 | ChatLog, 66 | CompendiumDirectory, 67 | CombatTracker 68 | >; 69 | 70 | interface Math { 71 | eq: (a: number, b: number) => boolean; 72 | gt: (a: number, b: number) => boolean; 73 | gte: (a: number, b: number) => boolean; 74 | lt: (a: number, b: number) => boolean; 75 | lte: (a: number, b: number) => boolean; 76 | ne: (a: number, b: number) => boolean; 77 | ternary: ( 78 | condition: boolean | number, 79 | ifTrue: number, 80 | ifFalse: number, 81 | ) => number; 82 | } 83 | } 84 | 85 | //interface ClientSettings {} 86 | 87 | //interface ClientSettingsMap {} 88 | 89 | const EN_JSON: typeof EnJSON; 90 | } 91 | -------------------------------------------------------------------------------- /templates/actor/stronghold/stronghold-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 | 7 |
    8 |
    9 |
    10 | 11 | 12 |
    13 |
    14 |
    15 | 16 | 17 |
    18 |
    19 | 20 | 26 |
    27 |
    28 |
    29 |
    30 |
    31 | 32 |
    33 | {{localize "TAB.BUILDING"}} 34 | {{localize "TAB.HIRELING"}} 35 | {{localize "TAB.GEAR"}} 36 | {{localize "TAB.NOTE"}} 37 |
    38 | 39 |
    40 |
    41 | {{> systems/forbidden-lands/templates/actor/stronghold/sheet-tabs/building-tab.hbs}} 42 |
    43 |
    44 | {{> systems/forbidden-lands/templates/actor/stronghold/sheet-tabs/hireling-tab.hbs}} 45 |
    46 |
    47 | {{> systems/forbidden-lands/templates/actor/stronghold/sheet-tabs/gear-tab.hbs}} 48 |
    49 |
    50 | {{editor system.description target="system.description" button=true owner=owner editable=true}} 51 |
    52 |
    53 |
    54 |
    55 | -------------------------------------------------------------------------------- /src/actor/stronghold/stronghold-sheet.js: -------------------------------------------------------------------------------- 1 | import { ForbiddenLandsActorSheet } from "../actor-sheet.js"; 2 | export class ForbiddenLandsStrongholdSheet extends ForbiddenLandsActorSheet { 3 | static get defaultOptions() { 4 | return foundry.utils.mergeObject(super.defaultOptions, { 5 | ...super.defaultOptions, 6 | classes: ["forbidden-lands", "sheet", "actor"], 7 | template: 8 | "systems/forbidden-lands/templates/actor/stronghold/stronghold-sheet.hbs", 9 | width: 650, 10 | height: 700, 11 | resizable: true, 12 | scrollY: [ 13 | ".buildings.item-list .items", 14 | ".hirelings.item-list .items", 15 | ".gears.item-listing .items", 16 | ], 17 | tabs: [ 18 | { 19 | navSelector: ".sheet-tabs", 20 | contentSelector: ".sheet-body", 21 | initial: "building", 22 | }, 23 | ], 24 | }); 25 | } 26 | 27 | async getData() { 28 | const actorData = await super.getData(); 29 | actorData.system.description = 30 | await foundry.applications.ux.TextEditor.implementation.enrichHTML( 31 | actorData.system.description, 32 | { async: true }, 33 | ); 34 | this._computeItems(actorData); 35 | return actorData; 36 | } 37 | 38 | _computeItems(data) { 39 | for (const item of Object.values(data.items)) { 40 | item.isWeapon = item.type === "weapon"; 41 | item.isArmor = item.type === "armor"; 42 | item.isGear = item.type === "gear"; 43 | item.isRawMaterial = item.type === "rawMaterial"; 44 | item.isBuilding = item.type === "building"; 45 | item.isHireling = item.type === "hireling"; 46 | if (item.type !== "building" || item.type !== "hireling") { 47 | item.totalWeight = 48 | (CONFIG.fbl.encumbrance[item.system.weight] ?? 49 | item.system.weight ?? 50 | 1) * (item.system.quantity ?? 1); 51 | } 52 | } 53 | } 54 | 55 | activateListeners(html) { 56 | super.activateListeners(html); 57 | 58 | const details = html.find("details"); 59 | details.on("click", (e) => { 60 | const detail = $(e.target).closest("details"); 61 | const content = detail.find("summary ~ *"); 62 | if (detail.attr("open")) { 63 | e.preventDefault(); 64 | content.slideUp(200); 65 | setTimeout(() => { 66 | detail.removeAttr("open"); 67 | }, 200); 68 | } else content.slideDown(200); 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "✨ New feature request" 2 | description: Suggest an idea for this project 3 | labels: ["feature"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for your interest in the project and taking the time to fill out this feature request! 9 | > **Warning** Features related to the official content modules should be [submitted here instead](https://github.com/aMediocreDad/fbl-content-modules/issues) 10 | - type: textarea 11 | id: feature-description 12 | attributes: 13 | label: Description 14 | description: "Clear and concise description of the request. Please make the reason and usecases as detailed as possible. If you intend to submit a PR for this issue, tell us in the description. Thanks!" 15 | placeholder: When using the Forbidden Lands system I would like to see [whish]. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: suggested-solution 20 | attributes: 21 | label: Suggested solution 22 | description: "The solution could look something like this..." 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: alternative 27 | attributes: 28 | label: Alternative 29 | description: Clear and concise description of any alternative solutions or features you've considered. 30 | - type: textarea 31 | id: additional-context 32 | attributes: 33 | label: Additional context 34 | description: Any other context or screenshots about the feature request here. 35 | - type: checkboxes 36 | id: checkboxes 37 | attributes: 38 | label: Validations 39 | description: Before submitting the issue, please make sure you do the following 40 | options: 41 | - label: "Not a feature request for the official content modules. Issues regarding the content modules go [here instead](https://github.com/aMediocreDad/fbl-content-modules/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)." 42 | required: true 43 | - label: 'Not a "How To"-question (You can ask questions here: [Discussions](https://github.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/discussions)).' 44 | required: true 45 | - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. 46 | required: true 47 | -------------------------------------------------------------------------------- /src/utils/sheet-config.js: -------------------------------------------------------------------------------- 1 | const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; 2 | 3 | export class ActorSheetConfig extends HandlebarsApplicationMixin( 4 | ApplicationV2, 5 | ) { 6 | constructor(options = {}) { 7 | super(options); 8 | this.document = options.document; 9 | 10 | // Set dynamic title based on actor name 11 | const actorName = this.document.name; 12 | const configTitle = 13 | game.i18n.localize("CONFIG.ACTOR_SHEET_CONFIG.TITLE") || 14 | "Sheet Configuration"; 15 | this.options.window.title = `${actorName}: ${configTitle}`; 16 | } 17 | 18 | static DEFAULT_OPTIONS = { 19 | tag: "form", 20 | classes: ["application", "sheet", "sheet-config"], 21 | form: { 22 | handler: ActorSheetConfig.myFormHandler, 23 | submitOnChange: false, 24 | closeOnSubmit: true, 25 | }, 26 | window: { 27 | icon: "fa-solid fa-gear", 28 | contentClasses: ["standard-form"], 29 | }, 30 | position: { 31 | width: 500, 32 | height: "auto", 33 | }, 34 | }; 35 | 36 | static PARTS = { 37 | body: { 38 | template: 39 | "systems/forbidden-lands/templates/components/sheet-config-modal.hbs", 40 | }, 41 | footer: { 42 | template: "templates/generic/form-footer.hbs", 43 | }, 44 | }; 45 | 46 | async _prepareContext(options) { 47 | const documentName = this.document.documentName; 48 | const allSheetClasses = CONFIG[documentName]?.sheetClasses || {}; 49 | 50 | const actorType = this.document.type || "character"; 51 | 52 | const relevantSheetClasses = allSheetClasses[actorType] || {}; 53 | 54 | console.log("actorType:", actorType); 55 | console.log("relevantSheetClasses:", relevantSheetClasses); 56 | 57 | return { 58 | document: this.document, 59 | types: CONFIG.fbl.characterSubtype, 60 | sheetClasses: relevantSheetClasses, 61 | sheetClass: this.document._sheetClass, 62 | defaultClass: CONFIG[documentName]?.sheetClass, 63 | isGM: game.user.isGM, 64 | blankLabel: game.i18n.localize("None"), 65 | type: documentName, 66 | editable: true, 67 | buttons: [ 68 | { 69 | type: "submit", 70 | icon: "fa-solid fa-save", 71 | label: "SHEETS.Save", 72 | }, 73 | ], 74 | }; 75 | } 76 | 77 | static async myFormHandler(event, form, formData) { 78 | const updateData = foundry.utils.expandObject(formData.object); 79 | await this.document.update(updateData); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/components/message-system.js: -------------------------------------------------------------------------------- 1 | import semverComp from "../utils/semver-compare"; 2 | 3 | export default async function displayMessages() { 4 | const { messages } = await fetch( 5 | "systems/forbidden-lands/assets/messages/messages.jsonc", 6 | ) 7 | .then((resp) => resp.text()) 8 | .then((jsonc) => JSON.parse(stripJSON(jsonc))); 9 | 10 | for (const message of messages) { 11 | handleDisplay(message); 12 | } 13 | } 14 | 15 | const stripJSON = (data) => { 16 | return data.replace(/[^:]\/\/(.*)/g, ""); 17 | }; 18 | 19 | const handleDisplay = (msg) => { 20 | const { content, title, type } = msg; 21 | if (!isCurrent(msg)) return; 22 | if (type === "prompt") return displayPrompt(title, content); 23 | if (type === "chat") return sendToChat(title, content); 24 | }; 25 | 26 | const isCurrent = (msg) => { 27 | const isDisplayable = !msg.display === "once" || !hasDisplayed(msg.title); 28 | const correctCoreVersion = 29 | foundry.utils.isNewerVersion( 30 | msg["max-core-version"] ?? "100.0.0", 31 | game.version, 32 | ) && 33 | foundry.utils.isNewerVersion( 34 | game.version, 35 | msg["min-core-version"] ?? "0.0.0", 36 | ); 37 | const correctSysVersion = semverComp( 38 | msg["min-sys-version"] ?? "0.0.0", 39 | game.system.version, 40 | msg["max-sys-version"] ?? "100.0.0", 41 | { gEqMin: true }, 42 | ); 43 | return isDisplayable && correctCoreVersion && correctSysVersion; 44 | }; 45 | 46 | const hasDisplayed = (identifier) => { 47 | const settings = game.settings.get("forbidden-lands", "messages"); 48 | if (settings?.includes(identifier)) return true; 49 | return false; 50 | }; 51 | 52 | const displayPrompt = (title, content) => { 53 | content = content.replace("{name}", game.user.name); 54 | return Dialog.prompt({ 55 | title: title, 56 | content: content, 57 | label: "Understood!", 58 | options: { width: 450 }, 59 | callback: () => setDisplayed(title), 60 | }); 61 | }; 62 | 63 | const sendToChat = (title, content) => { 64 | content = content.replace("{name}", game.user.name); 65 | setDisplayed(title); 66 | return ChatMessage.create({ 67 | title: title, 68 | content: `
    ${content}
    `, 69 | }); 70 | }; 71 | 72 | const setDisplayed = async (identifier) => { 73 | const settings = game.settings.get("forbidden-lands", "messages"); 74 | settings.push(identifier); 75 | await game.settings.set("forbidden-lands", "messages", settings.flat()); 76 | }; 77 | -------------------------------------------------------------------------------- /src/legacy-styles/components/_chat.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands.chat-item { 2 | background: var(--color-theme-background); 3 | border-radius: 3px; 4 | color: var(--color-theme-text); 5 | font-family: var(--font-body); 6 | font-size: var(--font-size-12); 7 | line-height: 16px; 8 | padding: 8px; 9 | } 10 | 11 | .chat-message .message-header .message-metadata { 12 | font-size: var(--font-size-12); 13 | } 14 | 15 | .forbidden-lands.chat-item .border { 16 | padding: 8px; 17 | } 18 | 19 | .forbidden-lands.chat-item .link-header { 20 | text-align: center; 21 | } 22 | 23 | .forbidden-lands.chat-item strong { 24 | text-transform: uppercase; 25 | } 26 | 27 | .forbidden-lands.chat-item h3 { 28 | font-family: var(--font-header-sc); 29 | font-size: var(--font-size-14); 30 | font-weight: bold; 31 | margin: 0 0 8px; 32 | text-align: center; 33 | } 34 | 35 | .forbidden-lands.chat-item h4 { 36 | font-family: var(--font-alternative); 37 | font-size: var(--font-size-14); 38 | font-weight: bold; 39 | margin: 0 0 8px; 40 | text-align: center; 41 | } 42 | 43 | .forbidden-lands.chat-item h3+h4 { 44 | margin-top: -8px; 45 | } 46 | 47 | .forbidden-lands.chat-item img { 48 | background: radial-gradient(circle, rgba(0, 0, 0, 0.5) -20%, rgba(0, 0, 0, 0.1) 40%, transparent 60%); 49 | border: 0; 50 | display: block; 51 | margin: 0 auto 8px; 52 | max-height: 64px; 53 | width: auto; 54 | } 55 | 56 | .forbidden-lands.chat-item p { 57 | margin: 0 0 4px; 58 | } 59 | 60 | .forbidden-lands { 61 | &.chat-item { 62 | .link-header { 63 | a { 64 | line-height: 18px; 65 | } 66 | } 67 | 68 | h3 { 69 | font-family: "IM Fell DW Pica SC"; 70 | } 71 | 72 | h4 { 73 | font-family: "IM Fell DW Pica"; 74 | } 75 | } 76 | 77 | &.item { 78 | .header { 79 | border: 10px solid transparent; 80 | border-image: url(./assets/journal-art/small-border.webp) 100/10px repeat; 81 | 82 | .header-stats { 83 | .bonus { 84 | grid-gap: 5px; 85 | 86 | i { 87 | vertical-align: unset; 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | .border { 95 | border: none; 96 | } 97 | } 98 | 99 | .forbidden-lands.chat-item { 100 | background: none; 101 | } 102 | 103 | .forbidden-lands.chat-item, 104 | .forbidden-lands.chat-item .border { 105 | padding: 0 4px; 106 | } 107 | 108 | .message-content { 109 | .entity-link { 110 | border: none; 111 | } 112 | } -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | release: 10 | name: Create & Publish Release 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Package 16 | run: | 17 | zip -r forbidden-lands.zip assets fonts lang templates forbidden-lands.js forbidden-lands.css system.json template.json CHANGELOG.md CONTRIBUTING.md LICENSE README.md 18 | - name: Update Release with Artifacts 19 | uses: ncipollo/release-action@v1.13.0 20 | with: 21 | allowUpdates: true 22 | name: ${{ github.event.release.name }} 23 | tag: ${{ github.event.release.tag_name }} 24 | body: ${{ github.event.release.body }} 25 | artifacts: './forbidden-lands.zip' 26 | token: ${{ secrets.GITHUB_TOKEN }} 27 | foundry: 28 | name: Push to Foundry 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Get Version Number 32 | id: tag 33 | run: | 34 | TAG_NAME=${{ github.event.release.tag_name }} 35 | echo "::set-output name=version::${TAG_NAME//v/}" 36 | - name: Push to Foundry 37 | id: foundry 38 | uses: fjogeleit/http-request-action@v1 39 | with: 40 | url: 'https://foundryvtt.com/_api/packages/release_version/' 41 | customHeaders: '{ "Content-Type": "application/json", "Authorization": "${{ secrets.FOUNDRY_TOKEN }}" }' 42 | data: 43 | '{ 44 | "id": "forbidden-lands", 45 | "release": { 46 | "version": "${{ steps.tag.outputs.version }}", 47 | "manifest": "https://raw.githubusercontent.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/${{ github.event.release.tag_name }}/system.json", 48 | "notes": "https://github.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/releases/tag/${{ github.event.release.tag_name }}", 49 | "compatibility": { 50 | "minimum": "13", 51 | "verified": "13.348", 52 | "maximum": "13" 53 | } 54 | } 55 | }' 56 | - name: Summary 57 | run: | 58 | echo "**Version:** ${{ steps.tag.outputs.version }}" >> $GITHUB_STEP_SUMMARY 59 | echo "**Release Notes:** ${{ github.event.release.html_url }}" >> $GITHUB_STEP_SUMMARY 60 | echo "**Published:** ${{ steps.foundry.outputs.response }}" >> $GITHUB_STEP_SUMMARY 61 | -------------------------------------------------------------------------------- /templates/components/roll-engine/roll.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | {{#if flavor}}{{flavor}}{{else}}{{formula}}{{/if}} 5 | {{#if roll.pushed}} – {{localize "PUSHED"}}{{/if}} 6 |

    7 |
    8 | 9 | 14 | 15 | {{#if (eq roll.type "yz")}} 16 |

    17 | x 18 | {{success}} 19 | {{#if (and roll.options.isAttack (gt success 0))}} 20 |  |  21 | 22 | {{roll.damage}} 23 | {{/if}} 24 | {{#if (gt roll.baneCount 0)}} 25 |  |  26 | l 27 | {{roll.baneCount}} 28 | {{/if}} 29 |

    30 | {{/if}} 31 | 32 | {{#if (eq roll.type "consumable")}} 33 |

    34 | 35 | {{total}} 36 |

    37 | {{/if}} 38 | 39 | {{#if (eq roll.type "spell")}} 40 |

    41 |
    {{localize "POWER_LEVEL"}}:
    42 |
    {{roll.damage}}
    43 |

    44 | {{/if}} 45 | 46 | {{#if showInfos}} 47 | {{{infos}}} 48 | {{/if}} 49 | 50 |
    51 | {{#if (and pushable (or roll.isOwner user.isGM))}} 52 | 55 | {{/if}} 56 | 57 | {{#if roll.isMishap}} 58 | {{#if roll.mishapTable}} 59 | 62 | {{else}} 63 |

    Roll Mishap!

    64 | {{/if}} 65 | {{/if}} 66 | 67 | {{#if (and success (eq roll.mishapType "travel-find-prey"))}} 68 | 71 | {{/if}} 72 |
    73 |
    74 | -------------------------------------------------------------------------------- /src/journal/adventure-sites/styles/_adventure-sites.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Journal Sheet Styles 3 | */ 4 | .app .journal-sheet-container { 5 | 6 | // Form content 7 | .journal-entry-page.text .adventure-site { 8 | &>* { 9 | margin-inline: auto; 10 | } 11 | 12 | h3 { 13 | margin-top: 1rem; 14 | } 15 | 16 | ul, 17 | ol { 18 | margin-inline: 0; 19 | } 20 | 21 | hr { 22 | width: 100%; 23 | } 24 | 25 | em { 26 | font-family: Branding, Ubuntu, sans-serif; 27 | font-size: var(--font-size-16); 28 | color: #111c; 29 | } 30 | 31 | strong { 32 | font-family: Branding, Ubuntu, sans-serif; 33 | font-weight: 600; 34 | } 35 | 36 | p+p { 37 | text-indent: 0; 38 | } 39 | 40 | .description { 41 | display: flex; 42 | flex-direction: column; 43 | gap: 0.5rem; 44 | 45 | strong { 46 | margin-right: 0.5ch; 47 | } 48 | } 49 | 50 | .room { 51 | margin-block: 1rem; 52 | width: 100%; 53 | border: 1px dotted #1116; 54 | padding: 1rem; 55 | border-radius: 0.5rem; 56 | position: relative; 57 | 58 | &:hover { 59 | background-color: #f0f0f0; 60 | } 61 | 62 | &>.index-tip { 63 | font-family: Branding, Ubuntu, sans-serif; 64 | position: absolute; 65 | top: 0; 66 | right: 0; 67 | block-size: 2.2rem; 68 | aspect-ratio: 1/1; 69 | border-radius: 0 0.4rem 0 0.5rem; 70 | background-color: #1119; 71 | color: #f0f0f0; 72 | display: grid; 73 | place-content: center; 74 | pointer-events: none; 75 | } 76 | } 77 | } 78 | 79 | .editable-tools { 80 | position: absolute; 81 | left: 50%; 82 | transform: translateX(-50%); 83 | bottom: 10px; 84 | width: 90%; 85 | display: grid; 86 | grid-template-columns: repeat(2, auto); 87 | place-content: center; 88 | gap: 4rem; 89 | 90 | // Buttons 91 | &>* { 92 | display: block; 93 | margin: 0; 94 | min-width: 14rem; 95 | width: auto; 96 | background: #fff; 97 | font-weight: 600; 98 | border: 1px solid transparent; 99 | border-image: url(/systems/forbidden-lands/assets/journal-art/box-border-large.webp) 28/7 repeat; 100 | border-image-outset: 6; 101 | background-color: #fff; 102 | opacity: 0.5; 103 | transition: opacity 0.3s cubic-bezier(0.39, 0.58, 0.57, 1); 104 | } 105 | 106 | &:hover { 107 | &>* { 108 | opacity: 1; 109 | 110 | &:hover { 111 | box-shadow: none; 112 | filter: drop-shadow(0 0 2px #111); 113 | } 114 | } 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /src/legacy-styles/sheets/tab/_monster-combat.scss: -------------------------------------------------------------------------------- 1 | .forbidden-lands .monster { 2 | .armors { 3 | .armor { 4 | align-items: center; 5 | padding: 0 5px 5px 5px; 6 | 7 | &.total input { 8 | flex-basis: 50%; 9 | margin-right: 5px; 10 | margin-left: 5px; 11 | text-align: center; 12 | } 13 | 14 | b { 15 | flex-basis: 50%; 16 | } 17 | 18 | input { 19 | flex-basis: 25%; 20 | margin-right: 5px; 21 | text-align: center; 22 | 23 | &:last-child { 24 | margin-right: 0; 25 | margin-left: 5px; 26 | } 27 | } 28 | } 29 | } 30 | 31 | .monster-talents { 32 | overflow: hidden; 33 | padding-bottom: 8px; 34 | 35 | .monster-talent { 36 | align-items: center; 37 | 38 | .name { 39 | align-items: center; 40 | display: flex; 41 | flex-basis: 90%; 42 | 43 | b { 44 | font-size: var(--font-size-14); 45 | font-weight: bold; 46 | text-transform: uppercase; 47 | } 48 | .name { 49 | display: block; 50 | padding-left: 4px; 51 | } 52 | } 53 | 54 | .button { 55 | flex-basis: 10%; 56 | font-size: var(--font-size-12); 57 | text-align: right; 58 | } 59 | } 60 | } 61 | 62 | .monster-attacks { 63 | margin-top: 8px; 64 | overflow: hidden; 65 | 66 | ol { 67 | list-style: none; 68 | counter-reset: item; 69 | margin: 0; 70 | padding: 0; 71 | 72 | li { 73 | counter-increment: item; 74 | 75 | &:before { 76 | margin-inline: 8px; 77 | content: counter(item); 78 | font-weight: 600; 79 | font-size: var(--font-size-12); 80 | text-align: center; 81 | display: inline-block; 82 | } 83 | } 84 | } 85 | 86 | .monster-attack { 87 | align-items: center; 88 | 89 | button { 90 | margin: -2px -4px; 91 | } 92 | 93 | .name { 94 | align-items: center; 95 | display: flex; 96 | flex-basis: 90%; 97 | 98 | a { 99 | font-size: small; 100 | font-weight: bold; 101 | text-transform: uppercase; 102 | } 103 | .name { 104 | padding-left: 4px; 105 | font-size: var(--font-size-14); 106 | } 107 | } 108 | 109 | .button { 110 | flex-basis: 10%; 111 | font-size: var(--font-size-12); 112 | text-align: right; 113 | } 114 | } 115 | } 116 | 117 | .talent-description, 118 | .monster-attack .name .name { 119 | display: -webkit-box; 120 | -webkit-line-clamp: 2; 121 | -webkit-box-orient: vertical; 122 | overflow: hidden; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /templates/components/roll-engine/dialog.hbs: -------------------------------------------------------------------------------- 1 |
    2 |

    {{title}}

    3 |
    4 |
    5 |

    {{localize "MONSTER.DICE"}}

    6 | {{#each dice as |die key|}} 7 | 8 |
    9 | 12 | 17 |
    18 | {{/each}} 19 | 26 |
    27 |
    28 |

    {{localize "MODIFIERS"}}

    29 |
    30 | {{localize "ACTIVE"}}{{localize "Name"}}{{localize "VALUE"}} 31 |
    32 | {{#each options.modifiers as |option|}} 33 |
    34 | 46 | 47 | 50 |
    51 | {{/each}} 52 |
    53 | 59 |
    60 |
    61 |
    62 |
    63 | 68 |
    69 |
    70 | -------------------------------------------------------------------------------- /assets/datasets/macros/macros.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "ACTION.UNARMED_STRIKE", 4 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"unarmed\")", 5 | "img": "icons/svg/combat.svg", 6 | "type": "script" 7 | }, 8 | { 9 | "name": "ACTION.DODGE", 10 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"dodge\")", 11 | "img": "icons/svg/combat.svg", 12 | "type": "script" 13 | }, 14 | { 15 | "name": "ACTION.PARRY", 16 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"parry\")", 17 | "img": "icons/svg/combat.svg", 18 | "type": "script" 19 | }, 20 | { 21 | "name": "ACTION.RETREAT", 22 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"retreat\")", 23 | "img": "icons/svg/combat.svg", 24 | "type": "script" 25 | }, 26 | { 27 | "name": "ACTION.BREAK_FREE", 28 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"breakFree\")", 29 | "img": "icons/svg/combat.svg", 30 | "type": "script" 31 | }, 32 | { 33 | "name": "ACTION.DISARM", 34 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"disarm\")", 35 | "img": "icons/svg/combat.svg", 36 | "type": "script" 37 | }, 38 | { 39 | "name": "ACTION.GRAPPLE", 40 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"grapple\")", 41 | "img": "icons/svg/combat.svg", 42 | "type": "script" 43 | }, 44 | { 45 | "name": "ACTION.GRAPPLE_ATTACK", 46 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"grappleAttack\")", 47 | "img": "icons/svg/combat.svg", 48 | "type": "script" 49 | }, 50 | { 51 | "name": "ACTION.SHOVE", 52 | "command": "const character = game.user.character;if (!character) return ui.notifications.warn(\"MACRO.NO_ACTOR\", { localize: true });character.sheet.rollAction(\"shove\")", 53 | "img": "icons/svg/combat.svg", 54 | "type": "script" 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /templates/actor/character/sheet-tabs/talent-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    {{localize "HEADER.TALENT"}}

    4 |
    5 |
    6 | {{localize "TALENT.NAME"}} 7 | {{localize "TALENT.RANK"}} 8 | 9 |
    10 |
    11 | {{#each items as |item|}} 12 | {{#if item.isTalent}} 13 |
    14 |
    15 | 16 |
    {{item.name}}
    17 |
    18 |
    {{item.system.rank}}
    19 |
    20 | 21 | 22 | 25 |
    26 |
    27 | {{/if}} 28 | {{/each}} 29 |
    30 |
    31 |
    32 |
    33 |

    {{localize "HEADER.SPELL"}}

    34 |
    35 |
    36 |  {{localize "SPELL.NAME"}} 37 | {{localize "SPELL.RANK"}} 38 | 39 |
    40 |
    41 | {{#each items as |item|}} 42 | {{#if item.isSpell}} 43 |
    44 |
    45 | 46 | 51 |
    52 |
    {{item.system.rank}}
    53 |
    54 | 55 | 56 | 59 |
    60 |
    61 | {{/if}} 62 | {{/each}} 63 |
    64 |
    65 |
    66 |
    -------------------------------------------------------------------------------- /templates/item/gear/gear-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 | 8 | 9 | 10 | 26 | 27 |
    28 | 31 |
    32 |
    33 |
    34 |
    35 | {{localize "TAB.MAIN"}} 36 | {{#if artifact}} {{#if (or showDescriptionField showDrawbackField showAppearanceField showEffectField)}} 37 | {{localize "TAB.ARTIFACT"}} 38 | {{/if}} {{else}} {{#if (or showCostField showSupplyField showCraftingFields)}} 39 | {{localize "TAB.CRAFT_SUPPLY"}} 40 | {{/if}} {{/if}} 41 |
    42 |
    43 |
    44 | 45 | {{! prettier-ignore }} 46 | {{> systems/forbidden-lands/templates/item/gear/main-tab.hbs}} 47 |
    48 |
    49 | 50 | {{! prettier-ignore }} 51 | {{> systems/forbidden-lands/templates/item/_shared-template-tabs/artifact-tab.hbs}} 52 |
    53 |
    54 | 55 | {{! prettier-ignore }} 56 | {{> systems/forbidden-lands/templates/item/_shared-template-tabs/supply-tab.hbs}} 57 |
    58 |
    59 |
    60 | -------------------------------------------------------------------------------- /templates/item/armor/armor-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 | 8 | 9 | 10 | 26 | 27 |
    28 | 31 |
    32 |
    33 |
    34 |
    35 | {{localize "TAB.MAIN"}} 36 | {{#if artifact}} {{#if (or showDescriptionField showDrawbackField showAppearanceField showEffectField)}} 37 | {{localize "TAB.ARTIFACT"}} 38 | {{/if}} {{else}} {{#if (or showCostField showSupplyField showCraftingFields)}} 39 | {{localize "TAB.CRAFT_SUPPLY"}} 40 | {{/if}} {{/if}} 41 |
    42 |
    43 |
    44 | 45 | {{! prettier-ignore }} 46 | {{> systems/forbidden-lands/templates/item/armor/main-tab.hbs}} 47 |
    48 |
    49 | 50 | {{! prettier-ignore }} 51 | {{> systems/forbidden-lands/templates/item/_shared-template-tabs/artifact-tab.hbs}} 52 |
    53 |
    54 | 55 | {{! prettier-ignore }} 56 | {{> systems/forbidden-lands/templates/item/_shared-template-tabs/supply-tab.hbs}} 57 |
    58 |
    59 |
    60 | -------------------------------------------------------------------------------- /templates/item/weapon/weapon-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 |
    6 |
    7 | 8 | 9 | 10 | 11 | 27 | 28 |
    29 | 32 |
    33 |
    34 |
    35 |
    36 | {{localize "TAB.MAIN"}} 37 | {{#if artifact}} {{#if (or showDescriptionField showDrawbackField showAppearanceField showEffectField)}} 38 | {{localize "TAB.ARTIFACT"}} 39 | {{/if}} {{else}} {{#if (or showCostField showSupplyField showCraftingFields)}} 40 | {{localize "TAB.CRAFT_SUPPLY"}} 41 | {{/if}} {{/if}} 42 |
    43 |
    44 |
    45 | 46 | {{! prettier-ignore }} 47 | {{> systems/forbidden-lands/templates/item/weapon/main-tab.hbs}} 48 |
    49 |
    50 | 51 | {{! prettier-ignore }} 52 | {{> systems/forbidden-lands/templates/item/_shared-template-tabs/artifact-tab.hbs}} 53 |
    54 |
    55 | 56 | {{! prettier-ignore }} 57 | {{> systems/forbidden-lands/templates/item/_shared-template-tabs/supply-tab.hbs}} 58 |
    59 |
    60 |
    61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: "\U0001F41B New bug report" 2 | description: Create a report to help us improve. 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for your interest in the project and taking the time to fill out this bug report! 9 | > **Warning** Bugs related to the official content modules should be [submitted here instead](https://github.com/aMediocreDad/fbl-content-modules/issues) 10 | - type: textarea 11 | id: bug-description 12 | attributes: 13 | label: Describe the bug 14 | description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks! 15 | placeholder: I am doing ... What I expect is ... What actually happening is ... 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: reproduction 20 | attributes: 21 | label: Reproduction 22 | description: Please provide a step by step guide to reproducing the issue. 23 | placeholder: Reproduction steps 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: system-info 28 | attributes: 29 | label: System Info 30 | description: | 31 | Please provide a bullet list of your system information. 32 | - OS: [e.g. macOS] 33 | - Client [e.g. Foundry Desktop app, Chrome, Firefox] 34 | - Foundry Version [e.g. 9.269, 10] 35 | render: shell 36 | placeholder: System, OS, version, etc. 37 | validations: 38 | required: true 39 | - type: textarea 40 | id: logs 41 | attributes: 42 | label: Logs 43 | description: | 44 | You can find logs by pressing "f12" or "cmd+shift+i" in the browser-window/Foundry. Please try not to insert an image but copy paste the log text. 45 | - type: checkboxes 46 | id: checkboxes 47 | attributes: 48 | label: Validations 49 | description: Before submitting the issue, please make sure you have done the following when troubleshooting the issue 50 | options: 51 | - label: Done a clean install of the system. 52 | required: true 53 | - label: Disabled all modules. 54 | required: true 55 | - label: Checked if the issue is present in a new world instance (create a new world in foundry using the Forbidden Lands system). 56 | required: true 57 | - label: "Not a bug report about the content modules. Reports regarding the content modules go [here instead](https://github.com/aMediocreDad/fbl-content-modules/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)." 58 | required: true 59 | - label: 'Not a "How To"-question (You can ask questions here: [Discussions](https://github.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/discussions)).' 60 | required: true 61 | -------------------------------------------------------------------------------- /templates/components/roll-engine/spell-dialog.hbs: -------------------------------------------------------------------------------- 1 |
    2 |

    {{title}}

    3 |
    4 | {{#unless (eq spell.willpower.max 0)}} 5 | 6 | 8 | 9 | {{#skulls spell.willpower.value spell.willpower.max}} 10 | 12 | {{/skulls}} 13 | 14 | 15 | {{/unless}} 16 |
    17 |

    {{localize "MONSTER.DICE"}}

    18 | 20 |
    {{spellDice}}
    21 |
    22 |
    23 |

    {{localize "DICE.POWER_LEVEL"}}

    24 | 26 |
    {{powerLevel}}
    27 |
    28 |
    29 |
    30 |

    {{localize "MODIFIERS"}}

    31 |
    32 |
    33 | 34 | 36 |
    37 |
    38 | 39 | 41 |
    42 |
    43 | 44 | 46 |
    47 |
    48 | 49 | 57 |
    58 |
    59 |
    60 |
    61 | 63 | 65 |
    66 |
    -------------------------------------------------------------------------------- /templates/actor/monster/monster-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 | 7 |
    8 |
    9 |
    10 | 13 | 18 |
    19 |
    20 | 24 | 26 |
    27 |
    28 |
    29 | 31 | 33 |
    34 | 36 |
    37 |
    38 |
    39 |
    40 | 41 |
    42 | {{localize "TAB.MAIN"}} 43 | {{localize "TAB.COMBAT"}} 44 | {{localize "TAB.GEAR"}} 45 | {{localize "TAB.NOTE"}} 46 |
    47 | 48 |
    49 |
    50 | {{> systems/forbidden-lands/templates/actor/character/sheet-tabs/main-tab.hbs}} 51 |
    52 |
    53 | {{> systems/forbidden-lands/templates/actor/monster/sheet-tabs/combat-tab.hbs}} 54 |
    55 |
    56 | {{> systems/forbidden-lands/templates/actor/_shared-template-tabs/gear-tab.hbs}} 57 |
    58 |
    59 | {{editor system.bio.note.value target="system.bio.note.value" button=true owner=owner 60 | editable=true}} 61 |
    62 |
    63 |
    64 |
    65 | -------------------------------------------------------------------------------- /src/legacy-styles/components/_character-generator.scss: -------------------------------------------------------------------------------- 1 | @use "../utils"; 2 | 3 | .chargen .chargen-roll { 4 | width: 30px; 5 | height: 30px; 6 | background-image: url("./assets/rolling-dices.png"); 7 | background-size: 30px 30px; 8 | background-position: center; 9 | background-repeat: no-repeat; 10 | } 11 | .chargen .chargen-roll:hover { 12 | background-image: url("./assets/rolling-dices.png"); 13 | background-size: 35px 35px; 14 | background-position: center; 15 | background-repeat: no-repeat; 16 | } 17 | 18 | .chargen-options { 19 | background: #363636; 20 | display: flex; 21 | justify-content: space-around; 22 | b { 23 | line-height: 1; 24 | position: relative; 25 | font-weight: 400; 26 | padding: 8px 19px; 27 | color: #fff; 28 | font-family: "IM Fell DW Pica"; 29 | text-transform: uppercase; 30 | &:hover { 31 | text-shadow: 0 0 6px #fff; 32 | } 33 | @include utils.tab-button("&:hover"); 34 | } 35 | } 36 | 37 | .chargen-title { 38 | margin-top: 20px; 39 | font-size: var(--font-size-18); 40 | font-family: "IM Fell DW Pica"; 41 | font-weight: 600; 42 | text-transform: uppercase; 43 | color: #363636; 44 | } 45 | 46 | .chargen-description { 47 | font-family: "IM Fell Great Primer"; 48 | font-size: var(--font-size-16); 49 | text-align: justify; 50 | margin: 20px 0; 51 | line-height: 1.35; 52 | } 53 | 54 | .chargen-section { 55 | align-items: flex-start; //To avoid the right-hand side boxes moving too much. 56 | button.chargen-roll { 57 | margin: 10px 10px 10px 5px; 58 | min-width: 40px; 59 | min-height: 40px; 60 | color: #fff; 61 | } 62 | .chargen-section-content { 63 | display: flex; 64 | flex-flow: column; 65 | flex: 1 0 20%; 66 | } 67 | } 68 | 69 | .chargen-build { 70 | min-width: 33%; 71 | display: flex; 72 | flex-direction: column; 73 | color: #363636; 74 | flex-wrap: wrap; 75 | padding: 15px; 76 | margin-top: 4px; 77 | margin-left: 30px; 78 | font-family: "IM FELL DW Pica"; 79 | justify-content: space-around; 80 | font-size: var(--font-size-16); 81 | background: #fff; 82 | border-image-outset: 3px; 83 | } 84 | 85 | .chargen-items { 86 | font-weight: bold; 87 | } 88 | 89 | .chargen-list-caption { 90 | text-transform: uppercase; 91 | border-bottom: 1px solid; 92 | } 93 | 94 | .chargen-list ul { 95 | padding: 0; 96 | li { 97 | list-style: none; 98 | } 99 | } 100 | 101 | .chargen-select-kin, 102 | .chargen-select-profession { 103 | max-width: 110px; 104 | } 105 | 106 | .chargen-age-container { 107 | white-space: nowrap; 108 | .chargen-age-input { 109 | max-width: 60px; 110 | } 111 | } 112 | 113 | .chargen-list li strong { 114 | text-transform: capitalize; 115 | } 116 | 117 | .chargen-background-table { 118 | & > div { 119 | border-bottom: 1px solid #363636; 120 | padding: 10px; 121 | } 122 | 123 | & > div:nth-child(odd) { 124 | background: #ccc; 125 | } 126 | button:hover { 127 | background-color: #fff; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/utils-tests/semver-compare.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it, describe } from "bun:test"; 2 | import semverComp from "@utils/semver-compare"; 3 | 4 | describe("semver-compare", () => { 5 | it("should accepts base cases", () => { 6 | expect(semverComp("5.5.5", "6.6.6", "7.7.7")).toBe(true); 7 | expect(semverComp("8.5.5", "6.6.6", "7.7.7")).toBe(false); 8 | }); 9 | 10 | it("should accept edge cases", () => { 11 | expect(semverComp("5.15.200", "10.90.47", "10.101.30")).toBe(true); 12 | expect(semverComp("5.15.200", "10.90.47", "", { gEqMin: true })).toBe(true); 13 | }); 14 | 15 | it("should throw with invalid SemVer", () => { 16 | expect(() => semverComp("3.3.3", "0", "02")).toThrow(); 17 | expect(() => semverComp("0", "0", "0")).toThrow(); 18 | // @ts-expect-error Testing invalid input 19 | expect(() => semverComp("0.0.0", 22)).toThrow(); 20 | expect(() => 21 | // @ts-expect-error Testing invalid input 22 | semverComp(Number.POSITIVE_INFINITY, "0.0.0", "0.0.0"), 23 | ).toThrow(); 24 | // @ts-expect-error Testing invalid input 25 | expect(() => semverComp({ min: "4.3.5" }, "0.0.0", "0.0.0")).toThrow(); 26 | // @ts-expect-error Testing invalid input 27 | expect(() => semverComp("4.3.5", Number.NaN, "0.0.0")).toThrow; 28 | }); 29 | 30 | it("should throw when missing comparators", () => { 31 | // @ts-expect-error Testing invalid input 32 | expect(() => semverComp("6.6.6")).toThrow(); 33 | expect(() => semverComp("", "3.4.4", "")).toThrow(); 34 | // @ts-expect-error Testing invalid input 35 | expect(() => semverComp(["0.0.0", "0.0.0"])).toThrow(); 36 | }); 37 | 38 | it("should accept equality", () => { 39 | expect(semverComp("6.6.6", "6.6.6", "", { eqMin: true })).toBe(true); 40 | expect(semverComp("8.8.8", "6.6.6", "6.6.6", { eqMax: true })).toBe(true); 41 | expect( 42 | semverComp("6.6.6", "6.6.6", "6.6.6", { eqMin: true, eqMax: true }), 43 | ).toBe(true); 44 | expect(semverComp("6.6.6", "5.5.5", "", { eqMin: true })).toBe(false); 45 | expect(semverComp("8.8.8", "7.7.7", "6.6.6", { eqMax: true })).toBe(false); 46 | expect( 47 | semverComp("4.4.4", "6.6.6", "6.6.6", { eqMin: true, eqMax: true }), 48 | ).toBe(false); 49 | }); 50 | 51 | it("should return true if less than or equal with opts", () => { 52 | expect(semverComp("6.6.6", "7.7.7", "7.7.7", { lEqMax: true })).toBe(true); 53 | expect(semverComp("6.8.8", "7.7.8", "7.7.7", { lEqMax: true })).toBe(false); 54 | }); 55 | 56 | it("should return true if greater than or equal with opts", () => { 57 | expect(semverComp("6.6.6", "6.6.6", "7.7.7", { gEqMin: true })).toBe(true); 58 | expect(semverComp("6.6.6", "5.5.5", "7.7.7", { gEqMin: true })).toBe(false); 59 | }); 60 | 61 | it("should return true if less than or equal and greater than or equal with opts", () => { 62 | expect( 63 | semverComp("6.6.6", "6.6.6", "6.6.6", { gEqMin: true, lEqMax: true }), 64 | ).toBe(true); 65 | expect( 66 | semverComp("6.7.6", "6.6.6", "6.5.6", { gEqMin: true, lEqMax: true }), 67 | ).toBe(false); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /src/journal/styles/_enrichers.scss: -------------------------------------------------------------------------------- 1 | .fbl-example { 2 | margin: 10px auto 0; 3 | font-family: "IM Fell Three Line Pica"; 4 | text-transform: uppercase; 5 | border-top: 1px solid black; 6 | width: 18%; 7 | padding-top: 4px; 8 | } 9 | 10 | .fbl-uppercase { 11 | font-style: normal; 12 | text-transform: uppercase; 13 | font-family: "IM Fell DW Pica SC"; 14 | font-size: var(--font-size-14); 15 | } 16 | 17 | .fbl-inline-heading { 18 | font-family: "IM Fell Three Line Pica"; 19 | font-weight: bold; 20 | margin-top: 4px; 21 | } 22 | 23 | .fbl-branding-bold { 24 | font-size: 0.9rem; 25 | font-family: Branding, Ubuntu, sans-serif; 26 | font-weight: bold; 27 | text-transform: uppercase; 28 | } 29 | 30 | .fbl-skull { 31 | display: inline; 32 | font-family: "skullz"; 33 | line-height: 0; 34 | text-transform: lowercase; 35 | font-style: normal; 36 | position: relative; 37 | top: 5px; 38 | font-size: 1.75rem; 39 | } 40 | 41 | .fbl-swords { 42 | display: inline; 43 | font-family: "Swordlings"; 44 | font-style: normal; 45 | position: relative; 46 | left: 1px; 47 | font-weight: 600; 48 | line-height: 0; 49 | } 50 | 51 | .inline-table, 52 | .inline-scene { 53 | border: none; 54 | background: transparent; 55 | border-bottom: 1px dotted grey; 56 | padding: 0; 57 | 58 | & > i { 59 | color: var(--color-text-dark-inactive); 60 | margin-right: 0.25em; 61 | } 62 | } 63 | 64 | .big-first-char { 65 | font-size: 4.125rem; 66 | transform: translateY(-0.2rem); 67 | font-style: normal; 68 | line-height: 1; 69 | position: relative; 70 | margin-bottom: -2rem; 71 | float: left; 72 | margin-right: 0.5rem; 73 | margin-inline-start: 1rem; 74 | } 75 | 76 | p:has(span.big-first-char) { 77 | padding-top: 6rem; 78 | } 79 | 80 | .big-first-char::before { 81 | content: url(./assets/journal-art/ravens/raven1.webp); 82 | position: absolute; 83 | display: inherit; 84 | transform: scale(0.25) translate(-170%, -220%); 85 | } 86 | .big-first-char.r2::before { 87 | content: url(./assets/journal-art/ravens/raven2.webp); 88 | transform: scale(0.22) translate(-200%, -259%); 89 | } 90 | 91 | p:has(span.big-first-char.r2) { 92 | padding-top: 5rem; 93 | } 94 | 95 | .big-first-char.r3::before { 96 | content: url(./assets/journal-art/ravens/raven3.webp); 97 | transform: scale(0.16) translate(-282%, -335%); 98 | } 99 | .big-first-char.r4::before { 100 | content: url(./assets/journal-art/ravens/raven4.webp); 101 | transform: scale(0.2) translate(-234%, -263%); 102 | } 103 | 104 | p:has(span.big-first-char.r4) { 105 | padding-top: 3rem; 106 | 107 | span.big-first-char.r4 { 108 | margin-inline-start: 1.8rem; 109 | } 110 | } 111 | 112 | .big-first-char.r6::before { 113 | content: url(./assets/journal-art/ravens/raven6.webp); 114 | transform: scale(0.15) translate(-302%, -357%); 115 | } 116 | 117 | .big-first-char.r7::before { 118 | content: url(./assets/journal-art/ravens/raven7.webp); 119 | transform: scale(0.22) translate(-200%, -210%); 120 | } 121 | 122 | p:has(span.big-first-char.r7) { 123 | padding-top: 2rem; 124 | } 125 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forbidden-lands", 3 | "version": "13.0.5", 4 | "private": true, 5 | "description": "

    \r \"system\r \r \"foundry-compatibility-version\"\r \r \r \"Maintenance\"\r \r \"Localization\"\r \r \"License:\r \r
    \r
    \r \"Logo\"\r

    ", 6 | "homepage": "https://github.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt#readme", 7 | "bugs": { 8 | "url": "https://github.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/fvtt-fria-ligan/forbidden-lands-foundry-vtt.git" 13 | }, 14 | "license": "GPL-3.0-or-later", 15 | "contributors": [ 16 | { 17 | "name": "aMediocreDad", 18 | "email": "hello@amediocre.dev" 19 | } 20 | ], 21 | "type": "module", 22 | "module": "index.ts", 23 | "scripts": { 24 | "build": "bun esbuild.config.ts", 25 | "format": "biome format --write .", 26 | "foundry": "bun tools/foundry.ts", 27 | "postinstall": "lefthook install && bun run setup", 28 | "lint": "biome check --write .", 29 | "lint:ci": "bunx biome ci .", 30 | "release": "bun tools/release.ts", 31 | "setup": "bun tools/setup.ts", 32 | "start": "bun run build --development", 33 | "test": "bun run test:types && bun run lint:ci", 34 | "test:e2e": "bunx playwright test", 35 | "test:types": "bunx tsc && bunx tsc -p ./src/tsconfig.json", 36 | "version": "bun tools/version.ts" 37 | }, 38 | "dependencies": { 39 | "foundry-year-zero-roller": "Stefouch/foundry-year-zero-roller#v5.2.2", 40 | "rpg-awesome": "0.2.0", 41 | "ultrahtml": "1.6.0" 42 | }, 43 | "devDependencies": { 44 | "@biomejs/biome": "1.9.4", 45 | "@changesets/changelog-github": "0.5.1", 46 | "@changesets/cli": "2.29.4", 47 | "@evilmartians/lefthook": "^1.11.13", 48 | "@playwright/test": "^1.52.0", 49 | "@types/jquery": "3.5.32", 50 | "@types/node": "22.15.24", 51 | "@types/showdown": "2.0.6", 52 | "bun-types": "1.2.15", 53 | "devmoji": "2.3.0", 54 | "esbuild": "0.25.5", 55 | "esbuild-sass-plugin": "3.3.1", 56 | "execa": "9.6.0", 57 | "globby": "14.1.0", 58 | "gluegun": "5.2.0", 59 | "typescript": "5.8.3" 60 | }, 61 | "trustedDependencies": ["@biomejs/biome", "esbuild"] 62 | } 63 | -------------------------------------------------------------------------------- /templates/actor/monster/sheet-tabs/combat-tab.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | {{localize "HEADER.TALENT"}} 5 | / 6 | {{localize "HEADER.SPELL"}} 7 | / 8 | {{localize "HEADER.CRITICAL_INJURY"}} 9 |

    10 |
    11 |
    12 | 13 | {{localize "MONSTER.NAME"}} 14 | 15 | 16 | {{localize "MONSTER.DESCRIPTION"}} 17 | 18 | 19 | 20 | 21 |
    22 |
    23 | {{#each items as |item|}} 24 | {{#if (or item.isTalent item.isSpell item.isCriticalInjury)}} 25 |
    26 |
    27 | 28 |
    29 | 30 | {{item.name}} 31 | 32 |
    33 | {{{item.system.description}}} 34 |
    35 |
    36 |
    37 | 48 |
    49 | {{/if}} 50 | {{/each}} 51 |
    52 |
    53 |
    54 |
    55 |

    56 | 59 |

    60 |
    61 |
    62 | 63 | 64 |  {{localize "MONSTER.NAME"}} 65 | 66 | 67 | 68 | 69 |
    70 |
      71 | {{#each items as |item|}} 72 | {{#if (or item.isMonsterAttack (and item.isWeapon item.isEquipped))}} 73 |
    1. 74 |
      75 | 76 |
      77 | 80 | {{{item.system.description}}} 81 |
      82 |
      83 | 94 |
    2. 95 | {{/if}} 96 | {{/each}} 97 |
    98 |
    99 |
    100 |
    -------------------------------------------------------------------------------- /templates/actor/character/npc-sheet.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 | 7 |
    8 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 27 | 31 | 41 | 45 | 48 | 52 | 53 |
    11 | 12 | 14 | 15 | 17 | 18 | 20 | 21 |
    25 | 26 | 28 | 30 | 32 | {{#if system.bio.reputation.value}} 33 | 37 | {{else}} 38 | 39 | {{/if}} 40 | 42 | 44 | 46 | 47 | 49 | 51 |
    54 |
    55 |
    56 | 57 |
    58 | {{localize "TAB.MAIN"}} 59 | {{localize "TAB.COMBAT"}} 60 | {{localize "TAB.TALENT"}} 61 | {{localize "TAB.GEAR"}} 62 | {{localize "TAB.NOTE"}} 63 |
    64 | 65 |
    66 |
    67 | 68 | {{! prettier-ignore }} 69 | {{> systems/forbidden-lands/templates/actor/character/sheet-tabs/main-tab.hbs}} 70 |
    71 |
    72 | 73 | {{! prettier-ignore }} 74 | {{> systems/forbidden-lands/templates/actor/character/sheet-tabs/combat-tab.hbs}} 75 |
    76 |
    77 | 78 | {{! prettier-ignore }} 79 | {{> systems/forbidden-lands/templates/actor/character/sheet-tabs/talent-tab.hbs}} 80 |
    81 |
    82 | 83 | {{! prettier-ignore }} 84 | {{> systems/forbidden-lands/templates/actor/_shared-template-tabs/gear-tab.hbs}} 85 |
    86 |
    87 | 88 | {{! prettier-ignore }} 89 | {{editor system.bio.note.value target="system.bio.note.value" button=true owner=owner 90 | editable=true}} 91 |
    92 |
    93 |
    94 |
    95 | --------------------------------------------------------------------------------