/*
================================================================
MANERO DA BOSS MAN
================================================================

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.
*/

#include "maneroboss.h"
#include "surface.h"
#include "chaingun.h"
#include "flamethrower.h"


//===============================================================
// MANERO BOSS HELICOPTER
//===============================================================

/*****************************************************************************/
/*SINED monster_helicopterboss (0 .5 .8) (0 0 0) (0 0 0) NOT_SOLID

 *****************************************************************************/

CLASS_DECLARATION( ScriptModel, ManeroCopter, "monster_helicopterboss" );

Event EV_ManeroCopter_Attack( "startattacking" );
Event EV_ManeroCopter_StopAttack( "stopattacking" );
Event EV_ManeroCopter_FireRockets( "firerockets" );
Event EV_ManeroCopter_RemoveManero( "removemanero" );

ResponseDef ManeroCopter::Responses[] =
{
	{&EV_ManeroCopter_Attack, (Response)&ManeroCopter::StartAttacking},
	{&EV_ManeroCopter_StopAttack, (Response)&ManeroCopter::StopAttacking},
	{&EV_ManeroCopter_FireRockets, (Response)&ManeroCopter::FireRockets},
	{&EV_ManeroCopter_RemoveManero, (Response)&ManeroCopter::RemoveManero},
	{NULL, NULL},
};

ManeroCopter::ManeroCopter()
{
	int groupindex;
	int tri_num;
	Vector orient;

	setModel("lamprey_man.def");
	setSize(Vector(-150,-150,0),Vector(150,150,150));
	lastrockettime = 0;
	lastguntime = 0;
	lastgrenadetime = 0;
	lastfiredir = vec_zero;

	flags &= ~FL_POSTTHINK; // don't start out attacking

	// spawn the manero model
	maneromodel = new Entity;
	maneromodel->setModel( "manero.def" );
	maneromodel->setSolidType( SOLID_NOT );
	maneromodel->edict->s.effects |= EF_WARM;
	gi.GetBoneInfo( edict->s.modelindex, "seat", &groupindex, &tri_num, orient.vec3());
	maneromodel->attach( entnum, groupindex, tri_num, orient );
	maneromodel->setOrigin( Vector( 60, 24, 8 ));
	maneromodel->RandomAnimate( "heliride", NULL );

	manerogun = new Entity;
	manerogun->setModel( "hvgun.def" );
	manerogun->setSolidType( SOLID_NOT );
	gi.GetBoneInfo( maneromodel->edict->s.modelindex, "gun", &groupindex, &tri_num, orient.vec3());
	manerogun->attach( maneromodel->entnum, groupindex, tri_num, orient );
	manerogun->setOrigin( vec_zero );
}

// this enables the copters attack state
void ManeroCopter::StartAttacking( Event *ev )
{
	lastrockettime = level.time + 1 + G_Random( 1 );
	lastguntime = level.time + 1 + G_Random( 3 );
	lastgrenadetime = level.time + 1 + G_Random( 2 );

	// start doing the copters post thinking
	flags |= FL_POSTTHINK;
}

// this disables the copters attack state
void ManeroCopter::StopAttacking( Event *ev )
{
	flags &= ~FL_POSTTHINK;
}

