shithub: qk2

ref: a4c4b45d8b2136733c76a4b86c7896bfa939ad25
dir: qk2/rogue/m_widow2.c

View raw version
#include <u.h>
#include <libc.h>
#include <stdio.h>
#include "../dat.h"
#include "../fns.h"
#include "m_widow2.h"

// timestamp used to prevent rapid fire of melee attack


#define	NUM_STALKERS_SPAWNED		6		// max # of stalkers she can spawn

#define	DISRUPT_TIME					3

static int	sound_pain1;
static int	sound_pain2;
static int	sound_pain3;
static int	sound_death;
static int	sound_search1;
static int	sound_disrupt;
static int	sound_tentacles_retract;

// sqrt(64*64*2) + sqrt(28*28*2) => 130.1
static vec3_t spawnpoints[] = {
	{30,  135, 0},
	{30, -135, 0}
};

static float sweep_angles[] = {
	-40.0, -32.0, -24.0, -16.0, -8.0, 0.0, 8.0, 16.0, 24.0, 32.0, 40.0
};

extern vec3_t	stalker_mins, stalker_maxs;

qboolean infront (edict_t *self, edict_t *other);
void WidowCalcSlots (edict_t *self);
void WidowPowerups (edict_t *self);

void widow2_run (edict_t *self);
void widow2_stand (edict_t *self);
void widow2_dead (edict_t *self);
void widow2_attack (edict_t *self);
void widow2_attack_beam (edict_t *self);
void widow2_reattack_beam (edict_t *self);
void widow2_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
void widow_start_spawn (edict_t *self);
void widow_done_spawn (edict_t *self);
void widow2_spawn_check (edict_t *self);
void widow2_prep_spawn (edict_t *self);
void Widow2SaveBeamTarget(edict_t *self);

// death stuff
void WidowExplode (edict_t *self);
void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
void ThrowWidowGibReal (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean large, int hitsound, qboolean fade);
void ThrowWidowGibSized (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, int hitsound, qboolean fade);
void ThrowWidowGibLoc (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean fade);
void WidowExplosion1 (edict_t *self);
void WidowExplosion2 (edict_t *self);
void WidowExplosion3 (edict_t *self);
void WidowExplosion4 (edict_t *self);
void WidowExplosion5 (edict_t *self);
void WidowExplosion6 (edict_t *self);
void WidowExplosion7 (edict_t *self);
void WidowExplosionLeg (edict_t *self);
void ThrowArm1 (edict_t *self);
void ThrowArm2 (edict_t *self);
void ClipGibVelocity (edict_t *ent);
// end of death stuff

// these offsets used by the tongue
static vec3_t offsets[] = {
	{17.48, 0.10, 68.92},
	{17.47, 0.29, 68.91},
	{17.45, 0.53, 68.87},
	{17.42, 0.78, 68.81},
	{17.39, 1.02, 68.75},
	{17.37, 1.20, 68.70},
	{17.36, 1.24, 68.71},
	{17.37, 1.21, 68.72},
};

void showme (edict_t *self);

void pauseme (edict_t *self)
{
	self->monsterinfo.aiflags |= AI_HOLD_FRAME;
}

void widow2_search (edict_t *self)
{
	if (qrandom() < 0.5)
		gi.sound (self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0);
}

void Widow2Beam (edict_t *self)
{
	vec3_t	forward, right, target;
	vec3_t	start, targ_angles, vec;
	int		flashnum;

	if ((!self->enemy) || (!self->enemy->inuse))
		return;

	AngleVectors (self->s.angles, forward, right, NULL);
	
	if ((self->s.frame >= FRAME_fireb05) && (self->s.frame <= FRAME_fireb09))
	{
		// regular beam attack
		Widow2SaveBeamTarget(self);
		flashnum = MZ2_WIDOW2_BEAMER_1 + self->s.frame - FRAME_fireb05;
		G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
		VectorCopy (self->pos2, target);
		target[2] += self->enemy->viewheight-10;
		VectorSubtract (target, start, forward);
		VectorNormalize (forward);
		monster_fire_heat (self, start, forward, vec3_origin, 10, 50, flashnum);
	}
	else if ((self->s.frame >= FRAME_spawn04) && (self->s.frame <= FRAME_spawn14))
	{
		// sweep
		flashnum = MZ2_WIDOW2_BEAM_SWEEP_1 + self->s.frame - FRAME_spawn04;
		G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
		VectorSubtract (self->enemy->s.origin, start, target);
		vectoangles2 (target, targ_angles);
		
		VectorCopy (self->s.angles, vec);

		vec[PITCH] += targ_angles[PITCH];
		vec[YAW] -= sweep_angles[flashnum-MZ2_WIDOW2_BEAM_SWEEP_1];

		AngleVectors (vec, forward, NULL, NULL);
		monster_fire_heat (self, start, forward, vec3_origin, 10, 50, flashnum);
/*
		if (self->s.frame == FRAME_spawn04)
		{
			VectorMA (start, 1024, forward, debugend);

			gi.WriteByte (svc_temp_entity);
			gi.WriteByte (TE_DEBUGTRAIL);
			gi.WritePosition (start);
			gi.WritePosition (debugend);
			gi.multicast (start, MULTICAST_ALL);

			drawbbox (self);
			self->monsterinfo.aiflags |= AI_HOLD_FRAME|AI_MANUAL_STEERING;
		}
*/
	}
	else
	{
//		if ((g_showlogic) && (g_showlogic->value))
//			gi.dprintf ("bad fire frame for widow2 beam -- tell me you saw this!\n");

		Widow2SaveBeamTarget(self);
		G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_WIDOW2_BEAMER_1], forward, right, start);

		VectorCopy (self->pos2, target);
		target[2] += self->enemy->viewheight-10;
		
		VectorSubtract (target, start, forward);
		VectorNormalize (forward);

		monster_fire_heat (self, start, forward, vec3_origin, 10, 50, 0);
	}	
}

