dobrograd-13-06-2022/garrysmod/addons/feature-wire/lua/entities/info_wiremapinterface/init.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

465 lines
13 KiB
Lua

--[[
This is the wire map interface entity by Grocel. (info_wiremapinterface)
This point entity allows you to give other entities wire in-/outputs.
Those wire ports allows you to control thinks on the map with Wiremod
or to let the map return thinks to wire outputs.
It supports many datatypes and custom lua codes.
A lua code is run when its input triggers.
It has special globals:
WIRE_NAME = Input name
WIRE_VALUE = Input value
WIRE_WIRED = Is the input wired?
WIRE_CALLER = This entity
WIRE_ACTIVATOR = The entity that has the input
Keep in mind that you have to know what you do
and that you have to activate spawnflag 8 to make it work.
Spawnflag 8 is better known as "Run given Lua codes (For advanced users!)" in the Hammer Editor.
Please don't change thinks unless you know what you do. You may break maps if do something wrong.
]]
include("convert.lua")
include("entitycontrol.lua")
include("entityoverride.lua")
include("output.lua")
local ALLOW_INTERFACE = CreateConVar("sv_wire_mapinterface", "1", {FCVAR_NOTIFY, FCVAR_ARCHIVE, FCVAR_GAMEDLL}, "Aktivate or deaktivate the wire map interface. Default: 1")
local Ents = {}
hook.Add("PlayerInitialSpawn", "WireMapInterface_PlayerInitialSpawn", function(ply)
if (!IsValid(ply)) then return end
for Ent, time in ipairs(Ents) do
if (!IsValid(Ent)) then break end
timer.Simple(time + 0.1, function()
if (!IsValid(ply)) then return end
if (!IsValid(Ent)) then return end
if (!Ent.GiveWireInterfeceClient) then return end
Ent:GiveWireInterfeceClient(ply)
end)
end
end)
-- This is a point entity
ENT.Base = "base_point"
ENT.Type = "point"
local MAX_PORTS = 256 -- This entity supports more than the 8 ports you see in the editor. This value is the port limit.
local MAX_ENTITIES = 32 -- The maximum number of entities per interface entitiy that can get wire ports
local MIN_TRIGGER_TIME = 0.01 -- Minimum triggering Time for in- and outputs.
-- This checks if you can give an entity wiremod abilities
function ENT:IsWireableEntity(Entity)
if (!IsValid(Entity)) then return false end -- No interface for invalid entities!
if (IsValid(Entity._WireMapInterfaceEnt) and (Entity._WireMapInterfaceEnt ~= self)) then return false end -- Only one interface per entity!
if (!IsValid(Entity._WireMapInterfaceEnt) and (WireLib.HasPorts(Entity) or Entity.IsWire or Entity.Inputs or Entity.Outputs)) then return false end -- Don't destroy wiremod entites!
if (Entity:IsWorld()) then return false end -- No interface for the worldspawn!
if (Entity:IsVehicle()) then return false end -- No interface for vehicles!
if (Entity:IsNPC()) then return false end -- No interface for NPCs!
if (Entity:IsPlayer()) then return false end -- No interface for players!
if (Entity:IsWeapon()) then return false end -- No interface for weapons!
if (string.match(Entity:GetClass(), "^(item_[%w_]+)")) then return false end -- No interface for items!
if (Entity:IsConstraint()) then return false end -- No interface for constraints!
if (Entity:GetPhysicsObjectCount() > 1) then return false end -- No interface for ragdolls!
if (IsValid(Entity:GetPhysicsObject())) then return true end -- Everything with a single physics object can get an interface!
return true
end
function ENT:CheckEntLimid(CallOnMax, ...)
if (self.WireEntsCount > MAX_ENTITIES) then
MsgN(self.ErrorName..": Warning, to many wire entities linked!")
if (CallOnMax) then
CallOnMax(self, ...)
end
return false
end
self.WireEntsCount = self.WireEntsCount + 1
return true
end
-- Run the given lua code
local function RunLua(I, name, value, wired, self, Ent)
local lua = self.Ins[I].lua or ""
if ((lua == "") or !self.RunLuaCode) then return end
local func = CompileString(lua, self.ErrorName.." (Input "..I..")", false)
local Err
if isfunction(func) then
-- Globals
WIRE_NAME = name -- Input name
WIRE_VALUE = value -- Input value
WIRE_WIRED = wired -- Is the input wired?
WIRE_CALLER = self -- This entity
WIRE_ACTIVATOR = Ent -- The entity that has the input
local status, err = xpcall(func, debug.traceback)
if (!status) then
Err = err or ""
end
-- Remove globals
WIRE_NAME = nil
WIRE_VALUE = nil
WIRE_WIRED = nil
WIRE_CALLER = nil
WIRE_ACTIVATOR = nil
else
Err = func
end
if (Err and (Err ~= "")) then
ErrorNoHalt(Err.."\n")
end
end
-- Wire input
function ENT:TriggerWireInput(name, value, wired, Ent)
if (!WireAddon) then return end
if (!IsValid(Ent)) then return end
if ((!self.Active or !ALLOW_INTERFACE:GetBool()) and wired) then
self.SavedIn = self.SavedIn or {}
self.SavedIn[name] = {value, wired, Ent}
return
end
self.Wired = self.Wired or {}
self.Wired[Ent] = self.Wired[Ent] or {}
local WireRemoved = ((self.Wired[Ent][name] or false) ~= wired) and !wired
self.Wired[Ent][name] = wired
self.Timer = self.Timer or {}
self.Timer.In = self.Timer.In or {}
if (((CurTime() - (self.Timer.In[name] or 0)) < (self.min_trigger_time or MIN_TRIGGER_TIME)) and !WireRemoved) then return end
self.Timer.In[name] = CurTime()
self.Data = self.Data or {}
self.Data.In = self.Data.In or {}
if ((self.Data.In[name] == value) and !WireRemoved) then return end
self.Data.In[name] = value
local I = self.InsIDs[name] or 0
if ((I > 0) and (I <= MAX_PORTS) and self.InsExist[I]) then
local _, Convert, Toggle = self:Convert_WireToMap(self.Ins[I].type)
if (!Convert) then return end
local Output = "onwireinput"..I
-- Map output
if (!wired) then
if (WireRemoved) then
if (!Toggle) then
self:TriggerOutput(Output, Ent, Convert(value))
end
self:TriggerOutput("onresetwireinput"..I, Ent)
RunLua(I, name, value, wired, self, Ent)
end
else
if (Toggle) then
if (Convert(value)) then
self:TriggerOutput(Output, Ent)
RunLua(I, name, value, wired, self, Ent)
end
else
self:TriggerOutput(Output, Ent, Convert(value))
RunLua(I, name, value, wired, self, Ent)
end
end
end
end
-- Wire output
function ENT:TriggerWireOutput(ent, i, val)
if (!IsValid(ent)) then return false end
local OutputName = self.Outs[i].name or ""
if (OutputName == "") then return false end
local _, Convert, Toggle = self:Convert_MapToWire(self.Outs[i].type)
if (!Convert) then return false end
if (Toggle) then
Wire_TriggerOutput(ent, OutputName, Convert(self, ent, i))
else
Wire_TriggerOutput(ent, OutputName, Convert(val))
end
return true
end
-- Map input
function ENT:AcceptInput(name, activator, caller, data)
if (!WireAddon) then return false end
name = string.lower(tostring(name or ""))
if (name == "") then return false end
if (name == "activate") then
self.Active = true
return true
end
if (name == "deactivate") then
self.Active = false
return true
end
if (name == "toggle") then
self.Active = !self.Active
return true
end
if (self.Active and ALLOW_INTERFACE:GetBool()) then
local pattern = "(%d+)"
local I = tonumber(string.match(name, "triggerwireoutput"..pattern)) or 0
if ((I > 0) and (I <= MAX_PORTS) and (self.OutsExist[I])) then
self.Timer = self.Timer or {}
self.Timer.Out = self.Timer.Out or {}
if ((CurTime() - (self.Timer.Out[name] or 0)) < (self.min_trigger_time or MIN_TRIGGER_TIME)) then return false end
self.Timer.Out[name] = CurTime()
-- Wire output
for Ent, _ in pairs(self.WireEnts or {}) do
self:TriggerWireOutput(Ent, I, data)
end
return true
end
end
if (!self.WirePortsChanged) then return false end
if (name == "addentity") then
local Ent, Func = self:AddSingleEntity(caller)
if (!IsValid(Ent) or !Func) then return false end
timer.Simple(0.02, function() Func( self, Ent, nil, true) end)
self:TriggerOutput("onwireentscreated", self)
self:TriggerOutput("onwireentsready", self)
return true
end
if (name == "removeentity") then
self:RemoveSingleEntity(caller)
return true
end
if (name == "addentities") then
self:AddEntitiesByName(data)
return true
end
if (name == "removeentities") then
self:RemoveEntitiesByName(data)
return true
end
if (name == "removeallentities") then
self:RemoveAllEntities()
return true
end
return false
end
function ENT:KeyValue(key, value)
if (!WireAddon) then return end
key = string.lower(tostring(key or ""))
value = tostring(value or "")
if ((key == "") or (value == "")) then return end
local pattern = "(%d+)"
local I = tonumber(string.match(key, "onwireinput"..pattern)) or 0
if ((I > 0) and (I <= MAX_PORTS)) then
self:StoreOutput(key, value)
end
local I = tonumber(string.match(key, "onresetwireinput"..pattern)) or 0
if ((I > 0) and (I <= MAX_PORTS)) then
self:StoreOutput(key, value)
end
if ((key == "onwireentscreated") or (key == "onwireentsremoved") or
(key == "onwireentsready") or (key == "onwireentsstartchanging")) then
self:StoreOutput(key, value)
end
if (key == "wire_entity_name") then
self.WireEntName = value
end
if (key == "min_trigger_time") then
self.min_trigger_time = math.max(tonumber(value) or 0, MIN_TRIGGER_TIME)
end
local pattern = "(%d+)_(%l+)"
local I, name = string.match(key, "input"..pattern)
local I, name = tonumber(I) or 0, tostring(name or "")
if ((I > 0) and (I <= MAX_PORTS) and (name ~= "")) then
self.Ins = self.Ins or {}
self.InsIDs = self.InsIDs or {}
self.InsExist = self.InsExist or {}
self.Ins[I] = self.Ins[I] or {}
if (name == "lua") then
self.Ins[I][name] = value
elseif (name == "type") then
self.Ins[I][name] = tonumber(value)
elseif (name == "desc") then
self.Ins[I][name] = value
elseif (name == "name") then
self.InsIDs[value] = I
self.InsExist[I] = true
self.Ins[I][name] = value
end
end
local I, name = string.match(key, "output"..pattern)
local I, name = tonumber(I) or 0, tostring(name or "")
if ((I > 0) and (I <= MAX_PORTS) and (name ~= "")) then
self.Outs = self.Outs or {}
--self.OutsIDs = self.OutsIDs or {}
self.OutsExist = self.OutsExist or {}
self.Outs[I] = self.Outs[I] or {}
if (name == "type") then
self.Outs[I][name] = tonumber(value)
elseif (name == "desc") then
self.Outs[I][name] = value
elseif (name == "name") then
--self.OutsIDs[value] = I
self.OutsExist[I] = true
self.Outs[I][name] = value
end
end
end
local Count = 1
function ENT:Initialize()
if (!WireAddon) then return end
self.WireEnts = self.WireEnts or {}
self.WireEntsCount = 0
self.WireEntName = self.WireEntName or ""
self:UpdateData()
self.Active = (bit.band(self.flags, 16) > 0) -- Start Active
self.oldActive = self.Active
self.old_ALLOW_INTERFACE_bool = ALLOW_INTERFACE:GetBool()
local Name = self:GetName() or ""
local ErrorName = "Wire Map Interface: "..tostring(self)
if (Name == "") then
self.ErrorName = ErrorName
else
self.ErrorName = ErrorName.."['"..Name.."']"
end
local time = Count * 0.3 + 0.5
if (self.WireEntName == "") then
self.WirePortsChanged = true
else
timer.Simple(time, function()
if (!IsValid(self)) then return end
self.WirePortsChanged = true
self:AddEntitiesByName(self.WireEntName)
end)
end
Ents[self] = time
Count = Count + 1
end
-- To cleanup and get the in-/outputs information.
local function SplitTable(tab, self)
if (!IsValid(self)) then return end
if (!tab) then return end
if (#tab == 0) then return end
local tab = table.Copy(tab)
local allowlua = self.RunLuaCode
local names, types, descs = {}, {}, {}
local Index = 0
for i = 1, #tab do
local Port = tab[i]
if (Port) then
local name = Port.name -- The port name for checking
if (name) then -- Do not add ports with no names
Index = Index + 1
names[Index] = name -- The port name
types[Index] = self:Convert_MapToWire(Port.type) -- The port type
descs[Index] = Port.desc -- The port description
if (!allowlua) then
tab[i].lua = nil -- remove lua codes if the lua mode isn't on.
end
else
tab[i] = nil -- Resort and cleanup the given table for later using
end
end
end
return names, types, descs, tab
end
function ENT:UpdateData()
self.flags = self:GetSpawnFlags()
self.RunLuaCode = (bit.band(self.flags, 8) > 0) -- Run given Lua codes
self.Inames, self.Itypes, self.Idescs, self.Ins = SplitTable(self.Ins, self)
self.Onames, self.Otypes, self.Odescs, self.Outs = SplitTable(self.Outs, self)
end
function ENT:Think()
if (!WireAddon) then return end
local ALLOW_INTERFACE_bool = ALLOW_INTERFACE:GetBool()
if ((self.Active ~= self.oldActive) or (ALLOW_INTERFACE_bool ~= self.old_ALLOW_INTERFACE_bool)) then
if (self.Active and ALLOW_INTERFACE_bool) then
self.SavedIn = self.SavedIn or {}
for name, values in pairs(self.SavedIn) do
self:TriggerWireInput(name, unpack(values))
self.SavedIn[name] = nil
end
end
self.oldActive = self.Active
self.old_ALLOW_INTERFACE_bool = ALLOW_INTERFACE_bool
end
end
function ENT:OnRemove()
if (!WireAddon) then return end
self.flags = self:GetSpawnFlags()
if (bit.band(self.flags, 4) > 0) then -- Remove in-/output entities on remove
for obj1, obj2 in pairs(self.WireEnts or {}) do
local Entity = (IsEntity(obj1) and obj1) or (IsEntity(obj2) and obj2)
if (IsValid(Entity)) then
SafeRemoveEntity(Entity)
end
end
else
self:RemoveAllEntities()
end
end