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

field.c

/*--------------------------------------------------------------------------*/
/* battle field                                                             */
/*--------------------------------------------------------------------------*/

#include <config.h>

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

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

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

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

#define NUM_ROCKS      12
#define ROCK_DELAY     2                /* rocks delay time in frames */
#define ROCK_FRAMES    (FPS * 5)        /* frames between luminance change */

#define BAR_WIDTH      15               /* life and ammo bar sizes */
#define LIFE_BAR       0                /* to make field_paint_bar paint */
#define AMMO_BAR       1                /*    the life or ammo bars */

#define BLINK_FRAMES   2                /* number of frames to show/hide */
#define NUM_BLINKS     20               /* number of blinks to make */

#define HAND_DURATION  (FPS * 0.3)      /* sword/club attack length */
#define CLOUD_DURATION (FPS * 1.0)      /* cloud attack length */
#define SHOOT_DURATION (FPS * 0.3)      /* how long it takes to shoot */

#define CLOUD_PART(x)  (x)
#define CLOUD_CENTER   CLOUD_PART(0)    /* time in frames before center, */
#define CLOUD_UPPER    CLOUD_PART(1)    /*    upper, and lower parts of  */
#define CLOUD_LOWER    CLOUD_PART(2)    /*    the cloud appear           */
#define CLOUD_FACTOR   2                /* damage every x frames */

#define PROLOG_FRAMES  75               /* length in frames of prolog */
#define EPILOG_FRAMES  50               /* length in frames of epilog */

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

typedef struct {
   int x, y;                            /* rock position */
   int lumi, lumi_d;                    /* luminance value and direction */
} ROCK;

typedef struct {
   FIELD_ACTOR light, dark;             /* the duelling actors */
   ROCK rocks[NUM_ROCKS];               /* rocks on the field */
   int cell_x, cell_y;                  /* (x,y) of disputed cell */
   int prolog_countdown;                /* if displaying prolog */
   int epilog_countdown;                /* if displaying epilog */
   int repaint_life;                    /* life probably needs repainting */
   int repaint_ammo;                    /* ammo probably needs repainting */
   int any_output;                      /* if any output emitted */
} FIELD_DATA;

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

unsigned char field_cells[FIELD_YCELLS][FIELD_XCELLS];
FIELD_ACTOR *field_me, *field_he;
int field_frame_time;

static FIELD_DATA field = { };

static int back_color, light_color, dark_color, life_color, ammo_color;
static void *font = NULL;

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

static void field_paint_cell(int x, int y);
static void field_paint_bar(int ammo, FIELD_ACTOR *fa);
static void field_paint_actor(FIELD_ACTOR *fa);
static void field_paint_weapon(FIELD_ACTOR *fw);
static int field_clear_actor(FIELD_ACTOR *fa, FIELD_ACTOR *ofa);
static void field_ssp(FIELD_ACTOR *fa);
static void field_init(void);
static void field_image_dim_func(int width, int height, unsigned short *pixmap, char *mask);
static void field_setup_actor(FIELD_ACTOR *fa, ACTOR *actor, CELL *cell);
static void field_animate(FIELD_ACTOR *fa);
static int field_player_frozen(FIELD_ACTOR *fa);
static void field_player(FIELD_ACTOR *fa);
static void field_animate_weapon(FIELD_ACTOR *fa);
static int field_weapon_collision(FIELD_ACTOR *fw);
static void field_weapon(FIELD_ACTOR *fa);
static void field_lumi_cycle(void);
static void field_print(char *msg, int row, int color);
static void field_prolog_epilog_move(FIELD_ACTOR *fa);
static void field_prolog(void);
static ACTOR *field_epilog(void);
static void field_frame_paint(FIELD_DATA *old_field);

/*--------------------------------------------------------------------------*/
/* field_setup_rocks                                                        */
/*--------------------------------------------------------------------------*/

void field_setup_rocks(void)
{
   int cx, cy;
   CELL *cell;
   ROCK *rocks;
   int i, j;

   for (cy = 0; cy < BOARD_YCELLS; cy++)
      for (cx = 0; cx < BOARD_XCELLS; cx++) {
         cell = &board_cells[cy][cx];
         if (cell->rocks == NULL)
            cell->rocks = malloc(sizeof(field.rocks));
         rocks = cell->rocks;

         for (i = 0; i < NUM_ROCKS; i++) {
            rocks[i].x = 1 + main_random() % (FIELD_XCELLS - 2);
            rocks[i].y = 1 + main_random() % (FIELD_YCELLS - 2);
            rocks[i].lumi = (i % 6 == 0) ? ROCK_WALKABLE :
                            (i % 2 == 0) ? ROCK_DARKEST : ROCK_LIGHTEST;
            rocks[i].lumi_d = (rocks[i].lumi == ROCK_DARKEST) ? 1 :
                              (rocks[i].lumi == ROCK_LIGHTEST) ? -1 :
                              (i % 2 == 0) ? 1 : -1;
            for (j = 0; j < i; j++)
               if (rocks[i].x >= rocks[j].x - 1 && rocks[i].x <= rocks[j].x + 1 &&
                   rocks[i].y >= rocks[j].y - 1 && rocks[i].y <= rocks[j].y + 1) {
                  i--;
                  break;
               }
         }
      }
}

/*--------------------------------------------------------------------------*/
/* field_paint_cell                                                         */
/*--------------------------------------------------------------------------*/

