chiark / gitweb /
937969ec48e5aa20e5cfabe5491a56d5ce5294ae
[xor] / game.c
1 /* -*-c-*-
2  *
3  * $Id: game.c,v 1.1 2003/12/12 10:55:30 mdw Exp $
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 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: game.c,v $
32  * Revision 1.1  2003/12/12 10:55:30  mdw
33  * Initial checkin.  Not there yet.
34  *
35  */
36
37 /*----- Header files ------------------------------------------------------*/
38
39 #include "xor.h"
40
41 /*----- Main code ---------------------------------------------------------*/
42
43 #define G_CELLSET(g, x, y, c) do {                                      \
44   undo_cell((g), (x), (y), (c));                                        \
45   CELLSET((g)->l, (x), (y), (c));                                       \
46 } while (0)
47
48 void game_die(game_state *g, int x, int y)
49 {
50   game_player *p;
51
52   for (p = g->p; p && (p->x != x || p->y != y); p = p->next) ;
53   if (!p) return;
54   ui_frame(g->p->u);
55   undo_die(g, p);
56   p->f |= PF_DEAD;
57   G_CELLSET(g, p->x, p->y, C_EMPTY);
58   for (p = g->p; p && (p->f & PF_DEAD); p = p->next) ;
59   if (p) ui_message(g->p->u, "Whoops!");
60   else ui_message(g->p->u, "Gotcha!");
61 }
62
63 void game_explode(game_state *g, const point *pt, int n)
64 {
65   int i;
66
67   ui_explode(g->p->u, pt, n);
68   for (i = 0; i < n; i++) game_die(g, pt[i].x, pt[i].y);
69   for (i = 0; i < n; i++) G_CELLSET(g, pt[i].x, pt[i].y, C_EMPTY);
70 }
71
72 void game_moveobj(game_state *g, int x, int y, int xx, int yy)
73 {
74   int c = CELL(g->l, x, y);
75   G_CELLSET(g, x, y, C_EMPTY);
76   G_CELLSET(g, xx, yy, c);
77 }
78
79 static int doupdateh(game_state *g)
80 {
81   level *l = g->l;
82   int i, j, c;
83   int rc = 0;
84
85   for (j = 0; j < l->h; j++) {
86     for (i = 0; i < l->w; i++) {
87       c = CELL(l, i, j) & CF_CELLMASK;
88       if (cellmap[c]->moveh && cellmap[c]->moveh(g, i, j)) rc = 1;
89     }
90   }
91   if (rc) ui_frame(g->p->u);
92   return (rc);
93 }
94
95 static int doupdatev(game_state *g)
96 {
97   level *l = g->l;
98   int i, j, c;
99   int rc = 0;
100
101   for (i = 0; i < l->w; i++) {
102     for (j = l->h - 1; j >= 0; j--) {
103       c = CELL(l, i, j) & CF_CELLMASK;
104       if (cellmap[c]->movev && cellmap[c]->movev(g, i, j)) rc = 1;
105     }
106   }
107   if (rc) ui_frame(g->p->u);
108   return (rc);
109 }
110
111 static void updatev(game_state *g) { while (doupdatev(g) || doupdateh(g)) ; }
112 static void updateh(game_state *g) { while (doupdateh(g) || doupdatev(g)) ; }
113
114 void game_switchto(game_state *g, game_player *p)
115 {
116   /* --- Don't stash undo records here --- */
117
118   if (!(g->p->f & PF_DEAD))
119     CELLSET(g->l, g->p->x, g->p->y, C_SPARE);
120   g->p->prev = g->ptail;
121   p->prev->next = 0;
122   g->ptail->next = g->p;
123   g->ptail = p->prev;
124   p->prev = 0;
125   g->p = p;
126   g->l->v++;
127   CELLSET(g->l, g->p->x, g->p->y, C_PLAYER);
128   ui_switch(g->p->u);
129 }
130
131 int game_switch(game_state *g)
132 {
133   game_player *p;
134
135   if (!g->p)
136     return (0);
137   for (p = g->p->next; p && (p->f & PF_DEAD); p = p->next)
138     ;
139   if (!p) return (0);
140   if (g->l->v >= g->l->vtot) return (1);
141   undo_switch(g);
142   game_switchto(g, p);
143   return (1);
144 }
145
146 void game_move(game_state *g, int dx, int dy)
147 {
148   int x = g->p->x;
149   int y = g->p->y;
150   int c = CELL(g->l, x + dx, y + dy) & CF_CELLMASK;
151
152   if (g->p->f & PF_DEAD) return;
153   if (g->l->v >= g->l->vtot) return;
154   if ((cellmap[c]->f & ((dx ? CF_HPASS : 0) | (dy ? CF_VPASS : 0))) ||
155       (cellmap[c]->nudge &&
156        cellmap[c]->nudge(g, x + dx, y + dy, dx, dy) > 0)) {
157     undo_pmove(g);
158     game_moveobj(g, x, y, x + dx, y + dy);
159     g->p->x = x + dx;
160     g->p->y = y + dy;
161     g->l->v++;
162     ui_track(g->p->u, x + dx, y + dy);
163     if (dx) updatev(g); else updateh(g);
164     ui_update(g->p->u);
165     if (g->p->f & PF_DEAD) game_switch(g);
166   }
167 }
168
169 void game_quit(game_state *g) { g->f |= GF_QUIT; }
170
171 static void addplayer(game_state *g, int x, int y)
172 {
173   game_player *p = CREATE(game_player);
174   p->f = 0;
175   p->x = x;
176   p->y = y;
177   p->u = ui_start(g->l, x, y);
178   p->next = 0;
179   p->prev = g->ptail;
180   if (g->ptail) g->ptail->next = p; else g->p = p;
181   g->ptail = p;
182 }
183
184 void game_go(level *l)
185 {
186   game_state g;
187   int x, y;
188
189   g.l = lev_copy(l);
190   lev_write(g.l, stdout);
191   g.f = 0;
192   g.ptail = 0;
193   lev_findcell(l, C_PLAYER, &x, &y);
194   addplayer(&g, x, y);
195   if (lev_findcell(l, C_SPARE, &x, &y)) {
196     do
197       addplayer(&g, x, y);
198     while (lev_findnext(l, C_SPARE, &x, &y));
199   }
200   undo_init(&g);
201   ui_switch(g.p->u);
202   while (!(g.f & GF_QUIT)) {
203     undo_begin(&g);
204     ui_go(&g, g.p->u);
205     undo_commit(&g);
206   }
207   lev_free(g.l);
208   undo_free(&g);
209 }
210
211 /*----- That's all, folks -------------------------------------------------*/