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