void Widow2Spawn (edict_t *self)
{
	vec3_t	f, r, u, offset, startpoint, spawnpoint;
	edict_t	*ent, *designated_enemy;
	int		i;

	AngleVectors (self->s.angles, f, r, u);

	for (i=0; i < 2; i++)
	{
		VectorCopy (spawnpoints[i], offset);

		G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);

		if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
		{
			ent = CreateGroundMonster (spawnpoint, self->s.angles, stalker_mins, stalker_maxs, "monster_stalker", 256);

			if (!ent)
				continue;

			self->monsterinfo.monster_used++;
			ent->monsterinfo.commander = self;
//			if ((g_showlogic) && (g_showlogic->value))
//				gi.dprintf ("widow: post-spawn : %d slots left\n", SELF_SLOTS_LEFT);

			ent->nextthink = level.time;
			ent->think (ent);
			
			ent->monsterinfo.aiflags |= AI_SPAWNED_WIDOW|AI_DO_NOT_COUNT|AI_IGNORE_SHOTS;

			if (!(coop && coop->value))
			{
				designated_enemy = self->enemy;
			}
			else
			{
				designated_enemy = PickCoopTarget(ent);
				if (designated_enemy)
				{
					// try to avoid using my enemy
					if (designated_enemy == self->enemy)
					{
						designated_enemy = PickCoopTarget(ent);
						if (designated_enemy)
						{
//							if ((g_showlogic) && (g_showlogic->value))
//							{
//								gi.dprintf ("PickCoopTarget returned a %s - ", designated_enemy->classname);
//								if (designated_enemy->client)
//									gi.dprintf ("with name %s\n", designated_enemy->client->pers.netname);
//								else
//									gi.dprintf ("NOT A CLIENT\n");
//							}
						}
						else
						{
//							if ((g_showlogic) && (g_showlogic->value))
//								gi.dprintf ("pick coop failed, using my current enemy\n");
							designated_enemy = self->enemy;
						}
					}
				}
				else
				{
//					if ((g_showlogic) && (g_showlogic->value))
//						gi.dprintf ("pick coop failed, using my current enemy\n");
					designated_enemy = self->enemy;
				}
			}

			if ((designated_enemy->inuse) && (designated_enemy->health > 0))
			{
				ent->enemy = designated_enemy;
				FoundTarget (ent);
				ent->monsterinfo.attack(ent);
			}
		}
	}
}

void widow2_spawn_check (edict_t *self)
{
	Widow2Beam(self);
	Widow2Spawn (self);
}

void widow2_ready_spawn (edict_t *self)
{
	vec3_t	f, r, u, offset, startpoint, spawnpoint;
	int		i;

	Widow2Beam(self);
	AngleVectors (self->s.angles, f, r, u);

	for (i=0; i < 2; i++)
	{
		VectorCopy (spawnpoints[i], offset);
		G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
		if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
		{
			SpawnGrow_Spawn (spawnpoint, 1);
		}
	}
}

mframe_t widow2_frames_stand [] =
{
//	ai_stand, 0, drawbbox
	ai_stand, 0, NULL
};
mmove_t	widow2_move_stand = {FRAME_blackwidow3, FRAME_blackwidow3, widow2_frames_stand, NULL};

mframe_t widow2_frames_walk [] =
{
//	ai_walk,	9.01,	drawbbox,
	ai_walk,	9.01,	NULL,
	ai_walk,	7.55,	NULL,
	ai_walk,	7.01,	NULL,
	ai_walk,	6.66,	NULL,
	ai_walk,	6.20,	NULL,
	ai_walk,	5.78,	NULL,
	ai_walk,	7.25,	NULL,
	ai_walk,	8.37,	NULL,
	ai_walk,	10.41,	NULL
};
mmove_t widow2_move_walk = {FRAME_walk01, FRAME_walk09, widow2_frames_walk, NULL};


mframe_t widow2_frames_run [] =
{
//	ai_run,	9.01,	drawbbox,
	ai_run,	9.01,	NULL,
	ai_run,	7.55,	NULL,
	ai_run,	7.01,	NULL,
	ai_run,	6.66,	NULL,
	ai_run,	6.20,	NULL,
	ai_run,	5.78,	NULL,
	ai_run,	7.25,	NULL,
	ai_run,	8.37,	NULL,
	ai_run,	10.41,	NULL
};
mmove_t widow2_move_run = {FRAME_walk01, FRAME_walk09, widow2_frames_run, NULL};

mframe_t widow2_frames_attack_pre_beam [] =
{
	ai_charge,	4,	NULL,
	ai_charge,	4,	NULL,
	ai_charge,	4,	NULL,
	ai_charge,	4,	widow2_attack_beam
};
mmove_t widow2_move_attack_pre_beam = {FRAME_fireb01, FRAME_fireb04, widow2_frames_attack_pre_beam, NULL};


// Loop this
mframe_t widow2_frames_attack_beam [] =
{
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	widow2_reattack_beam
};
mmove_t widow2_move_attack_beam = {FRAME_fireb05, FRAME_fireb09, widow2_frames_attack_beam, NULL};

mframe_t widow2_frames_attack_post_beam [] =
{
	ai_charge,	4,	NULL,
	ai_charge,	4,	NULL,
	ai_charge,	4,	NULL
};
mmove_t widow2_move_attack_post_beam = {FRAME_fireb06, FRAME_fireb07, widow2_frames_attack_post_beam, widow2_run};


void WidowDisrupt (edict_t *self)
{
	vec3_t	start;
	vec3_t	dir;
	vec3_t	forward, right;
	float	len;

	AngleVectors (self->s.angles, forward, right, NULL);
	G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_WIDOW_DISRUPTOR], forward, right, start);

	VectorSubtract (self->pos1, self->enemy->s.origin, dir);
	len = VectorLength (dir);

	if (len < 30)
	{
//		if ((g_showlogic) && (g_showlogic->value))
//			gi.dprintf ("target locked - dist %2.2f\n", len);
		// calc direction to where we targeted
		VectorSubtract (self->pos1, start, dir);
		VectorNormalize (dir);

		monster_fire_tracker(self, start, dir, 20, 500, self->enemy, MZ2_WIDOW_DISRUPTOR);
	}
	else
	{
//		if ((g_showlogic) && (g_showlogic->value))
//			gi.dprintf ("target missed - dist %2.2f\n", len);

		PredictAim (self->enemy, start, 1200, true, 0, dir, NULL);

//		VectorSubtract (self->enemy->s.origin, start, dir);
//		VectorNormalize (dir);
		monster_fire_tracker(self, start, dir, 20, 1200, NULL, MZ2_WIDOW_DISRUPTOR);
	}
}

