chiark / gitweb /
infra: Clean up project setup
[xor] / undo.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Management of undo records
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 void undo_init(game_state *g)
36 {
37   g->u = g->r = g->m = 0;
38 }
39
40 void undo_begin(game_state *g)
41 {
42   undo_move *u = CREATE(undo_move);
43   u->next = 0;
44   u->act = 0;
45   g->m = u;
46 }
47
48 void undo_cell(game_state *g, int x, int y, int c)
49 {
50   undo_action *a, **aa;
51
52   for (aa = &g->m->act; *aa; aa = &a->next) {
53     a = *aa;
54     if (a->type == A_CELL && a->u.c.x == x && a->u.c.y == y) {
55       if (a->u.c.c == c) {
56         *aa = a->next;
57         DESTROY(a);
58       }
59       return;
60     }
61   }
62   if (c == CELL(g->l, x, y)) return;
63   a = CREATE(undo_action);
64   a->next = 0;
65   *aa = a;
66   a->type = A_CELL;
67   a->u.c.x = x;
68   a->u.c.y = y;
69   a->u.c.c = CELL(g->l, x, y);
70 }
71
72 void undo_die(game_state *g, game_player *p)
73 {
74   undo_action *a = CREATE(undo_action);
75
76   a->next = g->m->act;
77   a->type = A_DIE;
78   a->u.p = p;
79   g->m->act = a;
80 }
81
82 void undo_levelf(game_state *g)
83 {
84   undo_action *a = CREATE(undo_action);
85
86   a->next = g->m->act;
87   a->type = A_LEVEL;
88   a->u.f = g->l->f;
89   g->m->act = a;
90 }
91
92 void undo_mask(game_state *g)
93 {
94   undo_action *a = CREATE(undo_action);
95
96   a->next = g->m->act;
97   a->type = A_MASK;
98   a->u.n = g->l->m;
99   g->m->act = a;
100 }
101
102 void undo_pmove(game_state *g)
103 {
104   undo_action *a = CREATE(undo_action);
105
106   a->next = g->m->act;
107   a->type = A_MOVE;
108   a->u.c.x = g->p->x;
109   a->u.c.y = g->p->y;
110   g->m->act = a;
111 }
112
113 void undo_switch(game_state *g)
114 {
115   undo_action *a = CREATE(undo_action);
116   a->next = g->m->act;
117   a->type = A_SWITCH;
118   a->u.p = g->p;
119   g->m->act = a;
120 }
121
122 static void freelist(undo_move **mm)
123 {
124   undo_move *u, *uu;
125   undo_action *a, *aa;
126
127   for (u = *mm; u; u = uu) {
128     uu = u->next;
129     for (a = u->act; a; a = aa) {
130       aa = a->next;
131       DESTROY(a);
132     }
133     DESTROY(u);
134   }
135   *mm = 0;
136 }
137
138 void undo_commit(game_state *g)
139 {
140   if (!g->m->act)
141     DESTROY(g->m);
142   else {
143     g->m->next = g->u;
144     g->u = g->m;
145     freelist(&g->r);
146   }
147   g->m = 0;
148 }
149
150 static int do_undo(game_state *g, undo_move **m, undo_move **mm)
151 {
152   undo_move *u = *m;
153   undo_action *a;
154   game_player *p;
155   int x, y, c;
156   unsigned f;
157
158   if (!u) return (0);
159   *m = u->next;
160
161   for (a = u->act; a; a = a->next) {
162     switch (a->type) {
163       case A_CELL:
164         c = a->u.c.c;
165         a->u.c.c = CELL(g->l, a->u.c.x, a->u.c.y);
166         CELLSET(g->l, a->u.c.x, a->u.c.y, c);
167         break;
168       case A_DIE:
169         a->u.p->f &= ~PF_DEAD;
170         a->type = A_LIVE;
171         break;
172       case A_MASK:
173         c = a->u.n;
174         a->u.n = g->l->m;
175         g->l->m = c;
176         break;
177       case A_LEVEL:
178         f = a->u.f;
179         a->u.f = g->l->f;
180         g->l->f = f;
181         break;
182       case A_MOVE:
183         x = a->u.c.x;
184         y = a->u.c.y;
185         a->u.c.x = g->p->x;
186         a->u.c.y = g->p->y;
187         g->p->x = x;
188         g->p->y = y;
189         ui_track(g->p->u, x, y);
190         break;
191       case A_LIVE:
192         a->u.p->f |= PF_DEAD;
193         a->type = A_DIE;
194         break;
195       case A_SWITCH:
196         p = a->u.p;
197         a->u.p = g->p;
198         game_switchto(g, p);
199         break;
200       default:
201         abort();
202     }
203   }
204   
205   u->next = *mm;
206   *mm = u;
207   return (1);
208 }
209
210 void undo(game_state *g)
211 {
212   if (do_undo(g, &g->u, &g->r)) g->l->v--;
213   ui_update(g->p->u);
214 }
215
216 void redo(game_state *g)
217 {
218   if (do_undo(g, &g->r, &g->u)) g->l->v++;
219   ui_update(g->p->u);
220 }
221
222 void undo_free(game_state *g)
223 {
224   freelist(&g->m);
225   freelist(&g->u);
226   freelist(&g->r);
227 }
228
229 /*----- That's all, folks -------------------------------------------------*/