Logo Search packages:      
Sourcecode: xarchon version File versions  Download package

board.c

/*--------------------------------------------------------------------------*/
/* game board                                                               */
/*--------------------------------------------------------------------------*/

#include <config.h>

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "board.h"
#include "field.h"
#include "main.h"
#include "sprite.h"
#include "canvas.h"
#include "iface.h"
#include "audio.h"

/*--------------------------------------------------------------------------*/
/* defines                                                                  */
/*--------------------------------------------------------------------------*/

#define BOARD_X(x)     CELL_X((CANVAS_XCELLS - BOARD_XCELLS) / 2.0 + x)
#define BOARD_Y(y)     CELL_Y((CANVAS_YCELLS - BOARD_YCELLS) / 2.0 + y)

#define FLOOR_DARK     0                /* steppings for light and dark */
#define FLOOR_LIGHT    7                /*   outside luminance cycle */

#define FONT_NAME      "-misc-fixed-medium-*-normal-*-15-0-*-*-*-*-iso8859-1"

#define V_CELL_YSIZE   40               /* revive cell height */

/*--------------------------------------------------------------------------*/
/* selection states                                                         */
/*--------------------------------------------------------------------------*/

enum {
   SELECT_SOURCE = 1,                   /* non-spell */
   SELECT_TARGET,
   SELECT_FIELD,                        /* field mode */
   SELECT_GAME_OVER,                    /* the state to end all states! */
   SELECT_SPELL,
   SELECT_SPELL_DONE,                   /* spell completed or failed */
   T_SELECT_SOURCE,                     /* teleport */
   T_SELECT_TARGET,
   H_SELECT_TARGET,                     /* heal */
   X_SELECT_SOURCE,                     /* exchange */
   X_SELECT_TARGET,
   L_SELECT_TARGET,                     /* elemental */
   V_SELECT_SOURCE,                     /* revive */
   M_SELECT_TARGET,                     /* imprison */
};

/*--------------------------------------------------------------------------*/
/* structures                                                               */
/*--------------------------------------------------------------------------*/

typedef struct {                        /* Slow Sprite Paint */
   int count;
   int x1, y1;
   int both;                            /* double ssp -- for exchange spell */
   int x2, y2;
   int complete;                        /* ssp completed */
} SSP;

typedef struct {
   int game_status;                     /* 0=none,  1=active,  -1=paused */
   int turn;                            /* count of turns since game start */
   int side;                            /* 0=light,  1=dark */
   int state;                           /* state of the board */
   int lumi, lumi_d;                    /* luminance value and direction */
   int cx, cy;                          /* current cursor position */
   int cstate;                          /* state of cursor movement */
   int ax0, ay0;                        /* initial position of picked actor */
   int fire_down;                       /* fire key is held */
   int spell;                           /* spell selected */
   int prev_down, next_down;            /* up/down key is held */
   int teleport, exchange;              /* teleport/exchange taking place */
   int tx0, ty0;                        /* teleport source / exchange 1st */
   int tx1, ty1;                        /* teleport target / exchange 2nd */
   SSP ssp;                             /* Slow Sprite Paint data */
   CELL elem;                           /* cell for active elemental */
   char message[64];                    /* last board message displayed */
   int any_output;                      /* if any output emitted */
} BOARD_DATA;

typedef struct {
   void *ptr;
   long fg, bg;
} FONT;

/*--------------------------------------------------------------------------*/
/* functions                                                                */
/*--------------------------------------------------------------------------*/

static void board_paint_cell(int x, int y);
static void board_paint_cursor(void);
static void board_message(char *format, ...);
static void board_ssp_init(int x1, int y1, int x2, int y2, int both);
static void board_ssp_frame(void);
static void board_init_cell(CELL *cell, int actor_num);
static void board_clear_cell(CELL *cell);
static void board_reset_actor(ACTOR *actor);
static void board_iface_turn(void);
static int  board_verify_move(int state);
static int  board_check_win(void);
static void board_end_turn(int swap_side);
static void board_field(int xd, int yd, int xa, int ya);
static void board_move_done(void);
static void board_spell_done(void);
static void board_input(void);
static void board_cursor(void);
static void board_spell(void);
static void board_teleport(int x, int y, int side);
static void board_heal(int x, int y, int side);
static void board_shift_time(int x, int y, int side);
static void board_exchange(int x, int y, int side);
static void board_summon_elemental(int x, int y, int side);
static int board_revive_frame(int *i, int *old_i, int *actors);
static void board_revive(int x, int y, int side);
static void board_imprison(int x, int y, int side);
static void board_cease_conjuring(int x, int y, int side);
static int board_get_route_2(int x1, int y1, int x2, int y2, int *route,
                             int prev_state, int light, int ground, int distance);

/*--------------------------------------------------------------------------*/
/* variables                                                                */
/*--------------------------------------------------------------------------*/

static FONT font = { NULL };

int init_board_cells[BOARD_YCELLS][BOARD_XCELLS] = {
   { ( CELL_DARK  | ACTOR_VALKYRIE            ), ( CELL_LIGHT | ACTOR_ARCHER ), ( CELL_DARK  ), ( CELL_LUMI  ), ( CELL_LUMI | CELL_POWER ), ( CELL_LUMI  ), ( CELL_LIGHT ), ( CELL_DARK  | ACTOR_MANTICORE ), ( CELL_LIGHT | ACTOR_BANSHEE                ) },
   { ( CELL_LIGHT | ACTOR_GOLEM               ), ( CELL_DARK  | ACTOR_KNIGHT ), ( CELL_LUMI  ), ( CELL_LIGHT ), ( CELL_LUMI              ), ( CELL_DARK  ), ( CELL_LUMI  ), ( CELL_LIGHT | ACTOR_GOBLIN    ), ( CELL_DARK  | ACTOR_TROLL                  ) },
   { ( CELL_DARK  | ACTOR_UNICORN             ), ( CELL_LUMI  | ACTOR_KNIGHT ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LUMI              ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LUMI  | ACTOR_GOBLIN    ), ( CELL_LIGHT | ACTOR_BASILISK               ) },
   { ( CELL_LUMI  | ACTOR_DJINNI              ), ( CELL_LIGHT | ACTOR_KNIGHT ), ( CELL_DARK  ), ( CELL_LIGHT ), ( CELL_LUMI              ), ( CELL_DARK  ), ( CELL_LIGHT ), ( CELL_DARK  | ACTOR_GOBLIN    ), ( CELL_LUMI  | ACTOR_SHAPESHIFTER           ) },
   { ( CELL_LIGHT | ACTOR_WIZARD | CELL_POWER ), ( CELL_LUMI  | ACTOR_KNIGHT ), ( CELL_LUMI  ), ( CELL_LUMI  ), ( CELL_LUMI | CELL_POWER ), ( CELL_LUMI  ), ( CELL_LUMI  ), ( CELL_LUMI  | ACTOR_GOBLIN    ), ( CELL_DARK  | ACTOR_SORCERESS | CELL_POWER ) },
   { ( CELL_LUMI  | ACTOR_PHOENIX             ), ( CELL_LIGHT | ACTOR_KNIGHT ), ( CELL_DARK  ), ( CELL_LIGHT ), ( CELL_LUMI              ), ( CELL_DARK  ), ( CELL_LIGHT ), ( CELL_DARK  | ACTOR_GOBLIN    ), ( CELL_LUMI  | ACTOR_DRAGON                 ) },
   { ( CELL_DARK  | ACTOR_UNICORN             ), ( CELL_LUMI  | ACTOR_KNIGHT ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LUMI              ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LUMI  | ACTOR_GOBLIN    ), ( CELL_LIGHT | ACTOR_BASILISK               ) },
   { ( CELL_LIGHT | ACTOR_GOLEM               ), ( CELL_DARK  | ACTOR_KNIGHT ), ( CELL_LUMI  ), ( CELL_LIGHT ), ( CELL_LUMI              ), ( CELL_DARK  ), ( CELL_LUMI  ), ( CELL_LIGHT | ACTOR_GOBLIN    ), ( CELL_DARK  | ACTOR_TROLL                  ) },
   { ( CELL_DARK  | ACTOR_VALKYRIE            ), ( CELL_LIGHT | ACTOR_ARCHER ), ( CELL_DARK  ), ( CELL_LUMI  ), ( CELL_LUMI | CELL_POWER ), ( CELL_LUMI  ), ( CELL_LIGHT ), ( CELL_DARK  | ACTOR_MANTICORE ), ( CELL_LIGHT | ACTOR_BANSHEE                ) }
};

