From 37300ccf906e3538c51a04c7ce301b803eeb65d8 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Fri, 12 Dec 2025 14:32:15 -0600 Subject: [PATCH] Initial project setup for Vagabond RPG Foundry VTT system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - System manifest (system.json) for Foundry v13 - Project structure with module/, templates/, styles/, lang/, packs/ - Docker Compose for local Foundry development environment - SCSS architecture with parchment theme and accessibility colors - Base system entry point with CONFIG and Handlebars helpers - English localization file with all game terms - Project roadmap with 98 tasks across 11 phases Phase 0 (Foundation) complete. Ready for Phase 1 (Data Models). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .env.example | 12 + .gitignore | 34 + CLAUDE.md | 86 ++ LICENSE | 28 + PROJECT_ROADMAP.json | 1034 +++++++++++++++++++++++++ README.md | 110 +++ docker-compose.yml | 28 + lang/en.json | 213 +++++ module/helpers/config.mjs | 200 +++++ module/vagabond.mjs | 139 ++++ package.json | 26 + styles/scss/_base.scss | 156 ++++ styles/scss/_mixins.scss | 200 +++++ styles/scss/_variables.scss | 102 +++ styles/scss/chat/_chat-cards.scss | 153 ++++ styles/scss/components/_buttons.scss | 108 +++ styles/scss/components/_forms.scss | 181 +++++ styles/scss/components/_tabs.scss | 111 +++ styles/scss/dialogs/_roll-dialog.scss | 154 ++++ styles/scss/sheets/_actor-sheet.scss | 83 ++ styles/scss/sheets/_item-sheet.scss | 101 +++ styles/scss/vagabond.scss | 24 + system.json | 127 +++ 23 files changed, 3410 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 LICENSE create mode 100644 PROJECT_ROADMAP.json create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 lang/en.json create mode 100644 module/helpers/config.mjs create mode 100644 module/vagabond.mjs create mode 100644 package.json create mode 100644 styles/scss/_base.scss create mode 100644 styles/scss/_mixins.scss create mode 100644 styles/scss/_variables.scss create mode 100644 styles/scss/chat/_chat-cards.scss create mode 100644 styles/scss/components/_buttons.scss create mode 100644 styles/scss/components/_forms.scss create mode 100644 styles/scss/components/_tabs.scss create mode 100644 styles/scss/dialogs/_roll-dialog.scss create mode 100644 styles/scss/sheets/_actor-sheet.scss create mode 100644 styles/scss/sheets/_item-sheet.scss create mode 100644 styles/scss/vagabond.scss create mode 100644 system.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6f2edd9 --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# Foundry VTT Credentials +# Copy this file to .env and fill in your values + +# Your Foundry VTT account credentials (for downloading Foundry) +FOUNDRY_USERNAME=your-foundry-username +FOUNDRY_PASSWORD=your-foundry-password + +# Your Foundry license key +FOUNDRY_LICENSE_KEY=your-license-key + +# Admin password for the Foundry instance +FOUNDRY_ADMIN_KEY=vagabond-dev-admin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8778f6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Dependencies +node_modules/ + +# Foundry data (dev environment) +foundrydata/ + +# Build artifacts +styles/*.css +styles/*.css.map + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Environment +.env +.env.local +*.local + +# Logs +*.log +npm-debug.log* + +# Secrets +*.pem +*.key +credentials.json diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..972bb05 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,86 @@ +# Vagabond RPG Foundry VTT System - Development Context + +## Project Overview +This is a complete Foundry VTT v13 system implementation for Vagabond RPG (Pulp Fantasy TTRPG). + +## Key Architecture Decisions + +### Data Models (Foundry v13 style) +- Use TypeDataModel classes in `module/data/` for Actor and Item schemas +- Character stats: Might, Dexterity, Awareness, Reason, Presence, Luck (range 2-7) +- Derived values calculated in `prepareData()`: HP, Speed, Save difficulties, Skill difficulties + +### Roll System +- Base formula: d20 >= (20 - Stat) for untrained, d20 >= (20 - Stat*2) for trained +- Favor: +d6 bonus die +- Hinder: -d6 penalty die +- Crit: Natural 20 by default, but threshold can be modified per-skill by Active Effects +- Exploding dice: d6! notation for certain abilities + +### Spell Casting +- Dynamic mana cost = base + delivery cost + duration cost + extra damage dice +- Delivery types: Touch(0), Remote(0), Imbue(0), Cube(1), Aura(2), Cone(2), Glyph(2), Line(2), Sphere(2) +- Duration: Instant (free), Focus (ongoing), Continual (permanent) +- Cast dialog must calculate and display total mana cost before casting + +### Class System +- Classes are Items with progression tables +- When dragged to character, creates Active Effects for current level +- On level up, update Active Effects to grant new features +- Supports future multiclassing by allowing multiple class items + +### Crit Threshold System +- Each skill/action has a `critThreshold` field (default 20) +- Active Effects from classes/perks can modify: `system.skills.melee.critThreshold` +- Fighter's Valor reduces crit by 1/2/3 at levels 1/4/8 +- Gunslinger's Deadeye dynamically reduces on consecutive hits + +### Resources +- HP: max = Might * Level +- Mana: class-dependent, max from class progression +- Luck: equals Luck stat, refreshes on rest +- Fatigue: 0-5, death at 5, each reduces item slots by 1 +- Studied Dice: some classes grant these +- Custom resources can be added dynamically + +## File Naming Conventions +- Main system entry: `vagabond.mjs` +- Document classes: `VagabondActor.mjs`, `VagabondItem.mjs` +- Sheet classes: `VagabondCharacterSheet.mjs`, `VagabondNPCSheet.mjs` +- Data models: `CharacterData.mjs`, `NPCData.mjs`, `SpellData.mjs`, etc. +- Templates: `character-sheet.hbs`, `npc-sheet.hbs`, `spell-item.hbs` + +## Testing Commands +```bash +# Start local Foundry +docker compose up -d + +# Watch SCSS +npm run watch + +# View logs +docker compose logs -f foundry +``` + +## Reference Data Location +Game rules and content are documented in NoteDiscovery under `gaming/vagabond-rpg/`: +- `core-mechanics.md` - Stats, checks, dice, HP +- `combat.md` - Actions, movement, defending, zones +- `character-creation.md` - Ancestries, classes, leveling +- `magic-system.md` - Casting, mana, delivery, duration +- `spells-full-text.md` - All 55+ spells with full descriptions +- `perks-full-list.md` - All 90+ perks with prerequisites +- `classes-full-text.md` - All 18 classes with progression tables +- `bestiary.md` - Creature categories, TL reference + +Original PDF at: `/mnt/NV2/Development/claude-home/gaming/Vagabond_RPG_-_Pulp_Fantasy_Core_Rulebook_Interactive_PDF.pdf` +Character sheet reference: `/mnt/NV2/Development/claude-home/gaming/Vagabond_-_Hero_Record_Interactive_PDF.pdf` + +## Project Roadmap +See `PROJECT_ROADMAP.json` for complete task breakdown with dependencies. + +## Style Guidelines +- Parchment color scheme with high contrast (WCAG AA compliant) +- Match official Hero Record layout where possible +- Use CSS custom properties for theming +- SCSS with BEM naming convention diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cdd9ec8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +MIT License + +Copyright (c) 2024 Cal Corum + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +Vagabond RPG is a product of its respective copyright holders. This system +implementation is a fan project and is not affiliated with or endorsed by +the original game creators. Game rules and content are used under fair use +for the purpose of creating a virtual tabletop implementation. diff --git a/PROJECT_ROADMAP.json b/PROJECT_ROADMAP.json new file mode 100644 index 0000000..f5dd472 --- /dev/null +++ b/PROJECT_ROADMAP.json @@ -0,0 +1,1034 @@ +{ + "project": { + "name": "Vagabond RPG - Foundry VTT System", + "description": "Complete Foundry VTT v13 system implementation for Vagabond RPG (Pulp Fantasy TTRPG)", + "repository": "vagabond-rpg-foundryvtt", + "license": "MIT", + "foundry_version": "13", + "created": "2024-12-12", + "status": "in_progress" + }, + "phases": [ + { + "id": "phase-0", + "name": "Project Foundation", + "description": "Initial setup, tooling, and development environment", + "tasks": [ + { + "id": "0.1", + "name": "Create system manifest (system.json)", + "description": "Define system metadata, compatibility, dependencies, and entry points for Foundry VTT", + "completed": true, + "tested": false, + "priority": "critical", + "dependencies": [] + }, + { + "id": "0.2", + "name": "Create project structure", + "description": "Set up directory structure: module/, templates/, styles/, lang/, packs/, assets/", + "completed": true, + "tested": false, + "priority": "critical", + "dependencies": [] + }, + { + "id": "0.3", + "name": "Set up local Foundry dev container", + "description": "Docker Compose for local Foundry instance with hot-reload system mounting", + "completed": true, + "tested": false, + "priority": "critical", + "dependencies": [] + }, + { + "id": "0.4", + "name": "Create README.md", + "description": "Project documentation with setup instructions, development guide, and contribution guidelines", + "completed": true, + "tested": false, + "priority": "high", + "dependencies": [] + }, + { + "id": "0.5", + "name": "Create .gitignore", + "description": "Ignore node_modules, foundry data, secrets, IDE files", + "completed": true, + "tested": false, + "priority": "high", + "dependencies": [] + }, + { + "id": "0.6", + "name": "Set up SCSS compilation", + "description": "Build tooling for SCSS to CSS compilation with watch mode", + "completed": true, + "tested": false, + "priority": "high", + "dependencies": ["0.2"] + }, + { + "id": "0.7", + "name": "Create LICENSE file", + "description": "MIT license file", + "completed": true, + "tested": false, + "priority": "medium", + "dependencies": [] + }, + { + "id": "0.8", + "name": "Create CLAUDE.md", + "description": "AI context file for development assistance", + "completed": true, + "tested": false, + "priority": "medium", + "dependencies": [] + } + ] + }, + { + "id": "phase-1", + "name": "Data Models", + "description": "Define all Actor and Item data structures using Foundry's Data Model system", + "tasks": [ + { + "id": "1.1", + "name": "Create base Actor data model", + "description": "Shared fields for all actors: name, img, type, system data container", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["0.1", "0.2"] + }, + { + "id": "1.2", + "name": "Create Character (PC) data model", + "description": "Stats (MIT/DEX/AWR/RSN/PRS/LUK), derived values (HP, Speed, Saves, Skill difficulties), level, XP, ancestry, class reference", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["1.1"] + }, + { + "id": "1.3", + "name": "Create NPC/Monster data model", + "description": "HD, HP, TL, Zone, Morale, Appearing, Armor, Immune, Weak, Actions array, Abilities array", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["1.1"] + }, + { + "id": "1.4", + "name": "Create dynamic resources system", + "description": "Extensible resource tracker: HP, Mana, Luck, Fatigue, Studied Dice, custom resources with current/max/bonus fields", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["1.2"] + }, + { + "id": "1.5", + "name": "Create Skills data structure", + "description": "12 skills with associated stat, trained boolean, difficulty calculation, custom crit threshold", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["1.2"] + }, + { + "id": "1.6", + "name": "Create base Item data model", + "description": "Shared fields for all items: name, img, type, system data container, description", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["0.1", "0.2"] + }, + { + "id": "1.7", + "name": "Create Ancestry item data model", + "description": "Being type, size, traits array with name/description pairs", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.6"] + }, + { + "id": "1.8", + "name": "Create Class item data model", + "description": "Key stat, action style, zone, training grants, starting pack, progression table (level -> features/mana/spells), feature definitions", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.6"] + }, + { + "id": "1.9", + "name": "Create Spell item data model", + "description": "Damage type, base effect, crit effect, valid delivery types, duration options, mana cost formula components", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.6"] + }, + { + "id": "1.10", + "name": "Create Perk item data model", + "description": "Prerequisites (stat requirements, training requirements, spell requirements), full description, mechanical effects", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.6"] + }, + { + "id": "1.11", + "name": "Create Weapon item data model", + "description": "Damage dice, grip type (1H/2H/Versatile), properties (Finesse, Thrown, Cleave, etc.), attack skill, slot cost, value", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.6"] + }, + { + "id": "1.12", + "name": "Create Armor item data model", + "description": "Armor value, type (Light/Heavy/Shield), slot cost, value, dodge penalty flag", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.6"] + }, + { + "id": "1.13", + "name": "Create Equipment item data model", + "description": "Generic items: slot cost, value, description, quantity, consumable flag", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.6"] + }, + { + "id": "1.14", + "name": "Create Feature item data model", + "description": "Class features as separate items: source class, level gained, description, mechanical effects, passive vs active", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.6"] + }, + { + "id": "1.15", + "name": "Create Active Effects integration", + "description": "System for items (classes, perks, features) to modify actor stats, crit thresholds, resources", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["1.2", "1.8", "1.10", "1.14"] + } + ] + }, + { + "id": "phase-2", + "name": "Core System Logic", + "description": "JavaScript modules for game mechanics, rolls, and calculations", + "tasks": [ + { + "id": "2.1", + "name": "Create main system entry point (vagabond.mjs)", + "description": "System initialization, hook registration, CONFIG setup, document class registration", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["1.1", "1.6"] + }, + { + "id": "2.2", + "name": "Implement VagabondActor class", + "description": "Extended Actor with prepareData for derived values calculation (HP, Speed, Saves, Skill difficulties)", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.1", "1.2", "1.3"] + }, + { + "id": "2.3", + "name": "Implement VagabondItem class", + "description": "Extended Item with type-specific preparation and chat card generation", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.1", "1.6"] + }, + { + "id": "2.4", + "name": "Create dice rolling module", + "description": "Core roll functions: d20 checks, damage rolls, exploding dice (d6!), countdown dice, favor/hinder modifiers", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.1"] + }, + { + "id": "2.5", + "name": "Implement skill check system", + "description": "Roll dialog with skill selection, favor/hinder toggles, automatic difficulty calculation, crit threshold display", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.2", "2.4"] + }, + { + "id": "2.6", + "name": "Implement attack roll system", + "description": "Weapon attack rolls with stat selection, damage calculation, crit bonus damage, Block/Dodge prompts for targets", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.4", "2.5", "1.11"] + }, + { + "id": "2.7", + "name": "Implement save roll system", + "description": "Reflex/Endure/Will saves with correct stat combinations, favor/hinder, crit detection", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.4", "2.5"] + }, + { + "id": "2.8", + "name": "Implement spell casting system", + "description": "Dynamic spell dialog: select damage dice, delivery type, duration; auto-calculate mana cost; track focus state", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.4", "1.9"] + }, + { + "id": "2.9", + "name": "Implement crit threshold modifier system", + "description": "Per-skill and per-action crit thresholds that can be modified by Active Effects from classes/perks/features", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["2.5", "1.15"] + }, + { + "id": "2.10", + "name": "Implement resource management", + "description": "HP damage/healing, Mana spending/recovery, Luck pool, Fatigue accumulation with death trigger at 5", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["2.2", "1.4"] + }, + { + "id": "2.11", + "name": "Implement inventory slot tracking", + "description": "Calculate slots used from equipped items, max slots from Might, over-capacity warnings", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["2.2", "1.11", "1.12", "1.13"] + }, + { + "id": "2.12", + "name": "Implement class feature automation", + "description": "When class item added to character, apply appropriate Active Effects for current level; update on level change", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["2.2", "2.3", "1.8", "1.15"] + }, + { + "id": "2.13", + "name": "Implement morale check system (NPC)", + "description": "2d6 vs Morale roll, triggered manually or via hooks on death/half HP", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["2.4", "1.3"] + }, + { + "id": "2.14", + "name": "Implement zone AI hints (NPC)", + "description": "Display suggested behavior based on Zone (Frontline/Midline/Backline) and HP status", + "completed": false, + "tested": false, + "priority": "low", + "dependencies": ["1.3"] + } + ] + }, + { + "id": "phase-3", + "name": "Actor Sheets (UI)", + "description": "Handlebars templates and sheet classes for Actor display and interaction", + "tasks": [ + { + "id": "3.1", + "name": "Create base VagabondActorSheet class", + "description": "Extended ActorSheet with common methods, tab handling, drag-drop, context menus", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.2"] + }, + { + "id": "3.2", + "name": "Design Character sheet layout (HTML/Handlebars)", + "description": "Match official Hero Record: Stats column, HP/Armor/Fatigue, Speed section, Saves, Skills grid, Attacks, Inventory, Abilities, Magic", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["3.1"] + }, + { + "id": "3.3", + "name": "Implement Character sheet - Header section", + "description": "Name, Ancestry, Level, Class, XP, Size, Being Type fields", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["3.2"] + }, + { + "id": "3.4", + "name": "Implement Character sheet - Stats section", + "description": "Six stats display with large numbers matching official sheet aesthetic", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["3.2"] + }, + { + "id": "3.5", + "name": "Implement Character sheet - Combat section", + "description": "HP (current/max), Armor, Fatigue, Speed (base/bonus/crawl/travel), Current Luck", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["3.2"] + }, + { + "id": "3.6", + "name": "Implement Character sheet - Saves section", + "description": "Reflex, Endure, Will with calculated difficulties, clickable for rolls", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["3.2", "2.7"] + }, + { + "id": "3.7", + "name": "Implement Character sheet - Skills section", + "description": "12 skills grid with trained checkboxes, stat associations, difficulty numbers, clickable for rolls", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["3.2", "2.5"] + }, + { + "id": "3.8", + "name": "Implement Character sheet - Attacks section", + "description": "Weapon attack skills (Melee/Brawl/Ranged/Finesse) with difficulties, equipped weapons list with roll buttons", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["3.2", "2.6"] + }, + { + "id": "3.9", + "name": "Implement Character sheet - Inventory tab", + "description": "Item list with slots display, wealth tracking (G/S/C), occupied/max/bonus slots, drag-drop support", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["3.2", "2.11"] + }, + { + "id": "3.10", + "name": "Implement Character sheet - Abilities tab", + "description": "List of Features, Perks, Ancestry traits; expandable descriptions; usage tracking for limited abilities", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["3.2"] + }, + { + "id": "3.11", + "name": "Implement Character sheet - Magic tab", + "description": "Mana (current/max/casting max), known spells list with cast buttons, focus indicator", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["3.2", "2.8"] + }, + { + "id": "3.12", + "name": "Implement Character sheet - Biography tab", + "description": "Rich text editor for character background, notes, bonds", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["3.2"] + }, + { + "id": "3.13", + "name": "Create NPC/Monster sheet layout", + "description": "Compact stat block format: HD, HP, TL, Zone, Morale, Armor, Immunities, Weaknesses, Actions, Abilities", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["3.1", "1.3"] + }, + { + "id": "3.14", + "name": "Implement NPC sheet - Stat block section", + "description": "Display all combat-relevant stats in traditional TTRPG stat block format", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["3.13"] + }, + { + "id": "3.15", + "name": "Implement NPC sheet - Actions section", + "description": "List of attack actions with clickable roll buttons, damage dice display", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["3.13", "2.4"] + }, + { + "id": "3.16", + "name": "Implement NPC sheet - Morale button", + "description": "Clickable morale check with result interpretation", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["3.13", "2.13"] + } + ] + }, + { + "id": "phase-4", + "name": "Item Sheets (UI)", + "description": "Handlebars templates and sheet classes for Item display and editing", + "tasks": [ + { + "id": "4.1", + "name": "Create base VagabondItemSheet class", + "description": "Extended ItemSheet with common methods, type-specific tab handling", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.3"] + }, + { + "id": "4.2", + "name": "Implement Ancestry item sheet", + "description": "Being type dropdown, size dropdown, traits editor (add/remove trait entries)", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["4.1", "1.7"] + }, + { + "id": "4.3", + "name": "Implement Class item sheet", + "description": "Key stat, training grants, progression table editor, feature definitions", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["4.1", "1.8"] + }, + { + "id": "4.4", + "name": "Implement Spell item sheet", + "description": "Damage type, effect text, crit effect, delivery options checkboxes, cost formula display", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["4.1", "1.9"] + }, + { + "id": "4.5", + "name": "Implement Perk item sheet", + "description": "Prerequisites editor (stat/training/spell requirements), description, effects", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["4.1", "1.10"] + }, + { + "id": "4.6", + "name": "Implement Weapon item sheet", + "description": "Damage dice selector, grip type, properties checkboxes, attack skill dropdown, value/slots", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["4.1", "1.11"] + }, + { + "id": "4.7", + "name": "Implement Armor item sheet", + "description": "Armor value, type dropdown, dodge penalty checkbox, value/slots", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["4.1", "1.12"] + }, + { + "id": "4.8", + "name": "Implement Equipment item sheet", + "description": "Generic item fields: description, quantity, slots, value, consumable flag", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["4.1", "1.13"] + }, + { + "id": "4.9", + "name": "Implement Feature item sheet", + "description": "Source class, level requirement, description, passive/active toggle, effects editor", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["4.1", "1.14"] + } + ] + }, + { + "id": "phase-5", + "name": "Styling & Theming", + "description": "SCSS styles for parchment theme with accessibility considerations", + "tasks": [ + { + "id": "5.1", + "name": "Create SCSS architecture", + "description": "Variables file, mixins, partials structure for maintainable styles", + "completed": true, + "tested": false, + "priority": "high", + "dependencies": ["0.6"] + }, + { + "id": "5.2", + "name": "Define color palette", + "description": "Parchment background tones, high-contrast text colors, accent colors; WCAG AA compliant", + "completed": true, + "tested": false, + "priority": "high", + "dependencies": ["5.1"] + }, + { + "id": "5.3", + "name": "Create typography system", + "description": "Font selections for headers (stylized) and body (readable), size scale, line heights", + "completed": true, + "tested": false, + "priority": "high", + "dependencies": ["5.1"] + }, + { + "id": "5.4", + "name": "Style Character sheet", + "description": "Match official Hero Record aesthetic: stat column layout, bordered sections, parchment background", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["5.1", "5.2", "5.3", "3.2"] + }, + { + "id": "5.5", + "name": "Style NPC sheet", + "description": "Clean stat block format, readable at small sizes, collapsible sections", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["5.1", "5.2", "5.3", "3.13"] + }, + { + "id": "5.6", + "name": "Style Item sheets", + "description": "Consistent look across all item types, clear form layouts", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["5.1", "5.2", "5.3", "4.1"] + }, + { + "id": "5.7", + "name": "Style roll dialogs", + "description": "Favor/Hinder toggles, modifier inputs, clear roll button", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["5.1", "5.2"] + }, + { + "id": "5.8", + "name": "Style chat cards", + "description": "Roll results, spell casts, attack outcomes in themed chat messages", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["5.1", "5.2"] + }, + { + "id": "5.9", + "name": "Accessibility audit", + "description": "Verify color contrast ratios, focus indicators, screen reader compatibility", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["5.4", "5.5", "5.6"] + } + ] + }, + { + "id": "phase-6", + "name": "Compendium Content", + "description": "Pre-populated compendium packs with all Vagabond RPG content", + "tasks": [ + { + "id": "6.1", + "name": "Create compendium pack definitions", + "description": "Define packs in system.json: ancestries, classes, spells, perks, weapons, armor, equipment, monsters", + "completed": true, + "tested": false, + "priority": "high", + "dependencies": ["0.1"] + }, + { + "id": "6.2", + "name": "Populate Ancestries compendium", + "description": "Human, Dwarf, Elf, Halfling, Draken, Goblin, Orc with full traits", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["6.1", "1.7"] + }, + { + "id": "6.3", + "name": "Populate Classes compendium", + "description": "All 18 classes with complete progression tables and features", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["6.1", "1.8"] + }, + { + "id": "6.4", + "name": "Populate Spells compendium", + "description": "All 55+ spells with damage types, effects, crit bonuses, delivery/duration options", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["6.1", "1.9"] + }, + { + "id": "6.5", + "name": "Populate Perks compendium", + "description": "All 90+ perks with prerequisites and full descriptions", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["6.1", "1.10"] + }, + { + "id": "6.6", + "name": "Populate Weapons compendium", + "description": "Standard weapons from equipment tables with stats and properties", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["6.1", "1.11"] + }, + { + "id": "6.7", + "name": "Populate Armor compendium", + "description": "Standard armor options from equipment tables", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["6.1", "1.12"] + }, + { + "id": "6.8", + "name": "Populate Equipment compendium", + "description": "Adventuring gear, alchemical items, tools from equipment tables", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["6.1", "1.13"] + }, + { + "id": "6.9", + "name": "Populate Bestiary compendium", + "description": "Monsters from bestiary with full stat blocks (prioritize common encounters)", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["6.1", "1.3"] + } + ] + }, + { + "id": "phase-7", + "name": "Roll Dialogs & Chat", + "description": "Interactive roll configuration dialogs and rich chat message output", + "tasks": [ + { + "id": "7.1", + "name": "Create Skill Check dialog", + "description": "Select skill, show difficulty/crit threshold, favor/hinder toggles, situational modifier input", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.5"] + }, + { + "id": "7.2", + "name": "Create Attack Roll dialog", + "description": "Weapon selection, attack skill, favor/hinder, show crit threshold, target selection", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.6"] + }, + { + "id": "7.3", + "name": "Create Save Roll dialog", + "description": "Save type selection (or auto from context), Block/Dodge choice for defense, favor/hinder", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.7"] + }, + { + "id": "7.4", + "name": "Create Spell Cast dialog", + "description": "Damage dice selector (+Mana), delivery type dropdown with cost display, duration selector, total mana cost, cast skill", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["2.8"] + }, + { + "id": "7.5", + "name": "Create Damage Roll dialog", + "description": "Dice pool display, exploding toggle, crit bonus indicator, target HP context", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["2.4"] + }, + { + "id": "7.6", + "name": "Create roll result chat cards", + "description": "Rich chat output: roll formula, result, success/fail/crit indicator, damage if applicable", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["2.4"] + }, + { + "id": "7.7", + "name": "Create spell cast chat cards", + "description": "Spell name, effect description, mana spent, damage if applicable, duration/focus indicator", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["2.8", "7.6"] + }, + { + "id": "7.8", + "name": "Create defense prompt system", + "description": "When attack targets a token, prompt defender with Block/Dodge buttons in chat", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["7.2", "7.3"] + } + ] + }, + { + "id": "phase-8", + "name": "Localization", + "description": "Internationalization support with English as base language", + "tasks": [ + { + "id": "8.1", + "name": "Create English language file", + "description": "All UI strings, stat names, skill names, item type names in en.json", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["3.2", "4.1"] + }, + { + "id": "8.2", + "name": "Implement localization in templates", + "description": "Replace hardcoded strings with {{localize}} calls throughout all Handlebars templates", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["8.1"] + } + ] + }, + { + "id": "phase-9", + "name": "Testing & Polish", + "description": "Quality assurance, bug fixes, and user experience refinements", + "tasks": [ + { + "id": "9.1", + "name": "Test character creation workflow", + "description": "Create character from scratch: set stats, add ancestry, add class, verify derived values", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["3.2", "6.2", "6.3"] + }, + { + "id": "9.2", + "name": "Test skill check workflow", + "description": "Roll all 12 skills with various favor/hinder combinations, verify difficulties", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["7.1"] + }, + { + "id": "9.3", + "name": "Test combat workflow", + "description": "Full combat round: attacks, saves, damage, HP tracking, death at 0 HP", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["7.2", "7.3", "7.5"] + }, + { + "id": "9.4", + "name": "Test spell casting workflow", + "description": "Cast spells with various delivery/duration/damage combinations, verify mana costs", + "completed": false, + "tested": false, + "priority": "critical", + "dependencies": ["7.4"] + }, + { + "id": "9.5", + "name": "Test leveling workflow", + "description": "Level up character: HP increase, feature gains, perk at odd levels, stat at even levels", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["2.12"] + }, + { + "id": "9.6", + "name": "Test compendium import", + "description": "Drag items from all compendiums to character, verify data integrity", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["6.2", "6.3", "6.4", "6.5"] + }, + { + "id": "9.7", + "name": "Test NPC/Monster workflow", + "description": "Create monster, run attacks, trigger morale check", + "completed": false, + "tested": false, + "priority": "high", + "dependencies": ["3.13", "6.9"] + }, + { + "id": "9.8", + "name": "Cross-browser testing", + "description": "Verify functionality in Chrome, Firefox, Safari", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["9.1", "9.2", "9.3", "9.4"] + }, + { + "id": "9.9", + "name": "Performance optimization", + "description": "Profile sheet rendering, optimize slow operations", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["9.1"] + } + ] + }, + { + "id": "phase-10", + "name": "Documentation & Release", + "description": "User documentation and package preparation for distribution", + "tasks": [ + { + "id": "10.1", + "name": "Write user guide", + "description": "How to create characters, use sheets, cast spells, run combat", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["9.1", "9.2", "9.3", "9.4"] + }, + { + "id": "10.2", + "name": "Write GM guide", + "description": "How to create NPCs/monsters, use compendiums, run encounters", + "completed": false, + "tested": false, + "priority": "medium", + "dependencies": ["9.7"] + }, + { + "id": "10.3", + "name": "Create changelog", + "description": "Version history with features and fixes", + "completed": false, + "tested": false, + "priority": "low", + "dependencies": [] + }, + { + "id": "10.4", + "name": "Prepare package for Foundry repository", + "description": "Verify manifest, create release bundle, test fresh installation", + "completed": false, + "tested": false, + "priority": "low", + "dependencies": ["10.1", "10.2"] + } + ] + } + ], + "summary": { + "total_tasks": 98, + "phases": 11, + "critical_path": [ + "0.1 -> 1.1 -> 2.1 -> 2.2 -> 3.1 -> 3.2 -> 7.1 -> 9.2", + "0.1 -> 1.6 -> 1.9 -> 2.8 -> 7.4 -> 9.4" + ], + "estimated_complexity": "large", + "notes": [ + "Phase 0-2 must complete before meaningful UI work", + "Phases 3-4 can proceed in parallel once data models exist", + "Phase 5 styling can be iterated throughout", + "Phase 6 content population can happen incrementally", + "Phase 7 dialogs depend on core mechanics but can be refined iteratively", + "Testing should happen continuously, not just in Phase 9" + ] + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..07ce9ab --- /dev/null +++ b/README.md @@ -0,0 +1,110 @@ +# Vagabond RPG - Foundry VTT System + +A complete Foundry VTT v13 system implementation for **Vagabond RPG** - Pulp Fantasy Roleplaying. + +## Features + +- Full character sheet matching the official Hero Record design +- Dynamic spell casting system with delivery/duration/damage customization +- Automated skill checks with favor/hinder modifiers +- Variable crit thresholds per skill (modified by class features and perks) +- Complete compendiums: 18 classes, 55+ spells, 90+ perks, ancestries, equipment, bestiary +- NPC/Monster stat blocks with morale system +- Parchment-themed UI with accessibility (color-blind friendly) + +## Installation + +### From Foundry +1. Open Foundry VTT Setup +2. Navigate to Game Systems +3. Click "Install System" +4. Search for "Vagabond" or paste the manifest URL: + ``` + https://github.com/calcorum/vagabond-rpg-foundryvtt/releases/latest/download/system.json + ``` + +### Manual Installation +1. Download the latest release from GitHub +2. Extract to `Data/systems/vagabond/` +3. Restart Foundry VTT + +## Development Setup + +### Prerequisites +- Node.js 18+ +- Docker & Docker Compose (for local Foundry instance) + +### Quick Start +```bash +# Clone the repository +git clone https://github.com/calcorum/vagabond-rpg-foundryvtt.git +cd vagabond-rpg-foundryvtt + +# Install dependencies +npm install + +# Start SCSS watcher +npm run watch + +# Start local Foundry instance +docker compose up -d + +# Access Foundry at http://localhost:30000 +``` + +### Project Structure +``` +vagabond-rpg-foundryvtt/ +├── module/ # JavaScript modules +│ ├── vagabond.mjs # System entry point +│ ├── documents/ # Actor/Item document classes +│ ├── sheets/ # Sheet classes +│ ├── helpers/ # Utility functions +│ └── dice/ # Roll handling +├── templates/ # Handlebars templates +│ ├── actor/ # Actor sheet templates +│ ├── item/ # Item sheet templates +│ ├── chat/ # Chat message templates +│ └── dialog/ # Roll dialog templates +├── styles/ # SCSS/CSS +│ └── scss/ # SCSS source files +├── lang/ # Localization files +├── packs/ # Compendium data +├── assets/ # Images and icons +├── system.json # System manifest +└── docker-compose.yml # Local dev environment +``` + +### Building Styles +```bash +# One-time build +npm run build + +# Watch for changes +npm run watch +``` + +### Creating a Release +```bash +npm run release +``` + +## Contributing + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## License + +MIT License - see [LICENSE](LICENSE) for details. + +Vagabond RPG is a product of its respective copyright holders. This system implementation is a fan project and is not affiliated with or endorsed by the original game creators. + +## Acknowledgments + +- Vagabond RPG by [Publisher] for the amazing game system +- Foundry VTT community for documentation and examples +- All contributors to this project diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c38d104 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,28 @@ +services: + foundry: + image: felddy/foundryvtt:release + container_name: vagabond-foundry-dev + hostname: vagabond-foundry-dev + restart: unless-stopped + environment: + - FOUNDRY_USERNAME=${FOUNDRY_USERNAME} + - FOUNDRY_PASSWORD=${FOUNDRY_PASSWORD} + - FOUNDRY_ADMIN_KEY=${FOUNDRY_ADMIN_KEY:-vagabond-dev} + - FOUNDRY_LICENSE_KEY=${FOUNDRY_LICENSE_KEY} + - CONTAINER_PRESERVE_CONFIG=true + volumes: + # Foundry data directory + - ./foundrydata:/data + # Mount system directly for hot reload development + - ./:/data/Data/systems/vagabond:ro + ports: + - "30000:30000" + # Required for Docker-in-LXC or rootless podman + security_opt: + - apparmor=unconfined + +# Note: Create a .env file with your Foundry credentials: +# FOUNDRY_USERNAME=your-username +# FOUNDRY_PASSWORD=your-password +# FOUNDRY_LICENSE_KEY=your-license-key +# FOUNDRY_ADMIN_KEY=your-admin-key diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..b4d4793 --- /dev/null +++ b/lang/en.json @@ -0,0 +1,213 @@ +{ + "VAGABOND.SystemName": "Vagabond RPG", + "VAGABOND.SystemDescription": "A Foundry VTT system for Vagabond RPG - Pulp Fantasy Roleplaying", + + "VAGABOND.SheetCharacter": "Character Sheet", + "VAGABOND.SheetNPC": "NPC Sheet", + "VAGABOND.SheetItem": "Item Sheet", + + "VAGABOND.StatMight": "Might", + "VAGABOND.StatDexterity": "Dexterity", + "VAGABOND.StatAwareness": "Awareness", + "VAGABOND.StatReason": "Reason", + "VAGABOND.StatPresence": "Presence", + "VAGABOND.StatLuck": "Luck", + + "VAGABOND.StatMightAbbr": "MIT", + "VAGABOND.StatDexterityAbbr": "DEX", + "VAGABOND.StatAwarenessAbbr": "AWR", + "VAGABOND.StatReasonAbbr": "RSN", + "VAGABOND.StatPresenceAbbr": "PRS", + "VAGABOND.StatLuckAbbr": "LUK", + + "VAGABOND.SkillArcana": "Arcana", + "VAGABOND.SkillBrawl": "Brawl", + "VAGABOND.SkillCraft": "Craft", + "VAGABOND.SkillDetect": "Detect", + "VAGABOND.SkillFinesse": "Finesse", + "VAGABOND.SkillInfluence": "Influence", + "VAGABOND.SkillLeadership": "Leadership", + "VAGABOND.SkillMedicine": "Medicine", + "VAGABOND.SkillMysticism": "Mysticism", + "VAGABOND.SkillPerformance": "Performance", + "VAGABOND.SkillSneak": "Sneak", + "VAGABOND.SkillSurvival": "Survival", + + "VAGABOND.AttackMelee": "Melee", + "VAGABOND.AttackBrawl": "Brawl", + "VAGABOND.AttackRanged": "Ranged", + "VAGABOND.AttackFinesse": "Finesse", + + "VAGABOND.SaveReflex": "Reflex", + "VAGABOND.SaveEndure": "Endure", + "VAGABOND.SaveWill": "Will", + + "VAGABOND.DeliveryTouch": "Touch", + "VAGABOND.DeliveryRemote": "Remote", + "VAGABOND.DeliveryImbue": "Imbue", + "VAGABOND.DeliveryCube": "Cube", + "VAGABOND.DeliveryAura": "Aura", + "VAGABOND.DeliveryCone": "Cone", + "VAGABOND.DeliveryGlyph": "Glyph", + "VAGABOND.DeliveryLine": "Line", + "VAGABOND.DeliverySphere": "Sphere", + + "VAGABOND.DurationInstant": "Instant", + "VAGABOND.DurationFocus": "Focus", + "VAGABOND.DurationContinual": "Continual", + + "VAGABOND.DamageBlunt": "Blunt", + "VAGABOND.DamageSlash": "Slash", + "VAGABOND.DamagePierce": "Pierce", + "VAGABOND.DamageFire": "Fire", + "VAGABOND.DamageCold": "Cold", + "VAGABOND.DamageShock": "Shock", + "VAGABOND.DamagePoison": "Poison", + "VAGABOND.DamageAcid": "Acid", + + "VAGABOND.PropertyFinesse": "Finesse", + "VAGABOND.PropertyThrown": "Thrown", + "VAGABOND.PropertyCleave": "Cleave", + "VAGABOND.PropertyReach": "Reach", + "VAGABOND.PropertyLoading": "Loading", + "VAGABOND.PropertyBrawl": "Brawl", + "VAGABOND.PropertyCrude": "Crude", + "VAGABOND.PropertyVersatile": "Versatile", + + "VAGABOND.Grip1H": "1H", + "VAGABOND.Grip2H": "2H", + "VAGABOND.GripVersatile": "Versatile", + "VAGABOND.GripFist": "Fist", + + "VAGABOND.ArmorLight": "Light", + "VAGABOND.ArmorHeavy": "Heavy", + "VAGABOND.ArmorShield": "Shield", + + "VAGABOND.SizeSmall": "Small", + "VAGABOND.SizeMedium": "Medium", + "VAGABOND.SizeLarge": "Large", + "VAGABOND.SizeHuge": "Huge", + "VAGABOND.SizeGiant": "Giant", + "VAGABOND.SizeColossal": "Colossal", + + "VAGABOND.BeingHumanlike": "Humanlike", + "VAGABOND.BeingFae": "Fae", + "VAGABOND.BeingCryptid": "Cryptid", + "VAGABOND.BeingArtificial": "Artificial", + "VAGABOND.BeingBeast": "Beast", + "VAGABOND.BeingOuter": "Outer", + "VAGABOND.BeingPrimordial": "Primordial", + "VAGABOND.BeingUndead": "Undead", + + "VAGABOND.ZoneFrontline": "Frontline", + "VAGABOND.ZoneMidline": "Midline", + "VAGABOND.ZoneBackline": "Backline", + + "VAGABOND.Level": "Level", + "VAGABOND.XP": "XP", + "VAGABOND.Class": "Class", + "VAGABOND.Ancestry": "Ancestry", + "VAGABOND.Name": "Name", + + "VAGABOND.HitPoints": "Hit Points", + "VAGABOND.HP": "HP", + "VAGABOND.Current": "Current", + "VAGABOND.Max": "Max", + + "VAGABOND.Armor": "Armor", + "VAGABOND.Fatigue": "Fatigue", + "VAGABOND.Speed": "Speed", + "VAGABOND.SpeedBonus": "Speed Bonus", + "VAGABOND.CrawlSpeed": "Crawl Speed", + "VAGABOND.TravelSpeed": "Travel Speed", + + "VAGABOND.Saves": "Saves", + "VAGABOND.Skills": "Skills", + "VAGABOND.Attacks": "Attacks", + "VAGABOND.Trained": "Trained", + "VAGABOND.Difficulty": "Difficulty", + + "VAGABOND.Inventory": "Inventory", + "VAGABOND.Abilities": "Abilities", + "VAGABOND.Magic": "Magic", + "VAGABOND.Biography": "Biography", + + "VAGABOND.Mana": "Mana", + "VAGABOND.CastingMax": "Casting Max", + "VAGABOND.Spells": "Spells", + + "VAGABOND.Wealth": "Wealth", + "VAGABOND.Gold": "Gold", + "VAGABOND.Silver": "Silver", + "VAGABOND.Copper": "Copper", + "VAGABOND.ItemSlots": "Item Slots", + "VAGABOND.Occupied": "Occupied", + "VAGABOND.Bonus": "Bonus", + + "VAGABOND.Features": "Features", + "VAGABOND.Perks": "Perks", + "VAGABOND.Traits": "Traits", + + "VAGABOND.Roll": "Roll", + "VAGABOND.RollCheck": "Roll Check", + "VAGABOND.RollSave": "Roll Save", + "VAGABOND.RollAttack": "Roll Attack", + "VAGABOND.RollDamage": "Roll Damage", + "VAGABOND.Cast": "Cast", + + "VAGABOND.Favor": "Favor", + "VAGABOND.Hinder": "Hinder", + "VAGABOND.Modifier": "Modifier", + + "VAGABOND.CritThreshold": "Crit on", + "VAGABOND.Success": "Success", + "VAGABOND.Failure": "Failure", + "VAGABOND.Critical": "Critical!", + + "VAGABOND.Damage": "Damage", + "VAGABOND.DamageType": "Damage Type", + "VAGABOND.Effect": "Effect", + "VAGABOND.CritEffect": "Crit Effect", + "VAGABOND.Delivery": "Delivery", + "VAGABOND.Duration": "Duration", + "VAGABOND.ManaCost": "Mana Cost", + + "VAGABOND.Prerequisites": "Prerequisites", + "VAGABOND.Description": "Description", + + "VAGABOND.HD": "HD", + "VAGABOND.TL": "TL", + "VAGABOND.Zone": "Zone", + "VAGABOND.Morale": "Morale", + "VAGABOND.Appearing": "# Appearing", + "VAGABOND.Immune": "Immune", + "VAGABOND.Weak": "Weak", + "VAGABOND.Actions": "Actions", + + "VAGABOND.MoraleCheck": "Morale Check", + "VAGABOND.MoraleHolds": "Morale Holds", + "VAGABOND.MoraleBreaks": "Morale Breaks!", + + "VAGABOND.Block": "Block", + "VAGABOND.Dodge": "Dodge", + + "VAGABOND.CurrentLuck": "Current Luck", + "VAGABOND.StudiedDice": "Studied Dice", + + "VAGABOND.Add": "Add", + "VAGABOND.Remove": "Remove", + "VAGABOND.Edit": "Edit", + "VAGABOND.Delete": "Delete", + "VAGABOND.Cancel": "Cancel", + "VAGABOND.Confirm": "Confirm", + "VAGABOND.Save": "Save", + + "VAGABOND.ItemTypeAncestry": "Ancestry", + "VAGABOND.ItemTypeClass": "Class", + "VAGABOND.ItemTypeSpell": "Spell", + "VAGABOND.ItemTypePerk": "Perk", + "VAGABOND.ItemTypeFeature": "Feature", + "VAGABOND.ItemTypeWeapon": "Weapon", + "VAGABOND.ItemTypeArmor": "Armor", + "VAGABOND.ItemTypeEquipment": "Equipment" +} diff --git a/module/helpers/config.mjs b/module/helpers/config.mjs new file mode 100644 index 0000000..eb78d83 --- /dev/null +++ b/module/helpers/config.mjs @@ -0,0 +1,200 @@ +/** + * Vagabond RPG Configuration + * Contains all system constants and configuration data + */ + +export const VAGABOND = {}; + +/** + * The set of Ability Scores (Stats) used within the system + */ +VAGABOND.stats = { + might: "VAGABOND.StatMight", + dexterity: "VAGABOND.StatDexterity", + awareness: "VAGABOND.StatAwareness", + reason: "VAGABOND.StatReason", + presence: "VAGABOND.StatPresence", + luck: "VAGABOND.StatLuck" +}; + +/** + * Abbreviated stat labels + */ +VAGABOND.statsAbbr = { + might: "VAGABOND.StatMightAbbr", + dexterity: "VAGABOND.StatDexterityAbbr", + awareness: "VAGABOND.StatAwarenessAbbr", + reason: "VAGABOND.StatReasonAbbr", + presence: "VAGABOND.StatPresenceAbbr", + luck: "VAGABOND.StatLuckAbbr" +}; + +/** + * Skills and their associated stats + */ +VAGABOND.skills = { + arcana: { label: "VAGABOND.SkillArcana", stat: "reason" }, + brawl: { label: "VAGABOND.SkillBrawl", stat: "might" }, + craft: { label: "VAGABOND.SkillCraft", stat: "reason" }, + detect: { label: "VAGABOND.SkillDetect", stat: "awareness" }, + finesse: { label: "VAGABOND.SkillFinesse", stat: "dexterity" }, + influence: { label: "VAGABOND.SkillInfluence", stat: "presence" }, + leadership: { label: "VAGABOND.SkillLeadership", stat: "presence" }, + medicine: { label: "VAGABOND.SkillMedicine", stat: "reason" }, + mysticism: { label: "VAGABOND.SkillMysticism", stat: "awareness" }, + performance: { label: "VAGABOND.SkillPerformance", stat: "presence" }, + sneak: { label: "VAGABOND.SkillSneak", stat: "dexterity" }, + survival: { label: "VAGABOND.SkillSurvival", stat: "awareness" } +}; + +/** + * Attack types and their associated stats + */ +VAGABOND.attackTypes = { + melee: { label: "VAGABOND.AttackMelee", stat: "might" }, + brawl: { label: "VAGABOND.AttackBrawl", stat: "might" }, + ranged: { label: "VAGABOND.AttackRanged", stat: "awareness" }, + finesse: { label: "VAGABOND.AttackFinesse", stat: "dexterity" } +}; + +/** + * Save types and their stat combinations + */ +VAGABOND.saves = { + reflex: { label: "VAGABOND.SaveReflex", stats: ["dexterity", "awareness"] }, + endure: { label: "VAGABOND.SaveEndure", stats: ["might", "might"] }, + will: { label: "VAGABOND.SaveWill", stats: ["reason", "presence"] } +}; + +/** + * Spell delivery types with base costs + */ +VAGABOND.spellDelivery = { + touch: { label: "VAGABOND.DeliveryTouch", cost: 0 }, + remote: { label: "VAGABOND.DeliveryRemote", cost: 0 }, + imbue: { label: "VAGABOND.DeliveryImbue", cost: 0 }, + cube: { label: "VAGABOND.DeliveryCube", cost: 1 }, + aura: { label: "VAGABOND.DeliveryAura", cost: 2 }, + cone: { label: "VAGABOND.DeliveryCone", cost: 2 }, + glyph: { label: "VAGABOND.DeliveryGlyph", cost: 2 }, + line: { label: "VAGABOND.DeliveryLine", cost: 2 }, + sphere: { label: "VAGABOND.DeliverySphere", cost: 2 } +}; + +/** + * Spell duration types + */ +VAGABOND.spellDuration = { + instant: { label: "VAGABOND.DurationInstant", focus: false }, + focus: { label: "VAGABOND.DurationFocus", focus: true }, + continual: { label: "VAGABOND.DurationContinual", focus: false } +}; + +/** + * Damage types + */ +VAGABOND.damageTypes = { + blunt: "VAGABOND.DamageBlunt", + slash: "VAGABOND.DamageSlash", + pierce: "VAGABOND.DamagePierce", + fire: "VAGABOND.DamageFire", + cold: "VAGABOND.DamageCold", + shock: "VAGABOND.DamageShock", + poison: "VAGABOND.DamagePoison", + acid: "VAGABOND.DamageAcid" +}; + +/** + * Weapon properties + */ +VAGABOND.weaponProperties = { + finesse: "VAGABOND.PropertyFinesse", + thrown: "VAGABOND.PropertyThrown", + cleave: "VAGABOND.PropertyCleave", + reach: "VAGABOND.PropertyReach", + loading: "VAGABOND.PropertyLoading", + brawl: "VAGABOND.PropertyBrawl", + crude: "VAGABOND.PropertyCrude", + versatile: "VAGABOND.PropertyVersatile" +}; + +/** + * Weapon grip types + */ +VAGABOND.gripTypes = { + "1h": "VAGABOND.Grip1H", + "2h": "VAGABOND.Grip2H", + versatile: "VAGABOND.GripVersatile", + fist: "VAGABOND.GripFist" +}; + +/** + * Armor types + */ +VAGABOND.armorTypes = { + light: "VAGABOND.ArmorLight", + heavy: "VAGABOND.ArmorHeavy", + shield: "VAGABOND.ArmorShield" +}; + +/** + * Size categories + */ +VAGABOND.sizes = { + small: "VAGABOND.SizeSmall", + medium: "VAGABOND.SizeMedium", + large: "VAGABOND.SizeLarge", + huge: "VAGABOND.SizeHuge", + giant: "VAGABOND.SizeGiant", + colossal: "VAGABOND.SizeColossal" +}; + +/** + * Being types + */ +VAGABOND.beingTypes = { + humanlike: "VAGABOND.BeingHumanlike", + fae: "VAGABOND.BeingFae", + cryptid: "VAGABOND.BeingCryptid", + artificial: "VAGABOND.BeingArtificial", + beast: "VAGABOND.BeingBeast", + outer: "VAGABOND.BeingOuter", + primordial: "VAGABOND.BeingPrimordial", + undead: "VAGABOND.BeingUndead" +}; + +/** + * Enemy zones for AI behavior + */ +VAGABOND.zones = { + frontline: "VAGABOND.ZoneFrontline", + midline: "VAGABOND.ZoneMidline", + backline: "VAGABOND.ZoneBackline" +}; + +/** + * Default crit threshold (Natural 20) + */ +VAGABOND.defaultCritThreshold = 20; + +/** + * Speed values by Dexterity + */ +VAGABOND.speedByDex = { + 2: 25, + 3: 25, + 4: 30, + 5: 30, + 6: 35, + 7: 35 +}; + +/** + * Max fatigue before death + */ +VAGABOND.maxFatigue = 5; + +/** + * Base item slots calculation + */ +VAGABOND.baseItemSlots = 8; diff --git a/module/vagabond.mjs b/module/vagabond.mjs new file mode 100644 index 0000000..4d972f1 --- /dev/null +++ b/module/vagabond.mjs @@ -0,0 +1,139 @@ +/** + * Vagabond RPG System for Foundry VTT + * @module vagabond + */ + +// Import configuration +import { VAGABOND } from "./helpers/config.mjs"; + +// Import document classes +// import { VagabondActor } from "./documents/actor.mjs"; +// import { VagabondItem } from "./documents/item.mjs"; + +// Import sheet classes +// import { VagabondCharacterSheet } from "./sheets/actor-sheet.mjs"; +// import { VagabondItemSheet } from "./sheets/item-sheet.mjs"; + +// Import helper functions +// import { preloadHandlebarsTemplates } from "./helpers/templates.mjs"; + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ + +/** + * Init hook - runs once when Foundry initializes + */ +Hooks.once("init", function() { + console.log("Vagabond RPG | Initializing Vagabond RPG System"); + + // Add custom constants for configuration + CONFIG.VAGABOND = VAGABOND; + + // Define custom Document classes + // CONFIG.Actor.documentClass = VagabondActor; + // CONFIG.Item.documentClass = VagabondItem; + + // Register sheet application classes + // Actors.unregisterSheet("core", ActorSheet); + // Actors.registerSheet("vagabond", VagabondCharacterSheet, { + // types: ["character"], + // makeDefault: true, + // label: "VAGABOND.SheetCharacter" + // }); + + // Items.unregisterSheet("core", ItemSheet); + // Items.registerSheet("vagabond", VagabondItemSheet, { + // makeDefault: true, + // label: "VAGABOND.SheetItem" + // }); + + // Preload Handlebars templates + // return preloadHandlebarsTemplates(); +}); + +/* -------------------------------------------- */ +/* Ready Hook */ +/* -------------------------------------------- */ + +/** + * Ready hook - runs when Foundry is fully loaded + */ +Hooks.once("ready", function() { + console.log("Vagabond RPG | System Ready"); + + // Display welcome message for GMs + if (game.user.isGM) { + const version = game.system.version; + ui.notifications.info(`Vagabond RPG v${version} - System loaded successfully!`); + } +}); + +/* -------------------------------------------- */ +/* Handlebars Helpers */ +/* -------------------------------------------- */ + +/** + * Define Handlebars helpers used throughout the system + */ +Hooks.once("init", function() { + // Multiply helper for formulas + Handlebars.registerHelper("multiply", function(a, b) { + return Number(a) * Number(b); + }); + + // Subtract helper + Handlebars.registerHelper("subtract", function(a, b) { + return Number(a) - Number(b); + }); + + // Calculate difficulty (20 - stat or 20 - stat*2 if trained) + Handlebars.registerHelper("difficulty", function(stat, trained) { + const statValue = Number(stat) || 0; + return trained ? 20 - (statValue * 2) : 20 - statValue; + }); + + // Check if value equals comparison + Handlebars.registerHelper("eq", function(a, b) { + return a === b; + }); + + // Check if value is greater than + Handlebars.registerHelper("gt", function(a, b) { + return Number(a) > Number(b); + }); + + // Check if value is less than + Handlebars.registerHelper("lt", function(a, b) { + return Number(a) < Number(b); + }); + + // Concatenate strings + Handlebars.registerHelper("concat", function(...args) { + // Remove the Handlebars options object from args + args.pop(); + return args.join(""); + }); + + // Capitalize first letter + Handlebars.registerHelper("capitalize", function(str) { + if (typeof str !== "string") return ""; + return str.charAt(0).toUpperCase() + str.slice(1); + }); + + // Format number with sign (+/-) + Handlebars.registerHelper("signedNumber", function(num) { + const n = Number(num) || 0; + return n >= 0 ? `+${n}` : `${n}`; + }); +}); + +/* -------------------------------------------- */ +/* Hot Reload Support (Development) */ +/* -------------------------------------------- */ + +if (import.meta.hot) { + import.meta.hot.accept((newModule) => { + console.log("Vagabond RPG | Hot reload triggered"); + }); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2c32fc0 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "vagabond-foundryvtt", + "version": "0.1.0", + "description": "Foundry VTT system for Vagabond RPG", + "scripts": { + "build": "sass styles/scss/vagabond.scss styles/vagabond.css --style=compressed", + "watch": "sass styles/scss/vagabond.scss styles/vagabond.css --watch --style=expanded --source-map", + "lint": "eslint module/", + "release": "npm run build && zip -r vagabond.zip system.json module/ templates/ styles/vagabond.css lang/ packs/ assets/ LICENSE README.md" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/calcorum/vagabond-rpg-foundryvtt.git" + }, + "author": "Cal Corum ", + "license": "MIT", + "bugs": { + "url": "https://github.com/calcorum/vagabond-rpg-foundryvtt/issues" + }, + "homepage": "https://github.com/calcorum/vagabond-rpg-foundryvtt#readme", + "devDependencies": { + "sass": "^1.69.0", + "eslint": "^8.56.0" + }, + "type": "module" +} diff --git a/styles/scss/_base.scss b/styles/scss/_base.scss new file mode 100644 index 0000000..ab2e41c --- /dev/null +++ b/styles/scss/_base.scss @@ -0,0 +1,156 @@ +// Vagabond RPG - Base Styles +// =========================== + +// Reset/normalize for Foundry context +.vagabond { + // Base typography + font-family: $font-family-body; + font-size: $font-size-base; + line-height: $line-height-normal; + color: $color-text-primary; + + // Box sizing + *, *::before, *::after { + box-sizing: border-box; + } + + // Links + a { + color: $color-accent-primary; + text-decoration: none; + transition: color $transition-fast; + + &:hover { + color: $color-accent-secondary; + text-decoration: underline; + } + + @include focus-visible; + } + + // Headings + h1, h2, h3, h4, h5, h6 { + font-family: $font-family-header; + font-weight: $font-weight-bold; + line-height: $line-height-tight; + color: $color-text-primary; + margin: 0 0 $spacing-2 0; + } + + h1 { font-size: $font-size-4xl; } + h2 { font-size: $font-size-3xl; } + h3 { font-size: $font-size-2xl; } + h4 { font-size: $font-size-xl; } + h5 { font-size: $font-size-lg; } + h6 { font-size: $font-size-base; } + + // Paragraphs + p { + margin: 0 0 $spacing-4 0; + + &:last-child { + margin-bottom: 0; + } + } + + // Lists + ul, ol { + margin: 0 0 $spacing-4 0; + padding-left: $spacing-6; + } + + li { + margin-bottom: $spacing-1; + } + + // Images + img { + max-width: 100%; + height: auto; + } + + // Tables + table { + width: 100%; + border-collapse: collapse; + margin-bottom: $spacing-4; + } + + th, td { + padding: $spacing-2 $spacing-3; + text-align: left; + border-bottom: 1px solid $color-border-light; + } + + th { + font-weight: $font-weight-semibold; + background-color: $color-parchment-dark; + } + + // Horizontal rule + hr { + border: none; + border-top: 1px solid $color-border; + margin: $spacing-4 0; + } + + // Code + code { + font-family: $font-family-mono; + font-size: $font-size-sm; + padding: $spacing-1 $spacing-2; + background-color: $color-parchment-dark; + border-radius: $radius-sm; + } + + // Blockquote + blockquote { + margin: 0 0 $spacing-4 0; + padding: $spacing-3 $spacing-4; + border-left: 3px solid $color-accent-primary; + background-color: $color-parchment-dark; + font-style: italic; + } + + // Selection + ::selection { + background-color: $color-accent-highlight; + color: $color-text-primary; + } +} + +// Foundry sheet wrapper styles +.sheet.vagabond { + @include custom-scrollbar; + background: $color-parchment; + background-image: + linear-gradient( + to bottom, + rgba($color-parchment-light, 0.3) 0%, + transparent 5%, + transparent 95%, + rgba($color-parchment-dark, 0.3) 100% + ); +} + +// Window header customization +.window-app.vagabond { + .window-header { + background: linear-gradient(to bottom, $color-parchment-dark, $color-parchment-darker); + border-bottom: 2px solid $color-border-dark; + + .window-title { + font-family: $font-family-header; + font-size: $font-size-lg; + color: $color-text-primary; + } + + .header-button { + color: $color-text-secondary; + + &:hover { + color: $color-text-primary; + } + } + } +} diff --git a/styles/scss/_mixins.scss b/styles/scss/_mixins.scss new file mode 100644 index 0000000..d259c50 --- /dev/null +++ b/styles/scss/_mixins.scss @@ -0,0 +1,200 @@ +// Vagabond RPG - SCSS Mixins +// =========================== + +// Flexbox shortcuts +@mixin flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +@mixin flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +@mixin flex-column { + display: flex; + flex-direction: column; +} + +// Grid shortcuts +@mixin grid($columns: 1, $gap: $spacing-4) { + display: grid; + grid-template-columns: repeat($columns, 1fr); + gap: $gap; +} + +// Typography +@mixin heading($size: $font-size-xl) { + font-family: $font-family-header; + font-size: $size; + font-weight: $font-weight-bold; + line-height: $line-height-tight; + color: $color-text-primary; +} + +@mixin body-text($size: $font-size-base) { + font-family: $font-family-body; + font-size: $size; + font-weight: $font-weight-normal; + line-height: $line-height-normal; + color: $color-text-primary; +} + +// Buttons +@mixin button-base { + display: inline-flex; + align-items: center; + justify-content: center; + padding: $spacing-2 $spacing-4; + font-family: $font-family-body; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + line-height: 1; + text-decoration: none; + border: 1px solid transparent; + border-radius: $radius-md; + cursor: pointer; + transition: all $transition-fast; + + &:focus { + outline: 2px solid $color-accent-primary; + outline-offset: 2px; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +@mixin button-primary { + @include button-base; + background-color: $color-accent-primary; + color: $color-text-inverse; + border-color: $color-accent-secondary; + + &:hover:not(:disabled) { + background-color: $color-accent-secondary; + } +} + +@mixin button-secondary { + @include button-base; + background-color: transparent; + color: $color-text-primary; + border-color: $color-border; + + &:hover:not(:disabled) { + background-color: $color-parchment-dark; + } +} + +// Form inputs +@mixin input-base { + padding: $spacing-2 $spacing-3; + font-family: $font-family-body; + font-size: $font-size-base; + color: $color-text-primary; + background-color: $color-parchment-light; + border: 1px solid $color-border; + border-radius: $radius-md; + transition: border-color $transition-fast; + + &:focus { + outline: none; + border-color: $color-accent-primary; + box-shadow: 0 0 0 2px rgba($color-accent-primary, 0.2); + } + + &::placeholder { + color: $color-text-muted; + } + + &:disabled { + background-color: $color-parchment-darker; + cursor: not-allowed; + } +} + +// Cards/Panels +@mixin panel { + background-color: $color-parchment; + border: 1px solid $color-border; + border-radius: $radius-md; + box-shadow: 0 1px 3px $shadow-light; +} + +@mixin panel-header { + @include flex-between; + padding: $spacing-3 $spacing-4; + background-color: $color-parchment-dark; + border-bottom: 1px solid $color-border; + border-radius: $radius-md $radius-md 0 0; +} + +// Scrollbar styling +@mixin custom-scrollbar { + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + &::-webkit-scrollbar-track { + background: $color-parchment-dark; + border-radius: $radius-full; + } + + &::-webkit-scrollbar-thumb { + background: $color-border; + border-radius: $radius-full; + + &:hover { + background: $color-border-dark; + } + } +} + +// Accessibility - focus visible +@mixin focus-visible { + &:focus-visible { + outline: 2px solid $color-accent-primary; + outline-offset: 2px; + } +} + +// Screen reader only (visually hidden but accessible) +@mixin sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +// Truncate text with ellipsis +@mixin truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +// Stat badge (for the large stat numbers) +@mixin stat-badge($color: $color-text-primary) { + @include flex-center; + width: 3rem; + height: 3rem; + font-family: $font-family-header; + font-size: $font-size-3xl; + font-weight: $font-weight-bold; + color: $color; + background-color: $color-parchment-light; + border: 2px solid $color-border; + border-radius: $radius-md; +} diff --git a/styles/scss/_variables.scss b/styles/scss/_variables.scss new file mode 100644 index 0000000..d11f458 --- /dev/null +++ b/styles/scss/_variables.scss @@ -0,0 +1,102 @@ +// Vagabond RPG - SCSS Variables +// ============================== + +// Color Palette - Parchment Theme +// Designed for WCAG AA accessibility (4.5:1 contrast ratio minimum) + +// Background colors (parchment tones) +$color-parchment-light: #f5f0e1; +$color-parchment: #e8dcc8; +$color-parchment-dark: #d4c4a8; +$color-parchment-darker: #c4b08c; + +// Text colors (high contrast) +$color-text-primary: #2c2416; // Main text - dark brown/black +$color-text-secondary: #4a3f2f; // Secondary text +$color-text-muted: #6b5d4d; // Muted/disabled text +$color-text-inverse: #f5f0e1; // Text on dark backgrounds + +// Accent colors +$color-accent-primary: #8b4513; // Saddle brown - primary actions +$color-accent-secondary: #654321; // Dark brown - secondary +$color-accent-highlight: #cd853f; // Peru - highlights/hover + +// Semantic colors +$color-success: #2d5a27; // Dark green - success/healing +$color-danger: #8b0000; // Dark red - damage/danger +$color-warning: #b8860b; // Dark goldenrod - warnings +$color-info: #2f4f4f; // Dark slate gray - info + +// Stat colors (for visual distinction) +$color-might: #8b0000; // Red - strength/power +$color-dexterity: #228b22; // Green - agility +$color-awareness: #4169e1; // Royal blue - perception +$color-reason: #9932cc; // Purple - intellect +$color-presence: #daa520; // Goldenrod - charisma +$color-luck: #20b2aa; // Light sea green - fortune + +// Borders +$color-border: #8b7355; +$color-border-light: #a08060; +$color-border-dark: #5c4a3a; + +// Shadows +$shadow-light: rgba(0, 0, 0, 0.1); +$shadow-medium: rgba(0, 0, 0, 0.2); +$shadow-dark: rgba(0, 0, 0, 0.3); + +// Typography +$font-family-header: 'Modesto Condensed', 'Roboto Slab', 'Georgia', serif; +$font-family-body: 'Signika', 'Roboto', 'Helvetica Neue', sans-serif; +$font-family-mono: 'Roboto Mono', 'Consolas', monospace; + +// Font sizes (using rem for accessibility) +$font-size-xs: 0.75rem; // 12px +$font-size-sm: 0.875rem; // 14px +$font-size-base: 1rem; // 16px +$font-size-lg: 1.125rem; // 18px +$font-size-xl: 1.25rem; // 20px +$font-size-2xl: 1.5rem; // 24px +$font-size-3xl: 1.875rem; // 30px +$font-size-4xl: 2.25rem; // 36px + +// Font weights +$font-weight-normal: 400; +$font-weight-medium: 500; +$font-weight-semibold: 600; +$font-weight-bold: 700; + +// Line heights +$line-height-tight: 1.25; +$line-height-normal: 1.5; +$line-height-relaxed: 1.75; + +// Spacing scale +$spacing-0: 0; +$spacing-1: 0.25rem; // 4px +$spacing-2: 0.5rem; // 8px +$spacing-3: 0.75rem; // 12px +$spacing-4: 1rem; // 16px +$spacing-5: 1.25rem; // 20px +$spacing-6: 1.5rem; // 24px +$spacing-8: 2rem; // 32px +$spacing-10: 2.5rem; // 40px + +// Border radius +$radius-sm: 2px; +$radius-md: 4px; +$radius-lg: 8px; +$radius-full: 9999px; + +// Transitions +$transition-fast: 150ms ease; +$transition-base: 250ms ease; +$transition-slow: 350ms ease; + +// Z-index scale +$z-dropdown: 100; +$z-sticky: 200; +$z-fixed: 300; +$z-modal-backdrop: 400; +$z-modal: 500; +$z-tooltip: 600; diff --git a/styles/scss/chat/_chat-cards.scss b/styles/scss/chat/_chat-cards.scss new file mode 100644 index 0000000..546adb6 --- /dev/null +++ b/styles/scss/chat/_chat-cards.scss @@ -0,0 +1,153 @@ +// Vagabond RPG - Chat Card Styles +// ================================ + +// Placeholder - will be expanded in Phase 7 +.vagabond.chat-card { + @include panel; + overflow: hidden; + + // Card header + .card-header { + @include flex-between; + padding: $spacing-2 $spacing-3; + background-color: $color-parchment-dark; + border-bottom: 1px solid $color-border; + + .card-title { + font-family: $font-family-header; + font-size: $font-size-base; + font-weight: $font-weight-bold; + } + + .card-subtitle { + font-size: $font-size-sm; + color: $color-text-muted; + } + } + + // Card content + .card-content { + padding: $spacing-3; + } + + // Roll result + .roll-result { + @include flex-center; + gap: $spacing-3; + padding: $spacing-3; + background-color: $color-parchment-light; + border-radius: $radius-md; + + .roll-total { + font-family: $font-family-header; + font-size: $font-size-3xl; + font-weight: $font-weight-bold; + } + + .roll-formula { + font-size: $font-size-sm; + color: $color-text-muted; + } + } + + // Result status + .result-status { + @include flex-center; + padding: $spacing-2; + font-weight: $font-weight-bold; + text-transform: uppercase; + letter-spacing: 0.1em; + border-radius: $radius-md; + + &.success { + background-color: rgba($color-success, 0.2); + color: $color-success; + } + + &.failure { + background-color: rgba($color-danger, 0.2); + color: $color-danger; + } + + &.critical { + background-color: rgba($color-warning, 0.2); + color: $color-warning; + animation: pulse 1s ease-in-out; + } + } + + // Damage display + .damage-display { + @include flex-center; + gap: $spacing-2; + margin-top: $spacing-3; + padding: $spacing-2; + background-color: rgba($color-danger, 0.1); + border: 1px solid $color-danger; + border-radius: $radius-md; + + .damage-label { + font-size: $font-size-sm; + color: $color-text-secondary; + } + + .damage-value { + font-family: $font-family-header; + font-size: $font-size-xl; + font-weight: $font-weight-bold; + color: $color-danger; + } + + .damage-type { + font-size: $font-size-sm; + color: $color-text-muted; + } + } + + // Card buttons (for interactive cards) + .card-buttons { + display: flex; + gap: $spacing-2; + padding: $spacing-3; + border-top: 1px solid $color-border; + + button { + flex: 1; + } + } +} + +// Spell card specific +.vagabond.chat-card.spell-card { + .spell-effect { + padding: $spacing-3; + font-style: italic; + border-left: 3px solid $color-accent-primary; + background-color: $color-parchment-light; + margin: $spacing-3 0; + } + + .spell-meta { + @include grid(2, $spacing-2); + font-size: $font-size-sm; + + .meta-item { + @include flex-between; + padding: $spacing-1; + + .meta-label { + color: $color-text-muted; + } + } + } +} + +// Animation +@keyframes pulse { + 0%, 100% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } +} diff --git a/styles/scss/components/_buttons.scss b/styles/scss/components/_buttons.scss new file mode 100644 index 0000000..aeb0185 --- /dev/null +++ b/styles/scss/components/_buttons.scss @@ -0,0 +1,108 @@ +// Vagabond RPG - Button Styles +// ============================= + +.vagabond { + // Primary button + .btn-primary { + @include button-primary; + } + + // Secondary button + .btn-secondary { + @include button-secondary; + } + + // Small button variant + .btn-sm { + padding: $spacing-1 $spacing-2; + font-size: $font-size-xs; + } + + // Large button variant + .btn-lg { + padding: $spacing-3 $spacing-6; + font-size: $font-size-lg; + } + + // Icon button (square, for icons only) + .btn-icon { + @include button-base; + @include flex-center; + width: 2rem; + height: 2rem; + padding: 0; + background-color: transparent; + border-color: transparent; + + &:hover:not(:disabled) { + background-color: $color-parchment-dark; + } + + i { + font-size: $font-size-base; + } + } + + // Rollable button (for dice rolls) + .btn-roll { + @include button-base; + background-color: $color-parchment-light; + color: $color-text-primary; + border-color: $color-border; + + &:hover:not(:disabled) { + background-color: $color-accent-highlight; + border-color: $color-accent-primary; + } + + i { + margin-right: $spacing-2; + } + } + + // Danger button + .btn-danger { + @include button-base; + background-color: $color-danger; + color: $color-text-inverse; + border-color: darken($color-danger, 10%); + + &:hover:not(:disabled) { + background-color: darken($color-danger, 10%); + } + } + + // Success button + .btn-success { + @include button-base; + background-color: $color-success; + color: $color-text-inverse; + border-color: darken($color-success, 10%); + + &:hover:not(:disabled) { + background-color: darken($color-success, 10%); + } + } + + // Button group + .btn-group { + display: inline-flex; + + .btn-primary, + .btn-secondary { + border-radius: 0; + + &:first-child { + border-radius: $radius-md 0 0 $radius-md; + } + + &:last-child { + border-radius: 0 $radius-md $radius-md 0; + } + + &:not(:last-child) { + border-right: none; + } + } + } +} diff --git a/styles/scss/components/_forms.scss b/styles/scss/components/_forms.scss new file mode 100644 index 0000000..0d55697 --- /dev/null +++ b/styles/scss/components/_forms.scss @@ -0,0 +1,181 @@ +// Vagabond RPG - Form Styles +// =========================== + +.vagabond { + // Text input + input[type="text"], + input[type="number"], + input[type="email"], + input[type="password"], + input[type="search"] { + @include input-base; + width: 100%; + } + + // Number input (smaller for stats) + input[type="number"] { + text-align: center; + + // Hide spinner buttons + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + -moz-appearance: textfield; + } + + // Textarea + textarea { + @include input-base; + width: 100%; + min-height: 100px; + resize: vertical; + } + + // Select + select { + @include input-base; + width: 100%; + cursor: pointer; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%232c2416' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right $spacing-3 center; + padding-right: $spacing-8; + } + + // Checkbox + input[type="checkbox"] { + width: 1rem; + height: 1rem; + margin: 0; + cursor: pointer; + accent-color: $color-accent-primary; + + @include focus-visible; + } + + // Checkbox with label + .checkbox-group { + display: flex; + align-items: center; + gap: $spacing-2; + + label { + cursor: pointer; + } + } + + // Radio button + input[type="radio"] { + width: 1rem; + height: 1rem; + margin: 0; + cursor: pointer; + accent-color: $color-accent-primary; + + @include focus-visible; + } + + // Form group (label + input) + .form-group { + margin-bottom: $spacing-4; + + label { + display: block; + margin-bottom: $spacing-1; + font-weight: $font-weight-medium; + color: $color-text-secondary; + } + + &.inline { + @include flex-between; + + label { + margin-bottom: 0; + margin-right: $spacing-2; + } + + input, select { + flex: 1; + max-width: 200px; + } + } + } + + // Form row (multiple inputs side by side) + .form-row { + display: flex; + gap: $spacing-4; + + .form-group { + flex: 1; + } + } + + // Stat input (large centered number) + .stat-input { + @include stat-badge; + + input { + width: 100%; + height: 100%; + padding: 0; + font-family: $font-family-header; + font-size: $font-size-3xl; + font-weight: $font-weight-bold; + text-align: center; + background: transparent; + border: none; + + &:focus { + outline: none; + } + } + } + + // Resource input (current / max) + .resource-input { + display: flex; + align-items: center; + gap: $spacing-1; + + input { + width: 3rem; + text-align: center; + } + + .separator { + color: $color-text-muted; + } + } + + // Inline editable field + .inline-edit { + padding: $spacing-1 $spacing-2; + background: transparent; + border: 1px solid transparent; + border-radius: $radius-sm; + transition: all $transition-fast; + + &:hover { + border-color: $color-border-light; + } + + &:focus { + background: $color-parchment-light; + border-color: $color-accent-primary; + outline: none; + } + } + + // Readonly display (looks like text, not input) + .readonly-value { + padding: $spacing-2 $spacing-3; + background-color: $color-parchment-dark; + border: 1px solid $color-border-light; + border-radius: $radius-md; + color: $color-text-secondary; + } +} diff --git a/styles/scss/components/_tabs.scss b/styles/scss/components/_tabs.scss new file mode 100644 index 0000000..338bb9b --- /dev/null +++ b/styles/scss/components/_tabs.scss @@ -0,0 +1,111 @@ +// Vagabond RPG - Tab Styles +// ========================== + +.vagabond { + // Tab navigation + .sheet-tabs { + display: flex; + gap: 0; + margin: 0; + padding: 0; + list-style: none; + border-bottom: 2px solid $color-border; + background-color: $color-parchment-dark; + + .item { + margin: 0; + padding: $spacing-2 $spacing-4; + font-family: $font-family-header; + font-size: $font-size-base; + font-weight: $font-weight-medium; + color: $color-text-secondary; + background-color: transparent; + border: none; + border-bottom: 2px solid transparent; + margin-bottom: -2px; + cursor: pointer; + transition: all $transition-fast; + + &:hover { + color: $color-text-primary; + background-color: $color-parchment; + } + + &.active { + color: $color-text-primary; + background-color: $color-parchment; + border-bottom-color: $color-accent-primary; + } + + @include focus-visible; + + i { + margin-right: $spacing-2; + } + } + } + + // Tab content area + .sheet-body { + @include custom-scrollbar; + overflow-y: auto; + padding: $spacing-4; + + .tab { + display: none; + + &.active { + display: block; + } + } + } + + // Vertical tabs variant + .sheet-tabs.vertical { + flex-direction: column; + border-bottom: none; + border-right: 2px solid $color-border; + + .item { + border-bottom: none; + border-right: 2px solid transparent; + margin-bottom: 0; + margin-right: -2px; + + &.active { + border-right-color: $color-accent-primary; + } + } + } + + // Sub-tabs (within a tab) + .sub-tabs { + display: flex; + gap: $spacing-2; + margin-bottom: $spacing-4; + padding-bottom: $spacing-2; + border-bottom: 1px solid $color-border-light; + + .sub-tab { + padding: $spacing-1 $spacing-3; + font-size: $font-size-sm; + color: $color-text-muted; + background-color: transparent; + border: 1px solid transparent; + border-radius: $radius-md; + cursor: pointer; + transition: all $transition-fast; + + &:hover { + color: $color-text-primary; + background-color: $color-parchment-dark; + } + + &.active { + color: $color-text-primary; + background-color: $color-parchment-dark; + border-color: $color-border; + } + } + } +} diff --git a/styles/scss/dialogs/_roll-dialog.scss b/styles/scss/dialogs/_roll-dialog.scss new file mode 100644 index 0000000..1e19946 --- /dev/null +++ b/styles/scss/dialogs/_roll-dialog.scss @@ -0,0 +1,154 @@ +// Vagabond RPG - Roll Dialog Styles +// ================================== + +// Placeholder - will be expanded in Phase 7 +.vagabond.dialog.roll-dialog { + .dialog-content { + padding: $spacing-4; + } + + // Roll info section + .roll-info { + @include panel; + padding: $spacing-3; + margin-bottom: $spacing-4; + + .difficulty-display { + @include flex-center; + gap: $spacing-4; + + .difficulty-value { + font-family: $font-family-header; + font-size: $font-size-3xl; + font-weight: $font-weight-bold; + } + + .crit-threshold { + font-size: $font-size-sm; + color: $color-text-muted; + + .crit-value { + color: $color-accent-primary; + font-weight: $font-weight-bold; + } + } + } + } + + // Favor/Hinder toggles + .modifiers { + @include flex-center; + gap: $spacing-4; + margin-bottom: $spacing-4; + + .modifier-toggle { + @include flex-center; + gap: $spacing-2; + + label { + font-weight: $font-weight-medium; + } + + &.favor .toggle-active { + color: $color-success; + } + + &.hinder .toggle-active { + color: $color-danger; + } + } + } + + // Situational modifier + .situational-modifier { + @include flex-center; + gap: $spacing-2; + margin-bottom: $spacing-4; + + input { + width: 60px; + text-align: center; + } + } + + // Roll button + .roll-button { + @include button-primary; + width: 100%; + padding: $spacing-3; + font-size: $font-size-lg; + + i { + margin-right: $spacing-2; + } + } +} + +// Spell cast dialog specific +.vagabond.dialog.spell-cast-dialog { + .mana-cost { + @include panel; + @include flex-between; + padding: $spacing-3; + margin-bottom: $spacing-4; + + .cost-label { + font-weight: $font-weight-semibold; + } + + .cost-value { + font-family: $font-family-header; + font-size: $font-size-xl; + font-weight: $font-weight-bold; + color: $color-accent-primary; + } + + &.insufficient { + border-color: $color-danger; + + .cost-value { + color: $color-danger; + } + } + } + + .delivery-selection, + .duration-selection, + .damage-selection { + margin-bottom: $spacing-4; + + .selection-label { + font-weight: $font-weight-semibold; + margin-bottom: $spacing-2; + } + + .selection-grid { + @include grid(2, $spacing-2); + } + + .selection-option { + @include panel; + padding: $spacing-2; + cursor: pointer; + transition: all $transition-fast; + + &:hover { + border-color: $color-accent-primary; + } + + &.selected { + background-color: $color-parchment-dark; + border-color: $color-accent-primary; + } + + .option-name { + font-weight: $font-weight-medium; + } + + .option-cost { + font-size: $font-size-sm; + color: $color-text-muted; + } + } + } +} diff --git a/styles/scss/sheets/_actor-sheet.scss b/styles/scss/sheets/_actor-sheet.scss new file mode 100644 index 0000000..206f724 --- /dev/null +++ b/styles/scss/sheets/_actor-sheet.scss @@ -0,0 +1,83 @@ +// Vagabond RPG - Actor Sheet Styles +// ================================== + +// Placeholder - will be expanded in Phase 3 +.vagabond.sheet.actor { + min-width: 600px; + min-height: 500px; + + .sheet-header { + @include flex-between; + padding: $spacing-4; + background-color: $color-parchment-dark; + border-bottom: 2px solid $color-border; + + .profile-img { + width: 100px; + height: 100px; + object-fit: cover; + border: 2px solid $color-border; + border-radius: $radius-md; + } + + .header-fields { + flex: 1; + margin-left: $spacing-4; + } + } +} + +// Character sheet specific +.vagabond.sheet.actor.character { + // Stats column (left side, matching official sheet) + .stats-column { + @include flex-column; + gap: $spacing-3; + padding: $spacing-4; + background-color: $color-parchment-dark; + border-right: 2px solid $color-border; + + .stat-block { + text-align: center; + + .stat-label { + font-family: $font-family-header; + font-size: $font-size-xs; + font-weight: $font-weight-bold; + text-transform: uppercase; + letter-spacing: 0.05em; + color: $color-text-secondary; + margin-bottom: $spacing-1; + } + + .stat-value { + @include stat-badge; + margin: 0 auto; + } + } + } +} + +// NPC/Monster sheet specific +.vagabond.sheet.actor.npc { + min-width: 400px; + + .stat-block-display { + @include panel; + padding: $spacing-4; + + .stat-line { + @include flex-between; + padding: $spacing-1 0; + border-bottom: 1px solid $color-border-light; + + &:last-child { + border-bottom: none; + } + + .label { + font-weight: $font-weight-semibold; + } + } + } +} diff --git a/styles/scss/sheets/_item-sheet.scss b/styles/scss/sheets/_item-sheet.scss new file mode 100644 index 0000000..9a486e6 --- /dev/null +++ b/styles/scss/sheets/_item-sheet.scss @@ -0,0 +1,101 @@ +// Vagabond RPG - Item Sheet Styles +// ================================= + +// Placeholder - will be expanded in Phase 4 +.vagabond.sheet.item { + min-width: 400px; + min-height: 300px; + + .sheet-header { + @include flex-between; + padding: $spacing-4; + background-color: $color-parchment-dark; + border-bottom: 2px solid $color-border; + + .item-img { + width: 60px; + height: 60px; + object-fit: cover; + border: 2px solid $color-border; + border-radius: $radius-md; + } + + .header-fields { + flex: 1; + margin-left: $spacing-4; + } + } + + .sheet-body { + padding: $spacing-4; + } + + // Item description + .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; + background-color: $color-parchment-dark; + border: 1px solid $color-border; + border-radius: $radius-full; + } + } +} + +// Class item specific +.vagabond.sheet.item.class { + .progression-table { + width: 100%; + margin-bottom: $spacing-4; + + th { + position: sticky; + top: 0; + background-color: $color-parchment-dark; + } + } +} diff --git a/styles/scss/vagabond.scss b/styles/scss/vagabond.scss new file mode 100644 index 0000000..89a734f --- /dev/null +++ b/styles/scss/vagabond.scss @@ -0,0 +1,24 @@ +// Vagabond RPG - Foundry VTT System Styles +// ========================================== + +// Configuration +@import 'variables'; +@import 'mixins'; + +// Base styles +@import 'base'; + +// Components +@import 'components/buttons'; +@import 'components/forms'; +@import 'components/tabs'; + +// Sheets +@import 'sheets/actor-sheet'; +@import 'sheets/item-sheet'; + +// Dialogs +@import 'dialogs/roll-dialog'; + +// Chat +@import 'chat/chat-cards'; diff --git a/system.json b/system.json new file mode 100644 index 0000000..3d1c179 --- /dev/null +++ b/system.json @@ -0,0 +1,127 @@ +{ + "id": "vagabond", + "title": "Vagabond RPG", + "description": "A Foundry VTT system implementation for Vagabond RPG - Pulp Fantasy Roleplaying", + "version": "0.1.0", + "compatibility": { + "minimum": "13", + "verified": "13", + "maximum": "13" + }, + "authors": [ + { + "name": "Cal Corum", + "email": "cal.corum@gmail.com" + } + ], + "esmodules": ["module/vagabond.mjs"], + "styles": ["styles/vagabond.css"], + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json" + } + ], + "packs": [ + { + "name": "ancestries", + "label": "Ancestries", + "path": "packs/ancestries", + "type": "Item", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "classes", + "label": "Classes", + "path": "packs/classes", + "type": "Item", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "spells", + "label": "Spells", + "path": "packs/spells", + "type": "Item", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "perks", + "label": "Perks", + "path": "packs/perks", + "type": "Item", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "weapons", + "label": "Weapons", + "path": "packs/weapons", + "type": "Item", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "armor", + "label": "Armor", + "path": "packs/armor", + "type": "Item", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "equipment", + "label": "Equipment", + "path": "packs/equipment", + "type": "Item", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "bestiary", + "label": "Bestiary", + "path": "packs/bestiary", + "type": "Actor", + "ownership": { + "PLAYER": "NONE", + "ASSISTANT": "OWNER" + } + } + ], + "packFolders": [ + { + "name": "Vagabond RPG", + "sorting": "a", + "packs": ["ancestries", "classes", "spells", "perks", "weapons", "armor", "equipment", "bestiary"] + } + ], + "socket": false, + "url": "https://github.com/calcorum/vagabond-rpg-foundryvtt", + "manifest": "https://github.com/calcorum/vagabond-rpg-foundryvtt/releases/latest/download/system.json", + "download": "https://github.com/calcorum/vagabond-rpg-foundryvtt/releases/latest/download/vagabond.zip", + "license": "LICENSE", + "readme": "README.md", + "bugs": "https://github.com/calcorum/vagabond-rpg-foundryvtt/issues", + "changelog": "CHANGELOG.md", + "primaryTokenAttribute": "resources.hp", + "secondaryTokenAttribute": "resources.mana", + "gridDistance": 5, + "gridUnits": "ft" +}