1 #ifndef TURRET_HK_WEAPON_H
2 #define TURRET_HK_WEAPON_H
4 CLASS(HunterKillerAttack, PortoLaunch)
5 /* flags */ ATTRIB(HunterKillerAttack, spawnflags, int, WEP_TYPE_OTHER);
6 /* impulse */ ATTRIB(HunterKillerAttack, impulse, int, 9);
7 /* refname */ ATTRIB(HunterKillerAttack, netname, string, "turret_hk");
8 /* wepname */ ATTRIB(HunterKillerAttack, message, string, _("Hunter-Killer"));
9 ENDCLASS(HunterKillerAttack)
10 REGISTER_WEAPON(HK, NEW(HunterKillerAttack));
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;
25 void turret_hk_missile_think();
26 METHOD(HunterKillerAttack, wr_think, bool(entity thiswep, bool fire1, bool fire2))
29 bool isPlayer = IS_PLAYER(self);
31 if (!isPlayer || weapon_prepareattack(false, WEP_CVAR_PRI(electro, refire))) {
33 turret_initparams(self);
34 W_SetupShot_Dir(self, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
35 self.tur_shotdir_updated = w_shotdir;
36 self.tur_shotorg = w_shotorg;
38 weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
40 entity missile = turret_projectile(SND(ROCKET_FIRE), 6, 10, DEATH_TURRET_HK, PROJECTILE_ROCKET, FALSE, FALSE);
41 te_explosion (missile.origin);
43 missile.think = turret_hk_missile_think;
44 missile.nextthink = time + 0.25;
45 missile.movetype = MOVETYPE_BOUNCEMISSILE;
46 missile.velocity = self.tur_shotdir_updated * (self.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;
53 if (self.tur_head.frame == 0)
54 self.tur_head.frame = self.tur_head.frame + 1;
59 bool hk_is_valid_target(entity e_target);
60 void turret_hk_missile_think()
62 vector vu, vd, vf, vl, vr, ve; // Vector (direction)
63 float fu, fd, ff, fl, fr, fe; // Fraction to solid
64 vector olddir,wishdir,newdir; // Final direction
65 float lt_for; // Length of Trace FORwrad
66 float lt_seek; // Length of Trace SEEK (left, right, up down)
67 float pt_seek; // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
73 self.nextthink = time + self.ticrate;
75 //if (self.cnt < time)
76 // turret_hk_missile_explode();
78 if (self.enemy.deadflag != DEAD_NO)
81 // Pick the closest valid target.
84 e = findradius(self.origin, 5000);
87 if (hk_is_valid_target(e))
92 if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin))
99 self.angles = vectoangles(self.velocity);
100 self.angles_x = self.angles_x * -1;
101 makevectors(self.angles);
102 self.angles_x = self.angles_x * -1;
106 edist = vlen(self.origin - self.enemy.origin);
107 // Close enougth to do decent damage?
108 if ( edist <= (self.owner.shot_radius * 0.25) )
110 turret_projectile_explode();
114 // Get data on enemy position
115 pre_pos = self.enemy.origin +
116 self.enemy.velocity *
117 min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5);
119 traceline(self.origin, pre_pos,true,self.enemy);
120 ve = normalize(pre_pos - self.origin);
131 if ((fe != 1) || (self.enemy == world) || (edist > 1000))
133 myspeed = vlen(self.velocity);
135 lt_for = myspeed * 3;
136 lt_seek = myspeed * 2.95;
139 traceline(self.origin, self.origin + v_forward * lt_for,false,self);
143 // Find angular offset
144 ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles);
146 // To close to something, Slow down!
147 if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) )
148 myspeed = max(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_decel), (autocvar_g_turrets_unit_hk_shot_speed));
150 // Failry clear, accelerate.
151 if ( (ff > 0.7) && (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) )
152 myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel), (autocvar_g_turrets_unit_hk_shot_speed_max));
156 pt_seek = bound(0.15,pt_seek,0.8);
157 if (ff < 0.5) pt_seek = 1;
160 traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,false,self);
165 traceline(self.origin, self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
170 traceline(self.origin, self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
175 traceline(self.origin, self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self);
179 vl = normalize(vl - self.origin);
180 vr = normalize(vr - self.origin);
181 vu = normalize(vu - self.origin);
182 vd = normalize(vd - self.origin);
184 // Panic tresh passed, find a single direction and turn as hard as we can
188 if (fl > fr) wishdir = -1 * v_right;
189 if (fu > fl) wishdir = v_up;
190 if (fd > fu) wishdir = -1 * v_up;
194 // Normalize our trace vectors to make a smooth path
195 wishdir = normalize( (vl * fl) + (vr * fr) + (vu * fu) + (vd * fd) );
200 if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target
201 wishdir = (wishdir * (1 - fe)) + (ve * fe);
206 // Got a clear path to target, speed up fast (if not at full speed) and go straight for it.
207 myspeed = vlen(self.velocity);
208 if (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max))
209 myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
214 if ((myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) && (self.cnt > time))
215 myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max));
220 self.cnt = time + 0.25;
222 self.movetype = MOVETYPE_BOUNCE;
226 // Calculate new heading
227 olddir = normalize(self.velocity);
228 newdir = normalize(olddir + wishdir * (autocvar_g_turrets_unit_hk_shot_speed_turnrate));
230 // Set heading & speed
231 self.velocity = newdir * myspeed;
233 // Align model with new heading
234 self.angles = vectoangles(self.velocity);
237 #ifdef TURRET_DEBUG_HK
238 //if(self.atime < time) {
239 if ((fe <= 0.99)||(edist > 1000))
241 te_lightning2(world,self.origin, self.origin + vr * lt_seek);
242 te_lightning2(world,self.origin, self.origin + vl * lt_seek);
243 te_lightning2(world,self.origin, self.origin + vu * lt_seek);
244 te_lightning2(world,self.origin, self.origin + vd * lt_seek);
245 te_lightning2(world,self.origin, vf);
249 te_lightning2(world,self.origin, self.enemy.origin);
251 bprint("Speed: ", ftos(rint(myspeed)), "\n");
252 bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n");
253 bprint("Trace to target:", ftos(rint(fe * 100)), "%\n");
254 self.atime = time + 0.2;
258 UpdateCSQCProjectile(self);
261 bool hk_is_valid_target(entity e_target)
263 if (e_target == world)
266 // If only this was used more..
267 if (e_target.flags & FL_NOTARGET)
271 if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))
275 if (IS_CLIENT(e_target))
277 if (self.owner.target_select_playerbias < 0)
280 if (e_target.deadflag != DEAD_NO)
285 if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0))
289 if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team))