--- /dev/null
+/*
+ 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
+