diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dd28403e..90a87adb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,18 @@ All notable changes to TTT2 will be documented here. Inspired by [keep a changel - Fixed classic armour protecting against crowbar damage (by @wgetJane) - Fixed C4/Radio sounds not playing outside of PAS (Facepunch/garrysmod/pull/2203, by @figardo) - Fixed players sometimes being revealed as dead when they chat/voicechat right as they die (Facepunch/garrysmod/pull/2229, by @wgetJane) +- Fixed various bugs related to using doors and buttons, to match base TTT behavior (by @wgetJane) + - Fixed brush doors on certain maps not being usable + - Fixed certain doors on certain maps being forcibly usable when they shouldn't be + - Fixed func_rotating being forcibly usable + - Fixed vehicles not being usable + - Removed forced button solidity + - Improved targetID trace detection for doors and buttons + - Allow brush doors to be detected as destructible for targetID + - Use NW2Vars instead of NWVars for door variables for more efficient networking + - Fixed clientside door.GetAll() to return a more updated list of doors + - Removed clientside TTT2PostDoorSetup hook + - Changed crowbar unlocking behavior to match base TTT ## [v0.14.2b](https://github.com/TTT-2/TTT2/tree/v0.14.2b) (2025-02-02) diff --git a/gamemodes/terrortown/entities/weapons/weapon_zm_improvised.lua b/gamemodes/terrortown/entities/weapons/weapon_zm_improvised.lua index fb868589d..341dbfda1 100644 --- a/gamemodes/terrortown/entities/weapons/weapon_zm_improvised.lua +++ b/gamemodes/terrortown/entities/weapons/weapon_zm_improvised.lua @@ -85,7 +85,7 @@ local pmnc_tbl = { } local function OpenableEnt(ent) - return pmnc_tbl[ent:GetClass()] or OPEN_NO + return ent:GetName() ~= "" and pmnc_tbl[ent:GetClass()] or OPEN_NO end local function CrowbarCanUnlock(t) @@ -108,7 +108,7 @@ function SWEP:OpenEnt(hitEnt) hitEnt:Fire("Unlock", nil, 0) end - if unlock or hitEnt:HasSpawnFlags(SF_NPC_LONG_RANGE) then -- Long Visibility/Shoot + if unlock or hitEnt:HasSpawnFlags(256) then -- SF_DOOR_PUSE if openable == OPEN_ROT then hitEnt:Fire("OpenAwayFrom", self:GetOwner(), 0) end diff --git a/gamemodes/terrortown/gamemode/client/cl_keys.lua b/gamemodes/terrortown/gamemode/client/cl_keys.lua index 8b2935db6..c75651184 100644 --- a/gamemodes/terrortown/gamemode/client/cl_keys.lua +++ b/gamemodes/terrortown/gamemode/client/cl_keys.lua @@ -75,7 +75,7 @@ function GM:PlayerBindPress(ply, bindName, pressed) return TBHUD:UseFocused() end - -- Find out if a marker is focussed otherwise check normal use + -- Find out if a marker is focused otherwise check normal use local isClientOnly = false local useEnt = markerVision.GetFocusedEntity() local isRemote = IsValid(useEnt) @@ -104,14 +104,17 @@ function GM:PlayerBindPress(ply, bindName, pressed) -- If returned true by ClientUse or RemoteUse, then dont call Use and UseOverride or RemoteUse serverside if isClientOnly then - return + return true end - net.Start("TTT2PlayerUseEntity") - net.WriteBool(useEnt ~= nil) - net.WriteEntity(useEnt) - net.WriteBool(isRemote) - net.SendToServer() + if IsValid(useEnt) and useEnt:IsSpecialUsableEntity() then + net.Start("TTT2PlayerUseEntity") + net.WriteEntity(useEnt) + net.WriteBool(isRemote) + net.SendToServer() + + return true + end elseif string.sub(bindName, 1, 4) == "slot" and pressed then local idx = tonumber(string.sub(bindName, 5, -1)) or 1 diff --git a/gamemodes/terrortown/gamemode/server/sv_entity.lua b/gamemodes/terrortown/gamemode/server/sv_entity.lua index 3ceb9130d..9618ada46 100644 --- a/gamemodes/terrortown/gamemode/server/sv_entity.lua +++ b/gamemodes/terrortown/gamemode/server/sv_entity.lua @@ -90,7 +90,7 @@ function entmeta:IsUsableEntity(requiredCaps) -- special case: TTT specific lua based use interactions -- when we're looking for specifically the lua use - if self:IsWeapon() or self.player_ragdoll or (self:IsScripted() and self.CanUseKey) then + if self:IsSpecialUsableEntity() then return true end diff --git a/gamemodes/terrortown/gamemode/server/sv_main.lua b/gamemodes/terrortown/gamemode/server/sv_main.lua index 2f039fe11..c199c8aab 100644 --- a/gamemodes/terrortown/gamemode/server/sv_main.lua +++ b/gamemodes/terrortown/gamemode/server/sv_main.lua @@ -1007,3 +1007,10 @@ function GM:TTT2PreEndRound(result, duration) -- send the clients the round log, players will be shown the report events.StreamToClients() end + +--- +-- Called right after all doors are initialized on the map. +-- @param table doorsTable A table with the newly registered door entities +-- @hook +-- @realm server +function GM:TTT2PostDoorSetup(doorsTable) end diff --git a/gamemodes/terrortown/gamemode/server/sv_player.lua b/gamemodes/terrortown/gamemode/server/sv_player.lua index 0b2736b6f..0b510e3c2 100644 --- a/gamemodes/terrortown/gamemode/server/sv_player.lua +++ b/gamemodes/terrortown/gamemode/server/sv_player.lua @@ -343,18 +343,30 @@ end -- Triggered when the @{Player} presses use on an object. -- Continuously runs until USE is released but will not activate other Entities -- until the USE key is released; dependent on activation type of the @{Entity}. --- @note TTT2 blocks all gmod internal use and only checks this for addons -- @param Player ply The @{Player} pressing the "use" key. -- @param Entity ent The entity which the @{Player} is looking at / activating USE on. --- @param bool overrideDoPlayerUse This is used to override the default outcome of the check -- @return boolean Return false if the @{Player} is not allowed to USE the entity. -- Do not return true if using a hook, otherwise other mods may not get a chance to block a @{Player}'s use. -- @hook -- @realm server -- @ref https://wiki.facepunch.com/gmod/GM:PlayerUse -- @local -function GM:PlayerUse(ply, ent, overrideDoPlayerUse) - return overrideDoPlayerUse or false +function GM:PlayerUse(ply, ent) + if not ply:IsTerror() then + return false + end + + if + ent:IsButton() + --- + -- @realm server + and hook.Run("TTT2OnButtonUse", ply, ent, ent:GetInternalVariable("m_toggle_state")) + == false + then + return false + end + + return true end --- @@ -514,11 +526,8 @@ end local function EntityContinuousUse(ent, ply) --- - -- Enable addons to allow handling PlayerUse - -- Otherwise default to old IsTerror Check -- @realm server - -- stylua: ignore - if hook.Run("PlayerUse", ply, ent, ply:IsTerror()) then + if hook.Run("PlayerUse", ply, ent) then ent:Use(ply, ply) end @@ -568,7 +577,7 @@ local function EntityContinuousUse(ent, ply) end -- make sure the entity is still in a good position - local distance = ply:GetShootPos():Distance(ent:GetPos()) + local distance = ply:GetShootPos():Distance(ent:WorldSpaceCenter()) if distance > 100 + ent:BoundingRadius() then return @@ -586,15 +595,10 @@ end -- @realm server -- @internal net.Receive("TTT2PlayerUseEntity", function(len, ply) - local hasEnt = net.ReadBool() local ent = net.ReadEntity() local isRemote = net.ReadBool() - if not hasEnt or not ent:IsUsableEntity() then - ent = ply:GetUseEntity() - end - - if not IsValid(ent) then + if not (IsValid(ent) and ent:IsSpecialUsableEntity()) then return end @@ -608,21 +612,11 @@ net.Receive("TTT2PlayerUseEntity", function(len, ply) -- Check if the use interaction is possible -- Add the bounding radius to compensate for center position - local distance = ply:GetShootPos():Distance(ent:GetPos()) + local distance = ply:GetShootPos():Distance(ent:WorldSpaceCenter()) if distance > 100 + ent:BoundingRadius() then return end - if - ent:IsButton() - --- - -- @realm server - and hook.Run("TTT2OnButtonUse", ply, ent, ent:GetInternalVariable("m_toggle_state")) - == false - then - return - end - EntityContinuousUse(ent, ply) end) @@ -1324,6 +1318,18 @@ function GM:EntityTakeDamage(ent, dmginfo) end end +--- +-- Called after an entity receives a damage event, after passing damage filters, etc. +-- @param Entity ent The @{Entity} taking damage +-- @param CTakeDamageInfo dmginfo Damage info +-- @param boolean wasDamageTaken Whether the entity actually took the damage +-- @hook +-- @realm server +-- @ref https://wiki.facepunch.com/gmod/GM:PostEntityTakeDamage +function GM:PostEntityTakeDamage(ent, dmginfo, wasDamageTaken) + door.PostHandleDamage(ent, dmginfo, wasDamageTaken) +end + --- -- Called by @{GM:EntityTakeDamage} -- @param Entity ent The @{Entity} taking damage diff --git a/gamemodes/terrortown/gamemode/shared/sh_door.lua b/gamemodes/terrortown/gamemode/shared/sh_door.lua index 6d7234923..b27351cd6 100644 --- a/gamemodes/terrortown/gamemode/shared/sh_door.lua +++ b/gamemodes/terrortown/gamemode/shared/sh_door.lua @@ -45,7 +45,7 @@ function entmeta:IsDoorLocked() return end - return self:GetNWBool("ttt2_door_locked", false) + return self:GetNW2Bool("ttt2_door_locked", false) end --- @@ -57,7 +57,7 @@ function entmeta:IsDoorForceclosed() return end - return self:GetNWBool("ttt2_door_forceclosed", false) + return self:GetNW2Bool("ttt2_door_forceclosed", false) end --- @@ -70,7 +70,7 @@ function entmeta:UseOpensDoor() return end - return self:GetNWBool("ttt2_door_player_use", false) + return self:GetNW2Bool("ttt2_door_player_use", false) end --- @@ -82,7 +82,7 @@ function entmeta:TouchOpensDoor() return end - return self:GetNWBool("ttt2_door_player_touch", false) + return self:GetNW2Bool("ttt2_door_player_touch", false) end --- @@ -106,11 +106,11 @@ function entmeta:DoorAutoCloses() return end - return self:GetNWBool("ttt2_door_auto_close", false) + return self:GetNW2Bool("ttt2_door_auto_close", false) end --- --- Retuens if a door is destructible. +-- Returns if a door is destructible. -- @return boolean If a door is destructible -- @realm shared function entmeta:DoorIsDestructible() @@ -118,7 +118,16 @@ function entmeta:DoorIsDestructible() return end - return self:GetNWBool("ttt2_door_is_destructable", false) + -- might be a little hacky + if + self:Health() > 0 + and self:GetMaxHealth() > 0 + and (self:GetInternalVariable("m_takedamage") or 2) > 1 + then + return true + end + + return self:GetNW2Bool("ttt2_door_is_destructable", false) end --- @@ -130,7 +139,7 @@ function entmeta:IsDoorOpen() return end - return self:GetNWBool("ttt2_door_open", false) + return self:GetNW2Bool("ttt2_door_open", false) end --- @@ -138,7 +147,7 @@ end -- @return number The synced health -- @realm shared function entmeta:GetFastSyncedHealth() - return math.max(0, self:GetNWInt("fast_sync_health", 100)) + return math.max(0, self:GetNW2Int("fast_sync_health", self:Health())) end if SERVER then @@ -259,7 +268,7 @@ if SERVER then function entmeta:SetDoorCanTouchOpen(state, surpressPair) door.SetPlayerCanTouch(self, state) - self:SetNWBool("ttt2_door_player_touch", door.PlayerCanTouch(self)) + self:SetNW2Bool("ttt2_door_player_touch", door.PlayerCanTouch(self)) -- if the door is grouped as a pair, call the other one as well if not surpressPair and IsValid(self.otherPairDoor) then @@ -275,7 +284,7 @@ if SERVER then function entmeta:SetDoorCanUseOpen(state, surpressPair) door.SetPlayerCanUse(self, state) - self:SetNWBool("ttt2_door_player_use", door.PlayerCanUse(self)) + self:SetNW2Bool("ttt2_door_player_use", door.PlayerCanUse(self)) -- if the door is grouped as a pair, call the other one as well if not surpressPair and IsValid(self.otherPairDoor) then @@ -291,7 +300,7 @@ if SERVER then function entmeta:SetDoorAutoCloses(state, surpressPair) door.SetAutoClose(self, state) - self:SetNWBool("ttt2_door_auto_close", door.AutoCloses(self)) + self:SetNW2Bool("ttt2_door_auto_close", door.AutoCloses(self)) -- if the door is grouped as a pair, call the other one as well if not surpressPair and IsValid(self.otherPairDoor) then @@ -309,12 +318,12 @@ if SERVER then return end - self:SetNWBool("ttt2_door_is_destructable", state) + self:SetNW2Bool("ttt2_door_is_destructable", state) if self:Health() == 0 then self:SetHealth(cvDoorHealth:GetInt()) - self:SetNWInt("fast_sync_health", self:Health()) + self:SetNW2Int("fast_sync_health", self:Health()) end -- if the door is grouped as a pair, call the other one as well diff --git a/gamemodes/terrortown/gamemode/shared/sh_entity.lua b/gamemodes/terrortown/gamemode/shared/sh_entity.lua index 6b95d1659..9c215d8f0 100644 --- a/gamemodes/terrortown/gamemode/shared/sh_entity.lua +++ b/gamemodes/terrortown/gamemode/shared/sh_entity.lua @@ -40,3 +40,15 @@ end function entmeta:IsButton() return self:IsDefaultButton() or self:IsRotatingButton() end + +--- +-- @return boolean +-- @realm server +function entmeta:IsSpecialUsableEntity() + return self:IsWeapon() + or self.player_ragdoll + or self:IsPlayerRagdoll() + or self.CanUseKey + or self.ClientUse + or self.RemoteUse +end diff --git a/gamemodes/terrortown/gamemode/shared/sh_main.lua b/gamemodes/terrortown/gamemode/shared/sh_main.lua index b0c90ca32..7d8378ddf 100644 --- a/gamemodes/terrortown/gamemode/shared/sh_main.lua +++ b/gamemodes/terrortown/gamemode/shared/sh_main.lua @@ -548,13 +548,6 @@ function GM:InitFallbackShops() end -- @realm shared function GM:LoadedFallbackShops() end ---- --- Called right after all doors are initialized on the map. --- @param table doorsTable A table with the newly registered door entities --- @hook --- @realm shared -function GM:TTT2PostDoorSetup(doorsTable) end - -- Called after all roles were loaded, @{ROLE:Preinitialize} and @{ROLE:Initialize} were called -- and their convars were set up. -- @hook diff --git a/lua/ttt2/libraries/buttons.lua b/lua/ttt2/libraries/buttons.lua index af45aca6a..06493f16e 100644 --- a/lua/ttt2/libraries/buttons.lua +++ b/lua/ttt2/libraries/buttons.lua @@ -31,10 +31,7 @@ if SERVER then for j = 1, #buttonsTable do local foundButton = buttonsTable[j] - foundButton:SetNotSolid(false) - foundButton:SetSolid(SOLID_BSP) - - foundButton:SetNWInt("button_class", i) + foundButton:SetNW2Int("button_class", i) end end @@ -62,7 +59,7 @@ end -- @return boolean Returns true if the entity matches the provided class name -- @realm shared function button.IsClass(ent, class) - local classID = ent:GetNWInt("button_class") + local classID = ent:GetNW2Int("button_class") if not classID or classID > #validButtons then return diff --git a/lua/ttt2/libraries/door.lua b/lua/ttt2/libraries/door.lua index 4b4138453..38a2cd538 100644 --- a/lua/ttt2/libraries/door.lua +++ b/lua/ttt2/libraries/door.lua @@ -5,7 +5,6 @@ local string = string local player = player -local net = net local hook = hook local util = util local timer = timer @@ -14,8 +13,6 @@ local Angle = Angle if SERVER then AddCSLuaFile() - - util.AddNetworkString("TTT2SyncDoorEntities") end --- @@ -58,9 +55,7 @@ SF_FUNC_DOOR_SILENT_GENERAL = 4096 door = door or {} -local door_list = { - doors = {}, -} +local door_list = {} local valid_doors = { special = { @@ -182,14 +177,14 @@ if SERVER then doors[#doors + 1] = ent - ent:SetNWBool("ttt2_door_locked", ent:GetInternalVariable("m_bLocked") or false) - ent:SetNWBool("ttt2_door_forceclosed", ent:GetInternalVariable("forceclosed") or false) - ent:SetNWBool("ttt2_door_open", door.IsOpen(ent) or false) + ent:SetNW2Bool("ttt2_door_locked", ent:GetInternalVariable("m_bLocked") or false) + ent:SetNW2Bool("ttt2_door_forceclosed", ent:GetInternalVariable("forceclosed") or false) + ent:SetNW2Bool("ttt2_door_open", door.IsOpen(ent) or false) - ent:SetNWBool("ttt2_door_player_use", door.PlayerCanUse(ent)) - ent:SetNWBool("ttt2_door_player_touch", door.PlayerCanTouch(ent)) - ent:SetNWBool("ttt2_door_auto_close", door.AutoCloses(ent)) - ent:SetNWBool("ttt2_door_is_destructable", door.IsDestructible(ent)) + ent:SetNW2Bool("ttt2_door_player_use", door.PlayerCanUse(ent)) + ent:SetNW2Bool("ttt2_door_player_touch", door.PlayerCanTouch(ent)) + ent:SetNW2Bool("ttt2_door_auto_close", door.AutoCloses(ent)) + ent:SetNW2Bool("ttt2_door_is_destructable", door.IsDestructible(ent)) entityOutputs.RegisterMapEntityOutput(ent, "OnOpen", "TTT2DoorOpens") entityOutputs.RegisterMapEntityOutput(ent, "OnClose", "TTT2DoorCloses") @@ -214,35 +209,7 @@ if SERVER then --- -- @realm server hook.Run("TTT2PostDoorSetup", doors) - - local amountDoors = #doors - - net.Start("TTT2SyncDoorEntities") - net.WriteUInt(amountDoors, 16) - - -- sync door list with clients - for i = 1, amountDoors do - net.WriteEntity(doors[i]) - end - - net.Broadcast() end -else -- CLIENT - net.Receive("TTT2SyncDoorEntities", function() - local amount = net.ReadUInt(16) - - local doors = {} - - for i = 1, amount do - doors[i] = net.ReadEntity() - end - - door_list.doors = doors - - --- - -- @realm client - hook.Run("TTT2PostDoorSetup", doors) - end) end --- @@ -271,12 +238,43 @@ function door.IsValidSpecial(cls) return valid_doors.special[cls] or false end ---- --- Returns all valid door entities found on a map --- @return table A table of door entities --- @realm shared -function door.GetAll() - return door_list.doors +if SERVER then + --- + -- Returns all valid door entities found on a map + -- @return table A table of door entities + -- @realm server + function door.GetAll() + return door_list.doors or {} + end +else + --- + -- Returns all valid door entities found on a map + -- @return table A table of door entities + -- @realm client + function door.GetAll() + if not door_list.doors then + local doors = {} + + for _, ent in ents.Iterator() do + if IsValid(ent) and ent:IsDoor() then + doors[#doors + 1] = ent + end + end + + door_list.doors = doors + end + + return door_list.doors + end + + local function InvalidateDoorCache(ent) + if ent.IsDoor and ent:IsDoor() then + door_list.doors = nil + end + end + + hook.Add("OnEntityCreated", "TTT2InvalidateDoorCache", InvalidateDoorCache) + hook.Add("EntityRemoved", "TTT2InvalidateDoorCache", InvalidateDoorCache) end if SERVER then @@ -365,7 +363,7 @@ if SERVER then if door.IsValidNormal(cls) then return ent:HasSpawnFlags(SF_PROP_DOOR_START_BREAKABLE) elseif door.IsValidSpecial(cls) then - return false + return (ent:GetInternalVariable("m_takedamage") or 0) > 1 end return false @@ -454,6 +452,11 @@ if SERVER then return end + -- skip if engine already handles damage + if (ent:GetInternalVariable("m_takedamage") or 0) > 1 then + return + end + local damage = math.max(0, dmginfo:GetDamage()) local health = ent:Health() - damage @@ -473,7 +476,21 @@ if SERVER then end ent:SetHealth(health) - ent:SetNWInt("fast_sync_health", health) + end + + --- + -- Called in @{GM:PostEntityTakeDamage}. + -- @param Entity ent The entity that is damaged + -- @param CTakeDamageInfo dmginfo The damage info object + -- @param boolean wasDamageTaken Whether the entity actually took the damage + -- @internal + -- @realm server + function door.PostHandleDamage(ent, dmginfo, wasDamageTaken) + if not ent:DoorIsDestructible() then + return + end + + ent:SetNW2Int("fast_sync_health", ent:Health()) end --- @@ -577,7 +594,7 @@ if SERVER then -- we expect the door to be locked now, but we check the real state after a short -- amount of time to be sure - ent:SetNWBool("ttt2_door_locked", true) + ent:SetNW2Bool("ttt2_door_locked", true) -- check if the assumed state was correct timer.Create("ttt2_recheck_door_lock_" .. ent:EntIndex(), 1, 1, function() @@ -585,7 +602,7 @@ if SERVER then return end - ent:SetNWBool("ttt2_door_locked", ent:GetInternalVariable("m_bLocked") or false) + ent:SetNW2Bool("ttt2_door_locked", ent:GetInternalVariable("m_bLocked") or false) end) elseif name == "unlock" then --- @@ -598,7 +615,7 @@ if SERVER then -- we expect the door to be unlocked now, but we check the real state after a short -- amount of time to be sure - ent:SetNWBool("ttt2_door_locked", false) + ent:SetNW2Bool("ttt2_door_locked", false) -- check if the assumed state was correct timer.Create("ttt2_recheck_door_unlock_" .. ent:EntIndex(), 1, 1, function() @@ -606,7 +623,7 @@ if SERVER then return end - ent:SetNWBool("ttt2_door_locked", ent:GetInternalVariable("m_bLocked") or false) + ent:SetNW2Bool("ttt2_door_locked", ent:GetInternalVariable("m_bLocked") or false) end) elseif name == "use" and ent:IsDoorOpen() then -- do not stack closing time if door closes automatically @@ -657,7 +674,7 @@ if SERVER then return end - doorEntity:SetNWBool("ttt2_door_open", true) + doorEntity:SetNW2Bool("ttt2_door_open", true) end --- @@ -671,7 +688,7 @@ if SERVER then return end - doorEntity:SetNWBool("ttt2_door_open", true) + doorEntity:SetNW2Bool("ttt2_door_open", true) end --- @@ -685,7 +702,7 @@ if SERVER then return end - doorEntity:SetNWBool("ttt2_door_open", false) + doorEntity:SetNW2Bool("ttt2_door_open", false) end --- @@ -699,7 +716,7 @@ if SERVER then return end - doorEntity:SetNWBool("ttt2_door_open", false) + doorEntity:SetNW2Bool("ttt2_door_open", false) end --- diff --git a/lua/ttt2/libraries/targetid.lua b/lua/ttt2/libraries/targetid.lua index 07101382b..e08cbd364 100644 --- a/lua/ttt2/libraries/targetid.lua +++ b/lua/ttt2/libraries/targetid.lua @@ -69,6 +69,8 @@ function targetid.Initialize() } end +local PLAYER_USE_RADIUS = 80 + --- -- This function handles finding Entities by casting a ray from a point in a direction, filtering out certain entities -- Use this in combination with the hook @GM:TTTModifyTargetedEntity to create your own Remote Camera with TargetIDs. @@ -82,9 +84,8 @@ end -- @realm client function targetid.FindEntityAlongView(pos, dir, filter) local client = LocalPlayer() - local endpos = dir - endpos:Mul(MAX_TRACE_LENGTH) + local endpos = dir * MAX_TRACE_LENGTH endpos:Add(pos) if entspawnscript.IsEditing(client) then @@ -108,15 +109,60 @@ function targetid.FindEntityAlongView(pos, dir, filter) return ent, pos:Distance(ent:GetPos()) end - local trace = util.TraceLine({ + local tracedata = { start = pos, endpos = endpos, - mask = MASK_ALL, filter = filter, - }) + } + + local trace = util.TraceLine(tracedata) + + -- if nothing is hit, check again with a different mask + -- this will hit any solid buttons + if not IsValid(trace.Entity) then + tracedata.mask = bit.bor(MASK_SOLID, CONTENTS_DEBRIS, CONTENTS_PLAYERCLIP) + + trace = util.TraceLine(tracedata) + end -- this is the entity the player is looking at right now local ent = trace.Entity + local distance = trace.StartPos:Distance(trace.HitPos) + + -- if nothing is hit, try to look for non-solid buttons + if not IsValid(ent) then + --local buttons = ents.FindInCone(pos, dir, PLAYER_USE_RADIUS, 0.8) + local buttons = ents.FindInSphere(pos, PLAYER_USE_RADIUS) + + local rayDelta = dir * PLAYER_USE_RADIUS + + for i = 1, #buttons do + local e = buttons[i] + + if e:IsSolid() or not e:IsButton() then + continue + end + + local _, _, frac = util.IntersectRayWithOBB( + pos, + rayDelta, + e:GetPos(), + e:GetAngles(), + e:GetCollisionBounds() + ) + + if not frac then + continue + end + + local dist = frac * PLAYER_USE_RADIUS + + if dist < distance then + distance = dist + ent = e + end + end + end -- if a vehicle, we identify the driver instead if IsValid(ent) and ent:IsVehicle() then @@ -127,7 +173,7 @@ function targetid.FindEntityAlongView(pos, dir, filter) end end - return ent, trace.StartPos:Distance(trace.HitPos) + return ent, distance end --- @@ -621,13 +667,25 @@ function targetid.HUDDrawTargetIDButtons(tData) local client = LocalPlayer() local ent = tData:GetEntity() - if - not IsValid(client) - or not client:IsTerror() - or not IsValid(ent) - or not ent:IsButton() - or tData:GetEntityDistance() > 100 - then + if not IsValid(client) or not client:IsTerror() or not IsValid(ent) then + return + end + + -- button is supposed to be invisible + if ent:GetNoDraw() or ent:GetRenderMode() == RENDERMODE_NONE then + return + end + + -- check if parent is button (for prop models parented to buttons and such) + if not ent:IsButton() then + ent = ent:GetMoveParent() + + if not IsValid(ent) or not ent:IsButton() then + return + end + end + + if tData:GetEntityDistance() > 100 then return end @@ -657,14 +715,25 @@ function targetid.HUDDrawTargetIDDoors(tData) local client = LocalPlayer() local ent = tData:GetEntity() - if - not IsValid(client) - or not client:IsTerror() - or not IsValid(ent) - or not ent:IsDoor() - or not ent:PlayerCanOpenDoor() - or tData:GetEntityDistance() > 90 - then + if not IsValid(client) or not client:IsTerror() or not IsValid(ent) then + return + end + + -- door is supposed to be invisible + if ent:GetNoDraw() or ent:GetRenderMode() == RENDERMODE_NONE then + return + end + + -- check if parent is door (for doors with breakable glass and such) + if not ent:IsDoor() then + ent = ent:GetMoveParent() + + if not IsValid(ent) or not ent:IsDoor() then + return + end + end + + if not ent:PlayerCanOpenDoor() or tData:GetEntityDistance() > 90 then return end