]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_campingrifle.qc
Merge branch 'master' into fruitiex/newpanelhud_stable
[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 w_campingrifle(float req)
177 {
178         float full;
179         if (req == WR_AIM)
180         {
181                 self.BUTTON_ATCK=FALSE;
182                 self.BUTTON_ATCK2=FALSE;
183                 if(vlen(self.origin-self.enemy.origin) > 1000)
184                         self.bot_secondary_campingriflemooth = 0;
185                 if(self.bot_secondary_campingriflemooth == 0)
186                 {
187                         if(bot_aim(cvar("g_balance_campingrifle_primary_speed"), 0, cvar("g_balance_campingrifle_primary_lifetime"), TRUE))
188                         {
189                                 self.BUTTON_ATCK = TRUE;
190                                 if(random() < 0.01) self.bot_secondary_campingriflemooth = 1;
191                         }
192                 }
193                 else
194                 {
195                         if(bot_aim(cvar("g_balance_campingrifle_secondary_speed"), 0, cvar("g_balance_campingrifle_secondary_lifetime"), TRUE))
196                         {
197                                 self.BUTTON_ATCK2 = TRUE;
198                                 if(random() < 0.03) self.bot_secondary_campingriflemooth = 0;
199                         }
200                 }
201         }
202         else if (req == WR_THINK)
203         {
204                 if(self.campingrifle_bulletcounter < 0) // forced reload (e.g. because interrupted)
205                 {
206                         if(self.switchweapon == self.weapon)
207                         if(self.weaponentity.state == WS_READY)
208                                 W_CampingRifle_Reload();
209                 }
210                 else
211                 {
212                         self.campingrifle_accumulator = bound(time - cvar("g_balance_campingrifle_bursttime"), self.campingrifle_accumulator, time);
213                         if (self.BUTTON_ATCK)
214                         if (weapon_prepareattack_check(0, cvar("g_balance_campingrifle_primary_refire")))
215                         if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_primary_burstcost"))
216                         {
217                                 weapon_prepareattack_do(0, cvar("g_balance_campingrifle_primary_refire"));
218                                 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"));
219                                 self.campingrifle_accumulator += cvar("g_balance_campingrifle_primary_burstcost");
220                         }
221                         if (self.BUTTON_ATCK2)
222                         if (weapon_prepareattack_check(1, cvar("g_balance_campingrifle_secondary_refire")))
223                         if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_secondary_burstcost"))
224                         {
225                                 weapon_prepareattack_do(1, cvar("g_balance_campingrifle_secondary_refire"));
226                                 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"));
227                                 self.campingrifle_accumulator += cvar("g_balance_campingrifle_secondary_burstcost");
228                         }
229                 }
230         }
231         else if (req == WR_PRECACHE)
232         {               
233                 precache_model ("models/weapons/g_campingrifle.md3");
234                 precache_model ("models/weapons/v_campingrifle.md3");
235                 precache_model ("models/weapons/h_campingrifle.iqm");
236                 precache_sound ("weapons/campingrifle_reload.wav");
237                 precache_sound ("weapons/campingrifle_fire.wav");
238                 precache_sound ("weapons/campingrifle_fire2.wav");
239         }
240         else if (req == WR_SETUP)
241         {
242                 weapon_setup(WEP_CAMPINGRIFLE);
243
244                 full = W_CampingRifle_CheckMaxBullets(TRUE);
245                 if(cvar("g_balance_campingrifle_auto_reload_after_changing_weapons"))
246                         if(!full)
247                                 self.campingrifle_bulletcounter = -1;
248         }
249         else if (req == WR_CHECKAMMO1)
250                 return self.ammo_nails >= cvar("g_balance_campingrifle_primary_ammo");
251         else if (req == WR_CHECKAMMO2)
252                 return self.ammo_nails >= cvar("g_balance_campingrifle_secondary_ammo");
253         else if (req == WR_RELOAD)
254         {
255                 W_CampingRifle_Reload();
256         }
257         else if (req == WR_RESETPLAYER)
258         {
259                 self.campingrifle_accumulator = time - cvar("g_balance_campingrifle_bursttime");
260                 self.campingrifle_bulletcounter = cvar("g_balance_campingrifle_magazinecapacity");
261                 W_CampingRifle_CheckMaxBullets(FALSE);
262         }
263         return TRUE;
264 };
265 #endif
266 #ifdef CSQC
267 float w_campingrifle(float req)
268 {
269         if(req == WR_IMPACTEFFECT)
270         {
271                 vector org2;
272                 org2 = w_org + w_backoff * 2;
273                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
274                 if(!w_issilent)
275                 {
276                         if(w_random < 0.2)
277                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
278                         else if(w_random < 0.4)
279                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
280                         else if(w_random < 0.5)
281                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
282                 }
283         }
284         else if(req == WR_PRECACHE)
285         {
286                 precache_sound("weapons/ric1.wav");
287                 precache_sound("weapons/ric2.wav");
288                 precache_sound("weapons/ric3.wav");
289         }
290         else if (req == WR_SUICIDEMESSAGE)
291         {
292                 if(w_deathtype & HITTYPE_SECONDARY)
293                         w_deathtypestring = "shot themself automatically";
294                 else
295                         w_deathtypestring = "sniped themself somehow";
296         }
297         else if (req == WR_KILLMESSAGE)
298         {
299                 if(w_deathtype & HITTYPE_SECONDARY)
300                 {
301                         if(w_deathtype & HITTYPE_BOUNCE)
302                                 w_deathtypestring = "failed to hide from #'s bullet hail";
303                         else
304                                 w_deathtypestring = "died in #'s bullet hail";
305                 }
306                 else
307                 {
308                         if(w_deathtype & HITTYPE_BOUNCE)
309                         {
310                                 // TODO special headshot message here too?
311                                 w_deathtypestring = "failed to hide from #'s rifle";
312                         }
313                         else
314                         {
315                                 if(w_deathtype & HITTYPE_HEADSHOT)
316                                         w_deathtypestring = "got hit in the head by #";
317                                 else
318                                         w_deathtypestring = "was sniped by #";
319                         }
320                 }
321         }
322         return TRUE;
323 }
324 #endif
325 #endif