vagabond-rpg-foundryvtt/module/data/item/weapon.mjs
Cal Corum 51f0472d99 Implement Phase 1: Complete data model system for actors and items
Actor Data Models:
- VagabondActorBase: Shared base class with biography field
- CharacterData: Full PC schema with stats, skills, saves, resources,
  custom crit thresholds, dynamic resources, item slots, wealth tracking
- NPCData: Monster stat block with HD, HP, TL, zone, morale, actions,
  abilities, immunities/weaknesses

Item Data Models:
- VagabondItemBase: Shared base with description field
- AncestryData: Being type, size, racial traits
- ClassData: Progression tables, features, mana/casting, trained skills
- SpellData: Dynamic mana cost calculation, delivery/duration types
- PerkData: Prerequisites system, stat/skill/spell requirements
- WeaponData: Damage, grip, properties, attack types, crit thresholds
- ArmorData: Armor value, type, dodge penalty
- EquipmentData: Quantity, slots, consumables
- FeatureData: Class features with Active Effect changes

Active Effects Integration:
- Helper module for creating and managing Active Effects
- Effect key mapping for stats, saves, skills, crit thresholds
- Utilities for applying/removing item effects

Derived Value Calculations (CharacterData):
- Max HP = Might × Level
- Speed by Dexterity lookup
- Item Slots = 8 + Might - Fatigue
- Save difficulties from stat pairs
- Skill difficulties (trained doubles stat contribution)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 15:22:09 -06:00

204 lines
5.2 KiB
JavaScript

/**
* Weapon Item Data Model
*
* Defines the data schema for weapons in Vagabond RPG.
*
* Key properties:
* - Damage dice (e.g., "1d6", "2d6")
* - Grip type (1H, 2H, Versatile)
* - Attack skill (Melee, Brawl, Ranged, Finesse)
* - Weapon properties (Finesse, Thrown, Cleave, Reach, etc.)
* - Slot cost for inventory management
*
* @extends VagabondItemBase
*/
import VagabondItemBase from "./base-item.mjs";
export default class WeaponData extends VagabondItemBase {
/**
* Define the schema for weapon items.
*
* @returns {Object} The schema definition
*/
static defineSchema() {
const fields = foundry.data.fields;
const baseSchema = super.defineSchema();
return {
...baseSchema,
// Damage dice formula
damage: new fields.StringField({
required: true,
initial: "1d6",
}),
// Damage type
damageType: new fields.StringField({
required: true,
initial: "blunt",
}),
// Bonus damage (from magic, etc.)
bonusDamage: new fields.NumberField({
integer: true,
initial: 0,
}),
// Grip type (1h, 2h, versatile, fist)
grip: new fields.StringField({
required: true,
initial: "1h",
choices: ["1h", "2h", "versatile", "fist"],
}),
// Versatile damage (when wielded 2H)
versatileDamage: new fields.StringField({
required: false,
blank: true,
}),
// Attack skill used
attackType: new fields.StringField({
required: true,
initial: "melee",
choices: ["melee", "brawl", "ranged", "finesse"],
}),
// Range (for ranged/thrown weapons)
range: new fields.SchemaField({
value: new fields.NumberField({ integer: true, initial: 0 }),
units: new fields.StringField({ initial: "ft" }),
}),
// Weapon properties
properties: new fields.SchemaField({
finesse: new fields.BooleanField({ initial: false }),
thrown: new fields.BooleanField({ initial: false }),
cleave: new fields.BooleanField({ initial: false }),
reach: new fields.BooleanField({ initial: false }),
loading: new fields.BooleanField({ initial: false }),
brawl: new fields.BooleanField({ initial: false }),
crude: new fields.BooleanField({ initial: false }),
versatile: new fields.BooleanField({ initial: false }),
}),
// Inventory slot cost
slots: new fields.NumberField({
integer: true,
initial: 1,
min: 0,
}),
// Monetary value (in copper)
value: new fields.NumberField({
integer: true,
initial: 0,
min: 0,
}),
// Is this weapon equipped?
equipped: new fields.BooleanField({ initial: false }),
// Quantity (for ammunition, thrown weapons)
quantity: new fields.NumberField({
integer: true,
initial: 1,
min: 0,
}),
// Attack bonus (from magic, etc.)
attackBonus: new fields.NumberField({
integer: true,
initial: 0,
}),
// Critical threshold override (if different from skill default)
critThreshold: new fields.NumberField({
integer: true,
nullable: true,
initial: null,
min: 1,
max: 20,
}),
};
}
/**
* Get the effective attack stat for this weapon.
* Accounts for Finesse property allowing DEX for melee.
*
* @returns {string} The stat key to use for attack rolls
*/
getAttackStat() {
const attackStats = {
melee: "might",
brawl: "might",
ranged: "awareness",
finesse: "dexterity",
};
// Finesse weapons can use DEX for melee
if (this.properties.finesse && this.attackType === "melee") {
return "dexterity";
}
return attackStats[this.attackType] || "might";
}
/**
* Get the active weapon properties as an array.
*
* @returns {Array<string>} Array of active property keys
*/
getActiveProperties() {
return Object.entries(this.properties)
.filter(([, active]) => active)
.map(([prop]) => prop);
}
/**
* Get the effective damage formula.
*
* @param {boolean} twoHanded - Whether using two-handed grip for versatile
* @returns {string} Damage formula
*/
getDamageFormula(twoHanded = false) {
// Use versatile damage if 2H and available
if (twoHanded && this.properties.versatile && this.versatileDamage) {
return this.bonusDamage > 0
? `${this.versatileDamage}+${this.bonusDamage}`
: this.versatileDamage;
}
return this.bonusDamage > 0 ? `${this.damage}+${this.bonusDamage}` : this.damage;
}
/**
* Calculate the slot cost when equipped.
*
* @returns {number} Slot cost
*/
getEquippedSlots() {
return this.equipped ? this.slots : 0;
}
/**
* Get chat card data for displaying weapon information.
*
* @returns {Object} Chat card data
*/
getChatData() {
const data = super.getChatData();
data.damage = this.damage;
data.damageType = this.damageType;
data.grip = this.grip;
data.attackType = this.attackType;
data.properties = this.getActiveProperties();
data.range = this.range.value > 0 ? `${this.range.value} ${this.range.units}` : null;
return data;
}
}