Implements inventory system, wip
This commit is contained in:
@@ -13,4 +13,7 @@ export { default as PrismRPGMiracleSheet } from "./sheets/miracle-sheet.mjs"
|
||||
export { default as PrismRPGRaceSheet } from "./sheets/race-sheet.mjs"
|
||||
export { default as PrismRPGClassSheet } from "./sheets/class-sheet.mjs"
|
||||
export { default as PrismRPGCharacterPathSheet } from "./sheets/character-path-sheet.mjs"
|
||||
export { default as PrismRPGContainerSheet } from "./sheets/container-sheet.mjs"
|
||||
export { default as PrismRPGConsumableSheet } from "./sheets/consumable-sheet.mjs"
|
||||
export { default as PrismRPGLootSheet } from "./sheets/loot-sheet.mjs"
|
||||
export { WeaponTypesConfig } from "./weapon-types-config.mjs"
|
||||
|
||||
@@ -29,6 +29,9 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
|
||||
hpTempPlus: PrismRPGCharacterSheet.#onHpTempPlus,
|
||||
hpTempMinus: PrismRPGCharacterSheet.#onHpTempMinus,
|
||||
postItemToChat: PrismRPGCharacterSheet.#onPostItemToChat,
|
||||
useConsumable: PrismRPGCharacterSheet.#onUseConsumable,
|
||||
toggleContainerEquipped: PrismRPGCharacterSheet.#onToggleContainerEquipped,
|
||||
toggleEquipped: PrismRPGCharacterSheet.#onToggleEquipped,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -109,13 +112,39 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
|
||||
classes[1] || null,
|
||||
classes[2] || null
|
||||
]
|
||||
// Burden computed values
|
||||
const bSubAttr = doc.system.burden.subAttribute
|
||||
const bSubVal = doc.system.subAttributes[bSubAttr]?.value ?? 0
|
||||
const baseBurden = doc.itemTypes.race?.[0]?.system.baseBurden ?? 0
|
||||
context.burdenMax = Math.max(0, baseBurden + bSubVal + doc.system.burden.other)
|
||||
// Equipped burden: only items with equipped=true count toward burden
|
||||
const equippableTypes = [
|
||||
...doc.itemTypes.weapon,
|
||||
...doc.itemTypes.armor,
|
||||
...doc.itemTypes.shield,
|
||||
...doc.itemTypes.equipment,
|
||||
...doc.itemTypes.container,
|
||||
]
|
||||
const burdenEquipped = equippableTypes
|
||||
.filter(i => i.system.equipped)
|
||||
.reduce((sum, i) => sum + (i.system.encLoad ?? 0), 0)
|
||||
context.burdenUsed = burdenEquipped
|
||||
// Excess equipped burden reduces Movement Rating
|
||||
const excessBurden = Math.max(0, burdenEquipped - context.burdenMax)
|
||||
// Movement Rating computed value (excess burden adds to reduction)
|
||||
const mrSubAttr = doc.system.movementRating.subAttribute
|
||||
const mrSubVal = doc.system.subAttributes[mrSubAttr]?.value ?? 0
|
||||
context.movementRatingValue = Math.max(0,
|
||||
3 + mrSubVal + doc.system.movementRating.other - doc.system.movementRating.reduction - excessBurden
|
||||
)
|
||||
context.excessBurden = excessBurden
|
||||
break
|
||||
case "skills":
|
||||
context.tab = context.tabs.skills
|
||||
context.skills = doc.itemTypes.skill
|
||||
context.racialAbilities = doc.itemTypes["racial-ability"]
|
||||
context.abilities = doc.itemTypes["ability"]
|
||||
context.vulnerabilities = doc.itemTypes.vulnerability
|
||||
context.abilities = doc.itemTypes.ability
|
||||
context.vulnerabilities = doc.itemTypes.vulnerability ?? []
|
||||
break
|
||||
case "subattributes":
|
||||
context.tab = context.tabs.subattributes
|
||||
@@ -133,7 +162,25 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
|
||||
break
|
||||
case "equipment":
|
||||
context.tab = context.tabs.equipment
|
||||
context.equipments = doc.itemTypes.equipment
|
||||
context.weapons = doc.itemTypes.weapon
|
||||
context.armors = [...doc.itemTypes.armor, ...doc.itemTypes.shield]
|
||||
context.consumables = doc.itemTypes.consumable
|
||||
context.kits = doc.itemTypes.equipment.filter(i => i.system.isKit)
|
||||
context.equipmentItems = doc.itemTypes.equipment.filter(i => !i.system.isKit)
|
||||
context.loots = doc.itemTypes.loot
|
||||
context.containers = doc.itemTypes.container
|
||||
context.packBurdenMax = doc.itemTypes.container
|
||||
.filter(c => c.system.equipped)
|
||||
.reduce((sum, c) => sum + (c.system.packBurden ?? 0), 0)
|
||||
context.packBurdenUsed = [
|
||||
...doc.itemTypes.equipment,
|
||||
...doc.itemTypes.consumable,
|
||||
...doc.itemTypes.loot,
|
||||
...doc.itemTypes.container,
|
||||
...doc.itemTypes.weapon,
|
||||
...doc.itemTypes.armor,
|
||||
...doc.itemTypes.shield,
|
||||
].reduce((sum, i) => sum + (i.system.encLoad ?? 0), 0)
|
||||
break
|
||||
case "biography":
|
||||
context.tab = context.tabs.biography
|
||||
@@ -237,6 +284,51 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
|
||||
}
|
||||
|
||||
static async #onCreateEquipment(event, target) {
|
||||
const itemType = target.dataset.itemType ?? "equipment"
|
||||
const isKit = target.dataset.itemKit === "true"
|
||||
const typeLabel = game.i18n.localize(`TYPES.Item.${itemType}`) || itemType
|
||||
const itemData = {
|
||||
name: game.i18n.format("DOCUMENT.New", { type: typeLabel }),
|
||||
type: itemType,
|
||||
}
|
||||
if (isKit) itemData["system.isKit"] = true
|
||||
await this.document.createEmbeddedDocuments("Item", [itemData])
|
||||
}
|
||||
|
||||
static async #onUseConsumable(event, target) {
|
||||
const itemElement = target.closest("[data-item-id]")
|
||||
if (!itemElement) return
|
||||
const item = this.document.items.get(itemElement.dataset.itemId)
|
||||
if (!item) return
|
||||
if (item.system.uses <= 0) return
|
||||
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: { title: game.i18n.localize("PRISMRPG.Dialog.useConsumable") },
|
||||
content: `<p>${game.i18n.format("PRISMRPG.Dialog.useConsumableContent", { name: item.name, uses: item.system.uses })}</p>`,
|
||||
})
|
||||
if (!confirmed) return
|
||||
|
||||
if (item.system.uses <= 1) {
|
||||
await item.delete()
|
||||
} else {
|
||||
await item.update({ "system.uses": item.system.uses - 1 })
|
||||
}
|
||||
}
|
||||
|
||||
static async #onToggleContainerEquipped(event, target) {
|
||||
const itemElement = target.closest("[data-item-id]")
|
||||
if (!itemElement) return
|
||||
const item = this.document.items.get(itemElement.dataset.itemId)
|
||||
if (!item) return
|
||||
await item.update({ "system.equipped": !item.system.equipped })
|
||||
}
|
||||
|
||||
static async #onToggleEquipped(event, target) {
|
||||
const itemElement = target.closest("[data-item-id]")
|
||||
if (!itemElement) return
|
||||
const item = this.document.items.get(itemElement.dataset.itemId)
|
||||
if (!item) return
|
||||
await item.update({ "system.equipped": !item.system.equipped })
|
||||
}
|
||||
|
||||
static async #onPostItemToChat(event, target) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import PrismRPGItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class PrismRPGConsumableSheet extends PrismRPGItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["consumable"],
|
||||
position: { width: 550 },
|
||||
window: { contentClasses: ["consumable-content"] },
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-prism-rpg/templates/consumable.hbs" },
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
|
||||
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
|
||||
effects: { id: "effects", group: "primary", label: "PRISMRPG.Label.effects" },
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.description, { async: true }
|
||||
)
|
||||
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.notes, { async: true }
|
||||
)
|
||||
return context
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import PrismRPGItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class PrismRPGContainerSheet extends PrismRPGItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["container"],
|
||||
position: {
|
||||
width: 600,
|
||||
},
|
||||
window: {
|
||||
contentClasses: ["container-content"],
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-prism-rpg/templates/container.hbs",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "details",
|
||||
}
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
|
||||
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
|
||||
effects: { id: "effects", group: "primary", label: "PRISMRPG.Label.effects" },
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.description, { async: true }
|
||||
)
|
||||
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.notes, { async: true }
|
||||
)
|
||||
return context
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import PrismRPGItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class PrismRPGLootSheet extends PrismRPGItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["loot"],
|
||||
position: { width: 500 },
|
||||
window: { contentClasses: ["loot-content"] },
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: { template: "systems/fvtt-prism-rpg/templates/loot.hbs" },
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "details" }
|
||||
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
|
||||
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
|
||||
effects: { id: "effects", group: "primary", label: "PRISMRPG.Label.effects" },
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
context.tabs = this.#getTabs()
|
||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.description, { async: true }
|
||||
)
|
||||
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.notes, { async: true }
|
||||
)
|
||||
return context
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,7 @@ export { default as PrismRPGAbility } from "./ability.mjs"
|
||||
export { default as PrismRPGEquipment } from "./equipment.mjs"
|
||||
export { default as PrismRPGRace } from "./race.mjs"
|
||||
export { default as PrismRPGClass } from "./class.mjs"
|
||||
export { default as PrismRPGCharacterPath } from "./character-path.mjs"
|
||||
export { default as PrismRPGCharacterPath } from "./character-path.mjs"
|
||||
export { default as PrismRPGContainer } from "./container.mjs"
|
||||
export { default as PrismRPGConsumable } from "./consumable.mjs"
|
||||
export { default as PrismRPGLoot } from "./loot.mjs"
|
||||
@@ -136,6 +136,21 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
|
||||
}, {}),
|
||||
)
|
||||
|
||||
// Sub-attribute choices for movement rating and burden selectors
|
||||
const subAttributeChoices = () =>
|
||||
Object.values(SYSTEM.SUB_ATTRIBUTES).reduce((obj, s) => { obj[s.id] = s.label; return obj }, {})
|
||||
|
||||
schema.movementRating = new fields.SchemaField({
|
||||
subAttribute: new fields.StringField({ required: true, initial: "stamina", choices: subAttributeChoices }),
|
||||
other: new fields.NumberField({ ...requiredInteger, initial: 0 }),
|
||||
reduction: new fields.NumberField({ ...requiredInteger, initial: 0 })
|
||||
})
|
||||
|
||||
schema.burden = new fields.SchemaField({
|
||||
subAttribute: new fields.StringField({ required: true, initial: "vigor", choices: subAttributeChoices }),
|
||||
other: new fields.NumberField({ ...requiredInteger, initial: 0 })
|
||||
})
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export default class PrismRPGConsumable extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields
|
||||
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||
const schema = {}
|
||||
|
||||
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||
schema.usesMax = new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 })
|
||||
schema.uses = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
|
||||
schema.encLoad = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||
schema.notes = new fields.HTMLField({ required: true })
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static LOCALIZATION_PREFIXES = ["PRISMRPG.Consumable"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export default class PrismRPGContainer extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields
|
||||
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||
const schema = {}
|
||||
|
||||
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||
schema.packBurden = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||
schema.encLoad = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||
schema.equipped = new fields.BooleanField({ required: true, initial: false })
|
||||
schema.notes = new fields.HTMLField({ required: true })
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static LOCALIZATION_PREFIXES = ["PRISMRPG.Container"]
|
||||
}
|
||||
@@ -12,6 +12,7 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel {
|
||||
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
|
||||
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
|
||||
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
|
||||
schema.equipped = new fields.BooleanField({ required: true, initial: false })
|
||||
|
||||
// Kit properties
|
||||
schema.isKit = new fields.BooleanField({
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
export default class PrismRPGLoot extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields
|
||||
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||
const schema = {}
|
||||
|
||||
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||
schema.encLoad = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||
schema.cost = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||
schema.notes = new fields.HTMLField({ required: true })
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static LOCALIZATION_PREFIXES = ["PRISMRPG.Loot"]
|
||||
}
|
||||
@@ -46,6 +46,15 @@ export default class PrismRPGRace extends foundry.abstract.TypeDataModel {
|
||||
label: "Language"
|
||||
})
|
||||
|
||||
schema.baseBurden = new fields.NumberField({
|
||||
required: true,
|
||||
nullable: false,
|
||||
integer: true,
|
||||
initial: 0,
|
||||
min: 0,
|
||||
label: "Base Burden"
|
||||
})
|
||||
|
||||
// Racial Passive
|
||||
schema.racialPassive = new fields.StringField({
|
||||
required: true,
|
||||
|
||||
Reference in New Issue
Block a user