X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fserver%2Fcl_weaponsystem.qc;h=271dff6291e551d75075d3eefc84db9d3bf32efc;hp=e17c346d283373e2c96de40c34ab709685806872;hb=fd5886b4935bc9bcec2c48615209621d3b2fc8dc;hpb=333e64c840c6b491fe47a0b9b72b4188040ab3fa diff --git a/qcsrc/server/cl_weaponsystem.qc b/qcsrc/server/cl_weaponsystem.qc index e17c346d28..271dff6291 100644 --- a/qcsrc/server/cl_weaponsystem.qc +++ b/qcsrc/server/cl_weaponsystem.qc @@ -125,14 +125,16 @@ void W_HitPlotAnalysis(entity player, vector screenforward, vector screenright, vector w_shotorg; vector w_shotdir; +vector w_shotend; // this function calculates w_shotorg and w_shotdir based on the weapon model // offset, trueaim and antilag, and won't put w_shotorg inside a wall. // make sure you call makevectors first (FIXME?) -void W_SetupShot_Dir_ProjectileSize(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float maxdamage) +.float prevstrengthsound; +.float prevstrengthsoundattempt; +void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float maxdamage, float range) { float nudge = 1; // added to traceline target and subtracted from result - local vector trueaimpoint; local float oldsolid; vector vecs, dv; oldsolid = ent.dphitcontentsmask; @@ -141,45 +143,35 @@ void W_SetupShot_Dir_ProjectileSize(entity ent, vector s_forward, vector mi, vec else ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; if(antilag) - WarpZone_traceline_antilag(world, ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); + WarpZone_traceline_antilag(world, ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); // passing world, because we do NOT want it to touch dphitcontentsmask else - WarpZone_TraceLine(ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * MAX_SHOT_DISTANCE, MOVE_NOMONSTERS, ent); + WarpZone_TraceLine(ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NOMONSTERS, ent); ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; vector vf, vr, vu; vf = v_forward; vr = v_right; vu = v_up; - trueaimpoint = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support + w_shotend = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support v_forward = vf; v_right = vr; v_up = vu; // track max damage - if not(inWarmupStage) { - entity w; - w = get_weaponinfo(ent.weapon); - if(w.spawnflags & WEP_TYPE_SPLASH) { // splash damage - ent.stats_fired[ent.weapon - 1] += maxdamage; - ent.stat_fired = ent.weapon + 64 * floor(ent.stats_fired[ent.weapon - 1]); - } - } + if(accuracy_canbegooddamage(ent)) + accuracy_add(ent, ent.weapon, maxdamage, 0); W_HitPlotAnalysis(ent, v_forward, v_right, v_up); if(ent.weaponentity.movedir_x > 0) - { vecs = ent.weaponentity.movedir; - vecs_y = -vecs_y; - } else vecs = '0 0 0'; - if(debug_shotorg != '0 0 0') vecs = debug_shotorg; - dv = v_right * vecs_y + v_up * vecs_z; + dv = v_right * -vecs_y + v_up * vecs_z; w_shotorg = ent.origin + ent.view_ofs + dv; // now move the shotorg forward as much as requested if possible @@ -194,17 +186,17 @@ void W_SetupShot_Dir_ProjectileSize(entity ent, vector s_forward, vector mi, vec tracebox(w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent); w_shotorg = trace_endpos - v_forward * nudge; // calculate the shotdir from the chosen shotorg - w_shotdir = normalize(trueaimpoint - w_shotorg); + w_shotdir = normalize(w_shotend - w_shotorg); if (antilag) if (!ent.cvar_cl_noantilag) { if (cvar("g_antilag") == 1) // switch to "ghost" if not hitting original { - traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, ent); + traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent); if (!trace_ent.takedamage) { - traceline_antilag_force (ent, w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); + traceline_antilag_force (ent, w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); if (trace_ent.takedamage && trace_ent.classname == "player") { entity e; @@ -225,7 +217,7 @@ void W_SetupShot_Dir_ProjectileSize(entity ent, vector s_forward, vector mi, vec { // verify that the shot would miss without antilag // (avoids an issue where guns would always shoot at their origin) - traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, ent); + traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent); if (!trace_ent.takedamage) { // verify that the shot would hit if altered @@ -245,79 +237,30 @@ void W_SetupShot_Dir_ProjectileSize(entity ent, vector s_forward, vector mi, vec ent.punchangle_x = recoil * -1; if (snd != "") - { sound (ent, CHAN_WEAPON, snd, VOL_BASE, ATTN_NORM); + + if(ent.items & IT_STRENGTH) + if(!g_minstagib) + if( + (time > ent.prevstrengthsound + cvar("sv_strengthsound_antispam_time")) + || + (time > ent.prevstrengthsoundattempt + cvar("sv_strengthsound_antispam_refire_threshold")) + ) // prevent insane sound spam + { + sound(ent, CHAN_AUTO, "weapons/strength_fire.wav", VOL_BASE, ATTN_NORM); + ent.prevstrengthsound = time; } + ent.prevstrengthsoundattempt = time; - if (ent.items & IT_STRENGTH) - if (!g_minstagib) - sound (ent, CHAN_AUTO, "weapons/strength_fire.wav", VOL_BASE, ATTN_NORM); + // nudge w_shotend so a trace to w_shotend hits + w_shotend = w_shotend + normalize(w_shotend - w_shotorg) * nudge; }; +#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, maxdamage, MAX_SHOT_DISTANCE) #define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, maxdamage) #define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, maxdamage) #define W_SetupShot(ent,antilag,recoil,snd,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, maxdamage) - -void LaserTarget_Think() -{ - entity e; - vector offset; - float uselaser; - uselaser = 0; - - // list of weapons that will use the laser, and the options that enable it - if(self.owner.laser_on && self.owner.weapon == WEP_ROCKET_LAUNCHER && g_laserguided_missile) - uselaser = 1; - // example - //if(self.owner.weapon == WEP_ELECTRO && cvar("g_laserguided_electro")) - // uselaser = 1; - - - - // if a laser-enabled weapon isn't selected, delete any existing laser and quit - if(!uselaser) - { - // rocket launcher isn't selected, so no laser target. - if(self.lasertarget != world) - { - remove(self.lasertarget); - self.lasertarget = world; - } - return; - } - - if(!self.lasertarget) - { - // we don't have a lasertarget entity, so spawn one - //bprint("create laser target\n"); - e = self.lasertarget = spawn(); - e.owner = self.owner; // Its owner is my owner - e.classname = "laser_target"; - e.movetype = MOVETYPE_NOCLIP; // don't touch things - setmodel(e, "models/laser_dot.mdl"); // what it looks like, precision set below - e.scale = 1.25; // make it larger - e.alpha = 0.25; // transparency - e.colormod = '255 0 0' * (1/255) * 8; // change colors - e.effects = EF_FULLBRIGHT | EF_LOWPRECISION; - // make it dynamically glow - // you should avoid over-using this, as it can slow down the player's computer. - e.glow_color = 251; // red color - e.glow_size = 12; - } - else - e = self.lasertarget; - - // move the laser dot to where the player is looking - - makevectors(self.owner.v_angle); // set v_forward etc to the direction the player is looking - offset = '0 0 26' + v_right*3; - traceline(self.owner.origin + offset, self.owner.origin + offset + v_forward * MAX_SHOT_DISTANCE, FALSE, self); // trace forward until you hit something, like a player or wall - setorigin(e, trace_endpos + v_forward*8); // move me to where the traceline ended - if(trace_plane_normal != '0 0 0') - e.angles = vectoangles(trace_plane_normal); - else - e.angles = vectoangles(v_forward); -} +#define W_SetupShot_Range(ent,antilag,recoil,snd,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, maxdamage, range) float CL_Weaponentity_CustomizeEntityForClient() { @@ -391,228 +334,258 @@ vector weapon_adjust = '10 0 -15'; * g_tuba.md3 - pickup model */ -void CL_Weaponentity_Think() +// writes: +// self.origin, self.angles +// self.weaponentity +// self.movedir, self.view_ofs +// attachment stuff +// anim stuff +// to free: +// call again with "" +// remove the ent +void CL_WeaponEntity_SetModel(string name) { - float tb, v_shot_idx; - self.nextthink = time; - if (intermission_running) - self.frame = self.anim_idle_x; - if (self.owner.weaponentity != self) + string animfilename; + float animfile; + float v_shot_idx; + if (name != "") { - if (self.weaponentity) - remove(self.weaponentity); - remove(self); - return; - } - if (self.owner.deadflag != DEAD_NO) - { - self.model = ""; + // if there is a child entity, hide it until we're sure we use it if (self.weaponentity) self.weaponentity.model = ""; - return; - } - if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) - { - self.cnt = self.owner.weapon; - self.dmg = self.owner.modelindex; - self.deadflag = self.owner.deadflag; + setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below + v_shot_idx = gettagindex(self, "shot"); // used later + if(!v_shot_idx) + v_shot_idx = gettagindex(self, "tag_shot"); - string animfilename; - float animfile; - if (self.owner.weaponname != "") + if(qcweaponanimation) { - // 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_", self.owner.weaponname, ".md3")); // precision set below - v_shot_idx = gettagindex(self, "shot"); // used later - if(!v_shot_idx) - v_shot_idx = gettagindex(self, "tag_shot"); + self.angles = '0 0 0'; + makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0' + self.angles_z * '0 0 1'); + self.movedir = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust; + self.movedir_x += 32; + self.spawnorigin = self.movedir; + // oldorigin - not calculated here + } + else + { + setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below + animfilename = strcat("models/weapons/h_", name, ".iqm.animinfo"); + animfile = fopen(animfilename, FILE_READ); + // preset some defaults that work great for renamed zym files (which don't need an animinfo) + self.anim_fire1 = '0 1 0.01'; + self.anim_fire2 = '1 1 0.01'; + self.anim_idle = '2 1 0.01'; + self.anim_reload = '3 1 0.01'; + if (animfile >= 0) + { + animparseerror = FALSE; + self.anim_fire1 = animparseline(animfile); + self.anim_fire2 = animparseline(animfile); + self.anim_idle = animparseline(animfile); + self.anim_reload = animparseline(animfile); + fclose(animfile); + if (animparseerror) + print("Parse error in ", animfilename, ", some player animations are broken\n"); + } - if(qcweaponanimation) + // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model) + // if we don't, this is a "real" animated model + if(gettagindex(self, "weapon")) { - self.angles = '0 0 0'; - makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0' + self.angles_z * '0 0 1'); - self.movedir = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust; - self.movedir_x += 32; - self.spawnorigin = self.movedir; - // oldorigin - not calculated here + if (!self.weaponentity) + self.weaponentity = spawn(); + setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter + 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 + setattachment(self.weaponentity, self, "tag_weapon"); } else { - setmodel(self, strcat("models/weapons/h_", self.owner.weaponname, ".iqm")); // precision set below - animfilename = strcat("models/weapons/h_", self.owner.weaponname, ".iqm.animinfo"); - animfile = fopen(animfilename, FILE_READ); - // preset some defaults that work great for renamed zym files (which don't need an animinfo) - self.anim_fire1 = '0 1 0.01'; - self.anim_fire2 = '1 1 0.01'; - self.anim_idle = '2 1 0.01'; - self.anim_reload = '3 1 0.01'; - if (animfile >= 0) - { - animparseerror = FALSE; - self.anim_fire1 = animparseline(animfile); - self.anim_fire2 = animparseline(animfile); - self.anim_idle = animparseline(animfile); - self.anim_reload = animparseline(animfile); - fclose(animfile); - if (animparseerror) - print("Parse error in ", animfilename, ", some player animations are broken\n"); - } - - // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model) - // if we don't, this is a "real" animated model - if(gettagindex(self, "weapon")) - { - if (!self.weaponentity) - self.weaponentity = spawn(); - setmodel(self.weaponentity, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision does not matter - setattachment(self.weaponentity, self, "weapon"); - } - else if(gettagindex(self, "tag_weapon")) - { - if (!self.weaponentity) - self.weaponentity = spawn(); - setmodel(self.weaponentity, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision does not matter - setattachment(self.weaponentity, self, "tag_weapon"); - } - else - { - if(self.weaponentity) - remove(self.weaponentity); - self.weaponentity = world; - } + if(self.weaponentity) + remove(self.weaponentity); + self.weaponentity = world; + } - setorigin(self,'0 0 0'); - self.angles = '0 0 0'; - self.frame = 0; - self.viewmodelforclient = world; + setorigin(self,'0 0 0'); + self.angles = '0 0 0'; + self.frame = 0; + self.viewmodelforclient = world; - float idx; + float idx; - if(v_shot_idx) // v_ model attached to invisible h_ model + if(v_shot_idx) // v_ model attached to invisible h_ model + { + self.movedir = gettaginfo(self.weaponentity, v_shot_idx); + } + else + { + idx = gettagindex(self, "shot"); + if(!idx) + idx = gettagindex(self, "tag_shot"); + if(idx) + self.movedir = gettaginfo(self, idx); + else { - self.movedir = gettaginfo(self.weaponentity, v_shot_idx); + print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n"); + self.movedir = '0 0 0'; } + } + + if(self.weaponentity) // v_ model attached to invisible h_ model + { + idx = gettagindex(self.weaponentity, "shell"); + if(!idx) + idx = gettagindex(self.weaponentity, "tag_shell"); + if(idx) + self.spawnorigin = gettaginfo(self.weaponentity, idx); + } + else + idx = 0; + if(!idx) + { + idx = gettagindex(self, "shell"); + if(!idx) + idx = gettagindex(self, "tag_shell"); + if(idx) + self.spawnorigin = gettaginfo(self, idx); else { - idx = gettagindex(self, "shot"); - if(!idx) - idx = gettagindex(self, "tag_shot"); - if(idx) - self.movedir = gettaginfo(self, idx); - else - { - print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n"); - self.movedir = '0 0 0'; - } + print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n"); + self.spawnorigin = self.movedir; } + } - if(self.weaponentity) // v_ model attached to invisible h_ model + if(v_shot_idx) + { + self.oldorigin = '0 0 0'; // use regular attachment + } + else + { + if(self.weaponentity) { - idx = gettagindex(self.weaponentity, "shell"); + idx = gettagindex(self, "weapon"); if(!idx) - idx = gettagindex(self.weaponentity, "tag_shell"); - if(idx) - self.spawnorigin = gettaginfo(self.weaponentity, idx); + idx = gettagindex(self, "tag_weapon"); } else - idx = 0; - if(!idx) { - idx = gettagindex(self, "shell"); + idx = gettagindex(self, "handle"); if(!idx) - idx = gettagindex(self, "tag_shell"); - if(idx) - self.spawnorigin = gettaginfo(self, idx); - else - { - print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n"); - self.spawnorigin = self.movedir; - } + idx = gettagindex(self, "tag_handle"); } - - if(v_shot_idx) + if(idx) { - self.oldorigin = '0 0 0'; // use regular attachment + self.oldorigin = self.movedir - gettaginfo(self, idx); } else { - if(self.weaponentity) - { - idx = gettagindex(self, "weapon"); - if(!idx) - idx = gettagindex(self, "tag_weapon"); - } - else - { - idx = gettagindex(self, "handle"); - if(!idx) - idx = gettagindex(self, "tag_handle"); - } - if(idx) - { - self.oldorigin = self.movedir - gettaginfo(self, idx); - } - 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"); - self.oldorigin = '0 0 0'; // there is no way to recover from this - } + 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"); + self.oldorigin = '0 0 0'; // there is no way to recover from this } - - self.viewmodelforclient = self.owner; } + + self.viewmodelforclient = self.owner; } - else - { - self.model = ""; - if(self.weaponentity) - remove(self.weaponentity); - self.weaponentity = world; - self.movedir = '0 0 0'; - self.spawnorigin = '0 0 0'; - self.oldorigin = '0 0 0'; - self.anim_fire1 = '0 1 0.01'; - self.anim_fire2 = '0 1 0.01'; - self.anim_idle = '0 1 0.01'; - self.anim_reload = '0 1 0.01'; - } + } + else + { + self.model = ""; + if(self.weaponentity) + remove(self.weaponentity); + self.weaponentity = world; + self.movedir = '0 0 0'; + self.spawnorigin = '0 0 0'; + self.oldorigin = '0 0 0'; + self.anim_fire1 = '0 1 0.01'; + self.anim_fire2 = '0 1 0.01'; + self.anim_idle = '0 1 0.01'; + self.anim_reload = '0 1 0.01'; + } - self.view_ofs = '0 0 0'; + self.view_ofs = '0 0 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.owner.stat_shotorg = compressShotOrigin(self.movedir); - self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly + 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.owner.stat_shotorg = compressShotOrigin(self.movedir); + self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly - self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount + self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount - // check if an instant weapon switch occurred - if (qcweaponanimation) + // check if an instant weapon switch occurred + if (qcweaponanimation) + { + if (self.state == WS_READY) { - if (self.state == WS_READY) - { - self.angles = '0 0 0'; - makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0' + self.angles_z * '0 0 1'); - setorigin(self, QCWEAPONANIMATION_ORIGIN(self)); - } + self.angles = '0 0 0'; + makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0' + self.angles_z * '0 0 1'); + setorigin(self, QCWEAPONANIMATION_ORIGIN(self)); } - else - setorigin(self, self.view_ofs); - // reset animstate now - self.wframe = WFRAME_IDLE; - self.weapon_morph0time = 0; - self.weapon_morph1time = 0; - self.weapon_morph2time = 0; - self.weapon_morph3time = 0; - self.weapon_morph4time = 0; - setanim(self, self.anim_idle, TRUE, FALSE, TRUE); + } + else + setorigin(self, self.view_ofs); + // reset animstate now + self.wframe = WFRAME_IDLE; + self.weapon_morph0time = 0; + self.weapon_morph1time = 0; + self.weapon_morph2time = 0; + self.weapon_morph3time = 0; + self.weapon_morph4time = 0; + 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(); + CL_WeaponEntity_SetModel(wi.mdl); + ret = self.movedir; + CL_WeaponEntity_SetModel(""); + remove(self); + self = oldself; + return ret; +} + +void CL_Weaponentity_Think() +{ + float tb; + self.nextthink = time; + if (intermission_running) + self.frame = self.anim_idle_x; + if (self.owner.weaponentity != self) + { + if (self.weaponentity) + remove(self.weaponentity); + remove(self); + return; + } + if (self.owner.deadflag != DEAD_NO) + { + self.model = ""; + if (self.weaponentity) + self.weaponentity.model = ""; + return; + } + if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) + { + self.cnt = self.owner.weapon; + self.dmg = self.owner.modelindex; + self.deadflag = self.owner.deadflag; + + CL_WeaponEntity_SetModel(self.owner.weaponname); } tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT)); @@ -793,9 +766,6 @@ void CL_Weaponentity_Think() self.owner.weapon_morph4origin = QCWEAPONANIMATION_ORIGIN(self); } - - // create or update the lasertarget entity - LaserTarget_Think(); }; void CL_ExteriorWeaponentity_Think() @@ -907,6 +877,16 @@ void CL_SpawnWeaponentity() self.exteriorweaponentity.nextthink = time; }; +void Send_WeaponComplain (entity e, float wpn, string wpnname, float type) +{ + msg_entity = e; + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN); + WriteByte(MSG_ONE, wpn); + WriteString(MSG_ONE, wpnname); + WriteByte(MSG_ONE, type); +} + .float hasweapon_complain_spam; float client_hasweapon(entity cl, float wpn, float andammo, float complain) @@ -940,6 +920,13 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain) self = cl; f = weapon_action(wpn, WR_CHECKAMMO1); f = f + weapon_action(wpn, WR_CHECKAMMO2); + + // always allow selecting the Mine Layer if we placed mines, so that we can detonate them + local entity mine; + if(wpn == WEP_MINE_LAYER) + for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self) + f = 1; + self = oldself; } if (!f) @@ -949,6 +936,7 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain) { play2(cl, "weapons/unavailable.wav"); sprint(cl, strcat("You don't have any ammo for the ^2", W_Name(wpn), "\n")); + Send_WeaponComplain (cl, wpn, W_Name(wpn), 0); } return FALSE; } @@ -962,6 +950,7 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain) if(weaponsInMap & weaponbit) { sprint(cl, strcat("You do not have the ^2", W_Name(wpn), "\n") ); + Send_WeaponComplain (cl, wpn, W_Name(wpn), 1); if(cvar("g_showweaponspawns")) { @@ -989,7 +978,10 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain) } } else + { + Send_WeaponComplain (cl, wpn, W_Name(wpn), 2); sprint(cl, strcat("The ^2", W_Name(wpn), "^7 is ^1NOT AVAILABLE^7 in this map\n") ); + } play2(cl, "weapons/unavailable.wav"); } @@ -1031,29 +1023,52 @@ void weapon_setup(float windex) }; // perform weapon to attack (weaponstate and attack_finished check is here) +void W_SwitchToOtherWeapon(entity pl) +{ + // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway) + float w, ww; + w = W_WeaponBit(pl.weapon); + pl.weapons &~= w; + ww = w_getbestweapon(pl); + pl.weapons |= w; + if(ww) + W_SwitchWeapon_Force(pl, ww); +} +.float prevdryfire; +float weapon_prepareattack_checkammo(float secondary) +{ + if not(self.items & IT_UNLIMITED_WEAPON_AMMO) + if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary)) + { + // always keep the Mine Layer if we placed mines, so that we can detonate them + local entity mine; + if(self.weapon == WEP_MINE_LAYER) + for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self) + return FALSE; + + if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons + { + sound (self, CHAN_AUTO, "weapons/dryfire.wav", VOL_BASE, ATTN_NORM); + self.prevdryfire = time; + } + + W_SwitchToOtherWeapon(self); + return FALSE; + } + return TRUE; +} .float race_penalty; -float weapon_prepareattack(float secondary, float attacktime) +float weapon_prepareattack_check(float secondary, float attacktime) { + if(!weapon_prepareattack_checkammo(secondary)) + 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; } - if not(self.items & IT_UNLIMITED_WEAPON_AMMO) - if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary)) - { - // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway) - float w, ww; - w = W_WeaponBit(self.weapon); - self.weapons &~= w; - ww = w_getbestweapon(self); - self.weapons |= w; - if(ww) - W_SwitchWeapon_Force(self, ww); - return FALSE; - } - if (timeoutStatus == 2) //don't allow the player to shoot while game is paused return FALSE; @@ -1070,6 +1085,11 @@ float weapon_prepareattack(float secondary, float attacktime) if (self.weaponentity.state != WS_READY) return FALSE; } + + return TRUE; +} +float weapon_prepareattack_do(float secondary, float attacktime) +{ self.weaponentity.state = WS_INUSE; self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire @@ -1087,7 +1107,17 @@ float weapon_prepareattack(float secondary, float attacktime) self.bulletcounter += 1; //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n"); return TRUE; -}; +} +float weapon_prepareattack(float secondary, float attacktime) +{ + if(weapon_prepareattack_check(secondary, attacktime)) + { + weapon_prepareattack_do(secondary, attacktime); + return TRUE; + } + else + return FALSE; +} void weapon_thinkf(float fr, float t, void() func) { @@ -1232,7 +1262,7 @@ void weapon_boblayer1(float spd, vector org) // VorteX: haste can be added here }; -vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity) +vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute) { vector mdirection; float mspeed; @@ -1246,7 +1276,7 @@ vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity) mspeed = vlen(mvelocity); nstyle = cvar("g_projectiles_newton_style"); - if(nstyle == 0) + if(nstyle == 0 || forceAbsolute) { // absolute velocity outvelocity = mvelocity; @@ -1317,7 +1347,10 @@ void W_AttachToShotorg(entity flash, vector offset) } else { - setattachment(flash, self.weaponentity, "shot"); + if(gettagindex(self.weaponentity, "shot")) + setattachment(flash, self.weaponentity, "shot"); + else + setattachment(flash, self.weaponentity, "tag_shot"); setorigin(flash, offset); xflash = spawn(); @@ -1332,7 +1365,11 @@ void W_AttachToShotorg(entity flash, vector offset) } else { - setattachment(xflash, self.exteriorweaponentity, "shot"); + if(gettagindex(self.exteriorweaponentity, "shot")) + setattachment(xflash, self.exteriorweaponentity, "shot"); + else + setattachment(xflash, self.exteriorweaponentity, "tag_shot"); + setorigin(xflash, offset); } } } @@ -1532,7 +1569,7 @@ float mspercallsum; float mspercallsstyle; float mspercallcount; #endif -void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread) +void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute) { if(missile.owner == world) error("Unowned missile"); @@ -1557,13 +1594,13 @@ void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float print("avg: ", ftos(mspercallcount / mspercallsum), " per sec\n"); #endif - missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, pSpeed * dir); + missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, pSpeed * dir, forceAbsolute); } void W_SetupProjectileVelocity(entity missile, float pSpeed, float spread) { - W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread); + W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread, FALSE); } -#define W_SETUPPROJECTILEVELOCITY_UP(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), cvar(#s "_speed_up"), cvar(#s "_speed_z"), cvar(#s "_spread")) -#define W_SETUPPROJECTILEVELOCITY(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), 0, 0, cvar(#s "_spread")) +#define W_SETUPPROJECTILEVELOCITY_UP(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), cvar(#s "_speed_up"), cvar(#s "_speed_z"), cvar(#s "_spread"), FALSE) +#define W_SETUPPROJECTILEVELOCITY(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), 0, 0, cvar(#s "_spread"), FALSE)