Compare commits

..

51 Commits

Author SHA1 Message Date
Mandar
5cf4401fa3 1.2.1 - Praised be Firefox 2021-02-18 02:23:38 +01:00
Mandar
69e96b6610 Fix dice swap and items on firefox 2021-02-18 02:10:48 +01:00
Mandar
87b54ac3ef 1.2.0 - Roll n Keep 2021-02-17 12:45:28 +01:00
Mandar
08c0059ca5 Add line for mark dice process in RnK 2021-02-16 19:42:43 +01:00
Mandar
bc615d05fa Fix TN position 2021-02-16 18:34:56 +01:00
Mandar
da0bdf453e Change npc skill position + fix profile img on rnk 2021-02-16 18:08:25 +01:00
Mandar
dfef6d4092 Fix rnk header html & css 2021-02-16 16:56:25 +01:00
Mandar
5ffaa062fc Fix npc tabs and Roll Button display 2021-02-16 16:02:49 +01:00
Mandar
14b47a5381 Add tabs on Npc sheets 2021-02-16 15:43:18 +01:00
Vlyan
d213d179dc Added some images for clan shuji 2021-02-16 09:13:54 +01:00
Vlyan
42708d54e8 No RnK button if regular dice are also in the roll 2021-02-16 09:13:54 +01:00
Mandar
386a62ef55 Fix gm toolbox 2021-02-15 19:47:13 +01:00
Mandar
babbafffef Html + css Rnk adjustement for flex content 2021-02-14 23:19:06 +01:00
Mandar
3eb623120a New Item liste style 2021-02-14 23:18:07 +01:00
Vlyan
d2f9b6891e Prepared: reverse on right-click 2021-02-13 17:48:16 +01:00
Vlyan
a00fa80607 Fix for undefined skillAssistance 2021-02-13 17:29:14 +01:00
Vlyan
95d031c7a8 Add an option to set the TN to 1 when the encounter type is selected (Intrigue, Duel, Skirmish or Mass battle) 2021-02-13 16:14:06 +01:00
Vlyan
cb0696f662 Split Items by category in actor sheet (pc & npc) for better readability
Fix actor fromData
2021-02-13 14:31:12 +01:00
Vlyan
db5d5b62aa Better management for School Techniques
Gray color for hidden TN result in chat
Removed unused "techniques.list" props in template
2021-02-13 13:23:02 +01:00
Vlyan
6b322be3f4 Added total success count on a hidden TN roll 2021-02-12 14:01:00 +01:00
Vlyan
48b17be458 RnK : Added ability to GM to undo the last choices 2021-02-10 17:41:35 +01:00
Vlyan
7d7d62f950 Breaking the message reference 2021-02-10 14:01:14 +01:00
Mandar
fa03e5152b Fix Gm Tools items width 2021-02-10 12:56:28 +01:00
Mandar
a823d89344 Fix chat header position 2021-02-10 00:11:33 +01:00
Vlyan
47eb508dd4 Added Skill assistance used indicator 2021-02-09 14:01:43 +01:00
Mandar
1cc496ad77 Style for new dice Rnk + technique fix 2021-02-09 13:54:00 +01:00
Vlyan
e0cb2bf235 Fix for Roll: If only skill dice, keepLimit = nb skill dice 2021-02-09 12:17:28 +01:00
Vlyan
8a22706ad2 Split techniques in actor sheet for better readability 2021-02-09 12:16:15 +01:00
Vlyan
0cb0a2f3ea Save and display initialFormula on chat 2021-02-08 19:19:15 +01:00
Vlyan
fc56afa28e fix difficulty path as Mandar do not report it :D 2021-02-06 13:00:17 +01:00
Vlyan
768fda1752 Tweaked reverse the token's bar on fatigue and colorize in red the strife bar if compromise 2021-02-06 12:49:24 +01:00
Mandar
694333a1cb Add style for chat result 2021-02-05 20:58:22 +01:00
Jzrzmy
b54f58c9ce Allow token fatigue bar reversing 2021-02-05 19:55:12 +00:00
Vlyan
6a9e84f126 Add skill assistance 2021-02-04 11:22:36 +01:00
Vlyan
84b160b50d Add Reroll and Swap visualisation in chatlog 2021-02-04 10:04:45 +01:00
Vlyan
78568ded55 changelog :D 2021-02-03 20:04:31 +01:00
Vlyan
b59fde880d RnK : Reroll add a extra step in RnK dialog
GmBx: Changed new BTs to "Sleep" & "Scene end" actions (more used)
2021-02-03 20:01:57 +01:00
Vlyan
7969c5f526 RnK : Reroll add a extra step 2021-02-03 19:41:39 +01:00
Vlyan
ca5a164f7f Add confirm on item deletion in readme 2021-02-03 12:27:36 +01:00
Vlyan
45ae96724e Add confirm on item deletion 2021-02-03 11:44:22 +01:00
Vlyan
7f987a7682 Some test on gm toolbox 2021-02-02 18:27:36 +01:00
Vlyan
d88d985575 20q Disable the generate button once clicked 2021-02-02 15:32:03 +01:00
Vlyan
9f8a24bd68 forgot a little test 2021-02-02 15:25:48 +01:00
Vlyan
4e629efe12 Added a booster for loading compendium's core items (speed up 20q) 2021-02-02 15:13:42 +01:00
Vlyan
e542dd390b Click on rings in the PC/PNC sheet now open the DicePicker 2021-02-02 12:28:14 +01:00
Vlyan
a517a92488 some renaming stuff 2021-02-02 12:09:11 +01:00
Vlyan
30da4f3a02 RnK French Translation and Minors fixes 2021-02-02 10:51:15 +01:00
Vlyan
931a28fbfe -RnK dialog auto-open after a roll
-Move for some roll props
-2 RnK bugfix (TN & Reroll)
2021-02-02 09:28:46 +01:00
Vlyan
4350506e09 Fix image's behavior on create for all items sub classes 2021-02-01 17:51:27 +01:00
Vlyan
68577737fc RnK with some bugs 2021-02-01 16:27:57 +01:00
Vlyan
30f71e31cd working on RnK 2021-01-30 16:43:57 +01:00
55 changed files with 2074 additions and 648 deletions

View File

@@ -1,5 +1,37 @@
# Changelog
## 1.2.1 - Praised be Firefox
- Fix dice swap on firefox that overflowed on the top and bottom of the RnK dialog
- Fix new items list on firefox who deformed the sheets
## 1.2.0 - Roll n Keep
- Added Roll n Keep 1st iteration !
- Ability to Keep, Discard, Re-roll and Swap:
- Keep: Keep the die for the next step, if it's an explosive one, automatically roll a new die
- Discard: Self explain, do not keep this die for the next step.
- Re-roll: Replace this die by a new roll (Usually Advantage & Disadvantage stuff). When a reroll is selected, all the dice in the current step will be tag as keep by default.
- Swap (Face): Set a desired face for this die (Some weird techniques stuff)
- Usage:
- All these actions are done by drag and drop a die result into a target action
- A colored icon symbolize the choice made on the dice
- You can always change choices for the current step until you clic next
- Please note all dice without choice will be discarded for the next step
- The GM has the ability to undo choices by left-clicking in the status headers
- Limitation: The roll need to only have L5R dice in it (no mixed regular + L5R)
- Fix image's behavior on create for all items sub classes
- Click on rings in the PC/PNC sheet now open the DicePicker with the selected ring
- Added a booster for loading compendium's core items (speed up 20Q)
- Added confirm dialog on item's deletion (Hold "ctrl" on the click, if you want to bypass it)
- Added "Sleep" & "Scene End" buttons in "GM ToolBox" (old "difficulty" box)
- Token's bar:
- The strife bar is now displayed in red if the actor is compromised
- Added an option, off by default, to reverse the fatigue's token bar (thanks to Jzrzmy)
- Added an option, on by default, to set the TN to 1 when the encounter type is selected (Intrigue, Duel, Skirmish or Mass battle)
- Split Techniques & Items by category in actor sheet (pc & npc) for better readability
- Armor & Weapon added in the conflict tab now set the "equipped" property by default
- Added Tabs on NPC sheets
- New styles for dice results
## 1.1.2 - One Compendium to bring them all
- Added compendiums (Thanks to Stéfano Fara for the English version !) Partial for French as PoW and CR are not translated yet
- Shadowlands

View File

@@ -67,7 +67,7 @@
},
{
"id": "Robes of Judgment",
"name": "Robes of Judgment (WIP)",
"name": "Robes de jugement",
"description": "",
"book_reference": "Les Royaumes Célestes p.97"
},

View File

@@ -4,7 +4,16 @@
"Maintainers": ["Team L5R"]
},
"SETTINGS": {
"None": "No option"
"None": "No option",
"ReverseFatigueBar": "Reverse token fatigue bar",
"RollNKeep": {
"DeleteOldMessage": "RnK Delete previous chat message",
"DeleteOldMessageHint": "Choose to keep or delete the previous message for a RnK series"
},
"Initiative": {
"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)"
}
},
"ACTOR": {
"TypeCharacter": "Player Character",
@@ -21,10 +30,9 @@
},
"l5r5e": {
"global": {
"ok": "OK",
"add": "Add",
"edit": "Edit",
"delete": "Delete",
"delete_confirm": "Are you sure you want to delete '{name}' ?",
"drop_here": "Drop here",
"edge_translation_disclaimer": ""
},
@@ -83,14 +91,19 @@
"difficulty_hidden": "TN ???",
"dicepicker": "Dice Picker",
"void_point_used": "Void point used",
"assistance_used": "Skill assistance used",
"roll_n_keep": "Roll & Keep",
"initiative_roll": "Initiative roll"
"initiative_roll": "Initiative roll",
"success_text": "Success!",
"bonus_text": "bonus successes",
"fail_text": "Fail!"
},
"dicepicker": {
"difficulty_title": "Difficulty",
"difficulty_hidden_label": "Hide TN",
"use_void_point_label": "Spend a",
"void_point_tooltip": "Void point",
"skill_assistance_label": "Assistance",
"roll_label": "Roll",
"bt_add_macro": "Add a macro"
},
@@ -98,10 +111,19 @@
"title": "L5R Roll & Keep",
"discard_drop_here": "Discard",
"reroll_drop_here": "Re-roll",
"reroll_chat": "Re-rolled dice",
"swap_drop_here": "Swap",
"keep_drop_here": "Keep",
"keep_chat": "New roll from an exploding die",
"bt_validate": "Finalize"
"bt_validate": "Finalize this step",
"undo": "[GM] Undo the last step choices"
},
"gm_toolbox": {
"title": "GM ToolBox",
"difficulty_hidden": "Change difficulty visibility",
"difficulty": "Change difficulty (right: add, left: subtract, middle: TN 2)",
"sleep": "Comfortable rest for all characters (Remove Water x2 fatigue)",
"sleep_info": "The characters had a good night's sleep.",
"scene_end": "End of scene (Conflict and Fatigue half reset for all characters)",
"scene_end_info": "The tension of the scene finally drops."
},
"max": "Max",
"current": "Current",

View File

@@ -4,7 +4,16 @@
"Maintainers": ["Team L5R"]
},
"SETTINGS": {
"None": "Sin opción"
"None": "Sin opción",
"ReverseFatigueBar": "Invertir la barra de fatiga de los token",
"RollNKeep": {
"DeleteOldMessage": "RnK Delete previous chat message",
"DeleteOldMessageHint": "Choose to keep or delete the previous message for a RnK series"
},
"Initiative": {
"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)"
}
},
"ACTOR": {
"TypeCharacter": "Personaje jugador",
@@ -21,10 +30,9 @@
},
"l5r5e": {
"global": {
"ok": "OK",
"add": "Añadir",
"edit": "Editar",
"delete": "Borrar",
"delete_confirm": "Are you sure you want to delete '{name}' ?",
"drop_here": "Dejar caer aquí",
"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."
},
@@ -83,14 +91,19 @@
"difficulty_hidden": "NO ???",
"dicepicker": "Dice Picker",
"void_point_used": "Punto de vacío utilizado",
"assistance_used": "Skill assistance used",
"roll_n_keep": "Tirar y guardar",
"initiative_roll": "Tirada de Iniciativa"
"initiative_roll": "Tirada de Iniciativa",
"success_text": "Éxitos!",
"bonus_text": "bonus successes",
"fail_text": "Fail!"
},
"dicepicker": {
"difficulty_title": "Dificultad",
"difficulty_hidden_label": "Ocultar NO",
"use_void_point_label": "Gasta un",
"void_point_tooltip": "Punto de vacío",
"skill_assistance_label": "Assistance",
"roll_label": "Tirar",
"bt_add_macro": "Añadir una macro"
},
@@ -98,10 +111,19 @@
"title": "L5R Roll & Keep",
"discard_drop_here": "Descartar",
"reroll_drop_here": "Relanzar",
"reroll_chat": "Dados relanzados",
"swap_drop_here": "Swap",
"keep_drop_here": "Guardar",
"keep_chat": "Nueva tirada de un dado relanzable",
"bt_validate": "Finalizar"
"bt_validate": "Finalizar",
"undo": "[GM] Undo the last step choices"
},
"gm_toolbox": {
"title": "GM ToolBox",
"difficulty_hidden": "Change difficulty visibility",
"difficulty": "Change difficulty (right: add, left: subtract, middle: TN 2)",
"sleep": "Comfortable rest for all characters (Remove Water x2 fatigue)",
"sleep_info": "The characters had a good night's sleep.",
"scene_end": "End of scene (Conflict and Fatigue half reset for all characters)",
"scene_end_info": "The tension of the scene finally drops."
},
"max": "Máx",
"current": "Actuales",

View File

@@ -4,7 +4,16 @@
"Maintainers": ["Team L5R"]
},
"SETTINGS": {
"None": "Aucune option"
"None": "Aucune option",
"ReverseFatigueBar": "Inverser la barre de fatigue des token",
"RollNKeep": {
"DeleteOldMessage": "RnK Supprime le précédent message du chat",
"DeleteOldMessageHint": "Si coché, on supprime le message précédent pour la série de jets via le RnK"
},
"Initiative": {
"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)"
}
},
"ACTOR": {
"TypeCharacter": "Personnage Joueur",
@@ -21,10 +30,9 @@
},
"l5r5e": {
"global": {
"ok": "OK",
"add": "Ajouter",
"edit": "Modifier",
"delete": "Supprimer",
"delete_confirm": "Etes-vous sûr de vouloir supprimer '{name}' ?",
"drop_here": "Déposez ici",
"edge_translation_disclaimer": ""
},
@@ -83,25 +91,39 @@
"difficulty_hidden": "ND ???",
"dicepicker": "Dice Picker",
"void_point_used": "Point de vide utilisé",
"assistance_used": "Assistance de compétence utilisée",
"roll_n_keep": "Roll & Keep",
"initiative_roll": "Jet d'initiative"
"initiative_roll": "Jet d'initiative",
"success_text": "Succès !",
"bonus_text": "succès bonus",
"fail_text": "Échec !"
},
"dicepicker": {
"difficulty_title": "Difficulté",
"difficulty_hidden_label": "ND Caché",
"use_void_point_label": "Dépenser un",
"void_point_tooltip": "Point de Vide",
"skill_assistance_label": "Assistance",
"roll_label": "Lancer",
"bt_add_macro": "Ajouter une macro"
},
"roll_n_keep": {
"title": "L5R Roll & Keep",
"discard_drop_here": "Discard",
"reroll_drop_here": "Re-roll",
"reroll_chat": "Re-rolled dice",
"keep_drop_here": "Keep",
"keep_chat": "New roll from a exploding dice",
"bt_validate": "Finalize"
"discard_drop_here": "Abandonner",
"reroll_drop_here": "Relancer",
"swap_drop_here": "Modifier",
"keep_drop_here": "Garder",
"bt_validate": "Finaliser cette étape",
"undo": "[GM] Annuler les choix de la dernière étape"
},
"gm_toolbox": {
"title": "GM ToolBox",
"difficulty_hidden": "Modifier la visibilité de la difficulté",
"difficulty": "Modifier la difficulté (droite: ajout, gauche: soustraction, milieu: ND 2)",
"sleep": "Repos confortable pour tous les personnages (Enlève Eau x2 de fatigue)",
"sleep_info": "Les personnages ont passé une bonne nuit de sommeil.",
"scene_end": "Fin de scène (Conflit et Fatigue à moitié pour tous les personnages dont la valeur dépasse ce seuil)",
"scene_end_info": "La tension de la scène retombe enfin"
},
"max": "Max",
"current": "Actuel",

View File

