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