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