Compare commits

...

6 commits

Author SHA1 Message Date
Carlos Fernandez
2fde61a1d5
[Feature] Make all item types quantifiable in the party (#1808)
* Show notification when invalid item types are added to actors

* Make all item types quantifiable in the party actor

* Remove from comment

* Use isInventoryItem to set quantity

* Fix formatting
2026-04-16 11:12:36 +02:00
Carlos Fernandez
d9b322406d
Fix party sheet rerenders from member updates interfering with currency and note input (#1809) 2026-04-16 10:23:55 +02:00
WBHarry
16c07d23bb Changed diceFaces->dieFaces for consistency 2026-04-16 09:57:16 +02:00
WBHarry
91aff8b10d
[Feature] Advantage Default Dice (#1802) 2026-04-16 03:55:12 -04:00
Carlos Fernandez
7e9385bc39
Add toggle for party sheet (#1806) 2026-04-16 09:22:26 +02:00
Carlos Fernandez
aa8771bf0d
Show notification when invalid item types are added to actors (#1807) 2026-04-16 08:23:25 +02:00
25 changed files with 207 additions and 87 deletions

View file

@ -213,6 +213,9 @@
"headerTitle": "Adversary Reaction Roll" "headerTitle": "Adversary Reaction Roll"
} }
}, },
"Base": {
"CannotAddType": "Cannot add {itemType} items to {actorType} actors."
},
"Character": { "Character": {
"advantageSources": { "advantageSources": {
"label": "Advantage Sources", "label": "Advantage Sources",
@ -2878,6 +2881,10 @@
} }
}, },
"Keybindings": { "Keybindings": {
"partySheet": {
"name": "Toggle Party Sheet",
"hint": "Open or close the active party's sheet"
},
"spotlight": { "spotlight": {
"name": "Spotlight Combatant", "name": "Spotlight Combatant",
"hint": "Move the spotlight to a hovered or selected token that's present in an active encounter" "hint": "Move the spotlight to a hovered or selected token that's present in an active encounter"

View file

@ -123,6 +123,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.advantage = this.config.roll?.advantage; context.advantage = this.config.roll?.advantage;
context.disadvantage = this.config.roll?.disadvantage; context.disadvantage = this.config.roll?.disadvantage;
context.diceOptions = CONFIG.DH.GENERAL.diceTypes; context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
context.dieFaces = CONFIG.DH.GENERAL.dieFaces.reduce((acc, face) => {
acc[face] = `d${face}`;
return acc;
}, {});
context.isLite = this.config.roll?.lite; context.isLite = this.config.roll?.lite;
context.extraFormula = this.config.extraFormula; context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config); context.formula = this.roll.constructFormula(this.config);
@ -152,9 +156,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
} }
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses); if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
if (rest.roll?.dice) { if (rest.roll?.dice) {
Object.entries(rest.roll.dice).forEach(([key, value]) => { this.roll = foundry.utils.mergeObject(this.roll, rest.roll.dice);
this.roll[key] = value;
});
} }
if (rest.hasOwnProperty('trait')) { if (rest.hasOwnProperty('trait')) {
this.config.roll.trait = rest.trait; this.config.roll.trait = rest.trait;
@ -173,6 +175,15 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
this.disadvantage = advantage === -1; this.disadvantage = advantage === -1;
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage; this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
if (this.config.roll.advantage === 1 && this.config.data.rules.roll.defaultAdvantageDice) {
const faces = Number.parseInt(this.config.data.rules.roll.defaultAdvantageDice);
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
} else if (this.config.roll.advantage === -1 && this.config.data.rules.roll.defaultDisadvantageDice) {
const faces = Number.parseInt(this.config.data.rules.roll.defaultDisadvantageDice);
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
}
this.render(); this.render();
} }

View file

@ -38,13 +38,15 @@ export default class ItemTransferDialog extends HandlebarsApplicationMixin(Appli
originActor ??= item?.actor; originActor ??= item?.actor;
const homebrewKey = CONFIG.DH.SETTINGS.gameSettings.Homebrew; const homebrewKey = CONFIG.DH.SETTINGS.gameSettings.Homebrew;
const currencySetting = game.settings.get(CONFIG.DH.id, homebrewKey).currency?.[currency] ?? null; const currencySetting = game.settings.get(CONFIG.DH.id, homebrewKey).currency?.[currency] ?? null;
const max = item?.system.quantity ?? originActor.system.gold[currency] ?? 0;
return { return {
originActor, originActor,
targetActor, targetActor,
itemImage: item?.img, itemImage: item?.img,
currencyIcon: currencySetting?.icon, currencyIcon: currencySetting?.icon,
max: item?.system.quantity ?? originActor.system.gold[currency] ?? 0, max,
initial: targetActor.system.metadata.quantifiable?.includes(item.type) ? max : 1,
title: item?.name ?? currencySetting?.label title: item?.name ?? currencySetting?.label
}; };
} }

