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_reloadtime - 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_reloadtime + 1;
53 ATTACK_FINISHED(self) = t;
55 weapon_thinkf(WFRAME_RELOAD, autocvar_g_balance_sniperrifle_reloadtime, 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_magazinecapacity;
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()
138 if(self.ammo_counter <= 0)
139 return; // reloading, so we are done
140 sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
141 af = ATTACK_FINISHED(self);
142 self.switchweapon = self.weapon;
143 ATTACK_FINISHED(self) = time;
144 print(ftos(self.ammo_nails), "\n");
145 r = weapon_prepareattack(self.sniperrifle_bullethail_frame == WFRAME_FIRE2, self.sniperrifle_bullethail_refire);
146 if(self.switchweapon == self.weapon)
147 self.switchweapon = sw;
150 self.sniperrifle_bullethail_attackfunc();
151 weapon_thinkf(self.sniperrifle_bullethail_frame, self.sniperrifle_bullethail_animtime, W_SniperRifle_BulletHail_Continue);
152 print("thinkf set\n");
156 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
157 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
161 void W_SniperRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
163 // if we get here, we have at least one bullet to fire
168 self.sniperrifle_bullethail_attackfunc = AttackFunc;
169 self.sniperrifle_bullethail_frame = fr;
170 self.sniperrifle_bullethail_animtime = animtime;
171 self.sniperrifle_bullethail_refire = refire;
172 weapon_thinkf(fr, animtime, W_SniperRifle_BulletHail_Continue);
177 weapon_thinkf(fr, animtime, w_ready);
181 .float bot_secondary_sniperriflemooth;
182 float w_sniperrifle(float req)
187 self.BUTTON_ATCK=FALSE;
188 self.BUTTON_ATCK2=FALSE;
189 if(vlen(self.origin-self.enemy.origin) > 1000)
190 self.bot_secondary_sniperriflemooth = 0;
191 if(self.bot_secondary_sniperriflemooth == 0)
193 if(bot_aim(autocvar_g_balance_sniperrifle_primary_speed, 0, autocvar_g_balance_sniperrifle_primary_lifetime, TRUE))
195 self.BUTTON_ATCK = TRUE;
196 if(random() < 0.01) self.bot_secondary_sniperriflemooth = 1;
201 if(bot_aim(autocvar_g_balance_sniperrifle_secondary_speed, 0, autocvar_g_balance_sniperrifle_secondary_lifetime, TRUE))
203 self.BUTTON_ATCK2 = TRUE;
204 if(random() < 0.03) self.bot_secondary_sniperriflemooth = 0;
208 else if (req == WR_THINK)
210 W_SniperRifle_SetAmmoCounter();
211 if(self.ammo_counter <= 0) // forced reload
212 W_SniperRifle_Reload();
215 self.sniperrifle_accumulator = bound(time - autocvar_g_balance_sniperrifle_bursttime, self.sniperrifle_accumulator, time);
216 if (self.BUTTON_ATCK)
217 if (weapon_prepareattack_check(0, autocvar_g_balance_sniperrifle_primary_refire))
218 if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_primary_burstcost)
220 weapon_prepareattack_do(0, autocvar_g_balance_sniperrifle_primary_refire);
221 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);
222 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_primary_burstcost;
224 if (self.BUTTON_ATCK2)
226 if (autocvar_g_balance_sniperrifle_secondary)
228 if(autocvar_g_balance_sniperrifle_secondary_reload)
229 W_SniperRifle_Reload();
232 if (weapon_prepareattack_check(1, autocvar_g_balance_sniperrifle_secondary_refire))
233 if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_secondary_burstcost)
235 weapon_prepareattack_do(1, autocvar_g_balance_sniperrifle_secondary_refire);
236 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);
237 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_secondary_burstcost;
245 if(self.switchweapon == self.weapon)
247 if(self.weaponentity.state == WS_READY)
249 self.wish_reload = 0;
250 W_SniperRifle_Reload(self.ammo_nails);
255 else if (req == WR_PRECACHE)
257 precache_model ("models/weapons/g_campingrifle.md3");
258 precache_model ("models/weapons/v_campingrifle.md3");
259 precache_model ("models/weapons/h_campingrifle.iqm");
260 precache_sound ("weapons/campingrifle_reload.wav");
261 precache_sound ("weapons/campingrifle_fire.wav");
262 precache_sound ("weapons/campingrifle_fire2.wav");
264 else if (req == WR_SETUP)
266 weapon_setup(WEP_SNIPERRIFLE);
268 full = W_SniperRifle_CheckMaxBullets(TRUE);
269 if(autocvar_g_balance_sniperrifle_auto_reload_on_switch)
271 self.ammo_counter = -1;
273 else if (req == WR_CHECKAMMO1)
274 return self.ammo_nails >= autocvar_g_balance_sniperrifle_primary_ammo;
275 else if (req == WR_CHECKAMMO2)
276 return self.ammo_nails >= autocvar_g_balance_sniperrifle_secondary_ammo;
277 else if (req == WR_RELOAD)
279 W_SniperRifle_Reload();
281 else if (req == WR_RESETPLAYER)
283 self.sniperrifle_accumulator = time - autocvar_g_balance_sniperrifle_bursttime;
284 self.ammo_counter = autocvar_g_balance_sniperrifle_magazinecapacity;
285 W_SniperRifle_CheckMaxBullets(FALSE);
291 float w_sniperrifle(float req)
293 if(req == WR_IMPACTEFFECT)
296 org2 = w_org + w_backoff * 2;
297 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
301 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
302 else if(w_random < 0.4)
303 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
304 else if(w_random < 0.5)
305 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
308 else if(req == WR_PRECACHE)
310 precache_sound("weapons/ric1.wav");
311 precache_sound("weapons/ric2.wav");
312 precache_sound("weapons/ric3.wav");
314 else if (req == WR_SUICIDEMESSAGE)
316 if(w_deathtype & HITTYPE_SECONDARY)
317 w_deathtypestring = "%s shot themself automatically";
319 w_deathtypestring = "%s sniped themself somehow";
321 else if (req == WR_KILLMESSAGE)
323 if(w_deathtype & HITTYPE_SECONDARY)
325 if(w_deathtype & HITTYPE_BOUNCE)
326 w_deathtypestring = "%s failed to hide from %s's bullet hail";
328 w_deathtypestring = "%s died in %s's bullet hail";
332 if(w_deathtype & HITTYPE_BOUNCE)
334 // TODO special headshot message here too?
335 w_deathtypestring = "%s failed to hide from %s's rifle";
339 if(w_deathtype & HITTYPE_HEADSHOT)
340 w_deathtypestring = "%s got hit in the head by %s";
342 w_deathtypestring = "%s was sniped by %s";