/* KJL 16:14:35 09/09/98 - BonusAbilities.c */
#include "3dc.h"
#include "module.h"
#include "inline.h"

#include "stratdef.h"
#include "gamedef.h"
#include "bh_types.h"

#include "weapons.h"
#include "dynblock.h"
#include "avpview.h"

#include "load_shp.h"
#include "kzsort.h"
#include "kshape.h"

#include "pfarlocs.h"
#include "pvisible.h"

#define UseLocalAssert Yes
#include "ourasert.h"


extern int NormalFrameTime;



/* KJL 16:18:20 09/09/98 - Predator Grappling Hook */
struct GrapplingHookData
{
	int IsEmbedded;
	int IsEngaged;
	int Tightness;

	VECTORCH Position;
	MATRIXCH Orientation;
	int ShapeIndex;
	DISPLAYBLOCK *DispPtr;
}; 

static struct GrapplingHookData GrapplingHook;

static DISPLAYBLOCK* CreateGrapplingHook(void);


extern void InitialiseGrapplingHook(void)
{
	GrapplingHook.IsEngaged = 0;
	GrapplingHook.IsEmbedded = 0;
	GrapplingHook.ShapeIndex = GetLoadedShapeMSL("spear");
	GrapplingHook.DispPtr = 0;
	
}

static void FireGrapplingHook(void);
void DisengageGrapplingHook(void);

extern void ActivateGrapplingHook(void)
{	
	if (GrapplingHook.IsEngaged)
	{
		DisengageGrapplingHook();
	}
	else
	{
		FireGrapplingHook();
	}
}
static void FireGrapplingHook(void)
{
	GrapplingHook.DispPtr = CreateGrapplingHook();
	
	if (GrapplingHook.DispPtr)
	{
		GrapplingHook.IsEngaged = 1;
		GrapplingHook.IsEmbedded = 0;
		GrapplingHook.Tightness = ONE_FIXED;

		/* CDF 14/4/99 Make a sound... */
		Sound_Play(SID_GRAPPLE_THROW,"h");

		#if 0
		/* los */
		GrapplingHook.Position = PlayersTarget.Position;
		GrapplingHook.Orientation = Global_VDB_Ptr->VDB_Mat;
		TransposeMatrixCH(&GrapplingHook.Orientation);
		#endif
	}

}

static DISPLAYBLOCK* CreateGrapplingHook(void)
{
	STRATEGYBLOCK* sbPtr;

	/* create and initialise a strategy block */
	sbPtr = CreateActiveStrategyBlock();
	if(!sbPtr) return NULL; /* failure */
	InitialiseSBValues(sbPtr);

	sbPtr->I_SBtype = I_BehaviourGrapplingHook;

	AssignNewSBName(sbPtr);
			
	/* create, initialise and attach a dynamics block */
	sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_ROCKET);
	if(sbPtr->DynPtr)
	{
		DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;

      	dynPtr->PrevPosition = dynPtr->Position = Global_VDB_Ptr->VDB_World;

		GrapplingHook.Orientation = Global_VDB_Ptr->VDB_Mat;
		TransposeMatrixCH(&GrapplingHook.Orientation);
		dynPtr->OrientMat = GrapplingHook.Orientation;
		
		dynPtr->LinVelocity.vx = dynPtr->OrientMat.mat31;
		dynPtr->LinVelocity.vy = dynPtr->OrientMat.mat32;
		dynPtr->LinVelocity.vz = dynPtr->OrientMat.mat33;
	}
	else
	{
		/* dynamics block allocation failed... */
		RemoveBehaviourStrategy(sbPtr);
		return NULL;
	}

	sbPtr->shapeIndex = GetLoadedShapeMSL("spear");

	sbPtr->maintainVisibility = 0;
	sbPtr->containingModule = ModuleFromPosition(&(sbPtr->DynPtr->Position), 0);
	LOCALASSERT(sbPtr->containingModule);
	if(!(sbPtr->containingModule))
	{
		/* no containing module can be found... abort*/
		RemoveBehaviourStrategy(sbPtr);
		return NULL;
	}
	{
		MODULE tempModule;
		DISPLAYBLOCK *dPtr;
		DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
  
		VisibilityDefaultObjectMap.MapShape = sbPtr->shapeIndex;
		tempModule.m_mapptr = &VisibilityDefaultObjectMap;
		tempModule.m_sbptr = (STRATEGYBLOCK*)NULL;
		tempModule.m_numlights = 0;
		tempModule.m_lightarray = (struct lightblock *)0;
		tempModule.m_extraitemdata = (struct extraitemdata *)0;
		tempModule.m_dptr = NULL; /* this is important */
		#if SupportWIndows95
		tempModule.name = NULL; /* this is important */
		#endif
		AllocateModuleObject(&tempModule); 
		dPtr = tempModule.m_dptr;		
		if(dPtr==NULL)
		{
			RemoveBehaviourStrategy(sbPtr);
			return NULL;
		}
		
		sbPtr->SBdptr = dPtr;
		dPtr->ObStrategyBlock = sbPtr;
		dPtr->ObMyModule = NULL;					
		dPtr->ObWorld = dynPtr->Position;
		dPtr->ObEuler = dynPtr->OrientEuler;
		dPtr->ObMat = dynPtr->OrientMat;

		dPtr->ObRadius=10;
		dPtr->ObMaxX=10;
		dPtr->ObMinX=-10;
		dPtr->ObMaxY=10;
		dPtr->ObMinY=-10;
		dPtr->ObMaxZ=10;
		dPtr->ObMinZ=-10;
		/* make displayblock a dynamic module object */
		dPtr->ObFlags3 |= ObFlag3_DynamicModuleObject;

//		sbPtr->SBDamageBlock.IsOnFire=1;

		return dPtr;
	}

}

