Skip to content

Commit

Permalink
Finished
Browse files Browse the repository at this point in the history
  • Loading branch information
LengthenedGradient committed Jul 18, 2024
1 parent e91eae9 commit bdc5c6d
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 75 deletions.
178 changes: 117 additions & 61 deletions lua/acf/core/utilities/util_sv.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 18 additions & 7 deletions lua/entities/acf_base_scalable/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
23 changes: 18 additions & 5 deletions lua/entities/acf_base_simple/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion lua/starfall/libs_sh/acffunctions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit bdc5c6d

Please sign in to comment.