This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Proto23 is a browser-based text RPG game, deployed as a GitHub Pages site (23html.github.io). The game is split across src/main.ts (~4,670 lines), src/game/ (8 modules, ~1,260 lines), src/ui/ (9 modules, ~1,540 lines), src/data/ (13 modules, ~5,870 lines), and src/systems/ (4 modules, ~1,710 lines), bundled via esbuild to dist/bundle.js, which index.html loads. CSS is in styles.css.
index.html— shell HTML, loadsstyles.cssanddist/bundle.jssrc/main.ts— core game logic, DOM setup, location scripts (~4,670 lines)src/game/— 8 game logic modules (~1,260 lines):utils-game.ts— Game utility functions (formatw,cansee,kill,roll,canRead)progression.ts— XP/leveling (giveExp,giveSkExp,giveCrExp,giveTitle,giveRcp,lvlup,giveAction)economy.ts— Wealth/shopping (giveWealth,spend,restock)inventory.ts— Item management (giveItem,removeItem,giveFurniture, trunk/container functions)combat.ts— Combat system (fght,attack,tattack,dmg_calc,dumb,hit_calc,wpndiestt)movement.ts— Area transitions (smove,area_init,inSector,Effector,addtosector)crafting.ts— Recipe crafting (canMake,make)exploration.ts— Scouting/disassembly (canScout,scoutGeneric,disassembleGeneric)
src/ui/— 9 UI modules (~1,540 lines):messages.ts— Game log (msg,_msg,msg_add)descriptions.ts— Tooltip/description popups (dscr,addDesc,descsinfo)stats.ts— Stat display updates (updateStatDisplay,updateCombatDisplay,updateMonsterDisplay,updateWealthDisplay)effects.ts— Effect display (giveEff,removeEff)equipment.ts— Equipment slot display (equip,unequip,resetEquipDisplay)inventory.ts— Inventory rendering/sorting (renderItem,isort,rsort,reduce)choices.ts— Choice buttons and icons (chs,clr_chs,icon,Chs)panels.ts— Crafting/skill/action/furniture panels (renderRcp,renderSkl,renderAct,deactivateAct,renderFurniture,showFurniturePanel)shop.ts— Shop UI rendering (recshop,rendershopitem,buycbs,coinAnimation)
src/data/— 13 data definition modules (~5,870 lines): titles, effects, furniture, skills, items, equipment, abilities, creatures, world, crafting, vendors, actions, masterysrc/systems/— 4 system modules (~1,710 lines):weather.ts— Weather/time/calendar system, callbacks, season display (wdrseason) (~620 lines)save-load.ts— Save/load serialization (~880 lines)player.ts— Player (You) constructor (~110 lines)loop.ts— Game tick loop (ontick) (~50 lines)
src/state.ts— shared game state: grouped exports (data,gameText,flags,stats,combat,settings), namespace singletons (dom,global), setter functions (setYou,resetFlags, etc.)src/constants.ts,src/base64.ts,src/random.ts,src/utils.ts,src/dom-utils.ts— utility modulesstyles.css— extracted CSS (previously inline<style>block)build.mjs— esbuild build script (src/main.ts→dist/bundle.jsas IIFE)package.json— project config, scripts:build,watch,typechecktsconfig.json— TypeScript config (strict: true,allowJs: true,noEmit: true).vscode/launch.json— Chrome debug launch (builds first viapreLaunchTask).vscode/tasks.json— npm build task for VSCodedist/bundle.js— built output (gitignored)changelog/changelog.html— historical changelogctst.png— sprite sheet,laugh6.wav— sound effect,favicon.ico
ROADMAP.md— refactoring plan with checkboxes (Phases 1-5 complete, Phase 6.1+6.3-6.5 complete, Phase 6.2+7-8 future)CLASS_MAP.md— CSS class rename mapping (cryptic → semantic, pending application)frontend-refactoring.md— CSS design token and component class analysis (future work)src/types.ts— 23 entity interfaces + 8 state interfaces (Player, Item, Creature, Effect, Skill, Area, Equipment, Flags, Stats, CombatState, Settings, etc.)
The game uses plain JS objects as namespaces (not modules). Key globals defined at the top of the script:
| Object | Purpose |
|---|---|
you |
Player character state (stats, equipment, inventory, effects) |
global |
Miscellaneous game-wide state (~62 remaining properties) |
data |
Grouped registry export: { creature, item, wpn, eqp, acc, sld, rcp, skl, effect, area, sector, furniture, vendor, quest, act, abl, container, ttl, mastery } |
gameText |
Read-only display constants: nt (number suffixes), wecs (rarity colors), lunarp (moon phases), eranks (ranking labels) |
flags |
Game state flags: btl, monsterFreeze, civil, sleepmode, pauseNextBattle, criticalHit, etc. (26 boolean/numeric flags) |
stats |
Gameplay statistics: tick, allKills, foodAttempts, moneyGained, deathsInCombat, etc. (~50 counters) |
combat |
Ephemeral combat state: currentMonster, currentLocation, currentZone, attackDamageFromMonster, hitAccuracy, keyTarget, etc. (not serialized) |
settings |
User-configurable preferences: sortMode, recipeSortMode, msgs_max, fps, timescale, home_loc, bg_r/g/b |
dom |
DOM element references |
creature |
Monster/NPC definitions |
area / sector |
Map zones and sector groupings |
item / wpn / eqp / sld / acc |
Item categories (general / weapon / equipment / shield / accessory) |
rcp |
Crafting recipes |
skl |
Skills |
effect |
Status effects |
vendor |
Shop/vendor definitions |
quest |
Quest definitions |
weather / w_manager |
Weather system |
timers |
setInterval references |
callback |
Event hook system |
container |
Storage containers (chests, bags) |
furniture |
Home furniture |
ttl |
Titles |
act |
Actions |
Game entities use constructor functions (e.g., Item(), Eqp(), Creature(), Area(), Skill(), Recipe(), Quest(), Action(), Vendor(), Effect(), Furniture(), Title(), Container(), Sector()). All constructors accept an optional cfg config object: if(cfg) for(let k in cfg) this[k]=cfg[k]. Delegate functions (.use, .oneq, .onuneq, .onDeath, .onGet, etc.) are included directly in the constructor config. Factory functions (foodItem(), healItem(), expItem()) create common item patterns with standardized .use functions. Remaining post-construction assignments are limited to sub-property access (.data.time, .eqp[0].aff), cross-module references (.dss), and conditional assignments.
Most data module delegates that previously imported and mutated the you singleton now receive the player as a parameter instead:
- Equipment (
oneq,onuneq,onDegrade):function(player: any) { player.mods.X += val } - Skills (milestone
f,onLevel,onGive):(player: any) => { player.str_bonus += 1 } - Effects (
use,un,mods,onGive,onRemove,onClick): player is first param - Items (
use,onGet):function(player: any) { ... } - Creatures (
onDeath): uses existingkillerparam (which IS the player) instead of importingyou - Actions/Furniture/Titles/Mastery/Abilities: delegates receive
playeras first param
Call sites pass you as the argument (e.g., w.oneq(you), skl.mlstn[ss].f(you)). Constructor defaults use _player: any for unused params. This removes you from imports in 9 of 13 data modules (equipment, effects, items, world, actions, furniture, mastery, titles, abilities).
Consuming modules (game/, ui/, systems/, main.ts) import grouped exports and destructure:
import { dom, global, you, data, flags, stats, combat, settings, gameText } from '../state';
const { creature, item, wpn, skl } = data;Data modules (src/data/*.ts) import individual registry vars directly since they populate them:
import { creature, item, wpn, effect, skl } from '../state';Setter functions are used for objects that need full reassignment in save/load: resetFlags(v?), setYou(v), setTime(v), etc.
save()serializes game state into a pipe-delimited (|) string of JSON segments, base64-encodes it, and stores inlocalStorageunder key"v0.3"load()reverses the process — segments are split on|and parsed in order- Adding new save fields requires appending to both
save()andload()in matching order - Helpers:
serializeIdData()for save,loadEquipCategory()andrestoreDiscovery()for load - Rename migration: After Phase 6.1, save keys use new names but old saves still have old keys.
load()uses key-mapping objects (PLAYER_KEY_MAP,STATS_KEY_MAP,FLAGS_KEY_MAP,MODS_KEY_MAP) to translate old serialized keys to new runtime property names. When renaming a serialized property, add the old→new mapping to the appropriate map
- Combat (
game/combat.ts):fght(),attack(),tattack(),dmg_calc(),hit_calc()— turn-based with stats, equipment, and effects - Inventory (
game/inventory.ts+ui/inventory.ts):giveItem(),removeItem(),equip(),unequip()— items tracked ininv[]array - Crafting (
game/crafting.ts):canMake(),make(),renderRcp()— recipe-based - Movement (
game/movement.ts):smove(),area_init()— transitions between areas, effector system - Time:
Time()constructor, constantsYEAR,MONTH,WEEK,DAY,HOUR(in minutes) - UI:
chs()for choice buttons,msg()/_msg()for game log,dscr()for description popups,addElement()for DOM creation - Weather:
wManager(),setWeather(), seasonal system - Effects:
giveEff(),removeEff()— buff/debuff system with types - Skills:
giveCrExp()for skill XP, milestone/perk system viamlstn
Copper-based: SILVER = 100, GOLD = 10000. Use giveWealth() / spend().
global.ver tracks the current game version (currently 470).
npm run build— bundlesrc/main.ts→dist/bundle.js(esbuild, IIFE format)npm run watch— rebuild on file changesnpm run typecheck— runtsc --noEmitfor type checking (0 errors; onlyrandom.tsuses@ts-nocheck— vendored MersenneTwister)
Edit files in src/, run npm run build (or use npm run watch), refresh index.html in browser. In VSCode, press F5 to build and launch in Chrome. The game uses localStorage for saves — clearing it resets progress. The game targets modern browsers and uses MS Gothic font.
All styles are in styles.css. Classes use short abbreviated names (.d, .dd, .bts, .chs, .inv_slot, etc.). Hover effects use background or background-color changes. Firefox-specific fixes are in @supports (-moz-appearance:none). See CLASS_MAP.md for the planned semantic rename mapping.