/* Popcorn header file; #include this in your C source, and link with
** o.Popcorn to use.
**
** by Matthew Bloch + Rik Griffin, 1996-7
**
** Header file help may not be in English; sentences may have had articles,
** subjects and verbs taken out so the comments fit on one line.  No
** responsibility will be taken for any mental disorders arising from
** attempts at comprehension of this file.
**
** Specifically, typedefs are NOT used, except in the case of the object
** handler.  This is because the word 'struct' shows up in a different colour
** and typedefs don't, and also because I'm inconsistent.  Popcorn works fine
** from the outside; play with the insides at your own risk.
*/

#ifndef POPCORN_H

#define POPCORN_H
#include "kernel.h"

#ifndef BOOL
 typedef unsigned BOOL;
#endif

#ifndef NULL
 #define NULL ((void *)0)
#endif

#ifndef FALSE
 #define FALSE 0
#endif

#ifndef TRUE
 #define TRUE (!FALSE)
#endif

/* Constants which will satisfy most games, but feel free to mess with them */
#define MAX_RESOURCES 512
#define MAX_PROTOTYPES 64

#define TABLE_HEADER_WORD 0x544A424F /* OBJT */

struct window {
  int x0, y0, x1, y1;
};

union object_flags {
  struct {
    unsigned int std_plot       : 1; /* 0 Use standard sprite plotter */
    unsigned int plot_offset    : 1; /* 1 use global plot_offset */
    unsigned int animate        : 1; /* 2 plot_id points to animation block */
    unsigned int collide        : 1; /* 3 Include object in collision tables */
    unsigned int velocities     : 1; /* 4 Apply velocity to object's position */
    unsigned int gravity        : 1; /* 5 Apply table gravity on processing */
    unsigned int attn_every     : 1; /* 6 Attention on every process pass */
    unsigned int attn_plot      : 1; /* 7 Attention for plot */
    unsigned int attn_timer     : 1; /* 8 Attention when timer runs down */
    unsigned int attn_plotout   : 1; /* 9 Attention when outside plot window */
    unsigned int attn_gameout   : 1; /*10 Attention when outside game window */
    unsigned int attn_userout   : 1; /*11 Attention when outside user window */
    unsigned int kill_timer     : 1; /*12 Kill when timer runs down */
    unsigned int kill_plotout   : 1; /*13 Kill when outisde plot window */
    unsigned int kill_gameout   : 1; /*14 Kill when outside game window */
    unsigned int kill_userout   : 1; /*15 Kill when outside user window */
    unsigned int yoyo           : 1; /*16 Signifies yoyo animation */
    unsigned int yoyo_dec       : 1; /*17 Used with above to show direction */
    unsigned int animate_skip   : 1; /*18 For 'every other frame' animation */
  } bits;
  unsigned int word; /* So we can zero all the flags easily */
};

/* This lot added by Rik. But I think we don't need them
**  (subject to change if the flags do, of course...)
*/
#define FLAG_STD_PLOT           (1U)
#define FLAG_PLOT_OFFSET        (1U<<1)
#define FLAG_ANIMATE            (1U<<2)
#define FLAG_COLLIDE            (1U<<3)
#define FLAG_VELOCITIES         (1U<<4)
#define FLAG_GRAVITY            (1U<<5)
#define FLAG_ATTN_EVERY         (1U<<6)
#define FLAG_ATTN_PLOT          (1U<<7)
#define FLAG_ATTN_TIMER         (1U<<8)
#define FLAG_ATTN_PLOTOUT       (1U<<9)
#define FLAG_ATTN_GAMEOUT       (1U<<10)
#define FLAG_ATTN_USEROUT       (1U<<11)
#define FLAG_KILL_TIMER         (1U<<12)
#define FLAG_KILL_PLOTOUT       (1U<<13)
#define FLAG_KILL_GAMEOUT       (1U<<14)
#define FLAG_KILL_USEROUT       (1U<<15)
#define FLAG_YOYO               (1U<<16)
#define FLAG_YOYO_DEC           (1U<<17)
#define FLAG_ANIMATE_SKIP       (1U<<18)

/* object_handler
**
**   A typedef for a Popcorn object handler, surprisingly.
*/
typedef void (*object_handler)(struct game_object*, union object_flags, void *);

