From 5f8d26cbe39670a140893f7e6a08d9825186726c Mon Sep 17 00:00:00 2001 From: sasquach45932 Date: Mon, 8 Apr 2024 16:35:10 +0200 Subject: [PATCH 1/3] Weapon - Ammo assigment --- src/lang/en.json | 4 ++ src/module/actor/actor.js | 32 ++++++++++-- src/module/data/item/WeaponDataModel.js | 14 ++++- src/module/item/sheets/base-item-sheet.js | 10 +++- src/module/utils/handlebars-helpers.js | 63 +++++++++++++++++++++-- src/templates/item/item-weapon-sheet.hbs | 25 +++++++++ 6 files changed, 136 insertions(+), 12 deletions(-) diff --git a/src/lang/en.json b/src/lang/en.json index 584e80d5..d263e434 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -727,6 +727,7 @@ "DL.WealthLifestyle": "Lifestyle: ", "DL.WealthTitle": "Wealth", "DL.WeaponAdd": "Add weapon", + "DL.WeaponAmmoRequired": "Ammunition required", "DL.WeaponAttack20": "Extra Damage 20+", "DL.WeaponAttack20Text": "Attack Roll 20+", "DL.WeaponAttackModifier": "Attack Modifier", @@ -744,6 +745,9 @@ "DL.WeaponModifiers": "Modifiers", "DL.WeaponName": "Weapon name", "DL.WeaponProperties": "Properties", + "DL.WeaponResourceComsumption": "Resource consumption", + "DL.WeaponRunOutOfAmmo": "{weaponName} has run out of its ammunition.", + "DL.WeaponNoAmmo": "{weaponName} has no assigned ammunition.", "DL.WeaponTitle": "Weapons", "DL.WeaponWear": "Equip weapon", "DL.With": "with" diff --git a/src/module/actor/actor.js b/src/module/actor/actor.js index 06a3bf1a..62ad9cbf 100644 --- a/src/module/actor/actor.js +++ b/src/module/actor/actor.js @@ -417,6 +417,23 @@ export class DemonlordActor extends Actor { */ async rollWeaponAttack(itemID, _options = {event: null}) { const item = this.getEmbeddedDocument('Item', itemID) + let ammoItem + + // Check if there is an ammo for weapon + if (item.system.consume.ammorequired) { + ammoItem = await this.ammo.find((x) => x.id === item.system.consume.ammoitemid) + if (ammoItem) { + if (ammoItem.system.quantity === 0) { + return ui.notifications.info(game.i18n.format("DL.WeaponRunOutOfAmmo", { + weaponName: item.name + })) + } + } else { + return ui.notifications.info(game.i18n.format("DL.WeaponNoAmmo", { + weaponName: item.name + })) + } + } // If no attribute to roll, roll without modifiers and boons const attribute = item.system.action?.attack @@ -427,11 +444,16 @@ export class DemonlordActor extends Actor { // Check if actor is blocked by an affliction if (!DLAfflictions.isActorBlocked(this, 'action', attribute)) - launchRollDialog(game.i18n.localize('DL.DialogAttackRoll') + game.i18n.localize(item.name), async html => - await this.rollAttack(item, html.find('[id="boonsbanes"]').val(), html.find('[id="modifier"]').val()), - ) - } - + launchRollDialog(game.i18n.localize('DL.DialogAttackRoll') + game.i18n.localize(item.name), async html => { + await this.rollAttack(item, html.find('[id="boonsbanes"]').val(), html.find('[id="modifier"]').val()) + // Decrease ammo quantity + if (item.system.consume.ammorequired) { + await ammoItem.update({ + 'system.quantity': ammoItem.system.quantity - item.system.consume.amount + }) + } + }) + } /* -------------------------------------------- */ async rollAttribute(attribute, inputBoons, inputModifier) { diff --git a/src/module/data/item/WeaponDataModel.js b/src/module/data/item/WeaponDataModel.js index a64eefff..28291058 100644 --- a/src/module/data/item/WeaponDataModel.js +++ b/src/module/data/item/WeaponDataModel.js @@ -17,7 +17,12 @@ export default class WeaponDataModel extends foundry.abstract.DataModel { wear: makeBoolField(true), quantity: makeIntField(1), availability: makeStringField(), - value: makeStringField() + value: makeStringField(), + consume: new foundry.data.fields.SchemaField({ + ammorequired: makeBoolField(false), + amount: makeIntField(1), + ammoitemid: makeStringField() + }), } } } @@ -37,6 +42,11 @@ export function makeWeaponSchema() { wear: makeBoolField(true), quantity: makeIntField(1), availability: makeStringField(), - value: makeStringField() + value: makeStringField(), + consume: new foundry.data.fields.SchemaField({ + ammorequired: makeBoolField(false), + amount: makeIntField(1), + ammoitemid: makeStringField() + }), }) } \ No newline at end of file diff --git a/src/module/item/sheets/base-item-sheet.js b/src/module/item/sheets/base-item-sheet.js index 8172c6b7..6a504279 100644 --- a/src/module/item/sheets/base-item-sheet.js +++ b/src/module/item/sheets/base-item-sheet.js @@ -164,7 +164,7 @@ export default class DLBaseItemSheet extends ItemSheet { }) tippy('.dl-new-project-2.dropdown', { content(reference) { - html = buildDropdownList(reference.attributes.name.value, reference.attributes.value.value) + html = buildDropdownList(reference.attributes.name.value, reference.attributes.value.value, data) return html }, allowHTML: true, @@ -190,6 +190,14 @@ export default class DLBaseItemSheet extends ItemSheet { inputs.focus(ev => ev.currentTarget.select()) } + // Weapon ammo required checkbox + html.find('.dl-ammorequired').click(async _ => { + await this.document.update({ + 'system.consume.ammorequired': !this.document.system.consume.ammorequired, + 'system.consume.ammoitemid' : '' + }) + }) + // Item autoDestroy checkbox html.find('.dl-autodestroy').click(async _ => { await this.document.update({'system.autoDestroy': !this.document.system.autoDestroy}) diff --git a/src/module/utils/handlebars-helpers.js b/src/module/utils/handlebars-helpers.js index 3079c0db..8c8fe985 100644 --- a/src/module/utils/handlebars-helpers.js +++ b/src/module/utils/handlebars-helpers.js @@ -50,7 +50,9 @@ export function registerHandlebarsHelpers() { ) Handlebars.registerHelper('dlAvailabilityDropdown', (groupName, checkedKey) => _buildAvailabilityDropdownItem(groupName, checkedKey)) Handlebars.registerHelper('dlConsumableDropdown', (groupName, checkedKey) => _buildConsumableDropdownItem(groupName, checkedKey)) - Handlebars.registerHelper('dlCheckCharacteristicsIsNull', (actorData) => _CheckCharacteristicsIsNull(actorData)); + Handlebars.registerHelper('dlAmmoDropdown', (groupName, checkedKey, weapon) => _buildAmmoDropdownItem(groupName, checkedKey, weapon)) + Handlebars.registerHelper('dlCheckItemOnActor', (data) => _CheckItemOnActor(data)) + Handlebars.registerHelper('dlCheckCharacteristicsIsNull', (actorData) => _CheckCharacteristicsIsNull(actorData)); } // ---------------------------------------------------- @@ -72,7 +74,7 @@ function _getAttributes(groupName) { } else if (groupName === 'system.consumabletype') { attributes = ['', 'D', 'F', 'P', 'V', 'T'] - } + } return attributes } @@ -102,7 +104,15 @@ function _CheckCharacteristicsIsNull(actorData) { return true } else { return false - } + } +} + +function _CheckItemOnActor(data) { + if (data.indexOf("Actor.") === -1) { + return false + } else { + return true + } } function _buildDropdownItem(groupName, checkedKey) { @@ -174,7 +184,7 @@ function _buildDropdownItemWithValue(groupName, checkedKey, valueName, valueKey) } -export function buildDropdownList(groupName, checkedKey) { +export function buildDropdownList(groupName, checkedKey, data) { let labelPrefix = 'DL.Attribute' let iconPrefix = 'dl-icon-' let useIcon = true @@ -182,6 +192,7 @@ export function buildDropdownList(groupName, checkedKey) { if (groupName === 'path-type') return _buildPathTypeDropdownList(checkedKey) if (groupName === 'level.attributeSelect') return _buildPathAttributeSelectDropdownList(checkedKey) if (groupName.startsWith('level.attributeSelectTwoSet')) return _buildPathAttributeTwoSetDropdownList(groupName, checkedKey) + if (groupName === 'system.consume.ammoitemid') return _buildAmmoDropdownList (groupName, checkedKey, data) if (groupName === 'system.hands') {labelPrefix = 'DL.WeaponHands'; useIcon = false} if (groupName === 'system.consumabletype') {labelPrefix = 'DL.ConsumableType'; useIcon = false} if (groupName === 'system.availability') {labelPrefix = 'DL.Availability', iconPrefix = 'dl-icon-availability-'} @@ -336,6 +347,28 @@ function _buildPathAttributeTwoSetViewSelector(attributeName, isSelected, select return new Handlebars.SafeString(html) } +function _buildAmmoDropdownList(groupName, checkedKey, data) { + let attributes = [{id: '', name: i18n('DL.None')}] + let html = `
` + if (!data.document) return "" + let baseItemUuid = data.document.uuid + let actor = fromUuidSync(baseItemUuid.substr(0,baseItemUuid.search(".Item."))) + actor.items.forEach((item) => { + if (item.type === "ammo") attributes.push({id: item._id, name: item.name}) + }); + for (let attribute of attributes) { + const value = attribute.id + const checked = value === checkedKey ? 'checked' : '' + const label = value ? attribute.name : i18n('DL.None') + html += `
+ + ${label} +
` + } + html += `
` + return new Handlebars.SafeString(html) +} + // ---------------------------------------------------- function _buildAvailabilityDropdownItem(groupName, checkedKey) { @@ -366,3 +399,25 @@ function _buildConsumableDropdownItem(groupName, checkedKey) { return new Handlebars.SafeString(html) } } + +function _buildAmmoDropdownItem(groupName, checkedKey, weapon) { + let actorUuid = weapon.parent.uuid + let actor = fromUuidSync(actorUuid.substr(0,actorUuid.search(".Item."))) + let attributes = [{id: "", name: ""}] + + actor.items.forEach((item) => { + if (item.type === "ammo") attributes.push({id: item._id, name: item.name}) + }); + + if (!attributes.find ((x) => x.id === checkedKey)) checkedKey = '' + + for (let attribute of attributes) { + if (checkedKey != attribute.id) continue + const label = attribute.id === '' ? i18n("DL.None") : attribute.name + let html = + `` + return new Handlebars.SafeString(html) + } +} \ No newline at end of file diff --git a/src/templates/item/item-weapon-sheet.hbs b/src/templates/item/item-weapon-sheet.hbs index 9aec5d75..3a37d8b8 100644 --- a/src/templates/item/item-weapon-sheet.hbs +++ b/src/templates/item/item-weapon-sheet.hbs @@ -66,6 +66,31 @@
+
+
+ {{localize "DL.WeaponAmmoRequired"}} +
+
+ {{#if (dlCheckItemOnActor system.parent.uuid)}} + {{#if system.consume.ammorequired}} +
+ {{localize "DL.WeaponResourceComsumption"}} +
+
+ + + {{localize "DL.AmmoAmount"}} + + +
+ {{dlAmmoDropdown "system.consume.ammoitemid" system.consume.ammoitemid system}} +
+
+ {{/if}} + {{/if}} +
+
+ {{#if system.description}}
{{localize "DL.TabsDescription"}}
From 642e48934be6f8106c0cbd4212ba5c2a94cae50d Mon Sep 17 00:00:00 2001 From: sasquach45932 Date: Mon, 8 Apr 2024 21:11:19 +0200 Subject: [PATCH 2/3] Weapon - Ammo assigment - ESLint fix --- src/module/utils/handlebars-helpers.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/module/utils/handlebars-helpers.js b/src/module/utils/handlebars-helpers.js index 8c8fe985..f8324168 100644 --- a/src/module/utils/handlebars-helpers.js +++ b/src/module/utils/handlebars-helpers.js @@ -1,3 +1,4 @@ +/* global fromUuidSync */ import {capitalize, enrichHTMLUnrolled, i18n} from "./utils"; import {handlebarsBuildEditor} from "./editor"; @@ -420,4 +421,4 @@ function _buildAmmoDropdownItem(groupName, checkedKey, weapon) {
` return new Handlebars.SafeString(html) } -} \ No newline at end of file +} From 6d240346d8664dbe402064ce4dcb19c951029170 Mon Sep 17 00:00:00 2001 From: sasquach45932 Date: Tue, 9 Apr 2024 17:59:12 +0200 Subject: [PATCH 3/3] Weapon - Ammo assigment - Prettier + warn --- src/module/actor/actor.js | 44 +++++++++++---------- src/module/utils/handlebars-helpers.js | 55 ++++++++++++-------------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/module/actor/actor.js b/src/module/actor/actor.js index 62ad9cbf..2f1407ab 100644 --- a/src/module/actor/actor.js +++ b/src/module/actor/actor.js @@ -418,20 +418,24 @@ export class DemonlordActor extends Actor { async rollWeaponAttack(itemID, _options = {event: null}) { const item = this.getEmbeddedDocument('Item', itemID) let ammoItem - + // Check if there is an ammo for weapon if (item.system.consume.ammorequired) { - ammoItem = await this.ammo.find((x) => x.id === item.system.consume.ammoitemid) + ammoItem = await this.ammo.find(x => x.id === item.system.consume.ammoitemid) if (ammoItem) { - if (ammoItem.system.quantity === 0) { - return ui.notifications.info(game.i18n.format("DL.WeaponRunOutOfAmmo", { - weaponName: item.name - })) - } + if (ammoItem.system.quantity === 0) { + return ui.notifications.warn( + game.i18n.format('DL.WeaponRunOutOfAmmo', { + weaponName: item.name, + }), + ) + } } else { - return ui.notifications.info(game.i18n.format("DL.WeaponNoAmmo", { - weaponName: item.name - })) + return ui.notifications.warn( + game.i18n.format('DL.WeaponNoAmmo', { + weaponName: item.name, + }), + ) } } @@ -444,16 +448,16 @@ export class DemonlordActor extends Actor { // Check if actor is blocked by an affliction if (!DLAfflictions.isActorBlocked(this, 'action', attribute)) - launchRollDialog(game.i18n.localize('DL.DialogAttackRoll') + game.i18n.localize(item.name), async html => { - await this.rollAttack(item, html.find('[id="boonsbanes"]').val(), html.find('[id="modifier"]').val()) - // Decrease ammo quantity - if (item.system.consume.ammorequired) { - await ammoItem.update({ - 'system.quantity': ammoItem.system.quantity - item.system.consume.amount - }) - } - }) - } + launchRollDialog(game.i18n.localize('DL.DialogAttackRoll') + game.i18n.localize(item.name), async html => { + await this.rollAttack(item, html.find('[id="boonsbanes"]').val(), html.find('[id="modifier"]').val()) + // Decrease ammo quantity + if (item.system.consume.ammorequired) { + await ammoItem.update({ + 'system.quantity': ammoItem.system.quantity - item.system.consume.amount, + }) + } + }) + } /* -------------------------------------------- */ async rollAttribute(attribute, inputBoons, inputModifier) { diff --git a/src/module/utils/handlebars-helpers.js b/src/module/utils/handlebars-helpers.js index f8324168..8d989e3a 100644 --- a/src/module/utils/handlebars-helpers.js +++ b/src/module/utils/handlebars-helpers.js @@ -53,7 +53,7 @@ export function registerHandlebarsHelpers() { Handlebars.registerHelper('dlConsumableDropdown', (groupName, checkedKey) => _buildConsumableDropdownItem(groupName, checkedKey)) Handlebars.registerHelper('dlAmmoDropdown', (groupName, checkedKey, weapon) => _buildAmmoDropdownItem(groupName, checkedKey, weapon)) Handlebars.registerHelper('dlCheckItemOnActor', (data) => _CheckItemOnActor(data)) - Handlebars.registerHelper('dlCheckCharacteristicsIsNull', (actorData) => _CheckCharacteristicsIsNull(actorData)); + Handlebars.registerHelper('dlCheckCharacteristicsIsNull', (actorData) => _CheckCharacteristicsIsNull(actorData)) } // ---------------------------------------------------- @@ -72,8 +72,7 @@ function _getAttributes(groupName) { attributes = ['', 'C', 'U', 'R', 'E'] } else if (groupName === 'system.requirement.attribute') { attributes = ['', 'strength', 'agility', 'intellect', 'will', 'perception'] - } - else if (groupName === 'system.consumabletype') { + } else if (groupName === 'system.consumabletype') { attributes = ['', 'D', 'F', 'P', 'V', 'T'] } return attributes @@ -101,18 +100,18 @@ function _buildRadioBoxes(groupName, checkedKey) { // ---------------------------------------------------- function _CheckCharacteristicsIsNull(actorData) { - if (actorData === null) { - return true - } else { - return false + if (actorData === null) { + return true + } else { + return false } } function _CheckItemOnActor(data) { - if (data.indexOf("Actor.") === -1) { - return false + if (data.indexOf('Actor.') === -1) { + return false } else { - return true + return true } } @@ -349,14 +348,14 @@ function _buildPathAttributeTwoSetViewSelector(attributeName, isSelected, select } function _buildAmmoDropdownList(groupName, checkedKey, data) { - let attributes = [{id: '', name: i18n('DL.None')}] + let attributes = [{ id: '', name: i18n('DL.None') }] let html = `
` - if (!data.document) return "" - let baseItemUuid = data.document.uuid - let actor = fromUuidSync(baseItemUuid.substr(0,baseItemUuid.search(".Item."))) - actor.items.forEach((item) => { - if (item.type === "ammo") attributes.push({id: item._id, name: item.name}) - }); + if (!data.document) return '' + let baseItemUuid = data.document.uuid + let actor = fromUuidSync(baseItemUuid.substr(0, baseItemUuid.search('.Item.'))) + actor.items.forEach(item => { + if (item.type === 'ammo') attributes.push({ id: item._id, name: item.name }) + }) for (let attribute of attributes) { const value = attribute.id const checked = value === checkedKey ? 'checked' : '' @@ -392,9 +391,8 @@ function _buildConsumableDropdownItem(groupName, checkedKey) { const attributes = ['', 'D', 'F', 'P', 'V', 'T'] for (let attribute of attributes) { if (checkedKey != attribute) continue - const label = attribute === '' ? i18n("DL.ConsumableNone") : i18n(`DL.ConsumableType${attribute}`) - let html = - `