
// Adapted from Glover

// NB NB NB - This header is also loaded by cr_lev
// (so be careful about referring to any non-platformy structures)

#ifndef _PLATFORM_H_
#define _PLATFORM_H_


//#ifndef _COLLIDE_H
//	#include "collide.h"	// rqd for collbox
//#endif
// This is tidier than requiring collide.
typedef struct tagCOLLBOX *COLLBOXPTR;
typedef struct tagCOLLDATA *COLLDATAPTR;

typedef struct TEXANIM *TEXANIMPTR;





#ifndef _MAPDRAW_H_
	#include "map_draw.h"	// rqd for fma_mesh_header
#endif

#define BFF_PLATFORM_ID (('P'<<0) | ('L'<<8) | ('T'<<16) | (1<<24))


//#define WAITFORHANDSLAM		(-1)
//#define WAITFORBALLHIT		(-3)
#define WAITFOREVER			(-100)

#define X_PLATAXIS 0
#define Y_PLATAXIS 1
#define Z_PLATAXIS 2

#define PLAT_SEESAW		0
#define PLAT_PENDULUM	1

#define MAX_OBJDEBRIS 10
#define MAX_CHILD_PLATS 4

#define MAGNET_RANGE	64

// ========= platform-reading ===========

typedef struct PLATFORM_POINTTAG
{
	int		posx;
	int		posy;
	int		posz;
	int		wait;
}PLATFORM_POINTSTR;

typedef struct PLATFORM_POINTX_TAG
{
	int		speed;
	int		accel;
}PLATFORM_POINTX_STR;

// bitmasks for the platform's "flags" short
#define PLATFLAG_NOCOLLISION		1
#define PLATFLAG_IS_RESTART			2

#define PLATFLAG_DESTRUCTIBLE		4
#define PLATFLAG_DESTRUCT_BALL		8
#define PLATFLAG_DESTRUCT_NODUST	16

#define PLATFLAG_CRUMBLE			32

#define PLATFLAG_NOCAMCOLLISION		64

#define PLATFLAG_EXITDOOR			128


#define PLATFLAG_TELEPORT			256
#define PLATFLAG_HOOP				512


// If the conveyor bits are set, then the "definfo/info" bits are full of useful stuff
#define PLATFLAG_CONVEYOR			1024
#define PLATFLAG_PUSHABLE			2048
#define PLATFLAG_ROTATING			4096
#define PLATFLAG_VARISPEED			8192

#define PLATFORM_SPRINGBOARD		16384
#define PLATFLAG_NONMECOLLISION		32768
// at 32768... & then we run out, but we have got 8 bits of "pad" byte still there

typedef struct tagPLATFORMWORK *PLATFORMHANDLE;




typedef struct PLATFORM_CONVEY_DEFSTR
{
	short ya;
	short dummy;
	int speed;

	int accel;
	TEXANIMPTR texanim;	// loaded in as a tag
	int texanim_speedratio_world;
	int texanim_speedratio_tex;

}PLATFORM_CONVEY_DEFSTR;

#define PUSHABLE_BY_GINGER	1
#define PUSHABLE_BY_ROCKY	2
#define PUSHABLE_BY_NICK	4
#define PUSHABLE_BY_FETCHER	8
#define PUSHABLE_KEEPS_GOING	256
typedef struct PLATFORM_PUSHABLE_DEFSTR
{
//	int character_mask;
	int push_flags;
}PLATFORM_PUSHABLE_DEFSTR;
#define PLATROTATE_XAXIS 0
#define PLATROTATE_YAXIS 1
#define PLATROTATE_ZAXIS 2
typedef struct PLATFORM_ROTATING_DEFSTR
{
	int speed;
	char axis;
	char dummy[3];
}PLATFORM_ROTATING_DEFSTR;
typedef struct PLATFORM_BIG_DUMMY_DEFSTR
{
	int temp[10];
}PLATFORM_BIG_DUMMY_DEFSTR;

