]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_sniperrifle.qc
Remove the switchable check and do all checks in the WR_CHECKAMMO checks. Only fixed...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_sniperrifle.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(SNIPERRIFLE, w_sniperrifle, IT_NAILS, 7, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "sniperrifle", _("Sniper Rifle"))
3 #else
4 #ifdef SVQC
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.
8
9 .float sniperrifle_accumulator;
10
11 void W_SniperRifle_SetAmmoCounter()
12 {
13         // set clip_load to the weapon we have switched to, if the gun uses reloading
14         if(!autocvar_g_balance_sniperrifle_reload_ammo)
15                 self.clip_load = 0; // also keeps crosshair ammo from displaying
16         else
17         {
18                 self.clip_load = self.sniperrifle_load;
19                 self.clip_size = autocvar_g_balance_sniperrifle_reload_ammo; // for the crosshair ammo display
20         }
21 }
22
23 void W_SniperRifle_ReloadedAndReady()
24 {
25         float t;
26
27         // now do the ammo transfer
28         self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
29         while(self.clip_load < autocvar_g_balance_sniperrifle_reload_ammo && self.ammo_nails) // make sure we don't add more ammo than we have
30         {
31                 self.clip_load += 1;
32                 self.ammo_nails -= 1;
33         }
34         self.sniperrifle_load = self.clip_load;
35
36         t = ATTACK_FINISHED(self) - autocvar_g_balance_sniperrifle_reload_time - 1;
37         ATTACK_FINISHED(self) = t;
38         w_ready();
39 }
40
41 void W_SniperRifle_Reload()
42 {
43         // return if reloading is disabled for this weapon
44         if(!autocvar_g_balance_sniperrifle_reload_ammo)
45                 return;
46
47         if(!W_ReloadCheck(self.ammo_nails, min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo)))
48                 return;
49
50         float t;
51
52         sound (self, CHAN_WEAPON2, "weapons/reload.wav", VOL_BASE, ATTN_NORM);
53
54         t = max(time, ATTACK_FINISHED(self)) + autocvar_g_balance_sniperrifle_reload_time + 1;
55         ATTACK_FINISHED(self) = t;
56
57         weapon_thinkf(WFRAME_RELOAD, autocvar_g_balance_sniperrifle_reload_time, W_SniperRifle_ReloadedAndReady);
58
59         self.old_clip_load = self.clip_load;
60         self.clip_load = -1;
61 }
62
63 void W_SniperRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAddedDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant)
64 {
65         // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
66         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
67         {
68                 if(autocvar_g_balance_sniperrifle_reload_ammo)
69                 {
70                         self.clip_load -= pAmmo;
71                         self.sniperrifle_load = self.clip_load;
72                 }
73                 else
74                         self.ammo_nails -= pAmmo;
75         }
76
77         if(deathtype & HITTYPE_SECONDARY)
78                 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);
79         else
80                 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);
81
82         pointparticles(particleeffectnum("sniperrifle_muzzleflash"), w_shotorg, w_shotdir * 2000, 1);
83
84         if(self.BUTTON_ZOOM) // if zoomed, shoot from the eye
85         {
86                 w_shotdir = v_forward;
87                 w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
88         }
89
90         if(deathtype & HITTYPE_SECONDARY)
91                 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);
92         else
93                 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);
94         endFireBallisticBullet();
95
96         if (autocvar_g_casings >= 2)
97                 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);
98 }
99
100 void W_SniperRifle_Attack()
101 {
102         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);
103 }
104
105 void W_SniperRifle_Attack2()
106 {
107         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);
108 }
109
110 void spawnfunc_weapon_sniperrifle (void)
111 {
112         weapon_defaultspawnfunc(WEP_SNIPERRIFLE);
113 }
114
115 // compatibility alias
116 void spawnfunc_weapon_campingrifle (void)
117 {
118         spawnfunc_weapon_sniperrifle();
119 }
120
121 .void(void) sniperrifle_bullethail_attackfunc;
122 .float sniperrifle_bullethail_frame;
123 .float sniperrifle_bullethail_animtime;
124 .float sniperrifle_bullethail_refire;
125 void W_SniperRifle_BulletHail_Continue()
126 {
127         float r, sw, af;
128
129         sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
130         af = ATTACK_FINISHED(self);
131         self.switchweapon = self.weapon;
132         ATTACK_FINISHED(self) = time;
133         print(ftos(self.ammo_nails), "\n");
134         r = weapon_prepareattack(self.sniperrifle_bullethail_frame == WFRAME_FIRE2, self.sniperrifle_bullethail_refire);
135         if(self.switchweapon == self.weapon)
136                 self.switchweapon = sw;
137         if(r)
138         {
139                 self.sniperrifle_bullethail_attackfunc();
140                 weapon_thinkf(self.sniperrifle_bullethail_frame, self.sniperrifle_bullethail_animtime, W_SniperRifle_BulletHail_Continue);
141                 print("thinkf set\n");
142         }
143         else
144         {
145                 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
146                 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
147         }
148 }
149
150 void W_SniperRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
151 {
152         // if we get here, we have at least one bullet to fire
153         AttackFunc();
154         if(mode)
155         {
156                 // continue hail
157                 self.sniperrifle_bullethail_attackfunc = AttackFunc;
158                 self.sniperrifle_bullethail_frame = fr;
159                 self.sniperrifle_bullethail_animtime = animtime;
160                 self.sniperrifle_bullethail_refire = refire;
161                 weapon_thinkf(fr, animtime, W_SniperRifle_BulletHail_Continue);
162         }
163         else
164         {
165                 // just one shot
166                 weapon_thinkf(fr, animtime, w_ready);
167         }
168 }
169
170 .float bot_secondary_sniperriflemooth;
171 float w_sniperrifle(float req)
172 {
173         if (req == WR_AIM)
174         {
175                 self.BUTTON_ATCK=FALSE;
176                 self.BUTTON_ATCK2=FALSE;
177                 if(vlen(self.origin-self.enemy.origin) > 1000)
178                         self.bot_secondary_sniperriflemooth = 0;
179                 if(self.bot_secondary_sniperriflemooth == 0)
180                 {
181                         if(bot_aim(autocvar_g_balance_sniperrifle_primary_speed, 0, autocvar_g_balance_sniperrifle_primary_lifetime, TRUE))
182                         {
183                                 self.BUTTON_ATCK = TRUE;
184                                 if(random() < 0.01) self.bot_secondary_sniperriflemooth = 1;
185                         }
186                 }
187                 else
188                 {
189                         if(bot_aim(autocvar_g_balance_sniperrifle_secondary_speed, 0, autocvar_g_balance_sniperrifle_secondary_lifetime, TRUE))
190                         {
191                                 self.BUTTON_ATCK2 = TRUE;
192                                 if(random() < 0.03) self.bot_secondary_sniperriflemooth = 0;
193                         }
194                 }
195         }
196         else if (req == WR_THINK)
197         {
198                 if(autocvar_g_balance_sniperrifle_reload_ammo && self.clip_load < min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo)) // forced reload
199             W_SniperRifle_Reload();
200                 else
201                 {
202                         self.sniperrifle_accumulator = bound(time - autocvar_g_balance_sniperrifle_bursttime, self.sniperrifle_accumulator, time);
203                         if (self.BUTTON_ATCK)
204                         if (weapon_prepareattack_check(0, autocvar_g_balance_sniperrifle_primary_refire))
205                         if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_primary_burstcost)
206                         {
207                                 weapon_prepareattack_do(0, autocvar_g_balance_sniperrifle_primary_refire);
208                                 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);
209                                 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_primary_burstcost;
210                         }
211                         if (self.BUTTON_ATCK2)
212                         {       
213                                 if (autocvar_g_balance_sniperrifle_secondary)
214                                 {
215                     if(autocvar_g_balance_sniperrifle_secondary_reload)
216                         W_SniperRifle_Reload();
217                     else
218                     {
219                         if (weapon_prepareattack_check(1, autocvar_g_balance_sniperrifle_secondary_refire))
220                         if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_secondary_burstcost)
221                         {
222                             weapon_prepareattack_do(1, autocvar_g_balance_sniperrifle_secondary_refire);
223                             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);
224                             self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_secondary_burstcost;
225                         }
226                     }
227                                 }
228                         }
229                 }
230         if(self.wish_reload)
231         {
232             if(self.switchweapon == self.weapon)
233             {
234                 if(self.weaponentity.state == WS_READY)
235                 {
236                     self.wish_reload = 0;
237                     W_SniperRifle_Reload();
238                 }
239             }
240         }
241         }
242         else if (req == WR_PRECACHE)
243         {
244                 precache_model ("models/weapons/g_campingrifle.md3");
245                 precache_model ("models/weapons/v_campingrifle.md3");
246                 precache_model ("models/weapons/h_campingrifle.iqm");
247                 precache_sound ("weapons/campingrifle_fire.wav");
248                 precache_sound ("weapons/campingrifle_fire2.wav");
249                 precache_sound ("weapons/reload.wav");
250         }
251         else if (req == WR_SETUP)
252         {
253                 weapon_setup(WEP_SNIPERRIFLE);
254                 W_SniperRifle_SetAmmoCounter();
255         }
256         else if (req == WR_CHECKAMMO1)
257         {
258                 if(autocvar_g_balance_sniperrifle_reload_ammo)
259                         return self.sniperrifle_load >= autocvar_g_balance_sniperrifle_primary_ammo;
260                 else
261                         return self.ammo_nails >= autocvar_g_balance_sniperrifle_primary_ammo;
262         }
263         else if (req == WR_CHECKAMMO2)
264         {
265                 if(autocvar_g_balance_sniperrifle_reload_ammo)
266                         return self.sniperrifle_load >= autocvar_g_balance_sniperrifle_secondary_ammo;
267                 else
268                         return self.ammo_nails >= autocvar_g_balance_sniperrifle_secondary_ammo;
269         }
270         else if (req == WR_RELOAD)
271         {
272                 W_SniperRifle_Reload();
273         }
274         else if (req == WR_RESETPLAYER)
275         {
276                 self.sniperrifle_accumulator = time - autocvar_g_balance_sniperrifle_bursttime;
277                 self.clip_load = autocvar_g_balance_sniperrifle_reload_ammo;
278         }
279         return TRUE;
280 };
281 #endif
282 #ifdef CSQC
283 float w_sniperrifle(float req)
284 {
285         if(req == WR_IMPACTEFFECT)
286         {
287                 vector org2;
288                 org2 = w_org + w_backoff * 2;
289                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
290                 if(!w_issilent)
291                 {
292                         if(w_random < 0.2)
293                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
294                         else if(w_random < 0.4)
295                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
296                         else if(w_random < 0.5)
297                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
298                 }
299         }
300         else if(req == WR_PRECACHE)
301         {
302                 precache_sound("weapons/ric1.wav");
303                 precache_sound("weapons/ric2.wav");
304                 precache_sound("weapons/ric3.wav");
305         }
306         else if (req == WR_SUICIDEMESSAGE)
307         {
308                 if(w_deathtype & HITTYPE_SECONDARY)
309                         w_deathtypestring = "%s shot themself automatically";
310                 else
311                         w_deathtypestring = "%s sniped themself somehow";
312         }
313         else if (req == WR_KILLMESSAGE)
314         {
315                 if(w_deathtype & HITTYPE_SECONDARY)
316                 {
317                         if(w_deathtype & HITTYPE_BOUNCE)
318                                 w_deathtypestring = "%s failed to hide from %s's bullet hail";
319                         else
320                                 w_deathtypestring = "%s died in %s's bullet hail";
321                 }
322                 else
323                 {
324                         if(w_deathtype & HITTYPE_BOUNCE)
325                         {
326                                 // TODO special headshot message here too?
327                                 w_deathtypestring = "%s failed to hide from %s's rifle";
328                         }
329                         else
330                         {
331                                 if(w_deathtype & HITTYPE_HEADSHOT)
332                                         w_deathtypestring = "%s got hit in the head by %s";
333                                 else
334                                         w_deathtypestring = "%s was sniped by %s";
335                         }
336                 }
337         }
338         return TRUE;
339 }
340 #endif
341 #endif