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