2 REGISTER_WEAPON(SNIPERRIFLE, w_sniperrifle, IT_NAILS, 7, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "sniperrifle", _("Sniper Rifle"))
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.
9 .float sniperrifle_accumulator;
10 .float sniperrifle_load;
12 void W_SniperRifle_SetAmmoCounter()
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
18 self.ammo_counter = self.sniperrifle_load;
21 void W_SniperRifle_ReloadedAndReady()
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
29 self.ammo_counter += 1;
32 self.sniperrifle_load = self.ammo_counter;
34 t = ATTACK_FINISHED(self) - autocvar_g_balance_sniperrifle_reload_time - 1;
35 ATTACK_FINISHED(self) = t;
39 void W_SniperRifle_Reload()
41 // reloading is disabled for this weapon
42 if(!autocvar_g_balance_sniperrifle_reload_ammo)
47 if(!W_ReloadCheck(self.ammo_nails))
50 sound (self, CHAN_WEAPON2, "weapons/campingrifle_reload.wav", VOL_BASE, ATTN_NORM);
52 t = max(time, ATTACK_FINISHED(self)) + autocvar_g_balance_sniperrifle_reload_time + 1;
53 ATTACK_FINISHED(self) = t;
55 weapon_thinkf(WFRAME_RELOAD, autocvar_g_balance_sniperrifle_reload_time, W_SniperRifle_ReloadedAndReady);
57 self.old_ammo_counter = self.ammo_counter;
58 self.ammo_counter = -1;
61 float W_SniperRifle_CheckMaxBullets(float checkammo)
64 maxbulls = autocvar_g_balance_sniperrifle_reload_ammo;
66 maxbulls = 8; // match HUD
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);
75 void W_SniperRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAddedDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant)
77 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
79 if(!autocvar_g_balance_sniperrifle_reload_ammo)
80 self.ammo_nails -= pAmmo;
82 self.sniperrifle_load -= pAmmo;
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);
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);
90 pointparticles(particleeffectnum("sniperrifle_muzzleflash"), w_shotorg, w_shotdir * 2000, 1);
92 if(self.BUTTON_ZOOM) // if zoomed, shoot from the eye
94 w_shotdir = v_forward;
95 w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
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);
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();
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);
107 self.ammo_counter = self.ammo_counter - 1;
110 void W_SniperRifle_Attack()
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);
115 void W_SniperRifle_Attack2()
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);
120 void spawnfunc_weapon_sniperrifle (void)
122 weapon_defaultspawnfunc(WEP_SNIPERRIFLE);
125 // compatibility alias
126 void spawnfunc_weapon_campingrifle (void)
128 spawnfunc_weapon_sniperrifle();
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()
139 if(autocvar_g_balance_sniperrifle_reload_ammo && self.ammo_counter <= 0)
140 return; // reloading, so we are done
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;
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");
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");
163 void W_SniperRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
165 // if we get here, we have at least one bullet to fire
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);
179 weapon_thinkf(fr, animtime, w_ready);
183 .float bot_secondary_sniperriflemooth;
184 float w_sniperrifle(float req)
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)
195 if(bot_aim(autocvar_g_balance_sniperrifle_primary_speed, 0, autocvar_g_balance_sniperrifle_primary_lifetime, TRUE))
197 self.BUTTON_ATCK = TRUE;
198 if(random() < 0.01) self.bot_secondary_sniperriflemooth = 1;
203 if(bot_aim(autocvar_g_balance_sniperrifle_secondary_speed, 0, autocvar_g_balance_sniperrifle_secondary_lifetime, TRUE))
205 self.BUTTON_ATCK2 = TRUE;
206 if(random() < 0.03) self.bot_secondary_sniperriflemooth = 0;
210 else if (req == WR_THINK)
212 W_SniperRifle_SetAmmoCounter();
213 if(autocvar_g_balance_sniperrifle_reload_ammo && self.ammo_counter <= 0) // forced reload
214 W_SniperRifle_Reload();
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)
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;
226 if (self.BUTTON_ATCK2)
228 if (autocvar_g_balance_sniperrifle_secondary)
230 if(autocvar_g_balance_sniperrifle_secondary_reload)
231 W_SniperRifle_Reload();
234 if (weapon_prepareattack_check(1, autocvar_g_balance_sniperrifle_secondary_refire))
235 if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_secondary_burstcost)
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;
247 if(self.switchweapon == self.weapon)
249 if(self.weaponentity.state == WS_READY)
251 self.wish_reload = 0;
252 W_SniperRifle_Reload(self.ammo_nails);
257 else if (req == WR_PRECACHE)
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");
266 else if (req == WR_SETUP)
268 weapon_setup(WEP_SNIPERRIFLE);
270 full = W_SniperRifle_CheckMaxBullets(TRUE);
271 if(autocvar_g_balance_sniperrifle_auto_reload_on_switch)
273 self.ammo_counter = -1;
275 else if (req == WR_CHECKAMMO1)
276 return self.ammo_nails >= autocvar_g_balance_sniperrifle_primary_ammo;
277 else if (req == WR_CHECKAMMO2)
278 return self.ammo_nails >= autocvar_g_balance_sniperrifle_secondary_ammo;
279 else if (req == WR_RELOAD)
281 W_SniperRifle_Reload();
283 else if (req == WR_RESETPLAYER)
285 self.sniperrifle_accumulator = time - autocvar_g_balance_sniperrifle_bursttime;
286 self.ammo_counter = autocvar_g_balance_sniperrifle_reload_ammo;
287 W_SniperRifle_CheckMaxBullets(FALSE);
293 float w_sniperrifle(float req)
295 if(req == WR_IMPACTEFFECT)
298 org2 = w_org + w_backoff * 2;
299 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
303 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
304 else if(w_random < 0.4)
305 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
306 else if(w_random < 0.5)
307 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
310 else if(req == WR_PRECACHE)
312 precache_sound("weapons/ric1.wav");
313 precache_sound("weapons/ric2.wav");
314 precache_sound("weapons/ric3.wav");
316 else if (req == WR_SUICIDEMESSAGE)
318 if(w_deathtype & HITTYPE_SECONDARY)
319 w_deathtypestring = "%s shot themself automatically";
321 w_deathtypestring = "%s sniped themself somehow";
323 else if (req == WR_KILLMESSAGE)
325 if(w_deathtype & HITTYPE_SECONDARY)
327 if(w_deathtype & HITTYPE_BOUNCE)
328 w_deathtypestring = "%s failed to hide from %s's bullet hail";
330 w_deathtypestring = "%s died in %s's bullet hail";
334 if(w_deathtype & HITTYPE_BOUNCE)
336 // TODO special headshot message here too?
337 w_deathtypestring = "%s failed to hide from %s's rifle";
341 if(w_deathtype & HITTYPE_HEADSHOT)
342 w_deathtypestring = "%s got hit in the head by %s";
344 w_deathtypestring = "%s was sniped by %s";