Review Inventory (#55)

* Weapons and Armor are now stored like any other item on the PC. Added equip/unequip logic.

* Changed so that equip attempts always go through and the neccessary weapons are unequipped to fascilitate it

* Fixed drag equip and extracted unequipBeforeEquip logic
This commit is contained in:
WBHarry 2025-05-26 15:43:04 +02:00 committed by GitHub
parent d36520438a
commit cf51153432
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 106 additions and 143 deletions

View file

@ -132,7 +132,6 @@ const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/sheets/parts/gold.hbs',
'systems/daggerheart/templates/sheets/parts/health.hbs',
'systems/daggerheart/templates/sheets/parts/hope.hbs',
'systems/daggerheart/templates/sheets/parts/inventory.hbs',
'systems/daggerheart/templates/sheets/parts/weapons.hbs',
'systems/daggerheart/templates/sheets/parts/domainCard.hbs',
'systems/daggerheart/templates/sheets/parts/heritage.hbs',

View file

@ -83,8 +83,6 @@
"Info": {
"ClassCanOnlyHaveTwoDomains": "A class can only have 2 domains!",
"NoTargetsSelected": "No targets are selected.",
"SecondaryEquipWhileTwohanded": "A secondary weapon can't be equipped together with a Two-Handed weapon.",
"TwohandedEquipWhileSecondary": "Can't equip a Two-Handed weapon together with a secondary weapon.",
"SelectClassBeforeSubclass": "Select a Class before selecting a Subclass.",
"SubclassNotOfClass": "This Subclass doesn't belong to your current Class.",
"AttackTargetDoesNotExist": "The target token no longer exists"
@ -843,9 +841,10 @@
"InventoryWeapon": "Inventory Weapon"
},
"InventoryTab": {
"EquipmentTitle": "Equipment",
"ConsumableTitle": "Consumables",
"MiscellaneousTitle": "Miscellaneous",
"WeaponsTitle": "Weapons",
"ArmorsTitle": "Armors",
"QuantityTitle": "Quantity"
},
"Weapons": {

View file

@ -48,8 +48,6 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
useFeature: this.useFeature,
takeShortRest: this.takeShortRest,
takeLongRest: this.takeLongRest,
removeActiveItem: this.removeActiveItem,
removeInventoryWeapon: this.removeInventoryWeapon,
addMiscItem: this.addMiscItem,
deleteItem: this.deleteItem,
addScar: this.addScar,
@ -63,18 +61,12 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
useAbility: this.useAbility,
useAdvancementCard: this.useAdvancementCard,
useAdvancementAbility: this.useAdvancementAbility,
selectFeatureSet: this.selectFeatureSet
selectFeatureSet: this.selectFeatureSet,
toggleEquipItem: this.toggleEquipItem
},
window: {
//frame: boolean;
//positioned: boolean;
//title: string;
//icon: string | false;
//controls: ApplicationHeaderControlsEntry[];
minimizable: false,
resizable: true
//contentTag: string;
//contentClasses: string[];
},
form: {
handler: this.updateForm,
@ -280,6 +272,20 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
},
items: this.document.items.filter(x => x.type === 'miscellaneous')
},
weapons: {
titles: {
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.WeaponsTitle'),
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
},
items: this.document.items.filter(x => x.type === 'weapon')
},
armor: {
titles: {
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ArmorsTitle'),
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
},
items: this.document.items.filter(x => x.type === 'armor')
}
};
@ -690,7 +696,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
(await game.packs.get('daggerheart.playtest-communities'))?.render(true);
}
static async viewObject(button) {
static async viewObject(_, button) {
const object = await fromUuid(button.dataset.value);
if (!object) return;
@ -712,18 +718,6 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
await this.minimize();
}
static async removeActiveItem(_, event) {
event.stopPropagation();
const item = await fromUuid(event.currentTarget.dataset.item);
await item.delete();
}
static async removeInventoryWeapon(_, event) {
event.stopPropagation();
const item = await fromUuid(event.currentTarget.dataset.item);
await item.delete();
}
static async addMiscItem() {
const result = await this.document.createEmbeddedDocuments('Item', [
{
@ -941,6 +935,31 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
this.render();
}
static async toggleEquipItem(_, button) {
const item = this.document.items.get(button.id);
if (item.system.equipped) {
await item.update({ 'system.equipped': false });
return;
}
switch (item.type) {
case 'armor':
const currentArmor = this.document.system.armor;
if (currentArmor) {
await currentArmor.update({ 'system.equipped': false });
}
await item.update({ 'system.equipped': true });
break;
case 'weapon':
await this.document.system.constructor.unequipBeforeEquip.bind(this.document.system)(item);
await item.update({ 'system.equipped': true });
break;
}
this.render();
}
static async close(options) {
this.onVaultTab = false;
super.close(options);
@ -984,14 +1003,17 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
const itemObject = await fromUuid(item.uuid);
switch (target) {
case 'weapon-section':
if (itemObject.system.secondary && this.document.system.activeWeapons.burden === 'twoHanded') {
if (
itemObject.system.secondary &&
this.document.system.equippedWeapons.burden === 'twoHanded'
) {
ui.notifications.info(
game.i18n.localize('DAGGERHEART.Notification.Info.SecondaryEquipWhileTwohanded')
);
return;
} else if (
itemObject.system.burden === 'twoHanded' &&
this.document.system.activeWeapons.secondary
this.document.system.equippedWeapons.secondary
) {
ui.notifications.info(
game.i18n.localize('DAGGERHEART.Notification.Info.TwohandedEquipWhileSecondary')
@ -1160,41 +1182,9 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
if (!element) return;
if (element.classList.contains('weapon-section')) {
if (item.system.secondary && this.document.system.activeWeapons.burden === 'twoHanded') {
ui.notifications.info(
game.i18n.localize('DAGGERHEART.Notification.Info.SecondaryEquipWhileTwohanded')
);
return;
} else if (item.system.burden === 'twoHanded' && this.document.system.activeWeapons.secondary) {
ui.notifications.info(
game.i18n.localize('DAGGERHEART.Notification.Info.TwohandedEquipWhileSecondary')
);
return;
await this.document.system.constructor.unequipBeforeEquip.bind(this.document.system)(itemData);
itemData.system.equipped = true;
}
const existing =
this.document.system.activeWeapons.primary && !item.system.secondary
? await fromUuid(this.document.system.activeWeapons.primary.uuid)
: this.document.system.activeWeapons.secondary && item.system.secondary
? await fromUuid(this.document.system.activeWeapons.secondary.uuid)
: null;
await existing?.delete();
itemData.system.active = true;
} else if (element.classList.contains('inventory-weapon-section-first')) {
const existing = this.document.system.inventoryWeapons.first
? await fromUuid(this.document.system.inventoryWeapons.first.uuid)
: null;
await existing?.delete();
itemData.system.inventoryWeapon = 1;
} else if (element.classList.contains('inventory-weapon-section-second')) {
const existing = this.document.system.inventoryWeapons.second
? await fromUuid(this.document.system.inventoryWeapons.second.uuid)
: null;
await existing?.delete();
itemData.system.inventoryWeapon = 2;
} else return [];
}
if (item.type === 'armor') {
@ -1204,8 +1194,9 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
const existing = this.document.system.armor
? await fromUuid(this.document.system.armor.uuid)
: null;
await existing?.delete();
} else return;
await existing?.update({ 'system.equipped': false });
itemData.system.equipped = true;
}
}
const createdItem = await this._onDropItemCreate(itemData);

View file

@ -27,8 +27,14 @@ export const range = {
};
export const burden = {
oneHanded: 'DAGGERHEART.Burden.OneHanded',
twoHanded: 'DAGGERHEART.Burden.TwoHanded'
oneHanded: {
value: 'oneHanded',
label: 'DAGGERHEART.Burden.OneHanded'
},
twoHanded: {
value: 'twoHanded',
label: 'DAGGERHEART.Burden.TwoHanded'
}
};
export const damageTypes = {

View file

@ -2,6 +2,7 @@ export default class DhpArmor extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
equipped: new fields.BooleanField({ initial: false }),
baseScore: new fields.NumberField({ initial: 1, integer: true }),
feature: new fields.StringField({
choices: SYSTEM.ITEM.armorFeatures,
@ -16,6 +17,7 @@ export default class DhpArmor extends foundry.abstract.TypeDataModel {
major: new fields.NumberField({ initial: 0, integer: true }),
severe: new fields.NumberField({ initial: 0, integer: true })
}),
quantity: new fields.NumberField({ initial: 1, integer: true }),
description: new fields.HTMLField({})
};
}

View file

@ -242,15 +242,15 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
}
get armor() {
return this.parent.items.find(x => x.type === 'armor');
return this.parent.items.find(x => x.type === 'armor' && x.system.equipped);
}
get activeWeapons() {
get equippedWeapons() {
const primaryWeapon = this.parent.items.find(
x => x.type === 'weapon' && x.system.active && !x.system.secondary
x => x.type === 'weapon' && x.system.equipped && !x.system.secondary
);
const secondaryWeapon = this.parent.items.find(
x => x.type === 'weapon' && x.system.active && x.system.secondary
x => x.type === 'weapon' && x.system.equipped && x.system.secondary
);
return {
primary: this.#weaponData(primaryWeapon),
@ -259,6 +259,28 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
};
}
static async unequipBeforeEquip(itemToEquip) {
const equippedWeapons = this.equippedWeapons;
if (itemToEquip.system.secondary) {
if (equippedWeapons.primary && equippedWeapons.primary.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
await this.parent.items.get(equippedWeapons.primary.id).update({ 'system.equipped': false });
}
if (equippedWeapons.secondary) {
await this.parent.items.get(equippedWeapons.secondary.id).update({ 'system.equipped': false });
}
} else {
if (equippedWeapons.secondary && itemToEquip.system.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
await this.parent.items.get(equippedWeapons.secondary.id).update({ 'system.equipped': false });
}
if (equippedWeapons.primary) {
await this.parent.items.get(equippedWeapons.primary.id).update({ 'system.equipped': false });
}
}
}
get inventoryWeapons() {
const inventoryWeaponFirst = this.parent.items.find(x => x.type === 'weapon' && x.system.inventoryWeapon === 1);
const inventoryWeaponSecond = this.parent.items.find(
@ -325,16 +347,19 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
);
}
//Should not be done in data?
#weaponData(weapon) {
return weapon
? {
id: weapon.id,
name: weapon.name,
trait: CONFIG.daggerheart.ACTOR.abilities[weapon.system.trait].name, //Should not be done in data?
trait: game.i18n.localize(CONFIG.daggerheart.ACTOR.abilities[weapon.system.trait].label),
range: CONFIG.daggerheart.GENERAL.range[weapon.system.range],
damage: {
value: weapon.system.damage.value,
type: CONFIG.daggerheart.GENERAL.damageTypes[weapon.system.damage.type]
},
burden: weapon.system.burden,
feature: CONFIG.daggerheart.ITEM.weaponFeatures[weapon.system.feature],
img: weapon.img,
uuid: weapon.uuid

View file

@ -2,7 +2,7 @@ export default class DhpWeapon extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
active: new fields.BooleanField({ initial: false }),
equipped: new fields.BooleanField({ initial: false }),
inventoryWeapon: new fields.NumberField({ initial: null, nullable: true, integer: true }),
secondary: new fields.BooleanField({ initial: false }),
trait: new fields.StringField({ choices: SYSTEM.ACTOR.abilities, integer: false }),

View file

@ -1250,7 +1250,7 @@
filter: drop-shadow(0 0 3px gold);
}
.daggerheart.sheet.pc div[data-application-part] .sheet-body .inventory-container .inventory-item-list .inventory-item .inventory-item-quantity {
width: 48px;
width: 60px;
display: flex;
align-items: center;
}

View file

@ -1455,7 +1455,7 @@
}
.inventory-item-quantity {
width: 48px;
width: 60px;
display: flex;
align-items: center;

View file

@ -1,12 +1,6 @@
<fieldset class="left-main-container armor-section active-item-section item-section">
<legend class="legend armor-container">
<span>{{localize "DAGGERHEART.Sheets.PC.Armor.Title"}}</span>
{{#if armor}}
<div data-action="viewObject" data-value="{{armor.uuid}}" class="active-item-label-chip">
<img src="{{armor.img}}" />
<button data-action="removeActiveItem" data-item="{{armor.uuid}}"><i class="fa-solid fa-x"></i></button>
</div>
{{/if}}
</legend>
<div class="active-item-container">

View file

@ -1,47 +0,0 @@
<fieldset class="left-main-container" style="flex: 1; display: flex; flex-direction: column;">
<legend class="legend inventory-legend">
{{localize "DAGGERHEART.Sheets.PC.Inventory.Title"}}
</legend>
<div class="inventory-items">
<div class="inventory-weapon-section-first item-section">
<h2 class="armor-container">
{{localize "DAGGERHEART.Sheets.PC.Inventory.InventoryWeapon"}}
{{#if weapons.first}}
<div data-action="viewObject" data-value="{{weapons.first.uuid}}" class="active-item-label-chip">
<img src="{{weapons.first.img}}" />
<button data-action="removeInventoryWeapon" data-item="{{weapons.first.uuid}}"><i class="fa-solid fa-x"></i></button>
</div>
{{/if}}
</h2>
<div class="active-item-container">
<div class="flexrow">
<input value="{{weapons.first.name}}" type="text" />
<input value="{{localize weapons.first.trait}}" type="text" />
<input value="{{localize weapons.first.range.name}}" type="text" />
<input value="{{weapons.first.damage.value}} {{#if weapons.first}}({{localize weapons.first.damage.type.abbreviation}}){{/if}}" type="text" />
</div>
<input value="{{localize weapons.first.feature.name}} {{#if weapons.first.feature}}({{localize weapons.first.feature.description}}){{/if}}" type="text" />
</div>
</div>
<div class="inventory-weapon-section-second item-section">
<h2 class="armor-container">
{{localize "DAGGERHEART.Sheets.PC.Inventory.InventoryWeapon"}}
{{#if weapons.second}}
<div data-action="viewObject" data-value="{{weapons.second.uuid}}" class="active-item-label-chip">
<img src="{{weapons.second.img}}" />
<button data-action="removeInventoryWeapon" data-item="{{weapons.second.uuid}}"><i class="fa-solid fa-x"></i></button>
</div>
{{/if}}
</h2>
<div class="active-item-container">
<div class="flexrow">
<input value="{{weapons.second.name}}" type="text" />
<input value="{{localize weapons.second.trait}}" type="text" />
<input value="{{localize weapons.second.range.name}}" type="text" />
<input value="{{weapons.second.damage.value}} {{#if weapons.second}}({{localize weapons.second.damage.type.abbreviation}}){{/if}}" type="text" />
</div>
<input value="{{localize weapons.second.feature.name}} {{#if weapons.second.feature}}({{localize weapons.second.feature.description}}){{/if}}" type="text" />
</div>
</div>
</div>
</fieldset>

View file

@ -20,37 +20,29 @@
<h2 class="weapons-label-row">
{{localize "DAGGERHEART.Sheets.PC.Weapons.PrimaryTitle"}}
{{#if weapons.primary}}
<div data-action="viewObject" data-value="{{weapons.primary.uuid}}" class="active-item-label-chip">
<img src="{{weapons.primary.img}}" />
<button data-action="removeActiveItem" data-item="{{weapons.primary.uuid}}"><i class="fa-solid fa-x"></i></button>
</div>
<img class="damage-roll" data-action="attackRoll" data-weapon="{{weapons.primary.uuid}}" src="icons/svg/d12-grey.svg" />
{{/if}}
</h2>
<div class="flexrow">
<input value="{{weapons.primary.name}}" type="text" />
<input value="{{localize weapons.primary.trait}}" type="text" />
<input value="{{localize weapons.primary.range.name}}" type="text" />
<input value="{{localize weapons.primary.range.label}}" type="text" />
<input value="{{weapons.primary.damage.value}} {{#if weapons.primary}}({{localize weapons.primary.damage.type.abbreviation}}){{/if}}" type="text" />
</div>
<input value="{{localize weapons.primary.feature.name}} {{#if weapons.primary.feature}}({{localize weapons.primary.feature.description}}){{/if}}" type="text" />
<input value="{{localize weapons.primary.feature.label}} {{#if weapons.primary.feature}}({{localize weapons.primary.feature.description}}){{/if}}" type="text" />
</div>
<div class="active-item-container">
<h2 class="weapons-label-row">
{{localize "DAGGERHEART.Sheets.PC.Weapons.SecondaryTitle"}}
{{#if weapons.secondary}}
<div data-action="viewObject" data-value="{{weapons.secondary.uuid}}" class="active-item-label-chip">
<img src="{{weapons.secondary.img}}" />
<button data-action="removeActiveItem" data-item="{{weapons.secondary.uuid}}"><i class="fa-solid fa-x"></i></button>
</div>
<img class="damage-roll" data-action="damageRoll" data-value="{{weapons.secondary.damage.value}}" src="icons/svg/d12-grey.svg" />
{{/if}}
</h2>
<div class="flexrow">
<input value="{{weapons.secondary.name}}" type="text" />
<input value="{{localize weapons.secondary.trait}}" type="text" />
<input value="{{localize weapons.secondary.range.name}}" type="text" />
<input value="{{weapons.secondary.damage.value}} {{#if weapons.secondary}}({{localize weapons.secondary.damage.type.abbreviation}}){{/if}}" type="text" />
<input value="{{localize weapons.secondary.range.label}}" type="text" />
<input value="{{weapons.secondary.damage.label}} {{#if weapons.secondary}}({{localize weapons.secondary.damage.type.abbreviation}}){{/if}}" type="text" />
</div>
<input value="{{localize weapons.secondary.feature.name}} {{#if weapons.secondary.feature}}({{localize weapons.secondary.feature.description}}){{/if}}" style="text-overflow: ellipsis;" type="text" />
</div>

View file

@ -88,9 +88,8 @@
</div>
<div class="body-section flex3">
{{> "systems/daggerheart/templates/sheets/parts/attributes.hbs" }}
{{> "systems/daggerheart/templates/sheets/parts/weapons.hbs" weapons=document.system.activeWeapons proficiency=document.system.proficiency.value }}
{{> "systems/daggerheart/templates/sheets/parts/weapons.hbs" weapons=document.system.equippedWeapons proficiency=document.system.proficiency.value }}
{{> "systems/daggerheart/templates/sheets/parts/armor.hbs" armor=document.system.armor }}
{{> "systems/daggerheart/templates/sheets/parts/inventory.hbs" weapons=document.system.inventoryWeapons }}
</div>
</div>
</div>

View file

@ -18,6 +18,9 @@
{{item.name}}
</div>
</div>
<div class="flexrow">
<button data-action="toggleEquipItem" id="{{item.id}}">{{localize "Equip"}}</button>
</div>
<div class="inventory-item-quantity spaced">
<i data-action="itemQuantityDecrease" class="fa-solid fa-chevron-left icon-button {{#if (lte item.system.quantity 1)}}disabled{{/if}}"></i>
<input type="text" data-item="system.quantity" value="{{item.system.quantity}}" data-dtype="Number" />