@@ -32,14 +32,14 @@
{"_id":"L5RCoreShu000032","name":"Regal Bearing","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"4","bought_at_rank":0,"ring":"water","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.220"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000033","name":"Well of Desire","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"1","bought_at_rank":0,"ring":"water","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.221"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000034","name":"Shallow Waters","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"1","bought_at_rank":0,"ring":"water","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.220"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000035","name":"Lady Shinjos Speed (Unicorn)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.222"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000036","name":"Lady Dojis Decree (Crane)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.222"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000035","name":"Lady Shinjos Speed (Unicorn)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.222"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/clans/unicorn.svg","effects":[]}
{"_id":"L5RCoreShu000036","name":"Lady Dojis Decree (Crane)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.222"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/clans/crane.svg","effects":[]}
{"_id":"L5RCoreShu000037","name":"Courtiers Resolve","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"1","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.222"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000038","name":"A Samurais Fate","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"4","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.222"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000039","name":"Lord Bayushis Whispers (Scorpion)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.223"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000040","name":"Lord Togashis Insight (Dragon)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.223"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000039","name":"Lord Bayushis Whispers (Scorpion)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.223"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/clans/scorpion.svg","effects":[]}
{"_id":"L5RCoreShu000040","name":"Lord Togashis Insight (Dragon)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.223"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/clans/dragon.svg","effects":[]}
{"_id":"L5RCoreShu000041","name":"Rouse the Soul","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"5","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.223"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000042","name":"Lord Akodos Roar (Lion)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.223"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000042","name":"Lord Akodos Roar (Lion)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"2","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.223"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/clans/lion.svg","effects":[]}
{"_id":"L5RCoreShu000043","name":"All Arts Are One","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"3","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Core Rulebook p.222"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000044","name":"Hidden in Smoke","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"4","bought_at_rank":0,"ring":"air","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Court of Stones p.117"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000045","name":"Assess Strengths","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"1","bought_at_rank":0,"ring":"air","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Court of Stones p.117"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
@@ -52,7 +52,7 @@
{"_id":"L5RCoreShu000052","name":"Fun and Games","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"1","bought_at_rank":0,"ring":"water","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Court of Stones p.119"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000053","name":"Foreseen Need","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"4","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Court of Stones p.119"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000054","name":"Spin the Web (Kolat)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"3","bought_at_rank":0,"ring":"air","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Emerald Empire p.248"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000055","name":"Awe of Heaven (Imperial)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"4","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Emerald Empire p.248"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000055","name":"Awe of Heaven (Imperial)","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"4","bought_at_rank":0,"ring":"void","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Emerald Empire p.248"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/clans/imperial.svg","effects":[]}
{"_id":"L5RCoreShu000056","name":"Look Out","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"3","bought_at_rank":0,"ring":"earth","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Path of Waves p.92"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000057","name":"Bellow of Resolve","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"1","bought_at_rank":0,"ring":"earth","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Path of Waves p.92"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}
{"_id":"L5RCoreShu000058","name":"Ruse of the Moon's Reflection","permission":{"default":0},"type":"technique","data":{"in_curriculum":false,"xp_used":0,"rank":"5","bought_at_rank":0,"ring":"air","technique_type":"shuji","xp_cost":"3","description":"","book_reference":"Path of Waves p.93"},"sort":100001,"flags":{},"img":"systems/l5r5e/assets/icons/techs/shuji.svg","effects":[]}

View File

@@ -12,7 +12,9 @@ export class ActorL5r5e extends Actor {
// }
// Replace default image
data.img = `${CONFIG.l5r5e.paths.assets}icons/actors/${data.type}.svg`;
if (data.img === undefined) {
data.img = `${CONFIG.l5r5e.paths.assets}icons/actors/${data.type}.svg`;
}
// Some tweak on actors token
data.token = data.token || {};

View File

@@ -27,9 +27,81 @@ export class BaseSheetL5r5e extends ActorSheet {
sheetData.data.stances = CONFIG.l5r5e.stances;
sheetData.data.techniquesList = CONFIG.l5r5e.techniques;
// Sort Items by name
sheetData.items.sort((a, b) => {
return a.name.localeCompare(b.name);
});
// Split Techniques by types
sheetData.data.splitTechniquesList = this._splitTechniques(sheetData);
// Split Items by types
sheetData.data.splitItemsList = this._splitItems(sheetData);
return sheetData;
}
/**
* Split Techniques by types for better readability
* @private
*/
_splitTechniques(sheetData) {
const out = {};
// Build the list order
[...CONFIG.l5r5e.techniques, ...CONFIG.l5r5e.techniques_school].forEach((tech) => {
out[tech] = [];
});
// Add tech the character knows
sheetData.items.forEach((item) => {
if (item.type === "technique") {
out[item.data.technique_type].push(item);
}
});
// Remove unused techs
Object.keys(out).forEach((tech) => {
if (
out[tech].length < 1 &&
!sheetData.data.techniques[tech] &&
!CONFIG.l5r5e.techniques_school.includes(tech)
) {
delete out[tech];
}
});
// Manage school add button
sheetData.data.techniques["school_ability"] = out["school_ability"].length === 0;
sheetData.data.techniques["mastery_ability"] = out["mastery_ability"].length === 0;
// Always display "school_ability", but display "mastery_ability" only if rank >= 5
if (sheetData.data.identity?.school_rank < 5 && out["mastery_ability"].length === 0) {
delete out["mastery_ability"];
}
return out;
}
/**
* Split Items by types for better readability
* @private
*/
_splitItems(sheetData) {
const out = {
weapon: [],
armor: [],
item: [],
};
sheetData.items.forEach((item) => {
if (["item", "armor", "weapon"].includes(item.type)) {
out[item.type].push(item);
}
});
return out;
}
/**
* Return a light sheet if in "limited" state
* @override
@@ -71,6 +143,7 @@ export class BaseSheetL5r5e extends ActorSheet {
/**
* Handle dropped data on the Actor sheet
* @param {Event} event
*/
async _onDrop(event) {
// *** Everything below here is only needed if the sheet is editable ***
@@ -185,6 +258,7 @@ export class BaseSheetL5r5e extends ActorSheet {
}
new game.l5r5e.DicePickerDialog({
ringId: li.data("ring") || null,
skillId: skillId,
skillCatId: li.data("skillcat") || null,
isInitiativeRoll: li.data("initiative") || false,
@@ -207,28 +281,12 @@ export class BaseSheetL5r5e extends ActorSheet {
});
// Equipped / Readied
html.find(".equip-readied-control").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
this._switchEquipReadied(event);
});
html.find(".equip-readied-control").on("click", this._switchEquipReadied.bind(this));
// *** Items : add, edit, delete ***
html.find(".item-add").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
this._addSubItem(event);
});
html.find(`.item-edit`).on("click", (event) => {
event.preventDefault();
event.stopPropagation();
this._editSubItem(event);
});
html.find(`.item-delete`).on("click", (event) => {
event.preventDefault();
event.stopPropagation();
this._deleteSubItem(event);
});
html.find(".item-add").on("click", this._addSubItem.bind(this));
html.find(`.item-edit`).on("click", this._editSubItem.bind(this));
html.find(`.item-delete`).on("click", this._deleteSubItem.bind(this));
}
/**
@@ -247,9 +305,13 @@ export class BaseSheetL5r5e extends ActorSheet {
/**
* Add a generic item with sub type
* @param {Event} event
* @private
*/
async _addSubItem(event) {
event.preventDefault();
event.stopPropagation();
const type = $(event.currentTarget).data("item-type");
const titles = {
item: "l5r5e.items.title_new",
@@ -262,7 +324,7 @@ export class BaseSheetL5r5e extends ActorSheet {
const created = await this.actor.createEmbeddedEntity("OwnedItem", {
name: game.i18n.localize(titles[type]),
type: type,
img: "icons/svg/mystery-man.svg",
img: `${CONFIG.l5r5e.paths.assets}icons/items/${type}.svg`,
});
const item = this.actor.getOwnedItem(created._id);
@@ -272,14 +334,37 @@ export class BaseSheetL5r5e extends ActorSheet {
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
}
switch (item.data.type) {
case "armor": // no break
case "weapon":
if ($(event.currentTarget).data("item-eqquiped")) {
item.data.data.equipped = true;
}
break;
case "technique": {
// If technique, select the current type
const techType = $(event.currentTarget).data("tech-type");
if ([...CONFIG.l5r5e.techniques, ...CONFIG.l5r5e.techniques_school].includes(techType)) {
item.data.data.technique_type = techType;
item.data.img = `${CONFIG.l5r5e.paths.assets}/icons/techs/${techType}.svg`;
}
break;
}
}
item.sheet.render(true);
}
/**
* Edit a generic item with sub type
* @param {Event} event
* @private
*/
_editSubItem(event) {
event.preventDefault();
event.stopPropagation();
const itemId = $(event.currentTarget).data("item-id");
const item = this.actor.getOwnedItem(itemId);
item.sheet.render(true);
@@ -287,9 +372,13 @@ export class BaseSheetL5r5e extends ActorSheet {
/**
* Delete a generic item with sub type
* @param {Event} event
* @private
*/
_deleteSubItem(event) {
event.preventDefault();
event.stopPropagation();
const itemId = $(event.currentTarget).data("item-id");
// Remove 1 qty if possible
@@ -298,32 +387,48 @@ export class BaseSheetL5r5e extends ActorSheet {
return;
}
// Specific advancements, remove 1 to selected ring/skill
if (tmpItem.type === "advancement") {
const actor = duplicate(this.actor.data.data);
const itmData = tmpItem.data.data;
if (itmData.advancement_type === "ring") {
// Ring
actor.rings[itmData.ring] = Math.max(1, actor.rings[itmData.ring] - 1);
} else {
// Skill
const skillCatId = CONFIG.l5r5e.skills.get(itmData.skill);
if (skillCatId) {
actor.skills[skillCatId][itmData.skill] = Math.max(0, actor.skills[skillCatId][itmData.skill] - 1);
const callback = async () => {
// Specific advancements, remove 1 to selected ring/skill
if (tmpItem.type === "advancement") {
const actor = duplicate(this.actor.data.data);
const itmData = tmpItem.data.data;
if (itmData.advancement_type === "ring") {
// Ring
actor.rings[itmData.ring] = Math.max(1, actor.rings[itmData.ring] - 1);
} else {
// Skill
const skillCatId = CONFIG.l5r5e.skills.get(itmData.skill);
if (skillCatId) {
actor.skills[skillCatId][itmData.skill] = Math.max(
0,
actor.skills[skillCatId][itmData.skill] - 1
);
}
}
// Update Actor
this.actor.update({
data: diffObject(this.actor.data.data, actor),
});
}
// Update Actor
this.actor.update({
data: diffObject(this.actor.data.data, actor),
});
return this.actor.deleteOwnedItem(itemId);
};
// Holing Ctrl = without confirm
if (event.ctrlKey) {
return callback();
}
return this.actor.deleteOwnedItem(itemId);
game.l5r5e.HelpersL5r5e.confirmDeleteDialog(
game.i18n.format("l5r5e.global.delete_confirm", { name: tmpItem.name }),
callback
);
}
/**
* Switch "in_curriculum"
* @param {Event} event
* @private
*/
_switchSubItemCurriculum(event) {
@@ -358,9 +463,13 @@ export class BaseSheetL5r5e extends ActorSheet {
/**
* Switch Readied state on a weapon
* @param {Event} event
* @private
*/
_switchEquipReadied(event) {
event.preventDefault();
event.stopPropagation();
const type = $(event.currentTarget).data("type");
if (!["equipped", "readied"].includes(type)) {
return;

View File

@@ -49,11 +49,6 @@ export class CharacterSheetL5r5e extends BaseSheetL5r5e {
// Split Money
sheetData.data.money = this._zeniToMoney(this.actor.data.data.zeni);
// Sort Items by name
sheetData.items.sort((a, b) => {
return a.name.localeCompare(b.name);
});
// split advancements list by rank, and calculate xp spent
this._prepareAdvancement(sheetData);
sheetData.data.xp_saved = Math.floor(parseInt(sheetData.data.xp_total) - parseInt(sheetData.data.xp_spent));

View File

@@ -208,6 +208,7 @@ export class TwentyQuestionsDialog extends FormApplication {
html.find("#generate").on("click", async (event) => {
event.preventDefault();
event.stopPropagation();
$(event.currentTarget).prop("disabled", true);
await this.object.toActor(this.actor, flattenObject(this.cache));
await this.close({ submit: true, force: true });
});

View File

@@ -41,7 +41,7 @@ export class CombatL5r5e extends Combat {
// Get score for each combatant
const updatedCombatants = [];
ids.forEach((combatantId) => {
for (const combatantId of ids) {
const combatant = game.combat.combatants.find((c) => c._id === combatantId);
// Skip if combatant already have a initiative value
@@ -77,33 +77,49 @@ export class CombatL5r5e extends Combat {
formula = createFormula.join("+");
}
const roll = new game.l5r5e.RollL5r5e(formula);
roll.actor = combatant.actor;
roll.l5r5e.stance = data.stance;
roll.l5r5e.skillId = skillId;
roll.l5r5e.skillCatId = skillCat;
roll.l5r5e.summary.difficulty =
messageOptions.difficulty !== undefined ? messageOptions.difficulty : cfg.difficulty;
roll.l5r5e.summary.difficultyHidden =
messageOptions.difficultyHidden !== undefined
? messageOptions.difficultyHidden
: cfg.difficultyHidden;
roll.l5r5e.summary.voidPointUsed = !!messageOptions.useVoidPoint;
let roll;
let rnkMessage;
const flavor =
game.i18n.localize("l5r5e.chatdices.initiative_roll") +
" (" +
game.i18n.localize(`l5r5e.conflict.initiative.prepared_${isPrepared}`) +
")";
roll.roll();
roll.toMessage({
flavor:
game.i18n.localize("l5r5e.chatdices.initiative_roll") +
" (" +
game.i18n.localize(`l5r5e.conflict.initiative.prepared_${isPrepared}`) +
")",
});
if (messageOptions.rnkRoll instanceof game.l5r5e.RollL5r5e && ids.length === 1) {
// Specific RnK
roll = messageOptions.rnkRoll;
rnkMessage = await roll.toMessage({ flavor });
} else {
// Regular
roll = new game.l5r5e.RollL5r5e(formula);
roll.actor = combatant.actor;
roll.l5r5e.isInitiativeRoll = true;
roll.l5r5e.stance = data.stance;
roll.l5r5e.skillId = skillId;
roll.l5r5e.skillCatId = skillCat;
roll.l5r5e.difficulty =
messageOptions.difficulty !== undefined ? messageOptions.difficulty : cfg.difficulty;
roll.l5r5e.difficultyHidden =
messageOptions.difficultyHidden !== undefined
? messageOptions.difficultyHidden
: cfg.difficultyHidden;
roll.l5r5e.voidPointUsed = !!messageOptions.useVoidPoint;
roll.l5r5e.skillAssistance = messageOptions.skillAssistance || 0;
roll.roll();
rnkMessage = await roll.toMessage({ flavor });
}
// Ugly but work... i need the new message
if (ids.length === 1) {
combatant.actor.rnkMessage = rnkMessage;
}
// if the character succeeded on their Initiative check, they add 1 to their base initiative value,
// plus an additional amount equal to their bonus successes.
const successes = Math.min(roll.l5r5e.summary.ringsUsed, roll.l5r5e.summary.success);
if (successes >= roll.l5r5e.summary.difficulty) {
initiative = initiative + 1 + Math.max(successes - roll.l5r5e.summary.difficulty, 0);
const successes = roll.l5r5e.summary.totalSuccess;
if (successes >= roll.l5r5e.difficulty) {
initiative = initiative + 1 + Math.max(successes - roll.l5r5e.difficulty, 0);
}
}
@@ -111,7 +127,7 @@ export class CombatL5r5e extends Combat {
_id: combatant._id,
initiative: initiative,
});
});
}
// Update all combatants at once
await this.updateEmbeddedEntity("Combatant", updatedCombatants);

View File

@@ -27,14 +27,15 @@ export class DicePickerDialog extends FormApplication {
skill: {
id: "",
value: 0,
default_value: 0,
defaultValue: 0,
cat: "",
name: "",
assistance: 0,
},
difficulty: {
value: 2,
hidden: false,
add_void_point: false,
addVoidPoint: false,
},
useVoidPoint: false,
isInitiativeRoll: false,
@@ -50,8 +51,6 @@ export class DicePickerDialog extends FormApplication {
classes: ["l5r5e", "dice-picker-dialog"],
template: CONFIG.l5r5e.paths.templates + "dice/dice-picker-dialog.html",
title: "L5R Dice Roller",
width: 660,
height: 460,
actor: null,
ringId: null,
skillId: "",
@@ -184,6 +183,7 @@ export class DicePickerDialog extends FormApplication {
}
this.object.skill = {
...this.object.skill,
id: skillId.toLowerCase().trim(),
value: 0,
cat: "",
@@ -215,13 +215,13 @@ export class DicePickerDialog extends FormApplication {
switch (this._actor.data.type) {
case "character":
this.object.skill.value = this._actor.data.data.skills[skillCatId]?.[this.object.skill.id] || 0;
this.object.skill.default_value = this.object.skill.value;
this.object.skill.defaultValue = this.object.skill.value;
break;
case "npc":
// Skill value is in categories for npc
this.object.skill.value = this._actor.data.data.skills[skillCatId] || 0;
this.object.skill.default_value = this.object.skill.value;
this.object.skill.defaultValue = this.object.skill.value;
break;
}
}
@@ -249,7 +249,7 @@ export class DicePickerDialog extends FormApplication {
isHidden = true;
}
this.object.difficulty.hidden = !!isHidden;
this.object.difficulty.add_void_point = this.object.difficulty.hidden;
this.object.difficulty.addVoidPoint = this.object.difficulty.hidden;
this._updateVoidPointUsage();
}
@@ -274,7 +274,7 @@ export class DicePickerDialog extends FormApplication {
actor: this._actor,
actorIsPc: !this._actor || this._actor.data?.type === "character",
canUseVoidPoint:
this.object.difficulty.add_void_point || !this._actor || this._actor.data.data.void_points.value > 0,
this.object.difficulty.addVoidPoint || !this._actor || this._actor.data.data.void_points.value > 0,
disableSubmit: this.object.skill.value < 1 && this.object.ring.value < 1,
difficultyHiddenIsLock: this._difficultyHiddenIsLock,
};
@@ -323,11 +323,27 @@ export class DicePickerDialog extends FormApplication {
this.render(false);
});
// Skill assistance
html.find(".assistance").on("click", async (event) => {
event.preventDefault();
event.stopPropagation();
const assistanceAdd = $(event.currentTarget).data("value");
if (this.object.skill.assistance > 0 || assistanceAdd > 0) {
this._quantityChange("skill", assistanceAdd);
}
this.object.skill.assistance = Math.max(
Math.min(parseInt(this.object.skill.assistance) + assistanceAdd, 9),
0
);
this.render(false);
});
// Click on the Default Skill Dice
html.find("#skill_default_value").on("click", async (event) => {
event.preventDefault();
event.stopPropagation();
this.object.skill.value = this.object.skill.default_value;
this.object.skill.value = this.object.skill.defaultValue;
this.object.skill.assistance = 0;
this.render(false);
});
@@ -345,7 +361,7 @@ export class DicePickerDialog extends FormApplication {
event.preventDefault();
event.stopPropagation();
this.object.difficulty.hidden = !this.object.difficulty.hidden;
this.object.difficulty.add_void_point = this.object.difficulty.hidden;
this.object.difficulty.addVoidPoint = this.object.difficulty.hidden;
this._updateVoidPointUsage();
this.render(false);
});
@@ -354,7 +370,7 @@ export class DicePickerDialog extends FormApplication {
html.find("#diff_add_void_point").on("click", async (event) => {
event.preventDefault();
event.stopPropagation();
this.object.difficulty.add_void_point = !this.object.difficulty.add_void_point;
this.object.difficulty.addVoidPoint = !this.object.difficulty.addVoidPoint;
this._updateVoidPointUsage();
this.render(false);
});
@@ -391,7 +407,7 @@ export class DicePickerDialog extends FormApplication {
}
// If hidden add 1 void pt
if (this.object.difficulty.add_void_point) {
if (this.object.difficulty.addVoidPoint) {
actorData.void_points.value = Math.min(actorData.void_points.value + 1, actorData.void_points.max);
}
@@ -415,9 +431,10 @@ export class DicePickerDialog extends FormApplication {
formula.push(`${this.object.skill.value}ds`);
}
let message;
if (this.object.isInitiativeRoll) {
// Initiative roll
this._actor.rollInitiative({
await this._actor.rollInitiative({
initiativeOptions: {
formula: formula.join("+"),
// updateTurn: true,
@@ -426,9 +443,13 @@ export class DicePickerDialog extends FormApplication {
difficulty: this.object.difficulty.value,
difficultyHidden: this.object.difficulty.hidden,
useVoidPoint: this.object.useVoidPoint,
skillAssistance: this.object.skill.assistance,
},
},
});
// Adhesive tape to get the messageId :/
message = this._actor.rnkMessage;
delete this._actor.rnkMessage;
} else {
// Regular roll, so let's roll !
const roll = await new game.l5r5e.RollL5r5e(formula.join("+"));
@@ -437,12 +458,17 @@ export class DicePickerDialog extends FormApplication {
roll.l5r5e.stance = this.object.ring.id;
roll.l5r5e.skillId = this.object.skill.id;
roll.l5r5e.skillCatId = this.object.skill.cat;
roll.l5r5e.summary.difficulty = this.object.difficulty.value;
roll.l5r5e.summary.difficultyHidden = this.object.difficulty.hidden;
roll.l5r5e.summary.voidPointUsed = this.object.useVoidPoint;
roll.l5r5e.difficulty = this.object.difficulty.value;
roll.l5r5e.difficultyHidden = this.object.difficulty.hidden;
roll.l5r5e.voidPointUsed = this.object.useVoidPoint;
roll.l5r5e.skillAssistance = this.object.skill.assistance;
await roll.roll();
await roll.toMessage();
message = await roll.toMessage();
}
if (message) {
new game.l5r5e.RollnKeepDialog(message._id).render(true);
}
return this.close();
@@ -463,7 +489,7 @@ export class DicePickerDialog extends FormApplication {
_updateVoidPointUsage() {
if (
this.object.useVoidPoint &&
!this.object.difficulty.add_void_point &&
!this.object.difficulty.addVoidPoint &&
!!this._actor &&
this._actor.data.data.void_points.value < 1
) {

View File

@@ -18,8 +18,8 @@ export class AbilityDie extends L5rBaseDie {
8: { success: 1, explosive: 0, opportunity: 0, strife: 0, image: "skill_s" },
9: { success: 1, explosive: 0, opportunity: 0, strife: 0, image: "skill_s" },
10: { success: 1, explosive: 0, opportunity: 1, strife: 0, image: "skill_so" },
11: { success: 1, explosive: 1, opportunity: 0, strife: 1, image: "skill_et" },
12: { success: 1, explosive: 1, opportunity: 0, strife: 0, image: "skill_e" },
11: { success: 0, explosive: 1, opportunity: 0, strife: 1, image: "skill_et" },
12: { success: 0, explosive: 1, opportunity: 0, strife: 0, image: "skill_e" },
};
/** @override */

View File

@@ -14,6 +14,14 @@ export class L5rBaseDie extends DiceTerm {
this.l5r5e = { success: 0, explosive: 0, opportunity: 0, strife: 0 };
}
/**
* Return the total number of success + explosives
* @returns {number}
*/
get totalSuccess() {
return this.l5r5e.success + this.l5r5e.explosive;
}
/**
* Return a standardized representation for the displayed formula associated with this DiceTerm
* @override
@@ -64,13 +72,7 @@ export class L5rBaseDie extends DiceTerm {
this._evaluateModifiers();
// Combine all results
this.l5r5e = { success: 0, explosive: 0, opportunity: 0, strife: 0 };
this.results.forEach((term) => {
const face = this.constructor.FACES[term.result];
["success", "explosive", "opportunity", "strife"].forEach((props) => {
this.l5r5e[props] += parseInt(face[props]);
});
});
this.l5rSummary();
// Return the evaluated term
this._evaluated = true;
@@ -79,24 +81,36 @@ export class L5rBaseDie extends DiceTerm {
return this;
}
/**
* Summarise the total of success, strife... for L5R dices for the current Die
*/
l5rSummary() {
this.l5r5e = { success: 0, explosive: 0, opportunity: 0, strife: 0 };
this.results.forEach((term) => {
const face = this.constructor.FACES[term.result];
["success", "explosive", "opportunity", "strife"].forEach((props) => {
this.l5r5e[props] += parseInt(face[props]);
});
if (face.explosive) {
term.exploded = true;
}
});
}
/**
* Roll the DiceTerm by mapping a random uniform draw against the faces of the dice term
* @override
*/
roll(options) {
const roll = super.roll(options);
//roll.l5r5e = this.l5r5e;
return roll;
}
/** @override */
static fromData(data) {
const roll = super.fromData(data);
roll.l5r5e = data.l5r5e;
return roll;
}
@@ -106,9 +120,7 @@ export class L5rBaseDie extends DiceTerm {
*/
toJSON() {
const json = super.toJSON();
json.l5r5e = this.l5r5e;
return json;
}
}

View File

@@ -13,7 +13,7 @@ export class RingDie extends L5rBaseDie {
3: { success: 0, explosive: 0, opportunity: 1, strife: 0, image: "ring_o" },
4: { success: 1, explosive: 0, opportunity: 0, strife: 1, image: "ring_st" },
5: { success: 1, explosive: 0, opportunity: 0, strife: 0, image: "ring_s" },
6: { success: 1, explosive: 1, opportunity: 0, strife: 1, image: "ring_et" },
6: { success: 0, explosive: 1, opportunity: 0, strife: 1, image: "ring_et" },
};
/** @override */

View File

@@ -1,5 +1,5 @@
/**
* L5R Initiative Roll dialog
* L5R GM Toolbox dialog
* @extends {FormApplication}
*/
export class GmToolsDialog extends FormApplication {
@@ -19,12 +19,9 @@ export class GmToolsDialog extends FormApplication {
id: "l5r5e-gm-tools-dialog",
classes: ["l5r5e", "gm-tools-dialog"],
template: CONFIG.l5r5e.paths.templates + "dice/gm-tools-dialog.html",
title: game.i18n.localize("l5r5e.dicepicker.difficulty_title"),
width: 200, // ignored under 200px
height: 130, // ignored under 50px
scale: 0.5, // so scale /2 :D
left: x - 470,
top: y - 94,
title: game.i18n.localize("l5r5e.gm_toolbox.title"),
left: x - 512,
top: y - 98,
closeOnSubmit: false,
submitOnClose: false,
submitOnChange: true,
@@ -37,6 +34,25 @@ export class GmToolsDialog extends FormApplication {
*/
constructor(options = {}) {
super(options);
this._initialize();
}
/**
* Refresh data (used from socket)
*/
async refresh() {
if (!game.user.isGM) {
return;
}
this._initialize();
this.render(false);
}
/**
* Initialize the values
* @private
*/
_initialize() {
this.object = {
difficulty: game.settings.get("l5r5e", "initiative.difficulty.value"),
difficultyHidden: game.settings.get("l5r5e", "initiative.difficulty.hidden"),
@@ -67,6 +83,8 @@ export class GmToolsDialog extends FormApplication {
if (!game.user.isGM) {
return false;
}
this.position.width = "auto";
this.position.height = "auto";
return super.render(force, options);
}
@@ -104,6 +122,8 @@ export class GmToolsDialog extends FormApplication {
// Modify difficulty hidden
html.find(`.difficulty_hidden`).on("click", (event) => {
event.preventDefault();
event.stopPropagation();
this.object.difficultyHidden = !this.object.difficultyHidden;
game.settings
.set("l5r5e", "initiative.difficulty.hidden", this.object.difficultyHidden)
@@ -130,6 +150,13 @@ export class GmToolsDialog extends FormApplication {
}
game.settings.set("l5r5e", "initiative.difficulty.value", this.object.difficulty).then(() => this.submit());
});
// Scene End & Sleep
html.find(`.gm_actor_updates`).on("click", (event) => {
event.preventDefault();
event.stopPropagation();
this._updatesActors($(event.currentTarget).data("type"));
});
}
/**
@@ -140,15 +167,54 @@ export class GmToolsDialog extends FormApplication {
* @override
*/
async _updateObject(event, formData) {
// Notify the change to other players if they already have opened the DicePicker
game.l5r5e.sockets.refreshAppId("l5r5e-dice-picker-dialog");
// If the current GM also have the DP open
const app = Object.values(ui.windows).find((e) => e.id === "l5r5e-dice-picker-dialog");
if (app && typeof app.refresh === "function") {
app.refresh();
}
this.render(false);
}
/**
* Update all actors
* @param {string} type
* @private
*/
_updatesActors(type) {
if (!game.user.isGM) {
return;
}
game.actors.entities.forEach((actor) => {
switch (type) {
case "sleep":
// Remove 'water x2' fatigue points
actor.data.data.fatigue.value = Math.max(
0,
actor.data.data.fatigue.value - Math.ceil(actor.data.data.rings.water * 2)
);
break;
case "scene_end":
// If more than half the value => roundup half conflit & fatigue
actor.data.data.fatigue.value = Math.min(
actor.data.data.fatigue.value,
Math.ceil(actor.data.data.fatigue.max / 2)
);
actor.data.data.strife.value = Math.min(
actor.data.data.strife.value,
Math.ceil(actor.data.data.strife.max / 2)
);
break;
}
actor.update({
data: {
fatigue: {
value: actor.data.data.fatigue.value,
},
strife: {
value: actor.data.data.strife.value,
},
},
});
});
ui.notifications.info(game.i18n.localize(`l5r5e.gm_toolbox.${type}_info`));
}
}

View File

@@ -8,23 +8,35 @@ export class RollnKeepDialog extends FormApplication {
*/
static CHOICES = {
discard: "discard",
face_change: "face-change",
keep: "keep",
nothing: null,
reroll: "reroll",
reserve: "reserve",
// reserve: "reserve",
swap: "swap",
};
/**
* The current ChatMessage where we come from
* @param {ChatMessage} message
*/
message = null;
_message = null;
/**
* The current Roll
* @param {RollL5r5e} roll
*/
roll = null;
/**
* Payload Object
*/
object = {
currentStep: 0,
submitDisabled: false,
swapDiceFaces: {
rings: [],
skills: [],
},
dicesList: [[]],
};
@@ -38,59 +50,102 @@ export class RollnKeepDialog extends FormApplication {
classes: ["l5r5e", "roll-n-keep-dialog"],
template: CONFIG.l5r5e.paths.templates + "dice/roll-n-keep-dialog.html",
title: game.i18n.localize("l5r5e.roll_n_keep.title"),
width: 660,
height: 660,
closeOnSubmit: false,
});
}
/**
* Define a unique and dynamic element ID for the rendered ActorSheet application
* Define a unique and dynamic element ID for the rendered application
*/
get id() {
return `l5r5e-roll-n-keep-dialog-${this.message._id}`;
return `l5r5e-roll-n-keep-dialog-${this._message._id}`;
}
/**
* ChatMessage
* @param {ChatMessage} msg
*/
set message(msg) {
this._message = msg instanceof ChatMessage ? msg : null;
}
/**
* ChatMessage
* @returns {ChatMessage}
*/
get message() {
return this._message;
}
/**
* Create the Roll n Keep dialog
* @param {ChatMessage} message
* @param {number} messageId
* @param {FormApplicationOptions} options
*/
constructor(message, options = {}) {
constructor(messageId, options = {}) {
super({}, options);
this.message = message;
this._initialize();
this.message = game.messages.get(messageId);
this.options.editable =
this._message?.isAuthor || this._message?._roll.l5r5e.actor?.owner || this._message?.owner || false;
this._initializeDiceFaces();
this._initializeHistory();
}
/**
* Refresh data (used from socket)
*/
async refresh() {
if (!this.message) {
if (!this._message) {
return;
}
this._initialize();
this._initializeHistory();
this.render(false);
}
/**
* Initialize the dialog with the message
* Render
* @param {boolean} force
* @param {RenderOptions} options
* @returns {Application}
* @override
*/
render(force = null, options = {}) {
if (!this._message) {
return;
}
this.position.width = "auto";
this.position.height = "auto";
return super.render(force, options);
}
/**
* Initialize the dice history list
* @private
*/
_initialize() {
// Get the roll
const roll = game.l5r5e.RollL5r5e.fromData(this.message.roll);
_initializeHistory() {
if (!this._message) {
return;
}
console.clear();
console.log(roll); // TODO TMP
// Get the roll
this.roll = game.l5r5e.RollL5r5e.fromData(this._message._roll);
// Already history
if (Array.isArray(roll.l5r5e.history)) {
this.object.dicesList = roll.l5r5e.history;
if (Array.isArray(this.roll.l5r5e.history)) {
this.object.dicesList = this.roll.l5r5e.history;
let currentStep = this.roll.l5r5e.history.length - 1;
if (!this._haveChoice(currentStep, RollnKeepDialog.CHOICES.nothing)) {
currentStep += 1;
}
this.object.currentStep = currentStep;
return;
}
// New
roll.terms.forEach((term) => {
this.object.dicesList = [[]];
this.roll.terms.forEach((term) => {
if (typeof term !== "object") {
return;
}
@@ -98,14 +153,24 @@ export class RollnKeepDialog extends FormApplication {
this.object.dicesList[0].push({
type: term.constructor.name,
face: res.result,
explosive: term.constructor.FACES[res.result].explosive,
img: term.constructor.getResultSrc(res.result),
choice: RollnKeepDialog.CHOICES.nothing,
});
});
});
}
/**
* Fill the dices faces
* @private
*/
_initializeDiceFaces() {
// All faces are unique for rings
this.object.swapDiceFaces.rings = Object.keys(game.l5r5e.RingDie.FACES);
// Only unique for Skills
this.object.swapDiceFaces.skills = [1, 3, 6, 8, 10, 11, 12];
}
/**
* Create drag-and-drop workflow handlers for this Application
* @return An array of DragDrop handlers
@@ -121,6 +186,15 @@ export class RollnKeepDialog extends FormApplication {
];
}
/**
* Define whether a user is able to begin a dragstart workflow for a given drag selector
* @param selector The candidate HTML selector for dragging
* @return Can the current user drag this selector?
*/
_canDragStart(selector) {
return this.options.editable;
}
/**
* Callback actions which occur at the beginning of a drag start workflow.
* @param {DragEvent} event The originating DragEvent
@@ -142,20 +216,23 @@ export class RollnKeepDialog extends FormApplication {
* @return {Object}
*/
getData(options = null) {
const draggableList = [];
this.object.dicesList.forEach((step, idx) => {
step.forEach((die, dieNum) => {
if (die) {
draggableList[dieNum] = idx;
}
});
});
// Disable submit / edition
this.options.classes = this.options.classes.filter((e) => e !== "finalized");
this.object.submitDisabled = false;
if (this._checkKeepCount(this.object.currentStep)) {
const kept = this._getKeepCount(this.object.currentStep);
this.object.submitDisabled = kept < 1 || kept > this.roll.l5r5e.keepLimit;
} else if (!this.object.dicesList[this.object.currentStep]) {
this.options.editable = false;
this.options.classes.push("finalized");
}
return {
...super.getData(options),
cssClass: this.options.classes.join(" "),
data: this.object,
draggableList: draggableList,
l5r5e: this.message._roll.l5r5e,
l5r5e: this.roll.l5r5e,
};
}
@@ -166,11 +243,28 @@ export class RollnKeepDialog extends FormApplication {
activateListeners(html) {
super.activateListeners(html);
// GM Only, need to be before the editable check
if (game.user.isGM && this.object.currentStep > 0) {
// Add Context menu to rollback choices
new ContextMenu(html, ".l5r5e.profil", [
{
name: game.i18n.localize("l5r5e.roll_n_keep.undo"),
icon: '<i class="fas fa-undo"></i>',
callback: () => this._undoLastStepChoices(),
},
]);
}
// *** Everything below here is only needed if the sheet is editable ***
if (!this.options.editable) {
return;
}
// Finalize Button
html.find("#finalize").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
if (this._getKeepCount() > 0) {
if (!this.object.submitDisabled) {
this.submit();
}
});
@@ -180,6 +274,11 @@ export class RollnKeepDialog extends FormApplication {
* Handle dropped items
*/
async _onDropItem(event) {
// *** Everything below here is only needed if the sheet is editable ***
if (!this.options.editable) {
return;
}
const type = $(event.currentTarget).data("type");
const json = event.dataTransfer.getData("text/plain");
if (!json || !Object.values(RollnKeepDialog.CHOICES).some((e) => !!e && e === type)) {
@@ -191,81 +290,361 @@ export class RollnKeepDialog extends FormApplication {
return;
}
let addNewRoll = false;
const current = this.object.dicesList[data.step][data.die];
current.choice = type;
delete current.newFace;
// Actions p 26 : change, ignore/discard, reroll, reserve, change face
switch (type) {
case RollnKeepDialog.CHOICES.keep:
if (current.explosive) {
addNewRoll = true;
case RollnKeepDialog.CHOICES.swap: {
// Dice Type Ring/Skill
const diceType = $(event.currentTarget).data("die");
const diceNewFace = $(event.currentTarget).data("face");
if (current.type !== diceType || current.face === diceNewFace) {
current.choice = RollnKeepDialog.CHOICES.nothing;
this.render(false);
return false;
}
current.newFace = diceNewFace;
break;
}
case RollnKeepDialog.CHOICES.reroll:
addNewRoll = true;
// If reroll, we need to keep all the line by default
this._forceChoiceForDiceWithoutOne(RollnKeepDialog.CHOICES.keep);
break;
}
// New roll
if (addNewRoll) {
if (!this.object.dicesList[data.step + 1]) {
this.object.dicesList[data.step + 1] = Array(this.object.dicesList[0].length).fill(null);
}
this.object.dicesList[data.step + 1][data.die] = await this._newRoll(current.type, type);
current.choice = type;
// Little time saving : if we reach the max kept dices, discard all dices without a choice
if (
this._checkKeepCount(this.object.currentStep) &&
this._getKeepCount(this.object.currentStep) === this.roll.l5r5e.keepLimit
) {
this._forceChoiceForDiceWithoutOne(RollnKeepDialog.CHOICES.discard);
}
this.render(false);
return false;
}
/**
* Roll a new die avec return the result
* @private
*/
async _newRoll(dieType, actionType) {
const roll = await new game.l5r5e.RollL5r5e(dieType === "RingDie" ? "1dr" : "1ds");
roll.actor = this.message.roll.l5r5e.actor;
roll.l5r5e.stance = this.message.roll.l5r5e.stance;
// roll.l5r5e.skillId = this.message.roll.l5r5e.skillId;
// roll.l5r5e.skillCatId = this.message.roll.l5r5e.skillCatId;
await roll.roll();
await roll.toMessage({
flavor: game.i18n.localize(`l5r5e.roll_n_keep.${actionType}_chat`),
});
const dice = roll.terms[0];
const result = dice.results[0].result;
return {
type: dieType,
face: result,
explosive: dice.constructor.FACES[result].explosive,
img: dice.constructor.getResultSrc(result),
choice: RollnKeepDialog.CHOICES.nothing,
};
}
/**
* Return the current number of dices kept
* @private
*/
_getKeepCount() {
return this.object.dicesList.reduce((acc, step) => {
return (
acc +
step.reduce((acc2, die) => {
if (!!die && die.choice === RollnKeepDialog.CHOICES.keep) {
acc2 = acc2 + 1;
}
return acc2;
}, 0)
);
_getKeepCount(step) {
return this.object.dicesList[step].reduce((acc, die) => {
if (
!!die &&
[RollnKeepDialog.CHOICES.keep, RollnKeepDialog.CHOICES.reroll, RollnKeepDialog.CHOICES.swap].includes(
die.choice
)
) {
acc = acc + 1;
}
return acc;
}, 0);
}
/**
* Return true if a "_getKeepCount" is needed
* @param {number} step
* @returns {boolean}
* @private
*/
_checkKeepCount(step) {
return (
!this._haveChoice(step, RollnKeepDialog.CHOICES.reroll) &&
(step === 0 || this._haveChoice(step - 1, RollnKeepDialog.CHOICES.reroll))
);
}
/**
* Return true if this choice exist in the current step
* @private
*/
_haveChoice(currentStep, choice) {
return (
this.object.dicesList[currentStep] &&
this.object.dicesList[currentStep].some((e) => !!e && e.choice === choice)
);
}
/**
* Discard all dices without a choice for the current step
* @param {string} newChoice
* @private
*/
_forceChoiceForDiceWithoutOne(newChoice) {
this.object.dicesList[this.object.currentStep]
.filter((e) => !!e)
.map((e) => {
if (e.choice === RollnKeepDialog.CHOICES.nothing) {
e.choice = newChoice;
}
return e;
});
}
/**
* Initialize dice array for "step" if needed
* @param {number} step
* @private
*/
_initializeDicesListStep(step) {
if (!this.object.dicesList[step]) {
this.object.dicesList[step] = Array(this.object.dicesList[0].length).fill(null);
}
}
/**
* Apply all choices to build the next step
* @returns {Promise<void>}
* @private
*/
async _applyChoices() {
const nextStep = this.object.currentStep + 1;
const haveReroll = this._haveChoice(this.object.currentStep, RollnKeepDialog.CHOICES.reroll);
// Foreach kept dices, apply choices
const newRolls = {};
this.object.dicesList[this.object.currentStep].forEach((die, idx) => {
if (!die) {
return;
}
switch (die.choice) {
case RollnKeepDialog.CHOICES.keep:
if (haveReroll) {
// Reroll line add all kept into a new line
this._initializeDicesListStep(nextStep);
this.object.dicesList[nextStep][idx] = duplicate(
this.object.dicesList[this.object.currentStep][idx]
);
this.object.dicesList[nextStep][idx].choice = RollnKeepDialog.CHOICES.nothing;
this.object.dicesList[this.object.currentStep][idx].choice = RollnKeepDialog.CHOICES.discard;
} else if (game.l5r5e[die.type].FACES[die.face].explosive) {
// Exploding dice : add a new dice in the next step
if (!newRolls[die.type]) {
newRolls[die.type] = 0;
}
newRolls[die.type] += 1;
}
break;
case RollnKeepDialog.CHOICES.reroll:
// Reroll : add a new dice in the next step
if (!newRolls[die.type]) {
newRolls[die.type] = 0;
}
newRolls[die.type] += 1;
break;
case RollnKeepDialog.CHOICES.swap:
// FaceSwap : add a new dice with selected face in next step
this._initializeDicesListStep(nextStep);
this.object.dicesList[nextStep][idx] = {
type: this.object.dicesList[this.object.currentStep][idx].type,
face: this.object.dicesList[this.object.currentStep][idx].newFace,
choice: RollnKeepDialog.CHOICES.keep,
};
delete this.object.dicesList[this.object.currentStep][idx].newFace;
break;
}
});
// If new rolls, roll and add them
if (Object.keys(newRolls).length > 0) {
const newRollsResults = await this._newRoll(newRolls);
this._initializeDicesListStep(nextStep);
this.object.dicesList[this.object.currentStep].forEach((die, idx) => {
if (!die) {
return;
}
if (
die.choice === RollnKeepDialog.CHOICES.reroll ||
(!haveReroll &&
die.choice === RollnKeepDialog.CHOICES.keep &&
game.l5r5e[die.type].FACES[die.face].explosive)
) {
this.object.dicesList[nextStep][idx] = newRollsResults[die.type].shift();
}
});
}
}
/**
* Transform a array (of int or object) into a formula ring/skill
* @param rolls
* @returns {string}
* @private
*/
_arrayToFormula(rolls) {
const formula = [];
if (rolls["RingDie"]) {
const rings = Array.isArray(rolls["RingDie"]) ? rolls["RingDie"].length : rolls["RingDie"];
formula.push(rings + "dr");
}
if (rolls["AbilityDie"]) {
const skills = Array.isArray(rolls["AbilityDie"]) ? rolls["AbilityDie"].length : rolls["AbilityDie"];
formula.push(skills + "ds");
}
if (formula.length < 1) {
return "";
}
return formula.join("+");
}
/**
* Roll all new dice at once (better performance) and return the result
* @private
*/
async _newRoll(newRolls) {
const out = {
RingDie: [],
AbilityDie: [],
};
const roll = await new game.l5r5e.RollL5r5e(this._arrayToFormula(newRolls));
await roll.roll();
// Show DsN dice for the new roll
if (game.dice3d !== undefined) {
game.dice3d.showForRoll(roll, game.user, true);
}
roll.terms.forEach((term) => {
if (typeof term !== "object") {
return;
}
term.results.forEach((res) => {
out[term.constructor.name].push({
type: term.constructor.name,
face: res.result,
choice: RollnKeepDialog.CHOICES.nothing,
});
});
});
return out;
}
/**
* Rebuild the message roll
* @param {boolean} forceKeep If true keep all dice regardless their choice
* @returns {Promise<void>}
* @private
*/
async _rebuildRoll(forceKeep = false) {
// Get all kept dices + new (choice null)
const diceList = this.object.dicesList.reduce((acc, step, stepIdx) => {
const haveReroll = stepIdx > 0 && this._haveChoice(stepIdx - 1, RollnKeepDialog.CHOICES.reroll);
step.forEach((die, idx) => {
if (
!!die &&
(forceKeep ||
die.choice === RollnKeepDialog.CHOICES.keep ||
(haveReroll && die.choice === RollnKeepDialog.CHOICES.nothing))
) {
if (!acc[die.type]) {
acc[die.type] = [];
}
// Check previous dice, to add html classes in chat
if (stepIdx > 0 && this.object.dicesList[stepIdx - 1][idx]) {
switch (this.object.dicesList[stepIdx - 1][idx].choice) {
case RollnKeepDialog.CHOICES.reroll:
die.class = "rerolled";
break;
case RollnKeepDialog.CHOICES.swap:
die.class = "swapped";
break;
}
}
acc[die.type].push(die);
}
});
return acc;
}, {});
// Re create a new roll
const roll = await new game.l5r5e.RollL5r5e(this._arrayToFormula(diceList));
roll.l5r5e = {
...this.roll.l5r5e,
summary: roll.l5r5e.summary,
};
// Fill the data
roll.evaluate();
// Modify results
roll.terms.map((term) => {
if (term instanceof game.l5r5e.L5rBaseDie) {
term.results.map((res) => {
const die = diceList[term.constructor.name].shift();
res.result = die.face;
// add class to term result
if (die.class) {
res[die.class] = true;
}
return res;
});
term.l5rSummary();
}
return term;
});
// Recompute summary
roll.l5rSummary();
// Add roll & history to message
this.roll = roll;
this.roll.l5r5e.history = this.object.dicesList;
}
/**
* Send the new roll in chat and delete the old message
* @returns {Promise<void>}
* @private
*/
async _toChatMessage() {
// Keep old Ids
const appOldId = this.id;
const msgOldId = this._message._id;
if (this.roll.l5r5e.isInitiativeRoll) {
await this.roll.l5r5e.actor.rollInitiative({
rerollInitiative: true,
initiativeOptions: {
messageOptions: {
rnkRoll: this.roll,
},
},
});
// Adhesive tape to get the message :/
this.message = this.roll.l5r5e.actor.rnkMessage;
delete this.roll.l5r5e.actor.rnkMessage;
} else {
// Send it to chat, switch to new message
this.message = await this.roll.toMessage();
}
// Refresh viewers
game.l5r5e.sockets.updateMessageIdAndRefresh(appOldId, this._message._id);
// Delete old chat message related to this series
if (game.settings.get("l5r5e", "rnk.deleteOldMessage")) {
if (game.user.isGM) {
const message = game.messages.get(msgOldId);
if (message) {
message.delete();
}
} else {
game.l5r5e.sockets.deleteChatMessage(msgOldId);
}
}
}
/**
* This method is called upon form submission after form data is validated
* @param event The initial triggering submission event
@@ -274,32 +653,66 @@ export class RollnKeepDialog extends FormApplication {
* @override
*/
async _updateObject(event, formData) {
console.log("**** _updateObject");
// *** Everything below here is only needed if the sheet is editable ***
if (!this.options.editable) {
return;
}
// Notify the change to other players
// game.l5r5e.sockets.refreshAppId(this.id);
// Discard all dices without a choice for the current step
this._forceChoiceForDiceWithoutOne(RollnKeepDialog.CHOICES.discard);
// this.message._roll.l5r5e.history = {test: "yahooo"};
// Apply all choices to build the next step
await this._applyChoices();
// await message.update({
// data: {
// roll: roll
// }
// });
// message.render(false);
// *** Below this the current step become the next step ***
this.object.currentStep++;
// console.log(roll.toJSON(), this.message);
// Rebuild the roll
await this._rebuildRoll(false);
// ui.chat.updateMessage(message);
// ui.chat.postOne(message, false);
// Send the new roll in chat and delete the old message
await this._toChatMessage();
// if (game.user.isGM) {
// message.delete();
// } else {
// game.l5r5e.sockets.deleteChatMessage(messageId);
// }
// If a next step exist, rerender, else close
if (this.object.dicesList[this.object.currentStep]) {
return this.render(false);
}
return this.close();
}
// return this.close();
/**
* Undo the last step choice
* @returns {Promise<Application|any>}
* @private
*/
async _undoLastStepChoices() {
// Find the step to work to
this.object.currentStep = this.object.dicesList[this.object.currentStep]
? this.object.currentStep
: Math.max(0, this.object.currentStep - 1);
// If all clear, delete this step
if (this._haveChoice(this.object.currentStep, RollnKeepDialog.CHOICES.nothing)) {
if (this.object.currentStep === 0) {
return;
}
this.object.dicesList.pop();
this.object.dicesList = this.object.dicesList.filter((e) => !!e);
this.object.currentStep--;
}
// Clear choices
this.object.dicesList[this.object.currentStep]
.filter((e) => !!e)
.map((e) => {
e.choice = RollnKeepDialog.CHOICES.nothing;
return e;
});
this.options.editable = true;
await this._rebuildRoll(true);
await this._toChatMessage();
return this.render(false);
}
/**
@@ -309,21 +722,22 @@ export class RollnKeepDialog extends FormApplication {
*/
static async onChatAction(event) {
event.preventDefault();
event.stopPropagation();
// Extract card data
const button = $(event.currentTarget);
button.attr("disabled", true);
const card = button.parents(".l5r5e.item-display.dices-l5r");
const messageId = card.parents(".chat-message").data("message-id");
const message = game.messages.get(messageId);
// Validate permission to proceed with the roll n keep
if (!message || !message._roll.l5r5e.actor.owner) {
return;
// Already open ? close it
const app = Object.values(ui.windows).find((e) => e.id === `l5r5e-roll-n-keep-dialog-${messageId}`);
if (app) {
app.close();
} else {
new RollnKeepDialog(messageId).render(true);
}
new RollnKeepDialog(message).render(true);
// Re-enable the button
button.attr("disabled", false);
}

View File

@@ -13,15 +13,20 @@ export class RollL5r5e extends Roll {
skillId: "",
skillCatId: "",
actor: null,
difficulty: 2,
difficultyHidden: false,
voidPointUsed: false,
keepLimit: null,
isInitiativeRoll: false,
skillAssistance: 0,
initialFormula: null,
dicesTypes: {
std: false,
l5r: false,
},
summary: {
difficulty: 2,
difficultyHidden: false,
voidPointUsed: false,
ringsUsed: 0,
totalSuccess: 0,
totalBonus: 0,
success: 0,
explosive: 0,
opportunity: 0,
@@ -40,8 +45,6 @@ export class RollL5r5e extends Roll {
this.l5r5e.skillId = res[2];
}
});
// TODO parse difficulty stance skillId from cmd line ?
}
set actor(actor) {
@@ -68,23 +71,15 @@ export class RollL5r5e extends Roll {
// Roll
super.evaluate({ minimize, maximize });
// Current terms - L5R Summary
this.terms.forEach((term) => this._l5rSummary(term));
// Check inner L5R rolls - L5R Summary
this._dice.forEach((term) => this._l5rSummary(term));
// Store final outputs
this._rolled = true;
this.l5r5e.dicesTypes.std = this.dice.some(
(term) => term instanceof DiceTerm && !(term instanceof game.l5r5e.L5rBaseDie)
); // ignore math symbols
this.l5r5e.dicesTypes.l5r = this.dice.some((term) => term instanceof game.l5r5e.L5rBaseDie);
this.l5r5e.summary.ringsUsed = this.dice.reduce(
(acc, term) => (term instanceof game.l5r5e.RingDie ? acc + term.number : acc),
0
);
// Save initial formula
if (!this.l5r5e.initialFormula) {
this.l5r5e.initialFormula = this.formula;
}
// Compute summary
this.l5rSummary();
return this;
}
@@ -92,10 +87,54 @@ export class RollL5r5e extends Roll {
/**
* Summarise the total of success, strife... for L5R dices for the current roll
*
* @private
*/
l5rSummary() {
const summary = this.l5r5e.summary;
// Reset totals
summary.success = 0;
summary.explosive = 0;
summary.opportunity = 0;
summary.strife = 0;
summary.totalSuccess = 0;
// Current terms - L5R Summary
this.terms.forEach((term) => this._l5rTermSummary(term));
// Check inner L5R rolls - L5R Summary
this._dice.forEach((term) => this._l5rTermSummary(term));
// Store final outputs
this.l5r5e.dicesTypes.std = this.dice.some(
(term) => term instanceof DiceTerm && !(term instanceof game.l5r5e.L5rBaseDie)
); // ignore math symbols
this.l5r5e.dicesTypes.l5r = this.dice.some((term) => term instanceof game.l5r5e.L5rBaseDie);
summary.totalBonus = Math.max(0, summary.totalSuccess - this.l5r5e.difficulty);
if (!this.l5r5e.keepLimit) {
// count ring die + skill assistance
this.l5r5e.keepLimit =
this.dice.reduce((acc, term) => (term instanceof game.l5r5e.RingDie ? acc + term.number : acc), 0) +
Math.max(0, this.l5r5e.skillAssistance || 0);
// if only bulk skill dice, count the skill dice
if (!this.l5r5e.keepLimit) {
this.l5r5e.keepLimit = this.dice.reduce(
(acc, term) => (term instanceof game.l5r5e.AbilityDie ? acc + term.number : acc),
0
);
}
}
}
/**
* Summarise the total of success, strife... for L5R dices for the current term
*
* @param term
* @private
*/
_l5rSummary(term) {
_l5rTermSummary(term) {
if (!(term instanceof game.l5r5e.L5rBaseDie)) {
return;
}
@@ -103,6 +142,8 @@ export class RollL5r5e extends Roll {
["success", "explosive", "opportunity", "strife"].forEach((props) => {
this.l5r5e.summary[props] += parseInt(term.l5r5e[props]);
});
this.l5r5e.summary.totalSuccess += term.totalSuccess;
// TODO Others advantage/disadvantage
}
@@ -160,8 +201,9 @@ export class RollL5r5e extends Roll {
classes: [
cls.name.toLowerCase(),
"d" + term.faces,
!isL5rDie && r.rerolled ? "rerolled" : null,
!isL5rDie && r.exploded ? "exploded" : null,
isL5rDie && r.swapped ? "swapped" : null,
r.rerolled ? "rerolled" : null,
r.exploded ? "exploded" : null,
!isL5rDie && r.discarded ? "discarded" : null,
!isL5rDie && r.result === 1 ? "min" : null,
!isL5rDie && r.result === term.faces ? "max" : null,
@@ -204,13 +246,6 @@ export class RollL5r5e extends Roll {
this.roll();
}
const canRnK = false; // TODO TMP dev in progress
// const canRnK = !this.l5r5e.dicesTypes.std
// && this.l5r5e.dicesTypes.l5r
// && this.dice.length > 1
// && this.l5r5e.actor // pb with dice with no actor
// && this.l5r5e.actor.owner;
// Define chat data
const chatData = {
formula: isPrivate ? "???" : this._formula,
@@ -224,13 +259,20 @@ export class RollL5r5e extends Roll {
? {}
: {
...this.l5r5e,
canRnK: canRnK,
dices: this.dice.map((d) => {
dices: this.dice.map((term) => {
const isL5rDie = term instanceof game.l5r5e.L5rBaseDie;
return {
diceTypeL5r: d instanceof game.l5r5e.L5rBaseDie,
rolls: d.results.map((r) => {
diceTypeL5r: isL5rDie,
rolls: term.results.map((r) => {
return {
result: d.constructor.getResultLabel(r.result),
result: term.constructor.getResultLabel(r.result),
classes: [
isL5rDie && r.swapped ? "swapped" : null,
r.rerolled ? "rerolled" : null,
r.exploded ? "exploded" : null,
]
.filter((c) => !!c)
.join(" "),
};
}),
};
@@ -294,10 +336,16 @@ export class RollL5r5e extends Roll {
roll.l5r5e = duplicate(data.l5r5e);
// get real Actor object
if (data.l5r5e.actor && !(data.l5r5e.actor instanceof game.l5r5e.ActorL5r5e)) {
const actor = game.actors.get(data.l5r5e.actor.id);
if (actor) {
roll.l5r5e.actor = actor;
if (data.l5r5e.actor) {
if (data.l5r5e.actor instanceof game.l5r5e.ActorL5r5e) {
// duplicate break the object, relink it
roll.l5r5e.actor = data.l5r5e.actor;
} else {
// only id, get the object
const actor = game.actors.get(data.l5r5e.actor.id);
if (actor) {
roll.l5r5e.actor = actor;
}
}
}

View File

@@ -29,6 +29,13 @@ export const RegisterHandlebars = function () {
return game.i18n.localize("l5r5e.techniques." + techniqueName.toLowerCase());
});
/* ------------------------------------ */
/* Dice */
/* ------------------------------------ */
Handlebars.registerHelper("getDiceFaceUrl", function (diceClass, faceId) {
return game.l5r5e[diceClass].getResultSrc(faceId);
});
/* ------------------------------------ */
/* Utility */
/* ------------------------------------ */

View File

@@ -68,6 +68,11 @@ export class HelpersL5r5e {
*/
static async getObjectGameOrPack(id, type, pack = null) {
try {
// If no pack passed, but it's a core item, we know the pack to get it
if (!pack && id.substr(0, 7) === "L5RCore") {
pack = HelpersL5r5e.getPackNameForCoreItem(id);
}
// Named pack
if (pack) {
const data = await game.packs.get(pack).getEntity(id);
@@ -184,4 +189,65 @@ export class HelpersL5r5e {
static escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
/**
* Get the associated pack for a core item (time saving)
*/
static getPackNameForCoreItem(itemId) {
const core = new Map();
core.set("Pro", "l5r5e.core-properties");
core.set("Kat", "l5r5e.core-techniques-kata");
core.set("Kih", "l5r5e.core-techniques-kiho");
core.set("Ins", "l5r5e.core-techniques-inversion");
core.set("Inv", "l5r5e.core-techniques-invocations");
core.set("Rit", "l5r5e.core-techniques-rituals");
core.set("Shu", "l5r5e.core-techniques-shuji");
core.set("Mah", "l5r5e.core-techniques-maho");
core.set("Nin", "l5r5e.core-techniques-ninjutsu");
core.set("Sch", "l5r5e.core-techniques-school");
core.set("Mas", "l5r5e.core-techniques-mastery");
core.set("Ite", "l5r5e.core-items");
core.set("Arm", "l5r5e.core-armors");
core.set("Wea", "l5r5e.core-weapons");
core.set("Dis", "l5r5e.core-peculiarities-distinctions");
core.set("Pas", "l5r5e.core-peculiarities-passions");
core.set("Adv", "l5r5e.core-peculiarities-adversities");
core.set("Anx", "l5r5e.core-peculiarities-anxieties");
return core.get(itemId.replace(/L5RCore(\w{3})\d+/gi, "$1"));
}
/**
* Show a confirm dialog before a deletion
* @param {string} content
* @param {function} callback The callback function for confirmed action
*/
static confirmDeleteDialog(content, callback) {
new Dialog({
title: game.i18n.localize("Delete"),
content,
buttons: {
confirm: {
icon: '<i class="fas fa-trash"></i>',
label: game.i18n.localize("Yes"),
callback,
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize("No"),
},
},
}).render(true);
}
/**
* Notify Applications using Difficulty settings that the values was changed
*/
static notifyDifficultyChange() {
["l5r5e-dice-picker-dialog", "l5r5e-gm-tools-dialog"].forEach((appId) => {
const app = Object.values(ui.windows).find((e) => e.id === appId);
if (app && typeof app.refresh === "function") {
app.refresh();
}
});
}
}

View File

@@ -122,27 +122,21 @@ export default class HooksL5r5e {
.then(() => HooksL5r5e._gmCombatBar(app, html, data));
});
html.find(".prepared-control").on("click", (event) => {
html.find(".prepared-control").on("mousedown", (event) => {
event.preventDefault();
event.stopPropagation();
let preparedId = $(event.currentTarget).data("id");
const preparedId = $(event.currentTarget).data("id");
if (!Object.hasOwnProperty.call(prepared, preparedId)) {
return;
}
let value = prepared[preparedId];
switch (value) {
case "false":
value = "true";
break;
case "true":
value = "null";
break;
case "null":
value = "false";
break;
}
const rev = event.which === 3;
const nextValue = {
false: rev ? "true" : "null",
true: rev ? "null" : "false",
null: rev ? "false" : "true",
};
game.settings
.set("l5r5e", `initiative.prepared.${preparedId}`, value)
.set("l5r5e", `initiative.prepared.${preparedId}`, nextValue[prepared[preparedId]])
.then(() => HooksL5r5e._gmCombatBar(app, html, data));
});
}
@@ -174,7 +168,7 @@ export default class HooksL5r5e {
}
/**
* DiceSoNice Hook
* DiceSoNice - Add L5R DicePresets
*/
static diceSoNiceReady(dice3d) {
const texturePath = `${CONFIG.l5r5e.paths.assets}dices/default/3d/`;
@@ -218,4 +212,16 @@ export default class HooksL5r5e {
"d12"
);
}
/**
* DiceSoNice - Do not show 3D roll for the Roll n Keep series
*
* @param {string} messageId
* @param {object} context
*/
static diceSoNiceRollStart(messageId, context) {
if (context.roll.l5r5e?.history) {
context.blind = true;
}
}
}

View File

@@ -4,7 +4,9 @@ export class ItemL5r5e extends Item {
* @override
*/
static async create(data, options = {}) {
data.img = `${CONFIG.l5r5e.paths.assets}icons/items/${data.type}.svg`;
if (data.img === undefined) {
data.img = `${CONFIG.l5r5e.paths.assets}icons/items/${data.type}.svg`;
}
return super.create(data, options);
}
}

View File

@@ -105,12 +105,7 @@ export class ItemSheetL5r5e extends ItemSheet {
});
// Delete a property
html.find(`.property-delete`).on("click", (event) => {
event.preventDefault();
event.stopPropagation();
const li = $(event.currentTarget).parents(".property");
this._deleteProperty(li.data("propertyId"));
});
html.find(`.property-delete`).on("click", this._deleteProperty.bind(this));
}
/**
@@ -174,20 +169,37 @@ export class ItemSheetL5r5e extends ItemSheet {
* Delete a property from the current item
* @private
*/
_deleteProperty(id) {
if (
!Array.isArray(this.entity.data.data.properties) ||
this.entity.data.data.properties.findIndex((p) => p.id === id) === -1
) {
_deleteProperty(event) {
event.preventDefault();
event.stopPropagation();
if (!Array.isArray(this.entity.data.data.properties)) {
return;
}
this.entity.data.data.properties = this.entity.data.data.properties.filter((p) => p.id !== id);
const id = $(event.currentTarget).parents(".property").data("propertyId");
const tmpProps = this.entity.data.data.properties.find((p) => p.id === id);
if (!tmpProps) {
return;
}
this.entity.update({
data: {
properties: this.entity.data.data.properties,
},
});
const callback = async () => {
this.entity.data.data.properties = this.entity.data.data.properties.filter((p) => p.id !== id);
this.entity.update({
data: {
properties: this.entity.data.data.properties,
},
});
};
// Holing Ctrl = without confirm
if (event.ctrlKey) {
return callback();
}
game.l5r5e.HelpersL5r5e.confirmDeleteDialog(
game.i18n.format("l5r5e.global.delete_confirm", { name: tmpProps.name }),
callback
);
}
}

View File

@@ -113,6 +113,40 @@ Hooks.once("init", async () => {
// Journal
Items.unregisterSheet("core", JournalSheet);
Items.registerSheet("l5r5e", BaseJournalSheetL5r5e, { makeDefault: true });
// Override the default Token _drawBar function to allow fatigue bar reversing.
Token.prototype._drawBar = function (number, bar, data) {
const reverseBar = data.attribute === "fatigue" && game.settings.get("l5r5e", "token.reverseFatigueBar");
// Bar value
const pct = Math.clamped(Number(data.value), 0, data.max) / data.max;
// Modify color
let color = number === 0 ? [pct / 1.2, 1 - pct, 0] : [0.5 * pct, 0.7 * pct, 0.5 + pct / 2];
// Red if compromised
if (data.attribute === "strife" && data.value > data.max) {
color = [1, 0.1, 0.1];
}
// Enlarge the bar for large tokens
let h = Math.max(canvas.dimensions.size / 12, 8);
if (this.data.height >= 2) {
h *= 1.6;
}
// Draw the bar
bar.clear()
.beginFill(0x000000, 0.5)
.lineStyle(2, 0x000000, 0.9)
.drawRoundedRect(0, 0, this.w, h, 3)
.beginFill(PIXI.utils.rgb2hex(color), 0.8)
.lineStyle(1, 0x000000, 0.8)
.drawRoundedRect(1, 1, (reverseBar ? 1 - pct : pct) * (this.w - 2), h - 2, 2);
// Set position
bar.position.set(0, number === 0 ? this.h - h : 0);
};
});
/* ------------------------------------ */
@@ -129,3 +163,4 @@ Hooks.on("renderSidebarTab", (app, html, data) => HooksL5r5e.renderSidebarTab(ap
Hooks.on("renderChatMessage", (message, html, data) => HooksL5r5e.renderChatMessage(message, 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));

View File

@@ -2,6 +2,33 @@
* Custom system settings register
*/
export const RegisterSettings = function () {
/* ------------------------------------ */
/* User settings */
/* ------------------------------------ */
game.settings.register("l5r5e", "rnk.deleteOldMessage", {
name: "SETTINGS.RollNKeep.DeleteOldMessage",
hint: "SETTINGS.RollNKeep.DeleteOldMessageHint",
scope: "world",
config: true,
default: true,
type: Boolean,
});
game.settings.register("l5r5e", "initiative.setTn1OnTypeChange", {
name: "SETTINGS.Initiative.SetTn1OnTypeChange",
hint: "SETTINGS.Initiative.SetTn1OnTypeChangeHint",
scope: "world",
config: true,
type: Boolean,
default: true,
});
game.settings.register("l5r5e", "token.reverseFatigueBar", {
name: "SETTINGS.ReverseFatigueBar",
scope: "world",
config: true,
type: Boolean,
default: false,
});
/* ------------------------------------ */
/* Update */
/* ------------------------------------ */
@@ -22,6 +49,7 @@ export const RegisterSettings = function () {
config: false,
type: Boolean,
default: false,
onChange: () => game.l5r5e.HelpersL5r5e.notifyDifficultyChange(),
});
game.settings.register("l5r5e", "initiative.difficulty.value", {
name: "Initiative difficulty value",
@@ -29,6 +57,7 @@ export const RegisterSettings = function () {
config: false,
type: Number,
default: 2,
onChange: () => game.l5r5e.HelpersL5r5e.notifyDifficultyChange(),
});
game.settings.register("l5r5e", "initiative.encounter", {
name: "Initiative encounter type",
@@ -36,6 +65,11 @@ export const RegisterSettings = function () {
config: false,
type: String,
default: "skirmish",
onChange: () => {
if (game.settings.get("l5r5e", "initiative.setTn1OnTypeChange")) {
game.settings.set("l5r5e", "initiative.difficulty.value", 1);
}
},
});
game.settings.register("l5r5e", "initiative.prepared.character", {
name: "Initiative PC prepared or not",

View File

@@ -25,6 +25,10 @@ export class SocketHandlerL5r5e {
this._onRefreshAppId(data);
break;
case "updateMessageIdAndRefresh":
this._onUpdateMessageIdAndRefresh(data);
break;
default:
console.warn(new Error("This socket event is not supported"), data);
break;
@@ -40,9 +44,13 @@ export class SocketHandlerL5r5e {
});
}
_onDeleteChatMessage(data) {
// Only delete the message if the user is a GM (otherwise it have no real effect)
// Currently only used in RnK
if (!game.user.isGM || !game.settings.get("l5r5e", "rnk.deleteOldMessage")) {
return;
}
const message = game.messages.get(data.messageId);
// only delete the message if the user is a GM and the event emitter is one of the recipients
if (game.user.isGM && message.data["whisper"].includes(data.userId)) {
if (message) {
message.delete();
}
}
@@ -65,4 +73,25 @@ export class SocketHandlerL5r5e {
}
app.refresh();
}
/**
* Change in app message and refresh (used in RnK)
* @param appId
* @param msgId
*/
updateMessageIdAndRefresh(appId, msgId) {
game.socket.emit(SocketHandlerL5r5e.SOCKET_NAME, {
type: "updateMessageIdAndRefresh",
appId,
msgId,
});
}
_onUpdateMessageIdAndRefresh(data) {
const app = Object.values(ui.windows).find((e) => e.id === data.appId);
if (!app || !app.message || typeof app.refresh !== "function") {
return;
}
app.message = game.messages.get(data.msgId);
app.refresh();
}
}

View File

@@ -7,7 +7,7 @@
@import "../scss/ui";
.l5r5e {
@import "../scss/dices";
@import "../scss/dices-chat";
@import "../scss/sheets";
@import "../scss/npc";
@import "../scss/nav";

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,73 @@
// Dice in chat
.chat-dice {
display: inline;
position: relative;
padding: 0.25rem;
&:after {
content: "";
position: absolute;
bottom: 0;
right: 0;
border-radius: 0.15rem;
padding: 0 0.1rem 0 0.15rem;
font-size: 0.65rem;
line-height: 1rem;
width: 0.75rem;
text-align: center;
color: white;
background: transparent;
}
&.rerolled {
> img {
border-bottom: 0 none;
}
&:after {
content: "\f2f9";
background: orangered;
}
}
&.swapped {
> img {
border-bottom: 0 none;
}
&:after {
content: "\f337";
background: fuchsia;
}
}
> img {
border: 1px solid transparent;
height: auto;
width: calc(100% / 6 - 0.255rem);
margin: auto;
}
}
.chat-profil {
text-align: center;
vertical-align: middle;
.profile-img {
margin: 0.25rem 0.25rem 0 0;
}
&-stance {
font-size: 2.5rem;
line-height: 2.5rem;
margin: 0.25rem;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
}
&-element {
flex-wrap: wrap;
flex-grow: 1;
&-skill {
flex-grow: 3;
}
&:last-child {
flex-grow: 2;
}
}
}

View File

@@ -1,46 +1,67 @@
// -- Dices.scss
.dice-roll {
.dice-formula,
.dice-total {
background: rgba(255, 255, 255, 0.1);
border: rgba(255, 255, 255, 0.75);
text-align: center;
margin: 0.5rem 0;
padding: 0.25rem 0.5rem 0.25rem 0.25rem;
&-rnk {
line-height: 2rem;
i {
margin-left: 0.5rem;
}
}
}
}
// Dice in chat
.chat-dice > img {
border: 1px solid transparent;
background-repeat: no-repeat;
background-position: center;
background-size: 100%;
height: 44px;
width: 44px;
//cursor: pointer;
//-webkit-appearance: none;
//appearance: none;
outline: none;
margin: 0;
flex: 0 0 20px;
display: inline-block;
}
.chat-profil {
text-align: center;
vertical-align: middle;
&-stance {
font-size: 40px;
position: relative;
top: 8px;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
button {
&.chat-dice-rnk {
cursor: default;
color: $white;
background: linear-gradient(
$l5r5e-linear-gradient-third,
$l5r5e-linear-gradient-third-dark,
$l5r5e-linear-gradient-third
);
background-origin: padding-box;
border-image: url("../assets/ui/macro-button.webp") 10 repeat;
border-image-width: 0.5rem;
border-image-outset: 0px;
margin: 0.5rem 0 0;
&:hover {
background: linear-gradient(
$l5r5e-linear-gradient-first-dark,
$l5r5e-linear-gradient-first,
$l5r5e-linear-gradient-first-dark
);
}
}
}
&-element {
flex-wrap: wrap;
flex-grow: 1;
&-skill {
flex-grow: 3;
.dice-result-rnk {
background: rgba(0, 0, 255, 0.1);
border: 1px solid rgba(55, 55, 155, 0.75);
padding: 0.25rem;
color: rgba(55, 55, 155, 0.75);
text-align: center;
font-weight: bold;
text-shadow: 0 0 0 $black;
&.success {
background: rgba(0, 255, 0, 0.1);
border-color: rgba(55, 155, 55, 0.75);
color: rgba(55, 155, 55, 0.75);
i.i_success {
font-size: 1rem;
}
}
&.unknown {
background: rgba(121, 121, 121, 0.1);
border-color: rgba(124, 124, 124, 0.75);
color: rgba(91, 91, 91, 0.75);
}
&.fail {
background: rgba(255, 0, 0, 0.1);
border-color: rgba(155, 55, 55, 0.75);
color: rgba(155, 55, 55, 0.75);
}
}
}
@@ -52,6 +73,8 @@
// Dice Picker
.dice-picker-dialog {
min-width: 600px;
min-height: auto;
// Utility
* {
transition: none;
@@ -72,18 +95,26 @@
text-align: center;
background: none;
border: none;
height: calc(100% - 3rem);
td:first-child {
width: 200px;
}
td {
width: 240px;
}
td:last-child {
width: 200px;
border: 0 none;
margin: 0;
padding: 0;
tbody {
tr {
td {
width: 250px;
padding: 0 0.5rem;
&:first-child,
&:last-child {
width: 150px;
}
}
&:last-child {
td {
width: 100%;
padding: 0.5rem;
}
}
}
}
}
@@ -151,17 +182,75 @@
}
.roll-n-keep-dialog {
min-width: 600px;
max-width: 800px;
&.finalized {
width: auto;
min-width: 400px;
}
img {
border: 0;
}
table {
display: table;
min-height: 9rem;
border: 0 none;
margin: 0.25rem 0;
padding: 0;
tbody {
tr {
background: transparent;
td {
margin: 0;
padding: 0;
}
}
}
}
.rnk-ct {
margin: 0;
display: flex;
flex-wrap: wrap;
border-radius: 0.25rem;
background: rgba(0, 0, 0, 0.05);
border: 1px solid rgba(255, 255, 255, 0.5);
.rnk-center {
flex: 350px;
flex-wrap: wrap;
display: flex;
}
}
.profil {
border-bottom: 1px solid #782e22;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.dropbox {
min-height: 100px;
position: relative;
min-height: 7rem;
legend {
i:last-child {
position: absolute;
top: 0;
right: 0;
border-radius: 0.15rem;
padding: 0 0.1rem 0 0.15rem;
font-size: 0.65rem;
line-height: 1rem;
width: 1rem;
margin: 0.25rem;
text-align: center;
color: white;
background: $l5r5e-label;
}
}
&.faces-change {
min-height: 40px;
margin: 0.5rem auto;
}
&.discards {
border: 1px solid gray;
}
@@ -169,31 +258,142 @@
border: 1px solid orangered;
}
&.keeps {
flex: 100%;
border: 1px solid green;
}
&.swap {
flex: 0 0 calc(100px + 1rem);
flex-direction: column;
border: 1px solid fuchsia;
}
&.discards,
&.rerolls {
flex: 0 0 calc(50% - 0.5rem);
margin-bottom: 0.5rem;
}
}
/* Dice Marker */
.dice-ct {
position: relative;
padding: 0.25rem;
&:before {
content: "";
position: absolute;
height: 0.5rem;
width: 2px;
top: -0.3rem;
right: calc(50% - 1px);
background: $l5r5e-black-light;
}
&:after {
content: "\f128";
position: absolute;
bottom: 0;
right: 0;
border-radius: 0.15rem;
padding: 0 0.1rem 0 0.15rem;
font-size: 0.65rem;
line-height: 1rem;
width: 0.65rem;
text-align: center;
color: white;
background: gray;
}
&.discard {
filter: opacity(0.5);
&:after {
content: "\f00d";
background: gray;
}
}
&.reroll {
filter: opacity(0.5);
&:after {
content: "\f2f9";
background: orangered;
}
}
&.keep {
&:after {
content: "\f00c";
background: green;
}
}
&.swap {
&:after {
content: "\f337";
background: fuchsia;
}
}
}
tr:first-child .dice-ct {
&:before {
display: none;
}
}
.dice {
height: 40px;
width: 40px;
&.discard {
filter: opacity(0.5);
border: 0 none;
}
&.reroll {
filter: opacity(0.5);
border: 0 none;
}
&.keep {
border: 0 none;
}
&.swap {
border: 0 none;
}
}
.discard {
filter: opacity(0.5);
border: 3px solid gray;
#finalize {
width: 100%;
margin: 0.5rem 0.25rem 0.25rem;
}
.reroll {
filter: opacity(0.5);
border: 3px solid orangered;
.section-header {
i {
font-size: 0.75rem;
margin: 0 0.25rem;
}
}
.keep {
border: 3px solid green;
.fa-sign-in-alt {
transform: rotate(90deg);
}
.chat-profil {
ul {
display: flex;
flex-direction: row;
li {
&:nth-child(1),
&:nth-child(2) {
flex: 0 0 4rem;
padding: 0 0.25rem 0.25rem;
}
&:nth-child(4) {
flex: 0 0 4rem;
padding: 0 0.25rem 0.25rem;
}
}
.profile-img {
width: 4rem;
}
.chat-profil-stance {
font-size: 3.5rem;
line-height: 3.5rem;
}
}
}
}
#l5r5e-gm-tools-dialog {
bottom: 0;
right: 0.5rem;
//bottom: 0;
//right: 0.5rem;
display: flex;
background-position: center;
background-size: 100%;
@@ -208,13 +408,15 @@
border-image: url("../assets/ui/macro-button.webp") 10 repeat;
border-image-width: 0.5rem;
border-image-outset: 0px;
padding: 0;
margin: 0.5rem;
.window-header {
text-align: center;
border-bottom: 1px solid rgb(195, 165, 130);
h4 {
font-family: $font-primary;
text-transform: uppercase;
letter-spacing: 0.15rem;
letter-spacing: 0.25rem;
line-height: 2.25rem;
color: $white-light;
}
}
.window-content {
@@ -222,22 +424,50 @@
vertical-align: middle;
background: transparent;
color: $white-light;
form {
padding: 0;
}
.gm-tools-container {
flex: 1;
display: flex;
flex-flow: wrap;
a {
font-size: 2rem;
line-height: 2rem;
min-height: 2rem;
margin: 0;
li {
flex: 1;
display: flex;
margin: 0;
padding: 0;
border-right: 1px solid #c3a582;
cursor: url("../assets/cursors/pointer.webp"), pointer;
i {
font-size: 3rem;
line-height: 4rem;
vertical-align: middle;
&:last-child {
margin: 0;
border: 0 none;
}
:hover {
text-shadow: 0 0 $red;
}
}
.difficulty {
flex: 1;
font-size: 3rem;
.difficulty_hidden {
.fa {
width: 3rem;
}
.difficulty {
flex: 1rem;
width: 2rem;
font-size: 2rem;
text-align: center;
margin: 0;
padding: 0.5rem;
}
}
.fa {
padding: 0.5rem;
}
.fa-bed,
.fa-star-half-alt {
width: 100%;
padding: 0.5rem;
}
}
}
@@ -246,3 +476,34 @@
display: none;
}
}
.dice-picker-dialog,
.roll-n-keep-dialog {
button {
cursor: default;
color: $white;
background: linear-gradient(
$l5r5e-linear-gradient-third,
$l5r5e-linear-gradient-third-dark,
$l5r5e-linear-gradient-third
);
background-origin: padding-box;
border-image: url("../assets/ui/macro-button.webp") 10 repeat;
border-image-width: 0.5rem;
border-image-outset: 0px;
margin: 0.5rem 0 0;
&:hover {
background: linear-gradient(
$l5r5e-linear-gradient-first-dark,
$l5r5e-linear-gradient-first,
$l5r5e-linear-gradient-first-dark
);
}
&[disabled] {
opacity: 0.25;
&:hover {
box-shadow: none;
}
}
}
}

View File

@@ -186,6 +186,7 @@ li {
// Fieldset
fieldset {
flex: 1;
flex-wrap: wrap;
display: flex;
margin: 0 0.25rem;
padding: 0.5rem;

View File

@@ -96,7 +96,7 @@
width: 100%;
line-height: 2rem;
font-size: 0.75rem;
margin: 0.25rem 0.25rem 0.5rem;
margin: 0 0 0.5rem;
text-align: center;
li {
flex: 1;
@@ -133,24 +133,32 @@
}
}
article {
.weapons-content {
flex: 1;
}
min-height: auto;
display: flex;
fieldset {
fieldset,
.checklist {
flex: 0 0 calc(100% - 0.5rem);
}
.items-content {
flex: 0 0 calc(100% - 0.5rem);
margin: 1rem 0.25rem 0;
}
.weapons-content {
flex: 1;
}
.initiative-wrapper {
margin-bottom: 0.5rem;
}
&:last-child {
padding-bottom: 1rem;
}
.techniques-wrapper {
padding-left: 0.5rem;
fieldset,
.checklist {
flex: 100%;
margin: 0;
}
}
}
.npc-note {
.editor {

View File

@@ -17,13 +17,6 @@
.sheet-body {
height: calc(100% - 27rem);
}
.techniques-wrapper {
fieldset {
&:last-child {
margin: 0 0 0 0.5rem;
}
}
}
fieldset {
&.advancement {
display: block;
@@ -501,6 +494,31 @@
padding: 0.5rem;
flex-wrap: wrap;
min-height: calc(100% - 3.25rem);
fieldset {
h3 {
font-size: 1.25rem;
width: 100%;
text-align: left;
line-height: 2rem;
color: $l5r5e-bold;
border-bottom: 1px solid;
.item-control {
&.item-add {
float: right;
font-size: 0.75rem;
line-height: 0.75rem;
border: 1px solid;
padding: 0.25rem;
margin: 0.25rem;
color: $white;
background: $l5r5e-bold;
&:hover {
opacity: 0.75;
}
}
}
}
}
&.tab[data-tab] {
&.active {
display: flex;
@@ -517,11 +535,15 @@
}
}
.techniques-wrapper {
padding-left: 0.25rem;
fieldset {
margin: 0 0 0 0.25rem;
}
.checklist {
display: flex;
flex-wrap: wrap;
font-size: 0.85rem;
margin: 0 0 0.25rem 0.5rem;
margin: 0 0 0.25rem 0.25rem;
padding: 0.5rem;
background: $l5r5e-title;
--notchSize: 0.25rem;

View File

@@ -2,7 +2,7 @@
"name": "l5r5e",
"title": "Legend of the Five Rings (5th Edition)",
"description": "This is an authorised multilingual game system En|Fr|Es, for Legend of the Five Rings (5th Edition) by <a href='https://edge-studio.net/'>Edge Studio</a> <p> - Join the official Discord server: <a href='https://discord.gg/foundryvtt'> Official Discord</a></p><p> - Rejoignez la communauté Francophone: <a href='https://discord.gg/pPSDNJk'>Francophone Discord</a></p>",
"version": "1.1.2",
"version": "1.2.1",
"minimumCoreVersion": "0.7.9",
"compatibleCoreVersion": "0.7.9",
"manifestPlusVersion": "1.0.0",
@@ -185,5 +185,5 @@
],
"url": "https://gitlab.com/teaml5r/l5r5e",
"manifest": "https://gitlab.com/teaml5r/l5r5e/-/raw/master/system/system.json",
"download": "https://gitlab.com/teaml5r/l5r5e/-/jobs/artifacts/v1.1.2/raw/l5r5e.zip?job=build"
"download": "https://gitlab.com/teaml5r/l5r5e/-/jobs/artifacts/v1.2.1/raw/l5r5e.zip?job=build"
}

View File

@@ -102,8 +102,7 @@
"ritual": false,
"shuji": false,
"maho": false,
"ninjutsu": false,
"list": []
"ninjutsu": false
}
}
},

View File

@@ -62,7 +62,10 @@
<input name="data.money.zeni" type="number" value="{{actor.data.money.zeni}}" data-dtype="Number" min="0" placeholder="0"/>
</label>
</fieldset>
{{> 'systems/l5r5e/templates/items/item/items.html' }}
<fieldset class="items-wrapper">
<legend>{{localize 'l5r5e.equipment' }}</legend>
{{> 'systems/l5r5e/templates/items/item/items.html' }}
</fieldset>
</article>
{{!-- Experience Tab --}}

View File

@@ -1,35 +1,35 @@
<ul class="rings">
<li id="earth">
<label class="earth">
<i class="i_earth"></i>
<i class="i_earth dice-picker rollable" data-ring="earth"></i>
<strong>{{ localizeRing 'earth' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.earth" value="{{data.rings.earth}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>
</li>
<li id="air">
<label class="air">
<i class="i_air"></i>
<i class="i_air dice-picker rollable" data-ring="air"></i>
<strong>{{ localizeRing 'air' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.air" value="{{data.rings.air}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>
</li>
<li id="water">
<label class="water">
<i class="i_water"></i>
<i class="i_water dice-picker rollable" data-ring="water"></i>
<strong>{{ localizeRing 'water' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.water" value="{{data.rings.water}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>
</li>
<li id="fire">
<label class="fire">
<i class="i_fire"></i>
<i class="i_fire dice-picker rollable" data-ring="fire"></i>
<strong>{{ localizeRing 'fire' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.fire" value="{{data.rings.fire}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>
</li>
<li id="void">
<label class="void">
<i class="i_void"></i>
<i class="i_void dice-picker rollable" data-ring="void"></i>
<strong>{{ localizeRing 'void' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.void" value="{{data.rings.void}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>

View File

@@ -8,19 +8,19 @@
</label>
{{/each}}
</div>
<fieldset class="section-header flexrow">
<legend class="technique-controls">
{{ localize 'l5r5e.techniques.title' }}
{{#if editable}}
<a data-item-type="technique" class="technique-control item-add" title="{{ localize 'l5r5e.global.add' }}"><i class="fas fa-plus"></i></a>
{{/if}}
</legend>
<ul class="item-list">
{{#each actor.items as |item id|}}
{{#ifCond item.type '==' 'technique'}}
{{> 'systems/l5r5e/templates/items/technique/technique-entry.html' technique=item editable=../editable }}
{{#each actor.data.splitTechniquesList as |list technique|}}
<fieldset class="section-header flexrow">
<legend class="technique-controls">
{{localize (localize 'l5r5e.techniques.{technique}' technique=technique) }}
{{#ifCond ../editable '&&' (lookup ../actor.data.techniques technique)}}
<a data-item-type="technique" class="technique-control item-add" data-tech-type="{{technique}}" title="{{ localize 'l5r5e.global.add' }}"><i class="fas fa-plus"></i></a>
{{/ifCond}}
{{/each}}
</ul>
</fieldset>
</legend>
<ul class="item-list">
{{#each list as |item id|}}
{{> 'systems/l5r5e/templates/items/technique/technique-entry.html' technique=item editable=../../editable }}
{{/each}}
</ul>
</fieldset>
{{/each}}
</div>

View File

@@ -20,20 +20,31 @@
</header>
{{!-- Sheet Body --}}
<section class="sheet-body">
<article>
{{!-- Skills No Tab --}}
{{> 'systems/l5r5e/templates/actors/npc/skill.html' }}
{{> 'systems/l5r5e/templates/actors/npc/conflict.html' }}
</article>
<article>
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="skills">{{ localize 'l5r5e.skills.title' }}</a>
<a class="item" data-tab="conflict">{{ localize 'l5r5e.conflict.title' }}</a>
<a class="item" data-tab="inventory">{{ localize 'l5r5e.inventory' }}</a>
</nav>
{{!-- Techniques Tab --}}
<article class="tab skills" data-group="primary" data-tab="skills">
{{> 'systems/l5r5e/templates/actors/npc/narrative.html' }}
{{> 'systems/l5r5e/templates/actors/npc/techniques.html' }}
</article>
<article>
{{!-- Conflict Tab --}}
<article class="tab conflict" data-group="primary" data-tab="conflict">
{{> 'systems/l5r5e/templates/actors/npc/conflict.html' }}
{{> 'systems/l5r5e/templates/items/weapon/weapons.html' }}
{{> 'systems/l5r5e/templates/items/armor/armors.html' }}
{{> 'systems/l5r5e/templates/items/item/items.html' }}
</article>
<article>
{{> 'systems/l5r5e/templates/actors/npc/techniques.html' }}
{{!-- Inventory Tab --}}
<article class="tab inventory" data-group="primary" data-tab="inventory">
<fieldset class="items-wrapper">
<legend>{{localize 'l5r5e.equipment' }}</legend>
{{> 'systems/l5r5e/templates/items/item/items.html' }}
</fieldset>
</article>
</section>
</form>

View File

@@ -1,35 +1,35 @@
<ul class="rings">
<li id="earth">
<label class="earth">
<i class="i_earth"></i>
<i class="i_earth dice-picker rollable" data-ring="earth"></i>
<strong>{{ localizeRing 'earth' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.earth" value="{{data.rings.earth}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>
</li>
<li id="air">
<li id="air">
<label class="air">
<i class="i_air"></i>
<i class="i_air dice-picker rollable" data-ring="air"></i>
<strong>{{ localizeRing 'air' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.air" value="{{data.rings.air}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>
</li>
<li id="water">
<label class="water">
<i class="i_water"></i>
<i class="i_water dice-picker rollable" data-ring="water"></i>
<strong>{{ localizeRing 'water' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.water" value="{{data.rings.water}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>
</li>
<li id="fire">
<label class="fire">
<i class="i_fire"></i>
<i class="i_fire dice-picker rollable" data-ring="fire"></i>
<strong>{{ localizeRing 'fire' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.fire" value="{{data.rings.fire}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>
</li>
<li id="void">
<label class="void">
<i class="i_void"></i>
<i class="i_void dice-picker rollable" data-ring="void"></i>
<strong>{{ localizeRing 'void' }}</strong>
<input class="centered-input select-on-focus" type="number" name="data.rings.void" value="{{data.rings.void}}" data-dtype="Number" min="1" max="9" placeholder="0"/>
</label>

View File

@@ -1,13 +1,29 @@
<fieldset class="techniques-wrapper section-header flexrow">
<legend class="technique-controls">
{{ localize 'l5r5e.techniques.title' }}
<a data-item-type="technique" class="technique-control item-add" title="{{ localize 'l5r5e.global.add' }}"><i class="fas fa-plus"></i></a>
</legend>
<ul class="item-list">
{{#each actor.items as |item id|}}
{{#ifCond item.type '==' 'technique'}}
{{> 'systems/l5r5e/templates/items/technique/technique-entry.html' technique=item editable=../editable }}
{{/ifCond}}
<div class="checklist">
<i>{{ localize 'l5r5e.techniques.type'}}</i>
{{#each actor.data.techniquesList as |technique|}}
<label>
<input type="checkbox" name="data.techniques.{{technique}}" {{checked (lookup ../actor.data.techniques technique)}} />
{{localizeTechnique technique}}
</label>
{{/each}}
</ul>
</div>
{{#each actor.data.splitTechniquesList as |list technique|}}
<fieldset class="section-header flexrow">
<legend class="technique-controls">
{{localize (localize 'l5r5e.techniques.{technique}' technique=technique) }}
{{#ifCond ../editable '&&' (lookup ../actor.data.techniques technique)}}
<a data-item-type="technique" class="technique-control item-add" data-tech-type="{{technique}}" title="{{ localize 'l5r5e.global.add' }}"><i class="fas fa-plus"></i></a>
{{/ifCond}}
</legend>
<ul class="item-list">
{{#each list as |item id|}}
{{> 'systems/l5r5e/templates/items/technique/technique-entry.html' technique=item editable=../../editable }}
{{/each}}
</ul>
</fieldset>
{{/each}}
</fieldset>

View File

@@ -1,17 +1,11 @@
<div class="l5r5e dice-roll">
{{#if isPublicRoll }}
{{#if isPublicRoll}}
{{#if l5r5e.stance}}
<div class="l5r5e profil">
<header class="part-header flexrow chat-profil">
<span class="chat-profil-element">
<img class="profile-img"
src="{{#if l5r5e.actor.img}}{{l5r5e.actor.img}}{{else}}icons/svg/mystery-man.svg{{/if}}"
data-edit="img"
height="40"
width="40"
alt="{{#if l5r5e.actor.name}}{{l5r5e.actor.name}}{{else}}mystery-man{{/if}}"
>
<img class="profile-img" src="{{#if l5r5e.actor.img}}{{l5r5e.actor.img}}{{else}}icons/svg/mystery-man.svg{{/if}}" data-edit="img" alt="{{#if l5r5e.actor.name}}{{l5r5e.actor.name}}{{else}}mystery-man{{/if}}" />
</span>
<span class="chat-profil-element">
@@ -27,57 +21,81 @@
</span>
<span class="chat-profil-element">
{{#if l5r5e.summary.difficultyHidden}}
{{#if l5r5e.difficultyHidden}}
{{localize 'l5r5e.chatdices.difficulty_hidden'}}
{{else}}
{{localize 'l5r5e.chatdices.difficulty'}} {{l5r5e.summary.difficulty}}
{{localize 'l5r5e.chatdices.difficulty'}} {{l5r5e.difficulty}}
{{/if}}
{{#if l5r5e.summary.voidPointUsed}}
<br><i class="i_void" title="{{localize 'l5r5e.chatdices.void_point_used'}}"></i>
<br>
{{#if l5r5e.voidPointUsed}}
<i class="i_void" title="{{localize 'l5r5e.chatdices.void_point_used'}}"></i>
{{/if}}
{{#if l5r5e.skillAssistance}}
<i class="i_skill" title="{{l5r5e.skillAssistance}}x {{localize 'l5r5e.chatdices.assistance_used'}}"></i>
{{/if}}
</span>
</header>
</div>
{{/if}}
<div class="l5r5e dice-formula">{{formula}}</div>
<div class="l5r5e dice-formula">{{#if l5r5e.initialFormula}}{{l5r5e.initialFormula}}{{else}}{{formula}}{{/if}}</div>
<div class="l5r5e dice-result">
{{#if l5r5e.dicesTypes.l5r}}
<div class="l5r5e item-display dices-l5r">
{{!-- Dices list --}}
{{#each l5r5e.dices}}
{{#if this.diceTypeL5r}}
{{#each this.rolls}}
<span class="l5r5e chat-dice">{{{this.result}}}</span>
{{/each}}
{{/if}}
{{#if this.diceTypeL5r}}
{{#each this.rolls}}
<span class="l5r5e fas chat-dice {{this.classes}}">{{{this.result}}}</span>
{{/each}}
{{/if}}
{{/each}}
{{!-- Roll & Keep Button --}}
{{#if l5r5e.canRnK}}
{{^if l5r5e.dicesTypes.std}}
<button class="l5r5e chat-dice-rnk">{{localize "l5r5e.chatdices.roll_n_keep"}}</button>
{{/if}}
{{#l5r5e.summary}}
<ul>
<li>{{localize "l5r5e.chatdices.successes"}}: {{this.success}}</li>
{{!-- summary symbols --}}
<div class="l5r5e dice-total dice-total-rnk">
{{#if success}}
<i class="i_success" title="{{localize 'l5r5e.chatdices.successes'}}"></i>x{{success}}
{{/if}}
{{#if explosive}}
<li>{{localize "l5r5e.chatdices.explosives"}}: {{this.explosive}}</li>
<i class="i_explosive" title="{{localize 'l5r5e.chatdices.explosives'}}"></i>x{{explosive}}
{{/if}}
{{#if opportunity}}
<li>{{localize "l5r5e.chatdices.opportunities"}}: {{this.opportunity}}</li>
<i class="i_opportunity" title="{{localize 'l5r5e.chatdices.opportunities'}}"></i>x{{opportunity}}
{{/if}}
{{#if strife}}
<li>{{localize "l5r5e.chatdices.strife"}}: {{this.strife}}</li>
<i class="i_strife" title="{{localize 'l5r5e.chatdices.strife'}}"></i>x{{strife}}
{{/if}}
</ul>
</div>
{{!-- Result text --}}
{{#if ../l5r5e.difficultyHidden}}
<div class="l5r5e dice-result-rnk unknown">
{{totalSuccess}} {{localize 'l5r5e.chatdices.successes'}}
</div>
{{else}}
<div class="l5r5e dice-result-rnk {{#ifCond totalSuccess '>=' ../l5r5e.difficulty}}success{{else}}fail{{/ifCond}}">
{{#ifCond totalSuccess '>=' ../l5r5e.difficulty}}
{{localize "l5r5e.chatdices.success_text"}} <i title="{{totalBonus}} {{localize 'l5r5e.chatdices.successes'}}">({{totalBonus}} {{localize "l5r5e.chatdices.bonus_text"}})</i>
{{else}}
{{localize "l5r5e.chatdices.fail_text"}}
{{/ifCond}}
</div>
{{/if}}
{{/l5r5e.summary}}
</div>
{{/if}}
{{!-- Regular dices --}}
{{#if l5r5e.dicesTypes.std}}
<div class="l5r5e dices-std">
{{#each results}}
@@ -90,7 +108,6 @@
{{{tooltip}}}
<h4 class="l5r5e dice-total dice-total-std">{{total}}</h4>
{{/if}}
</div>
{{/if}}

View File

@@ -3,28 +3,16 @@
{{!-- First line--}}
<tr>
<td class="profil center">
<img class="profile-img"
src="{{#if actor.img}}{{actor.img}}{{else}}icons/svg/mystery-man.svg{{/if}}"
data-edit="img"
height="200"
width="200"
alt="{{#if actor.name}}{{actor.name}}{{else}}mystery-man{{/if}}"
>
<img class="profile-img" src="{{#if actor.img}}{{actor.img}}{{else}}icons/svg/mystery-man.svg{{/if}}" data-edit="img" alt="{{#if actor.name}}{{actor.name}}{{else}}mystery-man{{/if}}" />
</td>
<td class="rings center">
<td class="center">
<ul class="rings">
{{#each ringsList}}
<li id="{{this.id}}">
<label class="attribute-label {{this.id}} centered-input ring-selection pointer-choice {{#ifCond ../data.ring.id '==' this.id}}ring-selected{{/ifCond}}">
<i class="i_{{this.id}}"></i>
<strong>{{this.label}}</strong>
<input class="centered-input approach_{{this.id}}"
type="text"
name="approach"
data-ringid="{{this.id}}"
value="{{this.value}}"
readonly="readonly"
/>
<input class="centered-input approach_{{this.id}}" type="text" name="approach" data-ringid="{{this.id}}" value="{{this.value}}" readonly="readonly" />
</label>
</li>
{{/each}}
@@ -32,33 +20,19 @@
</td>
<td class="skill">
{{#if data.skill.name}}
<div>
<label>{{localizeSkill data.skill.cat 'title'}}</label>
</div>
{{#if actorIsPc}}
<div>
<label>{{data.skill.name}}</label>
</div>
{{/if}}
<div>
<label id="stance_label">{{localizeSkill data.skill.cat data.ring.id}}</label>
</div>
<div id="skill_default_value" class="dice-container pointer-choice">
<img src="systems/l5r5e/assets/dices/default/skill_blank.svg" alt="1">
<div class="dice-value">
<span class="dice-skill" type="text" name="skill_{{data.skill.name}}">{{data.skill.default_value}}</span>
</div>
</div>
<label id="stance_label">{{localizeSkill data.skill.cat data.ring.id}}</label>
<div id="skill_default_value" class="dice-container pointer-choice">
<img src="systems/l5r5e/assets/dices/default/skill_blank.svg" alt="1">
<div class="dice-value">
<span class="dice-skill" type="text" name="skill_{{data.skill.name}}">{{data.skill.defaultValue}}</span>
</div>
</div>
{{else}}
<img class="profile-img"
src="systems/l5r5e/assets/imgs/noskill.webp"
data-edit="img"
height="200"
width="200"
alt="no skill selected"
>
<img class="profile-img" src="systems/l5r5e/assets/imgs/noskill.webp" data-edit="img" alt="no skill selected" />
{{/if}}
</td>
</tr>
@@ -81,7 +55,7 @@
<td>
{{#if data.difficulty.hidden}}
<label>
<input type="checkbox" id="diff_add_void_point" name="difficulty.add_void_point" value="1" {{checked data.difficulty.add_void_point}}>
<input type="checkbox" id="diff_add_void_point" name="difficulty.addVoidPoint" value="1" {{checked data.difficulty.addVoidPoint}} />
+1 <i class="i_void" title="{{localize 'l5r5e.dicepicker.void_point_tooltip'}}"></i>
</label>
{{else}}
@@ -92,9 +66,9 @@
<div class="third">
<div class="dice-container">
<img src="systems/l5r5e/assets/dices/default/3d/blank.png" alt="1">
<img src="systems/l5r5e/assets/dices/default/3d/blank.png" alt="1" />
<div class="dice-value">
<input class="input-dice" type="text" name="difficulty.value" value="{{data.difficulty.value}}" readonly="readonly">
<input class="input-dice" type="text" name="difficulty.value" value="{{data.difficulty.value}}" readonly="readonly" />
</div>
</div>
</div>
@@ -113,9 +87,9 @@
<div class="third">
<div class="dice-container">
<img src="systems/l5r5e/assets/dices/default/ring_blank.svg" alt="1">
<img src="systems/l5r5e/assets/dices/default/ring_blank.svg" alt="1" />
<div class="dice-value">
<input class="input-dice input-dice-ring" type="text" name="ring.value" value="{{data.ring.value}}" readonly="readonly">
<input class="input-dice input-dice-ring" type="text" name="ring.value" value="{{data.ring.value}}" readonly="readonly" />
</div>
</div>
</div>
@@ -132,9 +106,9 @@
<div class="third">
<div class="dice-container">
<img src="systems/l5r5e/assets/dices/default/skill_blank.svg" alt="1">
<img src="systems/l5r5e/assets/dices/default/skill_blank.svg" alt="1" />
<div class="dice-value">
<input class="input-dice input-dice-skill" type="text" name="skill.value" value="{{data.skill.value}}" readonly="readonly">
<input class="input-dice input-dice-skill" type="text" name="skill.value" value="{{data.skill.value}}" readonly="readonly" />
</div>
</div>
</div>
@@ -150,7 +124,7 @@
<td>
{{^if difficultyHiddenIsLock}}
<label>
<input type="checkbox" id="diff_hidden" name="difficulty.hidden" value="1" {{checked data.difficulty.hidden}}>
<input type="checkbox" id="diff_hidden" name="difficulty.hidden" value="1" {{checked data.difficulty.hidden}} />
{{localize 'l5r5e.dicepicker.difficulty_hidden_label'}}
</label>
{{/if}}
@@ -158,18 +132,35 @@
<td>
{{#if canUseVoidPoint}}
<label>
<input type="checkbox" id="use_void_point" name="useVoidPoint" value="1" {{checked data.useVoidPoint}}>
<input type="checkbox" id="use_void_point" name="useVoidPoint" value="1" {{checked data.useVoidPoint}} />
{{localize 'l5r5e.dicepicker.use_void_point_label'}} <i class="i_void" title="{{localize 'l5r5e.dicepicker.void_point_tooltip'}}"></i>
</label>
{{/if}}
</td>
<td>
<div class="third">
<i class="assistance pointer-choice fa fa-minus-square" data-item="assistance" data-value="-1"></i>
</div>
<div class="third">
<i class="i_skill"></i> {{localize 'l5r5e.dicepicker.skill_assistance_label'}}
<div class="dice-container">
<img src="systems/l5r5e/assets/dices/default/3d/blank.png" alt="1" />
<div class="dice-value">
<input class="input-dice" type="text" name="skill.assistance" value="{{data.skill.assistance}}" readonly="readonly" />
</div>
</div>
</div>
<div class="third">
<i class="assistance pointer-choice fa fa-plus-square" data-item="assistance" data-value="1"></i>
</div>
</td>
</tr>
<tr>
<td colspan="3">
<button name="roll" type="submit" {{#if disableSubmit}}disabled{{/if}}>{{localize 'l5r5e.dicepicker.roll_label'}} <i class='fas fa-arrow-circle-right'></i></button>
</td>
</tr>
</table>
<div class="form-group">
<button name="roll" type="submit" {{#if disableSubmit}}disabled{{/if}}>{{localize 'l5r5e.dicepicker.roll_label'}} <i class='fas fa-arrow-circle-right'></i></button>
</div>
</form>
</form>

View File

@@ -1,6 +1,14 @@
<form class="l5r5e gm-tools-dialog" autocomplete="off">
<p class="gm-tools-container">
<a class="difficulty_hidden"><i class="fa fa-eye{{#if data.difficultyHidden}}-slash{{/if}}"></i></a>
<strong class="difficulty">{{data.difficulty}}</strong>
</p>
</form>
<ul class="gm-tools-container">
<li class="difficulty_hidden" title="{{localize 'l5r5e.gm_toolbox.difficulty_hidden'}}">
<i class="fa fa-eye{{#if data.difficultyHidden}}-slash{{/if}}"></i>
<strong class="difficulty" title="{{localize 'l5r5e.gm_toolbox.difficulty'}}">{{data.difficulty}}</strong>
</li>
<li class="gm_actor_updates sleep" data-type="sleep" title="{{localize 'l5r5e.gm_toolbox.sleep'}}">
<i class="fa fa-bed"></i>
</li>
<li class="gm_actor_updates scene_end" data-type="scene_end" title="{{localize 'l5r5e.gm_toolbox.scene_end'}}">
<i class="fas fa-star-half-alt"></i>
</li>
</ul>
</form>

View File

@@ -1,90 +1,130 @@
<form class="l5r5e roll-n-keep-dialog" autocomplete="off">
<form class="{{cssClass}}" autocomplete="off">
{{!-- Profil --}}
<div class="l5r5e profil">
<header class="part-header flexrow chat-profil">
<span class="chat-profil-element">
<img class="profile-img"
src="{{#if l5r5e.actor.img}}{{l5r5e.actor.img}}{{else}}icons/svg/mystery-man.svg{{/if}}"
data-edit="img"
height="40"
width="40"
alt="{{#if l5r5e.actor.name}}{{l5r5e.actor.name}}{{else}}mystery-man{{/if}}"
>
</span>
<span class="chat-profil-element">
<header class="l5r5e profil part-header flexrow chat-profil">
<ul>
<li class="chat-profil-element">
<img class="profile-img" src="{{#if l5r5e.actor.img}}{{l5r5e.actor.img}}{{else}}icons/svg/mystery-man.svg{{/if}}" data-edit="img" alt="{{#if l5r5e.actor.name}}{{l5r5e.actor.name}}{{else}}mystery-man{{/if}}" />
</li>
<li class="chat-profil-element">
<i class="chat-profil-stance {{l5r5e.stance}} i_{{l5r5e.stance}}" title="{{localizeRing l5r5e.stance}}"></i>
</span>
<span class="chat-profil-element-skill">
</li>
<li class="chat-profil-element-skill">
{{#if l5r5e.skillId}}
{{localizeSkillId l5r5e.skillId}}
{{else}}
{{#if l5r5e.skillCatId}}{{localizeSkill l5r5e.skillCatId 'title'}}{{/if}}
{{/if}}
</span>
<span class="chat-profil-element">
{{#if l5r5e.summary.difficultyHidden}}
</li>
<li class="chat-profil-element">
{{#if l5r5e.difficultyHidden}}
{{localize 'l5r5e.chatdices.difficulty_hidden'}}
{{else}}
{{localize 'l5r5e.chatdices.difficulty'}} {{l5r5e.summary.difficulty}}
{{localize 'l5r5e.chatdices.difficulty'}} {{l5r5e.difficulty}}
{{/if}}
{{#if l5r5e.summary.voidPointUsed}}
<br><i class="i_void" title="{{localize 'l5r5e.chatdices.void_point_used'}}"></i>
<br />
{{#if l5r5e.voidPointUsed}}
<i class="i_void" title="{{localize 'l5r5e.chatdices.void_point_used'}}"></i>
{{/if}}
</span>
</header>
</div>
{{#if l5r5e.skillAssistance}}
<i class="i_skill" title="{{l5r5e.skillAssistance}}x {{localize 'l5r5e.chatdices.assistance_used'}}"></i>
{{/if}}
</li>
</ul>
</header>
<section class="rnk-ct">
{{!-- Body --}}
{{#if options.editable}}
{{!-- Face Rings --}}
<fieldset class="dropbox swap">
<legend class="section-header">
<i class="fa fa-sign-in-alt" aria-hidden="true"></i> {{localize 'l5r5e.roll_n_keep.swap_drop_here'}} <i class="fa fa-arrows-alt-h"></i>
</legend>
{{!-- Discard & ReRoll --}}
<table>
<tr>
<td>
{{#each data.swapDiceFaces.rings}}
<div class="dice dropbox faces-change" data-type="swap" data-face="{{this}}" data-die="RingDie">
<img src="{{getDiceFaceUrl 'RingDie' this}}" alt="{{this}}" />
</div>
{{/each}}
</fieldset>
{{!-- Center --}}
<article class="rnk-center">
{{!-- Discard & ReRoll --}}
<fieldset class="dropbox discards" data-type="discard">
<legend class="section-header"><i class="fa fa-arrow-down" aria-hidden="true"></i> {{ localize 'l5r5e.roll_n_keep.discard_drop_here' }}</legend>
<legend class="section-header">
<i class="fa fa-sign-in-alt" aria-hidden="true"></i> {{localize 'l5r5e.roll_n_keep.discard_drop_here'}} <i class="fa fa-times"></i>
</legend>
</fieldset>
</td>
<td>
&nbsp;
</td>
<td>
<fieldset class="dropbox rerolls" data-type="reroll">
<legend class="section-header"><i class="fa fa-arrow-down" aria-hidden="true"></i> {{ localize 'l5r5e.roll_n_keep.reroll_drop_here' }}</legend>
<legend class="section-header">
<i class="fa fa-sign-in-alt" aria-hidden="true"></i> {{localize 'l5r5e.roll_n_keep.reroll_drop_here'}} <i class="fa fa-redo-alt"></i>
</legend>
</fieldset>
</td>
</tr>
</table>
{{!-- DiceList history --}}
<table>
{{#each data.dicesList as |item idxStep|}}
<tr>
{{#each item as |dice idxDie|}}
<td>
<div class="dice {{this.choice}} {{#ifCond (lookup ../../draggableList idxDie) '==' idxStep }}draggable{{/ifCond}}" data-step="{{idxStep}}" data-die="{{idxDie}}">
{{#if this.img}}
<img src="{{this.img}}" alt="{{idxStep}}_{{idxDie}}" />
{{/if}}
</div>
</td>
{{/each}}
</tr>
{{/each}}
</table>
{{!-- Keep --}}
<table>
<tr>
<td>
{{!-- DiceList history --}}
<table>
{{#each data.dicesList as |item idxStep|}}
<tr>
{{#each item as |dice idxDie|}}
<td>
{{#if dice.face}}
{{#if dice.newFace}}
<span class="fas dice-ct {{dice.choice}}">
<img class="dice {{dice.choice}}{{#ifCond ../../data.currentStep '==' idxStep}} draggable{{/ifCond}}" data-step="{{idxStep}}" data-die="{{idxDie}}" src="{{getDiceFaceUrl dice.type dice.newFace}}" alt="{{idxStep}}_{{idxDie}}" />
</span>
{{else}}
<span class="fas dice-ct {{dice.choice}}">
<img class="dice {{dice.choice}}{{#ifCond ../../data.currentStep '==' idxStep}} draggable{{/ifCond}}" data-step="{{idxStep}}" data-die="{{idxDie}}" src="{{getDiceFaceUrl dice.type dice.face}}" alt="{{idxStep}}_{{idxDie}}" />
</span>
{{/if}}
{{/if}}
</td>
{{/each}}
</tr>
{{/each}}
</table>
{{!-- Keep --}}
<fieldset class="dropbox keeps" data-type="keep">
<legend class="section-header"><i class="fa fa-arrow-down" aria-hidden="true"></i> {{ localize 'l5r5e.roll_n_keep.keep_drop_here' }}</legend>
<legend class="section-header">
<i class="fa fa-sign-in-alt" aria-hidden="true"></i> {{localize 'l5r5e.roll_n_keep.keep_drop_here'}} <i class="fa fa-check"></i>
</legend>
</fieldset>
</td>
</tr>
</table>
<button id="finalize" name="finalize" type="button">{{ localize 'l5r5e.roll_n_keep.bt_validate' }} <i class='fas fa-arrow-circle-right'></i></button>
</article>
{{!-- Face Skills --}}
<fieldset class="dropbox swap">
<legend class="section-header">
<i class="fa fa-sign-in-alt" aria-hidden="true"></i> {{localize 'l5r5e.roll_n_keep.swap_drop_here'}} <i class="fa fa-arrows-alt-h"></i>
</legend>
{{#each data.swapDiceFaces.skills}}
<div class="dice dropbox faces-change" data-type="swap" data-face="{{this}}" data-die="AbilityDie">
<img src="{{getDiceFaceUrl 'AbilityDie' this}}" alt="{{this}}" />
</div>
{{/each}}
</fieldset>
<button id="finalize" name="finalize" type="button" {{#if data.submitDisabled}}disabled{{/if}}>
{{ localize 'l5r5e.roll_n_keep.bt_validate' }} <i class="fas fa-arrow-circle-right"></i>
</button>
{{else}}
<table>
{{!-- Non editable DiceList history --}}
{{#each data.dicesList as |item idxStep|}}
<tr>
{{#each item as |dice idxDie|}}
<td>
{{#if dice.face}}
{{#if dice.newFace}}
<span class="fas dice-ct {{dice.choice}}">
<img class="dice {{dice.choice}}" src="{{getDiceFaceUrl dice.type dice.newFace}}" alt="{{idxStep}}_{{idxDie}}" />
</span>
{{else}}
<span class="fas dice-ct {{dice.choice}}">
<img class="dice {{dice.choice}}" src="{{getDiceFaceUrl dice.type dice.face}}" alt="{{idxStep}}_{{idxDie}}" />
</span>
{{/if}}
{{/if}}
</td>
{{/each}}
</tr>
{{/each}}
</table>
{{/if}}
</section>
</form>

View File

@@ -4,7 +4,7 @@
<li class="item-name">{{ advancement.name }}</li>
{{#if editable}}
<li data-item-id="{{advancement._id}}" class="item-control item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
<li data-item-id="{{advancement._id}}" class="item-control item-delete" title="{{localize 'l5r5e.global.delete'}}"><i class="fas fa-trash"></i></li>
<li data-item-id="{{advancement._id}}" class="item-control item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
{{/if}}
</ul>
{{#if advancement.data.description}}

View File

@@ -8,7 +8,7 @@
</li>
{{#if editable}}
<li data-item-id="{{item._id}}" class="item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
<li data-item-id="{{item._id}}" class="item-delete" title="{{localize 'l5r5e.global.delete'}}"><i class="fas fa-trash"></i></li>
<li data-item-id="{{item._id}}" class="item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
{{/if}}
</ul>
<ul class="item-properties">

View File

@@ -1,8 +1,8 @@
<fieldset class="armors-content">
<legend class="section-header">
{{ localize 'l5r5e.armors.title' }}
{{localize 'l5r5e.armors.title'}}
{{#if editable}}
<a data-item-type="armor" class="armor-control item-add" title="{{ localize 'l5r5e.global.add'}}"><i class="fas fa-plus"></i></a>
<a data-item-type="armor" data-item-eqquiped="true" class="armor-control item-add" title="{{localize 'l5r5e.global.add'}}"><i class="fas fa-plus"></i></a>
{{/if}}
</legend>
<ul class="item-list">

View File

@@ -5,7 +5,7 @@
{{#if editable}}
<li data-item-id="{{item._id}}" data-type="equipped" class="item-equip equip-readied-control" title="{{localize 'l5r5e.armors.equipped'}}"><i class="fas {{#if item.data.equipped}}fa-tshirt{{else}}fa-weight-hanging{{/if}}"></i></li>
<li data-item-id="{{item._id}}" class="item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
<li data-item-id="{{item._id}}" class="item-delete" title="{{localize 'l5r5e.global.delete'}}"><i class="fas fa-trash"></i></li>
<li data-item-id="{{item._id}}" class="item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
{{/if}}
</ul>
<ul class="item-properties">

View File

@@ -1,15 +1,13 @@
<fieldset class="items-content">
<legend>
{{ localize 'l5r5e.equipment' }}
{{#if editable}}
<a data-item-type="item" class="item-control item-add" title="{{ localize 'l5r5e.global.add'}}"><i class="fas fa-plus"></i></a>
{{#each data.splitItemsList as |cat type|}}
<h3>
{{localize (localize 'l5r5e.{type}s.title' type=type) }}
{{#if ../editable}}
<a data-item-type="{{type}}" class="item-control item-add" title="{{ localize 'l5r5e.global.add'}}"><i class="fas fa-plus"></i></a>
{{/if}}
</legend>
</h3>
<ul class="item-list">
{{#each actor.items as |item id|}}
{{#ifCond '["item", "armor", "weapon"]' 'includes' item.type}}
{{> 'systems/l5r5e/templates/items/item/item-entry.html' item=item id=id editable=../editable }}
{{/ifCond}}
{{#each cat as |item id|}}
{{> 'systems/l5r5e/templates/items/item/item-entry.html' item=item id=id editable=../../editable }}
{{/each}}
</ul>
</fieldset>
{{/each}}

View File

@@ -4,7 +4,7 @@
<li class="item-name">{{ peculiarity.name }}</li>
{{#if editable}}
<li data-item-id="{{peculiarity._id}}" class="item-control item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
<li data-item-id="{{peculiarity._id}}" class="item-control item-delete" title="{{localize 'l5r5e.global.delete'}}"><i class="fas fa-trash"></i></li>
<li data-item-id="{{peculiarity._id}}" class="item-control item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
{{/if}}
</ul>
{{#if peculiarity.data.types}}

View File

@@ -3,7 +3,7 @@
<li class="item-img"><img src="{{item.img}}" title="{{item.name}}" width="32px" height="32px"/></li>
<li class="item-name">{{ item.name }}</li>
{{#if editable}}
<li class="property-delete" title="{{localize 'l5r5e.global.delete'}}"><i class="fas fa-trash"></i></li>
<li class="property-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
{{/if}}
</ul>
{{#if item.data.data.description}}

View File

@@ -4,7 +4,7 @@
<li class="item-name">{{ technique.name }}</li>
{{#if editable}}
<li data-item-id="{{technique._id}}" class="item-control item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
<li data-item-id="{{technique._id}}" class="item-control item-delete" title="{{localize 'l5r5e.global.delete'}}"><i class="fas fa-trash"></i></li>
<li data-item-id="{{technique._id}}" class="item-control item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
{{/if}}
</ul>
{{#if technique.data.description}}

View File

@@ -9,7 +9,7 @@
</li>
{{#if editable}}
<li data-item-id="{{item._id}}" class="item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
<li data-item-id="{{item._id}}" class="item-delete" title="{{localize 'l5r5e.global.delete'}}"><i class="fas fa-trash"></i></li>
<li data-item-id="{{item._id}}" class="item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
{{/if}}
</ul>
<ul class="item-properties">

View File

@@ -1,8 +1,8 @@
<fieldset class="weapons-content">
<legend class="section-header">
{{ localize 'l5r5e.weapons.title' }}
{{localize 'l5r5e.weapons.title'}}
{{#if editable}}
<a data-item-type="weapon" class="weapon-control item-add" title="{{ localize 'l5r5e.global.add'}}"><i class="fas fa-plus"></i></a>
<a data-item-type="weapon" data-item-eqquiped="true" class="weapon-control item-add" title="{{localize 'l5r5e.global.add'}}"><i class="fas fa-plus"></i></a>
{{/if}}
</legend>
<ul class="item-list">