// size const vector SHALRATH_MIN = '-32 -32 -24'; const vector SHALRATH_MAX = '32 32 32'; // cvars float autocvar_g_monster_shalrath; float autocvar_g_monster_shalrath_health; float autocvar_g_monster_shalrath_speed; float autocvar_g_monster_shalrath_attack_spike_damage; float autocvar_g_monster_shalrath_attack_spike_radius; float autocvar_g_monster_shalrath_attack_spike_delay; float autocvar_g_monster_shalrath_attack_melee_damage; float autocvar_g_monster_shalrath_attack_melee_delay; // animations #define shalrath_anim_idle 0 #define shalrath_anim_walk 1 #define shalrath_anim_attack 2 #define shalrath_anim_pain 3 #define shalrath_anim_death 4 #define shalrath_anim_run 5 void() ShalMissile; void shalrath_think () { self.think = shalrath_think; self.nextthink = time + 0.1; if(self.delay != -1) self.nextthink = self.delay; monster_move(autocvar_g_monster_shalrath_speed, autocvar_g_monster_shalrath_speed, 50, shalrath_anim_walk, shalrath_anim_run, shalrath_anim_idle); } void shalrath_attack () { self.frame = shalrath_anim_attack; self.delay = time + 0.2; self.attack_finished_single = time + autocvar_g_monster_shalrath_attack_spike_delay; self.monster_delayedattack = ShalMissile; } void shalrathattack_melee () { float bigdmg = 0, rdmg = autocvar_g_monster_shalrath_attack_melee_damage * random(); bigdmg = rdmg * self.scale; monster_melee(self.enemy, bigdmg * monster_skill, 120, DEATH_MONSTER_MAGE); } void shalrath_attack_melee () { self.monster_delayedattack = shalrathattack_melee; self.delay = time + 0.2; self.frame = shalrath_anim_attack; self.attack_finished_single = time + autocvar_g_monster_shalrath_attack_melee_delay; } float shal_missile () { shalrath_attack(); return TRUE; } void ShalHome () { local vector dir = '0 0 0', vtemp = self.enemy.origin + '0 0 10'; if (self.enemy.health <= 0 || self.owner.health <= 0 || time >= self.ltime) { remove(self); return; } dir = normalize(vtemp - self.origin); UpdateCSQCProjectile(self); if (monster_skill == 3) self.velocity = dir * 350; else self.velocity = dir * 250; self.nextthink = time + 0.2; self.think = ShalHome; } void shal_spike_explode () { self.event_damage = func_null; pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1); RadiusDamage (self, self.realowner, autocvar_g_monster_shalrath_attack_spike_damage, autocvar_g_monster_shalrath_attack_spike_damage * 0.5, autocvar_g_monster_shalrath_attack_spike_radius, world, 0, DEATH_MONSTER_MAGE, other); remove (self); } void shal_spike_touchexplode() { PROJECTILE_TOUCH; shal_spike_explode(); } void ShalMissile () { local entity missile = world; local vector dir = '0 0 0'; local float dist = 0; self.effects |= EF_MUZZLEFLASH; missile = spawn (); missile.owner = missile.realowner = self; self.v_angle = self.angles; makevectors (self.angles); dir = normalize((self.enemy.origin + '0 0 10') - self.origin); dist = vlen (self.enemy.origin - self.origin); missile.think = ShalHome; missile.ltime = time + 7; missile.nextthink = time; missile.solid = SOLID_BBOX; missile.movetype = MOVETYPE_FLYMISSILE; missile.flags = FL_PROJECTILE; setorigin (missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14); setsize (missile, '0 0 0', '0 0 0'); missile.velocity = dir * 400; missile.avelocity = '300 300 300'; missile.enemy = self.enemy; missile.touch = shal_spike_touchexplode; CSQCProjectile(missile, TRUE, PROJECTILE_VORE_SPIKE, TRUE); } float ShalrathCheckAttack () { local vector spot1 = '0 0 0', spot2 = '0 0 0'; local entity targ = self.enemy; if (self.health <= 0 || targ == world || targ.health < 1) return FALSE; if(self.monster_delayedattack && self.delay != -1) { if(time < self.delay) return FALSE; self.monster_delayedattack(); self.delay = -1; self.monster_delayedattack = func_null; } if(time < self.attack_finished_single) return FALSE; if (vlen(self.enemy.origin - self.origin) <= 120) { // melee attack if (self.attack_melee) { monster_sound(self.msound_attack_melee, 0, FALSE); // no delay for attack sounds self.attack_melee(); return TRUE; } } // see if any entities are in the way of the shot spot1 = self.origin + self.view_ofs; spot2 = targ.origin + targ.view_ofs; traceline (spot1, spot2, FALSE, self); if (trace_ent != targ && trace_fraction < 1) return FALSE; // don't have a clear shot //if (trace_inopen && trace_inwater) // return FALSE; // sight line crossed contents if (random() < 0.2) if (self.attack_ranged()) return TRUE; return FALSE; } void shalrath_die () { Monster_CheckDropCvars ("shalrath"); self.think = Monster_Fade; self.frame = shalrath_anim_death; self.solid = SOLID_NOT; self.takedamage = DAMAGE_NO; self.event_damage = func_null; self.enemy = world; self.nextthink = time + 2.1; self.movetype = MOVETYPE_TOSS; monster_hook_death(); // for post-death mods } void shalrath_spawn () { if not(self.health) self.health = autocvar_g_monster_shalrath_health * self.scale; self.damageforcescale = 0.003; self.classname = "monster_shalrath"; self.checkattack = ShalrathCheckAttack; self.attack_ranged = shal_missile; self.attack_melee = shalrath_attack_melee; self.nextthink = time + random() * 0.5 + 0.1; self.think = shalrath_think; self.frame = shalrath_anim_walk; self.sprite_height = 40 * self.scale; monster_hook_spawn(); // for post-spawn mods } void spawnfunc_monster_shalrath () { if not(autocvar_g_monster_shalrath) { remove(self); return; } self.monster_spawnfunc = spawnfunc_monster_shalrath; if(self.spawnflags & MONSTERFLAG_APPEAR) { self.think = func_null; self.nextthink = -1; self.use = Monster_Appear; return; } self.scale = 1.3; if not (monster_initialize( "Mage", "models/monsters/mage.dpm", SHALRATH_MIN, SHALRATH_MAX, FALSE, shalrath_die, shalrath_spawn)) { remove(self); return; } } // compatibility with old spawns void spawnfunc_monster_vore () { spawnfunc_monster_shalrath(); }