thirtyflightsofloving/zaero/z_boss.c
Knightmare66 8401c475f6 Improved Tactician Gunner's prox mine aiming in missionpack DLL.
Added powerarmortype field to default Lazaus DLL.  This changes the savegame version.
Added support for blood_type value of 4 to default Lazaurus, missionpack, and Zaero DLLs.
Added support for Lazarus initially dead monsters (health < 0) to missionpack DLL.
Added support for Lazarus monsterjump cvar (berserk, gunner, infantry, mutant, parasite) to missionpack DLL.
Cleaned up surface rendering code from mpolyvertex_t refactoring.
2021-07-31 02:07:06 -04:00

1908 lines
41 KiB
C

#include "g_local.h"
#include "z_anim.h"
#include "z_boss.h"
static int sound_pain1;
static int sound_pain2;
static int sound_pain3;
static int sound_die1;
static int sound_die2;
static int sound_hookimpact;
static int sound_sight;
static int sound_hooklaunch;
static int sound_hookfly;
static int sound_swing;
static int sound_idle1;
static int sound_idle2;
static int sound_walk;
static int sound_raisegun;
static int sound_lowergun;
static int sound_switchattacks;
static int sound_plamsaballfly;
static int sound_plamsaballexplode;
static int sound_plamsaballfire;
static int sound_taunt1;
static int sound_taunt2;
static int sound_taunt3;
void fire_empnuke(edict_t *ent, vec3_t center, int radius);
void SV_AddGravity (edict_t *ent);
void zboss_stand (edict_t *self);
void zboss_run (edict_t *self);
void zboss_run2 (edict_t *self);
void zboss_walk (edict_t *self);
void zboss_walk2(edict_t *self);
void zboss_chooseNextAttack(edict_t *self);
void zboss_reelInGraaple(edict_t *self);
void zboss_posthook(edict_t *self);
void HookDragThink (edict_t *self);
void zboss_attack (edict_t *self);
void zboss_walksound (edict_t *self)
{
gi.sound (self, CHAN_BODY, sound_walk, 1, ATTN_NORM, 0);
}
void zboss_sight (edict_t *self, edict_t *other)
{
gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
}
void possibleBossTaunt(edict_t *self)
{
float r = random();
if(random() < 0.10)
{
if(r < 0.33)
{
gi.sound (self, CHAN_VOICE, sound_taunt1, 1, ATTN_NORM, 0);
}
else if(r < 0.66)
{
gi.sound (self, CHAN_VOICE, sound_taunt2, 1, ATTN_NORM, 0);
}
else
{
gi.sound (self, CHAN_VOICE, sound_taunt3, 1, ATTN_NORM, 0);
}
}
}
//
// STAND
//
mframe_t zboss_frames_stand1 [] =
{
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, // 9
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, // 19
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, // 29
ai_stand, 0, NULL,
ai_stand, 0, NULL,
};
mmove_t zboss_stand1 = {FRAME_stand1start, FRAME_stand1end, zboss_frames_stand1, zboss_stand};
mframe_t zboss_frames_stand2 [] =
{
ai_stand, 0, NULL, // 32
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, // 41
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, // 51
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL,
ai_stand, 0, NULL // 56
};
mmove_t zboss_stand2 = {FRAME_stand2start, FRAME_stand2end, zboss_frames_stand2, zboss_stand};
void zboss_standidle (edict_t *self)
{
if (random() < 0.8)
{
gi.sound (self, CHAN_VOICE, sound_idle1, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &zboss_stand1;
}
else
{
gi.sound (self, CHAN_VOICE, sound_idle2, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &zboss_stand2;
}
}
//
// Post WALK/RUN leading into ilde.
//
mframe_t zboss_frames_postwalk [] =
{
ai_walk, 3, NULL, // 177
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL, // 184
};
mmove_t zboss_move_postwalk = {FRAME_postWalkStart, FRAME_postWalkEnd, zboss_frames_postwalk, zboss_standidle};
void zboss_postWalkRun (edict_t *self)
{
self->monsterinfo.currentmove = &zboss_move_postwalk;
}
//
// WALK
//
mframe_t zboss_frames_prewalk [] =
{
ai_walk, 3, NULL, //154
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 3, NULL, // 160
};
mmove_t zboss_move_prewalk = {FRAME_preWalkStart, FRAME_preWalkEnd, zboss_frames_prewalk, zboss_walk2};
mframe_t zboss_frames_walk [] =
{
ai_walk, 2, NULL, //161
ai_walk, 3, NULL,
ai_walk, 3, NULL,
ai_walk, 4, NULL,
ai_walk, 4, NULL,
ai_walk, 4, NULL,
ai_walk, 4, NULL,
ai_walk, 3, zboss_walksound,
ai_walk, 4, NULL,
ai_walk, 4, NULL, // 170
ai_walk, 4, NULL,
ai_walk, 4, NULL,
ai_walk, 3, NULL,
ai_walk, 2, NULL,
ai_walk, 2, NULL,
ai_walk, 3, zboss_walksound, // 176
};
mmove_t zboss_move_walk = {FRAME_walkStart, FRAME_walkEnd, zboss_frames_walk, zboss_walk2};
void zboss_walk (edict_t *self)
{
self->monsterinfo.currentmove = &zboss_move_prewalk;
}
void zboss_walk2(edict_t *self)
{
self->monsterinfo.currentmove = &zboss_move_walk;
}
//
// RUN
//
mframe_t zboss_frames_prerun [] =
{
ai_run, 3, NULL, //154
ai_run, 3, NULL,
ai_run, 3, NULL,
ai_run, 3, NULL,
ai_run, 3, NULL,
ai_run, 3, NULL,
ai_run, 3, NULL, // 160
};
mmove_t zboss_move_prerun = {FRAME_preWalkStart, FRAME_preWalkEnd, zboss_frames_prerun, zboss_run2};
mframe_t zboss_frames_run [] =
{
ai_run, 2, NULL, //161
ai_run, 3, NULL,
ai_run, 3, NULL,
ai_run, 4, NULL,
ai_run, 4, NULL,
ai_run, 4, NULL,
ai_run, 4, NULL,
ai_run, 3, zboss_walksound,
ai_run, 4, NULL,
ai_run, 4, NULL, // 170
ai_run, 4, NULL,
ai_run, 4, NULL,
ai_run, 3, NULL,
ai_run, 2, NULL,
ai_run, 2, NULL,
ai_run, 3, zboss_walksound, // 176
};
mmove_t zboss_move_run = {FRAME_walkStart, FRAME_walkEnd, zboss_frames_run, NULL};
void zboss_run (edict_t *self)
{
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
zboss_stand(self);
else
self->monsterinfo.currentmove = &zboss_move_prerun;
}
void zboss_run2 (edict_t *self)
{
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
zboss_stand(self);
else
self->monsterinfo.currentmove = &zboss_move_run;
}
//
// main stand function
//
void zboss_stand (edict_t *self)
{
if(self->monsterinfo.currentmove == &zboss_move_prewalk ||
self->monsterinfo.currentmove == &zboss_move_walk ||
self->monsterinfo.currentmove == &zboss_move_prerun ||
self->monsterinfo.currentmove == &zboss_move_run)
{
zboss_postWalkRun(self);
}
else
{
zboss_standidle(self);
}
}
//
// PAIN
//
mframe_t zboss_frames_pain1 [] =
{
ai_move, 0, NULL, // 185
ai_move, 0, NULL,
ai_move, 0, NULL // 187
};
mmove_t zboss_move_pain1 = {FRAME_pain1Start, FRAME_pain1End, zboss_frames_pain1, zboss_run};
mframe_t zboss_frames_pain2 [] =
{
ai_move, 0, NULL, // 188
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL // 192
};
mmove_t zboss_move_pain2 = {FRAME_pain2Start, FRAME_pain2End, zboss_frames_pain2, zboss_run};
mframe_t zboss_frames_pain3 [] =
{
ai_move, 0, NULL, // 193
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, // 202
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, // 212
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL // 217
};
mmove_t zboss_move_pain3 = {FRAME_pain3Start, FRAME_pain3End, zboss_frames_pain3, zboss_run};
void zboss_pain (edict_t *self, edict_t *other, float kick, int damage)
{
float r;
float hbreak = (self->max_health / 3.0);
// set the skin
if (self->health < hbreak)
{
self->s.skinnum = 2;
}
else if (self->health < hbreak * 2)
{
self->s.skinnum = 1;
}
else
{
self->s.skinnum = 0;
}
r = random();
if(r < 0.125)
{
gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
}
else if(r < 0.25)
{
gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
}
else if(r < 0.375)
{
gi.sound (self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0);
}
else if(r < 0.5)
{
gi.sound (self, CHAN_VOICE, sound_taunt1, 1, ATTN_NORM, 0);
}
else if(r < 0.625)
{
gi.sound (self, CHAN_VOICE, sound_taunt2, 1, ATTN_NORM, 0);
}
else if(r < 0.75)
{
gi.sound (self, CHAN_VOICE, sound_taunt3, 1, ATTN_NORM, 0);
}
if(self->bossFireCount && self->bossFireTimeout < level.time)
{
self->bossFireCount = 0;
}
if(self->bossFireCount > 40 && self->bossFireTimeout > level.time)
{
// that's it, we are pissed...
if(self->zDistance < level.time)
{
fire_empnuke(self, self->s.origin, 1024);
self->zDistance = level.time + 30 + (random() * 5);
}
zboss_attack(self);
self->bossFireCount = 0;
self->bossFireTimeout = 0;
return;
}
self->bossFireCount++;
self->bossFireTimeout = level.time + 1;
if(self->health < (self->max_health / 4) && self->zDistance < level.time)
{
fire_empnuke(self, self->s.origin, 1024);
self->zDistance = level.time + 30 + (random() * 5);
}
if (level.time < self->pain_debounce_time)
return;
self->pain_debounce_time = level.time + 5;
if (skill->value == 3)
return; // no pain anims in nightmare
if(self->laser)
return; // while hook is out.
r = random();
if(damage > 150 && r < 0.33)
{
self->monsterinfo.currentmove = &zboss_move_pain3;
}
else if(damage > 80 && r < 0.66)
{
self->monsterinfo.currentmove = &zboss_move_pain2;
}
else if(r < 0.60)
{
self->monsterinfo.currentmove = &zboss_move_pain1;
}
}
//
// MELEE
//
void zboss_swing (edict_t *self)
{
static vec3_t aim = {MELEE_DISTANCE, 0, -24};
fire_hit (self, aim, (15 + (rand() % 6)), 800);
}
mframe_t zboss_frames_attack2c [] =
{
ai_charge, 0, NULL, // 110
ai_charge, 0, zboss_swing,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, possibleBossTaunt,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, zboss_swing,
ai_charge, 0, NULL // 118
};
mmove_t zboss_move_attack2c = {FRAME_attack2cStart, FRAME_attack2cEnd, zboss_frames_attack2c, zboss_posthook};
void zboss_melee2 (edict_t *self)
{
self->monsterinfo.currentmove = &zboss_move_attack2c;
gi.sound (self, CHAN_WEAPON, sound_swing, 1, ATTN_NORM, 0);
}
mframe_t zboss_frames_premelee [] =
{
ai_charge, 0, NULL, // 57
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, possibleBossTaunt,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 66
};
mmove_t zboss_move_premelee = {FRAME_preHookStart, FRAME_preHookEnd, zboss_frames_premelee, zboss_melee2 };
// todo, pickup player, and throw...
void zboss_melee (edict_t *self)
{
gi.sound (self, CHAN_BODY, sound_raisegun, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &zboss_move_premelee;
}
//
// ATTACK
//
// Rocket / Hook
// Rocket attack
mframe_t zboss_frames_attack1b [] =
{
ai_charge, 0, NULL, // 92
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL // 98
};
mmove_t zboss_move_attack1b = {FRAME_attack1bStart, FRAME_attack1bEnd, zboss_frames_attack1b, zboss_chooseNextAttack };
void zboss_reloadRockets(edict_t *self)
{
self->monsterinfo.aiflags &= ~AI_ONESHOTTARGET;
self->monsterinfo.currentmove = &zboss_move_attack1b;
}
static vec3_t rocketoffset[] =
{
{-5, -50, 33},
{-5, -39, 27},
{-5, -39, 39},
{-5, -44, 27},
{-5, -44, 39},
{-5, -48, 29},
{-5, -48, 29},
};
void FireFlare(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t dir;
vec3_t vec;
int offset = (self->s.frame - 71) / 3;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, rocketoffset[offset], forward, right, start);
if (self->monsterinfo.aiflags & AI_ONESHOTTARGET)
{
VectorCopy( self->monsterinfo.shottarget, vec );
}
else
{
VectorCopy (self->enemy->s.origin, vec);
vec[2] += self->enemy->viewheight;
}
VectorSubtract (vec, start, dir);
VectorNormalize (dir);
if(!(self->monsterinfo.aiflags & AI_ONESHOTTARGET))
{
ANIM_AIM(self, dir);
}
fire_flare (self, start, dir, 10, 1000, 10, 10);
// play shooting sound
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/flare/shoot.wav"), 1, ATTN_NORM, 0);
}
void FireRocket(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t dir;
vec3_t vec;
int offset = (self->s.frame - 71) / 3;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, rocketoffset[offset], forward, right, start);
if(self->monsterinfo.aiflags & AI_ONESHOTTARGET)
{
VectorCopy( self->monsterinfo.shottarget, vec );
}
else
{
VectorCopy (self->enemy->s.origin, vec);
vec[2] += self->enemy->viewheight;
}
vec[0] += (100 - (200 * random()));
vec[1] += (100 - (200 * random()));
vec[2] += (40 - (80 * random()));
VectorSubtract (vec, start, dir);
VectorNormalize (dir);
// ANIM_AIM(self, dir);
fire_rocket (self, start, dir, 70, 500, 70+20, 70);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_BOSS2_ROCKET_1);
gi.multicast (start, MULTICAST_PVS);
}
mframe_t zboss_frames_attack1a [] =
{
ai_charge, 0, FireFlare, // 71
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, FireRocket,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, FireRocket,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, FireRocket,
ai_charge, 0, possibleBossTaunt,
ai_charge, 0, NULL,
ai_charge, 0, FireFlare,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, FireRocket,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, FireRocket,
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 91
};
mmove_t zboss_move_attack1a = {FRAME_attack1aStart, FRAME_attack1aEnd, zboss_frames_attack1a, zboss_reloadRockets };
// hook
void zboss_reelInGraaple2(edict_t *self)
{
vec3_t vec, dir;
float length;
edict_t *enemy = self->laser->enemy;
vec3_t hookoffset = {-5, -24, 34};
vec3_t forward, right;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource(self->s.origin, hookoffset, forward, right, vec);
VectorSubtract (vec, self->laser->s.origin, dir);
length = VectorLength (dir);
if(length <= 80 || (self->laser->think == HookDragThink && self->laser->powerarmor_time < level.time))
{
G_FreeEdict(self->laser);
self->laser = NULL;
self->s.modelindex3 = gi.modelindex ("models/monsters/bossz/grapple/tris.md2");
if(enemy)
{
VectorClear(enemy->velocity);
zboss_melee2(self);
}
else
{
zboss_chooseNextAttack(self);
}
}
else
{
zboss_reelInGraaple(self);
}
}
mframe_t zboss_frames_attack2b [] =
{
ai_charge, 0, NULL, // 107
ai_charge, 0, NULL,
ai_charge, 0, NULL // 109
};
mmove_t zboss_move_attack2b = {FRAME_attack2bStart, FRAME_attack2bEnd, zboss_frames_attack2b, zboss_reelInGraaple2 };
void HookDragThink (edict_t *self)
{
vec3_t dir, vec;
float length, speed;
vec3_t hookoffset = {-5, -24, 34};
vec3_t forward, right;
vec3_t offset = {0, 0, 0};
if(self->enemy && self->enemy->health > 0)
{
VectorCopy (self->enemy->s.origin, self->s.origin);
}
VectorSubtract (self->owner->s.origin, self->s.origin, dir);
length = VectorLength (dir);
AngleVectors (self->owner->s.angles, forward, right, NULL);
G_ProjectSource(self->owner->s.origin, hookoffset, forward, right, vec);
VectorSubtract (vec, self->s.origin, dir);
speed = VectorLength (dir);
VectorNormalize (dir);
/*
if(speed > 1000)
{
speed = 1000;
}
else if(speed < 500)
{
speed = (speed * 2) + 100;
}
*/
speed = 1000;
VectorScale (dir, speed, self->velocity);
if(self->enemy && self->enemy->health > 0)
{
VectorCopy (self->velocity, self->enemy->velocity);
self->enemy->velocity[2] *= 1.3;
}
gi.WriteByte (svc_temp_entity);
#if 0 //def USE_GRAPPLE_CABLE
gi.WriteByte (TE_GRAPPLE_CABLE);
gi.WriteShort (self - g_edicts);
gi.WritePosition (self->s.origin);
gi.WritePosition (vec);
gi.WritePosition (offset);
#else
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
gi.WriteShort (self - g_edicts);
gi.WritePosition (self->s.origin);
gi.WritePosition (vec);
#endif
gi.multicast (self->s.origin, MULTICAST_PVS);
self->nextthink = level.time + FRAMETIME;
}
void HookTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == ent->owner)
return;
if (other->takedamage)
{
gi.sound (ent, CHAN_WEAPON, sound_hookimpact, 1, ATTN_NORM, 0);
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, 10, 0, 0, MOD_ROCKET);
}
if(other->client && other->health > 0)
{ // alive... Let's drag the bastard back...
ent->enemy = other;
}
ent->powerarmor_time = level.time + 15;
VectorClear(ent->velocity);
ent->nextthink = level.time + FRAMETIME;
ent->think = HookDragThink;
ent->s.frame = 283;
}
void HookThink(edict_t *self)
{
vec3_t vec;
vec3_t hookoffset = {-3, -24, 34};
vec3_t forward, right;
vec3_t offset = {0, 0, 0};
if(self->powerarmor_time < level.time)
{
self->powerarmor_time = level.time + 15;
VectorClear(self->velocity);
self->enemy = NULL;
self->think = HookDragThink;
self->s.frame = 283;
}
AngleVectors (self->owner->s.angles, forward, right, NULL);
G_ProjectSource(self->owner->s.origin, hookoffset, forward, right, vec);
gi.WriteByte (svc_temp_entity);
#if 0 //def USE_GRAPPLE_CABLE
gi.WriteByte (TE_GRAPPLE_CABLE);
gi.WriteShort (self - g_edicts);
gi.WritePosition (self->s.origin);
gi.WritePosition (vec);
gi.WritePosition (offset);
#else
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
gi.WriteShort (self - g_edicts);
gi.WritePosition (self->s.origin);
gi.WritePosition (vec);
#endif
gi.multicast (self->s.origin, MULTICAST_PVS);
self->nextthink = level.time + FRAMETIME;
}
void FireHook(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t dir;
vec3_t vec;
vec3_t hookoffset = {-1, -24, 34};
edict_t *hook;
float speed;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, hookoffset, forward, right, start);
VectorCopy (self->enemy->s.origin, vec);
vec[2] += self->enemy->viewheight;
VectorSubtract (vec, start, dir);
VectorNormalize (dir);
ANIM_AIM(self, dir);
self->s.modelindex3 = 0;
speed = 1000;
gi.sound (self, CHAN_WEAPON, sound_hooklaunch, 1, ATTN_NORM, 0);
self->laser = hook = G_Spawn();
VectorCopy (start, hook->s.origin);
VectorCopy (dir, hook->movedir);
vectoangles (dir, hook->s.angles);
VectorScale (dir, speed, hook->velocity);
hook->movetype = MOVETYPE_FLYMISSILE;
hook->clipmask = MASK_SHOT;
hook->solid = SOLID_BBOX;
VectorClear (hook->mins);
VectorClear (hook->maxs);
hook->s.modelindex = gi.modelindex ("models/monsters/bossz/grapple/tris.md2");
hook->s.frame = 282;
hook->owner = self;
hook->touch = HookTouch;
hook->powerarmor_time = level.time + 8000 / speed;
hook->nextthink = level.time + FRAMETIME;
hook->think = HookThink;
hook->s.sound = sound_hookfly; // replace...
hook->classname = "bosshook";
gi.linkentity (hook);
}
void zboss_reelInGraaple(edict_t *self)
{
self->monsterinfo.currentmove = &zboss_move_attack2b;
}
mframe_t zboss_frames_attack2a [] =
{
ai_charge, 0, NULL, // 99
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, possibleBossTaunt,
ai_charge, 0, NULL,
ai_charge, 0, FireHook, // 104
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 106
};
mmove_t zboss_move_attack2a = {FRAME_attack2aStart, FRAME_attack2aEnd, zboss_frames_attack2a, zboss_reelInGraaple };
mframe_t zboss_frames_posthook [] =
{
ai_charge, 0, NULL, // 136
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 141
};
mmove_t zboss_move_posthook = {FRAME_postHookStart, FRAME_postHookEnd, zboss_frames_posthook, zboss_run };
void zboss_posthook(edict_t *self)
{
self->monsterinfo.currentmove = &zboss_move_posthook;
}
void zboss_chooseHookRocket(edict_t *self)
{
if(random() < 0.2 && !(self->monsterinfo.aiflags & AI_ONESHOTTARGET))
{
self->monsterinfo.currentmove = &zboss_move_attack2a;
}
else
{
self->monsterinfo.currentmove = &zboss_move_attack1a;
}
}
mframe_t zboss_frames_prehook [] =
{
ai_charge, 0, NULL, // 57
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, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 66
};
mmove_t zboss_move_prehook = {FRAME_preHookStart, FRAME_preHookEnd, zboss_frames_prehook, zboss_chooseHookRocket };
// Plasma Cannon
void PlasmaballBlastAnim(edict_t *ent)
{
ent->s.frame++;
ent->s.skinnum++;
if(ent->s.frame > 1)
{
G_FreeEdict(ent);
return;
}
else
{
ent->nextthink = level.time + FRAMETIME;
}
}
void Plasmaball_Explode (edict_t *ent)
{
//FIXME: if we are onground then raise our Z just a bit since we are a point?
if (ent->enemy)
{
float points;
vec3_t v;
vec3_t dir;
VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
VectorMA (ent->enemy->s.origin, 0.5, v, v);
VectorSubtract (ent->s.origin, v, v);
points = ent->dmg - 0.5 * VectorLength (v);
VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, MOD_UNKNOWN);
}
T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, MOD_UNKNOWN);
VectorMA (ent->s.origin, -0.02, ent->velocity, ent->s.origin);
VectorClear(ent->velocity);
ent->movetype = MOVETYPE_NONE;
ent->s.modelindex = gi.modelindex("models/objects/b_explode/tris.md2");
ent->s.effects &= ~EF_BFG & ~EF_ANIM_ALLFAST;
ent->s.frame = 0;
ent->s.skinnum = 6;
// ent->s.renderfx = RF_TRANSLUCENT | RF_FULLBRIGHT;
// ent->s.renderfx = RF_TRANSLUCENT;
gi.sound (ent, CHAN_AUTO, sound_plamsaballexplode, 1, ATTN_NORM, 0);
ent->think = PlasmaballBlastAnim;
ent->nextthink = level.time + FRAMETIME;
}
/*static*/ void Plasmaball_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == ent->owner)
return;
if (surf && (surf->flags & SURF_SKY))
{
G_FreeEdict (ent);
return;
}
ent->enemy = other;
Plasmaball_Explode (ent);
}
void fire_plasmaCannon (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, float distance)
{
edict_t *plasmaball;
vec3_t dir;
vec3_t forward, right, up;
vectoangles (aimdir, dir);
AngleVectors (dir, forward, right, up);
plasmaball = G_Spawn();
VectorCopy (start, plasmaball->s.origin);
VectorScale (aimdir, speed, plasmaball->velocity);
VectorMA (plasmaball->velocity, (distance - 500) + crandom() * 10.0, up, plasmaball->velocity);
VectorMA (plasmaball->velocity, crandom() * 10.0, right, plasmaball->velocity);
VectorSet (plasmaball->avelocity, 300, 300, 300);
plasmaball->movetype = MOVETYPE_BOUNCE;
plasmaball->clipmask = MASK_SHOT;
plasmaball->solid = SOLID_BBOX;
VectorClear (plasmaball->mins);
VectorClear (plasmaball->maxs);
plasmaball->s.modelindex = gi.modelindex ("sprites/plasma1.sp2");
plasmaball->s.effects = EF_BFG | EF_ANIM_ALLFAST;
plasmaball->owner = self;
plasmaball->touch = Plasmaball_Touch;
plasmaball->nextthink = level.time + timer;
plasmaball->think = Plasmaball_Explode;
plasmaball->dmg = damage;
plasmaball->dmg_radius = damage_radius;
plasmaball->classname = "plasmaball";
plasmaball->s.sound = sound_plamsaballfly;
gi.sound (self, CHAN_AUTO, sound_plamsaballfire, 1, ATTN_NORM, 0);
gi.linkentity (plasmaball);
}
static vec3_t cannonoffset[] =
{
{-19, -44, 30},
{-14, -33, 32},
{-4 , -45, 32},
{-2 , -34, 32},
{ 7, -49, 32},
{ 6, -36, 34},
{ 6, -36, 34},
};
void FireCannon(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t dir;
vec3_t vec;
float distance;
int offset = (self->s.frame - 119) / 2;
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, cannonoffset[offset], forward, right, start);
if(self->monsterinfo.aiflags & AI_ONESHOTTARGET)
{
VectorCopy( self->monsterinfo.shottarget, vec );
}
else
{
VectorCopy (self->enemy->s.origin, vec);
vec[2] += self->enemy->viewheight;
}
if(self->timeout)
{
if(self->seq)
{
VectorNegate(right, right);
}
VectorMA (vec, self->timeout, right, vec);
}
self->timeout -= 50;
VectorSubtract (vec, start, dir);
VectorNormalize (dir);
VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
distance = VectorLength (vec);
if(distance < 700)
{
distance = 700;
}
// ANIM_AIM(self, dir);
if(skill->value < 2)
{
fire_plasmaCannon (self, start, dir, 90, 700, 2.5, 90+40, distance);
}
else if(skill->value < 3)
{
fire_plasmaCannon (self, start, dir, 90, (int)(distance * 1.2), 2.5, 90+40, distance);
}
else
{
fire_plasmaCannon (self, start, dir, 90, (int)(distance * 1.6), 2.5, 90+40, distance);
}
}
mframe_t zboss_frames_attack3 [] =
{
ai_charge, 0, FireCannon, // 119
ai_charge, 0, NULL,
ai_charge, 0, FireCannon, // 121
ai_charge, 0, NULL,
ai_charge, 0, FireCannon, // 123
ai_charge, 0, NULL,
ai_charge, 0, FireCannon, // 125
ai_charge, 0, possibleBossTaunt,
ai_charge, 0, FireCannon, // 127
ai_charge, 0, NULL,
ai_charge, 0, FireCannon, // 129
ai_charge, 0, NULL,
ai_charge, 0, FireCannon, // 131
ai_charge, 0, NULL, // 132
};
mmove_t zboss_move_attack3 = {FRAME_attack3Start, FRAME_attack3End, zboss_frames_attack3, zboss_chooseNextAttack };
void zboss_fireCannons(edict_t *self)
{
self->monsterinfo.currentmove = &zboss_move_attack3;
// self->seq = (random() > 0.5);
self->seq = 0;
self->timeout = 150;
}
mframe_t zboss_frames_precannon [] =
{
ai_charge, 0, NULL, // 67
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 70
};
mmove_t zboss_move_precannon = {FRAME_preCannonStart, FRAME_preCannonEnd, zboss_frames_precannon, zboss_fireCannons };
mframe_t zboss_frames_postcannon [] =
{
ai_charge, 0, NULL, // 133
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 135
};
mmove_t zboss_move_postcannon = {FRAME_postCannonStart, FRAME_postCannonEnd, zboss_frames_postcannon, zboss_run };
void zboss_postcannon(edict_t *self)
{
self->monsterinfo.currentmove = &zboss_move_postcannon;
}
// switching in mid attack...
mframe_t zboss_frames_h2c [] =
{
ai_charge, 0, NULL, // 142
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 147
};
mmove_t zboss_move_h2c = {FRAME_attackH2CStart, FRAME_attackH2CEnd, zboss_frames_h2c, zboss_fireCannons };
mframe_t zboss_frames_c2h [] =
{
ai_charge, 0, NULL, // 148
ai_charge, 0, NULL,
ai_charge, 0, NULL,
ai_charge, 0, possibleBossTaunt,
ai_charge, 0, NULL,
ai_charge, 0, NULL, // 153
};
mmove_t zboss_move_c2h = {FRAME_attackC2HStart, FRAME_attackC2HEnd, zboss_frames_c2h, zboss_chooseHookRocket };
void zboss_chooseNextAttack(edict_t *self)
{
if (self->enemy == NULL)
return;
self->monsterinfo.aiflags &= ~AI_ONESHOTTARGET;
if(random() < 0.5 && self->enemy)
{
if(random() < 0.4)
{
if(self->monsterinfo.currentmove == &zboss_move_attack3)
{
gi.sound (self, CHAN_BODY, sound_switchattacks, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &zboss_move_c2h;
}
else
{
zboss_chooseHookRocket(self);
}
}
else
{
if(self->monsterinfo.currentmove == &zboss_move_attack3)
{
zboss_fireCannons(self);
}
else
{
gi.sound (self, CHAN_BODY, sound_switchattacks, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &zboss_move_h2c;
}
}
}
else
{
gi.sound (self, CHAN_BODY, sound_lowergun, 1, ATTN_NORM, 0);
if(self->monsterinfo.currentmove == &zboss_move_attack3)
{
zboss_postcannon(self);
}
else
{
zboss_posthook(self);
}
}
}
void zboss_attack (edict_t *self)
{
if (self->enemy == NULL)
return;
gi.sound (self, CHAN_BODY, sound_raisegun, 1, ATTN_NORM, 0);
if(random() < 0.4)
{
self->monsterinfo.currentmove = &zboss_move_prehook;
}
else
{
self->monsterinfo.currentmove = &zboss_move_precannon;
}
}
/*
===
Death Stuff Starts
===
*/
void zboss_dead (edict_t *self)
{
VectorSet (self->mins, -32, -74, -30);
VectorSet (self->maxs, 32, 40, 12);
self->movetype = MOVETYPE_TOSS;
self->svflags |= SVF_DEADMONSTER;
self->nextthink = 0;
gi.linkentity (self);
}
mframe_t zboss_frames_death1 [] =
{
ai_move, 0, NULL, // 218
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, // 227
ai_move, 0, NULL, // 228
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, // 236
};
mmove_t zboss_move_death1 = {FRAME_die1Start, FRAME_die1End, zboss_frames_death1, zboss_dead};
void FireDeadRocket1(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t rocketoffset = {-26, -26, 25};
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
fire_rocket (self, start, forward, 70, 500, 70+20, 70);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_BOSS2_ROCKET_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadRocket2(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t rocketoffset = {-16, -21, 20};
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
forward[1] += 10;
fire_rocket (self, start, forward, 70, 500, 70+20, 70);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_BOSS2_ROCKET_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadRocket3(edict_t *self)
{
vec3_t forward, right, up;
vec3_t start;
vec3_t rocketoffset = {-17, -20, 30};
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
fire_rocket (self, start, up, 70, 500, 70+20, 70);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_BOSS2_ROCKET_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadRocket4(edict_t *self)
{
vec3_t forward, right, up;
vec3_t start;
vec3_t rocketoffset = {-8, -16, 17};
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
fire_rocket (self, start, up, 70, 500, 70+20, 70);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_BOSS2_ROCKET_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadRocket5(edict_t *self)
{
vec3_t forward, right, up;
vec3_t start;
vec3_t rocketoffset = {-10, -16, 30};
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
VectorNegate(forward, forward);
fire_rocket (self, start, forward, 70, 500, 70+20, 70);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_BOSS2_ROCKET_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadRocket6(edict_t *self)
{
vec3_t forward, right, up;
vec3_t start;
vec3_t rocketoffset = {0, -18, 25};
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
VectorNegate(forward, forward);
forward[1] -= 10;
fire_rocket (self, start, forward, 70, 500, 70+20, 70);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_BOSS2_ROCKET_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadRocket7(edict_t *self)
{
vec3_t forward, right, up;
vec3_t start;
vec3_t rocketoffset = {17, -27, 30};
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, rocketoffset, forward, right, start);
VectorNegate(forward, forward);
forward[1] -= 10;
fire_rocket (self, start, forward, 70, 500, 70+20, 70);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_BOSS2_ROCKET_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadCannon1(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t cannonoffset = {9, -46, 33};
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, cannonoffset, forward, right, start);
fire_plasmaCannon (self, start, forward, 90, 700, 2.5, 90+40, 700);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_GUNNER_GRENADE_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadCannon2(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t cannonoffset = {3, -31, 37};
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, cannonoffset, forward, right, start);
fire_plasmaCannon (self, start, forward, 90, 700, 2.5, 90+40, 700);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_GUNNER_GRENADE_1);
gi.multicast (start, MULTICAST_PVS);
}
void FireDeadCannon3(edict_t *self)
{
vec3_t forward, right;
vec3_t start;
vec3_t cannonoffset = {-21, -19, 24};
AngleVectors (self->s.angles, forward, right, NULL);
G_ProjectSource (self->s.origin, cannonoffset, forward, right, start);
fire_plasmaCannon (self, start, forward, 90, 700, 2.5, 90+40, 700);
gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (MZ2_GUNNER_GRENADE_1);
gi.multicast (start, MULTICAST_PVS);
}
void DeadHookTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == ent->owner)
return;
if (other->takedamage)
{
gi.sound (ent, CHAN_WEAPON, sound_hookimpact, 1, ATTN_NORM, 0);
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, 10, 0, 0, MOD_ROCKET);
}
G_FreeEdict(ent);
}
void FireDeadGrapple(edict_t *self)
{
vec3_t forward, right, up;
vec3_t start;
vec3_t hookoffset = {-35, 8, 28};
edict_t *hook;
float speed;
if(self->s.modelindex3 == 0) // hook already out...
return;
AngleVectors (self->s.angles, forward, right, up);
G_ProjectSource (self->s.origin, hookoffset, forward, right, start);
self->s.modelindex3 = 0;
speed = 500;
gi.sound (self, CHAN_WEAPON, sound_hooklaunch, 1, ATTN_NORM, 0);
hook = G_Spawn();
VectorCopy (start, hook->s.origin);
VectorCopy (up, hook->movedir);
vectoangles (up, hook->s.angles);
VectorScale (up, speed, hook->velocity);
hook->movetype = MOVETYPE_FLYMISSILE;
hook->clipmask = MASK_SHOT;
hook->solid = SOLID_BBOX;
VectorClear (hook->mins);
VectorClear (hook->maxs);
hook->s.modelindex = gi.modelindex ("models/monsters/bossz/grapple/tris.md2");
hook->s.frame = 282;
hook->owner = self;
hook->touch = DeadHookTouch;
hook->nextthink = level.time + 8000 / speed;
hook->think = G_FreeEdict;
hook->s.sound = sound_hookfly; // replace...
hook->classname = "bosshook";
gi.linkentity (hook);
}
mframe_t zboss_frames_death2 [] =
{
ai_move, 0, NULL, // 237
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, // 246
ai_move, 0, NULL, // 247
ai_move, 0, NULL,
ai_move, 0, FireDeadRocket1, // 249
ai_move, 0, FireDeadRocket2, // 250
ai_move, 0, FireDeadRocket3, // 251
ai_move, 0, FireDeadRocket4, // 252
ai_move, 0, FireDeadRocket5, // 253
ai_move, 0, FireDeadRocket6, // 254
ai_move, 0, FireDeadRocket7, // 255
ai_move, 0, NULL, // 256
ai_move, 0, FireDeadCannon1, // 257
ai_move, 0, FireDeadCannon2, // 258
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, FireDeadCannon3, // 264
ai_move, 0, NULL,
ai_move, 0, NULL, // 266
ai_move, 0, NULL, // 267
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, // 276
ai_move, 0, NULL, // 277
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, NULL,
ai_move, 0, FireDeadGrapple, // 281
};
mmove_t zboss_move_death2 = {FRAME_die2Start, FRAME_die2End, zboss_frames_death2, zboss_dead};
void zboss_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
int n;
self->s.skinnum = 2;
if (self->laser)
{
G_FreeEdict(self->laser);
self->laser = NULL;
}
// check for gib
if (self->health <= self->gib_health)
{
self->s.modelindex2 = 0;
self->s.modelindex3 = 0;
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", damage, GIB_ORGANIC);
for (n= 0; n < 4; n++)
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
for (n= 0; n < 16; n++)
ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC);
for (n= 0; n < 16; n++)
ThrowGib (self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC);
ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
self->deadflag = DEAD_DEAD;
return;
}
if (self->deadflag == DEAD_DEAD)
return;
// regular death
self->deadflag = DEAD_DEAD;
self->takedamage = DAMAGE_YES;
self->s.skinnum = 2; // Knightmare- make sure pain skin is set if we got one-shotted
// todo
if (random() < 0.5)
{
gi.sound (self, CHAN_VOICE, sound_die1, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &zboss_move_death1;
}
else
{
gi.sound (self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
self->monsterinfo.currentmove = &zboss_move_death2;
}
}
/*
===
End Death Stuff
===
*/
void SP_monster_zboss_precache(void)
{
sound_pain1 = gi.soundindex ("monsters/bossz/bpain1.wav");
sound_pain2 = gi.soundindex ("monsters/bossz/bpain2.wav");
sound_pain3 = gi.soundindex ("monsters/bossz/bpain3.wav");
sound_die1 = gi.soundindex ("monsters/bossz/bdeth1.wav");
sound_die2 = gi.soundindex ("monsters/bossz/bdeth2.wav");
sound_hooklaunch = gi.soundindex("monsters/bossz/bhlaunch.wav");
sound_hookimpact = gi.soundindex("monsters/bossz/bhimpact.wav");
sound_hookfly = gi.soundindex("monsters/bossz/bhfly.wav");
sound_sight = gi.soundindex("monsters/bossz/bsight1.wav");
sound_swing = gi.soundindex("monsters/bossz/bswing.wav");
sound_idle1 = gi.soundindex("monsters/bossz/bidle1.wav");
sound_idle2 = gi.soundindex("monsters/bossz/bidle2.wav");
sound_walk = gi.soundindex("monsters/bossz/bwalk.wav");
sound_raisegun = gi.soundindex("monsters/bossz/braisegun.wav");
sound_lowergun = gi.soundindex("monsters/bossz/blowergun.wav");
sound_switchattacks = gi.soundindex("monsters/bossz/bswitch.wav");
sound_plamsaballfly = gi.soundindex("monsters/bossz/bpbfly.wav");
sound_plamsaballexplode = gi.soundindex("monsters/bossz/bpbexplode.wav");
sound_plamsaballfire = gi.soundindex("monsters/bossz/bpbfire.wav");
sound_taunt1 = gi.soundindex("monsters/bossz/btaunt1.wav");
sound_taunt2 = gi.soundindex("monsters/bossz/btaunt2.wav");
sound_taunt3 = gi.soundindex("monsters/bossz/btaunt3.wav");
}
/*QUAKED monster_zboss (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
*/
void SP_monster_zboss (edict_t *self)
{
if (deathmatch->value)
{
G_FreeEdict (self);
return;
}
SP_monster_zboss_precache();
// precache some models and sounds
gi.modelindex("sprites/plasma1.sp2");
gi.modelindex("models/objects/b_explode/tris.md2");
gi.soundindex("items/empnuke/emp_trg.wav");
self->s.modelindex = gi.modelindex ("models/monsters/bossz/mech/tris.md2");
self->s.modelindex2 = gi.modelindex ("models/monsters/bossz/pilot/tris.md2");
self->s.modelindex3 = gi.modelindex ("models/monsters/bossz/grapple/tris.md2");
VectorSet (self->mins, -32, -74, -30);
VectorSet (self->maxs, 32, 50, 74);
self->movetype = MOVETYPE_STEP;
self->solid = SOLID_BBOX;
self->monsterinfo.aiflags = AI_MONREDUCEDDAMAGE;
self->monsterinfo.reducedDamageAmount = 0.25;
if(skill->value == 0)
{
self->health = 3000;
}
else if(skill->value == 1)
{
self->health = 4500;
}
else if(skill->value == 2)
{
self->health = 6000;
}
else
{
self->health = 8000;
}
self->gib_health = -700;
self->mass = 1000;
self->pain = zboss_pain;
self->die = zboss_die;
self->monsterinfo.stand = zboss_stand;
self->monsterinfo.walk = zboss_walk;
self->monsterinfo.run = zboss_run;
self->monsterinfo.attack = zboss_attack;
self->monsterinfo.melee = zboss_melee;
self->monsterinfo.sight = zboss_sight;
self->monsterinfo.idle = possibleBossTaunt;
// Knightmare- added sparks and blood type
if (!self->blood_type)
self->blood_type = 2; //sparks
gi.linkentity (self);
self->monsterinfo.currentmove = &zboss_stand1;
self->monsterinfo.scale = MODEL_SCALE;
walkmonster_start (self);
}
/*QUAKED target_zboss_target
*/
void trigger_zboss (edict_t *self, edict_t *other, edict_t *activator)
{
edict_t *boss = NULL;
while ((boss = G_Find (boss, FOFS(targetname), self->target)) != NULL)
{
if(boss->health > 0)
{
VectorCopy( self->s.origin, boss->monsterinfo.shottarget );
boss->monsterinfo.aiflags |= AI_ONESHOTTARGET;
boss->monsterinfo.attack(boss);
}
}
}
void SP_target_zboss_target(edict_t *self)
{
if(!self->target)
{
gi.dprintf("target_zboss_target does not have a target");
G_FreeEdict (self);
return;
}
self->movetype = MOVETYPE_NONE;
self->svflags |= SVF_NOCLIENT;
self->solid = SOLID_NOT;
self->use = trigger_zboss;
gi.linkentity (self);
}