/* Debugging for AI */
/* **************** */


/* 
   int init_board_cells[BOARD_YCELLS][BOARD_XCELLS] = {
   { ( CELL_DARK ), ( CELL_LIGHT ), ( CELL_DARK ), ( CELL_LUMI ), ( CELL_LUMI | CELL_POWER ), ( CELL_LUMI  ), ( CELL_LIGHT ), ( CELL_DARK   ), ( CELL_LIGHT            ) },
   { ( CELL_LIGHT  ), ( CELL_DARK   ), ( CELL_LUMI  ), ( CELL_LIGHT ), ( CELL_LUMI              ), ( CELL_DARK  ), ( CELL_LUMI  ), ( CELL_LIGHT     ), ( CELL_DARK                    ) },
   { ( CELL_DARK               ), ( CELL_LUMI   ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LUMI              ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LUMI      ), ( CELL_LIGHT                ) },
   { ( CELL_LUMI                ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LIGHT ), ( CELL_LUMI              ), ( CELL_DARK  ), ( CELL_LIGHT ), ( CELL_DARK      ), ( CELL_LUMI             ) },
   { ( CELL_LIGHT | ACTOR_KNIGHT | CELL_POWER ), ( CELL_LUMI   ), ( CELL_LUMI  ), ( CELL_LUMI  ), ( CELL_LUMI | CELL_POWER ), ( CELL_LUMI  ), ( CELL_LUMI  ), ( CELL_LUMI      ), ( CELL_DARK  | ACTOR_BANSHEE | CELL_POWER ) },
   { ( CELL_LUMI               ), ( CELL_LIGHT  ), ( CELL_DARK  ), ( CELL_LIGHT ), ( CELL_LUMI              ), ( CELL_DARK  ), ( CELL_LIGHT ), ( CELL_DARK      ), ( CELL_LUMI                   ) },
   { ( CELL_DARK               ), ( CELL_LUMI   ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LUMI              ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LUMI      ), ( CELL_LIGHT                ) },
   { ( CELL_LIGHT                ), ( CELL_DARK   ), ( CELL_LUMI  ), ( CELL_LIGHT ), ( CELL_LUMI              ), ( CELL_DARK  ), ( CELL_LUMI  ), ( CELL_LIGHT    ), ( CELL_DARK                    ) },
   { ( CELL_DARK              ), ( CELL_LIGHT  ), ( CELL_DARK  ), ( CELL_LUMI  ), ( CELL_LUMI | CELL_POWER ), ( CELL_LUMI  ), ( CELL_LIGHT ), ( CELL_DARK  ), ( CELL_LIGHT                 ) }
   };
*/

CELL board_cells[BOARD_YCELLS][BOARD_XCELLS];

static BOARD_DATA board = { 0 };               /* game status == none */

int board_turn;

int board_frame_time = 0;

/*--------------------------------------------------------------------------*/
/* spells                                                                   */
/*--------------------------------------------------------------------------*/

static char *spell_names[SPELL_COUNT_2] = {
   NULL, "teleport", "heal", "shift time", "exchange", "summon elemental",
   "revive", "imprison", "cease conjuring", NULL
};
static void (*spell_funcs[SPELL_COUNT_2])(int x, int y, int side) = {
   NULL, board_teleport, board_heal, board_shift_time, board_exchange,
   board_summon_elemental, board_revive, board_imprison,
   board_cease_conjuring, NULL
};
int spell_avails[3][SPELL_COUNT_2] = {
   { },                                 /* for light */
   { },                                 /* for dark */
   { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 }     /* default--used to initialize */
};

/*--------------------------------------------------------------------------*/
/* board_paint_cell                                                         */
/*--------------------------------------------------------------------------*/

void board_paint_cell(int x, int y)
{
   int sx, sy;
   int height;

   sx = BOARD_X(x);
   sy = BOARD_Y(y);
   sprite_set_state(floor_sprite, STATE_BOARD,
                    (board_cells[y][x].flags & CELL_LIGHT) ? FLOOR_LIGHT :
                    (board_cells[y][x].flags & CELL_DARK) ? FLOOR_DARK :
                    board.lumi);
   height = (board.ssp.count != 0) ? board.ssp.count : CELL_YSIZE;
   sprite_paint_clipped(floor_sprite, STATE_BOARD, sx, sy,
                        0, 0, CELL_XSIZE, height);
   if (board_cells[y][x].flags & CELL_POWER)
      sprite_paint(floor_sprite, STATE_POWER, sx, sy);
   if (board.ssp.count == 0 && board_cells[y][x].actor != NULL &&
       (board.state != SELECT_TARGET || x != board.ax0 || y != board.ay0))
      sprite_paint(board_cells[y][x].actor->sprite, SPRITE_STOP, sx, sy);

   board.any_output = 1;
}

/*--------------------------------------------------------------------------*/
/* board_paint_cursor                                                       */
/*--------------------------------------------------------------------------*/

void board_paint_cursor(void)
{
   int sx, sy;

   if (board.state == SELECT_GAME_OVER)
      return;

   sx = BOARD_X(0) + board.cx;
   sy = BOARD_Y(0) + board.cy;
   if (board.state == SELECT_TARGET || board.state == L_SELECT_TARGET) {
      if (board.state == L_SELECT_TARGET)
         sprite_paint(board.elem.actor->sprite,
                      sprite_get_state(board.elem.actor->sprite), sx, sy);
      else
         sprite_paint(board_cells[board.ay0][board.ax0].actor->sprite,
                      ( (sx % CELL_XSIZE == 0 && sy % CELL_YSIZE == 0)
                        ? SPRITE_STOP : board.cstate ),
                      sx, sy);
      sx = BOARD_X(board.ax0);
      sy = BOARD_Y(board.ay0);
   }
   sprite_set_state(cursor_sprite, STATE_BOARD, board.side);
   sprite_paint(cursor_sprite, STATE_BOARD, sx, sy);

   board.any_output = 1;
}

/*--------------------------------------------------------------------------*/
/* board_message                                                            */
/*--------------------------------------------------------------------------*/

void board_message(char *format, ...)
{
   va_list ap;
   char msg[64];
   int width, height;

   if (format != NULL) {
      va_start(ap, format);
      vsprintf(msg, format, ap);
      va_end(ap);
      if (strcmp(msg, board.message) == 0)
         return;
   } else
      strcpy(msg, board.message);

   canvas_font_size(msg, font.ptr, &width, &height);
   canvas_rectangle(0, CANVAS_HEIGHT - CELL_YSIZE,
                    CANVAS_WIDTH, CELL_YSIZE, font.bg);
   canvas_font_print(msg, (CANVAS_WIDTH - width) / 2, CANVAS_HEIGHT - height,
                     font.ptr, font.fg);
   strcpy(board.message, msg);

   sprintf(msg, "(%c%c%d)", (board.lumi_d == -1 ? '-' : '+'),
                            (board.lumi <= 3 ? 'D' : 'L'),
                            board.lumi);
   canvas_font_size(msg, font.ptr, &width, &height);
   canvas_font_print(msg, CANVAS_WIDTH - width, CANVAS_HEIGHT - height,
                     font.ptr, font.fg);

   board.any_output = 1;

#ifdef AUTOPILOT
   printf("board:  %s\n", board.message);
#endif
}

