Skip to content

feat(heroe): stickman 3D con el equipo de la partida guardada - #56

Open
leocagli wants to merge 19 commits into
mainfrom
heroe-3d
Open

leocagli wants to merge 19 commits into
mainfrom
heroe-3d

Conversation

@leocagli

@leocagli leocagli commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Qué hace

Una página nueva del visor, http://127.0.0.1:4173/heroe (también /mundo), con el stickman de RUNA en 3D vestido con el equipo de la última partida guardada, caminando por el mundo del juego.

El héroe

  • Equipás algo en el inventario de la terminal y aparece en el héroe 3D, con un aviso ("equipaste capucha de cuero") y una animación.
  • Armas en la mano izquierda (daga, espada, lanza, martillo, arco largo, ballesta), escudo en la derecha, pecho (cuero, cota de malla, placas con hombreras), capucha o yelmo, y botas.
  • El yelmo de hierro usa el modelo real de Tripo (yelmo.glb). Cualquier GLB llamado como el id del ítem en assets/tripo/ (por ejemplo spear.glb) reemplaza a la forma simple sin tocar código.

El mundo

  • En la ciudad, NOX, el castillo, el Coliseo y la arena del Coloso, la página arma el mapa en 3D a partir del mismo arte ASCII y la tabla TILES del juego: paredes, tejados y ventanas suben, las letras de los carteles quedan como bloques con su letra ("mercado del alba" se lee en 3D), y hay árboles, agua, lava, puertas con su nombre y NPC con el color de la terminal.
  • La estatua de los héroes de la plaza es el héroe de Tripo y el Coloso de Tripo espera en su arena.
  • El héroe camina hacia la celda guardada y la cámara lo sigue. La pradera, los campamentos y la cripta muestran la vitrina con el fondo de Tripo de esa zona.

El stickman y el mundo están hechos con formas simples: no gastan créditos de Tripo. El juego no cambia: no toca lib/ ni bin.mjs.

Cómo funciona

  • El juego ya reescribe la ranura en cada tecla. El visor lee la más reciente de la carpeta de partidas (%APPDATA%\runa\saves en Windows, la misma de bin.mjs) y la publica en /state.json.
  • Medido en Windows: un escritor con bare-fs (.tmp y renameSync cada 66 ms, como SaveStore) contra lecturas de Node cada 1 ms no falló ningún rename. Leer la ranura no traba el guardado del juego.
  • Los mapas salen de lib/map.js y lib/boss-zone.js por /world/<id>.json; el constructor del mundo está en scripts/tripo-mundo.js.
  • three.js 0.186.1 se baja una vez con --fetch-lib a vendor/three/, fuera de git, con sha256 por archivo. Anda sin wifi.
  • Detalle completo en docs/heroe-3d.md.

Cómo se probó

  • npm test (142/142) y npm run lint.
  • Chrome headless (SwiftShader) con la partida real y con partidas de prueba.
  • Una sola página abierta por CDP mientras cambiaba el guardado: cambio de arma, casco, pecho y escudo con su aviso; la plaza de RUNA, doce pasos al este, la arena del Coloso, el salón del trono y la vuelta a la pradera. Sin errores en la consola.

Límites

Con el guardado llegan el equipo, el mapa y la celda. Los golpes y la vida durante una pelea no: eso necesita un puente en vivo desde el juego. La pradera tampoco: su terreno sale de la semilla del día, que el guardado no trae.

🤖 Generated with Claude Code

La pagina /heroe del visor dibuja al stickman de RUNA con three.js y lo
viste con el equipo de la ultima ranura guardada: armas, escudo, pecho,
casco y botas. El yelmo usa el GLB de Tripo y cualquier GLB con el id
del item reemplaza a la forma simple. Las ruinas muestran al Coloso con
ceniza y las ciudades el paisaje del reino.

El visor lee la ranura solo cuando cambia y tras 250 ms quieta, y
publica /state.json. three.js 0.186.1 se baja con --fetch-lib, con
sha256 por archivo. El juego no cambia.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Gitar is working

Gitar

leocagli and others added 18 commits October 8, 2026 22:14
En la ciudad, NOX, el castillo, el Coliseo y la arena del Coloso, la
pagina arma el mapa en 3D con las filas del juego y su tabla TILES:
paredes, tejados y ventanas suben, las letras de los carteles quedan
como bloques con su letra, y hay arboles, agua, lava, puertas con su
nombre y NPC con el color de la terminal. La estatua de los heroes es
el heroe de Tripo y el Coloso de Tripo espera en su arena.

