/*
   ================================================================
   HOVERBIKE WEAPONS
   ================================================================

   Copyright (C) 1998 by 2015, Inc.
   All rights reserved.

   This source is may not be distributed and/or modified without
   expressly written permission by 2015, Inc.

   serves as both the view model and the world model
   for when a bike has a rider. Also does all the weapon
   related stuff for hoverbikes.
 */

#include "hoverweap.h"
#include "player.h"
#include "explosion.h"
#include "surface.h"
#include "jitter.h"
#include "misc.h"

CLASS_DECLARATION( Weapon, HoverWeap, "weapon_hoverweap" );

ResponseDef HoverWeap::Responses[] =
{
	{&EV_Weapon_Shoot, (Response)HoverWeap::Shoot},
	{NULL, NULL}
};

HoverWeap::HoverWeap()
{
	SetModels( "bike_prototype.def", "view_hoverbike.def" );
	modelIndex( "stinger.def" );
	modelIndex( "hovermine.def" );
	modelIndex( "beam_bike.def" );
	SetAmmo( NULL, 0, 0 );

	notdroppable = true;
	side = 1;

	edict->s.effects |= EF_WARM;
}

void HoverWeap::AttachGun( void )
{
	int groupindex;
	int tri_num;
	Vector orient;

	if(!owner)
	{
		return;
	}

	if (attached)
		DetachGun();

	// uses the built in fake bone that works like Quake2's modelindexes did.
	gi.GetBoneInfo( owner->edict->s.modelindex, "origin", &groupindex, &tri_num, orient.vec3());
	attached = true;
	attach( owner->entnum, groupindex, tri_num, orient );
	showModel();

	edict->s.gunmodelindex  = modelIndex( worldmodel.c_str());
	edict->s.gunanim        = 0;
	edict->s.gunframe       = 0;

	// set the bike and rider values for the hoverbike
	if(owner->isSubclassOf( Player ))
	{
		Player *player;

		player = (Player *)owner.Ptr();
		bike = player->GetHoverbike();
	}
	else
		gi.dprintf( "ent number %i is not a player\n", owner->entnum );
}

void HoverWeap::DetachGun( void )
{
	if(attached)
	{
		attached = false;
		detach();
		hideModel();
		edict->s.gunmodelindex  = 0;
		edict->s.gunanim        = 0;
		edict->s.gunframe       = 0;
	}
}

void HoverWeap::Fire( void )
{
	int noammo;

	if(!ReadyToFire())
	{
		return;
	}

	// if bike isn't set yet, set it
	if(!bike)
	{
		if(owner->isSubclassOf( Player ))
		{
			Player *player;

			player = (Player *)owner.Ptr();
			bike = player->GetHoverbike();
		}

		// still not set
		if(!bike)
			return;
	}

	// use appropriate type of ammo when appropriate
	noammo = 0;
	if(owner->isClient() && !(owner->flags & FL_GODMODE) && !(DM_FLAG( DF_INFINITE_AMMO )))
	{
		switch(bike->weaponmode)
		{
		case HWMODE_ROCKETS:
			if(bike->rockets < 2)
				noammo = 1;
			else
				bike->rockets -= 2;
			break;
		case HWMODE_CHAINGUN:
			if(bike->bullets < 1)
				noammo = 1;
			else
				bike->bullets -= 1;
			break;
		case HWMODE_MINES:
			if(bike->mines < 1)
				noammo = 1;
			else
				bike->mines -= 1;
			break;
		}
	}

	// don't have enough ammo to fire current weapon
	if(noammo)
	{
		// auto weapon switch
		switch(bike->weaponmode)
		{
		case HWMODE_ROCKETS:
		{
			if(bike->bullets > 0)
			{
				bike->SelectWeapon( HWMODE_CHAINGUN );
			}
			else if(bike->mines > 0)
			{
				bike->SelectWeapon( HWMODE_MINES );
			}
			break;
		}
		case HWMODE_CHAINGUN:
		{
			if(bike->rockets > 0)
			{
				bike->SelectWeapon( HWMODE_ROCKETS );
			}
			else if(bike->mines > 0)
			{
				bike->SelectWeapon( HWMODE_MINES );
			}
			break;
		}
		case HWMODE_MINES:
		{
			if(bike->rockets > 0)
			{
				bike->SelectWeapon( HWMODE_ROCKETS );
			}
			else if(bike->bullets > 0)
			{
				bike->SelectWeapon( HWMODE_CHAINGUN );
			}
			break;
		}
		}

		bike->WeaponNoAmmoSound();
		NextAttack( 1 );
		return;
	}

	weaponstate = WEAPON_FIRING;

	CancelEventsOfType( EV_Weapon_DoneFiring );
	// this is just a precaution that we can re-trigger
	NextAttack( 5 );
	// activate the corrent firing animation for the view
	if(bike->weaponmode == HWMODE_ROCKETS)
	{
		RandomAnimate( "rocket", EV_Weapon_DoneFiring );
	}
	else if(bike->weaponmode == HWMODE_CHAINGUN)
	{
		// fire a laser beam out from the correct side
		if(side > 0)
			RandomAnimate( "rightlaser", EV_Weapon_DoneFiring );
		else
			RandomAnimate( "leftlaser", EV_Weapon_DoneFiring );
	}
	else if(bike->weaponmode == HWMODE_MINES)
	{
		RandomAnimate( "mine", EV_Weapon_DoneFiring );
	}
	last_attack_time = level.time;
}

