diff options
Diffstat (limited to 'foes.c')
-rw-r--r-- | foes.c | 290 |
1 files changed, 290 insertions, 0 deletions
@@ -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; +} |