aboutsummaryrefslogtreecommitdiffstats
path: root/foes.c
diff options
context:
space:
mode:
Diffstat (limited to 'foes.c')
-rw-r--r--foes.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/foes.c b/foes.c
new file mode 100644
index 0000000..abe3fba
--- /dev/null
+++ b/foes.c
@@ -0,0 +1,290 @@
+/* $Id: foes.c,v 1.1.1.1 2010/07/17 17:30:32 culot Exp $ */
+
+/*
+ * Copyright (c) 2010 Frederic Culot <frederic@culot.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "oldrunner.h"
+
+#define FOES_DELAY (OLDRUNNER_TIMEOUT * 3)
+#define FOES_ESCAPE_DELAY (BRICK_COMEBACK_TIME * .75)
+
+struct foe {
+ struct coord pos;
+ struct timer timer;
+ enum move current_move;
+ int state;
+ SLIST_ENTRY(foe) foesp;
+};
+
+SLIST_HEAD(, foe) foes;
+
+void
+foes_init (void)
+{
+ SLIST_INIT (&foes);
+}
+
+void
+foes_free (void)
+{
+ while (!SLIST_EMPTY (&foes))
+ {
+ struct foe *f;
+
+ f = SLIST_FIRST (&foes);
+ SLIST_REMOVE_HEAD (&foes, foesp);
+ xfree (f);
+ }
+}
+
+void
+foes_add (const struct coord *pos)
+{
+ struct foe *f;
+
+ f = xmalloc (sizeof *f);
+ coord_set_yx (&f->pos, pos->y, pos->x);
+ f->current_move = MOV_NONE;
+ f->state = STATE_ALIVE;
+ SLIST_INSERT_HEAD (&foes, f, foesp);
+}
+
+void
+foes_draw (void)
+{
+ struct foe *f;
+
+ SLIST_FOREACH (f, &foes, foesp)
+ gfx_show_sprite (SP_FOE, &f->pos);
+}
+
+/*
+ * Once killed, a foe reappears on top of current level with
+ * a random horizontal position.
+ */
+static void
+get_random_inipos (struct coord *pos)
+{
+ coord_set_yx (pos, 0, -1);
+ while (lvl_obstacle_at (pos))
+ pos->x = lvl_random_xpos ();
+}
+
+static void
+foe_killed (struct foe *foe)
+{
+ struct coord inipos;
+
+ get_random_inipos (&inipos);
+ coord_copy (&inipos, &foe->pos);
+ foe->current_move = MOV_FALL;
+ foe->state = STATE_ALIVE;
+}
+
+static void
+foe_trapped (struct foe *foe)
+{
+ foe->state |= STATE_TRAPPED;
+ timer_start (&foe->timer);
+}
+
+static void
+check_trap (struct foe *foe)
+{
+ if (lvl_got_hole_below (&foe->pos))
+ {
+ struct coord below;
+
+ foe_trapped (foe);
+ coord_below (&foe->pos, &below);
+ gfx_move_sprite (SP_FOE, &foe->pos, &below);
+ coord_copy (&below, &foe->pos);
+ }
+}
+
+static void
+check_hole (struct foe *foe)
+{
+ if (lvl_nothing_below (&foe->pos))
+ foe->current_move = MOV_FALL;
+}
+
+static unsigned
+another_foe_at_pos (const struct foe *foe, const struct coord *pos)
+{
+ struct foe *f;
+
+ SLIST_FOREACH (f, &foes, foesp)
+ if (f != foe && coord_equal (pos, &f->pos))
+ return 1;
+
+ return 0;
+}
+
+unsigned
+foes_at_pos (const struct coord *pos)
+{
+ struct foe *f;
+
+ SLIST_FOREACH (f, &foes, foesp)
+ if (coord_equal (pos, &f->pos))
+ return 1;
+
+ return 0;
+}
+
+static void
+compute_move (const struct coord *hero, struct foe *foe)
+{
+ struct coord dpos;
+
+ if (foe->current_move == MOV_FALL)
+ coord_compute (&foe->pos, foe->current_move, &foe->pos);
+ else
+ {
+ coord_diff (hero, &foe->pos, &dpos);
+ if (dpos.y != 0)
+ {
+ enum move wanted_dir, prefered_move;
+
+ if (foe->current_move != MOV_LEFT && foe->current_move != MOV_RIGHT)
+ prefered_move = dpos.x >= 0 ? MOV_RIGHT : MOV_LEFT;
+ else
+ prefered_move = foe->current_move;
+ wanted_dir = dpos.y > 0 ? MOV_DOWN : MOV_UP;
+ foe->current_move = lvl_shortest_way (&foe->pos,
+ wanted_dir,
+ prefered_move);
+ coord_compute (&foe->pos, foe->current_move, &foe->pos);
+ }
+ else if (dpos.x > 0)
+ {
+ foe->current_move = MOV_RIGHT;
+ foe->pos.x++;
+ }
+ else if (dpos.x < 0)
+ {
+ foe->current_move = MOV_LEFT;
+ foe->pos.x--;
+ }
+ else
+ foe->current_move = MOV_NONE;
+ }
+}
+
+static void
+try_escape (struct foe *f)
+{
+ struct coord posorig;
+
+ if (!timer_delay_elapsed (&f->timer, FOES_ESCAPE_DELAY))
+ return;
+
+ coord_set_yx (&posorig, f->pos.y, f->pos.x);
+ coord_set_yx (&f->pos, f->pos.y - 1, f->pos.x);
+ gfx_move_sprite (SP_FOE, &posorig, &f->pos);
+ f->state &= ~STATE_TRAPPED;
+}
+
+static void
+update_foe_pos (const struct coord *hero_pos, struct foe *f)
+{
+ struct coord posorig;
+
+ coord_set_yx (&posorig, f->pos.y, f->pos.x);
+ compute_move (hero_pos, f);
+ if (!coord_equal (&posorig, &f->pos)
+ && lvl_valid_move (&posorig, f->current_move, &f->pos, SP_FOE)
+ && !another_foe_at_pos (f, &f->pos))
+ {
+ gfx_move_sprite (SP_FOE, &posorig, &f->pos);
+ check_trap (f);
+ check_hole (f);
+ if (coord_equal (&f->pos, hero_pos))
+ hero_die ();
+ }
+ else
+ {
+ /*
+ * The move is not possible, revert to current position
+ * and force next move to be in opposite direction using
+ * foe->current_move.
+ */
+ coord_copy (&posorig, &f->pos);
+ f->current_move =
+ f->current_move == MOV_FALL ?
+ MOV_NONE : coord_opposite_dir (f->current_move);
+ }
+}
+
+void
+foes_update_pos (void)
+{
+ static struct timer foes_timer;
+ struct timer now;
+ struct foe *foe;
+ struct coord hero_pos;
+
+ timer_get_time (&now);
+ if (timer_diff (&now, &foes_timer) < FOES_DELAY)
+ return;
+
+ hero_get_pos (&hero_pos);
+
+ SLIST_FOREACH (foe, &foes, foesp)
+ {
+ struct coord foe_prevpos;
+
+ coord_set_yx (&foe_prevpos, foe->pos.y, foe->pos.x);
+
+ if (foe->state & STATE_TRAPPED)
+ try_escape (foe);
+ else
+ update_foe_pos (&hero_pos, foe);
+ }
+ timer_get_time (&foes_timer);
+}
+
+unsigned
+foes_wallup_at (const struct coord *pos)
+{
+ struct foe *f;
+
+ SLIST_FOREACH (f, &foes, foesp)
+ {
+ if (coord_equal (pos, &f->pos))
+ {
+ foe_killed (f);
+ return 1;
+ }
+ }
+
+ return 0;
+}
Un proyecto texto-plano.xyz