void HoverWeap::Shoot( Event *ev )
{
	Vector pos, dir, forward, right, up;
	Vector tmpvec;

	if ( !owner )
	{
		return;
	}
	if(!bike)
	{
		return;
	}

	if(bike->player)
	{
		Player *pent;

		pent = (Player *)bike->rider.Ptr();

		// need to scale the player's view pitch up to
		// compensate for the weapons firing from
		// so far forward.
		tmpvec[PITCH] = pent->v_angle[PITCH]*1.05;
	}

	if(tmpvec[PITCH] > 45)
		tmpvec[PITCH] = 45;
	else if(tmpvec[PITCH] < -45)
		tmpvec[PITCH] = -45;

	tmpvec[YAW] = bike->move_angles[YAW];
	tmpvec[ROLL] = 0;
	tmpvec.AngleVectors( &dir, NULL, NULL );

	// trans dir to bike's grav axis
	if(bike->gravaxis)
	{
		Vector tmpvec2;

		tmpvec2[gravity_axis[bike->gravaxis].x] = dir.x;
		tmpvec2[gravity_axis[bike->gravaxis].y] = dir.y*gravity_axis[bike->gravaxis].sign;
		tmpvec2[gravity_axis[bike->gravaxis].z] = dir.z*gravity_axis[bike->gravaxis].sign;
		dir = tmpvec2;
	}

	tmpvec[PITCH] = 0;
	tmpvec.AngleVectors( &forward, &right, &up );

	// trans dir to bike's grav axis
	if(bike->gravaxis)
	{
		Vector tmpvec2;

		tmpvec2[gravity_axis[bike->gravaxis].x] = forward.x;
		tmpvec2[gravity_axis[bike->gravaxis].y] = forward.y*gravity_axis[bike->gravaxis].sign;
		tmpvec2[gravity_axis[bike->gravaxis].z] = forward.z*gravity_axis[bike->gravaxis].sign;
		forward = tmpvec2;

		tmpvec2[gravity_axis[bike->gravaxis].x] = right.x;
		tmpvec2[gravity_axis[bike->gravaxis].y] = right.y*gravity_axis[bike->gravaxis].sign;
		tmpvec2[gravity_axis[bike->gravaxis].z] = right.z*gravity_axis[bike->gravaxis].sign;
		right = tmpvec2;

		tmpvec2[gravity_axis[bike->gravaxis].x] = up.x;
		tmpvec2[gravity_axis[bike->gravaxis].y] = up.y*gravity_axis[bike->gravaxis].sign;
		tmpvec2[gravity_axis[bike->gravaxis].z] = up.z*gravity_axis[bike->gravaxis].sign;
		up = tmpvec2;
	}


	// fire the currently selected weapon mode
	if(bike->weaponmode == HWMODE_ROCKETS)
	{
		HBRocket *rocket;

		pos = bike->worldorigin + forward*40 + right*18*side + up*8;

		rocket = new HBRocket;
		rocket->Setup( owner, pos, dir );

		pos = bike->worldorigin + forward*40 - right*18*side + up*8;

		rocket = new HBRocket;
		rocket->Setup( owner, pos, dir );

		NextAttack( 0.8 );
	}
	else if(bike->weaponmode == HWMODE_CHAINGUN)
	{
		pos = bike->worldorigin + forward*40 + right*10*side + up*8;
		FireBullets( pos, dir, 1, "100 100 100", 6, 8, DAMAGE_BULLET, MOD_HB_GUN, true );

		// also do a bullet trace in the center to make it easier to hit things
		pos = bike->worldorigin + forward*40 + up*8;
		FireBullets( pos, dir, 4, "50 50 50", 6, 8, DAMAGE_BULLET, MOD_HB_GUN, false );

		// Set the pitch so the client can use it to fire tracers in the right direction
		angles = dir.toAngles();
		setAngles( angles );

		NextAttack( 0.1 );
	}
	else if(bike->weaponmode == HWMODE_MINES)
	{
		HBMine *mine;

		pos = bike->worldorigin - forward*30 + up*16;
		dir = forward*(-1);

		mine = new HBMine;
		mine->Setup( owner, pos, dir );

		NextAttack( 1.2 );
	}

	// alternate firing side
	side *= -1;
}