typedef struct PLATFORM_CONVEY_WORKSTR
{
	int speed;	// note - can be -ve
	int targ_speed;
}PLATFORM_CONVEY_WORKSTR;
typedef struct PLATFORM_PUSHABLE_WORKSTR
{
	VECTOR pushvec;
	int moving;
}PLATFORM_PUSHABLE_WORKSTR;

// Structure elements that need converting after bff-load are marked "<BFF>"

// The .raw collision format is (n_boxes),(boxes)
// However, platforms do need a collision radius in there, because using the visible mesh radius instead can cause probs.
// It could feasibly be calculated from the boxes at load-time fairly easily


typedef struct COLLIDEBOUNDSTR
{
	COLLBOXPTR pBoxes;
	int		nCollBoxes;
	int minx,miny,minz;
	int maxx,maxy,maxz;
}COLLIDEBOUNDSTR;

typedef struct tagPLATFORMDEF
{
	unsigned long id;		// Standard BFF header
	unsigned long len;
	unsigned long crc;


// --- Definition constants ---

	char n_points;				// number of movement points.
	char pad;
	short flags;
	PLATFORM_POINTSTR * points;	// <BFF>=OFFSET for conversion to pointer

// um. Where do we get the collision mesh from dude? Generate / Load
//	COLLBOXPTR pBoxes;
//	int		nCollBoxes;
	void *removed_old_collboxes;
	int	def_sort_depth;

	short	tag;		// puzzles refer to platforms by tag
	short	parenttag;	// used for linkage. post-load, we need to do the two-way child/parent pointer thing like what Chris did on Glover

	int		start_point;
	int		maxspeed;
	int		accel;

	short	terrain;
	short pad2;

	FMA_MESH_HEADER *mesh;	// <BFF>=CRC, multiple platforms can use the same mesh.

// If we assume that things like conveyor/tilty/spinny/magnetic are mutually exclusive, then
// this can be a union, and what's in it can be determined by the flags
	union
	{
		PLATFORM_CONVEY_DEFSTR conveyor;
		PLATFORM_PUSHABLE_DEFSTR pushable;
		PLATFORM_ROTATING_DEFSTR rotating;
		PLATFORM_BIG_DUMMY_DEFSTR dummy;
	}definfo;

	PLATFORM_POINTX_STR *vari_speeds;

	COLLIDEBOUNDSTR coll_bounds;

	unsigned long crcSample;
	int	start_x,start_y,start_z;
	int dummy[1];

// Stuff Snipped from the Glover version....	
/*
	short	move_sfx;	// high byte = bank
	short	move_pitch;
	short	move_vol;
	short	stop_sfx;	// high byte = bank
	short	stop_pitch;
	short	stop_vol;
	short	dest_sfx;	// high byte = bank
	short	dest_pitch;
	short	dest_vol;
*/
/*
	struct
	{
		int		nBits;
		ULONG	fragID;	// crc of the frag model
	}destruct;

	struct
	{
		short yes;
		short limit;
		short time;
		short grav;
	}crumble;

	struct
	{
		short yes;
		short axis;
		short angle;
		short friction;
		short headstart;
		short pad;
	}pendulum;

	struct
	{
		short yes;
		short axis;
		short inertia;
		short levelout;
		short maxrot;
		short initrot;
		short maxspeed;
		short mass;
	}seesaw;				// she saw a seesaw on the sea shore

	struct
	{
		short yes;
		short axis;
		short initrot;
		short rotspeed;
	}spin;

	struct
	{
		short yes,mass;
		short pX,pY,pZ;
		short aX,aY,aZ;
	}confine;

	struct
	{
		short yes, axis;
		short angle, pad;
	}topple;

	struct
	{
		short yes, times;
		short wait, angle;
	}spinpause;

	struct
	{
		short yes, pad;
		short wait, angle;
	}spinflip;

	struct
	{
		short yes, pad;
	}magnet;
*/
}PLATFORMDEF;