View file

@ -73,7 +73,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
.hideAttribution; .hideAttribution;
// Prepare inventory data // Prepare inventory data
if (['party', 'character'].includes(this.document.type)) { if (this.document.system.metadata.hasInventory) {
context.inventory = { context.inventory = {
currencies: {}, currencies: {},
weapons: this.document.itemTypes.weapon.sort((a, b) => a.sort - b.sort), weapons: this.document.itemTypes.weapon.sort((a, b) => a.sort - b.sort),
@ -283,11 +283,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
async _onDropItem(event, item) { async _onDropItem(event, item) {
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const originActor = item.actor; const originActor = item.actor;
if ( if (!originActor || originActor.uuid === this.document.uuid || !this.document.system.metadata.hasInventory) {
item.actor?.uuid === this.document.uuid ||
!originActor ||
!['character', 'party'].includes(this.document.type)
) {
return super._onDropItem(event, item); return super._onDropItem(event, item);
} }
@ -302,45 +298,77 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
); );
} }
if (item.system.metadata.isQuantifiable) { // Perform the actual transfer, showing a dialog when doing it
const actorItem = originActor.items.get(data.originId); const availableQuantity = Math.max(1, item.system.quantity);
const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({ const actorItem = originActor.items.get(data.originId) ?? item;
if (availableQuantity > 1) {
const quantityTransferred = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
item, item,
targetActor: this.document targetActor: this.document
}); });
return this.#transferItem(actorItem, quantityTransferred);
if (quantityTransfered) {
const existingItem = this.document.items.find(x => itemIsIdentical(x, item));
if (existingItem) {
await existingItem.update({
'system.quantity': existingItem.system.quantity + quantityTransfered
});
} else { } else {
const createData = item.toObject(); return this.#transferItem(actorItem, availableQuantity);
await this.document.createEmbeddedDocuments('Item', [
{
...createData,
system: {
...createData.system,
quantity: quantityTransfered
} }
} }
]);
} }
if (quantityTransfered === actorItem.system.quantity) { /**
await originActor.deleteEmbeddedDocuments('Item', [data.originId]); * Helper to perform the actual transfer of an item to this actor, including stack/unstack logic based on target quantifiability.
* Make sure item is the actor item before calling this method or there will be issues
*/
async #transferItem(item, quantity) {
const originActor = item.actor;
const targetActor = this.document;
const allowStacking = targetActor.system.metadata.quantifiable?.includes(item.type);
const batch = [];
// First add/update the item to the target actor
const existing = allowStacking ? targetActor.items.find(x => itemIsIdentical(x, item)) : null;
if (existing) {
batch.push({
action: 'update',
documentName: 'Item',
parent: targetActor,
updates: [{ '_id': existing.id, 'system.quantity': existing.system.quantity + quantity }]
});
} else { } else {
await actorItem.update({ const itemsToCreate = [];
'system.quantity': actorItem.system.quantity - quantityTransfered if (allowStacking) {
itemsToCreate.push(foundry.utils.mergeObject(item.toObject(true), { system: { quantity } }));
} else {
const createData = new Array(Math.max(1, quantity))
.fill(0)
.map(() => foundry.utils.mergeObject(item.toObject(), { system: { quantity: 1 } }));
itemsToCreate.push(...createData);
}
batch.push({
action: 'create',
documentName: 'Item',
parent: targetActor,
data: itemsToCreate
}); });
} }
}
// Remove the item from the original actor (by either deleting it, or updating its quantity)
if (quantity >= item.system.quantity) {
batch.push({
action: 'delete',
documentName: 'Item',
parent: originActor,
ids: [item.id]
});
} else { } else {
await this.document.createEmbeddedDocuments('Item', [item.toObject()]); batch.push({
await originActor.deleteEmbeddedDocuments('Item', [data.originId]); action: 'update',
} documentName: 'Item',
parent: originActor,
updates: [{ '_id': item.id, 'system.quantity': item.system.quantity - quantity }]
});
} }
return foundry.documents.modifyBatch(batch);
} }
/** /**

View file

@ -31,11 +31,12 @@ export default class AncestrySheet extends DHHeritageSheet {
if (data.type === 'ActiveEffect') return super._onDrop(event); if (data.type === 'ActiveEffect') return super._onDrop(event);
const target = event.target.closest('fieldset.drop-section'); const target = event.target.closest('fieldset.drop-section');
if (target) {
const typeField = const typeField =
this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature']; this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature'];
if (!typeField) { if (!typeField) {
super._onDrop(event); super._onDrop(event);
} }
} }
} }
}

View file

@ -1,5 +1,6 @@
export const keybindings = { export const keybindings = {
spotlight: 'DHSpotlight' spotlight: 'DHSpotlight',
partySheet: 'DHPartySheet'
}; };
export const menu = { export const menu = {

View file

@ -133,7 +133,7 @@ export default class DhpAdversary extends DhCreature {
} }
isItemValid(source) { isItemValid(source) {
return source.type === 'feature'; return super.isItemValid(source) || source.type === 'feature';
} }
async _preUpdate(changes, options, user) { async _preUpdate(changes, options, user) {

View file

@ -107,7 +107,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
hasResistances: true, hasResistances: true,
hasAttribution: false, hasAttribution: false,
hasLimitedView: true, hasLimitedView: true,
usesSize: false usesSize: false,
hasInventory: false
}; };
} }
@ -168,6 +169,11 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
/* -------------------------------------------- */ /* -------------------------------------------- */
isItemValid(source) {
const inventoryTypes = ['weapon', 'armor', 'consumable', 'loot'];
return this.metadata.hasInventory && inventoryTypes.includes(source.type);
}
/** /**
* Obtain a data object used to evaluate any dice rolls associated with this Item Type * Obtain a data object used to evaluate any dice rolls associated with this Item Type
* @param {object} [options] - Options which modify the getRollData method. * @param {object} [options] - Options which modify the getRollData method.

View file

@ -18,7 +18,9 @@ export default class DhCharacter extends DhCreature {
label: 'TYPES.Actor.character', label: 'TYPES.Actor.character',
type: 'character', type: 'character',
settingSheet: DHCharacterSettings, settingSheet: DHCharacterSettings,
isNPC: false isNPC: false,
hasInventory: true,
quantifiable: ["loot", "consumable"]
}); });
} }
@ -284,7 +286,23 @@ export default class DhCharacter extends DhCreature {
guaranteedCritical: new fields.BooleanField({ guaranteedCritical: new fields.BooleanField({
label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label', label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label',
hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint' hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint'
}) }),
defaultAdvantageDice: new fields.NumberField({
nullable: true,
required: true,
integer: true,
choices: CONFIG.DH.GENERAL.dieFaces,
initial: null,
label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice'
}),
defaultDisadvantageDice: new fields.NumberField({
nullable: true,
required: true,
integer: true,
choices: CONFIG.DH.GENERAL.dieFaces,
initial: null,
label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice'
}),
}) })
}) })
}; };
@ -429,6 +447,11 @@ export default class DhCharacter extends DhCreature {
return attack; return attack;
} }
/* All items are valid on characters */
isItemValid() {
return true;
}
/** @inheritDoc */ /** @inheritDoc */
isItemAvailable(item) { isItemAvailable(item) {
if (!super.isItemAvailable(this)) return false; if (!super.isItemAvailable(this)) return false;

View file

@ -61,6 +61,24 @@ export default class DhCompanion extends DhCreature {
initial: false, initial: false,
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.vulnerable' label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.vulnerable'
}) })
}),
roll: new fields.SchemaField({
defaultAdvantageDice: new fields.NumberField({
nullable: true,
required: true,
integer: true,
choices: CONFIG.DH.GENERAL.dieFaces,
initial: null,
label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice'
}),
defaultDisadvantageDice: new fields.NumberField({
nullable: true,
required: true,
integer: true,
choices: CONFIG.DH.GENERAL.dieFaces,
initial: null,
label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice'
}),
}) })
}), }),
attack: new ActionField({ attack: new ActionField({
@ -118,10 +136,6 @@ export default class DhCompanion extends DhCreature {
return this.levelupChoicesLeft > 0; return this.levelupChoicesLeft > 0;
} }
isItemValid() {
return false;
}
prepareBaseData() { prepareBaseData() {
super.prepareBaseData(); super.prepareBaseData();
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0; this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;

View file

@ -56,7 +56,7 @@ export default class DhEnvironment extends BaseDataActor {
} }
isItemValid(source) { isItemValid(source) {
return source.type === 'feature'; return super.isItemValid(source) || source.type === 'feature';
} }
_onUpdate(changes, options, userId) { _onUpdate(changes, options, userId) {

View file

@ -5,6 +5,14 @@ import GroupRollData from '../groupRollData.mjs';
import { GoldField } from '../fields/actorField.mjs'; import { GoldField } from '../fields/actorField.mjs';
export default class DhParty extends BaseDataActor { export default class DhParty extends BaseDataActor {
/** @inheritdoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
hasInventory: true,
quantifiable: ["weapon", "armor", "loot", "consumable"]
});
}
/**@inheritdoc */ /**@inheritdoc */
static defineSchema() { static defineSchema() {
const fields = foundry.data.fields; const fields = foundry.data.fields;
@ -29,10 +37,6 @@ export default class DhParty extends BaseDataActor {
/* -------------------------------------------- */ /* -------------------------------------------- */
isItemValid(source) {
return ['weapon', 'armor', 'consumable', 'loot'].includes(source.type);
}
prepareBaseData() { prepareBaseData() {
super.prepareBaseData(); super.prepareBaseData();

View file

@ -4,7 +4,6 @@
* @property {string} label - A localizable label used on application. * @property {string} label - A localizable label used on application.
* @property {string} type - The system type that this data model represents. * @property {string} type - The system type that this data model represents.
* @property {boolean} hasDescription - Indicates whether items of this type have description field * @property {boolean} hasDescription - Indicates whether items of this type have description field
* @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
*/ */
@ -24,7 +23,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
type: 'base', type: 'base',
hasDescription: false, hasDescription: false,
hasResource: false, hasResource: false,
isQuantifiable: false,
isInventoryItem: false, isInventoryItem: false,
hasActions: false, hasActions: false,
hasAttribution: true hasAttribution: true
@ -83,7 +81,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
); );
} }
if (this.metadata.isQuantifiable) if (this.metadata.isInventoryItem)
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true }); schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
if (this.metadata.hasActions) schema.actions = new ActionsField(); if (this.metadata.hasActions) schema.actions = new ActionsField();

View file

@ -7,7 +7,6 @@ export default class DHConsumable extends BaseDataItem {
label: 'TYPES.Item.consumable', label: 'TYPES.Item.consumable',
type: 'consumable', type: 'consumable',
hasDescription: true, hasDescription: true,
isQuantifiable: true,
isInventoryItem: true, isInventoryItem: true,
hasActions: true hasActions: true
}); });

View file

@ -7,7 +7,6 @@ export default class DHLoot extends BaseDataItem {
label: 'TYPES.Item.loot', label: 'TYPES.Item.loot',
type: 'loot', type: 'loot',
hasDescription: true, hasDescription: true,
isQuantifiable: true,
isInventoryItem: true, isInventoryItem: true,
hasActions: true hasActions: true
}); });

View file

@ -3,7 +3,6 @@ import D20Roll from './d20Roll.mjs';
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
export default class DualityRoll extends D20Roll { export default class DualityRoll extends D20Roll {
_advantageFaces = 6;
_advantageNumber = 1; _advantageNumber = 1;
_rallyIndex; _rallyIndex;
@ -11,6 +10,9 @@ export default class DualityRoll extends D20Roll {
super(formula, data, options); super(formula, data, options);
this.rallyChoices = this.setRallyChoices(); this.rallyChoices = this.setRallyChoices();
this.guaranteedCritical = options.guaranteedCritical; this.guaranteedCritical = options.guaranteedCritical;
const advantageFaces = data.rules?.roll?.defaultAdvantageDice ? Number.parseInt(data.rules.roll.defaultAdvantageDice) : 6
this.advantageFaces = Number.isNaN(advantageFaces) ? 6 : advantageFaces;
} }
static messageType = 'dualityRoll'; static messageType = 'dualityRoll';
@ -51,14 +53,6 @@ export default class DualityRoll extends D20Roll {
return this.dice[2] instanceof game.system.api.dice.diceTypes.DisadvantageDie ? this.dice[2] : null; return this.dice[2] instanceof game.system.api.dice.diceTypes.DisadvantageDie ? this.dice[2] : null;
} }
get advantageFaces() {
return this._advantageFaces;
}
set advantageFaces(faces) {
this._advantageFaces = this.getFaces(faces);
}
get advantageNumber() { get advantageNumber() {
return this._advantageNumber; return this._advantageNumber;
} }

View file

@ -113,7 +113,7 @@ export default class DhpActor extends Actor {
_onUpdate(changes, options, userId) { _onUpdate(changes, options, userId) {
super._onUpdate(changes, options, userId); super._onUpdate(changes, options, userId);
for (const party of this.parties) { for (const party of this.parties) {
party.render(); party.render({ parts: ['partyMembers'] });
} }
} }
@ -132,7 +132,7 @@ export default class DhpActor extends Actor {
_onDelete(options, userId) { _onDelete(options, userId) {
super._onDelete(options, userId); super._onDelete(options, userId);
for (const party of this.parties) { for (const party of this.parties) {
party.render(); party.render({ parts: ['partyMembers'] });
} }
} }

View file

@ -31,8 +31,13 @@ export default class DHItem extends foundry.documents.Item {
static async createDocuments(sources, operation) { static async createDocuments(sources, operation) {
// Ensure that items being created are valid to the actor its being added to // Ensure that items being created are valid to the actor its being added to
const actor = operation.parent; const actor = operation.parent;
sources = actor?.system?.isItemValid ? sources.filter(s => actor.system.isItemValid(s)) : sources; const filtered = actor ? sources.filter(s => actor.system.isItemValid(s)) : sources;
return super.createDocuments(sources, operation); if (actor && filtered.length === 0 && sources.length > 0) {
const itemType = _loc(`TYPES.Item.${sources[0].type}`);
const actorType = _loc(`TYPES.Actor.${actor.type}`);
ui.notifications.error('DAGGERHEART.ACTORS.Base.CannotAddType', { format: { itemType, actorType } });
}
return super.createDocuments(filtered, operation);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View file

@ -52,6 +52,27 @@ export const registerKeyBindings = () => {
reservedModifiers: [], reservedModifiers: [],
precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL
}); });
game.keybindings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.keybindings.partySheet, {
name: _loc('DAGGERHEART.SETTINGS.Keybindings.partySheet.name'),
hint: _loc('DAGGERHEART.SETTINGS.Keybindings.partySheet.hint'),
editable: [{ key: "KeyP" }],
onDown: () => {
const controlled = canvas.ready ? canvas.tokens.controlled : [];
const selectedParty = controlled.find((c) => c.actor?.type === 'party')?.actor;
const party = selectedParty ?? game.actors.party;
if (!party) return;
const sheet = party.sheet;
if (!sheet.rendered) {
sheet.render(true);
} else if (sheet.minimized) {
sheet.maximize();
} else {
sheet.close();
}
}
});
}; };
const registerMenuSettings = () => { const registerMenuSettings = () => {

View file

@ -157,8 +157,8 @@
<option value="{{add this 1}}" {{#if (eq @root.roll.advantageNumber (add this 1))}} selected{{/if}}>{{add this 1}}</option> <option value="{{add this 1}}" {{#if (eq @root.roll.advantageNumber (add this 1))}} selected{{/if}}>{{add this 1}}</option>
{{/times}} {{/times}}
</select> </select>
<select name="roll.dice.advantageFaces"{{#unless advantage}} disabled{{/unless}}> <select name="roll.dice.advantageFaces"{{#unless advantage}} disabled{{/unless}} data-dtype="Number">
{{selectOptions diceOptions selected=(concat 'd' @root.roll.advantageFaces)}} {{selectOptions dieFaces selected=@root.roll.advantageFaces}}
</select> </select>
</div> </div>
{{#if abilities}} {{#if abilities}}

View file

@ -14,7 +14,7 @@
<div class="form-group"> <div class="form-group">
<label>{{localize "DAGGERHEART.GENERAL.quantity"}}</label> <label>{{localize "DAGGERHEART.GENERAL.quantity"}}</label>
<div class="form-fields"> <div class="form-fields">
<range-picker step="1" min="1" max="{{max}}" name="quantity" value="{{max}}"></range-picker> <range-picker step="1" min="1" max="{{max}}" name="quantity" value="{{initial}}"></range-picker>
</div> </div>
</div> </div>
</div> </div>

View file

@ -39,6 +39,7 @@
collection=@root.inventory.consumables collection=@root.inventory.consumables
isGlassy=true isGlassy=true
canCreate=true canCreate=true
isQuantifiable=true
}} }}
{{> 'daggerheart.inventory-items' {{> 'daggerheart.inventory-items'
title='TYPES.Item.loot' title='TYPES.Item.loot'
@ -47,6 +48,7 @@
isGlassy=true isGlassy=true
canCreate=true canCreate=true
showActions=true showActions=true
isQuantifiable=true
}} }}
</div> </div>
</section> </section>

View file

@ -29,6 +29,7 @@
canCreate=true canCreate=true
hideResources=true hideResources=true
hideContextMenu=true hideContextMenu=true
isQuantifiable=true
}} }}
{{> 'daggerheart.inventory-items' {{> 'daggerheart.inventory-items'
title='TYPES.Item.armor' title='TYPES.Item.armor'
@ -39,6 +40,7 @@
canCreate=true canCreate=true
hideResources=true hideResources=true
hideContextMenu=true hideContextMenu=true
isQuantifiable=true
}} }}
{{> 'daggerheart.inventory-items' {{> 'daggerheart.inventory-items'
title='TYPES.Item.consumable' title='TYPES.Item.consumable'
@ -48,6 +50,7 @@
isGlassy=true isGlassy=true
canCreate=true canCreate=true
hideContextMenu=true hideContextMenu=true
isQuantifiable=true
}} }}
{{> 'daggerheart.inventory-items' {{> 'daggerheart.inventory-items'
title='TYPES.Item.loot' title='TYPES.Item.loot'
@ -57,6 +60,7 @@
isGlassy=true isGlassy=true
canCreate=true canCreate=true
hideContextMenu=true hideContextMenu=true
isQuantifiable=true
}} }}
</div> </div>
</section> </section>

View file

@ -66,6 +66,7 @@ Parameters:
showLabels=../showLabels showLabels=../showLabels
isAction=../isAction isAction=../isAction
hideResources=../hideResources hideResources=../hideResources
isQuantifiable=../isQuantifiable
showActions=../showActions showActions=../showActions
}} }}

View file

@ -63,7 +63,7 @@ Parameters:
{{#if (and (not hideResources) (not (eq item.system.resource.type 'diceValue')))}} {{#if (and (not hideResources) (not (eq item.system.resource.type 'diceValue')))}}
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
{{/if}} {{/if}}
{{#if (and (not hideResources) (gte item.system.quantity 0))}} {{#if (or isQuantifiable (or (eq item.system.quantity 0) (gt item.system.quantity 1)))}}
<div class="item-resource"> <div class="item-resource">
<input type="number" id="{{item.uuid}}-quantity" class="inventory-item-quantity" value="{{item.system.quantity}}" min="0" /> <input type="number" id="{{item.uuid}}-quantity" class="inventory-item-quantity" value="{{item.system.quantity}}" min="0" />
</div> </div>