1 /*
2     engine.c
3 
4     Copyright (C) 2010-2019 Amf
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #include "chroma.h"
25 #include "level.h"
26 #include "util.h"
27 
28 #ifdef XOR_COMPATIBILITY
29 int xor_move(struct level* plevel, int move);
30 int xor_evolve(struct level* plevel);
31 void xor_focus(struct level* plevel);
32 extern int options_xor_mode;
33 #endif
34 #ifdef ENIGMA_COMPATIBILITY
35 int enigma_move(struct level* plevel, int move);
36 int enigma_evolve(struct level* plevel);
37 extern int options_enigma_mode;
38 #endif
39 
40 extern int options_debug;
41 extern char* piece_name[];
42 
43 /*               l  u  r  d  n  s  w   */
44 int move_x[] = {-1, 0, 1, 0, 0, 0, 0};
45 int move_y[] = {0, -1, 0, 1, 0, 0, 0};
46 
47 #ifdef ENIGMA_COMPATIBILITY
48 int enigma_move_order[] = {MOVE_DOWN, MOVE_RIGHT, MOVE_LEFT, MOVE_UP};
49 #endif
50 #ifdef XOR_COMPATIBILITY
51 int xor_teleport_order[] = {MOVE_RIGHT, MOVE_UP, MOVE_LEFT, MOVE_DOWN};
52 #endif
53 
54 struct mover* mover_new(struct level* plevel, int x, int y, int d, int piece, int fast);
55 void mover_consider(struct level* plevel, int x, int y, int d);
56 struct mover* mover_explode(struct level *plevel, int x, int y, int d, int p);
57 void explode_sides(struct level* plevel, int x, int y, int p, int d);
58 int canfall(int p, int into, int d);
59 int canmove(int p, int into, int d, int fast);
60 int canexplode(int p, int into, int d, int fast, int mode);
61 int canbepushed(int p, int into, int d, int mode);
62 
explosiontype(int p)63 int explosiontype(int p)
64 {
65     switch(p)
66     {
67         case PIECE_ARROW_RED_LEFT:
68         case PIECE_ARROW_RED_RIGHT:
69         case PIECE_BOMB_RED_LEFT:
70         case PIECE_BOMB_RED_RIGHT:
71             return PIECE_EXPLOSION_NEW_RED_VERTICAL;
72         case PIECE_ARROW_RED_UP:
73         case PIECE_ARROW_RED_DOWN:
74         case PIECE_BOMB_RED_UP:
75         case PIECE_BOMB_RED_DOWN:
76             return PIECE_EXPLOSION_NEW_RED_HORIZONTAL;
77 
78         case PIECE_ARROW_GREEN_LEFT:
79         case PIECE_ARROW_GREEN_RIGHT:
80         case PIECE_BOMB_GREEN_LEFT:
81         case PIECE_BOMB_GREEN_RIGHT:
82             return PIECE_EXPLOSION_NEW_GREEN_VERTICAL;
83         case PIECE_ARROW_GREEN_UP:
84         case PIECE_ARROW_GREEN_DOWN:
85         case PIECE_BOMB_GREEN_UP:
86         case PIECE_BOMB_GREEN_DOWN:
87             return PIECE_EXPLOSION_NEW_GREEN_HORIZONTAL;
88 
89         case PIECE_ARROW_BLUE_LEFT:
90         case PIECE_ARROW_BLUE_RIGHT:
91         case PIECE_BOMB_BLUE_LEFT:
92         case PIECE_BOMB_BLUE_RIGHT:
93             return PIECE_EXPLOSION_NEW_BLUE_VERTICAL;
94         case PIECE_ARROW_BLUE_UP:
95         case PIECE_ARROW_BLUE_DOWN:
96         case PIECE_BOMB_BLUE_UP:
97         case PIECE_BOMB_BLUE_DOWN:
98             return PIECE_EXPLOSION_NEW_BLUE_HORIZONTAL;
99 
100         default:
101             /* This should never happen */
102             return PIECE_GONE;
103     }
104 }
105 
level_moved(struct level * plevel,int move)106 void level_moved(struct level* plevel, int move)
107 {
108     if(move != MOVE_REDO)
109         level_addmove(plevel, move);
110     else
111     {
112         if(plevel->move_current != NULL)
113             plevel->move_current = plevel->move_current->next;
114         else
115             plevel->move_current = plevel->move_first;
116     }
117 
118     plevel->moves ++;
119     plevel->flags |= LEVELFLAG_MOVES;
120 
121     level_storemovers(plevel);
122 }
123 
level_move(struct level * plevel,int move)124 int level_move(struct level* plevel, int move)
125 {
126     int x, y;
127     int tx, ty;
128     int px, py;
129     int p;
130     int ok;
131 #ifdef XOR_COMPATIBILITY
132     int dx, dy;
133     int teleport;
134     int td;
135     int i;
136 #endif
137     int realmove;
138     struct move* pmove;
139 
140     realmove = move;
141 
142     if(plevel == NULL || plevel->mover_first != NULL)
143         return 0;
144 
145     if(move == MOVE_REDO)
146     {
147         if(plevel->move_current == NULL)
148             pmove = plevel->move_first;
149         else
150             pmove = plevel->move_current->next;
151 
152         if(pmove == NULL)
153             return 0;
154 
155         move = pmove->direction;
156     }
157 
158     if(move == MOVE_SWAP)
159     {
160         if(plevel->alive[1 - plevel->player])
161         {
162             plevel->player = 1 - plevel->player;
163 
164             /* Create new movers for the stationary swapped players to allow
165                the display to redraw them after the swap. */
166             mover_new(plevel, plevel->player_x[plevel->player], plevel->player_y[plevel->player], MOVE_SWAP, PIECE_PLAYER_ONE +  plevel->player, 0);
167 
168             /* Is the first player still alive? */
169             if(plevel->alive[1 - plevel->player])
170                 mover_new(plevel, plevel->player_x[1 - plevel->player], plevel->player_y[1 - plevel->player], MOVE_SWAPPED, PIECE_PLAYER_ONE + 1 - plevel->player, 0);
171 
172             level_moved(plevel, realmove);
173 
174             return 1;
175         }
176         return 0;
177     }
178 
179     if(plevel->alive[plevel->player] == 0)
180         return 0;
181 
182 #ifdef XOR_COMPATIBILITY
183     if(plevel->mode == MODE_XOR && options_xor_mode == 1)
184     {
185         if(xor_move(plevel, move))
186         {
187             level_moved(plevel, realmove);
188             xor_focus(plevel);
189             return 1;
190         }
191         return 0;
192     }
193 #endif
194 #ifdef ENIGMA_COMPATIBILITY
195     if(plevel->mode == MODE_ENIGMA && options_enigma_mode == 1)
196     {
197         if(enigma_move(plevel, move))
198         {
199             level_moved(plevel, realmove);
200             return 1;
201         }
202         return 0;
203     }
204 
205 #endif
206 
207     /* Consider where we are moving to */
208     x = plevel->player_x[plevel->player] + move_x[move];
209     y = plevel->player_y[plevel->player] + move_y[move];
210 
211     p = level_piece(plevel, x, y);
212 
213     ok = 0;
214 
215     /* Can we move into the piece in that direction? */
216     switch(p)
217     {
218         case PIECE_DOOR:
219             if(plevel->stars_caught == plevel->stars_total)
220             {
221                 plevel->flags |= LEVELFLAG_EXIT;
222                 ok = 1;
223             }
224             break;
225 
226         case PIECE_STAR:
227             plevel->stars_caught ++;
228             plevel->flags |= LEVELFLAG_STARS;
229             ok = 1;
230             break;
231 
232 #ifdef XOR_COMPATIBILITY
233         case PIECE_TELEPORT:
234             /* Only XOR has teleports. We force the issue so as not to break
235                Chroma's rotational symmetry by introducing teleport order. */
236             if(plevel->mode != MODE_XOR)
237                 break;
238 
239             teleport = -1;
240             if(x == plevel->teleport_x[0] && y == plevel->teleport_y[0])
241                 teleport = 0;
242             if(x == plevel->teleport_x[1] && y == plevel->teleport_y[1])
243                 teleport = 1;
244             if(teleport != -1)
245             {
246                 tx = plevel->teleport_x[1 - teleport];
247                 ty = plevel->teleport_y[1 - teleport];
248                 td = move;
249 
250                 /* Does the other teleport still exist? */
251                 if(level_piece(plevel, tx, ty) == PIECE_TELEPORT)
252                 {
253                     ok = 0;
254                     /* Find the first available exit from it */
255                     for(i = 0; i < 4; i ++)
256                     {
257                         dx = tx + move_x[xor_teleport_order[i]];
258                         dy = ty + move_y[xor_teleport_order[i]];
259                         if(!ok && level_piece(plevel, dx, dy) == PIECE_SPACE)
260                         {
261                             /* Change move to produce the effect of coming
262                                out of the teleport */
263                             x = dx; y = dy; move = xor_teleport_order[i];
264                             ok = 1;
265                         }
266                     }
267 
268                     if(ok)
269                     {
270                         /* Visual effects for the player going in one teleport */
271                         /* Store original player move direction in cosmetic mover */
272                         mover_new(plevel, plevel->teleport_x[teleport], plevel->teleport_y[teleport], td, PIECE_TELEPORT, 0);
273                         level_setprevious(plevel, plevel->teleport_x[teleport], plevel->teleport_y[teleport], PIECE_PLAYER_ONE + plevel->player);
274                         level_setpreviousmoving(plevel, plevel->teleport_x[teleport], plevel->teleport_y[teleport], realmove);
275                         /* and out of the other teleport */
276                         mover_new(plevel, plevel->teleport_x[1 - teleport], plevel->teleport_y[1 - teleport], MOVE_NONE, PIECE_TELEPORT, 0);
277 
278                         /* Change the viewpoint to that of the other teleport */
279                         plevel->view_x[plevel->player] = plevel->view_teleport_x[1 - teleport];
280                         plevel->view_y[plevel->player] = plevel->view_teleport_y[1 - teleport];
281 
282                     }
283                 }
284             }
285             break;
286 
287         case PIECE_SWITCH:
288             plevel->switched = 1 - plevel->switched;
289             plevel->flags |= LEVELFLAG_SWITCH;
290             ok = 1;
291             break;
292 
293         case PIECE_MAP_TOP_LEFT:
294             plevel->mapped |= MAPPED_TOP_LEFT;
295             plevel->flags |= LEVELFLAG_MAP;
296             ok = 1;
297             break;
298         case PIECE_MAP_TOP_RIGHT:
299             plevel->mapped |= MAPPED_TOP_RIGHT;
300             plevel->flags |= LEVELFLAG_MAP;
301             ok = 1;
302             break;
303         case PIECE_MAP_BOTTOM_LEFT:
304             plevel->mapped |= MAPPED_BOTTOM_LEFT;
305             plevel->flags |= LEVELFLAG_MAP;
306             ok = 1;
307             break;
308         case PIECE_MAP_BOTTOM_RIGHT:
309             plevel->mapped |= MAPPED_BOTTOM_RIGHT;
310             plevel->flags |= LEVELFLAG_MAP;
311             ok = 1;
312             break;
313 
314         case PIECE_DOTS_X:
315             if(move == MOVE_LEFT || move == MOVE_RIGHT)
316                 ok = 1;
317             break;
318 
319         case PIECE_DOTS_Y:
320             if(move == MOVE_UP || move == MOVE_DOWN)
321                 ok = 1;
322             break;
323 #endif
324 
325 #ifdef ENIGMA_COMPATIBILITY
326         case PIECE_DOTS_DOUBLE:
327 #endif
328         case PIECE_DOTS:
329         case PIECE_SPACE:
330             ok = 1;
331             break;
332     }
333 
334     /* Is there a piece we can push? */
335     if(!ok)
336     {
337         tx = x + move_x[move];
338         ty = y + move_y[move];
339 
340         if(canbepushed(p, level_piece(plevel, tx, ty), move, plevel->mode))
341         {
342             mover_new(plevel, tx, ty, move, p, 0);
343             ok = 1;
344         }
345     }
346 
347     if(ok)
348     {
349 
350         /* Cosmetic mover for storing the player's direction in undo */
351         mover_new(plevel, plevel->player_x[plevel->player], plevel->player_y[plevel->player], move, PIECE_GONE, 0);
352 
353         mover_new(plevel, x, y, move, PIECE_PLAYER_ONE + plevel->player, 0);
354 
355         px = plevel->player_x[plevel->player];
356         py = plevel->player_y[plevel->player];
357 
358 #ifdef XOR_COMPATIBILITY
359         /* XOR protects the players move */
360         if(plevel->mode == MODE_XOR)
361         {
362             /* Blank the player's space first to avoid upsetting undo */
363             level_setpiece(plevel, px, py, PIECE_SPACE);
364             mover_new(plevel, px, py, (move + 1) % 4, PIECE_SPACE, 1);
365         }
366         /* Chroma lets a piece follow in the player's trail */
367         else
368         {
369 #endif
370             /* Blank the player's space first to avoid upsetting undo */
371             level_setpiece(plevel, px, py, PIECE_SPACE);
372             mover_consider(plevel, px, py, move % 4);
373 #ifdef XOR_COMPATIBILITY
374         }
375 #endif
376 
377         plevel->player_x[plevel->player] = x;
378         plevel->player_y[plevel->player] = y;
379 
380         level_moved(plevel, realmove);
381 
382 #ifdef XOR_COMPATIBILITY
383         if(plevel->mode == MODE_XOR)
384             xor_focus(plevel);
385 #endif
386 
387         return 1;
388     }
389 
390     return 0;
391 }
392 
mover_explode(struct level * plevel,int x,int y,int d,int p)393 struct mover* mover_explode(struct level *plevel, int x, int y, int d, int p)
394 {
395     /* Don't explode any of the edge wall */
396     if(x == 0 || y == 0 || x == plevel->size_x - 1 || y == plevel->size_y - 1)
397         return NULL;
398 
399     /* What have we exploded? */
400     switch(level_piece(plevel, x, y))
401     {
402         case PIECE_STAR:
403             plevel->stars_exploded ++;
404             plevel->flags |= LEVELFLAG_STARS;
405             break;
406 
407 #ifdef XOR_COMPATIBILITY
408         case PIECE_SWITCH:
409             plevel->switched = 1 - plevel->switched;
410             plevel->flags |= LEVELFLAG_SWITCH;
411             break;
412 #endif
413     }
414 
415     return mover_new(plevel, x, y, d, p, 1);
416 }
417 
mover_new(struct level * plevel,int x,int y,int d,int piece,int fast)418 struct mover* mover_new(struct level* plevel, int x, int y, int d, int piece, int fast)
419 {
420     struct mover* pmover;
421     int previous;
422     int data;
423 
424     /* Don't allow two movers in the same space, unless one is exploding */
425     if(!isnewexplosion(piece) && level_moving(plevel, x, y) != MOVE_NONE)
426         return NULL;
427 
428     pmover = (struct mover*)malloc(sizeof(struct mover));
429     if(pmover == NULL)
430         fatal("Out of memory in mover_new()");
431 
432     previous = level_piece(plevel, x, y);
433 
434     pmover->x = x;
435     pmover->y = y;
436     pmover->direction = d;
437     pmover->piece = piece;
438     pmover->piece_previous = previous;
439     pmover->fast = fast;
440     pmover->next = NULL;
441     pmover->previous = plevel->mover_last;
442 
443     if(plevel->mover_first == NULL)
444         plevel->mover_first = pmover;
445 
446     if(plevel->mover_last != NULL)
447         plevel->mover_last->next = pmover;
448 
449     plevel->mover_last = pmover;
450 
451     /* Show pieces collected by players */
452     if(piece == PIECE_PLAYER_ONE || piece == PIECE_PLAYER_TWO)
453     {
454         if((previous < PIECE_MOVERS_FIRST || previous > PIECE_MOVERS_LAST)
455                 && previous != PIECE_CIRCLE
456 #ifdef ENIGMA_COMPATIBILITY
457                 && previous != PIECE_CIRCLE_DOUBLE
458 #endif
459                 && previous != PIECE_PLAYER_ONE
460                 && previous != PIECE_PLAYER_TWO)
461             level_setprevious(plevel, x, y, previous);
462     }
463 
464     /* Show players squashed by movers */
465     if(piece >= PIECE_MOVERS_FIRST && piece <= PIECE_MOVERS_LAST)
466     {
467         if(previous == PIECE_PLAYER_ONE || previous == PIECE_PLAYER_TWO)
468            level_setprevious(plevel, x, y, previous);
469     }
470 
471     /* Show pieces removed by movers or explosions */
472     if(previous == PIECE_DOTS
473 #ifdef ENIGMA_COMPATIBILITY
474             || previous == PIECE_DOTS_DOUBLE
475 #endif
476 #ifdef XOR_COMPATIBILITY
477             || previous == PIECE_DOTS_X
478             || previous == PIECE_DOTS_Y
479 #endif
480             || isexplosion(previous))
481         level_setprevious(plevel, x, y, previous);
482 
483     /* Show exploded pieces */
484     if(isnewexplosion(piece) && !isnewexplosion(previous))
485     {
486         level_setprevious(plevel, x, y, previous);
487         level_setpreviousmoving(plevel, x, y, level_moving(plevel, x, y));
488     }
489 
490     /* Explosions occur later */
491     if(!isnewexplosion(piece) && piece != PIECE_GONE)
492     {
493         level_setpiece(plevel, x, y, piece);
494         level_setmoving(plevel, x, y, d);
495     }
496 
497     /* Maintain piece graphic */
498     if(d != MOVE_NONE)
499     {
500         data = level_data(plevel, x - move_x[d], y - move_y[d]) & 0xff00;
501         data = (level_data(plevel, x, y) & ~0xff00) | data;
502         level_setdata(plevel, x, y, data);
503     }
504 
505     return pmover;
506 }
507 
mover_addtostack(struct level * plevel,int x,int y,int move)508 struct mover* mover_addtostack(struct level* plevel, int x, int y, int move)
509 {
510     struct mover* pmover;
511 
512     pmover = (struct mover*)malloc(sizeof(struct mover));
513     if(pmover == NULL)
514         fatal("Out of memory in mover_addtostack()");
515 
516     pmover->x = x;
517     pmover->y = y;
518     pmover->direction = move;
519     pmover->piece = PIECE_SPACE;
520     pmover->fast = 0;
521     pmover->next = NULL;
522 
523     if(plevel->stack_first == NULL)
524         plevel->stack_first = pmover;
525 
526     if(plevel->stack_last != NULL)
527         plevel->stack_last->next = pmover;
528 
529     plevel->stack_last = pmover;
530 
531     return pmover;
532 }
533 
level_storemovers(struct level * plevel)534 void level_storemovers(struct level* plevel)
535 {
536     struct mover* pmover;
537     int previous;
538 
539     int count = 0;
540 
541     if(plevel->move_current == NULL || plevel->mover_first == NULL)
542         return;
543 
544     if((options_debug & DEBUG_MOVERS) && plevel->move_current->mover_first == NULL)
545         fprintf(stderr, "\n");
546 
547     pmover = plevel->mover_first;
548     while(pmover != NULL)
549     {
550         /* If something is moving into an explosion, don't store it as the
551            previous piece for this space; it will have its own mover, and thus
552            will be stored elsewhere. */
553         previous = pmover->piece_previous;
554         if(isexplosion(pmover->piece) && level_previousmoving(plevel, pmover->x, pmover->y) != MOVE_NONE)
555             previous = PIECE_SPACE;
556 
557         mover_newundo(plevel, pmover->x, pmover->y,
558                 pmover->direction, pmover->piece, previous,
559                 MOVER_STORE | (pmover->next == NULL ? 0 : MOVER_FAST));
560 
561         if(options_debug & DEBUG_MOVERS)
562             fprintf(stderr, "[%d] Storing undo mover at (%d,%d) is %s was %s (direction=%c) (flags=%d)\n",
563                 count ++, pmover->x, pmover->y,
564                 piece_name[pmover->piece], piece_name[previous],
565                 directiontochar(pmover->direction),
566                 (pmover->next == NULL ? 0 : MOVER_FAST));
567 
568         pmover = pmover->next;
569     }
570 }
571 
level_evolve(struct level * plevel)572 int level_evolve(struct level* plevel)
573 {
574     struct mover* poldmovers;
575     struct mover* pmover;
576     int x, y;
577     int i;
578     int d;
579     int ad;
580     int ed;
581     int ax, ay;
582     int bp, bd;
583     int filled;
584 
585 #ifdef XOR_COMPATIBILITY
586     if(plevel->mode == MODE_XOR && options_xor_mode == 1)
587     {
588         return xor_evolve(plevel);
589     }
590 #endif
591 #ifdef ENIGMA_COMPATIBILITY
592     if(plevel->mode == MODE_ENIGMA && options_enigma_mode == 1)
593     {
594         return enigma_evolve(plevel);
595     }
596 #endif
597 
598     poldmovers = plevel->mover_first;
599 
600     plevel->mover_first = NULL;
601     plevel->mover_last = NULL;
602 
603     /* Chroma's engine isn't perfect. Pieces that appear to be in continuous
604        motion are actually momentarily stationary at the start of every cycle.
605        In pathological cases, this can give rise to some counterintuitive
606        situations, where the outcome depends on the order of the movers.
607 
608        See levels/regression/chroma-regression.chroma for some examples.
609     */
610 
611     pmover = poldmovers;
612     while(pmover != NULL)
613     {
614         level_setmoving(plevel, pmover->x, pmover->y, MOVE_NONE);
615         level_setprevious(plevel, pmover->x, pmover->y, PIECE_SPACE);
616         level_setpreviousmoving(plevel, pmover->x, pmover->y, MOVE_NONE);
617         level_setdetonator(plevel, pmover->x, pmover->y, PIECE_SPACE);
618         level_setdetonatormoving(plevel, pmover->x, pmover->y, MOVE_NONE);
619         pmover = pmover->next;
620     }
621 
622     pmover = poldmovers;
623     while(pmover != NULL)
624     {
625 
626         /* Remove the mover if something has already moved into its space */
627         if(level_moving(plevel, pmover->x, pmover->y) != MOVE_NONE
628                 /* or it isn't what it should be */
629                 || level_piece(plevel, pmover->x, pmover->y) != pmover->piece
630           )
631             pmover->piece = PIECE_GONE;
632 
633         switch(pmover->piece)
634         {
635             case PIECE_SPACE:
636             case PIECE_EXPLOSION_RED_LEFT:
637             case PIECE_EXPLOSION_RED_HORIZONTAL:
638             case PIECE_EXPLOSION_RED_RIGHT:
639             case PIECE_EXPLOSION_RED_TOP:
640             case PIECE_EXPLOSION_RED_VERTICAL:
641             case PIECE_EXPLOSION_RED_BOTTOM:
642             case PIECE_EXPLOSION_GREEN_LEFT:
643             case PIECE_EXPLOSION_GREEN_HORIZONTAL:
644             case PIECE_EXPLOSION_GREEN_RIGHT:
645             case PIECE_EXPLOSION_GREEN_TOP:
646             case PIECE_EXPLOSION_GREEN_VERTICAL:
647             case PIECE_EXPLOSION_GREEN_BOTTOM:
648             case PIECE_EXPLOSION_BLUE_LEFT:
649             case PIECE_EXPLOSION_BLUE_HORIZONTAL:
650             case PIECE_EXPLOSION_BLUE_RIGHT:
651             case PIECE_EXPLOSION_BLUE_TOP:
652             case PIECE_EXPLOSION_BLUE_VERTICAL:
653             case PIECE_EXPLOSION_BLUE_BOTTOM:
654                 i = 0;
655                 filled = 0;
656 
657                 /* Consider the pieces around the space */
658                 for(i = 0; i < 4; i ++)
659                 {
660                     if(filled)
661                         continue;
662 
663 #ifdef ENIGMA_COMPATIBILITY
664                     /* Enigma has a fixed move order */
665                     if(plevel->mode == MODE_ENIGMA)
666                         d = enigma_move_order[i];
667                     else
668 #endif
669                         /* Chroma and XOR depend on how the space was emptied */
670                         d = (pmover->direction + i) % 4;
671 
672                     ad = (d + 2) % 4;
673                     ax = pmover->x + move_x[ad];
674                     ay = pmover->y + move_y[ad];
675 
676                     /* Can the piece move into the space? */
677                     if(canfall(level_piece(plevel, ax, ay), PIECE_SPACE, d)
678                             /* and that piece isn't already moving */
679                             && level_moving(plevel, ax, ay) == MOVE_NONE
680                       )
681                     {
682                         x = pmover->x + move_x[d];
683                         y = pmover->y + move_y[d];
684 
685                         /* Can the piece from the opposite direction also
686                            move into this space? */
687                         if(canfall(level_piece(plevel, x, y), PIECE_SPACE, ad)
688                                 /* and that piece isn't already moving */
689                                 && level_moving(plevel, x, y) == MOVE_NONE
690                                 /* If so, can the two explode? */
691                                 && canexplode(level_piece(plevel, ax, ay), level_piece(plevel, x, y), d, 1, plevel->mode)
692                                 /* (but not for XOR and Enigma) */
693                                 && plevel->mode == MODE_CHROMA
694                           )
695                         {
696                             /* If so, detonate them in the middle */
697                             if((level_piece(plevel, x, y) & 4) == 4)
698                             {
699                                 /* The first piece is the bomb */
700                                 bp = level_piece(plevel, x, y);
701                                 bd = ad;
702                                 ed = level_piece(plevel, x, y) & 3;
703 
704                                 level_setdetonator(plevel, pmover->x, pmover->y, level_piece(plevel, ax, ay));
705                                 level_setdetonatormoving(plevel, pmover->x, pmover->y, d);
706                             }
707                             else
708                             {
709                                 /* The second piece is the bomb */
710                                 bp = level_piece(plevel, ax, ay);
711                                 bd = d;
712                                 ed = level_piece(plevel, ax, ay) & 3;
713 
714                                 level_setdetonator(plevel, pmover->x, pmover->y, level_piece(plevel, x, y));
715                                 level_setdetonatormoving(plevel, pmover->x, pmover->y, ad);
716                             }
717 
718                             /* and consider anything following them */
719                             mover_consider(plevel, x, y, ad);
720                             mover_consider(plevel, ax, ay, d);
721 
722                             /* Move the bomb into the space */
723                             level_setpiece(plevel, pmover->x, pmover->y, bp);
724                             level_setmoving(plevel, pmover->x, pmover->y, bd);
725 
726                             /* and explode it */
727                             mover_explode(plevel, pmover->x, pmover->y, ed, explosiontype(bp));
728 
729                             /* Create the central explosion now, to prevent the
730                                piece there being processed as a later mover. */
731                             level_setpiece(plevel, pmover->x, pmover->y, explosiontype(bp));
732 
733                             explode_sides(plevel, pmover->x, pmover->y, bp, ed);
734 
735                             filled = 1;
736                             break;
737                         }
738 
739                         /* Otherwise, keep the piece moving */
740                         mover_new(plevel, pmover->x, pmover->y, d, level_piece(plevel, ax, ay), 1);
741                         /* and see if anything is following in its trail */
742                         mover_consider(plevel, ax, ay, d);
743 
744                         filled = 1;
745                         break;
746                     }
747                 }
748 
749                 /* If the explosion has not been filled */
750                 if(isexplosion(pmover->piece) && filled == 0
751                         /* and nothing else is moving into it */
752                         && level_moving(plevel, pmover->x, pmover->y) == MOVE_NONE
753                   )
754                     /* then turn it into a space */
755                     mover_new(plevel, pmover->x, pmover->y, pmover->direction, PIECE_SPACE, 0);
756 
757                 break;
758 
759             case PIECE_PLAYER_ONE:
760             case PIECE_PLAYER_TWO:
761             case PIECE_GONE:
762                 /* These 'movers' are purely for cosmetic purposes */
763                 break;
764 
765 #ifdef XOR_COMPATIBILITY
766             case PIECE_TELEPORT:
767                 /* These 'movers' are purely for cosmetic purposes */
768                 break;
769 #endif
770 
771             default:
772                 /* A pushed arrow still falls in its natural direction */
773                 if(pmover->fast == 0 && pmover->piece >= PIECE_MOVERS_FIRST && pmover->piece <= PIECE_MOVERS_LAST)
774                     pmover->direction = pmover->piece % 4;
775 
776                 /* Consider the space in front of the mover */
777                 x = pmover->x + move_x[pmover->direction];
778                 y = pmover->y + move_y[pmover->direction];
779 
780                 /* Can the mover move into the space in front of it? */
781                 if(canmove(pmover->piece, level_piece(plevel, x, y), pmover->direction, pmover->fast)
782                         /* and that space doesn't already have something
783                            moving into it */
784                         && (level_moving(plevel, x, y) == MOVE_NONE)
785                   )
786                 {
787                     /* If so, keep it moving */
788                     mover_new(plevel, x, y, pmover->direction, pmover->piece, 1);
789                     /* and see if anything is following in its trail */
790                     mover_consider(plevel, pmover->x, pmover->y, pmover->direction);
791                     break;
792                 }
793 
794                 /* Can the mover explode the piece in front of it? */
795                 if(canexplode(pmover->piece, level_piece(plevel, x, y), pmover->direction, pmover->fast, plevel->mode)
796                         /* and the piece in front isn't moving */
797                         && (level_moving(plevel, x, y) == MOVE_NONE
798                         /* or it is moving towards us */
799                         || (level_moving(plevel, x, y) == ((pmover->direction + 2) % 4)
800                             /* (but not for XOR or Enigma) */
801                             && plevel->mode==MODE_CHROMA))
802                   )
803                 {
804                     bp = level_piece(plevel, x, y);
805                     level_setdetonator(plevel, x, y, pmover->piece);
806                     level_setdetonatormoving(plevel, x, y, pmover->direction);
807 
808                     /* Explosion direction is bomb fall direction */
809                     if(bp & 4)
810                         ed = bp & 3;
811                     else
812                         ed = pmover->piece & 3;
813 
814                     mover_explode(plevel, x, y, ed, explosiontype(bp));
815 
816                     /* Create the central explosion now, to prevent the piece
817                        there being processed as a later mover. */
818                     level_setpiece(plevel, x, y, explosiontype(bp));
819 
820                     mover_consider(plevel, pmover->x, pmover->y, pmover->direction);
821 
822                     explode_sides(plevel, x, y, bp, ed);
823 
824                     break;
825                 }
826         }
827 
828         pmover = pmover->next;
829     }
830 
831     /* Create the side explosions at the end, rather than during the previous
832        loop. This allows multiple explosions to occur in parallel. Centre
833        explosions will have already been created earlier on. */
834     pmover = plevel->mover_first;
835     while(pmover != NULL)
836     {
837         if(isnewexplosion(pmover->piece))
838         {
839             if(!isnewexplosion(level_piece(plevel, pmover->x, pmover->y)))
840             {
841                 level_setprevious(plevel, pmover->x, pmover->y, level_piece(plevel, pmover->x, pmover->y));
842                 level_setpreviousmoving(plevel, pmover->x, pmover->y, level_moving(plevel, pmover->x, pmover->y));
843             }
844 
845             /* Use PIECE_EXPLOSION_NEW to allow detection of overlapping
846                explosions further down. */
847             level_setpiece(plevel, pmover->x, pmover->y, pmover->piece);
848             level_setmoving(plevel, pmover->x, pmover->y, pmover->direction);
849 
850             pmover->piece += PIECE_EXPLOSION_FIRST - PIECE_EXPLOSION_NEW_FIRST;
851         }
852 
853         pmover = pmover->next;
854     }
855 
856     pmover = plevel->mover_first;
857     while(pmover != NULL)
858     {
859         if(isexplosion(pmover->piece))
860         {
861             /* Remove any explosions that overlap other explosions */
862             if(isexplosion(level_piece(plevel, pmover->x, pmover->y)))
863                 pmover->piece = PIECE_GONE;
864             /* Otherwise, convert new explosions into explosions proper */
865             else
866                 level_setpiece(plevel, pmover->x, pmover->y, pmover->piece);
867         }
868         /* Remove any movers that have exploded, or aren't as they should be */
869         if(level_piece(plevel, pmover->x, pmover->y) != pmover->piece)
870         {
871             pmover->piece = PIECE_GONE;
872         }
873         pmover = pmover->next;
874     }
875 
876     /* Is player one still alive? */
877     if(level_piece(plevel, plevel->player_x[0], plevel->player_y[0]) != PIECE_PLAYER_ONE)
878     {
879         plevel->flags |= LEVELFLAG_MOVES;
880            plevel->alive[0] = 0;
881     }
882 
883     /* Is player two still alive? */
884     if(level_piece(plevel, plevel->player_x[1], plevel->player_y[1]) != PIECE_PLAYER_TWO)
885     {
886         plevel->flags |= LEVELFLAG_MOVES;
887         plevel->alive[1] = 0;
888     }
889 
890     /* Free old movers */
891     while(poldmovers != NULL)
892     {
893         pmover = poldmovers;
894         poldmovers = poldmovers->next;
895         free(pmover);
896     }
897 
898     return 0;
899 }
900 
mover_consider(struct level * plevel,int x,int y,int d)901 void mover_consider(struct level* plevel, int x, int y, int d)
902 {
903     int tx, ty;
904     int ad;
905 
906     /* Is there already a mover in this space? If so, don't allow another */
907     if(level_moving(plevel, x, y) != MOVE_NONE)
908         return;
909 
910 #ifdef ENIGMA_COMPATIBILITY
911     /* Enigma doesn't consider the direction in which a space was emptied */
912     if(plevel->mode == MODE_ENIGMA)
913     {
914         mover_new(plevel, x, y, d, PIECE_SPACE, 1);
915         return;
916     }
917 #endif
918 
919 #ifdef XOR_COMPATIBILITY
920     if(plevel->mode == MODE_XOR)
921     {
922         mover_new(plevel, x, y, d, PIECE_SPACE, 1);
923         return;
924     }
925 #endif
926 
927     ad = (d + 2) % 4;
928     tx = x + move_x[ad];
929     ty = y + move_y[ad];
930 
931     /* Can a piece follow in the trail of this one? */
932     if(canfall(level_piece(plevel, tx, ty), PIECE_SPACE, d))
933     {
934         /* If it's moving already, just clear this space (1.07) */
935         if(level_moving(plevel, tx, ty) != MOVE_NONE)
936         {
937             mover_new(plevel, x, y, MOVE_NONE, PIECE_SPACE, 0);
938             return;
939         }
940 
941         /* Otherwise, set it moving */
942         mover_new(plevel, x, y, d, level_piece(plevel, tx, ty), 1);
943         /* and see if there's anything following in its trail */
944         mover_consider(plevel, tx, ty, d);
945         return;
946     }
947 
948     mover_new(plevel, x, y, d, PIECE_SPACE, 1);
949 }
950 
explode_sides(struct level * plevel,int x,int y,int p,int d)951 void explode_sides(struct level* plevel, int x, int y, int p, int d)
952 {
953     /* Chroma is subtle. This may be too subtle to have any effect in practice,
954        but the principle elsewhere is that things should be rotationally
955        symmetric, and this carries through here. */
956     if(plevel->mode == MODE_CHROMA)
957     {
958         switch(p % 4)
959         {
960             case 0: /* left */
961                 mover_explode(plevel, x, y - 1, d, explosiontype(p) - 1);
962                 mover_explode(plevel, x, y + 1, d, explosiontype(p) + 1);
963                 break;
964 
965             case 1: /* up */
966                 mover_explode(plevel, x + 1, y, d, explosiontype(p) + 1);
967                 mover_explode(plevel, x - 1, y, d, explosiontype(p) - 1);
968                 break;
969 
970             case 2: /* right */
971                 mover_explode(plevel, x, y + 1, d, explosiontype(p) + 1);
972                 mover_explode(plevel, x, y - 1, d, explosiontype(p) - 1);
973                 break;
974 
975             case 3: /* down */
976                 mover_explode(plevel, x - 1, y, d, explosiontype(p) - 1);
977                 mover_explode(plevel, x + 1, y, d, explosiontype(p) + 1);
978                 break;
979         }
980     }
981     else
982     {
983         switch(p % 2)
984         {
985             case 0: /* left / right */
986                 mover_explode(plevel, x, y - 1, d, explosiontype(p) - 1);
987                 mover_explode(plevel, x, y + 1, d, explosiontype(p) + 1);
988                 break;
989             case 1: /* up /down */
990                 mover_explode(plevel, x - 1, y, d, explosiontype(p) - 1);
991                 mover_explode(plevel, x + 1, y, d, explosiontype(p) + 1);
992                 break;
993         }
994     }
995 }
996 
997 
canfall(int p,int into,int d)998 int canfall(int p, int into, int d)
999 {
1000     /* Determine whether a piece can start moving */
1001 
1002     /* Arrows and bombs */
1003     if(p >= PIECE_MOVERS_FIRST && p<= PIECE_MOVERS_LAST)
1004     {
1005         /* can start falling in their natural direction */
1006         if(d == (p % 4))
1007         {
1008             /* but only into empty space */
1009             if(into == PIECE_SPACE)
1010                 return 1;
1011 #ifdef XOR_COMPATIBILITY
1012             /* or into directional dots if appropriate */
1013             if(into == PIECE_DOTS_X && (d == MOVE_LEFT || d == MOVE_RIGHT ))
1014                 return 1;
1015             if(into == PIECE_DOTS_Y && (d == MOVE_UP || d == MOVE_DOWN ))
1016                 return 1;
1017 #endif
1018         }
1019     }
1020 
1021     return 0;
1022 }
1023 
1024 
canmove(int p,int into,int d,int fast)1025 int canmove(int p, int into, int d, int fast)
1026 {
1027     /* Determine whether a piece can continue moving */
1028 
1029     /* Arrows and bombs */
1030     if(p >= PIECE_MOVERS_FIRST && p<= PIECE_MOVERS_LAST)
1031     {
1032         /* can continue moving in their natural direction */
1033         if(d == (p % 4))
1034         {
1035             /* into empty space */
1036             if(into == PIECE_SPACE)
1037                 return 1;
1038             /* into dots if they're already moving */
1039             if(into == PIECE_DOTS && fast)
1040                 return 1;
1041 #ifdef XOR_COMPATIBILITY
1042             /* into directional dots if appropriate */
1043             if(into == PIECE_DOTS_X && (d == MOVE_LEFT || d == MOVE_RIGHT ))
1044                 return 1;
1045             if(into == PIECE_DOTS_Y && (d == MOVE_UP || d == MOVE_DOWN ))
1046                 return 1;
1047 #endif
1048             /* through dying explosions */
1049             if(isexplosion(into))
1050                 return 1;
1051             /* can kill players if already moving */
1052             if(into == PIECE_PLAYER_ONE && fast)
1053                 return 1;
1054             if(into == PIECE_PLAYER_TWO && fast)
1055                 return 1;
1056         }
1057         return 0;
1058     }
1059 
1060     /* Circles */
1061     if(p == PIECE_CIRCLE)
1062     {
1063         /* are stopped by everything other than empty space */
1064         if(into == PIECE_SPACE)
1065             return 1;
1066         /* and dying explosions */
1067         if(isexplosion(into))
1068             return 1;
1069         return 0;
1070     }
1071 
1072   return 0;
1073 }
1074 
canbepushed(int p,int into,int d,int mode)1075 int canbepushed(int p, int into, int d, int mode)
1076 {
1077     /* Determine whether a piece can be pushed by the player */
1078 
1079     /* Arrows and bombs */
1080     if(p >= PIECE_MOVERS_FIRST && p<= PIECE_MOVERS_LAST)
1081     {
1082         /* can be pushed, but not against their natural direction */
1083         if(d != ((p + 2) % 4))
1084         {
1085             /* into empty space or through dots */
1086             if(into == PIECE_SPACE || into == PIECE_DOTS)
1087                 return 1;
1088 #ifdef XOR_COMPATIBILITY
1089             /* through directional dots if appropriate */
1090             if(into == PIECE_DOTS_X && (d == MOVE_LEFT || d == MOVE_RIGHT))
1091                 return 1;
1092             if(into == PIECE_DOTS_Y && (d == MOVE_UP || d == MOVE_DOWN))
1093                 return 1;
1094 #endif
1095         }
1096         return 0;
1097     }
1098 
1099     /* Circles can be pushed in any direction */
1100     if(p == PIECE_CIRCLE
1101 #ifdef ENIGMA_COMPATIBILITY
1102             || p == PIECE_CIRCLE_DOUBLE
1103 #endif
1104       )
1105     {
1106         /* into empty space */
1107         if(into == PIECE_SPACE)
1108             return 1;
1109 #ifdef XOR_COMPATIBILITY
1110         /* XOR won't let circles (dolls) pass through dots */
1111         if(mode == MODE_XOR)
1112             return 0;
1113 #endif
1114         /* pushed through dots */
1115         if(into == PIECE_DOTS)
1116             return 1;
1117         return 0;
1118     }
1119 
1120   return 0;
1121 }
1122 
canexplode(int p,int i,int d,int fast,int mode)1123 int canexplode(int p, int i, int d, int fast, int mode)
1124 {
1125     /* Only an already moving arrow or bomb can act as a detonator */
1126     if(fast == 0)
1127         return 0;
1128 
1129     /* Arrows can detonate bombs */
1130     if(p >= PIECE_ARROW_RED_LEFT && p<= PIECE_ARROW_RED_DOWN &&
1131        i >= PIECE_BOMB_RED_LEFT && i<= PIECE_BOMB_RED_DOWN)
1132         return 1;
1133     if(p >= PIECE_ARROW_GREEN_LEFT && p<= PIECE_ARROW_GREEN_DOWN &&
1134        i >= PIECE_BOMB_GREEN_LEFT && i<= PIECE_BOMB_GREEN_DOWN)
1135         return 1;
1136     if(p >= PIECE_ARROW_BLUE_LEFT && p<= PIECE_ARROW_BLUE_DOWN &&
1137        i >= PIECE_BOMB_BLUE_LEFT && i<= PIECE_BOMB_BLUE_DOWN)
1138         return 1;
1139 
1140 #ifdef ENIGMA_COMPATIBILITY
1141     /* Enigma requires a moving arrow to detonate a stationary bomb, and
1142        does not permit bombs to detonate other bombs */
1143     if(mode == MODE_ENIGMA)
1144         return 0;
1145 #endif
1146 
1147     /* Bombs can be detonated by arrows pointing towards them */
1148     if(p >= PIECE_BOMB_RED_LEFT && p<= PIECE_BOMB_RED_DOWN &&
1149        i == (PIECE_ARROW_RED_LEFT + ((d + 2) % 4)))
1150         return 1;
1151     if(p >= PIECE_BOMB_GREEN_LEFT && p<= PIECE_BOMB_GREEN_DOWN &&
1152        i == (PIECE_ARROW_GREEN_LEFT + ((d + 2) % 4)))
1153         return 1;
1154     if(p >= PIECE_BOMB_BLUE_LEFT && p<= PIECE_BOMB_BLUE_DOWN &&
1155        i == (PIECE_ARROW_BLUE_LEFT + ((d + 2) % 4)))
1156         return 1;
1157 
1158     /* Bombs can detonate other bombs */
1159     if(p >= PIECE_BOMB_RED_LEFT && p<= PIECE_BOMB_RED_DOWN &&
1160        i >= PIECE_BOMB_RED_LEFT && i<= PIECE_BOMB_RED_DOWN)
1161         return 1;
1162     if(p >= PIECE_BOMB_GREEN_LEFT && p<= PIECE_BOMB_GREEN_DOWN &&
1163        i >= PIECE_BOMB_GREEN_LEFT && i<= PIECE_BOMB_GREEN_DOWN)
1164         return 1;
1165     if(p >= PIECE_BOMB_BLUE_LEFT && p<= PIECE_BOMB_BLUE_DOWN &&
1166        i >= PIECE_BOMB_BLUE_LEFT && i<= PIECE_BOMB_BLUE_DOWN)
1167         return 1;
1168 
1169     return 0;
1170 }
1171 
mover_newundo(struct level * plevel,int x,int y,int d,int piece,int previous,int flags)1172 struct mover* mover_newundo(struct level* plevel, int x, int y, int d, int piece, int previous, int flags)
1173 {
1174     struct mover* pmover;
1175 
1176     static int count = 0;
1177 
1178     if(plevel->flags & LEVELFLAG_NOUNDO)
1179         return NULL;
1180 
1181     pmover = (struct mover*)malloc(sizeof(struct mover));
1182     if(pmover == NULL)
1183         fatal("Out of memory in mover_newundo()");
1184 
1185     pmover->x = x;
1186     pmover->y = y;
1187     pmover->direction = d;
1188     pmover->piece = piece;
1189     pmover->piece_previous = previous;
1190     pmover->next = NULL;
1191     pmover->previous = plevel->mover_last;
1192 
1193     if(flags & MOVER_FAST)
1194         pmover->fast = 1;
1195     else
1196         pmover->fast = 0;
1197 
1198     if(flags & MOVER_UNDO)
1199     {
1200         level_setmoving(plevel, pmover->x, pmover->y, pmover->direction);
1201 
1202         if(options_debug & DEBUG_MOVERS)
1203         {
1204             if(plevel->mover_first == NULL)
1205                 count = 0;
1206 
1207             fprintf(stderr, "[%d] Cosmetic mover at (%d,%d) is %s was %s (direction=%c) (flags=%d)\n",
1208                       count ++, pmover->x, pmover->y,
1209                     piece_name[pmover->piece], piece_name[pmover->piece_previous],
1210                     directiontochar(pmover->direction), pmover->fast);
1211         }
1212 
1213         if(plevel->mover_first == NULL)
1214             plevel->mover_first = pmover;
1215 
1216         if(plevel->mover_last != NULL)
1217             plevel->mover_last->next = pmover;
1218 
1219         plevel->mover_last = pmover;
1220 
1221     }
1222 
1223     if(flags & MOVER_STORE)
1224     {
1225         pmover->previous = plevel->move_current->mover_last;
1226         pmover->next = NULL;
1227 
1228         if(plevel->move_current->mover_first == NULL)
1229             plevel->move_current->mover_first = pmover;
1230         if(plevel->move_current->mover_last != NULL)
1231             plevel->move_current->mover_last->next = pmover;
1232         plevel->move_current->mover_last = pmover;
1233 
1234     }
1235 
1236     return pmover;
1237 }
1238 
1239 
level_undo(struct level * plevel)1240 int level_undo(struct level* plevel)
1241 {
1242     struct mover* pmover;
1243     struct mover* ptmp;
1244     struct mover* pmoverfirst;
1245 
1246     int d, td;
1247 
1248     int count = 0;
1249 
1250     /* Can't undo if the level has no undo data (eg, a partial save) */
1251     if(plevel->move_first == NULL || (plevel->move_first->mover_first == NULL && plevel->move_current != plevel->move_first))
1252         return 0;
1253 
1254     /* Working backwards, undo any changes made to the map by movers in the
1255        previous step. */
1256     pmoverfirst = NULL;
1257     pmover = plevel->mover_first;
1258     while(pmover != NULL)
1259     {
1260         pmoverfirst = pmover;
1261         pmover = pmover->next;
1262     }
1263     pmover = pmoverfirst;
1264     while(pmover != NULL)
1265     {
1266         /* Not setting SPACEs fixes a pathological case without apparently breaking anything (1.07) */
1267         if(pmover->piece != PIECE_SPACE)
1268         {
1269             level_setpiece(plevel, pmover->x, pmover->y, pmover->piece);
1270             if(options_debug & DEBUG_MOVERS)
1271                 fprintf(stderr, "+ level_setpiece(%d, %d, %s)\n", pmover->x, pmover->y, piece_name[pmover->piece]);
1272         }
1273 
1274         pmover = pmover->previous;
1275         if(pmover == NULL)
1276             break;
1277     }
1278 
1279     /* Is player one still alive? */
1280     if(level_piece(plevel, plevel->player_x[0], plevel->player_y[0]) != PIECE_PLAYER_ONE)
1281            plevel->alive[0] = 0;
1282     /* Is player two still alive? */
1283     if(level_piece(plevel, plevel->player_x[1], plevel->player_y[1]) != PIECE_PLAYER_TWO)
1284         plevel->alive[1] = 0;
1285 
1286     /* Tidy up any movers created in the previous step */
1287     pmover = plevel->mover_first;
1288     while(pmover != NULL)
1289     {
1290         level_setmoving(plevel, pmover->x, pmover->y, MOVE_NONE);
1291         level_setprevious(plevel, pmover->x, pmover->y, PIECE_SPACE);
1292         ptmp = pmover;
1293         pmover = pmover->next;
1294         free(ptmp);
1295     }
1296     plevel->mover_first = NULL;
1297     plevel->mover_last = NULL;
1298 
1299     /* Can't undo at very start of level */
1300     if(plevel->move_current == NULL)
1301         return 0;
1302 
1303     /* If there is no previous step to undo, remove this move entirely */
1304     if(plevel->move_current->mover_last == NULL)
1305     {
1306         plevel->move_current = plevel->move_current->previous;
1307         plevel->flags |= LEVELFLAG_MOVES;
1308 
1309         return 0;
1310     }
1311 
1312     if(options_debug & DEBUG_MOVERS)
1313         fprintf(stderr, "\n");
1314 
1315     /* Start from the last mover for this step. */
1316     pmover = plevel->move_current->mover_last;
1317 
1318     pmoverfirst = NULL;
1319 
1320     td = MOVE_NONE;
1321 
1322     /* Working backwards, remove these pieces from the map */
1323     while(pmover != NULL)
1324     {
1325         pmoverfirst = pmover;
1326 
1327         level_setpiece(plevel, pmover->x, pmover->y, PIECE_SPACE);
1328 
1329         if(options_debug & DEBUG_MOVERS)
1330             fprintf(stderr, "- level_setpiece(%d, %d, %s)\n", pmover->x, pmover->y, piece_name[PIECE_SPACE]);
1331 
1332         /* If the piece is the player, update position and status */
1333         if(pmover->piece_previous == PIECE_PLAYER_ONE || pmover->piece_previous == PIECE_PLAYER_TWO)
1334         {
1335             plevel->player_x[pmover->piece_previous - PIECE_PLAYER_ONE] = pmover->x;
1336             plevel->player_y[pmover->piece_previous - PIECE_PLAYER_ONE] = pmover->y;
1337 
1338 #ifdef XOR_COMPATIBILITY
1339             if(plevel->mode == MODE_XOR)
1340             {
1341                 /* If a player is being resurrected in this move, and the
1342                    other player is alive, undo the automatic swap */
1343                 if(plevel->alive[pmover->piece_previous - PIECE_PLAYER_ONE] == 0 && plevel->alive[plevel->player])
1344                 {
1345                     /* Cosmetic mover to deactivate other player */
1346                      mover_newundo(plevel, plevel->player_x[plevel->player], plevel->player_y[plevel->player], MOVE_SWAPPED, PIECE_PLAYER_ONE + plevel->player, PIECE_SPACE, MOVER_UNDO);
1347                     plevel->player = pmover->piece_previous - PIECE_PLAYER_ONE;
1348                 }
1349 
1350                 /* The active player is the one which moves first
1351                    (last in undo */
1352                 plevel->player = pmover->piece_previous - PIECE_PLAYER_ONE;
1353             }
1354 #endif
1355 
1356             plevel->alive[pmover->piece_previous - PIECE_PLAYER_ONE] = 1;
1357         }
1358 
1359 #ifdef XOR_COMPATIBILITY
1360         /* If the piece is a teleport, store the direction of the original move
1361            into it for later use. */
1362         if(pmover->piece == PIECE_TELEPORT)
1363             td = pmover->direction;
1364 #endif
1365 
1366         /* until we reach the first mover for this step. */
1367         pmover = pmover->previous;
1368         if(pmover != NULL && pmover->fast == 0)
1369             break;
1370     }
1371 
1372     pmover = pmoverfirst;
1373 
1374     /* Now, move forwards through the movers and create cosmetic effects. */
1375     while(pmover != NULL)
1376     {
1377 
1378         if(options_debug & DEBUG_MOVERS)
1379             fprintf(stderr, "[%d] Undo mover at (%d,%d) is %s was %s (direction=%c) (flags=%d)\n",
1380                 count++, pmover->x, pmover->y,
1381                 piece_name[pmover->piece], piece_name[pmover->piece_previous],
1382                 directiontochar(pmover->direction), pmover->fast);
1383 
1384         d = pmover->direction;
1385 
1386         if(d != MOVE_NONE && d != MOVE_SWAP && d != MOVE_SWAPPED)
1387             d = (d + 2) % 4;
1388 
1389         if(isexplosion(pmover->piece))
1390         {
1391             /* Explosions don't move. */
1392             d = MOVE_NONE;
1393             /* Show dying explosion when undoing new explosion */
1394             if(options_debug & DEBUG_MOVERS)
1395                 fprintf(stderr, "* level_setprevious(%d, %d, %s)\n", pmover->x, pmover->y, piece_name[pmover->piece]);
1396             level_setprevious(plevel, pmover->x, pmover->y, pmover->piece);
1397         }
1398 
1399         /* Do we need to patch up the direction this piece is moving in? */
1400         /* Is it the player? */
1401         if((pmover->piece_previous == PIECE_PLAYER_ONE || pmover->piece_previous == PIECE_PLAYER_TWO) && (pmover->piece == PIECE_SPACE || pmover->piece == PIECE_GONE))
1402         {
1403             /* If so, are they moving out of a teleport? Use original direction
1404                of move if so. */
1405             if(td != MOVE_NONE)
1406                 d = (td + 2) % 4;
1407         }
1408         /* Otherwise, if the previous piece wasn't a move, it must have been a
1409            static piece being eaten by a mover, and thus shouldn't move. */
1410         else if((pmover->piece_previous < PIECE_MOVERS_FIRST || pmover->piece_previous > PIECE_MOVERS_LAST) && pmover->piece_previous != PIECE_CIRCLE
1411 #ifdef ENIGMA_COMPATIBILITY
1412                 && pmover->piece_previous != PIECE_CIRCLE_DOUBLE
1413 #endif
1414                )
1415             d = MOVE_NONE;
1416 
1417         /* Plot a cosmetic mover. */
1418         if(level_previous(plevel, pmover->x, pmover->y) != PIECE_SPACE)
1419             d = MOVE_NONE;
1420         /* but not if there are overlapping explosions */
1421         if(!(pmover->piece_previous >= PIECE_EXPLOSION_NEW_FIRST && pmover->piece_previous <= PIECE_EXPLOSION_NEW_LAST))
1422             mover_newundo(plevel, pmover->x, pmover->y, d, pmover->piece_previous, PIECE_SPACE, MOVER_UNDO);
1423 
1424         pmover = pmover->next;
1425     }
1426 
1427     pmover = pmoverfirst->previous;
1428 
1429     /* If there is another step, set it up for the next iteration */
1430     if(pmover != NULL)
1431     {
1432         plevel->move_current->mover_last = pmover;
1433         pmover = pmover->next;
1434     }
1435     else
1436     {
1437         pmover = plevel->move_current->mover_first;
1438         plevel->move_current->mover_first = NULL;
1439         plevel->move_current->mover_last = NULL;
1440 
1441         plevel->moves --;
1442     }
1443 
1444     /* Remove the movers in the step we've just done */
1445     if(pmover != NULL)
1446     {
1447         while(pmover != NULL)
1448         {
1449             /* Undo any pieces exploded or caught */
1450             if(pmover->piece_previous == PIECE_STAR)
1451             {
1452                 if(pmover->piece == PIECE_PLAYER_ONE || pmover->piece == PIECE_PLAYER_TWO)
1453                     plevel->stars_caught --;
1454                 else
1455                     plevel->stars_exploded --;
1456 
1457                 plevel->flags |= LEVELFLAG_STARS;
1458             }
1459 #ifdef XOR_COMPATIBILITY
1460             if(pmover->piece_previous == PIECE_SWITCH)
1461             {
1462                 plevel->switched = 1 - plevel->switched;
1463                 plevel->flags |= LEVELFLAG_SWITCH;
1464             }
1465             if(pmover->piece_previous == PIECE_MAP_TOP_LEFT)
1466             {
1467                 plevel->mapped ^= MAPPED_TOP_LEFT;
1468                 plevel->flags |= LEVELFLAG_MAP;
1469             }
1470             if(pmover->piece_previous == PIECE_MAP_TOP_RIGHT)
1471             {
1472                 plevel->mapped ^= MAPPED_TOP_RIGHT;
1473                 plevel->flags |= LEVELFLAG_MAP;
1474             }
1475             if(pmover->piece_previous == PIECE_MAP_BOTTOM_LEFT)
1476             {
1477                 plevel->mapped ^= MAPPED_BOTTOM_LEFT;
1478                 plevel->flags |= LEVELFLAG_MAP;
1479             }
1480             if(pmover->piece_previous == PIECE_MAP_BOTTOM_RIGHT)
1481             {
1482                 plevel->mapped ^= MAPPED_BOTTOM_RIGHT;
1483                 plevel->flags |= LEVELFLAG_MAP;
1484             }
1485 #endif
1486 
1487             ptmp = pmover;
1488             pmover = pmover->next;
1489             free(ptmp);
1490         }
1491     }
1492 
1493     if(plevel->move_current->mover_last != NULL)
1494         plevel->move_current->mover_last->next = NULL;
1495 
1496     /* If the move was a swap, revert to the previous player */
1497     if(plevel->move_current->direction == MOVE_SWAP)
1498         plevel->player = 1 - plevel->player;
1499 
1500     /* Have we just undone failure? */
1501     if((plevel->flags & LEVELFLAG_FAILED) && (plevel->alive[0] != 0 || plevel->alive[1] != 0))
1502     {
1503         plevel->flags &= ~LEVELFLAG_FAILED;
1504         plevel->flags |= LEVELFLAG_MOVES;
1505     }
1506 
1507     /* Have we just undone success? */
1508     if(plevel->flags & (LEVELFLAG_SOLVED | LEVELFLAG_EXIT))
1509     {
1510         plevel->flags &= ~LEVELFLAG_SOLVED;
1511         plevel->flags &= ~LEVELFLAG_EXIT;
1512         plevel->flags |= LEVELFLAG_STARS;
1513     }
1514 
1515 #ifdef XOR_COMPATIBILITY
1516     if(plevel->mode == MODE_XOR)
1517         xor_focus(plevel);
1518 #endif
1519 
1520     /* If there are no more steps in this move, chroma-curses needs advanced
1521        warning that the move counter is going to change. */
1522     if(plevel->move_current->mover_last == NULL)
1523     {
1524         plevel->flags |= LEVELFLAG_MOVES;
1525     }
1526 
1527     return 1;
1528 }
1529