mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +00:00
8401c475f6
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.
1908 lines
41 KiB
C
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);
|
|
}
|