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