2 ===========================================================================
4 CLIENT WEAPONSYSTEM CODE
5 Bring back W_Weaponframe
7 ===========================================================================
10 .float weapon_frametime;
12 float W_WeaponRateFactor()
15 t = 1.0 / g_weaponratefactor;
20 // VorteX: static frame globals
21 const float WFRAME_DONTCHANGE = -1;
22 const float WFRAME_FIRE1 = 0;
23 const float WFRAME_FIRE2 = 1;
24 const float WFRAME_IDLE = 2;
25 const float WFRAME_RELOAD = 3;
28 void(float fr, float t, void() func) weapon_thinkf;
30 float CL_Weaponentity_CustomizeEntityForClient()
32 self.viewmodelforclient = self.owner;
34 if(other.enemy == self.owner)
35 self.viewmodelforclient = other;
42 * 1. simple animated model, muzzle flash handling on h_ model:
43 * h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
45 * shot = muzzle end (shot origin, also used for muzzle flashes)
46 * shell = casings ejection point (must be on the right hand side of the gun)
47 * weapon = attachment for v_tuba.md3
48 * v_tuba.md3 - first and third person model
49 * g_tuba.md3 - pickup model
51 * 2. simple animated model, muzzle flash handling on v_ model:
52 * h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
54 * weapon = attachment for v_tuba.md3
55 * v_tuba.md3 - first and third person model
57 * shot = muzzle end (shot origin, also used for muzzle flashes)
58 * shell = casings ejection point (must be on the right hand side of the gun)
59 * g_tuba.md3 - pickup model
61 * 3. fully animated model, muzzle flash handling on h_ model:
62 * h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
64 * shot = muzzle end (shot origin, also used for muzzle flashes)
65 * shell = casings ejection point (must be on the right hand side of the gun)
66 * handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes)
67 * v_tuba.md3 - third person model
68 * g_tuba.md3 - pickup model
70 * 4. fully animated model, muzzle flash handling on v_ model:
71 * h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
73 * shot = muzzle end (shot origin)
74 * shell = casings ejection point (must be on the right hand side of the gun)
75 * v_tuba.md3 - third person model
77 * shot = muzzle end (for muzzle flashes)
78 * g_tuba.md3 - pickup model
82 // self.origin, self.angles
84 // self.movedir, self.view_ofs
91 #define WEP_MODEL_FIRSTPERSON model // h_ // wep.WEP_MODEL_FIRSTPERSON
92 #define WEP_MODEL_THIRDPERSON model2 // v_ // wep.WEP_MODEL_THIRDPERSON
93 #define WEP_MODEL_PICKUP model3 // g_ // wep.WEP_MODEL_PICKUP
94 #define WEP_MODEL_SIMPLE mdl
95 void CL_WeaponEntity_SetModel(entity wep)
100 // if there is a child entity, hide it until we're sure we use it
101 if (self.weaponentity)
102 self.weaponentity.model = "";
103 setmodel(self, wep.WEP_MODEL_THIRDPERSON); // precision set below
104 v_shot_idx = gettagindex(self, "shot"); // used later
106 v_shot_idx = gettagindex(self, "tag_shot");
108 setmodel(self, wep.WEP_MODEL_FIRSTPERSON); // precision set below
109 // preset some defaults that work great for renamed zym files (which don't need an animinfo)
110 self.anim_fire1 = animfixfps(self, '0 1 0.01', '0 0 0');
111 self.anim_fire2 = animfixfps(self, '1 1 0.01', '0 0 0');
112 self.anim_idle = animfixfps(self, '2 1 0.01', '0 0 0');
113 self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0');
115 // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
116 // if we don't, this is a "real" animated model
117 if(gettagindex(self, "weapon"))
119 if (!self.weaponentity)
120 self.weaponentity = spawn();
121 setmodel(self.weaponentity, wep.WEP_MODEL_THIRDPERSON); // precision does not matter
122 setattachment(self.weaponentity, self, "weapon");
124 else if(gettagindex(self, "tag_weapon"))
126 if (!self.weaponentity)
127 self.weaponentity = spawn();
128 setmodel(self.weaponentity, wep.WEP_MODEL_THIRDPERSON); // precision does not matter
129 setattachment(self.weaponentity, self, "tag_weapon");
133 if(self.weaponentity)
134 remove(self.weaponentity);
135 self.weaponentity = world;
138 setorigin(self,'0 0 0');
139 self.angles = '0 0 0';
141 self.viewmodelforclient = world;
145 if(v_shot_idx) // v_ model attached to invisible h_ model
147 self.movedir = gettaginfo(self.weaponentity, v_shot_idx);
151 idx = gettagindex(self, "shot");
153 idx = gettagindex(self, "tag_shot");
155 self.movedir = gettaginfo(self, idx);
158 print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
159 self.movedir = '0 0 0';
163 if(self.weaponentity) // v_ model attached to invisible h_ model
165 idx = gettagindex(self.weaponentity, "shell");
167 idx = gettagindex(self.weaponentity, "tag_shell");
169 self.spawnorigin = gettaginfo(self.weaponentity, idx);
175 idx = gettagindex(self, "shell");
177 idx = gettagindex(self, "tag_shell");
179 self.spawnorigin = gettaginfo(self, idx);
182 print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
183 self.spawnorigin = self.movedir;
189 self.oldorigin = '0 0 0'; // use regular attachment
193 if(self.weaponentity)
195 idx = gettagindex(self, "weapon");
197 idx = gettagindex(self, "tag_weapon");
201 idx = gettagindex(self, "handle");
203 idx = gettagindex(self, "tag_handle");
207 self.oldorigin = self.movedir - gettaginfo(self, idx);
211 print("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
212 self.oldorigin = '0 0 0'; // there is no way to recover from this
216 self.viewmodelforclient = self.owner;
221 if(self.weaponentity)
222 remove(self.weaponentity);
223 self.weaponentity = world;
224 self.movedir = '0 0 0';
225 self.spawnorigin = '0 0 0';
226 self.oldorigin = '0 0 0';
227 self.anim_fire1 = '0 1 0.01';
228 self.anim_fire2 = '0 1 0.01';
229 self.anim_idle = '0 1 0.01';
230 self.anim_reload = '0 1 0.01';
233 self.view_ofs = '0 0 0';
235 if(self.movedir_x >= 0)
239 self.movedir = shotorg_adjust(v0, FALSE, FALSE);
240 self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0;
242 self.owner.stat_shotorg = compressShotOrigin(self.movedir);
243 self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly
245 self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount
247 // check if an instant weapon switch occurred
248 setorigin(self, self.view_ofs);
249 // reset animstate now
250 self.wframe = WFRAME_IDLE;
251 setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
254 vector CL_Weapon_GetShotOrg(float wpn)
260 CL_WeaponEntity_SetModel(get_weaponinfo(wpn));
262 CL_WeaponEntity_SetModel(world);
268 void CL_Weaponentity_Think()
271 self.nextthink = time;
272 if (intermission_running)
273 self.frame = self.anim_idle_x;
274 if (self.owner.weaponentity != self)
276 if (self.weaponentity)
277 remove(self.weaponentity);
281 if (self.owner.deadflag != DEAD_NO)
284 if (self.weaponentity)
285 self.weaponentity.model = "";
288 if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) // WEAPONTODO: get rid of .weaponname, handle model directly
290 self.weaponname = self.owner.weaponname;
291 self.dmg = self.owner.modelindex;
292 self.deadflag = self.owner.deadflag;
294 // WEAPONTODO: get weapon entity from here!
295 //CL_WeaponEntity_SetModel(self.owner.weaponname);
298 tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT));
299 self.effects = self.owner.effects & EFMASK_CHEAP;
300 self.effects &= ~EF_LOWPRECISION;
301 self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it
302 self.effects &= ~EF_TELEPORT_BIT;
303 self.effects &= ~EF_RESTARTANIM_BIT;
306 if(self.owner.alpha == default_player_alpha)
307 self.alpha = default_weapon_alpha;
308 else if(self.owner.alpha != 0)
309 self.alpha = self.owner.alpha;
313 self.glowmod = self.owner.weaponentity_glowmod;
314 self.colormap = self.owner.colormap;
315 if (self.weaponentity)
317 self.weaponentity.effects = self.effects;
318 self.weaponentity.alpha = self.alpha;
319 self.weaponentity.colormap = self.colormap;
320 self.weaponentity.glowmod = self.glowmod;
323 self.angles = '0 0 0';
325 float f = (self.owner.weapon_nextthink - time);
326 if (self.state == WS_RAISE && !intermission_running)
328 entity newwep = get_weaponinfo(self.owner.switchweapon);
329 f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise);
330 self.angles_x = -90 * f * f;
332 else if (self.state == WS_DROP && !intermission_running)
334 entity oldwep = get_weaponinfo(self.owner.weapon);
335 f = 1 - f * g_weaponratefactor / max(f, oldwep.switchdelay_drop);
336 self.angles_x = -90 * f * f;
338 else if (self.state == WS_CLEAR)
341 self.angles_x = -90 * f * f;
345 void CL_ExteriorWeaponentity_Think()
348 self.nextthink = time;
349 if (self.owner.exteriorweaponentity != self)
354 if (self.owner.deadflag != DEAD_NO)
359 if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
361 self.weaponname = self.owner.weaponname;
362 self.dmg = self.owner.modelindex;
363 self.deadflag = self.owner.deadflag;
364 if (self.owner.weaponname != "")
365 setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below
369 if((tag_found = gettagindex(self.owner, "tag_weapon")))
371 self.tag_index = tag_found;
372 self.tag_entity = self.owner;
375 setattachment(self, self.owner, "bip01 r hand");
377 self.effects = self.owner.effects;
378 self.effects |= EF_LOWPRECISION;
379 self.effects = self.effects & EFMASK_CHEAP; // eat performance
380 if(self.owner.alpha == default_player_alpha)
381 self.alpha = default_weapon_alpha;
382 else if(self.owner.alpha != 0)
383 self.alpha = self.owner.alpha;
387 self.glowmod = self.owner.weaponentity_glowmod;
388 self.colormap = self.owner.colormap;
390 CSQCMODEL_AUTOUPDATE();
393 // spawning weaponentity for client
394 void CL_SpawnWeaponentity()
396 self.weaponentity = spawn();
397 self.weaponentity.classname = "weaponentity";
398 self.weaponentity.solid = SOLID_NOT;
399 self.weaponentity.owner = self;
400 setmodel(self.weaponentity, ""); // precision set when changed
401 setorigin(self.weaponentity, '0 0 0');
402 self.weaponentity.angles = '0 0 0';
403 self.weaponentity.viewmodelforclient = self;
404 self.weaponentity.flags = 0;
405 self.weaponentity.think = CL_Weaponentity_Think;
406 self.weaponentity.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
407 self.weaponentity.nextthink = time;
409 self.exteriorweaponentity = spawn();
410 self.exteriorweaponentity.classname = "exteriorweaponentity";
411 self.exteriorweaponentity.solid = SOLID_NOT;
412 self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity;
413 self.exteriorweaponentity.owner = self;
414 setorigin(self.exteriorweaponentity, '0 0 0');
415 self.exteriorweaponentity.angles = '0 0 0';
416 self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think;
417 self.exteriorweaponentity.nextthink = time;
420 entity oldself = self;
421 self = self.exteriorweaponentity;
422 CSQCMODEL_AUTOINIT();
430 if (self.weapon != -1)
433 self.switchingweapon = 0;
435 if (self.weaponentity)
437 self.weaponentity.state = WS_CLEAR;
438 self.weaponentity.effects = 0;
444 if (self.weaponentity)
445 self.weaponentity.state = WS_READY;
446 weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
451 float weapon_prepareattack_checkammo(float secondary)
453 if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
454 if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1 + secondary))
456 // always keep the Mine Layer if we placed mines, so that we can detonate them
458 if(self.weapon == WEP_MINE_LAYER)
459 for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
462 if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
464 sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM);
465 self.prevdryfire = time;
468 if(WEP_ACTION(self.weapon, WR_CHECKAMMO2 - secondary)) // check if the other firing mode has enough ammo
470 if(time - self.prevwarntime > 1)
476 ITEM_WEAPON_PRIMORSEC,
482 self.prevwarntime = time;
484 else // this weapon is totally unable to fire, switch to another one
486 W_SwitchToOtherWeapon(self);
494 float weapon_prepareattack_check(float secondary, float attacktime)
496 if(!weapon_prepareattack_checkammo(secondary))
499 //if sv_ready_restart_after_countdown is set, don't allow the player to shoot
500 //if all players readied up and the countdown is running
501 if(time < game_starttime || time < self.race_penalty) {
505 if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused
508 // do not even think about shooting if switching
509 if(self.switchweapon != self.weapon)
514 // don't fire if previous attack is not finished
515 if (ATTACK_FINISHED(self) > time + self.weapon_frametime * 0.5)
517 // don't fire while changing weapon
518 if (self.weaponentity.state != WS_READY)
524 float weapon_prepareattack_do(float secondary, float attacktime)
526 self.weaponentity.state = WS_INUSE;
528 self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire
530 // if the weapon hasn't been firing continuously, reset the timer
533 if (ATTACK_FINISHED(self) < time - self.weapon_frametime * 1.5)
535 ATTACK_FINISHED(self) = time;
536 //dprint("resetting attack finished to ", ftos(time), "\n");
538 ATTACK_FINISHED(self) = ATTACK_FINISHED(self) + attacktime * W_WeaponRateFactor();
540 self.bulletcounter += 1;
541 //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n");
544 float weapon_prepareattack(float secondary, float attacktime)
546 if(weapon_prepareattack_check(secondary, attacktime))
548 weapon_prepareattack_do(secondary, attacktime);
555 void weapon_thinkf(float fr, float t, void() func)
561 if(fr == WFRAME_DONTCHANGE)
563 fr = self.weaponentity.wframe;
566 else if (fr == WFRAME_IDLE)
575 if (self.weaponentity)
577 self.weaponentity.wframe = fr;
579 if (fr == WFRAME_IDLE)
580 a = self.weaponentity.anim_idle;
581 else if (fr == WFRAME_FIRE1)
582 a = self.weaponentity.anim_fire1;
583 else if (fr == WFRAME_FIRE2)
584 a = self.weaponentity.anim_fire2;
585 else // if (fr == WFRAME_RELOAD)
586 a = self.weaponentity.anim_reload;
587 a_z *= g_weaponratefactor;
588 setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim);
595 if(self.weapon_think == w_ready && func != w_ready && self.weaponentity.state == WS_RAISE)
597 backtrace("Tried to override initial weapon think function - should this really happen?");
600 t *= W_WeaponRateFactor();
602 // VorteX: haste can be added here
603 if (self.weapon_think == w_ready)
605 self.weapon_nextthink = time;
606 //dprint("started firing at ", ftos(time), "\n");
608 if (self.weapon_nextthink < time - self.weapon_frametime * 1.5 || self.weapon_nextthink > time + self.weapon_frametime * 1.5)
610 self.weapon_nextthink = time;
611 //dprint("reset weapon animation timer at ", ftos(time), "\n");
613 self.weapon_nextthink = self.weapon_nextthink + t;
614 self.weapon_think = func;
615 //dprint("next ", ftos(self.weapon_nextthink), "\n");
617 if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
619 if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2)
620 animdecide_setaction(self, ANIMACTION_MELEE, restartanim);
622 animdecide_setaction(self, ANIMACTION_SHOOT, restartanim);
626 if(self.anim_upper_action == ANIMACTION_SHOOT || self.anim_upper_action == ANIMACTION_MELEE)
627 self.anim_upper_action = 0;
631 float forbidWeaponUse()
633 if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
635 if(round_handler_IsActive() && !round_handler_IsRoundStarted())
637 if(self.player_blocked)
639 if(self.freezetag_frozen)
649 self.weapon_frametime = frametime;
651 if (!self.weaponentity || self.health < 1)
652 return; // Dead player can't use weapons and injure impulse commands
654 if(forbidWeaponUse())
655 if(self.weaponentity.state != WS_CLEAR)
661 if(!self.switchweapon)
664 self.switchingweapon = 0;
665 self.weaponentity.state = WS_CLEAR;
666 self.weaponname = "";
667 //self.items &= ~IT_AMMO;
671 makevectors(self.v_angle);
672 fo = v_forward; // save them in case the weapon think functions change it
677 if (self.weapon != self.switchweapon)
679 if (self.weaponentity.state == WS_CLEAR)
682 self.switchingweapon = self.switchweapon;
683 entity newwep = get_weaponinfo(self.switchweapon);
685 //self.items &= ~IT_AMMO;
686 //self.items = self.items | (newwep.items & IT_AMMO);
688 // the two weapon entities will notice this has changed and update their models
689 self.weapon = self.switchweapon;
690 self.weaponname = newwep.mdl;
691 self.bulletcounter = 0; // WEAPONTODO
692 //self.ammo_field = newwep.ammo_field;
693 WEP_ACTION(self.switchweapon, WR_SETUP);
694 self.weaponentity.state = WS_RAISE;
696 // set our clip load to the load of the weapon we switched to, if it's reloadable
697 if(newwep.spawnflags & WEP_FLAG_RELOADABLE && newwep.reloading_ammo) // prevent accessing undefined cvars
699 self.clip_load = self.(weapon_load[self.switchweapon]);
700 self.clip_size = newwep.reloading_ammo;
703 self.clip_load = self.clip_size = 0;
705 // VorteX: add player model weapon select frame here
706 // setcustomframe(PlayerWeaponRaise);
707 weapon_thinkf(WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
708 //print(sprintf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname))));
710 else if (self.weaponentity.state == WS_DROP)
712 // in dropping phase we can switch at any time
713 self.switchingweapon = self.switchweapon;
715 else if (self.weaponentity.state == WS_READY)
718 self.switchingweapon = self.switchweapon;
720 entity oldwep = get_weaponinfo(self.weapon);
722 #ifndef INDEPENDENT_ATTACK_FINISHED
723 if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
726 sound (self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
727 self.weaponentity.state = WS_DROP;
728 // set up weapon switch think in the future, and start drop anim
729 weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
730 //print(sprintf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname))));
731 #ifndef INDEPENDENT_ATTACK_FINISHED
737 // LordHavoc: network timing test code
739 // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");
744 // call the think code which may fire the weapon
745 // and do so multiple times to resolve framerate dependency issues if the
746 // server framerate is very low and the weapon fire rate very high
749 while (c < W_TICSPERFRAME)
752 if(w && !(self.weapons & WepSet_FromWeapon(w)))
754 if(self.weapon == self.switchweapon)
755 W_SwitchWeapon_Force(self, w_getbestweapon(self));
764 WEP_ACTION(self.weapon, WR_THINK);
766 WEP_ACTION(self.weapon, WR_GONETHINK);
768 if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)
770 if(self.weapon_think)
778 bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");
783 void W_AttachToShotorg(entity flash, vector offset)
787 flash.angles_z = random() * 360;
789 if(gettagindex(self.weaponentity, "shot"))
790 setattachment(flash, self.weaponentity, "shot");
792 setattachment(flash, self.weaponentity, "tag_shot");
793 setorigin(flash, offset);
796 copyentity(flash, xflash);
798 flash.viewmodelforclient = self;
800 if(self.weaponentity.oldorigin_x > 0)
802 setattachment(xflash, self.exteriorweaponentity, "");
803 setorigin(xflash, self.weaponentity.oldorigin + offset);
807 if(gettagindex(self.exteriorweaponentity, "shot"))
808 setattachment(xflash, self.exteriorweaponentity, "shot");
810 setattachment(xflash, self.exteriorweaponentity, "tag_shot");
811 setorigin(xflash, offset);
815 void W_DecreaseAmmo(float ammo_use)
817 entity wep = get_weaponinfo(self.weapon);
819 if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo)
822 // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
823 if(wep.reloading_ammo)
825 self.clip_load -= ammo_use;
826 self.(weapon_load[self.weapon]) = self.clip_load;
829 self.(wep.ammo_field) -= ammo_use;
832 // weapon reloading code
834 .float reload_ammo_amount, reload_ammo_min, reload_time;
835 .float reload_complain;
836 .string reload_sound;
838 void W_ReloadedAndReady()
840 // finish the reloading process, and do the ammo transfer
842 self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
844 // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load
845 if(!self.reload_ammo_min || self.items & IT_UNLIMITED_WEAPON_AMMO || self.ammo_field == ammo_none)
846 self.clip_load = self.reload_ammo_amount;
849 while(self.clip_load < self.reload_ammo_amount && self.(self.ammo_field)) // make sure we don't add more ammo than we have
852 self.(self.ammo_field) -= 1;
855 self.(weapon_load[self.weapon]) = self.clip_load;
857 // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
858 // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
859 // so your weapon is disabled for a few seconds without reason
861 //ATTACK_FINISHED(self) -= self.reload_time - 1;
866 void W_Reload(float sent_ammo_min, string sent_sound)
868 // set global values to work with
870 e = get_weaponinfo(self.weapon);
872 self.reload_ammo_min = sent_ammo_min;
873 self.reload_ammo_amount = e.reloading_ammo;;
874 self.reload_time = e.reloading_time;
875 self.reload_sound = sent_sound;
877 // don't reload weapons that don't have the RELOADABLE flag
878 if (!(e.spawnflags & WEP_FLAG_RELOADABLE))
880 dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
884 // return if reloading is disabled for this weapon
885 if(!self.reload_ammo_amount)
888 // our weapon is fully loaded, no need to reload
889 if (self.clip_load >= self.reload_ammo_amount)
892 // no ammo, so nothing to load
893 if(self.ammo_field != ammo_none)
894 if(!self.(self.ammo_field) && self.reload_ammo_min)
895 if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
897 if(IS_REAL_CLIENT(self) && self.reload_complain < time)
899 play2(self, "weapons/unavailable.wav");
900 sprint(self, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(self.weapon), "\n"));
901 self.reload_complain = time + 1;
903 // switch away if the amount of ammo is not enough to keep using this weapon
904 if (!(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2)))
906 self.clip_load = -1; // reload later
907 W_SwitchToOtherWeapon(self);
912 if (self.weaponentity)
914 if (self.weaponentity.wframe == WFRAME_RELOAD)
917 // allow switching away while reloading, but this will cause a new reload!
918 self.weaponentity.state = WS_READY;
921 // now begin the reloading process
923 sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM);
925 // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
926 // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
927 // so your weapon is disabled for a few seconds without reason
929 //ATTACK_FINISHED(self) = max(time, ATTACK_FINISHED(self)) + self.reload_time + 1;
931 weapon_thinkf(WFRAME_RELOAD, self.reload_time, W_ReloadedAndReady);
933 if(self.clip_load < 0)
935 self.old_clip_load = self.clip_load;
936 self.clip_load = self.(weapon_load[self.weapon]) = -1;