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