]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_sniperrifle.qc
c4d273c82900b996e10b59228c682deab9de1850
[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_reloadtime - 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_reloadtime + 1;
53         ATTACK_FINISHED(self) = t;
54
55         weapon_thinkf(WFRAME_RELOAD, autocvar_g_balance_sniperrifle_reloadtime, 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_magazinecapacity;
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         if(self.ammo_counter <= 0)
139                 return; // reloading, so we are done
140         sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
141         af = ATTACK_FINISHED(self);
142         self.switchweapon = self.weapon;
143         ATTACK_FINISHED(self) = time;
144         print(ftos(self.ammo_nails), "\n");
145         r = weapon_prepareattack(self.sniperrifle_bullethail_frame == WFRAME_FIRE2, self.sniperrifle_bullethail_refire);
146         if(self.switchweapon == self.weapon)
147                 self.switchweapon = sw;
148         if(r)
149         {
150                 self.sniperrifle_bullethail_attackfunc();
151                 weapon_thinkf(self.sniperrifle_bullethail_frame, self.sniperrifle_bullethail_animtime, W_SniperRifle_BulletHail_Continue);
152                 print("thinkf set\n");
153         }
154         else
155         {
156                 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
157                 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
158         }
159 }
160
161 void W_SniperRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
162 {
163         // if we get here, we have at least one bullet to fire
164         AttackFunc();
165         if(mode)
166         {
167                 // continue hail
168                 self.sniperrifle_bullethail_attackfunc = AttackFunc;
169                 self.sniperrifle_bullethail_frame = fr;
170                 self.sniperrifle_bullethail_animtime = animtime;
171                 self.sniperrifle_bullethail_refire = refire;
172                 weapon_thinkf(fr, animtime, W_SniperRifle_BulletHail_Continue);
173         }
174         else
175         {
176                 // just one shot
177                 weapon_thinkf(fr, animtime, w_ready);
178         }
179 }
180
181 .float bot_secondary_sniperriflemooth;
182 float w_sniperrifle(float req)
183 {
184         float full;
185         if (req == WR_AIM)
186         {
187                 self.BUTTON_ATCK=FALSE;
188                 self.BUTTON_ATCK2=FALSE;
189                 if(vlen(self.origin-self.enemy.origin) > 1000)
190                         self.bot_secondary_sniperriflemooth = 0;
191                 if(self.bot_secondary_sniperriflemooth == 0)
192                 {
193                         if(bot_aim(autocvar_g_balance_sniperrifle_primary_speed, 0, autocvar_g_balance_sniperrifle_primary_lifetime, TRUE))
194                         {
195                                 self.BUTTON_ATCK = TRUE;
196                                 if(random() < 0.01) self.bot_secondary_sniperriflemooth = 1;
197                         }
198                 }
199                 else
200                 {
201                         if(bot_aim(autocvar_g_balance_sniperrifle_secondary_speed, 0, autocvar_g_balance_sniperrifle_secondary_lifetime, TRUE))
202                         {
203                                 self.BUTTON_ATCK2 = TRUE;
204                                 if(random() < 0.03) self.bot_secondary_sniperriflemooth = 0;
205                         }
206                 }
207         }
208         else if (req == WR_THINK)
209         {
210                 W_SniperRifle_SetAmmoCounter();
211                 if(self.ammo_counter <= 0) // forced reload
212             W_SniperRifle_Reload();
213                 else
214                 {
215                         self.sniperrifle_accumulator = bound(time - autocvar_g_balance_sniperrifle_bursttime, self.sniperrifle_accumulator, time);
216                         if (self.BUTTON_ATCK)
217                         if (weapon_prepareattack_check(0, autocvar_g_balance_sniperrifle_primary_refire))
218                         if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_primary_burstcost)
219                         {
220                                 weapon_prepareattack_do(0, autocvar_g_balance_sniperrifle_primary_refire);
221                                 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);
222                                 self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_primary_burstcost;
223                         }
224                         if (self.BUTTON_ATCK2)
225                         {       
226                                 if (autocvar_g_balance_sniperrifle_secondary)
227                                 {
228                     if(autocvar_g_balance_sniperrifle_secondary_reload)
229                         W_SniperRifle_Reload();
230                     else
231                     {
232                         if (weapon_prepareattack_check(1, autocvar_g_balance_sniperrifle_secondary_refire))
233                         if (time >= self.sniperrifle_accumulator + autocvar_g_balance_sniperrifle_secondary_burstcost)
234                         {
235                             weapon_prepareattack_do(1, autocvar_g_balance_sniperrifle_secondary_refire);
236                             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);
237                             self.sniperrifle_accumulator += autocvar_g_balance_sniperrifle_secondary_burstcost;
238                         }
239                     }
240                                 }
241                         }
242                 }
243         if(self.wish_reload)
244         {
245             if(self.switchweapon == self.weapon)
246             {
247                 if(self.weaponentity.state == WS_READY)
248                 {
249                     self.wish_reload = 0;
250                     W_SniperRifle_Reload(self.ammo_nails);
251                 }
252             }
253         }
254         }
255         else if (req == WR_PRECACHE)
256         {
257                 precache_model ("models/weapons/g_campingrifle.md3");
258                 precache_model ("models/weapons/v_campingrifle.md3");
259                 precache_model ("models/weapons/h_campingrifle.iqm");
260                 precache_sound ("weapons/campingrifle_reload.wav");
261                 precache_sound ("weapons/campingrifle_fire.wav");
262                 precache_sound ("weapons/campingrifle_fire2.wav");
263         }
264         else if (req == WR_SETUP)
265         {
266                 weapon_setup(WEP_SNIPERRIFLE);
267
268                 full = W_SniperRifle_CheckMaxBullets(TRUE);
269                 if(autocvar_g_balance_sniperrifle_auto_reload_on_switch)
270                         if(!full)
271                                 self.ammo_counter = -1;
272         }
273         else if (req == WR_CHECKAMMO1)
274                 return self.ammo_nails >= autocvar_g_balance_sniperrifle_primary_ammo;
275         else if (req == WR_CHECKAMMO2)
276                 return self.ammo_nails >= autocvar_g_balance_sniperrifle_secondary_ammo;
277         else if (req == WR_RELOAD)
278         {
279                 W_SniperRifle_Reload();
280         }
281         else if (req == WR_RESETPLAYER)
282         {
283                 self.sniperrifle_accumulator = time - autocvar_g_balance_sniperrifle_bursttime;
284                 self.ammo_counter = autocvar_g_balance_sniperrifle_magazinecapacity;
285                 W_SniperRifle_CheckMaxBullets(FALSE);
286         }
287         return TRUE;
288 };
289 #endif
290 #ifdef CSQC
291 float w_sniperrifle(float req)
292 {
293         if(req == WR_IMPACTEFFECT)
294         {
295                 vector org2;
296                 org2 = w_org + w_backoff * 2;
297                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
298                 if(!w_issilent)
299                 {
300                         if(w_random < 0.2)
301                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
302                         else if(w_random < 0.4)
303                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
304                         else if(w_random < 0.5)
305                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
306                 }
307         }
308         else if(req == WR_PRECACHE)
309         {
310                 precache_sound("weapons/ric1.wav");
311                 precache_sound("weapons/ric2.wav");
312                 precache_sound("weapons/ric3.wav");
313         }
314         else if (req == WR_SUICIDEMESSAGE)
315         {
316                 if(w_deathtype & HITTYPE_SECONDARY)
317                         w_deathtypestring = "%s shot themself automatically";
318                 else
319                         w_deathtypestring = "%s sniped themself somehow";
320         }
321         else if (req == WR_KILLMESSAGE)
322         {
323                 if(w_deathtype & HITTYPE_SECONDARY)
324                 {
325                         if(w_deathtype & HITTYPE_BOUNCE)
326                                 w_deathtypestring = "%s failed to hide from %s's bullet hail";
327                         else
328                                 w_deathtypestring = "%s died in %s's bullet hail";
329                 }
330                 else
331                 {
332                         if(w_deathtype & HITTYPE_BOUNCE)
333                         {
334                                 // TODO special headshot message here too?
335                                 w_deathtypestring = "%s failed to hide from %s's rifle";
336                         }
337                         else
338                         {
339                                 if(w_deathtype & HITTYPE_HEADSHOT)
340                                         w_deathtypestring = "%s got hit in the head by %s";
341                                 else
342                                         w_deathtypestring = "%s was sniped by %s";
343                         }
344                 }
345         }
346         return TRUE;
347 }
348 #endif
349 #endif