Implement complete item sheet system with Effects tab

Phase 4 (Item Sheets) complete:
- Created VagabondItemSheet base class using ApplicationV2 + HandlebarsApplicationMixin
- Implemented templates for all 8 item types: weapon, armor, equipment, ancestry, class, spell, perk, feature
- Added Details and Effects tabs to all item sheets
- Effects tab provides full CRUD for Active Effects (create, edit, toggle, delete)
- Added ~150 localization strings for item fields and UI elements
- Comprehensive SCSS styling matching parchment theme

Key features:
- Type-specific templates with appropriate fields for each item type
- Array field management for traits, progression tables, features
- Wider dropdowns for ancestry Being Type and Size fields
- Effect controls remain clickable when effect is disabled

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2025-12-15 02:30:18 -06:00
parent 350475b331
commit 5ddd33c41c
19 changed files with 2489 additions and 78 deletions

View File

@ -545,7 +545,7 @@
"id": "4.1", "id": "4.1",
"name": "Create base VagabondItemSheet class", "name": "Create base VagabondItemSheet class",
"description": "Extended ItemSheet with common methods, type-specific tab handling", "description": "Extended ItemSheet with common methods, type-specific tab handling",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "critical", "priority": "critical",
"dependencies": ["2.3"] "dependencies": ["2.3"]
@ -554,7 +554,7 @@
"id": "4.2", "id": "4.2",
"name": "Implement Ancestry item sheet", "name": "Implement Ancestry item sheet",
"description": "Being type dropdown, size dropdown, traits editor (add/remove trait entries)", "description": "Being type dropdown, size dropdown, traits editor (add/remove trait entries)",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "high", "priority": "high",
"dependencies": ["4.1", "1.7"] "dependencies": ["4.1", "1.7"]
@ -563,7 +563,7 @@
"id": "4.3", "id": "4.3",
"name": "Implement Class item sheet", "name": "Implement Class item sheet",
"description": "Key stat, training grants, progression table editor, feature definitions", "description": "Key stat, training grants, progression table editor, feature definitions",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "high", "priority": "high",
"dependencies": ["4.1", "1.8"] "dependencies": ["4.1", "1.8"]
@ -572,7 +572,7 @@
"id": "4.4", "id": "4.4",
"name": "Implement Spell item sheet", "name": "Implement Spell item sheet",
"description": "Damage type, effect text, crit effect, delivery options checkboxes, cost formula display", "description": "Damage type, effect text, crit effect, delivery options checkboxes, cost formula display",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "high", "priority": "high",
"dependencies": ["4.1", "1.9"] "dependencies": ["4.1", "1.9"]
@ -581,7 +581,7 @@
"id": "4.5", "id": "4.5",
"name": "Implement Perk item sheet", "name": "Implement Perk item sheet",
"description": "Prerequisites editor (stat/training/spell requirements), description, effects", "description": "Prerequisites editor (stat/training/spell requirements), description, effects",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "high", "priority": "high",
"dependencies": ["4.1", "1.10"] "dependencies": ["4.1", "1.10"]
@ -590,7 +590,7 @@
"id": "4.6", "id": "4.6",
"name": "Implement Weapon item sheet", "name": "Implement Weapon item sheet",
"description": "Damage dice selector, grip type, properties checkboxes, attack skill dropdown, value/slots", "description": "Damage dice selector, grip type, properties checkboxes, attack skill dropdown, value/slots",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "high", "priority": "high",
"dependencies": ["4.1", "1.11"] "dependencies": ["4.1", "1.11"]
@ -599,7 +599,7 @@
"id": "4.7", "id": "4.7",
"name": "Implement Armor item sheet", "name": "Implement Armor item sheet",
"description": "Armor value, type dropdown, dodge penalty checkbox, value/slots", "description": "Armor value, type dropdown, dodge penalty checkbox, value/slots",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "high", "priority": "high",
"dependencies": ["4.1", "1.12"] "dependencies": ["4.1", "1.12"]
@ -608,7 +608,7 @@
"id": "4.8", "id": "4.8",
"name": "Implement Equipment item sheet", "name": "Implement Equipment item sheet",
"description": "Generic item fields: description, quantity, slots, value, consumable flag", "description": "Generic item fields: description, quantity, slots, value, consumable flag",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "high", "priority": "high",
"dependencies": ["4.1", "1.13"] "dependencies": ["4.1", "1.13"]
@ -617,7 +617,7 @@
"id": "4.9", "id": "4.9",
"name": "Implement Feature item sheet", "name": "Implement Feature item sheet",
"description": "Source class, level requirement, description, passive/active toggle, effects editor", "description": "Source class, level requirement, description, passive/active toggle, effects editor",
"completed": false, "completed": true,
"tested": false, "tested": false,
"priority": "high", "priority": "high",
"dependencies": ["4.1", "1.14"] "dependencies": ["4.1", "1.14"]

View File

@ -287,6 +287,18 @@
"VAGABOND.TabAbilities": "Abilities", "VAGABOND.TabAbilities": "Abilities",
"VAGABOND.TabMagic": "Magic", "VAGABOND.TabMagic": "Magic",
"VAGABOND.TabBiography": "Biography", "VAGABOND.TabBiography": "Biography",
"VAGABOND.TabDetails": "Details",
"VAGABOND.TabEffects": "Effects",
"VAGABOND.ActiveEffects": "Active Effects",
"VAGABOND.AddEffect": "Add Effect",
"VAGABOND.NewEffect": "New Effect",
"VAGABOND.EditEffect": "Edit Effect",
"VAGABOND.DeleteEffect": "Delete Effect",
"VAGABOND.DeleteEffectConfirm": "Are you sure you want to delete the effect \"{name}\"?",
"VAGABOND.ToggleEffect": "Toggle Effect",
"VAGABOND.NoEffects": "No active effects on this item.",
"VAGABOND.EffectsHint": "Effects on this item will be applied when it is equipped or added to a character.",
"VAGABOND.CharacterName": "Character Name", "VAGABOND.CharacterName": "Character Name",
"VAGABOND.NPCName": "NPC Name", "VAGABOND.NPCName": "NPC Name",
@ -379,5 +391,144 @@
"VAGABOND.ItemNew": "New {type}", "VAGABOND.ItemNew": "New {type}",
"VAGABOND.ItemDeleteTitle": "Delete {name}", "VAGABOND.ItemDeleteTitle": "Delete {name}",
"VAGABOND.ItemDeleteConfirm": "Are you sure you want to delete {name}?" "VAGABOND.ItemDeleteConfirm": "Are you sure you want to delete {name}?",
"VAGABOND.ItemName": "Item Name",
"VAGABOND.DamageTypeBlunt": "Blunt",
"VAGABOND.DamageTypePiercing": "Piercing",
"VAGABOND.DamageTypeSlashing": "Slashing",
"VAGABOND.DamageTypeFire": "Fire",
"VAGABOND.DamageTypeCold": "Cold",
"VAGABOND.DamageTypeShock": "Shock",
"VAGABOND.DamageTypeAcid": "Acid",
"VAGABOND.DamageTypePoison": "Poison",
"VAGABOND.DamageTypeNone": "None",
"VAGABOND.DamageBase": "Damage Base",
"VAGABOND.Grip": "Grip",
"VAGABOND.AttackSkill": "Attack Skill",
"VAGABOND.SkillMelee": "Melee",
"VAGABOND.SkillRanged": "Ranged",
"VAGABOND.VersatileDamage": "Versatile Damage",
"VAGABOND.BonusDamage": "Bonus Damage",
"VAGABOND.AttackBonus": "Attack Bonus",
"VAGABOND.Slots": "Slots",
"VAGABOND.Value": "Value",
"VAGABOND.Quantity": "Quantity",
"VAGABOND.WeaponProperties": "Weapon Properties",
"VAGABOND.Material": "Material",
"VAGABOND.MaterialMundane": "Mundane",
"VAGABOND.MaterialSilvered": "Silvered",
"VAGABOND.MaterialAdamantine": "Adamantine",
"VAGABOND.MaterialMagical": "Magical",
"VAGABOND.Equipped": "Equipped",
"VAGABOND.EquippedHand": "Equipped Hand",
"VAGABOND.HandMain": "Main",
"VAGABOND.HandOff": "Off",
"VAGABOND.HandBoth": "Both",
"VAGABOND.Relic": "Relic",
"VAGABOND.RelicTier": "Relic Tier",
"VAGABOND.RequiresAttunement": "Requires Attunement",
"VAGABOND.Attuned": "Attuned",
"VAGABOND.RelicAbilityName": "Ability Name",
"VAGABOND.RelicAbilityDescription": "Ability Description",
"VAGABOND.ActivationCost": "Activation Cost",
"VAGABOND.UsesPerDay": "Uses/Day",
"VAGABOND.UsesRemaining": "Uses Remaining",
"VAGABOND.RelicLore": "Lore",
"VAGABOND.ArmorValue": "Armor Value",
"VAGABOND.ArmorType": "Armor Type",
"VAGABOND.ArmorTypeNone": "None",
"VAGABOND.ArmorTypeLight": "Light",
"VAGABOND.ArmorTypeMedium": "Medium",
"VAGABOND.ArmorTypeHeavy": "Heavy",
"VAGABOND.ArmorTypeShield": "Shield",
"VAGABOND.MagicBonus": "Magic Bonus",
"VAGABOND.Penalties": "Penalties",
"VAGABOND.DodgePenalty": "Dodge Penalty",
"VAGABOND.HindersDodge": "Hinders Dodge",
"VAGABOND.PreventsRage": "Prevents Rage",
"VAGABOND.SlotsPerItem": "Slots Per Item",
"VAGABOND.Category": "Category",
"VAGABOND.CategoryGear": "Gear",
"VAGABOND.CategoryTool": "Tool",
"VAGABOND.CategoryConsumable": "Consumable",
"VAGABOND.CategoryContainer": "Container",
"VAGABOND.CategoryTreasure": "Treasure",
"VAGABOND.CategoryMisc": "Misc",
"VAGABOND.Consumable": "Consumable",
"VAGABOND.Uses": "Uses",
"VAGABOND.UsesCurrent": "Current",
"VAGABOND.UsesMax": "Max",
"VAGABOND.AutoDestroy": "Auto Destroy",
"VAGABOND.ContainerCapacity": "Container Capacity",
"VAGABOND.MagicProperties": "Magic Properties",
"VAGABOND.IsTrinket": "Is Trinket",
"VAGABOND.CanCastThrough": "Can Cast Through",
"VAGABOND.BeingTypeHumanlike": "Humanlike",
"VAGABOND.BeingTypeFae": "Fae",
"VAGABOND.BeingTypeCryptid": "Cryptid",
"VAGABOND.BeingTypeBeast": "Beast",
"VAGABOND.BeingTypeUndead": "Undead",
"VAGABOND.BeingTypeArtificial": "Artificial",
"VAGABOND.BeingTypePrimordial": "Primordial",
"VAGABOND.BeingTypeHellspawn": "Hellspawn",
"VAGABOND.SizeTiny": "Tiny",
"VAGABOND.SizeGargantuan": "Gargantuan",
"VAGABOND.TraitName": "Trait Name",
"VAGABOND.TraitDescription": "Description",
"VAGABOND.NoTraits": "No traits defined",
"VAGABOND.MaxDice": "Max Dice",
"VAGABOND.UseClassDefault": "Use Class Default",
"VAGABOND.DeliveryTypes": "Delivery Types",
"VAGABOND.DurationTypes": "Duration Types",
"VAGABOND.StatRequirements": "Stat Requirements",
"VAGABOND.TrainingRequirements": "Training Requirements",
"VAGABOND.SpellRequirements": "Spell Requirements",
"VAGABOND.PerkRequirements": "Perk Requirements",
"VAGABOND.CustomRequirements": "Custom Requirements",
"VAGABOND.CommaSeparated": "Comma separated",
"VAGABOND.IsRitual": "Is Ritual",
"VAGABOND.RitualDuration": "Ritual Duration",
"VAGABOND.RecoveryPer": "Recovery",
"VAGABOND.ShortRest": "Short Rest",
"VAGABOND.LongRest": "Long Rest",
"VAGABOND.PerDay": "Per Day",
"VAGABOND.PerEncounter": "Per Encounter",
"VAGABOND.LuckCost": "Luck Cost",
"VAGABOND.GrantsLuck": "Grants Luck",
"VAGABOND.RitualComponents": "Components",
"VAGABOND.SourceClass": "Source Class",
"VAGABOND.Activation": "Activation",
"VAGABOND.ActivationType": "Type",
"VAGABOND.Action": "Action",
"VAGABOND.BonusAction": "Bonus Action",
"VAGABOND.Reaction": "Reaction",
"VAGABOND.FreeAction": "Free Action",
"VAGABOND.Special": "Special",
"VAGABOND.Unlimited": "Unlimited",
"VAGABOND.Requirements": "Requirements",
"VAGABOND.KeyStat": "Key Stat",
"VAGABOND.IsCaster": "Is Caster",
"VAGABOND.ActionStyle": "Casting Skill",
"VAGABOND.TrainedSkills": "Trained Skills",
"VAGABOND.StartingPack": "Starting Pack",
"VAGABOND.CustomResource": "Custom Resource",
"VAGABOND.ResourceName": "Resource Name",
"VAGABOND.ResourceMax": "Max Formula",
"VAGABOND.Progression": "Progression",
"VAGABOND.SpellsKnown": "Spells Known",
"VAGABOND.ClassFeatures": "Class Features",
"VAGABOND.NoProgression": "No progression entries",
"VAGABOND.FeatureName": "Feature Name",
"VAGABOND.FeatureDescription": "Description"
} }

