-- Compatibility Global if not WireLib then return end WireAddon = 1 local ents = ents local timer = timer local string = string local math_clamp = math.Clamp local table = table local hook = hook local concommand = concommand local Msg = Msg local MsgN = MsgN local pairs = pairs local ipairs = ipairs local IsValid = IsValid local tostring = tostring local Vector = Vector local Color = Color local Material = Material local HasPorts = WireLib.HasPorts -- Very important for checks! function WireLib.PortComparator(a,b) return a.Num < b.Num end -- Allow to specify the description and type, like "Name (Description) [TYPE]" local function ParsePortName(namedesctype, fbtype, fbdesc) local namedesc, tp = namedesctype:match("^(.+) %[(.+)%]$") if not namedesc then namedesc = namedesctype tp = fbtype end local name, desc = namedesc:match("^(.+) %((.*)%)$") if not name then name = namedesc desc = fbdesc end return name, desc, tp end local Inputs = {} local Outputs = {} local CurLink = {} local CurTime = CurTime -- helper function that pcalls an input function WireLib.TriggerInput(ent, name, value, ...) if (not IsValid(ent) or not HasPorts(ent) or not ent.Inputs or not ent.Inputs[name]) then return end ent.Inputs[name].Value = value if (not ent.TriggerInput) then return end local ok, ret = xpcall(ent.TriggerInput, debug.traceback, ent, name, value, ...) if not ok then local ply = WireLib.GetOwner(ent) local owner_msg = IsValid(ply) and (" by %s"):format(tostring(ply)) or "" local message = ("Wire error (%s%s):\n%s\n"):format(tostring(ent),owner_msg, ret) WireLib.ErrorNoHalt(message) if IsValid(ply) then WireLib.ClientError(message, ply) end end end -- an array of data types WireLib.DT = { NORMAL = { Zero = 0 }, -- Numbers VECTOR = { Zero = Vector(0, 0, 0) }, ANGLE = { Zero = Angle(0, 0, 0) }, COLOR = { Zero = Color(0, 0, 0) }, ENTITY = { Zero = NULL }, STRING = { Zero = "" }, TABLE = { Zero = {n={},ntypes={},s={},stypes={},size=0}, }, BIDIRTABLE = { Zero = {n={},ntypes={},s={},stypes={},size=0}, BiDir = true }, ANY = { Zero = 0 }, ARRAY = { Zero = {} }, BIDIRARRAY = { Zero = {}, BiDir = true }, } function WireLib.CreateSpecialInputs(ent, names, types, descs) types = types or {} descs = descs or {} local ent_ports = {} ent.Inputs = ent_ports for n,v in pairs(names) do local name, desc, tp = ParsePortName(v, types[n] or "NORMAL", descs and descs[n]) local port = { Entity = ent, Name = name, Desc = desc, Type = tp, Value = WireLib.DT[ tp ].Zero, Material = "tripmine_laser", Color = Color(255, 255, 255, 255), Width = 1, Num = n, } local idx = 1 while (Inputs[idx]) do idx = idx+1 end port.Idx = idx ent_ports[name] = port Inputs[idx] = port end WireLib._SetInputs(ent) return ent_ports end function WireLib.CreateSpecialOutputs(ent, names, types, descs) types = types or {} descs = descs or {} local ent_ports = {} ent.Outputs = ent_ports for n,v in pairs(names) do local name, desc, tp = ParsePortName(v, types[n] or "NORMAL", descs and descs[n]) local port = { Entity = ent, Name = name, Desc = desc, Type = tp, Value = WireLib.DT[ tp ].Zero, Connected = {}, TriggerLimit = 8, Num = n, } local idx = 1 while (Outputs[idx]) do idx = idx+1 end port.Idx = idx ent_ports[name] = port Outputs[idx] = port end WireLib._SetOutputs(ent) return ent_ports end function WireLib.AdjustSpecialInputs(ent, names, types, descs) types = types or {} descs = descs or {} local ent_ports = ent.Inputs or {} for n,v in ipairs(names) do local name, desc, tp = ParsePortName(v, types[n] or "NORMAL", descs and descs[n]) if (ent_ports[name]) then if tp ~= ent_ports[name].Type then timer.Simple(0, function() WireLib.Link_Clear(ent, name) end) ent_ports[name].Value = WireLib.DT[tp].Zero ent_ports[name].Type = tp end ent_ports[name].Keep = true ent_ports[name].Num = n ent_ports[name].Desc = descs[n] else local port = { Entity = ent, Name = name, Desc = desc, Type = tp, Value = WireLib.DT[ tp ].Zero, Material = "tripmine_laser", Color = Color(255, 255, 255, 255), Width = 1, Keep = true, Num = n, } local idx = 1 while (Inputs[idx]) do idx = idx+1 end port.Idx = idx ent_ports[name] = port Inputs[idx] = port end end for portname,port in pairs(ent_ports) do if (port.Keep) then port.Keep = nil else WireLib.Link_Clear(ent, portname) ent_ports[portname] = nil end end WireLib._SetInputs(ent) return ent_ports end function WireLib.AdjustSpecialOutputs(ent, names, types, descs) types = types or {} descs = descs or {} local ent_ports = ent.Outputs or {} for n,v in ipairs(names) do local name, desc, tp = ParsePortName(v, types[n] or "NORMAL", descs and descs[n]) if (ent_ports[name]) then if tp ~= ent_ports[name].Type then WireLib.DisconnectOutput(ent, name) ent_ports[name].Type = tp end ent_ports[name].Keep = true ent_ports[name].Num = n ent_ports[name].Desc = descs[n] else local port = { Keep = true, Name = name, Desc = descs[n], Type = types[n] or "NORMAL", Value = WireLib.DT[ (types[n] or "NORMAL") ].Zero, Connected = {}, TriggerLimit = 8, Num = n, } local idx = 1 while (Outputs[idx]) do idx = idx+1 end port.Idx = idx ent_ports[name] = port Outputs[idx] = port end end for portname,port in pairs(ent_ports) do if (port.Keep) then port.Keep = nil else WireLib.DisconnectOutput(ent, portname) ent_ports[portname] = nil end end WireLib._SetOutputs(ent) return ent_ports end --- Disconnects all wires from the given output. function WireLib.DisconnectOutput(entity, output_name) local output = entity.Outputs[output_name] if output == nil then return end for _, input in pairs_consume(output.Connected) do if IsValid(input.Entity) then WireLib.Link_Clear(input.Entity, input.Name) end end end function WireLib.RetypeInputs(ent, iname, itype, descs) if not HasPorts(ent) then return end local ent_ports = ent.Inputs if (not ent_ports[iname]) or (not itype) then return end if itype ~= ent_ports[iname].Type then WireLib.Link_Clear(ent, iname) ent_ports[iname].Type = itype end ent_ports[iname].Desc = descs ent_ports[iname].Value = WireLib.DT[itype].Zero WireLib._SetInputs(ent) end function WireLib.RetypeOutputs(ent, oname, otype, descs) if not HasPorts(ent) then return end local ent_ports = ent.Outputs if (not ent_ports[oname]) or (not otype) then return end if otype ~= ent_ports[oname].Type then WireLib.DisconnectOutput(ent, oname) ent_ports[oname].Type = otype end ent_ports[oname].Desc = descs ent_ports[oname].Value = WireLib.DT[otype].Zero WireLib._SetOutputs(ent) end -- force_outputs is only needed for existing components to allow them to be updated function WireLib.Restored(ent, force_outputs) if not HasPorts(ent) then return end local ent_ports = ent.Inputs if (ent_ports) then for name,port in pairs(ent_ports) do if (not port.Material) then -- Must be an old save port.Name = name if (port.Ropes) then for _,rope in pairs(port.Ropes) do rope:Remove() end port.Ropes = nil end end port.Entity = ent port.Type = port.Type or "NORMAL" port.Material = port.Material or "cable/blue_elec" port.Color = port.Color or Color(255, 255, 255, 255) port.Width = port.Width or 2 port.StartPos = port.StartPos or Vector(0, 0, 0) if (port.Src) and (not port.Path) then port.Path = { { Entity = port.Src, Pos = Vector(0, 0, 0) } } end local idx = 1 while (Inputs[idx]) do idx = idx+1 end port.Idx = idx Inputs[idx] = port end end local ent_ports = ent.Outputs if (ent_ports) then for _,port in pairs(ent_ports) do port.Entity = ent port.Type = port.Type or "NORMAL" local idx = 1 while (Outputs[idx]) do idx = idx+1 end port.Idx = idx Outputs[idx] = port end elseif (force_outputs) then ent.Outputs = WireLib.CreateOutputs(ent, force_outputs) end end local function ClearPorts(ports, ConnectEnt, DontSendToCL) local Valid, EmergencyBreak = true, 0 -- There is a strange bug, not all the links get removed at once. -- It works when you run it multiple times. while (Valid and (EmergencyBreak < 32)) do local newValid = nil for k,v in ipairs(ports) do local Ent, Name = v.Entity, v.Name if (IsValid(Ent) and (not ConnectEnt or (ConnectEnt == Ent))) then local ports = Ent.Inputs if (ports) then local port = ports[Name] if (port) then WireLib.Link_Clear(Ent, Name, DontSendToCL) newValid = true end end end end Valid = newValid EmergencyBreak = EmergencyBreak + 1 -- Prevents infinite loops if something goes wrong. end end -- Set DontUnList to true, if you want to call WireLib._RemoveWire(eid) manually. function WireLib.Remove(ent, DontUnList) --Clear the inputs local ent_ports = ent.Inputs if (ent_ports) then for _,inport in pairs(ent_ports) do local Source = inport.Src if (IsValid(Source)) then local Outports = Source.Outputs if (Outports) then local outport = Outports[inport.SrcId] if (outport) then ClearPorts(outport.Connected, ent, true) end end end Inputs[inport.Idx] = nil end end --Clear the outputs local ent_ports = ent.Outputs if (ent_ports) then for _,outport in pairs(ent_ports) do ClearPorts(outport.Connected) Outputs[outport.Idx] = nil end end ent.Inputs = nil -- Remove the inputs ent.Outputs = nil -- Remove the outputs ent.IsWire = nil -- Remove the wire mark if (DontUnList) then return end -- Set DontUnList to true if you want to remove ent from the list manually. WireLib._RemoveWire(ent:EntIndex()) -- Remove entity from the list, so it doesn't count as a wire able entity anymore. Very important for IsWire checks! end local function Wire_Link(dst, dstid, src, srcid, path) if (not IsValid(dst) or not HasPorts(dst) or not dst.Inputs or not dst.Inputs[dstid]) then Msg("Wire_link: Invalid destination!\n") return end if (not IsValid(src) or not HasPorts(src) or not src.Outputs or not src.Outputs[srcid]) then Msg("Wire_link: Invalid source!\n") return end local input = dst.Inputs[dstid] local output = src.Outputs[srcid] if (IsValid(input.Src)) then if (input.Src.Outputs) then local oldOutput = input.Src.Outputs[input.SrcId] if (oldOutput) then for k,v in ipairs(oldOutput.Connected) do if (v.Entity == dst) and (v.Name == dstid) then table.remove(oldOutput.Connected, k) end end end end end input.Src = src input.SrcId = srcid input.Path = path WireLib.Paths.Add(input) WireLib._SetLink(input) table.insert(output.Connected, { Entity = dst, Name = dstid }) if dst.OnInputWireLink then -- ENT:OnInputWireLink(iName, iType, oEnt, oName, oType) dst:OnInputWireLink(dstid, input.Type, src, srcid, output.Type) end if src.OnOutputWireLink then -- ENT:OnOutputWireLink(oName, oType, iEnt, iName, iType) src:OnOutputWireLink(srcid, output.Type, dst, dstid, input.Type) end WireLib.TriggerInput(dst, dstid, output.Value) end function WireLib.TriggerOutput(ent, oname, value, iter) if not IsValid(ent) then return end if not HasPorts(ent) then return end if (not ent.Outputs) then return end local output = ent.Outputs[oname] if (output) and (value ~= output.Value or output.Type == "ARRAY" or output.Type == "TABLE") then local timeOfFrame = CurTime() if timeOfFrame ~= output.TriggerTime then -- Reset the TriggerLimit every frame output.TriggerLimit = 4 output.TriggerTime = timeOfFrame elseif output.TriggerLimit <= 0 then return end output.TriggerLimit = output.TriggerLimit - 1 output.Value = value if (iter) then for _,dst in ipairs(output.Connected) do if (IsValid(dst.Entity)) then iter:Add(dst.Entity, dst.Name, value) end end return end iter = WireLib.CreateOutputIterator() for _,dst in ipairs(output.Connected) do if (IsValid(dst.Entity)) then WireLib.TriggerInput(dst.Entity, dst.Name, value, iter) end end iter:Process() end end local function Wire_Unlink(ent, iname, DontSendToCL) if not HasPorts(ent) then return end local input = ent.Inputs[iname] if (input) then if (IsValid(input.Src)) then local outputs = input.Src.Outputs or {} local output = outputs[input.SrcId] if (output) then for k,v in ipairs(output.Connected) do if (v.Entity == ent) and (v.Name == iname) then table.remove(output.Connected, k) end end -- untested if input.Src.OnOutputWireLink then -- ENT:OnOutputWireLink(oName, oType, iEnt, iName, iType) input.Src:OnOutputWireLink(input.SrcId, outputs[input.SrcId].Type, ent, iname, input.Type) end end -- untested if ent.OnInputWireUnlink then -- ENT:OnInputWireUnlink(iName, iType, oEnt, oName, oType) ent:OnInputWireUnlink(iname, input.Type, input.Src, input.SrcId, outputs[input.SrcId].Type) end end input.Src = nil input.SrcId = nil input.Path = nil WireLib.TriggerInput(ent, iname, WireLib.DT[input.Type].Zero, nil) if (DontSendToCL) then return end WireLib._SetLink(input) end end function WireLib.Link_Start(idx, ent, pos, iname, material, color, width) if not IsValid(ent) then return end if not HasPorts(ent) then return end if (not ent.Inputs or not ent.Inputs[iname]) then return end local input = ent.Inputs[iname] if not input.Path then input.Path = {} end CurLink[idx] = { Dst = ent, DstId = iname, Path = input.Path, OldPath = {} } for i=1, #input.Path do CurLink[idx].OldPath[i] = input.Path[i] input.Path[i] = nil end input.StartPos = pos input.Material = material input.Color = color input.Width = math_clamp(width, 0, 5) return true end function WireLib.Link_Node(idx, ent, pos) if not CurLink[idx] then return end if not IsValid(CurLink[idx].Dst) then return end if not IsValid(ent) then return end -- its the world, give up table.insert(CurLink[idx].Path, { Entity = ent, Pos = pos }) WireLib.Paths.Add(CurLink[idx].Dst.Inputs[CurLink[idx].DstId]) end function WireLib.Link_End(idx, ent, pos, oname, pl) if not CurLink[idx] then return end if not IsValid(CurLink[idx].Dst) then return end if not HasPorts(CurLink[idx].Dst) then return end if not IsValid(ent) then return end if not HasPorts(ent) then return end if not ent.Outputs then return end if (CurLink[idx].Dst:GetClass() == "gmod_wire_sensor") and (ent:GetClass() ~= "gmod_wire_target_finder") then MsgN("Wire_link: Beacon Sensor can only be wired to a Target Finder!") if pl then WireLib.AddNotify(pl, "Beacon Sensor can only be wired to a Target Finder!", NOTIFY_GENERIC, 7) end WireLib.Link_Cancel(idx) return end local input = CurLink[idx].Dst.Inputs[CurLink[idx].DstId] local output = ent.Outputs[oname] if not output then --output = { Type = "NORMAL" } local text = "Selected output not found or no output present." MsgN(text) if pl then WireLib.AddNotify(pl, text, NOTIFY_GENERIC, 7) end WireLib.Link_Cancel(idx) return end --Msg("input type= " .. input.Type .. " output type= " .. (output.Type or "NIL") .. "\n") -- I bet that was getting anoying (TAD2020) if (input.Type ~= output.Type) and (input.Type ~= "ANY") and (output.Type ~= "ANY") then local text = "Data Type Mismatch! Input takes "..input.Type.." and Output gives "..output.Type MsgN(text) if pl then WireLib.AddNotify(pl, text, NOTIFY_GENERIC, 7) end WireLib.Link_Cancel(idx) return end table.insert(CurLink[idx].Path, { Entity = ent, Pos = pos }) Wire_Link(CurLink[idx].Dst, CurLink[idx].DstId, ent, oname, CurLink[idx].Path) if (WireLib.DT[input.Type].BiDir) then Wire_Link(ent, oname, CurLink[idx].Dst, CurLink[idx].DstId, {}) end CurLink[idx] = nil end function WireLib.Link_Cancel(idx) if not CurLink[idx] then return end if not IsValid(CurLink[idx].Dst) then return end if CurLink[idx].input then CurLink[idx].Path = CurLink[idx].input.Path else WireLib.Paths.Add({Entity = CurLink[idx].Dst, Name = CurLink[idx].DstId, Width = 0}) end CurLink[idx] = nil end function WireLib.Link_Clear(ent, iname, DontSendToCL) WireLib.Paths.Add({Entity = ent, Name = iname, Width = 0}) Wire_Unlink(ent, iname, DontSendToCL) end function WireLib.WireAll(ply, ient, oent, ipos, opos, material, color, width) if not IsValid(ient) or not IsValid(oent) or not ient.Inputs or not oent.Outputs then return false end for iname, _ in pairs(ient.Inputs) do if oent.Outputs[iname] then WireLib.Link_Start(ply:UniqueID(), ient, ipos, iname, material or "arrowire/arrowire2", color or Color(255,255,255), width or 0) WireLib.Link_End(ply:UniqueID(), oent, opos, iname, ply) end end end do -- class OutputIterator local OutputIterator = {} OutputIterator.__index = OutputIterator function OutputIterator:Add(ent, iname, value) table.insert(self, { Entity = ent, IName = iname, Value = value }) end function OutputIterator:Process() if self.Processing then return end -- should not occur self.Processing = true while #self > 0 do local nextelement = self[1] table.remove(self, 1) WireLib.TriggerInput(nextelement.Entity, nextelement.IName, nextelement.Value, self) end self.Processing = nil end function WireLib.CreateOutputIterator() return setmetatable({}, OutputIterator) end end -- class OutputIterator duplicator.RegisterEntityModifier("WireDupeInfo", function(ply, Ent, DupeInfo) -- this does nothing for now, we need the blank function to get the duplicator to copy the WireDupeInfo into the pasted ent end) -- used for welding wired stuff, if trace is world, the ent is not welded and is frozen instead function WireLib.Weld(ent, traceEntity, tracePhysicsBone, DOR, collision, AllowWorldWeld) if (not ent or not traceEntity or traceEntity:IsNPC() or traceEntity:IsPlayer()) then return end local phys = ent:GetPhysicsObject() if ( traceEntity:IsValid() ) or ( traceEntity:IsWorld() and AllowWorldWeld ) then local const = constraint.Weld( ent, traceEntity, 0, tracePhysicsBone, 0, (not collision), DOR ) -- Don't disable collision if it's not attached to anything if (not collision) then if phys:IsValid() then phys:EnableCollisions( false ) end ent.nocollide = true end return const else if phys:IsValid() then ent:GetPhysicsObject():EnableMotion( false ) end return nil end end function WireLib.BuildDupeInfo( Ent ) if not Ent.Inputs then return {} end local info = { Wires = {} } for portname,input in pairs(Ent.Inputs) do if (IsValid(input.Src)) then info.Wires[portname] = { StartPos = input.StartPos, Material = input.Material, Color = input.Color, Width = input.Width, Src = input.Src:EntIndex(), SrcId = input.SrcId, SrcPos = Vector(0, 0, 0), } if (input.Path) then info.Wires[portname].Path = {} for _,v in ipairs(input.Path) do if (IsValid(v.Entity)) then table.insert(info.Wires[portname].Path, { Entity = v.Entity:EntIndex(), Pos = v.Pos }) end end local n = #info.Wires[portname].Path if (n > 0) and (info.Wires[portname].Path[n].Entity == info.Wires[portname].Src) then info.Wires[portname].SrcPos = info.Wires[portname].Path[n].Pos table.remove(info.Wires[portname].Path, n) end end end end return info end function WireLib.ApplyDupeInfo( ply, ent, info, GetEntByID ) if info.extended and not ent.extended then WireLib.CreateWirelinkOutput( ply, ent, {true} ) -- old dupe compatibility; use the new function end local idx = 0 if IsValid(ply) then idx = ply:UniqueID() end -- Map Save loading does not have a ply if (info.Wires) then for k,input in pairs(info.Wires) do local ent2 = GetEntByID(input.Src) -- Input alias if ent.Inputs and not ent.Inputs[k] then -- if the entity has any inputs and the input 'k' is not one of them... if ent.InputAliases and ent.InputAliases[k] then k = ent.InputAliases[k] else Msg("ApplyDupeInfo: Error, Could not find input '" .. k .. "' on entity type: '" .. ent:GetClass() .. "'\n") continue end end if IsValid( ent2 ) then -- Wirelink and entity outputs -- These are required if whichever duplicator you're using does not do entity modifiers before it runs PostEntityPaste -- because if so, the wirelink and entity outputs may not have been created yet if input.SrcId == "link" or input.SrcId == "wirelink" then -- If the target entity has no wirelink output, create one (& more old dupe compatibility) input.SrcId = "wirelink" if not ent2.extended then WireLib.CreateWirelinkOutput( ply, ent2, {true} ) end elseif input.SrcId == "entity" and ((ent2.Outputs and not ent2.Outputs.entity) or not ent2.Outputs) then -- if the input name is 'entity', and the target entity doesn't have that output... WireLib.CreateEntityOutput( ply, ent2, {true} ) end -- Output alias if ent2.Outputs and not ent2.Outputs[input.SrcId] then -- if the target entity has any outputs and the output 'input.SrcId' is not one of them... if ent2.OutputAliases and ent2.OutputAliases[input.SrcId] then input.SrcId = ent2.OutputAliases[input.SrcId] else Msg("ApplyDupeInfo: Error, Could not find output '" .. input.SrcId .. "' on entity type: '" .. ent2:GetClass() .. "'\n") continue end end end WireLib.Link_Start(idx, ent, input.StartPos, k, input.Material, input.Color, input.Width) if input.Path then for _,v in ipairs(input.Path) do local ent2 = GetEntByID(v.Entity) if IsValid(ent2) then WireLib.Link_Node(idx, ent2, v.Pos) else Msg("ApplyDupeInfo: Error, Could not find the entity for wire path\n") end end end if IsValid(ent2) then WireLib.Link_End(idx, ent2, input.SrcPos, input.SrcId) else Msg("ApplyDupeInfo: Error, Could not find the output entity\n") end end end end function WireLib.RefreshSpecialOutputs(ent) local names = {} local types = {} local descs = {} if ent.Outputs then for _,output in pairs(ent.Outputs) do local index = output.Num names[index] = output.Name types[index] = output.Type descs[index] = output.Desc end ent.Outputs = WireLib.AdjustSpecialOutputs(ent, names, types, descs) else ent.Outputs = WireLib.CreateSpecialOutputs(ent, names, types, descs) end WireLib.TriggerOutput(ent, "link", ent) end function WireLib.CreateInputs(ent, names, descs) return WireLib.CreateSpecialInputs(ent, names, {}, descs) end function WireLib.CreateOutputs(ent, names, descs) return WireLib.CreateSpecialOutputs(ent, names, {}, descs) end function WireLib.AdjustInputs(ent, names, descs) return WireLib.AdjustSpecialInputs(ent, names, {}, descs) end function WireLib.AdjustOutputs(ent, names, descs) return WireLib.AdjustSpecialOutputs(ent, names, {}, descs) end -- Backwards compatibility Wire_CreateInputs = WireLib.CreateInputs Wire_CreateOutputs = WireLib.CreateOutputs Wire_AdjustInputs = WireLib.AdjustInputs Wire_AdjustOutputs = WireLib.AdjustOutputs Wire_Restored = WireLib.Restored Wire_Remove = WireLib.Remove Wire_TriggerOutput = WireLib.TriggerOutput Wire_Link_Start = WireLib.Link_Start Wire_Link_Node = WireLib.Link_Node Wire_Link_End = WireLib.Link_End Wire_Link_Cancel = WireLib.Link_Cancel Wire_Link_Clear = WireLib.Link_Clear Wire_CreateOutputIterator = WireLib.CreateOutputIterator Wire_BuildDupeInfo = WireLib.BuildDupeInfo Wire_ApplyDupeInfo = WireLib.ApplyDupeInfo -- prevent applyForce+Anti-noclip-based killing contraptions hook.Add("InitPostEntity", "antiantinoclip", function() local ENT = scripted_ents.GetList().rt_antinoclip_handler if not ENT then return end ENT = ENT.t local rt_antinoclip_handler_StartTouch = ENT.StartTouch function ENT:StartTouch(...) if self.speed >= 20 then return end local phys = self.Ent:GetPhysicsObject() if phys:IsValid() and phys:GetAngleVelocity():Length() > 20 then return end rt_antinoclip_handler_StartTouch(self, ...) end --local rt_antinoclip_handler_Think = ENT.Think function ENT:Think() local t = CurTime() local dt = t-self.lastt self.lastt = t local phys = self.Ent:GetPhysicsObject() local pos if phys:IsValid() then pos = phys:LocalToWorld(phys:GetMassCenter()) else pos = self.Ent:GetPos() end self.speed = pos:Distance(self.oldpos)/dt self.oldpos = pos --rt_antinoclip_handler_Think(self, ...) end ENT.speed = 20 ENT.lastt = 0 ENT.oldpos = Vector(0,0,0) end) function WireLib.GetOwner(ent) return E2Lib.getOwner({}, ent) end function WireLib.NumModelSkins(model) if NumModelSkins then return NumModelSkins(model) end local info = util.GetModelInfo(model) return info and info.SkinCount end --- @return whether the given player can spawn an object with the given model and skin function WireLib.CanModel(player, model, skin) if not util.IsValidModel(model) then return false end if skin ~= nil then local count = WireLib.NumModelSkins(model) if skin < 0 or (count and skin >= count) then return false end end if IsValid(player) and player:IsPlayer() and not hook.Run("PlayerSpawnObject", player, model, skin) then return false end return true end function WireLib.MakeWireEnt( pl, Data, ... ) Data.Class = scripted_ents.Get(Data.Class).ClassName if IsValid(pl) and not pl:CheckLimit(Data.Class:sub(6).."s") then return false end if Data.Model and not WireLib.CanModel(pl, Data.Model, Data.Skin) then return false end local ent = ents.Create( Data.Class ) if not IsValid(ent) then return false end duplicator.DoGeneric( ent, Data ) ent:Spawn() ent:Activate() duplicator.DoGenericPhysics( ent, pl, Data ) -- Is deprecated, but is the only way to access duplicator.EntityPhysics.Load (its local) ent:SetPlayer(pl) if ent.Setup then ent:Setup(...) end if IsValid(pl) then pl:AddCount( Data.Class:sub(6).."s", ent ) end local phys = ent:GetPhysicsObject() if IsValid(phys) then if Data.frozen then phys:EnableMotion(false) end if Data.nocollide then phys:EnableCollisions(false) end end return ent end -- Adds an input alias so that we can rename inputs on entities without breaking old dupes -- Usage: WireLib.AddInputAlias( old, new ) works if used in the entity's file -- or WireLib.AddInputAlias( class, old, new ) if used elsewhere -- or WireLib.AddInputAlias( entity, old, new ) for a specific entity function WireLib.AddInputAlias( class, old, new ) if not new then new = old old = class class = nil end local ENT_table if not class and ENT then ENT_table = ENT elseif isstring( class ) then ENT_table = scripted_ents.GetStored( class ) elseif isentity( class ) and IsValid( class ) then ENT_table = class else error( "Invalid class or entity specified" ) return end if not ENT_table.InputAliases then ENT_table.InputAliases = {} end ENT_table.InputAliases[old] = new end -- Adds an output alias so that we can rename outputs on entities without breaking old dupes -- Usage: WireLib.AddOutputAlias( old, new ) works if used in the entity's file -- or WireLib.AddOutputAlias( class, old, new ) if used elsewhere -- or WireLib.AddOutputAlias( entity, old, new ) for a specific entity function WireLib.AddOutputAlias( class, old, new ) if not new then new = old old = class class = nil end local ENT_table if not class and ENT then ENT_table = ENT elseif isstring( class ) then ENT_table = scripted_ents.GetStored( class ) elseif isentity( class ) and IsValid( class ) then ENT_table = class else error( "Invalid class or entity specified" ) return end if not ENT_table.OutputAliases then ENT_table.OutputAliases = {} end ENT_table.OutputAliases[old] = new end local function effectiveMass(ent) if not isentity(ent) then return 1 end if ent:IsWorld() then return 99999 end if not IsValid(ent) or not IsValid(ent:GetPhysicsObject()) then return 1 end return ent:GetPhysicsObject():GetMass() end function WireLib.CalcElasticConsts(Ent1, Ent2) local minMass = math.min(effectiveMass(Ent1), effectiveMass(Ent2)) local const = minMass * 100 local damp = minMass * 20 return const, damp end -- Returns a string like "Git f3a4ac3" or "SVN 2703" or "Workshop" or "Extracted" -- The partial git hash can be plugged into https://github.com/wiremod/wire/commit/f3a4ac3 to show the actual commit local cachedversion function WireLib.GetVersion() -- If we've already found our version just return that again if cachedversion then return cachedversion end -- Find what our legacy folder is called local wirefolder = "addons/wire" if not file.Exists(wirefolder, "GAME") then for k, folder in pairs(({file.Find("addons/*", "GAME")})[2]) do if folder:find("wire") and not folder:find("extra") then wirefolder = "addons/"..folder break end end end if file.Exists(wirefolder, "GAME") then if file.Exists(wirefolder.."/.git", "GAME") then cachedversion = "Git "..(file.Read(wirefolder.."/.git/refs/heads/master", "GAME") or "Unknown"):sub(1,7) elseif file.Exists(wirefolder.."/.svn", "GAME") then -- Note: This method will likely only detect TortoiseSVN installs local wcdb = file.Read(wirefolder.."/.svn/wc.db", "GAME") or "" local start = wcdb:find("/wiremod/wire/!svn/ver/%d+/branches%)") if start then cachedversion = "SVN "..wcdb:sub(start+23, start+26) else cachedversion = "SVN Unknown" end else cachedversion = "Extracted" end end -- Check if we're Workshop version first for k, addon in pairs(engine.GetAddons()) do if addon.wsid == "160250458" then cachedversion = "Workshop" return cachedversion end end if not cachedversion then cachedversion = "Unknown" end return cachedversion end concommand.Add("wireversion", function(ply,cmd,args) local text = "Wiremod's version: '"..WireLib.GetVersion().."'" if IsValid(ply) then ply:ChatPrint(text) else print(text) end end, nil, "Prints the server's Wiremod version") local material_blacklist = { ["engine/writez"] = true, ["pp/copy"] = true, ["effects/ar2_altfire1"] = true } function WireLib.IsValidMaterial(material) material = string.sub(material, 1, 260) local path = string.StripExtension(string.GetNormalizedFilepath(string.lower(material))) if material_blacklist[path] then return "" end return material end function WireLib.SetColor(ent, color) color.r = math_clamp(color.r, 0, 255) color.g = math_clamp(color.g, 0, 255) color.b = math_clamp(color.b, 0, 255) color.a = ent:IsPlayer() and ent:GetColor().a or math_clamp(color.a, 0, 255) local rendermode = ent:GetRenderMode() if rendermode == RENDERMODE_NORMAL or rendermode == RENDERMODE_TRANSALPHA then rendermode = color.a == 255 and RENDERMODE_NORMAL or RENDERMODE_TRANSALPHA ent:SetRenderMode(rendermode) else rendermode = nil -- Don't modify the current stored modifier end ent:SetColor(color) duplicator.StoreEntityModifier(ent, "colour", { Color = color, RenderMode = rendermode }) end if not WireLib.PatchedDuplicator then WireLib.PatchedDuplicator = true local localPos local oldSetLocalPos = duplicator.SetLocalPos function duplicator.SetLocalPos(pos, ...) localPos = pos return oldSetLocalPos(pos, ...) end local oldPaste = duplicator.Paste function duplicator.Paste(player, entityList, constraintList, ...) local result = { oldPaste(player, entityList, constraintList, ...) } local createdEntities, createdConstraints = result[1], result[2] local data = { EntityList = entityList, ConstraintList = constraintList, CreatedEntities = createdEntities, CreatedConstraints = createdConstraints, Player = player, HitPos = localPos, } hook.Run("AdvDupe_FinishPasting", {data}, 1) return unpack(result) end end