Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

g_monster.c (17585B)


      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 #include "g_local.h"
     21 
     22 
     23 //
     24 // monster weapons
     25 //
     26 
     27 //FIXME mosnters should call these with a totally accurate direction
     28 // and we can mess it up based on skill.  Spread should be for normal
     29 // and we can tighten or loosen based on skill.  We could muck with
     30 // the damages too, but I'm not sure that's such a good idea.
     31 void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype)
     32 {
     33 	fire_bullet (self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN);
     34 
     35 	gi.WriteByte (svc_muzzleflash2);
     36 	gi.WriteShort (self - g_edicts);
     37 	gi.WriteByte (flashtype);
     38 	gi.multicast (start, MULTICAST_PVS);
     39 }
     40 
     41 void monster_fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype)
     42 {
     43 	fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN);
     44 
     45 	gi.WriteByte (svc_muzzleflash2);
     46 	gi.WriteShort (self - g_edicts);
     47 	gi.WriteByte (flashtype);
     48 	gi.multicast (start, MULTICAST_PVS);
     49 }
     50 
     51 void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
     52 {
     53 	fire_blaster (self, start, dir, damage, speed, effect, false);
     54 
     55 	gi.WriteByte (svc_muzzleflash2);
     56 	gi.WriteShort (self - g_edicts);
     57 	gi.WriteByte (flashtype);
     58 	gi.multicast (start, MULTICAST_PVS);
     59 }	
     60 
     61 void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype)
     62 {
     63 	fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40);
     64 
     65 	gi.WriteByte (svc_muzzleflash2);
     66 	gi.WriteShort (self - g_edicts);
     67 	gi.WriteByte (flashtype);
     68 	gi.multicast (start, MULTICAST_PVS);
     69 }
     70 
     71 void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
     72 {
     73 	fire_rocket (self, start, dir, damage, speed, damage+20, damage);
     74 
     75 	gi.WriteByte (svc_muzzleflash2);
     76 	gi.WriteShort (self - g_edicts);
     77 	gi.WriteByte (flashtype);
     78 	gi.multicast (start, MULTICAST_PVS);
     79 }	
     80 
     81 void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype)
     82 {
     83 	fire_rail (self, start, aimdir, damage, kick);
     84 
     85 	gi.WriteByte (svc_muzzleflash2);
     86 	gi.WriteShort (self - g_edicts);
     87 	gi.WriteByte (flashtype);
     88 	gi.multicast (start, MULTICAST_PVS);
     89 }
     90 
     91 void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
     92 {
     93 	fire_bfg (self, start, aimdir, damage, speed, damage_radius);
     94 
     95 	gi.WriteByte (svc_muzzleflash2);
     96 	gi.WriteShort (self - g_edicts);
     97 	gi.WriteByte (flashtype);
     98 	gi.multicast (start, MULTICAST_PVS);
     99 }
    100 
    101 
    102 
    103 //
    104 // Monster utility functions
    105 //
    106 
    107 static void M_FliesOff (edict_t *self)
    108 {
    109 	self->s.effects &= ~EF_FLIES;
    110 	self->s.sound = 0;
    111 }
    112 
    113 static void M_FliesOn (edict_t *self)
    114 {
    115 	if (self->waterlevel)
    116 		return;
    117 	self->s.effects |= EF_FLIES;
    118 	self->s.sound = gi.soundindex ("infantry/inflies1.wav");
    119 	self->think = M_FliesOff;
    120 	self->nextthink = level.time + 60;
    121 }
    122 
    123 void M_FlyCheck (edict_t *self)
    124 {
    125 	if (self->waterlevel)
    126 		return;
    127 
    128 	if (random() > 0.5)
    129 		return;
    130 
    131 	self->think = M_FliesOn;
    132 	self->nextthink = level.time + 5 + 10 * random();
    133 }
    134 
    135 void AttackFinished (edict_t *self, float time)
    136 {
    137 	self->monsterinfo.attack_finished = level.time + time;
    138 }
    139 
    140 
    141 void M_CheckGround (edict_t *ent)
    142 {
    143 	vec3_t		point;
    144 	trace_t		trace;
    145 
    146 	if (ent->flags & (FL_SWIM|FL_FLY))
    147 		return;
    148 
    149 	if (ent->velocity[2] > 100)
    150 	{
    151 		ent->groundentity = NULL;
    152 		return;
    153 	}
    154 
    155 // if the hull point one-quarter unit down is solid the entity is on ground
    156 	point[0] = ent->s.origin[0];
    157 	point[1] = ent->s.origin[1];
    158 	point[2] = ent->s.origin[2] - 0.25;
    159 
    160 	trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID);
    161 
    162 	// check steepness
    163 	if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
    164 	{
    165 		ent->groundentity = NULL;
    166 		return;
    167 	}
    168 
    169 //	ent->groundentity = trace.ent;
    170 //	ent->groundentity_linkcount = trace.ent->linkcount;
    171 //	if (!trace.startsolid && !trace.allsolid)
    172 //		VectorCopy (trace.endpos, ent->s.origin);
    173 	if (!trace.startsolid && !trace.allsolid)
    174 	{
    175 		VectorCopy (trace.endpos, ent->s.origin);
    176 		ent->groundentity = trace.ent;
    177 		ent->groundentity_linkcount = trace.ent->linkcount;
    178 		ent->velocity[2] = 0;
    179 	}
    180 }
    181 
    182 
    183 void M_CatagorizePosition (edict_t *ent)
    184 {
    185 	vec3_t		point;
    186 	int			cont;
    187 
    188 //
    189 // get waterlevel
    190 //
    191 	point[0] = ent->s.origin[0];
    192 	point[1] = ent->s.origin[1];
    193 	point[2] = ent->s.origin[2] + ent->mins[2] + 1;	
    194 	cont = gi.pointcontents (point);
    195 
    196 	if (!(cont & MASK_WATER))
    197 	{
    198 		ent->waterlevel = 0;
    199 		ent->watertype = 0;
    200 		return;
    201 	}
    202 
    203 	ent->watertype = cont;
    204 	ent->waterlevel = 1;
    205 	point[2] += 26;
    206 	cont = gi.pointcontents (point);
    207 	if (!(cont & MASK_WATER))
    208 		return;
    209 
    210 	ent->waterlevel = 2;
    211 	point[2] += 22;
    212 	cont = gi.pointcontents (point);
    213 	if (cont & MASK_WATER)
    214 		ent->waterlevel = 3;
    215 }
    216 
    217 
    218 void M_WorldEffects (edict_t *ent)
    219 {
    220 	int		dmg;
    221 
    222 	if (ent->health > 0)
    223 	{
    224 		if (!(ent->flags & FL_SWIM))
    225 		{
    226 			if (ent->waterlevel < 3)
    227 			{
    228 				ent->air_finished = level.time + 12;
    229 			}
    230 			else if (ent->air_finished < level.time)
    231 			{	// drown!
    232 				if (ent->pain_debounce_time < level.time)
    233 				{
    234 					dmg = 2 + 2 * floor(level.time - ent->air_finished);
    235 					if (dmg > 15)
    236 						dmg = 15;
    237 					T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
    238 					ent->pain_debounce_time = level.time + 1;
    239 				}
    240 			}
    241 		}
    242 		else
    243 		{
    244 			if (ent->waterlevel > 0)
    245 			{
    246 				ent->air_finished = level.time + 9;
    247 			}
    248 			else if (ent->air_finished < level.time)
    249 			{	// suffocate!
    250 				if (ent->pain_debounce_time < level.time)
    251 				{
    252 					dmg = 2 + 2 * floor(level.time - ent->air_finished);
    253 					if (dmg > 15)
    254 						dmg = 15;
    255 					T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
    256 					ent->pain_debounce_time = level.time + 1;
    257 				}
    258 			}
    259 		}
    260 	}
    261 	
    262 	if (ent->waterlevel == 0)
    263 	{
    264 		if (ent->flags & FL_INWATER)
    265 		{	
    266 			gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
    267 			ent->flags &= ~FL_INWATER;
    268 		}
    269 		return;
    270 	}
    271 
    272 	if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA))
    273 	{
    274 		if (ent->damage_debounce_time < level.time)
    275 		{
    276 			ent->damage_debounce_time = level.time + 0.2;
    277 			T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA);
    278 		}
    279 	}
    280 	if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
    281 	{
    282 		if (ent->damage_debounce_time < level.time)
    283 		{
    284 			ent->damage_debounce_time = level.time + 1;
    285 			T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME);
    286 		}
    287 	}
    288 	
    289 	if ( !(ent->flags & FL_INWATER) )
    290 	{	
    291 		if (!(ent->svflags & SVF_DEADMONSTER))
    292 		{
    293 			if (ent->watertype & CONTENTS_LAVA)
    294 				if (random() <= 0.5)
    295 					gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0);
    296 				else
    297 					gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0);
    298 			else if (ent->watertype & CONTENTS_SLIME)
    299 				gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
    300 			else if (ent->watertype & CONTENTS_WATER)
    301 				gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
    302 		}
    303 
    304 		ent->flags |= FL_INWATER;
    305 		ent->damage_debounce_time = 0;
    306 	}
    307 }
    308 
    309 
    310 void M_droptofloor (edict_t *ent)
    311 {
    312 	vec3_t		end;
    313 	trace_t		trace;
    314 
    315 	ent->s.origin[2] += 1;
    316 	VectorCopy (ent->s.origin, end);
    317 	end[2] -= 256;
    318 	
    319 	trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
    320 
    321 	if (trace.fraction == 1 || trace.allsolid)
    322 		return;
    323 
    324 	VectorCopy (trace.endpos, ent->s.origin);
    325 
    326 	gi.linkentity (ent);
    327 	M_CheckGround (ent);
    328 	M_CatagorizePosition (ent);
    329 }
    330 
    331 
    332 void M_SetEffects (edict_t *ent)
    333 {
    334 	ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN);
    335 	ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
    336 
    337 	if (ent->monsterinfo.aiflags & AI_RESURRECTING)
    338 	{
    339 		ent->s.effects |= EF_COLOR_SHELL;
    340 		ent->s.renderfx |= RF_SHELL_RED;
    341 	}
    342 
    343 	if (ent->health <= 0)
    344 		return;
    345 
    346 	if (ent->powerarmor_time > level.time)
    347 	{
    348 		if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN)
    349 		{
    350 			ent->s.effects |= EF_POWERSCREEN;
    351 		}
    352 		else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD)
    353 		{
    354 			ent->s.effects |= EF_COLOR_SHELL;
    355 			ent->s.renderfx |= RF_SHELL_GREEN;
    356 		}
    357 	}
    358 }
    359 
    360 
    361 void M_MoveFrame (edict_t *self)
    362 {
    363 	mmove_t	*move;
    364 	int		index;
    365 
    366 	move = self->monsterinfo.currentmove;
    367 	self->nextthink = level.time + FRAMETIME;
    368 
    369 	if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe))
    370 	{
    371 		self->s.frame = self->monsterinfo.nextframe;
    372 		self->monsterinfo.nextframe = 0;
    373 	}
    374 	else
    375 	{
    376 		if (self->s.frame == move->lastframe)
    377 		{
    378 			if (move->endfunc)
    379 			{
    380 				move->endfunc (self);
    381 
    382 				// regrab move, endfunc is very likely to change it
    383 				move = self->monsterinfo.currentmove;
    384 
    385 				// check for death
    386 				if (self->svflags & SVF_DEADMONSTER)
    387 					return;
    388 			}
    389 		}
    390 
    391 		if (self->s.frame < move->firstframe || self->s.frame > move->lastframe)
    392 		{
    393 			self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
    394 			self->s.frame = move->firstframe;
    395 		}
    396 		else
    397 		{
    398 			if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
    399 			{
    400 				self->s.frame++;
    401 				if (self->s.frame > move->lastframe)
    402 					self->s.frame = move->firstframe;
    403 			}
    404 		}
    405 	}
    406 
    407 	index = self->s.frame - move->firstframe;
    408 	if (move->frame[index].aifunc)
    409 		if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
    410 			move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale);
    411 		else
    412 			move->frame[index].aifunc (self, 0);
    413 
    414 	if (move->frame[index].thinkfunc)
    415 		move->frame[index].thinkfunc (self);
    416 }
    417 
    418 
    419 void monster_think (edict_t *self)
    420 {
    421 	M_MoveFrame (self);
    422 	if (self->linkcount != self->monsterinfo.linkcount)
    423 	{
    424 		self->monsterinfo.linkcount = self->linkcount;
    425 		M_CheckGround (self);
    426 	}
    427 	M_CatagorizePosition (self);
    428 	M_WorldEffects (self);
    429 	M_SetEffects (self);
    430 }
    431 
    432 
    433 /*
    434 ================
    435 monster_use
    436 
    437 Using a monster makes it angry at the current activator
    438 ================
    439 */
    440 void monster_use (edict_t *self, edict_t *other, edict_t *activator)
    441 {
    442 	if (self->enemy)
    443 		return;
    444 	if (self->health <= 0)
    445 		return;
    446 	if (activator->flags & FL_NOTARGET)
    447 		return;
    448 	if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY))
    449 		return;
    450 	
    451 // delay reaction so if the monster is teleported, its sound is still heard
    452 	self->enemy = activator;
    453 	FoundTarget (self);
    454 }
    455 
    456 
    457 void monster_start_go (edict_t *self);
    458 
    459 
    460 void monster_triggered_spawn (edict_t *self)
    461 {
    462 	self->s.origin[2] += 1;
    463 	KillBox (self);
    464 
    465 	self->solid = SOLID_BBOX;
    466 	self->movetype = MOVETYPE_STEP;
    467 	self->svflags &= ~SVF_NOCLIENT;
    468 	self->air_finished = level.time + 12;
    469 	gi.linkentity (self);
    470 
    471 	monster_start_go (self);
    472 
    473 	if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
    474 	{
    475 		FoundTarget (self);
    476 	}
    477 	else
    478 	{
    479 		self->enemy = NULL;
    480 	}
    481 }
    482 
    483 void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
    484 {
    485 	// we have a one frame delay here so we don't telefrag the guy who activated us
    486 	self->think = monster_triggered_spawn;
    487 	self->nextthink = level.time + FRAMETIME;
    488 	if (activator->client)
    489 		self->enemy = activator;
    490 	self->use = monster_use;
    491 }
    492 
    493 void monster_triggered_start (edict_t *self)
    494 {
    495 	self->solid = SOLID_NOT;
    496 	self->movetype = MOVETYPE_NONE;
    497 	self->svflags |= SVF_NOCLIENT;
    498 	self->nextthink = 0;
    499 	self->use = monster_triggered_spawn_use;
    500 }
    501 
    502 
    503 /*
    504 ================
    505 monster_death_use
    506 
    507 When a monster dies, it fires all of its targets with the current
    508 enemy as activator.
    509 ================
    510 */
    511 void monster_death_use (edict_t *self)
    512 {
    513 	self->flags &= ~(FL_FLY|FL_SWIM);
    514 	self->monsterinfo.aiflags &= AI_GOOD_GUY;
    515 
    516 	if (self->item)
    517 	{
    518 		Drop_Item (self, self->item);
    519 		self->item = NULL;
    520 	}
    521 
    522 	if (self->deathtarget)
    523 		self->target = self->deathtarget;
    524 
    525 	if (!self->target)
    526 		return;
    527 
    528 	G_UseTargets (self, self->enemy);
    529 }
    530 
    531 
    532 //============================================================================
    533 
    534 qboolean monster_start (edict_t *self)
    535 {
    536 	if (deathmatch->value)
    537 	{
    538 		G_FreeEdict (self);
    539 		return false;
    540 	}
    541 
    542 	if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY))
    543 	{
    544 		self->spawnflags &= ~4;
    545 		self->spawnflags |= 1;
    546 //		gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin));
    547 	}
    548 
    549 	if (!(self->monsterinfo.aiflags & AI_GOOD_GUY))
    550 		level.total_monsters++;
    551 
    552 	self->nextthink = level.time + FRAMETIME;
    553 	self->svflags |= SVF_MONSTER;
    554 	self->s.renderfx |= RF_FRAMELERP;
    555 	self->takedamage = DAMAGE_AIM;
    556 	self->air_finished = level.time + 12;
    557 	self->use = monster_use;
    558 	self->max_health = self->health;
    559 	self->clipmask = MASK_MONSTERSOLID;
    560 
    561 	self->s.skinnum = 0;
    562 	self->deadflag = DEAD_NO;
    563 	self->svflags &= ~SVF_DEADMONSTER;
    564 
    565 	if (!self->monsterinfo.checkattack)
    566 		self->monsterinfo.checkattack = M_CheckAttack;
    567 	VectorCopy (self->s.origin, self->s.old_origin);
    568 
    569 	if (st.item)
    570 	{
    571 		self->item = FindItemByClassname (st.item);
    572 		if (!self->item)
    573 			gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
    574 	}
    575 
    576 	// randomize what frame they start on
    577 	if (self->monsterinfo.currentmove)
    578 		self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
    579 
    580 	return true;
    581 }
    582 
    583 void monster_start_go (edict_t *self)
    584 {
    585 	vec3_t	v;
    586 
    587 	if (self->health <= 0)
    588 		return;
    589 
    590 	// check for target to combat_point and change to combattarget
    591 	if (self->target)
    592 	{
    593 		qboolean	notcombat;
    594 		qboolean	fixup;
    595 		edict_t		*target;
    596 
    597 		target = NULL;
    598 		notcombat = false;
    599 		fixup = false;
    600 		while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL)
    601 		{
    602 			if (strcmp(target->classname, "point_combat") == 0)
    603 			{
    604 				self->combattarget = self->target;
    605 				fixup = true;
    606 			}
    607 			else
    608 			{
    609 				notcombat = true;
    610 			}
    611 		}
    612 		if (notcombat && self->combattarget)
    613 			gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
    614 		if (fixup)
    615 			self->target = NULL;
    616 	}
    617 
    618 	// validate combattarget
    619 	if (self->combattarget)
    620 	{
    621 		edict_t		*target;
    622 
    623 		target = NULL;
    624 		while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL)
    625 		{
    626 			if (strcmp(target->classname, "point_combat") != 0)
    627 			{
    628 				gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
    629 					self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
    630 					self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
    631 					(int)target->s.origin[2]);
    632 			}
    633 		}
    634 	}
    635 
    636 	if (self->target)
    637 	{
    638 		self->goalentity = self->movetarget = G_PickTarget(self->target);
    639 		if (!self->movetarget)
    640 		{
    641 			gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
    642 			self->target = NULL;
    643 			self->monsterinfo.pausetime = 100000000;
    644 			self->monsterinfo.stand (self);
    645 		}
    646 		else if (strcmp (self->movetarget->classname, "path_corner") == 0)
    647 		{
    648 			VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
    649 			self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
    650 			self->monsterinfo.walk (self);
    651 			self->target = NULL;
    652 		}
    653 		else
    654 		{
    655 			self->goalentity = self->movetarget = NULL;
    656 			self->monsterinfo.pausetime = 100000000;
    657 			self->monsterinfo.stand (self);
    658 		}
    659 	}
    660 	else
    661 	{
    662 		self->monsterinfo.pausetime = 100000000;
    663 		self->monsterinfo.stand (self);
    664 	}
    665 
    666 	self->think = monster_think;
    667 	self->nextthink = level.time + FRAMETIME;
    668 }
    669 
    670 
    671 void walkmonster_start_go (edict_t *self)
    672 {
    673 	if (!(self->spawnflags & 2) && level.time < 1)
    674 	{
    675 		M_droptofloor (self);
    676 
    677 		if (self->groundentity)
    678 			if (!M_walkmove (self, 0, 0))
    679 				gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
    680 	}
    681 	
    682 	if (!self->yaw_speed)
    683 		self->yaw_speed = 20;
    684 	self->viewheight = 25;
    685 
    686 	monster_start_go (self);
    687 
    688 	if (self->spawnflags & 2)
    689 		monster_triggered_start (self);
    690 }
    691 
    692 void walkmonster_start (edict_t *self)
    693 {
    694 	self->think = walkmonster_start_go;
    695 	monster_start (self);
    696 }
    697 
    698 
    699 void flymonster_start_go (edict_t *self)
    700 {
    701 	if (!M_walkmove (self, 0, 0))
    702 		gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
    703 
    704 	if (!self->yaw_speed)
    705 		self->yaw_speed = 10;
    706 	self->viewheight = 25;
    707 
    708 	monster_start_go (self);
    709 
    710 	if (self->spawnflags & 2)
    711 		monster_triggered_start (self);
    712 }
    713 
    714 
    715 void flymonster_start (edict_t *self)
    716 {
    717 	self->flags |= FL_FLY;
    718 	self->think = flymonster_start_go;
    719 	monster_start (self);
    720 }
    721 
    722 
    723 void swimmonster_start_go (edict_t *self)
    724 {
    725 	if (!self->yaw_speed)
    726 		self->yaw_speed = 10;
    727 	self->viewheight = 10;
    728 
    729 	monster_start_go (self);
    730 
    731 	if (self->spawnflags & 2)
    732 		monster_triggered_start (self);
    733 }
    734 
    735 void swimmonster_start (edict_t *self)
    736 {
    737 	self->flags |= FL_SWIM;
    738 	self->think = swimmonster_start_go;
    739 	monster_start (self);
    740 }