]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/turrets/turret/hk_weapon.qc
Merge branch 'master' into TimePath/debug_draw
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / turrets / turret / hk_weapon.qc
1 #ifndef TURRET_HK_WEAPON_H
2 #define TURRET_HK_WEAPON_H
3
4 CLASS(HunterKillerAttack, PortoLaunch)
5 /* flags     */ ATTRIB(HunterKillerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
6 /* impulse   */ ATTRIB(HunterKillerAttack, impulse, int, 9);
7 /* refname   */ ATTRIB(HunterKillerAttack, netname, string, "turret_hk");
8 /* wepname   */ ATTRIB(HunterKillerAttack, m_name, string, _("Hunter-Killer"));
9 ENDCLASS(HunterKillerAttack)
10 REGISTER_WEAPON(HK, NEW(HunterKillerAttack));
11
12 #endif
13
14 #ifdef IMPLEMENTATION
15
16 #ifdef SVQC
17
18 float autocvar_g_turrets_unit_hk_shot_speed;
19 float autocvar_g_turrets_unit_hk_shot_speed_accel;
20 float autocvar_g_turrets_unit_hk_shot_speed_accel2;
21 float autocvar_g_turrets_unit_hk_shot_speed_decel;
22 float autocvar_g_turrets_unit_hk_shot_speed_max;
23 float autocvar_g_turrets_unit_hk_shot_speed_turnrate;
24
25 void turret_hk_missile_think();
26 SOUND(HunterKillerAttack_FIRE, W_Sound("electro_fire"));
27 METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
28 {
29         bool isPlayer = IS_PLAYER(actor);
30         if (fire & 1)
31         if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
32                 if (isPlayer) {
33             turret_initparams(actor);
34             W_SetupShot_Dir(actor, v_forward, false, 0, SND(HunterKillerAttack_FIRE), CH_WEAPON_B, 0);
35             actor.tur_shotdir_updated = w_shotdir;
36             actor.tur_shotorg = w_shotorg;
37             actor.tur_head = actor;
38             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
39         }
40         entity missile = turret_projectile(SND(ROCKET_FIRE), 6, 10, DEATH_TURRET_HK.m_id, PROJECTILE_ROCKET, FALSE, FALSE);
41         te_explosion (missile.origin);
42
43         missile.think = turret_hk_missile_think;
44         missile.nextthink = time + 0.25;
45         missile.movetype = MOVETYPE_BOUNCEMISSILE;
46         missile.velocity = actor.tur_shotdir_updated * (actor.shot_speed * 0.75);
47         missile.angles = vectoangles(missile.velocity);
48         missile.cnt = time + 30;
49         missile.ticrate = max(autocvar_sys_ticrate, 0.05);
50         missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_AI;
51
52         if (!isPlayer)
53         if (actor.tur_head.frame == 0)
54             actor.tur_head.frame = actor.tur_head.frame + 1;
55         }
56 }
57
58 bool hk_is_valid_target(entity e_target);
59 void turret_hk_missile_think()
60 {SELFPARAM();
61     vector vu, vd, vf, vl, vr, ve;  // Vector (direction)
62     float  fu, fd, ff, fl, fr, fe;  // Fraction to solid
63     vector olddir,wishdir,newdir;   // Final direction
64     float lt_for;   // Length of Trace FORwrad
65     float lt_seek;  // Length of Trace SEEK (left, right, up down)
66     float pt_seek;  // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
67     vector pre_pos;
68     float myspeed;
69     entity e;
70     float ad,edist;
71
72     self.nextthink = time + self.ticrate;
73
74     //if (self.cnt < time)
75     //  turret_hk_missile_explode();
76
77     if (self.enemy.deadflag != DEAD_NO)
78         self.enemy = world;
79
80     // Pick the closest valid target.
81     if (!self.enemy)
82     {
83         e = findradius(self.origin, 5000);
84         while (e)
85         {
86             if (hk_is_valid_target(e))
87             {
88                 if (!self.enemy)
89                     self.enemy = e;
90                 else
91                     if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin))
92                         self.enemy = e;
93             }
94             e = e.chain;
95         }
96     }
97
98     self.angles = vectoangles(self.velocity);
99     self.angles_x = self.angles_x * -1;
100     makevectors(self.angles);
101     self.angles_x = self.angles_x * -1;
102
103     if (self.enemy)
104     {
105         edist = vlen(self.origin - self.enemy.origin);
106         // Close enougth to do decent damage?
107         if ( edist <= (self.owner.shot_radius * 0.25) )
108         {
109             turret_projectile_explode();
110             return;
111         }
112
113         // Get data on enemy position
114         pre_pos = self.enemy.origin +
115                   self.enemy.velocity *
116                   min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5);
117
118         traceline(self.origin, pre_pos,true,self.enemy);
119         ve = normalize(pre_pos - self.origin);
120         fe = trace_fraction;
121
122     }
123     else
124     {
125     edist = 0;
126     ve = '0 0 0';
127         fe = 0;
128     }
129
130     if ((fe != 1) || (self.enemy == world) || (edist > 1000))
131     {
132         myspeed = vlen(self.velocity);
133
134         lt_for  = myspeed * 3;
135         lt_seek = myspeed * 2.95;
136
137         // Trace forward
138         traceline(self.origin, self.origin + v_forward * lt_for,false,self);
139         vf = trace_endpos;
140         ff = trace_fraction;
141
142         // Find angular offset
143         ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles);
144
145         // To close to something, Slow down!
146         if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) )
147             myspeed = max(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_decel), (autocvar_g_turrets_unit_hk_shot_speed));
148
149         // Failry clear, accelerate.
150         if ( (ff > 0.7) && (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) )
151             myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel), (autocvar_g_turrets_unit_hk_shot_speed_max));
152
153         // Setup trace pitch
154         pt_seek = 1 - ff;
155         pt_seek = bound(0.15,pt_seek,0.8);
156         if (ff < 0.5) pt_seek = 1;
157
158         // Trace left
159         traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,false,self);
160         vl = trace_endpos;
161         fl = trace_fraction;
162
163         // Trace right
164         traceline(self.origin,  self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
165         vr = trace_endpos;
166         fr = trace_fraction;
167
168         // Trace up
169         traceline(self.origin,  self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
170         vu = trace_endpos;
171         fu = trace_fraction;
172
173         // Trace down
174         traceline(self.origin,  self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
175         vd = trace_endpos;
176         fd = trace_fraction;
177
178         vl = normalize(vl - self.origin);
179         vr = normalize(vr - self.origin);
180         vu = normalize(vu - self.origin);
181         vd = normalize(vd - self.origin);
182
183         // Panic tresh passed, find a single direction and turn as hard as we can
184         if (pt_seek == 1)
185         {
186             wishdir = v_right;
187             if (fl > fr) wishdir = -1 * v_right;
188             if (fu > fl) wishdir = v_up;
189             if (fd > fu) wishdir = -1 * v_up;
190         }
191         else
192         {
193             // Normalize our trace vectors to make a smooth path
194             wishdir = normalize( (vl * fl) + (vr * fr) +  (vu * fu) +  (vd * fd) );
195         }
196
197         if (self.enemy)
198         {
199             if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target
200             wishdir = (wishdir * (1 - fe)) + (ve * fe);
201         }
202     }
203     else
204     {
205         // Got a clear path to target, speed up fast (if not at full speed) and go straight for it.
206         myspeed = vlen(self.velocity);
207         if (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max))
208             myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
209
210         wishdir = ve;
211     }
212
213     if ((myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) && (self.cnt > time))
214         myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
215
216     // Ranoutagazfish?
217     if (self.cnt < time)
218     {
219         self.cnt = time + 0.25;
220         self.nextthink = 0;
221         self.movetype            = MOVETYPE_BOUNCE;
222         return;
223     }
224
225     // Calculate new heading
226     olddir = normalize(self.velocity);
227     newdir = normalize(olddir + wishdir * (autocvar_g_turrets_unit_hk_shot_speed_turnrate));
228
229     // Set heading & speed
230     self.velocity = newdir * myspeed;
231
232     // Align model with new heading
233     self.angles = vectoangles(self.velocity);
234
235
236 #ifdef TURRET_DEBUG_HK
237     //if(self.atime < time) {
238     if ((fe <= 0.99)||(edist > 1000))
239     {
240         te_lightning2(world,self.origin, self.origin + vr * lt_seek);
241         te_lightning2(world,self.origin, self.origin + vl * lt_seek);
242         te_lightning2(world,self.origin, self.origin + vu * lt_seek);
243         te_lightning2(world,self.origin, self.origin + vd * lt_seek);
244         te_lightning2(world,self.origin, vf);
245     }
246     else
247     {
248         te_lightning2(world,self.origin, self.enemy.origin);
249     }
250     bprint("Speed: ", ftos(rint(myspeed)), "\n");
251     bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n");
252     bprint("Trace to target:", ftos(rint(fe * 100)), "%\n");
253     self.atime = time + 0.2;
254     //}
255 #endif
256
257     UpdateCSQCProjectile(self);
258 }
259
260 bool hk_is_valid_target(entity e_target)
261 {SELFPARAM();
262     if (e_target == world)
263         return 0;
264
265     // If only this was used more..
266     if (e_target.flags & FL_NOTARGET)
267         return 0;
268
269     // Cant touch this
270     if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))
271         return 0;
272
273     // player
274     if (IS_CLIENT(e_target))
275     {
276         if (self.owner.target_select_playerbias < 0)
277             return 0;
278
279         if (e_target.deadflag != DEAD_NO)
280             return 0;
281     }
282
283     // Missile
284     if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0))
285         return 0;
286
287     // Team check
288     if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team))
289         return 0;
290
291     return 1;
292 }
293
294 #endif
295
296 #endif