View File

@ -3,6 +3,10 @@
* Exports all actor and item sheet classes for the Vagabond RPG system. * Exports all actor and item sheet classes for the Vagabond RPG system.
*/ */
// Actor sheets
export { default as VagabondActorSheet } from "./base-actor-sheet.mjs"; export { default as VagabondActorSheet } from "./base-actor-sheet.mjs";
export { default as VagabondCharacterSheet } from "./character-sheet.mjs"; export { default as VagabondCharacterSheet } from "./character-sheet.mjs";
export { default as VagabondNPCSheet } from "./npc-sheet.mjs"; export { default as VagabondNPCSheet } from "./npc-sheet.mjs";
// Item sheets
export { default as VagabondItemSheet } from "./base-item-sheet.mjs";

View File

@ -0,0 +1,447 @@
/**
* Base Item Sheet for Vagabond RPG
*
* Provides common functionality for all item sheets:
* - Tab navigation (for complex items)
* - Form handling with automatic updates
* - Rich text editor integration
* - Type-specific rendering via parts system
*
* Uses Foundry VTT v13 ItemSheetV2 API with HandlebarsApplicationMixin.
*
* @extends ItemSheetV2
* @mixes HandlebarsApplicationMixin
*/
const { HandlebarsApplicationMixin } = foundry.applications.api;
const { ItemSheetV2 } = foundry.applications.sheets;
export default class VagabondItemSheet extends HandlebarsApplicationMixin(ItemSheetV2) {
/**
* @param {Object} options - Application options
*/
constructor(options = {}) {
super(options);
// Active tab tracking (for items with tabs)
this._activeTab = "details";
}
/* -------------------------------------------- */
/* Static Properties */
/* -------------------------------------------- */
/** @override */
static DEFAULT_OPTIONS = {
id: "vagabond-item-sheet-{id}",
classes: ["vagabond", "sheet", "item"],
tag: "form",
window: {
title: "VAGABOND.ItemSheet",
icon: "fa-solid fa-suitcase",
resizable: true,
},
position: {
width: 520,
height: 480,
},
form: {
handler: VagabondItemSheet.#onFormSubmit,
submitOnChange: true,
closeOnSubmit: false,
},
actions: {
changeTab: VagabondItemSheet.#onChangeTab,
addArrayEntry: VagabondItemSheet.#onAddArrayEntry,
deleteArrayEntry: VagabondItemSheet.#onDeleteArrayEntry,
createEffect: VagabondItemSheet.#onCreateEffect,
editEffect: VagabondItemSheet.#onEditEffect,
deleteEffect: VagabondItemSheet.#onDeleteEffect,
toggleEffect: VagabondItemSheet.#onToggleEffect,
},
};
/** @override */
static PARTS = {
header: {
template: "systems/vagabond/templates/item/parts/item-header.hbs",
},
tabs: {
template: "systems/vagabond/templates/item/parts/item-tabs.hbs",
},
body: {
template: "systems/vagabond/templates/item/parts/item-body.hbs",
},
effects: {
template: "systems/vagabond/templates/item/parts/item-effects.hbs",
},
};
/* -------------------------------------------- */
/* Getters */
/* -------------------------------------------- */
/**
* Convenient alias for the item document.
* @returns {VagabondItem}
*/
get item() {
return this.document;
}
/** @override */
get title() {
return this.document.name;
}
/**
* Get the available tabs for this item type.
* All items have Details and Effects tabs.
* @returns {Object[]} Array of tab definitions
*/
get tabs() {
return [
{
id: "details",
label: "VAGABOND.TabDetails",
icon: "fas fa-list",
},
{
id: "effects",
label: "VAGABOND.TabEffects",
icon: "fas fa-bolt",
},
];
}
/**
* Whether this item type uses tabs.
* @returns {boolean}
*/
get hasTabs() {
return this.tabs !== null && this.tabs.length > 0;
}
/* -------------------------------------------- */
/* Data Preparation */
/* -------------------------------------------- */
/** @override */
async _prepareContext(options) {
const context = await super._prepareContext(options);
// Basic item data
context.item = this.item;
context.system = this.item.system;
context.source = this.item.toObject().system;
// Sheet state
context.hasTabs = this.hasTabs;
context.activeTab = this._activeTab;
if (this.hasTabs) {
context.tabs = this.tabs.map((tab) => ({
...tab,
active: tab.id === this._activeTab,
cssClass: tab.id === this._activeTab ? "active" : "",
}));
}
// Roll data for formulas in templates
context.rollData = this.item.getRollData();
// Editable state
context.editable = this.isEditable;
context.owner = this.item.isOwner;
context.limited = this.item.limited;
// System configuration
context.config = CONFIG.VAGABOND;
// Item type for conditional rendering
context.itemType = this.item.type;
// Active Effects on this item
context.effects = this._prepareEffects();
// Type-specific context
await this._prepareTypeContext(context, options);
return context;
}
/**
* Prepare Active Effects for display.
* @returns {Object[]} Array of effect data for rendering
* @protected
*/
_prepareEffects() {
return this.item.effects.map((effect) => ({
id: effect.id,
name: effect.name,
icon: effect.icon,
disabled: effect.disabled,
duration: effect.duration,
source: effect.sourceName,
}));
}
/**
* Prepare type-specific context data.
* Subclasses should override this.
*
* @param {Object} context - The context object to augment
* @param {Object} options - Render options
* @protected
*/
async _prepareTypeContext(_context, _options) {
// Override in subclasses
}
/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */
/** @override */
_onRender(context, options) {
super._onRender(context, options);
// Clean up inactive tabs if using tabs
if (this.hasTabs) {
this._cleanupInactiveTabs();
}
// Initialize rich text editors
this._initializeEditors();
}
/**
* Remove tab content sections that don't match the active tab.
* ApplicationV2's parts rendering appends new parts without removing old ones.
* @protected
*/
_cleanupInactiveTabs() {
if (!this.element) return;
const activeTabClass = `${this._activeTab}-tab`;
const tabContents = this.element.querySelectorAll(".tab-content");
for (const tabContent of tabContents) {
if (!tabContent.classList.contains(activeTabClass)) {
tabContent.remove();
}
}
}
/**
* Initialize rich text editors for description fields.
* @protected
*/
_initializeEditors() {
// Foundry automatically handles ProseMirror/TinyMCE for HTMLField inputs
// Additional initialization can be added here if needed
}
/* -------------------------------------------- */
/* Action Handlers */
/* -------------------------------------------- */
/**
* Handle form submission.
* @param {Event} event
* @param {HTMLFormElement} form
* @param {FormDataExtended} formData
*/
static async #onFormSubmit(event, form, formData) {
const sheet = this;
const updateData = foundry.utils.expandObject(formData.object);
// Clean up array data that may have gaps from deletions
sheet._cleanArrayData(updateData);
await sheet.item.update(updateData);
}
/**
* Clean array data to remove gaps from deleted entries.
* Converts { "0": {...}, "2": {...} } to [ {...}, {...} ]
* @param {Object} data - The update data object
* @protected
*/
_cleanArrayData(data) {
// Handle system-level arrays
if (data.system) {
this._cleanArraysRecursive(data.system);
}
}
/**
* Recursively clean arrays in an object.
* @param {Object} obj - Object to clean
* @protected
*/
_cleanArraysRecursive(obj) {
for (const [key, value] of Object.entries(obj)) {
if (value && typeof value === "object" && !Array.isArray(value)) {
// Check if this looks like an indexed object (array-like)
const keys = Object.keys(value);
const isIndexed = keys.every((k) => /^\d+$/.test(k));
if (isIndexed && keys.length > 0) {
// Convert to array, filtering out nulls/undefined
obj[key] = Object.values(value).filter((v) => v != null);
} else {
// Recurse into nested objects
this._cleanArraysRecursive(value);
}
}
}
}
/**
* Handle tab change action.
* @param {PointerEvent} event
* @param {HTMLElement} target
*/
static async #onChangeTab(event, target) {
event.preventDefault();
const tab = target.dataset.tab;
if (!tab) return;
this._activeTab = tab;
this.render();
}
/**
* Handle adding a new entry to an array field.
* @param {PointerEvent} event
* @param {HTMLElement} target
*/
static async #onAddArrayEntry(event, target) {
event.preventDefault();
const field = target.dataset.field;
if (!field) return;
const currentArray = foundry.utils.getProperty(this.item, field) || [];
const template = this._getArrayEntryTemplate(field);
await this.item.update({
[field]: [...currentArray, template],
});
}
/**
* Handle deleting an entry from an array field.
* @param {PointerEvent} event
* @param {HTMLElement} target
*/
static async #onDeleteArrayEntry(event, target) {
event.preventDefault();
const field = target.dataset.field;
const index = parseInt(target.dataset.index, 10);
if (!field || isNaN(index)) return;
const currentArray = foundry.utils.getProperty(this.item, field) || [];
const newArray = [...currentArray];
newArray.splice(index, 1);
await this.item.update({
[field]: newArray,
});
}
/**
* Get the default template for a new array entry.
* Subclasses should override for their specific array fields.
* @param {string} field - The field path
* @returns {Object} Default entry object
* @protected
*/
_getArrayEntryTemplate(_field) {
// Default template - subclasses override for specific fields
return { name: "", description: "" };
}
/* -------------------------------------------- */
/* Active Effect Actions */
/* -------------------------------------------- */
/**
* Handle creating a new Active Effect on this item.
* @param {PointerEvent} event
* @param {HTMLElement} target
*/
static async #onCreateEffect(event, _target) {
event.preventDefault();
// Create a new effect with default values
const effectData = {
name: game.i18n.localize("VAGABOND.NewEffect"),
icon: "icons/svg/aura.svg",
origin: this.item.uuid,
disabled: false,
};
const [created] = await this.item.createEmbeddedDocuments("ActiveEffect", [effectData]);
// Open the effect config sheet
if (created) {
created.sheet.render(true);
}
}
/**
* Handle editing an existing Active Effect.
* @param {PointerEvent} event
* @param {HTMLElement} target
*/
static async #onEditEffect(event, target) {
event.preventDefault();
const effectId = target.closest("[data-effect-id]")?.dataset.effectId;
if (!effectId) return;
const effect = this.item.effects.get(effectId);
if (effect) {
effect.sheet.render(true);
}
}
/**
* Handle deleting an Active Effect.
* @param {PointerEvent} event
* @param {HTMLElement} target
*/
static async #onDeleteEffect(event, target) {
event.preventDefault();
const effectId = target.closest("[data-effect-id]")?.dataset.effectId;
if (!effectId) return;
const effect = this.item.effects.get(effectId);
if (!effect) return;
// Confirm deletion
const confirmed = await Dialog.confirm({
title: game.i18n.localize("VAGABOND.DeleteEffect"),
content: `<p>${game.i18n.format("VAGABOND.DeleteEffectConfirm", { name: effect.name })}</p>`,
});
if (confirmed) {
await effect.delete();
}
}
/**
* Handle toggling an Active Effect's disabled state.
* @param {PointerEvent} event
* @param {HTMLElement} target
*/
static async #onToggleEffect(event, target) {
event.preventDefault();
const effectId = target.closest("[data-effect-id]")?.dataset.effectId;
if (!effectId) return;
const effect = this.item.effects.get(effectId);
if (effect) {
await effect.update({ disabled: !effect.disabled });
}
}
}

