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