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