View File

@ -3,7 +3,7 @@
* @module vagabond * @module vagabond
*/ */
/* global Actors */ /* global Actors, Items */
// Import configuration // Import configuration
import { VAGABOND } from "./helpers/config.mjs"; import { VAGABOND } from "./helpers/config.mjs";
@ -35,7 +35,12 @@ import {
} from "./applications/_module.mjs"; } from "./applications/_module.mjs";
// Import sheet classes // Import sheet classes
import { VagabondActorSheet, VagabondCharacterSheet, VagabondNPCSheet } from "./sheets/_module.mjs"; import {
VagabondActorSheet,
VagabondCharacterSheet,
VagabondNPCSheet,
VagabondItemSheet,
} from "./sheets/_module.mjs";
// Import test registration (for Quench) // Import test registration (for Quench)
import { registerQuenchTests } from "./tests/quench-init.mjs"; import { registerQuenchTests } from "./tests/quench-init.mjs";
@ -64,6 +69,20 @@ async function preloadHandlebarsTemplates() {
"systems/vagabond/templates/actor/npc-actions.hbs", "systems/vagabond/templates/actor/npc-actions.hbs",
"systems/vagabond/templates/actor/npc-abilities.hbs", "systems/vagabond/templates/actor/npc-abilities.hbs",
"systems/vagabond/templates/actor/npc-notes.hbs", "systems/vagabond/templates/actor/npc-notes.hbs",
// Item sheet parts
"systems/vagabond/templates/item/parts/item-header.hbs",
"systems/vagabond/templates/item/parts/item-tabs.hbs",
"systems/vagabond/templates/item/parts/item-body.hbs",
"systems/vagabond/templates/item/parts/item-effects.hbs",
// Item type templates
"systems/vagabond/templates/item/types/weapon.hbs",
"systems/vagabond/templates/item/types/armor.hbs",
"systems/vagabond/templates/item/types/equipment.hbs",
"systems/vagabond/templates/item/types/ancestry.hbs",
"systems/vagabond/templates/item/types/class.hbs",
"systems/vagabond/templates/item/types/spell.hbs",
"systems/vagabond/templates/item/types/perk.hbs",
"systems/vagabond/templates/item/types/feature.hbs",
]; ];
return loadTemplates(templatePaths); return loadTemplates(templatePaths);
@ -93,6 +112,7 @@ Hooks.once("init", async () => {
VagabondActorSheet, VagabondActorSheet,
VagabondCharacterSheet, VagabondCharacterSheet,
VagabondNPCSheet, VagabondNPCSheet,
VagabondItemSheet,
}, },
}; };
@ -131,12 +151,12 @@ Hooks.once("init", async () => {
label: "VAGABOND.SheetNPC", label: "VAGABOND.SheetNPC",
}); });
// Register Item sheet classes (TODO: Phase 4) // Register Item sheet classes
// Items.unregisterSheet("core", ItemSheet); Items.unregisterSheet("core", ItemSheet);
// Items.registerSheet("vagabond", VagabondItemSheet, { Items.registerSheet("vagabond", VagabondItemSheet, {
// makeDefault: true, makeDefault: true,
// label: "VAGABOND.SheetItem" label: "VAGABOND.SheetItem",
// }); });
// Preload Handlebars templates // Preload Handlebars templates
await preloadHandlebarsTemplates(); await preloadHandlebarsTemplates();

View File

