1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "g_local.h"
30
31 #define GENERIC_DAMAGE 6
32
33 int snd_boardbreak;
34 int snd_glassbreak;
35 int snd_metalbreak;
36 int snd_ceramicbreak;
37 int snd_chaircreak;
38 int snd_chairthrow;
39 int snd_chairhitground;
40
41 // JOSEPH 1-28-00
DropToFloorG(gentity_t * ent)42 void DropToFloorG( gentity_t *ent ) {
43 vec3_t dest;
44 trace_t tr;
45
46 VectorSet( dest, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2] - 4096 );
47 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
48
49 if ( tr.startsolid ) {
50 return;
51 }
52
53 ent->s.groundEntityNum = tr.entityNum;
54
55 G_SetOrigin( ent, tr.endpos );
56
57 ent->nextthink = level.time + FRAMETIME;
58 }
59
DropToFloor(gentity_t * ent)60 void DropToFloor( gentity_t *ent ) {
61 vec3_t dest;
62 trace_t tr;
63
64 VectorSet( dest, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2] - 4096 );
65 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
66
67 if ( tr.startsolid ) {
68 return;
69 }
70
71 if ( fabs( ent->r.currentOrigin[2] - tr.endpos[2] ) > 1.0 ) {
72 tr.endpos[2] = ( ent->r.currentOrigin[2] - 1.0 );
73 }
74
75 ent->s.groundEntityNum = tr.entityNum;
76
77 G_SetOrigin( ent, tr.endpos );
78
79 ent->think = DropToFloorG;
80 ent->nextthink = level.time + FRAMETIME;
81 }
82
moveit(gentity_t * ent,float yaw,float dist)83 void moveit( gentity_t *ent, float yaw, float dist ) {
84 vec3_t move;
85 vec3_t origin;
86 trace_t tr;
87 vec3_t mins, maxs;
88
89 yaw = yaw * M_PI * 2 / 360;
90
91 move[0] = cos( yaw ) * dist;
92 move[1] = sin( yaw ) * dist;
93 move[2] = 0;
94
95 VectorAdd( ent->r.currentOrigin, move, origin );
96
97 mins[0] = ent->r.mins[0];
98 mins[1] = ent->r.mins[1];
99 mins[2] = ent->r.mins[2] + .01;
100
101 maxs[0] = ent->r.maxs[0];
102 maxs[1] = ent->r.maxs[1];
103 maxs[2] = ent->r.maxs[2] - .01;
104
105 trap_Trace( &tr, ent->r.currentOrigin, mins, maxs, origin, ent->s.number, MASK_SHOT );
106
107 if ( ( tr.endpos[0] != origin[0] ) || ( tr.endpos[1] != origin[1] ) ) {
108 mins[0] = ent->r.mins[0] - 2.0;
109 mins[1] = ent->r.mins[1] - 2.0;
110 maxs[0] = ent->r.maxs[0] + 2.0;
111 maxs[1] = ent->r.maxs[1] + 2.0;
112
113 trap_Trace( &tr, ent->r.currentOrigin, mins, maxs, origin, ent->s.number, MASK_SHOT );
114 }
115
116 VectorCopy( tr.endpos, ent->r.currentOrigin );
117
118 VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
119
120 trap_LinkEntity( ent );
121
122 //DropToFloor( ent );
123 }
124
touch_props_box_32(gentity_t * self,gentity_t * other,trace_t * trace)125 void touch_props_box_32( gentity_t *self, gentity_t *other, trace_t *trace ) {
126 float ratio;
127 vec3_t v;
128
129 if ( other->r.currentOrigin[2] > ( self->r.currentOrigin[2] + 10 + 15 ) ) {
130 return;
131 }
132
133 ratio = 2.5;
134 VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, v );
135 moveit( self, vectoyaw( v ), ( 20 * ratio * FRAMETIME ) * .001 );
136 }
137
138 /*QUAKED props_box_32 (1 0 0) (-16 -16 -16) (16 16 16)
139
140 */
SP_props_box_32(gentity_t * self)141 void SP_props_box_32( gentity_t *self ) {
142 self->s.modelindex = G_ModelIndex( "models/mapobjects/boxes/box32.md3" );
143
144 self->clipmask = CONTENTS_SOLID;
145 self->r.contents = CONTENTS_SOLID;
146 self->r.svFlags = SVF_USE_CURRENT_ORIGIN;
147
148 VectorSet( self->r.mins, -16, -16, -16 );
149 VectorSet( self->r.maxs, 16, 16, 16 );
150
151 self->touch = touch_props_box_32;
152
153 trap_LinkEntity( self );
154
155 self->think = DropToFloor;
156 self->nextthink = level.time + FRAMETIME;
157 }
158
touch_props_box_48(gentity_t * self,gentity_t * other,trace_t * trace)159 void touch_props_box_48( gentity_t *self, gentity_t *other, trace_t *trace ) {
160 float ratio;
161 vec3_t v;
162
163 if ( other->r.currentOrigin[2] > ( self->r.currentOrigin[2] + 10 + 23 ) ) {
164 return;
165 }
166
167 ratio = 2.0;
168 VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, v );
169 moveit( self, vectoyaw( v ), ( 20 * ratio * FRAMETIME ) * .001 );
170 }
171
172 /*QUAKED props_box_48 (1 0 0) (-24 -24 -24) (24 24 24)
173
174 */
SP_props_box_48(gentity_t * self)175 void SP_props_box_48( gentity_t *self ) {
176 self->s.modelindex = G_ModelIndex( "models/mapobjects/boxes/box48.md3" );
177
178 self->clipmask = CONTENTS_SOLID;
179 self->r.contents = CONTENTS_SOLID;
180 self->r.svFlags = SVF_USE_CURRENT_ORIGIN;
181
182 VectorSet( self->r.mins, -24, -24, -24 );
183 VectorSet( self->r.maxs, 24, 24, 24 );
184
185 self->touch = touch_props_box_48;
186
187 trap_LinkEntity( self );
188
189 self->think = DropToFloor;
190 self->nextthink = level.time + FRAMETIME;
191 }
192
touch_props_box_64(gentity_t * self,gentity_t * other,trace_t * trace)193 void touch_props_box_64( gentity_t *self, gentity_t *other, trace_t *trace ) {
194 float ratio;
195 vec3_t v;
196
197 if ( other->r.currentOrigin[2] > ( self->r.currentOrigin[2] + 10 + 31 ) ) {
198 return;
199 }
200
201 ratio = 1.5;
202 VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, v );
203 moveit( self, vectoyaw( v ), ( 20 * ratio * FRAMETIME ) * .001 );
204 }
205
206 /*QUAKED props_box_64 (1 0 0) (-32 -32 -32) (32 32 32)
207
208 */
SP_props_box_64(gentity_t * self)209 void SP_props_box_64( gentity_t *self ) {
210 self->s.modelindex = G_ModelIndex( "models/mapobjects/boxes/box64.md3" );
211
212 self->clipmask = CONTENTS_SOLID;
213 self->r.contents = CONTENTS_SOLID;
214 self->r.svFlags = SVF_USE_CURRENT_ORIGIN;
215
216 VectorSet( self->r.mins, -32, -32, -32 );
217 VectorSet( self->r.maxs, 32, 32, 32 );
218
219 self->touch = touch_props_box_64;
220
221 trap_LinkEntity( self );
222
223 self->think = DropToFloor;
224 self->nextthink = level.time + FRAMETIME;
225 }
226 // END JOSEPH
227
228 // Rafael
229
Psmoke_think(gentity_t * ent)230 void Psmoke_think( gentity_t *ent ) {
231 gentity_t *tent;
232
233 ent->count++;
234
235 if ( ent->count == 30 ) {
236 ent->think = G_FreeEntity;
237 }
238
239 tent = G_TempEntity( ent->s.origin, EV_SMOKE );
240 VectorCopy( ent->s.origin, tent->s.origin );
241 tent->s.time = 3000;
242 tent->s.time2 = 100;
243 tent->s.density = 0;
244 tent->s.angles2[0] = 4;
245 tent->s.angles2[1] = 32;
246 tent->s.angles2[2] = 50;
247
248 ent->nextthink = level.time + FRAMETIME;
249 }
250
prop_smoke(gentity_t * ent)251 void prop_smoke( gentity_t *ent ) {
252 gentity_t *Psmoke;
253
254 Psmoke = G_Spawn();
255 VectorCopy( ent->r.currentOrigin, Psmoke->s.origin );
256 Psmoke->think = Psmoke_think;
257 Psmoke->nextthink = level.time + FRAMETIME;
258 }
259
260 /*QUAKED props_sparks (.8 .46 .16) (-8 -8 -8) (8 8 8) ELECTRIC
261 the default direction is strait up use info_no_null for alt direction
262
263 delay = how long till next spark effect
264 wait = life of the spark with some random variance default 1.0 sec
265 health = random number of sparks upto specified amount default 8
266
267 start_size default 8 along the x
268 end_size default 8 along the y
269 by changing the size will change the spawn origin of the individual spark
270 ei 16 x 8 or 24 x 32 would cause the sparks to spawn that many units from
271 the origin
272
273 speed controls how quickly the sparks will travel default is 2
274 */
275
PGUNsparks_use(gentity_t * ent,gentity_t * self,gentity_t * activator)276 void PGUNsparks_use( gentity_t *ent, gentity_t *self, gentity_t *activator ) {
277 gentity_t *tent;
278
279 tent = G_TempEntity( ent->r.currentOrigin, EV_GUNSPARKS );
280 VectorCopy( ent->r.currentOrigin, tent->s.origin );
281 VectorCopy( ent->r.currentAngles, tent->s.angles );
282 tent->s.density = ent->health;
283 tent->s.angles2[2] = ent->speed;
284
285 }
286
Psparks_think(gentity_t * ent)287 void Psparks_think( gentity_t *ent ) {
288 #if 0 //(SA) MOVE TO CLIENT!
289 gentity_t *tent;
290
291 if ( ent->spawnflags & 1 ) {
292 tent = G_TempEntity( ent->r.currentOrigin, EV_SPARKS_ELECTRIC );
293 } else {
294 tent = G_TempEntity( ent->r.currentOrigin, EV_SPARKS );
295 }
296 VectorCopy( ent->r.currentOrigin, tent->s.origin );
297 VectorCopy( ent->r.currentAngles, tent->s.angles );
298 tent->s.density = ent->health;
299 tent->s.frame = ent->wait;
300 tent->s.angles2[0] = ent->start_size;
301 tent->s.angles2[1] = ent->end_size;
302 tent->s.angles2[2] = ent->speed;
303
304 ent->nextthink = level.time + FRAMETIME + ent->delay + ( rand() % 600 );
305 #endif
306 }
307
sparks_angles_think(gentity_t * ent)308 void sparks_angles_think( gentity_t *ent ) {
309
310 gentity_t *target = NULL;
311 vec3_t vec;
312
313 if ( ent->target ) {
314 target = G_Find( NULL, FOFS( targetname ), ent->target );
315 }
316
317 if ( !target ) {
318 VectorSet( ent->r.currentAngles, 0, 0, 1 );
319 } else
320 {
321 VectorSubtract( ent->s.origin, target->s.origin, vec );
322 VectorNormalize( vec );
323 VectorCopy( vec, ent->r.currentAngles );
324 }
325
326 trap_LinkEntity( ent );
327
328 ent->nextthink = level.time + FRAMETIME;
329 if ( !Q_stricmp( ent->classname, "props_sparks" ) ) {
330 ent->think = Psparks_think;
331 } else {
332 ent->use = PGUNsparks_use;
333 }
334
335 }
336
SP_props_sparks(gentity_t * ent)337 void SP_props_sparks( gentity_t *ent ) {
338 // (SA) don't use in multiplayer right now since it makes decyphering net messages almost impossible
339 if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
340 ent->think = G_FreeEntity;
341 return;
342 }
343
344 G_SetOrigin( ent, ent->s.origin );
345 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
346 ent->s.eType = ET_GENERAL;
347
348 ent->think = sparks_angles_think;
349 ent->nextthink = level.time + FRAMETIME;
350
351 if ( !ent->health ) {
352 ent->health = 8;
353 }
354
355 if ( !ent->wait ) {
356 ent->wait = 1200;
357 } else {
358 ent->wait *= 1000;
359 }
360
361 if ( !ent->start_size ) {
362 ent->start_size = 8;
363 }
364
365 if ( !ent->end_size ) {
366 ent->end_size = 8;
367 }
368
369 if ( !ent->speed ) {
370 ent->speed = 2;
371 }
372
373 trap_LinkEntity( ent );
374
375 }
376
377 /*QUAKED props_gunsparks (.8 .46 .16) (-8 -8 -8) (8 8 8)
378 the default direction is strait up use info_no_null for alt direction
379
380 this entity must be used to see the effect
381
382 "speed" default is 20
383 "health" number to spawn default is 4
384 */
385
SP_props_gunsparks(gentity_t * ent)386 void SP_props_gunsparks( gentity_t *ent ) {
387 G_SetOrigin( ent, ent->s.origin );
388 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
389 ent->s.eType = ET_GENERAL;
390
391 ent->think = sparks_angles_think;
392 ent->nextthink = level.time + FRAMETIME;
393
394 if ( !ent->speed ) {
395 ent->speed = 20;
396 }
397
398 if ( !ent->health ) {
399 ent->health = 4;
400 }
401
402 trap_LinkEntity( ent );
403
404 }
405
406 /*QUAKED props_smokedust (.8 .46 .16) (-8 -8 -8) (8 8 8)
407 health = how many pieces 16 is default
408 */
409
smokedust_use(gentity_t * ent,gentity_t * self,gentity_t * activator)410 void smokedust_use( gentity_t *ent, gentity_t *self, gentity_t *activator ) {
411 int i;
412 gentity_t *tent;
413 vec3_t forward;
414
415 AngleVectors( ent->r.currentAngles, forward, NULL, NULL );
416
417 for ( i = 0; i < ent->health; i++ )
418 {
419 tent = G_TempEntity( ent->r.currentOrigin, EV_SMOKE );
420 VectorCopy( ent->r.currentOrigin, tent->s.origin );
421 VectorCopy( forward, tent->s.origin2 );
422 tent->s.time = 1000;
423 tent->s.time2 = 750;
424 tent->s.density = 3;
425 }
426 }
427
SP_SmokeDust(gentity_t * ent)428 void SP_SmokeDust( gentity_t *ent ) {
429
430 ent->use = smokedust_use;
431
432 G_SetOrigin( ent, ent->s.origin );
433 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
434 ent->s.eType = ET_GENERAL;
435
436 if ( !ent->health ) {
437 ent->health = 16;
438 }
439 trap_LinkEntity( ent );
440 }
441
442
443 /*QUAKED props_dust (.7 .3 .16) (-8 -8 -8) (8 8 8) WHITE
444 you should give this ent a target use a not null
445 or you could set its angles in the editor
446 */
447
dust_use(gentity_t * ent,gentity_t * self,gentity_t * activator)448 void dust_use( gentity_t *ent, gentity_t *self, gentity_t *activator ) {
449 gentity_t *tent;
450 vec3_t forward;
451
452 if ( ent->target ) {
453 tent = G_TempEntity( ent->r.currentOrigin, EV_DUST );
454 VectorCopy( ent->r.currentOrigin, tent->s.origin );
455 VectorCopy( ent->r.currentAngles, tent->s.angles );
456 if ( ent->spawnflags & 1 ) {
457 tent->s.density = 1;
458 }
459 } else
460 {
461
462 AngleVectors( ent->r.currentAngles, forward, NULL, NULL );
463
464 tent = G_TempEntity( ent->r.currentOrigin, EV_DUST );
465 VectorCopy( ent->r.currentOrigin, tent->s.origin );
466 VectorCopy( forward, tent->s.angles );
467 if ( ent->spawnflags & 1 ) {
468 tent->s.density = 1;
469 }
470 }
471 }
472
dust_angles_think(gentity_t * ent)473 void dust_angles_think( gentity_t *ent ) {
474 gentity_t *target;
475 vec3_t vec;
476
477 target = G_Find( NULL, FOFS( targetname ), ent->target );
478
479 if ( !target ) {
480 return;
481 }
482
483 VectorSubtract( ent->s.origin, target->s.origin, vec );
484 VectorCopy( vec, ent->r.currentAngles );
485 trap_LinkEntity( ent );
486
487 }
488
SP_Dust(gentity_t * ent)489 void SP_Dust( gentity_t *ent ) {
490 ent->use = dust_use;
491 G_SetOrigin( ent, ent->s.origin );
492 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
493 ent->s.eType = ET_GENERAL;
494
495 if ( ent->target ) {
496 ent->think = dust_angles_think;
497 ent->nextthink = level.time + FRAMETIME;
498 }
499
500 trap_LinkEntity( ent );
501 }
502
503 //////////////////////////////////////////////////////////
504 //////////////////////////////////////////////////////////
505
506 extern void G_ExplodeMissile( gentity_t *ent );
507
propExplosionLarge(gentity_t * ent)508 void propExplosionLarge( gentity_t *ent ) {
509 gentity_t *bolt;
510
511 bolt = G_Spawn();
512 bolt->classname = "props_explosion_large";
513 bolt->nextthink = level.time + FRAMETIME;
514 bolt->think = G_ExplodeMissile;
515 bolt->s.eType = ET_MISSILE;
516 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
517
518 bolt->s.weapon = WP_NONE;
519
520 bolt->s.eFlags = EF_BOUNCE_HALF;
521 bolt->r.ownerNum = ent->s.number;
522 bolt->parent = ent;
523 bolt->damage = ent->health;
524 bolt->splashDamage = ent->health;
525 bolt->splashRadius = ent->health * 1.5;
526 bolt->methodOfDeath = MOD_GRENADE;
527 bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH;
528 bolt->clipmask = MASK_SHOT;
529
530 VectorCopy( ent->r.currentOrigin, bolt->s.pos.trBase );
531 VectorCopy( ent->r.currentOrigin, bolt->r.currentOrigin );
532 }
533
propExplosion(gentity_t * ent)534 void propExplosion( gentity_t *ent ) {
535 gentity_t *bolt;
536
537 extern void G_ExplodeMissile( gentity_t * ent );
538 bolt = G_Spawn();
539 bolt->classname = "props_explosion";
540 bolt->nextthink = level.time + FRAMETIME;
541 bolt->think = G_ExplodeMissile;
542 bolt->s.eType = ET_MISSILE;
543 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
544
545 bolt->s.weapon = WP_NONE;
546
547 bolt->s.eFlags = EF_BOUNCE_HALF;
548 bolt->r.ownerNum = ent->s.number;
549 bolt->parent = ent;
550 bolt->damage = ent->health;
551 bolt->splashDamage = ent->health;
552 bolt->splashRadius = ent->health * 1.5;
553 bolt->methodOfDeath = MOD_GRENADE;
554 bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH;
555 bolt->clipmask = MASK_SHOT;
556
557 VectorCopy( ent->r.currentOrigin, bolt->s.pos.trBase );
558 VectorCopy( ent->r.currentOrigin, bolt->r.currentOrigin );
559 }
560
InitProp(gentity_t * ent)561 void InitProp( gentity_t *ent ) {
562 float light;
563 vec3_t color;
564 qboolean lightSet, colorSet;
565 char *sound;
566
567 if ( !Q_stricmp( ent->classname, "props_bench" ) ) {
568 ent->s.modelindex2 = G_ModelIndex( "models/furniture/bench/bench_sm.md3" );
569 } else if ( !Q_stricmp( ent->classname, "props_radio" ) ) {
570 ent->s.modelindex2 = G_ModelIndex( "models/mapobjects/electronics/radio1.md3" );
571 } else if ( !Q_stricmp( ent->classname, "props_locker_tall" ) ) {
572 ent->s.modelindex2 = G_ModelIndex( "models/furniture/storage/lockertall.md3" );
573 } else if ( !Q_stricmp( ent->classname, "props_flippy_table" ) ) {
574 ent->s.modelindex2 = G_ModelIndex( "models/furniture/table/woodflip.md3" );
575 } else if ( !Q_stricmp( ent->classname, "props_crate_32x64" ) ) {
576 ent->s.modelindex2 = G_ModelIndex( "models/furniture/crate/crate32x64.md3" );
577 } else if ( !Q_stricmp( ent->classname, "props_58x112tablew" ) ) {
578 ent->s.modelindex2 = G_ModelIndex( "models/furniture/table/56x112tablew.md3" );
579 } else if ( !Q_stricmp( ent->classname, "props_castlebed" ) ) {
580 ent->s.modelindex2 = G_ModelIndex( "models/furniture/bed/castlebed.md3" );
581 } else if ( !Q_stricmp( ent->classname, "props_radioSEVEN" ) ) {
582 ent->s.modelindex2 = G_ModelIndex( "models/mapobjects/electronics/radios.md3" );
583 }
584
585 // if the "loopsound" key is set, use a constant looping sound when moving
586 if ( G_SpawnString( "noise", "100", &sound ) ) {
587 ent->s.loopSound = G_SoundIndex( sound );
588 }
589
590 // if the "color" or "light" keys are set, setup constantLight
591 lightSet = G_SpawnFloat( "light", "100", &light );
592 colorSet = G_SpawnVector( "color", "1 1 1", color );
593 if ( lightSet || colorSet ) {
594 int r, g, b, i;
595
596 r = color[0] * 255;
597 if ( r > 255 ) {
598 r = 255;
599 }
600 g = color[1] * 255;
601 if ( g > 255 ) {
602 g = 255;
603 }
604 b = color[2] * 255;
605 if ( b > 255 ) {
606 b = 255;
607 }
608 i = light / 4;
609 if ( i > 255 ) {
610 i = 255;
611 }
612 ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 );
613 }
614
615 ent->isProp = qtrue;
616
617 ent->moverState = MOVER_POS1;
618 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
619 ent->s.eType = ET_MOVER;
620
621 G_SetOrigin( ent, ent->s.origin );
622 G_SetAngle( ent, ent->s.angles );
623 }
624
props_bench_think(gentity_t * ent)625 void props_bench_think( gentity_t *ent ) {
626 ent->s.frame++;
627
628 if ( ent->s.frame < 28 ) {
629 ent->nextthink = level.time + ( FRAMETIME / 2 );
630 } else
631 {
632 ent->clipmask = 0;
633 ent->r.contents = 0;
634 ent->takedamage = qfalse;
635
636 G_UseTargets( ent, NULL );
637 }
638
639 }
640
props_bench_die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)641 void props_bench_die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
642 ent->think = props_bench_think;
643 ent->nextthink = level.time + FRAMETIME;
644 }
645
646 /*QUAKED props_bench (.8 .6 .2) ?
647 requires an origin brush
648 health = 10 by default
649 */
SP_Props_Bench(gentity_t * ent)650 void SP_Props_Bench( gentity_t *ent ) {
651
652 trap_SetBrushModel( ent, ent->model );
653
654 InitProp( ent );
655
656 if ( !ent->health ) {
657 ent->health = 10;
658 }
659
660 ent->takedamage = qtrue;
661
662 ent->clipmask = CONTENTS_SOLID;
663
664 ent->die = props_bench_die;
665
666 trap_LinkEntity( ent );
667 }
668
props_radio_die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)669 void props_radio_die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
670
671 propExplosion( ent );
672
673 ent->takedamage = qfalse;
674
675 G_UseTargets( ent, NULL );
676
677 G_FreeEntity( ent );
678 }
679
680 /*QUAKED props_radio (.8 .6 .2) ?
681 requires an origin brush
682 health = defaults to 100
683 */
SP_Props_Radio(gentity_t * ent)684 void SP_Props_Radio( gentity_t *ent ) {
685
686 // Ridah, had to add this so I could load castle18dk7
687 if ( !ent->model ) {
688 G_Printf( S_COLOR_RED "props_radio with NULL model\n" );
689 return;
690 }
691
692 trap_SetBrushModel( ent, ent->model );
693
694 InitProp( ent );
695
696 if ( !ent->health ) {
697 ent->health = 100;
698 }
699
700 ent->takedamage = qtrue;
701
702 ent->die = props_radio_die;
703
704 trap_LinkEntity( ent );
705
706 }
707
708
props_radio_dieSEVEN(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)709 void props_radio_dieSEVEN( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
710
711 int i;
712
713 propExplosion( ent );
714
715 for ( i = 0; i < 20; i++ )
716 Spawn_Shard( ent, inflictor, 1, ent->count );
717
718 Prop_Break_Sound( ent );
719
720 ent->takedamage = qfalse;
721 ent->die = 0;
722
723 trap_LinkEntity( ent );
724
725 G_UseTargets( ent, NULL );
726
727 G_FreeEntity( ent );
728 }
729
730 /*QUAKED props_radioSEVEN (.8 .6 .2) ?
731 requires an origin brush
732 health = defaults to 100
733
734
735 the models dims are
736 x 32
737 y 136
738 z 32
739
740 if you want more explosions you'll need func explosive
741
742 it will fire all its targets upon death
743 */
SP_Props_RadioSEVEN(gentity_t * ent)744 void SP_Props_RadioSEVEN( gentity_t *ent ) {
745
746 if ( !ent->model ) {
747 G_Printf( S_COLOR_RED "props_radio with NULL model\n" );
748 return;
749 }
750
751 trap_SetBrushModel( ent, ent->model );
752
753 InitProp( ent );
754
755 if ( !ent->health ) {
756 ent->health = 100;
757 }
758
759 ent->takedamage = qtrue;
760
761 ent->die = props_radio_dieSEVEN;
762
763 ent->count = 2; // metal shard and sound
764
765 trap_LinkEntity( ent );
766
767 }
768
769
locker_tall_think(gentity_t * ent)770 void locker_tall_think( gentity_t *ent ) {
771 if ( ent->s.frame == 30 ) {
772 G_UseTargets( ent, NULL );
773
774 } else
775 {
776 ent->s.frame++;
777 ent->nextthink = level.time + ( FRAMETIME / 2 );
778 }
779
780 }
781
props_locker_tall_die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)782 void props_locker_tall_die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
783 ent->think = locker_tall_think;
784 ent->nextthink = level.time + FRAMETIME;
785
786 ent->takedamage = qfalse;
787
788 G_UseTargets( ent, NULL );
789 }
790
791 /*QUAKED props_locker_tall (.8 .6 .2) ?
792 requires an origin brush
793 */
SP_Props_Locker_Tall(gentity_t * ent)794 void SP_Props_Locker_Tall( gentity_t *ent ) {
795
796 // Ridah, had to add this so I could load castle18dk7
797 if ( !ent->model ) {
798 G_Printf( S_COLOR_RED "props_locker_tall with NULL model\n" );
799 return;
800 }
801
802 trap_SetBrushModel( ent, ent->model );
803
804 InitProp( ent );
805
806 if ( !ent->health ) {
807 ent->health = 100;
808 }
809
810 ent->takedamage = qtrue;
811
812 ent->die = props_locker_tall_die;
813
814 trap_LinkEntity( ent );
815
816 }
817
818 /*QUAKED props_chair_chat(.8 .6 .2) (-16 -16 0) (16 16 32)
819 point entity
820 health = default = 10
821 wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )
822
823 shard =
824 shard_glass = 0,
825 shard_wood = 1,
826 shard_metal = 2,
827 shard_ceramic = 3
828
829 */
830
831 /*QUAKED props_chair_chatarm(.8 .6 .2) (-16 -16 0) (16 16 32)
832 point entity
833 health = default = 10
834 wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )
835
836 shard =
837 shard_glass = 0,
838 shard_wood = 1,
839 shard_metal = 2,
840 shard_ceramic = 3
841
842 */
843
844 /*QUAKED props_chair_side (.8 .6 .2) (-16 -16 0) (16 16 32)
845 point entity
846 health = default = 10
847 wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )
848
849 shard =
850 shard_glass = 0,
851 shard_wood = 1,
852 shard_metal = 2,
853 shard_ceramic = 3
854
855 */
856
857
858 /*QUAKED props_chair_hiback (.8 .6 .2) (-16 -16 0) (16 16 32)
859 point entity
860 health = default = 10
861 wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )
862
863 shard =
864 shard_glass = 0,
865 shard_wood = 1,
866 shard_metal = 2,
867 shard_ceramic = 3
868
869 */
870
871 /*QUAKED props_chair (.8 .6 .2) (-16 -16 0) (16 16 32)
872 point entity
873 health = default = 10
874 wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )
875
876 shard =
877 shard_glass = 0,
878 shard_wood = 1,
879 shard_metal = 2,
880 shard_ceramic = 3
881
882 */
883 void Props_Chair_Think( gentity_t *self );
884 void Props_Chair_Touch( gentity_t *self, gentity_t *other, trace_t *trace );
885 void Props_Chair_Die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod );
886
Just_Got_Thrown(gentity_t * self)887 void Just_Got_Thrown( gentity_t *self ) {
888 float len = 0;
889 vec3_t vec;
890
891 if ( self->s.groundEntityNum == -1 ) {
892 self->nextthink = level.time + FRAMETIME;
893
894 if ( self->enemy ) {
895 gentity_t *player;
896
897 player = AICast_FindEntityForName( "player" );
898
899 if ( player && player != self->enemy ) {
900 G_Damage( self->enemy, self, self, NULL, NULL, 5, 0, MOD_CRUSH );
901
902 self->die = Props_Chair_Die;
903
904 self->die( self, self, NULL, 10, 0 );
905 }
906 }
907
908 return;
909 } else
910 {
911 // RF, alert AI of sound event
912 AICast_AudibleEvent( self->s.number, self->r.currentOrigin, 384 );
913
914 G_AddEvent( self, EV_GENERAL_SOUND, snd_chairhitground );
915 VectorSubtract( self->r.currentOrigin, self->s.origin2, vec );
916 len = VectorLength( vec );
917
918 {
919 trace_t trace;
920 vec3_t end;
921 gentity_t *traceEnt;
922 gentity_t *player;
923
924 VectorCopy( self->r.currentOrigin, end );
925 end[2] += 1;
926
927 trap_Trace( &trace, self->r.currentOrigin, self->r.mins, self->r.maxs, end, self->s.number, MASK_SHOT );
928
929 traceEnt = &g_entities[ trace.entityNum ];
930
931 if ( trace.startsolid ) {
932 player = AICast_FindEntityForName( "player" );
933
934 if ( traceEnt == player && traceEnt->health >= 0 ) {
935 // pick the chair back up
936 self->active = qtrue;
937 self->r.ownerNum = player->s.number;
938 player->active = qtrue;
939 player->melee = self;
940 self->nextthink = level.time + 50;
941
942 self->think = Props_Chair_Think;
943 self->touch = 0;
944 self->die = Props_Chair_Die;
945 self->s.eType = ET_MOVER;
946 self->s.dmgFlags = HINT_CHAIR; // so client knows what kind of mover it is for cursorhints
947
948 player->client->ps.eFlags |= EF_MELEE_ACTIVE;
949
950 trap_LinkEntity( self );
951
952 return;
953 } else {
954 len = 9999;
955 }
956 }
957 }
958
959 }
960
961 self->think = Props_Chair_Think;
962 self->touch = Props_Chair_Touch;
963 self->die = Props_Chair_Die;
964 self->s.eType = ET_MOVER;
965 self->s.dmgFlags = HINT_CHAIR; // so client knows what kind of mover it is for cursorhints
966
967 self->nextthink = level.time + FRAMETIME;
968
969 self->r.ownerNum = self->s.number;
970
971 if ( len > 256 ) {
972 self->die( self, self, NULL, 10, 0 );
973 }
974
975 }
976
Props_TurnLightsOff(gentity_t * self)977 void Props_TurnLightsOff( gentity_t *self ) {
978 if ( !Q_stricmp( self->classname, "props_desklamp" ) ) {
979 if ( self->target ) {
980 G_UseTargets( self, NULL );
981 self->target = NULL;
982 }
983 }
984 }
985
Props_Activated(gentity_t * self)986 void Props_Activated( gentity_t *self ) {
987 vec3_t angles;
988 vec3_t dest;
989 vec3_t forward, right;
990 vec3_t velocity;
991 vec3_t prop_ang;
992
993 gentity_t *prop;
994
995 gentity_t *owner;
996
997 owner = &g_entities[self->r.ownerNum];
998
999 self->nextthink = level.time + 50;
1000
1001 if ( !owner->client ) {
1002 return;
1003 }
1004
1005 Props_TurnLightsOff( self );
1006
1007 if ( owner->active == qfalse ) {
1008
1009 owner->melee = NULL;
1010
1011 self->physicsObject = qtrue;
1012 self->physicsBounce = 0.2;
1013
1014 self->s.groundEntityNum = -1;
1015
1016 self->s.pos.trType = TR_GRAVITY;
1017 self->s.pos.trTime = level.time;
1018
1019 self->active = qfalse;
1020
1021 G_AddEvent( owner, EV_GENERAL_SOUND, snd_chairthrow );
1022
1023 AngleVectors( owner->client->ps.viewangles, velocity, NULL, NULL );
1024 VectorScale( velocity, 250, velocity );
1025 velocity[2] += 100 + crandom() * 25;
1026 VectorCopy( velocity, self->s.pos.trDelta );
1027
1028 self->think = 0;
1029 self->nextthink = 0;
1030
1031 prop = G_Spawn();
1032 prop->s.modelindex = self->s.modelindex;
1033 G_SetOrigin( prop, self->r.currentOrigin );
1034
1035 VectorCopy( owner->client->ps.viewangles, prop_ang );
1036 prop_ang[0] = 0;
1037
1038 G_SetAngle( prop, prop_ang );
1039
1040 prop->clipmask = CONTENTS_SOLID;
1041 prop->r.contents = CONTENTS_SOLID;
1042 prop->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1043 prop->isProp = qtrue;
1044
1045 VectorSet( prop->r.mins, -12, -12, 0 );
1046 VectorSet( prop->r.maxs, 12, 12, 48 );
1047
1048 prop->physicsObject = qtrue;
1049 prop->physicsBounce = 0.2;
1050
1051 VectorCopy( owner->client->ps.origin, prop->s.pos.trBase );
1052
1053 VectorCopy( self->s.pos.trDelta, prop->s.pos.trDelta );
1054
1055 prop->s.pos.trType = TR_GRAVITY;
1056 prop->s.pos.trTime = level.time;
1057
1058 prop->active = qfalse;
1059
1060 prop->health = self->health;
1061
1062 prop->duration = self->health;
1063
1064 prop->count = self->count;
1065
1066 prop->think = Just_Got_Thrown;
1067 prop->nextthink = level.time + FRAMETIME;
1068
1069 prop->takedamage = qtrue;
1070
1071 prop->wait = self->wait;
1072
1073 prop->classname = self->classname;
1074
1075 prop->s.groundEntityNum = -1;
1076
1077 VectorCopy( self->r.currentOrigin, prop->s.origin2 );
1078
1079 prop->die = Props_Chair_Die;
1080
1081 prop->r.ownerNum = owner->s.number;
1082
1083 trap_LinkEntity( prop );
1084
1085 G_FreeEntity( self );
1086
1087 return;
1088 } else
1089 {
1090 if ( !Q_stricmp( self->classname, "props_chair_hiback" ) ) {
1091 self->s.frame = 23;
1092 self->s.density = 1;
1093 } else if ( !Q_stricmp( self->classname, "props_chair" ) ) {
1094 self->s.frame = 28;
1095 self->s.density = 1;
1096 } else if ( !Q_stricmp( self->classname, "props_chair_side" ) ) {
1097 self->s.frame = 23;
1098 self->s.density = 1;
1099 }
1100 }
1101
1102 trap_UnlinkEntity( self );
1103
1104 // move the entity in step with the activators movement
1105 VectorCopy( owner->client->ps.viewangles, angles );
1106 angles[0] = 0;
1107
1108 self->s.apos.trBase[YAW] = owner->client->ps.viewangles[YAW];
1109
1110 AngleVectors( angles, forward, right, NULL );
1111 VectorCopy( owner->r.currentOrigin, dest );
1112
1113 VectorCopy( dest, self->r.currentOrigin );
1114 VectorCopy( dest, self->s.pos.trBase );
1115
1116 self->s.eType = ET_PROP;
1117
1118 trap_LinkEntity( self );
1119
1120 }
1121
1122 void Prop_Check_Ground( gentity_t *self );
1123
Props_Chair_Think(gentity_t * self)1124 void Props_Chair_Think( gentity_t *self ) {
1125 trace_t tr;
1126
1127 if ( self->active ) {
1128 Props_Activated( self );
1129 return;
1130 }
1131
1132 trap_UnlinkEntity( self );
1133
1134 BG_EvaluateTrajectory( &self->s.pos, level.time, self->s.pos.trBase );
1135
1136 if ( level.time > self->s.pos.trDuration ) {
1137 VectorClear( self->s.pos.trDelta );
1138 self->s.pos.trDuration = 0;
1139 self->s.pos.trType = TR_STATIONARY;
1140 } else
1141 {
1142 vec3_t mins, maxs;
1143
1144 VectorCopy( self->r.mins, mins );
1145 VectorCopy( self->r.maxs, maxs );
1146
1147 mins[2] += 1;
1148
1149 trap_Trace( &tr, self->r.currentOrigin, mins, maxs, self->s.pos.trBase, self->s.number, MASK_SHOT );
1150
1151 if ( tr.fraction == 1 ) {
1152 VectorCopy( self->s.pos.trBase, self->r.currentOrigin );
1153 } else
1154 {
1155 VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
1156 VectorClear( self->s.pos.trDelta );
1157 self->s.pos.trDuration = 0;
1158 self->s.pos.trType = TR_STATIONARY;
1159 }
1160
1161 }
1162
1163 if ( self->s.groundEntityNum == -1 ) {
1164
1165 self->physicsObject = qtrue;
1166 self->physicsBounce = 0.2;
1167
1168 self->s.pos.trDelta[2] -= 200;
1169
1170 self->s.pos.trType = TR_GRAVITY;
1171 self->s.pos.trTime = level.time;
1172
1173 self->active = qfalse;
1174
1175 self->think = Just_Got_Thrown;
1176
1177 if ( self->s.pos.trType != TR_GRAVITY ) {
1178 self->s.pos.trType = TR_GRAVITY;
1179 self->s.pos.trTime = level.time;
1180 }
1181 }
1182
1183
1184 Prop_Check_Ground( self );
1185
1186
1187 self->nextthink = level.time + 50;
1188 trap_LinkEntity( self );
1189 }
1190
Prop_Touch(gentity_t * self,gentity_t * other,vec3_t v)1191 qboolean Prop_Touch( gentity_t *self, gentity_t *other, vec3_t v ) {
1192
1193 vec3_t forward;
1194 vec3_t dest;
1195 vec3_t angle;
1196 vec3_t start, end;
1197 vec3_t mins, maxs;
1198 trace_t tr;
1199
1200 if ( !other->client ) {
1201 return qfalse;
1202 }
1203
1204 vectoangles( v, angle );
1205 angle[0] = 0;
1206 AngleVectors( angle, forward, NULL, NULL );
1207 VectorClear( dest );
1208 VectorMA( dest, 128, forward, dest );
1209 VectorMA( self->r.currentOrigin, 32, forward, end );
1210
1211 VectorCopy( self->r.currentOrigin, start );
1212 end[2] += 8;
1213 start[2] += 8;
1214
1215 VectorCopy( self->r.mins, mins );
1216 VectorCopy( self->r.maxs, maxs );
1217
1218 mins[2] += 1;
1219
1220 trap_Trace( &tr, start, mins, maxs, end, self->s.number, MASK_SHOT );
1221
1222 if ( tr.fraction != 1 ) {
1223 return qfalse;
1224 }
1225
1226 VectorCopy( dest, self->s.pos.trDelta );
1227 VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
1228
1229 self->s.pos.trDuration = level.time + 100;
1230 self->s.pos.trTime = level.time;
1231 self->s.pos.trType = TR_LINEAR;
1232
1233 self->physicsObject = qtrue;
1234
1235 return qtrue;
1236 }
1237
Prop_Check_Ground(gentity_t * self)1238 void Prop_Check_Ground( gentity_t *self ) {
1239 vec3_t mins, maxs;
1240 vec3_t start, end;
1241 trace_t tr;
1242
1243 VectorCopy( self->r.currentOrigin, start );
1244 VectorCopy( self->r.currentOrigin, end );
1245
1246 end[2] -= 4;
1247
1248 VectorCopy( self->r.mins, mins );
1249 VectorCopy( self->r.maxs, maxs );
1250
1251 trap_Trace( &tr, start, mins, maxs, end, self->s.number, MASK_SHOT );
1252
1253 if ( tr.fraction == 1 ) {
1254 self->s.groundEntityNum = -1;
1255 }
1256
1257 }
1258
Props_Chair_Touch(gentity_t * self,gentity_t * other,trace_t * trace)1259 void Props_Chair_Touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
1260 vec3_t v;
1261 qboolean has_moved;
1262
1263 if ( !other->client ) {
1264 return;
1265 }
1266
1267 if ( other->r.currentOrigin[2] > ( self->r.currentOrigin[2] + 10 + 15 ) ) {
1268 return;
1269 }
1270
1271 if ( self->active ) { // someone has activated me
1272 return;
1273 }
1274
1275 VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, v );
1276
1277 has_moved = Prop_Touch( self, other, v );
1278
1279 if ( !has_moved && ( other->r.svFlags & SVF_CASTAI ) ) {
1280 // RF, alert AI of sound event
1281 AICast_AudibleEvent( self->s.number, self->r.currentOrigin, 384 );
1282
1283 // other could play kick animation here
1284 Props_Chair_Die( self, other, other, 100, 0 );
1285 return;
1286 }
1287
1288 Prop_Check_Ground( self );
1289
1290 if ( level.time > self->random && has_moved ) {
1291 // RF, alert AI of sound event
1292 AICast_AudibleEvent( self->s.number, self->r.currentOrigin, 384 );
1293
1294 G_AddEvent( self, EV_GENERAL_SOUND, snd_chaircreak );
1295 self->random = level.time + 1000 + ( rand() % 200 );
1296 }
1297
1298 if ( !Q_stricmp( self->classname, "props_desklamp" ) ) {
1299 // player may have picked it up before
1300 if ( self->target ) {
1301 G_UseTargets( self, NULL );
1302 self->target = NULL;
1303 }
1304 }
1305
1306 }
1307
Props_Chair_Animate(gentity_t * ent)1308 void Props_Chair_Animate( gentity_t *ent ) {
1309
1310 ent->touch = 0;
1311
1312 if ( !Q_stricmp( ent->classname, "props_chair" ) ) {
1313 if ( ent->s.frame >= 27 ) {
1314 ent->s.frame = 27;
1315 G_UseTargets( ent, NULL );
1316 ent->think = G_FreeEntity;
1317 ent->nextthink = level.time + 2000;
1318 ent->s.time = level.time;
1319 ent->s.time2 = level.time + 2000;
1320 return;
1321 } else
1322 {
1323 ent->nextthink = level.time + ( FRAMETIME / 2 );
1324 }
1325 } else if (
1326 ( !Q_stricmp( ent->classname, "props_chair_side" ) ) ||
1327 ( !Q_stricmp( ent->classname, "props_chair_chat" ) ) ||
1328 ( !Q_stricmp( ent->classname, "props_chair_chatarm" ) ) ||
1329 ( !Q_stricmp( ent->classname, "props_chair_hiback" ) )
1330 ) {
1331 if ( ent->s.frame >= 20 ) {
1332 ent->s.frame = 20;
1333 G_UseTargets( ent, NULL );
1334 ent->think = G_FreeEntity;
1335 ent->nextthink = level.time + 2000;
1336 ent->s.time = level.time;
1337 ent->s.time2 = level.time + 2000;
1338 return;
1339 } else
1340 {
1341 ent->nextthink = level.time + ( FRAMETIME / 2 );
1342 }
1343 } else if ( !Q_stricmp( ent->classname, "props_desklamp" ) ) {
1344 if ( ent->s.frame >= 11 ) {
1345 // player may have picked it up before
1346 if ( ent->target ) {
1347 G_UseTargets( ent, NULL );
1348 }
1349
1350 ent->think = G_FreeEntity;
1351 ent->nextthink = level.time + 2000;
1352 ent->s.time = level.time;
1353 ent->s.time2 = level.time + 2000;
1354 return;
1355 } else
1356 {
1357 ent->nextthink = level.time + ( FRAMETIME / 2 );
1358 }
1359 }
1360
1361
1362 ent->s.frame++;
1363
1364 if ( ent->enemy ) {
1365 float ratio;
1366 vec3_t v;
1367
1368 ratio = 2.5;
1369 VectorSubtract( ent->r.currentOrigin, ent->enemy->r.currentOrigin, v );
1370 moveit( ent, vectoyaw( v ), ( ent->delay * ratio * FRAMETIME ) * .001 );
1371 }
1372
1373 }
1374
Spawn_Shard(gentity_t * ent,gentity_t * inflictor,int quantity,int type)1375 void Spawn_Shard( gentity_t *ent, gentity_t *inflictor, int quantity, int type ) {
1376 gentity_t *sfx;
1377 vec3_t dir, start;
1378
1379 VectorCopy( ent->r.currentOrigin, start );
1380
1381 if ( !Q_stricmp( ent->classname, "props_radioSEVEN" ) ) {
1382 start[0] += crandom() * 32;
1383 start[1] += crandom() * 32;
1384 VectorSubtract( inflictor->r.currentOrigin, ent->r.currentOrigin, dir );
1385 VectorNormalize( dir );
1386 } else if ( inflictor ) {
1387 VectorSubtract( inflictor->r.currentOrigin, ent->r.currentOrigin, dir );
1388 VectorNormalize( dir );
1389 VectorNegate( dir, dir );
1390 } else {
1391 VectorSet( dir, 0,0,1 );
1392 }
1393
1394 sfx = G_Spawn();
1395
1396 sfx->s.density = type;
1397
1398 if ( type < 4 ) {
1399 start[2] += 32;
1400 }
1401
1402 G_SetOrigin( sfx, start );
1403 G_SetAngle( sfx, ent->r.currentAngles );
1404
1405 G_AddEvent( sfx, EV_SHARD, DirToByte( dir ) );
1406
1407 sfx->think = G_FreeEntity;
1408
1409 sfx->nextthink = level.time + 1000;
1410
1411 sfx->s.frame = quantity;
1412
1413 trap_LinkEntity( sfx );
1414 }
1415
Prop_Break_Sound(gentity_t * ent)1416 void Prop_Break_Sound( gentity_t *ent ) {
1417 switch ( ent->count )
1418 {
1419 case shard_wood:
1420 G_AddEvent( ent, EV_GENERAL_SOUND, snd_boardbreak );
1421 break;
1422 case shard_glass:
1423 G_AddEvent( ent, EV_GENERAL_SOUND, snd_glassbreak );
1424 break;
1425 case shard_metal:
1426 G_AddEvent( ent, EV_GENERAL_SOUND, snd_metalbreak );
1427 break;
1428 case shard_ceramic:
1429 G_AddEvent( ent, EV_GENERAL_SOUND, snd_ceramicbreak );
1430 break;
1431 }
1432 }
1433
1434
Props_Chair_Die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)1435 void Props_Chair_Die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
1436 int quantity;
1437 int type;
1438
1439 if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
1440 gentity_t *player;
1441
1442 player = AICast_FindEntityForName( "player" );
1443
1444 if ( player && player->melee == ent ) {
1445 player->melee = NULL;
1446 player->active = qfalse;
1447 player->client->ps.eFlags &= ~EF_MELEE_ACTIVE;
1448
1449 } else if ( player && player->s.number == ent->r.ownerNum ) {
1450 player->active = qfalse;
1451 player->melee = NULL;
1452 player->client->ps.eFlags &= ~EF_MELEE_ACTIVE;
1453 }
1454 }
1455
1456 ent->think = Props_Chair_Animate;
1457 ent->nextthink = level.time + FRAMETIME;
1458
1459 ent->health = ent->duration;
1460 ent->delay = damage;
1461 ent->takedamage = qfalse;
1462 // ent->enemy = inflictor;
1463
1464 quantity = ent->wait;
1465 type = ent->count;
1466
1467 Spawn_Shard( ent, inflictor, quantity, type );
1468
1469 Prop_Break_Sound( ent );
1470
1471 trap_UnlinkEntity( ent );
1472
1473 ent->clipmask = 0;
1474 ent->r.contents = 0;
1475 ent->s.eType = ET_GENERAL;
1476
1477 trap_LinkEntity( ent );
1478
1479 }
1480
Props_Chair_Skyboxtouch(gentity_t * ent)1481 void Props_Chair_Skyboxtouch( gentity_t *ent ) {
1482
1483 gentity_t *player;
1484
1485 player = AICast_FindEntityForName( "player" );
1486
1487 if ( player && player->melee == ent ) {
1488 player->melee = NULL;
1489 player->active = qfalse;
1490 player->client->ps.eFlags &= ~EF_MELEE_ACTIVE;
1491 } else if ( player && player->s.number == ent->r.ownerNum ) {
1492 player->active = qfalse;
1493 player->melee = NULL;
1494 player->client->ps.eFlags &= ~EF_MELEE_ACTIVE;
1495 }
1496
1497 ent->think = G_FreeEntity;
1498
1499 }
1500
SP_Props_Chair(gentity_t * ent)1501 void SP_Props_Chair( gentity_t *ent ) {
1502 int mass;
1503
1504 ent->s.modelindex = G_ModelIndex( "models/furniture/chair/chair_office3.md3" );
1505
1506 ent->delay = 0; // inherits damage value
1507
1508 if ( G_SpawnInt( "mass", "5", &mass ) ) {
1509 ent->wait = mass;
1510 } else {
1511 ent->wait = 5;
1512 }
1513
1514 ent->clipmask = CONTENTS_SOLID;
1515 ent->r.contents = CONTENTS_SOLID;
1516 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1517 ent->s.eType = ET_MOVER;
1518 ent->s.dmgFlags = HINT_CHAIR; // so client knows what kind of mover it is for cursorhints
1519
1520 ent->isProp = qtrue;
1521
1522 VectorSet( ent->r.mins, -12, -12, 0 );
1523 VectorSet( ent->r.maxs, 12, 12, 48 );
1524
1525 G_SetOrigin( ent, ent->s.origin );
1526 G_SetAngle( ent, ent->s.angles );
1527
1528 if ( !ent->health ) {
1529 ent->health = 10;
1530 }
1531
1532 ent->duration = ent->health;
1533
1534 if ( !ent->count ) {
1535 ent->count = 1;
1536 }
1537
1538 ent->think = Props_Chair_Think;
1539 ent->nextthink = level.time + FRAMETIME;
1540
1541 ent->touch = Props_Chair_Touch;
1542 ent->die = Props_Chair_Die;
1543 ent->takedamage = qtrue;
1544 trap_LinkEntity( ent );
1545
1546 snd_boardbreak = G_SoundIndex( "sound/world/boardbreak.wav" );
1547 snd_glassbreak = G_SoundIndex( "sound/world/glassbreak.wav" );
1548 snd_metalbreak = G_SoundIndex( "sound/world/metalbreak.wav" );
1549 snd_ceramicbreak = G_SoundIndex( "sound/world/ceramicbreak.wav" );
1550 snd_chaircreak = G_SoundIndex( "sound/world/chaircreak.wav" );
1551
1552 }
1553
SP_Props_ChairHiback(gentity_t * ent)1554 void SP_Props_ChairHiback( gentity_t *ent ) {
1555 int mass;
1556
1557 // ent->s.modelindex = G_ModelIndex( "models/furniture/chair/hiback.md3" );
1558 ent->s.modelindex = G_ModelIndex( "models/furniture/chair/hiback5.md3" );
1559
1560 ent->delay = 0; // inherits damage value
1561
1562 if ( G_SpawnInt( "mass", "5", &mass ) ) {
1563 ent->wait = mass;
1564 } else {
1565 ent->wait = 5;
1566 }
1567
1568 ent->clipmask = CONTENTS_SOLID;
1569 ent->r.contents = CONTENTS_SOLID;
1570 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1571 ent->s.eType = ET_MOVER;
1572 ent->s.dmgFlags = HINT_CHAIR; // so client knows what kind of mover it is for cursorhints
1573
1574 ent->isProp = qtrue;
1575
1576 VectorSet( ent->r.mins, -12, -12, 0 );
1577 VectorSet( ent->r.maxs, 12, 12, 48 );
1578
1579 G_SetOrigin( ent, ent->s.origin );
1580 G_SetAngle( ent, ent->s.angles );
1581
1582 if ( !ent->health ) {
1583 ent->health = 10;
1584 }
1585
1586 ent->duration = ent->health;
1587
1588 if ( !ent->count ) {
1589 ent->count = 1;
1590 }
1591
1592 ent->think = Props_Chair_Think;
1593 ent->nextthink = level.time + FRAMETIME;
1594
1595 ent->touch = Props_Chair_Touch;
1596 ent->die = Props_Chair_Die;
1597 ent->takedamage = qtrue;
1598 trap_LinkEntity( ent );
1599
1600 snd_boardbreak = G_SoundIndex( "sound/world/boardbreak.wav" );
1601 snd_glassbreak = G_SoundIndex( "sound/world/glassbreak.wav" );
1602 snd_metalbreak = G_SoundIndex( "sound/world/metalbreak.wav" );
1603 snd_ceramicbreak = G_SoundIndex( "sound/world/ceramicbreak.wav" );
1604 snd_chaircreak = G_SoundIndex( "sound/world/chaircreak.wav" );
1605 }
1606
SP_Props_ChairSide(gentity_t * ent)1607 void SP_Props_ChairSide( gentity_t *ent ) {
1608 int mass;
1609
1610 ent->s.modelindex = G_ModelIndex( "models/furniture/chair/sidechair3.md3" );
1611
1612 ent->delay = 0; // inherits damage value
1613
1614 if ( G_SpawnInt( "mass", "5", &mass ) ) {
1615 ent->wait = mass;
1616 } else {
1617 ent->wait = 5;
1618 }
1619
1620 ent->clipmask = CONTENTS_SOLID;
1621 ent->r.contents = CONTENTS_SOLID;
1622 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1623 ent->s.eType = ET_MOVER;
1624 ent->s.dmgFlags = HINT_CHAIR; // so client knows what kind of mover it is for cursorhints
1625
1626 ent->isProp = qtrue;
1627
1628 VectorSet( ent->r.mins, -12, -12, 0 );
1629 VectorSet( ent->r.maxs, 12, 12, 48 );
1630
1631 G_SetOrigin( ent, ent->s.origin );
1632 G_SetAngle( ent, ent->s.angles );
1633
1634 if ( !ent->health ) {
1635 ent->health = 10;
1636 }
1637
1638 ent->duration = ent->health;
1639
1640 if ( !ent->count ) {
1641 ent->count = 1;
1642 }
1643
1644 ent->think = Props_Chair_Think;
1645 ent->nextthink = level.time + FRAMETIME;
1646
1647 ent->touch = Props_Chair_Touch;
1648 ent->die = Props_Chair_Die;
1649 ent->takedamage = qtrue;
1650 trap_LinkEntity( ent );
1651
1652 snd_boardbreak = G_SoundIndex( "sound/world/boardbreak.wav" );
1653 snd_glassbreak = G_SoundIndex( "sound/world/glassbreak.wav" );
1654 snd_metalbreak = G_SoundIndex( "sound/world/metalbreak.wav" );
1655 snd_ceramicbreak = G_SoundIndex( "sound/world/ceramicbreak.wav" );
1656 snd_chaircreak = G_SoundIndex( "sound/world/chaircreak.wav" );
1657 snd_chairthrow = G_SoundIndex( "sound/props/throw/chairthudgrunt.wav" );
1658 snd_chairhitground = G_SoundIndex( "sound/props/chair/chairthud.wav" );
1659 }
1660
1661 //----(SA) modified
1662
1663 // can be one of two types, but they have the same animations/etc, so re-use what you can
1664 /*
1665 ==============
1666 SP_Props_ChateauChair
1667 ==============
1668 */
SP_Props_ChateauChair(gentity_t * ent)1669 void SP_Props_ChateauChair( gentity_t *ent ) {
1670 int mass;
1671
1672 ent->delay = 0; // inherits damage value
1673
1674 if ( G_SpawnInt( "mass", "5", &mass ) ) {
1675 ent->wait = mass;
1676 } else {
1677 ent->wait = 5;
1678 }
1679
1680 ent->clipmask = CONTENTS_SOLID;
1681 ent->r.contents = CONTENTS_SOLID;
1682 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1683 ent->s.eType = ET_MOVER;
1684 ent->s.dmgFlags = HINT_CHAIR; // so client knows what kind of mover it is for cursorhints
1685
1686 ent->isProp = qtrue;
1687
1688 VectorSet( ent->r.mins, -12, -12, 0 );
1689 VectorSet( ent->r.maxs, 12, 12, 48 );
1690
1691 G_SetOrigin( ent, ent->s.origin );
1692 G_SetAngle( ent, ent->s.angles );
1693
1694 if ( !ent->health ) {
1695 ent->health = 10;
1696 }
1697
1698 ent->duration = ent->health;
1699
1700 if ( !ent->count ) {
1701 ent->count = 1;
1702 }
1703
1704 ent->think = Props_Chair_Think;
1705 ent->nextthink = level.time + FRAMETIME;
1706
1707 ent->touch = Props_Chair_Touch;
1708 ent->die = Props_Chair_Die;
1709 ent->takedamage = qtrue;
1710 trap_LinkEntity( ent );
1711
1712 snd_boardbreak = G_SoundIndex( "sound/world/boardbreak.wav" );
1713 snd_glassbreak = G_SoundIndex( "sound/world/glassbreak.wav" );
1714 snd_metalbreak = G_SoundIndex( "sound/world/metalbreak.wav" );
1715 snd_ceramicbreak = G_SoundIndex( "sound/world/ceramicbreak.wav" );
1716 snd_chaircreak = G_SoundIndex( "sound/world/chaircreak.wav" );
1717 snd_chairthrow = G_SoundIndex( "sound/props/throw/chairthudgrunt.wav" );
1718 snd_chairhitground = G_SoundIndex( "sound/props/chair/chairthud.wav" );
1719 }
1720
1721
1722 /*
1723 ==============
1724 SP_Props_ChairChat
1725 ==============
1726 */
SP_Props_ChairChat(gentity_t * ent)1727 void SP_Props_ChairChat( gentity_t *ent ) {
1728 ent->s.modelindex = G_ModelIndex( "models/furniture/chair/chair_chat.md3" );
1729 SP_Props_ChateauChair( ent );
1730 }
1731 /*
1732 ==============
1733 SP_Props_ChairChatArm
1734 ==============
1735 */
SP_Props_ChairChatArm(gentity_t * ent)1736 void SP_Props_ChairChatArm( gentity_t *ent ) {
1737 ent->s.modelindex = G_ModelIndex( "models/furniture/chair/chair_chatarm.md3" );
1738 SP_Props_ChateauChair( ent );
1739 }
1740
1741 //----(SA) end
1742
1743
Use_DamageInflictor(gentity_t * ent,gentity_t * other,gentity_t * activator)1744 void Use_DamageInflictor( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
1745 gentity_t *daent;
1746
1747 daent = NULL;
1748 while ( daent && ( ( daent = G_Find( daent, FOFS( targetname ), daent->target ) ) != NULL ) )
1749 {
1750 if ( daent == ent ) {
1751 G_Printf( "Use_DamageInflictor damaging self.\n" );
1752 } else
1753 {
1754 G_Damage( daent, ent, ent, NULL, NULL, 9999, 0, MOD_CRUSH );
1755 }
1756 }
1757
1758 G_FreeEntity( ent );
1759 }
1760
1761 /*QUAKED props_damageinflictor (.8 .6 .6) (-8 -8 -8) (8 8 8)
1762 this entity when used will cause 9999 damage to all entities it is targeting
1763 then it will be removed
1764 */
SP_Props_DamageInflictor(gentity_t * ent)1765 void SP_Props_DamageInflictor( gentity_t *ent ) {
1766 G_SetOrigin( ent, ent->s.origin );
1767 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1768 ent->s.eType = ET_GENERAL;
1769
1770 ent->use = Use_DamageInflictor;
1771 trap_LinkEntity( ent );
1772 }
1773
1774 /*QUAKED props_shard_generator (.8 .5 .1) (-4 -4 -4) (4 4 4)
1775
1776 wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )
1777
1778 shard =
1779 shard_glass = 0,
1780 shard_wood = 1,
1781 shard_metal = 2,
1782 shard_ceramic = 3
1783
1784 */
1785
Use_Props_Shard_Generator(gentity_t * ent,gentity_t * other,gentity_t * activator)1786 void Use_Props_Shard_Generator( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
1787 int quantity;
1788 int type;
1789 gentity_t *inflictor = NULL;
1790
1791 type = ent->count;
1792 quantity = ent->wait;
1793
1794 inflictor = G_Find( NULL, FOFS( targetname ), ent->target );
1795
1796 if ( inflictor ) {
1797 Spawn_Shard( ent, inflictor, quantity, type );
1798 }
1799
1800 G_FreeEntity( ent );
1801 }
1802
SP_props_shard_generator(gentity_t * ent)1803 void SP_props_shard_generator( gentity_t *ent ) {
1804 G_SetOrigin( ent, ent->s.origin );
1805 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1806 ent->s.eType = ET_GENERAL;
1807 ent->use = Use_Props_Shard_Generator;
1808
1809 if ( !ent->count ) {
1810 ent->count = shard_wood;
1811 }
1812
1813 if ( !ent->wait ) {
1814 ent->wait = 5;
1815 }
1816
1817 trap_LinkEntity( ent );
1818 }
1819
1820
1821 /*QUAKED props_desklamp (.8 .6 .2) (-16 -16 0) (16 16 32)
1822 point entity
1823 health = default = 10
1824 wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )
1825
1826 shard =
1827 shard_glass = 0,
1828 shard_wood = 1,
1829 shard_metal = 2,
1830 shard_ceramic = 3
1831
1832 */
SP_Props_Desklamp(gentity_t * ent)1833 void SP_Props_Desklamp( gentity_t *ent ) {
1834 int mass;
1835
1836 ent->s.modelindex = G_ModelIndex( "models/furniture/lights/desklamp.md3" );
1837
1838 ent->delay = 0; // inherits damage value
1839
1840 if ( G_SpawnInt( "mass", "5", &mass ) ) {
1841 ent->wait = mass;
1842 } else {
1843 ent->wait = 2;
1844 }
1845
1846 ent->clipmask = CONTENTS_SOLID;
1847 ent->r.contents = CONTENTS_SOLID;
1848 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1849 ent->s.eType = ET_MOVER;
1850
1851 ent->isProp = qtrue;
1852 ent->nopickup = qtrue;
1853
1854 VectorSet( ent->r.mins, -6, -6, 0 );
1855 VectorSet( ent->r.maxs, 6, 6, 14 );
1856
1857 G_SetOrigin( ent, ent->s.origin );
1858 G_SetAngle( ent, ent->s.angles );
1859
1860 if ( !ent->health ) {
1861 ent->health = 10;
1862 }
1863
1864 ent->duration = ent->health;
1865
1866 if ( !ent->count ) {
1867 ent->count = 2;
1868 }
1869
1870 ent->think = Props_Chair_Think;
1871 ent->nextthink = level.time + FRAMETIME;
1872
1873 ent->touch = Props_Chair_Touch;
1874 ent->die = Props_Chair_Die;
1875 ent->takedamage = qtrue;
1876 trap_LinkEntity( ent );
1877
1878 snd_boardbreak = G_SoundIndex( "sound/world/boardbreak.wav" );
1879 snd_glassbreak = G_SoundIndex( "sound/world/glassbreak.wav" );
1880 snd_metalbreak = G_SoundIndex( "sound/world/metalbreak.wav" );
1881 snd_ceramicbreak = G_SoundIndex( "sound/world/ceramicbreak.wav" );
1882 snd_chaircreak = G_SoundIndex( "sound/world/chaircreak.wav" );
1883 }
1884
1885 /*QUAKED props_flamebarrel (.8 .6 .2) (-13 -13 0) (13 13 40) SMOKING NOLID OIL -
1886 angle will determine which way the lid will fly off when it explodes
1887
1888 when selecting the OIL spawnflag you have the option of giving it a target
1889 this will ensure that the oil sprite will show up where you want it
1890 ( be sure to put it on the floor )
1891 the default is in the middle of the barrel on the floor
1892 */
Props_Barrel_Touch(gentity_t * self,gentity_t * other,trace_t * trace)1893 void Props_Barrel_Touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
1894 #if 0 // barrels cant move
1895 if ( !( self->spawnflags & 4 ) ) {
1896 Props_Chair_Touch( self, other, trace );
1897 }
1898 #endif
1899 }
1900
Props_Barrel_Animate(gentity_t * ent)1901 void Props_Barrel_Animate( gentity_t *ent ) {
1902 float ratio;
1903 vec3_t v;
1904
1905 if ( ent->s.frame == 14 ) {
1906 if ( ent->spawnflags & 1 ) {
1907 // G_UseTargets (ent, NULL);
1908 ent->think = G_FreeEntity;
1909 ent->nextthink = level.time + 25000;
1910 return;
1911 } else
1912 {
1913 // G_UseTargets (ent, NULL);
1914 ent->think = G_FreeEntity;
1915 ent->nextthink = level.time + 25000;
1916 //ent->s.time = level.time;
1917 //ent->s.time2 = level.time + 2000;
1918 return;
1919 }
1920 } else
1921 {
1922 ent->nextthink = level.time + ( FRAMETIME / 2 );
1923 }
1924
1925 ent->s.frame++;
1926
1927 if ( !( ent->spawnflags & 1 ) ) {
1928 ratio = 2.5;
1929 VectorSubtract( ent->r.currentOrigin, ent->enemy->r.currentOrigin, v );
1930 moveit( ent, vectoyaw( v ), ( ent->delay * ratio * FRAMETIME ) * .001 );
1931 }
1932
1933 }
1934
barrel_smoke(gentity_t * ent)1935 void barrel_smoke( gentity_t *ent ) {
1936 gentity_t *tent;
1937 vec3_t point;
1938
1939 VectorCopy( ent->r.currentOrigin, point );
1940
1941 tent = G_TempEntity( point, EV_SMOKE );
1942 VectorCopy( point, tent->s.origin );
1943 tent->s.time = 4000;
1944 tent->s.time2 = 1000;
1945 tent->s.density = 0;
1946 tent->s.angles2[0] = 8;
1947 tent->s.angles2[1] = 64;
1948 tent->s.angles2[2] = 50;
1949
1950 }
1951
smoker_think(gentity_t * ent)1952 void smoker_think( gentity_t *ent ) {
1953 ent->count--;
1954
1955 if ( !ent->count ) {
1956 G_FreeEntity( ent );
1957 } else
1958 {
1959 barrel_smoke( ent );
1960 ent->nextthink = level.time + FRAMETIME;
1961 }
1962
1963 }
1964
SP_OilSlick(gentity_t * ent)1965 void SP_OilSlick( gentity_t *ent ) {
1966 gentity_t *tent;
1967
1968 if ( ent->target ) {
1969 G_Find( NULL, FOFS( targetname ), ent->target );
1970 }
1971
1972 tent = G_TempEntity( ent->r.currentOrigin, EV_OILSLICK );
1973 VectorCopy( ent->r.currentOrigin, tent->s.origin );
1974 tent->s.angles2[0] = 16;
1975 tent->s.angles2[1] = 48;
1976 tent->s.angles2[2] = 10000;
1977 tent->s.density = ent->s.number;
1978 }
1979
OilParticles_think(gentity_t * ent)1980 void OilParticles_think( gentity_t *ent ) {
1981 gentity_t *tent;
1982 gentity_t *owner;
1983
1984 owner = &g_entities[ent->s.density];
1985
1986 if ( owner && owner->takedamage && ent->count2 > level.time - 5000 ) {
1987 ent->nextthink = ( level.time + FRAMETIME / 2 );
1988
1989 tent = G_TempEntity( ent->r.currentOrigin, EV_OILPARTICLES );
1990 VectorCopy( ent->r.currentOrigin, tent->s.origin );
1991 tent->s.time = ent->count2;
1992 tent->s.density = ent->s.density;
1993 VectorCopy( ent->rotate, tent->s.origin2 );
1994 } else {
1995 G_FreeEntity( ent );
1996 }
1997 }
1998
Delayed_Leak_Think(gentity_t * ent)1999 void Delayed_Leak_Think( gentity_t *ent ) {
2000 vec3_t point;
2001 gentity_t *tent;
2002
2003 VectorCopy( ent->r.currentOrigin, point );
2004
2005 tent = G_TempEntity( point, EV_OILSLICK );
2006 VectorCopy( point, tent->s.origin );
2007
2008 tent->s.angles2[0] = 0;
2009 tent->s.angles2[1] = 0;
2010 tent->s.angles2[2] = 2000;
2011 tent->s.density = ent->count;
2012 }
2013
validOilSlickSpawnPoint(vec3_t point,gentity_t * ent)2014 qboolean validOilSlickSpawnPoint( vec3_t point, gentity_t *ent ) {
2015 trace_t tr;
2016 vec3_t end;
2017 gentity_t *traceEnt;
2018
2019 VectorCopy( point, end );
2020 end[2] -= 9999;
2021
2022 trap_Trace( &tr, point, NULL, NULL, end, ent->s.number, MASK_SHOT );
2023
2024 traceEnt = &g_entities[ tr.entityNum ];
2025
2026 if ( traceEnt && traceEnt->classname ) {
2027 if ( !Q_stricmp( traceEnt->classname, "worldspawn" ) ) {
2028 if ( tr.plane.normal[0] == 0 && tr.plane.normal[1] == 0 && tr.plane.normal[2] == 1 ) {
2029 return qtrue;
2030 }
2031 }
2032 }
2033
2034 return qfalse;
2035
2036 }
2037
SP_OilParticles(gentity_t * ent)2038 void SP_OilParticles( gentity_t *ent ) {
2039 // Note to self quick fix
2040 // need to move this to client
2041 #if 0
2042 gentity_t *OilLeak;
2043 vec3_t point;
2044 vec3_t vec;
2045 vec3_t forward;
2046
2047 OilLeak = G_Spawn();
2048
2049 VectorCopy( ent->r.currentOrigin, point );
2050
2051 point[2] = ent->pos3[2];
2052
2053 VectorSubtract( ent->pos3, point, vec );
2054 vectoangles( vec, vec );
2055 AngleVectors( vec, forward, NULL, NULL );
2056 VectorMA( point, 12, forward, point );
2057
2058 G_SetOrigin( OilLeak, point );
2059
2060 G_SetAngle( OilLeak, ent->r.currentAngles );
2061
2062 VectorCopy( forward, OilLeak->rotate );
2063
2064 OilLeak->think = OilParticles_think;
2065 OilLeak->nextthink = level.time + FRAMETIME;
2066
2067 OilLeak->s.density = ent->s.number;
2068 OilLeak->count2 = level.time;
2069
2070 trap_LinkEntity( OilLeak );
2071 #endif
2072 }
2073
2074
Props_Barrel_Pain(gentity_t * ent,gentity_t * attacker,int damage,vec3_t point)2075 void Props_Barrel_Pain( gentity_t *ent, gentity_t *attacker, int damage, vec3_t point ) {
2076
2077 if ( ent->health <= 0 ) {
2078 return;
2079 }
2080
2081 if ( !( ent->spawnflags & 8 ) ) {
2082 SP_OilSlick( ent );
2083 ent->spawnflags |= 8;
2084 }
2085
2086 ent->count2++;
2087
2088 if ( ent->count2 < 6 ) {
2089 SP_OilParticles( ent );
2090 }
2091
2092 }
2093
OilSlick_remove_think(gentity_t * ent)2094 void OilSlick_remove_think( gentity_t *ent ) {
2095 gentity_t *tent;
2096
2097 tent = G_TempEntity( ent->r.currentOrigin, EV_OILSLICKREMOVE );
2098 tent->s.density = ent->s.density;
2099 }
2100
OilSlick_remove(gentity_t * ent)2101 void OilSlick_remove( gentity_t *ent ) {
2102 gentity_t *remove;
2103
2104 remove = G_Spawn();
2105 remove->s.density = ent->s.number;
2106 remove->think = OilSlick_remove_think;
2107 remove->nextthink = level.time + 1000;
2108 VectorCopy( ent->r.currentOrigin, remove->r.currentOrigin );
2109 trap_LinkEntity( remove );
2110 }
2111
Props_Barrel_Die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)2112 void Props_Barrel_Die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
2113 int quantity;
2114 int type;
2115 vec3_t dir;
2116
2117 if ( ent->spawnflags & 1 ) {
2118 ent->s.eFlags = EF_SMOKINGBLACK;
2119 }
2120
2121 G_UseTargets( ent, NULL );
2122
2123 if ( ent->spawnflags & 4 ) {
2124 OilSlick_remove( ent );
2125 }
2126
2127 ent->health = 100;
2128 propExplosion( ent );
2129 ent->health = 0;
2130
2131 ent->takedamage = qfalse;
2132
2133 AngleVectors( ent->r.currentAngles, dir, NULL, NULL );
2134 dir[2] = 1;
2135
2136 if ( !( ent->spawnflags & 2 ) ) {
2137 fire_flamebarrel( ent, ent->r.currentOrigin, dir );
2138 }
2139
2140 ent->touch = 0;
2141
2142 ent->think = Props_Barrel_Animate;
2143 ent->nextthink = level.time + FRAMETIME;
2144
2145 ent->health = ent->duration;
2146 ent->delay = damage;
2147 ent->enemy = inflictor;
2148
2149 quantity = ent->wait;
2150 type = ent->count;
2151
2152 if ( inflictor ) {
2153 Spawn_Shard( ent, inflictor, quantity, type );
2154 }
2155
2156 Prop_Break_Sound( ent );
2157
2158 trap_UnlinkEntity( ent );
2159
2160 ent->clipmask = 0;
2161 ent->r.contents = 0;
2162 ent->s.eType = ET_GENERAL;
2163
2164 trap_LinkEntity( ent );
2165 }
2166
Props_OilSlickSlippery(gentity_t * ent)2167 void Props_OilSlickSlippery( gentity_t *ent ) {
2168 gentity_t *player;
2169 vec3_t vec, kvel, dir;
2170 float len;
2171
2172 player = AICast_FindEntityForName( "player" );
2173
2174 if ( player ) {
2175 VectorSubtract( player->r.currentOrigin, ent->r.currentOrigin, vec );
2176 len = VectorLength( vec );
2177
2178 if ( len < 64 && player->s.groundEntityNum != -1 ) {
2179 len = VectorLength( player->client->ps.velocity );
2180
2181 if ( len && !( player->client->ps.pm_time ) ) {
2182 VectorSet( dir, fabs( crandom() ), fabs( crandom() ), 0 );
2183 VectorScale( dir, 32, kvel );
2184 VectorAdd( player->client->ps.velocity, kvel, player->client->ps.velocity );
2185
2186 {
2187 int t;
2188
2189 t = 32 * 2;
2190 if ( t < 50 ) {
2191 t = 50;
2192 }
2193 if ( t > 200 ) {
2194 t = 200;
2195 }
2196 player->client->ps.pm_time = t;
2197 player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
2198 }
2199
2200 }
2201
2202 }
2203 }
2204 }
2205
Props_Barrel_Think(gentity_t * self)2206 void Props_Barrel_Think( gentity_t *self ) {
2207 self->active = qfalse;
2208 Props_Chair_Think( self );
2209
2210 if ( self->spawnflags & 8 ) { // there is an oil slick
2211 Props_OilSlickSlippery( self );
2212 }
2213 }
2214
SP_Props_Flamebarrel(gentity_t * ent)2215 void SP_Props_Flamebarrel( gentity_t *ent ) {
2216 int mass;
2217
2218 if ( ent->spawnflags & 4 ) {
2219 ent->s.modelindex = G_ModelIndex( "models/furniture/barrel/barrel_c.md3" );
2220 } else if ( ent->spawnflags & 1 ) {
2221 ent->s.modelindex = G_ModelIndex( "models/furniture/barrel/barrel_d.md3" );
2222 } else {
2223 ent->s.modelindex = G_ModelIndex( "models/furniture/barrel/barrel_b.md3" );
2224 }
2225
2226 ent->delay = 0; // inherits damage value
2227
2228 if ( G_SpawnInt( "mass", "5", &mass ) ) {
2229 ent->wait = mass;
2230 } else {
2231 ent->wait = 10;
2232 }
2233
2234 ent->clipmask = CONTENTS_SOLID;
2235 ent->r.contents = CONTENTS_SOLID;
2236 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
2237 ent->s.eType = ET_MOVER;
2238
2239 ent->isProp = qtrue;
2240 ent->nopickup = qtrue;
2241
2242 VectorSet( ent->r.mins, -13, -13, 0 );
2243 VectorSet( ent->r.maxs, 13, 13, 36 );
2244
2245 G_SetOrigin( ent, ent->s.origin );
2246 G_SetAngle( ent, ent->s.angles );
2247
2248 if ( !ent->health ) {
2249 ent->health = 20;
2250 }
2251
2252 ent->duration = ent->health;
2253
2254 ent->count = 2; // metal shards
2255
2256 ent->think = Props_Barrel_Think;
2257 ent->nextthink = level.time + FRAMETIME;
2258
2259 ent->touch = Props_Barrel_Touch;
2260
2261 ent->die = Props_Barrel_Die;
2262
2263 if ( ent->spawnflags & 4 ) {
2264 ent->pain = Props_Barrel_Pain;
2265 }
2266
2267 ent->takedamage = qtrue;
2268 trap_LinkEntity( ent );
2269 }
2270
2271 /*QUAKED props_crate_64 (.8 .6 .2) (-32 -32 0) (32 32 64)
2272 breakable pushable
2273
2274 health = default = 20
2275 wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )
2276
2277 shard =
2278 shard_glass = 0,
2279 shard_wood = 1,
2280 shard_metal = 2,
2281 shard_ceramic = 3
2282
2283 */
2284
2285 /*QUAKED props_crate_32 (.8 .6 .2) (-16 -16 0) (16 16 32)
2286 breakable pushable
2287
2288 health = default = 20
2289 wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )
2290
2291 shard =
2292 shard_glass = 0,
2293 shard_wood = 1,
2294 shard_metal = 2,
2295 shard_ceramic = 3
2296
2297 */
2298
2299 /*QUAKED props_crate_32x64 (.8 .6 .2) ?
2300 requires an origin brush
2301
2302 breakable NOT pushable
2303
2304 brushmodel only
2305
2306 health = default = 20
2307 wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )
2308
2309 shard =
2310 shard_glass = 0,
2311 shard_wood = 1,
2312 shard_metal = 2,
2313 shard_ceramic = 3
2314
2315 */
2316
touch_crate_64(gentity_t * self,gentity_t * other,trace_t * trace)2317 void touch_crate_64( gentity_t *self, gentity_t *other, trace_t *trace ) {
2318 float ratio;
2319 vec3_t v;
2320
2321 if ( other->r.currentOrigin[2] > ( self->r.currentOrigin[2] + 10 + 31 ) ) {
2322 return;
2323 }
2324
2325 ratio = 1.5;
2326 VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, v );
2327 moveit( self, vectoyaw( v ), ( 20 * ratio * FRAMETIME ) * .001 );
2328 }
2329
crate_animate(gentity_t * ent)2330 void crate_animate( gentity_t *ent ) {
2331 if ( ent->s.frame == 17 ) {
2332 G_UseTargets( ent, NULL );
2333 ent->think = G_FreeEntity;
2334 ent->nextthink = level.time + 2000;
2335 ent->s.time = level.time;
2336 ent->s.time2 = level.time + 2000;
2337 return;
2338 }
2339
2340 ent->s.frame++;
2341 ent->nextthink = level.time + ( FRAMETIME / 2 );
2342 }
2343
crate_die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)2344 void crate_die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
2345 int quantity;
2346 int type;
2347
2348 quantity = ent->wait;
2349 type = ent->count;
2350
2351 Spawn_Shard( ent, inflictor, quantity, type );
2352
2353 ent->takedamage = qfalse;
2354 ent->think = crate_animate;
2355 ent->nextthink = level.time + FRAMETIME;
2356 ent->touch = 0;
2357
2358 trap_UnlinkEntity( ent );
2359
2360 ent->clipmask = 0;
2361 ent->r.contents = 0;
2362 ent->s.eType = ET_GENERAL;
2363
2364 trap_LinkEntity( ent );
2365
2366 }
2367
SP_crate_64(gentity_t * self)2368 void SP_crate_64( gentity_t *self ) {
2369 self->s.modelindex = G_ModelIndex( "models/furniture/crate/crate64.md3" );
2370
2371 self->clipmask = CONTENTS_SOLID;
2372 self->r.contents = CONTENTS_SOLID;
2373 self->r.svFlags = SVF_USE_CURRENT_ORIGIN;
2374
2375 VectorSet( self->r.mins, -32, -32, 0 );
2376 VectorSet( self->r.maxs, 32, 32, 64 );
2377
2378 self->s.eType = ET_MOVER;
2379
2380 self->isProp = qtrue;
2381 self->nopickup = qtrue;
2382 G_SetOrigin( self, self->s.origin );
2383 G_SetAngle( self, self->s.angles );
2384
2385 self->touch = touch_crate_64;
2386 self->die = crate_die;
2387
2388 self->takedamage = qtrue;
2389
2390 if ( !self->health ) {
2391 self->health = 20;
2392 }
2393
2394 if ( !self->count ) {
2395 self->count = 1;
2396 }
2397
2398 if ( !self->wait ) {
2399 self->wait = 10;
2400 }
2401
2402 self->isProp = qtrue;
2403 self->nopickup = qtrue;
2404
2405 trap_LinkEntity( self );
2406
2407 self->think = DropToFloor;
2408 self->nextthink = level.time + FRAMETIME;
2409 }
2410
SP_crate_32(gentity_t * self)2411 void SP_crate_32( gentity_t *self ) {
2412 self->s.modelindex = G_ModelIndex( "models/furniture/crate/crate32.md3" );
2413
2414 self->clipmask = CONTENTS_SOLID;
2415 self->r.contents = CONTENTS_SOLID;
2416 self->r.svFlags = SVF_USE_CURRENT_ORIGIN;
2417
2418 VectorSet( self->r.mins, -16, -16, 0 );
2419 VectorSet( self->r.maxs, 16, 16, 32 );
2420
2421 self->s.eType = ET_MOVER;
2422
2423 self->isProp = qtrue;
2424 self->nopickup = qtrue;
2425 G_SetOrigin( self, self->s.origin );
2426 G_SetAngle( self, self->s.angles );
2427
2428 self->touch = touch_crate_64;
2429 self->die = crate_die;
2430
2431 self->takedamage = qtrue;
2432
2433 if ( !self->health ) {
2434 self->health = 20;
2435 }
2436
2437 if ( !self->count ) {
2438 self->count = 1;
2439 }
2440
2441 if ( !self->wait ) {
2442 self->wait = 10;
2443 }
2444
2445 self->isProp = qtrue;
2446 self->nopickup = qtrue;
2447
2448 trap_LinkEntity( self );
2449
2450 self->think = DropToFloor;
2451 self->nextthink = level.time + FRAMETIME;
2452 }
2453
2454 //////////////////////////////////////////////
2455
props_crate32x64_think(gentity_t * ent)2456 void props_crate32x64_think( gentity_t *ent ) {
2457 ent->s.frame++;
2458
2459 if ( ent->s.frame < 17 ) {
2460 ent->nextthink = level.time + ( FRAMETIME / 2 );
2461 } else
2462 {
2463 ent->clipmask = 0;
2464 ent->r.contents = 0;
2465 ent->takedamage = qfalse;
2466
2467 G_UseTargets( ent, NULL );
2468 }
2469
2470 }
2471
props_crate32x64_die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)2472 void props_crate32x64_die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
2473 ent->think = props_crate32x64_think;
2474 ent->nextthink = level.time + FRAMETIME;
2475 }
2476
SP_Props_Crate32x64(gentity_t * ent)2477 void SP_Props_Crate32x64( gentity_t *ent ) {
2478
2479 trap_SetBrushModel( ent, ent->model );
2480
2481 InitProp( ent );
2482
2483 if ( !ent->health ) {
2484 ent->health = 10;
2485 }
2486
2487 ent->takedamage = qtrue;
2488
2489 ent->clipmask = CONTENTS_SOLID;
2490
2491 ent->die = props_crate32x64_die;
2492
2493 trap_LinkEntity( ent );
2494 }
2495
2496 /*QUAKED props_flippy_table (.8 .6 .2) ? - - X_AXIS Y_AXIS LEADER
2497 this entity will need a leader and an origin brush
2498 !!!!!!!!!!!!!!
2499 just a reminder to put the origin brush in the proper location for the leader and the
2500 slave so that the table will flip over correctly.
2501 */
2502
flippy_table_use(gentity_t * ent,gentity_t * other,gentity_t * activator)2503 void flippy_table_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
2504 qboolean is_infront;
2505 gentity_t *slave;
2506
2507 // it would be odd to flip a table if your standing on it
2508 if ( other && other->s.groundEntityNum == ent->s.number ) {
2509 // G_Printf ("can't push table over while standing on it\n");
2510 return;
2511 }
2512
2513 ent->use = 0;
2514
2515 is_infront = infront( ent, other );
2516
2517 if ( is_infront ) {
2518 // need to swap the team leader with the slave
2519 for ( slave = ent ; slave ; slave = slave->teamchain )
2520 {
2521 if ( slave == ent ) {
2522 continue;
2523 }
2524
2525 slave->s.pos.trType = ent->s.pos.trType;
2526 slave->s.pos.trTime = ent->s.pos.trTime;
2527 slave->s.pos.trDuration = ent->s.pos.trDuration;
2528 VectorCopy( ent->s.pos.trBase, slave->s.pos.trBase );
2529 VectorCopy( ent->s.pos.trDelta, slave->s.pos.trDelta );
2530
2531 slave->s.apos.trType = ent->s.apos.trType;
2532 slave->s.apos.trTime = ent->s.apos.trTime;
2533 slave->s.apos.trDuration = ent->s.apos.trDuration;
2534 VectorCopy( ent->s.apos.trBase, slave->s.apos.trBase );
2535 VectorCopy( ent->s.apos.trDelta, slave->s.apos.trDelta );
2536
2537 slave->think = ent->think;
2538 slave->nextthink = ent->nextthink;
2539
2540 VectorCopy( ent->pos1, slave->pos1 );
2541 VectorCopy( ent->pos2, slave->pos2 );
2542
2543 slave->speed = ent->speed;
2544
2545 slave->flags &= ~FL_TEAMSLAVE;
2546 // make it visible
2547 trap_LinkEntity( slave );
2548
2549 Use_BinaryMover( slave, other, other );
2550 }
2551
2552 trap_UnlinkEntity( ent );
2553 } else {
2554 Use_BinaryMover( ent, other, other );
2555 }
2556
2557 }
2558
flippy_table_animate(gentity_t * ent)2559 void flippy_table_animate( gentity_t *ent ) {
2560 #if 0
2561 if ( ent->s.frame == 9 ) {
2562 G_UseTargets( ent, NULL );
2563 ent->think = G_FreeEntity;
2564 ent->nextthink = level.time + 2000;
2565 } else
2566 {
2567 ent->s.frame++;
2568 ent->nextthink = level.time + ( FRAMETIME / 2 );
2569 }
2570 #endif
2571 }
2572
props_flippy_table_die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)2573 void props_flippy_table_die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
2574 ent->think = flippy_table_animate;
2575 ent->nextthink = level.time + FRAMETIME;
2576
2577 ent->takedamage = qfalse;
2578
2579 G_UseTargets( ent, NULL );
2580 }
2581
props_flippy_blocked(gentity_t * ent,gentity_t * other)2582 void props_flippy_blocked( gentity_t *ent, gentity_t *other ) {
2583 vec3_t velocity;
2584 vec3_t angles;
2585 vec3_t kvel;
2586
2587 // just for now
2588 float angle = ent->r.currentAngles[YAW];
2589
2590 if ( other->client ) {
2591 // shoot the player off of it
2592 VectorCopy( ent->s.apos.trBase, angles );
2593 angles[YAW] += angle;
2594 angles[PITCH] = 0; // always forward
2595
2596 AngleVectors( angles, velocity, NULL, NULL );
2597 VectorScale( velocity, 24, velocity );
2598 velocity[2] += 100 + crandom() * 50;
2599
2600 VectorScale( velocity, 32, kvel );
2601 VectorAdd( other->client->ps.velocity, kvel, other->client->ps.velocity );
2602 } else if ( other->s.eType == ET_ITEM ) {
2603 VectorCopy( ent->s.apos.trBase, angles );
2604 angles[YAW] += angle;
2605 angles[PITCH] = 0; // always forward
2606
2607 AngleVectors( angles, velocity, NULL, NULL );
2608 VectorScale( velocity, 150, velocity );
2609 velocity[2] += 300 + crandom() * 50;
2610
2611 VectorScale( velocity, 8, kvel );
2612 other->s.pos.trType = TR_GRAVITY;
2613 other->s.pos.trTime = level.time;
2614 VectorCopy( kvel, other->s.pos.trDelta );
2615
2616 other->s.eFlags |= EF_BOUNCE;
2617 } else
2618 {
2619 // just delete it or destroy it
2620 G_TempEntity( other->s.origin, EV_ITEM_POP );
2621 G_FreeEntity( other );
2622 return;
2623 }
2624 }
2625
SP_Props_Flipping_Table(gentity_t * ent)2626 void SP_Props_Flipping_Table( gentity_t *ent ) {
2627
2628 if ( !ent->model ) {
2629 G_Printf( S_COLOR_RED "props_Flipping_Table with NULL model\n" );
2630 return;
2631 }
2632
2633 trap_SetBrushModel( ent, ent->model );
2634
2635 ent->speed = 500;
2636 ent->angle = 90;
2637
2638 // ent->spawnflags |= 8;
2639 if ( !( ent->spawnflags & 4 ) && !( ent->spawnflags & 8 ) ) {
2640 G_Printf( "you forgot to select the X or Y Axis\n" );
2641 }
2642
2643 VectorClear( ent->rotate );
2644
2645 if ( ent->spawnflags & 4 ) {
2646 ent->rotate[2] = 1;
2647 } else if ( ent->spawnflags & 8 ) {
2648 ent->rotate[0] = 1;
2649 } else { ent->rotate[1] = 1;}
2650
2651 ent->spawnflags |= 64; // stay open
2652
2653 InitMoverRotate( ent );
2654
2655 VectorCopy( ent->s.origin, ent->s.pos.trBase );
2656 VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
2657 VectorCopy( ent->s.apos.trBase, ent->r.currentAngles );
2658
2659 ent->blocked = props_flippy_blocked;
2660
2661 if ( !ent->health ) {
2662 ent->health = 100;
2663 }
2664
2665 ent->wait *= 1000;
2666
2667 //ent->takedamage = qtrue;
2668
2669 //ent->die = props_flippy_table_die;
2670
2671 ent->use = flippy_table_use;
2672
2673 trap_LinkEntity( ent );
2674
2675 }
2676
2677
2678 /*QUAKED props_58x112tablew (.8 .6 .2) ?
2679 dimensions are 58 x 112 x 32 (x,y,z)
2680
2681 requires an origin brush
2682
2683 breakable NOT pushable
2684
2685 brushmodel only
2686
2687 health = default = 10
2688 wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )
2689
2690 shard =
2691 shard_glass = 0,
2692 shard_wood = 1,
2693 shard_metal = 2,
2694 shard_ceramic = 3
2695
2696 */
2697
props_58x112tablew_think(gentity_t * ent)2698 void props_58x112tablew_think( gentity_t *ent ) {
2699 ent->s.frame++;
2700
2701 if ( ent->s.frame < 16 ) {
2702 ent->nextthink = level.time + ( FRAMETIME / 2 );
2703 } else
2704 {
2705
2706 ent->clipmask = 0;
2707 ent->r.contents = 0;
2708
2709 G_UseTargets( ent, NULL );
2710 }
2711
2712 }
2713
props_58x112tablew_die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)2714 void props_58x112tablew_die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
2715 ent->think = props_58x112tablew_think;
2716 ent->nextthink = level.time + FRAMETIME;
2717 ent->takedamage = qfalse;
2718 }
2719
SP_Props_58x112tablew(gentity_t * ent)2720 void SP_Props_58x112tablew( gentity_t *ent ) {
2721
2722 trap_SetBrushModel( ent, ent->model );
2723
2724 InitProp( ent );
2725
2726 if ( !ent->health ) {
2727 ent->health = 10;
2728 }
2729
2730 ent->takedamage = qtrue;
2731
2732 ent->clipmask = CONTENTS_SOLID;
2733
2734 ent->die = props_58x112tablew_die;
2735
2736 trap_LinkEntity( ent );
2737 }
2738
2739 /*QUAKED props_castlebed (.8 .6 .2) ?
2740 dimensions are 112 x 128 x 80 (x,y,z)
2741
2742 requires an origin brush
2743
2744 breakable NOT pushable
2745
2746 brushmodel only
2747
2748 health = default = 20
2749 wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )
2750
2751 shard =
2752 shard_glass = 0,
2753 shard_wood = 1,
2754 shard_metal = 2,
2755 shard_ceramic = 3
2756
2757 */
2758
props_castlebed_touch(gentity_t * ent,gentity_t * other,trace_t * trace)2759 void props_castlebed_touch( gentity_t *ent, gentity_t *other, trace_t *trace ) {
2760 if ( !other->client ) {
2761 return;
2762 }
2763
2764 if ( other->client->ps.pm_flags & PMF_JUMP_HELD
2765 && other->s.groundEntityNum == ent->s.number
2766 && !other->client->ps.pm_time ) {
2767 G_Damage( ent, other, other, NULL, NULL, 1, 0, MOD_CRUSH );
2768
2769 // TDB: need sound of bed springs for this
2770 G_Printf( "SOUND sqweeky\n" );
2771
2772 other->client->ps.velocity[2] += 250;
2773
2774 other->client->ps.pm_time = 250;
2775 other->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
2776 }
2777
2778 }
2779
props_castlebed_animate(gentity_t * ent)2780 void props_castlebed_animate( gentity_t *ent ) {
2781 ent->s.frame++;
2782
2783 if ( ent->s.frame < 8 ) {
2784 ent->nextthink = level.time + ( FRAMETIME / 2 );
2785 } else
2786 {
2787 ent->clipmask = 0;
2788 ent->r.contents = 0;
2789 G_UseTargets( ent, NULL );
2790 }
2791 }
2792
props_castlebed_die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)2793 void props_castlebed_die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
2794 ent->think = props_castlebed_animate;
2795 ent->nextthink = level.time + FRAMETIME;
2796 ent->touch = 0;
2797 ent->takedamage = qfalse;
2798
2799 ent->count = shard_wood;
2800 Prop_Break_Sound( ent );
2801 }
2802
SP_props_castlebed(gentity_t * ent)2803 void SP_props_castlebed( gentity_t *ent ) {
2804 trap_SetBrushModel( ent, ent->model );
2805
2806 InitProp( ent );
2807
2808 if ( !ent->health ) {
2809 ent->health = 20;
2810 }
2811
2812 ent->takedamage = qtrue;
2813
2814 ent->clipmask = CONTENTS_SOLID;
2815
2816 ent->die = props_castlebed_die;
2817 ent->touch = props_castlebed_touch;
2818
2819 trap_LinkEntity( ent );
2820 }
2821
2822 /*QUAKED props_snowGenerator (3 2 7) ? TOGGLE_ON ALWAYS_ON
2823 entity brush need to be targeted to an info notnull this
2824 will determine the direction the snow particles will travel.
2825
2826 speed
2827 gravity
2828 turb
2829
2830 count is the number of snowflurries 3 to 5 would be a good number
2831
2832 duration is how long the effect will last 1 is 1 second
2833 */
2834
props_snowGenerator_think(gentity_t * ent)2835 void props_snowGenerator_think( gentity_t *ent ) {
2836 gentity_t *tent;
2837 float high, wide, deep;
2838 int i;
2839 vec3_t point;
2840
2841 if ( !( ent->spawnflags & 1 ) ) {
2842 return;
2843 }
2844
2845 high = ent->r.maxs[2] - ent->r.mins[2];
2846 wide = ent->r.maxs[1] - ent->r.mins[1];
2847 deep = ent->r.maxs[0] - ent->r.mins[0];
2848
2849 for ( i = 0; i < ent->count; i++ )
2850 {
2851 VectorCopy( ent->pos1, point );
2852
2853 // we need to randomize to the extent of the brush
2854 point[0] += crandom() * ( deep * 0.5 );
2855 point[1] += crandom() * ( wide * 0.5 );
2856 point[2] += crandom() * ( high * 0.5 );
2857
2858 tent = G_TempEntity( point, EV_SNOWFLURRY );
2859 VectorCopy( point, tent->s.origin );
2860 VectorCopy( ent->movedir, tent->s.angles );
2861 tent->s.time = 2000; // life time
2862 tent->s.time2 = 1000; // alpha fade start
2863 }
2864
2865 if ( ent->spawnflags & 2 ) {
2866 ent->nextthink = level.time + FRAMETIME;
2867 } else if ( ent->wait < level.time ) {
2868 ent->nextthink = level.time + FRAMETIME;
2869 }
2870 }
2871
props_snowGenerator_use(gentity_t * ent,gentity_t * other,gentity_t * activator)2872 void props_snowGenerator_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
2873 if ( !( ent->spawnflags & 1 ) ) {
2874 ent->spawnflags |= 1;
2875 ent->think = props_snowGenerator_think;
2876 ent->nextthink = level.time + FRAMETIME;
2877 ent->wait = level.time + ent->duration;
2878 } else {
2879 ent->spawnflags &= ~1;
2880 }
2881 }
2882
SP_props_snowGenerator(gentity_t * ent)2883 void SP_props_snowGenerator( gentity_t *ent ) {
2884 vec3_t center;
2885 gentity_t *target = NULL;
2886
2887 trap_SetBrushModel( ent, ent->model );
2888
2889 VectorAdd( ent->r.absmin, ent->r.absmax, center );
2890 VectorScale( center, 0.5, center );
2891
2892 VectorCopy( center, ent->pos1 );
2893
2894 if ( !ent->target ) {
2895 G_Printf( "snowGenerator at loc %s does not have a target\n", vtos( center ) );
2896 return;
2897 } else
2898 {
2899 target = G_Find( target, FOFS( targetname ), ent->target );
2900 if ( !target ) {
2901 G_Printf( "error snowGenerator at loc %s does cant find target %s\n", vtos( center ), ent->target );
2902 return;
2903 }
2904
2905 VectorSubtract( target->s.origin, ent->s.origin, ent->movedir );
2906 VectorNormalize( ent->movedir );
2907 }
2908
2909 ent->r.contents = CONTENTS_TRIGGER;
2910 ent->r.svFlags = SVF_NOCLIENT;
2911
2912 if ( ent->spawnflags & 1 || ent->spawnflags & 2 ) {
2913 ent->think = props_snowGenerator_think;
2914 ent->nextthink = level.time + FRAMETIME;
2915
2916 if ( ent->spawnflags & 2 ) {
2917 ent->spawnflags |= 1;
2918 }
2919 }
2920
2921 ent->use = props_snowGenerator_use;
2922
2923 if ( !( ent->delay ) ) {
2924 ent->delay = 100;
2925 } else {
2926 ent->delay *= 100;
2927 }
2928
2929 if ( !( ent->count ) ) {
2930 ent->count = 32;
2931 }
2932
2933 if ( !( ent->duration ) ) {
2934 ent->duration = 1;
2935 }
2936
2937 ent->duration *= 1000;
2938
2939 trap_LinkEntity( ent );
2940 }
2941
2942 /////////////////////////////
2943 // FIRES AND EXPLOSION PROPS
2944 /////////////////////////////
2945
2946 /*QUAKED props_FireColumn (.3 .2 .7) (-8 -8 -8) (8 8 8) CORKSCREW SMOKE GRAVITY HALFGRAVITY
2947 this entity will require a target use an infonotnull to specifiy its direction
2948
2949 defaults:
2950 will leave a flaming trail by default
2951 will not be affected by gravity
2952
2953 radius = distance flame will corkscrew from origin
2954 speed = default is 900
2955 duration = default is 3 sec
2956
2957 start_size = default is 5
2958 end_size = defaults 7 thru 17
2959 count = defaults 100 thru 500
2960
2961 Pending:
2962 delay before it happens again use trigger_relay for now
2963 assign a model
2964 */
2965
propsFireColumnUse(gentity_t * ent,gentity_t * other,gentity_t * activator)2966 void propsFireColumnUse( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
2967 gentity_t *tent;
2968 vec3_t start, dir;
2969
2970 VectorCopy( ent->s.origin, start );
2971
2972 AngleVectors( ent->r.currentAngles, dir, NULL, NULL );
2973
2974 tent = fire_flamebarrel( ent, start, dir );
2975
2976 if ( !tent ) {
2977 return;
2978 }
2979
2980 if ( ent->spawnflags & 2 ) {
2981 tent->s.eType = ET_FIRE_COLUMN_SMOKE;
2982 } else {
2983 tent->s.eType = ET_FIRE_COLUMN;
2984 }
2985
2986 if ( ent->spawnflags & 4 ) {
2987 tent->s.pos.trType = TR_GRAVITY;
2988 } else if ( ent->spawnflags & 8 ) {
2989 tent->s.pos.trType = TR_GRAVITY_LOW;
2990 } else {
2991 tent->s.pos.trType = TR_LINEAR;
2992 }
2993
2994 if ( ent->spawnflags & 1 ) {
2995 tent->s.density = ent->radius; // corkscrew effect
2996 }
2997
2998 tent->flags |= FL_NODRAW;
2999 //tent->s.eFlags |= EF_NODRAW;
3000
3001 // TBD
3002 // lifetime
3003 if ( ent->duration ) {
3004 tent->nextthink = level.time + ent->duration;
3005 }
3006
3007 // speed
3008 if ( ent->speed ) {
3009 VectorClear( tent->s.pos.trDelta );
3010 VectorScale( dir, ent->speed + ( crandom() * 100 ), tent->s.pos.trDelta );
3011 SnapVector( tent->s.pos.trDelta );
3012 VectorCopy( start, tent->r.currentOrigin );
3013 }
3014
3015 if ( ent->start_size ) {
3016 tent->s.angles[1] = ent->start_size;
3017 }
3018
3019 if ( ent->end_size ) {
3020 tent->s.angles[2] = ent->end_size;
3021 }
3022
3023 if ( ent->count ) {
3024 tent->s.angles[0] = ent->count;
3025 }
3026
3027 G_SetAngle( tent, ent->r.currentAngles );
3028 }
3029
propsFireColumnInit(gentity_t * ent)3030 void propsFireColumnInit( gentity_t *ent ) {
3031 gentity_t *target;
3032 vec3_t vec;
3033 vec3_t angles;
3034
3035 if ( ent->target ) {
3036 target = G_Find( NULL, FOFS( targetname ), ent->target );
3037 VectorSubtract( target->s.origin, ent->s.origin, vec );
3038 vectoangles( vec, angles );
3039 G_SetAngle( ent, angles );
3040 } else
3041 {
3042 // ok then just up
3043 VectorSet( vec, 0, 0, 1 );
3044 vectoangles( vec, angles );
3045 G_SetAngle( ent, angles );
3046 }
3047
3048 if ( ent->duration ) {
3049 ent->duration = ent->duration * 1000;
3050 }
3051
3052
3053 }
3054
SP_propsFireColumn(gentity_t * ent)3055 void SP_propsFireColumn( gentity_t *ent ) {
3056 G_SetOrigin( ent, ent->s.origin );
3057 ent->think = propsFireColumnInit;
3058 ent->nextthink = level.time + FRAMETIME;
3059 ent->use = propsFireColumnUse;
3060 trap_LinkEntity( ent );
3061 }
3062
3063 /*QUAKED props_ExploPart (.3 .5 .7) (-8 -8 -16) (8 8 16)
3064 "model" will load a discreet model
3065 "noise" will load looping sound for the model
3066 "target" point to an infonotnull to specify dir default will be up
3067 "speed" default to 900
3068
3069 "type" wood concrete or stone
3070 "count"in the absense of a model count will determine the piece to spawn
3071 for wood:
3072 it can be one of the following 64 48 32 24 16 8
3073 for concrete:
3074 for stone:
3075 */
3076 #define EXPLOPARTPIECES 8
3077
props_ExploPartUse(gentity_t * ent,gentity_t * other,gentity_t * activator)3078 void props_ExploPartUse( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
3079 // int i, numpieces;
3080 gentity_t *part;
3081 vec3_t start, dir;
3082
3083 VectorCopy( ent->s.origin, start );
3084 AngleVectors( ent->r.currentAngles, dir, NULL, NULL );
3085
3086 if ( ent->s.modelindex ) {
3087 part = fire_flamebarrel( ent, start, dir );
3088 part->s.modelindex = ent->s.modelindex;
3089 } else
3090 {
3091 G_Printf( "props_ExploPartUse has not been assigned a model\n" );
3092 return;
3093 }
3094
3095 if ( part ) {
3096 part->s.pos.trType = TR_GRAVITY;
3097 part->s.eType = ET_EXPLO_PART;
3098
3099 G_SetAngle( part, ent->r.currentAngles );
3100
3101 if ( ent->speed ) {
3102 VectorClear( part->s.pos.trDelta );
3103 VectorScale( dir, ent->speed + ( crandom() * 100 ), part->s.pos.trDelta );
3104 SnapVector( part->s.pos.trDelta );
3105 VectorCopy( start, part->r.currentOrigin );
3106 }
3107 }
3108
3109 G_UseTargets( ent, NULL );
3110 }
3111
props_ExploPartInit(gentity_t * ent)3112 void props_ExploPartInit( gentity_t *ent ) {
3113 gentity_t *target;
3114 vec3_t vec, angles;
3115
3116 if ( ent->target ) {
3117 target = G_Find( NULL, FOFS( targetname ), ent->target );
3118 VectorSubtract( target->s.origin, ent->s.origin, vec );
3119 vectoangles( vec, angles );
3120 G_SetAngle( ent, angles );
3121 } else
3122 {
3123 // ok then just up
3124 VectorSet( vec, 0, 0, 1 );
3125 vectoangles( vec, angles );
3126 G_SetAngle( ent, angles );
3127 }
3128 }
3129
SP_props_ExploPart(gentity_t * ent)3130 void SP_props_ExploPart( gentity_t *ent ) {
3131 char *sound;
3132 char *type;
3133 // float bbox;
3134
3135 if ( ent->model ) {
3136 ent->s.modelindex = G_ModelIndex( ent->model );
3137 }
3138
3139 G_SpawnString( "type", "wood", &type );
3140
3141 if ( !Q_stricmp( type,"wood" ) ) {
3142 if ( ent->count == 64 ) {
3143 ent->s.modelindex = G_ModelIndex( "models/shards/2x4a.md3" );
3144 } else if ( ent->count == 48 ) {
3145 ent->s.modelindex = G_ModelIndex( "models/shards/2x4b.md3" );
3146 } else if ( ent->count == 32 ) {
3147 ent->s.modelindex = G_ModelIndex( "models/shards/2x4c.md3" );
3148 } else if ( ent->count == 24 ) {
3149 ent->s.modelindex = G_ModelIndex( "models/shards/2x4d.md3" );
3150 } else if ( ent->count == 16 ) {
3151 ent->s.modelindex = G_ModelIndex( "models/shards/2x4e.md3" );
3152 } else if ( ent->count == 8 ) {
3153 ent->s.modelindex = G_ModelIndex( "models/shards/2x4f.md3" );
3154 }
3155 } else if ( !Q_stricmp( type,"concrete" ) ) {
3156 } else if ( !Q_stricmp( type,"stone" ) ) {
3157 }
3158
3159 if ( G_SpawnString( "noise", "100", &sound ) ) {
3160 ent->s.loopSound = G_SoundIndex( sound );
3161 }
3162
3163 ent->think = props_ExploPartInit;
3164 ent->nextthink = level.time + FRAMETIME;
3165
3166 ent->use = props_ExploPartUse;
3167 }
3168
3169 /*QUAKED props_decoration (.6 .7 .7) (-8 -8 0) (8 8 16) STARTINVIS DEBRIS ANIMATE KEEPBLOCK TOUCHACTIVATE LOOPING STARTON
3170 "model2" will specify the model to load
3171
3172 "noise" the looping sound entity is to make
3173
3174 "type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
3175 "count" how much debris ei. default 4 pieces
3176
3177 you will need to specify the bounding box for the entity
3178 "high" default is 4
3179 "wide" default is 4
3180
3181 "frames" how many frames of animation to play
3182 "loop" when the animation is done start again on this frame
3183 "startonframe" on what frame do you want to start the animation
3184 */
3185
props_decoration_animate(gentity_t * ent)3186 void props_decoration_animate( gentity_t *ent ) {
3187
3188 ent->s.frame++;
3189 ent->s.eType = ET_GENERAL;
3190
3191 if ( ent->s.frame > ent->count2 ) {
3192 if ( ent->spawnflags & 32 || ent->spawnflags & 64 ) {
3193 ent->s.frame = ent->props_frame_state;
3194
3195 if ( !( ent->spawnflags & 64 ) ) {
3196 ent->takedamage = qfalse;
3197 }
3198 } else
3199 {
3200 ent->s.frame = ent->count2;
3201 ent->takedamage = qfalse;
3202
3203 return;
3204 }
3205 }
3206
3207 ent->nextthink = level.time + 50;
3208 }
3209
props_decoration_death(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)3210 void props_decoration_death( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
3211 if ( !( ent->spawnflags & 8 ) ) {
3212 ent->clipmask = 0;
3213 ent->r.contents = 0;
3214 ent->s.eType = ET_GENERAL;
3215 trap_LinkEntity( ent );
3216 }
3217
3218 ent->takedamage = qfalse;
3219
3220 G_UseTargets( ent, NULL );
3221
3222 if ( ent->spawnflags & 2 ) {
3223 Spawn_Shard( ent, inflictor, ent->count, ent->key );
3224 }
3225
3226 if ( ent->spawnflags & 4 ) {
3227 ent->nextthink = level.time + 50;
3228 ent->think = props_decoration_animate;
3229 return;
3230 }
3231
3232 G_FreeEntity( ent );
3233
3234 }
3235
Use_props_decoration(gentity_t * ent,gentity_t * self,gentity_t * activator)3236 void Use_props_decoration( gentity_t *ent, gentity_t *self, gentity_t *activator ) {
3237 if ( ent->spawnflags & 1 ) {
3238 trap_LinkEntity( ent );
3239 ent->spawnflags &= ~1;
3240 } else if ( ent->spawnflags & 4 ) {
3241 ent->nextthink = level.time + 50;
3242 ent->think = props_decoration_animate;
3243 } else
3244 {
3245 trap_UnlinkEntity( ent );
3246 ent->spawnflags |= 1;
3247 }
3248
3249 }
3250
props_touch(gentity_t * self,gentity_t * other,trace_t * trace)3251 void props_touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
3252 if ( self->spawnflags & 16 ) {
3253 props_decoration_death( self, other, other, 9999, MOD_CRUSH );
3254 }
3255 }
3256
SP_props_decoration(gentity_t * ent)3257 void SP_props_decoration( gentity_t *ent ) {
3258 float light;
3259 vec3_t color;
3260 qboolean lightSet, colorSet;
3261 char *sound;
3262 char *type;
3263 char *high;
3264 char *wide;
3265 char *frames;
3266 float height;
3267 float width;
3268 float num_frames;
3269
3270 char *loop;
3271
3272 char *startonframe;
3273
3274 if ( G_SpawnString( "startonframe", "0", &startonframe ) ) {
3275 ent->s.frame = atoi( startonframe );
3276 }
3277
3278 if ( ent->model2 ) {
3279 ent->s.modelindex = G_ModelIndex( ent->model2 );
3280 }
3281
3282 if ( G_SpawnString( "noise", "100", &sound ) ) {
3283 ent->s.loopSound = G_SoundIndex( sound );
3284 }
3285
3286 if ( ( ent->spawnflags & 32 ) && G_SpawnString( "loop", "100", &loop ) ) {
3287 ent->props_frame_state = atoi( loop );
3288 }
3289
3290 // if the "color" or "light" keys are set, setup constantLight
3291 lightSet = G_SpawnFloat( "light", "100", &light );
3292 colorSet = G_SpawnVector( "color", "1 1 1", color );
3293 if ( lightSet || colorSet ) {
3294 int r, g, b, i;
3295
3296 r = color[0] * 255;
3297 if ( r > 255 ) {
3298 r = 255;
3299 }
3300 g = color[1] * 255;
3301 if ( g > 255 ) {
3302 g = 255;
3303 }
3304 b = color[2] * 255;
3305 if ( b > 255 ) {
3306 b = 255;
3307 }
3308 i = light / 4;
3309 if ( i > 255 ) {
3310 i = 255;
3311 }
3312 ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 );
3313 }
3314
3315 if ( ent->health ) {
3316 ent->isProp = qtrue;
3317 ent->takedamage = qtrue;
3318 ent->die = props_decoration_death;
3319
3320 G_SpawnString( "type", "wood", &type );
3321 if ( !Q_stricmp( type,"wood" ) ) {
3322 ent->key = 1;
3323 } else if ( !Q_stricmp( type,"glass" ) ) {
3324 ent->key = 0;
3325 } else if ( !Q_stricmp( type,"metal" ) ) {
3326 ent->key = 2;
3327 } else if ( !Q_stricmp( type,"ceramic" ) ) {
3328 ent->key = 3;
3329 } else if ( !Q_stricmp( type, "rubble" ) ) {
3330 ent->key = 4;
3331 }
3332
3333 G_SpawnString( "high", "0", &high );
3334 height = atof( high );
3335
3336 if ( !height ) {
3337 height = 4;
3338 }
3339
3340 G_SpawnString( "wide", "0", &wide );
3341 width = atof( wide );
3342
3343 if ( !width ) {
3344 width = 4;
3345 }
3346
3347 width /= 2;
3348
3349 if ( Q_stricmp( ent->classname, "props_decorBRUSH" ) ) {
3350 VectorSet( ent->r.mins, -width, -width, 0 );
3351 VectorSet( ent->r.maxs, width, width, height );
3352 }
3353
3354 ent->clipmask = CONTENTS_SOLID;
3355 ent->r.contents = CONTENTS_SOLID;
3356 ent->s.eType = ET_MOVER;
3357
3358 G_SpawnString( "frames", "0", &frames );
3359 num_frames = atof( frames );
3360
3361 ent->count2 = num_frames;
3362
3363 if ( ent->targetname ) {
3364 ent->use = Use_props_decoration;
3365 }
3366
3367 ent->touch = props_touch;
3368
3369 } else if ( !( ent->health ) && ent->spawnflags & 4 ) {
3370 G_SpawnString( "frames", "0", &frames );
3371 num_frames = atof( frames );
3372
3373 ent->count2 = num_frames;
3374 ent->use = Use_props_decoration;
3375 }
3376
3377 if ( ent->spawnflags & 64 ) {
3378 ent->nextthink = level.time + 50;
3379 ent->think = props_decoration_animate;
3380 }
3381
3382 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
3383
3384 G_SetOrigin( ent, ent->s.origin );
3385 G_SetAngle( ent, ent->s.angles );
3386
3387 if ( !( ent->spawnflags & 1 ) ) {
3388 trap_LinkEntity( ent );
3389 } else {
3390 ent->use = Use_props_decoration;
3391 }
3392
3393 }
3394
3395 /*QUAKED props_decorBRUSH (.6 .7 .7) ? STARTINVIS DEBRIS ANIMATE KEEPBLOCK - LOOPING STARTON
3396 ANIMATE animate on death
3397 STARTON playanimation on death
3398 must have an origin brush
3399
3400 "model2" will specify the model to load
3401
3402 "noise" the looping sound entity is to make
3403
3404 "type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
3405 "count" how much debris ei. default 4 pieces
3406
3407 "frames" how many frames of animation to play
3408 "loop" when the animation is done start again on this frame
3409 "startonframe" on what frame do you want to start the animation
3410 */
3411
SP_props_decorBRUSH(gentity_t * self)3412 void SP_props_decorBRUSH( gentity_t *self ) {
3413
3414 trap_SetBrushModel( self, self->model );
3415
3416 SP_props_decoration( self );
3417
3418 if ( self->model2 ) {
3419 self->s.modelindex2 = G_ModelIndex( self->model2 );
3420 }
3421
3422 }
3423
3424
3425 /*QUAKED props_decoration_scale (.6 .7 .7) (-8 -8 0) (8 8 16) STARTINVIS DEBRIS ANIMATE KEEPBLOCK TOUCHACTIVATE LOOPING STARTON
3426
3427 "modelscale" - Scale multiplier (defaults to 1.0 and scales uniformly)
3428 "modelscale_vec" - Set scale per-axis. Overrides "modelscale", so if you have both the "modelscale" is ignored
3429
3430 "model2" will specify the model to load
3431
3432 "noise" the looping sound entity is to make
3433
3434 "type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
3435 "count" how much debris ei. default 4 pieces
3436
3437 you will need to specify the bounding box for the entity
3438 "high" default is 4
3439 "wide" default is 4
3440
3441 "frames" how many frames of animation to play
3442 "loop" when the animation is done start again on this frame
3443 "startonframe" on what frame do you want to start the animation
3444 */
3445
SP_props_decor_Scale(gentity_t * ent)3446 void SP_props_decor_Scale( gentity_t *ent ) {
3447
3448 float scale[3] = {1,1,1};
3449 vec3_t scalevec;
3450
3451
3452 SP_props_decoration( ent );
3453
3454 ent->s.eType = ET_GAMEMODEL;
3455
3456 // look for general scaling
3457 if ( G_SpawnFloat( "modelscale", "1", &scale[0] ) ) {
3458 scale[2] = scale[1] = scale[0];
3459 }
3460
3461 // look for axis specific scaling
3462 if ( G_SpawnVector( "modelscale_vec", "1 1 1", &scalevec[0] ) ) {
3463 VectorCopy( scalevec, scale );
3464 }
3465
3466 // scale is stored in 'angles2'
3467 VectorCopy( scale, ent->s.angles2 );
3468
3469 trap_LinkEntity( ent );
3470
3471 }
3472
3473 /*QUAKED props_skyportal (.6 .7 .7) (-8 -8 0) (8 8 16)
3474 "fov" for the skybox default is 90
3475 To have the portal sky fogged, enter any of the following values:
3476 "fogcolor" (r g b) (values 0.0-1.0)
3477 "fognear" distance from entity to start fogging
3478 "fogfar" distance from entity that fog is opaque
3479
3480 */
SP_skyportal(gentity_t * ent)3481 void SP_skyportal( gentity_t *ent ) {
3482 char *fov;
3483 vec3_t fogv; //----(SA)
3484 int fogn; //----(SA)
3485 int fogf; //----(SA)
3486 int isfog = 0; // (SA)
3487
3488 float fov_x;
3489
3490 G_SpawnString( "fov", "90", &fov );
3491 fov_x = atof( fov );
3492
3493 //----(SA) modified
3494 isfog += G_SpawnVector( "fogcolor", "0 0 0", fogv );
3495 isfog += G_SpawnInt( "fognear", "0", &fogn );
3496 isfog += G_SpawnInt( "fogfar", "300", &fogf );
3497
3498 trap_SetConfigstring( CS_SKYBOXORG, va( "%.2f %.2f %.2f %.1f %i %.2f %.2f %.2f %i %i", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], fov_x, (int)isfog, fogv[0], fogv[1], fogv[2], fogn, fogf ) );
3499 //----(SA) end
3500 }
3501
3502 /*QUAKED props_statue (.6 .3 .2) (-8 -8 0) (8 8 128) HURT DEBRIS ANIMATE KEEPBLOCK
3503 "model2" will specify the model to load
3504
3505 "noise" the sound entity is to make
3506
3507 "type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
3508 "count" how much debris ei. default 4 pieces
3509
3510 you will need to specify the bounding box for the entity
3511 "high" default is 4
3512 "wide" default is 4
3513
3514 "frames" how many frames of animation to play
3515 "delay" how long of a delay before damage is inflicted ei. 0.5 sec or 2.7 sec
3516
3517 "damage" amount of damage to be inflicted
3518 */
3519
props_statue_blocked(gentity_t * ent)3520 void props_statue_blocked( gentity_t *ent ) {
3521 trace_t trace;
3522 vec3_t start, end, mins, maxs;
3523 vec3_t forward;
3524 float dist;
3525 gentity_t *traceEnt;
3526 float grav = 128;
3527 vec3_t kvel;
3528
3529 if ( !Q_stricmp( ent->classname, "props_statueBRUSH" ) ) {
3530 return;
3531 }
3532
3533 VectorCopy( ent->s.origin, start );
3534 start[2] += 24;
3535
3536 VectorSet( mins, ent->r.mins[0], ent->r.mins[1], -23 );
3537 VectorSet( maxs, ent->r.maxs[0], ent->r.maxs[1], 23 );
3538
3539 AngleVectors( ent->r.currentAngles, forward, NULL, NULL );
3540
3541 VectorCopy( start, end );
3542
3543 dist = ( ( ent->r.maxs[2] + 16 ) / ent->count2 ) * ent->s.frame;
3544
3545 VectorMA( end, dist, forward, end );
3546
3547 trap_Trace( &trace, start, mins, maxs, end, ent->s.number, MASK_SHOT );
3548
3549 if ( trace.surfaceFlags & SURF_NOIMPACT ) { // bogus test but just in case
3550 return;
3551 }
3552
3553 traceEnt = &g_entities[ trace.entityNum ];
3554
3555 if ( traceEnt->takedamage && traceEnt->client ) {
3556 G_Damage( traceEnt, ent, ent, NULL, trace.endpos, ent->damage, 0, MOD_CRUSH );
3557
3558 // TBD: push client back a bit
3559 VectorScale( forward, grav, kvel );
3560 VectorAdd( traceEnt->client->ps.velocity, kvel, traceEnt->client->ps.velocity );
3561
3562 if ( !traceEnt->client->ps.pm_time ) {
3563 int t;
3564
3565 t = grav * 2;
3566 if ( t < 50 ) {
3567 t = 50;
3568 }
3569 if ( t > 200 ) {
3570 t = 200;
3571 }
3572 traceEnt->client->ps.pm_time = t;
3573 traceEnt->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
3574 }
3575
3576 } else {
3577 G_Damage( traceEnt, ent, ent, NULL, trace.endpos, 9999, 0, MOD_CRUSH );
3578 }
3579
3580 }
3581
props_statue_animate(gentity_t * ent)3582 void props_statue_animate( gentity_t *ent ) {
3583
3584 qboolean takeashot = qfalse;
3585
3586 ent->s.frame++;
3587 ent->s.eType = ET_GENERAL;
3588
3589 if ( ent->s.frame > ent->count2 ) {
3590 ent->s.frame = ent->count2;
3591 ent->takedamage = qfalse;
3592 }
3593
3594 if ( ( ( ent->delay * 1000 ) + ent->timestamp ) > level.time ) {
3595 ent->count = 0;
3596 } else if ( ent->count == 5 ) {
3597 takeashot = qtrue;
3598 ent->count = 0;
3599 } else {
3600 ent->count++;
3601 }
3602
3603 if ( takeashot ) {
3604 props_statue_blocked( ent );
3605 }
3606
3607 if ( ent->s.frame < ent->count2 ) {
3608 ent->nextthink = level.time + 50;
3609 }
3610 }
3611
3612
props_statue_death(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)3613 void props_statue_death( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
3614
3615 ent->timestamp = level.time;
3616
3617 G_AddEvent( ent, EV_GENERAL_SOUND, ent->noise_index );
3618
3619 if ( !( ent->spawnflags & 8 ) ) {
3620 ent->clipmask = 0;
3621 ent->r.contents = 0;
3622 ent->s.eType = ET_GENERAL;
3623 trap_LinkEntity( ent );
3624 }
3625
3626 ent->takedamage = qfalse;
3627
3628 G_UseTargets( ent, NULL );
3629
3630 if ( ent->spawnflags & 2 ) {
3631 Spawn_Shard( ent, inflictor, ent->count, ent->key );
3632 }
3633
3634 if ( ent->spawnflags & 4 ) {
3635 ent->nextthink = level.time + 50;
3636 ent->think = props_statue_animate;
3637 return;
3638 }
3639
3640 G_FreeEntity( ent );
3641
3642 }
3643
props_statue_touch(gentity_t * self,gentity_t * other,trace_t * trace)3644 void props_statue_touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
3645 props_statue_death( self, other, other, 9999, MOD_CRUSH );
3646 }
3647
SP_props_statue(gentity_t * ent)3648 void SP_props_statue( gentity_t *ent ) {
3649 float light;
3650 vec3_t color;
3651 qboolean lightSet, colorSet;
3652 char *sound;
3653 char *type;
3654 char *high;
3655 char *wide;
3656 char *frames;
3657 float height;
3658 float width;
3659 float num_frames;
3660
3661 if ( ent->model2 ) {
3662 ent->s.modelindex = G_ModelIndex( ent->model2 );
3663 }
3664
3665 if ( G_SpawnString( "noise", "100", &sound ) ) {
3666 ent->noise_index = G_SoundIndex( sound );
3667 }
3668
3669 // if the "color" or "light" keys are set, setup constantLight
3670 lightSet = G_SpawnFloat( "light", "100", &light );
3671 colorSet = G_SpawnVector( "color", "1 1 1", color );
3672 if ( lightSet || colorSet ) {
3673 int r, g, b, i;
3674
3675 r = color[0] * 255;
3676 if ( r > 255 ) {
3677 r = 255;
3678 }
3679 g = color[1] * 255;
3680 if ( g > 255 ) {
3681 g = 255;
3682 }
3683 b = color[2] * 255;
3684 if ( b > 255 ) {
3685 b = 255;
3686 }
3687 i = light / 4;
3688 if ( i > 255 ) {
3689 i = 255;
3690 }
3691 ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 );
3692 }
3693
3694 ent->isProp = qtrue;
3695 ent->takedamage = qtrue;
3696 ent->die = props_statue_death;
3697
3698 G_SpawnString( "type", "wood", &type );
3699 if ( !Q_stricmp( type,"wood" ) ) {
3700 ent->key = 1;
3701 } else if ( !Q_stricmp( type,"glass" ) ) {
3702 ent->key = 0;
3703 } else if ( !Q_stricmp( type,"metal" ) ) {
3704 ent->key = 2;
3705 } else if ( !Q_stricmp( type,"ceramic" ) ) {
3706 ent->key = 3;
3707 } else if ( !Q_stricmp( type, "rubble" ) ) {
3708 ent->key = 4;
3709 }
3710
3711 G_SpawnString( "high", "0", &high );
3712 height = atof( high );
3713 if ( !height ) {
3714 height = 4;
3715 }
3716
3717 G_SpawnString( "wide", "0", &wide );
3718 width = atof( wide );
3719
3720 if ( !width ) {
3721 width = 4;
3722 }
3723
3724 width /= 2;
3725
3726 if ( Q_stricmp( ent->classname, "props_statueBRUSH" ) ) {
3727 VectorSet( ent->r.mins, -width, -width, 0 );
3728 VectorSet( ent->r.maxs, width, width, height );
3729 }
3730
3731 ent->clipmask = CONTENTS_SOLID;
3732 ent->r.contents = CONTENTS_SOLID;
3733 ent->s.eType = ET_MOVER;
3734
3735 G_SpawnString( "frames", "0", &frames );
3736 num_frames = atof( frames );
3737
3738 ent->count2 = num_frames;
3739
3740 ent->touch = props_statue_touch;
3741
3742 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
3743
3744 G_SetOrigin( ent, ent->s.origin );
3745 G_SetAngle( ent, ent->s.angles );
3746
3747 if ( !ent->damage ) {
3748 ent->damage = 1;
3749 }
3750
3751 trap_LinkEntity( ent );
3752 }
3753
3754
3755 /*QUAKED props_statueBRUSH (.6 .3 .2) ? HURT DEBRIS ANIMATE KEEPBLOCK
3756 needs an origin brush
3757
3758 "model2" will specify the model to load
3759
3760 "noise" the sound entity is to make
3761
3762 "type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
3763 "count" how much debris ei. default 4 pieces
3764
3765 "frames" how many frames of animation to play
3766 "delay" how long of a delay before damage is inflicted ei. 0.5 sec or 2.7 sec
3767
3768 THE damage has been disabled at the moment
3769 "damage" amount of damage to be inflicted
3770
3771 */
3772
SP_props_statueBRUSH(gentity_t * self)3773 void SP_props_statueBRUSH( gentity_t *self ) {
3774
3775 trap_SetBrushModel( self, self->model );
3776
3777 SP_props_statue( self );
3778
3779 if ( self->model2 ) {
3780 self->s.modelindex2 = G_ModelIndex( self->model2 );
3781 }
3782
3783 if ( !( self->health ) ) {
3784 self->health = 6;
3785 }
3786
3787 }
3788
3789 //////////////////////////////////////////////////
3790 // Lockers
3791 //////////////////////////////////////////////////
3792
3793 #define LOCKER_ANIM_USEEND 5
3794 #define LOCKER_ANIM_DEATHSTART 6
3795 #define LOCKER_ANIM_DEATHEND 11
3796
3797 //////////////////////////////////////////////////
3798 void init_locker( gentity_t *ent );
3799 void props_locker_death( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod );
3800 //////////////////////////////////////////////////
3801 #define MAX_LOCKER_DEBRIS 5
3802
3803 int locker_debris_model[MAX_LOCKER_DEBRIS];
3804 //////////////////////////////////////////////////
3805
Spawn_Junk(gentity_t * ent)3806 void Spawn_Junk( gentity_t *ent ) {
3807 gentity_t *sfx;
3808 vec3_t dir, start;
3809
3810 VectorCopy( ent->r.currentOrigin, start );
3811
3812 start[0] += crandom() * 32;
3813 start[1] += crandom() * 32;
3814 start[2] += 16;
3815
3816 VectorSubtract( start, ent->r.currentOrigin, dir );
3817 VectorNormalize( dir );
3818
3819 sfx = G_Spawn();
3820
3821 G_SetOrigin( sfx, start );
3822 G_SetAngle( sfx, ent->r.currentAngles );
3823
3824 G_AddEvent( sfx, EV_JUNK, DirToByte( dir ) );
3825
3826 sfx->think = G_FreeEntity;
3827
3828 sfx->nextthink = level.time + 1000;
3829
3830 trap_LinkEntity( sfx );
3831 }
3832
3833 /*
3834 ==============
3835 props_locker_endrattle
3836 ==============
3837 */
props_locker_endrattle(gentity_t * ent)3838 void props_locker_endrattle( gentity_t *ent ) {
3839 ent->s.frame = 0; // idle
3840 ent->think = 0;
3841 ent->nextthink = 0;
3842 ent->delay = 0;
3843 }
3844
3845
props_locker_use(gentity_t * ent,gentity_t * other,gentity_t * activator)3846 void props_locker_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
3847 if ( !ent->delay ) {
3848 ent->s.frame = 1; // rattle when pain starts
3849 }
3850 ent->delay = 1;
3851 ent->think = props_locker_endrattle;
3852 ent->nextthink = level.time + 1000; // rattle a sec
3853 }
3854
props_locker_pain(gentity_t * ent,gentity_t * attacker,int damage,vec3_t point)3855 void props_locker_pain( gentity_t *ent, gentity_t *attacker, int damage, vec3_t point ) {
3856 props_locker_use( ent, attacker, attacker );
3857 }
3858
3859
init_locker(gentity_t * ent)3860 void init_locker( gentity_t *ent ) {
3861 ent->isProp = qtrue;
3862 ent->takedamage = qtrue;
3863 ent->delay = 0;
3864
3865 ent->clipmask = CONTENTS_SOLID;
3866 ent->r.contents = CONTENTS_SOLID;
3867 // TODO: change from 'trap' to something else. 'trap' is a misnomer. it's actually used for other stuff too
3868 ent->s.eType = ET_TRAP;
3869
3870 ent->s.frame = 0; // closed animation
3871
3872 ent->count2 = LOCKER_ANIM_DEATHEND;
3873
3874 ent->die = props_locker_death;
3875 ent->use = props_locker_use; // trying it rattles the lock (could also allow 'waking' from trigger)
3876 ent->pain = props_locker_pain;
3877
3878 // drop origin down 8 so the designer can put the box entity on the floor rather than /in/ the floor
3879 // remove if you get a new model from jason w/ the origin moved up 8
3880 ent->s.origin[2] -= 8;
3881
3882 G_SetOrigin( ent, ent->s.origin );
3883 G_SetAngle( ent, ent->s.angles );
3884
3885 if ( !( ent->health ) ) {
3886 ent->health = 1;
3887 }
3888
3889 trap_LinkEntity( ent );
3890
3891 }
3892
props_locker_spawn_item(gentity_t * ent)3893 void props_locker_spawn_item( gentity_t *ent ) {
3894 gitem_t *item;
3895 gentity_t *drop = NULL;
3896
3897 item = BG_FindItem( ent->spawnitem );
3898
3899 if ( !item ) { // empty
3900 return;
3901 }
3902
3903 // drop = Drop_Item (ent, item, 0, qtrue);
3904 drop = LaunchItem( item, ent->r.currentOrigin, tv( 0, 0, 20 ), ent->s.number );
3905
3906
3907 if ( !drop ) {
3908 G_Printf( "-----> WARNING <-------\n" );
3909 G_Printf( "props_locker_spawn_item at %s failed!\n", vtos( ent->r.currentOrigin ) );
3910 }
3911 }
3912
3913 extern qhandle_t trap_R_GetShaderFromModel( qhandle_t modelid, int surfnum, int withlightmap );
3914
props_locker_mass(gentity_t * ent)3915 void props_locker_mass( gentity_t *ent ) {
3916 gentity_t *tent;
3917 vec3_t start;
3918 vec3_t dir;
3919
3920 VectorCopy( ent->r.currentOrigin, start );
3921
3922 start[0] += crandom() * 32;
3923 start[1] += crandom() * 32;
3924 start[2] += 16;
3925
3926 VectorSubtract( start, ent->r.currentOrigin, dir );
3927 VectorNormalize( dir );
3928
3929 tent = G_TempEntity( ent->r.currentOrigin, EV_EFFECT );
3930 VectorCopy( ent->r.currentOrigin, tent->s.origin );
3931 VectorCopy( dir, tent->s.angles2 );
3932
3933 tent->s.dl_intensity = 0;
3934
3935 trap_SetConfigstring( CS_TARGETEFFECT, ent->dl_shader ); //----(SA) allow shader to be set from entity
3936
3937 tent->s.frame = ent->key;
3938
3939 tent->s.eventParm = 8;
3940 tent->s.density = 100;
3941 }
3942
3943 /*QUAKED props_footlocker (.6 .7 .3) (-12 -21 -12) (12 21 12) ? NO_JUNK
3944 "noise" the sound entity is to make upon death
3945 the default sounds are:
3946 "wood" - "sound/world/boardbreak.wav"
3947 "glass" - "sound/world/glassbreak.wav"
3948 "metal" - "sound/world/metalbreak.wav"
3949 "gibs" - "sound/player/gibsplit1.wav"
3950 "brick" - "sound/world/brickfall.wav"
3951 "stone" - "sound/world/stonefall.wav"
3952 "fabric" - "sound/world/metalbreak.wav" // (SA) temp
3953
3954 "locknoise" the locked sound to play
3955 "wait" denotes how long the wait is going to be before the locked sound is played again default is 1 sec
3956 "health" default is 1
3957
3958 "spawnitem" - will spawn this item unpon death use the pickup_name ei 9mm
3959
3960 "type" - type of debris ("glass", "wood", "metal", "gibs", "brick", "rock", "fabric") default is "wood"
3961 "mass" - defaults to 75. This determines how much debris is emitted. 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.
3962
3963 "dl_shader" needs to be set the same way as a target_effect
3964
3965 TBD: the spawning of junk still pending and animation when used
3966
3967 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
3968 model="models/mapobjects/furniture/footlocker.md3"
3969 */
3970
3971 /*
3972 ==============
3973 props_locker_death
3974 ==============
3975 */
props_locker_death(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)3976 void props_locker_death( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
3977 ent->takedamage = qfalse;
3978 ent->s.frame = 2; // opening animation
3979 ent->think = 0;
3980 ent->nextthink = 0;
3981
3982 trap_UnlinkEntity( ent );
3983 ent->r.maxs[2] = 11; // (SA) make the dead bb half height so the item can look like it's sitting inside
3984 props_locker_spawn_item( ent );
3985 trap_LinkEntity( ent );
3986
3987 }
3988
3989
SP_props_footlocker(gentity_t * self)3990 void SP_props_footlocker( gentity_t *self ) {
3991 char *type;
3992 char *sound;
3993 char *locked;
3994 int mass;
3995
3996 // trap_SetBrushModel (self, self->model);
3997
3998 // (SA) if angle is xx or yy, rotate the bounding box 90 deg to match
3999 // NOTE: Non axis-aligned orientation not allowed. It will work, but
4000 // the bounding box will not exactly match the model.
4001 if ( self->s.angles[1] == 90 || self->s.angles[1] == 270 ) {
4002 VectorSet( self->r.mins, -21, -12, 0 );
4003 VectorSet( self->r.maxs, 21, 12, 24 );
4004 } else {
4005 VectorSet( self->r.mins, -12, -21, 0 );
4006 VectorSet( self->r.maxs, 12, 21, 24 );
4007 }
4008
4009 self->s.modelindex = G_ModelIndex( "models/mapobjects/furniture/footlocker.md3" );
4010
4011 if ( G_SpawnString( "noise", "NOSOUND", &sound ) ) {
4012 self->noise_index = G_SoundIndex( sound );
4013 }
4014
4015 if ( G_SpawnString( "locknoise", "NOSOUND", &locked ) ) {
4016 self->soundPos1 = G_SoundIndex( locked );
4017 }
4018
4019 if ( !( self->wait ) ) {
4020 self->wait = 1000;
4021 } else {
4022 self->wait *= 1000;
4023 }
4024
4025 if ( G_SpawnInt( "mass", "75", &mass ) ) {
4026 self->count = mass;
4027 } else {
4028 self->count = 75;
4029 }
4030
4031 if ( G_SpawnString( "type", "wood", &type ) ) {
4032 if ( !Q_stricmp( type,"wood" ) ) {
4033 self->key = 0;
4034 } else if ( !Q_stricmp( type,"glass" ) ) {
4035 self->key = 1;
4036 } else if ( !Q_stricmp( type,"metal" ) ) {
4037 self->key = 2;
4038 } else if ( !Q_stricmp( type,"gibs" ) ) {
4039 self->key = 3;
4040 } else if ( !Q_stricmp( type,"brick" ) ) {
4041 self->key = 4;
4042 } else if ( !Q_stricmp( type,"rock" ) ) {
4043 self->key = 5;
4044 } else if ( !Q_stricmp( type,"fabric" ) ) {
4045 self->key = 6;
4046 }
4047 } else {
4048 self->key = 0;
4049 }
4050
4051 self->delay = level.time + self->wait;
4052
4053 init_locker( self );
4054
4055 }
4056
4057 /*QUAKED props_flamethrower (.6 .7 .3) (-8 -8 -8) (8 8 8) TRACKING
4058 the effect occurs when this entity is used
4059 needs to aim at a info_notnull
4060 "duration" how long the effect is going to last for example 1.2 sec 2.7 sec
4061 "random" how long of a random variance so the effect isnt exactly the same each time for example 1.1 sec or 0.2 sec
4062 "size" valid ranges are 1.0 to 0.1
4063 */
props_flamethrower_think(gentity_t * ent)4064 void props_flamethrower_think( gentity_t *ent ) {
4065 vec3_t vec, angles;
4066 gentity_t *target = NULL;
4067
4068 if ( ent->spawnflags & 1 ) { // tracking
4069 if ( ent->target ) {
4070 target = G_Find( NULL, FOFS( targetname ), ent->target );
4071 }
4072
4073 if ( !target ) {
4074 // VectorSet (ent->r.currentAngles, 0, 0, 1); // (SA) wasn't working
4075 VectorSet( ent->s.apos.trBase, 0, 0, 1 );
4076 } else
4077 {
4078 VectorSubtract( target->s.origin, ent->s.origin, vec );
4079 VectorNormalize( vec );
4080 vectoangles( vec, angles );
4081 // VectorCopy (angles, ent->r.currentAngles); // (SA) wasn't working
4082 VectorCopy( angles, ent->s.apos.trBase );
4083 }
4084 }
4085
4086 if ( ( ent->timestamp + ent->duration ) > level.time ) {
4087 G_AddEvent( ent, EV_FLAMETHROWER_EFFECT, 0 );
4088
4089 ent->nextthink = level.time + 50;
4090
4091 {
4092 int rval;
4093 int rnd;
4094
4095 if ( ent->random ) {
4096 rval = ent->random * 1000;
4097 rnd = rand() % rval;
4098 } else {
4099 rnd = 0;
4100 }
4101
4102 ent->timestamp = level.time + rnd;
4103 ent->nextthink = ent->timestamp + 50;
4104 }
4105 }
4106
4107 }
4108
props_flamethrower_use(gentity_t * ent,gentity_t * other,gentity_t * activator)4109 void props_flamethrower_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
4110 int rval;
4111 int rnd;
4112
4113 if ( ent->spawnflags & 2 ) {
4114 ent->spawnflags &= ~2;
4115 ent->think = 0; // (SA) wasn't working
4116 ent->nextthink = 0;
4117 return;
4118 } else
4119 {
4120 ent->spawnflags |= 2;
4121 }
4122
4123 if ( ent->random ) {
4124 rval = ent->random * 1000;
4125 rnd = rand() % rval;
4126 } else {
4127 rnd = 0;
4128 }
4129
4130 ent->timestamp = level.time + rnd;
4131
4132 ent->think = props_flamethrower_think;
4133 ent->nextthink = level.time + 50;
4134
4135 }
4136
props_flamethrower_init(gentity_t * ent)4137 void props_flamethrower_init( gentity_t *ent ) {
4138 gentity_t *target = NULL;
4139 vec3_t vec;
4140 vec3_t angles;
4141
4142 if ( ent->target ) {
4143 target = G_Find( NULL, FOFS( targetname ), ent->target );
4144 }
4145
4146 if ( !target ) {
4147 // VectorSet (ent->r.currentAngles, 0, 0, 1); //----(SA)
4148 VectorSet( ent->s.apos.trBase, 0, 0, 1 );
4149 } else
4150 {
4151 VectorSubtract( target->s.origin, ent->s.origin, vec );
4152 VectorNormalize( vec );
4153 vectoangles( vec, angles );
4154 // VectorCopy (angles, ent->r.currentAngles); //----(SA)
4155 VectorCopy( angles, ent->s.apos.trBase );
4156 }
4157
4158 trap_LinkEntity( ent );
4159
4160 }
4161
SP_props_flamethrower(gentity_t * ent)4162 void SP_props_flamethrower( gentity_t *ent ) {
4163 char *size;
4164 float dsize;
4165
4166 ent->think = props_flamethrower_init;
4167 ent->nextthink = level.time + 50;
4168 ent->use = props_flamethrower_use;
4169
4170 G_SetOrigin( ent, ent->s.origin );
4171
4172 if ( !( ent->duration ) ) {
4173 ent->duration = 1000;
4174 } else {
4175 ent->duration *= 1000;
4176 }
4177
4178
4179 G_SpawnString( "size", "0", &size );
4180 dsize = atof( size );
4181 if ( !dsize ) {
4182 dsize = 1;
4183 }
4184 ent->accuracy = dsize;
4185
4186 }
4187