Added new dialog settings to configure default skill list.
Added uuid compatibility for tooltips
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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",
|
||||
|
||||
148
system/scripts/settings/default-skills-dialog.js
Normal file
148
system/scripts/settings/default-skills-dialog.js
Normal 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
@@ -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;
|
||||
|
||||
19
system/templates/settings/default-skills-dialog.html
Normal file
19
system/templates/settings/default-skills-dialog.html
Normal 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>
|
||||
Reference in New Issue
Block a user