Exemple #1
0
void
sphere_fire(edict_t *self, edict_t *enemy)
{
	vec3_t dest;
	vec3_t dir;

	if (!self || !enemy)
	{
		return;
	}

	if ((level.time >= self->wait) || !enemy)
	{
		sphere_think_explode(self);
		return;
	}

	VectorCopy(enemy->s.origin, dest);
	self->s.effects |= EF_ROCKET;

	VectorSubtract(dest, self->s.origin, dir);
	VectorNormalize(dir);
	vectoangles2(dir, self->s.angles);
	VectorScale(dir, 1000, self->velocity);

	self->touch = vengeance_touch;
	self->think = sphere_think_explode;
	self->nextthink = self->wait;
}
Exemple #2
0
void
fire_doppleganger(edict_t *ent, vec3_t start, vec3_t aimdir)
{
	edict_t *base;
	edict_t *body;
	vec3_t dir;
	vec3_t forward, right, up;
	int number;

	if (!ent)
	{
		return;
	}

	vectoangles2(aimdir, dir);
	AngleVectors(dir, forward, right, up);

	base = G_Spawn();
	VectorCopy(start, base->s.origin);
	VectorCopy(dir, base->s.angles);
	VectorClear(base->velocity);
	VectorClear(base->avelocity);
	base->movetype = MOVETYPE_TOSS;
	base->solid = SOLID_BBOX;
	base->s.renderfx |= RF_IR_VISIBLE;
	base->s.angles[PITCH] = 0;
	VectorSet(base->mins, -16, -16, -24);
	VectorSet(base->maxs, 16, 16, 32);
	base->s.modelindex = 0;
	base->teammaster = ent;
	base->svflags |= SVF_DAMAGEABLE;
	base->takedamage = DAMAGE_AIM;
	base->health = 30;
	base->pain = doppleganger_pain;
	base->die = doppleganger_die;

	base->nextthink = level.time + 30;
	base->think = doppleganger_timeout;
	base->classname = "doppleganger";

	gi.linkentity(base);

	body = G_Spawn();
	number = body->s.number;
	body->s = ent->s;
	body->s.sound = 0;
	body->s.event = 0;
	body->s.number = number;
	body->yaw_speed = 30;
	body->ideal_yaw = 0;
	VectorCopy(start, body->s.origin);
	body->s.origin[2] += 8;
	body->think = body_think;
	body->nextthink = level.time + FRAMETIME;
	gi.linkentity(body);

	base->teamchain = body;
	body->teammaster = base;
}
Exemple #3
0
/*
===============
CL_TrackerTrail
===============
*/
void CL_TrackerTrail (vec3_t start, vec3_t end, int particleColor)
{
	vec3_t		move;
	vec3_t		vec;
	vec3_t		forward,right,up,angle_dir;
	float		len;
	int			j;
	cparticle_t	*p;
	int			dec;
	float		dist;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	VectorCopy(vec, forward);
	vectoangles2 (forward, angle_dir);
	AngleVectors (angle_dir, forward, right, up);

	dec = 3;
	VectorScale (vec, 3, vec);

	// FIXME: this is a really silly way to have a loop
	while (len > 0)
	{
		len -= dec;

		if (!free_particles)
			return;
		p = free_particles;
		free_particles = p->next;
		p->next = active_particles;
		active_particles = p;
		VectorClear (p->accel);
		
		p->time = cl.time;

		p->alpha = 1.0;
		p->alphavel = -2.0;
#ifndef QMAX
		p->color = particleColor;
#endif
		dist = DotProduct(move, forward);
		VectorMA(move, 8 * cos(dist), up, p->org);
		for (j=0 ; j<3 ; j++)
		{
//			p->org[j] = move[j] + crand();
			p->vel[j] = 0;
			p->accel[j] = 0;
		}
		p->vel[2] = 5;

		VectorAdd (move, vec, move);
	}
}
/*
=====================
hintpath_start

Makes a monster go towards a hintpath spot,
and clears inhibiting AI flags.
=====================
*/
void hintpath_start (edict_t *monster, edict_t *spot)
{
	vec3_t	goDir, goAngles;

	VectorSubtract(spot->s.origin, monster->s.origin, goDir);
	vectoangles2 (goDir, goAngles);

	monster->monsterinfo.aiflags &= ~(AI_SOUND_TARGET | AI_PURSUIT_LAST_SEEN | AI_PURSUE_NEXT | AI_PURSUE_TEMP);
	monster->monsterinfo.aiflags |= AI_HINT_PATH;
	monster->monsterinfo.pausetime = 0;
	monster->ideal_yaw = goAngles[YAW];
	monster->movetarget = monster->goalentity = spot;
	// get moovin!
	monster->monsterinfo.search_time = level.time;
	monster->monsterinfo.run (monster);
}
Exemple #5
0
void WidowBlaster (edict_t *self)
{
	vec3_t	forward, right, target, vec, targ_angles;
	vec3_t	start;
	int		flashnum;
	int		effect;

	if (!self->enemy)
		return;

	shotsfired++;
	if (!(shotsfired % 4))
		effect = EF_BLASTER;
	else
		effect = 0;

	AngleVectors (self->s.angles, forward, right, NULL);
	if ((self->s.frame >= FRAME_spawn05) && (self->s.frame <= FRAME_spawn13))
	{
		// sweep
		flashnum = MZ2_WIDOW_BLASTER_SWEEP1 + self->s.frame - FRAME_spawn05;
		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_WIDOW_BLASTER_SWEEP1];

		AngleVectors (vec, forward, NULL, NULL);
		monster_fire_blaster2 (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
	}
	else if ((self->s.frame >= FRAME_fired02a) && (self->s.frame <= FRAME_fired20))
	{
		vec3_t angles;
		float aim_angle, target_angle;
		float error;

		self->monsterinfo.aiflags |= AI_MANUAL_STEERING;

		self->monsterinfo.nextframe = WidowTorso (self);

		if (!self->monsterinfo.nextframe)
			self->monsterinfo.nextframe = self->s.frame;

		if (self->s.frame == FRAME_fired02a)
			flashnum = MZ2_WIDOW_BLASTER_0;
		else
			flashnum = MZ2_WIDOW_BLASTER_100 + self->s.frame - FRAME_fired03;

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

		PredictAim (self->enemy, start, 1000, true, ((random()*0.1)-0.05), forward, NULL);

		// clamp it to within 10 degrees of the aiming angle (where she's facing)
		vectoangles2 (forward, angles);
		// give me 100 -> -70
		aim_angle = 100 - (10*(flashnum-MZ2_WIDOW_BLASTER_100));
		if (aim_angle <= 0)
			aim_angle += 360;
		target_angle = self->s.angles[YAW] - angles[YAW];
		if (target_angle <= 0)
			target_angle += 360;

		error = aim_angle - target_angle;

		// positive error is to entity's left, aka positive direction in engine
		// unfortunately, I decided that for the aim_angle, positive was right.  *sigh*
		if (error > VARIANCE)
		{
			angles[YAW] = (self->s.angles[YAW] - aim_angle) + VARIANCE;
			AngleVectors (angles, forward, NULL, NULL);
		}
		else if (error < -VARIANCE)
		{
			angles[YAW] = (self->s.angles[YAW] - aim_angle) - VARIANCE;
			AngleVectors (angles, forward, NULL, NULL);
		}

		monster_fire_blaster2 (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
	}
	else if ((self->s.frame >= FRAME_run01) && (self->s.frame <= FRAME_run08))
	{
		flashnum = MZ2_WIDOW_RUN_1 + self->s.frame - FRAME_run01;
		G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
		
		VectorSubtract (self->enemy->s.origin, start, target);
		target[2] += self->enemy->viewheight;

		monster_fire_blaster2 (self, start, target, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
	}
}	
Exemple #6
0
// ====================
// ====================
int stalker_do_pounce(edict_t *self, vec3_t dest)
{
	vec3_t	forward, right;
	vec3_t	dist;
	vec_t	length;
	vec3_t	jumpAngles;
	vec3_t	jumpLZ;
	float	velocity = 400.1;
	trace_t	trace;
	int		preferHighJump;

	// don't pounce when we're on the ceiling
	if(STALKER_ON_CEILING(self))
		return false;

	if(!stalker_check_lz (self, self->enemy, dest))
		return false;

	VectorSubtract(dest, self->s.origin, dist);
	
	// make sure we're pointing in that direction 15deg margin of error.
	vectoangles2 (dist, jumpAngles);
	if(abs(jumpAngles[YAW] - self->s.angles[YAW]) > 45)
		return false;			// not facing the player...

	self->ideal_yaw = jumpAngles[YAW];
	M_ChangeYaw(self);

	length = VectorLength(dist);
	if(length > 450)
		return false;			// can't jump that far...

	VectorCopy(dest, jumpLZ);

	preferHighJump = 0;

	// if we're having to jump up a distance, jump a little too high to compensate.
	if(dist[2] >= 32.0)
	{
		preferHighJump = 1;
		jumpLZ[2] += 32;
	}

	trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, dest, self, MASK_MONSTERSOLID);
	if((trace.fraction < 1) && (trace.ent != self->enemy))
	{
//		gi.dprintf("prefer high jump angle\n");
		preferHighJump = 1; 
	}

	// find a valid angle/velocity combination
	while(velocity <= 800)
	{
		calcJumpAngle(self->s.origin, jumpLZ, velocity, jumpAngles);
		if((!_isnan(jumpAngles[0]))  || (!_isnan(jumpAngles[1])))
			break;
		
		velocity+=200;
	};

	if(!preferHighJump && (!_isnan(jumpAngles[0])) )
	{
		AngleVectors (self->s.angles, forward, right, NULL);
		VectorNormalize ( forward ) ;

		VectorScale( forward, velocity * cos(DEG2RAD(jumpAngles[0])), self->velocity);
		self->velocity[2] = velocity * sin(DEG2RAD(jumpAngles[0])) + (0.5 * sv_gravity->value * FRAMETIME);
//		gi.dprintf("  pouncing! %0.1f,%0.1f (%0.1f)  --> %0.1f, %0.1f, %0.1f\n", 
//				jumpAngles[0], jumpAngles[1], jumpAngles[0],
//				self->velocity[0], self->velocity[1], self->velocity[2]);
		return 1;
	}

	if(!_isnan(jumpAngles[1]))
	{
		AngleVectors (self->s.angles, forward, right, NULL);
		VectorNormalize ( forward ) ;

		VectorScale( forward, velocity * cos(DEG2RAD(jumpAngles[1])), self->velocity);
		self->velocity[2] = velocity * sin(DEG2RAD(jumpAngles[1])) + (0.5 * sv_gravity->value * FRAMETIME);
//		gi.dprintf("  pouncing! %0.1f,%0.1f (%0.1f)  --> %0.1f, %0.1f, %0.1f\n", 
//				jumpAngles[0], jumpAngles[1], jumpAngles[1],
//				self->velocity[0], self->velocity[1], self->velocity[2]);
		return 1;
	}

//	gi.dprintf("  nan\n");
	return 0;
}
Exemple #7
0
void CL_ParseTEnt (void)
{
	int32_t		type;
	vec3_t	pos, pos2, dir;
	explosion_t	*ex;
	int32_t		cnt;
	int32_t		color;
	int32_t		r, i;
	int32_t		ent;
	int32_t		magnitude;

	type = MSG_ReadByte (&net_message);

	switch (type)
	{
	case TE_BLOOD:			// bullet hitting flesh
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
	// Psychospaz's enhanced particle code
		CL_BloodHit (pos, dir);
		break;

	case TE_GUNSHOT:			// bullet hitting wall
	case TE_SPARKS:
	case TE_BULLET_SPARKS:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
	// Psychospaz's enhanced particle code
		if (type == TE_BULLET_SPARKS)
			VectorScale(dir, 2, dir);
		{
			vec3_t color = { 255, 125, 10 };
			CL_ParticleEffectSparks (pos, dir, color, (type == TE_GUNSHOT)? 5 : 10);
		}

		if (type != TE_SPARKS)
		{
			CL_GunSmokeEffect (pos, dir);
			if (type == TE_GUNSHOT || type == TE_BULLET_SPARKS)
				CL_ParticleBulletDecal(pos, dir, 2.5);
			// impact sound
			cnt = rand()&15;
			if (cnt == 1)
				S_StartSound (pos, 0, 0, clMedia.sfx_ric[0], 1, ATTN_NORM, 0);
			else if (cnt == 2)
				S_StartSound (pos, 0, 0, clMedia.sfx_ric[1], 1, ATTN_NORM, 0);
			else if (cnt == 3)
				S_StartSound (pos, 0, 0, clMedia.sfx_ric[2], 1, ATTN_NORM, 0);
		}
		break;
				
	case TE_SHOTGUN:			// bullet hitting wall
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
	// Psychospaz's enhanced particle code
		CL_GunSmokeEffect (pos, dir);
		CL_ParticleBulletDecal(pos, dir, 2.8);
		{
			vec3_t color = { 200, 100, 10 };
			CL_ParticleEffectSparks (pos, dir, color, 8);
		}
		break;

	case TE_SCREEN_SPARKS:
	case TE_SHIELD_SPARKS:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		if (type == TE_SCREEN_SPARKS)
			CL_ParticleEffect (pos, dir, 0xd0, 40);
		else
			CL_ParticleEffect (pos, dir, 0xb0, 40);
		//FIXME : replace or remove this sound
		S_StartSound (pos, 0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
		break;

	case TE_SPLASH:			// bullet hitting water
		cnt = MSG_ReadByte (&net_message);
		cnt = min(cnt, 40); // cap at 40
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		r = MSG_ReadByte (&net_message);
		if (r > 6)
			color = 0x00;
		else
			color = splash_color[r];
		// Psychospaz's enhanced particle code
		if (r == SPLASH_SPARKS)
		{
			CL_ParticleEffectSplashSpark (pos, dir, color, cnt);
			r = rand() & 3;
			if (r == 0)
				S_StartSound (pos, 0, 0, clMedia.sfx_spark[0], 1, ATTN_STATIC, 0);
			else if (r == 1)
				S_StartSound (pos, 0, 0, clMedia.sfx_spark[1], 1, ATTN_STATIC, 0);
			else
				S_StartSound (pos, 0, 0, clMedia.sfx_spark[2], 1, ATTN_STATIC, 0);
		} else {
			CL_ParticleEffectSplash (pos, dir, color, cnt);
		}
		break;

	case TE_LASER_SPARKS:
		cnt = MSG_ReadByte (&net_message);
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		color = MSG_ReadByte (&net_message);
		CL_ParticleEffect2 (pos, dir, color, cnt);
		break;

	case TE_RAILTRAIL:			// railgun effect
		MSG_ReadPos (&net_message, pos);
		MSG_ReadPos (&net_message, pos2);
		CL_RailTrail (pos, pos2, false);
		S_StartSound (pos2, 0, 0, clMedia.sfx_railg, 1, ATTN_NORM, 0);
		break;
	// 12/23/2001 - red railgun trail
	case TE_RAILTRAIL2:			// red railgun effect
		MSG_ReadPos (&net_message, pos);
		MSG_ReadPos (&net_message, pos2);
		CL_RailTrail (pos, pos2, true);
		S_StartSound (pos2, 0, 0, clMedia.sfx_railg, 1, ATTN_NORM, 0);
		break;

	case TE_EXPLOSION2:
	case TE_GRENADE_EXPLOSION:
	case TE_GRENADE_EXPLOSION_WATER:
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
		ex->start = cl.frame.servertime - 100;
		ex->light = 350;
		ex->lightcolor[0] = 1.0;
		ex->lightcolor[1] = 0.5;
		ex->lightcolor[2] = 0.5;
		ex->ent.model = clMedia.mod_explo;
		ex->frames = 19;
		ex->baseframe = 30;
		ex->ent.angles[1] = rand() % 360;
		CL_Explosion_Sparks (pos, 16, 128);
		if (type != TE_EXPLOSION2)
			CL_Explosion_Decal (pos, 50, particle_burnmark);
		if (type == TE_GRENADE_EXPLOSION_WATER)
			S_StartSound (pos, 0, 0, clMedia.sfx_watrexp, 1, ATTN_NORM, 0);
		else
			S_StartSound (pos, 0, 0, clMedia.sfx_grenexp, 1, ATTN_NORM, 0);
		break;

		// RAFAEL
	case TE_PLASMA_EXPLOSION:
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
		ex->start = cl.frame.servertime - 100;
		ex->light = 350;
		ex->lightcolor[0] = 1.0; 
		ex->lightcolor[1] = 0.5;
		ex->lightcolor[2] = 0.5;
		ex->ent.angles[1] = rand() % 360;
		ex->ent.model = clMedia.mod_explo;
		if (frand() < 0.5)
			ex->baseframe = 15;
		ex->frames = 15;
		CL_Explosion_Sparks (pos, 16, 128);
		CL_Explosion_Decal (pos, 50, particle_burnmark);
		if (cl_plasma_explo_sound->value)
			S_StartSound (pos, 0, 0, clMedia.sfx_plasexp, 1, ATTN_NORM, 0);
		else
			S_StartSound (pos, 0, 0, clMedia.sfx_rockexp, 1, ATTN_NORM, 0);
		break;

	case TE_EXPLOSION1_BIG:						// PMM
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
		ex->start = cl.frame.servertime - 100;
		ex->light = 350;
		ex->lightcolor[0] = 1.0;
		ex->lightcolor[1] = 0.5;
		ex->lightcolor[2] = 0.5;
		ex->ent.angles[1] = rand() % 360;
		ex->ent.model = clMedia.mod_explo_big;
		if (frand() < 0.5)
			ex->baseframe = 15;
		ex->frames = 15;
		CL_Explosion_Sparks (pos, 48, 128);
		S_StartSound (pos, 0, 0, clMedia.sfx_rockexp, 1, ATTN_NORM, 0);
		break;

	case TE_EXPLOSION1_NP:						// PMM
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
		ex->start = cl.frame.servertime - 100;
		ex->light = 350;
		ex->lightcolor[0] = 1.0;
		ex->lightcolor[1] = 0.5;
		ex->lightcolor[2] = 0.5;
		ex->ent.angles[1] = rand() % 360;
		ex->ent.model = clMedia.mod_explo;
		if (frand() < 0.5)
			ex->baseframe = 15;
		ex->frames = 15;
		CL_Explosion_Sparks (pos, 10, 128);
		S_StartSound (pos, 0, 0, clMedia.sfx_grenexp, 1, ATTN_NORM, 0);
		break;

	case TE_EXPLOSION1:
	case TE_PLAIN_EXPLOSION:
	case TE_ROCKET_EXPLOSION:
	case TE_ROCKET_EXPLOSION_WATER:
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
		ex->start = cl.frame.servertime - 100;
		ex->light = 350;
		ex->lightcolor[0] = 1.0;
		ex->lightcolor[1] = 0.5;
		ex->lightcolor[2] = 0.5;
		ex->ent.angles[1] = rand() % 360;
		ex->ent.model = clMedia.mod_explo;
		if (frand() < 0.5)
			ex->baseframe = 15;
		ex->frames = 15;
		CL_Explosion_Sparks (pos, 16, 128);
		if (type == TE_ROCKET_EXPLOSION || type == TE_ROCKET_EXPLOSION_WATER)
			CL_Explosion_Decal (pos, 50, particle_burnmark);
		if (type == TE_ROCKET_EXPLOSION_WATER)
			S_StartSound (pos, 0, 0, clMedia.sfx_watrexp, 1, ATTN_NORM, 0);
		else
			S_StartSound (pos, 0, 0, clMedia.sfx_rockexp, 1, ATTN_NORM, 0);
		break;

	case TE_BFG_EXPLOSION:
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags |= RF_FULLBRIGHT;
		ex->start = cl.frame.servertime - 100;
		ex->light = 350;
		ex->lightcolor[0] = 0.0;
		ex->lightcolor[1] = 1.0;
		ex->lightcolor[2] = 0.0;
		ex->ent.model = clMedia.mod_bfg_explo;
		ex->ent.flags |= RF_TRANSLUCENT | RF_TRANS_ADDITIVE;
		ex->ent.alpha = 0.30;
		ex->frames = 4;
		break;

	case TE_BFG_BIGEXPLOSION:
		MSG_ReadPos (&net_message, pos);
		CL_BFGExplosionParticles (pos);
		CL_Explosion_Decal (pos, 75, particle_bfgmark);
		break;

	case TE_BFG_LASER:
		CL_ParseLaser (0xd0d1d2d3);
		break;

	case TE_BUBBLETRAIL:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadPos (&net_message, pos2);
		CL_BubbleTrail (pos, pos2);
		break;

	case TE_PARASITE_ATTACK:
	case TE_MEDIC_CABLE_ATTACK:
		ent = CL_ParseBeam (clMedia.mod_parasite_segment);
		break;

	case TE_BOSSTPORT:			// boss teleporting to station
		MSG_ReadPos (&net_message, pos);
		CL_BigTeleportParticles (pos);
		S_StartSound (pos, 0, 0, S_RegisterSound ("misc/bigtele.wav"), 1, ATTN_NONE, 0);
		break;

	case TE_GRAPPLE_CABLE:
		ent = CL_ParseBeam2 (clMedia.mod_grapple_cable);
		break;

	// RAFAEL
	case TE_WELDING_SPARKS:
		cnt = MSG_ReadByte (&net_message);
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		color = MSG_ReadByte (&net_message);
	// Psychospaz's enhanced particle code
		{
			vec3_t sparkcolor = {color8red(color), color8green(color), color8blue(color)};
			CL_ParticleEffectSparks (pos, dir, sparkcolor, 40);
		}
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_flash;
		// note to self
		// we need a better no draw flag
		ex->ent.flags |= RF_BEAM;
		ex->start = cl.frame.servertime - 0.1;
		ex->light = 100 + (rand()%75);
		ex->lightcolor[0] = 1.0;
		ex->lightcolor[1] = 1.0;
		ex->lightcolor[2] = 0.3;
		ex->ent.model = clMedia.mod_flash;
		ex->frames = 2;
		break;

	case TE_GREENBLOOD:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
	// Psychospaz's enhanced particle code
		CL_GreenBloodHit (pos, dir);
		break;

	// RAFAEL
	case TE_TUNNEL_SPARKS:
		cnt = MSG_ReadByte (&net_message);
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		color = MSG_ReadByte (&net_message);
		CL_ParticleEffect3 (pos, dir, color, cnt);
		break;

//=============
//PGM
	// PMM -following code integrated for flechette (different color)
	case TE_BLASTER:			// blaster hitting wall
	case TE_BLASTER2:			// green blaster hitting wall
	case TE_BLUEHYPERBLASTER:	// blue blaster hitting wall
	case TE_REDBLASTER:			// red blaster hitting wall
	case TE_FLECHETTE:			// flechette hitting wall
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->ent.angles[0] = acos(dir[2])/M_PI*180;
		if (dir[0]) // PMM - fixed to correct for pitch of 0
			ex->ent.angles[1] = atan2(dir[1], dir[0])/M_PI*180;
		else if (dir[1] > 0)
			ex->ent.angles[1] = 90;
		else if (dir[1] < 0)
			ex->ent.angles[1] = 270;
		else
			ex->ent.angles[1] = 0;

		ex->type = ex_misc;
		ex->ent.flags |= RF_FULLBRIGHT|RF_TRANSLUCENT|RF_NOSHADOW; // no shadow on these

		if (type == TE_BLASTER2)
			ex->ent.skinnum = 1;
		else if (type == TE_REDBLASTER)
			ex->ent.skinnum = 3;
		else if (type == TE_FLECHETTE || type == TE_BLUEHYPERBLASTER)
			ex->ent.skinnum = 2;
		else // TE_BLASTER
			ex->ent.skinnum = 0;

		ex->start = cl.frame.servertime - 100;
		ex->light = 150;

		if (type == TE_BLASTER2) {
			ex->lightcolor[0] = 0.15;
			ex->lightcolor[1] = 1;
			ex->lightcolor[2] = 0.15;
		}
		else if (type == TE_BLUEHYPERBLASTER) {
			ex->lightcolor[0] = 0.19;
			ex->lightcolor[1] = 0.41;
			ex->lightcolor[2] = 0.75;
		}
		else if (type == TE_REDBLASTER) {
			ex->lightcolor[0] = 0.75;
			ex->lightcolor[1] = 0.41;
			ex->lightcolor[2] = 0.19;
		}
		else if (type == TE_FLECHETTE) {
			ex->lightcolor[0] = 0.39;
			ex->lightcolor[1] = 0.61;
			ex->lightcolor[2] = 0.75;
		}
		else { // TE_BLASTER	
			ex->lightcolor[0] = 1;
			ex->lightcolor[1] = 1;
			ex->lightcolor[2] = 0;
		}

		ex->ent.model = clMedia.mod_explode;
		ex->frames = 4;
		
		// Psychospaz's enhanced particle code	
		{
			int32_t		red, green, blue, numparts;
			float	partsize = 4;
			numparts = (32 / max(cl_particle_scale->value/2, 1));
			if (type == TE_BLASTER2) {
				CL_BlasterParticles (pos, dir, numparts, partsize, 50, 235, 50, -10, 0, -10);
				red=50; green=235; blue=50; }
			else if (type == TE_BLUEHYPERBLASTER) {
				CL_BlasterParticles (pos, dir, numparts, partsize, 50, 50, 235, -10, 0, -10);
				red=50; green=50; blue=235; }
			else if (type == TE_REDBLASTER) {
				CL_BlasterParticles (pos, dir, numparts, partsize, 235, 50, 50, 0, -90, -30);
				red=235; green=50; blue=50; }
			else if (type == TE_FLECHETTE) {
 				CL_BlasterParticles (pos, dir, numparts, partsize, 100, 100, 195, -10, 0, -10);
				red=100; green=100; blue=195; }
			else { // TE_BLASTER
				CL_BlasterParticles (pos, dir, numparts, partsize, 255, 150, 50, 0, -90, -30); // was 40
				red=255; green=150; blue=50; }
			CL_ParticleBlasterDecal(pos, dir, 10, red, green, blue);
		}
		S_StartSound (pos,  0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
		break;

	// shockwave impact effect
	case TE_SHOCKSPLASH:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		// Spawn 5 rings
		for (i = 0; i < 5; i++)
		{
			ex = CL_AllocExplosion ();
			VectorMA (pos, 16.0*(i+1), dir, ex->ent.origin);
			vectoangles2(dir, ex->ent.angles);
			//VectorCopy (dir, ex->ent.angles);
			ex->type = ex_poly2;
			ex->ent.flags |= RF_FULLBRIGHT|RF_TRANSLUCENT|RF_NOSHADOW;
			ex->start = cl.frame.servertime - 100;

			ex->light = 75;
			ex->lightcolor[0] = 0.39;
			ex->lightcolor[1] = 0.61;
			ex->lightcolor[2] = 0.75;

			ex->ent.model = clMedia.mod_shocksplash;
			ex->ent.alpha = 0.70;
			ex->baseframe = 4-i;
			ex->frames = 4+i;
		}
		S_StartSound (pos,  0, 0, clMedia.sfx_shockhit, 1, ATTN_NONE, 0);
		break;

	case TE_LIGHTNING:
		// Psychospaz's enhanced particle code
		ent = CL_ParseLightning ();
		S_StartSound (NULL, ent, CHAN_WEAPON, clMedia.sfx_lightning, 1, ATTN_NORM, 0);
		break;

	case TE_DEBUGTRAIL:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadPos (&net_message, pos2);
		CL_DebugTrail (pos, pos2);
		break;

	case TE_FLASHLIGHT:
		MSG_ReadPos(&net_message, pos);
		ent = MSG_ReadShort(&net_message);
		CL_Flashlight(ent, pos);
		break;

	case TE_FORCEWALL:
		MSG_ReadPos(&net_message, pos);
		MSG_ReadPos(&net_message, pos2);
		color = MSG_ReadByte (&net_message);
		CL_ForceWall(pos, pos2, color);
		break;

	case TE_HEATBEAM:
		ent = CL_ParsePlayerBeam (clMedia.mod_heatbeam);
		break;

	case TE_MONSTER_HEATBEAM:
		ent = CL_ParsePlayerBeam (clMedia.mod_monster_heatbeam);
		break;

	case TE_HEATBEAM_SPARKS:
		cnt = 50;
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		r = 8;
		magnitude = 60;
		CL_ParticleSteamEffect (pos, dir, 240, 240, 240, -20, -20, -20, cnt, magnitude);
		S_StartSound (pos,  0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
		break;
	
	case TE_HEATBEAM_STEAM:
		cnt = 20;
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		magnitude = 60;
		CL_ParticleSteamEffect (pos, dir, 255, 150, 50, 0, -90, -30, cnt, magnitude);
		CL_ParticlePlasmaBeamDecal (pos, dir, 10); // added burnmark
		S_StartSound (pos,  0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
		break;

	case TE_STEAM:
		CL_ParseSteam();
		break;

	case TE_BUBBLETRAIL2:
		cnt = 3; // was 8
		MSG_ReadPos (&net_message, pos);
		MSG_ReadPos (&net_message, pos2);
		CL_BubbleTrail2 (pos, pos2, cnt);
		S_StartSound (pos,  0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
		break;

	case TE_MOREBLOOD:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
	// Psychospaz's enhanced particle code
		CL_BloodHit (pos, dir);
		CL_BloodHit (pos, dir);
		break;

	case TE_CHAINFIST_SMOKE:
		dir[0]=0; dir[1]=0; dir[2]=1;
		MSG_ReadPos(&net_message, pos);
	// Psychospaz's enhanced particle code
		CL_ParticleSmokeEffect (pos, dir, 8);
		break;

	case TE_ELECTRIC_SPARKS:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
	// new blue electric sparks
		CL_ElectricParticles (pos, dir, 40);
		//FIXME : replace or remove this sound
		S_StartSound (pos, 0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
		break;

	case TE_TRACKER_EXPLOSION:
		MSG_ReadPos (&net_message, pos);
		CL_ColorFlash (pos, 0, 150, -1, -1, -1);
	//	CL_ColorExplosionParticles (pos, 0, 1);
		CL_Tracker_Explode (pos);
		CL_Explosion_Decal (pos, 14, particle_trackermark);
		S_StartSound (pos, 0, 0, clMedia.sfx_disrexp, 1, ATTN_NORM, 0);
		break;

	case TE_TELEPORT_EFFECT:
	case TE_DBALL_GOAL:
		MSG_ReadPos (&net_message, pos);
		CL_TeleportParticles (pos);
		break;

	case TE_WIDOWBEAMOUT:
		CL_ParseWidow ();
		break;

	case TE_NUKEBLAST:
		CL_ParseNuke ();
		break;

	case TE_WIDOWSPLASH:
		MSG_ReadPos (&net_message, pos);
		CL_WidowSplash (pos);
		break;
//PGM
//==============

	default:
		Com_Error (ERR_DROP, "CL_ParseTEnt: bad type");
	}
}
Exemple #8
0
void
Widow2Beam(edict_t *self)
{
	vec3_t forward, right, target;
	vec3_t start, targ_angles, vec;
	int flashnum;

	if (!self)
	{
		return;
	}

	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);
	}
	else
	{
		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);
	}
}
Exemple #9
0
void
hunter_think(edict_t *self)
{
	edict_t *owner;
	vec3_t dir, ang;

	if (!self)
	{
		return;
	}

	/* if we've exited the level, just remove ourselves. */
	if (level.intermissiontime)
	{
		sphere_think_explode(self);
		return;
	}

	owner = self->owner;

	if (!owner && !(self->spawnflags & SPHERE_DOPPLEGANGER))
	{
		G_FreeEdict(self);
		return;
	}

	if (owner)
	{
		self->ideal_yaw = owner->s.angles[YAW];
	}
	else if (self->enemy) /* fired by doppleganger */
	{
		VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
		vectoangles2(dir, ang);
		self->ideal_yaw = ang[YAW];
	}

	M_ChangeYaw(self);

	if (self->enemy)
	{
		sphere_chase(self, 0);

		/* deal with sam raimi cam */
		if (owner && (owner->flags & FL_SAM_RAIMI))
		{
			if (self->inuse)
			{
				owner->movetype = MOVETYPE_FLYMISSILE;
				LookAtKiller(owner, self, self->enemy);

				/* owner is flying with us, move him too */
				owner->movetype = MOVETYPE_FLYMISSILE;
				owner->viewheight = self->s.origin[2] - owner->s.origin[2];
				VectorCopy(self->s.origin, owner->s.origin);
				VectorCopy(self->velocity, owner->velocity);
				VectorClear(owner->mins);
				VectorClear(owner->maxs);
				gi.linkentity(owner);
			}
			else /* sphere timed out */
			{
				VectorClear(owner->velocity);
				owner->movetype = MOVETYPE_NONE;
				gi.linkentity(owner);
			}
		}
	}
	else
	{
		sphere_fly(self);
	}

	if (self->inuse)
	{
		self->nextthink = level.time + 0.1;
	}
}
Exemple #10
0
void
sphere_chase(edict_t *self, int stupidChase)
{
	vec3_t dest;
	vec3_t dir;
	float dist;

	if (!self)
	{
		return;
	}

	if ((level.time >= self->wait) ||
		(self->enemy && (self->enemy->health < 1)))
	{
		sphere_think_explode(self);
		return;
	}

	VectorCopy(self->enemy->s.origin, dest);

	if (self->enemy->client)
	{
		dest[2] += self->enemy->viewheight;
	}

	if (visible(self, self->enemy) || stupidChase)
	{
		/* if moving, hunter sphere uses active sound */
		if (!stupidChase)
		{
			self->s.sound = gi.soundindex("spheres/h_active.wav");
		}

		VectorSubtract(dest, self->s.origin, dir);
		VectorNormalize(dir);
		vectoangles2(dir, self->s.angles);
		VectorScale(dir, 500, self->velocity);
		VectorCopy(dest, self->monsterinfo.saved_goal);
	}
	else if (VectorCompare(self->monsterinfo.saved_goal, vec3_origin))
	{
		VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
		vectoangles2(dir, self->s.angles);

		/* if lurking, hunter sphere uses lurking sound */
		self->s.sound = gi.soundindex("spheres/h_lurk.wav");
		VectorClear(self->velocity);
	}
	else
	{
		VectorSubtract(self->monsterinfo.saved_goal, self->s.origin, dir);
		dist = VectorNormalize(dir);

		if (dist > 1)
		{
			vectoangles2(dir, self->s.angles);

			if (dist > 500)
			{
				VectorScale(dir, 500, self->velocity);
			}
			else if (dist < 20)
			{
				VectorScale(dir, (dist / FRAMETIME), self->velocity);
			}
			else
			{
				VectorScale(dir, dist, self->velocity);
			}

			/* if moving, hunter sphere uses active sound */
			if (!stupidChase)
			{
				self->s.sound = gi.soundindex("spheres/h_active.wav");
			}
		}
		else
		{
			VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
			vectoangles2(dir, self->s.angles);

			/* if not moving, hunter sphere uses lurk sound */
			if (!stupidChase)
			{
				self->s.sound = gi.soundindex("spheres/h_lurk.wav");
			}

			VectorClear(self->velocity);
		}
	}
}
Exemple #11
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);
	}	
}
Exemple #12
0
// =================
// =================
void hunter_think (edict_t *self)
{
	edict_t *owner;
	vec3_t	dir, ang;

	// if we've exited the level, just remove ourselves.
	if (level.intermissiontime)
	{	
		sphere_think_explode(self);
		return;
	}	

	owner = self->owner;
	if(!owner && !(self->spawnflags & SPHERE_DOPPLEGANGER))
	{
//		gi.dprintf("think: no owner\n");
		G_FreeEdict(self);
		return;
	}

	if(owner)
		self->ideal_yaw = owner->s.angles[YAW];
	else if(self->enemy)		// fired by doppleganger
	{
		VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
		vectoangles2(dir, ang);
		self->ideal_yaw = ang[YAW];
	}

	M_ChangeYaw(self);

//	if(level.time - self->timestamp > 1)
//	{
//		gi.sound (self, CHAN_VOICE, gi.soundindex ("powerup/hsphere.wav"), 0.5, ATTN_NORM, 0);
//		self->timestamp = level.time;
//	}

	if(self->enemy)
	{
		sphere_chase (self, 0);

		// deal with sam raimi cam
		if(owner && (owner->flags & FL_SAM_RAIMI)) 
		{
			if(self->inuse)
			{
				owner->movetype = MOVETYPE_FLYMISSILE;
//				VectorCopy(self->s.angles, owner->s.angles);
//				VectorCopy(self->s.angles, owner->client->v_angle);
				LookAtKiller (owner, self, self->enemy);
//				owner->viewheight = 22;

//				owner->client->v_angle[YAW]+=5;
				// owner is flying with us, move him too
				owner->movetype = MOVETYPE_FLYMISSILE;
				owner->viewheight = self->s.origin[2] - owner->s.origin[2];
//				VectorCopy(self->s.angles, owner->s.angles);
//				VectorCopy(self->s.angles, owner->client->v_angle);
				VectorCopy(self->s.origin, owner->s.origin);
				VectorCopy(self->velocity, owner->velocity);
				VectorClear(owner->mins);
				VectorClear(owner->maxs);
				gi.linkentity(owner);
			}
			else	// sphere timed out
			{
				VectorClear(owner->velocity);
				owner->movetype = MOVETYPE_NONE;
				gi.linkentity(owner);
			}
		}
	}
	else 
	{
//		self->ideal_yaw+=3;
//		M_ChangeYaw (self);
		sphere_fly (self);
	}

	if(self->inuse)
		self->nextthink = level.time + 0.1;
}
Exemple #13
0
void WidowBlaster (edict_t *self)
{
	vec3_t	forward, right, target, vec, targ_angles;
	vec3_t	start, end;
	int		flashnum;
	int		effect;

	if (!self->enemy)
		return;

	shotsfired++;
	if (!(shotsfired % 4))
		effect = EF_BLASTER;
	else
		effect = 0;

	AngleVectors (self->s.angles, forward, right, NULL);
	if ((self->s.frame >= FRAME_spawn05) && (self->s.frame <= FRAME_spawn13))
	{
		// sweep
		flashnum = MZ2_WIDOW_BLASTER_SWEEP1 + self->s.frame - FRAME_spawn05;
		G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
		VectorCopy(self->enemy->s.origin, end);

		// Lazarus fog reduction of accuracy
		if(self->monsterinfo.visibility < FOG_CANSEEGOOD)
		{
			end[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
			end[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
			end[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
		}

		VectorSubtract (end, start, target);
		vectoangles2 (target, targ_angles);

		VectorCopy (self->s.angles, vec);

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

		AngleVectors (vec, forward, NULL, NULL);
	//	monster_fire_blaster (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, (effect!=0)?(effect|EF_TRACKER):0, BLASTER_GREEN);
		monster_fire_blaster2 (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);

/*		if (self->s.frame == FRAME_spawn13)
		{
			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 ((self->s.frame >= FRAME_fired02a) && (self->s.frame <= FRAME_fired20))
	{
		vec3_t angles;
		float aim_angle, target_angle;
		float error;

		self->monsterinfo.aiflags |= AI_MANUAL_STEERING;

		self->monsterinfo.nextframe = WidowTorso (self);

		if (!self->monsterinfo.nextframe)
			self->monsterinfo.nextframe = self->s.frame;

//		if ((g_showlogic) && (g_showlogic->value))
//			gi.dprintf ("%d\n", self->monsterinfo.nextframe);

		if (self->s.frame == FRAME_fired02a)
			flashnum = MZ2_WIDOW_BLASTER_0;
		else
			flashnum = MZ2_WIDOW_BLASTER_100 + self->s.frame - FRAME_fired03;

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

		PredictAim (self->enemy, start, 1000, true, ((random()*0.1)-0.05), forward, NULL);

		// clamp it to within 10 degrees of the aiming angle (where she's facing)
		vectoangles2 (forward, angles);
		// give me 100 -> -70
		aim_angle = 100 - (10*(flashnum-MZ2_WIDOW_BLASTER_100));
		if (aim_angle <= 0)
			aim_angle += 360;
		target_angle = self->s.angles[YAW] - angles[YAW];
		if (target_angle <= 0)
			target_angle += 360;

		error = aim_angle - target_angle;

		// positive error is to entity's left, aka positive direction in engine
		// unfortunately, I decided that for the aim_angle, positive was right.  *sigh*
		if (error > VARIANCE)
		{
//			if ((g_showlogic) && (g_showlogic->value))
//				gi.dprintf ("angle %2.2f (really %2.2f) (%2.2f off of %2.2f) corrected to", target_angle, angles[YAW], error, aim_angle);
			angles[YAW] = (self->s.angles[YAW] - aim_angle) + VARIANCE;
//			if ((g_showlogic) && (g_showlogic->value))
//			{
//				if (angles[YAW] <= 0)
//					angles[YAW] += 360;
//				gi.dprintf (" %2.2f\n", angles[YAW]);
//			}
			AngleVectors (angles, forward, NULL, NULL);
		}
		else if (error < -VARIANCE)
		{
//			if ((g_showlogic) && (g_showlogic->value))
//				gi.dprintf ("angle %2.2f (really %2.2f) (%2.2f off of %2.2f) corrected to", target_angle, angles[YAW], error, aim_angle);
			angles[YAW] = (self->s.angles[YAW] - aim_angle) - VARIANCE;
//			if ((g_showlogic) && (g_showlogic->value))
//			{
//				if (angles[YAW] <= 0)
//					angles[YAW] += 360;
//				gi.dprintf (" %2.2f\n", angles[YAW]);
//			}
			AngleVectors (angles, forward, NULL, NULL);
		}
//		gi.dprintf ("%2.2f - %2.2f - %2.2f - %2.2f\n", aim_angle, self->s.angles[YAW] - angles[YAW], target_angle, error);
//		gi.dprintf ("%2.2f - %2.2f - %2.2f\n", angles[YAW], aim_angle, self->s.angles[YAW]);

/*
		if (self->s.frame == FRAME_fired20)
		{
			VectorMA (start, 512, forward, debugend);
			gi.WriteByte (svc_temp_entity);
			gi.WriteByte (TE_DEBUGTRAIL);
			gi.WritePosition (start);
			gi.WritePosition (forward);
			gi.multicast (start, MULTICAST_ALL);

			drawbbox (self);
			self->monsterinfo.aiflags |= AI_HOLD_FRAME;
			self->monsterinfo.nextframe = FRAME_fired20;
			self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
		}
*/
/*
		if (!(self->plat2flags % 3))
			effect = EF_HYPERBLASTER;
		else
			effect = 0;
		self->plat2flags ++;
*/
	//	monster_fire_blaster (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, (effect!=0)?(effect|EF_TRACKER):0, BLASTER_GREEN);
		monster_fire_blaster2 (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
	}
	else if ((self->s.frame >= FRAME_run01) && (self->s.frame <= FRAME_run08))
	{
		flashnum = MZ2_WIDOW_RUN_1 + self->s.frame - FRAME_run01;
		G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
		VectorCopy(self->enemy->s.origin, end);

		// Lazarus fog reduction of accuracy
		if(self->monsterinfo.visibility < FOG_CANSEEGOOD)
		{
			end[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
			end[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
			end[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility);
		}

		VectorSubtract (end, start, target);
		target[2] += self->enemy->viewheight;

	//	monster_fire_blaster (self, start, target, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, (effect!=0)?(effect|EF_TRACKER):0, BLASTER_GREEN);
		monster_fire_blaster2 (self, start, target, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
	}
//	else
//	{
//		if ((g_showlogic) && (g_showlogic->value))
//			gi.dprintf ("widow: firing on non-fire frame!\n");
//	}
}