///////////////////////////////////////////////////////////////////////////////
//	target.cpp
//
///////////////////////////////////////////////////////////////////////////////

#include	"world.h"

///////////////////////////////////////////////////////////////////////////////
//	exports
///////////////////////////////////////////////////////////////////////////////

DllExport	void target_laser (userEntity_t *self);
DllExport	void target_earthquake (userEntity_t *self);
DllExport	void target_spotlight (userEntity_t *self);

///////////////////////////////////////////////////////////////////////////////
//	typedefs
///////////////////////////////////////////////////////////////////////////////

typedef	struct	laserHook_s
{
	int			damage;
} laserHook_t;

typedef	struct	quakeHook_s
{
	float		last_move_time;
	float		radius;
	float		duration;
	float		end_time;
	int			severity;
} quakeHook_t;

/*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT
When triggered, fires a laser.  You can either set a target
or a direction.
*/

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_laser_think (userEntity_t *self)
{
	laserHook_t		*hook = (laserHook_t *) self->userHook;
	userEntity_t	*ignore;
	trace_t			tr;
	Vector			start, end;
	Vector			point, dir;
	Vector			last_movedir;
	int				count;
	static			vec3_t	lmins = {-4, -4, -4};
	static			vec3_t	lmaxs = {4, 4, 4};

	if (self->spawnflags & 0x80000000)
		count = 8;
	else
		count = 4;

	if (self->enemy)
	{
		last_movedir = Vector (self->movedir);

		point = Vector (self->enemy->absmin) + Vector (self->enemy->size) * 0.5;
		dir = point - Vector (self->s.origin);
		dir.Normalize ();
		dir.set (self->movedir);

		if (dir != last_movedir)
		{
			com->VecToAngles (end, dir);
			end.set (self->s.angles);
			self->spawnflags |= 0x80000000;
		}
	}
	else
		dir = Vector (self->movedir);

	ignore = self;

	start = Vector (self->s.origin);
	end = start + dir * 2048;

	while(1)
	{
		tr = gstate->trace_q2 (start.vec (), NULL, NULL, end.vec (), ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);

		if (!tr.ent)
			break;

		// hurt it if we can
		if ((tr.ent->takedamage) && (tr.ent != self->owner))
			com->Damage (tr.ent, self, self->owner, tr.endpos, dir.vec (), hook->damage, DAMAGE_INERTIAL);

		//	if we hit something that's not a monster or player or is immune to lasers, we're done
		//	Your mom is done
		if (!(tr.ent->flags & (FL_CLIENT + FL_BOT + FL_MONSTER)))
		{
			if (self->spawnflags & 0x80000000)
			{
				self->spawnflags &= ~0x80000000;
				gstate->WriteByte (SVC_TEMP_ENTITY);
				gstate->WriteByte (TE_LASER_SPARKS);
				gstate->WriteByte (count);
				gstate->WritePosition (tr.endpos);
				gstate->WriteDir (tr.plane.normal);
				gstate->WriteByte (self->s.skinnum);
				gstate->MultiCast (tr.endpos, MULTICAST_PVS);
			}
			break;
		}

		ignore = tr.ent;
		start = Vector (tr.endpos);
	}

	com->CopyVector (self->s.old_origin, tr.endpos);

	self->nextthink = gstate->time + 0.05;
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_laser_on (userEntity_t *self)
{
	Vector	dir, ang;

	self->spawnflags |= 0x80000001;
	self->svflags &= ~SVF_NOCLIENT;

	if (!self->enemy)
	{
		//	set up angles here for renderer to use when drawing
		//	this way it doesn't have to be done every think
		dir = Vector (self->movedir);
		com->VecToAngles (ang, dir);
		ang.set (self->s.angles);
	}

	target_laser_think (self);
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_laser_off (userEntity_t *self)
{
	self->spawnflags &= ~1;
	self->svflags |= SVF_NOCLIENT;
	self->nextthink = 0;
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_laser_use (userEntity_t *self, userEntity_t *other, userEntity_t *activator)
{
	if (self->spawnflags & 1)
		target_laser_off (self);
	else
		target_laser_on (self);
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_laser_start (userEntity_t *self)
{
	userEntity_t	*ent;
	Vector			ang;
	laserHook_t		*hook = (laserHook_t *) self->userHook;

	self->movetype = MOVETYPE_NONE;
	self->solid = SOLID_NOT;
	self->s.renderfx |= RF_BEAM | RF_TRANSLUCENT;
	self->s.modelindex = 1;			// must be non-zero

	// set the beam diameter
	if (self->spawnflags & 64)
		self->s.frame = 16;
	else
		self->s.frame = 4;

	// set the color
	if (self->spawnflags & 2)
		self->s.skinnum = GC_RED;
	else if (self->spawnflags & 4)
		self->s.skinnum = GC_GREEN;
	else if (self->spawnflags & 8)
		self->s.skinnum = GC_BLUE;
	else if (self->spawnflags & 16)
		self->s.skinnum = GC_YELLOW;
	else if (self->spawnflags & 32)
		self->s.skinnum = GC_LTRED;

	if (!self->owner)
		self->owner = self;

	if (!self->enemy)
	{
		if (self->target)
		{
			ent = com->FindTarget (self->target);
			self->enemy = ent;
		}
		else
			com->SetMovedir (self);
	}
	self->use = target_laser_use;
	self->think = target_laser_think;

	if (!hook->damage)
		hook->damage = 1;

	VectorSet (self->s.mins, -8, -8, -8);
	VectorSet (self->s.maxs, 8, 8, 8);
	gstate->LinkEntity (self);

	if (self->spawnflags & 1)
		target_laser_on (self);
	else
		target_laser_off (self);
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_laser (userEntity_t *self)
{
	int			i;
	laserHook_t	*hook;

	self->userHook = com->malloc_hook (sizeof (laserHook_t), MEM_MALLOC);
	hook = (laserHook_t *) self->userHook;

	for (i = 0; self->epair [i].key; i++)
	{
		if (!stricmp (self->epair [i].key, "dmg") || !stricmp (self->epair [i].key, "damage"))
			hook->damage = atoi (self->epair [i].value);
	}

	// let everything else get spawned before we start firing
	self->think = target_laser_start;
	self->nextthink = gstate->time + 0.1;
}

/* ------------------------- target_earthquake ----------------------------- */

///////////////////////////////////////////////////////////////////////////////
//	target_earthquake_think
//
///////////////////////////////////////////////////////////////////////////////

void target_earthquake_think (userEntity_t *self)
{
	int				i;
	userEntity_t	*head;
	quakeHook_t		*hook = (quakeHook_t *) self->userHook;

	if (hook->last_move_time < gstate->time)
	{
		//	gstate->gi->positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
		hook->last_move_time = gstate->time + 0.5;
	}


	for (head = gstate->FirstEntity (); head; head = gstate->NextEntity (head))
	{
		if (!head->inuse)
			continue;

		if (head->groundEntity)
		{
			head->groundEntity = NULL;
			head->velocity [0] += (rnd () - 0.5) * 200;//hook->severity;
			head->velocity [1] += (rnd () - 0.5) * 200;//hook->severity;
			//	head->velocity [2] = /*hook->severity*/ 200 * (1.0 / head->mass);
		}

		if (head->flags & FL_CLIENT && head->client)
		{
			head->client->kick_angles [0] += ((rnd () - 0.5) * (hook->severity / 50));
			head->client->kick_angles [1] += ((rnd () - 0.5) * (hook->severity / 100));
			head->client->kick_angles [2] += ((rnd () - 0.5) * (hook->severity / 100));
		}
	}

	if (gstate->time < hook->end_time)
		self->nextthink = gstate->time + 0.05;
}

///////////////////////////////////////////////////////////////////////////////
//	target_earthquake_use
//
///////////////////////////////////////////////////////////////////////////////

void target_earthquake_use (userEntity_t *self, userEntity_t *other, userEntity_t *activator)
{
	quakeHook_t		*hook = (quakeHook_t *) self->userHook;

	hook->end_time = gstate->time + hook->duration;
	self->nextthink = gstate->time + 0.05;
//	self->activator = activator;
	hook->last_move_time = 0;
}

///////////////////////////////////////////////////////////////////////////////
//	target_earthquake
//
///////////////////////////////////////////////////////////////////////////////

void target_earthquake (userEntity_t *self)
{
	int			i;
	quakeHook_t	*hook;

	self->userHook = com->malloc_hook (sizeof (quakeHook_t), MEM_MALLOC);
	hook = (quakeHook_t *) self->userHook;
	
	for (i = 0; self->epair [i].key; i++)
	{
		if (!stricmp (self->epair [i].key, "severity") || !stricmp (self->epair [i].key, "speed"))
			hook->severity = atoi (self->epair [i].value);
		else if (!stricmp (self->epair [i].key, "duration") || !stricmp (self->epair [i].key, "count"))
			hook->duration = atof (self->epair [i].value);
	}

	if (!self->targetname)
		gstate->Con_Dprintf ("untargeted %s at %s\n", self->className, com->vtos (self->s.origin));

	if (!hook->duration)
		hook->duration = 5;

	if (!hook->severity)
		hook->severity = 200;

	self->svflags |= SVF_NOCLIENT;
	self->think = target_earthquake_think;
	self->use = target_earthquake_use;

	//self->noise_index = gstate->SoundIndex ("world/quake.wav");
}

/* ------------------------- target_earthquake ----------------------------- */

/*QUAKED target_spotlight (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT
*/

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_spotlight_think (userEntity_t *self)
{
//	laserHook_t		*hook = (laserHook_t *) self->userHook;
	userEntity_t	*ignore;
	trace_t			tr;
	Vector			start, end;
	Vector			point, dir;
	Vector			last_movedir;
	static			vec3_t	lmins = {-4, -4, -4};
	static			vec3_t	lmaxs = {4, 4, 4};

	if (self->enemy)
	{
		last_movedir = Vector (self->movedir);

		point = Vector (self->enemy->absmin) + Vector (self->enemy->size) * 0.5;
		dir = point - Vector (self->s.origin);
		dir.Normalize ();
		dir.set (self->movedir);

//		if (dir != last_movedir)
//			self->spawnflags |= 0x80000000;
	}
	else
		dir = Vector (self->movedir);

	start = Vector (self->s.origin);
	end = start + dir * 2048;

	ignore = self->enemy;

	while(1)
	{
		tr = gstate->trace_q2 (start.vec (), NULL, NULL, end.vec (), ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);

		if (!tr.ent)
			break;

		//	if we hit something that's not a monster or player or is immune to lasers, we're done
		//	Your mom is done
		if (!(tr.ent->flags & (FL_CLIENT + FL_BOT + FL_MONSTER)))
			break;

		ignore = tr.ent;
		start = Vector (tr.endpos);
	}

	com->CopyVector (self->s.old_origin, tr.endpos);

	self->nextthink = gstate->time + 0.05;
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_spotlight_on (userEntity_t *self)
{
	self->spawnflags |= 0x80000001;
	self->svflags &= ~SVF_NOCLIENT;
	target_spotlight_think (self);
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_spotlight_off (userEntity_t *self)
{
	self->spawnflags &= ~1;
	self->svflags |= SVF_NOCLIENT;
	self->nextthink = 0;
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_spotlight_use (userEntity_t *self, userEntity_t *other, userEntity_t *activator)
{
	if (self->spawnflags & 1)
		target_spotlight_off (self);
	else
		target_spotlight_on (self);
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_spotlight_start (userEntity_t *self)
{
	userEntity_t	*ent;
	Vector			ang;
	int				i, radius;

	self->movetype = MOVETYPE_NONE;
	self->solid = SOLID_NOT;
	self->s.renderfx |= RF_SPOTLIGHT|RF_TRANSLUCENT;
	self->s.modelindex = 1;			// must be non-zero

	for (i = 0; self->epair [i].key; i++)
	{
		if (!stricmp (self->epair[i].key, "radius"))
			 radius = atoi (self->epair[i].value);
	}
	if (radius)
		self->s.frame = radius;
	else
		self->s.frame = 4;

	// set the beam diameter

	// set the color
	if (self->spawnflags & 2)
		self->s.skinnum = 0xf2f2f0f0;
	else if (self->spawnflags & 4)
		self->s.skinnum = 0xd0d1d2d3;
	else if (self->spawnflags & 8)
		self->s.skinnum = 0xf3f3f1f1;
	else if (self->spawnflags & 16)
		self->s.skinnum = 0xdcdddedf;
	else if (self->spawnflags & 32)
		self->s.skinnum = 0xe0e1e2e3;

	if (!self->owner)
		self->owner = self;

	if (!self->enemy)
	{
		if (self->target)
		{
			ent = com->FindTarget (self->target);
			self->enemy = ent;
		}
		else
		{
			com->SetMovedir (self);
		}
	}
	self->use = target_spotlight_use;
	self->think = target_spotlight_think;

	VectorSet (self->s.mins, -8, -8, -8);
	VectorSet (self->s.maxs, 8, 8, 8);
	gstate->LinkEntity (self);

	if (self->spawnflags & 1)
		target_spotlight_on (self);
	else
		target_spotlight_off (self);
}

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void target_spotlight (userEntity_t *self)
{
	int			i;

	// let everything else get spawned before we start firing
	self->think = target_spotlight_start;
	self->nextthink = gstate->time + 0.1;
}

