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