/****************************************************************************
*
* BIGGUN.C
*
* PROGRAMMER : Chris
*    VERSION : 00.00
*    CREATED : 22/09/98
*   MODIFIED : 22/09/98
*	   TABS : 5
*
* DESCRIPTION
*	Big Gun controller
*
*****************************************************************************/

/*---------------------------------------------------------------------------
 * Import Headers
\*--------------------------------------------------------------------------*/

#ifdef PSX_VERSION
#include <stdio.h>
#include "../spec_psx/typedefs.h"
#include "../spec_psx/psxinput.h"
#include "../spec_psx/malloc.h"
#include "../spec_psx/specific.h"
#include "../spec_psx/maths.h"
#include "../spec_psx/gen_draw.h"
#define BIGGUNINFO_ONLY
#else
#include "../specific/standard.h"
#include "../specific/global.h"
#include "../specific/input.h"
#endif

#include "objects.h"
#include "lara.h"
#include "laraanim.h"
#include "laraswim.h"
#include "control.h"
#include "input.h"
#include "camera.h"
#include "sound.h"
#include "sphere.h"
#include "traps.h"
#include "laraflar.h"
#include "lot.h"
#include "effect2.h"
#include "text.h"
#include "savegame.h"
#include "biggun.h"

/*---------------------------------------------------------------------------
 * Constants
\*--------------------------------------------------------------------------*/

#define DEBUG_BIGGUN

#define RECOIL_TIME 26
#define RECOIL_START 20
#define RECOIL_Z	96		// barrel movement

#define ROCKET_SPEED 512

/* -------- gun flags */

#define BGF_UPDOWN	1
#define BGF_AUTOROT	2
#define BGF_GETOFF	4
#define BGF_FIRE	8

/* -------- animation definitions */

enum {
	BGUNS_GETON,
	BGUNS_GETOFF,
	BGUNS_UPDOWN,
	BGUNS_RECOIL
};

#define GF2(a)	(anims[objects[BIGGUN].anim_index+a].frame_base)

#define BGUN_GETON_A	0
#define BGUN_GETOFF_A	1
#define BGUN_UPDOWN_A	2
#define BGUN_RECOIL_A	3

#define BGUN_GETON_F	GF2(BGUN_GETON_A)
#define BGUN_GETOFF_F	GF2(BGUN_GETOFF_A)
#define BGUN_UPDOWN_F	GF2(BGUN_UPDOWN_A)
#define BGUN_RECOIL_F	GF2(BGUN_RECOIL_A)

#define BGUN_GETON_NFRAMES	48
#define BGUN_GETOFF_NFRAMES	12
#define BGUN_UPDOWN_NFRAMES	59
#define BGUN_RECOIL_NFRAMES	20

/*---------------------------------------------------------------------------
 * Externals
\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 * Globals
\*--------------------------------------------------------------------------*/

#if defined(PSX_VERSION) && defined(RELOC)

void BigGunInitialise(sint16 item_number);
int  BigGunControl(COLL_INFO *coll);
void BigGunCollision(sint16 item_number, ITEM_INFO *litem, COLL_INFO *coll);
void BigGunDraw(ITEM_INFO *v);

void *func[] __attribute__((section(".header"))) = {
	BigGunInitialise,
	BigGunControl,
	BigGunCollision,
	BigGunDraw
};

#endif

/*---------------------------------------------------------------------------
 * Locals
\*--------------------------------------------------------------------------*/

static long GunRotYAdd = 0;

/*---------------------------------------------------------------------------
 * Macros
\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 * Local Functions
\*--------------------------------------------------------------------------*/

PHD_VECTOR PooPos = { 0, 0, 0 };

