Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

m_actor.c (13963B)


      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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 }