/*--------------------------------------------------------------------------*/ /* battle field */ /*--------------------------------------------------------------------------*/ #include #include #include #include #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; }