/****************************************************************************
* FireBigGun
*
* SYNOPSIS
*	void FireBigGun()
*
* PURPOSE
*	Fires a shell from the big gun
*
* INPUTS
*	None
*
* RETURNS
*	Nothing
*/
void FireBigGun(ITEM_INFO *v)
{
	sint16 item_number;

	if ((item_number = CreateItem()) != NO_ITEM)
	{
		int lp;
		PHD_VECTOR pos;
		ITEM_INFO *item;
		long	x,y,z,wx,wy,wz,xv,yv,zv;
		BIGGUNINFO *gun = (BIGGUNINFO *)v->data;

		item = &items[item_number];

		item->object_number = ROCKET;
		item->room_number = lara_item->room_number;

		pos.x = 0;
		pos.y = 0;
		pos.z = 256;
		GetJointAbsPosition(v, &pos, 2);

		item->pos.x_pos = pos.x;
		item->pos.y_pos = pos.y;
		item->pos.z_pos = pos.z;

		InitialiseItem(item_number);

		item->pos.x_rot = -((gun->RotX-32) * (ONE_DEGREE));
		item->pos.y_rot = v->pos.y_rot;
		item->pos.z_rot = 0;
		item->speed = ROCKET_SPEED>>5;
		item->item_flags[0] = 1;

		AddActiveItem(item_number);

		SoundEffect(77, &item->pos, 0);
		SoundEffect(105, &item->pos, PITCH_SHIFT|0x2000000);

	/* -------- trigger effects */

		SmokeCountL = 32;
		SmokeWeapon = LG_ROCKET;

		for ( lp=0 ; lp<5 ; lp++ )
			TriggerGunSmoke(pos.x, pos.y, pos.z, 0,0,0, 1, LG_ROCKET, 32);

		phd_PushUnitMatrix();
#ifdef PSX_VERSION
		mSetTrans(0, 0, 0);
#else
		*(phd_mxptr+M03) = 0;
		*(phd_mxptr+M13) = 0;
		*(phd_mxptr+M23) = 0;
#endif
		phd_RotYXZ(item->pos.y_rot, item->pos.x_rot, item->pos.z_rot);
		phd_PushMatrix();
		phd_TranslateRel( 0, 0, -128);
#ifdef PSX_VERSION
		wx = Matrix->tx;
		wy = Matrix->ty;
		wz = Matrix->tz;
#else
		wx = (*(phd_mxptr+M03)>>W2V_SHIFT);
		wy = (*(phd_mxptr+M13)>>W2V_SHIFT);
		wz = (*(phd_mxptr+M23)>>W2V_SHIFT);
#endif
		phd_PopMatrix();

		for (lp=0;lp<8;lp++)
		{
			phd_PushMatrix();
			phd_TranslateRel( 0, 0, -(GetRandomControl()&2047));
#ifdef PSX_VERSION
			xv = Matrix->tx;
			yv = Matrix->ty;
			zv = Matrix->tz;
#else
			xv = (*(phd_mxptr+M03)>>W2V_SHIFT);
			yv = (*(phd_mxptr+M13)>>W2V_SHIFT);
			zv = (*(phd_mxptr+M23)>>W2V_SHIFT);
#endif
			phd_PopMatrix();
			TriggerRocketFlame(wx,wy,wz,xv-wx,yv-wy,zv-wz,item_number);
		}
		phd_PopMatrix();
	}
}
/****************************************************************************
* CanUseGun
*
* SYNOPSIS
*	int CanUseGun(ITEM_INFO *v, ITEM_INFO *l)
*
* PURPOSE
*	Determines whether Lara can use the gun
*
* INPUTS
*	v		= gun item
*	l		= Lara's item
*
* RETURNS
*	!0 if Lara can use the gun
*/
static int CanUseGun(ITEM_INFO *v, ITEM_INFO *l)
{
	int dist;
	sint32 x, z;

	if ((!(input & IN_ACTION))
	||  (lara.gun_status != LG_ARMLESS)
	||  (l->gravity_status))
		return 0;

	x = l->pos.x_pos - v->pos.x_pos;
	z = l->pos.z_pos - v->pos.z_pos;

	dist = (x * x) + (z * z);

	if (dist > 30000)
		return 0;
	else
		return 1;
}

/*---------------------------------------------------------------------------
 * Public Functions
\*--------------------------------------------------------------------------*/

