/* sprite.h

   A sprite is any object in the game besides the tile map itself. This
   module provides a generic standard by which all sprites may be
   implemented, and held together in a single memory structure.

   The sprites themselves are implemented in the s_*.h files.

   NOTE: THIS FILE MAY BE CHANGED WHILE draw.h IS CONSTRUCTED.
*/

#ifndef INCLUDED_SPRITE_H
#define INCLUDED_SPRITE_H

#include "tilemap.h"


typedef struct SPRITE {

 struct SPRITE *next;

 /* Sprite's life. Set this to 0 when you want the sprite to be destroyed. If
    your sprite exists for a set amount of time (as do explosion particles),
    you may use this as a countdown. Otherwise, just set it to 1 and leave it
    until you want the sprite destroyed.

    If you are using this as a countdown, take care. The interact proc,
    called first, may set the sprite's life to 0. Then the update proc,
    called second, may decrement it. To ensure that this situation is handled
    correctly, you must insert a check in the update proc to prevent 'life'
    from going below zero.
 */
 int life;

 /* Type of sprite. This should be set to one of the SPRITE_TYPE_* constants.
    The sprite_*_proc arrays will then govern the sprite's behaviour and
    appearance. See further down for details.
 */
 int type;

 /* Coordinates of centre of sprite. */
 float x, y; /* Position */
 float z;    /* Height */

 /* Horizontal angle (theta) in radians. 0 is up and M_PI/2 is right. If your
    sprite is directionless, you may leave this uninitialised.
 */
 float th;

 /* Linear and angular velocity of sprite. Specified as rate of change of
    position and theta per frame. You may leave these uninitialised if you do
    not use them.
 */
 float xv, yv, zv, thv;

 /* Radius. This is used by the collision detection routines. See collide.h.
    This may be left uninitialised if the following conditions are met:

    - There is no 'draw' function.
    - No generic collision detection routines, or other functions that use
      this field, are used.

    For example, an explosion source (s_exps.h) need not set the 'r' field;
    it is invisible and does not use the collision detection routines.
 */
 float r;


 int num_frames;
 fixed cur_frame;
 fixed anim_speed;
 BITMAP **anim;


 /* 'data' points to whatever extra data you need for this sprite. There are
    no rules for its allocation; it may be allocated and freed for this
    sprite, and it may contain further pointers to memory allocated and freed
    for this sprite; or it may point to memory already allocated that will be
    freed somewhere else. If you do not need it, you may leave it
    uninitialised. However, the sprite_freedata_proc entry for your sprite
    must be set appropriately.
 */
 void *data;

 /* ----------------------------------------------------------------------
    All fields below this point are for temporary usage. They need not be
    initialised when the sprite is created.
    ---------------------------------------------------------------------- */

 /* xview and yview are screen coordinates of the centre of the sprite, while
    zview is its depth in tiles. xrad and yrad are the radius of the sprite
    in pixels in each dimension. These are only meaningful when the 'draw'
    function is called.
 */
 float xview, yview, zview, xrad, yrad;

 struct SPRITE *tnext;

} SPRITE;


/* The types of sprite. */
#define SPRITE_TYPE_PLAYER 0 /* Player             - s_player.h */
#define SPRITE_TYPE_ANIMAL 1 /* Animal             - s_animal.h */
#define SPRITE_TYPE_BOMB   2 /* Bomb               - s_bomb.h   */
#define SPRITE_TYPE_PWRUP  3 /* Powerup            - s_pwrup.h  */
#define SPRITE_TYPE_EXPS   4 /* Explosion source   - s_exps.h   */
#define SPRITE_TYPE_PORTAL 5 /* Portal             - s_anim_s.h */

#define N_INTERACTING_SPRITE_TYPES 6

/* Sprites beyond this point (for which type >= N_INTERACTING_SPRITE_TYPES)
   will never be considered by the interact_sprites() function. They cannot
   interact with each other, and they cannot interact with any of the
   interacting sprites. Such sprites can be produced in large numbers without
   causing a serious loss in performance, as will be necessary with blood
   particles.
*/

#define SPRITE_TYPE_BLOOD  6 /* Blood particle     - s_blood.h  */
#define SPRITE_TYPE_CAMERA 7 /* A Camera           - s_camera.h */
#define SPRITE_TYPE_EXPP   8 /* Explosion particle - s_expp.h   */

#define N_SPRITE_TYPES     9