/*--------------------------------------------------------------------------*/
/* board_ssp_init                                                           */
/*--------------------------------------------------------------------------*/

void board_ssp_init(int x1, int y1, int x2, int y2, int both)
{
   if (x1 != x2 || y1 != y2)
      board_paint_cell(x2, y2);         /* repaint cell to remove cursor */
   board.ssp.count = 1;
   board.ssp.x1 = x1;
   board.ssp.y1 = y1;
   board.ssp.x2 = x2;
   board.ssp.y2 = y2;
   board.ssp.both = both;
}

/*--------------------------------------------------------------------------*/
/* board_ssp_frame                                                          */
/*--------------------------------------------------------------------------*/

void board_ssp_frame(void)
{
   if (board.ssp.both) {
      board_paint_cell(board.ssp.x1, board.ssp.y1);
      board_paint_cell(board.ssp.x2, board.ssp.y2);
      sprite_paint_clipped(board_cells[board.ssp.y1][board.ssp.x1].actor->sprite,
                           SPRITE_STOP, BOARD_X(board.ssp.x2), BOARD_Y(board.ssp.y2),
                           0, 0, CELL_XSIZE, board.ssp.count);
      sprite_paint_clipped(board_cells[board.ssp.y2][board.ssp.x2].actor->sprite,
                           SPRITE_STOP, BOARD_X(board.ssp.x1), BOARD_Y(board.ssp.y1),
                           0, 0, CELL_XSIZE, board.ssp.count);
   } else {
      board_paint_cell(board.ssp.x1, board.ssp.y1);
      if ((board.ssp.x1 != board.ssp.x2 || board.ssp.y1 != board.ssp.y2) &&
          (board_cells[board.ssp.y2][board.ssp.x2].actor != NULL))
         sprite_paint_clipped(board_cells[board.ssp.y2][board.ssp.x2].actor->sprite,
                              SPRITE_STOP, BOARD_X(board.ssp.x2), BOARD_Y(board.ssp.y2),
                              0, 0, CELL_XSIZE, board.ssp.count);
      sprite_paint_clipped(board_cells[board.ssp.y1][board.ssp.x1].actor->sprite,
                           SPRITE_STOP, BOARD_X(board.ssp.x2), BOARD_Y(board.ssp.y2),
                           0, 0, CELL_XSIZE, board.ssp.count);
   }
   if (board.ssp.count == CELL_YSIZE) {
      /* restart the spell that requested ssp */
      board.ssp.count = 0;
      board.ssp.complete = 1;
      board_spell_done();
      board.ssp.complete = 0;
   } else
      board.ssp.count++;
}

/*--------------------------------------------------------------------------*/
/* board_init_cell                                                          */
/*--------------------------------------------------------------------------*/

void board_init_cell(CELL *cell, int actor_num)
{
   cell->actor = malloc(sizeof(ACTOR));
   memcpy(cell->actor, &actors_list[actor_num], sizeof(ACTOR));
   cell->actor->sprite = sprite_copy(actors_list[actor_num].sprite, 0);
   sprite_set_state(cell->actor->sprite, (cell->actor->type & ACTOR_LIGHT ? STATE_MOVE_RIGHT : STATE_MOVE_LEFT), 0);   
}

/*--------------------------------------------------------------------------*/
/* board_clear_cell                                                         */
/*--------------------------------------------------------------------------*/

void board_clear_cell(CELL *cell)
{
   sprite_free(cell->actor->sprite);
   free(cell->actor);
   cell->actor = NULL;
   cell->flags &= ~CELL_IMPRISON;
}

/*--------------------------------------------------------------------------*/
/* board_reset_actor                                                        */
/*--------------------------------------------------------------------------*/

void board_reset_actor(ACTOR *actor)
{
   if (!(actor->type & ACTOR_ELEMENTAL))
      sprite_set_state(actor->sprite,
                       actor->type & ACTOR_LIGHT ? STATE_MOVE_RIGHT : STATE_MOVE_LEFT, 0);
}

/*--------------------------------------------------------------------------*/
/* board_iface_turn                                                         */
/*--------------------------------------------------------------------------*/

void board_iface_turn(void)
{
   board_turn = board.turn;
   iface_turn(board.side, IFACE_BOARD);
}

/*--------------------------------------------------------------------------*/
/* board_verify_move                                                        */
/*--------------------------------------------------------------------------*/

int board_verify_move(int state)
{
   static int opposite_state[] = { 0, STATE_MOVE_DOWN, STATE_MOVE_UP, STATE_MOVE_RIGHT, STATE_MOVE_LEFT };
   ACTOR *actor;
   int x0, y0, x1, y1;
   int distance;

   actor = board_cells[board.ay0][board.ax0].actor;
   x0 = board.cx / CELL_XSIZE;
   y0 = board.cy / CELL_YSIZE;
   x1 = max(0, min(BOARD_XCELLS - 1, x0 + state_move_x_step[state]));
   y1 = max(0, min(BOARD_YCELLS - 1, y0 + state_move_y_step[state]));

   /* fly actor checks:  only requirement is that it is within the range */

   if (actor->type & ACTOR_FLY) {
      distance = max(abs(x1 - board.ax0), abs(y1 - board.ay0));
      if (distance > actor->distance) {
         sprite_set_state(board_cells[board.ay0][board.ax0].actor->sprite, state, 0);
         board_message("alas, master, you have moved your limit");
         return 0;
      }
      return 1;
   }

   /* ground checkss:  first, cannot pass over opponent actors */

   if ((x0 != board.ax0 || y0 != board.ay0) &&
       board_cells[y0][x0].actor != NULL &&
       state != opposite_state[sprite_get_state(board_cells[board.ay0][board.ax0].actor->sprite)]) {
      board_message("you must attack or retreat");
      return 0;
   }

   /* check if it is possible to find a route on the board between */
   /* (x0,y0) and (x1,y1) for the creature standing at (x0,y0)     */

   if ((x1 != board.ax0 || y1 != board.ay0) &&
       board_get_route(board.ax0, board.ay0, x1, y1) == NULL) {
      sprite_set_state(board_cells[board.ay0][board.ax0].actor->sprite, state, 0);

      /* if it isn't possible, it might be because the creature is */
      /* blocked by an opponent, or because it's just too far      */
       
      if (actor->type & ACTOR_GROUND &&
          board_cells[y1][x1].actor != NULL &&
          (board_cells[y1][x1].actor->type & ACTOR_LIGHT) ==
             (board_cells[board.ay0][board.ax0].actor->type & ACTOR_LIGHT))
         board_message("the square ahead is occupied");
      else
         board_message("alas, master, you have moved your limit");
      return 0;
   }

   return 1;
}

/*--------------------------------------------------------------------------*/
/* board_check_win                                                          */
/*--------------------------------------------------------------------------*/

