/* $Id: foes.c,v 1.1.1.1 2010/07/17 17:30:32 culot Exp $ */ /* * Copyright (c) 2010 Frederic Culot * 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; }