void Widow2SaveDisruptLoc (edict_t *self)
{
	if (self->enemy && self->enemy->inuse)
	{
		VectorCopy (self->enemy->s.origin, self->pos1);	//save for aiming the shot
		self->pos1[2] += self->enemy->viewheight;
	}
	else
		VectorCopy (vec3_origin, self->pos1);
};

void widow2_disrupt_reattack (edict_t *self)
{
	float luck;
	
	luck = qrandom();

	if (luck < (0.25 + ((float)(skill->value))*0.15))
		self->monsterinfo.nextframe = FRAME_firea01;
}

mframe_t widow2_frames_attack_disrupt [] =
{
	ai_charge, 2, NULL,
	ai_charge, 2, NULL,
	ai_charge, 2, Widow2SaveDisruptLoc,
	ai_charge, -20, WidowDisrupt,
	ai_charge, 2, NULL,
	ai_charge, 2, NULL,
	ai_charge, 2, widow2_disrupt_reattack
};
mmove_t widow2_move_attack_disrupt = {FRAME_firea01, FRAME_firea07, widow2_frames_attack_disrupt, widow2_run};

void Widow2SaveBeamTarget (edict_t *self)
{
	if (self->enemy && self->enemy->inuse)
	{
		VectorCopy (self->pos1, self->pos2);
		VectorCopy (self->enemy->s.origin, self->pos1);	//save for aiming the shot
	}
	else
	{
		VectorCopy (vec3_origin, self->pos1);
		VectorCopy (vec3_origin, self->pos2);
	}
}

void Widow2BeamTargetRemove (edict_t *self)
{
	VectorCopy (vec3_origin, self->pos1);
	VectorCopy (vec3_origin, self->pos2);
}

void Widow2StartSweep (edict_t *self)
{
	Widow2SaveBeamTarget (self);
}

mframe_t widow2_frames_spawn [] =
{
	ai_charge,	0,	NULL,
	ai_charge,	0,	NULL,
	ai_charge,	0,	widow_start_spawn,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,				//5
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	widow2_ready_spawn,				//10
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	Widow2Beam,
	ai_charge,	0,	widow2_spawn_check,
	ai_charge,	0,	NULL,				//15
	ai_charge,	0,	NULL,
	ai_charge,	0,	NULL,
	ai_charge,	0,	widow2_reattack_beam
};
mmove_t widow2_move_spawn = {FRAME_spawn01, FRAME_spawn18, widow2_frames_spawn, NULL};

static qboolean widow2_tongue_attack_ok (vec3_t start, vec3_t end, float range)
{
	vec3_t	dir, angles;

	// check for max distance
	VectorSubtract (start, end, dir);
	if (VectorLength(dir) > range)
		return false;

	// check for min/max pitch
	vectoangles (dir, angles);
	if (angles[0] < -180)
		angles[0] += 360;
	if (fabs(angles[0]) > 30)
		return false;

	return true;
}

void Widow2Tongue (edict_t *self)
{
	vec3_t	f, r, u;
	vec3_t	start, end, dir;
	trace_t	tr;

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offsets[self->s.frame - FRAME_tongs01], f, r, u, start);
	VectorCopy (self->enemy->s.origin, end);
	if (!widow2_tongue_attack_ok(start, end, 256))
	{
		end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8;
		if (!widow2_tongue_attack_ok(start, end, 256))
		{
			end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8;
			if (!widow2_tongue_attack_ok(start, end, 256))
				return;
		}
	}
	VectorCopy (self->enemy->s.origin, end);

	tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
	if (tr.ent != self->enemy)
		return;

	gi.sound (self, CHAN_WEAPON, sound_tentacles_retract, 1, ATTN_NORM, 0);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_PARASITE_ATTACK);
	gi.WriteShort (self - g_edicts);
	gi.WritePosition (start);
	gi.WritePosition (end);
	gi.multicast (self->s.origin, MULTICAST_PVS);

	VectorSubtract (start, end, dir);
	T_Damage (self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, 2, 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN);
}

void Widow2TonguePull (edict_t *self)
{
	vec3_t	vec;
	vec3_t	f, r, u;
	vec3_t	start, end;

	if ((!self->enemy) || (!self->enemy->inuse))
	{
		self->monsterinfo.run (self);
		return;
	}

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offsets[self->s.frame - FRAME_tongs01], f, r, u, start);
	VectorCopy (self->enemy->s.origin, end);

	if (!widow2_tongue_attack_ok(start, end, 256))
	{
		return;
	}

	if (self->enemy->groundentity)
	{
		self->enemy->s.origin[2] += 1;
		self->enemy->groundentity = NULL;
		// interesting, you don't have to relink the player
	}
	
	VectorSubtract (self->s.origin, self->enemy->s.origin, vec);
	if (self->enemy->client)
	{
		VectorNormalize (vec);
		VectorMA (self->enemy->velocity, 1000, vec, self->enemy->velocity);
	}
	else
	{
		self->enemy->ideal_yaw = vectoyaw(vec);	
		M_ChangeYaw (self->enemy);
		VectorScale (f, 1000, self->enemy->velocity);
	}
}

void Widow2Crunch (edict_t *self)
{
	vec3_t	aim;

	if ((!self->enemy) || (!self->enemy->inuse))
	{
		self->monsterinfo.run (self);
		return;
	}

	Widow2TonguePull (self);

	// 70 + 32
	VectorSet (aim, 150, 0, 4);
	if (self->s.frame != FRAME_tongs07)
		fire_hit (self, aim, 20 + (rand() % 6), 0);
	else
	{
		if (self->enemy->groundentity)
			fire_hit (self, aim, (20 + (rand() % 6)), 500);
		else	// not as much kick if they're in the air .. makes it harder to land on her head
			fire_hit (self, aim, (20 + (rand() % 6)), 250);
	}
}

