forked from KolibriOS/kolibrios
405 lines
9.9 KiB
C
405 lines
9.9 KiB
C
|
/*
|
||
|
------------------------------------------------------------
|
||
|
Fixed Rate Pig - a fixed logic frame rate demo
|
||
|
------------------------------------------------------------
|
||
|
* Copyright (C) 2004 David Olofson <david@olofson.net>
|
||
|
*
|
||
|
* This software is released under the terms of the GPL.
|
||
|
*
|
||
|
* Contact author for permission if you want to use this
|
||
|
* software, or work derived from it, under other terms.
|
||
|
*/
|
||
|
|
||
|
#ifndef PIG_ENGINE_H
|
||
|
#define PIG_ENGINE_H
|
||
|
|
||
|
#include "SDL.h"
|
||
|
#include <math.h>
|
||
|
#ifndef M_PI
|
||
|
# define M_PI 3.14159265358979323846 /* pi */
|
||
|
#endif
|
||
|
#include "dirty.h"
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Game Engine
|
||
|
----------------------------------------------------------*/
|
||
|
|
||
|
typedef struct PIG_object PIG_object;
|
||
|
typedef struct PIG_engine PIG_engine;
|
||
|
|
||
|
|
||
|
/* Interpolated point */
|
||
|
typedef struct PIG_ipoint
|
||
|
{
|
||
|
/* From the last logic frame: */
|
||
|
float ox, oy; /* Position */
|
||
|
|
||
|
/* From the last/current rendered frame: */
|
||
|
int gimage; /* Sprite frame index */
|
||
|
float gx, gy; /* Interpolated position */
|
||
|
} PIG_ipoint;
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Game logic events
|
||
|
*
|
||
|
* PREFRAME:
|
||
|
* Occurs once per logic frame, before collision and
|
||
|
* off-screen detection, and before timer handlers.
|
||
|
*
|
||
|
* TIMERx:
|
||
|
* Occurs whenever timer x expires. Timers are one-
|
||
|
* shot, but can be reloaded by the handler for
|
||
|
* periodic action. Timer events are handled before
|
||
|
* before collision and off-screen detection.
|
||
|
*
|
||
|
* HIT_TILE:
|
||
|
* Occurs when the hot-spot of an object hits a
|
||
|
* marked side of a tile, and the corresponding bit
|
||
|
* in 'tilemask' is set.
|
||
|
*
|
||
|
* HIT_OBJECT:
|
||
|
* Occurs when the collision circle of an object
|
||
|
* intersects the collision circle of another object,
|
||
|
* provided one or more bits in 'hitgroup' of the
|
||
|
* other object matches bits in 'hitmask'.
|
||
|
*
|
||
|
* OFFSCREEN:
|
||
|
* Occurs when an object is off-screen. This takes
|
||
|
* in account the hot-spot and bounding rectangle of
|
||
|
* the current sprite frame.
|
||
|
*
|
||
|
* POSTFRAME:
|
||
|
* Occurs once per logic frame, after collision
|
||
|
* detection, off-screen detection and all other
|
||
|
* events.
|
||
|
*
|
||
|
*/
|
||
|
#define PIG_TIMERS 3
|
||
|
typedef enum
|
||
|
{
|
||
|
PIG_PREFRAME,
|
||
|
PIG_TIMER0,
|
||
|
PIG_TIMER1,
|
||
|
PIG_TIMER2,
|
||
|
PIG_HIT_TILE,
|
||
|
PIG_HIT_OBJECT,
|
||
|
PIG_OFFSCREEN,
|
||
|
PIG_POSTFRAME
|
||
|
} PIG_events;
|
||
|
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
PIG_NONE = 0,
|
||
|
|
||
|
/* Bit positions */
|
||
|
PIG_TOP_B = 0,
|
||
|
PIG_BOTTOM_B = 1,
|
||
|
PIG_LEFT_B = 2,
|
||
|
PIG_RIGHT_B = 3,
|
||
|
|
||
|
/* Masks */
|
||
|
PIG_TOP = 1 << PIG_TOP_B,
|
||
|
PIG_BOTTOM = 1 << PIG_BOTTOM_B,
|
||
|
PIG_LEFT = 1 << PIG_LEFT_B,
|
||
|
PIG_RIGHT = 1 << PIG_RIGHT_B,
|
||
|
|
||
|
/* Combined masks */
|
||
|
PIG_TL = PIG_TOP | PIG_LEFT,
|
||
|
PIG_TR = PIG_TOP | PIG_RIGHT,
|
||
|
PIG_BL = PIG_BOTTOM | PIG_LEFT,
|
||
|
PIG_BR = PIG_BOTTOM | PIG_RIGHT,
|
||
|
PIG_ALL = 0xf,
|
||
|
} PIG_sides;
|
||
|
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
PIG_UNCHANGED = -10000000,
|
||
|
PIG_MIN = -10000001,
|
||
|
PIG_CENTER = -10000002,
|
||
|
PIG_MAX = -10000003
|
||
|
} PIG_values;
|
||
|
|
||
|
|
||
|
/* Collision info */
|
||
|
typedef struct
|
||
|
{
|
||
|
float ff; /* Fractional frame */
|
||
|
int x, y; /* Exact position */
|
||
|
PIG_sides sides; /* Side of tile hit */
|
||
|
} PIG_cinfo;
|
||
|
|
||
|
|
||
|
typedef struct PIG_event
|
||
|
{
|
||
|
PIG_events type;
|
||
|
|
||
|
/* For HIT_TILE, HIT_OBJECT and OFFSCREEN: */
|
||
|
PIG_cinfo cinfo; /* Detailed collision info */
|
||
|
|
||
|
/* For HIT_OBJECT: */
|
||
|
PIG_object *obj; /* Which object? */
|
||
|
} PIG_event;
|
||
|
|
||
|
|
||
|
/* Logic object */
|
||
|
struct PIG_object
|
||
|
{
|
||
|
PIG_engine *owner;
|
||
|
PIG_object *next, *prev;
|
||
|
|
||
|
int id; /* Unique ID. 0 means "free". */
|
||
|
|
||
|
int ibase; /* Sprite frame base index */
|
||
|
int image; /* Sprite frame offset */
|
||
|
float x, y; /* Position */
|
||
|
float vx, vy; /* Speed */
|
||
|
float ax, ay; /* Acceleration */
|
||
|
PIG_ipoint ip;
|
||
|
int tilemask; /* Sprite/tile mask [PIG_ALL] */
|
||
|
|
||
|
int hitmask; /* Sprite/sprite mask [0] */
|
||
|
int hitgroup; /* Sprite/sprite group [0] */
|
||
|
|
||
|
int timer[PIG_TIMERS]; /* Down-counting timers */
|
||
|
int age; /* Age timer (logic frames) */
|
||
|
|
||
|
int score;
|
||
|
int power;
|
||
|
int target;
|
||
|
int state;
|
||
|
|
||
|
void (*handler)(PIG_object *po, const PIG_event *ev);
|
||
|
|
||
|
void *userdata;
|
||
|
};
|
||
|
|
||
|
|
||
|
/* Level map */
|
||
|
typedef struct PIG_map
|
||
|
{
|
||
|
PIG_engine *owner;
|
||
|
|
||
|
int w, h; /* Size of map (tiles) */
|
||
|
unsigned char *map; /* 2D aray of tile indices */
|
||
|
unsigned char *hit; /* 2D aray of collision flags */
|
||
|
|
||
|
int tw, th; /* Size of one tile (pixels) */
|
||
|
SDL_Surface *tiles; /* Tile palette image */
|
||
|
unsigned char hitinfo[256]; /* Collision info for the tiles */
|
||
|
} PIG_map;
|
||
|
|
||
|
|
||
|
/* Sprite frame */
|
||
|
typedef struct PIG_sprite
|
||
|
{
|
||
|
int w, h; /* Size of sprite (pixels) */
|
||
|
int hotx, hoty; /* Hot-spot offset (pixels) */
|
||
|
int radius; /* Collision zone radius (pixels) */
|
||
|
SDL_Surface *surface;
|
||
|
} PIG_sprite;
|
||
|
|
||
|
/* Engine */
|
||
|
struct PIG_engine
|
||
|
{
|
||
|
/* Video stuff */
|
||
|
SDL_Surface *screen;
|
||
|
SDL_Surface *buffer; /* For h/w surface displays */
|
||
|
SDL_Surface *surface; /* Where to render to */
|
||
|
int pages; /* # of display VRAM buffers */
|
||
|
SDL_Rect view; /* Viewport pos & size (pixels) */
|
||
|
int page; /* Current page (double buffer) */
|
||
|
PIG_dirtytable *pagedirty[2]; /* One table for each page */
|
||
|
PIG_dirtytable *workdirty; /* The work dirtytable */
|
||
|
|
||
|
/* "Live" switches */
|
||
|
int interpolation;
|
||
|
int direct; /* 1 ==> render directly to screen */
|
||
|
int show_dirtyrects;
|
||
|
|
||
|
/* Time */
|
||
|
double time; /* Logic time (frames) */
|
||
|
int frame; /* Logic time; integer part */
|
||
|
|
||
|
/* Background graphics */
|
||
|
PIG_map *map;
|
||
|
|
||
|
/* Sprites and stuff */
|
||
|
PIG_object *objects;
|
||
|
PIG_object *object_pool;
|
||
|
int object_id_counter;
|
||
|
int nsprites;
|
||
|
PIG_sprite **sprites;
|
||
|
|
||
|
/* Logic frame global handlers */
|
||
|
void (*before_objects)(PIG_engine *pe);
|
||
|
void (*after_objects)(PIG_engine *pe);
|
||
|
|
||
|
/* Space for user data */
|
||
|
void *userdata;
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Engine
|
||
|
*/
|
||
|
PIG_engine *pig_open(SDL_Surface *screen);
|
||
|
void pig_close(PIG_engine *pe);
|
||
|
|
||
|
/* Set viewport size and position */
|
||
|
void pig_viewport(PIG_engine *pe, int x, int y, int w, int h);
|
||
|
|
||
|
/* Start engine at logic time 'frame' */
|
||
|
void pig_start(PIG_engine *pe, int frame);
|
||
|
|
||
|
/*
|
||
|
* Load a sprite palette image. The image is chopped up into
|
||
|
* sprites, based on 'sw' and 'sh', and added as new frames
|
||
|
* in the sprite bank. Default values:
|
||
|
* Hot-spot: (sw/2, sh/2)
|
||
|
* Collision radius: 0.2 * (sw + sh)
|
||
|
*
|
||
|
* Passing 0 for 'sw' and/or 'sh' makes pig_sprites() take
|
||
|
* the respective value from the image width and/or height.
|
||
|
*
|
||
|
* Returns the index of the first frame loaded.
|
||
|
*/
|
||
|
int pig_sprites(PIG_engine *pe, const char *filename, int sw, int sh);
|
||
|
|
||
|
/* Set hot-spot of sprite 'frame' to (hotx, hoty) */
|
||
|
int pig_hotspot(PIG_engine *pe, int frame, int hotx, int hoty);
|
||
|
|
||
|
/* Set sprite/sprite collision zone radius of 'frame' */
|
||
|
int pig_radius(PIG_engine *pe, int frame, int radius);
|
||
|
|
||
|
/* Advance logic time by 'frames' logic frames */
|
||
|
void pig_animate(PIG_engine *pe, float frames);
|
||
|
|
||
|
/*
|
||
|
* Manually add a dirtyrect for pig_refresh().
|
||
|
* 'dr' can be outside the engine viewport.
|
||
|
*/
|
||
|
void pig_dirty(PIG_engine *pe, SDL_Rect *dr);
|
||
|
|
||
|
/*
|
||
|
* Do what's needed to deal with the dirtyrects
|
||
|
* and then make the new frame visible.
|
||
|
*/
|
||
|
void pig_flip(PIG_engine *pe);
|
||
|
|
||
|
/*
|
||
|
* Refresh the viewport and any additional dirtyrects.
|
||
|
*
|
||
|
* Note that this does not refresh the entire viewport;
|
||
|
* only the areas that have actually changed!
|
||
|
*/
|
||
|
void pig_refresh(PIG_engine *pe);
|
||
|
|
||
|
/*
|
||
|
* Refresh the whole viewport, including sprites.
|
||
|
*/
|
||
|
void pig_refresh_all(PIG_engine *pe);
|
||
|
|
||
|
/* Render a sprite "manually", bypassing the engine */
|
||
|
void pig_draw_sprite(PIG_engine *pe, int frame, int x, int y);
|
||
|
|
||
|
/*
|
||
|
* Get the collision flags for the tile at (x, y),
|
||
|
* where the unit of x and y is pixels. The return
|
||
|
* is the PIG_sides flags for the tile, or PIG_NONE
|
||
|
* if (x, y) is outside the map.
|
||
|
*/
|
||
|
int pig_test_map(PIG_engine *pe, int x, int y);
|
||
|
|
||
|
/*
|
||
|
* Find the first "collidable" tile side when going from
|
||
|
* (x1, y1) to (x2, y2). 'mask' determines which tile sides
|
||
|
* are considered for collisions.
|
||
|
*
|
||
|
* Returns the side(s) hit, if any tile was hit. If the return
|
||
|
* is non-zero, the PIG_cinfo struct at 'ci' contains detailed
|
||
|
* information about the collision.
|
||
|
*/
|
||
|
int pig_test_map_vector(PIG_engine *pe, int x1, int y1, int x2, int y2,
|
||
|
int mask, PIG_cinfo *ci);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Map
|
||
|
*/
|
||
|
PIG_map *pig_map_open(PIG_engine *pe, int w, int h);
|
||
|
void pig_map_close(PIG_map *pm);
|
||
|
|
||
|
/* Load a tile palette image */
|
||
|
int pig_map_tiles(PIG_map *pm, const char *filename, int tw, int th);
|
||
|
|
||
|
/*
|
||
|
* Set tile collision info for 'count' tiles, starting at
|
||
|
* 'first'. Each tile in the tile palette has a set of
|
||
|
* PIG_sides flags that determine which sides the tile are
|
||
|
* considered for sprite/map collisions.
|
||
|
*/
|
||
|
void pig_map_collisions(PIG_map *pm, unsigned first, unsigned count,
|
||
|
PIG_sides sides);
|
||
|
|
||
|
/*
|
||
|
* Load a map from a string (one byte/tile). 'trans'
|
||
|
* is a string used for translating 'data' into integer
|
||
|
* tile indices. Each position in 'trans' corresponds
|
||
|
* to one tile in the tile palette.
|
||
|
*/
|
||
|
int pig_map_from_string(PIG_map *pm, const char *trans, const char *data);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Object
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Create an object with the initial position (x, y). If
|
||
|
* 'last' is 1, the object will end up last in the
|
||
|
* processing and rendering order, otherwise, first.
|
||
|
*
|
||
|
* Note that relative processing order is very important
|
||
|
* to objects that chase each other and stuff like that!
|
||
|
* If they're placed in the "wrong" order, the tracking
|
||
|
* objects get an extra frame of reaction time, which is
|
||
|
* annoying if it's not what you intend.
|
||
|
*/
|
||
|
PIG_object *pig_object_open(PIG_engine *pe, int x, int y, int last);
|
||
|
|
||
|
/*
|
||
|
* Delete an object.
|
||
|
*
|
||
|
* Note that objects are never actually deleted. Instead,
|
||
|
* they are placed in a free pool, where pig_object_open()
|
||
|
* looks for objects to recycle.
|
||
|
*
|
||
|
* In fact, they are not even freed when you ask for it,
|
||
|
* but rather kept around until the next rendered frame,
|
||
|
* so they can be removed from the screen correctly.
|
||
|
*/
|
||
|
void pig_object_close(PIG_object *po);
|
||
|
|
||
|
/*
|
||
|
* Close all objects.
|
||
|
*/
|
||
|
void pig_object_close_all(PIG_engine *pe);
|
||
|
|
||
|
/*
|
||
|
* Find object by 'id', starting at object 'start'.
|
||
|
*
|
||
|
* The search starts at 'start' and is done in both
|
||
|
* directions in parallel, assuming that the matching
|
||
|
* object is near 'start' in the list. (It usually is
|
||
|
* when dealing with linked objects.)
|
||
|
*
|
||
|
* Returns NULL if the object was not found.
|
||
|
*/
|
||
|
PIG_object *pig_object_find(PIG_object *start, int id);
|
||
|
|
||
|
#endif /* PIG_ENGINE_H */
|