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