Orblike Design Plan
Summary
Design and planning session for porting Orblike from Anchor 1 to Anchor 2. Established the pincer migration strategy (write the high-level shell fresh in Anchor 2, port combat last), set up the directory structure to support both versions concurrently, and locked in the full high-level game design in design.md and a 10-phase implementation plan in plan.md.
Migration Strategy Discussion:
- User proposed pincer approach: code the high-level portion (menus, between-maps, between-runs, UIs) in Anchor 2 first, then progressively port the low-level portion (combat, maps, abilities) from Anchor 1
- Claude assessed this strategy as sound and actually stronger than initially thought because the high-level portions don't exist yet — it's greenfield in the new engine, not a port
- Anchor 2's explicit procedural style (no tree, no hooks, IDs not pointers, poll not subscribe) is a better fit for menu/flow code than for combat code
- Eventual combat port will be substantial (uses every pattern v2 removed: tree hierarchy, tags, link, flow_to, action phases), but by then will have deep v2 fluency
- User's long-term vision: Orblike as a "homebase" game supporting PoE-style leagues (tower defense, survivors, etc.) plugged into the same map/run/progression shell
Anchor 2 Engine Overview Research:
- Read the full Anchor 2 engine overview blog post
- Key differences from v1: ~4500 lines across 19 files (vs v1's 2500 lines, 15 files), C engine unchanged
- Philosophy: minimal framework, maximal explicitness; procedural by default; classes only where they earn it (only collider remains a class)
- Main architectural changes: replace tree with ID lookup; replace subscriptions with polling; replace hook-based ordering with explicit update functions; game owns main loop (update/draw are top-level globals)
- Core patterns:
make_entity(self),entities[id],collection_update(list, dt),process_destroy_queue(), always check_deadon both entities in collision events - Removed from v1:
angod-object,self:add(child),self:tag(),self:link(),self:flow_to(), early_action/action/late_action
Orblike Codebase Exploration:
- Read
reference/orblike_ability_system.md(board-based ability system) andreference/system_design.md(original orb design) - Read
main.lua(544KB) — has combat working, 23 abilities, damage system, map generation, SNKRX visual style - Missing: between-map transitions, loot/inventory, orb graph UI, map selection, run structure, meta-game
Directory Structure Setup:
- User initially proposed both Anchors inside Orblike/ with 2 main files, no conflicts
- Claude explored layout: engine takes directory arg, resolves paths relative to it (based on
Anchor2/arena/pattern withanchor.exe arena) - First proposal: v2 in subdirectory, v1 at root
- User preferred inverse: v2 at root, v1 tucked away
- Final structure:
- Root: v2 starter
main.lua, v2anchor/framework,anchor.exe,assets/,run.bat(runs v2) v1/: oldmain.lua,profiler.lua, v1anchor/,assets/(full copy per user request — initially was a junction),run-v1.bat
- Root: v2 starter
- Moved v1 files:
mv main.lua profiler.lua anchor/ v1/ - Copied v2 framework:
cp -r Anchor2/framework/anchor/* Orblike/anchor/ - Initial attempt at junction failed via
cmd.exe /c mklink /J, succeeded via PowerShellNew-Item -ItemType Junction - User then said don't use symlinks, copy fully — replaced junction with
cp -r - User moved
run-v1.battov1/folder; Claude updated it to..\anchor.exe . %* - Created v2 starter
main.luawithlayer_new('game'), minimalupdate()anddraw()functions, dark background
Documentation Cleanup:
- User requested moving ALL
.mdfiles toreference/and removing any folders that only contained.mdfiles - Moved:
interactions.md,lore.md,orblike_ability_system.md,tags.md,todo.mdfrom root - Moved
orbs/ability_design_reference.md,orbs/ability_reference.md,orbs/system_design.mdfromorbs/then removedorbs/directory - User asked about
tools/mov_to_spritesheet.py— it's a .mov-to-spritesheet converter for RTFX FX pack animations (ffmpeg frame extraction → luminance threshold → crop/scale → stitch); stays intools/as a production utility
High-Level Game Structure Brainstorm:
- User laid out the full structure: pre-run equipment selection, map view, arena (existing Orblike), inventory/orbs UIs accessible in arena, PoE-style hideout as physical base with NPCs
- Unified UI pattern: inventory always on right, action UI on left with slots (crafting, orb composition, map placement, trade all follow this)
- No main menu — game starts directly in hideout, everything diegetic except ESC options
- Leagues = map mechanics, not a top-level system (some may add persistent hideout NPCs)
Critical Design Questions Surfaced:
- "What resets on death?" — User: death nukes everything, fresh empty hideout, no meta-progression for now
- Run structure — clear final boss; target ~1hr first, scaling to 5-10hr
- Character/class — start with no classes, just different starter sigils (biggest diversity axis)
- Map completion — PoE-style, can leave anytime, boss has higher drop chance, portals persist ~10min for re-entry
- Map economy — maps drop from monsters, self-sustaining, craftable modifiers (unified with item modifiers TBD)
- UI during combat — full-screen, no pause, drag-and-drop works mid-fight (forces reconfig in hideout, mid-combat swap possible but costly)
- Inventory — infinite (no multiplayer = no need for friction)
- Map UI — PoE-style but simpler: maps random on world map, hover for info, no atlas tree/connectivity
Equipment Slot Investigation:
- User said 5 slots: orbs, sigil, robe, rings, + one forgotten
- Claude grepped
reference/lore.mdand found: Mask is the missing slot - From lore: floating robed masked figure with sigil overhead, orbs circling, hands and feet ringed with magical bands
- Mask is the identity piece (personal-mana frequency signature) — narratively non-interchangeable but mechanically a gear slot
- Ring count TBD (4 for hands+feet, or 2 for hands only)
Diegetic Starter Gear Solution:
- Claude raised the issue: if multiple masks exist in the hideout, why wouldn't the player grab them all?
- User's lore answer: the orb + null mana cleaning system is sponsored by the Psyonian state. They want mages to risk themselves cleansing corruption, so they provide starting gear and a hideout base
- Starter gear comes from an initial NPC (later becomes vendor) who shows multiple options, tells you to pick one
- Starter rules TBD: maybe sigil + 1-2 orbs, or mask + sigil + 1-2 orbs, etc.
UI-During-Combat Critique:
- Claude pushed back: full-screen + no pause means opening UI = fighting blind, so it's more "choose between fight and reconfig" than "tactical mid-combat reconfiguration"
- Offered middle option: full-screen UI but combat continues (hear hits, see damage numbers over UI)
- User confirmed no pause is intentional — forces experimentation in hideout
Map System Final Decision:
- Claude proposed rich lore-tied systems (null residue as currency, Regions model, connectivity via residue flavor)
- User simplified: PoE but simpler for now, maps drop maps, random world map placement, hover for info, defer crafting/connectivity decisions
Design Documents Written:
design.mdat Orblike root (not in reference/ per user request)- Concept, lore (Psyonian state), core loop, hideout (NPCs: Gear Officer, Teleporter Engineer), equipment slots (Mask/Sigil/Robe/Rings/Orbs), map system, unified UI pattern, run/death rules, leagues as map mechanics, design principles, open questions
- User linter-edited concept from "bullet-hell" to "action RPG"
plan.mdat Orblike root with 10 phases:- Phase 0: Setup (done)
- Phase 1: Hideout shell (player walks around, camera, ESC stub)
- Phase 2: UI primitives (text, buttons, full-screen pattern, real options menu)
- Phase 3: NPC + interaction system
- Phase 4: Inventory data model + UI (widest blast radius — affects everything downstream)
- Phase 5: Starter gear selection (first real NPC UI)
- Phase 6: Orb composition UI (character screen)
- Phase 7: Map UI + run lifecycle skeleton with stub arena (full meta-loop playable without real combat)
- Phase 8: Arena port from v1 (the big one — 23 abilities, map gen, damage, enemies)
- Phase 9: First league + league API validation
- Phase 10+: Content, balance, retire v1
- Architectural decisions flagged for later: Phase 4 item schema, Phase 6 orb composition schema, Phase 8 save system, Phase 9 league API
Final State:
- Orblike/ root runs v2 starter (dark screen)
- Orblike/v1/ runs full v1 game for comparison
- Design locked in
design.mdandplan.md - Next session will start Phase 1 implementation
Now that we have Anchor2 ready, let's keep working on Orblike. However, because Orblike is written in Anchor 1, we'll actually take a pincer approach. Orblike v1 is written from the bottom up. We have combat working, a bunch of abilities implemented, and the basics of what makes a single gameplay unit (a map) work, although this is not fully complete yet. Let's call this the low-level portion of the game. The high-level portion of the game would be what's between maps, what's between runs, so main menu, menu, options, various UIs for various things and so on. My strategy is that we should code the high-level portion of the game in Anchor 2, make sure that that's working nicely and is well-structured in the code, and then once that's done, progressively finish and rewrite the low-level portion. Does this strategy make sense to you? Do you have questions?
The strategy makes sense. Starting with the high-level shell in Anchor 2 is lower-risk — menus, progression screens, and between-run flow are structurally simpler, more self-contained, and less entangled with each other than the combat system with its dozens of abilities and interactions. It gives you a chance to validate Anchor 2's patterns with manageable code before tackling the hard stuff.
The main thing that could make or break this is the boundary between the two portions during the transition. The high-level code needs to start a map and receive results back (did the player win, what loot did they get, what's their health, etc.). If Anchor 1 and 2 have meaningfully different APIs or architectural patterns, that seam could get awkward.
A few questions:
What changed in Anchor 2 vs Anchor 1? I need to understand the scope of differences — is it a clean break in API/architecture, or more of an incremental evolution? This affects how much friction there'll be at the boundary between the high-level and low-level portions.
Will the whole game run on Anchor 2 from day one? Meaning — do you plan to port the existing low-level code over to Anchor 2 as-is (even if messy) so everything's in one runtime, then progressively rewrite it? Or would the low-level portion literally stay running on Anchor 1 somehow?
How much of the meta-game design do you have in mind? Do you know what the between-maps and between-runs flow looks like (shop, ability draft, map selection, etc.), or is that something we'd be designing as we go?
- You can read the new Anchor 2 Engine Overview post in the blog notes/ folder.
- The entire game will run in Anchor 2, once that's running we'll retire Anchor 1 and move the contents of the Anchor2/ folder to Anchor to replace it.
- Not sure if this is described in the documents in the Orblike/ folder, but I have pretty much the entire game already imagined in terms of its high-level components. Although I'm sure it will need some changes as we go. But we'll design the code such that making these changes is easy. The final goal for this game is for it to act as my main "homebase" game where I can try lots of different "leagues", similar to PoE, that have different gameplay to them using the base game. So one league might be a tower defense-like game, another might be a survivors-like game, etc, etc. The high-level structure needs to support this kind of variety, which is not hard, the map system from PoE, which we'll mostly copy, does most of the work on this front in terms of enabling league content to appear through the maps.
Let me get up to speed on both Anchor 2 and the current Orblike design.
Agent
[
{
"type": "text",
"text": "Perfect! Now I have a good picture. Let me compile my findings into a comprehensive report.\n\n## Orblike Design and Architecture Analysis\n\nI've thoroughly reviewed the key design documents and main.lua. Here's what you need to know:\n\n---\n\n## 1. ORBS.md / system_design.md - Core System Design\n\n**System Goals:**\nThe Orb composition system models each ability as a self-contained **Orb** that orbits the player. Orbs connect via **Links** with **Bridge Types** (trigger events), creating automatic ability chains without position-ordering. The system unifies all conditional activation patterns (on-hit, on-kill, on-damage-taken, etc.) as links from **Orb Zero** (the player).\n\n**Core Architecture:**\n\n- **Orb Zero (The Player):** Has carrier events like Hit (outgoing/incoming), Crit, Kill, Death, Movement, Dodge, Low Health, CC'd. Links FROM the player to other orbs trigger them on these events.\n\n- **Orb:** A complete, self-contained ability with:\n - Core: Carrier type (Projectile, Beam, Area, Zone, Chain, Orbit, etc.)\n - Activation: Manual, Passive, or Periodic\n - Targeting: Aimed, Auto-target, Self, etc.\n - Behavior: Instant, Duration, Channelled\n - Hit Behavior: Consumed, Pierce, Chain, Explode, Bounce\n - Effects: Damage, DoT, Slow, Displacement, etc.\n - Modifiers: Changes to any of the above\n\n- **Link:** A directed connection between two orbs defining when orb B fires based on orb A's carrier events. Contains:\n - Bridge type (Cast on Hit, Cast on Kill, Timer, Cast at Carrier, etc.)\n - Optional modifiers (apply only through this link)\n\n- **Modifier Placement & Scoping:**\n - **Payload isolation:** Modifiers on one orb do NOT affect other orbs\n - Modifiers can attach to orbs (apply to ALL activations) or links (apply only through that link)\n - Example: Formation(nova) on the orb = always fires nova; Formation(nova) on a link = nova only when triggered by that link\n\n- **Chain Throttling / Energy System:** Each link has an energy capacity (max firings per burst). After depleting energy, it recharges over time. Prevents infinite loops and geometric explosions while preserving chain fantasy.\n\n- **Shared State:**\n - **Charges:** Orbs produce/consume from a shared pool (Power Charge, Frenzy Charge, etc.)\n - **Exert (Temporal Modifier Buffering):** Orbs can produce temporary modifiers consumed by next N activations of ANY orb\n\n**Open Questions (from design):**\n1. Energy capacity defaults (varies by bridge type?)\n2. Energy recharge rate (fast = frequent bursts, slow = rare powerful bursts)\n3. Link limits (can one orb have multiple Cast on Hit links?)\n4. Self-links allowed? (creates recursive spawners)\n5. Fan-in allowed? (multiple orbs \u2192 same target orb)\n6. Global modifier stacking?\n7. Charge types (pre-defined or emergent?)\n8. Visual representation (show connections as energy lines?)\n9. Link modifier conflicts (link overrides orb?)\n10. Orb Zero link limits?\n\n---\n\n## 2. orblike_ability_system.md - Board-Based Implementation\n\n**Important:** This is a MODIFIED version of the system design above. Instead of orb-to-orb linking at runtime, boards are **pre-allocated fixed graphs** that players fill with abilities.\n\n**Board Structure:**\n- **Slot types:** Ability slots (1-4), Modifier slots (0-6 per ability), Trigger slots (pre-made or flexible)\n- **Connections:** Link two ability slots with a trigger type (on-hit, on-kill, etc.) and optional modifier slots\n- **Topology matters:** Linear chain, Branch, Fan-in, Loop, with player-event inputs, or complex combinations\n\n**Pre-made vs. Flexible:**\n- **Pre-made trigger:** Fixed type, high energy capacity, specialist boards\n- **Flexible trigger:** Player inserts any trigger type, low energy capacity, generalist boards\n\n**Entry Point & Activation:**\n- Leftmost ability = entry point (determines how the board activates)\n- Downstream abilities activate only through trigger connections\n- **One board = one button (or one passive)**\n\n**Example Boards:**\n- \"Basic Fireball\": 1 ability, 2 modifiers, no connections\n- \"Bomber\": 2 abilities, on-hit connection\n- \"Defensive Reactor\": Player-event input (on damage taken) \u2192 Area \u2192 Chain\n- \"Frostbolt + Ice Nova\": Cast at Carrier connection\n\n**Rarity Axes (what makes boards valuable):**\n- Ability slot count (1-2 common, 3-4 rare)\n- Modifier slots per ability (0-2 common, 4-6 rare)\n- Connection count (0-1 common, 3+ rare)\n- Pre-made vs. flexible ratio (more pre-made = specialist, higher power)\n- Connection types (Cast at Carrier, Timer, Detonate are rarer)\n- Player-event inputs (rare, valuable)\n- Topology complexity (loops, branches, fan-in)\n\n**Boards as Loot:**\nPlayers find boards as loot. Each board is one orb. Progression is incremental \u2014 early game boards are small (1-2 abilities), grow in complexity as players find bigger boards.\n\n**Cross-Board Synergy:**\n- Charges (shared pool across all boards)\n- Exert (shared buff pool)\n- Global modifiers (apply to all matching boards)\n\n---\n\n## 3. main.lua - Current Implementation\n\n**Overall Structure:**\nThe game uses the **Anchor framework** (a minimal 2D framework wrapping a C engine). Entry point is via `require('anchor')({...})` which initializes the engine.\n\n**Core Classes/Systems:**\n\n### Layers (Drawing)\nMultiple rendering layers for compositional drawing:\n- `bg`, `ground_floor`, `ground`, `ground_2` (background/terrain)\n- `enemy_layer` (enemies)\n- `ability_layer` (projectiles, beams, effects)\n- `game` (main game objects)\n- `effects` (particles, hit circles)\n- `ui`, `hud` (interface)\n\n### Key Objects\n\n**Player (line 7242):**\n- `player:new(x, y)` - Main player character\n- Properties: `x, y, hp, max_hp, speed, dead`\n- Has collider, timer, spring for animations\n- Can add/remove modifiers and abilities\n- Has 4 orb slots\n\n**Orb (line 1181):**\n- `orb:new(args)` - Wraps one ability with modifiers\n- Can add abilities, modifiers\n- `activate(override_ability, override_pos)` - Fire the ability\n- Properties: Position (orbits player), rotation, visual properties\n- Abilities can have sub-abilities triggered on events\n- Edition system for visual variants (ruby, emerald, polychrome, etc.)\n\n**Enemy (not shown in excerpt but referenced extensively):**\n- Takes damage, can be killed\n- Has position, velocity, HP, defenses (physical_defense, magic_defense)\n- Dies and spawns particles\n- Spatial hash optimization for performance\n\n**World/Map State:**\n- `map_floor` - Polygon defining playable area\n- `wall_points` - Distribution of decorative wall symbols\n- `generate_linear_map(seed)`, `generate_organic_linear_map(seed)` - Map generators\n- Enemies spawn in pre-made packs\n\n### Damage System\n- Dual damage: physical + magic + null\n- Formula: `max(0, damage - defense)`\n- Null bypasses all defenses\n- No minimum damage (fully blocked = 0)\n\n### Sound System\n- Volume-normalized SFX (baseline -24 dB)\n- Supports bitcrushed variants (_lightmed suffix)\n- Handles looped sounds, pitch variation\n- Recording support for videos\n\n### Visual Effects\n- **Edition effects:** Per-pixel shader treatment (ruby, emerald, polychrome, etc.)\n- **Shaders:** Wall gradient, shockwave, ground pattern, edition, enemy texture\n- **Particle effects:** Hit circles, lightning lines, cyclone particles, explosions\n- **Wall system:** Decorative symbols with distance field gradient coloring\n\n### Game Flow (Implied from structure)\n1. Player starts in a generated map\n2. Enemies spawn in packs\n3. Player activates orbs (buttons) or orbs activate passively\n4. Orbs fire abilities (projectiles, beams, areas, etc.)\n5. Abilities hit enemies, trigger chains (Cast on Hit links)\n6. Enemy dies \u2192 loot + progression\n7. Clear all enemies \u2192 next map (not yet implemented)\n\n**Missing/Not Yet Implemented:**\n- Between-map state/transitions\n- Loot pickup/inventory system\n- Orb graph UI (compose builds)\n- Map selection screen\n- Run structure (sequence of maps)\n- Boss encounters\n- Crafting system\n\n---\n\n## 4. Game Flow (From todo.md)\n\n**Current State:** 23 abilities implemented, many modifiers, basic map generation, SNKRX visual style.\n\n**Playable Single-Map State:** Just completed (player HP + death system done).\n\n**Remaining Milestones to Playable Run:**\n\n1. **Session 1 (done):** Enemy Types + Pack System\n - Enemies spawn in packs (all same type)\n - Special enemies (blue/yellow/prismatic) with unique modifiers\n - Pack UI showing collective HP + defenses\n\n2. **Session 2:** Damage-Dependent Modifiers (point_blank, concentrated_effect, DoT_on_hit, explode_on_kill, armor_shred_on_hit, +N physical/magic damage, conversion modifiers)\n\n3. **Session 3:** Core Missing Abilities (DoT, Summon, Trap)\n\n4. **Session 4:** Beach Map + Completion Loop\n - Linear ocean/cliff layout\n - Map completion condition\n - Transition to next map\n - Difficulty scaling\n\n5. **Session 5:** Loot + Inventory\n - Drop system\n - Pickup mechanic\n - Inventory UI\n - Equipping items\n\n6. **Session 6:** Orb Graph UI\n - Pause-screen composition display\n - Ability/modifier equipping\n - Link visualization\n\n7. **Session 7:** Map Selection + Run Structure\n - Map node graph\n - Map modifiers (PoE-style)\n - Run progression\n - Boss encounter\n\n8. **Session 8:** Economy + Resource Trading (currency orbs like PoE)\n\n9. **Session 9:** Polish + Balance Pass\n\n---\n\n## 5. Key Design Decisions & Philosophy\n\n**From the code and docs:**\n\n- **SNKRX visual style:** Minimal, geometric shapes, no detailed sprites\n- **Small integer damage:** 1-20 range, no percentages, no floats\n- **Numerator/Denominator chances:** \"1 in 2\" not \"50%\"\n- **Null abilities fire from orbs:** Casters can't process null, so orbs fire directly\n- **Channelling is permanent:** Channelling abilities always active, no button\n- **Per-orb editions:** Visual variants (ruby, emerald, polychrome) that apply to ability effects via shader\n- **Wall symbols:** Procedural decorative geometry with distance-field gradient coloring\n- **Performance:** Spatial hash for enemy collision/homing, batched rendering, shader-based visual effects\n\n---\n\n## Summary\n\n**High-level:** Orblike is a roguelike bullet-hell where players compose abilities (Orbs) on boards. Boards are fixed-topology loot pieces that players fill with ability gems and modifiers. Orbs orbit the player and fire based on player input or triggered events (links). The game is currently at a \"playable single map\" state with 23 abilities and the core damage/effect systems implemented. The next phase is map progression, loot, inventory, and the orb composition UI."
}
]
Good, now I have the full picture. Let me give you my honest assessment.
The strategy is sound, and stronger than I initially thought. Here's why:
The high-level portions — menus, map selection, run structure, between-run flow — don't exist yet. This isn't a port, it's greenfield development. You're writing new code in the new engine, which is the easiest possible migration path. Meanwhile, the combat code (~7000+ lines of deeply Anchor 1-style Lua using trees, tags, link, flow_to) is the hardest thing to port. Deferring it is correct.
Anchor 2's explicit procedural style is actually a better fit for the high-level flow code than it is for the combat code. Menu screens, map selection graphs, run state machines — these are naturally sequential, state-driven things that benefit from reading top-to-bottom with no hidden hooks.
The PoE leagues vision makes the architecture critical. If the game needs to support radically different gameplay modes (tower defense, survivors, etc.) plugging into the same map/run/progression shell, then the high-level structure needs to be designed as a stable platform with clean extension points. Getting this right first, before the combat code muddies the water, is the right sequencing.
One real concern for later: The Anchor 1 combat code uses almost every pattern that Anchor 2 explicitly removed — tree hierarchy, tags/queries, link, flow_to, action phases. The eventual port won't be cosmetic; it'll be a substantial rewrite. But that's a future problem, and by then you'll have deep Anchor 2 fluency.
Before we start coding — should we first sketch out the high-level architecture? I'm thinking: what are the top-level states/screens, how does the map system work structurally, what's the run lifecycle, and how does league content plug in. That would give us a roadmap before we touch code.
First, let's consider directory structure. We will want to have both Anchors inside the Orblike directory, we'll want to have 2 main files (high-level Anchor v2 + the old one), without any conflicts. What's your proposal there? We will only edit the new Anchor v2 version for a while, and only run the old one (+ make some edits when necessary) for comparison so we can make sure that our rewrite looks and behaves the same when we do port the combat and so on.
Agent
[
{
"type": "text",
"text": "Perfect! Now I have a complete picture. Let me create a comprehensive summary for the user.\n\n## Summary\n\nI've explored the directory structure of **Orblike**, **Anchor** (v1), and **Anchor2** (v2). Here's the complete picture:\n\n---\n\n## **1. ORBLIKE Directory Structure**\n\n**Location:** `/e/a327ex/Orblike/`\n\n### Top-level files:\n- `main.lua` (544 KB) \u2014 Main game code\n- `anchor.exe` (4.2 MB) \u2014 Compiled Anchor engine\n- `run.bat` \u2014 Runs game: `.\\anchor.exe .`\n- `tags.md`, `interactions.md`, `lore.md`, `todo.md`, `profiler.lua` \u2014 Game design docs\n- `.gitignore` \u2014 Standard git config\n\n### Top-level directories:\n- **`anchor/`** (256 KB) \u2014 Framework modules (17 files):\n - `init.lua` (50 KB), `timer.lua` (22 KB), `layer.lua` (20 KB), `math.lua` (20 KB), `object.lua` (18 KB), `array.lua` (17 KB), `collider.lua` (15 KB), `shake.lua` (13 KB), `color.lua` (11 KB), `camera.lua` (8.2 KB), `random.lua` (5.2 KB), `animation.lua` (4.8 KB), `spring.lua` (4.7 KB), `font.lua` (2.3 KB), `spritesheet.lua` (1.3 KB), `class.lua` (1.2 KB), `image.lua` (1 KB)\n \n- **`assets/`** (50 MB) \u2014 Sounds, sprites, shaders, fonts\n - Audio files (WAV/OGG pairs), particle effect PNGs, shader fragments (GLSL), font files\n \n- **`orbs/`** \u2014 Ability design reference (3 MD files)\n- **`recordings/`** (210 MB) \u2014 Video recordings (MP4s)\n- **`reference/`** \u2014 Analysis & design docs\n- **`tools/`** \u2014 `mov_to_spritesheet.py` utility\n\n### How Orblike uses Anchor:\n- `require('anchor')({width = 480, height = 270, scale = 3})` in main.lua (line 1)\n- The `anchor.exe` is run directly: `anchor.exe Orblike` loads `/Orblike/main.lua`\n- Framework modules are loaded via `require('anchor.X')` statements in `anchor/init.lua`\n\n---\n\n## **2. ANCHOR (v1) Directory Structure**\n\n**Location:** `/e/a327ex/Anchor/`\n\nThe original framework that Orblike uses.\n\n### Top-level directories:\n- **`framework/`** \u2014 The framework code:\n - `anchor/` (256 KB, 17 files) \u2014 Core framework modules\n - `main.lua` \u2014 Example/demo\n - `run.bat`, `run-web.bat` \u2014 Build scripts\n - `assets/`, `shaders/` \u2014 Demo resources\n\n- **`engine/`** \u2014 C engine source (unchanged across versions)\n - `build/`, `include/`, `lib/`, `src/` \u2014 Compilation artifacts\n - `anchor.obj`, `gl.obj` \u2014 Compiled objects\n\n- **`docs/`** \u2014 API documentation\n- **`reference/`** \u2014 Design notes\n- **`release/`**, **`scripts/`** \u2014 Build artifacts\n\n---\n\n## **3. ANCHOR 2 (v2) Directory Structure**\n\n**Location:** `/e/a327ex/Anchor2/`\n\nA rewrite of Anchor's framework layer with a different philosophy (minimal, explicit).\n\n### Top-level directories:\n\n- **`framework/`** (166 KB, 19 files) \u2014 New v2 framework:\n - `anchor/` \u2014 Framework modules:\n - All the old modules + **new ones**: `input.lua`, `helpers.lua`, `physics.lua`\n - Modules removed: `random.lua`\n - Key size reductions from v1:\n - `init.lua`: 50 KB \u2192 6 KB (no god-object tree)\n - `object.lua`: 18 KB \u2192 3.2 KB (minimal base class)\n - `timer.lua`: 22 KB \u2192 16 KB\n - `layer.lua`: 20 KB \u2192 8.7 KB\n - `run.bat`, `run-web.bat` \u2014 Demo scripts (orphaned, kept for reference)\n\n- **`arena/`** (4.2 MB) \u2014 **Canonical test game** validating v2:\n - `main.lua` (24 KB, ~727 lines) \u2014 Complete example exercising every framework module\n - `anchor/` \u2014 Copy of framework (source of truth is `../framework/anchor/`)\n - `anchor.exe` \u2014 Compiled engine\n - `assets/` \u2014 Game resources\n - `ARENA_PROGRESS.md` \u2014 Build history and status\n - Run: `.\\anchor.exe arena`\n\n- **`engine/`** (4.9 MB) \u2014 Same C engine as v1\n - `build/`, `include/`, `lib/`, `src/` \u2014 Same structure\n - `shell.html` \u2014 Web build template\n - `build.bat`, `build-web.sh` \u2014 Build scripts\n\n- **`docs/`** \u2014 API documentation\n- **`reference/`** (152 KB) \u2014 Design documentation:\n - `START_HERE.md` \u2014 Entry point\n - `anchor2_plan.md` (35 KB) \u2014 **Authoritative design plan**\n - `progress.md` (9 KB) \u2014 Rewrite status (complete)\n - `questions_for_user.md` (9 KB) \u2014 Implementation decisions\n - `reference_disciplines.md`, `reference_taxonomy.md`, `reference_system_design.md` \u2014 Rationale\n - `Anchor_v1/` \u2014 Archived original framework (for reference only)\n- **`scripts/`** \u2014 Build scripts\n\n---\n\n## **4. Key Differences: Anchor v1 vs Anchor 2**\n\n| Aspect | Anchor v1 (Orblike) | Anchor 2 |\n|--------|-------------------|----------|\n| **Framework Size** | 256 KB | 166 KB (35% smaller) |\n| **init.lua** | 50 KB (big god-object) | 6 KB (minimal setup) |\n| **object.lua** | 18 KB (object tree) | 3.2 KB (no tree) |\n| **Philosophy** | Hierarchical tree, action phases, subscriptions | Minimal, explicit, procedural, ID-based |\n| **New modules** | \u2014 | `physics.lua`, `input.lua`, `helpers.lua` |\n| **Removed** | \u2014 | `random.lua` (functionality moved elsewhere) |\n| **Paradigm** | OOP with god-object (`an`) | OOP for entities, procedural for utilities |\n| **Cross-refs** | Direct pointers | IDs (resolved at use time) |\n| **Events** | Subscription-based | Poll each frame |\n| **Test game** | n/a | `arena/main.lua` (727 lines, complete) |\n\n---\n\n## **5. How Orblike Currently References Anchor v1**\n\n**Direct path:** Orblike embeds a copy of the framework in `Orblike/anchor/`\n\n**Loading:** `require('anchor')({...})` in `Orblike/main.lua` line 1\n- This calls `anchor/init.lua:return function`\n- All framework modules are required from the `anchor/` directory\n\n**Engine:** `Orblike/anchor.exe` \u2014 compiled C engine\n\n**No external dependency:** Orblike is completely self-contained; it doesn't reference `/e/a327ex/Anchor/framework/anchor/`\n\n---\n\n## **6. Anchor2's Structure (Reference)**\n\nAnchor2 is more modular as a reference:\n\n- **Source of truth:** `/e/a327ex/Anchor2/framework/anchor/` (the v2 framework modules)\n- **Test game:** `/e/a327ex/Anchor2/arena/` \u2014 working example using v2 philosophy\n - Copies framework files from `framework/anchor/` into `arena/anchor/` for deployment\n- **Canonical style:** Read `arena/main.lua` to see how to code for v2\n\n---\n\n## **Summary for Directory Restructure**\n\n**Current state:**\n- Orblike has a **static embedded copy** of Anchor v1 framework in `Orblike/anchor/`\n- Framework files never change (they're from Feb 24 / Apr 13)\n- Main game logic is in `Orblike/main.lua`\n\n**Restructure considerations:**\n1. **Framework separation:** You could move `Orblike/anchor/` to a shared location and use a symlink or path references\n2. **Framework upgrade path:** Orblike would need refactoring to work with Anchor 2 (different API \u2014 no `an` object, no object tree)\n3. **Build process:** Currently `run.bat` just runs `anchor.exe .` in place; a restructure could involve:\n - Shared framework in `../Anchor/framework/anchor` (symlink?)\n - Or separate framework folders per project\n - Or copying framework into a build directory before running\n\n**Files to watch during restructure:**\n- `Orblike/main.lua` \u2014 uses Anchor v1 API extensively\n- `Orblike/anchor/init.lua` \u2014 entry point for framework\n- `Orblike/run.bat` \u2014 engine invocation\n- All `require('anchor.X')` statements in main.lua and framework modules"
}
]
Let me look at how the engine resolves paths, since that constrains the options.
Read (E:/a327ex/Orblike/run.bat)
1 @echo off
2 cd /d "%~dp0"
3 .\anchor.exe . %*
4
Read (E:/a327ex/Anchor2/arena/run.bat)
1 @echo off
2 REM Run arena test game. CWD must be repo root; first arg is the game folder name.
3
4 cd /d "%~dp0.."
5 "%~dp0anchor.exe" arena
6
Good — the arena pattern shows the engine takes a directory argument and resolves paths relative to it. anchor.exe arena loads arena/main.lua and resolves assets/... from arena/assets/....
Here's my proposal:
Orblike/
# v1 — completely untouched
main.lua (v1 game code)
anchor/ (v1 framework)
anchor.exe (shared engine binary)
assets/ (shared game assets)
run.bat (anchor.exe . → runs v1)
# v2 — clean subdirectory
v2/
main.lua (new high-level code)
anchor/ (v2 framework, copied from Anchor2/framework/anchor/)
assets → junction to ../assets (mklink /J, no duplication)
run-v2.bat (anchor.exe v2 → runs v2)
Why this works:
- V1 is untouched —
run.batworks exactly as before - V2 lives in its own directory with its own framework, no naming conflicts
- Assets shared via NTFS junction (zero-copy, transparent to the engine)
run-v2.batat root follows the same pattern as arena'srun.bat- When you retire v1: move
v2/contents up, delete old files, done
One thing to verify: that anchor.exe v2 resolves Lua require('anchor') to v2/anchor/init.lua. Based on the arena pattern it should, but worth a quick test after setup.
Does this structure work for you, or would you prefer v2 at the root and v1 tucked away instead?
I'd prefer v2 at the room and v1 tucked away I think.
Revised structure:
Orblike/
# v2 — at root (what we work on)
main.lua (new high-level code, starts fresh)
anchor/ (v2 framework, from Anchor2/framework/anchor/)
anchor.exe (engine binary)
assets/ (game assets)
run.bat (anchor.exe . → runs v2)
# v1 — tucked away for reference/comparison
v1/
main.lua (old game code, moved from root)
anchor/ (v1 framework, moved from root)
assets → junction to ../assets
run-v1.bat (anchor.exe v1 → runs v1 for comparison)
# Game docs stay at root (they're about the game, not a version)
tags.md, interactions.md, lore.md, todo.md, ORBS.md, etc.
orbs/, reference/, recordings/, tools/
Before I set this up — does main.lua depend on any other Lua files at the root besides the anchor/ framework? Things like profiler.lua that should move into v1/ as well?
Yes, additional Lua files also go in there I think.
Glob (*.lua)
Orblike\reference\112020\engine\external\clipper.lua
Orblike\reference\BUILDRX2\engine\external\clipper.lua
Orblike\reference\112020\engine\external\mlib.lua
Orblike\reference\BUILDRX2\engine\external\mlib.lua
Orblike\reference\112020\engine\external\ripple.lua
Orblike\reference\112020\engine\external\binser.lua
Orblike\reference\BUILDRX2\engine\external\binser.lua
Orblike\reference\112020\engine\datastructures\string.lua
Orblike\reference\112020\engine\datastructures\graph.lua
Orblike\reference\112020\engine\datastructures\grid.lua
Orblike\reference\112020\engine\input.lua
Orblike\reference\112020\engine\system.lua
Orblike\reference\112020\engine\log.lua
Orblike\reference\BUILDRX2\engine\log.lua
Orblike\reference\112020\engine\graphics\tileset.lua
Orblike\reference\BUILDRX2\engine\graphics\tileset.lua
Orblike\reference\112020\engine\game\state.lua
Orblike\reference\112020\engine\math\spring.lua
Orblike\reference\BUILDRX2\engine\math\spring.lua
Orblike\reference\112020\engine\math\random.lua
Orblike\reference\BUILDRX2\engine\math\random.lua
Orblike\reference\112020\engine\map\solid.lua
Orblike\reference\BUILDRX2\engine\map\solid.lua
Orblike\reference\112020\engine\game\object.lua
Orblike\reference\BUILDRX2\engine\game\object.lua
Orblike\reference\112020\engine\math\polygon.lua
Orblike\reference\112020\engine\math\circle.lua
Orblike\reference\112020\engine\math\triangle.lua
Orblike\reference\BUILDRX2\engine\math\triangle.lua
Orblike\reference\112020\engine\math\line.lua
... [71 more lines]
Those are all in reference/. Let me check just the top-level Lua files.
Bash (ls -la E:/a327ex/Orblike/*.lua)
-rw-r--r-- 1 adn 197121 544033 Apr 14 14:43 E:/a327ex/Orblike/main.lua
-rw-r--r-- 1 adn 197121 9463 Mar 3 21:02 E:/a327ex/Orblike/profiler.lua
Just main.lua and profiler.lua. Let me verify profiler is used by main.lua, then set this up.
Grep (profiler)
231: profiler:on_sound()
255: profiler:on_sound()
7144:require('profiler')
7147:profiler:init({font = 'pf', layer = effects})
7151: profiler:draw()
[Showing results with pagination = limit: 5]
TodoWrite
Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable
Bash (mkdir -p E:/a327ex/Orblike/v1)
(Bash completed with no output)
Bash (cd E:/a327ex/Orblike && mv main.lua v1/ && mv profiler.lua v1/ && mv anchor v1/)
(Bash completed with no output)
TodoWrite
Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable
Bash (cp -r E:/a327ex/Anchor2/framework/anchor E:/a327ex/Orblike/anchor)
Exit code 1
cp: cannot overwrite non-directory 'E:/a327ex/Orblike/anchor' with directory 'E:/a327ex/Anchor2/framework/anchor'
Bash (ls -la E:/a327ex/Orblike/anchor 2>/dev/null; file E:/a327ex/Orblike/anchor 2>/de...)
-rwxr-xr-x 1 adn 197121 4175872 Apr 3 11:54 E:/a327ex/Orblike/anchor*
E:/a327ex/Orblike/anchor: PE32+ executable for MS Windows 6.00 (console), x86-64, 6 sections
There's an anchor executable (no extension) alongside anchor.exe. Let me check the full picture.
Bash (ls -la E:/a327ex/Orblike/anchor*)
-rwxr-xr-x 1 adn 197121 4175872 Apr 3 11:54 E:/a327ex/Orblike/anchor.exe*
Bash (ls -la E:/a327ex/Orblike/v1/anchor/ | head -5)
total 256
drwxr-xr-x 1 adn 197121 0 Apr 13 09:33 ./
drwxr-xr-x 1 adn 197121 0 Apr 15 21:34 ../
-rw-r--r-- 1 adn 197121 4881 Feb 24 21:51 animation.lua
-rw-r--r-- 1 adn 197121 16703 Feb 24 21:53 array.lua
Good, v1 framework moved fine. The cp issue was likely a Windows/MSYS path confusion with anchor.exe. Let me use a different target name.
Bash (cp -r E:/a327ex/Anchor2/framework/anchor/ E:/a327ex/Orblike/anchor/)
Exit code 1
cp: failed to access 'E:/a327ex/Orblike/anchor/': Not a directory
Bash (mkdir E:/a327ex/Orblike/anchor && cp -r E:/a327ex/Anchor2/framework/anchor/* E:/...)
(Bash completed with no output)
Bash (ls E:/a327ex/Orblike/anchor/)
animation.lua
array.lua
camera.lua
class.lua
collider.lua
color.lua
font.lua
helpers.lua
image.lua
init.lua
input.lua
layer.lua
math.lua
object.lua
physics.lua
shake.lua
spring.lua
spritesheet.lua
timer.lua
TodoWrite
Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable
Bash (cmd.exe /c "mklink /J E:\a327ex\Orblike\v1\assets E:\a327ex\Orblike\assets")
Microsoft Windows [Version 10.0.26200.8246]
(c) Microsoft Corporation. All rights reserved.
E:\a327ex\Orblike>
Bash (ls -la E:/a327ex/Orblike/v1/assets 2>/dev/null | head -5)
(Bash completed with no output)
Bash (cmd.exe /c "dir E:\a327ex\Orblike\v1 /AL" 2>/dev/null; ls -d E:/a327ex/Orblike/v...)
Exit code 2
Microsoft Windows [Version 10.0.26200.8246]
(c) Microsoft Corporation. All rights reserved.
E:\a327ex\Orblike>
Bash (cd E:/a327ex/Orblike && cmd.exe /c "mklink /J v1\assets assets")
Microsoft Windows [Version 10.0.26200.8246]
(c) Microsoft Corporation. All rights reserved.
E:\a327ex\Orblike>
Bash (test -d E:/a327ex/Orblike/v1/assets && echo "exists" || echo "missing"; ls E:/a3...)
missing
Bash (powershell.exe -Command "New-Item -ItemType Junction -Path 'E:\a327ex\Orblike\v1...)
Directory: E:\a327ex\Orblike\v1
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----l 15/04/26 21:37 assets
Bash (ls E:/a327ex/Orblike/v1/assets/ | head -5)
ARROW_Hit_Body_stereo.ogg
ARROW_Hit_Body_stereo.wav
ARROW_Hit_Body_stereo_lightmed.ogg
ARROW_Hit_Wood_Shield_stereo.ogg
ARROW_Hit_Wood_Shield_stereo.wav
Now the run scripts.
TodoWrite
Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable
Read (E:/a327ex/Orblike/run.bat)
Wasted call — file unchanged since your last Read. Refer to that earlier tool_result instead.
Current run.bat is anchor.exe . — that'll now run v2 at root. Just need run-v1.bat.
Write (E:/a327ex/Orblike/run-v1.bat)
File created successfully at: E:/a327ex/Orblike/run-v1.bat (file state is current in your context — no need to Read it back)
TodoWrite
Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable
Now for the v2 starter main.lua. Let me base it on the arena pattern — minimal, just enough to confirm the engine + framework work.
Read (E:/a327ex/Anchor2/arena/main.lua)
1 --[[
2 arena — Anchor 2 test game.
3
4 Enemies: Orblike seeker (~9203–9514); `physics_enable_collision('enemy','enemy')`.
5
6 Projectiles: Orblike default box 10×4 sensor (~7822), speed 190, spawn spring + flash;
7 hit FX: impact point (~12597–12603), `hit_circle` + `hit_particle` null_arrow-style (~8305–8308).
8
9 Player contact: `hit_circle` + particles at contact (~12577–12584), spring 0.2 + flash (~7310–7319).
10
11 Camera fixed (no follow).
12 ]]
13
14 require('anchor')({
15 width = 480,
16 height = 270,
17 title = "Anchor 2 arena",
18 scale = 3,
19 filter = "rough",
20 })
21
22 print('arena: framework loaded OK')
23
24 -- Palette (cool-dark, high contrast foreground)
25 bg_color = color(16, 16, 24)
26 fg_color = color(235, 235, 235)
27 player_color = color(100, 150, 255)
28 enemy_color = color(220, 80, 80)
29 bullet_color = color(255, 220, 100)
30
Write (E:/a327ex/Orblike/main.lua)
File created successfully at: E:/a327ex/Orblike/main.lua (file state is current in your context — no need to Read it back)
TodoWrite
Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable
Here's the final structure:
Orblike/
main.lua ← v2 starter (dark screen, framework initialized)
anchor/ ← v2 framework (19 files)
anchor.exe ← engine binary
assets/ ← game assets
run.bat ← runs v2 (anchor.exe .)
run-v1.bat ← runs v1 (anchor.exe v1)
v1/
main.lua ← old game code
profiler.lua ← old profiler
anchor/ ← v1 framework (17 files)
assets → junction to ../assets
tags.md, interactions.md, lore.md, todo.md, ORBS.md, etc.
orbs/, reference/, recordings/, tools/
Can you test both? run.bat should show a dark window (v2), and run-v1.bat should run the old game as before.
Yes, let's move ALL .md files to reference/ and remove any folders that only contained .md files.
Bash (ls -la E:/a327ex/Orblike/orbs/)
total 228
drwxr-xr-x 1 adn 197121 0 Mar 12 10:27 ./
drwxr-xr-x 1 adn 197121 0 Apr 15 21:38 ../
-rw-r--r-- 1 adn 197121 54513 Mar 12 10:27 ability_design_reference.md
-rw-r--r-- 1 adn 197121 139657 Mar 3 16:45 ability_reference.md
-rw-r--r-- 1 adn 197121 25419 Feb 28 07:59 system_design.md
Bash (cd E:/a327ex/Orblike && mv interactions.md lore.md orblike_ability_system.md tag...)
(Bash completed with no output)
Done. All .md files moved to reference/, and orbs/ removed (it only contained .md files).
What does mov_to_spritesheet.py do?
Read (E:/a327ex/Orblike/tools/mov_to_spritesheet.py)
1 #!/usr/bin/env python3
2 """
3 Convert a .mov animation to a black-and-white spritesheet for Anchor engine.
4
5 === FULL WORKFLOW ===
6
7 This script is one step in a pipeline for getting FX pack animations into Orblike.
8 The source animations come from commercial FX packs (e.g., RTFX) which ship as
9 pre-rendered .mov files: 1000x1000 RGBA at 24fps, colored effects on transparent
10 background. The game uses a dark-variant visual style (near-black background,
11 white/monochrome effects), so we need to convert these to white-on-transparent
12 spritesheets.
13
14 Step 1: Choose an animation
15 Browse E:/a327ex/RTFX/2. Prerendered animations/FX elements/
16 Good candidates for hit effects: anything with "Hit Radial", "Hit Explosion Radial"
17 Good for projectile trails: "Shot Right Loop", "Projectile Right Loop"
18 Good for explosions: "Explosion Radial"
19
20 Step 2: Run this script to convert .mov -> BW spritesheet
21 cd E:/a327ex/Orblike
22 python tools/mov_to_spritesheet.py "<path_to.mov>" assets/<output_name>.png [options]
23
24 Options:
25 --threshold T Brightness cutoff 0.0-1.0 (default 0.5)
26 Controls how much of the soft glow survives conversion.
27 Lower (0.2-0.3) = more glow, softer look, bigger effect
28 Medium (0.4-0.5) = balanced, good default
29 Higher (0.6-0.8) = only bright core, sharper/smaller
30 This is the main knob to tweak per-animation. Some animations
... [271 more lines]
It's a .mov → spritesheet converter for getting FX pack animations (like RTFX) into the game. Extracts frames with ffmpeg, converts to white-on-transparent using a brightness threshold, crops/scales, and stitches into a horizontal spritesheet PNG. It's a production tool, not runtime code — stays in tools/.
You created a symlink for the v1 assets folder, don't do that, just copy it fully.
Bash (cd E:/a327ex/Orblike && rm -rf v1/assets && cp -r assets v1/assets)
(Bash completed with no output)
Done. v1/assets/ is now a full copy.
I've moved run-v1.bat to v1/ folder, change it so it runs properly from there.
Read (E:/a327ex/Orblike/v1/run-v1.bat)
1 @echo off
2 cd /d "%~dp0"
3 .\anchor.exe v1 %*
4
Edit (E:/a327ex/Orblike/v1/run-v1.bat)
The file E:/a327ex/Orblike/v1/run-v1.bat has been updated successfully. (file state is current in your context — no need to Read it back)
Now it runs from v1/ as the working directory, using ../anchor.exe to find the engine and . as the project directory (itself).
<local-command-caveat>Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.</local-command-caveat>
<command-name>/model</command-name>
<command-message>model</command-message>
<command-args>claude-opus-4-7[1m]</command-args>
<local-command-stdout>Set model to claude-opus-4-7[1m]</local-command-stdout>
Great, now let's brainstorm the high-level things we have to do. Based on what's described on the files, what's a high-level plan that you'd proposed?
[Request interrupted by user]
Great, now let's brainstorm the high-level things we have to do. Based on what's described on the files, what's a high-level plan that you'd propose?
Here's how I'd think about it. I'll split this into architectural decisions we need before coding, a rough phase order, and open questions. I think discussing the architectural decisions first matters more than the phase ordering.
The big picture
We're not just building a game — we're building a platform for leagues. That reframes everything. The "shell" (menus, inventory, map/run structure, progression) needs to be a stable substrate with clean extension points. Leagues are the thing that varies. This is closer to engine work than gameplay work, and the architectural decisions have long tails.
Key architectural decisions to make early
These are the things that, if we get wrong, force painful rewrites later:
League plugin architecture. How does a league (tower defense, survivors, etc.) hook into the core? A few options:
- Leagues register themselves and implement hook functions (
on_map_start,on_enemy_spawn,on_map_complete) - Leagues entirely override specific gameplay systems (spawn logic, scoring, win condition)
- Leagues are fully custom
main_loopequivalents called by the map system
In Anchor 2's "poll, don't subscribe" philosophy, hooks feel wrong. More likely leagues are "a table of functions the map loop calls at defined points," which is really the same thing, just rephrased. But it's worth nailing down early because every league you write downstream will be shaped by this.
- Leagues register themselves and implement hook functions (
Screen/state management pattern. Stack of screens? Single
current_screenvariable? Tree? Anchor 2's explicit style suggests something like acurrent_screen+ a switch-statement inupdate/draw. Stacks are useful for modal dialogs but add complexity. I'd lean toward simplest-possible and see when it breaks.Save data schema. What gets saved, when, and how it migrates. If we defer this decision but write code that assumes it, we pay for it later. Probably a single JSON/Lua table per save slot with versioning.
Data model for persistent state. Inventory, maps completed, currencies, unlocks. Shape these tables deliberately — they'll be referenced from many places.
UI component pattern. Given Anchor 2 is procedural-first, UI is probably "state tables + stateless draw/update functions" rather than widget classes. But this is worth a quick exercise — build one button, one text input, one menu — and see if the pattern works before building dozens.
Proposed phase order
Phases 1–3 unblock everything else. Phase 4 onward is where the real game design happens.
Phase 1: UI primitives & screen architecture
- Text rendering (fonts are already in Anchor 2)
- Buttons, sliders, text inputs — minimal set
- Screen state management (the pattern decision above)
- Transitions between screens
- Goal: able to navigate between blank-but-functional screens
Phase 2: Main menu + options
- Simplest real screens. Exercises the primitives.
- Settings (volume, resolution, keybinds) + settings persistence
- Goal: a main menu, options screen, and working save for settings
Phase 3: Run lifecycle skeleton
- "New run" → "In map" → "Map complete" → "Back to map selection" → "Run ends"
- At this stage "in map" is a stub that just says "pretend combat happened, here's some loot"
- This validates the run loop end-to-end before combat exists
- Goal: you can click through a full run without actually playing combat
Phase 4: Map system (the PoE part)
- Atlas/map tree structure and data model
- Map selection UI
- Map modifiers (prefixes/suffixes, tier, rarity)
- This is where league plug-in points become concrete
- Goal: a functioning atlas you can navigate and select maps from
Phase 5: Inventory & loot
- Data model for items (boards, ability gems, modifiers, currencies)
- Inventory UI
- Loot drop → pickup → inventory flow (stub drops from the stub combat)
- Goal: you can receive items from a "completed" map and see them in inventory
Phase 6: Orb composition UI (board editor)
- Viewing boards, placing abilities/modifiers in slots
- Visualizing connections (links)
- Save the composed orb set as part of save data
- Goal: you can build a character even though combat doesn't use it yet
Phase 7: Combat port
- The big one. Port combat from v1 to v2 idioms.
- Replace the stub in Phase 3 with real combat.
- Connect orb composition to combat behavior.
- Goal: real combat inside the full shell
Phase 8: First league
- Pick one league concept (probably the "default" Orblike mode) and implement it against the plugin architecture
- Validates the extension points actually work
Open questions I'd want answered before Phase 4
- Meta-progression. Is there permanent unlock progression across runs (like Hades), or are runs fully independent (like PoE)? This shapes save data and the run end screen significantly.
- Character system. Is there a character/class concept, or is the player just "the player"? If classes, they might be the first real variable the league plugin sees.
- Economy. PoE has dozens of currency types for crafting. Does Orblike have that? It shapes inventory design.
- Run length. Is one run ~30 min (Hades), ~4 hours (PoE mapping session), or ~60+ hours (PoE league)? Affects save frequency and run state design.
- The default Orblike mode itself. Is it the bullet-hell we already have, or does that eventually become a league too, with some simpler mode as the "default"?
What I'd do first
Honestly — before writing any code — I'd want to spend a session just on the league plugin architecture. That decision has the widest blast radius. Everything from the map data model to the inventory system to combat integration is shaped by "what can a league change and what can't it."
After that decision is made, Phase 1 (UI primitives + screen state) is a natural first build. It's low-risk, teaches us Anchor 2 patterns, and is a prerequisite for everything visible.
Does this shape match what you had in mind? And how firm are your answers to the open questions above — should we dig into the league architecture now, or do you have a view already?
Let's imagine the simplest possible high level structure for the game. Before a run starts, you choose your initial abilities + character/class. These can be in either 1 single screen or 2 screens. Then you are throw into the map view and you choose your first map for free. Click it, and it throws you in the arena. The arena is what happens in the old Orblike we already have coded, map gen, enemies, battle. Additionally, it needs to have inventory UI and orbs UI. In the game, you can also have a character + orbs UI, I think this may be the same screen you can have while in an arena. Shows your orbs, slots in them, connections between them, and then the inventory to the side so you can drag and drop abilities and various items on your orbs. In general, any UI for doing something with resources should be like this. So in the future if we have some crafting UI, inventory to the right, the crafting UI with slots of inventory items on the left. The map system should be... Actually, why not make it exactly like Path of Exile and have a hideout? So this hideout is a physical place that acts as your base. To do various things, you walk up to an NPC there and it takes you to the UI to do the thing. Throughout the run you can unlock various NPCs (for now let's have no progression between runs). And then at the center there's the map UI, which allows you to place a map item + modifiers so you can go do the map. Yea, this way we don't even need a main menu. Just start the game directly in the hideout, everything except options screen (which can be reached pressing ESC) happens through NPC in the hideout. What do you think? Anything important I'm missing?
And then the league system is just what happens inside each map, just like in PoE. Each map can randomly have league mechanics in them that will work differently based on the maps. Some leagues might add additional NPCs to the hideouts so you can do more things, but I think there's no need for any super special system. Thoughts?
This is a strong design. Let me react to what's good and surface the real questions.
What's good
- Hideout-as-hub is elegant. It collapses "main menu + meta-game + run launch" into one coherent physical space. Much better than a menu system — every interaction is diegetic, and adding a new system is just "add an NPC."
- "Inventory on right, action UI on left" is a strong unification. Crafting, orb composition, trading, enchanting, whatever — they all feel the same. PoE itself has some inconsistency here (currency stash, heist, etc.) that Orblike can avoid by committing to the pattern early.
- No meta-progression for now is wise. Removes a whole category of design (unlock pacing, save migration, balancing meta vs. in-run power) and lets the core loop prove itself first.
- Leagues as "stuff in maps" matches PoE exactly and is architecturally trivial — no special system needed, just map modifiers that add spawn logic / NPCs / objectives.
The real questions
These are the ones you probably have answers to but that aren't stated:
What is a "run," precisely? Without meta-progression, a "run" is a roguelike attempt: play → die → reset. But what resets?
- Does death nuke your hideout state (inventory, maps, orbs)?
- Or does the hideout persist and "run" just means "one mapping session"?
This is the biggest unanswered question. If hideout persists, we're more like PoE (your items ARE your progression, hardcore death = true loss). If hideout resets, we're more like traditional roguelikes.
Character/class — what does it mean mechanically? "Choose character + abilities" is clear, but:
- Is class a visual/thematic choice only?
- Or does it affect stats, starting orbs, starting abilities, inherent passives?
- In SNKRX (visual reference) there's no class. In PoE class is a starting point in a huge passive tree. In Hades class is a weapon. Which model?
How does a map complete / how does a run end?
- Map: clear all packs? Kill a boss? Time limit?
- Run: die in a map? Or some other explicit end condition?
- Do you return to hideout between maps, or is there a "map chain" concept?
Map item economy. First map is free. Then what?
- Maps drop from maps (PoE model — self-sustaining)?
- Need a fallback "always have at least one free map" so players can't softlock after a bad run
- Can you craft modifiers onto maps (PoE alch/chaos)?
UI-during-combat behavior. The orbs/inventory UI being accessible during arena is great, but:
- Does opening it pause combat? (PoE doesn't, Hades does with its menus)
- Can you drag abilities onto orbs mid-fight and change your build in real time? That's very powerful — could be a feature or an exploit
- Does it cover the whole screen or overlay?
Things you might be missing
Stash. Even without meta-progression, within a single run you'll want somewhere to put items you're not currently using. Inventory gets full. PoE's stash is essential even for "just this character." An NPC (or physical object) for this.
Vendors / currency sinks. If there's loot, there'll eventually be junk items. Sell them to a vendor NPC for currency? Destroy them for materials? Just trash them? Without this, inventory becomes a pure accumulation problem.
Death UX. What does the death screen look like? What carries over (nothing, based on no-meta-progression)? Where do you end up (back in hideout?)? This is a surprisingly emotional moment in roguelikes and worth designing deliberately.
Map selection ≠ one map. PoE's atlas is a huge tree with hundreds of maps and meaningful choices about which to run. For Orblike's first cut, is the "map UI" just a grid of available maps? A tree? A single slot that you feed one map into at a time? The simplest version is "one slot, one map, click to enter" which is probably the right start.
Hideout implementation cost. Physical walking-around hideout is more expensive than it sounds — character controller, collision, NPC interaction zones, camera. Not a blocker, but worth acknowledging that Phase 1 gets bigger if we go physical vs. a "grid of NPC portraits you click" non-physical version. My gut says: go physical from the start because it's the vibe you want, but know it's ~1-2 extra weeks of work vs. portraits.
My suggested first call
Before we architect anything, answer question #1 — what resets on death? That single answer determines:
- Save system design (one persistent save vs. per-run state)
- Hideout state model
- Whether the hideout has any meaning beyond "the thing between runs"
- Whether map inventory is a long-term asset or a per-run resource
What's your thinking there?
- A run consists of going through all maps and beating the final boss, whatever that is. I want to balance the game in two ways, one where a run can last ~1 hour, and another where it will last ~5-10 hours. But we'll start with the 1 hour one and then as we add more content work towards the longer one. Death nukes everything you have and you start from zero for now, since no progression between runs.
- I'm not sure yet. Maybe it's just initial equipment, maybe it's actual class modifiers. We'll start the game with no classes/characters and just different initial equipments I think, and then add more if we feel it's necessary for variety. Equipments we have are orbs, sigil, robe, rings and there's another one I forgot. But sigil is probably the equipment that has most potential for initial diversity, so probably just start with different sigils.
- Like in PoE, you can get out of a map at any time, even if you haven't killed everything. But there's a boss that has higher chance to drop good things in every map. So technically that's the end of the map. But you'll want to kill most monsters so you get more resources. I don't know yet if we'll have an XP system or not, since I don't know if we want things like a passive skill tree or similar. And yes, you return to the hideout between maps via portals. You can also return to the map afterwards, up to a duration, just like in PoE.
- Maps drops from monsters in maps, yes, self-sustaining. We'll make sure players can't softblock in various ways, most of them important at the beginning of a run, but we can come up with consistent and understandable systems that achieve that goal. And yes, you can craft modifiers into maps, although the specific details are still up in the air and will depend on how we do modifiers for items specifically, since it should be roughly the same system for everything.
- Yes, you can open in arena and I think it makes sense to not pause content, to force people to try things out in the hideout instead. You can drag abilities mid-fight, but we'll need to make sure it can't be exploited too. And it does cover the whole screen. It will be the same UI as the one you have in the hideout for it.
Stash: we can just make the inventory infinite. There's no reason to add friction here like in PoE because there's no multiplayer economy. Vendors: Yea, we can have various sinks and resource trades. This is an easy problem to solve as we make the game in more detail. Death UX: Nothing carries over in death, you just start again a new, empty hideout. Sure, we can design it deliberately and with care, but I don't have any specific ideas now. Usually people like stats about their build and what they achieved and so on, so we could add all that there. Map selection: Not sure how we'll display the maps yet, not even sure if we want them to be connected by lines like in PoE, where maps can only drop maps adjacent to them. I have to think about what makes sense lore-wise. Lore-wise, you're going through various places to charge your orbs with null mana, and thus clean up various places of null mana. You achieve this by using portals to various places. A "map item" should essentially be the resource required to teleport to a specific place. Maybe this can be crafted from base resources, or maybe it just drops already ready like in PoE. I think it would make sense for it be made from various resources you can find from monsters in various areas, like you need mana that is corrupted in a specific way around that area to serve as a beacon for the teleporter, or something. I have to think more, the point is, this is still up in the air. But if you have any ideas just say so, especially ideas that seem interesting both lore and gameplay wise. Hideout implementation cost: all those are already implemented in Orblike, so we can just copy it. Copying the base movement first also allows us to have the "base" for combat already done when we later get into re-implementing the arena.
Good — most of this is clear. Let me push on a couple of things and then give you map system ideas.
One critique: UI-during-combat
"Covers the whole screen + doesn't pause" is a strong choice that I want to make sure is intentional. The consequence: if you open inventory/orbs mid-fight, you literally can't see what's happening to you. So you're not really "managing your build during combat" — you're choosing between fighting blind or using the UI in lulls.
That might be exactly the intent (forces you to find a safe moment, punishes panic-swaps). But it's a different feeling than "tactical mid-combat reconfiguration." Worth being explicit about which you want. If the former, the design works. If the latter, you probably want a half-screen or translucent UI.
One middle option: full-screen UI that doesn't pause, but monsters still move and fire at you. You hear hits and see damage numbers pop up over the UI. Forces fast interaction. Same friction as pause-less but more honest about it.
Map system ideas
The lore you have is really good and wants a specific mechanical shape. "Cleanse regions of null mana by charging your orbs; map items are teleporter beacons" has strong implications:
Idea 1: Null residue as the core crafting material
What if the null mana you're cleansing IS the primary currency?
- Monsters drop "null residue" when killed (the stuff you're absorbing)
- Different region types leave different residue flavors (corrupted, entropic, resonant, etc.)
- Map items are crafted: base teleporter + X residue of the target region's flavor + tier cost
- Map modifiers are crafted: residue + alignment materials determine the map's character
This gives you one unified resource system — null mana flavors are currency, crafting materials, map-crafting materials, modifier-crafting materials. PoE has a dozen currency types; you could have ~5-8 residue flavors that double as everything.
Idea 2: Regions over atlas tree
PoE's atlas tree is great but complex and required years of iteration. For Orblike, I'd propose Regions instead:
- There are ~6-10 regions in the world (Forest of Whispers, Drowned Cathedral, Ember Wastes, etc.)
- Each region has a tier range (e.g., Forest = tier 1-5, Cathedral = tier 3-8)
- A map item = "beacon attuned to Region X at Tier Y"
- The "map UI" is a globe/map showing all regions; your available map items populate slots next to them
- You place a beacon on a region → portal opens → go
This is much simpler than an atlas tree while still supporting variety, and it matches the lore perfectly (you're cleansing specific places in the world, not abstract nodes).
Idea 3: How maps connect/progress
You asked about connectivity. Three options, from simplest to richest:
(A) No connectivity, flat tier progression. Just run any map at any tier you can afford. Higher tier = better loot. Simple, works, a bit flat.
(B) Region unlocking. Start with 1-2 regions. Beating a region's boss unlocks the next. By end-game all regions unlocked. Lore fits (you're discovering new corrupted places).
(C) Connectivity by residue flavor. Each region drops specific residues. You need residue from Region A to craft beacons to Region B. Creates natural "routes" through regions without a visual tree. Lore fits (you're following trails of null corruption).
I'd start with (B) — simplest and linear. Upgrade to (C) later if you want more depth. Avoid (A) because it has no discovery.
Idea 4: The boss / run completion
"Beat the final boss" as run end condition is great, but what is the final boss lore-wise? Some options:
- The Null Source. The origin of null mana. Defeating them permanently cleanses the world (for this run).
- The Hollow King. A being made of null mana hoarding it. Defeating them releases what they hoarded.
- Your own corruption. The last fight is you, fully null-corrupted, in a mirror region. A dramatic Hades-style final boss with narrative weight.
This affects the map progression too — does the final boss appear in a special region? Is it at tier max of every region? Does every region have a mini-boss culminating in the Null Source?
My gut: one special "final region" that only unlocks after you've cleansed a certain threshold. It's a one-shot: beat it, run ends. Lose, run ends. High stakes.
Smaller things
The forgotten equipment slot. You listed orbs, sigil, robe, rings. Typical missing slot in this kind of game: gloves (affects casting/crit), boots (affects movement speed), or belt (affects resource capacity). Gloves feel most thematically fitting for an orb-caster. Just a nudge to remember.
Return-to-map mechanic. In PoE, leaving a map creates a portal that persists for ~10 min. For Orblike: when you leave, do portals appear physically in the hideout? Or is it a menu listing "recent maps — rejoin (8:32 remaining)"? I'd vote physical portals — consistent with the diegetic hideout.
Softlock prevention patterns. Common and clean:
- A starter NPC who always sells the lowest-tier base beacon for a trivial cost
- "Emergency portal" — if you have literally zero map items, free tier-1 map available from this NPC
- First maps in a new region are always free/discounted (incentivizes exploration)
Pick any of these — they compose well and never let players get stuck.
XP / passive tree. You said you're unsure. My vote: no XP, no passive tree for v1. Power comes from gear (orbs, sigil, robe, rings) and how you compose them. PoE's passive tree is iconic but a massive design-and-balance project. If you want extra depth later, a small passive tree tied to region cleansing (each cleansed region grants a choice) is interesting and thematic. Defer.
Next step
Assuming you like the Regions model — the next architectural decision is the data shape of a map item and a region. Once we know that, everything downstream (inventory UI, map UI, crafting UI, map generation) has a concrete target. Want to nail that down, or is there something earlier you want to discuss first?
For the map system let's just start with it working just like PoE except simpler. Maps can drop maps. No connectivity. It's just random. There's "map UI" that shows them spread randomly across a world map, and the player can hover over each to get more information on each map. Later as we implement more of the game we can decide on how resources will tie into it and how we'll connect them if we do.
Forgotten equipment slot: this is described in one of the documentation files, so you should find it by reading the files. Portals: yes, a portal physically appears in the hideout, the most recent one. Softlock: sure, an NPC is the cleanest.
Let me find the equipment slot in the docs.
Grep (sigil|robe|ring)
Found 250 files limit: 250
Orblike\reference\todo.md
Orblike\reference\lore.md
Orblike\reference\file_split_and_ownership.md
Orblike\reference\feature_by_feature_analysis.md
Orblike\reference\reference_games_strategy.md
Orblike\reference\anchor_usage_analysis.md
Orblike\reference\ai_coding_paradigms.md
Orblike\reference\interactions.md
Orblike\reference\balatro_shader_analysis.md
Orblike\reference\ability_editions.md
Orblike\reference\map_visual_system.md
Orblike\reference\ability_modifier_map.md
Orblike\reference\ability_design_reference.md
Orblike\reference\orb_visual_mockup.html
Orblike\reference\orblike_ability_system.md
Orblike\reference\three_phase_system.md
Orblike\reference\004\lua\main.lua
Orblike\reference\004\lua\anchor\init.lua
Orblike\reference\004\lua\anchor\grid.lua
Orblike\reference\004\lua\anchor\timer.lua
Orblike\reference\004\lua\anchor\shake.lua
Orblike\reference\004\lua\anchor\vec2.lua
Orblike\reference\004\lua\anchor\spring.lua
Orblike\reference\004\lua\anchor\color.lua
Orblike\reference\004\lua\anchor\hitfx.lua
Orblike\reference\239A5-P60B91D\lua\main.lua
Orblike\reference\008 - Antitower [success]\lun\group.lua
Orblike\reference\008 - Antitower [success]\lun\shake.lua
Orblike\reference\008 - Antitower [success]\lun\layer.lua
... [221 more lines]
Grep (equipment|equip_slot|item_slot|slots.orb.sigil|sigil.robe.ring)
90:> **Game system mapping:** The player character renders as a floating, robed, masked figure with sigil, orbs, and rings as described. Mask, sigil, robe, and rings are the four equipment slot types (see todo.md "Equipment System"). The floating animation replaces walking — there is no foot contact with the ground for orb-user characters.
Grep (sigil|robe)
81-
82:- **Robed.** Long, flowing robes cover most of the body. The robe is part of the mage's identity and conceals the body's mana flows from outside view.
83-- **Masked.** A face-covering mask, often stylized. The mask signals the mage's signature personal-mana frequency — masks are personal identity objects, not interchangeable. Reference: Shun from *Shin Sekai Yori*.
84:- **Sigil overhead.** A glowing magical glyph — the **sigil** — floats above the head. The sigil is a binding/coordination point that lets orbs orbit the caster cleanly and lets mana flow between orbs and body without interference. Without a sigil, an orb user would have to handle each orb individually and manually.
85-- **Orbs orbiting.** 1-N orbs circle the caster, depending on binding capacity.
--
87-
88:The full image: a floating, robed, masked figure with a sigil glowing overhead, orbs circling around, and hands and feet ringed with small magical bands.
89-
90:> **Game system mapping:** The player character renders as a floating, robed, masked figure with sigil, orbs, and rings as described. Mask, sigil, robe, and rings are the four equipment slot types (see todo.md "Equipment System"). The floating animation replaces walking — there is no foot contact with the ground for orb-user characters.
91-
Found it. The five equipment slots are Mask, Sigil, Robe, Rings, Orbs. The mask is the identity piece — it signals the mage's personal-mana frequency and is non-interchangeable narratively (though mechanically it's still a gear slot). Rings go on hands and feet (so potentially 4 ring slots, but worth confirming when we get there).
Recap of locked-in design
Before we start coding, let me confirm what's decided so we can reference it later:
- No main menu. Game opens directly in the hideout.
- Hideout = physical walkable space. Movement code ported from v1 arena.
- NPCs for activities. Each NPC opens a UI for a specific activity (shop, map, etc.).
- Inventory & orbs/character UI are key-bound (not NPC-gated), accessible everywhere, full-screen, no pause in combat.
- Unified UI pattern: inventory on right, action UI on left with slots.
- Equipment slots: Mask, Sigil, Robe, Rings, Orbs.
- Run = clear final boss. Target ~1hr first, scaling to 5-10hr later.
- Death nukes everything, fresh empty hideout, no meta-progression.
- Map system: PoE-style but simpler. Random placement on world map UI, hover for info, maps drop maps, no connectivity/tree.
- Map portals: most recent physically appears in hideout.
- Softlock prevention: NPC sells cheap starter maps.
- Leagues = map mechanics. No special system. Some leagues may add hideout NPCs.
- ESC menu: options (graphics/audio/keybinds), resume, quit to desktop.
- Starting gear selection: TBD — worth deciding.
One small thing still open
How does a new run start? Two options:
- (A) Character creation screen (pick starter sigil + abilities, then spawn in hideout)
- (B) Diegetic — you spawn in empty hideout, there are pedestals/altars with starter sigil options, walk up and grab one
(B) is more consistent with "no main menu, everything is diegetic." (A) is simpler to implement and more legible to the player. I'd lean (B) for consistency but (A) is defensible.
Suggested next step: write a design doc, then start Phase 1
I'd propose we do two things:
Write a short design doc (
reference/design.mdor similar) capturing the decisions above. Not prescriptive — just a record so we don't forget or drift. Maybe 1-2 pages.Start Phase 1: hideout shell. The smallest meaningful thing we can build:
- Player sprite that walks around with ported v1 movement
- Empty square room with wall collision
- Camera follows player
- ESC opens a stub "options" menu that just says "options (ESC to close)"
This gets Anchor 2 running, validates movement port, and gives us something to look at. Then we add an NPC, interaction prompt, stub UI, etc. one at a time.
Want me to write the design doc first, or jump into Phase 1?
For the hideout to be diegetic we need a way to give the player multiple options but that he can only pick once. If there are multiple masks, why wouldn't he pick them all?
[Request interrupted by user]
Let's go with diegetic. I have the answer. The orb + null mana cleaning system is sponsored by the Psyonian state. They want mages to risk themselves into doing this, and thus give them starting locations plus a home base they can operate from. All other NPCs are there to help multiple such adventurers into teleporting to various places to clear them of null mana.
[Request interrupted by user]
Let's go with diegetic. I have the answer. The orb + null mana cleaning system is sponsored by the Psyonian state. They want mages to risk themselves into doing this, and thus give them starting locations plus a home base they can operate from. All other NPCs are there to help multiple such adventurers into teleporting to various places to clear them of null mana. So the initial equipments can come from the initial NPC, who later becomes a vendor. He shows you multiple options, tells you you can pick one of them. Not sure if we'll go with allowing the picking of mask + sigil + 1-2 orbs, or just sigil + 1-2 orbs or just mask + 1-2 orbs, we'll decide later.
[Request interrupted by user]
Knowing all this, you can write the design.md file, place it alongside main.lua instead of inside reference. Then also do a plan.md file containing the implementation plan with all phases, now that we've locked in most of the high-level design.
Write (E:/a327ex/Orblike/design.md)
File created successfully at: E:/a327ex/Orblike/design.md (file state is current in your context — no need to Read it back)
Write (E:/a327ex/Orblike/plan.md)
File created successfully at: E:/a327ex/Orblike/plan.md (file state is current in your context — no need to Read it back)
Both files are in place at Orblike/design.md and Orblike/plan.md.
design.md captures all locked-in decisions: the Psyonian sponsorship framing, hideout structure, equipment slots (Mask/Sigil/Robe/Rings/Orbs), map system (PoE-simplified), inventory patterns, death/run rules, league architecture, and open questions.
plan.md breaks the work into 10 phases:
- Phase 1 — Hideout shell (walk around, camera, ESC stub)
- Phase 2 — UI primitives (text, buttons, full-screen pattern, real options menu)
- Phase 3 — NPC + interaction system
- Phase 4 — Inventory data model + UI (biggest downstream dependencies)
- Phase 5 — Starter gear selection (first real NPC UI)
- Phase 6 — Orb composition UI
- Phase 7 — Map UI + run lifecycle with stub arena (full meta-loop playable)
- Phase 8 — Arena port from v1 (the big one)
- Phase 9 — First league + league API validation
- Phase 10+ — Content, balance, retire v1
A few things I flagged that you may want to think on before the relevant phase:
- Phase 4's item schema is the widest-blast-radius decision in the whole plan. Inventory, equipment, loot, crafting, vendor, map mods all depend on it. Worth a dedicated design pass when we get there.
- Phase 7 gives you a playable meta-loop before any combat port work, which de-risks Phase 8 enormously. You can feel whether the game's structure works before committing to the port.
- Save system is listed under Phase 8 as a dependency concern — I'd want to at least sketch it during Phase 7 so Phase 8 doesn't break saves every iteration.
Want to start Phase 1, or review/adjust the plan first?
We'll start implementation on another session. We can end this session, call it "Orblike Design Plan"
Skill
Launching skill: end-session
Base directory for this skill: E:\a327ex.claude\skills\end-session
End Session Workflow
This skill is invoked from the same conversation where the work was done. You already have the full session context in memory — you do NOT need to read the transcript.
Step 1: Get Session Info
Ask the user: 1. Session title (max 30 characters). Examples: "Anchor Phase 10 Part 5", "Physics Arena Setup", "Timer System Fix" 2. Which project was this session for? Options: Anchor, Anchor2, emoji-ball-battles, orblike, other
Step 2: Write Summary
Write the summary from your conversation memory. You have the full session context — no need to read any files.
The summary should be thorough and detailed. Each major topic deserves its own section with multiple specific bullet points. Don't compress — expand.
Purpose: These summaries serve as searchable records. Future Claude instances will grep through past logs to find how specific topics were handled. The more detail you include, the more useful the summary becomes for finding relevant context later.
Format (this is just an example structure — adapt sections to match what actually happened):
# [Title] ## Summary [1-2 sentence overview of the session's main focus] **[Topic 1 - e.g., "Spring Module Implementation"]:** - First specific detail about what was done - Second detail - include file names, function names - User correction or feedback (quote if notable) - Technical decisions and why **[Topic 2 - e.g., "Camera Research"]:** - What was researched - Key findings - How it influenced implementation **[Topic 3 - e.g., "Errors and Fixes"]:** - Specific error message encountered - Root cause identified - How it was fixed [Continue for each major topic...] --- [Rest of transcript follows]Rules: - Be thorough — If in doubt, include more detail, not less. Each topic should be as detailed as possible while still being a summary. - Think searchability — Future instances will search these logs. Include keywords, function names, error messages that someone might grep for. - One section per major topic — Don't combine unrelated work into one section - Chronological order — Sections should match conversation flow - Specific details — Error messages, file names, function names, parameter values - Include user quotes — When user gave notable feedback, quote it (e.g., "k/d variables are not intuitive at all") - Weight planning equally — Research, proposals, alternatives considered, user feedback on approach are as important as implementation - Weight problems solved — Errors, root causes, fixes, user corrections all matter - Technical specifics — Include formulas, API signatures, parameter changes when relevant
Step 3: Get User Approval
Show the title and summary to the user. Wait for approval before proceeding. If they have corrections, fix them.
Step 4: Convert Transcript and Write Log File
# Find recent sessions (Claude + Cursor; see script). Same script lives in Anchor2: python E:/a327ex/Anchor2/scripts/find-recent-session.py --limit 5 # or: python E:/a327ex/Anchor/scripts/find-recent-session.py --limit 5The script shows sessions sorted by when they ended. The first result is the current conversation (since end-session was invoked here). Use it.
Convert the transcript to markdown and write the log file with the approved summary as header:
python E:/a327ex/Anchor2/scripts/jsonl-to-markdown.py [SESSION_PATH] E:/a327ex/anchor.blot.im/logs/[slug].md # or: python E:/a327ex/Anchor/scripts/jsonl-to-markdown.py ...The same script auto-detects Claude Code JSONL vs Cursor/Composer agent JSONL (
~/.cursor/projects/.../agent-transcripts/...). For Composer sessions, usefind-recent-session.py(it merges both sources) and pick the[cursor]line for the current chat.Use lowercase hyphenated slug derived from the title (e.g., "anchor-primitives-hitstop-animation").
Then replace the default header (
# Session YYYY-MM-DD...) with the approved title and summary.Step 5: Sync Context Files to Blot
cp E:/a327ex/.claude/CLAUDE.md E:/a327ex/anchor.blot.im/context/CLAUDE.md cp E:/a327ex/Anchor/docs/* E:/a327ex/anchor.blot.im/context/When the session was Anchor2 (optional):
cp E:/a327ex/Anchor2/.claude/CLAUDE.md E:/a327ex/anchor.blot.im/context/CLAUDE_ANCHOR2.mdStep 6: Commit Project Repo
Based on which project was worked on:
For Anchor:
bash cd E:/a327ex/Anchor git add docs/ framework/ engine/ scripts/ reference/ git statusFor Anchor2:
bash cd E:/a327ex/Anchor2 git add framework/ engine/ arena/ reference/ scripts/ docs/ .claude/ git statusFor emoji-ball-battles:
bash cd E:/a327ex/emoji-ball-battles git add -A git statusFor orblike:
bash cd E:/a327ex/orblike git add -A git statusFor other projects: Ask the user which files to stage.
IMPORTANT — FULL SUMMARY IN COMMIT: The commit message MUST include the FULL summary from the log file. Read the summary back from the log file to ensure nothing is missing.
IMPORTANT — COMMIT METHOD: The summary contains backticks, special characters, and markdown that WILL break heredocs and
git commit -m. ALWAYS use the file-based method below. NEVER try a heredoc first — it will fail and produce a malformed commit that needs amending.Commit using a temporary file (extracts only the summary text, not the markdown heading or
## Summaryline):# Extract summary: skip line 1 (# Title) and line 3 (## Summary), take lines 4 onward until the --- separator awk 'NR>=4 && /^---$/{exit} NR>=4{print}' E:/a327ex/anchor.blot.im/logs/[slug].md > /tmp/commit_msg.txt # Prepend the title (plain text, no #) and append attribution sed -i "1i [Title]\n" /tmp/commit_msg.txt printf "\nGenerated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n" >> /tmp/commit_msg.txt git commit -F /tmp/commit_msg.txtStep 7: Push Both Repos
# Push project repo to GitHub git push origin main # Push Blot repo (logs and context are already there) cd E:/a327ex/anchor.blot.im && git add -A && git commit -m "[Title]" && git push origin masterStep 8: Confirm
Tell the user: - Commit hash - That GitHub push succeeded - That Blot push succeeded
ARGUMENTS: Orblike Design Plan
Session title: Orblike Design Plan (19 chars, fits) Project: orblike
Now writing the summary:
# Orblike Design Plan
## Summary
Design and planning session for porting Orblike from Anchor 1 to Anchor 2. Established the pincer migration strategy (write the high-level shell fresh in Anchor 2, port combat last), set up the directory structure to support both versions concurrently, and locked in the full high-level game design in `design.md` and a 10-phase implementation plan in `plan.md`.
**Migration Strategy Discussion:**
- User proposed pincer approach: code the high-level portion (menus, between-maps, between-runs, UIs) in Anchor 2 first, then progressively port the low-level portion (combat, maps, abilities) from Anchor 1
- Claude assessed this strategy as sound and actually stronger than initially thought because the high-level portions don't exist yet — it's greenfield in the new engine, not a port
- Anchor 2's explicit procedural style (no tree, no hooks, IDs not pointers, poll not subscribe) is a better fit for menu/flow code than for combat code
- Eventual combat port will be substantial (uses every pattern v2 removed: tree hierarchy, tags, link, flow_to, action phases), but by then will have deep v2 fluency
- User's long-term vision: Orblike as a "homebase" game supporting PoE-style leagues (tower defense, survivors, etc.) plugged into the same map/run/progression shell
**Anchor 2 Engine Overview Research:**
- Read the full Anchor 2 engine overview blog post
- Key differences from v1: ~4500 lines across 19 files (vs v1's 2500 lines, 15 files), C engine unchanged
- Philosophy: minimal framework, maximal explicitness; procedural by default; classes only where they earn it (only collider remains a class)
- Main architectural changes: replace tree with ID lookup; replace subscriptions with polling; replace hook-based ordering with explicit update functions; game owns main loop (update/draw are top-level globals)
- Core patterns: `make_entity(self)`, `entities[id]`, `collection_update(list, dt)`, `process_destroy_queue()`, always check `_dead` on both entities in collision events
- Removed from v1: `an` god-object, `self:add(child)`, `self:tag()`, `self:link()`, `self:flow_to()`, early_action/action/late_action
**Orblike Codebase Exploration:**
- Read `reference/orblike_ability_system.md` (board-based ability system) and `reference/system_design.md` (original orb design)
- Read `main.lua` (544KB) — has combat working, 23 abilities, damage system, map generation, SNKRX visual style
- Missing: between-map transitions, loot/inventory, orb graph UI, map selection, run structure, meta-game
**Directory Structure Setup:**
- User initially proposed both Anchors inside Orblike/ with 2 main files, no conflicts
- Claude explored layout: engine takes directory arg, resolves paths relative to it (based on `Anchor2/arena/` pattern with `anchor.exe arena`)
- First proposal: v2 in subdirectory, v1 at root
- User preferred inverse: v2 at root, v1 tucked away
- Final structure:
- Root: v2 starter `main.lua`, v2 `anchor/` framework, `anchor.exe`, `assets/`, `run.bat` (runs v2)
- `v1/`: old `main.lua`, `profiler.lua`, v1 `anchor/`, `assets/` (full copy per user request — initially was a junction), `run-v1.bat`
- Moved v1 files: `mv main.lua profiler.lua anchor/ v1/`
- Copied v2 framework: `cp -r Anchor2/framework/anchor/* Orblike/anchor/`
- Initial attempt at junction failed via `cmd.exe /c mklink /J`, succeeded via PowerShell `New-Item -ItemType Junction`
- User then said don't use symlinks, copy fully — replaced junction with `cp -r`
- User moved `run-v1.bat` to `v1/` folder; Claude updated it to `..\anchor.exe . %*`
- Created v2 starter `main.lua` with `layer_new('game')`, minimal `update()` and `draw()` functions, dark background
**Documentation Cleanup:**
- User requested moving ALL `.md` files to `reference/` and removing any folders that only contained `.md` files
- Moved: `interactions.md`, `lore.md`, `orblike_ability_system.md`, `tags.md`, `todo.md` from root
- Moved `orbs/ability_design_reference.md`, `orbs/ability_reference.md`, `orbs/system_design.md` from `orbs/` then removed `orbs/` directory
- User asked about `tools/mov_to_spritesheet.py` — it's a .mov-to-spritesheet converter for RTFX FX pack animations (ffmpeg frame extraction → luminance threshold → crop/scale → stitch); stays in `tools/` as a production utility
**High-Level Game Structure Brainstorm:**
- User laid out the full structure: pre-run equipment selection, map view, arena (existing Orblike), inventory/orbs UIs accessible in arena, PoE-style hideout as physical base with NPCs
- Unified UI pattern: inventory always on right, action UI on left with slots (crafting, orb composition, map placement, trade all follow this)
- No main menu — game starts directly in hideout, everything diegetic except ESC options
- Leagues = map mechanics, not a top-level system (some may add persistent hideout NPCs)
**Critical Design Questions Surfaced:**
- "What resets on death?" — User: death nukes everything, fresh empty hideout, no meta-progression for now
- Run structure — clear final boss; target ~1hr first, scaling to 5-10hr
- Character/class — start with no classes, just different starter sigils (biggest diversity axis)
- Map completion — PoE-style, can leave anytime, boss has higher drop chance, portals persist ~10min for re-entry
- Map economy — maps drop from monsters, self-sustaining, craftable modifiers (unified with item modifiers TBD)
- UI during combat — full-screen, no pause, drag-and-drop works mid-fight (forces reconfig in hideout, mid-combat swap possible but costly)
- Inventory — infinite (no multiplayer = no need for friction)
- Map UI — PoE-style but simpler: maps random on world map, hover for info, no atlas tree/connectivity
**Equipment Slot Investigation:**
- User said 5 slots: orbs, sigil, robe, rings, + one forgotten
- Claude grepped `reference/lore.md` and found: **Mask** is the missing slot
- From lore: floating robed masked figure with sigil overhead, orbs circling, hands and feet ringed with magical bands
- Mask is the identity piece (personal-mana frequency signature) — narratively non-interchangeable but mechanically a gear slot
- Ring count TBD (4 for hands+feet, or 2 for hands only)
**Diegetic Starter Gear Solution:**
- Claude raised the issue: if multiple masks exist in the hideout, why wouldn't the player grab them all?
- User's lore answer: the orb + null mana cleaning system is **sponsored by the Psyonian state**. They want mages to risk themselves cleansing corruption, so they provide starting gear and a hideout base
- Starter gear comes from an initial NPC (later becomes vendor) who shows multiple options, tells you to pick one
- Starter rules TBD: maybe sigil + 1-2 orbs, or mask + sigil + 1-2 orbs, etc.
**UI-During-Combat Critique:**
- Claude pushed back: full-screen + no pause means opening UI = fighting blind, so it's more "choose between fight and reconfig" than "tactical mid-combat reconfiguration"
- Offered middle option: full-screen UI but combat continues (hear hits, see damage numbers over UI)
- User confirmed no pause is intentional — forces experimentation in hideout
**Map System Final Decision:**
- Claude proposed rich lore-tied systems (null residue as currency, Regions model, connectivity via residue flavor)
- User simplified: PoE but simpler for now, maps drop maps, random world map placement, hover for info, defer crafting/connectivity decisions
**Design Documents Written:**
- `design.md` at Orblike root (not in reference/ per user request)
- Concept, lore (Psyonian state), core loop, hideout (NPCs: Gear Officer, Teleporter Engineer), equipment slots (Mask/Sigil/Robe/Rings/Orbs), map system, unified UI pattern, run/death rules, leagues as map mechanics, design principles, open questions
- User linter-edited concept from "bullet-hell" to "action RPG"
- `plan.md` at Orblike root with 10 phases:
- Phase 0: Setup (done)
- Phase 1: Hideout shell (player walks around, camera, ESC stub)
- Phase 2: UI primitives (text, buttons, full-screen pattern, real options menu)
- Phase 3: NPC + interaction system
- Phase 4: Inventory data model + UI (widest blast radius — affects everything downstream)
- Phase 5: Starter gear selection (first real NPC UI)
- Phase 6: Orb composition UI (character screen)
- Phase 7: Map UI + run lifecycle skeleton with stub arena (full meta-loop playable without real combat)
- Phase 8: Arena port from v1 (the big one — 23 abilities, map gen, damage, enemies)
- Phase 9: First league + league API validation
- Phase 10+: Content, balance, retire v1
- Architectural decisions flagged for later: Phase 4 item schema, Phase 6 orb composition schema, Phase 8 save system, Phase 9 league API
**Final State:**
- Orblike/ root runs v2 starter (dark screen)
- Orblike/v1/ runs full v1 game for comparison
- Design locked in `design.md` and `plan.md`
- Next session will start Phase 1 implementation
Does the summary look good? Once you approve I'll run the conversion scripts and do the commits.
Yes.
Bash (python E:/a327ex/Anchor2/scripts/find-recent-session.py --limit 5)
Exit code 1
2026-04-16T23:33:00.541Z 0c0fd859-028d-4b66-a198-a56c73d38aee [claude] <-- MOST RECENT
C:\Users\adn\.claude\projects\E--a327ex\0c0fd859-028d-4b66-a198-a56c73d38aee.jsonl
2026-04-16T22:29:22.826Z 3ec7c86a-d4e5-4f02-8a7e-6deb6961ef74 [claude]
C:\Users\adn\.claude\projects\E--a327ex\3ec7c86a-d4e5-4f02-8a7e-6deb6961ef74.jsonl
2026-04-16T16:58:29.669Z 66b86a17-0cbc-40a6-ac8c-f99d738e8c01 [claude]
C:\Users\adn\.claude\projects\E--a327ex\66b86a17-0cbc-40a6-ac8c-f99d738e8c01.jsonl
2026-04-16T10:34:18.953Z e1200e83-2231-48a2-8c36-cc8cf60f7356 [claude]
Traceback (most recent call last):
File "E:\a327ex\Anchor2\scripts\find-recent-session.py", line 165, in <module>
File "E:\a327ex\Anchor2\scripts\find-recent-session.py", line 160, in main
File "C:\Users\adn\AppData\Local\Programs\Python\Python314\Lib\encodings\cp1252.py", line 19, in encode
UnicodeEncodeError: 'charmap' codec can't encode characters in position 19-28: character maps to <undefined>