extern void GrapplingHookBehaviour(STRATEGYBLOCK *sbPtr)
{
	DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;

	if (!GrapplingHook.IsEmbedded)
	{
		COLLISIONREPORT *reportPtr = dynPtr->CollisionReportPtr;

		if(reportPtr)
		{
			char stickWhereYouAre = 0;

			if (reportPtr->ObstacleSBPtr)
			{
				DISPLAYBLOCK *dispPtr = reportPtr->ObstacleSBPtr->SBdptr;
				if (dispPtr)
				if (dispPtr->ObMyModule && (!dispPtr->ObMorphCtrl))
				{
					stickWhereYouAre=1;
				}
			}
			else
			{
				stickWhereYouAre = 1;
			}
						

			if(stickWhereYouAre)
			{
				dynPtr->IsStatic=1;
				dynPtr->PrevPosition=dynPtr->Position;
				GrapplingHook.Position=dynPtr->Position;
				GrapplingHook.IsEmbedded=1;
				/* CDF 14/4/99 Make a sound. */
				Sound_Play(SID_GRAPPLE_HIT_WALL,"d",&dynPtr->Position);
				return;
			}
			else
			{
				DisengageGrapplingHook();
				return;
			}
		}
	}
	else
	{
		GrapplingHook.Tightness -= NormalFrameTime*4;
		if (GrapplingHook.Tightness<0) GrapplingHook.Tightness=0;
	}
}

extern void DisengageGrapplingHook(void)
{
	GrapplingHook.IsEngaged = 0;
	GrapplingHook.IsEmbedded = 0;
	if (GrapplingHook.DispPtr)
	{
    	RemoveBehaviourStrategy(GrapplingHook.DispPtr->ObStrategyBlock);
		GrapplingHook.DispPtr=NULL;
	}
}

