aboutsummaryrefslogtreecommitdiffstats
path: root/action.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2003-03-24 16:31:19 +0000
committerH. Peter Anvin <hpa@zytor.com>2003-03-24 16:31:19 +0000
commit1128ad360b5cccb1b82de092505e5ca1c4dbed8d (patch)
tree89c7ebe649b47fa1b901872d646a2183df023c8d /action.c
downloadgrv-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.c327
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;
+ }
+}
+
+