thirtyflightsofloving/game/m_boss32.c
Knightmare66 d16b46e3cf Added re-implementation of func_plat2 and func_door2 from rogue to default Lazarus DLL.
Overhauled child entity movement in default Lazarus DLL.
Added bbox versions of various triggers to default Lazarus DLL.
Added level.maptype field to default Lazarus DLL.
Added entity class IDs to default Lazarus DLL.
Incremented savegame version for default Lazarus DLL.
2020-10-27 02:00:05 -04:00

1021 lines
24 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
Copyright (C) 2000-2002 Mr. Hyde and Mad Dog
This file is part of Lazarus Quake 2 Mod source code.
Lazarus Quake 2 Mod source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Lazarus Quake 2 Mod source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Lazarus Quake 2 Mod source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
/*
==============================================================================
Makron -- Final Boss
==============================================================================
*/
#include "g_local.h"
#include "m_boss32.h"
qboolean visible (edict_t *self, edict_t *other);
void MakronRailgun (edict_t *self);
void MakronSaveloc (edict_t *self);
void MakronHyperblaster (edict_t *self);
void makron_step_left (edict_t *self);
void makron_step_right (edict_t *self);
void makronBFG (edict_t *self);
void makron_dead (edict_t *self);
void SP_monster_makron_put (edict_t *self);
static int sound_pain4;
static int sound_pain5;
static int sound_pain6;
static int sound_death;
static int sound_step_left;
static int sound_step_right;
static int sound_attack_bfg;
static int sound_brainsplorch;
static int sound_prerailgun;
static int sound_popup;
static int sound_taunt1;
static int sound_taunt2;
static int sound_taunt3;
static int sound_hit;
void makron_taunt (edict_t *self)
{
float r;
r=random();
if (r <= 0.3)
gi.sound (self, CHAN_AUTO, sound_taunt1, 1, ATTN_NONE, 0);
else if (r <= 0.6)
gi.sound (self, CHAN_AUTO, sound_taunt2, 1, ATTN_NONE, 0);
else
gi.sound (self, CHAN_AUTO, sound_taunt3, 1, ATTN_NONE, 0);
}
//
// stand
//
mframe_t makron_frames_stand []=
{
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL, // 10
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL, // 20
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL, // 30
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL, // 40
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL, // 50
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL // 60
};
mmove_t makron_move_stand = {FRAME_stand201, FRAME_stand260, makron_frames_stand, NULL};
void makron_stand (edict_t *self)
{
self->monsterinfo.currentmove = &makron_move_stand;
}
mframe_t makron_frames_run [] =
{
ai_run, 3, makron_step_left,
ai_run, 12, NULL,
ai_run, 8, NULL,
ai_run, 8, NULL,
ai_run, 8, makron_step_right,
ai_run, 6, NULL,
ai_run, 12, NULL,
ai_run, 9, NULL,
ai_run, 6, NULL,
ai_run, 12, NULL
};
mmove_t makron_move_run = {FRAME_walk204, FRAME_walk213, makron_frames_run, NULL};
void makron_hit (edict_t *self)
{
gi.sound (self, CHAN_AUTO, sound_hit, 1, ATTN_NONE,0);
}
void makron_popup (edict_t *self)
{
gi.sound (self, CHAN_BODY, sound_popup, 1, ATTN_NONE,0);
}
void makron_step_left (edict_t *self)
{
gi.sound (self, CHAN_BODY, sound_step_left, 1, ATTN_NORM,0);
}
void makron_step_right (edict_t *self)
{
gi.sound (self, CHAN_BODY, sound_step_right, 1, ATTN_NORM,0);
}
void makron_brainsplorch (edict_t *self)
{
gi.sound (self, CHAN_VOICE, sound_brainsplorch, 1, ATTN_NORM,0);
}
void makron_prerailgun (edict_t *self)
{
gi.sound (self, CHAN_WEAPON, sound_prerailgun, 1, ATTN_NORM,0);
}
mframe_t makron_frames_walk [] =
{
ai_walk, 3, makron_step_left,
ai_walk, 12, NULL,
ai_walk, 8, NULL,
ai_walk, 8, NULL,
ai_walk, 8, makron_step_right,
ai_walk, 6, NULL,
ai_walk, 12, NULL,
ai_walk, 9, NULL,
ai_walk, 6, NULL,
ai_walk, 12, NULL
};
mmove_t makron_move_walk = {FRAME_walk204, FRAME_walk213, makron_frames_run, NULL};
void makron_walk (edict_t *self)
{
self->monsterinfo.currentmove = &makron_move_walk;
}
void makron_run (edict_t *self)
{
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
self->monsterinfo.currentmove = &makron_move_stand;
else
self->monsterinfo.currentmove = &makron_move_run;
}
mframe_t makron_frames_pain6 [] =
{
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, // 10
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, makron_popup,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL, // 20
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, makron_taunt,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t makron_move_pain6 = {FRAME_pain601, FRAME_pain627, makron_frames_pain6, makron_run};
mframe_t makron_frames_pain5 [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t makron_move_pain5 = {FRAME_pain501, FRAME_pain504, makron_frames_pain5, makron_run};
mframe_t makron_frames_pain4 [] =
{
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t makron_move_pain4 = {FRAME_pain401, FRAME_pain404, makron_frames_pain4, makron_run};
mframe_t makron_frames_death2 [] =
{
ai_move, -15, NULL,
ai_move, 3, NULL,
ai_move, -12, NULL,
ai_move, 0, makron_step_left,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL, // 10
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 11, NULL,
ai_move, 12, NULL,
ai_move, 11, makron_step_right,
ai_move, 0, NULL,
ai_move, 0, NULL, // 20
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, // 30
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 5, NULL,
ai_move, 7, NULL,
ai_move, 6, makron_step_left,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, -1, NULL,
ai_move, 2, NULL, // 40
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, // 50
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, -6, NULL,
ai_move, -4, NULL,
ai_move, -6, makron_step_right,
ai_move, -4, NULL,
ai_move, -4, makron_step_left,
ai_move, 0, NULL,
ai_move, 0, NULL, // 60
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, -2, NULL,
ai_move, -5, NULL,
ai_move, -3, makron_step_right,
ai_move, -8, NULL,
ai_move, -3, makron_step_left,
ai_move, -7, NULL,
ai_move, -4, NULL,
ai_move, -4, makron_step_right, // 70
ai_move, -6, NULL,
ai_move, -7, NULL,
ai_move, 0, makron_step_left,
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, // 80
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, -2, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 2, NULL,
ai_move, 0, NULL, // 90
ai_move, 27, makron_hit,
ai_move, 26, NULL,
ai_move, 0, makron_brainsplorch,
ai_move, 0, NULL,
ai_move, 0, NULL // 95
};
mmove_t makron_move_death2 = {FRAME_death201, FRAME_death295, makron_frames_death2, makron_dead};
mframe_t makron_frames_death3 [] =
{
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,
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 makron_move_death3 = {FRAME_death301, FRAME_death320, makron_frames_death3, NULL};
mframe_t makron_frames_sight [] =
{
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,
ai_move, 0, NULL
};
mmove_t makron_move_sight= {FRAME_active01, FRAME_active13, makron_frames_sight, makron_run};
void makronBFG (edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t dir;
vec3_t vec;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_MAKRON_BFG], forward, right, start);
VectorCopy (self->enemy->s.origin, vec);
vec[2] += self->enemy->viewheight;
VectorSubtract (vec, start, dir);
VectorNormalize (dir);
gi.sound (self, CHAN_VOICE, sound_attack_bfg, 1, ATTN_NORM, 0);
monster_fire_bfg (self, start, dir, 50, 300, 100, 300, MZ2_MAKRON_BFG);
}
mframe_t makron_frames_attack3 []=
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, makronBFG, // FIXME: BFG Attack here
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t makron_move_attack3 = {FRAME_attak301, FRAME_attak308, makron_frames_attack3, makron_run};
mframe_t makron_frames_attack4[]=
{
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, MakronHyperblaster, // fire
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL
};
mmove_t makron_move_attack4 = {FRAME_attak401, FRAME_attak426, makron_frames_attack4, makron_run};
mframe_t makron_frames_attack5[]=
{
ai_charge, 0, makron_prerailgun,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, MakronSaveloc,
ai_move, 0, MakronRailgun, // Fire railgun
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 makron_move_attack5 = {FRAME_attak501, FRAME_attak516, makron_frames_attack5, makron_run};
void MakronSaveloc (edict_t *self)
{
VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot
self->pos1[2] += self->enemy->viewheight;
};
// FIXME: He's not firing from the proper Z
void MakronRailgun (edict_t *self)
{
vec3_t start;
vec3_t dir;
vec3_t forward, right;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_MAKRON_RAILGUN_1], forward, right, start);
// calc direction to where we targted
VectorSubtract (self->pos1, start, dir);
VectorNormalize (dir);
monster_fire_railgun (self, start, dir, 50, 100, MZ2_MAKRON_RAILGUN_1);
}
// FIXME: This is all wrong. He's not firing at the proper angles.
void MakronHyperblaster (edict_t *self)
{
vec3_t dir;
vec3_t vec;
vec3_t start;
vec3_t forward, right;
int flash_number;
flash_number = MZ2_MAKRON_BLASTER_1 + (self->s.frame - FRAME_attak405);
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
if (self->enemy)
{
VectorCopy (self->enemy->s.origin, vec);
vec[2] += self->enemy->viewheight;
VectorSubtract (vec, start, vec);
vectoangles (vec, vec);
dir[0] = vec[0];
}
else
{
dir[0] = 0;
}
if (self->s.frame <= FRAME_attak413)
dir[1] = self->s.angles[1] - 10 * (self->s.frame - FRAME_attak413);
else
dir[1] = self->s.angles[1] + 10 * (self->s.frame - FRAME_attak421);
dir[2] = 0;
AngleVectors (dir, forward, NULL, NULL);
monster_fire_blaster (self, start, forward, 15, 1000, MZ2_MAKRON_BLASTER_1, EF_BLASTER, BLASTER_ORANGE);
}
void makron_pain (edict_t *self, edict_t *other, float kick, int damage)
{
if (self->health < (self->max_health / 2))
{
self->s.skinnum |= 1;
if (!(self->fogclip & 2)) //custom bloodtype flag check
self->blood_type = 3; //sparks and blood
}
if (level.time < self->pain_debounce_time)
return;
// Lessen the chance of him going into his pain frames
if (damage <=25)
if (random()<0.2)
return;
self->pain_debounce_time = level.time + 3;
if (skill->value == 3)
return; // no pain anims in nightmare
if (damage <= 40)
{
gi.sound (self, CHAN_VOICE, sound_pain4, 1, ATTN_NONE,0);
self->monsterinfo.currentmove = &makron_move_pain4;
}
else if (damage <= 110)
{
gi.sound (self, CHAN_VOICE, sound_pain5, 1, ATTN_NONE,0);
self->monsterinfo.currentmove = &makron_move_pain5;
}
else if (damage <= 150)
{
if (random() <= 0.45)
{
gi.sound (self, CHAN_VOICE, sound_pain6, 1, ATTN_NONE,0);
self->monsterinfo.currentmove = &makron_move_pain6;
}
}
else
{
if (random() <= 0.35)
{
gi.sound (self, CHAN_VOICE, sound_pain6, 1, ATTN_NONE,0);
self->monsterinfo.currentmove = &makron_move_pain6;
}
}
};
void makron_sight(edict_t *self, edict_t *other)
{
self->monsterinfo.currentmove = &makron_move_sight;
};
void makron_attack(edict_t *self)
{
vec3_t vec;
float range;
float r;
r = random();
VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
range = VectorLength (vec);
if (r <= 0.3)
self->monsterinfo.currentmove = &makron_move_attack3;
else if (r <= 0.6)
self->monsterinfo.currentmove = &makron_move_attack4;
else
self->monsterinfo.currentmove = &makron_move_attack5;
}
/*
---
Makron Torso. This needs to be spawned in
---
*/
void makron_torso_think (edict_t *self)
{
if (++self->s.frame < 365)
self->nextthink = level.time + FRAMETIME;
else
{
self->s.frame = 346;
self->nextthink = level.time + FRAMETIME;
}
}
void makron_torso_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
int n;
self->monsterinfo.power_armor_type = POWER_ARMOR_NONE;
// check for gib
if (self->health <= self->gib_health && !(self->spawnflags & SF_MONSTER_NOGIB))
{
for (n= 0; n < 1 /*4*/; n++)
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
for (n= 0; n < 4; n++)
ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC);
G_FreeEdict(self);
return;
}
}
void makron_torso (edict_t *ent)
{
// ent->movetype = MOVETYPE_NONE;
// ent->solid = SOLID_NOT;
// VectorSet (ent->mins, -8, -8, 0);
// VectorSet (ent->maxs, 8, 8, 8);
// Lazarus changes to make makron torso gibbable
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_BBOX;
ent->svflags = SVF_DEADMONSTER;
VectorSet (ent->mins, -32, -32, 0);
VectorSet (ent->maxs, 32, 32, 32);
ent->health = -1;
ent->gib_health = -900;
ent->die = makron_torso_die;
ent->takedamage = DAMAGE_YES;
ent->deadflag = DEAD_DEAD;
ent->s.frame = 346;
ent->s.modelindex = gi.modelindex ("models/monsters/boss3/rider/tris.md2");
ent->think = makron_torso_think;
ent->nextthink = level.time + 2 * FRAMETIME;
ent->s.sound = gi.soundindex ("makron/spine.wav");
gi.linkentity (ent);
}
//
// death
//
void makron_dead (edict_t *self)
{
// Lazarus - this is way too big
// VectorSet (self->mins, -60, -60, 0);
// VectorSet (self->maxs, 60, 60, 72);
VectorSet (self->mins, -48, -48, 0);
VectorSet (self->maxs, 48, 48, 32);
self->movetype = MOVETYPE_TOSS;
self->svflags |= SVF_DEADMONSTER;
self->nextthink = 0;
gi.linkentity (self);
M_FlyCheck (self);
}
void makron_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
edict_t *tempent;
int n;
self->s.skinnum |= 1;
if (!(self->fogclip & 2)) //custom bloodtype flag check
self->blood_type = 3; //sparks and blood
self->monsterinfo.power_armor_type = POWER_ARMOR_NONE;
self->s.sound = 0;
// 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 < 1 /*4*/; n++)
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
for (n= 0; n < 4; n++)
ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC);
ThrowHead (self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC);
self->deadflag = DEAD_DEAD;
return;
}
if (self->deadflag == DEAD_DEAD)
return;
// regular death
gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
self->deadflag = DEAD_DEAD;
self->takedamage = DAMAGE_YES;
tempent = G_Spawn();
VectorCopy (self->s.origin, tempent->s.origin);
VectorCopy (self->s.angles, tempent->s.angles);
tempent->s.origin[1] -= 84;
makron_torso (tempent);
self->monsterinfo.currentmove = &makron_move_death2;
}
qboolean Makron_CheckAttack (edict_t *self)
{
vec3_t spot1, spot2;
vec3_t temp;
float chance;
trace_t tr;
qboolean enemy_infront;
int enemy_range;
float enemy_yaw;
if (self->enemy->health > 0)
{
// see if any entities are in the way of the shot
VectorCopy (self->s.origin, spot1);
spot1[2] += self->viewheight;
VectorCopy (self->enemy->s.origin, spot2);
spot2[2] += self->enemy->viewheight;
tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA);
// do we have a clear shot?
if (tr.ent != self->enemy)
return false;
}
enemy_infront = infront(self, self->enemy);
enemy_range = range(self, self->enemy);
VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
enemy_yaw = vectoyaw(temp);
self->ideal_yaw = enemy_yaw;
// melee attack
if (enemy_range == RANGE_MELEE)
{
if (self->monsterinfo.melee)
self->monsterinfo.attack_state = AS_MELEE;
else
self->monsterinfo.attack_state = AS_MISSILE;
return true;
}
// missile attack
if (!self->monsterinfo.attack)
return false;
if (level.time < self->monsterinfo.attack_finished)
return false;
if (enemy_range == RANGE_FAR)
return false;
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
{
chance = 0.4;
}
else if (enemy_range == RANGE_MELEE)
{
chance = 0.8;
}
else if (enemy_range == RANGE_NEAR)
{
chance = 0.4;
}
else if (enemy_range == RANGE_MID)
{
chance = 0.2;
}
else
{
return false;
}
if (random () < chance)
{
self->monsterinfo.attack_state = AS_MISSILE;
self->monsterinfo.attack_finished = level.time + 2*random();
return true;
}
if (self->flags & FL_FLY)
{
if (random() < 0.3)
self->monsterinfo.attack_state = AS_SLIDING;
else
self->monsterinfo.attack_state = AS_STRAIGHT;
}
return false;
}
//
// monster_makron
//
void MakronPrecache (void)
{
sound_pain4 = gi.soundindex ("makron/pain3.wav");
sound_pain5 = gi.soundindex ("makron/pain2.wav");
sound_pain6 = gi.soundindex ("makron/pain1.wav");
sound_death = gi.soundindex ("makron/death.wav");
sound_step_left = gi.soundindex ("makron/step1.wav");
sound_step_right = gi.soundindex ("makron/step2.wav");
sound_attack_bfg = gi.soundindex ("makron/bfg_fire.wav");
sound_brainsplorch = gi.soundindex ("makron/brain1.wav");
sound_prerailgun = gi.soundindex ("makron/rail_up.wav");
sound_popup = gi.soundindex ("makron/popup.wav");
sound_taunt1 = gi.soundindex ("makron/voice4.wav");
sound_taunt2 = gi.soundindex ("makron/voice3.wav");
sound_taunt3 = gi.soundindex ("makron/voice.wav");
sound_hit = gi.soundindex ("makron/bhit.wav");
gi.modelindex ("models/monsters/boss3/rider/tris.md2");
}
/*QUAKED monster_makron (1 .5 0) (-30 -30 0) (30 30 90) Ambush Trigger_Spawn Sight
*/
// Knightmare- direct spawn function, sets nojump flag
void SP_monster_makron (edict_t *self)
{
self->fogclip |= 1;
SP_monster_makron_put (self);
}
void SP_monster_makron_put (edict_t *self)
{
if (deathmatch->value)
{
G_FreeEdict (self);
return;
}
MakronPrecache ();
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
self->s.modelindex = gi.modelindex ("models/monsters/boss3/rider/tris.md2");
VectorSet (self->mins, -30, -30, 0);
VectorSet (self->maxs, 30, 30, 90);
// Lazarus: mapper-configurable health
if (!self->health)
self->health = 3000;
// Lazarus: get around Killed's prevention of health dropping below -999
// if (!self->gib_health)
// self->gib_health = -2000;
self->gib_health = -900;
if (!self->mass)
self->mass = 500;
self->pain = makron_pain;
self->die = makron_die;
self->monsterinfo.stand = makron_stand;
self->monsterinfo.walk = makron_walk;
self->monsterinfo.run = makron_run;
self->monsterinfo.dodge = NULL;
self->monsterinfo.attack = makron_attack;
self->monsterinfo.melee = NULL;
self->monsterinfo.sight = makron_sight;
self->monsterinfo.checkattack = Makron_CheckAttack;
// Knightmare- added sparks and blood type
if (!self->blood_type)
self->blood_type = 2; // sparks
else
self->fogclip |= 2; // custom bloodtype flag
// Lazarus
if (self->powerarmor) {
self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
self->monsterinfo.power_armor_power = self->powerarmor;
}
self->common_name = "Makron";
self->class_id = ENTITY_MONSTER_MAKRON;
gi.linkentity (self);
// Knightmare- nojump flag
if (self->fogclip & 1)
self->monsterinfo.currentmove = &makron_move_stand;
else
self->monsterinfo.currentmove = &makron_move_sight;
if (self->health < 0)
{
mmove_t *deathmoves[] = {&makron_move_death2,
&makron_move_death3,
NULL};
M_SetDeath(self,(mmove_t **)&deathmoves);
}
self->monsterinfo.scale = MODEL_SCALE;
walkmonster_start(self);
}
/*
=================
MakronSpawn
=================
*/
void MakronSpawn (edict_t *self)
{
vec3_t vec;
edict_t *player;
SP_monster_makron_put (self);
// Knightmare- gross hack for map6 of COS3- don't jump
if (Q_stricmp(level.mapname, "grinsp3f") == 0)
return;
// jump at player
player = level.sight_client;
if (!player)
return;
VectorSubtract (player->s.origin, self->s.origin, vec);
self->s.angles[YAW] = vectoyaw(vec);
VectorNormalize (vec);
VectorMA (vec3_origin, 400, vec, self->velocity);
self->velocity[2] = 200;
self->groundentity = NULL;
}
/*
=================
MakronToss
Jorg is just about dead, so set up to launch Makron out
=================
*/
void MakronToss (edict_t *self)
{
edict_t *ent;
ent = G_Spawn ();
ent->nextthink = level.time + 0.8;
ent->think = MakronSpawn;
ent->target = self->target;
// Knightmare- if Jorg was killed by a trigger_hurt, set nojump flag
if (self->fogclip & 4)
ent->fogclip |= 1;
ent->health = self->health2;
ent->mass = self->mass2;
// ent->common_name = "Makron";
// self->class_id = ENTITY_MONSTER_MAKRON;
#ifdef KMQUAKE2_ENGINE_MOD
// If the Jorg was transparent, make Makron transparent, too.
if (self->s.alpha)
ent->s.alpha = self->s.alpha;
#endif
VectorCopy (self->s.origin, ent->s.origin);
self->s.modelindex2 = 0; // Knightmare- remove Makron model from dead Jorg
}