commit 81d7034e427025f3924725bc6da2f30cab8b11d2
parent e7c053eba5afaaebc3afd07e955b3e2b48175f7f
Author: Francisco Armando Rosmino <franciscorosmino@rosmino.com.ar>
Date: Sat, 3 Oct 2026 14:47:31 -0300
Oro para el que no tiene el turno, puntitos en Navegantes, una sola 'otra'
- ctrl: en la fase de oro actua cada asiento local con oro, aunque el turno
sea de otro (como el descarte): la partida CPWAZB quedo 'esperando a Bot 4'
cuando el oro era de Pancho. Test t_ctrl_gold.
- Fichas de numero: puntos de probabilidad desde R 20 con ficha de radio 10
(en los tableros de Navegantes el hexagono queda en ~20 px y no salian).
- AGAIN: si alguien de la mesa ya pidio otra partida, al siguiente se lo manda
a su asiento en esa en vez de crear una mas (se habian creado dos).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
4 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/catan/core/ctrl.c b/catan/core/ctrl.c
@@ -46,8 +46,9 @@ static int pick_actor(const Ctrl *c)
{
const Game *g = c->g;
if (g->phase == PH_OVER) return -1;
+ /* descartar (7) y elegir el oro los hace cada uno, no solo el del turno */
for (int s = 0; s < g->o.np; s++)
- if (is_local(c, s) && g->phase == PH_DISCARD && g->p[s].discard) return s;
+ if (is_local(c, s) && ((g->phase == PH_DISCARD && g->p[s].discard) || (g->phase == PH_GOLD && g->p[s].gold))) return s;
if (g->offer.active)
for (int s = 0; s < g->o.np; s++)
if (is_local(c, s) && s != g->cur && game_needs(g, s)) return s;
diff --git a/catan/gfx/board.c b/catan/gfx/board.c
@@ -208,9 +208,12 @@ static void token(Surface *s, const BoardGeom *g, int h, int num)
{
int cx, cy;
geom_hex(g, h, &cx, &cy);
+ /* los puntos de probabilidad entran con una ficha de radio 10 (en los tableros grandes
+ * de Navegantes el hexagono queda en ~20 px); la Pico (R 24) tambien los lleva ahora */
+ bool pips = g->R >= 20;
int r = g->R * 32 / 100;
const Font *f = g->R >= 30 ? &font_bold : &font_normal;
- if (r < f->h * 3 / 4) r = f->h * 3 / 4;
+ if (r < (pips ? 10 : f->h * 3 / 4)) r = pips ? 10 : f->h * 3 / 4;
gfx_disc(s, cx, cy, r + 1, C_OUTLINE);
gfx_disc(s, cx, cy, r, C_TOKEN);
char txt[3] = { 0 };
@@ -218,7 +221,6 @@ static void token(Surface *s, const BoardGeom *g, int h, int num)
else txt[0] = (char)('0' + num);
Color c = (num == 6 || num == 8) ? C_RED : C_OUTLINE;
int tw = gfx_text_w(f, txt);
- bool pips = g->R >= 26; /* los puntos de probabilidad; la Pico (R 24) no los tiene */
int ty = cy - f->h / 2 - (pips ? 2 : 0);
gfx_text(s, f, cx - tw / 2, ty, txt, c);
if (pips) {
diff --git a/catan/linux/selftest.c b/catan/linux/selftest.c
@@ -1365,6 +1365,28 @@ static void t_ctrl_robber(void)
CHECK(auth_submit(&a, &m) == E_OK && g->robber == h1 && g->p[1].res[GRAIN] == 1 && g->p[0].res[GRAIN] == 1);
}
+/* Navegantes: el oro lo elige cada uno aunque el turno sea de otro (igual que el descarte). */
+static void t_ctrl_gold(void)
+{
+ static Auth a;
+ Opts o;
+ opts_default(&o, 3, false);
+ opts_scenario(&o, SCEN_SEA1);
+ auth_new(&a, &o, 5, NULL, NULL);
+ Game *g = &a.g;
+ static const char *nm[MAXP] = { "A", "B", "C", "D" };
+ g->phase = PH_GOLD; g->cur = 1; g->rolled = 1;
+ g->p[0].gold = 1;
+ static Ctrl c;
+ ctrl_init(&c, 1, false); /* solo el asiento 0 es local */
+ for (int i = 0; i < MAXP; i++) c.names[i] = nm[i];
+ ctrl_sync(&c, g);
+ CHECK(c.actor == 0 && c.mode == CM_CARDS && c.intent == I_GOLD);
+ Move m;
+ CHECK(!ctrl_key(&c, '1', &m)); /* elige un recurso */
+ CHECK(ctrl_key(&c, K_OK, &m) && m.type == M_GOLD && m.p == 0);
+}
+
int app_selftest(void)
{
printf("selftest del nucleo\n");
@@ -1391,6 +1413,7 @@ int app_selftest(void)
t_ctrl_nav();
t_ctrl_setup();
t_ctrl_robber();
+ t_ctrl_gold();
t_serialize();
t_replay();
t_freeplay();
diff --git a/comun/server/srv.c b/comun/server/srv.c
@@ -44,6 +44,7 @@ typedef struct {
int nchat, capchat;
FILE *chatf;
bool loading;
+ char again[8]; /* la partida "otra como esta" ya creada (AGAIN), para no crear dos */
void *st; /* estado del juego (GameModule.state_size) */
} SGame;
@@ -588,13 +589,26 @@ static void cmd_again(Server *s, Client *c)
const GameModule *gm = s->gm;
if (c->game < 0) { sendf(s, c, "ERR %d not in a game", gm->err_args); return; }
SGame *old = s->games[c->game];
+ /* si otro de la mesa ya pidio "otra", es esa: lo mando a su asiento en vez de crear una mas */
+ if (old->again[0]) {
+ int gi = find_game(s, old->again);
+ if (gi >= 0 && !gm->is_over(s->games[gi]->st)) {
+ SGame *g = s->games[gi];
+ for (int k = 0; k < g->np; k++)
+ if (g->kind[k] == 'P' && !strcmp(g->token[k], old->token[c->seat])) { detach(s, c); attach(s, c, gi, k); return; }
+ }
+ }
char opts[SRV_LINE], seats[SRV_MAX_SEATS + 1] = "";
if (gm->again) gm->again(old->opts, opts, sizeof opts);
else snprintf(opts, sizeof opts, "%s", old->opts);
int np = old->np, myseat = c->seat;
for (int i = 0; i < np; i++) seats[i] = old->kind[i] == 'B' ? 'B' : old->kind[i] == 'N' ? 'N' : 'H';
seats[np] = 0;
+ char oldcode[8];
+ snprintf(oldcode, sizeof oldcode, "%s", old->code);
create_game(s, c, opts, seats, np, old, myseat);
+ int oi = find_game(s, oldcode); /* create_game puede mover el arreglo de partidas */
+ if (oi >= 0 && c->game >= 0) snprintf(s->games[oi]->again, sizeof s->games[oi]->again, "%s", s->games[c->game]->code);
}
static void cmd_join(Server *s, Client *c, const char *code)