1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6 
7 This file is part of the OpenJK source code.
8 
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22 
23 #include "g_local.h"
24 #include "g_functions.h"
25 #include "anims.h"
26 #include "wp_saber.h"
27 #include "../cgame/cg_local.h"
28 #include "b_local.h"
29 #include "g_navigator.h"
30 
31 extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt );
32 
33 //lock the owner into place relative to the cannon pos
EWebPositionUser(gentity_t * owner,gentity_t * eweb)34 void EWebPositionUser(gentity_t *owner, gentity_t *eweb)
35 {
36 	mdxaBone_t boltMatrix;
37 	vec3_t p, p2, d;
38 	trace_t tr;
39 	qboolean traceOver = qtrue;
40 
41 	if ( owner->s.number < MAX_CLIENTS )
42 	{//extra checks
43 		gi.trace(&tr, owner->currentOrigin, owner->mins, owner->maxs, owner->currentOrigin, owner->s.number, owner->clipmask, (EG2_Collision)0, 0);
44 		if ( tr.startsolid || tr.allsolid )
45 		{//crap, they're already in solid somehow, don't bother tracing over
46 			traceOver = qfalse;
47 		}
48 	}
49 	if ( traceOver )
50 	{//trace up
51 		VectorCopy( owner->currentOrigin, p2 );
52 		p2[2] += STEPSIZE;
53 		gi.trace(&tr, owner->currentOrigin, owner->mins, owner->maxs, p2, owner->s.number, owner->clipmask, (EG2_Collision)0, 0);
54 		if (!tr.startsolid && !tr.allsolid )
55 		{
56 			VectorCopy( tr.endpos, p2 );
57 		}
58 		else
59 		{
60 			VectorCopy( owner->currentOrigin, p2 );
61 		}
62 	}
63 	//trace over
64 	gi.G2API_GetBoltMatrix( eweb->ghoul2, 0, eweb->headBolt, &boltMatrix,
65 		eweb->s.apos.trBase, eweb->currentOrigin,
66 		(cg.time?cg.time:level.time), NULL, eweb->s.modelScale );
67 	gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, p );
68 	gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, d );
69 	d[2] = 0;
70 	VectorNormalize( d );
71 	VectorMA( p, -44.0f, d, p );
72 	if ( !traceOver )
73 	{
74 		VectorCopy( p, tr.endpos );
75 		tr.allsolid = tr.startsolid = qfalse;
76 	}
77 	else
78 	{
79 		p[2] = p2[2];
80 		if ( owner->s.number < MAX_CLIENTS )
81 		{//extra checks
82 			//just see if end point is not in solid
83 			gi.trace(&tr, p, owner->mins, owner->maxs, p, owner->s.number, owner->clipmask, (EG2_Collision)0, 0);
84 			if ( tr.startsolid || tr.allsolid )
85 			{//would be in solid there, so just trace over, I guess?
86 				gi.trace(&tr, p2, owner->mins, owner->maxs, p, owner->s.number, owner->clipmask, (EG2_Collision)0, 0);
87 			}
88 		}
89 		else
90 		{//trace over
91 			gi.trace(&tr, p2, owner->mins, owner->maxs, p, owner->s.number, owner->clipmask, (EG2_Collision)0, 0);
92 		}
93 	}
94 	if (!tr.startsolid && !tr.allsolid )
95 	{
96 		//trace down
97 		VectorCopy( tr.endpos, p );
98 		VectorCopy( p, p2 );
99 		p2[2] -= STEPSIZE;
100 		gi.trace(&tr, p, owner->mins, owner->maxs, p2, owner->s.number, owner->clipmask, (EG2_Collision)0, 0);
101 
102 		if (!tr.startsolid && !tr.allsolid )//&& tr.fraction == 1.0f)
103 		{ //all clear, we can move there
104 			vec3_t	moveDir;
105 			float moveDist;
106 			VectorCopy( tr.endpos, p );
107 			VectorSubtract( p, eweb->pos4, moveDir );
108 			moveDist = VectorNormalize( moveDir );
109 			if ( moveDist > 4.0f )
110 			{//moved past the threshold from last position
111 				vec3_t	oRight;
112 				int		strafeAnim;
113 
114 				VectorCopy( p, eweb->pos4 );//update the position
115 				//find out what direction he moved in
116 				AngleVectors( owner->currentAngles, NULL, oRight, NULL );
117 				if ( DotProduct( moveDir, oRight ) > 0 )
118 				{//moved to his right, play right strafe
119 					strafeAnim = BOTH_STRAFE_RIGHT1;
120 				}
121 				else
122 				{//moved left, play left strafe
123 					strafeAnim = BOTH_STRAFE_LEFT1;
124 				}
125 				NPC_SetAnim( owner, SETANIM_LEGS, strafeAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
126 			}
127 
128 			G_SetOrigin(owner, p);
129 			VectorCopy(p, owner->client->ps.origin);
130 			gi.linkentity( owner );
131 		}
132 	}
133 	//FIXME: IK the hands to the handles of the gun?
134 }
135 
136 //===============================================
137 //End E-Web
138 //===============================================
139 
140 //----------------------------------------------------------
141 
142 //===============================================
143 //Emplaced Gun
144 //===============================================
145 
146 // spawnflag
147 #define	EMPLACED_INACTIVE	1
148 #define EMPLACED_FACING		2
149 #define EMPLACED_VULNERABLE	4
150 #define EWEB_INVULNERABLE	4
151 #define EMPLACED_PLAYERUSE	8
152 
153 /*QUAKED emplaced_eweb (0 0 1) (-12 -12 -24) (12 12 24) INACTIVE FACING INVULNERABLE PLAYERUSE
154 
155  INACTIVE cannot be used until used by a target_activate
156  FACING - player must be facing relatively in the same direction as the gun in order to use it
157  VULNERABLE - allow the gun to take damage
158  PLAYERUSE - only the player makes it run its usescript
159 
160  count - how much ammo to give this gun ( default 999 )
161  health - how much damage the gun can take before it blows ( default 250 )
162  delay - ONLY AFFECTS NPCs - time between shots ( default 200 on hardest setting )
163  wait - ONLY AFFECTS NPCs - time between bursts ( default 800 on hardest setting )
164  splashdamage - how much damage a blowing up gun deals ( default 80 )
165  splashradius - radius for exploding damage ( default 128 )
166 
167  scripts:
168 	will run usescript, painscript and deathscript
169 */
170 //----------------------------------------------------------
eweb_pain(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,const vec3_t point,int damage,int mod,int hitLoc)171 void eweb_pain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc )
172 {
173 	if ( self->health <= 0 )
174 	{
175 		// play pain effect?
176 	}
177 	else
178 	{
179 		if ( self->paintarget )
180 		{
181 			G_UseTargets2( self, self->activator, self->paintarget );
182 		}
183 
184 		// Don't do script if dead
185 		G_ActivateBehavior( self, BSET_PAIN );
186 	}
187 }
188 //----------------------------------------------------------
eweb_die(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int mod,int dFlags,int hitLoc)189 void eweb_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc )
190 {
191 	vec3_t org;
192 
193 	// turn off any firing animations it may have been doing
194 	self->s.frame = self->startFrame = self->endFrame = 0;
195 	self->svFlags &= ~(SVF_ANIMATING|SVF_PLAYER_USABLE);
196 
197 
198 	self->health = 0;
199 //	self->s.weapon = WP_EMPLACED_GUN; // we need to be able to switch back to the old weapon
200 
201 	self->takedamage = qfalse;
202 	self->lastEnemy = attacker;
203 
204 	if ( self->activator && self->activator->client )
205 	{
206 		if ( self->activator->NPC )
207 		{
208 			vec3_t right;
209 
210 			// radius damage seems to throw them, but add an extra bit to throw them away from the weapon
211 			AngleVectors( self->currentAngles, NULL, right, NULL );
212 			VectorMA( self->activator->client->ps.velocity, 140, right, self->activator->client->ps.velocity );
213 			self->activator->client->ps.velocity[2] = -100;
214 
215 			// kill them
216 			self->activator->health = 0;
217 			self->activator->client->ps.stats[STAT_HEALTH] = 0;
218 		}
219 
220 		// kill the players emplaced ammo, cheesy way to keep the gun from firing
221 		self->activator->client->ps.ammo[weaponData[WP_EMPLACED_GUN].ammoIndex] = 0;
222 	}
223 
224 	self->e_PainFunc = painF_NULL;
225 
226 	if ( self->target )
227 	{
228 		G_UseTargets( self, attacker );
229 	}
230 
231 	G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN );
232 
233 	VectorCopy( self->currentOrigin,  org );
234 	org[2] += 20;
235 
236 	G_PlayEffect( "emplaced/explode", org );
237 
238 	// Turn the top of the eweb off.
239 #define TURN_OFF			0x00000100//G2SURFACEFLAG_NODESCENDANTS
240 	gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "eweb_damage", TURN_OFF );
241 
242 	// create some persistent smoke by using a dynamically created fx runner
243 	gentity_t *ent = G_Spawn();
244 
245 	if ( ent )
246 	{
247 		ent->delay = 200;
248 		ent->random = 100;
249 
250 		ent->fxID = G_EffectIndex( "emplaced/dead_smoke" );
251 
252 		ent->e_ThinkFunc = thinkF_fx_runner_think;
253 		ent->nextthink = level.time + 50;
254 
255 		// move up above the gun origin
256 		VectorCopy( self->currentOrigin, org );
257 		org[2] += 35;
258 		G_SetOrigin( ent, org );
259 		VectorCopy( org, ent->s.origin );
260 
261 		VectorSet( ent->s.angles, -90, 0, 0 ); // up
262 		G_SetAngles( ent, ent->s.angles );
263 
264 		gi.linkentity( ent );
265 	}
266 
267 	G_ActivateBehavior( self, BSET_DEATH );
268 }
269 
eweb_can_be_used(gentity_t * self,gentity_t * other,gentity_t * activator)270 qboolean eweb_can_be_used( gentity_t *self, gentity_t *other, gentity_t *activator )
271 {
272 	if ( self->health <= 0 )
273 	{
274 		// can't use a dead gun.
275 		return qfalse;
276 	}
277 
278 	if ( self->svFlags & SVF_INACTIVE )
279 	{
280 		return qfalse; // can't use inactive gun
281 	}
282 
283 	if ( !activator->client )
284 	{
285 		return qfalse; // only a client can use it.
286 	}
287 
288 	if ( self->activator )
289 	{
290 		// someone is already in the gun.
291 		return qfalse;
292 	}
293 
294 	if ( other && other->client && G_IsRidingVehicle( other ) )
295 	{//can't use eweb when on a vehicle
296 		return qfalse;
297 	}
298 
299 	if ( activator && activator->client && G_IsRidingVehicle( activator ) )
300 	{//can't use eweb when on a vehicle
301 		return qfalse;
302 	}
303 
304 	if ( activator && activator->client && (activator->client->ps.pm_flags&PMF_DUCKED) )
305 	{//stand up, ya cowardly varmint!
306 		return qfalse;
307 	}
308 
309 	if ( activator && activator->health <= 0 )
310 	{//dead men ain't got no more use fer guns...
311 		return qfalse;
312 	}
313 
314 	vec3_t fwd1, fwd2;
315 	vec3_t	facingAngles;
316 
317 	VectorAdd( self->s.angles, self->pos1, facingAngles );
318 	if ( activator->s.number < MAX_CLIENTS )
319 	{//player must be facing general direction of the turret head
320 		// Let's get some direction vectors for the users
321 		AngleVectors( activator->client->ps.viewangles, fwd1, NULL, NULL );
322 		fwd1[2] = 0;
323 
324 		// Get the gun's direction vector
325 		AngleVectors( facingAngles, fwd2, NULL, NULL );
326 		fwd2[2] = 0;
327 
328 		float dot = DotProduct( fwd1, fwd2 );
329 
330 		// Must be reasonably facing the way the gun points ( 90 degrees or so ), otherwise we don't allow to use it.
331 		if ( dot < 0.75f )
332 		{
333 			return qfalse;
334 		}
335 	}
336 
337 	if ( self->delay + 500 < level.time )
338 	{
339 		return qtrue;
340 	}
341 	return qfalse;
342 }
343 
eweb_use(gentity_t * self,gentity_t * other,gentity_t * activator)344 void eweb_use( gentity_t *self, gentity_t *other, gentity_t *activator )
345 {
346 	if ( !eweb_can_be_used( self, other, activator ) )
347 	{
348 		return;
349 	}
350 
351 	int	oldWeapon = activator->s.weapon;
352 
353 	if ( oldWeapon == WP_SABER )
354 	{
355 		self->alt_fire = activator->client->ps.SaberActive();
356 	}
357 
358 	// swap the users weapon with the emplaced gun and add the ammo the gun has to the player
359 	activator->client->ps.weapon = self->s.weapon;
360 	Add_Ammo( activator, WP_EMPLACED_GUN, self->count );
361 	activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN );
362 
363 	// Allow us to point from one to the other
364 	activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it.
365 	self->activator = activator;
366 
367 	G_RemoveWeaponModels( activator );
368 
369 extern void ChangeWeapon( gentity_t *ent, int newWeapon );
370 	if ( activator->NPC )
371 	{
372 		ChangeWeapon( activator, WP_EMPLACED_GUN );
373 	}
374 	else if ( activator->s.number == 0 )
375 	{
376 		// we don't want for it to draw the weapon select stuff
377 		cg.weaponSelect = WP_EMPLACED_GUN;
378 		CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 );
379 	}
380 
381 	VectorCopy( activator->currentOrigin, self->pos4 );//keep this around so we know when to make them play the strafe anim
382 
383 	// the gun will track which weapon we used to have
384 	self->s.weapon = oldWeapon;
385 
386 	// Lock the player
387 	activator->client->ps.eFlags |= EF_LOCKED_TO_WEAPON;
388 	activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it.
389 	self->activator = activator;
390 	self->delay = level.time; // can't disconnect from the thing for half a second
391 
392 	// Let the gun be considered an enemy
393 	//Ugh, so much AI code seems to assume enemies are clients, maybe this shouldn't be on, but it's too late in the game to change it now without knowing what side-effects this will have
394 	self->svFlags |= SVF_NONNPC_ENEMY;
395 	self->noDamageTeam = activator->client->playerTeam;
396 
397 	//FIXME: should really wait a bit after spawn and get this just once?
398 	self->waypoint = NAV::GetNearestNode(self);
399 #ifdef _DEBUG
400 	if ( self->waypoint == -1 )
401 	{
402 		gi.Printf( S_COLOR_RED"ERROR: no waypoint for emplaced_gun %s at %s\n", self->targetname, vtos(self->currentOrigin) );
403 	}
404 #endif
405 
406 	G_Sound( self, G_SoundIndex( "sound/weapons/eweb/eweb_mount.mp3" ));
407 
408 	if ( !(self->spawnflags&EMPLACED_PLAYERUSE) || activator->s.number == 0 )
409 	{//player-only usescript or any usescript
410 		// Run use script
411 		G_ActivateBehavior( self, BSET_USE );
412 	}
413 }
414 
415 //----------------------------------------------------------
SP_emplaced_eweb(gentity_t * ent)416 void SP_emplaced_eweb( gentity_t *ent )
417 {
418 	char name[] = "models/map_objects/hoth/eweb_model.glm";
419 
420 	ent->svFlags |= SVF_PLAYER_USABLE;
421 	ent->contents = CONTENTS_BODY;
422 
423 	if ( ent->spawnflags & EMPLACED_INACTIVE )
424 	{
425 		ent->svFlags |= SVF_INACTIVE;
426 	}
427 
428 	VectorSet( ent->mins, -12, -12, -24 );
429 	VectorSet( ent->maxs, 12, 12, 24 );
430 
431 	ent->takedamage = qtrue;
432 
433 	if ( ( ent->spawnflags & EWEB_INVULNERABLE ))
434 	{
435 		ent->flags |= FL_GODMODE;
436 	}
437 
438 	ent->s.radius = 80;
439 	ent->spawnflags |= 4; // deadsolid
440 
441 	//ent->e_ThinkFunc = thinkF_NULL;
442 	ent->e_PainFunc = painF_eweb_pain;
443 	ent->e_DieFunc  = dieF_eweb_die;
444 
445 	G_EffectIndex( "emplaced/explode" );
446 	G_EffectIndex( "emplaced/dead_smoke" );
447 
448 	G_SoundIndex( "sound/weapons/eweb/eweb_aim.wav" );
449 	G_SoundIndex( "sound/weapons/eweb/eweb_dismount.mp3" );
450 	//G_SoundIndex( "sound/weapons/eweb/eweb_empty.wav" );
451 	G_SoundIndex( "sound/weapons/eweb/eweb_fire.wav" );
452 	G_SoundIndex( "sound/weapons/eweb/eweb_hitplayer.wav" );
453 	G_SoundIndex( "sound/weapons/eweb/eweb_hitsurface.wav" );
454 	//G_SoundIndex( "sound/weapons/eweb/eweb_load.wav" );
455 	G_SoundIndex( "sound/weapons/eweb/eweb_mount.mp3" );
456 
457 	// Set up our defaults and override with custom amounts as necessary
458 	G_SpawnInt( "count", "999", &ent->count );
459 	G_SpawnInt( "health", "250", &ent->health );
460 	G_SpawnInt( "splashDamage", "40", &ent->splashDamage );
461 	G_SpawnInt( "splashRadius", "100", &ent->splashRadius );
462 	G_SpawnFloat( "delay", "200", &ent->random ); // NOTE: spawning into a different field!!
463 	G_SpawnFloat( "wait", "800", &ent->wait );
464 
465 	ent->max_health = ent->health;
466 	ent->dflags |= DAMAGE_CUSTOM_HUD; // dumb, but we draw a custom hud
467 
468 	ent->s.modelindex = G_ModelIndex( name );
469 	ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, name, ent->s.modelindex, NULL_HANDLE, NULL_HANDLE, 0, 0 );
470 
471 	// Activate our tags and bones
472 	ent->handLBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*cannonflash" ); //muzzle bolt
473 	ent->headBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "cannon_Xrot" ); //for placing the owner relative to rotation
474 	ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
475 	ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cannon_Yrot", qtrue );
476 	ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cannon_Xrot", qtrue );
477 	gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0);
478 	gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0);
479 	//gi.G2API_SetBoneAngles( &ent->ghoul2[0], "cannon_Yrot", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL);
480 	//set the constraints for this guy as an emplaced weapon, and his constraint angles
481 	//ent->s.origin2[0] = 60.0f; //60 degrees in either direction
482 
483 	RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN ));
484 	ent->s.weapon = WP_EMPLACED_GUN;
485 
486 	G_SetOrigin( ent, ent->s.origin );
487 	G_SetAngles( ent, ent->s.angles );
488 	VectorCopy( ent->s.angles, ent->lastAngles );
489 
490 	// store base angles for later
491 	VectorClear( ent->pos1 );
492 
493 	ent->e_UseFunc = useF_eweb_use;
494 	ent->bounceCount = 1;//to distinguish it from the emplaced gun
495 
496 	gi.linkentity (ent);
497 }
498 
499 /*QUAKED emplaced_gun (0 0 1) (-24 -24 0) (24 24 64) INACTIVE x VULNERABLE PLAYERUSE
500 
501  INACTIVE cannot be used until used by a target_activate
502  VULNERABLE - allow the gun to take damage
503  PLAYERUSE - only the player makes it run its usescript
504 
505  count - how much ammo to give this gun ( default 999 )
506  health - how much damage the gun can take before it blows ( default 250 )
507  delay - ONLY AFFECTS NPCs - time between shots ( default 200 on hardest setting )
508  wait - ONLY AFFECTS NPCs - time between bursts ( default 800 on hardest setting )
509  splashdamage - how much damage a blowing up gun deals ( default 80 )
510  splashradius - radius for exploding damage ( default 128 )
511 
512  scripts:
513 	will run usescript, painscript and deathscript
514 */
515 
516 //----------------------------------------------------------
emplaced_gun_use(gentity_t * self,gentity_t * other,gentity_t * activator)517 void emplaced_gun_use( gentity_t *self, gentity_t *other, gentity_t *activator )
518 {
519 	vec3_t fwd1, fwd2;
520 
521 	if ( self->health <= 0 )
522 	{
523 		// can't use a dead gun.
524 		return;
525 	}
526 
527 	if ( self->svFlags & SVF_INACTIVE )
528 	{
529 		return; // can't use inactive gun
530 	}
531 
532 	if ( !activator->client )
533 	{
534 		return; // only a client can use it.
535 	}
536 
537 	if ( self->activator )
538 	{
539 		// someone is already in the gun.
540 		return;
541 	}
542 
543 	if ( other && other->client && G_IsRidingVehicle( other ) )
544 	{//can't use eweb when on a vehicle
545 		return;
546 	}
547 
548 	if ( activator && activator->client && G_IsRidingVehicle( activator ) )
549 	{//can't use eweb when on a vehicle
550 		return;
551 	}
552 
553 	// We'll just let the designers duke this one out....I mean, as to whether they even want to limit such a thing.
554 	if ( self->spawnflags & EMPLACED_FACING )
555 	{
556 		// Let's get some direction vectors for the users
557 		AngleVectors( activator->client->ps.viewangles, fwd1, NULL, NULL );
558 
559 		// Get the guns direction vector
560 		AngleVectors( self->pos1, fwd2, NULL, NULL );
561 
562 		float dot = DotProduct( fwd1, fwd2 );
563 
564 		// Must be reasonably facing the way the gun points ( 90 degrees or so ), otherwise we don't allow to use it.
565 		if ( dot < 0.0f )
566 		{
567 			return;
568 		}
569 	}
570 
571 	// don't allow using it again for half a second
572 	if ( self->delay + 500 < level.time )
573 	{
574 		int	oldWeapon = activator->s.weapon;
575 
576 		if ( oldWeapon == WP_SABER )
577 		{
578 			self->alt_fire = activator->client->ps.SaberActive();
579 		}
580 
581 		// swap the users weapon with the emplaced gun and add the ammo the gun has to the player
582 		activator->client->ps.weapon = self->s.weapon;
583 		Add_Ammo( activator, WP_EMPLACED_GUN, self->count );
584 		activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN );
585 
586 		// Allow us to point from one to the other
587 		activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it.
588 		self->activator = activator;
589 
590 		G_RemoveWeaponModels( activator );
591 
592 extern void ChangeWeapon( gentity_t *ent, int newWeapon );
593 		if ( activator->NPC )
594 		{
595 			ChangeWeapon( activator, WP_EMPLACED_GUN );
596 		}
597 		else if ( activator->s.number == 0 )
598 		{
599 			// we don't want for it to draw the weapon select stuff
600 			cg.weaponSelect = WP_EMPLACED_GUN;
601 			CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 );
602 		}
603 		// Since we move the activator inside of the gun, we reserve a solid spot where they were standing in order to be able to get back out without being in solid
604 		if ( self->nextTrain )
605 		{//you never know
606 			G_FreeEntity( self->nextTrain );
607 		}
608 		self->nextTrain = G_Spawn();
609 		//self->nextTrain->classname = "emp_placeholder";
610 		self->nextTrain->contents = CONTENTS_MONSTERCLIP|CONTENTS_PLAYERCLIP;//hmm... playerclip too now that we're doing it for NPCs?
611 		G_SetOrigin( self->nextTrain, activator->client->ps.origin );
612 		VectorCopy( activator->mins, self->nextTrain->mins );
613 		VectorCopy( activator->maxs, self->nextTrain->maxs );
614 		gi.linkentity( self->nextTrain );
615 
616 		//need to inflate the activator's mins/maxs since the gunsit anim puts them outside of their bbox
617 		VectorSet( activator->mins, -24, -24, -24 );
618 		VectorSet( activator->maxs, 24, 24, 40 );
619 
620 		// Move the activator into the center of the gun.  For NPC's the only way the can get out of the gun is to die.
621 		VectorCopy( self->s.origin, activator->client->ps.origin );
622 		activator->client->ps.origin[2] += 30; // move them up so they aren't standing in the floor
623 		gi.linkentity( activator );
624 
625 		// the gun will track which weapon we used to have
626 		self->s.weapon = oldWeapon;
627 
628 		// Lock the player
629 		activator->client->ps.eFlags |= EF_LOCKED_TO_WEAPON;
630 		activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it.
631 		self->activator = activator;
632 		self->delay = level.time; // can't disconnect from the thing for half a second
633 
634 		// Let the gun be considered an enemy
635 		//Ugh, so much AI code seems to assume enemies are clients, maybe this shouldn't be on, but it's too late in the game to change it now without knowing what side-effects this will have
636 		self->svFlags |= SVF_NONNPC_ENEMY;
637 		self->noDamageTeam = activator->client->playerTeam;
638 
639 		// FIXME: don't do this, we'll try and actually put the player in this beast
640 		// move the player to the center of the gun
641 //		activator->contents = 0;
642 //		VectorCopy( self->currentOrigin, activator->client->ps.origin );
643 
644 		SetClientViewAngle( activator, self->pos1 );
645 
646 		//FIXME: should really wait a bit after spawn and get this just once?
647 		self->waypoint = NAV::GetNearestNode(self);
648 #ifdef _DEBUG
649 		if ( self->waypoint == -1 )
650 		{
651 			gi.Printf( S_COLOR_RED"ERROR: no waypoint for emplaced_gun %s at %s\n", self->targetname, vtos(self->currentOrigin) );
652 		}
653 #endif
654 
655 		G_Sound( self, G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" ));
656 
657 		if ( !(self->spawnflags&EMPLACED_PLAYERUSE) || activator->s.number == 0 )
658 		{//player-only usescript or any usescript
659 			// Run use script
660 			G_ActivateBehavior( self, BSET_USE );
661 		}
662 	}
663 }
664 
665 //----------------------------------------------------------
emplaced_gun_pain(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,const vec3_t point,int damage,int mod,int hitLoc)666 void emplaced_gun_pain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc )
667 {
668 	if ( self->health <= 0 )
669 	{
670 		// play pain effect?
671 	}
672 	else
673 	{
674 		if ( self->paintarget )
675 		{
676 			G_UseTargets2( self, self->activator, self->paintarget );
677 		}
678 
679 		// Don't do script if dead
680 		G_ActivateBehavior( self, BSET_PAIN );
681 	}
682 }
683 
684 //----------------------------------------------------------
emplaced_blow(gentity_t * ent)685 void emplaced_blow( gentity_t *ent )
686 {
687 	ent->e_DieFunc = dieF_NULL;
688 	emplaced_gun_die( ent, ent->lastEnemy, ent->lastEnemy, 0, MOD_UNKNOWN );
689 }
690 
691 //----------------------------------------------------------
emplaced_gun_die(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int mod,int dFlags,int hitLoc)692 void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc )
693 {
694 	vec3_t org;
695 
696 	// turn off any firing animations it may have been doing
697 	self->s.frame = self->startFrame = self->endFrame = 0;
698 	self->svFlags &= ~SVF_ANIMATING;
699 
700 	self->health = 0;
701 //	self->s.weapon = WP_EMPLACED_GUN; // we need to be able to switch back to the old weapon
702 
703 	self->takedamage = qfalse;
704 	self->lastEnemy = attacker;
705 
706 	// we defer explosion so the player has time to get out
707 	if ( self->e_DieFunc )
708 	{
709 		self->e_ThinkFunc = thinkF_emplaced_blow;
710 		self->nextthink = level.time + 3000; // don't blow for a couple of seconds
711 		return;
712 	}
713 
714 	if ( self->activator && self->activator->client )
715 	{
716 		if ( self->activator->NPC )
717 		{
718 			vec3_t right;
719 
720 			// radius damage seems to throw them, but add an extra bit to throw them away from the weapon
721 			AngleVectors( self->currentAngles, NULL, right, NULL );
722 			VectorMA( self->activator->client->ps.velocity, 140, right, self->activator->client->ps.velocity );
723 			self->activator->client->ps.velocity[2] = -100;
724 
725 			// kill them
726 			self->activator->health = 0;
727 			self->activator->client->ps.stats[STAT_HEALTH] = 0;
728 		}
729 
730 		// kill the players emplaced ammo, cheesy way to keep the gun from firing
731 		self->activator->client->ps.ammo[weaponData[WP_EMPLACED_GUN].ammoIndex] = 0;
732 	}
733 
734 	self->e_PainFunc = painF_NULL;
735 	self->e_ThinkFunc = thinkF_NULL;
736 
737 	if ( self->target )
738 	{
739 		G_UseTargets( self, attacker );
740 	}
741 
742 	G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN );
743 
744 	// when the gun is dead, add some ugliness to it.
745 	vec3_t ugly;
746 
747 	ugly[YAW] = 4;
748 	ugly[PITCH] = self->lastAngles[PITCH] * 0.8f + Q_flrand(-1.0f, 1.0f) * 6;
749 	ugly[ROLL] = Q_flrand(-1.0f, 1.0f) * 7;
750 	gi.G2API_SetBoneAnglesIndex( &self->ghoul2[self->playerModel], self->lowerLumbarBone, ugly, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 0, 0 );
751 
752 	VectorCopy( self->currentOrigin,  org );
753 	org[2] += 20;
754 
755 	G_PlayEffect( "emplaced/explode", org );
756 
757 	// create some persistent smoke by using a dynamically created fx runner
758 	gentity_t *ent = G_Spawn();
759 
760 	if ( ent )
761 	{
762 		ent->delay = 200;
763 		ent->random = 100;
764 
765 		ent->fxID = G_EffectIndex( "emplaced/dead_smoke" );
766 
767 		ent->e_ThinkFunc = thinkF_fx_runner_think;
768 		ent->nextthink = level.time + 50;
769 
770 		// move up above the gun origin
771 		VectorCopy( self->currentOrigin, org );
772 		org[2] += 35;
773 		G_SetOrigin( ent, org );
774 		VectorCopy( org, ent->s.origin );
775 
776 		VectorSet( ent->s.angles, -90, 0, 0 ); // up
777 		G_SetAngles( ent, ent->s.angles );
778 
779 		gi.linkentity( ent );
780 	}
781 
782 	G_ActivateBehavior( self, BSET_DEATH );
783 }
784 
785 //----------------------------------------------------------
SP_emplaced_gun(gentity_t * ent)786 void SP_emplaced_gun( gentity_t *ent )
787 {
788 	char name[] = "models/map_objects/imp_mine/turret_chair.glm";
789 
790 	ent->svFlags |= SVF_PLAYER_USABLE;
791 	ent->contents = CONTENTS_BODY;//CONTENTS_SHOTCLIP|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP;//CONTENTS_SOLID;
792 
793 	if ( ent->spawnflags & EMPLACED_INACTIVE )
794 	{
795 		ent->svFlags |= SVF_INACTIVE;
796 	}
797 
798 	VectorSet( ent->mins, -30, -30, -5 );
799 	VectorSet( ent->maxs, 30, 30, 60 );
800 
801 	ent->takedamage = qtrue;
802 
803 	if ( !( ent->spawnflags & EMPLACED_VULNERABLE ))
804 	{
805 		ent->flags |= FL_GODMODE;
806 	}
807 
808 	ent->s.radius = 110;
809 	ent->spawnflags |= 4; // deadsolid
810 
811 	//ent->e_ThinkFunc = thinkF_NULL;
812 	ent->e_PainFunc = painF_emplaced_gun_pain;
813 	ent->e_DieFunc  = dieF_emplaced_gun_die;
814 
815 	G_EffectIndex( "emplaced/explode" );
816 	G_EffectIndex( "emplaced/dead_smoke" );
817 
818 	G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" );
819 	G_SoundIndex( "sound/weapons/emplaced/emplaced_dismount.mp3" );
820 	G_SoundIndex( "sound/weapons/emplaced/emplaced_move_lp.wav" );
821 
822 	// Set up our defaults and override with custom amounts as necessary
823 	G_SpawnInt( "count", "999", &ent->count );
824 	G_SpawnInt( "health", "250", &ent->health );
825 	G_SpawnInt( "splashDamage", "80", &ent->splashDamage );
826 	G_SpawnInt( "splashRadius", "128", &ent->splashRadius );
827 	G_SpawnFloat( "delay", "200", &ent->random ); // NOTE: spawning into a different field!!
828 	G_SpawnFloat( "wait", "800", &ent->wait );
829 
830 	ent->max_health = ent->health;
831 	ent->dflags |= DAMAGE_CUSTOM_HUD; // dumb, but we draw a custom hud
832 
833 	ent->s.modelindex = G_ModelIndex( name );
834 	ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, name, ent->s.modelindex, NULL_HANDLE, NULL_HANDLE, 0, 0 );
835 
836 	// Activate our tags and bones
837 	ent->headBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*seat" );
838 	ent->handLBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*flash01" );
839 	ent->handRBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*flash02" );
840 	ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "base_bone", qtrue );
841 	ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "swivel_bone", qtrue );
842 	gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 0, 0);
843 
844 	RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN ));
845 	ent->s.weapon = WP_EMPLACED_GUN;
846 
847 	G_SetOrigin( ent, ent->s.origin );
848 	G_SetAngles( ent, ent->s.angles );
849 	VectorCopy( ent->s.angles, ent->lastAngles );
850 
851 	// store base angles for later
852 	VectorCopy( ent->s.angles, ent->pos1 );
853 
854 	ent->e_UseFunc = useF_emplaced_gun_use;
855 	ent->bounceCount = 0;//to distinguish it from the eweb
856 
857 	gi.linkentity (ent);
858 }
859 
860 //====================================================
861 //General Emplaced Weapon Funcs called in g_active.cpp
862 //====================================================
863 
G_UpdateEmplacedWeaponData(gentity_t * ent)864 void G_UpdateEmplacedWeaponData( gentity_t *ent )
865 {
866 	if ( ent && ent->owner && ent->health > 0 )
867 	{
868 		gentity_t *chair = ent->owner;
869 		if ( chair->e_UseFunc == useF_emplaced_gun_use )//yeah, crappy way to check this, but...
870 		{//one that you sit in
871 			//take the emplaced gun's waypoint as your own
872 			ent->waypoint = chair->waypoint;
873 
874 			//update the actual origin of the sitter
875 			mdxaBone_t	boltMatrix;
876 			vec3_t	chairAng = {0, ent->client->ps.viewangles[YAW], 0};
877 
878 			// Getting the seat bolt here
879 			gi.G2API_GetBoltMatrix( chair->ghoul2, chair->playerModel, chair->headBolt,
880 					&boltMatrix, chairAng, chair->currentOrigin, (cg.time?cg.time:level.time),
881 					NULL, chair->s.modelScale );
882 			// Storing ent position, bolt position, and bolt axis
883 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent->client->ps.origin );
884 			gi.linkentity( ent );
885 		}
886 		else if ( chair->e_UseFunc == useF_eweb_use )//yeah, crappy way to check this, but...
887 		{//standing at an E-Web
888 			EWebPositionUser( ent, chair );
889 		}
890 	}
891 }
892 
ExitEmplacedWeapon(gentity_t * ent)893 void ExitEmplacedWeapon( gentity_t *ent )
894 {
895 	// requesting to unlock from the weapon
896 	// We'll leave the gun pointed in the direction it was last facing, though we'll cut out the pitch
897 	if ( ent->client )
898 	{
899 		// if we are the player we will have put down a brush that blocks NPCs so that we have a clear spot to get back out.
900 		//gentity_t *place = G_Find( NULL, FOFS(classname), "emp_placeholder" );
901 
902 		if ( ent->health > 0 )
903 		{//he's still alive, and we have a placeholder, so put him back
904 			if ( ent->owner->nextTrain )
905 			{
906 				// reset the players position
907 				VectorCopy( ent->owner->nextTrain->currentOrigin, ent->client->ps.origin );
908 				//reset ent's size to normal
909 				VectorCopy( ent->owner->nextTrain->mins, ent->mins );
910 				VectorCopy( ent->owner->nextTrain->maxs, ent->maxs );
911 				//free the placeholder
912 				G_FreeEntity( ent->owner->nextTrain );
913 				//re-link the ent
914 				gi.linkentity( ent );
915 			}
916 			else if ( ent->owner->e_UseFunc == useF_eweb_use )//yeah, crappy way to check this, but...
917 			{
918 				// so give 'em a push away from us
919 				vec3_t backDir, start, end;
920 				trace_t trace;
921 				gentity_t *eweb = ent->owner;
922 				float curRadius = 0.0f;
923 				float minRadius, maxRadius;
924 				qboolean safeExit = qfalse;
925 
926 				VectorSubtract( ent->currentOrigin, eweb->currentOrigin, backDir );
927 				backDir[2] = 0;
928 				minRadius = VectorNormalize( backDir )-8.0f;
929 
930 				maxRadius = (ent->maxs[0]+ent->maxs[1])*0.5f;
931 				maxRadius += (eweb->maxs[0]+eweb->maxs[1])*0.5f;
932 				maxRadius *= 1.5f;
933 
934 				if ( minRadius >= maxRadius - 1.0f )
935 				{
936 					maxRadius = minRadius + 8.0f;
937 				}
938 
939 				ent->owner = NULL;//so his trace hits me
940 
941 				for ( curRadius = minRadius; curRadius <= maxRadius; curRadius += 4.0f )
942 				{
943 					VectorMA( ent->currentOrigin, curRadius, backDir, start );
944 					//make sure they're not in the ground
945 					VectorCopy( start, end );
946 					start[2] += 18;
947 					end[2] -= 18;
948 					gi.trace(&trace, start, ent->mins, ent->maxs, end, ent->s.number, ent->clipmask, (EG2_Collision)0, 0);
949 					if ( !trace.allsolid && !trace.startsolid )
950 					{
951 						G_SetOrigin( ent, trace.endpos );
952 						gi.linkentity( ent );
953 						safeExit = qtrue;
954 						break;
955 					}
956 				}
957 				//Hmm... otherwise, don't allow them to get off?
958 				ent->owner = eweb;
959 				if ( !safeExit )
960 				{//don't try again for a second
961 					ent->owner->delay = level.time + 500;
962 					return;
963 				}
964 			}
965 		}
966 		else if ( ent->health <= 0 )
967 		{
968 			// dead, so give 'em a push out of the chair
969 			vec3_t dir;
970 			AngleVectors( ent->owner->s.angles, NULL, dir, NULL );
971 
972 			if ( rand() & 1 )
973 			{
974 				VectorScale( dir, -1, dir );
975 			}
976 
977 			VectorMA( ent->client->ps.velocity, 75, dir, ent->client->ps.velocity );
978 		}
979 		//don't let them move towards me for a couple frames so they don't step back into me while I'm becoming solid to them
980 		if ( ent->s.number < MAX_CLIENTS )
981 		{
982 			if ( ent->client->ps.pm_time < 100 )
983 			{
984 				ent->client->ps.pm_time = 100;
985 			}
986 			ent->client->ps.pm_flags |= (PMF_TIME_NOFRICTION|PMF_TIME_KNOCKBACK);
987 		}
988 
989 		if ( !ent->owner->bounceCount )
990 		{//not an EWeb - the overridden bone angles will remember the angle we left it at
991 			VectorCopy( ent->client->ps.viewangles, ent->owner->s.angles );
992 			ent->owner->s.angles[PITCH] = 0;
993 			G_SetAngles( ent->owner, ent->owner->s.angles );
994 			VectorCopy( ent->owner->s.angles, ent->owner->pos1 );
995 		}
996 	}
997 
998 	// Remove the emplaced gun from our inventory
999 	ent->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_EMPLACED_GUN );
1000 
1001 extern void ChangeWeapon( gentity_t *ent, int newWeapon );
1002 extern void CG_ChangeWeapon( int num );
1003 	if ( ent->health <= 0 )
1004 	{//when die, don't set weapon back on when ejected from emplaced/eweb
1005 		//empty hands
1006 		ent->client->ps.weapon = WP_NONE;
1007 		if ( ent->NPC )
1008 		{
1009 			ChangeWeapon( ent, ent->client->ps.weapon );	// should be OK actually.
1010 		}
1011 		else
1012 		{
1013 			CG_ChangeWeapon( ent->client->ps.weapon );
1014 		}
1015 		if ( ent->s.number < MAX_CLIENTS )
1016 		{
1017 			gi.cvar_set( "cg_thirdperson", "1" );
1018 		}
1019 	}
1020 	else
1021 	{
1022 		// when we lock or unlock from the the gun, we get our old weapon back
1023 		ent->client->ps.weapon = ent->owner->s.weapon;
1024 
1025 		if ( ent->NPC )
1026 		{//BTW, if a saber-using NPC ever gets off of an emplaced gun/eweb, this will not work, look at NPC_ChangeWeapon for the proper way
1027 			ChangeWeapon( ent, ent->client->ps.weapon );
1028 		}
1029 		else
1030 		{
1031 			G_RemoveWeaponModels( ent );
1032 			CG_ChangeWeapon( ent->client->ps.weapon );
1033 			if ( ent->client->ps.weapon == WP_SABER )
1034 			{
1035 				WP_SaberAddG2SaberModels( ent );
1036 			}
1037 			else
1038 			{
1039 				G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
1040 			}
1041 
1042 			if ( ent->s.number < MAX_CLIENTS )
1043 			{
1044 				if ( ent->client->ps.weapon == WP_SABER )
1045 				{
1046 					gi.cvar_set( "cg_thirdperson", "1" );
1047 				}
1048 				else if ( ent->client->ps.weapon != WP_SABER && cg_gunAutoFirst.integer )
1049 				{
1050 					gi.cvar_set( "cg_thirdperson", "0" );
1051 				}
1052 			}
1053 		}
1054 
1055 		if ( ent->client->ps.weapon == WP_SABER )
1056 		{
1057 			if ( ent->owner->alt_fire )
1058 			{
1059 				ent->client->ps.SaberActivate();
1060 			}
1061 			else
1062 			{
1063 				ent->client->ps.SaberDeactivate();
1064 			}
1065 		}
1066 	}
1067 	//set the emplaced gun/eweb's weapon back to the emplaced gun
1068 	ent->owner->s.weapon = WP_EMPLACED_GUN;
1069 //	gi.G2API_DetachG2Model( &ent->ghoul2[ent->playerModel] );
1070 
1071 	ent->s.eFlags &= ~EF_LOCKED_TO_WEAPON;
1072 	ent->client->ps.eFlags &= ~EF_LOCKED_TO_WEAPON;
1073 
1074 	ent->owner->noDamageTeam = TEAM_FREE;
1075 	ent->owner->svFlags &= ~SVF_NONNPC_ENEMY;
1076 	ent->owner->delay = level.time;
1077 	ent->owner->activator = NULL;
1078 
1079 	if ( !ent->NPC )
1080 	{
1081 		// by keeping the owner, a dead npc can be pushed out of the chair without colliding with it
1082 		ent->owner = NULL;
1083 	}
1084 }
1085 
RunEmplacedWeapon(gentity_t * ent,usercmd_t ** ucmd)1086 void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd )
1087 {
1088 	if (( (*ucmd)->buttons & BUTTON_USE || (*ucmd)->forwardmove < 0 || (*ucmd)->upmove > 0 ) && ent->owner && ent->owner->delay + 500 < level.time )
1089 	{
1090 		ent->owner->s.loopSound = 0;
1091 
1092 		if ( ent->owner->e_UseFunc == useF_eweb_use )//yeah, crappy way to check this, but...
1093 		{
1094 			G_Sound( ent, G_SoundIndex( "sound/weapons/eweb/eweb_dismount.mp3" ));
1095 		}
1096 		else
1097 		{
1098 			G_Sound( ent, G_SoundIndex( "sound/weapons/emplaced/emplaced_dismount.mp3" ));
1099 
1100 		}
1101 
1102 		ExitEmplacedWeapon( ent );
1103 		(*ucmd)->buttons &= ~BUTTON_USE;
1104 		if ( (*ucmd)->upmove > 0 )
1105 		{//don't actually jump
1106 			(*ucmd)->upmove = 0;
1107 		}
1108 	}
1109 	else
1110 	{
1111 		// this is a crappy way to put sounds on a moving eweb....
1112 		if ( ent->owner
1113 			&& ent->owner->e_UseFunc == useF_eweb_use )//yeah, crappy way to check this, but...
1114 		{
1115 			if ( !VectorCompare( ent->client->ps.viewangles, ent->owner->movedir ))
1116 			{
1117 				ent->owner->s.loopSound = G_SoundIndex( "sound/weapons/eweb/eweb_aim.wav" );
1118 				ent->owner->fly_sound_debounce_time = level.time;
1119 			}
1120 			else
1121 			{
1122 				if ( ent->owner->fly_sound_debounce_time + 100 <= level.time )
1123 				{
1124 					ent->owner->s.loopSound = 0;
1125 				}
1126 			}
1127 
1128 			VectorCopy( ent->client->ps.viewangles, ent->owner->movedir );
1129 		}
1130 
1131 		// don't allow movement, weapon switching, and most kinds of button presses
1132 		(*ucmd)->forwardmove = 0;
1133 		(*ucmd)->rightmove = 0;
1134 		(*ucmd)->upmove = 0;
1135 		(*ucmd)->buttons &= (BUTTON_ATTACK|BUTTON_ALT_ATTACK);
1136 
1137 		(*ucmd)->weapon = ent->client->ps.weapon; //WP_EMPLACED_GUN;
1138 
1139 		if ( ent->health <= 0 )
1140 		{
1141 			ExitEmplacedWeapon( ent );
1142 		}
1143 	}
1144 }
1145