/* struct resource
**
**   This structure represents one resource; each resource has a group
**   associated with it for mass loading / deleting.  It also contains
**   the resource's size (bytes), filename, and address in memory.
**   An address of 0 means the resource isn't loaded.
**
**   This structure is used internally; you don't need to know how the
**   resource manager works because it's boring :-)
*/
struct resource {
  int  group;
  char *file;
  void *addr;
  int  size;
};

/* struct frame_data
**
**   Lists of these are kept inside 'struct animate_block's, for each frame
**   of an animation.  Each one contains a sprite anchor (usually a pointer
**   to a *addr in a 'struct resource', and the centering and size info
**   for each frame.
*/
struct frame_data {
  void **sprite_anchor;
  int  centre_x, centre_y;
  int  size_x, size_y;
};

/* struct animate_block
**
**   If an object's plot_id points to one of these, and the 'animate' bit is
**   set in its flags, the object is animated according to the list of frames.
*/
struct animate_block {
  int                 frames;
  struct frame_data   frame[1]; /* Open-ended structure (no fixed size) */
};

/* struct prototype
**
**   Popcorn maintains a master list of prototypes which are represented
**   by these data structures.  Each prototype has a unique id, a handler
**   associated with it and a set of object flags.  There's also an animation
**   block attached, whether the object is animated or not.  There can be
**   ANY number of frames in this animation block (0 or 1 included).
*/
struct prototype {
  int                   id;
  object_handler        handler;
  union object_flags    flags;
  struct animate_block  animation;
};

/* struct game_object
**
**   Each object in the game is represented by one of these structs;
**   it contains the position and speed of the object within the playing
**   area, along with the 'handler' function.  This is called whenever the
**   object requires 'attention', which is set by the flags explained above.
**
*/

struct game_object {
  int object_id;                   /* Solely for the object handler */
  union {
    void **sprite_anchor;
    struct animate_block *animation;
  } plot_id;                       /* Passed in R0 to sprite plotter */
  object_handler handler;
  int frame;
  union object_flags flags;
  int x, y, xv, yv;	           /* Object co-ordinates and velocity */
  struct {
    short unsigned int decrement;
    short signed int value;     /* Rik. Relies on short being 16 bits */
  } timer;
  struct {
    short unsigned int x;
    short unsigned int y;
  } size;
  struct {
    short unsigned int x;
    short unsigned int y;
  } plot_offset;
  void *user_data;
};

/* struct collision_object
**
**   This is used mainly for internal use, so that the collision detection
**   routines can work as fast as possible.  It contains the bounding box
**   of each object, plus a reference back to the objects handler in case
**   any collisions happen.  A table of these structs is built up at the
**   head of each table, so that these tables can be cross-referenced easily.
**
**   Checking 50 objects against 50 other objects requires 2500 checks, so
**   we need to work as quickly as possible.
*/

/* struct collision_object
**
**   Minimal information for faster collision detection.
*/
struct collision_object {
  int x0, y0, x1, y1;
  struct game_object *back_ptr;
};

/* struct collision_table
**
**   A smaller version of 'struct object_table', below, but contains only
**   the information necessary for collision detection.
*/
struct collision_table {
  int                     entries;
  struct collision_object collision[1];
};

/* struct object_table
**
**   This is the header to a table of game_object structs, as defined above.
**   It contains the number of entries used so far; when this reaches the
**   maximum number for that table, it starts looking for holes in it to
**   fill with new objects.  Regular use of Popcorn_Tidy will minimise
**   these holes.
*/

struct object_table {
  int                      header;
  int                      max_objects, next_free;
  int			   grav_x, grav_y;
  struct collision_table   *collisions;
  struct game_object       object[1];
};

/* Popcorn_CollisionCheck
**
**   Call this to check whether objects in two tables have collided.  Only
**   objects for which the collide flag has been set will be checked.
**   If a collision is found, the handlers in table1 ONLY will be called.
*/
extern void Popcorn_CollisionCheck(struct object_table* table1, struct object_table* table2);

/* Popcorn_ClearScreen
**
**   Clears the screen memory to a particular colour.
*/
void	 Popcorn_ClearScreen(int colour);

/*
** Popcorn_DeleteObject
**
**   Only needs to be a macro, this one: it just sets the object_id to
**   zero, and the processing function sees it as a hole in the table.
*/
#define Popcorn_DeleteObject(o) (o)->object_id = 0

