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