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