Compare commits

..
5 Commits
Author SHA1 Message Date
uberwald 12faf34631 Fix roll with armor 0
Release Creation / build (release) Failing after 48s
2026-01-19 20:19:36 +01:00
uberwald afa6e7a323 Fix roll with armor 0 2026-01-19 20:19:09 +01:00
uberwald 89f5674fb9 Fix roll with armor 0 2026-01-19 20:18:54 +01:00
uberwald cd06d2e5ec Fix roll with armor 0 2026-01-19 20:18:16 +01:00
uberwald cdddcb6aee Fix roll with armor 0 2026-01-19 20:14:24 +01:00
313 changed files with 2583 additions and 9158 deletions
+7 -16
View File
@@ -12,13 +12,15 @@ jobs:
#- uses: actions/checkout@v3 #- uses: actions/checkout@v3
- uses: RouxAntoine/checkout@v3.5.4 - uses: RouxAntoine/checkout@v3.5.4
with:
ref: "v13"
# get part of the tag after the `v` # get part of the tag after the `v`
- name: Extract tag version number - name: Extract tag version number
id: get_version id: get_version
uses: battila7/get-version-action@v2 uses: battila7/get-version-action@v2
# Substitute the Manifest and Download URLs in the system.json # Substitute the Manifest and Download URLs in the module.json
- name: Substitute Manifest and Download Links For Versioned Ones - name: Substitute Manifest and Download Links For Versioned Ones
id: sub_manifest_link_version id: sub_manifest_link_version
uses: microsoft/variable-substitution@v1 uses: microsoft/variable-substitution@v1
@@ -26,9 +28,9 @@ jobs:
files: "system.json" files: "system.json"
env: env:
version: ${{steps.get_version.outputs.version-without-v}} version: ${{steps.get_version.outputs.version-without-v}}
url: https://www.uberwald.me/gitea/${{gitea.repository}} url: https://www.uberwald.me/gitea/public/bol
manifest: https://www.uberwald.me/gitea/public/bol/releases/download/latest/system.json manifest: https://www.uberwald.me/gitea/public/bol/releases/latest/system.json
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/bol.zip download: https://www.uberwald.me/gitea/public/bol/releases/download/${{github.event.release.tag_name}}/bol.zip
# Create a zip file with all files required by the module to add to the release # Create a zip file with all files required by the module to add to the release
- run: | - run: |
@@ -49,15 +51,4 @@ jobs:
files: |- files: |-
./bol.zip ./bol.zip
system.json system.json
api_key: "${{secrets.ALLOW_PUSH_RELEASE}}" api_key: "${{secrets.RELEASE_TOKEN_BOL}}"
- name: Publish to Foundry server
uses: https://github.com/djlechuck/foundryvtt-publish-package-action@v1
with:
token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }}
id: "bol"
version: ${{github.event.release.tag_name}}
manifest: "https://www.uberwald.me/gitea/public/bol/releases/download/latest/system.json"
notes: "https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/bol.zip"
compatibility-minimum: "14"
compatibility-verified: "14"
-1
View File
@@ -8,4 +8,3 @@ todo.md
/jsconfig.json /jsconfig.json
/package.json /package.json
/package-lock.json /package-lock.json
/.github
-51
View File
@@ -1,51 +0,0 @@
# BoL — FoundryVTT Game System
FoundryVTT game system for Barbarians of Lemuria (French edition, Ludospherik). System id `bol`. Compatible Foundry v13v14.
## Commands
| Command | Action |
|---------|--------|
| `npm run css` | Compile LESS → `css/bol.css` (one-shot) |
| `npm run watch` / `npm start` | Watch `styles/**/*.less`, recompile on change |
No test or lint scripts exist.
## Entry Point & Architecture
`module/bol.js` — wires everything in `Hooks.once('init', ...)` and `Hooks.once('ready', ...)`.
| Path | Role |
|------|------|
| `module/bol.js` | System init, sheet/data model registration, socket setup |
| `module/system/config.js` | `BOL` constants, `System.debugMode` flag |
| `module/system/bol-utility.js` | Static helpers, settings (`bol` namespace), socket handler (`system.bol`) |
| `module/controllers/bol-rolls.js` | All roll logic — `BoLRoll` static methods |
| `module/models/` | DataModel classes (`.mjs`) for Actor/Item types |
| `module/applications/sheets/` | **AppV2 sheets** (`ActorSheetV2`/`ItemSheetV2` + `HandlebarsApplicationMixin`) |
| `styles/` | LESS sources → compiled to `css/bol.css` |
| `templates/` | Handlebars `.hbs` partials |
| `lang/` | `fr.json`, `en.json`, `de.json`, `es.json` |
## Key Conventions
- **AppV2 sheets** — base classes are `foundry.applications.sheets.ActorSheetV2` / `ItemSheetV2` mixed with `HandlebarsApplicationMixin`. Do NOT use `foundry.appv1.sheets.*`.
- **`game.bol`** — system exposes `BoLActor`, `BoLItem`, `BoLRoll`, `BoLUtility`, `Macros`, `BOL` config, `models`, `sheets` on this namespace after init.
- **i18n prefix** — all keys start with `BOL.`; use `game.i18n.localize("BOL.xxx")`.
- **FR-first** — `fr.json` is most complete. EN compendium translation requires [Babele](https://gitlab.com/riccisi/foundryvtt-babele) module. A warning fires at startup if EN is selected without Babele.
- **`System.debugMode = true`** in `module/system/config.js:8` — set to `false` before release.
- **LESS only** — never edit `css/bol.css` directly. Edit `.less` files under `styles/`, recompile with `npm run css`.
- **Data models** — `template.json` defines schema via `Actor.types` (character, encounter, horde, vehicle) and `Item.types` (item, feature). All data lives under `.system`.
- **Sheet partials** — registered in `module/system/templates.js`. Add new partials there.
- **Static classes** — `BoLRoll` and `BoLUtility` are purely static; never instantiate.
- **`template.json.SAVED`** in root is the data model reference; the actual `template.json` may need regeneration.
- **`package.json` and lockfile are in `.gitignore`** — npm is dev-only for LESS compilation. Not needed to run the system in Foundry.
- **`{{formInput}}` for HTML fields** — always use `{{formInput fieldDef enriched=x value=y name="system.path" toggled=isEditable}}` for `HTMLField` in AppV2 sheets. Never use raw `<prose-mirror>` elements; they break form submission.
- **Tab switching** — use CSS-only toggle (no `this.render()`) to preserve ProseMirror editor state. See `base-item-sheet.mjs` pattern.
## CI & Release
- CI triggers **only on release publish** (`.gitea/workflows/main.yml`).
- Release zips: `system.json template.json README.md LICENSE assets/ compendiums/ css/ fonts/ images/ lang/ module/ packs/ styles/ templates/ ui/`.
- Version/URL manifest/download are substituted in `system.json` during CI.
- Published to Foundry package registry via `foundryvtt-publish-package-action`.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

-63
View File
@@ -1,63 +0,0 @@
{
"label": "Game Aid",
"mapping": {
"name": "name",
"description": "content",
"categories": {
"path": "categories",
"converter": "nameCollection"
},
"pages": {
"path": "pages",
"converter": "pages"
}
},
"entries": {
"Aide du Jeu": "Game Aid",
"8ihDiCxC47fcdKVA": {
"name": "Game Aid",
"pages": {
"8agBoLYo99u530d1": {
"name": "Credits",
"text": "<p>This system was started by Zigmund, then taken over, completed and maintained by LeRatierBretonnien.</p><p>You can contact me on the French Foundry Discord: LeRatierBretonnien#2065</p><p>The git repository is available here: <a href=\"https://www.uberwald.me/gitea/public/bol\">https://www.uberwald.me/gitea/public/bol</a></p><p>Barbarians of Lemuria is a game by Simon Washbourne (Beyond Belief Games), translated into French by Ludospherik.</p><p>The full range is available on this page: <a href=\"http://www.ludospherik.fr/content/14-barbarians-of-lemuria\">http://www.ludospherik.fr/content/14-barbarians-of-lemuria</a></p><img src=\"systems/bol/assets/pages/b9BjVFaWB6uyyKsD-pages-8agBoLYo99u530d1-image-Rg2nVaMlglfJ6Qcm.png\" />"
},
"NAcpMm6NlyhwvWRA": {
"name": "Features",
"text": "<p>The BoL system under Foundry allows you to:</p><ul><li><p>create characters and NPCs (minions, toughs, rivals),</p></li><li><p>create creatures,</p></li><li><p>make dice rolls for attributes, aptitudes, and careers,</p></li><li><p>make dice rolls for combat attacks,</p></li><li><p>manage initiative,</p></li><li><p>make dice rolls to cast spells or perform alchemical recipes,</p></li><li><p>manage damage and lifeblood loss,</p></li><li><p>display and manage automations in chat windows, with assistance for the use of Hero Points and critical successes.</p></li></ul>"
},
"hdQixhZGfAytdbSg": {
"name": "Compendiums",
"text": "<p>The system comes with a series of compendiums providing quick access to careers, boons, and equipment.</p><p>The official cards are available, with the kind permission of Ludospherik.</p><p>To use content from a compendium, simply drag and drop the item into the world. For example: open the armours compendium, select <em>Light Armour</em>, and drag it onto a character sheet.</p><img src=\"systems/bol/assets/pages/help-en-compendium.png\" />"
},
"UfvTY80U49k6YFwe": {
"name": "Dice Rolls (Attributes/Aptitudes)",
"text": "<p>When a player clicks an attribute or aptitude, the following window appears:</p><img src=\"systems/bol/assets/pages/help-en-attribute-dialog.png\" /><ol><li><p>Attribute the player clicked on. It can be changed with this drop-down menu.</p></li><li><p>Career to apply if needed. Left-click to select, CTRL+left-click to deselect. When selected, the career rank is applied.</p></li><li><p>Available boons (i.e. those that grant a bonus die). Selection mode is identical to careers. When selected, the bonus die is applied to the roll.</p></li><li><p>Available flaws (i.e. those that impose a penalty die). Selection mode is identical to careers. When selected, the penalty die is applied to the roll.</p></li><li><p>Effects. Effects are permanent modifiers that affect certain rolls. See the corresponding chapter.</p></li><li><p>Manual addition of penalty or bonus dice.</p></li><li><p>Manual modifiers, according to the GM's ruling.</p></li><li><p>Reminder of the number of dice, based on the choices made in the dialog.</p></li><li><p>Complete list of modifiers applied to the roll.</p></li></ol>"
},
"4CLyyt3dtpG6YNMi": {
"name": "Dice Rolls (Weapon)",
"text": "<p>To make an attack with a weapon, go to the <strong>Actions</strong> tab and click the weapon's name. Targeting an opponent is recommended in order to take advantage of the system automations.</p><img src=\"systems/bol/assets/pages/help-en-weapon-dialog.png\" /><ol><li><p>Default weapon attribute. It can be changed with this drop-down menu.</p></li><li><p>Default weapon aptitude. It can be changed with this drop-down menu.</p></li><li><p>Possible weapon bonus on the attack roll.</p></li><li><p>Target defence, when an opponent is targeted.</p></li><li><p>Career to apply if needed. Left-click to select, CTRL+left-click to deselect. When selected, the career rank is applied.</p></li><li><p>Available boons (i.e. those that grant a bonus die). Selection mode is identical to careers. When selected, the bonus die is applied to the roll.</p></li><li><p>Available flaws (i.e. those that impose a penalty die). Selection mode is identical to careers. When selected, the penalty die is applied to the roll.</p></li><li><p>Effects. Effects are permanent modifiers that affect certain rolls. See the corresponding chapter.</p></li><li><p>Manual addition of penalty or bonus dice.</p></li><li><p>Information area if the weapon has the <em>Bonus Die</em> option enabled.</p></li><li><p>Agility penalty from armour and shield.</p></li><li><p>Manual modifiers, according to the GM's ruling.</p></li><li><p>Reminder of the number of dice, based on the choices made in the dialog.</p></li><li><p>Complete list of modifiers applied to the roll.</p></li></ol>"
},
"r003R5yIaiKxThOc": {
"name": "Character Sheet",
"text": "<h2>Attributes</h2><img src=\"systems/bol/assets/pages/help-en-sheet-attributes.png\" /><ol><li><p>Name + Experience area.</p></li><li><p>Tab bar for moving between sections.</p></li><li><p>Attributes area. Clicking a name opens the roll dialog, clicking a number allows editing.</p></li><li><p>Aptitudes area, same idea as attributes.</p></li><li><p>Counters area. The red value is the current one, the black value underneath is the maximum.</p></li></ol><h2>Actions</h2><img src=\"systems/bol/assets/pages/help-en-sheet-actions.png\" /><ol><li><p>Weapons. Clicking the name opens the attack dialog for that weapon.</p></li><li><p>Damage. Clicking the damage formula rolls it.</p></li><li><p>List of protections.</p></li><li><p>Clicking the protection formula rolls it.</p></li><li><p>List of shields.</p></li><li><p>Combat options the character knows. Once activated, they appear in the weapon attack dialog.</p></li></ol><h2>Traits</h2><img src=\"systems/bol/assets/pages/help-en-sheet-traits.png\" /><ol><li><p>List of careers. Left-click a career for details.</p></li><li><p>Career rank helper.</p></li><li><p>List of origins. Left-click for details.</p></li><li><p>List of boons. Left-click for details.</p></li><li><p>List of flaws. Left-click for details.</p></li><li><p>List of spoken languages. Left-click for details.</p></li><li><p>List of known combat options. Left-click for details.</p></li><li><p>List of beliefs. Left-click for details.</p></li><li><p>List of active effects. Left-click for details.</p></li></ol><h2>Equipment</h2><img src=\"systems/bol/assets/pages/help-en-sheet-equipment.png\" /><ol><li><p>Quick equipment creation controls.</p></li><li><p>Purse state (optional rule, system setting).</p></li><li><p>Weapons, with quantity management.</p></li><li><p>Shield equip / unequip control.</p></li><li><p>List of protections.</p></li><li><p>Armour equip / unequip control.</p></li></ol><h2>Spells &amp; Alchemy</h2><p>This tab is only available if the character has the Alchemist or Sorcerer career.</p><img src=\"systems/bol/assets/pages/help-en-sheet-spells.png\" /><ol><li><p>List of spells. Left-click the name to open the dedicated roll dialog.</p></li><li><p>Reminder of circle and difficulty.</p></li><li><p>The square button opens the spell details.</p></li><li><p>List of known alchemical preparations.</p></li><li><p>Crafting progress management.</p></li><li><p>The square button opens the preparation details.</p></li></ol><h2>Description</h2><img src=\"systems/bol/assets/pages/help-en-sheet-description.png\" /><p>The description area contains free-form fields for customising the character.</p>"
},
"eRbEqbCW4AhU0cpm": {
"name": "Effects",
"text": "<p>Effects allow permanent modifiers to be applied as long as they are present on the character sheet.</p><p><span style=\"text-decoration:underline\"><span style=\"text-decoration:underline\">Example</span></span>: a character is poisoned, and the poison causes mental fatigue. In game terms, they suffer a -2 penalty to all Mind rolls. To represent this, create an <em>Effect</em> like this:</p><img src=\"systems/bol/assets/pages/help-en-effect-sheet.png\" /><ol><li><p>Create a <em>Trait</em>, and in the <em>Details</em> tab select the subtype <em>Effect</em>.</p></li><li><p>Select <em>Mind</em>.</p></li><li><p>Set a -2 modifier.</p></li></ol><p>Once placed on a character, the penalty is automatically applied whenever a Mind roll is requested. Remove the effect from the character sheet (<em>Traits</em> tab) to remove both the effect and its penalty.<br />A compendium of basic effects is available in the system.</p>"
},
"QmNF6p0lJf3pJoAy": {
"name": "Commands",
"text": "<p>The system provides commands (currently just one) for a few automated tasks.</p><p><strong><span style=\"text-decoration:underline\">This command is only available if the BoL-rulebook module is installed.</span></strong></p><h3>/adventure command</h3><img src=\"systems/bol/assets/pages/help-en-command.png\" /><ol><li><p>Type <code>/adventure</code> in the chat input area (or trigger it from a macro).</p></li><li><p>The system generates an adventure from the core rulebook tables and posts the result in chat.</p></li></ol>"
},
"MOWru5Dbvs4iozXm": {
"name": "Macros",
"text": "<p>The system exposes macros through <code>game.bol.macros</code>. They provide quick access to the most common rolls from a Foundry macro.</p><p><strong>Automatic actor selection:</strong></p><ul><li><p>if exactly one token is selected, the macro uses it;</p></li><li><p>if several tokens are selected, the macro shows a warning;</p></li><li><p>if no token is selected, a GM must select one; a player automatically falls back to their assigned character.</p></li></ul><p><strong>Available rolls</strong></p><p>Attributes (internal keys):</p><pre><code>game.bol.macros.rollAttribute(\"vigor\")\ngame.bol.macros.rollAttribute(\"agility\")\ngame.bol.macros.rollAttribute(\"mind\")\ngame.bol.macros.rollAttribute(\"appeal\")</code></pre><p>Aptitudes:</p><pre><code>game.bol.macros.rollAptitude(\"init\")\ngame.bol.macros.rollAptitude(\"melee\")\ngame.bol.macros.rollAptitude(\"ranged\")\ngame.bol.macros.rollAptitude(\"def\")</code></pre><p>Weapons, spells, and alchemy:</p><pre><code>game.bol.macros.rollWeapon(\"Spear\")\ngame.bol.macros.rollWeapon(0)\n\ngame.bol.macros.rollSpell(\"Javelin\")\ngame.bol.macros.rollSpell(0)\n\ngame.bol.macros.rollAlchemy(\"Fire\")\ngame.bol.macros.rollAlchemy(0)</code></pre><p>For weapons, spells, and alchemical preparations, name lookup is <strong>partial</strong> and <strong>case-insensitive</strong>. You may also use a numeric index.</p><p>Horoscope:</p><pre><code>game.bol.macros.rollHoroscope(\"minor\")\ngame.bol.macros.rollHoroscope(\"major\")</code></pre><p><strong>Generic macro</strong></p><pre><code>game.bol.macros.roll(\"attribute\", \"vigor\")\ngame.bol.macros.roll(\"aptitude\", \"melee\")\ngame.bol.macros.roll(\"weapon\", \"Spear\")\ngame.bol.macros.roll(\"spell\", \"Javelin\")\ngame.bol.macros.roll(\"alchemy\", 0)\ngame.bol.macros.roll(\"horoscope\", \"minor\")</code></pre><p><strong>Compatibility</strong></p><p>The legacy entry point <code>game.bol.macros.rollMacro(type, key)</code> is still available for existing macros.</p>"
},
"rERizrPxSAsvsZY2": {
"name": "Initiative",
"text": "<p>In BoL, initiative is rolled once at the start of combat.</p><p>You can request it automatically through the Combat Tracker, or roll Initiative manually and tick the corresponding box in the dialog:</p><img src=\"systems/bol/assets/pages/help-en-initiative-dialog.png\" /><p>According to the roll result, an initiative rank from 10 to 3 is assigned as follows:</p><p><strong>10</strong> - PC: Legendary Success<br /><strong>9</strong> - PC: Heroic Success<br /><strong>8</strong> - PC: Normal Success<br /><strong>7</strong> - NPC: Opponents or creatures larger than medium size<br /><strong>6</strong> - NPC: Toughs or creatures of medium size or smaller<br /><strong>5</strong> - PC: Normal Failure<br /><strong>4</strong> - NPC: Minions<br /><strong>3</strong> - PC: Critical Failure</p>"
}
}
}
}
}
+1 -5
View File
@@ -1,7 +1,7 @@
{ {
"label": "Boons", "label": "Boons",
"mapping": { "mapping": {
"description": "system.description" "description": "system.description"
}, },
"entries": { "entries": {
"Agilité de lhomme-oiseau": { "Agilité de lhomme-oiseau": {
@@ -96,10 +96,6 @@
"name": "Plains-Born", "name": "Plains-Born",
"description": "<h1>Plains-Born</h1><p>You grew up on the plains. When tracking, trapping, hunting, or carrying out other similar activities (not fighting) in a plains environment, you receive a bonus die.</p>" "description": "<h1>Plains-Born</h1><p>You grew up on the plains. When tracking, trapping, hunting, or carrying out other similar activities (not fighting) in a plains environment, you receive a bonus die.</p>"
}, },
"Envoûtant": {
"name": "Beguiling",
"description": "<h1>Beguiling</h1><p> Your looks and bearing are such that people who might be attracted to your type are enchanted by you. Once per day you may suggest a course of action to a number of rabble equal to appeal + d6, who will do their best to please you, even if it potentially puts them in danger.</p><p>You must have the temptress career to take this boon.</p>"
},
"Fortuné": { "Fortuné": {
"name": "Great Wealth", "name": "Great Wealth",
"description": "<h1>Great Wealth</h1><p>You have a source of income or an inheritance. Roll a bonus die on any attempt to obtain any goods, services, or other items you need whilst in your home city.</p>" "description": "<h1>Great Wealth</h1><p>You have a source of income or an inheritance. Roll a bonus die on any attempt to obtain any goods, services, or other items you need whilst in your home city.</p>"
+1 -25
View File
@@ -1,34 +1,10 @@
{ {
"label": "Weapons", "label": "Weapons",
"mapping": { "mapping": {
"description": "system.description" "description": "system.description"
}, },
"entries": { "entries": {
"Arc de guerre de Tyrus": {
"name": "Tyrus Warbow",
"description": "<h1>Tyrus Warbow</h1><p> The archers of Tyrus train from a very young age in the use of this powerful bow. Tyrus archers have fun with other warriors by lending their bows to them and then watching them struggle to aim at the proverbial “barn door”.</p>"
},
"Épée Valgardienne (1 Main)": {
"name": "Valgardian Blade (1H)",
"description": "<h1>Valgardian Blade (1H)</h1><p>Around 3-4 in length, the Valgardian blade is used either in one hand or in both. The Valgardian bladesmiths guard the secret of their work closely, although many have tried to copy it. It is rumoured that the strength of the blades due mainly to the quality of the iron mined locally.</p>"
},
"Épée Valgardienne (2 mains)": {
"name": "Valgardian Blade (2H)",
"description": "<h1>Valgardian Blade (2H)</h1><p>Around 3-4 in length, the Valgardian blade is used either in one hand or in both. The Valgardian bladesmiths guard the secret of their work closely, although many have tried to copy it. It is rumoured that the strength of the blades due mainly to the quality of the iron mined locally.</p>"
},
"Fronde de lAxos": {
"name": "Axish Sling",
"description": "<h1>Axish Sling</h1><p>Sling: The sling is inexpensive and easy to build. It is a simple leather thong whirled around the head to cast small stones or cast lead bullets with some force, at 30 range increments. Two-handed versions are fitted onto a staff and are called staff-slings. This imparts a greater range, making the increments 60. The Axish sling is actually little different to any other common sling; its just that the people of the Axos mountains are particularly proficient with them. They have however played up the idea that there is some special plant fibre that the thongs are made from that gives them their extra range.</p>"
},
"Hache dabordage de Parsool (1 main)": {
"name": "Parsool Sea Axe (1H)",
"description": "<h1>Parsool Sea Axe (1H)</h1><p>A boarding axe used by Parsool seamen which can be used one or two-handed.</p>"
},
"Hache dabordage de Parsool (2 mains)": {
"name": "Parsool Sea Axe (2H)",
"description": "<h1>Parsool Sea Axe (2H)</h1><p>A boarding axe used by Parsool seamen which can be used one or two-handed.</p>"
},
"Arbalète": { "Arbalète": {
"name": "Crossbow", "name": "Crossbow",
"description": "<h1>Crossbow</h1><p>A crossbow is a simple device for firing a short bolt or quarrel with some force and little training. They take a round to load (ready to fire on the second round).</p>" "description": "<h1>Crossbow</h1><p>A crossbow is a simple device for firing a short bolt or quarrel with some force and little training. They take a round to load (ready to fire on the second round).</p>"
+423 -1730
View File
File diff suppressed because it is too large Load Diff
+1 -142
View File
@@ -55,16 +55,6 @@
"BOL.ui.equip": "Anlegen", "BOL.ui.equip": "Anlegen",
"BOL.ui.delete": "Löschen", "BOL.ui.delete": "Löschen",
"BOL.ui.roll" : "Würfeln", "BOL.ui.roll" : "Würfeln",
"BOL.ui.bonus": "Bonus",
"BOL.ui.log": "Protokoll",
"BOL.ui.add": "Hinzufügen",
"BOL.ui.rolls": "Würfe",
"BOL.ui.action": "Aktion",
"BOL.ui.unarmedAttack": "Unbewaffneter Angriff",
"BOL.ui.vehicleType.boat": "Schiff",
"BOL.ui.vehicleType.flyingboat": "Flugschiff",
"BOL.ui.vehicleType.chariot": "Streitwagen/Karre",
"BOL.ui.vehicleType.other": "Anderes",
"BOL.ui.equipment" : "Ausrüstung", "BOL.ui.equipment" : "Ausrüstung",
"BOL.ui.equipmentProperties" : "Ausrüstungseigenschaften", "BOL.ui.equipmentProperties" : "Ausrüstungseigenschaften",
"BOL.ui.weaponAttack" : "Waffenangriff", "BOL.ui.weaponAttack" : "Waffenangriff",
@@ -166,15 +156,6 @@
"BOL.ui.armorAgiMalus": "Rüschtung+Schild-Malus (Geschick)", "BOL.ui.armorAgiMalus": "Rüschtung+Schild-Malus (Geschick)",
"BOL.ui.armorInitMalus": "Rüstungsmalus (Init)", "BOL.ui.armorInitMalus": "Rüstungsmalus (Init)",
"BOL.ui.attackValue": "Angriffswert", "BOL.ui.attackValue": "Angriffswert",
"BOL.ui.attackModifier": "Angriffsmodifikator",
"BOL.ui.vehicleWeapons": "Fahrzeugwaffen",
"BOL.ui.hullDamage": "Rumpfschaden",
"BOL.ui.crewDamage": "Besatzungsschaden",
"BOL.ui.fireDamage": "Feuer ?",
"BOL.ui.undead": "Untot",
"BOL.ui.minor": "Gering",
"BOL.ui.major": "Groß",
"BOL.ui.majorgroup": "Große Gruppe",
"BOL.ui.initMalus": "Init malus", "BOL.ui.initMalus": "Init malus",
"BOL.ui.createEquipment": "Create Equipment", "BOL.ui.createEquipment": "Create Equipment",
@@ -194,9 +175,6 @@
"BOL.featureSubtypes.language": "Sprache", "BOL.featureSubtypes.language": "Sprache",
"BOL.featureSubtypes.gods": "Glauben & Götter", "BOL.featureSubtypes.gods": "Glauben & Götter",
"BOL.featureSubtypes.fightOption": "Kampfoption", "BOL.featureSubtypes.fightOption": "Kampfoption",
"BOL.featureSubtypes.effect": "Effekt",
"BOL.featureSubtypes.horoscope": "Horoskop",
"BOL.featureSubtypes.xplog": "XP-Protokoll",
"BOL.bougette.nomoney": "Mittellos", "BOL.bougette.nomoney": "Mittellos",
"BOL.bougette.tolive": "Zum Überleben", "BOL.bougette.tolive": "Zum Überleben",
@@ -313,17 +291,6 @@
"BOL.itemProperty.natural": "Natürliche Waffe", "BOL.itemProperty.natural": "Natürliche Waffe",
"BOL.itemProperty.onlymodifier": "Nur Modifikator (d.h. Angriffe von Kreaturen)", "BOL.itemProperty.onlymodifier": "Nur Modifikator (d.h. Angriffe von Kreaturen)",
"BOL.itemProperty.attackMalusDice": "Angriffsmalus (Würfel)", "BOL.itemProperty.attackMalusDice": "Angriffsmalus (Würfel)",
"BOL.itemProperty.crewDamageMultiplier": "Multiplikator",
"BOL.itemProperty.crewdamage": "Schadenswert für die Besatzung",
"BOL.itemProperty.hullDamageMultiplier": "Multiplikator",
"BOL.itemProperty.hulldamage": "Schadenswert für den Rumpf",
"BOL.itemProperty.isboarding": "Entern",
"BOL.itemProperty.isbreakrow": "Ruder brechen",
"BOL.itemProperty.iscrewdamage": "Besatzungsschaden",
"BOL.itemProperty.isfiredamage": "Feuerschaden",
"BOL.itemProperty.ishulldamage": "Rumpfschaden",
"BOL.itemProperty.isspur": "Rammstoß",
"BOL.itemProperty.vehicleDamageType": "Fahrzeugschadenstyp",
"BOL.itemStat.quantity": "Anzahl", "BOL.itemStat.quantity": "Anzahl",
"BOL.itemStat.weight": "Gewicht", "BOL.itemStat.weight": "Gewicht",
@@ -412,23 +379,6 @@
"BOL.notification.MacroMultipleTokensSelected": "Du hast mehrere Token ausgewählt", "BOL.notification.MacroMultipleTokensSelected": "Du hast mehrere Token ausgewählt",
"BOL.notification.MacroNoActorAvailable": "Es konnte kein Akteur gefunden werden", "BOL.notification.MacroNoActorAvailable": "Es konnte kein Akteur gefunden werden",
"BOL.notification.MacroNoTokenSelected": "Du musst einen Token auswählen", "BOL.notification.MacroNoTokenSelected": "Du musst einen Token auswählen",
"BOL.notification.MacroNoItemFound": "{actor} : kein(e) {type} gefunden.",
"BOL.notification.MacroSpecifyNameOrIndex": "Geben Sie einen Namen oder einen Index an : {names}",
"BOL.notification.MacroBadIndex": "{actor} : Index {index} für {type} ungültig.",
"BOL.notification.MacroItemNotFound": "{actor} : {type} \"{name}\" nicht gefunden.",
"BOL.notification.MacroUnknownAttribute": "Unbekanntes Attribut : \"{key}\". Werte : vigor, agility, mind, appeal",
"BOL.notification.MacroUnknownAptitude": "Unbekannte Fertigkeit : \"{key}\". Werte : init, melee, ranged, def",
"BOL.notification.MacroUnknownRollType": "Unbekannter Wurftyp : \"{type}\". Gültige Typen : attribute, aptitude, weapon, spell, alchemy, horoscope",
"BOL.notification.MacroNoPower": "Nicht genug Kraftpunkte !",
"BOL.notification.FullDefense": "{actor} ist in voller Verteidigung ! Er kann in dieser Runde nicht angreifen.",
"BOL.notification.WeaponNotFound": "Waffe nicht gefunden !",
"BOL.notification.AlchemyNotFound": "Alchemie nicht gefunden !",
"BOL.notification.SpellNotFound": "Diesen Zauber nicht gefunden !",
"BOL.notification.NotEnoughCreationPrep": "Nicht genug Schöpfungspunkte in die Vorbereitung investiert !",
"BOL.notification.NoPower": "Nicht genug Kraftpunkte !",
"BOL.notification.AttackNotFound": "Der entsprechende Angriff wurde nicht gefunden, Fehler bei der Kampfverfolgung.",
"BOL.notification.NoBirthHour": "{actor} hat keine Geburtsstunde, oder sie ist falsch : {hour}",
"BOL.notification.BadBirthHour": "{hour} entspricht keiner Geburtsstunde",
"BOL.size.tiny": "Winzig", "BOL.size.tiny": "Winzig",
"BOL.size.verysmall": "Sehr klein", "BOL.size.verysmall": "Sehr klein",
@@ -512,54 +462,6 @@
"BOL.ui.biosigns": "Erkennungszeichen", "BOL.ui.biosigns": "Erkennungszeichen",
"BOL.ui.biodescription": "Beschreibung", "BOL.ui.biodescription": "Beschreibung",
"BOL.ui.bionotes": "Notizen", "BOL.ui.bionotes": "Notizen",
"BOL.ui.aggressive": "Aggressiver Zauber?",
"BOL.ui.armornoformula": "Die Rüstung {protect.name} hat keine Formel für den Schutz!",
"BOL.ui.astrologerRank": "Rang des Astrologen",
"BOL.ui.astrology": "Astrologie und Horoskope",
"BOL.ui.astrologyMajor": "Großes Horoskop erstellen",
"BOL.ui.astrologyMajorGroup": "Großes Gruppen-Horoskop erstellen",
"BOL.ui.astrologyMinor": "Kleines Horoskop erstellen",
"BOL.ui.astrologyNoPoints": "Du hast nicht genug Astrologiepunkte!",
"BOL.ui.attributaptitude": "Attribut oder Fertigkeit",
"BOL.ui.boleffects": "Effekte (automatisch)",
"BOL.ui.confirmdelete": "Bist du sicher, dass du diesen Gegenstand löschen willst?",
"BOL.ui.cost": "XP-Kosten",
"BOL.ui.createNaturalProtection": "Natürlichen Schutz erstellen",
"BOL.ui.createNaturalWeapon": "Natürliche Waffe erstellen",
"BOL.ui.creature": "Kreatur",
"BOL.ui.daemon": "Dämon",
"BOL.ui.date": "Datum",
"BOL.ui.deletetitle": "Löschung",
"BOL.ui.effects": "Aktive Effekte",
"BOL.ui.heropoints": "Helden-/Bösewicht-Punkte",
"BOL.ui.hordeAttack": "Hordenangriff",
"BOL.ui.hordeHP": "Vitalität (1 Mitglied)",
"BOL.ui.hordeSize": "Hordengröße",
"BOL.ui.hordehp": "Vitalität (gesamt)",
"BOL.ui.horoscopeCost": "Kosten in Astrologiepunkten",
"BOL.ui.horoscopeDiceMax": "Maximale Würfel",
"BOL.ui.horoscopeDiceRemaining": "Verbleibende Würfel",
"BOL.ui.horoscopeGroup": "Gruppen-Horoskope",
"BOL.ui.isAstrologer": "Astrologenkarriere?",
"BOL.ui.isspecial": "Speziell?",
"BOL.ui.itemnotfound": "Das Objekt dieser Makro konnte nicht gefunden werden",
"BOL.ui.magicnewrules": "Zusätzliche Regeln (vgl. Fan-Zusatz Zauberei!)",
"BOL.ui.makeHoroscope": "Horoskop erstellen",
"BOL.ui.modifier": "Modifikator",
"BOL.ui.newEquipment": "Neue Ausrüstung",
"BOL.ui.newNaturalProtection": "Natürlicher Schutz",
"BOL.ui.newNaturalWeapon": "Natürliche Waffe",
"BOL.ui.nomorealchemypoints": "Nicht genug Schöpfungspunkte!",
"BOL.ui.notenoughxp": "Nicht genug Erfahrungspunkte!",
"BOL.ui.pclistbutton": "Kompakte Ansicht",
"BOL.ui.rabble": "Meute",
"BOL.ui.selectactor": "Wähle deinen Charakter aus, um die Makro zu verwenden",
"BOL.ui.tough": "Zäh",
"BOL.ui.value": "Wert",
"BOL.ui.villain": "Rivale",
"BOL.ui.warninitiative": "Deine Initiative ist nicht verfügbar. Führe für diesen Kampf einen Initiative-Wurf aus.",
"BOL.ui.weaponbonus": "Diese Waffe hat bereits einen Bonuswürfel (z. B. Lieblingswaffe berücksichtigt)",
"BOL.ui.xplog": "XP-Protokoll",
"BOL.chat.welcome1": "Willkommen zu Barbarians of Lemuria (französische Ludospherik-Version, deutsche Übersetzung von Truant Spiele)", "BOL.chat.welcome1": "Willkommen zu Barbarians of Lemuria (französische Ludospherik-Version, deutsche Übersetzung von Truant Spiele)",
"BOL.chat.welcome2": "Zum Spielen werden zwingend die passenden BoL Bücher benötigt. Die französischen Originalbücher für dieses System gibt es hier: http://www.ludospherik.fr/content/14-barbarians-of-lemuria<br>Die deutsche Übersetzung gibt es bei Truant Spiele: https://truant.com/produkt/barbarians-of-lemuria-2", "BOL.chat.welcome2": "Zum Spielen werden zwingend die passenden BoL Bücher benötigt. Die französischen Originalbücher für dieses System gibt es hier: http://www.ludospherik.fr/content/14-barbarians-of-lemuria<br>Die deutsche Übersetzung gibt es bei Truant Spiele: https://truant.com/produkt/barbarians-of-lemuria-2",
@@ -567,50 +469,7 @@
"BOL.chat.welcome4": "Im Discord findet ihr Support für die FoundryVTT-Implementierung dieses Systems: https://discord.gg/pPSDNJk", "BOL.chat.welcome4": "Im Discord findet ihr Support für die FoundryVTT-Implementierung dieses Systems: https://discord.gg/pPSDNJk",
"BOL.chat.welcome5": "Auf ein gutes Spiel in Lemuria!", "BOL.chat.welcome5": "Auf ein gutes Spiel in Lemuria!",
"BOL.chat.welcome6": "", "BOL.chat.welcome6": "",
"BOL.chat.msgonlygm": "Nachricht nur für den GM", "BOL.chat.bolRulebookMessage": "Don't miss the full Rulebook module (including Sagas) available at : https://www.ludospherik-editions.com/en_gb/ !",
"BOL.chat.babelewarning": "⚠ WARNUNG ! Englisch ist ausgewählt, aber das Babele-Modul ist nicht installiert !<br>Bitte installieren Sie babele über den Modul-Reiter in der Foundry-Oberfläche.",
"BOL.chat.bolRulebookMessage": "Don't miss the premium Rulebook module (including Sagas), available on the Foundry website : <a href='https://foundryvtt.com/packages/bol-rulebook' target='_blank'>https://foundryvtt.com/packages/bol-rulebook</a>",
"BOL.chat.bolChroniclesMessage": "Discover the premium Chronicles of Lemuria module (vol. 1), available on the Foundry website : <a href='https://foundryvtt.com/packages/bol-chronicles-1' target='_blank'>https://foundryvtt.com/packages/bol-chronicles-1</a>",
"BOL.chat.applyrecup": "Einige Minuten ausruhen (+{recupHP} Vitalität)",
"BOL.chat.armorRoll": "Rüstungswurf ",
"BOL.chat.bougettefailure": "Du hast zu viel ausgegeben, dein Budget hat sich verringert...",
"BOL.chat.bougettesuccess": "Dein Budget bleibt unverändert!",
"BOL.chat.criticalbuttonjournal": "Heroischer/Legendärer Erfolg",
"BOL.chat.criticalinfo": "Das ist ein heroischer Erfolg! Wähle deine Optionen und Effekte!",
"BOL.chat.criticallegendaryinfo": "Das ist ein legendärer Erfolg! Wähle deine Optionen und Effekte!",
"BOL.chat.damageresume": "{name} hat Verletzungen erlitten ...",
"BOL.chat.defenseReduceDamage2": "Du kannst außerdem 1 Helden-/Bösewicht-Punkt ausgeben, um [[/r 2d6KH(1D6B)]] Vitalitätspunkte zurückzugewinnen; das kostet dich deine nächste Aktion.",
"BOL.chat.fumblemessage": "Wenn du die Konsequenzen eines katastrophalen Fehlschlags akzeptierst (Wahl des Spielleiters), erhältst du 1 zusätzlichen Heldenpunkt",
"BOL.chat.heroicreminder": "Zusätzlich zu den Aktionen der Schaltflächen unten kannst du: <ul><li>Gemetzel: Greife denselben Gegner ein zweites Mal an</li><li>Präziser Schlag: Ein Maluswürfel für deinen Gegner auf eine gewählte Lokalisation</li><li>Entwaffnung</li><li>Massaker an der Meute</li><li>Umwerfen: Wirf deinen Gegner um (max. 1 Größe mehr)</li></ul>Wenn du zusätzlich einen Heldenpunkt ausgibst, können all diese Effekte verdoppelt werden",
"BOL.chat.horoscope": "Horoskop",
"BOL.chat.horoscopedeleted": "Das/die verwendete(n) Horoskop(e) wurde(n) automatisch entfernt.",
"BOL.chat.horoscopemajorfailure": "Dein großes Horoskop ist ein Fehlschlag: {horoscopeName} verliert für dieses Abenteuer 1 Heldenpunkt. Dieser Punkt wurde automatisch abgezogen.",
"BOL.chat.horoscopemajorgroupfailure": "Dein großes Gruppen-Horoskop ist ein Fehlschlag. Du und deine Freunde erleiden für dieses Abenteuer {careerBonus} Maluswürfel.",
"BOL.chat.horoscopemajorgroupsuccess": "Dein großes Gruppen-Horoskop ist ein Erfolg. Du und deine Freunde erhalten für dieses Abenteuer {careerBonus} Bonuswürfel.",
"BOL.chat.horoscopemajorsuccess": "Dein großes Horoskop ist ein Erfolg: {horoscopeName} erhält für dieses Abenteuer 1 zusätzlichen Heldenpunkt. Dieser Punkt wurde automatisch hinzugefügt.",
"BOL.chat.horoscopeminorfailure": "Dein kleines Horoskop ist ein Fehlschlag: Bearbeite den Namen des Horoskops auf deinem Blatt. Du erleidest für diese Situation 1 Maluswürfel.",
"BOL.chat.horoscopeminorsuccess": "Dein kleines Horoskop ist ein Erfolg: Bearbeite den Namen des Horoskops auf deinem Blatt. Du erhältst für diese Situation 1 Bonuswürfel.",
"BOL.chat.horoscopepoints": "Kosten: {points} Astrologiepunkte",
"BOL.chat.initiative": "Initiative-Rang (10 bis 1)",
"BOL.chat.legendaryreminder": "Zusätzlich zu den Aktionen der Schaltflächen unten kannst du 2 Aktionen ausführen: <ul><li>Gemetzel: Greife denselben Gegner ein zweites Mal an</li><li>Präziser Schlag: Ein Maluswürfel für deinen Gegner auf eine gewählte Lokalisation</li><li>Entwaffnung</li><li>Massaker an der Meute</li><li>Umwerfen: Wirf deinen Gegner um (max. 1 Größe mehr)</li></ul>",
"BOL.chat.losshp": "{name} hat {lossHP} Vitalitätspunkte verloren. Wenn er sich ein paar Minuten ausruht, kann er {recupHP} Vitalitätspunkte zurückgewinnen.",
"BOL.chat.nodamage": "Keinen Schaden anwenden",
"BOL.chat.nodamagesummary": "Es wurde kein Schaden erlitten ....",
"BOL.chat.pcnotlinked": "Das Token des Spielercharakters ist nicht mit dem Actor verknüpft",
"BOL.chat.pcnotlinkedmsg": "<b>ACHTUNG</b> Das Token des Spielercharakters ist nicht mit dem Actor verknüpft!",
"BOL.chat.pcwarning": "Achtung! Kein Charakter ist mit dem Spieler verknüpft!",
"BOL.chat.pcwarningmsg": "<b>ACHTUNG</b> Der Spieler ist mit keinem Charakter verknüpft!",
"BOL.chat.rangeinfo": "Wurf von {attackerName} auf {defenderName}",
"BOL.chat.rangenotvisible": "Die Sichtlinie zwischen den Beteiligten ist blockiert.",
"BOL.chat.rangeprefix": "Geschätzte Entfernung: ",
"BOL.chat.rangetitle": "SL-Information",
"BOL.chat.rangevisible": "Die Sichtlinie zwischen den Beteiligten ist frei.",
"BOL.chat.rangeweaponinfo": "Waffe: {weaponName} - Basisreichweite {weaponRange}",
"BOL.chat.rollbougette": "Budgetwurf",
"BOL.chat.rolldamage12Legend": "Schaden +12",
"BOL.chat.selecttarget": "Ziel wählen:",
"BOL.chat.usedhoroscope": "Verwendetes Horoskop",
"BOL.chat.weaponreroll1": "Zur Information: Diese Waffe würfelt Einsen bei ihrem Schaden neu.",
"BOL.settings.rollArmor": "Roll for armor", "BOL.settings.rollArmor": "Roll for armor",
"BOL.settings.rollArmorTooltip": "Roll for armor value, fixed value if unchecked", "BOL.settings.rollArmorTooltip": "Roll for armor value, fixed value if unchecked",
+2 -72
View File
@@ -78,16 +78,6 @@
"BOL.ui.equip": "Equip", "BOL.ui.equip": "Equip",
"BOL.ui.delete": "Delete", "BOL.ui.delete": "Delete",
"BOL.ui.roll" : "Roll", "BOL.ui.roll" : "Roll",
"BOL.ui.bonus": "Bonus",
"BOL.ui.log": "Log",
"BOL.ui.add": "Add",
"BOL.ui.rolls": "Rolls",
"BOL.ui.action": "Action",
"BOL.ui.unarmedAttack": "Unarmed attack",
"BOL.ui.vehicleType.boat": "Ship",
"BOL.ui.vehicleType.flyingboat": "Flying ship",
"BOL.ui.vehicleType.chariot": "Chariot/Cart",
"BOL.ui.vehicleType.other": "Other",
"BOL.ui.equipment" : "Equipment", "BOL.ui.equipment" : "Equipment",
"BOL.ui.equipmentProperties" : "Equipment properties", "BOL.ui.equipmentProperties" : "Equipment properties",
"BOL.ui.weaponAttack" : "Weapon attack", "BOL.ui.weaponAttack" : "Weapon attack",
@@ -187,10 +177,6 @@
"BOL.ui.armorInitMalus": "Armor Modifier (Init)", "BOL.ui.armorInitMalus": "Armor Modifier (Init)",
"BOL.ui.attackValue": "Attack Value", "BOL.ui.attackValue": "Attack Value",
"BOL.ui.attackModifier": "Attack Modifier", "BOL.ui.attackModifier": "Attack Modifier",
"BOL.ui.vehicleWeapons": "Vehicle weapons",
"BOL.ui.hullDamage": "Hull damage",
"BOL.ui.crewDamage": "Crew damage",
"BOL.ui.fireDamage": "Fire ?",
"BOL.ui.weaponbonus": "Cette arme bénéficie déja d'un Dé de Bonus (Arme Favorite prise en compte, par exemple)", "BOL.ui.weaponbonus": "Cette arme bénéficie déja d'un Dé de Bonus (Arme Favorite prise en compte, par exemple)",
"BOL.ui.initMalus": "Init malus", "BOL.ui.initMalus": "Init malus",
"BOL.ui.creature": "Creature", "BOL.ui.creature": "Creature",
@@ -209,7 +195,6 @@
"BOL.ui.deletetitle": "Delete", "BOL.ui.deletetitle": "Delete",
"BOL.ui.confirmdelete": "Are you sure to delete this item ?", "BOL.ui.confirmdelete": "Are you sure to delete this item ?",
"BOL.ui.nomorealchemypoints": "No more Creation Point !", "BOL.ui.nomorealchemypoints": "No more Creation Point !",
"BOL.ui.notenoughxp": "Not enough experience points !",
"BOL.ui.armornoformula": "The Armor {protect.name} has no protection formula !", "BOL.ui.armornoformula": "The Armor {protect.name} has no protection formula !",
"BOL.ui.selectactor": "Select the actors to apply the macro", "BOL.ui.selectactor": "Select the actors to apply the macro",
"BOL.ui.itemnotfound": "Impossible to find the object of this macro", "BOL.ui.itemnotfound": "Impossible to find the object of this macro",
@@ -381,18 +366,6 @@
"BOL.itemProperty.natural": "Natural weapon", "BOL.itemProperty.natural": "Natural weapon",
"BOL.itemProperty.onlymodifier": "Modifier only (ie creatures attacks)", "BOL.itemProperty.onlymodifier": "Modifier only (ie creatures attacks)",
"BOL.itemProperty.attackMalusDice": "Attack Malus (Dice)", "BOL.itemProperty.attackMalusDice": "Attack Malus (Dice)",
"BOL.itemProperty.crewDamageMultiplier": "Multiplier",
"BOL.itemProperty.crewdamage": "Crew damage value",
"BOL.itemProperty.hullDamageMultiplier": "Multiplier",
"BOL.itemProperty.hulldamage": "Hull damage value",
"BOL.itemProperty.isboarding": "Boarding",
"BOL.itemProperty.isbreakrow": "Break the oars",
"BOL.itemProperty.iscrewdamage": "Crew damage",
"BOL.itemProperty.isfiredamage": "Fire damage",
"BOL.itemProperty.ishulldamage": "Hull damage",
"BOL.itemProperty.isspur": "Ramming",
"BOL.itemProperty.magicalProperties": "Magical properties",
"BOL.itemProperty.vehicleDamageType": "Vehicle damage type",
"BOL.itemStat.quantity": "Quantity", "BOL.itemStat.quantity": "Quantity",
"BOL.itemStat.weight": "Weight", "BOL.itemStat.weight": "Weight",
@@ -471,23 +444,6 @@
"BOL.notification.MacroMultipleTokensSelected": "You have selected several tokens!", "BOL.notification.MacroMultipleTokensSelected": "You have selected several tokens!",
"BOL.notification.MacroNoActorAvailable": "No actor could be targeted!", "BOL.notification.MacroNoActorAvailable": "No actor could be targeted!",
"BOL.notification.MacroNoTokenSelected": "You must select a token!", "BOL.notification.MacroNoTokenSelected": "You must select a token!",
"BOL.notification.MacroNoItemFound": "{actor} : no {type} found.",
"BOL.notification.MacroSpecifyNameOrIndex": "Specify a name or an index : {names}",
"BOL.notification.MacroBadIndex": "{actor} : index {index} invalid for {type}.",
"BOL.notification.MacroItemNotFound": "{actor} : {type} \"{name}\" not found.",
"BOL.notification.MacroUnknownAttribute": "Unknown attribute : \"{key}\". Values : vigor, agility, mind, appeal",
"BOL.notification.MacroUnknownAptitude": "Unknown aptitude : \"{key}\". Values : init, melee, ranged, def",
"BOL.notification.MacroUnknownRollType": "Unknown roll type : \"{type}\". Valid types : attribute, aptitude, weapon, spell, alchemy, horoscope",
"BOL.notification.MacroNoPower": "Not enough Power points !",
"BOL.notification.FullDefense": "{actor} is in Full Defense ! He cannot attack this round.",
"BOL.notification.WeaponNotFound": "Unable to find weapon !",
"BOL.notification.AlchemyNotFound": "Unable to find Alchemy !",
"BOL.notification.SpellNotFound": "Unable to find this spell !",
"BOL.notification.NotEnoughCreationPrep": "Not enough Creation Points invested in the Preparation !",
"BOL.notification.NoPower": "Not enough Power points !",
"BOL.notification.AttackNotFound": "Unable to find the corresponding attack, combat tracking error.",
"BOL.notification.NoBirthHour": "{actor} has no birth hour, or it is incorrect : {hour}",
"BOL.notification.BadBirthHour": "{hour} does not correspond to a birth hour",
"BOL.size.tiny": "Tiny", "BOL.size.tiny": "Tiny",
"BOL.size.verysmall": "Very Small", "BOL.size.verysmall": "Very Small",
@@ -522,7 +478,6 @@
"BOL.chat.damageresult": "Damages of {name} : {total}", "BOL.chat.damageresult": "Damages of {name} : {total}",
"BOL.chat.damagetarget": "Target : {target}", "BOL.chat.damagetarget": "Target : {target}",
"BOL.chat.applydamagetotarget": "Apply damages to the target", "BOL.chat.applydamagetotarget": "Apply damages to the target",
"BOL.chat.selecttarget": "Choose a target:",
"BOL.chat.fightoption": "Combat options", "BOL.chat.fightoption": "Combat options",
"BOL.chat.reroll": "Reroll (1 HP)", "BOL.chat.reroll": "Reroll (1 HP)",
"BOL.chat.heroicreminder": "In addition of the actions below, you can : <ul><li>Carnage : Do a second free attack on the same opponent</li><li>Precise : 1 Malus Die on your opponent on a chosen location</li><li>Disarm</li><li>Rabble Massacre</li><li>Prone : Push your opponent on the ground (max +1 size)</li></ul>If you spent 1 Hero Point in addition, all these effects can be doubled.", "BOL.chat.heroicreminder": "In addition of the actions below, you can : <ul><li>Carnage : Do a second free attack on the same opponent</li><li>Precise : 1 Malus Die on your opponent on a chosen location</li><li>Disarm</li><li>Rabble Massacre</li><li>Prone : Push your opponent on the ground (max +1 size)</li></ul>If you spent 1 Hero Point in addition, all these effects can be doubled.",
@@ -589,8 +544,7 @@
"BOL.chat.criticalbuttonjournal": "Legendary/Heroic Success", "BOL.chat.criticalbuttonjournal": "Legendary/Heroic Success",
"BOL.chat.nodamage": "Do not apply damages", "BOL.chat.nodamage": "Do not apply damages",
"BOL.chat.armorRoll": "Armor roll", "BOL.chat.armorRoll": "Armor roll",
"BOL.chat.bolRulebookMessage": "Don't miss the premium Rulebook module (including Sagas), available on the Foundry website : <a href='https://foundryvtt.com/packages/bol-rulebook' target='_blank'>https://foundryvtt.com/packages/bol-rulebook</a>", "BOL.chat.bolRulebookMessage": "Don't miss the full Rulebook module (including Sagas) available at : https://www.ludospherik-editions.com/en_gb/ !",
"BOL.chat.bolChroniclesMessage": "Discover the premium Chronicles of Lemuria module (vol. 1), available on the Foundry website : <a href='https://foundryvtt.com/packages/bol-chronicles-1' target='_blank'>https://foundryvtt.com/packages/bol-chronicles-1</a>",
"BOL.dialog.soeasy": "So easy (+4)", "BOL.dialog.soeasy": "So easy (+4)",
"BOL.dialog.soeasy3": "So easy (+3)", "BOL.dialog.soeasy3": "So easy (+3)",
@@ -637,15 +591,6 @@
"BOL.chat.welcome4": "All support for this system is available on this Discord server : https://discord.gg/pPSDNJk", "BOL.chat.welcome4": "All support for this system is available on this Discord server : https://discord.gg/pPSDNJk",
"BOL.chat.welcome5": "<strong>In order to see compendiums in English, you must install and enable the Babele module.</strong>", "BOL.chat.welcome5": "<strong>In order to see compendiums in English, you must install and enable the Babele module.</strong>",
"BOL.chat.welcome6": "Good game in Lemuria !", "BOL.chat.welcome6": "Good game in Lemuria !",
"BOL.chat.msgonlygm": "Message only to the GM",
"BOL.chat.babelewarning": "⚠ WARNING ! English language selected, but Babele module is not installed !<br>Please install babele from the module tab in Foundry interface.",
"BOL.chat.applyrecup": "Recover during a few minutes (+{recupHP} Vitality)",
"BOL.chat.defenseReduceDamage2": "You can also spend 1 Heroism/Villainy Point to regain [[/r 2d6KH(1D6B)]] vitality points, this will cost you your next action.",
"BOL.chat.losshp": "{name} lost {lossHP} Vitality points. If he rests a few minutes, he can recover {recupHP} Vitality points.",
"BOL.chat.pcnotlinked": "The player character token is not linked to the actor",
"BOL.chat.pcnotlinkedmsg": "<b>WARNING</b> The player character token is not linked to the actor!",
"BOL.chat.pcwarning": "Warning! No character is linked to the player!",
"BOL.chat.pcwarningmsg": "<b>WARNING</b> The player is not linked to any character!",
"BOL.settings.rollArmor": "Roll for armor", "BOL.settings.rollArmor": "Roll for armor",
"BOL.settings.rollArmorTooltip": "Roll for armor value, fixed value if unchecked", "BOL.settings.rollArmorTooltip": "Roll for armor value, fixed value if unchecked",
@@ -664,20 +609,5 @@
"BOL.settings.defaultLogoActorSheetPath" : "Path for Actor sheet logo", "BOL.settings.defaultLogoActorSheetPath" : "Path for Actor sheet logo",
"BOL.settings.defaultLogoPathActorSheetTooltip": "Path of the Actor sheet logo (346 x 200, default : /systems/bol/ui/logo.webp)", "BOL.settings.defaultLogoPathActorSheetTooltip": "Path of the Actor sheet logo (346 x 200, default : /systems/bol/ui/logo.webp)",
"BOL.settings.defaultLogoTopLeftPath" : "Path for main top left logo", "BOL.settings.defaultLogoTopLeftPath" : "Path for main top left logo",
"BOL.settings.defaultLogoTopLeftPathTooltip": "Path of the logo in the top left window (718 x 416, default : /systems/bol/ui/logo2.webp)", "BOL.settings.defaultLogoTopLeftPathTooltip": "Path of the logo in the top left window (718 x 416, default : /systems/bol/ui/logo2.webp)"
"BOL.ui.charSummaryTitle": "Character Summary",
"BOL.ui.colGroupAttributes": "Attributes",
"BOL.ui.colGroupAptitudes": "Aptitudes",
"BOL.ui.colGroupResources": "Resources",
"BOL.ui.aggressive": "Aggressive spell?",
"BOL.ui.capacityProperties": "Capacity properties",
"BOL.ui.cost": "XP cost",
"BOL.ui.date": "Date",
"BOL.ui.heropoints": "Heroism/Villainy Points",
"BOL.ui.isbonusdice": "Provides a bonus die?",
"BOL.ui.ismalusdice": "Provides a penalty die?",
"BOL.ui.magicnewrules": "Additional rules (cf. fan-made supplement Sorcery!)",
"BOL.ui.value": "Value",
"BOL.ui.xplog": "XP Log"
} }
+1 -73
View File
@@ -77,16 +77,6 @@
"BOL.ui.equip": "Equipar", "BOL.ui.equip": "Equipar",
"BOL.ui.delete": "Borrar", "BOL.ui.delete": "Borrar",
"BOL.ui.roll" : "Tirar", "BOL.ui.roll" : "Tirar",
"BOL.ui.bonus": "Bonificación",
"BOL.ui.log": "Registro",
"BOL.ui.add": "Añadir",
"BOL.ui.rolls": "Tiradas",
"BOL.ui.action": "Acción",
"BOL.ui.unarmedAttack": "Ataque sin armas",
"BOL.ui.vehicleType.boat": "Barco",
"BOL.ui.vehicleType.flyingboat": "Nave Voladora",
"BOL.ui.vehicleType.chariot": "Carro",
"BOL.ui.vehicleType.other": "Otro",
"BOL.ui.equipment" : "Equipo", "BOL.ui.equipment" : "Equipo",
"BOL.ui.equipmentProperties" : "Propiedades Equipo", "BOL.ui.equipmentProperties" : "Propiedades Equipo",
"BOL.ui.weaponAttack" : "Ataque Arma", "BOL.ui.weaponAttack" : "Ataque Arma",
@@ -187,10 +177,6 @@
"BOL.ui.armorInitMalus": "Mod. Armadura (Inic)", "BOL.ui.armorInitMalus": "Mod. Armadura (Inic)",
"BOL.ui.attackValue": "Valor Ataque", "BOL.ui.attackValue": "Valor Ataque",
"BOL.ui.attackModifier": "Mod. Ataque", "BOL.ui.attackModifier": "Mod. Ataque",
"BOL.ui.vehicleWeapons": "Armas de vehículo",
"BOL.ui.hullDamage": "Daño casco",
"BOL.ui.crewDamage": "Daño tripulación",
"BOL.ui.fireDamage": "¿Fuego?",
"BOL.ui.weaponbonus": "Esta arma se beneficia de un Dado de ventaja (ej Arma Personal)", "BOL.ui.weaponbonus": "Esta arma se beneficia de un Dado de ventaja (ej Arma Personal)",
"BOL.ui.initMalus": "Desventaja Inic.", "BOL.ui.initMalus": "Desventaja Inic.",
"BOL.ui.creature": "Criatura", "BOL.ui.creature": "Criatura",
@@ -209,7 +195,6 @@
"BOL.ui.deletetitle": "Borrar", "BOL.ui.deletetitle": "Borrar",
"BOL.ui.confirmdelete": "¿Seguro que quieres borrar este objeto?", "BOL.ui.confirmdelete": "¿Seguro que quieres borrar este objeto?",
"BOL.ui.nomorealchemypoints": "¡Sin Puntos de Creación!", "BOL.ui.nomorealchemypoints": "¡Sin Puntos de Creación!",
"BOL.ui.notenoughxp": "¡No hay suficientes puntos de experiencia!",
"BOL.ui.armornoformula": "La Armadura {protect.name} no dispone de fórmula de protección!", "BOL.ui.armornoformula": "La Armadura {protect.name} no dispone de fórmula de protección!",
"BOL.ui.selectactor": "Selecciona un actora para aplicar la macro", "BOL.ui.selectactor": "Selecciona un actora para aplicar la macro",
"BOL.ui.itemnotfound": "Imposible encontrar el objeto de esta macro", "BOL.ui.itemnotfound": "Imposible encontrar el objeto de esta macro",
@@ -375,18 +360,6 @@
"BOL.itemProperty.natural": "Arma natural", "BOL.itemProperty.natural": "Arma natural",
"BOL.itemProperty.onlymodifier": "Sólo modificador (ej criatura)", "BOL.itemProperty.onlymodifier": "Sólo modificador (ej criatura)",
"BOL.itemProperty.attackMalusDice": "Dado Desventaja Ataque", "BOL.itemProperty.attackMalusDice": "Dado Desventaja Ataque",
"BOL.itemProperty.crewDamageMultiplier": "Multiplicador",
"BOL.itemProperty.crewdamage": "Valor del daño a la tripulación",
"BOL.itemProperty.hullDamageMultiplier": "Multiplicador",
"BOL.itemProperty.hulldamage": "Valor del daño al casco",
"BOL.itemProperty.isboarding": "Abordaje",
"BOL.itemProperty.isbreakrow": "Romper los remos",
"BOL.itemProperty.iscrewdamage": "Daño a la tripulación",
"BOL.itemProperty.isfiredamage": "Daño de fuego",
"BOL.itemProperty.ishulldamage": "Daño al casco",
"BOL.itemProperty.isspur": "Ariete",
"BOL.itemProperty.magicalProperties": "Propiedades mágicas",
"BOL.itemProperty.vehicleDamageType": "Tipo de daño de vehículo",
"BOL.itemStat.quantity": "Cantidad", "BOL.itemStat.quantity": "Cantidad",
"BOL.itemStat.weight": "Peso", "BOL.itemStat.weight": "Peso",
@@ -465,23 +438,6 @@
"BOL.notification.MacroMultipleTokensSelected": "¡Has seleccionado varios tokens!", "BOL.notification.MacroMultipleTokensSelected": "¡Has seleccionado varios tokens!",
"BOL.notification.MacroNoActorAvailable": "¡No se pudo marcar el actor como objetivo!", "BOL.notification.MacroNoActorAvailable": "¡No se pudo marcar el actor como objetivo!",
"BOL.notification.MacroNoTokenSelected": "¡Debes seleccionar un token!", "BOL.notification.MacroNoTokenSelected": "¡Debes seleccionar un token!",
"BOL.notification.MacroNoItemFound": "{actor} : no se encontró ningún {type}.",
"BOL.notification.MacroSpecifyNameOrIndex": "Especifica un nombre o un índice : {names}",
"BOL.notification.MacroBadIndex": "{actor} : índice {index} no válido para {type}.",
"BOL.notification.MacroItemNotFound": "{actor} : {type} \"{name}\" no encontrado.",
"BOL.notification.MacroUnknownAttribute": "Atributo desconocido : \"{key}\". Valores : vigor, agility, mind, appeal",
"BOL.notification.MacroUnknownAptitude": "Aptitud desconocida : \"{key}\". Valores : init, melee, ranged, def",
"BOL.notification.MacroUnknownRollType": "Tipo de tirada desconocido : \"{type}\". Tipos válidos : attribute, aptitude, weapon, spell, alchemy, horoscope",
"BOL.notification.MacroNoPower": "¡No quedan suficientes puntos de Poder!",
"BOL.notification.FullDefense": "{actor} está en Defensa Total ! ¡No puede atacar este asalto!",
"BOL.notification.WeaponNotFound": "¡No se pudo encontrar el arma!",
"BOL.notification.AlchemyNotFound": "¡No se pudo encontrar la alquimia!",
"BOL.notification.SpellNotFound": "¡No se pudo encontrar este hechizo!",
"BOL.notification.NotEnoughCreationPrep": "¡No hay suficientes puntos de Creación invertidos en la Preparación!",
"BOL.notification.NoPower": "¡No quedan suficientes puntos de Poder!",
"BOL.notification.AttackNotFound": "No se pudo encontrar el ataque correspondiente, error de seguimiento de combate.",
"BOL.notification.NoBirthHour": "{actor} no tiene hora de nacimiento, o es incorrecta : {hour}",
"BOL.notification.BadBirthHour": "{hour} no corresponde a una hora de nacimiento",
"BOL.size.tiny": "Minúsculo", "BOL.size.tiny": "Minúsculo",
"BOL.size.verysmall": "Muy pequeño", "BOL.size.verysmall": "Muy pequeño",
@@ -579,8 +535,7 @@
"BOL.chat.criticalinfo": "¡Esto es un éxito Asombroso o Legendario! Escoge tus opciones y efectos", "BOL.chat.criticalinfo": "¡Esto es un éxito Asombroso o Legendario! Escoge tus opciones y efectos",
"BOL.chat.criticalbuttonjournal": "Éxito Asombroso/Legendario", "BOL.chat.criticalbuttonjournal": "Éxito Asombroso/Legendario",
"BOL.chat.armorRoll": "Tirada de Armadura", "BOL.chat.armorRoll": "Tirada de Armadura",
"BOL.chat.bolRulebookMessage": "Don't miss the premium Rulebook module (including Sagas), available on the Foundry website : <a href='https://foundryvtt.com/packages/bol-rulebook' target='_blank'>https://foundryvtt.com/packages/bol-rulebook</a>", "BOL.chat.bolRulebookMessage": "Don't miss the full Rulebook module (including Sagas) available at : https://www.ludospherik-editions.com/en_gb/ !",
"BOL.chat.bolChroniclesMessage": "Discover the premium Chronicles of Lemuria module (vol. 1), available on the Foundry website : <a href='https://foundryvtt.com/packages/bol-chronicles-1' target='_blank'>https://foundryvtt.com/packages/bol-chronicles-1</a>",
"BOL.dialog.soeasy": "Demasiado fácil (+4)", "BOL.dialog.soeasy": "Demasiado fácil (+4)",
"BOL.dialog.veryeasy": "Muy fácil (+2)", "BOL.dialog.veryeasy": "Muy fácil (+2)",
@@ -614,20 +569,6 @@
"BOL.ui.biosigns": "Marcas", "BOL.ui.biosigns": "Marcas",
"BOL.ui.biodescription": "Descripción", "BOL.ui.biodescription": "Descripción",
"BOL.ui.bionotes": "Notas", "BOL.ui.bionotes": "Notas",
"BOL.ui.aggressive": "¿Hechizo agresivo?",
"BOL.ui.capacityProperties": "Propiedades de capacidad",
"BOL.ui.cost": "Coste XP",
"BOL.ui.date": "Fecha",
"BOL.ui.heropoints": "Puntos de Heroísmo/Villanía",
"BOL.ui.hordeAttack": "Ataque de horda",
"BOL.ui.hordeHP": "Vitalidad (1 miembro)",
"BOL.ui.hordeSize": "Tamaño de la horda",
"BOL.ui.hordehp": "Vitalidad (total)",
"BOL.ui.isbonusdice": "¿Proporciona un dado de bonificación?",
"BOL.ui.ismalusdice": "¿Proporciona un dado de penalización?",
"BOL.ui.magicnewrules": "Reglas adicionales (cf. suplemento hecho por fans ¡Hechicería!)",
"BOL.ui.value": "Valor",
"BOL.ui.xplog": "Registro de XP",
"BOL.chat.welcome1": "Bienvenidos a Bárbaros de Lemuria (Ludospherik)", "BOL.chat.welcome1": "Bienvenidos a Bárbaros de Lemuria (Ludospherik)",
"BOL.chat.welcome2": "Para jugar es necesario usar el manual, que lo podéis conseguir aquí: https://www.ludospherik-editions.com/en_gb/", "BOL.chat.welcome2": "Para jugar es necesario usar el manual, que lo podéis conseguir aquí: https://www.ludospherik-editions.com/en_gb/",
@@ -635,19 +576,6 @@
"BOL.chat.welcome4": "Todo el soporte para el sistema es a través del servidor Discord: https://discord.gg/pPSDNJk", "BOL.chat.welcome4": "Todo el soporte para el sistema es a través del servidor Discord: https://discord.gg/pPSDNJk",
"BOL.chat.welcome5": "¡Buena partida en Lemuria!", "BOL.chat.welcome5": "¡Buena partida en Lemuria!",
"BOL.chat.welcome6": "", "BOL.chat.welcome6": "",
"BOL.chat.msgonlygm": "Mensaje solo para el GM",
"BOL.chat.babelewarning": "⚠ ¡ADVERTENCIA! ¡El idioma inglés está seleccionado, pero el módulo Babele no está instalado!<br>Instale babele desde la pestaña de módulos en la interfaz de Foundry.",
"BOL.chat.applyrecup": "Recuperarse durante unos minutos (+{recupHP} Vitalidad)",
"BOL.chat.criticallegendaryinfo": "¡Es un éxito Legendario! ¡Elige tus opciones y efectos!",
"BOL.chat.defenseReduceDamage2": "También puedes gastar 1 Punto de Heroísmo/Villanía para recuperar [[/r 2d6KH(1D6B)]] puntos de vitalidad, esto te costará tu próxima acción.",
"BOL.chat.legendaryreminder": "Además de las acciones indicadas en los botones de abajo, puedes realizar 2 acciones entre: <ul><li>Carnicería: Atacar una segunda vez al mismo adversario</li><li>Golpe preciso: Un dado de penalización a tu adversario en una localización elegida</li><li>Desarme</li><li>Masacrar a la chusma</li><li>Derribo: Tira a tu adversario (1 tamaño más como máximo)</li></ul>",
"BOL.chat.losshp": "{name} ha perdido {lossHP} puntos de Vitalidad. Si descansa unos minutos, puede recuperar {recupHP} puntos de Vitalidad.",
"BOL.chat.pcnotlinked": "El token del personaje jugador no está vinculado al actor",
"BOL.chat.pcnotlinkedmsg": "<b>ATENCIÓN</b> ¡El token del personaje jugador no está vinculado al actor!",
"BOL.chat.pcwarning": "¡Atención! ¡Ningún personaje está vinculado al jugador!",
"BOL.chat.pcwarningmsg": "<b>ATENCIÓN</b> ¡El jugador no está vinculado a ningún personaje!",
"BOL.chat.rolldamage12Legend": "Daño +12",
"BOL.chat.selecttarget": "Elegir un objetivo:",
"BOL.settings.rollArmor": "Tiradas de armadura", "BOL.settings.rollArmor": "Tiradas de armadura",
"BOL.settings.rollArmorTooltip": "Tiradas de armadura, si no esta activo usará el valor fijo", "BOL.settings.rollArmorTooltip": "Tiradas de armadura, si no esta activo usará el valor fijo",
+2 -41
View File
@@ -78,16 +78,6 @@
"BOL.ui.equip": "Équiper", "BOL.ui.equip": "Équiper",
"BOL.ui.delete": "Supprimer", "BOL.ui.delete": "Supprimer",
"BOL.ui.roll": "Utiliser", "BOL.ui.roll": "Utiliser",
"BOL.ui.bonus": "Bonus",
"BOL.ui.log": "Journal",
"BOL.ui.add": "Ajouter",
"BOL.ui.rolls": "Jets",
"BOL.ui.action": "Action",
"BOL.ui.unarmedAttack": "Attaque à mains nues",
"BOL.ui.vehicleType.boat": "Navire",
"BOL.ui.vehicleType.flyingboat": "Nef Volante",
"BOL.ui.vehicleType.chariot": "Chariot/Carriole",
"BOL.ui.vehicleType.other": "Autre",
"BOL.ui.equipment": "Équipement", "BOL.ui.equipment": "Équipement",
"BOL.ui.equipmentProperties": "Propriétés d'équipement", "BOL.ui.equipmentProperties": "Propriétés d'équipement",
"BOL.ui.weaponAttack": "Attaque d'arme", "BOL.ui.weaponAttack": "Attaque d'arme",
@@ -239,7 +229,6 @@
"BOL.ui.deletetitle": "Suppression", "BOL.ui.deletetitle": "Suppression",
"BOL.ui.confirmdelete": "Vous êtes sûr de vouloir supprimer cet item ?", "BOL.ui.confirmdelete": "Vous êtes sûr de vouloir supprimer cet item ?",
"BOL.ui.nomorealchemypoints": "Plus assez de Points de Création !", "BOL.ui.nomorealchemypoints": "Plus assez de Points de Création !",
"BOL.ui.notenoughxp": "Pas assez de points d'expérience !",
"BOL.ui.armornoformula": "L'armure {protect.name} n'a pas de formule pour la protection !", "BOL.ui.armornoformula": "L'armure {protect.name} n'a pas de formule pour la protection !",
"BOL.ui.selectactor": "Selectionnez votre personnage pour utiliser la macro", "BOL.ui.selectactor": "Selectionnez votre personnage pour utiliser la macro",
"BOL.ui.itemnotfound": "Impossible de trouver l'objet de cette macro", "BOL.ui.itemnotfound": "Impossible de trouver l'objet de cette macro",
@@ -406,7 +395,6 @@
"BOL.itemProperty.isspur": "Eperonnage", "BOL.itemProperty.isspur": "Eperonnage",
"BOL.itemProperty.isbreakrow": "Briser les rames", "BOL.itemProperty.isbreakrow": "Briser les rames",
"BOL.itemProperty.attackMalusDice": "Malus d'attaque (Dés)", "BOL.itemProperty.attackMalusDice": "Malus d'attaque (Dés)",
"BOL.itemProperty.magicalProperties": "Propriétés magiques",
"BOL.itemStat.quantity": "Quantité", "BOL.itemStat.quantity": "Quantité",
"BOL.itemStat.weight": "Poids", "BOL.itemStat.weight": "Poids",
@@ -484,23 +472,6 @@
"BOL.notification.MacroMultipleTokensSelected": "Vous avez sélectionné plusieurs tokens", "BOL.notification.MacroMultipleTokensSelected": "Vous avez sélectionné plusieurs tokens",
"BOL.notification.MacroNoActorAvailable": "Aucun acteur n'a pu être ciblé", "BOL.notification.MacroNoActorAvailable": "Aucun acteur n'a pu être ciblé",
"BOL.notification.MacroNoTokenSelected": "Vous devez sélectionner un token", "BOL.notification.MacroNoTokenSelected": "Vous devez sélectionner un token",
"BOL.notification.MacroNoItemFound": "{actor} : aucun(e) {type} trouvé(e).",
"BOL.notification.MacroSpecifyNameOrIndex": "Précisez le nom ou l'index : {names}",
"BOL.notification.MacroBadIndex": "{actor} : index {index} invalide pour {type}.",
"BOL.notification.MacroItemNotFound": "{actor} : {type} \"{name}\" introuvable.",
"BOL.notification.MacroUnknownAttribute": "Attribut inconnu : \"{key}\". Valeurs : vigor, agility, mind, appeal",
"BOL.notification.MacroUnknownAptitude": "Aptitude inconnue : \"{key}\". Valeurs : init, melee, ranged, def",
"BOL.notification.MacroUnknownRollType": "Type de jet inconnu : \"{type}\". Types valides : attribute, aptitude, weapon, spell, alchemy, horoscope",
"BOL.notification.MacroNoPower": "Plus assez de points de Pouvoir !",
"BOL.notification.FullDefense": "{actor} est en Défense Totale ! Il ne peut pas attaquer ce round.",
"BOL.notification.WeaponNotFound": "Impossible de trouver l'arme !",
"BOL.notification.AlchemyNotFound": "Impossible de trouver l'alchimie !",
"BOL.notification.SpellNotFound": "Impossible de trouver ce sort !",
"BOL.notification.NotEnoughCreationPrep": "Pas assez de Points de Création investis dans la Préparation !",
"BOL.notification.NoPower": "Plus assez de points de Pouvoir !",
"BOL.notification.AttackNotFound": "Impossible de trouver l'attaque correspondante, erreur de suivi de combat.",
"BOL.notification.NoBirthHour": "{actor} n'a pas d'heure de naissance, ou elle est incorrecte : {hour}",
"BOL.notification.BadBirthHour": "{hour} ne correspond pas à une heure de naissance",
"BOL.size.tiny": "Minuscule", "BOL.size.tiny": "Minuscule",
"BOL.size.verysmall": "Très petite", "BOL.size.verysmall": "Très petite",
@@ -535,7 +506,6 @@
"BOL.chat.damageresult": "Dommages de {name} : {total}", "BOL.chat.damageresult": "Dommages de {name} : {total}",
"BOL.chat.damagetarget": "Cible : {target}", "BOL.chat.damagetarget": "Cible : {target}",
"BOL.chat.applydamagetotarget": "Appliquer les dommages à la cible", "BOL.chat.applydamagetotarget": "Appliquer les dommages à la cible",
"BOL.chat.selecttarget": "Choisir une cible :",
"BOL.chat.fightoption": "Option de combat", "BOL.chat.fightoption": "Option de combat",
"BOL.chat.reroll": "Relancer (1 P. Heroisme)", "BOL.chat.reroll": "Relancer (1 P. Heroisme)",
"BOL.chat.heroicreminder": "En plus des actions indiquées sur les boutons ci-dessous, vous pouvez : <ul><li>Carnage : Attaquer une seconde fois le même adversaire</li><li>Coup précis : Un dé de malus à votre adversaire sur une localisation choisie</li><li>Désarmement</li><li>Massacrer la piétaille</li><li>Renversement : Renversez votre adversaire (1 taille de plus max)</li></ul>Si vous dépensez un Point d'Héroisme en plus, tout ces effets peuvent être doublés", "BOL.chat.heroicreminder": "En plus des actions indiquées sur les boutons ci-dessous, vous pouvez : <ul><li>Carnage : Attaquer une seconde fois le même adversaire</li><li>Coup précis : Un dé de malus à votre adversaire sur une localisation choisie</li><li>Désarmement</li><li>Massacrer la piétaille</li><li>Renversement : Renversez votre adversaire (1 taille de plus max)</li></ul>Si vous dépensez un Point d'Héroisme en plus, tout ces effets peuvent être doublés",
@@ -600,8 +570,7 @@
"BOL.chat.criticalinfo": "C'est un succès Héroïque ! Choisissez vos options et effets !", "BOL.chat.criticalinfo": "C'est un succès Héroïque ! Choisissez vos options et effets !",
"BOL.chat.criticallegendaryinfo": "C'est un succès Légendaire ! Choisissez vos options et effets !", "BOL.chat.criticallegendaryinfo": "C'est un succès Légendaire ! Choisissez vos options et effets !",
"BOL.chat.criticalbuttonjournal": "Succès Héroïque/Légendaire", "BOL.chat.criticalbuttonjournal": "Succès Héroïque/Légendaire",
"BOL.chat.bolRulebookMessage": "N'oubliez pas le module premium du Livre de Règle et des Sagas, disponible sur le site Foundry : <a href='https://foundryvtt.com/packages/bol-rulebook' target='_blank'>https://foundryvtt.com/packages/bol-rulebook</a>", "BOL.chat.bolRulebookMessage": "N'oubliez pas le module complet du Livre de Règle et des Sagas disponible ici : https://www.ludospherik-editions.com !",
"BOL.chat.bolChroniclesMessage": "Découvrez le module premium des Chroniques de Lémurie (vol. 1), disponible sur le site Foundry : <a href='https://foundryvtt.com/packages/bol-chronicles-1' target='_blank'>https://foundryvtt.com/packages/bol-chronicles-1</a>",
"BOL.chat.losshp": "{name} a perdu {lossHP} points de Vitalité. Si il se repose quelques minutes, il peut récupérer {recupHP} points de Vitalité.", "BOL.chat.losshp": "{name} a perdu {lossHP} points de Vitalité. Si il se repose quelques minutes, il peut récupérer {recupHP} points de Vitalité.",
"BOL.chat.applyrecup": "Récupérer pendant quelques minutes (+{recupHP} Vitalité)", "BOL.chat.applyrecup": "Récupérer pendant quelques minutes (+{recupHP} Vitalité)",
"BOL.chat.inforecup": "{name} vient de récupérer {recupHP} points de Vitalité après quelques minutes de repos.", "BOL.chat.inforecup": "{name} vient de récupérer {recupHP} points de Vitalité après quelques minutes de repos.",
@@ -660,8 +629,6 @@
"BOL.chat.pcwarningmsg": "<b>ATTENTION</b> Le joueur n'est relié à aucun personnage !", "BOL.chat.pcwarningmsg": "<b>ATTENTION</b> Le joueur n'est relié à aucun personnage !",
"BOL.chat.pcnotlinked": "Le token du personnage joueur n'est pas relié à l'acteur", "BOL.chat.pcnotlinked": "Le token du personnage joueur n'est pas relié à l'acteur",
"BOL.chat.pcnotlinkedmsg": "<b>ATTENTION</b> Le token du personnage joueur n'est pas relié à l'acteur !", "BOL.chat.pcnotlinkedmsg": "<b>ATTENTION</b> Le token du personnage joueur n'est pas relié à l'acteur !",
"BOL.chat.msgonlygm": "Message réservé au MJ uniquement",
"BOL.chat.babelewarning": "⚠ ATTENTION ! La langue anglaise est sélectionnée, mais le module Babele n'est pas installé !<br>Veuillez installer babele depuis l'onglet des modules dans l'interface de Foundry.",
"BOL.settings.rollArmor": "Effectuer des jets pour les armures", "BOL.settings.rollArmor": "Effectuer des jets pour les armures",
"BOL.settings.rollArmorTooltip": "Effectue un jet de dés pour les armures (valeur fixe si désactivé)", "BOL.settings.rollArmorTooltip": "Effectue un jet de dés pour les armures (valeur fixe si désactivé)",
@@ -683,11 +650,5 @@
"BOL.settings.defaultLogoTopLeftPathTooltip": "Vous pouvez changer le logo BoL en haut à gauche de chaque écran (idéalement 718 x 416, défaut : /systems/bol/ui/logo2.webp)", "BOL.settings.defaultLogoTopLeftPathTooltip": "Vous pouvez changer le logo BoL en haut à gauche de chaque écran (idéalement 718 x 416, défaut : /systems/bol/ui/logo2.webp)",
"EFFECT.StatusProne": "A terre", "EFFECT.StatusProne": "A terre",
"EFFECT.StatusDead": "Mort", "EFFECT.StatusDead": "Mort"
"BOL.ui.charSummaryTitle": "Résumé des Personnages",
"BOL.ui.colGroupAttributes": "Attributs",
"BOL.ui.colGroupAptitudes": "Aptitudes",
"BOL.ui.colGroupResources": "Ressources",
"BOL.ui.heropoints": "Points d'Héroïsme/Vilainie"
} }
+33 -41
View File
@@ -7,8 +7,6 @@ import { BoLUtility } from "../system/bol-utility.js";
*/ */
export class BoLActor extends Actor { export class BoLActor extends Actor {
static _healthLock = new Set()
static async create(data, options) { static async create(data, options) {
// Case of compendium global import // Case of compendium global import
@@ -301,7 +299,7 @@ export class BoLActor extends Actor {
this.update({ [`system.attributes.${key}`]: attr, [`system.xp`]: xp }) this.update({ [`system.attributes.${key}`]: attr, [`system.xp`]: xp })
this.addXPLog("attribute", key, nextXP, attr.value) this.addXPLog("attribute", key, nextXP, attr.value)
} else { } else {
ui.notifications.warn(game.i18n.localize("BOL.ui.notenoughxp")) ui.notifications.warn("Pas assez de points d'expérience !")
} }
} }
} }
@@ -318,7 +316,7 @@ export class BoLActor extends Actor {
this.update({ [`system.aptitudes.${key}`]: apt, [`system.xp`]: xp }) this.update({ [`system.aptitudes.${key}`]: apt, [`system.xp`]: xp })
this.addXPLog("aptitude", key, nextXP, apt.value) this.addXPLog("aptitude", key, nextXP, apt.value)
} else { } else {
ui.notifications.warn(game.i18n.localize("BOL.ui.notenoughxp")) ui.notifications.warn("Pas assez de points d'expérience !")
} }
} }
} }
@@ -335,7 +333,7 @@ export class BoLActor extends Actor {
this.updateEmbeddedDocuments('Item', [{ _id: career._id, 'system.rank': career.system.rank + 1 }]) this.updateEmbeddedDocuments('Item', [{ _id: career._id, 'system.rank': career.system.rank + 1 }])
this.addXPLog("career", career.name, nextXP, career.system.rank + 1) this.addXPLog("career", career.name, nextXP, career.system.rank + 1)
} else { } else {
ui.notifications.warn(game.i18n.localize("BOL.ui.notenoughxp")) ui.notifications.warn("Pas assez de points d'expérience !")
} }
} }
} }
@@ -359,7 +357,7 @@ export class BoLActor extends Actor {
ChatMessage.create({ ChatMessage.create({
alias: this.name, alias: this.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name), whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/chat-activate-fight-option.hbs', { name: this.name, img: fightOption.img, foName: fightOption.name, state: state }) content: await renderTemplate('systems/bol/templates/chat/chat-activate-fight-option.hbs', { name: this.name, img: fightOption.img, foName: fightOption.name, state: state })
}) })
} }
@@ -854,42 +852,36 @@ export class BoLActor extends Actor {
/*-------------------------------------------- */ /*-------------------------------------------- */
async manageHealthState() { async manageHealthState() {
if (BoLActor._healthLock.has(this.id)) return let hpID = "lastHP" + this.id
BoLActor._healthLock.add(this.id) let lastHP = await this.getFlag("world", hpID)
try { if (lastHP != this.system.resources.hp.value && game.user.isGM) { // Only GM sends this
let hpID = "lastHP" + this.id await this.setFlag("world", hpID, this.system.resources.hp.value)
let lastHP = await this.getFlag("world", hpID) let prone = this.effects.find(ef => ef.name == game.i18n.localize("EFFECT.StatusProne"))
if (lastHP != this.system.resources.hp.value && game.user.isGM) { // Only GM sends this let dead = this.effects.find(ef => ef.name == game.i18n.localize("EFFECT.StatusDead"))
await this.setFlag("world", hpID, this.system.resources.hp.value) if (this.system.resources.hp.value <= 0) {
let prone = this.effects.find(ef => ef.name == game.i18n.localize("EFFECT.StatusProne")) if (!prone) {
let dead = this.effects.find(ef => ef.name == game.i18n.localize("EFFECT.StatusDead")) await this.createEmbeddedDocuments("ActiveEffect", [
if (this.system.resources.hp.value <= 0) { { name: game.i18n.localize('EFFECT.StatusProne'), icon: 'icons/svg/falling.svg', statuses: 'prone' }
if (!prone) { ])
await this.createEmbeddedDocuments("ActiveEffect", [ }
{ name: game.i18n.localize('EFFECT.StatusProne'), img: 'icons/svg/falling.svg', statuses: ['prone'] } if (this.system.resources.hp.value < -5 && !dead) {
]) await this.createEmbeddedDocuments("ActiveEffect", [
} { name: game.i18n.localize('EFFECT.StatusDead'), icon: 'icons/svg/skull.svg', statuses: 'dead' }
if (this.system.resources.hp.value < -5 && !dead) { ])
await this.createEmbeddedDocuments("ActiveEffect", [ }
{ name: game.i18n.localize('EFFECT.StatusDead'), img: 'icons/svg/skull.svg', statuses: ['dead'] } ChatMessage.create({
]) alias: this.name,
} whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
ChatMessage.create({ content: await renderTemplate('systems/bol/templates/chat/chat-vitality-zero.hbs', { name: this.name, img: this.img, hp: this.system.resources.hp.value, isHeroAdversary: this.isHeroAdversary() })
alias: this.name, })
whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name), } else {
content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/chat-vitality-zero.hbs', { name: this.name, img: this.img, hp: this.system.resources.hp.value, isHeroAdversary: this.isHeroAdversary() }) if (prone) {
}) await this.deleteEmbeddedDocuments("ActiveEffect", [prone.id])
} else { }
if (prone) { if (dead) {
await this.deleteEmbeddedDocuments("ActiveEffect", [prone.id]) await this.deleteEmbeddedDocuments("ActiveEffect", [dead.id])
}
if (dead) {
await this.deleteEmbeddedDocuments("ActiveEffect", [dead.id])
}
} }
} }
} finally {
BoLActor._healthLock.delete(this.id)
} }
} }
@@ -912,7 +904,7 @@ export class BoLActor extends Actor {
let msg = await ChatMessage.create({ let msg = await ChatMessage.create({
alias: this.name, alias: this.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name), whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/chat-recup-information.hbs', { content: await renderTemplate('systems/bol/templates/chat/chat-recup-information.hbs', {
name: this.name, name: this.name,
img: this.img, img: this.img,
actorId: this.id, actorId: this.id,
+2 -2
View File
@@ -84,8 +84,8 @@ export class BoLHordeSheet extends foundry.appv1.sheets.ActorSheet {
// Delete Inventory Item // Delete Inventory Item
html.find('.item-delete').click(ev => { html.find('.item-delete').click(ev => {
Dialog.confirm({ Dialog.confirm({
title: game.i18n.localize("BOL.ui.deletetitle"), title: "Suppression",
content: game.i18n.localize("BOL.ui.confirmdelete"), content: `Vous êtes sûr de vouloir supprimer cet item ?`,
yes: () => { yes: () => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
this.actor.deleteEmbeddedDocuments("Item", [li.data("itemId")]) this.actor.deleteEmbeddedDocuments("Item", [li.data("itemId")])
+2 -2
View File
@@ -94,8 +94,8 @@ export class BoLVehicleSheet extends foundry.appv1.sheets.ActorSheet {
// Delete Inventory Item // Delete Inventory Item
html.find('.item-delete').click(ev => { html.find('.item-delete').click(ev => {
Dialog.confirm({ Dialog.confirm({
title: game.i18n.localize("BOL.ui.deletetitle"), title: "Suppression",
content: game.i18n.localize("BOL.ui.confirmdelete"), content: `Vous êtes sûr de vouloir supprimer cet item ?`,
yes: () => { yes: () => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
this.actor.deleteEmbeddedDocuments("Item", [li.data("itemId")]) this.actor.deleteEmbeddedDocuments("Item", [li.data("itemId")])
-7
View File
@@ -1,7 +0,0 @@
export { default as BoLBaseItemSheet } from "./base-item-sheet.mjs"
export { default as BoLItemSheet } from "./item-sheet.mjs"
export { default as BoLFeatureSheet } from "./feature-sheet.mjs"
export { default as BoLBaseActorSheet } from "./base-actor-sheet.mjs"
export { default as BoLActorSheet } from "./actor-sheet.mjs"
export { default as BoLHordeSheet } from "./horde-sheet.mjs"
export { default as BoLVehicleSheet } from "./vehicle-sheet.mjs"
-144
View File
@@ -1,144 +0,0 @@
import BoLBaseActorSheet from "./base-actor-sheet.mjs"
import { BoLUtility } from "../../system/bol-utility.js"
/**
* Actor Sheet for BoL characters and encounters using AppV2
*/
export default class BoLActorSheet extends BoLBaseActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
actions: {
...super.DEFAULT_OPTIONS.actions,
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/bol/templates/actor/actor-sheet.hbs",
},
}
/** @override */
tabGroups = { primary: "stats" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
context.data = actor.toObject()
context.details = actor.details
context.attributes = actor.attributes
context.aptitudes = actor.aptitudes
context.resources = actor.getResourcesFromType()
context.xp = actor.system.xp
context.equipment = actor.equipment
context.equipmentCreature = actor.equipmentCreature
context.weapons = actor.weapons
context.protections = actor.protections
context.spells = actor.spells
context.alchemy = actor.alchemy
context.containers = actor.containers
context.treasure = actor.treasure
context.boleffects = actor.boleffects
context.alchemyrecipe = actor.alchemyrecipe
context.horoscopes = actor.horoscopes
context.vehicles = actor.vehicles
context.fightoptions = actor.fightoptions
context.ammos = actor.ammos
context.misc = actor.misc
context.xplog = actor.xplog
context.combat = actor.buildCombat()
context.initiativeRank = actor.getInitiativeRank()
context.features = actor.buildFeatures()
context.options = this.options
context.editScore = this.options.editScore
context.useBougette = (actor.type === "character" && BoLUtility.getUseBougette()) || false
context.bougette = actor.getBougette()
context.charType = actor.getCharType()
context.villainy = actor.getVillainy()
context.isUndead = actor.isUndead()
context.biography = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
actor.system.details?.biography || "", { async: true }
)
context.notes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
actor.system.details?.notes || "", { async: true }
)
context.isSorcerer = actor.isSorcerer()
context.isAlchemist = actor.isAlchemist()
context.isAstrologer = actor.isAstrologer()
context.isMysteries = context.isSorcerer || context.isAlchemist || context.isAstrologer
context.isPriest = actor.isPriest()
context.horoscopeGroupList = game.settings.get("bol", "horoscope-group")
console.log("ACTORDATA (AppV2)", context)
return context
}
/** @override */
_activateListeners() {
super._activateListeners()
if (!this.isEditable) return
// Create generic item
this.element.querySelectorAll(".create-item").forEach((el) => {
el.addEventListener("click", () => {
this.actor.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("BOL.ui.newEquipment"), type: "item" }], { renderSheet: true })
})
})
// Create natural weapon
this.element.querySelectorAll(".create-natural-weapon").forEach((el) => {
el.addEventListener("click", () => {
const system = foundry.utils.duplicate(game.bol.config.defaultNaturalWeapon)
this.actor.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("BOL.ui.newNaturalWeapon"), type: "item", system }], { renderSheet: true })
})
})
// Create natural protection
this.element.querySelectorAll(".create-natural-protection").forEach((el) => {
el.addEventListener("click", () => {
const system = foundry.utils.duplicate(game.bol.config.defaultNaturalProtection)
this.actor.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("BOL.ui.newNaturalProtection"), type: "item", system }], { renderSheet: true })
})
})
// Item create via header dataset (type-based creation)
this.element.querySelectorAll(".item-create").forEach((el) => {
el.addEventListener("click", (ev) => {
ev.preventDefault()
const header = ev.currentTarget
const type = header.dataset.type
const data = foundry.utils.duplicate(header.dataset)
const name = `New ${type}`
delete data.type
this.actor.createEmbeddedDocuments("Item", [{ name, type, data }])
})
})
// Add XP Log entry
this.element.querySelectorAll(".xplog-add").forEach((el) => {
el.addEventListener("click", (ev) => {
ev.preventDefault()
this.actor.addXPLog("other", "Nouveau", 0, 0)
})
})
// Add item by category/subtype (equipment tab headers)
this.element.querySelectorAll(".item-add").forEach((el) => {
el.addEventListener("click", (ev) => {
ev.preventDefault()
const { itemType, category, subtype } = ev.currentTarget.dataset
const system = { category, subtype }
this.actor.createEmbeddedDocuments("Item", [{
name: game.i18n.localize("BOL.ui.newEquipment"),
type: itemType || "item",
system,
}], { renderSheet: true })
})
})
}
}
@@ -1,309 +0,0 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
import { BoLRoll } from "../../controllers/bol-rolls.js"
import { BoLUtility } from "../../system/bol-utility.js"
/**
* Base Actor Sheet for BoL system using AppV2
* @extends {ActorSheetV2}
*/
export default class BoLBaseActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["bol", "sheet", "actor"],
position: {
width: 836,
height: 807,
},
form: {
submitOnChange: true,
closeOnSubmit: false,
},
window: {
resizable: true,
},
tabs: [
{
navSelector: "nav[data-group=\"primary\"]",
contentSelector: "section.sheet-body",
initial: "stats",
},
],
dragDrop: [{ dragSelector: ".items-list .item", dropSelector: null }],
actions: {},
}
/** Tab groups state */
tabGroups = { primary: "stats" }
/** @override */
async _prepareContext() {
const actor = this.document
return {
actor,
system: actor.system,
systemFields: actor.system.schema.fields,
config: game.bol.config,
isGM: game.user.isGM,
isEditable: this.isEditable,
owner: this.document.isOwner,
cssClass: this.options.classes.join(" "),
}
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
this._activateTabs()
this._activateListeners()
this._applyBackgroundImage()
this._activateImageEdit()
}
/**
* Apply background image to the actor form
* @private
*/
_applyBackgroundImage() {
const logoUrl = BoLUtility.getLogoActorSheet()
const form = this.element.querySelector(".bol-actor-form")
if (form) form.style.backgroundImage = `url(${logoUrl})`
}
/**
* Activate image editing via FilePicker
* @private
*/
_activateImageEdit() {
const img = this.element.querySelector('[data-edit="img"]')
if (img && this.isEditable) {
img.style.cursor = "pointer"
img.addEventListener("click", () => {
new FilePicker({
type: "image",
current: this.document.img,
callback: (path) => this.document.update({ img: path }),
}).browse()
})
}
}
/**
* Activate tab navigation
* @private
*/
_activateTabs() {
const nav = this.element.querySelector("nav[data-group]")
if (!nav) return
const group = nav.dataset.group
const activeTab = this.tabGroups[group] || "stats"
nav.querySelectorAll("[data-tab]").forEach((link) => {
const tab = link.dataset.tab
link.classList.toggle("active", tab === activeTab)
link.addEventListener("click", (event) => {
event.preventDefault()
this.tabGroups[group] = tab
nav.querySelectorAll("[data-tab]").forEach(l => l.classList.toggle("active", l.dataset.tab === tab))
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach(c => c.classList.toggle("active", c.dataset.tab === tab))
})
})
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach((content) => {
content.classList.toggle("active", content.dataset.tab === activeTab)
})
}
/**
* Activate event listeners (replaces activateListeners with vanilla DOM)
* @private
*/
_activateListeners() {
if (!this.isEditable) return
// Item edit
this.element.querySelectorAll(".item-edit").forEach((el) => {
el.addEventListener("click", (ev) => {
const li = ev.currentTarget.closest(".item")
const item = this.actor.items.get(li?.dataset.itemId)
item?.sheet.render(true)
})
})
// Item delete
this.element.querySelectorAll(".item-delete").forEach((el) => {
el.addEventListener("click", (ev) => {
const li = ev.currentTarget.closest(".item")
const itemId = li?.dataset.itemId
Dialog.confirm({
title: game.i18n.localize("BOL.ui.deletetitle"),
content: game.i18n.localize("BOL.ui.confirmdelete"),
yes: () => {
this.actor.deleteEmbeddedDocuments("Item", [itemId])
li?.remove()
},
no: () => {},
defaultYes: false,
})
})
})
// Item equip/unequip
this.element.querySelectorAll(".item-equip").forEach((el) => {
el.addEventListener("click", (ev) => {
const li = ev.currentTarget.closest(".item")
const item = this.actor.items.get(li?.dataset.itemId)
if (item) this.actor.toggleEquipItem(item)
})
})
// Toggle fight option
this.element.querySelectorAll(".toggle-fight-option").forEach((el) => {
el.addEventListener("click", (ev) => {
const li = ev.currentTarget.closest(".item")
this.actor.toggleFightOption(li?.dataset.itemId)
})
})
// Inc/dec alchemy points
this.element.querySelectorAll(".inc-dec-btns-alchemy").forEach((el) => {
el.addEventListener("click", (ev) => {
const li = ev.currentTarget.closest(".item")
this.actor.spendAlchemyPoint(li?.dataset.itemId, 1)
})
})
// Inc/dec resource buttons
this.element.querySelectorAll(".inc-dec-btns-resource").forEach((el) => {
el.addEventListener("click", (ev) => {
const dataset = ev.currentTarget.dataset
this.actor.incDecResources(dataset.target, parseInt(dataset.incr))
})
})
// Generic inc/dec buttons for item fields
this.element.querySelectorAll(".inc-dec-btns").forEach((el) => {
el.addEventListener("click", (ev) => {
const li = ev.currentTarget.closest(".item")
if (!li) return
const item = this.actor.items.get(li.dataset.itemId)
if (!item) return
const dataset = ev.currentTarget.dataset
const operator = dataset.operator
const target = dataset.target
const incr = parseInt(dataset.incr)
const min = parseInt(dataset.min)
const max = parseInt(dataset.max) || 10000
// eslint-disable-next-line no-eval
let value = eval("item." + target) || 0
if (operator === "minus") value = value >= min + incr ? value - incr : min
if (operator === "plus") value = value <= max - incr ? value + incr : max
item.update({ [target]: value })
})
})
// Rollable elements
this.element.querySelectorAll(".rollable").forEach((el) => {
el.addEventListener("click", (ev) => this._onRoll(ev))
})
}
/**
* Handle clickable rolls (replaces _onRoll with vanilla DOM)
* @param {Event} event
* @private
*/
_onRoll(event) {
event.preventDefault()
const element = event.currentTarget
const dataset = element.dataset
const rollType = dataset.rollType
const li = element.closest(".item")
const itemId = li?.dataset.itemId
switch (rollType) {
case "attribute":
BoLRoll.attributeCheck(this.actor, dataset.key, event)
break
case "aptitude":
BoLRoll.aptitudeCheck(this.actor, dataset.key, event)
break
case "weapon":
BoLRoll.weaponCheck(this.actor, event)
break
case "spell":
BoLRoll.spellCheck(this.actor, event)
break
case "alchemy":
BoLRoll.alchemyCheck(this.actor, event)
break
case "protection":
this.actor.rollProtection(itemId)
break
case "damage":
this.actor.rollWeaponDamage(itemId)
break
case "aptitudexp":
this.actor.incAptitudeXP(dataset.key)
break
case "attributexp":
this.actor.incAttributeXP(dataset.key)
break
case "careerxp":
this.actor.incCareerXP(itemId)
break
case "horoscope-minor":
BoLRoll.horoscopeCheck(this.actor, event, "minor")
break
case "horoscope-major":
BoLRoll.horoscopeCheck(this.actor, event, "major")
break
case "horoscope-major-group":
BoLRoll.horoscopeCheck(this.actor, event, "majorgroup")
break
case "bougette":
this.actor.rollBougette()
break
default:
break
}
}
// #region Drag-and-Drop
#createDragDropHandlers() {
return (this.options.dragDrop || []).map((dragDrop) =>
new foundry.applications.ux.DragDrop.implementation({
...dragDrop,
permissions: {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this),
},
callbacks: {
dragstart: this._onDragStart.bind(this),
dragover: this._onDragOver.bind(this),
drop: this._onDrop.bind(this),
},
})
)
}
_canDragStart(selector) {
return this.isEditable
}
_canDragDrop(selector) {
return this.isEditable
}
// #endregion
}
@@ -1,255 +0,0 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
/**
* Base Item Sheet for BoL system using AppV2
* @extends {ItemSheetV2}
*/
export default class BoLBaseItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["bol", "sheet", "item"],
position: {
width: 650,
height: 780,
},
form: {
submitOnChange: true,
},
window: {
resizable: true,
},
tabs: [
{
navSelector: 'nav[data-group="primary"]',
contentSelector: "section.sheet-body",
initial: "description",
},
],
actions: {
editImage: BoLBaseItemSheet.#onEditImage,
postItem: BoLBaseItemSheet.#onPostItem,
},
}
/**
* Tab groups state
* @type {object}
*/
tabGroups = { primary: "description" }
/** @override */
async _prepareContext() {
const context = {
// Document & system
fields: this.document.schema.fields,
systemFields: this.document.system.schema.fields,
item: this.document,
system: this.document.system,
source: this.document.toObject(),
// Enriched content
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
this.document.system.description,
{ async: true }
),
// Properties
category: this.document.system.category,
itemProperties: this.document.itemProperties,
// Config & permissions
config: game.bol.config,
isGM: game.user.isGM,
isEditable: this.isEditable,
// CSS classes for template
cssClass: this.options.classes.join(" "),
// Tab state
tabs: this._getTabs(),
activeTab: this.tabGroups.primary || "description"
}
// Add careers if item is on an actor
if (this.document.actor) {
context.careers = this.document.actor.careers
}
// Apply dynamic defaults based on item type
this._applyDynamicDefaults(context)
return context
}
/**
* Get tabs configuration
* @returns {object[]}
* @private
*/
_getTabs() {
return [
{ id: "description", label: "BOL.ui.tab.description", icon: "fa-solid fa-book" },
{ id: "properties", label: "BOL.ui.tab.details", icon: "fa-solid fa-cog" }
]
}
/**
* Apply dynamic defaults to context based on item type and category
* @param {object} context
* @private
*/
_applyDynamicDefaults(context) {
const itemData = context.item
if (itemData.type === "item") {
// Set default category
if (!itemData.system.category) {
itemData.system.category = "equipment"
}
// Handle equipment slot
if (itemData.system.category === "equipment" && itemData.system.properties.equipable) {
if (!itemData.system.properties.slot) {
itemData.system.properties.slot = "-"
}
}
// Handle spell conditions
if (itemData.system.category === 'spell') {
if (!itemData.system.properties.mandatoryconditions) {
itemData.system.properties.mandatoryconditions = []
}
if (!itemData.system.properties.optionnalconditions) {
itemData.system.properties.optionnalconditions = []
}
for (let i = 0; i < 4; i++) {
itemData.system.properties.mandatoryconditions[i] = itemData.system.properties.mandatoryconditions[i] ?? ""
}
for (let i = 0; i < 8; i++) {
itemData.system.properties.optionnalconditions[i] = itemData.system.properties.optionnalconditions[i] ?? ""
}
}
} else if (itemData.type === "feature") {
// Set default subtype/category
if (!itemData.system.subtype) {
itemData.system.category = "origin"
}
}
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
this._activateTabs()
this._activateListeners()
}
/**
* Activate tab navigation via CSS only (no re-render) to preserve unsaved form state.
* @private
*/
_activateTabs() {
const nav = this.element.querySelector('nav[data-group="primary"]')
if (!nav) return
const section = this.element.querySelector('section.sheet-body')
if (!section) return
const activeTab = this.tabGroups.primary || "description"
// Set initial active state
nav.querySelectorAll('[data-tab]').forEach(link => {
link.classList.toggle('active', link.dataset.tab === activeTab)
})
section.querySelectorAll('[data-tab]').forEach(content => {
content.classList.toggle('active', content.dataset.tab === activeTab)
})
// Tab click — CSS-only switch, no render()
nav.querySelectorAll('[data-tab]').forEach(link => {
link.addEventListener('click', (event) => {
event.preventDefault()
const tab = link.dataset.tab
this.tabGroups.primary = tab
nav.querySelectorAll('[data-tab]').forEach(l => l.classList.toggle('active', l.dataset.tab === tab))
section.querySelectorAll('[data-tab]').forEach(c => c.classList.toggle('active', c.dataset.tab === tab))
})
})
}
/**
* Activate custom listeners
* @private
*/
_activateListeners() {
if (!this.isEditable) return
// Armor quality change handler
const armorQuality = this.element.querySelector('.armorQuality')
if (armorQuality) {
armorQuality.addEventListener('change', (ev) => {
const value = ev.currentTarget.value
const soakFormula = this.element.querySelector('.soakFormula')
if (soakFormula && game.bol.config.soakFormulas[value]) {
soakFormula.value = game.bol.config.soakFormulas[value]
}
})
}
}
// #region Drag-and-Drop Workflow
/**
* Create drag-and-drop workflow handlers for this Application
* @returns {DragDrop[]}
* @private
*/
#createDragDropHandlers() {
return []
}
// #endregion
// #region Actions
/**
* Handle editing the item image
* @param {PointerEvent} event
* @param {HTMLElement} target
* @private
*/
static async #onEditImage(event, target) {
const fp = new FilePicker({
current: this.document.img,
type: "image",
callback: (path) => {
this.document.update({ img: path })
},
})
return fp.browse()
}
/**
* Handle posting the item to chat
* @param {PointerEvent} event
* @param {HTMLElement} target
* @private
*/
static async #onPostItem(event, target) {
const BoLUtility = (await import("../../system/bol-utility.js")).BoLUtility
let chatData = foundry.utils.duplicate(this.document)
if (this.document.actor) {
chatData.actor = { id: this.document.actor.id }
}
BoLUtility.postItem(chatData)
}
// #endregion
}
@@ -1,39 +0,0 @@
import BoLBaseItemSheet from "./base-item-sheet.mjs"
/**
* Item Sheet for "feature" type items (boons, careers, origins, etc.)
* @extends {BoLBaseItemSheet}
*/
export default class BoLFeatureSheet extends BoLBaseItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["bol", "sheet", "item", "item-type-feature"],
}
/** @override */
static PARTS = {
main: {
template: "systems/bol/templates/item/feature-sheet.hbs",
},
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
// Add feature-specific context
context.isFeature = true
context.isBoon = context.system.subtype === "boon"
context.isFlaw = context.system.subtype === "flaw"
context.isCareer = context.system.subtype === "career"
context.isOrigin = context.system.subtype === "origin"
context.isRace = context.system.subtype === "race"
context.isFightOption = context.system.subtype === "fightoption"
context.isEffect = context.system.subtype === "effect"
context.isHoroscope = context.system.subtype === "horoscope"
context.isXpLog = context.system.subtype === "xplog"
return context
}
}
@@ -1,65 +0,0 @@
import BoLBaseActorSheet from "./base-actor-sheet.mjs"
/**
* Actor Sheet for BoL hordes using AppV2
*/
export default class BoLHordeSheet extends BoLBaseActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
actions: {
...super.DEFAULT_OPTIONS.actions,
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/bol/templates/actor/horde-sheet.hbs",
},
}
/** @override */
tabGroups = { primary: "stats" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
context.options = this.options
context.editScore = this.options.editScore
context.description = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
actor.system.details?.biography || "", { async: true }
)
console.log("HORDE (AppV2)", context)
return context
}
/** @override */
_activateListeners() {
super._activateListeners()
if (!this.isEditable) return
// Item create via header dataset
this.element.querySelectorAll(".item-create").forEach((el) => {
el.addEventListener("click", (ev) => {
ev.preventDefault()
const header = ev.currentTarget
const type = header.dataset.type
const data = foundry.utils.duplicate(header.dataset)
delete data.type
this.actor.createEmbeddedDocuments("Item", [{ name: `New ${type}`, type, data }])
})
})
// Create generic item
this.element.querySelectorAll(".create_item").forEach((el) => {
el.addEventListener("click", () => {
this.actor.createEmbeddedDocuments("Item", [{ name: "Nouvel Equipement", type: "item" }], { renderSheet: true })
})
})
}
}
-38
View File
@@ -1,38 +0,0 @@
import BoLBaseItemSheet from "./base-item-sheet.mjs"
/**
* Item Sheet for "item" type items (equipment, weapons, etc.)
* @extends {BoLBaseItemSheet}
*/
export default class BoLItemSheet extends BoLBaseItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["bol", "sheet", "item", "item-type-item"],
}
/** @override */
static PARTS = {
main: {
template: "systems/bol/templates/item/item-sheet.hbs",
},
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
// Add item-specific context
context.isItem = true
context.isEquipment = context.category === "equipment"
context.isWeapon = context.category === "weapon"
context.isProtection = context.category === "protection"
context.isSpell = context.category === "spell"
context.isAlchemy = context.category === "alchemy"
context.isCapacity = context.category === "capacity"
context.isMagical = context.category === "magical"
context.isVehicle = context.category === "vehicle"
return context
}
}
@@ -1,70 +0,0 @@
import BoLBaseActorSheet from "./base-actor-sheet.mjs"
/**
* Actor Sheet for BoL vehicles using AppV2
*/
export default class BoLVehicleSheet extends BoLBaseActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
actions: {
...super.DEFAULT_OPTIONS.actions,
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/bol/templates/actor/vehicle-sheet.hbs",
},
}
/** @override */
tabGroups = { primary: "stats" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
context.weapons = actor.vehicleWeapons
context.options = this.options
context.editScore = this.options.editScore
context.description = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
actor.system.description || "", { async: true }
)
console.log("VEHICLE (AppV2)", context) // TODO: remove before release
return context
}
/** @override */
_activateListeners() {
super._activateListeners()
if (!this.isEditable) return
// Item create via header dataset
this.element.querySelectorAll(".item-create").forEach((el) => {
el.addEventListener("click", (ev) => {
ev.preventDefault()
const header = ev.currentTarget
const type = header.dataset.type
const data = foundry.utils.duplicate(header.dataset)
delete data.type
this.actor.createEmbeddedDocuments("Item", [{ name: `New ${type}`, type, data }])
})
})
// Create vehicle weapon
this.element.querySelectorAll(".vehicle-weapon-add").forEach((el) => {
el.addEventListener("click", () => {
this.actor.createEmbeddedDocuments("Item", [{
name: game.i18n.localize("BOL.ui.newEquipment"),
type: "item",
system: { category: "vehicleweapon" },
}], { renderSheet: true })
})
})
}
}
+58 -52
View File
@@ -1,9 +1,11 @@
/* -------------------------------------------- */ /* -------------------------------------------- */
// Import Modules // Import Modules
import { BoLActor } from "./actor/actor.js" import { BoLActor } from "./actor/actor.js"
// AppV1 actor sheets kept for reference only (AppV2 used via sheets.* below) import { BoLActorSheet } from "./actor/actor-sheet.js"
import { BoLVehicleSheet } from "./actor/vehicle-sheet.js"
import { BoLHordeSheet } from "./actor/horde-sheet.js"
import { BoLItem } from "./item/item.js" import { BoLItem } from "./item/item.js"
// Note: Old BoLItemSheet (AppV1) is now replaced by AppV2 sheets import { BoLItemSheet } from "./item/item-sheet.js"
import { System, BOL } from "./system/config.js" import { System, BOL } from "./system/config.js"
import { preloadHandlebarsTemplates } from "./system/templates.js" import { preloadHandlebarsTemplates } from "./system/templates.js"
import { registerHandlebarsHelpers } from "./system/helpers.js" import { registerHandlebarsHelpers } from "./system/helpers.js"
@@ -16,11 +18,8 @@ import { BoLHotbar } from "./system/bol-hotbar.js"
import { BoLCommands } from "./system/bol-commands.js" import { BoLCommands } from "./system/bol-commands.js"
import { BoLRoll } from "./controllers/bol-rolls.js" import { BoLRoll } from "./controllers/bol-rolls.js"
// Import DataModels /* -------------------------------------------- */
import * as models from "./models/_module.mjs" const BOL_WELCOME_MESSAGE_URL = "https://www.uberwald.me/gitea/public/bol/raw/branch/v13/welcome-message-bol.html"
// Import AppV2 Sheets
import * as sheets from "./applications/sheets/_module.mjs"
/* -------------------------------------------- */ /* -------------------------------------------- */
Hooks.once('init', async function () { Hooks.once('init', async function () {
@@ -32,9 +31,7 @@ Hooks.once('init', async function () {
BoLRoll, BoLRoll,
BoLUtility, BoLUtility,
macros: Macros, macros: Macros,
config: BOL, config: BOL
models,
sheets
}; };
// Game socket // Game socket
@@ -53,38 +50,17 @@ Hooks.once('init', async function () {
// Define custom Entity classes // Define custom Entity classes
CONFIG.Actor.documentClass = BoLActor; CONFIG.Actor.documentClass = BoLActor;
CONFIG.Actor.dataModels = {
character: models.BoLCharacter,
encounter: models.BoLEncounter,
horde: models.BoLHorde,
vehicle: models.BoLVehicle
}
CONFIG.Item.documentClass = BoLItem; CONFIG.Item.documentClass = BoLItem;
CONFIG.Item.dataModels = {
item: models.BoLItem,
feature: models.BoLFeature
}
CONFIG.Combat.documentClass = BoLCombatManager; CONFIG.Combat.documentClass = BoLCombatManager;
// Register sheet application classes // Register sheet application classes
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet); foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
foundry.documents.collections.Actors.registerSheet("bol", sheets.BoLActorSheet, { types: ["character", "encounter"], makeDefault: true }) foundry.documents.collections.Actors.registerSheet("bol", BoLActorSheet, { types: ["character", "encounter"], makeDefault: true })
foundry.documents.collections.Actors.registerSheet("bol", sheets.BoLVehicleSheet, { types: ["vehicle"], makeDefault: true }) foundry.documents.collections.Actors.registerSheet("bol", BoLVehicleSheet, { types: ["vehicle"], makeDefault: true })
foundry.documents.collections.Actors.registerSheet("bol", sheets.BoLHordeSheet, { types: ["horde"], makeDefault: true }) foundry.documents.collections.Actors.registerSheet("bol", BoLHordeSheet, { types: ["horde"], makeDefault: true })
// Register AppV2 Item Sheets
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet); foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
foundry.documents.collections.Items.registerSheet("bol", sheets.BoLItemSheet, { types: ["item"], makeDefault: true }); foundry.documents.collections.Items.registerSheet("bol", BoLItemSheet, { makeDefault: true });
foundry.documents.collections.Items.registerSheet("bol", sheets.BoLFeatureSheet, { types: ["feature"], makeDefault: true });
// Debug: Verify AppV2 sheets are loaded
console.log("BoL Item Sheets registered:", {
BoLItemSheet: sheets.BoLItemSheet.name,
BoLFeatureSheet: sheets.BoLFeatureSheet.name,
extendsApplicationV2: sheets.BoLItemSheet.prototype instanceof foundry.applications.api.ApplicationV2
});
// Inot useful stuff // Inot useful stuff
BoLUtility.init() BoLUtility.init()
@@ -101,33 +77,62 @@ Hooks.once('init', async function () {
// Register hooks // Register hooks
registerHooks() registerHooks()
}); if (typeof Babele !== 'undefined') {
Babele.get().setSystemTranslationsDir("compendiums");
}
/* -------------------------------------------- */
Hooks.once("babele.init", (babele) => {
console.log("Initializing Babele translations")
babele.setSystemTranslationsDir("compendiums");
}); });
/* -------------------------------------------- */ /* -------------------------------------------- */
async function welcomeMessage() { function welcomeMessage() {
const noRulebook = !game.modules.get("bol-rulebook")?.active let content = `<div id="welcome-message-bol"><span class="rdd-roll-part">
const noChronicles = !game.modules.get("bol-chronicles-1")?.active <strong>` + game.i18n.localize("BOL.chat.welcome1") + `</strong><p>` +
const content = await foundry.applications.handlebars.renderTemplate( game.i18n.localize("BOL.chat.welcome2") + "</p><p>" +
"systems/bol/templates/chat/chat-welcome.hbs", game.i18n.localize("BOL.chat.welcome3") + "</p><p>" +
{ noRulebook, noChronicles } game.i18n.localize("BOL.chat.welcome4") + "</p><p>" +
) game.i18n.localize("BOL.chat.welcome5") + "</p>" +
ChatMessage.create({ user: game.user.id, whisper: [game.user.id], content }) game.i18n.localize("BOL.chat.welcome6")
if (game.user.isGM && game.i18n.lang == 'en' && !game.modules.get("babele")?.active) { let rulebook = game.modules.find(m => m.id === "bol-rulebook")
const babeleWarning = game.i18n.localize("BOL.chat.babelewarning") if (!rulebook) {
content += "<p>" + game.i18n.localize("BOL.chat.bolRulebookMessage") + "</p>"
}
ChatMessage.create({
user: game.user.id,
whisper: [game.user.id],
content: content
})
if (game.user.isGM && game.i18n.lang == 'en' && !game.modules.find(m => m.id == "babele")) {
ChatMessage.create({ ChatMessage.create({
user: game.user.id, user: game.user.id,
whisper: [game.user.id], whisper: [game.user.id],
content: `<div class="bol-welcome-card"><div class="welcome-body"><p class="welcome-warning">${babeleWarning}</p></div></div>` content: `<div id="welcome-message-bol"><span class="rdd-roll-part">
<strong>WARNING ! English language selected, but Babele module is not installed !<br>Please install babele from the module tab in Foundry interface.`
}) })
ui.notifications.warn(babeleWarning) ui.notifications.warn("WARNING ! English language selected, but babele module is not installed !<br>Please install babele from the module tab in Foundry interface.")
}
}
/* -------------------------------------------- */
function welcomeMessageRepository() {
if (game.user.isGM) {
// Try to fetch the welcome message from the github repo "welcome-message-ecryme.html"
fetch(BOL_WELCOME_MESSAGE_URL)
.then(response => response.text())
.then(html => {
ChatMessage.create({
user: game.user.id,
whisper: [game.user.id],
content: html
});
})
.catch(error => {
console.error("Error fetching welcome message:", error);
});
} }
} }
@@ -144,6 +149,7 @@ Hooks.once('ready', async function () {
) )
welcomeMessage() welcomeMessage()
welcomeMessageRepository();
// User warning // User warning
if (!game.user.isGM && game.user.character == undefined) { if (!game.user.isGM && game.user.character == undefined) {
+95 -100
View File
@@ -5,6 +5,11 @@ const _apt2attr = { init: "mind", melee: "agility", ranged: "agility", def: "vig
/* -------------------------------------------- */ /* -------------------------------------------- */
export class BoLRoll { export class BoLRoll {
/* -------------------------------------------- */
static options() {
return { classes: ["bol", "dialog"], width: 480, height: 'fit-content' };
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static getDefaultAttribute(key) { static getDefaultAttribute(key) {
return _apt2attr[key] return _apt2attr[key]
@@ -124,7 +129,7 @@ export class BoLRoll {
rangeMsg = "BOL.chat.range6" rangeMsg = "BOL.chat.range6"
} }
ChatMessage.create({ ChatMessage.create({
content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/chat-info-range.hbs', { content: await renderTemplate('systems/bol/templates/chat/chat-info-range.hbs', {
weapon: weapon, weapon: weapon,
attackerName: _token.actor.name, attackerName: _token.actor.name,
defenderName: target.actor.name, defenderName: target.actor.name,
@@ -154,7 +159,7 @@ export class BoLRoll {
// Manage specific case // Manage specific case
let fightOption = actor.getActiveFightOption() let fightOption = actor.getActiveFightOption()
if (fightOption && fightOption.system.properties.fightoptiontype == "fulldefense") { if (fightOption && fightOption.system.properties.fightoptiontype == "fulldefense") {
ui.notifications.warn(game.i18n.format("BOL.notification.FullDefense", { actor: actor.name })) ui.notifications.warn(`{{actor.name}} est en Défense Totale ! Il ne peut pas attaquer ce round.`)
return return
} }
@@ -175,7 +180,7 @@ export class BoLRoll {
const li = $(event.currentTarget).parents(".item") const li = $(event.currentTarget).parents(".item")
let weapon = actor.items.get(li.data("item-id")) let weapon = actor.items.get(li.data("item-id"))
if (!weapon) { if (!weapon) {
ui.notifications.warn(game.i18n.localize("BOL.notification.WeaponNotFound")) ui.notifications.warn("Unable to find weapon !")
return return
} }
weapon = foundry.utils.duplicate(weapon) weapon = foundry.utils.duplicate(weapon)
@@ -187,18 +192,13 @@ export class BoLRoll {
const li = $(event.currentTarget).parents(".item"); const li = $(event.currentTarget).parents(".item");
let alchemy = actor.items.get(li.data("item-id")); let alchemy = actor.items.get(li.data("item-id"));
if (!alchemy) { if (!alchemy) {
ui.notifications.warn(game.i18n.localize("BOL.notification.AlchemyNotFound")); ui.notifications.warn("Unable to find Alchemy !");
return; return;
} }
alchemy = foundry.utils.duplicate(alchemy) alchemy = foundry.utils.duplicate(alchemy)
return this.alchemyCheckWithItem(actor, alchemy)
}
/* -------------------------------------------- */
static alchemyCheckWithItem(actor, alchemy) {
let alchemyData = alchemy.system let alchemyData = alchemy.system
if (alchemyData.properties.pccurrent < alchemyData.properties.pccost) { if (alchemyData.properties.pccurrent < alchemyData.properties.pccost) {
ui.notifications.warn(game.i18n.localize("BOL.notification.NotEnoughCreationPrep")) ui.notifications.warn("Pas assez de Points de Création investis dans la Préparation !")
return return
} }
@@ -260,13 +260,13 @@ export class BoLRoll {
/* -------------------------------------------- */ /* -------------------------------------------- */
static spellCheck(actor, event) { static spellCheck(actor, event) {
if (actor.system.resources.power.value <= 0) { if (actor.system.resources.power.value <= 0) {
ui.notifications.warn(game.i18n.localize("BOL.notification.NoPower")) ui.notifications.warn("Plus assez de points de Pouvoir !")
return return
} }
const li = $(event.currentTarget).parents(".item") const li = $(event.currentTarget).parents(".item")
let spell = actor.items.get(li.data("item-id")) let spell = actor.items.get(li.data("item-id"))
if (!spell) { if (!spell) {
ui.notifications.warn(game.i18n.localize("BOL.notification.SpellNotFound")) ui.notifications.warn("Impossible de trouver ce sort !")
return return
} }
spell = foundry.utils.duplicate(spell) spell = foundry.utils.duplicate(spell)
@@ -308,31 +308,26 @@ export class BoLRoll {
// Final number of dices // Final number of dices
this.rollData.nbDice = 2 + Math.abs(this.rollData.bmDice) this.rollData.nbDice = 2 + Math.abs(this.rollData.bmDice)
// Bonus or Malus ? // Bonus or Malus ?
const nbDiceEl = document.querySelector('#roll-nbdice') if (this.rollData.bmDice == 0) {
if (nbDiceEl) { $('#roll-nbdice').val("2")
if (this.rollData.bmDice == 0) { } else {
nbDiceEl.value = "2" let letter = (this.rollData.bmDice > 0) ? "B" : "M"
} else { $('#roll-nbdice').val("2 + " + String(Math.abs(this.rollData.bmDice)) + letter)
let letter = (this.rollData.bmDice > 0) ? "B" : "M"
nbDiceEl.value = "2 + " + String(Math.abs(this.rollData.bmDice)) + letter
}
} }
let rollbase = this.rollData.attrValue + "+" + this.rollData.aptValue let rollbase = this.rollData.attrValue + "+" + this.rollData.aptValue
if (this.rollData.weapon && this.rollData.weapon.system.properties.onlymodifier) { if (this.rollData.weapon && this.rollData.weapon.system.properties.onlymodifier) {
rollbase = "" rollbase = ""
} }
const modifierEl = document.querySelector('#roll-modifier') $('#roll-modifier').val(rollbase + "+" + this.rollData.careerBonus + "+" + this.rollData.mod + "+" +
if (modifierEl) modifierEl.value = rollbase + "+" + this.rollData.careerBonus + "+" + this.rollData.mod + "+" +
this.rollData.modRanged + "+" + this.rollData.weaponModifier + "-" + this.rollData.defence + "-" + this.rollData.modArmorMalus + "-" + this.rollData.modRanged + "+" + this.rollData.weaponModifier + "-" + this.rollData.defence + "-" + this.rollData.modArmorMalus + "-" +
this.rollData.shieldMalus + "+" + this.rollData.attackModifier + "+" + this.rollData.appliedArmorMalus + "+" + effectModifier this.rollData.shieldMalus + "+" + this.rollData.attackModifier + "+" + this.rollData.appliedArmorMalus + "+" + effectModifier)
// Rebuild list of applicable effects // Rebuild lits of applicable effects
let selectEffects = "" let selectEffects = ""
for (let effect of this.rollData.bolApplicableEffects) { for (let effect of this.rollData.bolApplicableEffects) {
selectEffects += `<option value="${effect.id}" selected>${effect.name}</option>` selectEffects += `<option value="${effect.id}" selected>${effect.name}</option>`
} }
const effectsEl = document.querySelector('#applicable-effects') $('#applicable-effects').html(selectEffects)
if (effectsEl) effectsEl.innerHTML = selectEffects
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -365,48 +360,46 @@ export class BoLRoll {
/* -------------------------------------------- */ /* -------------------------------------------- */
static updateArmorMalus(rollData) { static updateArmorMalus(rollData) {
rollData.appliedArmorMalus = 0 rollData.appliedArmorMalus = 0
const agiEl = document.querySelector('#armor-agi-malus')
if (rollData.attribute.key == "agility") { if (rollData.attribute.key == "agility") {
if (agiEl) agiEl.style.display = '' $("#armor-agi-malus").show()
rollData.appliedArmorMalus += rollData.armorAgiMalus rollData.appliedArmorMalus += rollData.armorAgiMalus
} else { } else {
if (agiEl) agiEl.style.display = 'none' $("#armor-agi-malus").hide()
} }
const initEl = document.querySelector('#armor-init-malus')
if (rollData.aptitude && rollData.aptitude.key == "init") { if (rollData.aptitude && rollData.aptitude.key == "init") {
if (initEl) initEl.style.display = '' $("#armor-init-malus").show()
rollData.appliedArmorMalus += rollData.armorInitMalus rollData.appliedArmorMalus += rollData.armorInitMalus
} else { } else {
if (initEl) initEl.style.display = 'none' $("#armor-init-malus").hide()
} }
} }
/* ------------------------------ -------------- */ /* ------------------------------ -------------- */
static updatePPCost(rollData) { static updatePPCost(rollData) {
const el = document.querySelector('#ppcost') $('#ppcost').html(rollData.ppCost + " + Armor(" + rollData.ppCostArmor + ")=" + Number(rollData.ppCost + rollData.ppCostArmor))
if (el) el.innerHTML = rollData.ppCost + " + Armor(" + rollData.ppCostArmor + ")=" + Number(rollData.ppCost + rollData.ppCostArmor)
} }
/* ------------------------------ -------------- */ /* ------------------------------ -------------- */
static rollDialogListener(html) { static rollDialogListener(html) {
this.updateTotalDice() this.updateTotalDice()
html.querySelector('#optcond')?.addEventListener('change', (event) => { html.find('#optcond').change((event) => { // Dynamic change of PP cost of spell
let pp = BoLUtility.computeSpellCost(this.rollData.spell, event.currentTarget.selectedOptions.length) let pp = BoLUtility.computeSpellCost(this.rollData.spell, event.currentTarget.selectedOptions.length)
this.rollData.ppCost = pp this.rollData.ppCost = pp
this.updatePPCost(this.rollData) this.updatePPCost(this.rollData)
}) })
html.querySelector('#mod')?.addEventListener('change', (event) => { html.find('#mod').change((event) => {
this.rollData.mod = Number(event.currentTarget.value) this.rollData.mod = Number(event.currentTarget.value)
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#modRanged')?.addEventListener('change', (event) => { html.find('#modRanged').change((event) => {
this.rollData.modRanged = Number(event.currentTarget.value) this.rollData.modRanged = Number(event.currentTarget.value)
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#attr')?.addEventListener('change', (event) => { html.find('#attr').change((event) => {
let attrKey = event.currentTarget.value let attrKey = event.currentTarget.value
let actor = BoLUtility.getActorFromRollData(this.rollData) let actor = BoLUtility.getActorFromRollData(this.rollData)
this.rollData.attribute = foundry.utils.duplicate(actor.system.attributes[attrKey]) this.rollData.attribute = foundry.utils.duplicate(actor.system.attributes[attrKey])
@@ -414,7 +407,7 @@ export class BoLRoll {
this.rollData.bolApplicableEffects = this.updateApplicableEffects(this.rollData) this.rollData.bolApplicableEffects = this.updateApplicableEffects(this.rollData)
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#apt')?.addEventListener('change', (event) => { html.find('#apt').change((event) => {
let aptKey = event.currentTarget.value let aptKey = event.currentTarget.value
let actor = BoLUtility.getActorFromRollData(this.rollData) let actor = BoLUtility.getActorFromRollData(this.rollData)
this.rollData.aptitude = foundry.utils.duplicate(actor.system.aptitudes[aptKey]) this.rollData.aptitude = foundry.utils.duplicate(actor.system.aptitudes[aptKey])
@@ -423,58 +416,65 @@ export class BoLRoll {
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#applyShieldMalus')?.addEventListener('click', (event) => { html.find('#applyShieldMalus').click((event) => {
this.rollData.shieldMalus = event.currentTarget.checked ? this.rollData.shieldAttackMalus : 0 if (event.currentTarget.checked) {
this.rollData.shieldMalus = this.rollData.shieldAttackMalus
} else {
this.rollData.shieldMalus = 0
}
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#career')?.addEventListener('change', (event) => { html.find('#career').change((event) => {
let careers = Array.from(event.currentTarget.selectedOptions).map(o => o.value) let careers = $('#career').val()
this.rollData.careerBonus = (!careers || careers.length == 0) ? 0 : Math.max(...careers.map(i => parseInt(i))) this.rollData.careerBonus = (!careers || careers.length == 0) ? 0 : Math.max(...careers.map(i => parseInt(i)))
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#boon')?.addEventListener('change', (event) => { html.find('#boon').change((event) => {
let boons = Array.from(event.currentTarget.selectedOptions).map(o => o.value) let boons = $('#boon').val()
this.rollData.nbBoons = (!boons || boons.length == 0) ? 0 : boons.length this.rollData.nbBoons = (!boons || boons.length == 0) ? 0 : boons.length
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#flaw')?.addEventListener('change', (event) => { html.find('#flaw').change((event) => {
let flaws = Array.from(event.currentTarget.selectedOptions).map(o => o.value) let flaws = $('#flaw').val()
this.rollData.nbFlaws = (!flaws || flaws.length == 0) ? 0 : flaws.length this.rollData.nbFlaws = (!flaws || flaws.length == 0) ? 0 : flaws.length
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelectorAll('.bdice').forEach(el => el.addEventListener('click', (event) => { html.find('.bdice').click((event) => {
this.rollData.mDice = 0 this.rollData.mDice = 0
this.rollData.bDice = Number(event.currentTarget.value) this.rollData.bDice = Number(event.currentTarget.value)
this.updateTotalDice() this.updateTotalDice()
})) })
html.querySelectorAll('.mdice').forEach(el => el.addEventListener('click', (event) => { html.find('.mdice').click((event) => {
this.rollData.bDice = 0 this.rollData.bDice = 0
this.rollData.mDice = Number(event.currentTarget.value) this.rollData.mDice = Number(event.currentTarget.value)
this.updateTotalDice() this.updateTotalDice()
})) })
html.querySelector('#horoscope-bonus-applied')?.addEventListener('change', (event) => { html.find('#horoscope-bonus-applied').change((event) => {
this.rollData.selectedHoroscope = [] this.rollData.selectedHoroscope = []
for (let option of event.currentTarget.selectedOptions) { for (let option of event.currentTarget.selectedOptions) {
this.rollData.selectedHoroscope.push(foundry.utils.duplicate(this.rollData.horoscopeBonusList[Number(option.index)])) this.rollData.selectedHoroscope.push(foundry.utils.duplicate(this.rollData.horoscopeBonusList[Number(option.index)]))
} }
let horoscopes = Array.from(event.currentTarget.selectedOptions).map(o => o.value) let horoscopes = $('#horoscope-bonus-applied').val()
this.rollData.horoscopeBonus = (!horoscopes || horoscopes.length == 0) ? 0 : horoscopes.length this.rollData.horoscopeBonus = (!horoscopes || horoscopes.length == 0) ? 0 : horoscopes.length
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#horoscope-malus-applied')?.addEventListener('change', (event) => {
html.find('#horoscope-malus-applied').change((event) => {
this.rollData.selectedHoroscope = [] this.rollData.selectedHoroscope = []
for (let option of event.currentTarget.selectedOptions) { for (let option of event.currentTarget.selectedOptions) {
this.rollData.selectedHoroscope.push(foundry.utils.duplicate(this.rollData.horoscopeBonusList[Number(option.index)])) this.rollData.selectedHoroscope.push(foundry.utils.duplicate(this.rollData.horoscopeBonusList[Number(option.index)]))
} }
let horoscopes = Array.from(event.currentTarget.selectedOptions).map(o => o.value) let horoscopes = $('#horoscope-malus-applied').val()
this.rollData.horoscopeMalus = (!horoscopes || horoscopes.length == 0) ? 0 : horoscopes.length this.rollData.horoscopeMalus = (!horoscopes || horoscopes.length == 0) ? 0 : horoscopes.length
this.updateTotalDice() this.updateTotalDice()
}) })
html.querySelector('#horoscope-group-applied')?.addEventListener('change', (event) => { html.find('#horoscope-group-applied').change((event) => {
this.rollData.selectedGroupHoroscopeIndex = event.currentTarget.value this.rollData.selectedGroupHoroscopeIndex = event.currentTarget.value
this.updateTotalDice() this.updateTotalDice()
}) })
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -552,47 +552,41 @@ export class BoLRoll {
} else { } else {
rollData.shieldMalus = 0 rollData.shieldMalus = 0
} }
// Save & pre-initialize computed fields // Save
this.rollData = rollData this.rollData = rollData
this.updateTotalDice()
console.log("ROLLDATA", rollData) console.log("ROLLDATA", rollData)
// Then display+process the dialog // Then display+process the dialog
const rollOptionContent = await foundry.applications.handlebars.renderTemplate(rollOptionTpl, rollData); const rollOptionContent = await foundry.applications.handlebars.renderTemplate(rollOptionTpl, rollData);
// Use Hooks to reliably get the rendered HTMLElement (renderDialogV2 receives (app, element, context)) let d = new Dialog({
Hooks.once('renderDialogV2', (app, element) => { title: rollData.label,
element.classList.add('bol');
this.rollDialogListener(element);
});
return foundry.applications.api.DialogV2.wait({
window: { title: rollData.label },
content: rollOptionContent, content: rollOptionContent,
rejectClose: false, rollData: rollData,
buttons: [ render: html => this.rollDialogListener(html),
{ buttons: {
type: 'button', cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize("BOL.ui.cancel"), label: game.i18n.localize("BOL.ui.cancel"),
icon: 'fas fa-times', callback: () => {
action: 'cancel' }
}, },
{ submit: {
type: 'submit', icon: '<i class="fas fa-check"></i>',
label: game.i18n.localize("BOL.ui.submit"), label: game.i18n.localize("BOL.ui.submit"),
icon: 'fas fa-check', callback: (html) => {
action: 'submit',
callback: (event, button, dialog) => {
console.log("Submit Roll!!!!"); console.log("Submit Roll!!!!");
if (rollData.mode == 'spell' && rollData.ppCurrent < rollData.ppCost) { if (rollData.mode == 'spell' && rollData.ppCurrent < rollData.ppCost) { // Check PP available
ui.notifications.warn(game.i18n.localize("BOL.notification.NoPower")) ui.notifications.warn("Pas assez de Points de Pouvoir !")
return false return
} }
rollData.registerInit = (rollData.aptitude && rollData.aptitude.key == 'init') ? rollData.registerInit = (rollData.aptitude && rollData.aptitude.key == 'init') ? $('#register-init').is(":checked") : false;
(dialog.element.querySelector('#register-init')?.checked ?? false) : false;
const isMalus = (rollData.bmDice < 0) const isMalus = (rollData.bmDice < 0)
let rollbase = rollData.attrValue + rollData.aptValue
if (rollData.weapon?.system.properties.onlymodifier) rollbase = 0
let rollbase = rollData.attrValue + rollData.aptValue
if (rollData.weapon?.system.properties.onlymodifier) {
rollbase = 0
}
let diceData = BoLUtility.getDiceData() let diceData = BoLUtility.getDiceData()
let malusInit = rollData.combatData?.malusInit || 0 let malusInit = rollData.combatData?.malusInit || 0
const modifiers = rollbase + rollData.careerBonus + rollData.mod + rollData.weaponModifier - rollData.defence - rollData.modArmorMalus + rollData.shieldMalus + rollData.attackModifier + rollData.appliedArmorMalus + rollData.effectModifier - malusInit const modifiers = rollbase + rollData.careerBonus + rollData.mod + rollData.weaponModifier - rollData.defence - rollData.modArmorMalus + rollData.shieldMalus + rollData.attackModifier + rollData.appliedArmorMalus + rollData.effectModifier - malusInit
@@ -605,8 +599,12 @@ export class BoLRoll {
r.roll(); r.roll();
} }
} }
] },
}, { classes: ['bol', 'dialog'], width: 480 }); default: onEnter,
close: () => { }
}, this.options());
return d.render(true);
} }
} }
@@ -700,15 +698,18 @@ export class BoLDefaultRoll {
/* -------------------------------------------- */ /* -------------------------------------------- */
async sendChatMessage() { async sendChatMessage() {
const actor = BoLUtility.getActorFromRollData(this.rollData) let actor = BoLUtility.getActorFromRollData(this.rollData)
const rollMode = game.settings.get("core", "rollMode") this._buildChatMessage(this.rollData).then(async msgFlavor => {
const msgFlavor = await this._buildChatMessage(this.rollData) //console.log("MSG", msgFlavor )
const msg = await this.rollData.roll.toMessage({ let msg = await this.rollData.roll.toMessage({
flavor: msgFlavor, user: game.user.id,
speaker: ChatMessage.getSpeaker({ actor: actor }), rollMode: game.settings.get("core", "rollMode"),
}, { rollMode }) flavor: msgFlavor,
this.rollData.roll = foundry.utils.duplicate(this.rollData.roll) speaker: ChatMessage.getSpeaker({ actor: actor }),
if (msg) await msg.setFlag("world", "bol-roll-data", this.rollData) })
this.rollData.roll = foundry.utils.duplicate(this.rollData.roll) // Remove object, keep data (v111 ready)
msg.setFlag("world", "bol-roll-data", this.rollData)
})
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -752,12 +753,6 @@ export class BoLDefaultRoll {
/* -------------------------------------------- */ /* -------------------------------------------- */
async sendDamageMessage() { async sendDamageMessage() {
let actor = BoLUtility.getActorFromRollData(this.rollData) let actor = BoLUtility.getActorFromRollData(this.rollData)
// If no target, collect potential targets from active scene (excluding attacker)
if (!this.rollData.targetId && game.scenes.current) {
this.rollData.potentialTargets = game.scenes.current.tokens
.filter(t => t.actor && t.actor.id !== this.rollData.actorId)
.map(t => ({ id: t.id, actorId: t.actor.id, name: t.name, img: t.texture?.src || t.actor.img }))
}
this._buildDamageChatMessage(this.rollData).then(async msgFlavor => { this._buildDamageChatMessage(this.rollData).then(async msgFlavor => {
let msg = await this.rollData.damageRoll.toMessage({ let msg = await this.rollData.damageRoll.toMessage({
user: game.user.id, user: game.user.id,
+119
View File
@@ -0,0 +1,119 @@
import { BoLUtility } from "../system/bol-utility.js";
/**
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
*/
export class BoLItemSheet extends foundry.appv1.sheets.ItemSheet {
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["bol", "sheet", "item"],
template: "systems/bol/templates/item/item-sheet.hbs",
width: 650,
height: 780,
dragDrop: [{ dragSelector: null, dropSelector: null }],
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }]
});
}
/* -------------------------------------------- */
/** @override */
async getData(options) {
const data = super.getData(options)
let itemData = foundry.utils.duplicate(data.document)
data.config = game.bol.config
data.item = itemData
data.category = itemData.system.category
data.isGM = game.user.isGM;
data.itemProperties = this.item.itemProperties;
data.description = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.description, { async: true })
if (data.document.actor) {
data.careers = data.document.actor.careers
}
// Dynamic default data fix/adapt
if (itemData.type == "item") {
if (!itemData.system.category) {
itemData.system.category = "equipment"
}
if (itemData.system.category == "equipment" && itemData.system.properties.equipable) {
if (!itemData.system.properties.slot) {
itemData.system.properties.slot = "-"
}
}
if (itemData.system.category == 'spell') {
if (!itemData.system.properties.mandatoryconditions) {
itemData.system.properties.mandatoryconditions = []
}
if (!itemData.system.properties.optionnalconditions) {
itemData.system.properties.optionnalconditions = []
}
for (let i = 0; i < 4; i++) {
itemData.system.properties.mandatoryconditions[i] = itemData.system.properties.mandatoryconditions[i] ?? ""
}
for (let i = 0; i < 8; i++) {
itemData.system.properties.optionnalconditions[i] = itemData.system.properties.optionnalconditions[i] ?? ""
}
}
} else {
if (!itemData.system.subtype) {
itemData.system.category = "origin"
}
}
console.log("ITEMDATA", data);
return data;
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
buttons.unshift({
class: "post",
icon: "fas fa-comment",
onclick: ev => this.postItem()
});
return buttons
}
/* -------------------------------------------- */
postItem() {
let chatData = foundry.utils.duplicate(this.item)
if (this.actor) {
chatData.actor = { id: this.actor.id };
}
BoLUtility.postItem(chatData);
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Roll handlers, click handlers, etc. would go here.
html.find('.armorQuality').change(ev => {
const li = $(ev.currentTarget);
console.log(game.bol.config.soakFormulas[li.val()]);
$('.soakFormula').val(game.bol.config.soakFormulas[li.val()]);
});
}
}
-6
View File
@@ -1,6 +0,0 @@
export { default as BoLCharacter } from "./character.mjs"
export { default as BoLEncounter } from "./encounter.mjs"
export { default as BoLHorde } from "./horde.mjs"
export { default as BoLVehicle } from "./vehicle.mjs"
export { default as BoLItem } from "./item.mjs"
export { default as BoLFeature } from "./feature.mjs"
-192
View File
@@ -1,192 +0,0 @@
/**
* Data model for Character actors
*/
export default class BoLCharacterDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
const requiredInteger = { required: true, nullable: false, integer: true };
return {
// Details
details: new fields.SchemaField({
biography: new fields.HTMLField({ initial: "" }),
notes: new fields.HTMLField({ initial: "" }),
height: new fields.StringField({ initial: "" }),
age: new fields.StringField({ initial: "" }),
weight: new fields.StringField({ initial: "" }),
hair: new fields.StringField({ initial: "" }),
eyes: new fields.StringField({ initial: "" }),
signs: new fields.StringField({ initial: "" }),
size: new fields.StringField({ initial: "" }),
languages: new fields.ArrayField(new fields.StringField(), { initial: [] }),
xplog: new fields.ArrayField(new fields.ObjectField(), { initial: [] })
}),
// Combat
combat: new fields.SchemaField({
lastinit: new fields.NumberField({ ...requiredInteger, initial: 0 }),
iscritical: new fields.BooleanField({ initial: false }),
isfumble: new fields.BooleanField({ initial: false }),
islegendary: new fields.BooleanField({ initial: false })
}),
// Character type
chartype: new fields.StringField({ initial: "player" }),
villainy: new fields.BooleanField({ initial: false }),
// Bougette
bougette: new fields.SchemaField({
state: new fields.StringField({ initial: "nomoney" }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
// XP
xp: new fields.SchemaField({
key: new fields.StringField({ initial: "xp" }),
label: new fields.StringField({ initial: "BOL.traits.xp" }),
total: new fields.NumberField({ ...requiredInteger, initial: 0 }),
spent: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
// Creation
creation: new fields.SchemaField({
key: new fields.StringField({ initial: "creation" }),
label: new fields.StringField({ initial: "BOL.resources.creation" }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
// Protection
prot: new fields.SchemaField({
key: new fields.StringField({ initial: "prot" }),
label: new fields.StringField({ initial: "BOL.aptitudes.prot" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
// Attributes
attributes: new fields.SchemaField({
vigor: new fields.SchemaField({
key: new fields.StringField({ initial: "vigor" }),
label: new fields.StringField({ initial: "BOL.attributes.vigor" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
agility: new fields.SchemaField({
key: new fields.StringField({ initial: "agility" }),
label: new fields.StringField({ initial: "BOL.attributes.agility" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
mind: new fields.SchemaField({
key: new fields.StringField({ initial: "mind" }),
label: new fields.StringField({ initial: "BOL.attributes.mind" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
appeal: new fields.SchemaField({
key: new fields.StringField({ initial: "appeal" }),
label: new fields.StringField({ initial: "BOL.attributes.appeal" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
}),
// Aptitudes
aptitudes: new fields.SchemaField({
init: new fields.SchemaField({
key: new fields.StringField({ initial: "init" }),
label: new fields.StringField({ initial: "BOL.aptitudes.init" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
melee: new fields.SchemaField({
key: new fields.StringField({ initial: "melee" }),
label: new fields.StringField({ initial: "BOL.aptitudes.melee" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
ranged: new fields.SchemaField({
key: new fields.StringField({ initial: "ranged" }),
label: new fields.StringField({ initial: "BOL.aptitudes.ranged" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
def: new fields.SchemaField({
key: new fields.StringField({ initial: "def" }),
label: new fields.StringField({ initial: "BOL.aptitudes.def" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
}),
// Resources
resources: new fields.SchemaField({
hp: new fields.SchemaField({
key: new fields.StringField({ initial: "hp" }),
label: new fields.StringField({ initial: "BOL.resources.hp" }),
ismain: new fields.BooleanField({ initial: true }),
base: new fields.NumberField({ ...requiredInteger, initial: 1 }),
value: new fields.NumberField({ ...requiredInteger, initial: 1 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 1 })
}),
hero: new fields.SchemaField({
key: new fields.StringField({ initial: "hero" }),
label: new fields.StringField({ initial: "BOL.resources.hero" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 5 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 })
}),
faith: new fields.SchemaField({
key: new fields.StringField({ initial: "faith" }),
label: new fields.StringField({ initial: "BOL.resources.faith" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
power: new fields.SchemaField({
key: new fields.StringField({ initial: "power" }),
label: new fields.StringField({ initial: "BOL.resources.power" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
alchemypoints: new fields.SchemaField({
key: new fields.StringField({ initial: "alchemypoints" }),
label: new fields.StringField({ initial: "BOL.resources.alchemypoints" }),
ismain: new fields.BooleanField({ initial: false }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
astrologypoints: new fields.SchemaField({
key: new fields.StringField({ initial: "astrologypoints" }),
label: new fields.StringField({ initial: "BOL.resources.astrologypoints" }),
ismain: new fields.BooleanField({ initial: false }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
})
};
}
static LOCALIZATION_PREFIXES = ["BOL.Character"];
}
-173
View File
@@ -1,173 +0,0 @@
/**
* Data model for Encounter actors
*/
export default class BoLEncounterDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
const requiredInteger = { required: true, nullable: false, integer: true };
return {
// Details
details: new fields.SchemaField({
biography: new fields.HTMLField({ initial: "" }),
notes: new fields.HTMLField({ initial: "" }),
height: new fields.StringField({ initial: "" }),
age: new fields.StringField({ initial: "" }),
weight: new fields.StringField({ initial: "" }),
hair: new fields.StringField({ initial: "" }),
eyes: new fields.StringField({ initial: "" }),
signs: new fields.StringField({ initial: "" }),
size: new fields.StringField({ initial: "" }),
languages: new fields.ArrayField(new fields.StringField(), { initial: [] }),
xplog: new fields.ArrayField(new fields.ObjectField(), { initial: [] })
}),
// Combat
combat: new fields.SchemaField({
lastinit: new fields.NumberField({ ...requiredInteger, initial: 0 }),
iscritical: new fields.BooleanField({ initial: false }),
isfumble: new fields.BooleanField({ initial: false }),
islegendary: new fields.BooleanField({ initial: false })
}),
// Character type
chartype: new fields.StringField({ initial: "tough" }),
isundead: new fields.BooleanField({ initial: false }),
villainy: new fields.BooleanField({ initial: false }),
size: new fields.StringField({ initial: "" }),
environment: new fields.StringField({ initial: "" }),
// Protection
prot: new fields.SchemaField({
key: new fields.StringField({ initial: "prot" }),
label: new fields.StringField({ initial: "BOL.aptitudes.prot" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
// Attributes
attributes: new fields.SchemaField({
vigor: new fields.SchemaField({
key: new fields.StringField({ initial: "vigor" }),
label: new fields.StringField({ initial: "BOL.attributes.vigor" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
agility: new fields.SchemaField({
key: new fields.StringField({ initial: "agility" }),
label: new fields.StringField({ initial: "BOL.attributes.agility" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
mind: new fields.SchemaField({
key: new fields.StringField({ initial: "mind" }),
label: new fields.StringField({ initial: "BOL.attributes.mind" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
appeal: new fields.SchemaField({
key: new fields.StringField({ initial: "appeal" }),
label: new fields.StringField({ initial: "BOL.attributes.appeal" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
}),
// Aptitudes
aptitudes: new fields.SchemaField({
init: new fields.SchemaField({
key: new fields.StringField({ initial: "init" }),
label: new fields.StringField({ initial: "BOL.aptitudes.init" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
melee: new fields.SchemaField({
key: new fields.StringField({ initial: "melee" }),
label: new fields.StringField({ initial: "BOL.aptitudes.melee" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
ranged: new fields.SchemaField({
key: new fields.StringField({ initial: "ranged" }),
label: new fields.StringField({ initial: "BOL.aptitudes.ranged" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
def: new fields.SchemaField({
key: new fields.StringField({ initial: "def" }),
label: new fields.StringField({ initial: "BOL.aptitudes.def" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
}),
// Resources
resources: new fields.SchemaField({
hp: new fields.SchemaField({
key: new fields.StringField({ initial: "hp" }),
label: new fields.StringField({ initial: "BOL.resources.hp" }),
ismain: new fields.BooleanField({ initial: true }),
base: new fields.NumberField({ ...requiredInteger, initial: 1 }),
value: new fields.NumberField({ ...requiredInteger, initial: 1 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 1 })
}),
hero: new fields.SchemaField({
key: new fields.StringField({ initial: "hero" }),
label: new fields.StringField({ initial: "BOL.resources.hero" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 5 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 })
}),
faith: new fields.SchemaField({
key: new fields.StringField({ initial: "faith" }),
label: new fields.StringField({ initial: "BOL.resources.faith" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
power: new fields.SchemaField({
key: new fields.StringField({ initial: "power" }),
label: new fields.StringField({ initial: "BOL.resources.power" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
alchemypoints: new fields.SchemaField({
key: new fields.StringField({ initial: "alchemypoints" }),
label: new fields.StringField({ initial: "BOL.resources.alchemypoints" }),
ismain: new fields.BooleanField({ initial: false }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
astrologypoints: new fields.SchemaField({
key: new fields.StringField({ initial: "astrologypoints" }),
label: new fields.StringField({ initial: "BOL.resources.astrologypoints" }),
ismain: new fields.BooleanField({ initial: false }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
})
};
}
static LOCALIZATION_PREFIXES = ["BOL.Encounter"];
}
-55
View File
@@ -1,55 +0,0 @@
/**
* Data model for Feature items
*/
export default class BoLFeatureDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
const requiredInteger = { required: true, nullable: false, integer: true };
const nullableNumber = { required: false, nullable: true, initial: null };
return {
// Base fields
category: new fields.StringField({ initial: "" }),
subtype: new fields.StringField({ initial: "default" }),
description: new fields.HTMLField({ initial: "" }),
properties: new fields.SchemaField({
// Career
sorcerer: new fields.BooleanField({ initial: false }),
alchemist: new fields.BooleanField({ initial: false }),
priest: new fields.BooleanField({ initial: false }),
astrologer: new fields.BooleanField({ initial: false }),
// Boon
isbonusdice: new fields.BooleanField({ initial: false }),
// Flaw
ismalusdice: new fields.BooleanField({ initial: false }),
// Fight option
fightoptiontype: new fields.StringField({ initial: "" }),
activated: new fields.BooleanField({ initial: false }),
isspecial: new fields.BooleanField({ initial: false }),
// Effect (boleffect)
identifier: new fields.StringField({ initial: "" }),
modifier: new fields.StringField({ initial: "" }),
// Horoscope
horoscopeanswer: new fields.StringField({ initial: "" }),
rank: new fields.NumberField({ ...nullableNumber }),
// XP log
xptype: new fields.StringField({ initial: "" }),
xpdate: new fields.StringField({ initial: "" }),
xpname: new fields.StringField({ initial: "" }),
xpcost: new fields.NumberField({ ...nullableNumber }),
xpvalue: new fields.NumberField({ ...nullableNumber }),
}),
// Feature-specific fields
rank: new fields.NumberField({ ...requiredInteger, initial: 0 })
};
}
static LOCALIZATION_PREFIXES = ["BOL.Feature"];
}
-174
View File
@@ -1,174 +0,0 @@
/**
* Data model for Horde actors
*/
export default class BoLHordeDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
const requiredInteger = { required: true, nullable: false, integer: true };
return {
// Details
details: new fields.SchemaField({
biography: new fields.HTMLField({ initial: "" }),
notes: new fields.HTMLField({ initial: "" }),
height: new fields.StringField({ initial: "" }),
age: new fields.StringField({ initial: "" }),
weight: new fields.StringField({ initial: "" }),
hair: new fields.StringField({ initial: "" }),
eyes: new fields.StringField({ initial: "" }),
signs: new fields.StringField({ initial: "" }),
size: new fields.StringField({ initial: "" }),
languages: new fields.ArrayField(new fields.StringField(), { initial: [] }),
xplog: new fields.ArrayField(new fields.ObjectField(), { initial: [] })
}),
// Combat
combat: new fields.SchemaField({
lastinit: new fields.NumberField({ ...requiredInteger, initial: 0 }),
iscritical: new fields.BooleanField({ initial: false }),
isfumble: new fields.BooleanField({ initial: false }),
islegendary: new fields.BooleanField({ initial: false })
}),
// Character type
chartype: new fields.StringField({ initial: "horde" }),
villainy: new fields.BooleanField({ initial: false }),
hordesize: new fields.NumberField({ ...requiredInteger, initial: 1 }),
hordebasehp: new fields.NumberField({ ...requiredInteger, initial: 1 }),
hasdamagerule: new fields.BooleanField({ initial: false }),
damagerule: new fields.StringField({ initial: "none" }),
// Protection
prot: new fields.SchemaField({
key: new fields.StringField({ initial: "prot" }),
label: new fields.StringField({ initial: "BOL.aptitudes.prot" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
// Attributes
attributes: new fields.SchemaField({
vigor: new fields.SchemaField({
key: new fields.StringField({ initial: "vigor" }),
label: new fields.StringField({ initial: "BOL.attributes.vigor" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
agility: new fields.SchemaField({
key: new fields.StringField({ initial: "agility" }),
label: new fields.StringField({ initial: "BOL.attributes.agility" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
mind: new fields.SchemaField({
key: new fields.StringField({ initial: "mind" }),
label: new fields.StringField({ initial: "BOL.attributes.mind" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: -1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
appeal: new fields.SchemaField({
key: new fields.StringField({ initial: "appeal" }),
label: new fields.StringField({ initial: "BOL.attributes.appeal" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
}),
// Aptitudes
aptitudes: new fields.SchemaField({
init: new fields.SchemaField({
key: new fields.StringField({ initial: "init" }),
label: new fields.StringField({ initial: "BOL.aptitudes.init" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
melee: new fields.SchemaField({
key: new fields.StringField({ initial: "melee" }),
label: new fields.StringField({ initial: "BOL.aptitudes.melee" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
ranged: new fields.SchemaField({
key: new fields.StringField({ initial: "ranged" }),
label: new fields.StringField({ initial: "BOL.aptitudes.ranged" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
def: new fields.SchemaField({
key: new fields.StringField({ initial: "def" }),
label: new fields.StringField({ initial: "BOL.aptitudes.def" }),
base: new fields.NumberField({ ...requiredInteger, initial: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
}),
// Resources
resources: new fields.SchemaField({
hp: new fields.SchemaField({
key: new fields.StringField({ initial: "hp" }),
label: new fields.StringField({ initial: "BOL.resources.hp" }),
ismain: new fields.BooleanField({ initial: true }),
base: new fields.NumberField({ ...requiredInteger, initial: 1 }),
value: new fields.NumberField({ ...requiredInteger, initial: 1 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 1 })
}),
hero: new fields.SchemaField({
key: new fields.StringField({ initial: "hero" }),
label: new fields.StringField({ initial: "BOL.resources.hero" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 5 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 })
}),
faith: new fields.SchemaField({
key: new fields.StringField({ initial: "faith" }),
label: new fields.StringField({ initial: "BOL.resources.faith" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
power: new fields.SchemaField({
key: new fields.StringField({ initial: "power" }),
label: new fields.StringField({ initial: "BOL.resources.power" }),
ismain: new fields.BooleanField({ initial: true }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
alchemypoints: new fields.SchemaField({
key: new fields.StringField({ initial: "alchemypoints" }),
label: new fields.StringField({ initial: "BOL.resources.alchemypoints" }),
ismain: new fields.BooleanField({ initial: false }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
}),
astrologypoints: new fields.SchemaField({
key: new fields.StringField({ initial: "astrologypoints" }),
label: new fields.StringField({ initial: "BOL.resources.astrologypoints" }),
ismain: new fields.BooleanField({ initial: false }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0 })
})
})
};
}
static LOCALIZATION_PREFIXES = ["BOL.Horde"];
}
-113
View File
@@ -1,113 +0,0 @@
/**
* Data model for Item items
*/
export default class BoLItemDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
const requiredInteger = { required: true, nullable: false, integer: true };
const nullableNumber = { required: false, nullable: true, initial: null };
return {
// Base fields
category: new fields.StringField({ initial: "" }),
subtype: new fields.StringField({ initial: "default" }),
description: new fields.HTMLField({ initial: "" }),
properties: new fields.SchemaField({
// Base flags
ranged: new fields.BooleanField({ initial: false }),
melee: new fields.BooleanField({ initial: false }),
spell: new fields.BooleanField({ initial: false }),
protection: new fields.BooleanField({ initial: false }),
weapon: new fields.BooleanField({ initial: false }),
armor: new fields.BooleanField({ initial: false }),
helm: new fields.BooleanField({ initial: false }),
shield: new fields.BooleanField({ initial: false }),
equipable: new fields.BooleanField({ initial: false }),
consumable: new fields.BooleanField({ initial: false }),
magical: new fields.BooleanField({ initial: false }),
"2H": new fields.BooleanField({ initial: false }),
reloadable: new fields.BooleanField({ initial: false }),
bow: new fields.BooleanField({ initial: false }),
crossbow: new fields.BooleanField({ initial: false }),
throwing: new fields.BooleanField({ initial: false }),
// Equipment
stackable: new fields.BooleanField({ initial: false }),
stacksize: new fields.NumberField({ ...nullableNumber }),
slot: new fields.StringField({ initial: "-" }),
// Weapon flags
natural: new fields.BooleanField({ initial: false }),
concealable: new fields.BooleanField({ initial: false }),
ignoreshield: new fields.BooleanField({ initial: false }),
attackBonusDice: new fields.BooleanField({ initial: false }),
attackMalusDice: new fields.BooleanField({ initial: false }),
onlymodifier: new fields.BooleanField({ initial: false }),
bashing: new fields.BooleanField({ initial: false }),
throwable: new fields.BooleanField({ initial: false }),
damageReroll1: new fields.BooleanField({ initial: false }),
// Weapon stats
attackAttribute: new fields.StringField({ initial: "vigor" }),
attackAptitude: new fields.StringField({ initial: "melee" }),
attackModifiers: new fields.NumberField({ ...nullableNumber }),
weaponSize: new fields.StringField({ initial: "unarmed" }),
damage: new fields.StringField({ initial: "0" }),
damageAttribute: new fields.StringField({ initial: "" }),
damageModifiers: new fields.NumberField({ ...nullableNumber }),
damageMultiplier: new fields.StringField({ initial: "1" }),
range: new fields.NumberField({ ...nullableNumber }),
reload: new fields.NumberField({ ...nullableNumber }),
// Protection
armorQuality: new fields.StringField({ initial: "" }),
soak: new fields.SchemaField({
formula: new fields.StringField({ initial: "" }),
value: new fields.NumberField({ initial: 0, nullable: true }),
modifier: new fields.NumberField({ initial: 0, nullable: true }),
}),
blocking: new fields.SchemaField({
malus: new fields.NumberField({ initial: 0, nullable: true }),
blocking1: new fields.BooleanField({ initial: false }),
blockingAll: new fields.BooleanField({ initial: false }),
}),
modifiers: new fields.SchemaField({
init: new fields.NumberField({ initial: 0, nullable: true }),
agility: new fields.NumberField({ initial: 0, nullable: true }),
powercost: new fields.NumberField({ initial: 0, nullable: true }),
social: new fields.BooleanField({ initial: false }),
}),
// Spell
circle: new fields.NumberField({ initial: 0, nullable: true }),
difficulty: new fields.StringField({ initial: "" }),
ppcost: new fields.NumberField({ initial: 0, nullable: true }),
duration: new fields.StringField({ initial: "" }),
nbmandatoryconditions: new fields.NumberField({ initial: 0, nullable: true }),
mandatoryconditions: new fields.ArrayField(new fields.StringField()),
optionnalconditions: new fields.ArrayField(new fields.StringField()),
// Alchemy
alchemytype: new fields.StringField({ initial: "" }),
pccost: new fields.NumberField({ initial: 0, nullable: true }),
pccurrent: new fields.NumberField({ initial: 0, nullable: true }),
// Vehicle weapon
isfiredamage: new fields.BooleanField({ initial: false }),
ishulldamage: new fields.BooleanField({ initial: false }),
iscrewdamage: new fields.BooleanField({ initial: false }),
isboarding: new fields.BooleanField({ initial: false }),
isspur: new fields.BooleanField({ initial: false }),
isbreakrow: new fields.BooleanField({ initial: false }),
}),
// Equipment fields
quantity: new fields.NumberField({ ...requiredInteger, initial: 1 }),
weight: new fields.NumberField({ initial: 0 }),
price: new fields.NumberField({ initial: 0 }),
worn: new fields.BooleanField({ initial: false })
};
}
static LOCALIZATION_PREFIXES = ["BOL.Item"];
}
-55
View File
@@ -1,55 +0,0 @@
/**
* Data model for Vehicle actors
*/
export default class BoLVehicleDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
const requiredInteger = { required: true, nullable: false, integer: true };
return {
vehicletype: new fields.StringField({ initial: "boat" }),
attributes: new fields.SchemaField({
hull: new fields.SchemaField({
key: new fields.StringField({ initial: "hull" }),
label: new fields.StringField({ initial: "BOL.attributes.hull" }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 })
}),
crew: new fields.SchemaField({
key: new fields.StringField({ initial: "crew" }),
label: new fields.StringField({ initial: "BOL.attributes.crew" }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 })
}),
resources: new fields.SchemaField({
key: new fields.StringField({ initial: "resources" }),
label: new fields.StringField({ initial: "BOL.attributes.resources" }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 })
})
}),
row: new fields.SchemaField({
key: new fields.StringField({ initial: "row" }),
label: new fields.StringField({ initial: "BOL.attributes.row" }),
value: new fields.NumberField({ ...requiredInteger, initial: 0 }),
min: new fields.NumberField({ ...requiredInteger, initial: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 5 })
}),
spur: new fields.SchemaField({
value: new fields.StringField({ initial: "" })
}),
status: new fields.SchemaField({}),
description: new fields.HTMLField({ initial: "" })
};
}
static LOCALIZATION_PREFIXES = ["BOL.Vehicle"];
}
+2 -2
View File
@@ -246,10 +246,10 @@ export class BoLCalendar extends Application {
} }
} }
else if (name) { else if (name) {
ui.notifications.warn(game.i18n.format("BOL.notification.NoBirthHour", { actor: name, hour: heureNaissance })); ui.notifications.warn(name + " n'a pas d'heure de naissance, ou elle est incorrecte : " + heureNaissance);
} }
else { else {
ui.notifications.warn(game.i18n.format("BOL.notification.BadBirthHour", { hour: heureNaissance })); ui.notifications.warn(heureNaissance + " ne correspond pas à une heure de naissance");
} }
return 0; return 0;
} }
+15 -39
View File
@@ -237,11 +237,6 @@ export class BoLUtility {
if (chatData.img.includes("/blank.png")) { if (chatData.img.includes("/blank.png")) {
chatData.img = null; chatData.img = null;
} }
// For old-format weapon items lacking stat fields, apply defaults so the chat card can display them
if (chatData.system?.properties?.weapon && !chatData.system.properties.damage) {
const defaults = game.bol.config.defaultNaturalWeapon?.properties ?? {};
chatData.system.properties = Object.assign(foundry.utils.duplicate(defaults), chatData.system.properties);
}
// JSON object for easy creation // JSON object for easy creation
chatData.jsondata = JSON.stringify( chatData.jsondata = JSON.stringify(
{ {
@@ -249,7 +244,7 @@ export class BoLUtility {
payload: chatData, payload: chatData,
}); });
foundry.applications.handlebars.renderTemplate('systems/bol/templates/item/post-item.hbs', chatData).then(html => { renderTemplate('systems/bol/templates/item/post-item.hbs', chatData).then(html => {
let chatOptions = BoLUtility.chatDataSetup(html); let chatOptions = BoLUtility.chatDataSetup(html);
ChatMessage.create(chatOptions) ChatMessage.create(chatOptions)
}); });
@@ -395,16 +390,6 @@ export class BoLUtility {
BoLUtility.sendAttackSuccess(rollData) BoLUtility.sendAttackSuccess(rollData)
}); });
html.on("click", '.chat-target-select', event => {
event.preventDefault()
const btn = event.currentTarget
let rollData = BoLUtility.getRollDataFromMessage(event)
rollData.targetId = btn.dataset.tokenId
rollData.defenderId = btn.dataset.actorId
BoLUtility.cleanupButtons(rollData.applyId)
BoLUtility.sendAttackSuccess(rollData)
});
html.on("click", '.chat-damage-roll', event => { html.on("click", '.chat-damage-roll', event => {
event.preventDefault() event.preventDefault()
let rollData = BoLUtility.getRollDataFromMessage(event) let rollData = BoLUtility.getRollDataFromMessage(event)
@@ -445,7 +430,7 @@ export class BoLUtility {
event.preventDefault() event.preventDefault()
let attr = event.currentTarget.attributes['data-attack-id'] let attr = event.currentTarget.attributes['data-attack-id']
if (!attr) { if (!attr) {
ui.notifications.warn(game.i18n.localize("BOL.notification.AttackNotFound")) ui.notifications.warn("Impossible de trouver l'attaque correspondante, erreur de suivi de combat.")
return return
} }
let attackId = event.currentTarget.attributes['data-attack-id'].value let attackId = event.currentTarget.attributes['data-attack-id'].value
@@ -497,21 +482,20 @@ export class BoLUtility {
if (defenseMode == 'damage-with-armor') { if (defenseMode == 'damage-with-armor') {
let armorFormula = defender.getArmorFormula() let armorFormula = defender.getArmorFormula()
if (armorFormula === "0") { rollData.armorProtect = 0
rollData.armorProtect = 0 armorFormula = String(armorFormula) || "0"
} else { if (armorFormula && armorFormula != "0" ) {
rollData.rollArmor = new Roll(armorFormula) rollData.rollArmor = new Roll(armorFormula)
await rollData.rollArmor.roll() await rollData.rollArmor.roll()
let msg = await rollData.rollArmor.toMessage({ flavor: game.i18n.localize("BOL.chat.armorRoll") + " : " + armorFormula }) let msg = await rollData.rollArmor.toMessage({ flavor: game.i18n.localize("BOL.chat.armorRoll") + " : " + armorFormula });
if (game.dice3d && msg) { if (game.dice3d) { // wait animation end when DsN is there
await game.dice3d.waitFor3DAnimationByMessageID(msg.id) await game.dice3d.waitFor3DAnimationByMessageID(msg.id);
} }
rollData.armorProtect = (rollData.rollArmor.total < 0) ? 0 : rollData.rollArmor.total rollData.armorProtect = (rollData.rollArmor.total < 0) ? 0 : rollData.rollArmor.total
} }
rollData.finalDamage = rollData.damageTotal - rollData.armorProtect rollData.finalDamage = rollData.damageTotal - rollData.armorProtect
rollData.finalDamage = (rollData.finalDamage < 0) ? 0 : rollData.finalDamage rollData.finalDamage = (rollData.finalDamage < 0) ? 0 : rollData.finalDamage
await defender.sufferDamage(rollData.finalDamage) await defender.sufferDamage(rollData.finalDamage)
console.log("Armor roll -> result ", rollData)
} }
if (defenseMode == 'damage-without-armor') { if (defenseMode == 'damage-without-armor') {
rollData.finalDamage = rollData.damageTotal rollData.finalDamage = rollData.damageTotal
@@ -519,17 +503,9 @@ export class BoLUtility {
} }
if (defenseMode == 'hero-reduce-damage') { if (defenseMode == 'hero-reduce-damage') {
let armorFormula = defender.getArmorFormula() let armorFormula = defender.getArmorFormula()
if (armorFormula === "0") { rollData.rollArmor = new Roll(armorFormula)
rollData.armorProtect = 0 await rollData.rollArmor.roll()
} else { rollData.armorProtect = (rollData.rollArmor.total < 0) ? 0 : rollData.rollArmor.total
rollData.rollArmor = new Roll(armorFormula)
await rollData.rollArmor.roll()
let msg = await rollData.rollArmor.toMessage({ flavor: game.i18n.localize("BOL.chat.armorRoll") + " : " + armorFormula })
if (game.dice3d && msg) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
}
rollData.armorProtect = (rollData.rollArmor.total < 0) ? 0 : rollData.rollArmor.total
}
rollData.rollHero = new Roll("1d6") rollData.rollHero = new Roll("1d6")
await rollData.rollHero.roll() await rollData.rollHero.roll()
rollData.finalDamage = rollData.damageTotal - rollData.rollHero.total - rollData.armorProtect rollData.finalDamage = rollData.damageTotal - rollData.rollHero.total - rollData.armorProtect
@@ -564,13 +540,13 @@ export class BoLUtility {
ChatMessage.create({ ChatMessage.create({
alias: defender.name, alias: defender.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(defender.name), whisper: BoLUtility.getWhisperRecipientsAndGMs(defender.name),
content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/rolls/defense-result-card.hbs', damageResults) content: await renderTemplate('systems/bol/templates/chat/rolls/defense-result-card.hbs', damageResults)
}) })
console.log("Defender data : ", defenderUser) console.log("Defender data : ", defenderUser)
ChatMessage.create({ ChatMessage.create({
alias: defender.name, alias: defender.name,
whisper: BoLUtility.getOtherWhisperRecipients(defenderUser?.name), whisper: BoLUtility.getOtherWhisperRecipients(defenderUser?.name),
content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/rolls/defense-summary-card.hbs', damageResults) content: await renderTemplate('systems/bol/templates/chat/rolls/defense-summary-card.hbs', damageResults)
}) })
} }
} }
@@ -583,7 +559,7 @@ export class BoLUtility {
this.blindMessageToGM(chatOptions); this.blindMessageToGM(chatOptions);
chatOptions.whisper = [game.user.id]; chatOptions.whisper = [game.user.id];
chatOptions.content = game.i18n.localize("BOL.chat.msgonlygm"); chatOptions.content = "Message only to the GM";
} }
else { else {
chatOptions.whisper = this.getUsers(user => user.isGM); chatOptions.whisper = this.getUsers(user => user.isGM);
@@ -657,7 +633,7 @@ export class BoLUtility {
let msg = await ChatMessage.create({ let msg = await ChatMessage.create({
alias: defender.name, alias: defender.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(defender.name), whisper: BoLUtility.getWhisperRecipientsAndGMs(defender.name),
content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/rolls/defense-request-card.hbs', { content: await renderTemplate('systems/bol/templates/chat/rolls/defense-request-card.hbs', {
attackId: rollData.id, attackId: rollData.id,
attacker: rollData.attacker, attacker: rollData.attacker,
defender: defender, defender: defender,
+61 -61
View File
@@ -188,25 +188,25 @@ BOL.rangeModifiers = {
"-8": "BOL.dialog.utmost" "-8": "BOL.dialog.utmost"
} }
BOL.difficultyModifiers = [ BOL.difficultyModifiers = {
{ value: "-12", label: "BOL.dialog.divine" }, "4": "BOL.dialog.soeasy",
{ value: "-11", label: "BOL.dialog.mythic11" }, "3": "BOL.dialog.soeasy3",
{ value: "-10", label: "BOL.dialog.mythic" }, "2": "BOL.dialog.veryeasy",
{ value: "-9", label: "BOL.dialog.heroic9" }, "1": "BOL.dialog.easy",
{ value: "-8", label: "BOL.dialog.heroic" }, "0": "BOL.dialog.moderate",
{ value: "-7", label: "BOL.dialog.formidable7" }, "-1": "BOL.dialog.hard",
{ value: "-6", label: "BOL.dialog.formidable" }, "-2": "BOL.dialog.tough",
{ value: "-5", label: "BOL.dialog.demanding5" }, "-3": "BOL.dialog.tough3",
{ value: "-4", label: "BOL.dialog.demanding" }, "-4": "BOL.dialog.demanding",
{ value: "-3", label: "BOL.dialog.tough3" }, "-5": "BOL.dialog.demanding5",
{ value: "-2", label: "BOL.dialog.tough" }, "-6": "BOL.dialog.formidable",
{ value: "-1", label: "BOL.dialog.hard" }, "-7": "BOL.dialog.formidable7",
{ value: "0", label: "BOL.dialog.moderate" }, "-8": "BOL.dialog.heroic",
{ value: "1", label: "BOL.dialog.easy" }, "-9": "BOL.dialog.heroic9",
{ value: "2", label: "BOL.dialog.veryeasy" }, "-10": "BOL.dialog.mythic",
{ value: "3", label: "BOL.dialog.soeasy3" }, "-11": "BOL.dialog.mythic11",
{ value: "4", label: "BOL.dialog.soeasy" }, "-12": "BOL.dialog.divine"
] }
BOL.alchemyModifiers = { BOL.alchemyModifiers = {
"2": "BOL.dialog.veryeasy", "2": "BOL.dialog.veryeasy",
@@ -413,108 +413,108 @@ BOL.bolEffectModifier = {
BOL.statusEffects = [ BOL.statusEffects = [
{ {
"id": "dead", "id": "dead",
"name": "EFFECT.StatusDead", "label": "EFFECT.StatusDead",
"img": "icons/svg/skull.svg" "icon": "icons/svg/skull.svg"
}, },
{ {
"id": "unconscious", "id": "unconscious",
"name": "EFFECT.StatusUnconscious", "label": "EFFECT.StatusUnconscious",
"img": "icons/svg/unconscious.svg" "icon": "icons/svg/unconscious.svg"
}, },
{ {
"id": "sleep", "id": "sleep",
"name": "EFFECT.StatusAsleep", "label": "EFFECT.StatusAsleep",
"img": "icons/svg/sleep.svg" "icon": "icons/svg/sleep.svg"
}, },
{ {
"id": "stun", "id": "stun",
"name": "EFFECT.StatusStunned", "label": "EFFECT.StatusStunned",
"img": "icons/svg/daze.svg" "icon": "icons/svg/daze.svg"
}, },
{ {
"id": "prone", "id": "prone",
"name": "EFFECT.StatusProne", "label": "EFFECT.StatusProne",
"img": "icons/svg/falling.svg" "icon": "icons/svg/falling.svg"
}, },
{ {
"id": "restrain", "id": "restrain",
"name": "EFFECT.StatusRestrained", "label": "EFFECT.StatusRestrained",
"img": "icons/svg/net.svg" "icon": "icons/svg/net.svg"
}, },
{ {
"id": "paralysis", "id": "paralysis",
"name": "EFFECT.StatusParalysis", "label": "EFFECT.StatusParalysis",
"img": "icons/svg/paralysis.svg" "icon": "icons/svg/paralysis.svg"
}, },
{ {
"id": "fly", "id": "fly",
"name": "EFFECT.StatusFlying", "label": "EFFECT.StatusFlying",
"img": "icons/svg/wing.svg" "icon": "icons/svg/wing.svg"
}, },
{ {
"id": "blind", "id": "blind",
"name": "EFFECT.StatusBlind", "label": "EFFECT.StatusBlind",
"img": "icons/svg/blind.svg" "icon": "icons/svg/blind.svg"
}, },
{ {
"id": "deaf", "id": "deaf",
"name": "EFFECT.StatusDeaf", "label": "EFFECT.StatusDeaf",
"img": "icons/svg/deaf.svg" "icon": "icons/svg/deaf.svg"
}, },
{ {
"id": "silence", "id": "silence",
"name": "EFFECT.StatusSilenced", "label": "EFFECT.StatusSilenced",
"img": "icons/svg/silenced.svg" "icon": "icons/svg/silenced.svg"
}, },
{ {
"id": "fear", "id": "fear",
"name": "EFFECT.StatusFear", "label": "EFFECT.StatusFear",
"img": "icons/svg/terror.svg" "icon": "icons/svg/terror.svg"
}, },
{ {
"id": "burning", "id": "burning",
"name": "EFFECT.StatusBurning", "label": "EFFECT.StatusBurning",
"img": "icons/svg/fire.svg" "icon": "icons/svg/fire.svg"
}, },
{ {
"id": "frozen", "id": "frozen",
"name": "EFFECT.StatusFrozen", "label": "EFFECT.StatusFrozen",
"img": "icons/svg/frozen.svg" "icon": "icons/svg/frozen.svg"
}, },
{ {
"id": "shock", "id": "shock",
"name": "EFFECT.StatusShocked", "label": "EFFECT.StatusShocked",
"img": "icons/svg/lightning.svg" "icon": "icons/svg/lightning.svg"
}, },
{ {
"id": "disease", "id": "disease",
"name": "EFFECT.StatusDisease", "label": "EFFECT.StatusDisease",
"img": "icons/svg/biohazard.svg" "icon": "icons/svg/biohazard.svg"
}, },
{ {
"id": "poison", "id": "poison",
"name": "EFFECT.StatusPoison", "label": "EFFECT.StatusPoison",
"img": "icons/svg/poison.svg" "icon": "icons/svg/poison.svg"
}, },
{ {
"id": "curse", "id": "curse",
"name": "EFFECT.StatusCursed", "label": "EFFECT.StatusCursed",
"img": "icons/svg/sun.svg" "icon": "icons/svg/sun.svg"
}, },
{ {
"id": "invisible", "id": "invisible",
"name": "EFFECT.StatusInvisible", "label": "EFFECT.StatusInvisible",
"img": "icons/svg/invisible.svg" "icon": "icons/svg/invisible.svg"
}, },
{ {
"id": "target", "id": "target",
"name": "EFFECT.StatusTarget", "label": "EFFECT.StatusTarget",
"img": "icons/svg/target.svg" "icon": "icons/svg/target.svg"
}, },
{ {
"id": "eye", "id": "eye",
"name": "EFFECT.StatusMarked", "label": "EFFECT.StatusMarked",
"img": "icons/svg/eye.svg" "icon": "icons/svg/eye.svg"
} }
] ]
-7
View File
@@ -141,13 +141,6 @@ export const registerHandlebarsHelpers = function () {
return key == "creature" || key == "daemon"; return key == "creature" || key == "daemon";
}) })
// Journal entry documenting Heroic/Legendary success options, localized
Handlebars.registerHelper('criticalJournalId', function () {
return game.i18n.lang === "en"
? "cNk3xcQ1F7I3Uk8Q" // Rules summary (EN)
: "veAAxCtCKcFIsnln"; // Vademecum des Joueurs (FR)
})
// Handle v12 removal of this helper // Handle v12 removal of this helper
Handlebars.registerHelper('select', function (selected, options) { Handlebars.registerHelper('select', function (selected, options) {
const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected)); const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));
+44 -220
View File
@@ -1,228 +1,52 @@
import { BoLRoll } from "../controllers/bol-rolls.js"; import {BoLRoll} from "../controllers/bol-rolls.js";
/**
* BoL Macro API accessible via game.bol.macros
*
* Usage examples (in a Foundry macro):
*
* game.bol.macros.rollAttribute("vigor")
* game.bol.macros.rollAttribute("mind")
*
* game.bol.macros.rollAptitude("melee")
* game.bol.macros.rollAptitude("ranged")
* game.bol.macros.rollAptitude("def")
* game.bol.macros.rollAptitude("init")
*
* game.bol.macros.rollWeapon("Épée courte") // by name (partial match)
* game.bol.macros.rollWeapon(0) // by index (first weapon)
*
* game.bol.macros.rollSpell("Boule de feu") // by name (partial match)
* game.bol.macros.rollSpell(0) // by index
*
* game.bol.macros.rollAlchemy("Potion de soin") // by name (partial match)
* game.bol.macros.rollAlchemy(0) // by index
*
* game.bol.macros.rollHoroscope("minor")
* game.bol.macros.rollHoroscope("major")
*
* // Generic dispatcher:
* game.bol.macros.roll("attribute", "vigor")
* game.bol.macros.roll("aptitude", "melee")
* game.bol.macros.roll("weapon", "Épée courte")
* game.bol.macros.roll("spell", "Boule de feu")
* game.bol.macros.roll("alchemy", 0)
* game.bol.macros.roll("horoscope", "minor")
*/
export class Macros { export class Macros {
/**
/* -------------------------------------------- */ * @name getSpeakersActor
/** * @description
* Resolves the actor for macro use: *
* - If multiple tokens are selected error (always) * @returns
* - If exactly one token is selected use it (GM or player) */
* - If no token selected and user is GM error (GM must select a token) static getSpeakersActor = function(){
* - If no token selected and user is a player use their assigned character // Vérifie qu'un seul token est sélectionné
* @returns {Actor|null} const tokens = canvas.tokens.controlled;
*/ if (tokens.length > 1) {
static getSpeakersActor() { ui.notifications.warn(game.i18n.localize('BOL.notification.MacroMultipleTokensSelected'));
const tokens = canvas.tokens?.controlled ?? [] return null;
}
if (tokens.length > 1) {
ui.notifications.warn(game.i18n.localize('BOL.notification.MacroMultipleTokensSelected')) const speaker = ChatMessage.getSpeaker();
return null let actor;
// Si un token est sélectionné, le prendre comme acteur cible
if (speaker.token) actor = game.actors.tokens[speaker.token];
// Sinon prendre l'acteur par défaut pour l'utilisateur courrant
if (!actor) actor = game.actors.get(speaker.actor);
return actor;
} }
if (tokens.length === 1) { static rollMacro = async function (rollType, key, adv, mod){
return tokens[0].actor ?? null const actor = this.getSpeakersActor();
} // Several tokens selected
if (actor === null) return;
// No token selected
if (actor === undefined) return ui.notifications.error(game.i18n.localize("BOL.notification.MacroNoTokenSelected"));
// No token selected const actorData = {};
if (game.user.isGM) { actorData.data = {
ui.notifications.error(game.i18n.localize("BOL.notification.MacroNoTokenSelected")) features : actor.buildFeatures()
return null };
}
// Player: fall back to their assigned character if(rollType === "attribute") {
const actor = game.user.character let attribute = eval(`actor.system.attributes.${key}`);
if (!actor) { let rollLabel = (attribute.label) ? game.i18n.localize(attribute.label) : null;
ui.notifications.error(game.i18n.localize("BOL.notification.MacroNoTokenSelected")) let description = actor.name + " - " + game.i18n.localize('BOL.ui.attributeCheck') + " - " + game.i18n.localize(attribute.label) ;
return null BoLRoll.attributeRollDialog(actor, actorData, attribute, rollLabel, description, adv, mod);
}
else if(rollType === "aptitude") {
let aptitude = eval(`actor.system.aptitudes.${key}`);
let rollLabel = (aptitude.label) ? game.i18n.localize(aptitude.label) : null;
let description = actor.name + " - " + game.i18n.localize('BOL.ui.aptitudeCheck') + " - " + game.i18n.localize(aptitude.label) ;
BoLRoll.aptitudeRollDialog(actor, actorData, aptitude, rollLabel, description, adv, mod);
}
} }
return actor
}
/* -------------------------------------------- */
/**
* Finds an item on an actor by name (partial, case-insensitive) or index.
* @param {Actor} actor
* @param {string} type - item type: "weapon", "spell", "alchemy"
* @param {string|number} nameOrIndex
* @returns {object|undefined}
*/
static _findItem(actor, type, nameOrIndex) {
const items = actor.items.filter(i => i.type === type)
if (items.length === 0) {
ui.notifications.warn(game.i18n.format("BOL.notification.MacroNoItemFound", { actor: actor.name, type }))
return undefined
}
if (nameOrIndex === undefined || nameOrIndex === null) {
if (items.length === 1) return foundry.utils.duplicate(items[0])
const names = items.map((it, i) => `[${i}] ${it.name}`).join(', ')
ui.notifications.warn(game.i18n.format("BOL.notification.MacroSpecifyNameOrIndex", { names }))
return undefined
}
if (typeof nameOrIndex === "number") {
const item = items[nameOrIndex]
if (!item) {
ui.notifications.warn(game.i18n.format("BOL.notification.MacroBadIndex", { actor: actor.name, index: nameOrIndex, type }))
return undefined
}
return foundry.utils.duplicate(item)
}
const lower = String(nameOrIndex).toLowerCase()
const found = items.find(i => i.name.toLowerCase().includes(lower))
if (!found) {
ui.notifications.warn(game.i18n.format("BOL.notification.MacroItemNotFound", { actor: actor.name, type, name: nameOrIndex }))
return undefined
}
return foundry.utils.duplicate(found)
}
/* -------------------------------------------- */
/**
* Roll an attribute check.
* @param {string} key - "vigor" | "agility" | "mind" | "appeal"
* @param {Actor} [actor] - optional, defaults to selected/owned token
*/
static rollAttribute(key = "vigor", actor = undefined) {
actor = actor ?? this.getSpeakersActor()
if (!actor) return
if (!actor.system.attributes[key]) {
ui.notifications.warn(game.i18n.format("BOL.notification.MacroUnknownAttribute", { key }))
return
}
return BoLRoll.attributeCheck(actor, key)
}
/* -------------------------------------------- */
/**
* Roll an aptitude check.
* @param {string} key - "init" | "melee" | "ranged" | "def"
* @param {Actor} [actor] - optional, defaults to selected/owned token
*/
static rollAptitude(key = "melee", actor = undefined) {
actor = actor ?? this.getSpeakersActor()
if (!actor) return
if (!actor.system.aptitudes[key]) {
ui.notifications.warn(game.i18n.format("BOL.notification.MacroUnknownAptitude", { key }))
return
}
return BoLRoll.aptitudeCheck(actor, key)
}
/* -------------------------------------------- */
/**
* Roll a weapon attack.
* @param {string|number} [nameOrIndex] - weapon name (partial) or index. Defaults to first weapon if only one.
* @param {Actor} [actor] - optional, defaults to selected/owned token
*/
static rollWeapon(nameOrIndex = undefined, actor = undefined) {
actor = actor ?? this.getSpeakersActor()
if (!actor) return
const weapon = this._findItem(actor, "weapon", nameOrIndex)
if (!weapon) return
return BoLRoll.weaponCheckWithWeapon(actor, weapon)
}
/* -------------------------------------------- */
/**
* Roll a spell check.
* @param {string|number} [nameOrIndex] - spell name (partial) or index
* @param {Actor} [actor] - optional, defaults to selected/owned token
*/
static rollSpell(nameOrIndex = undefined, actor = undefined) {
actor = actor ?? this.getSpeakersActor()
if (!actor) return
if ((actor.system.resources.power?.value ?? 1) <= 0) {
ui.notifications.warn(game.i18n.localize("BOL.notification.MacroNoPower"))
return
}
const spell = this._findItem(actor, "spell", nameOrIndex)
if (!spell) return
return BoLRoll.spellCheckWithSpell(actor, spell)
}
/* -------------------------------------------- */
/**
* Roll an alchemy check.
* @param {string|number} [nameOrIndex] - alchemy item name (partial) or index
* @param {Actor} [actor] - optional, defaults to selected/owned token
*/
static rollAlchemy(nameOrIndex = undefined, actor = undefined) {
actor = actor ?? this.getSpeakersActor()
if (!actor) return
const alchemy = this._findItem(actor, "alchemy", nameOrIndex)
if (!alchemy) return
return BoLRoll.alchemyCheckWithItem(actor, alchemy)
}
/* -------------------------------------------- */
/**
* Roll a horoscope check.
* @param {"minor"|"major"} [type="minor"]
* @param {Actor} [actor] - optional, defaults to selected/owned token
*/
static rollHoroscope(type = "minor", actor = undefined) {
actor = actor ?? this.getSpeakersActor()
if (!actor) return
return BoLRoll.horoscopeCheck(actor, undefined, type)
}
/* -------------------------------------------- */
/**
* Generic roll dispatcher.
* @param {"attribute"|"aptitude"|"weapon"|"spell"|"alchemy"|"horoscope"} type
* @param {string|number} [key] - attribute/aptitude key, item name/index, or horoscope type
* @param {Actor} [actor] - optional, defaults to selected/owned token
*/
static roll(type, key = undefined, actor = undefined) {
switch (type) {
case "attribute": return this.rollAttribute(key ?? "vigor", actor)
case "aptitude": return this.rollAptitude(key ?? "melee", actor)
case "weapon": return this.rollWeapon(key, actor)
case "spell": return this.rollSpell(key, actor)
case "alchemy": return this.rollAlchemy(key, actor)
case "horoscope": return this.rollHoroscope(key ?? "minor", actor)
default:
ui.notifications.warn(game.i18n.format("BOL.notification.MacroUnknownRollType", { type }))
}
}
/* -------------------------------------------- */
// Kept for backward-compat (previously called by old macros)
static rollMacro = async function (rollType, key) {
const actor = Macros.getSpeakersActor()
if (!actor) return
return Macros.roll(rollType, key, actor)
}
} }
+1 -3
View File
@@ -52,7 +52,6 @@ export const preloadHandlebarsTemplates = async function () {
"systems/bol/templates/chat/rolls/alchemy-roll-card.hbs", "systems/bol/templates/chat/rolls/alchemy-roll-card.hbs",
"systems/bol/templates/chat/rolls/selected-horoscope-roll-card.hbs", "systems/bol/templates/chat/rolls/selected-horoscope-roll-card.hbs",
"systems/bol/templates/chat/rolls/horoscope-roll-card.hbs", "systems/bol/templates/chat/rolls/horoscope-roll-card.hbs",
"systems/bol/templates/chat/chat-welcome.hbs",
"systems/bol/templates/dialogs/aptitude-roll-part.hbs", "systems/bol/templates/dialogs/aptitude-roll-part.hbs",
"systems/bol/templates/dialogs/attribute-roll-part.hbs", "systems/bol/templates/dialogs/attribute-roll-part.hbs",
"systems/bol/templates/dialogs/mod-roll-part.hbs", "systems/bol/templates/dialogs/mod-roll-part.hbs",
@@ -63,8 +62,7 @@ export const preloadHandlebarsTemplates = async function () {
"systems/bol/templates/dialogs/flaws-roll-part.hbs", "systems/bol/templates/dialogs/flaws-roll-part.hbs",
"systems/bol/templates/dialogs/total-roll-part.hbs", "systems/bol/templates/dialogs/total-roll-part.hbs",
"systems/bol/templates/dialogs/fightoptions-roll-part.hbs", "systems/bol/templates/dialogs/fightoptions-roll-part.hbs",
"systems/bol/templates/dialogs/horoscope-roll-part.hbs", "systems/bol/templates/dialogs/horoscope-roll-part.hbs"
"systems/bol/templates/apps/character-summary-template.html"
]; ];
// Load the template parts // Load the template parts
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-001239 MANIFEST-000990
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/04-08:21:29.571109 7f3591bfd6c0 Recovering log #1238 2026/01/19-19:47:49.729596 7f14da7fc6c0 Recovering log #988
2026/08/04-08:21:29.591159 7f3591bfd6c0 Delete type=0 #1238 2026/01/19-19:47:49.739434 7f14da7fc6c0 Delete type=3 #986
2026/08/04-08:21:29.591228 7f3591bfd6c0 Delete type=3 #1237 2026/01/19-19:47:49.739512 7f14da7fc6c0 Delete type=0 #988
2026/08/04-09:53:52.957051 7f3590bfb6c0 Level-0 table #1242: started 2026/01/19-20:19:02.579851 7f1243fff6c0 Level-0 table #993: started
2026/08/04-09:53:52.957089 7f3590bfb6c0 Level-0 table #1242: 0 bytes OK 2026/01/19-20:19:02.579872 7f1243fff6c0 Level-0 table #993: 0 bytes OK
2026/08/04-09:53:52.998497 7f3590bfb6c0 Delete type=0 #1240 2026/01/19-20:19:02.585797 7f1243fff6c0 Delete type=0 #991
2026/08/04-09:53:53.115038 7f3590bfb6c0 Manual compaction at level-0 from '!journal!6cCdSvQgEHJ1bvX4' @ 72057594037927935 : 1 .. '!journal.pages!veAAxCtCKcFIsnln.0kUgZspxXO7VS8bd' @ 0 : 0; will stop at (end) 2026/01/19-20:19:02.586018 7f1243fff6c0 Manual compaction at level-0 from '!journal!3xJg1rCxnWvEmoxS' @ 72057594037927935 : 1 .. '!journal.pages!veAAxCtCKcFIsnln.0kUgZspxXO7VS8bd' @ 0 : 0; will stop at (end)
+7 -3
View File
@@ -1,3 +1,7 @@
2026/08/04-08:20:57.432442 7f5160dfe6c0 Recovering log #1235 2026/01/18-20:07:47.845406 7f14d8ff96c0 Recovering log #984
2026/08/04-08:20:57.444689 7f5160dfe6c0 Delete type=3 #1233 2026/01/18-20:07:47.861224 7f14d8ff96c0 Delete type=3 #982
2026/08/04-08:20:57.444770 7f5160dfe6c0 Delete type=0 #1235 2026/01/18-20:07:47.861280 7f14d8ff96c0 Delete type=0 #984
2026/01/18-20:13:44.130598 7f1243fff6c0 Level-0 table #989: started
2026/01/18-20:13:44.130625 7f1243fff6c0 Level-0 table #989: 0 bytes OK
2026/01/18-20:13:44.157241 7f1243fff6c0 Delete type=0 #987
2026/01/18-20:13:44.192634 7f1243fff6c0 Manual compaction at level-0 from '!journal!3xJg1rCxnWvEmoxS' @ 72057594037927935 : 1 .. '!journal.pages!veAAxCtCKcFIsnln.0kUgZspxXO7VS8bd' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-001138 MANIFEST-000895
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/04-08:21:29.528298 7f3591bfd6c0 Recovering log #1136 2026/01/19-19:47:49.702991 7f14d97fa6c0 Recovering log #893
2026/08/04-08:21:29.545330 7f3591bfd6c0 Delete type=3 #1134 2026/01/19-19:47:49.714316 7f14d97fa6c0 Delete type=3 #891
2026/08/04-08:21:29.545404 7f3591bfd6c0 Delete type=0 #1136 2026/01/19-19:47:49.714383 7f14d97fa6c0 Delete type=0 #893
2026/08/04-09:53:52.998701 7f3590bfb6c0 Level-0 table #1141: started 2026/01/19-20:19:02.560145 7f1243fff6c0 Level-0 table #898: started
2026/08/04-09:53:52.998737 7f3590bfb6c0 Level-0 table #1141: 0 bytes OK 2026/01/19-20:19:02.560173 7f1243fff6c0 Level-0 table #898: 0 bytes OK
2026/08/04-09:53:53.035632 7f3590bfb6c0 Delete type=0 #1139 2026/01/19-20:19:02.566093 7f1243fff6c0 Delete type=0 #896
2026/08/04-09:53:53.115056 7f3590bfb6c0 Manual compaction at level-0 from '!items!G3dZTHIabA3LA1hY' @ 72057594037927935 : 1 .. '!items!xhEcsi3WHjbt2ro9' @ 0 : 0; will stop at (end) 2026/01/19-20:19:02.585964 7f1243fff6c0 Manual compaction at level-0 from '!items!G3dZTHIabA3LA1hY' @ 72057594037927935 : 1 .. '!items!xhEcsi3WHjbt2ro9' @ 0 : 0; will stop at (end)
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/03-06:19:48.750852 7f0ff57ff6c0 Recovering log #1132 2026/01/18-20:07:47.807797 7f14d9ffb6c0 Recovering log #889
2026/08/03-06:19:48.761758 7f0ff57ff6c0 Delete type=3 #1130 2026/01/18-20:07:47.824331 7f14d9ffb6c0 Delete type=3 #887
2026/08/03-06:19:48.761842 7f0ff57ff6c0 Delete type=0 #1132 2026/01/18-20:07:47.824381 7f14d9ffb6c0 Delete type=0 #889
2026/08/03-06:38:26.817856 7f0fa6ffd6c0 Level-0 table #1137: started 2026/01/18-20:13:44.157350 7f1243fff6c0 Level-0 table #894: started
2026/08/03-06:38:26.817889 7f0fa6ffd6c0 Level-0 table #1137: 0 bytes OK 2026/01/18-20:13:44.157372 7f1243fff6c0 Level-0 table #894: 0 bytes OK
2026/08/03-06:38:26.824151 7f0fa6ffd6c0 Delete type=0 #1135 2026/01/18-20:13:44.192480 7f1243fff6c0 Delete type=0 #892
2026/08/03-06:38:26.824513 7f0fa6ffd6c0 Manual compaction at level-0 from '!items!G3dZTHIabA3LA1hY' @ 72057594037927935 : 1 .. '!items!xhEcsi3WHjbt2ro9' @ 0 : 0; will stop at (end) 2026/01/18-20:13:44.192647 7f1243fff6c0 Manual compaction at level-0 from '!items!G3dZTHIabA3LA1hY' @ 72057594037927935 : 1 .. '!items!xhEcsi3WHjbt2ro9' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-001232 MANIFEST-000989
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/04-08:21:29.355610 7f3591bfd6c0 Recovering log #1230 2026/01/19-19:47:49.594444 7f14d8ff96c0 Recovering log #987
2026/08/04-08:21:29.376426 7f3591bfd6c0 Delete type=3 #1228 2026/01/19-19:47:49.605020 7f14d8ff96c0 Delete type=3 #985
2026/08/04-08:21:29.376535 7f3591bfd6c0 Delete type=0 #1230 2026/01/19-19:47:49.605095 7f14d8ff96c0 Delete type=0 #987
2026/08/04-09:53:52.678512 7f3590bfb6c0 Level-0 table #1235: started 2026/01/19-20:19:02.521702 7f1243fff6c0 Level-0 table #992: started
2026/08/04-09:53:52.678542 7f3590bfb6c0 Level-0 table #1235: 0 bytes OK 2026/01/19-20:19:02.521733 7f1243fff6c0 Level-0 table #992: 0 bytes OK
2026/08/04-09:53:52.734515 7f3590bfb6c0 Delete type=0 #1233 2026/01/19-20:19:02.528339 7f1243fff6c0 Delete type=0 #990
2026/08/04-09:53:52.803577 7f3590bfb6c0 Manual compaction at level-0 from '!items!039ZF3E3MtAGwbiX' @ 72057594037927935 : 1 .. '!items!zgspy1QKaxdEetEw' @ 0 : 0; will stop at (end) 2026/01/19-20:19:02.534634 7f1243fff6c0 Manual compaction at level-0 from '!items!039ZF3E3MtAGwbiX' @ 72057594037927935 : 1 .. '!items!zgspy1QKaxdEetEw' @ 0 : 0; will stop at (end)
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/03-06:19:48.636406 7f0ff4ffe6c0 Recovering log #1226 2026/01/18-20:07:47.640110 7f14da7fc6c0 Recovering log #983
2026/08/03-06:19:48.647892 7f0ff4ffe6c0 Delete type=3 #1224 2026/01/18-20:07:47.659075 7f14da7fc6c0 Delete type=3 #981
2026/08/03-06:19:48.647974 7f0ff4ffe6c0 Delete type=0 #1226 2026/01/18-20:07:47.659147 7f14da7fc6c0 Delete type=0 #983
2026/08/03-06:38:26.769280 7f0fa6ffd6c0 Level-0 table #1231: started 2026/01/18-20:13:43.881522 7f1243fff6c0 Level-0 table #988: started
2026/08/03-06:38:26.769359 7f0fa6ffd6c0 Level-0 table #1231: 0 bytes OK 2026/01/18-20:13:43.881548 7f1243fff6c0 Level-0 table #988: 0 bytes OK
2026/08/03-06:38:26.776073 7f0fa6ffd6c0 Delete type=0 #1229 2026/01/18-20:13:43.921371 7f1243fff6c0 Delete type=0 #986
2026/08/03-06:38:26.797077 7f0fa6ffd6c0 Manual compaction at level-0 from '!items!039ZF3E3MtAGwbiX' @ 72057594037927935 : 1 .. '!items!zgspy1QKaxdEetEw' @ 0 : 0; will stop at (end) 2026/01/18-20:13:43.921513 7f1243fff6c0 Manual compaction at level-0 from '!items!039ZF3E3MtAGwbiX' @ 72057594037927935 : 1 .. '!items!zgspy1QKaxdEetEw' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-001231 MANIFEST-000988
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/04-08:21:29.380656 7f35913fc6c0 Recovering log #1229 2026/01/19-19:47:49.609310 7f14da7fc6c0 Recovering log #986
2026/08/04-08:21:29.401993 7f35913fc6c0 Delete type=3 #1227 2026/01/19-19:47:49.619302 7f14da7fc6c0 Delete type=3 #984
2026/08/04-08:21:29.402068 7f35913fc6c0 Delete type=0 #1229 2026/01/19-19:47:49.619373 7f14da7fc6c0 Delete type=0 #986
2026/08/04-09:53:52.630733 7f3590bfb6c0 Level-0 table #1234: started 2026/01/19-20:19:02.515027 7f1243fff6c0 Level-0 table #991: started
2026/08/04-09:53:52.630808 7f3590bfb6c0 Level-0 table #1234: 0 bytes OK 2026/01/19-20:19:02.515048 7f1243fff6c0 Level-0 table #991: 0 bytes OK
2026/08/04-09:53:52.678363 7f3590bfb6c0 Delete type=0 #1232 2026/01/19-20:19:02.521555 7f1243fff6c0 Delete type=0 #989
2026/08/04-09:53:52.803556 7f3590bfb6c0 Manual compaction at level-0 from '!items!CoqlfsDV1gL5swbK' @ 72057594037927935 : 1 .. '!items!yofwG0YrsL902G77' @ 0 : 0; will stop at (end) 2026/01/19-20:19:02.534623 7f1243fff6c0 Manual compaction at level-0 from '!items!CoqlfsDV1gL5swbK' @ 72057594037927935 : 1 .. '!items!yofwG0YrsL902G77' @ 0 : 0; will stop at (end)
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/03-06:19:48.651558 7f0ff57ff6c0 Recovering log #1225 2026/01/18-20:07:47.664234 7f14d8ff96c0 Recovering log #982
2026/08/03-06:19:48.662260 7f0ff57ff6c0 Delete type=3 #1223 2026/01/18-20:07:47.679427 7f14d8ff96c0 Delete type=3 #980
2026/08/03-06:19:48.662354 7f0ff57ff6c0 Delete type=0 #1225 2026/01/18-20:07:47.679484 7f14d8ff96c0 Delete type=0 #982
2026/08/03-06:38:26.782903 7f0fa6ffd6c0 Level-0 table #1230: started 2026/01/18-20:13:43.851971 7f1243fff6c0 Level-0 table #987: started
2026/08/03-06:38:26.782939 7f0fa6ffd6c0 Level-0 table #1230: 0 bytes OK 2026/01/18-20:13:43.851994 7f1243fff6c0 Level-0 table #987: 0 bytes OK
2026/08/03-06:38:26.790042 7f0fa6ffd6c0 Delete type=0 #1228 2026/01/18-20:13:43.881382 7f1243fff6c0 Delete type=0 #985
2026/08/03-06:38:26.797110 7f0fa6ffd6c0 Manual compaction at level-0 from '!items!CoqlfsDV1gL5swbK' @ 72057594037927935 : 1 .. '!items!yofwG0YrsL902G77' @ 0 : 0; will stop at (end) 2026/01/18-20:13:43.921506 7f1243fff6c0 Manual compaction at level-0 from '!items!CoqlfsDV1gL5swbK' @ 72057594037927935 : 1 .. '!items!yofwG0YrsL902G77' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-001231 MANIFEST-000988
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/04-08:21:29.426266 7f35923fe6c0 Recovering log #1229 2026/01/19-19:47:49.635172 7f14d8ff96c0 Recovering log #986
2026/08/04-08:21:29.442494 7f35923fe6c0 Delete type=3 #1227 2026/01/19-19:47:49.644889 7f14d8ff96c0 Delete type=3 #984
2026/08/04-08:21:29.442560 7f35923fe6c0 Delete type=0 #1229 2026/01/19-19:47:49.644941 7f14d8ff96c0 Delete type=0 #986
2026/08/04-09:53:52.882869 7f3590bfb6c0 Level-0 table #1234: started 2026/01/19-20:19:02.508864 7f1243fff6c0 Level-0 table #991: started
2026/08/04-09:53:52.882905 7f3590bfb6c0 Level-0 table #1234: 0 bytes OK 2026/01/19-20:19:02.508913 7f1243fff6c0 Level-0 table #991: 0 bytes OK
2026/08/04-09:53:52.919503 7f3590bfb6c0 Delete type=0 #1232 2026/01/19-20:19:02.514913 7f1243fff6c0 Delete type=0 #989
2026/08/04-09:53:52.956792 7f3590bfb6c0 Manual compaction at level-0 from '!items!4S4xAfMXGnuU0O1a' @ 72057594037927935 : 1 .. '!items!zxY3sW0iCJBvwjOS' @ 0 : 0; will stop at (end) 2026/01/19-20:19:02.534608 7f1243fff6c0 Manual compaction at level-0 from '!items!4S4xAfMXGnuU0O1a' @ 72057594037927935 : 1 .. '!items!zxY3sW0iCJBvwjOS' @ 0 : 0; will stop at (end)
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/03-06:19:48.681657 7f0ff4ffe6c0 Recovering log #1225 2026/01/18-20:07:47.704039 7f14d8ff96c0 Recovering log #982
2026/08/03-06:19:48.692268 7f0ff4ffe6c0 Delete type=3 #1223 2026/01/18-20:07:47.721242 7f14d8ff96c0 Delete type=3 #980
2026/08/03-06:19:48.692351 7f0ff4ffe6c0 Delete type=0 #1225 2026/01/18-20:07:47.721902 7f14d8ff96c0 Delete type=0 #982
2026/08/03-06:38:26.790164 7f0fa6ffd6c0 Level-0 table #1230: started 2026/01/18-20:13:43.777269 7f1243fff6c0 Level-0 table #987: started
2026/08/03-06:38:26.790190 7f0fa6ffd6c0 Level-0 table #1230: 0 bytes OK 2026/01/18-20:13:43.777312 7f1243fff6c0 Level-0 table #987: 0 bytes OK
2026/08/03-06:38:26.796894 7f0fa6ffd6c0 Delete type=0 #1228 2026/01/18-20:13:43.819988 7f1243fff6c0 Delete type=0 #985
2026/08/03-06:38:26.797197 7f0fa6ffd6c0 Manual compaction at level-0 from '!items!4S4xAfMXGnuU0O1a' @ 72057594037927935 : 1 .. '!items!zxY3sW0iCJBvwjOS' @ 0 : 0; will stop at (end) 2026/01/18-20:13:43.921486 7f1243fff6c0 Manual compaction at level-0 from '!items!4S4xAfMXGnuU0O1a' @ 72057594037927935 : 1 .. '!items!zxY3sW0iCJBvwjOS' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-001229 MANIFEST-000986
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/04-08:21:29.736235 7f3592bff6c0 Recovering log #1227 2026/01/19-19:47:49.822315 7f14d9ffb6c0 Recovering log #984
2026/08/04-08:21:29.753212 7f3592bff6c0 Delete type=3 #1225 2026/01/19-19:47:49.833348 7f14d9ffb6c0 Delete type=3 #982
2026/08/04-08:21:29.753288 7f3592bff6c0 Delete type=0 #1227 2026/01/19-19:47:49.833401 7f14d9ffb6c0 Delete type=0 #984
2026/08/04-09:53:53.269403 7f3590bfb6c0 Level-0 table #1232: started 2026/01/19-20:19:02.619154 7f1243fff6c0 Level-0 table #989: started
2026/08/04-09:53:53.269440 7f3590bfb6c0 Level-0 table #1232: 0 bytes OK 2026/01/19-20:19:02.619176 7f1243fff6c0 Level-0 table #989: 0 bytes OK
2026/08/04-09:53:53.308866 7f3590bfb6c0 Delete type=0 #1230 2026/01/19-20:19:02.626822 7f1243fff6c0 Delete type=0 #987
2026/08/04-09:53:53.444562 7f3590bfb6c0 Manual compaction at level-0 from '!items!6fTZ6hOKR4pWbWOe' @ 72057594037927935 : 1 .. '!items!zwSNMO9HpiqUCMt8' @ 0 : 0; will stop at (end) 2026/01/19-20:19:02.639748 7f1243fff6c0 Manual compaction at level-0 from '!items!6fTZ6hOKR4pWbWOe' @ 72057594037927935 : 1 .. '!items!zwSNMO9HpiqUCMt8' @ 0 : 0; will stop at (end)
+7 -7
View File
@@ -1,7 +1,7 @@
2026/08/03-06:19:48.879803 7f0ff57ff6c0 Recovering log #1223 2026/01/18-20:07:48.004635 7f14d9ffb6c0 Recovering log #980
2026/08/03-06:19:48.890084 7f0ff57ff6c0 Delete type=3 #1221 2026/01/18-20:07:48.021334 7f14d9ffb6c0 Delete type=3 #978
2026/08/03-06:19:48.890177 7f0ff57ff6c0 Delete type=0 #1223 2026/01/18-20:07:48.021403 7f14d9ffb6c0 Delete type=0 #980
2026/08/03-06:38:26.891447 7f0fa6ffd6c0 Level-0 table #1228: started 2026/01/18-20:13:44.336530 7f1243fff6c0 Level-0 table #985: started
2026/08/03-06:38:26.891477 7f0fa6ffd6c0 Level-0 table #1228: 0 bytes OK 2026/01/18-20:13:44.336556 7f1243fff6c0 Level-0 table #985: 0 bytes OK
2026/08/03-06:38:26.898218 7f0fa6ffd6c0 Delete type=0 #1226 2026/01/18-20:13:44.372711 7f1243fff6c0 Delete type=0 #983
2026/08/03-06:38:26.913139 7f0fa6ffd6c0 Manual compaction at level-0 from '!items!6fTZ6hOKR4pWbWOe' @ 72057594037927935 : 1 .. '!items!zwSNMO9HpiqUCMt8' @ 0 : 0; will stop at (end) 2026/01/18-20:13:44.502487 7f1243fff6c0 Manual compaction at level-0 from '!items!6fTZ6hOKR4pWbWOe' @ 72057594037927935 : 1 .. '!items!zwSNMO9HpiqUCMt8' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More