// never drop a hoverbike weapon
qboolean HoverWeap::Drop( void )
{
	return false;
}

//==============================================================
// rocket weapon stuff

#define ROCKET_SPEED    1800
#define ROCKET_RADIUS   150

CLASS_DECLARATION( Projectile, HBRocket, NULL );

Event EV_HBRocket_Explode( "explode" );

ResponseDef HBRocket::Responses[] =
{
	{&EV_Touch, (Response)HBRocket::Explode},
	{&EV_HBRocket_Explode, (Response)HBRocket::Explode},
	{NULL, NULL}
};

EXPORT_FROM_DLL void HBRocket::Explode( Event *ev )
{
	int damg;
	Vector v;
	Entity      *other;
	Vector norm;
	RadiusJitter *jitter;
	Entity      *owner;

	other = ev->GetEntity( 1 );
	assert( other );

	if(other->isSubclassOf( Teleporter ))
	{
		return;
	}

	if(other->entnum == this->owner ||
	   other->entnum == bike ||
	   other->entnum == frontbox ||
	   other->entnum == backbox)
	{
		return;
	}

	stopsound( CHAN_VOICE );

	setSolidType( SOLID_NOT );
	hideModel();
	if ( HitSky() )
	{
		PostEvent( EV_Remove, 0 );
		return;
	}

	owner = G_GetEntity( this->owner );

	damg = 60 + ( int )G_Random( 20 );

	if (other->takedamage)
		other->Damage( this, owner, damg, origin, velocity, level.impact_trace.plane.normal, 32, 0, MOD_HB_ROCKET, -1, -1, 1.0f );

	SpawnBlastDamage( &level.impact_trace, damg, owner );

	v = velocity;
	v.normalize();
	v = origin - v * 36;

	// don't do radius damage to the other, because all the damage
	// was done in the impact
	CreateExplosion( v, damg, 0.75, true, this, owner, this, MOD_HB_ROCKETSPLASH, 0.35 );

	jitter = new RadiusJitter;
	jitter->SetupLarge( v );

	PostEvent( EV_Remove, 0.1 );
}

