]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_sniperrifle.qc
Use only WR_SWITCHABLE to check if we can select our weapon. The ammo checks still...
[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         }
231         else if (req == WR_PRECACHE)
232         {
233                 precache_model ("models/weapons/g_campingrifle.md3");
234                 precache_model ("models/weapons/v_campingrifle.md3");
235                 precache_model ("models/weapons/h_campingrifle.iqm");
236                 precache_sound ("weapons/campingrifle_fire.wav");
237                 precache_sound ("weapons/campingrifle_fire2.wav");
238                 precache_sound ("weapons/reload.wav");
239         }
240         else if (req == WR_SETUP)
241         {
242                 weapon_setup(WEP_SNIPERRIFLE);
243                 W_SniperRifle_SetAmmoCounter();
244         }
245         else if (req == WR_CHECKAMMO1)
246         {
247                 if(autocvar_g_balance_sniperrifle_reload_ammo)
248                         return self.sniperrifle_load >= autocvar_g_balance_sniperrifle_primary_ammo;
249                 else
250                         return self.ammo_nails >= autocvar_g_balance_sniperrifle_primary_ammo;
251         }
252         else if (req == WR_CHECKAMMO2)
253         {
254                 if(autocvar_g_balance_sniperrifle_reload_ammo)
255                         return self.sniperrifle_load >= autocvar_g_balance_sniperrifle_secondary_ammo;
256                 else
257                         return self.ammo_nails >= autocvar_g_balance_sniperrifle_secondary_ammo;
258         }
259         else if (req == WR_RELOAD)
260         {
261                 W_SniperRifle_Reload();
262         }
263         else if (req == WR_RESETPLAYER)
264         {
265                 self.sniperrifle_accumulator = time - autocvar_g_balance_sniperrifle_bursttime;
266                 self.clip_load = autocvar_g_balance_sniperrifle_reload_ammo;
267         }
268         else if (req == WR_SWITCHABLE)
269         {
270                 // checks if this weapon can be switched to, when reloading is enabled
271                 // returns true if there's either enough load in the weapon to use it,
272                 // or we have enough ammo to reload the weapon to a usable point
273                 float ammo_amount;
274                 ammo_amount = min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo);
275                 return (autocvar_g_balance_sniperrifle_reload_ammo && self.sniperrifle_load >= ammo_amount) || self.ammo_nails >= ammo_amount;
276         }
277         return TRUE;
278 };
279 #endif
280 #ifdef CSQC
281 float w_sniperrifle(float req)
282 {
283         if(req == WR_IMPACTEFFECT)
284         {
285                 vector org2;
286                 org2 = w_org + w_backoff * 2;
287                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
288                 if(!w_issilent)
289                 {
290                         if(w_random < 0.2)
291                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
292                         else if(w_random < 0.4)
293                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
294                         else if(w_random < 0.5)
295                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
296                 }
297         }
298         else if(req == WR_PRECACHE)
299         {
300                 precache_sound("weapons/ric1.wav");
301                 precache_sound("weapons/ric2.wav");
302                 precache_sound("weapons/ric3.wav");
303         }
304         else if (req == WR_SUICIDEMESSAGE)
305         {
306                 if(w_deathtype & HITTYPE_SECONDARY)
307                         w_deathtypestring = "%s shot themself automatically";
308                 else
309                         w_deathtypestring = "%s sniped themself somehow";
310         }
311         else if (req == WR_KILLMESSAGE)
312         {
313                 if(w_deathtype & HITTYPE_SECONDARY)
314                 {
315                         if(w_deathtype & HITTYPE_BOUNCE)
316                                 w_deathtypestring = "%s failed to hide from %s's bullet hail";
317                         else
318                                 w_deathtypestring = "%s died in %s's bullet hail";
319                 }
320                 else
321                 {
322                         if(w_deathtype & HITTYPE_BOUNCE)
323                         {
324                                 // TODO special headshot message here too?
325                                 w_deathtypestring = "%s failed to hide from %s's rifle";
326                         }
327                         else
328                         {
329                                 if(w_deathtype & HITTYPE_HEADSHOT)
330                                         w_deathtypestring = "%s got hit in the head by %s";
331                                 else
332                                         w_deathtypestring = "%s was sniped by %s";
333                         }
334                 }
335         }
336         return TRUE;
337 }
338 #endif
339 #endif