extern void HandleGrapplingHookForces(void)
{
	if (GrapplingHook.IsEmbedded)
	{
		DYNAMICSBLOCK *dynPtr = Player->ObStrategyBlock->DynPtr;
		VECTORCH direction = GrapplingHook.Position;
		int distance;

		direction.vx -= dynPtr->Position.vx;
		direction.vy -= dynPtr->Position.vy-1000;
		direction.vz -= dynPtr->Position.vz;

		distance = Approximate3dMagnitude(&direction);
		if (distance>4096+1024)
		{
			Normalise(&direction);
			dynPtr->LinImpulse.vx += MUL_FIXED(direction.vx,NormalFrameTime);
			dynPtr->LinImpulse.vy += MUL_FIXED(direction.vy,NormalFrameTime);
			dynPtr->LinImpulse.vz += MUL_FIXED(direction.vz,NormalFrameTime);
		}
		else if (distance>1024)
		{
			int s = MUL_FIXED((distance-1024)*16,NormalFrameTime);
			Normalise(&direction);
			dynPtr->LinImpulse.vx += MUL_FIXED(direction.vx,s);
			dynPtr->LinImpulse.vy += MUL_FIXED(direction.vy,s);
			dynPtr->LinImpulse.vz += MUL_FIXED(direction.vz,s);

			dynPtr->LinImpulse.vx -= MUL_FIXED(dynPtr->LinImpulse.vx,NormalFrameTime/2);
			dynPtr->LinImpulse.vy -= MUL_FIXED(dynPtr->LinImpulse.vy,NormalFrameTime/2);
			dynPtr->LinImpulse.vz -= MUL_FIXED(dynPtr->LinImpulse.vz,NormalFrameTime/2);
		}

		if (Approximate3dMagnitude(&dynPtr->LinImpulse)>ONE_FIXED)
		{
			Normalise(&dynPtr->LinImpulse);
		}
	}
}

extern void RenderGrapplingHook(void)
{
	if (GrapplingHook.IsEngaged && GrapplingHook.DispPtr)
	{
		extern void D3D_DrawCable(VECTORCH *centrePtr, MATRIXCH *orientationPtr);
		extern int CloakingPhase;
		VECTORCH cable[46];
		int i;
		{
			MATRIXCH mat = Global_VDB_Ptr->VDB_Mat;
			TransposeMatrixCH(&mat);

			cable[0].vx = Global_VDB_Ptr->VDB_World.vx-mat.mat31/128;
			cable[0].vy = Global_VDB_Ptr->VDB_World.vy-mat.mat32/128+500;
			cable[0].vz = Global_VDB_Ptr->VDB_World.vz-mat.mat33/128;
		}
//		cable[0].vx = Global_VDB_Ptr->VDB_World.vx;
//		cable[0].vy = Global_VDB_Ptr->VDB_World.vy+500;
//		cable[0].vz = Global_VDB_Ptr->VDB_World.vz;

		for (i=1; i<46; i++)
		{	
			cable[i].vx = ((45-i)*cable[0].vx + (i)*GrapplingHook.DispPtr->ObStrategyBlock->DynPtr->Position.vx)/45;
			cable[i].vy = ((45-i)*cable[0].vy + (i)*GrapplingHook.DispPtr->ObStrategyBlock->DynPtr->Position.vy)/45;
			cable[i].vz = ((45-i)*cable[0].vz + (i)*GrapplingHook.DispPtr->ObStrategyBlock->DynPtr->Position.vz)/45;

			if (GrapplingHook.Tightness!=0)
			{
				int x = GetSin((302*i+CloakingPhase)&4095)/256;
				int y = GetSin((502*i+200+CloakingPhase)&4095)/256;
				int z = GetCos((302*i+100+CloakingPhase)&4095)/256;
				int u = GetSin( ((4096*i)/45)&4095 );
				u = MUL_FIXED(MUL_FIXED(u,u),GrapplingHook.Tightness);
				cable[i].vx += MUL_FIXED(u,x);
				cable[i].vy += MUL_FIXED(u,y);
				cable[i].vz += MUL_FIXED(u,z);
			}
		}
		
		{
			MATRIXCH mat;
			VECTORCH dir = cable[45];
			dir.vx -= cable[0].vx;
			dir.vy -= cable[0].vy;
			dir.vz -= cable[0].vz;
			Normalise(&dir);
			MakeMatrixFromDirection(&dir,&mat);
			D3D_DrawCable(cable, &mat);
		}
		#if 0
		DISPLAYBLOCK displayblock;
		displayblock.ObWorld=GrapplingHook.Position;
		displayblock.ObMat=GrapplingHook.Orientation;
		displayblock.ObShape=GrapplingHook.ShapeIndex;
		displayblock.ObShapeData=GetShapeData(GrapplingHook.ShapeIndex);

		displayblock.name=NULL;
		displayblock.ObEuler.EulerX=0;
		displayblock.ObEuler.EulerY=0;
		displayblock.ObEuler.EulerZ=0;
		displayblock.ObFlags=0;
		displayblock.ObFlags2=0;
		displayblock.ObFlags3=0;
		displayblock.ObNumLights=0;
		displayblock.ObRadius=0;
		displayblock.ObMaxX=0;
		displayblock.ObMinX=0;
		displayblock.ObMaxY=0;
		displayblock.ObMinY=0;
		displayblock.ObMaxZ=0;
		displayblock.ObMinZ=0;
		displayblock.ObTxAnimCtrlBlks=NULL;
		displayblock.ObEIDPtr=NULL;
		displayblock.ObMorphCtrl=NULL;
		displayblock.ObStrategyBlock=NULL;
		displayblock.ShapeAnimControlBlock=NULL;
		displayblock.HModelControlBlock=NULL;
		displayblock.ObMyModule=NULL;		
		displayblock.SpecialFXFlags = 0;
		displayblock.SfxPtr=0;

		MakeVector(&displayblock.ObWorld, &Global_VDB_Ptr->VDB_World, &displayblock.ObView);
		RotateVector(&displayblock.ObView, &Global_VDB_Ptr->VDB_Mat);
		RenderThisDisplayblock(&displayblock);
		{
			PARTICLE particle;
			particle.Colour = 0xffffffff;
			particle.ParticleID = PARTICLE_LASERBEAM;
			particle.Position = Player->ObStrategyBlock->DynPtr->Position;
			particle.Position.vy-=1000;
			particle.Offset = GrapplingHook.DispPtr->ObStrategyBlock->DynPtr->Position;
			particle.Size = 20;
			RenderParticle(&particle);
		}
		#endif

	}
}