EXPORT_FROM_DLL void HBRocket::Setup( Entity *owner, Vector pos, Vector dir )
{
	Event *ev;
	Player *rider;
	Hoverbike *bike;

	this->owner = owner->entnum;
	edict->owner = owner->edict;
	rider = (Player *)owner;
	bike = rider->GetHoverbike();
	this->bike = bike->entnum;
	frontbox = bike->frontbox->entnum;
	backbox = bike->backbox->entnum;

	setMoveType( MOVETYPE_FLYMISSILE );
	setSolidType( SOLID_BBOX );
	edict->clipmask = MASK_PROJECTILE;

	angles = dir.toAngles();
	angles[ PITCH ] = -angles[ PITCH ];
	setAngles( angles );

	speed = ROCKET_SPEED;
	velocity = dir * ROCKET_SPEED;

	// set missile duration
	ev = new Event( EV_Remove );
	ev->AddEntity( world );
	PostEvent( ev, 20 );

	setModel( "stinger.def" );
	edict->s.effects  |= EF_ANIMEROCKET;
	edict->s.effects  |= EF_EVERYFRAME;
	edict->s.angles[ROLL] = rand() % 360;
	avelocity = "0 0 90";
	gravity = 0;

	// setup ambient thrust
	ev = new Event( EV_RandomEntitySound );
	ev->AddString( "thrust" );
	ProcessEvent( ev );

	setSize( "-1 -1 -1", "1 1 1" );
	setOrigin( pos );
	worldorigin.copyTo( edict->s.old_origin );
}

//==============================================================
// bullet weapon stuff

#define MAX_RICOCHETS 4

void HoverWeap::TraceAttack( Vector start,Vector end,int damage,trace_t *trace,int numricochets,int kick,int dflags,int meansofdeath,qboolean server_effects )
{
	Vector org;
	Vector dir;
	Vector endpos;
	int surfflags;
	int surftype;
	int timeofs;
	Entity      *ent;
	qboolean ricochet;

	if ( HitSky( trace ) )
	{
		return;
	}

	ricochet = false;
	dir = end - start;
	dir.normalize();

	org = end - dir;

	ent = trace->ent->entity;

	if ( !trace->surface )
	{
		surfflags = 0;
		surftype = 0;
	}
	else
	{
		surfflags = trace->surface->flags;
		surftype = SURFACETYPE_FROM_FLAGS( surfflags );
		surfaceManager.DamageSurface( trace, damage, owner );

		if ( surfflags & SURF_RICOCHET )
			ricochet = true;
	}
	if ( trace->intersect.valid && ent )
	{
		//
		// see if the parent group has ricochet turned on
		//
		if ( trace->intersect.parentgroup >= 0 )
		{
			int flags;

			flags = gi.Group_Flags( ent->edict->s.modelindex, trace->intersect.parentgroup );
			if ( flags & MDL_GROUP_RICOCHET )
			{
				surftype = ( flags >> 8 ) & 0xf;
				ricochet = true;
			}
		}
	}

	if ( ent )
	{
		if ( !(ent->flags & FL_SHIELDS) )
		{
			if ( ent->flags & FL_SPARKS )
			{
				// Take care of ricochet effects on the server
				if ( server_effects && !ricochet )
				{
					timeofs = MAX_RICOCHETS - numricochets;
					if ( timeofs > 0xf )
					{
						timeofs = 0xf;
					}

					gi.WriteByte( svc_temp_entity );
					gi.WriteByte( TE_SCALED_EXPLOSION );
					gi.WritePosition( org.vec3() );
					gi.WriteByte( 16 );
					gi.multicast( org.vec3(), MULTICAST_PVS );
				}
				MadeBreakingSound( org, owner );
			}

			if ( ent->takedamage )
			{
				if ( trace->intersect.valid )
				{
					// We hit a valid group so send in location based damage
					ent->Damage( this,
					             owner,
					             damage,
					             trace->endpos,
					             dir,
					             trace->plane.normal,
					             kick,
					             dflags,
					             meansofdeath,
					             trace->intersect.parentgroup,
					             -1,
					             trace->intersect.damage_multiplier );
				}
				else
				{
					// We didn't hit any groups, so send in generic damage
					ent->Damage( this,
					             owner,
					             damage,
					             trace->endpos,
					             dir,
					             trace->plane.normal,
					             kick,
					             dflags,
					             meansofdeath,
					             -1,
					             -1,
					             1 );
				}
			}
		}
		else
		{
			surftype = SURF_TYPE_METAL;
			ricochet = true;
		}
	}

	if ( ricochet && ( server_effects || ( numricochets < MAX_RICOCHETS ) ) )
	{
		timeofs = MAX_RICOCHETS - numricochets;
		if ( timeofs > 0xf )
		{
			timeofs = 0xf;
		}
		gi.WriteByte( svc_temp_entity );
		gi.WriteByte( TE_GUNSHOT );
		gi.WritePosition( org.vec3() );
		gi.WriteDir( trace->plane.normal );
		gi.WriteByte( timeofs );
		gi.multicast( org.vec3(), MULTICAST_PVS );
	}

	if (
		ricochet &&
		numricochets &&
		damage
		)
	{
		dir += Vector( trace->plane.normal ) * 2;
		endpos = org + dir * 8192;

		//
		// since this is a ricochet, we don't ignore the weapon owner this time.
		//
		if(DM_FLAG( DF_BBOX_BULLETS ))
		{
			*trace = G_Trace( org, vec_zero, vec_zero, endpos, NULL, MASK_SHOT, "BulletWeapon::TraceAttack" );
		}
		else
		{
			*trace = G_FullTrace( org, vec_zero, vec_zero, endpos, 5, NULL, MASK_SHOT, "BulletWeapon::TraceAttack" );
		}

		if ( trace->fraction != 1.0 )
		{
			endpos = trace->endpos;
			TraceAttack( org, endpos, damage * 0.8f, trace, numricochets - 1, kick, dflags, meansofdeath, true );
		}
	}
}