void ManeroCopter::Postthink( void )
{
	float delta, yawofs;
	Vector forward, left, down;
	Vector playerdir;
	Vector tmpvec;


	// get directional vectors that we need
	forward = Vector( orientation[0] );
	left = Vector( orientation[1] );
	down = Vector( orientation[2] ) * -1;

	// get vector pointing to the player
	playerdir = G_GetEntity( 1 )->worldorigin - worldorigin;
	playerdir.normalize();

	// point manero's gun at the player
	manerogun->setAngles( vec_zero );
	tmpvec = playerdir.toAngles();
	tmpvec[PITCH] *= -1;
	tmpvec -= manerogun->worldangles;
	manerogun->setAngles( tmpvec );

	// find the yaw region the player is in relative to the copter
	tmpvec = playerdir.toAngles();
	yawofs = tmpvec[YAW] - angles[YAW];
	if(yawofs < -180)
		yawofs += 360;
	else if(yawofs > 180)
		yawofs -= 360;

	if((yawofs < 40) && (yawofs > -40)) // rockets yaw range
	{
		// check for firing rockets
		if(lastrockettime < level.time)
		{
			delta = DotProduct( playerdir.vec3(), forward.vec3());

			if(delta > 0.4)
			{
				Event *e;

				lastfiredir = playerdir;
				e = new Event( EV_ManeroCopter_FireRockets );
				e->AddInteger( 4 );
				PostEvent( e, 0.1 );

				lastrockettime = level.time + 2 + G_Random( 2 );
			}
		}
	}
	else if((yawofs > 30) && (yawofs < 150)) // Manero's chaingun yaw range
	{
		if(lastguntime < level.time)
		{
			delta = DotProduct( playerdir.vec3(), left.vec3());

			if(delta > 0.3)
			{
				FireGun();

				// have periodic pauses in the gun fire
				if(G_Random() < 0.075)
				{
					lastguntime = level.time + 0.7 + G_Random( 1.5 );
				}
			}
		}
	}

	// check for dropping grenades
	delta = DotProduct( playerdir.vec3(), down.vec3());
	if(delta > 0.7 && lastgrenadetime < level.time)
	{
		FireGrenade();

		lastgrenadetime = level.time + 0.7 + G_Random( 0.5 );
		// let's be nice and delay the chaingun
		lastguntime = level.time + 0.5 + G_Random( 0.5 );
	}
}

void ManeroCopter::RemoveManero( Event *ev )
{
	// make sure attack mode is off
	flags &= ~FL_POSTTHINK;

	if(manerogun)
	{
		manerogun->hideModel();
		manerogun->PostEvent( EV_Remove, 0.1 );
	}

	if(maneromodel)
	{
		maneromodel->hideModel();
		maneromodel->PostEvent( EV_Remove, 0.1 );
	}
}

void ManeroCopter::FireRockets( Event *ev )
{
	int rocketcount;
	float yawofs;
	Vector firepos, forward, right, up;
	Vector playerpos, playerdir, tmpvec;
	StingerRocket *rocket;

	rocketcount = ev->GetInteger( 1 );

	forward = Vector( orientation[0] );
	right = Vector( orientation[1] );
	up = Vector( orientation[2] );

	// set the place to fire from
	firepos = worldorigin + forward*200;
	playerpos = G_GetEntity( 1 )->worldorigin;
	switch(rocketcount)
	{
	case 4:
		firepos += right*40;
		firepos += up*32;
		break;
	case 3:
		firepos += forward*20;
		firepos += right*15;
		firepos += up*16;
		// a little bit of lead
		if(G_Random() < 0.7)
			playerpos += G_GetEntity( 1 )->velocity * 0.25;
		break;
	case 2:
		firepos += forward*20;
		firepos -= right*15;
		firepos += up*16;
		// a bit more lead
		if(G_Random() < 0.7)
			playerpos += G_GetEntity( 1 )->velocity * 0.5;
		break;
	default:
		firepos -= right*40;
		firepos += up*32;
		// even more lead
		if(G_Random() < 0.7)
			playerpos += G_GetEntity( 1 )->velocity;
		break;
	}

	playerdir = playerpos - firepos;
	playerdir.normalize();
	tmpvec = playerdir.toAngles();
	yawofs = tmpvec[YAW] - angles[YAW];
	if(yawofs < -180)
		yawofs += 360;
	else if(yawofs > 180)
		yawofs -= 360;

	// use last fire dir if player is now out of fire arc
	if((yawofs < -40) && (yawofs > 40))
	{
		playerdir = lastfiredir;
	}
	else // check fire pitch as well
	{
		yawofs = DotProduct( playerdir.vec3(), forward.vec3());

		if(yawofs < 0.3)
		{
			// nope, use old dir
			playerdir = lastfiredir;
		}
	}

	// fire off the rocket
	tmpvec = firepos + playerdir*256;
	rocket = new StingerRocket;
	rocket->Setup( this, firepos, tmpvec, playerdir, playerdir );
	lastfiredir = playerdir;

	// make firing sound
	sound( "weapons/rlaunch/mix2.wav" );

	rocketcount--;
	if(rocketcount)
	{
		Event *e;

		e = new Event( EV_ManeroCopter_FireRockets );
		e->AddInteger( rocketcount );
		PostEvent( e, 0.1 );
	}
}

