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