1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player 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 single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP 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 SP 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 SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP 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 SP 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 	SP_Tramcar
31 */
32 #include "g_local.h"
33 
34 // defines
35 #define TRAMCAR_START_ON        1
36 #define TRAMCAR_TOGGLE          2
37 #define TRAMCAR_BLOCK_STOPS     4
38 #define TRAMCAR_LEADER          8
39 
40 void props_me109_think( gentity_t *ent );
41 void ExplodePlaneSndFx( gentity_t *self );
42 
43 void Think_SetupAirplaneWaypoints( gentity_t *ent );
44 void truck_cam_think( gentity_t *ent );
45 
46 // extern calls
47 extern void Think_SetupTrainTargets( gentity_t *ent );
48 extern void Reached_BinaryMover( gentity_t *ent );
49 extern void MatchTeam( gentity_t *teamLeader, int moverState, int time );
50 extern void SetMoverState( gentity_t *ent, moverState_t moverState, int time );
51 extern void Blocked_Door( gentity_t *ent, gentity_t *other );
52 extern void Think_BeginMoving( gentity_t *ent );
53 extern void propExplosionLarge( gentity_t *ent );
54 ////////////////////////
55 // truck states
56 ////////////////////////
57 typedef enum
58 {
59 	truck_nosound = 0,
60 	truck_idle,
61 	truck_gear1,
62 	truck_gear2,
63 	truck_gear3,
64 	truck_reverse,
65 	truck_moving,
66 	truck_breaking,
67 	truck_bouncy1,
68 	truck_bouncy2,
69 	truck_bouncy3
70 } truck_states1;
71 
72 /////////////////////////
73 // truck sounds
74 /////////////////////////
75 
76 int truck_idle_snd;
77 int truck_gear1_snd;
78 int truck_gear2_snd;
79 int truck_gear3_snd;
80 int truck_reverse_snd;
81 int truck_moving_snd;
82 int truck_breaking_snd;
83 int truck_bouncy1_snd;
84 int truck_bouncy2_snd;
85 int truck_bouncy3_snd;
86 int truck_sound;
87 
88 
89 ////////////////////////
90 // plane states
91 ////////////////////////
92 typedef enum
93 {
94 	plane_nosound = 0,
95 	plane_idle,
96 	plane_flyby1,
97 	plane_flyby2,
98 	plane_loop,
99 	plane_choke,
100 	plane_startup
101 } truck_states2;
102 
103 ///////////////////////////
104 // aircraft sounds
105 ///////////////////////////
106 
107 int fploop_snd;
108 int fpchoke_snd;
109 int fpattack_snd;
110 int fpexpdebris_snd;
111 
112 int fpflyby1_snd;
113 int fpflyby2_snd;
114 int fpidle_snd;
115 int fpstartup_snd;
116 
117 ///////////////////////////
118 // airplane parts
119 ///////////////////////////
120 int fuse_part;
121 int wing_part;
122 int tail_part;
123 int nose_part;
124 int crash_part;
125 
126 // functions to be added
InitTramcar(gentity_t * ent)127 void InitTramcar( gentity_t *ent ) {
128 	vec3_t move;
129 	float distance;
130 	float light;
131 	vec3_t color;
132 	qboolean lightSet, colorSet;
133 	char        *sound;
134 
135 	// This is here just for show
136 	// if the "model2" key is set, use a seperate model
137 	// for drawing, but clip against the brushes
138 	if ( ent->model2 ) {
139 		ent->s.modelindex2 = G_ModelIndex( ent->model2 );
140 	}
141 
142 	if ( !Q_stricmp( ent->classname, "props_me109" ) ) {
143 		ent->s.modelindex2 = G_ModelIndex( "models/mapobjects/vehicles/m109s.md3" );
144 	}
145 
146 	if ( !Q_stricmp( ent->classname, "truck_cam" ) ) {
147 		ent->s.modelindex2 = G_ModelIndex( "models/mapobjects/vehicles/truck_base.md3" );
148 	}
149 
150 	// if the "loopsound" key is set, use a constant looping sound when moving
151 	if ( G_SpawnString( "noise", "100", &sound ) ) {
152 		ent->s.loopSound = G_SoundIndex( sound );
153 	}
154 
155 	// if the "color" or "light" keys are set, setup constantLight
156 	lightSet = G_SpawnFloat( "light", "100", &light );
157 	colorSet = G_SpawnVector( "color", "1 1 1", color );
158 	if ( lightSet || colorSet ) {
159 		int r, g, b, i;
160 
161 		r = color[0] * 255;
162 		if ( r > 255 ) {
163 			r = 255;
164 		}
165 		g = color[1] * 255;
166 		if ( g > 255 ) {
167 			g = 255;
168 		}
169 		b = color[2] * 255;
170 		if ( b > 255 ) {
171 			b = 255;
172 		}
173 		i = light / 4;
174 		if ( i > 255 ) {
175 			i = 255;
176 		}
177 		ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 );
178 	}
179 
180 	ent->use = Use_BinaryMover;
181 //	ent->reached = Reached_BinaryMover;
182 
183 	ent->moverState = MOVER_POS1;
184 	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
185 	ent->s.eType = ET_MOVER;
186 
187 	VectorCopy( ent->pos1, ent->r.currentOrigin );
188 
189 	trap_LinkEntity( ent );
190 
191 	ent->s.pos.trType = TR_STATIONARY;
192 	VectorCopy( ent->pos1, ent->s.pos.trBase );
193 
194 	// calculate time to reach second position from speed
195 	VectorSubtract( ent->pos2, ent->pos1, move );
196 	distance = VectorLength( move );
197 	if ( !ent->speed ) {
198 		ent->speed = 100;
199 	}
200 	VectorScale( move, ent->speed, ent->s.pos.trDelta );
201 	ent->s.pos.trDuration = distance * 1000 / ent->speed;
202 	if ( ent->s.pos.trDuration <= 0 ) {
203 		ent->s.pos.trDuration = 1;
204 	}
205 }
206 
Calc_Roll(gentity_t * ent)207 void Calc_Roll( gentity_t *ent ) {
208 	gentity_t *target;
209 	vec3_t vec;
210 	vec3_t forward;
211 	vec3_t right;
212 	float dot2;
213 	vec3_t tang;
214 
215 	target = ent->nextTrain;
216 
217 	VectorCopy( ent->r.currentAngles, tang );
218 	tang[ROLL] = 0;
219 
220 	AngleVectors( tang, forward, right, NULL );
221 	VectorSubtract( target->nextTrain->nextTrain->s.origin, ent->r.currentOrigin, vec );
222 	VectorNormalize( vec );
223 
224 	dot2 = DotProduct( vec, right );
225 
226 	ent->angle = (int) ent->angle;
227 
228 	if ( dot2 > 0 ) {
229 		if ( ent->s.apos.trBase[ROLL] < -( ent->angle * 2 ) ) {
230 			ent->s.apos.trBase[ROLL] += 2;
231 		} else if ( ent->s.apos.trBase[ROLL] > -( ent->angle * 2 ) ) {
232 			ent->s.apos.trBase[ROLL] -= 2;
233 		}
234 
235 		if ( ent->s.apos.trBase[ROLL] > 90 ) {
236 			ent->s.apos.trBase[ROLL] = 90;
237 		}
238 	} else if ( dot2 < 0 )     {
239 		if ( ent->s.apos.trBase[ROLL] > -( ent->angle * 2 ) ) {
240 			ent->s.apos.trBase[ROLL] -= 2;
241 		} else if ( ent->s.apos.trBase[ROLL] < -( ent->angle * 2 ) ) {
242 			ent->s.apos.trBase[ROLL] += 2;
243 		}
244 
245 		if ( ent->s.apos.trBase[ROLL] < -90 ) {
246 			ent->s.apos.trBase[ROLL] = -90;
247 		}
248 	} else {
249 		ent->s.apos.trBase[ROLL] = 0;
250 	}
251 
252 	trap_LinkEntity( ent );
253 
254 	ent->nextthink = level.time + 50;
255 }
256 
257 
258 #define MAXCHOICES  8
259 
GetNextTrack(gentity_t * ent)260 void GetNextTrack( gentity_t *ent ) {
261 	gentity_t   *track = NULL;
262 	gentity_t   *next;
263 	gentity_t   *choice[MAXCHOICES];
264 	int num_choices = 0;
265 	int rval;
266 
267 	next = ent->nextTrain;
268 
269 	if ( !( next->track ) ) {
270 		G_Printf( "NULL track name for %s on %s\n", ent->classname, next->targetname );
271 		return;
272 	}
273 
274 	while ( 1 )
275 	{
276 		track = G_Find( track, FOFS( targetname ), next->track );
277 
278 		if ( !track ) {
279 			break;
280 		}
281 
282 		choice[num_choices++] = track;
283 
284 		if ( num_choices == MAXCHOICES ) {
285 			break;
286 		}
287 	}
288 
289 	if ( !num_choices ) {
290 		G_Printf( "GetNextTrack didn't find a track\n" );
291 		return;
292 	}
293 
294 	rval = rand() % num_choices;
295 
296 	ent->nextTrain = NULL;
297 	ent->target = choice[rval]->targetname;
298 
299 }
300 
Reached_Tramcar(gentity_t * ent)301 void Reached_Tramcar( gentity_t *ent ) {
302 	gentity_t       *next;
303 	float speed;
304 	vec3_t move;
305 	float length;
306 
307 	// copy the apropriate values
308 	next = ent->nextTrain;
309 	if ( !next || !next->nextTrain ) {
310 		return;     // just stop
311 	}
312 
313 	// Rafael
314 	if ( next->wait == -1 && next->count ) {
315 		// G_Printf ("stoped wait = -1 count %i\n",next->count);
316 		return;
317 	}
318 
319 	if ( !Q_stricmp( ent->classname, "props_me109" ) ) {
320 		vec3_t vec, angles;
321 		float diff;
322 
323 		if ( next->spawnflags & 8 ) { // laps
324 			next->count--;
325 
326 			if ( !next->count ) {
327 				next->count = next->count2;
328 
329 				GetNextTrack( ent );
330 				Think_SetupAirplaneWaypoints( ent );
331 
332 				next = ent->nextTrain;
333 
334 				G_Printf( "changed track to %s\n", next->targetname );
335 			} else {
336 				G_Printf( "%s lap %i\n", next->targetname, next->count );
337 			}
338 		} else if ( ( next->spawnflags & 1 ) && !( next->count ) && ent->health > 0 )         { // SCRIPT flag
339 			GetNextTrack( ent );
340 			Think_SetupAirplaneWaypoints( ent );
341 		} else if ( ( next->spawnflags & 2 ) && ( ent->spawnflags & 8 ) && ent->health <= 0 && ent->takedamage )         { // death path
342 			ent->takedamage = qfalse;
343 
344 			GetNextTrack( ent );
345 			Think_SetupAirplaneWaypoints( ent );
346 		} else if ( ( next->spawnflags & 4 ) )       { // explode the plane
347 			ExplodePlaneSndFx( ent );
348 
349 			ent->s.modelindex = crash_part;
350 			// spawn the wing at the player effect
351 
352 			ent->nextTrain = NULL;
353 			G_UseTargets( next, NULL );
354 
355 			return;
356 		}
357 
358 		VectorSubtract( ent->nextTrain->nextTrain->s.origin, ent->r.currentOrigin, vec );
359 		vectoangles( vec, angles );
360 
361 
362 		diff = AngleSubtract( ent->r.currentAngles [YAW], angles[YAW] );
363 		// diff = AngleSubtract (ent->TargetAngles [YAW], angles[YAW]);
364 
365 		ent->rotate[1] = 1;
366 		ent->angle = -diff;
367 
368 		//if (angles[YAW] == 0)
369 		//	ent->s.apos.trDuration = ent->s.pos.trDuration;
370 		//else
371 		//	ent->s.apos.trDuration = 1000;
372 
373 		{
374 			VectorCopy( next->s.origin, ent->pos1 );
375 			VectorCopy( next->nextTrain->s.origin, ent->pos2 );
376 
377 			// if the path_corner has a speed, use that
378 			if ( next->speed ) {
379 				speed = next->speed;
380 			} else {
381 				// otherwise use the train's speed
382 				speed = ent->speed;
383 			}
384 			if ( speed < 1 ) {
385 				speed = 1;
386 			}
387 
388 			// calculate duration
389 			VectorSubtract( ent->pos2, ent->pos1, move );
390 			length = VectorLength( move );
391 
392 			ent->s.apos.trDuration = length * 1000 / speed;
393 
394 //testing
395 // ent->gDuration = ent->s.apos.trDuration;
396 			ent->gDurationBack = ent->gDuration = ent->s.apos.trDuration;
397 // ent->gDeltaBack = ent->gDelta =
398 
399 		}
400 
401 		VectorClear( ent->s.apos.trDelta );
402 
403 		SetMoverState( ent, MOVER_1TO2ROTATE, level.time );
404 		VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
405 
406 		trap_LinkEntity( ent );
407 
408 		ent->think = props_me109_think;
409 		ent->nextthink = level.time + 50;
410 	} else if ( !Q_stricmp( ent->classname, "truck_cam" ) )       {
411 		G_Printf( "target: %s\n", next->targetname );
412 
413 		if ( next->spawnflags & 2 ) { // END
414 			ent->s.loopSound = 0; // stop sound
415 			ent->nextTrain = NULL;
416 			return;
417 		} else
418 		{
419 			vec3_t vec, angles;
420 			float diff;
421 
422 			if ( next->spawnflags & 4 ) { // reverse
423 				ent->props_frame_state = truck_reverse;
424 				VectorSubtract( ent->r.currentOrigin, ent->nextTrain->nextTrain->s.origin, vec );
425 			} else
426 			{
427 				ent->props_frame_state = truck_moving;
428 				VectorSubtract( ent->nextTrain->nextTrain->s.origin, ent->r.currentOrigin, vec );
429 			}
430 
431 			vectoangles( vec, angles );
432 
433 			diff = AngleSubtract( ent->r.currentAngles [YAW], angles[YAW] );
434 
435 			ent->rotate[1] = 1;
436 			ent->angle = -diff;
437 
438 			if ( angles[YAW] == 0 ) {
439 				ent->s.apos.trDuration = ent->s.pos.trDuration;
440 			} else {
441 				ent->s.apos.trDuration = 1000;
442 			}
443 
444 //testing
445 			ent->gDuration = ent->s.pos.trDuration;
446 
447 			VectorClear( ent->s.apos.trDelta );
448 
449 			SetMoverState( ent, MOVER_1TO2ROTATE, level.time );
450 			VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
451 
452 			trap_LinkEntity( ent );
453 		}
454 
455 		if ( next->wait == -1 ) {
456 			ent->props_frame_state = truck_idle;
457 		}
458 
459 		if ( next->count2 == 1 ) {
460 			ent->props_frame_state = truck_gear1;
461 		} else if ( next->count2 == 2 ) {
462 			ent->props_frame_state = truck_gear2;
463 		} else if ( next->count2 == 3 ) {
464 			ent->props_frame_state = truck_gear3;
465 		}
466 
467 		switch ( ent->props_frame_state )
468 		{
469 		case truck_idle: ent->s.loopSound = truck_idle_snd; break;
470 		case truck_gear1: ent->s.loopSound = truck_gear1_snd; break;
471 		case truck_gear2: ent->s.loopSound = truck_gear2_snd; break;
472 		case truck_gear3: ent->s.loopSound = truck_gear3_snd; break;
473 		case truck_reverse: ent->s.loopSound = truck_reverse_snd; break;
474 		case truck_moving: ent->s.loopSound = truck_moving_snd; break;
475 		case truck_breaking: ent->s.loopSound = truck_breaking_snd; break;
476 		case truck_bouncy1: ent->s.loopSound = truck_bouncy1_snd; break;
477 		case truck_bouncy2: ent->s.loopSound = truck_bouncy2_snd; break;
478 		case truck_bouncy3: ent->s.loopSound = truck_bouncy3_snd; break;
479 		}
480 
481 //testing
482 		ent->s.loopSound = truck_sound;
483 		ent->think = truck_cam_think;
484 		ent->nextthink = level.time + ( FRAMETIME / 2 );
485 
486 	} else if ( !Q_stricmp( ent->classname, "camera_cam" ) )       {
487 
488 	}
489 
490 	// fire all other targets
491 	G_UseTargets( next, NULL );
492 
493 	// set the new trajectory
494 	ent->nextTrain = next->nextTrain;
495 
496 	if ( next->wait == -1 ) {
497 		next->count = 1;
498 	}
499 
500 	VectorCopy( next->s.origin, ent->pos1 );
501 	VectorCopy( next->nextTrain->s.origin, ent->pos2 );
502 
503 	// if the path_corner has a speed, use that
504 	if ( next->speed ) {
505 		speed = next->speed;
506 	} else {
507 		// otherwise use the train's speed
508 		speed = ent->speed;
509 	}
510 	if ( speed < 1 ) {
511 		speed = 1;
512 	}
513 
514 	// calculate duration
515 	VectorSubtract( ent->pos2, ent->pos1, move );
516 	length = VectorLength( move );
517 
518 	ent->s.pos.trDuration = length * 1000 / speed;
519 
520 //testing
521 // ent->gDuration = ent->s.pos.trDuration;
522 	ent->gDurationBack = ent->gDuration = ent->s.pos.trDuration;
523 // ent->gDeltaBack = ent->gDelta = ;
524 
525 	// looping sound
526 	if ( next->soundLoop ) {
527 		ent->s.loopSound = next->soundLoop;
528 	}
529 
530 	// start it going
531 	SetMoverState( ent, MOVER_1TO2, level.time );
532 
533 	// if there is a "wait" value on the target, don't start moving yet
534 	// if ( next->wait )
535 	if ( next->wait && next->wait != -1 ) {
536 		ent->nextthink = level.time + next->wait * 1000;
537 		ent->think = Think_BeginMoving;
538 		ent->s.pos.trType = TR_STATIONARY;
539 	}
540 }
541 
542 extern void func_explosive_explode( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod );
543 
Tramcar_die(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)544 void Tramcar_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
545 	gentity_t       *slave;
546 
547 	if ( !self ) {
548 		return;
549 	}
550 
551 	func_explosive_explode( self, self, inflictor, 0, 0 );
552 
553 	// link all teammembers
554 	for ( slave = self ; slave ; slave = slave->teamchain )
555 	{
556 
557 		if ( slave == self ) {
558 			continue;
559 		}
560 
561 		// slaves need to inherit position
562 		slave->nextTrain = self->nextTrain;
563 
564 		slave->s.pos.trType = self->s.pos.trType;
565 		slave->s.pos.trTime = self->s.pos.trTime;
566 		slave->s.pos.trDuration = self->s.pos.trDuration;
567 		VectorCopy( self->s.pos.trBase, slave->s.pos.trBase );
568 		VectorCopy( self->s.pos.trDelta, slave->s.pos.trDelta );
569 
570 		slave->s.apos.trType = self->s.apos.trType;
571 		slave->s.apos.trTime = self->s.apos.trTime;
572 		slave->s.apos.trDuration = self->s.apos.trDuration;
573 		VectorCopy( self->s.apos.trBase, slave->s.apos.trBase );
574 		VectorCopy( self->s.apos.trDelta, slave->s.apos.trDelta );
575 
576 		slave->think = self->think;
577 		slave->nextthink = self->nextthink;
578 
579 		VectorCopy( self->pos1, slave->pos1 );
580 		VectorCopy( self->pos2, slave->pos2 );
581 
582 		slave->speed = self->speed;
583 
584 		slave->flags &= ~FL_TEAMSLAVE;
585 		// make it visible
586 
587 		if ( self->use ) {
588 			slave->use = self->use;
589 		}
590 
591 		trap_LinkEntity( slave );
592 	}
593 
594 	self->use = 0;
595 
596 	self->is_dead = qtrue;
597 
598 	self->takedamage = qfalse;
599 
600 	if ( self->nextTrain ) {
601 		self->nextTrain = 0;
602 	}
603 
604 	self->s.loopSound = 0;
605 
606 	VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
607 	VectorCopy( self->r.currentAngles, self->s.apos.trBase );
608 
609 	self->flags |= FL_TEAMSLAVE;
610 	trap_UnlinkEntity( self );
611 
612 }
613 
TramCarUse(gentity_t * ent,gentity_t * other,gentity_t * activator)614 void TramCarUse( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
615 	gentity_t       *next;
616 
617 	if ( level.time >= ent->s.pos.trTime + ent->s.pos.trDuration ) {
618 
619 		next = ent->nextTrain;
620 
621 		if ( next->wait == -1 && next->count ) {
622 			next->count = 0;
623 			//G_Printf ("Moving next->count %i\n", next->count);
624 		}
625 
626 		Reached_Tramcar( ent );
627 
628 	}
629 //	else
630 //		G_Printf ("no can do havent reached yet\n");
631 
632 }
633 
634 
Blocked_Tramcar(gentity_t * ent,gentity_t * other)635 void Blocked_Tramcar( gentity_t *ent, gentity_t *other ) {
636 	// remove anything other than a client
637 	if ( !other->client ) {
638 		// except CTF flags!!!!
639 		if ( other->s.eType == ET_ITEM && other->item->giType == IT_TEAM ) {
640 			Team_DroppedFlagThink( other );
641 			return;
642 		}
643 		G_TempEntity( other->s.origin, EV_ITEM_POP );
644 		G_FreeEntity( other );
645 		return;
646 	}
647 
648 	if ( other->flags & FL_GODMODE ) {
649 		other->flags &= ~FL_GODMODE;
650 		other->client->ps.stats[STAT_HEALTH] = other->health = 0;
651 	}
652 
653 	G_Damage( other, ent, ent, NULL, NULL, 99999, 0, MOD_CRUSH );
654 
655 }
656 
657 /*QUAKED func_tramcar (0 .5 .8) ? START_ON TOGGLE - LEADER
658 health value of 999 will designate the piece as non damageable
659 
660 The leader of the tramcar group must have the leader flag set or
661 you'll end up with co-planer poly heaven. When the health of the Leader
662 of the team hits zero it will unlink and the team will become visible
663 
664 A tramcar is a mover that moves between path_corner target points.
665 all tramcar parts MUST HAVE AN ORIGIN BRUSH. (this is true for all parts)
666 
667 The tramcar spawns at the first target it is pointing at.
668 
669 If you are going to have enemies ride the tramcar it must be placed in its ending
670 position when you bsp the map you can the start it by targeting the desired path_corner
671 
672 "model2"	.md3 model to also draw
673 "speed"		default 100
674 "dmg"		default	2
675 "noise"		looping sound to play when the train is in motion
676 "target"	next path corner
677 "color"		constantLight color
678 "light"		constantLight radius
679 
680 "type" type of debris ("glass", "wood", "metal", "gibs") default is "wood"
681 "mass" defaults to 75.  This determines how much debris is emitted when it explodes.  You get one large chunk per 100 of mass (up to 8) and one small chunk per 25 of mass (up to 16).  So 800 gives the most.
682 */
SP_func_tramcar(gentity_t * self)683 void SP_func_tramcar( gentity_t *self ) {
684 
685 	int mass;
686 	char    *type;
687 	char    *s;
688 	char buffer[MAX_QPATH];
689 
690 	VectorClear( self->s.angles );
691 
692 	//if (self->spawnflags & TRAMCAR_BLOCK_STOPS) {
693 	//	self->damage = 0;
694 	//	self->s.eFlags |= EF_MOVER_STOP;
695 	//}
696 	//else {
697 	if ( !self->damage ) {
698 		self->damage = 100;
699 	}
700 	//}
701 
702 	if ( !self->speed ) {
703 		self->speed = 100;
704 	}
705 
706 	if ( !self->target ) {
707 		G_Printf( "func_tramcar without a target at %s\n", vtos( self->r.absmin ) );
708 		G_FreeEntity( self );
709 		return;
710 	}
711 
712 	if ( self->spawnflags & TRAMCAR_LEADER ) {
713 		if ( !self->health ) {
714 			self->health = 50;
715 		}
716 
717 		self->takedamage = qtrue;
718 		self->die = Tramcar_die;
719 
720 		if ( self->health < 999 ) {
721 			self->isProp = qtrue;
722 		}
723 	}
724 
725 	trap_SetBrushModel( self, self->model );
726 
727 	if ( G_SpawnInt( "mass", "75", &mass ) ) {
728 		self->count = mass;
729 	} else {
730 		self->count = 75;
731 	}
732 
733 	G_SpawnString( "type", "wood", &type );
734 	if ( !Q_stricmp( type,"wood" ) ) {
735 		self->key = 0;
736 	} else if ( !Q_stricmp( type,"glass" ) )   {
737 		self->key = 1;
738 	} else if ( !Q_stricmp( type,"metal" ) )                                                            {
739 		self->key = 2;
740 	} else if ( !Q_stricmp( type,"gibs" ) )                                                                                                                     {
741 		self->key = 3;
742 	}
743 
744 	if ( G_SpawnString( "noise", "NOSOUND", &s ) ) {
745 		if ( Q_stricmp( s, "nosound" ) ) {
746 			Q_strncpyz( buffer, s, sizeof( buffer ) );
747 			self->s.dl_intensity = G_SoundIndex( buffer );
748 		}
749 	} else {
750 		switch ( self->key )
751 		{
752 		case 0:     // "wood"
753 			self->s.dl_intensity = G_SoundIndex( "sound/world/boardbreak.wav" );
754 			break;
755 		case 1:     // "glass"
756 			self->s.dl_intensity = G_SoundIndex( "sound/world/glassbreak.wav" );
757 			break;
758 		case 2:     // "metal"
759 			self->s.dl_intensity = G_SoundIndex( "sound/world/metalbreak.wav" );
760 			break;
761 		case 3:     // "gibs"
762 			self->s.dl_intensity = G_SoundIndex( "sound/player/gibsplit1.wav" );
763 			break;
764 		}
765 	}
766 
767 	self->s.density = self->count;  // pass the "mass" to the client
768 
769 	InitTramcar( self );
770 
771 	self->reached = Reached_Tramcar;
772 
773 	self->nextthink = level.time + ( FRAMETIME / 2 );
774 
775 	self->think = Think_SetupTrainTargets;
776 
777 	self->blocked = Blocked_Tramcar;
778 
779 	if ( self->spawnflags & TRAMCAR_TOGGLE ) {
780 		self->use = TramCarUse;
781 	}
782 
783 }
784 
785 
786 ////////////////////////////
787 // me109
788 ////////////////////////////
789 
plane_AIScript_AlertEntity(gentity_t * ent)790 void plane_AIScript_AlertEntity( gentity_t *ent ) {
791 
792 	// when count reaches 0, the marker is active
793 	ent->count--;
794 
795 	if ( ent->count <= 0 ) {
796 		ent->count = 0;
797 	}
798 }
799 
800 /*QUAKED plane_waypoint (.5 .3 0) (-8 -8 -8) (8 8 8) SCRIPTS DIE EXPLODE LAPS ATTACK
801 "count" number of times this waypoint needs to be triggered by an AIScript "alertentity" call before the aircraft changes tracks
802 "track"	tells it what track to branch off to there can be several track with the same track name
803 the program will pick one randomly there can be a maximum of eight tracks at any branch
804 
805 the entity will fire its target when reached
806 */
SP_plane_waypoint(gentity_t * self)807 void SP_plane_waypoint( gentity_t *self ) {
808 
809 	if ( !self->targetname ) {
810 		G_Printf( "plane_waypoint with no targetname at %s\n", vtos( self->s.origin ) );
811 		G_FreeEntity( self );
812 		return;
813 	}
814 
815 	if ( self->spawnflags & 1 ) {
816 		self->AIScript_AlertEntity = plane_AIScript_AlertEntity;
817 	}
818 
819 	if ( self->count ) {
820 		self->count2 = self->count;
821 	}
822 
823 	if ( self->wait == -1 ) {
824 		self->count = 1;
825 	}
826 }
827 
828 
829 /*QUAKED props_me109 (.7 .3 .1) (-128 -128 0) (128 128 64) START_ON TOGGLE SPINNINGPROP FIXED_DIE
830 default health = 1000
831 */
832 
ExplodePlaneSndFx(gentity_t * self)833 void ExplodePlaneSndFx( gentity_t *self ) {
834 	gentity_t   *temp;
835 	vec3_t dir;
836 	gentity_t   *part;
837 	int i;
838 	vec3_t start;
839 
840 	temp = G_Spawn();
841 
842 	if ( !temp ) {
843 		return;
844 	}
845 
846 	G_SetOrigin( temp, self->melee->s.pos.trBase );
847 	G_AddEvent( temp, EV_GLOBAL_SOUND, fpexpdebris_snd );
848 	temp->think = G_FreeEntity;
849 	temp->nextthink = level.time + 10000;
850 	trap_LinkEntity( temp );
851 
852 	// added this because plane may be parked on runway
853 	// we may want to add some exotic deaths to parked aircraft
854 	if ( self->nextTrain && self->nextTrain->spawnflags & 4 ) { // explode the plane
855 		// spawn the wing at the player
856 		gentity_t   *player;
857 		vec3_t vec, ang;
858 
859 		player = AICast_FindEntityForName( "player" );
860 
861 		if ( !player ) {
862 			return;
863 		}
864 
865 		VectorSubtract( player->s.origin, self->r.currentOrigin, vec );
866 		vectoangles( vec, ang );
867 		AngleVectors( ang, dir, NULL, NULL );
868 
869 		dir[2] = 1;
870 
871 		VectorCopy( self->r.currentOrigin, start );
872 
873 		part = fire_flamebarrel( temp, start, dir );
874 
875 		if ( !part ) {
876 			G_Printf( "ExplodePlaneSndFx Failed to spawn part\n" );
877 			return;
878 		}
879 
880 		part->s.eType = ET_FP_PARTS;
881 
882 		part->s.modelindex = wing_part;
883 
884 		return;
885 	}
886 
887 	AngleVectors( self->r.currentAngles, dir, NULL, NULL );
888 
889 	for ( i = 0; i < 4; i++ )
890 	{
891 		VectorCopy( self->r.currentOrigin, start );
892 
893 		start[0] += crandom() * 64;
894 		start[1] += crandom() * 64;
895 		start[2] += crandom() * 32;
896 
897 		part = fire_flamebarrel( temp, start, dir );
898 
899 		if ( !part ) {
900 			continue;
901 		}
902 
903 		part->s.eType = ET_FP_PARTS;
904 
905 		if ( i == 0 ) {
906 			part->s.modelindex = fuse_part;
907 		} else if ( i == 1 ) {
908 			part->s.modelindex = wing_part;
909 		} else if ( i == 2 ) {
910 			part->s.modelindex = tail_part;
911 		} else {
912 			part->s.modelindex = nose_part;
913 		}
914 	}
915 }
916 
props_me109_die(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)917 void props_me109_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
918 	G_Printf( "dead\n" );
919 
920 	VectorClear( self->rotate );
921 	VectorSet( self->rotate, 0, 1, 0 ); //sigh
922 
923 	if ( self->spawnflags & 8 ) { // FIXED_DIE
924 		return;
925 	}
926 
927 	propExplosionLarge( self );
928 	self->melee->s.loopSound = self->melee->noise_index = 0;
929 	ExplodePlaneSndFx( self );
930 	G_FreeEntity( self );
931 }
932 
props_me109_pain(gentity_t * self,gentity_t * attacker,int damage,vec3_t point)933 void props_me109_pain( gentity_t *self, gentity_t *attacker, int damage, vec3_t point ) {
934 	vec3_t temp;
935 
936 	G_Printf( "pain: health = %i\n", self->health );
937 
938 	VectorCopy( self->r.currentOrigin, temp );
939 	VectorCopy( self->pos3, self->r.currentOrigin );
940 	Spawn_Shard( self, NULL, 6, 999 );
941 	VectorCopy( temp, self->r.currentOrigin );
942 
943 	VectorClear( self->rotate );
944 	VectorSet( self->rotate, 0, 1, 0 ); //sigh
945 }
946 
Plane_Fire_Lead(gentity_t * self)947 void Plane_Fire_Lead( gentity_t *self ) {
948 	vec3_t dir, right;
949 	vec3_t pos1, pos2;
950 	vec3_t position;
951 
952 	AngleVectors( self->r.currentAngles, dir, right, NULL );
953 	VectorCopy( self->r.currentOrigin, position );
954 	VectorMA( position, 64, right, pos1 );
955 	VectorMA( position, -64, right, pos2 );
956 
957 	fire_lead( self, pos1, dir, 12 );
958 	fire_lead( self, pos2, dir, 12 );
959 }
960 
Plane_Attack(gentity_t * self,qboolean in_PVS)961 void Plane_Attack( gentity_t *self, qboolean in_PVS ) {
962 	if ( self->nextTrain->spawnflags & 16 ) {
963 		self->count++;
964 
965 		if ( self->count == 3 ) {
966 			self->s.density = 8, self->count = 0;
967 
968 			if ( in_PVS ) {
969 				G_AddEvent( self, EV_GLOBAL_SOUND, fpattack_snd );
970 			} else {
971 				G_AddEvent( self, EV_GENERAL_SOUND, fpattack_snd );
972 			}
973 
974 			Plane_Fire_Lead( self );
975 		} else {
976 			self->s.density = 7;
977 		}
978 	} else if ( self->spawnflags & 4 )     { // spinning prop
979 		self->s.density = 7;
980 	} else {
981 		self->s.density = 0;
982 	}
983 }
984 
props_me109_think(gentity_t * self)985 void props_me109_think( gentity_t *self ) {
986 
987 	qboolean in_PVS = qfalse;
988 
989 	{
990 		gentity_t *player;
991 
992 		player = AICast_FindEntityForName( "player" );
993 
994 		if ( player ) {
995 			in_PVS = trap_InPVS( player->r.currentOrigin, self->s.pos.trBase );
996 
997 			if ( in_PVS ) {
998 				self->melee->s.eType = ET_GENERAL;
999 
1000 				{
1001 					float len;
1002 					vec3_t vec;
1003 					vec3_t forward;
1004 					vec3_t dir;
1005 					vec3_t point;
1006 
1007 					VectorCopy( player->r.currentOrigin, point );
1008 					VectorSubtract( player->r.currentOrigin, self->r.currentOrigin, vec );
1009 					len = VectorLength( vec );
1010 					vectoangles( vec, dir );
1011 					AngleVectors( dir, forward, NULL, NULL );
1012 					VectorMA( point, len * 0.1, forward, point );
1013 
1014 					G_SetOrigin( self->melee, point );
1015 				}
1016 			} else
1017 			{
1018 				self->melee->s.eType = ET_GENERAL;
1019 			}
1020 
1021 			trap_LinkEntity( self->melee );
1022 		}
1023 	}
1024 
1025 	Plane_Attack( self, in_PVS );
1026 
1027 	Calc_Roll( self );
1028 
1029 	if ( self->health < 250 ) {
1030 		gentity_t *tent;
1031 		vec3_t point;
1032 
1033 		VectorCopy( self->r.currentOrigin, point );
1034 		tent = G_TempEntity( point, EV_SMOKE );
1035 		VectorCopy( point, tent->s.origin );
1036 		tent->s.time = 2000;
1037 		tent->s.time2 = 1000;
1038 		tent->s.density = 4;
1039 		tent->s.angles2[0] = 16;
1040 		tent->s.angles2[1] = 48;
1041 		tent->s.angles2[2] = 10;
1042 
1043 		self->props_frame_state = plane_choke;
1044 		self->health--;
1045 	}
1046 
1047 	if ( self->health > 0 ) {
1048 		self->nextthink = level.time + 50;
1049 
1050 		if ( self->props_frame_state == plane_choke ) {
1051 			self->melee->s.loopSound = self->melee->noise_index = fpchoke_snd;
1052 		} else if ( self->props_frame_state == plane_startup )     {
1053 			self->melee->s.loopSound = self->melee->noise_index = fpstartup_snd;
1054 		} else if ( self->props_frame_state == plane_idle )     {
1055 			self->melee->s.loopSound = self->melee->noise_index = fpidle_snd;
1056 		} else if ( self->props_frame_state == plane_flyby1 )     {
1057 			self->melee->s.loopSound = self->melee->noise_index = fpflyby1_snd;
1058 		} else if ( self->props_frame_state == plane_flyby2 )     {
1059 			self->melee->s.loopSound = self->melee->noise_index = fpflyby2_snd;
1060 		}
1061 	} else
1062 	{
1063 		propExplosionLarge( self );
1064 		self->melee->s.loopSound = self->melee->noise_index = 0;
1065 
1066 		ExplodePlaneSndFx( self );
1067 		G_FreeEntity( self->melee );
1068 		G_FreeEntity( self );
1069 
1070 
1071 	}
1072 
1073 }
1074 
Think_SetupAirplaneWaypoints(gentity_t * ent)1075 void Think_SetupAirplaneWaypoints( gentity_t *ent ) {
1076 	gentity_t       *path, *next, *start;
1077 
1078 	ent->nextTrain = G_Find( NULL, FOFS( targetname ), ent->target );
1079 	if ( !ent->nextTrain ) {
1080 		G_Printf( "plane at %s with an unfound target\n",
1081 				  vtos( ent->r.absmin ) );
1082 		return;
1083 	}
1084 
1085 	start = NULL;
1086 	for ( path = ent->nextTrain ; path != start ; path = next ) {
1087 		if ( !start ) {
1088 			start = path;
1089 		}
1090 
1091 		if ( !path->target ) {
1092 			G_Printf( "plane at %s without a target\n",
1093 					  vtos( path->s.origin ) );
1094 			return;
1095 		}
1096 
1097 		// find a path_corner among the targets
1098 		// there may also be other targets that get fired when the corner
1099 		// is reached
1100 		next = NULL;
1101 		do {
1102 			next = G_Find( next, FOFS( targetname ), path->target );
1103 			if ( !next ) {
1104 				G_Printf( "plane at %s without a target path_corner\n",
1105 						  vtos( path->s.origin ) );
1106 				return;
1107 			}
1108 		} while ( strcmp( next->classname, "plane_waypoint" ) );
1109 
1110 		path->nextTrain = next;
1111 	}
1112 
1113 	if ( ent->spawnflags & 2 ) { // Toggle
1114 		VectorCopy( ent->nextTrain->s.origin, ent->s.pos.trBase );
1115 		VectorCopy( ent->nextTrain->s.origin, ent->r.currentOrigin );
1116 		trap_LinkEntity( ent );
1117 	} else {
1118 		Reached_Tramcar( ent );
1119 	}
1120 }
1121 
1122 
PlaneUse(gentity_t * ent,gentity_t * other,gentity_t * activator)1123 void PlaneUse( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
1124 	gentity_t       *next;
1125 
1126 	if ( level.time >= ent->s.pos.trTime + ent->s.pos.trDuration ) {
1127 
1128 		next = ent->nextTrain;
1129 
1130 		if ( next->wait == -1 && next->count ) {
1131 			next->count = 0;
1132 			//G_Printf ("Moving next->count %i\n", next->count);
1133 		}
1134 
1135 		Reached_Tramcar( ent );
1136 
1137 	}
1138 //	else
1139 //		G_Printf ("no can do havent reached yet\n");
1140 
1141 }
1142 
1143 
InitPlaneSpeaker(gentity_t * ent)1144 void InitPlaneSpeaker( gentity_t *ent ) {
1145 	gentity_t   *snd;
1146 
1147 	snd = G_Spawn();
1148 
1149 	snd->noise_index = fploop_snd;
1150 
1151 	snd->s.eType = ET_SPEAKER;
1152 	snd->s.eventParm = snd->noise_index;
1153 	snd->s.frame = 0;
1154 	snd->s.clientNum = 0;
1155 
1156 	snd->s.loopSound = snd->noise_index;
1157 
1158 	snd->r.svFlags |= SVF_BROADCAST;
1159 
1160 	VectorCopy( ent->s.origin, snd->s.pos.trBase );
1161 
1162 	ent->melee = snd;
1163 
1164 	trap_LinkEntity( snd );
1165 
1166 }
1167 
SP_props_me109(gentity_t * ent)1168 void SP_props_me109( gentity_t *ent ) {
1169 
1170 	VectorSet( ent->r.mins, -128, -128, -128 );
1171 	VectorSet( ent->r.maxs, 128, 128, 128 );
1172 
1173 	ent->clipmask   = CONTENTS_SOLID;
1174 	ent->r.contents = CONTENTS_SOLID;
1175 	ent->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
1176 	ent->s.eType = ET_MOVER;
1177 
1178 	ent->isProp = qtrue;
1179 
1180 	ent->s.modelindex = G_ModelIndex( "models/mapobjects/vehicles/m109.md3" );
1181 
1182 	if ( !ent->health ) {
1183 		ent->health = 500;
1184 	}
1185 
1186 	ent->takedamage = qtrue;
1187 
1188 	ent->die = props_me109_die;
1189 	ent->pain = props_me109_pain;
1190 
1191 	ent->reached = Reached_Tramcar;
1192 
1193 	ent->nextthink = level.time + ( FRAMETIME / 2 );
1194 
1195 	ent->think = Think_SetupAirplaneWaypoints;
1196 
1197 	ent->use = PlaneUse;
1198 
1199 	if ( !( ent->speed ) ) {
1200 		ent->speed = 1000;
1201 	}
1202 
1203 	G_SetOrigin( ent, ent->s.origin );
1204 	G_SetAngle( ent, ent->s.angles );
1205 
1206 	if ( ent->spawnflags & 4 ) {
1207 		ent->s.density = 7;
1208 	}
1209 
1210 	trap_LinkEntity( ent );
1211 
1212 	fploop_snd = G_SoundIndex( "sound/fighterplane/fploop.wav" );
1213 	fpchoke_snd = G_SoundIndex( "sound/fighterplane/fpchoke.wav" );
1214 	fpattack_snd = G_SoundIndex( "sound/weapons/mg42/37mm.wav" );
1215 	fpexpdebris_snd = G_SoundIndex( "sound/fighterplane/fpexpdebris.wav" );
1216 
1217 
1218 	fpflyby1_snd = G_SoundIndex( "sound/fighterplane/fpflyby1.wav" );
1219 	fpflyby2_snd = G_SoundIndex( "sound/fighterplane/fpflyby2.wav" );
1220 	fpidle_snd = G_SoundIndex( "sound/fighterplane/fpidle.wav" );
1221 	fpstartup_snd = G_SoundIndex( "sound/fighterplane/fpstartup.wav" );
1222 
1223 
1224 	fuse_part = G_ModelIndex( "models/mapobjects/vehicles/m109debris_a.md3" );
1225 	wing_part = G_ModelIndex( "models/mapobjects/vehicles/m109debris_b.md3" );
1226 	tail_part = G_ModelIndex( "models/mapobjects/vehicles/m109debris_c.md3" );
1227 	nose_part = G_ModelIndex( "models/mapobjects/vehicles/m109debris_d.md3" );
1228 
1229 	crash_part = G_ModelIndex( "models/mapobjects/vehicles/m109crash.md3" );
1230 
1231 	InitPlaneSpeaker( ent );
1232 
1233 }
1234 
1235 /////////////////////////
1236 // TRUCK DRIVE
1237 /////////////////////////
1238 
1239 /*QUAKED truck_cam (.7 .3 .1) ? START_ON TOGGLE - -
1240 */
truck_cam_touch(gentity_t * self,gentity_t * other,trace_t * trace)1241 void truck_cam_touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
1242 	gentity_t *player;
1243 
1244 	player = AICast_FindEntityForName( "player" );
1245 
1246 	if ( player && player != other ) {
1247 		// G_Printf ("other: %s\n", other->aiName);
1248 		return;
1249 	}
1250 
1251 	if ( !self->nextTrain ) {
1252 		self->touch = 0;
1253 		return;
1254 	}
1255 
1256 	// lock the player to the moving truck
1257 	{
1258 		vec3_t point;
1259 
1260 		trap_UnlinkEntity( other );
1261 
1262 		// VectorCopy ( self->r.currentOrigin, other->client->ps.origin );
1263 		VectorCopy( self->r.currentOrigin, point );
1264 		point[2] = other->client->ps.origin[2];
1265 		VectorCopy( point, other->client->ps.origin );
1266 
1267 		// save results of pmove
1268 		BG_PlayerStateToEntityState( &other->client->ps, &other->s, qtrue );
1269 
1270 		// use the precise origin for linking
1271 		VectorCopy( other->client->ps.origin, other->r.currentOrigin );
1272 
1273 		other->client->ps.persistant[PERS_HWEAPON_USE] = 1;
1274 
1275 		trap_LinkEntity( other );
1276 	}
1277 
1278 }
1279 
truck_cam_think(gentity_t * ent)1280 void truck_cam_think( gentity_t *ent ) {
1281 	ent->nextthink = level.time + ( FRAMETIME / 2 );
1282 }
1283 
SP_truck_cam(gentity_t * self)1284 void SP_truck_cam( gentity_t *self ) {
1285 	int mass;
1286 
1287 	VectorClear( self->s.angles );
1288 
1289 	if ( !self->speed ) {
1290 		self->speed = 100;
1291 	}
1292 
1293 	if ( !self->target ) {
1294 		G_Printf( "truck_cam without a target at %s\n", vtos( self->r.absmin ) );
1295 		G_FreeEntity( self );
1296 		return;
1297 	}
1298 
1299 	trap_SetBrushModel( self, self->model );
1300 
1301 	if ( G_SpawnInt( "mass", "20", &mass ) ) {
1302 		self->count = mass;
1303 	} else {
1304 		self->count = 20;
1305 	}
1306 
1307 	InitTramcar( self );
1308 
1309 	self->nextthink = level.time + ( FRAMETIME / 2 );
1310 
1311 	self->think = Think_SetupTrainTargets;
1312 
1313 	self->touch = truck_cam_touch;
1314 
1315 	self->s.loopSound = 0;
1316 	self->props_frame_state = 0;
1317 
1318 	self->clipmask = CONTENTS_SOLID;
1319 
1320 	// G_SetOrigin (self, self->s.origin);
1321 	// G_SetAngle (self, self->s.angles);
1322 
1323 	self->reached = Reached_Tramcar;
1324 
1325 	self->s.density = 6;
1326 
1327 	//start_drive_grind_gears
1328 	truck_sound = G_SoundIndex( "sound/vehicles/start_drive_grind_gears_01_11k.wav" );
1329 	// truck_sound = G_SoundIndex ( "sound/vehicles/tankmove1.wav" );
1330 
1331 	truck_idle_snd = G_SoundIndex( "sound/vehicles/truckidle.wav" );
1332 	truck_gear1_snd = G_SoundIndex( "sound/vehicles/truckgear1.wav" );
1333 	truck_gear2_snd = G_SoundIndex( "sound/vehicles/truckgear2.wav" );
1334 	truck_gear3_snd = G_SoundIndex( "sound/vehicles/truckgear3.wav" );
1335 	truck_reverse_snd = G_SoundIndex( "sound/vehicles/truckreverse.wav" );
1336 	truck_moving_snd = G_SoundIndex( "sound/vehicles/truckmoving.wav" );
1337 	truck_breaking_snd = G_SoundIndex( "sound/vehicles/truckbreaking.wav" );
1338 	truck_bouncy1_snd = G_SoundIndex( "sound/vehicles/truckbouncy1.wav" );
1339 	truck_bouncy2_snd = G_SoundIndex( "sound/vehicles/truckbouncy2.wav" );
1340 	truck_bouncy3_snd = G_SoundIndex( "sound/vehicles/truckbouncy3.wav" );
1341 }
1342 
1343 /////////////////////////
1344 // camera cam
1345 /////////////////////////
1346 
1347 /*QUAKED camera_cam (.5 .7 .3) (-8 -8 -8) (8 8 8) ON TRACKING MOVING -
1348 "track" is the targetname of the entity providing the starting direction use an info_notnull
1349 */
1350 
delayOnthink(gentity_t * ent)1351 void delayOnthink( gentity_t *ent ) {
1352 	if ( ent->melee ) {
1353 		ent->melee->use( ent->melee, NULL, NULL );
1354 	}
1355 }
1356 
Init_Camera(gentity_t * ent)1357 void Init_Camera( gentity_t *ent ) {
1358 	vec3_t move;
1359 	float distance;
1360 
1361 	ent->moverState = MOVER_POS1;
1362 	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1363 	ent->s.eType = ET_MOVER;
1364 
1365 	VectorCopy( ent->pos1, ent->r.currentOrigin );
1366 
1367 	trap_LinkEntity( ent );
1368 
1369 	ent->s.pos.trType = TR_STATIONARY;
1370 	VectorCopy( ent->pos1, ent->s.pos.trBase );
1371 
1372 	// calculate time to reach second position from speed
1373 	VectorSubtract( ent->pos2, ent->pos1, move );
1374 	distance = VectorLength( move );
1375 	if ( !ent->speed ) {
1376 		ent->speed = 100;
1377 	}
1378 	VectorScale( move, ent->speed, ent->s.pos.trDelta );
1379 	ent->s.pos.trDuration = distance * 1000 / ent->speed;
1380 	if ( ent->s.pos.trDuration <= 0 ) {
1381 		ent->s.pos.trDuration = 1;
1382 	}
1383 }
1384 
camera_cam_think(gentity_t * ent)1385 void camera_cam_think( gentity_t *ent ) {
1386 	gentity_t *player;
1387 
1388 	player = AICast_FindEntityForName( "player" );
1389 
1390 	if ( !player ) {
1391 		return;
1392 	}
1393 
1394 	if ( ent->spawnflags & 2 ) { // tracking
1395 		vec3_t point;
1396 
1397 		trap_UnlinkEntity( player );
1398 
1399 		// VectorCopy ( self->r.currentOrigin, other->client->ps.origin );
1400 		VectorCopy( ent->r.currentOrigin, point );
1401 		point[2] = player->client->ps.origin[2];
1402 		VectorCopy( point, player->client->ps.origin );
1403 
1404 		// save results of pmove
1405 		BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
1406 
1407 		// use the precise origin for linking
1408 		VectorCopy( player->client->ps.origin, player->r.currentOrigin );
1409 
1410 		// tracking
1411 		{
1412 			gentity_t   *target = NULL;
1413 			vec3_t dang;
1414 			vec3_t vec;
1415 
1416 			if ( ent->track ) {
1417 				target = G_Find( NULL, FOFS( targetname ), ent->track );
1418 			}
1419 
1420 			if ( target ) {
1421 				VectorSubtract( target->r.currentOrigin, ent->r.currentOrigin, vec );
1422 				vectoangles( vec, dang );
1423 				SetClientViewAngle( player, dang );
1424 
1425 				VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
1426 				VectorCopy( dang, ent->s.apos.trBase );
1427 
1428 				trap_LinkEntity( ent );
1429 			}
1430 		}
1431 
1432 		trap_LinkEntity( player );
1433 	}
1434 
1435 	ent->nextthink = level.time + ( FRAMETIME / 2 );
1436 }
1437 
camera_cam_use(gentity_t * ent,gentity_t * other,gentity_t * activator)1438 void camera_cam_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
1439 	gentity_t *player;
1440 
1441 	player = AICast_FindEntityForName( "player" );
1442 
1443 	if ( !player ) {
1444 		return;
1445 	}
1446 
1447 	if ( !( ent->spawnflags & 1 ) ) {
1448 		ent->think = camera_cam_think;
1449 		ent->nextthink = level.time + ( FRAMETIME / 2 );
1450 		ent->spawnflags |= 1;
1451 		{
1452 			player->client->ps.persistant[PERS_HWEAPON_USE] = 1;
1453 			player->client->ps.viewlocked = 4;
1454 			player->client->ps.viewlocked_entNum = ent->s.number;
1455 		}
1456 	} else
1457 	{
1458 		ent->spawnflags &= ~1;
1459 		ent->think = 0;
1460 		{
1461 			player->client->ps.persistant[PERS_HWEAPON_USE] = 0;
1462 			player->client->ps.viewlocked = 0;
1463 			player->client->ps.viewlocked_entNum = 0;
1464 		}
1465 	}
1466 
1467 }
1468 
camera_cam_firstthink(gentity_t * ent)1469 void camera_cam_firstthink( gentity_t *ent ) {
1470 	gentity_t   *target = NULL;
1471 	vec3_t dang;
1472 	vec3_t vec;
1473 
1474 	if ( ent->track ) {
1475 		target = G_Find( NULL, FOFS( targetname ), ent->track );
1476 	}
1477 
1478 	if ( target ) {
1479 		VectorSubtract( target->s.origin, ent->r.currentOrigin, vec );
1480 		vectoangles( vec, dang );
1481 		G_SetAngle( ent, dang );
1482 	}
1483 
1484 	if ( ent->target ) {
1485 		ent->nextthink = level.time + ( FRAMETIME / 2 );
1486 		ent->think = Think_SetupTrainTargets;
1487 	}
1488 }
1489 
SP_camera_cam(gentity_t * ent)1490 void SP_camera_cam( gentity_t *ent ) {
1491 	Init_Camera( ent );
1492 
1493 	ent->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
1494 	ent->s.eType = ET_MOVER;
1495 
1496 	G_SetOrigin( ent, ent->s.origin );
1497 	G_SetAngle( ent, ent->s.angles );
1498 
1499 	ent->reached = Reached_Tramcar;
1500 
1501 	ent->nextthink = level.time + ( FRAMETIME / 2 );
1502 
1503 	ent->think = camera_cam_firstthink;
1504 
1505 	ent->use = camera_cam_use;
1506 
1507 	if ( ent->spawnflags & 1 ) { // On
1508 		gentity_t *delayOn;
1509 
1510 		delayOn = G_Spawn();
1511 		delayOn->think = delayOnthink;
1512 		delayOn->nextthink = level.time + 1000;
1513 		delayOn->melee = ent;
1514 		trap_LinkEntity( delayOn );
1515 	}
1516 
1517 }
1518 
1519 
1520 /*QUAKED screen_fade (.3 .7 .9) (-8 -8 -8) (8 8 8)
1521 "wait" duration of fade out
1522 "delay" duration of fade in
1523 
1524   1 = 1 sec
1525   .5 = .5 sec
1526 
1527 defaults are .5 sec
1528 */
screen_fade_use(gentity_t * ent,gentity_t * other,gentity_t * activator)1529 void screen_fade_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
1530 	if ( ent->spawnflags & 1 ) {
1531 		// fade out
1532 		trap_SetConfigstring( CS_SCREENFADE, va( "1 %i %i", level.time + 100, (int) ent->wait ) );
1533 		ent->spawnflags &= ~1;
1534 	} else
1535 	{
1536 		// fade in
1537 		trap_SetConfigstring( CS_SCREENFADE, va( "0 %i %i", level.time + 100, (int) ent->delay ) );
1538 		ent->spawnflags |= 1;
1539 	}
1540 
1541 }
1542 
SP_screen_fade(gentity_t * ent)1543 void SP_screen_fade( gentity_t *ent ) {
1544 	ent->use = screen_fade_use;
1545 
1546 	if ( !ent->wait ) {
1547 		ent->wait = 500;
1548 	}
1549 	if ( !ent->delay ) {
1550 		ent->delay = 500;
1551 	}
1552 
1553 }
1554 
1555 /*QUAKED camera_reset_player (.5 .7 .3) ?
1556 touched will record the players position and fire off its targets and or cameras
1557 
1558 used will reset the player back to his last position
1559 */
1560 
mark_players_pos(gentity_t * ent,gentity_t * other,trace_t * trace)1561 void mark_players_pos( gentity_t *ent, gentity_t *other, trace_t *trace ) {
1562 	gentity_t   *player;
1563 
1564 	player = AICast_FindEntityForName( "player" );
1565 
1566 	if ( player == other ) {
1567 		VectorCopy( player->r.currentOrigin, ent->s.origin2 );
1568 		VectorCopy( player->r.currentAngles, ent->s.angles2 );
1569 
1570 		G_UseTargets( ent, NULL );
1571 	}
1572 
1573 }
1574 
reset_players_pos(gentity_t * ent,gentity_t * other,gentity_t * activator)1575 void reset_players_pos( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
1576 
1577 	gentity_t *player;
1578 
1579 	player = AICast_FindEntityForName( "player" );
1580 
1581 	if ( !player ) {
1582 		return;
1583 	}
1584 
1585 	trap_UnlinkEntity( player );
1586 
1587 	VectorCopy( ent->s.origin2, player->client->ps.origin );
1588 
1589 	// save results of pmove
1590 	BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
1591 
1592 	// use the precise origin for linking
1593 	VectorCopy( player->client->ps.origin, player->r.currentOrigin );
1594 
1595 	SetClientViewAngle( player, ent->s.angles2 );
1596 
1597 	player->client->ps.persistant[PERS_HWEAPON_USE] = 0;
1598 	player->client->ps.viewlocked = 0;
1599 	player->client->ps.viewlocked_entNum = 0;
1600 
1601 	trap_LinkEntity( player );
1602 
1603 }
1604 
1605 extern void InitTrigger( gentity_t *self );
1606 
SP_camera_reset_player(gentity_t * ent)1607 void SP_camera_reset_player( gentity_t *ent ) {
1608 	InitTrigger( ent );
1609 
1610 	ent->r.contents = CONTENTS_TRIGGER;
1611 
1612 	ent->touch = mark_players_pos;
1613 	ent->use = reset_players_pos;
1614 
1615 	trap_LinkEntity( ent );
1616 }
1617