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