]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/turrets/turret/hk.qc
8ec6f16bc0489ec1200de88d6cf368e29667e120
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / turrets / turret / hk.qc
1 #ifndef TUR_HK_H
2 #define TUR_HK_H
3 REGISTER_TURRET(
4 /* TUR_##id   */ HK,
5 /* function   */ t_hk,
6 /* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS,
7 /* mins,maxs  */ '-32 -32 0', '32 32 64',
8 /* model          */ "base.md3",
9 /* head_model */ "hk.md3",
10 /* netname        */ "hk",
11 /* fullname   */ _("Hunter-Killer Turret")
12 );
13 #endif
14
15 #ifdef IMPLEMENTATION
16 #ifdef SVQC
17 float autocvar_g_turrets_unit_hk_shot_speed;
18 float autocvar_g_turrets_unit_hk_shot_speed_accel;
19 float autocvar_g_turrets_unit_hk_shot_speed_accel2;
20 float autocvar_g_turrets_unit_hk_shot_speed_decel;
21 float autocvar_g_turrets_unit_hk_shot_speed_max;
22 float autocvar_g_turrets_unit_hk_shot_speed_turnrate;
23
24 //#define TURRET_DEBUG_HK
25
26 #ifdef TURRET_DEBUG_HK
27 .float atime;
28 #endif
29
30 float hk_is_valid_target(entity e_target)
31 {SELFPARAM();
32     if (e_target == world)
33         return 0;
34
35     // If only this was used more..
36     if (e_target.flags & FL_NOTARGET)
37         return 0;
38
39     // Cant touch this
40     if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))
41         return 0;
42
43     // player
44     if (IS_CLIENT(e_target))
45     {
46         if (self.owner.target_select_playerbias < 0)
47             return 0;
48
49         if (e_target.deadflag != DEAD_NO)
50             return 0;
51     }
52
53     // Missile
54     if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0))
55         return 0;
56
57     // Team check
58     if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team))
59         return 0;
60
61     return 1;
62 }
63
64 void turret_hk_missile_think()
65 {SELFPARAM();
66     vector vu, vd, vf, vl, vr, ve;  // Vector (direction)
67     float  fu, fd, ff, fl, fr, fe;  // Fraction to solid
68     vector olddir,wishdir,newdir;   // Final direction
69     float lt_for;   // Length of Trace FORwrad
70     float lt_seek;  // Length of Trace SEEK (left, right, up down)
71     float pt_seek;  // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
72     vector pre_pos;
73     float myspeed;
74     entity e;
75     float ad,edist;
76
77     self.nextthink = time + self.ticrate;
78
79     //if (self.cnt < time)
80     //  turret_hk_missile_explode();
81
82     if (self.enemy.deadflag != DEAD_NO)
83         self.enemy = world;
84
85     // Pick the closest valid target.
86     if (!self.enemy)
87     {
88         e = findradius(self.origin, 5000);
89         while (e)
90         {
91             if (hk_is_valid_target(e))
92             {
93                 if (!self.enemy)
94                     self.enemy = e;
95                 else
96                     if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin))
97                         self.enemy = e;
98             }
99             e = e.chain;
100         }
101     }
102
103     self.angles = vectoangles(self.velocity);
104     self.angles_x = self.angles_x * -1;
105     makevectors(self.angles);
106     self.angles_x = self.angles_x * -1;
107
108     if (self.enemy)
109     {
110         edist = vlen(self.origin - self.enemy.origin);
111         // Close enougth to do decent damage?
112         if ( edist <= (self.owner.shot_radius * 0.25) )
113         {
114             turret_projectile_explode();
115             return;
116         }
117
118         // Get data on enemy position
119         pre_pos = self.enemy.origin +
120                   self.enemy.velocity *
121                   min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5);
122
123         traceline(self.origin, pre_pos,true,self.enemy);
124         ve = normalize(pre_pos - self.origin);
125         fe = trace_fraction;
126
127     }
128     else
129     {
130     edist = 0;
131     ve = '0 0 0';
132         fe = 0;
133     }
134
135     if ((fe != 1) || (self.enemy == world) || (edist > 1000))
136     {
137         myspeed = vlen(self.velocity);
138
139         lt_for  = myspeed * 3;
140         lt_seek = myspeed * 2.95;
141
142         // Trace forward
143         traceline(self.origin, self.origin + v_forward * lt_for,false,self);
144         vf = trace_endpos;
145         ff = trace_fraction;
146
147         // Find angular offset
148         ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles);
149
150         // To close to something, Slow down!
151         if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) )
152             myspeed = max(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_decel), (autocvar_g_turrets_unit_hk_shot_speed));
153
154         // Failry clear, accelerate.
155         if ( (ff > 0.7) && (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) )
156             myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel), (autocvar_g_turrets_unit_hk_shot_speed_max));
157
158         // Setup trace pitch
159         pt_seek = 1 - ff;
160         pt_seek = bound(0.15,pt_seek,0.8);
161         if (ff < 0.5) pt_seek = 1;
162
163         // Trace left
164         traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,false,self);
165         vl = trace_endpos;
166         fl = trace_fraction;
167
168         // Trace right
169         traceline(self.origin,  self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
170         vr = trace_endpos;
171         fr = trace_fraction;
172
173         // Trace up
174         traceline(self.origin,  self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
175         vu = trace_endpos;
176         fu = trace_fraction;
177
178         // Trace down
179         traceline(self.origin,  self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
180         vd = trace_endpos;
181         fd = trace_fraction;
182
183         vl = normalize(vl - self.origin);
184         vr = normalize(vr - self.origin);
185         vu = normalize(vu - self.origin);
186         vd = normalize(vd - self.origin);
187
188         // Panic tresh passed, find a single direction and turn as hard as we can
189         if (pt_seek == 1)
190         {
191             wishdir = v_right;
192             if (fl > fr) wishdir = -1 * v_right;
193             if (fu > fl) wishdir = v_up;
194             if (fd > fu) wishdir = -1 * v_up;
195         }
196         else
197         {
198             // Normalize our trace vectors to make a smooth path
199             wishdir = normalize( (vl * fl) + (vr * fr) +  (vu * fu) +  (vd * fd) );
200         }
201
202         if (self.enemy)
203         {
204             if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target
205             wishdir = (wishdir * (1 - fe)) + (ve * fe);
206         }
207     }
208     else
209     {
210         // Got a clear path to target, speed up fast (if not at full speed) and go straight for it.
211         myspeed = vlen(self.velocity);
212         if (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max))
213             myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
214
215         wishdir = ve;
216     }
217
218     if ((myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) && (self.cnt > time))
219         myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
220
221     // Ranoutagazfish?
222     if (self.cnt < time)
223     {
224         self.cnt = time + 0.25;
225         self.nextthink = 0;
226         self.movetype            = MOVETYPE_BOUNCE;
227         return;
228     }
229
230     // Calculate new heading
231     olddir = normalize(self.velocity);
232     newdir = normalize(olddir + wishdir * (autocvar_g_turrets_unit_hk_shot_speed_turnrate));
233
234     // Set heading & speed
235     self.velocity = newdir * myspeed;
236
237     // Align model with new heading
238     self.angles = vectoangles(self.velocity);
239
240
241 #ifdef TURRET_DEBUG_HK
242     //if(self.atime < time) {
243     if ((fe <= 0.99)||(edist > 1000))
244     {
245         te_lightning2(world,self.origin, self.origin + vr * lt_seek);
246         te_lightning2(world,self.origin, self.origin + vl * lt_seek);
247         te_lightning2(world,self.origin, self.origin + vu * lt_seek);
248         te_lightning2(world,self.origin, self.origin + vd * lt_seek);
249         te_lightning2(world,self.origin, vf);
250     }
251     else
252     {
253         te_lightning2(world,self.origin, self.enemy.origin);
254     }
255     bprint("Speed: ", ftos(rint(myspeed)), "\n");
256     bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n");
257     bprint("Trace to target:", ftos(rint(fe * 100)), "%\n");
258     self.atime = time + 0.2;
259     //}
260 #endif
261
262     UpdateCSQCProjectile(self);
263 }
264
265 float turret_hk_addtarget(entity e_target,entity e_sender)
266 {SELFPARAM();
267     if (e_target)
268     {
269         if (turret_validate_target(self,e_target,self.target_validate_flags) > 0)
270         {
271             self.enemy = e_target;
272             return 1;
273         }
274     }
275
276     return 0;
277 }
278
279 void spawnfunc_turret_hk() { SELFPARAM(); if(!turret_initialize(TUR_HK.m_id)) remove(self); }
280
281 float t_hk(float req)
282 {SELFPARAM();
283     switch(req)
284     {
285         case TR_ATTACK:
286         {
287             entity missile;
288
289             missile = turret_projectile(SND(ROCKET_FIRE), 6, 10, DEATH_TURRET_HK, PROJECTILE_ROCKET, FALSE, FALSE);
290             te_explosion (missile.origin);
291
292             missile.think                       = turret_hk_missile_think;
293             missile.nextthink           = time + 0.25;
294             missile.movetype             = MOVETYPE_BOUNCEMISSILE;
295             missile.velocity             = self.tur_shotdir_updated * (self.shot_speed * 0.75);
296             missile.angles                 = vectoangles(missile.velocity);
297             missile.cnt                   = time + 30;
298             missile.ticrate               = max(autocvar_sys_ticrate, 0.05);
299             missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_AI;
300
301             if (self.tur_head.frame == 0)
302                 self.tur_head.frame = self.tur_head.frame + 1;
303
304             return true;
305         }
306         case TR_THINK:
307         {
308             if (self.tur_head.frame != 0)
309                 self.tur_head.frame = self.tur_head.frame + 1;
310
311             if (self.tur_head.frame > 5)
312                 self.tur_head.frame = 0;
313
314             return true;
315         }
316         case TR_DEATH:
317         {
318             return true;
319         }
320         case TR_SETUP:
321         {
322             self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
323             self.aim_flags = TFL_AIM_SIMPLE;
324             self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK;
325             self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCHECK  | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF;
326             self.shoot_flags = TFL_SHOOT_CLEARTARGET;
327             self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK;
328
329             self.turret_addtarget = turret_hk_addtarget;
330
331             return true;
332         }
333         case TR_PRECACHE:
334         {
335             return true;
336         }
337     }
338
339     return true;
340 }
341
342 #endif // SVQC
343 #ifdef CSQC
344 float t_hk(float req)
345 {
346     switch(req)
347     {
348         case TR_SETUP:
349         {
350             return true;
351         }
352         case TR_PRECACHE:
353         {
354             return true;
355         }
356     }
357
358     return true;
359 }
360
361 #endif // CSQC
362 #endif // REGISTER_TURRET