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_actor.c
21 
22 #include "g_local.h"
23 #include "m_actor.h"
24 
25 #define	MAX_ACTOR_NAMES		8
26 char *actor_names[MAX_ACTOR_NAMES] =
27 {
28 	"Hellrot",
29 	"Tokay",
30 	"Killme",
31 	"Disruptor",
32 	"Adrianator",
33 	"Rambear",
34 	"Titus",
35 	"Bitterman"
36 };
37 
38 
39 mframe_t actor_frames_stand [] =
40 {
41 	{ai_stand, 0, NULL},
42 	{ai_stand, 0, NULL},
43 	{ai_stand, 0, NULL},
44 	{ai_stand, 0, NULL},
45 	{ai_stand, 0, NULL},
46 	{ai_stand, 0, NULL},
47 	{ai_stand, 0, NULL},
48 	{ai_stand, 0, NULL},
49 	{ai_stand, 0, NULL},
50 	{ai_stand, 0, NULL},
51 
52 	{ai_stand, 0, NULL},
53 	{ai_stand, 0, NULL},
54 	{ai_stand, 0, NULL},
55 	{ai_stand, 0, NULL},
56 	{ai_stand, 0, NULL},
57 	{ai_stand, 0, NULL},
58 	{ai_stand, 0, NULL},
59 	{ai_stand, 0, NULL},
60 	{ai_stand, 0, NULL},
61 	{ai_stand, 0, NULL},
62 
63 	{ai_stand, 0, NULL},
64 	{ai_stand, 0, NULL},
65 	{ai_stand, 0, NULL},
66 	{ai_stand, 0, NULL},
67 	{ai_stand, 0, NULL},
68 	{ai_stand, 0, NULL},
69 	{ai_stand, 0, NULL},
70 	{ai_stand, 0, NULL},
71 	{ai_stand, 0, NULL},
72 	{ai_stand, 0, NULL},
73 
74 	{ai_stand, 0, NULL},
75 	{ai_stand, 0, NULL},
76 	{ai_stand, 0, NULL},
77 	{ai_stand, 0, NULL},
78 	{ai_stand, 0, NULL},
79 	{ai_stand, 0, NULL},
80 	{ai_stand, 0, NULL},
81 	{ai_stand, 0, NULL},
82 	{ai_stand, 0, NULL},
83 	{ai_stand, 0, NULL}
84 };
85 mmove_t actor_move_stand = {FRAME_stand101, FRAME_stand140, actor_frames_stand, NULL};
86 
actor_stand(edict_t * self)87 void actor_stand (edict_t *self)
88 {
89 	self->monsterinfo.currentmove = &actor_move_stand;
90 
91 	// randomize on startup
92 	if (level.time < 1.0)
93 		self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
94 }
95 
96 
97 mframe_t actor_frames_walk [] =
98 {
99 	{ai_walk, 0,  NULL},
100 	{ai_walk, 6,  NULL},
101 	{ai_walk, 10, NULL},
102 	{ai_walk, 3,  NULL},
103 	{ai_walk, 2,  NULL},
104 	{ai_walk, 7,  NULL},
105 	{ai_walk, 10, NULL},
106 	{ai_walk, 1,  NULL},
107 	{ai_walk, 4,  NULL},
108 	{ai_walk, 0,  NULL},
109 	{ai_walk, 0,  NULL}
110 };
111 mmove_t actor_move_walk = {FRAME_walk01, FRAME_walk08, actor_frames_walk, NULL};
112 
actor_walk(edict_t * self)113 void actor_walk (edict_t *self)
114 {
115 	self->monsterinfo.currentmove = &actor_move_walk;
116 }
117 
118 
119 mframe_t actor_frames_run [] =
120 {
121 	{ai_run, 4,  NULL},
122 	{ai_run, 15, NULL},
123 	{ai_run, 15, NULL},
124 	{ai_run, 8,  NULL},
125 	{ai_run, 20, NULL},
126 	{ai_run, 15, NULL},
127 	{ai_run, 8,  NULL},
128 	{ai_run, 17, NULL},
129 	{ai_run, 12, NULL},
130 	{ai_run, -2, NULL},
131 	{ai_run, -2, NULL},
132 	{ai_run, -1, NULL}
133 };
134 mmove_t actor_move_run = {FRAME_run02, FRAME_run07, actor_frames_run, NULL};
135 
actor_run(edict_t * self)136 void actor_run (edict_t *self)
137 {
138 	if ((level.time < self->pain_debounce_time) && (!self->enemy))
139 	{
140 		if (self->movetarget)
141 			actor_walk(self);
142 		else
143 			actor_stand(self);
144 		return;
145 	}
146 
147 	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
148 	{
149 		actor_stand(self);
150 		return;
151 	}
152 
153 	self->monsterinfo.currentmove = &actor_move_run;
154 }
155 
156 
157 mframe_t actor_frames_pain1 [] =
158 {
159 	{ai_move, -5, NULL},
160 	{ai_move, 4,  NULL},
161 	{ai_move, 1,  NULL}
162 };
163 mmove_t actor_move_pain1 = {FRAME_pain101, FRAME_pain103, actor_frames_pain1, actor_run};
164 
165 mframe_t actor_frames_pain2 [] =
166 {
167 	{ai_move, -4, NULL},
168 	{ai_move, 4,  NULL},
169 	{ai_move, 0,  NULL}
170 };
171 mmove_t actor_move_pain2 = {FRAME_pain201, FRAME_pain203, actor_frames_pain2, actor_run};
172 
173 mframe_t actor_frames_pain3 [] =
174 {
175 	{ai_move, -1, NULL},
176 	{ai_move, 1,  NULL},
177 	{ai_move, 0,  NULL}
178 };
179 mmove_t actor_move_pain3 = {FRAME_pain301, FRAME_pain303, actor_frames_pain3, actor_run};
180 
181 mframe_t actor_frames_flipoff [] =
182 {
183 	{ai_turn, 0,  NULL},
184 	{ai_turn, 0,  NULL},
185 	{ai_turn, 0,  NULL},
186 	{ai_turn, 0,  NULL},
187 	{ai_turn, 0,  NULL},
188 	{ai_turn, 0,  NULL},
189 	{ai_turn, 0,  NULL},
190 	{ai_turn, 0,  NULL},
191 	{ai_turn, 0,  NULL},
192 	{ai_turn, 0,  NULL},
193 	{ai_turn, 0,  NULL},
194 	{ai_turn, 0,  NULL},
195 	{ai_turn, 0,  NULL},
196 	{ai_turn, 0,  NULL}
197 };
198 mmove_t actor_move_flipoff = {FRAME_flip01, FRAME_flip14, actor_frames_flipoff, actor_run};
199 
200 mframe_t actor_frames_taunt [] =
201 {
202 	{ai_turn, 0,  NULL},
203 	{ai_turn, 0,  NULL},
204 	{ai_turn, 0,  NULL},
205 	{ai_turn, 0,  NULL},
206 	{ai_turn, 0,  NULL},
207 	{ai_turn, 0,  NULL},
208 	{ai_turn, 0,  NULL},
209 	{ai_turn, 0,  NULL},
210 	{ai_turn, 0,  NULL},
211 	{ai_turn, 0,  NULL},
212 	{ai_turn, 0,  NULL},
213 	{ai_turn, 0,  NULL},
214 	{ai_turn, 0,  NULL},
215 	{ai_turn, 0,  NULL},
216 	{ai_turn, 0,  NULL},
217 	{ai_turn, 0,  NULL},
218 	{ai_turn, 0,  NULL}
219 };
220 mmove_t actor_move_taunt = {FRAME_taunt01, FRAME_taunt17, actor_frames_taunt, actor_run};
221 
222 char *messages[] =
223 {
224 	"Watch it",
225 	"#$@*&",
226 	"Idiot",
227 	"Check your targets"
228 };
229 
actor_pain(edict_t * self,edict_t * other,float kick,int damage)230 void actor_pain (edict_t *self, edict_t *other, float kick, int damage)
231 {
232 	int		n;
233 
234 	if (self->health < (self->max_health / 2))
235 		self->s.skinnum = 1;
236 
237 	if (level.time < self->pain_debounce_time)
238 		return;
239 
240 	self->pain_debounce_time = level.time + 3;
241 //	gi.sound (self, CHAN_VOICE, actor.sound_pain, 1, ATTN_NORM, 0);
242 
243 	if ((other->client) && (random() < 0.4))
244 	{
245 		vec3_t	v;
246 		char	*name;
247 
248 		VectorSubtract (other->s.origin, self->s.origin, v);
249 		self->ideal_yaw = vectoyaw (v);
250 		if (random() < 0.5)
251 			self->monsterinfo.currentmove = &actor_move_flipoff;
252 		else
253 			self->monsterinfo.currentmove = &actor_move_taunt;
254 		name = actor_names[(self - g_edicts)%MAX_ACTOR_NAMES];
255 		gi.cprintf (other, PRINT_CHAT, "%s: %s!\n", name, messages[rand()%3]);
256 		return;
257 	}
258 
259 	n = rand() % 3;
260 	if (n == 0)
261 		self->monsterinfo.currentmove = &actor_move_pain1;
262 	else if (n == 1)
263 		self->monsterinfo.currentmove = &actor_move_pain2;
264 	else
265 		self->monsterinfo.currentmove = &actor_move_pain3;
266 }
267 
268 
actorMachineGun(edict_t * self)269 void actorMachineGun (edict_t *self)
270 {
271 	vec3_t	start, target;
272 	vec3_t	forward, right;
273 
274 	AngleVectors (self->s.angles, forward, right, NULL);
275 	G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_ACTOR_MACHINEGUN_1], forward, right, start);
276 	if (self->enemy)
277 	{
278 		if (self->enemy->health > 0)
279 		{
280 			VectorMA (self->enemy->s.origin, -0.2, self->enemy->velocity, target);
281 			target[2] += self->enemy->viewheight;
282 		}
283 		else
284 		{
285 			VectorCopy (self->enemy->absmin, target);
286 			target[2] += (self->enemy->size[2] / 2);
287 		}
288 		VectorSubtract (target, start, forward);
289 		VectorNormalize (forward);
290 	}
291 	else
292 	{
293 		AngleVectors (self->s.angles, forward, NULL, NULL);
294 	}
295 	monster_fire_bullet (self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_ACTOR_MACHINEGUN_1);
296 }
297 
298 
actor_dead(edict_t * self)299 void actor_dead (edict_t *self)
300 {
301 	VectorSet (self->mins, -16, -16, -24);
302 	VectorSet (self->maxs, 16, 16, -8);
303 	self->movetype = MOVETYPE_TOSS;
304 	self->svflags |= SVF_DEADMONSTER;
305 	self->nextthink = 0;
306 	gi.linkentity (self);
307 }
308 
309 mframe_t actor_frames_death1 [] =
310 {
311 	{ai_move, 0,   NULL},
312 	{ai_move, 0,   NULL},
313 	{ai_move, -13, NULL},
314 	{ai_move, 14,  NULL},
315 	{ai_move, 3,   NULL},
316 	{ai_move, -2,  NULL},
317 	{ai_move, 1,   NULL}
318 };
319 mmove_t actor_move_death1 = {FRAME_death101, FRAME_death107, actor_frames_death1, actor_dead};
320 
321 mframe_t actor_frames_death2 [] =
322 {
323 	{ai_move, 0,   NULL},
324 	{ai_move, 7,   NULL},
325 	{ai_move, -6,  NULL},
326 	{ai_move, -5,  NULL},
327 	{ai_move, 1,   NULL},
328 	{ai_move, 0,   NULL},
329 	{ai_move, -1,  NULL},
330 	{ai_move, -2,  NULL},
331 	{ai_move, -1,  NULL},
332 	{ai_move, -9,  NULL},
333 	{ai_move, -13, NULL},
334 	{ai_move, -13, NULL},
335 	{ai_move, 0,   NULL}
336 };
337 mmove_t actor_move_death2 = {FRAME_death201, FRAME_death213, actor_frames_death2, actor_dead};
338 
actor_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)339 void actor_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
340 {
341 	int		n;
342 
343 // check for gib
344 	if (self->health <= -80)
345 	{
346 //		gi.sound (self, CHAN_VOICE, actor.sound_gib, 1, ATTN_NORM, 0);
347 		for (n= 0; n < 2; n++)
348 			ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
349 		for (n= 0; n < 4; n++)
350 			ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
351 		ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
352 		self->deadflag = DEAD_DEAD;
353 		return;
354 	}
355 
356 	if (self->deadflag == DEAD_DEAD)
357 		return;
358 
359 // regular death
360 //	gi.sound (self, CHAN_VOICE, actor.sound_die, 1, ATTN_NORM, 0);
361 	self->deadflag = DEAD_DEAD;
362 	self->takedamage = DAMAGE_YES;
363 
364 	n = rand() % 2;
365 	if (n == 0)
366 		self->monsterinfo.currentmove = &actor_move_death1;
367 	else
368 		self->monsterinfo.currentmove = &actor_move_death2;
369 }
370 
371 
actor_fire(edict_t * self)372 void actor_fire (edict_t *self)
373 {
374 	actorMachineGun (self);
375 
376 	if (level.time >= self->monsterinfo.pausetime)
377 		self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
378 	else
379 		self->monsterinfo.aiflags |= AI_HOLD_FRAME;
380 }
381 
382 mframe_t actor_frames_attack [] =
383 {
384 	{ai_charge, -2,  actor_fire},
385 	{ai_charge, -2,  NULL},
386 	{ai_charge, 3,   NULL},
387 	{ai_charge, 2,   NULL}
388 };
389 mmove_t actor_move_attack = {FRAME_attak01, FRAME_attak04, actor_frames_attack, actor_run};
390 
actor_attack(edict_t * self)391 void actor_attack(edict_t *self)
392 {
393 	int		n;
394 
395 	self->monsterinfo.currentmove = &actor_move_attack;
396 	n = (rand() & 15) + 3 + 7;
397 	self->monsterinfo.pausetime = level.time + n * FRAMETIME;
398 }
399 
400 
actor_use(edict_t * self,edict_t * other,edict_t * activator)401 void actor_use (edict_t *self, edict_t *other, edict_t *activator)
402 {
403 	vec3_t		v;
404 
405 	self->goalentity = self->movetarget = G_PickTarget(self->target);
406 	if ((!self->movetarget) || (strcmp(self->movetarget->classname, "target_actor") != 0))
407 	{
408 		gi.dprintf ("%s has bad target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
409 		self->target = NULL;
410 		self->monsterinfo.pausetime = 100000000;
411 		self->monsterinfo.stand (self);
412 		return;
413 	}
414 
415 	VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
416 	self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
417 	self->monsterinfo.walk (self);
418 	self->target = NULL;
419 }
420 
421 
422 /*QUAKED misc_actor (1 .5 0) (-16 -16 -24) (16 16 32)
423 */
424 
SP_misc_actor(edict_t * self)425 void SP_misc_actor (edict_t *self)
426 {
427 	if (deathmatch->value)
428 	{
429 		G_FreeEdict (self);
430 		return;
431 	}
432 
433 	if (!self->targetname)
434 	{
435 		gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
436 		G_FreeEdict (self);
437 		return;
438 	}
439 
440 	if (!self->target)
441 	{
442 		gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
443 		G_FreeEdict (self);
444 		return;
445 	}
446 
447 	self->movetype = MOVETYPE_STEP;
448 	self->solid = SOLID_BBOX;
449 	self->s.modelindex = gi.modelindex("players/male/tris.md2");
450 	VectorSet (self->mins, -16, -16, -24);
451 	VectorSet (self->maxs, 16, 16, 32);
452 
453 	if (!self->health)
454 		self->health = 100;
455 	self->mass = 200;
456 
457 	self->pain = actor_pain;
458 	self->die = actor_die;
459 
460 	self->monsterinfo.stand = actor_stand;
461 	self->monsterinfo.walk = actor_walk;
462 	self->monsterinfo.run = actor_run;
463 	self->monsterinfo.attack = actor_attack;
464 	self->monsterinfo.melee = NULL;
465 	self->monsterinfo.sight = NULL;
466 
467 	self->monsterinfo.aiflags |= AI_GOOD_GUY;
468 
469 	gi.linkentity (self);
470 
471 	self->monsterinfo.currentmove = &actor_move_stand;
472 	self->monsterinfo.scale = MODEL_SCALE;
473 
474 	walkmonster_start (self);
475 
476 	// actors always start in a dormant state, they *must* be used to get going
477 	self->use = actor_use;
478 }
479 
480 
481 /*QUAKED target_actor (.5 .3 0) (-8 -8 -8) (8 8 8) JUMP SHOOT ATTACK x HOLD BRUTAL
482 JUMP			jump in set direction upon reaching this target
483 SHOOT			take a single shot at the pathtarget
484 ATTACK			attack pathtarget until it or actor is dead
485 
486 "target"		next target_actor
487 "pathtarget"	target of any action to be taken at this point
488 "wait"			amount of time actor should pause at this point
489 "message"		actor will "say" this to the player
490 
491 for JUMP only:
492 "speed"			speed thrown forward (default 200)
493 "height"		speed thrown upwards (default 200)
494 */
495 
target_actor_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)496 void target_actor_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
497 {
498 	vec3_t	v;
499 
500 	if (other->movetarget != self)
501 		return;
502 
503 	if (other->enemy)
504 		return;
505 
506 	other->goalentity = other->movetarget = NULL;
507 
508 	if (self->message)
509 	{
510 		int		n;
511 		edict_t	*ent;
512 
513 		for (n = 1; n <= game.maxclients; n++)
514 		{
515 			ent = &g_edicts[n];
516 			if (!ent->inuse)
517 				continue;
518 			gi.cprintf (ent, PRINT_CHAT, "%s: %s\n", actor_names[(other - g_edicts)%MAX_ACTOR_NAMES], self->message);
519 		}
520 	}
521 
522 	if (self->spawnflags & 1)		//jump
523 	{
524 		other->velocity[0] = self->movedir[0] * self->speed;
525 		other->velocity[1] = self->movedir[1] * self->speed;
526 
527 		if (other->groundentity)
528 		{
529 			other->groundentity = NULL;
530 			other->velocity[2] = self->movedir[2];
531 			gi.sound(other, CHAN_VOICE, gi.soundindex("player/male/jump1.wav"), 1, ATTN_NORM, 0);
532 		}
533 	}
534 
535 	if (self->spawnflags & 2)	//shoot
536 	{
537 	}
538 	else if (self->spawnflags & 4)	//attack
539 	{
540 		other->enemy = G_PickTarget(self->pathtarget);
541 		if (other->enemy)
542 		{
543 			other->goalentity = other->enemy;
544 			if (self->spawnflags & 32)
545 				other->monsterinfo.aiflags |= AI_BRUTAL;
546 			if (self->spawnflags & 16)
547 			{
548 				other->monsterinfo.aiflags |= AI_STAND_GROUND;
549 				actor_stand (other);
550 			}
551 			else
552 			{
553 				actor_run (other);
554 			}
555 		}
556 	}
557 
558 	if (!(self->spawnflags & 6) && (self->pathtarget))
559 	{
560 		char *savetarget;
561 
562 		savetarget = self->target;
563 		self->target = self->pathtarget;
564 		G_UseTargets (self, other);
565 		self->target = savetarget;
566 	}
567 
568 	other->movetarget = G_PickTarget(self->target);
569 
570 	if (!other->goalentity)
571 		other->goalentity = other->movetarget;
572 
573 	if (!other->movetarget && !other->enemy)
574 	{
575 		other->monsterinfo.pausetime = level.time + 100000000;
576 		other->monsterinfo.stand (other);
577 	}
578 	else if (other->movetarget == other->goalentity)
579 	{
580 		VectorSubtract (other->movetarget->s.origin, other->s.origin, v);
581 		other->ideal_yaw = vectoyaw (v);
582 	}
583 }
584 
SP_target_actor(edict_t * self)585 void SP_target_actor (edict_t *self)
586 {
587 	if (!self->targetname)
588 		gi.dprintf ("%s with no targetname at %s\n", self->classname, vtos(self->s.origin));
589 
590 	self->solid = SOLID_TRIGGER;
591 	self->touch = target_actor_touch;
592 	VectorSet (self->mins, -8, -8, -8);
593 	VectorSet (self->maxs, 8, 8, 8);
594 	self->svflags = SVF_NOCLIENT;
595 
596 	if (self->spawnflags & 1)
597 	{
598 		if (!self->speed)
599 			self->speed = 200;
600 		if (!st.height)
601 			st.height = 200;
602 		if (self->s.angles[YAW] == 0)
603 			self->s.angles[YAW] = 360;
604 		G_SetMovedir (self->s.angles, self->movedir);
605 		self->movedir[2] = st.height;
606 	}
607 
608 	gi.linkentity (self);
609 }
610