int board_check_win(void)
{
   static char *msgs[] = {  NULL,  "the dark side wins",
      "the light side wins",  "it is a tie!"  };
   int x, y;
   CELL *cell;
   int is_dark;
   int num_light = 0, num_dark = 0;
   int num_pp = 0, num_light_pp = 0, num_dark_pp = 0;
   int winner = 0;
   int old_side;

   for (y = 0; y < BOARD_YCELLS; y++)
      for (x = 0; x < BOARD_XCELLS; x++) {
         cell = &board_cells[y][x];
         if (cell->flags & CELL_POWER)
            num_pp++;
         if (cell->actor != NULL) {
            is_dark = ((cell->actor->type & ACTOR_LIGHT) == 0);
            /* count an actor for its side only if it isn't imprisoned; */
            /* or if it is imprisoned, but its side has just played.    */
            if ((cell->flags & CELL_IMPRISON) == 0 || board.side == is_dark) {
               /* count it only if it can be picked. */
               old_side = board.side;
               board.side = (cell->actor->type & ACTOR_LIGHT) ? 0 : 1;
               if (board_is_pickable(x, y, 0)) {
                  num_light += is_dark == 0;
                  num_dark  += is_dark == 1;
               }
               board.side = old_side;
            }
            /* count control of power points */
            if (cell->flags & CELL_POWER) {
               num_light_pp += is_dark == 0;
               num_dark_pp  += is_dark == 1;
            }
         }
      }

   if (num_light == 0 || num_dark_pp == num_pp)
      winner++;                         /* dark wins */
   if (num_dark == 0 || num_light_pp == num_pp)
      winner += 2;                      /* light wins */
   if (winner != 0) {
      board_message("the game is over.  %s", msgs[winner]);
      board.state = SELECT_GAME_OVER;
      audio_end_game(!(winner - 1));
      return 1;
   }
   return 0;
}

/*--------------------------------------------------------------------------*/
/* board_end_turn                                                           */
/*--------------------------------------------------------------------------*/

void board_end_turn(int swap_side)
{
   int x, y;
   CELL *cell;
   ACTOR *actor;

   board.state = SELECT_SOURCE;
   board.ax0 = -1;

   if (swap_side) {                     /* otherwise just reset this turn */
      board.turn++;
      if (board.turn % 2 == 0) {
         board.lumi += board.lumi_d;
         if (board.lumi == LUMI_DARKEST || board.lumi == LUMI_LIGHTEST)
            board.lumi_d = -board.lumi_d;
         for (y = 0; y < BOARD_YCELLS; y++)
            for (x = 0; x < BOARD_XCELLS; x++)
               if (board_cells[y][x].actor != NULL) {
                  cell = &board_cells[y][x];
                  actor = cell->actor;
                  /* un-imprison light actor when luminance cycle is lightest; */
                  /* un-imprison dark actor when luminance cycle is darkest.   */
                  if (cell->flags & CELL_IMPRISON &&
                      ((board.lumi == LUMI_LIGHTEST && actor->type & ACTOR_LIGHT) ||
                       (board.lumi == LUMI_DARKEST && !(actor->type & ACTOR_LIGHT))))
                     cell->flags ^= CELL_IMPRISON;
                  /* heal actors, twice as fast if on a power point */
                  actor->strength = min(actors_list[actor->type & ACTOR_MASK].strength,
                     actor->strength + 2 * (1 + ((cell->flags & CELL_POWER) == CELL_POWER)));
               }
      }

      if (board.elem.actor != NULL)
         board_clear_cell(&board.elem);
      if (board_check_win()) {          /* important that check_win() be */
         board_refresh();               /*    called only after un-imprison */
         return;                        /*    is done */
      }
      board.side = !board.side;

      board.cx = (board.side == 0) ? CELL_X(0) : CELL_X(BOARD_XCELLS - 1);
      board.cy = CELL_Y(BOARD_YCELLS / 2);
      board.cstate = 0;
      audio_start_turn(board.side);
   }

   sprintf(board.message, "the %s side plays", !board.side ? "light" : "dark");
   board_refresh();
   board_iface_turn();
}

/*--------------------------------------------------------------------------*/
/* board_field                                                              */
/*--------------------------------------------------------------------------*/

void board_field(int xd, int yd, int xa, int ya)
{
   static CELL *defender, *attacker;
   static int light_attacks;
   ACTOR *light, *dark, *winner;

   if (board.state != SELECT_FIELD) {
      defender = &board_cells[yd][xd];
      attacker = &board_cells[ya][xa];
      light = attacker->actor;
      if (light->type & ACTOR_LIGHT) {
         dark = defender->actor;
         light_attacks = 1;
      } else {
         dark = light;
         light = defender->actor;
         light_attacks = 0;
      }
      defender->flags &= ~CELL_LUMI_MASK;
      if (defender->flags & CELL_LIGHT)
         defender->flags |= FLOOR_LIGHT;
      if (defender->flags & CELL_DARK)
         defender->flags |= FLOOR_DARK;
      if (defender->flags & CELL_LUMI)
         defender->flags |= board.lumi;
      board_reset_actor(defender->actor);
      board_reset_actor(attacker->actor);
      field_start_game(light, dark, defender, BOARD_X(xd), BOARD_Y(yd));
      board.state = SELECT_FIELD;

   } else {
      winner = field_frame();
      if (winner != NULL) {
         if (winner == attacker->actor) {
            board_clear_cell(defender);
            if ((attacker->actor->type & ACTOR_ELEMENTAL) != ACTOR_ELEMENTAL) {
               board_reset_actor(attacker->actor);
               defender->actor = attacker->actor;
            }
         } else
            if (winner == defender->actor) {
               board_reset_actor(defender->actor);
               if ((attacker->actor->type & ACTOR_ELEMENTAL) != ACTOR_ELEMENTAL)
                  board_clear_cell(attacker);
            }
         attacker->actor = NULL;
         board_end_turn(1);
      }
   }
}

/*--------------------------------------------------------------------------*/
/* board_move_done                                                          */
/*--------------------------------------------------------------------------*/

void board_move_done(void)
{
   int x, y;

   x = board.cx / CELL_XSIZE;
   y = board.cy / CELL_YSIZE;
   if (x == board.ax0 && y == board.ay0) {
      if ((board_cells[board.ay0][board.ax0].actor->type & ACTOR_MASTER) == ACTOR_MASTER) {
         board_reset_actor(board_cells[board.ay0][board.ax0].actor);
         board_paint_cell(board.ax0, board.ay0);
         board_paint_cursor();
         board_message("the %s conjures a spell", board_cells[board.ay0][board.ax0].actor->name);
         board.state = SELECT_SPELL;
         board.spell = 0;
      } else
         board_message("this is where you started");
      return;
   }
   if (board_cells[y][x].actor != NULL) {
      if ((board_cells[y][x].actor->type & ACTOR_LIGHT) == (board_cells[board.ay0][board.ax0].actor->type & ACTOR_LIGHT))
         board_message("you cannot attack your own");
      else
         board_field(x, y, board.ax0, board.ay0);
      return;
   }

   board_cells[y][x].actor = board_cells[board.ay0][board.ax0].actor;
   board_cells[board.ay0][board.ax0].actor = NULL;
   board_reset_actor(board_cells[y][x].actor);
   board_end_turn(1);
}

/*--------------------------------------------------------------------------*/
/* board_spell_done                                                         */
/*--------------------------------------------------------------------------*/

void board_spell_done(void)
{
   int x, y;
   int side;

   x = board.cx / CELL_XSIZE;
   y = board.cy / CELL_YSIZE;
   if (board_cells[y][x].flags & CELL_POWER) {
      board_message("power points block all spells");
      board.state = SELECT_SPELL_DONE;
      board.spell = 0;
      return;
   }

   if (board_cells[y][x].flags & CELL_IMPRISON &&
       (board.state == T_SELECT_SOURCE ||
        board.state == X_SELECT_SOURCE ||
        board.state == X_SELECT_TARGET)) {
      board_message("alas, master, this %s is imprisoned",
                    board_cells[y][x].actor->name);
      board.state = SELECT_SPELL_DONE;
      board.spell = 0;
      return;
   }

   /* set side: -1 if empty cell, 0 if light, 1 if dark */
   if (board_cells[y][x].actor != NULL)
      side = (board_cells[y][x].actor->type & ACTOR_LIGHT) != ACTOR_LIGHT;
   else
      side = -1;

   spell_funcs[board.spell](x, y, side);
}

/*--------------------------------------------------------------------------*/
/* board_input                                                              */
/*--------------------------------------------------------------------------*/

void board_input(void)
{
   int state, state_last;

   iface_frame();
   if (board.game_status == 0)
      return;

   if (board.state == SELECT_TARGET &&
       board_cells[board.ay0][board.ax0].actor->type & ACTOR_GROUND)
      state_last = STATE_MOVE_RIGHT;
   else
      state_last = STATE_MOVE_DOWN_RIGHT;
   for (state = state_last; state >= STATE_MOVE_FIRST; state--)
      if (iface_key_down(state))
         if (board.state != SELECT_TARGET ||
             (board.state == SELECT_TARGET && board_verify_move(state))) {
            board.cstate = state;
            return;
         }

   if (iface_key_down(STATE_FIRE))
      board.fire_down = 1;
   if (board.fire_down && !iface_key_down(STATE_FIRE)) {
      board.fire_down = 0;
      if (board.state == SELECT_SOURCE) {
         if (board_is_pickable(board.cx / CELL_XSIZE, board.cy / CELL_YSIZE, 1)) {
            board.ax0 = board.cx / CELL_XSIZE;
            board.ay0 = board.cy / CELL_YSIZE;
            board.state = SELECT_TARGET;
         }
      } else
         if (board.state == SELECT_TARGET)
            board_move_done();
         else                           /* all other states are spells */
            board_spell_done();         /* (except SELECT_FIELD, but we'll */
   }                                    /* never get here in that state) */
}

/*--------------------------------------------------------------------------*/
/* board_cursor                                                             */
/*--------------------------------------------------------------------------*/

void board_cursor(void)
{
   int old_state;
   int old_cx, old_cy;
   
   old_state = board.state;
   old_cx = board.cx;
   old_cy = board.cy;
   board.cx = max(0, min((BOARD_XCELLS - 1) * CELL_XSIZE, board.cx + state_move_x_step[board.cstate] * CELL_XSIZE / 8));
   board.cy = max(0, min((BOARD_YCELLS - 1) * CELL_YSIZE, board.cy + state_move_y_step[board.cstate] * CELL_YSIZE / 8));

   if (board.cx != old_cx || board.cy != old_cy) {
      board_paint_cell(old_cx / CELL_XSIZE, old_cy / CELL_YSIZE);
      if (old_cx % CELL_XSIZE != 0)
         board_paint_cell(old_cx / CELL_XSIZE + 1, old_cy / CELL_YSIZE);
      if (old_cy % CELL_YSIZE != 0)
         board_paint_cell(old_cx / CELL_XSIZE, old_cy / CELL_YSIZE + 1);
      if (old_cx % CELL_XSIZE != 0 && old_cy % CELL_YSIZE != 0)
         board_paint_cell(old_cx / CELL_XSIZE + 1, old_cy / CELL_YSIZE + 1);
      board_paint_cursor();
   }

   if (board.cx % CELL_XSIZE == 0 && board.cy % CELL_YSIZE == 0) {
      board.cstate = 0;
      board_input();
   }

   if (board.cstate != 0 || board.state != old_state) {
      if (board.state == SELECT_SOURCE)
         board_message("the %s side plays", !board.side ? "light" : "dark");
      if (board.state == SELECT_TARGET)
         board_message("%s %s (%s %d)",
                       board_cells[board.ay0][board.ax0].actor->type & ACTOR_LIGHT ? "light" : "dark",
                       board_cells[board.ay0][board.ax0].actor->name,
                       board_cells[board.ay0][board.ax0].actor->type & ACTOR_GROUND ? "ground" : "fly",
                       board_cells[board.ay0][board.ax0].actor->distance);
   }
}

/*--------------------------------------------------------------------------*/
/* board_spell                                                              */
/*--------------------------------------------------------------------------*/

void board_spell(void)
{
   int fire_up = 0;
   int old_spell;

   iface_frame();
   if (board.game_status == 0)
      return;
   
   if (iface_key_down(STATE_FIRE))
      board.fire_down = 1;
   if (board.fire_down && !iface_key_down(STATE_FIRE)) {
      board.fire_down = 0;
      fire_up = 1;
   }
   if (board.spell == 0 || board.state == SELECT_SPELL_DONE) {
      if (fire_up) {
         if (board.state == SELECT_SPELL_DONE) {
            board.cx = CELL_X(board.ax0);
            board.cy = CELL_Y(board.ay0);
            board_end_turn(board.spell);
         } else
            for (board.spell = 1; !spell_avails[board.side][board.spell]; board.spell++)
               ;
      }
      return;
   }

   board_message("select spell: %s", spell_names[board.spell]);
   old_spell = board.spell;
   if (iface_key_down(STATE_MOVE_UP))
      board.prev_down = 1;
   if (board.prev_down && !iface_key_down(STATE_MOVE_UP)) {
      board.prev_down = 0;
      do {
         board.spell--;
      } while (board.spell > SPELL_TELEPORT && !spell_avails[board.side][board.spell]);
      if (!spell_avails[board.side][board.spell])
         board.spell = old_spell;
   }
   if (iface_key_down(STATE_MOVE_DOWN))
      board.next_down = 1;
   if (board.next_down && !iface_key_down(STATE_MOVE_DOWN)) {
      board.next_down = 0;
      do {
         board.spell++;
      } while (board.spell < SPELL_CEASE_CONJURING && !spell_avails[board.side][board.spell]);
      if (!spell_avails[board.side][board.spell])
         board.spell = old_spell;
   }

   if (fire_up)
      spell_funcs[board.spell](0, 0, 0);
}

/*--------------------------------------------------------------------------*/
/* board_teleport                                                           */
/*--------------------------------------------------------------------------*/

void board_teleport(int x, int y, int side)
{
   static int x0, y0;

   switch (board.state) {
      case SELECT_SPELL:
         board_message("who would you like to teleport?");
         board.state = T_SELECT_SOURCE;
         break;
      case T_SELECT_SOURCE:
         if (side != board.side || board_cells[y][x].flags & CELL_IMPRISON)
            return;
         x0 = x;
         y0 = y;
         board_message("where would you like to teleport it?");
         board.state = T_SELECT_TARGET;
         break;
      case T_SELECT_TARGET:
         if (side == board.side)
            return;
         if (!board.ssp.complete) {
            board_ssp_init(x0, y0, x, y, 0);
            return;
         }
         spell_avails[board.side][SPELL_TELEPORT] = 0;
         if (side == -1) {
            board_cells[y][x].actor = board_cells[y0][x0].actor;
            board_cells[y0][x0].actor = NULL;
            board_reset_actor(board_cells[y][x].actor);
            board_end_turn(1);
         } else
            board_field(x, y, x0, y0);
         break;
   }
}
   
/*--------------------------------------------------------------------------*/
/* board_heal                                                               */
/*--------------------------------------------------------------------------*/

void board_heal(int x, int y, int side)
{
   if (board.state == SELECT_SPELL) {
      board_message("who would you like to heal?");
      board.state = H_SELECT_TARGET;
   } else {                             /* H_SELECT_TARGET */
      if (side != board.side)
         return;
      spell_avails[board.side][SPELL_HEAL] = 0;
      board_cells[y][x].actor->strength =
         actors_list[board_cells[y][x].actor->type & ACTOR_MASK].strength;
      board_message("this %s is now healed", board_cells[y][x].actor->name);
      board.state = SELECT_SPELL_DONE;
      board.spell = 1;
   }
}

/*--------------------------------------------------------------------------*/
/* board_shift_time                                                         */
/*--------------------------------------------------------------------------*/

