diff options
author | H. Peter Anvin <hpa@zytor.com> | 2003-03-24 16:31:19 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2003-03-24 16:31:19 +0000 |
commit | 1128ad360b5cccb1b82de092505e5ca1c4dbed8d (patch) | |
tree | 89c7ebe649b47fa1b901872d646a2183df023c8d /action.c | |
download | grv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.tar.gz grv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.tar.xz grv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.zip |
Port of "grävning" to C/SDL, started 2003-03-22
Diffstat (limited to 'action.c')
-rw-r--r-- | action.c | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/action.c b/action.c new file mode 100644 index 0000000..1e27fe2 --- /dev/null +++ b/action.c @@ -0,0 +1,327 @@ +/* + * action.c + * + * Handle action queue events + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <math.h> +#include "graphics.h" +#include "grv.h" + +/* ------------------------------------------------------------------------ * + * Primary action event queue machinery + * ------------------------------------------------------------------------ */ + +struct action { + int x, y; + enum actions what; + double when; +}; + +#define MAXACT 80 +static struct action actions[MAXACT]; +static int action_count; + +void addaction(int x, int y, double when, enum actions what) +{ + int i = 0; + if ( action_count >= MAXACT ) + return; + + while ( i < action_count && actions[i].when <= when && actions[i].what ) + i++; + + memmove(&actions[i+1], &actions[i], + (action_count-i)*sizeof(struct action)); + actions[i].x = x; + actions[i].y = y; + actions[i].when = when; + actions[i].what = what; + action_count++; +} + +/* Remove any action which matches the given (x,y,what) */ +void removeaction(int x, int y, enum actions what) +{ + int i = 0; + + while ( i < action_count ) { + if ( actions[i].x == x && actions[i].y == y && actions[i].what == what ) { + memmove(&actions[i], &actions[i+1], + (action_count-i-1)*sizeof(struct action)); + action_count--; + } else { + i++; + } + } +} + +void reset_actions(void) +{ + action_count = 0; +} + +static int sort_by_time(const void *a, const void *b) +{ + const struct action *aa = (const struct action *)a; + const struct action *bb = (const struct action *)b; + + /* This *must* be a stable sort! */ + if ( aa->when == bb->when ) + return (aa < bb) ? -1 : 1; + else + return (aa->when < bb->when) ? -1 : 1; +} + +/* This changes the time of *all* actions of a specifc type to "when" */ +void retime_all(double when, enum actions what) +{ + int i; + + for ( i = 0 ; i < action_count ; i++ ) { + if ( actions[i].what == what ) + actions[i].when = when; + } + + qsort(actions, action_count, sizeof(struct action), sort_by_time); +} + +/* ------------------------------------------------------------------------ * + * Stuff that gets triggered by action events... + * ------------------------------------------------------------------------ */ + +/* Blow bomb or smash */ +static void blow_bomb(int x, int y, int smash) +{ + /* The first parameter is how far above and below (x,y) does this happen? */ + static const int bomb_pattern[] = { + 5, 0, 0, 2, 2, 3, 3, 3, 2, 2, 0, 0 + }; + static const int smash_pattern[] = { + 6, 0, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 0 + }; + static const int big_smash_pattern[] = { + 9, 0, 2, 4, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 6, 6, 5, 4, 2, 0 + }; + const int *pattern; + int path, patw; + int x2, y2; + int ch, m, f, fl; + int erase; + + pattern = smash + ? (rnd() < 0.1 ? big_smash_pattern : smash_pattern) + : bomb_pattern; + + patw = *pattern++; + + /* Note the order in which this happens. We escavate from the bottom up + in each column; this means that when we run into rocks we can just let + them fall. We can also activate rocks and apples above the hole + at the end of each x2 loop. + + This was a bug in the original BASIC version... it didn't do this. */ + + for ( y2 = y-patw ; y2 <= y+patw ; y2++ ) { + path = *pattern++; + + if ( y2 < 1 || y2 > 40 ) + continue; + + for ( x2 = x+path ; x2 >= x-path ; x2-- ) { + if ( x2 < 2 || x2 > 23 ) + continue; + + ch = screen(x2,y2); + m = fg(x2,y2); + fl = screen1(x2,y2) & 0x80; /* Flashing? */ + erase = 1; + + switch ( ch ) { + case SYM_PLAYER: + if ( smash ) + erase = 0; + else + gp.Status = Status_Dead; + break; + + case SYM_GHOST: + kill_ghost(x2,y2); + break; + + case SYM_CHERRY: + take_cherry(); + break; + + case SYM_BONUS: + if ( gp.Bon ) + gp.Sc += 15*gp.Level; /* This sucks :) */ + break; + + case SYM_APPLE: + erase = 0; + color(m+16, 0); + lprint(x2,y2,"\xfe"); + if ( !fl ) + addaction(x2, y2, gp.Tid+30.0/(gp.Level+1), act_apple); + break; + + case SYM_BOMB: + case 'H': + case 'Y': + case 'P': + case 'E': + case 'R': + /* Don't erase, but put it on dark background */ + if ( x2 != x || y2 != y ) { + erase = 0; + color(m + (fl ? 16 : 0), 0); + lprintf(x2,y2,"%c",ch); + } + break; + + case SYM_ROCK: + fall_rock(x2,y2); + erase = 0; + break; + + default: + break; + } + + if ( erase ) { + color(m, 0); + lprint(x2,y2," "); + } + } + + /* See if there is something above this row that should be activated */ + if ( x2 >= 2 && x2 <= 23 ) { + ch = screen(x2,y2); + m = fg(x2,y2); + f = bg(x2,y2); + fl = screen1(x2,y2) & 0x80; /* Flashing? */ + + if ( ch == SYM_ROCK ) { + fall_rock(x2,y2); + } else if ( ch == SYM_APPLE && !fl ) { + color(16+m,f); + lprint(x2,y2,"\xfe"); + addaction(x2, y2, gp.Tid+30.0/(gp.Level+1), act_apple); + } + } + } + + update_score(); +} + +/* Turn on or off the bonus dots */ +static void bonus_on(void) +{ + gp.Bon = 1; + color(30,0); + lprint(25, 1, " BONUS "); + addaction(0, 0, gp.Tid+12.0, act_bonus_off); +} + +static void bonus_off(void) +{ + gp.Bon = 0; + color(0, gp.c); + lprint(25, 1, " "); + addaction(0, 0, gp.Tid+120.0, act_bonus_on); +} + + +/* ------------------------------------------------------------------------ * + * Action event queue trigger function + * ------------------------------------------------------------------------ */ + +void handle_action(void) +{ + struct action action; + + if ( !action_count || actions[0].when > gp.Tid ) + return; /* No action currently pending */ + + action = actions[0]; + + /* Remove from queue */ + memmove(&actions[0], &actions[1], (--action_count)*sizeof(struct action)); + + switch ( action.what ) { + case act_none: + break; + + case act_apple: + fall_apple(action.x, action.y); + break; + + case act_bomb: + blow_bomb(action.x, action.y, 0); + break; + + case act_bonus_on: + bonus_on(); + break; + + case act_bonus_off: + bonus_off(); + break; + + case act_smash: + blow_bomb(gp.x, gp.y, 1); + break; + + case act_toolate: + color(20,0); + lprint(12,14," OUT OF TIME! "); + gp.Status = Status_Dead; + break; + + case act_droplevel: + gp.ZLevel--; + color(0,gp.c); + if ( gp.ZLevel > 0 ) { + addaction(0, 0, gp.Tid+10.0, act_droplevel); + lprintf(25, 2, "JUMP TO %-2d", gp.ZLevel+1); + } else { + lprint(25, 2, " "); + } + break; + + case act_door: + /* Bombs can blow holes in doors, so don't assume the door is + still there */ + if ( strchr(DOORS, screen(action.x,action.y)) ) { + color(fg(action.x,action.y), bg(action.x,action.y)); + lprint(action.x,action.y," "); + if ( screen(action.x-1,action.y) == SYM_ROCK ) + fall_rock(action.x-1,action.y); + } + break; + + case act_ghost_thaw: + { + int e; + + color(28,0); + for ( e = 0 ; e < MAXGHOST ; e++ ) { + if ( !ghost[e].dead ) { + lprint(ghost[e].x, ghost[e].y, "\x02"); + } + } + } + break; + + case act_ghost_normal: + gp.FS = 1; /* Normal ghosts */ + break; + } +} + + |