/* Popcorn_FindResource
**
**   This call will return an address pointer for the resource specified in
**   'res_name', providing you have called Popcorn_LoadResourceFile earlier
**   and the resource has been loaded.  Otherwise a NULL pointer is returned.
*/
void    **Popcorn_FindResource(char *res_name);

/* Popcorn_FindSymbol
**
**   Call which finds the address of a symbol in a program; it returns the
**   object_handler type because this is its main use.  Before calling this,
**   you must set 'symbol_table_filename' to be the filename of the symbol
**   table produced by the linker.
*/
object_handler Popcorn_FindSymbol(char *name);

/* Popcorn_LoadGroup
**
**   This call finds all the resources with a specified ID, and loads them
**   into memory.  An error is returned if all of them have not been loaded.
*/
_kernel_oserror *Popcorn_LoadGroup(int id);

/* Popcorn_LoadPrototypes
**
**   Should only be called at once, to load the global list of prototypes
**   for an application.
*/
_kernel_oserror *Popcorn_LoadPrototypes(char *filename);

/* Popcorn_LoadResourceFile
**
**   This function initialises the resource managing routines; all the
**   file should contain is a list of group/resource pairs.  None of the
**   resources will be loaded into memory until Popcorn_LoadGroup() is
**   called with a valid ID.
*/
_kernel_oserror *Popcorn_LoadResourceFile(char *filename);

/* Popcorn_LoseGroup
**
**   This call deletes (i.e. frees the memory used by) all the resources
**   with a specified group ID.
*/
void Popcorn_LoseGroup(int id);

/* Popcorn_NewObject
**
**   This function finds a space in a specified table for a new object,
**   returning NULL if the table is full.
*/
extern struct game_object* Popcorn_NewObject(struct object_table *table);

/* Popcorn_NewObjectZeroed
**
**   Identical to the above functon, but zeroes all the object's attributes
**   before returning its address.
*/
extern struct game_object* Popcorn_NewObjectZeroed(struct object_table *table);

/* Popcorn_NewPrototype
**
**   This function creates an object with Popcorn_NewObject then fills
**   in the flags and animation details from the specified prototype
**   name.  The velocities and timer are zeroed, and should be filled in
**   by the program if necessary.
*/
extern struct game_object* Popcorn_NewPrototype(struct object_table *table,
       	      		   			 int    prototype_id,
       	      		   			 int	x,
       	      		   			 int	y);

/* Popcorn_NewTable
**
**   This function allocates space for a new object table, allowing for
**   a maximum of max_objects and allowing for collision detection on
**   the table only if (collisions == TRUE).
*/
extern struct object_table* Popcorn_NewTable(int max_objects,
					     BOOL collisions);

/* Popcorn_Outside
**
**   Returns TRUE if a given object (with co-ordinates shifted << 12, remember)
**   is outside a certain window (co-ordinates specified as screen pixels).
**   Internal use, but you might want it.
*/
BOOL Popcorn_Outside(struct game_object *obj, struct window *win);

/* Popcorn_PlotBackdrop
**
**   Copies the specified area of memory to the screen memory as quickly
**   as possible.
*/
void	 Popcorn_PlotBackdrop(char *backdrop);

/* Popcorn_PlotSprite
**
**   *sprite should be a pointer to a Popcorn sprite file in memory; x and y
**   can be -ve, or off the screen and the sprite will be clipped according
**   to plot_window (see below).
*/
void	 Popcorn_PlotSprite(char *sprite, signed int x, signed int y);

/* Popcorn_Process
**
**   This is the function that does all the work :-)  Call this every
**   frame on each of your tables to plot all the objects, move them
**   on, call their handlers if necessary and add them to the collision
**   tables (i.e. you must call this before Popcorn_CollisionCheck)
*/
extern void Popcorn_Process(struct object_table* table,
       	    		     BOOL plot, int moves);

/* Popcorn_ReadScreenDetails
**
**   Call once on every mode change so that the display routines can sort
**   themselves out; also sets the 'plot' bank to 0 and the 'display' bank to
**   1, or t'other way around... see the source code if you're that bothered.
*/
void     Popcorn_ReadScreenDetails(void);

/* Popcorn_SwapBanks
**
**   Swaps the 'display' and 'plot' banks around; usually used after
**   OS_Byte 19 for smooth animation.
*/
void	 Popcorn_SwapBanks(void);

