Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
Proyecto de video juego para competencia escolar
Translation pending
GamesHTML · JavaScriptGitHub @quiquitapoOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
El juego sigue funcionando exactamente igual que antes. Lo único nuevo es una pestaña GLOBAL junto a la de LOCAL, y una pregunta al terminar una partida de invasión. Si no despliegas nada de esto, el juego funciona igual: la pestaña GLOBAL simplemente avisará de que no pudo conectar. index.html va en la raíz. La carpeta api/ es lo que Vercel convierte automáticamente en endpoints: api/scores.js se publica como /api/scores. 1. Crear la base de datos en NeonRead the full README ↗
El juego sigue funcionando exactamente igual que antes. Lo único nuevo es una pestaña GLOBAL junto a la de LOCAL, y una pregunta al terminar una partida de invasión. Si no despliegas nada de esto, el juego funciona igual: la pestaña GLOBAL simplemente avisará de que no pudo conectar.
tu-repo/
├── index.html (el juego)
├── package.json
├── schema.sql (ranking global)
├── schema_salas.sql (salas del multijugador en línea)
└── api/
├── scores.js (función de servidor: ranking)
└── salas.js (función de servidor: salas)
index.html va en la raíz. La carpeta api/ es lo que Vercel convierte
automáticamente en endpoints: api/scores.js se publica como /api/scores.
schema.sql y pulsa Run.schema_salas.sql y pulsa Run (sin esto, el
multijugador en línea no funciona).postgresql://. Marca la opción Pooled connection si aparece.Guarda esa cadena: es la que usarás como DATABASE_URL.
Vuelve a pegar y ejecutar los dos archivos, schema.sql y
schema_salas.sql, en el SQL Editor de Neon. Están hechos para ejecutarse
sobre una base existente: no borran ni cambian ningún puntaje, solo añaden lo
que falte:
alien,
viltrum, dragon, maquina, mago y boros), tanto en el ranking como
en las salas;ranking_alien,
ranking_viltrum, ranking_dragon, ranking_maquina, ranking_mago y
ranking_boros.
Sirven para ver cada tabla de puntajes desde Neon
(SELECT * FROM ranking_mago;); el juego no las necesita.Después haz Redeploy en Vercel para que api/scores.js y api/salas.js
acepten a los personajes nuevos. Hasta entonces el servidor antiguo guardaba
lo suyo como si fuera la cría.
Cada personaje tiene su propio ranking: los cinco comparten la tabla scores
y se separan por la columna hero. En el navegador, los puntajes locales
también van por separado (invasion_scores, invasion_scores_vil,
invasion_scores_dra, invasion_scores_maq e invasion_scores_mag).
Cada mapa tiene además su propia tabla, global y local. Para activarlo en una
base que ya existía, vuelve a pegar y ejecutar schema.sql en el SQL Editor
de Neon y después haz Redeploy en Vercel. El script:
mapa (ciudad, alien o infierno); todos los puntajes
que ya tenías quedan como de la ciudad, sin perder ninguno;ranking_mapa_alien y ranking_mapa_infierno.Ejecuta schema.sql antes del Redeploy: el api/scores.js nuevo ya
consulta la columna mapa y, sin ella, el ranking global no cargaría.
En el navegador, los puntajes locales de los mapas nuevos añaden el nombre
del mapa a la clave (por ejemplo invasion_scores_alien o
invasion_scores_vil_infierno); los de la ciudad no cambian.
git init
git add index.html package.json schema.sql api/scores.js
git commit -m "Invasión Tentacular con ranking global"
git branch -M main
git remote add origin https://github.com/TU-USUARIO/TU-REPO.git
git push -u origin main
Entra en https://vercel.com y accede con tu cuenta de GitHub.
Add New → Project e importa el repositorio.
En Framework Preset deja Other. No hace falta build command.
Antes de desplegar, abre Environment Variables y añade:
| Name | Value | Environments |
|---|---|---|
DATABASE_URL | la cadena postgresql://… de Neon | Production, Preview, Development |
Pulsa Deploy.
Si añades la variable después del primer despliegue, entra en Settings → Environment Variables, guárdala y luego Deployments → ⋯ → Redeploy, porque las variables se leen al construir.
En tu proyecto de Vercel, Storage → Connect Database → Neon. Al vincularlo,
Vercel crea DATABASE_URL por ti y no tienes que copiar nada a mano.
https://tu-proyecto.vercel.app/api/scores?hero=dragon debe responder
{"ok":true,"hero":"dragon","scores":[…]} (vacío al principio).https://tu-proyecto.vercel.app/api/scores debe responder
{"ok":true,"scores":[]}.Si algo falla, Vercel → Deployments → Functions muestra los registros de
/api/scores.
| Variable | Obligatoria | Para qué |
|---|---|---|
DATABASE_URL | sí | Conexión a Neon. Solo la lee el servidor. |
La cadena nunca aparece en el HTML ni en el JavaScript del navegador: el
juego solo conoce la ruta /api/scores.
GET /api/scores?limit=25&hero=alienHay una tabla por personaje, igual que en el ranking local. hero acepta
alien, viltrum, dragon, maquina, mago o boros; si se omite, devuelve el
ranking conjunto.
{ "ok": true, "scores": [
{ "id": 1, "player_name": "ZORA", "score": 99000,
"hero": "alien", "device": "pc", "created_at": "2026-09-11T12:00:00.000Z" }
]}
limit admite de 1 a 100; por defecto 25. El campo hero de la respuesta
indica de qué tabla se trata, o null si vienen mezclados.
GET /api/scores?hero=alien&name=ZORAConsulta la posición de un jugador concreto, para poder decirle en qué puesto está cuando no entra en los 25 visibles.
{ "ok": true, "encontrado": true, "hero": "alien",
"score": 9000, "rank": 4, "total": 128 }
Si ese jugador nunca subió nada con ese personaje: { "ok": true, "encontrado": false }.
POST /api/scores{ "player_name": "ZORA", "score": 99000, "hero": "alien", "device": "pc" }
Cada jugador tiene un solo registro por personaje: el de su mejor partida.
{ "ok": true, "mejorado": true, "score": 99000, "rank": 3, "total": 128, "hero": "alien" }
201 y mejorado: true → era mejor que la marca anterior y se guardó.200 y mejorado: false → ya tenía una marca igual o mejor; la tabla no se
toca y score devuelve la que conserva.Quién decide es la base de datos, con ON CONFLICT ... WHERE scores.score < EXCLUDED.score, así que dos partidas terminadas a la vez no pueden pisarse.
El servidor no se fía del navegador y rechaza con 400:
Además recorta el nombre a 14 caracteres, le quita etiquetas y caracteres de
control, y normaliza hero y device a los valores conocidos.
SaveIO, localStorage, saveScore(), loadScores(),
renderScoreList(), commitName() y pendingScore siguen tal cual.