309717305052fefb239085d85a8ed1ff41b5b103
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_campingrifle.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(CAMPINGRIFLE, w_campingrifle, IT_NAILS, 7, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "campingrifle", "Sniper Rifle");
3 #else
4 #ifdef SVQC
5 //Camping 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 campingrifle_accumulator;
10
11 float W_CampingRifle_CheckMaxBullets(float checkammo)
12 {
13         float maxbulls;
14         maxbulls = cvar("g_balance_campingrifle_magazinecapacity");
15         if(!maxbulls)
16                 maxbulls = 8; // match HUD
17         if(checkammo)
18                 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
19                         maxbulls = min(maxbulls, floor(self.ammo_nails / min(cvar("g_balance_campingrifle_primary_ammo"), cvar("g_balance_campingrifle_secondary_ammo"))));
20         if(self.campingrifle_bulletcounter > maxbulls || !cvar("g_balance_campingrifle_magazinecapacity"))
21                 self.campingrifle_bulletcounter = maxbulls;
22         return (self.campingrifle_bulletcounter == maxbulls);
23 }
24
25 void W_CampingRifle_ReloadedAndReady()
26 {
27         float t;
28         self.campingrifle_bulletcounter = cvar("g_balance_campingrifle_magazinecapacity");
29         W_CampingRifle_CheckMaxBullets(TRUE);
30         t = ATTACK_FINISHED(self) - cvar("g_balance_campingrifle_reloadtime") - 1;
31         ATTACK_FINISHED(self) = t;
32         w_ready();
33 }
34
35 float W_CampingRifle_Reload()
36 {
37         float t;
38
39         W_CampingRifle_CheckMaxBullets(TRUE);
40
41         if(self.ammo_nails < min(cvar("g_balance_campingrifle_primary_ammo"), cvar("g_balance_campingrifle_secondary_ammo"))) // when we get here, bulletcounter must be 0 or -1
42         {
43                 print("cannot reload... not enough bullets\n");
44                 self.campingrifle_bulletcounter = -1; // reload later
45                 W_SwitchToOtherWeapon(self);
46                 return 0;
47         }
48         
49         if (self.campingrifle_bulletcounter >= cvar("g_balance_campingrifle_magazinecapacity"))
50                 return 0;
51
52         if (self.weaponentity)
53         {
54                 if (self.weaponentity.wframe == WFRAME_RELOAD)
55                         return 0;
56
57                 // allow to switch away while reloading, but this will cause a new reload!
58                 self.weaponentity.state = WS_READY;
59         }
60
61         sound (self, CHAN_WEAPON2, "weapons/campingrifle_reload.wav", VOL_BASE, ATTN_NORM);
62
63         t = max(time, ATTACK_FINISHED(self)) + cvar("g_balance_campingrifle_reloadtime") + 1;
64         ATTACK_FINISHED(self) = t;
65
66         weapon_thinkf(WFRAME_RELOAD, cvar("g_balance_campingrifle_reloadtime"), W_CampingRifle_ReloadedAndReady);
67
68         self.campingrifle_bulletcounter = -1;
69
70         return 1;
71 }
72
73 void W_CampingRifle_CheckReloadAndReady()
74 {
75         w_ready();
76         if(self.campingrifle_bulletcounter <= 0)
77                 if(W_CampingRifle_Reload())
78                         return;
79 }
80
81 void W_CampingRifle_FireBullet(float pSpread, float pDamage, float pHeadshotAddedDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant)
82 {
83         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
84                 self.ammo_nails -= pAmmo;
85
86         if(deathtype & HITTYPE_SECONDARY)
87                 W_SetupShot (self, cvar("g_antilag_bullets") && pSpeed >= cvar("g_antilag_bullets"), 2, "weapons/campingrifle_fire2.wav", CHAN_WEAPON, cvar("g_balance_campingrifle_secondary_damage") + cvar("g_balance_campingrifle_secondary_headshotaddeddamage"));
88         else
89                 W_SetupShot (self, cvar("g_antilag_bullets") && pSpeed >= cvar("g_antilag_bullets"), 2, "weapons/campingrifle_fire.wav", CHAN_WEAPON, cvar("g_balance_campingrifle_primary_damage") + cvar("g_balance_campingrifle_primary_headshotaddeddamage"));
90
91         pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 2000, 1);
92
93         if(self.BUTTON_ZOOM) // if zoomed, shoot from the eye
94         {
95                 w_shotdir = v_forward;
96                 w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
97         }
98
99         if(deathtype & HITTYPE_SECONDARY)
100                 fireBallisticBullet(w_shotorg, w_shotdir, pSpread, pSpeed, pLifetime, pDamage, pHeadshotAddedDamage / pDamage, pForce, deathtype, (cvar("g_balance_campingrifle_secondary_tracer") ? EF_RED : EF_BLUE), 1, pBulletConstant);
101         else
102                 fireBallisticBullet(w_shotorg, w_shotdir, pSpread, pSpeed, pLifetime, pDamage, pHeadshotAddedDamage / pDamage, pForce, deathtype, (cvar("g_balance_campingrifle_primary_tracer") ? EF_RED : EF_BLUE), 1, pBulletConstant);
103         endFireBallisticBullet();
104
105         if (cvar("g_casings") >= 2)
106                 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);
107         
108         self.campingrifle_bulletcounter = self.campingrifle_bulletcounter - 1;
109         W_CampingRifle_CheckMaxBullets(TRUE);
110 }
111
112 void W_CampingRifle_Attack()
113 {
114         W_CampingRifle_FireBullet(cvar("g_balance_campingrifle_primary_spread"), cvar("g_balance_campingrifle_primary_damage"), cvar("g_balance_campingrifle_primary_headshotaddeddamage"), cvar("g_balance_campingrifle_primary_force"), cvar("g_balance_campingrifle_primary_speed"), cvar("g_balance_campingrifle_primary_lifetime"), cvar("g_balance_campingrifle_primary_ammo"), WEP_CAMPINGRIFLE, cvar("g_balance_campingrifle_primary_bulletconstant"));
115 }
116
117 void W_CampingRifle_Attack2()
118 {
119         W_CampingRifle_FireBullet(cvar("g_balance_campingrifle_secondary_spread"), cvar("g_balance_campingrifle_secondary_damage"), cvar("g_balance_campingrifle_secondary_headshotaddeddamage"), cvar("g_balance_campingrifle_secondary_force"), cvar("g_balance_campingrifle_secondary_speed"), cvar("g_balance_campingrifle_secondary_lifetime"), cvar("g_balance_campingrifle_secondary_ammo"), WEP_CAMPINGRIFLE | HITTYPE_SECONDARY, cvar("g_balance_campingrifle_secondary_bulletconstant"));
120 }
121
122 void spawnfunc_weapon_campingrifle (void)
123 {
124         weapon_defaultspawnfunc(WEP_CAMPINGRIFLE);
125 }
126
127 .void(void) campingrifle_bullethail_attackfunc;
128 .float campingrifle_bullethail_frame;
129 .float campingrifle_bullethail_animtime;
130 .float campingrifle_bullethail_refire;
131 void W_CampingRifle_BulletHail_Continue()
132 {
133         float r, sw, af;
134         W_CampingRifle_CheckReloadAndReady();
135         if(self.campingrifle_bulletcounter < 0)
136                 return; // reloading, so we are done
137         sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
138         af = ATTACK_FINISHED(self);
139         self.switchweapon = self.weapon;
140         ATTACK_FINISHED(self) = time;
141         print(ftos(self.ammo_nails), "\n");
142         r = weapon_prepareattack(self.campingrifle_bullethail_frame == WFRAME_FIRE2, self.campingrifle_bullethail_refire);
143         if(self.switchweapon == self.weapon)
144                 self.switchweapon = sw;
145         if(r)
146         {
147                 self.campingrifle_bullethail_attackfunc();
148                 weapon_thinkf(self.campingrifle_bullethail_frame, self.campingrifle_bullethail_animtime, W_CampingRifle_BulletHail_Continue);
149                 print("thinkf set\n");
150         }
151         else
152         {
153                 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
154                 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
155         }
156 }
157
158 void W_CampingRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
159 {
160         // if we get here, we have at least one bullet to fire
161         AttackFunc();
162         if(mode)
163         {
164                 // continue hail
165                 self.campingrifle_bullethail_attackfunc = AttackFunc;
166                 self.campingrifle_bullethail_frame = fr;
167                 self.campingrifle_bullethail_animtime = animtime;
168                 self.campingrifle_bullethail_refire = refire;
169                 weapon_thinkf(fr, animtime, W_CampingRifle_BulletHail_Continue);
170         }
171         else
172         {
173                 // just one shot
174                 weapon_thinkf(fr, animtime, W_CampingRifle_CheckReloadAndReady);
175         }
176 }
177
178 .float bot_secondary_campingriflemooth;
179 float w_campingrifle(float req)
180 {
181         float full;
182         if (req == WR_AIM)
183         {
184                 self.BUTTON_ATCK=FALSE;
185                 self.BUTTON_ATCK2=FALSE;
186                 if(vlen(self.origin-self.enemy.origin) > 1000)
187                         self.bot_secondary_campingriflemooth = 0;
188                 if(self.bot_secondary_campingriflemooth == 0)
189                 {
190                         if(bot_aim(cvar("g_balance_campingrifle_primary_speed"), 0, cvar("g_balance_campingrifle_primary_lifetime"), TRUE))
191                         {
192                                 self.BUTTON_ATCK = TRUE;
193                                 if(random() < 0.01) self.bot_secondary_campingriflemooth = 1;
194                         }
195                 }
196                 else
197                 {
198                         if(bot_aim(cvar("g_balance_campingrifle_secondary_speed"), 0, cvar("g_balance_campingrifle_secondary_lifetime"), TRUE))
199                         {
200                                 self.BUTTON_ATCK2 = TRUE;
201                                 if(random() < 0.03) self.bot_secondary_campingriflemooth = 0;
202                         }
203                 }
204         }
205         else if (req == WR_THINK)
206         {
207                 if(self.campingrifle_bulletcounter < 0) // forced reload (e.g. because interrupted)
208                 {
209                         if(self.switchweapon == self.weapon)
210                         if(self.weaponentity.state == WS_READY)
211                                 W_CampingRifle_Reload();
212                 }
213                 else
214                 {
215                         self.campingrifle_accumulator = bound(time - cvar("g_balance_campingrifle_bursttime"), self.campingrifle_accumulator, time);
216                         if (self.BUTTON_ATCK)
217                         if (weapon_prepareattack_check(0, cvar("g_balance_campingrifle_primary_refire")))
218                         if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_primary_burstcost"))
219                         {
220                                 weapon_prepareattack_do(0, cvar("g_balance_campingrifle_primary_refire"));
221                                 W_CampingRifle_BulletHail(cvar("g_balance_campingrifle_primary_bullethail"), W_CampingRifle_Attack, WFRAME_FIRE1, cvar("g_balance_campingrifle_primary_animtime"), cvar("g_balance_campingrifle_primary_refire"));
222                                 self.campingrifle_accumulator += cvar("g_balance_campingrifle_primary_burstcost");
223                         }
224                         if (self.BUTTON_ATCK2)
225                         {       
226                                 if (cvar("g_balance_campingrifle_secondary"))
227                                 {
228                                         if (weapon_prepareattack_check(1, cvar("g_balance_campingrifle_secondary_refire")))
229                                         if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_secondary_burstcost"))
230                                         {
231                                                 weapon_prepareattack_do(1, cvar("g_balance_campingrifle_secondary_refire"));
232                                                 W_CampingRifle_BulletHail(cvar("g_balance_campingrifle_secondary_bullethail"), W_CampingRifle_Attack2, WFRAME_FIRE2, cvar("g_balance_campingrifle_secondary_animtime"), cvar("g_balance_campingrifle_primary_refire"));
233                                                 self.campingrifle_accumulator += cvar("g_balance_campingrifle_secondary_burstcost");
234                                         }
235                                 }
236                         }
237                 }
238         }
239         else if (req == WR_PRECACHE)
240         {
241                 precache_model ("models/weapons/g_campingrifle.md3");
242                 precache_model ("models/weapons/v_campingrifle.md3");
243                 precache_model ("models/weapons/h_campingrifle.iqm");
244                 precache_sound ("weapons/campingrifle_reload.wav");
245                 precache_sound ("weapons/campingrifle_fire.wav");
246                 precache_sound ("weapons/campingrifle_fire2.wav");
247         }
248         else if (req == WR_SETUP)
249         {
250                 weapon_setup(WEP_CAMPINGRIFLE);
251
252                 full = W_CampingRifle_CheckMaxBullets(TRUE);
253                 if(cvar("g_balance_campingrifle_auto_reload_after_changing_weapons"))
254                         if(!full)
255                                 self.campingrifle_bulletcounter = -1;
256         }
257         else if (req == WR_CHECKAMMO1)
258                 return self.ammo_nails >= cvar("g_balance_campingrifle_primary_ammo");
259         else if (req == WR_CHECKAMMO2)
260                 return self.ammo_nails >= cvar("g_balance_campingrifle_secondary_ammo");
261         else if (req == WR_RELOAD)
262         {
263                 W_CampingRifle_Reload();
264         }
265         else if (req == WR_RESETPLAYER)
266         {
267                 self.campingrifle_accumulator = time - cvar("g_balance_campingrifle_bursttime");
268                 self.campingrifle_bulletcounter = cvar("g_balance_campingrifle_magazinecapacity");
269                 W_CampingRifle_CheckMaxBullets(FALSE);
270         }
271         return TRUE;
272 };
273 #endif
274 #ifdef CSQC
275 float w_campingrifle(float req)
276 {
277         if(req == WR_IMPACTEFFECT)
278         {
279                 vector org2;
280                 org2 = w_org + w_backoff * 2;
281                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
282                 if(!w_issilent)
283                 {
284                         if(w_random < 0.2)
285                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
286                         else if(w_random < 0.4)
287                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
288                         else if(w_random < 0.5)
289                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
290                 }
291         }
292         else if(req == WR_PRECACHE)
293         {
294                 precache_sound("weapons/ric1.wav");
295                 precache_sound("weapons/ric2.wav");
296                 precache_sound("weapons/ric3.wav");
297         }
298         else if (req == WR_SUICIDEMESSAGE)
299         {
300                 if(w_deathtype & HITTYPE_SECONDARY)
301                         w_deathtypestring = "%s shot themself automatically";
302                 else
303                         w_deathtypestring = "%s sniped themself somehow";
304         }
305         else if (req == WR_KILLMESSAGE)
306         {
307                 if(w_deathtype & HITTYPE_SECONDARY)
308                 {
309                         if(w_deathtype & HITTYPE_BOUNCE)
310                                 w_deathtypestring = "%s failed to hide from %s's bullet hail";
311                         else
312                                 w_deathtypestring = "%s died in %s's bullet hail";
313                 }
314                 else
315                 {
316                         if(w_deathtype & HITTYPE_BOUNCE)
317                         {
318                                 // TODO special headshot message here too?
319                                 w_deathtypestring = "%s failed to hide from %s's rifle";
320                         }
321                         else
322                         {
323                                 if(w_deathtype & HITTYPE_HEADSHOT)
324                                         w_deathtypestring = "%s got hit in the head by %s";
325                                 else
326                                         w_deathtypestring = "%s was sniped by %s";
327                         }
328                 }
329         }
330         return TRUE;
331 }
332 #endif
333 #endif