]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_campingrifle.qc
Merge remote branch 'refs/remotes/origin/divVerent/mapinfo-cleanup'
[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                         {       
223                                 if (cvar("g_balance_campingrifle_secondary"))
224                                 {
225                                         if (weapon_prepareattack_check(1, cvar("g_balance_campingrifle_secondary_refire")))
226                                         if (time >= self.campingrifle_accumulator + cvar("g_balance_campingrifle_secondary_burstcost"))
227                                         {
228                                                 weapon_prepareattack_do(1, cvar("g_balance_campingrifle_secondary_refire"));
229                                                 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"));
230                                                 self.campingrifle_accumulator += cvar("g_balance_campingrifle_secondary_burstcost");
231                                         }
232                                 }
233                         }
234                 }
235         }
236         else if (req == WR_PRECACHE)
237         {
238                 precache_model ("models/weapons/g_campingrifle.md3");
239                 precache_model ("models/weapons/v_campingrifle.md3");
240                 precache_model ("models/weapons/h_campingrifle.iqm");
241                 precache_sound ("weapons/campingrifle_reload.wav");
242                 precache_sound ("weapons/campingrifle_fire.wav");
243                 precache_sound ("weapons/campingrifle_fire2.wav");
244         }
245         else if (req == WR_SETUP)
246         {
247                 weapon_setup(WEP_CAMPINGRIFLE);
248
249                 full = W_CampingRifle_CheckMaxBullets(TRUE);
250                 if(cvar("g_balance_campingrifle_auto_reload_after_changing_weapons"))
251                         if(!full)
252                                 self.campingrifle_bulletcounter = -1;
253         }
254         else if (req == WR_CHECKAMMO1)
255                 return self.ammo_nails >= cvar("g_balance_campingrifle_primary_ammo");
256         else if (req == WR_CHECKAMMO2)
257                 return self.ammo_nails >= cvar("g_balance_campingrifle_secondary_ammo");
258         else if (req == WR_RELOAD)
259         {
260                 W_CampingRifle_Reload();
261         }
262         else if (req == WR_RESETPLAYER)
263         {
264                 self.campingrifle_accumulator = time - cvar("g_balance_campingrifle_bursttime");
265                 self.campingrifle_bulletcounter = cvar("g_balance_campingrifle_magazinecapacity");
266                 W_CampingRifle_CheckMaxBullets(FALSE);
267         }
268         return TRUE;
269 };
270 #endif
271 #ifdef CSQC
272 float w_campingrifle(float req)
273 {
274         if(req == WR_IMPACTEFFECT)
275         {
276                 vector org2;
277                 org2 = w_org + w_backoff * 2;
278                 pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
279                 if(!w_issilent)
280                 {
281                         if(w_random < 0.2)
282                                 sound(self, CHAN_PROJECTILE, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
283                         else if(w_random < 0.4)
284                                 sound(self, CHAN_PROJECTILE, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
285                         else if(w_random < 0.5)
286                                 sound(self, CHAN_PROJECTILE, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
287                 }
288         }
289         else if(req == WR_PRECACHE)
290         {
291                 precache_sound("weapons/ric1.wav");
292                 precache_sound("weapons/ric2.wav");
293                 precache_sound("weapons/ric3.wav");
294         }
295         else if (req == WR_SUICIDEMESSAGE)
296         {
297                 if(w_deathtype & HITTYPE_SECONDARY)
298                         w_deathtypestring = "%s shot themself automatically";
299                 else
300                         w_deathtypestring = "%s sniped themself somehow";
301         }
302         else if (req == WR_KILLMESSAGE)
303         {
304                 if(w_deathtype & HITTYPE_SECONDARY)
305                 {
306                         if(w_deathtype & HITTYPE_BOUNCE)
307                                 w_deathtypestring = "%s failed to hide from %s's bullet hail";
308                         else
309                                 w_deathtypestring = "%s died in %s's bullet hail";
310                 }
311                 else
312                 {
313                         if(w_deathtype & HITTYPE_BOUNCE)
314                         {
315                                 // TODO special headshot message here too?
316                                 w_deathtypestring = "%s failed to hide from %s's rifle";
317                         }
318                         else
319                         {
320                                 if(w_deathtype & HITTYPE_HEADSHOT)
321                                         w_deathtypestring = "%s got hit in the head by %s";
322                                 else
323                                         w_deathtypestring = "%s was sniped by %s";
324                         }
325                 }
326         }
327         return TRUE;
328 }
329 #endif
330 #endif