thirtyflightsofloving/missionpack/z_hound.c
Knightmare66 add4c7cc46 Reworked 2D pic drawing using a new, merged R_DrawPic() function. Moved old pic drawing functions to r_draw_removed.c.
Added new SCR_DrawPic() variants in cl_screen.c.
Added new graphics for text fields and sliders in menus.
Improved mouse interaction for menu sliders.
Added resettargets developer command to default Lazarus and  missionpack DLLs.
Added hint_test developer command to missionpack DLL.
Fixed freeze developer command in default Lazarus and missionpack DLLs so it can be used more than once.
More tweaks to Tactician Gunner prox mine safety checks in misssionpack DLL.
2021-08-07 00:43:46 -04:00

738 lines
16 KiB
C

/*
==============================================================================
hound
==============================================================================
*/
#include "g_local.h"
#include "z_hound.h"
static int sound_pain1;
static int sound_pain2;
static int sound_die;
static int sound_launch;
static int sound_impact;
static int sound_sight;
static int sound_bite;
static int sound_bitemiss;
static int sound_jump;
void hound_stand (edict_t *self);
void hound_run (edict_t *self);
void hound_walk (edict_t *self);
void hound_launch (edict_t *self)
{
gi.sound (self, CHAN_WEAPON, sound_launch, 1, ATTN_NORM, 0);
}
void hound_sight (edict_t *self, edict_t *other)
{
gi.sound (self, CHAN_WEAPON, sound_sight, 1, ATTN_NORM, 0);
}
//
// STAND
//
mframe_t hound_frames_stand1 [] =
{
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL, // 10
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL
};
mmove_t hound_stand1 = {FRAME_stand1start, FRAME_stand1end, hound_frames_stand1, hound_stand};
mframe_t hound_frames_stand2 [] =
{
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL, // 10
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL,
ai_schoolStand, 0, NULL, // 20
ai_schoolStand, 0, NULL
};
mmove_t hound_stand2 = {FRAME_stand2start, FRAME_stand2end, hound_frames_stand2, hound_stand};
void hound_stand (edict_t *self)
{
if (random() < 0.8)
{
self->monsterinfo.currentmove = &hound_stand1;
}
else
{
self->monsterinfo.currentmove = &hound_stand2;
}
}
//
// RUN
//
mframe_t hound_frames_run [] =
{
ai_schoolRun, 60, NULL,
ai_schoolRun, 60, NULL,
ai_schoolRun, 40, NULL,
ai_schoolRun, 30, NULL,
ai_schoolRun, 30, NULL,
ai_schoolRun, 30, NULL,
ai_schoolRun, 40, NULL
};
mmove_t hound_move_run = {FRAME_runStart, FRAME_runEnd, hound_frames_run, NULL};
void hound_run (edict_t *self)
{
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
hound_stand(self);
else
self->monsterinfo.currentmove = &hound_move_run;
}
//
// WALK
//
mframe_t hound_frames_walk [] =
{
ai_schoolWalk, 7, NULL,
ai_schoolWalk, 7, NULL,
ai_schoolWalk, 7, NULL,
ai_schoolWalk, 7, NULL,
ai_schoolWalk, 7, NULL,
ai_schoolWalk, 7, NULL,
ai_schoolWalk, 7, NULL,
ai_schoolWalk, 7, NULL
};
mmove_t hound_move_walk = {FRAME_walkStart, FRAME_walkEnd, hound_frames_walk, hound_walk};
void hound_walk (edict_t *self)
{
self->monsterinfo.currentmove = &hound_move_walk;
}
//
// PAIN
//
mframe_t hound_frames_pain1 [] =
{
ai_move, 6, NULL,
ai_move, 16, NULL,
ai_move, -6, NULL,
ai_move, -7, NULL,
};
mmove_t hound_move_pain1 = {FRAME_pain1Start, FRAME_pain1End, hound_frames_pain1, hound_run};
mframe_t hound_frames_pain2 [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 6, NULL,
ai_move, 16, NULL,
ai_move, -6, NULL,
ai_move, -7, NULL,
ai_move, 0, NULL,
};
mmove_t hound_move_pain2 = {FRAME_pain2Start, FRAME_pain2End, hound_frames_pain2, hound_run};
void hound_pain (edict_t *self, edict_t *other, float kick, int damage)
{
if (self->health < (self->max_health / 2))
self->s.skinnum |= 1;
if (random() < 0.5)
gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
else
gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
if (level.time < self->pain_debounce_time)
return;
self->pain_debounce_time = level.time + 3;
if (skill->value == 3)
return; // no pain anims in nightmare
if (random() < 0.5)
self->monsterinfo.currentmove = &hound_move_pain1;
else
self->monsterinfo.currentmove = &hound_move_pain2;
}
//
// MELEE
//
void hound_bite (edict_t *self)
{
vec3_t aim;
VectorSet (aim, MELEE_DISTANCE, self->mins[0], 8);
if (fire_hit (self, aim, (30 + (rand() %5)), 100))
gi.sound (self, CHAN_WEAPON, sound_bite, 1, ATTN_NORM, 0);
else
gi.sound (self, CHAN_WEAPON, sound_bitemiss, 1, ATTN_NORM, 0);
}
void hound_bite2 (edict_t *self)
{
vec3_t aim;
VectorSet (aim, MELEE_DISTANCE, self->mins[0], 8);
fire_hit (self, aim, (30 + (rand() %5)), 100);
}
mframe_t hound_frames_attack1 [] =
{
ai_schoolCharge, 0, hound_launch,
ai_schoolCharge, 0, NULL,
ai_schoolCharge, 0, hound_bite,
ai_schoolCharge, 0, hound_bite2
};
mmove_t hound_move_attack1 = {FRAME_attack1Start, FRAME_attack1End, hound_frames_attack1, hound_run};
mframe_t hound_frames_attack2 [] =
{
ai_schoolCharge, 0, hound_launch,
ai_schoolCharge, 0, NULL,
ai_schoolCharge, 0, NULL,
ai_schoolCharge, 0, NULL,
ai_schoolCharge, 0, NULL,
ai_schoolCharge, 0, NULL,
ai_schoolCharge, 0, NULL,
ai_schoolCharge, 0, NULL,
ai_schoolCharge, 0, hound_bite,
ai_schoolCharge, 0, hound_bite2,
ai_schoolCharge, 0, hound_bite2,
ai_schoolCharge, 0, hound_bite2,
ai_schoolCharge, 0, NULL,
};
mmove_t hound_move_attack2 = {FRAME_attack2Start, FRAME_attack2End, hound_frames_attack2, hound_run};
void hound_attack (edict_t *self)
{
if (random() < 0.6)
{
self->monsterinfo.currentmove = &hound_move_attack1;
}
else
{
self->monsterinfo.currentmove = &hound_move_attack2;
}
}
//
// ATTACK
//
void hound_jump_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (self->health <= 0)
{
self->touch = NULL;
return;
}
if (other->takedamage && strcmp(self->classname, other->classname) != 0)
{
if (VectorLength(self->velocity) > 400)
{
vec3_t point;
vec3_t normal;
int damage;
VectorCopy (self->velocity, normal);
VectorNormalize(normal);
VectorMA (self->s.origin, self->maxs[0], normal, point);
damage = 40 + 10 * random();
T_Damage (other, self, self, self->velocity, point, normal, damage, damage, 0, MOD_UNKNOWN);
}
}
if (!M_CheckBottom (self))
{
if (self->groundentity)
{
self->monsterinfo.nextframe = FRAME_leapLoop;
self->touch = NULL;
}
return;
}
self->touch = NULL;
}
void hound_jump_takeoff (edict_t *self)
{
vec3_t forward;
gi.sound (self, CHAN_VOICE, sound_jump, 1, ATTN_NORM, 0);
AngleVectors (self->s.angles, forward, NULL, NULL);
self->s.origin[2] += 1;
VectorScale (forward, 400, self->velocity);
self->velocity[2] = 200;
self->groundentity = NULL;
// self->monsterinfo.aiflags |= AI_JUMPING;
self->monsterinfo.attack_finished = level.time + 3;
self->touch = hound_jump_touch;
}
void hound_check_landing (edict_t *self)
{
if (self->groundentity)
{
gi.sound (self, CHAN_WEAPON, sound_impact, 1, ATTN_NORM, 0);
self->monsterinfo.attack_finished = 0;
// self->monsterinfo.aiflags &= ~AI_JUMPING;
return;
}
if (level.time > self->monsterinfo.attack_finished)
self->monsterinfo.nextframe = FRAME_leapLoop;
else
self->monsterinfo.nextframe = FRAME_leapEndStart;
}
void hound_check_landing2 (edict_t *self)
{
self->owner = NULL;
if (self->groundentity)
{
gi.sound (self, CHAN_WEAPON, sound_impact, 1, ATTN_NORM, 0);
self->monsterinfo.attack_finished = 0;
// self->monsterinfo.aiflags &= ~AI_JUMPING;
return;
}
if (level.time > self->monsterinfo.attack_finished)
self->monsterinfo.nextframe = FRAME_hattack1Loop;
else
self->monsterinfo.nextframe = FRAME_hattack1LoopEnd;
}
mframe_t hound_frames_handlerjump [] =
{
ai_charge, 0, NULL,
ai_charge, 20, hound_jump_takeoff,
ai_move, 40, NULL,
ai_move, 30, hound_check_landing2,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
};
mmove_t hound_move_handlerjump = {FRAME_hattack1Sep, FRAME_hattack1End, hound_frames_handlerjump, hound_run};
mframe_t hound_frames_jump [] =
{
ai_charge, 20, NULL,
ai_charge, 20, hound_jump_takeoff,
ai_move, 40, NULL,
ai_move, 30, hound_check_landing,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t hound_move_jump = {FRAME_leapStart, FRAME_leapEnd, hound_frames_jump, hound_run};
void hound_jump (edict_t *self)
{
self->monsterinfo.currentmove = &hound_move_jump;
}
/*
===
attack check routines
===
*/
qboolean hound_check_melee (edict_t *self)
{
if (range (self, self->enemy) == RANGE_MELEE)
return true;
return false;
}
qboolean hound_check_jump (edict_t *self)
{
vec3_t v;
float distance;
if (self->absmin[2] > (self->enemy->absmin[2] + 0.75 * self->enemy->size[2]))
return false;
if (self->absmax[2] < (self->enemy->absmin[2] + 0.25 * self->enemy->size[2]))
return false;
v[0] = self->s.origin[0] - self->enemy->s.origin[0];
v[1] = self->s.origin[1] - self->enemy->s.origin[1];
v[2] = 0;
distance = VectorLength(v);
if (distance < 100)
return false;
if (distance > 100)
{
if (random() < 0.9)
return false;
}
return true;
}
qboolean hound_checkattack (edict_t *self)
{
if (!self->enemy || self->enemy->health <= 0)
return false;
if (hound_check_melee(self))
{
self->monsterinfo.attack_state = AS_MELEE;
return true;
}
if (hound_check_jump(self))
{
self->monsterinfo.attack_state = AS_MISSILE;
// FIXME play a jump sound here
return true;
}
return false;
}
/*
===
Death Stuff Starts
===
*/
void hound_dead (edict_t *self)
{
// Lazarus: Stupid... if flies aren't set by M_FlyCheck, monster_think
// will cause us to come back here over and over and over
// until flies ARE set or monster is gibbed.
// This line fixes that:
self->nextthink = 0;
VectorSet (self->mins, -16, -16, -24);
VectorSet (self->maxs, 16, 16, -8);
self->movetype = MOVETYPE_TOSS;
self->svflags |= SVF_DEADMONSTER;
self->nextthink = 0;
gi.linkentity (self);
M_FlyCheck (self);
// Lazarus monster fade
if (world->effects & FX_WORLDSPAWN_CORPSEFADE)
{
self->think = FadeDieSink;
self->nextthink = level.time+corpse_fadetime->value;
}
}
mframe_t hound_frames_death [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t hound_move_death = {FRAME_die1Start, FRAME_die1End, hound_frames_death, hound_dead};
void hound_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
int n;
// check for gib
if (self->health <= self->gib_health && !(self->spawnflags & SF_MONSTER_NOGIB))
{
gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
for (n = 0; n < 2; n++)
ThrowGib (self, "models/objects/gibs/bone/tris.md2", 0, 0, damage, GIB_ORGANIC);
for (n = 0; n < 4; n++)
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", 0, 0, damage, GIB_ORGANIC);
ThrowHead (self, "models/objects/gibs/head2/tris.md2", 0, 0, damage, GIB_ORGANIC);
self->deadflag = DEAD_DEAD;
return;
}
if (self->deadflag == DEAD_DEAD)
return;
// regular death
gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
self->s.skinnum |= 1;
self->deadflag = DEAD_DEAD;
self->takedamage = DAMAGE_YES;
self->monsterinfo.currentmove = &hound_move_death;
}
/*
===
End Death Stuff
===
*/
void SP_monster_hound_precache(void)
{
sound_pain1 = gi.soundindex ("monsters/hound/hpain1.wav");
sound_pain2 = gi.soundindex ("monsters/hound/hpain2.wav");
sound_die = gi.soundindex ("monsters/hound/hdeth1.wav");
sound_launch = gi.soundindex("monsters/hound/hlaunch.wav");
sound_impact = gi.soundindex("monsters/hound/himpact.wav");
sound_sight = gi.soundindex("monsters/hound/hsight1.wav");
sound_jump = gi.soundindex("monsters/hound/hjump.wav");
sound_bite = gi.soundindex("monsters/hound/hbite1.wav");
sound_bitemiss = gi.soundindex("monsters/hound/hbite2.wav");
}
/*QUAKED monster_hound (1 .5 0) (-16 -16 -24) (16 16 24) Ambush Trigger_Spawn Sight GoodGuy NoGib
model="models/monsters/guard/hound/"
*/
void SP_monster_hound (edict_t *self)
{
if (deathmatch->value)
{
G_FreeEdict (self);
return;
}
SP_monster_hound_precache();
// Lazarus: special purpose skins
if ( self->style )
{
PatchMonsterModel("models/monsters/guard/hound/tris.md2");
self->s.skinnum = self->style * 2;
}
self->s.modelindex = gi.modelindex ("models/monsters/guard/hound/tris.md2");
VectorSet (self->mins, -16, -16, -24);
VectorSet (self->maxs, 16, 16, 24);
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
self->yaw_speed = 30;
if (!self->health)
self->health = 175;
if (!self->gib_health)
self->gib_health = -50;
if (!self->mass)
self->mass = 250;
self->pain = hound_pain;
self->die = hound_die;
if (self->spawnflags & 0x8)
{
self->monsterinfo.aiflags2 = AI2_SCHOOLING;
}
self->monsterinfo.zSchoolSightRadius = 500;
self->monsterinfo.zSchoolMaxSpeed = 4;
self->monsterinfo.zSchoolMinSpeed = 3;
self->monsterinfo.zSpeedStandMax = 1;
self->monsterinfo.zSpeedWalkMax = 3;
self->monsterinfo.zSchoolDecayRate = 0.95;
self->monsterinfo.zSchoolMinimumDistance = 100;
self->monsterinfo.stand = hound_stand;
self->monsterinfo.walk = hound_walk;
self->monsterinfo.run = hound_run;
self->monsterinfo.attack = hound_jump;
self->monsterinfo.melee = hound_attack;
self->monsterinfo.sight = hound_sight;
self->monsterinfo.idle = hound_stand;
self->monsterinfo.checkattack = hound_checkattack;
if (!self->monsterinfo.flies)
self->monsterinfo.flies = 0.60;
// Lazarus
if (self->powerarmor)
{
if (self->powerarmortype == 1)
self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN;
else
self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
self->monsterinfo.power_armor_power = self->powerarmor;
}
self->common_name = "HellHound";
self->class_id = ENTITY_MONSTER_HOUND;
gi.linkentity (self);
self->monsterinfo.currentmove = &hound_stand1;
if (self->health < 0)
{
mmove_t *deathmoves[] = {&hound_move_death,
NULL};
M_SetDeath (self, (mmove_t **)&deathmoves);
}
self->monsterinfo.scale = MODEL_SCALE;
walkmonster_start (self);
}
void monster_think (edict_t *self);
qboolean monster_start (edict_t *self);
void hound_createHound (edict_t *self, float healthPercent)
{
edict_t *hound;
hound = G_Spawn();
//*hound = *self;
hound->classname = "monster_hound";
hound->s.modelindex = gi.modelindex ("models/monsters/guard/hound/tris.md2");
VectorSet (hound->mins, -16, -16, -24);
VectorSet (hound->maxs, 16, 16, 24);
VectorCopy(self->s.origin, hound->s.origin);
VectorCopy(self->s.old_origin, hound->s.old_origin);
VectorCopy(self->s.angles, hound->s.angles);
hound->movetype = MOVETYPE_STEP;
hound->solid = SOLID_BBOX;
hound->takedamage = DAMAGE_YES;
hound->svflags |= SVF_MONSTER;
hound->svflags &= ~SVF_DEADMONSTER;
hound->s.renderfx |= RF_FRAMELERP;
hound->clipmask = MASK_MONSTERSOLID;
hound->deadflag = DEAD_NO;
hound->owner = self;
hound->yaw_speed = 30;
hound->enemy = self->enemy;
hound->ideal_yaw = self->ideal_yaw;
hound->health = 175.0 * healthPercent;
hound->gib_health = -50;
hound->mass = 250;
hound->pain = hound_pain;
hound->die = hound_die;
hound->monsterinfo.stand = hound_stand;
hound->monsterinfo.walk = hound_walk;
hound->monsterinfo.run = hound_run;
hound->monsterinfo.attack = hound_jump;
hound->monsterinfo.melee = hound_attack;
hound->monsterinfo.sight = hound_sight;
hound->monsterinfo.idle = hound_stand;
hound->monsterinfo.checkattack = hound_checkattack;
hound->monsterinfo.currentmove = &hound_move_handlerjump;
hound->monsterinfo.scale = MODEL_SCALE;
hound->think = monster_think;
hound->nextthink = level.time + FRAMETIME;
// Lazarus
hound->powerarmor = self->powerarmor;
if (hound->powerarmor)
{
if (hound->powerarmortype == 1)
hound->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN;
else
hound->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
hound->monsterinfo.power_armor_power = self->powerarmor;
}
hound->monsterinfo.flies = self->monsterinfo.flies;
if (!hound->monsterinfo.flies)
hound->monsterinfo.flies = 0.60;
hound->common_name = "HellHound";
hound->class_id = ENTITY_MONSTER_HOUND;
// monster_start(hound);
gi.linkentity (hound);
// move the fucker now!!!
ai_move (hound, 20);
}