void Widow2Toss (edict_t *self)
{
	self->timestamp = level.time + 3;
	return;
}

mframe_t widow2_frames_tongs [] =
{
	ai_charge,	0,	Widow2Tongue,
	ai_charge,	0,	Widow2Tongue,
	ai_charge,	0,	Widow2Tongue,
	ai_charge,	0,	Widow2TonguePull,
	ai_charge,	0,	Widow2TonguePull,				//5
	ai_charge,	0,	Widow2TonguePull,
	ai_charge,	0,	Widow2Crunch,
	ai_charge,	0,	Widow2Toss
};
mmove_t widow2_move_tongs = {FRAME_tongs01, FRAME_tongs08, widow2_frames_tongs, widow2_run};

mframe_t widow2_frames_pain [] =
{
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL
};
mmove_t widow2_move_pain = {FRAME_pain01, FRAME_pain05, widow2_frames_pain, widow2_run};

mframe_t widow2_frames_death [] =
{
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	WidowExplosion1,	// 3 boom
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,				// 5

	ai_move,	0,	WidowExplosion2,	// 6 boom
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,				// 10

	ai_move,	0,	NULL,
	ai_move,	0,	NULL,				// 12
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,				// 15

	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	WidowExplosion3,	// 18
	ai_move,	0,	NULL,				// 19
	ai_move,	0,	NULL,				// 20

	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	WidowExplosion4,	// 25

	ai_move,	0,	NULL,				// 26
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	WidowExplosion5,
	ai_move,	0,	WidowExplosionLeg,	// 30

	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	WidowExplosion6,
	ai_move,	0,	NULL,				// 35

	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	WidowExplosion7,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,				// 40

	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	WidowExplode		// 44
};
mmove_t widow2_move_death = {FRAME_death01, FRAME_death44, widow2_frames_death, NULL};

void widow2_start_searching (edict_t *self);
void widow2_keep_searching (edict_t *self);
void widow2_finaldeath (edict_t *self);

mframe_t widow2_frames_dead [] =
{
	ai_move,	0,	widow2_start_searching,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,

	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,

	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	widow2_keep_searching
};
mmove_t widow2_move_dead = {FRAME_dthsrh01, FRAME_dthsrh15, widow2_frames_dead, NULL};

mframe_t widow2_frames_really_dead [] =
{
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,
	ai_move,	0,	NULL,

	ai_move,	0,	NULL,
	ai_move,	0,	widow2_finaldeath
};
mmove_t widow2_move_really_dead = {FRAME_dthsrh16, FRAME_dthsrh22, widow2_frames_really_dead, NULL};

void widow2_start_searching (edict_t *self)
{
	self->count = 0;
}

void widow2_keep_searching (edict_t *self)
{
	if (self->count <= 2)
	{
		self->monsterinfo.currentmove = &widow2_move_dead;
		self->s.frame = FRAME_dthsrh01;
		self->count++;
		return;
	}

	self->monsterinfo.currentmove = &widow2_move_really_dead;
}

void widow2_finaldeath (edict_t *self)
{
	VectorSet (self->mins, -70, -70, 0);
	VectorSet (self->maxs, 70, 70, 80);
	self->movetype = MOVETYPE_TOSS;
//	self->svflags |= SVF_DEADMONSTER;
	self->takedamage = DAMAGE_YES;
	self->nextthink = 0;
	gi.linkentity (self);
}

void widow2_stand (edict_t *self)
{
//	gi.dprintf ("widow2 stand\n");
	self->monsterinfo.currentmove = &widow2_move_stand;
}

void widow2_run (edict_t *self)
{

//	gi.dprintf ("widow2 run - %2.2f - %s \n", level.time, self->enemy->classname);
	self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;

	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
		self->monsterinfo.currentmove = &widow2_move_stand;
	else
		self->monsterinfo.currentmove = &widow2_move_run;
}

void widow2_walk (edict_t *self)
{
	self->monsterinfo.currentmove = &widow2_move_walk;
}

void widow2_melee (edict_t *self)
{
	self->monsterinfo.currentmove = &widow2_move_tongs;
}

void widow2_attack (edict_t *self)
{
	float	range, luck;
	qboolean blocked = false;

	if (self->monsterinfo.aiflags & AI_BLOCKED)
	{
		blocked = true;
		self->monsterinfo.aiflags &= ~AI_BLOCKED;
	}

//	gi.dprintf ("widow2 attack\n");
	
	if (!self->enemy)
		return;

	if (self->bad_area)
	{
		if ((qrandom() < 0.75) || (level.time < self->monsterinfo.attack_finished))
			self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
		else
		{
			self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
		}
		return;
	}

	WidowCalcSlots(self);

	// if we can't see the target, spawn stuff
	if ((self->monsterinfo.attack_state == AS_BLIND) && (SELF_SLOTS_LEFT >= 2))
	{
		self->monsterinfo.currentmove = &widow2_move_spawn;
		return;
	}

	// accept bias towards spawning
	if (blocked  && (SELF_SLOTS_LEFT >= 2))
	{
		self->monsterinfo.currentmove = &widow2_move_spawn;
		return;
	}

	range = realrange (self, self->enemy);

	if (range < 600)
	{
		luck = qrandom();
		if (SELF_SLOTS_LEFT >= 2)
		{
			if (luck <= 0.40)
				self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
			else if ((luck <= 0.7) && !(level.time < self->monsterinfo.attack_finished))
			{
//				gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
				self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
			}
			else
				self->monsterinfo.currentmove = &widow2_move_spawn;
		}
		else
		{
			if ((luck <= 0.50) || (level.time < self->monsterinfo.attack_finished))
				self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
			else
			{
//				gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
				self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
			}
		}
	}
	else
	{
		luck = qrandom();
		if (SELF_SLOTS_LEFT >= 2)
		{
			if (luck < 0.3)
				self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
			else if ((luck < 0.65) || (level.time < self->monsterinfo.attack_finished))
				self->monsterinfo.currentmove = &widow2_move_spawn;
			else
			{
//				gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
				self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
			}
		}
		else
		{
			if ((luck < 0.45) || (level.time < self->monsterinfo.attack_finished))
				self->monsterinfo.currentmove = &widow2_move_attack_pre_beam;
			else
			{
//				gi.sound (self, CHAN_WEAPON, sound_disrupt, 1, ATTN_NORM, 0);
				self->monsterinfo.currentmove = &widow2_move_attack_disrupt;
			}
		}
	}
}

