2 REGISTER_WEAPON(CAMPINGRIFLE, w_campingrifle, IT_NAILS, 3, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "campingrifle", "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 = cvar("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(cvar("g_balance_campingrifle_primary_ammo"), cvar("g_balance_campingrifle_secondary_ammo"))));
20 if(self.campingrifle_bulletcounter > maxbulls || !cvar("g_balance_campingrifle_magazinecapacity"))
21 self.campingrifle_bulletcounter = maxbulls;
22 return (self.campingrifle_bulletcounter == maxbulls);
25 void W_CampingRifle_ReloadedAndReady()
28 self.campingrifle_bulletcounter = cvar("g_balance_campingrifle_magazinecapacity");
29 W_CampingRifle_CheckMaxBullets(TRUE);
30 t = ATTACK_FINISHED(self) - cvar("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(cvar("g_balance_campingrifle_primary_ammo"), cvar("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 >= cvar("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)) + cvar("g_balance_campingrifle_reloadtime") + 1;
64 ATTACK_FINISHED(self) = t;
66 weapon_thinkf(WFRAME_RELOAD, cvar("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, cvar("g_antilag_bullets") && pSpeed >= cvar("g_antilag_bullets"), 2, "weapons/campingrifle_fire2.wav", cvar("g_balance_campingrifle_secondary_damage"));
89 W_SetupShot (self, cvar("g_antilag_bullets") && pSpeed >= cvar("g_antilag_bullets"), 2, "weapons/campingrifle_fire.wav", cvar("g_balance_campingrifle_primary_damage"));
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 fireBallisticBullet(w_shotorg, w_shotdir, pSpread, pSpeed, pLifetime, pDamage, pHeadshotAddedDamage / pDamage, pForce, deathtype, (cvar("g_balance_campingrifle_tracer") ? EF_RED : EF_BLUE), 1, pBulletConstant);
100 endFireBallisticBullet();
102 if (cvar("g_casings") >= 2)
103 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);
105 self.campingrifle_bulletcounter = self.campingrifle_bulletcounter - 1;
106 W_CampingRifle_CheckMaxBullets(TRUE);
109 void W_CampingRifle_Attack()
111 W_CampingRifle_FireBullet(cvar("g_balance_campingrifle_primary_spread"), cvar("g_balance_campingrifle_primary_damage"), cvar("g_balance_campingrifle_primary_headshotaddeddamage"), cvar("g_balance_campingrifle_primary_force"), cvar("g_balance_campingrifle_primary_speed"), cvar("g_balance_campingrifle_primary_lifetime"), cvar("g_balance_campingrifle_primary_ammo"), WEP_CAMPINGRIFLE, cvar("g_balance_campingrifle_primary_bulletconstant"));
114 void W_CampingRifle_Attack2()
116 W_CampingRifle_FireBullet(cvar("g_balance_campingrifle_secondary_spread"), cvar("g_balance_campingrifle_secondary_damage"), cvar("g_balance_campingrifle_secondary_headshotaddeddamage"), cvar("g_balance_campingrifle_secondary_force"), cvar("g_balance_campingrifle_secondary_speed"), cvar("g_balance_campingrifle_secondary_lifetime"), cvar("g_balance_campingrifle_secondary_ammo"), WEP_CAMPINGRIFLE | HITTYPE_SECONDARY, cvar("g_balance_campingrifle_secondary_bulletconstant"));
119 void spawnfunc_weapon_campingrifle (void)
121 weapon_defaultspawnfunc(WEP_CAMPINGRIFLE);
124 .void(void) campingrifle_bullethail_attackfunc;
125 .float campingrifle_bullethail_frame;
126 .float campingrifle_bullethail_animtime;
127 .float campingrifle_bullethail_refire;
128 void W_CampingRifle_BulletHail_Continue()
131 W_CampingRifle_CheckReloadAndReady();
132 if(self.campingrifle_bulletcounter < 0)
133 return; // reloading, so we are done
134 sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
135 af = ATTACK_FINISHED(self);
136 self.switchweapon = self.weapon;
137 ATTACK_FINISHED(self) = time;
138 print(ftos(self.ammo_nails), "\n");
139 r = weapon_prepareattack(self.campingrifle_bullethail_frame == WFRAME_FIRE2, self.campingrifle_bullethail_refire);
140 if(self.switchweapon == self.weapon)
141 self.switchweapon = sw;
144 self.campingrifle_bullethail_attackfunc();
145 weapon_thinkf(self.campingrifle_bullethail_frame, self.campingrifle_bullethail_animtime, W_CampingRifle_BulletHail_Continue);
146 print("thinkf set\n");
150 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
151 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
155 void W_CampingRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
157 // if we get here, we have at least one bullet to fire
162 self.campingrifle_bullethail_attackfunc = AttackFunc;
163 self.campingrifle_bullethail_frame = fr;
164 self.campingrifle_bullethail_animtime = animtime;
165 self.campingrifle_bullethail_refire = refire;
166 weapon_thinkf(fr, animtime, W_CampingRifle_BulletHail_Continue);
171 weapon_thinkf(fr, animtime, W_CampingRifle_CheckReloadAndReady);
175 .float bot_secondary_campingriflemooth;
176 .float sent_campingrifle_scope;
177 float w_campingrifle(float req)
182 self.BUTTON_ATCK=FALSE;
183 self.BUTTON_ATCK2=FALSE;
184 if(vlen(self.origin-self.enemy.origin) > 1000)
185 self.bot_secondary_campingriflemooth = 0;
186 if(self.bot_secondary_campingriflemooth == 0)
188 if(bot_aim(cvar("g_balance_campingrifle_primary_speed"), 0, cvar("g_balance_campingrifle_primary_lifetime"), TRUE))
190 self.BUTTON_ATCK = TRUE;
191 if(random() < 0.01) self.bot_secondary_campingriflemooth = 1;
196 if(bot_aim(cvar("g_balance_campingrifle_secondary_speed"), 0, cvar("g_balance_campingrifle_secondary_lifetime"), TRUE))
198 self.BUTTON_ATCK2 = TRUE;
199 if(random() < 0.03) self.bot_secondary_campingriflemooth = 0;
203 else if (req == WR_THINK)
205 if(self.campingrifle_bulletcounter < 0) // forced reload (e.g. because interrupted)
207 if(self.switchweapon == self.weapon)
208 if(self.weaponentity.state == WS_READY)
209 W_CampingRifle_Reload();
213 self.campingrifle_accumulator = bound(time - cvar("g_balance_campingrifle_bursttime"), self.campingrifle_accumulator, time);
214 if (self.BUTTON_ATCK)
215 if (weapon_prepareattack_check(0, cvar("g_balance_campingrifle_primary_refire")))
216 if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_primary_burstcost"))
218 weapon_prepareattack_do(0, cvar("g_balance_campingrifle_primary_refire"));
219 W_CampingRifle_BulletHail(cvar("g_balance_campingrifle_primary_bullethail"), W_CampingRifle_Attack, WFRAME_FIRE1, cvar("g_balance_campingrifle_primary_animtime"), cvar("g_balance_campingrifle_primary_refire"));
220 self.campingrifle_accumulator += cvar("g_balance_campingrifle_primary_burstcost");
222 if (self.BUTTON_ATCK2)
224 if (cvar("g_balance_campingrifle_secondary"))
226 if (weapon_prepareattack_check(1, cvar("g_balance_campingrifle_secondary_refire")))
227 if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_secondary_burstcost"))
229 weapon_prepareattack_do(1, cvar("g_balance_campingrifle_secondary_refire"));
230 W_CampingRifle_BulletHail(cvar("g_balance_campingrifle_secondary_bullethail"), W_CampingRifle_Attack2, WFRAME_FIRE2, cvar("g_balance_campingrifle_secondary_animtime"), cvar("g_balance_campingrifle_primary_refire"));
231 self.campingrifle_accumulator += cvar("g_balance_campingrifle_secondary_burstcost");
236 if(clienttype(self) == CLIENTTYPE_REAL)
237 if(!self.sent_campingrifle_scope)
240 WriteByte(MSG_ONE, SVC_TEMPENTITY);
241 WriteByte(MSG_ONE, TE_CSQC_CAMPINGRIFLE_SCOPE);
242 self.sent_campingrifle_scope = 1;
248 else if (req == WR_PRECACHE)
250 precache_model ("models/weapons/g_campingrifle.md3");
251 precache_model ("models/weapons/v_campingrifle.md3");
252 precache_model ("models/weapons/h_campingrifle.iqm");
253 precache_sound ("weapons/campingrifle_reload.wav");
254 precache_sound ("weapons/campingrifle_fire.wav");
255 precache_sound ("weapons/campingrifle_fire2.wav");
257 else if (req == WR_SETUP)
259 weapon_setup(WEP_CAMPINGRIFLE);
261 full = W_CampingRifle_CheckMaxBullets(TRUE);
262 if(cvar("g_balance_campingrifle_auto_reload_after_changing_weapons"))
264 self.campingrifle_bulletcounter = -1;
266 else if (req == WR_CHECKAMMO1)
267 return self.ammo_nails >= cvar("g_balance_campingrifle_primary_ammo");
268 else if (req == WR_CHECKAMMO2)
269 return self.ammo_nails >= cvar("g_balance_campingrifle_secondary_ammo");
270 else if (req == WR_RELOAD)
272 W_CampingRifle_Reload();
274 else if (req == WR_RESETPLAYER)
276 self.campingrifle_accumulator = time - cvar("g_balance_campingrifle_bursttime");
277 self.campingrifle_bulletcounter = cvar("g_balance_campingrifle_magazinecapacity");
278 W_CampingRifle_CheckMaxBullets(FALSE);
284 float w_campingrifle(float req)
286 if(req == WR_IMPACTEFFECT)
289 org2 = w_org + w_backoff * 2;
290 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
294 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
295 else if(w_random < 0.4)
296 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
297 else if(w_random < 0.5)
298 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
301 else if(req == WR_PRECACHE)
303 precache_sound("weapons/ric1.wav");
304 precache_sound("weapons/ric2.wav");
305 precache_sound("weapons/ric3.wav");
307 else if (req == WR_SUICIDEMESSAGE)
309 if(w_deathtype & HITTYPE_SECONDARY)
310 w_deathtypestring = "shot themself automatically";
312 w_deathtypestring = "sniped themself somehow";
314 else if (req == WR_KILLMESSAGE)
316 if(w_deathtype & HITTYPE_SECONDARY)
318 if(w_deathtype & HITTYPE_BOUNCE)
319 w_deathtypestring = "failed to hide from #'s bullet hail";
321 w_deathtypestring = "died in #'s bullet hail";
325 if(w_deathtype & HITTYPE_BOUNCE)
327 // TODO special headshot message here too?
328 w_deathtypestring = "failed to hide from #'s rifle";
332 if(w_deathtype & HITTYPE_HEADSHOT)
333 w_deathtypestring = "got hit in the head by #";
335 w_deathtypestring = "was sniped by #";