@ -92,6 +92,38 @@
} }
} }
@mixin button-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
padding: 0;
font-size: $font-size-sm;
color: $color-text-secondary;
background-color: transparent;
border: 1px solid transparent;
border-radius: $radius-sm;
cursor: pointer;
transition: all $transition-fast;
&:hover:not(:disabled) {
color: $color-text-primary;
background-color: $color-parchment-dark;
border-color: $color-border;
}
&:focus {
outline: 2px solid $color-accent-primary;
outline-offset: 2px;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
// Form inputs // Form inputs
@mixin input-base { @mixin input-base {
padding: $spacing-2 $spacing-3; padding: $spacing-2 $spacing-3;

View File

@ -1,101 +1,656 @@
// Vagabond RPG - Item Sheet Styles // Vagabond RPG - Item Sheet Styles
// ================================= // =================================
// Placeholder - will be expanded in Phase 4
.vagabond.sheet.item { .vagabond.sheet.item {
min-width: 400px; min-width: 500px;
min-height: 300px; min-height: 400px;
.sheet-header { // Header styling
.sheet-header.item-header {
@include flex-between; @include flex-between;
padding: $spacing-4; padding: $spacing-4;
background-color: $color-parchment-dark; background-color: $color-parchment-dark;
border-bottom: 2px solid $color-border; border-bottom: 2px solid $color-border;
gap: $spacing-4;
.item-img { .item-img {
width: 60px; width: 64px;
height: 60px; height: 64px;
object-fit: cover; object-fit: cover;
border: 2px solid $color-border; border: 2px solid $color-border;
border-radius: $radius-md; border-radius: $radius-md;
cursor: pointer;
&:hover {
border-color: $color-accent-primary;
}
} }
.header-fields { .header-fields {
flex: 1; flex: 1;
margin-left: $spacing-4; display: flex;
flex-direction: column;
gap: $spacing-2;
.item-name {
margin: 0;
input {
width: 100%;
font-size: $font-size-lg;
font-weight: bold;
border: none;
background: transparent;
border-bottom: 1px solid transparent;
&:focus {
border-bottom-color: $color-accent-primary;
}
} }
} }
.item-type {
.type-badge {
display: inline-block;
padding: $spacing-1 $spacing-3;
font-size: $font-size-sm;
font-weight: 500;
text-transform: capitalize;
background-color: $color-parchment;
border: 1px solid $color-border;
border-radius: $radius-full;
&.weapon {
background-color: mix($color-danger, $color-parchment, 15%);
}
&.armor {
background-color: mix($color-info, $color-parchment, 15%);
}
&.spell {
background-color: mix($color-reason, $color-parchment, 15%);
}
&.perk {
background-color: mix($color-success, $color-parchment, 15%);
}
&.class {
background-color: mix($color-warning, $color-parchment, 15%);
}
}
}
}
}
// Sheet body
.sheet-body { .sheet-body {
padding: $spacing-4; padding: $spacing-4;
overflow-y: auto;
max-height: calc(100% - 80px);
}
// Item content container
.item-content {
display: flex;
flex-direction: column;
gap: $spacing-4;
}
// Stats row
.item-stats-row {
display: flex;
flex-wrap: wrap;
gap: $spacing-4;
align-items: flex-end;
.stat-group {
display: flex;
flex-direction: column;
gap: $spacing-1;
min-width: 80px;
&.full-width {
flex: 1 1 100%;
}
&.wide {
min-width: 140px;
select {
min-width: 130px;
}
}
label {
font-size: $font-size-sm;
font-weight: 500;
color: $color-text-secondary;
}
input[type="text"],
input[type="number"],
select,
textarea {
padding: $spacing-2;
border: 1px solid $color-border;
border-radius: $radius-sm;
background-color: $color-parchment-light;
&:focus {
border-color: $color-accent-primary;
outline: none;
}
&:disabled {
background-color: $color-parchment;
cursor: not-allowed;
}
}
input[type="checkbox"] {
width: 18px;
height: 18px;
margin: 0;
cursor: pointer;
}
input[type="number"] {
width: 70px;
}
.range-input {
display: flex;
align-items: center;
gap: $spacing-2;
input {
width: 60px;
}
}
.units {
font-size: $font-size-sm;
color: $color-text-secondary;
}
textarea {
min-height: 60px;
resize: vertical;
}
}
}
// Fieldset styling
fieldset {
border: 1px solid $color-border;
border-radius: $radius-md;
padding: $spacing-3;
margin: 0;
background-color: rgba($color-parchment-dark, 0.3);
legend {
padding: 0 $spacing-2;
font-weight: 600;
font-size: $font-size-sm;
display: flex;
align-items: center;
gap: $spacing-2;
button {
@include button-icon;
padding: $spacing-1;
font-size: $font-size-xs;
}
}
&.collapsed {
padding: $spacing-2;
background-color: transparent;
}
}
// Properties grid (checkboxes)
.properties-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: $spacing-2;
.property-checkbox {
display: flex;
align-items: center;
gap: $spacing-2;
cursor: pointer;
font-size: $font-size-sm;
input[type="checkbox"] {
width: 16px;
height: 16px;
}
}
}
// Relic section
.relic-section {
.relic-toggle {
display: flex;
align-items: center;
gap: $spacing-2;
cursor: pointer;
}
.relic-fields {
display: flex;
flex-direction: column;
gap: $spacing-3;
margin-top: $spacing-3;
}
}
// Description editor
.item-description {
display: flex;
flex-direction: column;
gap: $spacing-2;
> label {
font-weight: 600;
}
.editor-container {
border: 1px solid $color-border;
border-radius: $radius-sm;
overflow: hidden;
.editor {
min-height: 120px;
padding: $spacing-2;
background-color: $color-parchment-light;
}
}
}
// Traits list (ancestry)
.traits-list {
display: flex;
flex-direction: column;
gap: $spacing-3;
.trait-entry {
padding: $spacing-3;
border: 1px solid $color-border;
border-radius: $radius-sm;
background-color: $color-parchment-light;
.trait-header {
display: flex;
gap: $spacing-2;
margin-bottom: $spacing-2;
input {
flex: 1;
font-weight: 500;
}
button {
@include button-icon;
color: $color-danger;
}
}
.trait-description textarea {
width: 100%;
min-height: 60px;
}
}
.no-traits {
color: $color-text-secondary;
font-style: italic;
text-align: center;
padding: $spacing-4;
}
}
// Prerequisite sections (perk)
.prerequisites-section {
.prereq-stats {
margin-bottom: $spacing-3;
}
.stat-prereq-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: $spacing-2;
margin-top: $spacing-2;
.stat-prereq {
display: flex;
flex-direction: column;
align-items: center;
gap: $spacing-1;
label {
font-size: $font-size-xs;
font-weight: 600;
color: $color-text-secondary;
}
input {
width: 40px;
text-align: center;
}
}
}
.prereq-label {
font-weight: 500;
font-size: $font-size-sm;
}
.prereq-training,
.prereq-spells,
.prereq-perks,
.prereq-custom {
margin-top: $spacing-3;
label {
display: block;
margin-bottom: $spacing-1;
font-size: $font-size-sm;
font-weight: 500;
}
input {
width: 100%;
}
.hint {
font-size: $font-size-xs;
color: $color-text-secondary;
font-style: italic;
}
}
}
// Progression table (class)
.progression-section {
.progression-table {
margin-top: $spacing-3;
.progression-header {
display: grid;
grid-template-columns: 50px 60px 80px 80px 1fr 40px;
gap: $spacing-2;
padding: $spacing-2;
background-color: $color-parchment-dark;
font-size: $font-size-xs;
font-weight: 600;
border-radius: $radius-sm $radius-sm 0 0;
}
.progression-row {
display: grid;
grid-template-columns: 50px 60px 80px 80px 1fr 40px;
gap: $spacing-2;
padding: $spacing-2;
border-bottom: 1px solid $color-border;
background-color: $color-parchment-light;
&:last-child {
border-radius: 0 0 $radius-sm $radius-sm;
}
input {
width: 100%;
padding: $spacing-1;
font-size: $font-size-sm;
}
button {
@include button-icon;
color: $color-danger;
}
}
.no-progression {
padding: $spacing-4;
text-align: center;
color: $color-text-secondary;
font-style: italic;
}
}
}
// Features list (class)
.features-section {
.features-list {
display: flex;
flex-direction: column;
gap: $spacing-3;
margin-top: $spacing-3;
.feature-entry {
padding: $spacing-3;
border: 1px solid $color-border;
border-radius: $radius-sm;
background-color: $color-parchment-light;
.feature-header {
display: flex;
gap: $spacing-2;
align-items: center;
margin-bottom: $spacing-2;
input[type="text"] {
flex: 1;
font-weight: 500;
}
input[type="number"] {
width: 50px;
}
.passive-toggle {
display: flex;
align-items: center;
gap: $spacing-1;
font-size: $font-size-sm;
cursor: pointer;
}
button {
@include button-icon;
color: $color-danger;
}
}
.feature-description textarea {
width: 100%;
min-height: 60px;
}
}
.no-features {
padding: $spacing-4;
text-align: center;
color: $color-text-secondary;
font-style: italic;
}
}
}
// Spell effect and crit effect
.spell-effect,
.spell-crit-effect {
display: flex;
flex-direction: column;
gap: $spacing-2;
> label {
font-weight: 600;
}
.editor-container {
border: 1px solid $color-border;
border-radius: $radius-sm;
.editor {
min-height: 80px;
padding: $spacing-2;
background-color: $color-parchment-light;
}
}
}
// Uses section
.uses-section,
.consumable-uses {
.item-stats-row {
margin-top: $spacing-2;
}
}
// Activation section
.activation-section {
.item-stats-row {
margin-top: $spacing-2;
}
}
// Effects tab
.effects-tab {
padding: $spacing-4;
.effects-list {
display: flex;
flex-direction: column;
gap: $spacing-3;
}
.effects-header {
display: flex;
justify-content: space-between;
align-items: center;
h3 {
margin: 0;
font-size: $font-size-lg;
font-weight: 600;
}
.add-effect {
@include button-primary;
padding: $spacing-1 $spacing-3;
font-size: $font-size-sm;
i {
margin-right: $spacing-1;
}
}
}
.effect-items {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: $spacing-2;
}
.effect-item {
display: flex;
align-items: center;
gap: $spacing-2;
padding: $spacing-2;
background-color: $color-parchment-light;
border: 1px solid $color-border;
border-radius: $radius-sm;
&.disabled {
opacity: 0.6;
.effect-name {
text-decoration: line-through;
}
// Ensure controls remain clickable when effect is disabled
.effect-controls {
opacity: 1;
pointer-events: auto;
button {
pointer-events: auto;
}
}
}
.effect-icon {
width: 24px;
height: 24px;
border-radius: $radius-sm;
border: 1px solid $color-border;
}
.effect-name {
flex: 1;
font-weight: 500;
}
.effect-controls {
display: flex;
gap: $spacing-1;
.effect-control {
@include button-icon;
&.delete {
color: $color-danger;
&:hover {
background-color: rgba($color-danger, 0.1);
}
}
}
}
}
.no-effects {
color: $color-text-secondary;
font-style: italic;
text-align: center;
padding: $spacing-4;
} }
// Item description .effects-hint {
.item-description {
.editor {
min-height: 150px;
}
}
}
// Spell item specific
.vagabond.sheet.item.spell {
.spell-details {
@include grid(2);
margin-bottom: $spacing-4;
}
.delivery-options {
margin-bottom: $spacing-4;
.option-grid {
@include grid(3, $spacing-2);
}
}
}
// Perk item specific
.vagabond.sheet.item.perk {
.prerequisites {
@include panel;
padding: $spacing-3;
margin-bottom: $spacing-4;
.prereq-list {
@include flex-column;
gap: $spacing-2;
}
}
}
// Weapon item specific
.vagabond.sheet.item.weapon {
.weapon-properties {
display: flex;
flex-wrap: wrap;
gap: $spacing-2;
margin-bottom: $spacing-4;
.property-tag {
padding: $spacing-1 $spacing-2;
font-size: $font-size-sm; font-size: $font-size-sm;
background-color: $color-parchment-dark; color: $color-text-secondary;
border: 1px solid $color-border; margin-top: $spacing-2;
border-radius: $radius-full;
i {
margin-right: $spacing-1;
}
}
}
}
// Type-specific additional styles
.vagabond.sheet.item.weapon {
.weapon-content {
.item-properties {
margin-top: $spacing-2;
} }
} }
} }
// Class item specific
.vagabond.sheet.item.class { .vagabond.sheet.item.class {
.progression-table { min-width: 600px;
width: 100%; min-height: 600px;
margin-bottom: $spacing-4;
th { .class-content {
position: sticky; .progression-header {
top: 0; // Adjust columns without delete button column for non-editable
background-color: $color-parchment-dark; &.readonly {
grid-template-columns: 50px 60px 80px 80px 1fr;
}
} }
} }
} }

View File

@ -0,0 +1,5 @@
{{!-- Item Sheet Body - Routes to type-specific template --}}
<section class="sheet-body tab-content details-tab{{#unless (eq activeTab "details")}} hidden{{/unless}}">
{{!-- Type-specific content is injected by subclass sheets --}}
{{> (concat "systems/vagabond/templates/item/types/" itemType ".hbs") }}
</section>

View File

