Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

g_misc.c (45231B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // g_misc.c
     21 
     22 #include "g_local.h"
     23 
     24 
     25 /*QUAKED func_group (0 0 0) ?
     26 Used to group brushes together just for editor convenience.
     27 */
     28 
     29 //=====================================================
     30 
     31 void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator)
     32 {
     33 	ent->count ^= 1;		// toggle state
     34 //	gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count);
     35 	gi.SetAreaPortalState (ent->style, ent->count);
     36 }
     37 
     38 /*QUAKED func_areaportal (0 0 0) ?
     39 
     40 This is a non-visible object that divides the world into
     41 areas that are seperated when this portal is not activated.
     42 Usually enclosed in the middle of a door.
     43 */
     44 void SP_func_areaportal (edict_t *ent)
     45 {
     46 	ent->use = Use_Areaportal;
     47 	ent->count = 0;		// always start closed;
     48 }
     49 
     50 //=====================================================
     51 
     52 
     53 /*
     54 =================
     55 Misc functions
     56 =================
     57 */
     58 void VelocityForDamage (int damage, vec3_t v)
     59 {
     60 	v[0] = 100.0 * crandom();
     61 	v[1] = 100.0 * crandom();
     62 	v[2] = 200.0 + 100.0 * random();
     63 
     64 	if (damage < 50)
     65 		VectorScale (v, 0.7, v);
     66 	else 
     67 		VectorScale (v, 1.2, v);
     68 }
     69 
     70 void ClipGibVelocity (edict_t *ent)
     71 {
     72 	if (ent->velocity[0] < -300)
     73 		ent->velocity[0] = -300;
     74 	else if (ent->velocity[0] > 300)
     75 		ent->velocity[0] = 300;
     76 	if (ent->velocity[1] < -300)
     77 		ent->velocity[1] = -300;
     78 	else if (ent->velocity[1] > 300)
     79 		ent->velocity[1] = 300;
     80 	if (ent->velocity[2] < 200)
     81 		ent->velocity[2] = 200;	// always some upwards
     82 	else if (ent->velocity[2] > 500)
     83 		ent->velocity[2] = 500;
     84 }
     85 
     86 
     87 /*
     88 =================
     89 gibs
     90 =================
     91 */
     92 void gib_think (edict_t *self)
     93 {
     94 	self->s.frame++;
     95 	self->nextthink = level.time + FRAMETIME;
     96 
     97 	if (self->s.frame == 10)
     98 	{
     99 		self->think = G_FreeEdict;
    100 		self->nextthink = level.time + 8 + random()*10;
    101 	}
    102 }
    103 
    104 void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
    105 {
    106 	vec3_t	normal_angles, right;
    107 
    108 	if (!self->groundentity)
    109 		return;
    110 
    111 	self->touch = NULL;
    112 
    113 	if (plane)
    114 	{
    115 		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
    116 
    117 		vectoangles (plane->normal, normal_angles);
    118 		AngleVectors (normal_angles, NULL, right, NULL);
    119 		vectoangles (right, self->s.angles);
    120 
    121 		if (self->s.modelindex == sm_meat_index)
    122 		{
    123 			self->s.frame++;
    124 			self->think = gib_think;
    125 			self->nextthink = level.time + FRAMETIME;
    126 		}
    127 	}
    128 }
    129 
    130 void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
    131 {
    132 	G_FreeEdict (self);
    133 }
    134 
    135 void ThrowGib (edict_t *self, char *gibname, int damage, int type)
    136 {
    137 	edict_t *gib;
    138 	vec3_t	vd;
    139 	vec3_t	origin;
    140 	vec3_t	size;
    141 	float	vscale;
    142 
    143 	gib = G_Spawn();
    144 
    145 	VectorScale (self->size, 0.5, size);
    146 	VectorAdd (self->absmin, size, origin);
    147 	gib->s.origin[0] = origin[0] + crandom() * size[0];
    148 	gib->s.origin[1] = origin[1] + crandom() * size[1];
    149 	gib->s.origin[2] = origin[2] + crandom() * size[2];
    150 
    151 	gi.setmodel (gib, gibname);
    152 	gib->solid = SOLID_NOT;
    153 	gib->s.effects |= EF_GIB;
    154 	gib->flags |= FL_NO_KNOCKBACK;
    155 	gib->takedamage = DAMAGE_YES;
    156 	gib->die = gib_die;
    157 
    158 	if (type == GIB_ORGANIC)
    159 	{
    160 		gib->movetype = MOVETYPE_TOSS;
    161 		gib->touch = gib_touch;
    162 		vscale = 0.5;
    163 	}
    164 	else
    165 	{
    166 		gib->movetype = MOVETYPE_BOUNCE;
    167 		vscale = 1.0;
    168 	}
    169 
    170 	VelocityForDamage (damage, vd);
    171 	VectorMA (self->velocity, vscale, vd, gib->velocity);
    172 	ClipGibVelocity (gib);
    173 	gib->avelocity[0] = random()*600;
    174 	gib->avelocity[1] = random()*600;
    175 	gib->avelocity[2] = random()*600;
    176 
    177 	gib->think = G_FreeEdict;
    178 	gib->nextthink = level.time + 10 + random()*10;
    179 
    180 	gi.linkentity (gib);
    181 }
    182 
    183 void ThrowHead (edict_t *self, char *gibname, int damage, int type)
    184 {
    185 	vec3_t	vd;
    186 	float	vscale;
    187 
    188 	self->s.skinnum = 0;
    189 	self->s.frame = 0;
    190 	VectorClear (self->mins);
    191 	VectorClear (self->maxs);
    192 
    193 	self->s.modelindex2 = 0;
    194 	gi.setmodel (self, gibname);
    195 	self->solid = SOLID_NOT;
    196 	self->s.effects |= EF_GIB;
    197 	self->s.effects &= ~EF_FLIES;
    198 	self->s.sound = 0;
    199 	self->flags |= FL_NO_KNOCKBACK;
    200 	self->svflags &= ~SVF_MONSTER;
    201 	self->takedamage = DAMAGE_YES;
    202 	self->die = gib_die;
    203 
    204 	if (type == GIB_ORGANIC)
    205 	{
    206 		self->movetype = MOVETYPE_TOSS;
    207 		self->touch = gib_touch;
    208 		vscale = 0.5;
    209 	}
    210 	else
    211 	{
    212 		self->movetype = MOVETYPE_BOUNCE;
    213 		vscale = 1.0;
    214 	}
    215 
    216 	VelocityForDamage (damage, vd);
    217 	VectorMA (self->velocity, vscale, vd, self->velocity);
    218 	ClipGibVelocity (self);
    219 
    220 	self->avelocity[YAW] = crandom()*600;
    221 
    222 	self->think = G_FreeEdict;
    223 	self->nextthink = level.time + 10 + random()*10;
    224 
    225 	gi.linkentity (self);
    226 }
    227 
    228 
    229 void ThrowClientHead (edict_t *self, int damage)
    230 {
    231 	vec3_t	vd;
    232 	char	*gibname;
    233 
    234 	if (rand()&1)
    235 	{
    236 		gibname = "models/objects/gibs/head2/tris.md2";
    237 		self->s.skinnum = 1;		// second skin is player
    238 	}
    239 	else
    240 	{
    241 		gibname = "models/objects/gibs/skull/tris.md2";
    242 		self->s.skinnum = 0;
    243 	}
    244 
    245 	self->s.origin[2] += 32;
    246 	self->s.frame = 0;
    247 	gi.setmodel (self, gibname);
    248 	VectorSet (self->mins, -16, -16, 0);
    249 	VectorSet (self->maxs, 16, 16, 16);
    250 
    251 	self->takedamage = DAMAGE_NO;
    252 	self->solid = SOLID_NOT;
    253 	self->s.effects = EF_GIB;
    254 	self->s.sound = 0;
    255 	self->flags |= FL_NO_KNOCKBACK;
    256 
    257 	self->movetype = MOVETYPE_BOUNCE;
    258 	VelocityForDamage (damage, vd);
    259 	VectorAdd (self->velocity, vd, self->velocity);
    260 
    261 	if (self->client)	// bodies in the queue don't have a client anymore
    262 	{
    263 		self->client->anim_priority = ANIM_DEATH;
    264 		self->client->anim_end = self->s.frame;
    265 	}
    266 	else
    267 	{
    268 		self->think = NULL;
    269 		self->nextthink = 0;
    270 	}
    271 
    272 	gi.linkentity (self);
    273 }
    274 
    275 
    276 /*
    277 =================
    278 debris
    279 =================
    280 */
    281 void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
    282 {
    283 	G_FreeEdict (self);
    284 }
    285 
    286 void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin)
    287 {
    288 	edict_t	*chunk;
    289 	vec3_t	v;
    290 
    291 	chunk = G_Spawn();
    292 	VectorCopy (origin, chunk->s.origin);
    293 	gi.setmodel (chunk, modelname);
    294 	v[0] = 100 * crandom();
    295 	v[1] = 100 * crandom();
    296 	v[2] = 100 + 100 * crandom();
    297 	VectorMA (self->velocity, speed, v, chunk->velocity);
    298 	chunk->movetype = MOVETYPE_BOUNCE;
    299 	chunk->solid = SOLID_NOT;
    300 	chunk->avelocity[0] = random()*600;
    301 	chunk->avelocity[1] = random()*600;
    302 	chunk->avelocity[2] = random()*600;
    303 	chunk->think = G_FreeEdict;
    304 	chunk->nextthink = level.time + 5 + random()*5;
    305 	chunk->s.frame = 0;
    306 	chunk->flags = 0;
    307 	chunk->classname = "debris";
    308 	chunk->takedamage = DAMAGE_YES;
    309 	chunk->die = debris_die;
    310 	gi.linkentity (chunk);
    311 }
    312 
    313 
    314 void BecomeExplosion1 (edict_t *self)
    315 {
    316 	gi.WriteByte (svc_temp_entity);
    317 	gi.WriteByte (TE_EXPLOSION1);
    318 	gi.WritePosition (self->s.origin);
    319 	gi.multicast (self->s.origin, MULTICAST_PVS);
    320 
    321 	G_FreeEdict (self);
    322 }
    323 
    324 
    325 void BecomeExplosion2 (edict_t *self)
    326 {
    327 	gi.WriteByte (svc_temp_entity);
    328 	gi.WriteByte (TE_EXPLOSION2);
    329 	gi.WritePosition (self->s.origin);
    330 	gi.multicast (self->s.origin, MULTICAST_PVS);
    331 
    332 	G_FreeEdict (self);
    333 }
    334 
    335 
    336 /*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT
    337 Target: next path corner
    338 Pathtarget: gets used when an entity that has
    339 	this path_corner targeted touches it
    340 */
    341 
    342 void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
    343 {
    344 	vec3_t		v;
    345 	edict_t		*next;
    346 
    347 	if (other->movetarget != self)
    348 		return;
    349 	
    350 	if (other->enemy)
    351 		return;
    352 
    353 	if (self->pathtarget)
    354 	{
    355 		char *savetarget;
    356 
    357 		savetarget = self->target;
    358 		self->target = self->pathtarget;
    359 		G_UseTargets (self, other);
    360 		self->target = savetarget;
    361 	}
    362 
    363 	if (self->target)
    364 		next = G_PickTarget(self->target);
    365 	else
    366 		next = NULL;
    367 
    368 	if ((next) && (next->spawnflags & 1))
    369 	{
    370 		VectorCopy (next->s.origin, v);
    371 		v[2] += next->mins[2];
    372 		v[2] -= other->mins[2];
    373 		VectorCopy (v, other->s.origin);
    374 		next = G_PickTarget(next->target);
    375 		other->s.event = EV_OTHER_TELEPORT;
    376 	}
    377 
    378 	other->goalentity = other->movetarget = next;
    379 
    380 	if (self->wait)
    381 	{
    382 		other->monsterinfo.pausetime = level.time + self->wait;
    383 		other->monsterinfo.stand (other);
    384 		return;
    385 	}
    386 
    387 	if (!other->movetarget)
    388 	{
    389 		other->monsterinfo.pausetime = level.time + 100000000;
    390 		other->monsterinfo.stand (other);
    391 	}
    392 	else
    393 	{
    394 		VectorSubtract (other->goalentity->s.origin, other->s.origin, v);
    395 		other->ideal_yaw = vectoyaw (v);
    396 	}
    397 }
    398 
    399 void SP_path_corner (edict_t *self)
    400 {
    401 	if (!self->targetname)
    402 	{
    403 		gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin));
    404 		G_FreeEdict (self);
    405 		return;
    406 	}
    407 
    408 	self->solid = SOLID_TRIGGER;
    409 	self->touch = path_corner_touch;
    410 	VectorSet (self->mins, -8, -8, -8);
    411 	VectorSet (self->maxs, 8, 8, 8);
    412 	self->svflags |= SVF_NOCLIENT;
    413 	gi.linkentity (self);
    414 }
    415 
    416 
    417 /*QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold
    418 Makes this the target of a monster and it will head here
    419 when first activated before going after the activator.  If
    420 hold is selected, it will stay here.
    421 */
    422 void point_combat_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
    423 {
    424 	edict_t	*activator;
    425 
    426 	if (other->movetarget != self)
    427 		return;
    428 
    429 	if (self->target)
    430 	{
    431 		other->target = self->target;
    432 		other->goalentity = other->movetarget = G_PickTarget(other->target);
    433 		if (!other->goalentity)
    434 		{
    435 			gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target);
    436 			other->movetarget = self;
    437 		}
    438 		self->target = NULL;
    439 	}
    440 	else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM|FL_FLY)))
    441 	{
    442 		other->monsterinfo.pausetime = level.time + 100000000;
    443 		other->monsterinfo.aiflags |= AI_STAND_GROUND;
    444 		other->monsterinfo.stand (other);
    445 	}
    446 
    447 	if (other->movetarget == self)
    448 	{
    449 		other->target = NULL;
    450 		other->movetarget = NULL;
    451 		other->goalentity = other->enemy;
    452 		other->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
    453 	}
    454 
    455 	if (self->pathtarget)
    456 	{
    457 		char *savetarget;
    458 
    459 		savetarget = self->target;
    460 		self->target = self->pathtarget;
    461 		if (other->enemy && other->enemy->client)
    462 			activator = other->enemy;
    463 		else if (other->oldenemy && other->oldenemy->client)
    464 			activator = other->oldenemy;
    465 		else if (other->activator && other->activator->client)
    466 			activator = other->activator;
    467 		else
    468 			activator = other;
    469 		G_UseTargets (self, activator);
    470 		self->target = savetarget;
    471 	}
    472 }
    473 
    474 void SP_point_combat (edict_t *self)
    475 {
    476 	if (deathmatch->value)
    477 	{
    478 		G_FreeEdict (self);
    479 		return;
    480 	}
    481 	self->solid = SOLID_TRIGGER;
    482 	self->touch = point_combat_touch;
    483 	VectorSet (self->mins, -8, -8, -16);
    484 	VectorSet (self->maxs, 8, 8, 16);
    485 	self->svflags = SVF_NOCLIENT;
    486 	gi.linkentity (self);
    487 };
    488 
    489 
    490 /*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
    491 Just for the debugging level.  Don't use
    492 */
    493 void TH_viewthing(edict_t *ent)
    494 {
    495 	ent->s.frame = (ent->s.frame + 1) % 7;
    496 	ent->nextthink = level.time + FRAMETIME;
    497 }
    498 
    499 void SP_viewthing(edict_t *ent)
    500 {
    501 	gi.dprintf ("viewthing spawned\n");
    502 
    503 	ent->movetype = MOVETYPE_NONE;
    504 	ent->solid = SOLID_BBOX;
    505 	ent->s.renderfx = RF_FRAMELERP;
    506 	VectorSet (ent->mins, -16, -16, -24);
    507 	VectorSet (ent->maxs, 16, 16, 32);
    508 	ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
    509 	gi.linkentity (ent);
    510 	ent->nextthink = level.time + 0.5;
    511 	ent->think = TH_viewthing;
    512 	return;
    513 }
    514 
    515 
    516 /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
    517 Used as a positional target for spotlights, etc.
    518 */
    519 void SP_info_null (edict_t *self)
    520 {
    521 	G_FreeEdict (self);
    522 };
    523 
    524 
    525 /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
    526 Used as a positional target for lightning.
    527 */
    528 void SP_info_notnull (edict_t *self)
    529 {
    530 	VectorCopy (self->s.origin, self->absmin);
    531 	VectorCopy (self->s.origin, self->absmax);
    532 };
    533 
    534 
    535 /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF
    536 Non-displayed light.
    537 Default light value is 300.
    538 Default style is 0.
    539 If targeted, will toggle between on and off.
    540 Default _cone value is 10 (used to set size of light for spotlights)
    541 */
    542 
    543 #define START_OFF	1
    544 
    545 static void light_use (edict_t *self, edict_t *other, edict_t *activator)
    546 {
    547 	if (self->spawnflags & START_OFF)
    548 	{
    549 		gi.configstring (CS_LIGHTS+self->style, "m");
    550 		self->spawnflags &= ~START_OFF;
    551 	}
    552 	else
    553 	{
    554 		gi.configstring (CS_LIGHTS+self->style, "a");
    555 		self->spawnflags |= START_OFF;
    556 	}
    557 }
    558 
    559 void SP_light (edict_t *self)
    560 {
    561 	// no targeted lights in deathmatch, because they cause global messages
    562 	if (!self->targetname || deathmatch->value)
    563 	{
    564 		G_FreeEdict (self);
    565 		return;
    566 	}
    567 
    568 	if (self->style >= 32)
    569 	{
    570 		self->use = light_use;
    571 		if (self->spawnflags & START_OFF)
    572 			gi.configstring (CS_LIGHTS+self->style, "a");
    573 		else
    574 			gi.configstring (CS_LIGHTS+self->style, "m");
    575 	}
    576 }
    577 
    578 
    579 /*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
    580 This is just a solid wall if not inhibited
    581 
    582 TRIGGER_SPAWN	the wall will not be present until triggered
    583 				it will then blink in to existance; it will
    584 				kill anything that was in it's way
    585 
    586 TOGGLE			only valid for TRIGGER_SPAWN walls
    587 				this allows the wall to be turned on and off
    588 
    589 START_ON		only valid for TRIGGER_SPAWN walls
    590 				the wall will initially be present
    591 */
    592 
    593 void func_wall_use (edict_t *self, edict_t *other, edict_t *activator)
    594 {
    595 	if (self->solid == SOLID_NOT)
    596 	{
    597 		self->solid = SOLID_BSP;
    598 		self->svflags &= ~SVF_NOCLIENT;
    599 		KillBox (self);
    600 	}
    601 	else
    602 	{
    603 		self->solid = SOLID_NOT;
    604 		self->svflags |= SVF_NOCLIENT;
    605 	}
    606 	gi.linkentity (self);
    607 
    608 	if (!(self->spawnflags & 2))
    609 		self->use = NULL;
    610 }
    611 
    612 void SP_func_wall (edict_t *self)
    613 {
    614 	self->movetype = MOVETYPE_PUSH;
    615 	gi.setmodel (self, self->model);
    616 
    617 	if (self->spawnflags & 8)
    618 		self->s.effects |= EF_ANIM_ALL;
    619 	if (self->spawnflags & 16)
    620 		self->s.effects |= EF_ANIM_ALLFAST;
    621 
    622 	// just a wall
    623 	if ((self->spawnflags & 7) == 0)
    624 	{
    625 		self->solid = SOLID_BSP;
    626 		gi.linkentity (self);
    627 		return;
    628 	}
    629 
    630 	// it must be TRIGGER_SPAWN
    631 	if (!(self->spawnflags & 1))
    632 	{
    633 //		gi.dprintf("func_wall missing TRIGGER_SPAWN\n");
    634 		self->spawnflags |= 1;
    635 	}
    636 
    637 	// yell if the spawnflags are odd
    638 	if (self->spawnflags & 4)
    639 	{
    640 		if (!(self->spawnflags & 2))
    641 		{
    642 			gi.dprintf("func_wall START_ON without TOGGLE\n");
    643 			self->spawnflags |= 2;
    644 		}
    645 	}
    646 
    647 	self->use = func_wall_use;
    648 	if (self->spawnflags & 4)
    649 	{
    650 		self->solid = SOLID_BSP;
    651 	}
    652 	else
    653 	{
    654 		self->solid = SOLID_NOT;
    655 		self->svflags |= SVF_NOCLIENT;
    656 	}
    657 	gi.linkentity (self);
    658 }
    659 
    660 
    661 /*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
    662 This is solid bmodel that will fall if it's support it removed.
    663 */
    664 
    665 void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
    666 {
    667 	// only squash thing we fall on top of
    668 	if (!plane)
    669 		return;
    670 	if (plane->normal[2] < 1.0)
    671 		return;
    672 	if (other->takedamage == DAMAGE_NO)
    673 		return;
    674 	T_Damage (other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
    675 }
    676 
    677 void func_object_release (edict_t *self)
    678 {
    679 	self->movetype = MOVETYPE_TOSS;
    680 	self->touch = func_object_touch;
    681 }
    682 
    683 void func_object_use (edict_t *self, edict_t *other, edict_t *activator)
    684 {
    685 	self->solid = SOLID_BSP;
    686 	self->svflags &= ~SVF_NOCLIENT;
    687 	self->use = NULL;
    688 	KillBox (self);
    689 	func_object_release (self);
    690 }
    691 
    692 void SP_func_object (edict_t *self)
    693 {
    694 	gi.setmodel (self, self->model);
    695 
    696 	self->mins[0] += 1;
    697 	self->mins[1] += 1;
    698 	self->mins[2] += 1;
    699 	self->maxs[0] -= 1;
    700 	self->maxs[1] -= 1;
    701 	self->maxs[2] -= 1;
    702 
    703 	if (!self->dmg)
    704 		self->dmg = 100;
    705 
    706 	if (self->spawnflags == 0)
    707 	{
    708 		self->solid = SOLID_BSP;
    709 		self->movetype = MOVETYPE_PUSH;
    710 		self->think = func_object_release;
    711 		self->nextthink = level.time + 2 * FRAMETIME;
    712 	}
    713 	else
    714 	{
    715 		self->solid = SOLID_NOT;
    716 		self->movetype = MOVETYPE_PUSH;
    717 		self->use = func_object_use;
    718 		self->svflags |= SVF_NOCLIENT;
    719 	}
    720 
    721 	if (self->spawnflags & 2)
    722 		self->s.effects |= EF_ANIM_ALL;
    723 	if (self->spawnflags & 4)
    724 		self->s.effects |= EF_ANIM_ALLFAST;
    725 
    726 	self->clipmask = MASK_MONSTERSOLID;
    727 
    728 	gi.linkentity (self);
    729 }
    730 
    731 
    732 /*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST
    733 Any brush that you want to explode or break apart.  If you want an
    734 ex0plosion, set dmg and it will do a radius explosion of that amount
    735 at the center of the bursh.
    736 
    737 If targeted it will not be shootable.
    738 
    739 health defaults to 100.
    740 
    741 mass defaults to 75.  This determines how much debris is emitted when
    742 it explodes.  You get one large chunk per 100 of mass (up to 8) and
    743 one small chunk per 25 of mass (up to 16).  So 800 gives the most.
    744 */
    745 void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
    746 {
    747 	vec3_t	origin;
    748 	vec3_t	chunkorigin;
    749 	vec3_t	size;
    750 	int		count;
    751 	int		mass;
    752 
    753 	// bmodel origins are (0 0 0), we need to adjust that here
    754 	VectorScale (self->size, 0.5, size);
    755 	VectorAdd (self->absmin, size, origin);
    756 	VectorCopy (origin, self->s.origin);
    757 
    758 	self->takedamage = DAMAGE_NO;
    759 
    760 	if (self->dmg)
    761 		T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
    762 
    763 	VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity);
    764 	VectorNormalize (self->velocity);
    765 	VectorScale (self->velocity, 150, self->velocity);
    766 
    767 	// start chunks towards the center
    768 	VectorScale (size, 0.5, size);
    769 
    770 	mass = self->mass;
    771 	if (!mass)
    772 		mass = 75;
    773 
    774 	// big chunks
    775 	if (mass >= 100)
    776 	{
    777 		count = mass / 100;
    778 		if (count > 8)
    779 			count = 8;
    780 		while(count--)
    781 		{
    782 			chunkorigin[0] = origin[0] + crandom() * size[0];
    783 			chunkorigin[1] = origin[1] + crandom() * size[1];
    784 			chunkorigin[2] = origin[2] + crandom() * size[2];
    785 			ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin);
    786 		}
    787 	}
    788 
    789 	// small chunks
    790 	count = mass / 25;
    791 	if (count > 16)
    792 		count = 16;
    793 	while(count--)
    794 	{
    795 		chunkorigin[0] = origin[0] + crandom() * size[0];
    796 		chunkorigin[1] = origin[1] + crandom() * size[1];
    797 		chunkorigin[2] = origin[2] + crandom() * size[2];
    798 		ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin);
    799 	}
    800 
    801 	G_UseTargets (self, attacker);
    802 
    803 	if (self->dmg)
    804 		BecomeExplosion1 (self);
    805 	else
    806 		G_FreeEdict (self);
    807 }
    808 
    809 void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator)
    810 {
    811 	func_explosive_explode (self, self, other, self->health, vec3_origin);
    812 }
    813 
    814 void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator)
    815 {
    816 	self->solid = SOLID_BSP;
    817 	self->svflags &= ~SVF_NOCLIENT;
    818 	self->use = NULL;
    819 	KillBox (self);
    820 	gi.linkentity (self);
    821 }
    822 
    823 void SP_func_explosive (edict_t *self)
    824 {
    825 	if (deathmatch->value)
    826 	{	// auto-remove for deathmatch
    827 		G_FreeEdict (self);
    828 		return;
    829 	}
    830 
    831 	self->movetype = MOVETYPE_PUSH;
    832 
    833 	gi.modelindex ("models/objects/debris1/tris.md2");
    834 	gi.modelindex ("models/objects/debris2/tris.md2");
    835 
    836 	gi.setmodel (self, self->model);
    837 
    838 	if (self->spawnflags & 1)
    839 	{
    840 		self->svflags |= SVF_NOCLIENT;
    841 		self->solid = SOLID_NOT;
    842 		self->use = func_explosive_spawn;
    843 	}
    844 	else
    845 	{
    846 		self->solid = SOLID_BSP;
    847 		if (self->targetname)
    848 			self->use = func_explosive_use;
    849 	}
    850 
    851 	if (self->spawnflags & 2)
    852 		self->s.effects |= EF_ANIM_ALL;
    853 	if (self->spawnflags & 4)
    854 		self->s.effects |= EF_ANIM_ALLFAST;
    855 
    856 	if (self->use != func_explosive_use)
    857 	{
    858 		if (!self->health)
    859 			self->health = 100;
    860 		self->die = func_explosive_explode;
    861 		self->takedamage = DAMAGE_YES;
    862 	}
    863 
    864 	gi.linkentity (self);
    865 }
    866 
    867 
    868 /*QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40)
    869 Large exploding box.  You can override its mass (100),
    870 health (80), and dmg (150).
    871 */
    872 
    873 void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
    874 
    875 {
    876 	float	ratio;
    877 	vec3_t	v;
    878 
    879 	if ((!other->groundentity) || (other->groundentity == self))
    880 		return;
    881 
    882 	ratio = (float)other->mass / (float)self->mass;
    883 	VectorSubtract (self->s.origin, other->s.origin, v);
    884 	M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME);
    885 }
    886 
    887 void barrel_explode (edict_t *self)
    888 {
    889 	vec3_t	org;
    890 	float	spd;
    891 	vec3_t	save;
    892 
    893 	T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL);
    894 
    895 	VectorCopy (self->s.origin, save);
    896 	VectorMA (self->absmin, 0.5, self->size, self->s.origin);
    897 
    898 	// a few big chunks
    899 	spd = 1.5 * (float)self->dmg / 200.0;
    900 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    901 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    902 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    903 	ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
    904 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    905 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    906 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    907 	ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
    908 
    909 	// bottom corners
    910 	spd = 1.75 * (float)self->dmg / 200.0;
    911 	VectorCopy (self->absmin, org);
    912 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
    913 	VectorCopy (self->absmin, org);
    914 	org[0] += self->size[0];
    915 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
    916 	VectorCopy (self->absmin, org);
    917 	org[1] += self->size[1];
    918 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
    919 	VectorCopy (self->absmin, org);
    920 	org[0] += self->size[0];
    921 	org[1] += self->size[1];
    922 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
    923 
    924 	// a bunch of little chunks
    925 	spd = 2 * self->dmg / 200;
    926 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    927 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    928 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    929 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
    930 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    931 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    932 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    933 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
    934 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    935 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    936 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    937 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
    938 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    939 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    940 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    941 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
    942 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    943 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    944 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    945 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
    946 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    947 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    948 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    949 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
    950 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    951 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    952 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    953 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
    954 	org[0] = self->s.origin[0] + crandom() * self->size[0];
    955 	org[1] = self->s.origin[1] + crandom() * self->size[1];
    956 	org[2] = self->s.origin[2] + crandom() * self->size[2];
    957 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
    958 
    959 	VectorCopy (save, self->s.origin);
    960 	if (self->groundentity)
    961 		BecomeExplosion2 (self);
    962 	else
    963 		BecomeExplosion1 (self);
    964 }
    965 
    966 void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
    967 {
    968 	self->takedamage = DAMAGE_NO;
    969 	self->nextthink = level.time + 2 * FRAMETIME;
    970 	self->think = barrel_explode;
    971 	self->activator = attacker;
    972 }
    973 
    974 void SP_misc_explobox (edict_t *self)
    975 {
    976 	if (deathmatch->value)
    977 	{	// auto-remove for deathmatch
    978 		G_FreeEdict (self);
    979 		return;
    980 	}
    981 
    982 	gi.modelindex ("models/objects/debris1/tris.md2");
    983 	gi.modelindex ("models/objects/debris2/tris.md2");
    984 	gi.modelindex ("models/objects/debris3/tris.md2");
    985 
    986 	self->solid = SOLID_BBOX;
    987 	self->movetype = MOVETYPE_STEP;
    988 
    989 	self->model = "models/objects/barrels/tris.md2";
    990 	self->s.modelindex = gi.modelindex (self->model);
    991 	VectorSet (self->mins, -16, -16, 0);
    992 	VectorSet (self->maxs, 16, 16, 40);
    993 
    994 	if (!self->mass)
    995 		self->mass = 400;
    996 	if (!self->health)
    997 		self->health = 10;
    998 	if (!self->dmg)
    999 		self->dmg = 150;
   1000 
   1001 	self->die = barrel_delay;
   1002 	self->takedamage = DAMAGE_YES;
   1003 	self->monsterinfo.aiflags = AI_NOSTEP;
   1004 
   1005 	self->touch = barrel_touch;
   1006 
   1007 	self->think = M_droptofloor;
   1008 	self->nextthink = level.time + 2 * FRAMETIME;
   1009 
   1010 	gi.linkentity (self);
   1011 }
   1012 
   1013 
   1014 //
   1015 // miscellaneous specialty items
   1016 //
   1017 
   1018 /*QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8)
   1019 */
   1020 
   1021 void misc_blackhole_use (edict_t *ent, edict_t *other, edict_t *activator)
   1022 {
   1023 	/*
   1024 	gi.WriteByte (svc_temp_entity);
   1025 	gi.WriteByte (TE_BOSSTPORT);
   1026 	gi.WritePosition (ent->s.origin);
   1027 	gi.multicast (ent->s.origin, MULTICAST_PVS);
   1028 	*/
   1029 	G_FreeEdict (ent);
   1030 }
   1031 
   1032 void misc_blackhole_think (edict_t *self)
   1033 {
   1034 	if (++self->s.frame < 19)
   1035 		self->nextthink = level.time + FRAMETIME;
   1036 	else
   1037 	{		
   1038 		self->s.frame = 0;
   1039 		self->nextthink = level.time + FRAMETIME;
   1040 	}
   1041 }
   1042 
   1043 void SP_misc_blackhole (edict_t *ent)
   1044 {
   1045 	ent->movetype = MOVETYPE_NONE;
   1046 	ent->solid = SOLID_NOT;
   1047 	VectorSet (ent->mins, -64, -64, 0);
   1048 	VectorSet (ent->maxs, 64, 64, 8);
   1049 	ent->s.modelindex = gi.modelindex ("models/objects/black/tris.md2");
   1050 	ent->s.renderfx = RF_TRANSLUCENT;
   1051 	ent->use = misc_blackhole_use;
   1052 	ent->think = misc_blackhole_think;
   1053 	ent->nextthink = level.time + 2 * FRAMETIME;
   1054 	gi.linkentity (ent);
   1055 }
   1056 
   1057 /*QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32)
   1058 */
   1059 
   1060 void misc_eastertank_think (edict_t *self)
   1061 {
   1062 	if (++self->s.frame < 293)
   1063 		self->nextthink = level.time + FRAMETIME;
   1064 	else
   1065 	{		
   1066 		self->s.frame = 254;
   1067 		self->nextthink = level.time + FRAMETIME;
   1068 	}
   1069 }
   1070 
   1071 void SP_misc_eastertank (edict_t *ent)
   1072 {
   1073 	ent->movetype = MOVETYPE_NONE;
   1074 	ent->solid = SOLID_BBOX;
   1075 	VectorSet (ent->mins, -32, -32, -16);
   1076 	VectorSet (ent->maxs, 32, 32, 32);
   1077 	ent->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2");
   1078 	ent->s.frame = 254;
   1079 	ent->think = misc_eastertank_think;
   1080 	ent->nextthink = level.time + 2 * FRAMETIME;
   1081 	gi.linkentity (ent);
   1082 }
   1083 
   1084 /*QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32)
   1085 */
   1086 
   1087 
   1088 void misc_easterchick_think (edict_t *self)
   1089 {
   1090 	if (++self->s.frame < 247)
   1091 		self->nextthink = level.time + FRAMETIME;
   1092 	else
   1093 	{		
   1094 		self->s.frame = 208;
   1095 		self->nextthink = level.time + FRAMETIME;
   1096 	}
   1097 }
   1098 
   1099 void SP_misc_easterchick (edict_t *ent)
   1100 {
   1101 	ent->movetype = MOVETYPE_NONE;
   1102 	ent->solid = SOLID_BBOX;
   1103 	VectorSet (ent->mins, -32, -32, 0);
   1104 	VectorSet (ent->maxs, 32, 32, 32);
   1105 	ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2");
   1106 	ent->s.frame = 208;
   1107 	ent->think = misc_easterchick_think;
   1108 	ent->nextthink = level.time + 2 * FRAMETIME;
   1109 	gi.linkentity (ent);
   1110 }
   1111 
   1112 /*QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32)
   1113 */
   1114 
   1115 
   1116 void misc_easterchick2_think (edict_t *self)
   1117 {
   1118 	if (++self->s.frame < 287)
   1119 		self->nextthink = level.time + FRAMETIME;
   1120 	else
   1121 	{		
   1122 		self->s.frame = 248;
   1123 		self->nextthink = level.time + FRAMETIME;
   1124 	}
   1125 }
   1126 
   1127 void SP_misc_easterchick2 (edict_t *ent)
   1128 {
   1129 	ent->movetype = MOVETYPE_NONE;
   1130 	ent->solid = SOLID_BBOX;
   1131 	VectorSet (ent->mins, -32, -32, 0);
   1132 	VectorSet (ent->maxs, 32, 32, 32);
   1133 	ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2");
   1134 	ent->s.frame = 248;
   1135 	ent->think = misc_easterchick2_think;
   1136 	ent->nextthink = level.time + 2 * FRAMETIME;
   1137 	gi.linkentity (ent);
   1138 }
   1139 
   1140 
   1141 /*QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48)
   1142 Not really a monster, this is the Tank Commander's decapitated body.
   1143 There should be a item_commander_head that has this as it's target.
   1144 */
   1145 
   1146 void commander_body_think (edict_t *self)
   1147 {
   1148 	if (++self->s.frame < 24)
   1149 		self->nextthink = level.time + FRAMETIME;
   1150 	else
   1151 		self->nextthink = 0;
   1152 
   1153 	if (self->s.frame == 22)
   1154 		gi.sound (self, CHAN_BODY, gi.soundindex ("tank/thud.wav"), 1, ATTN_NORM, 0);
   1155 }
   1156 
   1157 void commander_body_use (edict_t *self, edict_t *other, edict_t *activator)
   1158 {
   1159 	self->think = commander_body_think;
   1160 	self->nextthink = level.time + FRAMETIME;
   1161 	gi.sound (self, CHAN_BODY, gi.soundindex ("tank/pain.wav"), 1, ATTN_NORM, 0);
   1162 }
   1163 
   1164 void commander_body_drop (edict_t *self)
   1165 {
   1166 	self->movetype = MOVETYPE_TOSS;
   1167 	self->s.origin[2] += 2;
   1168 }
   1169 
   1170 void SP_monster_commander_body (edict_t *self)
   1171 {
   1172 	self->movetype = MOVETYPE_NONE;
   1173 	self->solid = SOLID_BBOX;
   1174 	self->model = "models/monsters/commandr/tris.md2";
   1175 	self->s.modelindex = gi.modelindex (self->model);
   1176 	VectorSet (self->mins, -32, -32, 0);
   1177 	VectorSet (self->maxs, 32, 32, 48);
   1178 	self->use = commander_body_use;
   1179 	self->takedamage = DAMAGE_YES;
   1180 	self->flags = FL_GODMODE;
   1181 	self->s.renderfx |= RF_FRAMELERP;
   1182 	gi.linkentity (self);
   1183 
   1184 	gi.soundindex ("tank/thud.wav");
   1185 	gi.soundindex ("tank/pain.wav");
   1186 
   1187 	self->think = commander_body_drop;
   1188 	self->nextthink = level.time + 5 * FRAMETIME;
   1189 }
   1190 
   1191 
   1192 /*QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4)
   1193 The origin is the bottom of the banner.
   1194 The banner is 128 tall.
   1195 */
   1196 void misc_banner_think (edict_t *ent)
   1197 {
   1198 	ent->s.frame = (ent->s.frame + 1) % 16;
   1199 	ent->nextthink = level.time + FRAMETIME;
   1200 }
   1201 
   1202 void SP_misc_banner (edict_t *ent)
   1203 {
   1204 	ent->movetype = MOVETYPE_NONE;
   1205 	ent->solid = SOLID_NOT;
   1206 	ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
   1207 	ent->s.frame = rand() % 16;
   1208 	gi.linkentity (ent);
   1209 
   1210 	ent->think = misc_banner_think;
   1211 	ent->nextthink = level.time + FRAMETIME;
   1212 }
   1213 
   1214 /*QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED
   1215 This is the dead player model. Comes in 6 exciting different poses!
   1216 */
   1217 void misc_deadsoldier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
   1218 {
   1219 	int		n;
   1220 
   1221 	if (self->health > -80)
   1222 		return;
   1223 
   1224 	gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
   1225 	for (n= 0; n < 4; n++)
   1226 		ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
   1227 	ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
   1228 }
   1229 
   1230 void SP_misc_deadsoldier (edict_t *ent)
   1231 {
   1232 	if (deathmatch->value)
   1233 	{	// auto-remove for deathmatch
   1234 		G_FreeEdict (ent);
   1235 		return;
   1236 	}
   1237 
   1238 	ent->movetype = MOVETYPE_NONE;
   1239 	ent->solid = SOLID_BBOX;
   1240 	ent->s.modelindex=gi.modelindex ("models/deadbods/dude/tris.md2");
   1241 
   1242 	// Defaults to frame 0
   1243 	if (ent->spawnflags & 2)
   1244 		ent->s.frame = 1;
   1245 	else if (ent->spawnflags & 4)
   1246 		ent->s.frame = 2;
   1247 	else if (ent->spawnflags & 8)
   1248 		ent->s.frame = 3;
   1249 	else if (ent->spawnflags & 16)
   1250 		ent->s.frame = 4;
   1251 	else if (ent->spawnflags & 32)
   1252 		ent->s.frame = 5;
   1253 	else
   1254 		ent->s.frame = 0;
   1255 
   1256 	VectorSet (ent->mins, -16, -16, 0);
   1257 	VectorSet (ent->maxs, 16, 16, 16);
   1258 	ent->deadflag = DEAD_DEAD;
   1259 	ent->takedamage = DAMAGE_YES;
   1260 	ent->svflags |= SVF_MONSTER|SVF_DEADMONSTER;
   1261 	ent->die = misc_deadsoldier_die;
   1262 	ent->monsterinfo.aiflags |= AI_GOOD_GUY;
   1263 
   1264 	gi.linkentity (ent);
   1265 }
   1266 
   1267 /*QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32)
   1268 This is the Viper for the flyby bombing.
   1269 It is trigger_spawned, so you must have something use it for it to show up.
   1270 There must be a path for it to follow once it is activated.
   1271 
   1272 "speed"		How fast the Viper should fly
   1273 */
   1274 
   1275 extern void train_use (edict_t *self, edict_t *other, edict_t *activator);
   1276 extern void func_train_find (edict_t *self);
   1277 
   1278 void misc_viper_use  (edict_t *self, edict_t *other, edict_t *activator)
   1279 {
   1280 	self->svflags &= ~SVF_NOCLIENT;
   1281 	self->use = train_use;
   1282 	train_use (self, other, activator);
   1283 }
   1284 
   1285 void SP_misc_viper (edict_t *ent)
   1286 {
   1287 	if (!ent->target)
   1288 	{
   1289 		gi.dprintf ("misc_viper without a target at %s\n", vtos(ent->absmin));
   1290 		G_FreeEdict (ent);
   1291 		return;
   1292 	}
   1293 
   1294 	if (!ent->speed)
   1295 		ent->speed = 300;
   1296 
   1297 	ent->movetype = MOVETYPE_PUSH;
   1298 	ent->solid = SOLID_NOT;
   1299 	ent->s.modelindex = gi.modelindex ("models/ships/viper/tris.md2");
   1300 	VectorSet (ent->mins, -16, -16, 0);
   1301 	VectorSet (ent->maxs, 16, 16, 32);
   1302 
   1303 	ent->think = func_train_find;
   1304 	ent->nextthink = level.time + FRAMETIME;
   1305 	ent->use = misc_viper_use;
   1306 	ent->svflags |= SVF_NOCLIENT;
   1307 	ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
   1308 
   1309 	gi.linkentity (ent);
   1310 }
   1311 
   1312 
   1313 /*QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72) 
   1314 This is a large stationary viper as seen in Paul's intro
   1315 */
   1316 void SP_misc_bigviper (edict_t *ent)
   1317 {
   1318 	ent->movetype = MOVETYPE_NONE;
   1319 	ent->solid = SOLID_BBOX;
   1320 	VectorSet (ent->mins, -176, -120, -24);
   1321 	VectorSet (ent->maxs, 176, 120, 72);
   1322 	ent->s.modelindex = gi.modelindex ("models/ships/bigviper/tris.md2");
   1323 	gi.linkentity (ent);
   1324 }
   1325 
   1326 
   1327 /*QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8)
   1328 "dmg"	how much boom should the bomb make?
   1329 */
   1330 void misc_viper_bomb_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
   1331 {
   1332 	G_UseTargets (self, self->activator);
   1333 
   1334 	self->s.origin[2] = self->absmin[2] + 1;
   1335 	T_RadiusDamage (self, self, self->dmg, NULL, self->dmg+40, MOD_BOMB);
   1336 	BecomeExplosion2 (self);
   1337 }
   1338 
   1339 void misc_viper_bomb_prethink (edict_t *self)
   1340 {
   1341 	vec3_t	v;
   1342 	float	diff;
   1343 
   1344 	self->groundentity = NULL;
   1345 
   1346 	diff = self->timestamp - level.time;
   1347 	if (diff < -1.0)
   1348 		diff = -1.0;
   1349 
   1350 	VectorScale (self->moveinfo.dir, 1.0 + diff, v);
   1351 	v[2] = diff;
   1352 
   1353 	diff = self->s.angles[2];
   1354 	vectoangles (v, self->s.angles);
   1355 	self->s.angles[2] = diff + 10;
   1356 }
   1357 
   1358 void misc_viper_bomb_use (edict_t *self, edict_t *other, edict_t *activator)
   1359 {
   1360 	edict_t	*viper;
   1361 
   1362 	self->solid = SOLID_BBOX;
   1363 	self->svflags &= ~SVF_NOCLIENT;
   1364 	self->s.effects |= EF_ROCKET;
   1365 	self->use = NULL;
   1366 	self->movetype = MOVETYPE_TOSS;
   1367 	self->prethink = misc_viper_bomb_prethink;
   1368 	self->touch = misc_viper_bomb_touch;
   1369 	self->activator = activator;
   1370 
   1371 	viper = G_Find (NULL, FOFS(classname), "misc_viper");
   1372 	VectorScale (viper->moveinfo.dir, viper->moveinfo.speed, self->velocity);
   1373 
   1374 	self->timestamp = level.time;
   1375 	VectorCopy (viper->moveinfo.dir, self->moveinfo.dir);
   1376 }
   1377 
   1378 void SP_misc_viper_bomb (edict_t *self)
   1379 {
   1380 	self->movetype = MOVETYPE_NONE;
   1381 	self->solid = SOLID_NOT;
   1382 	VectorSet (self->mins, -8, -8, -8);
   1383 	VectorSet (self->maxs, 8, 8, 8);
   1384 
   1385 	self->s.modelindex = gi.modelindex ("models/objects/bomb/tris.md2");
   1386 
   1387 	if (!self->dmg)
   1388 		self->dmg = 1000;
   1389 
   1390 	self->use = misc_viper_bomb_use;
   1391 	self->svflags |= SVF_NOCLIENT;
   1392 
   1393 	gi.linkentity (self);
   1394 }
   1395 
   1396 
   1397 /*QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32)
   1398 This is a Storgg ship for the flybys.
   1399 It is trigger_spawned, so you must have something use it for it to show up.
   1400 There must be a path for it to follow once it is activated.
   1401 
   1402 "speed"		How fast it should fly
   1403 */
   1404 
   1405 extern void train_use (edict_t *self, edict_t *other, edict_t *activator);
   1406 extern void func_train_find (edict_t *self);
   1407 
   1408 void misc_strogg_ship_use  (edict_t *self, edict_t *other, edict_t *activator)
   1409 {
   1410 	self->svflags &= ~SVF_NOCLIENT;
   1411 	self->use = train_use;
   1412 	train_use (self, other, activator);
   1413 }
   1414 
   1415 void SP_misc_strogg_ship (edict_t *ent)
   1416 {
   1417 	if (!ent->target)
   1418 	{
   1419 		gi.dprintf ("%s without a target at %s\n", ent->classname, vtos(ent->absmin));
   1420 		G_FreeEdict (ent);
   1421 		return;
   1422 	}
   1423 
   1424 	if (!ent->speed)
   1425 		ent->speed = 300;
   1426 
   1427 	ent->movetype = MOVETYPE_PUSH;
   1428 	ent->solid = SOLID_NOT;
   1429 	ent->s.modelindex = gi.modelindex ("models/ships/strogg1/tris.md2");
   1430 	VectorSet (ent->mins, -16, -16, 0);
   1431 	VectorSet (ent->maxs, 16, 16, 32);
   1432 
   1433 	ent->think = func_train_find;
   1434 	ent->nextthink = level.time + FRAMETIME;
   1435 	ent->use = misc_strogg_ship_use;
   1436 	ent->svflags |= SVF_NOCLIENT;
   1437 	ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
   1438 
   1439 	gi.linkentity (ent);
   1440 }
   1441 
   1442 
   1443 /*QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128)
   1444 */
   1445 void misc_satellite_dish_think (edict_t *self)
   1446 {
   1447 	self->s.frame++;
   1448 	if (self->s.frame < 38)
   1449 		self->nextthink = level.time + FRAMETIME;
   1450 }
   1451 
   1452 void misc_satellite_dish_use (edict_t *self, edict_t *other, edict_t *activator)
   1453 {
   1454 	self->s.frame = 0;
   1455 	self->think = misc_satellite_dish_think;
   1456 	self->nextthink = level.time + FRAMETIME;
   1457 }
   1458 
   1459 void SP_misc_satellite_dish (edict_t *ent)
   1460 {
   1461 	ent->movetype = MOVETYPE_NONE;
   1462 	ent->solid = SOLID_BBOX;
   1463 	VectorSet (ent->mins, -64, -64, 0);
   1464 	VectorSet (ent->maxs, 64, 64, 128);
   1465 	ent->s.modelindex = gi.modelindex ("models/objects/satellite/tris.md2");
   1466 	ent->use = misc_satellite_dish_use;
   1467 	gi.linkentity (ent);
   1468 }
   1469 
   1470 
   1471 /*QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12)
   1472 */
   1473 void SP_light_mine1 (edict_t *ent)
   1474 {
   1475 	ent->movetype = MOVETYPE_NONE;
   1476 	ent->solid = SOLID_BBOX;
   1477 	ent->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2");
   1478 	gi.linkentity (ent);
   1479 }
   1480 
   1481 
   1482 /*QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12)
   1483 */
   1484 void SP_light_mine2 (edict_t *ent)
   1485 {
   1486 	ent->movetype = MOVETYPE_NONE;
   1487 	ent->solid = SOLID_BBOX;
   1488 	ent->s.modelindex = gi.modelindex ("models/objects/minelite/light2/tris.md2");
   1489 	gi.linkentity (ent);
   1490 }
   1491 
   1492 
   1493 /*QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8)
   1494 Intended for use with the target_spawner
   1495 */
   1496 void SP_misc_gib_arm (edict_t *ent)
   1497 {
   1498 	gi.setmodel (ent, "models/objects/gibs/arm/tris.md2");
   1499 	ent->solid = SOLID_NOT;
   1500 	ent->s.effects |= EF_GIB;
   1501 	ent->takedamage = DAMAGE_YES;
   1502 	ent->die = gib_die;
   1503 	ent->movetype = MOVETYPE_TOSS;
   1504 	ent->svflags |= SVF_MONSTER;
   1505 	ent->deadflag = DEAD_DEAD;
   1506 	ent->avelocity[0] = random()*200;
   1507 	ent->avelocity[1] = random()*200;
   1508 	ent->avelocity[2] = random()*200;
   1509 	ent->think = G_FreeEdict;
   1510 	ent->nextthink = level.time + 30;
   1511 	gi.linkentity (ent);
   1512 }
   1513 
   1514 /*QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8)
   1515 Intended for use with the target_spawner
   1516 */
   1517 void SP_misc_gib_leg (edict_t *ent)
   1518 {
   1519 	gi.setmodel (ent, "models/objects/gibs/leg/tris.md2");
   1520 	ent->solid = SOLID_NOT;
   1521 	ent->s.effects |= EF_GIB;
   1522 	ent->takedamage = DAMAGE_YES;
   1523 	ent->die = gib_die;
   1524 	ent->movetype = MOVETYPE_TOSS;
   1525 	ent->svflags |= SVF_MONSTER;
   1526 	ent->deadflag = DEAD_DEAD;
   1527 	ent->avelocity[0] = random()*200;
   1528 	ent->avelocity[1] = random()*200;
   1529 	ent->avelocity[2] = random()*200;
   1530 	ent->think = G_FreeEdict;
   1531 	ent->nextthink = level.time + 30;
   1532 	gi.linkentity (ent);
   1533 }
   1534 
   1535 /*QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8)
   1536 Intended for use with the target_spawner
   1537 */
   1538 void SP_misc_gib_head (edict_t *ent)
   1539 {
   1540 	gi.setmodel (ent, "models/objects/gibs/head/tris.md2");
   1541 	ent->solid = SOLID_NOT;
   1542 	ent->s.effects |= EF_GIB;
   1543 	ent->takedamage = DAMAGE_YES;
   1544 	ent->die = gib_die;
   1545 	ent->movetype = MOVETYPE_TOSS;
   1546 	ent->svflags |= SVF_MONSTER;
   1547 	ent->deadflag = DEAD_DEAD;
   1548 	ent->avelocity[0] = random()*200;
   1549 	ent->avelocity[1] = random()*200;
   1550 	ent->avelocity[2] = random()*200;
   1551 	ent->think = G_FreeEdict;
   1552 	ent->nextthink = level.time + 30;
   1553 	gi.linkentity (ent);
   1554 }
   1555 
   1556 //=====================================================
   1557 
   1558 /*QUAKED target_character (0 0 1) ?
   1559 used with target_string (must be on same "team")
   1560 "count" is position in the string (starts at 1)
   1561 */
   1562 
   1563 void SP_target_character (edict_t *self)
   1564 {
   1565 	self->movetype = MOVETYPE_PUSH;
   1566 	gi.setmodel (self, self->model);
   1567 	self->solid = SOLID_BSP;
   1568 	self->s.frame = 12;
   1569 	gi.linkentity (self);
   1570 	return;
   1571 }
   1572 
   1573 
   1574 /*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8)
   1575 */
   1576 
   1577 void target_string_use (edict_t *self, edict_t *other, edict_t *activator)
   1578 {
   1579 	edict_t *e;
   1580 	int		n, l;
   1581 	char	c;
   1582 
   1583 	l = strlen(self->message);
   1584 	for (e = self->teammaster; e; e = e->teamchain)
   1585 	{
   1586 		if (!e->count)
   1587 			continue;
   1588 		n = e->count - 1;
   1589 		if (n > l)
   1590 		{
   1591 			e->s.frame = 12;
   1592 			continue;
   1593 		}
   1594 
   1595 		c = self->message[n];
   1596 		if (c >= '0' && c <= '9')
   1597 			e->s.frame = c - '0';
   1598 		else if (c == '-')
   1599 			e->s.frame = 10;
   1600 		else if (c == ':')
   1601 			e->s.frame = 11;
   1602 		else
   1603 			e->s.frame = 12;
   1604 	}
   1605 }
   1606 
   1607 void SP_target_string (edict_t *self)
   1608 {
   1609 	if (!self->message)
   1610 		self->message = "";
   1611 	self->use = target_string_use;
   1612 }
   1613 
   1614 
   1615 /*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE
   1616 target a target_string with this
   1617 
   1618 The default is to be a time of day clock
   1619 
   1620 TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget"
   1621 If START_OFF, this entity must be used before it starts
   1622 
   1623 "style"		0 "xx"
   1624 			1 "xx:xx"
   1625 			2 "xx:xx:xx"
   1626 */
   1627 
   1628 #define	CLOCK_MESSAGE_SIZE	16
   1629 
   1630 // don't let field width of any clock messages change, or it
   1631 // could cause an overwrite after a game load
   1632 
   1633 static void func_clock_reset (edict_t *self)
   1634 {
   1635 	self->activator = NULL;
   1636 	if (self->spawnflags & 1)
   1637 	{
   1638 		self->health = 0;
   1639 		self->wait = self->count;
   1640 	}
   1641 	else if (self->spawnflags & 2)
   1642 	{
   1643 		self->health = self->count;
   1644 		self->wait = 0;
   1645 	}
   1646 }
   1647 
   1648 static void func_clock_format_countdown (edict_t *self)
   1649 {
   1650 	if (self->style == 0)
   1651 	{
   1652 		Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health);
   1653 		return;
   1654 	}
   1655 
   1656 	if (self->style == 1)
   1657 	{
   1658 		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60);
   1659 		if (self->message[3] == ' ')
   1660 			self->message[3] = '0';
   1661 		return;
   1662 	}
   1663 
   1664 	if (self->style == 2)
   1665 	{
   1666 		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60);
   1667 		if (self->message[3] == ' ')
   1668 			self->message[3] = '0';
   1669 		if (self->message[6] == ' ')
   1670 			self->message[6] = '0';
   1671 		return;
   1672 	}
   1673 }
   1674 
   1675 void func_clock_think (edict_t *self)
   1676 {
   1677 	if (!self->enemy)
   1678 	{
   1679 		self->enemy = G_Find (NULL, FOFS(targetname), self->target);
   1680 		if (!self->enemy)
   1681 			return;
   1682 	}
   1683 
   1684 	if (self->spawnflags & 1)
   1685 	{
   1686 		func_clock_format_countdown (self);
   1687 		self->health++;
   1688 	}
   1689 	else if (self->spawnflags & 2)
   1690 	{
   1691 		func_clock_format_countdown (self);
   1692 		self->health--;
   1693 	}
   1694 	else
   1695 	{
   1696 		struct tm	*ltime;
   1697 		time_t		gmtime;
   1698 
   1699 		time(&gmtime);
   1700 		ltime = localtime(&gmtime);
   1701 		Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec);
   1702 		if (self->message[3] == ' ')
   1703 			self->message[3] = '0';
   1704 		if (self->message[6] == ' ')
   1705 			self->message[6] = '0';
   1706 	}
   1707 
   1708 	self->enemy->message = self->message;
   1709 	self->enemy->use (self->enemy, self, self);
   1710 
   1711 	if (((self->spawnflags & 1) && (self->health > self->wait)) ||
   1712 		((self->spawnflags & 2) && (self->health < self->wait)))
   1713 	{
   1714 		if (self->pathtarget)
   1715 		{
   1716 			char *savetarget;
   1717 			char *savemessage;
   1718 
   1719 			savetarget = self->target;
   1720 			savemessage = self->message;
   1721 			self->target = self->pathtarget;
   1722 			self->message = NULL;
   1723 			G_UseTargets (self, self->activator);
   1724 			self->target = savetarget;
   1725 			self->message = savemessage;
   1726 		}
   1727 
   1728 		if (!(self->spawnflags & 8))
   1729 			return;
   1730 
   1731 		func_clock_reset (self);
   1732 
   1733 		if (self->spawnflags & 4)
   1734 			return;
   1735 	}
   1736 
   1737 	self->nextthink = level.time + 1;
   1738 }
   1739 
   1740 void func_clock_use (edict_t *self, edict_t *other, edict_t *activator)
   1741 {
   1742 	if (!(self->spawnflags & 8))
   1743 		self->use = NULL;
   1744 	if (self->activator)
   1745 		return;
   1746 	self->activator = activator;
   1747 	self->think (self);
   1748 }
   1749 
   1750 void SP_func_clock (edict_t *self)
   1751 {
   1752 	if (!self->target)
   1753 	{
   1754 		gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
   1755 		G_FreeEdict (self);
   1756 		return;
   1757 	}
   1758 
   1759 	if ((self->spawnflags & 2) && (!self->count))
   1760 	{
   1761 		gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin));
   1762 		G_FreeEdict (self);
   1763 		return;
   1764 	}
   1765 
   1766 	if ((self->spawnflags & 1) && (!self->count))
   1767 		self->count = 60*60;;
   1768 
   1769 	func_clock_reset (self);
   1770 
   1771 	self->message = gi.TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL);
   1772 
   1773 	self->think = func_clock_think;
   1774 
   1775 	if (self->spawnflags & 4)
   1776 		self->use = func_clock_use;
   1777 	else
   1778 		self->nextthink = level.time + 1;
   1779 }
   1780 
   1781 //=================================================================================
   1782 
   1783 void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
   1784 {
   1785 	edict_t		*dest;
   1786 	int			i;
   1787 
   1788 	if (!other->client)
   1789 		return;
   1790 	dest = G_Find (NULL, FOFS(targetname), self->target);
   1791 	if (!dest)
   1792 	{
   1793 		gi.dprintf ("Couldn't find destination\n");
   1794 		return;
   1795 	}
   1796 
   1797 	// unlink to make sure it can't possibly interfere with KillBox
   1798 	gi.unlinkentity (other);
   1799 
   1800 	VectorCopy (dest->s.origin, other->s.origin);
   1801 	VectorCopy (dest->s.origin, other->s.old_origin);
   1802 	other->s.origin[2] += 10;
   1803 
   1804 	// clear the velocity and hold them in place briefly
   1805 	VectorClear (other->velocity);
   1806 	other->client->ps.pmove.pm_time = 160>>3;		// hold time
   1807 	other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
   1808 
   1809 	// draw the teleport splash at source and on the player
   1810 	self->owner->s.event = EV_PLAYER_TELEPORT;
   1811 	other->s.event = EV_PLAYER_TELEPORT;
   1812 
   1813 	// set angles
   1814 	for (i=0 ; i<3 ; i++)
   1815 		other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
   1816 
   1817 	VectorClear (other->s.angles);
   1818 	VectorClear (other->client->ps.viewangles);
   1819 	VectorClear (other->client->v_angle);
   1820 
   1821 	// kill anything at the destination
   1822 	KillBox (other);
   1823 
   1824 	gi.linkentity (other);
   1825 }
   1826 
   1827 /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
   1828 Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
   1829 */
   1830 void SP_misc_teleporter (edict_t *ent)
   1831 {
   1832 	edict_t		*trig;
   1833 
   1834 	if (!ent->target)
   1835 	{
   1836 		gi.dprintf ("teleporter without a target.\n");
   1837 		G_FreeEdict (ent);
   1838 		return;
   1839 	}
   1840 
   1841 	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
   1842 	ent->s.skinnum = 1;
   1843 	ent->s.effects = EF_TELEPORTER;
   1844 	ent->s.sound = gi.soundindex ("world/amb10.wav");
   1845 	ent->solid = SOLID_BBOX;
   1846 
   1847 	VectorSet (ent->mins, -32, -32, -24);
   1848 	VectorSet (ent->maxs, 32, 32, -16);
   1849 	gi.linkentity (ent);
   1850 
   1851 	trig = G_Spawn ();
   1852 	trig->touch = teleporter_touch;
   1853 	trig->solid = SOLID_TRIGGER;
   1854 	trig->target = ent->target;
   1855 	trig->owner = ent;
   1856 	VectorCopy (ent->s.origin, trig->s.origin);
   1857 	VectorSet (trig->mins, -8, -8, 8);
   1858 	VectorSet (trig->maxs, 8, 8, 24);
   1859 	gi.linkentity (trig);
   1860 	
   1861 }
   1862 
   1863 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
   1864 Point teleporters at these.
   1865 */
   1866 void SP_misc_teleporter_dest (edict_t *ent)
   1867 {
   1868 	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
   1869 	ent->s.skinnum = 0;
   1870 	ent->solid = SOLID_BBOX;
   1871 //	ent->s.effects |= EF_FLIES;
   1872 	VectorSet (ent->mins, -32, -32, -24);
   1873 	VectorSet (ent->maxs, 32, 32, -16);
   1874 	gi.linkentity (ent);
   1875 }
   1876