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 if(!autocvar_g_balance_sniperrifle_reload_ammo)
15 self.ammo_counter = 0; // also keeps the crosshair ammo from displaying
17 self.ammo_counter = self.sniperrifle_load;
20 void W_SniperRifle_Reload()
22 // reloading is disabled for this weapon
23 if(!autocvar_g_balance_shotgun_reload_ammo)
27 if(W_Reload(self.ammo_nails))
29 // now do the actual ammo transfer
30 for(self.sniperrifle_load; self.sniperrifle_load < autocvar_g_balance_shotgun_reload_ammo && self.ammo_nails; ++self.sniperrifle_load)
36 float W_SniperRifle_CheckMaxBullets(float checkammo)
39 maxbulls = autocvar_g_balance_sniperrifle_magazinecapacity;
41 maxbulls = 8; // match HUD
43 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
44 maxbulls = min(maxbulls, floor(self.ammo_nails / min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo)));
45 if(self.ammo_counter > maxbulls)
46 self.ammo_counter = maxbulls;
47 return (self.ammo_counter == maxbulls);
50 void W_SniperRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAddedDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant)
52 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
54 if(!autocvar_g_balance_sniperrifle_reload_ammo)
55 self.ammo_nails -= pAmmo;
57 self.sniperrifle_load -= pAmmo;
60 if(deathtype & HITTYPE_SECONDARY)
61 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);
63 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);
65 pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 2000, 1);
67 if(self.BUTTON_ZOOM) // if zoomed, shoot from the eye
69 w_shotdir = v_forward;
70 w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
73 if(deathtype & HITTYPE_SECONDARY)
74 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);
76 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);
77 endFireBallisticBullet();
79 if (autocvar_g_casings >= 2)
80 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);
82 self.ammo_counter = self.ammo_counter - 1;
85 void W_SniperRifle_Attack()
87 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);
90 void W_SniperRifle_Attack2()
92 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);
95 void spawnfunc_weapon_sniperrifle (void)
97 weapon_defaultspawnfunc(WEP_SNIPERRIFLE);
100 // compatibility alias
101 void spawnfunc_weapon_campingrifle (void)
103 spawnfunc_weapon_sniperrifle();
106 .void(void) sniperrifle_bullethail_attackfunc;
107 .float sniperrifle_bullethail_frame;
108 .float sniperrifle_bullethail_animtime;
109 .float sniperrifle_bullethail_refire;
110 void W_SniperRifle_BulletHail_Continue()
113 if(self.ammo_counter <= 0)
114 W_SniperRifle_Reload();
115 if(self.ammo_counter < 0)
116 return; // reloading, so we are done
117 sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
118 af = ATTACK_FINISHED(self);
119 self.switchweapon = self.weapon;
120 ATTACK_FINISHED(self) = time;
121 print(ftos(self.ammo_nails), "\n");
122 r = weapon_prepareattack(self.sniperrifle_bullethail_frame == WFRAME_FIRE2, self.sniperrifle_bullethail_refire);
123 if(self.switchweapon == self.weapon)
124 self.switchweapon = sw;
127 self.sniperrifle_bullethail_attackfunc();
128 weapon_thinkf(self.sniperrifle_bullethail_frame, self.sniperrifle_bullethail_animtime, W_SniperRifle_BulletHail_Continue);
129 print("thinkf set\n");
133 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
134 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
138 void W_SniperRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
140 // if we get here, we have at least one bullet to fire
145 self.sniperrifle_bullethail_attackfunc = AttackFunc;
146 self.sniperrifle_bullethail_frame = fr;
147 self.sniperrifle_bullethail_animtime = animtime;
148 self.sniperrifle_bullethail_refire = refire;
149 weapon_thinkf(fr, animtime, W_SniperRifle_BulletHail_Continue);
154 weapon_thinkf(fr, animtime, w_ready);
158 .float bot_secondary_sniperriflemooth;
159 float w_sniperrifle(float req)
164 self.BUTTON_ATCK=FALSE;
165 self.BUTTON_ATCK2=FALSE;
166 if(vlen(self.origin-self.enemy.origin) > 1000)
167 self.bot_secondary_sniperriflemooth = 0;
168 if(self.bot_secondary_sniperriflemooth == 0)
170 if(bot_aim(autocvar_g_balance_sniperrifle_primary_speed, 0, autocvar_g_balance_sniperrifle_primary_lifetime, TRUE))
172 self.BUTTON_ATCK = TRUE;
173 if(random() < 0.01) self.bot_secondary_sniperriflemooth = 1;
178 if(bot_aim(autocvar_g_balance_sniperrifle_secondary_speed, 0, autocvar_g_balance_sniperrifle_secondary_lifetime, TRUE))
180 self.BUTTON_ATCK2 = TRUE;
181 if(random() < 0.03) self.bot_secondary_sniperriflemooth = 0;
185 else if (req == WR_THINK)
187 W_SniperRifle_SetAmmoCounter();
188 if(self.ammo_counter < 0) // forced reload (e.g. because interrupted)
189 self.wish_reload = 1;
192 self.sniperrifle_accumulator = bound(time - autocvar_g_balance_sniperrifle_bursttime, self.sniperrifle_accumulator, time);
193 if (self.BUTTON_ATCK)
194 if (weapon_prepareattack_check(0, autocvar_g_balance_sniperrifle_primary_refire))
195 if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_primary_burstcost)
197 weapon_prepareattack_do(0, autocvar_g_balance_sniperrifle_primary_refire);
198 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);
199 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_primary_burstcost;
201 if (self.BUTTON_ATCK2)
203 if (autocvar_g_balance_sniperrifle_secondary)
205 if(autocvar_g_balance_sniperrifle_secondary_reload)
206 self.wish_reload = 1;
209 if (weapon_prepareattack_check(1, autocvar_g_balance_sniperrifle_secondary_refire))
210 if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_secondary_burstcost)
212 weapon_prepareattack_do(1, autocvar_g_balance_sniperrifle_secondary_refire);
213 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);
214 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_secondary_burstcost;
222 if(self.switchweapon == self.weapon)
224 if(self.weaponentity.state == WS_READY)
226 self.wish_reload = 0;
227 W_Reload(self.ammo_nails);
232 else if (req == WR_PRECACHE)
234 precache_model ("models/weapons/g_campingrifle.md3");
235 precache_model ("models/weapons/v_campingrifle.md3");
236 precache_model ("models/weapons/h_campingrifle.iqm");
237 precache_sound ("weapons/campingrifle_reload.wav");
238 precache_sound ("weapons/campingrifle_fire.wav");
239 precache_sound ("weapons/campingrifle_fire2.wav");
241 else if (req == WR_SETUP)
243 weapon_setup(WEP_SNIPERRIFLE);
245 full = W_SniperRifle_CheckMaxBullets(TRUE);
246 if(autocvar_g_balance_sniperrifle_auto_reload_on_switch)
248 self.ammo_counter = -1;
250 else if (req == WR_CHECKAMMO1)
251 return self.ammo_nails >= autocvar_g_balance_sniperrifle_primary_ammo;
252 else if (req == WR_CHECKAMMO2)
253 return self.ammo_nails >= autocvar_g_balance_sniperrifle_secondary_ammo;
254 else if (req == WR_RELOAD)
256 self.wish_reload = 1;
258 else if (req == WR_RESETPLAYER)
260 self.sniperrifle_accumulator = time - autocvar_g_balance_sniperrifle_bursttime;
261 self.ammo_counter = autocvar_g_balance_sniperrifle_magazinecapacity;
262 W_SniperRifle_CheckMaxBullets(FALSE);
268 float w_sniperrifle(float req)
270 if(req == WR_IMPACTEFFECT)
273 org2 = w_org + w_backoff * 2;
274 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
278 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
279 else if(w_random < 0.4)
280 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
281 else if(w_random < 0.5)
282 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
285 else if(req == WR_PRECACHE)
287 precache_sound("weapons/ric1.wav");
288 precache_sound("weapons/ric2.wav");
289 precache_sound("weapons/ric3.wav");
291 else if (req == WR_SUICIDEMESSAGE)
293 if(w_deathtype & HITTYPE_SECONDARY)
294 w_deathtypestring = "%s shot themself automatically";
296 w_deathtypestring = "%s sniped themself somehow";
298 else if (req == WR_KILLMESSAGE)
300 if(w_deathtype & HITTYPE_SECONDARY)
302 if(w_deathtype & HITTYPE_BOUNCE)
303 w_deathtypestring = "%s failed to hide from %s's bullet hail";
305 w_deathtypestring = "%s died in %s's bullet hail";
309 if(w_deathtype & HITTYPE_BOUNCE)
311 // TODO special headshot message here too?
312 w_deathtypestring = "%s failed to hide from %s's rifle";
316 if(w_deathtype & HITTYPE_HEADSHOT)
317 w_deathtypestring = "%s got hit in the head by %s";
319 w_deathtypestring = "%s was sniped by %s";