Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8e7170f
Extract terminal UI into shared docs/js/ui factories
cursoragent Jul 25, 2026
6b2ebb4
Mount shared terminal screens inside the RPG
cursoragent Jul 25, 2026
15b9cb1
Add START menu, collections, and real venue gating to the RPG
cursoragent Jul 25, 2026
0b467ba
Add line-of-sight challengers, story items, and node-level quest gating
cursoragent Jul 25, 2026
9c4f4f1
Give every quest a giver, a gated follow-up, and a cosmetic reward
cursoragent Jul 25, 2026
548cd7c
Move world authoring into JSON: 28 maps, MapLoader, per-map spawns
cursoragent Jul 25, 2026
fc963d7
Fill out dialogue and easter eggs for the 20 new rooms
cursoragent Jul 25, 2026
755fc38
Drive the overworld from world-cycle: phases, daily charges, eviction
cursoragent Jul 25, 2026
5c7cde5
Bump saves to v8 with RPG collection buckets and CLI passthrough
cursoragent Jul 25, 2026
7687d84
Add an end-to-end RPG journey to the smoke test and wire up CI
cursoragent Jul 25, 2026
622d5b8
Tag every room with its wing and show it on the placard
cursoragent Jul 25, 2026
72e839a
Rewrite the GDD and refresh the player, architecture, and README docs
cursoragent Jul 25, 2026
e2726ea
Add a data-driven pixel RPG page to the Quarto site
cursoragent Jul 25, 2026
82311fe
Refresh expired Posit tokens and stop the publisher duplicating content
cursoragent Jul 25, 2026
b71db65
Merge origin/main: host craps and lottery instead of duplicating them
cursoragent Jul 25, 2026
b437e9e
Refresh the wiki NPC roster for the 59-NPC world
cursoragent Jul 25, 2026
9a8dcd5
Give craps, the lottery, and prediction markets doors in the overworld
cursoragent Jul 25, 2026
956c520
Fail the checks when a routable encounter has no way in
cursoragent Jul 25, 2026
57cbd94
Point the docs at the shared renderers and the two new NPCs
cursoragent Jul 25, 2026
cfc2fe5
Stop freezing the RPG page so it publishes the current world
cursoragent Jul 25, 2026
0490e6d
Merge origin/main: DS-scale art, character creator, and touch controls
cursoragent Jul 25, 2026
b757929
Document the character creator, touch controls, and floor wayfinding
cursoragent Jul 25, 2026
ad0dac6
Merge origin/main: hotel front desk fix and manual gh-pages publish
cursoragent Jul 25, 2026
3234628
Check the hosted front desk hands a checked-in guest their room
cursoragent Jul 25, 2026
239b29f
Merge remote-tracking branch 'origin/main' into cursor/pixel-rpg-tota…
cursoragent Jul 25, 2026
37d03ab
Say when the room key activates now that either path grants it
cursoragent Jul 25, 2026
59e625d
Merge remote-tracking branch 'origin/main' into cursor/pixel-rpg-tota…
cursoragent Jul 25, 2026
69e3529
Merge origin/main into cursor/pixel-rpg-total-expansion-30a1
github-actions[bot] Jul 25, 2026
584746f
Stop the overworld crashing before it draws a single guest
cursoragent Jul 25, 2026
af2572e
Redraw the last flat tiles and check every drawer headlessly
cursoragent Jul 25, 2026
eb3e22b
Give the whole screen to the game instead of a letterboxed square
cursoragent Jul 25, 2026
cca4212
Route taps around the furniture instead of into it
cursoragent Jul 25, 2026
32c29ce
Put a thumb pad and tap-anywhere dialogue on phones
cursoragent Jul 25, 2026
3b59ced
Merge remote branch state
cursoragent Jul 25, 2026
5767fbb
Say tap instead of Enter on touch and size menu rows for thumbs
cursoragent Jul 25, 2026
e4c0f8f
Redraw characters as authored pixel grids and scuff the big floors
cursoragent Jul 25, 2026
08e7515
Keep the rewards phone under the pixel panels and document the art pi…
cursoragent Jul 25, 2026
79e6d7a
Document the phone controls in the wiki and Quarto pages
cursoragent Jul 25, 2026
6832009
Merge origin/main into pixel-rpg expansion; keep grid sprites and pho…
cursoragent Jul 26, 2026
bc38949
Merge main into PR 89: synthesize room access, boot fixes, and card s…
cursoragent Jul 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ Walk the resort in a **Pokémon-style pixel overworld** built with Phaser 3:
- **One clock, one wallet** — daily resort charges, rotating reservation requirements, and eviction all reach the overworld
- **Legible floors** — gold walkways connect every door, dark trim separates each zone, and floating signs name the room you're standing in
- **Your guest, your sprite** — a character creator for archetype, skin tone, hair, and outfit, reopenable as the Trainer Card's wardrobe
- **Plays on a phone** — tap a tile to walk, tap dialogue to advance
- **Arcade polish** — DS-scale procedural textures, Web Audio BGM/SFX, cabinet bezel, room placards, Konami code
- **Plays on a phone** — full-bleed handheld framing, a thumb pad, tap a tile to walk, tap anywhere to advance dialogue
- **Hand-authored sprites** — characters are pixel grids with proper faces and a three-frame stride, drawn at 2× on a 16-pixel art grid that lights from the top left
- **Arcade polish** — DS-scale textures, Web Audio BGM/SFX, cabinet bezel, room placards, Konami code
- **Unified saves** — position, quests, flags, and chips ride the same slot as the terminal and the CLI

