]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_sniperrifle.qc
Properly implement the new check for all weapons (hopefully, only testing can tell)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_sniperrifle.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(SNIPERRIFLE, w_sniperrifle, IT_NAILS, 7, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "sniperrifle", _("Sniper Rifle"))
3 #else
4 #ifdef SVQC
5 //Sniper rifle Primary mode: manually operated bolt*, Secondary: full automatic**
6 //* Manually operating the bolt means that all the power of the gas is used to propell the bullet. In this mode the bolt is prevented from moving backwards in response to the firing of the bullet.
7 //** In fully automatic mode some of the gas is used to extract and reload the next cartrige, thus there is less power and range.
8
9 .float sniperrifle_accumulator;
10
11 void W_SniperRifle_SetAmmoCounter()
12 {
13         // set clip_load to the weapon we have switched to, if the gun uses reloading
14         if(!autocvar_g_balance_sniperrifle_reload_ammo)
15                 self.clip_load = 0; // also keeps crosshair ammo from displaying
16         else
17         {
18                 self.clip_load = self.sniperrifle_load;
19                 self.clip_size = autocvar_g_balance_sniperrifle_reload_ammo; // for the crosshair ammo display
20         }
21 }
22
23 void W_SniperRifle_ReloadedAndReady()
24 {
25         float t;
26
27         // now do the ammo transfer
28         self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
29         while(self.clip_load < autocvar_g_balance_sniperrifle_reload_ammo && self.ammo_nails) // make sure we don't add more ammo than we have
30         {
31                 self.clip_load += 1;
32                 self.ammo_nails -= 1;
33         }
34         self.sniperrifle_load = self.clip_load;
35
36         t = ATTACK_FINISHED(self) - autocvar_g_balance_sniperrifle_reload_time - 1;
37         ATTACK_FINISHED(self) = t;
38         w_ready();
39 }
40
41 void W_SniperRifle_Reload()
42 {
43         // return if reloading is disabled for this weapon
44         if(!autocvar_g_balance_sniperrifle_reload_ammo)
45                 return;
46
47         if(!W_ReloadCheck(self.ammo_nails, min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo)))
48                 return;
49
50         float t;
51
52         sound (self, CHAN_WEAPON2, "weapons/reload.wav", VOL_BASE, ATTN_NORM);
53
54         t = max(time, ATTACK_FINISHED(self)) + autocvar_g_balance_sniperrifle_reload_time + 1;
55         ATTACK_FINISHED(self) = t;
56
57         weapon_thinkf(WFRAME_RELOAD, autocvar_g_balance_sniperrifle_reload_time, W_SniperRifle_ReloadedAndReady);
58
59         self.old_clip_load = self.clip_load;
60         self.clip_load = -1;
61 }
62
63 void W_SniperRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAddedDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant)
64 {
65         // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
66         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
67         {
68                 if(autocvar_g_balance_sniperrifle_reload_ammo)
69                 {
70                         self.clip_load -= pAmmo;
71                         self.sniperrifle_load = self.clip_load;
72                 }
73                 else
74                         self.ammo_nails -= pAmmo;
75         }
76
77         if(deathtype & HITTYPE_SECONDARY)
78                 W_SetupShot (self, autocvar_g_antilag_bullets && pSpeed >= autocvar_g_antilag_bullets, 2, "weapons/campingrifle_fire2.wav", CHAN_WEAPON, autocvar_g_balance_sniperrifle_secondary_damage + autocvar_g_balance_sniperrifle_secondary_headshotaddeddamage);
79         else
80                 W_SetupShot (self, autocvar_g_antilag_bullets && pSpeed >= autocvar_g_antilag_bullets, 2, "weapons/campingrifle_fire.wav", CHAN_WEAPON, autocvar_g_balance_sniperrifle_primary_damage + autocvar_g_balance_sniperrifle_primary_headshotaddeddamage);
81
82         pointparticles(particleeffectnum("sniperrifle_muzzleflash"), w_shotorg, w_shotdir * 2000, 1);
83
84         if(self.BUTTON_ZOOM) // if zoomed, shoot from the eye
85         {
86                 w_shotdir = v_forward;
87                 w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
88         }
89
90         if(deathtype & HITTYPE_SECONDARY)
91                 fireBallisticBullet(w_shotorg, w_shotdir, pSpread, pSpeed, pLifetime, pDamage, pHeadshotAddedDamage / pDamage, pForce, deathtype, (autocvar_g_balance_sniperrifle_secondary_tracer ? EF_RED : EF_BLUE), 1, pBulletConstant);
92         else
93                 fireBallisticBullet(w_shotorg, w_shotdir, pSpread, pSpeed, pLifetime, pDamage, pHeadshotAddedDamage / pDamage, pForce, deathtype, (autocvar_g_balance_sniperrifle_primary_tracer ? EF_RED : EF_BLUE), 1, pBulletConstant);
94         endFireBallisticBullet();
95
96         if (autocvar_g_casings >= 2)
97                 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, self);
98 }
99
100 void W_SniperRifle_Attack()
101 {
102         W_SniperRifle_FireBullet(autocvar_g_balance_sniperrifle_primary_spread, autocvar_g_balance_sniperrifle_primary_damage, autocvar_g_balance_sniperrifle_primary_headshotaddeddamage, autocvar_g_balance_sniperrifle_primary_force, autocvar_g_balance_sniperrifle_primary_speed, autocvar_g_balance_sniperrifle_primary_lifetime, autocvar_g_balance_sniperrifle_primary_ammo, WEP_SNIPERRIFLE, autocvar_g_balance_sniperrifle_primary_bulletconstant);
103 }
104
105 void W_SniperRifle_Attack2()
106 {
107         W_SniperRifle_FireBullet(autocvar_g_balance_sniperrifle_secondary_spread, autocvar_g_balance_sniperrifle_secondary_damage, autocvar_g_balance_sniperrifle_secondary_headshotaddeddamage, autocvar_g_balance_sniperrifle_secondary_force, autocvar_g_balance_sniperrifle_secondary_speed, autocvar_g_balance_sniperrifle_secondary_lifetime, autocvar_g_balance_sniperrifle_secondary_ammo, WEP_SNIPERRIFLE | HITTYPE_SECONDARY, autocvar_g_balance_sniperrifle_secondary_bulletconstant);
108 }
109
110 void spawnfunc_weapon_sniperrifle (void)
111 {
112         weapon_defaultspawnfunc(WEP_SNIPERRIFLE);
113 }
114
115 // compatibility alias
116 void spawnfunc_weapon_campingrifle (void)
117 {
118         spawnfunc_weapon_sniperrifle();
119 }
120
121 .void(void) sniperrifle_bullethail_attackfunc;
122 .float sniperrifle_bullethail_frame;
123 .float sniperrifle_bullethail_animtime;
124 .float sniperrifle_bullethail_refire;
125 void W_SniperRifle_BulletHail_Continue()
126 {
127         float r, sw, af;
128
129         sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
130         af = ATTACK_FINISHED(self);
131         self.switchweapon = self.weapon;
132         ATTACK_FINISHED(self) = time;
133         print(ftos(self.ammo_nails), "\n");
134         r = weapon_prepareattack(self.sniperrifle_bullethail_frame == WFRAME_FIRE2, self.sniperrifle_bullethail_refire);
135         if(self.switchweapon == self.weapon)
136                 self.switchweapon = sw;
137         if(r)
138         {
139                 self.sniperrifle_bullethail_attackfunc();
140                 weapon_thinkf(self.sniperrifle_bullethail_frame, self.sniperrifle_bullethail_animtime, W_SniperRifle_BulletHail_Continue);
141                 print("thinkf set\n");
142         }
143         else
144         {
145                 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
146                 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
147         }
148 }
149
150 void W_SniperRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
151 {
152         // if we get here, we have at least one bullet to fire
153         AttackFunc();
154         if(mode)
155         {
156                 // continue hail
157                 self.sniperrifle_bullethail_attackfunc = AttackFunc;
158                 self.sniperrifle_bullethail_frame = fr;
159                 self.sniperrifle_bullethail_animtime = animtime;
160                 self.sniperrifle_bullethail_refire = refire;
161                 weapon_thinkf(fr, animtime, W_SniperRifle_BulletHail_Continue);
162         }
163         else
164         {
165                 // just one shot
166                 weapon_thinkf(fr, animtime, w_ready);
167         }
168 }
169
170 .float bot_secondary_sniperriflemooth;
171 float w_sniperrifle(float req)
172 {
173         float ammo_amount;
174
175         if (req == WR_AIM)
176         {
177                 self.BUTTON_ATCK=FALSE;
178                 self.BUTTON_ATCK2=FALSE;
179                 if(vlen(self.origin-self.enemy.origin) > 1000)
180                         self.bot_secondary_sniperriflemooth = 0;
181                 if(self.bot_secondary_sniperriflemooth == 0)
182                 {
183                         if(bot_aim(autocvar_g_balance_sniperrifle_primary_speed, 0, autocvar_g_balance_sniperrifle_primary_lifetime, TRUE))
184                         {
185                                 self.BUTTON_ATCK = TRUE;
186                                 if(random() < 0.01) self.bot_secondary_sniperriflemooth = 1;
187                         }
188                 }
189                 else
190                 {
191                         if(bot_aim(autocvar_g_balance_sniperrifle_secondary_speed, 0, autocvar_g_balance_sniperrifle_secondary_lifetime, TRUE))
192                         {
193                                 self.BUTTON_ATCK2 = TRUE;
194                                 if(random() < 0.03) self.bot_secondary_sniperriflemooth = 0;
195                         }
196                 }
197         }
198         else if (req == WR_THINK)
199         {
200                 if(autocvar_g_balance_sniperrifle_reload_ammo && self.clip_load < min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo)) // forced reload
201             W_SniperRifle_Reload();
202                 else
203                 {
204                         self.sniperrifle_accumulator = bound(time - autocvar_g_balance_sniperrifle_bursttime, self.sniperrifle_accumulator, time);
205                         if (self.BUTTON_ATCK)
206                         if (weapon_prepareattack_check(0, autocvar_g_balance_sniperrifle_primary_refire))
207                         if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_primary_burstcost)
208                         {
209                                 weapon_prepareattack_do(0, autocvar_g_balance_sniperrifle_primary_refire);
210                                 W_SniperRifle_BulletHail(autocvar_g_balance_sniperrifle_primary_bullethail, W_SniperRifle_Attack, WFRAME_FIRE1, autocvar_g_balance_sniperrifle_primary_animtime, autocvar_g_balance_sniperrifle_primary_refire);
211                                 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_primary_burstcost;
212                         }
213                         if (self.BUTTON_ATCK2)
214                         {       
215                                 if (autocvar_g_balance_sniperrifle_secondary)
216                                 {
217                     if(autocvar_g_balance_sniperrifle_secondary_reload)
218                         W_SniperRifle_Reload();
219                     else
220                     {
221                         if (weapon_prepareattack_check(1, autocvar_g_balance_sniperrifle_secondary_refire))
222                         if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_secondary_burstcost)
223                         {
224                             weapon_prepareattack_do(1, autocvar_g_balance_sniperrifle_secondary_refire);
225                             W_SniperRifle_BulletHail(autocvar_g_balance_sniperrifle_secondary_bullethail, W_SniperRifle_Attack2, WFRAME_FIRE2, autocvar_g_balance_sniperrifle_secondary_animtime, autocvar_g_balance_sniperrifle_primary_refire);
226                             self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_secondary_burstcost;
227                         }
228                     }
229                                 }
230                         }
231                 }
232         if(self.wish_reload)
233         {
234             if(self.switchweapon == self.weapon)
235             {
236                 if(self.weaponentity.state == WS_READY)
237                 {
238                     self.wish_reload = 0;
239                     W_SniperRifle_Reload();
240                 }
241             }
242         }
243         }
244         else if (req == WR_PRECACHE)
245         {
246                 precache_model ("models/weapons/g_campingrifle.md3");
247                 precache_model ("models/weapons/v_campingrifle.md3");
248                 precache_model ("models/weapons/h_campingrifle.iqm");
249                 precache_sound ("weapons/campingrifle_fire.wav");
250                 precache_sound ("weapons/campingrifle_fire2.wav");
251                 precache_sound ("weapons/reload.wav");
252         }
253         else if (req == WR_SETUP)
254         {
255                 weapon_setup(WEP_SNIPERRIFLE);
256                 W_SniperRifle_SetAmmoCounter();
257         }
258         else if (req == WR_CHECKAMMO1)
259         {
260                 ammo_amount = self.ammo_cells >= autocvar_g_balance_sniperrifle_primary_ammo;
261                 ammo_amount += (autocvar_g_balance_sniperrifle_reload_ammo && self.sniperrifle_load >= autocvar_g_balance_sniperrifle_primary_ammo);
262                 return ammo_amount;
263         }
264         else if (req == WR_CHECKAMMO2)
265         {
266                 ammo_amount = self.ammo_cells >= autocvar_g_balance_sniperrifle_secondary_ammo;
267                 ammo_amount += (autocvar_g_balance_sniperrifle_reload_ammo && self.sniperrifle_load >= autocvar_g_balance_sniperrifle_secondary_ammo);
268                 return ammo_amount;
269         }
270         else if (req == WR_RELOAD)
271         {
272                 W_SniperRifle_Reload();
273         }
274         else if (req == WR_RESETPLAYER)
275         {
276                 self.sniperrifle_accumulator = time - autocvar_g_balance_sniperrifle_bursttime;
277                 self.clip_load = autocvar_g_balance_sniperrifle_reload_ammo;
278         }
279         return TRUE;
280 };
281 #endif
282 #ifdef CSQC
283 float w_sniperrifle(float req)
284 {
285         if(req == WR_IMPACTEFFECT)
286         {
287                 vector org2;
288                 org2 = w_org + w_backoff * 2;
289                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
290                 if(!w_issilent)
291                 {
292                         if(w_random < 0.2)
293                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
294                         else if(w_random < 0.4)
295                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
296                         else if(w_random < 0.5)
297                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
298                 }
299         }
300         else if(req == WR_PRECACHE)
301         {
302                 precache_sound("weapons/ric1.wav");
303                 precache_sound("weapons/ric2.wav");
304                 precache_sound("weapons/ric3.wav");
305         }
306         else if (req == WR_SUICIDEMESSAGE)
307         {
308                 if(w_deathtype & HITTYPE_SECONDARY)
309                         w_deathtypestring = "%s shot themself automatically";
310                 else
311                         w_deathtypestring = "%s sniped themself somehow";
312         }
313         else if (req == WR_KILLMESSAGE)
314         {
315                 if(w_deathtype & HITTYPE_SECONDARY)
316                 {
317                         if(w_deathtype & HITTYPE_BOUNCE)
318                                 w_deathtypestring = "%s failed to hide from %s's bullet hail";
319                         else
320                                 w_deathtypestring = "%s died in %s's bullet hail";
321                 }
322                 else
323                 {
324                         if(w_deathtype & HITTYPE_BOUNCE)
325                         {
326                                 // TODO special headshot message here too?
327                                 w_deathtypestring = "%s failed to hide from %s's rifle";
328                         }
329                         else
330                         {
331                                 if(w_deathtype & HITTYPE_HEADSHOT)
332                                         w_deathtypestring = "%s got hit in the head by %s";
333                                 else
334                                         w_deathtypestring = "%s was sniped by %s";
335                         }
336                 }
337         }
338         return TRUE;
339 }
340 #endif
341 #endif