vagabond-rpg-foundryvtt/module/data/item/equipment.mjs
Cal Corum c06192f90f Add minor data model improvements from rulebook audit (16 items)
CharacterData additions:
- Senses (darksight, blindsight, tremorsense, elvenEyes, witchsight, sixthSense)
- Languages array (default: ["Common"])
- Rest tracking (lastRest, breathersTaken, restBonuses)
- Travel tracking (milesThisDay, pace, canForage, shiftsElapsed)
- Crafting projects (activeProjects with materials, shifts, bonuses)
- Combat tracking (isFlanked, flankingAllies, ignoresFlankingPenalty,
  currentZone, isDualWielding, mainHandWeapon, offHandWeapon)
- Casting tracking (equippedTrinket, canCastThroughWeapon/Instrument)
- Downtime activities with type and shifts
- Quest tracking (active, completed, lastQuestCompleted)
- Death state (isDead, deathCause, canBeRevived, luminaryRevivifyUsed)
- Summoned creatures (active array with type, HD, HP, command method)
- Size mechanical effects (unitsOccupied, allowsMovementThrough)
- Being type choices (humanlike, fae, cryptid, etc.)
- preferredZone for class-based positioning

NPCData additions:
- Morale status tracking (checkedThisCombat, lastTrigger, lastResult, broken)
- Senses (darksight, blindsight, tremorsense)

ArmorData additions:
- Medium armor type, hindersDodge, preventsRage flags

WeaponData additions:
- Damage type choices (blunt/piercing/slashing + elemental)
- equippedHand for dual-wielding (main/off/both)

EquipmentData additions:
- isTrinket and canCastThrough for spell component tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 16:02:23 -06:00

198 lines
5.3 KiB
JavaScript

/**
* Equipment Item Data Model
*
* Defines the data schema for generic equipment/gear in Vagabond RPG.
* This covers adventuring gear, tools, consumables, and miscellaneous items.
*
* Examples: Rope, Torches, Potions, Lockpicks, Rations
*
* @extends VagabondItemBase
*/
import VagabondItemBase from "./base-item.mjs";
export default class EquipmentData extends VagabondItemBase {
/**
* Define the schema for equipment items.
*
* @returns {Object} The schema definition
*/
static defineSchema() {
const fields = foundry.data.fields;
const baseSchema = super.defineSchema();
return {
...baseSchema,
// Item quantity
quantity: new fields.NumberField({
required: true,
integer: true,
initial: 1,
min: 0,
}),
// Inventory slot cost (per item or per stack)
slots: new fields.NumberField({
integer: true,
initial: 1,
min: 0,
}),
// Whether slots are per-item or for the whole stack
slotsPerItem: new fields.BooleanField({ initial: false }),
// Monetary value per item (in copper)
value: new fields.NumberField({
integer: true,
initial: 0,
min: 0,
}),
// Is this a consumable item?
consumable: new fields.BooleanField({ initial: false }),
// For consumables: uses remaining
uses: new fields.SchemaField({
value: new fields.NumberField({ integer: true, initial: 0 }),
max: new fields.NumberField({ integer: true, initial: 0 }),
autoDestroy: new fields.BooleanField({ initial: true }),
}),
// Equipment category for organization
category: new fields.StringField({
initial: "gear",
choices: ["gear", "tool", "consumable", "container", "treasure", "misc"],
}),
// Is this item currently equipped/active?
equipped: new fields.BooleanField({ initial: false }),
// Container capacity (if this is a container)
containerCapacity: new fields.NumberField({
integer: true,
initial: 0,
min: 0,
}),
// Tags for filtering/searching
tags: new fields.ArrayField(new fields.StringField(), { initial: [] }),
// Is this item a valid spell casting trinket?
isTrinket: new fields.BooleanField({ initial: false }),
// Can spells be cast through this item? (for Familiars, Gish weapons)
canCastThrough: new fields.BooleanField({ initial: false }),
// Relic System - Powerful magic items with unique abilities
relic: new fields.SchemaField({
// Is this item a relic?
isRelic: new fields.BooleanField({ initial: false }),
// Relic tier (determines power level)
tier: new fields.NumberField({
integer: true,
initial: 1,
min: 1,
max: 5,
}),
// Unique ability name
abilityName: new fields.StringField({ required: false, blank: true }),
// Unique ability description
abilityDescription: new fields.HTMLField({ required: false, blank: true }),
// Activation cost (mana, luck, etc.)
activationCost: new fields.StringField({ required: false, blank: true }),
// Uses per day (0 = unlimited or passive)
usesPerDay: new fields.NumberField({ integer: true, initial: 0, min: 0 }),
// Current uses remaining
usesRemaining: new fields.NumberField({ integer: true, initial: 0, min: 0 }),
// Attunement required?
requiresAttunement: new fields.BooleanField({ initial: false }),
// Currently attuned?
attuned: new fields.BooleanField({ initial: false }),
// Lore/history of the relic
lore: new fields.HTMLField({ required: false, blank: true }),
}),
};
}
/**
* Calculate the total slot cost for this item stack.
*
* @returns {number} Total slots used
*/
getTotalSlots() {
if (this.slotsPerItem) {
return this.slots * this.quantity;
}
return this.slots;
}
/**
* Calculate the total value of this item stack.
*
* @returns {number} Total value in copper
*/
getTotalValue() {
return this.value * this.quantity;
}
/**
* Consume one use of a consumable item.
*
* @returns {Object} Result with new values
*/
consume() {
if (!this.consumable) {
return { consumed: false, reason: "Not consumable" };
}
if (this.uses.max > 0) {
// Uses-based consumable
if (this.uses.value <= 0) {
return { consumed: false, reason: "No uses remaining" };
}
return {
consumed: true,
newUses: this.uses.value - 1,
depleted: this.uses.value - 1 <= 0,
};
}
// Quantity-based consumable
if (this.quantity <= 0) {
return { consumed: false, reason: "No items remaining" };
}
return {
consumed: true,
newQuantity: this.quantity - 1,
depleted: this.quantity - 1 <= 0,
};
}
/**
* Get chat card data for displaying equipment information.
*
* @returns {Object} Chat card data
*/
getChatData() {
const data = super.getChatData();
data.quantity = this.quantity;
data.category = this.category;
data.consumable = this.consumable;
if (this.consumable && this.uses.max > 0) {
data.uses = `${this.uses.value}/${this.uses.max}`;
}
return data;
}
}