/* server/weapons/weapon_core.qc weapon logic Copyright (C) 2021-2024 NZ:P Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ void() W_PutOut; float(entity who, float weapon) Zombie_HeadCanGib; void(float side) W_Reload; void() GrenadeExplode; void() W_SprintStart; #ifdef FTE void() AI_SetAllEnemiesBBOX; void() AI_RevertEnemySolidState float() push_away_zombies; #endif // FTE // // W_HideCrosshair(person) // Toggles off the crosshair for provided client. // This is considered "nasty" by use of stuffcmd, // however non-FTE has no way of better controlling // cvars for individual clients. // void(entity person) W_HideCrosshair = { if (person == world) return; stuffcmd(person, "crosshair 0\n"); } // // W_ShowCrosshair(person) // Toggles on the crosshair for provided client. // This is considered "nasty" by use of stuffcmd, // however non-FTE has no way of better controlling // cvars for individual clients. // void(entity person) W_ShowCrosshair = { if (person == world) return; // Grab the type of crosshair float crosshair_type; // Grenades have a special crosshair type that can pulse. if (person.grenade_delay) { crosshair_type = 4; } else crosshair_type = WepDef_GetWeaponCrosshairType(person.weapon); string crosshair_string = "crosshair "; crosshair_string = strzone(strcat(crosshair_string, ftos(crosshair_type))); stuffcmd(person, strcat(crosshair_string, "\n")); strunzone(crosshair_string); } void() ReturnWeaponModel = { self.weaponmodel = GetWeaponModel(self.weapon, 0); if (IsDualWeapon(self.weapon)) { self.weapon2model = GetLeftWeaponModel(self.weapon); } else if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) { self.weapon2model = "models/weapons/kar/v_karscope.mdl"; UpdateV2model(self.weapon2model, 0); } UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); UpdateV2model(self.weapon2model, GetWepSkin(self.weapon)); // Always try to reload after any action. if (self.weapons[0].weapon_magazine == 0 && self.weapons[0].weapon_reserve != 0) W_Reload(S_RIGHT); if (IsDualWeapon(self.weapon) && self.weapons[0].weapon_magazine_left == 0 && self.weapons[0].weapon_reserve != 0) W_Reload(S_LEFT); // If the person is swapping, play the sprint anim if they're sprinting after swap. Otherwise it plays idle if (self.sprinting == TRUE) W_SprintStart(); W_ShowCrosshair(self); } void() W_PlayTakeOut = { W_HideCrosshair(self); Weapon_PlayViewModelAnimation(ANIM_TAKE_OUT, ReturnWeaponModel, 0); } .float scopetime; void() W_AimIn = { if (WepDef_DoesNotADS(self.weapon)) return; if (IsDualWeapon(self.weapon) || self.reload_delay > time || self.knife_delay > time || self.sprinting || self.new_anim_stop || self.semiknife == true) { return; } float ads_frame = GetFrame(self.weapon, AIM_IN); if (ads_frame != 0 && self.fire_delay < time) { self.weaponframe_end = self.weaponframe = ads_frame; } else if (self.zoom) { return; } if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { SetUpdate(self, UT_ZOOM2, 0, 0, 0); self.zoom = 2; self.scopetime = time + 0.3; } else { self.zoom = 1; } } void() W_AimOut = { if (!self.zoom || self.new_anim_stop || self.sprinting) { return; } if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { ReturnWeaponModel(); } #ifndef FTE self.zoom = 0; #endif // FTE } void() W_SprintStop = { if (self.sprinting) { self.sprint_stop_time = time; self.sprint_duration = self.sprint_timer; } if (self.isBuying || !self.sprinting) return; Weapon_PlayViewModelAnimation(ANIM_SPRINT_STOP, ReturnWeaponModel, 0); self.zoom = 0; self.tp_anim_time = 0; PAnim_Walk6(); // Run Walk for a few frames to simulate an ease in velocity self.sprinting = 0; self.into_sprint = 0; self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = 0; } void W_SprintStart () { if (self.speed_penalty_time > time || self.zoom != 0) return; self.sprint_start_time = time; if (self.sprint_rest_time > sprint_max_time) self.sprint_duration = 0.0; else self.sprint_duration -= self.sprint_rest_time; if (!self.sprintflag) { return; } if (self.fire_delay > time || self.fire_delay2 > time || self.new_anim_stop || self.new_anim2_stop || self.isBuying || self.downed || !(self.flags & FL_ONGROUND) || self.sprint_delay > time) { return; } Weapon_PlayViewModelAnimation(ANIM_SPRINT_START, ContinueRun, 0); self.zoom = 3; self.sprint_delay = time + 1; self.sprinting = true; self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = 0; } void() W_PutOutHack = { Weapon_SwapWeapons(true); } void() W_PutOut = { // We don't hold more than one weapon if (self.weapons[1].weapon_id == 0 || self.downed) return; float duration = getWeaponDelay(self.weapon, PUTOUT); W_AimOut(); W_HideCrosshair(self); if (self.stance == 2) PAnim_Swap(); if (self.weapon_count != 1 && !self.new_anim_stop) Weapon_PlayViewModelAnimation(ANIM_PUT_AWAY, W_PutOutHack, duration); } void() W_TakeOut = { self.isBuying = false; float duration = getWeaponDelay(self.weapon, TAKEOUT); W_AimOut(); W_HideCrosshair(self); Weapon_PlayViewModelAnimation(ANIM_TAKE_OUT, ReturnWeaponModel, duration); } //RELOAD void(float side) W_Give_Ammo = { float ammo_shot, max_mag, loadammo; max_mag = getWeaponMag(self.weapon); if (side == S_LEFT) { ammo_shot = max_mag - self.weapons[0].weapon_magazine_left; } else { ammo_shot = max_mag - self.weapons[0].weapon_magazine; } if (ammo_shot < self.weapons[0].weapon_reserve) { self.weapons[0].weapon_reserve = self.weapons[0].weapon_reserve - ammo_shot; loadammo = max_mag; } else { loadammo = self.weapons[0].weapon_magazine + self.weapons[0].weapon_reserve; self.weapons[0].weapon_reserve = 0; } if (side == S_LEFT) { self.weapons[0].weapon_magazine_left = loadammo; } else { self.weapons[0].weapon_magazine = loadammo; } }; void () W_LoadAmmo; void() ContinueReload = //Special reloads { if (self.new_anim_stop) return; if (self.weapon == W_GUT && self.weapons[0].weapon_magazine == 10) return; W_HideCrosshair(self); float delay = 0; string modelname = GetWeaponModel(self.weapon, 0); float startframe = 0; float endframe = 0; void(optional float t) endanimfunc = SUB_Null; if (self.weapons[0].weapon_magazine >= getWeaponMag(self.weapon) || !self.weapons[0].weapon_reserve || self.reloadinterupted) { if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) { delay = 1; startframe = 24; endframe = 28; } else if (self.weapon == W_TRENCH || self.weapon == W_GUT) { delay = 0.5; startframe = 24; endframe = 26; endanimfunc = W_LoadAmmo; } self.reloadinterupted = FALSE; } else if (self.weapons[0].weapon_magazine < getWeaponMag(self.weapon)) { if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) { self.weapons[0].weapon_magazine++; self.weapons[0].weapon_reserve = self.weapons[0].weapon_reserve - 1; delay = 0.8; startframe = 19; endframe = 24; endanimfunc = ContinueReload; } else if (self.weapon == W_TRENCH || self.weapon == W_GUT) { if (self.weapon == W_GUT && self.weapons[0].weapon_reserve >= 2 && self.weapons[0].weapon_magazine < 9) { self.weapons[0].weapon_magazine = self.weapons[0].weapon_magazine + 2; self.weapons[0].weapon_reserve = self.weapons[0].weapon_reserve - 2; } else { self.weapons[0].weapon_magazine++; self.weapons[0].weapon_reserve = self.weapons[0].weapon_reserve - 1; } delay = 0.5; startframe = 18; endframe = 23; endanimfunc = ContinueReload; } } if (delay) { if (self.perks & P_SPEED) { delay *= 0.5; } self.reload_delay = time + delay; Set_W_Frame (startframe, endframe, delay, 0, RELOAD, endanimfunc, modelname, false, S_RIGHT); } } void(float side) W_AdvanceAnim = { float startframe, endframe, delay, reloadcancelframe; string modelname; startframe = GetFrame(self.weapon, RELOAD_START); endframe = GetFrame(self.weapon, RELOAD_END); delay = getWeaponDelay(self.weapon, RELOAD); reloadcancelframe = GetFrame(self.weapon, RELOAD_CANCEL); if (self.perks & P_SPEED) delay *= 0.5; modelname = GetWeaponModel(self.weapon, 0); self.reload_delay = self.reload_delay2 = delay + time; Set_W_Frame (startframe, endframe, delay, reloadcancelframe, RELOAD, W_Give_Ammo, modelname, false, side); } void(float side) W_Reload = { if (self.zoom == 2) return; if (GetFiretype(self.weapon) == FIRETYPE_FLAME) return; if (side == S_BOTH) { W_Reload(S_RIGHT); if (IsDualWeapon(self.weapon)) { W_Reload(S_LEFT); } return; } if (side == S_RIGHT && (self.reload_delay > time || self.new_anim_stop || !self.weapons[0].weapon_reserve || self.weapons[0].weapon_magazine >= getWeaponMag(self.weapon))){ return; } if (side == S_LEFT && (self.reload_delay2 > time || self.new_anim2_stop || !self.weapons[0].weapon_reserve || self.weapons[0].weapon_magazine_left >= getWeaponMag(self.weapon))) { return; } self.zoom = false; W_AimOut(); W_SprintStop(); // If it's a dual wielded weapon, we don't want to hide the crosshair // unless both weapons are being reloaded. if (!IsDualWeapon(self.weapon) || (self.reload_delay > time || self.reload_delay2 > time)) W_HideCrosshair(self); float endframe, startframe, reloadcancelframe, delay; string modelname = ""; if (side == S_RIGHT) { modelname = GetWeaponModel(self.weapon, 0); } else { if (IsDualWeapon(self.weapon)) { modelname = GetLeftWeaponModel(self.weapon); } } if (self.weapons[0].weapon_reserve) { switch(self.stance) { case 2: PAnim_Reload(); break; case 1: PAnim_CrReload(); break; case 0: if (!self.downed) PAnim_PrReload(); break; } startframe = GetFrame(self.weapon,RELOAD_START); endframe = GetFrame(self.weapon,RELOAD_END); reloadcancelframe = GetFrame(self.weapon,RELOAD_CANCEL); delay = getWeaponDelay(self.weapon,RELOAD); void(optional float t) endanimfunc = SUB_Null; if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER ){ startframe = 14; endframe = 18; reloadcancelframe = 0; delay = 0.8; endanimfunc = ContinueReload; } // Check for special reload type else if (GetFrame(self.weapon, RELOAD_EMPTY_START) != 0) { if (self.weapons[0].weapon_magazine > 0) { startframe = GetFrame(self.weapon, RELOAD_PART_START); endframe = GetFrame(self.weapon, RELOAD_PART_END); delay = getWeaponDelay(self.weapon, RELOAD_PAR); } else { startframe = GetFrame(self.weapon, RELOAD_EMPTY_START); endframe = GetFrame(self.weapon, RELOAD_EMPTY_END); delay = getWeaponDelay(self.weapon, RELOAD_EMP); } endanimfunc = W_AdvanceAnim; } else if (self.weapon == W_TRENCH || self.weapon == W_GUT) { // cypress -- world at war depicts the trench gun as needing to // pump after reload regardless of context, so i removed a reference // that would only do it if the mag was empty. tradeoff: now we're // pumping 2 shells away instead of 1 if not empty. oh well. self.NeedLoad = true; startframe = 14; endframe = 17; reloadcancelframe = 0; delay = 0.8; endanimfunc = ContinueReload; } else { endanimfunc = W_Give_Ammo; } if (self.perks & P_SPEED) delay = delay*0.5; if (side == S_RIGHT) { self.reload_delay = delay + time; } else { self.reload_delay2 = delay + time; } Set_W_Frame (startframe, endframe, delay, reloadcancelframe, RELOAD, endanimfunc, modelname, false, side); } if (self.weapon != W_TRENCH) { self.NeedLoad = false; } }; void () W_LoadAmmoDone = { self.NeedLoad = false; } void () W_LoadAmmo = { if (!self.NeedLoad) return; if (!self.weapons[0].weapon_magazine) { W_Reload(S_BOTH); return; } string modelname; float endframe, startframe, reloadcancelframe, delay; modelname = GetWeaponModel(self.weapon, 0); startframe = GetFrame(self.weapon,RELOAD_START); endframe = GetFrame(self.weapon,RELOAD_END); reloadcancelframe = GetFrame(self.weapon,RELOAD_CANCEL); delay = getWeaponDelay(self.weapon,RELOAD); void(optional float t) endanimfunc = SUB_Null; if (self.weapon == W_TRENCH || self.weapon == W_GUT) { startframe = 4; endframe = 14; reloadcancelframe = 12; delay = 0.9; endanimfunc = W_LoadAmmoDone; } else if (self.weapon == W_KAR || self.weapon == W_ARMAGEDDON || self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER || self.weapon == W_SPRING || self.weapon == W_PULVERIZER) { startframe = 4; endframe = 13; reloadcancelframe = 9; delay = 1.2; endanimfunc = W_LoadAmmoDone; } if (delay) { if (self.perks & P_DOUBLE) delay *= 0.66; Set_W_Frame (startframe, endframe, delay, reloadcancelframe, FIRE, W_LoadAmmoDone, modelname, false, S_RIGHT); self.fire_delay = delay + time; } } void () CheckReload = { if (!self.weapons[0].weapon_magazine_left && IsDualWeapon(self.weapon)) { W_Reload(S_LEFT); } if (!self.weapons[0].weapon_magazine && self.weapons[0].weapon_reserve) { W_Reload(S_RIGHT); } } /* ================ SpawnBlood ================ */ void(vector org, vector vel, float damage) SpawnBlood = { local entity f; #ifdef FTE WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_BLOOD); WriteCoord(MSG_MULTICAST, org_x); WriteCoord(MSG_MULTICAST, org_y); WriteCoord(MSG_MULTICAST, org_z); msg_entity = self; multicast('0 0 0', MULTICAST_ONE); #else particle (org, vel*0.1, 73, damage*2); #endif // FTE f = find (world, classname, "player"); f.hitcount = f.hitcount + 1; }; void(vector where) spawn_gibs = { #ifndef FTE particle(where, where*0.1, 225, 1); #else WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_LIMBGIB); WriteCoord(MSG_MULTICAST, where_x); WriteCoord(MSG_MULTICAST, where_y); WriteCoord(MSG_MULTICAST, where_z); multicast('0 0 0', MULTICAST_ALL); #endif // FTE } void Parse_Damage () = // DO NOT TOUCH { entity ent; float total_dmg; entity body_ent; float head_hit; //warn head_hit = 0; ent = findfloat (world, washit, 1); while (ent) { if (ent.classname != "radio" && ent.classname != "explosive_barrel") { if (ent.classname == "ai_zombie_head") head_hit = 1; if (ent.classname != "ai_zombie" && ent.classname != "ai_dog") //limb body_ent = ent.owner; else body_ent = ent; total_dmg = body_ent.hitamount; if (body_ent.head.washit) { total_dmg += body_ent.head.hitamount; body_ent.head.health -= body_ent.head.hitamount; head_hit = 1; // Check if we should Gib the head, before this would // be a really weird check to "ressurect" a zombie if // it was out of health. I *think* this is more accurate // now. -- cypress (5 nov 2023) if (Zombie_HeadCanGib(body_ent, self.weapon)) { makevectors(body_ent.head.owner.angles); vector where = body_ent.head.owner.origin + (body_ent.head.view_ofs_x * v_right) + (body_ent.head.view_ofs_y * v_forward) + (body_ent.head.view_ofs_z * v_up); spawn_gibs(where); body_ent.head.deadflag = false; body_ent.head.solid = SOLID_NOT; setmodel(body_ent.head, ""); body_ent.head.frame = 0; body_ent.usedent = self; body_ent.bleedingtime = time + 1; #ifndef FTE updateLimb (body_ent.head.owner, 0, world); #endif // FTE } body_ent.head.washit = 0; body_ent.head.hitamount = 0; } if (body_ent.larm.washit) { total_dmg += body_ent.larm.hitamount; body_ent.larm.health -= body_ent.larm.hitamount; if (WepDef_WeaponCanGibEnemy(self.weapon) && body_ent.larm.owner.rarm.deadflag && body_ent.larm.deadflag) { makevectors(body_ent.larm.owner.angles); where = body_ent.larm.owner.origin + (body_ent.larm.view_ofs_x * v_right) + (body_ent.larm.view_ofs_y * v_forward) + (body_ent.larm.view_ofs_z * v_up); spawn_gibs(where); body_ent.larm.deadflag = false; body_ent.larm.solid = SOLID_NOT; setmodel(body_ent.larm,""); body_ent.larm.frame = 0; #ifndef FTE updateLimb (body_ent.larm.owner, 1, world); #endif // FTE } body_ent.larm.washit = 0; body_ent.larm.hitamount = 0; } if (body_ent.rarm.washit) { total_dmg += body_ent.rarm.hitamount; body_ent.rarm.health -= body_ent.rarm.hitamount; if (WepDef_WeaponCanGibEnemy(self.weapon) && body_ent.rarm.owner.larm.deadflag && body_ent.rarm.deadflag) { makevectors(body_ent.rarm.owner.angles); where = body_ent.rarm.owner.origin + (body_ent.rarm.view_ofs_x * v_right) + (body_ent.rarm.view_ofs_y * v_forward) + (body_ent.rarm.view_ofs_z * v_up); spawn_gibs(where); body_ent.rarm.deadflag = false; body_ent.rarm.solid = SOLID_NOT; setmodel(body_ent.rarm,""); body_ent.rarm.frame = 0; #ifndef FTE updateLimb (body_ent.rarm.owner, 2, world); #endif // FTE } body_ent.rarm.washit = 0; body_ent.rarm.hitamount = 0; } if (instakill_finished) { if(body_ent.head.deadflag) { body_ent.head.deadflag = false; body_ent.head.solid = SOLID_NOT; setmodel(body_ent.head,""); body_ent.head.frame = 0; } } if (head_hit) DamageHandler(body_ent,self, total_dmg, S_HEADSHOT); else DamageHandler(body_ent,self, total_dmg, S_NORMAL); if (body_ent != world) { body_ent.washit = 0; body_ent.hitamount = 0; } } ent = findfloat (ent, washit, 1); } } void() LungeKnifeHit = { vector source,org; float f_damage; entity hit_ent; makevectors (self.v_angle); source = self.origin + self.view_ofs; if (self.weapon == W_BK || self.weapon == W_KRAUS) traceline (source, source + v_forward*80*1.2, 0, self); // naievil -- added some extra length else traceline (source, source + v_forward*74*1.2, 0, self); // naievil -- ^^ org = trace_endpos - v_forward*3; // naievil -- changed to factor of 3 from 4 if (trace_ent.takedamage) { if(trace_ent.classname == "item_radio" || trace_ent.classname == "teddy_spawn") { entity oldself; oldself = self; self = trace_ent; self.th_die(); self = oldself; return; } sound (self, CHAN_WEAPON, "sounds/weapons/knife/knife_hitbod.wav", 1, ATTN_NORM); // Calculate melee damage f_damage = WepDef_CalculateMeleeDamage(self.weapon, self.bowie); if (trace_ent.classname == "ai_zombie_head") { hit_ent = trace_ent.owner; } else if (trace_ent.classname == "ai_zombie_larm") { hit_ent = trace_ent.owner; } else if (trace_ent.classname == "ai_zombie_rarm") { hit_ent = trace_ent.owner; } else { hit_ent = trace_ent; } if (hit_ent.takedamage) { org = source + (v_forward * 20); SpawnBlood (org, org, 50); //hit = 1; } DamageHandler (hit_ent, self, f_damage, S_KNIFE); } } void(float damage, vector dir, vector org, vector plane, entity hit_ent, float side) TraceAttack = { vector vel; float f_damage = 0; vel = normalize(dir); vel = vel + 2*plane; vel = vel * 200; if (hit_ent.takedamage) { if (trace_fraction >= 1) { return; } if(hit_ent.classname == "item_radio" || hit_ent.classname == "teddy_spawn") { entity oldself; oldself = self; self = hit_ent; self.th_die(); self = oldself; return; } else if (hit_ent.classname == "explosive_barrel") { hit_ent.enemy = self; oldself = self; self = hit_ent; self.health = self.health - damage; Barrel_Hit(); self = oldself; return; } else if (hit_ent.solid == SOLID_BSP) { oldself = self; self = hit_ent; self.health -= damage; self.enemy = oldself; if (self.health < 0) self.th_die(); self = oldself; return; } SpawnBlood (org, vel, damage*10); switch(hit_ent.classname) { case "ai_zombie_head": f_damage = damage*getWeaponMultiplier(self.weapon, HEAD_X); break; case "ai_zombie_larm": case "ai_zombie_rarm": f_damage = damage*getWeaponMultiplier(self.weapon, LIMBS_X); break; case "ai_zombie": if (trace_endpos_z < hit_ent.origin_z) f_damage = damage*getWeaponMultiplier(self.weapon, LOWER_TORSO_X); else f_damage = damage*getWeaponMultiplier(self.weapon, UPPER_TORSO_X); break; default: f_damage = damage; break; } hit_ent.washit = 1; hit_ent.hitamount = hit_ent.hitamount + f_damage; SetUpdate(self, UT_HM, 0, 0, 0); #ifndef FTE msg_entity = self; WriteByte(MSG_ONE, SVC_HITMARK); #endif // FTE } else { #ifndef FTE WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_GUNSHOT); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); #endif // FTE } #ifdef FTE WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_PISTOLFIRE); WriteEntity(MSG_MULTICAST, self); WriteFloat(MSG_MULTICAST, side); WriteCoord(MSG_MULTICAST, org_x); WriteCoord(MSG_MULTICAST, org_y); WriteCoord(MSG_MULTICAST, org_z); WriteCoord(MSG_MULTICAST, plane_x); WriteCoord(MSG_MULTICAST, plane_y); WriteCoord(MSG_MULTICAST, plane_z); WriteEntity(MSG_MULTICAST, hit_ent); multicast(trace_endpos, MULTICAST_PHS); #endif // FTE }; void (float shotcount, float sprd, float Damage, float side) FireTrace = { float count, r, ru, penetration_times, do_break; vector dir, src, trace_start_org; entity hitent; count = 0; makevectors(self.v_angle); src = self.origin + self.view_ofs; CL_SendWeaponFire(self, self.weapon); while (count < shotcount) { dir = aim (self,0); r = random() * sprd*10; if (random() < 0.5) r = r*-1; ru = random() * sprd*10; if (random() < 0.5) ru = ru*-1; dir = dir*2048; dir = dir + v_right*r + v_up*ru; trace_start_org = src; trace_ent = self; do_break = 0; penetration_times = 0; hitent = world; while (!do_break && random() < getWeaponPenetration(self.weapon, penetration_times)) { #ifdef FTE AI_SetAllEnemiesBBOX(); #endif // FTE traceline (trace_start_org, trace_start_org + dir, MOVE_HITMODEL, trace_ent); #ifdef FTE AI_RevertEnemySolidState(); #endif // FTE if (trace_fraction != 1.0) TraceAttack (Damage, dir, trace_endpos, trace_plane_normal, trace_ent, side); trace_start_org = trace_endpos; penetration_times++; if (!trace_ent.takedamage) do_break = 1; if (trace_ent.classname == "ai_zombie_head" || trace_ent.classname == "ai_zombie_larm" || trace_ent.classname == "ai_zombie_rarm") hitent = trace_ent.owner; else hitent = trace_ent; } hitent = world; count++; } if (self.weapon != W_TESLA) Parse_Damage(); } void(float side) W_Fire = { if (GetFiretype(self.weapon) == FIRETYPE_FLAME && self.cooldown) return; if (self.semiswitch == true || self.dive) return; //First check that we can actualy fire if (side == S_RIGHT && (time < self.fire_delay || self.new_anim_stop || self.reload_delay > time || !self.weapons[0].weapon_magazine)) { return; } if (side == S_LEFT && (time < self.fire_delay2 || self.new_anim2_stop || self.reload_delay2 > time)) { return; } if (self.sprinting) { W_SprintStop(); return; } float startframe = 0; float endframe = 0; float firetype; float damage; float shotcount; string modelname = ""; string soundname; float spread; float delay; //Update the basic vectors makevectors(self.v_angle); //make sure magazine is loading if (!self.weapons[0].weapon_magazine && side == S_RIGHT) { W_Reload(S_RIGHT); return; } else if (!self.weapons[0].weapon_magazine_left && side == S_LEFT) { W_Reload(S_LEFT); return; } //Dont fire if the gun has to cycle if (self.NeedLoad && (self.weapon == W_TRENCH || self.weapon == W_GUT || self.weapon == W_KAR_SCOPE || self.weapon == W_KAR || self.weapon == W_ARMAGEDDON || self.weapon == W_HEADCRACKER || self.weapon == W_SPRING || self.weapon == W_PULVERIZER)) { W_LoadAmmo(); return; } if (self.downed) PAnim_LastFire(); else { switch(self.stance) { case 2: PAnim_Fire(); break; case 1: PAnim_CrouchFire(); break; case 0: break; } } //get some basic info damage = getWeaponDamage(self.weapon); if (global_trace_damage_multiplier != 0) damage *= global_trace_damage_multiplier; firetype = GetFiretype (self.weapon); if (side == S_RIGHT) { modelname = GetWeaponModel(self.weapon, 0); } else { if (IsDualWeapon(self.weapon)) { modelname = GetLeftWeaponModel(self.weapon); } } shotcount = GetWeaponShotcount(self.weapon); soundname = GetWeaponSound(self.weapon); delay = getWeaponDelay(self.weapon, FIRE); // Double Tap 2.0 if (self.perks & P_DOUBLE && self.has_doubletap_damage_buff) shotcount *= 2; spread = WepDef_WeaponSpread(self.weapon, self.stance); if (self.perks & P_DEAD) { spread *= 0.65; } if (self.zoom == 1 && W_SpreadAffectedByADS(self.weapon)) { spread *= 0.02; } else if (self.zoom == 2) { if (W_HighPrecisionWhenADS(self.weapon)) spread = 0; else spread *= 0.15; } // Check if weapon is semi-automatic and if it is, if it's ready to be fired. if (Weapon_IsSemiAutomatic(self.weapon)) { if (side == S_RIGHT) { if (self.semi) return; self.semi = true; } else if (side == S_LEFT) { if (self.semi2) return; self.semi2 = true; } } // Weapon Projectile/Trace Logic. if (Weapon_FiresTraceshot(self.weapon)) { #ifdef FTE self.dimension_hit = HITBOX_DIM_LIMBS | HITBOX_DIM_ZOMBIES; #endif // FTE FireTrace(shotcount, spread, damage, side); #ifdef FTE self.dimension_hit = HITBOX_DIM_ZOMBIES; #endif // FTE } switch(firetype) { case FIRETYPE_ROCKET: W_FireRocket(); break; case FIRETYPE_GRENADE: W_FireGrenade(side); break; case FIRETYPE_RAYBEAM: W_FireRay(); break; case FIRETYPE_TESLA: W_FireTesla(); break; case FIRETYPE_FLAME: W_FireFlame(); break; default: break; } // Play weapon animation and sound if (GetFrame(self.weapon, FIRE_HOLD) == 0) { if (self.zoom && GetFrame(self.weapon, AIM_FIRE_START) != 0) { startframe = GetFrame(self.weapon, AIM_FIRE_START); endframe = GetFrame(self.weapon, AIM_FIRE_END); } else { startframe = GetFrame(self.weapon, FIRE_START); endframe = GetFrame(self.weapon, FIRE_END); } } else { startframe = endframe = GetFrame(self.weapon, FIRE_HOLD); } // Increment the amount of shots fired while downed if (self.downed == true) self.teslacount++; if (self.perks & P_DOUBLE) { delay *= 0.66; } // Players shouldn't be allowed to move while firing and prone. if (self.stance == 0) { self.speed_penalty = 0.01; self.speed_penalty_time = time + delay; } if (self.weapon == W_GUT || self.weapon == W_KAR_SCOPE || self.weapon == W_TRENCH || self.weapon == W_KAR || self.weapon == W_ARMAGEDDON || self.weapon == W_HEADCRACKER || self.weapon == W_SPRING || self.weapon == W_PULVERIZER) { Set_W_Frame (startframe, endframe, delay, 0, FIRE, W_LoadAmmo, modelname, FALSE, side); self.NeedLoad = true; } else { Set_W_Frame (startframe, endframe, delay, 0, FIRE, CheckReload, modelname, FALSE, side); } sound (self, CHAN_WEAPON, soundname, 1, ATTN_NORM); if (side == S_RIGHT) { self.weapons[0].weapon_magazine = self.weapons[0].weapon_magazine - 1; self.fire_delay = getWeaponDelay(self.weapon, FIRE) + time; } else { self.weapons[0].weapon_magazine_left = self.weapons[0].weapon_magazine_left - 1; self.fire_delay2 = getWeaponDelay(self.weapon, FIRE) + time; } if (IsPapWeapon(self.weapon) && !WepDef_DoesNotPlayUpgradedSound(self.weapon)) { sound (self, 0, "sounds/weapons/papfire.wav", 1, ATTN_NORM); } SetUpdate(self, UT_HUD, 6, 0, 0); SetUpdate(self, UT_CROSSHAIR, 5, 0, 0); // REFORMAT THIS #ifndef FTE if (side == S_LEFT) self.Flash_Offset = WepDef_GetLeftFlashOffset(self.weapon); else self.Flash_Offset = GetWeaponFlash_Offset(self.weapon); self.effects = self.effects | EF_MUZZLEFLASH; #endif // FTE } /****************************** * W_Knife * ******************************/ void () W_Knife = { if (time < self.knife_delay || self.new_anim_stop || self.zoom || self.health == 10 || (!(self.flags & FL_ONGROUND))) return; vector source; entity hit_ent = world; //entity oldself; float r, backupSkin; vector org; float hit = false; vector applied_velocity = '0 0 0'; if (self.sprinting) { W_SprintStop(); } W_HideCrosshair(self); if (self.stance == 2) PAnim_Melee(); backupSkin = self.weaponskin; self.weaponskin = 0; sound (self, CHAN_WEAPON, "sounds/weapons/knife/knife.wav", 1, ATTN_NORM); makevectors (self.v_angle); source = self.origin + self.view_ofs; if (self.weapon == W_BK || self.weapon == W_KRAUS) traceline (source, source + v_forward*80*1.2, 0, self); else traceline (source, source + v_forward*74*1.2, 0, self); org = trace_endpos - v_forward*4; r = fabs(vlen(source - org)); if (trace_ent.takedamage) { if (trace_ent.classname == "ai_zombie_head") hit_ent = trace_ent.owner; else if (trace_ent.classname == "ai_zombie_larm") hit_ent = trace_ent.owner; else if (trace_ent.classname == "ai_zombie_rarm") hit_ent = trace_ent.owner; else hit_ent = trace_ent; if (hit_ent.classname == "ai_zombie") hit = true; // if zombie has a head and it's instakill, gib for a e s t h e t i c if (hit_ent.head && instakill_finished >= time) { spawn_gibs(hit_ent.head.origin); setmodel(hit_ent.head, ""); } } else if (trace_fraction < 1.0) { // hit wall sound (self, CHAN_WEAPON, "sounds/weapons/knife/knife_hit.wav", 1, ATTN_NORM); } if (r > 40 && hit)//lunge { if (self.weapon == W_BK || self.weapon == W_KRAUS) { Set_W_Frame (14, 24, 1.4, 0, KNIFE, SUB_Null, "models/weapons/bk/v_bk.mdl", false, S_RIGHT); self.fire_delay = time + 1.5; self.knife_delay = time + 1.75; } else if (self.bowie) { Set_W_Frame (4, 10, 1.0, 0, KNIFE, W_PlayTakeOut, "models/weapons/knife/v_bowie.mdl", false, S_RIGHT); self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.8; self.knife_delay = time + 1.3; } else { self.punchangle_x = -5; self.punchangle_y = -10; Set_W_Frame (4, 12, 0.74, 0, KNIFE, W_PlayTakeOut, "models/weapons/knife/v_knife.mdl", false, S_RIGHT); self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.8; self.knife_delay = time + 1.1; } applied_velocity = v_forward * r * 6; } else { if (self.weapon == W_BK || self.weapon == W_KRAUS)//slash { Set_W_Frame (8, 13, 0, 0.4, KNIFE, SUB_Null, "models/weapons/bk/v_bk.mdl", false, S_RIGHT); self.fire_delay = time + 0.5; self.knife_delay = time + 0.8; } else if (self.bowie) { Set_W_Frame (0, 3, 0.65, 0, KNIFE, W_PlayTakeOut, "models/weapons/knife/v_bowie.mdl", false, S_RIGHT); self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4; self.knife_delay = time + 0.9; } else { self.punchangle_x = -5; self.punchangle_y = -10; //self.punchangle_z = 5; Set_W_Frame (0, 3, 0.45, 0, KNIFE, W_PlayTakeOut, "models/weapons/knife/v_knife.mdl", false, S_RIGHT); self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4; self.knife_delay = time + 0.8; } } self.reload_delay2 = self.reload_delay = 0; self.weaponskin = backupSkin; // Prohibit any sort of melee lunge while not standing if (self.stance != PLAYER_STANCE_STAND) self.velocity = applied_velocity; LungeKnifeHit(); }; /****************************** * W_Grenade * ******************************/ void() switch_nade = { if (self.downed || self.isBuying || !(self.grenades & 2) || self.seminade) return; if (self.pri_grenade_state == 0) { centerprint (self, "Bouncing Betties Selected"); self.bk_nade = 1; } if (self.pri_grenade_state == 1) { centerprint (self, "Frag Grenades Selected"); self.bk_nade = 0; } self.pri_grenade_state = self.bk_nade; }; void() GrenadeExplode = { sound (self, CHAN_WEAPON, "sounds/weapons/grenade/explode.wav", 1, ATTN_NORM); if (self.classname == "grenade") DamgageExplode (self, self.owner, 200, 50, 200); else if (self.classname == "betty") DamgageExplode (self, self.owner, 12000, 11000, 128); else DamgageExplode (self, self.owner, 1200, 1000, 75); #ifdef FTE te_customflash(self.origin, 200, 300, '1 1 1'); #endif // FTE // Really stupid hack: For betties, let's dramatically increase // the explosion effect. if (self.classname == "betty") { CallExplosion(self.origin); CallExplosion(self.origin); CallExplosion(self.origin); CallExplosion(self.origin); } else { CallExplosion(self.origin); } SUB_Remove (); }; void() NadeStraighten = { self.angles_x = (self.angles_x > 0) ? 90 : -90; if (self.pri_grenade_state == 1) return; }; void() Velocity_reduce = { if (!other.solid || other.solid == SOLID_TRIGGER) if (other != world) return; self.velocity = self.velocity*0.5; NadeStraighten(); }; void() W_ThrowGrenade = { string modelname; float startframe; float endframe; local entity nade; W_HideCrosshair(self); if (self.pri_grenade_state == 0) { nade = spawn (); nade.owner = self; nade.grenade_delay = nade.owner.grenade_delay; nade.owner.grenade_delay = 0; nade.movetype = MOVETYPE_BOUNCE; nade.solid = SOLID_BBOX; nade.classname = "grenade"; // set nade speed makevectors (self.v_angle); nade.velocity = v_forward*1400; nade.avelocity = '400 -400 400'; //nade.angles = vectoangles(nade.velocity); //nade.angles_z += (nade.angles_z + 180 < 360)? 180 : -180; nade.angles = vectoangles(v_forward); nade.angles_z -= 15; // set nade duration nade.nextthink = nade.grenade_delay; nade.think = GrenadeExplode; nade.touch = Velocity_reduce; setmodel (nade, "models/weapons/grenade/g_grenade.mdl"); setsize (nade, '0 0 0', '0 0 0'); nade.origin = self.origin + self.view_ofs; nade.origin += v_forward * 12; setorigin (nade, nade.origin); self.animend = ReturnWeaponModel; self.callfuncat = 0; self.isBuying = false; } else if (self.pri_grenade_state == 1) { Betty_Drop(); } else { centerprint (other, "No grenadetype defined...\n"); } startframe = GetFrame(self.weapon,TAKE_OUT_START); endframe = GetFrame(self.weapon,TAKE_OUT_END); modelname = GetWeaponModel(self.weapon, 0); Set_W_Frame (startframe, endframe, 0, 0, 0, ReturnWeaponModel, modelname, false, S_BOTH); SetUpdate(self, UT_HUD, 6, 0, 0); } void() checkHold = { if (self.pri_grenade_state == 1) { Betty_CheckForRelease(); return; } if (!self.button3 || self.grenade_delay < time) { if(self.grenade_delay < time) self.grenade_delay = time + 0.05; self.isBuying = true; Set_W_Frame (3, 6, 0, 5, GRENADE, W_ThrowGrenade, "models/weapons/grenade/v_grenade.mdl", true, S_RIGHT); sound (self, CHAN_WEAPON, "sounds/weapons/grenade/throw.wav", 1, ATTN_NORM); self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4; self.throw_delay = time + 0.9; } else { if (self.isBuying == false) { W_ShowCrosshair(self); self.isBuying = true; } // Pulse the grenade crosshair every 1 second float difference = rint(self.grenade_delay - time); if (difference != self.beingrevived) { self.beingrevived = difference; grenade_pulse(self); } Set_W_Frame (2, 2, 0, 0, GRENADE, checkHold, "models/weapons/grenade/v_grenade.mdl", true, S_RIGHT); } } void() W_Grenade = { if (self.throw_delay > time || self.zoom || self.downed || self.primary_grenades < 1 || self.isBuying) return; // Prevent the Player from Sprinting and also avoid issues with // the equipment being completely cancelled.. if (self.sprinting) W_SprintStop(); W_HideCrosshair(self); Set_W_Frame (0, 2, 0.6, 0, GRENADE, checkHold, "models/weapons/grenade/v_grenade.mdl", true, S_RIGHT); sound (self, CHAN_WEAPON, "sounds/weapons/grenade/prime.wav", 1, ATTN_NORM); self.primary_grenades -= 1; self.reload_delay2 = self.fire_delay2 = self.throw_delay = self.reload_delay = self.fire_delay = time + 6; self.grenade_delay = time + 5; self.seminade = true; } void(float wepnum) CheckButton = { W_PutOut(); } void() Change_Stance = { if (self.downed || !(self.flags & FL_ONGROUND) || self.changestance == true || self.new_ofs_z != self.view_ofs_z) return; switch(self.stance) { case PLAYER_STANCE_STAND: Player_SetStance(self, PLAYER_STANCE_CROUCH, true); break; case 1: // Prohibit Proning if Drinking a Perk-A-Cola, just stand. if (self.isBuying) { Player_SetStance(self, PLAYER_STANCE_STAND, true); } // Stance flag isn't set, so go Prone. else if (!self.stancereset) { Player_SetStance(self, PLAYER_STANCE_PRONE, true); } // Stance flag IS set, so stand if applicable. else if (Player_CanStandHere(self)) { Player_SetStance(self, PLAYER_STANCE_STAND, true); self.stancereset = false; } // There's no room to stand, just prone again. else { Player_SetStance(self, PLAYER_STANCE_PRONE, true); } break; case 0: Player_SetStance(self, PLAYER_STANCE_CROUCH, true); self.stancereset = true; break; default: break; } } void() dolphin_dive = //naievil { if (self.stance != 2) return; if ((map_compatibility_mode == MAP_COMPAT_BETA && self.view_ofs_z != VIEW_OFS_QK[2]) || (!map_compatibility_mode && self.view_ofs_z != VIEW_OFS_HL[2])) return; if (self.flags & FL_WATERJUMP) return; if (self.waterlevel >= 2) { if (self.watertype == CONTENT_WATER) self.velocity_z = 100; else if (self.watertype == CONTENT_SLIME) self.velocity_z = 80; else self.velocity_z = 50; } if (!(self.flags & FL_ONGROUND)) return; if ( !(self.flags & FL_JUMPRELEASED) ) return; // don't pogo stick self.flags = self.flags - (self.flags & FL_JUMPRELEASED); self.flags = self.flags - FL_ONGROUND; // don't stairwalk makevectors (self.v_angle); self.velocity = self.velocity * 1.1; self.velocity_z = self.velocity_z + 270; if (self.button2) self.button2 = 0; self.dive = 1; W_SprintStop(); PAnim_EnterDive(); sound(self, CHAN_VOICE, "sounds/player/jump.wav", 1, 1); self.oldz = self.origin_z; Player_SetStance(self, PLAYER_STANCE_PRONE, false); } void () Impulse_Functions = { if (!self.impulse) return; switch (self.impulse) { case 10: #ifdef FTE // FTE handles jumping, which we don't want.. so we use impulses to override. JumpCheck(1); #endif // FTE break; case 22: addmoney(self, 10000, 0); rounds += 4; break; case 23: #ifdef FTE if (!checkMovement(forward)) return; #endif // FTE if (self.dive || self.downed || !Player_CanStandHere(self)) return; Player_SetStance(self, PLAYER_STANCE_STAND, false); self.sprintflag = true; W_SprintStart(); break; case 24: self.sprintflag = false; W_SprintStop(); break; case 25: switch_nade(); break; case 33: W_PrimeBetty(); break; case 26: case 30: if (self.sprinting) dolphin_dive(); else Change_Stance(); break; case 31: self.sprintflag = 0; break; case 32: self.sprintflag = 1; break; case 110: case 111: W_PutOut(); self.semiswitch = true; break; case 100: cvar_set("waypoint_mode", "1"); localcmd("restart\n"); break; default: #ifdef FTE bprint(PRINT_HIGH, "Unknown impulse: ", ftos(self.impulse)); #endif // FTE break; } self.impulse = 0; }; void() CheckImpulses = { self.impulse = 0; } void(entity ent) CheckPowerups = { if (instakill_finished >= time) { if (instakill_finished < 4 + time) { if (insta_blink< time - 0.25) insta_blink = time + 0.25; } else if (instakill_finished < 7 + time) { if (insta_blink< time - 0.5) insta_blink = time + 0.5; } else if (instakill_finished < 10 + time) { if (insta_blink< time - 1) insta_blink = time + 1; } if (insta_blink < time) ent.insta_icon = true; else ent.insta_icon = false; } else if(instakill_finished) { instakill_finished = 0; ent.insta_icon = false; } if (x2_finished > time) { if (x2_finished < 4 + time) { if (x2_blink< time - 0.25) x2_blink = time + 0.25; } else if (x2_finished < 7 + time) { if (x2_blink< time - 0.5) x2_blink = time + 0.5; } else if (x2_finished < 10 + time) { if (x2_blink< time - 1) x2_blink = time + 1; } if (x2_blink < time) ent.x2_icon = true; else ent.x2_icon = false; } else if(x2_finished) { x2_finished = 0; ent.x2_icon = false; } } void() CheckPlayer = { CheckPowerups(self); CheckRevive(self); if (!self.sprintflag && self.sprinting) { W_SprintStop(); } // FIXME - can't do frame independent stance changing.. ofs starts spazzing.. if (self.view_ofs_z > self.new_ofs_z) { self.changestance = true; self.view_ofs_z = self.view_ofs_z - 1; } else if (self.view_ofs_z < self.new_ofs_z) { self.changestance = true; self.view_ofs_z = self.view_ofs_z + 1; } else { self.changestance = false; } if (self.view_ofs_z > 32) { self.view_ofs_z = 32; self.changestance = false; } // Diving on top of other players :) if (self.dive && !(self.flags & FL_ONGROUND)) { makevectors(self.angles); traceline(self.origin, self.origin + (v_up * -40), 0, self); if (trace_ent.classname == "player") { DamageHandler(self, trace_ent, 100000, S_NORMAL); DamageHandler(trace_ent, self, 100000, S_NORMAL); } } if (!self.button2 && self.flags & FL_ONGROUND) { float dist = fabs(self.oldz - self.origin_z); if (self.dive) { if (self.perks & P_FLOP && dist >= 64) { sound (self, CHAN_WEAPON, "sounds/weapons/grenade/explode.wav", 1, ATTN_NORM); DamgageExplode (self, self, 5000, 1000, 300); #ifdef FTE te_customflash(self.origin, 300, 300, '1 0.6 0.1'); #endif // FTE CallExplosion(self.origin); CallExplosion(self.origin); CallExplosion(self.origin); } PAnim_Flop(); self.dive = 0; } if (dist + 64 > 176 && !(self.perks & P_FLOP)) { float height = dist + 64; float damage = height*(0.68); if (damage > 98) damage = 98; DamageHandler (self, other, damage, S_ZOMBIE); if (self.health <= 5) GiveAchievement(7, self); } self.oldz = self.origin_z; } // // Revive Sequence // #ifdef FTE entity ent; ent = findradius(self.origin, 70); while(ent) { if(ent.classname == "player" && ent != self && ent.downed) { // perform a trace to make sure they're always facing the revivee vector source; makevectors(self.angles); source = self.origin - '0 0 12'; traceline(source, source + v_forward*50, 0, self); self.active_door = trace_ent; if (ent.beingrevived == false && self.active_door == ent && !self.downed) useprint (self, 13, 0, 0); if (self.button7 && !ent.invoke_revive && self.active_door == ent && !self.downed) { if (ent.beingrevived == true && ent.firer != self) return; // Broadcast that they're being revived BroadcastMessageToClient(ent, time + 2, 3, self.netname); ent.speed_penalty = 0.01; ent.speed_penalty_time = time + 100; ent.beingrevived = true; ent.firer = self; if (!self.progress_bar_percent) { ChangeReviveIconState(ent.electro_targeted, 2); self.movetype = MOVETYPE_TOSS; Set_W_Frame (0, 21, 0, 0, SPRINT, SUB_Null, "models/weapons/morphine/v_morphine.mdl", false, S_RIGHT); if !(self.perks & P_REVIVE) self.progress_bar_time = 4; else self.progress_bar_time = 2; self.progress_bar = self.progress_bar_time + time; self.progress_bar_percent = 1; self.reviving = true; } if (self.revived) { self.movetype = MOVETYPE_WALK; W_TakeOut(); ent.invoke_revive = 1; ent.beingrevived = false; ent.speed_penalty_time = 0; self.reviving = 0; self.progress_bar = 0; self.progress_bar_time = 0; self.progress_bar_percent = 0; self.revived = 0; addmoney(self, ent.requirespower, false); } } else if (self.downed || (!self.button7 && self.reviving) || (self.reviving && self.active_door != ent) || ent.classname == "disconnected") { if (ent.classname != "disconnected") ChangeReviveIconState(ent.electro_targeted, 1); else DisableReviveIcon(ent.electro_targeted); self.movetype = MOVETYPE_WALK; ent.beingrevived = false; ent.speed_penalty_time = 0; ent.firer = world; W_TakeOut(); self.progress_bar = 0; self.progress_bar_time = 0; self.progress_bar_percent = 0; self.revived = 0; self.reviving = 0; } } else if(ent.classname == "player" && ent != self && !ent.downed) // small player push { vector push; push = ent.origin - self.origin; push_z = 0; push = normalize(push) * 0.5; ent.velocity += push; } ent = ent.chain; } #endif // FTE } // // Wep_UpdateRecoilAndSpread() // Somewhat-intelligent serverside Weapon Recoil // update system that takes into account velocity // and how long the weapon has been fired. // void() Wep_UpdateRecoilAndSpread = { // Moving? Our weapon spread should be the maximum. if (self.velocity) self.cur_spread = WepDef_WeaponMaxSpread(self.weapon, self.stance); // Slowly increase our spread based on how long the weapon // has been fired. else if (self.recoil_delay > time && self.recoil_delay) { self.cur_spread = self.cur_spread + 10; if (self.cur_spread >= WepDef_WeaponMaxSpread(self.weapon, self.stance)) self.cur_spread = WepDef_WeaponMaxSpread(self.weapon, self.stance); } // Diminish spread over time as recoil decreases. else if (self.recoil_delay < time && self.recoil_delay) { self.cur_spread = self.cur_spread - 4; if (self.cur_spread <= 0) { self.cur_spread = 0; self.recoil_delay = 0; } } } void () Weapon_Logic = { // HACK HACK: Don't let the frame updater take control of hold fire. if (self.weaponframe == GetFrame(self.weapon, FIRE_HOLD) && GetFrame(self.weapon, FIRE_HOLD) != 0 && self.weaponmodel == GetWeaponModel(self.weapon, 0)) { } else { W_Frame_Update(); W2_Frame_Update(); } Impulse_Functions(); #ifndef FTE // For non-FTE only to tell our engine to display the scope if ((self.scopetime < time) && self.scopetime) { self.scopetime = 0; self.zoom = 2; self.weapon2model = ""; self.weaponmodel = ""; } #else UpdatePunchangle(self); #endif // FTE // If the player is aiming down the sight while prone and moving, // force them to stop if (self.stance == 0 && self.velocity && self.zoom) { W_AimOut(); self.zoom = 0; #ifdef FTE self.viewzoom = 1; UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); UpdateV2model(self.weapon2model, 0); #endif // FTE } // // Force-ADS if toggled // if (cvar("cl_adsmode")) { // Toggle ADS back and forth if (self.ads_toggle == false) { W_AimOut(); self.zoom = 0; #ifdef FTE self.viewzoom = 1; UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); UpdateV2model(self.weapon2model, 0); #endif // FTE } else if (self.reload_delay < time && self.reload_delay2 < time && self.knife_delay < time && self.grenade_delay < time) { if (self.stance == 0 && self.velocity) return; W_AimIn(); #ifdef FTE if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { if (self.scopetime >= time) { self.viewzoom = 1 - (0.018*GetWeaponZoomAmount(self.weapon)); self.zoom = 2; } } else { self.viewzoom = 1 - (0.018*GetWeaponZoomAmount(self.weapon)); } #endif // FTE if (!self.zoom) { if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { SetUpdate(self, UT_ZOOM2, 0, 0, 0); self.zoom = 2; self.scopetime = time + 0.3; } else { self.zoom = 1; } } } } // // Fire Button Logic // NOTE: Since the right weapon is the "normal" side there's some // flip flop logic on FTE so that LMB will fire the left side instead. // // Right Trigger/LMB/etc. if (self.button0) { #ifdef FTE if (IsDualWeapon(self.weapon)) { W_Fire(S_LEFT); } else { W_Fire(S_RIGHT); } #else W_Fire(S_RIGHT); #endif // FTE } else { // Check for release for semi automatic weapons. #ifdef FTE if (IsDualWeapon(self.weapon)) { self.semi2 = false; } else { self.semi = false; } // Hold fire weapons should get set back to base frame. if (GetFrame(self.weapon, FIRE_HOLD) != 0 && self.weaponframe == GetFrame(self.weapon, FIRE_HOLD) && self.weaponmodel == GetWeaponModel(self.weapon, 0)) { self.weaponframe = 0; } #else self.semi = false; #endif // FTE } // Left Trigger/RMB/etc. if (self.button8) { if (IsDualWeapon(self.weapon)) { #ifdef FTE W_Fire(S_RIGHT); #else W_Fire(S_LEFT); #endif // FTE } else { if (cvar("cl_adsmode")) { if (self.ads_release == false) return; self.ads_toggle = !self.ads_toggle; self.ads_release = false; } else { if (self.stance == 0 && self.velocity) return; W_AimIn(); #ifdef FTE if (self.zoom) { if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { if (self.scopetime >= time) self.viewzoom = 1 - (0.018*GetWeaponZoomAmount(self.weapon)); } else { float zoom_factor = 1 - (0.018*GetWeaponZoomAmount(self.weapon)); float sigmoid_input = (self.viewzoom - zoom_factor) * 10; // multiply by 10 to adjust the steepness of the sigmoid float sigmoid_output = 1 / (1 + exp(-sigmoid_input)); // apply the sigmoid function float zoom_speed = 0.06 * (sigmoid_output + 0.5) * (frametime*40); // adjust the zoom speed based on the sigmoid output if (self.viewzoom > zoom_factor) { self.viewzoom -= zoom_speed; if (self.viewzoom < zoom_factor) self.viewzoom = zoom_factor; } else { self.viewzoom = zoom_factor; } } } #endif // FTE } } } // AIM button is released else if (!self.button8 && self.zoom) { // So ADS toggle knows not to spam updates. self.ads_release = true; if (cvar("cl_adsmode") && self.ads_toggle == true) return; #ifdef FTE if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { ReturnWeaponModel(); UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); UpdateV2model(self.weapon2model, 0); self.viewzoom = 1; self.zoom = 0; if (self.weapons[0].weapon_magazine == 0) W_Reload(S_BOTH); } else { if (self.viewzoom == 0.75) { W_AimOut(); } else { float zoom_factor2 = 1 + (0.018 * GetWeaponZoomAmount(self.weapon)); // increase zoom factor float sigmoid_input2 = (zoom_factor2 - self.viewzoom) * 10; // adjust sigmoid input float sigmoid_output2 = 1 / (1 + exp(-sigmoid_input2)); // apply the sigmoid function float zoom_speed2 = 0.06 * (sigmoid_output2 + 0.5) * (frametime * 80); // adjust the zoom speed based on the sigmoid output if (self.viewzoom < zoom_factor2) { self.viewzoom += zoom_speed2; if (self.viewzoom > zoom_factor2) self.viewzoom = zoom_factor2; } else { self.viewzoom = zoom_factor2; } } } // Don't stop aiming until the camera is restored if (self.viewzoom >= 1) { self.viewzoom = 1; if (self.zoom != 3) self.zoom = 0; } #else W_AimOut(); #endif // FTE } else { // So ADS toggle knows not to spam updates. self.ads_release = true; // Check for release for semi automatic weapons. if (IsDualWeapon(self.weapon)) { #ifdef FTE self.semi = false; #else self.semi2 = false; #endif // FTE } } if (!self.button7) { self.semiuse = false; } if (self.button4 && !self.semiswitch && self.weapon_count != 1 && !self.zoom) { W_PutOut(); self.semiswitch = true; } else if (!self.button4 && self.semiswitch && self.impulse != 110 && self.impulse != 111) { self.semiswitch = false; } if (self.button3 && self.isBuying != true && self.seminade != true) { if (self.pri_grenade_state == 0) W_Grenade(); else W_PrimeBetty(); self.seminade = true; } else if (!self.button3 && self.seminade) { self.seminade = false; } if (self.button5 && !self.semireload) { W_Reload(S_BOTH); self.semireload = TRUE; } else if (!self.button5) { self.semireload = FALSE; } if (self.button6 && !self.semiknife) { // Don't do this at all on FTE. #ifndef FTE if (self.sprinting) { dolphin_dive(); return; } #endif // FTE W_Knife(); self.semiknife = true; } else if (!self.button6) self.semiknife = false; CheckImpulses(); CheckPlayer(); Wep_UpdateRecoilAndSpread(); // These are sent to clients in engine for stat // reference. self.currentammo = self.weapons[0].weapon_reserve; self.currentmag = self.weapons[0].weapon_magazine; self.currentmag2 = self.weapons[0].weapon_magazine_left; }