--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] -- Copyright (c) 2018-2020 TFA Base Devs -- Permission is hereby granted, free of charge, to any person obtaining a copy -- of this software and associated documentation files (the "Software"), to deal -- in the Software without restriction, including without limitation the rights -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -- copies of the Software, and to permit persons to whom the Software is -- furnished to do so, subject to the following conditions: -- The above copyright notice and this permission notice shall be included in all -- copies or substantial portions of the Software. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE. local lshift = bit.lshift local band = bit.band local bor = bit.bor local sp = game.SinglePlayer() local l_CT = CurTime local is, spr, wlk, cst --[[ Function Name: ResetEvents Syntax: self:ResetEvents() Returns: Nothing. Purpose: Cleans up events table. ]]-- function SWEP:ResetEvents() self:SetEventStatus1(0x00000000) self:SetEventStatus2(0x00000000) self:SetEventStatus3(0x00000000) self:SetEventStatus4(0x00000000) self:SetEventStatus5(0x00000000) self:SetEventStatus6(0x00000000) self:SetEventStatus7(0x00000000) self:SetEventStatus8(0x00000000) self:SetEventTimer(l_CT()) -- self:SetFirstDeployEvent(false) if self.EventTable then for _, eventtable in pairs(self.EventTable) do for i = 1, #eventtable do eventtable[i].called = false end end end if self.event_table_overflow then local editcts = self.EventTableEdict if editcts[0] then editcts[0].called = false for i = 1, #editcts do editcts[i].called = false end end end if sp then self:CallOnClient("ResetEvents", "") end end function SWEP:GetEventPlayed(event_slot) if self.event_table_overflow then return assert(self.EventTableEdict[event_slot], string.format("Unknown event %d", event_slot)).called end local inner_index = event_slot % 32 local outer_index = (event_slot - inner_index) / 32 + 1 local lindex = lshift(1, inner_index) return band(self.get_event_status_lut[outer_index](self), lindex) ~= 0, inner_index, outer_index, lindex end function SWEP:SetEventPlayed(event_slot) if self.event_table_overflow then assert(self.EventTableEdict[event_slot], string.format("Unknown event %d", event_slot)).called = true return end local inner_index = event_slot % 32 local outer_index = (event_slot - inner_index) / 32 + 1 local lindex = lshift(1, inner_index) self.set_event_status_lut[outer_index](self, bor(self.get_event_status_lut[outer_index](self), lindex)) return inner_index, outer_index, lindex end --[[ Function Name: ProcessEvents Syntax: self:ProcessEvents(). Returns: Nothing. Notes: Critical for the event table to function. Purpose: Main SWEP function ]]-- SWEP._EventSlotCount = 0 SWEP.EventTableEdict = {} function SWEP:DispatchLuaEvent(arg) if not self.event_table_built then self:RebuildEventEdictTable() end local fn = assert(assert(self.EventTableEdict[tonumber(arg)], "No such event with edict " .. arg).value, "Event is missing a function to call") assert(isfunction(fn), "Event " .. arg .. " is not a Lua event") fn(self, self:VMIV(), true) end function SWEP:DispatchBodygroupEvent(arg) if not self.event_table_built then self:RebuildEventEdictTable() end local event = assert(self.EventTableEdict[tonumber(arg)], "No such event with edict " .. arg) assert(isstring(event.name), "Event " .. arg .. " is missing bodygroup name to set") assert(isstring(event.value), "Event " .. arg .. " is missing bodygroup value to set") if event.view then self.ViewModelBodygroups[event.name] = event.value end if event.world then self.WorldModelBodygroups[event.name] = event.value end end local isstring = isstring local function eventtablesorter(a, b) local sa, sb = isstring(a), isstring(b) if sa and not sb or not sa and sb then if sa then return false end return true end return a < b end function SWEP:RebuildEventEdictTable() local self2 = self:GetTable() local slot = 0 for i = #self2.EventTableEdict, 0, -1 do self2.EventTableEdict[i] = nil end self:ResetEvents() local eventtable = self2.EventTable local keys = table.GetKeys(eventtable) table.sort(keys, eventtablesorter) for _, key in ipairs(keys) do local value = eventtable[key] if istable(value) then for _, event in SortedPairs(value) do if istable(event) then event.slot = slot slot = slot + 1 if not event.autodetect then if event.type == "lua" then if event.server == nil then event.server = true end elseif event.type == "snd" or event.type == "sound" then if event.server == nil then event.server = false end elseif event.type == "bg" or event.type == "bodygroup" then if event.server == nil then event.server = true end if event.view == nil then event.view = true end if event.world == nil then event.world = true end end if event.client == nil then event.client = true end event.autodetect = true end event.called = false if slot > 256 and not self.event_table_warning then ErrorNoHalt("[TFA Base] Weapon " .. self:GetClass() .. " got too many events! 256 is maximum! Event table would NOT be properly predicted this time!\n") self.event_table_warning = true end self2.EventTableEdict[event.slot] = event end end end end self.event_table_overflow = slot > 256 self._built_event_debug_string_fn = nil self._EventSlotCount = math.ceil(slot / 32) self._EventSlotNum = slot - 1 self.event_table_built = true end function SWEP:ProcessEvents(firstprediction) local viewmodel = self:VMIVNPC() if not viewmodel then return end if not self.event_table_built then self:RebuildEventEdictTable() end if sp and CLIENT then return end if sp and SERVER then return self:ProcessEventsSP() end local ply = self:GetOwner() local isplayer = ply:IsPlayer() local evtbl = self.EventTable[self:GetLastActivity() or -1] or self.EventTable[viewmodel:GetSequenceName(viewmodel:GetSequence())] if not evtbl then return end local curtime = l_CT() local eventtimer = self:GetEventTimer() local is_local = CLIENT and ply == LocalPlayer() local animrate = self:GetAnimationRate(self:GetLastActivity() or -1) self.current_event_iftp = firstprediction self.processing_events = true for i = 1, #evtbl do local event = evtbl[i] if self:GetEventPlayed(event.slot) or curtime < eventtimer + event.time / animrate then goto CONTINUE end self:SetEventPlayed(event.slot) event.called = true if not event.autodetect then if event.type == "lua" then if event.server == nil then event.server = true end elseif event.type == "snd" or event.type == "sound" then if event.server == nil then event.server = false end elseif event.type == "bg" or event.type == "bodygroup" then if event.server == nil then event.server = true end if event.view == nil then event.view = true end if event.world == nil then event.world = true end end if event.client == nil then event.client = true end event.autodetect = true end if event.type == "lua" then if ((event.client and CLIENT and (not event.client_predictedonly or is_local)) or (event.server and SERVER)) and event.value then event.value(self, viewmodel, firstprediction) end elseif event.type == "snd" or event.type == "sound" then if SERVER then if event.client then if not isplayer and player.GetCount() ~= 0 then net.Start("tfaSoundEvent", true) net.WriteEntity(self) net.WriteString(event.value or "") net.SendPVS(self:GetPos()) elseif isplayer then net.Start("tfaSoundEvent", true) net.WriteEntity(self) net.WriteString(event.value or "") net.SendOmit(ply) end elseif event.server and event.value and event.value ~= "" then self:EmitSound(event.value) end elseif event.client and is_local and not sp and event.value and event.value ~= "" then if firstprediction or firstprediction == nil then if event.time <= 0.01 then self:EmitSoundSafe(event.value) else self:EmitSound(event.value) end end end elseif event.type == "bg" or event.type == "bodygroup" then if ((event.client and CLIENT and (not event.client_predictedonly or is_local)) or (event.server and SERVER)) and (event.name and event.value and event.value ~= "") then if event.view then self.ViewModelBodygroups[event.name] = event.value end if event.world then self.WorldModelBodygroups[event.name] = event.value end end end ::CONTINUE:: end self.processing_events = false self.current_event_iftp = nil end -- This function is exclusively targeting singleplayer function SWEP:ProcessEventsSP(firstprediction) local viewmodel = self:VMIVNPC() if not viewmodel then return end local evtbl = self.EventTable[self:GetLastActivity() or -1] or self.EventTable[viewmodel:GetSequenceName(viewmodel:GetSequence())] if not evtbl then return end local curtime = l_CT() local eventtimer = self:GetEventTimer() local is_local = self:GetOwner() == Entity(1) local animrate = self:GetAnimationRate(self:GetLastActivity() or -1) self.processing_events = true for i = 1, #evtbl do local event = evtbl[i] if self:GetEventPlayed(event.slot) or curtime < eventtimer + event.time / animrate then goto CONTINUE end self:SetEventPlayed(event.slot) event.called = true if not event.autodetect then if event.type == "lua" then if event.server == nil then event.server = true end elseif event.type == "snd" or event.type == "sound" then if event.server == nil then event.server = false end elseif event.type == "bg" or event.type == "bodygroup" then if event.server == nil then event.server = true end if event.view == nil then event.view = true end if event.world == nil then event.world = true end end if event.client == nil then event.client = true end event.autodetect = true end if event.type == "lua" then if event.value then if event.server then event.value(self, viewmodel, true) end if event.client and (not event.client_predictedonly or is_local) then self:CallOnClient("DispatchLuaEvent", tostring(event.slot)) end end elseif event.type == "snd" or event.type == "sound" then if event.client then net.Start("tfaSoundEvent", true) net.WriteEntity(self) net.WriteString(event.value or "") net.Broadcast() elseif event.server and event.value and event.value ~= "" then self:EmitSound(event.value) end elseif event.type == "bg" or event.type == "bodygroup" then if event.name and event.value and event.value ~= "" then if event.server then if event.view then self.ViewModelBodygroups[event.name] = event.value end if event.world then self.WorldModelBodygroups[event.name] = event.value end end if event.client and (not event.client_predictedonly or is_local) then self:CallOnClient("DispatchBodygroupEvent", tostring(event.slot)) end end end ::CONTINUE:: end self.processing_events = false end function SWEP:EmitSoundSafe(snd) timer.Simple(0, function() if IsValid(self) and snd then self:EmitSound(snd) end end) end local ct, stat, statend, finalstat, waittime, lact function SWEP:ProcessStatus() local self2 = self:GetTable() is = self2.GetIronSightsRaw(self) spr = self2.GetSprinting(self) wlk = self2.GetWalking(self) cst = self2.GetCustomizing(self) local ply = self:GetOwner() local isplayer = ply:IsPlayer() if stat == TFA.Enum.STATUS_FIDGET and is then self:SetStatusEnd(0) self2.Idle_Mode_Old = self2.Idle_Mode self2.Idle_Mode = TFA.Enum.IDLE_BOTH self2.ChooseIdleAnim(self) if sp then self:CallOnClient("ChooseIdleAnim", "") end self2.Idle_Mode = self2.Idle_Mode_Old self2.Idle_Mode_Old = nil statend = -1 end is = self:GetIronSights() stat = self:GetStatus() statend = self:GetStatusEnd() ct = l_CT() if stat ~= TFA.Enum.STATUS_IDLE and ct > statend then self:SetFirstDeployEvent(false) finalstat = TFA.Enum.STATUS_IDLE --Holstering if stat == TFA.Enum.STATUS_HOLSTER then finalstat = TFA.Enum.STATUS_HOLSTER_READY self:SetStatusEnd(ct) elseif stat == TFA.Enum.STATUS_HOLSTER_READY then self2.FinishHolster(self) finalstat = TFA.Enum.STATUS_HOLSTER_FINAL self:SetStatusEnd(ct + 0.6) elseif stat == TFA.Enum.STATUS_RELOADING_LOOP_START_EMPTY then --Shotgun Reloading from empty if not self2.IsJammed(self) then self2.TakePrimaryAmmo(self, 1, true) self2.TakePrimaryAmmo(self, -1) end if self2.Ammo1(self) <= 0 or self:Clip1() >= self2.GetPrimaryClipSize(self) or self:GetReloadLoopCancel() then finalstat = TFA.Enum.STATUS_RELOADING_LOOP_END local _, tanim = self2.ChooseShotgunPumpAnim(self) self:SetStatusEnd(ct + self:GetActivityLength(tanim)) self:SetReloadLoopCancel(false) if not self:GetReloadLoopCancel() then self:SetJammed(false) end else lact = self:GetLastActivity() waittime = self2.GetActivityLength(self, lact, false) - self2.GetActivityLength(self, lact, true) if waittime > 0.01 then finalstat = TFA.Enum.STATUS_RELOADING_WAIT self:SetStatusEnd(ct + waittime) else finalstat = self2.LoadShell(self) end self:SetJammed(false) --finalstat = self:LoadShell() --self:SetStatusEnd( self:GetNextPrimaryFire() ) end elseif stat == TFA.Enum.STATUS_RELOADING_LOOP_START then --Shotgun Reloading finalstat = self2.LoadShell(self) elseif stat == TFA.Enum.STATUS_RELOADING_LOOP then self2.TakePrimaryAmmo(self, 1, true) self2.TakePrimaryAmmo(self, -1) lact = self:GetLastActivity() if self2.GetActivityLength(self, lact, true) < self2.GetActivityLength(self, lact, false) - 0.01 then local sht = self2.GetStatL(self, "LoopedReloadInsertTime") if sht then sht = sht / self2.GetAnimationRate(self, ACT_VM_RELOAD) end waittime = (sht or self2.GetActivityLength(self, lact, false)) - self2.GetActivityLength(self, lact, true) else waittime = 0 end if waittime > 0.01 then finalstat = TFA.Enum.STATUS_RELOADING_WAIT self:SetStatusEnd(ct + waittime) else if self2.Ammo1(self) <= 0 or self:Clip1() >= self:GetPrimaryClipSize() or self:GetReloadLoopCancel() then finalstat = TFA.Enum.STATUS_RELOADING_LOOP_END local _, tanim = self2.ChooseShotgunPumpAnim(self) self:SetStatusEnd(ct + self:GetActivityLength(tanim)) self:SetReloadLoopCancel(false) else finalstat = self2.LoadShell(self) end end elseif stat == TFA.Enum.STATUS_RELOADING then self2.CompleteReload(self) lact = self:GetLastActivity() waittime = self2.GetActivityLength(self, lact, false) - self2.GetActivityLength(self, lact, true) if waittime > 0.01 then finalstat = TFA.Enum.STATUS_RELOADING_WAIT self:SetStatusEnd(ct + waittime) end elseif stat == TFA.Enum.STATUS_SILENCER_TOGGLE then --self:SetStatusEnd( self:GetNextPrimaryFire() ) self:SetSilenced(not self:GetSilenced()) self2.Silenced = self:GetSilenced() elseif stat == TFA.Enum.STATUS_RELOADING_WAIT and self:GetStatL("LoopedReload") then if self2.Ammo1(self) <= 0 or self:Clip1() >= self:GetPrimaryClipSize() or self:GetReloadLoopCancel() then finalstat = TFA.Enum.STATUS_RELOADING_LOOP_END local _, tanim = self2.ChooseShotgunPumpAnim(self) self:SetStatusEnd(ct + self:GetActivityLength(tanim)) --self:SetReloadLoopCancel( false ) else finalstat = self2.LoadShell(self) end elseif stat == TFA.Enum.STATUS_RELOADING_LOOP_END and self:GetStatL("LoopedReload") then self:SetReloadLoopCancel(false) elseif self2.GetStatL(self, "PumpAction") and stat == TFA.Enum.STATUS_PUMP then self:SetReloadLoopCancel(false) elseif stat == TFA.Enum.STATUS_SHOOTING and self2.GetStatL(self, "PumpAction") then if self:Clip1() == 0 and self2.GetStatL(self, "PumpAction").value_empty then --finalstat = TFA.Enum.STATUS_PUMP_READY self:SetReloadLoopCancel(true) elseif (self2.GetStatL(self, "Primary.ClipSize") < 0 or self:Clip1() > 0) and self2.GetStatL(self, "PumpAction").value then --finalstat = TFA.Enum.STATUS_PUMP_READY self:SetReloadLoopCancel(true) end end --self:SetStatusEnd( math.huge ) self:SetStatus(finalstat) local smi = self2.Sights_Mode == TFA.Enum.LOCOMOTION_HYBRID or self2.Sights_Mode == TFA.Enum.LOCOMOTION_ANI local spi = self2.Sprint_Mode == TFA.Enum.LOCOMOTION_HYBRID or self2.Sprint_Mode == TFA.Enum.LOCOMOTION_ANI local wmi = self2.Walk_Mode == TFA.Enum.LOCOMOTION_HYBRID or self2.Walk_Mode == TFA.Enum.LOCOMOTION_ANI local cmi = self2.Customize_Mode == TFA.Enum.LOCOMOTION_HYBRID or self2.Customize_Mode == TFA.Enum.LOCOMOTION_ANI if not TFA.Enum.ReadyStatus[stat] and stat ~= TFA.Enum.STATUS_SHOOTING and stat ~= TFA.Enum.STATUS_PUMP and finalstat == TFA.Enum.STATUS_IDLE and ((smi or spi) or (cst and cmi)) then is = self2.GetIronSights(self, true) if (is and smi) or (spr and spi) or (wlk and wmi) or (cst and cmi) then local success, _ = self2.Locomote(self, is and smi, is, spr and spi, spr, wlk and wmi, wlk, cst and cmi, cst) if success == false then self:SetNextIdleAnim(-1) else self:SetNextIdleAnim(math.max(self:GetNextIdleAnim(), ct + 0.1)) end end end self2.LastBoltShoot = nil if self:GetBurstCount() > 0 then if finalstat ~= TFA.Enum.STATUS_SHOOTING and finalstat ~= TFA.Enum.STATUS_IDLE then self:SetBurstCount(0) elseif self:GetBurstCount() < self:GetMaxBurst() and self:Clip1() > 0 then self:PrimaryAttack() else self:SetBurstCount(0) self:SetNextPrimaryFire(self2.GetNextCorrectedPrimaryFire(self, self2.GetBurstDelay(self))) end end end --if stat == TFA.Enum.STATUS_IDLE and self:GetReloadLoopCancel() and (self2.GetStatL(self, "AllowSprintAttack") or self:GetSprintProgress() < 0.1) then if stat == TFA.Enum.STATUS_IDLE and self:GetReloadLoopCancel() then if self2.GetStatL(self, "PumpAction") then if ct > self:GetNextPrimaryFire() and not self:KeyDown(IN_ATTACK) then self2.DoPump(self) end else self:SetReloadLoopCancel(false) end end end