]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/tturrets/system/system_main.qc
minor reordering of code in target selection, should run faster
[divverent/nexuiz.git] / data / qcsrc / server / tturrets / system / system_main.qc
1 #define cvar_base "g_turrets_unit_"\r
2 \r
3 //.float tur_lastscore;\r
4 .string cvar_basename;\r
5 \r
6 string cvar_gets(string s_base,string s_add)\r
7 {\r
8     return strcat(s_base,s_add);\r
9 }\r
10 \r
11 .float turret_scale_damage;\r
12 .float turret_scale_range;\r
13 .float turret_scale_refire;\r
14 .float turret_scale_ammo;\r
15 .float turret_scale_aim;\r
16 .float turret_scale_health;\r
17 .float turret_scale_respawn;\r
18 \r
19 void load_unit_settings(entity ent,string unitname,float is_reload)\r
20 {\r
21 \r
22     string sbase;\r
23 \r
24     if (ent == world)\r
25         return;\r
26 \r
27     if (!ent.turret_scale_damage)    ent.turret_scale_damage  = 1;\r
28     if (!ent.turret_scale_range)     ent.turret_scale_range   = 1;\r
29     if (!ent.turret_scale_refire)    ent.turret_scale_refire  = 1;\r
30     if (!ent.turret_scale_ammo)      ent.turret_scale_ammo    = 1;\r
31     if (!ent.turret_scale_aim)       ent.turret_scale_aim     = 1;\r
32     if (!ent.turret_scale_health)    ent.turret_scale_health  = 1;\r
33     if (!ent.turret_scale_respawn)   ent.turret_scale_respawn = 1;\r
34 \r
35     sbase = strcat(cvar_base,unitname);\r
36     if (is_reload)\r
37     {\r
38         ent.enemy = world;\r
39         ent.tur_head.avelocity = '0 0 0';\r
40         ent.tur_head.angles = ent.angles;\r
41     }\r
42     ent.health = cvar(cvar_gets(sbase,"_health")) * ent.turret_scale_health;\r
43     ent.respawntime = cvar(cvar_gets(sbase,"_respawntime")) * ent.turret_scale_respawn;\r
44 \r
45     ent.shot_dmg = cvar(cvar_gets(sbase,"_shot_dmg")) * ent.turret_scale_damage;\r
46     ent.shot_refire = cvar(cvar_gets(sbase,"_shot_refire")) * ent.turret_scale_refire;\r
47     ent.shot_radius = cvar(cvar_gets(sbase,"_shot_radius")) * ent.turret_scale_damage;\r
48     ent.shot_speed = cvar(cvar_gets(sbase,"_shot_speed"));\r
49     ent.shot_spread = cvar(cvar_gets(sbase,"_shot_spread"));\r
50     ent.shot_force = cvar(cvar_gets(sbase,"_shot_force")) * ent.turret_scale_damage;\r
51     ent.shot_volly = cvar(cvar_gets(sbase,"_shot_volly"));\r
52     ent.shot_volly_refire = cvar(cvar_gets(sbase,"_shot_volly_refire")) * ent.turret_scale_refire;\r
53 \r
54     ent.target_range = cvar(cvar_gets(sbase,"_target_range")) * ent.turret_scale_range;\r
55     ent.target_range_min = cvar(cvar_gets(sbase,"_target_range_min")) * ent.turret_scale_range;\r
56     ent.target_range_fire = cvar(cvar_gets(sbase,"_target_range_fire")) * ent.turret_scale_range;\r
57     ent.target_range_optimal = cvar(cvar_gets(sbase,"_target_range_optimal")) * ent.turret_scale_range;\r
58 \r
59     ent.target_select_rangebias = cvar(cvar_gets(sbase,"_target_select_rangebias"));\r
60     ent.target_select_samebias = cvar(cvar_gets(sbase,"_target_select_samebias"));\r
61     ent.target_select_anglebias = cvar(cvar_gets(sbase,"_target_select_anglebias"));\r
62     ent.target_select_playerbias = cvar(cvar_gets(sbase,"_target_select_playerbias"));\r
63 \r
64     ent.ammo_max = cvar(cvar_gets(sbase,"_ammo_max")) * ent.turret_scale_ammo;\r
65     //ent.ammo = cvar(cvar_gets(sbase,"_ammo"));\r
66     ent.ammo_recharge = cvar(cvar_gets(sbase,"_ammo_recharge")) * ent.turret_scale_ammo;\r
67 \r
68     ent.aim_firetolerance_dist = cvar(cvar_gets(sbase,"_aim_firetolerance_dist"));\r
69 //    ent.aim_firetolerance_angle = cvar(cvar_gets(sbase,"_aim_firetolerance_angle"));\r
70     ent.aim_speed = cvar(cvar_gets(sbase,"_aim_speed")) * ent.turret_scale_aim;\r
71     ent.aim_maxrot = cvar(cvar_gets(sbase,"_aim_maxrot"));\r
72     ent.aim_maxpitch = cvar(cvar_gets(sbase,"_aim_maxpitch"));\r
73 \r
74     ent.track_type = cvar(cvar_gets(sbase,"_track_type"));\r
75     ent.track_accel_pitch = cvar(cvar_gets(sbase,"_track_accel_pitch"));\r
76     ent.track_accel_rot = cvar(cvar_gets(sbase,"_track_accel_rot"));\r
77     ent.track_blendrate = cvar(cvar_gets(sbase,"_track_blendrate"));\r
78 }\r
79 \r
80 float turret_stdproc_true()\r
81 {\r
82     return 1;\r
83 }\r
84 \r
85 float turret_stdproc_false()\r
86 {\r
87     return 0;\r
88 }\r
89 \r
90 void turret_stdproc_nothing()\r
91 {\r
92     return;\r
93 }\r
94 \r
95 /**\r
96 ** updates enemy distances, preicted impact point/time\r
97 ** & aim<->predict impact distance.\r
98 ** Also translates shoot & aimorgs by current rotation.\r
99 **/\r
100 void turret_do_updates(entity e_turret)\r
101 {\r
102     if (self.turrcaps_flags & TFL_TURRCAPS_LINKED)\r
103     {\r
104         e_turret.tur_head.angles_x = e_turret.tur_head.angles_x * -1;\r
105         e_turret.angles_x = e_turret.angles_x * -1;\r
106         makevectors(e_turret.tur_head.angles + e_turret.angles);\r
107         e_turret.tur_head.angles_x = e_turret.tur_head.angles_x * -1;\r
108         e_turret.angles_x = e_turret.angles_x * -1;\r
109     }\r
110     else\r
111     {\r
112         e_turret.tur_head.angles_x = e_turret.tur_head.angles_x * -1;\r
113         makevectors(e_turret.tur_head.angles);\r
114         e_turret.tur_head.angles_x = e_turret.tur_head.angles_x * -1;\r
115     }\r
116     // v_right = (v_right * -1);\r
117     e_turret.tur_shotorg_updated = e_turret.origin + v_forward * e_turret.tur_shotorg_x + v_right * e_turret.tur_shotorg_y + v_up * e_turret.tur_shotorg_z;\r
118 \r
119     e_turret.tur_shotdir_updated = normalize((e_turret.tur_shotorg_updated + v_forward) - e_turret.tur_shotorg_updated);\r
120     e_turret.tur_aimorg_updated = e_turret.origin + v_forward * e_turret.tur_aimorg_x + v_right * e_turret.tur_aimorg_y + v_up * e_turret.tur_aimorg_z;\r
121 \r
122 \r
123     e_turret.tur_dist_enemy = vlen(e_turret.tur_aimorg_updated - real_origin(e_turret.enemy));\r
124 \r
125     traceline(e_turret.tur_aimorg_updated,e_turret.tur_aimorg_updated+(e_turret.tur_shotdir_updated * e_turret.tur_dist_enemy),MOVE_NORMAL,e_turret);\r
126 \r
127     e_turret.tur_impactpoint = trace_endpos;\r
128     e_turret.tur_impactent = trace_ent;\r
129     //e_turret.tur_impacttime = e_turret.tur_dist_enemy / e_turret.shot_speed;\r
130     e_turret.tur_impacttime = e_turret.tur_dist_enemy / e_turret.shot_speed;\r
131     e_turret.tur_dist_toaimpos = vlen(trace_endpos - e_turret.tur_aimpos);\r
132 }\r
133 \r
134 /**\r
135 ** Handles head rotation according to\r
136 ** the units .track_type and .track_flags\r
137 **/\r
138 void turret_stdproc_track()\r
139 {\r
140     vector wish_angle;  // This is where we'd need to be\r
141 \r
142     vector real_angle;  // This is where we can go\r
143     float f_tmp;\r
144 \r
145 \r
146     if (self.track_flags == TFL_TRACK_NO)\r
147         return;\r
148 \r
149     if(!self.tur_active)\r
150     {\r
151         wish_angle = self.idle_aim - ('1 0 0' * self.aim_maxpitch);\r
152     }\r
153     else if (self.enemy == world)\r
154     {\r
155         if (self.turrcaps_flags & TFL_TURRCAPS_LINKED)\r
156             wish_angle = self.idle_aim + self.angles;\r
157         else\r
158             if(cvar("g_turrets_aimidle_delay") > (time - self.lip))\r
159                 wish_angle = self.idle_aim;\r
160             else\r
161                 wish_angle = self.tur_head.angles;\r
162 \r
163     }\r
164     else\r
165     {\r
166         // Find the direction\r
167         if (self.turrcaps_flags & TFL_TURRCAPS_LINKED)\r
168             wish_angle = normalize(self.tur_aimpos - self.origin);\r
169         else\r
170             wish_angle = normalize(self.tur_aimpos - self.tur_head.origin);\r
171 \r
172         wish_angle = vectoangles(wish_angle); // And make a angle\r
173     }\r
174 \r
175     // Find the diffrence between where we currently aim and where we want to aim\r
176     if (self.turrcaps_flags & TFL_TURRCAPS_LINKED)\r
177         real_angle = wish_angle - (self.angles + self.tur_head.angles);\r
178     else\r
179     {\r
180         real_angle = wish_angle - self.tur_head.angles;\r
181 \r
182     }\r
183 \r
184     if(real_angle_x > self.tur_head.angles_x)\r
185     {\r
186         if(real_angle_x >= 180)\r
187             real_angle_x -= 360;\r
188     }\r
189     else\r
190     {\r
191         if(real_angle_x <= -180)\r
192             real_angle_x += 360;\r
193     }\r
194     if(real_angle_y > self.tur_head.angles_y)\r
195     {\r
196         if(real_angle_y >= 180)\r
197             real_angle_y -= 360;\r
198     }\r
199     else\r
200     {\r
201         if(real_angle_y <= -180)\r
202             real_angle_y += 360;\r
203     }\r
204 \r
205     // Limit pitch\r
206 \r
207     if (self.track_flags & TFL_TRACK_PITCH)\r
208         real_angle_x = bound(self.aim_maxpitch * -1,real_angle_x,self.aim_maxpitch);\r
209 \r
210     // Limit rot\r
211     if (self.track_flags & TFL_TRACK_ROT)\r
212         real_angle_y = bound(self.aim_maxrot * -1,real_angle_y,self.aim_maxrot);\r
213 \r
214 \r
215     if (self.track_type == TFL_TRACKTYPE_STEPMOTOR)\r
216     {\r
217         f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic\r
218 \r
219         // Limit turning speed\r
220         real_angle_x = bound((-1 * f_tmp),real_angle_x, f_tmp);\r
221         real_angle_y = bound((-1 * f_tmp),real_angle_y, f_tmp);\r
222 \r
223         // Limit pich and rot.\r
224         if (self.track_flags & TFL_TRACK_PITCH)\r
225             self.tur_head.angles_x = bound((-1 * self.aim_maxpitch),self.tur_head.angles_x + real_angle_x,self.aim_maxpitch);\r
226 \r
227         if (self.track_flags & TFL_TRACK_ROT)\r
228             self.tur_head.angles_y = bound((-1 * self.aim_maxrot),self.tur_head.angles_y  + real_angle_y,self.aim_maxrot);\r
229 \r
230         return;\r
231     }\r
232 \r
233     if (self.track_type == TFL_TRACKTYPE_FLUIDPRECISE)\r
234     {\r
235         if (self.track_flags & TFL_TRACK_PITCH)\r
236             self.tur_head.avelocity_x = real_angle_x;\r
237 \r
238         if (self.track_flags & TFL_TRACK_ROT)\r
239             self.tur_head.avelocity_y = real_angle_y;\r
240     }\r
241     else if (self.track_type == TFL_TRACKTYPE_FLUIDINERTIA)\r
242     {\r
243         f_tmp = self.aim_speed * self.ticrate;\r
244 \r
245         real_angle_y = bound(self.aim_speed * -1,real_angle_y * self.track_accel_rot * f_tmp,self.aim_speed);\r
246         real_angle_x = bound(self.aim_speed * -1,real_angle_x * self.track_accel_pitch * f_tmp,self.aim_speed);\r
247         real_angle = (self.tur_head.avelocity * self.track_blendrate) + (real_angle * (1 - self.track_blendrate));\r
248 \r
249         if (self.track_flags & TFL_TRACK_PITCH) self.tur_head.avelocity_x = real_angle_x;\r
250         if (self.track_flags & TFL_TRACK_ROT)   self.tur_head.avelocity_y = real_angle_y;\r
251         self.tur_head.avelocity_z = real_angle_z;\r
252     }\r
253 \r
254     // Limit pitch\r
255     /*\r
256     if (self.track_flags & TFL_TRACK_PITCH)\r
257     {\r
258         if (self.tur_head.angles_x > self.aim_maxpitch)\r
259         {\r
260             self.tur_head.angles_x = self.aim_maxpitch;\r
261             self.tur_head.avelocity_x = 0;\r
262         }\r
263         else if (self.tur_head.angles_x < (self.aim_maxpitch * -1))\r
264         {\r
265             self.tur_head.angles_x = (self.aim_maxpitch * -1);\r
266             self.tur_head.avelocity_x = 0;\r
267         }\r
268     }\r
269 \r
270     // Limit rot\r
271     if (self.track_flags & TFL_TRACK_ROT)\r
272     {\r
273         if (self.tur_head.angles_y > self.aim_maxrot)\r
274         {\r
275             self.tur_head.angles_y = self.aim_maxrot;\r
276             self.tur_head.avelocity_y = 0;\r
277         }\r
278         else if (self.tur_head.angles_y < (self.aim_maxrot * -1))\r
279         {\r
280             self.tur_head.angles_y = (self.aim_maxrot * -1);\r
281             self.tur_head.avelocity_y = 0;\r
282         }\r
283     }\r
284     */\r
285 \r
286 \r
287 }\r
288 \r
289 /*\r
290  + = implemented\r
291  - = not implemented\r
292 \r
293  + TFL_FIRECHECK_NO\r
294  + TFL_FIRECHECK_WORLD\r
295  + TFL_FIRECHECK_DEAD\r
296  + TFL_FIRECHECK_DISTANCES\r
297  - TFL_FIRECHECK_LOS\r
298  + TFL_FIRECHECK_AIMDIST\r
299  + TFL_FIRECHECK_REALDIST\r
300  - TFL_FIRECHECK_ANGLEDIST\r
301  - TFL_FIRECHECK_TEAMCECK\r
302  + TFL_FIRECHECK_AFF\r
303  + TFL_FIRECHECK_OWM_AMMO\r
304  + TFL_FIRECHECK_OTHER_AMMO\r
305  + TFL_FIRECHECK_REFIRE\r
306 */\r
307 \r
308 /**\r
309 ** Preforms pre-fire checks based on the uints firecheck_flags\r
310 **/\r
311 float turret_stdproc_firecheck()\r
312 {\r
313     // This one just dont care =)\r
314     if (self.firecheck_flags & TFL_FIRECHECK_NO) return 1;\r
315 \r
316     // Ready?\r
317     if (self.firecheck_flags & TFL_FIRECHECK_REFIRE)\r
318         if (self.attack_finished_single >= time) return 0;\r
319 \r
320     //\r
321     if (self.firecheck_flags & TFL_FIRECHECK_DEAD)\r
322         if (self.enemy.deadflag != DEAD_NO) return 0;\r
323 \r
324     // Plz stop killing the world!\r
325     if (self.firecheck_flags & TFL_FIRECHECK_WORLD)\r
326         if (self.enemy == world) return 0;\r
327 \r
328     // Own ammo?\r
329     if (self.firecheck_flags & TFL_FIRECHECK_OWM_AMMO)\r
330         if (self.ammo < self.shot_dmg) return 0;\r
331 \r
332     // Other's ammo? (carefull using this...)\r
333     if (self.firecheck_flags & TFL_FIRECHECK_OTHER_AMMO)\r
334         if (self.enemy.ammo >= self.enemy.ammo_max) return 0;\r
335 \r
336     if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES)\r
337     {\r
338         // Not close enougth?\r
339         if (self.tur_dist_enemy > self.target_range_fire) return 0;\r
340 \r
341         // To close?\r
342         if (self.tur_dist_enemy < self.target_range_min) return 0;\r
343     }\r
344 \r
345     // Try to avoid FF?\r
346     if (self.firecheck_flags & TFL_FIRECHECK_AFF)\r
347         if (self.tur_impactent.team == self.team) return 0;\r
348 \r
349     // aim<->predicted impact\r
350     if (self.firecheck_flags & TFL_FIRECHECK_AIMDIST)\r
351         if (self.tur_dist_toaimpos  > self.aim_firetolerance_dist) return 0;\r
352 \r
353     // Volly status\r
354     if (self.shot_volly > 1)\r
355     {\r
356         if (self.volly_counter == self.shot_volly)\r
357             if (self.ammo < (self.shot_dmg * self.shot_volly))\r
358                 return 0;\r
359     }\r
360 \r
361     //if(self.tur_enemy_adist >= self.aim_firetolerance) return 0;\r
362 \r
363 \r
364     return 1;\r
365 }\r
366 \r
367 /*\r
368  + TFL_TARGETSELECT_NO\r
369  + TFL_TARGETSELECT_LOS\r
370  + TFL_TARGETSELECT_PLAYERS\r
371  + TFL_TARGETSELECT_MISSILES\r
372  - TFL_TARGETSELECT_TRIGGERTARGET\r
373  + TFL_TARGETSELECT_ANGLELIMITS\r
374  + TFL_TARGETSELECT_RANGELIMTS\r
375  + TFL_TARGETSELECT_TEAMCHECK\r
376  - TFL_TARGETSELECT_NOBUILTIN\r
377  + TFL_TARGETSELECT_OWNTEAM\r
378 */\r
379 \r
380 /**\r
381 ** Evaluate a entity for target valitity based on validate_flags\r
382 **/\r
383 float turret_validate_target(entity e_turret,entity e_target,float validate_flags)\r
384 {\r
385     vector v_tmp;\r
386 \r
387     //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN)\r
388     //    return -0.5;\r
389 \r
390     if (!e_target)// == world)\r
391         return -1;\r
392 \r
393     if (e_target.classname == "grapplinghook")\r
394         return - 1.5;\r
395 \r
396         if(g_onslaught)\r
397                 if (substring(e_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job!\r
398                         return - 1.75;\r
399 \r
400     if (validate_flags & TFL_TARGETSELECT_NO)\r
401         return -2;\r
402 \r
403     // If only this was used more..\r
404     if (e_target.flags & FL_NOTARGET)\r
405         return -3;\r
406 \r
407     // Cant touch this\r
408     if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))\r
409         return -4;\r
410 \r
411     // player\r
412     if (e_target.flags & FL_CLIENT)\r
413     {\r
414         if (!(validate_flags & TFL_TARGETSELECT_PLAYERS))\r
415             return -5;\r
416 \r
417         if (e_target.deadflag != DEAD_NO)\r
418             return -6;\r
419     }\r
420 \r
421         // enemy turrets\r
422         if (e_target.turret_firefunc || e_target.owner.tur_head == e_target)\r
423         {\r
424                 if (validate_flags & TFL_TARGETSELECT_NOTURRETS)\r
425                         return -5.5;\r
426         }\r
427 \r
428 \r
429     // Missile\r
430     if (e_target.flags & FL_PROJECTILE)\r
431     {\r
432         if (!(validate_flags & TFL_TARGETSELECT_MISSILES))\r
433             return -7;\r
434     }\r
435 \r
436     // Team check\r
437     if (validate_flags & TFL_TARGETSELECT_TEAMCHECK)\r
438     {\r
439         if (validate_flags & TFL_TARGETSELECT_OWNTEAM)\r
440         {\r
441             if (e_target.team != e_turret.team)\r
442                 return -8;\r
443 \r
444             if (e_turret.team != e_target.owner.team)\r
445                 return -8.5;\r
446         }\r
447         else\r
448         {\r
449             if (e_target.team == e_turret.team)\r
450                 return -9;\r
451 \r
452             if (e_turret.team == e_target.owner.team)\r
453                 return -9.5;\r
454         }\r
455     }\r
456 \r
457     // Range limits?\r
458     tvt_dist = vlen(e_turret.origin - real_origin(e_target));\r
459     if (validate_flags & TFL_TARGETSELECT_RANGELIMTS)\r
460     {\r
461         if (tvt_dist < e_turret.target_range_min)\r
462             return -13;\r
463 \r
464         if (tvt_dist > e_turret.target_range)\r
465             return -14;\r
466     }\r
467 \r
468     // Can we even aim this thing?\r
469     tvt_thadv = angleofs(e_turret.tur_head,e_target);\r
470     tvt_tadv  = angleofs(e_turret,e_target);\r
471     tvt_thadf = vlen(tvt_thadv);\r
472     tvt_tadf  = vlen(tvt_tadv);\r
473 \r
474     if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS)\r
475     {\r
476         if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch)\r
477             return -11;\r
478 \r
479         if (fabs(tvt_tadv_y) > e_turret.aim_maxrot)\r
480             return -12;\r
481     }\r
482 \r
483     // Line of sight?\r
484     if (validate_flags & TFL_TARGETSELECT_LOS)\r
485     {\r
486         v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5);\r
487         //v_tmp = e_target.origin;\r
488         traceline(e_turret.origin + e_turret.tur_aimorg,v_tmp,0,e_turret);\r
489 \r
490         if (e_turret.aim_firetolerance_dist < vlen(v_tmp - trace_endpos))\r
491             return -10;\r
492     }\r
493 \r
494 #ifdef TURRET_DEBUG_TARGETSELECT\r
495     bprint("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n");\r
496 #endif\r
497 \r
498     return 1;\r
499 }\r
500 \r
501 entity turret_select_target()\r
502 {\r
503     entity e;        // target looper entity\r
504     float  score;    // target looper entity score\r
505     entity e_enemy;  // currently best scoreing target\r
506     float  m_score;  // currently best scoreing target's score\r
507     float f;\r
508 \r
509     m_score = 0;\r
510     if(self.enemy)\r
511     if(turret_validate_target(self,e,self.target_select_flags) > 0)\r
512     {\r
513         e_enemy = self.enemy;\r
514         m_score = self.turret_score_target(self,e_enemy) * self.target_select_samebias;\r
515     }\r
516 \r
517     e = findradius(self.origin,self.target_range);\r
518 \r
519     // Nothing to aim at?\r
520     if (!e) return world;\r
521 \r
522     while (e)\r
523     {\r
524         f = turret_validate_target(self,e,self.target_select_flags);\r
525         if (f > 0)\r
526         {\r
527             score = self.turret_score_target(self,e);\r
528             if ((score > m_score) && (score > 0))\r
529             {\r
530                 e_enemy = e;\r
531                 m_score = score;\r
532             }\r
533         }\r
534         e = e.chain;\r
535     }\r
536 \r
537     return e_enemy;\r
538 }\r
539 \r
540 void turret_think()\r
541 {\r
542     entity e;\r
543 \r
544     self.nextthink = (time + self.ticrate);\r
545 \r
546     // ONS uses somewhat backwards linking.\r
547     if (teamplay)\r
548     {\r
549         if (g_onslaught)\r
550         {\r
551 \r
552         }\r
553         else\r
554         {\r
555             if (self.target)\r
556             {\r
557                 e = find(world,targetname,self.target);\r
558                 if (e != world)\r
559                     self.team = e.team;\r
560             }\r
561         }\r
562 \r
563         if (self.team != self.tur_head.team)\r
564             turret_stdproc_respawn();\r
565     }\r
566 \r
567 \r
568     if (cvar("g_turrets_reloadcvars") == 1)\r
569     {\r
570         e = nextent(world);\r
571         while (e)\r
572         {\r
573             if (e.tur_head != world)\r
574             {\r
575 \r
576                 load_unit_settings(e,e.cvar_basename,1);\r
577                 e.turret_postthink();\r
578             }\r
579 \r
580             e = nextent(e);\r
581         }\r
582 \r
583         cvar_set("g_turrets_reloadcvars","0");\r
584     }\r
585 \r
586 #ifdef TURRET_DEBUG\r
587     if (self.tur_dbg_tmr1 < time)\r
588     {\r
589         if (self.enemy) paint_target (self.enemy,128,self.tur_dbg_rvec,0.9);\r
590         paint_target(self,256,self.tur_dbg_rvec,0.9);\r
591         self.tur_dbg_tmr1 = time + 1;\r
592     }\r
593 #endif\r
594 \r
595     //Do custom prethink, and bail if it fails.\r
596     //if (!self.turret_prethink()) return;\r
597 \r
598     // Handle ammo\r
599     if (self.ammo < self.ammo_max)\r
600         self.ammo = min(self.ammo + self.ammo_recharge,self.ammo_max);\r
601 \r
602 \r
603     // Inactive turrets needs to run the think loop too\r
604     // So they can handle animation and wake up if need be.\r
605     if(!self.tur_active)\r
606     {\r
607         turret_stdproc_track();\r
608         return;\r
609     }\r
610 \r
611     //This is just wrong :|\r
612     if(self.deadflag != DEAD_NO)\r
613     {\r
614         dprint("Warning:dead turret running the think function!\n");\r
615         //self.enemy = world;\r
616         //turret_stdproc_track();\r
617         return;\r
618     }\r
619 \r
620     // This is typicaly used for zaping every target in range\r
621     // turret_fusionreactor uses this to recharge friendlys.\r
622     if (self.shoot_flags & TFL_SHOOT_HITALLVALID)\r
623     {\r
624 \r
625         // Do a self.turret_fire for every valid target.\r
626         e = findradius(self.origin,self.target_range);\r
627 \r
628         while (e)\r
629         {\r
630             if (turret_validate_target(self,e,self.target_validate_flags))\r
631             {\r
632                 self.enemy = e;\r
633 \r
634                 turret_do_updates(self);\r
635 \r
636                 if ( self.turret_firecheckfunc() ) turret_fire();\r
637             }\r
638 \r
639             e = e.chain;\r
640         }\r
641         self.enemy = world;\r
642 \r
643     }\r
644     else\r
645     {\r
646         // Check if we have a vailid enemy, and try to find one if we dont.\r
647         if ((turret_validate_target(self,self.enemy,self.target_validate_flags) <= 0) || (self.cnt < time))\r
648         {\r
649             self.enemy = turret_select_target();\r
650             self.cnt = time + cvar("g_turrets_targetscan_mindelay");\r
651         }\r
652 \r
653 \r
654         // No target, just go to idle, do any custom stuff and bail.\r
655         if (self.enemy == world)\r
656         {\r
657             // Turn & pitch\r
658             if (!self.track_flags & TFL_TRACK_NO)\r
659                 turret_stdproc_track();\r
660 \r
661             // do any per-turret stuff\r
662             self.turret_postthink();\r
663 \r
664             // And bail.\r
665             return;\r
666         }\r
667         else\r
668             self.lip = time; // Keep track of the last time we had a target.\r
669 \r
670         turret_do_updates(self);\r
671 \r
672         // Predict or whatnot\r
673         if not((self.aim_flags & TFL_AIM_NO))\r
674             self.tur_aimpos = turret_stdproc_aim_generic();\r
675 \r
676         // Fire?\r
677         if (self.turret_firecheckfunc() != 0)\r
678             turret_fire();\r
679 \r
680         turret_do_updates(self);\r
681 \r
682         // Turn & pitch\r
683         if (!self.track_flags & TFL_TRACK_NO)\r
684             turret_stdproc_track();\r
685 \r
686         turret_do_updates(self);\r
687 \r
688         // Fire?\r
689         if (self.turret_firecheckfunc() != 0)\r
690             turret_fire();\r
691     }\r
692 \r
693     // do any per-turret stuff\r
694     self.turret_postthink();\r
695 }\r
696 \r
697 void turret_fire()\r
698 {\r
699     if (cvar("g_turrets_nofire") != 0)   return;\r
700     if ((!self.tur_active) || (self.deadflag != DEAD_NO)) return;\r
701 \r
702     self.turret_firefunc();\r
703 \r
704     self.attack_finished_single    = time + self.shot_refire;\r
705     self.ammo               = self.ammo - self.shot_dmg;\r
706     self.volly_counter      = self.volly_counter - 1;\r
707 \r
708 \r
709     if (self.volly_counter <= 0)\r
710     {\r
711         self.volly_counter = self.shot_volly;\r
712         if (self.shoot_flags & TFL_SHOOT_CLEARTARGET) self.enemy = world;\r
713 \r
714         if (self.shot_volly > 1)\r
715             self.attack_finished_single = time + self.shot_volly_refire;\r
716     }\r
717 \r
718 \r
719 #ifdef TURRET_DEBUG\r
720     if (self.enemy) paint_target3(self.tur_aimpos ,64,self.tur_dbg_rvec,self.tur_impacttime+0.25);\r
721 #endif\r
722 }\r
723 \r
724 void turret_stdproc_fire()\r
725 {\r
726     dprint("^1Bang, ^3your dead^7 ",self.enemy.netname,"! ^1(turret with no real firefunc)\n");\r
727 }\r
728 \r
729 /*\r
730     When .used a turret switched team to activator.team.\r
731     If activator is world, the turrets goes inactive.\r
732 */\r
733 void turret_stdproc_use()\r
734 {\r
735      //bprint("Used:",self.netname, " By ",other.classname,"\n");\r
736 \r
737     self.team = activator.team;\r
738 \r
739     if(self.team == 0)\r
740         self.tur_active = 0;\r
741     else\r
742         self.tur_active = 1;\r
743 \r
744 }\r
745 \r
746 /*\r
747 * Standard turret initialization. use this!\r
748 * (unless you have a very good reason not to.)\r
749 * Any special stuff like multiple cannon models should be done\r
750 * after this is proc called.\r
751 * if the return value is 0, the turret should be removed.\r
752 */\r
753 float turret_stdproc_init (string cvar_base_name)\r
754 {\r
755         entity e;\r
756 \r
757     // Are turrets allowed atm?\r
758     if (cvar("g_turrets") == 0) return 0;\r
759 \r
760     // Better more then once then never.\r
761     // turret_gibs_precash();\r
762 \r
763     if (self.spawnflags & 2)\r
764     {\r
765         entity tb;\r
766         precache_model("models/turrets/terrainbase.md3");\r
767         tb = spawn();\r
768         setmodel(tb,"models/turrets/terrainbase.md3");\r
769         setorigin(tb,self.origin);\r
770         tb.solid = SOLID_BBOX;\r
771         makestatic(tb);\r
772     }\r
773 \r
774     self.cvar_basename = cvar_base_name;\r
775     load_unit_settings(self,self.cvar_basename,0);\r
776 \r
777     // Group all turrets into the same team if in non teamplaymode, so they dont try to kill eachother.\r
778     if (cvar("g_assult") != 0)\r
779     {\r
780         if (!self.team)\r
781             self.team = 14; // Assume turrets are on the defending side if not explicitly set otehrwize\r
782     }\r
783     else if (!teamplay)\r
784                 self.team = MAX_SHOT_DISTANCE;\r
785         else if(g_onslaught && self.targetname)\r
786         {\r
787                 e = find(world,target,self.targetname);\r
788                 if(e != world)\r
789                         self.team = e.team;\r
790         }\r
791         else if(!self.team)\r
792                 self.team = MAX_SHOT_DISTANCE;\r
793 \r
794 \r
795     /*\r
796     * Try to guess some reasonaly defaults\r
797     * for missing params and do sanety checks\r
798     * thise checks could produce some "interesting" results\r
799     * if it hits a glitch in my logic :P so try to set as mutch\r
800     * as possible beforehand.\r
801     */\r
802     if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT)\r
803     {\r
804         // Support units generaly dont need to have a high speed ai-loop\r
805         if (!self.ticrate) self.ticrate = 0.25;     // Speed of this turrets AI loop\r
806     }\r
807     else\r
808     {\r
809         if (!self.ticrate) self.ticrate = 0.1;     // Speed of this turrets AI loop\r
810     }\r
811 \r
812     self.ticrate = bound(0.01,self.ticrate,60);  // keep it sane plz\r
813 \r
814 // General stuff\r
815     if (self.netname == "")  self.netname        = "turret";\r
816 \r
817     if (!self.respawntime) self.respawntime = 60;\r
818     self.respawntime = max(-1,self.respawntime);\r
819 \r
820     if (!self.health)        self.health         = 1000;\r
821     self.tur_health = max(1,self.health);\r
822 \r
823     if (!self.turrcaps_flags) self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL;\r
824 \r
825     if (!self.damage_flags) self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE;\r
826 \r
827 // Shot stuff.\r
828     if (!self.shot_refire) self.shot_refire     = 1;\r
829     self.shot_refire = bound(0.01,self.shot_refire,9999);\r
830 \r
831     if (!self.shot_dmg) self.shot_dmg        = self.shot_refire * 50;\r
832     self.shot_dmg = max(1,self.shot_dmg);\r
833 \r
834     if (!self.shot_radius) self.shot_radius     = self.shot_dmg * 0.5;\r
835     self.shot_radius = max(1,self.shot_radius);\r
836 \r
837     if (!self.shot_speed) self.shot_speed      = 2500;\r
838     self.shot_speed = max(1,self.shot_speed);\r
839 \r
840     if (!self.shot_spread) self.shot_spread     = 0.0125;\r
841     self.shot_spread = bound(0.0001,self.shot_spread,500);\r
842 \r
843     if (!self.shot_force) self.shot_force      = self.shot_dmg * 0.5 + self.shot_radius * 0.5;\r
844     self.shot_force = bound(0.001,self.shot_force,MAX_SHOT_DISTANCE * 0.5);\r
845 \r
846     if (!self.shot_volly) self.shot_volly = 1;\r
847     self.shot_volly = bound(1,self.shot_volly,floor(self.ammo_max / self.shot_dmg));\r
848 \r
849     if (!self.shot_volly_refire) self.shot_volly_refire = self.shot_refire * self.shot_volly;\r
850     self.shot_volly_refire = bound(self.shot_refire,self.shot_volly_refire,60);\r
851 \r
852     if (!self.firecheck_flags)\r
853         self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES |\r
854                                TFL_FIRECHECK_LOS | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCECK |\r
855                                TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_WORLD;\r
856 \r
857 // Range stuff.\r
858     if (!self.target_range) self.target_range               = self.shot_speed * 0.5;\r
859     self.target_range = bound(0,self.target_range,MAX_SHOT_DISTANCE);\r
860 \r
861     if (!self.target_range_min)          self.target_range_min           = self.shot_radius * 2;\r
862     self.target_range_min = bound(0,self.target_range_min,MAX_SHOT_DISTANCE);\r
863 \r
864     if (!self.target_range_fire)         self.target_range_fire          = self.target_range * 0.8;\r
865     self.target_range_fire = bound(0,self.target_range_fire,MAX_SHOT_DISTANCE);\r
866 \r
867     if (!self.target_range_optimal)      self.target_range_optimal       = self.target_range_fire * 0.5;\r
868     self.target_range_optimal = bound(0,self.target_range_optimal,MAX_SHOT_DISTANCE);\r
869 \r
870 \r
871 // Aim stuff.\r
872     if (!self.aim_maxrot)    self.aim_maxrot    = 45;\r
873     self.aim_maxrot = bound(0,self.aim_maxrot,361);\r
874 \r
875     if (!self.aim_maxpitch)  self.aim_maxpitch  = 20;\r
876     self.aim_maxpitch = bound(0,self.aim_maxpitch,90);\r
877 \r
878     if (!self.aim_speed)     self.aim_speed     = 36;\r
879     self.aim_speed  = bound(0.1,self.aim_speed, 1000);\r
880 \r
881     if (!self.aim_firetolerance_dist)     self.aim_firetolerance_dist  = 5 + (self.shot_radius * 2);\r
882     self.aim_firetolerance_dist = bound(0.1,self.aim_firetolerance_dist,MAX_SHOT_DISTANCE);\r
883 \r
884 //    if (!self.aim_firetolerance_angle)     self.aim_firetolerance_angle  = 10;\r
885 //    self.aim_firetolerance_angle = bound(0.1,self.aim_firetolerance_angle,360);\r
886 \r
887     if (!self.aim_flags) self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_ZEASE;\r
888 \r
889     // Sill the most tested (and aim-effective)\r
890     if (!self.track_type) self.track_type = TFL_TRACKTYPE_STEPMOTOR;\r
891 \r
892     if (self.track_type != TFL_TRACKTYPE_STEPMOTOR)\r
893     {\r
894         // Fluid / Ineria mode. Looks mutch nicer, bit experimental &\r
895         // Can inmapt aim preformance alot.\r
896         // needs a bit diffrent aimspeed\r
897         if (!self.aim_speed) self.aim_speed = 180;\r
898         self.aim_speed  = bound(0.1,self.aim_speed, 1000);\r
899 \r
900         if (!self.track_accel_pitch) self.track_accel_pitch = 0.75;\r
901         if (!self.track_accel_rot)   self.track_accel_rot   = 0.75;\r
902         if (!self.track_blendrate)   self.track_blendrate   = 0.35;\r
903     }\r
904 \r
905     if (!self.track_flags) self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROT;\r
906 \r
907 \r
908 // Target selection stuff.\r
909     if (!self.target_select_rangebias)   self.target_select_rangebias     = 1;\r
910     self.target_select_rangebias = bound(-10,self.target_select_rangebias,10);\r
911 \r
912     if (!self.target_select_samebias)    self.target_select_samebias      = 1;\r
913     self.target_select_samebias = bound(-10,self.target_select_samebias,10);\r
914 \r
915     if (!self.target_select_anglebias)   self.target_select_anglebias     = 1;\r
916     self.target_select_anglebias = bound(-10,self.target_select_anglebias,10);\r
917 \r
918     if (!self.target_select_missilebias)   self.target_select_missilebias = -10;\r
919     self.target_select_missilebias = bound(-10,self.target_select_missilebias,10);\r
920     self.target_select_playerbias = bound(-10,self.target_select_playerbias,10);\r
921 \r
922     if (!self.target_select_flags)\r
923         if (self.turrcaps_flags & TFL_TURRCAPS_MISSILEKILL)\r
924             self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_MISSILES |\r
925                                        TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_ANGLELIMITS;\r
926         else\r
927             self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS |\r
928                                        TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_ANGLELIMITS;\r
929 \r
930     //if(!self.target_validate_flags)\r
931     self.target_validate_flags = self.target_select_flags;\r
932 \r
933 \r
934 // Ammo stuff\r
935     if (!self.ammo_max)          self.ammo_max       = self.shot_dmg * 10;\r
936     self.ammo_max = max(self.shot_dmg,self.ammo_max);\r
937 \r
938     if (!self.ammo)              self.ammo           = self.shot_dmg * 5;\r
939     self.ammo = bound(0,self.ammo,self.ammo_max);\r
940 \r
941     if (!self.ammo_recharge)     self.ammo_recharge = self.shot_dmg / 2;\r
942     self.ammo_recharge = max(0,self.ammo_recharge);\r
943 \r
944     // Convert the recharge from X per sec to X per ticrate\r
945     self.ammo_recharge = self.ammo_recharge * self.ticrate;\r
946 \r
947     if (!self.ammo_flags) self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE;\r
948 \r
949 // Offsets & origins\r
950     if (!self.tur_aimorg)    self.tur_aimorg = '0 0 50';\r
951     if (!self.tur_shotorg)   self.tur_shotorg = '50 0 50';\r
952 \r
953 // End of default & sanety checks, start building the turret.\r
954 \r
955 // Spawn extra bits\r
956     self.tur_head   = spawn();\r
957 \r
958     self.tur_head.netname = self.tur_head.classname     = "turret_head";\r
959     self.tur_head.team = self.team;\r
960 \r
961     // Defend mode?\r
962     if (self.target != "")\r
963     {\r
964         self.tur_defend = find(world, targetname, self.target);\r
965         if (self.tur_defend == world)\r
966         {\r
967             self.target = "";\r
968             dprint("Turret has invalid defendpoint!\n");\r
969         }\r
970     }\r
971 \r
972 // Claim ownership\r
973     self.tur_head.owner   = self;\r
974 \r
975 // Put pices in place\r
976 \r
977     if (!(self.turrcaps_flags & TFL_TURRCAPS_LINKED))\r
978         setorigin(self.tur_head,self.origin);\r
979 \r
980     // In target defense mode, aim on the spot to defens when idle.\r
981     if (self.tur_defend)\r
982         self.idle_aim  = self.tur_head.angles + angleofs(self.tur_head,self.tur_defend);\r
983     else\r
984         self.idle_aim  = self.angles;\r
985 \r
986     if (!(self.turrcaps_flags & TFL_TURRCAPS_LINKED))\r
987         self.tur_head.angles    = self.idle_aim;\r
988 \r
989     if (!self.health) self.health  = 150;\r
990     self.tur_health = self.health;\r
991 \r
992     //Solid bbox for preformance reasons\r
993     self.solid              = SOLID_BBOX;\r
994     self.tur_head.solid     = SOLID_BBOX;\r
995 \r
996     self.takedamage             = DAMAGE_AIM;\r
997     self.tur_head.takedamage    = DAMAGE_AIM;\r
998 \r
999     self.movetype            = MOVETYPE_NOCLIP;\r
1000     self.tur_head.movetype   = MOVETYPE_NOCLIP;\r
1001 \r
1002     // Team colouring?track\r
1003     if (self.team == COLOR_TEAM1) self.colormod = '1.4 0.8 0.8';\r
1004     if (self.team == COLOR_TEAM2) self.colormod = '0.8 0.8 1.4';\r
1005 \r
1006     // Attach stdprocs. override when and what needed\r
1007     if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT)\r
1008     {\r
1009         //self.turret_prethink        = turret_stdproc_true;\r
1010         self.turret_score_target    = turret_stdproc_targetscore_support;\r
1011         //self.turret_aim             = turret_stdproc_aim_generic;\r
1012         //self.turret_track           = turret_stdproc_track;\r
1013         self.turret_firecheckfunc   = turret_stdproc_firecheck;\r
1014         self.turret_firefunc        = turret_stdproc_fire;\r
1015         self.turret_postthink       = turret_stdproc_nothing;\r
1016 \r
1017         //self.turret_damagefunc          = turret_stdproc_damage;\r
1018         self.event_damage               = turret_stdproc_damage;\r
1019         self.tur_head.event_damage      = turret_stdproc_damage;\r
1020 \r
1021         //self.turret_diefunc             = turret_stdproc_die;\r
1022         //self.turret_spawnfunc           = turret_stdproc_respawn;\r
1023 \r
1024     }\r
1025     else\r
1026     {\r
1027 \r
1028         //self.turret_prethink        = turret_stdproc_true;\r
1029         self.turret_score_target    = turret_stdproc_targetscore_generic;\r
1030 \r
1031         //if (self.aim_flags & TFL_AIM_SIMPLE)\r
1032         //    self.turret_aim             = turret_stdproc_aim_simple;\r
1033         //else\r
1034         //    self.turret_aim             = turret_stdproc_aim_generic;\r
1035 \r
1036         //self.turret_track           = turret_stdproc_track;\r
1037         self.turret_firecheckfunc   = turret_stdproc_firecheck;\r
1038         self.turret_firefunc        = turret_stdproc_fire;\r
1039         self.turret_postthink       = turret_stdproc_nothing;\r
1040 \r
1041         //self.turret_damagefunc          = turret_stdproc_damage;\r
1042         self.event_damage               = turret_stdproc_damage;\r
1043         self.tur_head.event_damage      = turret_stdproc_damage;\r
1044 \r
1045         //self.turret_diefunc             = turret_stdproc_die;\r
1046         //self.turret_spawnfunc           = turret_stdproc_respawn;\r
1047         self.turret_addtarget           = turret_stdproc_false;\r
1048     }\r
1049 \r
1050     self.use = turret_stdproc_use;\r
1051     self.bot_attack = TRUE;\r
1052 \r
1053     // Initiate the main AI loop\r
1054     self.think     = turret_think;\r
1055     self.nextthink = time + self.ticrate;\r
1056 \r
1057     self.tur_head.team = self.team;\r
1058     self.view_ofs = '0 0 0';\r
1059 \r
1060 #ifdef TURRET_DEBUG\r
1061     self.tur_dbg_start = self.nextthink;\r
1062     while (vlen(self.tur_dbg_rvec) < 2)\r
1063         self.tur_dbg_rvec  = randomvec() * 4;\r
1064 \r
1065     self.tur_dbg_rvec_x = fabs(self.tur_dbg_rvec_x);\r
1066     self.tur_dbg_rvec_y = fabs(self.tur_dbg_rvec_y);\r
1067     self.tur_dbg_rvec_z = fabs(self.tur_dbg_rvec_z);\r
1068 #endif\r
1069 \r
1070     // Its all good.\r
1071     self.classname = "turret_main";\r
1072 \r
1073     self.tur_active = 1;\r
1074 \r
1075     //if (g_onslaught)\r
1076     //    self.use();\r
1077 \r
1078     //turret_stdproc_use();\r
1079 \r
1080     return 1;\r
1081 }\r
1082 \r
1083 \r