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