]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/monsters/monster/shalrath.qc
Merge branch 'master' into mario/monsters
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / monster / shalrath.qc
1 // size
2 const vector SHALRATH_MIN = '-32 -32 -24';
3 const vector SHALRATH_MAX = '32 32 32';
4
5 // cvars
6 float autocvar_g_monster_shalrath;
7 float autocvar_g_monster_shalrath_health;
8 float autocvar_g_monster_shalrath_speed;
9 float autocvar_g_monster_shalrath_attack_spike_damage;
10 float autocvar_g_monster_shalrath_attack_spike_radius;
11 float autocvar_g_monster_shalrath_attack_spike_delay;
12 float autocvar_g_monster_shalrath_attack_melee_damage;
13 float autocvar_g_monster_shalrath_attack_melee_delay;
14
15 // animations
16 #define shalrath_anim_idle              0
17 #define shalrath_anim_walk              1
18 #define shalrath_anim_attack    2
19 #define shalrath_anim_pain              3
20 #define shalrath_anim_death     4
21 #define shalrath_anim_run               5
22
23
24 void() ShalMissile;
25
26 void shalrath_think ()
27 {
28         self.think = shalrath_think;
29         self.nextthink = time + 0.1;
30         
31         if(self.delay != -1)
32                 self.nextthink = self.delay;
33         
34         monster_move(autocvar_g_monster_shalrath_speed, autocvar_g_monster_shalrath_speed, 50, shalrath_anim_walk, shalrath_anim_run, shalrath_anim_idle);
35 }
36
37 void shalrath_attack ()
38 {
39         self.frame = shalrath_anim_attack;
40         self.delay = time + 0.2;
41         self.attack_finished_single = time + autocvar_g_monster_shalrath_attack_spike_delay;
42         self.monster_delayedattack = ShalMissile;
43 }
44
45 void shalrathattack_melee ()
46 {
47         float bigdmg = 0, rdmg = autocvar_g_monster_shalrath_attack_melee_damage * random();
48
49         bigdmg = rdmg * self.scale;
50
51         monster_melee(self.enemy, bigdmg * monster_skill, 120, DEATH_MONSTER_SHALRATH_MELEE);
52 }
53
54 void shalrath_attack_melee ()
55 {
56         self.monster_delayedattack = shalrathattack_melee;
57         self.delay = time + 0.2;
58         self.frame = shalrath_anim_attack;
59         self.attack_finished_single = time + autocvar_g_monster_shalrath_attack_melee_delay;
60 }
61
62 float shal_missile ()
63 {
64         // don't throw if it is blocked
65         traceline(self.origin + '0 0 10', self.enemy.origin + '0 0 10', FALSE, self);
66         if (enemy_range() > 1000)
67                 return FALSE;
68         if (trace_ent != self.enemy)
69                 return FALSE;
70         shalrath_attack();
71         return TRUE;
72 }
73
74 .float shal_cycles;
75 void ShalHome ()
76 {
77         local vector dir = '0 0 0', vtemp = self.enemy.origin + '0 0 10';
78         
79         self.shal_cycles += 1;
80         if (self.enemy.health <= 0 || self.owner.health <= 0 || self.shal_cycles >= 20)
81         {
82                 remove(self);
83                 return;
84         }
85         dir = normalize(vtemp - self.origin);
86         UpdateCSQCProjectile(self);
87         if (monster_skill == 3)
88                 self.velocity = dir * 350;
89         else
90                 self.velocity = dir * 250;
91         self.nextthink = time + 0.2;
92         self.think = ShalHome;  
93 }
94
95 void shal_spike_explode ()
96 {
97         self.event_damage = func_null;
98
99         pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1);
100         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_SHALRATH_MELEE, other);
101
102         remove (self);
103 }
104
105 void shal_spike_touchexplode()
106 {
107         PROJECTILE_TOUCH;
108
109         shal_spike_explode();
110 }
111
112 void ShalMissile ()
113 {
114         local   entity  missile = world;
115         local   vector  dir = '0 0 0';
116         local   float   dist = 0;
117         
118         self.effects |= EF_MUZZLEFLASH;
119
120         missile = spawn ();
121         missile.owner = missile.realowner = self;
122         
123         self.v_angle = self.angles;
124         makevectors (self.angles);
125         
126         dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
127         dist = vlen (self.enemy.origin - self.origin);
128
129         missile.think = ShalHome;
130         missile.nextthink = time;
131         missile.solid = SOLID_BBOX;
132         missile.movetype = MOVETYPE_FLYMISSILE;
133         missile.flags = FL_PROJECTILE;
134         setorigin (missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14);
135         setsize (missile, '0 0 0', '0 0 0');    
136         missile.velocity = dir * 400;
137         missile.avelocity = '300 300 300';
138         missile.enemy = self.enemy;
139         missile.touch = shal_spike_touchexplode;
140         
141         CSQCProjectile(missile, TRUE, PROJECTILE_VORE_SPIKE, TRUE);
142 }
143
144 float ShalrathCheckAttack ()
145 {
146         local vector spot1 = '0 0 0', spot2 = '0 0 0';
147         local entity targ = self.enemy;
148
149         if (self.health <= 0 || targ == world || targ.health < 1)
150                 return FALSE;
151         
152         if(self.monster_delayedattack && self.delay != -1)
153         {
154                 if(time < self.delay)
155                         return FALSE;
156                         
157                 self.monster_delayedattack();
158                 self.delay = -1;
159                 self.monster_delayedattack = func_null;
160         }
161         
162         if(time < self.attack_finished_single)
163                 return FALSE;
164         
165         if (vlen(self.enemy.origin - self.origin) <= 120)
166         {       // melee attack
167                 if (self.attack_melee)
168                 {
169                         self.attack_melee();
170                         return TRUE;
171                 }
172         }
173
174         if (vlen(targ.origin - self.origin) >= 2000) // long traces are slow
175                 return FALSE;
176
177 // see if any entities are in the way of the shot
178         spot1 = self.origin + '0 0 10';
179         spot2 = targ.origin + '0 0 10';
180
181         traceline (spot1, spot2, FALSE, self);
182
183         if (trace_ent != targ && trace_fraction < 1)
184                 return FALSE; // don't have a clear shot
185
186         //if (trace_inopen && trace_inwater)
187         //      return FALSE; // sight line crossed contents
188
189         if (random() < 0.2)
190         if (self.attack_ranged())
191                 return TRUE;
192
193         return FALSE;
194 }
195
196 void shalrath_die ()
197 {
198         Monster_CheckDropCvars ("shalrath");
199         
200         self.think                      = Monster_Fade;
201         self.frame                      = shalrath_anim_death;
202         self.solid                      = SOLID_NOT;
203         self.takedamage         = DAMAGE_NO;
204         self.event_damage   = func_null;
205         self.enemy                      = world;
206         self.nextthink          = time + 2.1;
207         self.pain_finished  = self.nextthink;   
208         self.movetype           = MOVETYPE_TOSS;
209         
210         monster_hook_death(); // for post-death mods
211 }
212
213 void shalrath_spawn ()
214 {
215         if not(self.health)
216                 self.health = autocvar_g_monster_shalrath_health * self.scale;
217
218         self.damageforcescale   = 0.003;
219         self.classname                  = "monster_shalrath";
220         self.checkattack                = ShalrathCheckAttack;
221         self.attack_ranged              = shal_missile;
222         self.attack_melee               = shalrath_attack_melee;
223         self.nextthink                  = time + random() * 0.5 + 0.1;
224         self.think                              = shalrath_think;
225         self.frame                              = shalrath_anim_walk;
226         self.sprite_height              = 40 * self.scale;
227         
228         monster_hook_spawn(); // for post-spawn mods
229 }
230
231 void spawnfunc_monster_shalrath ()
232 {       
233         if not(autocvar_g_monster_shalrath) { remove(self); return; }
234         
235         self.monster_spawnfunc = spawnfunc_monster_shalrath;
236         
237         if(self.spawnflags & MONSTERFLAG_APPEAR)
238         {
239                 self.think = func_null;
240                 self.nextthink = -1;
241                 self.use = Monster_Appear;
242                 
243                 return;
244         }
245         
246         self.scale = 1.3;
247         
248         if not (monster_initialize(
249                          "Mage",
250                          "models/monsters/mage.dpm",
251                          SHALRATH_MIN, SHALRATH_MAX,
252                          FALSE,
253                          shalrath_die, shalrath_spawn))
254         {
255                 remove(self);
256                 return;
257         }
258 }
259
260 // compatibility with old spawns
261 void spawnfunc_monster_vore () { spawnfunc_monster_shalrath(); }