1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 //
23 #include "g_local.h"
24
25 //==========================================================
26
27 /*QUAKED target_give (1 0 0) (-8 -8 -8) (8 8 8)
28 Gives the activator all the items pointed to.
29 */
Use_Target_Give(gentity_t * ent,gentity_t * other,gentity_t * activator)30 void Use_Target_Give( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
31 gentity_t *t;
32 trace_t trace;
33
34 if ( !activator->client ) {
35 return;
36 }
37
38 if ( !ent->target ) {
39 return;
40 }
41
42 memset( &trace, 0, sizeof( trace ) );
43 t = NULL;
44 while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) {
45 if ( !t->item ) {
46 continue;
47 }
48 Touch_Item( t, activator, &trace );
49
50 // make sure it isn't going to respawn or show any events
51 t->nextthink = 0;
52 trap_UnlinkEntity( t );
53 }
54 }
55
SP_target_give(gentity_t * ent)56 void SP_target_give( gentity_t *ent ) {
57 ent->use = Use_Target_Give;
58 }
59
60
61 //==========================================================
62
63 /*QUAKED target_remove_powerups (1 0 0) (-8 -8 -8) (8 8 8)
64 takes away all the activators powerups.
65 Used to drop flight powerups into death puts.
66 */
Use_target_remove_powerups(gentity_t * ent,gentity_t * other,gentity_t * activator)67 void Use_target_remove_powerups( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
68 if( !activator->client ) {
69 return;
70 }
71
72 if( activator->client->ps.powerups[PW_REDFLAG] ) {
73 Team_ReturnFlag( TEAM_RED );
74 } else if( activator->client->ps.powerups[PW_BLUEFLAG] ) {
75 Team_ReturnFlag( TEAM_BLUE );
76 } else if( activator->client->ps.powerups[PW_NEUTRALFLAG] ) {
77 Team_ReturnFlag( TEAM_FREE );
78 }
79
80 memset( activator->client->ps.powerups, 0, sizeof( activator->client->ps.powerups ) );
81 }
82
SP_target_remove_powerups(gentity_t * ent)83 void SP_target_remove_powerups( gentity_t *ent ) {
84 ent->use = Use_target_remove_powerups;
85 }
86
87
88 //==========================================================
89
90 /*QUAKED target_delay (1 0 0) (-8 -8 -8) (8 8 8)
91 "wait" seconds to pause before firing targets.
92 "random" delay variance, total delay = delay +/- random seconds
93 */
Think_Target_Delay(gentity_t * ent)94 void Think_Target_Delay( gentity_t *ent ) {
95 G_UseTargets( ent, ent->activator );
96 }
97
Use_Target_Delay(gentity_t * ent,gentity_t * other,gentity_t * activator)98 void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
99 ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
100 ent->think = Think_Target_Delay;
101 ent->activator = activator;
102 }
103
SP_target_delay(gentity_t * ent)104 void SP_target_delay( gentity_t *ent ) {
105 // check delay for backwards compatability
106 if ( !G_SpawnFloat( "delay", "0", &ent->wait ) ) {
107 G_SpawnFloat( "wait", "1", &ent->wait );
108 }
109
110 if ( !ent->wait ) {
111 ent->wait = 1;
112 }
113 ent->use = Use_Target_Delay;
114 }
115
116
117 //==========================================================
118
119 /*QUAKED target_score (1 0 0) (-8 -8 -8) (8 8 8)
120 "count" number of points to add, default 1
121
122 The activator is given this many points.
123 */
Use_Target_Score(gentity_t * ent,gentity_t * other,gentity_t * activator)124 void Use_Target_Score (gentity_t *ent, gentity_t *other, gentity_t *activator) {
125 AddScore( activator, ent->r.currentOrigin, ent->count );
126 }
127
SP_target_score(gentity_t * ent)128 void SP_target_score( gentity_t *ent ) {
129 if ( !ent->count ) {
130 ent->count = 1;
131 }
132 ent->use = Use_Target_Score;
133 }
134
135
136 //==========================================================
137
138 /*QUAKED target_print (1 0 0) (-8 -8 -8) (8 8 8) redteam blueteam private
139 "message" text to print
140 If "private", only the activator gets the message. If no checks, all clients get the message.
141 */
Use_Target_Print(gentity_t * ent,gentity_t * other,gentity_t * activator)142 void Use_Target_Print (gentity_t *ent, gentity_t *other, gentity_t *activator) {
143 if ( activator->client && ( ent->spawnflags & 4 ) ) {
144 trap_SendServerCommand( activator-g_entities, va("cp \"%s\"", ent->message ));
145 return;
146 }
147
148 if ( ent->spawnflags & 3 ) {
149 if ( ent->spawnflags & 1 ) {
150 G_TeamCommand( TEAM_RED, va("cp \"%s\"", ent->message) );
151 }
152 if ( ent->spawnflags & 2 ) {
153 G_TeamCommand( TEAM_BLUE, va("cp \"%s\"", ent->message) );
154 }
155 return;
156 }
157
158 trap_SendServerCommand( -1, va("cp \"%s\"", ent->message ));
159 }
160
SP_target_print(gentity_t * ent)161 void SP_target_print( gentity_t *ent ) {
162 ent->use = Use_Target_Print;
163 }
164
165
166 //==========================================================
167
168
169 /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off global activator
170 "noise" wav file to play
171
172 A global sound will play full volume throughout the level.
173 Activator sounds will play on the player that activated the target.
174 Global and activator sounds can't be combined with looping.
175 Normal sounds play each time the target is used.
176 Looped sounds will be toggled by use functions.
177 Multiple identical looping sounds will just increase volume without any speed cost.
178 "wait" : Seconds between auto triggerings, 0 = don't auto trigger
179 "random" wait variance, default is 0
180 */
Use_Target_Speaker(gentity_t * ent,gentity_t * other,gentity_t * activator)181 void Use_Target_Speaker (gentity_t *ent, gentity_t *other, gentity_t *activator) {
182 if (ent->spawnflags & 3) { // looping sound toggles
183 if (ent->s.loopSound)
184 ent->s.loopSound = 0; // turn it off
185 else
186 ent->s.loopSound = ent->noise_index; // start it
187 }else { // normal sound
188 if ( ent->spawnflags & 8 ) {
189 G_AddEvent( activator, EV_GENERAL_SOUND, ent->noise_index );
190 } else if (ent->spawnflags & 4) {
191 G_AddEvent( ent, EV_GLOBAL_SOUND, ent->noise_index );
192 } else {
193 G_AddEvent( ent, EV_GENERAL_SOUND, ent->noise_index );
194 }
195 }
196 }
197
SP_target_speaker(gentity_t * ent)198 void SP_target_speaker( gentity_t *ent ) {
199 char buffer[MAX_QPATH];
200 char *s;
201
202 G_SpawnFloat( "wait", "0", &ent->wait );
203 G_SpawnFloat( "random", "0", &ent->random );
204
205 if ( !G_SpawnString( "noise", "NOSOUND", &s ) ) {
206 G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) );
207 }
208
209 // force all client reletive sounds to be "activator" speakers that
210 // play on the entity that activates it
211 if ( s[0] == '*' ) {
212 ent->spawnflags |= 8;
213 }
214
215 if (!strstr( s, ".wav" )) {
216 Com_sprintf (buffer, sizeof(buffer), "%s.wav", s );
217 } else {
218 Q_strncpyz( buffer, s, sizeof(buffer) );
219 }
220 ent->noise_index = G_SoundIndex(buffer);
221
222 // a repeating speaker can be done completely client side
223 ent->s.eType = ET_SPEAKER;
224 ent->s.eventParm = ent->noise_index;
225 ent->s.frame = ent->wait * 10;
226 ent->s.clientNum = ent->random * 10;
227
228
229 // check for prestarted looping sound
230 if ( ent->spawnflags & 1 ) {
231 ent->s.loopSound = ent->noise_index;
232 }
233
234 ent->use = Use_Target_Speaker;
235
236 if (ent->spawnflags & 4) {
237 ent->r.svFlags |= SVF_BROADCAST;
238 }
239
240 VectorCopy( ent->s.origin, ent->s.pos.trBase );
241
242 // must link the entity so we get areas and clusters so
243 // the server can determine who to send updates to
244 trap_LinkEntity( ent );
245 }
246
247
248
249 //==========================================================
250
251 /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON
252 When triggered, fires a laser. You can either set a target or a direction.
253 */
target_laser_think(gentity_t * self)254 void target_laser_think (gentity_t *self) {
255 vec3_t end;
256 trace_t tr;
257 vec3_t point;
258
259 // if pointed at another entity, set movedir to point at it
260 if ( self->enemy ) {
261 VectorMA (self->enemy->s.origin, 0.5, self->enemy->r.mins, point);
262 VectorMA (point, 0.5, self->enemy->r.maxs, point);
263 VectorSubtract (point, self->s.origin, self->movedir);
264 VectorNormalize (self->movedir);
265 }
266
267 // fire forward and see what we hit
268 VectorMA (self->s.origin, 2048, self->movedir, end);
269
270 trap_Trace( &tr, self->s.origin, NULL, NULL, end, self->s.number, CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_CORPSE);
271
272 if ( tr.entityNum ) {
273 // hurt it if we can
274 G_Damage ( &g_entities[tr.entityNum], self, self->activator, self->movedir,
275 tr.endpos, self->damage, DAMAGE_NO_KNOCKBACK, MOD_TARGET_LASER);
276 }
277
278 VectorCopy (tr.endpos, self->s.origin2);
279
280 trap_LinkEntity( self );
281 self->nextthink = level.time + FRAMETIME;
282 }
283
target_laser_on(gentity_t * self)284 void target_laser_on (gentity_t *self)
285 {
286 if (!self->activator)
287 self->activator = self;
288 target_laser_think (self);
289 }
290
target_laser_off(gentity_t * self)291 void target_laser_off (gentity_t *self)
292 {
293 trap_UnlinkEntity( self );
294 self->nextthink = 0;
295 }
296
target_laser_use(gentity_t * self,gentity_t * other,gentity_t * activator)297 void target_laser_use (gentity_t *self, gentity_t *other, gentity_t *activator)
298 {
299 self->activator = activator;
300 if ( self->nextthink > 0 )
301 target_laser_off (self);
302 else
303 target_laser_on (self);
304 }
305
target_laser_start(gentity_t * self)306 void target_laser_start (gentity_t *self)
307 {
308 gentity_t *ent;
309
310 self->s.eType = ET_BEAM;
311
312 if (self->target) {
313 ent = G_Find (NULL, FOFS(targetname), self->target);
314 if (!ent) {
315 G_Printf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
316 }
317 self->enemy = ent;
318 } else {
319 G_SetMovedir (self->s.angles, self->movedir);
320 }
321
322 self->use = target_laser_use;
323 self->think = target_laser_think;
324
325 if ( !self->damage ) {
326 self->damage = 1;
327 }
328
329 if (self->spawnflags & 1)
330 target_laser_on (self);
331 else
332 target_laser_off (self);
333 }
334
SP_target_laser(gentity_t * self)335 void SP_target_laser (gentity_t *self)
336 {
337 // let everything else get spawned before we start firing
338 self->think = target_laser_start;
339 self->nextthink = level.time + FRAMETIME;
340 }
341
342
343 //==========================================================
344
target_teleporter_use(gentity_t * self,gentity_t * other,gentity_t * activator)345 void target_teleporter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
346 gentity_t *dest;
347
348 if (!activator->client)
349 return;
350 dest = G_PickTarget( self->target );
351 if (!dest) {
352 G_Printf ("Couldn't find teleporter destination\n");
353 return;
354 }
355
356 TeleportPlayer( activator, dest->s.origin, dest->s.angles );
357 }
358
359 /*QUAKED target_teleporter (1 0 0) (-8 -8 -8) (8 8 8)
360 The activator will be teleported away.
361 */
SP_target_teleporter(gentity_t * self)362 void SP_target_teleporter( gentity_t *self ) {
363 if (!self->targetname)
364 G_Printf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
365
366 self->use = target_teleporter_use;
367 }
368
369 //==========================================================
370
371
372 /*QUAKED target_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM
373 This doesn't perform any actions except fire its targets.
374 The activator can be forced to be from a certain team.
375 if RANDOM is checked, only one of the targets will be fired, not all of them
376 */
target_relay_use(gentity_t * self,gentity_t * other,gentity_t * activator)377 void target_relay_use (gentity_t *self, gentity_t *other, gentity_t *activator) {
378 if ( ( self->spawnflags & 1 ) && activator->client
379 && activator->client->sess.sessionTeam != TEAM_RED ) {
380 return;
381 }
382 if ( ( self->spawnflags & 2 ) && activator->client
383 && activator->client->sess.sessionTeam != TEAM_BLUE ) {
384 return;
385 }
386 if ( self->spawnflags & 4 ) {
387 gentity_t *ent;
388
389 ent = G_PickTarget( self->target );
390 if ( ent && ent->use ) {
391 ent->use( ent, self, activator );
392 }
393 return;
394 }
395 G_UseTargets (self, activator);
396 }
397
SP_target_relay(gentity_t * self)398 void SP_target_relay (gentity_t *self) {
399 self->use = target_relay_use;
400 }
401
402
403 //==========================================================
404
405 /*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8)
406 Kills the activator.
407 */
target_kill_use(gentity_t * self,gentity_t * other,gentity_t * activator)408 void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
409 G_Damage ( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
410 }
411
SP_target_kill(gentity_t * self)412 void SP_target_kill( gentity_t *self ) {
413 self->use = target_kill_use;
414 }
415
416 /*QUAKED target_position (0 0.5 0) (-4 -4 -4) (4 4 4)
417 Used as a positional target for in-game calculation, like jumppad targets.
418 */
SP_target_position(gentity_t * self)419 void SP_target_position( gentity_t *self ){
420 G_SetOrigin( self, self->s.origin );
421 }
422
target_location_linkup(gentity_t * ent)423 static void target_location_linkup(gentity_t *ent)
424 {
425 int i;
426 int n;
427
428 if (level.locationLinked)
429 return;
430
431 level.locationLinked = qtrue;
432
433 level.locationHead = NULL;
434
435 trap_SetConfigstring( CS_LOCATIONS, "unknown" );
436
437 for (i = 0, ent = g_entities, n = 1;
438 i < level.num_entities;
439 i++, ent++) {
440 if (ent->classname && !Q_stricmp(ent->classname, "target_location")) {
441 // lets overload some variables!
442 ent->health = n; // use for location marking
443 trap_SetConfigstring( CS_LOCATIONS + n, ent->message );
444 n++;
445 ent->nextTrain = level.locationHead;
446 level.locationHead = ent;
447 }
448 }
449
450 // All linked together now
451 }
452
453 /*QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8)
454 Set "message" to the name of this location.
455 Set "count" to 0-7 for color.
456 0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white
457
458 Closest target_location in sight used for the location, if none
459 in site, closest in distance
460 */
SP_target_location(gentity_t * self)461 void SP_target_location( gentity_t *self ){
462 self->think = target_location_linkup;
463 self->nextthink = level.time + 200; // Let them all spawn first
464
465 G_SetOrigin( self, self->s.origin );
466 }
467
468