aboutsummaryrefslogblamecommitdiffstats
path: root/foes.c
blob: abe3fbae5c7efb17f252b052b1fc35e13640192b (plain) (tree)

































































































































































































































































































                                                                              
/*  $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