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