extern Event EV_Grenade_Explode;

void ManeroCopter::FireGrenade( void )
{
	Grenade *grenade;
	Vector playerdir;
	Event *ev;

	playerdir = G_GetEntity( 1 )->centroid - worldorigin;
	playerdir.normalize();
	playerdir.x *= 1.5;
	playerdir.y *= 1.5;

	grenade = new Grenade;
	grenade->Setup( this, worldorigin, playerdir, vec_zero, vec_zero );

	// set it to blow up faster than a regular grenade
	grenade->CancelEventsOfType( EV_Grenade_Explode );
	ev = new Event( EV_Grenade_Explode );
	ev->AddEntity( world );
	grenade->PostEvent( ev, 1.0 + G_Random( 0.5 ));

	// make firing sound
	sound( "weapons/grenade/fire1.wav" );
}

void ManeroCopter::FireGun( void )
{
	Vector firepos, playerdir;

	firepos = GunPosition();

	playerdir = G_GetEntity( 1 )->centroid - firepos;
	playerdir.normalize();

	FireBullets( "150 150 150", 5, 10, firepos, playerdir );

	// make firing sound
	sound( "weapons/hvgun/2a.wav", 3 );
}

#define MAX_RICOCHETS 10

void ManeroCopter::TraceAttack( Vector start,Vector end,int damage,trace_t *trace,int numricochets,int kick )
{
	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, this );

		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 ( !ricochet )
				{
					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( 0 );
					gi.multicast( org.vec3(), MULTICAST_PVS );
				}
				MadeBreakingSound( org, this );
			}

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

	if ( 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 wewapon owner this time.
		//
		*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 );
		}
	}
}

void ManeroCopter::FireBullets( Vector spread,int mindamage,int maxdamage,Vector src,Vector dir )
{
	Vector end;
	trace_t trace;
	Vector right;
	Vector up;

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

	end = src +
	      dir       * 8192 +
	      right     * G_CRandom() * spread.x +
	      up        * G_CRandom() * spread.y;

	trace = G_FullTrace( src, vec_zero, vec_zero, end, 5, this, MASK_SHOT, "ManeroCopter::FireBullets" );
	FireTracer( src, Vector( trace.endpos ));

	if ( trace.fraction != 1.0 )
	{
		TraceAttack( src, trace.endpos, mindamage + (int)G_Random( maxdamage - mindamage + 1 ), &trace, MAX_RICOCHETS, 0 );
	}
}

void ManeroCopter::FireTracer( Vector src, Vector end )
{
	ManeroCopterTracer   *tracer;
	Vector dir;

	dir = end - src;
	dir.normalize();

	tracer = new ManeroCopterTracer;

	tracer->angles = dir.toAngles();
	tracer->angles[ PITCH ] = -tracer->angles[ PITCH ] + 90;

	tracer->setAngles( tracer->angles );
	tracer->setOrigin( manerogun->worldorigin );
	tracer->velocity = dir*2000;
}

Vector ManeroCopter::GunPosition( void )
{
	vec3_t trans[3];
	vec3_t orient;
	int groupindex;
	int tri_num;
	Vector offset = vec_zero;
	Vector result;

	// get the gun position of the actor
	if(!gi.GetBoneInfo( maneromodel->edict->s.modelindex, "gun", &groupindex, &tri_num, orient ))
	{
		// Gun doesn't have a barrel, just return the default
		return manerogun->worldorigin;
	}

	gi.GetBoneTransform( maneromodel->edict->s.modelindex, groupindex, tri_num, orient, maneromodel->edict->s.anim,
	                     maneromodel->edict->s.frame, maneromodel->edict->s.scale, trans, offset.vec3() );

	MatrixTransformVector( offset.vec3(), orientation, result.vec3() );
	result += maneromodel->worldorigin;

	return result;
}

//===============================================================

CLASS_DECLARATION( ScriptModel, ManeroCopterTracer, "coptertracer" );

ResponseDef ManeroCopterTracer::Responses[] =
{
	{&EV_Touch, (Response)&ManeroCopterTracer::RemoveTracer},
	{NULL, NULL},
};

ManeroCopterTracer::ManeroCopterTracer()
{
	setMoveType( MOVETYPE_FLY );
	setSolidType( SOLID_TRIGGER );
	setModel( "sprites/tracer.spr" );
	setSize(Vector(-4,-4,-4),Vector(4,4,4));
	edict->s.renderfx &= ~RF_FRAMELERP;
	edict->clipmask = MASK_SOLID;

	PostEvent( EV_Remove, 5 );
}

void ManeroCopterTracer::RemoveTracer( Event *ev )
{
	Hide();
	PostEvent( EV_Remove, 0.1 );
}

//===============================================================
// MANERO BOSS HIMSELF
//===============================================================

CLASS_DECLARATION( Actor, ManeroBoss, "monster_maneroboss" );

Event EV_Manero_ShieldsOn( "shieldson" );
Event EV_Manero_ShieldsOff( "shieldsoff" );
Event EV_Manero_CloakOn( "cloakon" );
Event EV_Manero_CloakOff( "cloakoff" );
Event EV_Manero_FireNuke( "firenuke" );

ResponseDef ManeroBoss::Responses[] =
{
	{&EV_Manero_ShieldsOn, (Response)&ManeroBoss::ShieldsOn},
	{&EV_Manero_ShieldsOff, (Response)&ManeroBoss::ShieldsOff},
	{&EV_Manero_CloakOn, (Response)&ManeroBoss::CloakOn},
	{&EV_Manero_CloakOff, (Response)&ManeroBoss::CloakOff},
	{&EV_Manero_FireNuke, (Response)&ManeroBoss::FireNuke},
	{&EV_Damage, (Response)&ManeroBoss::ArmorDamage},
	{&EV_Pain, (Response)&ManeroBoss::Pain},
	{&EV_Killed, (Response)&ManeroBoss::Killed},
	{&EV_Actor_Dead, (Response)&ManeroBoss::Dead},
	{NULL, NULL},
};

ManeroBoss::ManeroBoss()
{
	int armamount;

	// sorry Mr. player, you can't mess with Manero
	mass = 2000;

	// give him a second weapon... the nuke >)
	secondaryWeapon = new ManeroIP36;

	secondaryWeapon->SetOwner( this );
	secondaryWeapon->AttachToOwner();

	dropweapon = false;
	flags |= FL_POSTTHINK;

	// give Manero lotsa armor
	if(skill->value == 0)
		armamount = 200;
	if(skill->value == 1)
		armamount = 250;
	else
		armamount = 300;
	giveItem( "RiotHelmet", armamount );
	giveItem( "FlakJacket", armamount );
	giveItem( "FlakPants", armamount );
}

void ManeroBoss::Postthink( void )
{
	// ensure that the nuke stays transparent
	// when Manero's cloaked
	if(translucence && secondaryWeapon)
	{
		secondaryWeapon->setAlpha( 0.1 );
	}
}

void ManeroBoss::ShieldsOn( Event *ev )
{
	int armamount;

	flags |= FL_SHIELDS;
	edict->s.renderfx |= RF_DLIGHT;
	edict->s.renderfx |= RF_ENVMAPPED;
	edict->s.color_r = 0;
	edict->s.color_g = 0;
	edict->s.color_b = 0.75;
	edict->s.radius = 120;
	edict->s.effects &= ~EF_WARM;

	if(currentWeapon)
	{
		currentWeapon->edict->s.renderfx |= RF_ENVMAPPED;
	}

	if(secondaryWeapon)
	{
		secondaryWeapon->edict->s.renderfx |= RF_ENVMAPPED;
	}

	// give Manero lotsa armor
	if(skill->value == 0)
		armamount = 200;
	if(skill->value == 1)
		armamount = 250;
	else
		armamount = 300;
	giveItem( "RiotHelmet", armamount );
	giveItem( "FlakJacket", armamount );
	giveItem( "FlakPants", armamount );
}

