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