void widow2_attack_beam (edict_t *self)
{
	self->monsterinfo.currentmove = &widow2_move_attack_beam;
}

void widow2_reattack_beam (edict_t *self)
{
	self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;

	if ( infront(self, self->enemy) )
		if (qrandom() <= 0.5)
			if ((qrandom() < 0.7) || (SELF_SLOTS_LEFT < 2))
				self->monsterinfo.currentmove = &widow2_move_attack_beam;
			else
				self->monsterinfo.currentmove = &widow2_move_spawn;
		else
			self->monsterinfo.currentmove = &widow2_move_attack_post_beam;
	else
		self->monsterinfo.currentmove = &widow2_move_attack_post_beam;
}



void widow2_pain (edict_t *self, edict_t *, float, int damage)
{
	if (self->health < (self->max_health / 2))
		self->s.skinnum = 1;

	if (skill->value == 3)
		return;		// no pain anims in nightmare

//	gi.dprintf ("widow2 pain\n");
	if (level.time < self->pain_debounce_time)
		return;

	self->pain_debounce_time = level.time + 5;

	if (damage < 15)
	{
		gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0);
	}
	else if (damage < 75)
	{
		gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0);
		if ((skill->value < 3) && (qrandom() < (0.6 - (0.2*((float)skill->value)))))
		{
			self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
			self->monsterinfo.currentmove = &widow2_move_pain;
		}
	}
	else 
	{
		gi.sound (self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0);
		if ((skill->value < 3) && (qrandom() < (0.75 - (0.1*((float)skill->value)))))
		{
			self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
			self->monsterinfo.currentmove = &widow2_move_pain;
		}
	}
}

void widow2_dead (edict_t *)
{
}

void KillChildren (edict_t *self)
{
	edict_t *ent;
	int		field;

	ent = NULL;
	field = FOFS(classname);
	while (1)
	{
		ent = G_Find (ent, field, "monster_stalker");
		if(!ent)
			return;
		
		// FIXME - may need to stagger
		if ((ent->inuse) && (ent->health > 0))
			T_Damage (ent, self, self, vec3_origin, self->enemy->s.origin, vec3_origin, (ent->health + 1), 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN);
	}
}

void widow2_die (edict_t *self, edict_t *, edict_t *, int damage, vec3_t)
{
	int n;
	int	clipped;

// check for gib
	if (self->health <= self->gib_health)
	{
		clipped = qmin(damage, 100);

		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
		for (n= 0; n < 2; n++)
			ThrowWidowGibLoc (self, "models/objects/gibs/bone/tris.md2", clipped, GIB_ORGANIC, NULL, false);
		for (n= 0; n < 3; n++)
			ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", clipped, GIB_ORGANIC, NULL, false);
		for (n= 0; n < 3; n++)
		{
			ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib1/tris.md2", clipped, GIB_METALLIC, NULL,
				0, false);
			ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib2/tris.md2", clipped, GIB_METALLIC, NULL, 
				gi.soundindex ("misc/fhit3.wav"), false);
		}
		for (n= 0; n < 2; n++)
		{
			ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib3/tris.md2", clipped, GIB_METALLIC, NULL, 
				0, false);
			ThrowWidowGibSized (self, "models/monsters/blackwidow/gib3/tris.md2", clipped, GIB_METALLIC, NULL, 
				0, false);
		}
		ThrowGib (self, "models/objects/gibs/chest/tris.md2", clipped, GIB_ORGANIC);
		ThrowHead (self, "models/objects/gibs/head2/tris.md2", clipped, GIB_ORGANIC);
		self->deadflag = DEAD_DEAD;
		return;
	}

	if (self->deadflag == DEAD_DEAD)
		return;

	gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
	self->deadflag = DEAD_DEAD;
	self->takedamage = DAMAGE_NO;
	self->count = 0;
	KillChildren (self);
	self->monsterinfo.quad_framenum = 0;
	self->monsterinfo.double_framenum = 0;
	self->monsterinfo.invincible_framenum = 0;
	self->monsterinfo.currentmove = &widow2_move_death;
}

qboolean Widow2_CheckAttack (edict_t *self)
{
	vec3_t		spot1, spot2;
	vec3_t		temp;
	float		chance = 0.0;
	trace_t		tr;
	int			enemy_range;
	float		enemy_yaw;
	float		real_enemy_range;
	vec3_t		f, r, u;

	if (!self->enemy)
		return false;

	WidowPowerups(self);

	if ((qrandom() < 0.8) && (SELF_SLOTS_LEFT >= 2) && (realrange(self, self->enemy) > 150))
	{
		self->monsterinfo.aiflags |= AI_BLOCKED;
		self->monsterinfo.attack_state = AS_MISSILE;
		return true;
	}

	if (self->enemy->health > 0)
	{
	// see if any entities are in the way of the shot
		VectorCopy (self->s.origin, spot1);
		spot1[2] += self->viewheight;
		VectorCopy (self->enemy->s.origin, spot2);
		spot2[2] += self->enemy->viewheight;

		tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA);

		// do we have a clear shot?
		if (tr.ent != self->enemy)
		{	
			// go ahead and spawn stuff if we're mad a a client
			if (self->enemy->client && SELF_SLOTS_LEFT >= 2)
			{
				self->monsterinfo.attack_state = AS_BLIND;
				return true;
			}
				
			// PGM - we want them to go ahead and shoot at info_notnulls if they can.
			if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0)		//PGM
				return false;
		}
	}

	enemy_range = range(self, self->enemy);
	VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
	enemy_yaw = vectoyaw2(temp);

	self->ideal_yaw = enemy_yaw;

	// melee attack
	if (self->timestamp < level.time)
	{
		real_enemy_range = realrange (self, self->enemy);
		if (real_enemy_range < 300)
		{
			AngleVectors (self->s.angles, f, r, u);
			G_ProjectSource2 (self->s.origin, offsets[0], f, r, u, spot1);
			VectorCopy (self->enemy->s.origin, spot2);
			if (widow2_tongue_attack_ok(spot1, spot2, 256))
			{
				// melee attack ok

				// be nice in easy mode
				if (skill->value == 0 && (rand()&3) )
					return false;

				if (self->monsterinfo.melee)
					self->monsterinfo.attack_state = AS_MELEE;
				else
					self->monsterinfo.attack_state = AS_MISSILE;
				return true;
			}
		}
	}
	
	if (level.time < self->monsterinfo.attack_finished)
		return false;
		
	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
	{
		chance = 0.4;
	}
	else if (enemy_range == RANGE_NEAR)
	{
		chance = 0.8;
	}
	else if (enemy_range == RANGE_MID)
	{
		chance = 0.8;
	}
	else if (enemy_range == RANGE_FAR)
	{
		chance = 0.5;
	}

	// PGM - go ahead and shoot every time if it's a info_notnull
	if ((qrandom () < chance) || (self->enemy->solid == SOLID_NOT))
	{
		self->monsterinfo.attack_state = AS_MISSILE;
//		self->monsterinfo.attack_finished = level.time + 1.0 + 2*random();
		return true;
	}

	return false;
}