El heroe camina hacia la celda guardada y la camara lo sigue. El visor
sirve /world/<id>.json y /mundo.js, y lee la ranura cada vez que cambia:
medido en Windows, un escritor bare-fs contra lecturas de Node cada 1 ms
no fallo ningun rename.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Los edificios cerrados del arte pasan a ser cajas altas con techo a dos
aguas y ventanas, todas en una sola malla; lo suelto sube por tramos. La
camara mira en 2.5D, los NPC son figuras livianas y la ciudad queda en
88 llamadas de dibujo. Un mapa que se esta armando ya no se vuelve a
pedir con cada consulta: en una maquina lenta nunca terminaba.

Con npm start -- --teclas, bin.mjs lee las teclas que manda la pagina y
las mete por program.input, el mismo camino que la terminal. El visor
solo las acepta de su propia pagina (header propio y Origin).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aviza

En los mapas 3D el personaje es heroe.glb, el mismo heroe de Tripo del
menu, girado hacia +z y con un balanceo al caminar; el stickman con el
equipo cambiable queda en la vitrina. La estatua mira a la plaza y el
Coloso hacia el portal. El suelo se filtra suave, el pasto alto lleva
matas y el mapa tiene cielo de atardecer. La ciudad baja a 43 llamadas
de dibujo por cuadro.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Q quits the game on the title menu and in play, so the viewer never
forwards it and the page tells the player to quit from the terminal.
When 12 or more key files wait for more than 1.5 s, POST /key answers
409 and the page says the game is not reading keys instead of
swallowing them in silence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/demo/runa-tripo-focused-en.mp4 (78 s, 1280x720, narrated):
the four Tripo models in a turntable gallery, two passages of the
ASCII terminal demo, and real gameplay recorded from the 2.5D browser
view: the Tripo hero walking the city, the Tripo helmet equipped on
the stickman and the hero facing the Tripo Colossus among the lava.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o hero

Built for GitHub Pages, the page runs without the game: the Tripo hero
walks the game's maps in the browser with the game's collisions (TILES
on the maps; walls, lava and the Colossus body in the ruins), keys 1-6
switch between the city, NOX, the castle, the Coliseum, the Colossus
and the showcase, H toggles the Tripo helmet, and touch screens get a
pad. scripts/build-web.js builds the site from the same pieces the
viewer serves, with relative paths so it works under a subpath.

Head pieces now also go on the walking Tripo hero: the page measures
the top of its head in the mesh and SKINS.<id>.hero fits the piece
around it. The model list, the maps and the GLBs are retried, since a
fresh Chrome can fail its first request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 2.5D view now prefers heroe-anim.glb: Runa's hero remade in A-pose
with Tripo H3.1 through Higgsfield, rigged and animated by Higgsfield
(Idle and Walk). It is cloned with SkeletonUtils, cross-fades between
the clips and wears head pieces on its Head bone. Without that file the
original Tripo hero walks as before. scripts/glb-orient.js turns a
Tripo GLB to face +Z and stand on y = 0, as the auto-rig expects.

Fixes from the three verifiers: the demo blocks NPC cells and keeps
diagonals from slipping between walls; walkable ruin glyphs are drawn
flat instead of as blocks the hero walks through; the showcase backdrop
no longer duplicates; the equip toast survives the hero finishing its
load; place buttons release focus; 4xx responses are not retried and
live mode retries the model list too; the published pages start in
English with a link description and say when WebGL is missing; on
phones the HUD is compact, links and toasts stay clear of the pad, a
finger can slide between arrows, and the lens widens in portrait so
the Colossus is in view on arrival.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The viewer now sends each NPC's role, line, sprite kind and action, the
target of every door and gate, the saved bag and gold, and whether the
terminal game is listening. A game started with --teclas writes
keys/alive every second; when that heartbeat is older than 3 s the page
can play on its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 2.5D page now has one play controller for the published demo and
for the local page when the terminal game is not listening: it starts
where the save left the hero, with its gear and bag, walks with the
game's collisions, crosses gates to the matching gate of the next 3D
map, lets E talk to the nearest NPC (their own line from the game) and
I open the bag to equip items in the view. When the terminal's
heartbeat comes back, the terminal drives again. The bag items now
carry their slot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each NPC kind (priest, resident, tavern, alchemist, smith, armorer, guard,
king, villager) now has a painted sprite in the style of the Tripo hero: a
blocky toy figure with a cube head and a pixel face. The nine figures come
from one Higgsfield Nano Banana Pro character sheet (2 credits, job
b1da01fb), cut out of the white background with a border flood fill, a ring
test that keeps glossy highlights and removes real holes (staff ring, basket
handle), soft edges un-mixed from white, alpha bleeding for clean mipmaps
and one common scale so the median figure is 256 px tall. They live in
assets/npcs/<kind>.png (590 KB in total).