void field_paint_cell(int x, int y)
{
   int i;

   if (x < 0 || x >= FIELD_XCELLS || y < 0 || y >= FIELD_YCELLS) {
      canvas_rectangle(FIELD_X(x), FIELD_Y(y), CELL_XSIZE, CELL_YSIZE, back_color);
      field.repaint_life = 1;
      field.repaint_ammo = 1;
      return;
   }

   /*
   sprite_set_state(floor_sprite, STATE_FIELD, (y + x) % 2);
   sprite_paint(floor_sprite, STATE_FIELD, FIELD_X(x), FIELD_Y(y));
   */
   i = field_cells[y][x] >> ROCK_IX_SHIFT;
   if (i != ROCK_NONE)
      i = field.rocks[i].lumi - 1;
   else
      i = ROCK_LIGHTEST - 1;
   sprite_set_state(floor_sprite, STATE_FIELD, i);
   sprite_paint(floor_sprite, STATE_FIELD, FIELD_X(x), FIELD_Y(y));
   field.any_output = 1;
}

/*--------------------------------------------------------------------------*/
/* field_paint_bar                                                          */
/*--------------------------------------------------------------------------*/

void field_paint_bar(int ammo, FIELD_ACTOR *fa)
{
   int offset, height, color;

   if (ammo) {
      offset = BAR_WIDTH + 1;
      height = max(0, fa->weapon->actor->recharge - (field_frame_time - fa->weapon->fire_time));
      color = ammo_color;
   } else {
      offset = 0;
      height = CANVAS_HEIGHT * fa->life / 100;
      color = life_color;
   }
   if (fa == &field.dark)
      offset = CANVAS_WIDTH - BAR_WIDTH - offset;
   canvas_rectangle(offset, 0, BAR_WIDTH - 1, CANVAS_HEIGHT - height, back_color);
   canvas_rectangle(offset, CANVAS_HEIGHT - height, BAR_WIDTH - 1, height, color);
   field.any_output = 1;
}

/*--------------------------------------------------------------------------*/
/* field_paint_actor                                                        */
/*--------------------------------------------------------------------------*/