typedef struct tagPLATFORMWORK
{
	struct tagPLATFORMWORK *next;	// Linked list of all platforms (active or not)
	PLATFORMDEF *def;

	int coll_radius;

// --- Variables ---
	char active;
	char generated_collision;
	char dummy[2];

	struct tagPLATFORMWORK *pParentPlat;		// pointers to fill in
	struct tagPLATFORMWORK *pFirstChildPlat;
	struct tagPLATFORMWORK *pNextChildPlat;

// In glover, platform coordinates were 4096'd. In CRun, that shouldn't be necessary
	VECTOR	pos;			// parent-relative
	VECTOR  oldPos;			// old parent-relative
	VECTOR	cumPos;			// world coordinates taking parents into account

	WORLD_MESH_INSTANCE instance;

	struct
	{
		int waitTimer;
		int pointCtr;		// countdown of # of points to move, or -1 for forever
//		int progress;
		int point;			// point we're at, or are moving from
		int speed;

		VECTOR realVel;		// CPW: used to determine whether or not plat is moving

		char jap;			// just arrived at this point
		char moving;
		char dummy[2];

	}move;

	union
	{
		PLATFORM_CONVEY_WORKSTR conveyor;
		PLATFORM_PUSHABLE_WORKSTR pushable;
	}info;

	struct
	{
		void *pSample;
		short touchCounter;
		short playing;
	}noisy;

// Stuff snipped from the Glover version...
//	VECTOR	platOffs;	// unused in glover, even
//	int platNumber;		// used for model & fragmodel
//	int nChildPlats;

/* - contents of platloaded (indexed by platNumber) - designed for instances of things
	ULONG crc;				// CRC of filename of box (minus .ext)
	NEWMODEL *pModel;		// pointer to PSA model
	NEWMODEL *pFragModel;	// pointer to fragment model (if any)
	int nCollBoxes;			// number of collboxes in collision
	COLLBOX *pBoxes;		// pointer to collision mesh
	int collRadiusSqr;
*/



//	PLATFORM_DEFSTR *pPlatDef;	// the defstr should be inlined with this lot


/*
//	int		tiltSpin;
	int deadTimer;			// wobbling counter for collapsing plats
	int ballTouchTime;
	int ballTouchFrame;
	int ballTouchCounter;
	int handTouchTime;

	int snatch_ball_time;	// used by the exit portals to ensure the ball doesn't escape
	char moveticker;		// used as an interval ticker for sound effects
*/
/*

	struct
	{
		int timer;
		int progress;
		int dist;
	}seesaw;

	struct
	{
		int progress;
		int dir;
	}pendulum;

	struct
	{
		int timer;
		int currentAngle;
		int progress;
		VECTOR touchPos;
	}spin;
	
	struct
	{
		int progress;	// progress (zeroed when at point)
		int timer;
		int jar;		// just at rot
	}spinpause;

	struct
	{
		int progress;
		int dir;
		int timer;
	}spinflip;

	struct
	{
		int progress;
		int falling;
		int jar;
	}topple;

	struct
	{
		int active;
		int atEnd;
		int movingToMiddle;
	}magnet;
*/
}PLATFORMWORK;


typedef struct COLL_PLAT_CACHE
{
	PLATFORMWORK *platform;
	int set_time;
	VECTOR set_world;

	int grabbed_time;
	int grabbed_ya;
	VECTOR plat_relative;
}COLL_PLAT_CACHE;


// ========= sound triggers on the landscape ===========
typedef struct
{
	int px,py,pz;
	int sx,sy,sz;
	int flags;
	void *sample;	// comes in as a CRC, and is converted to a (SfxSampleType *) pointer postload 
	int dummy[4];	// coz I suspect we'll need a bit of space without needing to change the bff id too often
}SOUND_TRIGGER;

#define BFF_STRIG_ID (('S'<<0) | ('T'<<8) | ('G'<<16) | (0<<24))

#define TRIGGER_SAMPLE_DOGCALL	(1<<0)
#define TRIGGER_SAMPLE_HASBOX	(1<<1)

#define TRIGGER_SAMPLE_SPLASH	(1<<16)

typedef struct SOUNDTRIGGERLIST
{
	unsigned long id;			// standard BFF header
	unsigned long len;
	unsigned long name_crc;
	int n_sound_triggers;
// Followed by an array
}SOUNDTRIGGERLIST;