scripts/tripo-mundo.js draws each NPC as a camera-facing THREE.Sprite that
stands on its cell, with one shared texture and material per kind (marked
shared so dispose() keeps them), a soft ground shadow (one instanced mesh
per map) and the name tag just above the head. The sprite shader pulls each
vertex toward the camera along its own ray in proportion to its height, so
the billboard occludes and is occluded like an upright figure (it no longer
sinks into the castle facade, and the hero walking behind an NPC does not
draw over its head) while looking the same on screen; it also samples the
texture half a mip level sharper. Kinds without art, or art that fails to
load, fall back to extras.figure() as before. buildWorld() also returns
update(time) for a gentle idle breathing; the page does not call it yet.

The local viewer serves /assets/npcs/<kind>.png (names matching
/^[a-z]+\.png$/, image/png) and build-web.js copies the PNGs into the site.

City at the start view: 43 draw calls and 79,486 triangles (was 43 and
79,600). Where NPCs gather the calls drop, for example from 55 to 48, and
from 80 to 66 with the camera fully zoomed out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebuild the building pass of scripts/tripo-mundo.js so every detected
building reads as architecture from the far 2.5D camera: a stone plinth,
walls tinted per building with floor bands and corner posts, roofs with
thickness, eaves, tile courses and ridge caps (hipped for near-square
footprints, gabled for long ones), chimneys, dormers on the slopes that
face the camera, framed windows with sills and mullions, shutters in the
city, and a recessed door with an arch or lintel frame on the facade of
the door tile, so the door cell is now visible and the hero can stand in
the doorway.

The huge castle of RUNA becomes a fortress: corner towers with slate
spires and banners, crenellated curtain walls with arrow slits, a
gatehouse whose gate spans two floors over the castle gate tile, and a
keep with corner turrets. Churches get a stone bell tower. NOX is darker
and stranger: steep slate roofs with iron cresting, corner pinnacles,
pointed doors with a glowing rim and teal veins along bands, ridges and
hips. Detection, labels, the buildings count and dispose() are unchanged.

Static parts go into one vertex-colored mesh per map and the NOX veins
into one unlit mesh; window frames and shutters are InstancedMesh. City
runaInfo(): 45 calls and 124k triangles (was 43 and 80k).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Ground texture at 2 to 4 texels per column, with noise-warped soft edges
  between grass, dirt, cobble and gravel, shade at the foot of walls, trees
  and rocks, and a warm glow beside lava. Loose cobbles in grass become
  stones peeking through instead of square tiles.
- Ground shader detail at world scale: large patches tinted per map, relief,
  fine grain, cobblestones on ';' and gravel on '.' and '%'.
- A field around each map that fades into the fog, so the map no longer
  floats in the void past its walls.
- Low-poly pines, oaks and poplars with per-instance color, size, turn and
  tilt, bushes at their feet, rocks, flowers with stems, grass tufts (dense on
  tall grass, sparse on grass and quiet ground), reeds at pond edges and
  pebbles on paths. Solid shapes stay inside non-walkable cells. Small
  repeated plants are split by zone so only visible zones are drawn.
- Water and lava surfaces over the existing flat blocks: shallows, ripples
  and glints on water; drifting crust plates with glowing cracks on lava;
  a short skirt hides the block sides.
- buildWorld returns update(time) to drive wind, water and lava.

City at the default camera: 56 draw calls and 87k triangles (43 and 80k
before). Building the city map takes 67 to 91 ms in Chrome (34 to 91 ms
before).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Move the terrain helpers from right above buildWorld to right after
  hash(): the NPC sprites branch adds its helpers at the old spot.
- Drive the wind, water and lava clock from the ground mesh's
  onBeforeRender instead of a returned update(time): the NPC branch also
  adds update(time), and two keys with that name would silently drop one.
  The motion now runs without any change to the page.

Checked with git merge-tree: this branch merges cleanly with wip/edificios,
with wip/npcs, and with both together; the merged file runs in Chrome with
no console errors (city: 58 draw calls, 132k triangles).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The city start moves next to Elian, so the market sign no longer hides
the hero and the talk hint shows at once; the castle start moves near
the king so his name stays clear of the buttons. dispose() now frees
the instance buffers of every instanced mesh when a map is left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant