mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
0d4e872ce9
Added plasma guards (monster_soldier_plasma_re and monster_soldier_plasma_sp) from LM Escape to missionpack DLL. Added Zaero items/weapons to missionpack DLL. Added support for Zaero doors to missionpack DLL. Fixed crash caused by killtargeting sentien (laser edict not freed) in missionpack DLL. Fixed bug with broken Rogue turrets in missionpack DLL. Fixed crash in g_combat.c->M_ReactToDamage() caused by attacker with NULL classname in missionpack DLL.
1856 lines
41 KiB
C
1856 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 = (self->style * 3) + 2;
|
|
}
|
|
else if (self->health < hbreak * 2)
|
|
{
|
|
self->s.skinnum = (self->style * 3) + 1;
|
|
}
|
|
else
|
|
{
|
|
self->s.skinnum = (self->style * 3) + 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.aiflags2 &= ~AI2_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.aiflags2 & AI2_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.aiflags2 & AI2_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.aiflags2 & AI2_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, NULL);
|
|
|
|
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);
|
|
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, 10, 0, 0, MOD_HIT);
|
|
}
|
|
|
|
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.aiflags2 & AI2_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.aiflags2 & AI2_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.aiflags2 &= ~AI2_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, NULL);
|
|
|
|
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, NULL);
|
|
|
|
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, NULL);
|
|
|
|
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, NULL);
|
|
|
|
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, NULL);
|
|
|
|
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, NULL);
|
|
|
|
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, NULL);
|
|
|
|
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);
|
|
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, 10, 0, 0, MOD_HIT);
|
|
}
|
|
|
|
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 = (self->style * 3) + 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;
|
|
|
|
// 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) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight GoodGuy NoGib
|
|
model="models/monsters/bossz/mech/"
|
|
model2="models/monsters/bossz/pilot/"
|
|
model3="models/monsters/bossz/grapple/"
|
|
*/
|
|
void SP_monster_zboss (edict_t *self)
|
|
{
|
|
if (deathmatch->value)
|
|
{
|
|
G_FreeEdict (self);
|
|
return;
|
|
}
|
|
|
|
SP_monster_zboss_precache();
|
|
|
|
// Lazarus: special purpose skins
|
|
if ( self->style )
|
|
{
|
|
PatchMonsterModel("models/monsters/bossz/mech/tris.md2");
|
|
PatchMonsterModel("models/monsters/bossz/pilot/tris.md2");
|
|
self->s.skinnum = self->style * 3;
|
|
}
|
|
|
|
// 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.aiflags2 = AI2_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
|
|
|
|
self->common_name = "Titan";
|
|
|
|
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.aiflags2 |= AI2_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);
|
|
}
|