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