g_trigger.cpp 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685
  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 "b_local.h"
  6. #include "anims.h"
  7. #define ENTDIST_PLAYER 1
  8. #define ENTDIST_NPC 2
  9. extern qboolean G_PointInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs );
  10. extern qboolean G_ClearTrace( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int ignore, int clipmask );
  11. extern qboolean SpotWouldTelefrag2( gentity_t *mover, vec3_t dest );
  12. extern qboolean PM_CrouchAnim( int anim );
  13. extern void Boba_FlyStart( gentity_t *self );
  14. extern qboolean Boba_Flying( gentity_t *self );
  15. void InitTrigger( gentity_t *self ) {
  16. if (!VectorCompare (self->s.angles, vec3_origin))
  17. G_SetMovedir (self->s.angles, self->movedir);
  18. gi.SetBrushModel( self, self->model );
  19. self->contents = CONTENTS_TRIGGER; // replaces the -1 from gi.SetBrushModel
  20. self->svFlags = SVF_NOCLIENT;
  21. if(self->spawnflags & 128)
  22. {
  23. self->svFlags |= SVF_INACTIVE;
  24. }
  25. }
  26. // the wait time has passed, so set back up for another activation
  27. void multi_wait( gentity_t *ent ) {
  28. ent->nextthink = 0;
  29. }
  30. // the trigger was just activated
  31. // ent->activator should be set to the activator so it can be held through a delay
  32. // so wait for the delay time before firing
  33. void multi_trigger_run( gentity_t *ent )
  34. {
  35. ent->e_ThinkFunc = thinkF_NULL;
  36. G_ActivateBehavior( ent, BSET_USE );
  37. if ( ent->soundSet && ent->soundSet[0] )
  38. {
  39. gi.SetConfigstring( CS_AMBIENT_SET, ent->soundSet );
  40. }
  41. G_UseTargets (ent, ent->activator);
  42. if ( ent->noise_index )
  43. {
  44. G_Sound( ent->activator, ent->noise_index );
  45. }
  46. if ( ent->target2 && ent->target2[0] && ent->wait >= 0 )
  47. {
  48. ent->e_ThinkFunc = thinkF_trigger_cleared_fire;
  49. ent->nextthink = level.time + ent->speed;
  50. }
  51. else if ( ent->wait > 0 )
  52. {
  53. if ( ent->painDebounceTime != level.time )
  54. {//first ent to touch it this frame
  55. //ent->e_ThinkFunc = thinkF_multi_wait;
  56. ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
  57. ent->painDebounceTime = level.time;
  58. }
  59. }
  60. else if ( ent->wait < 0 )
  61. {
  62. // we can't just remove (self) here, because this is a touch function
  63. // called while looping through area links...
  64. ent->contents &= ~CONTENTS_TRIGGER;//so the EntityContact trace doesn't have to be done against me
  65. ent->e_TouchFunc = touchF_NULL;
  66. ent->e_UseFunc = useF_NULL;
  67. //Don't remove, Icarus may barf?
  68. //ent->nextthink = level.time + FRAMETIME;
  69. //ent->think = G_FreeEntity;
  70. }
  71. if( ent->activator && ent->activator->s.number == 0 )
  72. { // mark the trigger as being touched by the player
  73. ent->aimDebounceTime = level.time;
  74. }
  75. }
  76. void multi_trigger( gentity_t *ent, gentity_t *activator )
  77. {
  78. if ( ent->e_ThinkFunc == thinkF_multi_trigger_run )
  79. {//already triggered, just waiting to run
  80. return;
  81. }
  82. if ( ent->nextthink > level.time )
  83. {
  84. if( ent->spawnflags & 2048 ) // MULTIPLE - allow multiple entities to touch this trigger in a single frame
  85. {
  86. if ( ent->painDebounceTime && ent->painDebounceTime != level.time )
  87. {//this should still allow subsequent ents to fire this trigger in the current frame
  88. return; // can't retrigger until the wait is over
  89. }
  90. }
  91. else
  92. {
  93. return;
  94. }
  95. }
  96. if ( ent->spawnflags & 32)
  97. {
  98. ent->nextthink = level.time + ent->delay;
  99. // trace_t viewTrace;
  100. // gi.trace(&viewTrace, ent->currentOrigin, 0, 0, activator->currentOrigin, ent->s.number, MASK_SHOT);
  101. // if ((viewTrace.allsolid) || (viewTrace.startsolid) || (viewTrace.entityNum!=activator->s.number))
  102. // {
  103. // return;
  104. // }
  105. }
  106. // if the player has already activated this trigger this frame
  107. if( activator && !activator->s.number && ent->aimDebounceTime == level.time )
  108. {
  109. return;
  110. }
  111. if ( ent->svFlags & SVF_INACTIVE )
  112. {//Not active at this time
  113. return;
  114. }
  115. ent->activator = activator;
  116. if(ent->delay && ent->painDebounceTime < (level.time + ent->delay) )
  117. {//delay before firing trigger
  118. ent->e_ThinkFunc = thinkF_multi_trigger_run;
  119. ent->nextthink = level.time + ent->delay;
  120. ent->painDebounceTime = level.time;
  121. }
  122. else
  123. {
  124. multi_trigger_run (ent);
  125. }
  126. }
  127. void Use_Multi( gentity_t *ent, gentity_t *other, gentity_t *activator )
  128. {
  129. multi_trigger( ent, activator );
  130. }
  131. extern int Pilot_ActivePilotCount(void);
  132. void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace )
  133. {
  134. if( !other->client )
  135. {
  136. return;
  137. }
  138. if ( self->svFlags & SVF_INACTIVE )
  139. {//set by target_deactivate
  140. return;
  141. }
  142. if( self->noDamageTeam )
  143. {
  144. if ( other->client->playerTeam != self->noDamageTeam )
  145. {
  146. return;
  147. }
  148. }
  149. // moved to just above multi_trigger because up here it just checks if the trigger is not being touched
  150. // we want it to check any conditions set on the trigger, if one of those isn't met, the trigger is considered to be "cleared"
  151. // if ( self->e_ThinkFunc == thinkF_trigger_cleared_fire )
  152. // {//We're waiting to fire our target2 first
  153. // self->nextthink = level.time + self->speed;
  154. // return;
  155. // }
  156. if ( self->spawnflags & 1 )
  157. {
  158. if ( other->s.number != 0 )
  159. {
  160. return;
  161. }
  162. }
  163. else
  164. {
  165. if ( self->spawnflags & 16 )
  166. {//NPCONLY
  167. if ( other->NPC == NULL )
  168. {
  169. return;
  170. }
  171. }
  172. if ( self->NPC_targetname && self->NPC_targetname[0] )
  173. {
  174. if ( other->script_targetname && other->script_targetname[0] )
  175. {
  176. if ( Q_stricmp( self->NPC_targetname, other->script_targetname ) != 0 )
  177. {//not the right guy to fire me off
  178. return;
  179. }
  180. }
  181. else
  182. {
  183. return;
  184. }
  185. }
  186. }
  187. if ( self->spawnflags & 4 )
  188. {//USE_BUTTON
  189. if ( !other->client )
  190. {
  191. return;
  192. }
  193. if( !( other->client->usercmd.buttons & BUTTON_USE ) )
  194. {//not pressing use button
  195. return;
  196. }
  197. }
  198. if ( self->spawnflags & 2 )
  199. {//FACING
  200. vec3_t forward;
  201. if ( other->client )
  202. {
  203. AngleVectors( other->client->ps.viewangles, forward, NULL, NULL );
  204. }
  205. else
  206. {
  207. AngleVectors( other->currentAngles, forward, NULL, NULL );
  208. }
  209. if ( DotProduct( self->movedir, forward ) < 0.5 )
  210. {//Not Within 45 degrees
  211. return;
  212. }
  213. }
  214. if ( self->spawnflags & 8 )
  215. {//FIRE_BUTTON
  216. if ( !other->client )
  217. {
  218. return;
  219. }
  220. if( !( other->client->ps.eFlags & EF_FIRING /*usercmd.buttons & BUTTON_ATTACK*/ ) &&
  221. !( other->client->ps.eFlags & EF_ALT_FIRING/*usercmd.buttons & BUTTON_ALT_ATTACK*/ ) )
  222. {//not pressing fire button or altfire button
  223. return;
  224. }
  225. //FIXME: do we care about the sniper rifle or not?
  226. if( other->s.number == 0 && ( other->client->ps.weapon > MAX_PLAYER_WEAPONS || other->client->ps.weapon <= WP_NONE ) )
  227. {//don't care about non-player weapons if this is the player
  228. return;
  229. }
  230. }
  231. if ( other->client && self->radius )
  232. {
  233. vec3_t eyeSpot;
  234. //Only works if your head is in it, but we allow leaning out
  235. //NOTE: We don't use CalcEntitySpot SPOT_HEAD because we don't want this
  236. //to be reliant on the physical model the player uses.
  237. VectorCopy(other->currentOrigin, eyeSpot);
  238. eyeSpot[2] += other->client->ps.viewheight;
  239. if ( G_PointInBounds( eyeSpot, self->absmin, self->absmax ) )
  240. {
  241. if( !( other->client->ps.eFlags & EF_FIRING ) &&
  242. !( other->client->ps.eFlags & EF_ALT_FIRING ) )
  243. {//not attacking, so hiding bonus
  244. //FIXME: should really have sound events clear the hiddenDist
  245. other->client->hiddenDist = self->radius;
  246. //NOTE: movedir HAS to be normalized!
  247. if ( VectorLength( self->movedir ) )
  248. {//They can only be hidden from enemies looking in this direction
  249. VectorCopy( self->movedir, other->client->hiddenDir );
  250. }
  251. else
  252. {
  253. VectorClear( other->client->hiddenDir );
  254. }
  255. }
  256. }
  257. }
  258. if ( self->spawnflags & 4 )
  259. {//USE_BUTTON
  260. NPC_SetAnim( other, SETANIM_TORSO, BOTH_BUTTON_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
  261. /*
  262. if ( !VectorLengthSquared( other->client->ps.velocity ) && !PM_CrouchAnim( other->client->ps.legsAnim ) )
  263. {
  264. NPC_SetAnim( other, SETANIM_LEGS, BOTH_BUTTON_HOLD, SETANIM_FLAG_NORMAL|SETANIM_FLAG_HOLD );
  265. }
  266. */
  267. //other->client->ps.weaponTime = other->client->ps.torsoAnimTimer;
  268. }
  269. if ( self->e_ThinkFunc == thinkF_trigger_cleared_fire )
  270. {//We're waiting to fire our target2 first
  271. self->nextthink = level.time + self->speed;
  272. return;
  273. }
  274. if ( self->spawnflags & 32)
  275. {
  276. if (Pilot_ActivePilotCount()>=self->lastInAirTime)
  277. {
  278. return;
  279. }
  280. }
  281. multi_trigger( self, other );
  282. }
  283. void trigger_cleared_fire (gentity_t *self)
  284. {
  285. G_UseTargets2( self, self->activator, self->target2 );
  286. self->e_ThinkFunc = thinkF_NULL;
  287. // should start the wait timer now, because the trigger's just been cleared, so we must "wait" from this point
  288. if ( self->wait > 0 )
  289. {
  290. self->nextthink = level.time + ( self->wait + self->random * crandom() ) * 1000;
  291. }
  292. }
  293. qboolean G_TriggerActive( gentity_t *self )
  294. {
  295. if ( self->svFlags & SVF_INACTIVE )
  296. {//set by target_deactivate
  297. return qfalse;
  298. }
  299. if ( self->spawnflags & 1 )
  300. {//player only
  301. return qfalse;
  302. }
  303. /*
  304. ???
  305. if ( self->spawnflags & 4 )
  306. {//USE_BUTTON
  307. return qfalse;
  308. }
  309. */
  310. /*
  311. ???
  312. if ( self->spawnflags & 8 )
  313. {//FIRE_BUTTON
  314. return qfalse;
  315. }
  316. */
  317. /*
  318. if ( self->radius )
  319. {//Only works if your head is in it, but we allow leaning out
  320. //NOTE: We don't use CalcEntitySpot SPOT_HEAD because we don't want this
  321. //to be reliant on the physical model the player uses.
  322. return qfalse;
  323. }
  324. */
  325. return qtrue;
  326. }
  327. /*QUAKED trigger_multiple (.1 .5 .1) ? PLAYERONLY FACING USE_BUTTON FIRE_BUTTON NPCONLY LIMITED_PILOT x INACTIVE MULTIPLE
  328. PLAYERONLY - only a player can trigger this by touch
  329. FACING - Won't fire unless triggering ent's view angles are within 45 degrees of trigger's angles (in addition to any other conditions)
  330. USE_BUTTON - Won't fire unless player is in it and pressing use button (in addition to any other conditions)
  331. FIRE_BUTTON - Won't fire unless player/NPC is in it and pressing fire button (in addition to any other conditions)
  332. NPCONLY - only non-player NPCs can trigger this by touch
  333. LIMITED_PILOT - only spawn if there are open pilot slots
  334. INACTIVE - Start off, has to be activated to be touchable/usable
  335. MULTIPLE - multiple entities can touch this trigger in a single frame *and* if needed, the trigger can have a wait of > 0
  336. "wait" Seconds between triggerings, 0 default, number < 0 means one time only.
  337. "random" wait variance, default is 0
  338. "delay" how many seconds to wait to fire targets after tripped
  339. "hiderange" As long as NPC's head is in this trigger, NPCs out of this hiderange cannot see him. If you set an angle on the trigger, they're only hidden from enemies looking in that direction. the player's crouch viewheight is 36, his standing viewheight is 54. So a trigger thast should hide you when crouched but not standing should be 48 tall.
  340. "target2" The trigger will fire this only when the trigger has been activated and subsequently 'cleared'( once any of the conditions on the trigger have not been satisfied). This will not fire the "target" more than once until the "target2" is fired (trigger field is 'cleared')
  341. "speed" How many seconds to wait to fire the target2, default is 1
  342. "noise" Sound to play when the trigger fires (plays at activator's origin)
  343. "max_pilots" Number of pilots this spawner will allow
  344. Variable sized repeatable trigger. Must be targeted at one or more entities.
  345. so, the basic time between firing is a random time between
  346. (wait - random) and (wait + random)
  347. "NPC_targetname" - If set, only an NPC with a matching NPC_targetname will trip this trigger
  348. "team" - If set, only this team can trip this trigger
  349. player
  350. enemy
  351. neutral
  352. "soundSet" Ambient sound set to play when this trigger is activated
  353. */
  354. void SP_trigger_multiple( gentity_t *ent )
  355. {
  356. char buffer[MAX_QPATH];
  357. char *s;
  358. if ( G_SpawnString( "noise", "*NOSOUND*", &s ) )
  359. {
  360. Q_strncpyz( buffer, s, sizeof(buffer) );
  361. COM_DefaultExtension( buffer, sizeof(buffer), ".wav");
  362. ent->noise_index = G_SoundIndex(buffer);
  363. }
  364. G_SpawnFloat( "wait", "0", &ent->wait );//was 0.5 ... but that means wait can never be zero... we should probably put it back to 0.5, though...
  365. G_SpawnFloat( "random", "0", &ent->random );
  366. G_SpawnInt( "max_pilots", "2", &ent->lastInAirTime );
  367. if ( (ent->wait > 0) && (ent->random >= ent->wait) ) {
  368. ent->random = ent->wait - FRAMETIME;
  369. gi.Printf(S_COLOR_YELLOW"trigger_multiple has random >= wait\n");
  370. }
  371. ent->delay *= 1000;//1 = 1 msec, 1000 = 1 sec
  372. if ( !ent->speed && ent->target2 && ent->target2[0] )
  373. {
  374. ent->speed = 1000;
  375. }
  376. else
  377. {
  378. ent->speed *= 1000;
  379. }
  380. ent->e_TouchFunc = touchF_Touch_Multi;
  381. ent->e_UseFunc = useF_Use_Multi;
  382. if ( ent->team && ent->team[0] )
  383. {
  384. ent->noDamageTeam = (team_t)GetIDForString( TeamTable, ent->team );
  385. ent->team = NULL;
  386. }
  387. InitTrigger( ent );
  388. gi.linkentity (ent);
  389. }
  390. /*QUAKED trigger_once (.5 1 .5) ? PLAYERONLY FACING USE_BUTTON FIRE_BUTTON NPCONLY x x INACTIVE MULTIPLE
  391. PLAYERONLY - only a player can trigger this by touch
  392. FACING - Won't fire unless triggering ent's view angles are within 45 degrees of trigger's angles (in addition to any other conditions)
  393. USE_BUTTON - Won't fire unless player is in it and pressing use button (in addition to any other conditions)
  394. FIRE_BUTTON - Won't fire unless player/NPC is in it and pressing fire button (in addition to any other conditions)
  395. NPCONLY - only non-player NPCs can trigger this by touch
  396. INACTIVE - Start off, has to be activated to be touchable/usable
  397. MULTIPLE - multiple entities can touch this trigger in a single frame *and* if needed, the trigger can have a wait of > 0
  398. "random" wait variance, default is 0
  399. "delay" how many seconds to wait to fire targets after tripped
  400. Variable sized repeatable trigger. Must be targeted at one or more entities.
  401. so, the basic time between firing is a random time between
  402. (wait - random) and (wait + random)
  403. "noise" Sound to play when the trigger fires (plays at activator's origin)
  404. "NPC_targetname" - If set, only an NPC with a matching NPC_targetname will trip this trigger
  405. "team" - If set, only this team can trip this trigger
  406. player
  407. enemy
  408. neutral
  409. "soundSet" Ambient sound set to play when this trigger is activated
  410. */
  411. void SP_trigger_once( gentity_t *ent )
  412. {
  413. char buffer[MAX_QPATH];
  414. char *s;
  415. if ( G_SpawnString( "noise", "*NOSOUND*", &s ) )
  416. {
  417. Q_strncpyz( buffer, s, sizeof(buffer) );
  418. COM_DefaultExtension( buffer, sizeof(buffer), ".wav");
  419. ent->noise_index = G_SoundIndex(buffer);
  420. }
  421. ent->wait = -1;
  422. ent->e_TouchFunc = touchF_Touch_Multi;
  423. ent->e_UseFunc = useF_Use_Multi;
  424. if ( ent->team && ent->team[0] )
  425. {
  426. ent->noDamageTeam = (team_t)GetIDForString( TeamTable, ent->team );
  427. ent->team = NULL;
  428. }
  429. ent->delay *= 1000;//1 = 1 msec, 1000 = 1 sec
  430. InitTrigger( ent );
  431. gi.linkentity (ent);
  432. }
  433. /*QUAKED trigger_bidirectional (.1 .5 .1) ? PLAYER_ONLY x x x x x x INACTIVE
  434. NOT IMPLEMENTED
  435. INACTIVE - Start off, has to be activated to be touchable/usable
  436. set "angle" for forward direction
  437. Fires "target" when someone moves through it in direction of angle
  438. Fires "backwardstarget" when someone moves through it in the opposite direction of angle
  439. "NPC_targetname" - If set, only an NPC with a matching NPC_targetname will trip this trigger
  440. "wait" - how long to wait between triggerings
  441. TODO:
  442. count
  443. */
  444. void SP_trigger_bidirectional( gentity_t *ent )
  445. {
  446. G_FreeEntity(ent);
  447. //FIXME: Implement
  448. /* if(!ent->wait)
  449. {
  450. ent->wait = -1;
  451. }
  452. ent->touch = Touch_Multi;
  453. ent->use = Use_Multi;
  454. InitTrigger( ent );
  455. gi.linkentity (ent);
  456. */
  457. }
  458. /*QUAKED trigger_location (.1 .5 .1) ?
  459. When an ent is asked for it's location, it will return this ent's "message" field if it is in it.
  460. "message" - location name
  461. NOTE: always rectangular
  462. */
  463. char *G_GetLocationForEnt( gentity_t *ent )
  464. {
  465. vec3_t mins, maxs;
  466. gentity_t *found = NULL;
  467. VectorAdd( ent->currentOrigin, ent->mins, mins );
  468. VectorAdd( ent->currentOrigin, ent->maxs, maxs );
  469. while( (found = G_Find(found, FOFS(classname), "trigger_location")) != NULL )
  470. {
  471. if ( gi.EntityContact( mins, maxs, found ) )
  472. {
  473. return found->message;
  474. }
  475. }
  476. return NULL;
  477. }
  478. void SP_trigger_location( gentity_t *ent )
  479. {
  480. if ( !ent->message || !ent->message[0] )
  481. {
  482. gi.Printf("WARNING: trigger_location with no message!\n");
  483. G_FreeEntity(ent);
  484. return;
  485. }
  486. gi.SetBrushModel( ent, ent->model );
  487. ent->contents = 0;
  488. ent->svFlags = SVF_NOCLIENT;
  489. gi.linkentity (ent);
  490. }
  491. /*
  492. ==============================================================================
  493. trigger_always
  494. ==============================================================================
  495. */
  496. void trigger_always_think( gentity_t *ent ) {
  497. G_UseTargets(ent, ent);
  498. G_FreeEntity( ent );
  499. }
  500. /*QUAKED trigger_always (.1 .5 .1) (-8 -8 -8) (8 8 8)
  501. This trigger will always fire. It is activated by the world.
  502. */
  503. void SP_trigger_always (gentity_t *ent) {
  504. // we must have some delay to make sure our use targets are present
  505. ent->nextthink = level.time + 300;
  506. ent->e_ThinkFunc = thinkF_trigger_always_think;
  507. }
  508. /*
  509. ==============================================================================
  510. trigger_push
  511. ==============================================================================
  512. */
  513. #define PUSH_CONVEYOR 32
  514. void trigger_push_touch (gentity_t *self, gentity_t *other, trace_t *trace ) {
  515. if ( self->svFlags & SVF_INACTIVE )
  516. {//set by target_deactivate
  517. return;
  518. }
  519. if( level.time < self->painDebounceTime + self->wait ) // normal 'wait' check
  520. {
  521. if( self->spawnflags & 2048 ) // MULTIPLE - allow multiple entities to touch this trigger in one frame
  522. {
  523. if ( self->painDebounceTime && level.time > self->painDebounceTime ) // if we haven't reached the next frame continue to let ents touch the trigger
  524. {
  525. return;
  526. }
  527. }
  528. else // only allowing one ent per frame to touch trigger
  529. {
  530. return;
  531. }
  532. }
  533. // if the player has already activated this trigger this frame
  534. if( other && !other->s.number && self->aimDebounceTime == level.time )
  535. {
  536. return;
  537. }
  538. if( self->spawnflags & PUSH_CONVEYOR )
  539. { // only push player if he's on the ground
  540. if( other->s.groundEntityNum == ENTITYNUM_NONE )
  541. {
  542. return;
  543. }
  544. }
  545. if ( self->spawnflags & 1 )
  546. {//PLAYERONLY
  547. if ( other->s.number != 0 )
  548. {
  549. return;
  550. }
  551. }
  552. else
  553. {
  554. if ( self->spawnflags & 8 )
  555. {//NPCONLY
  556. if ( other->NPC == NULL )
  557. {
  558. return;
  559. }
  560. }
  561. }
  562. if ( !other->client ) {
  563. if ( other->s.pos.trType != TR_STATIONARY && other->s.pos.trType != TR_LINEAR_STOP && other->s.pos.trType != TR_NONLINEAR_STOP && VectorLengthSquared( other->s.pos.trDelta ) )
  564. {//already moving
  565. VectorCopy( other->currentOrigin, other->s.pos.trBase );
  566. VectorCopy( self->s.origin2, other->s.pos.trDelta );
  567. other->s.pos.trTime = level.time;
  568. }
  569. return;
  570. }
  571. if ( other->client->ps.pm_type != PM_NORMAL ) {
  572. return;
  573. }
  574. if ( (self->spawnflags&16) )
  575. {//relative, dir to it * speed
  576. vec3_t dir;
  577. VectorSubtract( self->s.origin2, other->currentOrigin, dir );
  578. if ( self->speed )
  579. {
  580. VectorNormalize( dir );
  581. VectorScale( dir, self->speed, dir );
  582. }
  583. VectorCopy( dir, other->client->ps.velocity );
  584. }
  585. else if ( (self->spawnflags&4) )
  586. {//linear dir * speed
  587. VectorScale( self->s.origin2, self->speed, other->client->ps.velocity );
  588. }
  589. else
  590. {
  591. VectorCopy( self->s.origin2, other->client->ps.velocity );
  592. }
  593. //so we don't take damage unless we land lower than we start here...
  594. other->client->ps.forceJumpZStart = 0;
  595. other->client->ps.pm_flags |= PMF_TRIGGER_PUSHED;//pushed by a trigger
  596. other->client->ps.jumpZStart = other->client->ps.origin[2];
  597. if ( self->wait == -1 )
  598. {
  599. self->e_TouchFunc = touchF_NULL;
  600. }
  601. else if ( self->wait > 0 )
  602. {
  603. self->painDebounceTime = level.time;
  604. }
  605. if( other && !other->s.number )
  606. { // mark that the player has activated this trigger this frame
  607. self->aimDebounceTime =level.time;
  608. }
  609. }
  610. #define PUSH_CONSTANT 2
  611. /*
  612. =================
  613. AimAtTarget
  614. Calculate origin2 so the target apogee will be hit
  615. =================
  616. */
  617. void AimAtTarget( gentity_t *self )
  618. {
  619. gentity_t *ent;
  620. vec3_t origin;
  621. float height, gravity, time, forward;
  622. float dist;
  623. VectorAdd( self->absmin, self->absmax, origin );
  624. VectorScale ( origin, 0.5, origin );
  625. ent = G_PickTarget( self->target );
  626. if ( !ent )
  627. {
  628. G_FreeEntity( self );
  629. return;
  630. }
  631. if ( self->classname && !Q_stricmp( "trigger_push", self->classname ) )
  632. {
  633. if ( (self->spawnflags&2) )
  634. {//check once a second to see if we should activate or deactivate ourselves
  635. self->e_ThinkFunc = thinkF_trigger_push_checkclear;
  636. self->nextthink = level.time + FRAMETIME;
  637. }
  638. if ( (self->spawnflags&16) )
  639. {//relative, not an arc or linear
  640. VectorCopy( ent->currentOrigin, self->s.origin2 );
  641. return;
  642. }
  643. else if ( (self->spawnflags&4) )
  644. {//linear, not an arc
  645. VectorSubtract( ent->currentOrigin, origin, self->s.origin2 );
  646. VectorNormalize( self->s.origin2 );
  647. return;
  648. }
  649. }
  650. if ( self->classname && !Q_stricmp( "target_push", self->classname ) )
  651. {
  652. if( self->spawnflags & PUSH_CONSTANT )
  653. {
  654. VectorSubtract ( ent->s.origin, self->s.origin, self->s.origin2 );
  655. VectorNormalize( self->s.origin2);
  656. VectorScale (self->s.origin2, self->speed, self->s.origin2);
  657. return;
  658. }
  659. }
  660. height = ent->s.origin[2] - origin[2];
  661. if ( height < 0 )
  662. {//sqrt of negative is bad!
  663. height = 0;
  664. }
  665. gravity = g_gravity->value;
  666. if ( gravity < 0 )
  667. {
  668. gravity = 0;
  669. }
  670. time = sqrt( height / ( .5 * gravity ) );
  671. if ( !time ) {
  672. G_FreeEntity( self );
  673. return;
  674. }
  675. // set s.origin2 to the push velocity
  676. VectorSubtract ( ent->s.origin, origin, self->s.origin2 );
  677. self->s.origin2[2] = 0;
  678. dist = VectorNormalize( self->s.origin2);
  679. forward = dist / time;
  680. VectorScale( self->s.origin2, forward, self->s.origin2 );
  681. self->s.origin2[2] = time * gravity;
  682. }
  683. void trigger_push_checkclear( gentity_t *self )
  684. {
  685. trace_t trace;
  686. vec3_t center;
  687. self->nextthink = level.time + 500;
  688. VectorAdd( self->absmin, self->absmax, center );
  689. VectorScale( center, 0.5, center );
  690. gentity_t *target = G_Find( NULL, FOFS(targetname), self->target );
  691. gi.trace( &trace, center, vec3_origin, vec3_origin, target->currentOrigin, ENTITYNUM_NONE, CONTENTS_SOLID );
  692. if ( trace.fraction >= 1.0f )
  693. {//can trace, turn on
  694. self->contents |= CONTENTS_TRIGGER;//so the EntityContact trace doesn't have to be done against me
  695. self->e_TouchFunc = touchF_trigger_push_touch;
  696. gi.linkentity( self );
  697. }
  698. else
  699. {//no trace, turn off
  700. self->contents &= ~CONTENTS_TRIGGER;//so the EntityContact trace doesn't have to be done against me
  701. self->e_TouchFunc = touchF_NULL;
  702. gi.unlinkentity( self );
  703. }
  704. }
  705. /*QUAKED trigger_push (.1 .5 .1) ? PLAYERONLY CHECKCLEAR LINEAR NPCONLY RELATIVE CONVEYOR x INACTIVE MULTIPLE
  706. Must point at a target_position, which will be the apex of the leap.
  707. This will be client side predicted, unlike target_push
  708. PLAYERONLY - only the player is affected
  709. LINEAR - Instead of tossing the client at the target_position, it will push them towards it. Must set a "speed" (see below)
  710. CHECKCLEAR - Every 1 second, it will check to see if it can trace to the target_position, if it can, the trigger is touchable, if it can't, the trigger is not touchable
  711. NPCONLY - only NPCs are affected
  712. RELATIVE - instead of pushing you in a direction that is always from the center of the trigger to the target_position, it pushes *you* toward the target position, relative to your current location (can use with "speed"... if don't set a speed, it will use the distance from you to the target_position)
  713. CONVEYOR - acts like a conveyor belt, will only push if player is on the ground ( should probably use RELATIVE also, if you want a true conveyor belt )
  714. INACTIVE - not active until targeted by a target_activate
  715. MULTIPLE - multiple entities can touch this trigger in a single frame *and* if needed, the trigger can have a wait of > 0
  716. wait - how long to wait between pushes: -1 = push only once
  717. speed - when used with the LINEAR spawnflag, pushes the client toward the position at a constant speed (default is 1000)
  718. */
  719. void SP_trigger_push( gentity_t *self ) {
  720. InitTrigger (self);
  721. if ( self->wait > 0 )
  722. {
  723. self->wait *= 1000;
  724. }
  725. // unlike other triggers, we need to send this one to the client
  726. self->svFlags &= ~SVF_NOCLIENT;
  727. self->s.eType = ET_PUSH_TRIGGER;
  728. if ( !(self->spawnflags&2) )
  729. {//start on
  730. self->e_TouchFunc = touchF_trigger_push_touch;
  731. }
  732. if ( self->spawnflags & 4 )
  733. {//linear
  734. self->speed = 1000;
  735. }
  736. self->e_ThinkFunc = thinkF_AimAtTarget;
  737. self->nextthink = level.time + START_TIME_LINK_ENTS;
  738. gi.linkentity (self);
  739. }
  740. void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator ) {
  741. if ( !activator->client ) {
  742. return;
  743. }
  744. if ( activator->client->ps.pm_type != PM_NORMAL ) {
  745. return;
  746. }
  747. G_ActivateBehavior(self,BSET_USE);
  748. VectorCopy( self->s.origin2, activator->client->ps.velocity );
  749. if( self->spawnflags & 4 ) // lower
  750. {
  751. // reset this so I don't take falling damage when I land
  752. activator->client->ps.jumpZStart = activator->currentOrigin[2];
  753. }
  754. //so we don't take damage unless we land lower than we start here...
  755. activator->client->ps.forceJumpZStart = 0;
  756. activator->client->ps.pm_flags |= PMF_TRIGGER_PUSHED;//pushed by a trigger
  757. // play fly sound every 1.5 seconds
  758. if ( self->noise_index && activator->fly_sound_debounce_time < level.time ) {
  759. activator->fly_sound_debounce_time = level.time + 1500;
  760. G_Sound( activator, self->noise_index );
  761. }
  762. }
  763. /*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) ENERGYNOISE CONSTANT NO_DAMAGE
  764. When triggered, pushes the activator in the direction of angles
  765. "speed" defaults to 1000
  766. ENERGYNOISE plays energy noise
  767. CONSTANT will push activator in direction of 'target' at constant 'speed'
  768. NO_DAMAGE the activator won't take falling damage after being pushed
  769. */
  770. void SP_target_push( gentity_t *self ) {
  771. if (!self->speed) {
  772. self->speed = 1000;
  773. }
  774. G_SetMovedir (self->s.angles, self->s.origin2);
  775. VectorScale (self->s.origin2, self->speed, self->s.origin2);
  776. if ( self->spawnflags & 1 ) {
  777. //self->noise_index = G_SoundIndex("sound/ambience/forge/antigrav.wav");
  778. }
  779. if ( self->target ) {
  780. VectorCopy( self->s.origin, self->absmin );
  781. VectorCopy( self->s.origin, self->absmax );
  782. self->e_ThinkFunc = thinkF_AimAtTarget;
  783. self->nextthink = level.time + START_TIME_LINK_ENTS;
  784. }
  785. self->e_UseFunc = useF_Use_target_push;
  786. }
  787. /*
  788. ==============================================================================
  789. trigger_teleport
  790. ==============================================================================
  791. */
  792. #define SNAP_ANGLES 1
  793. #define NO_MISSILES 2
  794. #define NO_NPCS 4
  795. #define TTSF_STASIS 8
  796. #define TTSF_DEAD_OK 16
  797. void TeleportMover( gentity_t *mover, vec3_t origin, vec3_t diffAngles, qboolean snapAngle );
  798. void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace )
  799. {
  800. gentity_t *dest;
  801. if ( self->svFlags & SVF_INACTIVE )
  802. {//set by target_deactivate
  803. return;
  804. }
  805. dest = G_PickTarget( self->target );
  806. if (!dest)
  807. {
  808. gi.Printf ("Couldn't find teleporter destination\n");
  809. return;
  810. }
  811. if ( other->client )
  812. {
  813. if ( other->client->ps.pm_type == PM_DEAD )
  814. {
  815. if ( !(self->spawnflags&TTSF_DEAD_OK) )
  816. {//dead men can't teleport
  817. return;
  818. }
  819. }
  820. if ( other->NPC )
  821. {
  822. if ( self->spawnflags & NO_NPCS )
  823. {
  824. return;
  825. }
  826. }
  827. if ( other->client->playerTeam != TEAM_FREE && SpotWouldTelefrag2( other, dest->currentOrigin ) )//SpotWouldTelefrag( dest, other->client->playerTeam ) )
  828. {//Don't go through if something blocking on the other side
  829. return;
  830. }
  831. TeleportPlayer( other, dest->s.origin, dest->s.angles );
  832. }
  833. //FIXME: check for SVF_NO_TELEPORT
  834. else if ( !(self->svFlags & SVF_NO_TELEPORT) && !(self->spawnflags & NO_MISSILES) && VectorLengthSquared( other->s.pos.trDelta ) )
  835. {//It's a mover of some sort and is currently moving
  836. vec3_t diffAngles = {0, 0, 0};
  837. qboolean snap = qfalse;
  838. if ( self->lastEnemy )
  839. {
  840. VectorSubtract( dest->s.angles, self->lastEnemy->s.angles, diffAngles );
  841. }
  842. else
  843. {//snaps to angle
  844. VectorSubtract( dest->s.angles, other->currentAngles, diffAngles );
  845. snap = qtrue;
  846. }
  847. TeleportMover( other, dest->s.origin, diffAngles, snap );
  848. }
  849. }
  850. void trigger_teleporter_find_closest_portal( gentity_t *self )
  851. {
  852. gentity_t *found = NULL;
  853. vec3_t org, vec;
  854. float dist, bestDist = 64*64;
  855. VectorAdd( self->mins, self->maxs, org );
  856. VectorScale( org, 0.5, org );
  857. while ( (found = G_Find( found, FOFS(classname), "misc_portal_surface" )) != NULL )
  858. {
  859. VectorSubtract( found->currentOrigin, org, vec );
  860. dist = VectorLengthSquared( vec );
  861. if ( dist < bestDist )
  862. {
  863. self->lastEnemy = found;
  864. bestDist = dist;
  865. }
  866. }
  867. if ( self->lastEnemy )
  868. {
  869. gi.Printf("trigger_teleporter found misc_portal_surface\n");
  870. }
  871. self->e_ThinkFunc = thinkF_NULL;
  872. }
  873. /*QUAKED trigger_teleport (.1 .5 .1) ? SNAP_ANGLES NO_MISSILES NO_NPCS STASIS DEAD_OK x x INACTIVE
  874. Allows client side prediction of teleportation events.
  875. Must point at a target_position, which will be the teleport destination.
  876. SNAP_ANGLES - Make the entity that passes through snap to the target_position's angles
  877. NO_MISSILES - Missiles and thrown objects cannot pass through
  878. NO_NPCS - NPCs cannot pass through
  879. STASIS - will play stasis teleport sound and fx instead of starfleet
  880. DEAD_OK - even if dead, you will teleport
  881. */
  882. void SP_trigger_teleport( gentity_t *self )
  883. {
  884. InitTrigger (self);
  885. // unlike other triggers, we need to send this one to the client
  886. self->svFlags &= ~SVF_NOCLIENT;
  887. self->s.eType = ET_TELEPORT_TRIGGER;
  888. self->e_TouchFunc = touchF_trigger_teleporter_touch;
  889. self->e_ThinkFunc = thinkF_trigger_teleporter_find_closest_portal;
  890. self->nextthink = level.time + START_TIME_LINK_ENTS;
  891. gi.linkentity (self);
  892. }
  893. /*
  894. ==============================================================================
  895. trigger_hurt
  896. ==============================================================================
  897. */
  898. /*QUAKED trigger_hurt (.1 .5 .1) ? START_OFF PLAYERONLY SILENT NO_PROTECTION LOCKCAM FALLING ELECTRICAL INACTIVE MULTIPLE
  899. Any entity that touches this will be hurt.
  900. It does dmg points of damage each server frame
  901. PLAYERONLY only the player is hurt by it
  902. SILENT supresses playing the sound
  903. NO_PROTECTION *nothing* stops the damage
  904. LOCKCAM Falling death results in camera locking in place
  905. FALLING Forces a falling scream and anim
  906. ELECTRICAL does electrical damage
  907. INACTIVE Cannot be triggered until used by a target_activate
  908. MULTIPLE multiple entities can touch this trigger in a single frame *and* if needed, the trigger can have a wait of > 0
  909. "dmg" default 5 (whole numbers only)
  910. "delay" How many seconds it takes to get from 0 to "dmg" (default is 0)
  911. "wait" Use in instead of "SLOW" - determines how often the player gets hurt, 0.1 is every frame, 1.0 is once per second. -1 will stop after one use
  912. "count" If set, FALLING death causes a fade to black in this many milliseconds (default is 10000 = 10 seconds)
  913. "NPC_targetname" - If set, only an NPC with a matching NPC_targetname will trip this trigger
  914. "noise" sound to play when it hurts something ( default: "sound/world/electro" )
  915. */
  916. void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
  917. G_ActivateBehavior(self,BSET_USE);
  918. //FIXME: Targeting the trigger will toggle its on / off state???
  919. if ( self->linked ) {
  920. gi.unlinkentity( self );
  921. } else {
  922. gi.linkentity( self );
  923. }
  924. }
  925. void trigger_hurt_reset (gentity_t *self)
  926. {
  927. self->attackDebounceTime = 0;
  928. self->e_ThinkFunc = thinkF_NULL;
  929. }
  930. extern void JET_FlyStart(gentity_t* actor);
  931. void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace )
  932. {
  933. int dflags;
  934. int actualDmg = self->damage;
  935. if ( self->svFlags & SVF_INACTIVE )
  936. {//set by target_deactivate
  937. return;
  938. }
  939. if ( !other->takedamage )
  940. {
  941. return;
  942. }
  943. if( level.time < self->painDebounceTime + self->wait ) // normal 'wait' check
  944. {
  945. if( self->spawnflags & 2048 ) // MULTIPLE - allow multiple entities to touch this trigger in one frame
  946. {
  947. if ( self->painDebounceTime && level.time > self->painDebounceTime ) // if we haven't reached the next frame continue to let ents touch the trigger
  948. {
  949. return;
  950. }
  951. }
  952. else // only allowing one ent per frame to touch trigger
  953. {
  954. return;
  955. }
  956. }
  957. // if the player has already activated this trigger this frame
  958. if( other && !other->s.number && self->aimDebounceTime == level.time )
  959. {
  960. return;
  961. }
  962. if ( self->spawnflags & 2 )
  963. {//player only
  964. if ( other->s.number )
  965. {
  966. return;
  967. }
  968. }
  969. if ( self->NPC_targetname && self->NPC_targetname[0] )
  970. {//I am for you, Kirk
  971. if ( other->script_targetname && other->script_targetname[0] )
  972. {//must have a name
  973. if ( Q_stricmp( self->NPC_targetname, other->script_targetname ) != 0 )
  974. {//not the right guy to fire me off
  975. return;
  976. }
  977. }
  978. else
  979. {//no name? No trigger.
  980. return;
  981. }
  982. }
  983. // play sound
  984. if ( !(self->spawnflags & 4) )
  985. {
  986. G_Sound( other, self->noise_index );
  987. }
  988. if ( self->spawnflags & 8 )
  989. {
  990. dflags = DAMAGE_NO_PROTECTION;
  991. }
  992. else
  993. {
  994. dflags = 0;
  995. }
  996. if ( self->delay )
  997. {//Increase dmg over time
  998. if ( self->attackDebounceTime < self->delay )
  999. {//FIXME: this is for the entire trigger, not per person, so if someone else jumped in after you were in it for 5 seconds, they'd get damaged faster
  1000. actualDmg = floor( (float)(self->damage * self->attackDebounceTime / self->delay) );
  1001. }
  1002. self->attackDebounceTime += FRAMETIME;
  1003. self->e_ThinkFunc = thinkF_trigger_hurt_reset;
  1004. self->nextthink = level.time + FRAMETIME*2;
  1005. }
  1006. if ( actualDmg )
  1007. {
  1008. if (( self->spawnflags & 64 ) && other->client )//electrical damage
  1009. {
  1010. // zap effect
  1011. other->s.powerups |= ( 1 << PW_SHOCKED );
  1012. other->client->ps.powerups[PW_SHOCKED] = level.time + 1000;
  1013. }
  1014. if ( self->spawnflags & 32 )
  1015. {//falling death
  1016. if ( other->NPC && other->client &&
  1017. (other->client->NPC_class == CLASS_BOBAFETT || other->client->NPC_class == CLASS_ROCKETTROOPER ))
  1018. {//boba never falls to his death!
  1019. //FIXME: fall through if jetpack broken?
  1020. JET_FlyStart(other);
  1021. }
  1022. else
  1023. {
  1024. G_Damage (other, self, self, NULL, NULL, actualDmg, dflags|DAMAGE_NO_ARMOR, MOD_FALLING);
  1025. // G_Damage will free this ent, which makes it s.number 0, so we must check inuse...
  1026. if ( !other->s.number && other->health <= 0 )
  1027. {
  1028. if ( self->count )
  1029. {
  1030. extern void CGCam_Fade( vec4_t source, vec4_t dest, float duration );
  1031. float src[4] = {0,0,0,0},dst[4]={0,0,0,1};
  1032. CGCam_Fade( src, dst, self->count );
  1033. }
  1034. if ( self->spawnflags & 16 )
  1035. {//lock cam
  1036. cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_CDP;
  1037. cg.overrides.thirdPersonCameraDamp = 0;
  1038. }
  1039. if ( other->client )
  1040. {
  1041. other->client->ps.pm_flags |= PMF_SLOW_MO_FALL;
  1042. }
  1043. //G_SoundOnEnt( other, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN?
  1044. }
  1045. }
  1046. }
  1047. else
  1048. {
  1049. G_Damage (other, self, self, NULL, NULL, actualDmg, dflags, MOD_TRIGGER_HURT);
  1050. }
  1051. if( other && !other->s.number )
  1052. {
  1053. self->aimDebounceTime = level.time;
  1054. }
  1055. if (( self->spawnflags & 64 ) && other->client && other->health <= 0 )//electrical damage
  1056. {//just killed them, make the effect last longer since dead clients don't touch triggers
  1057. other->client->ps.powerups[PW_SHOCKED] = level.time + 10000;
  1058. }
  1059. self->painDebounceTime = level.time;
  1060. }
  1061. if ( self->wait < 0 )
  1062. {
  1063. self->e_TouchFunc = touchF_NULL;
  1064. }
  1065. }
  1066. void SP_trigger_hurt( gentity_t *self )
  1067. {
  1068. char buffer[MAX_QPATH];
  1069. char *s;
  1070. InitTrigger (self);
  1071. if ( !( self->spawnflags & 4 ))
  1072. {
  1073. G_SpawnString( "noise", "sound/world/electro", &s );
  1074. Q_strncpyz( buffer, s, sizeof(buffer) );
  1075. self->noise_index = G_SoundIndex(buffer);
  1076. }
  1077. self->e_TouchFunc = touchF_hurt_touch;
  1078. if ( !self->damage ) {
  1079. self->damage = 5;
  1080. }
  1081. self->delay *= 1000;
  1082. self->wait *= 1000;
  1083. self->contents = CONTENTS_TRIGGER;
  1084. if ( self->targetname ) {//NOTE: for some reason, this used to be: if(self->spawnflags&2)
  1085. self->e_UseFunc = useF_hurt_use;
  1086. }
  1087. // link in to the world if starting active
  1088. if ( !(self->spawnflags & 1) )
  1089. {
  1090. gi.linkentity (self);
  1091. }
  1092. else // triggers automatically get linked into the world by SetBrushModel, so we have to unlink it here
  1093. {
  1094. gi.unlinkentity(self);
  1095. }
  1096. }
  1097. #define INITIAL_SUFFOCATION_DELAY 5000 //5 seconds
  1098. void space_touch( gentity_t *self, gentity_t *other, trace_t *trace )
  1099. {
  1100. if (!other || !other->inuse || !other->client )
  1101. //NOTE: we need vehicles to know this, too...
  1102. //|| other->s.number >= MAX_CLIENTS)
  1103. {
  1104. return;
  1105. }
  1106. if (other->s.m_iVehicleNum
  1107. && other->s.m_iVehicleNum <= MAX_CLIENTS )
  1108. {//a player client inside a vehicle
  1109. gentity_t *veh = &g_entities[other->s.m_iVehicleNum];
  1110. if (veh->inuse && veh->client && veh->m_pVehicle &&
  1111. veh->m_pVehicle->m_pVehicleInfo->hideRider)
  1112. { //if they are "inside" a vehicle, then let that protect them from THE HORRORS OF SPACE.
  1113. return;
  1114. }
  1115. }
  1116. if (!G_PointInBounds(other->client->ps.origin, self->absmin, self->absmax))
  1117. { //his origin must be inside the trigger
  1118. return;
  1119. }
  1120. if (!other->client->inSpaceIndex ||
  1121. other->client->inSpaceIndex == ENTITYNUM_NONE)
  1122. { //freshly entering space
  1123. other->client->inSpaceSuffocation = level.time + INITIAL_SUFFOCATION_DELAY;
  1124. }
  1125. other->client->inSpaceIndex = self->s.number;
  1126. }
  1127. /*QUAKED trigger_space (.5 .5 .5) ?
  1128. causes human clients to suffocate and have no gravity.
  1129. */
  1130. void SP_trigger_space(gentity_t *self)
  1131. {
  1132. InitTrigger(self);
  1133. self->contents = CONTENTS_TRIGGER;
  1134. //FIXME: implement!!!
  1135. //self->e_TouchFunc = touchF_space_touch;
  1136. gi.linkentity(self);
  1137. }
  1138. void shipboundary_touch( gentity_t *self, gentity_t *other, trace_t *trace )
  1139. {
  1140. gentity_t *ent;
  1141. if (!other || !other->inuse || !other->client ||
  1142. other->s.number < MAX_CLIENTS ||
  1143. !other->m_pVehicle)
  1144. { //only let vehicles touch
  1145. return;
  1146. }
  1147. ent = G_Find (NULL, FOFS(targetname), self->target);
  1148. if (!ent || !ent->inuse)
  1149. { //this is bad
  1150. G_Error("trigger_shipboundary has invalid target '%s'\n", self->target);
  1151. return;
  1152. }
  1153. if (!other->s.m_iVehicleNum || other->m_pVehicle->m_iRemovedSurfaces)
  1154. { //if a vehicle touches a boundary without a pilot in it or with parts missing, just blow the thing up
  1155. G_Damage(other, other, other, NULL, other->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE);
  1156. return;
  1157. }
  1158. other->client->ps.vehTurnaroundIndex = ent->s.number;
  1159. other->client->ps.vehTurnaroundTime = level.time + self->count;
  1160. }
  1161. /*QUAKED trigger_shipboundary (.5 .5 .5) ?
  1162. causes vehicle to turn toward target and travel in that direction for a set time when hit.
  1163. "target" name of entity to turn toward (can be info_notnull, or whatever).
  1164. "traveltime" time to travel in this direction
  1165. */
  1166. void SP_trigger_shipboundary(gentity_t *self)
  1167. {
  1168. InitTrigger(self);
  1169. self->contents = CONTENTS_TRIGGER;
  1170. if (!self->target || !self->target[0])
  1171. {
  1172. G_Error("trigger_shipboundary without a target.");
  1173. }
  1174. G_SpawnInt("traveltime", "0", &self->count);
  1175. if (!self->count)
  1176. {
  1177. G_Error("trigger_shipboundary without traveltime.");
  1178. }
  1179. //FIXME: implement!
  1180. //self->e_TouchFunc = touchF_shipboundary_touch;
  1181. gi.linkentity(self);
  1182. }
  1183. /*
  1184. ==============================================================================
  1185. timer
  1186. ==============================================================================
  1187. */
  1188. /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
  1189. This should be renamed trigger_timer...
  1190. Repeatedly fires its targets.
  1191. Can be turned on or off by using.
  1192. "wait" base time between triggering all targets, default is 1
  1193. "random" wait variance, default is 0
  1194. so, the basic time between firing is a random time between
  1195. (wait - random) and (wait + random)
  1196. */
  1197. void func_timer_think( gentity_t *self ) {
  1198. G_UseTargets (self, self->activator);
  1199. // set time before next firing
  1200. self->nextthink = level.time + 1000 * ( self->wait + crandom() * self->random );
  1201. }
  1202. void func_timer_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
  1203. self->activator = activator;
  1204. G_ActivateBehavior(self,BSET_USE);
  1205. // if on, turn it off
  1206. if ( self->nextthink ) {
  1207. self->nextthink = 0;
  1208. return;
  1209. }
  1210. // turn it on
  1211. func_timer_think (self);
  1212. }
  1213. void SP_func_timer( gentity_t *self ) {
  1214. G_SpawnFloat( "random", "1", &self->random);
  1215. G_SpawnFloat( "wait", "1", &self->wait );
  1216. self->e_UseFunc = useF_func_timer_use;
  1217. self->e_ThinkFunc = thinkF_func_timer_think;
  1218. if ( self->random >= self->wait ) {
  1219. self->random = self->wait - 1;//NOTE: was - FRAMETIME, but FRAMETIME is in msec (100) and these numbers are in *seconds*!
  1220. gi.Printf( "func_timer at %s has random >= wait\n", vtos( self->s.origin ) );
  1221. }
  1222. if ( self->spawnflags & 1 ) {
  1223. self->nextthink = level.time + FRAMETIME;
  1224. self->activator = self;
  1225. }
  1226. self->svFlags = SVF_NOCLIENT;
  1227. }
  1228. /*
  1229. ==============================================================================
  1230. timer
  1231. ==============================================================================
  1232. */
  1233. /*QUAKED trigger_entdist (.1 .5 .1) (-8 -8 -8) (8 8 8) PLAYER NPC
  1234. fires if the given entity is within the given distance. Sets itself inactive after one use.
  1235. ----- KEYS -----
  1236. distance - radius entity can be away to fire trigger
  1237. target - fired if entity is within distance
  1238. target2 - fired if entity not within distance
  1239. NPC_target - NPC_types to look for
  1240. ownername - If any, who to calc the distance from- default is the trigger_entdist himself
  1241. example: target "biessman telsia" will look for the biessman and telsia NPC
  1242. if it finds either of these within distance it will fire.
  1243. todo -
  1244. add delay, count
  1245. add monster classnames?????
  1246. add LOS to it???
  1247. */
  1248. void trigger_entdist_use( gentity_t *self, gentity_t *other, gentity_t *activator )
  1249. {
  1250. vec3_t diff;
  1251. gentity_t *found = NULL;
  1252. gentity_t *owner = NULL;
  1253. qboolean useflag;
  1254. const char *token, *holdString;
  1255. if ( self->svFlags & SVF_INACTIVE ) // Don't use INACTIVE
  1256. return;
  1257. G_ActivateBehavior(self,BSET_USE);
  1258. if(self->ownername && self->ownername[0])
  1259. {
  1260. owner = G_Find(NULL, FOFS(targetname), self->ownername);
  1261. }
  1262. if(owner == NULL)
  1263. {
  1264. owner = self;
  1265. }
  1266. self->activator = activator;
  1267. useflag = qfalse;
  1268. self->svFlags |= SVF_INACTIVE; // Make it inactive after one use
  1269. if (self->spawnflags & ENTDIST_PLAYER) // Look for player???
  1270. {
  1271. found = &g_entities[0];
  1272. if (found)
  1273. {
  1274. VectorSubtract(owner->currentOrigin, found->currentOrigin, diff);
  1275. if(VectorLength(diff) < self->count)
  1276. {
  1277. useflag = qtrue;
  1278. }
  1279. }
  1280. }
  1281. if ((self->spawnflags & ENTDIST_NPC) && (!useflag))
  1282. {
  1283. holdString = self->NPC_target;
  1284. while (holdString)
  1285. {
  1286. token = COM_Parse( &holdString);
  1287. if ( !token ) // Nothing left to look at
  1288. {
  1289. break;
  1290. }
  1291. found = G_Find(found, FOFS(targetname), token); // Look for the specified NPC
  1292. if (found) //Found???
  1293. {
  1294. VectorSubtract(owner->currentOrigin, found->currentOrigin, diff);
  1295. if(VectorLength(diff) < self->count) // Within distance
  1296. {
  1297. useflag = qtrue;
  1298. break;
  1299. }
  1300. }
  1301. }
  1302. }
  1303. if (useflag)
  1304. {
  1305. G_UseTargets2 (self, self->activator, self->target);
  1306. }
  1307. else if (self->target2)
  1308. {
  1309. // This is the negative target
  1310. G_UseTargets2 (self, self->activator, self->target2);
  1311. }
  1312. }
  1313. void SP_trigger_entdist( gentity_t *self )
  1314. {
  1315. G_SpawnInt( "distance", "0", &self->count);
  1316. self->e_UseFunc = useF_trigger_entdist_use;
  1317. }
  1318. // spawnflag
  1319. #define TRIGGERVISIBLE_FORCESIGHT 2
  1320. void trigger_visible_check_player_visibility( gentity_t *self )
  1321. {
  1322. //Check every FRAMETIME*2
  1323. self->nextthink = level.time + FRAMETIME*2;
  1324. if ( self->svFlags & SVF_INACTIVE )
  1325. {
  1326. return;
  1327. }
  1328. vec3_t dir;
  1329. float dist;
  1330. gentity_t *player = &g_entities[0];
  1331. if (!player || !player->client )
  1332. {
  1333. return;
  1334. }
  1335. // Added 01/20/03 by AReis
  1336. // If this trigger can only be used if the players force sight is on...
  1337. if ( self->spawnflags & TRIGGERVISIBLE_FORCESIGHT )
  1338. {
  1339. // If their force sight is not on, leave...
  1340. if ( !( player->client->ps.forcePowersActive & (1 << FP_SEE) ) )
  1341. {
  1342. return;
  1343. }
  1344. }
  1345. //1: see if player is within 512*512 range
  1346. VectorSubtract( self->currentOrigin, player->client->renderInfo.eyePoint, dir );
  1347. dist = VectorNormalize( dir );
  1348. if ( dist < self->radius )
  1349. {//Within range
  1350. vec3_t forward;
  1351. float dot;
  1352. //2: see if dot to us and player viewangles is > 0.7
  1353. AngleVectors( player->client->renderInfo.eyeAngles, forward, NULL, NULL );
  1354. dot = DotProduct( forward, dir );
  1355. if ( dot > self->random )
  1356. {//Within the desired FOV
  1357. //3: see if player is in PVS
  1358. if ( gi.inPVS( self->currentOrigin, player->client->renderInfo.eyePoint ) )
  1359. {
  1360. vec3_t mins = {-1, -1, -1};
  1361. vec3_t maxs = {1, 1, 1};
  1362. //4: If needbe, trace to see if there is clear LOS from player viewpos
  1363. if ( (self->spawnflags&1) || G_ClearTrace( player->client->renderInfo.eyePoint, mins, maxs, self->currentOrigin, 0, MASK_OPAQUE ) )
  1364. {
  1365. //5: Fire!
  1366. G_UseTargets( self, player );
  1367. //6: Remove yourself
  1368. G_FreeEntity( self );
  1369. }
  1370. }
  1371. }
  1372. }
  1373. }
  1374. /*QUAKED trigger_visible (.1 .5 .1) (-8 -8 -8) (8 8 8) NOTRACE FORCESIGHT x x x x x INACTIVE
  1375. Only fires when player is looking at it, fires only once then removes itself.
  1376. NOTRACE - Doesn't check to make sure the line of sight is completely clear (penetrates walls, forcefields, etc)
  1377. FORCESIGHT - Only activates this trigger if force sight is on.
  1378. INACTIVE - won't check for player visibility until activated
  1379. radius - how far this ent can be from player's eyes, max, and still be considered "seen"
  1380. FOV - how far off to the side of the player's field of view this can be, max, and still be considered "seen". Player FOV is 80, so the default for this value is 30.
  1381. "target" - What to use when it fires.
  1382. */
  1383. void SP_trigger_visible( gentity_t *self )
  1384. {
  1385. if ( self->radius <= 0 )
  1386. {
  1387. self->radius = 512;
  1388. }
  1389. if ( self->random <= 0 )
  1390. {//about 30 degrees
  1391. self->random = 0.7f;
  1392. }
  1393. else
  1394. {//convert from FOV degrees to number meaningful for dot products
  1395. self->random = 1.0f - (self->random/90.0f);
  1396. }
  1397. if ( self->spawnflags & 128 )
  1398. {// Make it inactive
  1399. self->svFlags |= SVF_INACTIVE;
  1400. }
  1401. G_SetOrigin( self, self->s.origin );
  1402. gi.linkentity( self );
  1403. self->e_ThinkFunc = thinkF_trigger_visible_check_player_visibility;
  1404. self->nextthink = level.time + FRAMETIME*2;
  1405. }