1 /*
2  * Copyright (C) 1997-2001 Id Software, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * 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 along with
16  * this program; if not, write to the Free Software Foundation, Inc., 59
17  * 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 
87 void
actor_stand(edict_t * self)88 actor_stand(edict_t * self)
89 {
90 	self->monsterinfo.currentmove = &actor_move_stand;
91 
92 	/* randomize on startup */
93 	if (level.time < 1.0)
94 		self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
95 }
96 
97 
98 mframe_t	actor_frames_walk[] =
99 {
100 	{ai_walk, 0, NULL},
101 	{ai_walk, 6, NULL},
102 	{ai_walk, 10, NULL},
103 	{ai_walk, 3, NULL},
104 	{ai_walk, 2, NULL},
105 	{ai_walk, 7, NULL},
106 	{ai_walk, 10, NULL},
107 	{ai_walk, 1, NULL},
108 	{ai_walk, 4, NULL},
109 	{ai_walk, 0, NULL},
110 	{ai_walk, 0, NULL}
111 };
112 mmove_t		actor_move_walk = {FRAME_walk01, FRAME_walk08, actor_frames_walk, NULL};
113 
114 void
actor_walk(edict_t * self)115 actor_walk(edict_t * self)
116 {
117 	self->monsterinfo.currentmove = &actor_move_walk;
118 }
119 
120 
121 mframe_t	actor_frames_run[] =
122 {
123 	{ai_run, 4, NULL},
124 	{ai_run, 15, NULL},
125 	{ai_run, 15, NULL},
126 	{ai_run, 8, NULL},
127 	{ai_run, 20, NULL},
128 	{ai_run, 15, NULL},
129 	{ai_run, 8, NULL},
130 	{ai_run, 17, NULL},
131 	{ai_run, 12, NULL},
132 	{ai_run, -2, NULL},
133 	{ai_run, -2, NULL},
134 	{ai_run, -1, NULL}
135 };
136 mmove_t		actor_move_run = {FRAME_run02, FRAME_run07, actor_frames_run, NULL};
137 
138 void
actor_run(edict_t * self)139 actor_run(edict_t * self)
140 {
141 	if ((level.time < self->pain_debounce_time) && (!self->enemy)) {
142 		if (self->movetarget)
143 			actor_walk(self);
144 		else
145 			actor_stand(self);
146 		return;
147 	}
148 	if (self->monsterinfo.aiflags & AI_STAND_GROUND) {
149 		actor_stand(self);
150 		return;
151 	}
152 	self->monsterinfo.currentmove = &actor_move_run;
153 }
154 
155 
156 mframe_t	actor_frames_pain1[] =
157 {
158 	{ai_move, -5, NULL},
159 	{ai_move, 4, NULL},
160 	{ai_move, 1, NULL}
161 };
162 mmove_t		actor_move_pain1 = {FRAME_pain101, FRAME_pain103, actor_frames_pain1, actor_run};
163 
164 mframe_t	actor_frames_pain2[] =
165 {
166 	{ai_move, -4, NULL},
167 	{ai_move, 4, NULL},
168 	{ai_move, 0, NULL}
169 };
170 mmove_t		actor_move_pain2 = {FRAME_pain201, FRAME_pain203, actor_frames_pain2, actor_run};
171 
172 mframe_t	actor_frames_pain3[] =
173 {
174 	{ai_move, -1, NULL},
175 	{ai_move, 1, NULL},
176 	{ai_move, 0, NULL}
177 };
178 mmove_t		actor_move_pain3 = {FRAME_pain301, FRAME_pain303, actor_frames_pain3, actor_run};
179 
180 mframe_t	actor_frames_flipoff[] =
181 {
182 	{ai_turn, 0, NULL},
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 };
197 mmove_t		actor_move_flipoff = {FRAME_flip01, FRAME_flip14, actor_frames_flipoff, actor_run};
198 
199 mframe_t	actor_frames_taunt[] =
200 {
201 	{ai_turn, 0, NULL},
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 };
219 mmove_t		actor_move_taunt = {FRAME_taunt01, FRAME_taunt17, actor_frames_taunt, actor_run};
220 
221 char           *messages[] =
222 {
223 	"Watch it",
224 	"#$@*&",
225 	"Idiot",
226 	"Check your targets"
227 };
228 
229 void
actor_pain(edict_t * self,edict_t * other,float kick,int damage)230 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 		vec3_t		v;
245 		char           *name;
246 
247 		VectorSubtract(other->s.origin, self->s.origin, v);
248 		self->ideal_yaw = vectoyaw(v);
249 		if (random() < 0.5)
250 			self->monsterinfo.currentmove = &actor_move_flipoff;
251 		else
252 			self->monsterinfo.currentmove = &actor_move_taunt;
253 		name = actor_names[(self - g_edicts) % MAX_ACTOR_NAMES];
254 #ifdef WITH_ACEBOT
255 		safe_cprintf
256 #else
257 		gi.cprintf
258 #endif
259 		    (other, PRINT_CHAT, "%s: %s!\n", name, messages[rand() % 3]);
260 		return;
261 	}
262 	n = rand() % 3;
263 	if (n == 0)
264 		self->monsterinfo.currentmove = &actor_move_pain1;
265 	else if (n == 1)
266 		self->monsterinfo.currentmove = &actor_move_pain2;
267 	else
268 		self->monsterinfo.currentmove = &actor_move_pain3;
269 }
270 
271 
272 void
actorMachineGun(edict_t * self)273 actorMachineGun(edict_t * self)
274 {
275 	vec3_t		start  , target;
276 	vec3_t		forward, right;
277 
278 	AngleVectors(self->s.angles, forward, right, NULL);
279 	G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_ACTOR_MACHINEGUN_1], forward, right, start);
280 	if (self->enemy) {
281 		if (self->enemy->health > 0) {
282 			VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target);
283 			target[2] += self->enemy->viewheight;
284 		} else {
285 			VectorCopy(self->enemy->absmin, target);
286 			target[2] += (self->enemy->size[2] / 2);
287 		}
288 		VectorSubtract(target, start, forward);
289 		VectorNormalize(forward);
290 	} else {
291 		AngleVectors(self->s.angles, forward, NULL, NULL);
292 	}
293 	monster_fire_bullet(self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_ACTOR_MACHINEGUN_1);
294 }
295 
296 
297 void
actor_dead(edict_t * self)298 actor_dead(edict_t * self)
299 {
300 	VectorSet(self->mins, -16, -16, -24);
301 	VectorSet(self->maxs, 16, 16, -8);
302 	self->movetype = MOVETYPE_TOSS;
303 	self->svflags |= SVF_DEADMONSTER;
304 	self->nextthink = 0;
305 	gi.linkentity(self);
306 }
307 
308 mframe_t	actor_frames_death1[] =
309 {
310 	{ai_move, 0, NULL},
311 	{ai_move, 0, NULL},
312 	{ai_move, -13, NULL},
313 	{ai_move, 14, NULL},
314 	{ai_move, 3, NULL},
315 	{ai_move, -2, NULL},
316 	{ai_move, 1, NULL}
317 };
318 mmove_t		actor_move_death1 = {FRAME_death101, FRAME_death107, actor_frames_death1, actor_dead};
319 
320 mframe_t	actor_frames_death2[] =
321 {
322 	{ai_move, 0, NULL},
323 	{ai_move, 7, NULL},
324 	{ai_move, -6, NULL},
325 	{ai_move, -5, NULL},
326 	{ai_move, 1, NULL},
327 	{ai_move, 0, NULL},
328 	{ai_move, -1, NULL},
329 	{ai_move, -2, NULL},
330 	{ai_move, -1, NULL},
331 	{ai_move, -9, NULL},
332 	{ai_move, -13, NULL},
333 	{ai_move, -13, NULL},
334 	{ai_move, 0, NULL}
335 };
336 mmove_t		actor_move_death2 = {FRAME_death201, FRAME_death213, actor_frames_death2, actor_dead};
337 
338 void
actor_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)339 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 		/*
347 		 * gi.sound (self, CHAN_VOICE, actor.sound_gib, 1, ATTN_NORM,
348 		 * 0);
349 		 */
350 		for (n = 0; n < 2; n++)
351 			ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
352 		for (n = 0; n < 4; n++)
353 			ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
354 		ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
355 		self->deadflag = DEAD_DEAD;
356 		return;
357 	}
358 	if (self->deadflag == DEAD_DEAD)
359 		return;
360 
361 	/* regular death */
362 	/* gi.sound (self, CHAN_VOICE, actor.sound_die, 1, ATTN_NORM, 0); */
363 	self->deadflag = DEAD_DEAD;
364 	self->takedamage = DAMAGE_YES;
365 
366 	n = rand() % 2;
367 	if (n == 0)
368 		self->monsterinfo.currentmove = &actor_move_death1;
369 	else
370 		self->monsterinfo.currentmove = &actor_move_death2;
371 }
372 
373 
374 void
actor_fire(edict_t * self)375 actor_fire(edict_t * self)
376 {
377 	actorMachineGun(self);
378 
379 	if (level.time >= self->monsterinfo.pausetime)
380 		self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
381 	else
382 		self->monsterinfo.aiflags |= AI_HOLD_FRAME;
383 }
384 
385 mframe_t	actor_frames_attack[] =
386 {
387 	{ai_charge, -2, actor_fire},
388 	{ai_charge, -2, NULL},
389 	{ai_charge, 3, NULL},
390 	{ai_charge, 2, NULL}
391 };
392 mmove_t		actor_move_attack = {FRAME_attak01, FRAME_attak04, actor_frames_attack, actor_run};
393 
394 void
actor_attack(edict_t * self)395 actor_attack(edict_t * self)
396 {
397 	int		n;
398 
399 	self->monsterinfo.currentmove = &actor_move_attack;
400 	n = (rand() & 15) + 3 + 7;
401 	self->monsterinfo.pausetime = level.time + n * FRAMETIME;
402 }
403 
404 
405 void
actor_use(edict_t * self,edict_t * other,edict_t * activator)406 actor_use(edict_t * self, edict_t * other, edict_t * activator)
407 {
408 	vec3_t		v;
409 
410 	self->goalentity = self->movetarget = G_PickTarget(self->target);
411 	if ((!self->movetarget) || (strcmp(self->movetarget->classname, "target_actor") != 0)) {
412 		gi.dprintf("%s has bad target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
413 		self->target = NULL;
414 		self->monsterinfo.pausetime = 100000000;
415 		self->monsterinfo.stand(self);
416 		return;
417 	}
418 	VectorSubtract(self->goalentity->s.origin, self->s.origin, v);
419 	self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
420 	self->monsterinfo.walk(self);
421 	self->target = NULL;
422 }
423 
424 
425 /*
426  * QUAKED misc_actor (1 .5 0) (-16 -16 -24) (16 16 32)
427  */
428 
429 void
SP_misc_actor(edict_t * self)430 SP_misc_actor(edict_t * self)
431 {
432 	if (deathmatch->value) {
433 		G_FreeEdict(self);
434 		return;
435 	}
436 	if (!self->targetname) {
437 		gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
438 		G_FreeEdict(self);
439 		return;
440 	}
441 	if (!self->target) {
442 		gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
443 		G_FreeEdict(self);
444 		return;
445 	}
446 	self->movetype = MOVETYPE_STEP;
447 	self->solid = SOLID_BBOX;
448 	self->s.modelindex = gi.modelindex("players/male/tris.md2");
449 	VectorSet(self->mins, -16, -16, -24);
450 	VectorSet(self->maxs, 16, 16, 32);
451 
452 	if (!self->health)
453 		self->health = 100;
454 	self->mass = 200;
455 
456 	self->pain = actor_pain;
457 	self->die = actor_die;
458 
459 	self->monsterinfo.stand = actor_stand;
460 	self->monsterinfo.walk = actor_walk;
461 	self->monsterinfo.run = actor_run;
462 	self->monsterinfo.attack = actor_attack;
463 	self->monsterinfo.melee = NULL;
464 	self->monsterinfo.sight = NULL;
465 
466 	self->monsterinfo.aiflags |= AI_GOOD_GUY;
467 
468 	gi.linkentity(self);
469 
470 	self->monsterinfo.currentmove = &actor_move_stand;
471 	self->monsterinfo.scale = MODEL_SCALE;
472 
473 	walkmonster_start(self);
474 
475 	/*
476 	 * actors always start in a dormant state, they *must* be used to get
477 	 * going
478 	 */
479 	self->use = actor_use;
480 }
481 
482 
483 /*
484  * QUAKED target_actor (.5 .3 0) (-8 -8 -8) (8 8 8) JUMP SHOOT ATTACK x HOLD
485  * BRUTAL JUMP			jump in set direction upon reaching this
486  * target SHOOT			take a single shot at the pathtarget ATTACK
487  * ttack pathtarget until it or actor is dead
488  *
489  * "target"		next target_actor "pathtarget"	target of any action
490  * to be taken at this point "wait"			amount of time actor
491  * should pause at this point "message"		actor will "say" this to the
492  * player
493  *
494  * for JUMP only: "speed"			speed thrown forward (default 200)
495  * "height"		speed thrown upwards (default 200)
496  */
497 
498 void
target_actor_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)499 target_actor_touch(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
500 {
501 	vec3_t		v;
502 
503 	if (other->movetarget != self)
504 		return;
505 
506 	if (other->enemy)
507 		return;
508 
509 	other->goalentity = other->movetarget = NULL;
510 
511 	if (self->message) {
512 		int		n;
513 		edict_t        *ent;
514 
515 		for (n = 1; n <= game.maxclients; n++) {
516 			ent = &g_edicts[n];
517 			if (!ent->inuse)
518 				continue;
519 #ifdef WITH_ACEBOT
520 			safe_cprintf
521 #else
522 			gi.cprintf
523 #endif
524 			    (ent, PRINT_CHAT, "%s: %s\n", actor_names[(other - g_edicts) % MAX_ACTOR_NAMES], self->message);
525 		}
526 	}
527 	if (self->spawnflags & 1) {	/* jump */
528 		other->velocity[0] = self->movedir[0] * self->speed;
529 		other->velocity[1] = self->movedir[1] * self->speed;
530 
531 		if (other->groundentity) {
532 			other->groundentity = NULL;
533 			other->velocity[2] = self->movedir[2];
534 			gi.sound(other, CHAN_VOICE, gi.soundindex("player/male/jump1.wav"), 1, ATTN_NORM, 0);
535 		}
536 	}
537 	if (self->spawnflags & 2) {	/* shoot */
538 	} else if (self->spawnflags & 4) {	/* attack */
539 		other->enemy = G_PickTarget(self->pathtarget);
540 		if (other->enemy) {
541 			other->goalentity = other->enemy;
542 			if (self->spawnflags & 32)
543 				other->monsterinfo.aiflags |= AI_BRUTAL;
544 			if (self->spawnflags & 16) {
545 				other->monsterinfo.aiflags |= AI_STAND_GROUND;
546 				actor_stand(other);
547 			} else {
548 				actor_run(other);
549 			}
550 		}
551 	}
552 	if (!(self->spawnflags & 6) && (self->pathtarget)) {
553 		char           *savetarget;
554 
555 		savetarget = self->target;
556 		self->target = self->pathtarget;
557 		G_UseTargets(self, other);
558 		self->target = savetarget;
559 	}
560 	other->movetarget = G_PickTarget(self->target);
561 
562 	if (!other->goalentity)
563 		other->goalentity = other->movetarget;
564 
565 	if (!other->movetarget && !other->enemy) {
566 		other->monsterinfo.pausetime = level.time + 100000000;
567 		other->monsterinfo.stand(other);
568 	} else if (other->movetarget == other->goalentity) {
569 		VectorSubtract(other->movetarget->s.origin, other->s.origin, v);
570 		other->ideal_yaw = vectoyaw(v);
571 	}
572 }
573 
574 void
SP_target_actor(edict_t * self)575 SP_target_actor(edict_t * self)
576 {
577 	if (!self->targetname)
578 		gi.dprintf("%s with no targetname at %s\n", self->classname, vtos(self->s.origin));
579 
580 	self->solid = SOLID_TRIGGER;
581 	self->touch = target_actor_touch;
582 	VectorSet(self->mins, -8, -8, -8);
583 	VectorSet(self->maxs, 8, 8, 8);
584 	self->svflags = SVF_NOCLIENT;
585 
586 	if (self->spawnflags & 1) {
587 		if (!self->speed)
588 			self->speed = 200;
589 		if (!st.height)
590 			st.height = 200;
591 		if (self->s.angles[YAW] == 0)
592 			self->s.angles[YAW] = 360;
593 		G_SetMovedir(self->s.angles, self->movedir);
594 		self->movedir[2] = st.height;
595 	}
596 	gi.linkentity(self);
597 }
598