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;
11 float W_SniperRifle_CheckMaxBullets(float checkammo)
14 maxbulls = autocvar_g_balance_sniperrifle_magazinecapacity;
16 maxbulls = 8; // match HUD
18 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
19 maxbulls = min(maxbulls, floor(self.ammo_nails / min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo)));
20 if(self.sniperrifle_bulletcounter > maxbulls || !autocvar_g_balance_sniperrifle_magazinecapacity)
21 self.sniperrifle_bulletcounter = maxbulls;
22 return (self.sniperrifle_bulletcounter == maxbulls);
25 void W_SniperRifle_ReloadedAndReady()
28 self.sniperrifle_bulletcounter = autocvar_g_balance_sniperrifle_magazinecapacity;
29 W_SniperRifle_CheckMaxBullets(TRUE);
30 t = ATTACK_FINISHED(self) - autocvar_g_balance_sniperrifle_reloadtime - 1;
31 ATTACK_FINISHED(self) = t;
35 float W_SniperRifle_Reload()
39 W_SniperRifle_CheckMaxBullets(TRUE);
41 if(self.ammo_nails < min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo)) // when we get here, bulletcounter must be 0 or -1
43 print("cannot reload... not enough bullets\n");
44 self.sniperrifle_bulletcounter = -1; // reload later
45 W_SwitchToOtherWeapon(self);
49 if (self.sniperrifle_bulletcounter >= autocvar_g_balance_sniperrifle_magazinecapacity)
52 if (self.weaponentity)
54 if (self.weaponentity.wframe == WFRAME_RELOAD)
57 // allow to switch away while reloading, but this will cause a new reload!
58 self.weaponentity.state = WS_READY;
61 sound (self, CHAN_WEAPON2, "weapons/campingrifle_reload.wav", VOL_BASE, ATTN_NORM);
63 t = max(time, ATTACK_FINISHED(self)) + autocvar_g_balance_sniperrifle_reloadtime + 1;
64 ATTACK_FINISHED(self) = t;
66 weapon_thinkf(WFRAME_RELOAD, autocvar_g_balance_sniperrifle_reloadtime, W_SniperRifle_ReloadedAndReady);
68 self.sniperrifle_bulletcounter = -1;
73 void W_SniperRifle_CheckReloadAndReady()
76 if(self.sniperrifle_bulletcounter <= 0)
77 if(W_SniperRifle_Reload())
81 void W_SniperRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAddedDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant)
83 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
84 self.ammo_nails -= pAmmo;
86 if(deathtype & HITTYPE_SECONDARY)
87 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);
89 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);
91 pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 2000, 1);
93 if(self.BUTTON_ZOOM) // if zoomed, shoot from the eye
95 w_shotdir = v_forward;
96 w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
99 if(deathtype & HITTYPE_SECONDARY)
100 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);
102 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);
103 endFireBallisticBullet();
105 if (autocvar_g_casings >= 2)
106 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);
108 self.sniperrifle_bulletcounter = self.sniperrifle_bulletcounter - 1;
109 W_SniperRifle_CheckMaxBullets(TRUE);
112 void W_SniperRifle_Attack()
114 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);
117 void W_SniperRifle_Attack2()
119 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);
122 void spawnfunc_weapon_sniperrifle (void)
124 weapon_defaultspawnfunc(WEP_SNIPERRIFLE);
127 // compatibility alias
128 void spawnfunc_weapon_campingrifle (void)
130 spawnfunc_weapon_sniperrifle();
133 .void(void) sniperrifle_bullethail_attackfunc;
134 .float sniperrifle_bullethail_frame;
135 .float sniperrifle_bullethail_animtime;
136 .float sniperrifle_bullethail_refire;
137 void W_SniperRifle_BulletHail_Continue()
140 W_SniperRifle_CheckReloadAndReady();
141 if(self.sniperrifle_bulletcounter < 0)
142 return; // reloading, so we are done
143 sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
144 af = ATTACK_FINISHED(self);
145 self.switchweapon = self.weapon;
146 ATTACK_FINISHED(self) = time;
147 print(ftos(self.ammo_nails), "\n");
148 r = weapon_prepareattack(self.sniperrifle_bullethail_frame == WFRAME_FIRE2, self.sniperrifle_bullethail_refire);
149 if(self.switchweapon == self.weapon)
150 self.switchweapon = sw;
153 self.sniperrifle_bullethail_attackfunc();
154 weapon_thinkf(self.sniperrifle_bullethail_frame, self.sniperrifle_bullethail_animtime, W_SniperRifle_BulletHail_Continue);
155 print("thinkf set\n");
159 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
160 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
164 void W_SniperRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
166 // if we get here, we have at least one bullet to fire
171 self.sniperrifle_bullethail_attackfunc = AttackFunc;
172 self.sniperrifle_bullethail_frame = fr;
173 self.sniperrifle_bullethail_animtime = animtime;
174 self.sniperrifle_bullethail_refire = refire;
175 weapon_thinkf(fr, animtime, W_SniperRifle_BulletHail_Continue);
180 weapon_thinkf(fr, animtime, W_SniperRifle_CheckReloadAndReady);
184 .float bot_secondary_sniperriflemooth;
185 float w_sniperrifle(float req)
190 self.BUTTON_ATCK=FALSE;
191 self.BUTTON_ATCK2=FALSE;
192 if(vlen(self.origin-self.enemy.origin) > 1000)
193 self.bot_secondary_sniperriflemooth = 0;
194 if(self.bot_secondary_sniperriflemooth == 0)
196 if(bot_aim(autocvar_g_balance_sniperrifle_primary_speed, 0, autocvar_g_balance_sniperrifle_primary_lifetime, TRUE))
198 self.BUTTON_ATCK = TRUE;
199 if(random() < 0.01) self.bot_secondary_sniperriflemooth = 1;
204 if(bot_aim(autocvar_g_balance_sniperrifle_secondary_speed, 0, autocvar_g_balance_sniperrifle_secondary_lifetime, TRUE))
206 self.BUTTON_ATCK2 = TRUE;
207 if(random() < 0.03) self.bot_secondary_sniperriflemooth = 0;
211 else if (req == WR_THINK)
213 if(self.sniperrifle_bulletcounter < 0) // forced reload (e.g. because interrupted)
215 self.wish_reload = 1;
219 self.sniperrifle_accumulator = bound(time - autocvar_g_balance_sniperrifle_bursttime, self.sniperrifle_accumulator, time);
220 if (self.BUTTON_ATCK)
221 if (weapon_prepareattack_check(0, autocvar_g_balance_sniperrifle_primary_refire))
222 if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_primary_burstcost)
224 weapon_prepareattack_do(0, autocvar_g_balance_sniperrifle_primary_refire);
225 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);
226 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_primary_burstcost;
228 if (self.BUTTON_ATCK2)
230 if (autocvar_g_balance_sniperrifle_secondary)
232 if(autocvar_g_balance_sniperrifle_secondary_reload)
233 self.wish_reload = 1;
236 if (weapon_prepareattack_check(1, autocvar_g_balance_sniperrifle_secondary_refire))
237 if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_secondary_burstcost)
239 weapon_prepareattack_do(1, autocvar_g_balance_sniperrifle_secondary_refire);
240 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);
241 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_secondary_burstcost;
249 if(self.switchweapon == self.weapon)
251 if(self.weaponentity.state == WS_READY)
253 self.wish_reload = 0;
254 W_SniperRifle_Reload();
259 else if (req == WR_PRECACHE)
261 precache_model ("models/weapons/g_campingrifle.md3");
262 precache_model ("models/weapons/v_campingrifle.md3");
263 precache_model ("models/weapons/h_campingrifle.iqm");
264 precache_sound ("weapons/campingrifle_reload.wav");
265 precache_sound ("weapons/campingrifle_fire.wav");
266 precache_sound ("weapons/campingrifle_fire2.wav");
268 else if (req == WR_SETUP)
270 weapon_setup(WEP_SNIPERRIFLE);
272 full = W_SniperRifle_CheckMaxBullets(TRUE);
273 if(autocvar_g_balance_sniperrifle_auto_reload_on_switch)
275 self.sniperrifle_bulletcounter = -1;
277 else if (req == WR_CHECKAMMO1)
278 return self.ammo_nails >= autocvar_g_balance_sniperrifle_primary_ammo;
279 else if (req == WR_CHECKAMMO2)
280 return self.ammo_nails >= autocvar_g_balance_sniperrifle_secondary_ammo;
281 else if (req == WR_RELOAD)
283 self.wish_reload = 1;
285 else if (req == WR_RESETPLAYER)
287 self.sniperrifle_accumulator = time - autocvar_g_balance_sniperrifle_bursttime;
288 self.sniperrifle_bulletcounter = autocvar_g_balance_sniperrifle_magazinecapacity;
289 W_SniperRifle_CheckMaxBullets(FALSE);
295 float w_sniperrifle(float req)
297 if(req == WR_IMPACTEFFECT)
300 org2 = w_org + w_backoff * 2;
301 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
305 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
306 else if(w_random < 0.4)
307 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
308 else if(w_random < 0.5)
309 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
312 else if(req == WR_PRECACHE)
314 precache_sound("weapons/ric1.wav");
315 precache_sound("weapons/ric2.wav");
316 precache_sound("weapons/ric3.wav");
318 else if (req == WR_SUICIDEMESSAGE)
320 if(w_deathtype & HITTYPE_SECONDARY)
321 w_deathtypestring = "%s shot themself automatically";
323 w_deathtypestring = "%s sniped themself somehow";
325 else if (req == WR_KILLMESSAGE)
327 if(w_deathtype & HITTYPE_SECONDARY)
329 if(w_deathtype & HITTYPE_BOUNCE)
330 w_deathtypestring = "%s failed to hide from %s's bullet hail";
332 w_deathtypestring = "%s died in %s's bullet hail";
336 if(w_deathtype & HITTYPE_BOUNCE)
338 // TODO special headshot message here too?
339 w_deathtypestring = "%s failed to hide from %s's rifle";
343 if(w_deathtype & HITTYPE_HEADSHOT)
344 w_deathtypestring = "%s got hit in the head by %s";
346 w_deathtypestring = "%s was sniped by %s";