1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5
6 This file is part of Tremulous.
7
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 ===========================================================================
22 */
23
24 #include "g_local.h"
25
26
InitTrigger(gentity_t * self)27 void InitTrigger( gentity_t *self )
28 {
29 if( !VectorCompare( self->s.angles, vec3_origin ) )
30 G_SetMovedir( self->s.angles, self->movedir );
31
32 trap_SetBrushModel( self, self->model );
33 self->r.contents = CONTENTS_TRIGGER; // replaces the -1 from trap_SetBrushModel
34 self->r.svFlags = SVF_NOCLIENT;
35 }
36
37
38 // the wait time has passed, so set back up for another activation
multi_wait(gentity_t * ent)39 void multi_wait( gentity_t *ent )
40 {
41 ent->nextthink = 0;
42 }
43
44
45 // the trigger was just activated
46 // ent->activator should be set to the activator so it can be held through a delay
47 // so wait for the delay time before firing
multi_trigger(gentity_t * ent,gentity_t * activator)48 void multi_trigger( gentity_t *ent, gentity_t *activator )
49 {
50 ent->activator = activator;
51 if( ent->nextthink )
52 return; // can't retrigger until the wait is over
53
54 if( activator->client )
55 {
56 if( ( ent->spawnflags & 1 ) &&
57 activator->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
58 return;
59
60 if( ( ent->spawnflags & 2 ) &&
61 activator->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
62 return;
63 }
64
65 G_UseTargets( ent, ent->activator );
66
67 if( ent->wait > 0 )
68 {
69 ent->think = multi_wait;
70 ent->nextthink = level.time + ( ent->wait + ent->random * crandom( ) ) * 1000;
71 }
72 else
73 {
74 // we can't just remove (self) here, because this is a touch function
75 // called while looping through area links...
76 ent->touch = 0;
77 ent->nextthink = level.time + FRAMETIME;
78 ent->think = G_FreeEntity;
79 }
80 }
81
Use_Multi(gentity_t * ent,gentity_t * other,gentity_t * activator)82 void Use_Multi( gentity_t *ent, gentity_t *other, gentity_t *activator )
83 {
84 multi_trigger( ent, activator );
85 }
86
Touch_Multi(gentity_t * self,gentity_t * other,trace_t * trace)87 void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace )
88 {
89 if( !other->client && other->s.eType != ET_BUILDABLE )
90 return;
91
92 multi_trigger( self, other );
93 }
94
95 /*QUAKED trigger_multiple (.5 .5 .5) ?
96 "wait" : Seconds between triggerings, 0.5 default, -1 = one time only.
97 "random" wait variance, default is 0
98 Variable sized repeatable trigger. Must be targeted at one or more entities.
99 so, the basic time between firing is a random time between
100 (wait - random) and (wait + random)
101 */
SP_trigger_multiple(gentity_t * ent)102 void SP_trigger_multiple( gentity_t *ent )
103 {
104 G_SpawnFloat( "wait", "0.5", &ent->wait );
105 G_SpawnFloat( "random", "0", &ent->random );
106
107 if( ent->random >= ent->wait && ent->wait >= 0 )
108 {
109 ent->random = ent->wait - FRAMETIME;
110 G_Printf( "trigger_multiple has random >= wait\n" );
111 }
112
113 ent->touch = Touch_Multi;
114 ent->use = Use_Multi;
115
116 InitTrigger( ent );
117 trap_LinkEntity( ent );
118 }
119
120
121
122 /*
123 ==============================================================================
124
125 trigger_always
126
127 ==============================================================================
128 */
129
trigger_always_think(gentity_t * ent)130 void trigger_always_think( gentity_t *ent )
131 {
132 G_UseTargets( ent, ent );
133 G_FreeEntity( ent );
134 }
135
136 /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
137 This trigger will always fire. It is activated by the world.
138 */
SP_trigger_always(gentity_t * ent)139 void SP_trigger_always( gentity_t *ent )
140 {
141 // we must have some delay to make sure our use targets are present
142 ent->nextthink = level.time + 300;
143 ent->think = trigger_always_think;
144 }
145
146
147 /*
148 ==============================================================================
149
150 trigger_push
151
152 ==============================================================================
153 */
154
trigger_push_touch(gentity_t * self,gentity_t * other,trace_t * trace)155 void trigger_push_touch( gentity_t *self, gentity_t *other, trace_t *trace )
156 {
157 if( !other->client )
158 return;
159 }
160
161
162 /*
163 =================
164 AimAtTarget
165
166 Calculate origin2 so the target apogee will be hit
167 =================
168 */
AimAtTarget(gentity_t * self)169 void AimAtTarget( gentity_t *self )
170 {
171 gentity_t *ent;
172 vec3_t origin;
173 float height, gravity, time, forward;
174 float dist;
175
176 VectorAdd( self->r.absmin, self->r.absmax, origin );
177 VectorScale( origin, 0.5, origin );
178
179 ent = G_PickTarget( self->target );
180
181 if( !ent )
182 {
183 G_FreeEntity( self );
184 return;
185 }
186
187 height = ent->s.origin[ 2 ] - origin[ 2 ];
188 gravity = g_gravity.value;
189 time = sqrt( height / ( 0.5 * gravity ) );
190
191 if( !time )
192 {
193 G_FreeEntity( self );
194 return;
195 }
196
197 // set s.origin2 to the push velocity
198 VectorSubtract( ent->s.origin, origin, self->s.origin2 );
199 self->s.origin2[ 2 ] = 0;
200 dist = VectorNormalize( self->s.origin2 );
201
202 forward = dist / time;
203 VectorScale( self->s.origin2, forward, self->s.origin2 );
204
205 self->s.origin2[ 2 ] = time * gravity;
206 }
207
208
209 /*QUAKED trigger_push (.5 .5 .5) ?
210 Must point at a target_position, which will be the apex of the leap.
211 This will be client side predicted, unlike target_push
212 */
SP_trigger_push(gentity_t * self)213 void SP_trigger_push( gentity_t *self )
214 {
215 InitTrigger( self );
216
217 // unlike other triggers, we need to send this one to the client
218 self->r.svFlags &= ~SVF_NOCLIENT;
219
220 // make sure the client precaches this sound
221 G_SoundIndex( "sound/world/jumppad.wav" );
222
223 self->s.eType = ET_PUSH_TRIGGER;
224 self->touch = trigger_push_touch;
225 self->think = AimAtTarget;
226 self->nextthink = level.time + FRAMETIME;
227 trap_LinkEntity( self );
228 }
229
230
Use_target_push(gentity_t * self,gentity_t * other,gentity_t * activator)231 void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator )
232 {
233 if( !activator->client )
234 return;
235
236 if( activator->client->ps.pm_type != PM_NORMAL )
237 return;
238
239 VectorCopy( self->s.origin2, activator->client->ps.velocity );
240
241 // play fly sound every 1.5 seconds
242 if( activator->fly_sound_debounce_time < level.time )
243 {
244 activator->fly_sound_debounce_time = level.time + 1500;
245 G_Sound( activator, CHAN_AUTO, self->noise_index );
246 }
247 }
248
249 /*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) bouncepad
250 Pushes the activator in the direction.of angle, or towards a target apex.
251 "speed" defaults to 1000
252 if "bouncepad", play bounce noise instead of windfly
253 */
SP_target_push(gentity_t * self)254 void SP_target_push( gentity_t *self )
255 {
256 if( !self->speed )
257 self->speed = 1000;
258
259 G_SetMovedir( self->s.angles, self->s.origin2 );
260 VectorScale( self->s.origin2, self->speed, self->s.origin2 );
261
262 if( self->spawnflags & 1 )
263 self->noise_index = G_SoundIndex( "sound/world/jumppad.wav" );
264 else
265 self->noise_index = G_SoundIndex( "sound/misc/windfly.wav" );
266
267 if( self->target )
268 {
269 VectorCopy( self->s.origin, self->r.absmin );
270 VectorCopy( self->s.origin, self->r.absmax );
271 self->think = AimAtTarget;
272 self->nextthink = level.time + FRAMETIME;
273 }
274
275 self->use = Use_target_push;
276 }
277
278 /*
279 ==============================================================================
280
281 trigger_teleport
282
283 ==============================================================================
284 */
285
trigger_teleporter_touch(gentity_t * self,gentity_t * other,trace_t * trace)286 void trigger_teleporter_touch( gentity_t *self, gentity_t *other, trace_t *trace )
287 {
288 gentity_t *dest;
289
290 if( !other->client )
291 return;
292
293 if( other->client->ps.pm_type == PM_DEAD )
294 return;
295
296 // Spectators only?
297 if( ( self->spawnflags & 1 ) &&
298 other->client->sess.sessionTeam != TEAM_SPECTATOR )
299 return;
300
301
302 dest = G_PickTarget( self->target );
303
304 if( !dest )
305 {
306 G_Printf( "Couldn't find teleporter destination\n" );
307 return;
308 }
309
310 TeleportPlayer( other, dest->s.origin, dest->s.angles );
311 }
312
313
314 /*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR
315 Allows client side prediction of teleportation events.
316 Must point at a target_position, which will be the teleport destination.
317
318 If spectator is set, only spectators can use this teleport
319 Spectator teleporters are not normally placed in the editor, but are created
320 automatically near doors to allow spectators to move through them
321 */
SP_trigger_teleport(gentity_t * self)322 void SP_trigger_teleport( gentity_t *self )
323 {
324 InitTrigger( self );
325
326 // unlike other triggers, we need to send this one to the client
327 // unless is a spectator trigger
328 if( self->spawnflags & 1 )
329 self->r.svFlags |= SVF_NOCLIENT;
330 else
331 self->r.svFlags &= ~SVF_NOCLIENT;
332
333 // make sure the client precaches this sound
334 G_SoundIndex( "sound/world/jumppad.wav" );
335
336 self->s.eType = ET_TELEPORT_TRIGGER;
337 self->touch = trigger_teleporter_touch;
338
339 trap_LinkEntity( self );
340 }
341
342
343 /*
344 ==============================================================================
345
346 trigger_hurt
347
348 ==============================================================================
349 */
350
351 /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF - SILENT NO_PROTECTION SLOW
352 Any entity that touches this will be hurt.
353 It does dmg points of damage each server frame
354 Targeting the trigger will toggle its on / off state.
355
356 SILENT supresses playing the sound
357 SLOW changes the damage rate to once per second
358 NO_PROTECTION *nothing* stops the damage
359
360 "dmg" default 5 (whole numbers only)
361
362 */
hurt_use(gentity_t * self,gentity_t * other,gentity_t * activator)363 void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator )
364 {
365 if( self->r.linked )
366 trap_UnlinkEntity( self );
367 else
368 trap_LinkEntity( self );
369 }
370
hurt_touch(gentity_t * self,gentity_t * other,trace_t * trace)371 void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace )
372 {
373 int dflags;
374
375 if( !other->takedamage )
376 return;
377
378 if( self->timestamp > level.time )
379 return;
380
381 if( self->spawnflags & 16 )
382 self->timestamp = level.time + 1000;
383 else
384 self->timestamp = level.time + FRAMETIME;
385
386 // play sound
387 if( !( self->spawnflags & 4 ) )
388 G_Sound( other, CHAN_AUTO, self->noise_index );
389
390 if( self->spawnflags & 8 )
391 dflags = DAMAGE_NO_PROTECTION;
392 else
393 dflags = 0;
394
395 G_Damage( other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT );
396 }
397
SP_trigger_hurt(gentity_t * self)398 void SP_trigger_hurt( gentity_t *self )
399 {
400 InitTrigger( self );
401
402 self->noise_index = G_SoundIndex( "sound/misc/electro.wav" );
403 self->touch = hurt_touch;
404
405 if( !self->damage )
406 self->damage = 5;
407
408 self->r.contents = CONTENTS_TRIGGER;
409
410 if( self->spawnflags & 2 )
411 self->use = hurt_use;
412
413 // link in to the world if starting active
414 if( !( self->spawnflags & 1 ) )
415 trap_LinkEntity( self );
416 }
417
418
419 /*
420 ==============================================================================
421
422 timer
423
424 ==============================================================================
425 */
426
427
428 /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
429 This should be renamed trigger_timer...
430 Repeatedly fires its targets.
431 Can be turned on or off by using.
432
433 "wait" base time between triggering all targets, default is 1
434 "random" wait variance, default is 0
435 so, the basic time between firing is a random time between
436 (wait - random) and (wait + random)
437
438 */
func_timer_think(gentity_t * self)439 void func_timer_think( gentity_t *self )
440 {
441 G_UseTargets( self, self->activator );
442 // set time before next firing
443 self->nextthink = level.time + 1000 * ( self->wait + crandom( ) * self->random );
444 }
445
func_timer_use(gentity_t * self,gentity_t * other,gentity_t * activator)446 void func_timer_use( gentity_t *self, gentity_t *other, gentity_t *activator )
447 {
448 self->activator = activator;
449
450 // if on, turn it off
451 if( self->nextthink )
452 {
453 self->nextthink = 0;
454 return;
455 }
456
457 // turn it on
458 func_timer_think( self );
459 }
460
SP_func_timer(gentity_t * self)461 void SP_func_timer( gentity_t *self )
462 {
463 G_SpawnFloat( "random", "1", &self->random );
464 G_SpawnFloat( "wait", "1", &self->wait );
465
466 self->use = func_timer_use;
467 self->think = func_timer_think;
468
469 if( self->random >= self->wait )
470 {
471 self->random = self->wait - FRAMETIME;
472 G_Printf( "func_timer at %s has random >= wait\n", vtos( self->s.origin ) );
473 }
474
475 if( self->spawnflags & 1 )
476 {
477 self->nextthink = level.time + FRAMETIME;
478 self->activator = self;
479 }
480
481 self->r.svFlags = SVF_NOCLIENT;
482 }
483
484
485 /*
486 ===============
487 G_Checktrigger_stages
488
489 Called when stages change
490 ===============
491 */
G_Checktrigger_stages(pTeam_t team,stage_t stage)492 void G_Checktrigger_stages( pTeam_t team, stage_t stage )
493 {
494 int i;
495 gentity_t *ent;
496
497 for( i = 1, ent = g_entities + i ; i < level.num_entities ; i++, ent++ )
498 {
499 if( !ent->inuse )
500 continue;
501
502 if( !Q_stricmp( ent->classname, "trigger_stage" ) )
503 {
504 if( team == ent->stageTeam && stage == ent->stageStage )
505 ent->use( ent, ent, ent );
506 }
507 }
508 }
509
510
511 /*
512 ===============
513 trigger_stage_use
514 ===============
515 */
trigger_stage_use(gentity_t * self,gentity_t * other,gentity_t * activator)516 void trigger_stage_use( gentity_t *self, gentity_t *other, gentity_t *activator )
517 {
518 G_UseTargets( self, self );
519 }
520
SP_trigger_stage(gentity_t * self)521 void SP_trigger_stage( gentity_t *self )
522 {
523 G_SpawnInt( "team", "0", (int *)&self->stageTeam );
524 G_SpawnInt( "stage", "0", (int *)&self->stageStage );
525
526 self->use = trigger_stage_use;
527
528 self->r.svFlags = SVF_NOCLIENT;
529 }
530
531
532 /*
533 ===============
534 trigger_win
535 ===============
536 */
trigger_win(gentity_t * self,gentity_t * other,gentity_t * activator)537 void trigger_win( gentity_t *self, gentity_t *other, gentity_t *activator )
538 {
539 G_UseTargets( self, self );
540 }
541
SP_trigger_win(gentity_t * self)542 void SP_trigger_win( gentity_t *self )
543 {
544 G_SpawnInt( "team", "0", (int *)&self->stageTeam );
545
546 self->use = trigger_win;
547
548 self->r.svFlags = SVF_NOCLIENT;
549 }
550
551
552 /*
553 ===============
554 trigger_buildable_trigger
555 ===============
556 */
trigger_buildable_trigger(gentity_t * self,gentity_t * activator)557 void trigger_buildable_trigger( gentity_t *self, gentity_t *activator )
558 {
559 int i = 0;
560
561 self->activator = activator;
562 if( self->nextthink )
563 return; // can't retrigger until the wait is over
564
565 //if there is no buildable list every buildable triggers
566 if( self->bTriggers[ i ] == BA_NONE )
567 G_UseTargets( self, activator );
568 else
569 {
570 //otherwise check against the list
571 for( i = 0; self->bTriggers[ i ] != BA_NONE; i++ )
572 {
573 if( activator->s.modelindex == self->bTriggers[ i ] )
574 {
575 G_UseTargets( self, activator );
576 return;
577 }
578 }
579 }
580
581 if( self->wait > 0 )
582 {
583 self->think = multi_wait;
584 self->nextthink = level.time + ( self->wait + self->random * crandom( ) ) * 1000;
585 }
586 else
587 {
588 // we can't just remove (self) here, because this is a touch function
589 // called while looping through area links...
590 self->touch = 0;
591 self->nextthink = level.time + FRAMETIME;
592 self->think = G_FreeEntity;
593 }
594 }
595
596 /*
597 ===============
598 trigger_buildable_touch
599 ===============
600 */
trigger_buildable_touch(gentity_t * ent,gentity_t * other,trace_t * trace)601 void trigger_buildable_touch( gentity_t *ent, gentity_t *other, trace_t *trace )
602 {
603 //only triggered by buildables
604 if( other->s.eType != ET_BUILDABLE )
605 return;
606
607 trigger_buildable_trigger( ent, other );
608 }
609
610 /*
611 ===============
612 trigger_buildable_use
613 ===============
614 */
trigger_buildable_use(gentity_t * ent,gentity_t * other,gentity_t * activator)615 void trigger_buildable_use( gentity_t *ent, gentity_t *other, gentity_t *activator )
616 {
617 trigger_buildable_trigger( ent, activator );
618 }
619
620 /*
621 ===============
622 SP_trigger_buildable
623 ===============
624 */
SP_trigger_buildable(gentity_t * self)625 void SP_trigger_buildable( gentity_t *self )
626 {
627 char *buffer;
628
629 G_SpawnFloat( "wait", "0.5", &self->wait );
630 G_SpawnFloat( "random", "0", &self->random );
631
632 if( self->random >= self->wait && self->wait >= 0 )
633 {
634 self->random = self->wait - FRAMETIME;
635 G_Printf( S_COLOR_YELLOW "WARNING: trigger_buildable has random >= wait\n" );
636 }
637
638 G_SpawnString( "buildables", "", &buffer );
639
640 BG_ParseCSVBuildableList( buffer, self->bTriggers, BA_NUM_BUILDABLES );
641
642 self->touch = trigger_buildable_touch;
643 self->use = trigger_buildable_use;
644
645 InitTrigger( self );
646 trap_LinkEntity( self );
647 }
648
649
650 /*
651 ===============
652 trigger_class_trigger
653 ===============
654 */
trigger_class_trigger(gentity_t * self,gentity_t * activator)655 void trigger_class_trigger( gentity_t *self, gentity_t *activator )
656 {
657 int i = 0;
658
659 //sanity check
660 if( !activator->client )
661 return;
662
663 if( activator->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
664 return;
665
666 self->activator = activator;
667 if( self->nextthink )
668 return; // can't retrigger until the wait is over
669
670 //if there is no class list every class triggers (stupid case)
671 if( self->cTriggers[ i ] == PCL_NONE )
672 G_UseTargets( self, activator );
673 else
674 {
675 //otherwise check against the list
676 for( i = 0; self->cTriggers[ i ] != PCL_NONE; i++ )
677 {
678 if( activator->client->ps.stats[ STAT_PCLASS ] == self->cTriggers[ i ] )
679 {
680 G_UseTargets( self, activator );
681 return;
682 }
683 }
684 }
685
686 if( self->wait > 0 )
687 {
688 self->think = multi_wait;
689 self->nextthink = level.time + ( self->wait + self->random * crandom( ) ) * 1000;
690 }
691 else
692 {
693 // we can't just remove (self) here, because this is a touch function
694 // called while looping through area links...
695 self->touch = 0;
696 self->nextthink = level.time + FRAMETIME;
697 self->think = G_FreeEntity;
698 }
699 }
700
701 /*
702 ===============
703 trigger_class_touch
704 ===============
705 */
trigger_class_touch(gentity_t * ent,gentity_t * other,trace_t * trace)706 void trigger_class_touch( gentity_t *ent, gentity_t *other, trace_t *trace )
707 {
708 //only triggered by clients
709 if( !other->client )
710 return;
711
712 trigger_class_trigger( ent, other );
713 }
714
715 /*
716 ===============
717 trigger_class_use
718 ===============
719 */
trigger_class_use(gentity_t * ent,gentity_t * other,gentity_t * activator)720 void trigger_class_use( gentity_t *ent, gentity_t *other, gentity_t *activator )
721 {
722 trigger_class_trigger( ent, activator );
723 }
724
725 /*
726 ===============
727 SP_trigger_class
728 ===============
729 */
SP_trigger_class(gentity_t * self)730 void SP_trigger_class( gentity_t *self )
731 {
732 char *buffer;
733
734 G_SpawnFloat( "wait", "0.5", &self->wait );
735 G_SpawnFloat( "random", "0", &self->random );
736
737 if( self->random >= self->wait && self->wait >= 0 )
738 {
739 self->random = self->wait - FRAMETIME;
740 G_Printf( S_COLOR_YELLOW "WARNING: trigger_class has random >= wait\n" );
741 }
742
743 G_SpawnString( "classes", "", &buffer );
744
745 BG_ParseCSVClassList( buffer, self->cTriggers, PCL_NUM_CLASSES );
746
747 self->touch = trigger_class_touch;
748 self->use = trigger_class_use;
749
750 InitTrigger( self );
751 trap_LinkEntity( self );
752 }
753
754
755 /*
756 ===============
757 trigger_equipment_trigger
758 ===============
759 */
trigger_equipment_trigger(gentity_t * self,gentity_t * activator)760 void trigger_equipment_trigger( gentity_t *self, gentity_t *activator )
761 {
762 int i = 0;
763
764 //sanity check
765 if( !activator->client )
766 return;
767
768 if( activator->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
769 return;
770
771 self->activator = activator;
772 if( self->nextthink )
773 return; // can't retrigger until the wait is over
774
775 //if there is no equipment list all equipment triggers (stupid case)
776 if( self->wTriggers[ i ] == WP_NONE && self->wTriggers[ i ] == UP_NONE )
777 G_UseTargets( self, activator );
778 else
779 {
780 //otherwise check against the lists
781 for( i = 0; self->wTriggers[ i ] != WP_NONE; i++ )
782 {
783 if( BG_InventoryContainsWeapon( self->wTriggers[ i ], activator->client->ps.stats ) )
784 {
785 G_UseTargets( self, activator );
786 return;
787 }
788 }
789
790 for( i = 0; self->uTriggers[ i ] != UP_NONE; i++ )
791 {
792 if( BG_InventoryContainsUpgrade( self->uTriggers[ i ], activator->client->ps.stats ) )
793 {
794 G_UseTargets( self, activator );
795 return;
796 }
797 }
798 }
799
800 if( self->wait > 0 )
801 {
802 self->think = multi_wait;
803 self->nextthink = level.time + ( self->wait + self->random * crandom( ) ) * 1000;
804 }
805 else
806 {
807 // we can't just remove (self) here, because this is a touch function
808 // called while looping through area links...
809 self->touch = 0;
810 self->nextthink = level.time + FRAMETIME;
811 self->think = G_FreeEntity;
812 }
813 }
814
815 /*
816 ===============
817 trigger_equipment_touch
818 ===============
819 */
trigger_equipment_touch(gentity_t * ent,gentity_t * other,trace_t * trace)820 void trigger_equipment_touch( gentity_t *ent, gentity_t *other, trace_t *trace )
821 {
822 //only triggered by clients
823 if( !other->client )
824 return;
825
826 trigger_equipment_trigger( ent, other );
827 }
828
829 /*
830 ===============
831 trigger_equipment_use
832 ===============
833 */
trigger_equipment_use(gentity_t * ent,gentity_t * other,gentity_t * activator)834 void trigger_equipment_use( gentity_t *ent, gentity_t *other, gentity_t *activator )
835 {
836 trigger_equipment_trigger( ent, activator );
837 }
838
839 /*
840 ===============
841 SP_trigger_equipment
842 ===============
843 */
SP_trigger_equipment(gentity_t * self)844 void SP_trigger_equipment( gentity_t *self )
845 {
846 char *buffer;
847
848 G_SpawnFloat( "wait", "0.5", &self->wait );
849 G_SpawnFloat( "random", "0", &self->random );
850
851 if( self->random >= self->wait && self->wait >= 0 )
852 {
853 self->random = self->wait - FRAMETIME;
854 G_Printf( S_COLOR_YELLOW "WARNING: trigger_equipment has random >= wait\n" );
855 }
856
857 G_SpawnString( "equipment", "", &buffer );
858
859 BG_ParseCSVEquipmentList( buffer, self->wTriggers, WP_NUM_WEAPONS,
860 self->uTriggers, UP_NUM_UPGRADES );
861
862 self->touch = trigger_equipment_touch;
863 self->use = trigger_equipment_use;
864
865 InitTrigger( self );
866 trap_LinkEntity( self );
867 }
868
869
870 /*
871 ===============
872 trigger_gravity_touch
873 ===============
874 */
trigger_gravity_touch(gentity_t * ent,gentity_t * other,trace_t * trace)875 void trigger_gravity_touch( gentity_t *ent, gentity_t *other, trace_t *trace )
876 {
877 //only triggered by clients
878 if( !other->client )
879 return;
880
881 other->client->ps.gravity = ent->triggerGravity;
882 }
883
884 /*
885 ===============
886 trigger_gravity_use
887 ===============
888 */
trigger_gravity_use(gentity_t * ent,gentity_t * other,gentity_t * activator)889 void trigger_gravity_use( gentity_t *ent, gentity_t *other, gentity_t *activator )
890 {
891 if( ent->r.linked )
892 trap_UnlinkEntity( ent );
893 else
894 trap_LinkEntity( ent );
895 }
896
897
898 /*
899 ===============
900 SP_trigger_gravity
901 ===============
902 */
SP_trigger_gravity(gentity_t * self)903 void SP_trigger_gravity( gentity_t *self )
904 {
905 G_SpawnInt( "gravity", "800", &self->triggerGravity );
906
907 self->touch = trigger_gravity_touch;
908 self->use = trigger_gravity_use;
909
910 InitTrigger( self );
911 trap_LinkEntity( self );
912 }
913
914
915 /*
916 ===============
917 trigger_heal_use
918 ===============
919 */
trigger_heal_use(gentity_t * self,gentity_t * other,gentity_t * activator)920 void trigger_heal_use( gentity_t *self, gentity_t *other, gentity_t *activator )
921 {
922 if( self->r.linked )
923 trap_UnlinkEntity( self );
924 else
925 trap_LinkEntity( self );
926 }
927
928 /*
929 ===============
930 trigger_heal_touch
931 ===============
932 */
trigger_heal_touch(gentity_t * self,gentity_t * other,trace_t * trace)933 void trigger_heal_touch( gentity_t *self, gentity_t *other, trace_t *trace )
934 {
935 int max;
936
937 if( !other->client )
938 return;
939
940 if( self->timestamp > level.time )
941 return;
942
943 if( self->spawnflags & 2 )
944 self->timestamp = level.time + 1000;
945 else
946 self->timestamp = level.time + FRAMETIME;
947
948 max = other->client->ps.stats[ STAT_MAX_HEALTH ];
949
950 other->health += self->damage;
951
952 if( other->health > max )
953 other->health = max;
954
955 other->client->ps.stats[ STAT_HEALTH ] = other->health;
956 }
957
958 /*
959 ===============
960 SP_trigger_heal
961 ===============
962 */
SP_trigger_heal(gentity_t * self)963 void SP_trigger_heal( gentity_t *self )
964 {
965 G_SpawnInt( "heal", "5", &self->damage );
966
967 self->touch = trigger_heal_touch;
968 self->use = trigger_heal_use;
969
970 InitTrigger( self );
971
972 // link in to the world if starting active
973 if( !( self->spawnflags & 1 ) )
974 trap_LinkEntity( self );
975 }
976
977
978 /*
979 ===============
980 trigger_ammo_touch
981 ===============
982 */
trigger_ammo_touch(gentity_t * self,gentity_t * other,trace_t * trace)983 void trigger_ammo_touch( gentity_t *self, gentity_t *other, trace_t *trace )
984 {
985 int ammo, clips, maxClips, maxAmmo;
986
987 if( !other->client )
988 return;
989
990 if( other->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
991 return;
992
993 if( self->timestamp > level.time )
994 return;
995
996 if( other->client->ps.weaponstate != WEAPON_READY )
997 return;
998
999 if( BG_FindUsesEnergyForWeapon( other->client->ps.weapon ) && self->spawnflags & 2 )
1000 return;
1001
1002 if( !BG_FindUsesEnergyForWeapon( other->client->ps.weapon ) && self->spawnflags & 4 )
1003 return;
1004
1005 if( self->spawnflags & 1 )
1006 self->timestamp = level.time + 1000;
1007 else
1008 self->timestamp = level.time + FRAMETIME;
1009
1010 BG_FindAmmoForWeapon( other->client->ps.weapon, &maxAmmo, &maxClips );
1011 BG_UnpackAmmoArray( other->client->ps.weapon, other->client->ps.ammo, other->client->ps.powerups,
1012 &ammo, &clips );
1013
1014 if( ( ammo + self->damage ) > maxAmmo )
1015 {
1016 if( clips < maxClips )
1017 {
1018 clips++;
1019 ammo = 1;
1020 }
1021 else
1022 ammo = maxAmmo;
1023 }
1024 else
1025 ammo += self->damage;
1026
1027 BG_PackAmmoArray( other->client->ps.weapon, other->client->ps.ammo, other->client->ps.powerups,
1028 ammo, clips );
1029 }
1030
1031 /*
1032 ===============
1033 SP_trigger_ammo
1034 ===============
1035 */
SP_trigger_ammo(gentity_t * self)1036 void SP_trigger_ammo( gentity_t *self )
1037 {
1038 G_SpawnInt( "ammo", "1", &self->damage );
1039
1040 self->touch = trigger_ammo_touch;
1041
1042 InitTrigger( self );
1043 trap_LinkEntity( self );
1044 }
1045