g_emplaced.cpp 36 KB

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