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 
42 //ROGUE
monster_fire_blaster2(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int flashtype,int effect)43 void monster_fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
44 {
45 	fire_blaster2 (self, start, dir, damage, speed, effect, false);
46 
47 	gi.WriteByte (svc_muzzleflash2);
48 	gi.WriteShort (self - g_edicts);
49 	gi.WriteByte (flashtype);
50 	gi.multicast (start, MULTICAST_PVS);
51 }
52 
53 // FIXME -- add muzzle flash
monster_fire_tracker(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,edict_t * enemy,int flashtype)54 void monster_fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy, int flashtype)
55 {
56 	fire_tracker (self, start, dir, damage, speed, enemy);
57 
58 	gi.WriteByte (svc_muzzleflash2);
59 	gi.WriteShort (self - g_edicts);
60 	gi.WriteByte (flashtype);
61 	gi.multicast (start, MULTICAST_PVS);
62 }
63 
monster_fire_heat(edict_t * self,vec3_t start,vec3_t dir,vec3_t offset,int damage,int kick,int flashtype)64 void monster_fire_heat (edict_t *self, vec3_t start, vec3_t dir, vec3_t offset, int damage, int kick, int flashtype)
65 {
66 	fire_heat (self, start, dir, offset, damage, kick, true);
67 
68 	gi.WriteByte (svc_muzzleflash2);
69 	gi.WriteShort (self - g_edicts);
70 	gi.WriteByte (flashtype);
71 	gi.multicast (start, MULTICAST_PVS);
72 }
73 //ROGUE
74 
monster_fire_grenade(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed,int flashtype)75 void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype)
76 {
77 	fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40);
78 
79 	gi.WriteByte (svc_muzzleflash2);
80 	gi.WriteShort (self - g_edicts);
81 	gi.WriteByte (flashtype);
82 	gi.multicast (start, MULTICAST_PVS);
83 }
84 
monster_fire_rocket(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int flashtype)85 void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
86 {
87 	fire_rocket (self, start, dir, damage, speed, damage+20, damage);
88 
89 	gi.WriteByte (svc_muzzleflash2);
90 	gi.WriteShort (self - g_edicts);
91 	gi.WriteByte (flashtype);
92 	gi.multicast (start, MULTICAST_PVS);
93 }
94 
monster_fire_railgun(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick,int flashtype)95 void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype)
96 {
97 	// PMM
98 	if (!(gi.pointcontents (start) & MASK_SOLID))
99 		fire_rail (self, start, aimdir, damage, kick);
100 
101 	gi.WriteByte (svc_muzzleflash2);
102 	gi.WriteShort (self - g_edicts);
103 	gi.WriteByte (flashtype);
104 	gi.multicast (start, MULTICAST_PVS);
105 }
106 
monster_fire_bfg(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed,int kick,float damage_radius,int flashtype)107 void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
108 {
109 	fire_bfg (self, start, aimdir, damage, speed, damage_radius);
110 
111 	gi.WriteByte (svc_muzzleflash2);
112 	gi.WriteShort (self - g_edicts);
113 	gi.WriteByte (flashtype);
114 	gi.multicast (start, MULTICAST_PVS);
115 }
116 
117 
118 
119 //
120 // Monster utility functions
121 //
122 
M_FliesOff(edict_t * self)123 void M_FliesOff (edict_t *self)
124 {
125 	self->s.effects &= ~EF_FLIES;
126 	self->s.sound = 0;
127 }
128 
M_FliesOn(edict_t * self)129 void M_FliesOn (edict_t *self)
130 {
131 	if (self->waterlevel)
132 		return;
133 	self->s.effects |= EF_FLIES;
134 	self->s.sound = gi.soundindex ("infantry/inflies1.wav");
135 	self->think = M_FliesOff;
136 	self->nextthink = level.time + 60;
137 }
138 
M_FlyCheck(edict_t * self)139 void M_FlyCheck (edict_t *self)
140 {
141 	if (self->waterlevel)
142 		return;
143 
144 	if (random() > 0.5)
145 		return;
146 
147 	self->think = M_FliesOn;
148 	self->nextthink = level.time + 5 + 10 * random();
149 }
150 
AttackFinished(edict_t * self,float time)151 void AttackFinished (edict_t *self, float time)
152 {
153 	self->monsterinfo.attack_finished = level.time + time;
154 }
155 
156 
M_CheckGround(edict_t * ent)157 void M_CheckGround (edict_t *ent)
158 {
159 	vec3_t		point;
160 	trace_t		trace;
161 
162 	if (ent->flags & (FL_SWIM|FL_FLY))
163 		return;
164 
165 #ifdef ROGUE_GRAVITY
166 	if ((ent->velocity[2] * ent->gravityVector[2]) < -100)		// PGM
167 #else
168 	if (ent->velocity[2] > 100)
169 #endif
170 	{
171 		ent->groundentity = NULL;
172 		return;
173 	}
174 
175 // if the hull point one-quarter unit down is solid the entity is on ground
176 	point[0] = ent->s.origin[0];
177 	point[1] = ent->s.origin[1];
178 #ifdef ROGUE_GRAVITY
179 	point[2] = ent->s.origin[2] + (0.25 * ent->gravityVector[2]);	//PGM
180 #else
181 	point[2] = ent->s.origin[2] - 0.25;
182 #endif
183 
184 	trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID);
185 
186 	// check steepness
187 #ifdef ROGUE_GRAVITY
188 //PGM
189 	if ( ent->gravityVector[2] < 0)		// normal gravity
190 	{
191 		if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
192 		{
193 			ent->groundentity = NULL;
194 			return;
195 		}
196 	}
197 	else								// inverted gravity
198 	{
199 		if ( trace.plane.normal[2] > -0.7 && !trace.startsolid)
200 		{
201 			ent->groundentity = NULL;
202 			return;
203 		}
204 	}
205 //PGM
206 #else
207 	if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
208 	{
209 		ent->groundentity = NULL;
210 		return;
211 	}
212 #endif
213 
214 //	ent->groundentity = trace.ent;
215 //	ent->groundentity_linkcount = trace.ent->linkcount;
216 //	if (!trace.startsolid && !trace.allsolid)
217 //		VectorCopy (trace.endpos, ent->s.origin);
218 	if (!trace.startsolid && !trace.allsolid)
219 	{
220 		VectorCopy (trace.endpos, ent->s.origin);
221 		ent->groundentity = trace.ent;
222 		ent->groundentity_linkcount = trace.ent->linkcount;
223 		ent->velocity[2] = 0;
224 	}
225 }
226 
227 
M_CatagorizePosition(edict_t * ent)228 void M_CatagorizePosition (edict_t *ent)
229 {
230 	vec3_t		point;
231 	int			cont;
232 
233 //
234 // get waterlevel
235 //
236 	point[0] = ent->s.origin[0];
237 	point[1] = ent->s.origin[1];
238 	point[2] = ent->s.origin[2] + ent->mins[2] + 1;
239 	cont = gi.pointcontents (point);
240 
241 	if (!(cont & MASK_WATER))
242 	{
243 		ent->waterlevel = 0;
244 		ent->watertype = 0;
245 		return;
246 	}
247 
248 	ent->watertype = cont;
249 	ent->waterlevel = 1;
250 	point[2] += 26;
251 	cont = gi.pointcontents (point);
252 	if (!(cont & MASK_WATER))
253 		return;
254 
255 	ent->waterlevel = 2;
256 	point[2] += 22;
257 	cont = gi.pointcontents (point);
258 	if (cont & MASK_WATER)
259 		ent->waterlevel = 3;
260 }
261 
262 
M_WorldEffects(edict_t * ent)263 void M_WorldEffects (edict_t *ent)
264 {
265 	int		dmg;
266 
267 	if (ent->health > 0)
268 	{
269 		if (!(ent->flags & FL_SWIM))
270 		{
271 			if (ent->waterlevel < 3)
272 			{
273 				ent->air_finished = level.time + 12;
274 			}
275 			else if (ent->air_finished < level.time)
276 			{	// drown!
277 				if (ent->pain_debounce_time < level.time)
278 				{
279 					dmg = 2 + 2 * floor(level.time - ent->air_finished);
280 					if (dmg > 15)
281 						dmg = 15;
282 					T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
283 					ent->pain_debounce_time = level.time + 1;
284 				}
285 			}
286 		}
287 		else
288 		{
289 			if (ent->waterlevel > 0)
290 			{
291 				ent->air_finished = level.time + 9;
292 			}
293 			else if (ent->air_finished < level.time)
294 			{	// suffocate!
295 				if (ent->pain_debounce_time < level.time)
296 				{
297 					dmg = 2 + 2 * floor(level.time - ent->air_finished);
298 					if (dmg > 15)
299 						dmg = 15;
300 					T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
301 					ent->pain_debounce_time = level.time + 1;
302 				}
303 			}
304 		}
305 	}
306 
307 	if (ent->waterlevel == 0)
308 	{
309 		if (ent->flags & FL_INWATER)
310 		{
311 			gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
312 			ent->flags &= ~FL_INWATER;
313 		}
314 		return;
315 	}
316 
317 	if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA))
318 	{
319 		if (ent->damage_debounce_time < level.time)
320 		{
321 			ent->damage_debounce_time = level.time + 0.2;
322 			T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA);
323 		}
324 	}
325 	if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
326 	{
327 		if (ent->damage_debounce_time < level.time)
328 		{
329 			ent->damage_debounce_time = level.time + 1;
330 			T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME);
331 		}
332 	}
333 
334 	if ( !(ent->flags & FL_INWATER) )
335 	{
336 		if (!(ent->svflags & SVF_DEADMONSTER))
337 		{
338 			if (ent->watertype & CONTENTS_LAVA)
339 				if (random() <= 0.5)
340 					gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0);
341 				else
342 					gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0);
343 			else if (ent->watertype & CONTENTS_SLIME)
344 				gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
345 			else if (ent->watertype & CONTENTS_WATER)
346 				gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
347 		}
348 
349 		ent->flags |= FL_INWATER;
350 		ent->damage_debounce_time = 0;
351 	}
352 }
353 
354 
M_droptofloor(edict_t * ent)355 void M_droptofloor (edict_t *ent)
356 {
357 	vec3_t		end;
358 	trace_t		trace;
359 
360 #ifdef ROGUE_GRAVITY
361 //PGM
362 	if(ent->gravityVector[2] < 0)
363 	{
364 		ent->s.origin[2] += 1;
365 		VectorCopy (ent->s.origin, end);
366 		end[2] -= 256;
367 	}
368 	else
369 	{
370 		ent->s.origin[2] -= 1;
371 		VectorCopy (ent->s.origin, end);
372 		end[2] += 256;
373 	}
374 //PGM
375 #else
376 	ent->s.origin[2] += 1;
377 	VectorCopy (ent->s.origin, end);
378 	end[2] -= 256;
379 #endif
380 
381 	trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
382 
383 	if (trace.fraction == 1 || trace.allsolid)
384 		return;
385 
386 	VectorCopy (trace.endpos, ent->s.origin);
387 
388 	gi.linkentity (ent);
389 	M_CheckGround (ent);
390 	M_CatagorizePosition (ent);
391 }
392 
393 
M_SetEffects(edict_t * ent)394 void M_SetEffects (edict_t *ent)
395 {
396 	int remaining;
397 
398 	ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN|EF_DOUBLE|EF_QUAD|EF_PENT);
399 	ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE|RF_SHELL_DOUBLE);
400 
401 	if (ent->monsterinfo.aiflags & AI_RESURRECTING)
402 	{
403 		ent->s.effects |= EF_COLOR_SHELL;
404 		ent->s.renderfx |= RF_SHELL_RED;
405 	}
406 
407 	if (ent->health <= 0)
408 		return;
409 
410 	if (ent->powerarmor_time > level.time)
411 	{
412 		if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN)
413 		{
414 			ent->s.effects |= EF_POWERSCREEN;
415 		}
416 		else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD)
417 		{
418 			ent->s.effects |= EF_COLOR_SHELL;
419 			ent->s.renderfx |= RF_SHELL_GREEN;
420 		}
421 	}
422 	// PMM - new monster powerups
423 	if (ent->monsterinfo.quad_framenum > level.framenum)
424 	{
425 		remaining = ent->monsterinfo.quad_framenum - level.framenum;
426 		if (remaining > 30 || (remaining & 4) )
427 			ent->s.effects |= EF_QUAD;
428 	}
429 	else
430 		ent->s.effects &= ~EF_QUAD;
431 
432 	if (ent->monsterinfo.double_framenum > level.framenum)
433 	{
434 		remaining = ent->monsterinfo.double_framenum - level.framenum;
435 		if (remaining > 30 || (remaining & 4) )
436 			ent->s.effects |= EF_DOUBLE;
437 	}
438 	else
439 		ent->s.effects &= ~EF_DOUBLE;
440 
441 	if (ent->monsterinfo.invincible_framenum > level.framenum)
442 	{
443 		remaining = ent->monsterinfo.invincible_framenum - level.framenum;
444 		if (remaining > 30 || (remaining & 4) )
445 			ent->s.effects |= EF_PENT;
446 	}
447 	else
448 		ent->s.effects &= ~EF_PENT;
449 
450 	// PMM
451 	// PMM - testing
452 //	ent->s.effects |= EF_COLOR_SHELL;
453 //	ent->s.renderfx |= RF_SHELL_HALF_DAM;
454 /*
455 	if (fmod (level.time, 4.0) > 2.0)
456 	{
457 		gi.dprintf ("invulnerable ");
458 		ent->s.renderfx |= RF_SHELL_RED;
459 	}
460 	else
461 		ent->s.renderfx &= ~RF_SHELL_RED;
462 
463 	if (fmod (level.time, 8.0) > 4.0)
464 	{
465 		gi.dprintf ("shield ");
466 		ent->s.renderfx |= RF_SHELL_GREEN;
467 	}
468 	else
469 		ent->s.renderfx &= ~RF_SHELL_GREEN;
470 
471 	if (fmod (level.time, 16.0) > 8.0)
472 	{
473 		gi.dprintf ("quad ");
474 		ent->s.renderfx |= RF_SHELL_BLUE;\
475 	}
476 	else
477 		ent->s.renderfx &= ~RF_SHELL_BLUE;
478 
479 	if (fmod (level.time, 32.0) > 16.0)
480 	{
481 		gi.dprintf ("double ");
482 		ent->s.renderfx |= RF_SHELL_DOUBLE;
483 	}
484 	else
485 		ent->s.renderfx &= ~RF_SHELL_DOUBLE;
486 
487 	if (fmod (level.time, 64.0) > 32.0)
488 	{
489 		gi.dprintf ("half ");
490 		ent->s.renderfx |= RF_SHELL_HALF_DAM;
491 	}
492 	else
493 		ent->s.renderfx &= ~RF_SHELL_HALF_DAM;
494 
495 	gi.dprintf ("\n");
496 */
497 }
498 
499 
M_MoveFrame(edict_t * self)500 void M_MoveFrame (edict_t *self)
501 {
502 	mmove_t	*move;
503 	int		index;
504 
505 	move = self->monsterinfo.currentmove;
506 	self->nextthink = level.time + FRAMETIME;
507 
508 	if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe))
509 	{
510 		self->s.frame = self->monsterinfo.nextframe;
511 		self->monsterinfo.nextframe = 0;
512 	}
513 	else
514 	{
515 		if (self->s.frame == move->lastframe)
516 		{
517 			if (move->endfunc)
518 			{
519 				move->endfunc (self);
520 
521 				// regrab move, endfunc is very likely to change it
522 				move = self->monsterinfo.currentmove;
523 
524 				// check for death
525 				if (self->svflags & SVF_DEADMONSTER)
526 					return;
527 			}
528 		}
529 
530 		if (self->s.frame < move->firstframe || self->s.frame > move->lastframe)
531 		{
532 			self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
533 			self->s.frame = move->firstframe;
534 		}
535 		else
536 		{
537 			if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
538 			{
539 				self->s.frame++;
540 				if (self->s.frame > move->lastframe)
541 					self->s.frame = move->firstframe;
542 			}
543 		}
544 	}
545 
546 	index = self->s.frame - move->firstframe;
547 
548 	if (move->frame[index].aifunc)
549 		if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
550 			move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale);
551 		else
552 			move->frame[index].aifunc (self, 0);
553 
554 	if (move->frame[index].thinkfunc)
555 		move->frame[index].thinkfunc (self);
556 }
557 
558 
monster_think(edict_t * self)559 void monster_think (edict_t *self)
560 {
561 	M_MoveFrame (self);
562 	if (self->linkcount != self->monsterinfo.linkcount)
563 	{
564 		self->monsterinfo.linkcount = self->linkcount;
565 		M_CheckGround (self);
566 	}
567 	M_CatagorizePosition (self);
568 	M_WorldEffects (self);
569 	M_SetEffects (self);
570 }
571 
572 
573 /*
574 ================
575 monster_use
576 
577 Using a monster makes it angry at the current activator
578 ================
579 */
monster_use(edict_t * self,edict_t * other,edict_t * activator)580 void monster_use (edict_t *self, edict_t *other, edict_t *activator)
581 {
582 	if (self->enemy)
583 		return;
584 	if (self->health <= 0)
585 		return;
586 	if (activator->flags & FL_NOTARGET)
587 		return;
588 	if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY))
589 		return;
590 	if (activator->flags & FL_DISGUISED)		// PGM
591 		return;									// PGM
592 
593 // delay reaction so if the monster is teleported, its sound is still heard
594 	self->enemy = activator;
595 	FoundTarget (self);
596 }
597 
598 
599 void monster_start_go (edict_t *self);
600 
601 
monster_triggered_spawn(edict_t * self)602 void monster_triggered_spawn (edict_t *self)
603 {
604 	self->s.origin[2] += 1;
605 	KillBox (self);
606 
607 	self->solid = SOLID_BBOX;
608 	self->movetype = MOVETYPE_STEP;
609 	self->svflags &= ~SVF_NOCLIENT;
610 	self->air_finished = level.time + 12;
611 	gi.linkentity (self);
612 
613 	monster_start_go (self);
614 
615 	if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
616 	{
617 		if(!(self->enemy->flags & FL_DISGUISED))		// PGM
618 			FoundTarget (self);
619 		else // PMM - just in case, make sure to clear the enemy so FindTarget doesn't get confused
620 			self->enemy = NULL;
621 	}
622 	else
623 	{
624 		self->enemy = NULL;
625 	}
626 }
627 
monster_triggered_spawn_use(edict_t * self,edict_t * other,edict_t * activator)628 void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
629 {
630 	// we have a one frame delay here so we don't telefrag the guy who activated us
631 	self->think = monster_triggered_spawn;
632 	self->nextthink = level.time + FRAMETIME;
633 	if (activator->client)
634 		self->enemy = activator;
635 	self->use = monster_use;
636 }
637 
monster_triggered_start(edict_t * self)638 void monster_triggered_start (edict_t *self)
639 {
640 	self->solid = SOLID_NOT;
641 	self->movetype = MOVETYPE_NONE;
642 	self->svflags |= SVF_NOCLIENT;
643 	self->nextthink = 0;
644 	self->use = monster_triggered_spawn_use;
645 }
646 
647 
648 /*
649 ================
650 monster_death_use
651 
652 When a monster dies, it fires all of its targets with the current
653 enemy as activator.
654 ================
655 */
monster_death_use(edict_t * self)656 void monster_death_use (edict_t *self)
657 {
658 	self->flags &= ~(FL_FLY|FL_SWIM);
659 	self->monsterinfo.aiflags &= AI_GOOD_GUY;
660 
661 	if (self->item)
662 	{
663 		Drop_Item (self, self->item);
664 		self->item = NULL;
665 	}
666 
667 	if (self->deathtarget)
668 		self->target = self->deathtarget;
669 
670 	if (!self->target)
671 		return;
672 
673 	G_UseTargets (self, self->enemy);
674 }
675 
676 
677 //============================================================================
678 
monster_start(edict_t * self)679 qboolean monster_start (edict_t *self)
680 {
681 	if (deathmatch->value)
682 	{
683 		G_FreeEdict (self);
684 		return false;
685 	}
686 
687 	if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY))
688 	{
689 		self->spawnflags &= ~4;
690 		self->spawnflags |= 1;
691 //		gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin));
692 	}
693 
694 	if ((!(self->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(self->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
695 		level.total_monsters++;
696 
697 	self->nextthink = level.time + FRAMETIME;
698 	self->svflags |= SVF_MONSTER;
699 	self->s.renderfx |= RF_FRAMELERP;
700 	self->takedamage = DAMAGE_AIM;
701 	self->air_finished = level.time + 12;
702 	self->use = monster_use;
703 	self->max_health = self->health;
704 	self->clipmask = MASK_MONSTERSOLID;
705 
706 	self->s.skinnum = 0;
707 	self->deadflag = DEAD_NO;
708 	self->svflags &= ~SVF_DEADMONSTER;
709 
710 	if (!self->monsterinfo.checkattack)
711 		self->monsterinfo.checkattack = M_CheckAttack;
712 	VectorCopy (self->s.origin, self->s.old_origin);
713 
714 	if (st.item)
715 	{
716 		self->item = FindItemByClassname (st.item);
717 		if (!self->item)
718 			gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
719 	}
720 
721 	// randomize what frame they start on
722 	if (self->monsterinfo.currentmove)
723 		self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
724 
725 	// PMM - get this so I don't have to do it in all of the monsters
726 	self->monsterinfo.base_height = self->maxs[2];
727 
728 	// PMM - clear these
729 	self->monsterinfo.quad_framenum = 0;
730 	self->monsterinfo.double_framenum = 0;
731 	self->monsterinfo.invincible_framenum = 0;
732 
733 	return true;
734 }
735 
monster_start_go(edict_t * self)736 void monster_start_go (edict_t *self)
737 {
738 	vec3_t	v;
739 
740 	if (self->health <= 0)
741 		return;
742 
743 	// check for target to combat_point and change to combattarget
744 	if (self->target)
745 	{
746 		qboolean	notcombat;
747 		qboolean	fixup;
748 		edict_t		*target;
749 
750 		target = NULL;
751 		notcombat = false;
752 		fixup = false;
753 		while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL)
754 		{
755 			if (strcmp(target->classname, "point_combat") == 0)
756 			{
757 				self->combattarget = self->target;
758 				fixup = true;
759 			}
760 			else
761 			{
762 				notcombat = true;
763 			}
764 		}
765 		if (notcombat && self->combattarget)
766 			gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
767 		if (fixup)
768 			self->target = NULL;
769 	}
770 
771 	// validate combattarget
772 	if (self->combattarget)
773 	{
774 		edict_t		*target;
775 
776 		target = NULL;
777 		while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL)
778 		{
779 			if (strcmp(target->classname, "point_combat") != 0)
780 			{
781 				gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
782 					self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
783 					self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
784 					(int)target->s.origin[2]);
785 			}
786 		}
787 	}
788 
789 	if (self->target)
790 	{
791 		self->goalentity = self->movetarget = G_PickTarget(self->target);
792 		if (!self->movetarget)
793 		{
794 			gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
795 			self->target = NULL;
796 			self->monsterinfo.pausetime = 100000000;
797 			self->monsterinfo.stand (self);
798 		}
799 		else if (strcmp (self->movetarget->classname, "path_corner") == 0)
800 		{
801 			VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
802 			self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
803 			self->monsterinfo.walk (self);
804 			self->target = NULL;
805 		}
806 		else
807 		{
808 			self->goalentity = self->movetarget = NULL;
809 			self->monsterinfo.pausetime = 100000000;
810 			self->monsterinfo.stand (self);
811 		}
812 	}
813 	else
814 	{
815 		self->monsterinfo.pausetime = 100000000;
816 		self->monsterinfo.stand (self);
817 	}
818 
819 	self->think = monster_think;
820 	self->nextthink = level.time + FRAMETIME;
821 }
822 
823 
walkmonster_start_go(edict_t * self)824 void walkmonster_start_go (edict_t *self)
825 {
826 	if (!(self->spawnflags & 2) && level.time < 1)
827 	{
828 		M_droptofloor (self);
829 
830 		if (self->groundentity)
831 			if (!M_walkmove (self, 0, 0))
832 				gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
833 	}
834 
835 	if (!self->yaw_speed)
836 		self->yaw_speed = 20;
837 	// PMM - stalkers are too short for this
838 	if (!(strcmp(self->classname, "monster_stalker")))
839 		self->viewheight = 15;
840 	else
841 		self->viewheight = 25;
842 
843 	monster_start_go (self);
844 
845 	if (self->spawnflags & 2)
846 		monster_triggered_start (self);
847 }
848 
walkmonster_start(edict_t * self)849 void walkmonster_start (edict_t *self)
850 {
851 	self->think = walkmonster_start_go;
852 	monster_start (self);
853 }
854 
855 
flymonster_start_go(edict_t * self)856 void flymonster_start_go (edict_t *self)
857 {
858 	if (!M_walkmove (self, 0, 0))
859 		gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
860 
861 	if (!self->yaw_speed)
862 		self->yaw_speed = 10;
863 	self->viewheight = 25;
864 
865 	monster_start_go (self);
866 
867 	if (self->spawnflags & 2)
868 		monster_triggered_start (self);
869 }
870 
871 
flymonster_start(edict_t * self)872 void flymonster_start (edict_t *self)
873 {
874 	self->flags |= FL_FLY;
875 	self->think = flymonster_start_go;
876 	monster_start (self);
877 }
878 
879 
swimmonster_start_go(edict_t * self)880 void swimmonster_start_go (edict_t *self)
881 {
882 	if (!self->yaw_speed)
883 		self->yaw_speed = 10;
884 	self->viewheight = 10;
885 
886 	monster_start_go (self);
887 
888 	if (self->spawnflags & 2)
889 		monster_triggered_start (self);
890 }
891 
swimmonster_start(edict_t * self)892 void swimmonster_start (edict_t *self)
893 {
894 	self->flags |= FL_SWIM;
895 	self->think = swimmonster_start_go;
896 	monster_start (self);
897 }
898 
899 //ROGUE
900 
901 void stationarymonster_start_go (edict_t *self);
902 
stationarymonster_triggered_spawn(edict_t * self)903 void stationarymonster_triggered_spawn (edict_t *self)
904 {
905 	KillBox (self);
906 
907 	self->solid = SOLID_BBOX;
908 	self->movetype = MOVETYPE_NONE;
909 	self->svflags &= ~SVF_NOCLIENT;
910 	self->air_finished = level.time + 12;
911 	gi.linkentity (self);
912 
913 	// FIXME - why doesn't this happen with real monsters?
914 	self->spawnflags &= ~2;
915 
916 	stationarymonster_start_go (self);
917 
918 	if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
919 	{
920 		if(!(self->enemy->flags & FL_DISGUISED))		// PGM
921 			FoundTarget (self);
922 		else // PMM - just in case, make sure to clear the enemy so FindTarget doesn't get confused
923 			self->enemy = NULL;
924 	}
925 	else
926 	{
927 		self->enemy = NULL;
928 	}
929 }
930 
stationarymonster_triggered_spawn_use(edict_t * self,edict_t * other,edict_t * activator)931 void stationarymonster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
932 {
933 	// we have a one frame delay here so we don't telefrag the guy who activated us
934 	self->think = stationarymonster_triggered_spawn;
935 	self->nextthink = level.time + FRAMETIME;
936 	if (activator->client)
937 		self->enemy = activator;
938 	self->use = monster_use;
939 }
940 
stationarymonster_triggered_start(edict_t * self)941 void stationarymonster_triggered_start (edict_t *self)
942 {
943 	self->solid = SOLID_NOT;
944 	self->movetype = MOVETYPE_NONE;
945 	self->svflags |= SVF_NOCLIENT;
946 	self->nextthink = 0;
947 	self->use = stationarymonster_triggered_spawn_use;
948 }
949 
stationarymonster_start_go(edict_t * self)950 void stationarymonster_start_go (edict_t *self)
951 {
952 // PGM - only turrets use this, so remove the error message. They're supposed to be in solid.
953 
954 //	if (!M_walkmove (self, 0, 0))
955 //		gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
956 
957 	if (!self->yaw_speed)
958 		self->yaw_speed = 20;
959 //	self->viewheight = 25;
960 
961 	monster_start_go (self);
962 
963 	if (self->spawnflags & 2)
964 		stationarymonster_triggered_start (self);
965 }
966 
stationarymonster_start(edict_t * self)967 void stationarymonster_start (edict_t *self)
968 {
969 	self->think = stationarymonster_start_go;
970 	monster_start (self);
971 }
972 
monster_done_dodge(edict_t * self)973 void monster_done_dodge (edict_t *self)
974 {
975 //	if ((g_showlogic) && (g_showlogic->value))
976 //		gi.dprintf ("%s done dodging\n", self->classname);
977 	self->monsterinfo.aiflags &= ~AI_DODGING;
978 }
979 //ROGUE
980