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