1 #include "machinegun.qh"
5 void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity weaponentity)
7 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);
9 if(!autocvar_g_norecoil)
11 actor.punchangle_x = random() - 0.5;
12 actor.punchangle_y = random() - 0.5;
15 // this attack_finished just enforces a cooldown at the end of a burst
16 ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
18 if(actor.(weaponentity).misc_bulletcounter == 1)
19 fireBullet_falloff(actor, weaponentity, w_shotorg, w_shotdir,
20 WEP_CVAR(machinegun, first_spread),
21 WEP_CVAR(machinegun, solidpenetration),
22 WEP_CVAR(machinegun, first_damage),
23 WEP_CVAR(machinegun, damagefalloff_halflife),
24 WEP_CVAR(machinegun, damagefalloff_mindist),
25 WEP_CVAR(machinegun, damagefalloff_maxdist),
27 WEP_CVAR(machinegun, first_force),
28 WEP_CVAR(machinegun, damagefalloff_forcehalflife),
29 deathtype, EFFECT_BULLET, true);
31 fireBullet_falloff(actor, weaponentity, w_shotorg, w_shotdir,
32 WEP_CVAR(machinegun, sustained_spread),
33 WEP_CVAR(machinegun, solidpenetration),
34 WEP_CVAR(machinegun, sustained_damage),
35 WEP_CVAR(machinegun, damagefalloff_halflife),
36 WEP_CVAR(machinegun, damagefalloff_mindist),
37 WEP_CVAR(machinegun, damagefalloff_maxdist),
39 WEP_CVAR(machinegun, sustained_force),
40 WEP_CVAR(machinegun, damagefalloff_forcehalflife),
41 deathtype, EFFECT_BULLET, true);
43 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
46 if(autocvar_g_casings >= 2)
48 makevectors(actor.v_angle); // for some reason, this is lost
49 SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
52 if(actor.(weaponentity).misc_bulletcounter == 1)
53 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, first_ammo), weaponentity);
55 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, sustained_ammo), weaponentity);
59 void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire)
61 if(actor.(weaponentity).m_weapon != actor.(weaponentity).m_switchweapon || !weapon_prepareattack_check(thiswep, actor, weaponentity, (fire & 2), -1)) // abort immediately if switching
63 w_ready(thiswep, actor, weaponentity, fire);
66 if(PHYS_INPUT_BUTTON_ATCK(actor))
68 if(!thiswep.wr_checkammo2(thiswep, actor, weaponentity))
69 if(!(actor.items & IT_UNLIMITED_AMMO))
71 W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
72 w_ready(thiswep, actor, weaponentity, fire);
75 actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
76 W_MachineGun_Attack(thiswep, thiswep.m_id, actor, weaponentity);
77 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
80 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready);
84 void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
86 float machinegun_spread;
88 if(!(fire & 1) || !weapon_prepareattack_check(thiswep, actor, weaponentity, false, -1))
90 w_ready(thiswep, actor, weaponentity, fire);
94 if(!thiswep.wr_checkammo1(thiswep, actor, weaponentity))
95 if(!(actor.items & IT_UNLIMITED_AMMO))
97 W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
98 w_ready(thiswep, actor, weaponentity, fire);
102 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, sustained_ammo), weaponentity);
104 W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage), thiswep.m_id);
105 if(!autocvar_g_norecoil)
107 actor.punchangle_x = random() - 0.5;
108 actor.punchangle_y = random() - 0.5;
111 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));
112 fireBullet_falloff(actor, weaponentity, w_shotorg, w_shotdir, machinegun_spread, WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage),
113 WEP_CVAR(machinegun, damagefalloff_halflife), WEP_CVAR(machinegun, damagefalloff_mindist), WEP_CVAR(machinegun, damagefalloff_maxdist), 0,
114 WEP_CVAR(machinegun, sustained_force), WEP_CVAR(machinegun, damagefalloff_forcehalflife), thiswep.m_id, EFFECT_BULLET, true);
116 actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
118 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
120 if(autocvar_g_casings >= 2) // casing code
122 makevectors(actor.v_angle); // for some reason, this is lost
123 SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
126 ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
127 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto);
130 void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire)
132 W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage), thiswep.m_id);
133 if(!autocvar_g_norecoil)
135 actor.punchangle_x = random() - 0.5;
136 actor.punchangle_y = random() - 0.5;
139 fireBullet_falloff(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage),
140 WEP_CVAR(machinegun, damagefalloff_halflife), WEP_CVAR(machinegun, damagefalloff_mindist), WEP_CVAR(machinegun, damagefalloff_maxdist), 0,
141 WEP_CVAR(machinegun, sustained_force), WEP_CVAR(machinegun, damagefalloff_forcehalflife), thiswep.m_id, EFFECT_BULLET, true);
143 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
145 if(autocvar_g_casings >= 2) // casing code
147 makevectors(actor.v_angle); // for some reason, this is lost
148 SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
151 actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
152 if(actor.(weaponentity).misc_bulletcounter == 0)
154 ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(actor);
155 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready);
159 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst);
164 METHOD(MachineGun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
166 if(vdist(actor.origin - actor.enemy.origin, <, 3000 - bound(0, skill, 10) * 200))
167 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false, true);
169 PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false, true);
171 METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
173 // forced reload - wait until the bulletcounter is 0 so a burst loop can finish
174 if(WEP_CVAR(machinegun, reload_ammo)
175 && actor.(weaponentity).clip_load < min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo))
176 && actor.(weaponentity).misc_bulletcounter >= 0)
178 thiswep.wr_reload(thiswep, actor, weaponentity);
180 else if(WEP_CVAR(machinegun, mode) == 1)
183 if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
185 actor.(weaponentity).misc_bulletcounter = 0;
186 W_MachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
189 // You can "shoot" more rounds than what's "used", and vice versa.
191 if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
193 if(!thiswep.wr_checkammo2(thiswep, actor, weaponentity))
194 if(!(actor.items & IT_UNLIMITED_AMMO))
196 W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
197 w_ready(thiswep, actor, weaponentity, fire);
201 float ammo_available;
202 if (WEP_CVAR(machinegun, reload_ammo) > 0)
204 ammo_available = actor.(weaponentity).clip_load;
208 ammo_available = GetResource(actor, thiswep.ammo_type);
211 // We don't want to shoot 3 rounds if there's 2 left in the mag, so we'll use a fraction.
212 // Also keep the fraction <= 1 otherwise we'd mag dump in one burst.
213 float burst_fraction = min(1, ammo_available / WEP_CVAR(machinegun, burst_ammo));
214 int to_shoot = floor(WEP_CVAR(machinegun, burst) * burst_fraction);
216 // We also don't want to use 3 rounds if there's only 2 left.
217 int to_use = min(WEP_CVAR(machinegun, burst_ammo), ammo_available);
218 W_DecreaseAmmo(thiswep, actor, to_use, weaponentity);
220 // Bursting counts up to 0 from a negative.
221 actor.(weaponentity).misc_bulletcounter = -to_shoot;
222 W_MachineGun_Attack_Burst(thiswep, actor, weaponentity, fire);
229 if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
231 actor.(weaponentity).misc_bulletcounter = 1;
232 W_MachineGun_Attack(thiswep, thiswep.m_id, actor, weaponentity); // sets attack_finished
233 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
236 if((fire & 2) && WEP_CVAR(machinegun, first))
237 if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
239 actor.(weaponentity).misc_bulletcounter = 1;
240 W_MachineGun_Attack(thiswep, thiswep.m_id | HITTYPE_SECONDARY, actor, weaponentity); // sets attack_finished
241 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready);
245 METHOD(MachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
248 if(WEP_CVAR(machinegun, mode) == 1)
249 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, sustained_ammo);
251 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, first_ammo);
253 if(WEP_CVAR(machinegun, reload_ammo))
255 if(WEP_CVAR(machinegun, mode) == 1)
256 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, sustained_ammo);
258 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, first_ammo);
262 METHOD(MachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
265 float burst_ammo_per_shot = WEP_CVAR(machinegun, burst_ammo) / WEP_CVAR(machinegun, burst);
266 if(WEP_CVAR(machinegun, mode) == 1)
267 ammo_amount = GetResource(actor, thiswep.ammo_type) >= burst_ammo_per_shot;
269 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, first_ammo);
271 if(WEP_CVAR(machinegun, reload_ammo))
273 if(WEP_CVAR(machinegun, mode) == 1)
274 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= burst_ammo_per_shot;
276 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, first_ammo);
280 METHOD(MachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
282 if(actor.(weaponentity).misc_bulletcounter < 0)
284 W_Reload(actor, weaponentity, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND_RELOAD);
286 METHOD(MachineGun, wr_suicidemessage, Notification(entity thiswep))
288 return WEAPON_THINKING_WITH_PORTALS;
290 METHOD(MachineGun, wr_killmessage, Notification(entity thiswep))
292 if(w_deathtype & HITTYPE_SECONDARY)
293 return WEAPON_MACHINEGUN_MURDER_SNIPE;
295 return WEAPON_MACHINEGUN_MURDER_SPRAY;
301 METHOD(MachineGun, wr_impacteffect, void(entity thiswep, entity actor))
303 vector org2 = w_org + w_backoff * 2;
304 pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
306 sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM);