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