1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 /*
30 * name: g_target.c
31 *
32 * desc:
33 *
34 */
35
36 #include "g_local.h"
37
38 //==========================================================
39
40 /*QUAKED target_give (1 0 0) (-8 -8 -8) (8 8 8)
41 Gives the activator all the items pointed to.
42 */
Use_Target_Give(gentity_t * ent,gentity_t * other,gentity_t * activator)43 void Use_Target_Give( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
44 gentity_t *t;
45 trace_t trace;
46
47 if ( !activator->client ) {
48 return;
49 }
50
51 if ( !ent->target ) {
52 return;
53 }
54
55 memset( &trace, 0, sizeof( trace ) );
56 t = NULL;
57 while ( ( t = G_Find( t, FOFS( targetname ), ent->target ) ) != NULL ) {
58 if ( !t->item ) {
59 continue;
60 }
61 Touch_Item( t, activator, &trace );
62
63 // make sure it isn't going to respawn or show any events
64 t->nextthink = 0;
65 trap_UnlinkEntity( t );
66 }
67 }
68
SP_target_give(gentity_t * ent)69 void SP_target_give( gentity_t *ent ) {
70 ent->use = Use_Target_Give;
71 }
72
73
74 //==========================================================
75
76 /*QUAKED target_remove_powerups (1 0 0) (-8 -8 -8) (8 8 8)
77 takes away all the activators powerups.
78 Used to drop flight powerups into death puts.
79 */
Use_target_remove_powerups(gentity_t * ent,gentity_t * other,gentity_t * activator)80 void Use_target_remove_powerups( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
81 if ( !activator->client ) {
82 return;
83 }
84
85 if ( activator->client->ps.powerups[PW_REDFLAG] ) {
86 Team_ReturnFlag( TEAM_RED );
87 } else if ( activator->client->ps.powerups[PW_BLUEFLAG] ) {
88 Team_ReturnFlag( TEAM_BLUE );
89 }
90
91 memset( activator->client->ps.powerups, 0, sizeof( activator->client->ps.powerups ) );
92 }
93
SP_target_remove_powerups(gentity_t * ent)94 void SP_target_remove_powerups( gentity_t *ent ) {
95 ent->use = Use_target_remove_powerups;
96 }
97
98
99 //==========================================================
100
101 /*QUAKED target_delay (1 1 0) (-8 -8 -8) (8 8 8)
102 "wait" seconds to pause before firing targets.
103 "random" delay variance, total delay = delay +/- random seconds
104 */
Think_Target_Delay(gentity_t * ent)105 void Think_Target_Delay( gentity_t *ent ) {
106 G_UseTargets( ent, ent->activator );
107 }
108
Use_Target_Delay(gentity_t * ent,gentity_t * other,gentity_t * activator)109 void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
110 ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
111 ent->think = Think_Target_Delay;
112 ent->activator = activator;
113 }
114
SP_target_delay(gentity_t * ent)115 void SP_target_delay( gentity_t *ent ) {
116 // check delay for backwards compatability
117 if ( !G_SpawnFloat( "delay", "0", &ent->wait ) ) {
118 G_SpawnFloat( "wait", "1", &ent->wait );
119 }
120
121 if ( !ent->wait ) {
122 ent->wait = 1;
123 }
124 ent->use = Use_Target_Delay;
125 }
126
127
128 //==========================================================
129
130 /*QUAKED target_score (1 0 0) (-8 -8 -8) (8 8 8)
131 "count" number of points to add, default 1
132
133 The activator is given this many points.
134 */
Use_Target_Score(gentity_t * ent,gentity_t * other,gentity_t * activator)135 void Use_Target_Score( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
136 AddScore( activator, ent->count );
137 }
138
SP_target_score(gentity_t * ent)139 void SP_target_score( gentity_t *ent ) {
140 if ( !ent->count ) {
141 ent->count = 1;
142 }
143 ent->use = Use_Target_Score;
144 }
145
146
147
148 //==========================================================
149
150 /*QUAKED target_print (1 0 0) (-8 -8 -8) (8 8 8) redteam blueteam private
151 "message" text to print
152 If "private", only the activator gets the message. If no checks, all clients get the message.
153 */
Use_Target_Print(gentity_t * ent,gentity_t * other,gentity_t * activator)154 void Use_Target_Print( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
155 if ( activator->client && ( ent->spawnflags & 4 ) ) {
156 trap_SendServerCommand( activator - g_entities, va( "cp \"%s\"", ent->message ) );
157 return;
158 }
159
160 if ( ent->spawnflags & 3 ) {
161 if ( ent->spawnflags & 1 ) {
162 G_TeamCommand( TEAM_RED, va( "cp \"%s\"", ent->message ) );
163 }
164 if ( ent->spawnflags & 2 ) {
165 G_TeamCommand( TEAM_BLUE, va( "cp \"%s\"", ent->message ) );
166 }
167 return;
168 }
169
170 trap_SendServerCommand( -1, va( "cp \"%s\"", ent->message ) );
171 }
172
SP_target_print(gentity_t * ent)173 void SP_target_print( gentity_t *ent ) {
174 ent->use = Use_Target_Print;
175 }
176
177
178 //==========================================================
179
180
181 /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) LOOPED_ON LOOPED_OFF GLOBAL ACTIVATOR VIS_MULTIPLE NO_PVS
182 "noise" wav file to play
183
184 A global sound will play full volume throughout the level.
185 Activator sounds will play on the player that activated the target.
186 Global and activator sounds can't be combined with looping.
187 Normal sounds play each time the target is used.
188 Looped sounds will be toggled by use functions.
189 Multiple identical looping sounds will just increase volume without any speed cost.
190 NO_PVS - this sound will not turn off when not in the player's PVS
191 "wait" : Seconds between auto triggerings, 0 = don't auto trigger
192 "random" : wait variance, default is 0
193 */
Use_Target_Speaker(gentity_t * ent,gentity_t * other,gentity_t * activator)194 void Use_Target_Speaker( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
195 if ( ent->spawnflags & 3 ) { // looping sound toggles
196 if ( ent->s.loopSound ) {
197 ent->s.loopSound = 0; // turn it off
198 } else {
199 ent->s.loopSound = ent->noise_index; // start it
200 }
201 } else { // normal sound
202 if ( ent->spawnflags & 8 ) {
203 G_AddEvent( activator, EV_GENERAL_SOUND, ent->noise_index );
204 } else if ( ent->spawnflags & 4 ) {
205 G_AddEvent( ent, EV_GLOBAL_SOUND, ent->noise_index );
206 } else {
207 G_AddEvent( ent, EV_GENERAL_SOUND, ent->noise_index );
208 }
209 }
210 }
211
target_speaker_multiple(gentity_t * ent)212 void target_speaker_multiple( gentity_t *ent ) {
213 gentity_t *vis_dummy = NULL;
214
215 if ( !( ent->target ) ) {
216 G_Error( "target_speaker missing target at pos %s", vtos( ent->s.origin ) );
217 }
218
219 vis_dummy = G_Find( NULL, FOFS( targetname ), ent->target );
220
221 if ( vis_dummy ) {
222 ent->s.otherEntityNum = vis_dummy->s.number;
223 } else {
224 G_Error( "target_speaker cant find vis_dummy_multiple %s", vtos( ent->s.origin ) );
225 }
226
227 }
228
SP_target_speaker(gentity_t * ent)229 void SP_target_speaker( gentity_t *ent ) {
230 char buffer[MAX_QPATH];
231 char *s;
232
233 G_SpawnFloat( "wait", "0", &ent->wait );
234 G_SpawnFloat( "random", "0", &ent->random );
235
236 if ( !G_SpawnString( "noise", "NOSOUND", &s ) ) {
237 G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) );
238 }
239
240 // force all client relative sounds to be "activator" speakers that
241 // play on the entity that activates it
242 if ( s[0] == '*' ) {
243 ent->spawnflags |= 8;
244 }
245
246 // Ridah, had to disable this so we can use sound scripts
247 // don't worry, if the script isn't found, it'll default back to
248 // .wav on the client-side
249 //if (!strstr( s, ".wav" )) {
250 // Com_sprintf (buffer, sizeof(buffer), "%s.wav", s );
251 //} else {
252 Q_strncpyz( buffer, s, sizeof( buffer ) );
253 //}
254 ent->noise_index = G_SoundIndex( buffer );
255
256 // a repeating speaker can be done completely client side
257 ent->s.eType = ET_SPEAKER;
258 ent->s.eventParm = ent->noise_index;
259 ent->s.frame = ent->wait * 10;
260 ent->s.clientNum = ent->random * 10;
261
262
263 // check for prestarted looping sound
264 if ( ent->spawnflags & 1 ) {
265 ent->s.loopSound = ent->noise_index;
266 }
267
268 ent->use = Use_Target_Speaker;
269
270 // GLOBAL
271 if ( ent->spawnflags & ( 4 | 32 ) ) {
272 ent->r.svFlags |= SVF_BROADCAST;
273 }
274
275 VectorCopy( ent->s.origin, ent->s.pos.trBase );
276
277 if ( ent->spawnflags & 16 ) {
278 ent->think = target_speaker_multiple;
279 ent->nextthink = level.time + 50;
280 }
281
282 // NO_PVS
283 if ( ent->spawnflags & 32 ) {
284 ent->s.density = 1;
285 } else {
286 ent->s.density = 0;
287 }
288
289 if ( ent->radius ) {
290 ent->s.dmgFlags = ent->radius; // store radius in dmgflags
291 } else {
292 ent->s.dmgFlags = 0;
293 }
294
295
296 // must link the entity so we get areas and clusters so
297 // the server can determine who to send updates to
298 trap_LinkEntity( ent );
299 }
300
301
302
303 //==========================================================
304
305 /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON
306 When triggered, fires a laser. You can either set a target or a direction.
307 */
target_laser_think(gentity_t * self)308 void target_laser_think( gentity_t *self ) {
309 vec3_t end;
310 trace_t tr;
311 vec3_t point;
312
313 // if pointed at another entity, set movedir to point at it
314 if ( self->enemy ) {
315 VectorMA( self->enemy->s.origin, 0.5, self->enemy->r.mins, point );
316 VectorMA( point, 0.5, self->enemy->r.maxs, point );
317 VectorSubtract( point, self->s.origin, self->movedir );
318 VectorNormalize( self->movedir );
319 }
320
321 // fire forward and see what we hit
322 VectorMA( self->s.origin, 2048, self->movedir, end );
323
324 trap_Trace( &tr, self->s.origin, NULL, NULL, end, self->s.number, CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE );
325
326 if ( tr.entityNum ) {
327 // hurt it if we can
328 G_Damage( &g_entities[tr.entityNum], self, self->activator, self->movedir,
329 tr.endpos, self->damage, DAMAGE_NO_KNOCKBACK, MOD_TARGET_LASER );
330 }
331
332 VectorCopy( tr.endpos, self->s.origin2 );
333
334 trap_LinkEntity( self );
335 self->nextthink = level.time + FRAMETIME;
336 }
337
target_laser_on(gentity_t * self)338 void target_laser_on( gentity_t *self ) {
339 if ( !self->activator ) {
340 self->activator = self;
341 }
342 target_laser_think( self );
343 }
344
target_laser_off(gentity_t * self)345 void target_laser_off( gentity_t *self ) {
346 trap_UnlinkEntity( self );
347 self->nextthink = 0;
348 }
349
target_laser_use(gentity_t * self,gentity_t * other,gentity_t * activator)350 void target_laser_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
351 self->activator = activator;
352 if ( self->nextthink > 0 ) {
353 target_laser_off( self );
354 } else {
355 target_laser_on( self );
356 }
357 }
358
target_laser_start(gentity_t * self)359 void target_laser_start( gentity_t *self ) {
360 gentity_t *ent;
361
362 self->s.eType = ET_BEAM;
363
364 if ( self->target ) {
365 ent = G_Find( NULL, FOFS( targetname ), self->target );
366 if ( !ent ) {
367 G_Printf( "%s at %s: %s is a bad target\n", self->classname, vtos( self->s.origin ), self->target );
368 }
369 self->enemy = ent;
370 } else {
371 G_SetMovedir( self->s.angles, self->movedir );
372 }
373
374 self->use = target_laser_use;
375 self->think = target_laser_think;
376
377 if ( !self->damage ) {
378 self->damage = 1;
379 }
380
381 if ( self->spawnflags & 1 ) {
382 target_laser_on( self );
383 } else {
384 target_laser_off( self );
385 }
386 }
387
SP_target_laser(gentity_t * self)388 void SP_target_laser( gentity_t *self ) {
389 // let everything else get spawned before we start firing
390 self->think = target_laser_start;
391 self->nextthink = level.time + FRAMETIME;
392 }
393
394
395 //==========================================================
396
target_teleporter_use(gentity_t * self,gentity_t * other,gentity_t * activator)397 void target_teleporter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
398 gentity_t *dest;
399
400 if ( !activator->client ) {
401 return;
402 }
403 dest = G_PickTarget( self->target );
404 if ( !dest ) {
405 G_Printf( "Couldn't find teleporter destination\n" );
406 return;
407 }
408
409 TeleportPlayer( activator, dest->s.origin, dest->s.angles );
410 }
411
412 /*QUAKED target_teleporter (1 0 0) (-8 -8 -8) (8 8 8)
413 The activator will be teleported away.
414 */
SP_target_teleporter(gentity_t * self)415 void SP_target_teleporter( gentity_t *self ) {
416 if ( !self->targetname ) {
417 G_Printf( "untargeted %s at %s\n", self->classname, vtos( self->s.origin ) );
418 }
419
420 self->use = target_teleporter_use;
421 }
422
423 //==========================================================
424
425
426 /*QUAKED target_relay (1 1 0) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM NOKEY_ONLY TAKE_KEY NO_LOCKED_NOISE
427 This doesn't perform any actions except fire its targets.
428 The activator can be forced to be from a certain team.
429 if RANDOM is checked, only one of the targets will be fired, not all of them
430 "key" specifies an item you can be carrying that affects the operation of this relay
431 this key is currently an int (1-16) which matches the id of a key entity (key_key1 = 1, etc)
432 NOKEY_ONLY means "fire only if I do /not/ have the specified key"
433 TAKE_KEY removes the key from the players inventory
434 "lockednoise" specifies a .wav file to play if the relay is used and the player doesn't have the necessary key.
435 By default this sound is "sound/movers/doors/default_door_locked.wav"
436 NO_LOCKED_NOISE specifies that it will be silent if activated without proper key
437 */
target_relay_use(gentity_t * self,gentity_t * other,gentity_t * activator)438 void target_relay_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
439 if ( ( self->spawnflags & 1 ) && activator && activator->client
440 && activator->client->sess.sessionTeam != TEAM_RED ) {
441 return;
442 }
443 if ( ( self->spawnflags & 2 ) && activator && activator->client
444 && activator->client->sess.sessionTeam != TEAM_BLUE ) {
445 return;
446 }
447
448 if ( self->spawnflags & 4 ) {
449 gentity_t *ent;
450
451 ent = G_PickTarget( self->target );
452 if ( ent && ent->use ) {
453 ent->use( ent, self, activator );
454 }
455 return;
456 }
457
458 if ( activator ) { // activator can be NULL if called from script
459 if ( self->key ) {
460 gitem_t *item;
461
462 if ( self->key == -1 ) { // relay permanently locked
463 if ( self->soundPos1 ) {
464 G_Sound( self, self->soundPos1 ); //----(SA) added
465 }
466 return;
467 }
468
469 item = BG_FindItemForKey( self->key, 0 );
470
471 if ( item ) {
472 if ( activator->client->ps.stats[STAT_KEYS] & ( 1 << item->giTag ) ) { // user has key
473 if ( self->spawnflags & 8 ) { // relay is NOKEY_ONLY and player has key
474 if ( self->soundPos1 ) {
475 G_Sound( self, self->soundPos1 ); //----(SA) added
476 }
477 return;
478 }
479 } else // user does not have key
480 {
481 if ( !( self->spawnflags & 8 ) ) {
482 if ( self->soundPos1 ) {
483 G_Sound( self, self->soundPos1 ); //----(SA) added
484 }
485 return;
486 }
487 }
488 }
489
490 if ( item && ( self->spawnflags & 16 ) ) { // (SA) take key
491 activator->client->ps.stats[STAT_KEYS] &= ~( 1 << item->giTag );
492 // (SA) TODO: "took inventory item" sound
493 }
494 }
495 }
496
497 G_UseTargets( self, activator );
498 }
499
500
relay_AIScript_AlertEntity(gentity_t * self)501 void relay_AIScript_AlertEntity( gentity_t *self ) {
502 self->use( self, NULL, NULL );
503 }
504
505
506 /*
507 ==============
508 SP_target_relay
509 ==============
510 */
SP_target_relay(gentity_t * self)511 void SP_target_relay( gentity_t *self ) {
512 char *sound;
513
514 self->use = target_relay_use;
515 self->AIScript_AlertEntity = relay_AIScript_AlertEntity;
516
517 if ( !( self->spawnflags & 32 ) ) { // !NO_LOCKED_NOISE
518 if ( G_SpawnString( "lockednoise", "0", &sound ) ) {
519 self->soundPos1 = G_SoundIndex( sound );
520 } else {
521 self->soundPos1 = G_SoundIndex( "sound/movers/doors/default_door_locked.wav" );
522 }
523 }
524
525 }
526
527
528 //==========================================================
529
530 /*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8) kill_user_too
531 Kills the activator. (default)
532 If targets, they will be killed when this is fired
533 "kill_user_too" will still kill the activator when this ent has targets (default is only kill targets, not activator)
534 */
target_kill_use(gentity_t * self,gentity_t * other,gentity_t * activator)535 void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
536 gentity_t *targ = NULL;
537
538 if ( self->spawnflags & 1 ) { // kill usertoo
539 G_Damage( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
540 }
541
542 while ( ( targ = G_Find( targ, FOFS( targetname ), self->target ) ) != NULL ) {
543 if ( targ->aiCharacter ) { // (SA) if it's an ai character, free it nicely
544 targ->aiInactive = qtrue;
545 } else
546 {
547 // make sure it isn't going to respawn or show any events
548 targ->nextthink = 0;
549 if ( targ == activator ) {
550 continue;
551 }
552
553 // RF, script_movers should die!
554 if ( !Q_stricmp( targ->classname, "script_mover" ) && targ->die ) {
555 targ->die( targ, self, self, targ->health, 0 );
556 continue;
557 }
558
559 trap_UnlinkEntity( targ );
560 targ->use = 0;
561 targ->touch = 0;
562 targ->nextthink = level.time + FRAMETIME;
563 targ->think = G_FreeEntity;
564 }
565 }
566 }
567
SP_target_kill(gentity_t * self)568 void SP_target_kill( gentity_t *self ) {
569 self->use = target_kill_use;
570 }
571
572 /*DEFUNCT target_position (0 0.5 0) (-4 -4 -4) (4 4 4)
573 Used as a positional target for in-game calculation, like jumppad targets.
574 */
SP_target_position(gentity_t * self)575 void SP_target_position( gentity_t *self ) {
576 G_SetOrigin( self, self->s.origin );
577 }
578
579 // Ridah, note to everyone: static functions can cause problems with the savegame code, so avoid
580 // using them unless necessary. if that function is then assigned to an entity, client or cast field,
581 // the game will not save.
582 //static void target_location_linkup(gentity_t *ent)
target_location_linkup(gentity_t * ent)583 void target_location_linkup( gentity_t *ent ) {
584 int i;
585 int n;
586
587 if ( level.locationLinked ) {
588 return;
589 }
590
591 level.locationLinked = qtrue;
592
593 level.locationHead = NULL;
594
595 trap_SetConfigstring( CS_LOCATIONS, "unknown" );
596
597 for ( i = 0, ent = g_entities, n = 1;
598 i < level.num_entities;
599 i++, ent++ ) {
600 if ( ent->classname && !Q_stricmp( ent->classname, "target_location" ) ) {
601 // lets overload some variables!
602 ent->health = n; // use for location marking
603 trap_SetConfigstring( CS_LOCATIONS + n, ent->message );
604 n++;
605 ent->nextTrain = level.locationHead;
606 level.locationHead = ent;
607 }
608 }
609
610 // All linked together now
611 }
612
613 /*QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8)
614 Set "message" to the name of this location.
615 Set "count" to 0-7 for color.
616 0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white
617
618 Closest target_location in sight used for the location, if none
619 in site, closest in distance
620 */
SP_target_location(gentity_t * self)621 void SP_target_location( gentity_t *self ) {
622 self->think = target_location_linkup;
623 self->nextthink = level.time + 200; // Let them all spawn first
624
625 G_SetOrigin( self, self->s.origin );
626 }
627
628
629
630
631
632
633
634 //---- (SA) Wolf targets
635
636 /*
637 ==============
638 Use_Target_Autosave
639 save game for emergency backup or convienience
640 ==============
641 */
642 /*void Use_Target_Autosave( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
643 G_SaveGame("autosave.svg");
644 }*/
645
646
647
648 /*
649 ==============
650 Use_Target_Counter
651 ==============
652 */
Use_Target_Counter(gentity_t * ent,gentity_t * other,gentity_t * activator)653 void Use_Target_Counter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
654 if ( ent->count < 0 ) { // if the count has already been hit, ignore this
655 return;
656 }
657
658 ent->count -= 1; // dec count
659
660 // G_Printf("count at: %d\n", ent->count);
661
662 if ( !ent->count ) { // specified count is now hit
663 // G_Printf("firing!!\n");
664 G_UseTargets( ent, other );
665 }
666 }
667
668 /*
669 ==============
670 Use_Target_Lock
671 ==============
672 */
Use_Target_Lock(gentity_t * ent,gentity_t * other,gentity_t * activator)673 void Use_Target_Lock( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
674 gentity_t *t = 0;
675
676 while ( ( t = G_Find( t, FOFS( targetname ), ent->target ) ) != NULL )
677 {
678 // G_Printf("target_lock locking entity with key: %d\n", ent->count);
679 t->key = ent->key;
680 G_SetAASBlockingEntity( t, t->key != 0 );
681 }
682
683 }
684
685 //==========================================================
686
687 /*
688 ==============
689 Use_target_fog
690 ==============
691 */
Use_target_fog(gentity_t * ent,gentity_t * other,gentity_t * activator)692 void Use_target_fog( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
693 // CS_FOGVARS reads:
694 // near
695 // far
696 // density
697 // r,g,b
698 // time to complete
699 trap_SetConfigstring( CS_FOGVARS, va( "%f %f %f %f %f %f %i", 1.0f, (float)ent->s.density, 1.0f, (float)ent->dl_color[0], (float)ent->dl_color[1], (float)ent->dl_color[2], ent->s.time ) );
700 }
701
702 /*QUAKED target_fog (1 1 0) (-8 -8 -8) (8 8 8)
703 color picker chooses color of fog
704 "distance" sets fog distance. Use value '0' to give control back to the game (and use the fog values specified in the sky shader if present)
705 "time" time it takes to change fog to new value. default time is 1 sec
706 */
SP_target_fog(gentity_t * ent)707 void SP_target_fog( gentity_t *ent ) {
708 int dist;
709 float ftime;
710
711 ent->use = Use_target_fog;
712
713 // ent->s.density will carry the 'distance' value
714 if ( G_SpawnInt( "distance", "0", &dist ) ) {
715 if ( dist >= 0 ) {
716 ent->s.density = dist;
717 }
718 }
719
720 // ent->s.time will carry the 'time' value
721 if ( G_SpawnFloat( "time", "0.5", &ftime ) ) {
722 if ( ftime >= 0 ) {
723 ent->s.time = ftime * 1000; // sec to ms
724 }
725 }
726 }
727
728 //==========================================================
729
730 /*QUAKED target_counter (1 1 0) (-8 -8 -8) (8 8 8)
731 Increments the counter pointed to.
732 "count" is the key for the count value
733 */
SP_target_counter(gentity_t * ent)734 void SP_target_counter( gentity_t *ent ) {
735 // G_Printf("target counter created with val of: %d\n", ent->count);
736 ent->use = Use_Target_Counter;
737 }
738
739
740
741 /*QUAKED target_autosave (1 1 0) (-8 -8 -8) (8 8 8)
742 saves game to 'autosave.svg' when triggered then dies.
743 */
SP_target_autosave(gentity_t * ent)744 void SP_target_autosave( gentity_t *ent ) {
745 //ent->use = Use_Target_Autosave;
746 G_FreeEntity( ent );
747 }
748
749 //==========================================================
750
751 /*QUAKED target_lock (1 1 0) (-8 -8 -8) (8 8 8)
752 Sets the door to a state requiring key n
753 "key" is the required key
754 so:
755 key:0 unlocks the door
756 key:-1 locks the door until a target_lock with key:0
757 key:n means the door now requires key n
758 */
SP_target_lock(gentity_t * ent)759 void SP_target_lock( gentity_t *ent ) {
760 ent->use = Use_Target_Lock;
761 }
762
763
764
Use_Target_Alarm(gentity_t * ent,gentity_t * other,gentity_t * activator)765 void Use_Target_Alarm( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
766 G_UseTargets( ent, other );
767 }
768
769 /*QUAKED target_alarm (1 1 0) (-4 -4 -4) (4 4 4)
770 does nothing yet (effectively a relay right now)
771 */
SP_target_alarm(gentity_t * ent)772 void SP_target_alarm( gentity_t *ent ) {
773 ent->use = Use_Target_Alarm;
774 }
775
776 //---- end
777
778 /*QUAKED target_smoke (1 0 0) (-32 -32 -16) (32 32 16) Black White SmokeON Gravity
779 1 second = 1000
780 1 FRAME = 100
781 delay = 100 = one millisecond default this is the maximum smoke that will show up
782 time = 5000 default before the smoke disipates
783 duration = 2000 before the smoke starts to alpha
784 start_size = 24 default
785 end_size = 96 default
786 wait = default is 50 the rate at which it will travel up
787 */
788
789 /*void smoke_think (gentity_t *ent)
790 {
791 gentity_t *tent;
792
793 ent->nextthink = level.time + ent->delay;
794
795 if (!(ent->spawnflags & 4))
796 return;
797
798 if (ent->health)
799 {
800 ent->health --;
801 if (!ent->health)
802 {
803 ent->think = G_FreeEntity;
804 ent->nextthink = level.time + FRAMETIME;
805 }
806 }
807
808 tent = G_TempEntity (ent->r.currentOrigin, EV_SMOKE);
809 VectorCopy (ent->r.currentOrigin, tent->s.origin);
810 tent->s.time = ent->speed;
811 tent->s.time2 = ent->duration;
812 tent->s.density = ent->s.density;
813
814 // this is used to set the size of the smoke particle
815 tent->s.angles2[0] = ent->start_size;
816 tent->s.angles2[1] = ent->end_size;
817 tent->s.angles2[2] = ent->wait;
818
819 VectorCopy (ent->pos3, tent->s.origin2);
820
821 if (ent->s.frame) // denotes reverse gravity effect
822 tent->s.frame = 1;
823
824 }*/
825
smoke_think(gentity_t * ent)826 void smoke_think( gentity_t *ent ) {
827 ent->nextthink = level.time + ent->s.constantLight;
828
829 if ( !( ent->spawnflags & 4 ) ) {
830 return;
831 }
832
833 if ( ent->s.dl_intensity ) {
834 ent->s.dl_intensity--;
835 if ( !ent->s.dl_intensity ) {
836 ent->think = G_FreeEntity;
837 ent->nextthink = level.time + FRAMETIME;
838 }
839 }
840 }
841
smoke_toggle(gentity_t * ent,gentity_t * self,gentity_t * activator)842 void smoke_toggle( gentity_t *ent, gentity_t *self, gentity_t *activator ) {
843 if ( ent->spawnflags & 4 ) { // smoke is on turn it off
844 ent->spawnflags &= ~4;
845 trap_UnlinkEntity( ent );
846 } else
847 {
848 ent->spawnflags |= 4;
849 trap_LinkEntity( ent );
850 }
851 }
852
smoke_init(gentity_t * ent)853 void smoke_init( gentity_t *ent ) {
854 gentity_t *target;
855 vec3_t vec;
856
857 ent->think = smoke_think;
858 ent->nextthink = level.time + FRAMETIME;
859
860 if ( ent->target ) {
861 target = G_Find( NULL, FOFS( targetname ), ent->target );
862 if ( target ) {
863 VectorSubtract( target->s.origin, ent->s.origin, vec );
864 VectorCopy( vec, ent->pos3 );
865 } else {
866 VectorSet( ent->s.origin2, 0, 0, 1 );
867 }
868 } else
869 {
870 VectorSet( ent->s.origin2, 0, 0, 1 );
871 }
872
873 if ( ent->spawnflags & 4 ) {
874 trap_LinkEntity( ent );
875 }
876 }
877
SP_target_smoke(gentity_t * ent)878 void SP_target_smoke( gentity_t *ent ) {
879
880 // (SA) don't use in multiplayer right now since it makes decyphering net messages almost impossible
881 // ent->think = G_FreeEntity;
882 // return;
883 // Arnout - modified this a lot to be sent to the client as one entity and then is shown at the client
884
885 if ( !ent->delay ) {
886 ent->delay = 100;
887 }
888
889 ent->use = smoke_toggle;
890
891 ent->think = smoke_init;
892 ent->nextthink = level.time + FRAMETIME;
893
894 G_SetOrigin( ent, ent->s.origin );
895 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
896 ent->s.eType = ET_SMOKER;
897
898 if ( ent->spawnflags & 2 ) {
899 ent->s.density = 4;
900 } else {
901 ent->s.density = 0;
902 }
903
904 // using "time"
905 ent->s.time = ent->speed;
906 if ( !ent->s.time ) {
907 ent->s.time = 5000; // 5 seconds
908
909 }
910 ent->s.time2 = ent->duration;
911 if ( !ent->s.time2 ) {
912 ent->s.time2 = 2000;
913 }
914
915 ent->s.angles2[0] = ent->start_size;
916 if ( !ent->s.angles2[0] ) {
917 ent->s.angles2[0] = 24;
918 }
919
920 ent->s.angles2[1] = ent->end_size;
921 if ( !ent->s.angles2[1] ) {
922 ent->s.angles2[1] = 96;
923 }
924
925 ent->s.angles2[2] = ent->wait;
926 if ( !ent->s.angles2[2] ) {
927 ent->s.angles2[2] = 50;
928 }
929
930 // idiot check
931 if ( ent->s.time < ent->s.time2 ) {
932 ent->s.time = ent->s.time2 + 100;
933 }
934
935 if ( ent->spawnflags & 8 ) {
936 ent->s.frame = 1;
937 }
938
939 ent->s.dl_intensity = ent->health;
940 ent->s.constantLight = ent->delay;
941
942 if ( ent->spawnflags & 4 ) {
943 trap_LinkEntity( ent );
944 }
945
946 }
947
948
949 /*QUAKED target_script_trigger (1 .7 .2) (-8 -8 -8) (8 8 8)
950 must have an aiName
951 must have a target
952
953 when used it will fire its targets
954 */
target_script_trigger_use(gentity_t * ent,gentity_t * other,gentity_t * activator)955 void target_script_trigger_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
956 gentity_t *player;
957
958 if ( ent->aiName ) {
959 player = AICast_FindEntityForName( "player" );
960 if ( player ) {
961 AICast_ScriptEvent( AICast_GetCastState( player->s.number ), "trigger", ent->target );
962 }
963 }
964
965 // DHM - Nerve :: In multiplayer, we use the brush scripting only
966 if ( g_gametype.integer >= GT_WOLF && ent->scriptName ) {
967 G_Script_ScriptEvent( ent, "trigger", ent->target );
968 }
969
970 G_UseTargets( ent, other );
971
972 }
973
SP_target_script_trigger(gentity_t * ent)974 void SP_target_script_trigger( gentity_t *ent ) {
975 G_SetOrigin( ent, ent->s.origin );
976 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
977 ent->s.eType = ET_GENERAL;
978 ent->use = target_script_trigger_use;
979 }
980
981
982 /*QUAKED target_rumble (0 0.75 0.8) (-8 -8 -8) (8 8 8) STARTOFF
983 wait = default is 2 seconds = time the entity will enable rumble effect
984 "pitch" value from 1 to 10 default is 5
985 "yaw" value from 1 to 10 default is 5
986
987 "rampup" how much time it will take to reach maximum pitch and yaw in seconds
988 "rampdown" how long till effect ends after rampup is reached in seconds
989
990 "startnoise" startingsound
991 "noise" the looping sound entity is to make
992 "endnoise" endsound
993
994 "duration" the amount of time the effect is to last ei 1.0 sec 3.6 sec
995 */
996 int rumble_snd;
997
target_rumble_think(gentity_t * ent)998 void target_rumble_think( gentity_t * ent ) {
999 gentity_t *tent;
1000 float ratio;
1001 float time, time2;
1002 float dapitch, dayaw;
1003 qboolean validrumble = qtrue;
1004
1005 if ( !( ent->count ) ) {
1006 ent->timestamp = level.time;
1007 ent->count++;
1008 // start sound here
1009 if ( ent->soundPos1 ) {
1010 G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos1 );
1011 }
1012 } else
1013 {
1014 // looping sound
1015 ent->s.loopSound = ent->soundLoop;
1016 }
1017
1018 dapitch = ent->delay;
1019 dayaw = ent->random;
1020 ratio = 1.0f;
1021
1022 if ( ent->start_size ) {
1023 if ( level.time < ( ent->timestamp + ent->start_size ) ) {
1024 time = level.time - ent->timestamp;
1025 time2 = ( ent->timestamp + ent->start_size ) - ent->timestamp;
1026 ratio = time / time2;
1027 } else if ( level.time < ( ent->timestamp + ent->end_size + ent->start_size ) ) {
1028 time = level.time - ent->timestamp;
1029 time2 = ( ent->timestamp + ent->start_size + ent->end_size ) - ent->timestamp;
1030 ratio = time2 / time;
1031 } else {
1032 validrumble = qfalse;
1033 }
1034 }
1035
1036 if ( validrumble ) {
1037 tent = G_TempEntity( ent->r.currentOrigin, EV_RUMBLE_EFX );
1038
1039 tent->s.angles[0] = dapitch * ratio;
1040 tent->s.angles[1] = dayaw * ratio;
1041 }
1042
1043 // end sound
1044 if ( level.time > ent->duration + ent->timestamp ) {
1045 if ( ent->soundPos2 ) {
1046 G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos2 );
1047 ent->s.loopSound = 0;
1048 }
1049
1050 ent->nextthink = 0;
1051 } else {
1052 ent->nextthink = level.time + 50;
1053 }
1054
1055 }
1056
target_rumble_use(gentity_t * ent,gentity_t * other,gentity_t * activator)1057 void target_rumble_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
1058 if ( ent->spawnflags & 1 ) {
1059 ent->spawnflags &= ~1;
1060 ent->think = target_rumble_think;
1061 ent->count = 0;
1062 ent->nextthink = level.time + 50;
1063 } else
1064 {
1065 ent->spawnflags |= 1;
1066 ent->think = 0;
1067 ent->count = 0;
1068 }
1069 }
1070
SP_target_rumble(gentity_t * self)1071 void SP_target_rumble( gentity_t *self ) {
1072 char *pitch;
1073 char *yaw;
1074 char *rampup;
1075 char *rampdown;
1076 float dapitch;
1077 float dayaw;
1078 char *sound;
1079 char *startsound;
1080 char *endsound;
1081
1082 if ( G_SpawnString( "noise", "100", &sound ) ) {
1083 self->soundLoop = G_SoundIndex( sound );
1084 }
1085
1086 if ( G_SpawnString( "startnoise", "100", &startsound ) ) {
1087 self->soundPos1 = G_SoundIndex( startsound );
1088 }
1089
1090 if ( G_SpawnString( "endnoise", "100", &endsound ) ) {
1091 self->soundPos2 = G_SoundIndex( endsound );
1092 }
1093
1094 self->use = target_rumble_use;
1095
1096 G_SpawnString( "pitch", "0", &pitch );
1097 dapitch = atof( pitch );
1098 self->delay = dapitch;
1099 if ( !( self->delay ) ) {
1100 self->delay = 5;
1101 }
1102
1103 G_SpawnString( "yaw", "0", &yaw );
1104 dayaw = atof( yaw );
1105 self->random = dayaw;
1106 if ( !( self->random ) ) {
1107 self->random = 5;
1108 }
1109
1110 G_SpawnString( "rampup", "0", &rampup );
1111 self->start_size = atoi( rampup ) * 1000;
1112 if ( !( self->start_size ) ) {
1113 self->start_size = 1000;
1114 }
1115
1116 G_SpawnString( "rampdown", "0", &rampdown );
1117 self->end_size = atoi( rampdown ) * 1000;
1118 if ( !( self->end_size ) ) {
1119 self->end_size = 1000;
1120 }
1121
1122 if ( !( self->duration ) ) {
1123 self->duration = 1000;
1124 } else {
1125 self->duration *= 1000;
1126 }
1127
1128 trap_LinkEntity( self );
1129 }
1130