Compare commits

...

16 Commits

Author SHA1 Message Date
Vlyan
87fd300a22 forgot to add skill in folders 2023-12-14 10:26:37 +01:00
Vlyan
fe2aa5dd25 merge 2023-12-14 10:10:03 +01:00
Vlyan
6f7b00328e Merge branch 'dev' into dev_skillslist
# Conflicts:
#	CHANGELOG.md
#	system/lang/en-en.json
#	system/lang/fr-fr.json
#	system/scripts/actor.js
#	system/scripts/actors/base-character-sheet.js
#	system/scripts/combat.js
#	system/scripts/config.js
#	system/scripts/dice/dice-picker-dialog.js
#	system/scripts/dice/roll-n-keep-dialog.js
#	system/scripts/gm/gm-monitor.js
#	system/scripts/gm/gm-toolbox.js
#	system/scripts/hooks.js
#	system/scripts/items/technique-sheet.js
#	system/scripts/main-l5r5e.js
#	system/scripts/migration.js
#	system/scripts/preloadTemplates.js
#	system/scripts/settings.js
#	system/scripts/socket-handler.js
#	system/styles/l5r5e.css
#	system/system.json
#	system/templates/actors/character-sheet.html
2023-12-14 10:08:41 +01:00
Vlyan
67b7601fa1 tmp 2023-04-05 14:11:44 +02:00
Vlyan
87096ab286 Merge branch 'dev' into dev_skillslist 2023-03-30 09:09:44 +02:00
Vlyan
273531f522 Merge branch 'dev' into dev_skillslist
# Conflicts:
#	CHANGELOG.md
#	system/styles/l5r5e.css
#	system/system.json
2023-03-26 11:22:28 +02:00
Vlyan
c1bf9644f8 fix : Prepare values are sometimes null, and default do nothing for it 2023-03-23 09:34:40 +01:00
Vlyan
1b68d33bd2 converting advancement to dynamic list skills 2023-03-15 17:44:06 +01:00
Vlyan
a24e775001 fix tech sheet 2023-03-15 15:01:40 +01:00
Vlyan
823b883d4e fix #44 Lists not showing correctly in journal 2023-03-06 13:52:43 +01:00
Vlyan
12c8a70f60 Added modifier to skills 2023-01-17 10:10:58 +01:00
Vlyan
1d42d2970d Added new dialog settings to configure default skill list.
Added uuid compatibility for tooltips
2023-01-13 15:38:15 +01:00
Vlyan
0b3816587b migration added a check on skillvalue 2023-01-11 09:46:16 +01:00
Vlyan
710afd9804 Working on Skills and some fixes
- Added Skills to character sheet
- Added migration to old skills
- Added icon on skills
- Added actor.isNpc
- Money out of system
2023-01-08 15:12:22 +01:00
Vlyan
1ec9e65ca5 added ns in CONFIG.l5r5e.systemName 2023-01-08 10:32:35 +01:00
Vlyan
5fbb34d882 Debuting the conversion of skills to items list : Db and item Sheet 2023-01-07 15:57:44 +01:00
46 changed files with 1161 additions and 336 deletions

View File

@@ -1,6 +1,10 @@
# Changelog # Changelog
Date format : day/month/year Date format : day/month/year
## 2.0.0 - xx/xx/2024 - Skill list
- Skills are now items, this can break things update with caution. Save before upgrading !
- Added a new dialog settings to configure default skills list.
## 1.11.0 - 13/12/2023 - Little fixes ## 1.11.0 - 13/12/2023 - Little fixes
- 20Q : - 20Q :
- Starting techniques now have a limit of 6 techniques instead of 5 (see Celestial Realms : `Moshi Sun Sentinel School`). - Starting techniques now have a limit of 6 techniques instead of 5 (see Celestial Realms : `Moshi Sun Sentinel School`).

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="239.5px" height="239.5px" viewBox="0 0 239.5 239.5" style="enable-background:new 0 0 239.5 239.5;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFCC;}
</style>
<g>
<g>
<path class="st0" d="M173.2,103.1c0.8-4.9,1.4-9.8,1.4-14.9c0-48.2-39.1-87.3-87.3-87.3C39.1,0.9,0,40,0,88.2s39.1,87.3,87.3,87.3
c5.7,0,11.3-0.6,16.7-1.6c0-0.1,0-0.2,0-0.3c0-7.6,1.2-14.9,3.5-21.7c-6.4,2-13.1,3.1-20.1,3.1c-36.8,0-66.7-29.9-66.7-66.7
s29.9-66.7,66.7-66.7s66.7,29.9,66.7,66.7c0,6.5-1,12.9-2.8,18.8C158.2,104.6,165.5,103.2,173.2,103.1z"/>
<path class="st0" d="M87.3,26.8c-33.8,0-61.4,27.5-61.4,61.4c0,33.8,27.5,61.4,61.4,61.4c8,0,15.7-1.6,22.7-4.4
c6.9-15.6,19.3-28.2,34.7-35.4c2.5-6.7,4-13.9,4-21.5C148.6,54.3,121.1,26.8,87.3,26.8z M110.6,101.7c0,5.4-4.4,9.8-9.8,9.8H73.7
c-5.4,0-9.8-4.4-9.8-9.8v-27c0-5.4,4.4-9.8,9.8-9.8h27.1c5.4,0,9.8,4.4,9.8,9.8V101.7z"/>
<path class="st0" d="M174.4,108.4c-0.8,0-1.6,0.1-2.4,0.1c-8.2,0.3-16,2-23.1,5.1c-1.3,0.5-2.5,1.2-3.8,1.8
c-1.4,0.7-2.8,1.5-4.2,2.3c-9.5,5.7-17.5,13.8-23.1,23.5c-0.8,1.4-1.5,2.8-2.2,4.3c-0.6,1.2-1.2,2.5-1.7,3.8
c-2.9,7.2-4.6,15-4.7,23.2c0,0.3,0,0.7,0,1c0,35.9,29.2,65.1,65.1,65.1s65.1-29.2,65.1-65.1C239.5,137.6,210.3,108.4,174.4,108.4z
M174.4,226c-28.9,0-52.4-23.5-52.4-52.4c0-1.8,0.1-3.6,0.3-5.4c2.5-24.2,21.6-43.6,45.7-46.6c2.1-0.3,4.3-0.4,6.5-0.4
c28.9,0,52.4,23.5,52.4,52.4C226.9,202.4,203.3,226,174.4,226z"/>
<path class="st0" d="M174.4,126.5c-3.2,0-6.2,0.3-9.2,0.9c-18.9,3.8-33.7,18.9-37.1,37.9c-0.5,2.7-0.8,5.4-0.8,8.2
c0,26,21.1,47.1,47.1,47.1s47.1-21.1,47.1-47.1S200.4,126.5,174.4,126.5z M192.6,184c0,4.2-3.4,7.6-7.6,7.6h-21
c-4.2,0-7.6-3.4-7.6-7.6v-21c0-4.2,3.4-7.6,7.6-7.6h21c4.2,0,7.6,3.4,7.6,7.6V184z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,153 @@
{
"label": "Compétences",
"mapping": {
"description": "system.description",
"book_reference": "system.book_reference"
},
"entries": [
{
"id": "Composition",
"name": "Composition",
"description": "",
"book_reference": "Livre de Règles p.146"
},
{
"id": "Aesthetics",
"name": "Esthétique",
"description": "",
"book_reference": "Livre de Règles p.146"
},
{
"id": "Smithing",
"name": "Forge",
"description": "",
"book_reference": "Livre de Règles p.149"
},
{
"id": "Design",
"name": "Stylisme",
"description": "",
"book_reference": "Livre de Règles p.147"
},
{
"id": "Martial Arts [Melee]",
"name": "Arts martiaux (corps à corps)",
"description": "",
"book_reference": "Livre de Règles p.162"
},
{
"id": "Martial Arts [Ranged]",
"name": "Arts martiaux (distance)",
"description": "",
"book_reference": "Livre de Règles p.163"
},
{
"id": "Martial Arts [Unarmed]",
"name": "Arts martiaux (mains nues)",
"description": "",
"book_reference": "Livre de Règles p.164"
},
{
"id": "Fitness",
"name": "Forme",
"description": "",
"book_reference": "Livre de Règles p.162"
},
{
"id": "Meditation",
"name": "Méditation",
"description": "",
"book_reference": "Livre de Règles p.164"
},
{
"id": "Tactics",
"name": "Tactique",
"description": "",
"book_reference": "Livre de Règles p.165"
},
{
"id": "Commerce",
"name": "Commerce",
"description": "",
"book_reference": "Livre de Règles p.167"
},
{
"id": "Skulduggery",
"name": "Magouilles",
"description": "",
"book_reference": "Livre de Règles p.168"
},
{
"id": "Seafaring",
"name": "Navigation",
"description": "",
"book_reference": "Livre de Règles p.168"
},
{
"id": "Survival",
"name": "Survie",
"description": "",
"book_reference": "Livre de Règles p.169"
},
{
"id": "Labor",
"name": "Travail manuel",
"description": "",
"book_reference": "Livre de Règles p.167"
},
{
"id": "Culture",
"name": "Culture",
"description": "",
"book_reference": "Livre de Règles p.156"
},
{
"id": "Government",
"name": "Gouvernement",
"description": "",
"book_reference": "Livre de Règles p.156"
},
{
"id": "Medicine",
"name": "Médecine",
"description": "",
"book_reference": "Livre de Règles p.157"
},
{
"id": "Sentiment",
"name": "Sentiments",
"description": "",
"book_reference": "Livre de Règles p.158"
},
{
"id": "Theology",
"name": "Théologie",
"description": "",
"book_reference": "Livre de Règles p.158"
},
{
"id": "Command",
"name": "Commandement",
"description": "",
"book_reference": "Livre de Règles p.151"
},
{
"id": "Courtesy",
"name": "Courtoisie",
"description": "",
"book_reference": "Livre de Règles p.152"
},
{
"id": "Games",
"name": "Jeux",
"description": "",
"book_reference": "Livre de Règles p.153"
},
{
"id": "Performance",
"name": "Représentations",
"description": "",
"book_reference": "Livre de Règles p.154"
}
]
}

View File