void board_shift_time(int x, int y, int side)
{
   ACTOR *actor;

   spell_avails[board.side][SPELL_SHIFT_TIME] = 0;
   board.lumi = (board.lumi == LUMI_DARKEST) ? LUMI_LIGHTEST :
                (board.lumi == LUMI_LIGHTEST) ? LUMI_DARKEST :
                board.lumi;
   board.lumi_d = -board.lumi_d;

   for (y = 0; y < BOARD_YCELLS; y++)
      for (x = 0; x < BOARD_XCELLS; x++)
         if (board_cells[y][x].flags & CELL_IMPRISON) {
            actor = board_cells[y][x].actor;
            /* un-imprison light actor if luminance cycle is lightest; */
            /* un-imprison dark actor if luminance cycle is darkest.   */
            if ((board.lumi == LUMI_LIGHTEST && actor->type & ACTOR_LIGHT) ||
                (board.lumi == LUMI_DARKEST && !(actor->type & ACTOR_LIGHT)))
               board_cells[y][x].flags ^= CELL_IMPRISON;
         }

   board_message("the flow of time is reversed");
   board.state = SELECT_SPELL_DONE;
   board.spell = 1;
}

/*--------------------------------------------------------------------------*/
/* board_exchange                                                           */
/*--------------------------------------------------------------------------*/

void board_exchange(int x, int y, int side)
{
   static int x0, y0;
   ACTOR *actor;

   switch (board.state) {
      case SELECT_SPELL:
         board_message("who would you like to exchange?");
         board.state = X_SELECT_SOURCE;
         break;
      case X_SELECT_SOURCE:
         x0 = x;
         y0 = y;
         board_message("whom to exchange with?");
         board.state = X_SELECT_TARGET;
         break;
      case X_SELECT_TARGET:
         if (!board.ssp.complete) {
            board_ssp_init(x0, y0, x, y, 1);
            return;
         }
         spell_avails[board.side][SPELL_EXCHANGE] = 0;
         actor = board_cells[y0][x0].actor;
         board_cells[y0][x0].actor = board_cells[y][x].actor;
         board_cells[y][x].actor = actor;
         board_reset_actor(board_cells[y0][x0].actor);
         board_reset_actor(board_cells[y][x].actor);
         board_end_turn(1);
         break;
   }
}

/*--------------------------------------------------------------------------*/
/* board_summon_elemental                                                   */
/*--------------------------------------------------------------------------*/

void board_summon_elemental(int x, int y, int side)
{
   static char *elem_names[] = { "an air", "an earth", "a fire", "a water" };
   int y0, x0;

   if (board.state == SELECT_SPELL) {
      if (board.elem.actor == NULL) {
         board.elem.flags = board_frame_time % 4;
         board_init_cell(&board.elem, ACTOR_AIR_ELEM + board.elem.flags);
      }
      board_message("%s elemental appears", elem_names[board.elem.flags]);
      sprite_set_state(board.elem.actor->sprite, STATE_AIR_ELEM + board.elem.flags, 0);
      board.state = L_SELECT_TARGET;
      board_paint_cursor();

   } else {                             /* L_SELECT_TARGET */
      if (side == board.side || side == -1)
         return;
      spell_avails[board.side][SPELL_SUMMON_ELEMENTAL] = 0;
      for (y0 = 0; y0 < BOARD_YCELLS; y++)
         for (x0 = 0; x0 < BOARD_XCELLS; x0++)
            if (board_cells[y0][x0].actor == NULL) {
               board.elem.actor->type |= (!board.side) * ACTOR_LIGHT;
               board_cells[y0][x0].actor = board.elem.actor;
               board_field(x, y, x0, y0);
               return;                  /* there should be lots of empty */
            }                           /* cells, so we will always get here */
   }
}

/*--------------------------------------------------------------------------*/
/* board_revive_check                                                       */
/*--------------------------------------------------------------------------*/

int board_revive_check(int *actors, int *cell_x, int *cell_y)
{
   int num_actors;
   int first, last, i;
   int y, x, dx;
   int init_count, real_count;
   int master_x, master_y;

   num_actors = 0;
   first = (board.side == 0) ? ACTOR_LIGHT_FIRST : ACTOR_DARK_FIRST;
   last = (board.side == 0) ? ACTOR_LIGHT_LAST : ACTOR_DARK_LAST;
   for (i = first; i <= last; i++) {
      init_count = real_count = 0;
      for (y = 0; y < BOARD_YCELLS; y++)
         for (x = 0; x < BOARD_XCELLS; x++) {
            if ((init_board_cells[y][x] & ACTOR_MASK) == i)
               init_count++;
            if (board_cells[y][x].actor != NULL && (board_cells[y][x].actor->type & ACTOR_MASK) == i)
               real_count++;
         }
      if (init_count != real_count)
         actors[num_actors++] = i;
   }
   if (num_actors == 0) {
      if (cell_x != NULL)               /* only if interactive mode */
         board_message("happily, master, all are alive");
      return 0;
   }
   actors[num_actors] = 0;

   board_find_actor((board.side == 0 ? ACTOR_WIZARD : ACTOR_SORCERESS),
                    &master_x, &master_y);
   dx = (board.side == 0) ? 1 : -1;
   for (y = master_y - 1; y <= master_y + 1; y++)
      for (x = master_x - dx; x != master_x + (dx * 2); x += dx)
         if (y >= 0 && y < BOARD_YCELLS && x >= 0 && x < BOARD_XCELLS &&
             board_cells[y][x].actor == NULL) {
            if (cell_x != NULL) {       /* only if interactive mode */
               *cell_x = x;
               *cell_y = y;
            }
            return 1;
         }
   if (cell_x != NULL)                  /* only if interactive mode */
      board_message("there is no open square near your mage");
   return 0;
}

/*--------------------------------------------------------------------------*/
/* board_revive_frame                                                       */
/*--------------------------------------------------------------------------*/

int board_revive_frame(int *i, int *old_i, int *actors)
{
   if (iface_key_down(STATE_FIRE))
      board.fire_down = 1;
   if (board.fire_down && !iface_key_down(STATE_FIRE)) {
      board.fire_down = 0;
      return 1;
   }

   if (iface_key_down(STATE_MOVE_UP))
      board.prev_down = 1;
   if (board.prev_down && !iface_key_down(STATE_MOVE_UP)) {
      board.prev_down = 0;
      if ((*i) > 0)
         (*i)--;
   }
   if (iface_key_down(STATE_MOVE_DOWN))
      board.next_down = 1;
   if (board.next_down && !iface_key_down(STATE_MOVE_DOWN)) {
      board.next_down = 0;
      if (actors[(*i)] != 0)
         (*i)++;
   }

   if (*i != *old_i) {
      sprite_set_state(cursor_sprite, STATE_BOARD, 1);  /* erase cursor */
      sprite_paint(cursor_sprite, STATE_BOARD,
                   (board.side == 0 ? CELL_XSIZE : CANVAS_WIDTH - CELL_XSIZE * 2),
                   (*old_i) * 40 + 32);
      sprite_set_state(cursor_sprite, STATE_BOARD, 2);  /* draw cursor */
      sprite_paint(cursor_sprite, STATE_BOARD,
                   (board.side == 0 ? CELL_XSIZE : CANVAS_WIDTH - CELL_XSIZE * 2),
                   (*i) * 40 + 32);
      board.any_output = 1;
   }

   return 0;
}

/*--------------------------------------------------------------------------*/
/* board_revive                                                             */
/*--------------------------------------------------------------------------*/

