]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/monsters/monster/hknight.qc
Remove enemy_range() checks
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / monster / hknight.qc
1 // size
2 const vector HELLKNIGHT_MIN = '-16 -16 -24';
3 const vector HELLKNIGHT_MAX = '16 16 32';
4
5 // cvars
6 float autocvar_g_monster_hellknight;
7 float autocvar_g_monster_hellknight_health;
8 float autocvar_g_monster_hellknight_melee_damage;
9 float autocvar_g_monster_hellknight_inferno_damage;
10 float autocvar_g_monster_hellknight_inferno_damagetime;
11 float autocvar_g_monster_hellknight_inferno_chance;
12 float autocvar_g_monster_hellknight_speed_walk;
13 float autocvar_g_monster_hellknight_speed_run;
14 float autocvar_g_monster_hellknight_fireball_damage;
15 float autocvar_g_monster_hellknight_fireball_force;
16 float autocvar_g_monster_hellknight_fireball_radius;
17 float autocvar_g_monster_hellknight_fireball_chance;
18 float autocvar_g_monster_hellknight_fireball_edgedamage;
19 float autocvar_g_monster_hellknight_spike_chance;
20 float autocvar_g_monster_hellknight_spike_force;
21 float autocvar_g_monster_hellknight_spike_radius;
22 float autocvar_g_monster_hellknight_spike_edgedamage;
23 float autocvar_g_monster_hellknight_spike_damage;
24 float autocvar_g_monster_hellknight_jump_chance;
25 float autocvar_g_monster_hellknight_jump_damage;
26 float autocvar_g_monster_hellknight_jump_dist;
27
28 // animations
29 #define hellknight_anim_stand   0
30 #define hellknight_anim_walk    1
31 #define hellknight_anim_run     2
32 #define hellknight_anim_pain    3
33 #define hellknight_anim_death1  4
34 #define hellknight_anim_death2  5
35 #define hellknight_anim_charge1 6
36 #define hellknight_anim_magic1  7
37 #define hellknight_anim_magic2  8
38 #define hellknight_anim_charge2 9
39 #define hellknight_anim_slice   10
40 #define hellknight_anim_smash   11
41 #define hellknight_anim_wattack 12
42 #define hellknight_anim_magic3  13
43
44 void hknight_spike_think()
45 {
46         if(self)
47         {
48                 RadiusDamage (self, self.realowner, autocvar_g_monster_hellknight_spike_damage * self.realowner.scale, autocvar_g_monster_hellknight_spike_edgedamage, autocvar_g_monster_hellknight_spike_force, world, autocvar_g_monster_hellknight_spike_radius, DEATH_MONSTER_HKNIGHT_SPIKE, other);
49                 remove(self);
50         }
51 }
52
53 void hknight_spike_touch()
54 {
55         PROJECTILE_TOUCH;
56         
57         pointparticles(particleeffectnum("TE_WIZSPIKE"), self.origin, '0 0 0', 1);
58         
59         hknight_spike_think();
60 }
61
62 void() hellknight_think;
63 void hknight_shoot ()
64 {
65         local   entity  missile = world;
66         local   vector  dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
67         local   float   dist = vlen (self.enemy.origin - self.origin), flytime = 0;
68
69         flytime = dist * 0.002;
70         if (flytime < 0.1)
71                 flytime = 0.1;
72
73         self.effects |= EF_MUZZLEFLASH;
74         sound (self, CHAN_WEAPON, "weapons/spike.wav", 1, ATTN_NORM);
75
76         missile = spawn ();
77         missile.owner = missile.realowner = self;
78         missile.solid = SOLID_TRIGGER;
79         missile.movetype = MOVETYPE_FLYMISSILE;
80         setsize (missile, '0 0 0', '0 0 0');            
81         setorigin(missile, self.origin + '0 0 10' + v_forward * 14);
82         missile.scale = self.scale;
83         missile.flags = FL_PROJECTILE;
84         missile.velocity = dir * 400;
85         missile.avelocity = '300 300 300';
86         missile.nextthink = time + 5;
87         missile.think = hknight_spike_think;
88         missile.enemy = self.enemy;
89         missile.touch = hknight_spike_touch;
90         CSQCProjectile(missile, TRUE, PROJECTILE_CRYLINK, TRUE);
91 }
92
93 void hknight_inferno ()
94 {
95         traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, TRUE, world);
96         if (trace_fraction != 1)
97                 return; // not visible
98         if(vlen(self.enemy.origin - self.origin) <= 2000)
99                 Fire_AddDamage(self.enemy, self, autocvar_g_monster_hellknight_inferno_damage * monster_skill, autocvar_g_monster_hellknight_inferno_damagetime, DEATH_MONSTER_HKNIGHT_INFERNO);
100 }
101
102 void hknight_infernowarning ()
103 {
104         if(!self.enemy)
105                 return;
106                 
107         traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, TRUE, world);
108         if (trace_fraction != 1)
109                 return; // not visible
110         self.enemy.effects |= EF_MUZZLEFLASH;
111         sound(self.enemy, CHAN_AUTO, "player/lava.wav", 1, ATTN_NORM);
112         
113         hknight_inferno();
114 }
115
116 float() hknight_magic;
117 float hknight_checkmagic ()
118 {
119         local vector v1 = '0 0 0', v2 = '0 0 0';
120         local float dot = 0;
121
122         // use magic to kill zombies as they heal too fast for sword
123         if (self.enemy.classname == "monster_zombie")
124         {
125                 traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, FALSE, self);
126                 if (trace_ent == self.enemy)
127                 {
128                         hknight_magic();
129                         return TRUE;
130                 }
131         }
132
133         if (random() < 0.25)
134                 return FALSE; // 25% of the time it won't do anything
135         v1 = normalize(self.enemy.velocity);
136         v2 = normalize(self.enemy.origin - self.origin);
137         dot = v1 * v2;
138         if (dot >= 0.7) // moving away
139         if (vlen(self.enemy.velocity) >= 150) // walking/running away
140                 return hknight_magic();
141         return FALSE;
142 }
143
144 void() hellknight_charge;
145 void CheckForCharge ()
146 {
147         // check for mad charge
148         if (time < self.attack_finished_single)
149                 return;
150         if (fabs(self.origin_z - self.enemy.origin_z) > 20)
151                 return;         // too much height change
152         if (vlen (self.origin - self.enemy.origin) < 80)
153                 return;         // use regular attack
154         if (hknight_checkmagic())
155                 return; // chose magic
156
157         // charge
158         hellknight_charge();
159 }
160
161 void CheckContinueCharge ()
162 {
163         if(hknight_checkmagic())
164                 return; // chose magic
165         if(time >= self.attack_finished_single)
166         {
167                 hellknight_think();
168                 return;         // done charging
169         }
170 }
171
172 void hellknight_think ()
173 {
174         self.think = hellknight_think;
175         self.nextthink = time + 0.1;
176         
177         monster_move(autocvar_g_monster_hellknight_speed_run, autocvar_g_monster_hellknight_speed_walk, 100, hellknight_anim_run, hellknight_anim_walk, hellknight_anim_stand);
178 }
179
180 .float hknight_cycles;
181 void hellknight_magic ()
182 {
183         self.hknight_cycles += 1;
184         self.think = hellknight_magic;
185         
186         if(self.hknight_cycles >= 5)
187         {
188                 self.frame = hellknight_anim_magic1;
189                 self.attack_finished_single = time + 0.7;
190                 hknight_infernowarning();
191                 self.think = hellknight_think;
192         }
193         
194         self.nextthink = time + 0.1;
195 }
196
197 void hknight_fireball_explode(entity targ)
198 {
199         float scle = self.realowner.scale;
200         if(self)
201         {
202                 RadiusDamage (self, self.realowner, autocvar_g_monster_hellknight_fireball_damage * scle, autocvar_g_monster_hellknight_fireball_edgedamage * scle, autocvar_g_monster_hellknight_fireball_force * scle, world, autocvar_g_monster_hellknight_fireball_radius * scle, self.projectiledeathtype, targ);
203                 if(targ)
204                         Fire_AddDamage(targ, self, 5 * monster_skill, autocvar_g_monster_hellknight_inferno_damagetime, self.projectiledeathtype);
205                 remove(self);
206         }
207 }
208
209 void hknight_fireball_think()
210 {
211         hknight_fireball_explode(world);
212 }
213
214 void hknight_fireball_touch()
215 {
216         PROJECTILE_TOUCH;
217         
218         hknight_fireball_explode(other);
219 }
220
221 void hellknight_fireball ()
222 {
223         local   entity  missile = spawn();
224         local   vector  dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
225         vector fmins = ((self.scale >= 2) ? '-16 -16 -16' : '-4 -4 -4'), fmaxs = ((self.scale >= 2) ? '16 16 16' : '4 4 4');
226
227         self.effects |= EF_MUZZLEFLASH;
228         sound (self, CHAN_WEAPON, "weapons/fireball2.wav", 1, ATTN_NORM);
229
230         missile.owner = missile.realowner = self;
231         missile.solid = SOLID_TRIGGER;
232         missile.movetype = MOVETYPE_FLYMISSILE;
233         self.projectiledeathtype = DEATH_MONSTER_HKNIGHT_FBALL;
234         setsize (missile, fmins, fmaxs);                
235         setorigin(missile, self.origin + '0 0 10' + v_forward * 14);
236         missile.flags = FL_PROJECTILE;
237         missile.velocity = dir * 400;
238         missile.avelocity = '300 300 300';
239         missile.nextthink = time + 5;
240         missile.think = hknight_fireball_think;
241         missile.enemy = self.enemy;
242         missile.touch = hknight_fireball_touch;
243         CSQCProjectile(missile, TRUE, ((self.scale >= 2) ? PROJECTILE_FIREBALL : PROJECTILE_FIREMINE), TRUE);
244         
245         self.delay = -1;
246 }
247
248 void hellknight_magic2 ()
249 {
250         self.frame = hellknight_anim_magic2;
251         self.attack_finished_single = time + 1.2;
252         self.delay = time + 0.4;
253         self.monster_delayedattack = hellknight_fireball;
254 }
255
256 void hellknight_spikes ()
257 {
258         self.think = hellknight_spikes;
259         self.nextthink = time + 0.1;
260         self.hknight_cycles += 1;
261         hknight_shoot();
262         if(self.hknight_cycles >= 7)
263                 self.think = hellknight_think;
264 }
265
266 void hellknight_magic3 ()
267 {
268         self.frame = hellknight_anim_magic3;
269         self.attack_finished_single = time + 1;
270         self.think = hellknight_spikes;
271         self.nextthink = time + 0.4;
272 }
273
274 void hellknight_charge ()
275 {
276         self.frame = hellknight_anim_charge1;
277         self.attack_finished_single = time + 0.5;
278         
279         hknight_checkmagic();
280         monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE);
281         hknight_checkmagic();
282 }
283
284 void hellknight_charge2 ()
285 {
286         self.frame = hellknight_anim_charge2;
287         self.attack_finished_single = time + 0.5;
288         
289         CheckContinueCharge ();
290         monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE);
291 }
292
293 void hellknight_slice ()
294 {
295         self.frame = hellknight_anim_slice;
296         self.attack_finished_single = time + 0.7;
297         monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE);
298 }
299
300 void hellknight_smash ()
301 {
302         self.frame = hellknight_anim_smash;
303         self.attack_finished_single = time + 0.7;
304         monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE);
305 }
306
307 void hellknight_weapon_attack ()
308 {
309         self.frame = hellknight_anim_wattack;
310         self.attack_finished_single = time + 0.7;
311         monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE);
312 }
313
314 float hknight_type;
315 void hknight_melee ()
316 {
317         hknight_type += 1;
318
319         if (hknight_type == 1)
320                 hellknight_slice();
321         else if (hknight_type == 2)
322                 hellknight_smash();
323         else
324         {
325                 hellknight_weapon_attack();
326                 hknight_type = 0;
327         }
328 }
329
330 float hknight_magic ()
331 {
332         if not(self.flags & FL_ONGROUND)
333                 return FALSE;
334                 
335         if not(self.enemy)
336                 return FALSE; // calling attack check with no enemy?!
337                 
338         if(time < self.attack_finished_single)
339                 return FALSE;
340                 
341         self.hknight_cycles = 0;
342
343         if (self.enemy.classname == "monster_zombie")
344         {
345                 // always use fireball to kill zombies
346                 hellknight_magic2();
347                 self.attack_finished_single = time + 2;
348                 return TRUE;
349         }
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 * self.scale) ? 1 : autocvar_g_monster_hellknight_jump_chance), 1);
356         
357         switch(RandomSelection_chosen_string)
358         {
359                 case "fireball":
360                 {
361                         hellknight_magic2();
362                         self.attack_finished_single = time + 2;
363                         return TRUE;
364                 }
365                 case "spikes":
366                 {
367                         hellknight_magic3();
368                         self.attack_finished_single = time + 3;
369                         return TRUE;
370                 }
371                 case "inferno":
372                 {
373                         hellknight_magic();
374                         self.attack_finished_single = time + 3;
375                         return TRUE;
376                 }
377                 case "jump":
378                 {
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))
381                         {
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;
385                                 return TRUE;
386                         }
387                         return FALSE;
388                 }
389                 default:
390                         return FALSE;
391         }
392         // never get here
393 }
394
395 void hellknight_die ()
396 {
397         float chance = random();
398         Monster_CheckDropCvars ("hellknight");
399         
400         self.solid                      = SOLID_NOT;
401         self.takedamage         = DAMAGE_NO;
402         self.event_damage   = func_null;
403         self.enemy                      = world;
404         self.movetype           = MOVETYPE_TOSS;
405         self.think                      = Monster_Fade;
406         self.nextthink          = time + 2.1;
407         
408         if(chance < 0.10 || self.flags & MONSTERFLAG_MINIBOSS)
409         if(self.candrop)
410         {
411                 self.superweapons_finished = time + autocvar_g_balance_superweapons_time + 5; // give the player a few seconds to find the weapon
412                 W_ThrowNewWeapon(self, WEP_FIREBALL, 0, self.origin, self.velocity);
413         }
414         
415         if (random() > 0.5)
416                 self.frame = hellknight_anim_death1;
417         else
418                 self.frame = hellknight_anim_death2;
419                 
420         monster_hook_death(); // for post-death mods
421 }
422
423 void hellknight_spawn ()
424 {
425         if not(self.health)
426                 self.health = autocvar_g_monster_hellknight_health * self.scale;
427
428         self.damageforcescale   = 0.003;
429         self.classname                  = "monster_hellknight";
430         self.checkattack                = GenericCheckAttack;
431         self.attack_melee               = hknight_melee;
432         self.attack_ranged              = hknight_magic;
433         self.nextthink                  = time + random() * 0.5 + 0.1;
434         self.think                              = hellknight_think;
435         self.sprite_height              = 30 * self.scale;
436         self.frame                              = hellknight_anim_stand;
437         
438         monster_hook_spawn(); // for post-spawn mods
439 }
440
441 void spawnfunc_monster_hell_knight ()
442 {       
443         if not(autocvar_g_monster_hellknight) { remove(self); return; }
444         
445         self.monster_spawnfunc = spawnfunc_monster_hell_knight;
446         
447         if(self.spawnflags & MONSTERFLAG_APPEAR)
448         {
449                 self.think = func_null;
450                 self.nextthink = -1;
451                 self.use = Monster_Appear;
452                 return;
453         }
454         
455         self.scale = 1.3;
456         
457         if not (monster_initialize(
458                          "Hell-knight",
459                          "models/monsters/hknight.mdl",
460                          HELLKNIGHT_MIN, HELLKNIGHT_MAX,
461                          FALSE,
462                          hellknight_die, hellknight_spawn))
463         {
464                 remove(self);
465                 return;
466         }
467         
468         precache_sound ("weapons/spike.wav");
469 }
470
471 // compatibility with old spawns
472 void spawnfunc_monster_hellknight () { spawnfunc_monster_hell_knight(); }