Misiones diarias y semanales: un motivo para volver
Cada partida terminada ahora hace avanzar un conjunto de misiones diarias y semanales, otorga XP en el instante en que completas una y alimenta una nueva clasificación semanal.

En palabras sencillas
Hasta ahora, la única progresión en opolyx era una recompensa única por ganar una partida. No había nada que perseguir día a día. Las misiones cambian eso: un pequeño conjunto de objetivos diarios y semanales que avanzan cada vez que terminas una partida, ganes o pierdas.
No reclamas nada y no hay ningún botón que pulsar: en el momento en que termina una partida, el servidor revisa tus misiones, abona las que completaste y suma la XP directamente a tu nivel. Seis misiones corren a la vez:
- Diarias: juega 1 partida, juega 3 partidas, gana 1 partida.
- Semanales: juega 10 partidas, gana 3 partidas, gana 3 partidas en modo Rápido.
- Las misiones diarias se reinician cada día; las semanales, cada lunes.
| Misión | Frecuencia | Objetivo | Recompensa |
|---|---|---|---|
| Juega una partida | Diaria | 1 | +50 XP |
| Sigue jugando | Diaria | 3 | +120 XP |
| Gana una partida | Diaria | 1 | +150 XP |
| Constante | Semanal | 10 partidas | +300 XP |
| En racha | Semanal | 3 victorias | +400 XP |
| Demonio de la velocidad | Semanal | 3 victorias en Rápido | +400 XP |
Terminar partidas también otorga puntos para la clasificación semanal, así que cuanto más juegas, más alto subes. La clasificación se reinicia cada semana, dando a todos una oportunidad limpia de llegar a la cima el lunes por la mañana.
Para los más técnicos
El esquema para un bucle de retención completo ya existía, pero estaba inactivo y sin usar: missions / user_missions (frecuencia, objetivo, progreso, xp_reward) y weekly_top (week_start, puntos). La migración 0031 lo conectó a la ruta de recompensa por fin de partida ya existente en lugar de inventar nada nuevo.
En un fin de partida real, el daemon recorre a cada jugador humano y llama a un único RPC con rol de servicio. Avanza cada misión que la partida satisface en el ciclo actual, otorga la XP la primera vez que se cruza un objetivo y suma al recuento de puntos semanal:
-- grant_play_rewards(p_game, p_user, p_won): security definer, service-role only
-- daily cadence = current_date; weekly = date_trunc('week', now())
-- weekly_top.points += 20 (play) + (won ? 200 : 0) + mission_xp_earned
-- idempotent per (game_id, user_id) via the game_play_grants guard; bots skipped| Coincidencia de evento | Avanza | Puntos |
|---|---|---|
| {"event":"play"} | Cada partida terminada | +20 |
| {"event":"win"} | Solo victorias | +200 |
| {"event":"play","mode":"speed"} | Solo partidas en Rápido | +20 |
La idempotencia es una fila de guarda por (partida, usuario), de modo que un commit reintentado nunca paga dos veces. En el momento de aplicar surgió una trampa de escalada de privilegios: los privilegios por defecto de Postgres conceden EXECUTE sobre las funciones nuevas a anon/authenticated, y revoke … from public no lo elimina. Una función de recompensa SECURITY DEFINER invocable por cualquier usuario con sesión es una granja de XP: se corrigió con revoke … from public, anon, authenticated para que solo {postgres, service_role} pueda llamarla, igual que award_game_winner.
Las recompensas son solo de XP por ahora: las monedas pertenecen a la vía de monetización, que primero necesita un sumidero de gasto. Tanto el panel de la página de Juego como la Clasificación leen el progreso del ciclo actual en el cliente, con la semana anclada al mismo límite del lunes que usa Postgres para que nunca discrepen.