void HoverWeap::FireBullets( Vector src,Vector dir,int numbullets,Vector spread,int mindamage,int maxdamage,int dflags,int meansofdeath,qboolean server_effects )
{
	Vector end;
	trace_t trace;
	Vector right;
	Vector up;
	int i;

	assert( owner );
	if ( !owner )
	{
		return;
	}

	owner->angles.AngleVectors( NULL, &right, &up );

	angles = dir.toAngles();
	setAngles( angles );

	for( i = 0; i < numbullets; i++ )
	{
		end = src +
		      dir       * 8192 +
		      right     * G_CRandom() * spread.x +
		      up        * G_CRandom() * spread.y;

// first need to do a regular trace to check for hitting a hoverbike
		trace = G_Trace( src, vec_zero, vec_zero, end, bike->frontbox, MASK_SHOT, "BulletWeapon::FireBullets" );
		if(trace.fraction != 1)
		{
			Entity *hit;

			hit = trace.ent->entity;

			if(hit->isSubclassOf( Hoverbike ) || hit->isSubclassOf( HoverbikeBox ))
			{
				trace_t trace2;

				// also do a short full trace to see if we're hitting a player on a hoverbike
				end = trace.endpos + dir * 64;
				trace2 = G_FullTrace( Vector( trace.endpos ), vec_zero, vec_zero, end, 5, bike->frontbox, MASK_SHOT, "BulletWeapon::FireBullets" );
				if(trace2.fraction != 1)
				{
					Entity *hit2;

					hit2 = trace2.ent->entity;
					if(hit2->takedamage && hit2->isClient())
					{
						// probably traced to the rider, so hit him instead
						hit2->Damage( this, owner, mindamage + (int)G_Random( maxdamage - mindamage + 1 ),
						              trace.endpos, dir, trace.plane.normal, kick, dflags, meansofdeath,
						              trace.intersect.parentgroup, -1, trace.intersect.damage_multiplier );
						return;
					}
				}

				hit->Damage( this, owner, mindamage + (int)G_Random( maxdamage - mindamage + 1 ), trace.endpos, dir, trace.plane.normal, kick, dflags, meansofdeath, -1, -1, 1 );

				return; // hit something already, so don't do a regular full trace
			}
		}

		if(!damagedtarget && DM_FLAG( DF_BBOX_BULLETS ))
		{
			trace = G_Trace( src, vec_zero, vec_zero, end, owner, MASK_SHOT, "BulletWeapon::FireBullets" );

			if(trace.fraction != 1.0)
			{
				// do less than regular damage on a bbox hit
				TraceAttack( src, trace.endpos, (mindamage + (int)G_Random( maxdamage - mindamage + 1 ))*0.85, &trace, MAX_RICOCHETS, kick, dflags, meansofdeath, server_effects );
			}
		}
		else
		{
			trace = G_FullTrace( src, vec_zero, vec_zero, end, 5, bike->frontbox, MASK_SHOT, "BulletWeapon::FireBullets" );
#if 0
			Com_Printf( "Server OWNER  Angles:%0.2f %0.2f %0.2f\n",owner->angles[0],owner->angles[1],owner->angles[2] );
			Com_Printf( "Server Bullet Angles:%0.2f %0.2f %0.2f\n",angles[0],angles[1],angles[2] );
			Com_Printf( "Right               :%0.2f %0.2f %0.2f\n",right[0],right[1],right[2] );
			Com_Printf( "Up                  :%0.2f %0.2f %0.2f\n",up[0],up[1],up[2] );
			Com_Printf( "Direction           :%0.2f %0.2f %0.2f\n",dir[0],dir[1],dir[2] );
			Com_Printf( "Endpoint            :%0.2f %0.2f %0.2f\n",end[0],end[1],end[2] );
			Com_Printf( "Server Trace Start  :%0.2f %0.2f %0.2f\n",src[0],src[1],src[2] );
			Com_Printf( "Server Trace End    :%0.2f %0.2f %0.2f\n",trace.endpos[0],trace.endpos[1],trace.endpos[2] );
			Com_Printf( "\n" );
#endif
			damagedtarget = false;
			if ( trace.fraction != 1.0 )
			{
				TraceAttack( src, trace.endpos, mindamage + (int)G_Random( maxdamage - mindamage + 1 ), &trace, MAX_RICOCHETS, kick, dflags, meansofdeath, server_effects );
			}
		}
	}
}

