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