]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_crylink.qc
crylink: when a projectile explodes on world, not on player, don't detonate the other...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_crylink.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(CRYLINK, w_crylink, IT_CELLS, 6, WEP_FLAG_NORMAL | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_MID, "crylink", "crylink", "Crylink");
3 #else
4 #ifdef SVQC
5 .float gravity;
6
7 .entity queuenext;
8 .entity queueprev;
9
10 // force projectile to explode
11 void W_Crylink_LinkExplode (entity e, entity e2)
12 {
13         float a;
14         a = bound(0, 1 - (time - e.fade_time) * e.fade_rate, 1);
15
16         RadiusDamage (e, e.realowner, cvar("g_balance_crylink_primary_damage") * a, cvar("g_balance_crylink_primary_edgedamage") * a, cvar("g_balance_crylink_primary_radius"), world, cvar("g_balance_crylink_primary_force") * a, e.projectiledeathtype, other);
17
18         if(e.queuenext != e2)
19                 W_Crylink_LinkExplode(e.queuenext, e2);
20         remove (e);
21 }
22
23 // NO bounce protection, as bounces are limited!
24 void W_Crylink_Touch (void)
25 {
26         float finalhit;
27         float f;
28         //PROJECTILE_TOUCH;
29         local entity savenext, saveprev;
30         savenext = self.queuenext;
31         saveprev = self.queueprev;
32         if(WarpZone_Projectile_Touch())
33         {
34                 if(wasfreed(self))
35                 {
36                         savenext.queueprev = saveprev;
37                         saveprev.queuenext = savenext;
38                 }
39                 return;
40         }
41
42         float a;
43         a = bound(0, 1 - (time - self.fade_time) * self.fade_rate, 1);
44
45         finalhit = ((self.cnt <= 0) || (other.takedamage != DAMAGE_NO));
46         if(finalhit)
47                 f = 1;
48         else
49                 f = cvar("g_balance_crylink_primary_bouncedamagefactor");
50         if(a)
51                 f *= a;
52         if (RadiusDamage (self, self.realowner, cvar("g_balance_crylink_primary_damage") * f, cvar("g_balance_crylink_primary_edgedamage") * f, cvar("g_balance_crylink_primary_radius"), world, cvar("g_balance_crylink_primary_force") * f, self.projectiledeathtype, other) || finalhit)
53         {
54                 W_Crylink_LinkExplode(self.queuenext, self);
55                 remove (self);
56                 return;
57         }
58         self.cnt = self.cnt - 1;
59         self.angles = vectoangles(self.velocity);
60         self.owner = world;
61         self.projectiledeathtype |= HITTYPE_BOUNCE;
62         // commented out as it causes a little hitch...
63         //if(proj.cnt == 0)
64         //      CSQCProjectile(proj, TRUE, PROJECTILE_CRYLINK, TRUE);
65 }
66
67 void W_Crylink_Touch2 (void)
68 {
69         float finalhit;
70         float f;
71         //PROJECTILE_TOUCH;
72         local entity savenext, saveprev;
73         savenext = self.queuenext;
74         saveprev = self.queueprev;
75         if(WarpZone_Projectile_Touch())
76         {
77                 if(wasfreed(self))
78                 {
79                         savenext.queueprev = saveprev;
80                         saveprev.queuenext = savenext;
81                 }
82                 return;
83         }
84
85         float a;
86         a = 1 - (time - self.fade_time) * self.fade_rate;
87
88         finalhit = ((self.cnt <= 0) || (other.takedamage != DAMAGE_NO));
89         if(finalhit)
90                 f = 1;
91         else
92                 f = cvar("g_balance_crylink_secondary_bouncedamagefactor");
93         if(a)
94                 f *= a;
95         if (RadiusDamage (self, self.realowner, cvar("g_balance_crylink_secondary_damage") * f, cvar("g_balance_crylink_secondary_edgedamage") * f, cvar("g_balance_crylink_secondary_radius"), world, cvar("g_balance_crylink_secondary_force") * f, self.projectiledeathtype, other))
96         {
97                 W_Crylink_LinkExplode(self.queuenext, self);
98                 remove (self);
99                 return;
100         }
101         else if(finalhit)
102         {
103                 // just unlink
104                 self.queuenext.queueprev = self.queueprev;
105                 self.queueprev.queuenext = self.queuenext;
106                 remove(self);
107                 return;
108         }
109         self.cnt = self.cnt - 1;
110         self.angles = vectoangles(self.velocity);
111         self.owner = world;
112         self.projectiledeathtype |= HITTYPE_BOUNCE;
113         // commented out as it causes a little hitch...
114         //if(proj.cnt == 0)
115         //      CSQCProjectile(proj, TRUE, PROJECTILE_CRYLINK, TRUE);
116 }
117
118 void W_Crylink_Fadethink (void)
119 {
120         self.queuenext.queueprev = self.queueprev;
121         self.queueprev.queuenext = self.queuenext;
122         remove(self);
123 }
124
125 void W_Crylink_Attack (void)
126 {
127         local float counter, shots;
128         local entity proj, prevproj, firstproj;
129         local vector s;
130         vector forward, right, up;
131
132         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
133                 self.ammo_cells = self.ammo_cells - cvar("g_balance_crylink_primary_ammo");
134
135         W_SetupShot (self, FALSE, 2, "weapons/crylink_fire.wav", (cvar("g_balance_crylink_primary_damage")*cvar("g_balance_crylink_primary_shots")));
136         forward = v_forward;
137         right = v_right;
138         up = v_up;
139
140         shots = cvar("g_balance_crylink_primary_shots");
141         pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots);
142         while (counter < shots)
143         {
144                 proj = spawn ();
145                 proj.realowner = proj.owner = self;
146                 proj.classname = "spike";
147                 proj.bot_dodge = TRUE;
148                 proj.bot_dodgerating = cvar("g_balance_crylink_primary_damage");
149                 if(counter == 0) { // first projectile, store in firstproj for now
150                         firstproj = proj;
151                 }
152                 else if(counter == shots - 1) { // last projectile, link up with first projectile
153                         prevproj.queuenext = proj;
154                         firstproj.queueprev = proj;
155                         proj.queuenext = firstproj;
156                         proj.queueprev = prevproj;
157                 }
158                 else { // else link up with previous projectile
159                         prevproj.queuenext = proj;
160                         proj.queueprev = prevproj;
161                 }
162
163                 prevproj = proj;
164
165                 proj.movetype = MOVETYPE_BOUNCEMISSILE;
166                 PROJECTILE_MAKETRIGGER(proj);
167                 proj.projectiledeathtype = WEP_CRYLINK;
168                 //proj.gravity = 0.001;
169
170                 setorigin (proj, w_shotorg);
171                 setsize(proj, '0 0 0', '0 0 0');
172
173
174                 s = '0 0 0';
175                 if (counter == 0)
176                         s = '0 0 0';
177                 else
178                 {
179                         makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
180                         s_y = v_forward_x;
181                         s_z = v_forward_y;
182                 }
183                 s = s * cvar("g_balance_crylink_primary_spread") * g_weaponspreadfactor;
184                 W_SetupProjectileVelocityEx(proj, w_shotdir + right * s_y + up * s_z, v_up, cvar("g_balance_crylink_primary_speed"), 0, 0, 0);
185                 proj.touch = W_Crylink_Touch;
186
187                 proj.think = W_Crylink_Fadethink;
188                 if(counter == 0)
189                 {
190                         proj.fade_time = time + cvar("g_balance_crylink_primary_middle_lifetime");
191                         self.fade_rate = 1 / cvar("g_balance_crylink_primary_middle_fadetime");
192                         proj.nextthink = time + cvar("g_balance_crylink_primary_middle_lifetime") + cvar("g_balance_crylink_primary_middle_fadetime");
193                 }
194                 else if(counter <= 3)
195                 {
196                         proj.fade_time = time + cvar("g_balance_crylink_primary_star_lifetime");
197                         self.fade_rate = 1 / cvar("g_balance_crylink_primary_star_fadetime");
198                         proj.nextthink = time + cvar("g_balance_crylink_primary_star_lifetime") + cvar("g_balance_crylink_primary_star_fadetime");
199                 }
200                 else
201                 {
202                         proj.fade_time = time + cvar("g_balance_crylink_primary_other_lifetime");
203                         self.fade_rate = 1 / cvar("g_balance_crylink_primary_other_fadetime");
204                         proj.nextthink = time + cvar("g_balance_crylink_primary_other_lifetime") + cvar("g_balance_crylink_primary_other_fadetime");
205                 }
206                 proj.cnt = cvar("g_balance_crylink_primary_bounces");
207                 //proj.scale = 1 + 1 * proj.cnt;
208
209                 proj.angles = vectoangles (proj.velocity);
210
211                 //proj.glow_size = 20;
212
213                 proj.flags = FL_PROJECTILE;
214
215                 CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE);
216
217                 other = proj; MUTATOR_CALLHOOK(EditProjectile);
218
219                 counter = counter + 1;
220         }
221 }
222
223 void W_Crylink_Attack2 (void)
224 {
225         local float counter, shots;
226         local entity proj, prevproj, firstproj;
227
228         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
229                 self.ammo_cells = self.ammo_cells - cvar("g_balance_crylink_secondary_ammo");
230
231         W_SetupShot (self, FALSE, 2, "weapons/crylink_fire2.wav", (cvar("g_balance_crylink_secondary_damage")*cvar("g_balance_crylink_secondary_shots")));
232
233         shots = cvar("g_balance_crylink_secondary_shots");
234         pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots);
235         while (counter < shots)
236         {
237                 proj = spawn ();
238                 proj.realowner = proj.owner = self;
239                 proj.classname = "spike";
240                 proj.bot_dodge = TRUE;
241                 proj.bot_dodgerating = cvar("g_balance_crylink_secondary_damage");
242                 if(counter == 0) { // first projectile, store in firstproj for now
243                         firstproj = proj;
244                 }
245                 else if(counter == shots - 1) { // last projectile, link up with first projectile
246                         prevproj.queuenext = proj;
247                         firstproj.queueprev = proj;
248                         proj.queuenext = firstproj;
249                         proj.queueprev = prevproj;
250                 }
251                 else { // else link up with previous projectile
252                         prevproj.queuenext = proj;
253                         proj.queueprev = prevproj;
254                 }
255
256                 prevproj = proj;
257
258                 proj.movetype = MOVETYPE_BOUNCEMISSILE;
259                 PROJECTILE_MAKETRIGGER(proj);
260                 proj.projectiledeathtype = WEP_CRYLINK | HITTYPE_SECONDARY;
261                 //proj.gravity = 0.001;
262
263                 setorigin (proj, w_shotorg);
264                 setsize(proj, '0 0 0', '0 0 0');
265
266                 W_SetupProjectileVelocityEx(proj, (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * cvar("g_balance_crylink_secondary_spread") * g_weaponspreadfactor), v_up, cvar("g_balance_crylink_secondary_speed"), 0, 0, 0);
267                 proj.touch = W_Crylink_Touch2;
268                 proj.think = W_Crylink_Fadethink;
269                 if(counter == (shots - 1) / 2)
270                 {
271                         proj.fade_time = time + cvar("g_balance_crylink_secondary_middle_lifetime");
272                         self.fade_rate = 1 / cvar("g_balance_crylink_secondary_middle_fadetime");
273                         proj.nextthink = time + cvar("g_balance_crylink_secondary_middle_lifetime") + cvar("g_balance_crylink_secondary_middle_fadetime");
274                 }
275                 else
276                 {
277                         proj.fade_time = time + cvar("g_balance_crylink_secondary_line_lifetime");
278                         self.fade_rate = 1 / cvar("g_balance_crylink_secondary_line_fadetime");
279                         proj.nextthink = time + cvar("g_balance_crylink_secondary_line_lifetime") + cvar("g_balance_crylink_secondary_line_fadetime");
280                 }
281                 proj.cnt = cvar("g_balance_crylink_secondary_bounces");
282                 //proj.scale = 1 + 1 * proj.cnt;
283
284                 proj.angles = vectoangles (proj.velocity);
285
286                 //proj.glow_size = 20;
287
288                 proj.flags = FL_PROJECTILE;
289
290                 CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE);
291
292                 other = proj; MUTATOR_CALLHOOK(EditProjectile);
293
294                 counter = counter + 1;
295         }
296 }
297
298 void spawnfunc_weapon_crylink (void)
299 {
300         weapon_defaultspawnfunc(WEP_CRYLINK);
301 }
302
303 float w_crylink(float req)
304 {
305         if (req == WR_AIM)
306         {
307                 if (random() > 0.15)
308                         self.BUTTON_ATCK = bot_aim(cvar("g_balance_crylink_primary_speed"), 0, cvar("g_balance_crylink_primary_middle_lifetime"), FALSE);
309                 else
310                         self.BUTTON_ATCK2 = bot_aim(cvar("g_balance_crylink_secondary_speed"), 0, cvar("g_balance_crylink_secondary_middle_lifetime"), FALSE);
311         }
312         else if (req == WR_THINK)
313         {
314                 if (self.BUTTON_ATCK)
315                 if (weapon_prepareattack(0, cvar("g_balance_crylink_primary_refire")))
316                 {
317                         W_Crylink_Attack();
318                         weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_crylink_primary_animtime"), w_ready);
319                 }
320                 if (self.BUTTON_ATCK2 && cvar("g_balance_crylink_secondary"))
321                 if (weapon_prepareattack(1, cvar("g_balance_crylink_secondary_refire")))
322                 {
323                         W_Crylink_Attack2();
324                         weapon_thinkf(WFRAME_FIRE2, cvar("g_balance_crylink_secondary_animtime"), w_ready);
325                 }
326         }
327         else if (req == WR_PRECACHE)
328         {
329                 precache_model ("models/weapons/g_crylink.md3");
330                 precache_model ("models/weapons/v_crylink.md3");
331                 precache_model ("models/weapons/h_crylink.iqm");
332                 precache_sound ("weapons/crylink_fire.wav");
333                 precache_sound ("weapons/crylink_fire2.wav");
334         }
335         else if (req == WR_SETUP)
336                 weapon_setup(WEP_CRYLINK);
337         else if (req == WR_CHECKAMMO1)
338                 return self.ammo_cells >= cvar("g_balance_crylink_primary_ammo");
339         else if (req == WR_CHECKAMMO2)
340                 return self.ammo_cells >= cvar("g_balance_crylink_secondary_ammo");
341         return TRUE;
342 };
343 #endif
344 #ifdef CSQC
345 float w_crylink(float req)
346 {
347         if(req == WR_IMPACTEFFECT)
348         {
349                 vector org2;
350                 org2 = w_org + w_backoff * 2;
351                 if(w_deathtype & HITTYPE_SECONDARY)
352                 {
353                         pointparticles(particleeffectnum("crylink_impact"), org2, '0 0 0', 1);
354                         if(!w_issilent)
355                                 sound(self, CHAN_PROJECTILE, "weapons/crylink_impact2.wav", VOL_BASE, ATTN_NORM);
356                 }
357                 else
358                 {
359                         pointparticles(particleeffectnum("crylink_impactbig"), org2, '0 0 0', 1);
360                         if(!w_issilent)
361                                 sound(self, CHAN_PROJECTILE, "weapons/crylink_impact.wav", VOL_BASE, ATTN_NORM);
362                 }
363         }
364         else if(req == WR_PRECACHE)
365         {
366                 precache_sound("weapons/crylink_impact2.wav");
367                 precache_sound("weapons/crylink_impact.wav");
368         }
369         else if (req == WR_SUICIDEMESSAGE)
370         {
371                 w_deathtypestring = "%s succeeded at self-destructing themself with the Crylink";
372         }
373         else if (req == WR_KILLMESSAGE)
374         {
375                 if(w_deathtype & HITTYPE_BOUNCE)
376                         w_deathtypestring = "%s could not hide from %s's Crylink"; // unchecked: SPLASH (SECONDARY can't be)
377                 else if(w_deathtype & HITTYPE_SPLASH)
378                         w_deathtypestring = "%s was too close to %s's Crylink"; // unchecked: SECONDARY
379                 else
380                         w_deathtypestring = "%s took a close look at %s's Crylink"; // unchecked: SECONDARY
381         }
382         return TRUE;
383 }
384 #endif
385 #endif