void board_revive(int x, int y, int side)
{
   static int actors[10], i;
   static int cell_x, cell_y;
   int old_i;

   if (board.ssp.complete) {
      board.cx = CELL_X(cell_x);        /* once ssp is complete, place */
      board.cy = CELL_Y(cell_y);        /*    cursor on revived cell */
      board_end_turn(1);
      return;
   }

   if (x >= 0) {
      if (!board_revive_check(actors, &cell_x, &cell_y)) {
         board.state = SELECT_SPELL_DONE;
         board.spell = 0;
         return;
      }
      board_message("select who to revive");
      for (i = 0; actors[i] != 0; i++)
         sprite_paint(actors_list[actors[i]].sprite,
                      (board.side == 0 ? STATE_MOVE_RIGHT : STATE_MOVE_LEFT),
                      (board.side == 0 ? CELL_XSIZE : CANVAS_WIDTH - CELL_XSIZE * 2),
                      i * 40 + 32);
      old_i = 0;                        /* force i != old_i, thus redraw */
      board.state = V_SELECT_SOURCE;
   } else
      old_i = i;

   if (board_revive_frame(&i, &old_i, actors)) {
      if (actors[i] != 0) {
         spell_avails[board.side][SPELL_REVIVE] = 0;
         board_init_cell(&board_cells[cell_y][cell_x], actors[i]);
         board_ssp_init(cell_x, cell_y, cell_x, cell_y, 0);
         board.cx = CELL_X(1);          /* make sure cursor is not on a */
         board.cy = CELL_Y(1);          /*    power cell (temporarily) */
      } else {
         board.cx = CELL_X(board.ax0);  /* put the cursor on the master */
         board.cy = CELL_Y(board.ay0);
         board_end_turn(0);
      }

   } else
      board.cy = i;                   /* for board_absolute_control_delta() */
}

/*--------------------------------------------------------------------------*/
/* board_imprison                                                           */
/*--------------------------------------------------------------------------*/

void board_imprison(int x, int y, int side)
{
   if (board.state == SELECT_SPELL) {
      if (!board_is_imprison_ok()) {
         board_message("the %s side cannot be imprisoned at this time",
                       board.side == 0 ? "dark" : "light");
         board.state = SELECT_SPELL_DONE;
         board.spell = 0;
      } else {
         board_message("who would you like to imprison?");
         board.state = M_SELECT_TARGET;
      }

   } else
      if (side != board.side) {
         spell_avails[board.side][SPELL_IMPRISON] = 0;
         board_cells[y][x].flags |= CELL_IMPRISON;
         board_message("this %s is now imprisoned",
                       board_cells[y][x].actor->name);
         board.state = SELECT_SPELL_DONE;
         board.spell = 1;
      }
}

/*--------------------------------------------------------------------------*/
/* board_cease_conjuring                                                    */
/*--------------------------------------------------------------------------*/

void board_cease_conjuring(int x, int y, int side)
{
   board_end_turn(0);                   /* reset this turn */
}

/*--------------------------------------------------------------------------*/
/* board_start_game                                                         */
/*--------------------------------------------------------------------------*/

void board_start_game(int light_first)
{
   int x, y;
   CELL *cell;

   /* setup font stuff */
   if (font.ptr == NULL) {
      font.ptr = canvas_font_load(FONT_NAME);
      font.fg = canvas_alloc_color(255, 255, 0);
      font.bg = canvas_alloc_color(255, 0, 0);
   }

   /* setup initial board[] data */
   memset(&board, 0, sizeof(board));
   board.game_status = 1;               /* game on! */
   board.side = !light_first;
   board.state = SELECT_SOURCE;
   board.lumi   = light_first ? LUMI_LIGHTEST : LUMI_DARKEST;
   board.lumi_d = light_first ? -1            : 1;
   board.cx     = light_first ? CELL_X(0)     : CELL_X(BOARD_XCELLS - 1);
   board.cy     = CELL_Y(BOARD_YCELLS / 2);
   board.ax0 = -1;
   board.elem.actor = NULL;
   sprintf(board.message,
           "the %s side goes first", !board.side ? "light" : "dark");
   board.any_output = 0;

   /* setup actors on the board */
   for (y = 0; y < BOARD_YCELLS; y++)
      for (x = 0; x < BOARD_XCELLS; x++) {
         cell = &board_cells[y][x];
         cell->flags = init_board_cells[y][x];
         if ((cell->flags & ACTOR_MASK) != 0)
            board_init_cell(cell, cell->flags & ACTOR_MASK);
         else
            cell->actor = NULL;

      }

   field_setup_rocks();

   /* setup global variables */
   memcpy(spell_avails[0], spell_avails[2], sizeof(spell_avails[2]));
   memcpy(spell_avails[1], spell_avails[2], sizeof(spell_avails[2]));
   board_frame_time = 0;
   board_refresh();
   board_iface_turn();
   audio_start_game();
   /*
   sleep(2);
   audio_start_turn(board.side);
   */
}

/*--------------------------------------------------------------------------*/
/* board_end_game                                                           */
/*--------------------------------------------------------------------------*/

int board_end_game(void)
{
   int x, y;

   if (board.game_status == 0)
      return 0;
   if (board.state == SELECT_FIELD)
      field_end_game();
   for (y = 0; y < BOARD_YCELLS; y++)
      for (x = 0; x < BOARD_XCELLS; x++)
         if (board_cells[y][x].actor != NULL) {
            if (board_cells[y][x].actor == board.elem.actor)
               board.elem.actor = NULL;
            board_clear_cell(&board_cells[y][x]);
         }
   if (board.elem.actor != NULL)
      board_clear_cell(&board.elem);
   board.game_status = 0;               /* game ends */
   board.state = SELECT_GAME_OVER;
   audio_terminate();
   return 1;
}

/*--------------------------------------------------------------------------*/
/* board_pause_game                                                         */
/*--------------------------------------------------------------------------*/

int board_pause_game(int pause)
{
   if (board.game_status == 0)
      return 0;
   if (pause != -1)
      board.game_status = pause ? -1 : 1;
   return 1;
}

/*--------------------------------------------------------------------------*/
/* board_refresh                                                            */
/*--------------------------------------------------------------------------*/

void board_refresh(void)
{
   int x, y;

   if (board.state == SELECT_FIELD) {
      field_refresh();
      return;
   }

   canvas_clear();
   for (y = 0; y < BOARD_YCELLS; y++)
      for (x = 0; x < BOARD_XCELLS; x++)
         board_paint_cell(x, y);
   board_message(NULL);
   board_paint_cursor();
   canvas_refresh();
}

/*--------------------------------------------------------------------------*/
/* board_frame                                                              */
/*--------------------------------------------------------------------------*/

int board_frame(void)
{
#ifdef AUTOPILOT
   if (board.turn == 1000) {
      printf("board:  game is taking too long, ending it.\n");
      board_end_game();
   }
#endif

   if (board.game_status != 1)
      return (board.state != SELECT_GAME_OVER);

   if (board.ssp.count != 0)
      board_ssp_frame();
   else {

      switch (board.state) {
         case SELECT_SPELL:
         case SELECT_SPELL_DONE:
            board_spell();
            break;
         case SELECT_FIELD:
            board_field(0, 0, 0, 0);
            break;
         case V_SELECT_SOURCE:
            iface_frame();
            if (board.game_status != 0)
               board_revive(-99, 0, 0);
            break;
         default:
            board_cursor();
            break;
      }
      if (board.state == SELECT_GAME_OVER)
         board_end_game();
   }

   if (board.any_output) {
      canvas_refresh();
      board.any_output = 0;
   }
   board_frame_time++;

   return (board.state != SELECT_GAME_OVER);
}

/*--------------------------------------------------------------------------*/
/* board_cell_lumi                                                          */
/*--------------------------------------------------------------------------*/

int board_cell_lumi(CELL *cell)
{
   if (cell->flags & CELL_DARK)
      return CELL_DARK | (LUMI_COUNT / 2);
   if (cell->flags & CELL_LIGHT)
      return CELL_LIGHT | (LUMI_COUNT / 2);
   if (board.lumi < LUMI_COUNT / 2)
      return CELL_DARK | ((LUMI_COUNT / 2) - board.lumi);
   else
      return CELL_LIGHT | (board.lumi - (LUMI_COUNT / 2) + 1);
}

