#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; 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); 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) (-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; 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); }