/* collide.h

   This module handles collision detection between the sprite and the tile
   map. Use the sprite_tilemap_collision() function to test for, and process,
   such collisions.

   sprite_tilemap_collision() assumes the 'r' field of the SPRITE struct is
   initialised, and it assumes the 'xv', 'yv' and 'zv' fields are in use.

   If a collision occurs, the 'callback' function will be called. Callback
   functions are prefixed with stc_. See the STC_CALLBACK typedef below for
   the parameter list.

   'xi', 'yi' and 'zi' are the coordinates of the impact, and may be passed
   to the originate_sample() function (see sound.h) if you want a noisy
   collision. 'iv' is the impact speed, and may be used to control the volume
   of the sound effect. 'iv' is also useful if you wish to inflict damage on
   the sprite.

   Note that the impact speed is not the same as the speed with which the
   sprite is moving. Suppose a sprite is moving fast along the ground, and it
   bounces on a tile's surface. Although its speed is high, the impact speed
   is low. The 'iv' parameter is valid for all collisions, not just
   collisions with tile surfaces.

   The 'tile' parameter points to the tile with which the sprite collided.
   If a sprite collides with a wall, 'tile' points to the upper tile. In most
   cases, 'hit_tile' will be nonzero (1).

   'hit' indicates which surfaces were hit; if the sprite collides directly
   with a surface, the corresponding field here will be 1 and the others 0.
   If the sprite collides with an edge or a corner, the fields here will be
   set proportionately depending on the angle. They always add up to 1.

   Special case: if the sprite collides with an outside wall, 'hit_tile' will
   be 0. In this case, you should not inflict any damage to the tile itself
   (e.g. you should not make it subside). However, you can safely change the
   tile's TILE_SURFACES structures; 'hit' will be set up so that you change
   the value corresponding to the outside wall.

   In short, the TILE_SURFACES structs may always be changed, but all other
   data should only be changed if hit_tile is nonzero.

   The callback function is called after the collision has been processed.
   The sprite's velocity will be directed away from the tile map, and the
   sprite will not overlap with the tile map. The callback function can
   modify the velocity further if need be.

   If you have no use for a callback function, pass NULL. The sprite will
   bounce silently and harmlessly.

   See sprhelp.h for some helper callback functions.

   Warning: this function is slow. Do not use it for numerous tiny particles.
*/

#ifndef INCLUDED_COLLIDE_H
#define INCLUDED_COLLIDE_H

#include "tile.h"
#include "tilemap.h"
#include "sprite.h"


/* No hit_tile. TRUTH: if xt < 0, no tile was hit. */
typedef void (*STC_CALLBACK)(TILEMAP *map,
                             SPRITE *sprite,
                             float xi, float yi, float zi,
                             float iv,
                             int xt, int yt,
                             TILE *tile, TILE_SURFACES *hit);


/* collide_vertex(): handles a collision with a vertex.

   The sprite's position and velocity may be accessed directly in the
   'sprite' struct. 'xvtx', 'yvtx' and 'zvtx' are the coordinates of the
   vertex with which the sprite is colliding.

   'sx', 'sy' and 'sz' point to the relevant fields of the 'hit' parameter
   (see above). The face in the xy-plane (i.e. the tile surface) should go in
   'sx'; the face in the yz-plane (i.e. the top or bottom wall) should go in
   'sy'; the face in the xz-plane (i.e. the left or right wall) should go in
   'sz'. These fields should be reset to zero afterwards, in case a further
   collision occurs.

   If a collision occurs, and 'callback' is not null, this function will call
   it with all the relevant parameters. 'hit_tile' is always 1 for this type
   of collision.
*/
void collide_vertex(TILEMAP *map,
                    SPRITE *sprite,
                    int xt, int yt,
                    TILE *tile,
                    TILE_SURFACES *hit,
                    float xvtx, float yvtx, float zvtx,
                    float *sx, float *sy, float *sz,
                    STC_CALLBACK callback);


/* collide_edge(): handles a collision with an edge. Note that this function
   does not check if the sprite is beyond the ends of the edge; it is assumed
   that this test has already been done.

       -> u    'u' and 'v' are the position of the sprite, in the two
    +--------  coordinate axes perpendicular to the edge, as indicated. They
    :    sv    are pointers; if you pass &x for u and &y for v, then u and v
    :          will correspond to x and y respectively. 'uv' and 'vv' are the
   ::          velocity of the sprite in the same two coordinate axes.
   v:          'uedge' and 'vedge' are the coordinates of the edge itself.
    :su        'su' and 'sv' point to the relevant fields of the 'hit'
   v:          parameter (see above). If the sprite almost hits the 'su'
    :          surface, but just catches the edge, then 'su' will take most
    :          of the hit. These fields should be cleared afterwards.

   If a collision occurs, and 'callback' is not null, this function will call
   it with all the relevant parameters. 'hit_tile' is always 1 for this type
   of collision. The coordinates of impact may be established by storing
   uedge and vedge in *u and *v respectively, then reading the 'x', 'y' and
   'z' fields of the sprite. However, *u and *v should be set to their final
   values (so that the sprite does not overlap the edge and its velocity is
   directed away) before you call 'callback'.
*/
void collide_edge(TILEMAP *map,
                  SPRITE *sprite,
                  int xt, int yt,
                  TILE *tile,
                  TILE_SURFACES *hit,
                  float *u, float *v,
                  float *uv, float *vv,
                  float uedge, float vedge,
                  float *su, float *sv,
                  STC_CALLBACK callback);


/* sprite_tilemap_collision() handles collisions with faces directly. It uses
   collide_edge() and collide_vertex() for all other collisions. Faces should
   be considered first, then edges, then vertices.

   See the top of this file for information on how to use this function.
*/
void sprite_tilemap_collision(TILEMAP *tilemap,
                              SPRITE *sprite,
                              STC_CALLBACK callback);

#endif /* INCLUDED_COLLIDE_H */
