]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/weapon/machinegun.qc
fixed/replaced tab and space usage with space only as it originally was I think
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / weapon / machinegun.qc
1 #include "machinegun.qh"
2
3 #ifdef SVQC
4
5 .float machinegun_spread_accumulation;
6
7 .float oldTime;
8
9 void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity weaponentity)
10 {
11         W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, ((actor.(weaponentity).misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)), deathtype);
12         if(!autocvar_g_norecoil)
13         {
14                 actor.punchangle_x = random() - 0.5;
15                 actor.punchangle_y = random() - 0.5;
16         }
17         // this attack_finished just enforces a cooldown at the end of a burst
18         ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
19
20         if(actor.(weaponentity).misc_bulletcounter == 1)
21                 fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, first_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, first_damage), 0, WEP_CVAR(machinegun, first_force), deathtype, EFFECT_BULLET);
22         else
23                 fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, sustained_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), 0, WEP_CVAR(machinegun, sustained_force), deathtype, EFFECT_BULLET);
24
25         W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
26
27         // casing code
28         if(autocvar_g_casings >= 2)
29         {
30                 makevectors(actor.v_angle); // for some reason, this is lost
31                 SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
32         }
33
34         if(actor.(weaponentity).misc_bulletcounter == 1)
35                 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, first_ammo), weaponentity);
36         else
37                 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, sustained_ammo), weaponentity);
38 }
39
40 // weapon frames
41 void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire)
42 {
43         if(actor.(weaponentity).m_weapon != actor.(weaponentity).m_switchweapon || !weapon_prepareattack_check(thiswep, actor, weaponentity, (fire & 2), -1)) // abort immediately if switching
44         {
45                 w_ready(thiswep, actor, weaponentity, fire);
46                 return;
47         }
48         if(PHYS_INPUT_BUTTON_ATCK(actor))
49         {
50                 if(!thiswep.wr_checkammo2(thiswep, actor, weaponentity))
51                 if(!(actor.items & IT_UNLIMITED_AMMO))
52                 {
53                         W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
54                         w_ready(thiswep, actor, weaponentity, fire);
55                         return;
56                 }
57                 actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
58                 W_MachineGun_Attack(thiswep, thiswep.m_id, actor, weaponentity);
59                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
60         }
61         else
62                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready);
63 }
64
65
66 void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
67 {
68         float machinegun_spread;
69
70         if(!(fire & 1) || !weapon_prepareattack_check(thiswep, actor, weaponentity, false, -1))
71         {
72                 w_ready(thiswep, actor, weaponentity, fire);
73                 return;
74         }
75
76         if(!thiswep.wr_checkammo1(thiswep, actor, weaponentity))
77         if(!(actor.items & IT_UNLIMITED_AMMO))
78         {
79                 W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
80                 w_ready(thiswep, actor, weaponentity, fire);
81                 return;
82         }
83
84         W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, sustained_ammo), weaponentity);
85
86         W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage), thiswep.m_id);
87         if(!autocvar_g_norecoil)
88         {
89                 actor.punchangle_x = random() - 0.5;
90                 actor.punchangle_y = random() - 0.5;
91         }
92
93         // Step by step calculations for machinegun_spread_accumulation with optional debug printing
94
95         // check current time
96         //print("\n Current time: ");
97         //print(sprintf("%f ", time));
98
99         // seconds passed since the last shot
100         // oldTime is given value at the end of the calculations
101         //float seconds = (time - actor.(weaponentity).oldTime);
102         //print("\n seconds between last time and this time: ");
103         //print(sprintf("%f ", seconds));
104
105         // use seconds passed since last shot to calculate how much spread should have decayed since then
106         //float spreadReduction = seconds * WEP_CVAR(machinegun, spread_decay);
107         //print("\n reduced spread: ");
108         //print(sprintf("%f ", spreadReduction));
109
110         // reduce spread by the calculated amount
111         //actor.(weaponentity).machinegun_spread_accumulation = actor.(weaponentity).machinegun_spread_accumulation - spreadReduction;
112         //print("\n current spread: ");
113         //print(sprintf("%f ", actor.(weaponentity).machinegun_spread_accumulation));
114
115         // if spread is reduced to the negatives make it 0 instead
116         // the following if block is replaced by max(accumulation, 0);
117         //if(actor.(weaponentity).machinegun_spread_accumulation < 0){
118         //      print("\n spread was <0, was reset to 0 before firing");
119         //      actor.(weaponentity).machinegun_spread_accumulation = 0;
120         //}
121
122         // store the time of the last shot
123         //actor.(weaponentity).oldTime = time;
124         //print("\n Old time: ");
125         //print(sprintf("%f ", actor.(weaponentity).oldTime));
126
127         actor.(weaponentity).machinegun_spread_accumulation = max(actor.(weaponentity).machinegun_spread_accumulation - ((time - actor.(weaponentity).oldTime) * WEP_CVAR(machinegun, spread_decay)), 0);
128         actor.(weaponentity).oldTime = time;
129         if(WEP_CVAR(machinegun, spread_decay) < 0){
130                 machinegun_spread = bound(WEP_CVAR(machinegun, spread_min), WEP_CVAR(machinegun, spread_min) + (WEP_CVAR(machinegun, spread_add) * actor.(weaponentity).misc_bulletcounter), WEP_CVAR(machinegun, spread_max));
131         } else {
132                 machinegun_spread = bound(WEP_CVAR(machinegun, spread_min), WEP_CVAR(machinegun, spread_min) + actor.(weaponentity).machinegun_spread_accumulation, WEP_CVAR(machinegun, spread_max));
133         }
134         fireBullet(actor, weaponentity, w_shotorg, w_shotdir, machinegun_spread, WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), 0, WEP_CVAR(machinegun, sustained_force), thiswep.m_id, EFFECT_BULLET);
135
136         actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
137         actor.(weaponentity).machinegun_spread_accumulation = actor.(weaponentity).machinegun_spread_accumulation + WEP_CVAR(machinegun, spread_add);
138         if(actor.(weaponentity).machinegun_spread_accumulation > WEP_CVAR(machinegun, spread_max)){
139                 actor.(weaponentity).machinegun_spread_accumulation = WEP_CVAR(machinegun, spread_max);
140         }
141
142         W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
143
144         if(autocvar_g_casings >= 2) // casing code
145         {
146                 makevectors(actor.v_angle); // for some reason, this is lost
147                 SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
148         }
149
150         ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
151         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto);
152 }
153
154 void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire)
155 {
156         W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage), thiswep.m_id);
157         if(!autocvar_g_norecoil)
158         {
159                 actor.punchangle_x = random() - 0.5;
160                 actor.punchangle_y = random() - 0.5;
161         }
162
163         fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), 0, WEP_CVAR(machinegun, sustained_force), thiswep.m_id, EFFECT_BULLET);
164
165         W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
166
167         if(autocvar_g_casings >= 2) // casing code
168         {
169                 makevectors(actor.v_angle); // for some reason, this is lost
170                 SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
171         }
172
173         actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
174         actor.(weaponentity).machinegun_spread_accumulation = actor.(weaponentity).machinegun_spread_accumulation + (WEP_CVAR(machinegun, spread_add) * 0.5);
175         if(actor.(weaponentity).machinegun_spread_accumulation > WEP_CVAR(machinegun, spread_max)){
176                 actor.(weaponentity).machinegun_spread_accumulation = WEP_CVAR(machinegun, spread_max);
177         }
178         if(actor.(weaponentity).misc_bulletcounter == 0)
179         {
180                 ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(actor);
181                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready);
182         }
183         else
184         {
185                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst);
186         }
187
188 }
189
190 METHOD(MachineGun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
191 {
192     if(vdist(actor.origin - actor.enemy.origin, <, 3000 - bound(0, skill, 10) * 200))
193         PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
194     else
195         PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
196 }
197 METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
198 {
199     // forced reload - wait until the bulletcounter is 0 so a burst loop can finish
200     if(WEP_CVAR(machinegun, reload_ammo)
201         && actor.(weaponentity).clip_load < min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo))
202         && actor.(weaponentity).misc_bulletcounter >= 0)
203     {
204         thiswep.wr_reload(thiswep, actor, weaponentity);
205     }
206     else if(WEP_CVAR(machinegun, mode) == 1)
207     {
208         if(fire & 1)
209         if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
210         {
211             actor.(weaponentity).misc_bulletcounter = 0;
212             W_MachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
213         }
214
215         // You can "shoot" more rounds than what's "used", and vice versa.
216         if(fire & 2)
217         if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
218         {
219             if(WEP_CVAR(machinegun, spread_decay) >= 0){
220                 actor.(weaponentity).machinegun_spread_accumulation = max(actor.(weaponentity).machinegun_spread_accumulation - ((time - actor.(weaponentity).oldTime) * WEP_CVAR(machinegun, spread_decay)), 0);
221                 actor.(weaponentity).oldTime = time;
222                 if(actor.(weaponentity).machinegun_spread_accumulation > 0){
223                     w_ready(thiswep, actor, weaponentity, fire);
224                     return;
225                 }
226             }
227
228             if(!thiswep.wr_checkammo2(thiswep, actor, weaponentity))
229             if(!(actor.items & IT_UNLIMITED_AMMO))
230             {
231                 W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
232                 w_ready(thiswep, actor, weaponentity, fire);
233                 return;
234             }
235
236             float ammo_available;
237             if (WEP_CVAR(machinegun, reload_ammo) > 0)
238             {
239                 ammo_available = actor.(weaponentity).clip_load;
240             }
241             else
242             {
243                 ammo_available = GetResource(actor, thiswep.ammo_type);
244             }
245
246             // We don't want to shoot 3 rounds if there's 2 left in the mag, so we'll use a fraction.
247             // Also keep the fraction <= 1 otherwise we'd mag dump in one burst.
248             float burst_fraction = min(1, ammo_available / WEP_CVAR(machinegun, burst_ammo));
249             int to_shoot = floor(WEP_CVAR(machinegun, burst) * burst_fraction);
250
251             // We also don't want to use 3 rounds if there's only 2 left.
252             int to_use = min(WEP_CVAR(machinegun, burst_ammo), ammo_available);
253             W_DecreaseAmmo(thiswep, actor, to_use, weaponentity);
254
255             // Bursting counts up to 0 from a negative.
256             actor.(weaponentity).misc_bulletcounter = -to_shoot;
257             W_MachineGun_Attack_Burst(thiswep, actor, weaponentity, fire);
258         }
259     }
260     else
261     {
262
263         if(fire & 1)
264         if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
265         {
266             actor.(weaponentity).misc_bulletcounter = 1;
267             W_MachineGun_Attack(thiswep, thiswep.m_id, actor, weaponentity); // sets attack_finished
268             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
269         }
270
271         if((fire & 2) && WEP_CVAR(machinegun, first))
272         if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
273         {
274             actor.(weaponentity).misc_bulletcounter = 1;
275             W_MachineGun_Attack(thiswep, thiswep.m_id | HITTYPE_SECONDARY, actor, weaponentity); // sets attack_finished
276             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready);
277         }
278     }
279 }
280 METHOD(MachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
281 {
282     float ammo_amount;
283     if(WEP_CVAR(machinegun, mode) == 1)
284         ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, sustained_ammo);
285     else
286         ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, first_ammo);
287
288     if(WEP_CVAR(machinegun, reload_ammo))
289     {
290         if(WEP_CVAR(machinegun, mode) == 1)
291             ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, sustained_ammo);
292         else
293             ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, first_ammo);
294     }
295     return ammo_amount;
296 }
297 METHOD(MachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
298 {
299     float ammo_amount;
300     float burst_ammo_per_shot = WEP_CVAR(machinegun, burst_ammo) / WEP_CVAR(machinegun, burst);
301     if(WEP_CVAR(machinegun, mode) == 1)
302         ammo_amount = GetResource(actor, thiswep.ammo_type) >= burst_ammo_per_shot;
303     else
304         ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, first_ammo);
305
306     if(WEP_CVAR(machinegun, reload_ammo))
307     {
308         if(WEP_CVAR(machinegun, mode) == 1)
309             ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= burst_ammo_per_shot;
310         else
311             ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, first_ammo);
312     }
313     return ammo_amount;
314 }
315 METHOD(MachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
316 {
317         if(actor.(weaponentity).misc_bulletcounter < 0)
318                 return;
319     W_Reload(actor, weaponentity, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND_RELOAD);
320 }
321 METHOD(MachineGun, wr_suicidemessage, Notification(entity thiswep))
322 {
323     return WEAPON_THINKING_WITH_PORTALS;
324 }
325 METHOD(MachineGun, wr_killmessage, Notification(entity thiswep))
326 {
327     if(w_deathtype & HITTYPE_SECONDARY)
328         return WEAPON_MACHINEGUN_MURDER_SNIPE;
329     else
330         return WEAPON_MACHINEGUN_MURDER_SPRAY;
331 }
332
333 #endif
334 #ifdef CSQC
335
336 METHOD(MachineGun, wr_impacteffect, void(entity thiswep, entity actor))
337 {
338     vector org2;
339     org2 = w_org + w_backoff * 2;
340     pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
341     if(!w_issilent)
342         sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM);
343 }
344
345 #endif