/* The sprite_*_proc arrays. The procs are typedeffed for convenience, and
   these arrays provide a separate function for each type of sprite. The
   various s_*.h modules will provide the functions where required.

   The interact procs are an exception, since they apply to two sprites at
   once. See the comments for sprite_interact_proc for details.
*/


/* The sprite draw proc. This should draw the sprite on the bitmap 'bmp',
   centred at the screen coordinates (xview, yview). Its radii in the two
   dimensions are xrad and yrad; use xview-xrad and xview+xrad, and the
   equivalents in 'y', to determine the edges of the sprite. The distance of
   the sprite from the camera (actually from the camera plane) is zview, but
   you will not generally need this.

   Some sprites must be drawn differently from different directions. The
   'cth' parameter, short for 'camera theta', represents the direction of the
   camera. Such sprites may use the sprite_direction() function; see
   sprhelp.h for details.

   In most cases, the sprite can be drawn with draw_generic_sprite() in
   sprhelp.h. This function will use polygon3d_f() and POLYTYPE_ATEX_MASK to
   draw the bitmap you pass. You cannot use this function directly in the
   draw proc array, so you will have to call it from your own function. See
   sprhelp.h for details.

   If you want the sprite to be invisible, set this pointer to null.
*/
typedef void (*SPRITE_DRAW_PROC)(BITMAP *bmp, SPRITE *sprite, float cth);

SPRITE_DRAW_PROC sprite_draw_proc[N_SPRITE_TYPES];


/* The sprite update proc. Unlike the draw proc, this function must be
   specified. If you have a case for not specifying this function, appeal to
   me (entheh) and I shall change the rules.

   This is called once every logic frame, at a rate of 100 Hz. It is
   responsible for all your sprite are behaviour, from basic physics to
   collisions with the tile map. The only exception is interaction with other
   sprites, which is handled by a range of interact procs instead.

   This function may safely spawn new sprites using the spawn_sprite()
   function. Such sprites will not be processed in the same logic frame. See
   the description of spawn_sprite() further down for details.
*/
typedef void (*SPRITE_UPDATE_PROC)(TILEMAP *map, SPRITE *sprite);

SPRITE_UPDATE_PROC sprite_update_proc[N_SPRITE_TYPES];


/* The sprite freedata function. When the sprite is destroyed, this function
   will be called with the sprite's 'data' field as its parameter. This could
   happen at any moment during the sprite's evolution, since all sprites will
   be destroyed if the game is aborted or concluded.

   If 'data' is unused, or if it points to memory whose allocation is handled
   elsewhere, then set this pointer to null.

   If 'data' is allocated with malloc() and must be freed, then this function
   pointer may simply be set to 'free'.

   If any other action is required, e.g. the allocated memory has pointers to
   further allocated memory, then you must write your own function here.
   Remember, it operates only on the sprite's 'data' field, not on the whole
   sprite.
*/
typedef void (*SPRITE_FREEDATA_PROC)(void *data);

SPRITE_FREEDATA_PROC sprite_freedata_proc[N_SPRITE_TYPES];


/* The two-sprite interaction function. This two-dimensional array allows
   a different action for every possible combination of sprites. Use the two
   sprites' types as indices into this array to obtain a proc. Then call the
   proc with pointers to the two sprites. It doesn't matter which sprite is
   which; the array will be constructed so that ...[a][b] == ...[b][a] for
   any two object types, and the functions will be designed to handle both
   cases.

   This proc will be called for all pairs of interacting sprites in the game;
   they do not have to be overlapping in space. It is the responsibility of
   the interact proc to test their proximity if appropriate. An interacting
   sprite is any for which type < N_INTERACTING_SPRITE_TYPES.

   The interact proc will be called for all pairs *before* the update proc is
   called for any sprites. See further down for the reasons for this.

   The interact proc may set either sprite's 'life' field to zero. However,
   such a sprite will still interact with other sprites during this logic
   frame, and the sprite's update function will still be called.

   All sprite interaction procs are prefixed with si_. If a proc applies to
   only one specific type of sprite, the proc will be located in the sprite's
   module (e.g. si_expp_expp() is located in s_expp.h). All other interaction
   procs are located in interact.h.

   The sprite interaction should work as follows:

   player/player, bomb/bomb, player/bomb: use si_collision().
   player/expp: use si_player_expp().
   bomb/expp: use si_bomb_expp().
   expp/expp: use si_expp_expp() in s_expp.h.
   player/powerup: use si_player_powerup() in s_pwrup.h.
   bomb/powerup: use si_collision().
   expp/powerup: use si_repel().
   powerup/powerup: use si_collision().
   animal/anything: use si_animal() in s_animal.h.
*/
typedef void (*SPRITE_INTERACT_PROC)(TILEMAP *map, SPRITE *spr1, SPRITE *spr2);

