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