quake2-rerelease-dll/rerelease/m_arachnid.cpp

386 lines
8.7 KiB
C++
Raw Normal View History

2023-08-07 19:47:28 +00:00
// Copyright (c) ZeniMax Media Inc.
// Licensed under the GNU General Public License 2.0.
/*
==============================================================================
TANK
==============================================================================
*/
#include "g_local.h"
#include "m_arachnid.h"
#include "m_flash.h"
static int sound_pain;
static int sound_death;
static int sound_sight;
MONSTERINFO_SIGHT(arachnid_sight) (edict_t *self, edict_t *other) -> void
{
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
}
//
// stand
//
mframe_t arachnid_frames_stand[] = {
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand },
{ ai_stand }
};
MMOVE_T(arachnid_move_stand) = { FRAME_idle1, FRAME_idle13, arachnid_frames_stand, nullptr };
MONSTERINFO_STAND(arachnid_stand) (edict_t *self) -> void
{
M_SetAnimation(self, &arachnid_move_stand);
}
//
// walk
//
static int sound_step;
void arachnid_footstep(edict_t *self)
{
gi.sound(self, CHAN_BODY, sound_step, 0.5f, ATTN_IDLE, 0.0f);
}
mframe_t arachnid_frames_walk[] = {
{ ai_walk, 8, arachnid_footstep },
{ ai_walk, 8 },
{ ai_walk, 8 },
{ ai_walk, 8 },
{ ai_walk, 8 },
{ ai_walk, 8, arachnid_footstep },
{ ai_walk, 8 },
{ ai_walk, 8 },
{ ai_walk, 8 },
{ ai_walk, 8 }
};
MMOVE_T(arachnid_move_walk) = { FRAME_walk1, FRAME_walk10, arachnid_frames_walk, nullptr };
MONSTERINFO_WALK(arachnid_walk) (edict_t *self) -> void
{
M_SetAnimation(self, &arachnid_move_walk);
}
//
// run
//
mframe_t arachnid_frames_run[] = {
{ ai_run, 8, arachnid_footstep },
{ ai_run, 8 },
{ ai_run, 8 },
{ ai_run, 8 },
{ ai_run, 8 },
{ ai_run, 8, arachnid_footstep },
{ ai_run, 8 },
{ ai_run, 8 },
{ ai_run, 8 },
{ ai_run, 8 }
};
MMOVE_T(arachnid_move_run) = { FRAME_walk1, FRAME_walk10, arachnid_frames_run, nullptr };
MONSTERINFO_RUN(arachnid_run) (edict_t *self) -> void
{
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
{
M_SetAnimation(self, &arachnid_move_stand);
return;
}
M_SetAnimation(self, &arachnid_move_run);
}
//
// pain
//
mframe_t arachnid_frames_pain1[] = {
{ ai_move },
{ ai_move },
{ ai_move },
{ ai_move },
{ ai_move }
};
MMOVE_T(arachnid_move_pain1) = { FRAME_pain11, FRAME_pain15, arachnid_frames_pain1, arachnid_run };
mframe_t arachnid_frames_pain2[] = {
{ ai_move },
{ ai_move },
{ ai_move },
{ ai_move },
{ ai_move },
{ ai_move }
};
MMOVE_T(arachnid_move_pain2) = { FRAME_pain21, FRAME_pain26, arachnid_frames_pain2, arachnid_run };
PAIN(arachnid_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
{
if (level.time < self->pain_debounce_time)
return;
self->pain_debounce_time = level.time + 3_sec;
gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
if (!M_ShouldReactToPain(self, mod))
return; // no pain anims in nightmare
float r = frandom();
if (r < 0.5f)
M_SetAnimation(self, &arachnid_move_pain1);
else
M_SetAnimation(self, &arachnid_move_pain2);
}
static int sound_charge;
void arachnid_charge_rail(edict_t *self)
{
if (!self->enemy || !self->enemy->inuse)
return;
gi.sound(self, CHAN_WEAPON, sound_charge, 1.f, ATTN_NORM, 0.f);
self->pos1 = self->enemy->s.origin;
self->pos1[2] += self->enemy->viewheight;
}
void arachnid_rail(edict_t *self)
{
vec3_t start;
vec3_t dir;
vec3_t forward, right;
monster_muzzleflash_id_t id;
switch (self->s.frame)
{
case FRAME_rails4:
default:
id = MZ2_ARACHNID_RAIL1;
break;
case FRAME_rails8:
id = MZ2_ARACHNID_RAIL2;
break;
case FRAME_rails_up7:
id = MZ2_ARACHNID_RAIL_UP1;
break;
case FRAME_rails_up11:
id = MZ2_ARACHNID_RAIL_UP2;
break;
}
AngleVectors(self->s.angles, forward, right, nullptr);
start = M_ProjectFlashSource(self, monster_flash_offset[id], forward, right);
// calc direction to where we targeted
dir = self->pos1 - start;
dir.normalize();
monster_fire_railgun(self, start, dir, 35, 100, id);
}
mframe_t arachnid_frames_attack1[] = {
{ ai_charge, 0, arachnid_charge_rail },
{ ai_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_rail },
{ ai_charge, 0, arachnid_charge_rail },
{ ai_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_rail },
{ ai_charge },
{ ai_charge },
{ ai_charge }
};
MMOVE_T(arachnid_attack1) = { FRAME_rails1, FRAME_rails11, arachnid_frames_attack1, arachnid_run };
mframe_t arachnid_frames_attack_up1[] = {
{ ai_charge },
{ ai_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_charge_rail },
{ ai_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_rail },
{ ai_charge, 0, arachnid_charge_rail },
{ ai_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_rail },
{ ai_charge },
{ ai_charge },
{ ai_charge },
{ ai_charge },
{ ai_charge },
};
MMOVE_T(arachnid_attack_up1) = { FRAME_rails_up1, FRAME_rails_up16, arachnid_frames_attack_up1, arachnid_run };
static int sound_melee, sound_melee_hit;
void arachnid_melee_charge(edict_t *self)
{
gi.sound(self, CHAN_WEAPON, sound_melee, 1.f, ATTN_NORM, 0.f);
}
void arachnid_melee_hit(edict_t *self)
{
if (!fire_hit(self, { MELEE_DISTANCE, 0, 0 }, 15, 50))
self->monsterinfo.melee_debounce_time = level.time + 1000_ms;
}
mframe_t arachnid_frames_melee[] = {
{ ai_charge },
{ ai_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_melee_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_melee_hit },
{ ai_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_melee_charge },
{ ai_charge },
{ ai_charge, 0, arachnid_melee_hit },
{ ai_charge }
};
MMOVE_T(arachnid_melee) = { FRAME_melee_atk1, FRAME_melee_atk12, arachnid_frames_melee, arachnid_run };
MONSTERINFO_ATTACK(arachnid_attack) (edict_t *self) -> void
{
if (!self->enemy || !self->enemy->inuse)
return;
if (self->monsterinfo.melee_debounce_time < level.time && range_to(self, self->enemy) < MELEE_DISTANCE)
M_SetAnimation(self, &arachnid_melee);
else if ((self->enemy->s.origin[2] - self->s.origin[2]) > 150.f)
M_SetAnimation(self, &arachnid_attack_up1);
else
M_SetAnimation(self, &arachnid_attack1);
}
//
// death
//
void arachnid_dead(edict_t *self)
{
self->mins = { -16, -16, -24 };
self->maxs = { 16, 16, -8 };
self->movetype = MOVETYPE_TOSS;
self->svflags |= SVF_DEADMONSTER;
self->nextthink = 0_ms;
gi.linkentity(self);
}
mframe_t arachnid_frames_death1[] = {
{ ai_move, 0 },
{ ai_move, -1.23f },
{ ai_move, -1.23f },
{ ai_move, -1.23f },
{ ai_move, -1.23f },
{ ai_move, -1.64f },
{ ai_move, -1.64f },
{ ai_move, -2.45f },
{ ai_move, -8.63f },
{ ai_move, -4.0f },
{ ai_move, -4.5f },
{ ai_move, -6.8f },
{ ai_move, -8.0f },
{ ai_move, -5.4f },
{ ai_move, -3.4f },
{ ai_move, -1.9f },
{ ai_move },
{ ai_move },
{ ai_move },
{ ai_move }
};
MMOVE_T(arachnid_move_death) = { FRAME_death1, FRAME_death20, arachnid_frames_death1, arachnid_dead };
DIE(arachnid_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
{
// check for gib
if (M_CheckGib(self, mod))
{
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
ThrowGibs(self, damage, {
{ 2, "models/objects/gibs/bone/tris.md2" },
{ 4, "models/objects/gibs/sm_meat/tris.md2" },
{ "models/objects/gibs/head2/tris.md2", GIB_HEAD }
});
self->deadflag = true;
return;
}
if (self->deadflag)
return;
// regular death
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
self->deadflag = true;
self->takedamage = true;
M_SetAnimation(self, &arachnid_move_death);
}
//
// monster_arachnid
//
/*QUAKED monster_arachnid (1 .5 0) (-48 -48 -20) (48 48 48) Ambush Trigger_Spawn Sight
*/
void SP_monster_arachnid(edict_t *self)
{
if ( !M_AllowSpawn( self ) ) {
G_FreeEdict( self );
return;
}
sound_step = gi.soundindex("insane/insane11.wav");
sound_charge = gi.soundindex("gladiator/railgun.wav");
sound_melee = gi.soundindex("gladiator/melee3.wav");
sound_melee_hit = gi.soundindex("gladiator/melee2.wav");
sound_pain = gi.soundindex("arachnid/pain.wav");
sound_death = gi.soundindex("arachnid/death.wav");
sound_sight = gi.soundindex("arachnid/sight.wav");
self->s.modelindex = gi.modelindex("models/monsters/arachnid/tris.md2");
self->mins = { -48, -48, -20 };
self->maxs = { 48, 48, 48 };
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
self->health = 1000 * st.health_multiplier;
self->gib_health = -200;
self->monsterinfo.scale = MODEL_SCALE;
self->mass = 450;
self->pain = arachnid_pain;
self->die = arachnid_die;
self->monsterinfo.stand = arachnid_stand;
self->monsterinfo.walk = arachnid_walk;
self->monsterinfo.run = arachnid_run;
self->monsterinfo.attack = arachnid_attack;
self->monsterinfo.sight = arachnid_sight;
gi.linkentity(self);
M_SetAnimation(self, &arachnid_move_stand);
walkmonster_start(self);
}