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