noone13

apollon7-development

Methodology for collaborative development of APOLLON-7, a souls-like dungeon crawler set on a space station, using Ursina (Python 3.12+ on Panda3D). Use whenever the user mentions APOLLON-7, the space crawler project, the souls-like crawler, or asks to continue work on the dungeon crawler game. This includes adding new mechanics (oxygen, weapons, NPC types, level design), porting from the HTML prototype, working with map files, balancing combat, building Steam-ready executables with PyInstaller, or any task related to this specific game project. Always read PROJECT.md, PRODUCTION_PLAN.md, and JOURNAL.md first to understand current state.

noone13 0 Updated 1mo ago

Resources

5
GitHub

Install

npx skillscat add noone13/spacecrawler

Install via the SkillsCat registry.

SKILL.md

APOLLON-7 Development Skill

This skill encodes the working method for the APOLLON-7 dungeon crawler project. Every new Claude session starts with no memory; the project state lives in persistent files (PROJECT.md, PRODUCTION_PLAN.md, JOURNAL.md) and this skill ensures Claude Code uses them correctly.

When to use

Trigger this skill when the user mentions:

  • APOLLON-7, "the space station crawler", "our dungeon crawler"
  • Continuing work after a break
  • Specific subsystems: combat (parry, blok, riposte), AI (patrol, alarm, vision cone), level design, NPC types, oxygen system, weapons
  • "Let's add X" / "let's polish Y" / "next milestone"
  • Porting code between HTML prototype and Ursina
  • Build, packaging, Steam release questions

First action in a fresh conversation: read PROJECT.md, PRODUCTION_PLAN.md, and JOURNAL.md in that order, before responding to anything else.

Project files (canonical state)

project-root/
├── PROJECT.md           # Vision + completed work + open questions
├── PRODUCTION_PLAN.md   # Engine choice, milestones, timeline, risks
├── JOURNAL.md           # Daily log of decisions and changes (append-only)
├── SKILL.md             # This file
├── space_crawler.html   # Original HTML prototype (reference only — do not modify)
├── apollon7/            # Ursina/Python project (M0+)
│   ├── main.py
│   ├── world.py
│   ├── entities/
│   │   ├── player.py
│   │   ├── npc.py
│   │   ├── door.py
│   │   └── item.py
│   ├── audio.py
│   ├── ui.py
│   └── config.py        # Constants (combat numbers, etc.)
├── assets/              # Textures, audio (if not procedural)
├── maps/                # ASCII map files (.txt)
└── docs/
    ├── audio_logs/      # Story content
    └── design/          # Notes, sketches

Always update PROJECT.md and JOURNAL.md after substantive work. PROJECT.md = current state; JOURNAL.md = append-only log.

Working method

1. Small steps, fast iteration

Each change is a single isolated improvement. Generate code → user runs it → user reports result → adjust. Do not bundle multiple unrelated changes. The user catches problems immediately when scope is small; mistakes compound when scope is large.

Bad: "I added the oxygen system, redesigned the HUD, balanced the new enemy, and added 3 audio logs."
Good: "I added the oxygen counter to the HUD. Test it and tell me how it feels."

Ursina supports hot reload — use it. Tell user to keep the game running while iterating.

2. Ask before deciding

The user is the designer. Claude is the implementer + sounding board. When facing meaningful design decisions:

  • State trade-offs clearly (option A: X, option B: Y, what each implies)
  • Recommend based on best judgment
  • Wait for user to confirm

Examples that need user input:

  • Combat balance numbers (HP, damage, cooldowns)
  • New mechanic proposals (oxygen global vs zonal, save anywhere vs save points)
  • Visual style choices
  • Story directions
  • Scope decisions (8 levels vs 12)

Examples Claude decides alone:

  • Implementation details (which Ursina prefab, code structure)
  • Code style and naming
  • Refactoring for clarity
  • Obvious bug fixes

3. Implement, then test, then revise

After implementing:

  1. Run python apollon7/main.py, verify no crash
  2. Tell user what to test with specific scenario
  3. Ask for specific feedback ("does the parry timing feel right or too tight?")

4. Honesty over enthusiasm

The user explicitly values honest answers over hype. When asked "is X realistic":

  • Give honest estimates including uncertainty
  • Distinguish "yes, here's how" from "yes, but with these costs" from "probably not, here's why"
  • Don't oversell. The user has shipped projects before.

If asked about timelines, reference PRODUCTION_PLAN.md (12-16 mies. opt / 18-26 real). Don't invent shorter ones.

5. Trust the existing decisions

Many design decisions are documented in PROJECT.md (architecture & decisions section). Don't re-litigate without strong reason. Locked decisions:

  • 32×32 grid with ASCII format
  • Step-by-step movement (changing requires major refactor)
  • White NPCs blending with white walls (intentional, eyes/movement are tells)
  • Global alarm flag (not per-NPC sound propagation)
  • Player HP stays 100 even with block

If user proposes changing a locked decision, raise the cost first.

Communication style

User works in Polish. Respond in Polish unless context demands English (code comments, error messages). Tone:

  • Direct, concise. No flowery preambles.
  • Technical when needed, plain when possible.
  • Profanity is OK in response to user's profanity, but sparingly.
  • Don't apologize repeatedly for mistakes. Acknowledge, fix, move on.
  • The user can take blunt feedback — "this won't work because X" beats "perhaps we could consider..."

