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, autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo))
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)
277 if(autocvar_g_balance_sniperrifle_reload_ammo)
278 return self.sniperrifle_load;
280 return self.ammo_nails >= autocvar_g_balance_sniperrifle_primary_ammo;
282 else if (req == WR_CHECKAMMO2)
284 if(autocvar_g_balance_sniperrifle_reload_ammo)
285 return self.sniperrifle_load;
287 return self.ammo_nails >= autocvar_g_balance_sniperrifle_secondary_ammo;
289 else if (req == WR_RELOAD)
291 W_SniperRifle_Reload();
293 else if (req == WR_RESETPLAYER)
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);
303 float w_sniperrifle(float req)
305 if(req == WR_IMPACTEFFECT)
308 org2 = w_org + w_backoff * 2;
309 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
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);
320 else if(req == WR_PRECACHE)
322 precache_sound("weapons/ric1.wav");
323 precache_sound("weapons/ric2.wav");
324 precache_sound("weapons/ric3.wav");
326 else if (req == WR_SUICIDEMESSAGE)
328 if(w_deathtype & HITTYPE_SECONDARY)
329 w_deathtypestring = "%s shot themself automatically";
331 w_deathtypestring = "%s sniped themself somehow";
333 else if (req == WR_KILLMESSAGE)
335 if(w_deathtype & HITTYPE_SECONDARY)
337 if(w_deathtype & HITTYPE_BOUNCE)
338 w_deathtypestring = "%s failed to hide from %s's bullet hail";
340 w_deathtypestring = "%s died in %s's bullet hail";
344 if(w_deathtype & HITTYPE_BOUNCE)
346 // TODO special headshot message here too?
347 w_deathtypestring = "%s failed to hide from %s's rifle";
351 if(w_deathtype & HITTYPE_HEADSHOT)
352 w_deathtypestring = "%s got hit in the head by %s";
354 w_deathtypestring = "%s was sniped by %s";