/*--------------------**
** Loading and Saving **
**--------------------*/
#include "savegame.h"

typedef struct grapple_save_block
{
	SAVE_BLOCK_STRATEGY_HEADER header;

	int IsEmbedded;
	int IsEngaged;
	int Tightness;

	VECTORCH Position;
	MATRIXCH Orientation;
//strategy block stuff
	DYNAMICSBLOCK dynamics;
}GRAPPLE_SAVE_BLOCK;

//defines for load/save macros
#define SAVELOAD_BLOCK block
#define SAVELOAD_BEHAV (&GrapplingHook)

void LoadStrategy_Grapple(SAVE_BLOCK_STRATEGY_HEADER* header)
{
	GRAPPLE_SAVE_BLOCK* block = (GRAPPLE_SAVE_BLOCK*) header;

	//check the size of the save block
	if(header->size!=sizeof(*block)) return;

	//create the grappling hook
	GrapplingHook.DispPtr = CreateGrapplingHook();
	if(!GrapplingHook.DispPtr) return;

	//copy suff from the save block
	COPYELEMENT_LOAD(IsEmbedded)
	COPYELEMENT_LOAD(IsEngaged)
	COPYELEMENT_LOAD(Tightness)
	COPYELEMENT_LOAD(Position)
	COPYELEMENT_LOAD(Orientation)
	
	*GrapplingHook.DispPtr->ObStrategyBlock->DynPtr = block->dynamics;
}

void SaveStrategy_Grapple(STRATEGYBLOCK* sbPtr)
{
	GRAPPLE_SAVE_BLOCK* block;
	
	GET_STRATEGY_SAVE_BLOCK(block,sbPtr);

	//copy stuff to the save block
	COPYELEMENT_SAVE(IsEmbedded)
	COPYELEMENT_SAVE(IsEngaged)
	COPYELEMENT_SAVE(Tightness)
	COPYELEMENT_SAVE(Position)
	COPYELEMENT_SAVE(Orientation)
	
	block->dynamics = *sbPtr->DynPtr;
	block->dynamics.CollisionReportPtr=0;
}