Imported from celunah/celstep (
AGENTS.md). Install upstream withnpx skills add celunah/celstep. Copyright stays with the author.
CelStep agent guidance
Project identity and philosophy
CelStep is a minimal, independently implemented Lua/LÖVE rhythm-game engine.
Its current focus is DDR and PIU gameplay and StepMania-compatible .sm,
.ssc, and legacy AMX .sma charts. The long-term style target includes
singles, doubles, half-doubles, couples, co-op, solo, and related styles, as
well as normal, technical, and gimmick charts.
The project philosophy is: Do more with less.
- Prefer small, composable primitives over large command systems.
- Prefer explicit data and clear contracts over hidden magic.
- Keep timing, judgment, and replay behavior deterministic.
- Separate chart data, timing, gameplay, rendering, input, and theming.
- Add abstractions only when they solve a demonstrated problem.
- Use simple defaults while allowing powerful composition and scripting.
- Preserve raw data when normalization cannot safely interpret it.
- Fail clearly instead of silently discarding compatibility-relevant data.
- Keep compatibility logic at clear boundaries.
- Build a CelStep-native Lua architecture; do not copy another engine's layout.
The authoritative runtime is LÖVE 11.5 with LuaJIT. Pure Lua modules should remain Lua 5.1-compatible where practical. The planned source license is Apache License 2.0.
Repository boundaries
src/contains engine modules;main.luais the minimal LÖVE harness.songs/contains local user content and must not become a hardcoded song list.noteskins/andthemes/contain local or user-provided visual content.data/is for settings, profiles, scores, caches, and runtime state.bin/contains generated builds and copied external content.tests/contains dependency-light smoke cases and fixtures.spec/contains Busted unit specs..ci/lua-addons/contains LuaLS third-party definitions, including LÖVE and Busted APIs; it is tooling input, not engine code.
Keep the core independent of LÖVE. Platform integration belongs at the edge; headless tests must be able to load charts and run gameplay without a window, audio device, renderer, or LÖVE global.
Architecture contracts
- Chart readers parse source syntax and retain source tags.
- Normalization produces one universal chart model for SM, SSC, and SMA.
- Timing maps resolve beats and seconds, BPM changes, stops, delays, warps, scrolls, speed events, and version-specific timing behavior.
- Gameplay owns note state, input, judgment, scoring, combo, life, holds, rolls, mines, lifts, fakes, and results.
- Autoplay injects direct engine-level input events through the same pipeline as human input. It must not award score or bypass judgment.
- The renderer consumes gameplay state/events and never determines judgment.
- NoteSkins select visual assets and animation frames; they do not define note semantics or ruleset behavior.
- Themes own screens, elements, layers, scripts, events, and animations.
- LÖVE provides the platform layer; it is not the authority for chart timing.
Timing must come from an external/audio clock or explicit simulation position.
Never advance chart time by accumulating render-frame dt. Keep visual scroll
position separate from judgment time.
StepMania behavioral references
Use source as a behavioral specification, not as a template to copy. CelStep must reproduce observable behavior independently in Lua and must not mechanically translate StepMania C++, copy its distinctive organization/comments, or import its architecture merely for convenience.
Primary references:
- Mainline StepMania: https://github.com/stepmania/stepmania
- StepMania 5.1 development behavior: https://github.com/stepmania/stepmania/tree/5_1-new
- StepMania AMX: https://github.com/StepMania-AMX/StepMania-AMX
For compatibility work:
- Use a verified StepMania 3.9 branch or tag for legacy
.smbehavior. Do not assume a ref: verify it with the repository's available refs orgit ls-remoteand record the exact ref used. - Use StepMania 5 and
5_1-newfor modern.sscbehavior. - Use StepMania AMX as the primary
.smareference. Confirm differences with AMX source and real fixtures instead of treating SMA as old SSC. - Preserve version-dependent timing semantics, including timing tags, warps, stops, delays, negative timing values, time signatures, tick counts, chart-local timing, and unknown fields.
- Record the source URL, exact branch/tag, file, symbol, and behavioral conclusion for non-obvious compatibility decisions in a code comment or compatibility note.
If StepMania or StepMania AMX code is copied or directly adapted, retain the
applicable copyright and MIT license notices and list affected files in
THIRD_PARTY_NOTICES.md. Those licenses do not grant permission to redistribute
StepMania songs, charts, themes, NoteSkins, artwork, or third-party dependencies.
Chart, gameplay, and visual rules
Chart readers must validate the actual structure in addition to using the file extension. Unknown tags and raw source metadata must remain available for future compatibility work. Do not hardcode lane counts, panel layouts, styles, song lists, or chart counts.
Rulesets and style definitions must remain explicit and support DDR and PIU without assuming four, five, or ten lanes. Input from keyboards, controllers, Celune, and future replay/autoplay sources must use normalized timestamped press/release events. Event timestamps are chart timestamps, not processing time.
Keep NoteSkin support data-driven. Match the correct dance or pump assets,
respect sprite sheets and metrics.ini, and test active/inactive states and
hold/roll head, body, cap, and tail composition. Do not execute untrusted or
copyrighted theme/NoteSkin scripts as a shortcut for rendering.
The planned minimal theme system uses image, video, text, shape, and script elements. Elements share layer, position, anchor, opacity, diffuse, and scale properties. Animations use linear/curve interpolation, wait delays, and duration zero for immediate changes. Events include enter, exit, manual, judgment, and beat-synchronized BPM events. Keep this system composable and small; it is separate from chart parsing and gameplay rules.
Testing and completion standard
- Add a regression test for every compatibility fix.
- Prefer real chart fixtures in addition to small synthetic fixtures, while keeping the external validation corpus local and excluded from distribution.
- Test SM, SSC, and SMA separately wherever their behavior differs.
- Use synthetic charts for isolated timing, lane, judgment, input, replay, and NoteSkin tests.
- Test sprite sheets, frame selection,
metrics.ini, active/inactive states, and hold/roll composition. - Never make tests depend on copyrighted external content being present.
- Before considering a change complete, run formatting, linting, type checking, unit tests, fixture smoke tests, and relevant runtime/import checks.
The local CI entry point is:
lua scripts/run_ci.lua
It runs StyLua, Luacheck, LuaLS with LuaCATS LÖVE definitions, Busted, and the
dependency-light fixture runner. Keep CI-compatible Lua 5.1/LuaJIT syntax and
globals configured through .luacheckrc, .luarc.json, and .stylua.toml.
Do not add third-party songs, charts, music, videos, themes, NoteSkins, or other copyrighted content to the distributed engine unless the license clearly permits redistribution. Local user content belongs outside release artifacts.