chiark / gitweb /
Update upstream source from tag 'upstream/1.18'
[chroma-debian.git] / xor.c
diff --git a/xor.c b/xor.c
new file mode 100644 (file)
index 0000000..8b01a78
--- /dev/null
+++ b/xor.c
@@ -0,0 +1,813 @@
+/*  
+    xor.c
+
+    An (almost) exact reverse engineering of the original BBC XOR engine.
+    Rather than the usual list of movers, it maintains a separate stack of
+    spaces to be examined. In each round, we examine the first space on this
+    stack and generate movers accordingly; in doing so, we may add additional
+    spaces to the stack. The movers are cosmetic, and are removed at the end of
+    the round.
+
+    See levels/regression/xor-regression.chroma for further details of what
+    it gets right versus other engines. The memory addresses are those of the
+    equivalent 6502 code in the original version.
+
+
+    Copyright (C) 2010-2019 Amf
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version. 
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "chroma.h"
+#include "level.h"
+#include "util.h"
+
+#ifdef XOR_COMPATIBILITY
+
+extern int move_x[];
+extern int move_y[];
+
+extern int options_xor_mode;
+
+#define PIECE_XOR_MAGUS                PIECE_PLAYER_ONE
+#define PIECE_XOR_QUAESTOR        PIECE_PLAYER_TWO
+#define PIECE_XOR_DOTS                PIECE_DOTS_X
+#define PIECE_XOR_WAVES                PIECE_DOTS_Y
+#define PIECE_XOR_CHICKEN        PIECE_ARROW_RED_LEFT
+#define PIECE_XOR_V_BOMB        PIECE_BOMB_RED_LEFT
+#define PIECE_XOR_FISH                PIECE_ARROW_RED_DOWN
+#define PIECE_XOR_H_BOMB        PIECE_BOMB_RED_DOWN
+#define PIECE_XOR_DOLL                PIECE_CIRCLE
+#define PIECE_XOR_MASK                PIECE_STAR
+
+void xor_focus(struct level* plevel);
+
+/* 0x192e - 0x1941 */
+/* mover_addtostack */
+
+/* 0x1a4f - 0x1a75 */
+void xor_considerdownmover(struct level* plevel, int x, int y)
+{
+    int p; 
+
+    p = level_piece(plevel, x, y - 1);
+    if(p == PIECE_XOR_FISH || p == PIECE_XOR_H_BOMB)
+        mover_addtostack(plevel, x, y - 1, 0);
+}
+
+/* 0x1a76 - 0x1a98 */
+void xor_considerleftmover(struct level* plevel, int x, int y)
+{
+    int p;
+
+    p = level_piece(plevel, x + 1, y);
+    if(p == PIECE_XOR_CHICKEN || p == PIECE_XOR_V_BOMB)
+        mover_addtostack(plevel, x + 1, y, 0);
+}
+
+/* 0x1f39 - 0x202b */
+int xor_teleport(struct level* plevel, int px, int py, int *dx, int *dy, int *move)
+{
+    int teleport;
+    int tx, ty;
+    int moveout = MOVE_NONE;
+
+    teleport = -1;
+    if(px == plevel->teleport_x[0] && py == plevel->teleport_y[0])
+        teleport = 0;
+    if(px == plevel->teleport_x[1] && py == plevel->teleport_y[1])
+        teleport = 1;
+    if(teleport == -1)
+        return 0;
+
+    tx = plevel->teleport_x[1 - teleport];
+    ty = plevel->teleport_y[1 - teleport];
+
+    /* If the other teleport has been destroyed, turn this one into a wall */
+    if(level_piece(plevel, tx, ty) != PIECE_TELEPORT)
+    {
+        mover_new(plevel, plevel->teleport_x[teleport], plevel->teleport_y[teleport], MOVE_NONE, PIECE_WALL, 0);
+        return 0;
+    }
+
+    if(level_piece(plevel, tx + 1, ty) == PIECE_SPACE)
+    {
+        *dx = tx + 1;
+        *dy = ty;
+        moveout = MOVE_RIGHT;
+    }
+    else if(level_piece(plevel, tx, ty - 1) == PIECE_SPACE)
+    {
+        *dx = tx;
+        *dy = ty - 1;
+        moveout = MOVE_UP;
+    }
+    else if(level_piece(plevel, tx - 1, ty) == PIECE_SPACE)
+    {
+        *dx = tx - 1;
+        *dy = ty;
+        moveout = MOVE_LEFT;
+    }
+    else if(level_piece(plevel, tx, ty + 1) == PIECE_SPACE)
+    {
+        *dx = tx;
+        *dy = ty + 1;
+        moveout = MOVE_DOWN;
+    }
+
+    if(moveout != MOVE_NONE)
+    {
+
+        /* Visual effects for the player moving in to the teleport */
+        /* Store original player move direction in cosmetic mover */
+        mover_new(plevel, px, py, *move, PIECE_TELEPORT, 0);
+        level_setprevious(plevel, px, py, PIECE_PLAYER_ONE + plevel->player);
+        level_setpreviousmoving(plevel, px, py, *move);
+
+        /* Visual effects for the player moving out of the teleport */
+        mover_new(plevel, tx, ty, MOVE_NONE, PIECE_TELEPORT, 0);
+
+        /* Change move to produce the effect of coming out of the teleport */
+        *move = moveout;
+
+        plevel->view_x[plevel->player] = plevel->view_teleport_x[1 - teleport];
+        plevel->view_y[plevel->player] = plevel->view_teleport_y[1 - teleport];
+
+        return 1;
+    }
+
+    return 0;
+}
+
+/* 0x1f0e - 0x1f38 */
+void xor_explode(struct level* plevel, int x, int y, int type, int previous)
+{
+    int p;
+
+    p = level_piece(plevel, x, y);
+
+    if(p == PIECE_SWITCH)
+    {
+        plevel->switched = 1 - plevel->switched;
+        plevel->flags |= LEVELFLAG_SWITCH;
+    }
+    if(p == PIECE_XOR_MASK)
+    {
+        plevel->stars_exploded ++;
+        plevel->flags |= LEVELFLAG_STARS;
+    }
+
+    /* Don't explode edge walls */
+    if(x == 0 || y == 0 || x == plevel->size_x - 1 || y == plevel->size_y - 1)
+        return;
+
+    if(previous != MOVE_NONE)
+    {
+        mover_new(plevel, x, y, previous, PIECE_GONE, 0);
+        level_setpiece(plevel, x, y, PIECE_SPACE);
+    }
+    mover_new(plevel, x, y, MOVE_NONE, type, 0);
+    if(previous == MOVE_NONE)
+        level_setprevious(plevel, x, y, p);
+}
+
+int xor_move(struct level* plevel, int move)
+{
+    int p;
+    int px, py;
+    int dx, dy;
+    int ox, oy;
+    int into;
+    int bp;
+
+    if(plevel->alive[plevel->player] == 0)
+        return 0;
+
+    px = plevel->player_x[plevel->player];
+    py = plevel->player_y[plevel->player];
+
+    /* 0x141e - 0x14df */
+    if(move == MOVE_LEFT)
+    {
+        dx = px - 1;
+        dy = py;
+
+        ox = px - 2;
+        oy = py;
+
+        p = level_piece(plevel, dx, dy);
+        if(p == PIECE_WALL || p == PIECE_XOR_MAGUS || p == PIECE_XOR_QUAESTOR)
+            return 0;
+        if(p == PIECE_DOOR)
+        {
+            if(plevel->stars_caught == plevel->stars_total)
+                plevel->flags |= LEVELFLAG_EXIT;
+            else
+                return 0;
+        }
+        if(p == PIECE_XOR_WAVES || p == PIECE_XOR_CHICKEN || p == PIECE_XOR_V_BOMB)
+            return 0;
+        if(p == PIECE_XOR_FISH || p == PIECE_XOR_H_BOMB)
+        {
+            into = level_piece(plevel, ox, oy);
+            if(into == PIECE_SPACE || into == PIECE_XOR_DOTS)
+            {
+                mover_new(plevel, ox, oy, MOVE_LEFT, p, 0);
+                bp = level_piece(plevel, ox, oy + 1);
+                if(bp == PIECE_SPACE || bp == PIECE_XOR_WAVES)
+                    mover_addtostack(plevel, ox, oy, 0);
+            }
+            else
+                return 0;
+        }
+        if(p == PIECE_TELEPORT)
+        {
+            if(!xor_teleport(plevel, px - 1, py, &dx, &dy, &move))
+                return 0;
+        }
+        if(p == PIECE_XOR_MASK)
+        {
+            plevel->stars_caught ++;
+            plevel->flags |= LEVELFLAG_STARS;
+        }
+        if(p == PIECE_SWITCH)
+        {
+            plevel->switched = 1 - plevel->switched;
+            plevel->flags |= LEVELFLAG_SWITCH;
+        }
+        if(p == PIECE_MAP_TOP_LEFT)
+        {
+            plevel->mapped |= MAPPED_TOP_LEFT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_TOP_RIGHT)
+        {
+            plevel->mapped |= MAPPED_TOP_RIGHT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_BOTTOM_LEFT)
+        {
+            plevel->mapped |= MAPPED_BOTTOM_LEFT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_BOTTOM_RIGHT)
+        {
+            plevel->mapped |= MAPPED_BOTTOM_RIGHT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_XOR_DOLL)
+        {
+            into = level_piece(plevel, ox, oy);
+            if(into == PIECE_SPACE)
+            {
+                mover_new(plevel, ox, oy, MOVE_LEFT, p, 0);
+                if(level_piece(plevel, ox - 1, oy) == PIECE_SPACE)
+                    mover_addtostack(plevel, ox, oy, MOVE_LEFT);
+            }
+            else
+                return 0;
+        }
+
+        plevel->player_x[plevel->player] = dx;
+        plevel->player_y[plevel->player] = dy;
+
+        mover_new(plevel, dx, dy, move, PIECE_XOR_MAGUS + plevel->player, 0);
+        mover_new(plevel, px, py, move, PIECE_SPACE, 0);
+
+        if(plevel->flags & LEVELFLAG_EXIT)
+            return 1;
+
+        xor_considerdownmover(plevel, px, py);
+        xor_considerleftmover(plevel, px, py);
+
+        return 1;
+    }
+
+    /* 0x14e8 - 0x15a2 */
+    if(move == MOVE_RIGHT)
+    {
+        dx = px + 1;
+        dy = py;
+
+        ox = px + 2;
+        oy = py;
+
+        p = level_piece(plevel, dx, dy);
+        if(p == PIECE_WALL || p == PIECE_XOR_MAGUS || p == PIECE_XOR_QUAESTOR)
+            return 0;
+        if(p == PIECE_DOOR)
+        {
+            if(plevel->stars_caught == plevel->stars_total)
+                plevel->flags |= LEVELFLAG_EXIT;
+            else
+                return 0;
+        }
+        if(p == PIECE_XOR_WAVES || p == PIECE_XOR_CHICKEN || p == PIECE_XOR_V_BOMB)
+            return 0;
+        if(p == PIECE_XOR_FISH || p == PIECE_XOR_H_BOMB)
+        {
+            into = level_piece(plevel, ox, oy);
+            if(into == PIECE_SPACE || into == PIECE_XOR_DOTS)
+            {
+                mover_new(plevel, ox, oy, MOVE_RIGHT, p, 0);
+                bp = level_piece(plevel, ox, oy + 1);
+                if(bp == PIECE_SPACE || bp == PIECE_XOR_WAVES)
+                    mover_addtostack(plevel, ox, oy, 0);
+            }
+            else
+                return 0;
+        }
+        if(p == PIECE_TELEPORT)
+        {
+            if(!xor_teleport(plevel, px + 1, py, &dx, &dy, &move))
+                return 0;
+        }
+        if(p == PIECE_XOR_MASK)
+        {
+            plevel->stars_caught ++;
+            plevel->flags |= LEVELFLAG_STARS;
+        }
+        if(p == PIECE_SWITCH)
+        {
+            plevel->switched = 1 - plevel->switched;
+            plevel->flags |= LEVELFLAG_SWITCH;
+        }
+        if(p == PIECE_MAP_TOP_LEFT)
+        {
+            plevel->mapped |= MAPPED_TOP_LEFT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_TOP_RIGHT)
+        {
+            plevel->mapped |= MAPPED_TOP_RIGHT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_BOTTOM_LEFT)
+        {
+            plevel->mapped |= MAPPED_BOTTOM_LEFT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_BOTTOM_RIGHT)
+        {
+            plevel->mapped |= MAPPED_BOTTOM_RIGHT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_XOR_DOLL)
+        {
+            into = level_piece(plevel, ox, oy);
+            if(into == PIECE_SPACE)
+            {
+                mover_new(plevel, ox, oy, MOVE_RIGHT, p, 0);
+                if(level_piece(plevel, ox + 1, oy) == PIECE_SPACE)
+                    mover_addtostack(plevel, ox, oy, MOVE_RIGHT);
+            }
+            else
+                return 0;
+        }
+
+        plevel->player_x[plevel->player] = dx;
+        plevel->player_y[plevel->player] = dy;
+
+        mover_new(plevel, dx, dy, move, PIECE_XOR_MAGUS + plevel->player, 0);
+        mover_new(plevel, px, py, move, PIECE_SPACE, 0);
+
+        if(plevel->flags & LEVELFLAG_EXIT)
+            return 1;
+
+        xor_considerdownmover(plevel, px, py);
+
+        return 1;
+    }
+
+    /* 0x15a5 - 0x1666 */
+    if(move == MOVE_UP)
+    {
+        dx = px;
+        dy = py - 1;
+
+        ox = px;
+        oy = py - 2;
+
+        p = level_piece(plevel, dx, dy);
+        if(p == PIECE_WALL || p == PIECE_XOR_MAGUS || p == PIECE_XOR_QUAESTOR)
+            return 0;
+        if(p == PIECE_DOOR)
+        {
+            if(plevel->stars_caught == plevel->stars_total)
+                plevel->flags |= LEVELFLAG_EXIT;
+            else
+                return 0;
+        }
+        if(p == PIECE_XOR_DOTS || p == PIECE_XOR_FISH || p == PIECE_XOR_H_BOMB)
+            return 0;
+        if(p == PIECE_XOR_CHICKEN || p == PIECE_XOR_V_BOMB)
+        {
+            into = level_piece(plevel, ox, oy);
+            if(into == PIECE_SPACE || into == PIECE_XOR_WAVES)
+            {
+                mover_new(plevel, ox, oy, MOVE_UP, p, 0);
+                bp = level_piece(plevel, ox - 1, oy);
+                if(bp == PIECE_SPACE || bp == PIECE_XOR_DOTS)
+                    mover_addtostack(plevel, ox, oy, 0);
+            }
+            else
+                return 0;
+        }
+        if(p == PIECE_TELEPORT)
+        {
+            if(!xor_teleport(plevel, px, py - 1, &dx, &dy, &move))
+                return 0;
+        }
+        if(p == PIECE_XOR_MASK)
+        {
+            plevel->stars_caught ++;
+            plevel->flags |= LEVELFLAG_STARS;
+        }
+        if(p == PIECE_SWITCH)
+        {
+            plevel->switched = 1 - plevel->switched;
+            plevel->flags |= LEVELFLAG_SWITCH;
+        }
+        if(p == PIECE_MAP_TOP_LEFT)
+        {
+            plevel->mapped |= MAPPED_TOP_LEFT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_TOP_RIGHT)
+        {
+            plevel->mapped |= MAPPED_TOP_RIGHT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_BOTTOM_LEFT)
+        {
+            plevel->mapped |= MAPPED_BOTTOM_LEFT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_BOTTOM_RIGHT)
+        {
+            plevel->mapped |= MAPPED_BOTTOM_RIGHT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_XOR_DOLL)
+        {
+            into = level_piece(plevel, ox, oy);
+            if(into == PIECE_SPACE)
+            {
+                mover_new(plevel, ox, oy, MOVE_UP, p, 0);
+                if(level_piece(plevel, ox, oy - 1) == PIECE_SPACE)
+                    mover_addtostack(plevel, ox, oy, MOVE_UP);
+            }
+            else
+                return 0;
+        }
+
+        plevel->player_x[plevel->player] = dx;
+        plevel->player_y[plevel->player] = dy;
+
+        mover_new(plevel, dx, dy, move, PIECE_XOR_MAGUS + plevel->player, 0);
+        mover_new(plevel, px, py, move, PIECE_SPACE, 0);
+
+        if(plevel->flags & LEVELFLAG_EXIT)
+            return 1;
+
+        xor_considerleftmover(plevel, px, py);
+
+        return 1;
+    }
+
+    /* 0x1669 - 0x1729 */
+    if(move == MOVE_DOWN)
+    {
+        dx = px;
+        dy = py + 1;
+
+        ox = px;
+        oy = py + 2;
+
+        p = level_piece(plevel, dx, dy);
+        if(p == PIECE_WALL || p == PIECE_XOR_MAGUS || p == PIECE_XOR_QUAESTOR)
+            return 0;
+        if(p == PIECE_DOOR)
+        {
+            if(plevel->stars_caught == plevel->stars_total)
+                plevel->flags |= LEVELFLAG_EXIT;
+            else
+                return 0;
+        }
+        if(p == PIECE_XOR_DOTS || p == PIECE_XOR_FISH || p == PIECE_XOR_H_BOMB)
+            return 0;
+        if(p == PIECE_XOR_CHICKEN || p == PIECE_XOR_V_BOMB)
+        {
+            into = level_piece(plevel, ox, oy);
+            if(into == PIECE_SPACE || into == PIECE_XOR_WAVES)
+            {
+                mover_new(plevel, ox, oy, MOVE_DOWN, p, 0);
+                bp = level_piece(plevel, ox - 1, oy);
+                if(bp == PIECE_SPACE || bp == PIECE_XOR_DOTS)
+                    mover_addtostack(plevel, ox, oy, 0);
+            }
+            else
+                return 0;
+        }
+        if(p == PIECE_TELEPORT)
+        {
+            if(!xor_teleport(plevel, px, py + 1, &dx, &dy, &move))
+                return 0;
+        }
+        if(p == PIECE_XOR_MASK)
+        {
+            plevel->stars_caught ++;
+            plevel->flags |= LEVELFLAG_STARS;
+        }
+        if(p == PIECE_SWITCH)
+        {
+            plevel->switched = 1 - plevel->switched;
+            plevel->flags |= LEVELFLAG_SWITCH;
+        }
+        if(p == PIECE_MAP_TOP_LEFT)
+        {
+            plevel->mapped |= MAPPED_TOP_LEFT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_TOP_RIGHT)
+        {
+            plevel->mapped |= MAPPED_TOP_RIGHT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_BOTTOM_LEFT)
+        {
+            plevel->mapped |= MAPPED_BOTTOM_LEFT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_MAP_BOTTOM_RIGHT)
+        {
+            plevel->mapped |= MAPPED_BOTTOM_RIGHT;
+            plevel->flags |= LEVELFLAG_MAP;
+        }
+        if(p == PIECE_XOR_DOLL)
+        {
+            into = level_piece(plevel, ox, oy);
+            if(into == PIECE_SPACE)
+            {
+                mover_new(plevel, ox, oy, MOVE_DOWN, p, 0);
+                if(level_piece(plevel, ox, oy + 1) == PIECE_SPACE)
+                    mover_addtostack(plevel, ox, oy, MOVE_DOWN);
+            }
+            else
+                return 0;
+        }
+
+        plevel->player_x[plevel->player] = dx;
+        plevel->player_y[plevel->player] = dy;
+
+        mover_new(plevel, dx, dy, move, PIECE_XOR_MAGUS + plevel->player, 0);
+        mover_new(plevel, px, py, move, PIECE_SPACE, 0);
+
+        if(plevel->flags & LEVELFLAG_EXIT)
+            return 1;
+
+        xor_considerleftmover(plevel, px, py);
+        xor_considerdownmover(plevel, px, py);
+
+        return 1;
+    }
+
+    return 0;
+}
+
+int xor_evolve(struct level* plevel)
+{
+    struct mover* poldmovers;
+    struct mover* pmover;
+    struct mover* ptmp;
+
+    int mp;
+    int into;
+    int np;
+
+    int d;
+
+    int px, py;
+
+    poldmovers = plevel->mover_first;
+
+    plevel->mover_first = NULL;
+    plevel->mover_last = NULL;
+
+    pmover = poldmovers;
+    while(pmover != NULL)
+    {
+        if(isexplosion(pmover->piece))
+            mover_new(plevel, pmover->x, pmover->y, MOVE_NONE, PIECE_SPACE, 0);
+
+        level_setmoving(plevel, pmover->x, pmover->y, MOVE_NONE);
+        level_setprevious(plevel, pmover->x, pmover->y, PIECE_SPACE);
+        level_setpreviousmoving(plevel, pmover->x, pmover->y, MOVE_NONE);
+        level_setdetonator(plevel, pmover->x, pmover->y, PIECE_SPACE);
+        level_setdetonatormoving(plevel, pmover->x, pmover->y, MOVE_NONE);
+
+        if(isexplosion(pmover->piece))
+            level_setprevious(plevel, pmover->x, pmover->y, pmover->piece);
+
+        ptmp = pmover->next;
+        free(pmover);
+        pmover = ptmp;
+    }
+
+    while(plevel->mover_first == NULL)
+    {
+
+    pmover = plevel->stack_first;
+
+    if(pmover == NULL)
+        return 0;
+
+    px = pmover->x;
+    py = pmover->y;
+
+    mp = level_piece(plevel, px, py);
+
+    /* 0x1988 - 0x19df */
+    if(mp == PIECE_XOR_FISH || mp == PIECE_XOR_H_BOMB)
+    {
+        into = level_piece(plevel, px, py + 1);
+        if(into == PIECE_SPACE || into == PIECE_XOR_WAVES || into == PIECE_XOR_MAGUS || into == PIECE_XOR_QUAESTOR)
+        {
+            xor_considerdownmover(plevel, px, py);
+            xor_considerleftmover(plevel, px, py);
+
+            np = level_piece(plevel, px, py + 2);
+            if(np == PIECE_SPACE || np == PIECE_XOR_WAVES || np == PIECE_XOR_MAGUS || np == PIECE_XOR_QUAESTOR || np == PIECE_XOR_V_BOMB || np == PIECE_XOR_H_BOMB)
+                mover_addtostack(plevel, px, py + 1, MOVE_NONE);
+
+            mover_new(plevel, px, py + 1, MOVE_DOWN, mp, 0);
+            mover_new(plevel, px, py, MOVE_DOWN, PIECE_SPACE, 0);
+
+        }
+        /* 0x1ae9 - 0x1b54 */
+        if(into == PIECE_XOR_H_BOMB)
+        {
+            /* BBC XOR does *not* considerleftmover(px, py) */
+            xor_considerdownmover(plevel, px, py);
+            xor_considerdownmover(plevel, px - 1, py + 1);
+            xor_considerdownmover(plevel, px + 1, py + 1);
+            xor_considerleftmover(plevel, px + 1, py + 1);
+
+            mover_new(plevel, px, py, MOVE_DOWN, PIECE_SPACE, 0);
+            level_setdetonator(plevel, px, py + 1, mp);
+            level_setdetonatormoving(plevel, px, py + 1, MOVE_DOWN);
+
+            xor_explode(plevel, px - 1, py + 1, PIECE_EXPLOSION_RED_LEFT, MOVE_NONE);
+            xor_explode(plevel, px, py + 1, PIECE_EXPLOSION_RED_HORIZONTAL, MOVE_NONE);
+            xor_explode(plevel, px + 1, py + 1, PIECE_EXPLOSION_RED_RIGHT, MOVE_NONE);
+        }
+
+        /* 0x1b57 - 0x1baf */
+        if(into == PIECE_XOR_V_BOMB)
+        {
+            xor_considerdownmover(plevel, px, py);
+            xor_considerleftmover(plevel, px, py);
+            xor_considerleftmover(plevel, px, py + 1);
+            xor_considerleftmover(plevel, px, py + 2);
+
+            level_setdetonator(plevel, px, py + 1, mp);
+            level_setdetonatormoving(plevel, px, py + 1, MOVE_DOWN);
+
+            xor_explode(plevel, px, py + 0, PIECE_EXPLOSION_RED_TOP, MOVE_DOWN);
+            xor_explode(plevel, px, py + 1, PIECE_EXPLOSION_RED_VERTICAL, MOVE_NONE);
+            xor_explode(plevel, px, py + 2, PIECE_EXPLOSION_RED_BOTTOM, MOVE_NONE);
+        }
+    }
+
+    /* 0x1bb2 - 0x1c05 */
+    if(mp == PIECE_XOR_CHICKEN || mp == PIECE_XOR_V_BOMB)
+    {
+        into = level_piece(plevel, px - 1, py);
+        if(into == PIECE_SPACE || into == PIECE_XOR_DOTS || into == PIECE_XOR_MAGUS || into == PIECE_XOR_QUAESTOR)
+        {
+            xor_considerleftmover(plevel, px, py);
+            xor_considerdownmover(plevel, px, py);
+
+            np = level_piece(plevel, px - 2, py);
+            if(np == PIECE_SPACE || np == PIECE_XOR_DOTS || np == PIECE_XOR_MAGUS || np == PIECE_XOR_QUAESTOR || np == PIECE_XOR_V_BOMB || np == PIECE_XOR_H_BOMB)
+                mover_addtostack(plevel, px - 1, py, MOVE_NONE);
+
+            mover_new(plevel, px - 1, py, MOVE_LEFT, mp, 0);
+            mover_new(plevel, px, py, MOVE_LEFT, PIECE_SPACE, 0);
+
+        }
+
+        /* 0x1c08 - 0x1c4e */
+        if(into == PIECE_XOR_H_BOMB)
+        {
+            xor_considerleftmover(plevel, px, py);
+            xor_considerdownmover(plevel, px, py);
+            xor_considerdownmover(plevel, px - 1, py);
+            xor_considerdownmover(plevel, px - 2, py);
+
+            level_setdetonator(plevel, px - 1, py, mp);
+            level_setdetonatormoving(plevel, px - 1, py, MOVE_LEFT);
+
+            xor_explode(plevel, px - 2, py, PIECE_EXPLOSION_RED_LEFT, MOVE_NONE);
+            xor_explode(plevel, px - 1, py, PIECE_EXPLOSION_RED_HORIZONTAL, MOVE_NONE);
+            xor_explode(plevel, px, py, PIECE_EXPLOSION_RED_RIGHT, MOVE_LEFT);
+        }
+
+        /* 0x1c51 - 0x1cb4 */
+        if(into == PIECE_XOR_V_BOMB)
+        {
+            xor_considerleftmover(plevel, px, py);
+            xor_considerdownmover(plevel, px, py);
+            xor_considerleftmover(plevel, px - 1, py + 1);
+            xor_considerleftmover(plevel, px - 1, py - 1);
+            xor_considerdownmover(plevel, px - 1, py - 1);
+
+            mover_new(plevel, px, py, MOVE_LEFT, PIECE_SPACE, 0);
+            level_setdetonator(plevel, px - 1, py, mp);
+            level_setdetonatormoving(plevel, px - 1, py, MOVE_LEFT);
+
+            xor_explode(plevel, px - 1, py - 1, PIECE_EXPLOSION_RED_TOP, MOVE_NONE);
+            xor_explode(plevel, px - 1, py, PIECE_EXPLOSION_RED_VERTICAL, MOVE_NONE);
+            xor_explode(plevel, px - 1, py + 1, PIECE_EXPLOSION_RED_BOTTOM, MOVE_NONE);
+        }
+    }
+
+    /* 0x209e - 0x20f4 */
+    if(mp == PIECE_XOR_DOLL)
+    {
+        d = pmover->direction;
+
+        into = level_piece(plevel, px + move_x[d], py + move_y[d]);
+        if(into == PIECE_SPACE)
+        {
+            np = level_piece(plevel, px + 2 * move_x[d], py + 2 * move_y[d]);
+            if(np == PIECE_SPACE)
+                mover_addtostack(plevel, px + move_x[d], py + move_y[d], d);
+
+            mover_new(plevel, px + move_x[d], py + move_y[d], d, PIECE_XOR_DOLL, 0);
+            mover_new(plevel, px, py, d, PIECE_SPACE, 0);
+        }
+    }
+
+    if(plevel->stack_last == plevel->stack_first)
+        plevel->stack_last = NULL;
+    plevel->stack_first = plevel->stack_first->next;
+    free(pmover);
+
+    }
+
+    /* Is player one still alive? */
+    if(level_piece(plevel, plevel->player_x[0], plevel->player_y[0]) != PIECE_PLAYER_ONE)
+        plevel->alive[0] = 0;
+
+    /* Is player two still alive? */
+    if(level_piece(plevel, plevel->player_x[1], plevel->player_y[1]) != PIECE_PLAYER_TWO)
+        plevel->alive[1] = 0; 
+
+    /* If a player has died, swap to the other player at the end of the move */
+    if(plevel->alive[plevel->player] == 0 && plevel->stack_first == NULL)
+    {
+        if(plevel->alive[1 - plevel->player])
+        {
+            plevel->player = 1 - plevel->player;
+
+            /* Cosmetic mover for the newly swapped in player */
+            mover_new(plevel, plevel->player_x[plevel->player], plevel->player_y[plevel->player], MOVE_SWAP, PIECE_PLAYER_ONE +  plevel->player, 0);
+        }
+
+        /* Force a check for whether a redraw is needed */
+        return 1;
+    }
+
+    return 0;
+}
+
+void xor_focus(struct level* plevel)
+{
+    if(plevel->player_x[plevel->player] <= plevel->view_x[plevel->player])
+        plevel->view_x[plevel->player] = plevel->player_x[plevel->player] - 1;
+    if(plevel->player_x[plevel->player] >= plevel->view_x[plevel->player] + 7)
+        plevel->view_x[plevel->player] = plevel->player_x[plevel->player] - 6;
+    if(plevel->player_y[plevel->player] <= plevel->view_y[plevel->player])
+        plevel->view_y[plevel->player] = plevel->player_y[plevel->player] - 1;
+    if(plevel->player_y[plevel->player] >= plevel->view_y[plevel->player] + 7)
+        plevel->view_y[plevel->player] = plevel->player_y[plevel->player] - 6;
+}
+
+#endif
+