//==============================================================
// mine weapon stuff

#define MINE_SPEED 300
#define MINE_RADIUS 160
#define MINE_TIME 5
#define MINE_PUSH 700
#define MINE_DAMAGE 100

CLASS_DECLARATION( Projectile, HBMine, NULL );

Event EV_HBMine_Explode( "explode" );
Event EV_HBMine_Detect( "proximity_detect" );

ResponseDef HBMine::Responses[] =
{
	{&EV_HBMine_Explode, (Response)HBMine::Explode},
	{&EV_HBMine_Detect, (Response)HBMine::Detect},
	{&EV_Killed, (Response)HBMine::Explode},
	{NULL, NULL}
};

EXPORT_FROM_DLL void HBMine::Detect( Event *ev )
{
	Entity  *ent;

	// check if we should blow up now
	if((detonate_time + MINE_TIME) < level.time)
	{
		takedamage = DAMAGE_NO;
		ProcessEvent( EV_HBMine_Explode );
		return;
	}

	ent = findradius( NULL, origin.vec3(), MINE_RADIUS );
	while(ent)
	{
		if(ent->isSubclassOf( Sentient ))
		{
			if(!(detonate_time > level.time && ent->entnum == owner))
			{
				takedamage = DAMAGE_NO;
				PostEvent( EV_HBMine_Explode, 0.1 );
				return;
			}
		}
		ent = findradius( ent, origin.vec3(), MINE_RADIUS );
	}

	PostEvent( EV_HBMine_Detect, 0.1 );
}

