1 // g_misc.c
2 
3 #include "g_local.h"
4 
5 //Time befor gib etc starts to fade away...
6 #define FADE 3
7 
8 /*QUAKED func_group (0 0 0) ?
9 Used to group brushes together just for editor convenience.
10 */
11 
12 //=====================================================
13 
Use_Areaportal(edict_t * ent,edict_t * other,edict_t * activator)14 void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator)
15 {
16 	ent->count ^= 1;		// toggle state
17 //	gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count);
18 	gi.SetAreaPortalState (ent->style, ent->count);
19 }
20 
21 /*QUAKED func_areaportal (0 0 0) ?
22 
23 This is a non-visible object that divides the world into
24 areas that are seperated when this portal is not activated.
25 Usually enclosed in the middle of a door.
26 */
SP_func_areaportal(edict_t * ent)27 void SP_func_areaportal (edict_t *ent)
28 {
29 	ent->use = Use_Areaportal;
30 	ent->count = 0;		// always start closed;
31 }
32 
33 //=====================================================
34 
35 
36 /*
37 =================
38 Misc functions
39 =================
40 */
VelocityForDamage(int damage,vec3_t v)41 void VelocityForDamage (int damage, vec3_t v)
42 {
43 	v[0] = 100.0 * crandom();
44 	v[1] = 100.0 * crandom();
45 	v[2] = 200.0 + 100.0 * random();
46 
47 	if (damage < 50)
48 		VectorScale (v, 0.7, v);
49 	else
50 		VectorScale (v, 1.2, v);
51 }
52 
ClipGibVelocity(edict_t * ent)53 void ClipGibVelocity (edict_t *ent)
54 {
55 	if (ent->velocity[0] < -300)
56 		ent->velocity[0] = -300;
57 	else if (ent->velocity[0] > 300)
58 		ent->velocity[0] = 300;
59 	if (ent->velocity[1] < -300)
60 		ent->velocity[1] = -300;
61 	else if (ent->velocity[1] > 300)
62 		ent->velocity[1] = 300;
63 	if (ent->velocity[2] < 200)
64 		ent->velocity[2] = 200;	// always some upwards
65 	else if (ent->velocity[2] > 500)
66 		ent->velocity[2] = 500;
67 }
68 
69 
70 /*
71 =================
72 gibs
73 =================
74 */
gib_think(edict_t * self)75 void gib_think (edict_t *self)
76 {
77 	self->s.frame++;
78 	self->nextthink = level.time + FRAMETIME;
79 
80 	if (self->s.frame == 10)
81 	{
82 		self->think = G_FreeEdict;
83 		self->nextthink = level.time + 8 + random()*10;
84 	}
85 }
86 
gib_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)87 void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
88 {
89 	vec3_t	normal_angles, right;
90 
91 	if (!self->groundentity)
92 		return;
93 
94 	self->touch = NULL;
95 
96 	if (plane)
97 	{
98 		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
99 
100 		vectoangles (plane->normal, normal_angles);
101 		AngleVectors (normal_angles, NULL, right, NULL);
102 		vectoangles (right, self->s.angles);
103 
104 		if (self->s.modelindex == sm_meat_index)
105 		{
106 			self->s.frame++;
107 			self->think = gib_think;
108 			self->nextthink = level.time + FRAMETIME;
109 		}
110 	}
111 }
112 
gib_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)113 void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
114 {
115 	FadeDieSink (self);
116 }
117 
ThrowGib(edict_t * self,char * gibname,int damage,int type)118 void ThrowGib (edict_t *self, char *gibname, int damage, int type)
119 {
120 	edict_t *gib;
121 	vec3_t	vd;
122 	vec3_t	origin;
123 	vec3_t	size;
124 	float	vscale;
125 
126 	gib = G_Spawn();
127 
128 	gib->s.renderfx |= RF_IR_VISIBLE;
129 
130 	VectorScale (self->size, 0.5, size);
131 	VectorAdd (self->absmin, size, origin);
132 	gib->s.origin[0] = origin[0] + crandom() * size[0];
133 	gib->s.origin[1] = origin[1] + crandom() * size[1];
134 	gib->s.origin[2] = origin[2] + crandom() * size[2];
135 
136 	gi.setmodel (gib, gibname);
137 	gib->solid = SOLID_NOT;
138 	gib->s.effects |= EF_GIB;
139 	gib->flags |= FL_NO_KNOCKBACK;
140 	gib->takedamage = DAMAGE_YES;
141 	gib->die = gib_die;
142 
143 	if (type == GIB_ORGANIC)
144 	{
145 		gib->movetype = MOVETYPE_TOSS;
146 		gib->touch = gib_touch;
147 		vscale = 0.5;
148 	}
149 	else
150 	{
151 		gib->movetype = MOVETYPE_BOUNCE;
152 		vscale = 1.0;
153 	}
154 
155 	VelocityForDamage (damage, vd);
156 	VectorMA (self->velocity, vscale, vd, gib->velocity);
157 	ClipGibVelocity (gib);
158 	gib->avelocity[0] = random()*600;
159 	gib->avelocity[1] = random()*600;
160 	gib->avelocity[2] = random()*600;
161 
162 	if (damage==0)
163 	{
164 		VectorClear(gib->avelocity);
165 		VectorClear(gib->velocity);
166 		VectorSet(gib->avelocity,crandom()*20-40, crandom()*20-40, 300);
167 		VectorSet(gib->velocity, crandom()*20-40, crandom()*20-40, 300);
168 	}
169 
170 	gib->think=FadeDieSink;
171 	gib->nextthink=level.time+FADE+random()*3;
172 	gib->floater=1;
173 
174 	gi.linkentity (gib);
175 }
176 
ThrowGibHead(edict_t * self,char * gibname,int damage,int type)177 void ThrowGibHead (edict_t *self, char *gibname, int damage, int type)
178 {
179 	edict_t *gib;
180 	vec3_t	vd;
181 	vec3_t	origin;
182 	vec3_t	size;
183 	float	vscale;
184 
185 	gib = G_Spawn();
186 
187 	gib->s.skinnum = 0;
188 	gib->s.frame = 0;
189 	VectorSet (gib->mins, -16, -16, 0);
190 	VectorSet (gib->maxs, 16, 16, 16);
191 
192 	VectorScale (self->size, 0.5, size);
193 	VectorAdd (self->absmin, size, origin);
194 	gib->s.origin[0] = origin[0] + crandom() * size[0];
195 	gib->s.origin[1] = origin[1] + crandom() * size[1];
196 	gib->s.origin[2] = origin[2] + crandom() * size[2];
197 
198 	gi.setmodel (gib, gibname);
199 	gib->solid = SOLID_NOT;
200 	gib->s.effects |= EF_GIB;
201 	gib->s.renderfx |= RF_IR_VISIBLE;
202 	gib->flags |= FL_NO_KNOCKBACK;
203 	gib->takedamage = DAMAGE_YES;
204 	gib->die = gib_die;
205 
206 	if (type == GIB_ORGANIC)
207 	{
208 		gib->movetype = MOVETYPE_TOSS;
209 		gib->touch = gib_touch;
210 		vscale = 0.5;
211 	}
212 	else
213 	{
214 		gib->movetype = MOVETYPE_BOUNCE;
215 		vscale = 1.0;
216 	}
217 
218 	VelocityForDamage (damage, vd);
219 	VectorMA (self->velocity, vscale, vd, gib->velocity);
220 	ClipGibVelocity (gib);
221 	gib->avelocity[0] = random()*600;
222 	gib->avelocity[1] = random()*600;
223 	gib->avelocity[2] = random()*600;
224 
225 	gib->think=FadeDieSink;
226 	gib->nextthink=level.time+FADETIME+random()*3;
227 	gib->floater=1;
228 
229 	gi.linkentity (gib);
230 }
231 
ThrowHead(edict_t * self,char * gibname,int damage,int type)232 void ThrowHead (edict_t *self, char *gibname, int damage, int type)
233 {
234 	vec3_t	vd;
235 	float	vscale;
236 
237 	CleanUpEnt (self);
238 
239 	self->s.skinnum = 0;
240 	self->s.frame = 0;
241 	VectorClear (self->mins);
242 	VectorClear (self->maxs);
243 
244 	VectorSet (self->mins, -16, -16, 0);
245 	VectorSet (self->maxs, 16, 16, 16);
246 
247 	self->s.modelindex2 = 0;
248 	gi.setmodel (self, gibname);
249 	self->solid = SOLID_NOT;
250 	self->s.effects |= EF_GIB;
251 	self->s.effects &= ~EF_FLIES;
252 	self->s.sound = 0;
253 	self->flags |= FL_NO_KNOCKBACK;
254 	self->svflags &= ~SVF_MONSTER;
255 	self->takedamage = DAMAGE_YES;
256 	self->die = gib_die;
257 
258 	if (self->shadow)
259 		G_FreeEdict(self->shadow);
260 
261 	self->floater=1;
262 
263 	self->s.renderfx |= RF_IR_VISIBLE;
264 
265 	if (type == GIB_ORGANIC)
266 	{
267 		self->movetype = MOVETYPE_TOSS;
268 		self->touch = gib_touch;
269 		vscale = 0.5;
270 	}
271 	else
272 	{
273 		self->movetype = MOVETYPE_BOUNCE;
274 		vscale = 1.0;
275 	}
276 
277 	VelocityForDamage (damage, vd);
278 	VectorMA (self->velocity, vscale, vd, self->velocity);
279 	ClipGibVelocity (self);
280 
281 	self->avelocity[YAW] = crandom()*600;
282 
283 	self->think=FadeDieSink;
284 	self->nextthink=level.time+FADETIME+random()*3;
285 	self->floater=1;
286 
287 	if (!Q_stricmp("models/objects/gibs/sm_meat/tris.md2", gibname))
288 		self->nextthink=level.time+FADE+random()*3;
289 
290 	gi.linkentity (self);
291 }
292 
293 
ThrowClientHead(edict_t * self,int damage)294 void ThrowClientHead (edict_t *self, int damage)
295 {
296 	vec3_t	vd;
297 	char	*gibname;
298 
299 	CleanUpEnt (self);
300 		//*
301 	if (rand()&1)
302 	{
303 		gibname = "models/objects/gibs/head2/tris.md2";
304 		self->s.skinnum = 1;		// second skin is player
305 	}
306 	else
307 	{	//*/
308 		gibname = "models/objects/gibs/skull/tris.md2";
309 		self->s.skinnum = 0;
310 		//*
311 	}	//*/
312 
313 	self->s.origin[2] += 32;
314 	self->s.frame = 0;
315 	gi.setmodel (self, gibname);
316 	VectorSet (self->mins, -16, -16, 0);
317 	VectorSet (self->maxs, 16, 16, 16);
318 
319 	self->floater = 1;
320 
321 	self->takedamage = DAMAGE_NO;
322 	self->solid = SOLID_NOT;
323 	self->s.effects = EF_GIB;
324 	self->s.sound = 0;
325 	self->flags |= FL_NO_KNOCKBACK;
326 	self->s.frame=0;
327 
328 	self->movetype = MOVETYPE_BOUNCE;
329 	VelocityForDamage (damage, vd);
330 	VectorAdd (self->velocity, vd, self->velocity);
331 	/*
332 	if (self->client)	// bodies in the queue don't have a client anymore
333 	{
334 		self->client->anim_priority = ANIM_DEATH;
335 		self->client->anim_end = self->s.frame;
336 	}
337 	else
338 	{
339 		self->think = NULL;
340 		self->nextthink = 0;
341 
342 	}	//*/
343 
344 	self->think=FadeDieSink;
345 	self->nextthink=level.time+FADETIME+random()*3;
346 	self->floater=1;
347 
348 	gi.linkentity (self);
349 }
350 
351 
ThrowClientHeadNew(edict_t * self)352 edict_t * ThrowClientHeadNew (edict_t *self)
353 {
354 	edict_t *head;
355 	vec3_t	vd;
356 	char	*gibname;
357 
358 
359 	head = G_Spawn();
360 		//*
361 	if (rand()&1)
362 	{
363 		gibname = "models/objects/gibs/head2/tris.md2";
364 		head->s.skinnum = 1;		// second skin is player
365 	}
366 	else
367 	{	//*/
368 		gibname = "models/objects/gibs/skull/tris.md2";
369 		head->s.skinnum = 0;
370 		//*
371 	}	//*/
372 
373 	head->s.origin[2] += 32;
374 	head->s.frame = 0;
375 	gi.setmodel (head, gibname);
376 	VectorCopy(self->s.origin, head->s.origin);
377 	VectorSet (head->mins, -20, -20, 0);
378 	VectorSet (head->maxs, 20, 20, 20);
379 
380 	head->floater = 1;
381 
382 	head->takedamage = DAMAGE_NO;
383 	head->solid = SOLID_NOT;
384 	head->s.effects = EF_GIB;
385 	head->s.sound = 0;
386 	head->flags |= FL_NO_KNOCKBACK;
387 	head->s.frame=0;
388 
389 	head->movetype = MOVETYPE_BOUNCE;
390 
391 	head->think=FadeDieSink;
392 	head->nextthink=level.time+FADETIME+random()*3;
393 	head->floater=1;
394 
395 	VectorClear(head->avelocity);
396 	VectorClear(head->velocity);
397 	VectorSet(head->avelocity,crandom()*20-40, crandom()*20-40, 300);
398 	VectorSet(head->velocity, crandom()*20-40, crandom()*20-40, 300);
399 
400 	if (self->client)
401 		head->PlayerDeadName = self->client->pers.netname;
402 	else
403 		head->PlayerDeadName = self->PlayerDeadName;
404 
405 	return head;
406 }
407 
408 /*
409 =================
410 lens flares for lights etc
411 =================
412 */
413 
FlareThink(edict_t * flare)414 void FlareThink (edict_t *flare)
415 {
416 	int dot;
417 	vec3_t forward, dir;
418 
419 	flare->nextthink = level.time+FRAMETIME;
420 	if (!SPClient)
421 		return;
422 
423 	AngleVectors (SPClient->client->v_angle, forward, NULL, NULL);
424 	VectorSubtract(flare->s.origin, SPClient->s.origin, dir); VectorNormalize(dir);
425 
426 	dot = 100 * DotProduct(dir, forward);
427 
428 	if (dot>95)
429 		flare->s.frame = 2;
430 	else if (dot>85)
431 		flare->s.frame = 1;
432 	else
433 		flare->s.frame = 0;
434 }
435 
SP_LensFlare(edict_t * flare)436 void SP_LensFlare (edict_t *flare)
437 {
438 	if (sv_serversideonly->value)
439 	{
440 		G_FreeEdict(flare);
441 		return;
442 	}
443 
444 	gi.setmodel (flare, "sprites/s_lensflare.sp2");
445 
446 	flare->s.frame = 0;
447 
448 	flare->s.effects = 0;
449 	flare->s.renderfx = RF_TRANSLUCENT;
450 	flare->solid = SOLID_NOT;
451 	flare->svflags = SVF_DEADMONSTER;
452 	flare->clipmask = 0;
453 	flare->takedamage = DAMAGE_NO;
454 	flare->movetype = MOVETYPE_NONE;
455 
456 	if (!deathmatch->value && !coop->value)
457 	{
458 		flare->think = FlareThink;
459 		flare->nextthink = level.time+FRAMETIME;
460 	}
461 
462 	flare->classname = "lensflare";
463 
464 	gi.linkentity (flare);
465 }
466 
SunFlareThink(edict_t * flare)467 void SunFlareThink (edict_t *flare)
468 {
469 	int dot, frametarg;
470 	vec3_t forward, dir;
471 
472 	flare->nextthink = level.time+FRAMETIME;
473 	if (!SPClient)
474 		return;
475 
476 	AngleVectors (SPClient->client->v_angle, forward, NULL, NULL);
477 	VectorSubtract(flare->s.origin, SPClient->s.origin, dir); VectorNormalize(dir);
478 
479 	dot = 100 * DotProduct(dir, forward);
480 
481 	if (dot>95)
482 		frametarg = 4;
483 	else if (dot>90)
484 		frametarg = 3;
485 	else if (dot>85)
486 		frametarg = 2;
487 	else if (dot>80)
488 		frametarg = 1;
489 	else
490 		frametarg = 0;
491 
492 	if (flare->s.frame<frametarg)
493 		flare->s.frame++;
494 	else if (flare->s.frame>frametarg)
495 		flare->s.frame--;
496 
497 	if (flare->chain)
498 	{
499 		flare->chain->s.frame = flare->s.frame;
500 		if (flare->chain->s.frame>0)
501 			flare->chain->s.frame--;
502 	}
503 }
504 
SunFlareChild(edict_t * master)505 edict_t *SunFlareChild (edict_t *master)
506 {
507 	edict_t *flare;
508 
509 	flare = G_Spawn();
510 
511 	flare->s.modelindex = master->s.modelindex;
512 	VectorCopy(master->s.origin, flare->s.origin);
513 	flare->s.frame = 0;
514 	flare->s.effects = 0;
515 	flare->s.renderfx = RF_TRANSLUCENT;
516 	flare->solid = SOLID_NOT;
517 	flare->svflags = SVF_DEADMONSTER;
518 	flare->clipmask = 0;
519 	flare->takedamage = DAMAGE_NO;
520 	flare->movetype = MOVETYPE_NONE;
521 
522 	gi.linkentity (flare);
523 
524 	return flare;
525 }
526 
SP_SunFlare(edict_t * flare)527 void SP_SunFlare (edict_t *flare)
528 {
529 	if (deathmatch->value || coop->value || sv_serversideonly->value)
530 	{
531 		G_FreeEdict(flare);
532 		return;
533 	}
534 
535 	gi.setmodel (flare, "sprites/s_sunflare.sp2");
536 
537 	flare->s.frame = 0;
538 
539 	flare->s.effects = 0;
540 	flare->s.renderfx = RF_TRANSLUCENT;
541 	flare->solid = SOLID_NOT;
542 	flare->svflags = SVF_DEADMONSTER;
543 	flare->clipmask = 0;
544 	flare->takedamage = DAMAGE_NO;
545 	flare->movetype = MOVETYPE_NONE;
546 	flare->think = SunFlareThink;
547 	flare->nextthink = level.time+FRAMETIME;
548 
549 	flare->chain = SunFlareChild(flare);
550 
551 	flare->classname = "sunflare";
552 
553 	gi.linkentity (flare);
554 }
555 
556 /*
557 =================
558 debris
559 =================
560 */
561 
LinkToBSP(edict_t * chunk,edict_t * other)562 void LinkToBSP(edict_t *chunk, edict_t *other)
563 {
564 	VectorSubtract(chunk->s.origin, other->s.origin, chunk->move_origin);
565 
566 	//chunk->move_origin will be used to calc angle offset for rotating brushes
567 
568 	chunk->enemy = other;
569 	chunk->movetype = MOVETYPE_MOVEWITH;
570 }
571 
debris_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)572 void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
573 {
574 	FadeDieSink (self);
575 }
576 
ThrowDebris(edict_t * self,char * modelname,float speed,vec3_t origin)577 void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin)
578 {
579 	edict_t	*chunk;
580 	vec3_t	v;
581 
582 	chunk = G_Spawn();
583 	VectorCopy (origin, chunk->s.origin);
584 	gi.setmodel (chunk, modelname);
585 	v[0] = 100 * crandom();
586 	v[1] = 100 * crandom();
587 	v[2] = 100 + 100 * crandom();
588 	VectorMA (self->velocity, speed, v, chunk->velocity);
589 	chunk->movetype = MOVETYPE_BOUNCE;
590 	chunk->solid = SOLID_NOT;
591 	chunk->avelocity[0] = random()*600;
592 	chunk->avelocity[1] = random()*600;
593 	chunk->avelocity[2] = random()*600;
594 	chunk->think = G_FreeEdict;
595 	chunk->nextthink = level.time + 2 + random()*2;
596 	chunk->s.frame = 0;
597 	chunk->flags = 0;
598 	chunk->classname = "debris";
599 	chunk->takedamage = DAMAGE_YES;
600 	chunk->die = debris_die;
601 	chunk->think=FadeDieSink;
602 	chunk->nextthink=level.time+FADE+random()*3;
603 	chunk->floater=1;
604 
605 	gi.linkentity (chunk);
606 }
607 
sizeByType(meansodeath)608 int sizeByType(meansodeath)
609 {
610 	int mod = (meansodeath & ~MOD_FRIENDLY_FIRE & ~MOD_HEAD);
611 	switch (mod)
612 	{
613 		case MOD_BLASTER:
614 		case MOD_BLASTER2:
615 		case MOD_SHOTGUN:
616 		case MOD_SSHOTGUN:
617 		case MOD_MACHINEGUN:
618 		case MOD_CHAINGUN2:
619 			return 0;
620 			break;
621 		case MOD_CHAINGUN:
622 		case MOD_SHOTGUN2:
623 		case MOD_SSHOTGUN2:
624 			return 1;
625 			break;
626 		case MOD_RAILGUN:
627 			return 2;
628 			break;
629 		default:
630 			return 3;
631 	}
632 }
633 
BulletMarkThink(edict_t * self)634 void BulletMarkThink (edict_t *self)
635 {
636 	int i=0;
637 	edict_t	*findMark=NULL;
638 
639 	if (!self)
640 		return;
641 
642 	while (findMark!=self)
643 	{
644 		findMark=bulletptr[i];
645 		i++;
646 		if (i>bulletmarks)
647 			break;
648 	}
649 	for (i=i-1; i <= bulletmarks;i++)
650 		bulletptr[i] = bulletptr[i+1];
651 	bulletptr[bulletmarks]=NULL;
652 	bulletmarks--;
653 
654 	G_FreeEdict(self);
655 }
656 
findradiusbhole(edict_t * from,vec3_t org,float rad)657 edict_t *findradiusbhole (edict_t *from, vec3_t org, float rad)
658 {
659 	vec3_t	eorg;
660 	int		j;
661 
662 	if (!from)
663 		from = g_edicts;
664 	else
665 		from++;
666 	for ( ; from < &g_edicts[globals.num_edicts]; from++)
667 	{
668 		if (!from->inuse)
669 			continue;
670 		for (j=0 ; j<3 ; j++)
671 			eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
672 		if (VectorLength(eorg) > rad)
673 			continue;
674 		return from;
675 	}
676 
677 	return NULL;
678 }
679 
FindBulletMarkRadius(vec3_t start,int skin,int radius)680 qboolean FindBulletMarkRadius (vec3_t start, int skin, int radius)
681 {
682 	edict_t	*ent = NULL;
683 
684 	while ((ent = findradiusbhole(ent, start, radius)) != NULL)
685 	{
686 		if (!Q_stricmp (ent->classname, "bullethole") && ent->s.skinnum == skin)
687 			return true;
688 	}
689 	return false;
690 }
691 
692 
vectoanglenormaled(vec3_t value1,float angleyaw,vec3_t angles)693 void vectoanglenormaled (vec3_t value1, float angleyaw, vec3_t angles)
694 {
695 	float	forward, yaw, pitch;
696 
697 	yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
698 	forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
699 	pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
700 
701 	if (pitch < 0)
702 		pitch += 360;
703 
704 	angles[PITCH] = -pitch;
705 	angles[YAW] =  yaw;
706 	angles[ROLL] = yaw - angleyaw;
707 }
708 
FootPrint(edict_t * ent,vec3_t start,vec3_t dir,int type,edict_t * other)709 void FootPrint (edict_t *ent, vec3_t start, vec3_t dir, int type, edict_t *other)
710 {
711 	edict_t	*chunk;
712 
713 	if (sv_serversideonly->value)
714 		return;
715 
716 	if ((int)sv_bulletmarks->value<=0)
717 		return;
718 
719 	if (FindBulletMarkRadius(start, type, 4))
720 		return;
721 
722 	chunk = G_Spawn();
723 
724 	if (sv_bulletmarks->value <= bulletmarks)
725 		BulletMarkThink(bulletptr[0]);
726 
727 	VectorCopy (start, chunk->s.origin);
728 	vectoanglenormaled (dir, ent->s.angles[1], chunk->s.angles);
729 
730 	gi.setmodel (chunk, "models/objects/footprint/tris.md2");
731 	chunk->s.frame = 0;
732 	chunk->movetype = MOVETYPE_FLYMISSILE;
733 	chunk->solid = SOLID_NOT;
734 	chunk->think = BulletMarkThink;
735 	chunk->nextthink = level.time + 30 + random()*10;
736 	chunk->flags = 0;
737 	chunk->classname = "bullethole";
738 	chunk->takedamage = DAMAGE_NO;
739 
740 	chunk->s.skinnum = type;
741 	chunk->s.renderfx = RF_TRANSLUCENT;
742 
743 	chunk->s.origin[2]+=0.1;
744 
745 	if (other && other->solid == SOLID_BSP && other->movetype == MOVETYPE_PUSH)
746 		LinkToBSP(chunk, other);
747 
748 	gi.linkentity (chunk);
749 	bulletptr[bulletmarks] = chunk;
750 	bulletmarks++;
751 }
752 
BulletMark(edict_t * self,trace_t * tr,int mod)753 void BulletMark (edict_t *self, trace_t *tr, int mod)
754 {
755 	int size = sizeByType(mod);
756 	edict_t	*chunk;
757 
758 	if ((int)sv_bulletmarks->value<=0)
759 		return;
760 
761 	if (FindBulletMarkRadius(tr->endpos, size, 1))
762 		return;
763 
764 	chunk = G_Spawn();
765 
766 	if (sv_bulletmarks->value <= bulletmarks)
767 		BulletMarkThink(bulletptr[0]);
768 
769 	VectorCopy (tr->endpos, chunk->s.origin);
770 	vectoanglenormaled (tr->plane.normal, (30 * (int)(random()*12)), chunk->s.angles);
771 
772 	gi.setmodel (chunk, "models/objects/hole/tris.md2");
773 	chunk->s.frame = 0;
774 	chunk->movetype = MOVETYPE_NONE;
775 	chunk->solid = SOLID_NOT;
776 	chunk->think = BulletMarkThink;
777 	chunk->nextthink = level.time + 30 + random()*10;
778 	chunk->flags = 0;
779 	chunk->classname = "bullethole";
780 	chunk->takedamage = DAMAGE_NO;
781 	chunk->s.skinnum = size;
782 	chunk->s.renderfx = RF_TRANSLUCENT;
783 
784 	if (tr && tr->ent && tr->ent->solid == SOLID_BSP && tr->ent->movetype == MOVETYPE_PUSH)
785 		LinkToBSP(chunk, tr->ent);
786 
787 	chunk->s.origin[2]+=0.1;
788 
789 	gi.linkentity (chunk);
790 	bulletptr[bulletmarks] = chunk;
791 	bulletmarks++;
792 }
793 
BulletMarkSlow(edict_t * self,vec3_t dir,edict_t * other)794 void BulletMarkSlow (edict_t *self, vec3_t dir, edict_t *other)
795 {
796 	int size = sizeByType(self->mod);
797 	edict_t	*chunk;
798 
799 	gi.WriteByte (svc_temp_entity);
800 	gi.WriteByte (self->timer);
801 	gi.WritePosition (self->s.origin);
802 	gi.WriteDir (dir);
803 	gi.multicast (self->s.origin, MULTICAST_PVS);
804 
805 	if ((int)sv_bulletmarks->value<=0)
806 		return;
807 
808 	if (FindBulletMarkRadius(self->s.origin, size, 1))
809 		return;
810 
811 	if (sv_bulletmarks->value <= bulletmarks)
812 		BulletMarkThink(bulletptr[0]);
813 
814 	chunk = G_Spawn();
815 
816 	VectorCopy (self->s.origin, chunk->s.origin);
817 	vectoanglenormaled (dir, (30 * (int)(random()*12)), chunk->s.angles);
818 
819 	if (!sv_serversideonly->value)
820 		gi.setmodel (chunk, "models/objects/hole/tris.md2");
821 	else
822 		gi.setmodel (chunk, "models/objects/flash/tris.md2");
823 	chunk->s.frame = 0;
824 	chunk->movetype = MOVETYPE_NONE;
825 	chunk->solid = SOLID_NOT;
826 	chunk->think = BulletMarkThink;
827 	chunk->nextthink = level.time + 30 + random()*10;
828 	chunk->flags = 0;
829 	chunk->classname = "bullethole";
830 	chunk->takedamage = DAMAGE_NO;
831 	chunk->s.skinnum = size;
832 
833 	if (other && other->solid == SOLID_BSP && other->movetype == MOVETYPE_PUSH)
834 		LinkToBSP(chunk, other);
835 
836 	chunk->s.origin[2]+=0.1;
837 
838 	chunk->s.renderfx = RF_TRANSLUCENT;
839 	gi.linkentity (chunk);
840 	bulletptr[bulletmarks] = chunk;
841 	bulletmarks++;
842 }
843 
ExplodeMark(edict_t * self,vec3_t origin,float time)844 void ExplodeMark (edict_t *self, vec3_t origin, float time)
845 {
846 	edict_t	*chunk;
847 	chunk = G_Spawn();
848 	VectorCopy (origin, chunk->s.origin);
849 
850 	gi.setmodel (chunk, "models/objects/flash/tris.md2");
851 	chunk->s.frame = 0;
852 	chunk->s.renderfx = RF_BEAM;
853 	chunk->s.effects = EF_TRACKER;		//EF_TRACKER;
854 	chunk->movetype = MOVETYPE_NONE;
855 	chunk->solid = SOLID_NOT;
856 	chunk->think = G_FreeEdict;
857 	chunk->nextthink = time;
858 	chunk->flags = 0;
859 	chunk->classname = "debris";
860 	chunk->takedamage = DAMAGE_NO;
861 	gi.linkentity (chunk);
862 }
863 
BecomeExplosion1(edict_t * self)864 void BecomeExplosion1 (edict_t *self)
865 {
866 	if (sv_sprite_explosions->value)
867 	{
868 		self->s.origin[2]+=30;
869 		sprite_explosion (self->s.origin, 0, NULL);
870 	}
871 	else
872 	{
873 		gi.WriteByte (svc_temp_entity);
874 		gi.WriteByte (TE_EXPLOSION1);
875 		gi.WritePosition (self->s.origin);
876 		gi.multicast (self->s.origin, MULTICAST_PVS);
877 	}
878 
879 	G_FreeEdict (self);
880 }
881 
882 
BecomeExplosion2(edict_t * self)883 void BecomeExplosion2 (edict_t *self)
884 {
885 	if (sv_sprite_explosions->value)
886 	{
887 		self->s.origin[2]+=50;
888 		sprite_explosion (self->s.origin, 1, NULL);
889 	}
890 	else
891 	{
892 		gi.WriteByte (svc_temp_entity);
893 		gi.WriteByte (TE_EXPLOSION2);
894 		gi.WritePosition (self->s.origin);
895 		gi.multicast (self->s.origin, MULTICAST_PVS);
896 	}
897 
898 	G_FreeEdict (self);
899 }
900 
901 
902 /*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT
903 Target: next path corner
904 Pathtarget: gets used when an entity that has
905 	this path_corner targeted touches it
906 */
907 
path_corner_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)908 void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
909 {
910 	vec3_t		v;
911 	edict_t		*next;
912 
913 	if (other->movetarget != self)
914 		return;
915 
916 	if (other->enemy)
917 		return;
918 
919 	if (self->pathtarget)
920 	{
921 		char *savetarget;
922 
923 		savetarget = self->target;
924 		self->target = self->pathtarget;
925 		G_UseTargets (self, other);
926 		self->target = savetarget;
927 	}
928 
929 	if (self->target)
930 		next = G_PickTarget(self->target);
931 	else
932 		next = NULL;
933 
934 	if ((next) && (next->spawnflags & 1))
935 	{
936 		VectorCopy (next->s.origin, v);
937 		v[2] += next->mins[2];
938 		v[2] -= other->mins[2];
939 		VectorCopy (v, other->s.origin);
940 		next = G_PickTarget(next->target);
941 		other->s.event = EV_OTHER_TELEPORT;
942 	}
943 
944 	other->goalentity = other->movetarget = next;
945 
946 	if (self->wait)
947 	{
948 		other->monsterinfo.pausetime = level.time + self->wait;
949 		other->monsterinfo.stand (other);
950 		return;
951 	}
952 
953 	if (!other->movetarget)
954 	{
955 		other->monsterinfo.pausetime = level.time + 100000000;
956 		other->monsterinfo.stand (other);
957 	}
958 	else
959 	{
960 		VectorSubtract (other->goalentity->s.origin, other->s.origin, v);
961 		other->ideal_yaw = vectoyaw (v);
962 	}
963 }
964 
SP_path_corner(edict_t * self)965 void SP_path_corner (edict_t *self)
966 {
967 	if (!self->targetname)
968 	{
969 		gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin));
970 		G_FreeEdict (self);
971 		return;
972 	}
973 
974 	self->solid = SOLID_TRIGGER;
975 	self->touch = path_corner_touch;
976 	VectorSet (self->mins, -8, -8, -8);
977 	VectorSet (self->maxs, 8, 8, 8);
978 	self->svflags |= SVF_NOCLIENT;
979 	gi.linkentity (self);
980 }
981 
982 
983 /*QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold
984 Makes this the target of a monster and it will head here
985 when first activated before going after the activator.  If
986 hold is selected, it will stay here.
987 */
point_combat_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)988 void point_combat_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
989 {
990 	edict_t	*activator;
991 
992 	if (other->movetarget != self)
993 		return;
994 
995 	if (self->target)
996 	{
997 		other->target = self->target;
998 		other->goalentity = other->movetarget = G_PickTarget(other->target);
999 		if (!other->goalentity)
1000 		{
1001 			gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target);
1002 			other->movetarget = self;
1003 		}
1004 		self->target = NULL;
1005 	}
1006 	else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM|FL_FLY)))
1007 	{
1008 		other->monsterinfo.pausetime = level.time + 100000000;
1009 		other->monsterinfo.aiflags |= AI_STAND_GROUND;
1010 		other->monsterinfo.stand (other);
1011 	}
1012 
1013 	if (other->movetarget == self)
1014 	{
1015 		other->target = NULL;
1016 		other->movetarget = NULL;
1017 		other->goalentity = other->enemy;
1018 		other->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
1019 	}
1020 
1021 	if (self->pathtarget)
1022 	{
1023 		char *savetarget;
1024 
1025 		savetarget = self->target;
1026 		self->target = self->pathtarget;
1027 		if (other->enemy && other->enemy->client)
1028 			activator = other->enemy;
1029 		else if (other->oldenemy && other->oldenemy->client)
1030 			activator = other->oldenemy;
1031 		else if (other->activator && other->activator->client)
1032 			activator = other->activator;
1033 		else
1034 			activator = other;
1035 		G_UseTargets (self, activator);
1036 		self->target = savetarget;
1037 	}
1038 }
1039 
SP_point_combat(edict_t * self)1040 void SP_point_combat (edict_t *self)
1041 {
1042 	if (deathmatch->value)
1043 	{
1044 		G_FreeEdict (self);
1045 		return;
1046 	}
1047 	self->solid = SOLID_TRIGGER;
1048 	self->touch = point_combat_touch;
1049 	VectorSet (self->mins, -8, -8, -16);
1050 	VectorSet (self->maxs, 8, 8, 16);
1051 	self->svflags = SVF_NOCLIENT;
1052 	gi.linkentity (self);
1053 };
1054 
1055 
1056 /*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
1057 Just for the debugging level.  Don't use
1058 */
TH_viewthing(edict_t * ent)1059 void TH_viewthing(edict_t *ent)
1060 {
1061 	ent->s.frame = (ent->s.frame + 1) % 7;
1062 	ent->nextthink = level.time + FRAMETIME;
1063 }
1064 
SP_viewthing(edict_t * ent)1065 void SP_viewthing(edict_t *ent)
1066 {
1067 	gi.dprintf ("viewthing spawned\n");
1068 
1069 	ent->movetype = MOVETYPE_NONE;
1070 	ent->solid = SOLID_BBOX;
1071 	ent->s.renderfx = RF_FRAMELERP;
1072 	VectorSet (ent->mins, -16, -16, -24);
1073 	VectorSet (ent->maxs, 16, 16, 32);
1074 	ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
1075 	gi.linkentity (ent);
1076 	ent->nextthink = level.time + 0.5;
1077 	ent->think = TH_viewthing;
1078 	return;
1079 }
1080 
1081 
1082 /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
1083 Used as a positional target for spotlights, etc.
1084 */
SP_info_null(edict_t * self)1085 void SP_info_null (edict_t *self)
1086 {
1087 	G_FreeEdict (self);
1088 };
1089 
1090 
1091 /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
1092 Used as a positional target for lightning.
1093 */
SP_info_notnull(edict_t * self)1094 void SP_info_notnull (edict_t *self)
1095 {
1096 	VectorCopy (self->s.origin, self->absmin);
1097 	VectorCopy (self->s.origin, self->absmax);
1098 };
1099 
1100 
1101 /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF
1102 Non-displayed light.
1103 Default light value is 300.
1104 Default style is 0.
1105 If targeted, will toggle between on and off.
1106 Default _cone value is 10 (used to set size of light for spotlights)
1107 */
1108 
1109 #define START_OFF	1
1110 
light_use(edict_t * self,edict_t * other,edict_t * activator)1111 static void light_use (edict_t *self, edict_t *other, edict_t *activator)
1112 {
1113 	if (self->spawnflags & START_OFF)
1114 	{
1115 		gi.configstring (CS_LIGHTS+self->style, "m");
1116 		self->spawnflags &= ~START_OFF;
1117 	}
1118 	else
1119 	{
1120 		gi.configstring (CS_LIGHTS+self->style, "a");
1121 		self->spawnflags |= START_OFF;
1122 	}
1123 }
1124 
SP_light(edict_t * self)1125 void SP_light (edict_t *self)
1126 {
1127 	// no targeted lights in deathmatch, because they cause global messages
1128 	if (!self->targetname || deathmatch->value)
1129 	{
1130 		G_FreeEdict (self);
1131 		return;
1132 	}
1133 
1134 	if (self->style >= 32)
1135 	{
1136 		self->use = light_use;
1137 		if (self->spawnflags & START_OFF)
1138 			gi.configstring (CS_LIGHTS+self->style, "a");
1139 		else
1140 			gi.configstring (CS_LIGHTS+self->style, "m");
1141 	}
1142 }
1143 
1144 
1145 /*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
1146 This is just a solid wall if not inhibited
1147 
1148 TRIGGER_SPAWN	the wall will not be present until triggered
1149 				it will then blink in to existance; it will
1150 				kill anything that was in it's way
1151 
1152 TOGGLE			only valid for TRIGGER_SPAWN walls
1153 				this allows the wall to be turned on and off
1154 
1155 START_ON		only valid for TRIGGER_SPAWN walls
1156 				the wall will initially be present
1157 */
1158 
func_wall_use(edict_t * self,edict_t * other,edict_t * activator)1159 void func_wall_use (edict_t *self, edict_t *other, edict_t *activator)
1160 {
1161 	if (self->solid == SOLID_NOT)
1162 	{
1163 		self->solid = SOLID_BSP;
1164 		self->svflags &= ~SVF_NOCLIENT;
1165 		KillBox (self);
1166 	}
1167 	else
1168 	{
1169 		self->solid = SOLID_NOT;
1170 		self->svflags |= SVF_NOCLIENT;
1171 	}
1172 	gi.linkentity (self);
1173 
1174 	if (!(self->spawnflags & 2))
1175 		self->use = NULL;
1176 }
1177 
SP_func_wall(edict_t * self)1178 void SP_func_wall (edict_t *self)
1179 {
1180 	self->movetype = MOVETYPE_PUSH;
1181 	gi.setmodel (self, self->model);
1182 
1183 	if (self->spawnflags & 8)
1184 		self->s.effects |= EF_ANIM_ALL;
1185 	if (self->spawnflags & 16)
1186 		self->s.effects |= EF_ANIM_ALLFAST;
1187 
1188 	// just a wall
1189 	if ((self->spawnflags & 7) == 0)
1190 	{
1191 		self->solid = SOLID_BSP;
1192 		gi.linkentity (self);
1193 		return;
1194 	}
1195 
1196 	// it must be TRIGGER_SPAWN
1197 	if (!(self->spawnflags & 1))
1198 	{
1199 //		gi.dprintf("func_wall missing TRIGGER_SPAWN\n");
1200 		self->spawnflags |= 1;
1201 	}
1202 
1203 	// yell if the spawnflags are odd
1204 	if (self->spawnflags & 4)
1205 	{
1206 		if (!(self->spawnflags & 2))
1207 		{
1208 			gi.dprintf("func_wall START_ON without TOGGLE\n");
1209 			self->spawnflags |= 2;
1210 		}
1211 	}
1212 
1213 	self->use = func_wall_use;
1214 	if (self->spawnflags & 4)
1215 	{
1216 		self->solid = SOLID_BSP;
1217 	}
1218 	else
1219 	{
1220 		self->solid = SOLID_NOT;
1221 		self->svflags |= SVF_NOCLIENT;
1222 	}
1223 	gi.linkentity (self);
1224 }
1225 
1226 
1227 /*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
1228 This is solid bmodel that will fall if it's support it removed.
1229 */
1230 
func_object_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1231 void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1232 {
1233 	// only squash thing we fall on top of
1234 	if (!plane)
1235 		return;
1236 	if (plane->normal[2] < 1.0)
1237 		return;
1238 	if (other->takedamage == DAMAGE_NO)
1239 		return;
1240 	T_Damage (other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
1241 }
1242 
func_object_release(edict_t * self)1243 void func_object_release (edict_t *self)
1244 {
1245 	self->movetype = MOVETYPE_TOSS;
1246 	self->touch = func_object_touch;
1247 }
1248 
func_object_use(edict_t * self,edict_t * other,edict_t * activator)1249 void func_object_use (edict_t *self, edict_t *other, edict_t *activator)
1250 {
1251 	self->solid = SOLID_BSP;
1252 	self->svflags &= ~SVF_NOCLIENT;
1253 	self->use = NULL;
1254 	KillBox (self);
1255 	func_object_release (self);
1256 }
1257 
SP_func_object(edict_t * self)1258 void SP_func_object (edict_t *self)
1259 {
1260 	gi.setmodel (self, self->model);
1261 
1262 	self->mins[0] += 1;
1263 	self->mins[1] += 1;
1264 	self->mins[2] += 1;
1265 	self->maxs[0] -= 1;
1266 	self->maxs[1] -= 1;
1267 	self->maxs[2] -= 1;
1268 
1269 	if (!self->dmg)
1270 		self->dmg = 100;
1271 
1272 	if (self->spawnflags == 0)
1273 	{
1274 		self->solid = SOLID_BSP;
1275 		self->movetype = MOVETYPE_PUSH;
1276 		self->think = func_object_release;
1277 		self->nextthink = level.time + 2 * FRAMETIME;
1278 	}
1279 	else
1280 	{
1281 		self->solid = SOLID_NOT;
1282 		self->movetype = MOVETYPE_PUSH;
1283 		self->use = func_object_use;
1284 		self->svflags |= SVF_NOCLIENT;
1285 	}
1286 
1287 	if (self->spawnflags & 2)
1288 		self->s.effects |= EF_ANIM_ALL;
1289 	if (self->spawnflags & 4)
1290 		self->s.effects |= EF_ANIM_ALLFAST;
1291 
1292 	self->clipmask = MASK_MONSTERSOLID;
1293 
1294 	gi.linkentity (self);
1295 }
1296 
1297 
1298 /*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST
1299 Any brush that you want to explode or break apart.  If you want an
1300 ex0plosion, set dmg and it will do a radius explosion of that amount
1301 at the center of the bursh.
1302 
1303 If targeted it will not be shootable.
1304 
1305 health defaults to 100.
1306 
1307 mass defaults to 75.  This determines how much debris is emitted when
1308 it explodes.  You get one large chunk per 100 of mass (up to 8) and
1309 one small chunk per 25 of mass (up to 16).  So 800 gives the most.
1310 */
func_explosive_explode(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1311 void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1312 {
1313 	vec3_t	origin;
1314 	vec3_t	chunkorigin;
1315 	vec3_t	size;
1316 	int		count;
1317 	int		mass;
1318 
1319 	// bmodel origins are (0 0 0), we need to adjust that here
1320 	VectorScale (self->size, 0.5, size);
1321 	VectorAdd (self->absmin, size, origin);
1322 	VectorCopy (origin, self->s.origin);
1323 
1324 	self->takedamage = DAMAGE_NO;
1325 
1326 	if (self->dmg)
1327 		T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
1328 
1329 	VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity);
1330 	VectorNormalize (self->velocity);
1331 	VectorScale (self->velocity, 150, self->velocity);
1332 
1333 	// start chunks towards the center
1334 	VectorScale (size, 0.5, size);
1335 
1336 	mass = self->mass;
1337 	if (!mass)
1338 		mass = 75;
1339 
1340 	// big chunks
1341 	if (mass >= 100)
1342 	{
1343 		count = mass / 100;
1344 		if (count > 8)
1345 			count = 8;
1346 		while(count--)
1347 		{
1348 			chunkorigin[0] = origin[0] + crandom() * size[0];
1349 			chunkorigin[1] = origin[1] + crandom() * size[1];
1350 			chunkorigin[2] = origin[2] + crandom() * size[2];
1351 			ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin);
1352 		}
1353 	}
1354 
1355 	// small chunks
1356 	count = mass / 25;
1357 	if (count > 16)
1358 		count = 16;
1359 	while(count--)
1360 	{
1361 		chunkorigin[0] = origin[0] + crandom() * size[0];
1362 		chunkorigin[1] = origin[1] + crandom() * size[1];
1363 		chunkorigin[2] = origin[2] + crandom() * size[2];
1364 		ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin);
1365 	}
1366 
1367 	G_UseTargets (self, attacker);
1368 
1369 	if (self->dmg)
1370 		BecomeExplosion1 (self);
1371 	else
1372 		G_FreeEdict (self);
1373 }
1374 
func_explosive_use(edict_t * self,edict_t * other,edict_t * activator)1375 void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator)
1376 {
1377 	func_explosive_explode (self, self, other, self->health, vec3_origin);
1378 }
1379 
func_explosive_spawn(edict_t * self,edict_t * other,edict_t * activator)1380 void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator)
1381 {
1382 	self->solid = SOLID_BSP;
1383 	self->svflags &= ~SVF_NOCLIENT;
1384 	self->use = NULL;
1385 	KillBox (self);
1386 	gi.linkentity (self);
1387 }
1388 
SP_func_explosive(edict_t * self)1389 void SP_func_explosive (edict_t *self)
1390 {
1391 	if (deathmatch->value)
1392 	{	// auto-remove for deathmatch
1393 		G_FreeEdict (self);
1394 		return;
1395 	}
1396 
1397 	self->movetype = MOVETYPE_PUSH;
1398 
1399 	gi.modelindex ("models/objects/debris1/tris.md2");
1400 	gi.modelindex ("models/objects/debris2/tris.md2");
1401 
1402 	gi.setmodel (self, self->model);
1403 
1404 	if (self->spawnflags & 1)
1405 	{
1406 		self->svflags |= SVF_NOCLIENT;
1407 		self->solid = SOLID_NOT;
1408 		self->use = func_explosive_spawn;
1409 	}
1410 	else
1411 	{
1412 		self->solid = SOLID_BSP;
1413 		if (self->targetname)
1414 			self->use = func_explosive_use;
1415 	}
1416 
1417 	if (self->spawnflags & 2)
1418 		self->s.effects |= EF_ANIM_ALL;
1419 	if (self->spawnflags & 4)
1420 		self->s.effects |= EF_ANIM_ALLFAST;
1421 
1422 	if (self->use != func_explosive_use)
1423 	{
1424 		if (!self->health)
1425 			self->health = 100;
1426 		self->die = func_explosive_explode;
1427 		self->takedamage = DAMAGE_YES;
1428 	}
1429 
1430 	gi.linkentity (self);
1431 }
1432 
1433 
1434 /*QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40)
1435 Large exploding box.  You can override its mass (100),
1436 health (80), and dmg (150).
1437 */
1438 
barrel_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1439 void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1440 
1441 {
1442 	float	ratio;
1443 	vec3_t	v;
1444 
1445 	if ((!other->groundentity) || (other->groundentity == self))
1446 		return;
1447 
1448 	ratio = (float)other->mass / (float)self->mass;
1449 	VectorSubtract (self->s.origin, other->s.origin, v);
1450 	M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME);
1451 }
1452 
barrel_explode(edict_t * self)1453 void barrel_explode (edict_t *self)
1454 {
1455 	vec3_t	org;
1456 	float	spd;
1457 	vec3_t	save;
1458 
1459 	T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL);
1460 
1461 	VectorCopy (self->s.origin, save);
1462 	VectorMA (self->absmin, 0.5, self->size, self->s.origin);
1463 
1464 	// a few big chunks
1465 	spd = 1.5 * (float)self->dmg / 200.0;
1466 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1467 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1468 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1469 	ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
1470 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1471 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1472 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1473 	ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
1474 
1475 	// bottom corners
1476 	spd = 1.75 * (float)self->dmg / 200.0;
1477 	VectorCopy (self->absmin, org);
1478 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
1479 	VectorCopy (self->absmin, org);
1480 	org[0] += self->size[0];
1481 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
1482 	VectorCopy (self->absmin, org);
1483 	org[1] += self->size[1];
1484 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
1485 	VectorCopy (self->absmin, org);
1486 	org[0] += self->size[0];
1487 	org[1] += self->size[1];
1488 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
1489 
1490 	// a bunch of little chunks
1491 	spd = 2 * self->dmg / 200;
1492 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1493 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1494 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1495 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1496 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1497 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1498 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1499 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1500 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1501 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1502 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1503 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1504 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1505 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1506 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1507 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1508 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1509 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1510 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1511 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1512 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1513 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1514 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1515 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1516 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1517 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1518 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1519 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1520 	org[0] = self->s.origin[0] + crandom() * self->size[0];
1521 	org[1] = self->s.origin[1] + crandom() * self->size[1];
1522 	org[2] = self->s.origin[2] + crandom() * self->size[2];
1523 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1524 
1525 	VectorCopy (save, self->s.origin);
1526 	if (self->groundentity)
1527 		BecomeExplosion2 (self);
1528 	else
1529 		BecomeExplosion1 (self);
1530 }
1531 
barrel_delay(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1532 void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1533 {
1534 	self->takedamage = DAMAGE_NO;
1535 	self->nextthink = level.time + 2 * FRAMETIME;
1536 	self->think = barrel_explode;
1537 	self->activator = attacker;
1538 }
1539 
SP_misc_explobox(edict_t * self)1540 void SP_misc_explobox (edict_t *self)
1541 {
1542 	if (deathmatch->value)
1543 	{	// auto-remove for deathmatch
1544 		G_FreeEdict (self);
1545 		return;
1546 	}
1547 
1548 	gi.modelindex ("models/objects/debris1/tris.md2");
1549 	gi.modelindex ("models/objects/debris2/tris.md2");
1550 	gi.modelindex ("models/objects/debris3/tris.md2");
1551 
1552 	self->solid = SOLID_BBOX;
1553 	self->movetype = MOVETYPE_STEP;
1554 
1555 	self->model = "models/objects/barrels/tris.md2";
1556 	self->s.modelindex = gi.modelindex (self->model);
1557 	VectorSet (self->mins, -16, -16, 0);
1558 	VectorSet (self->maxs, 16, 16, 40);
1559 
1560 	if (!self->mass)
1561 		self->mass = 400;
1562 	if (!self->health)
1563 		self->health = 10;
1564 	if (!self->dmg)
1565 		self->dmg = 150;
1566 
1567 	self->die = barrel_delay;
1568 	self->takedamage = DAMAGE_YES;
1569 	self->monsterinfo.aiflags = AI_NOSTEP;
1570 
1571 	self->touch = barrel_touch;
1572 
1573 	self->think = M_droptofloor;
1574 	self->nextthink = level.time + 2 * FRAMETIME;
1575 
1576 	gi.linkentity (self);
1577 }
1578 
1579 
1580 //
1581 // miscellaneous specialty items
1582 //
1583 
1584 /*QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8)
1585 */
1586 
misc_blackhole_use(edict_t * ent,edict_t * other,edict_t * activator)1587 void misc_blackhole_use (edict_t *ent, edict_t *other, edict_t *activator)
1588 {
1589 	/*
1590 	gi.WriteByte (svc_temp_entity);
1591 	gi.WriteByte (TE_BOSSTPORT);
1592 	gi.WritePosition (ent->s.origin);
1593 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1594 	*/
1595 	G_FreeEdict (ent);
1596 }
1597 
misc_blackhole_think(edict_t * self)1598 void misc_blackhole_think (edict_t *self)
1599 {
1600 	if (++self->s.frame < 19)
1601 		self->nextthink = level.time + FRAMETIME;
1602 	else
1603 	{
1604 		self->s.frame = 0;
1605 		self->nextthink = level.time + FRAMETIME;
1606 	}
1607 }
1608 
SP_misc_blackhole(edict_t * ent)1609 void SP_misc_blackhole (edict_t *ent)
1610 {
1611 	ent->movetype = MOVETYPE_NONE;
1612 	ent->solid = SOLID_NOT;
1613 	VectorSet (ent->mins, -64, -64, 0);
1614 	VectorSet (ent->maxs, 64, 64, 8);
1615 	ent->s.modelindex = gi.modelindex ("models/objects/black/tris.md2");
1616 	ent->s.renderfx = RF_TRANSLUCENT;
1617 	ent->use = misc_blackhole_use;
1618 	ent->think = misc_blackhole_think;
1619 	ent->nextthink = level.time + 2 * FRAMETIME;
1620 	gi.linkentity (ent);
1621 }
1622 
1623 /*QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32)
1624 */
1625 
misc_eastertank_think(edict_t * self)1626 void misc_eastertank_think (edict_t *self)
1627 {
1628 	if (++self->s.frame < 293)
1629 		self->nextthink = level.time + FRAMETIME;
1630 	else
1631 	{
1632 		self->s.frame = 254;
1633 		self->nextthink = level.time + FRAMETIME;
1634 	}
1635 }
1636 
SP_misc_eastertank(edict_t * ent)1637 void SP_misc_eastertank (edict_t *ent)
1638 {
1639 	ent->movetype = MOVETYPE_NONE;
1640 	ent->solid = SOLID_BBOX;
1641 	VectorSet (ent->mins, -32, -32, -16);
1642 	VectorSet (ent->maxs, 32, 32, 32);
1643 	ent->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2");
1644 	ent->s.frame = 254;
1645 	ent->think = misc_eastertank_think;
1646 	ent->nextthink = level.time + 2 * FRAMETIME;
1647 	gi.linkentity (ent);
1648 }
1649 
1650 /*QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32)
1651 */
1652 
1653 
misc_easterchick_think(edict_t * self)1654 void misc_easterchick_think (edict_t *self)
1655 {
1656 	if (++self->s.frame < 247)
1657 		self->nextthink = level.time + FRAMETIME;
1658 	else
1659 	{
1660 		self->s.frame = 208;
1661 		self->nextthink = level.time + FRAMETIME;
1662 	}
1663 }
1664 
SP_misc_easterchick(edict_t * ent)1665 void SP_misc_easterchick (edict_t *ent)
1666 {
1667 	ent->movetype = MOVETYPE_NONE;
1668 	ent->solid = SOLID_BBOX;
1669 	VectorSet (ent->mins, -32, -32, 0);
1670 	VectorSet (ent->maxs, 32, 32, 32);
1671 	ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2");
1672 	ent->s.frame = 208;
1673 	ent->think = misc_easterchick_think;
1674 	ent->nextthink = level.time + 2 * FRAMETIME;
1675 	gi.linkentity (ent);
1676 }
1677 
1678 /*QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32)
1679 */
1680 
1681 
misc_easterchick2_think(edict_t * self)1682 void misc_easterchick2_think (edict_t *self)
1683 {
1684 	if (++self->s.frame < 287)
1685 		self->nextthink = level.time + FRAMETIME;
1686 	else
1687 	{
1688 		self->s.frame = 248;
1689 		self->nextthink = level.time + FRAMETIME;
1690 	}
1691 }
1692 
SP_misc_easterchick2(edict_t * ent)1693 void SP_misc_easterchick2 (edict_t *ent)
1694 {
1695 	ent->movetype = MOVETYPE_NONE;
1696 	ent->solid = SOLID_BBOX;
1697 	VectorSet (ent->mins, -32, -32, 0);
1698 	VectorSet (ent->maxs, 32, 32, 32);
1699 	ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2");
1700 	ent->s.frame = 248;
1701 	ent->think = misc_easterchick2_think;
1702 	ent->nextthink = level.time + 2 * FRAMETIME;
1703 	gi.linkentity (ent);
1704 }
1705 
1706 
1707 /*QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48)
1708 Not really a monster, this is the Tank Commander's decapitated body.
1709 There should be a item_commander_head that has this as it's target.
1710 */
1711 
commander_body_think(edict_t * self)1712 void commander_body_think (edict_t *self)
1713 {
1714 	if (++self->s.frame < 24)
1715 		self->nextthink = level.time + FRAMETIME;
1716 	else
1717 		self->nextthink = 0;
1718 
1719 	if (self->s.frame == 22)
1720 		gi.sound (self, CHAN_BODY, gi.soundindex ("tank/thud.wav"), 1, ATTN_NORM, 0);
1721 }
1722 
commander_body_use(edict_t * self,edict_t * other,edict_t * activator)1723 void commander_body_use (edict_t *self, edict_t *other, edict_t *activator)
1724 {
1725 	self->think = commander_body_think;
1726 	self->nextthink = level.time + FRAMETIME;
1727 	gi.sound (self, CHAN_BODY, gi.soundindex ("tank/pain.wav"), 1, ATTN_NORM, 0);
1728 }
1729 
commander_body_drop(edict_t * self)1730 void commander_body_drop (edict_t *self)
1731 {
1732 	self->movetype = MOVETYPE_TOSS;
1733 	self->s.origin[2] += 2;
1734 }
1735 
SP_monster_commander_body(edict_t * self)1736 void SP_monster_commander_body (edict_t *self)
1737 {
1738 	self->movetype = MOVETYPE_NONE;
1739 	self->solid = SOLID_BBOX;
1740 	self->model = "models/monsters/commandr/tris.md2";
1741 	self->s.modelindex = gi.modelindex (self->model);
1742 	VectorSet (self->mins, -32, -32, 0);
1743 	VectorSet (self->maxs, 32, 32, 48);
1744 	self->use = commander_body_use;
1745 	self->takedamage = DAMAGE_YES;
1746 	self->flags = FL_GODMODE;
1747 	self->s.renderfx |= RF_FRAMELERP;
1748 	gi.linkentity (self);
1749 
1750 	gi.soundindex ("tank/thud.wav");
1751 	gi.soundindex ("tank/pain.wav");
1752 
1753 	self->think = commander_body_drop;
1754 	self->nextthink = level.time + 5 * FRAMETIME;
1755 }
1756 
1757 
1758 /*QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4)
1759 The origin is the bottom of the banner.
1760 The banner is 128 tall.
1761 */
misc_banner_think(edict_t * ent)1762 void misc_banner_think (edict_t *ent)
1763 {
1764 	ent->s.frame = (ent->s.frame + 1) % 16;
1765 	ent->nextthink = level.time + FRAMETIME;
1766 }
1767 
SP_misc_banner(edict_t * ent)1768 void SP_misc_banner (edict_t *ent)
1769 {
1770 	ent->movetype = MOVETYPE_NONE;
1771 	ent->solid = SOLID_NOT;
1772 	ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
1773 	ent->s.frame = rand() % 16;
1774 	gi.linkentity (ent);
1775 
1776 	ent->think = misc_banner_think;
1777 	ent->nextthink = level.time + FRAMETIME;
1778 }
1779 
1780 /*QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED
1781 This is the dead player model. Comes in 6 exciting different poses!
1782 */
misc_deadsoldier_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1783 void misc_deadsoldier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1784 {
1785 	int		n;
1786 
1787 	if (self->health > -300)
1788 		return;
1789 
1790 	gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
1791 	for (n= 0; n < 4; n++)
1792 		ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
1793 	ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
1794 }
1795 
SP_misc_deadsoldier(edict_t * ent)1796 void SP_misc_deadsoldier (edict_t *ent)
1797 {
1798 	if (deathmatch->value)
1799 	{	// auto-remove for deathmatch
1800 		G_FreeEdict (ent);
1801 		return;
1802 	}
1803 
1804 	ent->movetype = MOVETYPE_NONE;
1805 	ent->solid = SOLID_BBOX;
1806 	ent->s.modelindex=gi.modelindex ("models/deadbods/dude/tris.md2");
1807 
1808 	// Defaults to frame 0
1809 	if (ent->spawnflags & 2)
1810 		ent->s.frame = 1;
1811 	else if (ent->spawnflags & 4)
1812 		ent->s.frame = 2;
1813 	else if (ent->spawnflags & 8)
1814 		ent->s.frame = 3;
1815 	else if (ent->spawnflags & 16)
1816 		ent->s.frame = 4;
1817 	else if (ent->spawnflags & 32)
1818 		ent->s.frame = 5;
1819 	else
1820 		ent->s.frame = 0;
1821 
1822 	VectorSet (ent->mins, -16, -16, 0);
1823 	VectorSet (ent->maxs, 16, 16, 16);
1824 	ent->deadflag = DEAD_DEAD;
1825 	ent->takedamage = DAMAGE_YES;
1826 	ent->svflags |= SVF_MONSTER|SVF_DEADMONSTER;
1827 	ent->die = misc_deadsoldier_die;
1828 	ent->monsterinfo.aiflags |= AI_GOOD_GUY;
1829 
1830 	ent->think=FadeDieSink;
1831 	ent->nextthink=level.time+FADETIME+random()*FADETIME*2;
1832 	ent->floater=1;
1833 
1834 	gi.linkentity (ent);
1835 }
1836 
1837 /*QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32)
1838 This is the Viper for the flyby bombing.
1839 It is trigger_spawned, so you must have something use it for it to show up.
1840 There must be a path for it to follow once it is activated.
1841 
1842 "speed"		How fast the Viper should fly
1843 */
1844 
1845 extern void train_use (edict_t *self, edict_t *other, edict_t *activator);
1846 extern void func_train_find (edict_t *self);
1847 
misc_viper_use(edict_t * self,edict_t * other,edict_t * activator)1848 void misc_viper_use  (edict_t *self, edict_t *other, edict_t *activator)
1849 {
1850 	self->svflags &= ~SVF_NOCLIENT;
1851 	self->use = train_use;
1852 	train_use (self, other, activator);
1853 }
1854 
SP_misc_viper(edict_t * ent)1855 void SP_misc_viper (edict_t *ent)
1856 {
1857 	if (!ent->target)
1858 	{
1859 		gi.dprintf ("misc_viper without a target at %s\n", vtos(ent->absmin));
1860 		G_FreeEdict (ent);
1861 		return;
1862 	}
1863 
1864 	if (!ent->speed)
1865 		ent->speed = 300;
1866 
1867 	ent->movetype = MOVETYPE_PUSH;
1868 	ent->solid = SOLID_NOT;
1869 	ent->s.modelindex = gi.modelindex ("models/ships/viper/tris.md2");
1870 	VectorSet (ent->mins, -16, -16, 0);
1871 	VectorSet (ent->maxs, 16, 16, 32);
1872 
1873 	ent->think = func_train_find;
1874 	ent->nextthink = level.time + FRAMETIME;
1875 	ent->use = misc_viper_use;
1876 	ent->svflags |= SVF_NOCLIENT;
1877 	ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
1878 
1879 	gi.linkentity (ent);
1880 }
1881 
1882 
1883 /*QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72)
1884 This is a large stationary viper as seen in Paul's intro
1885 */
SP_misc_bigviper(edict_t * ent)1886 void SP_misc_bigviper (edict_t *ent)
1887 {
1888 	ent->movetype = MOVETYPE_NONE;
1889 	ent->solid = SOLID_BBOX;
1890 	VectorSet (ent->mins, -176, -120, -24);
1891 	VectorSet (ent->maxs, 176, 120, 72);
1892 	ent->s.modelindex = gi.modelindex ("models/ships/bigviper/tris.md2");
1893 	gi.linkentity (ent);
1894 }
1895 
1896 
1897 /*QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8)
1898 "dmg"	how much boom should the bomb make?
1899 */
misc_viper_bomb_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1900 void misc_viper_bomb_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1901 {
1902 	G_UseTargets (self, self->activator);
1903 
1904 	self->s.origin[2] = self->absmin[2] + 1;
1905 	T_RadiusDamage (self, self, self->dmg, NULL, self->dmg+40, MOD_BOMB);
1906 	BecomeExplosion2 (self);
1907 }
1908 
misc_viper_bomb_prethink(edict_t * self)1909 void misc_viper_bomb_prethink (edict_t *self)
1910 {
1911 	vec3_t	v;
1912 	float	diff;
1913 
1914 	self->groundentity = NULL;
1915 
1916 	diff = self->timestamp - level.time;
1917 	if (diff < -1.0)
1918 		diff = -1.0;
1919 
1920 	VectorScale (self->moveinfo.dir, 1.0 + diff, v);
1921 	v[2] = diff;
1922 
1923 	diff = self->s.angles[2];
1924 	vectoangles (v, self->s.angles);
1925 	self->s.angles[2] = diff + 10;
1926 }
1927 
misc_viper_bomb_use(edict_t * self,edict_t * other,edict_t * activator)1928 void misc_viper_bomb_use (edict_t *self, edict_t *other, edict_t *activator)
1929 {
1930 	edict_t	*viper;
1931 
1932 	self->solid = SOLID_BBOX;
1933 	self->svflags &= ~SVF_NOCLIENT;
1934 	self->s.effects |= EF_ROCKET;
1935 	self->use = NULL;
1936 	self->movetype = MOVETYPE_TOSS;
1937 	self->prethink = misc_viper_bomb_prethink;
1938 	self->touch = misc_viper_bomb_touch;
1939 	self->activator = activator;
1940 
1941 	viper = G_Find (NULL, FOFS(classname), "misc_viper");
1942 	VectorScale (viper->moveinfo.dir, viper->moveinfo.speed, self->velocity);
1943 
1944 	self->timestamp = level.time;
1945 	VectorCopy (viper->moveinfo.dir, self->moveinfo.dir);
1946 }
1947 
SP_misc_viper_bomb(edict_t * self)1948 void SP_misc_viper_bomb (edict_t *self)
1949 {
1950 	self->movetype = MOVETYPE_NONE;
1951 	self->solid = SOLID_NOT;
1952 	VectorSet (self->mins, -8, -8, -8);
1953 	VectorSet (self->maxs, 8, 8, 8);
1954 
1955 	self->s.modelindex = gi.modelindex ("models/objects/bomb/tris.md2");
1956 
1957 	if (!self->dmg)
1958 		self->dmg = 1000;
1959 
1960 	self->use = misc_viper_bomb_use;
1961 	self->svflags |= SVF_NOCLIENT;
1962 
1963 	gi.linkentity (self);
1964 }
1965 
1966 
1967 /*QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32)
1968 This is a Storgg ship for the flybys.
1969 It is trigger_spawned, so you must have something use it for it to show up.
1970 There must be a path for it to follow once it is activated.
1971 
1972 "speed"		How fast it should fly
1973 */
1974 
1975 extern void train_use (edict_t *self, edict_t *other, edict_t *activator);
1976 extern void func_train_find (edict_t *self);
1977 
misc_strogg_ship_use(edict_t * self,edict_t * other,edict_t * activator)1978 void misc_strogg_ship_use  (edict_t *self, edict_t *other, edict_t *activator)
1979 {
1980 	self->svflags &= ~SVF_NOCLIENT;
1981 	self->use = train_use;
1982 	train_use (self, other, activator);
1983 }
1984 
SP_misc_strogg_ship(edict_t * ent)1985 void SP_misc_strogg_ship (edict_t *ent)
1986 {
1987 	if (!ent->target)
1988 	{
1989 		gi.dprintf ("%s without a target at %s\n", ent->classname, vtos(ent->absmin));
1990 		G_FreeEdict (ent);
1991 		return;
1992 	}
1993 
1994 	if (!ent->speed)
1995 		ent->speed = 300;
1996 
1997 	ent->movetype = MOVETYPE_PUSH;
1998 	ent->solid = SOLID_NOT;
1999 	ent->s.modelindex = gi.modelindex ("models/ships/strogg1/tris.md2");
2000 	VectorSet (ent->mins, -16, -16, 0);
2001 	VectorSet (ent->maxs, 16, 16, 32);
2002 
2003 	ent->think = func_train_find;
2004 	ent->nextthink = level.time + FRAMETIME;
2005 	ent->use = misc_strogg_ship_use;
2006 	ent->svflags |= SVF_NOCLIENT;
2007 	ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
2008 
2009 	gi.linkentity (ent);
2010 }
2011 
2012 
2013 /*QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128)
2014 */
misc_satellite_dish_think(edict_t * self)2015 void misc_satellite_dish_think (edict_t *self)
2016 {
2017 	self->s.frame++;
2018 	if (self->s.frame < 38)
2019 		self->nextthink = level.time + FRAMETIME;
2020 }
2021 
misc_satellite_dish_use(edict_t * self,edict_t * other,edict_t * activator)2022 void misc_satellite_dish_use (edict_t *self, edict_t *other, edict_t *activator)
2023 {
2024 	self->s.frame = 0;
2025 	self->think = misc_satellite_dish_think;
2026 	self->nextthink = level.time + FRAMETIME;
2027 }
2028 
SP_misc_satellite_dish(edict_t * ent)2029 void SP_misc_satellite_dish (edict_t *ent)
2030 {
2031 	ent->movetype = MOVETYPE_NONE;
2032 	ent->solid = SOLID_BBOX;
2033 	VectorSet (ent->mins, -64, -64, 0);
2034 	VectorSet (ent->maxs, 64, 64, 128);
2035 	ent->s.modelindex = gi.modelindex ("models/objects/satellite/tris.md2");
2036 	ent->use = misc_satellite_dish_use;
2037 	gi.linkentity (ent);
2038 }
2039 
2040 
2041 /*QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12)
2042 */
2043 
Mine_Light_Break(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)2044 static void Mine_Light_Break (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
2045 {
2046 	int spd, i;
2047 	vec3_t org;
2048 
2049 	if (self->s.modelindex2)
2050 	{
2051 		self->s.modelindex = self->s.modelindex2;
2052 		self->s.frame = 0;
2053 	}
2054 	self->s.effects = 0;
2055 	self->takedamage = DAMAGE_NO;
2056 	self->die = NULL;
2057 
2058 	gi.sound(self, CHAN_AUTO, gi.soundindex("world/spark1.wav"), 1, ATTN_IDLE, 0);
2059 	gi.sound(self, CHAN_VOICE, gi.soundindex("world/brkglas.wav"), 0.7, ATTN_IDLE, 0);
2060 
2061 	spd = 5+(crandom()*10);
2062 	org[0] = self->s.origin[0] + crandom() * 10;
2063 	org[1] = self->s.origin[1] + crandom() * 10;
2064 	org[2] = self->s.origin[2] + crandom() * 10;
2065 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
2066 
2067 	self->think = FadeDie;
2068 	self->nextthink = level.time + 1;
2069 }
2070 
SP_light_mine1(edict_t * ent)2071 void SP_light_mine1 (edict_t *ent)
2072 {
2073 	ent->movetype = MOVETYPE_NONE;
2074 	ent->solid = SOLID_BBOX;
2075 	ent->s.effects = EF_PLASMA;
2076 	ent->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2");
2077 
2078 	if (!sv_serversideonly->value)
2079 	{
2080 		ent->s.modelindex2 = gi.modelindex ("models/objects/minelite/light1/tris.md2");
2081 		ent->s.modelindex = gi.modelindex ("sprites/s_lensflare.sp2");
2082 
2083 		if (!deathmatch->value && !coop->value)
2084 		{
2085 			ent->think = FlareThink;
2086 			ent->nextthink = level.time+FRAMETIME;
2087 		}
2088 	}
2089 
2090 	VectorSet(ent->mins, -8, -8, -20);
2091 	VectorSet(ent->maxs, 8, 8, 20);
2092 	ent->health = 25;
2093 	ent->die = Mine_Light_Break;
2094 	ent->takedamage = DAMAGE_YES;
2095 	ent->monsterinfo.aiflags = AI_NOSTEP;
2096 
2097 	gi.linkentity (ent);
2098 }
2099 
2100 
2101 /*QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12)
2102 */
SP_light_mine2(edict_t * ent)2103 void SP_light_mine2 (edict_t *ent)
2104 {
2105 	ent->movetype = MOVETYPE_NONE;
2106 	ent->solid = SOLID_BBOX;
2107 	ent->s.effects = EF_PLASMA;
2108 	ent->s.modelindex = gi.modelindex ("models/objects/minelite/light2/tris.md2");
2109 
2110 	if (!sv_serversideonly->value)
2111 	{
2112 		ent->s.modelindex2 = gi.modelindex ("models/objects/minelite/light1/tris.md2");
2113 		ent->s.modelindex = gi.modelindex ("sprites/s_lensflare.sp2");
2114 
2115 		if (!deathmatch->value && !coop->value)
2116 		{
2117 			ent->think = FlareThink;
2118 			ent->nextthink = level.time+FRAMETIME;
2119 		}
2120 	}
2121 
2122 	VectorSet(ent->mins, -8, -8, -20);
2123 	VectorSet(ent->maxs, 8, 8, 20);
2124 	ent->health = 25;
2125 	ent->die = Mine_Light_Break;
2126 	ent->takedamage = DAMAGE_YES;
2127 
2128 	gi.linkentity (ent);
2129 }
2130 
2131 
2132 /*QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8)
2133 Intended for use with the target_spawner
2134 */
SP_misc_gib_arm(edict_t * ent)2135 void SP_misc_gib_arm (edict_t *ent)
2136 {
2137 	gi.setmodel (ent, "models/objects/gibs/arm/tris.md2");
2138 	ent->solid = SOLID_NOT;
2139 	ent->s.effects |= EF_GIB;
2140 	ent->takedamage = DAMAGE_YES;
2141 	ent->die = gib_die;
2142 	ent->movetype = MOVETYPE_TOSS;
2143 	ent->svflags |= SVF_MONSTER;
2144 	ent->deadflag = DEAD_DEAD;
2145 	ent->avelocity[0] = random()*200;
2146 	ent->avelocity[1] = random()*200;
2147 	ent->avelocity[2] = random()*200;
2148 	ent->think = G_FreeEdict;
2149 	ent->nextthink = level.time + 30;
2150 	gi.linkentity (ent);
2151 }
2152 
2153 /*QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8)
2154 Intended for use with the target_spawner
2155 */
SP_misc_gib_leg(edict_t * ent)2156 void SP_misc_gib_leg (edict_t *ent)
2157 {
2158 	gi.setmodel (ent, "models/objects/gibs/leg/tris.md2");
2159 	ent->solid = SOLID_NOT;
2160 	ent->s.effects |= EF_GIB;
2161 	ent->takedamage = DAMAGE_YES;
2162 	ent->die = gib_die;
2163 	ent->movetype = MOVETYPE_TOSS;
2164 	ent->svflags |= SVF_MONSTER;
2165 	ent->deadflag = DEAD_DEAD;
2166 	ent->avelocity[0] = random()*200;
2167 	ent->avelocity[1] = random()*200;
2168 	ent->avelocity[2] = random()*200;
2169 	ent->think = G_FreeEdict;
2170 	ent->nextthink = level.time + 30;
2171 	gi.linkentity (ent);
2172 }
2173 
2174 /*QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8)
2175 Intended for use with the target_spawner
2176 */
SP_misc_gib_head(edict_t * ent)2177 void SP_misc_gib_head (edict_t *ent)
2178 {
2179 	gi.setmodel (ent, "models/objects/gibs/head/tris.md2");
2180 	ent->solid = SOLID_NOT;
2181 	ent->s.effects |= EF_GIB;
2182 	ent->takedamage = DAMAGE_YES;
2183 	ent->die = gib_die;
2184 	ent->movetype = MOVETYPE_TOSS;
2185 	ent->svflags |= SVF_MONSTER;
2186 	ent->deadflag = DEAD_DEAD;
2187 	ent->avelocity[0] = random()*200;
2188 	ent->avelocity[1] = random()*200;
2189 	ent->avelocity[2] = random()*200;
2190 	ent->think = G_FreeEdict;
2191 	ent->nextthink = level.time + 30;
2192 	gi.linkentity (ent);
2193 }
2194 
2195 //=====================================================
2196 
2197 /*QUAKED target_character (0 0 1) ?
2198 used with target_string (must be on same "team")
2199 "count" is position in the string (starts at 1)
2200 */
2201 
SP_target_character(edict_t * self)2202 void SP_target_character (edict_t *self)
2203 {
2204 	self->movetype = MOVETYPE_PUSH;
2205 	gi.setmodel (self, self->model);
2206 	self->solid = SOLID_BSP;
2207 	self->s.frame = 12;
2208 	gi.linkentity (self);
2209 	return;
2210 }
2211 
2212 
2213 /*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8)
2214 */
2215 
target_string_use(edict_t * self,edict_t * other,edict_t * activator)2216 void target_string_use (edict_t *self, edict_t *other, edict_t *activator)
2217 {
2218 	edict_t *e;
2219 	int		n, l;
2220 	char	c;
2221 
2222 	l = strlen(self->message);
2223 	for (e = self->teammaster; e; e = e->teamchain)
2224 	{
2225 		if (!e->count)
2226 			continue;
2227 		n = e->count - 1;
2228 		if (n > l)
2229 		{
2230 			e->s.frame = 12;
2231 			continue;
2232 		}
2233 
2234 		c = self->message[n];
2235 		if (c >= '0' && c <= '9')
2236 			e->s.frame = c - '0';
2237 		else if (c == '-')
2238 			e->s.frame = 10;
2239 		else if (c == ':')
2240 			e->s.frame = 11;
2241 		else
2242 			e->s.frame = 12;
2243 	}
2244 }
2245 
SP_target_string(edict_t * self)2246 void SP_target_string (edict_t *self)
2247 {
2248 	if (!self->message)
2249 		self->message = "";
2250 	self->use = target_string_use;
2251 }
2252 
2253 
2254 /*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE
2255 target a target_string with this
2256 
2257 The default is to be a time of day clock
2258 
2259 TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget"
2260 If START_OFF, this entity must be used before it starts
2261 
2262 "style"		0 "xx"
2263 			1 "xx:xx"
2264 			2 "xx:xx:xx"
2265 */
2266 
2267 #define	CLOCK_MESSAGE_SIZE	16
2268 
2269 // don't let field width of any clock messages change, or it
2270 // could cause an overwrite after a game load
2271 
func_clock_reset(edict_t * self)2272 static void func_clock_reset (edict_t *self)
2273 {
2274 	self->activator = NULL;
2275 	if (self->spawnflags & 1)
2276 	{
2277 		self->health = 0;
2278 		self->wait = self->count;
2279 	}
2280 	else if (self->spawnflags & 2)
2281 	{
2282 		self->health = self->count;
2283 		self->wait = 0;
2284 	}
2285 }
2286 
func_clock_format_countdown(edict_t * self)2287 static void func_clock_format_countdown (edict_t *self)
2288 {
2289 	if (self->style == 0)
2290 	{
2291 		Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health);
2292 		return;
2293 	}
2294 
2295 	if (self->style == 1)
2296 	{
2297 		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60);
2298 		if (self->message[3] == ' ')
2299 			self->message[3] = '0';
2300 		return;
2301 	}
2302 
2303 	if (self->style == 2)
2304 	{
2305 		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60);
2306 		if (self->message[3] == ' ')
2307 			self->message[3] = '0';
2308 		if (self->message[6] == ' ')
2309 			self->message[6] = '0';
2310 		return;
2311 	}
2312 }
2313 
func_clock_think(edict_t * self)2314 void func_clock_think (edict_t *self)
2315 {
2316 	if (!self->enemy)
2317 	{
2318 		self->enemy = G_Find (NULL, FOFS(targetname), self->target);
2319 		if (!self->enemy)
2320 			return;
2321 	}
2322 
2323 	if (self->spawnflags & 1)
2324 	{
2325 		func_clock_format_countdown (self);
2326 		self->health++;
2327 	}
2328 	else if (self->spawnflags & 2)
2329 	{
2330 		func_clock_format_countdown (self);
2331 		self->health--;
2332 	}
2333 	else
2334 	{
2335 		struct tm	*ltime;
2336 		time_t		gmtime;
2337 
2338 		time(&gmtime);
2339 		ltime = localtime(&gmtime);
2340 		Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec);
2341 		if (self->message[3] == ' ')
2342 			self->message[3] = '0';
2343 		if (self->message[6] == ' ')
2344 			self->message[6] = '0';
2345 	}
2346 
2347 	self->enemy->message = self->message;
2348 	self->enemy->use (self->enemy, self, self);
2349 
2350 	if (((self->spawnflags & 1) && (self->health > self->wait)) ||
2351 		((self->spawnflags & 2) && (self->health < self->wait)))
2352 	{
2353 		if (self->pathtarget)
2354 		{
2355 			char *savetarget;
2356 			char *savemessage;
2357 
2358 			savetarget = self->target;
2359 			savemessage = self->message;
2360 			self->target = self->pathtarget;
2361 			self->message = NULL;
2362 			G_UseTargets (self, self->activator);
2363 			self->target = savetarget;
2364 			self->message = savemessage;
2365 		}
2366 
2367 		if (!(self->spawnflags & 8))
2368 			return;
2369 
2370 		func_clock_reset (self);
2371 
2372 		if (self->spawnflags & 4)
2373 			return;
2374 	}
2375 
2376 	self->nextthink = level.time + 1;
2377 }
2378 
func_clock_use(edict_t * self,edict_t * other,edict_t * activator)2379 void func_clock_use (edict_t *self, edict_t *other, edict_t *activator)
2380 {
2381 	if (!(self->spawnflags & 8))
2382 		self->use = NULL;
2383 	if (self->activator)
2384 		return;
2385 	self->activator = activator;
2386 	self->think (self);
2387 }
2388 
SP_func_clock(edict_t * self)2389 void SP_func_clock (edict_t *self)
2390 {
2391 	if (!self->target)
2392 	{
2393 		gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
2394 		G_FreeEdict (self);
2395 		return;
2396 	}
2397 
2398 	if ((self->spawnflags & 2) && (!self->count))
2399 	{
2400 		gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin));
2401 		G_FreeEdict (self);
2402 		return;
2403 	}
2404 
2405 	if ((self->spawnflags & 1) && (!self->count))
2406 		self->count = 60*60;;
2407 
2408 	func_clock_reset (self);
2409 
2410 	self->message = gi.TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL);
2411 
2412 	self->think = func_clock_think;
2413 
2414 	if (self->spawnflags & 4)
2415 		self->use = func_clock_use;
2416 	else
2417 		self->nextthink = level.time + 1;
2418 }
2419 
2420 //=================================================================================
2421 
teleporter_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)2422 void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
2423 {
2424 	edict_t		*dest;
2425 	int			i;
2426 
2427 	if (!other->client)
2428 		return;
2429 	dest = G_Find (NULL, FOFS(targetname), self->target);
2430 	if (!dest)
2431 	{
2432 		gi.dprintf ("Couldn't find destination\n");
2433 		return;
2434 	}
2435 
2436 	// unlink to make sure it can't possibly interfere with KillBox
2437 	gi.unlinkentity (other);
2438 
2439 	VectorCopy (dest->s.origin, other->s.origin);
2440 	VectorCopy (dest->s.origin, other->s.old_origin);
2441 	other->s.origin[2] += 10;
2442 
2443 	// clear the velocity and hold them in place briefly
2444 	VectorClear (other->velocity);
2445 	other->client->ps.pmove.pm_time = 160>>3;		// hold time
2446 	other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
2447 
2448 	// draw the teleport splash at source and on the player
2449 	self->owner->s.event = EV_PLAYER_TELEPORT;
2450 	other->s.event = EV_PLAYER_TELEPORT;
2451 
2452 	// set angles
2453 	for (i=0 ; i<3 ; i++)
2454 	{
2455 		other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
2456 	}
2457 
2458 	VectorClear (other->s.angles);
2459 	VectorClear (other->client->ps.viewangles);
2460 	VectorClear (other->client->v_angle);
2461 
2462 	// kill anything at the destination
2463 	KillBox (other);
2464 
2465 	gi.linkentity (other);
2466 }
2467 
2468 /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
2469 Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
2470 */
SP_misc_teleporter(edict_t * ent)2471 void SP_misc_teleporter (edict_t *ent)
2472 {
2473 	edict_t		*trig;
2474 
2475 	if (!ent->target)
2476 	{
2477 		gi.dprintf ("teleporter without a target.\n");
2478 		G_FreeEdict (ent);
2479 		return;
2480 	}
2481 
2482 	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
2483 	ent->s.skinnum = 1;
2484 	ent->s.effects = EF_TELEPORTER;
2485 	ent->s.sound = gi.soundindex ("world/amb10.wav");
2486 	ent->solid = SOLID_BBOX;
2487 
2488 	VectorSet (ent->mins, -32, -32, -24);
2489 	VectorSet (ent->maxs, 32, 32, -16);
2490 	gi.linkentity (ent);
2491 
2492 	trig = G_Spawn ();
2493 	trig->touch = teleporter_touch;
2494 
2495 	trig->solid = SOLID_TRIGGER;
2496 
2497 
2498 	trig->target = ent->target;
2499 	trig->owner = ent;
2500 	VectorCopy (ent->s.origin, trig->s.origin);
2501 	VectorSet (trig->mins, -8, -8, 8);
2502 	VectorSet (trig->maxs, 8, 8, 24);
2503 	gi.linkentity (trig);
2504 
2505 }
2506 
2507 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
2508 Point teleporters at these.
2509 */
2510 
2511 //sv_spawner->value
2512 
SP_misc_teleporter_dest(edict_t * ent)2513 void SP_misc_teleporter_dest (edict_t *ent)
2514 {
2515 	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
2516 	ent->s.skinnum = 0;
2517 	ent->solid = SOLID_BBOX;
2518 //	ent->s.effects |= EF_FLIES;
2519 	VectorSet (ent->mins, -32, -32, -24);
2520 	VectorSet (ent->maxs, 32, 32, -16);
2521 	gi.linkentity (ent);
2522 }
2523 
SP_misc_spawn_dest(edict_t * ent)2524 void SP_misc_spawn_dest (edict_t *ent)
2525 {
2526 
2527 	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
2528 	ent->s.skinnum = 1;
2529 
2530 	if (sv_spawner->value<2)
2531 		ent->solid = SOLID_NOT;
2532 
2533 	if (sv_spawner->value==1)
2534 	{
2535 		ent->s.renderfx = RF_GLOW;
2536 		ent->s.effects = EF_SPHERETRANS;
2537 	}
2538 	else if (sv_spawner->value==0)
2539 	{
2540 		ent->s.renderfx = RF_BEAM;
2541 	}
2542 	else
2543 		ent->solid = SOLID_BBOX;
2544 
2545 	VectorSet (ent->mins, -32, -32, -24);
2546 	VectorSet (ent->maxs, 32, 32, -16);
2547 	gi.linkentity (ent);
2548 }