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