{"id":50877177,"url":"https://github.com/griffio/krogue-compose","last_synced_at":"2026-06-15T11:01:30.599Z","repository":{"id":363559850,"uuid":"1263686070","full_name":"griffio/krogue-compose","owner":"griffio","description":"rogue like using Kotlin KMP compose","archived":false,"fork":false,"pushed_at":"2026-06-09T11:03:11.000Z","size":289,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-09T13:06:30.068Z","etag":null,"topics":["claude-code","compose-multiplatform","kotlin","roguelike","roguelike-game","slop","vibe-coded"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/griffio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-09T07:13:49.000Z","updated_at":"2026-06-09T11:03:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/griffio/krogue-compose","commit_stats":null,"previous_names":["griffio/krogue-compose"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/griffio/krogue-compose","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/griffio%2Fkrogue-compose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/griffio%2Fkrogue-compose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/griffio%2Fkrogue-compose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/griffio%2Fkrogue-compose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/griffio","download_url":"https://codeload.github.com/griffio/krogue-compose/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/griffio%2Fkrogue-compose/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34357285,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude-code","compose-multiplatform","kotlin","roguelike","roguelike-game","slop","vibe-coded"],"created_at":"2026-06-15T11:01:29.663Z","updated_at":"2026-06-15T11:01:30.594Z","avatar_url":"https://github.com/griffio.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# krogue-compose\n\nA terminal-style roguelike rendered with **Compose Multiplatform**, instead of a\nreal terminal library. The point of using Compose is laying\nout several text \"panels\" side-by-side and stacked, while keeping the classic\nASCII look. The field-of-view and dungeon-generation algorithms are ported from\n[`krogue-kotter`](https://github.com/griffio/krogue-kotter).\n\n![a wisp spits venom at the hero](docs/threats.png)\n\n## Milestone 5 — sound: epic themes \u0026 spell SFX\n\n- **Looping music** — an epic theme plays on a loop, swapping between two tracks\n  each new game (`R`) so a restart sounds fresh.\n- **Spell sound effects** — the same semantic `GameEvent.Bolt` that spawns a\n  particle now also fires a clip: a strike for the **fire bolt** (`F`), a crackle\n  for the **energy blast** (`B`), and a dark hiss for the wisp's **venom bolt** —\n  monster shots get audio for free, since they emit the same event.\n- **World SFX** — the same event stream drives a set of short effects: a coin\n  chime on gold, a thud when a monster is struck, a grunt when the hero is hurt,\n  a splash in `~` water, a snap when a `^` trap springs, a sweep down the `\u003e`\n  stairs, and a wooden crack smashing through a `+` door. A `GameEvent.Step`\n  carries the tile's terrain to the audio layer, so new terrain SFX are one asset\n  plus one line in a map.\n- **Procedural retro SFX** — these effects are synthesised with `ffmpeg`\n  (sine blips, filtered noise, pitch sweeps) rather than recorded, fitting the\n  terminal aesthetic; sources live in `sound-effects/` and are easy to regenerate\n  or replace.\n- **One more layer, core untouched** — audio hangs off the event stream exactly\n  like the effects layer (`onEvent` fans out to both), behind an `expect`\n  `AudioEngine`. Desktop drives `javax.sound.sampled`; the web build ships a\n  silent no-op for now (browser autoplay rules + multi-megabyte WAVs are deferred).\n  `M` mutes.\n\n## Milestone 4 — greater threats: ranged casters, door traps, ambushes\n\n- **Ranged casters** — the `w` wisp doesn't close in: with line of sight and in\n  range it *winds up* (a charge pulse + a log warning) and looses a venom bolt the\n  next turn. Counterplay: break line of sight to make it whiff, or close to melee\n  to disrupt the shot. This punishes standing back and plinking with spells.\n- **Hidden door traps** — the `~` water and `^` trap terrains were wired all along\n  but never placed; M4 scatters them onto the floor around `+` doorways (and a few\n  in rooms). Traps are **hidden** — drawn as plain floor — until you step adjacent,\n  then shown as `^` so you can route around them, or risk a 1-wide corridor.\n- **Sleeping monsters \u0026 ambush** — monsters start dormant and only wake when they\n  see you within range (or take a hit), so a room can erupt at once instead of\n  trickling out. Getting greedy near a packed room is now dangerous.\n\n## Milestone 3 — ranged spells \u0026 ASCII particle effects\n\n- **Two spells, keyboard-cast** — `F` throws a single-target **fire bolt**; `B`\n  looses an **energy blast** that detonates for area damage. Both auto-target the\n  nearest visible monster; `Tab` cycles targets (the locked foe is highlighted).\n- **Mana** — an `MP` pool gates casting and regenerates slowly (one point every\n  few turns), so spells are a resource to ration rather than spam.\n- **Instant sim, animated playback** — a cast resolves immediately (line of fire\n  traced to the first wall or monster, damage applied, mana spent); the projectile\n  and explosion are pure eye-candy played over the already-settled state, so the\n  turn-based core never depends on the animation.\n- **ASCII particles** — the effects layer grew from floating numbers into a small\n  particle taxonomy: a **bolt** glyph travelling its line with a fading trail, and\n  an expanding **burst** ring at impact, both coloured by an age-keyed ramp\n  (white-hot → orange → red → smoke for fire; an icy ramp for energy). In the\n  spirit of the [Grid Sage Games particle write-up](https://www.gridsagegames.com/blog/2014/03/particle-effects/).\n\n## Milestone 2 — monsters, combat, a winnable loop, and an effects layer\n\n- **Monsters** — rats `r`, kobolds `k`, snakes `s`, and orcs `o` spawn per level,\n  scaling in count and roster with depth. A monster that can see the hero (its\n  tile is lit by the shared FOV) chases along the dominant axis; otherwise it\n  wanders. They're rendered only where you can currently see them.\n- **Melee combat** — move into a foe to attack (bump-to-attack); adjacent\n  monsters strike back on their turn. The hero's damage grows slightly with\n  depth. Hit numbers float up over the grid: red for damage taken, yellow for\n  damage dealt.\n- **A winnable loop** — descend to depth `5`, where the down-stairs are replaced\n  by the relic `*`. Reach it to win; die, and it's `R` to start over.\n- **Real-time effects layer** — a frame-driven (`withFrameNanos`) Canvas overlaid\n  on the text grid, fed by semantic game events. The floating combat numbers are\n  the first effect; the layer is the seam future particle effects plug into,\n  with the turn-based core left untouched.\n\n## Milestone 1 — movement, generated map, field of vision\n\n- **Procedural dungeon** — rooms grown and connected by one-tile corridors\n  (the pushcx/ironwood algorithm), with scattered gold (`$`) and a down-stairs\n  (`\u003e`).\n- **Recursive shadowcasting FOV** — eight-octant light casting around the hero\n  with a fog of war: currently-visible tiles are bright, previously-seen tiles\n  are dimmed from memory, unseen tiles are blank.\n- **Movement** — arrow keys, `wasd`, or vi keys (`hjkl`). Walking onto `$`\n  collects gold, `~` water and `^` traps cost HP, and `\u003e` descends to a freshly\n  generated, slightly tougher level. `R` starts a new game.\n- **Tiled terminal UI** — a map section beside stacked HERO / LEGEND / LOG /\n  CONTROLS sections, all drawn in a monospace, ANSI-flavoured palette.\n\n## Tech\n\n| | |\n|---|---|\n| Language | Kotlin 2.4.0 |\n| Toolchain | Java 21 |\n| UI | Compose Multiplatform 1.11.0 |\n| Targets | Desktop (JVM) and Web (WasmJs) |\n| Coroutines | kotlinx-coroutines 1.11.0 |\n| Build | Gradle 9.4.0 (wrapper) |\n\n## Project layout\n\n```\ncomposeApp/\n  src/commonMain/kotlin/griffio/krogue/\n    game/          # pure, multiplatform game core (no UI, no platform APIs)\n      Terrain.kt           # tile kinds + glyphs\n      DungeonGenerator.kt  # room-growing map generation\n      ShadowCast.kt        # recursive-shadowcasting FOV\n      Monster.kt           # monster kinds (incl. ranged wisp), spawning, AI state\n      Spell.kt             # spells, costs, and Bresenham line-of-fire / line-of-sight\n      Effects.kt           # game events + particle model (text/bolt/burst) + colour ramps\n      Audio.kt             # audio layer fed by the same events; expect AudioEngine\n      GameState.kt         # observable model: hero, monsters, combat, spells, traps, AI\n    ui/            # Compose terminal renderer\n      TerminalTheme.kt     # palette (terrain + monster colours)\n      MapPanel.kt          # camera viewport, per-row AnnotatedString grid, target marker\n      EffectsOverlay.kt    # Canvas + withFrameNanos loop drawing the particles\n      Panels.kt            # HERO / LEGEND / LOG / CONTROLS sections\n      GameScreen.kt        # layout + keyboard input (move / cast / target)\n    App.kt         # shared composable entry point\n  src/jvmMain/     # desktop entry point, headless screenshot, javax.sound AudioEngine, sounds/*.wav\n  src/wasmJsMain/  # browser entry point + index.html + silent AudioEngine\n  src/commonTest/  # generation / FOV / movement tests\n```\n\nThe `game` package is platform-agnostic and unit-tested; the `ui` package and\nthe entry points are the only Compose/platform code.\n\n## Running\n\nDesktop:\n\n```bash\n./gradlew :composeApp:run\n```\n\nWeb (WasmJs) in the browser:\n\n```bash\n./gradlew :composeApp:wasmJsBrowserDevelopmentRun\n```\n\nTests:\n\n```bash\n./gradlew :composeApp:jvmTest\n```\n\nHeadless UI render (writes a PNG without opening a window — handy for CI /\nvisual checks). Optional second arg scripts N random-walk steps first:\n\n```bash\n./gradlew renderScreenshot --args=\"/tmp/krogue.png\"\n./gradlew renderScreenshot --args=\"/tmp/krogue.png 400\"\n```\n\n## Controls\n\n| Action | Keys |\n|---|---|\n| Move | `↑ ↓ ← →` · `w a s d` · `h j k l` |\n| Attack | move into a monster |\n| Fire bolt | `F` |\n| Energy blast | `B` |\n| Cycle target | `Tab` |\n| Descend | walk onto `\u003e` |\n| Win | reach the relic `*` on depth 5 |\n| Mute | `M` (sound is desktop-only) |\n| New map | `R` |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgriffio%2Fkrogue-compose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgriffio%2Fkrogue-compose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgriffio%2Fkrogue-compose/lists"}