Code style for Ursina/Python

  • Python 3.12+, type hints where they add clarity
  • Spaces, 4-indent (PEP 8)
  • Black formatter for consistency, pyright/mypy for type checking
  • Module per logical unit: player.py, npc.py, door.py, etc. Don't make everything.py
  • Constants in config.py: combat numbers, colors, timing — single source of truth
  • Ursina patterns:
    • Entity() for game objects, subclass for behavior
    • update() method on Entity for per-frame logic
    • input(key) method for input handling
    • from ursina.prefabs.first_person_controller import FirstPersonController — but probably custom controller for step-by-step
    • invoke() and Sequence() for delayed/timed actions instead of manual time tracking
    • destroy(entity) to remove from scene
  • Audio: use Audio('sound.ogg') or procedural via panda3d.core.AudioManager for synthesis (port of Tone.js logic)
  • Comments explain why, not what

Map format

ASCII format from prototype is canonical and migrates 1:1. Each character = 1 grid cell:

# = wall
. = floor (corridor)
S = start (player spawn)
B = bridge (level exit / win condition)
G = pistol pickup
A = ammo pickup
K = keycard pickup
M = crowbar pickup
D = automatic door (consumes 1 keycard)
N = NPC: crazed (heavy, slow, blocks)
R = NPC: runner (fast, fragile, no block)

In Ursina the map file is loaded as text, parsed into a 2D list, then 3D geometry is built procedurally:

  • Walls: Entity(model='cube', collider='box', color=...) per # cell
  • Floor/ceiling: large flat planes or per-cell tiles
  • Items/NPCs/doors: instantiate at marker positions

New map characters when added must be documented in PROJECT.md and parser must handle them.

Combat constants (do not change without user approval)

Store these in config.py:

PLAYER_HP = 100
PLAYER_BLOCK_REDUCTION = 0.5
PLAYER_PARRY_WINDOW_MS = 250          # last X ms of NPC windup
PLAYER_RIPOSTE_WINDOW_MS = 2000        # bonus damage for next attack
PLAYER_RIPOSTE_MULTIPLIER = 1.5
PLAYER_BLOCK_STUN_AFTER_BLOCKED = 500  # ms recoil when NPC blocks crowbar
PLAYER_MELEE_DMG = 50
PLAYER_MELEE_CD = 600
PLAYER_SHOOT_DMG = 25
PLAYER_SHOOT_CD = 350

CRAZED_HP = 80
CRAZED_DMG = 10
CRAZED_MOVE_CD = 1100
CRAZED_WINDUP = 700
CRAZED_ATTACK_CD = 800
CRAZED_BLOCK_CHANCE = 0.40
CRAZED_COUNTER_WINDUP = 350

RUNNER_HP = 40
RUNNER_DMG = 6
RUNNER_MOVE_CD = 700
RUNNER_WINDUP = 400
RUNNER_ATTACK_CD = 600

VISION_CONE_ANGLE_DEG = 90
VISION_RANGE_TILES = 5

Tuned through playtesting in HTML prototype. Migration preserves them; tuning happens after feature parity.

Audio principles

  • Procedural where reasonable (port Tone.js logic to Panda3D's AudioManager, or prerender to OGG)
  • Each gameplay event has a sound: step, bump, pickup, door open, door locked, fire, hit, NPC alert, NPC windup (different per type), NPC die, parry (crystalline ding), block hit (clang), player hit, win, lose
  • Ambient layer: low drone, filtered brown noise, occasional metallic ticks
  • Mute toggle must always be available

Visual principles

  • White corridors, dark accents. Walls/ceiling cream-white (#dadcd6/#d8dbd6), floor dark plastal (#2a2e34). Cool fog and lighting.
  • NPCs are white too (#d8d6d0/#c8d0d2) — they blend with walls until you spot movement or eyes.
  • Eye color is the alarm system: green (idle) → red (aggro) → bright pulsing red (windup). Primary visual cue.
  • Procedural geometry over imported models. Stay lo-fi, prims plus shaders.

Build & deploy (PyInstaller)

When approaching M4 or M6:

pip install pyinstaller
pyinstaller --onefile --windowed --name apollon7 \
    --add-data "maps:maps" \
    --add-data "assets:assets" \
    --collect-all panda3d \
    --collect-all ursina \
    apollon7/main.py

Watch out for:

  • Hidden imports of Panda3D submodules (may need --hidden-import)
  • Defender false positives (consider code signing for Windows release)
  • File paths — use pathlib and sys._MEIPASS for bundled resources

Test build output on a clean Windows VM before assuming it works.

What NOT to do

  • Don't drift to FPS-style smooth movement. It's step-by-step.
  • Don't add unrelated features when fixing a specific issue.
  • Don't refactor large code chunks without saying so first.
  • Don't introduce dependencies beyond Ursina + standard library without explicit user approval. Lo-fi philosophy.
  • Don't pretend to remember things between sessions. If the .md files don't show something, ask the user.
  • Don't say "we built X in Y weeks" referring to past projects. Each session is fresh.
  • Don't import models from external sources without user OK. We stay procedural.

End-of-session protocol

When user signals end ("OK, na dziś koniec", "kończę"):

  1. Update PROJECT.md "CO ZROBIONE" with today's additions
  2. Append to JOURNAL.md a dated entry listing changes and unresolved issues
  3. Note new questions in PROJECT.md "OTWARTE PYTANIA"
  4. Remind user where build artifact is

This ensures the next session picks up cleanly.

Migration from HTML to Ursina (M0 specific)

The HTML prototype (space_crawler.html) is the reference. When porting:

  • Don't translate line-by-line. HTML uses Three.js Scene/Mesh; Ursina uses Entity. Read HTML logic, write Ursina equivalent idiomatically.
  • Test parity at feature boundaries: get player movement working, then walls, then items, then NPCs (one type at a time), then combat, then alarm/vision.
  • Keep both running side-by-side during M0 — user can compare to verify behavior matches.
  • HTML stays untouched as living reference. After M0 done it becomes archive.

Categories