void field_paint_actor(FIELD_ACTOR *fa)
{
   int state;
   int weapon_duration = 0;

   fa->redraw = 0;
   field.any_output = 1;                /* output will be made here */

   if (fa == &field.dark && fa->orig_actor != fa->actor &&
       field.prolog_countdown + field.epilog_countdown != 0) {
      field_ssp(fa);
      return;
   }

   if (!fa->blink || (field_frame_time / BLINK_FRAMES) % 2 == 0) {
      state = SPRITE_STOP; 
      if (fa->actor->type & ACTOR_ELEMENTAL) {
         if (fa->state != 0)
            state = sprite_get_state(fa->sprite);
      } else {
         if (fa->x % CELL_XSIZE == 0 && fa->y % CELL_YSIZE == 0) {
            if (fa->weapon->state != 0 &&
                (fa->weapon->actor->type & ACTOR_WEAPON_HAND) == ACTOR_WEAPON_HAND)
               weapon_duration = HAND_DURATION;
            else
               if (fa->fire_state != 0 &&
                   (fa->weapon->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT)
                  weapon_duration = SHOOT_DURATION;
            if (weapon_duration != 0 && !WAS_TIME(fa->weapon, weapon_duration))
               state = fa->fire_state - STATE_MOVE_FIRST + STATE_FIRE_FIRST;
            else
               state = fa->last_state;
         } else
            state = sprite_get_state(fa->sprite);
      }
      if (fa->blink || fa->inanimate)
         sprite_set_state(fa->sprite, state, 0);
      sprite_paint(fa->sprite, state, fa->x + FIELD_X(0), fa->y + FIELD_Y(0));
   }
}

/*--------------------------------------------------------------------------*/
/* field_paint_weapon                                                       */
/*--------------------------------------------------------------------------*/

void field_paint_weapon(FIELD_ACTOR *fw)
{
   fw->redraw = 0;

   if (fw->state == 0)
      return;

   if ((fw->actor->type & ACTOR_WEAPON_CLOUD) != ACTOR_WEAPON_CLOUD) {
      sprite_paint(fw->sprite,
                   fw->state - STATE_MOVE_FIRST + STATE_FIRE_FIRST,
                   fw->x + FIELD_X(0), fw->y + FIELD_Y(0));
      return;
   }

   sprite_paint(fw->sprite, STATE_FIRE_ANY,
                fw->x + FIELD_X(0), fw->y + FIELD_Y(0));
   if (WAS_TIME(fw, CLOUD_CENTER)) {
      sprite_paint(fw->sprite, STATE_FIRE_LEFT,
                   fw->x + FIELD_X(-2), fw->y + FIELD_Y(0));
      sprite_paint(fw->sprite, STATE_FIRE_ANY,
                   fw->x + FIELD_X(-1), fw->y + FIELD_Y(0));
      sprite_paint(fw->sprite, STATE_FIRE_ANY,
                   fw->x + FIELD_X(1), fw->y + FIELD_Y(0));
      sprite_paint(fw->sprite, STATE_FIRE_RIGHT,
                   fw->x + FIELD_X(2), fw->y + FIELD_Y(0));
   }
   if (WAS_TIME(fw, CLOUD_UPPER)) {
      sprite_paint(fw->sprite, STATE_FIRE_UP_LEFT,
                   fw->x + FIELD_X(-1), fw->y + FIELD_Y(-1));
      sprite_paint(fw->sprite, STATE_FIRE_ANY,
                   fw->x + FIELD_X(0), fw->y + FIELD_Y(-1));
      sprite_paint(fw->sprite, STATE_FIRE_UP_RIGHT,
                   fw->x + FIELD_X(1), fw->y + FIELD_Y(-1));
   }
   if (WAS_TIME(fw, CLOUD_LOWER)) {
      sprite_paint(fw->sprite, STATE_FIRE_DOWN_LEFT,
                   fw->x + FIELD_X(-1), fw->y + FIELD_Y(1));
      sprite_paint(fw->sprite, STATE_FIRE_ANY,
                   fw->x + FIELD_X(0), fw->y + FIELD_Y(1));
      sprite_paint(fw->sprite, STATE_FIRE_DOWN_RIGHT,
                   fw->x + FIELD_X(1), fw->y + FIELD_Y(1));
   }
}

/*--------------------------------------------------------------------------*/
/* field_clear_actor                                                        */
/*--------------------------------------------------------------------------*/

int field_clear_actor(FIELD_ACTOR *fa, FIELD_ACTOR *ofa)
{
   FIELD_ACTOR *fw;
   int x, y;
   int other_cleared;

   field_paint_cell(fa->x / CELL_XSIZE, fa->y / CELL_XSIZE);
   other_cleared = (ofa != NULL && field_collision(fa->x, fa->y, ofa->x, ofa->y));
   if (fa->x % CELL_XSIZE != 0) {
      field_paint_cell(fa->x / CELL_XSIZE + 1, fa->y / CELL_YSIZE);
      other_cleared |= (ofa != NULL && field_collision(fa->x + CELL_XSIZE, fa->y, ofa->x, ofa->y));
   }
   if (fa->y % CELL_YSIZE != 0) {
      field_paint_cell(fa->x / CELL_XSIZE, fa->y / CELL_YSIZE + 1);
      other_cleared |= (ofa != NULL && field_collision(fa->x, fa->y + CELL_YSIZE, ofa->x, ofa->y));
   }
   if (fa->x % CELL_XSIZE != 0 && fa->y % CELL_YSIZE != 0) {
      field_paint_cell(fa->x / CELL_XSIZE + 1, fa->y / CELL_YSIZE + 1);
      other_cleared |= (ofa != NULL && field_collision(fa->x + CELL_XSIZE, fa->y + CELL_YSIZE, ofa->x, ofa->y));
   }

   fw = fa->weapon;
   if (fw == NULL || fw->state == 0)
      return other_cleared;
   if ((fw->actor->type & ACTOR_WEAPON_CLOUD) != ACTOR_WEAPON_CLOUD)
      other_cleared |= field_clear_actor(fw, ofa);
   else
      for (y = fw->y / CELL_YSIZE - 1; y <= fw->y / CELL_YSIZE + 2; y++)
         if (y >= 0 && y < FIELD_YCELLS)
            for (x = fw->x / CELL_XSIZE - 2; x <= fw->x / CELL_XSIZE + 3; x++) {
               field_paint_cell(x, y);
               other_cleared |= (ofa != NULL && field_collision(CELL_X(x), CELL_Y(y), ofa->x, ofa->y));
            }
   return other_cleared;
}

/*--------------------------------------------------------------------------*/
/* field_ssp                                                                */
/*--------------------------------------------------------------------------*/

void field_ssp(FIELD_ACTOR *fa)
{
   void *top = NULL, *bot = NULL;
   int tick = 0;

   if (field.prolog_countdown != 0) {
      top = fa->sprite;
      bot = fa->orig_actor->sprite;
      tick = PROLOG_FRAMES - field.prolog_countdown;
   }
   if (field.epilog_countdown != 0) {
      top = fa->orig_actor->sprite;
      bot = fa->sprite;
      tick = EPILOG_FRAMES - field.epilog_countdown;
   }

   sprite_paint_clipped(top, SPRITE_STOP, fa->x + FIELD_X(0), fa->y + FIELD_Y(0),
                        0, 0, CELL_XSIZE, tick);
   sprite_paint_clipped(bot, SPRITE_STOP, fa->x + FIELD_X(0), fa->y + FIELD_Y(0),
                        0, tick, CELL_XSIZE, CELL_YSIZE - tick);
}

/*--------------------------------------------------------------------------*/
/* field_init                                                               */
/*--------------------------------------------------------------------------*/

void field_init(void)
{
   if (font != NULL)
      return;
   back_color = canvas_alloc_color(0, 0, 0);
   light_color = canvas_alloc_color(255, 255, 0);
   dark_color = canvas_alloc_color(0, 0, 255);
   life_color = canvas_alloc_color(96, 96, 96);
   ammo_color = canvas_alloc_color(255, 0, 0);
   font = canvas_font_load(FONT_NAME);
}

/*--------------------------------------------------------------------------*/
/* field_image_dim_func                                                     */
/*--------------------------------------------------------------------------*/

void field_image_dim_func(int width, int height, unsigned short *pixmap, char *mask)
{
   int x, y;
   unsigned short *p;
   int r, g, b;

   for (y = 0; y < height; y++) {
      p = pixmap + y * width * 3;
      for (x = 0; x < width; x++) {
         r = *(p + 0);
         g = *(p + 1);
         b = *(p + 2);
         r = max(0, r - 20000);
         g = max(0, g - 20000);
         b = max(0, b - 20000);
         *(p + 0) = r;
         *(p + 1) = g;
         *(p + 2) = b;
         p += 3;
      }
   }
}

/*--------------------------------------------------------------------------*/
/* field_setup_actor                                                        */
/*--------------------------------------------------------------------------*/

void field_setup_actor(FIELD_ACTOR *fa, ACTOR *actor, CELL *cell)
{
   fa->orig_actor = actor;
   fa->weapon = (FIELD_ACTOR *)&fa->weapon_space;

   /* the dark shapeshifter needs to take its enemy's sprite during battle */
   if (fa == &field.dark &&
       (actors_list[actor->weapon].type & ACTOR_WEAPON_CLONE) == ACTOR_WEAPON_CLONE) {
      fa->actor = &actors_list[field.light.actor->type & ACTOR_MASK];
#ifndef AUTOPILOT
      fa->sprite = sprite_copy(actors_list[(field.light.actor->type & ACTOR_MASK)].sprite, 1);
      sprite_modify(fa->sprite, field_image_dim_func);
#else
      fa->sprite = fa->actor->sprite;
#endif
      if (fa->actor->type & ACTOR_ELEMENTAL)
         sprite_set_state(fa->sprite, sprite_get_state(field.light.sprite), 0);
      else
         sprite_set_state(fa->sprite, STATE_MOVE_LEFT, 0);
      fa->weapon->actor = &actors_list[field.light.actor->weapon];
#ifndef AUTOPILOT
      fa->weapon->sprite = sprite_copy(actors_list[field.light.actor->weapon].sprite, 1);
      sprite_modify(fa->weapon->sprite, field_image_dim_func);
#else
      fa->weapon->sprite = fa->weapon->actor->sprite;
#endif

   /* non-shapeshifters have their own sprites */
   } else {
      fa->actor = actor;
      fa->sprite = fa->actor->sprite;
      if (!(fa->actor->type & ACTOR_ELEMENTAL))
         sprite_set_state(fa->sprite, (fa->actor->type & ACTOR_LIGHT ? STATE_MOVE_RIGHT : STATE_MOVE_LEFT), 0);
      fa->weapon->actor = &actors_list[actor->weapon];
      fa->weapon->sprite = fa->weapon->actor->sprite;
   }

   fa->x = field.cell_x;
   fa->y = field.cell_y;
   fa->x1 = CELL_X(actor->type & ACTOR_LIGHT ? 0 : FIELD_XCELLS - 1);
   fa->y1 = CELL_Y(FIELD_YCELLS / 2);
   fa->state = 0;
   fa->onrock = 0;
   fa->blink = 0;
   fa->slow_pause = 0;
   fa->fire_time = -10000;
   fa->fire_state = 0;
   fa->num_hits = 0;
   fa->life = field_initial_life(actor, cell);
   fa->orig_life = fa->life;
   fa->last_state = sprite_get_state(fa->sprite);
   fa->inanimate = 1;

   fa->weapon->x = -1;
   fa->weapon->y = 0;
   fa->weapon->state = 0;
   fa->weapon->onrock = 0;
   fa->weapon->fire_time = fa->fire_time;
   fa->weapon->redraw = 0;
   fa->weapon->weapon = NULL;
}

/*--------------------------------------------------------------------------*/
/* field_start_game                                                         */
/*--------------------------------------------------------------------------*/

void field_start_game(ACTOR *_light, ACTOR *_dark, CELL *cell, int cx, int cy)
{
   int i;

   field_init();
   field_frame_time = 0;

   field.prolog_countdown = PROLOG_FRAMES;
   field.epilog_countdown = 0;
   field.repaint_life = 0;
   field.repaint_ammo = 0;
   field.cell_x = cx - FIELD_X(0);
   field.cell_y = cy - FIELD_Y(0);

   field_setup_actor(&field.light, _light, cell);
   field_setup_actor(&field.dark, _dark, cell);

   memcpy(&field.rocks, cell->rocks, sizeof(field.rocks));
   memset(field_cells, ROCK_NONE << ROCK_IX_SHIFT, sizeof(field_cells));
   for (i = 0; i < NUM_ROCKS; i++)
      field_cells[field.rocks[i].y][field.rocks[i].x] =
         (i << ROCK_IX_SHIFT) + field.rocks[i].lumi;

   canvas_clear();
   field_refresh();

   audio_start_battle(_light,
                      ((actors_list[_dark->weapon].type & ACTOR_WEAPON_CLONE)
                      == ACTOR_WEAPON_CLONE)?_light:_dark);
}

/*--------------------------------------------------------------------------*/
/* field_end_game                                                           */
/*--------------------------------------------------------------------------*/

void field_end_game(void)
{
#ifndef AUTOPILOT
   if (field.dark.actor != field.dark.orig_actor) {
      sprite_free(field.dark.sprite);
      sprite_free(field.dark.weapon->sprite);
   }
#endif
}

/*--------------------------------------------------------------------------*/
/* field_collision                                                          */
/*--------------------------------------------------------------------------*/

int field_collision(int ax1, int ay1, int bx1, int by1)
{
   int ax2, ay2;
   int bx2, by2;

   ax2 = ax1 + CELL_XSIZE - 1;
   ay2 = ay1 + CELL_YSIZE - 1;
   bx2 = bx1 + CELL_XSIZE - 1;
   by2 = by1 + CELL_YSIZE - 1;

   if (ax1 >= bx1 && ay1 >= by1 && ax1 < bx2 && ay1 < by2) /* ax1,ay1 */
      return 1;
   if (ax1 >= bx1 && ay2 >= by1 && ax1 < bx2 && ay2 < by2) /* ax1,ay2 */
      return 1;
   if (ax2 >= bx1 && ay1 >= by1 && ax2 < bx2 && ay1 < by2) /* ax2,ay1 */
      return 1;
   if (ax2 >= bx1 && ay2 >= by1 && ax2 < bx2 && ay2 < by2) /* ax2,ay2 */
      return 1;

   if (bx1 >= ax1 && by1 >= ay1 && bx1 < ax2 && by1 < ay2) /* bx1,by1 */
      return 1;
   if (bx1 >= ax1 && by2 >= ay1 && bx1 < ax2 && by2 < ay2) /* bx1,by2 */
      return 1;
   if (bx2 >= ax1 && by1 >= ay1 && bx2 < ax2 && by1 < ay2) /* bx2,by1 */
      return 1;
   if (bx2 >= ax1 && by2 >= ay1 && bx2 < ax2 && by2 < ay2) /* bx2,by2 */
      return 1;

   return 0;
}


/*--------------------------------------------------------------------------*/
/* field_animate                                                            */
/*--------------------------------------------------------------------------*/

void field_animate(FIELD_ACTOR *fa)
{
   int x, y;
   FIELD_ACTOR *other;
   int i;

   if (fa->state == 0 || fa->blink)
      return;

   x = fa->x;
   y = fa->y;
   other = (&field.light == fa ? &field.dark : &field.light);
   
   if (x % CELL_XSIZE == 0 && y % CELL_YSIZE == 0) {
      fa->x1 = x + CELL_X(state_move_x_step[fa->state]);
      fa->y1 = y + CELL_Y(state_move_y_step[fa->state]);
      fa->onrock = 0;
      if (fa->x1 < 0 || fa->x1 >= CELL_X(FIELD_XCELLS) ||
          fa->y1 < 0 || fa->y1 >= CELL_Y(FIELD_YCELLS)) {
         fa->state = 0;
         return;
      }
      i = field_cells[fa->y1 / CELL_YSIZE][fa->x1 / CELL_XSIZE] >> ROCK_IX_SHIFT;
      if (i != ROCK_NONE) {
         if (field.rocks[i].lumi < ROCK_WALKABLE) {
            fa->state = 0;
            return;
         }
         fa->x1 += CELL_X(state_move_x_step[fa->state]);
         fa->y1 += CELL_Y(state_move_y_step[fa->state]);
         if (!(fa->actor->type & ACTOR_WEAPON) &&
             (field_collision(fa->x1, fa->y1, other->x1, other->y1) ||
              field_collision(fa->x1, fa->y1, other->x, other->y))) {
            fa->state = 0;
            return;
         }
         fa->onrock = 1;
      }
   }

   if (fa->actor->type & ACTOR_WEAPON) {
      x += CELL_X(state_move_x_step[fa->state]) / 8;
      y += CELL_Y(state_move_y_step[fa->state]) / 8;
   } else {
      /* when a slow actor is about to make its middle step, we make the */
      /* actor pause for 1 frame */ 
      if ((fa->actor->type & ACTOR_SLOW_SPEED) && !fa->slow_pause &&
          (x % CELL_XSIZE == 8 || x % CELL_XSIZE == 24 ||
           y % CELL_YSIZE == 8 || y % CELL_YSIZE == 24))
         fa->slow_pause = 1;
      else {
         fa->slow_pause = 0;
         x += CELL_X(state_move_x_step[fa->state]) / 8;
         y += CELL_Y(state_move_y_step[fa->state]) / 8;
      }
   }
   x = max(0, min(CELL_X(FIELD_XCELLS - 1), x));
   y = max(0, min(CELL_Y(FIELD_YCELLS - 1), y));

   if (!(fa->actor->type & ACTOR_WEAPON) &&
       !fa->onrock && fa->x % CELL_XSIZE == 0 && fa->y % CELL_YSIZE == 0 &&
       (field_collision(fa->x1, fa->y1, other->x1, other->y1) ||
        field_collision(fa->x1, fa->y1, other->x, other->y))) {
      fa->state = 0;
      return;
   }
      
   if (fa->onrock)
      for (i = 0; i < NUM_ROCKS; i++)
         if (field_collision(x, y, CELL_X(field.rocks[i].x), CELL_Y(field.rocks[i].y))) {
            if (field.rocks[i].lumi != ROCK_LIGHTEST && field_frame_time % ROCK_DELAY != 0)
               return;
            break;
         }

   fa->x = x;
   fa->y = y;
}

/*--------------------------------------------------------------------------*/
/* field_player_frozen                                                      */
/*--------------------------------------------------------------------------*/

int field_player_frozen(FIELD_ACTOR *fa)
{
   FIELD_ACTOR *fw;

   fw = fa->weapon;

   /* if blinking */
   if (fa->blink) {
      fa->blink++;
      if (fa->blink == NUM_BLINKS) {
         fa->blink = 0;
         if (fa->life <= 0)
            field.epilog_countdown = EPILOG_FRAMES;
         else {
            /* if not on a even cell boundary, keep the actor moving */
            if (fa->x % CELL_XSIZE != 0 || fa->y % CELL_YSIZE != 0 || fa->onrock)
               fa->state = fa->last_state;
            else                        /* otherwise let the actor rest */
               fa->state = 0;
         }
      }
      return 1;
   }

   /* if not on an even cell boundary */
   if (fa->x % CELL_XSIZE != 0 || fa->y % CELL_YSIZE != 0 || fa->onrock)
      return 1;

   /* if firing a hand weapon (light knight/dark goblin) */
   /* or a light cloud weapon (light phoenix) */
   if (fw->state != 0 &&
       ((fw->actor->type & ACTOR_WEAPON_HAND) == ACTOR_WEAPON_HAND ||
        (fw->actor->type & ACTOR_MASK) == ACTOR_LIGHT_CLOUD))
      return 1;

   /* if recently fired a shoot weapon */
   if ((fw->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT &&
       fa->fire_state != 0) {
      if (!WAS_TIME(fw, SHOOT_DURATION))
         return 1;
      else {                            /* if the "shooting-freeze" ended */
         fa->fire_state = 0;
         fa->redraw = 1;
      }
   }

   return 0;
}

/*--------------------------------------------------------------------------*/
/* field_player                                                             */
/*--------------------------------------------------------------------------*/

void field_player(FIELD_ACTOR *fa)
{
   int state;
   int fire_down;

   field_animate(fa);
   if (fa->state == 0) {
      fa->x1 = fa->x;
      fa->y1 = fa->y;
      fa->onrock = 0;
   }
   if (field_player_frozen(fa))
      return;

   field_me = fa;
   field_he = (fa == &field.dark) ? &field.light : &field.dark;
   iface_turn((fa == &field.dark), IFACE_FIELD);
   iface_frame();
   if (!board_pause_game(-1))
      return;
   fire_down = iface_key_down(STATE_FIRE);
   fa->state = 0;

   if ((fa->weapon->actor->type & ACTOR_WEAPON_CLOUD) == ACTOR_WEAPON_CLOUD &&
       fire_down) {
      if (fa->weapon->state == 0 &&
          WAS_TIME(fa->weapon, fa->weapon->actor->recharge)) {
         fa->weapon->state = 1;
         fa->weapon->fire_time = field_frame_time;
         fa->weapon->x = fa->x;
         fa->weapon->y = fa->y;
      }
      return;
   }

   for (state = STATE_MOVE_LAST; state >= STATE_MOVE_FIRST; state--)
      if (iface_key_down(state)) {
         if (!fire_down) {
            fa->state = state;
            fa->last_state = state;
         } else
            if (fa->weapon->state == 0 &&
                WAS_TIME(fa->weapon, fa->weapon->actor->recharge)) {
               fa->state = 0;
               fa->fire_state = state;
               fa->weapon->state = state;
               fa->weapon->fire_time = field_frame_time;
               fa->weapon->x  = fa->x;
               fa->weapon->x1 = fa->x;
               fa->weapon->y  = fa->y;
               fa->weapon->y1 = fa->y;
            }
         break;
      }
}

/*--------------------------------------------------------------------------*/
/* field_animate_weapon                                                     */
/*--------------------------------------------------------------------------*/

void field_animate_weapon(FIELD_ACTOR *fa)
{
   FIELD_ACTOR *fw;
   int weapon_duration;

   fw = fa->weapon;
   if ((fw->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT) {
      field_animate(fw);
      return;
   }

   if ((fw->actor->type & ACTOR_WEAPON_HAND) == ACTOR_WEAPON_HAND) {
      fw->x = fa->x + CELL_X(state_move_x_step[fw->state]);
      fw->y = fa->y + CELL_Y(state_move_y_step[fw->state]);
      weapon_duration = HAND_DURATION;
   } else {                             /* otherwise ACTOR_WEAPON_CLOUD */
      fw->x = fa->x;
      fw->y = fa->y;
      weapon_duration = CLOUD_DURATION;
   }
   fw->x1 = fw->x;
   fw->y1 = fw->y;
   if (fw->x < 0 || fw->x > CELL_X(FIELD_XCELLS - 1) ||
       fw->y < 0 || fw->y > CELL_Y(FIELD_YCELLS - 1))
      fw->fire_time = field_frame_time - weapon_duration;
   if (WAS_TIME(fw, weapon_duration))
      fw->state = 0;
   else
      if ((fw->actor->type & ACTOR_WEAPON_CLOUD) == ACTOR_WEAPON_CLOUD) /* &&
          !WAS_TIME(fw, CLOUD_LOWER + 1)) */
         fw->redraw = 1;
}

/*--------------------------------------------------------------------------*/
/* field_weapon_collision                                                   */
/*--------------------------------------------------------------------------*/

int field_weapon_collision(FIELD_ACTOR *fw)
{
   FIELD_ACTOR *ofa, *me_fa;

   if (fw->state == 0)
      return 0;
   ofa = (field.light.weapon == fw ? &field.dark : &field.light);

   if ((fw->actor->type & ACTOR_WEAPON_CLOUD) == ACTOR_WEAPON_CLOUD) {
      if ((WAS_TIME(fw, CLOUD_CENTER) &&
              ofa->y > fw->y - CELL_YSIZE &&
              ofa->y < fw->y + CELL_YSIZE &&
              ofa->x > fw->x - CELL_XSIZE * 3 &&
              ofa->x < fw->x + CELL_XSIZE * 3) ||
          (WAS_TIME(fw, CLOUD_UPPER) &&
              ofa->y > fw->y - CELL_YSIZE * 2 &&
              ofa->y < fw->y &&
              ofa->x >= fw->x - CELL_XSIZE &&
              ofa->x < fw->x + CELL_XSIZE * 2) ||
          (WAS_TIME(fw, CLOUD_LOWER) &&
              ofa->y >= fw->y + CELL_YSIZE &&
              ofa->y < fw->y + CELL_YSIZE * 2 &&
              ofa->x >= fw->x - CELL_XSIZE &&
              ofa->x < fw->x + CELL_XSIZE * 2)) {
         /* cloud only damages once per CLOUD_FACTOR frames, and it can't */
         /* cause damage to an actor who is firing a light cloud */
         if ((field_frame_time % CLOUD_FACTOR == 0) &&
             ((ofa->weapon->actor->type & ACTOR_MASK) != ACTOR_LIGHT_CLOUD ||
              ofa->weapon->state == 0)) {
            me_fa = (ofa == &field.dark ? &field.light : &field.dark);
            if (me_fa->life > 0) {      /* if my life span is zero, I am */
               ofa->life--;             /*  not allowed to damage the    */
               if (ofa->life <= 0) {    /*    other actor                */
                  fw->state = 0;
                  field.epilog_countdown = EPILOG_FRAMES;
               }
               me_fa->num_hits++;
            }
            return 1;
         }
      }

   } else
      if (/* !ofa->onrock && */ !ofa->blink &&
          field_collision(fw->x, fw->y, ofa->x, ofa->y)) {
         /* can't cause damage to an actor who is firing a light cloud */
         if ((ofa->weapon->actor->type & ACTOR_MASK) != ACTOR_LIGHT_CLOUD ||
             ofa->weapon->state == 0) {
            me_fa = (ofa == &field.dark ? &field.light : &field.dark);
            if (me_fa->life > 0) {      /* if my life span is zero, I am */
               ofa->blink = 1;          /*    not allowed to damage the  */
               ofa->life =              /*    other actor                */
                  max(ofa->life - fw->actor->strength, 0);
               ofa->state = ofa->last_state;
               me_fa->num_hits++;
            }
         }
         if ((fw->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT)
            fw->state = 0;
         return 1;
      }

   return 0;
}

/*--------------------------------------------------------------------------*/
/* field_weapon                                                             */
/*--------------------------------------------------------------------------*/

void field_weapon(FIELD_ACTOR *fa)
{
   FIELD_ACTOR *fw;
   int i, n;
   int collision;

   fw = fa->weapon;
   if (IS_TIME(fw, fw->actor->recharge))
      audio_player_reload(fa == &field.dark);
   if (fw->state == 0)
      return;

   /* check collision before advancing the weapon, then advance weapon */
   /* as many times as needed, while checking for collision each time */
   collision = field_weapon_collision(fw);
   if ((fw->actor->type & ACTOR_FAST_SPEED) && (field_frame_time % 2 == 0))
      n = 6;
   else
      n = 4;
   for (i = 0; i < n && fw->state != 0; i++) {
      field_animate_weapon(fa);
      if (fw->state != 0)
         collision |= field_weapon_collision(fw);
      else
         break;
   }
   if (collision)
      audio_damage(fa == &field.light);
}

/*--------------------------------------------------------------------------*/
/* field_lumi_cycle                                                         */
/*--------------------------------------------------------------------------*/

void field_lumi_cycle(void)
{
   int i;
   ROCK *rock;

   if (field_frame_time % ROCK_FRAMES != 0)
      return;
   for (i = 0; i < NUM_ROCKS; i++) {
      rock = &field.rocks[i];
      rock->lumi += rock->lumi_d;
      if (rock->lumi == ROCK_DARKEST || rock->lumi == ROCK_LIGHTEST)
         rock->lumi_d = -rock->lumi_d;
      field_cells[rock->y][rock->x] = (i << ROCK_IX_SHIFT) + rock->lumi;
      field_paint_cell(rock->x, rock->y);
      if (field.light.x == rock->x && field.dark.y == rock->y)
         field_paint_actor(&field.light);
      if (field.dark.y == rock->y && field.dark.y == rock->y)
         field_paint_actor(&field.dark);
      if ((field.light.weapon->x == rock->x && field.light.weapon->y == rock->y) ||
          (field.light.weapon->actor->type & ACTOR_WEAPON_CLOUD) == ACTOR_WEAPON_CLOUD)
         field_paint_weapon(field.light.weapon);
      if ((field.dark.weapon->x == rock->x && field.dark.weapon->y == rock->y) ||
          (field.dark.weapon->actor->type & ACTOR_WEAPON_CLOUD) == ACTOR_WEAPON_CLOUD)
         field_paint_weapon(field.dark.weapon);
   }
}

/*--------------------------------------------------------------------------*/
/* field_print                                                              */
/*--------------------------------------------------------------------------*/

void field_print(char *msg, int row, int color)
{
   int w, h;

   canvas_font_size(msg, font, &w, &h);
   canvas_font_print(msg, (CANVAS_WIDTH - w) / 2, row * h, font, color);
   field.any_output = 1;

#ifdef AUTOPILOT
   printf("field:  %s\n", msg);
#endif
}

/*--------------------------------------------------------------------------*/
/* field_prolog_epilog_move                                                 */
/*--------------------------------------------------------------------------*/

void field_prolog_epilog_move(FIELD_ACTOR *fa)
{
   if (fa->x < fa->x1)
      fa->x = min(fa->x1, fa->x + CELL_XSIZE / 4);
   else if (fa->x > fa->x1)
      fa->x = max(fa->x1, fa->x - CELL_XSIZE / 4);

   if (fa->y < fa->y1)
      fa->y = min(fa->y1, fa->y + CELL_YSIZE / 4);
   else if (fa->y > fa->y1)
      fa->y = max(fa->y1, fa->y - CELL_YSIZE / 4);
}

/*--------------------------------------------------------------------------*/
/* field_prolog                                                             */
/*--------------------------------------------------------------------------*/

void field_prolog(void)
{
   char msg[64];

   field_clear_actor(&field.light, &field.dark);
   field_clear_actor(&field.dark, &field.light);
   field_prolog_epilog_move(&field.light);
   field_prolog_epilog_move(&field.dark);
   field_paint_actor(&field.light);
   field_paint_actor(&field.dark);

   if (field.prolog_countdown <= PROLOG_FRAMES * 5 / 6) {
      field_print("fighting for the light side", 1, light_color);
      sprintf(msg, "the light %s", field.light.actor->name);
      field_print(msg, 2, light_color);
   }

   if (field.prolog_countdown <= PROLOG_FRAMES * 4 / 6) {
      field_print("fighting for the dark side", 5, dark_color);
      sprintf(msg, "the dark %s", field.dark.orig_actor->name);
      field_print(msg, 6, dark_color);
   }

   if (field.prolog_countdown <= PROLOG_FRAMES * 2 / 6)
      field_print("fight!", 10, back_color);

   field.prolog_countdown--;
   if (field.prolog_countdown == 0) {
      field_refresh();
      field.light.inanimate = 0;
      field.dark.inanimate = 0;
   }
}

/*--------------------------------------------------------------------------*/
/* field_epilog                                                             */
/*--------------------------------------------------------------------------*/

ACTOR *field_epilog(void)
{
   ACTOR *winner;
   FIELD_ACTOR *fa_winner, *fa_loser;
   int life;
   char msg[64];
   int color;

   if (field.light.life <= 0) {
      winner = field.dark.orig_actor;
      color = dark_color;
      /* the max(1,...) is there to keep the winning actor at health >= 1 */
      /* even after factoring *out* the lumi-bonus */
      life = max(1, winner->strength - (field.dark.orig_life - field.dark.life));
      fa_winner = &field.dark;
      fa_loser = &field.light;
   } else {
      winner = field.light.actor;
      color = light_color;
      life = max(1, winner->strength - (field.light.orig_life - field.light.life));
      fa_winner = &field.light;
      fa_loser = &field.dark;
   }

   if (field.epilog_countdown == EPILOG_FRAMES * 6 / 6) {
      field_clear_actor(fa_loser, fa_winner);
      audio_end_battle(color == dark_color);
      fa_winner->x1 = field.cell_x;
      fa_winner->y1 = field.cell_y;
      fa_winner->last_state = (color == light_color) ? STATE_MOVE_RIGHT : STATE_MOVE_LEFT;
      fa_winner->inanimate = 1;
   }

   field_clear_actor(fa_winner, fa_loser);
   field_prolog_epilog_move(fa_winner);
   field_paint_actor(fa_winner);

   sprintf(msg, "the %s %s wins", color == light_color ? "light" : "dark",
           winner->name);
   field_print(msg, 5, color);

   field.epilog_countdown--;
   if (field.epilog_countdown > 0)
      winner = NULL;
   else {
      winner->strength = life;
      field_me = &field.light;          /* notify the computer player */
      field_he = &field.dark;           /*    (if any) of a victory */
      iface_notify_computer(IFACE_FIELD_STOP);
      field_end_game();
   }
   return winner;
}

/*--------------------------------------------------------------------------*/
/* field_refresh                                                            */
/*--------------------------------------------------------------------------*/

void field_refresh(void)
{
   int x, y;

   for (y = 0; y < FIELD_YCELLS; y++)
      for (x = 0; x < FIELD_XCELLS; x++)
         field_paint_cell(x, y);
   field_paint_bar(LIFE_BAR, &field.light);
   field_paint_bar(LIFE_BAR, &field.dark);
   field_paint_actor(&field.light);
   field_paint_actor(&field.dark);
   field_paint_weapon(field.light.weapon);
   field_paint_weapon(field.light.weapon);
   canvas_refresh();
   field.any_output = 0;
}

/*--------------------------------------------------------------------------*/
/* field_frame_paint                                                        */
/*--------------------------------------------------------------------------*/

void field_frame_paint(FIELD_DATA *old_field)
{
   int light_cleared = 0, dark_cleared = 0;

   if (old_field->light.life != field.light.life ||
       old_field->dark.life != field.dark.life)
      field.repaint_life = 1;

   if (old_field->light.x != field.light.x ||
       old_field->light.y != field.light.y ||
       old_field->light.state != field.light.state ||
       old_field->light.blink != field.light.blink ||
       old_field->light.redraw != field.light.redraw ||
       old_field->light.weapon->state != field.light.weapon->state ||
       old_field->light.weapon->x != field.light.weapon->x ||
       old_field->light.weapon->y != field.light.weapon->y ||
       old_field->light.weapon->redraw != field.light.weapon->redraw) {
      if (field_clear_actor(&old_field->light, &old_field->dark))
         dark_cleared = 1;
      light_cleared = 1;
   }
   if (old_field->dark.x != field.dark.x ||
       old_field->dark.y != field.dark.y ||
       old_field->dark.state != field.dark.state ||
       old_field->dark.blink != field.dark.blink ||
       old_field->dark.redraw != field.dark.redraw ||
       old_field->dark.weapon->state != field.dark.weapon->state ||
       old_field->dark.weapon->x != field.dark.weapon->x ||
       old_field->dark.weapon->y != field.dark.weapon->y ||
       old_field->dark.weapon->redraw != field.dark.weapon->redraw) {
      if (field_clear_actor(&old_field->dark, &old_field->light))
         light_cleared = 1;
      dark_cleared = 1;
   }

   if (field.repaint_life) {
       field_paint_bar(LIFE_BAR, &field.light);
       field_paint_bar(LIFE_BAR, &field.dark);
       field.repaint_life = 0;
   }
   if (field.repaint_ammo ||
       field_frame_time - field.light.weapon->fire_time <= field.light.weapon->actor->recharge)
      field_paint_bar(AMMO_BAR, &field.light);
   if (field.repaint_ammo ||
       field_frame_time - field.dark.weapon->fire_time <= field.dark.weapon->actor->recharge)
      field_paint_bar(AMMO_BAR, &field.dark);
   field.repaint_ammo = 0;

   if (light_cleared)
      field_paint_actor(&field.light);
   if (dark_cleared)
      field_paint_actor(&field.dark);
   if (light_cleared && field.light.weapon->state != 0)
      field_paint_weapon(field.light.weapon);
   if (dark_cleared && field.dark.weapon->state != 0)
      field_paint_weapon(field.dark.weapon);
}

/*--------------------------------------------------------------------------*/
/* field_frame                                                              */
/*--------------------------------------------------------------------------*/

ACTOR *field_frame(void)
{
   FIELD_DATA old_field;
   ACTOR *winner = NULL;

   field_frame_time++;

   if (field.prolog_countdown > 0)
      field_prolog();

   if (field.prolog_countdown + field.epilog_countdown == 0) {
      memcpy(&old_field, &field, sizeof(FIELD_DATA));
      old_field.light.weapon = (FIELD_ACTOR *)&old_field.light.weapon_space;
      old_field.dark.weapon = (FIELD_ACTOR *)&old_field.dark.weapon_space;
      field_player(&field.light);
      if (!board_pause_game(-1))
         return NULL;
      field_player(&field.dark);
      if (!board_pause_game(-1))
         return NULL;
      field_weapon(&field.light);
      field_weapon(&field.dark);
      field_frame_paint(&old_field);
      field_lumi_cycle();
   }

   if (field.epilog_countdown > 0)
      winner = field_epilog();

   if (field.any_output) {
      canvas_refresh();
      field.any_output = 0;
   }

   return winner;
}

/*--------------------------------------------------------------------------*/
/* field_initial_life                                                       */
/*--------------------------------------------------------------------------*/

int field_initial_life(ACTOR *actor, CELL *cell)
{
   int lumi;

#ifndef AUTOPILOT
   lumi = board_cell_lumi(cell);
   if (((actor->type & ACTOR_LIGHT) == ACTOR_LIGHT && (lumi & CELL_LIGHT)) ||
       ((actor->type & ACTOR_LIGHT) != ACTOR_LIGHT && (lumi & CELL_DARK)))
      lumi = (lumi & CELL_LUMI_MASK) * 5;
   else
#endif
      lumi = 0;
   return actor->strength + lumi;
}

/*--------------------------------------------------------------------------*/
/* field_absolute_control_delta                                             */
/*--------------------------------------------------------------------------*/

void field_absolute_control_delta(int *dx, int *dy)
{
   *dx = (*dx/CELL_XSIZE)*CELL_XSIZE - FIELD_X(0) - field_me->x;
   *dy = (*dy/CELL_YSIZE)*CELL_YSIZE - FIELD_Y(0) - field_me->y;
}

Generated by  Doxygen 1.6.0   Back to index