chiark / gitweb /
infra: Clean up project setup
[xor] / game.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Main game logic
6  *
7  * (c) 2003 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of XOR.
13  *
14  * XOR is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * XOR is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with XOR; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "xor.h"
32
33 /*----- Main code ---------------------------------------------------------*/
34
35 #define G_CELLSET(g, x, y, c) do {                                      \
36   undo_cell((g), (x), (y), (c));                                        \
37   CELLSET((g)->l, (x), (y), (c));                                       \
38 } while (0)
39
40 void game_die(game_state *g, int x, int y)
41 {
42   game_player *p;
43
44   for (p = g->p; p && (p->x != x || p->y != y); p = p->next) ;
45   if (!p) return;
46   ui_frame(g->p->u);
47   undo_die(g, p);
48   p->f |= PF_DEAD;
49   G_CELLSET(g, p->x, p->y, C_EMPTY);
50   for (p = g->p; p && (p->f & PF_DEAD); p = p->next) ;
51   if (p) ui_message(g->p->u, "Whoops!");
52   else ui_message(g->p->u, "Gotcha!");
53 }
54
55 void game_explode(game_state *g, const point *pt, int n)
56 {
57   int i;
58
59   ui_explode(g->p->u, pt, n);
60   for (i = 0; i < n; i++) game_die(g, pt[i].x, pt[i].y);
61   for (i = 0; i < n; i++) G_CELLSET(g, pt[i].x, pt[i].y, C_EMPTY);
62 }
63
64 void game_moveobj(game_state *g, int x, int y, int xx, int yy)
65 {
66   int c = CELL(g->l, x, y);
67   G_CELLSET(g, x, y, C_EMPTY);
68   G_CELLSET(g, xx, yy, c);
69 }
70
71 static int doupdateh(game_state *g)
72 {
73   level *l = g->l;
74   int i, j, c;
75   int rc = 0;
76
77   for (j = 0; j < l->h; j++) {
78     for (i = 0; i < l->w; i++) {
79       c = CELL(l, i, j) & CF_CELLMASK;
80       if (cellmap[c]->moveh && cellmap[c]->moveh(g, i, j)) rc = 1;
81     }
82   }
83   if (rc) ui_frame(g->p->u);
84   return (rc);
85 }
86
87 static int doupdatev(game_state *g)
88 {
89   level *l = g->l;
90   int i, j, c;
91   int rc = 0;
92
93   for (i = 0; i < l->w; i++) {
94     for (j = l->h - 1; j >= 0; j--) {
95       c = CELL(l, i, j) & CF_CELLMASK;
96       if (cellmap[c]->movev && cellmap[c]->movev(g, i, j)) rc = 1;
97     }
98   }
99   if (rc) ui_frame(g->p->u);
100   return (rc);
101 }
102
103 static void updatev(game_state *g) { while (doupdatev(g) || doupdateh(g)) ; }
104 static void updateh(game_state *g) { while (doupdateh(g) || doupdatev(g)) ; }
105
106 void game_switchto(game_state *g, game_player *p)
107 {
108   /* --- Don't stash undo records here --- */
109
110   if (!(g->p->f & PF_DEAD))
111     CELLSET(g->l, g->p->x, g->p->y, C_SPARE);
112   g->p->prev = g->ptail;
113   p->prev->next = 0;
114   g->ptail->next = g->p;
115   g->ptail = p->prev;
116   p->prev = 0;
117   g->p = p;
118   CELLSET(g->l, g->p->x, g->p->y, C_PLAYER);
119   ui_switch(g->p->u);
120 }
121
122 int game_switch(game_state *g)
123 {
124   game_player *p;
125
126   if (!g->p)
127     return (0);
128   for (p = g->p->next; p && (p->f & PF_DEAD); p = p->next)
129     ;
130   if (!p) return (0);
131   if (g->l->v >= g->l->vtot) return (1);
132   g->l->v++;
133   undo_switch(g);
134   game_switchto(g, p);
135   return (1);
136 }
137
138 void game_move(game_state *g, int dx, int dy)
139 {
140   int x = g->p->x;
141   int y = g->p->y;
142   int c = CELL(g->l, x + dx, y + dy) & CF_CELLMASK;
143
144   if (g->p->f & PF_DEAD) return;
145   if (g->l->v >= g->l->vtot) return;
146   if ((cellmap[c]->f & ((dx ? CF_HPASS : 0) | (dy ? CF_VPASS : 0))) ||
147       (cellmap[c]->nudge &&
148        cellmap[c]->nudge(g, x + dx, y + dy, dx, dy) > 0)) {
149     undo_pmove(g);
150     game_moveobj(g, x, y, x + dx, y + dy);
151     g->p->x = x + dx;
152     g->p->y = y + dy;
153     g->l->v++;
154     ui_track(g->p->u, x + dx, y + dy);
155     if (dx) updatev(g); else updateh(g);
156     ui_update(g->p->u);
157     if (g->p->f & PF_DEAD) game_switch(g);
158   }
159 }
160
161 void game_quit(game_state *g) { g->f |= GF_QUIT; }
162
163 static void addplayer(game_state *g, int x, int y)
164 {
165   game_player *p = CREATE(game_player);
166   p->f = 0;
167   p->x = x;
168   p->y = y;
169   p->u = ui_start(g->l, x, y);
170   p->next = 0;
171   p->prev = g->ptail;
172   if (g->ptail) g->ptail->next = p; else g->p = p;
173   g->ptail = p;
174 }
175
176 void game_go(level *l)
177 {
178   game_state g;
179   int x, y;
180
181   g.l = lev_copy(l);
182   lev_write(g.l, stdout);
183   g.f = 0;
184   g.ptail = 0;
185   lev_findcell(l, C_PLAYER, &x, &y);
186   addplayer(&g, x, y);
187   if (lev_findcell(l, C_SPARE, &x, &y)) {
188     do
189       addplayer(&g, x, y);
190     while (lev_findnext(l, C_SPARE, &x, &y));
191   }
192   undo_init(&g);
193   ui_switch(g.p->u);
194   while (!(g.f & GF_QUIT)) {
195     undo_begin(&g);
196     ui_go(&g, g.p->u);
197     undo_commit(&g);
198   }
199   lev_free(g.l);
200   undo_free(&g);
201 }
202
203 /*----- That's all, folks -------------------------------------------------*/