@ -0,0 +1,44 @@
{{!-- Item Effects Tab --}}
<section class="tab-content effects-tab{{#unless (eq activeTab "effects")}} hidden{{/unless}}">
<div class="effects-list">
<header class="effects-header">
<h3>{{localize "VAGABOND.ActiveEffects"}}</h3>
{{#if editable}}
<button type="button" class="add-effect" data-action="createEffect">
<i class="fas fa-plus"></i> {{localize "VAGABOND.AddEffect"}}
</button>
{{/if}}
</header>
{{#if effects.length}}
<ul class="effect-items">
{{#each effects}}
<li class="effect-item{{#if disabled}} disabled{{/if}}" data-effect-id="{{id}}">
<img class="effect-icon" src="{{icon}}" alt="{{name}}" />
<span class="effect-name">{{name}}</span>
<div class="effect-controls">
{{#if ../editable}}
<button type="button" class="effect-control" data-action="toggleEffect" title="{{localize 'VAGABOND.ToggleEffect'}}">
<i class="fas {{#if disabled}}fa-toggle-off{{else}}fa-toggle-on{{/if}}"></i>
</button>
<button type="button" class="effect-control" data-action="editEffect" title="{{localize 'VAGABOND.EditEffect'}}">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="effect-control delete" data-action="deleteEffect" title="{{localize 'VAGABOND.DeleteEffect'}}">
<i class="fas fa-trash"></i>
</button>
{{/if}}
</div>
</li>
{{/each}}
</ul>
{{else}}
<p class="no-effects">{{localize "VAGABOND.NoEffects"}}</p>
{{/if}}
<p class="effects-hint">
<i class="fas fa-info-circle"></i>
{{localize "VAGABOND.EffectsHint"}}
</p>
</div>
</section>

View File

@ -0,0 +1,14 @@
{{!-- Item Sheet Header --}}
<header class="sheet-header item-header">
<img class="item-img" src="{{item.img}}" alt="{{item.name}}" data-edit="img" />
<div class="header-fields">
<h1 class="item-name">
<input type="text" name="name" value="{{item.name}}" placeholder="{{localize 'VAGABOND.ItemName'}}" {{#unless editable}}disabled{{/unless}} />
</h1>
<div class="item-type">
<span class="type-badge {{itemType}}">{{localize (concat "VAGABOND.ItemType" (capitalize itemType))}}</span>
</div>
</div>
</header>

View File

@ -0,0 +1,9 @@
{{!-- Item Sheet Tabs --}}
<nav class="sheet-tabs tabs{{#unless hasTabs}} hidden{{/unless}}" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-action="changeTab">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{localize label}}
</a>
{{/each}}
</nav>

View File

@ -0,0 +1,71 @@
{{!-- Ancestry Item Sheet Body --}}
<div class="item-content ancestry-content">
{{!-- Core Stats Row --}}
<div class="item-stats-row">
<div class="stat-group wide">
<label>{{localize "VAGABOND.BeingType"}}</label>
<select name="system.beingType" {{#unless editable}}disabled{{/unless}}>
<option value="humanlike" {{#if (eq system.beingType "humanlike")}}selected{{/if}}>{{localize "VAGABOND.BeingTypeHumanlike"}}</option>
<option value="fae" {{#if (eq system.beingType "fae")}}selected{{/if}}>{{localize "VAGABOND.BeingTypeFae"}}</option>
<option value="cryptid" {{#if (eq system.beingType "cryptid")}}selected{{/if}}>{{localize "VAGABOND.BeingTypeCryptid"}}</option>
<option value="beast" {{#if (eq system.beingType "beast")}}selected{{/if}}>{{localize "VAGABOND.BeingTypeBeast"}}</option>
<option value="undead" {{#if (eq system.beingType "undead")}}selected{{/if}}>{{localize "VAGABOND.BeingTypeUndead"}}</option>
<option value="artificial" {{#if (eq system.beingType "artificial")}}selected{{/if}}>{{localize "VAGABOND.BeingTypeArtificial"}}</option>
<option value="primordial" {{#if (eq system.beingType "primordial")}}selected{{/if}}>{{localize "VAGABOND.BeingTypePrimordial"}}</option>
<option value="hellspawn" {{#if (eq system.beingType "hellspawn")}}selected{{/if}}>{{localize "VAGABOND.BeingTypeHellspawn"}}</option>
</select>
</div>
<div class="stat-group wide">
<label>{{localize "VAGABOND.Size"}}</label>
<select name="system.size" {{#unless editable}}disabled{{/unless}}>
<option value="tiny" {{#if (eq system.size "tiny")}}selected{{/if}}>{{localize "VAGABOND.SizeTiny"}}</option>
<option value="small" {{#if (eq system.size "small")}}selected{{/if}}>{{localize "VAGABOND.SizeSmall"}}</option>
<option value="medium" {{#if (eq system.size "medium")}}selected{{/if}}>{{localize "VAGABOND.SizeMedium"}}</option>
<option value="large" {{#if (eq system.size "large")}}selected{{/if}}>{{localize "VAGABOND.SizeLarge"}}</option>
<option value="huge" {{#if (eq system.size "huge")}}selected{{/if}}>{{localize "VAGABOND.SizeHuge"}}</option>
<option value="gargantuan" {{#if (eq system.size "gargantuan")}}selected{{/if}}>{{localize "VAGABOND.SizeGargantuan"}}</option>
</select>
</div>
</div>
{{!-- Traits Section --}}
<fieldset class="traits-section">
<legend>
{{localize "VAGABOND.Traits"}}
{{#if editable}}
<button type="button" class="add-trait" data-action="addArrayEntry" data-field="system.traits">
<i class="fas fa-plus"></i>
</button>
{{/if}}
</legend>
<div class="traits-list">
{{#each system.traits as |trait index|}}
<div class="trait-entry" data-index="{{index}}">
<div class="trait-header">
<input type="text" name="system.traits.{{index}}.name" value="{{trait.name}}" placeholder="{{localize 'VAGABOND.TraitName'}}" {{#unless ../editable}}disabled{{/unless}} />
{{#if ../editable}}
<button type="button" class="delete-trait" data-action="deleteArrayEntry" data-field="system.traits" data-index="{{index}}">
<i class="fas fa-trash"></i>
</button>
{{/if}}
</div>
<div class="trait-description">
<textarea name="system.traits.{{index}}.description" placeholder="{{localize 'VAGABOND.TraitDescription'}}" {{#unless ../editable}}disabled{{/unless}}>{{trait.description}}</textarea>
</div>
</div>
{{else}}
<p class="no-traits">{{localize "VAGABOND.NoTraits"}}</p>
{{/each}}
</div>
</fieldset>
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
</div>
</div>
</div>

View File

@ -0,0 +1,135 @@
{{!-- Armor Item Sheet Body --}}
<div class="item-content armor-content">
{{!-- Core Stats Row --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.ArmorValue"}}</label>
<input type="number" name="system.armorValue" value="{{system.armorValue}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.ArmorType"}}</label>
<select name="system.armorType" {{#unless editable}}disabled{{/unless}}>
<option value="none" {{#if (eq system.armorType "none")}}selected{{/if}}>{{localize "VAGABOND.ArmorTypeNone"}}</option>
<option value="light" {{#if (eq system.armorType "light")}}selected{{/if}}>{{localize "VAGABOND.ArmorTypeLight"}}</option>
<option value="medium" {{#if (eq system.armorType "medium")}}selected{{/if}}>{{localize "VAGABOND.ArmorTypeMedium"}}</option>
<option value="heavy" {{#if (eq system.armorType "heavy")}}selected{{/if}}>{{localize "VAGABOND.ArmorTypeHeavy"}}</option>
<option value="shield" {{#if (eq system.armorType "shield")}}selected{{/if}}>{{localize "VAGABOND.ArmorTypeShield"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.MagicBonus"}}</label>
<input type="number" name="system.magicBonus" value="{{system.magicBonus}}" {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Secondary Stats Row --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.Slots"}}</label>
<input type="number" name="system.slots" value="{{system.slots}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Value"}}</label>
<input type="number" name="system.value" value="{{system.value}}" min="0" {{#unless editable}}disabled{{/unless}} />
<span class="units">c</span>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Equipped"}}</label>
<input type="checkbox" name="system.equipped" {{#if system.equipped}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Penalties --}}
<fieldset class="item-penalties">
<legend>{{localize "VAGABOND.Penalties"}}</legend>
<div class="properties-grid">
<label class="property-checkbox">
<input type="checkbox" name="system.dodgePenalty" {{#if system.dodgePenalty}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DodgePenalty"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.hindersDodge" {{#if system.hindersDodge}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.HindersDodge"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.preventsRage" {{#if system.preventsRage}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PreventsRage"}}
</label>
</div>
</fieldset>
{{!-- Relic Section (collapsible) --}}
<fieldset class="relic-section {{#unless system.relic.isRelic}}collapsed{{/unless}}">
<legend>
<label class="relic-toggle">
<input type="checkbox" name="system.relic.isRelic" {{#if system.relic.isRelic}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.Relic"}}
</label>
</legend>
{{#if system.relic.isRelic}}
<div class="relic-fields">
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.RelicTier"}}</label>
<input type="number" name="system.relic.tier" value="{{system.relic.tier}}" min="1" max="5" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.RequiresAttunement"}}</label>
<input type="checkbox" name="system.relic.requiresAttunement" {{#if system.relic.requiresAttunement}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
{{#if system.relic.requiresAttunement}}
<div class="stat-group">
<label>{{localize "VAGABOND.Attuned"}}</label>
<input type="checkbox" name="system.relic.attuned" {{#if system.relic.attuned}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
{{/if}}
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicAbilityName"}}</label>
<input type="text" name="system.relic.abilityName" value="{{system.relic.abilityName}}" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicAbilityDescription"}}</label>
<textarea name="system.relic.abilityDescription" {{#unless editable}}disabled{{/unless}}>{{system.relic.abilityDescription}}</textarea>
</div>
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.ActivationCost"}}</label>
<input type="text" name="system.relic.activationCost" value="{{system.relic.activationCost}}" placeholder="e.g. 1 Mana" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.UsesPerDay"}}</label>
<input type="number" name="system.relic.usesPerDay" value="{{system.relic.usesPerDay}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
{{#if (gt system.relic.usesPerDay 0)}}
<div class="stat-group">
<label>{{localize "VAGABOND.UsesRemaining"}}</label>
<input type="number" name="system.relic.usesRemaining" value="{{system.relic.usesRemaining}}" min="0" max="{{system.relic.usesPerDay}}" {{#unless editable}}disabled{{/unless}} />
</div>
{{/if}}
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicLore"}}</label>
<textarea name="system.relic.lore" {{#unless editable}}disabled{{/unless}}>{{system.relic.lore}}</textarea>
</div>
</div>
{{/if}}
</fieldset>
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
</div>
</div>
</div>

View File

@ -0,0 +1,163 @@
{{!-- Class Item Sheet Body --}}
<div class="item-content class-content">
{{!-- Core Stats Row --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.KeyStat"}}</label>
<select name="system.keyStat" {{#unless editable}}disabled{{/unless}}>
<option value="might" {{#if (eq system.keyStat "might")}}selected{{/if}}>{{localize "VAGABOND.StatMight"}}</option>
<option value="dexterity" {{#if (eq system.keyStat "dexterity")}}selected{{/if}}>{{localize "VAGABOND.StatDexterity"}}</option>
<option value="awareness" {{#if (eq system.keyStat "awareness")}}selected{{/if}}>{{localize "VAGABOND.StatAwareness"}}</option>
<option value="reason" {{#if (eq system.keyStat "reason")}}selected{{/if}}>{{localize "VAGABOND.StatReason"}}</option>
<option value="presence" {{#if (eq system.keyStat "presence")}}selected{{/if}}>{{localize "VAGABOND.StatPresence"}}</option>
<option value="luck" {{#if (eq system.keyStat "luck")}}selected{{/if}}>{{localize "VAGABOND.StatLuck"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Zone"}}</label>
<select name="system.zone" {{#unless editable}}disabled{{/unless}}>
<option value="frontline" {{#if (eq system.zone "frontline")}}selected{{/if}}>{{localize "VAGABOND.ZoneFrontline"}}</option>
<option value="midline" {{#if (eq system.zone "midline")}}selected{{/if}}>{{localize "VAGABOND.ZoneMidline"}}</option>
<option value="backline" {{#if (eq system.zone "backline")}}selected{{/if}}>{{localize "VAGABOND.ZoneBackline"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.IsCaster"}}</label>
<input type="checkbox" name="system.isCaster" {{#if system.isCaster}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Casting Skill (if caster) --}}
{{#if system.isCaster}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.ActionStyle"}}</label>
<select name="system.actionStyle" {{#unless editable}}disabled{{/unless}}>
<option value="" {{#unless system.actionStyle}}selected{{/unless}}>-</option>
<option value="arcana" {{#if (eq system.actionStyle "arcana")}}selected{{/if}}>{{localize "VAGABOND.SkillArcana"}}</option>
<option value="mysticism" {{#if (eq system.actionStyle "mysticism")}}selected{{/if}}>{{localize "VAGABOND.SkillMysticism"}}</option>
<option value="influence" {{#if (eq system.actionStyle "influence")}}selected{{/if}}>{{localize "VAGABOND.SkillInfluence"}}</option>
<option value="leadership" {{#if (eq system.actionStyle "leadership")}}selected{{/if}}>{{localize "VAGABOND.SkillLeadership"}}</option>
<option value="performance" {{#if (eq system.actionStyle "performance")}}selected{{/if}}>{{localize "VAGABOND.SkillPerformance"}}</option>
</select>
</div>
</div>
{{/if}}
{{!-- Trained Skills --}}
<div class="trained-skills-section">
<label>{{localize "VAGABOND.TrainedSkills"}}</label>
<input type="text" name="system.trainedSkills" value="{{join system.trainedSkills ", "}}" placeholder="e.g. melee, brawl, detect" {{#unless editable}}disabled{{/unless}} />
<span class="hint">{{localize "VAGABOND.CommaSeparated"}}</span>
</div>
{{!-- Starting Pack --}}
<div class="starting-pack-section">
<label>{{localize "VAGABOND.StartingPack"}}</label>
<div class="editor-container">
{{editor system.startingPack target="system.startingPack" button=true editable=editable engine="prosemirror"}}
</div>
</div>
{{!-- Custom Resource --}}
<fieldset class="custom-resource-section">
<legend>{{localize "VAGABOND.CustomResource"}}</legend>
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.ResourceName"}}</label>
<input type="text" name="system.customResource.name" value="{{system.customResource.name}}" placeholder="e.g. Studied Dice" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.ResourceMax"}}</label>
<input type="text" name="system.customResource.max" value="{{system.customResource.max}}" placeholder="e.g. @level" {{#unless editable}}disabled{{/unless}} />
</div>
</div>
</fieldset>
{{!-- Progression Table --}}
<fieldset class="progression-section">
<legend>
{{localize "VAGABOND.Progression"}}
{{#if editable}}
<button type="button" class="add-progression" data-action="addArrayEntry" data-field="system.progression">
<i class="fas fa-plus"></i>
</button>
{{/if}}
</legend>
<div class="progression-table">
<div class="progression-header">
<span class="col-level">{{localize "VAGABOND.Level"}}</span>
<span class="col-mana">{{localize "VAGABOND.Mana"}}</span>
<span class="col-casting">{{localize "VAGABOND.CastingMax"}}</span>
<span class="col-spells">{{localize "VAGABOND.SpellsKnown"}}</span>
<span class="col-features">{{localize "VAGABOND.Features"}}</span>
{{#if editable}}<span class="col-actions"></span>{{/if}}
</div>
{{#each system.progression as |prog index|}}
<div class="progression-row" data-index="{{index}}">
<input type="number" class="col-level" name="system.progression.{{index}}.level" value="{{prog.level}}" min="1" max="10" {{#unless ../editable}}disabled{{/unless}} />
<input type="number" class="col-mana" name="system.progression.{{index}}.mana" value="{{prog.mana}}" min="0" {{#unless ../editable}}disabled{{/unless}} />
<input type="number" class="col-casting" name="system.progression.{{index}}.castingMax" value="{{prog.castingMax}}" min="0" {{#unless ../editable}}disabled{{/unless}} />
<input type="number" class="col-spells" name="system.progression.{{index}}.spellsKnown" value="{{prog.spellsKnown}}" min="0" {{#unless ../editable}}disabled{{/unless}} />
<input type="text" class="col-features" name="system.progression.{{index}}.features" value="{{join prog.features ", "}}" {{#unless ../editable}}disabled{{/unless}} />
{{#if ../editable}}
<button type="button" class="col-actions delete-progression" data-action="deleteArrayEntry" data-field="system.progression" data-index="{{index}}">
<i class="fas fa-trash"></i>
</button>
{{/if}}
</div>
{{else}}
<p class="no-progression">{{localize "VAGABOND.NoProgression"}}</p>
{{/each}}
</div>
</fieldset>
{{!-- Features --}}
<fieldset class="features-section">
<legend>
{{localize "VAGABOND.ClassFeatures"}}
{{#if editable}}
<button type="button" class="add-feature" data-action="addArrayEntry" data-field="system.features">
<i class="fas fa-plus"></i>
</button>
{{/if}}
</legend>
<div class="features-list">
{{#each system.features as |feature index|}}
<div class="feature-entry" data-index="{{index}}">
<div class="feature-header">
<input type="text" name="system.features.{{index}}.name" value="{{feature.name}}" placeholder="{{localize 'VAGABOND.FeatureName'}}" {{#unless ../editable}}disabled{{/unless}} />
<input type="number" name="system.features.{{index}}.level" value="{{feature.level}}" min="1" max="10" title="{{localize 'VAGABOND.Level'}}" {{#unless ../editable}}disabled{{/unless}} />
<label class="passive-toggle">
<input type="checkbox" name="system.features.{{index}}.passive" {{#if feature.passive}}checked{{/if}} {{#unless ../editable}}disabled{{/unless}} />
{{localize "VAGABOND.Passive"}}
</label>
{{#if ../editable}}
<button type="button" class="delete-feature" data-action="deleteArrayEntry" data-field="system.features" data-index="{{index}}">
<i class="fas fa-trash"></i>
</button>
{{/if}}
</div>
<div class="feature-description">
<textarea name="system.features.{{index}}.description" placeholder="{{localize 'VAGABOND.FeatureDescription'}}" {{#unless ../editable}}disabled{{/unless}}>{{feature.description}}</textarea>
</div>
</div>
{{else}}
<p class="no-features">{{localize "VAGABOND.NoFeatures"}}</p>
{{/each}}
</div>
</fieldset>
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
</div>
</div>
</div>

View File

@ -0,0 +1,169 @@
{{!-- Equipment Item Sheet Body --}}
<div class="item-content equipment-content">
{{!-- Core Stats Row --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.Quantity"}}</label>
<input type="number" name="system.quantity" value="{{system.quantity}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Slots"}}</label>
<input type="number" name="system.slots" value="{{system.slots}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.SlotsPerItem"}}</label>
<input type="checkbox" name="system.slotsPerItem" {{#if system.slotsPerItem}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Value"}}</label>
<input type="number" name="system.value" value="{{system.value}}" min="0" {{#unless editable}}disabled{{/unless}} />
<span class="units">c</span>
</div>
</div>
{{!-- Category and Flags --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.Category"}}</label>
<select name="system.category" {{#unless editable}}disabled{{/unless}}>
<option value="gear" {{#if (eq system.category "gear")}}selected{{/if}}>{{localize "VAGABOND.CategoryGear"}}</option>
<option value="tool" {{#if (eq system.category "tool")}}selected{{/if}}>{{localize "VAGABOND.CategoryTool"}}</option>
<option value="consumable" {{#if (eq system.category "consumable")}}selected{{/if}}>{{localize "VAGABOND.CategoryConsumable"}}</option>
<option value="container" {{#if (eq system.category "container")}}selected{{/if}}>{{localize "VAGABOND.CategoryContainer"}}</option>
<option value="treasure" {{#if (eq system.category "treasure")}}selected{{/if}}>{{localize "VAGABOND.CategoryTreasure"}}</option>
<option value="misc" {{#if (eq system.category "misc")}}selected{{/if}}>{{localize "VAGABOND.CategoryMisc"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Equipped"}}</label>
<input type="checkbox" name="system.equipped" {{#if system.equipped}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Consumable"}}</label>
<input type="checkbox" name="system.consumable" {{#if system.consumable}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Consumable Uses (if consumable) --}}
{{#if system.consumable}}
<fieldset class="consumable-uses">
<legend>{{localize "VAGABOND.Uses"}}</legend>
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.UsesCurrent"}}</label>
<input type="number" name="system.uses.value" value="{{system.uses.value}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.UsesMax"}}</label>
<input type="number" name="system.uses.max" value="{{system.uses.max}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.AutoDestroy"}}</label>
<input type="checkbox" name="system.uses.autoDestroy" {{#if system.uses.autoDestroy}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
</div>
</fieldset>
{{/if}}
{{!-- Container Capacity (if container) --}}
{{#if (eq system.category "container")}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.ContainerCapacity"}}</label>
<input type="number" name="system.containerCapacity" value="{{system.containerCapacity}}" min="0" {{#unless editable}}disabled{{/unless}} />
<span class="units">{{localize "VAGABOND.Slots"}}</span>
</div>
</div>
{{/if}}
{{!-- Magic Properties --}}
<fieldset class="item-properties">
<legend>{{localize "VAGABOND.MagicProperties"}}</legend>
<div class="properties-grid">
<label class="property-checkbox">
<input type="checkbox" name="system.isTrinket" {{#if system.isTrinket}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.IsTrinket"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.canCastThrough" {{#if system.canCastThrough}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.CanCastThrough"}}
</label>
</div>
</fieldset>
{{!-- Relic Section (collapsible) --}}
<fieldset class="relic-section {{#unless system.relic.isRelic}}collapsed{{/unless}}">
<legend>
<label class="relic-toggle">
<input type="checkbox" name="system.relic.isRelic" {{#if system.relic.isRelic}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.Relic"}}
</label>
</legend>
{{#if system.relic.isRelic}}
<div class="relic-fields">
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.RelicTier"}}</label>
<input type="number" name="system.relic.tier" value="{{system.relic.tier}}" min="1" max="5" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.RequiresAttunement"}}</label>
<input type="checkbox" name="system.relic.requiresAttunement" {{#if system.relic.requiresAttunement}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
{{#if system.relic.requiresAttunement}}
<div class="stat-group">
<label>{{localize "VAGABOND.Attuned"}}</label>
<input type="checkbox" name="system.relic.attuned" {{#if system.relic.attuned}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
{{/if}}
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicAbilityName"}}</label>
<input type="text" name="system.relic.abilityName" value="{{system.relic.abilityName}}" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicAbilityDescription"}}</label>
<textarea name="system.relic.abilityDescription" {{#unless editable}}disabled{{/unless}}>{{system.relic.abilityDescription}}</textarea>
</div>
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.ActivationCost"}}</label>
<input type="text" name="system.relic.activationCost" value="{{system.relic.activationCost}}" placeholder="e.g. 1 Mana" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.UsesPerDay"}}</label>
<input type="number" name="system.relic.usesPerDay" value="{{system.relic.usesPerDay}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
{{#if (gt system.relic.usesPerDay 0)}}
<div class="stat-group">
<label>{{localize "VAGABOND.UsesRemaining"}}</label>
<input type="number" name="system.relic.usesRemaining" value="{{system.relic.usesRemaining}}" min="0" max="{{system.relic.usesPerDay}}" {{#unless editable}}disabled{{/unless}} />
</div>
{{/if}}
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicLore"}}</label>
<textarea name="system.relic.lore" {{#unless editable}}disabled{{/unless}}>{{system.relic.lore}}</textarea>
</div>
</div>
{{/if}}
</fieldset>
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
</div>
</div>
</div>

View File

@ -0,0 +1,83 @@
{{!-- Feature Item Sheet Body --}}
<div class="item-content feature-content">
{{!-- Core Stats Row --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.SourceClass"}}</label>
<input type="text" name="system.sourceClass" value="{{system.sourceClass}}" placeholder="e.g. Fighter" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Level"}}</label>
<input type="number" name="system.level" value="{{system.level}}" min="1" max="10" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Passive"}}</label>
<input type="checkbox" name="system.passive" {{#if system.passive}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Activation (for active features) --}}
{{#unless system.passive}}
<fieldset class="activation-section">
<legend>{{localize "VAGABOND.Activation"}}</legend>
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.ActivationType"}}</label>
<select name="system.activation.type" {{#unless editable}}disabled{{/unless}}>
<option value="" {{#unless system.activation.type}}selected{{/unless}}>-</option>
<option value="action" {{#if (eq system.activation.type "action")}}selected{{/if}}>{{localize "VAGABOND.Action"}}</option>
<option value="bonus" {{#if (eq system.activation.type "bonus")}}selected{{/if}}>{{localize "VAGABOND.BonusAction"}}</option>
<option value="reaction" {{#if (eq system.activation.type "reaction")}}selected{{/if}}>{{localize "VAGABOND.Reaction"}}</option>
<option value="free" {{#if (eq system.activation.type "free")}}selected{{/if}}>{{localize "VAGABOND.FreeAction"}}</option>
<option value="special" {{#if (eq system.activation.type "special")}}selected{{/if}}>{{localize "VAGABOND.Special"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.ActivationCost"}}</label>
<input type="text" name="system.activation.cost" value="{{system.activation.cost}}" placeholder="e.g. 1 Mana" {{#unless editable}}disabled{{/unless}} />
</div>
</div>
</fieldset>
{{/unless}}
{{!-- Uses (if limited) --}}
<fieldset class="uses-section">
<legend>{{localize "VAGABOND.Uses"}}</legend>
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.UsesCurrent"}}</label>
<input type="number" name="system.uses.value" value="{{system.uses.value}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.UsesMax"}}</label>
<input type="number" name="system.uses.max" value="{{system.uses.max}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.RecoveryPer"}}</label>
<select name="system.uses.per" {{#unless editable}}disabled{{/unless}}>
<option value="" {{#unless system.uses.per}}selected{{/unless}}>{{localize "VAGABOND.Unlimited"}}</option>
<option value="short" {{#if (eq system.uses.per "short")}}selected{{/if}}>{{localize "VAGABOND.ShortRest"}}</option>
<option value="long" {{#if (eq system.uses.per "long")}}selected{{/if}}>{{localize "VAGABOND.LongRest"}}</option>
<option value="day" {{#if (eq system.uses.per "day")}}selected{{/if}}>{{localize "VAGABOND.PerDay"}}</option>
<option value="encounter" {{#if (eq system.uses.per "encounter")}}selected{{/if}}>{{localize "VAGABOND.PerEncounter"}}</option>
</select>
</div>
</div>
</fieldset>
{{!-- Requirements --}}
<div class="requirements-section">
<label>{{localize "VAGABOND.Requirements"}}</label>
<input type="text" name="system.requirements" value="{{system.requirements}}" placeholder="e.g. Chose Path of Rage" {{#unless editable}}disabled{{/unless}} />
</div>
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
</div>
</div>
</div>

View File

@ -0,0 +1,140 @@
{{!-- Perk Item Sheet Body --}}
<div class="item-content perk-content">
{{!-- Prerequisites Section --}}
<fieldset class="prerequisites-section">
<legend>{{localize "VAGABOND.Prerequisites"}}</legend>
{{!-- Stat Requirements --}}
<div class="prereq-stats">
<label class="prereq-label">{{localize "VAGABOND.StatRequirements"}}</label>
<div class="stat-prereq-grid">
<div class="stat-prereq">
<label>MIT</label>
<input type="number" name="system.prerequisites.stats.might" value="{{system.prerequisites.stats.might}}" min="0" max="7" placeholder="-" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-prereq">
<label>DEX</label>
<input type="number" name="system.prerequisites.stats.dexterity" value="{{system.prerequisites.stats.dexterity}}" min="0" max="7" placeholder="-" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-prereq">
<label>AWR</label>
<input type="number" name="system.prerequisites.stats.awareness" value="{{system.prerequisites.stats.awareness}}" min="0" max="7" placeholder="-" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-prereq">
<label>RSN</label>
<input type="number" name="system.prerequisites.stats.reason" value="{{system.prerequisites.stats.reason}}" min="0" max="7" placeholder="-" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-prereq">
<label>PRS</label>
<input type="number" name="system.prerequisites.stats.presence" value="{{system.prerequisites.stats.presence}}" min="0" max="7" placeholder="-" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-prereq">
<label>LUK</label>
<input type="number" name="system.prerequisites.stats.luck" value="{{system.prerequisites.stats.luck}}" min="0" max="7" placeholder="-" {{#unless editable}}disabled{{/unless}} />
</div>
</div>
</div>
{{!-- Training Requirements --}}
<div class="prereq-training">
<label>{{localize "VAGABOND.TrainingRequirements"}}</label>
<input type="text" name="system.prerequisites.trainedSkills" value="{{join system.prerequisites.trainedSkills ", "}}" placeholder="e.g. melee, arcana" {{#unless editable}}disabled{{/unless}} />
<span class="hint">{{localize "VAGABOND.CommaSeparated"}}</span>
</div>
{{!-- Spell Requirements --}}
<div class="prereq-spells">
<label>{{localize "VAGABOND.SpellRequirements"}}</label>
<input type="text" name="system.prerequisites.spells" value="{{join system.prerequisites.spells ", "}}" placeholder="e.g. Light, Charm" {{#unless editable}}disabled{{/unless}} />
<span class="hint">{{localize "VAGABOND.CommaSeparated"}}</span>
</div>
{{!-- Perk Requirements --}}
<div class="prereq-perks">
<label>{{localize "VAGABOND.PerkRequirements"}}</label>
<input type="text" name="system.prerequisites.perks" value="{{join system.prerequisites.perks ", "}}" placeholder="e.g. Tough, Familiar" {{#unless editable}}disabled{{/unless}} />
<span class="hint">{{localize "VAGABOND.CommaSeparated"}}</span>
</div>
{{!-- Custom Requirements --}}
<div class="prereq-custom">
<label>{{localize "VAGABOND.CustomRequirements"}}</label>
<input type="text" name="system.prerequisites.custom" value="{{system.prerequisites.custom}}" placeholder="e.g. Maximum Mana 7+" {{#unless editable}}disabled{{/unless}} />
</div>
</fieldset>
{{!-- Usage & Activation --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.Passive"}}</label>
<input type="checkbox" name="system.passive" {{#if system.passive}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.IsRitual"}}</label>
<input type="checkbox" name="system.isRitual" {{#if system.isRitual}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
{{#if system.isRitual}}
<div class="stat-group">
<label>{{localize "VAGABOND.RitualDuration"}}</label>
<input type="number" name="system.ritualDuration" value="{{system.ritualDuration}}" min="0" {{#unless editable}}disabled{{/unless}} />
<span class="units">min</span>
</div>
{{/if}}
</div>
{{!-- Uses (if not passive) --}}
{{#unless system.passive}}
<fieldset class="uses-section">
<legend>{{localize "VAGABOND.Uses"}}</legend>
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.UsesCurrent"}}</label>
<input type="number" name="system.uses.value" value="{{system.uses.value}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.UsesMax"}}</label>
<input type="number" name="system.uses.max" value="{{system.uses.max}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.RecoveryPer"}}</label>
<select name="system.uses.per" {{#unless editable}}disabled{{/unless}}>
<option value="" {{#unless system.uses.per}}selected{{/unless}}>-</option>
<option value="short" {{#if (eq system.uses.per "short")}}selected{{/if}}>{{localize "VAGABOND.ShortRest"}}</option>
<option value="long" {{#if (eq system.uses.per "long")}}selected{{/if}}>{{localize "VAGABOND.LongRest"}}</option>
<option value="day" {{#if (eq system.uses.per "day")}}selected{{/if}}>{{localize "VAGABOND.PerDay"}}</option>
</select>
</div>
</div>
</fieldset>
{{/unless}}
{{!-- Luck Integration --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.LuckCost"}}</label>
<input type="number" name="system.luckCost" value="{{system.luckCost}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.GrantsLuck"}}</label>
<input type="number" name="system.grantsLuck" value="{{system.grantsLuck}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Ritual Components --}}
{{#if system.isRitual}}
<div class="ritual-components">
<label>{{localize "VAGABOND.RitualComponents"}}</label>
<input type="text" name="system.ritualComponents" value="{{system.ritualComponents}}" placeholder="e.g. a corpse, herbs" {{#unless editable}}disabled{{/unless}} />
</div>
{{/if}}
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
</div>
</div>
</div>

View File

@ -0,0 +1,134 @@
{{!-- Spell Item Sheet Body --}}
<div class="item-content spell-content">
{{!-- Damage Row --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.DamageType"}}</label>
<select name="system.damageType" {{#unless editable}}disabled{{/unless}}>
<option value="" {{#unless system.damageType}}selected{{/unless}}>{{localize "VAGABOND.DamageTypeNone"}}</option>
<option value="fire" {{#if (eq system.damageType "fire")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeFire"}}</option>
<option value="cold" {{#if (eq system.damageType "cold")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeCold"}}</option>
<option value="shock" {{#if (eq system.damageType "shock")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeShock"}}</option>
<option value="acid" {{#if (eq system.damageType "acid")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeAcid"}}</option>
<option value="poison" {{#if (eq system.damageType "poison")}}selected{{/if}}>{{localize "VAGABOND.DamageTypePoison"}}</option>
<option value="blunt" {{#if (eq system.damageType "blunt")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeBlunt"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.DamageBase"}}</label>
<input type="text" name="system.damageBase" value="{{system.damageBase}}" placeholder="d6" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.MaxDice"}}</label>
<input type="number" name="system.maxDice" value="{{system.maxDice}}" min="0" placeholder="0 = no limit" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.CastingSkill"}}</label>
<select name="system.castingSkill" {{#unless editable}}disabled{{/unless}}>
<option value="" {{#unless system.castingSkill}}selected{{/unless}}>{{localize "VAGABOND.UseClassDefault"}}</option>
<option value="arcana" {{#if (eq system.castingSkill "arcana")}}selected{{/if}}>{{localize "VAGABOND.SkillArcana"}}</option>
<option value="mysticism" {{#if (eq system.castingSkill "mysticism")}}selected{{/if}}>{{localize "VAGABOND.SkillMysticism"}}</option>
<option value="influence" {{#if (eq system.castingSkill "influence")}}selected{{/if}}>{{localize "VAGABOND.SkillInfluence"}}</option>
<option value="leadership" {{#if (eq system.castingSkill "leadership")}}selected{{/if}}>{{localize "VAGABOND.SkillLeadership"}}</option>
<option value="performance" {{#if (eq system.castingSkill "performance")}}selected{{/if}}>{{localize "VAGABOND.SkillPerformance"}}</option>
</select>
</div>
</div>
{{!-- Delivery Types --}}
<fieldset class="delivery-types">
<legend>{{localize "VAGABOND.DeliveryTypes"}}</legend>
<div class="properties-grid">
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.touch" {{#if system.deliveryTypes.touch}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliveryTouch"}} (0)
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.remote" {{#if system.deliveryTypes.remote}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliveryRemote"}} (0)
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.imbue" {{#if system.deliveryTypes.imbue}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliveryImbue"}} (0)
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.cube" {{#if system.deliveryTypes.cube}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliveryCube"}} (1)
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.aura" {{#if system.deliveryTypes.aura}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliveryAura"}} (2)
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.cone" {{#if system.deliveryTypes.cone}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliveryCone"}} (2)
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.glyph" {{#if system.deliveryTypes.glyph}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliveryGlyph"}} (2)
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.line" {{#if system.deliveryTypes.line}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliveryLine"}} (2)
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.deliveryTypes.sphere" {{#if system.deliveryTypes.sphere}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DeliverySphere"}} (2)
</label>
</div>
</fieldset>
{{!-- Duration Types --}}
<fieldset class="duration-types">
<legend>{{localize "VAGABOND.DurationTypes"}}</legend>
<div class="properties-grid">
<label class="property-checkbox">
<input type="checkbox" name="system.durationTypes.instant" {{#if system.durationTypes.instant}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DurationInstant"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.durationTypes.focus" {{#if system.durationTypes.focus}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DurationFocus"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.durationTypes.continual" {{#if system.durationTypes.continual}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.DurationContinual"}}
</label>
</div>
</fieldset>
{{!-- Focus Tracking --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.CurrentlyFocusing"}}</label>
<input type="checkbox" name="system.focusing" {{#if system.focusing}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Effect --}}
<div class="spell-effect">
<label>{{localize "VAGABOND.Effect"}}</label>
<div class="editor-container">
{{editor system.effect target="system.effect" button=true editable=editable engine="prosemirror"}}
</div>
</div>
{{!-- Crit Effect --}}
<div class="spell-crit-effect">
<label>{{localize "VAGABOND.CritEffect"}}</label>
<div class="editor-container">
{{editor system.critEffect target="system.critEffect" button=true editable=editable engine="prosemirror"}}
</div>
</div>
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
</div>
</div>
</div>

View File

@ -0,0 +1,235 @@
{{!-- Weapon Item Sheet Body --}}
<div class="item-content weapon-content">
{{!-- Core Stats Row --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.Damage"}}</label>
<input type="text" name="system.damage" value="{{system.damage}}" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.DamageType"}}</label>
<select name="system.damageType" {{#unless editable}}disabled{{/unless}}>
<option value="blunt" {{#if (eq system.damageType "blunt")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeBlunt"}}</option>
<option value="piercing" {{#if (eq system.damageType "piercing")}}selected{{/if}}>{{localize "VAGABOND.DamageTypePiercing"}}</option>
<option value="slashing" {{#if (eq system.damageType "slashing")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeSlashing"}}</option>
<option value="fire" {{#if (eq system.damageType "fire")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeFire"}}</option>
<option value="cold" {{#if (eq system.damageType "cold")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeCold"}}</option>
<option value="shock" {{#if (eq system.damageType "shock")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeShock"}}</option>
<option value="acid" {{#if (eq system.damageType "acid")}}selected{{/if}}>{{localize "VAGABOND.DamageTypeAcid"}}</option>
<option value="poison" {{#if (eq system.damageType "poison")}}selected{{/if}}>{{localize "VAGABOND.DamageTypePoison"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Grip"}}</label>
<select name="system.grip" {{#unless editable}}disabled{{/unless}}>
<option value="1h" {{#if (eq system.grip "1h")}}selected{{/if}}>{{localize "VAGABOND.Grip1H"}}</option>
<option value="2h" {{#if (eq system.grip "2h")}}selected{{/if}}>{{localize "VAGABOND.Grip2H"}}</option>
<option value="versatile" {{#if (eq system.grip "versatile")}}selected{{/if}}>{{localize "VAGABOND.GripVersatile"}}</option>
<option value="fist" {{#if (eq system.grip "fist")}}selected{{/if}}>{{localize "VAGABOND.GripFist"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.AttackSkill"}}</label>
<select name="system.attackType" {{#unless editable}}disabled{{/unless}}>
<option value="melee" {{#if (eq system.attackType "melee")}}selected{{/if}}>{{localize "VAGABOND.SkillMelee"}}</option>
<option value="brawl" {{#if (eq system.attackType "brawl")}}selected{{/if}}>{{localize "VAGABOND.SkillBrawl"}}</option>
<option value="ranged" {{#if (eq system.attackType "ranged")}}selected{{/if}}>{{localize "VAGABOND.SkillRanged"}}</option>
<option value="finesse" {{#if (eq system.attackType "finesse")}}selected{{/if}}>{{localize "VAGABOND.SkillFinesse"}}</option>
</select>
</div>
</div>
{{!-- Secondary Stats Row --}}
<div class="item-stats-row">
{{#if (eq system.grip "versatile")}}
<div class="stat-group">
<label>{{localize "VAGABOND.VersatileDamage"}}</label>
<input type="text" name="system.versatileDamage" value="{{system.versatileDamage}}" placeholder="e.g. 1d8" {{#unless editable}}disabled{{/unless}} />
</div>
{{/if}}
<div class="stat-group">
<label>{{localize "VAGABOND.BonusDamage"}}</label>
<input type="number" name="system.bonusDamage" value="{{system.bonusDamage}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.AttackBonus"}}</label>
<input type="number" name="system.attackBonus" value="{{system.attackBonus}}" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.CritThreshold"}}</label>
<input type="number" name="system.critThreshold" value="{{system.critThreshold}}" min="1" max="20" placeholder="20" {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Range (for ranged/thrown) --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.Range"}}</label>
<div class="range-input">
<input type="number" name="system.range.value" value="{{system.range.value}}" min="0" {{#unless editable}}disabled{{/unless}} />
<span class="units">{{system.range.units}}</span>
</div>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Slots"}}</label>
<input type="number" name="system.slots" value="{{system.slots}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Value"}}</label>
<input type="number" name="system.value" value="{{system.value}}" min="0" {{#unless editable}}disabled{{/unless}} />
<span class="units">c</span>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Quantity"}}</label>
<input type="number" name="system.quantity" value="{{system.quantity}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
</div>
{{!-- Properties --}}
<fieldset class="item-properties">
<legend>{{localize "VAGABOND.WeaponProperties"}}</legend>
<div class="properties-grid">
<label class="property-checkbox">
<input type="checkbox" name="system.properties.finesse" {{#if system.properties.finesse}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PropertyFinesse"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.properties.thrown" {{#if system.properties.thrown}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PropertyThrown"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.properties.cleave" {{#if system.properties.cleave}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PropertyCleave"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.properties.reach" {{#if system.properties.reach}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PropertyReach"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.properties.loading" {{#if system.properties.loading}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PropertyLoading"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.properties.brawl" {{#if system.properties.brawl}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PropertyBrawl"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.properties.crude" {{#if system.properties.crude}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PropertyCrude"}}
</label>
<label class="property-checkbox">
<input type="checkbox" name="system.properties.versatile" {{#if system.properties.versatile}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.PropertyVersatile"}}
</label>
</div>
</fieldset>
{{!-- Material --}}
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.Material"}}</label>
<select name="system.material" {{#unless editable}}disabled{{/unless}}>
<option value="mundane" {{#if (eq system.material "mundane")}}selected{{/if}}>{{localize "VAGABOND.MaterialMundane"}}</option>
<option value="silvered" {{#if (eq system.material "silvered")}}selected{{/if}}>{{localize "VAGABOND.MaterialSilvered"}}</option>
<option value="adamantine" {{#if (eq system.material "adamantine")}}selected{{/if}}>{{localize "VAGABOND.MaterialAdamantine"}}</option>
<option value="magical" {{#if (eq system.material "magical")}}selected{{/if}}>{{localize "VAGABOND.MaterialMagical"}}</option>
</select>
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.Equipped"}}</label>
<input type="checkbox" name="system.equipped" {{#if system.equipped}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
{{#if system.equipped}}
<div class="stat-group">
<label>{{localize "VAGABOND.EquippedHand"}}</label>
<select name="system.equippedHand" {{#unless editable}}disabled{{/unless}}>
<option value="main" {{#if (eq system.equippedHand "main")}}selected{{/if}}>{{localize "VAGABOND.HandMain"}}</option>
<option value="off" {{#if (eq system.equippedHand "off")}}selected{{/if}}>{{localize "VAGABOND.HandOff"}}</option>
<option value="both" {{#if (eq system.equippedHand "both")}}selected{{/if}}>{{localize "VAGABOND.HandBoth"}}</option>
</select>
</div>
{{/if}}
</div>
{{!-- Relic Section (collapsible) --}}
<fieldset class="relic-section {{#unless system.relic.isRelic}}collapsed{{/unless}}">
<legend>
<label class="relic-toggle">
<input type="checkbox" name="system.relic.isRelic" {{#if system.relic.isRelic}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
{{localize "VAGABOND.Relic"}}
</label>
</legend>
{{#if system.relic.isRelic}}
<div class="relic-fields">
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.RelicTier"}}</label>
<input type="number" name="system.relic.tier" value="{{system.relic.tier}}" min="1" max="5" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.RequiresAttunement"}}</label>
<input type="checkbox" name="system.relic.requiresAttunement" {{#if system.relic.requiresAttunement}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
{{#if system.relic.requiresAttunement}}
<div class="stat-group">
<label>{{localize "VAGABOND.Attuned"}}</label>
<input type="checkbox" name="system.relic.attuned" {{#if system.relic.attuned}}checked{{/if}} {{#unless editable}}disabled{{/unless}} />
</div>
{{/if}}
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicAbilityName"}}</label>
<input type="text" name="system.relic.abilityName" value="{{system.relic.abilityName}}" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicAbilityDescription"}}</label>
<textarea name="system.relic.abilityDescription" {{#unless editable}}disabled{{/unless}}>{{system.relic.abilityDescription}}</textarea>
</div>
<div class="item-stats-row">
<div class="stat-group">
<label>{{localize "VAGABOND.ActivationCost"}}</label>
<input type="text" name="system.relic.activationCost" value="{{system.relic.activationCost}}" placeholder="e.g. 1 Mana" {{#unless editable}}disabled{{/unless}} />
</div>
<div class="stat-group">
<label>{{localize "VAGABOND.UsesPerDay"}}</label>
<input type="number" name="system.relic.usesPerDay" value="{{system.relic.usesPerDay}}" min="0" {{#unless editable}}disabled{{/unless}} />
</div>
{{#if (gt system.relic.usesPerDay 0)}}
<div class="stat-group">
<label>{{localize "VAGABOND.UsesRemaining"}}</label>
<input type="number" name="system.relic.usesRemaining" value="{{system.relic.usesRemaining}}" min="0" max="{{system.relic.usesPerDay}}" {{#unless editable}}disabled{{/unless}} />
</div>
{{/if}}
</div>
<div class="stat-group full-width">
<label>{{localize "VAGABOND.RelicLore"}}</label>
<textarea name="system.relic.lore" {{#unless editable}}disabled{{/unless}}>{{system.relic.lore}}</textarea>
</div>
</div>
{{/if}}
</fieldset>
{{!-- Description --}}
<div class="item-description">
<label>{{localize "VAGABOND.Description"}}</label>
<div class="editor-container">
{{editor system.description target="system.description" button=true editable=editable engine="prosemirror"}}
</div>
</div>
</div>