10 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
HikeMap is a location-based RPG web application with trail/GPS track editing capabilities. Players walk in the real world to explore, battle monsters, and level up their characters. The frontend is a single-page application in index.html with a Node.js/Express backend in server.js.
Development
Docker (Recommended)
docker-compose up -d
Access at http://localhost:880
Local Development
npm install
node server.js
Then open http://localhost:8080
Architecture
File Structure
| File | Purpose |
|---|---|
index.html |
Frontend SPA (CSS, HTML, JavaScript) |
server.js |
Express API server, WebSocket handling |
database.js |
SQLite database layer (better-sqlite3) |
docker-compose.yml |
Container configuration |
hikemap.db |
SQLite database (auto-created) |
SKILLS.md |
Complete skills documentation |
Key Libraries
- Frontend: Leaflet.js 1.9.4, leaflet-rotate 0.2.8
- Backend: Express, better-sqlite3, jsonwebtoken, ws (WebSocket)
RPG System
Core Concepts
Character Creation:
- Player chooses Race (Human, Elf, Dwarf, Halfling) and Class (Trail Runner)
- Race provides stat bonuses, Class determines skills and growth
Stats:
| Stat | Description |
|---|---|
| HP | Health points - reach 0 and you die |
| MP | Mana points - used for skills |
| ATK | Attack power |
| DEF | Defense (reduces damage taken) |
| Accuracy | Hit chance modifier |
| Dodge | Evasion chance |
Home Base:
- Set by player, 20-meter radius (
HOME_BASE_RADIUS) - 3x HP regeneration when at home
- Required to respawn after death
- Skill loadout can only be changed at home
Combat System
Monster Spawning:
- Monsters spawn while walking (configurable distance)
- No spawns within home base radius
- Multiple monsters can accumulate (entourage)
Combat Flow:
- Player taps monster to engage
- Turn-based combat with skill selection
- Hit/miss calculated per skill accuracy
- XP awarded on victory
- Death sends player to respawn state
Damage Formula:
damage = (skillPower * playerATK / 100) - enemyDEF
hitChance = skillAccuracy + (attackerAccuracy - 90) - defenderDodge
Skill System
Skill Tiers: Skills unlock at level milestones (2, 3, 4, 5, 6, 7)
Skill Types:
damage: Deal damage to enemiesheal: Restore HPbuff: Temporary stat boosts (defense, accuracy)utility: Out-of-combat effects (MP regen boost)status: Apply effects like poison
Loadout System:
- On level-up: ALL offered skills unlock, player picks one to activate
unlockedSkills: Everything learnedactiveSkills: Currently equipped (one per tier)- Swap active skills at home base only
See SKILLS.md for complete skill reference.
Classes
Trail Runner (currently the only class):
| Base Stat | Value | Per Level |
|---|---|---|
| HP | 100 | +10 |
| MP | 50 | +5 |
| ATK | 12 | +2 |
| DEF | 8 | +1 |
| Accuracy | 90 | - |
| Dodge | 15 | - |
Races
| Race | HP | MP | ATK | DEF |
|---|---|---|---|---|
| Human | +5 | +5 | +0 | +0 |
| Elf | -5 | +15 | +0 | -2 |
| Dwarf | +15 | -5 | +0 | +3 |
| Halfling | -5 | +0 | +2 | +5 |
Monsters
Current Monster Types:
| Monster | Level Range | Notes |
|---|---|---|
| Moop | 1-5 | Basic enemy |
| Fancy Moop | 1-5 | Higher ATK |
| Fanciest Moop | 3-5 | Stronger variant |
| Sub Par Moop | 1-1 | Very weak, tutorial enemy |
MOOP = Matter Out Of Place (litter-themed enemies)
Database Schema (database.js)
Key Tables
Users & Auth:
users: id, username, password_hash, is_admin, created_at
RPG Stats:
rpg_stats: player_id, name, race, class, level, xp, hp, maxHp, mp, maxMp, atk, def, home_base_lat/lng, unlocked_skills, active_skills, is_dead, home_base_icon
Skills:
skills: id, name, description, type, mp_cost, base_power, accuracy, hit_count, target, status_effectclass_skills: Links skills to classes with unlock_level and choice_groupclass_skill_names: Class-specific skill renames
Monsters:
monster_types: id, name, icon, base_hp/atk/def, xp_reward, level scaling, dialogues
Buffs:
player_buffs: Active buffs with expiry times (e.g., Second Wind)
Important Methods
// database.js key methods
db.getRpgStats(userId) // Get player stats
db.saveRpgStats(userId, stats) // Save player stats
db.resetUserProgress(userId) // Admin: reset player
db.swapActiveSkill(userId, tier, newSkillId) // Swap loadout
API Endpoints (server.js)
Authentication
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Create account |
| POST | /api/auth/login |
Login, returns JWT |
RPG
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/user/has-character |
Check if character exists |
| POST | /api/user/character |
Create character |
| GET | /api/user/rpg-stats |
Get player stats |
| PUT | /api/user/rpg-stats |
Save player stats |
| POST | /api/user/swap-skill |
Swap active skill (at home) |
| POST | /api/user/activate-buff |
Activate utility skill |
| GET | /api/user/buffs |
Get active buffs |
Game Data
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/skills |
All skills |
| GET | /api/classes |
All classes with skills |
| GET | /api/monster-types |
All monster types |
| GET | /api/spawn-settings |
Monster spawn config |
Admin
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/admin/reset-progress/:userId |
Reset player |
| POST | /api/admin/reset-homebase/:userId |
Clear home base |
| PUT | /api/admin/spawn-settings |
Update spawn config |
Frontend Structure (index.html)
Line Ranges (approximate)
| Lines | Content |
|---|---|
| 1-3000 | CSS styles |
| 3000-3700 | HTML structure |
| 3700-4500 | Constants, state, skill definitions |
| 4500-10000 | Map, track editing, geocaches |
| 10000-11000 | Character creation, sheet |
| 11000-12000 | Stats management, saving |
| 12000-13500 | Combat system, monsters |
Key State Variables
playerStats // Player RPG data
combatState // Active combat info
monsterEntourage // Spawned monsters
statsLoadedFromServer // Prevents stale saves
pendingSkillChoice // Level-up skill selection
Key Functions
initializePlayerStats(username) // Load from server
savePlayerStats() // Save to server
showSkillChoiceModal(level) // Level-up skill pick
swapSkillFromHomebase(tier, id) // Change loadout
startCombat(monsters) // Begin combat
useSkill(skillId) // Execute skill in combat
Data Persistence
Server-Authoritative Model:
- On page load, fetch stats from server
statsLoadedFromServerflag prevents premature saves- Auto-save every 30 seconds after initial load
beforeunloadusessendBeaconfor reliable final save- localStorage used as read-only backup
Critical: Never save to server until statsLoadedFromServer = true
Common Tasks
Adding a New Skill
- Insert into
skillstable (database.js or admin panel) - Add to
class_skillswith unlock_level and choice_group - Optionally add class rename in
class_skill_names - Add icon/calculate function in
SKILLSconstant if special behavior needed
Adding a New Class
- Insert into
classestable - Create
class_skillsentries for skill progression - Add
class_skill_namesfor thematic renames - Update
getClassIcon()for display
Adding a New Monster
- Insert into
monster_typesvia admin panel or database - Set level range, stats, dialogues
- Optionally add to
monster_skillsfor special abilities
Troubleshooting
Docker: Restart vs Rebuild
Most source files are volume-mounted in docker-compose.yml, so changes are reflected without rebuilding.
Action required by file type:
| File | Action | Notes |
|---|---|---|
index.html |
Browser refresh | Volume-mounted, served fresh |
admin.html |
Browser refresh | Volume-mounted, served fresh |
service-worker.js |
Browser refresh + bump cache version | Volume-mounted |
server.js |
docker restart hikemap_hikemap_1 |
Volume-mounted, Node needs restart |
database.js |
docker restart hikemap_hikemap_1 |
Volume-mounted, Node needs restart |
mapgameimgs/* |
Browser refresh | Volume-mounted |
mapgamemusic/* |
Browser refresh | Volume-mounted |
sfx/* |
Browser refresh | Volume-mounted |
hikemap.db |
Nothing | Volume-mounted, persists outside container |
Files requiring full rebuild (docker-compose up -d --build):
| File | Why Rebuild? |
|---|---|
package.json |
New npm dependencies need installing |
Dockerfile |
Build process changes |
manifest.json |
Not volume-mounted |
icon-*.png |
Not volume-mounted |
.env |
Not volume-mounted |
.well-known/* |
Not volume-mounted |
default.kml |
Not volume-mounted |
docker-compose.yml |
Container config changes |
Commands:
# Restart container (fast - for server.js/database.js changes)
docker restart hikemap_hikemap_1
# Full rebuild (slow - only when needed)
docker-compose up -d --build
# If rebuild fails with volume errors:
docker ps -a | grep hike # Find container ID
docker rm <container_id>
docker-compose up -d --build
Cache Issues
After server-side changes, users may need to clear browser cache:
- Increment
CACHE_NAMEversion inservice-worker.js - Hard refresh in browser (Ctrl+Shift+R)
- Or clear site data in browser DevTools
Common Issues
"Changes not appearing after edit"
- Client files (html/js): Just refresh browser
- Server files (server.js, database.js): Run
docker restart hikemap_hikemap_1 - Did you bump the service worker cache version?
"API returns old data"
- Check container is running:
docker ps | grep hike - Restart container:
docker restart hikemap_hikemap_1 - Check container logs:
docker logs hikemap_hikemap_1
"Database changes lost after rebuild"
- The
hikemap.dbfile is mounted as a volume and persists - Schema changes are applied via migrations in
database.json startup