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