2 REGISTER_WEAPON(CAMPINGRIFLE, w_campingrifle, IT_NAILS, 7, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "campingrifle", "Sniper Rifle");
5 //Camping 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 campingrifle_accumulator;
11 float W_CampingRifle_CheckMaxBullets(float checkammo)
14 maxbulls = autocvar_g_balance_campingrifle_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_campingrifle_primary_ammo, autocvar_g_balance_campingrifle_secondary_ammo)));
20 if(self.campingrifle_bulletcounter > maxbulls || !autocvar_g_balance_campingrifle_magazinecapacity)
21 self.campingrifle_bulletcounter = maxbulls;
22 return (self.campingrifle_bulletcounter == maxbulls);
25 void W_CampingRifle_ReloadedAndReady()
28 self.campingrifle_bulletcounter = autocvar_g_balance_campingrifle_magazinecapacity;
29 W_CampingRifle_CheckMaxBullets(TRUE);
30 t = ATTACK_FINISHED(self) - autocvar_g_balance_campingrifle_reloadtime - 1;
31 ATTACK_FINISHED(self) = t;
35 float W_CampingRifle_Reload()
39 W_CampingRifle_CheckMaxBullets(TRUE);
41 if(self.ammo_nails < min(autocvar_g_balance_campingrifle_primary_ammo, autocvar_g_balance_campingrifle_secondary_ammo)) // when we get here, bulletcounter must be 0 or -1
43 print("cannot reload... not enough bullets\n");
44 self.campingrifle_bulletcounter = -1; // reload later
45 W_SwitchToOtherWeapon(self);
49 if (self.campingrifle_bulletcounter >= autocvar_g_balance_campingrifle_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_campingrifle_reloadtime + 1;
64 ATTACK_FINISHED(self) = t;
66 weapon_thinkf(WFRAME_RELOAD, autocvar_g_balance_campingrifle_reloadtime, W_CampingRifle_ReloadedAndReady);
68 self.campingrifle_bulletcounter = -1;
73 void W_CampingRifle_CheckReloadAndReady()
76 if(self.campingrifle_bulletcounter <= 0)
77 if(W_CampingRifle_Reload())
81 void W_CampingRifle_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_campingrifle_secondary_damage + autocvar_g_balance_campingrifle_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_campingrifle_primary_damage + autocvar_g_balance_campingrifle_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_campingrifle_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_campingrifle_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.campingrifle_bulletcounter = self.campingrifle_bulletcounter - 1;
109 W_CampingRifle_CheckMaxBullets(TRUE);
112 void W_CampingRifle_Attack()
114 W_CampingRifle_FireBullet(autocvar_g_balance_campingrifle_primary_spread, autocvar_g_balance_campingrifle_primary_damage, autocvar_g_balance_campingrifle_primary_headshotaddeddamage, autocvar_g_balance_campingrifle_primary_force, autocvar_g_balance_campingrifle_primary_speed, autocvar_g_balance_campingrifle_primary_lifetime, autocvar_g_balance_campingrifle_primary_ammo, WEP_CAMPINGRIFLE, autocvar_g_balance_campingrifle_primary_bulletconstant);
117 void W_CampingRifle_Attack2()
119 W_CampingRifle_FireBullet(autocvar_g_balance_campingrifle_secondary_spread, autocvar_g_balance_campingrifle_secondary_damage, autocvar_g_balance_campingrifle_secondary_headshotaddeddamage, autocvar_g_balance_campingrifle_secondary_force, autocvar_g_balance_campingrifle_secondary_speed, autocvar_g_balance_campingrifle_secondary_lifetime, autocvar_g_balance_campingrifle_secondary_ammo, WEP_CAMPINGRIFLE | HITTYPE_SECONDARY, autocvar_g_balance_campingrifle_secondary_bulletconstant);
122 void spawnfunc_weapon_campingrifle (void)
124 weapon_defaultspawnfunc(WEP_CAMPINGRIFLE);
127 .void(void) campingrifle_bullethail_attackfunc;
128 .float campingrifle_bullethail_frame;
129 .float campingrifle_bullethail_animtime;
130 .float campingrifle_bullethail_refire;
131 void W_CampingRifle_BulletHail_Continue()
134 W_CampingRifle_CheckReloadAndReady();
135 if(self.campingrifle_bulletcounter < 0)
136 return; // reloading, so we are done
137 sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
138 af = ATTACK_FINISHED(self);
139 self.switchweapon = self.weapon;
140 ATTACK_FINISHED(self) = time;
141 print(ftos(self.ammo_nails), "\n");
142 r = weapon_prepareattack(self.campingrifle_bullethail_frame == WFRAME_FIRE2, self.campingrifle_bullethail_refire);
143 if(self.switchweapon == self.weapon)
144 self.switchweapon = sw;
147 self.campingrifle_bullethail_attackfunc();
148 weapon_thinkf(self.campingrifle_bullethail_frame, self.campingrifle_bullethail_animtime, W_CampingRifle_BulletHail_Continue);
149 print("thinkf set\n");
153 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
154 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
158 void W_CampingRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
160 // if we get here, we have at least one bullet to fire
165 self.campingrifle_bullethail_attackfunc = AttackFunc;
166 self.campingrifle_bullethail_frame = fr;
167 self.campingrifle_bullethail_animtime = animtime;
168 self.campingrifle_bullethail_refire = refire;
169 weapon_thinkf(fr, animtime, W_CampingRifle_BulletHail_Continue);
174 weapon_thinkf(fr, animtime, W_CampingRifle_CheckReloadAndReady);
178 .float bot_secondary_campingriflemooth;
179 float w_campingrifle(float req)
184 self.BUTTON_ATCK=FALSE;
185 self.BUTTON_ATCK2=FALSE;
186 if(vlen(self.origin-self.enemy.origin) > 1000)
187 self.bot_secondary_campingriflemooth = 0;
188 if(self.bot_secondary_campingriflemooth == 0)
190 if(bot_aim(autocvar_g_balance_campingrifle_primary_speed, 0, autocvar_g_balance_campingrifle_primary_lifetime, TRUE))
192 self.BUTTON_ATCK = TRUE;
193 if(random() < 0.01) self.bot_secondary_campingriflemooth = 1;
198 if(bot_aim(autocvar_g_balance_campingrifle_secondary_speed, 0, autocvar_g_balance_campingrifle_secondary_lifetime, TRUE))
200 self.BUTTON_ATCK2 = TRUE;
201 if(random() < 0.03) self.bot_secondary_campingriflemooth = 0;
205 else if (req == WR_THINK)
207 if(self.campingrifle_bulletcounter < 0) // forced reload (e.g. because interrupted)
209 if(self.switchweapon == self.weapon)
210 if(self.weaponentity.state == WS_READY)
211 W_CampingRifle_Reload();
215 self.campingrifle_accumulator = bound(time - autocvar_g_balance_campingrifle_bursttime, self.campingrifle_accumulator, time);
216 if (self.BUTTON_ATCK)
217 if (weapon_prepareattack_check(0, autocvar_g_balance_campingrifle_primary_refire))
218 if (time >= self.campingrifle_accumulator + autocvar_g_balance_campingrifle_primary_burstcost)
220 weapon_prepareattack_do(0, autocvar_g_balance_campingrifle_primary_refire);
221 W_CampingRifle_BulletHail(autocvar_g_balance_campingrifle_primary_bullethail, W_CampingRifle_Attack, WFRAME_FIRE1, autocvar_g_balance_campingrifle_primary_animtime, autocvar_g_balance_campingrifle_primary_refire);
222 self.campingrifle_accumulator += autocvar_g_balance_campingrifle_primary_burstcost;
224 if (self.BUTTON_ATCK2)
226 if (autocvar_g_balance_campingrifle_secondary)
228 if (weapon_prepareattack_check(1, autocvar_g_balance_campingrifle_secondary_refire))
229 if (time >= self.campingrifle_accumulator + autocvar_g_balance_campingrifle_secondary_burstcost)
231 weapon_prepareattack_do(1, autocvar_g_balance_campingrifle_secondary_refire);
232 W_CampingRifle_BulletHail(autocvar_g_balance_campingrifle_secondary_bullethail, W_CampingRifle_Attack2, WFRAME_FIRE2, autocvar_g_balance_campingrifle_secondary_animtime, autocvar_g_balance_campingrifle_primary_refire);
233 self.campingrifle_accumulator += autocvar_g_balance_campingrifle_secondary_burstcost;
239 else if (req == WR_PRECACHE)
241 precache_model ("models/weapons/g_campingrifle.md3");
242 precache_model ("models/weapons/v_campingrifle.md3");
243 precache_model ("models/weapons/h_campingrifle.iqm");
244 precache_sound ("weapons/campingrifle_reload.wav");
245 precache_sound ("weapons/campingrifle_fire.wav");
246 precache_sound ("weapons/campingrifle_fire2.wav");
248 else if (req == WR_SETUP)
250 weapon_setup(WEP_CAMPINGRIFLE);
252 full = W_CampingRifle_CheckMaxBullets(TRUE);
253 if(autocvar_g_balance_campingrifle_auto_reload_on_switch)
255 self.campingrifle_bulletcounter = -1;
257 else if (req == WR_CHECKAMMO1)
258 return self.ammo_nails >= autocvar_g_balance_campingrifle_primary_ammo;
259 else if (req == WR_CHECKAMMO2)
260 return self.ammo_nails >= autocvar_g_balance_campingrifle_secondary_ammo;
261 else if (req == WR_RELOAD)
263 W_CampingRifle_Reload();
265 else if (req == WR_RESETPLAYER)
267 self.campingrifle_accumulator = time - autocvar_g_balance_campingrifle_bursttime;
268 self.campingrifle_bulletcounter = autocvar_g_balance_campingrifle_magazinecapacity;
269 W_CampingRifle_CheckMaxBullets(FALSE);
275 float w_campingrifle(float req)
277 if(req == WR_IMPACTEFFECT)
280 org2 = w_org + w_backoff * 2;
281 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
285 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
286 else if(w_random < 0.4)
287 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
288 else if(w_random < 0.5)
289 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
292 else if(req == WR_PRECACHE)
294 precache_sound("weapons/ric1.wav");
295 precache_sound("weapons/ric2.wav");
296 precache_sound("weapons/ric3.wav");
298 else if (req == WR_SUICIDEMESSAGE)
300 if(w_deathtype & HITTYPE_SECONDARY)
301 w_deathtypestring = "%s shot themself automatically";
303 w_deathtypestring = "%s sniped themself somehow";
305 else if (req == WR_KILLMESSAGE)
307 if(w_deathtype & HITTYPE_SECONDARY)
309 if(w_deathtype & HITTYPE_BOUNCE)
310 w_deathtypestring = "%s failed to hide from %s's bullet hail";
312 w_deathtypestring = "%s died in %s's bullet hail";
316 if(w_deathtype & HITTYPE_BOUNCE)
318 // TODO special headshot message here too?
319 w_deathtypestring = "%s failed to hide from %s's rifle";
323 if(w_deathtype & HITTYPE_HEADSHOT)
324 w_deathtypestring = "%s got hit in the head by %s";
326 w_deathtypestring = "%s was sniped by %s";