void Widow2Precache (void)
{
	// cache in all of the stalker stuff, widow stuff, spawngro stuff, gibs
	gi.soundindex ("parasite/parpain1.wav");	
	gi.soundindex ("parasite/parpain2.wav");	
	gi.soundindex ("parasite/pardeth1.wav");	
	gi.soundindex ("parasite/paratck1.wav");
	gi.soundindex ("parasite/parsght1.wav");
	gi.soundindex ("infantry/melee2.wav");
	gi.soundindex ("misc/fhit3.wav");

	gi.soundindex ("tank/tnkatck3.wav");
	gi.soundindex ("weapons/disrupt.wav");
	gi.soundindex ("weapons/disint2.wav");

	gi.modelindex ("models/monsters/stalker/tris.md2");
	gi.modelindex ("models/items/spawngro2/tris.md2");
	gi.modelindex ("models/objects/gibs/sm_metal/tris.md2");
	gi.modelindex ("models/proj/laser2/tris.md2");
	gi.modelindex ("models/proj/disintegrator/tris.md2");

	gi.modelindex ("models/monsters/blackwidow/gib1/tris.md2");
	gi.modelindex ("models/monsters/blackwidow/gib2/tris.md2");
	gi.modelindex ("models/monsters/blackwidow/gib3/tris.md2");
	gi.modelindex ("models/monsters/blackwidow/gib4/tris.md2");
	gi.modelindex ("models/monsters/blackwidow2/gib1/tris.md2");
	gi.modelindex ("models/monsters/blackwidow2/gib2/tris.md2");
	gi.modelindex ("models/monsters/blackwidow2/gib3/tris.md2");
	gi.modelindex ("models/monsters/blackwidow2/gib4/tris.md2");
}

/*QUAKED monster_widow2 (1 .5 0) (-70 -70 0) (70 70 144) Ambush Trigger_Spawn Sight
*/
void SP_monster_widow2 (edict_t *self)
{
	if (deathmatch->value)
	{
		G_FreeEdict (self);
		return;
	}

	sound_pain1 = gi.soundindex ("widow/bw2pain1.wav");
	sound_pain2 = gi.soundindex ("widow/bw2pain2.wav");
	sound_pain3 = gi.soundindex ("widow/bw2pain3.wav");
	sound_death = gi.soundindex ("widow/death.wav");
	sound_search1 = gi.soundindex ("bosshovr/bhvunqv1.wav");
//	sound_disrupt = gi.soundindex ("gladiator/railgun.wav");
	sound_tentacles_retract = gi.soundindex ("brain/brnatck3.wav");

//	self->s.sound = gi.soundindex ("bosshovr/bhvengn1.wav");

	self->movetype = MOVETYPE_STEP;
	self->solid = SOLID_BBOX;
	self->s.modelindex = gi.modelindex ("models/monsters/blackwidow2/tris.md2");
	VectorSet (self->mins, -70, -70, 0);
	VectorSet (self->maxs, 70, 70, 144);

	self->health = 2000 + 800 + 1000*(skill->value);
	if (coop->value)
		self->health += 500*(skill->value);
//	self->health = 1;
	self->gib_health = -900;
	self->mass = 2500;

/*	if (skill->value == 2)
	{
		self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
		self->monsterinfo.power_armor_power = 500;
	}
	else */if (skill->value == 3)
	{
		self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
		self->monsterinfo.power_armor_power = 750;
	}

	self->yaw_speed = 30;
	
	self->flags |= FL_IMMUNE_LASER;
	self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;

	self->pain = widow2_pain;
	self->die = widow2_die;

	self->monsterinfo.melee = widow2_melee;
	self->monsterinfo.stand = widow2_stand;
	self->monsterinfo.walk = widow2_walk;
	self->monsterinfo.run = widow2_run;
	self->monsterinfo.attack = widow2_attack;
	self->monsterinfo.search = widow2_search;
	self->monsterinfo.checkattack = Widow2_CheckAttack;
	gi.linkentity (self);

	self->monsterinfo.currentmove = &widow2_move_stand;	
	self->monsterinfo.scale = MODEL_SCALE;

	Widow2Precache();
	WidowCalcSlots(self);
	walkmonster_start (self);
}

//
// Death sequence stuff
//

void WidowVelocityForDamage (int damage, vec3_t v)
{
	v[0] = damage * crandom();
	v[1] = damage * crandom();
	v[2] = damage * crandom() + 200.0;
}