EXPORT_FROM_DLL void HBMine::Explode( Event *ev )
{
	int damg;
	Entity      *ent;
	float tmpflt;
	Vector tmpvec;

	CancelPendingEvents();

	stopsound( CHAN_VOICE );

	takedamage = DAMAGE_NO;
	setSolidType( SOLID_NOT );
	hideModel();

	damg = MINE_DAMAGE;

	if(!deathmatch->value)
		damg *= 1.5;

	// don't do radius damage to the other, because all the damage
	// was done in the impact
	CreateExplosion( origin, damg, 1.0f, true, this, G_GetEntity( owner ), world, MOD_HB_MINE, 0.1 );

	// push sentient's away from the blast
	ent = findradius( NULL, origin.vec3(), MINE_RADIUS );
	while(ent)
	{
		if(ent->isSubclassOf( Sentient ))
		{
			tmpvec = ent->origin - origin;
			tmpflt = MINE_PUSH - tmpvec.length()*3;
			if((tmpvec[gravity_axis[gravaxis].z]*gravity_axis[gravaxis].sign) < 0)
			{
				tmpvec[gravity_axis[gravaxis].z] = 0;
			}
			// was just tmpvec.normalize(), but the compiler is a wanker
			tmpvec = tmpvec.normalize();
			tmpvec *= tmpflt;
			tmpvec[gravity_axis[gravaxis].z] += 150*gravity_axis[gravaxis].sign;
			// check if it's a player on a bike
			if(ent->isSubclassOf( Player ))
			{
				Player *rider;
				Hoverbike *bike;

				rider = (Player *)ent;
				bike = rider->GetHoverbike();

				if(bike)
				{
					// bike's get a bit of slow down from mines
					// but only half the push
					bike->velocity *= 0.9;
					bike->velocity += tmpvec*0.75;
				}
				else
				{
					rider->velocity += tmpvec;
				}
			}
			else
			{
				ent->velocity += tmpvec;
			}
		}
		ent = findradius( ent, origin.vec3(), MINE_RADIUS );
	}

	PostEvent( EV_Remove, 0.1 );
}

EXPORT_FROM_DLL void HBMine::Setup( Entity *owner, Vector pos, Vector dir )
{
	Event *ev;
	Vector smins, smaxs;
	Vector forward, right, up;
	Vector t[3];

	this->owner = owner->entnum;
	edict->owner = owner->edict;

	setMoveType( MOVETYPE_TOSS );
	setSolidType( SOLID_BBOX );
	edict->clipmask = MASK_PROJECTILE | MASK_WATER;

	SetGravityAxis( owner->gravaxis );

	angles = vec_zero;
	angles[YAW] = rand() % 360;
	angles.AngleVectors( &t[0], &t[1], &t[2] );
	forward[gravity_axis[gravaxis].x] = t[0][0];
	forward[gravity_axis[gravaxis].y] = t[0][1] * gravity_axis[gravaxis].sign;
	forward[gravity_axis[gravaxis].z] = t[0][2] * gravity_axis[gravaxis].sign;
	right[gravity_axis[gravaxis].x] = t[1][0];
	right[gravity_axis[gravaxis].y] = t[1][1] * gravity_axis[gravaxis].sign;
	right[gravity_axis[gravaxis].z] = t[1][2] * gravity_axis[gravaxis].sign;
	up[gravity_axis[gravaxis].x] = t[2][0];
	up[gravity_axis[gravaxis].y] = t[2][1] * gravity_axis[gravaxis].sign;
	up[gravity_axis[gravaxis].z] = t[2][2] * gravity_axis[gravaxis].sign;
	VectorsToEulerAngles( forward.vec3(), right.vec3(), up.vec3(), angles.vec3());
	setAngles( angles );

	velocity = owner->velocity*0.5 + dir * MINE_SPEED;
	velocity[gravity_axis[gravaxis].z] += 100*gravity_axis[gravaxis].sign;

	// set mine to proximity detect
	detonate_time = level.time + 2;
	PostEvent( EV_HBMine_Detect, 0.5 );

	takedamage = DAMAGE_YES;
	health = 25;

	setModel( "hovermine.def" );
	edict->s.effects  |= EF_ROCKET;
	edict->s.effects  |= EF_EVERYFRAME;
	gravity = 0.75;

	// setup ambient thrust
	ev = new Event( EV_RandomEntitySound );
	ev->AddString( "hover" );
	ProcessEvent( ev );

	smins = Vector( -10, -10, -10 );
	smaxs = Vector( 10, 10, 10 );
	smaxs[gravity_axis[gravaxis].z] = 18;
	smins[gravity_axis[gravaxis].z] = -18;
	setSize( smins, smaxs );
	setOrigin( pos );
	worldorigin.copyTo( edict->s.old_origin );

	RandomAnimate( "idle", NULL );
}
