]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/weapon/machinegun.qc
Merge branch 'Mario/teams_bitflag' into 'master'
[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)
92 {
93         if(actor.muzzle_flash == NULL)
94                 actor.muzzle_flash = spawn();
95
96         // muzzle flash for 1st person view
97         setmodel(actor.muzzle_flash, MDL_MACHINEGUN_MUZZLEFLASH); // precision set below
98
99         actor.muzzle_flash.scale = 0.75;
100         setthink(actor.muzzle_flash, W_MachineGun_MuzzleFlash_Think);
101         actor.muzzle_flash.nextthink = time + 0.02;
102         actor.muzzle_flash.frame = 2;
103         actor.muzzle_flash.alpha = 0.75;
104         actor.muzzle_flash.angles_z = random() * 180;
105         actor.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
106         actor.muzzle_flash.owner = actor.muzzle_flash.realowner = actor;
107 }
108
109 void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity weaponentity)
110 {
111         W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, ((actor.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)));
112         if(!autocvar_g_norecoil)
113         {
114                 actor.punchangle_x = random() - 0.5;
115                 actor.punchangle_y = random() - 0.5;
116         }
117         int slot = weaponslot(weaponentity);
118         // this attack_finished just enforces a cooldown at the end of a burst
119         ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
120
121         if(actor.misc_bulletcounter == 1)
122                 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);
123         else
124                 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);
125
126         Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
127
128         W_MachineGun_MuzzleFlash(actor);
129         W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
130
131         // casing code
132         if(autocvar_g_casings >= 2)
133         {
134                 makevectors(actor.v_angle); // for some reason, this is lost
135                 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);
136         }
137
138         if(actor.misc_bulletcounter == 1)
139                 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, first_ammo));
140         else
141                 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, sustained_ammo));
142 }
143
144 // weapon frames
145 void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire)
146 {
147         if(PS(actor).m_weapon != PS(actor).m_switchweapon) // abort immediately if switching
148         {
149                 w_ready(thiswep, actor, weaponentity, fire);
150                 return;
151         }
152         if(PHYS_INPUT_BUTTON_ATCK(actor))
153         {
154                 if(!thiswep.wr_checkammo2(thiswep, actor))
155                 if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
156                 {
157                         W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
158                         w_ready(thiswep, actor, weaponentity, fire);
159                         return;
160                 }
161                 actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
162                 W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, actor, weaponentity);
163                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
164         }
165         else
166                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready);
167 }
168
169
170 void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
171 {
172         float machinegun_spread;
173
174         if(!(fire & 1))
175         {
176                 w_ready(thiswep, actor, weaponentity, fire);
177                 return;
178         }
179
180         if(!thiswep.wr_checkammo1(thiswep, actor))
181         if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
182         {
183                 W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
184                 w_ready(thiswep, actor, weaponentity, fire);
185                 return;
186         }
187
188         W_DecreaseAmmo(WEP_MACHINEGUN, actor, WEP_CVAR(machinegun, sustained_ammo));
189
190         W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
191         if(!autocvar_g_norecoil)
192         {
193                 actor.punchangle_x = random() - 0.5;
194                 actor.punchangle_y = random() - 0.5;
195         }
196
197         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));
198         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);
199
200         actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
201
202         Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
203
204         W_MachineGun_MuzzleFlash(actor);
205         W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
206
207         if(autocvar_g_casings >= 2) // casing code
208         {
209                 makevectors(actor.v_angle); // for some reason, this is lost
210                 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);
211         }
212
213         int slot = weaponslot(weaponentity);
214         ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor);
215         weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto);
216 }
217
218 void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire)
219 {
220         W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
221         if(!autocvar_g_norecoil)
222         {
223                 actor.punchangle_x = random() - 0.5;
224                 actor.punchangle_y = random() - 0.5;
225         }
226
227         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);
228
229         Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
230
231         W_MachineGun_MuzzleFlash(actor);
232         W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
233
234         if(autocvar_g_casings >= 2) // casing code
235         {
236                 makevectors(actor.v_angle); // for some reason, this is lost
237                 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);
238         }
239
240         actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
241         if(actor.misc_bulletcounter == 0)
242         {
243                 int slot = weaponslot(weaponentity);
244                 ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(actor);
245                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready);
246         }
247         else
248         {
249                 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst);
250         }
251
252 }
253
254 METHOD(MachineGun, wr_aim, void(entity thiswep, entity actor))
255 {
256     if(vdist(actor.origin - actor.enemy.origin, <, 3000 - bound(0, skill, 10) * 200))
257         PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, 1000000, 0, 0.001, false);
258     else
259         PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, 1000000, 0, 0.001, false);
260 }
261 METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
262 {
263     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
264         thiswep.wr_reload(thiswep, actor, weaponentity);
265     } else
266     if(WEP_CVAR(machinegun, mode) == 1)
267     {
268         if(fire & 1)
269         if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
270         {
271             actor.misc_bulletcounter = 0;
272             W_MachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
273         }
274
275         if(fire & 2)
276         if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
277         {
278             if(!thiswep.wr_checkammo2(thiswep, actor))
279             if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
280             {
281                 W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
282                 w_ready(thiswep, actor, weaponentity, fire);
283                 return;
284             }
285
286             W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, burst_ammo));
287
288             actor.misc_bulletcounter = WEP_CVAR(machinegun, burst) * -1;
289             W_MachineGun_Attack_Burst(thiswep, actor, weaponentity, fire);
290         }
291     }
292     else
293     {
294
295         if(fire & 1)
296         if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
297         {
298             actor.misc_bulletcounter = 1;
299             W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, actor, weaponentity); // sets attack_finished
300             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
301         }
302
303         if((fire & 2) && WEP_CVAR(machinegun, first))
304         if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
305         {
306             actor.misc_bulletcounter = 1;
307             W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY, actor, weaponentity); // sets attack_finished
308             weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready);
309         }
310     }
311 }
312 METHOD(MachineGun, wr_checkammo1, bool(entity thiswep, entity actor))
313 {
314     float ammo_amount;
315     if(WEP_CVAR(machinegun, mode) == 1)
316         ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, sustained_ammo);
317     else
318         ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, first_ammo);
319
320     if(WEP_CVAR(machinegun, reload_ammo))
321     {
322         if(WEP_CVAR(machinegun, mode) == 1)
323             ammo_amount += actor.(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, sustained_ammo);
324         else
325             ammo_amount += actor.(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, first_ammo);
326     }
327     return ammo_amount;
328 }
329 METHOD(MachineGun, wr_checkammo2, bool(entity thiswep, entity actor))
330 {
331     float ammo_amount;
332     if(WEP_CVAR(machinegun, mode) == 1)
333         ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, burst_ammo);
334     else
335         ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, first_ammo);
336
337     if(WEP_CVAR(machinegun, reload_ammo))
338     {
339         if(WEP_CVAR(machinegun, mode) == 1)
340             ammo_amount += actor.(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, burst_ammo);
341         else
342             ammo_amount += actor.(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, first_ammo);
343     }
344     return ammo_amount;
345 }
346 METHOD(MachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
347 {
348     W_Reload(actor, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND_RELOAD);
349 }
350 METHOD(MachineGun, wr_suicidemessage, Notification(entity thiswep))
351 {
352     return WEAPON_THINKING_WITH_PORTALS;
353 }
354 METHOD(MachineGun, wr_killmessage, Notification(entity thiswep))
355 {
356     if(w_deathtype & HITTYPE_SECONDARY)
357         return WEAPON_MACHINEGUN_MURDER_SNIPE;
358     else
359         return WEAPON_MACHINEGUN_MURDER_SPRAY;
360 }
361
362 #endif
363 #ifdef CSQC
364
365 METHOD(MachineGun, wr_impacteffect, void(entity thiswep, entity actor))
366 {
367     vector org2;
368     org2 = w_org + w_backoff * 2;
369     pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
370     if(!w_issilent)
371         sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM);
372 }
373
374 #endif
375 #endif