void ManeroBoss::ShieldsOff( Event *ev )
{
	flags &= ~FL_SHIELDS;
	edict->s.renderfx &= ~RF_DLIGHT;
	edict->s.renderfx &= ~RF_ENVMAPPED;
	edict->s.color_r = 0;
	edict->s.color_g = 0;
	edict->s.color_b = 0;
	edict->s.radius = 0;
	edict->s.effects |= EF_WARM;

	if(currentWeapon)
	{
		currentWeapon->edict->s.renderfx &= ~RF_ENVMAPPED;
	}

	if(secondaryWeapon)
	{
		secondaryWeapon->edict->s.renderfx &= ~RF_ENVMAPPED;
	}

	sound( "player/pkup/shield/fail.wav", 2, CHAN_ITEM );
}

void ManeroBoss::CloakOn( Event *ev )
{
	int armamount;

	edict->s.renderfx |= RF_DLIGHT;
	edict->s.color_r = 0.75;
	edict->s.color_g = 0.75;
	edict->s.color_b = 0.75;
	edict->s.radius = -120;
	setAlpha( 0 );

	if(currentWeapon)
	{
		currentWeapon->setAlpha( 0.1 );
	}

	if(secondaryWeapon)
	{
		secondaryWeapon->setAlpha( 0.1 );
	}

	sound( "player/pkup/shield/activate3.wav", 2, CHAN_ITEM );

	// give Manero lotsa armor
	if(skill->value == 0)
		armamount = 200;
	if(skill->value == 1)
		armamount = 250;
	else
		armamount = 300;
	giveItem( "RiotHelmet", armamount );
	giveItem( "FlakJacket", armamount );
	giveItem( "FlakPants", armamount );
}

void ManeroBoss::CloakOff( Event *ev )
{
	edict->s.renderfx &= ~RF_DLIGHT;
	edict->s.color_r = 0;
	edict->s.color_g = 0;
	edict->s.color_b = 0;
	edict->s.radius = 0;
	setAlpha( 1.0 );

	if(currentWeapon)
	{
		currentWeapon->setAlpha( 1.0 );
	}

	if(secondaryWeapon)
	{
		secondaryWeapon->setAlpha( 1.0 );
	}

	sound( "player/pkup/shield/fail.wav", 2, CHAN_ITEM );
}

void ManeroBoss::FireNuke( Event *ev )
{
	int armamount;

	if(!secondaryWeapon)
		return;

	if(!secondaryWeapon->ReadyToFire())
	{
		PostEvent( EV_Manero_FireNuke, 0.1 );
		return;
	}

	secondaryWeapon->Fire();

	// make sure Manero's got some armor
	if(skill->value == 0)
		armamount = 50;
	if(skill->value == 1)
		armamount = 100;
	else
		armamount = 150;
	giveItem( "RiotHelmet", armamount );
	giveItem( "FlakJacket", armamount );
	giveItem( "FlakPants", armamount );
}

void ManeroBoss::ArmorDamage( Event *ev )
{
	Entity  *inflictor;
	Entity  *attacker;

	// let his almightyness ignore damaging himself
	inflictor = ev->GetEntity( 2 );
	attacker = ev->GetEntity( 3 );

	if((inflictor == this) || (attacker == this))
		return;

	Sentient::ArmorDamage( ev );
}

