]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_sniperrifle.qc
Doh, it CAN be done differently than my ugly list. Use WR checks
[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         else if (req == WR_SWITCHABLE)
280         {
281                 // checks if this weapon can be switched to, when reloading is enabled
282                 // returns true if there's either enough load in the weapon to use it,
283                 // or we have enough ammo to reload the weapon to a usable point
284                 float ammo_amount;
285                 ammo_amount = min(autocvar_g_balance_sniperrifle_primary_ammo, autocvar_g_balance_sniperrifle_secondary_ammo);
286                 return self.sniperrifle_load >= ammo_amount || self.ammo_nails >= ammo_amount;
287         }
288         return TRUE;
289 };
290 #endif
291 #ifdef CSQC
292 float w_sniperrifle(float req)
293 {
294         if(req == WR_IMPACTEFFECT)
295         {
296                 vector org2;
297                 org2 = w_org + w_backoff * 2;
298                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
299                 if(!w_issilent)
300                 {
301                         if(w_random < 0.2)
302                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
303                         else if(w_random < 0.4)
304                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
305                         else if(w_random < 0.5)
306                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
307                 }
308         }
309         else if(req == WR_PRECACHE)
310         {
311                 precache_sound("weapons/ric1.wav");
312                 precache_sound("weapons/ric2.wav");
313                 precache_sound("weapons/ric3.wav");
314         }
315         else if (req == WR_SUICIDEMESSAGE)
316         {
317                 if(w_deathtype & HITTYPE_SECONDARY)
318                         w_deathtypestring = "%s shot themself automatically";
319                 else
320                         w_deathtypestring = "%s sniped themself somehow";
321         }
322         else if (req == WR_KILLMESSAGE)
323         {
324                 if(w_deathtype & HITTYPE_SECONDARY)
325                 {
326                         if(w_deathtype & HITTYPE_BOUNCE)
327                                 w_deathtypestring = "%s failed to hide from %s's bullet hail";
328                         else
329                                 w_deathtypestring = "%s died in %s's bullet hail";
330                 }
331                 else
332                 {
333                         if(w_deathtype & HITTYPE_BOUNCE)
334                         {
335                                 // TODO special headshot message here too?
336                                 w_deathtypestring = "%s failed to hide from %s's rifle";
337                         }
338                         else
339                         {
340                                 if(w_deathtype & HITTYPE_HEADSHOT)
341                                         w_deathtypestring = "%s got hit in the head by %s";
342                                 else
343                                         w_deathtypestring = "%s was sniped by %s";
344                         }
345                 }
346         }
347         return TRUE;
348 }
349 #endif
350 #endif