Files
impostor/CLAUDE.md
Zoltan Timar 3e31398d9d
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
added 89 ascension
2026-04-29 16:20:11 +02:00

7.9 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Definitely not an Impostor is a narrative-driven fantasy game built for TIC-80, a fantasy console. The game is written entirely in Lua. All source modules in inc/ are concatenated at build time into a single impostor.lua file that TIC-80 loads.

Build Commands

make build          # Concatenate inc/**/*.lua into impostor.lua (order from impostor.inc)
make minify         # Build then minify (downloads minify.lua if missing)
make lint           # Run luacheck with source mapping to original files
make watch          # Auto-rebuild on file changes in inc/
make export         # Export minified game to HTML and .tic formats
make import_assets  # Import PNG sprite/tile assets into the TIC-80 cartridge
make export_assets  # Extract TIC-80 asset sections into inc/meta/meta.assets.lua
make docs           # Generate documentation with ldoc
make clean          # Remove build artifacts

To run the game locally: tic80 --fs=. impostor.lua

VSCode tasks are available for "Run TIC80", "Build & Run TIC80", "Export assets", and "Make build".

There is no test framework — validation is done via make lint (luacheck).

Important Workflow Note

Do not run git add or git commit — git operations are the user's responsibility.

Code Conventions (from GEMINI.md)

  • Functions: PascalCase (e.g., UpdatePlayer, DrawHUD)
  • Variables: snake_case (e.g., player_x, game_state)
  • Constants: SCREAMING_SNAKE_CASE (e.g., MAX_SPEED)
  • Indentation: 2 spaces
  • Tables: Always multi-line with one key-value pair per line
  • Code sections: Delimited with --- @section SectionName comments
  • TIC-80 APIs: Use btn() for input, spr() for sprites, map() for tilemaps, Print.text() for text

Architecture

The game is a state machine driven by a window manager. The build order is defined in impostor.inc — 99 source files are concatenated in dependency order.

Main Loop

TIC() in inc/system/system.main.lua is TIC-80's per-frame callback. It:

  1. Initializes game state once on first call
  2. Updates mouse/context timing
  3. Delegates to the current active window handler
  4. Updates meters, timers, triggers, and glitch effects
  5. Draws UI overlays

Window Manager (inc/window/window.manager.lua)

Central UI state machine. Windows register with id, update(), and draw() handlers. Only one window is active at a time. All windows are declared in window.register.lua.

Window Purpose
intro_title Title screen
intro_ttg "Thanks To Grandma" credits
intro_brief Game briefing
menu Main menu
game Main gameplay (screens + decisions)
popup General popup overlay
discussion NPC dialogue/conversation
minigame_button_mash Button Mash minigame
minigame_rhythm Rhythm minigame
minigame_ddr DDR minigame
game_over Game over / restart screen
end End game choice screen
continued Day-continued notification
credits Credits roll
controls Control scheme display
audiotest Audio testing utility
player_name 3-character name entry before new game
ascend_debug Debug utility: start at a specific ascension level

Screen & Decision System (inc/screen/, inc/decision/)

  • Screens are gameplay scenes. Registered with Screen.register({id, name, decisions[], background, init, update, draw, exit}). They manage background maps and NPC sprite placement.
  • Decisions are player choices available on a screen. Registered with Decision.register({id, label, condition, handle}). A condition function gates visibility; handle drives transitions (to new screens, dialogue, minigames).

Screens: home, office, work, toilet, walking_to_office, walking_to_home, mysterious_man, manager

Maps (inc/map/): bedroom, office, street — rendered via map.manager.lua.

Game Logic (inc/logic/)

Module Purpose
logic.meter.lua Tracks ISM/WPM/BM stats (01000), combo multipliers, daily decay (20/day)
logic.day.lua Day counter; ascension triggers at day 3, game over at day 100
logic.timer.lua Event scheduling/delayed callbacks, one-shot and repeating
logic.trigger.lua Conditional event handlers with start/stop callbacks
logic.discussion.lua Dialogue parsing, branching answers, NPC portrait rendering
logic.minigame.lua Config and win-overlay for Button Mash, Rhythm, and DDR
logic.focus.lua Circular reveal/hide overlay transitions (expanding/shrinking circle)
logic.glitch.lua Visual glitch effect (random vertical stripes), toggled via Glitch.show()/hide()
logic.commute_glitch.lua 7-level glitch progression during ascension 7: corrupts sprite lists, remaps Norman to norman_echo, speeds up music, blocks/redirects decisions
logic.codegenerator.lua Encodes player's 3-char name to a 6-char base-36 completion code shown on the end screen

Global State (inc/init/)

  • init.context.lua: All runtime game state (current screen, meter values, progress flags). Persisted in memory bank 6. Key fields: player_name (3-char string), commute_glitch_level (07), talked_to_norman_echo, talked_to_true_sumphore, have_been_to_office, have_done_work_today.
  • init.config.lua: Screen dimensions (240×136), palette colors, timing constants. Persisted in memory bank 7.
  • init.ascension.lua: 9-level meta-progression system ("ASCENSION" letters progressively lit). Level 7 activates CommunteGlitch; level 9 unlocks the final "Break the cycle" decision.
  • init.context_debug.lua: Context.new_game_debug(level) — starts a new game at a specific ascension level for testing.

Audio (inc/audio/)

  • audio.manager.lua: Music playback (no-restart if already playing). Named tracks: room_work (0), activity_work (1), mystery (2).
  • audio.generator.lua / audio.songs.lua: Sound generation and song definitions.

Sprites (inc/sprite/)

sprite.manager.lua handles registration. Supports single and composite sprites with offset layers.

NPCs: norman, norman_echo (palette-remapped glitch variant of Norman, shown at commute glitch level 7), sumphore, pizza_vendor, and 10 developer archetypes (dev_boy, dev_buddy, dev_extrovert, dev_girl, dev_guard, dev_guru, dev_hr_girl, dev_introvert, dev_operator, dev_project_manager). Matrix characters: matrix_architect, matrix_neo, matrix_oraculum, matrix_trinity.

Discussions (inc/discussion/)

Branching dialogue files loaded by logic.discussion.lua. Each file defines one or more named dialogue trees (keyed strings with answer arrays that apply meter deltas).

File Dialogues
discussion.sumphore.lua Sumphore conversations (glitch-aware variants at commute glitch level 7)
discussion.coworker.lua Coworker coffee-chat variants per ascension level (disc_0, disc_1, disc_asc_1, disc_2, disc_asc_2, …)
discussion.commute_glitch.lua 8 commute glitch encounter variants (cg_0cg_7) + truth/Sumphore variant
discussion.truth.lua Dialogue with the "truth" mysterious man
discussion.pizza_vendor.lua Pizza vendor interaction

Input Utilities (inc/system/)

  • system.textinput.lua: 3-character uppercase letter selector. Supports next/prev letter cycling (A↔Z wrapping) and cursor navigation. Used by PlayerNameWindow.

Key Directories

inc/          Source modules (concatenated at build)
assets/       Game assets (sprites, tiles, SFX, music)
assets_src/   Source art (Aseprite files, PNGs for import)
docs/         Design documentation (mostly Hungarian)
tools/        Build utilities (musicator: MIDI→TIC-80 converter)
prompts/      Feature templates