void ManeroBoss::Pain( Event *ev )
{
	float damage;
	Entity  *ent;
	float oldhealth;
	float newhealth;

	damage = ev->GetFloat( 1 );
	ent = ev->GetEntity( 2 );

	// if it's a Sentient and not liked, attack 'em.
	if ( ent && ent->isSubclassOf( Sentient ) && !Likes( ent ) )
	{
		MakeEnemy( ent );
		if ( ent != currentEnemy )
		{
			currentEnemy = BestTarget();
		}
	}

	if ( damage <= 0 )
	{
		return;
	}

	oldhealth = ( health + damage ) / max_health;
	newhealth = health / max_health;

	SetVariable( "other", ev->GetEntity( 2 ) );

	// If we pass more than one range,
	if ( ( oldhealth > 0.875 ) && ( newhealth <= 0.875 ) )
	{
		DoAction( "health_good", true );
	}
	if ( ( oldhealth > 0.75 ) && ( newhealth <= 0.75 ) )
	{
		DoAction( "health_ok", true );
	}
	if ( ( oldhealth > 0.5 ) && ( newhealth <= 0.5 ) )
	{
		DoAction( "health_med", true );
	}
	if ( ( oldhealth > 0.25 ) && ( newhealth <= 0.25 ) )
	{
		DoAction( "health_low", true );
	}

	if ( damage <= pain_threshold )
	{
		Chatter( "snd_pain_taunt", 5, true );
		return;
	}

	if ( strncmp( animname.c_str(), "pain", 4 ) && strncmp( animname.c_str(), "crouch_pain", 11 ) )
	{
		str aname;
		int index;

		//
		// determine pain animation
		//
		if ( !strncmp( animname.c_str(), "crouch", 6 ) )
		{
			aname = "crouch_";
		}
		aname += str( "pain_" ) + str( ev->GetString( 3 ) );
		index = gi.Anim_Random( edict->s.modelindex, aname.c_str() );
		if ( ( index == -1 ) && !strncmp( animname.c_str(), "crouch", 6 ) )
		{
			aname = "crouch_pain";
			index = gi.Anim_Random( edict->s.modelindex, aname.c_str() );
		}

		if ( index == -1 )
		{
			aname = "pain";
		}

		SetVariable( "painanim", aname.c_str() );
		DoAction( "pain" );
	}
}