/*
typedef struct tagPLATSLOADED
{
	ULONG crc;				// CRC of filename of box (minus .ext)
	NEWMODEL *pModel;		// pointer to PSA model
	NEWMODEL *pFragModel;	// pointer to fragment model (if any)
	int nCollBoxes;			// number of collboxes in collision
	COLLBOX *pBoxes;		// pointer to collision mesh
	int collRadiusSqr;
}PLATSLOADED;
*/
/*
typedef struct _BodgeCtrl
{
	DYNCOLLBOX	*carnivalPlat;
	UBYTE		hitPlat;
}BODGECTRL;

extern	BODGECTRL	BodgeCtrl;
*/

/*
extern DYNCOLLBOXLIST dyncollList;
extern PLATSLOADED pPlatsLoaded[];

void platformInitialise(void); // initialises platform system (also used for resetting)
int platformCreatePlatform(PLATFORM_DEFSTR *def);
void platformGenerateShrapnel(VECTOR *pPos, int nBits, int radius, NEWMODEL *pModel, NEWMODEL *pFragModel);

void platformCreateDynamicBoxList(void);
DYNCOLLBOX *platformCreateDynamicBox(int);
void platformLinkDynamicBox(COLLBOX **, NEWMODEL *pModel, int);
void platformMoveDynamicBox(DYNCOLLBOX *pBox, int x, int y, int z);
void platformDrawDynamicBoxes(void);
void platformDestroyDynamicBox(DYNCOLLBOX *ptr);
ULONG platformTryAndFindInstance(ULONG crc);
void platformSmash(DYNCOLLBOX *pBox);
//void platformLoadFragmentBodge(void);
void platformEndFile(void);

void platformStartMove(DYNCOLLBOX *pBox,int flags, int value);
*/


// Before BFF-loading
void platformInit();
// After BFF-loading
void platformSetup();
void platformShutdown();
void platformDraw();
void platformCycle();

// The linked list of all platforms
extern PLATFORMWORK *platform_list;
extern char platcoll_is_enemy;

// CPW: extern SfxSampleType *pSplashSample;

void platformLinkBFF(PLATFORMDEF *platformdef);

PLATFORMWORK *platformFindPlatform(int tag);
// These are actually defines...
void platformEnable(PLATFORMWORK *platform, int enable);
int platformIsEnabled(PLATFORMWORK *platform);
void platformSetDepth(PLATFORMWORK *platform, int depth);

// Here's the defines for quick & easy functions

#define platformEnable(platform, enable) (platform)->active = (enable);
#define platformIsEnabled(platform) ((platform)->active)
#define platformSetDepth(platform,depth) ((platform)->instance.sort_depth = (depth))


#define PLATWILLPUSH_OK				0
#define PLATWILLPUSH_NOT_ON_AXIS	1
#define PLATWILLPUSH_ENDOFRUN		2
#define PLATWILLPUSH_BLOCKED		3
#define PLATWILLPUSH_WRONGCHAR		4
int platformWillPush(PLATFORMWORK *platform, VECTOR *pushdirn);


int platformIsFreeToMove(PLATFORMWORK *platform, VECTOR *newpos);

void platformSetSpeed(PLATFORMWORK *platform, int speed);
void platformSetConveyorSpeed(PLATFORMWORK *platform, int speed);
void platformSetRotSpeed(PLATFORMWORK *platform, int speed);


// Acutally in platcoll.c
//void platformCheckCollision(COLLDATA *pD, int *pnPolysHit);
void platformCheckCollision(COLLDATAPTR pD, int *pnPolysHit);

void platformSetTerrain(PLATFORMWORK *plat, int terrain);

void platformDestroy(PLATFORMWORK *plat);
int platformAtPoint(PLATFORMWORK *platform, int p);


// Sound-Trigger-boxes
#ifdef __ISLSFX2_H__

void platformLinkSoundTriggerBFF(SOUNDTRIGGERLIST *list);

SfxSampleType * platformCheckForSampleTriggers(VECTOR *pos, int *flag_ret);
#endif

#endif
