+#include "weaponsystem.qh"
+#include "../_all.qh"
+
+#include "selection.qh"
+
+#include "../command/common.qh"
+#include "../mutators/mutators_include.qh"
+#include "../round_handler.qh"
+#include "../t_items.qh"
+#include "../../common/animdecide.qh"
+#include "../../common/constants.qh"
+#include "../../common/monsters/all.qh"
+#include "../../common/notifications.qh"
+#include "../../common/util.qh"
+#include "../../common/weapons/all.qh"
+#include "../../csqcmodellib/sv_model.qh"
+
/*
===========================================================================
float W_WeaponRateFactor()
{
- float t;
- t = 1.0 / g_weaponratefactor;
+ float t = 1.0 / g_weaponratefactor;
+
+ MUTATOR_CALLHOOK(WeaponRateFactor, t);
+ t = weapon_rate;
+
+ return t;
+}
+
+float W_WeaponSpeedFactor()
+{
+ float t = 1.0 * g_weaponspeedfactor;
+
+ MUTATOR_CALLHOOK(WeaponSpeedFactor, t);
+ t = ret_float;
return t;
}
-// VorteX: static frame globals
-const float WFRAME_DONTCHANGE = -1;
-const float WFRAME_FIRE1 = 0;
-const float WFRAME_FIRE2 = 1;
-const float WFRAME_IDLE = 2;
-const float WFRAME_RELOAD = 3;
-.float wframe;
void(float fr, float t, void() func) weapon_thinkf;
float CL_Weaponentity_CustomizeEntityForClient()
-{
+{SELFPARAM();
self.viewmodelforclient = self.owner;
if(IS_SPEC(other))
if(other.enemy == self.owner)
self.viewmodelforclient = other;
- return TRUE;
+ return true;
}
/*
// call again with ""
// remove the ent
void CL_WeaponEntity_SetModel(string name)
-{
+{SELFPARAM();
float v_shot_idx;
if (name != "")
{
// if there is a child entity, hide it until we're sure we use it
if (self.weaponentity)
self.weaponentity.model = "";
- setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below
+ _setmodel(self, W_Model(strcat("v_", name, ".md3")));
v_shot_idx = gettagindex(self, "shot"); // used later
if(!v_shot_idx)
v_shot_idx = gettagindex(self, "tag_shot");
- setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below
+ _setmodel(self, W_Model(strcat("h_", name, ".iqm")));
// preset some defaults that work great for renamed zym files (which don't need an animinfo)
self.anim_fire1 = animfixfps(self, '0 1 0.01', '0 0 0');
self.anim_fire2 = animfixfps(self, '1 1 0.01', '0 0 0');
{
if (!self.weaponentity)
self.weaponentity = spawn();
- setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
+ _setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3")));
setattachment(self.weaponentity, self, "weapon");
}
else if(gettagindex(self, "tag_weapon"))
{
if (!self.weaponentity)
self.weaponentity = spawn();
- setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
+ _setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3")));
setattachment(self.weaponentity, self, "tag_weapon");
}
else
self.movedir = gettaginfo(self, idx);
else
{
- print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
+ LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
self.movedir = '0 0 0';
}
}
self.spawnorigin = gettaginfo(self, idx);
else
{
- print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
+ LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
self.spawnorigin = self.movedir;
}
}
}
else
{
- 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");
+ LOG_INFO("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");
self.oldorigin = '0 0 0'; // there is no way to recover from this
}
}
self.view_ofs = '0 0 0';
- if(self.movedir_x >= 0)
+ if(self.movedir.x >= 0)
{
vector v0;
v0 = self.movedir;
- self.movedir = shotorg_adjust(v0, FALSE, FALSE);
- self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0;
+ self.movedir = shotorg_adjust(v0, false, false);
+ self.view_ofs = shotorg_adjust(v0, false, true) - v0;
}
self.owner.stat_shotorg = compressShotOrigin(self.movedir);
self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly
setorigin(self, self.view_ofs);
// reset animstate now
self.wframe = WFRAME_IDLE;
- setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
+ setanim(self, self.anim_idle, true, false, true);
}
vector CL_Weapon_GetShotOrg(float wpn)
-{
- entity wi, oldself;
- vector ret;
- wi = get_weaponinfo(wpn);
- oldself = self;
- self = spawn();
+{SELFPARAM();
+ entity wi = get_weaponinfo(wpn);
+ setself(spawn());
CL_WeaponEntity_SetModel(wi.mdl);
- ret = self.movedir;
+ vector ret = self.movedir;
CL_WeaponEntity_SetModel("");
remove(self);
- self = oldself;
+ setself(this);
return ret;
}
void CL_Weaponentity_Think()
-{
- float tb;
+{SELFPARAM();
+ int tb;
self.nextthink = time;
if (intermission_running)
- self.frame = self.anim_idle_x;
+ self.frame = self.anim_idle.x;
if (self.owner.weaponentity != self)
{
if (self.weaponentity)
}
void CL_ExteriorWeaponentity_Think()
-{
+{SELFPARAM();
float tag_found;
self.nextthink = time;
if (self.owner.exteriorweaponentity != self)
self.dmg = self.owner.modelindex;
self.deadflag = self.owner.deadflag;
if (self.owner.weaponname != "")
- setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below
+ _setmodel(self, W_Model(strcat("v_", self.owner.weaponname, ".md3")));
else
self.model = "";
self.glowmod = self.owner.weaponentity_glowmod;
self.colormap = self.owner.colormap;
- CSQCMODEL_AUTOUPDATE();
+ CSQCMODEL_AUTOUPDATE(self);
}
// spawning weaponentity for client
-void CL_SpawnWeaponentity()
+void CL_SpawnWeaponentity(entity e)
{
- self.weaponentity = spawn();
- self.weaponentity.classname = "weaponentity";
- self.weaponentity.solid = SOLID_NOT;
- self.weaponentity.owner = self;
- setmodel(self.weaponentity, ""); // precision set when changed
- setorigin(self.weaponentity, '0 0 0');
- self.weaponentity.angles = '0 0 0';
- self.weaponentity.viewmodelforclient = self;
- self.weaponentity.flags = 0;
- self.weaponentity.think = CL_Weaponentity_Think;
- self.weaponentity.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
- self.weaponentity.nextthink = time;
-
- self.exteriorweaponentity = spawn();
- self.exteriorweaponentity.classname = "exteriorweaponentity";
- self.exteriorweaponentity.solid = SOLID_NOT;
- self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity;
- self.exteriorweaponentity.owner = self;
- setorigin(self.exteriorweaponentity, '0 0 0');
- self.exteriorweaponentity.angles = '0 0 0';
- self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think;
- self.exteriorweaponentity.nextthink = time;
-
- {
- entity oldself = self;
- self = self.exteriorweaponentity;
- CSQCMODEL_AUTOINIT();
- self = oldself;
- }
+ entity view = e.weaponentity = spawn();
+ view.classname = "weaponentity";
+ view.solid = SOLID_NOT;
+ view.owner = e;
+ setmodel(view, MDL_Null); // precision set when changed
+ setorigin(view, '0 0 0');
+ view.angles = '0 0 0';
+ view.viewmodelforclient = e;
+ view.flags = 0;
+ view.think = CL_Weaponentity_Think;
+ view.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
+ view.nextthink = time;
+
+ entity exterior = e.exteriorweaponentity = spawn();
+ exterior.classname = "exteriorweaponentity";
+ exterior.solid = SOLID_NOT;
+ exterior.exteriorweaponentity = exterior;
+ exterior.owner = e;
+ setorigin(exterior, '0 0 0');
+ exterior.angles = '0 0 0';
+ exterior.think = CL_ExteriorWeaponentity_Think;
+ exterior.nextthink = time;
+
+ CSQCMODEL_AUTOINIT(exterior);
}
// Weapon subs
void w_clear()
-{
+{SELFPARAM();
if (self.weapon != -1)
{
self.weapon = 0;
}
void w_ready()
-{
+{SELFPARAM();
if (self.weaponentity)
self.weaponentity.state = WS_READY;
weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
.float prevdryfire;
.float prevwarntime;
float weapon_prepareattack_checkammo(float secondary)
-{
+{SELFPARAM();
if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1 + secondary))
{
// always keep the Mine Layer if we placed mines, so that we can detonate them
entity mine;
- if(self.weapon == WEP_MINE_LAYER)
+ if(self.weapon == WEP_MINE_LAYER.m_id)
for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
- return FALSE;
+ return false;
+
+ if(self.weapon == WEP_SHOTGUN.m_id)
+ if(!secondary && WEP_CVAR(shotgun, secondary) == 1)
+ return false; // no clicking, just allow
if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
{
- sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM);
+ sound (self, CH_WEAPON_A, SND_DRYFIRE, VOL_BASE, ATTEN_NORM);
self.prevdryfire = time;
}
W_SwitchToOtherWeapon(self);
}
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
.float race_penalty;
float weapon_prepareattack_check(float secondary, float attacktime)
-{
+{SELFPARAM();
if(!weapon_prepareattack_checkammo(secondary))
- return FALSE;
+ return false;
//if sv_ready_restart_after_countdown is set, don't allow the player to shoot
//if all players readied up and the countdown is running
if(time < game_starttime || time < self.race_penalty) {
- return FALSE;
+ return false;
}
if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused
- return FALSE;
+ return false;
// do not even think about shooting if switching
if(self.switchweapon != self.weapon)
- return FALSE;
+ return false;
if(attacktime >= 0)
{
// don't fire if previous attack is not finished
if (ATTACK_FINISHED(self) > time + self.weapon_frametime * 0.5)
- return FALSE;
+ return false;
// don't fire while changing weapon
if (self.weaponentity.state != WS_READY)
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
float weapon_prepareattack_do(float secondary, float attacktime)
-{
+{SELFPARAM();
self.weaponentity.state = WS_INUSE;
self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire
}
self.bulletcounter += 1;
//dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n");
- return TRUE;
+ return true;
}
float weapon_prepareattack(float secondary, float attacktime)
{
if(weapon_prepareattack_check(secondary, attacktime))
{
weapon_prepareattack_do(secondary, attacktime);
- return TRUE;
+ return true;
}
else
- return FALSE;
+ return false;
}
void weapon_thinkf(float fr, float t, void() func)
-{
+{SELFPARAM();
vector a;
vector of, or, ou;
float restartanim;
if(fr == WFRAME_DONTCHANGE)
{
fr = self.weaponentity.wframe;
- restartanim = FALSE;
+ restartanim = false;
}
else if (fr == WFRAME_IDLE)
- restartanim = FALSE;
+ restartanim = false;
else
- restartanim = TRUE;
+ restartanim = true;
of = v_forward;
or = v_right;
a = self.weaponentity.anim_fire2;
else // if (fr == WFRAME_RELOAD)
a = self.weaponentity.anim_reload;
- a_z *= g_weaponratefactor;
- setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim);
+ a.z *= g_weaponratefactor;
+ setanim(self.weaponentity, a, restartanim == false, restartanim, restartanim);
}
v_forward = of;
if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
{
- if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2)
+ if((self.weapon == WEP_SHOCKWAVE.m_id || self.weapon == WEP_SHOTGUN.m_id) && fr == WFRAME_FIRE2)
animdecide_setaction(self, ANIMACTION_MELEE, restartanim);
else
animdecide_setaction(self, ANIMACTION_SHOOT, restartanim);
}
}
-float forbidWeaponUse()
+float forbidWeaponUse(entity player)
{
if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
return 1;
if(round_handler_IsActive() && !round_handler_IsRoundStarted())
return 1;
- if(self.player_blocked)
+ if(player.player_blocked)
return 1;
- if(self.freezetag_frozen)
+ if(player.frozen)
+ return 1;
+ if(player.weapon_blocked)
return 1;
return 0;
}
void W_WeaponFrame()
-{
+{SELFPARAM();
vector fo, ri, up;
if (frametime)
if (!self.weaponentity || self.health < 1)
return; // Dead player can't use weapons and injure impulse commands
- if(forbidWeaponUse())
+ if(forbidWeaponUse(self))
if(self.weaponentity.state != WS_CLEAR)
{
w_ready();
self.switchingweapon = self.switchweapon;
entity newwep = get_weaponinfo(self.switchweapon);
- //self.items &= ~IT_AMMO;
- //self.items = self.items | (newwep.items & IT_AMMO);
-
// the two weapon entities will notice this has changed and update their models
self.weapon = self.switchweapon;
self.weaponname = newwep.mdl;
- self.bulletcounter = 0; // WEAPONTODO
- //self.ammo_field = newwep.ammo_field;
+ self.bulletcounter = 0;
+ self.ammo_field = newwep.ammo_field;
WEP_ACTION(self.switchweapon, WR_SETUP);
self.weaponentity.state = WS_RAISE;
else
self.clip_load = self.clip_size = 0;
- // VorteX: add player model weapon select frame here
- // setcustomframe(PlayerWeaponRaise);
weapon_thinkf(WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
- //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))));
}
else if (self.weaponentity.state == WS_DROP)
{
{
// start switching!
self.switchingweapon = self.switchweapon;
-
entity oldwep = get_weaponinfo(self.weapon);
-
-#ifndef INDEPENDENT_ATTACK_FINISHED
+
+ // set up weapon switch think in the future, and start drop anim
+ #ifndef INDEPENDENT_ATTACK_FINISHED
if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
{
-#endif
- sound (self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
- self.weaponentity.state = WS_DROP;
- // set up weapon switch think in the future, and start drop anim
- weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
- //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))));
-#ifndef INDEPENDENT_ATTACK_FINISHED
+ #endif
+ sound(self, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM);
+ self.weaponentity.state = WS_DROP;
+ weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
+ #ifndef INDEPENDENT_ATTACK_FINISHED
}
-#endif
+ #endif
}
}
}
void W_AttachToShotorg(entity flash, vector offset)
-{
+{SELFPARAM();
entity xflash;
flash.owner = self;
flash.angles_z = random() * 360;
flash.viewmodelforclient = self;
- if(self.weaponentity.oldorigin_x > 0)
+ if(self.weaponentity.oldorigin.x > 0)
{
setattachment(xflash, self.exteriorweaponentity, "");
setorigin(xflash, self.weaponentity.oldorigin + offset);
}
void W_DecreaseAmmo(float ammo_use)
-{
+{SELFPARAM();
entity wep = get_weaponinfo(self.weapon);
+ if(cvar("g_overkill"))
+ if(self.ok_use_ammocharge)
+ {
+ ok_DecreaseCharge(self, self.weapon);
+ return; // TODO
+ }
+
if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo)
return;
self.clip_load -= ammo_use;
self.(weapon_load[self.weapon]) = self.clip_load;
}
- else
+ else if(wep.ammo_field != ammo_none)
+ {
self.(wep.ammo_field) -= ammo_use;
+ if(self.(wep.ammo_field) < 0)
+ {
+ backtrace(sprintf(
+ "W_DecreaseAmmo(%.2f): '%s' subtracted too much %s from '%s', resulting with '%.2f' left... "
+ "Please notify Samual immediately with a copy of this backtrace!\n",
+ ammo_use,
+ wep.netname,
+ GetAmmoPicture(wep.ammo_field),
+ self.netname,
+ self.(wep.ammo_field)
+ ));
+ }
+ }
}
// weapon reloading code
.string reload_sound;
void W_ReloadedAndReady()
-{
+{SELFPARAM();
// finish the reloading process, and do the ammo transfer
self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
self.clip_load = self.reload_ammo_amount;
else
{
- while(self.clip_load < self.reload_ammo_amount && self.(self.ammo_field)) // make sure we don't add more ammo than we have
- {
- self.clip_load += 1;
- self.(self.ammo_field) -= 1;
- }
+ // make sure we don't add more ammo than we have
+ float load = min(self.reload_ammo_amount - self.clip_load, self.(self.ammo_field));
+ self.clip_load += load;
+ self.(self.ammo_field) -= load;
}
self.(weapon_load[self.weapon]) = self.clip_load;
}
void W_Reload(float sent_ammo_min, string sent_sound)
-{
+{SELFPARAM();
// set global values to work with
entity e;
e = get_weaponinfo(self.weapon);
+ if(cvar("g_overkill"))
+ if(self.ok_use_ammocharge)
+ return; // TODO
+
self.reload_ammo_min = sent_ammo_min;
- self.reload_ammo_amount = e.reloading_ammo;;
+ self.reload_ammo_amount = e.reloading_ammo;
self.reload_time = e.reloading_time;
self.reload_sound = sent_sound;
// don't reload weapons that don't have the RELOADABLE flag
if (!(e.spawnflags & WEP_FLAG_RELOADABLE))
{
- dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
+ LOG_TRACE("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
return;
}
{
if(IS_REAL_CLIENT(self) && self.reload_complain < time)
{
- play2(self, "weapons/unavailable.wav");
+ play2(self, SND(UNAVAILABLE));
sprint(self, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(self.weapon), "\n"));
self.reload_complain = time + 1;
}
// now begin the reloading process
- sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM);
+ _sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM);
// do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
// then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
self.old_clip_load = self.clip_load;
self.clip_load = self.(weapon_load[self.weapon]) = -1;
}
+
+void W_DropEvent(float event, entity player, float weapon_type, entity weapon_item)
+{SELFPARAM();
+ setself(player);
+ weapon_dropevent_item = weapon_item;
+ WEP_ACTION(weapon_type, event);
+ setself(this);
+}