#include <allegro.h>


/* The coordinate system is as follows:
 *
 *    x goes to the right.
 *    y goes towards the player.
 *    z goes upwards.
 */


#define DIR_AWAY    0
#define DIR_RIGHT   1
#define DIR_TOWARDS 2
#define DIR_LEFT    3

#define BLOCK_CLEAR          0
#define BLOCK_FULL           1
#define BLOCK_INDESTRUCTIBLE 2
#define BLOCK_SLOPE(d)       ((d) + 4) /* Slope downhill in direction given */
#define BLOCK_SLOPE_MASK     3

#define BLOCK_DESTRUCTIBLE(b) ((b) != 2)
#define BLOCK_CAN_SUPPORT(b)  ((b) < 4)
#define BLOCK_INVALID(b)      ((b) == 3 || (b) >= 8)

#define MIN_SHEEP_INTERVAL 50

#define COL_WHITE  1
#define COL_YELLOW 2
#define COL_WATER  3
#define COL_SKY    4


typedef struct LEVELSET LEVELSET;
typedef struct LEVEL LEVEL;
typedef struct BLOWER BLOWER;
typedef struct STATE STATE;
typedef struct BLOCK BLOCK;
typedef struct SHEEP SHEEP;
typedef struct BLOWERLIST BLOWERLIST;


struct LEVELSET
{
	int n_levels;
	LEVEL *level;
	char name[41];
};


/* Inactive level data */

struct LEVEL
{
	unsigned char w, h;
	unsigned char **n_indestructible;
	unsigned char **n_full;
	unsigned char **topping;
	unsigned char **hole; /* An element is set to 1 if a hole is present */
	int n_blowers;
	BLOWER *blower;
	int n_sheep;
	int sheep_needed; /* How many must the player save in order to proceed? */
	int initial_sheep_delay; /* Time before the first sheep appears */
	int sheep_interval; /* Time between sheep */
	unsigned char px, py; /* Player start position */
	unsigned char pd; /* Player start direction */
};

struct BLOWER
{
	unsigned char x, y, z;
	unsigned char d;
};


/* Active level data */

#define PZPHASE_MAX 64 /* Must be a power of two */

#define PDISP_SHIFT 5
#define PDISP_MAX (1 << PDISP_SHIFT)

#define PTURNDELAY 32
#define PDESTROYCOUNT 64

#define N_PIPELINE_BLOCKS 5

struct STATE
{
	LEVEL *l; /* Reference only - do not free */
	BLOCK ***block; /* Dynamic 2D array of linked lists */
	BLOWERLIST ***blowerarray; /* For the drawing routines */
	SHEEP *sheep;
	int sheep_left, sheep_saved, sheep_lost;
	int blower_next; /* Which blower will produce a sheep next? */
	int blower_time; /* How long before this happens? */
	int px, py, pz; /* Player position */
	unsigned char pzphase; /* Parameter to sine wave for vertical oscillation */
	unsigned char pfd; /* Player face direction */
	unsigned char pdisp; /* Displacement, for when player is moving. */
	unsigned char pmd; /* Player move direction (irrelevant if pdisp=0) */
	signed char pzmd; /* Player z move direction (-1,0,1) */
	float pzdisp; /* Zero normally; decreases (negative) when falling */
	float pzv; /* Falling velocity */
	float pfd_disp; /* Player face direction displacement, in radians */
	unsigned char pturndelay; /* Delay between changes of pfd */
	float plookup; /* Player looking up or down; up is positive; radians */
	unsigned char pipeline[N_PIPELINE_BLOCKS]; /* Stores block types */
	float pipeline_disp; /* Used to scroll the pipeline */
	unsigned char pcount; /* Counts to PDESTROYCOUNT before destruction */
	unsigned char watershift; /* Added to water texture coordinates */
	unsigned char fade; /* Starts at 128 and counts down at end of game */
};

#define BLOCK_RAISED 0.00001f
#define BLOCKS_SEPARATED (1.0f + BLOCK_RAISED)

struct BLOCK
{
	BLOCK *nextdown; /* The block at the top of the stack is listed first. */
	unsigned char type;
	/* x,y position is implicit in where the struct is stored. */
	float z; /* This refers to the base of the block. */
	float zv;
};

#define SHEEP_R 0.25f

struct SHEEP
{
	SHEEP *next, *prev; /* These need bubble-sorting by y. No pun intended. */
	float x, y, z;
	float xv, yv, zv;
};


/* load.c */
LEVELSET *load_levelset(const char *filename);
void unload_levelset(LEVELSET *set);

/* logic.c */
STATE *init_state(LEVEL *level);
int update_logic(STATE *state); /* Returns nonzero on end. */
void shut_down_state(STATE *state);

/* sheep.c */
int update_sheep(STATE *state); /* Returns nonzero on no more sheep. */
/* Sheep are responsible for bouncing/breaking on contact with anything. */

/* block.c */
unsigned char new_pipeline_block(void);
void update_blocks(STATE *state); /* Makes blocks fall */

/* player.c */
void update_player(STATE *state);

/* draw.c */
void destroy_blower_array(STATE *state);
void create_blower_array(STATE *state);
void draw_level(BITMAP *bmp, STATE *state);

/* main.c */
#include "datafile.h"
extern DATAFILE *dat;
extern signed char ymove[];
#define xmove (ymove+1)