@@ -22,6 +22,11 @@
"Title": "Custom Compendium Name", "Title": "Custom Compendium Name",
"Hint": "For advanced users that want to change the name of the custom compendiums (Used to disables the embedded ones).", "Hint": "For advanced users that want to change the name of the custom compendiums (Used to disables the embedded ones).",
"Notification": "Unable set Custom Compendium: '{name}'. Is it activated and registered with Babele?" "Notification": "Unable set Custom Compendium: '{name}'. Is it activated and registered with Babele?"
},
"DefaultSkillsList": {
"Title": "Skills",
"Label": "Set Default Skills",
"Hint": "Set default skills list for new characters."
} }
}, },
"TYPES": { "TYPES": {
@@ -31,6 +36,7 @@
"army": "Army" "army": "Army"
}, },
"Item": { "Item": {
"skill": "Skill",
"item": "Item", "item": "Item",
"armor": "Armor", "armor": "Armor",
"weapon": "Weapon", "weapon": "Weapon",
@@ -285,6 +291,7 @@
"skills": { "skills": {
"title": "Skills", "title": "Skills",
"label": "Skill", "label": "Skill",
"category": "Category",
"artisan": { "artisan": {
"title": "Artisan", "title": "Artisan",
"aesthetics": "Aesthetics", "aesthetics": "Aesthetics",

View File

@@ -285,6 +285,7 @@
"skills": { "skills": {
"title": "Habilidades", "title": "Habilidades",
"label": "Habilidad", "label": "Habilidad",
"category": "Categoría",
"artisan": { "artisan": {
"title": "Artesanales", "title": "Artesanales",
"aesthetics": "Estética", "aesthetics": "Estética",

View File

@@ -22,6 +22,11 @@
"Title": "Nom du CustomCompendium", "Title": "Nom du CustomCompendium",
"Hint": "Pour les utilisateurs avancés qui souhaitent modifier le nom du compendium personnalisé (utilisé pour désactiver ceux intégrés).", "Hint": "Pour les utilisateurs avancés qui souhaitent modifier le nom du compendium personnalisé (utilisé pour désactiver ceux intégrés).",
"Notification": "Impossible de définir le compendium personnalisé : '{name}'. Est-il activé et enregistré auprès de Babele ?" "Notification": "Impossible de définir le compendium personnalisé : '{name}'. Est-il activé et enregistré auprès de Babele ?"
},
"DefaultSkillsList": {
"Title": "Compétences",
"Label": "Définir les compétences par défaut",
"Hint": "Définie la liste des compétences par défaut pour les nouveaux personnages."
} }
}, },
"TYPES": { "TYPES": {
@@ -31,6 +36,7 @@
"army": "Armée" "army": "Armée"
}, },
"Item": { "Item": {
"skill": "Compétence",
"item": "Objet", "item": "Objet",
"armor": "Armure", "armor": "Armure",
"weapon": "Arme", "weapon": "Arme",
@@ -285,6 +291,7 @@
"skills": { "skills": {
"title": "Compétences", "title": "Compétences",
"label": "Compétence", "label": "Compétence",
"category": "Catégorie",
"artisan": { "artisan": {
"title": "Artisanales", "title": "Artisanales",
"aesthetics": "Esthétique", "aesthetics": "Esthétique",

View File

@@ -285,6 +285,7 @@
"skills": { "skills": {
"title": "Abilità", "title": "Abilità",
"label": "Abilità", "label": "Abilità",
"category": "Categoria",
"artisan": { "artisan": {
"title": "Artigiane", "title": "Artigiane",
"aesthetics": "Estetica", "aesthetics": "Estetica",

View File

@@ -0,0 +1,24 @@
{"_id":"L5RCoreSkl000001","name":"Composition","permission":{"default":0},"type":"skill","data":{"category":"artisan","description":"","book_reference":"Core Rulebook p.146","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"composition"}},"img":"icons/sundries/scrolls/scroll-symbol-circle-white.webp","effects":[]}
{"_id":"L5RCoreSkl000002","name":"Aesthetics","permission":{"default":0},"type":"skill","data":{"category":"artisan","description":"","book_reference":"Core Rulebook p.146","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"aesthetics"}},"img":"systems/l5r5e/assets/icons/techs/kiho.svg","effects":[]}
{"_id":"L5RCoreSkl000003","name":"Smithing","permission":{"default":0},"type":"skill","data":{"category":"artisan","description":"","book_reference":"Core Rulebook p.149","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"smithing"}},"img":"systems/l5r5e/assets/icons/items/item_pattern.svg","effects":[]}
{"_id":"L5RCoreSkl000004","name":"Design","permission":{"default":0},"type":"skill","data":{"category":"artisan","description":"","book_reference":"Core Rulebook p.147","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"design"}},"img":"systems/l5r5e/assets/icons/items/keikogi.svg","effects":[]}
{"_id":"L5RCoreSkl000005","name":"Martial Arts [Melee]","permission":{"default":0},"type":"skill","data":{"category":"martial","description":"","book_reference":"Core Rulebook p.162","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"melee"}},"img":"systems/l5r5e/assets/icons/weapons/katana.svg","effects":[]}
{"_id":"L5RCoreSkl000006","name":"Martial Arts [Ranged]","permission":{"default":0},"type":"skill","data":{"category":"martial","description":"","book_reference":"Core Rulebook p.163","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"ranged"}},"img":"systems/l5r5e/assets/icons/weapons/crossbow.svg","effects":[]}
{"_id":"L5RCoreSkl000007","name":"Martial Arts [Unarmed]","permission":{"default":0},"type":"skill","data":{"category":"martial","description":"","book_reference":"Core Rulebook p.164","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"unarmed"}},"img":"systems/l5r5e/assets/icons/weapons/unarmed.svg","effects":[]}
{"_id":"L5RCoreSkl000008","name":"Fitness","permission":{"default":0},"type":"skill","data":{"category":"martial","description":"","book_reference":"Core Rulebook p.162","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"fitness"}},"img":"icons/skills/melee/maneuver-daggers-paired-orange.webp","effects":[]}
{"_id":"L5RCoreSkl000009","name":"Meditation","permission":{"default":0},"type":"skill","data":{"category":"martial","description":"","book_reference":"Core Rulebook p.164","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"meditation"}},"img":"systems/l5r5e/assets/icons/techs/ritual.svg","effects":[]}
{"_id":"L5RCoreSkl000010","name":"Tactics","permission":{"default":0},"type":"skill","data":{"category":"martial","description":"","book_reference":"Core Rulebook p.165","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"tactics"}},"img":"systems/l5r5e/assets/icons/items/armor.svg","effects":[]}
{"_id":"L5RCoreSkl000011","name":"Commerce","permission":{"default":0},"type":"skill","data":{"category":"trade","description":"","book_reference":"Core Rulebook p.167","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"commerce"}},"img":"icons/skills/trades/baking-bread-rolls-green.webp","effects":[]}
{"_id":"L5RCoreSkl000012","name":"Skulduggery","permission":{"default":0},"type":"skill","data":{"category":"trade","description":"","book_reference":"Core Rulebook p.168","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"skulduggery"}},"img":"icons/skills/social/intimidation-impressing.webp","effects":[]}
{"_id":"L5RCoreSkl000013","name":"Seafaring","permission":{"default":0},"type":"skill","data":{"category":"trade","description":"","book_reference":"Core Rulebook p.168","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"seafaring"}},"img":"icons/skills/trades/profession-sailing-ship.webp","effects":[]}
{"_id":"L5RCoreSkl000014","name":"Survival","permission":{"default":0},"type":"skill","data":{"category":"trade","description":"","book_reference":"Core Rulebook p.169","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"survival"}},"img":"icons/environment/wilderness/camp-improvised.webp","effects":[]}
{"_id":"L5RCoreSkl000015","name":"Labor","permission":{"default":0},"type":"skill","data":{"category":"trade","description":"","book_reference":"Core Rulebook p.167","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"labor"}},"img":"icons/tools/hand/hammer-and-nail.webp","effects":[]}
{"_id":"L5RCoreSkl000016","name":"Culture","permission":{"default":0},"type":"skill","data":{"category":"scholar","description":"","book_reference":"Core Rulebook p.156","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"culture"}},"img":"icons/sundries/documents/document-sealed-signatures-red.webp","effects":[]}
{"_id":"L5RCoreSkl000017","name":"Government","permission":{"default":0},"type":"skill","data":{"category":"scholar","description":"","book_reference":"Core Rulebook p.156","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"government"}},"img":"systems/l5r5e/assets/icons/items/army_fortification.svg","effects":[]}
{"_id":"L5RCoreSkl000018","name":"Medicine","permission":{"default":0},"type":"skill","data":{"category":"scholar","description":"","book_reference":"Core Rulebook p.157","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"medicine"}},"img":"icons/tools/laboratory/bowl-liquid-pink-yellow-green.webp","effects":[]}
{"_id":"L5RCoreSkl000019","name":"Sentiment","permission":{"default":0},"type":"skill","data":{"category":"scholar","description":"","book_reference":"Core Rulebook p.158","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"sentiment"}},"img":"systems/l5r5e/assets/icons/actors/traditional-japanese-woman.svg","effects":[]}
{"_id":"L5RCoreSkl000020","name":"Theology","permission":{"default":0},"type":"skill","data":{"category":"scholar","description":"","book_reference":"Core Rulebook p.158","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"theology"}},"img":"systems/l5r5e/assets/icons/items/blessed.svg","effects":[]}
{"_id":"L5RCoreSkl000021","name":"Command","permission":{"default":0},"type":"skill","data":{"category":"social","description":"","book_reference":"Core Rulebook p.151","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"command"}},"img":"systems/l5r5e/assets/icons/items/army_cohort.svg","effects":[]}
{"_id":"L5RCoreSkl000022","name":"Courtesy","permission":{"default":0},"type":"skill","data":{"category":"social","description":"","book_reference":"Core Rulebook p.152","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"courtesy"}},"img":"systems/l5r5e/assets/icons/social.svg","effects":[]}
{"_id":"L5RCoreSkl000023","name":"Games","permission":{"default":0},"type":"skill","data":{"category":"social","description":"","book_reference":"Core Rulebook p.153","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"games"}},"img":"icons/sundries/gaming/dice-runed-tan.webp","effects":[]}
{"_id":"L5RCoreSkl000024","name":"Performance","permission":{"default":0},"type":"skill","data":{"category":"social","description":"","book_reference":"Core Rulebook p.154","rank":0,"modifier":0},"sort":100001,"flags":{"l5r5e":{"skillCoreId":"performance"}},"img":"icons/environment/people/group.webp","effects":[]}

View File

@@ -16,10 +16,17 @@ export class ActorL5r5e extends Actor {
docData.img = `${CONFIG.l5r5e.paths.assets}icons/actors/${docData.type}.svg`; docData.img = `${CONFIG.l5r5e.paths.assets}icons/actors/${docData.type}.svg`;
} }
// Only for new actors (New actor has no items, duplicates does)
if (!docData.items) {
// Some tweak on actors prototypeToken // Some tweak on actors prototypeToken
docData.prototypeToken = docData.prototypeToken || {}; docData.prototypeToken = docData.prototypeToken || {};
switch (docData.type) { switch (docData.type) {
case "character": case "character":
// Load skills from core compendiums (only for pc character)
docData.items = [];
await ActorL5r5e.addSkillsFromDefaultList(docData);
// Set token properties
foundry.utils.mergeObject( foundry.utils.mergeObject(
docData.prototypeToken, docData.prototypeToken,
{ {
@@ -73,9 +80,28 @@ export class ActorL5r5e extends Actor {
); );
break; break;
} }
}
await super.create(docData, options); await super.create(docData, options);
} }
/**
* Add all the skills from compendiums to "items"
*/
static async addSkillsFromDefaultList(docData) {
console.log(`L5R5E | Adding default skills to ${docData.name}`);
const skillList = await game.l5r5e.HelpersL5r5e.getSkillsItemsList();
skillList.forEach(item => {
// Get the json data and replace the object id/rank
const tmpData = item.toObject();
tmpData._id = foundry.utils.randomID();
tmpData.system.rank = 0;
docData.items.push(tmpData);
});
}
/** /**
* Entity-specific actions that should occur when the Entity is updated * Entity-specific actions that should occur when the Entity is updated
* @override * @override
@@ -94,7 +120,7 @@ export class ActorL5r5e extends Actor {
context.pack = this.pack; context.pack = this.pack;
// NPC switch between types : Linked actor for Adversary, unlinked for Minion // NPC switch between types : Linked actor for Adversary, unlinked for Minion
if (!!docData["system.type"] && this.type === "npc" && docData["system.type"] !== this.system.type) { if (!!docData["system.type"] && this.isNpc && docData["system.type"] !== this.system.type) {
docData["prototypeToken.actorLink"] = docData["system.type"] === "adversary"; docData["prototypeToken.actorLink"] = docData["system.type"] === "adversary";
} }
@@ -197,38 +223,51 @@ export class ActorL5r5e extends Actor {
* @private * @private
*/ */
async _updateActorFromAdvancement(item, isAdd) { async _updateActorFromAdvancement(item, isAdd) {
if (item && item.type === "advancement") { if (!item || item.type !== "advancement") {
const actor = foundry.utils.duplicate(this.system); return;
}
const actorSystem = foundry.utils.duplicate(this.system);
const itemData = item.system; const itemData = item.system;
if (itemData.advancement_type === "ring") { if (itemData.advancement_type === "ring") {
// Ring // Ring
if (isAdd) { if (isAdd) {
actor.rings[itemData.ring] = Math.min(9, actor.rings[itemData.ring] + 1); actorSystem.rings[itemData.ring] = Math.min(9, actorSystem.rings[itemData.ring] + 1);
} else { } else {
actor.rings[itemData.ring] = Math.max(1, actor.rings[itemData.ring] - 1); actorSystem.rings[itemData.ring] = Math.max(1, actorSystem.rings[itemData.ring] - 1);
}
} else {
// Skill
const skillCatId = CONFIG.l5r5e.skills.get(itemData.skill);
if (skillCatId) {
if (isAdd) {
actor.skills[skillCatId][itemData.skill] = Math.min(
9,
actor.skills[skillCatId][itemData.skill] + 1
);
} else {
actor.skills[skillCatId][itemData.skill] = Math.max(
0,
actor.skills[skillCatId][itemData.skill] - 1
);
}
}
} }
// Update Actor // Update Actor
await this.update({ await this.update({
system: foundry.utils.diffObject(this.system, actor), system: foundry.utils.diffObject(this.system, actorSystem),
}); });
} else {
// Skill
let skillItem = await fromUuid(itemData.skill); // Skill itemUuid
if (!skillItem) {
console.warn("L5R5E | Unknown skill item uuid", itemData.skill);
return;
}
// Out of actor item ?
if (!skillItem.actor || skillItem.actor._id !== this._id) {
const checkItem = this.items.getName(skillItem.name);
if (checkItem) {
skillItem = checkItem;
} else {
throw new Error(`Unable to find "${skillItem.name}" on this actor`);
}
}
const newRank = isAdd
? Math.min(9, skillItem.system.rank + 1)
: Math.max(0, skillItem.system.rank - 1);
if (skillItem.system.rank === newRank) {
return;
}
// Update Item
await skillItem.update({ "system.rank": newRank });
} }
} }
@@ -261,12 +300,20 @@ export class ActorL5r5e extends Actor {
return this.type === "character"; return this.type === "character";
} }
/**
* Return true if this actor is a NPC
* @return {boolean}
*/
get isNpc() {
return this.type === "npc";
}
/** /**
* Return true if this actor is an Adversary * Return true if this actor is an Adversary
* @return {boolean} * @return {boolean}
*/ */
get isAdversary() { get isAdversary() {
return this.type === "npc" && this.system.type === "adversary"; return this.isNpc && this.system.type === "adversary";
} }
/** /**
@@ -274,7 +321,7 @@ export class ActorL5r5e extends Actor {
* @return {boolean} * @return {boolean}
*/ */
get isMinion() { get isMinion() {
return this.type === "npc" && this.system.type === "minion"; return this.isNpc && this.system.type === "minion";
} }
/** /**
@@ -370,7 +417,7 @@ export class ActorL5r5e extends Actor {
if (!this.isCharacterType) { if (!this.isCharacterType) {
return null; return null;
} }
return this.type === "npc" ? this.system.conflict_rank.social : this.system.identity.school_rank; return this.isNpc ? this.system.conflict_rank.social : this.system.identity.school_rank;
} }
/** /**
@@ -381,6 +428,6 @@ export class ActorL5r5e extends Actor {
if (!this.isCharacterType) { if (!this.isCharacterType) {
return null; return null;
} }
return this.type === "npc" ? this.system.conflict_rank.martial : this.system.identity.school_rank; return this.isNpc ? this.system.conflict_rank.martial : this.system.identity.school_rank;
} }
} }

View File

@@ -133,6 +133,7 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
* @param {DragEvent} event * @param {DragEvent} event
*/ */
async _onDrop(event) { async _onDrop(event) {
try {
// *** Everything below here is only needed if the sheet is editable *** // *** Everything below here is only needed if the sheet is editable ***
if (!this.isEditable || this.actor.system.soft_locked) { if (!this.isEditable || this.actor.system.soft_locked) {
console.log("L5R5E | BCS | This sheet is not editable"); console.log("L5R5E | BCS | This sheet is not editable");
@@ -211,7 +212,7 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
return; return;
case "advancement": case "advancement":
// Specific advancements, remove 1 to selected ring/skill // Specific advancements, add x to selected ring/skill
await this.actor.addBonus(item); await this.actor.addBonus(item);
break; break;
@@ -230,6 +231,11 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
itemData = item.toObject(true); itemData = item.toObject(true);
break; break;
case "skill":
itemData.system.rank = 0;
itemData.system.modifier = 0;
break;
case "technique": case "technique":
// School_ability and mastery_ability, allow only 1 per type // School_ability and mastery_ability, allow only 1 per type
if (CONFIG.l5r5e.techniques.get(itemData.system.technique_type)?.type === "school") { if (CONFIG.l5r5e.techniques.get(itemData.system.technique_type)?.type === "school") {
@@ -268,6 +274,10 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
// Finally create the embed // Finally create the embed
return this.actor.createEmbeddedDocuments("Item", [itemData]); return this.actor.createEmbeddedDocuments("Item", [itemData]);
} catch (ex) {
console.warn("L5R5E |", ex.message);
}
} }
/** @inheritdoc */ /** @inheritdoc */

View File

@@ -98,7 +98,7 @@ export class CharacterGeneratorDialog extends FormApplication {
})); }));
return { return {
...(await super.getData(options)), ...(await super.getData(options)),
isNpc: this.actor.type === "npc", isNpc: this.actor.isNpc,
clanList: [{ id: "random", label: game.i18n.localize("l5r5e.global.random") }, ...clans], clanList: [{ id: "random", label: game.i18n.localize("l5r5e.global.random") }, ...clans],
genderList: [ genderList: [
{ id: "random", label: game.i18n.localize("l5r5e.global.random") }, { id: "random", label: game.i18n.localize("l5r5e.global.random") },

View File

@@ -292,7 +292,7 @@ export class CharacterGenerator {
} }
) { ) {
const actorDatas = actor.system; const actorDatas = actor.system;
const isNpc = actor.type === "npc"; const isNpc = actor.isNpc;
// Need to set some required values // Need to set some required values
this.data.age = actorDatas.identity.age || CharacterGenerator.genAge(this.data.avgRingsValue); this.data.age = actorDatas.identity.age || CharacterGenerator.genAge(this.data.avgRingsValue);

View File

@@ -47,8 +47,11 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
// Min rank = 1 // Min rank = 1
this.actor.system.identity.school_rank = Math.max(1, this.actor.system.identity.school_rank); this.actor.system.identity.school_rank = Math.max(1, this.actor.system.identity.school_rank);
// Split Skills
sheetData.data.skillCategories = game.l5r5e.HelpersL5r5e.splitSkillByCategory(sheetData.items);
// Split Money // Split Money
sheetData.data.system.money = this._zeniToMoney(this.actor.system.zeni); sheetData.data.money = this._zeniToMoney(this.actor.system.zeni);
// Split school advancements by rank, and calculate xp spent and add it to total // Split school advancements by rank, and calculate xp spent and add it to total
this._prepareSchoolAdvancement(sheetData); this._prepareSchoolAdvancement(sheetData);
@@ -180,6 +183,12 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
* @param formData * @param formData
*/ */
_updateObject(event, formData) { _updateObject(event, formData) {
// Update items ranks
const formDataObj = foundry.utils.expandObject(formData);
if (formDataObj.skillsValues) {
this._updateItemsRank(foundry.utils.expandObject(formDataObj.skillsValues));
}
// Clan tag trim if autocomplete in school name // Clan tag trim if autocomplete in school name
if ( if (
formData["autoCompleteListName"] === "system.identity.school" && formData["autoCompleteListName"] === "system.identity.school" &&
@@ -194,16 +203,12 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
} }
// Store money in Zeni // Store money in Zeni
if (formData["system.money.koku"] || formData["system.money.bu"] || formData["system.money.zeni"]) { if (formData["money.koku"] || formData["money.bu"] || formData["money.zeni"]) {
formData["system.zeni"] = this._moneyToZeni( formData["system.zeni"] = this._moneyToZeni(
formData["system.money.koku"] || 0, formData["money.koku"] || 0,
formData["system.money.bu"] || 0, formData["money.bu"] || 0,
formData["system.money.zeni"] || 0 formData["money.zeni"] || 0
); );
// Remove fake money object
delete formData["system.money.koku"];
delete formData["system.money.bu"];
delete formData["system.money.zeni"];
} }
// Save computed values // Save computed values
@@ -219,6 +224,20 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
return super._updateObject(event, formData); return super._updateObject(event, formData);
} }
/**
* Update embedded items ranks
* @param {Object<String:String>} itemsValues items new values "ids: rank"
*/
_updateItemsRank(itemsValues) {
Object.entries(itemsValues).forEach(([key, rank]) => {
const item = this.actor.items.get(key);
if (!item || item.system.rank === rank) {
return;
}
item.update({ "system.rank": rank });
});
}
/** /**
* Convert a sum in Zeni to Zeni, Bu and Koku * Convert a sum in Zeni to Zeni, Bu and Koku
* @param {number} zeni * @param {number} zeni

View File

@@ -32,7 +32,7 @@ export class CombatL5r5e extends Combat {
const skillId = messageOptions.skillId const skillId = messageOptions.skillId
? messageOptions.skillId ? messageOptions.skillId
: CONFIG.l5r5e.initiativeSkills[game.settings.get(CONFIG.l5r5e.namespace, "initiative-encounter")]; : CONFIG.l5r5e.initiativeSkills[game.settings.get(CONFIG.l5r5e.namespace, "initiative-encounter")];
const skillCat = CONFIG.l5r5e.skills.get(skillId); const skillCat = CONFIG.l5r5e.skills.get(skillId); // TODO refacto with skill items
// Get score for each combatant // Get score for each combatant
const networkActors = []; const networkActors = [];
@@ -199,6 +199,6 @@ export class CombatL5r5e extends Combat {
* @private * @private
*/ */
static _getWeightByActorType(actor) { static _getWeightByActorType(actor) {
return actor.type === "npc" ? (actor.type === "minion" ? 3 : 2) : 1; return actor.isNpc ? (actor.type === "minion" ? 3 : 2) : 1;
} }
} }

View File

@@ -7,6 +7,7 @@ export const L5R5E = {
money: [50, 10], money: [50, 10],
stances: ["earth", "air", "water", "fire", "void"], stances: ["earth", "air", "water", "fire", "void"],
roles: ["artisan", "bushi", "courtier", "monk", "sage", "shinobi", "shugenja"], roles: ["artisan", "bushi", "courtier", "monk", "sage", "shinobi", "shugenja"],
skillCategories: ["artisan", "martial", "scholar", "social", "trade"],
xp: { xp: {
costPerRank: [0, 20, 24, 32, 44, 60], costPerRank: [0, 20, 24, 32, 44, 60],
bondCostPerRank: [0, 3, 4, 6, 8, 10], bondCostPerRank: [0, 3, 4, 6, 8, 10],
@@ -46,6 +47,7 @@ L5R5E.techniques.set("title_ability", { type: "title", displayInTypes: false });
L5R5E.techniques.set("specificity", { type: "custom", displayInTypes: false }); L5R5E.techniques.set("specificity", { type: "custom", displayInTypes: false });
// *** SkillId - CategoryId *** // *** SkillId - CategoryId ***
// Hardcoded skills are still required for compatibility (migration & olds worlds)
L5R5E.skills = new Map(); L5R5E.skills = new Map();
L5R5E.skills.set("aesthetics", "artisan"); L5R5E.skills.set("aesthetics", "artisan");
L5R5E.skills.set("composition", "artisan"); L5R5E.skills.set("composition", "artisan");

View File

@@ -110,7 +110,7 @@ export class DicePickerDialog extends FormApplication {
* ex: new game.l5r5e.DicePickerDialog({skillId: 'aesthetics', ringId: 'water', actor: game.user.character}).render(true); * ex: new game.l5r5e.DicePickerDialog({skillId: 'aesthetics', ringId: 'water', actor: game.user.character}).render(true);
* *
* Options : * Options :
* actor {Actor} Any `Actor` object instance. Ex : `game.user.character`, `canvas.tokens.controlled[0].actor` * actor {ActorL5r5e} Any `Actor` object instance. Ex : `game.user.character`, `canvas.tokens.controlled[0].actor`
* actorId {string} This is the `id` not the `uuid` of an actor. Ex : "AbYgKrNwWeAxa9jT" * actorId {string} This is the `id` not the `uuid` of an actor. Ex : "AbYgKrNwWeAxa9jT"
* actorName {string} Careful this is case-sensitive. Ex : "Isawa Aki" * actorName {string} Careful this is case-sensitive. Ex : "Isawa Aki"
* difficulty {number} `1` to `9` * difficulty {number} `1` to `9`
@@ -271,7 +271,8 @@ export class DicePickerDialog extends FormApplication {
if (!skillsList) { if (!skillsList) {
return; return;
} }
this.object.skill.list = this.parseSkillsList(skillsList); this.parseSkillsList(skillsList).then((list) => {
this.object.skill.list = list;
if (this.object.skill.list.length > 0) { if (this.object.skill.list.length > 0) {
// Set 1st skill // Set 1st skill
if (this.useCategory) { if (this.useCategory) {
@@ -284,6 +285,7 @@ export class DicePickerDialog extends FormApplication {
this.object.skill.list = null; this.object.skill.list = null;
} }
} }
});
} }
/** /**
@@ -303,7 +305,7 @@ export class DicePickerDialog extends FormApplication {
name: "", name: "",
}; };
this.skillCatId = CONFIG.l5r5e.skills.get(skillId); this.skillCatId = CONFIG.l5r5e.skills.get(skillId); // TODO refacto with skill items
} }
/** /**
@@ -382,7 +384,7 @@ export class DicePickerDialog extends FormApplication {
* @return {boolean} * @return {boolean}
*/ */
get useCategory() { get useCategory() {
return !!this._actor && this._actor.type === "npc"; return !!this._actor && this._actor.isNpc;
} }
/** /**
@@ -830,10 +832,10 @@ export class DicePickerDialog extends FormApplication {
* NPC : shrink to category names * NPC : shrink to category names
* *
* @param {string} skillList * @param {string} skillList
* @return {string[]} * @returns {Promise<{id: *, label: string}[]>}
*/ */
parseSkillsList(skillList) { async parseSkillsList(skillList) {
const categories = game.l5r5e.HelpersL5r5e.getCategoriesSkillsList(); const categories = await game.l5r5e.HelpersL5r5e.getCategoriesSkillsList();
// Sanitize and uniques values // Sanitize and uniques values
const unqSkillList = new Set(); const unqSkillList = new Set();

View File

@@ -113,7 +113,7 @@ export class GmMonitor extends FormApplication {
} else { } else {
// If empty add pc with owner // If empty add pc with owner
actors = game.actors.filter((actor) => actor.type === "character" && actor.hasPlayerOwnerActive); actors = game.actors.filter((actor) => actor.isCharacter && actor.hasPlayerOwnerActive);
this._saveActorsIds(); this._saveActorsIds();
} }

View File

@@ -64,6 +64,12 @@ export const RegisterHandlebars = function () {
return objects.join(""); return objects.join("");
}); });
// Make the sum of values
Handlebars.registerHelper('sum', function(...nb) {
nb.pop(); // remove this function call
return nb.reduce((acc, n) => acc + Number(n ?? 0), 0);
});
// Add a setter // Add a setter
Handlebars.registerHelper("setVar", function (varName, varValue, options) { Handlebars.registerHelper("setVar", function (varName, varValue, options) {
options.data.root[varName] = varValue; options.data.root[varName] = varValue;

View File

@@ -4,7 +4,7 @@
export class HelpersL5r5e { export class HelpersL5r5e {
/** /**
* Get Rings/Element for List / Select * Get Rings/Element for List / Select
* @param {Actor|null} actor * @param {ActorL5r5e|null} actor
* @return {{id: string, label: *, value}[]} * @return {{id: string, label: *, value}[]}
*/ */
static getRingsList(actor = null) { static getRingsList(actor = null) {
@@ -15,12 +15,83 @@ export class HelpersL5r5e {
})); }));
} }
/**
* Return the default skill list for settings
* @returns {string[]}
*/
static getDefaultSkillsUuidFromPack() {
return Array.from({length: 24}, (_, i) => "Compendium.l5r5e.core-skills.L5RCoreSkl" + ('' +(i + 1)).padStart(6, "0"));
}
/**
* Return the skill items list for this actor or from the default (settings)
* @param {ActorL5r5e} actor If actor provided, get the skills preferably from it
* @returns {Promise<Item[]>}
*/
static async getSkillsItemsList(actor = null) {
const skillList = [];
// If actor provided, get the skills preferably from it
if (actor instanceof Actor && actor?.isCharacter) {
actor.items
.filter(item => item.type === "skill")
.forEach(item => skillList.push(item))
}
// Get the default list from settings
let defaultList = game.settings.get(CONFIG.l5r5e.namespace, "defaultSkillsList") || [];
// If empty, refill with default values
if (foundry.utils.isEmpty(defaultList)) {
defaultList = HelpersL5r5e.getDefaultSkillsUuidFromPack();
await game.settings.set(CONFIG.l5r5e.namespace, "defaultSkillsList", defaultList);
}
defaultList = await Promise.all(defaultList.map(async uuid => await fromUuid(uuid)));
// Merge the two list by name
const namesList = skillList.map(item => item.name);
defaultList.forEach(item => {
if (item && !namesList.includes(item.name)) {
skillList.push(item);
}
});
return skillList;
}
/**
* Split Skills item by categories, and sort them alphabetically
* @param {Item[]} itemsList
* @return {{catName: Item[]}}
*/
static splitSkillByCategory(itemsList) {
const skill = CONFIG.l5r5e.skillCategories.reduce((acc,curr) => (acc[curr] = [], acc), {});
itemsList.forEach((item) => {
if (item.type === "skill") {
const cat = item.system.category ?? "artisan";
skill[cat].push(item);
}
});
// Sort Items by name
Object.values(skill).forEach(section => {
section.sort((a, b) => a.name.localeCompare(b.name));
});
return skill;
}
/** /**
* Get Skills for List / Select with groups * Get Skills for List / Select with groups
* @param {boolean} useGroup * @param {boolean} useGroup
* @return {{cat: any, id: any, label: *}[]} * @return {{cat: any, id: any, label: *}[]}
*/ */
static getSkillsList(useGroup = false) { static getSkillsList(useGroup = false) {
console.warn('@deprecated hardcoded skills - helpers.getSkillsList() - Use getSkillsItemsList() + splitSkillByCategory() instead'); // TODO @deprecated hardcoded skills
if (!useGroup) { if (!useGroup) {
return Array.from(CONFIG.l5r5e.skills).map(([id, cat]) => ({ return Array.from(CONFIG.l5r5e.skills).map(([id, cat]) => ({
id: id, id: id,
@@ -45,17 +116,25 @@ export class HelpersL5r5e {
/** /**
* Return Categories and Skill names in it * Return Categories and Skill names in it
* @return {Map} * @param {ActorL5r5e} actor If actor provided, get the skills preferably from it
* @returns {Promise<Map>}
*/ */
static getCategoriesSkillsList() { static async getCategoriesSkillsList(actor = null) {
return Array.from(CONFIG.l5r5e.skills).reduce((acc, [id, cat]) => { const skillList = await HelpersL5r5e.getSkillsItemsList(actor);
const acc = new Map();
skillList.forEach((item) => {
const id = item.name;
const cat = item.system.category;
if (acc.has(cat)) { if (acc.has(cat)) {
acc.set(cat, [...acc.get(cat), id]); acc.set(cat, [...acc.get(cat), id]);
} else { } else {
acc.set(cat, [id]); acc.set(cat, [id]);
} }
});
return acc; return acc;
}, new Map());
} }
/** /**
@@ -297,6 +376,8 @@ export class HelpersL5r5e {
static getPackNameForCoreItem(documentId) { static getPackNameForCoreItem(documentId) {
const core = new Map(); const core = new Map();
core.set("Skl", "l5r5e.core-skills");
core.set("Arm", "l5r5e.core-armors"); core.set("Arm", "l5r5e.core-armors");
core.set("Bon", "l5r5e.core-bonds"); core.set("Bon", "l5r5e.core-bonds");
core.set("Boo", "l5r5e.core-celestial-implement-boons"); core.set("Boo", "l5r5e.core-celestial-implement-boons");
@@ -443,7 +524,7 @@ export class HelpersL5r5e {
/** /**
* Subscribe to common events from the sheet. * Subscribe to common events from the sheet.
* @param {jQuery} html HTML content of the sheet. * @param {jQuery} html HTML content of the sheet.
* @param {Actor} actor Actor Object * @param {ActorL5r5e} actor Actor Object
*/ */
static commonListeners(html, actor = null) { static commonListeners(html, actor = null) {
// Toggle // Toggle
@@ -567,12 +648,15 @@ export class HelpersL5r5e {
static async getEmbedItemByEvent(event, actor) { static async getEmbedItemByEvent(event, actor) {
const current = $(event.currentTarget); const current = $(event.currentTarget);
const itemId = current.data("item-id"); const itemId = current.data("item-id");
const itemUuid = current.data("item-uuid");
const propertyId = current.data("property-id"); const propertyId = current.data("property-id");
const itemParentId = current.data("item-parent-id"); const itemParentId = current.data("item-parent-id");
let item; let item;
if (propertyId) { if (propertyId) {
item = await HelpersL5r5e.getObjectGameOrPack({ id: propertyId, type: "Item" }); item = await HelpersL5r5e.getObjectGameOrPack({ id: propertyId, type: "Item" });
} else if (itemUuid) {
item = await fromUuid(itemUuid);
} else if (itemParentId) { } else if (itemParentId) {
// Embed Item // Embed Item
let parentItem; let parentItem;
@@ -739,7 +823,7 @@ export class HelpersL5r5e {
* @return {Promise<{RollTableDraw}>} The drawn results * @return {Promise<{RollTableDraw}>} The drawn results
*/ */
static async drawManyFromPack(pack, tableName, retrieve = 5, opt = { rollMode: "selfroll" }) { static async drawManyFromPack(pack, tableName, retrieve = 5, opt = { rollMode: "selfroll" }) {
const comp = await game.packs.get(pack); const comp = game.packs.get(pack);
if (!comp) { if (!comp) {
console.log(`L5R5E | Helpers | Pack not found[${pack}]`); console.log(`L5R5E | Helpers | Pack not found[${pack}]`);
return; return;
@@ -754,6 +838,23 @@ export class HelpersL5r5e {
return await table.drawMany(retrieve, opt); return await table.drawMany(retrieve, opt);
} }
/**
* Load all compendium data from his id
*
* @param {String} compendium
* @param {Function} filter
* @returns {Promise<Item[]>}
*/
static async loadCompendium(compendium, filter = item => true) {
const pack = game.packs.get(compendium);
if (!pack) {
console.log(`L5R5E | Pack not found[${compendium}]`);
return;
}
const data = (await pack.getDocuments()) ?? [];
return data.filter(filter);
}
/** /**
* Return the string simplified for comparaison * Return the string simplified for comparaison
* @param {string} str * @param {string} str

View File

@@ -28,6 +28,14 @@ export default class HooksL5r5e {
game.l5r5e.migrations.migrateWorld({ force: false }).then(); game.l5r5e.migrations.migrateWorld({ force: false }).then();
} }
// Prepare values are sometimes null
["character", "adversary", "minion"].forEach(preparedId => {
const prepVal = game.settings.get(CONFIG.l5r5e.namespace, `initiative-prepared-${preparedId}`);
if (prepVal === null || prepVal === "null") {
game.settings.set(CONFIG.l5r5e.namespace, `initiative-prepared-${preparedId}`, "actor");
}
});
// For some reasons, not always really ready, so wait a little // For some reasons, not always really ready, so wait a little
await new Promise((r) => setTimeout(r, 2000)); await new Promise((r) => setTimeout(r, 2000));

View File

@@ -9,7 +9,7 @@ export class ItemL5r5e extends Item {
/** /**
* Return the linked Actor instance if any (current or embed) * Return the linked Actor instance if any (current or embed)
* @return {Actor|null} * @return {ActorL5r5e|null}
*/ */
get actor() { get actor() {
return super.actor || game.actors.get(this.system.parent_id?.actor_id) || null; return super.actor || game.actors.get(this.system.parent_id?.actor_id) || null;
@@ -58,8 +58,12 @@ export class ItemL5r5e extends Item {
*/ */
static async create(data, context = {}) { static async create(data, context = {}) {
if (data.img === undefined) { if (data.img === undefined) {
if (data.type === 'technique') {
data.img = `${CONFIG.l5r5e.paths.assets}icons/techs/kata.svg`;
} else {
data.img = `${CONFIG.l5r5e.paths.assets}icons/items/${data.type}.svg`; data.img = `${CONFIG.l5r5e.paths.assets}icons/items/${data.type}.svg`;
} }
}
return super.create(data, context); return super.create(data, context);
} }
@@ -235,7 +239,11 @@ export class ItemL5r5e extends Item {
if (addBonusToActor) { if (addBonusToActor) {
const actor = this.actor; const actor = this.actor;
if (item instanceof Item && actor instanceof Actor) { if (item instanceof Item && actor instanceof Actor) {
actor.addBonus(item); try {
await actor.addBonus(item);
} catch (ex) {
console.warn("L5R5E |", ex.message);
}
} }
} }

View File

@@ -24,7 +24,7 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
const sheetData = await super.getData(options); const sheetData = await super.getData(options);
sheetData.data.subTypesList = AdvancementSheetL5r5e.types; sheetData.data.subTypesList = AdvancementSheetL5r5e.types;
sheetData.data.skillsList = game.l5r5e.HelpersL5r5e.getSkillsList(true); sheetData.data.skillsList = game.l5r5e.HelpersL5r5e.splitSkillByCategory(await game.l5r5e.HelpersL5r5e.getSkillsItemsList(this.actor));
return sheetData; return sheetData;
} }
@@ -82,33 +82,45 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
let name = this.object.name; let name = this.object.name;
let img = this.object.img; let img = this.object.img;
const getLocalItemByUuid = async (uuid) => {
const item = await fromUuid(uuid);
if (!item) {
return null;
}
if (item?.actor?._id === this.actor._id) {
return item;
}
return this.items.getName(item.name);
}
const skillItemNew = newChoice.skill ? (await getLocalItemByUuid(newChoice.skill)) : null;
const skillItemOld = oldChoice.skill ? (await getLocalItemByUuid(oldChoice.skill)) : null;
// Modify image to reflect choice // Modify image to reflect choice
if (newChoice.ring) { if (newChoice.ring) {
name = game.i18n.localize(`l5r5e.rings.${newChoice.ring}`) + "+1"; name = game.i18n.localize(`l5r5e.rings.${newChoice.ring}`) + "+1";
img = `systems/l5r5e/assets/icons/rings/${newChoice.ring}.svg`; img = `systems/l5r5e/assets/icons/rings/${newChoice.ring}.svg`;
} else if (newChoice.skill) {
name = } else if (newChoice.skill && skillItemNew) {
game.i18n.localize(`l5r5e.skills.${CONFIG.l5r5e.skills.get(newChoice.skill)}.${newChoice.skill}`) + name = skillItemNew.name +"+1";
"+1";
img = `systems/l5r5e/assets/dices/default/skill_blank.svg`; img = `systems/l5r5e/assets/dices/default/skill_blank.svg`;
} }
// Object embed in actor ? // Object embed in actor ?
const actor = this.document.actor; const actor = this.document.actor;
if (actor) { if (actor) {
if (newChoice.skill && !skillItemNew.actor) {
ui.notifications.warn(`Unable to find "${skillItemNew?.name}" on this actor`);
return;
}
const actorData = foundry.utils.duplicate(actor.system); const actorData = foundry.utils.duplicate(actor.system);
let skillCatId = null;
// Old choices // Old choices
if (oldChoice.ring) { if (oldChoice.ring) {
actorData.rings[oldChoice.ring] = Math.max(1, actorData.rings[oldChoice.ring] - 1); actorData.rings[oldChoice.ring] = Math.max(1, actorData.rings[oldChoice.ring] - 1);
} }
if (oldChoice.skill) { if (oldChoice.skill && skillItemOld) {
skillCatId = CONFIG.l5r5e.skills.get(oldChoice.skill); await skillItemOld.update({ "system.rank": Math.max(0, skillItemOld.system.rank - 1) });
actorData.skills[skillCatId][oldChoice.skill] = Math.max(
0,
actorData.skills[skillCatId][oldChoice.skill] - 1
);
} }
// new choices // new choices
@@ -119,15 +131,11 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
game.i18n.localize(`l5r5e.rings.${newChoice.ring}`) + game.i18n.localize(`l5r5e.rings.${newChoice.ring}`) +
` +1 (${actorData.rings[newChoice.ring] - 1} -> ${actorData.rings[newChoice.ring]})`; ` +1 (${actorData.rings[newChoice.ring] - 1} -> ${actorData.rings[newChoice.ring]})`;
} }
if (newChoice.skill) { if (newChoice.skill && skillItemNew) {
skillCatId = CONFIG.l5r5e.skills.get(newChoice.skill); const newRank = Math.min(9, skillItemNew.system.rank + 1);
actorData.skills[skillCatId][newChoice.skill] = actorData.skills[skillCatId][newChoice.skill] + 1; await skillItemNew.update({ "system.rank": newRank });
xp_used = actorData.skills[skillCatId][newChoice.skill] * CONFIG.l5r5e.xp.skillCostMultiplier; xp_used = newRank * CONFIG.l5r5e.xp.skillCostMultiplier;
name = name = `${skillItemNew.name} +1 (${newRank - 1} -> ${newRank})`;
game.i18n.localize(`l5r5e.skills.${skillCatId}.${newChoice.skill}`) +
` +1 (${actorData.skills[skillCatId][newChoice.skill] - 1} -> ${
actorData.skills[skillCatId][newChoice.skill]
})`;
} }
// Update Actor // Update Actor
@@ -141,7 +149,7 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
name: name, name: name,
img: img, img: img,
system: { system: {
xp_used: xp_used, xp_used,
}, },
}); });

View File

@@ -0,0 +1,28 @@
import { ItemSheetL5r5e } from "./item-sheet.js";
/**
* @extends {ItemSheet}
*/
export class SkillSheetL5r5e extends ItemSheetL5r5e {
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["l5r5e", "sheet", "skill"],
template: CONFIG.l5r5e.paths.templates + "items/skill/skill-sheet.html",
width: 520,
height: 480,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
});
}
/**
* @return {Object|Promise}
*/
async getData(options = {}) {
const sheetData = await super.getData(options);
sheetData.data.SkillCategoriesList = CONFIG.l5r5e.skillCategories;
return sheetData;
}
}

View File

@@ -29,7 +29,7 @@ export class TechniqueSheetL5r5e extends ItemSheetL5r5e {
// Sanitize Difficulty and Skill list // Sanitize Difficulty and Skill list
sheetData.data.system.difficulty = TechniqueSheetL5r5e.formatDifficulty(sheetData.data.system.difficulty); sheetData.data.system.difficulty = TechniqueSheetL5r5e.formatDifficulty(sheetData.data.system.difficulty);
sheetData.data.system.skill = TechniqueSheetL5r5e.translateSkillsList( sheetData.data.system.skill = TechniqueSheetL5r5e.translateSkillsList(
TechniqueSheetL5r5e.formatSkillList(sheetData.data.system.skill.split(",")), await TechniqueSheetL5r5e.formatSkillList(sheetData.data.system.skill.split(",")),
false false
).join(", "); ).join(", ");
@@ -54,9 +54,9 @@ export class TechniqueSheetL5r5e extends ItemSheetL5r5e {
// Sanitize Difficulty and Skill list // Sanitize Difficulty and Skill list
formData["system.difficulty"] = TechniqueSheetL5r5e.formatDifficulty(formData["system.difficulty"]); formData["system.difficulty"] = TechniqueSheetL5r5e.formatDifficulty(formData["system.difficulty"]);
formData["system.skill"] = TechniqueSheetL5r5e.formatSkillList( formData["system.skill"] = (await TechniqueSheetL5r5e.formatSkillList(
TechniqueSheetL5r5e.translateSkillsList(formData["system.skill"].split(","), true) TechniqueSheetL5r5e.translateSkillsList(formData["system.skill"].split(","), true)
).join(","); )).join(",");
return super._updateObject(event, formData); return super._updateObject(event, formData);
} }
@@ -143,13 +143,13 @@ export class TechniqueSheetL5r5e extends ItemSheetL5r5e {
/** /**
* Sanitize the technique skill list * Sanitize the technique skill list
* @param {string[]} skillList * @param {string[]} skillList
* @return {string[]} * @returns {Promise<string|*[]>}
*/ */
static formatSkillList(skillList) { static async formatSkillList(skillList) {
if (!skillList) { if (!skillList) {
return ""; return "";
} }
const categories = game.l5r5e.HelpersL5r5e.getCategoriesSkillsList(); const categories = await game.l5r5e.HelpersL5r5e.getCategoriesSkillsList(this.actor);
// List categories // List categories
const unqCatList = new Set(); const unqCatList = new Set();
@@ -160,18 +160,6 @@ export class TechniqueSheetL5r5e extends ItemSheetL5r5e {
} }
}); });
// List skill (not include in cat) return [...unqCatList];
const unqSkillList = new Set();
skillList.forEach((s) => {
s = s?.trim();
if (!!s && CONFIG.l5r5e.skills.has(s)) {
const cat = CONFIG.l5r5e.skills.get(s);
if (!unqCatList.has(cat)) {
unqSkillList.add(s);
}
}
});
return [...unqCatList, ...unqSkillList];
} }
} }

View File

@@ -17,6 +17,8 @@ export class WeaponSheetL5r5e extends ItemSheetL5r5e {
async getData(options = {}) { async getData(options = {}) {
const sheetData = await super.getData(options); const sheetData = await super.getData(options);
const categories = await game.l5r5e.HelpersL5r5e.getCategoriesSkillsList(this.actor);
console.log(categories);
// Martial skills only // Martial skills only
sheetData.data.skills = Array.from(CONFIG.l5r5e.skills) sheetData.data.skills = Array.from(CONFIG.l5r5e.skills)

View File

@@ -23,6 +23,7 @@ import { CombatL5r5e } from "./combat.js";
// Items // Items
import { ItemL5r5e } from "./item.js"; import { ItemL5r5e } from "./item.js";
import { ItemSheetL5r5e } from "./items/item-sheet.js"; import { ItemSheetL5r5e } from "./items/item-sheet.js";
import { SkillSheetL5r5e } from "./items/skill-sheet.js";
import { ArmorSheetL5r5e } from "./items/armor-sheet.js"; import { ArmorSheetL5r5e } from "./items/armor-sheet.js";
import { WeaponSheetL5r5e } from "./items/weapon-sheet.js"; import { WeaponSheetL5r5e } from "./items/weapon-sheet.js";
import { TechniqueSheetL5r5e } from "./items/technique-sheet.js"; import { TechniqueSheetL5r5e } from "./items/technique-sheet.js";
@@ -43,6 +44,7 @@ import { MigrationL5r5e } from "./migration.js";
import { GmToolbox } from "./gm/gm-toolbox.js"; import { GmToolbox } from "./gm/gm-toolbox.js";
import { GmMonitor } from "./gm/gm-monitor.js"; import { GmMonitor } from "./gm/gm-monitor.js";
import { Storage } from "./storage.js"; import { Storage } from "./storage.js";
import { DefaultSkillsDialogL5r5e } from "./settings/default-skills-dialog.js";
/* ------------------------------------ */ /* ------------------------------------ */
/* Initialize system */ /* Initialize system */
@@ -92,6 +94,7 @@ Hooks.once("init", async () => {
GmToolbox, GmToolbox,
GmMonitor, GmMonitor,
HelpDialog, HelpDialog,
DefaultSkillsDialogL5r5e,
storage: new Storage(), storage: new Storage(),
sockets: new SocketHandlerL5r5e(), sockets: new SocketHandlerL5r5e(),
migrations: MigrationL5r5e, migrations: MigrationL5r5e,
@@ -132,6 +135,11 @@ Hooks.once("init", async () => {
label: "TYPES.Item.item", label: "TYPES.Item.item",
makeDefault: true, makeDefault: true,
}); });
Items.registerSheet(L5R5E.namespace, SkillSheetL5r5e, {
types: ["skill"],
label: "ITEM.TypeSkill",
makeDefault: true,
});
Items.registerSheet(L5R5E.namespace, ArmorSheetL5r5e, { Items.registerSheet(L5R5E.namespace, ArmorSheetL5r5e, {
types: ["armor"], types: ["armor"],
label: "TYPES.Item.armor", label: "TYPES.Item.armor",

View File

@@ -6,7 +6,7 @@ export class MigrationL5r5e {
* Minimum Version needed for migration stuff to trigger * Minimum Version needed for migration stuff to trigger
* @type {string} * @type {string}
*/ */
static NEEDED_VERSION = "1.3.0"; static NEEDED_VERSION = "2.0.0";
/** /**
* Return true if the version need some updates * Return true if the version need some updates
@@ -28,6 +28,11 @@ export class MigrationL5r5e {
return; return;
} }
// Wait for translation to load
if (!options.force && typeof Babele !== "undefined") {
await new Promise((r) => setTimeout(r, 5000));
}
// if (MigrationL5r5e.needUpdate("1.3.0")) { // if (MigrationL5r5e.needUpdate("1.3.0")) {
// ChatMessage.create({"content": "<strong>L5R5E v1.3.0 :</strong><br>"}); // ChatMessage.create({"content": "<strong>L5R5E v1.3.0 :</strong><br>"});
// } // }
@@ -42,7 +47,7 @@ export class MigrationL5r5e {
// Migrate World Actors // Migrate World Actors
for (let actor of game.actors.contents) { for (let actor of game.actors.contents) {
try { try {
const updateData = MigrationL5r5e._migrateActorData(actor, options); const updateData = await MigrationL5r5e._migrateActorData(actor, options);
if (!foundry.utils.isEmpty(updateData)) { if (!foundry.utils.isEmpty(updateData)) {
console.log(`L5R5E | Migration | Migrating Actor document ${actor.name}[${actor._id}]`); console.log(`L5R5E | Migration | Migrating Actor document ${actor.name}[${actor._id}]`);
await actor.update(updateData); await actor.update(updateData);
@@ -225,7 +230,7 @@ export class MigrationL5r5e {
* @param options * @param options
* @return {Object} The updateData to apply * @return {Object} The updateData to apply
*/ */
static _migrateActorData(actor, options = { force: false }) { static async _migrateActorData(actor, options = { force: false }) {
const updateData = {}; const updateData = {};
const system = actor.system; const system = actor.system;
@@ -243,7 +248,7 @@ export class MigrationL5r5e {
} }
// NPC are now without autostats, we need to save the value // NPC are now without autostats, we need to save the value
if (actor.type === "npc") { if (actor.isNpc) {
if (system.endurance < 1) { if (system.endurance < 1) {
updateData["system.endurance"] = (Number(system.rings.earth) + Number(system.rings.fire)) * 2; updateData["system.endurance"] = (Number(system.rings.earth) + Number(system.rings.fire)) * 2;
updateData["system.composure"] = (Number(system.rings.earth) + Number(system.rings.water)) * 2; updateData["system.composure"] = (Number(system.rings.earth) + Number(system.rings.water)) * 2;
@@ -264,7 +269,7 @@ export class MigrationL5r5e {
} }
// NPC have now more than a Strength and a Weakness // NPC have now more than a Strength and a Weakness
if (actor.type === "npc" && system.rings_affinities?.strength) { if (actor.isNpc && system.rings_affinities?.strength) {
const aff = system.rings_affinities; const aff = system.rings_affinities;
updateData["system.rings_affinities." + aff.strength.ring] = aff.strength.value; updateData["system.rings_affinities." + aff.strength.ring] = aff.strength.value;
updateData["system.rings_affinities." + aff.weakness.ring] = aff.weakness.value; updateData["system.rings_affinities." + aff.weakness.ring] = aff.weakness.value;
@@ -276,6 +281,29 @@ export class MigrationL5r5e {
} }
// ***** End of 1.3.0 ***** // ***** End of 1.3.0 *****
// ***** Start of 2.0.0 *****
if (options?.force || MigrationL5r5e.needUpdate("2.0.0")) {
// Only PC : Convert fixed skills to items list
if (actor.isCharacter && Array.from(actor.items).every(i => i.type !== "skill")) {
// Add skills items
const update = {items: [], name: actor.name};
await game.l5r5e.ActorL5r5e.addSkillsFromDefaultList(update);
// Set actor value
update.items.forEach(item => {
const skillId = item.flags?.l5r5e?.skillCoreId;
const skillCatId = CONFIG.l5r5e.skills.get(skillId);
const skillValue = system.skills[skillCatId][skillId];
if (skillCatId && skillValue > 0) {
item.system.rank = skillValue;
}
});
updateData.items = update.items;
}
}
// ***** End of 2.0.0 *****
return updateData; return updateData;
} }

View File

@@ -14,7 +14,6 @@ export const PreloadTemplates = async function () {
`${tpl}actors/character/narrative.html`, `${tpl}actors/character/narrative.html`,
`${tpl}actors/character/rings.html`, `${tpl}actors/character/rings.html`,
`${tpl}actors/character/effects.html`, `${tpl}actors/character/effects.html`,
`${tpl}actors/character/skill.html`,
`${tpl}actors/character/social.html`, `${tpl}actors/character/social.html`,
`${tpl}actors/character/stance.html`, `${tpl}actors/character/stance.html`,
`${tpl}actors/character/techniques.html`, `${tpl}actors/character/techniques.html`,
@@ -59,6 +58,9 @@ export const PreloadTemplates = async function () {
`${tpl}items/property/properties.html`, `${tpl}items/property/properties.html`,
`${tpl}items/property/property-entry.html`, `${tpl}items/property/property-entry.html`,
`${tpl}items/property/property-sheet.html`, `${tpl}items/property/property-sheet.html`,
`${tpl}items/skill/skill-entry.html`,
`${tpl}items/skill/skill-sheet.html`,
`${tpl}items/skill/skill-text.html`,
`${tpl}items/signature-scroll/signature-scroll-entry.html`, `${tpl}items/signature-scroll/signature-scroll-entry.html`,
`${tpl}items/signature-scroll/signature-scroll-sheet.html`, `${tpl}items/signature-scroll/signature-scroll-sheet.html`,
`${tpl}items/signature-scroll/signature-scroll-text.html`, `${tpl}items/signature-scroll/signature-scroll-text.html`,

View File

@@ -7,6 +7,22 @@ export const RegisterSettings = function () {
/* ------------------------------------ */ /* ------------------------------------ */
/* User settings */ /* User settings */
/* ------------------------------------ */ /* ------------------------------------ */
game.settings.registerMenu(CONFIG.l5r5e.namespace, "defaultSkillsListMenu", {
name: "SETTINGS.DefaultSkillsList.Title",
label: "SETTINGS.DefaultSkillsList.Label",
hint: "SETTINGS.DefaultSkillsList.Hint",
icon: "fas fa-bars",
type: game.l5r5e.DefaultSkillsDialogL5r5e,
restricted: true, // GameMaster only
});
game.settings.register(CONFIG.l5r5e.namespace, "defaultSkillsList", {
name: "System Migration Version",
scope: "world",
config: false,
type: Array,
default: game.l5r5e.HelpersL5r5e.getDefaultSkillsUuidFromPack(),
});
game.settings.register(CONFIG.l5r5e.namespace, "rnk-deleteOldMessage", { game.settings.register(CONFIG.l5r5e.namespace, "rnk-deleteOldMessage", {
name: "SETTINGS.RollNKeep.DeleteOldMessage", name: "SETTINGS.RollNKeep.DeleteOldMessage",
hint: "SETTINGS.RollNKeep.DeleteOldMessageHint", hint: "SETTINGS.RollNKeep.DeleteOldMessageHint",

View File

@@ -0,0 +1,148 @@
/**
* L5R Settings dialog for default skills list
* @extends {FormApplication}
*/
export class DefaultSkillsDialogL5r5e extends FormApplication {
/**
* Key for skills list in Settings
* @type {string}
*/
static skillsLisKey = "defaultSkillsList";
/**
* Assign the default options
* @override
*/
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
id: "l5r5e-settings-default-skills-dialog",
classes: ["l5r5e", "settings", "default-skills"],
template: CONFIG.l5r5e.paths.templates + "settings/default-skills-dialog.html",
title: game.i18n.localize("SETTINGS.DefaultSkillsList.Label"),
width: 500,
height: 680,
resizable: true,
closeOnSubmit: false,
submitOnClose: false,
submitOnChange: false,
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
});
}
/**
* Prevent non GM to render this windows
* @override
*/
render(force = false, options = {}) {
if (!this.isEditable) {
console.log("L5R5E | You don't have the rights to display this application");
return false;
}
return super.render(force, options);
}
/**
* Is the Form Application currently editable?
* @type {boolean}
*/
get isEditable() {
return game.user.isGM;
}
/**
* Construct and return the data object used to render the HTML template for this form application.
* @param options
* @return {Object}
*/
async getData(options = null) {
// Transform skills uuids to items by categories
let skillList = await game.l5r5e.HelpersL5r5e.getSkillsItemsList();
skillList = game.l5r5e.HelpersL5r5e.splitSkillByCategory(skillList);
return {
...(await super.getData(options)),
skillList,
};
}
/**
* Handle dropped data on the Actor sheet
* @param {DragEvent} event
*/
async _onDrop(event) {
// *** Everything below here is only needed if the sheet is editable ***
if (!this.isEditable) {
console.log("L5R5E | This sheet is not editable");
return;
}
// Check item type and subtype
const item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
if (!item || item.documentName !== "Item" || item?.type !== "skill") {
console.log(`L5R5E | Item dropped must be a Skill Item : ${item?.type}`, item);
return;
}
// EmbedItem actor item ?
if (item.uuid.startsWith('Actor.')) {
console.log("L5R5E | This element has been ignored because it's a EmbedItem actor item", item.uuid);
return;
}
const skillListUuids = game.settings.get(CONFIG.l5r5e.namespace, DefaultSkillsDialogL5r5e.skillsLisKey) || [];
// Dropped an item with same "id" as one owned
if (skillListUuids.some((embedItem) => embedItem.uuid === item.uuid)) {
console.log("L5R5E | This element has been ignored because it already exists", item.uuid);
return;
}
// Add the uuid and save
skillListUuids.push(item.uuid);
await game.settings.set(CONFIG.l5r5e.namespace, DefaultSkillsDialogL5r5e.skillsLisKey, skillListUuids);
// Refresh
this.render(false);
}
/**
* Subscribe to events from the sheet.
* @param {jQuery} html HTML content of the sheet.
*/
activateListeners(html) {
super.activateListeners(html);
// Commons
game.l5r5e.HelpersL5r5e.commonListeners(html);
// *** Everything below here is only needed if the sheet is editable ***
if (!this.isEditable) {
return;
}
// Delete an item
html.find(`.item-delete`).on("click", this._removeSkill.bind(this));
}
/**
* Remove an item from it's uuid
* @param {Event} event
* @private
*/
async _removeSkill(event) {
event.preventDefault();
event.stopPropagation();
const itemUuid = $(event.currentTarget).data("item-uuid");
if (!itemUuid) {
return;
}
// Remove and save
const skillListUuids = game.settings.get(CONFIG.l5r5e.namespace, DefaultSkillsDialogL5r5e.skillsLisKey) || [];
await game.settings.set(CONFIG.l5r5e.namespace, DefaultSkillsDialogL5r5e.skillsLisKey, skillListUuids.filter((uuid) => uuid !== itemUuid));
// Refresh
this.render(false);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,8 @@
.item { .item {
.item-header { .item-header {
display: flex; display: flex;
align-items: center;
.item-img { .item-img {
flex: 0 0 32px; flex: 0 0 32px;
padding-right: 0.25rem; padding-right: 0.25rem;
@@ -110,6 +112,7 @@
&.peculiarity, &.peculiarity,
&.property, &.property,
&.signature-scroll, &.signature-scroll,
&.skill,
&.technique, &.technique,
&.title, &.title,
&.weapon { &.weapon {
@@ -356,6 +359,16 @@
} }
} }
} }
&.skill {
article {
&.attributes {
height: 4rem;
}
&.infos {
height: calc(100% - 5rem);
}
}
}
&.weapon { &.weapon {
article { article {
&.attributes { &.attributes {

View File

@@ -41,29 +41,63 @@
); );
} }
ul { ul {
flex: 50%; flex: 100%;
padding: 0.25rem 0.5rem 0.25rem 0; padding: 0 0.15rem;
li { li {
text-align: left; text-align: left;
line-height: 2rem; line-height: 2rem;
margin: 0.25rem 0; margin: 0.25rem 0;
&.skill {
text-align: right;
span {
color: $l5r5e-black;
&[data-skill="melee"],
&[data-skill="ranged"],
&[data-skill="unarmed"] {
float: left;
line-height: 1rem;
width: calc(100% - 2rem);
} }
&.skill-category-skills-list {
img {
width: 32px;
height: 32px;
object-fit: contain;
object-position: 50% 0;
border: none;
padding: 2px 0;
}
input {
text-align: center;
}
.item-header {
flex: 4;
align-items: center;
h4 {
margin: 0;
}
.item-img {
height: 32px;
margin: 0;
flex: 0 0 32px;
}
.item-rank {
flex: 0 0 32px;
text-align: center;
}
.item-edit,
.item-delete {
text-align: center;
line-height: 1rem;
font-size: 0.75rem;
flex: 0 0 1rem;
padding: 0 0.1rem;
color: $black-light;
} }
} }
} }
&.skill-category-ring-actions { &.skill-category-ring-actions {
padding: 0.25rem 0 0.25rem 0.5rem; padding: 0.25rem 0 0.25rem 0.5rem;
border-top: 1px dashed $l5r5e-title;
border-left: 1px solid $l5r5e-title; border-left: 1px solid $l5r5e-title;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-content: center;
justify-content: space-evenly;
} }
} }
input { input {

View File

@@ -933,6 +933,41 @@ button {
} }
} }
#l5r5e-settings-default-skills-dialog {
.item-header {
align-items: center;
.item-img {
flex: 0 0 32px;
padding-right: 0.25rem;
img {
border: 1px solid rgba(0, 0, 0, 0.1);
}
}
.item-name {
flex: 1;
font-size: 1rem;
line-height: 1rem;
color: $l5r5e-bold;
}
.item-source {
flex: 1;
}
.item-delete {
text-align: center;
line-height: 1rem;
font-size: 0.75rem;
flex: 0 0 1rem;
padding: 0 0.1rem;
color: $black-light;
}
}
}
.autocomplete-wrapper { .autocomplete-wrapper {
position: relative; position: relative;
display: inline-block; display: inline-block;

View File

@@ -7,8 +7,8 @@
"changelog": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/CHANGELOG.md", "changelog": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/CHANGELOG.md",
"license": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/LICENSE.md", "license": "https://gitlab.com/teaml5r/l5r5e/-/blob/master/LICENSE.md",
"manifest": "https://gitlab.com/teaml5r/l5r5e/-/raw/master/system/system.json", "manifest": "https://gitlab.com/teaml5r/l5r5e/-/raw/master/system/system.json",
"download": "https://gitlab.com/teaml5r/l5r5e/-/jobs/artifacts/v1.11.0/raw/l5r5e.zip?job=build", "download": "https://gitlab.com/teaml5r/l5r5e/-/jobs/artifacts/v2.0.0/raw/l5r5e.zip?job=build",
"version": "1.11.0", "version": "2.0.0",
"compatibility": { "compatibility": {
"minimum": 11, "minimum": 11,
"verified": "11" "verified": "11"
@@ -50,6 +50,7 @@
"color": "#019806", "color": "#019806",
"sorting": "m", "sorting": "m",
"packs": [ "packs": [
"core-skills",
"core-peculiarities-distinctions", "core-peculiarities-distinctions",
"core-peculiarities-passions", "core-peculiarities-passions",
"core-peculiarities-adversities", "core-peculiarities-adversities",
@@ -115,6 +116,13 @@
} }
], ],
"packs": [ "packs": [
{
"name": "core-skills",
"label": "Skills",
"path": "packs/core-skills.db",
"type": "Item",
"system": "l5r5e"
},
{ {
"name": "core-properties", "name": "core-properties",
"label": "Properties", "label": "Properties",

View File

@@ -194,7 +194,8 @@
"signature_scroll", "signature_scroll",
"item_pattern", "item_pattern",
"army_cohort", "army_cohort",
"army_fortification" "army_fortification",
"skill"
], ],
"templates": { "templates": {
"basics": { "basics": {
@@ -220,6 +221,12 @@
"properties": [] "properties": []
} }
}, },
"skill": {
"templates": ["basics"],
"category": "artisan",
"rank": 0,
"modifier": 0
},
"item": { "item": {
"templates": ["basics", "item"] "templates": ["basics", "item"]
}, },

View File

@@ -127,7 +127,7 @@
{{!-- items list --}} {{!-- items list --}}
<h2>{{localize 'l5r5e.sheets.equipment'}}</h2> <h2>{{localize 'l5r5e.sheets.equipment'}}</h2>
<ul> <ul>
<li>{{localize 'l5r5e.money.title'}} : {{data.system.money.koku}} {{localize 'l5r5e.money.koku'}} / {{data.system.money.bu}} {{localize 'l5r5e.money.bu'}} / {{data.system.money.zeni}} {{localize 'l5r5e.money.zeni'}}</li> <li>{{localize 'l5r5e.money.title'}} : {{data.money.koku}} {{localize 'l5r5e.money.koku'}} / {{data.money.bu}} {{localize 'l5r5e.money.bu'}} / {{data.money.zeni}} {{localize 'l5r5e.money.zeni'}}</li>
{{#each data.splitItemsList as |cat type|}} {{#each data.splitItemsList as |cat type|}}
<li> <li>
<strong>{{localize (localize 'l5r5e.{type}s.title' type=type)}} ({{cat.length}})</strong> <strong>{{localize (localize 'l5r5e.{type}s.title' type=type)}} ({{cat.length}})</strong>

View File

@@ -46,8 +46,8 @@
{{!-- Skills Tab --}} {{!-- Skills Tab --}}
<article class="tab skills" data-group="primary" data-tab="skills"> <article class="tab skills" data-group="primary" data-tab="skills">
<ul class="skills-wrapper"> <ul class="skills-wrapper">
{{#each data.system.skills as |category id|}} {{> {{#each data.skillCategories as |itemsInCategory id|}}
'systems/l5r5e/templates/actors/character/category.html' category=category categoryId=id data=../data}} {{> 'systems/l5r5e/templates/actors/character/category.html' itemsInCategory=itemsInCategory categoryId=id data=../data}}
{{/each}} {{/each}}
</ul> </ul>
{{> 'systems/l5r5e/templates/actors/character/techniques.html'}} {{> 'systems/l5r5e/templates/actors/character/techniques.html'}}

View File

@@ -1,8 +1,8 @@
<li class="skill-category-wrapper skill-category-content"> <li class="skill-category-wrapper skill-category-content">
<h4 class="section-header toggle-on-click" data-toggle="toggle-skill-category-{{categoryId}}">{{localizeSkill categoryId 'title'}}</h4> <h4 class="section-header toggle-on-click" data-toggle="toggle-skill-category-{{categoryId}}">{{localizeSkill categoryId 'title'}}</h4>
<ul class="skill-category-skills-list toggle-skill-category-{{categoryId}} {{#ifCond data.storeInfos 'includes' (concat 'toggle-skill-category-' categoryId)}}toggle-hidden{{/ifCond}}"> <ul class="skill-category-skills-list toggle-skill-category-{{categoryId}} {{#ifCond data.storeInfos 'includes' (concat 'toggle-skill-category-' categoryId)}}toggle-hidden{{/ifCond}}">
{{#each category as |skill id|}} {{#each itemsInCategory as |skill|}}
{{> 'systems/l5r5e/templates/actors/character/skill.html' categoryId=../categoryId skill=skill skillId=id data=../data}} {{> 'systems/l5r5e/templates/items/skill/skill-entry.html' skill=skill data=../data}}
{{/each}} {{/each}}
</ul> </ul>
<ul class="skill-category-ring-actions toggle-skill-category-{{categoryId}} {{#ifCond data.storeInfos 'includes' (concat 'toggle-skill-category-' categoryId)}}toggle-hidden{{/ifCond}}"> <ul class="skill-category-ring-actions toggle-skill-category-{{categoryId}} {{#ifCond data.storeInfos 'includes' (concat 'toggle-skill-category-' categoryId)}}toggle-hidden{{/ifCond}}">

View File

@@ -2,7 +2,7 @@
<legend class="section-header">{{localize 'l5r5e.money.title'}}</legend> <legend class="section-header">{{localize 'l5r5e.money.title'}}</legend>
<label> <label>
{{localize 'l5r5e.money.koku'}} {{localize 'l5r5e.money.koku'}}
<input name="system.money.koku" type="number" value="{{data.system.money.koku}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/> <input name="money.koku" type="number" value="{{data.money.koku}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
<span class="money-buttons"> <span class="money-buttons">
<i class="money-control pointer-choice fa fa-plus-square" data-type="koku" data-value="1"></i> <i class="money-control pointer-choice fa fa-plus-square" data-type="koku" data-value="1"></i>
<i class="money-control pointer-choice fa fa-minus-square" data-type="koku" data-value="-1"></i> <i class="money-control pointer-choice fa fa-minus-square" data-type="koku" data-value="-1"></i>
@@ -10,7 +10,7 @@
</label> </label>
<label> <label>
{{localize 'l5r5e.money.bu'}} {{localize 'l5r5e.money.bu'}}
<input name="system.money.bu" type="number" value="{{data.system.money.bu}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/> <input name="money.bu" type="number" value="{{data.money.bu}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
<span class="money-buttons"> <span class="money-buttons">
<i class="money-control pointer-choice fa fa-plus-square" data-type="bu" data-value="1"></i> <i class="money-control pointer-choice fa fa-plus-square" data-type="bu" data-value="1"></i>
<i class="money-control pointer-choice fa fa-minus-square" data-type="bu" data-value="-1"></i> <i class="money-control pointer-choice fa fa-minus-square" data-type="bu" data-value="-1"></i>
@@ -18,7 +18,7 @@
</label> </label>
<label> <label>
{{localize 'l5r5e.money.zeni'}} {{localize 'l5r5e.money.zeni'}}
<input name="system.money.zeni" type="number" value="{{data.system.money.zeni}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/> <input name="money.zeni" type="number" value="{{data.money.zeni}}" data-dtype="Number" min="0" placeholder="0" {{^if data.editable_not_soft_locked}}disabled{{/if}}/>
<span class="money-buttons"> <span class="money-buttons">
<i class="money-control pointer-choice fa fa-plus-square" data-type="zeni" data-value="1"></i> <i class="money-control pointer-choice fa fa-plus-square" data-type="zeni" data-value="1"></i>
<i class="money-control pointer-choice fa fa-minus-square" data-type="zeni" data-value="-1"></i> <i class="money-control pointer-choice fa fa-minus-square" data-type="zeni" data-value="-1"></i>

View File

@@ -1,16 +0,0 @@
<li class="skill skill-wrapper">
<label class="skill-content">
<span class="dice-picker attribute-label rollable" data-skill="{{skillId}}">{{localizeSkill categoryId skillId}}</span>
<input
class="centered-input select-on-focus"
type="number"
name="system.skills.{{categoryId}}.{{skillId}}"
value="{{skill}}"
data-dtype="Number"
min="0"
max="9"
placeholder="0"
{{^if data.editable_not_soft_locked}}disabled{{/if}}
/>
</label>
</li>

View File

@@ -32,7 +32,7 @@
{{#each data.skillsList as |skills catId|}} {{#each data.skillsList as |skills catId|}}
<optgroup label="{{localizeSkill catId 'title'}}"> <optgroup label="{{localizeSkill catId 'title'}}">
{{#each skills as |obj|}} {{#each skills as |obj|}}
<option value="{{obj.id}}">{{obj.label}}</option> <option value="{{obj.uuid}}">{{obj.name}}</option>
{{/each}} {{/each}}
</optgroup> </optgroup>
{{/each}} {{/each}}

View File

@@ -0,0 +1,26 @@
<li class="item skill flexcol" data-item-id="{{skill.id}}">
<ul class="item-header skill-controls flexrow">
<li class="item-img"><img src="{{skill.img}}" title="{{skill.name}}" width="32px" height="32px"/></li>
<li class="item-name dice-picker attribute-label rollable l5r5e-tooltip" data-item-id="{{skill._id}}" data-skill="{{skill._id}}">{{skill.name}}</li>
<li class="item-rank">
{{#if data.editable_not_soft_locked}}
<input
class="centered-input select-on-focus"
type="number"
name="skillsValues.{{skill._id}}"
value="{{skill.system.rank}}"
data-dtype="Number"
min="0"
max="9"
placeholder="0"
/>
{{else}}
{{sum skill.system.rank skill.system.modifier}}
{{/if}}
</li>
{{#if data.editable_not_soft_locked}}
<li data-item-id="{{skill._id}}" class="item-control item-edit" title="{{localize 'l5r5e.global.edit'}}"><i class="fas fa-edit"></i></li>
<li data-item-id="{{skill._id}}" class="item-control item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
{{/if}}
</ul>
</li>

View File

@@ -0,0 +1,27 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{data.img}}" data-edit="img" title="{{data.name}}"/>
<h1 class="charname"><input name="name" type="text" value="{{data.name}}" placeholder="Name"/></h1>
</header>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Attributes Tab --}}
<article class="attributes" data-group="primary" data-tab="attributes">
<label class="attribute">
{{localize 'l5r5e.skills.category'}}
<select name="system.category">
{{#select data.system.category}}
{{#each data.SkillCategoriesList as |id|}}
<option value="{{id}}">{{localizeSkill id 'title'}}</option>
{{/each}}
{{/select}}
</select>
</label>
<label class="attribute">
{{localize 'l5r5e.sheets.rank'}}
<input class="select-on-focus" type="number" name="system.rank" value="{{data.system.rank}}" data-dtype="Number" min="0" placeholder="0"/>
</label>
</article>
{{> 'systems/l5r5e/templates/items/item/item-infos.html'}}
</section>
</form>

View File

@@ -0,0 +1,18 @@
<div class="{{cssClass}}" data-actor-id="{{actor._id}}" data-item-id="{{data._id}}">
<header class="card-header">
<h2 class="item-name"><img src="{{data.img}}" title="{{data.name}}" /> {{data.name}}</h2>
</header>
<section class="sheet-body">
<ul>
<li>
<strong>{{localize 'l5r5e.skills.category'}}</strong> : {{localizeSkill data.system.category 'title'}}
</li>
<li>
<strong>{{localize 'l5r5e.sheets.rank'}}</strong> : {{data.system.rank}}{{#if data.system.modifier}} ({{data.system.modifier}}){{/if}}
</li>
</ul>
{{!--item-infos--}}
<p><strong>{{localize 'l5r5e.sheets.description'}}</strong> : {{{data.enrichedHtml.description}}}</p>
<p><strong>{{localize 'l5r5e.sheets.book_reference'}}</strong> : {{data.system.book_reference}}</p>
</section>
</div>

View File

@@ -0,0 +1,19 @@
<form autocomplete="off">
{{#each skillList as |category name|}}
<h2>{{localizeSkill name 'title'}}</h2>
<div class="form-group">
<ul>
{{#each category as |skill|}}
<li class="item skill flexcol">
<ul class="item-header skill-controls flexrow">
<li class="item-img"><img src="{{skill.img}}" title="{{skill.name}}" width="32px" height="32px"/></li>
<li class="item-name attribute-label l5r5e-tooltip" data-item-uuid="{{skill.uuid}}">{{skill.name}}</li>
<li class="item-source">{{#if skill.pack}}{{skill.pack}}{{else}}world{{/if}}</li>
<li data-item-uuid="{{skill.uuid}}" class="item-control item-delete" title="{{localize 'Delete'}}"><i class="fas fa-trash"></i></li>
</ul>
</li>
{{/each}}
</ul>
</div>
{{/each}}
</form>