]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/w_seeker.qc
now all projectile hit effects are clientside
[divverent/nexuiz.git] / data / qcsrc / server / w_seeker.qc
1 //.float speed; = switchweapon\r
2 //.float proxytime; = autoswitch\r
3 //.float tl; = wait\r
4 \r
5 void Seeker_Missile_Explode ()\r
6 {\r
7         self.event_damage = SUB_Null;\r
8         RadiusDamage (self, self.owner, cvar("g_balance_seeker_missile_damage"), cvar("g_balance_seeker_missile_edgedamage"), cvar("g_balance_seeker_missile_radius"), world, cvar("g_balance_seeker_missile_force"), self.projectiledeathtype, other);\r
9 \r
10         remove (self);\r
11 }\r
12 \r
13 void Seeker_Missile_Touch()\r
14 {\r
15         PROJECTILE_TOUCH;\r
16 \r
17         Seeker_Missile_Explode();\r
18 }\r
19 \r
20 void Seeker_Missile_Think()\r
21 {\r
22         entity e;\r
23         vector desireddir, olddir, newdir, eorg;\r
24         float turnrate;\r
25         float dist;\r
26 \r
27         if (time > self.cnt)\r
28                 Seeker_Missile_Explode();\r
29 \r
30         if (!self.switchweapon)\r
31                 self.switchweapon = cvar("g_balance_seeker_missile_speed");\r
32 \r
33         if ((self.switchweapon < cvar("g_balance_seeker_missile_speed_max")) && cvar("g_balance_seeker_missile_speed_accel"))\r
34                 self.switchweapon = self.switchweapon * cvar("g_balance_seeker_missile_accel");\r
35 \r
36         if (self.switchweapon > cvar("g_balance_seeker_missile_speed_max"))\r
37                 self.switchweapon = self.switchweapon * cvar("g_balance_seeker_missile_decel");\r
38 \r
39         if (self.enemy != world)\r
40                 if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO)\r
41                         self.enemy = world;\r
42 \r
43         if (self.enemy != world)\r
44         {\r
45                 e               = self.enemy;\r
46                 eorg            = 0.5 * (e.absmin + e.absmax);\r
47                 turnrate        = cvar("g_balance_seeker_missile_turnrate");                // how fast to turn\r
48                 desireddir      = normalize(eorg - self.origin);\r
49                 olddir          = normalize(self.velocity);                                         // get my current direction\r
50                 dist            = vlen(eorg - self.origin);\r
51 \r
52                 // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )\r
53                 if (cvar("g_balance_seeker_missile_smart") && (dist > cvar("g_balance_seeker_missile_smart_mindist")))\r
54                 {\r
55                         // Is it a better idea (shorter distance) to trace to the target itself?\r
56                         if ( vlen(self.origin + olddir * self.wait) < dist)\r
57                                 traceline(self.origin, self.origin + olddir * self.wait, FALSE, self);\r
58                         else\r
59                                 traceline(self.origin, eorg, FALSE, self);\r
60 \r
61                         // Setup adaptive tracelength\r
62                         self.wait = vlen(self.origin - trace_endpos);\r
63                         if (self.wait < cvar("g_balance_seeker_missile_smart_trace_min")) self.wait = cvar("g_balance_seeker_missile_smart_trace_min");\r
64                         if (self.wait > cvar("g_balance_seeker_missile_smart_trace_max")) self.wait = cvar("g_balance_seeker_missile_smart_trace_max");\r
65 \r
66                         // Calc how important it is that we turn and add this to the desierd (enemy) dir.\r
67                         desireddir  = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);\r
68                 }\r
69 \r
70                 //newdir = normalize((olddir + desireddir * turnrate) * 0.5);// take the average of the 2 directions; not the best method but simple & easy\r
71                 newdir = normalize(olddir + desireddir * turnrate);// take the average of the 2 directions; not the best method but simple & easy\r
72 \r
73                 self.velocity = newdir * self.switchweapon;                                         // make me fly in the new direction at my flight speed\r
74         }\r
75 \r
76         // Proxy\r
77         if (cvar("g_balance_seeker_missile_proxy"))\r
78         {\r
79                 if ( dist <= cvar("g_balance_seeker_missile_proxy_maxrange"))\r
80                 {\r
81                         if (self.autoswitch == 0)\r
82                         {\r
83                                 self.autoswitch = time + cvar("g_balance_seeker_missile_proxy_delay");\r
84                         }\r
85                         else\r
86                         {\r
87                                 if (self.autoswitch <= time)\r
88                                 {\r
89                                         Seeker_Missile_Explode();\r
90                                         self.autoswitch = 0;\r
91                                 }\r
92                         }\r
93                 }\r
94                 else\r
95                 {\r
96                         if (self.autoswitch != 0)\r
97                                 self.autoswitch = 0;\r
98                 }\r
99         }\r
100         ///////////////\r
101 \r
102         if (self.enemy.deadflag != DEAD_NO)\r
103         {\r
104                 self.enemy = world;\r
105                 self.cnt = time + 1 + (random() * 4);\r
106                 self.nextthink = self.cnt;\r
107                 return;\r
108         }\r
109 \r
110         self.angles = vectoangles(self.velocity);                       // turn model in the new flight direction\r
111         self.nextthink = time + 0.05;\r
112 \r
113         UpdateCSQCProjectile(self);\r
114 }\r
115 \r
116 \r
117 \r
118 void Seeker_Missile_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
119 {\r
120         float d;\r
121         d = damage;\r
122 \r
123         if (self.owner == attacker)\r
124                 d = d * 0.25;\r
125 \r
126         self.health = self.health - d;\r
127 \r
128         if (self.health <= 0)\r
129                 W_PrepareExplosionByDamage(attacker, Seeker_Missile_Explode);\r
130 }\r
131 \r
132 void Seeker_Missile_Animate()\r
133 {\r
134         self.frame = self.frame +1;\r
135         self.nextthink = time + 0.05;\r
136 \r
137         if (self.enemy != world)\r
138                 if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO)\r
139                         self.enemy = world;\r
140 \r
141         if(self.frame == 5)\r
142         {\r
143                 self.think           = Seeker_Missile_Think;\r
144                 self.nextthink       = time + cvar("g_balance_seeker_missile_activate_delay");\r
145                 self.effects         = EF_LOWPRECISION | EF_NOSHADOW;\r
146                 self.modelflags      = MF_ROCKET;\r
147 \r
148                 if (cvar("g_balance_seeker_guided_proxy"))\r
149                         self.movetype    = MOVETYPE_BOUNCEMISSILE;\r
150                 else\r
151                         self.movetype    = MOVETYPE_FLYMISSILE;\r
152         }\r
153 \r
154         UpdateCSQCProjectile(self);\r
155 }\r
156 \r
157 void Seeker_Fire_Missile(vector f_org)\r
158 {\r
159         local entity missile;\r
160 \r
161         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)\r
162                 self.ammo_rockets = self.ammo_rockets - cvar("g_balance_seeker_missile_ammo");\r
163 \r
164         makevectors(self.v_angle);\r
165         W_SetupShot (self, f_org, FALSE, 2, "weapons/seeker_fire.wav");\r
166         pointparticles(particleeffectnum("seeker_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);\r
167 \r
168         //self.detornator         = FALSE;\r
169 \r
170         missile                 = spawn();\r
171         missile.owner           = self;\r
172         missile.classname       = "seeker_missile";\r
173         missile.bot_dodge       = TRUE;\r
174         missile.bot_dodgerating = cvar("g_balance_seeker_missile_damage");\r
175 \r
176         missile.think           = Seeker_Missile_Animate;\r
177 \r
178         //if (!cvar("g_balance_seeker_missile_proxy"))\r
179         missile.touch           = Seeker_Missile_Touch;\r
180 \r
181         missile.event_damage    = Seeker_Missile_Damage;\r
182         missile.nextthink       = time + 0.2;// + cvar("g_balance_seeker_missile_activate_delay");\r
183         missile.cnt             = time + cvar("g_balance_seeker_missile_lifetime");\r
184         missile.enemy           = self.enemy;\r
185         missile.switchweapon           = cvar("g_balance_seeker_missile_speed");\r
186         missile.effects         = EF_LOWPRECISION | EF_NOSHADOW;\r
187         missile.solid           = SOLID_BBOX;\r
188         missile.scale           = 2;\r
189         missile.takedamage          = DAMAGE_YES;\r
190         missile.damageforcescale    = 4;\r
191         missile.health              = 5;\r
192         missile.projectiledeathtype = WEP_SEEKER;\r
193 \r
194         setorigin (missile, w_shotorg);\r
195         setmodel  (missile, "models/tagrocket.md3");\r
196         setsize (missile, '-2 -2 -2', '2 2 2');\r
197 \r
198 \r
199         missile.movetype    = MOVETYPE_FLYMISSILE;// MOVETYPE_TOSS;\r
200 \r
201         missile.flags       = FL_PROJECTILE;\r
202 \r
203         missile.velocity    = (w_shotdir + '0 0 0.45') * missile.switchweapon;\r
204         W_SetupProjectileVelocity(missile);\r
205 \r
206         missile.switchweapon = vlen(missile.velocity);\r
207         missile.angles = vectoangles (missile.velocity);\r
208 \r
209         CSQCProjectile(missile, FALSE, PROJECTILE_SEEKER);\r
210 }\r
211 \r
212 void Seeker_Vollycontroler_Think()\r
213 {\r
214         float c;\r
215         entity oldself,oldenemy;\r
216         self.cnt = self.cnt - 1;\r
217 \r
218         if ((self.owner.ammo_rockets < cvar("g_balance_seeker_missile_ammo")) || (self.cnt <= -1) || (self.owner.deadflag != DEAD_NO))\r
219         {\r
220                 remove(self);\r
221                 return;\r
222         }\r
223 \r
224         self.nextthink = time + cvar("g_balance_seeker_missile_delay");\r
225 \r
226         oldself = self;\r
227         self = self.owner;\r
228 \r
229         oldenemy = self.enemy;\r
230         self.enemy = oldself.enemy;\r
231 \r
232         c = mod(oldself.cnt, 4);\r
233         switch(c)\r
234         {\r
235                 case 0:\r
236                         Seeker_Fire_Missile('37.5 17 -22');\r
237                         break;\r
238                 case 1:\r
239                         Seeker_Fire_Missile('37.5 9.5 -22');\r
240                         break;\r
241                 case 2:\r
242                         Seeker_Fire_Missile('40 17 -29');\r
243                         break;\r
244                 case 3:\r
245                 default:\r
246                         Seeker_Fire_Missile('40 9.5 -29');\r
247                         break;\r
248         }\r
249 \r
250         self.enemy = oldenemy;\r
251         self = oldself;\r
252 }\r
253 \r
254 void Seeker_Tag_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
255 {\r
256         self.health = self.health - damage;\r
257         if (self.health <= 0)\r
258                 self.think ();\r
259 }\r
260 \r
261 void Seeker_Tag_Explode ()\r
262 {\r
263         vector  org2;\r
264         float b;\r
265 \r
266         //if(other==self.owner)\r
267         //    return;\r
268         Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE);\r
269 \r
270         remove (self);\r
271 }\r
272 \r
273 void Seeker_Tag_Think()\r
274 {\r
275         remove(self);\r
276         return;\r
277 }\r
278 \r
279 void Seeker_Tag_Touch()\r
280 {\r
281         vector dir;\r
282         vector org2;\r
283 \r
284         dir     = normalize (self.owner.origin - self.origin);\r
285         org2    = findbetterlocation (self.origin, 8);\r
286 \r
287         te_knightspike(org2);\r
288 \r
289         self.event_damage = SUB_Null;\r
290         Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_HEADSHOT);\r
291 \r
292         if (other.takedamage == DAMAGE_AIM && other.deadflag == DEAD_NO)\r
293         {\r
294                 entity e;\r
295                 e           = spawn();\r
296                 e.cnt       = cvar("g_balance_seeker_missile_count");\r
297                 e.owner     = self.owner;\r
298                 e.enemy     = other;\r
299                 e.think     = Seeker_Vollycontroler_Think;\r
300                 e.nextthink = time;\r
301 \r
302                 //sprint(self.owner, "^1Target lock ^3[^7 ",other.netname, " ^3]^1 acquired - autofire activated.\n");\r
303                 //sprint(other,"^1You are targeted!\n");\r
304 \r
305                 // stuffcmd(other,"play2 weapons/zany-alarm4.ogg\n");\r
306                 // stuffcmd(self.owner, "play2 weapons/zany-lock4.ogg\n");\r
307         }\r
308 \r
309         remove(self);\r
310         return;\r
311 }\r
312 \r
313 \r
314 \r
315 void Seeker_Fire_Tag()\r
316 {\r
317         local entity missile;\r
318         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)\r
319                 self.ammo_rockets = self.ammo_rockets - cvar("g_balance_seeker_tag_ammo");\r
320 \r
321         W_SetupShot (self, '56 13 -15', FALSE, 2, "weapons/tag_fire.wav");\r
322 \r
323         missile                 = spawn();\r
324         missile.owner           = self;\r
325         missile.classname       = "seeker_tag";\r
326         missile.bot_dodge       = TRUE;\r
327         missile.bot_dodgerating = 50;\r
328         missile.touch           = Seeker_Tag_Touch;\r
329         missile.think           = Seeker_Tag_Think;\r
330         missile.nextthink       = time + 15;\r
331         missile.movetype        = MOVETYPE_FLY;\r
332         missile.solid           = SOLID_BBOX;\r
333         missile.owner           = self;\r
334 \r
335         missile.takedamage       = DAMAGE_YES;\r
336         missile.event_damage    = Seeker_Tag_Explode;\r
337         missile.health          = 5;\r
338 \r
339         setmodel (missile, "models/laser.mdl"); // TODO make this seeker specific\r
340         setorigin (missile, w_shotorg);\r
341         setsize (missile, '0 0 0', '0 0 0');\r
342 \r
343         missile.effects     = EF_FULLBRIGHT | EF_NOSHADOW | EF_LOWPRECISION;\r
344         missile.modelflags  = MF_TRACER3;\r
345         //sound (missile, CHAN_PAIN, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM); // csqc\r
346         missile.flags       = FL_PROJECTILE;\r
347 \r
348         missile.velocity    = w_shotdir  * cvar("g_balance_seeker_tag_speed");\r
349         missile.movetype    = MOVETYPE_BOUNCEMISSILE;\r
350         W_SetupProjectileVelocity(missile);\r
351         missile.angles = vectoangles (missile.velocity);\r
352 \r
353         CSQCProjectile(missile, TRUE, PROJECTILE_TAG);\r
354 }\r
355 \r
356 \r
357 void Seeker_Flac_Explode ()\r
358 {\r
359         self.event_damage = SUB_Null;\r
360 \r
361         RadiusDamage (self, self.owner, cvar("g_balance_seeker_flac_damage"), cvar("g_balance_seeker_flac_edgedamage"), cvar("g_balance_seeker_flac_radius"), world, cvar("g_balance_seeker_flac_force"), self.projectiledeathtype, other);\r
362 \r
363         remove (self);\r
364 }\r
365 \r
366 void Seeker_Flac_Touch()\r
367 {\r
368         PROJECTILE_TOUCH;\r
369 \r
370         Seeker_Flac_Explode();\r
371 }\r
372 \r
373 void Seeker_Fire_Flac()\r
374 {\r
375         local entity missile;\r
376         vector f_org;\r
377         float c;\r
378 \r
379         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)\r
380                 self.ammo_rockets = self.ammo_rockets - cvar("g_balance_seeker_flac_ammo");\r
381 \r
382         c = mod(self.bulletcounter, 4);\r
383         switch(c)\r
384         {\r
385                 case 1:\r
386                         f_org = '37.5 17 -22';\r
387                         break;\r
388                 case 2:\r
389                         f_org = '37.5 9.5 -22';\r
390                         break;\r
391                 case 3:\r
392                         f_org = '40 17 -29';\r
393                         break;\r
394                 case 0:\r
395                 default:\r
396                         f_org = '40 9.5 -29';\r
397                         break;\r
398         }\r
399         W_SetupShot (self, f_org, FALSE, 2, "weapons/flac_fire.wav");\r
400 \r
401         pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);\r
402 \r
403         missile = spawn ();\r
404         missile.owner = missile.realowner = self;\r
405         missile.classname = "missile";\r
406         missile.bot_dodge = TRUE;\r
407         missile.bot_dodgerating = cvar("g_balance_seeker_flac_damage");\r
408         missile.touch = Seeker_Flac_Explode;\r
409         missile.use = Seeker_Flac_Explode;\r
410         missile.think = Seeker_Flac_Explode;\r
411         missile.nextthink = time + cvar("g_balance_seeker_flac_lifetime") + cvar("g_balance_seeker_flac_lifetime_rand");\r
412         missile.solid = SOLID_BBOX;\r
413         missile.scale = 0.4; // BUG: the model is too big\r
414         missile.projectiledeathtype = WEP_SEEKER;\r
415         setorigin (missile, w_shotorg);\r
416         setmodel (missile, "models/hagarmissile.mdl"); // precision set below\r
417         setsize (missile, '0 0 0', '0 0 0');\r
418         missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY;\r
419 \r
420         missile.effects = EF_LOWPRECISION;\r
421         missile.modelflags = MF_TRACER3;\r
422 \r
423         missile.movetype = MOVETYPE_FLY;\r
424         w_shotdir = w_shotdir + '0 0 0.3';\r
425         missile.velocity    = (w_shotdir  + randomvec() * cvar("g_balance_seeker_flac_spread")) * cvar("g_balance_seeker_flac_speed");\r
426 \r
427         W_SetupProjectileVelocity(missile);\r
428 \r
429         missile.angles = vectoangles (missile.velocity);\r
430         missile.flags = FL_PROJECTILE;\r
431 \r
432         CSQCProjectile(missile, TRUE, PROJECTILE_FLAC);\r
433 }\r
434 \r
435 void spawnfunc_weapon_seeker (void)\r
436 {\r
437         weapon_defaultspawnfunc(WEP_SEEKER);\r
438 }\r
439 \r
440 float w_seeker(float req)\r
441 {\r
442         if (req == WR_AIM)\r
443                 self.BUTTON_ATCK = bot_aim(cvar("g_balance_seeker_tag_speed"), 0, 20, FALSE);\r
444 \r
445         else if (req == WR_THINK)\r
446         {\r
447                 if (self.BUTTON_ATCK)\r
448                         if (weapon_prepareattack(0, cvar("g_balance_seeker_tag_refire")))\r
449                         {\r
450                                 Seeker_Fire_Tag();\r
451                                 weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_seeker_tag_animtime"), w_ready);\r
452                         }\r
453 \r
454                 if (self.BUTTON_ATCK2)\r
455                         if (weapon_prepareattack(1, cvar("g_balance_seeker_flac_refire")))\r
456                         {\r
457                                 Seeker_Fire_Flac();\r
458                                 weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_seeker_flac_animtime"), w_ready);\r
459                         }\r
460 \r
461         }\r
462         else if (req == WR_PRECACHE)\r
463         {\r
464                 precache_model ("models/hagarmissile.mdl");\r
465                 precache_model ("models/tagrocket.md3");\r
466                 precache_model ("models/weapons/g_seeker.md3");\r
467                 precache_model ("models/weapons/v_seeker.md3");\r
468                 precache_model ("models/weapons/w_seeker.zym");\r
469                 precache_sound ("weapons/tag_fire.wav");\r
470                 precache_sound ("weapons/tag_impact.wav");\r
471                 precache_sound ("weapons/tagexp1.wav");\r
472                 precache_sound ("weapons/tagexp2.wav");\r
473                 precache_sound ("weapons/tagexp3.wav");\r
474                 precache_sound ("weapons/tag_rocket_fly.wav");\r
475                 precache_sound ("weapons/flac_fire.wav");\r
476                 precache_sound ("weapons/flacexp1.wav");\r
477                 precache_sound ("weapons/flacexp2.wav");\r
478                 precache_sound ("weapons/flacexp3.wav");\r
479                 precache_sound ("weapons/seeker_fire.wav");\r
480                 precache_sound ("weapons/seekerexp1.wav");\r
481                 precache_sound ("weapons/seekerexp2.wav");\r
482                 precache_sound ("weapons/seekerexp3.wav");\r
483         }\r
484         else if (req == WR_SETUP)\r
485                 weapon_setup(WEP_SEEKER);\r
486         else if (req == WR_CHECKAMMO1)\r
487                 return self.ammo_rockets >= cvar("g_balance_seeker_tag_ammo") + cvar("g_balance_seeker_missile_ammo");\r
488         else if (req == WR_CHECKAMMO2)\r
489                 return self.ammo_rockets >= cvar("g_balance_seeker_flac_ammo");\r
490         else if (req == WR_SUICIDEMESSAGE)\r
491                 w_deathtypestring = "played with tiny rockets";\r
492         else if (req == WR_KILLMESSAGE)\r
493         {\r
494                 if(w_deathtype & HITTYPE_SECONDARY)\r
495                         w_deathtypestring = "ran into #'s flac";\r
496                 else\r
497                         w_deathtypestring = "was tagged by";\r
498         }\r
499         return TRUE;\r
500 };\r
501 \r