]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/monsters/monster/hknight.qc
1de3ba525a2a226fc71983ca6fd00789f86e770c
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / monster / hknight.qc
1 // size
2 const vector HELLKNIGHT_MIN = '-20 -20 -32';
3 const vector HELLKNIGHT_MAX = '20 20 41';
4
5 // model
6 string HELLKNIGHT_MODEL = "models/monsters/hknight.mdl";
7
8 #ifdef SVQC
9 // cvars
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;
31
32 // animations
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;
47
48 void hellknight_think()
49 {
50         self.think = hellknight_think;
51         self.nextthink = time + self.ticrate;
52         
53         monster_move(autocvar_g_monster_hellknight_speed_run, autocvar_g_monster_hellknight_speed_walk, 100, hellknight_anim_run, hellknight_anim_walk, hellknight_anim_stand);
54 }
55
56 void hknight_spike_think()
57 {
58         if(self)
59         {
60                 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);
61                 remove(self);
62         }
63 }
64
65 void hknight_spike_touch()
66 {
67         PROJECTILE_TOUCH;
68         
69         pointparticles(particleeffectnum("TE_WIZSPIKE"), self.origin, '0 0 0', 1);
70         
71         hknight_spike_think();
72 }
73
74 void() hellknight_think;
75 void hknight_shoot ()
76 {
77         local   entity  missile = world;
78         local   vector  dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
79         local   float   dist = vlen (self.enemy.origin - self.origin), flytime = 0;
80
81         flytime = dist * 0.002;
82         if (flytime < 0.1)
83                 flytime = 0.1;
84
85         self.effects |= EF_MUZZLEFLASH;
86
87         missile = spawn ();
88         missile.owner = missile.realowner = self;
89         missile.solid = SOLID_TRIGGER;
90         missile.movetype = MOVETYPE_FLYMISSILE;
91         setsize (missile, '0 0 0', '0 0 0');            
92         setorigin(missile, self.origin + '0 0 10' + v_forward * 14);
93         missile.scale = self.scale;
94         missile.flags = FL_PROJECTILE;
95         missile.velocity = dir * 400;
96         missile.avelocity = '300 300 300';
97         missile.nextthink = time + 5;
98         missile.think = hknight_spike_think;
99         missile.enemy = self.enemy;
100         missile.touch = hknight_spike_touch;
101         CSQCProjectile(missile, TRUE, PROJECTILE_CRYLINK, TRUE);
102 }
103
104 void hknight_inferno ()
105 {
106         traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, TRUE, world);
107         if (trace_fraction != 1)
108                 return; // not visible
109         if(vlen(self.enemy.origin - self.origin) <= 2000)
110                 Fire_AddDamage(self.enemy, self, autocvar_g_monster_hellknight_inferno_damage * monster_skill, autocvar_g_monster_hellknight_inferno_damagetime, DEATH_MONSTER_HKNIGHT_INFERNO);
111 }
112
113 void hknight_infernowarning ()
114 {
115         self.monster_delayedattack = func_null;
116         self.delay = -1;
117         if(!self.enemy)
118                 return;
119                 
120         traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, TRUE, world);
121         if (trace_fraction != 1)
122                 return; // not visible
123         self.enemy.effects |= EF_MUZZLEFLASH;
124         sound(self.enemy, CHAN_AUTO, "player/lava.wav", 1, ATTN_NORM);
125         
126         hknight_inferno();
127 }
128
129 .float hknight_cycles;
130 void hellknight_magic ()
131 {
132         self.monster_delayedattack = hknight_infernowarning;
133         self.delay = time + 0.5;
134         self.attack_finished_single = time + 1.2;
135 }
136
137 void hknight_fireball_explode(entity targ)
138 {
139         if(self)
140         {
141                 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);
142                 if(targ)
143                         Fire_AddDamage(targ, self, 5 * monster_skill, autocvar_g_monster_hellknight_inferno_damagetime, self.projectiledeathtype);
144                 remove(self);
145         }
146 }
147
148 void hknight_fireball_think()
149 {
150         hknight_fireball_explode(world);
151 }
152
153 void hknight_fireball_touch()
154 {
155         PROJECTILE_TOUCH;
156         
157         hknight_fireball_explode(other);
158 }
159
160 void hellknight_fireball ()
161 {
162         local   entity  missile = spawn();
163         local   vector  dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
164         vector fmins = '-4 -4 -4', fmaxs = '4 4 4';
165
166         self.effects |= EF_MUZZLEFLASH;
167         sound (self, CHAN_WEAPON, "weapons/fireball2.wav", 1, ATTN_NORM);
168
169         missile.owner = missile.realowner = self;
170         missile.solid = SOLID_TRIGGER;
171         missile.movetype = MOVETYPE_FLYMISSILE;
172         self.projectiledeathtype = DEATH_MONSTER_HKNIGHT_FBALL;
173         setsize (missile, fmins, fmaxs);                
174         setorigin(missile, self.origin + '0 0 10' + v_forward * 14);
175         missile.flags = FL_PROJECTILE;
176         missile.velocity = dir * 400;
177         missile.avelocity = '300 300 300';
178         missile.nextthink = time + 5;
179         missile.think = hknight_fireball_think;
180         missile.enemy = self.enemy;
181         missile.touch = hknight_fireball_touch;
182         CSQCProjectile(missile, TRUE, PROJECTILE_FIREMINE, TRUE);
183         
184         self.delay = -1;
185 }
186
187 void hellknight_magic2 ()
188 {
189         monsters_setframe(hellknight_anim_magic2);
190         self.attack_finished_single = time + 1.2;
191         self.delay = time + 0.4;
192         self.monster_delayedattack = hellknight_fireball;
193 }
194
195 void hellknight_spikes ()
196 {
197         self.monster_delayedattack = hellknight_spikes;
198         self.delay = time + 0.1;
199         self.hknight_cycles += 1;
200         hknight_shoot();
201         if(self.hknight_cycles >= 7)
202         {
203                 self.monster_delayedattack = func_null;
204                 self.delay = -1;
205         }
206 }
207
208 void hellknight_magic3 ()
209 {
210         monsters_setframe(hellknight_anim_magic3);
211         self.attack_finished_single = time + 1.1;
212         self.monster_delayedattack = hellknight_spikes;
213         self.delay = time + 0.4;
214 }
215
216 float hknight_magic()
217 {
218         if not(self.flags & FL_ONGROUND)
219                 return FALSE;
220                 
221         if not(self.enemy)
222                 return FALSE; // calling attack check with no enemy?!
223                 
224         if(time < self.attack_finished_single)
225                 return FALSE;
226                 
227         self.hknight_cycles = 0;
228
229         if (self.enemy.monsterid == MONSTER_ZOMBIE)
230         {
231                 // always use fireball to kill zombies
232                 hellknight_magic2();
233                 self.attack_finished_single = time + 2;
234                 return TRUE;
235         }
236         RandomSelection_Init();
237         RandomSelection_Add(world, 0, "fireball", autocvar_g_monster_hellknight_fireball_chance, 1);
238         RandomSelection_Add(world, 0, "inferno", autocvar_g_monster_hellknight_inferno_chance, 1);
239         RandomSelection_Add(world, 0, "spikes", autocvar_g_monster_hellknight_spike_chance, 1);
240         if(self.health >= 100)
241                 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);
242         
243         switch(RandomSelection_chosen_string)
244         {
245                 case "fireball":
246                 {
247                         hellknight_magic2();
248                         self.attack_finished_single = time + 2;
249                         return TRUE;
250                 }
251                 case "spikes":
252                 {
253                         hellknight_magic3();
254                         self.attack_finished_single = time + 3;
255                         return TRUE;
256                 }
257                 case "inferno":
258                 {
259                         hellknight_magic();
260                         self.attack_finished_single = time + 3;
261                         return TRUE;
262                 }
263                 case "jump":
264                 {
265                         if (vlen(self.enemy.origin - self.origin) >= 400)
266                         if (findtrajectorywithleading(self.origin, self.mins, self.maxs, self.enemy, 1000, 0, 10, 0, self))
267                         {
268                                 self.velocity = findtrajectory_velocity;
269                                 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));
270                                 self.attack_finished_single = time + 2;
271                                 return TRUE;
272                         }
273                         return FALSE;
274                 }
275         }
276         
277         return FALSE;
278 }
279
280 float knight_attack(float attack_type)
281 {
282         switch(attack_type)
283         {
284                 case MONSTER_ATTACK_MELEE:
285                 {
286                         float anim;
287                         if(random() < 0.3)
288                                 anim = hellknight_anim_slice;
289                         else if(random() < 0.6)
290                                 anim = hellknight_anim_smash;
291                         else
292                                 anim = hellknight_anim_wattack;
293                         
294                         monsters_setframe(anim);
295                         self.attack_finished_single = time + 0.7;
296                         monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, TRUE);
297                         
298                         return TRUE;
299                 }
300                 case MONSTER_ATTACK_RANGED:
301                 {
302                         if(hknight_magic())
303                                 return TRUE;
304                 }
305         }
306         
307         return FALSE;
308 }
309
310 void hellknight_die ()
311 {
312         float chance = random();
313         Monster_CheckDropCvars ("hellknight");
314         
315         self.think = monster_dead_think;
316         self.nextthink = time + self.ticrate;
317         self.ltime = time + 5;
318         monsters_setframe((random() > 0.5) ? hellknight_anim_death1 : hellknight_anim_death2);
319         
320         if(chance < 0.10 || self.flags & MONSTERFLAG_MINIBOSS)
321         if(self.candrop)
322         {
323                 self.superweapons_finished = time + autocvar_g_balance_superweapons_time + 5; // give the player a few seconds to find the weapon
324                 self.weapon = WEP_FIREBALL;
325         }
326                 
327         monster_hook_death(); // for post-death mods
328 }
329
330 void hellknight_spawn ()
331 {
332         if not(self.health)
333                 self.health = autocvar_g_monster_hellknight_health;
334
335         self.damageforcescale   = 0.003;
336         self.classname                  = "monster_hellknight";
337         self.monster_attackfunc = knight_attack;
338         self.nextthink                  = time + random() * 0.5 + 0.1;
339         self.think                              = hellknight_think;
340         
341         monsters_setframe(hellknight_anim_stand);
342         
343         monster_setupsounds("hellknight");
344         
345         monster_hook_spawn(); // for post-spawn mods
346 }
347
348 void spawnfunc_monster_hellknight ()
349 {       
350         if not(autocvar_g_monster_hellknight) { remove(self); return; }
351         
352         self.monster_spawnfunc = spawnfunc_monster_hellknight;
353         
354         if(Monster_CheckAppearFlags(self))
355                 return;
356         
357         self.scale = 1.3;
358         
359         if not (monster_initialize(
360                          "Hell-knight", MONSTER_HELLKNIGHT,
361                          HELLKNIGHT_MIN, HELLKNIGHT_MAX,
362                          FALSE,
363                          hellknight_die, hellknight_spawn))
364         {
365                 remove(self);
366                 return;
367         }
368 }
369
370 // compatibility with old spawns
371 void spawnfunc_monster_hell_knight() { spawnfunc_monster_hellknight(); }
372
373 #endif // SVQC