Initial project setup for Vagabond RPG Foundry VTT system
- 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 <noreply@anthropic.com>
This commit is contained in:
commit
37300ccf90
12
.env.example
Normal file
12
.env.example
Normal file
@ -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
|
||||||
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@ -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
|
||||||
86
CLAUDE.md
Normal file
86
CLAUDE.md
Normal file
@ -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
|
||||||
28
LICENSE
Normal file
28
LICENSE
Normal file
@ -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.
|
||||||
1034
PROJECT_ROADMAP.json
Normal file
1034
PROJECT_ROADMAP.json
Normal file
File diff suppressed because it is too large
Load Diff
110
README.md
Normal file
110
README.md
Normal file
@ -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
|
||||||
28
docker-compose.yml
Normal file
28
docker-compose.yml
Normal file
@ -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
|
||||||
213
lang/en.json
Normal file
213
lang/en.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
200
module/helpers/config.mjs
Normal file
200
module/helpers/config.mjs
Normal file
@ -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;
|
||||||
139
module/vagabond.mjs
Normal file
139
module/vagabond.mjs
Normal file
@ -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");
|
||||||
|
});
|
||||||
|
}
|
||||||
26
package.json
Normal file
26
package.json
Normal file
@ -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 <cal.corum@gmail.com>",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
156
styles/scss/_base.scss
Normal file
156
styles/scss/_base.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
200
styles/scss/_mixins.scss
Normal file
200
styles/scss/_mixins.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
102
styles/scss/_variables.scss
Normal file
102
styles/scss/_variables.scss
Normal file
@ -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;
|
||||||
153
styles/scss/chat/_chat-cards.scss
Normal file
153
styles/scss/chat/_chat-cards.scss
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
108
styles/scss/components/_buttons.scss
Normal file
108
styles/scss/components/_buttons.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
181
styles/scss/components/_forms.scss
Normal file
181
styles/scss/components/_forms.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
111
styles/scss/components/_tabs.scss
Normal file
111
styles/scss/components/_tabs.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
154
styles/scss/dialogs/_roll-dialog.scss
Normal file
154
styles/scss/dialogs/_roll-dialog.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
styles/scss/sheets/_actor-sheet.scss
Normal file
83
styles/scss/sheets/_actor-sheet.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
101
styles/scss/sheets/_item-sheet.scss
Normal file
101
styles/scss/sheets/_item-sheet.scss
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
styles/scss/vagabond.scss
Normal file
24
styles/scss/vagabond.scss
Normal file
@ -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';
|
||||||
127
system.json
Normal file
127
system.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user