/*--------------------------------------------------------------------------*/
/* board_is_pickable                                                        */
/*--------------------------------------------------------------------------*/

int board_is_pickable(int cx, int cy, int msg)
{
   int x0, y0;
   int x, y;
   int distance;

   x0 = cx;
   y0 = cy;
   if (board_cells[y0][x0].actor == NULL)
      return 0;
   if (!actor_is_side(board_cells[y0][x0].actor, board.side))
      return 0;
   if (board_cells[y0][x0].flags & CELL_IMPRISON) {
      if (msg)
         board_message("alas, master, this %s is imprisoned",
                       board_cells[y0][x0].actor->name);
      return 0;
   }
   if ((board_cells[y0][x0].actor->type & ACTOR_MASTER) == ACTOR_MASTER)
      return 1;
   distance = (board_cells[y0][x0].actor->type & ACTOR_GROUND) ? 1 : (board_cells[y0][x0].actor->distance);
   for (y = y0 - distance; y <= y0 + distance; y++)
      if (y >= 0 && y <= BOARD_YCELLS - 1)
         for (x = x0 - distance; x <= x0 + distance; x++) {
            if (x < 0 || x > BOARD_XCELLS - 1)
               continue;
            if (board_cells[y0][x0].actor->type & ACTOR_GROUND &&
                abs(x - x0) == 1 && abs(y - y0) == 1)
               continue;
            if (board_cells[y][x].actor == NULL)
               return 1;
            if (actor_is_side(board_cells[y][x].actor, !board.side))
               return 1;
         }
   if (msg)
      board_message("alas, master, this %s cannot move", board_cells[y0][x0].actor->name);
   return 0;
}

/*--------------------------------------------------------------------------*/
/* board_get_route_2                                                        */
/*--------------------------------------------------------------------------*/

int board_get_route_2(int x1, int y1, int x2, int y2, int *route,
                      int prev_state, int light, int ground, int distance)
{
   int state, state_last;
   int x, y;
   int count;

   if (distance == 0)
      return 0;
   state_last = ground ? STATE_MOVE_RIGHT : STATE_MOVE_DOWN_RIGHT;
   for (state = STATE_MOVE_FIRST; state <= state_last; state++) {
      if (state == state_opposite[prev_state])
         continue;
      x = max(0, min(BOARD_XCELLS - 1, x1 + state_move_x_step[state]));
      y = max(0, min(BOARD_YCELLS - 1, y1 + state_move_y_step[state]));
      if (x == x1 && y == y1)
         continue;
      if (ground && board_cells[y][x].actor != NULL &&
          (board_cells[y][x].actor->type & ACTOR_LIGHT) == light)
         continue;
      *route = state;
      if (x == x2 && y == y2)
         return 1;
      if (ground && board_cells[y][x].actor != NULL)
         continue;
      count = board_get_route_2(x, y, x2, y2, route + 1, state, light, ground, distance - 1);
      if (count != 0)
         return (count + 1);
   }
   return 0;
}

/*--------------------------------------------------------------------------*/
/* board_get_route                                                          */
/*--------------------------------------------------------------------------*/

int *board_get_route(int x1, int y1, int x2, int y2)
{
   static int route[16];
   ACTOR *actor;
   int count;

   route[0] = 0;
   if (x1 == x2 && y1 == y2)
      return NULL;
   actor = board_cells[y1][x1].actor;
   if (board_cells[y2][x2].actor != NULL &&
       (board_cells[y2][x2].actor->type & ACTOR_LIGHT) == (actor->type & ACTOR_LIGHT))
      return NULL;
   count = board_get_route_2(x1, y1, x2, y2, route,
                             0,
                             (actor->type & ACTOR_LIGHT),
                             (actor->type & ACTOR_GROUND),
                             actor->distance);
   route[count] = 0;
   return (count == 0) ? NULL : route;
}

/*--------------------------------------------------------------------------*/
/* board_find_actor                                                         */
/*--------------------------------------------------------------------------*/

int board_find_actor(int type, int *ax, int *ay)
{
   int x, y;

   for (y = 0; y < BOARD_YCELLS; y++)
      for (x = 0; x < BOARD_XCELLS; x++)
         if (board_cells[y][x].actor != NULL &&
             (board_cells[y][x].actor->type & ACTOR_MASK) == type) {
            *ax = x;
            *ay = y;
            return 1;
         }
   *ax = -1;
   *ay = -1;
   return 0;
}

/*--------------------------------------------------------------------------*/
/* board_is_imprison_ok                                                     */
/*--------------------------------------------------------------------------*/

int board_is_imprison_ok(void)
{
   int next_lumi;

   /* don't allow if luminance on next turn is max of enemy's side */
   next_lumi = board.lumi + board.lumi_d * ((board.turn + 1) % 2 == 0);
   if ((board.side == 0 && next_lumi == LUMI_DARKEST) ||
       (board.side == 1 && next_lumi == LUMI_LIGHTEST))
      return 0;
   return 1;
}

/*--------------------------------------------------------------------------*/
/* board_get_data                                                           */
/*--------------------------------------------------------------------------*/

void board_get_data(int *_side, int *_turn, int *_lumi, int *_lumi_d)
{
   *_side = board.side;
   *_turn = board.turn;
   *_lumi = board.lumi;
   *_lumi_d = board.lumi_d;
}

/*--------------------------------------------------------------------------*/
/* board_absolute_control_delta                                             */
/*--------------------------------------------------------------------------*/

void board_absolute_control_delta(int *dx, int *dy)
{
   int spell;
   int curr_x, curr_y;
   int target_x, target_y;

   switch (board.state) {
      case SELECT_SPELL:
         *dx = 0;
         /* don't just hold the direction down, or the selection will get
          * stuck on the first option */
         if (board.prev_down || board.next_down)
            *dy = 0;
         else {
            *dy = (*dy / CELL_YSIZE) - 2;
            for (spell = 0; spell < board.spell; spell++)
               if (spell_avails[board.side][spell])
                  (*dy)--;
         }
         break;
      case V_SELECT_SOURCE:
         *dx = 0;
         /* don't just hold the direction down, or the selection will get
          * stuck on the first option */
         if (board.prev_down || board.next_down)
            *dy = 0;
         else
            *dy = ((*dy - CELL_YSIZE) / V_CELL_YSIZE) - board.cy;
         break;
      default:
         *dx = (*dx / CELL_XSIZE) * CELL_XSIZE - BOARD_X(0) - board.cx;
         *dy = (*dy / CELL_YSIZE) * CELL_YSIZE - BOARD_Y(0) - board.cy;
         /* elimiate diagonals when moving ground units */
         if (*dx && *dy && board_cells[board.ay0][board.ax0].actor &&
             (board_cells[board.ay0][board.ax0].actor->type & ACTOR_GROUND)) {
            curr_x = board.cx / CELL_XSIZE;
            curr_y = board.cy / CELL_YSIZE;
            target_x = curr_x + ((*dx > 0) ? 1 : -1);
            target_y = curr_y + ((*dy > 0) ? 1 : -1);
            if (target_x >= 0 && target_x < BOARD_XCELLS &&
                target_y >= 0 && target_y < BOARD_YCELLS) {
               if (board_cells[target_y][curr_x].actor ||
                   (!board_cells[curr_y][target_x].actor &&
                    (curr_x - board.ax0 > 0) != (*dx > 0)))
                  *dy = 0;
               else
                  *dx = 0;
            }
         }
         break;
   }
}

Generated by  Doxygen 1.6.0   Back to index