2 const vector HELLKNIGHT_MIN = '-20 -20 -32';
3 const vector HELLKNIGHT_MAX = '20 20 41';
6 string HELLKNIGHT_MODEL = "models/monsters/hknight.mdl";
10 float autocvar_g_monster_hellknight;
11 float autocvar_g_monster_hellknight_health;
12 float autocvar_g_monster_hellknight_melee_damage;
13 float autocvar_g_monster_hellknight_inferno_damage;
14 float autocvar_g_monster_hellknight_inferno_damagetime;
15 float autocvar_g_monster_hellknight_inferno_chance;
16 float autocvar_g_monster_hellknight_speed_walk;
17 float autocvar_g_monster_hellknight_speed_run;
18 float autocvar_g_monster_hellknight_fireball_damage;
19 float autocvar_g_monster_hellknight_fireball_force;
20 float autocvar_g_monster_hellknight_fireball_radius;
21 float autocvar_g_monster_hellknight_fireball_chance;
22 float autocvar_g_monster_hellknight_fireball_edgedamage;
23 float autocvar_g_monster_hellknight_spike_chance;
24 float autocvar_g_monster_hellknight_spike_force;
25 float autocvar_g_monster_hellknight_spike_radius;
26 float autocvar_g_monster_hellknight_spike_edgedamage;
27 float autocvar_g_monster_hellknight_spike_damage;
28 float autocvar_g_monster_hellknight_jump_chance;
29 float autocvar_g_monster_hellknight_jump_damage;
30 float autocvar_g_monster_hellknight_jump_dist;
33 const float hellknight_anim_stand = 0;
34 const float hellknight_anim_walk = 1;
35 const float hellknight_anim_run = 2;
36 const float hellknight_anim_pain = 3;
37 const float hellknight_anim_death1 = 4;
38 const float hellknight_anim_death2 = 5;
39 const float hellknight_anim_charge1 = 6;
40 const float hellknight_anim_magic1 = 7;
41 const float hellknight_anim_magic2 = 8;
42 const float hellknight_anim_charge2 = 9;
43 const float hellknight_anim_slice = 10;
44 const float hellknight_anim_smash = 11;
45 const float hellknight_anim_wattack = 12;
46 const float hellknight_anim_magic3 = 13;
48 void hknight_spike_think()
52 RadiusDamage (self, self.realowner, autocvar_g_monster_hellknight_spike_damage, autocvar_g_monster_hellknight_spike_edgedamage, autocvar_g_monster_hellknight_spike_force, world, autocvar_g_monster_hellknight_spike_radius, DEATH_MONSTER_HKNIGHT_SPIKE, other);
57 void hknight_spike_touch()
61 pointparticles(particleeffectnum("TE_WIZSPIKE"), self.origin, '0 0 0', 1);
63 hknight_spike_think();
66 void() hellknight_think;
69 local entity missile = world;
70 local vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
71 local float dist = vlen (self.enemy.origin - self.origin), flytime = 0;
73 flytime = dist * 0.002;
77 self.effects |= EF_MUZZLEFLASH;
80 missile.owner = missile.realowner = self;
81 missile.solid = SOLID_TRIGGER;
82 missile.movetype = MOVETYPE_FLYMISSILE;
83 setsize (missile, '0 0 0', '0 0 0');
84 setorigin(missile, self.origin + '0 0 10' + v_forward * 14);
85 missile.scale = self.scale;
86 missile.flags = FL_PROJECTILE;
87 missile.velocity = dir * 400;
88 missile.avelocity = '300 300 300';
89 missile.nextthink = time + 5;
90 missile.think = hknight_spike_think;
91 missile.enemy = self.enemy;
92 missile.touch = hknight_spike_touch;
93 CSQCProjectile(missile, TRUE, PROJECTILE_CRYLINK, TRUE);
96 void hknight_inferno ()
98 traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, TRUE, world);
99 if (trace_fraction != 1)
100 return; // not visible
101 if(vlen(self.enemy.origin - self.origin) <= 2000)
102 Fire_AddDamage(self.enemy, self, autocvar_g_monster_hellknight_inferno_damage * monster_skill, autocvar_g_monster_hellknight_inferno_damagetime, DEATH_MONSTER_HKNIGHT_INFERNO);
105 void hknight_infernowarning ()
107 self.monster_delayedattack = func_null;
112 traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, TRUE, world);
113 if (trace_fraction != 1)
114 return; // not visible
115 self.enemy.effects |= EF_MUZZLEFLASH;
116 sound(self.enemy, CHAN_AUTO, "player/lava.wav", 1, ATTN_NORM);
121 float() hknight_magic;
122 float hknight_checkmagic ()
124 local vector v1 = '0 0 0', v2 = '0 0 0';
127 // use magic to kill zombies as they heal too fast for sword
128 if (self.enemy.monsterid == MONSTER_ZOMBIE)
130 traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, FALSE, self);
131 if (trace_ent == self.enemy)
139 return FALSE; // 25% of the time it won't do anything
140 v1 = normalize(self.enemy.velocity);
141 v2 = normalize(self.enemy.origin - self.origin);
143 if (dot >= 0.7) // moving away
144 if (vlen(self.enemy.velocity) >= 150) // walking/running away
145 return hknight_magic();
149 void() hellknight_charge;
150 void CheckForCharge ()
152 // check for mad charge
153 if (time < self.attack_finished_single)
155 if (fabs(self.origin_z - self.enemy.origin_z) > 20)
156 return; // too much height change
157 if (vlen (self.origin - self.enemy.origin) < 80)
158 return; // use regular attack
159 if (hknight_checkmagic())
160 return; // chose magic
166 void CheckContinueCharge ()
168 if(hknight_checkmagic())
169 return; // chose magic
170 if(time >= self.attack_finished_single)
173 return; // done charging
177 void hellknight_think ()
179 self.think = hellknight_think;
180 self.nextthink = time + self.ticrate;
182 monster_move(autocvar_g_monster_hellknight_speed_run, autocvar_g_monster_hellknight_speed_walk, 100, hellknight_anim_run, hellknight_anim_walk, hellknight_anim_stand);
185 .float hknight_cycles;
186 void hellknight_magic ()
188 self.monster_delayedattack = hknight_infernowarning;
189 self.delay = time + 0.5;
190 self.attack_finished_single = time + 1.2;
193 void hknight_fireball_explode(entity targ)
197 RadiusDamage (self, self.realowner, autocvar_g_monster_hellknight_fireball_damage, autocvar_g_monster_hellknight_fireball_edgedamage, autocvar_g_monster_hellknight_fireball_force, world, autocvar_g_monster_hellknight_fireball_radius, self.projectiledeathtype, targ);
199 Fire_AddDamage(targ, self, 5 * monster_skill, autocvar_g_monster_hellknight_inferno_damagetime, self.projectiledeathtype);
204 void hknight_fireball_think()
206 hknight_fireball_explode(world);
209 void hknight_fireball_touch()
213 hknight_fireball_explode(other);
216 void hellknight_fireball ()
218 local entity missile = spawn();
219 local vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
220 vector fmins = '-4 -4 -4', fmaxs = '4 4 4';
222 self.effects |= EF_MUZZLEFLASH;
223 sound (self, CHAN_WEAPON, "weapons/fireball2.wav", 1, ATTN_NORM);
225 missile.owner = missile.realowner = self;
226 missile.solid = SOLID_TRIGGER;
227 missile.movetype = MOVETYPE_FLYMISSILE;
228 self.projectiledeathtype = DEATH_MONSTER_HKNIGHT_FBALL;
229 setsize (missile, fmins, fmaxs);
230 setorigin(missile, self.origin + '0 0 10' + v_forward * 14);
231 missile.flags = FL_PROJECTILE;
232 missile.velocity = dir * 400;
233 missile.avelocity = '300 300 300';
234 missile.nextthink = time + 5;
235 missile.think = hknight_fireball_think;
236 missile.enemy = self.enemy;
237 missile.touch = hknight_fireball_touch;
238 CSQCProjectile(missile, TRUE, PROJECTILE_FIREMINE, TRUE);
243 void hellknight_magic2 ()
245 monsters_setframe(hellknight_anim_magic2);
246 self.attack_finished_single = time + 1.2;
247 self.delay = time + 0.4;
248 self.monster_delayedattack = hellknight_fireball;
251 void hellknight_spikes ()
253 self.monster_delayedattack = hellknight_spikes;
254 self.delay = time + 0.1;
255 self.hknight_cycles += 1;
257 if(self.hknight_cycles >= 7)
259 self.monster_delayedattack = func_null;
264 void hellknight_magic3 ()
266 monsters_setframe(hellknight_anim_magic3);
267 self.attack_finished_single = time + 1.1;
268 self.monster_delayedattack = hellknight_spikes;
269 self.delay = time + 0.4;
272 void hellknight_charge ()
274 monsters_setframe(hellknight_anim_charge1);
275 self.attack_finished_single = time + 0.5;
277 hknight_checkmagic();
278 monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, FALSE);
279 hknight_checkmagic();
282 void hellknight_charge2 ()
284 monsters_setframe(hellknight_anim_charge2);
285 self.attack_finished_single = time + 0.5;
287 CheckContinueCharge ();
288 monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, FALSE);
291 void hellknight_slice ()
293 monsters_setframe(hellknight_anim_slice);
294 self.attack_finished_single = time + 0.7;
295 monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, FALSE);
298 void hellknight_smash ()
300 monsters_setframe(hellknight_anim_smash);
301 self.attack_finished_single = time + 0.7;
302 monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, TRUE);
305 void hellknight_weapon_attack ()
307 monsters_setframe(hellknight_anim_wattack);
308 self.attack_finished_single = time + 0.7;
309 monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, TRUE);
313 float hknight_melee ()
317 if (hknight_type == 1)
319 else if (hknight_type == 2)
323 hellknight_weapon_attack();
330 float hknight_magic ()
332 if not(self.flags & FL_ONGROUND)
336 return FALSE; // calling attack check with no enemy?!
338 if(time < self.attack_finished_single)
341 self.hknight_cycles = 0;
343 if (self.enemy.monsterid == MONSTER_ZOMBIE)
345 // always use fireball to kill zombies
347 self.attack_finished_single = time + 2;
350 RandomSelection_Init();
351 RandomSelection_Add(world, 0, "fireball", autocvar_g_monster_hellknight_fireball_chance, 1);
352 RandomSelection_Add(world, 0, "inferno", autocvar_g_monster_hellknight_inferno_chance, 1);
353 RandomSelection_Add(world, 0, "spikes", autocvar_g_monster_hellknight_spike_chance, 1);
354 if(self.health >= 100)
355 RandomSelection_Add(world, 0, "jump", ((vlen(self.enemy.origin - self.origin) > autocvar_g_monster_hellknight_jump_dist) ? 1 : autocvar_g_monster_hellknight_jump_chance), 1);
357 switch(RandomSelection_chosen_string)
362 self.attack_finished_single = time + 2;
368 self.attack_finished_single = time + 3;
374 self.attack_finished_single = time + 3;
379 if (vlen(self.enemy.origin - self.origin) >= 400)
380 if (findtrajectorywithleading(self.origin, self.mins, self.maxs, self.enemy, 1000, 0, 10, 0, self))
382 self.velocity = findtrajectory_velocity;
383 Damage(self.enemy, self, self, autocvar_g_monster_hellknight_jump_damage * monster_skill, DEATH_MONSTER_HKNIGHT_CRUSH, self.enemy.origin, normalize(self.enemy.origin - self.origin));
384 self.attack_finished_single = time + 2;
395 void hellknight_die ()
397 float chance = random();
398 Monster_CheckDropCvars ("hellknight");
400 self.think = monster_dead_think;
401 self.nextthink = time + self.ticrate;
402 self.ltime = time + 5;
403 monsters_setframe((random() > 0.5) ? hellknight_anim_death1 : hellknight_anim_death2);
405 if(chance < 0.10 || self.flags & MONSTERFLAG_MINIBOSS)
408 self.superweapons_finished = time + autocvar_g_balance_superweapons_time + 5; // give the player a few seconds to find the weapon
409 self.weapon = WEP_FIREBALL;
412 monster_hook_death(); // for post-death mods
415 void hellknight_spawn ()
418 self.health = autocvar_g_monster_hellknight_health;
420 self.damageforcescale = 0.003;
421 self.classname = "monster_hellknight";
422 self.checkattack = GenericCheckAttack;
423 self.attack_melee = hknight_melee;
424 self.attack_ranged = hknight_magic;
425 self.nextthink = time + random() * 0.5 + 0.1;
426 self.think = hellknight_think;
428 monsters_setframe(hellknight_anim_stand);
430 monster_setupsounds("hellknight");
432 monster_hook_spawn(); // for post-spawn mods
435 void spawnfunc_monster_hellknight ()
437 if not(autocvar_g_monster_hellknight) { remove(self); return; }
439 self.monster_spawnfunc = spawnfunc_monster_hellknight;
441 if(Monster_CheckAppearFlags(self))
446 if not (monster_initialize(
447 "Hell-knight", MONSTER_HELLKNIGHT,
448 HELLKNIGHT_MIN, HELLKNIGHT_MAX,
450 hellknight_die, hellknight_spawn))
457 // compatibility with old spawns
458 void spawnfunc_monster_hell_knight() { spawnfunc_monster_hellknight(); }