]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/weapon/machinegun.qc
e3d1da2fea86db9d6ad4cff51cbe881a45e5273e
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / weapon / machinegun.qc
1 #ifndef IMPLEMENTATION
2 CLASS(MachineGun, Weapon)
3 /* ammotype  */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails);
4 /* impulse   */ ATTRIB(MachineGun, impulse, int, 3);
5 /* flags     */ ATTRIB(MachineGun, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
6 /* rating    */ ATTRIB(MachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID);
7 /* color     */ ATTRIB(MachineGun, wpcolor, vector, '1 1 0');
8 /* modelname */ ATTRIB(MachineGun, mdl, string, "uzi");
9 #ifndef MENUQC
10 /* model     */ ATTRIB(MachineGun, m_model, Model, MDL_MACHINEGUN_ITEM);
11 #endif
12 /* crosshair */ ATTRIB(MachineGun, w_crosshair, string, "gfx/crosshairuzi");
13 /* crosshair */ ATTRIB(MachineGun, w_crosshair_size, float, 0.6);
14 /* wepimg    */ ATTRIB(MachineGun, model2, string, "weaponuzi");
15 /* refname   */ ATTRIB(MachineGun, netname, string, "machinegun");
16 /* wepname   */ ATTRIB(MachineGun, m_name, string, _("MachineGun"));
17
18 #define X(BEGIN, P, END, class, prefix) \
19         BEGIN(class) \
20                 P(class, prefix, burst, float, NONE) \
21                 P(class, prefix, burst_ammo, float, NONE) \
22                 P(class, prefix, burst_animtime, float, NONE) \
23                 P(class, prefix, burst_refire2, float, NONE) \
24                 P(class, prefix, burst_refire, float, NONE) \
25                 P(class, prefix, burst_speed, float, NONE) \
26                 P(class, prefix, first, float, NONE) \
27                 P(class, prefix, first_ammo, float, NONE) \
28                 P(class, prefix, first_damage, float, NONE) \
29                 P(class, prefix, first_force, float, NONE) \
30                 P(class, prefix, first_refire, float, NONE) \
31                 P(class, prefix, first_spread, float, NONE) \
32                 P(class, prefix, mode, float, NONE) \
33                 P(class, prefix, reload_ammo, float, NONE) \
34         P(class, prefix, reload_time, float, NONE) \
35                 P(class, prefix, solidpenetration, float, NONE) \
36                 P(class, prefix, spread_add, float, NONE) \
37                 P(class, prefix, spread_max, float, NONE) \
38                 P(class, prefix, spread_min, float, NONE) \
39                 P(class, prefix, sustained_ammo, float, NONE) \
40                 P(class, prefix, sustained_damage, float, NONE) \
41                 P(class, prefix, sustained_force, float, NONE) \
42                 P(class, prefix, sustained_refire, float, NONE) \
43                 P(class, prefix, sustained_spread, float, NONE) \
44         P(class, prefix, switchdelay_drop, float, NONE) \
45         P(class, prefix, switchdelay_raise, float, NONE) \
46         P(class, prefix, weaponreplace, string,NONE) \
47         P(class, prefix, weaponstartoverride, float, NONE) \
48         P(class, prefix, weaponstart, float, NONE) \
49         P(class, prefix, weaponthrowable, float, NONE) \
50         END()
51     W_PROPS(X, MachineGun, machinegun)
52 #undef X
53
54 ENDCLASS(MachineGun)
55 REGISTER_WEAPON(MACHINEGUN, machinegun, NEW(MachineGun));
56
57
58 #endif
59 #ifdef IMPLEMENTATION
60 #ifdef SVQC
61
62 spawnfunc(weapon_machinegun)
63 {
64         if(autocvar_sv_q3acompat_machineshotgunswap)
65         if(this.classname != "droppedweapon")
66         {
67                 weapon_defaultspawnfunc(this, WEP_SHOCKWAVE);
68                 return;
69         }
70         weapon_defaultspawnfunc(this, WEP_MACHINEGUN);
71 }
72 spawnfunc(weapon_uzi) { spawnfunc_weapon_machinegun(this); }
73
74 void W_MachineGun_MuzzleFlash_Think(entity this)
75 {
76         this.frame += 2;
77         this.scale *= 0.5;
78         this.alpha -= 0.25;
79         this.nextthink = time + 0.05;
80
81         if(this.alpha <= 0)
82         {
83                 setthink(this, SUB_Remove);
84                 this.nextthink = time;
85                 this.realowner.muzzle_flash = NULL;
86                 return;
87         }
88
89 }
90
91 void W_MachineGun_MuzzleFlash(entity actor, .entity weaponentity)
92 {
93         entity wepent = actor.(weaponentity);
94
95         if(wepent.muzzle_flash == NULL)
96                 wepent.muzzle_flash = spawn();
97
98         // muzzle flash for 1st person view
99         setmodel(wepent.muzzle_flash, MDL_MACHINEGUN_MUZZLEFLASH); // precision set below
100
101         wepent.muzzle_flash.scale = 0.75;
102         setthink(wepent.muzzle_flash, W_MachineGun_MuzzleFlash_Think);
103         wepent.muzzle_flash.nextthink = time + 0.02;
104         wepent.muzzle_flash.frame = 2;
105         wepent.muzzle_flash.alpha = 0.75;
106         wepent.muzzle_flash.angles_z = random() * 180;
107         wepent.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
108         wepent.muzzle_flash.owner = wepent.muzzle_flash.realowner = wepent;
109 }
110
111 void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity weaponentity)
112 {
113         W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, ((actor.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)));
114         if(!autocvar_g_norecoil)
115         {
116                 actor.punchangle_x = random() - 0.5;
117                 actor.punchangle_y = random() - 0.5;
118         }
119         int slot = weaponslot(weaponentity);
120         // this attack_finished just enforces a cooldown at the end of a burst
121         ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
122
123         if(actor.misc_bulletcounter == 1)
124                 fireBullet(actor, w_shotorg, w_shotdir, WEP_CVAR(machinegun, first_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, first_damage), WEP_CVAR(machinegun, first_force), deathtype, 0);
125         else
126                 fireBullet(actor, w_shotorg, w_shotdir, WEP_CVAR(machinegun, sustained_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), deathtype, 0);
127
128         Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
129
130         W_MachineGun_MuzzleFlash(actor, weaponentity);
131         W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0');
132
133         // casing code
134         if(autocvar_g_casings >= 2)
135         {
136                 makevectors(actor.v_angle); // for some reason, this is lost
137                 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);
138         }
139
140         if(actor.misc_bulletcounter == 1)
141                 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, first_ammo));
142         else
143                 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, sustained_ammo));
144 }
145
146 // weapon frames
147 void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire)
148 {
149         if(PS(actor).m_weapon != PS(actor).m_switchweapon) // abort immediately if switching
150         {
151                 w_ready(thiswep, actor, weaponentity, fire);
152                 return;
153         }
154         if(PHYS_INPUT_BUTTON_ATCK(actor))
155         {
156                 if(!thiswep.wr_checkammo2(thiswep, actor))
157                 if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
158                 {
159                         W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
160                         w_ready(thiswep, actor, weaponentity, fire);
161                         return;
162                 }
163                 actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
164                 W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, actor, weaponentity);
165                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
166         }
167         else
168                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready);
169 }
170
171
172 void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
173 {
174         float machinegun_spread;
175
176         if(!(fire & 1))
177         {
178                 w_ready(thiswep, actor, weaponentity, fire);
179                 return;
180         }
181
182         if(!thiswep.wr_checkammo1(thiswep, actor))
183         if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
184         {
185                 W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
186                 w_ready(thiswep, actor, weaponentity, fire);
187                 return;
188         }
189
190         W_DecreaseAmmo(WEP_MACHINEGUN, actor, WEP_CVAR(machinegun, sustained_ammo));
191
192         W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
193         if(!autocvar_g_norecoil)
194         {
195                 actor.punchangle_x = random() - 0.5;
196                 actor.punchangle_y = random() - 0.5;
197         }
198
199         machinegun_spread = bound(WEP_CVAR(machinegun, spread_min), WEP_CVAR(machinegun, spread_min) + (WEP_CVAR(machinegun, spread_add) * actor.misc_bulletcounter), WEP_CVAR(machinegun, spread_max));
200         fireBullet(actor, w_shotorg, w_shotdir, machinegun_spread, WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN.m_id, 0);
201
202         actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
203
204         Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
205
206         W_MachineGun_MuzzleFlash(actor, weaponentity);
207         W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0');
208
209         if(autocvar_g_casings >= 2) // casing code
210         {
211                 makevectors(actor.v_angle); // for some reason, this is lost
212                 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);
213         }
214
215         int slot = weaponslot(weaponentity);
216         ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
217         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto);
218 }
219
220 void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire)
221 {
222         W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
223         if(!autocvar_g_norecoil)
224         {
225                 actor.punchangle_x = random() - 0.5;
226                 actor.punchangle_y = random() - 0.5;
227         }
228
229         fireBullet(actor, w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_speed), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN.m_id, 0);
230
231         Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
232
233         W_MachineGun_MuzzleFlash(actor, weaponentity);
234         W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0');
235
236         if(autocvar_g_casings >= 2) // casing code
237         {
238                 makevectors(actor.v_angle); // for some reason, this is lost
239                 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);
240         }
241
242         actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
243         if(actor.misc_bulletcounter == 0)
244         {
245                 int slot = weaponslot(weaponentity);
246                 ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(actor);
247                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready);
248         }
249         else
250         {
251                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst);
252         }
253
254 }
255
256 METHOD(MachineGun, wr_aim, void(entity thiswep, entity actor))
257 {
258     if(vdist(actor.origin - actor.enemy.origin, <, 3000 - bound(0, skill, 10) * 200))
259         PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, 1000000, 0, 0.001, false);
260     else
261         PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, 1000000, 0, 0.001, false);
262 }
263 METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
264 {
265     if(WEP_CVAR(machinegun, reload_ammo) && actor.clip_load < min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo))) { // forced reload
266         thiswep.wr_reload(thiswep, actor, weaponentity);
267     } else
268     if(WEP_CVAR(machinegun, mode) == 1)
269     {
270         if(fire & 1)
271         if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
272         {
273             actor.misc_bulletcounter = 0;
274             W_MachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
275         }
276
277         if(fire & 2)
278         if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
279         {
280             if(!thiswep.wr_checkammo2(thiswep, actor))
281             if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
282             {
283                 W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
284                 w_ready(thiswep, actor, weaponentity, fire);
285                 return;
286             }
287
288             W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, burst_ammo));
289
290             actor.misc_bulletcounter = WEP_CVAR(machinegun, burst) * -1;
291             W_MachineGun_Attack_Burst(thiswep, actor, weaponentity, fire);
292         }
293     }
294     else
295     {
296
297         if(fire & 1)
298         if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
299         {
300             actor.misc_bulletcounter = 1;
301             W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, actor, weaponentity); // sets attack_finished
302             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
303         }
304
305         if((fire & 2) && WEP_CVAR(machinegun, first))
306         if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
307         {
308             actor.misc_bulletcounter = 1;
309             W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY, actor, weaponentity); // sets attack_finished
310             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready);
311         }
312     }
313 }
314 METHOD(MachineGun, wr_checkammo1, bool(entity thiswep, entity actor))
315 {
316     float ammo_amount;
317     if(WEP_CVAR(machinegun, mode) == 1)
318         ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, sustained_ammo);
319     else
320         ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, first_ammo);
321
322     if(WEP_CVAR(machinegun, reload_ammo))
323     {
324         if(WEP_CVAR(machinegun, mode) == 1)
325             ammo_amount += actor.(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, sustained_ammo);
326         else
327             ammo_amount += actor.(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, first_ammo);
328     }
329     return ammo_amount;
330 }
331 METHOD(MachineGun, wr_checkammo2, bool(entity thiswep, entity actor))
332 {
333     float ammo_amount;
334     if(WEP_CVAR(machinegun, mode) == 1)
335         ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, burst_ammo);
336     else
337         ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, first_ammo);
338
339     if(WEP_CVAR(machinegun, reload_ammo))
340     {
341         if(WEP_CVAR(machinegun, mode) == 1)
342             ammo_amount += actor.(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, burst_ammo);
343         else
344             ammo_amount += actor.(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, first_ammo);
345     }
346     return ammo_amount;
347 }
348 METHOD(MachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
349 {
350     W_Reload(actor, weaponentity, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND_RELOAD);
351 }
352 METHOD(MachineGun, wr_suicidemessage, Notification(entity thiswep))
353 {
354     return WEAPON_THINKING_WITH_PORTALS;
355 }
356 METHOD(MachineGun, wr_killmessage, Notification(entity thiswep))
357 {
358     if(w_deathtype & HITTYPE_SECONDARY)
359         return WEAPON_MACHINEGUN_MURDER_SNIPE;
360     else
361         return WEAPON_MACHINEGUN_MURDER_SPRAY;
362 }
363
364 #endif
365 #ifdef CSQC
366
367 METHOD(MachineGun, wr_impacteffect, void(entity thiswep, entity actor))
368 {
369     vector org2;
370     org2 = w_org + w_backoff * 2;
371     pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
372     if(!w_issilent)
373         sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM);
374 }
375
376 #endif
377 #endif