void widow_gib_touch (edict_t *self, edict_t *, cplane_t *, csurface_t *)
{

	self->solid = SOLID_NOT;
	self->touch = NULL;
	self->s.angles[PITCH] = 0;
	self->s.angles[ROLL] = 0;
	VectorClear (self->avelocity);

	if (self->plat2flags)
		gi.sound (self, CHAN_VOICE, self->plat2flags, 1, ATTN_NORM, 0);
/*
	if (plane)
	{
		if (plane->normal[2] < -0.8)
		{
			gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
		}
		
		//vectoangles (plane->normal, normal_angles);
		//AngleVectors (normal_angles, NULL, right, NULL);
		//vectoangles (right, self->s.angles);
		//VectorClear (self->avelocity);
	}
*/
}

void ThrowWidowGib (edict_t *self, char *gibname, int damage, int type)
{
	ThrowWidowGibReal (self, gibname, damage, type, NULL, false, 0, true);
}

void ThrowWidowGibLoc (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean fade)
{
	ThrowWidowGibReal (self, gibname, damage, type, startpos, false, 0, fade);
}

void ThrowWidowGibSized (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, int hitsound, qboolean fade)
{
	ThrowWidowGibReal (self, gibname, damage, type, startpos, true, hitsound, fade);
}

void ThrowWidowGibReal (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean sized, int hitsound, qboolean fade)
{
	edict_t *gib;
	vec3_t	vd;
	vec3_t	origin;
	vec3_t	size;
	float	vscale;

	if (!gibname)
		return;

	gib = G_Spawn();

	if (startpos)
		VectorCopy (startpos, gib->s.origin);
	else
	{
		VectorScale (self->size, 0.5, size);
		VectorAdd (self->absmin, size, origin);
		gib->s.origin[0] = origin[0] + crandom() * size[0];
		gib->s.origin[1] = origin[1] + crandom() * size[1];
		gib->s.origin[2] = origin[2] + crandom() * size[2];
	}

	gib->solid = SOLID_NOT;
	gib->s.effects |= EF_GIB;
	gib->flags |= FL_NO_KNOCKBACK;
	gib->takedamage = DAMAGE_YES;
	gib->die = gib_die;
	gib->s.renderfx |= RF_IR_VISIBLE;

	if (fade)
	{
		gib->think = G_FreeEdict;
		// sized gibs last longer
		if (sized)
			gib->nextthink = level.time + 20 + qrandom()*15;
		else
			gib->nextthink = level.time + 5 + qrandom()*10;
	}
	else
	{
		gib->think = G_FreeEdict;
		// sized gibs last longer
		if (sized)
			gib->nextthink = level.time + 60 + qrandom()*15;
		else
			gib->nextthink = level.time + 25 + qrandom()*10;
	}

	if (type == GIB_ORGANIC)
	{
		gib->movetype = MOVETYPE_TOSS;
		gib->touch = gib_touch;
		vscale = 0.5;
	}
	else
	{
		gib->movetype = MOVETYPE_BOUNCE;
		vscale = 1.0;
	}

	WidowVelocityForDamage (damage, vd);
	VectorMA (self->velocity, vscale, vd, gib->velocity);
	ClipGibVelocity (gib);

	gi.setmodel (gib, gibname);

	if (sized)
	{
		gib->plat2flags = hitsound;
		gib->solid = SOLID_BBOX;
		gib->avelocity[0] = qrandom()*400;
		gib->avelocity[1] = qrandom()*400;
		gib->avelocity[2] = qrandom()*200;
		if (gib->velocity[2] < 0)
			gib->velocity[2] *= -1;
		gib->velocity[0] *= 2;
		gib->velocity[1] *= 2;
		ClipGibVelocity (gib);
		gib->velocity[2] = qmax((350 + (qrandom()*100.0)), gib->velocity[2]);
		gib->gravity = 0.25;
		gib->touch = widow_gib_touch;
		gib->owner = self;
		if (gib->s.modelindex == gi.modelindex ("models/monsters/blackwidow2/gib2/tris.md2"))
		{
			VectorSet (gib->mins, -10, -10, 0);
			VectorSet (gib->maxs, 10, 10, 10);
		}
		else
		{
			VectorSet (gib->mins, -5, -5, 0);
			VectorSet (gib->maxs, 5, 5, 5);
		}
	}
	else
	{
		gib->velocity[0] *= 2;
		gib->velocity[1] *= 2;
		gib->avelocity[0] = qrandom()*600;
		gib->avelocity[1] = qrandom()*600;
		gib->avelocity[2] = qrandom()*600;
	}

//	gib->think = G_FreeEdict;
//	gib->nextthink = level.time + 10 + qrandom()*10;

	gi.linkentity (gib);
}

void BloodFountain (edict_t */*self*/, int /*number*/, vec3_t /*startpos*/, int /*damage*/)
{
	return;
/*
	int n;
	vec3_t	vd;
	vec3_t	origin, size, velocity;

	for (n= 0; n < number; n++)
		if (startpos)
			VectorCopy (startpos, origin);
		else
		{
			VectorScale (self->size, 0.5, size);
			VectorAdd (self->absmin, size, origin);
			origin[0] = origin[0] + crandom() * size[0];
			origin[1] = origin[1] + crandom() * size[1];
			origin[2] = origin[2] + crandom() * size[2];
		}

		WidowVelocityForDamage (damage, vd);
		VectorMA (self->velocity, 1.0, vd, velocity);
		velocity[0] *= 2;
		velocity[1] *= 2;

//		gi.WriteByte (svc_temp_entity);
//		gi.WriteByte (TE_BLOOD_FOUNTAIN);
//		gi.WritePosition (origin);
//		gi.WritePosition (velocity);
//		gi.WriteShort (50);
//		gi.multicast (self->s.origin, MULTICAST_ALL);
	}
*/
}

void ThrowSmallStuff (edict_t *self, vec3_t point)
{
	int n;

	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, point, false);
	ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, point, false);
	ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, point, false);

}

void ThrowMoreStuff (edict_t *self, vec3_t point)
{
	int n;

	if (coop && coop->value)
	{
		ThrowSmallStuff (self, point);
		return;
	}

	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, point, false);
	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, point, false);
	for (n= 0; n < 3; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, point, false);

}

