Added new dialog settings to configure default skill list.

Added uuid compatibility for tooltips
This commit is contained in:
Vlyan
2023-01-13 15:38:15 +01:00
parent 0b3816587b
commit 1d42d2970d
13 changed files with 292 additions and 37 deletions

View File

@@ -2,7 +2,8 @@
Date format : day/month/year
## 2.0.0 - xx/xx/2023 - Skill list
- Skills are now items, this can break things update with caution !
- 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.9.4 - 31/12/2022 - Last bugfixes of the Year !
- Fix prepared settings bugs (trackers icons sometimes disappears).

View File

@@ -17,6 +17,11 @@
"CustomTechniques": {
"Title": "Use custom techniques",
"Hint": "Add 'Specificity' technique type to serve as a catch-all."
},
"DefaultSkillsList": {
"Title": "Skills",
"Label": "Set Default Skills",
"Hint": "Set default skills list for new characters."
}
},
"ACTOR": {

View File

@@ -17,6 +17,11 @@
"CustomTechniques": {
"Title": "Utiliser les techniques personnalisées",
"Hint": "Ajoute un type de technique 'Particularités' pour servir de fourre-tout."
},
"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."
}
},
"ACTOR": {

View File

@@ -24,7 +24,7 @@ export class ActorL5r5e extends Actor {
case "character":
// Load skills from core compendiums (only for pc character)
docData.items = [];
await ActorL5r5e.addSkillsFromCompendiums(docData);
await ActorL5r5e.addSkillsFromDefaultList(docData);
// Set token properties
foundry.utils.mergeObject(
@@ -87,20 +87,16 @@ export class ActorL5r5e extends Actor {
/**
* Add all the skills from compendiums to "items"
*/
static async addSkillsFromCompendiums(docData) {
console.log(`L5R5E | Adding skills to ${docData.name}`);
static async addSkillsFromDefaultList(docData) {
console.log(`L5R5E | Adding default skills to ${docData.name}`);
const packName = CONFIG.l5r5e.systemName + ".core-skills";
const skills = await game.l5r5e.HelpersL5r5e.loadCompendium(packName);
if (skills.length < 1) {
console.log(`L5R5E | No items found in Pack [${packName}]`);
return;
}
const skillList = await game.l5r5e.HelpersL5r5e.getDefaultSkillsItems();
// Get the json data and replace the object id
skills.forEach(item => {
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);
});
}

View File

@@ -48,7 +48,7 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
this.actor.system.identity.school_rank = Math.max(1, this.actor.system.identity.school_rank);
// Split Skills
sheetData.data.skillCategories = this._splitSkills(sheetData);
sheetData.data.skillCategories = game.l5r5e.HelpersL5r5e.splitSkillByCategory(sheetData.items);
// Split Money
sheetData.data.money = this._zeniToMoney(this.actor.system.zeni);
@@ -67,28 +67,6 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
return sheetData;
}
/**
* Split Skills item by categories
* @private
*/
_splitSkills(sheetData) {
const skill = CONFIG.l5r5e.skillCategories.reduce((acc,curr) => (acc[curr] = [], acc), {});
sheetData.items.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;
}
/**
* Subscribe to events from the sheet.
* @param {jQuery} html HTML content of the sheet.

View File

@@ -15,6 +15,53 @@ 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 default skill items list
* @returns {Promise<Item[]>}
*/
static async getDefaultSkillsItems() {
let skillList = game.settings.get(CONFIG.l5r5e.systemName, "defaultSkillsList") || [];
// If empty, refill with default values
if (foundry.utils.isEmpty(skillList)) {
skillList = HelpersL5r5e.getDefaultSkillsUuidFromPack();
await game.settings.set(CONFIG.l5r5e.systemName, "defaultSkillsList", skillList);
}
return await Promise.all(skillList.map(async uuid => await fromUuid(uuid)));
}
/**
* 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
* @param {boolean} useGroup
@@ -573,12 +620,15 @@ export class HelpersL5r5e {
static async getEmbedItemByEvent(event, actor) {
const current = $(event.currentTarget);
const itemId = current.data("item-id");
const itemUuid = current.data("item-uuid");
const propertyId = current.data("property-id");
const itemParentId = current.data("item-parent-id");
let item;
if (propertyId) {
item = await HelpersL5r5e.getObjectGameOrPack({ id: propertyId, type: "Item" });
} else if (itemUuid) {
item = await fromUuid(itemUuid);
} else if (itemParentId) {
// Embed Item
let parentItem;

View File

@@ -44,6 +44,7 @@ import { MigrationL5r5e } from "./migration.js";
import { GmToolbox } from "./gm/gm-toolbox.js";
import { GmMonitor } from "./gm/gm-monitor.js";
import { Storage } from "./storage.js";
import { DefaultSkillsDialogL5r5e } from "./settings/default-skills-dialog.js";
/* ------------------------------------ */
/* Initialize system */
@@ -93,6 +94,7 @@ Hooks.once("init", async () => {
GmToolbox,
GmMonitor,
HelpDialog,
DefaultSkillsDialogL5r5e,
storage: new Storage(),
sockets: new SocketHandlerL5r5e(),
migrations: MigrationL5r5e,

View File

@@ -289,7 +289,7 @@ export class MigrationL5r5e {
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.addSkillsFromCompendiums(update);
await game.l5r5e.ActorL5r5e.addSkillsFromDefaultList(update);
// Set actor value
update.items.forEach(item => {

View File

@@ -5,6 +5,22 @@ export const RegisterSettings = function () {
/* ------------------------------------ */
/* User settings */
/* ------------------------------------ */
game.settings.registerMenu(CONFIG.l5r5e.systemName, "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.systemName, "defaultSkillsList", {
name: "System Migration Version",
scope: "world",
config: false,
type: Array,
default: game.l5r5e.HelpersL5r5e.getDefaultSkillsUuidFromPack(),
});
game.settings.register(CONFIG.l5r5e.systemName, "rnk-deleteOldMessage", {
name: "SETTINGS.RollNKeep.DeleteOldMessage",
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.getDefaultSkillsItems();
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.systemName, 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.systemName, 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.systemName, DefaultSkillsDialogL5r5e.skillsLisKey) || [];
await game.settings.set(CONFIG.l5r5e.systemName, DefaultSkillsDialogL5r5e.skillsLisKey, skillListUuids.filter((uuid) => uuid !== itemUuid));
// Refresh
this.render(false);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -927,6 +927,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 {
position: relative;
display: inline-block;

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>