2026-08-07 — marvelousdb: URLs compartibles de VS battle + lore generado

Contexto: el usuario pidio (1) URLs especificas de enfrentamientos para compartir (mas amigables que ?a=36639&b=1009473) y (2) inventar un pequeno lore cuando el personaje no tenga introduccion, sin dejar nada vacio.

URLs compartibles amigables

  • Formato: /vs/<slugA>-vs-<slugB> — ej. /vs/cthulhu-vs-u-s-agent (slugs de “U.S. Agent”: los puntos → guiones).
  • slugify() duplicado en api.js (resolucion) y web.js (generacion): lowercase, quita comillas/parentesis, separadores → ’-’, max 60 chars.
  • GET /api/characters/by-slug?universe=&slug= resuelve slug → id (scan sobre los nombres del universo, <50ms en SQLite). Ruta declarada antes de /api/characters/:id.
  • El boton “Share this fight” copia location.origin + shareUrl (los universos no-default se anaden como ?uA=/uB=).
  • Determinismo: con idA+idB, la suerte se calcula con strHash(idA|idB)%3-1 → la misma URL SIEMPRE da el mismo ganador. Las batallas aleatorias mantienen suerte random.
  • Las rutas viejas (?a=&b=) siguen funcionando.

Lore generado (nada vacio)

  • characterLore(c): primer parrafo de wiki.bio si existe (el “parrafo introductorio” real); si no, plantilla pulp generada (6 variantes elegidas por strHash(id), estable entre visitas) rellenando NAME/DEBUT/UNIVERSE con los datos disponibles.
  • API: toCharacterFull incluye lore. Vista character: description real si existe, si no el lore con clase .lore-generated (cursiva).
  • hasData: con lore siempre presente, el aviso “no information” desaparece.

Bugs corregidos

  • Score minimo 0 posible (Loki LEGO: tier human + bio vacia + luck -1) → Math.max(1, …).

Verificacion

  • 24/24 tests (nuevos: determinismo por pareja, by-slug, lore nunca vacio en 50 personajes PDSH).
  • /vs/cthulhu-vs-u-s-agent → 200, “Cthulhu defeats U.S. Agent”.
  • Loki LEGO (sin bio): lore generado “Some say Loki was never real…”