See [`docs/rpg/GDD.md`](docs/rpg/GDD.md).
Expand Down
14 changes: 7 additions & 7 deletions docs/js/dealers.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const DEALER_ROSTER = [
id: "steve_harvey",
name: "Steve Harvey",
games: ["roulette", "horse_racing", "craps"],
sprite: "dealer_steve",
sprite: "npc_gold",
tagline: "Survey says… bet responsibly!",
quips: {
greeting: [
Expand Down Expand Up @@ -82,7 +82,7 @@ export const DEALER_ROSTER = [
id: "meryl_screech",
name: "Dealer Meryl Screech",
games: ["blackjack"],
sprite: "dealer_meryl",
sprite: "npc_green",
tagline: "And the Oscar for Most Aggressive Double goes to… you.",
quips: {
greeting: [
Expand Down Expand Up @@ -116,7 +116,7 @@ export const DEALER_ROSTER = [
id: "judi_bench",
name: "Croupier Judi Bench",
games: ["holdem"],
sprite: "dealer_judi",
sprite: "npc_teal",
tagline: "Bond. James Bond. Blinds.",
quips: {
greeting: [
Expand Down Expand Up @@ -150,7 +150,7 @@ export const DEALER_ROSTER = [
id: "jennifer_lawless",
name: "Jennifer Lawless",
games: ["blackjack", "holdem"],
sprite: "dealer_jennifer",
sprite: "npc_pink",
tagline: "I tripped on the felt once. The shoe didn't even notice.",
quips: {
greeting: [
Expand Down Expand Up @@ -184,7 +184,7 @@ export const DEALER_ROSTER = [
id: "sofia_volume",
name: "Sofia Volume",
games: ["roulette"],
sprite: "dealer_sofia",
sprite: "npc_red",
tagline: "Dale, amigo — the wheel is feeling generous tonight!",
quips: {
greeting: [
Expand Down Expand Up @@ -218,7 +218,7 @@ export const DEALER_ROSTER = [
id: "octavia_spectacular",
name: "Octavia Spectacular",
games: ["holdem", "blackjack"],
sprite: "dealer_octavia",
sprite: "npc_orange",
tagline: "Honey, the house always wins — but you look good losing.",
quips: {
greeting: [
Expand Down Expand Up @@ -252,7 +252,7 @@ export const DEALER_ROSTER = [
id: "nicole_widechart",
name: "Nicole Widechart",
games: ["blackjack", "roulette"],
sprite: "dealer_nicole",
sprite: "npc_silver",
tagline: "The chips are whispering. I'm listening.",
quips: {
greeting: [
Expand Down
7 changes: 7 additions & 0 deletions docs/player-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,13 @@ terminal's own screens rather than reimplementing them.
| T | Trainer Card and wardrobe |
| P | MGM Rewards phone |

On a phone the game fills the screen and a thumb pad appears in the bottom
corners: a d-pad on the left, **B** to run, **A** to talk, and **☰** for the
START menu. You can ignore the pad entirely and just tap — tap a tile to walk
there, tap somebody to walk over and talk to them, and tap anywhere on the
screen to advance dialogue. The pad gets out of the way whenever a conversation
or a panel is on screen.

Gold walkways are the resort's wayfinding: follow them and they connect the
entrance, the pits, the aisles, and every door. Dark trim marks where one floor
ends and the next begins, and floating signs name the zone you're standing in.
Expand Down
32 changes: 31 additions & 1 deletion docs/rpg/GDD.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ flowchart TB
| Tile vocabulary | `js/systems/MapTiles.js` |
| JSON → tile layers | `js/systems/MapLoader.js` |
| Map/NPC/door accessors + procedural fallback | `js/systems/MapData.js` |
| Vendored tiles + procedural FX | `js/systems/EnvironmentTextures.js`, `js/systems/TextureFactory.js` |
| Procedural sprites and tiles (16px art grids) | `js/systems/TextureFactory.js` |
| Vendored tiles + procedural FX | `js/systems/EnvironmentTextures.js` |
| Character / staff sprite sheets | `js/systems/CharacterSprites.js` |
| Dialogue graph | `js/systems/DialogueManager.js` |
| Encounter routing | `js/systems/EncounterBridge.js`, `js/systems/HostedEncounters.js` |
Expand All @@ -78,6 +79,35 @@ flowchart TB
| Save bridge | `js/systems/SaveAdapter.js`, `docs/js/core.js` |
| Procedural audio | `js/systems/AudioManager.js` |

### Art

Everything is drawn in code against a 16-pixel grid and blitted at 2×, so a tile
is 32 screen pixels and one art pixel is always two. `TILE_SIZE` and `ART_UNIT`
in `MapTiles.js` are the only two numbers that decide this.

Tiles are procedural: a base fill, a light edge on the top and left, a dark edge
on the bottom and right, then whatever pattern the surface needs. Every surface
lights from the top left; breaking that is what makes a set of tiles look like
it came from different games. Wide floors — lobby, carpet, felt, road, sand —
also register three scuffed variants, and `groundTileKey()` spreads them across
the map so a ballroom does not read as wallpaper. `EnvironmentTextures.js` can
also map vendored PNGs under `assets/tiles/` onto the same `TILE` enum without
changing map JSON.

Characters are **authored pixel grids**, not stacked rectangles: `CHAR_BODY` and
`CHAR_LEGS` in `TextureFactory.js` are arrays of 16-character strings, one
character per pixel, resolved through a palette legend (`O` outline, `H` hair,
`S` skin, `B` outfit, `N` denim, and so on). The upper body and the legs are
separate blocks, which is what lets a three-frame walk cycle swap only the legs
and drop the torso a pixel. Right-facing is the left grid mirrored.
`CharacterSprites.js` loads optional staff/dealer sheets from
`assets/characters/` for named NPCs.

To change how a character looks, edit the strings. `scripts/smoke-test-rpg.mjs`
asserts every row is exactly 16 characters and every character is in the legend,
then renders all twelve frames of every wardrobe combination headlessly, because
a bad palette lookup at boot takes the whole overworld down.

---

## 3. The world
Expand Down
178 changes: 149 additions & 29 deletions docs/rpg/css/rpg.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,10 @@ body {
#game-shell.arcade-cabinet {
border: 14px solid #1a1520;
box-shadow:
inset 0 0 0 3px #4a3a58,
inset 0 0 0 7px #2a2038,
inset 0 0 0 10px #0a0812,
0 0 0 2px #e8c54733,
0 0 0 7px #2a2438,
0 28px 72px rgba(0, 0, 0, 0.8);
inset 0 0 0 4px #3a3048,
inset 0 0 0 8px #0a0812,
0 0 0 6px #2a2438,
0 24px 64px rgba(0, 0, 0, 0.75);
}

#game-shell.arcade-cabinet::before {
Expand All @@ -69,43 +67,27 @@ body {
z-index: 30;
font-size: 7px;
color: var(--gold);
letter-spacing: 0.14em;
pointer-events: none;
opacity: 0.92;
text-shadow: 0 0 8px rgba(232, 197, 71, 0.35);
}

#game-shell.arcade-cabinet::after {
content: "";
position: absolute;
inset: 14px;
z-index: 25;
letter-spacing: 0.12em;
pointer-events: none;
border-radius: 2px;
box-shadow:
inset 0 0 48px rgba(0, 0, 0, 0.45),
inset 0 0 120px rgba(8, 4, 16, 0.35);
background:
radial-gradient(ellipse at 50% 40%, transparent 55%, rgba(0, 0, 0, 0.28) 100%);
opacity: 0.85;
}

#game-shell.konami-mode {
filter: contrast(1.15) saturate(0.65) hue-rotate(-15deg);
}

#phaser-root {
position: relative;
z-index: 1;
position: absolute;
inset: 0;
}

#phaser-root canvas {
display: block;
width: 100%;
height: 100%;
image-rendering: pixelated;
image-rendering: crisp-edges;
touch-action: none;
box-shadow:
0 0 0 3px #3a3048,
0 0 0 5px #0a0812,
0 16px 56px rgba(0, 0, 0, 0.7);
}

#hud {
Expand Down Expand Up @@ -1165,6 +1147,144 @@ body {
}
}

/* ─── Thumb controls ───────────────────────────────────────────────────────
Only mounted on touch devices. Sits under the panels (z-index 20) and hides
itself whenever one of them takes the screen. */

.touch-pad {
position: absolute;
inset: auto 0 0 0;
z-index: 14;
display: flex;
align-items: flex-end;
justify-content: space-between;
padding: 0 max(14px, env(safe-area-inset-left))
max(18px, env(safe-area-inset-bottom)) max(14px, env(safe-area-inset-right));
pointer-events: none;
opacity: 0.9;
transition: opacity 120ms ease-out;
}

.touch-pad--hidden {
opacity: 0;
pointer-events: none;
visibility: hidden;
}

.touch-pad__dpad {
position: relative;
display: grid;
grid-template-columns: repeat(3, 46px);
grid-template-rows: repeat(3, 46px);
gap: 2px;
}

.touch-pad__actions {
display: grid;
grid-template-columns: repeat(2, auto);
align-items: end;
justify-items: center;
gap: 12px;
}

.touch-btn {
pointer-events: auto;
touch-action: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-pixel);
font-size: 12px;
color: var(--text);
background: rgba(20, 16, 31, 0.82);
border: 3px solid var(--border);
border-radius: 8px;
box-shadow: 0 3px 0 rgba(0, 0, 0, 0.55);
}

.touch-btn.is-pressed {
background: rgba(232, 197, 71, 0.28);
border-color: var(--gold);
transform: translateY(2px);
box-shadow: none;
}

.touch-btn--up { grid-area: 1 / 2; }
.touch-btn--left { grid-area: 2 / 1; }
.touch-btn--right { grid-area: 2 / 3; }
.touch-btn--down { grid-area: 3 / 2; }

.touch-btn--talk,
.touch-btn--run {
border-radius: 50%;
font-size: 14px;
}

.touch-btn--talk {
grid-area: 2 / 2;
width: 74px;
height: 74px;
color: #0a0812;
background: rgba(232, 197, 71, 0.88);
border-color: #c4a030;
}

.touch-btn--run {
grid-area: 2 / 1;
width: 56px;
height: 56px;
color: var(--cyan);
border-color: #2a6a70;
}

.touch-btn--menu {
grid-area: 1 / 1;
width: 46px;
height: 46px;
font-size: 15px;
}

/* Menu rows are keyboard-sized by default; a thumb needs a bigger target. */
@media (pointer: coarse) {
.menu-panel-list button,
.terminal-host-mount .menu-btn {
min-height: 44px;
align-items: center;
}
}

/* With a pad on screen, the bottom-right corner belongs to the A button: move
the hint and the rewards phone out from under the player's thumb. */
body.has-touch-pad .touch-hint {
top: max(62px, calc(env(safe-area-inset-top) + 62px));
bottom: auto;
}

body.has-touch-pad .rewards-phone-root {
top: max(62px, calc(env(safe-area-inset-top) + 62px));
right: max(10px, env(safe-area-inset-right));
bottom: auto;
}

/* The flip phone floats over everything in the terminal build. In here it has
to sit under the pixel panels, or its badge covers the menu's chip total. */
body .rewards-phone-root {
z-index: 18;
}

/* Landscape phones have no room for a tall stack. */
@media (orientation: landscape) and (max-height: 480px) {
.touch-pad__dpad {
grid-template-columns: repeat(3, 38px);
grid-template-rows: repeat(3, 38px);
}
.touch-btn--talk { width: 60px; height: 60px; }
.touch-btn--run { width: 46px; height: 46px; }
.touch-btn--menu { width: 42px; height: 42px; }
}

/* ─── Hosted terminal screens ──────────────────────────────────────────────
Shared docs/js/ui renderers mounted inside an encounter panel. The terminal
stylesheet supplies layout; these rules re-skin it for the pixel cabinet. */
Expand Down
7 changes: 4 additions & 3 deletions docs/rpg/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&display=swap" rel="stylesheet">
<!-- Terminal styles come first so the RPG's pixel skin overrides them. -->
<link rel="stylesheet" href="../css/casino.css?v=card-sprites-1">
<link rel="stylesheet" href="css/rpg.css?v=card-sprites-1">
<link rel="stylesheet" href="../css/casino.css?v=pr89-main-1">
<link rel="stylesheet" href="css/rpg.css?v=pr89-main-1">
<link rel="stylesheet" href="../css/intoxication-effects.css">
<link rel="stylesheet" href="../css/rewards-phone.css">
<script type="importmap">
Expand All @@ -30,6 +30,7 @@
<div id="hud" aria-live="polite"></div>
<div id="map-banner" hidden aria-live="polite"></div>
<div id="touch-hint" class="touch-hint" aria-hidden="true">Tap to move · Tap to talk</div>
<div id="touch-pad" class="touch-pad" hidden></div>
<div id="rewards-phone" aria-live="polite"></div>
<div id="trainer-card" hidden aria-live="polite"></div>
<div id="dialogue-overlay" hidden aria-live="polite"></div>
Expand All @@ -48,7 +49,7 @@ <h1 class="title-intro-logo">The Mandalay Bay</h1>
</div>
</div>
</div>
<script type="module" src="js/main.js?v=card-sprites-1"></script>
<script type="module" src="js/main.js?v=pr89-main-1"></script>
<script>
// Surface module-load failures that would otherwise leave a blank title.
window.addEventListener("error", (event) => {
Expand Down
Loading