SPRITE_INTERACT_PROC sprite_interact_proc[N_INTERACTING_SPRITE_TYPES]
                                         [N_INTERACTING_SPRITE_TYPES];


SPRITE *add_sprite(SPRITE *sprite);


/* spawn_sprite(): this function may be used by existing sprites to spawn new
   sprites during the 'update' function. The new sprite will be placed
   directly after the current sprite in the list, but it will *not* be
   processed in the same logic frame.

   The function returns a pointer to the new sprite, so that you can set it
   up. Be sure to fill in *all* the sprite's fields, except where otherwise
   indicated in their comments.

   Great care required here; bugs will be hard to trace!

   This function is *not* suitable for adding a sprite out of the blue. It
   can only be used safely where a sprite already exists to be passed as the
   parameter, i.e. in the 'update' function belonging to a sprite.
*/
SPRITE *spawn_sprite(SPRITE *sprite);


/* destroy_sprite(): destroys a sprite, returning its 'next' field. This
   should only be used by this module.

   To destroy a sprite, first check sprite_freedata_proc. If this sprite has
   a freedata proc, then call it, passing the 'data' field as parameter.
   Finally, free the memory for the sprite itself. Don't forget to make a
   copy of the 'next' field *before* freeing the memory!
*/
SPRITE *destroy_sprite(SPRITE *sprite);


/* Note: during the update_game() function (see game.h), interact_sprites()
   should be called *before* update_sprites(). There are two reasons for
   this:

   1. An interact proc can set a flag in a sprite, triggering an action in
      the same frame; that action can then be handled by the update proc.

   2. The update proc, responsible for collision detection with the tile map,
      will have the last word. While this arrangement allows sprites to
      overlap for a frame before a collision is detected, it prevents sprites
      from overlapping with the tile map, which would be a more serious
      problem.
*/


/* interact_sprites(). The parameter is the first sprite in the list.

   1. Go through the list of sprites. Create a linked list containing all
      the interacting sprites. An interacting sprite is any sprite for which
      type < N_INTERACTING_SPRITE_TYPES. Use the sprites' temporary 'tnext'
      fields to create the list, and, obviously, keep a pointer to the first
      one.

   2. Scan the linked list in such a way that every possible pair of entries
      is considered. For each one, get the appropriate entry from the
      sprite_interact_proc array. If the entry is not null, then call it,
      passing pointers to the two sprites.

   Note: there is no need to clear the 'tnext' fields afterwards.
*/
void interact_sprites(TILEMAP *map, SPRITE *sprite);


/* update_sprites(): pass the tile map and the first sprite in the list as
   parameters. Note that the 'sprite' parameter is a pointer to a pointer, so
   you need to pass &game->sprite here, not just game->sprite. This function
   has two stages:

   1. Take a copy of the sprite pointer (i.e. *sprite, where 'sprite' is the
      parameter). Use this to iterate through the sprite list.

      For each sprite:

      - Save its 'next' pointer. This will be the next sprite to process.

      - Call the sprite's 'update' function, passing the necessary variables.

      - Proceed to the next sprite as recorded in step 1. We need to have
        recorded the pointer to the next sprite in order to skip the newly
        spawned sprites for this frame.

   2. Iterate through the sprite list for a second time, this time using the
      parameter directly. At first it points to the place where the first
      sprite's pointer is stored; on subsequent iterations it points to the
      previous sprite's 'next' field. That way, in order to remove the
      current sprite, we can store its 'next' pointer (as returned by
      destroy_sprite()) in the location to which 'sprite' points. We do not
      have to make a special case for the first sprite in the list. Magic!

      In this loop, each sprite whose 'life' field is zero must be destroyed.
      Use the method described above to remove sprites from the list.
*/
void update_sprites(TILEMAP *map, SPRITE **sprite);


/* destroy_all_sprites(): this is called only when the game is aborted or
   concluded. Pass the first sprite in the list as parameter. It will use
   destroy_sprite() repeatedly, taking the return value as the next sprite to
   destroy each time, until there are no sprites left (i.e. destroy_sprite()
   returns null).
*/
void destroy_all_sprites(SPRITE *sprite);

#endif /* INCLUDED_SPRITE_H */
