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