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