/****************************************************************************
* BigGunInitialise
*
* SYNOPSIS
*	void BigGunInitialise(sint16 item_number)
*
* PURPOSE
*	Initialises the big gun
*
* INPUTS
*	item_number	= vehicle item #
*
* RETURNS
*	Nothing
*/
void BigGunInitialise(sint16 item_number)
{
	ITEM_INFO *v;
	BIGGUNINFO *gun;

	v = &items[item_number];
	gun = (BIGGUNINFO *)v->data = game_malloc(sizeof(BIGGUNINFO), 0);

	gun->Flags = gun->FireCount = 0;
	gun->RotX = 30;
	gun->RotY = 0;
	gun->StartRotY = v->pos.y_rot;
}
/****************************************************************************
* BigGunCollision
*
* SYNOPSIS
*	void BigGunCollision(sint16 item_number, ITEM_INFO *litem, COLL_INFO *coll)
*
* PURPOSE
*	Determines whether Lara wants to use the big gun
*
* INPUTS
*	item_number	= vehicle item #
*	litem		= ptr to Lara's item structure
*	coll			= ptr to Lara's collision structure
*
* RETURNS
*	Nothing
*/
void BigGunCollision(sint16 item_number, ITEM_INFO *l, COLL_INFO *coll)
{
	ITEM_INFO *v;

	if ((l->hit_points < 0)
	||  (lara.skidoo != NO_ITEM))
		return;

	v = &items[item_number];

	if (CanUseGun(v, l))
	{
		BIGGUNINFO *gun;

		lara.skidoo = item_number;

	/* -------- throw flare away if using */

		if (lara.gun_type == LG_FLARE)
		{
			CreateFlare(0);
			undraw_flare_meshes();
			lara.flare_control_left = 0;
			lara.request_gun_type = lara.gun_type = LG_ARMLESS;
		}

	/* -------- initiate animation */

		lara.gun_status = LG_HANDSBUSY;

		l->pos.x_pos = v->pos.x_pos;
		l->pos.y_pos = v->pos.y_pos;
		l->pos.z_pos = v->pos.z_pos;
		l->pos.x_rot = v->pos.x_rot;
		l->pos.y_rot = v->pos.y_rot;
		l->pos.z_rot = v->pos.z_rot;

		l->anim_number = objects[VEHICLE_ANIM].anim_index + BGUN_GETON_A;
		l->frame_number = BGUN_GETON_F;
		l->current_anim_state = l->goal_anim_state = BGUNS_GETON;

		gun = (BIGGUNINFO *)v->data;
		gun->Flags = 0;
		gun->RotX = 30;
	}
	else
		ObjectCollision(item_number, l, coll);
}
/****************************************************************************
* BigGunDraw
*
* SYNOPSIS
*	void BigGunDraw(ITEM_INFO *v)
*
* PURPOSE
*	Draws the big gun
*
* INPUTS
*	v		= ptr to the big gun item
*
* RETURNS
*	Nothing
*/
void BigGunDraw(ITEM_INFO *v)
{
	sint16 *frmptr[2];
	OBJECT_INFO *object;
	int clip, rate, frac;

	frac = GetFrames(v, frmptr, &rate);

	object = &objects[v->object_number];

	phd_PushMatrix();
	phd_TranslateAbs(v->pos.x_pos, v->pos.y_pos, v->pos.z_pos);
	phd_RotYXZ(v->pos.y_rot, v->pos.x_rot, v->pos.z_rot);

	if ((clip = S_GetObjectBounds(frmptr[0])))
	{
		sint32 *bone;
		sint16 **meshpp;
		sint16 *rotation;
		BIGGUNINFO *gun = (BIGGUNINFO *)v->data;

		CalculateObjectLighting(v, frmptr[0]);

		meshpp = &meshes[object->mesh_index];	// Mesh Pointers
		bone = bones + object->bone_index;      // Bone pointer

		if (!frac)
		{
			phd_TranslateRel((sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8));
			rotation = frmptr[0] + 9;
			gar_RotYXZsuperpack(&rotation, 0);
			phd_PutPolygons(*meshpp++, clip);

			phd_TranslateRel(*(bone+1), *(bone+2), *(bone+3));
			gar_RotYXZsuperpack(&rotation, 0);
			phd_PutPolygons(*meshpp++, clip);

		/* -------- draw barrel */

			phd_TranslateRel(*(bone+1+4), *(bone+2+4), *(bone+3+4));
			gar_RotYXZsuperpack(&rotation, 0);

/*			if (gun->Flags & BGF_FIRE)
			{
				sint32 z;

				if (gun->FireCount > RECOIL_START)
					z = -RECOIL_Z;
				else
					z = -(RECOIL_Z - ((RECOIL_TIME - gun->FireCount) << 2));

				phd_TranslateRel(0, 0, z);
			}*/

			phd_PutPolygons(*meshpp++, clip);
		}
		else
		{
			sint16 *rotation2;

#ifdef PSX_VERSION
			InitInterpolate(frac, rate, &iMatrixStack[0]);
#else
			InitInterpolate(frac, rate);
#endif
			rotation = frmptr[0] + 9;
			rotation2 = frmptr[1] + 9;

			phd_TranslateRel_ID((sint32)*(frmptr[0]+6), (sint32)*(frmptr[0]+7), (sint32)*(frmptr[0]+8),
							(sint32)*(frmptr[1]+6), (sint32)*(frmptr[1]+7), (sint32)*(frmptr[1]+8));
			gar_RotYXZsuperpack_I(&rotation, &rotation2, 0);
			phd_PutPolygons_I(*meshpp++, clip);

			phd_TranslateRel_I(*(bone+1), *(bone+2), *(bone+3));
			gar_RotYXZsuperpack_I(&rotation, &rotation2, 0);
			phd_PutPolygons_I(*meshpp++, clip);

		/* -------- draw barrel */

			phd_TranslateRel_I(*(bone+1+4), *(bone+2+4), *(bone+3+4));
			gar_RotYXZsuperpack_I(&rotation, &rotation2, 0);
			phd_PutPolygons_I(*meshpp++, clip);
		}
	}
	phd_PopMatrix();
}
/****************************************************************************
* BigGunControl
*
* SYNOPSIS
*	int BigGunControl()
*
* PURPOSE
*	Big gun controller
*
* INPUTS
*	None
*
* RETURNS
*	0 if not under vehicle's control
*/
int BigGunControl(COLL_INFO *coll)
{
	BIGGUNINFO *gun;
	ITEM_INFO *v, *l;

	l = lara_item;
	v = &items[lara.skidoo];
	gun = (BIGGUNINFO *)v->data;

/* -------- control vehicle */

	if (gun->Flags & BGF_UPDOWN)
	{
		if ((l->hit_points <= 0)
		||  (input & IN_ROLL))
			gun->Flags = BGF_AUTOROT;

		else if ((input & IN_ACTION) && gun->FireCount == 0 )
		{
			FireBigGun(v);

//			gun->Flags = BGF_FIRE;
			gun->FireCount = RECOIL_TIME;
		}

		else
		{
			if (input & IN_LEFT)
			{
				if (GunRotYAdd > 0)
					GunRotYAdd >>= 1;

				GunRotYAdd -= 8;
				if (GunRotYAdd < -64)
					GunRotYAdd = -64;
				if ((wibble&7)==0 && (abs(gun->RotY) < (136<<2)))
					SoundEffect(44, &v->pos, 0);
			}

			else if (input & IN_RIGHT)
			{
				if (GunRotYAdd < 0)
					GunRotYAdd >>= 1;

				GunRotYAdd += 8;
				if (GunRotYAdd > 64)
					GunRotYAdd = 64;

				if ((wibble&7)==0 && (abs(gun->RotY) < (136<<2)))
					SoundEffect(44, &v->pos, 0);
			}

			else
			{
				GunRotYAdd -= GunRotYAdd>>2;
				if (abs(GunRotYAdd) < 8)
					GunRotYAdd = 0;
			}

			gun->RotY += GunRotYAdd>>2;
			if (gun->RotY < -(136<<2))
			{
				gun->RotY = -(136<<2);
				GunRotYAdd = 0;
			}
			else	if (gun->RotY > (136<<2))
			{
				gun->RotY = (136<<2);
				GunRotYAdd = 0;
			}


			if ((input & IN_FORWARD) && (gun->RotX < BGUN_UPDOWN_NFRAMES))
				gun->RotX++;

			else if ((input & IN_BACK) && (gun->RotX))
				gun->RotX--;
		}
	}

	if (gun->Flags & BGF_AUTOROT)
	{
		if (gun->RotX < 30)
			gun->RotX++;
		else if (gun->RotX > 30)
			gun->RotX--;
		else
		{
			l->anim_number = objects[VEHICLE_ANIM].anim_index + BGUN_GETOFF_A;
			l->frame_number = BGUN_GETOFF_F;
			l->current_anim_state = l->goal_anim_state = BGUNS_GETOFF;
			gun->Flags = BGF_GETOFF;
		}
	}

/* -------- animate Lara and vehicle */

	switch (l->current_anim_state)
	{
		case BGUNS_GETON:
		case BGUNS_GETOFF:
			AnimateItem(l);
			v->anim_number = objects[BIGGUN].anim_index + (l->anim_number - objects[VEHICLE_ANIM].anim_index);
			v->frame_number = anims[v->anim_number].frame_base + (l->frame_number - anims[l->anim_number].frame_base);

			if ((gun->Flags & BGF_GETOFF)
			&&  (l->frame_number == anims[l->anim_number].frame_end))
			{
				l->anim_number = STOP_A;
				l->frame_number = STOP_F;
				l->current_anim_state = l->goal_anim_state = AS_STOP;
				lara.skidoo = NO_ITEM;
				lara.gun_status = LG_ARMLESS;
			}
			break;

		case BGUNS_UPDOWN:
			l->anim_number = objects[VEHICLE_ANIM].anim_index + BGUN_UPDOWN_A;
			l->frame_number = BGUN_UPDOWN_F + gun->RotX;

			v->anim_number = objects[BIGGUN].anim_index + (l->anim_number - objects[VEHICLE_ANIM].anim_index);
			v->frame_number = anims[v->anim_number].frame_base + (l->frame_number - anims[l->anim_number].frame_base);

			if (gun->FireCount)
				gun->FireCount--;
//			else
  			gun->Flags = BGF_UPDOWN;
			break;
	}

	l->pos.y_rot = v->pos.y_rot = gun->StartRotY + (gun->RotY * (ONE_DEGREE>>2));

	coll->enable_spaz = 0;
	coll->enable_baddie_push = 0;
	LaraBaddieCollision(l, coll);

	camera.target_elevation = -15*ONE_DEGREE;
	return 1;
}
