Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8555138366 | ||
|
|
b14006a826 | ||
|
|
48a751b508 | ||
|
|
8dcfbcfaf7 | ||
|
|
55f6dfaee7 | ||
|
|
79b3290002 | ||
|
|
1cbe8af998 | ||
|
|
852703c4ef | ||
|
|
71f01b1c5c | ||
|
|
4b3b7d86fd | ||
|
|
1c27693dbe | ||
|
|
ff7be43861 | ||
|
|
9927c40963 | ||
|
|
236a0fd300 | ||
|
|
36a66d3eac | ||
|
|
317411ce60 | ||
|
|
3c6529bc99 | ||
|
|
061390df80 | ||
|
|
b4fd1c738f | ||
|
|
c7d6c6c5e5 | ||
|
|
31f094818e | ||
|
|
40afa53337 | ||
|
|
cb98d721c5 | ||
|
|
6ba5137ea1 | ||
|
|
5377674a30 | ||
|
|
f6ed462bce | ||
|
|
890223021a | ||
|
|
b1f874b3c8 | ||
|
|
2dd9ee19e9 | ||
|
|
aa203c546c | ||
|
|
8f31031244 | ||
|
|
84e367b79f | ||
|
|
0854e25a66 | ||
|
|
0c299db26f | ||
|
|
f267d06536 | ||
|
|
494b027513 | ||
|
|
35c58ff631 | ||
|
|
e87cd6d73e | ||
|
|
05b7a1181c | ||
|
|
2e91fe7ae4 | ||
|
|
9c3de358b3 | ||
|
|
b1e73f0761 | ||
|
|
222aa75a1d | ||
|
|
caa78d7c45 | ||
|
|
5edcaa373c | ||
|
|
08e412b32f | ||
|
|
4269946c30 | ||
|
|
607817302b | ||
|
|
dd39fa6113 | ||
|
|
d035b963de | ||
|
|
a14c26d168 | ||
|
|
b25a25a94f | ||
|
|
98ffb27db7 | ||
|
|
a6e0f60665 | ||
|
|
8488ed1bd1 | ||
|
|
dda47c51a8 | ||
|
|
a2285931b3 | ||
|
|
37b8956048 | ||
|
|
e54a105222 | ||
|
|
bee911df5e | ||
|
|
e2e533d481 | ||
|
|
2518ded84f | ||
|
|
fab88401ea | ||
|
|
7fed2f0a98 | ||
|
|
7c3ef81dec | ||
|
|
cbb52bf3aa | ||
|
|
86f73a96d0 | ||
|
|
dbba39373b | ||
|
|
30455759e8 | ||
|
|
eb675f24ea | ||
|
|
1357ec9b6d | ||
|
|
396986fefd | ||
|
|
c591cee153 | ||
|
|
54ae4fdb63 | ||
|
|
e846a0a30c |
54
CHANGELOG.md
@@ -6,13 +6,57 @@ Date format : day/month/year
|
|||||||
> - `foundry-version`: Stick to the major version of FoundryVTT.
|
> - `foundry-version`: Stick to the major version of FoundryVTT.
|
||||||
> - `system-version`: System functionalities and Fixes.
|
> - `system-version`: System functionalities and Fixes.
|
||||||
|
|
||||||
|
## 1.14.0 - ??/04/2026 - Foundry v14 Compatibility
|
||||||
|
__! Be certain to carefully back up any critical user data before installing this update !__
|
||||||
|
- Updated the System to FoundryVTT v14.
|
||||||
|
- Fix List without bullet on item sheets.
|
||||||
|
- Fix black embedded links displays on Tooltips.
|
||||||
|
|
||||||
|
## 1.13.4 - 01/03/2026 - UI Polish, Compendium Upgrades
|
||||||
|
Welcoming Litasa as a maintainer for the system!
|
||||||
|
|
||||||
|
- Fixing type in Courts of Stone title, thanks to SagaTympana (!51).
|
||||||
|
- Update to the [development wiki](https://gitlab.com/teaml5r/l5r5e/-/wikis/home), thanks to Norman Briggs (!52)
|
||||||
|
- Updating the sidebar icons to show L5r5e specific ones (#72)(!53) (Litasa)
|
||||||
|
- Adding incremental buttons for honor, glory, and status (#69)(!54) (Litasa)
|
||||||
|
- New rank is now shown directly when completing a school rank (#64)(!57) (Litasa)
|
||||||
|
- Adding Starting items to Compendiums that was missing. Split Poison and Omamori into individual items(!58) (Litasa)
|
||||||
|
- Some combinations of light and dark theme made the GM Toolbox and GM Monitor hard/impossible to use. Fixed now (#75)(!59) (Litasa)
|
||||||
|
- Conditions now show in the top of dice-picker and roll-n-keep (related to #74)(!60) (Litasa)
|
||||||
|
- Compendium filter is now a lot "snappier". New search box and now able to multi select elements/ranks/rarity. (!61) (Litasa)
|
||||||
|
- Adding selection circle to Combat encounter (!62) (Litasa)
|
||||||
|
|
||||||
|
## 1.13.3 - 01/02/2026 - Tactical Grid & Fixes
|
||||||
|
- Updated demeanors from books up to Imperfect Land (included), thanks to Olivier Brencklé (!48).
|
||||||
|
- Added Tactical Grid Range Band, thanks to Litasa (!49).
|
||||||
|
- Fix Title advancement auto-name/icon, thanks to SagaTympana (!50).
|
||||||
|
|
||||||
|
## 1.13.2 - 18/10/2025 - Conditions Icons & Fixes
|
||||||
|
- Fix Actor Sheet for pressing key `Enter` in input trigger `no active Encounter...` message.
|
||||||
|
- Fix Compendium `Astrolab` is duplicate with `Mantis Clan` and `Children of the Five Winds`. Renamed the `cotfw` version to `Astrolabe (Unicorn)`.
|
||||||
|
- Fix 20Q autocomplete and menu css.
|
||||||
|
- Added handmade conditions icons by Nikotka (thx to rex35game for the share).
|
||||||
|
- Spanish language updated thanks to Alejabarr.
|
||||||
|
|
||||||
|
## 1.13.1 - 21/09/2025 - Conditions & Fixes
|
||||||
|
- Fix for Clicking on items doesn't show item window (#65 Thx to Litasa).
|
||||||
|
- Fix for fade configuration (#66).
|
||||||
|
- Added some Tooltips loading optimizations (#62 Thanks to KitCat).
|
||||||
|
- Added some Properties loading optimizations (#63 Thanks to KitCat).
|
||||||
|
- Conditions changes :
|
||||||
|
- Added basic token conditions (Thanks to Putty)
|
||||||
|
- Added compromised condition when strife goes beyond max (Thanks to Putty).
|
||||||
|
- Added apply incapacitated if character's fatigue goes beyond endurance (Thanks to Putty).
|
||||||
|
- Added ability to remove condition from actor sheet and show core journal on click.
|
||||||
|
- Added option to show all conditions (StatusEffect), default to false (show only l5R conditions).
|
||||||
|
|
||||||
## 1.13.0 - 24/08/2025 - Foundry v13 Compatibility (Thx to Litasa)
|
## 1.13.0 - 24/08/2025 - Foundry v13 Compatibility (Thx to Litasa)
|
||||||
__! Be certain to carefully back up any critical user data before installing this update !__
|
__! Be certain to carefully back up any critical user data before installing this update !__
|
||||||
- Updated the System to FoundryVTT v13.
|
- Updated the System to FoundryVTT v13.
|
||||||
- Compendiums
|
- Compendiums
|
||||||
- Added English compendium for `Children of the Five Winds`.
|
- Added English compendium for `Children of the Five Winds`.
|
||||||
- Added French translation for `Writ of the Wild`.
|
- Added French translation for `Writ of the Wild`.
|
||||||
- Fix Compendium Typo: "Beseech Hida's MIght" -> "Beseech Hida's Might" (!59).
|
- Fix Compendium Typo: "Beseech Hida's MIght" -> "Beseech Hida's Might" (#59).
|
||||||
- Actor sheets: Technique types are now hidden when they are not checked in locked mode.
|
- Actor sheets: Technique types are now hidden when they are not checked in locked mode.
|
||||||
- Fix 20Q: Technique message fixed (unallowed tech for School).
|
- Fix 20Q: Technique message fixed (unallowed tech for School).
|
||||||
- Switched wysiwyg editor engine to `prosemirror` for text editor (`tinymce` will be removed in Foundry v14).
|
- Switched wysiwyg editor engine to `prosemirror` for text editor (`tinymce` will be removed in Foundry v14).
|
||||||
@@ -54,12 +98,12 @@ __! Be certain to carefully back up any critical user data before installing thi
|
|||||||
## 1.11.0 - 13/12/2023 - Little fixes
|
## 1.11.0 - 13/12/2023 - Little fixes
|
||||||
- 20Q :
|
- 20Q :
|
||||||
- Starting techniques now have a limit of 6 techniques instead of 5 (see Celestial Realms : `Moshi Sun Sentinel School`).
|
- Starting techniques now have a limit of 6 techniques instead of 5 (see Celestial Realms : `Moshi Sun Sentinel School`).
|
||||||
- Enable dropping on the 'drop here' label for 20Q (thk to Litasa !34).
|
- Enable dropping on the 'drop here' label for 20Q (thk to Litasa #34).
|
||||||
- Compendiums : Added masteries and abilities from Deathly Turns.
|
- Compendiums : Added masteries and abilities from Deathly Turns.
|
||||||
|
|
||||||
## 1.10.1 - 22/08/2023 - Litasa's fixes
|
## 1.10.1 - 22/08/2023 - Litasa's fixes
|
||||||
All these changes are thanks to Litasa.
|
All these changes are thanks to Litasa.
|
||||||
- Roll-n-Keep dialog now waits for the DiceSoNice animation to finish before displaying the result when re-rolling or exploding dice (!28).
|
- Roll-n-Keep dialog now waits for the DiceSoNice animation to finish before displaying the result when re-rolling or exploding dice (#28).
|
||||||
- Adding the ability to have a different name for the custom-compendium (needed to disable the system embedded ones).
|
- Adding the ability to have a different name for the custom-compendium (needed to disable the system embedded ones).
|
||||||
- Fixes some CSS issues when the font size is not the default (#50, #51 and #52).
|
- Fixes some CSS issues when the font size is not the default (#50, #51 and #52).
|
||||||
|
|
||||||
@@ -75,8 +119,8 @@ __! Be certain to carefully back up any critical user data before installing thi
|
|||||||
|
|
||||||
## 1.9.6 - 14/05/2023 - Bragma's QoL
|
## 1.9.6 - 14/05/2023 - Bragma's QoL
|
||||||
All these changes are thanks to Bragma.
|
All these changes are thanks to Bragma.
|
||||||
- Added effects panel to both pc and npc (!26).
|
- Added effects panel to both pc and npc (#26).
|
||||||
- Added a underline on rings to show current stance (!25).
|
- Added an underline on rings to show current stance (#25).
|
||||||
- Fix Lists not showing correctly in journal (#44).
|
- Fix Lists not showing correctly in journal (#44).
|
||||||
|
|
||||||
## 1.9.5 - 11/01/2023 - Adding Modifiers
|
## 1.9.5 - 11/01/2023 - Adding Modifiers
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://ko-fi.com/vlyan)
|
[](https://ko-fi.com/vlyan)
|
||||||
[](https://foundryvtt.com/)
|
[](https://foundryvtt.com/)
|
||||||
[](https://forge-vtt.com/bazaar#package=l5r5e)
|
[](https://forge-vtt.com/bazaar#package=l5r5e)
|
||||||
[](https://www.foundryvtt-hub.com/package/l5r5e/)
|
[](https://www.foundryvtt-hub.com/package/l5r5e/)
|
||||||
[](https://www.foundryvtt-hub.com/package/l5r5e/)
|
[](https://www.foundryvtt-hub.com/package/l5r5e/)
|
||||||
@@ -18,13 +18,14 @@ See the [Wiki page - Installation](https://gitlab.com/teaml5r/l5r5e/-/wikis/user
|
|||||||
|
|
||||||
|
|
||||||
## Current L5R team (alphabetical order)
|
## Current L5R team (alphabetical order)
|
||||||
- Carter (compendiums, adventure adaptation)
|
- Litasa (development)
|
||||||
- Vlyan (development)
|
- Vlyan (development)
|
||||||
|
|
||||||
|
|
||||||
## Historical L5R team (alphabetical order)
|
## Historical L5R team (alphabetical order)
|
||||||
- Carter (compendiums, adventure adaptation)
|
- Carter (compendiums, adventure adaptation)
|
||||||
- Hrunh (compendiums, pre-gen characters adaptation)
|
- Hrunh (compendiums, pre-gen characters adaptation)
|
||||||
|
- Litasa (development)
|
||||||
- Mandar (development)
|
- Mandar (development)
|
||||||
- Sasmira (contributor)
|
- Sasmira (contributor)
|
||||||
- Vlyan (development)
|
- Vlyan (development)
|
||||||
|
|||||||
11226
package-lock.json
generated
@@ -19,17 +19,18 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/teaml5r/l5r5e#readme",
|
"homepage": "https://gitlab.com/teaml5r/l5r5e#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@foundryvtt/foundryvtt-cli": "^3.0.3",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"browser-sync": "^2.27.10",
|
"browser-sync": "^3.0.4",
|
||||||
"eslint": "^8.27.0",
|
"eslint": "^8.27.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^5.0.1",
|
||||||
"gulp-autoprefixer": "^8.0.0",
|
"gulp-autoprefixer": "^8.0.0",
|
||||||
"gulp-sass": "^5.1.0",
|
"gulp-sass": "^5.1.0",
|
||||||
"lint-staged": "^10.5.1",
|
"lint-staged": "^10.5.1",
|
||||||
"sass": "^1.56.1",
|
"prettier": "^2.7.1",
|
||||||
"prettier": "^2.7.1"
|
"sass": "^1.56.1"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.js": "eslint --cache --fix",
|
"*.js": "eslint --cache --fix",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
||||||
</metadata>
|
</metadata>
|
||||||
<g transform="translate(0.000000,1264.000000) scale(0.100000,-0.100000)"
|
<g transform="translate(0.000000,1264.000000) scale(0.100000,-0.100000)"
|
||||||
fill="#000000" stroke="none">
|
fill="currentColor" stroke="none">
|
||||||
<path d="M5925 12625 c-248 -21 -380 -39 -378 -51 1 -7 -56 -18 -148 -29 -83
|
<path d="M5925 12625 c-248 -21 -380 -39 -378 -51 1 -7 -56 -18 -148 -29 -83
|
||||||
-9 -190 -25 -237 -35 -244 -53 -454 -105 -602 -151 -91 -28 -210 -64 -264 -81
|
-9 -190 -25 -237 -35 -244 -53 -454 -105 -602 -151 -91 -28 -210 -64 -264 -81
|
||||||
-465 -138 -1038 -415 -1493 -719 -234 -157 -585 -429 -773 -600 -215 -196
|
-465 -138 -1038 -415 -1493 -719 -234 -157 -585 -429 -773 -600 -215 -196
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
BIN
system/assets/icons/conditions/afflicted.webp
Normal file
|
After Width: | Height: | Size: 29 KiB |
2
system/assets/icons/conditions/artist_nikotka.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
All original conditions icons was made by Nikotka.
|
||||||
|
Altered by Vlyan to be more visible in FoundryVTT.
|
||||||
BIN
system/assets/icons/conditions/bleeding.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
system/assets/icons/conditions/burning.webp
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
system/assets/icons/conditions/centered.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
system/assets/icons/conditions/compromised.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
system/assets/icons/conditions/dazed.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
system/assets/icons/conditions/disoriented.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
system/assets/icons/conditions/dying_1.webp
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
system/assets/icons/conditions/dying_2.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
system/assets/icons/conditions/dying_3.webp
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
system/assets/icons/conditions/emboldened.webp
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
system/assets/icons/conditions/enraged.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
system/assets/icons/conditions/exhausted.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
system/assets/icons/conditions/heavily_wounded.webp
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
system/assets/icons/conditions/heavily_wounded_air.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
system/assets/icons/conditions/heavily_wounded_earth.webp
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
system/assets/icons/conditions/heavily_wounded_fire.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
system/assets/icons/conditions/heavily_wounded_void.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
system/assets/icons/conditions/heavily_wounded_water.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
system/assets/icons/conditions/illness_coughing_illness.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
system/assets/icons/conditions/illness_fire_rash.webp
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
system/assets/icons/conditions/illness_gut_sickness.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
system/assets/icons/conditions/illness_oozing_sore_disease.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
system/assets/icons/conditions/illness_unsteady_illness.webp
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
system/assets/icons/conditions/immobilized.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
system/assets/icons/conditions/incapacitated.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
system/assets/icons/conditions/intoxicated.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
system/assets/icons/conditions/lightly_wounded_air.webp
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
system/assets/icons/conditions/lightly_wounded_earth.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
system/assets/icons/conditions/lightly_wounded_fire.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
system/assets/icons/conditions/lightly_wounded_void.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
system/assets/icons/conditions/lightly_wounded_water.webp
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
system/assets/icons/conditions/possesed.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
system/assets/icons/conditions/prone.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
system/assets/icons/conditions/silenced.webp
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
system/assets/icons/conditions/unconscious.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
@@ -1,13 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
width="48.2px" height="48.2px" viewBox="0 0 48.2 48.2" xml:space="preserve">
|
||||||
width="48.2px" height="48.2px" viewBox="0 0 48.2 48.2" style="enable-background:new 0 0 48.2 48.2;" xml:space="preserve">
|
<defs>
|
||||||
|
<linearGradient id="iconGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#E0E0E0;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#FFFFFF;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.st0{fill:#030104;}
|
/* Gradient fill with contrasting stroke */
|
||||||
|
.fill-gradient{fill:url(#iconGradient);}
|
||||||
|
.stroke-contrast{stroke:#2a2a2a;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
</style>
|
</style>
|
||||||
<g>
|
<g>
|
||||||
<g>
|
<g>
|
||||||
<path class="st0" d="M43,40c-0.9-7.2-2.3-13.9-5.7-20.2c-1-1.9-2.6-3.5-3.8-5.3c-0.2-0.3-0.5-0.7-0.7-1.1c0.7-0.6,1.4-1.3,2.1-2
|
<path class="fill-gradient stroke-contrast" d="M43,40c-0.9-7.2-2.3-13.9-5.7-20.2c-1-1.9-2.6-3.5-3.8-5.3c-0.2-0.3-0.5-0.7-0.7-1.1c0.7-0.6,1.4-1.3,2.1-2
|
||||||
c0.8-0.8,1.5-1.5,2.1-2.3c0.7-0.8,1.2-1.5,1.7-2.1c1-1.3,1.5-2.3,1.5-2.3s-1,0.5-2.3,1.5c-0.7,0.5-1.4,1.1-2.1,1.7
|
c0.8-0.8,1.5-1.5,2.1-2.3c0.7-0.8,1.2-1.5,1.7-2.1c1-1.3,1.5-2.3,1.5-2.3s-1,0.5-2.3,1.5c-0.7,0.5-1.4,1.1-2.1,1.7
|
||||||
c-0.8,0.7-1.5,1.4-2.3,2.1c-0.4,0.4-0.9,0.9-1.3,1.4c-0.7-3.4-3.5-6.2-7-6.6c-3.7-0.5-7.6,1.7-8.5,5.4c-0.1,0.5-0.3,1-0.4,1.4
|
c-0.8,0.7-1.5,1.4-2.3,2.1c-0.4,0.4-0.9,0.9-1.3,1.4c-0.7-3.4-3.5-6.2-7-6.6c-3.7-0.5-7.6,1.7-8.5,5.4c-0.1,0.5-0.3,1-0.4,1.4
|
||||||
c-0.5-0.5-1-1-1.5-1.5C14,9.3,13.2,8.6,12.5,8c-0.8-0.7-1.5-1.2-2.1-1.7C9,5.3,8,4.8,8,4.8s0.5,1,1.5,2.3c0.5,0.7,1.1,1.4,1.7,2.1
|
c-0.5-0.5-1-1-1.5-1.5C14,9.3,13.2,8.6,12.5,8c-0.8-0.7-1.5-1.2-2.1-1.7C9,5.3,8,4.8,8,4.8s0.5,1,1.5,2.3c0.5,0.7,1.1,1.4,1.7,2.1
|
||||||
@@ -17,10 +24,10 @@
|
|||||||
c0.5,0.1,1,0,1.4-0.2c0.4-0.2,0.8-0.6,1.1-1.1c0.5-0.7,0.7-0.3,1.3,0.6c0.5,0.9,1.6,1.2,3,1.4c2.9,0.4,2.8,0.6,2.5,3.5
|
c0.5,0.1,1,0,1.4-0.2c0.4-0.2,0.8-0.6,1.1-1.1c0.5-0.7,0.7-0.3,1.3,0.6c0.5,0.9,1.6,1.2,3,1.4c2.9,0.4,2.8,0.6,2.5,3.5
|
||||||
c0,0.5,0,1-0.1,1.4c-0.5,2.3,0.1,5.1-1.8,6.9c-0.8,0.7-1.6,1.1-1.5,1.2c0.1,0.1,1.1,0,2.1-0.1c2.4-0.3,4.9-0.7,7.4-1
|
c0,0.5,0,1-0.1,1.4c-0.5,2.3,0.1,5.1-1.8,6.9c-0.8,0.7-1.6,1.1-1.5,1.2c0.1,0.1,1.1,0,2.1-0.1c2.4-0.3,4.9-0.7,7.4-1
|
||||||
C42.3,42.1,43.1,41.1,43,40z M21.3,8.2C20.2,9,19.2,9.3,19,9s0.4-1.2,1.5-1.9c1-0.7,2.1-1.1,2.3-0.7C23,6.6,22.3,7.5,21.3,8.2z"/>
|
C42.3,42.1,43.1,41.1,43,40z M21.3,8.2C20.2,9,19.2,9.3,19,9s0.4-1.2,1.5-1.9c1-0.7,2.1-1.1,2.3-0.7C23,6.6,22.3,7.5,21.3,8.2z"/>
|
||||||
<path class="st0" d="M32,35.9c0-0.2-0.9-0.2-1.9-0.1c-1,0.1-1.8,0.4-1.8,0.7c0,0.2,0.9,0.3,1.9,0.1C31.3,36.4,32,36.1,32,35.9z"/>
|
<path class="fill-gradient stroke-contrast" d="M32,35.9c0-0.2-0.9-0.2-1.9-0.1c-1,0.1-1.8,0.4-1.8,0.7c0,0.2,0.9,0.3,1.9,0.1C31.3,36.4,32,36.1,32,35.9z"/>
|
||||||
<path class="st0" d="M18.2,35.8c-0.9-0.2-1.7-0.1-1.8,0.1c0,0.2,0.7,0.5,1.6,0.7c0.9,0.2,1.7,0.1,1.8-0.1
|
<path class="fill-gradient stroke-contrast" d="M18.2,35.8c-0.9-0.2-1.7-0.1-1.8,0.1c0,0.2,0.7,0.5,1.6,0.7c0.9,0.2,1.7,0.1,1.8-0.1
|
||||||
C19.9,36.3,19.2,36,18.2,35.8z"/>
|
C19.9,36.3,19.2,36,18.2,35.8z"/>
|
||||||
<path class="st0" d="M25.3,40.3c-0.1,0-0.3,0-0.5-0.1c-0.1,0-0.2-0.1-0.3-0.1c-0.1,0-0.2,0-0.2-0.1c-0.1,0-0.3-0.1-0.4,0
|
<path class="fill-gradient stroke-contrast" d="M25.3,40.3c-0.1,0-0.3,0-0.5-0.1c-0.1,0-0.2-0.1-0.3-0.1c-0.1,0-0.2,0-0.2-0.1c-0.1,0-0.3-0.1-0.4,0
|
||||||
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.2,0.1-0.2,0.1c-0.1,0-0.2,0.1-0.2,0.1c-0.2,0.1-0.4,0-0.5,0.1c-0.1,0-0.2,0.1-0.2,0.1s0,0.1,0,0.3
|
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.2,0.1-0.2,0.1c-0.1,0-0.2,0.1-0.2,0.1c-0.2,0.1-0.4,0-0.5,0.1c-0.1,0-0.2,0.1-0.2,0.1s0,0.1,0,0.3
|
||||||
c0,0.2,0,0.3,0.1,0.6c0.1,0.1,0.1,0.2,0.3,0.3c0.1,0.1,0.2,0.2,0.4,0.3c0.1,0.1,0.3,0.2,0.4,0.2c0.2,0,0.3,0.1,0.5,0.1
|
c0,0.2,0,0.3,0.1,0.6c0.1,0.1,0.1,0.2,0.3,0.3c0.1,0.1,0.2,0.2,0.4,0.3c0.1,0.1,0.3,0.2,0.4,0.2c0.2,0,0.3,0.1,0.5,0.1
|
||||||
c0.2,0,0.3,0,0.5-0.1c0.2,0,0.3-0.1,0.4-0.2c0.3-0.1,0.5-0.4,0.6-0.6c0.1-0.2,0.2-0.4,0.2-0.6c0-0.2,0-0.2,0-0.2
|
c0.2,0,0.3,0,0.5-0.1c0.2,0,0.3-0.1,0.4-0.2c0.3-0.1,0.5-0.4,0.6-0.6c0.1-0.2,0.2-0.4,0.2-0.6c0-0.2,0-0.2,0-0.2
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
width="48.2px" height="48.2px" viewBox="0 0 48.2 48.2" style="enable-background:new 0 0 48.2 48.2;" xml:space="preserve">
|
width="48.2px" height="48.2px" viewBox="-1 -1 50.2 50.2" style="enable-background:new -1 -1 50.2 50.2;" xml:space="preserve">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.st0{fill:#1D1C1A;}
|
.st0{fill:#030104;}
|
||||||
</style>
|
</style>
|
||||||
<g>
|
<g>
|
||||||
<path class="st0" d="M48.7,4.8c0,0-0.1,0-0.2,0.1c-0.9,0.6-4.4,2-9.3,3.1c-4.6,1-10.8,1.8-16.7,2.3c-6.2,0.6-14.9,0.8-21.3-0.5
|
<path class="st0" d="M48.7,4.8c0,0-0.1,0-0.2,0.1c-0.9,0.6-4.4,2-9.3,3.1c-4.6,1-10.8,1.8-16.7,2.3c-6.2,0.6-14.9,0.8-21.3-0.5
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
@@ -262,7 +262,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "Journal of observations",
|
"id": "Journal of Observations",
|
||||||
"name": "Journal d'observations",
|
"name": "Journal d'observations",
|
||||||
"description": "",
|
"description": "",
|
||||||
"source_reference": {
|
"source_reference": {
|
||||||
@@ -894,8 +894,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "Astrolabe",
|
"id": "Astrolabe (Unicorn)",
|
||||||
"name": "Astrolabe (WIP)",
|
"name": "Astrolabe (Licorne)",
|
||||||
"description": "",
|
"description": "",
|
||||||
"source_reference": {
|
"source_reference": {
|
||||||
"page": ""
|
"page": ""
|
||||||
@@ -972,6 +972,198 @@
|
|||||||
"source_reference": {
|
"source_reference": {
|
||||||
"page": ""
|
"page": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Glass Ornament (Dragonfly)",
|
||||||
|
"name": "Le Verre de Libellule",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Arrows: Armor-Piercing",
|
||||||
|
"name": "Flèches : Perce-armure",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "236"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Arrows: Flesh-Cutter",
|
||||||
|
"name": "Flèches : Fouilleuse d’entrailles",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "236"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Arrows: Humming-Bulb",
|
||||||
|
"name": "Flèches : Bulbe bourdonnant",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "236"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Journal",
|
||||||
|
"name": "Journal",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "66"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Smithing hammer",
|
||||||
|
"name": "Marteau de forgeron",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Sumai Garb",
|
||||||
|
"name": "Tenue de Sumai",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "76"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Drafting Paper",
|
||||||
|
"name": "Papier à dessin",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Fine set of Chisels",
|
||||||
|
"name": "Ensemble de burins de grande qualité",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Omamori (Boon of Fukurokujin)",
|
||||||
|
"name": "Omamori (Bienfait de Fukurokujin)",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "243"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Omamori (Boon of Bishamon)",
|
||||||
|
"name": "Omamori (Bienfait de Bishamon)",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "243"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Omamori (Boon of Benten)",
|
||||||
|
"name": "Omamori (Bienfait de Benten)",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "243"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Poison - Noxious Poison (One Vial)",
|
||||||
|
"name": "Poison - Toxines (un flacon)",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "244"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Poison - Fire Biter (One Vial)",
|
||||||
|
"name": "Poison - Morsure Brûlante (un flacon)",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "244"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Poison - Night Milk (One Vial)",
|
||||||
|
"name": "Poison - Lait de la Nuit (un flacon)",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "244"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Blanket",
|
||||||
|
"name": "Couverture",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "245"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Bowl",
|
||||||
|
"name": "Bol",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "245"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Flint and Tinder",
|
||||||
|
"name": "Silex et amadou",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "245"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Furoshiki",
|
||||||
|
"name": "Furoshiki",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "245"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "The Obsidian Journal",
|
||||||
|
"name": "Le Journal d’obsidienne",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "128"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Pouch of Insence",
|
||||||
|
"name": "Bourse d’Encens",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "85"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Religious texts",
|
||||||
|
"name": "Textes religieux",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "86"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Small Sachel of Ingredients",
|
||||||
|
"name": "Petit sachet d'ingrédients",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "80"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Blessed Glass vial",
|
||||||
|
"name": "Fiole en verre bénie",
|
||||||
|
"description": "",
|
||||||
|
"source_reference": {
|
||||||
|
"page": "80"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Afflicted": {
|
"Figure: Afflicted": {
|
||||||
"name": "Tourmenté - Image",
|
"name": "Tourmenté - Image",
|
||||||
"src": "icons/svg/sun.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/afflicted.webp"
|
||||||
},
|
},
|
||||||
"Afflicted": {
|
"Afflicted": {
|
||||||
"name": "Tourmenté",
|
"name": "Tourmenté",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Bleeding": {
|
"Figure: Bleeding": {
|
||||||
"name": "En sang - Image",
|
"name": "En sang - Image",
|
||||||
"src": "icons/svg/blood.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/bleeding.webp"
|
||||||
},
|
},
|
||||||
"Bleeding": {
|
"Bleeding": {
|
||||||
"name": "En sang",
|
"name": "En sang",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Burning": {
|
"Figure: Burning": {
|
||||||
"name": "En feu - Image",
|
"name": "En feu - Image",
|
||||||
"src": "icons/svg/fire.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/burning.webp"
|
||||||
},
|
},
|
||||||
"Burning": {
|
"Burning": {
|
||||||
"name": "En feu",
|
"name": "En feu",
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Compromised": {
|
"Figure: Compromised": {
|
||||||
"name": "Compromis - Image",
|
"name": "Compromis - Image",
|
||||||
"src": "icons/svg/terror.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/compromised.webp"
|
||||||
},
|
},
|
||||||
"Compromised": {
|
"Compromised": {
|
||||||
"name": "Compromis",
|
"name": "Compromis",
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Dazed": {
|
"Figure: Dazed": {
|
||||||
"name": "Hébété - Image",
|
"name": "Hébété - Image",
|
||||||
"src": "icons/svg/eye.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/dazed.webp"
|
||||||
},
|
},
|
||||||
"Dazed": {
|
"Dazed": {
|
||||||
"name": "Hébété",
|
"name": "Hébété",
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Disoriented": {
|
"Figure: Disoriented": {
|
||||||
"name": "Désorienté - Image",
|
"name": "Désorienté - Image",
|
||||||
"src": "icons/svg/daze.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/disoriented.webp"
|
||||||
},
|
},
|
||||||
"Disoriented": {
|
"Disoriented": {
|
||||||
"name": "Désorienté",
|
"name": "Désorienté",
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Dying [X Rounds]": {
|
"Figure: Dying [X Rounds]": {
|
||||||
"name": "Mourant (X rounds) - Image",
|
"name": "Mourant (X rounds) - Image",
|
||||||
"src": "icons/svg/skull.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/dying_1.webp"
|
||||||
},
|
},
|
||||||
"Dying [X Rounds]": {
|
"Dying [X Rounds]": {
|
||||||
"name": "Mourant (X rounds)",
|
"name": "Mourant (X rounds)",
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Enraged": {
|
"Figure: Enraged": {
|
||||||
"name": "Enragé - Image",
|
"name": "Enragé - Image",
|
||||||
"src": "icons/svg/lightning.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/enraged.webp"
|
||||||
},
|
},
|
||||||
"Enraged": {
|
"Enraged": {
|
||||||
"name": "Enragé",
|
"name": "Enragé",
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Exhausted": {
|
"Figure: Exhausted": {
|
||||||
"name": "Epuisé - Image",
|
"name": "Epuisé - Image",
|
||||||
"src": "icons/svg/sleep.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/exhausted.webp"
|
||||||
},
|
},
|
||||||
"Exhausted": {
|
"Exhausted": {
|
||||||
"name": "Epuisé",
|
"name": "Epuisé",
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Immobilized": {
|
"Figure: Immobilized": {
|
||||||
"name": "Immobilisé - Image",
|
"name": "Immobilisé - Image",
|
||||||
"src": "icons/svg/net.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/immobilized.webp"
|
||||||
},
|
},
|
||||||
"Immobilized": {
|
"Immobilized": {
|
||||||
"name": "Immobilisé",
|
"name": "Immobilisé",
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Incapacitated": {
|
"Figure: Incapacitated": {
|
||||||
"name": "Hors de combat - Image",
|
"name": "Hors de combat - Image",
|
||||||
"src": "icons/svg/downgrade.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/incapacitated.webp"
|
||||||
},
|
},
|
||||||
"Incapacitated": {
|
"Incapacitated": {
|
||||||
"name": "Hors de combat",
|
"name": "Hors de combat",
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Intoxicated": {
|
"Figure: Intoxicated": {
|
||||||
"name": "Ivre - Image",
|
"name": "Ivre - Image",
|
||||||
"src": "icons/svg/poison.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/intoxicated.webp"
|
||||||
},
|
},
|
||||||
"Intoxicated": {
|
"Intoxicated": {
|
||||||
"name": "Ivre",
|
"name": "Ivre",
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Prone": {
|
"Figure: Prone": {
|
||||||
"name": "A terre - Image",
|
"name": "A terre - Image",
|
||||||
"src": "icons/svg/falling.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/prone.webp"
|
||||||
},
|
},
|
||||||
"Prone": {
|
"Prone": {
|
||||||
"name": "A terre",
|
"name": "A terre",
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Silenced": {
|
"Figure: Silenced": {
|
||||||
"name": "Aphone - Image",
|
"name": "Aphone - Image",
|
||||||
"src": "icons/svg/silenced.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/silenced.webp"
|
||||||
},
|
},
|
||||||
"Silenced": {
|
"Silenced": {
|
||||||
"name": "Aphone",
|
"name": "Aphone",
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Unconscious": {
|
"Figure: Unconscious": {
|
||||||
"name": "Inconscient - Image",
|
"name": "Inconscient - Image",
|
||||||
"src": "icons/svg/unconscious.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/unconscious.webp"
|
||||||
},
|
},
|
||||||
"Unconscious": {
|
"Unconscious": {
|
||||||
"name": "Inconscient",
|
"name": "Inconscient",
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Wounded": {
|
"Figure: Wounded": {
|
||||||
"name": "Blessé - Image",
|
"name": "Blessé - Image",
|
||||||
"src": "icons/svg/degen.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/heavily_wounded.webp"
|
||||||
},
|
},
|
||||||
"Wounded": {
|
"Wounded": {
|
||||||
"name": "Blessé",
|
"name": "Blessé",
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Illness: Oozing Sore Disease": {
|
"Figure: Illness: Oozing Sore Disease": {
|
||||||
"name": "Maladie : Bubons purulents - Image",
|
"name": "Maladie : Bubons purulents - Image",
|
||||||
"src": "icons/svg/eye.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/illness_oozing_sore_disease.webp"
|
||||||
},
|
},
|
||||||
"Illness: Oozing Sore Disease": {
|
"Illness: Oozing Sore Disease": {
|
||||||
"name": "Maladie : Bubons purulents",
|
"name": "Maladie : Bubons purulents",
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Illness: Gut Sickness": {
|
"Figure: Illness: Gut Sickness": {
|
||||||
"name": "Maladie : Mal des entrailles - Image",
|
"name": "Maladie : Mal des entrailles - Image",
|
||||||
"src": "icons/svg/poison.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/illness_gut_sickness.webp"
|
||||||
},
|
},
|
||||||
"Illness: Gut Sickness": {
|
"Illness: Gut Sickness": {
|
||||||
"name": "Maladie : Mal des entrailles",
|
"name": "Maladie : Mal des entrailles",
|
||||||
@@ -243,7 +243,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Illness: Coughing Illness": {
|
"Figure: Illness: Coughing Illness": {
|
||||||
"name": "Maladie : Mauvaise toux - Image",
|
"name": "Maladie : Mauvaise toux - Image",
|
||||||
"src": "icons/svg/poison.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/illness_coughing_illness.webp"
|
||||||
},
|
},
|
||||||
"Illness: Coughing Illness": {
|
"Illness: Coughing Illness": {
|
||||||
"name": "Maladie : Mauvaise toux",
|
"name": "Maladie : Mauvaise toux",
|
||||||
@@ -256,7 +256,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Illness: Unsteady Illness": {
|
"Figure: Illness: Unsteady Illness": {
|
||||||
"name": "Maladie : Vertiges - Image",
|
"name": "Maladie : Vertiges - Image",
|
||||||
"src": "icons/svg/daze.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/illness_unsteady_illness.webp"
|
||||||
},
|
},
|
||||||
"Illness: Unsteady Illness": {
|
"Illness: Unsteady Illness": {
|
||||||
"name": "Maladie : Vertiges",
|
"name": "Maladie : Vertiges",
|
||||||
@@ -269,7 +269,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Illness: Fire Rash": {
|
"Figure: Illness: Fire Rash": {
|
||||||
"name": "Maladie : Rougeurs - Image",
|
"name": "Maladie : Rougeurs - Image",
|
||||||
"src": "icons/svg/fire.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/illness_fire_rash.webp"
|
||||||
},
|
},
|
||||||
"Illness: Fire Rash": {
|
"Illness: Fire Rash": {
|
||||||
"name": "Maladie : Rougeurs",
|
"name": "Maladie : Rougeurs",
|
||||||
@@ -282,7 +282,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Centered": {
|
"Figure: Centered": {
|
||||||
"name": "Centered (WIP) - Image",
|
"name": "Centered (WIP) - Image",
|
||||||
"src": "systems/l5r5e/assets/icons/social.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/centered.webp"
|
||||||
},
|
},
|
||||||
"Centered": {
|
"Centered": {
|
||||||
"name": "Centered (WIP)",
|
"name": "Centered (WIP)",
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Emboldened": {
|
"Figure: Emboldened": {
|
||||||
"name": "Emboldened (WIP) - Image",
|
"name": "Emboldened (WIP) - Image",
|
||||||
"src": "systems/l5r5e/assets/icons/social.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/emboldened.webp"
|
||||||
},
|
},
|
||||||
"Emboldened": {
|
"Emboldened": {
|
||||||
"name": "Emboldened (WIP)",
|
"name": "Emboldened (WIP)",
|
||||||
@@ -308,7 +308,7 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"Figure: Possessed": {
|
"Figure: Possessed": {
|
||||||
"name": "Possessed (WIP) - Image",
|
"name": "Possessed (WIP) - Image",
|
||||||
"src": "icons/svg/terror.svg"
|
"src": "systems/l5r5e/assets/icons/conditions/possesed.webp"
|
||||||
},
|
},
|
||||||
"Possessed": {
|
"Possessed": {
|
||||||
"name": "Possessed (WIP)",
|
"name": "Possessed (WIP)",
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
"SetTn1OnTypeChange": "Set TN to 1 on encounter change",
|
"SetTn1OnTypeChange": "Set TN to 1 on encounter change",
|
||||||
"SetTn1OnTypeChangeHint": "Set the TN to 1 when the encounter type is selected (Intrigue, Duel, Skirmish or Mass battle)"
|
"SetTn1OnTypeChangeHint": "Set the TN to 1 when the encounter type is selected (Intrigue, Duel, Skirmish or Mass battle)"
|
||||||
},
|
},
|
||||||
|
"ShowAllStatusEffects": {
|
||||||
|
"Title": "Show all StatusEffects",
|
||||||
|
"Hint": "If uncheck (default), only L5R conditions are shown."
|
||||||
|
},
|
||||||
"CustomTechniques": {
|
"CustomTechniques": {
|
||||||
"Title": "Use custom techniques",
|
"Title": "Use custom techniques",
|
||||||
"Hint": "Add 'Specificity' technique type to serve as a catch-all."
|
"Hint": "Add 'Specificity' technique type to serve as a catch-all."
|
||||||
@@ -36,8 +40,8 @@
|
|||||||
},
|
},
|
||||||
"Compendium": {
|
"Compendium": {
|
||||||
"HideDisabledSources": {
|
"HideDisabledSources": {
|
||||||
"Title": "[Compendium] Hide sources filter without reference",
|
"Title": "[Compendium] Hide unavailable sources",
|
||||||
"Hint": "Hide empty source with no elements in source filter."
|
"Hint": "Hide sources that have no available content from the source filter dropdown."
|
||||||
},
|
},
|
||||||
"HideEmptySourcesFromPlayers": {
|
"HideEmptySourcesFromPlayers": {
|
||||||
"Title": "[Compendium] Hide elements with empty reference",
|
"Title": "[Compendium] Hide elements with empty reference",
|
||||||
@@ -80,6 +84,41 @@
|
|||||||
},
|
},
|
||||||
"l5r5e": {
|
"l5r5e": {
|
||||||
"title": "Legend of the Five Rings",
|
"title": "Legend of the Five Rings",
|
||||||
|
"conditions": {
|
||||||
|
"afflicted": "Afflicted",
|
||||||
|
"bleeding": "Bleeding",
|
||||||
|
"burning": "Burning",
|
||||||
|
"centered": "Centered",
|
||||||
|
"compromised": "Compromised",
|
||||||
|
"dazed": "Dazed",
|
||||||
|
"disoriented": "Disoriented",
|
||||||
|
"dying": "Dying",
|
||||||
|
"emboldened": "Emboldened",
|
||||||
|
"enraged": "Enraged",
|
||||||
|
"exhausted": "Exhausted",
|
||||||
|
"immobilized": "Immobilized",
|
||||||
|
"illness_coughing_illness": "Illness: Coughing Illness",
|
||||||
|
"illness_fire_rash": "Illness: Fire Rash",
|
||||||
|
"illness_gut_sickness": "Illness: Gut Sickness",
|
||||||
|
"illness_oozing_sore_disease": "Illness: Oozing Sore Disease",
|
||||||
|
"illness_unsteady_illness": "Illness: Unsteady Illness",
|
||||||
|
"incapacitated": "Incapacitated",
|
||||||
|
"intoxicated": "Intoxicated",
|
||||||
|
"possessed": "Possessed",
|
||||||
|
"prone": "Prone",
|
||||||
|
"silenced": "Silenced",
|
||||||
|
"unconscious": "Unconscious",
|
||||||
|
"lightly_wounded_fire": "Lightly Wounded (Fire)",
|
||||||
|
"lightly_wounded_water": "Lightly Wounded (Water)",
|
||||||
|
"lightly_wounded_air": "Lightly Wounded (Air)",
|
||||||
|
"lightly_wounded_earth": "Lightly Wounded (Earth)",
|
||||||
|
"lightly_wounded_void": "Lightly Wounded (Void)",
|
||||||
|
"severely_wounded_fire": "Severely Wounded (Fire)",
|
||||||
|
"severely_wounded_water": "Severely Wounded (Water)",
|
||||||
|
"severely_wounded_air": "Severely Wounded (Air)",
|
||||||
|
"severely_wounded_earth": "Severely Wounded (Earth)",
|
||||||
|
"severely_wounded_void": "Severely Wounded (Void)"
|
||||||
|
},
|
||||||
"global": {
|
"global": {
|
||||||
"edge_translation_disclaimer": "",
|
"edge_translation_disclaimer": "",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
@@ -97,6 +136,7 @@
|
|||||||
"player_filter_label": "Player filter",
|
"player_filter_label": "Player filter",
|
||||||
"player_filter_tooltip": "Apply player filter",
|
"player_filter_tooltip": "Apply player filter",
|
||||||
"already_in_filter": "Already in filter",
|
"already_in_filter": "Already in filter",
|
||||||
|
"no_results": "Not Found",
|
||||||
"sources_categories": {
|
"sources_categories": {
|
||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"adventures": "Adventures",
|
"adventures": "Adventures",
|
||||||
@@ -453,7 +493,10 @@
|
|||||||
"rarity_modifier": "Rarity modifier",
|
"rarity_modifier": "Rarity modifier",
|
||||||
"item_pattern": "Item Patterns",
|
"item_pattern": "Item Patterns",
|
||||||
"signature_scroll": "Signature Scrolls",
|
"signature_scroll": "Signature Scrolls",
|
||||||
"school_curriculum_journal": "Drop curriculum's journal in sheet to link it"
|
"school_curriculum_journal": "Drop curriculum's journal in sheet to link it",
|
||||||
|
"warning": {
|
||||||
|
"total_less_then_spent": "Total Experience is less then Used Experience."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"character_types": {
|
"character_types": {
|
||||||
"character": "Player Character",
|
"character": "Player Character",
|
||||||
@@ -666,6 +709,7 @@
|
|||||||
"demeanor": {
|
"demeanor": {
|
||||||
"adaptable": "Adaptable",
|
"adaptable": "Adaptable",
|
||||||
"aggressive": "Aggressive",
|
"aggressive": "Aggressive",
|
||||||
|
"alluring": "Alluring",
|
||||||
"ambitious": "Ambitious",
|
"ambitious": "Ambitious",
|
||||||
"amiable": "Amiable",
|
"amiable": "Amiable",
|
||||||
"analytical": "Analytical",
|
"analytical": "Analytical",
|
||||||
@@ -674,6 +718,7 @@
|
|||||||
"assertive": "Assertive",
|
"assertive": "Assertive",
|
||||||
"beguiling": "Beguiling",
|
"beguiling": "Beguiling",
|
||||||
"bitter": "Bitter",
|
"bitter": "Bitter",
|
||||||
|
"bloodthirsty": "Bloodthirsty",
|
||||||
"bold": "Bold",
|
"bold": "Bold",
|
||||||
"calculating": "Calculating",
|
"calculating": "Calculating",
|
||||||
"calm": "Calm",
|
"calm": "Calm",
|
||||||
@@ -684,37 +729,68 @@
|
|||||||
"confused": "Confused",
|
"confused": "Confused",
|
||||||
"courageous": "Courageous",
|
"courageous": "Courageous",
|
||||||
"cowardly": "Cowardly",
|
"cowardly": "Cowardly",
|
||||||
|
"crestfallen": "Crestfallen",
|
||||||
"curious": "Curious",
|
"curious": "Curious",
|
||||||
|
"defensive": "Defensive",
|
||||||
"dependable": "Dependable",
|
"dependable": "Dependable",
|
||||||
"detached": "Detached",
|
"detached": "Detached",
|
||||||
|
"determined": "Determined",
|
||||||
|
"devoted": "Devoted",
|
||||||
|
"direct": "Direct",
|
||||||
"disheartened": "Disheartened",
|
"disheartened": "Disheartened",
|
||||||
|
"dour": "Dour",
|
||||||
|
"duplicitous": "Duplicitous",
|
||||||
|
"effusive": "Effusive",
|
||||||
"enraged": "Enraged",
|
"enraged": "Enraged",
|
||||||
|
"fanatical": "Fanatical",
|
||||||
"feral": "Feral",
|
"feral": "Feral",
|
||||||
|
"fervent": "Fervent",
|
||||||
"fickle": "Fickle",
|
"fickle": "Fickle",
|
||||||
"fierce": "Fierce",
|
"fierce": "Fierce",
|
||||||
"flighty": "Flighty",
|
"flighty": "Flighty",
|
||||||
"flippant": "Flippant",
|
"flippant": "Flippant",
|
||||||
"friendly": "Friendly",
|
"friendly": "Friendly",
|
||||||
"gruff": "Gruff",
|
"gruff": "Gruff",
|
||||||
|
"honorable": "Honorable",
|
||||||
|
"hubristic": "Prétentieuse",
|
||||||
"hungry": "Hungry",
|
"hungry": "Hungry",
|
||||||
|
"idealistic": "Idealistic",
|
||||||
|
"imposing": "Imposing",
|
||||||
|
"inquisitive": "Inquisitive",
|
||||||
"intense": "Intense",
|
"intense": "Intense",
|
||||||
"intimidating": "Intimidating",
|
"intimidating": "Intimidating",
|
||||||
"irritable": "Irritable",
|
"irritable": "Irritable",
|
||||||
"loyal": "Loyal",
|
"loyal": "Loyal",
|
||||||
|
"methodical": "Methodical",
|
||||||
|
"meticulous": "Meticulous",
|
||||||
"mischievous": "Mischievous",
|
"mischievous": "Mischievous",
|
||||||
|
"moon_blessed": "Moon-blessed",
|
||||||
"morose": "Morose",
|
"morose": "Morose",
|
||||||
|
"near_feral": "Near feral",
|
||||||
"nurturing": "Nurturing",
|
"nurturing": "Nurturing",
|
||||||
|
"obsessed": "Obsessed",
|
||||||
"obstinate": "Obstinate",
|
"obstinate": "Obstinate",
|
||||||
"opportunistic": "Opportunistic",
|
"opportunistic": "Opportunistic",
|
||||||
|
"otherworldly": "Otherworldly",
|
||||||
|
"outgoing": "Outgoing",
|
||||||
"passionate": "Passionate",
|
"passionate": "Passionate",
|
||||||
|
"patient": "Patient",
|
||||||
|
"personable": "Personable",
|
||||||
"playful": "Playful",
|
"playful": "Playful",
|
||||||
"power_hungry": "Power hungry",
|
"power_hungry": "Power hungry",
|
||||||
"proud": "Proud",
|
"proud": "Proud",
|
||||||
|
"refined": "Refined",
|
||||||
|
"reserved": "Reserved",
|
||||||
"restrained": "Restrained",
|
"restrained": "Restrained",
|
||||||
|
"righteous": "Righteous",
|
||||||
"scheming": "Scheming",
|
"scheming": "Scheming",
|
||||||
"serene": "Serene",
|
"serene": "Serene",
|
||||||
"serious": "Serious",
|
"serious": "Serious",
|
||||||
"shrewd": "Shrewd",
|
"shrewd": "Shrewd",
|
||||||
|
"sinister": "Sinister",
|
||||||
|
"sociable": "Sociable",
|
||||||
|
"stoic": "Stoic",
|
||||||
|
"starved": "Starved",
|
||||||
"stubborn": "Stubborn",
|
"stubborn": "Stubborn",
|
||||||
"suspicious": "Suspicious",
|
"suspicious": "Suspicious",
|
||||||
"teasing": "Teasing",
|
"teasing": "Teasing",
|
||||||
@@ -722,7 +798,12 @@
|
|||||||
"uncertain": "Uncertain",
|
"uncertain": "Uncertain",
|
||||||
"unenthused": "Unenthused",
|
"unenthused": "Unenthused",
|
||||||
"vain": "Vain",
|
"vain": "Vain",
|
||||||
"wary": "Wary"
|
"vengeful": "Vengeful",
|
||||||
|
"vindictive": "Vindictive",
|
||||||
|
"wary": "Wary",
|
||||||
|
"watchful": "Watchful",
|
||||||
|
"wrathful": "Wrathful",
|
||||||
|
"zealous": "Zealous"
|
||||||
},
|
},
|
||||||
"compendium": {
|
"compendium": {
|
||||||
"filter_rank": "Show Rank",
|
"filter_rank": "Show Rank",
|
||||||
@@ -730,14 +811,15 @@
|
|||||||
"filter": {
|
"filter": {
|
||||||
"rank": "Rank",
|
"rank": "Rank",
|
||||||
"rarity": "Rarity",
|
"rarity": "Rarity",
|
||||||
"ring": "Ring"
|
"ring": "Ring",
|
||||||
|
"clear": "Clear Filter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source_reference": {
|
"source_reference": {
|
||||||
"core_rulebook": "Core Rulebook",
|
"core_rulebook": "Core Rulebook",
|
||||||
"emerald_empire": "Emerald Empire",
|
"emerald_empire": "Emerald Empire",
|
||||||
"shadowlands": "Shadowlands",
|
"shadowlands": "Shadowlands",
|
||||||
"court_of_stones": "Court of Stones",
|
"court_of_stones": "Courts of Stone",
|
||||||
"path_of_waves": "Path of Waves",
|
"path_of_waves": "Path of Waves",
|
||||||
"celestial_realms": "Celestial Realms",
|
"celestial_realms": "Celestial Realms",
|
||||||
"fields_of_victory": "Fields of Victory",
|
"fields_of_victory": "Fields of Victory",
|
||||||
@@ -760,6 +842,32 @@
|
|||||||
"the_scroll_or_the_blade": "The Scroll or the Blade",
|
"the_scroll_or_the_blade": "The Scroll or the Blade",
|
||||||
"legacies_of_war": "Legacies of War",
|
"legacies_of_war": "Legacies of War",
|
||||||
"children_of_the_five_winds": "Children of the Five Winds"
|
"children_of_the_five_winds": "Children of the Five Winds"
|
||||||
|
},
|
||||||
|
"tactical_grid": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Tactical Grid Settings",
|
||||||
|
"label": "Tactical Grid Settings",
|
||||||
|
"hint": "Configures tactical grid range band distances (GM only) and their visual appearance colors and transparency (all users).",
|
||||||
|
"cells": "spaces",
|
||||||
|
"world": {
|
||||||
|
"enabled": "Enable Tactical Grid",
|
||||||
|
"enabled_hint": "Enables or Disable tactical grid for everyone",
|
||||||
|
"start": "Start"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"color": "Color",
|
||||||
|
"alpha": "Alpha"
|
||||||
|
},
|
||||||
|
"range": "Range {index}",
|
||||||
|
"validate": {
|
||||||
|
"start-too-small": "Must be greater than Range Band {previousRangeIndex} ({previousStart})",
|
||||||
|
"start-too-large": "Must be lower then Range Band {nextRangeIndex} ({nextStart})"
|
||||||
|
},
|
||||||
|
"reset": "Reset to Default",
|
||||||
|
"submit": "Save"
|
||||||
|
},
|
||||||
|
"range_band": "Range Band {band}",
|
||||||
|
"range_abbreviation": "RB {range}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,52 +4,56 @@
|
|||||||
"Maintainers": ["Team L5R"]
|
"Maintainers": ["Team L5R"]
|
||||||
},
|
},
|
||||||
"SETTINGS": {
|
"SETTINGS": {
|
||||||
"None": "Sin opción",
|
"None": "Sin opciones",
|
||||||
"ReverseTokenBars": {
|
"ReverseTokenBars": {
|
||||||
"Title": "Reverse tokens bar",
|
"Title": "Barra de tokens inversa",
|
||||||
"Hint": "Change the order in which the bars under the tokens are filled in",
|
"Hint": "Cambia el orden en el que se rellenan las barras debajo de los tokens",
|
||||||
"None": "None",
|
"None": "Ninguno",
|
||||||
"Fatigue": "Fatigue only",
|
"Fatigue": "Sólo Fatiga",
|
||||||
"Strife": "Strife",
|
"Strife": "Conflicto",
|
||||||
"Both": "Both Fatigue and Strife"
|
"Both": "Tanto la Fatiga como el Conflicto"
|
||||||
},
|
},
|
||||||
"RollNKeep": {
|
"RollNKeep": {
|
||||||
"DeleteOldMessage": "TyG Eliminar el mensaje anterior del chat",
|
"DeleteOldMessage": "TyG eliminar el mensaje anterior del chat",
|
||||||
"DeleteOldMessageHint": "Elige si mantener o borrar el mensaje anterior de la serie TyG"
|
"DeleteOldMessageHint": "Elige si mantener o borrar el mensaje anterior de la serie TyG"
|
||||||
},
|
},
|
||||||
"Initiative": {
|
"Initiative": {
|
||||||
"SetTn1OnTypeChange": "Poner el NO a 1 al seleccionar el tipo de encuentro",
|
"SetTn1OnTypeChange": "Poner el NO a 1 al seleccionar el tipo de encuentro",
|
||||||
"SetTn1OnTypeChangeHint": "Poner el NO a 1 cuando se elige el tipo de encuentro (Intriga, Duelo, Escaramuza o Batalla a gran escala)"
|
"SetTn1OnTypeChangeHint": "Poner el NO a 1 cuando se elige el tipo de encuentro (Intriga, Duelo, Escaramuza o Batalla a gran escala)"
|
||||||
},
|
},
|
||||||
|
"ShowAllStatusEffects": {
|
||||||
|
"Title": "Mostrar todos los efectos de estado",
|
||||||
|
"Hint": "Si se desmarca (por defecto), solo se muestran las condiciones de L5A."
|
||||||
|
},
|
||||||
"CustomTechniques": {
|
"CustomTechniques": {
|
||||||
"Title": "Use custom techniques",
|
"Title": "Usar técnicas personalizadas",
|
||||||
"Hint": "Add 'Specificity' technique type to serve as a catch-all."
|
"Hint": "Añadir el tipo de técnica 'Particularidad' para que sirva como comodín."
|
||||||
},
|
},
|
||||||
"CustomCompendiumName": {
|
"CustomCompendiumName": {
|
||||||
"Title": "Custom Compendium Name",
|
"Title": "Nombre de Compendio personalizado",
|
||||||
"Hint": "For advanced users that want to change the name of the custom compendiums (Used to disables the embedded ones).",
|
"Hint": "Permite a los usuarios avanzados cambiar el nombre de los compendios personalizados (utilizados para desactivar los compendios integrados).",
|
||||||
"Notification": "Unable set Custom Compendium: '{name}'. Is it activated and registered with Babele?"
|
"Notification": "No se puede configurar el compendio personalizado: '{name}'. ¿Está activado y registrado con Babele?"
|
||||||
},
|
},
|
||||||
"CustomItemsHeight": {
|
"CustomItemsHeight": {
|
||||||
"Title": "Default items windows height",
|
"Title": "Altura predeterminada de las ventanas de objetos",
|
||||||
"Hint": "Set the default height for 'Items' windows types (techniques, weapons...), in pixels"
|
"Hint": "Establecer la altura predeterminada para las ventanas de 'objetos' (técnicas, armas...), en píxeles."
|
||||||
},
|
},
|
||||||
"Compendium": {
|
"Compendium": {
|
||||||
"HideDisabledSources": {
|
"HideDisabledSources": {
|
||||||
"Title": "[Compendium] Hide sources filter without reference",
|
"Title": "[Compendio] Ocultar filtro de fuentes sin referencia",
|
||||||
"Hint": "Hide empty source with no elements in source filter."
|
"Hint": "Ocultar fuentes vacías sin elementos en el filtro de fuentes."
|
||||||
},
|
},
|
||||||
"HideEmptySourcesFromPlayers": {
|
"HideEmptySourcesFromPlayers": {
|
||||||
"Title": "[Compendium] Hide elements with empty reference",
|
"Title": "[Compendio] Ocultar elementos con referencias vacías",
|
||||||
"Hint": "Basically require a reference to be set in order for players to view the content in compendiums"
|
"Hint": "Requiere que se establezca una referencia para que los jugadores puedan ver el contenido de los compendios."
|
||||||
},
|
},
|
||||||
"AllowedOfficialSources": {
|
"AllowedOfficialSources": {
|
||||||
"Title": "[Compendium] Available official resources",
|
"Title": "[Compendio] Recursos oficiales disponibles",
|
||||||
"Hint": "Useful if you as a GM want to limit the available official content to only books you own"
|
"Hint": "Útil si, como DJ, quieres limitar el contenido oficial disponible solo a los libros que tienes."
|
||||||
},
|
},
|
||||||
"AllowedUnofficialSources": {
|
"AllowedUnofficialSources": {
|
||||||
"Title": "[Compendium] Available unofficial resources",
|
"Title": "[Compendio] Recursos no oficiales disponibles",
|
||||||
"Hint": "Useful if you have compendiums with custom items mixed with player facing items."
|
"Hint": "Útil si tienes compendios con objetos personalizados mezclados con objetos destinados a los jugadores.."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -79,29 +83,65 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"l5r5e": {
|
"l5r5e": {
|
||||||
"title": "Legend of the five Rings",
|
"title": "La Leyenda de los Cinco Anillos",
|
||||||
|
"conditions": {
|
||||||
|
"afflicted": "Afligido",
|
||||||
|
"bleeding": "Hemorragia",
|
||||||
|
"burning": "Ardiendo",
|
||||||
|
"centered": "Centrado",
|
||||||
|
"compromised": "Comprometido",
|
||||||
|
"dazed": "Atontado",
|
||||||
|
"disoriented": "Desorientado",
|
||||||
|
"dying": "Moribundo",
|
||||||
|
"emboldened": "Alentado",
|
||||||
|
"enraged": "Enfurecido",
|
||||||
|
"exhausted": "Agotado",
|
||||||
|
"immobilized": "Inmovilizado",
|
||||||
|
"illness_coughing_illness": "Enfermedad: tos enfermiza",
|
||||||
|
"illness_fire_rash": "Enfermedad: sarpullido de fuego",
|
||||||
|
"illness_gut_sickness": "Enfermedad: malestar intestinal",
|
||||||
|
"illness_oozing_sore_disease": "Enfermedad: llagas supurantes",
|
||||||
|
"illness_unsteady_illness": "Enfermedad: temblores",
|
||||||
|
"incapacitated": "Incapacitado",
|
||||||
|
"intoxicated": "Intoxicado",
|
||||||
|
"possessed": "Poseído",
|
||||||
|
"prone": "Tumbado",
|
||||||
|
"silenced": "Silenciado",
|
||||||
|
"unconscious": "Inconsciente",
|
||||||
|
"lightly_wounded_fire": "Herida leve (Fuego)",
|
||||||
|
"lightly_wounded_water": "Herida leve (Agua)",
|
||||||
|
"lightly_wounded_air": "Herida leve (Aire)",
|
||||||
|
"lightly_wounded_earth": "Herida leve (Tierra)",
|
||||||
|
"lightly_wounded_void": "Herida leve (Vacío)",
|
||||||
|
"severely_wounded_fire": "Herida grave (Fuego)",
|
||||||
|
"severely_wounded_water": "Herida grave (Agua)",
|
||||||
|
"severely_wounded_air": "Herida grave (Aire)",
|
||||||
|
"severely_wounded_earth": "Herida grave (Tierra)",
|
||||||
|
"severely_wounded_void": "Herida grave (Vacío)"
|
||||||
|
},
|
||||||
"global": {
|
"global": {
|
||||||
"edge_translation_disclaimer": "Edge Studio nos da su permiso para ofrecer este módulo a la comunidad, pero tanto los textos así como los códigos que lo constituyen no tienen su aprobación explícita.",
|
"edge_translation_disclaimer": "Edge Studio nos da su permiso para ofrecer este módulo a la comunidad, pero tanto los textos así como los códigos que lo constituyen no tienen su aprobación explícita.",
|
||||||
"add": "Añadir",
|
"add": "Añadir",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"delete_confirm": "¿Estás seguro de que quieres borrar '{name}'?",
|
"delete_confirm": "¿Estás seguro de que quieres borrar '{name}'?",
|
||||||
"drop_here": "Dejar caer aquí",
|
"drop_here": "Dejar caer aquí",
|
||||||
"send_to_chat": "To Chat",
|
"send_to_chat": "Al Chat",
|
||||||
"locked": "Locked",
|
"locked": "Bloqueado",
|
||||||
"unlocked": "Unlocked",
|
"unlocked": "Desbloqueado",
|
||||||
"random": "Random"
|
"random": "Aleatorio"
|
||||||
},
|
},
|
||||||
"multiselect": {
|
"multiselect": {
|
||||||
"empty_tag": "<blank>",
|
"empty_tag": "<blank>",
|
||||||
"placeholder": "Filter Sources",
|
"placeholder": "Filtro de recursos",
|
||||||
"player_filter_label": "Player filter",
|
"player_filter_label": "Filtro de jugador",
|
||||||
"player_filter_tooltip": "Apply player filter",
|
"player_filter_tooltip": "Aplicar filtro de jugador",
|
||||||
"already_in_filter": "Already in filter",
|
"already_in_filter": "Ya en el filtro",
|
||||||
|
"no_results": "Not Found",
|
||||||
"sources_categories": {
|
"sources_categories": {
|
||||||
"rules": "Rules",
|
"rules": "Reglas",
|
||||||
"adventures": "Adventures",
|
"adventures": "Aventuras",
|
||||||
"supplements": "Supplements",
|
"supplements": "Suplementos",
|
||||||
"others": "Others"
|
"others": "Otros"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"logo": {
|
"logo": {
|
||||||
@@ -121,19 +161,19 @@
|
|||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
"title": "Discord oficial de FoundryVTT",
|
"title": "Discord oficial de FoundryVTT",
|
||||||
"info": "Su navegador se abrirá el discord oficial de Foundry",
|
"info": "Tu navegador abrirá el discord oficial de Foundry",
|
||||||
"link": "https://discordapp.com/invite/DDBZUDf"
|
"link": "https://discordapp.com/invite/DDBZUDf"
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
"title": "Changelog",
|
"title": "Registro de cambios",
|
||||||
"link": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/CHANGELOG.md"
|
"link": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/CHANGELOG.md"
|
||||||
},
|
},
|
||||||
"issues": {
|
"issues": {
|
||||||
"title": "Issues",
|
"title": "Problemas",
|
||||||
"link": "https://gitlab.com/teaml5r/l5r5e/-/issues"
|
"link": "https://gitlab.com/teaml5r/l5r5e/-/issues"
|
||||||
},
|
},
|
||||||
"custom-compendiums": {
|
"custom-compendiums": {
|
||||||
"title": "Compendiums",
|
"title": "Compendios",
|
||||||
"link": "https://gitlab.com/teaml5r/l5r5e/-/wikis/users/custom-compendiums.md"
|
"link": "https://gitlab.com/teaml5r/l5r5e/-/wikis/users/custom-compendiums.md"
|
||||||
},
|
},
|
||||||
"wiki": {
|
"wiki": {
|
||||||
@@ -192,10 +232,10 @@
|
|||||||
"success_text": "¡Éxito!",
|
"success_text": "¡Éxito!",
|
||||||
"bonus_text": "Éxitos adicionales",
|
"bonus_text": "Éxitos adicionales",
|
||||||
"fail_text": "¡Fallo!",
|
"fail_text": "¡Fallo!",
|
||||||
"unknown_target": "Unknown target"
|
"unknown_target": "Objetivo desconocido"
|
||||||
},
|
},
|
||||||
"dicepicker": {
|
"dicepicker": {
|
||||||
"title": "Dice Picker",
|
"title": "Selector de dados",
|
||||||
"difficulty_title": "Dificultad",
|
"difficulty_title": "Dificultad",
|
||||||
"difficulty_hidden_label": "Ocultar NO",
|
"difficulty_hidden_label": "Ocultar NO",
|
||||||
"use_void_point_label": "Gasta un",
|
"use_void_point_label": "Gasta un",
|
||||||
@@ -203,7 +243,7 @@
|
|||||||
"skill_assistance_label": "Asistencia",
|
"skill_assistance_label": "Asistencia",
|
||||||
"roll_label": "Tirar",
|
"roll_label": "Tirar",
|
||||||
"bt_add_macro": "Añadir una macro",
|
"bt_add_macro": "Añadir una macro",
|
||||||
"gm_request_dp_to_players": "Roll request sent to players"
|
"gm_request_dp_to_players": "Solicitud de tirada enviada a los jugadores"
|
||||||
},
|
},
|
||||||
"roll_n_keep": {
|
"roll_n_keep": {
|
||||||
"title": "Tirar y guardar",
|
"title": "Tirar y guardar",
|
||||||
@@ -213,13 +253,13 @@
|
|||||||
"keep_drop_here": "Guardar",
|
"keep_drop_here": "Guardar",
|
||||||
"max": "Máx",
|
"max": "Máx",
|
||||||
"bt_validate": "Terminar este paso",
|
"bt_validate": "Terminar este paso",
|
||||||
"bt_strife": "Apply strife",
|
"bt_strife": "Aplicar Conflicto",
|
||||||
"undo": "[GM] Deshacer los últimos cambios"
|
"undo": "[GM] Deshacer los últimos cambios"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gm": {
|
"gm": {
|
||||||
"toolbox": {
|
"toolbox": {
|
||||||
"title": "GM ToolBox",
|
"title": "Caja de herramientas del DJ",
|
||||||
"difficulty_hidden": "Cambiar la dificultad visible",
|
"difficulty_hidden": "Cambiar la dificultad visible",
|
||||||
"difficulty": "Cambiar dificultad (Izquierda: añadir, Derecha: sustraer, central: NO 2)",
|
"difficulty": "Cambiar dificultad (Izquierda: añadir, Derecha: sustraer, central: NO 2)",
|
||||||
"sleep": "Descanso confortable para todos los personajes (Eliminar fatiga = Agua x2. (Click Izquierdo: sólo a los personajes seleccionados. Derecho: a todos los actores)",
|
"sleep": "Descanso confortable para todos los personajes (Eliminar fatiga = Agua x2. (Click Izquierdo: sólo a los personajes seleccionados. Derecho: a todos los actores)",
|
||||||
@@ -235,7 +275,7 @@
|
|||||||
"add_selected_tokens": "Add selected tokens",
|
"add_selected_tokens": "Add selected tokens",
|
||||||
"honor_glory_status": "H/G/E",
|
"honor_glory_status": "H/G/E",
|
||||||
"focus_vigilance": "Con./Ale.",
|
"focus_vigilance": "Con./Ale.",
|
||||||
"mouse_control": "Click Izquierdo +1, Derecho: -1, middle: set to 0"
|
"mouse_control": "Click Izquierdo +1, Derecho: -1, medio: poner a 0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"weapons": {
|
"weapons": {
|
||||||
@@ -268,7 +308,7 @@
|
|||||||
"type": "Técnicas permitidas",
|
"type": "Técnicas permitidas",
|
||||||
"kata": "Kata",
|
"kata": "Kata",
|
||||||
"kiho": "Kihõ",
|
"kiho": "Kihõ",
|
||||||
"inversion": "Inversion",
|
"inversion": "Inversión",
|
||||||
"invocation": "Invocación",
|
"invocation": "Invocación",
|
||||||
"ritual": "Ritual",
|
"ritual": "Ritual",
|
||||||
"shuji": "Shuji",
|
"shuji": "Shuji",
|
||||||
@@ -278,7 +318,7 @@
|
|||||||
"school_ability": "Capacidad de escuela",
|
"school_ability": "Capacidad de escuela",
|
||||||
"mastery_ability": "Habilidad de maestría",
|
"mastery_ability": "Habilidad de maestría",
|
||||||
"title_ability": "Capacidad de título",
|
"title_ability": "Capacidad de título",
|
||||||
"specificity": "Specificity"
|
"specificity": "Particularidad"
|
||||||
},
|
},
|
||||||
"peculiarities": {
|
"peculiarities": {
|
||||||
"types": {
|
"types": {
|
||||||
@@ -301,7 +341,7 @@
|
|||||||
"status": "Estatus",
|
"status": "Estatus",
|
||||||
"ninjo": "Ninjo",
|
"ninjo": "Ninjo",
|
||||||
"giri": "Giri",
|
"giri": "Giri",
|
||||||
"past": "Past",
|
"past": "Pasado",
|
||||||
"bushido_tenets": {
|
"bushido_tenets": {
|
||||||
"title": "Preceptos del Bushidō",
|
"title": "Preceptos del Bushidō",
|
||||||
"paramount": "Más importante",
|
"paramount": "Más importante",
|
||||||
@@ -318,20 +358,20 @@
|
|||||||
"combat": "Combate",
|
"combat": "Combate",
|
||||||
"intrigue": "Intriga"
|
"intrigue": "Intriga"
|
||||||
},
|
},
|
||||||
"age": "Age",
|
"age": "Edad",
|
||||||
"children": "Children",
|
"children": "Vástagos",
|
||||||
"marital_status": {
|
"marital_status": {
|
||||||
"title": "Marital Status",
|
"title": "Estado civil",
|
||||||
"partner": "Partner",
|
"partner": "Pareja",
|
||||||
"married": "Married",
|
"married": "Casado",
|
||||||
"betrothed": "Betrothed",
|
"betrothed": "Prometido",
|
||||||
"unmarried": "Unmarried",
|
"unmarried": "Soltero",
|
||||||
"widowed": "Widowed"
|
"widowed": "Viudo"
|
||||||
},
|
},
|
||||||
"gender": {
|
"gender": {
|
||||||
"title": "Gender",
|
"title": "Género",
|
||||||
"male": "Male",
|
"male": "Masculino",
|
||||||
"female": "Female"
|
"female": "Femenino"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"skills": {
|
"skills": {
|
||||||
@@ -453,7 +493,10 @@
|
|||||||
"rarity_modifier": "Modificador de rareza",
|
"rarity_modifier": "Modificador de rareza",
|
||||||
"item_pattern": "Patrones de objetos",
|
"item_pattern": "Patrones de objetos",
|
||||||
"signature_scroll": "Pergaminos espaciales",
|
"signature_scroll": "Pergaminos espaciales",
|
||||||
"school_curriculum_journal": "Arrastra el diario del programa en la hoja para vincularlo"
|
"school_curriculum_journal": "Arrastra el diario del programa en la hoja para vincularlo",
|
||||||
|
"warning": {
|
||||||
|
"total_less_then_spent": "La experiencia total es menor que la experiencia utilizada."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"character_types": {
|
"character_types": {
|
||||||
"character": "Personaje jugador",
|
"character": "Personaje jugador",
|
||||||
@@ -461,33 +504,33 @@
|
|||||||
"minion": "Esbirro"
|
"minion": "Esbirro"
|
||||||
},
|
},
|
||||||
"army": {
|
"army": {
|
||||||
"warlord": "Warlord",
|
"warlord": "Señor de la guerra",
|
||||||
"allies_backers": "Allies and Backers",
|
"allies_backers": "Aliados y apoyos",
|
||||||
"purpose_mustering": "Purpose for Mustering",
|
"purpose_mustering": "Propósito de la movilización",
|
||||||
"battle_readiness": {
|
"battle_readiness": {
|
||||||
"title": "Battle Readiness",
|
"title": "Preparación para la batalla",
|
||||||
"strength": "Strength",
|
"strength": "Fuerza",
|
||||||
"casualties": "Casualties",
|
"casualties": "Bajas",
|
||||||
"discipline": "Discipline",
|
"discipline": "Disciplina",
|
||||||
"panic": "Panic"
|
"panic": "Pánico"
|
||||||
},
|
},
|
||||||
"commander": "Commander",
|
"commander": "Comandante",
|
||||||
"commander_abilities": "Commander's relevant abilities",
|
"commander_abilities": "Habilidades relevantes del comandante",
|
||||||
"army_abilities": "Army Abilities",
|
"army_abilities": "Habilidades del ejército",
|
||||||
"commander_standing": "Commander's Standing",
|
"commander_standing": "Posición del comandante",
|
||||||
"supplies_logistics": "Supplies and Logistics",
|
"supplies_logistics": "Logística y suministros",
|
||||||
"past_battles": "Past Battles",
|
"past_battles": "Batallas anteriores",
|
||||||
"cohort": {
|
"cohort": {
|
||||||
"tab": "Cohorts",
|
"tab": "Cohortes",
|
||||||
"title": "Cohort",
|
"title": "Cohorte",
|
||||||
"leader": "Leader",
|
"leader": "Líder",
|
||||||
"abilities": "Abilities"
|
"abilities": "Habilidades"
|
||||||
},
|
},
|
||||||
"fortification": {
|
"fortification": {
|
||||||
"tab": "Fortifications",
|
"tab": "Fortificaciones",
|
||||||
"title": "Fortification Held",
|
"title": "Fortificación",
|
||||||
"difficulty": "Difficulty Value",
|
"difficulty": "Valor de dificultad",
|
||||||
"attrition_reduction": "Attrition Reduction"
|
"attrition_reduction": "Reducción del desgaste"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"twenty_questions": {
|
"twenty_questions": {
|
||||||
@@ -514,19 +557,19 @@
|
|||||||
},
|
},
|
||||||
"part1": {
|
"part1": {
|
||||||
"title": "Parte I: Identidad básica (Clan y Familia)",
|
"title": "Parte I: Identidad básica (Clan y Familia)",
|
||||||
"title_pow": "Part I: Core Identity (Region and Upbringing)",
|
"title_pow": "Parte I: Identidad básica (Región y educación)",
|
||||||
"q1": "1. ¿A qué clan pertenece tu personaje? (p. 41)",
|
"q1": "1. ¿A qué clan pertenece tu personaje? (p. 41)",
|
||||||
"q1_pow": "1. What region does your character come from? (p. 31)",
|
"q1_pow": "1. ¿De que región viene tu personaje? (p. 31)",
|
||||||
"status": "Estatus",
|
"status": "Estatus",
|
||||||
"q2": "2. ¿A qué familia pertenece tu personaje? (p. 49)",
|
"q2": "2. ¿A qué familia pertenece tu personaje? (p. 49)",
|
||||||
"q2_pow": "2. What was your character’s upbringing? (p. 43)",
|
"q2_pow": "2. ¿Cual fue la educación de tu personaje? (p. 43)",
|
||||||
"money": "Riqueza inicial en Koku",
|
"money": "Riqueza inicial en Koku",
|
||||||
"glory": "Gloria"
|
"glory": "Gloria"
|
||||||
},
|
},
|
||||||
"part2": {
|
"part2": {
|
||||||
"title": "Parte II: Función y escuela",
|
"title": "Parte II: Función y escuela",
|
||||||
"q3": "3. ¿Cuál es la escuela de tu personaje, y en qué funciones cumple esa escuela? (p. 56)",
|
"q3": "3. ¿Cuál es la escuela de tu personaje, y en qué funciones cumple esa escuela? (p. 56)",
|
||||||
"q3_pow": "3. What is your character’s school, and what are its associated roles? (p. 46)",
|
"q3_pow": "3. ¿Cuál es la escuela de tu personaje y cuáles son sus funciones asociadas? (p. 46)",
|
||||||
"school": "Escuela",
|
"school": "Escuela",
|
||||||
"role": "Funciones",
|
"role": "Funciones",
|
||||||
"honor": "Honor",
|
"honor": "Honor",
|
||||||
@@ -535,23 +578,23 @@
|
|||||||
"starting_techniques": "Técnicas iniciales (2-6)",
|
"starting_techniques": "Técnicas iniciales (2-6)",
|
||||||
"outfit": "Equipo inicial",
|
"outfit": "Equipo inicial",
|
||||||
"q4": "4. ¿De qué manera destaca tu personaje dentro de su escuela? (p. 88)",
|
"q4": "4. ¿De qué manera destaca tu personaje dentro de su escuela? (p. 88)",
|
||||||
"q4_pow": "4. What gets your character in and out of trouble? (p. 60)"
|
"q4_pow": "4. ¿Qué es lo que mete y saca a tu personaje de problemas? (p. 60)"
|
||||||
},
|
},
|
||||||
"part3": {
|
"part3": {
|
||||||
"title": "Parte III: Honor y Gloria",
|
"title": "Parte III: Honor y Gloria",
|
||||||
"title_pow": "Part III: The Past and the Future they interact and process",
|
"title_pow": "Parte III: El pasado y el futuro interactúan y se funden.",
|
||||||
"q5": "5. ¿Quién es tu señor y cuál es el deber de tu personaje hacia él? (p. 88)",
|
"q5": "5. ¿Quién es tu señor y cuál es el deber de tu personaje hacia él? (p. 88)",
|
||||||
"q5_pow": "5. What is your character’s past and how does it Affect them? (p. 60)",
|
"q5_pow": "5. ¿Cuál es el pasado de tu personaje y cómo le afecta? (p. 60)",
|
||||||
"choose_giri": "Elige un giri:",
|
"choose_giri": "Elige un giri:",
|
||||||
"choose_past": "Select past",
|
"choose_past": "Elige un pasado:",
|
||||||
"q6": "6. ¿Qué es lo que anhela tu personaje, y cómo podría esto interferir con su deber? (p. 90)",
|
"q6": "6. ¿Qué es lo que anhela tu personaje, y cómo podría esto interferir con su deber? (p. 90)",
|
||||||
"q6_pow": "6. What does your character long for, and how might their past impact their ninjō? (p. 62)",
|
"q6_pow": "6. ¿Qué anhela tu personaje y cómo podría afectar su pasado a su ninjō? (p. 62)",
|
||||||
"choose_ninjo": "Elige un ninjō:",
|
"choose_ninjo": "Elige un ninjō:",
|
||||||
"q7": "7. ¿Cuál es la relación de tu personaje con tu clan? (p. 91)",
|
"q7": "7. ¿Cuál es la relación de tu personaje con tu clan? (p. 91)",
|
||||||
"q7_pow": "7. What is your character known for? (p. 61)",
|
"q7_pow": "7. ¿Por qué es conocido tu personaje? (p. 61)",
|
||||||
"increase_glory": "Aumento de la gloria",
|
"increase_glory": "Aumento de la gloria",
|
||||||
"q8": "8. ¿Qué piensa tu personaje acerca del Bushidō? (p. 91)",
|
"q8": "8. ¿Qué piensa tu personaje acerca del Bushidō? (p. 91)",
|
||||||
"q8_pow": "8. What does your character think of Bushidō? (p. 62)",
|
"q8_pow": "8. ¿Qué piensa tu personaje acerca del Bushidō? (p. 62)",
|
||||||
"increase_honor": "Aumento del honor",
|
"increase_honor": "Aumento del honor",
|
||||||
"tenets": "Escoge un precepto del Bushidō más importante y un precepto como menos significativo (ver las opiniones de los Clanes respecto del Bushidō, página 301 del libro de reglas básicas):",
|
"tenets": "Escoge un precepto del Bushidō más importante y un precepto como menos significativo (ver las opiniones de los Clanes respecto del Bushidō, página 301 del libro de reglas básicas):",
|
||||||
"object": "Objeto (Rareza 5 o inferior)"
|
"object": "Objeto (Rareza 5 o inferior)"
|
||||||
@@ -578,22 +621,22 @@
|
|||||||
"part5": {
|
"part5": {
|
||||||
"title": "Parte V: Personalidad y Comportamiento",
|
"title": "Parte V: Personalidad y Comportamiento",
|
||||||
"q14": "14. ¿Qué es lo que advierte primero la gente al encontrarse con tu personaje? (p. 93)",
|
"q14": "14. ¿Qué es lo que advierte primero la gente al encontrarse con tu personaje? (p. 93)",
|
||||||
"q14_pow": "14. What is your character’s most prized possession? (p. 66)",
|
"q14_pow": "14. ¿Cuál es la posesión más preciada de tu personaje? (p. 66)",
|
||||||
"accoutrement": "Accesorio estético distintivo",
|
"accoutrement": "Accesorio estético distintivo",
|
||||||
"q15": "15. ¿Cómo reacciona tu personaje ante situaciones de tensión? (p. 94)",
|
"q15": "15. ¿Cómo reacciona tu personaje ante situaciones de tensión? (p. 94)",
|
||||||
"q15_pow": "15. ¿Cómo reacciona tu personaje ante situaciones de tensión? (p. 66)",
|
"q15_pow": "15. ¿Cómo reacciona tu personaje ante situaciones de tensión? (p. 66)",
|
||||||
"q16": "16. ¿Cuáles son las relaciones previas de tu personaje con otros clanes, familias, organizaciones y tradiciones? (p. 94)",
|
"q16": "16. ¿Cuáles son las relaciones previas de tu personaje con otros clanes, familias, organizaciones y tradiciones? (p. 94)",
|
||||||
"q16_pow": "16. What are your relationships to your family, the clans, peasants, and others? (p. 66)",
|
"q16_pow": "16. ¿Cómo son tus relaciones con tu familia, los clanes, los campesinos y demás? (p. 66)",
|
||||||
"object": "Objeto (Rareza 7 o inferior)"
|
"object": "Objeto (Rareza 7 o inferior)"
|
||||||
},
|
},
|
||||||
"part6": {
|
"part6": {
|
||||||
"title": "Parte VI: Ascestros y familia",
|
"title": "Parte VI: Ascestros y familia",
|
||||||
"title_pow": "Part VI: Ancestry and Bonds",
|
"title_pow": "Parte VI: Ascestros y vínculos",
|
||||||
"q17": "17. ¿Cómo describirían sus padres a tu personaje? (p. 95)",
|
"q17": "17. ¿Cómo describirían sus padres a tu personaje? (p. 95)",
|
||||||
"q17_pow": "17. What shared history do you have with your group? (p. 66)",
|
"q17_pow": "17. ¿Qué historia compartes con tu grupo? (p. 66)",
|
||||||
"bond": "Determine an appropriate bond to apply to your relationship",
|
"bond": "Determina el vínculo adecuado que debes aplicar a tu relación.",
|
||||||
"q18": "18. ¿En honor de quién se eligio el nombre de tu personaje? (p. 95)",
|
"q18": "18. ¿En honor de quién se eligio el nombre de tu personaje? (p. 95)",
|
||||||
"q18_pow": "18. Who raised you? (p. 67)",
|
"q18_pow": "18. ¿Quién te crió? (p. 67)",
|
||||||
"d10r1": "Resultado D10 (1/2)",
|
"d10r1": "Resultado D10 (1/2)",
|
||||||
"d10r1_choice": "Primer efecto de D10",
|
"d10r1_choice": "Primer efecto de D10",
|
||||||
"d10r2": "Resultado D10 (2/2)",
|
"d10r2": "Resultado D10 (2/2)",
|
||||||
@@ -613,153 +656,218 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"char_generator": {
|
"char_generator": {
|
||||||
"title": "Character Generator",
|
"title": "Generador de personajes",
|
||||||
"head_bt_title": "Char. Generator",
|
"head_bt_title": "Generador de pj",
|
||||||
"generate": "Generate",
|
"generate": "Generar",
|
||||||
"average_value": "Average value",
|
"average_value": "Valor medio",
|
||||||
"identity": "Clan, gender, age, marital status",
|
"identity": "Clan, género, edad, estado civil",
|
||||||
"attributes": "Social standing, Rings, Attributes and Skills",
|
"attributes": "Posición social, Anillos, Atributos y Habilidades",
|
||||||
"demeanor": "Demeanor & rings affinities",
|
"demeanor": "Comportamiento y afinidades con los anillos",
|
||||||
"peculiarities": "Advantages and Disadvantages",
|
"peculiarities": "Ventajas y desventajas",
|
||||||
"items": "Armors, Weapons, and Items",
|
"items": "Armaduras, armas y objetos.",
|
||||||
"narrative": "Narrative (Description)"
|
"narrative": "Historia (descripción)"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"title": "Funciones",
|
"title": "Funciones",
|
||||||
"artisan": "Artisan",
|
"artisan": "Artesano",
|
||||||
"bushi": "Bushi",
|
"bushi": "Bushi",
|
||||||
"courtier": "Courtier",
|
"courtier": "Cortesano",
|
||||||
"monk": "Monk",
|
"monk": "Monje",
|
||||||
"sage": "Sage",
|
"sage": "Sabio",
|
||||||
"shinobi": "Shinobi",
|
"shinobi": "Shinobi",
|
||||||
"shugenja": "Shugenja"
|
"shugenja": "Shugenja"
|
||||||
},
|
},
|
||||||
"clans": {
|
"clans": {
|
||||||
"title": "Clans",
|
"title": "Clanes",
|
||||||
"label": "Clan",
|
"label": "Clan",
|
||||||
"imperial": "Imperial",
|
"imperial": "Imperial",
|
||||||
"crab": "Crab",
|
"crab": "Cangrejo",
|
||||||
"crane": "Crane",
|
"crane": "Grulla",
|
||||||
"dragon": "Dragon",
|
"dragon": "Dragón",
|
||||||
"lion": "Lion",
|
"lion": "León",
|
||||||
"phoenix": "Phoenix",
|
"phoenix": "Fénix",
|
||||||
"scorpion": "Scorpion",
|
"scorpion": "Escorpión",
|
||||||
"unicorn": "Unicorn",
|
"unicorn": "Unicornio",
|
||||||
"mantis": "Mantis",
|
"mantis": "Mantis",
|
||||||
"ronin": "Ronin",
|
"ronin": "Ronin",
|
||||||
"badger": "Badger",
|
"badger": "Tejón",
|
||||||
"bat": "Bat",
|
"bat": "Muerciélago",
|
||||||
"boar": "Boar",
|
"boar": "Jabalí",
|
||||||
"dragonfly": "Dragonfly",
|
"dragonfly": "Libélula",
|
||||||
"firefly": "Firefly",
|
"firefly": "Luciérnaga",
|
||||||
"fox": "Fox",
|
"fox": "Zorro",
|
||||||
"hare": "Hare",
|
"hare": "Liebre",
|
||||||
"monkey": "Monkey",
|
"monkey": "Mono",
|
||||||
"oriole": "Oriole",
|
"oriole": "Oropéndula",
|
||||||
"ox": "Ox",
|
"ox": "Buey",
|
||||||
"sparrow": "Sparrow",
|
"sparrow": "Gorrión",
|
||||||
"tortoise": "Tortoise",
|
"tortoise": "Tortuga",
|
||||||
"ivory_kingdoms": "Ivory Kingdoms",
|
"ivory_kingdoms": "Reinos de Marfil",
|
||||||
"qamarist": "Qamarist",
|
"qamarist": "Qamarista",
|
||||||
"ujik": "Ujik"
|
"ujik": "Ujik"
|
||||||
},
|
},
|
||||||
"demeanor": {
|
"demeanor": {
|
||||||
"adaptable": "Adaptable",
|
"adaptable": "Adaptable",
|
||||||
"aggressive": "Aggressive",
|
"aggressive": "Agresivo",
|
||||||
"ambitious": "Ambitious",
|
"alluring": "Alluring",
|
||||||
"amiable": "Amiable",
|
"ambitious": "Ambicioso",
|
||||||
"analytical": "Analytical",
|
"amiable": "Amigable",
|
||||||
"angry": "Angry",
|
"analytical": "Analítico",
|
||||||
"arrogant": "Arrogant",
|
"angry": "Enojado",
|
||||||
"assertive": "Assertive",
|
"arrogant": "Arrogante",
|
||||||
"beguiling": "Beguiling",
|
"assertive": "Firme",
|
||||||
"bitter": "Bitter",
|
"beguiling": "Seductor",
|
||||||
"bold": "Bold",
|
"bitter": "Amargado",
|
||||||
"calculating": "Calculating",
|
"bloodthirsty": "Bloodthirsty",
|
||||||
"calm": "Calm",
|
"bold": "Atrevido",
|
||||||
"capricious": "Capricious",
|
"calculating": "Calculador",
|
||||||
"cautious": "Cautious",
|
"calm": "Calmado",
|
||||||
"clever": "Clever",
|
"capricious": "Caprichoso",
|
||||||
"compassionate": "Compassionate",
|
"cautious": "Cuidadoso",
|
||||||
"confused": "Confused",
|
"clever": "Ingenioso",
|
||||||
"courageous": "Courageous",
|
"compassionate": "Compasivo",
|
||||||
"cowardly": "Cowardly",
|
"confused": "Confuso",
|
||||||
"curious": "Curious",
|
"courageous": "Valiente",
|
||||||
"dependable": "Dependable",
|
"cowardly": "Cobarde",
|
||||||
"detached": "Detached",
|
"crestfallen": "Crestfallen",
|
||||||
"disheartened": "Disheartened",
|
"curious": "Curioso",
|
||||||
"enraged": "Enraged",
|
"defensive": "Defensive",
|
||||||
"feral": "Feral",
|
"dependable": "Fiable",
|
||||||
"fickle": "Fickle",
|
"detached": "Desapegado",
|
||||||
"fierce": "Fierce",
|
"determined": "Determined",
|
||||||
"flighty": "Flighty",
|
"devoted": "Devoted",
|
||||||
"flippant": "Flippant",
|
"direct": "Direct",
|
||||||
"friendly": "Friendly",
|
"disheartened": "Desanimado",
|
||||||
"gruff": "Gruff",
|
"dour": "Dour",
|
||||||
"hungry": "Hungry",
|
"duplicitous": "Duplicitous",
|
||||||
"intense": "Intense",
|
"effusive": "Effusive",
|
||||||
"intimidating": "Intimidating",
|
"enraged": "Furioso",
|
||||||
|
"fanatical": "Fanatical",
|
||||||
|
"feral": "Salvaje",
|
||||||
|
"fervent": "Fervent",
|
||||||
|
"fickle": "Voluble",
|
||||||
|
"fierce": "Fiero",
|
||||||
|
"flighty": "Veleidoso",
|
||||||
|
"flippant": "Frívolo",
|
||||||
|
"friendly": "Amable",
|
||||||
|
"gruff": "Hosco",
|
||||||
|
"honorable": "Honorable",
|
||||||
|
"hubristic": "Hubristic",
|
||||||
|
"hungry": "Hambriento",
|
||||||
|
"idealistic": "Idealistic",
|
||||||
|
"imposing": "Imposing",
|
||||||
|
"inquisitive": "Inquisitive",
|
||||||
|
"intense": "Intenso",
|
||||||
|
"intimidating": "Intimidante",
|
||||||
"irritable": "Irritable",
|
"irritable": "Irritable",
|
||||||
"loyal": "Loyal",
|
"loyal": "Leal",
|
||||||
"mischievous": "Mischievous",
|
"methodical": "Methodical",
|
||||||
"morose": "Morose",
|
"meticulous": "Meticulous",
|
||||||
"nurturing": "Nurturing",
|
"mischievous": "Travieso",
|
||||||
"obstinate": "Obstinate",
|
"moon_blessed": "Moon-blessed",
|
||||||
"opportunistic": "Opportunistic",
|
"morose": "Taciturno",
|
||||||
"passionate": "Passionate",
|
"near_feral": "Near feral",
|
||||||
"playful": "Playful",
|
"nurturing": "Animador",
|
||||||
"power_hungry": "Power hungry",
|
"obsessed": "Obsessed",
|
||||||
"proud": "Proud",
|
"obstinate": "Obstinado",
|
||||||
"restrained": "Restrained",
|
"opportunistic": "Oportunista",
|
||||||
"scheming": "Scheming",
|
"otherworldly": "Otherworldly",
|
||||||
"serene": "Serene",
|
"outgoing": "Outgoing",
|
||||||
"serious": "Serious",
|
"passionate": "Apasionado",
|
||||||
"shrewd": "Shrewd",
|
"patient": "Patient",
|
||||||
"stubborn": "Stubborn",
|
"personable": "Personable",
|
||||||
"suspicious": "Suspicious",
|
"playful": "Juguetón",
|
||||||
"teasing": "Teasing",
|
"power_hungry": "Ávido de poder",
|
||||||
|
"proud": "Orgulloso",
|
||||||
|
"refined": "Refined",
|
||||||
|
"reserved": "Reserved",
|
||||||
|
"restrained": "Contenido",
|
||||||
|
"righteous": "Righteous",
|
||||||
|
"scheming": "Taimado",
|
||||||
|
"serene": "Sereno",
|
||||||
|
"serious": "Serio",
|
||||||
|
"shrewd": "Artero",
|
||||||
|
"sinister": "Sinister",
|
||||||
|
"sociable": "Sociable",
|
||||||
|
"stoic": "Stoic",
|
||||||
|
"starved": "Starved",
|
||||||
|
"stubborn": "Testarudo",
|
||||||
|
"suspicious": "Suspicaz",
|
||||||
|
"teasing": "Bromista",
|
||||||
"territorial": "Territorial",
|
"territorial": "Territorial",
|
||||||
"uncertain": "Uncertain",
|
"uncertain": "Inseguro",
|
||||||
"unenthused": "Unenthused",
|
"unenthused": "Sin entusiasmo",
|
||||||
"vain": "Vain",
|
"vain": "Vanidoso",
|
||||||
"wary": "Wary"
|
"vengeful": "Vengeful",
|
||||||
|
"vindictive": "Vindictive",
|
||||||
|
"wary": "Precavido",
|
||||||
|
"watchful": "Watchful",
|
||||||
|
"wrathful": "Wrathful",
|
||||||
|
"zealous": "Zealous"
|
||||||
},
|
},
|
||||||
"compendium": {
|
"compendium": {
|
||||||
"filter_rank": "Show Rank",
|
"filter_rank": "Mostrar rango",
|
||||||
"not_for_players": "Not shown to players",
|
"not_for_players": "No mostrar a los jugadores",
|
||||||
"filter": {
|
"filter": {
|
||||||
"rank": "Rank",
|
"rank": "Rango",
|
||||||
"rarity": "Rarity",
|
"rarity": "Rareza",
|
||||||
"ring": "Ring"
|
"ring": "Anillo",
|
||||||
|
"clear": "Clear Filter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source_reference": {
|
"source_reference": {
|
||||||
"core_rulebook": "Core Rulebook",
|
"core_rulebook": "Libro básico",
|
||||||
"emerald_empire": "Emerald Empire",
|
"emerald_empire": "La guia del Imperio Esmeralda",
|
||||||
"shadowlands": "Shadowlands",
|
"shadowlands": "Las Tierras Sombrías",
|
||||||
"court_of_stones": "Court of Stones",
|
"court_of_stones": "Cortes de piedra",
|
||||||
"path_of_waves": "Path of Waves",
|
"path_of_waves": "Path of Waves",
|
||||||
"celestial_realms": "Celestial Realms",
|
"celestial_realms": "Celestial Realms",
|
||||||
"fields_of_victory": "Fields of Victory",
|
"fields_of_victory": "Fields of Victory",
|
||||||
"writ_of_the_wild": "Writ of the Wild",
|
"writ_of_the_wild": "Writ of the Wild",
|
||||||
"gm_kit": "Game Master's Kit",
|
"gm_kit": "Pantalla del DJ",
|
||||||
"beginner_game": "Beginner Game",
|
"beginner_game": "Caja de inicio",
|
||||||
"the_mantis_clan": "The Mantis Clan",
|
"the_mantis_clan": "El Clan de la Mantis",
|
||||||
"mask_of_the_oni": "Mask of the Oni",
|
"mask_of_the_oni": "La máscara del oni",
|
||||||
"winters_embrace": "Winter's Embrace",
|
"winters_embrace": "El abrazo del invierno",
|
||||||
"sins_of_regret": "Sins of Regret",
|
"sins_of_regret": "Sins of Regret",
|
||||||
"wheel_of_judgment": "Wheel of Judgment",
|
"wheel_of_judgment": "Wheel of Judgment",
|
||||||
"blood_of_the_lioness": "Blood of the Lioness",
|
"blood_of_the_lioness": "Blood of the Lioness",
|
||||||
"imperfect_land": "Imperfect Land",
|
"imperfect_land": "Imperfect Land",
|
||||||
"in_the_palace_of_the_emerald_champion": "In the Palace of the Emerald Champion",
|
"in_the_palace_of_the_emerald_champion": "En el palacio del Campeón Esmeralda",
|
||||||
"the_highwayman": "The Highwayman",
|
"the_highwayman": "The Highwayman",
|
||||||
"wedding_at_kyotei_castle": "Wedding at Kyotei Castle",
|
"wedding_at_kyotei_castle": "Esponsales en el Castillo Kyotei",
|
||||||
"the_knotted_tails": "The Knotted Tails",
|
"the_knotted_tails": "Las Colas Anudadas",
|
||||||
"cresting_waves": "Cresting Waves",
|
"cresting_waves": "Mareas Oscuras",
|
||||||
"deathly_turns": "Deathly Turns",
|
"deathly_turns": "Deathly Turns",
|
||||||
"the_scroll_or_the_blade": "The Scroll or the Blade",
|
"the_scroll_or_the_blade": "El pergamino o la espada",
|
||||||
"legacies_of_war": "Legacies of War",
|
"legacies_of_war": "Legacies of War",
|
||||||
"children_of_the_five_winds": "Children of the Five Winds"
|
"children_of_the_five_winds": "Children of the Five Winds"
|
||||||
|
},
|
||||||
|
"tactical_grid": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Tactical Grid Settings",
|
||||||
|
"label": "Tactical Grid Settings",
|
||||||
|
"hint": "Configures tactical grid range band distances (GM only) and their visual appearance colors and transparency (all users).",
|
||||||
|
"cells": "spaces",
|
||||||
|
"world": {
|
||||||
|
"enabled": "Enable Tactical Grid",
|
||||||
|
"enabled_hint": "Enables or Disable tactical grid for everyone",
|
||||||
|
"start": "Start"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"color": "Color",
|
||||||
|
"alpha": "Alpha"
|
||||||
|
},
|
||||||
|
"range": "Range {index}",
|
||||||
|
"validate": {
|
||||||
|
"start-too-small": "Must be greater than Range Band {previousRangeIndex} ({previousStart})",
|
||||||
|
"start-too-large": "Must be lower then Range Band {nextRangeIndex} ({nextStart})"
|
||||||
|
},
|
||||||
|
"reset": "Reset to Default",
|
||||||
|
"submit": "Save"
|
||||||
|
},
|
||||||
|
"range_band": "Range Band {band}",
|
||||||
|
"range_abbreviation": "RB {range}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
"SetTn1OnTypeChange": "ND 1 à la sélection du type de rencontre",
|
"SetTn1OnTypeChange": "ND 1 à la sélection du type de rencontre",
|
||||||
"SetTn1OnTypeChangeHint": "Met le ND à 1 lorsqu'on modifie le type de rencontre (Intrigue, Duel, Escarmouche ou Bataille rangée)"
|
"SetTn1OnTypeChangeHint": "Met le ND à 1 lorsqu'on modifie le type de rencontre (Intrigue, Duel, Escarmouche ou Bataille rangée)"
|
||||||
},
|
},
|
||||||
|
"ShowAllStatusEffects": {
|
||||||
|
"Title": "Affiche tous les StatusEffects",
|
||||||
|
"Hint": "Si décoché (défaut), uniquement les conditions de L5R sont affichées."
|
||||||
|
},
|
||||||
"CustomTechniques": {
|
"CustomTechniques": {
|
||||||
"Title": "Utiliser les techniques personnalisées",
|
"Title": "Utiliser les techniques personnalisées",
|
||||||
"Hint": "Ajoute un type de technique 'Particularités' pour servir de fourre-tout."
|
"Hint": "Ajoute un type de technique 'Particularités' pour servir de fourre-tout."
|
||||||
@@ -79,7 +83,42 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"l5r5e": {
|
"l5r5e": {
|
||||||
"title": "Legend of the five Rings",
|
"title": "La Légende des Cinq Anneaux",
|
||||||
|
"conditions": {
|
||||||
|
"afflicted": "Tourmenté",
|
||||||
|
"bleeding": "En sang",
|
||||||
|
"burning": "En feu",
|
||||||
|
"centered": "Centered",
|
||||||
|
"compromised": "Compromis",
|
||||||
|
"dazed": "Hébété",
|
||||||
|
"disoriented": "Désorienté",
|
||||||
|
"dying": "Mourant",
|
||||||
|
"emboldened": "Emboldened",
|
||||||
|
"enraged": "Enragé",
|
||||||
|
"exhausted": "Épuisé",
|
||||||
|
"immobilized": "Immobilisé",
|
||||||
|
"illness_coughing_illness": "Maladie : Mauvaise toux",
|
||||||
|
"illness_fire_rash": "Maladie : Rougeurs",
|
||||||
|
"illness_gut_sickness": "Maladie : Mal des entrailles",
|
||||||
|
"illness_oozing_sore_disease": "Maladie : Bubons purulents",
|
||||||
|
"illness_unsteady_illness": "Maladie : Vertiges",
|
||||||
|
"incapacitated": "Hors de combat",
|
||||||
|
"intoxicated": "Ivre",
|
||||||
|
"possessed": "Possédé",
|
||||||
|
"prone": "A terre",
|
||||||
|
"silenced": "Aphone",
|
||||||
|
"unconscious": "Inconscient",
|
||||||
|
"lightly_wounded_fire": "Légèrement Blessé (Feu)",
|
||||||
|
"lightly_wounded_water": "Légèrement Blessé (Eau)",
|
||||||
|
"lightly_wounded_air": "Légèrement Blessé (Air)",
|
||||||
|
"lightly_wounded_earth": "Légèrement Blessé (Terre)",
|
||||||
|
"lightly_wounded_void": "Légèrement Blessé (Vide)",
|
||||||
|
"severely_wounded_fire": "Gravement blessé (Feu)",
|
||||||
|
"severely_wounded_water": "Gravement blessé (Eau)",
|
||||||
|
"severely_wounded_air": "Gravement blessé (Air)",
|
||||||
|
"severely_wounded_earth": "Gravement blessé (Terre)",
|
||||||
|
"severely_wounded_void": "Gravement blessé (Vide)"
|
||||||
|
},
|
||||||
"global": {
|
"global": {
|
||||||
"edge_translation_disclaimer": "",
|
"edge_translation_disclaimer": "",
|
||||||
"add": "Ajouter",
|
"add": "Ajouter",
|
||||||
@@ -97,6 +136,7 @@
|
|||||||
"player_filter_label": "Filtre joueur",
|
"player_filter_label": "Filtre joueur",
|
||||||
"player_filter_tooltip": "Applique le filtre des joueurs",
|
"player_filter_tooltip": "Applique le filtre des joueurs",
|
||||||
"already_in_filter": "Filtre déjà appliqué",
|
"already_in_filter": "Filtre déjà appliqué",
|
||||||
|
"no_results": "Aucun résultat",
|
||||||
"sources_categories": {
|
"sources_categories": {
|
||||||
"rules": "Règles",
|
"rules": "Règles",
|
||||||
"adventures": "Aventures",
|
"adventures": "Aventures",
|
||||||
@@ -453,7 +493,10 @@
|
|||||||
"rarity_modifier": "Modificateur de rareté",
|
"rarity_modifier": "Modificateur de rareté",
|
||||||
"item_pattern": "Procédés de fabrication",
|
"item_pattern": "Procédés de fabrication",
|
||||||
"signature_scroll": "Rouleaux de marque",
|
"signature_scroll": "Rouleaux de marque",
|
||||||
"school_curriculum_journal": "Déposer un journal de Cursus dans la feuille pour le lier"
|
"school_curriculum_journal": "Déposer un journal de Cursus dans la feuille pour le lier",
|
||||||
|
"warning": {
|
||||||
|
"total_less_then_spent": "L'expérience totale est inférieure à l'expérience utilisée."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"character_types": {
|
"character_types": {
|
||||||
"character": "Personnage Joueur",
|
"character": "Personnage Joueur",
|
||||||
@@ -664,65 +707,103 @@
|
|||||||
"ujik": "Ujik"
|
"ujik": "Ujik"
|
||||||
},
|
},
|
||||||
"demeanor": {
|
"demeanor": {
|
||||||
"adaptable": "Adaptable",
|
"adaptable": "Malléable",
|
||||||
"aggressive": "Agressive",
|
"aggressive": "Agressive",
|
||||||
|
"alluring": "Attirante",
|
||||||
"ambitious": "Ambitieuse",
|
"ambitious": "Ambitieuse",
|
||||||
"amiable": "Sympathique",
|
"amiable": "Aimable",
|
||||||
"analytical": "Réfléchie",
|
"analytical": "Analytique",
|
||||||
"angry": "Enervée",
|
"angry": "En colère",
|
||||||
"arrogant": "Arrogante",
|
"arrogant": "Arrogante",
|
||||||
"assertive": "Assurée",
|
"assertive": "Sûre de soi",
|
||||||
"beguiling": "Séduisante",
|
"beguiling": "Envoûtante",
|
||||||
"bitter": "Amère",
|
"bitter": "Amère",
|
||||||
"bold": "Audacieuse",
|
"bloodthirsty": "Sanguinaire",
|
||||||
|
"bold": "Courageuse",
|
||||||
"calculating": "Calculatrice",
|
"calculating": "Calculatrice",
|
||||||
"calm": "Calme",
|
"calm": "Calme",
|
||||||
"capricious": "Capricieuse",
|
"capricious": "Capricieuse",
|
||||||
"cautious": "Prudente",
|
"cautious": "Prudente",
|
||||||
"clever": "Astucieuse",
|
"clever": "Malicieuse",
|
||||||
"compassionate": "Compatissante",
|
"compassionate": "Compatissante",
|
||||||
"confused": "Confuse",
|
"confused": "Confuse",
|
||||||
"courageous": "Courageuse",
|
"courageous": "Courageuse",
|
||||||
"cowardly": "Lâche",
|
"cowardly": "Lâche",
|
||||||
|
"crestfallen": "Démoralisée",
|
||||||
"curious": "Curieuse",
|
"curious": "Curieuse",
|
||||||
|
"defensive": "Sur la défensive",
|
||||||
"dependable": "Fiable",
|
"dependable": "Fiable",
|
||||||
"detached": "Détachée",
|
"detached": "Détachée",
|
||||||
"disheartened": "Découragée",
|
"determined": "Déterminée",
|
||||||
|
"devoted": "Fervente",
|
||||||
|
"direct": "Directe",
|
||||||
|
"disheartened": "Abattue",
|
||||||
|
"dour": "Renfrognée",
|
||||||
|
"duplicitous": "Sournoise",
|
||||||
|
"effusive": "Communicative",
|
||||||
"enraged": "Enragée",
|
"enraged": "Enragée",
|
||||||
|
"fanatical": "Fanatique",
|
||||||
"feral": "Sauvage",
|
"feral": "Sauvage",
|
||||||
"fickle": "Inconstante",
|
"fervent": "Dévote",
|
||||||
|
"fickle": "Volatile",
|
||||||
"fierce": "Féroce",
|
"fierce": "Féroce",
|
||||||
"flighty": "Volage",
|
"flighty": "Inconstante",
|
||||||
"flippant": "Désinvolte",
|
"flippant": "Désinvolte",
|
||||||
"friendly": "Amicale",
|
"friendly": "Amicale",
|
||||||
"gruff": "Bourrue",
|
"gruff": "Bourrue",
|
||||||
|
"honorable": "Honorable",
|
||||||
|
"hubristic": "Prétentieuse",
|
||||||
"hungry": "Affamée",
|
"hungry": "Affamée",
|
||||||
"intense": "Intense",
|
"idealistic": "Idéaliste",
|
||||||
|
"imposing": "Impressionnante",
|
||||||
|
"inquisitive": "Inquisitrice",
|
||||||
|
"intense": "Excessive",
|
||||||
"intimidating": "Intimidante",
|
"intimidating": "Intimidante",
|
||||||
"irritable": "Irritable",
|
"irritable": "Colérique",
|
||||||
"loyal": "Fidèle",
|
"loyal": "Loyale",
|
||||||
"mischievous": "Malicieuse",
|
"methodical": "Méthodique",
|
||||||
|
"meticulous": "Méticuleuse",
|
||||||
|
"mischievous": "Taquine",
|
||||||
|
"moon_blessed": "Bénie par la Lune",
|
||||||
"morose": "Morose",
|
"morose": "Morose",
|
||||||
"nurturing": "Encourageante",
|
"near_feral": "Presque sauvage",
|
||||||
|
"nurturing": "Maternelle",
|
||||||
|
"obsessed": "Obsessionnelle",
|
||||||
"obstinate": "Obstinée",
|
"obstinate": "Obstinée",
|
||||||
"opportunistic": "Opportuniste",
|
"opportunistic": "Opportuniste",
|
||||||
|
"otherworldly": "Mystique",
|
||||||
|
"outgoing": "Agréable",
|
||||||
"passionate": "Passionnée",
|
"passionate": "Passionnée",
|
||||||
"playful": "Enjouée",
|
"patient": "Patiente",
|
||||||
|
"personable": "Avenante",
|
||||||
|
"playful": "Joueuse",
|
||||||
"power_hungry": "Avide de pouvoir",
|
"power_hungry": "Avide de pouvoir",
|
||||||
"proud": "Fière",
|
"proud": "Fière",
|
||||||
"restrained": "Restreinte",
|
"refined": "Raffinée",
|
||||||
"scheming": "Intrigante",
|
"restrained": "Modérée",
|
||||||
|
"reserved": "Réservée",
|
||||||
|
"righteous": "Intègre",
|
||||||
|
"scheming": "Fourbe",
|
||||||
"serene": "Sereine",
|
"serene": "Sereine",
|
||||||
"serious": "Sérieuse",
|
"serious": "Sérieuse",
|
||||||
"shrewd": "Astucieuse",
|
"shrewd": "Rusée",
|
||||||
|
"sinister": "Sinistre",
|
||||||
|
"sociable": "Affable",
|
||||||
|
"starved": "Famélique",
|
||||||
|
"stoic": "Stoïque",
|
||||||
"stubborn": "Têtue",
|
"stubborn": "Têtue",
|
||||||
"suspicious": "Soupçonneuse",
|
"suspicious": "Suspicieuse",
|
||||||
"teasing": "Taquine",
|
"teasing": "Moqueuse",
|
||||||
"territorial": "Territoriale",
|
"territorial": "Territoriale",
|
||||||
"uncertain": "Incertaine",
|
"uncertain": "Peu sûre de soi",
|
||||||
"unenthused": "Peu enthousiaste",
|
"unenthused": "Amorphe",
|
||||||
"vain": "Vaine",
|
"vain": "Orgueilleuse",
|
||||||
"wary": "Méfiante"
|
"vengeful": "Vengeuse",
|
||||||
|
"vindictive": "Vindicative",
|
||||||
|
"wary": "Méfiante",
|
||||||
|
"watchful": "Attentif",
|
||||||
|
"wrathful": "Furieuse",
|
||||||
|
"zealous": "Zélée"
|
||||||
},
|
},
|
||||||
"compendium": {
|
"compendium": {
|
||||||
"filter_rank": "Aff. Rangs",
|
"filter_rank": "Aff. Rangs",
|
||||||
@@ -730,7 +811,8 @@
|
|||||||
"filter": {
|
"filter": {
|
||||||
"rank": "Rang",
|
"rank": "Rang",
|
||||||
"rarity": "Rareté",
|
"rarity": "Rareté",
|
||||||
"ring": "Anneau"
|
"ring": "Anneau",
|
||||||
|
"clear": "Suppr. les Filtres"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source_reference": {
|
"source_reference": {
|
||||||
@@ -760,6 +842,32 @@
|
|||||||
"the_scroll_or_the_blade": "Le Parchemin ou le Sabre",
|
"the_scroll_or_the_blade": "Le Parchemin ou le Sabre",
|
||||||
"legacies_of_war": "Les Flambeaux de la Guerre",
|
"legacies_of_war": "Les Flambeaux de la Guerre",
|
||||||
"children_of_the_five_winds": "Les Enfants des Cinq Vents"
|
"children_of_the_five_winds": "Les Enfants des Cinq Vents"
|
||||||
|
},
|
||||||
|
"tactical_grid": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Plan Tactique",
|
||||||
|
"label": "Paramètres du Plan Tactique",
|
||||||
|
"hint": "Configure les Niveaux de Portée (GM uniquement), ainsi que les différentes couleurs et transparence (tous les utilisateurs).",
|
||||||
|
"cells": "cases",
|
||||||
|
"world": {
|
||||||
|
"enabled": "Activer le Plan Tactique",
|
||||||
|
"enabled_hint": "Active ou désactive le plan tactique pour tout le monde",
|
||||||
|
"start": "Début"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"color": "Couleur",
|
||||||
|
"alpha": "Alpha"
|
||||||
|
},
|
||||||
|
"range": "Portée {index}",
|
||||||
|
"validate": {
|
||||||
|
"start-too-small": "Doit être supérieur à la Portée {previousRangeIndex} ({previousStart})",
|
||||||
|
"start-too-large": "Doit être inférieur à la Portée {nextRangeIndex} ({nextStart})"
|
||||||
|
},
|
||||||
|
"reset": "Réinitialiser les paramètres par défaut",
|
||||||
|
"submit": "Enregistrer"
|
||||||
|
},
|
||||||
|
"range_band": "Portée {band}",
|
||||||
|
"range_abbreviation": "NP {range}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
"SetTn1OnTypeChange": "Fissa la TN a 1 quando si cambia il conflitto",
|
"SetTn1OnTypeChange": "Fissa la TN a 1 quando si cambia il conflitto",
|
||||||
"SetTn1OnTypeChangeHint": "Fissa la TN a 1 quando si seleziona il tipo di conflitto (Intrigo, Duello, Schermaglia or Battaglia campale)"
|
"SetTn1OnTypeChangeHint": "Fissa la TN a 1 quando si seleziona il tipo di conflitto (Intrigo, Duello, Schermaglia or Battaglia campale)"
|
||||||
},
|
},
|
||||||
|
"ShowAllStatusEffects": {
|
||||||
|
"Title": "Show all StatusEffects",
|
||||||
|
"Hint": "If uncheck (default), only L5R conditions are shown."
|
||||||
|
},
|
||||||
"CustomTechniques": {
|
"CustomTechniques": {
|
||||||
"Title": "Usa tecniche custom",
|
"Title": "Usa tecniche custom",
|
||||||
"Hint": "Aggiunge il tipo 'Speciale' come termine generale."
|
"Hint": "Aggiunge il tipo 'Speciale' come termine generale."
|
||||||
@@ -80,6 +84,41 @@
|
|||||||
},
|
},
|
||||||
"l5r5e": {
|
"l5r5e": {
|
||||||
"title": "Legend of the five Rings",
|
"title": "Legend of the five Rings",
|
||||||
|
"conditions": {
|
||||||
|
"afflicted": "Afflicted",
|
||||||
|
"bleeding": "Bleeding",
|
||||||
|
"burning": "Burning",
|
||||||
|
"centered": "Centered",
|
||||||
|
"compromised": "Compromised",
|
||||||
|
"dazed": "Dazed",
|
||||||
|
"disoriented": "Disoriented",
|
||||||
|
"dying": "Dying",
|
||||||
|
"emboldened": "Emboldened",
|
||||||
|
"enraged": "Enraged",
|
||||||
|
"exhausted": "Exhausted",
|
||||||
|
"immobilized": "Immobilized",
|
||||||
|
"illness_coughing_illness": "Illness: Coughing Illness",
|
||||||
|
"illness_fire_rash": "Illness: Fire Rash",
|
||||||
|
"illness_gut_sickness": "Illness: Gut Sickness",
|
||||||
|
"illness_oozing_sore_disease": "Illness: Oozing Sore Disease",
|
||||||
|
"illness_unsteady_illness": "Illness: Unsteady Illness",
|
||||||
|
"incapacitated": "Incapacitated",
|
||||||
|
"intoxicated": "Intoxicated",
|
||||||
|
"possessed": "Possessed",
|
||||||
|
"prone": "Prone",
|
||||||
|
"silenced": "Silenced",
|
||||||
|
"unconscious": "Unconscious",
|
||||||
|
"lightly_wounded_fire": "Lightly Wounded (Fire)",
|
||||||
|
"lightly_wounded_water": "Lightly Wounded (Water)",
|
||||||
|
"lightly_wounded_air": "Lightly Wounded (Air)",
|
||||||
|
"lightly_wounded_earth": "Lightly Wounded (Earth)",
|
||||||
|
"lightly_wounded_void": "Lightly Wounded (Void)",
|
||||||
|
"severely_wounded_fire": "Severely Wounded (Fire)",
|
||||||
|
"severely_wounded_water": "Severely Wounded (Water)",
|
||||||
|
"severely_wounded_air": "Severely Wounded (Air)",
|
||||||
|
"severely_wounded_earth": "Severely Wounded (Earth)",
|
||||||
|
"severely_wounded_void": "Severely Wounded (Void)"
|
||||||
|
},
|
||||||
"global": {
|
"global": {
|
||||||
"edge_translation_disclaimer": "",
|
"edge_translation_disclaimer": "",
|
||||||
"add": "Aggiungi",
|
"add": "Aggiungi",
|
||||||
@@ -97,6 +136,7 @@
|
|||||||
"player_filter_label": "Player filter",
|
"player_filter_label": "Player filter",
|
||||||
"player_filter_tooltip": "Apply player filter",
|
"player_filter_tooltip": "Apply player filter",
|
||||||
"already_in_filter": "Already in filter",
|
"already_in_filter": "Already in filter",
|
||||||
|
"no_results": "Not Found",
|
||||||
"sources_categories": {
|
"sources_categories": {
|
||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"adventures": "Adventures",
|
"adventures": "Adventures",
|
||||||
@@ -453,7 +493,10 @@
|
|||||||
"rarity_modifier": "Modificatore rarità",
|
"rarity_modifier": "Modificatore rarità",
|
||||||
"item_pattern": "Item Patterns",
|
"item_pattern": "Item Patterns",
|
||||||
"signature_scroll": "Signature Scrolls",
|
"signature_scroll": "Signature Scrolls",
|
||||||
"school_curriculum_journal": "Trascina il diario del curriculum sulla scheda per collegarlo"
|
"school_curriculum_journal": "Trascina il diario del curriculum sulla scheda per collegarlo",
|
||||||
|
"warning": {
|
||||||
|
"total_less_then_spent": "L'esperienza totale è inferiore all'esperienza utilizzata."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"character_types": {
|
"character_types": {
|
||||||
"character": "Personaggio giocante",
|
"character": "Personaggio giocante",
|
||||||
@@ -666,6 +709,7 @@
|
|||||||
"demeanor": {
|
"demeanor": {
|
||||||
"adaptable": "Flessibile",
|
"adaptable": "Flessibile",
|
||||||
"aggressive": "Aggressivo",
|
"aggressive": "Aggressivo",
|
||||||
|
"alluring": "Alluring",
|
||||||
"ambitious": "Ambizioso",
|
"ambitious": "Ambizioso",
|
||||||
"amiable": "Affabile",
|
"amiable": "Affabile",
|
||||||
"analytical": "Analitico",
|
"analytical": "Analitico",
|
||||||
@@ -674,6 +718,7 @@
|
|||||||
"assertive": "Risoluto",
|
"assertive": "Risoluto",
|
||||||
"beguiling": "Ammaliante",
|
"beguiling": "Ammaliante",
|
||||||
"bitter": "Amaro",
|
"bitter": "Amaro",
|
||||||
|
"bloodthirsty": "Bloodthirsty",
|
||||||
"bold": "Ardito",
|
"bold": "Ardito",
|
||||||
"calculating": "Calcolatore",
|
"calculating": "Calcolatore",
|
||||||
"calm": "Calmo",
|
"calm": "Calmo",
|
||||||
@@ -684,37 +729,68 @@
|
|||||||
"confused": "Confuso",
|
"confused": "Confuso",
|
||||||
"courageous": "Coraggioso",
|
"courageous": "Coraggioso",
|
||||||
"cowardly": "Codardo",
|
"cowardly": "Codardo",
|
||||||
|
"crestfallen": "Crestfallen",
|
||||||
"curious": "Curioso",
|
"curious": "Curioso",
|
||||||
|
"defensive": "Defensive",
|
||||||
"dependable": "Affidabile",
|
"dependable": "Affidabile",
|
||||||
"detached": "Distaccato",
|
"detached": "Distaccato",
|
||||||
|
"determined": "Determined",
|
||||||
|
"devoted": "Devoted",
|
||||||
|
"direct": "Direct",
|
||||||
"disheartened": "Sconfortato",
|
"disheartened": "Sconfortato",
|
||||||
|
"dour": "Dour",
|
||||||
|
"duplicitous": "Duplicitous",
|
||||||
|
"effusive": "Effusive",
|
||||||
"enraged": "Infuriato",
|
"enraged": "Infuriato",
|
||||||
|
"fanatical": "Fanatical",
|
||||||
"feral": "Selvaggio",
|
"feral": "Selvaggio",
|
||||||
|
"fervent": "Fervent",
|
||||||
"fickle": "Volubile",
|
"fickle": "Volubile",
|
||||||
"fierce": "Agguerrito",
|
"fierce": "Agguerrito",
|
||||||
"flighty": "Volubile",
|
"flighty": "Volubile",
|
||||||
"flippant": "Irriverente",
|
"flippant": "Irriverente",
|
||||||
"friendly": "Amichevole",
|
"friendly": "Amichevole",
|
||||||
"gruff": "Burbero",
|
"gruff": "Burbero",
|
||||||
|
"honorable": "Honorable",
|
||||||
|
"hubristic": "Prétentieuse",
|
||||||
"hungry": "Affamato",
|
"hungry": "Affamato",
|
||||||
|
"idealistic": "Idealistic",
|
||||||
|
"imposing": "Imposing",
|
||||||
|
"inquisitive": "Inquisitive",
|
||||||
"intense": "Intenso",
|
"intense": "Intenso",
|
||||||
"intimidating": "Intimidatorio",
|
"intimidating": "Intimidatorio",
|
||||||
"irritable": "Irritabile",
|
"irritable": "Irritabile",
|
||||||
"loyal": "Leale",
|
"loyal": "Leale",
|
||||||
|
"methodical": "Methodical",
|
||||||
|
"meticulous": "Meticulous",
|
||||||
"mischievous": "Malandrino",
|
"mischievous": "Malandrino",
|
||||||
|
"moon_blessed": "Moon-blessed",
|
||||||
"morose": "Cupo",
|
"morose": "Cupo",
|
||||||
|
"near_feral": "Near feral",
|
||||||
"nurturing": "Materno",
|
"nurturing": "Materno",
|
||||||
|
"obsessed": "Obsessed",
|
||||||
"obstinate": "Ostinato",
|
"obstinate": "Ostinato",
|
||||||
"opportunistic": "Opportunista",
|
"opportunistic": "Opportunista",
|
||||||
|
"otherworldly": "Otherworldly",
|
||||||
|
"outgoing": "Outgoing",
|
||||||
"passionate": "Appassionato",
|
"passionate": "Appassionato",
|
||||||
|
"patient": "Patient",
|
||||||
|
"personable": "Personable",
|
||||||
"playful": "Giocoso",
|
"playful": "Giocoso",
|
||||||
"power_hungry": "Affamato di potere",
|
"power_hungry": "Affamato di potere",
|
||||||
"proud": "Orgoglioso",
|
"proud": "Orgoglioso",
|
||||||
|
"refined": "Refined",
|
||||||
|
"reserved": "Reserved",
|
||||||
"restrained": "Contenuto",
|
"restrained": "Contenuto",
|
||||||
|
"righteous": "Righteous",
|
||||||
"scheming": "Cospiratore",
|
"scheming": "Cospiratore",
|
||||||
"serene": "Sereno",
|
"serene": "Sereno",
|
||||||
"serious": "Serio",
|
"serious": "Serio",
|
||||||
"shrewd": "Scaltro",
|
"shrewd": "Scaltro",
|
||||||
|
"sinister": "Sinister",
|
||||||
|
"sociable": "Sociable",
|
||||||
|
"stoic": "Stoic",
|
||||||
|
"starved": "Starved",
|
||||||
"stubborn": "Testardo",
|
"stubborn": "Testardo",
|
||||||
"suspicious": "Sospettoso",
|
"suspicious": "Sospettoso",
|
||||||
"teasing": "Stuzzicante",
|
"teasing": "Stuzzicante",
|
||||||
@@ -722,7 +798,12 @@
|
|||||||
"uncertain": "Incerto",
|
"uncertain": "Incerto",
|
||||||
"unenthused": "Non entusiasta",
|
"unenthused": "Non entusiasta",
|
||||||
"vain": "Vanesio",
|
"vain": "Vanesio",
|
||||||
"wary": "Diffidente"
|
"vengeful": "Vengeful",
|
||||||
|
"vindictive": "Vindictive",
|
||||||
|
"wary": "Diffidente",
|
||||||
|
"watchful": "Watchful",
|
||||||
|
"wrathful": "Wrathful",
|
||||||
|
"zealous": "Zealous"
|
||||||
},
|
},
|
||||||
"compendium": {
|
"compendium": {
|
||||||
"filter_rank": "Show Rank",
|
"filter_rank": "Show Rank",
|
||||||
@@ -730,14 +811,15 @@
|
|||||||
"filter": {
|
"filter": {
|
||||||
"rank": "Rank",
|
"rank": "Rank",
|
||||||
"rarity": "Rarity",
|
"rarity": "Rarity",
|
||||||
"ring": "Ring"
|
"ring": "Ring",
|
||||||
|
"clear": "Clear Filter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"source_reference": {
|
"source_reference": {
|
||||||
"core_rulebook": "Core Rulebook",
|
"core_rulebook": "Core Rulebook",
|
||||||
"emerald_empire": "Emerald Empire",
|
"emerald_empire": "Emerald Empire",
|
||||||
"shadowlands": "Shadowlands",
|
"shadowlands": "Shadowlands",
|
||||||
"court_of_stones": "Court of Stones",
|
"court_of_stones": "Courts of Stone",
|
||||||
"path_of_waves": "Path of Waves",
|
"path_of_waves": "Path of Waves",
|
||||||
"celestial_realms": "Celestial Realms",
|
"celestial_realms": "Celestial Realms",
|
||||||
"fields_of_victory": "Fields of Victory",
|
"fields_of_victory": "Fields of Victory",
|
||||||
@@ -760,6 +842,32 @@
|
|||||||
"the_scroll_or_the_blade": "The Scroll or the Blade",
|
"the_scroll_or_the_blade": "The Scroll or the Blade",
|
||||||
"legacies_of_war": "Legacies of War",
|
"legacies_of_war": "Legacies of War",
|
||||||
"children_of_the_five_winds": "Children of the Five Winds"
|
"children_of_the_five_winds": "Children of the Five Winds"
|
||||||
|
},
|
||||||
|
"tactical_grid": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Tactical Grid Settings",
|
||||||
|
"label": "Tactical Grid Settings",
|
||||||
|
"hint": "Configures tactical grid range band distances (GM only) and their visual appearance colors and transparency (all users).",
|
||||||
|
"cells": "spaces",
|
||||||
|
"world": {
|
||||||
|
"enabled": "Enable Tactical Grid",
|
||||||
|
"enabled_hint": "Enables or Disable tactical grid for everyone",
|
||||||
|
"start": "Start"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"color": "Color",
|
||||||
|
"alpha": "Alpha"
|
||||||
|
},
|
||||||
|
"range": "Range {index}",
|
||||||
|
"validate": {
|
||||||
|
"start-too-small": "Must be greater than Range Band {previousRangeIndex} ({previousStart})",
|
||||||
|
"start-too-large": "Must be lower then Range Band {nextRangeIndex} ({nextStart})"
|
||||||
|
},
|
||||||
|
"reset": "Reset to Default",
|
||||||
|
"submit": "Save"
|
||||||
|
},
|
||||||
|
"range_band": "Range Band {band}",
|
||||||
|
"range_abbreviation": "RB {range}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
{"_id":"L5RCoreIte000030","name":"Tent (Yurt)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"2","rarity":"5","zeni":"10 koku","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"245"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000030","name":"Tent (Yurt)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"2","rarity":"5","zeni":"10 koku","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"245"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000031","name":"Scroll satchel","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"3","zeni":"1 bu","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"60"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000031","name":"Scroll satchel","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"3","zeni":"1 bu","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"60"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000032","name":"Traveling pack","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"1","zeni":"10 koku","properties":[{"id":"L5RCorePro000012","name":"Mundane"}],"description":"","source_reference":{"source":"core_rulebook","page":"245"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000032","name":"Traveling pack","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"1","zeni":"10 koku","properties":[{"id":"L5RCorePro000012","name":"Mundane"}],"description":"","source_reference":{"source":"core_rulebook","page":"245"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000033","name":"Journal of observations","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"3","zeni":"1 bu","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"67"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000033","name":"Journal of Observations","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"3","zeni":"1 bu","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"67"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000034","name":"Omamori (Boon of Jurojin)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"97"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000034","name":"Omamori (Boon of Jurojin)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"97"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000035","name":"Omamori (Boon of Kisshoten)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"97"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000035","name":"Omamori (Boon of Kisshoten)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"97"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000036","name":"Omamori (Boon of Hotei)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"97"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000036","name":"Omamori (Boon of Hotei)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"97"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
{"_id":"L5RCoreIte000111","name":"The horagai of Sacred Rains","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[{"id":"L5RCorePro000010","name":"Resplendent"},{"id":"L5RCorePro000014","name":"Sacred"}],"description":"","source_reference":{"source":"celestial_realms","page":"96"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000111","name":"The horagai of Sacred Rains","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[{"id":"L5RCorePro000010","name":"Resplendent"},{"id":"L5RCorePro000014","name":"Sacred"}],"description":"","source_reference":{"source":"celestial_realms","page":"96"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000112","name":"Daikoku's Mallet","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[{"id":"L5RCorePro000015","name":"Durable"},{"id":"L5RCorePro000014","name":"Sacred"},{"id":"L5RCorePro000016","name":"Subtle"}],"description":"","source_reference":{"source":"celestial_realms","page":"96"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000112","name":"Daikoku's Mallet","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[{"id":"L5RCorePro000015","name":"Durable"},{"id":"L5RCorePro000014","name":"Sacred"},{"id":"L5RCorePro000016","name":"Subtle"}],"description":"","source_reference":{"source":"celestial_realms","page":"96"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000113","name":"Candles of the Moth","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"9","zeni":"10 Koku","properties":[{"id":"L5RCorePro000002","name":"Ceremonial"},{"id":"L5RCorePro000014","name":"Sacred"},{"id":"L5RCorePro000016","name":"Subtle"}],"description":"","source_reference":{"source":"celestial_realms","page":"96"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000113","name":"Candles of the Moth","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"9","zeni":"10 Koku","properties":[{"id":"L5RCorePro000002","name":"Ceremonial"},{"id":"L5RCorePro000014","name":"Sacred"},{"id":"L5RCorePro000016","name":"Subtle"}],"description":"","source_reference":{"source":"celestial_realms","page":"96"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000114","name":"Astrolabe","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"8","zeni":"1 Koku","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"102"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000114","name":"Astrolabe (Unicorn)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"8","zeni":"1 Koku","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"102"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000115","name":"Horo (Arrow Cloak)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"6","zeni":"5 Koku","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"102"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000115","name":"Horo (Arrow Cloak)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"6","zeni":"5 Koku","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"102"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000116","name":"Lantern (Personal)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"1 Bu","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"102"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000116","name":"Lantern (Personal)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"1 Bu","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"102"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000117","name":"Mineral-Oil Lamp","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"8","zeni":"6 Koku","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"102"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000117","name":"Mineral-Oil Lamp","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"8","zeni":"6 Koku","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"102"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
@@ -119,3 +119,27 @@
|
|||||||
{"_id":"L5RCoreIte000121","name":"Word of the Prophet","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"9","zeni":"15 Koku","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"103"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000121","name":"Word of the Prophet","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"9","zeni":"15 Koku","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"103"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000122","name":"Talisman of the Sun [Blessed Treasure]","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"104"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000122","name":"Talisman of the Sun [Blessed Treasure]","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"104"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
{"_id":"L5RCoreIte000123","name":"Fox Pipe [Blessed Treasure]","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"105"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
{"_id":"L5RCoreIte000123","name":"Fox Pipe [Blessed Treasure]","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[],"description":"","source_reference":{"source":"children_of_the_five_winds","page":"105"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000124","name":"Glass Ornament (Dragonfly)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"6","zeni":"1 koku","properties":[],"description":"","source_reference":{"source":"writ_of_the_wild","page":"83"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000125","name":"Arrows: Armor-Piercing","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"0","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"236"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000126","name":"Arrows: Flesh-Cutter","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"0","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"236"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000127","name":"Arrows: Humming-Bulb","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"0","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"236"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000128","name":"Journal","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"1","zeni":"0","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"66"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000129","name":"Smithing hammer","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"1","zeni":"0","properties":[],"description":"","source_reference":{"source":"shadowlands","page":"89"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000130","name":"Sumai Garb","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"1","zeni":"0","properties":[],"description":"","source_reference":{"source":"fields_of_victory","page":"89"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000131","name":"Drafting Paper","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"1","zeni":"0","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"84"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000132","name":"Fine set of Chisels","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"1","zeni":"0","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"84"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000133","name":"Omamori (Boon of Fukurokujin)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"243"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000134","name":"Omamori (Boon of Bishamon)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"243"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000135","name":"Omamori (Boon of Benten)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"5 bu","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"243"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000136","name":"Poison - Noxious Poison (One Vial)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"5","zeni":"30 zeni","properties":[{"id":"L5RCorePro000009","name":"Forbidden"}],"description":"","source_reference":{"source":"core_rulebook","page":"244"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000137","name":"Poison - Fire Biter (One Vial)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"5","zeni":"30 zeni","properties":[{"id":"L5RCorePro000009","name":"Forbidden"}],"description":"","source_reference":{"source":"core_rulebook","page":"244"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000138","name":"Poison - Night Milk (One Vial)","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"5","zeni":"30 zeni","properties":[{"id":"L5RCorePro000009","name":"Forbidden"}],"description":"","source_reference":{"source":"core_rulebook","page":"244"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000139","name":"Blanket","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"0","zeni":"1 zeni","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"245"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000140","name":"Bowl","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"0","zeni":"1 zeni","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"245"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000141","name":"Flint and Tinder","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"0","zeni":"1 zeni","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"245"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000142","name":"Furoshiki","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"0","zeni":"1 zeni","properties":[],"description":"","source_reference":{"source":"core_rulebook","page":"245"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000143","name":"The Obsidian Journal","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"10","zeni":"0","properties":[{"id":"L5RCorePro000009","name":"Forbidden"},{"id":"L5RCorePro000008","name":"Unholy"}],"description":"","source_reference":{"source":"core_rulebook","page":"127"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000144","name":"Pouch of Insence","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"0","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"85"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000145","name":"Religious texts","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"0","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"86"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000146","name":"Small Sachel of Ingredients","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"10 bu","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"80"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
{"_id":"L5RCoreIte000147","name":"Blessed Glass vial","permission":{"default":0},"type":"item","data":{"equipped":false,"quantity":1,"weight":"0","rarity":"2","zeni":"0","properties":[],"description":"","source_reference":{"source":"celestial_realms","page":"80"}},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/items/item.svg","effects":[]}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
{"_id":"L5RCoreCon000001","name":"Afflicted","content":"<blockquote>Core Rulebook p.271</blockquote>","img":"icons/svg/sun.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000001","name":"Afflicted","content":"<blockquote>Core Rulebook p.271</blockquote>","img":"systems/l5r5e/assets/icons/conditions/afflicted.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000002","name":"Bleeding","content":"<blockquote>Core Rulebook p.271</blockquote>","img":"icons/svg/blood.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000002","name":"Bleeding","content":"<blockquote>Core Rulebook p.271</blockquote>","img":"systems/l5r5e/assets/icons/conditions/bleeding.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000003","name":"Burning","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/fire.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000003","name":"Burning","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/burning.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000004","name":"Compromised","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/terror.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000004","name":"Compromised","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/compromised.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000005","name":"Dazed","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/eye.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000005","name":"Dazed","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/dazed.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000006","name":"Disoriented","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/daze.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000006","name":"Disoriented","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/disoriented.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000007","name":"Dying [X Rounds]","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/skull.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000007","name":"Dying [X Rounds]","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/dying_1.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000008","name":"Enraged","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/lightning.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000008","name":"Enraged","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/enraged.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000009","name":"Exhausted","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/sleep.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000009","name":"Exhausted","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/exhausted.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000010","name":"Immobilized","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/net.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000010","name":"Immobilized","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/immobilized.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000011","name":"Incapacitated","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"icons/svg/downgrade.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000011","name":"Incapacitated","content":"<blockquote>Core Rulebook p.272</blockquote>","img":"systems/l5r5e/assets/icons/conditions/incapacitated.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000012","name":"Intoxicated","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"icons/svg/poison.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000012","name":"Intoxicated","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"systems/l5r5e/assets/icons/conditions/intoxicated.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000013","name":"Prone","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"icons/svg/falling.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000013","name":"Prone","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"systems/l5r5e/assets/icons/conditions/prone.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000014","name":"Silenced","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"icons/svg/silenced.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000014","name":"Silenced","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"systems/l5r5e/assets/icons/conditions/silenced.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000015","name":"Unconscious","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"icons/svg/unconscious.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000015","name":"Unconscious","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"systems/l5r5e/assets/icons/conditions/unconscious.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000016","name":"Wounded","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"icons/svg/degen.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000016","name":"Wounded","content":"<blockquote>Core Rulebook p.273</blockquote>","img":"systems/l5r5e/assets/icons/conditions/heavily_wounded.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000017","name":"Illness: Oozing Sore Disease","content":"<blockquote>Writ of the Wild p.140</blockquote>","img":"icons/svg/eye.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000017","name":"Illness: Oozing Sore Disease","content":"<blockquote>Writ of the Wild p.140</blockquote>","img":"systems/l5r5e/assets/icons/conditions/illness_oozing_sore_disease.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000018","name":"Illness: Gut Sickness","content":"<blockquote>Writ of the Wild p.141</blockquote>","img":"icons/svg/poison.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000018","name":"Illness: Gut Sickness","content":"<blockquote>Writ of the Wild p.141</blockquote>","img":"systems/l5r5e/assets/icons/conditions/illness_gut_sickness.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000019","name":"Illness: Coughing Illness","content":"<blockquote>Writ of the Wild p.141</blockquote>","img":"icons/svg/poison.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000019","name":"Illness: Coughing Illness","content":"<blockquote>Writ of the Wild p.141</blockquote>","img":"systems/l5r5e/assets/icons/conditions/illness_coughing_illness.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000020","name":"Illness: Unsteady Illness","content":"<blockquote>Writ of the Wild p.141</blockquote>","img":"icons/svg/daze.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000020","name":"Illness: Unsteady Illness","content":"<blockquote>Writ of the Wild p.141</blockquote>","img":"systems/l5r5e/assets/icons/conditions/illness_unsteady_illness.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000021","name":"Illness: Fire Rash","content":"<blockquote>Writ of the Wild p.141</blockquote>","img":"icons/svg/fire.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000021","name":"Illness: Fire Rash","content":"<blockquote>Writ of the Wild p.141</blockquote>","img":"systems/l5r5e/assets/icons/conditions/illness_fire_rash.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000022","name":"Centered","content":"<blockquote>Children of the Five Winds p.133</blockquote>","img":"systems/l5r5e/assets/icons/social.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000022","name":"Centered","content":"<blockquote>Children of the Five Winds p.133</blockquote>","img":"systems/l5r5e/assets/icons/conditions/centered.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000023","name":"Emboldened","content":"<blockquote>Children of the Five Winds p.133</blockquote>","img":"systems/l5r5e/assets/icons/social.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000023","name":"Emboldened","content":"<blockquote>Children of the Five Winds p.133</blockquote>","img":"systems/l5r5e/assets/icons/conditions/emboldened.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
{"_id":"L5RCoreCon000024","name":"Possessed","content":"<blockquote>Children of the Five Winds p.133</blockquote>","img":"icons/svg/terror.svg","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
{"_id":"L5RCoreCon000024","name":"Possessed","content":"<blockquote>Children of the Five Winds p.133</blockquote>","img":"systems/l5r5e/assets/icons/conditions/possesed.webp","folder":null,"sort":100001,"permission":{"default":0},"flags":{}}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export class ActorL5r5e extends Actor {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
await super.create(docData, options);
|
return super.create(docData, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,6 +125,22 @@ export class ActorL5r5e extends Actor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
async _preUpdate(changes, options, user) {
|
||||||
|
if (this.isCharacterType) {
|
||||||
|
// apply compromised condition if strife goes beyond max
|
||||||
|
const strife = changes.system?.strife?.value ?? this.system.strife.value;
|
||||||
|
const isCompromised = strife > this.system.composure;
|
||||||
|
// apply incapacitated if fatigue goes beyond max endurance
|
||||||
|
const fatigue = changes.system?.fatigue?.value ?? this.system.fatigue.value;
|
||||||
|
const isIncapacitated = fatigue > this.system.endurance;
|
||||||
|
await Promise.all([
|
||||||
|
this.toggleStatusEffect('compromised', {active: isCompromised}),
|
||||||
|
this.toggleStatusEffect('incapacitated', {active: isIncapacitated}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
prepareData() {
|
prepareData() {
|
||||||
super.prepareData();
|
super.prepareData();
|
||||||
@@ -137,13 +153,16 @@ export class ActorL5r5e extends Actor {
|
|||||||
ActorL5r5e.computeDerivedAttributes(system);
|
ActorL5r5e.computeDerivedAttributes(system);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isAfflicted = this.statuses.has("afflicted");
|
||||||
|
const isCompromised = this.statuses.has("compromised");
|
||||||
|
|
||||||
// Attributes bars
|
// Attributes bars
|
||||||
system.fatigue.max = system.endurance;
|
system.fatigue.max = system.endurance;
|
||||||
system.strife.max = system.composure;
|
system.strife.max = system.composure;
|
||||||
system.void_points.max = system.rings.void;
|
system.void_points.max = system.rings.void;
|
||||||
|
|
||||||
// if compromise, vigilance = 1
|
// if compromised or afflicted, vigilance = 1
|
||||||
system.is_compromised = system.strife.value > system.strife.max;
|
system.is_afflicted_or_compromised = isAfflicted || isCompromised;
|
||||||
|
|
||||||
// Make sure void points are never greater than max
|
// Make sure void points are never greater than max
|
||||||
if (system.void_points.value > system.void_points.max) {
|
if (system.void_points.value > system.void_points.max) {
|
||||||
@@ -188,6 +207,21 @@ export class ActorL5r5e extends Actor {
|
|||||||
return this._updateActorFromAdvancement(item, false);
|
return this._updateActorFromAdvancement(item, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("./types").Condition}
|
||||||
|
*
|
||||||
|
* Remove conditions by known string ids
|
||||||
|
* @param conditions {Set<Condition>}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async removeConditions(conditions) {
|
||||||
|
const effectsToRemove = this.statuses.intersection(conditions);
|
||||||
|
const idsToRemove = this.effects.contents
|
||||||
|
.filter(effect => effect.statuses.isSubsetOf(effectsToRemove))
|
||||||
|
.map(effect => effect.id);
|
||||||
|
await this.deleteEmbeddedDocuments("ActiveEffect", idsToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alter Actor skill/ring from a advancement
|
* Alter Actor skill/ring from a advancement
|
||||||
* @param {Item} item
|
* @param {Item} item
|
||||||
|
|||||||
@@ -321,6 +321,73 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
|
|||||||
|
|
||||||
// Fatigue/Strife +/-
|
// Fatigue/Strife +/-
|
||||||
html.find(".addsub-control").on("click", this._modifyFatigueOrStrife.bind(this));
|
html.find(".addsub-control").on("click", this._modifyFatigueOrStrife.bind(this));
|
||||||
|
|
||||||
|
// Effect remove/display
|
||||||
|
html.find(".effect-delete").on("click", this._removeEffectId.bind(this));
|
||||||
|
html.find(".effect-name").on("click", this._openEffectJournal.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an effect
|
||||||
|
* @param {Event} event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_removeEffectId(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const effectId = $(event.currentTarget).data("effect-id");
|
||||||
|
if (!effectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmpItem = this.actor.effects.get(effectId);
|
||||||
|
if (!tmpItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const callback = async () => {
|
||||||
|
return this.actor.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Holing Ctrl = without confirm
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
game.l5r5e.HelpersL5r5e.confirmDeleteDialog(
|
||||||
|
game.i18n.format("l5r5e.global.delete_confirm", { name: tmpItem.name }),
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the core linked journal effect if exist
|
||||||
|
* @param {Event} event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _openEffectJournal(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const effectId = $(event.currentTarget).data("effect-id");
|
||||||
|
if (!effectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const effect = this.actor.effects.get(effectId);
|
||||||
|
if (!effect?.system?.id && !effect?.system?.uuid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const journal = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack({
|
||||||
|
id: effect.system.id,
|
||||||
|
uuid: effect.system.uuid,
|
||||||
|
type: "JournalEntry",
|
||||||
|
});
|
||||||
|
if (journal) {
|
||||||
|
journal.sheet.render(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -557,6 +624,36 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "honor":
|
||||||
|
await this.actor.update({
|
||||||
|
system: {
|
||||||
|
social: {
|
||||||
|
honor: Math.max(0, this.actor.system.social.honor + mod),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "glory":
|
||||||
|
await this.actor.update({
|
||||||
|
system: {
|
||||||
|
social: {
|
||||||
|
glory: Math.max(0, this.actor.system.social.glory + mod),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "status":
|
||||||
|
await this.actor.update({
|
||||||
|
system: {
|
||||||
|
social: {
|
||||||
|
status: Math.max(0, this.actor.system.social.status + mod),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.warn("L5R5E | BCS | Unsupported type", type);
|
console.warn("L5R5E | BCS | Unsupported type", type);
|
||||||
break;
|
break;
|
||||||
@@ -627,6 +724,12 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
|
|||||||
_openDicePickerForSkill(event) {
|
_openDicePickerForSkill(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
|
// In Fvtt v13+ "Enter" trigger that mouse event, we ignore that below
|
||||||
|
if (event.clientX === 0 && event.clientY === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const li = $(event.currentTarget);
|
const li = $(event.currentTarget);
|
||||||
const weapon = this._getWeaponInfos(li.data("weapon-id") || null);
|
const weapon = this._getWeaponInfos(li.data("weapon-id") || null);
|
||||||
const isInitiative = li.data("initiative") || false;
|
const isInitiative = li.data("initiative") || false;
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
|
|||||||
// Split Others advancements, and calculate xp spent and add it to total
|
// Split Others advancements, and calculate xp spent and add it to total
|
||||||
this._prepareOthersAdvancement(sheetData);
|
this._prepareOthersAdvancement(sheetData);
|
||||||
|
|
||||||
|
// Update spent_xp to actor
|
||||||
|
this.actor.system.xp_spent = sheetData.data.system.xp_spent;
|
||||||
|
|
||||||
// Total
|
// Total
|
||||||
sheetData.data.system.xp_saved = Math.floor(
|
sheetData.data.system.xp_saved = Math.floor(
|
||||||
parseInt(sheetData.data.system.xp_total) - parseInt(sheetData.data.system.xp_spent)
|
parseInt(sheetData.data.system.xp_total) - parseInt(sheetData.data.system.xp_spent)
|
||||||
@@ -114,6 +117,9 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
|
|||||||
// Money +/-
|
// Money +/-
|
||||||
html.find(".money-control").on("click", this._modifyMoney.bind(this));
|
html.find(".money-control").on("click", this._modifyMoney.bind(this));
|
||||||
|
|
||||||
|
// XP +/-
|
||||||
|
html.find(".xp-control").on("click", this._modifyXP.bind(this));
|
||||||
|
|
||||||
// Advancements Tab to current rank onload
|
// Advancements Tab to current rank onload
|
||||||
// TODO class "Active" Bug on load, dunno why :/
|
// TODO class "Active" Bug on load, dunno why :/
|
||||||
this._tabs
|
this._tabs
|
||||||
@@ -149,6 +155,12 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
|
|||||||
adv[rank].spent.total += xp_used_total;
|
adv[rank].spent.total += xp_used_total;
|
||||||
adv[rank].spent.curriculum += xp_used;
|
adv[rank].spent.curriculum += xp_used;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If we finished the rank but haven't added anything to the next rank we should show an empty tab
|
||||||
|
// note: adv is index from 1, not 0 because of rank starting at 1
|
||||||
|
if(adv.length -1 < sheetData.data.system.identity.school_rank) {
|
||||||
|
adv.push({list: [], rank: sheetData.data.system.identity.school_rank, spent: { total: 0, curriculum: 0}});
|
||||||
|
}
|
||||||
sheetData.data.advancementsListByRank = adv;
|
sheetData.data.advancementsListByRank = adv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +297,35 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
|
|||||||
this.render(false);
|
this.render(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or Subtract XP (+/- buttons)
|
||||||
|
* @param {Event} event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _modifyXP(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const elmt = $(event.currentTarget);
|
||||||
|
let mod = elmt.data("value");
|
||||||
|
if (!mod) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_xp_total = Math.max(0, this.actor.system.xp_total + mod);
|
||||||
|
this.actor.update({
|
||||||
|
system: {
|
||||||
|
xp_total: new_xp_total,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if(this.actor.system.xp_spent > new_xp_total) {
|
||||||
|
ui.notifications.warn("l5r5e.advancements.warning.total_less_then_spent", { localize: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.render(false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add +1 to actor school rank
|
* Add +1 to actor school rank
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export class CombatL5r5e extends Combat {
|
|||||||
// If the character was unprepared (such as when surprised), their base initiative value is their vigilance attribute.
|
// If the character was unprepared (such as when surprised), their base initiative value is their vigilance attribute.
|
||||||
// Minion NPCs can generate initiative value without a check, using their focus or vigilance attribute
|
// Minion NPCs can generate initiative value without a check, using their focus or vigilance attribute
|
||||||
let initiative =
|
let initiative =
|
||||||
isPrepared === "true" ? actorSystem.focus : actorSystem.is_compromised ? 1 : actorSystem.vigilance;
|
isPrepared === "true" ? actorSystem.focus : actorSystem.is_afflicted_or_compromised ? 1 : actorSystem.vigilance;
|
||||||
|
|
||||||
// Roll only for PC and Adversary
|
// Roll only for PC and Adversary
|
||||||
if (isPc || combatant.actor.isAdversary) {
|
if (isPc || combatant.actor.isAdversary) {
|
||||||
@@ -111,7 +111,7 @@ export class CombatL5r5e extends Combat {
|
|||||||
if (messageOptions.rnkRoll instanceof game.l5r5e.RollL5r5e && ids.length === 1) {
|
if (messageOptions.rnkRoll instanceof game.l5r5e.RollL5r5e && ids.length === 1) {
|
||||||
// Specific RnK
|
// Specific RnK
|
||||||
roll = messageOptions.rnkRoll;
|
roll = messageOptions.rnkRoll;
|
||||||
rnkMessage = await roll.toMessage({ flavor }, { rollMode: messageOptions.rollMode || null });
|
rnkMessage = await roll.toMessage({ flavor }, { messageMode: messageOptions.messageMode || null });
|
||||||
} else {
|
} else {
|
||||||
// Regular
|
// Regular
|
||||||
roll = new game.l5r5e.RollL5r5e(formula ?? createFormula.join("+"));
|
roll = new game.l5r5e.RollL5r5e(formula ?? createFormula.join("+"));
|
||||||
|
|||||||
11
system/scripts/compendium/l5r5e-compendium-directory.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const { CompendiumDirectory } = foundry.applications.sidebar.tabs;
|
||||||
|
|
||||||
|
export class CompendiumDirectoryL5r5e extends CompendiumDirectory {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options);
|
||||||
|
context.sidebarIcon = foundry.applications.sidebar.Sidebar.TABS.compendium.icon;
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
497
system/scripts/compendium/l5r5e-item-compendium.js
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
import { L5r5eHtmlMultiSelectElement } from "../misc/l5r5e-multiselect.js";
|
||||||
|
|
||||||
|
const { Compendium } = foundry.applications.sidebar.apps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Compendium application for L5R5e.
|
||||||
|
* Adds source/rank/ring/rarity filters to Item compendiums
|
||||||
|
* @extends {Compendium}
|
||||||
|
*/
|
||||||
|
export class ItemCompendiumL5r5e extends Compendium {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
actions: {
|
||||||
|
applyPlayerView: ItemCompendiumL5r5e.#onApplyPlayerView,
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
resizable: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our own entry partial which mirrors Foundry's index-partial.hbs structure
|
||||||
|
* and appends ring/rarity/rank badges using data from _prepareDirectoryContext.
|
||||||
|
*
|
||||||
|
* NOTE: We intentionally duplicate Foundry's <li> structure here rather than
|
||||||
|
* trying to include their partial, because their partial renders a complete <li>
|
||||||
|
* element which cannot be nested or extended from outside. If Foundry ever
|
||||||
|
* changes their index-partial.hbs, this file will need updating to match.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static _entryPartial = "systems/l5r5e/templates/" + "compendium/l5r5e-index-partial.html";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sources present in this specific compendium, populated during _prepareContext.
|
||||||
|
* @type {Set<string>}
|
||||||
|
*/
|
||||||
|
#sourcesInThisCompendium = new Set();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sources unavailable to players based on permission settings.
|
||||||
|
* @type {Set<string>}
|
||||||
|
*/
|
||||||
|
#unavailableSourceForPlayersSet = new Set();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to hide entries with empty sources from players.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
#hideEmptySourcesFromPlayers = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which filter UI controls are worth showing.
|
||||||
|
* Determined during _prepareContext by checking whether at least two
|
||||||
|
* distinct values exist for each filterable property.
|
||||||
|
* @type {{ rank: boolean, rarity: boolean, source: boolean, ring: boolean }|null}
|
||||||
|
*/
|
||||||
|
#filtersToShow = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached active filter values, read from the DOM once at the start of
|
||||||
|
* each filter pass in #reapplyFilters and held for _onMatchSearchEntry
|
||||||
|
* to consume per-entry without re-querying the DOM.
|
||||||
|
* @type {{ userFilter: string[], rankFilter: string[], ringFilter: string[], rarityFilter: string[] }}
|
||||||
|
*/
|
||||||
|
#activeFilters = {
|
||||||
|
userFilter: [],
|
||||||
|
rankFilter: [],
|
||||||
|
ringFilter: [],
|
||||||
|
rarityFilter: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the filter part between header and directory by composing with
|
||||||
|
* the parent parts rather than replacing them, so future Foundry changes
|
||||||
|
* to Compendium.PARTS are picked up automatically.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_configureRenderParts(options) {
|
||||||
|
const parts = super._configureRenderParts(options);
|
||||||
|
|
||||||
|
const ordered = {};
|
||||||
|
for (const [key, value] of Object.entries(parts)) {
|
||||||
|
ordered[key] = value;
|
||||||
|
if (key === "header") {
|
||||||
|
ordered.filter = {
|
||||||
|
template: `${CONFIG.l5r5e.paths.templates}compendium/filter-bar.html`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ordered;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options);
|
||||||
|
this.#sourcesInThisCompendium = new Set();
|
||||||
|
this.#resolvePermissions();
|
||||||
|
this.#filtersToShow = this.#computeFilterVisibility();
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
async _preparePartContext(partId, context, options) {
|
||||||
|
context = await super._preparePartContext(partId, context, options);
|
||||||
|
|
||||||
|
if (partId === "filter") {
|
||||||
|
const ns = CONFIG.l5r5e.namespace;
|
||||||
|
const allCompendiumReferencesSet = game.settings.get(ns, "all-compendium-references");
|
||||||
|
const hideDisabledOptions = game.settings.get(ns, "compendium-hide-disabled-sources");
|
||||||
|
|
||||||
|
context.filtersToShow = this.#filtersToShow;
|
||||||
|
context.ranks = [1, 2, 3, 4, 5];
|
||||||
|
context.rarities = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
context.rings = ["fire", "water", "earth", "air", "void"];
|
||||||
|
context.hideDisabledOptions = hideDisabledOptions;
|
||||||
|
context.showPlayerView = game.user.isGM && this.#unavailableSourceForPlayersSet.size > 0;
|
||||||
|
|
||||||
|
// Source multiselect options — plain data for {{selectOptions}} in the template.
|
||||||
|
context.sources = [...allCompendiumReferencesSet].map((reference) => ({
|
||||||
|
value: reference,
|
||||||
|
label: CONFIG.l5r5e.sourceReference[reference]?.label ?? reference,
|
||||||
|
translate: true,
|
||||||
|
group:
|
||||||
|
CONFIG.l5r5e.sourceReference[reference]?.type.split(",")[0] ??
|
||||||
|
"l5r5e.multiselect.sources_categories.others",
|
||||||
|
disabled:
|
||||||
|
!this.#sourcesInThisCompendium.has(reference) ||
|
||||||
|
(!game.user.isGM && this.#unavailableSourceForPlayersSet.has(reference)),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partId === "directory") {
|
||||||
|
context.entryFilterData = Object.fromEntries(
|
||||||
|
[...this.collection.index.values()].map((entry) => [
|
||||||
|
entry._id,
|
||||||
|
{
|
||||||
|
rank: entry.system?.rank,
|
||||||
|
ring: entry.system?.ring,
|
||||||
|
rarity: entry.system?.rarity,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
async _onRender(context, options) {
|
||||||
|
await super._onRender(context, options);
|
||||||
|
|
||||||
|
if (options.parts.includes("filter")) {
|
||||||
|
this.#bindButtonFilter(".rank-filter");
|
||||||
|
this.#bindButtonFilter(".rarity-filter");
|
||||||
|
this.#bindButtonFilter(".ring-filter");
|
||||||
|
this.#bindSourceFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reapply filters whenever the filter controls or the entry list changes.
|
||||||
|
if (options.parts.some((p) => p === "filter" || p === "directory")) {
|
||||||
|
this.#reapplyFilters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_preSyncPartState(partId, newElement, priorElement, state) {
|
||||||
|
super._preSyncPartState(partId, newElement, priorElement, state);
|
||||||
|
|
||||||
|
if (partId === "filter") {
|
||||||
|
state.selectedRanks = [...priorElement.querySelectorAll(".rank-filter .selected")].map((element) => element.dataset.rank);
|
||||||
|
state.selectedRarities = [...priorElement.querySelectorAll(".rarity-filter .selected")].map((element) => element.dataset.rarity);
|
||||||
|
state.selectedRings = [...priorElement.querySelectorAll(".ring-filter .selected")].map((element) => element.dataset.ring);
|
||||||
|
state.sourceValue = priorElement.querySelector("l5r5e-multi-select")?.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore filter selections after the filter part has been re-rendered.
|
||||||
|
* The [data-clear] button visibility is derived from whether any values
|
||||||
|
* were restored — no extra state needed.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_syncPartState(partId, newElement, priorElement, state) {
|
||||||
|
super._syncPartState(partId, newElement, priorElement, state);
|
||||||
|
|
||||||
|
if (partId === "filter") {
|
||||||
|
for (const rank of state.selectedRanks ?? []) {
|
||||||
|
newElement.querySelector(`.rank-filter [data-rank="${rank}"]`)?.classList.add("selected");
|
||||||
|
}
|
||||||
|
const rankClear = newElement.querySelector(".rank-filter [data-clear]");
|
||||||
|
if (rankClear) {
|
||||||
|
rankClear.style.display = state.selectedRanks?.length ? "" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const rarity of state.selectedRarities ?? []) {
|
||||||
|
newElement.querySelector(`.rarity-filter [data-rarity="${rarity}"]`)?.classList.add("selected");
|
||||||
|
}
|
||||||
|
const rarityClear = newElement.querySelector(".rarity-filter [data-clear]");
|
||||||
|
if (rarityClear) {
|
||||||
|
rarityClear.style.display = state.selectedRarities?.length ? "" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ring of state.selectedRings ?? []) {
|
||||||
|
newElement.querySelector(`.ring-filter [data-ring="${ring}"]`)?.classList.add("selected");
|
||||||
|
}
|
||||||
|
const ringClear = newElement.querySelector(".ring-filter [data-clear]");
|
||||||
|
if (ringClear) {
|
||||||
|
ringClear.style.display = state.selectedRings?.length ? "" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.sourceValue) {
|
||||||
|
const multiSelect = newElement.querySelector("l5r5e-multi-select");
|
||||||
|
if (multiSelect) {
|
||||||
|
multiSelect.value = state.sourceValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_onMatchSearchEntry(query, entryIds, entry, options) {
|
||||||
|
super._onMatchSearchEntry(query, entryIds, entry, options);
|
||||||
|
if (entry.style.display === "none") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#applyEntryFilter(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snapshot active filter state then re-run the search filter (or walk entries directly as fallback).
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#reapplyFilters() {
|
||||||
|
this.#refreshActiveFilters();
|
||||||
|
|
||||||
|
const searchFilter = this._searchFilters?.[0];
|
||||||
|
if (searchFilter) {
|
||||||
|
searchFilter.filter(null, searchFilter.query);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback
|
||||||
|
for (const entry of this.element.querySelectorAll(".directory-item")) {
|
||||||
|
this.#applyEntryFilter(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read current filter selections from the DOM and cache them in #activeFilters.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#refreshActiveFilters() {
|
||||||
|
const filterElement = this.element.querySelector("[data-application-part=\"filter\"]");
|
||||||
|
const multiSelect = filterElement?.querySelector("l5r5e-multi-select");
|
||||||
|
|
||||||
|
const collectSelected = (containerSelector, dataKey) =>
|
||||||
|
[...(filterElement?.querySelectorAll(`${containerSelector} .selected`) ?? [])]
|
||||||
|
.map((element) => element.dataset[dataKey])
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
this.#activeFilters = {
|
||||||
|
userFilter: multiSelect?.value ?? [],
|
||||||
|
rankFilter: collectSelected(".rank-filter", "rank"),
|
||||||
|
ringFilter: collectSelected(".ring-filter", "ring"),
|
||||||
|
rarityFilter: collectSelected(".rarity-filter", "rarity"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply all active filters to a single directory entry, showing or hiding it accordingly.
|
||||||
|
* @param {HTMLElement} entry
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#applyEntryFilter(entry) {
|
||||||
|
const indexEntry = this.collection.index.get(entry.dataset.entryId);
|
||||||
|
if (!indexEntry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const system = indexEntry.system;
|
||||||
|
const lineSource = system?.source_reference?.source ?? null;
|
||||||
|
const { userFilter, rankFilter, ringFilter, rarityFilter } = this.#activeFilters;
|
||||||
|
let shouldShow = true;
|
||||||
|
|
||||||
|
const sourceUnavailable =
|
||||||
|
(lineSource && this.#unavailableSourceForPlayersSet.has(lineSource)) ||
|
||||||
|
(lineSource === "" && this.#hideEmptySourcesFromPlayers);
|
||||||
|
|
||||||
|
if (sourceUnavailable) {
|
||||||
|
if (game.user.isGM) {
|
||||||
|
entry.classList.add("not-for-players");
|
||||||
|
entry.dataset.tooltip = game.i18n.localize("l5r5e.compendium.not_for_players");
|
||||||
|
} else {
|
||||||
|
shouldShow = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rankFilter.length) {
|
||||||
|
shouldShow &&= rankFilter.includes(String(system?.rank));
|
||||||
|
}
|
||||||
|
if (rarityFilter.length) {
|
||||||
|
shouldShow &&= rarityFilter.includes(String(system?.rarity));
|
||||||
|
}
|
||||||
|
if (ringFilter.length) {
|
||||||
|
shouldShow &&= ringFilter.includes(system?.ring);
|
||||||
|
}
|
||||||
|
if (userFilter.length) {
|
||||||
|
shouldShow &&= userFilter.includes(lineSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.style.display = shouldShow ? "" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate the compendium index to:
|
||||||
|
* 1. Populate #sourcesInThisCompendium for source filter options
|
||||||
|
* 2. Determine which filter controls have enough distinct values to show
|
||||||
|
* @returns {{ rank: boolean, rarity: boolean, source: boolean, ring: boolean }}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#computeFilterVisibility() {
|
||||||
|
const filtersToShow = { rank: false, rarity: false, source: true, ring: false };
|
||||||
|
const firstSeen = { rank: null, rarity: null, ring: null };
|
||||||
|
|
||||||
|
const markIfDistinct = (prop, value) => {
|
||||||
|
if (filtersToShow[prop] || value === undefined || value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (firstSeen[prop] === null) {
|
||||||
|
firstSeen[prop] = value;
|
||||||
|
} else if (firstSeen[prop] !== value) {
|
||||||
|
filtersToShow[prop] = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const entry of this.collection.index.values()) {
|
||||||
|
const sys = entry.system;
|
||||||
|
if (!sys) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sys.rank !== undefined) {
|
||||||
|
markIfDistinct("rank", sys.rank);
|
||||||
|
}
|
||||||
|
if (sys.ring !== undefined) {
|
||||||
|
markIfDistinct("ring", sys.ring);
|
||||||
|
}
|
||||||
|
if (sys.rarity !== undefined) {
|
||||||
|
markIfDistinct("rarity", sys.rarity);
|
||||||
|
}
|
||||||
|
if (sys.source_reference?.source !== undefined) {
|
||||||
|
this.#sourcesInThisCompendium.add(sys.source_reference.source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtersToShow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve which sources are restricted from players and cache the result
|
||||||
|
* in instance-level sets for use by #applyEntryFilter.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#resolvePermissions() {
|
||||||
|
const ns = CONFIG.l5r5e.namespace;
|
||||||
|
const officialSet = game.settings.get(ns, "compendium-official-content-for-players");
|
||||||
|
const unofficialSet = game.settings.get(ns, "compendium-unofficial-content-for-players");
|
||||||
|
const allRefsSet = game.settings.get(ns, "all-compendium-references");
|
||||||
|
|
||||||
|
this.#hideEmptySourcesFromPlayers = game.settings.get(ns, "compendium-hide-empty-sources-from-players");
|
||||||
|
|
||||||
|
this.#unavailableSourceForPlayersSet = new Set(
|
||||||
|
[...allRefsSet].filter((ref) => {
|
||||||
|
if (CONFIG.l5r5e.sourceReference[ref]) {
|
||||||
|
return officialSet.size > 0 ? !officialSet.has(ref) : false;
|
||||||
|
}
|
||||||
|
return unofficialSet.size > 0 ? !unofficialSet.has(ref) : false;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind toggle-selection click handlers to all children of a button filter container.
|
||||||
|
* A [data-clear] element at the end of the container acts as an inline reset:
|
||||||
|
* - It is hidden (display:none in the template) when no values are selected.
|
||||||
|
* - It becomes visible as soon as any value is selected.
|
||||||
|
* - Clicking it deselects all values and hides itself again.
|
||||||
|
* @param {string} containerSelector
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#bindButtonFilter(containerSelector) {
|
||||||
|
const container = this.element.querySelector(containerSelector);
|
||||||
|
if (!container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearButton = container.querySelector("[data-clear]");
|
||||||
|
|
||||||
|
const updateClearButton = () => {
|
||||||
|
if (!clearButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const anySelected = [...container.children].some(
|
||||||
|
(element) => element.dataset.clear === undefined && element.classList.contains("selected")
|
||||||
|
);
|
||||||
|
clearButton.style.display = anySelected ? "" : "none";
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const child of container.children) {
|
||||||
|
child.addEventListener("click", (event) => {
|
||||||
|
const target = event.currentTarget;
|
||||||
|
|
||||||
|
if (target.dataset.clear !== undefined) {
|
||||||
|
// Clicked the clear button — deselect all value elements
|
||||||
|
for (const element of container.children) {
|
||||||
|
element.classList.remove("selected");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Clicked a value element — toggle it
|
||||||
|
target.classList.toggle("selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateClearButton();
|
||||||
|
this.#reapplyFilters();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wire up the change listener on the already-rendered multiselect element.
|
||||||
|
* The element and its options are fully declared in filter-bar.html via
|
||||||
|
* {{selectOptions}} — no imperative construction needed here.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#bindSourceFilter() {
|
||||||
|
const multiSelect = this.element.querySelector("l5r5e-multi-select[name=\"filter-sources\"]");
|
||||||
|
if (!multiSelect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
multiSelect.addEventListener("change", () => this.#reapplyFilters());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the GM player-view button, selecting only the sources that are
|
||||||
|
* both visible to players and present in this specific compendium.
|
||||||
|
* @this {ItemCompendiumL5r5e}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #onApplyPlayerView() {
|
||||||
|
const ns = CONFIG.l5r5e.namespace;
|
||||||
|
const allRefsSet = game.settings.get(ns, "all-compendium-references");
|
||||||
|
|
||||||
|
const availableForPlayers = [...allRefsSet]
|
||||||
|
.filter((ref) => !this.#unavailableSourceForPlayersSet.has(ref))
|
||||||
|
.filter((ref) => this.#sourcesInThisCompendium.has(ref));
|
||||||
|
|
||||||
|
const multiSelect = this.element.querySelector("l5r5e-multi-select[name=\"filter-sources\"]");
|
||||||
|
if (!multiSelect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
multiSelect.value = availableForPlayers;
|
||||||
|
this.#reapplyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register this compendium class and extend the index fields for all Item packs.
|
||||||
|
*/
|
||||||
|
static applyToPacks() {
|
||||||
|
CONFIG.Item.compendiumIndexFields = [
|
||||||
|
...(CONFIG.Item.compendiumIndexFields ?? []),
|
||||||
|
"system.rank",
|
||||||
|
"system.ring",
|
||||||
|
"system.rarity",
|
||||||
|
"system.source_reference.source",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const pack of game.packs.filter((p) => p.metadata.type === "Item")) {
|
||||||
|
pack.applicationClass = ItemCompendiumL5r5e;
|
||||||
|
pack.getIndex(); // rebuild index with new fields — no need to await since this happens before anyone have a chance to act
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,177 @@ export const L5R5E = {
|
|||||||
skillCostMultiplier: 2,
|
skillCostMultiplier: 2,
|
||||||
techniqueCost: 3,
|
techniqueCost: 3,
|
||||||
},
|
},
|
||||||
|
// For rings wound to be aligned, add them first
|
||||||
|
conditions: [{
|
||||||
|
id: "lightly_wounded_fire",
|
||||||
|
name: "l5r5e.conditions.lightly_wounded_fire",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/lightly_wounded_fire.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "lightly_wounded_water",
|
||||||
|
name: "l5r5e.conditions.lightly_wounded_water",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/lightly_wounded_water.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "lightly_wounded_air",
|
||||||
|
name: "l5r5e.conditions.lightly_wounded_air",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/lightly_wounded_air.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "lightly_wounded_earth",
|
||||||
|
name: "l5r5e.conditions.lightly_wounded_earth",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/lightly_wounded_earth.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "lightly_wounded_void",
|
||||||
|
name: "l5r5e.conditions.lightly_wounded_void",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/lightly_wounded_void.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "severely_wounded_fire",
|
||||||
|
name: "l5r5e.conditions.severely_wounded_fire",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/heavily_wounded_fire.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "severely_wounded_water",
|
||||||
|
name: "l5r5e.conditions.severely_wounded_water",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/heavily_wounded_water.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "severely_wounded_air",
|
||||||
|
name: "l5r5e.conditions.severely_wounded_air",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/heavily_wounded_air.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "severely_wounded_earth",
|
||||||
|
name: "l5r5e.conditions.severely_wounded_earth",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/heavily_wounded_earth.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "severely_wounded_void",
|
||||||
|
name: "l5r5e.conditions.severely_wounded_void",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/heavily_wounded_void.webp",
|
||||||
|
system: { id: "L5RCoreCon000016" }
|
||||||
|
},{
|
||||||
|
id: "afflicted",
|
||||||
|
name: "l5r5e.conditions.afflicted",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/afflicted.webp",
|
||||||
|
system: { id: "L5RCoreCon000001" }
|
||||||
|
},{
|
||||||
|
id: "bleeding",
|
||||||
|
name: "l5r5e.conditions.bleeding",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/bleeding.webp",
|
||||||
|
system: { id: "L5RCoreCon000002" }
|
||||||
|
},{
|
||||||
|
id: "burning",
|
||||||
|
name: "l5r5e.conditions.burning",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/burning.webp",
|
||||||
|
system: { id: "L5RCoreCon000003" }
|
||||||
|
},{
|
||||||
|
id: "centered",
|
||||||
|
name: "l5r5e.conditions.centered",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/centered.webp",
|
||||||
|
system: { id: "L5RCoreCon000022" }
|
||||||
|
},{
|
||||||
|
id: "compromised",
|
||||||
|
name: "l5r5e.conditions.compromised",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/compromised.webp",
|
||||||
|
system: { id: "L5RCoreCon000004" }
|
||||||
|
},{
|
||||||
|
id: "dazed",
|
||||||
|
name: "l5r5e.conditions.dazed",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/dazed.webp",
|
||||||
|
system: { id: "L5RCoreCon000005" }
|
||||||
|
},{
|
||||||
|
id: "disoriented",
|
||||||
|
name: "l5r5e.conditions.disoriented",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/disoriented.webp",
|
||||||
|
system: { id: "L5RCoreCon000006" }
|
||||||
|
},{
|
||||||
|
id: "dying",
|
||||||
|
name: "l5r5e.conditions.dying",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/dying_1.webp",
|
||||||
|
system: { id: "L5RCoreCon000007" }
|
||||||
|
},{
|
||||||
|
id: "emboldened",
|
||||||
|
name: "l5r5e.conditions.emboldened",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/emboldened.webp",
|
||||||
|
system: { id: "L5RCoreCon000023" }
|
||||||
|
},{
|
||||||
|
id: "enraged",
|
||||||
|
name: "l5r5e.conditions.enraged",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/enraged.webp",
|
||||||
|
system: { id: "L5RCoreCon000008" }
|
||||||
|
},{
|
||||||
|
id: "exhausted",
|
||||||
|
name: "l5r5e.conditions.exhausted",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/exhausted.webp",
|
||||||
|
system: { id: "L5RCoreCon000009" }
|
||||||
|
},{
|
||||||
|
id: "illness_coughing_illness",
|
||||||
|
name: "l5r5e.conditions.illness_coughing_illness",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/illness_coughing_illness.webp",
|
||||||
|
system: { id: "L5RCoreCon000019" }
|
||||||
|
},{
|
||||||
|
id: "illness_fire_rash",
|
||||||
|
name: "l5r5e.conditions.illness_fire_rash",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/illness_fire_rash.webp",
|
||||||
|
system: { id: "L5RCoreCon000021" }
|
||||||
|
},{
|
||||||
|
id: "illness_gut_sickness",
|
||||||
|
name: "l5r5e.conditions.illness_gut_sickness",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/illness_gut_sickness.webp",
|
||||||
|
system: { id: "L5RCoreCon000018" }
|
||||||
|
},{
|
||||||
|
id: "illness_oozing_sore_disease",
|
||||||
|
name: "l5r5e.conditions.illness_oozing_sore_disease",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/illness_oozing_sore_disease.webp",
|
||||||
|
system: { id: "L5RCoreCon000017" }
|
||||||
|
},{
|
||||||
|
id: "illness_unsteady_illness",
|
||||||
|
name: "l5r5e.conditions.illness_unsteady_illness",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/illness_unsteady_illness.webp",
|
||||||
|
system: { id: "L5RCoreCon000020" }
|
||||||
|
},{
|
||||||
|
id: "immobilized",
|
||||||
|
name: "l5r5e.conditions.immobilized",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/immobilized.webp",
|
||||||
|
system: { id: "L5RCoreCon000010" }
|
||||||
|
},{
|
||||||
|
id: "incapacitated",
|
||||||
|
name: "l5r5e.conditions.incapacitated",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/incapacitated.webp",
|
||||||
|
system: { id: "L5RCoreCon000011" }
|
||||||
|
},{
|
||||||
|
id: "intoxicated",
|
||||||
|
name: "l5r5e.conditions.intoxicated",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/intoxicated.webp",
|
||||||
|
system: { id: "L5RCoreCon000012" }
|
||||||
|
},{
|
||||||
|
id: "possessed",
|
||||||
|
name: "l5r5e.conditions.possessed",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/possesed.webp",
|
||||||
|
system: { id: "L5RCoreCon000024" }
|
||||||
|
},{
|
||||||
|
id: "prone",
|
||||||
|
name: "l5r5e.conditions.prone",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/prone.webp",
|
||||||
|
system: { id: "L5RCoreCon000013" }
|
||||||
|
},{
|
||||||
|
id: "silenced",
|
||||||
|
name: "l5r5e.conditions.silenced",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/silenced.webp",
|
||||||
|
system: { id: "L5RCoreCon000014" }
|
||||||
|
},{
|
||||||
|
id: "unconscious",
|
||||||
|
name: "l5r5e.conditions.unconscious",
|
||||||
|
img: "systems/l5r5e/assets/icons/conditions/unconscious.webp",
|
||||||
|
system: { id: "L5RCoreCon000015" }
|
||||||
|
},{
|
||||||
|
id: "dead",
|
||||||
|
name: "EFFECT.StatusDead",
|
||||||
|
img: "icons/svg/skull.svg"
|
||||||
|
}],
|
||||||
regex: {
|
regex: {
|
||||||
techniqueDifficulty: /^@([TS]):([^|]+?)(?:\|(min|max)(?:\(([^)]+?)\))?)?$/,
|
techniqueDifficulty: /^@([TS]):([^|]+?)(?:\|(min|max)(?:\(([^)]+?)\))?)?$/,
|
||||||
},
|
},
|
||||||
@@ -289,6 +460,7 @@ L5R5E.demeanors = [
|
|||||||
{ id: "adaptable", mod: { water: 2, earth: -2 } },
|
{ id: "adaptable", mod: { water: 2, earth: -2 } },
|
||||||
{ id: "aggressive", mod: { fire: 2, air: -2 } },
|
{ id: "aggressive", mod: { fire: 2, air: -2 } },
|
||||||
{ id: "aggressive", mod: { fire: 2, water: -2 } },
|
{ id: "aggressive", mod: { fire: 2, water: -2 } },
|
||||||
|
{ id: "alluring", mod: { air: 2, earth: -1, fire: -1 } },
|
||||||
{ id: "ambitious", mod: { fire: 2, water: -2 } },
|
{ id: "ambitious", mod: { fire: 2, water: -2 } },
|
||||||
{ id: "amiable", mod: { air: 2, earth: -2 } },
|
{ id: "amiable", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "analytical", mod: { fire: 2, air: -2 } },
|
{ id: "analytical", mod: { fire: 2, air: -2 } },
|
||||||
@@ -299,23 +471,38 @@ L5R5E.demeanors = [
|
|||||||
{ id: "beguiling", mod: { air: 2, earth: -2 } },
|
{ id: "beguiling", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "beguiling", mod: { fire: 2, earth: -2 } },
|
{ id: "beguiling", mod: { fire: 2, earth: -2 } },
|
||||||
{ id: "bitter", mod: { fire: 2, air: -2 } },
|
{ id: "bitter", mod: { fire: 2, air: -2 } },
|
||||||
|
{ id: "bloodthirsty", mod: { fire: 2, water: -2 } },
|
||||||
{ id: "bold", mod: { fire: 1, earth: -1 } },
|
{ id: "bold", mod: { fire: 1, earth: -1 } },
|
||||||
{ id: "calculating", mod: { air: 2, fire: -2 } },
|
{ id: "calculating", mod: { air: 2, fire: -2 } },
|
||||||
{ id: "calm", mod: { fire: 2, air: -2 } },
|
{ id: "calm", mod: { fire: 2, air: -2 } },
|
||||||
{ id: "capricious", mod: { air: 2, earth: -2 } },
|
{ id: "capricious", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "cautious", mod: { air: 2, earth: -2 } },
|
{ id: "cautious", mod: { air: 2, earth: -2 } },
|
||||||
|
{ id: "cautious", mod: { water: 1, void: -1 } },
|
||||||
{ id: "clever", mod: { air: 2, earth: -2 } },
|
{ id: "clever", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "compassionate", mod: { fire: 2, air: -1, water: -1}},
|
{ id: "compassionate", mod: { fire: 2, air: -1, water: -1}},
|
||||||
|
{ id: "compassionate", mod: { water: 2, fire: -2 } },
|
||||||
|
{ id: "compassionate", mod: { water: 2, void: -2 } },
|
||||||
{ id: "confused", mod: { fire: 1, void: 1, air: -2 } },
|
{ id: "confused", mod: { fire: 1, void: 1, air: -2 } },
|
||||||
{ id: "courageous", mod: { air: 2, earth: -2 } },
|
{ id: "courageous", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "cowardly", mod: { earth: 2, fire: -2 } },
|
{ id: "cowardly", mod: { earth: 2, fire: -2 } },
|
||||||
|
{ id: "crestfallen", mod: { void: 2, fire: -2 } },
|
||||||
{ id: "curious", mod: { earth: 1, void: -2 } },
|
{ id: "curious", mod: { earth: 1, void: -2 } },
|
||||||
{ id: "curious", mod: { fire: 1, void: 1, air: -2 } },
|
{ id: "curious", mod: { fire: 1, void: 1, air: -2 } },
|
||||||
|
{ id: "defensive", mod: { fire: 2, air: -2 } },
|
||||||
{ id: "dependable", mod: { fire: 1, water: 1, earth: -2 } },
|
{ id: "dependable", mod: { fire: 1, water: 1, earth: -2 } },
|
||||||
{ id: "detached", mod: { earth: 1, fire: 1, void: -2 } },
|
{ id: "detached", mod: { earth: 1, fire: 1, void: -2 } },
|
||||||
|
{ id: "determined", mod: { earth: 2, air: -2 } },
|
||||||
|
{ id: "devoted", mod: { fire: 2, earth: -2 } },
|
||||||
|
{ id: "direct", mod: { air: 2, fire: -1, water: -1 } },
|
||||||
{ id: "disheartened", mod: { fire: 1, earth: -1 } },
|
{ id: "disheartened", mod: { fire: 1, earth: -1 } },
|
||||||
|
{ id: "dour", mod: { earth: 1, water: 1, air: -1 } },
|
||||||
|
{ id: "duplicitous", mod: { water: 2, fire: -2 } },
|
||||||
|
{ id: "effusive", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "enraged", mod: { air: 1, fire: -2 } },
|
{ id: "enraged", mod: { air: 1, fire: -2 } },
|
||||||
|
{ id: "fanatical", mod: { earth: 1, air: 1, fire: -2 } },
|
||||||
{ id: "feral", mod: { air: 2, fire: -2 } },
|
{ id: "feral", mod: { air: 2, fire: -2 } },
|
||||||
|
{ id: "fervent", mod: { fire: 2, earth: -2 } },
|
||||||
|
{ id: "fervent", mod: { air: 1, water: 1, fire: -1, void: -1 } },
|
||||||
{ id: "fickle", mod: { fire: 2, air: -2 } },
|
{ id: "fickle", mod: { fire: 2, air: -2 } },
|
||||||
{ id: "fierce", mod: { fire: 2, earth: -2 } },
|
{ id: "fierce", mod: { fire: 2, earth: -2 } },
|
||||||
{ id: "flighty", mod: { air: 2, fire: -2 } },
|
{ id: "flighty", mod: { air: 2, fire: -2 } },
|
||||||
@@ -323,32 +510,55 @@ L5R5E.demeanors = [
|
|||||||
{ id: "flippant", mod: { fire: 2, air: -2 } },
|
{ id: "flippant", mod: { fire: 2, air: -2 } },
|
||||||
{ id: "friendly", mod: { fire: 1, earth: -2, water: -2 } },
|
{ id: "friendly", mod: { fire: 1, earth: -2, water: -2 } },
|
||||||
{ id: "gruff", mod: { water: 2, earth: -2 } },
|
{ id: "gruff", mod: { water: 2, earth: -2 } },
|
||||||
|
{ id: "honorable", mod: { fire: 2, earth: -2 } },
|
||||||
|
{ id: "hubristic", mod: { earth: 2, air: -2 } },
|
||||||
{ id: "hungry", mod: { fire: 2, air: -2 } },
|
{ id: "hungry", mod: { fire: 2, air: -2 } },
|
||||||
|
{ id: "idealistic", mod: { water: 2, earth: -2 } },
|
||||||
|
{ id: "idealistic", mod: { earth: 1, water: -1 } },
|
||||||
|
{ id: "imposing", mod: { fire: 2, water: -2 } },
|
||||||
|
{ id: "inquisitive", mod: { earth: 2, water: -2 } },
|
||||||
{ id: "intense", mod: { air: 2, water: -2 } },
|
{ id: "intense", mod: { air: 2, water: -2 } },
|
||||||
{ id: "intense", mod: { fire: 2, water: -2 } },
|
{ id: "intense", mod: { fire: 2, water: -2 } },
|
||||||
{ id: "intimidating", mod: { fire: 2, air: -2 } },
|
{ id: "intimidating", mod: { fire: 2, air: -2 } },
|
||||||
{ id: "irritable", mod: { fire: 2, air: -1, water: -1 } },
|
{ id: "irritable", mod: { fire: 2, air: -1, water: -1 } },
|
||||||
{ id: "loyal", mod: { air: 1, earth: -2, fire: -2 } },
|
{ id: "loyal", mod: { air: 1, earth: -2, fire: -2 } },
|
||||||
{ id: "loyal", mod: { water: 2, fire: -2 } },
|
{ id: "loyal", mod: { water: 2, fire: -2 } },
|
||||||
|
{ id: "methodical", mod: { earth: 2, fire: -2 } },
|
||||||
|
{ id: "meticulous", mod: { fire: 1, water: 1, air: -1, earth: -1 } },
|
||||||
|
{ id: "meticulous", mod: { fire: 1, water: 1, earth: -2 } },
|
||||||
{ id: "mischievous", mod: { fire: 2, air: -2 } },
|
{ id: "mischievous", mod: { fire: 2, air: -2 } },
|
||||||
{ id: "mischievous", mod: { air: 2, earth: -2 } },
|
{ id: "mischievous", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "mischievous", mod: { earth: 2, fire: -2 } },
|
{ id: "mischievous", mod: { earth: 2, fire: -2 } },
|
||||||
|
{ id: "moon_blessed", mod: { water: 2, fire: -2 } },
|
||||||
{ id: "morose", mod: { water: 2, fire: -2 } },
|
{ id: "morose", mod: { water: 2, fire: -2 } },
|
||||||
|
{ id: "near_feral", mod: { air: 1, fire: -1 } },
|
||||||
{ id: "nurturing", mod: { earth: 2, fire: -2 } },
|
{ id: "nurturing", mod: { earth: 2, fire: -2 } },
|
||||||
|
{ id: "obsessed", mod: { earth: 2, air: -2 } },
|
||||||
{ id: "obstinate", mod: { earth: 2, air: -2 } },
|
{ id: "obstinate", mod: { earth: 2, air: -2 } },
|
||||||
{ id: "obstinate", mod: { water: 2, air: -2 } },
|
{ id: "obstinate", mod: { water: 2, air: -2 } },
|
||||||
|
{ id: "otherworldly", mod: { water: 1, void: -1 } },
|
||||||
|
{ id: "outgoing", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "opportunistic", mod: { water: 2, fire: -2 } },
|
{ id: "opportunistic", mod: { water: 2, fire: -2 } },
|
||||||
{ id: "passionate", mod: { earth: 2, air: -2 } },
|
{ id: "passionate", mod: { earth: 2, air: -2 } },
|
||||||
|
{ id: "patient", mod: { fire: 1, water: 1, air: -1, void: -1 } },
|
||||||
|
{ id: "personable", mod: { fire: 2, air: 1, void: -2 } },
|
||||||
{ id: "playful", mod: { earth: 2, water: -2 } },
|
{ id: "playful", mod: { earth: 2, water: -2 } },
|
||||||
{ id: "playful", mod: { fire: 1, air: 1, void: -2 } },
|
{ id: "playful", mod: { fire: 1, air: 1, void: -2 } },
|
||||||
{ id: "power_hungry", mod: { fire: 2, earth: -2 } },
|
{ id: "power_hungry", mod: { fire: 2, earth: -2 } },
|
||||||
{ id: "proud", mod: { fire: 2, earth: -2 } },
|
{ id: "proud", mod: { fire: 2, earth: -2 } },
|
||||||
|
{ id: "refined", mod: { earth: 1, water: 1, air: -1, fire: -1 } },
|
||||||
|
{ id: "reserved", mod: { earth: 2, water: -2 } },
|
||||||
{ id: "restrained", mod: { earth: 2, air: -2 } },
|
{ id: "restrained", mod: { earth: 2, air: -2 } },
|
||||||
|
{ id: "righteous", mod: { water: 2, fire: -1, void: -1 } },
|
||||||
{ id: "scheming", mod: { air: 2, void: -2 } },
|
{ id: "scheming", mod: { air: 2, void: -2 } },
|
||||||
{ id: "serene", mod: { fire: 2, void: -2 } },
|
{ id: "serene", mod: { fire: 2, void: -2 } },
|
||||||
{ id: "serene", mod: { void: 2, fire: -2 } },
|
{ id: "serene", mod: { void: 2, fire: -2 } },
|
||||||
{ id: "serious", mod: { fire: 2, earth: -2 } },
|
{ id: "serious", mod: { fire: 2, earth: -2 } },
|
||||||
{ id: "shrewd", mod: { air: 2, fire: -2 } },
|
{ id: "shrewd", mod: { air: 2, fire: -2 } },
|
||||||
|
{ id: "sinister", mod: { fire: 2, air: -2 } },
|
||||||
|
{ id: "sociable", mod: { air: 1, earth: 1, fire: -1, water: -1 } },
|
||||||
|
{ id: "starved", mod: { water: 2, fire: -2 } },
|
||||||
|
{ id: "stoic", mod: { earth: 2, fire: -2 } },
|
||||||
{ id: "stubborn", mod: { earth: 2, water: -2 } },
|
{ id: "stubborn", mod: { earth: 2, water: -2 } },
|
||||||
{ id: "suspicious", mod: { air: 2, earth: -2 } },
|
{ id: "suspicious", mod: { air: 2, earth: -2 } },
|
||||||
{ id: "teasing", mod: { air: 2, earth: -2 } },
|
{ id: "teasing", mod: { air: 2, earth: -2 } },
|
||||||
@@ -356,5 +566,10 @@ L5R5E.demeanors = [
|
|||||||
{ id: "uncertain", mod: { air: 2, fire: -2 } },
|
{ id: "uncertain", mod: { air: 2, fire: -2 } },
|
||||||
{ id: "unenthused", mod: { earth: 2, fire: -2 } },
|
{ id: "unenthused", mod: { earth: 2, fire: -2 } },
|
||||||
{ id: "vain", mod: { earth: 2, air: -2 } },
|
{ id: "vain", mod: { earth: 2, air: -2 } },
|
||||||
|
{ id: "vengeful", mod: { fire: 2, void: -2 } },
|
||||||
|
{ id: "vindictive", mod: { fire: 2, water: -2 } },
|
||||||
{ id: "wary", mod: { earth: 2, fire: -2 } },
|
{ id: "wary", mod: { earth: 2, fire: -2 } },
|
||||||
|
{ id: "watchful", mod: { fire: 2, earth: -1, void: -1 } },
|
||||||
|
{ id: "wrathful", mod: { fire: 2, earth: -2 } },
|
||||||
|
{ id: "zealous", mod: { earth: 2, fire: -2 } },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,47 +1,110 @@
|
|||||||
import { L5r5eHtmlMultiSelectElement } from "../misc/l5r5e-multiselect.js";
|
import { L5r5eHtmlMultiSelectElement } from "../misc/l5r5e-multiselect.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A subclass of [ArrayField]{@link ArrayField} which supports a set of contained elements.
|
* A Foundry `SetField` that renders as an {@link L5r5eHtmlMultiSelectElement} chip-input.
|
||||||
* Elements in this set are treated as fungible and may be represented in any order or discarded if invalid.
|
*
|
||||||
|
* Use this in a DataModel schema whenever a field stores an unordered collection of
|
||||||
|
* string values drawn from a fixed option list. On form submission the element returns a
|
||||||
|
* comma-separated string; `clean()` splits it back into an Array before Foundry processes
|
||||||
|
* it, and `initialize()` wraps the result in a `Set` for use in the model.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // In a DataModel schema:
|
||||||
|
* skills: new L5r5eSetField({
|
||||||
|
* options: [
|
||||||
|
* { value: "athletics", label: "Athletics" },
|
||||||
|
* { value: "meditation", label: "Meditation", disabled: true, tooltip: "Requires rank 3" },
|
||||||
|
* ]
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* // Renders automatically via {{formGroup}} in a Handlebars template:
|
||||||
|
* // {{formGroup fields.skills name="skills" value=data.skills localize=true}}
|
||||||
|
*
|
||||||
|
* @param {object} options
|
||||||
|
* @param {{ value: string, label: string, disabled?: boolean, tooltip?: string }[]} options.options
|
||||||
|
* Flat list of selectable items. Passed directly to {@link L5r5eHtmlMultiSelectElement.create}.
|
||||||
|
* @param {object[]} [options.groups]
|
||||||
|
* Optional optgroup definitions, forwarded to the element factory unchanged.
|
||||||
|
* @param {boolean} [options.hideDisabledOptions=false]
|
||||||
|
* When true, disabled options are hidden from the dropdown instead of greyed out.
|
||||||
*/
|
*/
|
||||||
export class L5r5eSetField extends foundry.data.fields.SetField {
|
export class L5r5eSetField extends foundry.data.fields.SetField {
|
||||||
|
/**
|
||||||
// We don't get the options we expect when we convert this to input,
|
* Saved constructor options, used to reconstruct the multiselect input on form render.
|
||||||
// So store them here
|
* @type {object}
|
||||||
|
*/
|
||||||
#savedOptions;
|
#savedOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} options
|
||||||
|
* @param {object} context
|
||||||
|
*/
|
||||||
constructor(options = {}, context = {}) {
|
constructor(options = {}, context = {}) {
|
||||||
super(new foundry.data.fields.StringField({
|
super(
|
||||||
choices: options.options.map((option) => option.value)
|
new foundry.data.fields.StringField({
|
||||||
}), options, context);
|
choices: options.options?.map((option) => option.value) ?? [],
|
||||||
|
}),
|
||||||
|
options,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
this.#savedOptions = options;
|
this.#savedOptions = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/**
|
||||||
|
* @param {*} value
|
||||||
|
* @param {object} model
|
||||||
|
* @param {object} options
|
||||||
|
* @return {Set}
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
initialize(value, model, options = {}) {
|
initialize(value, model, options = {}) {
|
||||||
if ( !value ) return value;
|
if (!value || (Array.isArray(value) && value.length === 0)) {
|
||||||
return new Set(super.initialize(value, model, options));
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
return new Set(super.initialize(value, model, options).filter(Boolean));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Set} value
|
||||||
|
* @return {*[]|*}
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
toObject(value) {
|
toObject(value) {
|
||||||
if ( !value ) return value;
|
if (!value) {
|
||||||
return Array.from(value).map(v => this.element.toObject(v));
|
return value;
|
||||||
|
}
|
||||||
|
return Array.from(value).map((v) => this.element.toObject(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/**
|
||||||
/* Form Field Integration */
|
* @param {string|Array} value
|
||||||
/* -------------------------------------------- */
|
* @param {object} options
|
||||||
|
* @return {Array}
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
clean(value, options) {
|
||||||
|
// Settings forms submit comma-separated strings; split before normal cleaning.
|
||||||
|
if (typeof value === "string") {
|
||||||
|
value = value.length ? value.split(",").filter(Boolean) : [];
|
||||||
|
}
|
||||||
|
return super.clean(value, options);
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/**
|
||||||
|
* @param {object} config
|
||||||
|
* @return {L5r5eHtmlMultiSelectElement}
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
_toInput(config) {
|
_toInput(config) {
|
||||||
const e = this.element;
|
|
||||||
return L5r5eHtmlMultiSelectElement.create({
|
return L5r5eHtmlMultiSelectElement.create({
|
||||||
name: config.name,
|
name: config.name,
|
||||||
options: this.#savedOptions.options,
|
options: this.#savedOptions.options,
|
||||||
groups: this.#savedOptions.groups,
|
groups: this.#savedOptions.groups,
|
||||||
value: config.value,
|
value: config.value,
|
||||||
localize: config.localize
|
localize: config.localize,
|
||||||
|
hideDisabledOptions: this.#savedOptions.hideDisabledOptions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,6 +513,9 @@ export class DicePickerDialog extends FormApplication {
|
|||||||
this._updateVoidPointUsage();
|
this._updateVoidPointUsage();
|
||||||
this.render(false);
|
this.render(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Open journal on effect name
|
||||||
|
html.find(".effect-name").on("click", this._openEffectJournal.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -864,4 +867,34 @@ export class DicePickerDialog extends FormApplication {
|
|||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the core linked journal effect if exist
|
||||||
|
* @param {Event} event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _openEffectJournal(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const effectId = $(event.currentTarget).data("effect-id");
|
||||||
|
if (!effectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const effect = this._actor?.effects?.get(effectId);
|
||||||
|
if (!effect?.system?.id && !effect?.system?.uuid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const journal = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack({
|
||||||
|
id: effect.system.id,
|
||||||
|
uuid: effect.system.uuid,
|
||||||
|
type: "JournalEntry",
|
||||||
|
});
|
||||||
|
if (journal) {
|
||||||
|
// Open on the "rules" section. If non exists then it will open the first page
|
||||||
|
journal.sheet.render(true, {pageIndex: 2});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { HelpersL5r5e } from "../helpers.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* L5R Dice Roll n Keep dialog
|
* L5R Dice Roll n Keep dialog
|
||||||
* @extends {FormApplication}
|
* @extends {FormApplication}
|
||||||
@@ -268,6 +270,9 @@ export class RollnKeepDialog extends FormApplication {
|
|||||||
], { jQuery: false });
|
], { jQuery: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open journal on effect name
|
||||||
|
html.find(".effect-name").on("click", this._openEffectJournal.bind(this));
|
||||||
|
|
||||||
// *** Everything below here is only needed if the sheet is editable ***
|
// *** Everything below here is only needed if the sheet is editable ***
|
||||||
if (!this.isEditable) {
|
if (!this.isEditable) {
|
||||||
return;
|
return;
|
||||||
@@ -648,7 +653,7 @@ export class RollnKeepDialog extends FormApplication {
|
|||||||
if (this.roll.l5r5e.isInitiativeRoll) {
|
if (this.roll.l5r5e.isInitiativeRoll) {
|
||||||
let msgOptions = {
|
let msgOptions = {
|
||||||
rnkRoll: this.roll,
|
rnkRoll: this.roll,
|
||||||
rollMode: game.l5r5e.HelpersL5r5e.getRollMode(this._message),
|
messageMode: HelpersL5r5e.getMessageMode(this._message),
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.roll.l5r5e.actor.rollInitiative({
|
await this.roll.l5r5e.actor.rollInitiative({
|
||||||
@@ -664,7 +669,7 @@ export class RollnKeepDialog extends FormApplication {
|
|||||||
// Send it to chat, switch to new message
|
// Send it to chat, switch to new message
|
||||||
this.message = await this.roll.toMessage(
|
this.message = await this.roll.toMessage(
|
||||||
{},
|
{},
|
||||||
{ rollMode: game.l5r5e.HelpersL5r5e.getRollMode(this._message) }
|
{ messageMode: HelpersL5r5e.getMessageMode(this._message) }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -802,4 +807,34 @@ export class RollnKeepDialog extends FormApplication {
|
|||||||
// Re-enable the button
|
// Re-enable the button
|
||||||
button.attr("disabled", false);
|
button.attr("disabled", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the core linked journal effect if exist
|
||||||
|
* @param {Event} event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _openEffectJournal(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const effectId = $(event.currentTarget).data("effect-id");
|
||||||
|
if (!effectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const effect = this.roll.l5r5e?.actor?.effects?.get(effectId);
|
||||||
|
if (!effect?.system?.id && !effect?.system?.uuid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const journal = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack({
|
||||||
|
id: effect.system.id,
|
||||||
|
uuid: effect.system.uuid,
|
||||||
|
type: "JournalEntry",
|
||||||
|
});
|
||||||
|
if (journal) {
|
||||||
|
// Open on the "rules" section. If non exists then it will open the first page
|
||||||
|
journal.sheet.render(true, {pageIndex: 2});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,18 +327,12 @@ export class RollL5r5e extends Roll {
|
|||||||
* This function can either create the ChatMessage directly, or return the data object that will be used to create.
|
* This function can either create the ChatMessage directly, or return the data object that will be used to create.
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
async toMessage(messageData = {}, { rollMode = null } = {}) {
|
async toMessage(messageData = {}, { messageMode = null } = {}) {
|
||||||
// Perform the roll, if it has not yet been rolled
|
// Perform the roll, if it has not yet been rolled
|
||||||
if (!this._evaluated) {
|
if (!this._evaluated) {
|
||||||
await this.evaluate();
|
await this.evaluate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// RollMode
|
|
||||||
const rMode = rollMode || messageData.rollMode || game.settings.get("core", "rollMode");
|
|
||||||
if (rMode) {
|
|
||||||
messageData = ChatMessage.applyRollMode(messageData, rMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force the content to avoid weird foundry behaviour
|
// Force the content to avoid weird foundry behaviour
|
||||||
const content = this.l5r5e.dicesTypes.l5r ? await this.render({}) : this.total;
|
const content = this.l5r5e.dicesTypes.l5r ? await this.render({}) : this.total;
|
||||||
|
|
||||||
@@ -358,9 +352,15 @@ export class RollL5r5e extends Roll {
|
|||||||
);
|
);
|
||||||
messageData.rolls = [this];
|
messageData.rolls = [this];
|
||||||
|
|
||||||
|
// Message mode
|
||||||
|
const mMode = messageMode || messageData.messageMode || game.settings.get("core", "messageMode");
|
||||||
|
if (mMode) {
|
||||||
|
messageData = ChatMessage.applyMode(messageData, mMode);
|
||||||
|
}
|
||||||
|
|
||||||
// Either create the message or just return the chat data
|
// Either create the message or just return the chat data
|
||||||
return ChatMessage.implementation.create(messageData, {
|
return ChatMessage.implementation.create(messageData, {
|
||||||
rollMode: rMode,
|
messageMode: mMode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -166,6 +166,15 @@ export class GmMonitor extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Apply global interface theme if it is set
|
||||||
|
if (!this.options.classes.includes("themed")) {
|
||||||
|
this.element.classList.remove("theme-light", "theme-dark");
|
||||||
|
const {colorScheme} = game.settings.get("core", "uiConfig");
|
||||||
|
if (colorScheme.interface) {
|
||||||
|
this.element.classList.add("themed", `theme-${colorScheme.interface}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override ApplicationV2 */
|
/** @override ApplicationV2 */
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ export class GmToolbox extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
/** @override ApplicationV2 */
|
/** @override ApplicationV2 */
|
||||||
static get DEFAULT_OPTIONS() { return {
|
static get DEFAULT_OPTIONS() { return {
|
||||||
id: "l5r5e-gm-toolbox",
|
id: "l5r5e-gm-toolbox",
|
||||||
|
classes: ["faded-ui"],
|
||||||
window: {
|
window: {
|
||||||
contentClasses: ["l5r5e", "gm-toolbox", "faded-ui"],
|
contentClasses: ["l5r5e", "gm-toolbox"],
|
||||||
title: "l5r5e.gm.toolbox.title",
|
title: "l5r5e.gm.toolbox.title",
|
||||||
minimizable: true,
|
minimizable: false,
|
||||||
},
|
},
|
||||||
position: {
|
position: {
|
||||||
width: "auto",
|
width: "auto",
|
||||||
@@ -89,6 +90,19 @@ export class GmToolbox extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
options.position.left = 220; //x - 630;
|
options.position.left = 220; //x - 630;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _onRender(context, options) {
|
||||||
|
await super._onRender(context, options);
|
||||||
|
|
||||||
|
if (this.options.classes.includes("themed")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.element.classList.remove("theme-light", "theme-dark");
|
||||||
|
const {colorScheme} = game.settings.get("core", "uiConfig");
|
||||||
|
if (colorScheme.interface) {
|
||||||
|
this.element.classList.add("themed", `theme-${colorScheme.interface}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GM Toolbox should not be removed when toggling the main menu with the esc key etc.
|
* The GM Toolbox should not be removed when toggling the main menu with the esc key etc.
|
||||||
* @override ApplicationV2
|
* @override ApplicationV2
|
||||||
@@ -205,6 +219,7 @@ export class GmToolbox extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await actor.removeConditions(new Set(["exhausted"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
GmToolbox.#uiNotification(allActors, "sleep");
|
GmToolbox.#uiNotification(allActors, "sleep");
|
||||||
@@ -216,7 +231,8 @@ export class GmToolbox extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
static async #onSceneEnd(event) {
|
static async #onSceneEnd(event) {
|
||||||
const allActors = event.button !== 0;
|
const allActors = event.button !== 0;
|
||||||
for await (const actor of game.actors.contents) {
|
for await (const actor of game.actors.contents) {
|
||||||
if (!GmToolbox.#updatableCharacter(allActors, actor)) {
|
if (!GmToolbox.#updatableCharacter(allActors, actor)
|
||||||
|
|| actor.statuses.has("exhausted")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,14 @@ export const RegisterHandlebars = function () {
|
|||||||
return sanitizeIfFail(game.i18n.localize(isYes ? "Yes" : "No"));
|
return sanitizeIfFail(game.i18n.localize(isYes ? "Yes" : "No"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Replace "localize (localize" for partial var (broken in v14)
|
||||||
|
Handlebars.registerHelper("localizeEmbedded", function (string, objects) {
|
||||||
|
Object.keys(objects.hash).forEach(key => {
|
||||||
|
string = string.replace("{" + key + "}", objects.hash[key]);
|
||||||
|
});
|
||||||
|
return sanitizeIfFail(game.i18n.localize(string));
|
||||||
|
});
|
||||||
|
|
||||||
/* ------------------------------------ */
|
/* ------------------------------------ */
|
||||||
/* Dice */
|
/* Dice */
|
||||||
/* ------------------------------------ */
|
/* ------------------------------------ */
|
||||||
|
|||||||
@@ -189,12 +189,12 @@ export class HelpersL5r5e {
|
|||||||
|
|
||||||
// Unknown pack object, iterate all packs
|
// Unknown pack object, iterate all packs
|
||||||
if (!document) {
|
if (!document) {
|
||||||
for (const comp of game.packs) {
|
await Promise.all(game.packs.map(async (comp) => {
|
||||||
const tmpData = await comp.getDocument(id);
|
const tmpData = await comp.getDocument(id);
|
||||||
if (tmpData) {
|
if (tmpData) {
|
||||||
document = HelpersL5r5e.createDocumentFromCompendium({ type, data: tmpData });
|
document = HelpersL5r5e.createDocumentFromCompendium({ type, data: tmpData });
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final
|
// Final
|
||||||
@@ -629,21 +629,21 @@ export class HelpersL5r5e {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the RollMode for this ChatData
|
* Return the MessageMode for this ChatData
|
||||||
* @param {object} chatData
|
* @param {object} chatData
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
static getRollMode(chatData) {
|
static getMessageMode(chatData) {
|
||||||
if (chatData.whisper.length === 1 && chatData.whisper[0] === game.user.id) {
|
if (chatData.whisper.length === 1 && chatData.whisper[0] === game.user.id) {
|
||||||
return "selfroll";
|
return "self";
|
||||||
}
|
}
|
||||||
if (chatData.blind) {
|
if (chatData.blind) {
|
||||||
return "blindroll";
|
return "blind";
|
||||||
}
|
}
|
||||||
if (chatData.whisper.length > 1) {
|
if (chatData.whisper.length > 1) {
|
||||||
return "gmroll";
|
return "gm";
|
||||||
}
|
}
|
||||||
return "roll";
|
return "public";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -690,7 +690,7 @@ export class HelpersL5r5e {
|
|||||||
* @param {object} opt drawMany config option object
|
* @param {object} opt drawMany config option object
|
||||||
* @return {Promise<{RollTableDraw}>} The drawn results
|
* @return {Promise<{RollTableDraw}>} The drawn results
|
||||||
*/
|
*/
|
||||||
static async drawManyFromPack(pack, tableName, retrieve = 5, opt = { rollMode: "selfroll" }) {
|
static async drawManyFromPack(pack, tableName, retrieve = 5, opt = { messageMode: "self" }) {
|
||||||
const comp = await game.packs.get(pack);
|
const comp = await game.packs.get(pack);
|
||||||
if (!comp) {
|
if (!comp) {
|
||||||
console.log(`L5R5E | Helpers | Pack not found[${pack}]`);
|
console.log(`L5R5E | Helpers | Pack not found[${pack}]`);
|
||||||
|
|||||||
@@ -1,6 +1,20 @@
|
|||||||
import { L5r5eHtmlMultiSelectElement } from "./misc/l5r5e-multiselect.js";
|
import { ItemCompendiumL5r5e } from "./compendium/l5r5e-item-compendium.js"
|
||||||
|
|
||||||
export default class HooksL5r5e {
|
export default class HooksL5r5e {
|
||||||
|
/**
|
||||||
|
* Do initialization
|
||||||
|
*/
|
||||||
|
static async init() {
|
||||||
|
// L5R conditions
|
||||||
|
if (game.settings.get(CONFIG.l5r5e.namespace, "show-all-status-effects")) {
|
||||||
|
// Add L5R conditions to foundry conditions (don't restrict users)
|
||||||
|
CONFIG.statusEffects.push(...CONFIG.l5r5e.conditions);
|
||||||
|
} else {
|
||||||
|
// L5R conditions only
|
||||||
|
CONFIG.statusEffects = CONFIG.l5r5e.conditions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do anything after initialization but before ready
|
* Do anything after initialization but before ready
|
||||||
*/
|
*/
|
||||||
@@ -12,6 +26,8 @@ export default class HooksL5r5e {
|
|||||||
) {
|
) {
|
||||||
game.babele.setSystemTranslationsDir("babele"); // Since Babele v2.0.7
|
game.babele.setSystemTranslationsDir("babele"); // Since Babele v2.0.7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemCompendiumL5r5e.applyToPacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,7 +82,7 @@ export default class HooksL5r5e {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.settings.set(CONFIG.l5r5e.namespace, "all-compendium-references", Array.from(references));
|
game.settings.set(CONFIG.l5r5e.namespace, "all-compendium-references", references);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -231,236 +247,6 @@ export default class HooksL5r5e {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compendium display (Add filters)
|
|
||||||
*/
|
|
||||||
static async renderCompendium(app, html, data) {
|
|
||||||
html = $(html); // basic patch for v13
|
|
||||||
|
|
||||||
if (app.collection.documentName === "Item") {
|
|
||||||
const content = await app.collection.getDocuments();
|
|
||||||
const sourcesInThisCompendium = new Set([]);
|
|
||||||
const filtersToShow = {
|
|
||||||
rank: false,
|
|
||||||
rarity: false,
|
|
||||||
source: false,
|
|
||||||
ring: false,
|
|
||||||
};
|
|
||||||
// Used to auto hide same values for a full compendium
|
|
||||||
const previousValue = {
|
|
||||||
rank: null,
|
|
||||||
rarity: null,
|
|
||||||
source: null,
|
|
||||||
ring: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cache
|
|
||||||
const header = html.find(".directory-header");
|
|
||||||
const entries = html.find(".directory-item");
|
|
||||||
|
|
||||||
// Add additional data to the entries to make it faster to lookup.
|
|
||||||
// Add Ring/rank/rarity information
|
|
||||||
for (const document of content) {
|
|
||||||
const entry = entries.filter(`[data-entry-id="${document.id}"]`);
|
|
||||||
|
|
||||||
// Hide filter if only one value of this type is found in the compendium
|
|
||||||
const autoDisplayFilter = (props, documentData = null) => {
|
|
||||||
documentData ??= document.system[props];
|
|
||||||
|
|
||||||
if (filtersToShow[props] || previousValue[props] === documentData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
filtersToShow[props] = previousValue[props] !== null && previousValue[props] !== documentData;
|
|
||||||
previousValue[props] = documentData;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (document.system?.rank) {
|
|
||||||
autoDisplayFilter('rank');
|
|
||||||
entry.data("rank", document.system.rank);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.system?.source_reference.source) {
|
|
||||||
autoDisplayFilter('source', document.system.source_reference.source);
|
|
||||||
sourcesInThisCompendium.add(document.system.source_reference.source);
|
|
||||||
entry.data("source", document.system.source_reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.system?.ring) {
|
|
||||||
autoDisplayFilter('ring');
|
|
||||||
entry.data("ring", document.system.ring);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.system?.rarity) {
|
|
||||||
autoDisplayFilter('rarity');
|
|
||||||
entry.data("rarity", document.system.rarity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add ring/rank/rarity information on the item in the compendium view
|
|
||||||
if (document.system?.ring || document.system?.rarity || document.system?.rank) {
|
|
||||||
const ringRarityRank = await foundry.applications.handlebars.renderTemplate(`${CONFIG.l5r5e.paths.templates}compendium/ring-rarity-rank.html`, document.system);
|
|
||||||
entry.append(ringRarityRank);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup filters
|
|
||||||
const officialContent = game.settings.get(CONFIG.l5r5e.namespace, "compendium-official-content-for-players");
|
|
||||||
const unofficialContent = game.settings.get(CONFIG.l5r5e.namespace, "compendium-unofficial-content-for-players");
|
|
||||||
const allCompendiumReferences = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references")
|
|
||||||
const hideEmptySourcesFromPlayers = game.settings.get(CONFIG.l5r5e.namespace, "compendium-hide-empty-sources-from-players");
|
|
||||||
|
|
||||||
const unavailableSourceForPlayers = allCompendiumReferences.filter((element) => {
|
|
||||||
if (CONFIG.l5r5e.sourceReference[element]) {
|
|
||||||
return officialContent.size > 0 ? !officialContent.has(element) : false;
|
|
||||||
}
|
|
||||||
return unofficialContent.size > 0 ? !unofficialContent.has(element) : false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create filter function
|
|
||||||
const applyCompendiumFilter = () => {
|
|
||||||
const userFilter = header.find("l5r5e-multi-select").val();
|
|
||||||
const rankFilter = header.find(".rank-filter .selected").data("rank");
|
|
||||||
const ringFilter = header.find(".ring-filter .selected").data("ring");
|
|
||||||
const rarityFilter = header.find(".rarity-filter .selected").data("rarity");
|
|
||||||
|
|
||||||
entries.each(function () {
|
|
||||||
const lineSource = $(this).data("source")?.source;
|
|
||||||
|
|
||||||
// We might have stuff in the compendium view that does not have a source (folders etc.) Ignore those.
|
|
||||||
if (lineSource === null || lineSource === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let shouldShow = true;
|
|
||||||
|
|
||||||
// Handle unavailable sources
|
|
||||||
if (unavailableSourceForPlayers.has(lineSource)) {
|
|
||||||
if (game.user.isGM) {
|
|
||||||
shouldShow &= true;
|
|
||||||
$(this)
|
|
||||||
.addClass("not-for-players")
|
|
||||||
.attr("data-tooltip", game.i18n.localize("l5r5e.compendium.not_for_players"));
|
|
||||||
} else {
|
|
||||||
shouldShow &= false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle empty sources
|
|
||||||
if (lineSource === "" && hideEmptySourcesFromPlayers) {
|
|
||||||
if (game.user.isGM) {
|
|
||||||
shouldShow &= true;
|
|
||||||
$(this)
|
|
||||||
.addClass("not-for-players")
|
|
||||||
.attr("data-tooltip", game.i18n.localize("l5r5e.compendium.not_for_players"));
|
|
||||||
} else {
|
|
||||||
shouldShow &= false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply filters
|
|
||||||
if (rankFilter) {
|
|
||||||
shouldShow &= $(this).data("rank") == rankFilter;
|
|
||||||
}
|
|
||||||
if (userFilter?.length) {
|
|
||||||
shouldShow &= userFilter.includes(lineSource);
|
|
||||||
}
|
|
||||||
if (ringFilter) {
|
|
||||||
shouldShow &= $(this).data("ring") == ringFilter;
|
|
||||||
}
|
|
||||||
if (rarityFilter >= 0) {
|
|
||||||
shouldShow &= $(this).data("rarity") == rarityFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show or hide this entry based on the result
|
|
||||||
shouldShow ? $(this).show() : $(this).hide();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Filter setup
|
|
||||||
const addFilter = async (filterType, templateFile, templateData) => {
|
|
||||||
if (!filtersToShow[filterType]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const filterTemplate = await foundry.applications.handlebars.renderTemplate(
|
|
||||||
`${CONFIG.l5r5e.paths.templates}compendium/${templateFile}.html`,
|
|
||||||
templateData
|
|
||||||
);
|
|
||||||
header.append(filterTemplate);
|
|
||||||
|
|
||||||
header.find(`.${filterType}-filter`).children().each(function () {
|
|
||||||
$(this).on("click", (event) => {
|
|
||||||
const selected = $(event.target).hasClass("selected");
|
|
||||||
header.find(`.${filterType}-filter`).children().removeClass("selected");
|
|
||||||
$(event.target).toggleClass("selected", !selected);
|
|
||||||
applyCompendiumFilter();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add Rank, Rarity, Ring Filters
|
|
||||||
await Promise.all([
|
|
||||||
addFilter('rank' , 'rank-filter', { type: "rank", number: [1, 2, 3, 4, 5] }),
|
|
||||||
addFilter('rarity', 'rank-filter', { type: "rarity", number: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }),
|
|
||||||
addFilter('ring' , 'ring-filter', {}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (filtersToShow.source) {
|
|
||||||
// Build the source select
|
|
||||||
const selectableSources = allCompendiumReferences.map((reference) => ({
|
|
||||||
value: reference,
|
|
||||||
label: CONFIG.l5r5e.sourceReference[reference]?.label ?? reference,
|
|
||||||
translate: true,
|
|
||||||
group: CONFIG.l5r5e.sourceReference[reference]?.type.split(",")[0] ?? "l5r5e.multiselect.sources_categories.others",
|
|
||||||
disabled: !sourcesInThisCompendium.has(reference) || (!game.user.isGM && unavailableSourceForPlayers.has(reference))
|
|
||||||
}));
|
|
||||||
const filterSourcesBox = L5r5eHtmlMultiSelectElement.create({
|
|
||||||
name: "filter-sources",
|
|
||||||
options: selectableSources,
|
|
||||||
localize: true,
|
|
||||||
});
|
|
||||||
header.append(filterSourcesBox.outerHTML);
|
|
||||||
$("l5r5e-multi-select").on("change", applyCompendiumFilter);
|
|
||||||
|
|
||||||
// If gm add an extra button to easily filter the content to see the same stuff as a player
|
|
||||||
if (game.user.isGM && unavailableSourceForPlayers.length > 0) {
|
|
||||||
const buttonHTML = `<button type="button" class="gm" data-tooltip="${game.i18n.localize('l5r5e.multiselect.player_filter_tooltip')}">`
|
|
||||||
+ game.i18n.localize('l5r5e.multiselect.player_filter_label')
|
|
||||||
+ '</button>'
|
|
||||||
|
|
||||||
const filterPlayerView = allCompendiumReferences
|
|
||||||
.filter((item) => !unavailableSourceForPlayers.has(item))
|
|
||||||
.filter((item) => sourcesInThisCompendium.has(item));
|
|
||||||
|
|
||||||
$(buttonHTML).appendTo($(header).find("l5r5e-multi-select")).click(function() {
|
|
||||||
header.find("l5r5e-multi-select")[0].value = filterPlayerView;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This delay is a workaround and should be addressed in another way.
|
|
||||||
// This is ugly but if we hide the content too early then it won't be hidden for some reason.
|
|
||||||
// Current guess is that the foundry search filter is doing something.
|
|
||||||
// Adding a delay here so that we hide the content. This will fail on slow computers/network...
|
|
||||||
setTimeout(() => {
|
|
||||||
applyCompendiumFilter();
|
|
||||||
}, 250);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static updateCompendium(pack, documents, options, userId) {
|
|
||||||
documents.forEach((document) => {
|
|
||||||
const inc_reference = document?.system?.source_reference?.source?.trim();
|
|
||||||
if (!!inc_reference) {
|
|
||||||
const references = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references");
|
|
||||||
if (!references.includes(inc_reference)) {
|
|
||||||
references.push(inc_reference);
|
|
||||||
game.settings.set(CONFIG.l5r5e.namespace, "all-compendium-references", references);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DiceSoNice - Add L5R DicePresets
|
* DiceSoNice - Add L5R DicePresets
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -149,5 +149,8 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
|
|||||||
xp_used: xp_used,
|
xp_used: xp_used,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Re-render sheet
|
||||||
|
this.render(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ export class BaseItemSheetL5r5e extends foundry.appv1.sheets.ItemSheet {
|
|||||||
sheetData.data.system.source_reference.source = game.i18n.localize(label_or_reference);
|
sheetData.data.system.source_reference.source = game.i18n.localize(label_or_reference);
|
||||||
|
|
||||||
// Translate list of available references
|
// Translate list of available references
|
||||||
const all_references = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references");
|
const all_referencesSet = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references");
|
||||||
sheetData.source_references = all_references.map((reference) => {
|
sheetData.source_references = [...all_referencesSet].map((reference) => {
|
||||||
const label_or_reference = CONFIG.l5r5e.sourceReference[reference]?.label ?? reference
|
const label_or_reference = CONFIG.l5r5e.sourceReference[reference]?.label ?? reference
|
||||||
return game.i18n.localize(label_or_reference)
|
return game.i18n.localize(label_or_reference)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -37,31 +37,26 @@ export class ItemSheetL5r5e extends BaseItemSheetL5r5e {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
async _prepareProperties(sheetData) {
|
async _prepareProperties(sheetData) {
|
||||||
sheetData.data.propertiesList = [];
|
sheetData.data.propertiesList = await Promise.all((sheetData.data?.system?.properties || []).map(async (property) => {
|
||||||
|
|
||||||
if (Array.isArray(sheetData.data.system.properties)) {
|
|
||||||
const props = [];
|
|
||||||
for (const property of sheetData.data.system.properties) {
|
|
||||||
const gameProp = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack({ id: property.id, type: "Item" });
|
const gameProp = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack({ id: property.id, type: "Item" });
|
||||||
if (gameProp) {
|
if (gameProp) {
|
||||||
sheetData.data.propertiesList.push(gameProp);
|
return gameProp;
|
||||||
props.push({ id: gameProp.id, name: gameProp.name });
|
}
|
||||||
} else {
|
|
||||||
// Item not found
|
// Item not found
|
||||||
console.warn(`L5R5E | IS | Unknown property id[${property.id}], name[${property.name}]`);
|
console.warn(`L5R5E | IS | Unknown property id[${property.id}], name[${property.name}]`);
|
||||||
sheetData.data.propertiesList.push({
|
return {
|
||||||
id: property.id,
|
id: property.id,
|
||||||
name: property.name,
|
name: property.name,
|
||||||
type: "property",
|
type: "property",
|
||||||
img: "systems/l5r5e/assets/icons/items/property.svg",
|
img: "systems/l5r5e/assets/icons/items/property.svg",
|
||||||
removed: true,
|
removed: true,
|
||||||
});
|
};
|
||||||
}
|
}));
|
||||||
}
|
|
||||||
sheetData.data.system.properties = props;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to events from the sheet.
|
* Subscribe to events from the sheet.
|
||||||
* @param {jQuery} html HTML content of the sheet.
|
* @param {jQuery} html HTML content of the sheet.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { ActorL5r5e } from "./actor.js";
|
|||||||
import { CharacterSheetL5r5e } from "./actors/character-sheet.js";
|
import { CharacterSheetL5r5e } from "./actors/character-sheet.js";
|
||||||
import { NpcSheetL5r5e } from "./actors/npc-sheet.js";
|
import { NpcSheetL5r5e } from "./actors/npc-sheet.js";
|
||||||
import { ArmySheetL5r5e } from "./actors/army-sheet.js";
|
import { ArmySheetL5r5e } from "./actors/army-sheet.js";
|
||||||
|
import { RulerL5r5e, TokenRulerL5r5e } from "./tatical-grid-rulers.js";
|
||||||
// Dice and rolls
|
// Dice and rolls
|
||||||
import { L5rBaseDie } from "./dice/dietype/l5r-base-die.js";
|
import { L5rBaseDie } from "./dice/dietype/l5r-base-die.js";
|
||||||
import { AbilityDie } from "./dice/dietype/ability-die.js";
|
import { AbilityDie } from "./dice/dietype/ability-die.js";
|
||||||
@@ -37,6 +38,8 @@ import { ArmyFortificationSheetL5r5e } from "./items/army-fortification-sheet.js
|
|||||||
// JournalEntry
|
// JournalEntry
|
||||||
import { JournalL5r5e } from "./journal.js";
|
import { JournalL5r5e } from "./journal.js";
|
||||||
import { BaseJournalSheetL5r5e } from "./journals/base-journal-sheet.js";
|
import { BaseJournalSheetL5r5e } from "./journals/base-journal-sheet.js";
|
||||||
|
// Compendium
|
||||||
|
import { CompendiumDirectoryL5r5e } from "./compendium/l5r5e-compendium-directory.js";
|
||||||
// Specific
|
// Specific
|
||||||
import { MigrationL5r5e } from "./migration.js";
|
import { MigrationL5r5e } from "./migration.js";
|
||||||
import { GmToolbox } from "./gm/gm-toolbox.js";
|
import { GmToolbox } from "./gm/gm-toolbox.js";
|
||||||
@@ -44,8 +47,10 @@ import { GmMonitor } from "./gm/gm-monitor.js";
|
|||||||
import { Storage } from "./storage.js";
|
import { Storage } from "./storage.js";
|
||||||
// Misc
|
// Misc
|
||||||
import { L5r5eHtmlMultiSelectElement } from "./misc/l5r5e-multiselect.js";
|
import { L5r5eHtmlMultiSelectElement } from "./misc/l5r5e-multiselect.js";
|
||||||
|
import { L5R5eHtmlComboBoxElement } from "./misc/l5r5e-combo-box.js";
|
||||||
|
|
||||||
window.customElements.define(L5r5eHtmlMultiSelectElement.tagName, L5r5eHtmlMultiSelectElement);
|
window.customElements.define(L5r5eHtmlMultiSelectElement.tagName, L5r5eHtmlMultiSelectElement);
|
||||||
|
window.customElements.define(L5R5eHtmlComboBoxElement.tagName, L5R5eHtmlComboBoxElement);
|
||||||
|
|
||||||
/* ------------------------------------ */
|
/* ------------------------------------ */
|
||||||
/* Initialize system */
|
/* Initialize system */
|
||||||
@@ -65,6 +70,23 @@ Hooks.once("init", async () => {
|
|||||||
// Global access to L5R Config
|
// Global access to L5R Config
|
||||||
CONFIG.l5r5e = L5R5E;
|
CONFIG.l5r5e = L5R5E;
|
||||||
|
|
||||||
|
// Setting up sidebar icons
|
||||||
|
CONFIG.ChatMessage.sidebarIcon = "l5r5e chatIcon";
|
||||||
|
CONFIG.Combat.sidebarIcon = "l5r5e combatIcon";
|
||||||
|
CONFIG.Scene.sidebarIcon = "l5r5e sceneIcon";
|
||||||
|
CONFIG.Actor.sidebarIcon = "l5r5e actorIcon";
|
||||||
|
CONFIG.Item.sidebarIcon = "l5r5e itemIcon";
|
||||||
|
CONFIG.JournalEntry.sidebarIcon = "l5r5e journalIcon";
|
||||||
|
CONFIG.RollTable.sidebarIcon = "l5r5e rolltableIcon";
|
||||||
|
CONFIG.Playlist.sidebarIcon = "l5r5e playlistIcon";
|
||||||
|
// Note: We don't have any custom icons here so just append l5r5e and type
|
||||||
|
CONFIG.Cards.sidebarIcon += " l5r5e cardsIcon";
|
||||||
|
CONFIG.Macro.sidebarIcon += " l5r5e macroIcon";
|
||||||
|
|
||||||
|
// The compendium and the settings menu is registered a little different.
|
||||||
|
foundry.applications.sidebar.Sidebar.TABS.compendium.icon = "l5r5e compendiumIcon";
|
||||||
|
foundry.applications.sidebar.Sidebar.TABS.settings.icon = "l5r5e settingsIcon";
|
||||||
|
|
||||||
// Assign custom classes and constants here
|
// Assign custom classes and constants here
|
||||||
CONFIG.Combat.documentClass = CombatL5r5e;
|
CONFIG.Combat.documentClass = CombatL5r5e;
|
||||||
CONFIG.Actor.documentClass = ActorL5r5e;
|
CONFIG.Actor.documentClass = ActorL5r5e;
|
||||||
@@ -72,6 +94,10 @@ Hooks.once("init", async () => {
|
|||||||
CONFIG.Item.documentClass = ItemL5r5e;
|
CONFIG.Item.documentClass = ItemL5r5e;
|
||||||
CONFIG.JournalEntry.documentClass = JournalL5r5e;
|
CONFIG.JournalEntry.documentClass = JournalL5r5e;
|
||||||
CONFIG.JournalEntry.sheetClass = BaseJournalSheetL5r5e;
|
CONFIG.JournalEntry.sheetClass = BaseJournalSheetL5r5e;
|
||||||
|
CONFIG.Token.rulerClass = TokenRulerL5r5e;
|
||||||
|
CONFIG.Canvas.rulerClass = RulerL5r5e;
|
||||||
|
|
||||||
|
CONFIG.ui.compendium = CompendiumDirectoryL5r5e;
|
||||||
|
|
||||||
// Define custom Roll class
|
// Define custom Roll class
|
||||||
CONFIG.Dice.rolls.unshift(RollL5r5e);
|
CONFIG.Dice.rolls.unshift(RollL5r5e);
|
||||||
@@ -252,6 +278,7 @@ Hooks.once("init", async () => {
|
|||||||
/* ------------------------------------ */
|
/* ------------------------------------ */
|
||||||
Hooks.once("setup", HooksL5r5e.setup);
|
Hooks.once("setup", HooksL5r5e.setup);
|
||||||
Hooks.once("ready", HooksL5r5e.ready);
|
Hooks.once("ready", HooksL5r5e.ready);
|
||||||
|
Hooks.once("init", HooksL5r5e.init);
|
||||||
Hooks.once("diceSoNiceReady", (dice3d) => HooksL5r5e.diceSoNiceReady(dice3d));
|
Hooks.once("diceSoNiceReady", (dice3d) => HooksL5r5e.diceSoNiceReady(dice3d));
|
||||||
|
|
||||||
/* ------------------------------------ */
|
/* ------------------------------------ */
|
||||||
@@ -261,6 +288,4 @@ Hooks.on("renderSidebarTab", (app, html, data) => HooksL5r5e.renderSidebarTab(ap
|
|||||||
Hooks.on("activateSettings", async (app)=> HooksL5r5e.activateSettings(app));
|
Hooks.on("activateSettings", async (app)=> HooksL5r5e.activateSettings(app));
|
||||||
Hooks.on("renderChatMessageHTML", (message, html, data) => HooksL5r5e.renderChatMessage(message, html, data));
|
Hooks.on("renderChatMessageHTML", (message, html, data) => HooksL5r5e.renderChatMessage(message, html, data));
|
||||||
Hooks.on("renderCombatTracker", (app, html, data) => HooksL5r5e.renderCombatTracker(app, html, data));
|
Hooks.on("renderCombatTracker", (app, html, data) => HooksL5r5e.renderCombatTracker(app, html, data));
|
||||||
Hooks.on("renderCompendium", async (app, html, data) => HooksL5r5e.renderCompendium(app, html, data));
|
|
||||||
Hooks.on("diceSoNiceRollStart", (messageId, context) => HooksL5r5e.diceSoNiceRollStart(messageId, context));
|
Hooks.on("diceSoNiceRollStart", (messageId, context) => HooksL5r5e.diceSoNiceRollStart(messageId, context));
|
||||||
Hooks.on("updateCompendium", (pack, documents, options, userId) => HooksL5r5e.updateCompendium(pack, documents, options, userId));
|
|
||||||
|
|||||||
257
system/scripts/misc/l5r5e-combo-box.js
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
import { DropdownMixin } from "./l5r5e-dropdown-mixin.js";
|
||||||
|
|
||||||
|
const { AbstractFormInputElement } = foundry.applications.elements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom `<l5r5e-combo-box>` combining a free-text input with a filterable option dropdown.
|
||||||
|
*
|
||||||
|
* Stores a **single string value** — either a predefined option's `value` attribute, or
|
||||||
|
* whatever the user typed freely. Use this when a field holds exactly one value, whether
|
||||||
|
* chosen from a list or entered manually (e.g. a weapon name, a title, a custom skill).
|
||||||
|
* For storing multiple values from a list, use {@link L5r5eHtmlMultiSelectElement} instead.
|
||||||
|
*
|
||||||
|
* Picking a predefined option stores its `value` attribute while displaying its human-readable
|
||||||
|
* label. Free-typing stores the typed string as both value and label. Fires `input` on every
|
||||||
|
* keystroke and `change` only on commit (blur or Enter) to avoid triggering sheet re-renders
|
||||||
|
* mid-typing. Picking from the dropdown fires both `input` and `change` immediately.
|
||||||
|
*
|
||||||
|
* Use `{{selectOptions}}` without `selected=` to render the available options, and set the
|
||||||
|
* current value via the `value` attribute on the element directly.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```hbs
|
||||||
|
* {{!-- Pass current value via the element's `value` attribute, not selectOptions selected= --}}
|
||||||
|
* <l5r5e-combo-box name="weapon" value="{{data.weapon}}">
|
||||||
|
* {{selectOptions choices localize=true}}
|
||||||
|
* </l5r5e-combo-box>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Programmatic update — query the element by its name attribute, then set value directly.
|
||||||
|
* // The visible input label updates automatically to match the selected option's label.
|
||||||
|
* const el = document.querySelector("l5r5e-combo-box[name='weapon']");
|
||||||
|
* el.value = "axe"; // input shows "Battleaxe", el.value returns "axe"
|
||||||
|
*
|
||||||
|
* // Free-text entry (no matching option) — value and label are both set to the typed string:
|
||||||
|
* el.value = "naginata"; // input shows "naginata", el.value returns "naginata"
|
||||||
|
*/
|
||||||
|
export class L5R5eHtmlComboBoxElement extends DropdownMixin(
|
||||||
|
AbstractFormInputElement,
|
||||||
|
{ multiSelect: false, debounceMs: 150, clearOnClose: false }
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* The label currently shown in the text input. Differs from `_value` when a predefined
|
||||||
|
* option is selected (value = option's value attribute, label = option's display text).
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
_label = "";
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static tagName = "l5r5e-combo-box";
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static observedAttributes = ["disabled", "placeholder", "value"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flat descriptor list built once in _initialize(), mirrors the light-DOM options.
|
||||||
|
* @type {{ value: string, label: string, group: string|null }[]}
|
||||||
|
*/
|
||||||
|
#options = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value snapshot taken when the input receives focus.
|
||||||
|
* Used by the blur handler to decide whether to fire a change event.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
#valueAtFocus = "";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Accessors */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
get value() {
|
||||||
|
return this._value ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(value) {
|
||||||
|
const match = this.#options.find(option => option.value === String(value ?? ""));
|
||||||
|
if (match) {
|
||||||
|
this._value = match.value;
|
||||||
|
this._label = match.label;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._value = String(value ?? "");
|
||||||
|
this._label = this._value;
|
||||||
|
}
|
||||||
|
this._internals.setFormValue(this._value);
|
||||||
|
if (this._dropdownInput) {
|
||||||
|
this._dropdownInput.value = this._label;
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
|
||||||
|
this.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AbstractFormInputElement does not have an _initialize() hook, so we override
|
||||||
|
* connectedCallback to snapshot options from the light DOM before _buildElements()
|
||||||
|
* is called by super.connectedCallback().
|
||||||
|
*
|
||||||
|
* We keep this minimal: just build the flat #options list so _getDropdownOptions()
|
||||||
|
* and the value setter can look options up by value.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
connectedCallback() {
|
||||||
|
this.#snapshotOptions();
|
||||||
|
this.#resolveInitialValue();
|
||||||
|
super.connectedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshotOptions() {
|
||||||
|
const makeOption = (option, group = null) => ({
|
||||||
|
value: option.value,
|
||||||
|
label: option.innerText,
|
||||||
|
group,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.#options = [...this.children].flatMap(child => {
|
||||||
|
if (child instanceof HTMLOptGroupElement) {
|
||||||
|
return [...child.querySelectorAll("option")]
|
||||||
|
.filter(option => option.value)
|
||||||
|
.map(option => makeOption(option, child.label));
|
||||||
|
}
|
||||||
|
if (child instanceof HTMLOptionElement && child.value) {
|
||||||
|
return makeOption(child);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#resolveInitialValue() {
|
||||||
|
// Honour a `value` attribute if set, otherwise find a selected option.
|
||||||
|
const attrValue = this.getAttribute("value");
|
||||||
|
const initial = attrValue ?? this.#options.find(option => option.selected)?.value ?? "";
|
||||||
|
const match = this.#options.find(option => option.value === initial);
|
||||||
|
if (match) {
|
||||||
|
this._value = match.value;
|
||||||
|
this._label = match.label;
|
||||||
|
} else {
|
||||||
|
this._value = initial;
|
||||||
|
this._label = initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Element Lifecycle */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_buildElements() {
|
||||||
|
const wrapper = this._buildDropdownElements({
|
||||||
|
placeholder: this.getAttribute("placeholder") ?? "",
|
||||||
|
});
|
||||||
|
|
||||||
|
this._dropdownInput.value = this._label || this._value || "";
|
||||||
|
this._primaryInput = this._dropdownInput;
|
||||||
|
|
||||||
|
return [wrapper];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_activateListeners() {
|
||||||
|
const signal = this.abortSignal;
|
||||||
|
this._activateDropdownListeners();
|
||||||
|
|
||||||
|
// Prevent the inner <input>'s own native change from reaching Foundry's form handler.
|
||||||
|
// We dispatch our own change at commit time only (see below).
|
||||||
|
this._dropdownInput.addEventListener("change", (e) => e.stopPropagation(), { signal });
|
||||||
|
|
||||||
|
// Free typing: update value and fire `input` immediately.
|
||||||
|
// Do NOT fire `change` here — Foundry's form handler re-renders the sheet on
|
||||||
|
// every `change` event, which would destroy the element mid-typing.
|
||||||
|
// `change` is fired at commit time: on blur (if value changed) or Enter.
|
||||||
|
this._dropdownInput.addEventListener("input", () => {
|
||||||
|
const typed = this._dropdownInput.value;
|
||||||
|
this._label = typed;
|
||||||
|
this._value = typed;
|
||||||
|
this._internals.setFormValue(typed);
|
||||||
|
this.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
|
||||||
|
// DropdownMixin's #onInput fires the debounced dropdown re-render.
|
||||||
|
}, { signal });
|
||||||
|
|
||||||
|
// Commit on blur if the value has changed since the element was focused.
|
||||||
|
this._dropdownInput.addEventListener("focus", () => {
|
||||||
|
this.#valueAtFocus = this._value;
|
||||||
|
}, { signal });
|
||||||
|
|
||||||
|
this._dropdownInput.addEventListener("blur", () => {
|
||||||
|
if (this._value !== this.#valueAtFocus) {
|
||||||
|
this.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
|
||||||
|
}
|
||||||
|
}, { signal });
|
||||||
|
|
||||||
|
// Commit on Enter for free-text values (not picked from the dropdown).
|
||||||
|
this._dropdownInput.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
this._dropdownInput.blur();
|
||||||
|
}
|
||||||
|
}, { signal });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_toggleDisabled(disabled) {
|
||||||
|
this._toggleDropdownDisabled(disabled);
|
||||||
|
// Add/remove .disabled on the wrapper so CSS can dim the whole control.
|
||||||
|
const wrapper = this.querySelector(".wrapper");
|
||||||
|
if (wrapper) {
|
||||||
|
wrapper.classList.toggle("disabled", disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
attributeChangedCallback(attrName, oldValue, newValue) {
|
||||||
|
super.attributeChangedCallback(attrName, oldValue, newValue);
|
||||||
|
if (attrName === "disabled") {
|
||||||
|
this._toggleDropdownDisabled(newValue !== null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_refresh() {
|
||||||
|
if (!this._dropdownInput)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._dropdownInput.value = this._label ?? "";
|
||||||
|
this._internals.setFormValue(this._value ?? "");
|
||||||
|
this._dropdownRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* DropdownMixin Contract */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_getDropdownOptions() {
|
||||||
|
return this.#options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_isOptionSelected(value) {
|
||||||
|
return this._value === value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit the picked option as the current value and close the dropdown.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_onDropdownPick(option) {
|
||||||
|
this._value = option.value;
|
||||||
|
this._label = option.label;
|
||||||
|
this._dropdownInput.value = option.label;
|
||||||
|
this._internals.setFormValue(option.value);
|
||||||
|
this._dropdownInput.blur();
|
||||||
|
this.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
|
||||||
|
this.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
|
||||||
|
}
|
||||||
|
}
|
||||||
492
system/scripts/misc/l5r5e-dropdown-mixin.js
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {Object} DropdownMixinConfig
|
||||||
|
* @property {boolean} [multiSelect=false]
|
||||||
|
* When true the dropdown toggles selection and stays open after a pick.
|
||||||
|
* When false a pick commits the value and closes (combo-box mode).
|
||||||
|
* @property {number} [debounceMs=150]
|
||||||
|
* Trailing-debounce delay in milliseconds for dropdown re-renders while typing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DropdownMixin
|
||||||
|
*
|
||||||
|
* Adds a fully custom `<ul role="listbox">` dropdown to any AbstractFormInputElement
|
||||||
|
* subclass. Handles UI only — building, opening, closing, rendering options, and keyboard
|
||||||
|
* navigation. Has no opinion about how options are stored or how values are managed.
|
||||||
|
*
|
||||||
|
* ### Contract: three methods the host must implement
|
||||||
|
*
|
||||||
|
* - `_getDropdownOptions()`
|
||||||
|
* Return `{ value, label, group, disabled, tooltip }[]` — the full unfiltered list.
|
||||||
|
*
|
||||||
|
* - `_isOptionSelected(value)` → boolean
|
||||||
|
* Return whether a given value is currently selected.
|
||||||
|
*
|
||||||
|
* - `_onDropdownPick(option)` → void
|
||||||
|
* Called when the user picks an option. The mixin handles post-pick behaviour
|
||||||
|
* (close in single mode, re-render in multi mode) after this returns.
|
||||||
|
*
|
||||||
|
* ### Host responsibilities
|
||||||
|
* - Call `this._buildDropdownElements({ placeholder })` inside `_buildElements()`
|
||||||
|
* and include the returned wrapper in the returned array.
|
||||||
|
* - Call `this._activateDropdownListeners()` inside `_activateListeners()`.
|
||||||
|
* - Call `this._toggleDropdownDisabled(disabled)` inside `_toggleDisabled()`.
|
||||||
|
* - Call `this._dropdownRefresh()` inside `_refresh()` to keep checkmarks in sync.
|
||||||
|
*
|
||||||
|
* @param {typeof AbstractFormInputElement} Base
|
||||||
|
* @param {DropdownMixinConfig} [mixinConfig={}]
|
||||||
|
* @return {typeof Base}
|
||||||
|
*/
|
||||||
|
export function DropdownMixin(Base, mixinConfig = {}) {
|
||||||
|
const {
|
||||||
|
multiSelect = false,
|
||||||
|
debounceMs = 150,
|
||||||
|
/**
|
||||||
|
* When true, closing the dropdown clears the search input text.
|
||||||
|
* Use true for multi-select (search is transient) and false for
|
||||||
|
* combo-box (the input IS the value and must persist after close).
|
||||||
|
*/
|
||||||
|
clearOnClose = true,
|
||||||
|
} = mixinConfig;
|
||||||
|
|
||||||
|
return class DropdownMixinElement extends Base {
|
||||||
|
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
/* Private Fields */
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
|
||||||
|
/** @type {HTMLInputElement} */
|
||||||
|
#searchInput;
|
||||||
|
|
||||||
|
/** @type {HTMLUListElement} */
|
||||||
|
#list;
|
||||||
|
|
||||||
|
/** @type {boolean} */
|
||||||
|
#open = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snapshot of #open at the moment a focus event fires.
|
||||||
|
* Lets us distinguish a fresh focus (should open) from a click
|
||||||
|
* on an already-focused input (should toggle closed).
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
#wasOpenOnFocus = false;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
#activeIndex = -1;
|
||||||
|
|
||||||
|
/** @type {HTMLLIElement[]} — flat list of pickable <li>s, excludes group headers */
|
||||||
|
#optionElements = [];
|
||||||
|
|
||||||
|
/** @type {Function} */
|
||||||
|
#debouncedOpen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-instance key so multiple elements on the same page never share a timer.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
#debounceId = `l5r5e-dropdown-${foundry.utils.randomID()}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The search input element. The host should assign this to `this._primaryInput`.
|
||||||
|
* @return {HTMLInputElement}
|
||||||
|
*/
|
||||||
|
get _dropdownInput() {
|
||||||
|
return this.#searchInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the search `<input>` + `<ul>` dropdown inside a positioned wrapper div.
|
||||||
|
* Include the returned element in the array returned from `_buildElements()`.
|
||||||
|
*
|
||||||
|
* @param {object} [opts]
|
||||||
|
* @param {string} [opts.placeholder=""]
|
||||||
|
* @return {HTMLDivElement}
|
||||||
|
*/
|
||||||
|
_buildDropdownElements({ placeholder = "" } = {}) {
|
||||||
|
const wrapper = document.createElement("div");
|
||||||
|
wrapper.classList.add("wrapper");
|
||||||
|
|
||||||
|
this.#searchInput = document.createElement("input");
|
||||||
|
this.#searchInput.type = "text";
|
||||||
|
this.#searchInput.classList.add("input");
|
||||||
|
this.#searchInput.setAttribute("autocomplete", "off");
|
||||||
|
this.#searchInput.setAttribute("role", "combobox");
|
||||||
|
this.#searchInput.setAttribute("aria-autocomplete", "list");
|
||||||
|
this.#searchInput.setAttribute("aria-expanded", "false");
|
||||||
|
this.#searchInput.setAttribute("aria-haspopup", "listbox");
|
||||||
|
if (placeholder) {
|
||||||
|
this.#searchInput.setAttribute("placeholder", placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#list = document.createElement("ul");
|
||||||
|
this.#list.classList.add("dropdown");
|
||||||
|
this.#list.setAttribute("role", "listbox");
|
||||||
|
if (multiSelect) {
|
||||||
|
this.#list.setAttribute("aria-multiselectable", "true");
|
||||||
|
}
|
||||||
|
this.#list.hidden = true;
|
||||||
|
|
||||||
|
this.#syncOffset();
|
||||||
|
wrapper.append(this.#searchInput, this.#list);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Attach dropdown event listeners. Call inside `_activateListeners()`. */
|
||||||
|
_activateDropdownListeners() {
|
||||||
|
const signal = this.abortSignal;
|
||||||
|
|
||||||
|
this.#debouncedOpen = game.l5r5e.HelpersL5r5e.debounce(
|
||||||
|
this.#debounceId,
|
||||||
|
(query) => this.#openDropdown(query),
|
||||||
|
debounceMs,
|
||||||
|
false // trailing — fires after the user pauses
|
||||||
|
);
|
||||||
|
|
||||||
|
this.#searchInput.addEventListener("mousedown", this.#onMouseDown.bind(this), { signal });
|
||||||
|
this.#searchInput.addEventListener("focus", this.#onFocus.bind(this), { signal });
|
||||||
|
this.#searchInput.addEventListener("input", this.#onInput.bind(this), { signal });
|
||||||
|
this.#searchInput.addEventListener("keydown", this.#onKeydown.bind(this), { signal });
|
||||||
|
this.#searchInput.addEventListener("blur", this.#onBlur.bind(this), { signal });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable the search input. Call inside `_toggleDisabled()`.
|
||||||
|
* @param {boolean} disabled
|
||||||
|
*/
|
||||||
|
_toggleDropdownDisabled(disabled) {
|
||||||
|
if (this.#searchInput) {
|
||||||
|
this.#searchInput.disabled = disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
/* Refresh */
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-render the open dropdown in place so checkmarks and disabled states
|
||||||
|
* stay in sync with the current value. Call inside `_refresh()`.
|
||||||
|
*/
|
||||||
|
_dropdownRefresh() {
|
||||||
|
if (this.#open) {
|
||||||
|
this.#renderOptions(this.#filter(this.#searchInput?.value ?? ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract
|
||||||
|
* @return {{ value: string, label: string, group: string|null, disabled?: boolean, tooltip?: string }[]}
|
||||||
|
*/
|
||||||
|
_getDropdownOptions() {
|
||||||
|
throw new Error(`${this.constructor.name} must implement _getDropdownOptions()`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract
|
||||||
|
* @param {string} value
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
_isOptionSelected(value) {
|
||||||
|
throw new Error(`${this.constructor.name} must implement _isOptionSelected()`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract
|
||||||
|
* @param {{ value: string, label: string }} option
|
||||||
|
*/
|
||||||
|
_onDropdownPick(option) {
|
||||||
|
throw new Error(`${this.constructor.name} must implement _onDropdownPick()`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Case-insensitive label filter over `_getDropdownOptions()`.
|
||||||
|
* Returns all options when query is empty.
|
||||||
|
* @param {string} query
|
||||||
|
* @return {object[]}
|
||||||
|
*/
|
||||||
|
#filter(query) {
|
||||||
|
const all = this._getDropdownOptions();
|
||||||
|
if (!query) {
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
const lower = query.toLowerCase();
|
||||||
|
return all.filter(option => option.label.toLowerCase().includes(lower));
|
||||||
|
}
|
||||||
|
|
||||||
|
#openDropdown(query = "") {
|
||||||
|
this.#renderOptions(this.#filter(query));
|
||||||
|
this.#list.hidden = false;
|
||||||
|
this.#searchInput.setAttribute("aria-expanded", "true");
|
||||||
|
this.#open = true;
|
||||||
|
this.#activeIndex = -1;
|
||||||
|
this._onDropdownOpened();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the dropdown opens. Override in host classes to snapshot
|
||||||
|
* the current value so _onDropdownClosed can compare against it.
|
||||||
|
* Default is a no-op.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDropdownOpened() {}
|
||||||
|
|
||||||
|
#closeDropdown() {
|
||||||
|
this.#list.hidden = true;
|
||||||
|
this.#searchInput.setAttribute("aria-expanded", "false");
|
||||||
|
this.#open = false;
|
||||||
|
this.#wasOpenOnFocus = false;
|
||||||
|
this.#activeIndex = -1;
|
||||||
|
this.#optionElements = [];
|
||||||
|
// In combo-box mode the input IS the value, so we leave it intact.
|
||||||
|
// In multi-select mode the search text is transient and should reset.
|
||||||
|
if (clearOnClose && this.#searchInput) {
|
||||||
|
this.#searchInput.value = "";
|
||||||
|
}
|
||||||
|
// Notify the host that the dropdown has closed. Hosts can override this
|
||||||
|
// to fire a single change event after a multi-pick session.
|
||||||
|
this._onDropdownClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the dropdown closes. Override in host classes to fire a
|
||||||
|
* consolidated change event after a multi-pick session.
|
||||||
|
* Default is a no-op.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDropdownClosed() {}
|
||||||
|
|
||||||
|
#renderOptions(options) {
|
||||||
|
this.#list.innerHTML = "";
|
||||||
|
this.#optionElements = [];
|
||||||
|
|
||||||
|
// hideDisabledOptions is a host-level attribute, read via the DOM.
|
||||||
|
const visible = this.hasAttribute("hidedisabledoptions")
|
||||||
|
? options.filter(o => !o.disabled)
|
||||||
|
: options;
|
||||||
|
|
||||||
|
if (!visible.length) {
|
||||||
|
const empty = document.createElement("li");
|
||||||
|
empty.classList.add("no-results");
|
||||||
|
empty.textContent = game.i18n.localize("l5r5e.multiselect.no_results") ?? "No matches";
|
||||||
|
empty.setAttribute("aria-disabled", "true");
|
||||||
|
this.#list.append(empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bucket into groups, preserving insertion order.
|
||||||
|
const groups = new Map();
|
||||||
|
const ungrouped = [];
|
||||||
|
for (const option of visible) {
|
||||||
|
if (option.group) {
|
||||||
|
if (!groups.has(option.group)) groups.set(option.group, []);
|
||||||
|
groups.get(option.group).push(option);
|
||||||
|
} else {
|
||||||
|
ungrouped.push(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [label, options] of groups) {
|
||||||
|
const header = document.createElement("li");
|
||||||
|
header.classList.add("group");
|
||||||
|
header.setAttribute("role", "presentation");
|
||||||
|
header.textContent = label;
|
||||||
|
this.#list.append(header);
|
||||||
|
for (const option of options) {
|
||||||
|
this.#list.append(this.#buildOptionEl(option));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const option of ungrouped){
|
||||||
|
this.#list.append(this.#buildOptionEl(option));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#buildOptionEl(option) {
|
||||||
|
const selected = this._isOptionSelected(option.value);
|
||||||
|
const disabled = !!option.disabled;
|
||||||
|
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.classList.add("option");
|
||||||
|
if (selected) {
|
||||||
|
li.classList.add("selected");
|
||||||
|
}
|
||||||
|
if (disabled) {
|
||||||
|
li.classList.add("disabled");
|
||||||
|
}
|
||||||
|
li.setAttribute("role", "option");
|
||||||
|
li.setAttribute("aria-selected", String(selected));
|
||||||
|
li.setAttribute("aria-disabled", String(disabled));
|
||||||
|
li.dataset.value = option.value;
|
||||||
|
|
||||||
|
if (multiSelect) {
|
||||||
|
const check = document.createElement("span");
|
||||||
|
check.classList.add("checkmark");
|
||||||
|
check.setAttribute("aria-hidden", "true");
|
||||||
|
li.append(check);
|
||||||
|
}
|
||||||
|
|
||||||
|
const labelEl = document.createElement("span");
|
||||||
|
labelEl.classList.add("label");
|
||||||
|
labelEl.textContent = option.label;
|
||||||
|
li.append(labelEl);
|
||||||
|
|
||||||
|
if (selected && multiSelect) {
|
||||||
|
li.title = game.i18n.localize("l5r5e.multiselect.already_in_filter");
|
||||||
|
} else if (disabled && option.tooltip) {
|
||||||
|
li.title = option.tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!disabled) {
|
||||||
|
li.addEventListener("mouseenter", () => {
|
||||||
|
for (const element of this.#optionElements) {
|
||||||
|
element.classList.remove("active");
|
||||||
|
}
|
||||||
|
this.#activeIndex = this.#optionElements.indexOf(li);
|
||||||
|
li.classList.add("active");
|
||||||
|
});
|
||||||
|
li.addEventListener("mouseleave", () => {
|
||||||
|
li.classList.remove("active");
|
||||||
|
this.#activeIndex = -1;
|
||||||
|
});
|
||||||
|
li.addEventListener("mousedown", (event) => {
|
||||||
|
event.preventDefault(); // keep focus on the search input
|
||||||
|
this.#pick(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#optionElements.push(li);
|
||||||
|
return li;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pick(option) {
|
||||||
|
this._onDropdownPick(option);
|
||||||
|
if (multiSelect) {
|
||||||
|
// Stay open — re-render so checkmarks reflect the new state.
|
||||||
|
this.#renderOptions(this.#filter(this.#searchInput.value));
|
||||||
|
} else {
|
||||||
|
this.#closeDropdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#moveHighlight(direction) {
|
||||||
|
if (!this.#optionElements.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const prev = this.#optionElements[this.#activeIndex];
|
||||||
|
if (prev) {
|
||||||
|
prev.classList.remove("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#activeIndex = Math.max(
|
||||||
|
-1,
|
||||||
|
Math.min(this.#optionElements.length - 1, this.#activeIndex + direction)
|
||||||
|
);
|
||||||
|
|
||||||
|
const next = this.#optionElements[this.#activeIndex];
|
||||||
|
if (next) {
|
||||||
|
next.classList.add("active");
|
||||||
|
next.scrollIntoView({ block: "nearest" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#syncOffset() {
|
||||||
|
if (!this.#searchInput || !this.#list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const offset = this.#searchInput.offsetLeft;
|
||||||
|
this.#list.style.left = `${offset}px`;
|
||||||
|
this.#list.style.right = `-${offset}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles click-to-toggle behaviour.
|
||||||
|
*
|
||||||
|
* Three cases:
|
||||||
|
* 1. Dropdown is open → close it. preventDefault stops blur firing, which
|
||||||
|
* would otherwise trigger a second close via #onBlur.
|
||||||
|
* 2. Dropdown is closed AND input is already focused → open immediately.
|
||||||
|
* In this case focus will NOT fire again (input never blurred after the
|
||||||
|
* previous preventDefault), so we must open here rather than in #onFocus.
|
||||||
|
* 3. Dropdown is closed AND input is not yet focused → do nothing; the
|
||||||
|
* browser will fire focus naturally and #onFocus will open the dropdown.
|
||||||
|
*/
|
||||||
|
#onMouseDown(event) {
|
||||||
|
this.#wasOpenOnFocus = this.#open;
|
||||||
|
if (this.#open) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.#closeDropdown();
|
||||||
|
} else if (document.activeElement === this.#searchInput) {
|
||||||
|
// Case 2: already focused, focus won't re-fire — open directly.
|
||||||
|
this.#openDropdown(this.#searchInput.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onFocus(event) {
|
||||||
|
// Only open if mousedown didn't already handle it (cases 1 & 2 above).
|
||||||
|
if (!this.#wasOpenOnFocus && document.activeElement === this.#searchInput) {
|
||||||
|
this.#openDropdown(this.#searchInput.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onInput(event) {
|
||||||
|
this.#debouncedOpen(this.#searchInput.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the dropdown when focus genuinely leaves the component.
|
||||||
|
*
|
||||||
|
* event.relatedTarget is the element that is RECEIVING focus. If it is
|
||||||
|
* inside our host element (e.g. the clear button, a chip remove span that
|
||||||
|
* managed to steal focus) we leave the dropdown open. Only when focus moves
|
||||||
|
* completely outside do we close — and we do so synchronously, so that
|
||||||
|
* _onDropdownClosed fires its change event BEFORE the browser hands control
|
||||||
|
* to whatever the user clicked. This eliminates the 100 ms race where Foundry
|
||||||
|
* could read stale FormData between the blur and a deferred close.
|
||||||
|
*/
|
||||||
|
#onBlur(event) {
|
||||||
|
if (this.contains(event.relatedTarget)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#closeDropdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
#onKeydown(event) {
|
||||||
|
if (!this.#open) {
|
||||||
|
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
||||||
|
event.preventDefault();
|
||||||
|
this.#openDropdown(this.#searchInput.value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (event.key) {
|
||||||
|
case "ArrowDown": {
|
||||||
|
event.preventDefault();
|
||||||
|
this.#moveHighlight(1);
|
||||||
|
} break;
|
||||||
|
case "ArrowUp": {
|
||||||
|
event.preventDefault();
|
||||||
|
this.#moveHighlight(-1);
|
||||||
|
}break;
|
||||||
|
case "Enter": {
|
||||||
|
event.preventDefault();
|
||||||
|
if (this.#activeIndex >= 0) {
|
||||||
|
const li = this.#optionElements[this.#activeIndex];
|
||||||
|
const option = this._getDropdownOptions().find(option => option.value === li.dataset.value);
|
||||||
|
if (option) {
|
||||||
|
this.#pick(option);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.#closeDropdown();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case "Escape": {
|
||||||
|
event.preventDefault();
|
||||||
|
this.#closeDropdown();
|
||||||
|
} break;
|
||||||
|
case "Tab": {
|
||||||
|
this.#closeDropdown();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,301 +1,424 @@
|
|||||||
|
import { DropdownMixin } from "./l5r5e-dropdown-mixin.js";
|
||||||
|
|
||||||
const { AbstractMultiSelectElement } = foundry.applications.elements;
|
const { AbstractMultiSelectElement } = foundry.applications.elements;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a multi-select workflow using a select element as the input mechanism.
|
* A custom `<l5r5e-multi-select>` form element providing Select2-style chip multi-selection.
|
||||||
* It is a expanded copy of the HTMLMultiselect with support for disabling options
|
|
||||||
* and a clear all button. Also have support for hover-over information using titlea
|
|
||||||
*
|
*
|
||||||
* @example Multi-Select HTML Markup
|
* Stores **multiple string values** from a fixed option list, shown as removable chips inside
|
||||||
* ```html
|
* the input box. A live-search input filters the dropdown as the user types. Use this when a
|
||||||
* <l5r5e-multi-select name="select-many-things">
|
* field holds an unordered collection of values (e.g. a set of skills, tags, or abilities).
|
||||||
* <optgroup label="Basic Options">
|
* For storing a single value — predefined or free-text — use {@link L5R5eHtmlComboBoxElement} instead.
|
||||||
* <option value="foo">Foo</option>
|
*
|
||||||
* <option value="bar">Bar</option>
|
* The element's `value` getter returns a comma-separated string (e.g. `"fire,water"`),
|
||||||
* <option value="baz">Baz</option>
|
* which is what `FormData` will read on submission. `_getValue()` returns a plain Array,
|
||||||
* </optgroup>
|
* which is what `FormDataExtended` will use.
|
||||||
* <optgroup label="Advanced Options">
|
*
|
||||||
* <option value="fizz">Fizz</option>
|
* Pre-selection on render is handled via the `value` attribute on the element — NOT via
|
||||||
* <option value="buzz">Buzz</option>
|
* `{{selectOptions selected=...}}`, which cannot handle comma-separated strings. Use
|
||||||
* </optgroup>
|
* `{{selectOptions}}` without `selected` purely to render the available options, and let
|
||||||
|
* the `value` attribute drive pre-selection. Since `getAttribute()` always returns a string,
|
||||||
|
* passing a `Set` or `Array` via Handlebars will not work correctly — always pass a
|
||||||
|
* comma-separated string to `value=`.
|
||||||
|
*
|
||||||
|
* Prefer {@link L5r5eSetField} + `{{formGroup}}` when wiring this into a DataModel — the
|
||||||
|
* field handles the full round-trip automatically.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```hbs
|
||||||
|
* {{!-- Use value= (comma-separated string) for pre-selection, not selectOptions selected= --}}
|
||||||
|
* <l5r5e-multi-select name="elements" value="{{data.elements}}">
|
||||||
|
* {{selectOptions choices localize=true}}
|
||||||
* </l5r5e-multi-select>
|
* </l5r5e-multi-select>
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Static factory — use only when building outside of Foundry's field/template system:
|
||||||
|
* const el = L5r5eHtmlMultiSelectElement.create({
|
||||||
|
* name: "elements",
|
||||||
|
* options: [{ value: "fire", label: "Fire" }, { value: "water", label: "Water" }],
|
||||||
|
* value: "fire,water", // comma-separated pre-selection
|
||||||
|
* });
|
||||||
|
* form.appendChild(el);
|
||||||
|
*
|
||||||
|
* // Reading the value back:
|
||||||
|
* el.value; // "fire,water" — comma-separated string, compatible with FormData
|
||||||
|
* el._getValue(); // ["fire","water"] — array, compatible with FormDataExtended
|
||||||
*/
|
*/
|
||||||
export class L5r5eHtmlMultiSelectElement extends AbstractMultiSelectElement {
|
export class L5r5eHtmlMultiSelectElement extends DropdownMixin(
|
||||||
|
AbstractMultiSelectElement,
|
||||||
constructor() {
|
{ multiSelect: true, debounceMs: 150 }
|
||||||
super();
|
) {
|
||||||
this.#setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static tagName = "l5r5e-multi-select";
|
static tagName = "l5r5e-multi-select";
|
||||||
|
|
||||||
/**
|
/** @type {HTMLDivElement} — outer box containing chips, input, clear button */
|
||||||
* A select element used to choose options.
|
#selectionBox;
|
||||||
* @type {HTMLSelectElement}
|
|
||||||
*/
|
/** @type {HTMLDivElement} — chips are injected here */
|
||||||
#select;
|
#chipList;
|
||||||
|
|
||||||
|
/** @type {HTMLSpanElement} — auto-sizing wrapper around the search input */
|
||||||
|
#inputSizer;
|
||||||
|
|
||||||
|
/** @type {HTMLButtonElement} — trailing clear-all button */
|
||||||
|
#clearButton;
|
||||||
|
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
#disabledValues = new Set();
|
||||||
|
|
||||||
|
/** @type {Map<string, string>} */
|
||||||
|
#tooltips = new Map();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A display element which lists the chosen options.
|
* Returns a comma-separated string
|
||||||
* @type {HTMLDivElement}
|
* FormData reads this via field.value.
|
||||||
|
* @override
|
||||||
*/
|
*/
|
||||||
#tags;
|
get value() {
|
||||||
|
return Array.from(this._value).join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
set value(val) {
|
||||||
|
this._value.clear();
|
||||||
|
const values = Array.isArray(val) ? val : String(val).split(",").filter(Boolean);
|
||||||
|
for (const v of values) {
|
||||||
|
this._value.add(v);
|
||||||
|
}
|
||||||
|
this._internals.setFormValue(this.value);
|
||||||
|
this._refresh();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A button element which clear all the options.
|
* Return an array so FormDataExtended.object[name] matches Foundry's own
|
||||||
* @type {HTMLButtonElement}
|
* HTMLMultiSelectElement — both field.value (string) and .object (array) are correct.
|
||||||
|
* @override
|
||||||
|
* @protected
|
||||||
*/
|
*/
|
||||||
#clearAll;
|
_getValue() {
|
||||||
|
return Array.from(this._value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Set containing the values that should always be disabled.
|
* Accept either an array or comma-separated string when Foundry calls _setValue().
|
||||||
* @type {Set}
|
* @override
|
||||||
|
* @protected
|
||||||
*/
|
*/
|
||||||
#disabledValues;
|
_setValue(val) {
|
||||||
|
const values = Array.isArray(val) ? val : String(val).split(",").filter(Boolean);
|
||||||
|
if (values.some(v => v && !(v in this._choices))) {
|
||||||
|
throw new Error("The values assigned to a multi-select element must all be valid options.");
|
||||||
|
}
|
||||||
|
this._value.clear();
|
||||||
|
for (const v of values) {
|
||||||
|
this._value.add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_initialize() {
|
||||||
|
super._initialize(); // fills this._choices, this._value, this._options
|
||||||
|
for (const option of this.querySelectorAll("option")) {
|
||||||
|
if (option.disabled)
|
||||||
|
this.#disabledValues.add(option.value);
|
||||||
|
if (option.title)
|
||||||
|
this.#tooltips.set(option.value, option.title);
|
||||||
|
}
|
||||||
|
if (this.hasAttribute("value")) {
|
||||||
|
this._setValue(this.getAttribute("value"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
/* Element Lifecycle */
|
||||||
// We will call initialize twice (one in the parent constructor) then one in #setup
|
/* -------------------------------------------- */
|
||||||
// required since when we want to build the elements we should to an initialize first
|
|
||||||
// and we cannot override _initialize since we don't have access to #disabledValues there
|
|
||||||
#setup() {
|
|
||||||
super._initialize();
|
|
||||||
this.#disabledValues = new Set();
|
|
||||||
for (const option of this.querySelectorAll("option")) {
|
|
||||||
if (option.value === "") {
|
|
||||||
option.label = game.i18n.localize("l5r5e.multiselect.empty_tag");
|
|
||||||
this._choices[option.value] = game.i18n.localize("l5r5e.multiselect.empty_tag");
|
|
||||||
}
|
|
||||||
if (option.disabled) {
|
|
||||||
this.#disabledValues.add(option.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
_buildElements() {
|
_buildElements() {
|
||||||
this.#setup();
|
// Ask mixin to build <input> + <ul>, then re-home them into our structure.
|
||||||
|
const mixinWrapper = this._buildDropdownElements({
|
||||||
// Create select element
|
placeholder: this.getAttribute("placeholder")
|
||||||
this.#select = this._primaryInput = document.createElement("select");
|
?? game.i18n.localize("l5r5e.multiselect.placeholder"),
|
||||||
this.#select.insertAdjacentHTML("afterbegin", `<option id="l5r5e-multiselect-placeholder" value="" disabled selected hidden>${game.i18n.localize("l5r5e.multiselect.placeholder")}</option>`);
|
|
||||||
this.#select.append(...this._options);
|
|
||||||
this.#select.disabled = !this.editable;
|
|
||||||
|
|
||||||
// Create a div element for display
|
|
||||||
this.#tags = document.createElement("div");
|
|
||||||
this.#tags.className = "tags input-element-tags";
|
|
||||||
|
|
||||||
// Create a clear all button
|
|
||||||
this.#clearAll = document.createElement("button");
|
|
||||||
this.#clearAll.textContent = "X";
|
|
||||||
return [this.#select, this.#clearAll, this.#tags];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
_refresh() {
|
|
||||||
// Update the displayed tags
|
|
||||||
const tags = Array.from(this._value).map(id => {
|
|
||||||
return foundry.applications.elements.HTMLStringTagsElement.renderTag(id, this._choices[id], this.editable);
|
|
||||||
});
|
});
|
||||||
this.#tags.replaceChildren(...tags);
|
const searchInput = mixinWrapper.querySelector("input.input");
|
||||||
|
const dropdownList = mixinWrapper.querySelector("ul.dropdown");
|
||||||
|
|
||||||
// Figure out if we are overflowing the tag div.
|
// Selection box
|
||||||
if($(this.#tags).css("max-height")) {
|
this.#selectionBox = document.createElement("div");
|
||||||
const numericMaxHeight = parseInt($(this.#tags).css("max-height"), 10);
|
this.#selectionBox.classList.add("selection-box");
|
||||||
if(numericMaxHeight) {
|
|
||||||
if($(this.#tags).prop("scrollHeight") > numericMaxHeight) {
|
|
||||||
this.#tags.classList.add("overflowing");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.#tags.classList.remove("overflowing");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable selected options
|
// Chip list
|
||||||
const hideDisabled = game.settings.get(CONFIG.l5r5e.namespace, "compendium-hide-disabled-sources");
|
this.#chipList = document.createElement("div");
|
||||||
for (const option of this.#select) {
|
this.#chipList.classList.add("chip-list");
|
||||||
if (this._value.has(option.value)) {
|
|
||||||
option.disabled = true;
|
|
||||||
option.title = game.i18n.localize("l5r5e.multiselect.already_in_filter");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (this.#disabledValues.has(option.value)) {
|
|
||||||
option.disabled = true;
|
|
||||||
option.hidden = hideDisabled;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
option.disabled = false;
|
|
||||||
option.removeAttribute("title");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
// Auto-sizing sizer — CSS grid trick: ::after mirrors data-value, input shares the cell
|
||||||
|
this.#inputSizer = document.createElement("span");
|
||||||
|
this.#inputSizer.classList.add("input-sizer");
|
||||||
|
this.#inputSizer.dataset.value = "";
|
||||||
|
this.#inputSizer.append(searchInput);
|
||||||
|
|
||||||
|
// Clear-all button
|
||||||
|
this.#clearButton = document.createElement("button");
|
||||||
|
this.#clearButton.type = "button";
|
||||||
|
this.#clearButton.classList.add("clear-btn");
|
||||||
|
this.#clearButton.setAttribute("aria-label",
|
||||||
|
game.i18n.localize("l5r5e.multiselect.clear_all") ?? "Clear all");
|
||||||
|
this.#clearButton.textContent = "×";
|
||||||
|
this.#clearButton.hidden = true;
|
||||||
|
|
||||||
|
this.#selectionBox.append(this.#chipList, this.#inputSizer, this.#clearButton);
|
||||||
|
|
||||||
|
// Container: selection box + dropdown must share the same positioned ancestor.
|
||||||
|
const container = document.createElement("div");
|
||||||
|
container.classList.add("multi-select-container");
|
||||||
|
container.append(this.#selectionBox, dropdownList);
|
||||||
|
|
||||||
|
this._primaryInput = searchInput;
|
||||||
|
return [container];
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
_activateListeners() {
|
_activateListeners() {
|
||||||
this.#select.addEventListener("change", this.#onChangeSelect.bind(this));
|
this._activateDropdownListeners();
|
||||||
this.#clearAll.addEventListener("click", this.#onClickClearAll.bind(this));
|
const signal = this.abortSignal;
|
||||||
this.#tags.addEventListener("click", this.#onClickTag.bind(this));
|
|
||||||
|
|
||||||
this.#tags.addEventListener("mouseleave", this.#onMouseLeave.bind(this));
|
this.#selectionBox.addEventListener("mousedown", this.#onBoxMouseDown.bind(this), { signal });
|
||||||
|
this.#chipList.addEventListener("click", this.#onChipClick.bind(this), { signal });
|
||||||
|
this.#clearButton.addEventListener("click", this.#onClearAll.bind(this), { signal });
|
||||||
|
// stop the clear button from opening the selection box when pressing it
|
||||||
|
this.#clearButton.addEventListener("mousedown", (event) => {event.preventDefault(); event.stopPropagation();}, {signal});
|
||||||
|
this._dropdownInput.addEventListener("input", () => this.#updateInputSizer(), { signal });
|
||||||
}
|
}
|
||||||
|
|
||||||
#onMouseLeave(event) {
|
|
||||||
// Figure out if we are overflowing the tag div.
|
|
||||||
if($(this.#tags).css("max-height")) {
|
|
||||||
const numericMaxHeight = parseInt($(this.#tags).css("max-height"), 10);
|
|
||||||
|
|
||||||
if($(this.#tags).prop("scrollHeight") > numericMaxHeight) {
|
|
||||||
this.#tags.classList.add("overflowing");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.#tags.classList.remove("overflowing");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle changes to the Select input, marking the selected option as a chosen value.
|
|
||||||
* @param {Event} event The change event on the select element
|
|
||||||
*/
|
|
||||||
#onChangeSelect(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
const select = event.currentTarget;
|
|
||||||
if (select.valueIndex === 0)
|
|
||||||
return; // Ignore placeholder
|
|
||||||
this.select(select.value);
|
|
||||||
select.value = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle click events on a tagged value, removing it from the chosen set.
|
|
||||||
* @param {PointerEvent} event The originating click event on a chosen tag
|
|
||||||
*/
|
|
||||||
#onClickTag(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
if (!event.target.classList.contains("remove"))
|
|
||||||
return;
|
|
||||||
if (!this.editable)
|
|
||||||
return;
|
|
||||||
const tag = event.target.closest(".tag");
|
|
||||||
this.unselect(tag.dataset.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle clickling the clear all button
|
|
||||||
* @param {Event} event The originating click event on the clear all button
|
|
||||||
*/
|
|
||||||
#onClickClearAll(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
var _this = this;
|
|
||||||
$(this.#tags).children().each(function () {
|
|
||||||
_this.unselect($(this).data("key"));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/** @override */
|
/** @override */
|
||||||
_toggleDisabled(disabled) {
|
_toggleDisabled(disabled) {
|
||||||
this.#select.toggleAttribute("disabled", disabled);
|
this._toggleDropdownDisabled(disabled);
|
||||||
|
|
||||||
|
if (this.#selectionBox) {
|
||||||
|
this.#selectionBox.classList.toggle("disabled", disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#chipList) {
|
||||||
|
this._refresh(); // re-render chips so × appears/disappears
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_refresh() {
|
||||||
|
const values = Array.from(this._value);
|
||||||
|
this._internals.setFormValue(values.length ? values.join(",") : "");
|
||||||
|
|
||||||
|
this.#renderChips(values);
|
||||||
|
|
||||||
|
// Clear button: only visible when editable and something is selected.
|
||||||
|
if (this.#clearButton) {
|
||||||
|
this.#clearButton.hidden = (!this.editable || values.length === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#updateInputSizer();
|
||||||
|
this._dropdownRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {string[]} values */
|
||||||
|
#renderChips(values) {
|
||||||
|
if (!this.#chipList){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.#chipList.replaceChildren(...values.map(id => this.#buildChip(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {string} id */
|
||||||
|
#buildChip(id) {
|
||||||
|
const chip = document.createElement("span");
|
||||||
|
chip.classList.add("chip");
|
||||||
|
chip.dataset.key = id;
|
||||||
|
|
||||||
|
const label = document.createElement("span");
|
||||||
|
label.classList.add("chip-label");
|
||||||
|
label.textContent = this._choices[id] ?? id;
|
||||||
|
chip.append(label);
|
||||||
|
|
||||||
|
// Only add × when the element is editable (not disabled, not readonly).
|
||||||
|
if (this.editable) {
|
||||||
|
const remove = document.createElement("span");
|
||||||
|
remove.classList.add("chip-remove");
|
||||||
|
remove.setAttribute("aria-label", `Remove ${this._choices[id] ?? id}`);
|
||||||
|
remove.setAttribute("aria-hidden", "true");
|
||||||
|
remove.textContent = "×";
|
||||||
|
chip.append(remove);
|
||||||
|
}
|
||||||
|
return chip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Mirror typed text into the sizer span so CSS sizes the input correctly. */
|
||||||
|
#updateInputSizer() {
|
||||||
|
if (!this.#inputSizer || !this._dropdownInput)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const input = this._dropdownInput;
|
||||||
|
const text = input.value || input.placeholder || "";
|
||||||
|
this.#inputSizer.dataset.value = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_getDropdownOptions() {
|
||||||
|
const makeOption = (option, group = null) => ({
|
||||||
|
value: option.value,
|
||||||
|
label: this._choices[option.value] ?? option.innerText,
|
||||||
|
group,
|
||||||
|
disabled: this.#disabledValues.has(option.value),
|
||||||
|
tooltip: this.#tooltips.get(option.value) ?? "",
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._options.flatMap(child => {
|
||||||
|
if (child instanceof HTMLOptGroupElement) {
|
||||||
|
return [...child.querySelectorAll("option")]
|
||||||
|
.filter(option => option.value)
|
||||||
|
.map(option => makeOption(option, child.label));
|
||||||
|
}
|
||||||
|
if (child instanceof HTMLOptionElement && child.value) {
|
||||||
|
return makeOption(child);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_isOptionSelected(value) {
|
||||||
|
return this._value.has(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_onDropdownPick(option) {
|
||||||
|
const inValue = this._value.has(option.value);
|
||||||
|
const inChoices = option.value in this._choices;
|
||||||
|
if(!(inValue || inChoices))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (inValue) {
|
||||||
|
this._value.delete(option.value);
|
||||||
|
}
|
||||||
|
else if(inChoices) {
|
||||||
|
this._value.add(option.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._internals.setFormValue(this.value);
|
||||||
|
this._refresh();
|
||||||
|
this.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#onBoxMouseDown(event) {
|
||||||
|
// Fully block interaction when not editable.
|
||||||
|
if (!this.editable) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.target.classList.contains("chip-remove"))
|
||||||
|
return;
|
||||||
|
if (event.target === this._dropdownInput)
|
||||||
|
return;
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
this._dropdownInput?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
#onChipClick(event) {
|
||||||
|
if (!event.target.classList.contains("chip-remove") || !this.editable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const chip = event.target.closest(".chip");
|
||||||
|
if (!chip)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._value.delete(chip.dataset.key);
|
||||||
|
this._internals.setFormValue(this.value);
|
||||||
|
this._refresh();
|
||||||
|
this.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
|
||||||
|
this._dropdownInput?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
#onClearAll(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.editable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._value.clear();
|
||||||
|
this._internals.setFormValue("");
|
||||||
|
this._refresh();
|
||||||
|
this.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Static Factory */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a HTML_l5r5e_MultiSelectElement using provided configuration data.
|
|
||||||
* @param {FormInputConfig<string[]> & Omit<SelectInputConfig, "blank">} config
|
|
||||||
* @returns {L5r5eHtmlMultiSelectElement}
|
|
||||||
*/
|
|
||||||
static create(config) {
|
static create(config) {
|
||||||
// Foundry creates either a select with tag multi-select or multi-checkboxes. We want a l5r5e-multi-select
|
|
||||||
// Copied the implementation from foundry.applications.fields.createMultiSelectInput with our required changes.
|
|
||||||
const groups = prepareSelectOptionGroups(config);
|
const groups = prepareSelectOptionGroups(config);
|
||||||
|
const element = document.createElement(L5r5eHtmlMultiSelectElement.tagName);
|
||||||
|
element.name = config.name;
|
||||||
|
foundry.applications.fields.setInputAttributes(element, config);
|
||||||
|
if (config.hideDisabledOptions) {
|
||||||
|
element.toggleAttribute("hidedisabledoptions", true);
|
||||||
|
}
|
||||||
|
|
||||||
//Setup the HTML
|
for (const groupEntry of groups) {
|
||||||
const select = document.createElement(L5r5eHtmlMultiSelectElement.tagName);
|
let parent = element;
|
||||||
select.name = config.name;
|
if (groupEntry.group) {
|
||||||
foundry.applications.fields.setInputAttributes(select, config);
|
parent = _appendOptgroup(groupEntry.group, element);
|
||||||
for (const group_entry of groups) {
|
|
||||||
let parent = select;
|
|
||||||
if (group_entry.group) {
|
|
||||||
parent = _appendOptgroupHtml(group_entry.group, select);
|
|
||||||
}
|
}
|
||||||
for (const option_entry of group_entry.options) {
|
for (const groupOption of groupEntry.options){
|
||||||
_appendOptionHtml(option_entry, parent);
|
_appendOption(groupOption, parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return select;
|
return element;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stolen from foundry.applications.fields.prepareSelectOptionGroups: Needed to add support for tooltips
|
/* -------------------------------------------- */
|
||||||
*
|
/* Module Helpers */
|
||||||
*/
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
function prepareSelectOptionGroups(config) {
|
function prepareSelectOptionGroups(config) {
|
||||||
const result = foundry.applications.fields.prepareSelectOptionGroups(config);
|
const result = foundry.applications.fields.prepareSelectOptionGroups(config);
|
||||||
|
config.options.filter(option => option?.disabled || option?.tooltip).forEach(special => {
|
||||||
// Disable options based on input
|
result.forEach(group => {
|
||||||
config.options.filter((option) => option?.disabled || option?.tooltip).forEach((SpecialOption) => {
|
group.options.forEach(groupOption => {
|
||||||
result.forEach((group) => {
|
if (groupOption.value === special.value) {
|
||||||
group.options.forEach((option) => {
|
groupOption.disabled = special.disabled;
|
||||||
if (SpecialOption.value === option.value) {
|
groupOption.tooltip = special.tooltip;
|
||||||
option.disabled = SpecialOption.disabled;
|
|
||||||
option.tooltip = SpecialOption?.tooltip;
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stolen from foundry.applications.fields
|
function _appendOptgroup(label, parent) {
|
||||||
* Create and append an optgroup element to a parent select.
|
const element = document.createElement("optgroup");
|
||||||
* @param {string} label
|
element.label = label;
|
||||||
* @param {HTMLSelectElement} parent
|
parent.appendChild(element);
|
||||||
* @returns {HTMLOptGroupElement}
|
|
||||||
* @internal
|
return element;
|
||||||
*/
|
|
||||||
function _appendOptgroupHtml(label, parent) {
|
|
||||||
const optgroup = document.createElement("optgroup");
|
|
||||||
optgroup.label = label;
|
|
||||||
parent.appendChild(optgroup);
|
|
||||||
return optgroup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stolen from foundry.applications.fields
|
function _appendOption(option, parent) {
|
||||||
* Create and append an option element to a parent select or optgroup.
|
|
||||||
* @param {FormSelectOption} option
|
|
||||||
* @param {HTMLSelectElement|HTMLOptGroupElement} parent
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
function _appendOptionHtml(option, parent) {
|
|
||||||
const { value, label, selected, disabled, rule, tooltip } = option;
|
const { value, label, selected, disabled, rule, tooltip } = option;
|
||||||
if ((value !== undefined) && (label !== undefined)) {
|
if (value !== undefined && label !== undefined) {
|
||||||
const option_html = document.createElement("option");
|
const element = document.createElement("option");
|
||||||
option_html.value = value;
|
element.value = value;
|
||||||
option_html.innerText = label;
|
element.innerText = label;
|
||||||
if (selected) {
|
if (selected) {
|
||||||
option_html.toggleAttribute("selected", true);
|
element.toggleAttribute("selected", true);
|
||||||
}
|
}
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
option_html.toggleAttribute("disabled", true);
|
element.toggleAttribute("disabled", true);
|
||||||
}
|
}
|
||||||
if (tooltip) {
|
if (tooltip) {
|
||||||
option_html.setAttribute("title", tooltip);
|
element.setAttribute("title", tooltip);
|
||||||
}
|
}
|
||||||
parent.appendChild(option_html);
|
parent.appendChild(element);
|
||||||
}
|
}
|
||||||
if (rule) {
|
if (rule) {
|
||||||
parent.insertAdjacentHTML("beforeend", "<hr>");
|
parent.insertAdjacentHTML("beforeend", "<hr>");
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ export class L5r5ePopupManager {
|
|||||||
/** @type {HTMLElement} */
|
/** @type {HTMLElement} */
|
||||||
#container = null;
|
#container = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment number to ignore old tooltips if template is too long to load (#62)
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
#displayId = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string|jQuery} selector - Selector or jQuery object for tooltip-bound elements.
|
* @param {string|jQuery} selector - Selector or jQuery object for tooltip-bound elements.
|
||||||
* @param {(event: MouseEvent) => Promise<string>} callback - Async function returning tooltip HTML content.
|
* @param {(event: MouseEvent) => Promise<string>} callback - Async function returning tooltip HTML content.
|
||||||
@@ -45,10 +51,24 @@ export class L5r5ePopupManager {
|
|||||||
.on("mouseenter.popup", async (event) => {
|
.on("mouseenter.popup", async (event) => {
|
||||||
$(this.#container).find("#l5r5e-tooltip-ct").remove();
|
$(this.#container).find("#l5r5e-tooltip-ct").remove();
|
||||||
|
|
||||||
|
// Memory save
|
||||||
|
if (this.#displayId >= 200) {
|
||||||
|
this.#displayId = 0;
|
||||||
|
}
|
||||||
|
const currentDisplayId = ++this.#displayId;
|
||||||
|
|
||||||
|
// Load the template, can take a while
|
||||||
const tpl = await this.#callback(event);
|
const tpl = await this.#callback(event);
|
||||||
|
|
||||||
// Abort if no content or the target element is no longer in the DOM
|
// Abort if no content or the target element is no longer in the DOM
|
||||||
if (!tpl || !document.body.contains(event.currentTarget)) return;
|
if (!tpl || !document.body.contains(event.currentTarget)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If mismatched, that tpl is too old, the user already display another tooltip
|
||||||
|
if (this.#displayId !== currentDisplayId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$(this.#container).append(
|
$(this.#container).append(
|
||||||
`<div id="l5r5e-tooltip-ct" class="l5r5e-tooltip l5r5e-tooltip-ct">${tpl}</div>`
|
`<div id="l5r5e-tooltip-ct" class="l5r5e-tooltip l5r5e-tooltip-ct">${tpl}</div>`
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { L5r5eSetField } from "./data/l5r5e-setfield.js";
|
import { L5r5eSetField } from "./data/l5r5e-setfield.js";
|
||||||
|
import { TacticalGridSettingsL5R5E } from "./settings/tactical-grid-settings.js"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom system settings register
|
* Custom system settings register
|
||||||
@@ -25,6 +26,15 @@ export const RegisterSettings = function () {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
});
|
});
|
||||||
|
game.settings.register(CONFIG.l5r5e.namespace, "show-all-status-effects", {
|
||||||
|
name: "SETTINGS.ShowAllStatusEffects.Title",
|
||||||
|
hint: "SETTINGS.ShowAllStatusEffects.Hint",
|
||||||
|
scope: "world",
|
||||||
|
config: true,
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
requiresReload: true,
|
||||||
|
});
|
||||||
game.settings.register(CONFIG.l5r5e.namespace, "techniques-customs", {
|
game.settings.register(CONFIG.l5r5e.namespace, "techniques-customs", {
|
||||||
name: "SETTINGS.CustomTechniques.Title",
|
name: "SETTINGS.CustomTechniques.Title",
|
||||||
hint: "SETTINGS.CustomTechniques.Hint",
|
hint: "SETTINGS.CustomTechniques.Hint",
|
||||||
@@ -227,4 +237,28 @@ export const RegisterSettings = function () {
|
|||||||
default: [],
|
default: [],
|
||||||
onChange: () => game.l5r5e.HelpersL5r5e.refreshLocalAndSocket("l5r5e-gm-monitor"),
|
onChange: () => game.l5r5e.HelpersL5r5e.refreshLocalAndSocket("l5r5e-gm-monitor"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* -------------------------------------- */
|
||||||
|
/* Grid Settings (GM only) */
|
||||||
|
/* -------------------------------------- */
|
||||||
|
// UI Configuration
|
||||||
|
game.settings.register(CONFIG.l5r5e.namespace, "tactical-grid-settings-world", {
|
||||||
|
scope: "world",
|
||||||
|
config: false,
|
||||||
|
type: TacticalGridSettingsL5R5E.worldSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register(CONFIG.l5r5e.namespace, "tactical-grid-settings-client", {
|
||||||
|
scope: "client",
|
||||||
|
config: false,
|
||||||
|
type: TacticalGridSettingsL5R5E.clientSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.registerMenu(CONFIG.l5r5e.namespace, "tactical-grid-settings", {
|
||||||
|
name: "l5r5e.tactical_grid.settings.title",
|
||||||
|
label: "l5r5e.tactical_grid.settings.label",
|
||||||
|
hint: "l5r5e.tactical_grid.settings.hint",
|
||||||
|
icon: "fa-solid fa-table-layout",
|
||||||
|
type: TacticalGridSettingsL5R5E
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
351
system/scripts/settings/tactical-grid-settings.js
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
const HandlebarsApplicationMixin = foundry.applications.api.HandlebarsApplicationMixin;
|
||||||
|
const ApplicationV2 = foundry.applications.api.ApplicationV2;
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @typedef {Object} RangeBand
|
||||||
|
* @property {number} start
|
||||||
|
*
|
||||||
|
* @typedef {Object} ClientRangeBand
|
||||||
|
* @property {string} color
|
||||||
|
* @property {number} alpha
|
||||||
|
*
|
||||||
|
* @typedef {Object} WorldSettings
|
||||||
|
* @property {boolean} enabled
|
||||||
|
* @property {Record<number, RangeBand>} ranges - Indexed 0-6
|
||||||
|
*
|
||||||
|
* @typedef {Object} ClientSettings
|
||||||
|
* @property {Record<number, ClientRangeBand>} ranges - Indexed 0-6
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class TacticalGridSettingsL5R5E extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
id: "tactical-grid-settings",
|
||||||
|
tag: "form",
|
||||||
|
classes: [""], // We could add l5r here but that would add styling that is not matching the default settings menu
|
||||||
|
window: {
|
||||||
|
title: "l5r5e.tactical_grid.settings.title",
|
||||||
|
contentClasses: ["standard-form"]
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
closeOnSubmit: true,
|
||||||
|
handler: TacticalGridSettingsL5R5E.#onSubmit
|
||||||
|
},
|
||||||
|
position: { width: 540 },
|
||||||
|
actions: {
|
||||||
|
reset: TacticalGridSettingsL5R5E.#onReset
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
form: {
|
||||||
|
template: "systems/l5r5e/templates/" + "settings/tactical-grid-settings.html",
|
||||||
|
scrollable: [""],
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
template: "templates/generic/form-footer.hbs"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a SchemaField defining a world range band.
|
||||||
|
* @param {{start: number}} initial - Initial range values.
|
||||||
|
* `start` must be ≥ 0.
|
||||||
|
* * @returns {SchemaField} A schema field containing a 'start' field
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #createWorldRangeBandSchema(initial) {
|
||||||
|
return new fields.SchemaField({
|
||||||
|
start: new fields.NumberField({ initial: initial.start, label: "l5r5e.tactical_grid.settings.world.start", min: 0, max:Infinity, nullable: false, required: true, gmOnly: true})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a SchemaField defining a client range band.
|
||||||
|
* @param {{color: string, alpha: number}} initial - Initial range band values.
|
||||||
|
* `color` should be a valid CSS color string and `alpha` a valid alpha value.
|
||||||
|
* @returns {SchemaField} A schema field containing `color` and `alpha` fields.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #createClientRangeBandSchema(initial) {
|
||||||
|
return new fields.SchemaField({
|
||||||
|
color: new fields.ColorField({initial: initial.color, label: "l5r5e.tactical_grid.settings.client.color", required: true}),
|
||||||
|
alpha: new fields.AlphaField({initial: initial.alpha, label: "l5r5e.tactical_grid.settings.client.alpha", required: true}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combined Foundry VTT settings schema representing both:
|
||||||
|
* - **World (GM-controlled)** configuration
|
||||||
|
* - **Client (per-user)** visual configuration
|
||||||
|
*
|
||||||
|
* This variable serves as a single source-of-truth definition for the module’s
|
||||||
|
* tactical grid settings structure, including field types, defaults, labels, and
|
||||||
|
* validation rules for world ranges.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #schema = {
|
||||||
|
world: new fields.SchemaField({
|
||||||
|
enabled: new fields.BooleanField({ initial: true, label: "l5r5e.tactical_grid.settings.world.enabled", hint: "l5r5e.tactical_grid.settings.world.enabled_hint"}),
|
||||||
|
ranges: new fields.SchemaField({
|
||||||
|
0: TacticalGridSettingsL5R5E.#createWorldRangeBandSchema({ start: 0}),
|
||||||
|
1: TacticalGridSettingsL5R5E.#createWorldRangeBandSchema({ start: 1}),
|
||||||
|
2: TacticalGridSettingsL5R5E.#createWorldRangeBandSchema({ start: 2}),
|
||||||
|
3: TacticalGridSettingsL5R5E.#createWorldRangeBandSchema({ start: 3}),
|
||||||
|
4: TacticalGridSettingsL5R5E.#createWorldRangeBandSchema({ start: 6}),
|
||||||
|
5: TacticalGridSettingsL5R5E.#createWorldRangeBandSchema({ start: 10}),
|
||||||
|
6: TacticalGridSettingsL5R5E.#createWorldRangeBandSchema({ start: 15})
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
validate: TacticalGridSettingsL5R5E.#validateWorldRangeConfiguration
|
||||||
|
}),
|
||||||
|
client: new fields.SchemaField({
|
||||||
|
ranges: new fields.SchemaField({
|
||||||
|
0: TacticalGridSettingsL5R5E.#createClientRangeBandSchema({color: "#00FFFF", alpha: 0.5}),
|
||||||
|
1: TacticalGridSettingsL5R5E.#createClientRangeBandSchema({color: "#FF00FF", alpha: 0.5}),
|
||||||
|
2: TacticalGridSettingsL5R5E.#createClientRangeBandSchema({color: "#FFFF00", alpha: 0.5}),
|
||||||
|
3: TacticalGridSettingsL5R5E.#createClientRangeBandSchema({color: "#0000FF", alpha: 0.5}),
|
||||||
|
4: TacticalGridSettingsL5R5E.#createClientRangeBandSchema({color: "#7FFF00", alpha: 0.5}),
|
||||||
|
5: TacticalGridSettingsL5R5E.#createClientRangeBandSchema({color: "#4B0082", alpha: 0.5}),
|
||||||
|
6: TacticalGridSettingsL5R5E.#createClientRangeBandSchema({color: "#FF8800", alpha: 0.5})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the **world (GM-controlled)** portion of the tactical grid settings schema.
|
||||||
|
* @return {SchemaField}
|
||||||
|
*/
|
||||||
|
static get worldSchema() {
|
||||||
|
return TacticalGridSettingsL5R5E.#schema.world;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the **client (per-user visual)** portion of the tactical grid settings schema.
|
||||||
|
* @return {SchemaField}
|
||||||
|
*/
|
||||||
|
static get clientSchema() {
|
||||||
|
return TacticalGridSettingsL5R5E.#schema.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds a mutable copy of the tactical grid settings so the form can operate on current values without altering the schema. */
|
||||||
|
static #setting = null;
|
||||||
|
|
||||||
|
|
||||||
|
/** @override ApplicationV2 */
|
||||||
|
async _prepareContext(options) {
|
||||||
|
if (options.isFirstRender) {
|
||||||
|
const client = game.settings.get(CONFIG.l5r5e.namespace, "tactical-grid-settings-client");
|
||||||
|
const world = game.settings.get(CONFIG.l5r5e.namespace, "tactical-grid-settings-world");
|
||||||
|
TacticalGridSettingsL5R5E.#setting = foundry.utils.deepClone({client: client, world: world});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-process range bands for easier template access
|
||||||
|
const rangeBands = Object.entries(this.constructor.worldSchema.fields.ranges.fields).map(([index, field]) => ({
|
||||||
|
index: Number(index),
|
||||||
|
worldField: field,
|
||||||
|
clientFields: this.constructor.clientSchema.fields.ranges.fields[index],
|
||||||
|
worldValue: TacticalGridSettingsL5R5E.#setting.world.ranges[index],
|
||||||
|
clientValue: TacticalGridSettingsL5R5E.#setting.client.ranges[index]
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
isGm: game.user.isGM,
|
||||||
|
tactical_grid_enabled: {
|
||||||
|
field: this.constructor.worldSchema.fields.enabled,
|
||||||
|
value: TacticalGridSettingsL5R5E.#setting.world.enabled,
|
||||||
|
},
|
||||||
|
rangeBands,
|
||||||
|
buttons: [
|
||||||
|
{ type: "reset", label: "l5r5e.tactical_grid.settings.reset", icon: "fa-solid fa-arrow-rotate-left", action: "reset" },
|
||||||
|
{ type: "submit", label: "l5r5e.tactical_grid.settings.submit", icon: "fa-solid fa-floppy-disk" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @override ApplicationV2 */
|
||||||
|
_onChangeForm(formConfig, event) {
|
||||||
|
const formData = new foundry.applications.ux.FormDataExtended(this.form, { readonly: true });
|
||||||
|
|
||||||
|
const {
|
||||||
|
cleaned: cleanedWorldSettings,
|
||||||
|
failure: validationFailures
|
||||||
|
} = TacticalGridSettingsL5R5E.#validateAndCleanWorldSettings(formData, event);
|
||||||
|
TacticalGridSettingsL5R5E.#applyValidationState(validationFailures);
|
||||||
|
|
||||||
|
TacticalGridSettingsL5R5E.#setting.world = cleanedWorldSettings
|
||||||
|
TacticalGridSettingsL5R5E.#setting.client = foundry.utils.expandObject(formData.object).l5r5e["tactical-grid-settings-client"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates world schema ensuring range bands are properly ordered and connected.
|
||||||
|
* Note: internal field validation takes precedence, and will result in this validation potentially not running
|
||||||
|
* Checks that:
|
||||||
|
* - Sequential range bands connect properly (range[n].start < range[n+1].start)
|
||||||
|
* @param {*} value - The world settings object to validate
|
||||||
|
* @param {DataFieldValidationOptions} options - Validation options including lastElementChange
|
||||||
|
* @returns {boolean|foundry.data.validation.DataModelValidationFailure} True if valid, otherwise validation failure object
|
||||||
|
*/
|
||||||
|
static #validateWorldRangeConfiguration(value, options) {
|
||||||
|
if(!value.enabled) // don't validate if tactical_grids are disabled
|
||||||
|
return true;
|
||||||
|
|
||||||
|
let previousStart = -1;
|
||||||
|
let previousRangeIndex = null;
|
||||||
|
const failure = new foundry.data.validation.DataModelValidationFailure({ unresolved: true });
|
||||||
|
const changedElementName = options?.element?.name;
|
||||||
|
|
||||||
|
for (const [rangeIndex, range] of Object.entries(value.ranges)) {
|
||||||
|
if (range.start <= previousStart) {
|
||||||
|
let errorKey = TacticalGridSettingsL5R5E.worldSchema.fields.ranges.fields[rangeIndex].fields.start.fieldPath;
|
||||||
|
const previousErrorKey = errorKey.replace(/\.(\d+)\./, `.${previousRangeIndex}.`);
|
||||||
|
|
||||||
|
let isErrorOnPrevious = false;
|
||||||
|
|
||||||
|
// If the previous field was changed, show error there instead
|
||||||
|
if (changedElementName === previousErrorKey) {
|
||||||
|
errorKey = previousErrorKey;
|
||||||
|
isErrorOnPrevious = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
failure.fields[errorKey] = new foundry.data.validation.DataModelValidationFailure({
|
||||||
|
invalidValue: isErrorOnPrevious ? previousStart : range.start,
|
||||||
|
unresolved: true,
|
||||||
|
message: game.i18n.format(
|
||||||
|
isErrorOnPrevious
|
||||||
|
? "l5r5e.tactical_grid.settings.validate.start-too-large"
|
||||||
|
: "l5r5e.tactical_grid.settings.validate.start-too-small",
|
||||||
|
isErrorOnPrevious
|
||||||
|
? { nextRangeIndex: Number(rangeIndex), nextStart: range.start }
|
||||||
|
: { previousRangeIndex: Number(previousRangeIndex), previousStart: previousStart }
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
previousStart = range.start;
|
||||||
|
previousRangeIndex = rangeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(failure.fields).length > 0 ? failure : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates and cleans the world portion of the tactical grid settings.
|
||||||
|
*
|
||||||
|
* Expands raw form data, validates it against the schema, updates form input
|
||||||
|
* error states and tooltips, and returns a cleaned object ready to save.
|
||||||
|
*
|
||||||
|
* @param {foundry.applications.ux.FormDataExtended} formData - The submitted form data.
|
||||||
|
* @param {Event} [event] - Optional event for determining which field changed.
|
||||||
|
* @returns {WorldSettings} A cleaned and validated copy of the world settings.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #validateAndCleanWorldSettings(formData, event) {
|
||||||
|
const expanded = foundry.utils.expandObject(formData.object).l5r5e["tactical-grid-settings-world"];
|
||||||
|
const validate = TacticalGridSettingsL5R5E.#schema.world.validate(expanded, { element: event.target});
|
||||||
|
|
||||||
|
// validation from Number etc. itself has the error key just as "ranges.0.start"
|
||||||
|
// so fixing that here so that we can directly reference they html elements
|
||||||
|
const prefix = "l5r5e.tactical-grid-settings-world.";
|
||||||
|
const failures = Object.fromEntries(
|
||||||
|
Object.entries(validate?.asError()?.getAllFailures() ?? {}).map(([key, value]) => [
|
||||||
|
key.startsWith(prefix) ? key : `${prefix}${key}`,
|
||||||
|
value
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return cleaned schema so that we have something that is somewhat correct we can save
|
||||||
|
return {
|
||||||
|
cleaned: TacticalGridSettingsL5R5E.#schema.world.clean(expanded),
|
||||||
|
failure: failures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a validation message to a form element.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element - The element to apply validation to
|
||||||
|
* @param {string|null} message - The validation message, or null to clear
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #applyValidationMessage(element, message) {
|
||||||
|
if (message) {
|
||||||
|
element.setCustomValidity(message);
|
||||||
|
element.dataset.tooltip = message;
|
||||||
|
element.ariaLabel = game.i18n.localize(element.dataset.tooltip);
|
||||||
|
game.tooltip.activate(element, {
|
||||||
|
direction: foundry.CONFIG.ux.TooltipManager.TOOLTIP_DIRECTIONS.RIGHT,
|
||||||
|
locked: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
element?.setCustomValidity("");
|
||||||
|
delete element?.dataset?.tooltip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies validation state to all range band start fields.
|
||||||
|
*
|
||||||
|
* @param {Object} failures - Validation failures keyed by field name
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #applyValidationState(failures) {
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
const name = `l5r5e.tactical-grid-settings-world.ranges.${i}.start`;
|
||||||
|
this.#applyValidationMessage(
|
||||||
|
document.getElementsByName(name)[0],
|
||||||
|
failures?.[name]?.message || null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles form submission.
|
||||||
|
*
|
||||||
|
* @param {Event} event - The submission event
|
||||||
|
* @param {HTMLFormElement} form - The form element
|
||||||
|
* @param {foundry.applications.ux.FormDataExtended} formData - The submitted form data
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onSubmit(event, form, formData) {
|
||||||
|
const {
|
||||||
|
cleaned: cleanedWorldSettings,
|
||||||
|
failure: validationFailures
|
||||||
|
} = TacticalGridSettingsL5R5E.#validateAndCleanWorldSettings(formData, event);
|
||||||
|
TacticalGridSettingsL5R5E.#applyValidationState(validationFailures);
|
||||||
|
|
||||||
|
TacticalGridSettingsL5R5E.#setting.world = cleanedWorldSettings;
|
||||||
|
TacticalGridSettingsL5R5E.#setting.client = foundry.utils.expandObject(formData.object).l5r5e["tactical-grid-settings-client"];
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
promises.push(game.settings.set(CONFIG.l5r5e.namespace, "tactical-grid-settings-world", TacticalGridSettingsL5R5E.#setting.world));
|
||||||
|
promises.push(game.settings.set(CONFIG.l5r5e.namespace, "tactical-grid-settings-client", TacticalGridSettingsL5R5E.#setting.client));
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles reset action to restore default settings.
|
||||||
|
*
|
||||||
|
* @param {Event} event - The reset event
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onReset(event) {
|
||||||
|
const client = TacticalGridSettingsL5R5E.clientSchema.clean();
|
||||||
|
const world = TacticalGridSettingsL5R5E.worldSchema.clean();
|
||||||
|
TacticalGridSettingsL5R5E.#setting = foundry.utils.deepClone({client: client, world: world});
|
||||||
|
|
||||||
|
await this.render({ force: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
81
system/scripts/tatical-grid-rulers.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
function getRangeband(gridSettings, distance) {
|
||||||
|
const entries = Object.entries(gridSettings.ranges);
|
||||||
|
for (let i = entries.length - 1; i >= 0; i--) {
|
||||||
|
const [range, { start }] = entries[i];
|
||||||
|
if (distance >= start) {
|
||||||
|
return Number(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RulerL5r5e extends foundry.canvas.interaction.Ruler {
|
||||||
|
|
||||||
|
static WAYPOINT_LABEL_TEMPLATE = "systems/l5r5e/templates/" + "hud/tactical-grid-ruler.html"
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_getWaypointLabelContext(waypoint, state) {
|
||||||
|
const context = super._getWaypointLabelContext(waypoint, state);
|
||||||
|
if (!context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const gridSettings = game.settings.get(CONFIG.l5r5e.namespace, "tactical-grid-settings-world");
|
||||||
|
if(gridSettings.enabled) {
|
||||||
|
const diagonalCost = game.canvas.grid.distance * waypoint.measurement.diagonals;
|
||||||
|
context.distance.total = waypoint.measurement.distance.toNearest(0.1) + diagonalCost; //Diagonals count twice
|
||||||
|
context.additional = {
|
||||||
|
label: game.i18n.format("l5r5e.tactical_grid.range_abbreviation", {range: getRangeband(gridSettings, waypoint.measurement.distance)})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_getSegmentStyle(waypoint) {
|
||||||
|
const context = super._getSegmentStyle(waypoint);
|
||||||
|
const client = game.settings.get(CONFIG.l5r5e.namespace, "tactical-grid-settings-client");
|
||||||
|
const gridSettings = game.settings.get(CONFIG.l5r5e.namespace, "tactical-grid-settings-world");
|
||||||
|
if(gridSettings.enabled) {
|
||||||
|
const rangeband = getRangeband(gridSettings, waypoint.measurement.distance);
|
||||||
|
context.color = client.ranges[rangeband].color;
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TokenRulerL5r5e extends foundry.canvas.placeables.tokens.TokenRuler {
|
||||||
|
static WAYPOINT_LABEL_TEMPLATE = "systems/l5r5e/templates/" + "hud/tactical-grid-ruler.html"
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_getWaypointLabelContext(waypoint, state) {
|
||||||
|
const context = super._getWaypointLabelContext(waypoint, state);
|
||||||
|
if (!context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.token.actor)
|
||||||
|
return context;
|
||||||
|
|
||||||
|
const gridSettings = game.settings.get(CONFIG.l5r5e.namespace, "tactical-grid-settings-world");
|
||||||
|
if(gridSettings.enabled) {
|
||||||
|
const diagonalCost = game.canvas.grid.distance * waypoint.measurement.diagonals;
|
||||||
|
context.cost.total = waypoint.measurement.cost.toNearest(0.1) + diagonalCost; //Diagonals count twice
|
||||||
|
context.additional = {
|
||||||
|
label: game.i18n.format("l5r5e.tactical_grid.range_abbreviation", {range: getRangeband(gridSettings, waypoint.measurement.distance)})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_getGridHighlightStyle(waypoint, offset) {
|
||||||
|
const context = super._getGridHighlightStyle(waypoint, offset);
|
||||||
|
const client = game.settings.get(CONFIG.l5r5e.namespace, "tactical-grid-settings-client");
|
||||||
|
const gridSettings = game.settings.get(CONFIG.l5r5e.namespace, "tactical-grid-settings-world");
|
||||||
|
if(gridSettings.enabled) {
|
||||||
|
const rangeband = getRangeband(gridSettings, waypoint.measurement.distance);
|
||||||
|
context.color = client.ranges[rangeband].color;
|
||||||
|
context.alpha = client.ranges[rangeband].alpha;
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
system/scripts/types.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {"afflicted" | "bleeding" | "burning" | "centered" | "compromised" | "dazed" | "disoriented" | "dying" | "emboldened" | "enraged" | "exhausted" | "illness_coughing_illness" | "illness_fire_rash" | "illness_gut_sickness" | "illness_oozing_sore_disease" | "illness_unsteady_illness" | "immobilized" | "incapacitated" | "intoxicated" | "possessed" | "prone" | "silenced" | "unconscious" | "lightly_wounded_fire" | "lightly_wounded_water" | "lightly_wounded_air" | "lightly_wounded_earth" | "lightly_wounded_void" | "severely_wounded_fire" | "severely_wounded_water" | "severely_wounded_air" | "severely_wounded_earth" | "severely_wounded_void"} Condition
|
||||||
|
*/
|
||||||
@@ -20,5 +20,6 @@
|
|||||||
@import "../scss/skills";
|
@import "../scss/skills";
|
||||||
@import "../scss/items";
|
@import "../scss/items";
|
||||||
@import "../scss/twenty-questions";
|
@import "../scss/twenty-questions";
|
||||||
|
@import "../scss/tactical-grid";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
.readiness {
|
.readiness {
|
||||||
flex: 0 0 100%;
|
flex: 0 0 100%;
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -21,15 +19,20 @@
|
|||||||
|
|
||||||
li {
|
li {
|
||||||
flex: 25%;
|
flex: 25%;
|
||||||
display: inline-grid;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.attributes-buttons {
|
.increment-control {
|
||||||
position: relative;
|
position: absolute;
|
||||||
line-height: 13px;
|
right: -0.70rem;
|
||||||
top: 0.3rem;
|
top: 15%;
|
||||||
right: 1.2rem;
|
|
||||||
width: 12px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap:0.1rem;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
@@ -40,14 +43,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
flex: 100%;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 0 none;
|
border: 0 none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0.3rem 1.6rem 0 1.5rem;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
@@ -55,8 +63,6 @@
|
|||||||
width: 2rem;
|
width: 2rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: calc(50% - 0.9rem);
|
|
||||||
top: 0.1rem;
|
|
||||||
background: transparent url("../assets/icons/circle.svg") no-repeat 0 0;
|
background: transparent url("../assets/icons/circle.svg") no-repeat 0 0;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
@@ -64,9 +70,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:nth-child(1) {
|
&:nth-child(1) {
|
||||||
input {
|
|
||||||
margin: 0.3rem 1rem 0 1.5rem;
|
|
||||||
}
|
|
||||||
&:after {
|
&:after {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
@@ -79,9 +82,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:nth-child(3) {
|
&:nth-child(3) {
|
||||||
input {
|
|
||||||
margin: 0.3rem 1rem 0 1.5rem;
|
|
||||||
}
|
|
||||||
&:after {
|
&:after {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ $l5r5e-chat-color-roll: rgba(225, 215, 200, 0.75);
|
|||||||
$l5r5e-chat-color-blind: transparent;
|
$l5r5e-chat-color-blind: transparent;
|
||||||
$l5r5e-chat-color-whisper: rgba(225, 200, 225, 0.75);
|
$l5r5e-chat-color-whisper: rgba(225, 200, 225, 0.75);
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
$l5r5e-selection-circle-color: #8a1a00;
|
||||||
|
|
||||||
// -- Rings
|
// -- Rings
|
||||||
|
|
||||||
// Earth
|
// Earth
|
||||||
@@ -65,6 +68,7 @@ $l5r5e-water: rgb(95, 145, 155);
|
|||||||
$l5r5e-fire: rgb(155, 115, 80);
|
$l5r5e-fire: rgb(155, 115, 80);
|
||||||
// Void
|
// Void
|
||||||
$l5r5e-void: rgb(75, 70, 65);
|
$l5r5e-void: rgb(75, 70, 65);
|
||||||
|
$l5r5e-void-light: rgba(207,207,207,.8);
|
||||||
|
|
||||||
// -- Clans
|
// -- Clans
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
height: 48px;
|
||||||
|
width: 48px;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -20,6 +23,7 @@
|
|||||||
color: white;
|
color: white;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-of-type,
|
&:last-of-type,
|
||||||
&:nth-child(6),
|
&:nth-child(6),
|
||||||
&:nth-child(12),
|
&:nth-child(12),
|
||||||
@@ -28,10 +32,12 @@
|
|||||||
padding: 0 0.175rem 0 0.15rem;
|
padding: 0 0.175rem 0 0.15rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.rerolled {
|
&.rerolled {
|
||||||
> img {
|
> img {
|
||||||
border-bottom: 0 none;
|
border-bottom: 0 none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: "\f2f9";
|
content: "\f2f9";
|
||||||
background: orangered;
|
background: orangered;
|
||||||
@@ -42,6 +48,7 @@
|
|||||||
> img {
|
> img {
|
||||||
border-bottom: 0 none;
|
border-bottom: 0 none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: "\f337";
|
content: "\f337";
|
||||||
background: fuchsia;
|
background: fuchsia;
|
||||||
@@ -50,9 +57,9 @@
|
|||||||
|
|
||||||
> img {
|
> img {
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
height: auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
.chat-dice {
|
.chat-dice {
|
||||||
}
|
}
|
||||||
@@ -72,6 +79,7 @@
|
|||||||
margin: 0.25rem;
|
margin: 0.25rem;
|
||||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-element {
|
&-element {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -79,11 +87,13 @@
|
|||||||
&-skill {
|
&-skill {
|
||||||
flex-grow: 3;
|
flex-grow: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dice-formula,
|
.dice-formula,
|
||||||
.dice-total {
|
.dice-total {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
@@ -91,15 +101,19 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0.5rem 0;
|
margin: 0.5rem 0;
|
||||||
padding: 0.25rem 0.5rem 0.25rem 0.25rem;
|
padding: 0.25rem 0.5rem 0.25rem 0.25rem;
|
||||||
|
|
||||||
&-rnk {
|
&-rnk {
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
|
|
||||||
&.chat-dice-rnk {
|
&.chat-dice-rnk {
|
||||||
cursor: url("../assets/cursors/pointer.webp"), pointer;
|
cursor: url("../assets/cursors/pointer.webp"), pointer;
|
||||||
color: $white;
|
color: $white;
|
||||||
@@ -113,6 +127,7 @@
|
|||||||
border-image-width: 0.5rem;
|
border-image-width: 0.5rem;
|
||||||
border-image-outset: 0px;
|
border-image-outset: 0px;
|
||||||
margin: 0.5rem 0 0;
|
margin: 0.5rem 0 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
$l5r5e-linear-gradient-first-dark,
|
$l5r5e-linear-gradient-first-dark,
|
||||||
@@ -121,6 +136,7 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.chat-dice-rnk-ended {
|
&.chat-dice-rnk-ended {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
$l5r5e-linear-gradient-second,
|
$l5r5e-linear-gradient-second,
|
||||||
@@ -129,6 +145,7 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dice-result-rnk {
|
.dice-result-rnk {
|
||||||
background: rgba(0, 0, 255, 0.1);
|
background: rgba(0, 0, 255, 0.1);
|
||||||
border: 1px solid rgba(55, 55, 155, 0.75);
|
border: 1px solid rgba(55, 55, 155, 0.75);
|
||||||
@@ -137,25 +154,30 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-shadow: 0 0 0 $black;
|
text-shadow: 0 0 0 $black;
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
background: rgba(0, 255, 0, 0.1);
|
background: rgba(0, 255, 0, 0.1);
|
||||||
border-color: rgba(55, 155, 55, 0.75);
|
border-color: rgba(55, 155, 55, 0.75);
|
||||||
color: rgba(55, 155, 55, 0.75);
|
color: rgba(55, 155, 55, 0.75);
|
||||||
|
|
||||||
i.i_success {
|
i.i_success {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.unknown {
|
&.unknown {
|
||||||
background: rgba(121, 121, 121, 0.1);
|
background: rgba(121, 121, 121, 0.1);
|
||||||
border-color: rgba(124, 124, 124, 0.75);
|
border-color: rgba(124, 124, 124, 0.75);
|
||||||
color: rgba(91, 91, 91, 0.75);
|
color: rgba(91, 91, 91, 0.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.fail {
|
&.fail {
|
||||||
background: rgba(255, 0, 0, 0.1);
|
background: rgba(255, 0, 0, 0.1);
|
||||||
border-color: rgba(155, 55, 55, 0.75);
|
border-color: rgba(155, 55, 55, 0.75);
|
||||||
color: rgba(155, 55, 55, 0.75);
|
color: rgba(155, 55, 55, 0.75);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.target,
|
.target,
|
||||||
.item-infos {
|
.item-infos {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -166,20 +188,25 @@
|
|||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
border: solid 1px rgba(100, 0, 0, 0.75);
|
border: solid 1px rgba(100, 0, 0, 0.75);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
.profile {
|
.profile {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin: 0.25rem 0.25rem 0 0;
|
margin: 0.25rem 0.25rem 0 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.profile-img {
|
.profile-img {
|
||||||
position: relative;
|
position: relative;
|
||||||
border: none;
|
border: none;
|
||||||
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.66));
|
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.66));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
flex: 6;
|
flex: 6;
|
||||||
font-family: "BrushtipTexe", sans-serif;
|
font-family: "BrushtipTexe", sans-serif;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-link {
|
.content-link {
|
||||||
background: unset;
|
background: unset;
|
||||||
border: unset;
|
border: unset;
|
||||||
@@ -188,8 +215,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-infos {
|
.item-infos {
|
||||||
border: solid 1px rgba(0, 78, 100, 0.75);
|
border: solid 1px rgba(0, 78, 100, 0.75);
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: var(--font-size-12);
|
font-size: var(--font-size-12);
|
||||||
}
|
}
|
||||||
@@ -199,3 +228,4 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,55 @@
|
|||||||
cursor: url("../assets/cursors/pointer.webp"), pointer;
|
cursor: url("../assets/cursors/pointer.webp"), pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// define a mixin
|
||||||
|
@mixin roll-effects-base {
|
||||||
|
clear: both;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2px 4px;
|
||||||
|
|
||||||
|
.effect-container {
|
||||||
|
border: 1px solid #5a6e5a;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
padding: 3px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-delete {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
text-align: end;
|
||||||
|
cursor: url("../assets/cursors/pointer.webp"), pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-name {
|
||||||
|
vertical-align: top;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: $white;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dice Picker
|
// Dice Picker
|
||||||
.dice-picker-dialog {
|
.dice-picker-dialog {
|
||||||
min-width: 35rem;
|
.effects {
|
||||||
|
@include roll-effects-base();
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 35rem;
|
||||||
min-height: auto;
|
min-height: auto;
|
||||||
// Utility
|
// Utility
|
||||||
* {
|
* {
|
||||||
@@ -167,6 +213,7 @@
|
|||||||
td {
|
td {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,6 +253,12 @@
|
|||||||
|
|
||||||
.profil {
|
.profil {
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column; // stack profile ul and effects ul vertically
|
||||||
|
|
||||||
|
.effects {
|
||||||
|
@include roll-effects-base();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropbox {
|
.dropbox {
|
||||||
@@ -258,6 +311,7 @@
|
|||||||
.dice-ct {
|
.dice-ct {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
|
width: 48px;
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.application {
|
.application {
|
||||||
color: var(--color-text-dark-primary);
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
.scrollable {
|
.scrollable {
|
||||||
--scroll-margin: 0;
|
--scroll-margin: 0;
|
||||||
@@ -48,10 +48,70 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Sidebar here. Starting with custom icons
|
||||||
|
$l5r5e-custom-icons: (
|
||||||
|
chatIcon: "../assets/ui/sidebar/chat.svg",
|
||||||
|
combatIcon: "../assets/ui/sidebar/combat-tracker.svg",
|
||||||
|
sceneIcon: "../assets/ui/sidebar/scenes.svg",
|
||||||
|
actorIcon: "../assets/ui/sidebar/actors.svg",
|
||||||
|
itemIcon: "../assets/ui/sidebar/object.svg",
|
||||||
|
journalIcon: "../assets/ui/sidebar/journal.svg",
|
||||||
|
rolltableIcon: "../assets/ui/sidebar/rolltable.svg",
|
||||||
|
playlistIcon: "../assets/ui/sidebar/playlist.svg",
|
||||||
|
compendiumIcon: "../assets/ui/sidebar/compendium.svg",
|
||||||
|
settingsIcon: "../assets/ui/sidebar/settings.svg"
|
||||||
|
);
|
||||||
|
|
||||||
|
$selectors: (
|
||||||
|
"#sidebar-tabs button.l5r5e",
|
||||||
|
"#sidebar-content .create-button.l5r5e",
|
||||||
|
"#sidebar-content i.l5r5e",
|
||||||
|
"#context-menu i.l5r5e"
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $selector in $selectors {
|
||||||
|
#{$selector} {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
@if str-index($selector, "i.") {
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if str-index($selector, "create-button") {
|
||||||
|
filter: drop-shadow(0 0 3px var(--color-dark-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply masks for each icon
|
||||||
|
@each $name, $url in $l5r5e-custom-icons {
|
||||||
|
&.#{$name}::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 85%;
|
||||||
|
height: 85%;
|
||||||
|
background-color: currentColor;
|
||||||
|
mask: url($url) no-repeat center / contain;
|
||||||
|
-webkit-mask: url($url) no-repeat center / contain;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic plus icon badge
|
||||||
|
&.icon-plus::after {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#sidebar-tabs > menu {
|
#sidebar-tabs > menu {
|
||||||
gap: 4px; // halve the distance between menu icons
|
gap: 4px; // halve the distance between menu icons
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#sidebar-content.expanded {
|
#sidebar-content.expanded {
|
||||||
background: url("../assets/ui/bgSidebar.webp") no-repeat;
|
background: url("../assets/ui/bgSidebar.webp") no-repeat;
|
||||||
border-image: url("../assets/ui/macro-button.webp") 10 repeat;
|
border-image: url("../assets/ui/macro-button.webp") 10 repeat;
|
||||||
|
|||||||
@@ -122,11 +122,6 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Focus, Active */
|
/* Focus, Active */
|
||||||
* {
|
|
||||||
transition-property: background, color, border-color, text-shadow, box-shadow;
|
|
||||||
transition-duration: 0.5s;
|
|
||||||
transition-timing-function: ease;
|
|
||||||
}
|
|
||||||
input[type="text"]:focus,
|
input[type="text"]:focus,
|
||||||
input[type="number"]:focus,
|
input[type="number"]:focus,
|
||||||
input[type="password"]:focus,
|
input[type="password"]:focus,
|
||||||
@@ -150,6 +145,11 @@ input[type="time"]:focus {
|
|||||||
line-height: initial;
|
line-height: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove icon on link (bc specificity)
|
||||||
|
#sidebar-content .content-link i.l5r5e {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
box-shadow: 0 0 10px $red;
|
box-shadow: 0 0 10px $red;
|
||||||
}
|
}
|
||||||
@@ -187,13 +187,13 @@ fieldset {
|
|||||||
}
|
}
|
||||||
.editor {
|
.editor {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Editors
|
// Editors
|
||||||
.editor,
|
.editor,
|
||||||
.editor-container {
|
.editor-container {
|
||||||
flex: 1;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +232,7 @@ sup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editor-container,
|
.editor-container,
|
||||||
|
.editor-content,
|
||||||
.item-description {
|
.item-description {
|
||||||
ul {
|
ul {
|
||||||
margin: 0.5rem 0;
|
margin: 0.5rem 0;
|
||||||
@@ -309,4 +310,3 @@ a.compendium-link {
|
|||||||
color: #0096ff;
|
color: #0096ff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,13 +67,21 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.effect-delete {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
text-align: end;
|
||||||
|
cursor: url("../assets/cursors/pointer.webp"), pointer;
|
||||||
}
|
}
|
||||||
.effect-icon {
|
.effect-icon {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
float: left;
|
|
||||||
}
|
}
|
||||||
.effect-name {
|
.effect-name {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
@@ -81,7 +89,6 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
color: $white;
|
color: $white;
|
||||||
float: right;
|
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
@@ -319,7 +326,7 @@
|
|||||||
margin: 0.25rem 0;
|
margin: 0.25rem 0;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
strong {
|
strong {
|
||||||
flex: 0 0 calc(100% - 3.5rem);
|
flex: 0 0 calc(100% - 4.5rem);
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
flex: 0 0 3rem;
|
flex: 0 0 3rem;
|
||||||
@@ -437,7 +444,7 @@
|
|||||||
width: 3.5rem;
|
width: 3.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.attributes-buttons {
|
.increment-control {
|
||||||
line-height: 13px;
|
line-height: 13px;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0.2rem;
|
top: 0.2rem;
|
||||||
@@ -744,12 +751,22 @@
|
|||||||
flex: calc(100% / 3);
|
flex: calc(100% / 3);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
align-items: center;
|
||||||
input {
|
input {
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.xp-buttons,
|
||||||
.money-buttons {
|
.money-buttons {
|
||||||
line-height: 13px;
|
line-height: 13px;
|
||||||
|
padding-left: 0.3em;
|
||||||
|
}
|
||||||
|
.increment-control {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 0.2rem;
|
||||||
|
flex: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
32
system/styles/scss/tactical-grid.scss
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
// Set the label for in-world measurement to be the same as normal waypoint-label
|
||||||
|
@at-root #measurement .waypoint-label-additional {
|
||||||
|
color: var(--color-text-emphatic);
|
||||||
|
font-size: var(--font-size-24);
|
||||||
|
}
|
||||||
|
|
||||||
|
@at-root #tactical-grid-settings {
|
||||||
|
input[type="number"]:invalid {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"]:read-only {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none; /* not clickable or focusable */
|
||||||
|
user-select: none; /* text cannot be selected */
|
||||||
|
-webkit-user-select: none; /* Safari/Chrome */
|
||||||
|
-moz-user-select: none; /* Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
|
.range_band {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
|
fieldset {
|
||||||
|
flex: 25%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
width: 4rem;
|
width: 4rem;
|
||||||
height: 41.58rem;
|
height: 41.58rem;
|
||||||
margin: 1%;
|
margin: 1%;
|
||||||
line-height: 5rem;
|
line-height: 3rem;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
border-bottom: 0 none;
|
border-bottom: 0 none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
"changelog": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/CHANGELOG.md",
|
"changelog": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/CHANGELOG.md",
|
||||||
"license": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/LICENSE.md",
|
"license": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/LICENSE.md",
|
||||||
"manifest": "https://gitlab.com/teaml5r/l5r5e/-/raw/master/system/system.json",
|
"manifest": "https://gitlab.com/teaml5r/l5r5e/-/raw/master/system/system.json",
|
||||||
"download": "https://gitlab.com/teaml5r/l5r5e/-/jobs/artifacts/v1.13.0/raw/l5r5e.zip?job=build",
|
"download": "https://gitlab.com/teaml5r/l5r5e/-/jobs/artifacts/v1.14.0/raw/l5r5e.zip?job=build",
|
||||||
"version": "1.13.0",
|
"version": "1.14.0",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13",
|
"minimum": "13",
|
||||||
"verified": "13",
|
"verified": "13",
|
||||||
"maximum": "13"
|
"maximum": "14"
|
||||||
},
|
},
|
||||||
"socket": true,
|
"socket": true,
|
||||||
"authors": [
|
"authors": [
|
||||||
@@ -29,6 +29,10 @@
|
|||||||
"name": "Carter",
|
"name": "Carter",
|
||||||
"discord": "Carter#2703",
|
"discord": "Carter#2703",
|
||||||
"url": "https://fr.tipeee.com/carter-foundryvtt"
|
"url": "https://fr.tipeee.com/carter-foundryvtt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Litasa",
|
||||||
|
"discord": "Litasa#3139"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"background": "systems/l5r5e/assets/l5r-header.webp",
|
"background": "systems/l5r5e/assets/l5r-header.webp",
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
{{#each data.splitTechniquesList as |list techName|}}
|
{{#each data.splitTechniquesList as |list techName|}}
|
||||||
<li>
|
<li>
|
||||||
<strong>{{localize (localize 'l5r5e.techniques.{techName}' techName=techName) }}</strong>
|
<strong>{{localizeEmbedded 'l5r5e.techniques.{techName}' techName=techName}}</strong>
|
||||||
<ul>
|
<ul>
|
||||||
{{#each list as |technique|}}
|
{{#each list as |technique|}}
|
||||||
<li>
|
<li>
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
<li>{{localize 'l5r5e.money.title'}} : {{data.system.money.koku}} {{localize 'l5r5e.money.koku'}} / {{data.system.money.bu}} {{localize 'l5r5e.money.bu'}} / {{data.system.money.zeni}} {{localize 'l5r5e.money.zeni'}}</li>
|
<li>{{localize 'l5r5e.money.title'}} : {{data.system.money.koku}} {{localize 'l5r5e.money.koku'}} / {{data.system.money.bu}} {{localize 'l5r5e.money.bu'}} / {{data.system.money.zeni}} {{localize 'l5r5e.money.zeni'}}</li>
|
||||||
{{#each data.splitItemsList as |cat type|}}
|
{{#each data.splitItemsList as |cat type|}}
|
||||||
<li>
|
<li>
|
||||||
<strong>{{localize (localize 'l5r5e.{type}s.title' type=type)}} ({{cat.length}})</strong>
|
<strong>{{localizeEmbedded 'l5r5e.{type}s.title' type=type}} ({{cat.length}})</strong>
|
||||||
<ul>
|
<ul>
|
||||||
{{#each cat as |item|}}
|
{{#each cat as |item|}}
|
||||||
<li>{{> 'systems/l5r5e/templates/items/item/item-text.html' data=item editable=../../options.editable}}</li>
|
<li>{{> 'systems/l5r5e/templates/items/item/item-text.html' data=item editable=../../options.editable}}</li>
|
||||||
@@ -209,20 +209,20 @@
|
|||||||
{{/ifCond}}
|
{{/ifCond}}
|
||||||
<h2>{{localize 'l5r5e.twenty_questions.title'}}</h2>
|
<h2>{{localize 'l5r5e.twenty_questions.title'}}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part2.q4{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step4.stand_out}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part2.q4{suffix}' suffix}} : {{data.system.twenty_questions.step4.stand_out}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part3.q7{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step7.clan_relations}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part3.q7{suffix}' suffix}} : {{data.system.twenty_questions.step7.clan_relations}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part3.q8{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step8.bushido}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part3.q8{suffix}' suffix}} : {{data.system.twenty_questions.step8.bushido}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part4.q9{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step9.success}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part4.q9{suffix}' suffix}} : {{data.system.twenty_questions.step9.success}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part4.q10{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step10.difficulty}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part4.q10{suffix}' suffix}} : {{data.system.twenty_questions.step10.difficulty}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part4.q11{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step11.calms}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part4.q11{suffix}' suffix}} : {{data.system.twenty_questions.step11.calms}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part4.q12{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step12.worries}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part4.q12{suffix}' suffix}} : {{data.system.twenty_questions.step12.worries}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part4.q13{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step13.most_learn}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part4.q13{suffix}' suffix}} : {{data.system.twenty_questions.step13.most_learn}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part5.q14{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step14.first_sight}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part5.q14{suffix}' suffix}} : {{data.system.twenty_questions.step14.first_sight}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part5.q15{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step15.stress}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part5.q15{suffix}' suffix}} : {{data.system.twenty_questions.step15.stress}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part5.q16{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step16.relations}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part5.q16{suffix}' suffix}} : {{data.system.twenty_questions.step16.relations}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part6.q17{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step17.parents_pov}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part6.q17{suffix}' suffix}} : {{data.system.twenty_questions.step17.parents_pov}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part6.q18{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step18.heritage_name}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part6.q18{suffix}' suffix}} : {{data.system.twenty_questions.step18.heritage_name}}</li>
|
||||||
<li>{{localize (localize 'l5r5e.twenty_questions.part7.q20{suffix}' suffix=suffix)}} : {{data.system.twenty_questions.step20.death}}</li>
|
<li>{{localizeEmbedded 'l5r5e.twenty_questions.part7.q20{suffix}' suffix}} : {{data.system.twenty_questions.step20.death}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
<div class="readiness">
|
<div class="readiness">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<label class="attribute-label-casualties">
|
<label class="attribute-label casualties">
|
||||||
<input name="system.battle_readiness.casualties_strength.value" type="number" value="{{data.system.battle_readiness.casualties_strength.value}}" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input name="system.battle_readiness.casualties_strength.value" type="number" value="{{data.system.battle_readiness.casualties_strength.value}}" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
<span class="attributes-buttons">
|
<span class="increment-control casualties">
|
||||||
<i class="addsub-control pointer-choice fa fa-plus-square" data-type="casualties" data-value="1"></i>
|
<i class="addsub-control pointer-choice fa fa-plus-square" data-type="casualties" data-value="1"></i>
|
||||||
<i class="addsub-control pointer-choice fa fa-minus-square" data-type="casualties" data-value="-1"></i>
|
<i class="addsub-control pointer-choice fa fa-minus-square" data-type="casualties" data-value="-1"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -23,15 +23,15 @@
|
|||||||
<strong>{{localize 'l5r5e.army.battle_readiness.casualties'}}</strong>
|
<strong>{{localize 'l5r5e.army.battle_readiness.casualties'}}</strong>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label class="attribute-label-strength">
|
<label class="attribute-label strength">
|
||||||
<input name="system.battle_readiness.casualties_strength.max" type="number" value="{{data.system.battle_readiness.casualties_strength.max}}" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input name="system.battle_readiness.casualties_strength.max" type="number" value="{{data.system.battle_readiness.casualties_strength.max}}" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
</label>
|
</label>
|
||||||
<strong>{{localize 'l5r5e.army.battle_readiness.strength'}}</strong>
|
<strong>{{localize 'l5r5e.army.battle_readiness.strength'}}</strong>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label class="attribute-label-panic">
|
<label class="attribute-label panic">
|
||||||
<input name="system.battle_readiness.panic_discipline.value" type="number" value="{{data.system.battle_readiness.panic_discipline.value}}" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input name="system.battle_readiness.panic_discipline.value" type="number" value="{{data.system.battle_readiness.panic_discipline.value}}" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
<span class="attributes-buttons">
|
<span class="increment-control panic">
|
||||||
<i class="addsub-control pointer-choice fa fa-plus-square" data-type="panic" data-value="1"></i>
|
<i class="addsub-control pointer-choice fa fa-plus-square" data-type="panic" data-value="1"></i>
|
||||||
<i class="addsub-control pointer-choice fa fa-minus-square" data-type="panic" data-value="-1"></i>
|
<i class="addsub-control pointer-choice fa fa-minus-square" data-type="panic" data-value="-1"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<strong>{{localize 'l5r5e.army.battle_readiness.panic'}}</strong>
|
<strong>{{localize 'l5r5e.army.battle_readiness.panic'}}</strong>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label class="attribute-label-discipline">
|
<label class="attribute-label discipline">
|
||||||
<input name="system.battle_readiness.panic_discipline.max" type="number" value="{{data.system.battle_readiness.panic_discipline.max}}" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input name="system.battle_readiness.panic_discipline.max" type="number" value="{{data.system.battle_readiness.panic_discipline.max}}" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
</label>
|
</label>
|
||||||
<strong>{{localize 'l5r5e.army.battle_readiness.discipline'}}</strong>
|
<strong>{{localize 'l5r5e.army.battle_readiness.discipline'}}</strong>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<label class="attribute-label">
|
<label class="attribute-label">
|
||||||
<strong>{{localize 'l5r5e.attributes.fatigue'}}</strong>
|
<strong>{{localize 'l5r5e.attributes.fatigue'}}</strong>
|
||||||
<input class="centered-input select-on-focus" type="number" name="system.fatigue.value" value="{{data.system.fatigue.value}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input class="centered-input select-on-focus" type="number" name="system.fatigue.value" value="{{data.system.fatigue.value}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
<span class="attributes-buttons">
|
<span class="increment-control fatigue">
|
||||||
<i class="addsub-control pointer-choice fa fa-plus-square" data-type="fatigue" data-value="1"></i>
|
<i class="addsub-control pointer-choice fa fa-plus-square" data-type="fatigue" data-value="1"></i>
|
||||||
<i class="addsub-control pointer-choice fa fa-minus-square" data-type="fatigue" data-value="-1"></i>
|
<i class="addsub-control pointer-choice fa fa-minus-square" data-type="fatigue" data-value="-1"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<label class="attribute-label">
|
<label class="attribute-label">
|
||||||
<strong>{{localize 'l5r5e.attributes.strife'}}</strong>
|
<strong>{{localize 'l5r5e.attributes.strife'}}</strong>
|
||||||
<input class="centered-input select-on-focus" type="number" name="system.strife.value" value="{{data.system.strife.value}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input class="centered-input select-on-focus" type="number" name="system.strife.value" value="{{data.system.strife.value}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
<span class="attributes-buttons">
|
<span class="increment-control strife">
|
||||||
<i class="addsub-control pointer-choice fa fa-plus-square" data-type="strife" data-value="1"></i>
|
<i class="addsub-control pointer-choice fa fa-plus-square" data-type="strife" data-value="1"></i>
|
||||||
<i class="addsub-control pointer-choice fa fa-minus-square" data-type="strife" data-value="-1"></i>
|
<i class="addsub-control pointer-choice fa fa-minus-square" data-type="strife" data-value="-1"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -36,10 +36,10 @@
|
|||||||
</label>
|
</label>
|
||||||
<p class="item-description"> {{localize 'l5r5e.attributes.focustip'}}</p>
|
<p class="item-description"> {{localize 'l5r5e.attributes.focustip'}}</p>
|
||||||
</li>
|
</li>
|
||||||
<li class="vigilance-content {{#if data.system.is_compromised}}compromised{{/if}}">
|
<li class="vigilance-content {{#if data.system.is_afflicted_or_compromised}}compromised{{/if}}">
|
||||||
<label class="attribute-label">
|
<label class="attribute-label">
|
||||||
<strong>{{localize 'l5r5e.attributes.vigilance'}}</strong>
|
<strong>{{localize 'l5r5e.attributes.vigilance'}}</strong>
|
||||||
{{#if data.system.is_compromised}}
|
{{#if data.system.is_afflicted_or_compromised}}
|
||||||
<input class="centered-input" type="number" value="1" disabled/>
|
<input class="centered-input" type="number" value="1" disabled/>
|
||||||
{{else}}
|
{{else}}
|
||||||
<input class="centered-input" type="number" name="system.vigilance" value="{{data.system.vigilance}}" data-dtype="Number" disabled/>
|
<input class="centered-input" type="number" name="system.vigilance" value="{{data.system.vigilance}}" data-dtype="Number" disabled/>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<legend class="section-header">
|
<legend class="section-header">
|
||||||
{{localize 'l5r5e.conflict.initiative.title'}}
|
{{localize 'l5r5e.conflict.initiative.title'}}
|
||||||
<a class="encounter prepared-control" data-id="{{data.type}}">
|
<a class="encounter prepared-control" data-id="{{data.type}}">
|
||||||
<i class="fa fas prepared-icon prepared-icon-{{data.system.prepared}} prepared-{{data.type}}" title="{{localize (localize 'l5r5e.conflict.initiative.prepared_{value}' value=data.system.prepared)}}"></i>
|
<i class="fa fas prepared-icon prepared-icon-{{data.system.prepared}} prepared-{{data.type}}" title="{{localizeEmbedded 'l5r5e.conflict.initiative.prepared_{value}' value=data.system.prepared}}"></i>
|
||||||
</a>
|
</a>
|
||||||
</legend>
|
</legend>
|
||||||
<button class="initiative dice-picker" data-initiative="true" data-skill="sentiment">{{localize 'l5r5e.conflict.initiative.intrigue'}}</button>
|
<button class="initiative dice-picker" data-initiative="true" data-skill="sentiment">{{localize 'l5r5e.conflict.initiative.intrigue'}}</button>
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
{{#each actor.effects as |effect|}}
|
{{#each actor.effects as |effect|}}
|
||||||
<li class="effect-container" title="{{name}}">
|
<li class="effect-container" title="{{name}}">
|
||||||
<div class="effect-icon" style="background-image: url({{effect.img}})"></div>
|
<div class="effect-icon" style="background-image: url({{effect.img}})"></div>
|
||||||
<div class="effect-name"><label>{{name}}</label></div>
|
<div data-effect-id="{{effect.id}}" class="effect-name"><label>{{name}}</label></div>
|
||||||
|
{{#if ../data.editable_not_soft_locked}}
|
||||||
|
<div data-effect-id="{{effect.id}}" class="effect-delete" title="{{localize 'Delete'}}"><i class="fas fa-remove"></i></div>
|
||||||
|
{{/if}}
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
<label class="attribute-label">
|
<label class="attribute-label">
|
||||||
{{localize 'l5r5e.advancements.total'}}
|
{{localize 'l5r5e.advancements.total'}}
|
||||||
<input class="centered-input select-on-focus" type="number" name="system.xp_total" value="{{data.system.xp_total}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input class="centered-input select-on-focus" type="number" name="system.xp_total" value="{{data.system.xp_total}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
|
<span class="increment-control xp">
|
||||||
|
<i class="xp-control pointer-choice fa fa-plus-square" data-type="xp" data-value="1"></i>
|
||||||
|
<i class="xp-control pointer-choice fa fa-minus-square" data-type="xp" data-value="-1"></i>
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="attribute-label">
|
<label class="attribute-label">
|
||||||
{{localize 'l5r5e.advancements.spent'}}
|
{{localize 'l5r5e.advancements.spent'}}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
<fieldset class="money money-wrapper">
|
<fieldset class="money money-wrapper">
|
||||||
<legend class="section-header">{{localize 'l5r5e.money.title'}}</legend>
|
<legend class="section-header">{{localize 'l5r5e.money.title'}}</legend>
|
||||||
<label>
|
<label class="attribute-label money">
|
||||||
{{localize 'l5r5e.money.koku'}}
|
{{localize 'l5r5e.money.koku'}}
|
||||||
<input name="system.money.koku" type="number" value="{{data.system.money.koku}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input name="system.money.koku" type="number" value="{{data.system.money.koku}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
<span class="money-buttons">
|
<span class="increment-control money">
|
||||||
<i class="money-control pointer-choice fa fa-plus-square" data-type="koku" data-value="1"></i>
|
<i class="money-control pointer-choice fa fa-plus-square" data-type="koku" data-value="1"></i>
|
||||||
<i class="money-control pointer-choice fa fa-minus-square" data-type="koku" data-value="-1"></i>
|
<i class="money-control pointer-choice fa fa-minus-square" data-type="koku" data-value="-1"></i>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label class="attribute-label money">
|
||||||
{{localize 'l5r5e.money.bu'}}
|
{{localize 'l5r5e.money.bu'}}
|
||||||
<input name="system.money.bu" type="number" value="{{data.system.money.bu}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input name="system.money.bu" type="number" value="{{data.system.money.bu}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
<span class="money-buttons">
|
<span class="increment-control money">
|
||||||
<i class="money-control pointer-choice fa fa-plus-square" data-type="bu" data-value="1"></i>
|
<i class="money-control pointer-choice fa fa-plus-square" data-type="bu" data-value="1"></i>
|
||||||
<i class="money-control pointer-choice fa fa-minus-square" data-type="bu" data-value="-1"></i>
|
<i class="money-control pointer-choice fa fa-minus-square" data-type="bu" data-value="-1"></i>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label class="attribute-label money">
|
||||||
{{localize 'l5r5e.money.zeni'}}
|
{{localize 'l5r5e.money.zeni'}}
|
||||||
<input name="system.money.zeni" type="number" value="{{data.system.money.zeni}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
<input name="system.money.zeni" type="number" value="{{data.system.money.zeni}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
|
||||||
<span class="money-buttons">
|
<span class="increment-control money">
|
||||||
<i class="money-control pointer-choice fa fa-plus-square" data-type="zeni" data-value="1"></i>
|
<i class="money-control pointer-choice fa fa-plus-square" data-type="zeni" data-value="1"></i>
|
||||||
<i class="money-control pointer-choice fa fa-minus-square" data-type="zeni" data-value="-1"></i>
|
<i class="money-control pointer-choice fa fa-minus-square" data-type="zeni" data-value="-1"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||