]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_campingrifle.qc
Merge remote branch 'origin/fruitiex/fruitbalance' into divVerent/fruitbalance
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_campingrifle.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(CAMPINGRIFLE, w_campingrifle, IT_NAILS, 3, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "campingrifle", "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", cvar("g_balance_campingrifle_secondary_damage"));
88         else
89                 W_SetupShot (self, cvar("g_antilag_bullets") && pSpeed >= cvar("g_antilag_bullets"), 2, "weapons/campingrifle_fire.wav", cvar("g_balance_campingrifle_primary_damage"));
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         fireBallisticBullet(w_shotorg, w_shotdir, pSpread, pSpeed, pLifetime, pDamage, pHeadshotAddedDamage / pDamage, pForce, deathtype, (cvar("g_balance_campingrifle_tracer") ? EF_RED : EF_BLUE), 1, pBulletConstant);
100         endFireBallisticBullet();
101
102         if (cvar("g_casings") >= 2)
103                 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);
104         
105         self.campingrifle_bulletcounter = self.campingrifle_bulletcounter - 1;
106         W_CampingRifle_CheckMaxBullets(TRUE);
107 }
108
109 void W_CampingRifle_Attack()
110 {
111         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"));
112 }
113
114 void W_CampingRifle_Attack2()
115 {
116         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"));
117 }
118
119 void spawnfunc_weapon_campingrifle (void)
120 {
121         weapon_defaultspawnfunc(WEP_CAMPINGRIFLE);
122 }
123
124 .void(void) campingrifle_bullethail_attackfunc;
125 .float campingrifle_bullethail_frame;
126 .float campingrifle_bullethail_animtime;
127 .float campingrifle_bullethail_refire;
128 void W_CampingRifle_BulletHail_Continue()
129 {
130         float r, sw, af;
131         W_CampingRifle_CheckReloadAndReady();
132         if(self.campingrifle_bulletcounter < 0)
133                 return; // reloading, so we are done
134         sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
135         af = ATTACK_FINISHED(self);
136         self.switchweapon = self.weapon;
137         ATTACK_FINISHED(self) = time;
138         print(ftos(self.ammo_nails), "\n");
139         r = weapon_prepareattack(self.campingrifle_bullethail_frame == WFRAME_FIRE2, self.campingrifle_bullethail_refire);
140         if(self.switchweapon == self.weapon)
141                 self.switchweapon = sw;
142         if(r)
143         {
144                 self.campingrifle_bullethail_attackfunc();
145                 weapon_thinkf(self.campingrifle_bullethail_frame, self.campingrifle_bullethail_animtime, W_CampingRifle_BulletHail_Continue);
146                 print("thinkf set\n");
147         }
148         else
149         {
150                 ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
151                 print("out of ammo... ", ftos(self.weaponentity.state), "\n");
152         }
153 }
154
155 void W_CampingRifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
156 {
157         // if we get here, we have at least one bullet to fire
158         AttackFunc();
159         if(mode)
160         {
161                 // continue hail
162                 self.campingrifle_bullethail_attackfunc = AttackFunc;
163                 self.campingrifle_bullethail_frame = fr;
164                 self.campingrifle_bullethail_animtime = animtime;
165                 self.campingrifle_bullethail_refire = refire;
166                 weapon_thinkf(fr, animtime, W_CampingRifle_BulletHail_Continue);
167         }
168         else
169         {
170                 // just one shot
171                 weapon_thinkf(fr, animtime, W_CampingRifle_CheckReloadAndReady);
172         }
173 }
174
175 .float bot_secondary_campingriflemooth;
176 .float sent_campingrifle_scope;
177 float w_campingrifle(float req)
178 {
179         float full;
180         if (req == WR_AIM)
181         {
182                 self.BUTTON_ATCK=FALSE;
183                 self.BUTTON_ATCK2=FALSE;
184                 if(vlen(self.origin-self.enemy.origin) > 1000)
185                         self.bot_secondary_campingriflemooth = 0;
186                 if(self.bot_secondary_campingriflemooth == 0)
187                 {
188                         if(bot_aim(cvar("g_balance_campingrifle_primary_speed"), 0, cvar("g_balance_campingrifle_primary_lifetime"), TRUE))
189                         {
190                                 self.BUTTON_ATCK = TRUE;
191                                 if(random() < 0.01) self.bot_secondary_campingriflemooth = 1;
192                         }
193                 }
194                 else
195                 {
196                         if(bot_aim(cvar("g_balance_campingrifle_secondary_speed"), 0, cvar("g_balance_campingrifle_secondary_lifetime"), TRUE))
197                         {
198                                 self.BUTTON_ATCK2 = TRUE;
199                                 if(random() < 0.03) self.bot_secondary_campingriflemooth = 0;
200                         }
201                 }
202         }
203         else if (req == WR_THINK)
204         {
205                 if(self.campingrifle_bulletcounter < 0) // forced reload (e.g. because interrupted)
206                 {
207                         if(self.switchweapon == self.weapon)
208                         if(self.weaponentity.state == WS_READY)
209                                 W_CampingRifle_Reload();
210                 }
211                 else
212                 {
213                         self.campingrifle_accumulator = bound(time - cvar("g_balance_campingrifle_bursttime"), self.campingrifle_accumulator, time);
214                         if (self.BUTTON_ATCK)
215                         if (weapon_prepareattack_check(0, cvar("g_balance_campingrifle_primary_refire")))
216                         if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_primary_burstcost"))
217                         {
218                                 weapon_prepareattack_do(0, cvar("g_balance_campingrifle_primary_refire"));
219                                 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"));
220                                 self.campingrifle_accumulator += cvar("g_balance_campingrifle_primary_burstcost");
221                         }
222                         if (self.BUTTON_ATCK2)
223                         {       
224                                 if (cvar("g_balance_campingrifle_secondary"))
225                                 {
226                                         if (weapon_prepareattack_check(1, cvar("g_balance_campingrifle_secondary_refire")))
227                                         if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_secondary_burstcost"))
228                                         {
229                                                 weapon_prepareattack_do(1, cvar("g_balance_campingrifle_secondary_refire"));
230                                                 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"));
231                                                 self.campingrifle_accumulator += cvar("g_balance_campingrifle_secondary_burstcost");
232                                         }
233                                 }
234                                 else
235                                 {
236                                         if(clienttype(self) == CLIENTTYPE_REAL)
237                                         if(!self.sent_campingrifle_scope)
238                                         {
239                                                 msg_entity = self;
240                                                 WriteByte(MSG_ONE, SVC_TEMPENTITY);
241                                                 WriteByte(MSG_ONE, TE_CSQC_CAMPINGRIFLE_SCOPE);
242                                                 self.sent_campingrifle_scope = 1;
243                                         }       
244                                 }
245                         }
246                 }
247         }
248         else if (req == WR_PRECACHE)
249         {
250                 precache_model ("models/weapons/g_campingrifle.md3");
251                 precache_model ("models/weapons/v_campingrifle.md3");
252                 precache_model ("models/weapons/h_campingrifle.iqm");
253                 precache_sound ("weapons/campingrifle_reload.wav");
254                 precache_sound ("weapons/campingrifle_fire.wav");
255                 precache_sound ("weapons/campingrifle_fire2.wav");
256         }
257         else if (req == WR_SETUP)
258         {
259                 weapon_setup(WEP_CAMPINGRIFLE);
260
261                 full = W_CampingRifle_CheckMaxBullets(TRUE);
262                 if(cvar("g_balance_campingrifle_auto_reload_after_changing_weapons"))
263                         if(!full)
264                                 self.campingrifle_bulletcounter = -1;
265         }
266         else if (req == WR_CHECKAMMO1)
267                 return self.ammo_nails >= cvar("g_balance_campingrifle_primary_ammo");
268         else if (req == WR_CHECKAMMO2)
269                 return self.ammo_nails >= cvar("g_balance_campingrifle_secondary_ammo");
270         else if (req == WR_RELOAD)
271         {
272                 W_CampingRifle_Reload();
273         }
274         else if (req == WR_RESETPLAYER)
275         {
276                 self.campingrifle_accumulator = time - cvar("g_balance_campingrifle_bursttime");
277                 self.campingrifle_bulletcounter = cvar("g_balance_campingrifle_magazinecapacity");
278                 W_CampingRifle_CheckMaxBullets(FALSE);
279         }
280         return TRUE;
281 };
282 #endif
283 #ifdef CSQC
284 float w_campingrifle(float req)
285 {
286         if(req == WR_IMPACTEFFECT)
287         {
288                 vector org2;
289                 org2 = w_org + w_backoff * 2;
290                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
291                 if(!w_issilent)
292                 {
293                         if(w_random < 0.2)
294                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
295                         else if(w_random < 0.4)
296                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
297                         else if(w_random < 0.5)
298                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
299                 }
300         }
301         else if(req == WR_PRECACHE)
302         {
303                 precache_sound("weapons/ric1.wav");
304                 precache_sound("weapons/ric2.wav");
305                 precache_sound("weapons/ric3.wav");
306         }
307         else if (req == WR_SUICIDEMESSAGE)
308         {
309                 if(w_deathtype & HITTYPE_SECONDARY)
310                         w_deathtypestring = "%s shot themself automatically";
311                 else
312                         w_deathtypestring = "%s sniped themself somehow";
313         }
314         else if (req == WR_KILLMESSAGE)
315         {
316                 if(w_deathtype & HITTYPE_SECONDARY)
317                 {
318                         if(w_deathtype & HITTYPE_BOUNCE)
319                                 w_deathtypestring = "%s failed to hide from %s's bullet hail";
320                         else
321                                 w_deathtypestring = "%s died in %s's bullet hail";
322                 }
323                 else
324                 {
325                         if(w_deathtype & HITTYPE_BOUNCE)
326                         {
327                                 // TODO special headshot message here too?
328                                 w_deathtypestring = "%s failed to hide from %s's rifle";
329                         }
330                         else
331                         {
332                                 if(w_deathtype & HITTYPE_HEADSHOT)
333                                         w_deathtypestring = "%s got hit in the head by %s";
334                                 else
335                                         w_deathtypestring = "%s was sniped by %s";
336                         }
337                 }
338         }
339         return TRUE;
340 }
341 #endif
342 #endif