From e91eae928d62825b11704f3ed7541e0c17e87e07 Mon Sep 17 00:00:00 2001 From: LengthenedGradient <109800352+LengthenedGradient@users.noreply.github.com> Date: Tue, 16 Jul 2024 23:47:39 -0400 Subject: [PATCH 01/12] WIP --- lua/acf/core/utilities/util_sv.lua | 113 +++++++-------- lua/entities/acf_base_scalable/init.lua | 2 +- lua/entities/acf_engine/init.lua | 181 ++++++++++++------------ 3 files changed, 148 insertions(+), 148 deletions(-) diff --git a/lua/acf/core/utilities/util_sv.lua b/lua/acf/core/utilities/util_sv.lua index 8b3d09682..03375bdbc 100644 --- a/lua/acf/core/utilities/util_sv.lua +++ b/lua/acf/core/utilities/util_sv.lua @@ -414,83 +414,80 @@ do -- Entity linking } } } + + ClassLink = { + acf_ammo = { + acf_guns = { + + } + }, + + acf_ + } ]]-- - local ClassLink = { Link = {}, Unlink = {} } + + --- @class LinkFunction + --- @field Ent1 table The first entity in the link + --- @field Ent2 table The second entity in the link + --- @field FromChip boolean If the link is from a chip + --- @return boolean? Success Whether the link was successful + --- @return string? Message A message about the link status + + --- @class LinkOptions + --- @field Link LinkFunction The function to handle linking + --- @field Unlink LinkFunction The function to handle unlinking + --- @field Check LinkFunction The function to check the link status + --- @field Delay number The delay associated with the link + + local ClassLink = { } --- Registers a link or unlink between two classes and how to handle them. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) - local function RegisterNewLink(Action, Class1, Class2, Function) - if not isfunction(Function) then return end + --- @param Options LinkOptions The options for handling the link + local function RegisterClassLink(Class1, Class2, Options) + -- Safely initialize tables + if not ClassLink[Class1] then ClassLink[Class1] = {} end + if not ClassLink[Class1][Class2] then ClassLink[Class1][Class2] = {} end - local Target = ClassLink[Action] - local Data1 = Target[Class1] + if not ClassLink[Class2] then ClassLink[Class2] = {} end + if not ClassLink[Class2][Class1] then ClassLink[Class2][Class1] = {} end - if not Data1 then - Target[Class1] = { - [Class2] = function(Ent1, Ent2) - return Function(Ent1, Ent2) - end - } - else - Data1[Class2] = function(Ent1, Ent2) - return Function(Ent1, Ent2) - end + for k,v in pairs(Options) do + ClassLink[Class1][Class2][k] = v end + -- If Class1 == Class2, the operations below are a duplicate of those above. Avoid that. if Class1 == Class2 then return end - local Data2 = Target[Class2] - - if not Data2 then - Target[Class2] = { - [Class1] = function(Ent2, Ent1) - return Function(Ent1, Ent2) - end - } - else - Data2[Class1] = function(Ent2, Ent1) - return Function(Ent1, Ent2) - end + for k,v in pairs(Options) do + ClassLink[Class2][Class1][k] = v end end - --- Registers that two classes can be linked, as well as how to handle entities of their class being linked. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message - function ACF.RegisterClassLink(Class1, Class2, Function) - RegisterNewLink("Link", Class1, Class2, Function) + function ACF.GetClassLinkData(Class1, Class2) + if not ClassLink[Class1] then return end + return ClassLink[Class1][Class2] end - --- Returns the callback defined previously by ACF.RegisterClassLink between Class1 and Class2. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @return fun(Entity1:table, Entity2:table) | nil # The linking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no linking functions - function ACF.GetClassLink(Class1, Class2) - if not ClassLink.Link[Class1] then return end - - return ClassLink.Link[Class1][Class2] - end + -- --- Returns the callback defined previously by ACF.RegisterClassUnlink between Class1 and Class2. + -- --- @param Class1 string The first class in the link + -- --- @param Class2 string The other class in the link + -- --- @return fun(Entity1:table, Entity2:table) | nil # The unlinking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no unlinking functions + -- function ACF.GetClassUnlink(Class1, Class2) + -- if not ClassLink.Unlink[Class1] then return end - --- Registers that two classes can be unlinked, as well as how to handle entities of their class being unlinked. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) The unlinking function defined between an entity of Class1 and an entity of Class2 - function ACF.RegisterClassUnlink(Class1, Class2, Function) - RegisterNewLink("Unlink", Class1, Class2, Function) - end + -- return ClassLink.Unlink[Class1][Class2] + -- end - --- Returns the callback defined previously by ACF.RegisterClassUnlink between Class1 and Class2. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @return fun(Entity1:table, Entity2:table) | nil # The unlinking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no unlinking functions - function ACF.GetClassUnlink(Class1, Class2) - if not ClassLink.Unlink[Class1] then return end + -- --- Returns the callback defined previously by ACF.RegisterClassLink between Class1 and Class2. + -- --- @param Class1 string The first class in the link + -- --- @param Class2 string The other class in the link + -- function ACF.GetClassLink(Class1, Class2) + -- if not ClassLink.Link[Class1] then return end - return ClassLink.Unlink[Class1][Class2] - end + -- return ClassLink.Link[Class1][Class2] + -- end end do -- Entity inputs diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index d8f778074..ac74a8ebf 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -83,7 +83,7 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetClassUnlink(self:GetClass(), Class) + local Function = ACF.GetClassLinkData(self:GetClass(), Class).Link if Function then return Function(self, Target) diff --git a/lua/entities/acf_engine/init.lua b/lua/entities/acf_engine/init.lua index 8835e931c..06110ba9a 100644 --- a/lua/entities/acf_engine/init.lua +++ b/lua/entities/acf_engine/init.lua @@ -10,101 +10,104 @@ local MaxDistance = ACF.LinkDistance * ACF.LinkDistance -- Engine class setup --===============================================================================================-- do - ACF.RegisterClassLink("acf_engine", "acf_fueltank", function(Engine, Target) - if Engine.FuelTanks[Target] then return false, "This engine is already linked to this fuel tank!" end - if Target.Engines[Engine] then return false, "This engine is already linked to this fuel tank!" end - if not Engine.FuelTypes[Target.FuelType] then return false, "Cannot link because fuel type is incompatible." end - if Target.NoLinks then return false, "This fuel tank doesn't allow linking." end - if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This fuel tank is too far away from this engine." end - - Engine.FuelTanks[Target] = true - Target.Engines[Engine] = true - - Engine:UpdateOverlay() - Target:UpdateOverlay() - - return true, "Engine linked successfully!" - end) - - ACF.RegisterClassUnlink("acf_engine", "acf_fueltank", function(Engine, Target) - if Engine.FuelTanks[Target] or Target.Engines[Engine] then - if Engine.FuelTank == Target then - Engine.FuelTank = next(Engine.FuelTanks, Target) + ACF.RegisterClassLink("acf_engine", "acf_fueltank", { + Link = function(Engine, Target) + if Engine.FuelTanks[Target] then return false, "This engine is already linked to this fuel tank!" end + if Target.Engines[Engine] then return false, "This engine is already linked to this fuel tank!" end + if not Engine.FuelTypes[Target.FuelType] then return false, "Cannot link because fuel type is incompatible." end + if Target.NoLinks then return false, "This fuel tank doesn't allow linking." end + if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This fuel tank is too far away from this engine." end + + Engine.FuelTanks[Target] = true + Target.Engines[Engine] = true + + Engine:UpdateOverlay() + Target:UpdateOverlay() + + return true, "Engine linked successfully!" + end, + Unlink = function(Engine, Target) + if Engine.FuelTanks[Target] or Target.Engines[Engine] then + if Engine.FuelTank == Target then + Engine.FuelTank = next(Engine.FuelTanks, Target) + end + + Engine.FuelTanks[Target] = nil + Target.Engines[Engine] = nil + + Engine:UpdateOverlay() + Target:UpdateOverlay() + + return true, "Engine unlinked successfully!" end - - Engine.FuelTanks[Target] = nil + + return false, "This engine is not linked to this fuel tank." + end + }) + + + ACF.RegisterClassLink("acf_engine", "acf_gearbox", { + function(Engine, Target) + if Engine.Gearboxes[Target] then return false, "This engine is already linked to this gearbox." end + if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This gearbox is too far away from this engine!" end + + -- make sure the angle is not excessive + local InPos = Target:LocalToWorld(Target.In) + local OutPos = Engine:LocalToWorld(Engine.Out) + local Direction + + if Engine.IsTrans then + Direction = -Engine:GetRight() + else + Direction = Engine:GetForward() + end + + if (OutPos - InPos):GetNormalized():Dot(Direction) < 0.7 then + return false, "Cannot link due to excessive driveshaft angle!" + end + + local Rope + + if tobool(Engine.Owner:GetInfoNum("ACF_MobilityRopeLinks", 1)) then + Rope = constraint.CreateKeyframeRope(OutPos, 1, "cable/cable2", nil, Engine, Engine.Out, 0, Target, Target.In, 0) + end + + local Link = { + Rope = Rope, + RopeLen = (OutPos - InPos):Length(), + ReqTq = 0 + } + + Engine.Gearboxes[Target] = Link + Target.Engines[Engine] = true + + Engine:UpdateOverlay() + Target:UpdateOverlay() + + Engine:InvalidateClientInfo() + + return true, "Engine linked successfully!" + end, + Unlink = function(Engine, Target) + if not Engine.Gearboxes[Target] then + return false, "This engine is not linked to this gearbox." + end + + local Rope = Engine.Gearboxes[Target].Rope + + if IsValid(Rope) then Rope:Remove() end + + Engine.Gearboxes[Target] = nil Target.Engines[Engine] = nil - + Engine:UpdateOverlay() Target:UpdateOverlay() - + + Engine:InvalidateClientInfo() + return true, "Engine unlinked successfully!" end - - return false, "This engine is not linked to this fuel tank." - end) - - ACF.RegisterClassLink("acf_engine", "acf_gearbox", function(Engine, Target) - if Engine.Gearboxes[Target] then return false, "This engine is already linked to this gearbox." end - if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This gearbox is too far away from this engine!" end - - -- make sure the angle is not excessive - local InPos = Target:LocalToWorld(Target.In) - local OutPos = Engine:LocalToWorld(Engine.Out) - local Direction - - if Engine.IsTrans then - Direction = -Engine:GetRight() - else - Direction = Engine:GetForward() - end - - if (OutPos - InPos):GetNormalized():Dot(Direction) < 0.7 then - return false, "Cannot link due to excessive driveshaft angle!" - end - - local Rope - - if tobool(Engine.Owner:GetInfoNum("ACF_MobilityRopeLinks", 1)) then - Rope = constraint.CreateKeyframeRope(OutPos, 1, "cable/cable2", nil, Engine, Engine.Out, 0, Target, Target.In, 0) - end - - local Link = { - Rope = Rope, - RopeLen = (OutPos - InPos):Length(), - ReqTq = 0 - } - - Engine.Gearboxes[Target] = Link - Target.Engines[Engine] = true - - Engine:UpdateOverlay() - Target:UpdateOverlay() - - Engine:InvalidateClientInfo() - - return true, "Engine linked successfully!" - end) - - ACF.RegisterClassUnlink("acf_engine", "acf_gearbox", function(Engine, Target) - if not Engine.Gearboxes[Target] then - return false, "This engine is not linked to this gearbox." - end - - local Rope = Engine.Gearboxes[Target].Rope - - if IsValid(Rope) then Rope:Remove() end - - Engine.Gearboxes[Target] = nil - Target.Engines[Engine] = nil - - Engine:UpdateOverlay() - Target:UpdateOverlay() - - Engine:InvalidateClientInfo() - - return true, "Engine unlinked successfully!" - end) + }) end --===============================================================================================-- -- Local Funcs and Vars From bdc5c6d52b1cae1b3e8e6926a44e60c89adcae17 Mon Sep 17 00:00:00 2001 From: LengthenedGradient <109800352+LengthenedGradient@users.noreply.github.com> Date: Thu, 18 Jul 2024 16:36:24 -0400 Subject: [PATCH 02/12] Finished --- lua/acf/core/utilities/util_sv.lua | 178 ++++++++++++------ lua/entities/acf_base_scalable/init.lua | 25 ++- lua/entities/acf_base_simple/init.lua | 23 ++- .../core/custom/acffunctions.lua | 2 +- lua/starfall/libs_sh/acffunctions.lua | 2 +- 5 files changed, 155 insertions(+), 75 deletions(-) diff --git a/lua/acf/core/utilities/util_sv.lua b/lua/acf/core/utilities/util_sv.lua index 03375bdbc..4611fc2e1 100644 --- a/lua/acf/core/utilities/util_sv.lua +++ b/lua/acf/core/utilities/util_sv.lua @@ -399,95 +399,151 @@ do -- Entity linking return Result end - --[[ - Example structure of ClassLink: - - ClassLink = { - ["Link"] = { - ["acf_ammo"] = { - ["acf_gun"] = function(Ent1, Ent2) -- Handles linking guns and ammo - } - }, - ["Unlink"] = { - ["acf_ammo"] = { - ["acf_gun"] = function(Ent1, Ent2) -- Handles unlinking guns and ammo - } - } - } - - ClassLink = { - acf_ammo = { - acf_guns = { - - } - }, - - acf_ - } - ]]-- - --- @class LinkFunction --- @field Ent1 table The first entity in the link --- @field Ent2 table The second entity in the link --- @field FromChip boolean If the link is from a chip --- @return boolean? Success Whether the link was successful --- @return string? Message A message about the link status - + + --- Represents information about storing the link on an entity + --- @class LinkStore + --- @field StoreName string Any entity of this class will store references to the other class in this attribute + --- @field Ordered boolean If true, maintains the link as an array rather than a lookup table + --- @class LinkOptions --- @field Link LinkFunction The function to handle linking --- @field Unlink LinkFunction The function to handle unlinking --- @field Check LinkFunction The function to check the link status - --- @field Delay number The delay associated with the link + --- @field ChipDelay number The delay associated with the link if done via chip + --- @field LinkStore1 LinkStore Information about this class stores links to the other class + --- @field LinkStore2 LinkStore Information about how the other class stores links to this class local ClassLink = { } - --- Registers a link or unlink between two classes and how to handle them. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @param Options LinkOptions The options for handling the link - local function RegisterClassLink(Class1, Class2, Options) - -- Safely initialize tables + function ACF.GetLinkDataSafe(Class1, Class2) if not ClassLink[Class1] then ClassLink[Class1] = {} end if not ClassLink[Class1][Class2] then ClassLink[Class1][Class2] = {} end + return ClassLink[Class1][Class2] + end - if not ClassLink[Class2] then ClassLink[Class2] = {} end - if not ClassLink[Class2][Class1] then ClassLink[Class2][Class1] = {} end + --- Adds a link to this entity's list of links of this LinkData + local function AddLink(Ent, LinkEnt, LinkData) + local StoreName, Ordered = LinkData.StoreName, LinkData.Ordered + if not Ent[StoreName] then Ent[StoreName] = {} end + if Ordered then table.insert(Ent[StoreName], LinkEnt) + else Ent[StoreName][LinkEnt] = true end + end - for k,v in pairs(Options) do - ClassLink[Class1][Class2][k] = v - end + --- Removes a link from this entity's list of links of this LinkData + local function RemoveLink(Ent, LinkEnt, LinkData) + local StoreName, Ordered = LinkData.StoreName, LinkData.Ordered + if not Ent[StoreName] then Ent[StoreName] = {} end + if Ordered then table.RemoveByValue(Ent[StoreName], LinkEnt) + else Ent[StoreName][LinkEnt] = nil end + end - -- If Class1 == Class2, the operations below are a duplicate of those above. Avoid that. + --- Registers that two classes can be linked, as well as how to handle entities of their class being linked. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param Function fun(Entity1:table, Entity2:table, FromChip:boolean) The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message + function ACF.RegisterClassLink(Class1, Class2, Function) + local LinkData1 = ACF.GetLinkDataSafe(Class1,Class2) + LinkData1.Link = function(Ent1, Ent2, FromChip) + if LinkData1.LinkStore1 then AddLink(Ent1, Ent2, LinkData1.LinkStore1) end + if LinkData1.LinkStore2 then AddLink(Ent1, Ent2, LinkData1.LinkStore2) end + return Function(Ent1, Ent2, FromChip) + end if Class1 == Class2 then return end + local LinkData2 = ACF.GetLinkDataSafe(Class1,Class2) + LinkData2.Link = function(Ent2, Ent1, FromChip) + if LinkData2.LinkStore1 then AddLink(Ent1, Ent2, LinkData2.LinkStore1) end + if LinkData1.LinkStore2 then AddLink(Ent1, Ent2, LinkData1.LinkStore2) end + return Function(Ent1, Ent2, FromChip) + end + end - for k,v in pairs(Options) do - ClassLink[Class2][Class1][k] = v + --- Registers that two classes can be unlinked, as well as how to handle entities of their class being unlinked. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param Function fun(Entity1:table, Entity2:table, FromChip:boolean) The unlinking function defined between an entity of Class1 and an entity of Class2 + function ACF.RegisterClassUnlink(Class1, Class2, Function) + local LinkData1 = ACF.GetLinkDataSafe(Class1,Class2) + LinkData1.Unlink = function(Ent1, Ent2, FromChip) + if LinkData1.LinkStore1 then RemoveLink(Ent1, Ent2, LinkData1.LinkStore1) end + if LinkData1.LinkStore2 then RemoveLink(Ent1, Ent2, LinkData1.LinkStore2) end + return Function(Ent1, Ent2, FromChip) + end + if Class1 == Class2 then return end + local LinkData2 = ACF.GetLinkDataSafe(Class1,Class2) + LinkData2.Unlink = function(Ent2, Ent1, FromChip) + if LinkData2.LinkStore1 then RemoveLink(Ent1, Ent2, LinkData2.LinkStore1) end + if LinkData1.LinkStore2 then RemoveLink(Ent1, Ent2, LinkData1.LinkStore2) end + return Function(Ent1, Ent2, FromChip) end end - function ACF.GetClassLinkData(Class1, Class2) - if not ClassLink[Class1] then return end - return ClassLink[Class1][Class2] + --- Registers a validation check between two classes. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param Function fun(Entity1:table, Entity2:table, FromChip:boolean) The checking function defined between an entity of Class1 and an entity of Class2 + function ACF.RegisterClassCheck(Class1, Class2, Function) + ACF.GetLinkDataSafe(Class1,Class2).Check = function(Ent1, Ent2, FromChip) return Function(Ent1, Ent2, FromChip) end + if Class1 == Class2 then return end + ACF.GetLinkDataSafe(Class2,Class1).Check = function(Ent2, Ent1, FromChip) return Function(Ent1, Ent2, FromChip) end end - -- --- Returns the callback defined previously by ACF.RegisterClassUnlink between Class1 and Class2. - -- --- @param Class1 string The first class in the link - -- --- @param Class2 string The other class in the link - -- --- @return fun(Entity1:table, Entity2:table) | nil # The unlinking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no unlinking functions - -- function ACF.GetClassUnlink(Class1, Class2) - -- if not ClassLink.Unlink[Class1] then return end + --- Registers a chip delay between two classes. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param ChipDelay number If the link happens from the chip, then delay it by this amount + function ACF.RegisterClassDelay(Class1, Class2, ChipDelay) + ACF.GetLinkDataSafe(Class1,Class2).ChipDelay = ChipDelay + if Class1 == Class2 then return end + ACF.GetLinkDataSafe(Class2,Class1).ChipDelay = ChipDelay + end - -- return ClassLink.Unlink[Class1][Class2] - -- end + function ACF.RegisterClassStore1(Class1, Class2, LinkStore) + ACF.GetLinkDataSafe(Class1,Class2).LinkStore1 = LinkStore + if Class1 == Class2 then return end + ACF.GetLinkDataSafe(Class2,Class1).LinkStore2 = LinkStore + end - -- --- Returns the callback defined previously by ACF.RegisterClassLink between Class1 and Class2. - -- --- @param Class1 string The first class in the link - -- --- @param Class2 string The other class in the link - -- function ACF.GetClassLink(Class1, Class2) - -- if not ClassLink.Link[Class1] then return end + function ACF.RegisterClassStore2(Class1, Class2, LinkStore) + ACF.GetLinkDataSafe(Class1,Class2).LinkStore2 = LinkStore + if Class1 == Class2 then return end + ACF.GetLinkDataSafe(Class2,Class1).LinkStore1 = LinkStore + end + + --- Saves a link in an entity's AD2 entity modifiers + function ACF.AD2SaveLinks(Entity, StoreName, SaveName) + local tbl = {} + + if table.IsSequential(Entity[StoreName]) then + for _,ent in ipairs(Entity[StoreName]) do table.insert(tbl,ent:EntIndex()) end + else + for ent,_ in pairs(Entity[StoreName]) do table.insert(tbl,ent:EntIndex()) end + end + + duplicator.StoreEntityModifier(Entity, SaveName, tbl) + end - -- return ClassLink.Link[Class1][Class2] - -- end + --- Loads a link from an entity's AD2 entity modifiers + function ACF.AD2LoadLinks(Entity, SaveName, EntMods, CreatedEntities) + if EntMods[SaveName] then + for _, id in ipairs(EntMods[SaveName]) do Entity:Link(CreatedEntities[id]) end + EntMods[SaveName] = nil + end + end + + --- Unlinks every entity from this entity of this link type + function ACF.UnlinkAll(Entity, StoreName) + if table.IsSequential(Entity[StoreName]) then + for _, ent in ipairs(Entity[StoreName]) do Entity:Unlink(ent) end + else + for ent, _ in pairs(Entity[StoreName]) do Entity:Unlink(ent) end + end + end end do -- Entity inputs diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index ac74a8ebf..1d874f75a 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -61,18 +61,31 @@ end --------------------------------------------- do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." + local timer = timer - function ENT:Link(Target) + function ENT:Link(Target, FromChip) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end if self == Target then return false, "Can't link an entity to itself." end local Class = Target:GetClass() - local Function = ACF.GetClassLink(self:GetClass(), Class) + local LinkData = ACF.GetLinkDataSafe(self:GetClass(), Class) + local Function = LinkData.Link + local Check = LinkData.Check + local ChipDelay = LinkData.ChipDelay if Function then + if Check then + local result, message = Check(self, Target) + if result then + if FromChip and ChipDelay then + timer.Simple(ChipDelay,function() + if Check(self, Target) then Function(self, Target) end + end) + else Function(self,Target) end + end + return result, message + end return Function(self, Target) - elseif self.DefaultLink then - return self:DefaultLink(Target) end return false, LinkText:format(self.PluralName, Target.PluralName or Class) @@ -83,12 +96,10 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetClassLinkData(self:GetClass(), Class).Link + local Function = ACF.GetLinkDataSafe(self:GetClass(), Class).Unlink if Function then return Function(self, Target) - elseif self.DefaultUnlink then - return self:DefaultUnlink(Target) end return false, UnlinkText:format(self.PluralName, Target.PluralName or Class) diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index d8f778074..fbc66ec67 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -61,18 +61,31 @@ end --------------------------------------------- do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." + local timer = timer - function ENT:Link(Target) + function ENT:Link(Target, FromChip) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end if self == Target then return false, "Can't link an entity to itself." end local Class = Target:GetClass() - local Function = ACF.GetClassLink(self:GetClass(), Class) + local LinkData = ACF.GetLinkDataSafe(self:GetClass(), Class) + local Function = LinkData.Link + local Check = LinkData.Check + local ChipDelay = LinkData.ChipDelay if Function then + if Check then + local result, message = Check(self, Target) + if result then + if FromChip and ChipDelay then + timer.Simple(ChipDelay,function() + if Check(self, Target) then Function(self, Target) end + end) + else Function(self,Target) end + end + return result, message + end return Function(self, Target) - elseif self.DefaultLink then - return self:DefaultLink(Target) end return false, LinkText:format(self.PluralName, Target.PluralName or Class) @@ -83,7 +96,7 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetClassUnlink(self:GetClass(), Class) + local Function = ACF.GetLinkDataSafe(self:GetClass(), Class).Unlink if Function then return Function(self, Target) diff --git a/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua b/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua index 5534336d3..c517d1e60 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua @@ -348,7 +348,7 @@ e2function number entity:acfLinkTo(entity Target, number Notify) return 0 end - local Sucess, Message = this:Link(Target) + local Sucess, Message = this:Link(Target, true) if Notify ~= 0 then ACF.SendNotify(self.player, Sucess, Message) diff --git a/lua/starfall/libs_sh/acffunctions.lua b/lua/starfall/libs_sh/acffunctions.lua index a6560b0b7..cb83043a7 100644 --- a/lua/starfall/libs_sh/acffunctions.lua +++ b/lua/starfall/libs_sh/acffunctions.lua @@ -1105,7 +1105,7 @@ if SERVER then CheckPerms(instance, This, "entities.acf") CheckPerms(instance, Target, "entities.acf") - local Success, Message = This:Link(Target) + local Success, Message = This:Link(Target, true) if notify then ACF.SendNotify(instance.player, Success, Message) From ef731d17350c60975fc73ee7ac6e587327ae0f5c Mon Sep 17 00:00:00 2001 From: LengthenedGradient <109800352+LengthenedGradient@users.noreply.github.com> Date: Thu, 18 Jul 2024 17:14:17 -0400 Subject: [PATCH 03/12] Silence Linter --- lua/entities/acf_engine/init.lua | 46 ++++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lua/entities/acf_engine/init.lua b/lua/entities/acf_engine/init.lua index 06110ba9a..004652463 100644 --- a/lua/entities/acf_engine/init.lua +++ b/lua/entities/acf_engine/init.lua @@ -17,13 +17,13 @@ do if not Engine.FuelTypes[Target.FuelType] then return false, "Cannot link because fuel type is incompatible." end if Target.NoLinks then return false, "This fuel tank doesn't allow linking." end if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This fuel tank is too far away from this engine." end - + Engine.FuelTanks[Target] = true Target.Engines[Engine] = true - + Engine:UpdateOverlay() Target:UpdateOverlay() - + return true, "Engine linked successfully!" end, Unlink = function(Engine, Target) @@ -31,16 +31,16 @@ do if Engine.FuelTank == Target then Engine.FuelTank = next(Engine.FuelTanks, Target) end - + Engine.FuelTanks[Target] = nil Target.Engines[Engine] = nil - + Engine:UpdateOverlay() Target:UpdateOverlay() - + return true, "Engine unlinked successfully!" end - + return false, "This engine is not linked to this fuel tank." end }) @@ -50,61 +50,61 @@ do function(Engine, Target) if Engine.Gearboxes[Target] then return false, "This engine is already linked to this gearbox." end if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This gearbox is too far away from this engine!" end - + -- make sure the angle is not excessive local InPos = Target:LocalToWorld(Target.In) local OutPos = Engine:LocalToWorld(Engine.Out) local Direction - + if Engine.IsTrans then Direction = -Engine:GetRight() else Direction = Engine:GetForward() end - + if (OutPos - InPos):GetNormalized():Dot(Direction) < 0.7 then return false, "Cannot link due to excessive driveshaft angle!" end - + local Rope - + if tobool(Engine.Owner:GetInfoNum("ACF_MobilityRopeLinks", 1)) then Rope = constraint.CreateKeyframeRope(OutPos, 1, "cable/cable2", nil, Engine, Engine.Out, 0, Target, Target.In, 0) end - + local Link = { Rope = Rope, RopeLen = (OutPos - InPos):Length(), ReqTq = 0 } - + Engine.Gearboxes[Target] = Link Target.Engines[Engine] = true - + Engine:UpdateOverlay() Target:UpdateOverlay() - + Engine:InvalidateClientInfo() - + return true, "Engine linked successfully!" end, Unlink = function(Engine, Target) if not Engine.Gearboxes[Target] then return false, "This engine is not linked to this gearbox." end - + local Rope = Engine.Gearboxes[Target].Rope - + if IsValid(Rope) then Rope:Remove() end - + Engine.Gearboxes[Target] = nil Target.Engines[Engine] = nil - + Engine:UpdateOverlay() Target:UpdateOverlay() - + Engine:InvalidateClientInfo() - + return true, "Engine unlinked successfully!" end }) From de253ca2e3f782204cac5ca072d05de1bff11565 Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Wed, 28 Aug 2024 15:38:12 -0400 Subject: [PATCH 04/12] Revert "Silence Linter" This reverts commit ef731d17350c60975fc73ee7ac6e587327ae0f5c. --- lua/entities/acf_engine/init.lua | 46 ++++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lua/entities/acf_engine/init.lua b/lua/entities/acf_engine/init.lua index 004652463..06110ba9a 100644 --- a/lua/entities/acf_engine/init.lua +++ b/lua/entities/acf_engine/init.lua @@ -17,13 +17,13 @@ do if not Engine.FuelTypes[Target.FuelType] then return false, "Cannot link because fuel type is incompatible." end if Target.NoLinks then return false, "This fuel tank doesn't allow linking." end if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This fuel tank is too far away from this engine." end - + Engine.FuelTanks[Target] = true Target.Engines[Engine] = true - + Engine:UpdateOverlay() Target:UpdateOverlay() - + return true, "Engine linked successfully!" end, Unlink = function(Engine, Target) @@ -31,16 +31,16 @@ do if Engine.FuelTank == Target then Engine.FuelTank = next(Engine.FuelTanks, Target) end - + Engine.FuelTanks[Target] = nil Target.Engines[Engine] = nil - + Engine:UpdateOverlay() Target:UpdateOverlay() - + return true, "Engine unlinked successfully!" end - + return false, "This engine is not linked to this fuel tank." end }) @@ -50,61 +50,61 @@ do function(Engine, Target) if Engine.Gearboxes[Target] then return false, "This engine is already linked to this gearbox." end if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This gearbox is too far away from this engine!" end - + -- make sure the angle is not excessive local InPos = Target:LocalToWorld(Target.In) local OutPos = Engine:LocalToWorld(Engine.Out) local Direction - + if Engine.IsTrans then Direction = -Engine:GetRight() else Direction = Engine:GetForward() end - + if (OutPos - InPos):GetNormalized():Dot(Direction) < 0.7 then return false, "Cannot link due to excessive driveshaft angle!" end - + local Rope - + if tobool(Engine.Owner:GetInfoNum("ACF_MobilityRopeLinks", 1)) then Rope = constraint.CreateKeyframeRope(OutPos, 1, "cable/cable2", nil, Engine, Engine.Out, 0, Target, Target.In, 0) end - + local Link = { Rope = Rope, RopeLen = (OutPos - InPos):Length(), ReqTq = 0 } - + Engine.Gearboxes[Target] = Link Target.Engines[Engine] = true - + Engine:UpdateOverlay() Target:UpdateOverlay() - + Engine:InvalidateClientInfo() - + return true, "Engine linked successfully!" end, Unlink = function(Engine, Target) if not Engine.Gearboxes[Target] then return false, "This engine is not linked to this gearbox." end - + local Rope = Engine.Gearboxes[Target].Rope - + if IsValid(Rope) then Rope:Remove() end - + Engine.Gearboxes[Target] = nil Target.Engines[Engine] = nil - + Engine:UpdateOverlay() Target:UpdateOverlay() - + Engine:InvalidateClientInfo() - + return true, "Engine unlinked successfully!" end }) From ce719e81fdfea6ec73370d216e0f34bee57490ec Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Wed, 28 Aug 2024 15:38:27 -0400 Subject: [PATCH 05/12] Revert "Finished" This reverts commit bdc5c6d52b1cae1b3e8e6926a44e60c89adcae17. --- lua/acf/core/utilities/util_sv.lua | 178 ++++++------------ lua/entities/acf_base_scalable/init.lua | 25 +-- lua/entities/acf_base_simple/init.lua | 23 +-- .../core/custom/acffunctions.lua | 2 +- lua/starfall/libs_sh/acffunctions.lua | 2 +- 5 files changed, 75 insertions(+), 155 deletions(-) diff --git a/lua/acf/core/utilities/util_sv.lua b/lua/acf/core/utilities/util_sv.lua index 4611fc2e1..03375bdbc 100644 --- a/lua/acf/core/utilities/util_sv.lua +++ b/lua/acf/core/utilities/util_sv.lua @@ -399,151 +399,95 @@ do -- Entity linking return Result end + --[[ + Example structure of ClassLink: + + ClassLink = { + ["Link"] = { + ["acf_ammo"] = { + ["acf_gun"] = function(Ent1, Ent2) -- Handles linking guns and ammo + } + }, + ["Unlink"] = { + ["acf_ammo"] = { + ["acf_gun"] = function(Ent1, Ent2) -- Handles unlinking guns and ammo + } + } + } + + ClassLink = { + acf_ammo = { + acf_guns = { + + } + }, + + acf_ + } + ]]-- + --- @class LinkFunction --- @field Ent1 table The first entity in the link --- @field Ent2 table The second entity in the link --- @field FromChip boolean If the link is from a chip --- @return boolean? Success Whether the link was successful --- @return string? Message A message about the link status - - --- Represents information about storing the link on an entity - --- @class LinkStore - --- @field StoreName string Any entity of this class will store references to the other class in this attribute - --- @field Ordered boolean If true, maintains the link as an array rather than a lookup table - + --- @class LinkOptions --- @field Link LinkFunction The function to handle linking --- @field Unlink LinkFunction The function to handle unlinking --- @field Check LinkFunction The function to check the link status - --- @field ChipDelay number The delay associated with the link if done via chip - --- @field LinkStore1 LinkStore Information about this class stores links to the other class - --- @field LinkStore2 LinkStore Information about how the other class stores links to this class + --- @field Delay number The delay associated with the link local ClassLink = { } - function ACF.GetLinkDataSafe(Class1, Class2) + --- Registers a link or unlink between two classes and how to handle them. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param Options LinkOptions The options for handling the link + local function RegisterClassLink(Class1, Class2, Options) + -- Safely initialize tables if not ClassLink[Class1] then ClassLink[Class1] = {} end if not ClassLink[Class1][Class2] then ClassLink[Class1][Class2] = {} end - return ClassLink[Class1][Class2] - end - - --- Adds a link to this entity's list of links of this LinkData - local function AddLink(Ent, LinkEnt, LinkData) - local StoreName, Ordered = LinkData.StoreName, LinkData.Ordered - if not Ent[StoreName] then Ent[StoreName] = {} end - if Ordered then table.insert(Ent[StoreName], LinkEnt) - else Ent[StoreName][LinkEnt] = true end - end - --- Removes a link from this entity's list of links of this LinkData - local function RemoveLink(Ent, LinkEnt, LinkData) - local StoreName, Ordered = LinkData.StoreName, LinkData.Ordered - if not Ent[StoreName] then Ent[StoreName] = {} end - if Ordered then table.RemoveByValue(Ent[StoreName], LinkEnt) - else Ent[StoreName][LinkEnt] = nil end - end + if not ClassLink[Class2] then ClassLink[Class2] = {} end + if not ClassLink[Class2][Class1] then ClassLink[Class2][Class1] = {} end - --- Registers that two classes can be linked, as well as how to handle entities of their class being linked. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table, FromChip:boolean) The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message - function ACF.RegisterClassLink(Class1, Class2, Function) - local LinkData1 = ACF.GetLinkDataSafe(Class1,Class2) - LinkData1.Link = function(Ent1, Ent2, FromChip) - if LinkData1.LinkStore1 then AddLink(Ent1, Ent2, LinkData1.LinkStore1) end - if LinkData1.LinkStore2 then AddLink(Ent1, Ent2, LinkData1.LinkStore2) end - return Function(Ent1, Ent2, FromChip) + for k,v in pairs(Options) do + ClassLink[Class1][Class2][k] = v end - if Class1 == Class2 then return end - local LinkData2 = ACF.GetLinkDataSafe(Class1,Class2) - LinkData2.Link = function(Ent2, Ent1, FromChip) - if LinkData2.LinkStore1 then AddLink(Ent1, Ent2, LinkData2.LinkStore1) end - if LinkData1.LinkStore2 then AddLink(Ent1, Ent2, LinkData1.LinkStore2) end - return Function(Ent1, Ent2, FromChip) - end - end - --- Registers that two classes can be unlinked, as well as how to handle entities of their class being unlinked. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table, FromChip:boolean) The unlinking function defined between an entity of Class1 and an entity of Class2 - function ACF.RegisterClassUnlink(Class1, Class2, Function) - local LinkData1 = ACF.GetLinkDataSafe(Class1,Class2) - LinkData1.Unlink = function(Ent1, Ent2, FromChip) - if LinkData1.LinkStore1 then RemoveLink(Ent1, Ent2, LinkData1.LinkStore1) end - if LinkData1.LinkStore2 then RemoveLink(Ent1, Ent2, LinkData1.LinkStore2) end - return Function(Ent1, Ent2, FromChip) - end - if Class1 == Class2 then return end - local LinkData2 = ACF.GetLinkDataSafe(Class1,Class2) - LinkData2.Unlink = function(Ent2, Ent1, FromChip) - if LinkData2.LinkStore1 then RemoveLink(Ent1, Ent2, LinkData2.LinkStore1) end - if LinkData1.LinkStore2 then RemoveLink(Ent1, Ent2, LinkData1.LinkStore2) end - return Function(Ent1, Ent2, FromChip) - end - end - - --- Registers a validation check between two classes. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table, FromChip:boolean) The checking function defined between an entity of Class1 and an entity of Class2 - function ACF.RegisterClassCheck(Class1, Class2, Function) - ACF.GetLinkDataSafe(Class1,Class2).Check = function(Ent1, Ent2, FromChip) return Function(Ent1, Ent2, FromChip) end + -- If Class1 == Class2, the operations below are a duplicate of those above. Avoid that. if Class1 == Class2 then return end - ACF.GetLinkDataSafe(Class2,Class1).Check = function(Ent2, Ent1, FromChip) return Function(Ent1, Ent2, FromChip) end - end - - --- Registers a chip delay between two classes. - --- @param Class1 string The first class in the link - --- @param Class2 string The other class in the link - --- @param ChipDelay number If the link happens from the chip, then delay it by this amount - function ACF.RegisterClassDelay(Class1, Class2, ChipDelay) - ACF.GetLinkDataSafe(Class1,Class2).ChipDelay = ChipDelay - if Class1 == Class2 then return end - ACF.GetLinkDataSafe(Class2,Class1).ChipDelay = ChipDelay - end - function ACF.RegisterClassStore1(Class1, Class2, LinkStore) - ACF.GetLinkDataSafe(Class1,Class2).LinkStore1 = LinkStore - if Class1 == Class2 then return end - ACF.GetLinkDataSafe(Class2,Class1).LinkStore2 = LinkStore + for k,v in pairs(Options) do + ClassLink[Class2][Class1][k] = v + end end - function ACF.RegisterClassStore2(Class1, Class2, LinkStore) - ACF.GetLinkDataSafe(Class1,Class2).LinkStore2 = LinkStore - if Class1 == Class2 then return end - ACF.GetLinkDataSafe(Class2,Class1).LinkStore1 = LinkStore + function ACF.GetClassLinkData(Class1, Class2) + if not ClassLink[Class1] then return end + return ClassLink[Class1][Class2] end - --- Saves a link in an entity's AD2 entity modifiers - function ACF.AD2SaveLinks(Entity, StoreName, SaveName) - local tbl = {} + -- --- Returns the callback defined previously by ACF.RegisterClassUnlink between Class1 and Class2. + -- --- @param Class1 string The first class in the link + -- --- @param Class2 string The other class in the link + -- --- @return fun(Entity1:table, Entity2:table) | nil # The unlinking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no unlinking functions + -- function ACF.GetClassUnlink(Class1, Class2) + -- if not ClassLink.Unlink[Class1] then return end - if table.IsSequential(Entity[StoreName]) then - for _,ent in ipairs(Entity[StoreName]) do table.insert(tbl,ent:EntIndex()) end - else - for ent,_ in pairs(Entity[StoreName]) do table.insert(tbl,ent:EntIndex()) end - end + -- return ClassLink.Unlink[Class1][Class2] + -- end - duplicator.StoreEntityModifier(Entity, SaveName, tbl) - end + -- --- Returns the callback defined previously by ACF.RegisterClassLink between Class1 and Class2. + -- --- @param Class1 string The first class in the link + -- --- @param Class2 string The other class in the link + -- function ACF.GetClassLink(Class1, Class2) + -- if not ClassLink.Link[Class1] then return end - --- Loads a link from an entity's AD2 entity modifiers - function ACF.AD2LoadLinks(Entity, SaveName, EntMods, CreatedEntities) - if EntMods[SaveName] then - for _, id in ipairs(EntMods[SaveName]) do Entity:Link(CreatedEntities[id]) end - EntMods[SaveName] = nil - end - end - - --- Unlinks every entity from this entity of this link type - function ACF.UnlinkAll(Entity, StoreName) - if table.IsSequential(Entity[StoreName]) then - for _, ent in ipairs(Entity[StoreName]) do Entity:Unlink(ent) end - else - for ent, _ in pairs(Entity[StoreName]) do Entity:Unlink(ent) end - end - end + -- return ClassLink.Link[Class1][Class2] + -- end end do -- Entity inputs diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index 1d874f75a..ac74a8ebf 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -61,31 +61,18 @@ end --------------------------------------------- do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." - local timer = timer - function ENT:Link(Target, FromChip) + function ENT:Link(Target) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end if self == Target then return false, "Can't link an entity to itself." end local Class = Target:GetClass() - local LinkData = ACF.GetLinkDataSafe(self:GetClass(), Class) - local Function = LinkData.Link - local Check = LinkData.Check - local ChipDelay = LinkData.ChipDelay + local Function = ACF.GetClassLink(self:GetClass(), Class) if Function then - if Check then - local result, message = Check(self, Target) - if result then - if FromChip and ChipDelay then - timer.Simple(ChipDelay,function() - if Check(self, Target) then Function(self, Target) end - end) - else Function(self,Target) end - end - return result, message - end return Function(self, Target) + elseif self.DefaultLink then + return self:DefaultLink(Target) end return false, LinkText:format(self.PluralName, Target.PluralName or Class) @@ -96,10 +83,12 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetLinkDataSafe(self:GetClass(), Class).Unlink + local Function = ACF.GetClassLinkData(self:GetClass(), Class).Link if Function then return Function(self, Target) + elseif self.DefaultUnlink then + return self:DefaultUnlink(Target) end return false, UnlinkText:format(self.PluralName, Target.PluralName or Class) diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index fbc66ec67..d8f778074 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -61,31 +61,18 @@ end --------------------------------------------- do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." - local timer = timer - function ENT:Link(Target, FromChip) + function ENT:Link(Target) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end if self == Target then return false, "Can't link an entity to itself." end local Class = Target:GetClass() - local LinkData = ACF.GetLinkDataSafe(self:GetClass(), Class) - local Function = LinkData.Link - local Check = LinkData.Check - local ChipDelay = LinkData.ChipDelay + local Function = ACF.GetClassLink(self:GetClass(), Class) if Function then - if Check then - local result, message = Check(self, Target) - if result then - if FromChip and ChipDelay then - timer.Simple(ChipDelay,function() - if Check(self, Target) then Function(self, Target) end - end) - else Function(self,Target) end - end - return result, message - end return Function(self, Target) + elseif self.DefaultLink then + return self:DefaultLink(Target) end return false, LinkText:format(self.PluralName, Target.PluralName or Class) @@ -96,7 +83,7 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetLinkDataSafe(self:GetClass(), Class).Unlink + local Function = ACF.GetClassUnlink(self:GetClass(), Class) if Function then return Function(self, Target) diff --git a/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua b/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua index c517d1e60..5534336d3 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua @@ -348,7 +348,7 @@ e2function number entity:acfLinkTo(entity Target, number Notify) return 0 end - local Sucess, Message = this:Link(Target, true) + local Sucess, Message = this:Link(Target) if Notify ~= 0 then ACF.SendNotify(self.player, Sucess, Message) diff --git a/lua/starfall/libs_sh/acffunctions.lua b/lua/starfall/libs_sh/acffunctions.lua index cb83043a7..a6560b0b7 100644 --- a/lua/starfall/libs_sh/acffunctions.lua +++ b/lua/starfall/libs_sh/acffunctions.lua @@ -1105,7 +1105,7 @@ if SERVER then CheckPerms(instance, This, "entities.acf") CheckPerms(instance, Target, "entities.acf") - local Success, Message = This:Link(Target, true) + local Success, Message = This:Link(Target) if notify then ACF.SendNotify(instance.player, Success, Message) From ac3b9e9fb0ed8dc6d490f5f26f49290a911b130c Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Wed, 28 Aug 2024 15:38:31 -0400 Subject: [PATCH 06/12] Revert "WIP" This reverts commit e91eae928d62825b11704f3ed7541e0c17e87e07. --- lua/acf/core/utilities/util_sv.lua | 113 ++++++++------- lua/entities/acf_base_scalable/init.lua | 2 +- lua/entities/acf_engine/init.lua | 181 ++++++++++++------------ 3 files changed, 148 insertions(+), 148 deletions(-) diff --git a/lua/acf/core/utilities/util_sv.lua b/lua/acf/core/utilities/util_sv.lua index 03375bdbc..8b3d09682 100644 --- a/lua/acf/core/utilities/util_sv.lua +++ b/lua/acf/core/utilities/util_sv.lua @@ -414,80 +414,83 @@ do -- Entity linking } } } - - ClassLink = { - acf_ammo = { - acf_guns = { - - } - }, - - acf_ - } ]]-- - - --- @class LinkFunction - --- @field Ent1 table The first entity in the link - --- @field Ent2 table The second entity in the link - --- @field FromChip boolean If the link is from a chip - --- @return boolean? Success Whether the link was successful - --- @return string? Message A message about the link status - - --- @class LinkOptions - --- @field Link LinkFunction The function to handle linking - --- @field Unlink LinkFunction The function to handle unlinking - --- @field Check LinkFunction The function to check the link status - --- @field Delay number The delay associated with the link - - local ClassLink = { } + local ClassLink = { Link = {}, Unlink = {} } --- Registers a link or unlink between two classes and how to handle them. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @param Options LinkOptions The options for handling the link - local function RegisterClassLink(Class1, Class2, Options) - -- Safely initialize tables - if not ClassLink[Class1] then ClassLink[Class1] = {} end - if not ClassLink[Class1][Class2] then ClassLink[Class1][Class2] = {} end + --- @param Function fun(Entity1:table, Entity2:table) + local function RegisterNewLink(Action, Class1, Class2, Function) + if not isfunction(Function) then return end - if not ClassLink[Class2] then ClassLink[Class2] = {} end - if not ClassLink[Class2][Class1] then ClassLink[Class2][Class1] = {} end + local Target = ClassLink[Action] + local Data1 = Target[Class1] - for k,v in pairs(Options) do - ClassLink[Class1][Class2][k] = v + if not Data1 then + Target[Class1] = { + [Class2] = function(Ent1, Ent2) + return Function(Ent1, Ent2) + end + } + else + Data1[Class2] = function(Ent1, Ent2) + return Function(Ent1, Ent2) + end end - -- If Class1 == Class2, the operations below are a duplicate of those above. Avoid that. if Class1 == Class2 then return end - for k,v in pairs(Options) do - ClassLink[Class2][Class1][k] = v + local Data2 = Target[Class2] + + if not Data2 then + Target[Class2] = { + [Class1] = function(Ent2, Ent1) + return Function(Ent1, Ent2) + end + } + else + Data2[Class1] = function(Ent2, Ent1) + return Function(Ent1, Ent2) + end end end - function ACF.GetClassLinkData(Class1, Class2) - if not ClassLink[Class1] then return end - return ClassLink[Class1][Class2] + --- Registers that two classes can be linked, as well as how to handle entities of their class being linked. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param Function fun(Entity1:table, Entity2:table) The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message + function ACF.RegisterClassLink(Class1, Class2, Function) + RegisterNewLink("Link", Class1, Class2, Function) end - -- --- Returns the callback defined previously by ACF.RegisterClassUnlink between Class1 and Class2. - -- --- @param Class1 string The first class in the link - -- --- @param Class2 string The other class in the link - -- --- @return fun(Entity1:table, Entity2:table) | nil # The unlinking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no unlinking functions - -- function ACF.GetClassUnlink(Class1, Class2) - -- if not ClassLink.Unlink[Class1] then return end + --- Returns the callback defined previously by ACF.RegisterClassLink between Class1 and Class2. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @return fun(Entity1:table, Entity2:table) | nil # The linking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no linking functions + function ACF.GetClassLink(Class1, Class2) + if not ClassLink.Link[Class1] then return end + + return ClassLink.Link[Class1][Class2] + end - -- return ClassLink.Unlink[Class1][Class2] - -- end + --- Registers that two classes can be unlinked, as well as how to handle entities of their class being unlinked. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param Function fun(Entity1:table, Entity2:table) The unlinking function defined between an entity of Class1 and an entity of Class2 + function ACF.RegisterClassUnlink(Class1, Class2, Function) + RegisterNewLink("Unlink", Class1, Class2, Function) + end - -- --- Returns the callback defined previously by ACF.RegisterClassLink between Class1 and Class2. - -- --- @param Class1 string The first class in the link - -- --- @param Class2 string The other class in the link - -- function ACF.GetClassLink(Class1, Class2) - -- if not ClassLink.Link[Class1] then return end + --- Returns the callback defined previously by ACF.RegisterClassUnlink between Class1 and Class2. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @return fun(Entity1:table, Entity2:table) | nil # The unlinking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no unlinking functions + function ACF.GetClassUnlink(Class1, Class2) + if not ClassLink.Unlink[Class1] then return end - -- return ClassLink.Link[Class1][Class2] - -- end + return ClassLink.Unlink[Class1][Class2] + end end do -- Entity inputs diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index ac74a8ebf..d8f778074 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -83,7 +83,7 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetClassLinkData(self:GetClass(), Class).Link + local Function = ACF.GetClassUnlink(self:GetClass(), Class) if Function then return Function(self, Target) diff --git a/lua/entities/acf_engine/init.lua b/lua/entities/acf_engine/init.lua index 06110ba9a..8835e931c 100644 --- a/lua/entities/acf_engine/init.lua +++ b/lua/entities/acf_engine/init.lua @@ -10,104 +10,101 @@ local MaxDistance = ACF.LinkDistance * ACF.LinkDistance -- Engine class setup --===============================================================================================-- do - ACF.RegisterClassLink("acf_engine", "acf_fueltank", { - Link = function(Engine, Target) - if Engine.FuelTanks[Target] then return false, "This engine is already linked to this fuel tank!" end - if Target.Engines[Engine] then return false, "This engine is already linked to this fuel tank!" end - if not Engine.FuelTypes[Target.FuelType] then return false, "Cannot link because fuel type is incompatible." end - if Target.NoLinks then return false, "This fuel tank doesn't allow linking." end - if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This fuel tank is too far away from this engine." end - - Engine.FuelTanks[Target] = true - Target.Engines[Engine] = true - - Engine:UpdateOverlay() - Target:UpdateOverlay() - - return true, "Engine linked successfully!" - end, - Unlink = function(Engine, Target) - if Engine.FuelTanks[Target] or Target.Engines[Engine] then - if Engine.FuelTank == Target then - Engine.FuelTank = next(Engine.FuelTanks, Target) - end - - Engine.FuelTanks[Target] = nil - Target.Engines[Engine] = nil - - Engine:UpdateOverlay() - Target:UpdateOverlay() - - return true, "Engine unlinked successfully!" - end - - return false, "This engine is not linked to this fuel tank." - end - }) - - - ACF.RegisterClassLink("acf_engine", "acf_gearbox", { - function(Engine, Target) - if Engine.Gearboxes[Target] then return false, "This engine is already linked to this gearbox." end - if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This gearbox is too far away from this engine!" end - - -- make sure the angle is not excessive - local InPos = Target:LocalToWorld(Target.In) - local OutPos = Engine:LocalToWorld(Engine.Out) - local Direction - - if Engine.IsTrans then - Direction = -Engine:GetRight() - else - Direction = Engine:GetForward() - end - - if (OutPos - InPos):GetNormalized():Dot(Direction) < 0.7 then - return false, "Cannot link due to excessive driveshaft angle!" - end - - local Rope - - if tobool(Engine.Owner:GetInfoNum("ACF_MobilityRopeLinks", 1)) then - Rope = constraint.CreateKeyframeRope(OutPos, 1, "cable/cable2", nil, Engine, Engine.Out, 0, Target, Target.In, 0) - end - - local Link = { - Rope = Rope, - RopeLen = (OutPos - InPos):Length(), - ReqTq = 0 - } - - Engine.Gearboxes[Target] = Link - Target.Engines[Engine] = true - - Engine:UpdateOverlay() - Target:UpdateOverlay() - - Engine:InvalidateClientInfo() - - return true, "Engine linked successfully!" - end, - Unlink = function(Engine, Target) - if not Engine.Gearboxes[Target] then - return false, "This engine is not linked to this gearbox." + ACF.RegisterClassLink("acf_engine", "acf_fueltank", function(Engine, Target) + if Engine.FuelTanks[Target] then return false, "This engine is already linked to this fuel tank!" end + if Target.Engines[Engine] then return false, "This engine is already linked to this fuel tank!" end + if not Engine.FuelTypes[Target.FuelType] then return false, "Cannot link because fuel type is incompatible." end + if Target.NoLinks then return false, "This fuel tank doesn't allow linking." end + if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This fuel tank is too far away from this engine." end + + Engine.FuelTanks[Target] = true + Target.Engines[Engine] = true + + Engine:UpdateOverlay() + Target:UpdateOverlay() + + return true, "Engine linked successfully!" + end) + + ACF.RegisterClassUnlink("acf_engine", "acf_fueltank", function(Engine, Target) + if Engine.FuelTanks[Target] or Target.Engines[Engine] then + if Engine.FuelTank == Target then + Engine.FuelTank = next(Engine.FuelTanks, Target) end - - local Rope = Engine.Gearboxes[Target].Rope - - if IsValid(Rope) then Rope:Remove() end - - Engine.Gearboxes[Target] = nil + + Engine.FuelTanks[Target] = nil Target.Engines[Engine] = nil - + Engine:UpdateOverlay() Target:UpdateOverlay() - - Engine:InvalidateClientInfo() - + return true, "Engine unlinked successfully!" end - }) + + return false, "This engine is not linked to this fuel tank." + end) + + ACF.RegisterClassLink("acf_engine", "acf_gearbox", function(Engine, Target) + if Engine.Gearboxes[Target] then return false, "This engine is already linked to this gearbox." end + if Engine:GetPos():DistToSqr(Target:GetPos()) > MaxDistance then return false, "This gearbox is too far away from this engine!" end + + -- make sure the angle is not excessive + local InPos = Target:LocalToWorld(Target.In) + local OutPos = Engine:LocalToWorld(Engine.Out) + local Direction + + if Engine.IsTrans then + Direction = -Engine:GetRight() + else + Direction = Engine:GetForward() + end + + if (OutPos - InPos):GetNormalized():Dot(Direction) < 0.7 then + return false, "Cannot link due to excessive driveshaft angle!" + end + + local Rope + + if tobool(Engine.Owner:GetInfoNum("ACF_MobilityRopeLinks", 1)) then + Rope = constraint.CreateKeyframeRope(OutPos, 1, "cable/cable2", nil, Engine, Engine.Out, 0, Target, Target.In, 0) + end + + local Link = { + Rope = Rope, + RopeLen = (OutPos - InPos):Length(), + ReqTq = 0 + } + + Engine.Gearboxes[Target] = Link + Target.Engines[Engine] = true + + Engine:UpdateOverlay() + Target:UpdateOverlay() + + Engine:InvalidateClientInfo() + + return true, "Engine linked successfully!" + end) + + ACF.RegisterClassUnlink("acf_engine", "acf_gearbox", function(Engine, Target) + if not Engine.Gearboxes[Target] then + return false, "This engine is not linked to this gearbox." + end + + local Rope = Engine.Gearboxes[Target].Rope + + if IsValid(Rope) then Rope:Remove() end + + Engine.Gearboxes[Target] = nil + Target.Engines[Engine] = nil + + Engine:UpdateOverlay() + Target:UpdateOverlay() + + Engine:InvalidateClientInfo() + + return true, "Engine unlinked successfully!" + end) end --===============================================================================================-- -- Local Funcs and Vars From 12e044364e34692d3c188245142cad4b4fbea3fb Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Thu, 29 Aug 2024 17:00:45 -0400 Subject: [PATCH 07/12] Remove Link Store, Link Symmetry and Fix Issues - Removes LinkStore variables - Link information is now stored in the direction registered, but retrieval will try either so to keep the way we link things the same - Link functions will now be called with an additional `FromChip` parameter which specifies if the linkage comes from a chip. - Links will now support a `ChipDelay` property which will apply the linkage a set time after the initial call. This should be used with the `RegisterClassCheck` function. --- lua/acf/core/utilities/util_sv.lua | 105 +++++++++--------- lua/entities/acf_base_scalable/init.lua | 40 +++++-- lua/entities/acf_base_simple/init.lua | 41 +++++-- .../core/custom/acffunctions.lua | 2 +- lua/starfall/libs_sh/acffunctions.lua | 2 +- 5 files changed, 116 insertions(+), 74 deletions(-) diff --git a/lua/acf/core/utilities/util_sv.lua b/lua/acf/core/utilities/util_sv.lua index 8b3d09682..677fb0c72 100644 --- a/lua/acf/core/utilities/util_sv.lua +++ b/lua/acf/core/utilities/util_sv.lua @@ -415,81 +415,78 @@ do -- Entity linking } } ]]-- - local ClassLink = { Link = {}, Unlink = {} } - - --- Registers a link or unlink between two classes and how to handle them. + --- @class LinkFunction + --- @field Ent1 table The first entity in the link + --- @field Ent2 table The second entity in the link + --- @field FromChip boolean If the link is from a chip + --- @return boolean? Success Whether the link was successful + --- @return string? Message A message about the link status + + --- @class LinkData + --- @field Link LinkFunction? The function to handle linking + --- @field Unlink LinkFunction? The function to handle unlinking + --- @field Check LinkFunction? The function to check the link status + --- @field ChipDelay number? The delay associated with the link if done via chip + + --- @type table> + local ClassLink = { } + + --- Initializes a link in the ClassLink table if it doesn't already exist and returns the result. + --- The Link is initialized directionally (InitLink(Class1,Class2) != InitLink(Class2,Class1)) --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) - local function RegisterNewLink(Action, Class1, Class2, Function) - if not isfunction(Function) then return end - - local Target = ClassLink[Action] - local Data1 = Target[Class1] - - if not Data1 then - Target[Class1] = { - [Class2] = function(Ent1, Ent2) - return Function(Ent1, Ent2) - end - } - else - Data1[Class2] = function(Ent1, Ent2) - return Function(Ent1, Ent2) - end - end - - if Class1 == Class2 then return end - - local Data2 = Target[Class2] - - if not Data2 then - Target[Class2] = { - [Class1] = function(Ent2, Ent1) - return Function(Ent1, Ent2) - end - } - else - Data2[Class1] = function(Ent2, Ent1) - return Function(Ent1, Ent2) - end - end + --- @return LinkData? LinkData The returned link + function ACF.InitLink(Class1, Class2) + if not ClassLink[Class1] then ClassLink[Class1] = {} end + if not ClassLink[Class1][Class2] then ClassLink[Class1][Class2] = {} end + return ClassLink[Class1][Class2] end - --- Registers that two classes can be linked, as well as how to handle entities of their class being linked. + --- Attempts to retrieve link information from Class 1 to Class2, otherwise tries Class 2 to Class1. If link exists in either direction, return nil. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message - function ACF.RegisterClassLink(Class1, Class2, Function) - RegisterNewLink("Link", Class1, Class2, Function) + --- @return LinkData? LinkData The returned link + --- @return boolean Reversed Whether you should reverse your entity arguments when calling with entities + function ACF.GetClassLink(Class1, Class2) + if ClassLink[Class1] ~= nil and ClassLink[Class1][Class2] ~= nil then return ClassLink[Class1][Class2], false end + if ClassLink[Class2] ~= nil and ClassLink[Class2][Class1] ~= nil then return ClassLink[Class2][Class1], true end + return nil, false end - --- Returns the callback defined previously by ACF.RegisterClassLink between Class1 and Class2. + --- Registers that two classes can be linked, as well as how to handle entities of their class being linked. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @return fun(Entity1:table, Entity2:table) | nil # The linking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no linking functions - function ACF.GetClassLink(Class1, Class2) - if not ClassLink.Link[Class1] then return end - - return ClassLink.Link[Class1][Class2] + --- @param Function LinkFunction The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message + function ACF.RegisterClassLink(Class1, Class2, Function) + local LinkData = ACF.InitLink(Class1,Class2) + LinkData.Link = Function end --- Registers that two classes can be unlinked, as well as how to handle entities of their class being unlinked. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) The unlinking function defined between an entity of Class1 and an entity of Class2 + --- @param Function LinkFunction The unlinking function defined between an entity of Class1 and an entity of Class2 function ACF.RegisterClassUnlink(Class1, Class2, Function) - RegisterNewLink("Unlink", Class1, Class2, Function) + local LinkData = ACF.InitLink(Class1, Class2) + LinkData.Unlink = Function end - --- Returns the callback defined previously by ACF.RegisterClassUnlink between Class1 and Class2. + --- Registers a validation check between two classes. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @return fun(Entity1:table, Entity2:table) | nil # The unlinking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no unlinking functions - function ACF.GetClassUnlink(Class1, Class2) - if not ClassLink.Unlink[Class1] then return end + --- @param Function LinkFunction The checking function defined between an entity of Class1 and an entity of Class2 + function ACF.RegisterClassCheck(Class1, Class2, Function) + local LinkData = ACF.InitLink(Class1, Class2) + LinkData.Check = Function + end - return ClassLink.Unlink[Class1][Class2] + --- Registers a chip delay between two classes. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param ChipDelay number If the link happens from the chip, then delay it by this amount + function ACF.RegisterClassDelay(Class1, Class2, ChipDelay) + local LinkData = ACF.InitLink(Class1, Class2) + LinkData.ChipDelay = ChipDelay end end diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index d8f778074..b7903db0e 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -62,17 +62,35 @@ do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." - function ENT:Link(Target) + function ENT:Link(Target, FromChip) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end if self == Target then return false, "Can't link an entity to itself." end local Class = Target:GetClass() - local Function = ACF.GetClassLink(self:GetClass(), Class) + local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) + + if LinkData == nil then return false, "Links between these two entities are impossible" end + + local Function = LinkData.Link + local Check = LinkData.Check + local ChipDelay = LinkData.ChipDelay + + local A, B = self, Target -- Default argument order + if Reversed then A, B = Target, self end -- If reversed, reverse argument order if Function then - return Function(self, Target) - elseif self.DefaultLink then - return self:DefaultLink(Target) + if Check then + local result, message = Check(A, B) + if result then + if FromChip and ChipDelay then + timer.Simple(ChipDelay,function() + if Check(A, B) then Function(A, B) end + end) + else Function(A,B) end + end + return result, message + end + return Function(A, B) end return false, LinkText:format(self.PluralName, Target.PluralName or Class) @@ -83,12 +101,16 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetClassUnlink(self:GetClass(), Class) + local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) + local Function = LinkData.Unlink + + if LinkData == nil then return false, "Links between these two entities are impossible" end + + local A, B = self, Target -- Default argument order + if Reversed then A, B = Target, self end -- If reversed, reverse argument order if Function then - return Function(self, Target) - elseif self.DefaultUnlink then - return self:DefaultUnlink(Target) + return Function(A, B) end return false, UnlinkText:format(self.PluralName, Target.PluralName or Class) diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index d8f778074..c1410621a 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -62,17 +62,35 @@ do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." - function ENT:Link(Target) + function ENT:Link(Target, FromChip) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end if self == Target then return false, "Can't link an entity to itself." end local Class = Target:GetClass() - local Function = ACF.GetClassLink(self:GetClass(), Class) + local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) + + if LinkData == nil then return false, "Links between these two entities are impossible" end + + local Function = LinkData.Link + local Check = LinkData.Check + local ChipDelay = LinkData.ChipDelay + + local A, B = self, Target -- Default argument order + if Reversed then A, B = Target, self end -- If reversed, reverse argument order if Function then - return Function(self, Target) - elseif self.DefaultLink then - return self:DefaultLink(Target) + if Check then + local result, message = Check(A, B) + if result then + if FromChip and ChipDelay then + timer.Simple(ChipDelay,function() + if Check(A, B) then Function(A, B) end + end) + else Function(A,B) end + end + return result, message + end + return Function(A, B) end return false, LinkText:format(self.PluralName, Target.PluralName or Class) @@ -83,12 +101,17 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetClassUnlink(self:GetClass(), Class) + local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) + + if LinkData == nil then return false, "Links between these two entities are impossible" end + + local Function = LinkData.Unlink + + local A, B = self, Target -- Default argument order + if Reversed then A, B = Target, self end -- If reversed, reverse argument order if Function then - return Function(self, Target) - elseif self.DefaultUnlink then - return self:DefaultUnlink(Target) + return Function(A, B) end return false, UnlinkText:format(self.PluralName, Target.PluralName or Class) diff --git a/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua b/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua index 5534336d3..087188819 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua @@ -348,7 +348,7 @@ e2function number entity:acfLinkTo(entity Target, number Notify) return 0 end - local Sucess, Message = this:Link(Target) + local Sucess, Message = this:Link(Target,true) if Notify ~= 0 then ACF.SendNotify(self.player, Sucess, Message) diff --git a/lua/starfall/libs_sh/acffunctions.lua b/lua/starfall/libs_sh/acffunctions.lua index a6560b0b7..35ed14fa6 100644 --- a/lua/starfall/libs_sh/acffunctions.lua +++ b/lua/starfall/libs_sh/acffunctions.lua @@ -1105,7 +1105,7 @@ if SERVER then CheckPerms(instance, This, "entities.acf") CheckPerms(instance, Target, "entities.acf") - local Success, Message = This:Link(Target) + local Success, Message = This:Link(Target,true) if notify then ACF.SendNotify(instance.player, Success, Message) From 6479163e0b337b677624234f09cd57383cb1a424 Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Thu, 29 Aug 2024 17:05:08 -0400 Subject: [PATCH 08/12] Fix Linter --- lua/acf/core/utilities/util_sv.lua | 2 +- lua/entities/acf_base_scalable/init.lua | 5 +++-- lua/entities/acf_base_simple/init.lua | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lua/acf/core/utilities/util_sv.lua b/lua/acf/core/utilities/util_sv.lua index 677fb0c72..d123b5769 100644 --- a/lua/acf/core/utilities/util_sv.lua +++ b/lua/acf/core/utilities/util_sv.lua @@ -458,7 +458,7 @@ do -- Entity linking --- @param Class2 string The other class in the link --- @param Function LinkFunction The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message function ACF.RegisterClassLink(Class1, Class2, Function) - local LinkData = ACF.InitLink(Class1,Class2) + local LinkData = ACF.InitLink(Class1, Class2) LinkData.Link = Function end diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index b7903db0e..6b839516b 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -61,6 +61,7 @@ end --------------------------------------------- do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." + local timer = timer function ENT:Link(Target, FromChip) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end @@ -83,10 +84,10 @@ do -- Entity linking and unlinking -------------- local result, message = Check(A, B) if result then if FromChip and ChipDelay then - timer.Simple(ChipDelay,function() + timer.Simple(ChipDelay, function() if Check(A, B) then Function(A, B) end end) - else Function(A,B) end + else Function(A, B) end end return result, message end diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index c1410621a..45cccddf3 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -61,6 +61,7 @@ end --------------------------------------------- do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." + local timer = timer function ENT:Link(Target, FromChip) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end @@ -83,7 +84,7 @@ do -- Entity linking and unlinking -------------- local result, message = Check(A, B) if result then if FromChip and ChipDelay then - timer.Simple(ChipDelay,function() + timer.Simple(ChipDelay, function() if Check(A, B) then Function(A, B) end end) else Function(A,B) end From 209e5c1423f6d3b3bd955428023e189c8f87b255 Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Thu, 29 Aug 2024 17:08:13 -0400 Subject: [PATCH 09/12] One More Try --- lua/entities/acf_base_simple/init.lua | 2 +- lua/starfall/libs_sh/acffunctions.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index 45cccddf3..a0d068852 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -87,7 +87,7 @@ do -- Entity linking and unlinking -------------- timer.Simple(ChipDelay, function() if Check(A, B) then Function(A, B) end end) - else Function(A,B) end + else Function(A, B) end end return result, message end diff --git a/lua/starfall/libs_sh/acffunctions.lua b/lua/starfall/libs_sh/acffunctions.lua index 35ed14fa6..cb83043a7 100644 --- a/lua/starfall/libs_sh/acffunctions.lua +++ b/lua/starfall/libs_sh/acffunctions.lua @@ -1105,7 +1105,7 @@ if SERVER then CheckPerms(instance, This, "entities.acf") CheckPerms(instance, Target, "entities.acf") - local Success, Message = This:Link(Target,true) + local Success, Message = This:Link(Target, true) if notify then ACF.SendNotify(instance.player, Success, Message) From e6c6a4b1d62d0aa28c5e102f1f465274895d92cb Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Thu, 29 Aug 2024 20:16:16 -0400 Subject: [PATCH 10/12] Fix Style Issues --- lua/acf/core/utilities/util_sv.lua | 4 ++-- lua/entities/acf_base_scalable/init.lua | 6 +++--- lua/entities/acf_base_simple/init.lua | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lua/acf/core/utilities/util_sv.lua b/lua/acf/core/utilities/util_sv.lua index d123b5769..5175189d8 100644 --- a/lua/acf/core/utilities/util_sv.lua +++ b/lua/acf/core/utilities/util_sv.lua @@ -448,8 +448,8 @@ do -- Entity linking --- @return LinkData? LinkData The returned link --- @return boolean Reversed Whether you should reverse your entity arguments when calling with entities function ACF.GetClassLink(Class1, Class2) - if ClassLink[Class1] ~= nil and ClassLink[Class1][Class2] ~= nil then return ClassLink[Class1][Class2], false end - if ClassLink[Class2] ~= nil and ClassLink[Class2][Class1] ~= nil then return ClassLink[Class2][Class1], true end + if ClassLink[Class1] and ClassLink[Class1][Class2] then return ClassLink[Class1][Class2], false end + if ClassLink[Class2] and ClassLink[Class2][Class1] then return ClassLink[Class2][Class1], true end return nil, false end diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index 6b839516b..d394ceea5 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -81,15 +81,15 @@ do -- Entity linking and unlinking -------------- if Function then if Check then - local result, message = Check(A, B) - if result then + local Result, Message = Check(A, B) + if Result then if FromChip and ChipDelay then timer.Simple(ChipDelay, function() if Check(A, B) then Function(A, B) end end) else Function(A, B) end end - return result, message + return Result, Message end return Function(A, B) end diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index a0d068852..5d12c53c6 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -81,15 +81,15 @@ do -- Entity linking and unlinking -------------- if Function then if Check then - local result, message = Check(A, B) - if result then + local Result, Message = Check(A, B) + if Result then if FromChip and ChipDelay then timer.Simple(ChipDelay, function() if Check(A, B) then Function(A, B) end end) else Function(A, B) end end - return result, message + return Result, Message end return Function(A, B) end From 730c197b0c42c5e624a837496c2a5e2dc8192b50 Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Thu, 29 Aug 2024 20:17:13 -0400 Subject: [PATCH 11/12] One More Thing --- lua/entities/acf_base_scalable/init.lua | 2 +- lua/entities/acf_base_simple/init.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index d394ceea5..ba3991de4 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -70,7 +70,7 @@ do -- Entity linking and unlinking -------------- local Class = Target:GetClass() local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) - if LinkData == nil then return false, "Links between these two entities are impossible" end + if not LinkData then return false, "Links between these two entities are impossible" end local Function = LinkData.Link local Check = LinkData.Check diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index 5d12c53c6..c35447823 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -70,7 +70,7 @@ do -- Entity linking and unlinking -------------- local Class = Target:GetClass() local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) - if LinkData == nil then return false, "Links between these two entities are impossible" end + if not LnkData then return false, "Links between these two entities are impossible" end local Function = LinkData.Link local Check = LinkData.Check From c2c2d241849bc01b223a38bb775daf769ef4f337 Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Thu, 29 Aug 2024 20:18:34 -0400 Subject: [PATCH 12/12] Update init.lua --- lua/entities/acf_base_simple/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index c35447823..5f32e4e1b 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -70,7 +70,7 @@ do -- Entity linking and unlinking -------------- local Class = Target:GetClass() local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) - if not LnkData then return false, "Links between these two entities are impossible" end + if not LinkData then return false, "Links between these two entities are impossible" end local Function = LinkData.Link local Check = LinkData.Check