Misiones diarias y semanales: un motivo para volver
Cada partida terminada ahora hace avanzar un conjunto de misiones diarias y semanales, otorga XP en cuanto usted completa 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 termina una partida (salvo las que acaban pronto), gane o pierda.
No hay que reclamar nada ni pulsar ningún botón: en el momento en que termina una partida, el servidor revisa sus misiones, abona las que usted completó y suma la XP directamente a su nivel. En el lanzamiento corrían seis misiones:
- Diarias: jugar 1 partida, jugar 3 partidas, ganar 1 partida.
- Semanales: jugar 10 partidas, ganar 3 partidas, jugar 3 partidas Rápidas.
- Las misiones diarias se reinician cada día; las semanales, cada lunes.
| Misión | Frecuencia | Objetivo | Recompensa |
|---|---|---|---|
| Jugar una partida | Diaria | 1 | +50 XP |
| Seguir jugando | Diaria | 3 | +150 XP |
| Ganar una partida | Diaria | 1 | +250 XP |
| Constante | Semanal | 10 partidas | +400 XP |
| En racha | Semanal | 3 victorias | +600 XP |
| Demonio de la velocidad | Semanal | 3 partidas Rápidas | +500 XP |
En el lanzamiento, terminar partidas también otorgaba puntos para la clasificación semanal; desde el 12 de agosto de 2026, la clasificación semanal ordena según la puntuación ganada durante la semana. 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 Rápidas | +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 Jugar 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.