void ManeroBoss::Killed( Event *ev )
{
	Entity      *attacker;
	Entity      *inflictor;
	Vector dir;
	Event           *event;
	int i;
	str dname;
	int meansofdeath;

	if(secondaryWeapon)
	{
		secondaryWeapon->hideModel();
		secondaryWeapon->PostEvent( EV_Remove, 0.1 );
	}

	ExecuteThread( "manero_is_a_dead_dumb_bitch", true );

	CheckWater();
	StopAnimating();
	CancelPendingEvents();

	// don't allow them to fly, think, or swim anymore
	flags &= ~( FL_PRETHINK | FL_SWIM | FL_FLY );

	deadflag = DEAD_DYING;
	takedamage = DAMAGE_YES;
	groundentity = NULL;

	attacker    = ev->GetEntity( 1 );
	inflictor = ev->GetEntity( 3 );
	meansofdeath = ev->GetInteger( 5 );

	SetVariable( "other", ev->GetEntity( 1 ) );
	if ( !DoAction( "killed" ) && actorthread )
	{
		actorthread->ProcessEvent( EV_ScriptThread_End );
	}


	// Turn off dlight and shadow
	edict->s.renderfx &= ~( RF_DLIGHT|RF_XFLIP );

	// skin darkening for death from flames
	if(inflictor->isSubclassOf( ThrowerFlame ) || meansofdeath == MOD_FLAMETHROWER)
	{
		edict->s.renderfx |= RF_LIGHTOFFSET;
		edict->s.lightofs = -127;

		CancelEventsOfType( EV_Sentient_HurtFlame );
		edict->s.effects  &= ~EF_FLAMES;
		edict->s.effects  |= EF_DEATHFLAMES;
	}
	else
	{
		// turn off the actor's heat signature
		edict->s.effects &= ~EF_WARM;
	}

	if ( currentWeapon )
	{
		currentWeapon->PostEvent( EV_Remove, 0.1 );
	}

	animOverride = false;

	//
	// determine death animation
	//
	if ( !strncmp( animname.c_str(), "crouch", 6 ) )
	{
		dname = "crouch_";
	}

	if ( deathgib )
	{
		str location;

		location = ev->GetString( 4 );

		// Check for location first otherwise randomize
		if ( location == "torso_upper" )
			dname += str( "gibdeath_upper" );
		else if ( location == "torso_lower" )
			dname += str( "gibdeath_lower" );
		else if ( strstr( location.c_str(), "leg" ) )
			dname += str( "gibdeath_lower" );
		else if ( strstr( location.c_str(), "arm" ) )
			dname += str( "gibdeath_upper" );
		else if ( strstr( location.c_str(), "head" ) )
			dname += str( "gibdeath_upper" );
		else if ( G_Random() > 0.5 )
			dname += str( "gibdeath_upper" );
		else
			dname += str( "gibdeath_lower" );
	}
	else
	{
		dname += str( "death_" ) + str( ev->GetString( 4 ) );
	}

	i = gi.Anim_Random( edict->s.modelindex, dname.c_str() );

	if ( ( i == -1 ) && !strncmp( animname.c_str(), "crouch", 6 ) )
	{
		dname = "crouch_death";
		i = gi.Anim_Random( edict->s.modelindex, dname.c_str() );
	}

	if ( i == -1 )
	{
		dname = "death";
	}

	if ( ( i != -1 ) &&  ( !strncmp( dname.c_str(), "gibdeath", 7 ) ) )
	{
		Event *ev1;

		ev1 = new Event( EV_Gib );
		ev1->AddInteger( 1 );
		ProcessEvent( ev1 );
	}
	if ( attacker )
	{
		str location;
		float damage;

		damage = ev->GetFloat( 2 );
		location = ev->GetString( 4 );

		event = new Event( EV_GotKill );
		event->AddEntity( this );
		event->AddInteger( damage );
		event->AddEntity( inflictor );
		event->AddString( location );
		event->AddInteger( meansofdeath );
		event->AddInteger( deathgib );
		attacker->ProcessEvent( event );
	}

	SetAnim( dname.c_str(), EV_Actor_Dead );

	// Call changeanim immediatly since we're no longer calling prethink
	ChangeAnim();

	//
	// moved this here so guys would not be solid right away
	//
	edict->svflags |= SVF_DEADMONSTER;
	edict->clipmask = MASK_DEADSOLID;

	if ( velocity.z < 10 )
	{
		velocity.z += G_Random( 300 );
	}

	angles.x = 0;
	angles.z = 0;
	setAngles( angles );
}

void ManeroBoss::Dead( Event *ev )
{
	Vector min, max;

	StopAnimating();
	if ( !groundentity && ( velocity != vec_zero ) )
	{
		// wait until we hit the ground
		PostEvent( ev, FRAMETIME );
		return;
	}

	deadflag = DEAD_DEAD;
	maxs[0] *= 2.0f;
	maxs[1] *= 2.0f;
	maxs[2] *= 0.3f;
	setSize( mins,maxs );
	edict->svflags |= SVF_DEADMONSTER;
	setMoveType( MOVETYPE_NONE );
	setOrigin( worldorigin );
	takedamage = DAMAGE_NO;
}

//===============================================================
// WEAPONS MODIFIED FOR MANERO BOSS
//===============================================================

CLASS_DECLARATION( StingerPack, ManeroStingerPack, "weapon_manerostingerpack" );

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

ManeroStingerPack::ManeroStingerPack()
{
	SetMinRange( 40 );
}

void ManeroStingerPack::Shoot( Event *ev )
{
	StingerPack::Shoot( ev );
	NextAttack( 2.5 - (skill->value*0.5));
}

CLASS_DECLARATION( IP36, ManeroIP36, "weapon_maneroip36" );

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

ManeroIP36::ManeroIP36()
{}

void ManeroIP36::Shoot( Event *ev )
{
	IP36::Shoot( ev );
	NextAttack( 0.1 );
}