/* Popcorn_Tidy
**
**   Call this whenever possible to push all the objects in a table
**   towards the top, and free up spaces at the end, making creation
**   of new objects quicker.  It can be slow, so only use when the
**   game is paused or between levels.
**
**   DON'T use this call when objects rely on finding other objects in certain
**   positions in the object table, otherwise they may move and run into
**   trouble.
*/
extern void Popcorn_Tidy(struct object_table* table);

/* plot_window, game_window, user_window
**
**   Three windows which objects can be inside or outside; plot_window affects
**   sprite clipping, but the others two are unused for any other purpose.
*/
extern struct window       plot_window;
extern struct window	   game_window;
extern struct window	   user_window;

/* resource and prototype arrays
**
**   Used internally; look at the source if you're bothered.
*/
extern struct resource     *resource[MAX_RESOURCES];
extern struct prototype    *prototype[MAX_PROTOTYPES];
extern int    		   resource_free, prototype_free;

/* symbol_table_filename
**
**   To find symbols at run-time, this must contain the filename of a symbol
**   table produced by the linker.  Add '-symbols <filename>' when you call
**   the linker to produce one.
*/
extern char                symbol_table_filename[255];

/* Everything below this line was added by Rik Griffin, */
/* so blame him for any nasty bits.                     */


/* plot_offset
** 
**   Set plot_offset.x and plot_offset.y to set the offset from the top-left
**   that a particular object table should be displayed at; this allows for
**   scrolling screens.  The relevant Popcorn routines take note of this
**   variable; any custom-written routines should as well.
*/
extern struct {int x,y;} plot_offset;

#define Popcorn_SetPlotOffset(a,b) { plot_offset.x=(a); plot_offset.y=(b); }

/* a new object type for the optimised dust objects     */
/* which don't have handlers, sizes, sprites, collisions */

#define DUST_HEADER_WORD        0x54535544      /* 'DUST' */

struct dust_object {
  int object_id;                /* Solely for the object handler */
  union object_flags flags;     /* use the object flags but only a few work */
  int x, y, xv, yv;	        /* Object co-ordinates and velocity */
  int timer_value;              /* decrement is always 1 */
  void *colours;                /* pointer to colour table */
  int colour_div;
  int colour_num;               /* size of colour array, colour displayed is */
                                /* (timer.value / colour_div) % colour_num   */
};

/* no collision checking for dust objects */
struct dust_object_table {
  int header;
  int max_objects;
  int next_slot;
  int grav_x, grav_y;
  struct dust_object object[1];
};

/* Popcorn_NewDustTable
**
**   Like Popcorn_NewTable, this reserves space for a dust table, with the
**   maximum number of objects specified.
*/
extern struct dust_object_table* Popcorn_NewDustTable(int max_objects);

/* last parameter in these next 2 routines is importance flag - if      */
/* set, an object may be deleted to make room for the new one           */
extern struct dust_object *Popcorn_NewDustObject(
  struct dust_object_table *table, int i);
extern struct dust_object *Popcorn_NewDustPrototype(
  struct dust_object_table *table, int prototype_id, int x, int y, int i);

extern void Popcorn_ProcessDust(struct dust_object_table *table, int moves);

/* same as DeleteObject */
#define Popcorn_DeleteDust(o) (o)->object_id = 0

BOOL Popcorn_DustOutside(struct dust_object *obj, struct window *win);

extern void Popcorn_PlotPoint(int x, int y, int col);

extern void Popcorn_PlotSpriteNoBounds(char *sprite, int x, int y);

/* this routine reserves space for all the user_data needed for an      */
/* entire table and initialises the user_data pointers for all the      */
/* objects in the table. Hence don't alter object[x].user_data unless   */
/* you really want to. The space can be freed with                      */
/* free(table->object[0].user_data) if you must. Don't use              */
/* Popcorn_NewObjZ on the table as this destroys the user_data field.   */
/* size is the size of a single struct that user_data will point to     */
extern BOOL Popcorn_NewSubTable(struct object_table *table, size_t size);

/* this might be faster than the function version */
#define Popcorn_FastOutside(x, y, w) \
  ((x) < (w)->x0 || (x) > (w)->x1 || (y) < (w)->y0 || (y) > (w)->y1)

#endif /* POPCORN_H */