void WidowExplode (edict_t *self)
{
	vec3_t	org;
	int		n;

	self->think = WidowExplode;
//	gi.dprintf ("count = %d\n");

//redo:
	VectorCopy (self->s.origin, org);
	org[2] += 24 + (rand()&15);
	if (self->count < 8)
		org[2] += 24 + (rand()&31);
	switch (self->count)
	{
	case 0:
		org[0] -= 24;
		org[1] -= 24;
		break;
	case 1:
		org[0] += 24;
		org[1] += 24;
		ThrowSmallStuff(self, org);
		break;
	case 2:
		org[0] += 24;
		org[1] -= 24;
		break;
	case 3:
		org[0] -= 24;
		org[1] += 24;
		ThrowMoreStuff(self, org);
		break;
	case 4:
		org[0] -= 48;
		org[1] -= 48;
		break;
	case 5:
		org[0] += 48;
		org[1] += 48;
		ThrowArm1 (self);
		break;
	case 6:
		org[0] -= 48;
		org[1] += 48;
		ThrowArm2 (self);
		break;
	case 7:
		org[0] += 48;
		org[1] -= 48;
		ThrowSmallStuff(self, org);
		break;
	case 8:
		org[0] += 18;
		org[1] += 18;
		org[2] = self->s.origin[2] + 48;
		ThrowMoreStuff(self, org);
		break;
	case 9:
		org[0] -= 18;
		org[1] += 18;
		org[2] = self->s.origin[2] + 48;
		break;
	case 10:
		org[0] += 18;
		org[1] -= 18;
		org[2] = self->s.origin[2] + 48;
		break;
	case 11:
		org[0] -= 18;
		org[1] -= 18;
		org[2] = self->s.origin[2] + 48;
		break;
	case 12:
		self->s.sound = 0;
		for (n= 0; n < 1; n++)
			ThrowWidowGib (self, "models/objects/gibs/sm_meat/tris.md2", 400, GIB_ORGANIC);
		for (n= 0; n < 2; n++)
			ThrowWidowGib (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC);
		for (n= 0; n < 2; n++)
			ThrowWidowGib (self, "models/objects/gibs/sm_metal/tris.md2", 400, GIB_METALLIC);
//		ThrowGib (self, "models/objects/gibs/chest/tris.md2", 1000, GIB_ORGANIC);
//		ThrowHead (self, "models/objects/gibs/gear/tris.md2", 1000, GIB_METALLIC);
		self->deadflag = DEAD_DEAD;
		self->think = monster_think;
		self->nextthink = level.time + 0.1;
		self->monsterinfo.currentmove = &widow2_move_dead;
		return;
	}

	self->count++;
	if (self->count >=9 && self->count <=12)
	{
		gi.WriteByte (svc_temp_entity);
		gi.WriteByte (TE_EXPLOSION1_BIG);
		gi.WritePosition (org);
		gi.multicast (self->s.origin, MULTICAST_ALL);
//		goto redo;
	} 
	else
	{
		// else
		gi.WriteByte (svc_temp_entity);
		if (self->count %2)
			gi.WriteByte (TE_EXPLOSION1);
		else
			gi.WriteByte (TE_EXPLOSION1_NP);
		gi.WritePosition (org);
		gi.multicast (self->s.origin, MULTICAST_ALL);
	}

	self->nextthink = level.time + 0.1;
}

void WidowExplosion1 (edict_t *self)
{
	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset = {23.74, -37.67, 76.96};

//	gi.dprintf ("1\n");
	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);
	
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
}

void WidowExplosion2 (edict_t *self)
{
	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset = {-20.49, 36.92, 73.52};

//	gi.dprintf ("2\n");

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);
	
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
}

void WidowExplosion3 (edict_t *self)
{
	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset = {2.11, 0.05, 92.20};

//	gi.dprintf ("3\n");

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);
	
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
}

void WidowExplosion4 (edict_t *self)
{
	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset = {-28.04, -35.57, -77.56};

//	gi.dprintf ("4\n");

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);
	
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
}

void WidowExplosion5 (edict_t *self)
{
	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset = {-20.11, -1.11, 40.76};

//	gi.dprintf ("5\n");

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);
	
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
}

void WidowExplosion6 (edict_t *self)
{
	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset = {-20.11, -1.11, 40.76};

	//gi.dprintf ("6\n");

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);
	
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
}

void WidowExplosion7 (edict_t *self)
{
	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset = {-20.11, -1.11, 40.76};

	//gi.dprintf ("7\n");

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);
	
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	for (n= 0; n < 1; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 300, GIB_METALLIC, startpoint, false);
}

void WidowExplosionLeg (edict_t *self)
{
//	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset1 = {-31.89, -47.86, 67.02};
	vec3_t	offset2 = {-44.9, -82.14, 54.72};

	//gi.dprintf ("Leg\n");

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1_BIG);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);

	ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib2/tris.md2", 200, GIB_METALLIC, startpoint, 
		gi.soundindex ("misc/fhit3.wav"), false);
	ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);

	G_ProjectSource2 (self->s.origin, offset2, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);

	ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib1/tris.md2", 300, GIB_METALLIC, startpoint,
		gi.soundindex ("misc/fhit3.wav"), false);
	ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
	ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
}

void ThrowArm1 (edict_t *self)
{
	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset1 = {65.76, 17.52, 7.56};

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);

	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1_BIG);
	gi.WritePosition (startpoint);
	gi.multicast (self->s.origin, MULTICAST_ALL);

	for (n= 0; n < 2; n++)
		ThrowWidowGibLoc (self, "models/objects/gibs/sm_metal/tris.md2", 100, GIB_METALLIC, startpoint, false);
}

void ThrowArm2 (edict_t *self)
{
//	int		n;
	vec3_t	f,r,u, startpoint;
	vec3_t	offset1 = {65.76, 17.52, 7.56};

	AngleVectors (self->s.angles, f, r, u);
	G_ProjectSource2 (self->s.origin, offset1, f, r, u, startpoint);

	ThrowWidowGibSized (self, "models/monsters/blackwidow2/gib4/tris.md2", 200, GIB_METALLIC, startpoint, 
		gi.soundindex ("misc/fhit3.wav"), false);
	ThrowWidowGibLoc (self, "models/objects/gibs/sm_meat/tris.md2", 300, GIB_ORGANIC, startpoint, false);
}