From dad790090eae27bf7cacf553030097065d4cb8e2 Mon Sep 17 00:00:00 2001 From: U~man Date: Thu, 2 Jul 2020 12:10:14 +0200 Subject: [PATCH] ENH: Inventory --- src/lang/en.json | 36 ++-- src/module/actor/actor-sheet.js | 183 ++++++++++-------- src/module/actor/character-sheet.js | 77 ++++---- src/module/actor/monster-sheet.js | 27 --- src/module/preloadTemplates.js | 1 + src/module/settings.js | 2 +- src/scss/actor-base.scss | 52 +---- src/scss/character.scss | 112 ++++++++++- src/scss/monster.scss | 64 +++++- src/template.json | 6 +- src/templates/actors/character-sheet.html | 6 + .../partials/character-abilities-tab.html | 46 +++++ .../partials/character-attributes-tab.html | 36 ---- .../partials/character-inventory-tab.html | 162 +++++++++++++--- 14 files changed, 529 insertions(+), 281 deletions(-) create mode 100644 src/templates/actors/partials/character-abilities-tab.html diff --git a/src/lang/en.json b/src/lang/en.json index a18a895..a4b1af9 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -75,6 +75,7 @@ "OSE.category.attributes": "Attributes", "OSE.category.inventory": "Inventory", + "OSE.category.abilities": "Abilities", "OSE.category.spells": "Spells", "OSE.category.notes": "Notes", @@ -92,18 +93,25 @@ "OSE.Setting.VariableWeaponDamage": "Variable Weapon Damage", "OSE.Setting.VariableWeaponDamageHint": "Weapons have different damage dice", - "OSE.ItemWeight": "Weight", - "OSE.ItemCost": "Cost", - "OSE.ItemQuantity": "Quantity", - "OSE.ItemRoll": "Roll", - "OSE.WeaponDamage": "Damage", - "OSE.WeaponMelee": "Melee", - "OSE.WeaponMissile": "Missile", - "OSE.WeaponSlow": "Slow", - "OSE.SpellRange": "Range", - "OSE.SpellClass": "Class", - "OSE.SpellDuration": "Duration", - "OSE.SpellLevel": "Level", - "OSE.ArmorAC": "AC", - "OSE.ArmorAAC": "AAC" + "OSE.items.Equip": "Equip", + "OSE.items.Unequip": "Unequip", + "OSE.items.Misc": "Misc", + "OSE.items.Weapons": "Weapons", + "OSE.items.Armors": "Armors", + "OSE.items.Weight": "Wght.", + "OSE.items.Qualities": "Qualities", + "OSE.items.Notes": "Notes", + "OSE.items.Cost": "Cost", + "OSE.items.Quantity": "Qt.", + "OSE.items.Roll": "Roll", + "OSE.items.Damage": "Damage", + "OSE.items.Melee": "Melee", + "OSE.items.Missile": "Missile", + "OSE.items.Slow": "Slow", + "OSE.spells.Range": "Range", + "OSE.spells.Class": "Class", + "OSE.spells.Duration": "Duration", + "OSE.spells.Level": "Level", + "OSE.items.ArmorAC": "AC", + "OSE.items.ArmorAAC": "AAC" } \ No newline at end of file diff --git a/src/module/actor/actor-sheet.js b/src/module/actor/actor-sheet.js index f05273f..aaad751 100644 --- a/src/module/actor/actor-sheet.js +++ b/src/module/actor/actor-sheet.js @@ -2,87 +2,116 @@ import { OseActor } from "./entity.js"; import { OseEntityTweaks } from "../dialog/entity-tweaks.js"; export class OseActorSheet extends ActorSheet { - constructor(...args) { - super(...args); - } - /* -------------------------------------------- */ - - getData() { - const data = super.getData(); + constructor(...args) { + super(...args); + } + /* -------------------------------------------- */ - data.config = CONFIG.OSE; - // Settings - data.config.ascendingAC = game.settings.get('ose', 'ascendingAC'); - - return data; - } + getData() { + const data = super.getData(); - activateListeners(html) { - html.find('.saving-throw .attribute-name a').click(ev => { - let actorObject = this.actor; - let element = event.currentTarget; - let save = element.parentElement.parentElement.dataset.save; - actorObject.rollSave(save, { event: event }); - }) + data.config = CONFIG.OSE; + // Settings + data.config.ascendingAC = game.settings.get("ose", "ascendingAC"); - super.activateListeners(html); - } + // Prepare owned items + this._prepareItems(data); - // Override to set resizable initial size - async _renderInner(...args) { - const html = await super._renderInner(...args); - this.form = html[0]; - - // Resize resizable classes - let resizable = html.find('.resizable'); - if (resizable.length == 0) { - return; - } - resizable.each((_, el) => { - let heightDelta = this.position.height - (this.options.height); - el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`; - }); - return html; - } - - async _onResize(event) { - super._onResize(event); - let html = $(event.path); - let resizable = html.find('.resizable'); - resizable.each((_, el) => { - let heightDelta = this.position.height - (this.options.height); - el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`; - }); - } + return data; + } - - _onConfigureActor(event) { - event.preventDefault(); - new OseEntityTweaks(this.actor, { - top: this.position.top + 40, - left: this.position.left + (this.position.width - 400) / 2, - }).render(true); - } + /** + * Organize and classify Owned Items for Character sheets + * @private + */ + _prepareItems(data) { + // Partition items by category + let [inventory, weapons, armors, abilities, spells] = data.items.reduce( + (arr, item) => { + // Classify items into types + if (item.type === "item") arr[0].push(item); + else if (item.type === "weapon") arr[1].push(item); + else if (item.type === "armor") arr[2].push(item); + else if (item.type === "ability") arr[3].push(item); + else if (item.type === "spell") arr[4].push(item); + return arr; + }, + [[], [], [], [], []] + ); - /** - * Extend and override the sheet header buttons - * @override - */ - _getHeaderButtons() { - let buttons = super._getHeaderButtons(); - - // Token Configuration - const canConfigure = game.user.isGM || this.actor.owner; - if (this.options.editable && canConfigure) { - buttons = [ - { - label: 'Tweaks', - class: 'configure-actor', - icon: 'fas fa-dice', - onclick: (ev) => this._onConfigureActor(ev), - }, - ].concat(buttons); - } - return buttons; + // Assign and return + data.inventory = inventory; + data.weapons = weapons; + data.armors = armors; + data.spells = spells; + data.abilities = abilities; + } + + activateListeners(html) { + html.find(".saving-throw .attribute-name a").click((ev) => { + let actorObject = this.actor; + let element = event.currentTarget; + let save = element.parentElement.parentElement.dataset.save; + actorObject.rollSave(save, { event: event }); + }); + + super.activateListeners(html); + } + + // Override to set resizable initial size + async _renderInner(...args) { + const html = await super._renderInner(...args); + this.form = html[0]; + + // Resize resizable classes + let resizable = html.find(".resizable"); + if (resizable.length == 0) { + return; } -} \ No newline at end of file + resizable.each((_, el) => { + let heightDelta = this.position.height - this.options.height; + el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`; + }); + return html; + } + + async _onResize(event) { + super._onResize(event); + let html = $(event.path); + let resizable = html.find(".resizable"); + resizable.each((_, el) => { + let heightDelta = this.position.height - this.options.height; + el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`; + }); + } + + _onConfigureActor(event) { + event.preventDefault(); + new OseEntityTweaks(this.actor, { + top: this.position.top + 40, + left: this.position.left + (this.position.width - 400) / 2, + }).render(true); + } + + /** + * Extend and override the sheet header buttons + * @override + */ + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + + // Token Configuration + const canConfigure = game.user.isGM || this.actor.owner; + if (this.options.editable && canConfigure) { + buttons = [ + { + label: "Tweaks", + class: "configure-actor", + icon: "fas fa-dice", + onclick: (ev) => this._onConfigureActor(ev), + }, + ].concat(buttons); + } + return buttons; + } +} diff --git a/src/module/actor/character-sheet.js b/src/module/actor/character-sheet.js index 144e430..fe569fd 100644 --- a/src/module/actor/character-sheet.js +++ b/src/module/actor/character-sheet.js @@ -42,57 +42,44 @@ export class OseActorSheetCharacter extends OseActorSheet { for (let [a, score] of Object.entries(data.data.scores)) { data.data.scores[a].label = game.i18n.localize(`OSE.scores.${a}`); } - // Prepare owned items - this._prepareItems(data); // Settings - data.config.individualInit = game.settings.get('ose', 'individualInit'); + data.config.variableWeaponDamage = game.settings.get( + "ose", + "variableWeaponDamage" + ); + data.config.ascendingAC = game.settings.get("ose", "ascendingAC"); + data.config.individualInit = game.settings.get("ose", "individualInit"); return data; } - /** - * Organize and classify Owned Items for Character sheets - * @private - */ - _prepareItems(data) { - // Partition items by category - let [inventory, abilities, spells] = data.items.reduce( - (arr, item) => { - // Classify items into types - if (item.type === "item") arr[0].push(item); - if (item.type === "ability") arr[1].push(item); - else if (item.type === "spell") arr[2].push(item); - return arr; - }, - [[], [], [], []] - ); - - // Assign and return - data.inventory = inventory; - data.spells = spells; - data.abilities = abilities; - } - /* -------------------------------------------- */ _onItemSummary(event) { event.preventDefault(); let li = $(event.currentTarget).parents(".item"), - item = this.actor.getOwnedItem(li.data("item-id")), - description = TextEditor.enrichHTML(item.data.data.description); + item = this.actor.getOwnedItem(li.data("item-id")), + description = TextEditor.enrichHTML(item.data.data.description); // Toggle summary - if ( li.hasClass("expanded") ) { - let summary = li.parents('.item-entry').children(".item-summary"); + if (li.hasClass("expanded")) { + let summary = li.parents(".item-entry").children(".item-summary"); summary.slideUp(200, () => summary.remove()); } else { let div = $(`
${description}
`); - li.parents('.item-entry').append(div.hide()); + li.parents(".item-entry").append(div.hide()); div.slideDown(200); } li.toggleClass("expanded"); } + async _onQtChange(event) { + event.preventDefault(); + const itemId = event.currentTarget.closest(".item").dataset.itemId; + const item = this.actor.getOwnedItem(itemId); + return item.update({ "data.quantity.value": parseInt(event.target.value) }); + } + /** * Activate event listeners using the prepared sheet HTML * @param html {HTML} The prepared HTML object ready to be rendered into the DOM @@ -128,16 +115,34 @@ export class OseActorSheetCharacter extends OseActorSheet { return this.actor.createOwnedItem(itemData); }); - // Item summaries - html.find('.item .item-name h4').click(event => this._onItemSummary(event)); + //Toggle Equipment + html.find(".item-toggle").click(async (ev) => { + const li = $(ev.currentTarget).parents(".item"); + const item = this.actor.getOwnedItem(li.data("itemId")); + await this.actor.updateOwnedItem({ + _id: li.data("itemId"), + data: { + equipped: !item.data.data.equipped, + }, + }); + }); - - html.find('.ability-score .attribute-name a').click(ev => { + html + .find(".quantity input") + .click((ev) => ev.target.select()) + .change(this._onQtChange.bind(this)); + + // Item summaries + html + .find(".item .item-name h4") + .click((event) => this._onItemSummary(event)); + + html.find(".ability-score .attribute-name a").click((ev) => { let actorObject = this.actor; let element = event.currentTarget; let score = element.parentElement.parentElement.dataset.score; actorObject.rollCheck(score, { event: event }); - }) + }); // Handle default listeners last so system listeners are triggered first super.activateListeners(html); diff --git a/src/module/actor/monster-sheet.js b/src/module/actor/monster-sheet.js index e0820ff..e799ed2 100644 --- a/src/module/actor/monster-sheet.js +++ b/src/module/actor/monster-sheet.js @@ -39,39 +39,12 @@ export class OseActorSheetMonster extends OseActorSheet { getData() { const data = super.getData(); - // Prepare owned items - this._prepareItems(data); - // Settings data.config.morale = game.settings.get('ose', 'morale'); return data; } - /** - * Organize and classify Owned Items for Character sheets - * @private - */ - _prepareItems(data) { - // Partition items by category - let [inventory, abilities, spells] = data.items.reduce( - (arr, item) => { - // Classify items into types - if (item.type === "item") arr[0].push(item); - if (item.type === "ability") arr[1].push(item); - else if (item.type === "spell") arr[2].push(item); - return arr; - }, - [[], [], [], []] - ); - - // Assign and return - data.inventory = inventory; - data.spells = spells; - data.abilities = abilities; - } - - _onItemSummary(event) { event.preventDefault(); let li = $(event.currentTarget).parents(".item"), diff --git a/src/module/preloadTemplates.js b/src/module/preloadTemplates.js index 9bbff1c..4b4c546 100644 --- a/src/module/preloadTemplates.js +++ b/src/module/preloadTemplates.js @@ -7,6 +7,7 @@ export const preloadHandlebarsTemplates = async function () { //Sheet tabs 'systems/ose/templates/actors/partials/character-header.html', 'systems/ose/templates/actors/partials/character-attributes-tab.html', + 'systems/ose/templates/actors/partials/character-abilities-tab.html', 'systems/ose/templates/actors/partials/character-spells-tab.html', 'systems/ose/templates/actors/partials/character-inventory-tab.html', diff --git a/src/module/settings.js b/src/module/settings.js index 00f2f46..4a01fcc 100644 --- a/src/module/settings.js +++ b/src/module/settings.js @@ -35,7 +35,7 @@ export const registerSettings = function () { config: true }); - game.settings.register('ose', 'variableDamage', { + game.settings.register('ose', 'variableWeaponDamage', { name: game.i18n.localize('OSE.Setting.VariableWeaponDamage'), hint: game.i18n.localize('OSE.Setting.VariableWeaponDamageHint'), default: false, diff --git a/src/scss/actor-base.scss b/src/scss/actor-base.scss index 3009bfe..b085386 100644 --- a/src/scss/actor-base.scss +++ b/src/scss/actor-base.scss @@ -129,57 +129,7 @@ } .editor { height: 300px; - } - .inventory { - .item-entry { - padding: 0; - margin: 0; - list-style: none; - .item { - .item-image { - flex: 0 0 24px; - height: 24px; - background-size: cover; - } - .item-name { - line-height: 24px; - height: 24px; - overflow: hidden; - h4 { - text-indent: 4px; - margin: 0; - cursor: pointer; - &:hover { - color: whitesmoke; - background: linear-gradient( - 45deg, - rgba(0, 0, 0, 0.5), - transparent - ); - } - } - } - .item-controls { - line-height: 24px; - flex: 0 0 32px; - margin: 0 3px; - .fas { - color: $colorTan; - font-size: 12px; - &:hover { - color: $colorDark; - } - } - } - } - .item-summary { - font-size: 12px; - padding: 0 4px; - } - &:nth-child(odd) { - background: rgba(0, 0, 0, 0.1); - } - } + padding: 4px; } } } diff --git a/src/scss/character.scss b/src/scss/character.scss index bdb073b..d71dbad 100644 --- a/src/scss/character.scss +++ b/src/scss/character.scss @@ -18,13 +18,111 @@ /* Sheet Body */ /* ----------------------------------------- */ .sheet-body { - } - - - .abilities { - .panel-content { - height: 250px; - overflow: auto; + .inventory { + overflow: auto; + height: 520px; + .items-section { + .header-field { + margin: 0; + } + } + .item-titles { + text-align: center; + margin-top: 1px; + padding-top: 2px; + .item-name { + text-align: left; + text-indent: 8px; + } + font-weight: 300; + font-size: 12px; + background: $colorDark; + color: white; + } + .item-list { + list-style: none; + margin: 0; + padding: 0; + li { + padding: 0 2px; + } + .item-header { + @extend %header-field !optional; + padding: 0px; + margin-bottom: 0px; + } + .item-entry { + &:nth-child(even) { + background: rgba(0, 0, 0, 0.1); + } + } + .item { + line-height: 30px; + height: 30px; + overflow: hidden; + } + .item-equipped { + grid-area: item-equipped; + justify-self: center; + } + .item-name { + text-indent: 8px; + text-align: left; + overflow: hidden; + height: 30px; + margin: 0; + line-height: 30px; + .item-image { + flex-basis: 30px; + flex-grow: 0; + background-size: contain; + background-repeat: no-repeat; + &:hover { + background-image: url("/icons/svg/d20-grey.svg") !important; + cursor: pointer; + } + } + h4 { + margin: 0; + } + } + } + .field-longer { + text-indent: 8px; + text-align: left; + flex-basis: 150px; + font-size: 12px; + flex-grow: 0; + } + .field-long { + flex-basis: 65px; + flex-grow: 0; + text-align: center; + font-size: 12px; + } + .field-short { + font-size: 12px; + flex-basis: 45px; + flex-grow: 0; + text-align: center; + &.quantity { + margin: 4px 0; + display: flex; + input { + border-bottom: none; + } + } + } + .item-controls { + font-size: 12px; + flex-basis: 60px; + flex-grow: 0; + text-align: right; + margin-right: 4px; + .item-unequipped { + color: rgba(0, 0, 0, 0.2); + } + } } } diff --git a/src/scss/monster.scss b/src/scss/monster.scss index 96cb216..5654c8d 100644 --- a/src/scss/monster.scss +++ b/src/scss/monster.scss @@ -1,6 +1,6 @@ .ose.actor.monster { - min-height: 565px; - min-width: 460px; + min-height: 565px; + min-width: 460px; .sheet-body { .editor { height: 300px; @@ -9,13 +9,65 @@ .abilities { .panel-content { height: 230px; - overflow: auto; + overflow: auto; } } .attribute-row { - padding: 2px; - .abilities { - margin: 2px; + padding: 2px; + .abilities { + margin: 2px; + } + } + + .inventory { + .item-entry { + padding: 0; + margin: 0; + list-style: none; + .item { + .item-image { + flex: 0 0 30px; + height: 30px; + background-size: cover; + } + .item-name { + line-height: 30px; + height: 30px; + overflow: hidden; + h4 { + text-indent: 4px; + margin: 0; + cursor: pointer; + &:hover { + color: whitesmoke; + background: linear-gradient( + 45deg, + rgba(0, 0, 0, 0.5), + transparent + ); + } + } + } + .item-controls { + line-height: 30px; + flex: 0 0 32px; + margin: 0 3px; + .fas { + color: $colorTan; + font-size: 12px; + &:hover { + color: $colorDark; + } + } + } } + .item-summary { + font-size: 13px; + padding: 0 4px; + } + &:nth-child(odd) { + background: rgba(0, 0, 0, 0.1); + } + } } } diff --git a/src/template.json b/src/template.json index 2dab957..4d8ec4c 100644 --- a/src/template.json +++ b/src/template.json @@ -13,10 +13,12 @@ "max": 20 }, "ac": { + "naked": 0, "value": 0, "mod": 0 }, "aac": { + "naked": 0, "value": 0, "mod": 0 }, @@ -141,7 +143,7 @@ "max": 0 }, "cost": 0, - "weight": 0 + "weight": 80 }, "weapon": { "description": "", @@ -151,6 +153,7 @@ "missile": true, "ranged": true, "cost": 0, + "equipped": false, "weight": 0 }, "armor": { @@ -158,6 +161,7 @@ "ac": 9, "aac": 10, "cost": 0, + "equipped": false, "weight": 0 }, "spell": { diff --git a/src/templates/actors/character-sheet.html b/src/templates/actors/character-sheet.html index 1d2ede3..4fa3b7f 100644 --- a/src/templates/actors/character-sheet.html +++ b/src/templates/actors/character-sheet.html @@ -12,6 +12,9 @@ {{localize "OSE.category.inventory"}} + + {{localize "OSE.category.abilities"}} + {{#if data.spells.enabled}} {{localize "OSE.category.spells"}} @@ -30,6 +33,9 @@
{{> "systems/ose/templates/actors/partials/character-inventory-tab.html"}}
+
+ {{> "systems/ose/templates/actors/partials/character-abilities-tab.html"}} +
{{#if data.spells.enabled}}
{{> "systems/ose/templates/actors/partials/character-spells-tab.html"}} diff --git a/src/templates/actors/partials/character-abilities-tab.html b/src/templates/actors/partials/character-abilities-tab.html new file mode 100644 index 0000000..a16022e --- /dev/null +++ b/src/templates/actors/partials/character-abilities-tab.html @@ -0,0 +1,46 @@ +
+ +
    +
    + {{#each abilities as |item|}} +
  • +
    +
    +
    +

    + {{item.name~}} +

    +
    +
    + {{#if ../owner}} + + + {{/if}} +
    +
    +
  • + {{/each}} +
    +
+
diff --git a/src/templates/actors/partials/character-attributes-tab.html b/src/templates/actors/partials/character-attributes-tab.html index 540842b..b802c76 100644 --- a/src/templates/actors/partials/character-attributes-tab.html +++ b/src/templates/actors/partials/character-attributes-tab.html @@ -111,42 +111,6 @@
- {{!-- Skills and abilities --}} -
-
-

{{localize 'OSE.panel.abilities'}}

-
- {{#if owner}} - - {{/if}} -
-
- -
{{!-- Saving throws --}}