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