605 lines
19 KiB
Lua
605 lines
19 KiB
Lua
|
|
AddCSLuaFile()
|
|
|
|
DEFINE_BASECLASS( "base_wire_entity" )
|
|
|
|
ENT.PrintName = "ACF Engine Flywheel"
|
|
ENT.WireDebugName = "ACF Engine Flywheel"
|
|
|
|
if CLIENT then
|
|
|
|
local ACFCUSTOM_EngineFlyInfoWhileSeated = CreateClientConVar("ACFCUSTOM_EngineFlyInfoWhileSeated", 0, true, false)
|
|
|
|
-- copied from base_wire_entity: DoNormalDraw's notip arg isn't accessible from ENT:Draw defined there.
|
|
function ENT:Draw()
|
|
local lply = LocalPlayer()
|
|
local hideBubble = not GetConVar("ACFCUSTOM_EngineFlyInfoWhileSeated"):GetBool() and IsValid(lply) and lply:InVehicle()
|
|
|
|
self.BaseClass.DoNormalDraw(self, false, hideBubble)
|
|
Wire_Render(self)
|
|
|
|
if self.GetBeamLength and (not self.GetShowBeam or self:GetShowBeam()) then
|
|
-- Every SENT that has GetBeamLength should draw a tracer. Some of them have the GetShowBeam boolean
|
|
Wire_DrawTracerBeam( self, 1, self.GetBeamHighlight and self:GetBeamHighlight() or false )
|
|
end
|
|
end
|
|
|
|
function ACFEngineFlyGUICreate( Table )
|
|
|
|
if not acfmenupanelcustom.ModData then
|
|
acfmenupanelcustom.ModData = {}
|
|
end
|
|
if not acfmenupanelcustom.ModData[Table.id] then
|
|
acfmenupanelcustom.ModData[Table.id] = {}
|
|
acfmenupanelcustom.ModData[Table.id].ModTable = Table.modtable
|
|
end
|
|
|
|
acfmenupanelcustom:CPanelText("Name", Table.name)
|
|
|
|
acfmenupanelcustom.CData.DisplayModel = vgui.Create( "DModelPanel", acfmenupanelcustom.CustomDisplay )
|
|
acfmenupanelcustom.CData.DisplayModel:SetModel( Table.model )
|
|
acfmenupanelcustom.CData.DisplayModel:SetCamPos( Vector( 250, 500, 250 ) )
|
|
acfmenupanelcustom.CData.DisplayModel:SetLookAt( Vector( 0, 0, 0 ) )
|
|
acfmenupanelcustom.CData.DisplayModel:SetFOV( 8 )
|
|
acfmenupanelcustom.CData.DisplayModel:SetSize(acfmenupanelcustom:GetWide(),acfmenupanelcustom:GetWide())
|
|
acfmenupanelcustom.CData.DisplayModel.LayoutEntity = function( panel, entity ) end
|
|
acfmenupanelcustom.CustomDisplay:AddItem( acfmenupanelcustom.CData.DisplayModel )
|
|
|
|
acfmenupanelcustom:CPanelText("Desc", Table.desc)
|
|
|
|
for ID,Value in pairs(acfmenupanelcustom.ModData[Table.id]["ModTable"]) do
|
|
ACF_FlySlider(ID, Value, Table.id)
|
|
end
|
|
|
|
acfmenupanelcustom.CustomDisplay:PerformLayout()
|
|
|
|
end
|
|
|
|
function ACF_FlySlider(Mod, Value, ID)
|
|
if Mod and not acfmenupanelcustom.CData[Mod] then
|
|
acfmenupanelcustom.CData[Mod] = vgui.Create( "DNumSlider", acfmenupanelcustom.CustomDisplay )
|
|
acfmenupanelcustom.CData[Mod]:SetWide(100)
|
|
if Mod == 1 then
|
|
acfmenupanelcustom.CData[Mod]:SetText("Idle RPM")
|
|
acfmenupanelcustom.CData[Mod]:SetMin( 5 )
|
|
acfmenupanelcustom.CData[Mod]:SetMax( 1200 )
|
|
acfmenupanelcustom.CData[Mod]:SetDecimals( 0 )
|
|
elseif Mod == 2 then
|
|
acfmenupanelcustom.CData[Mod]:SetText("FlywheelMass")
|
|
acfmenupanelcustom.CData[Mod]:SetMin( 0.001 )
|
|
acfmenupanelcustom.CData[Mod]:SetMax( 5 )
|
|
acfmenupanelcustom.CData[Mod]:SetDecimals( 3 )
|
|
end
|
|
acfmenupanelcustom.CData[Mod]["Mod"] = Mod
|
|
acfmenupanelcustom.CData[Mod]["ID"] = ID
|
|
acfmenupanelcustom.CData[Mod]:SetValue(Value)
|
|
acfmenupanelcustom.CData[Mod]:SetDark( true )
|
|
RunConsoleCommand( "acfcustom_data"..Mod, Value )
|
|
acfmenupanelcustom.CData[Mod].OnValueChanged = function( slider, val )
|
|
acfmenupanelcustom.ModData[slider.ID]["ModTable"][slider.Mod] = val
|
|
RunConsoleCommand( "acfcustom_data"..Mod, val )
|
|
end
|
|
acfmenupanelcustom.CustomDisplay:AddItem( acfmenupanelcustom.CData[Mod] )
|
|
end
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
--###################################################
|
|
--##### END CL_INIT #####
|
|
--###################################################
|
|
|
|
function ENT:Initialize()
|
|
self.IsMaster = true
|
|
self.GearLink = {} -- a "Link" has these components: Ent, Rope, RopeLen, ReqTq, ReqTq2
|
|
|
|
self.LastCheck = 0
|
|
self.LastThink = 0
|
|
self.MassRatio = 1
|
|
self.Legal = true
|
|
self.CanUpdate = true
|
|
|
|
-- Manual Gearbox Vars
|
|
self.ManualGearbox = false
|
|
self.GearboxCurrentGear = 0
|
|
|
|
self.Outputs = WireLib.CreateSpecialOutputs( self, { "RPM", "Back Force", "Entity", "Mass", "Physical Mass" }, { "NORMAL", "NORMAL", "ENTITY", "NORMAL", "NORMAL" } )
|
|
Wire_TriggerOutput( self, "Entity", self )
|
|
Wire_CreateInputs( self, { "Throttle", "RPM", "IdleRPM", "FlywheelMass" } )
|
|
self.WireDebugName = "ACF Engine Flywheel"
|
|
end
|
|
|
|
--###################################################
|
|
--##### MAKE ENGINE #####
|
|
--###################################################
|
|
|
|
function MakeACF_EngineFly(Owner, Pos, Angle, Id, Data1, Data2)
|
|
|
|
if not Owner:CheckLimit("_acf_misc") then return false end
|
|
|
|
local Engine = ents.Create( "acf_engine_flywheel" )
|
|
if not IsValid( Engine ) then return false end
|
|
|
|
local EID
|
|
local List = list.Get("ACFCUSTOMEnts")
|
|
if List.MobilityCustom[Id] then EID = Id else EID = "Fly-Small" end
|
|
local Lookup = List.MobilityCustom[EID]
|
|
|
|
Engine:SetAngles(Angle)
|
|
Engine:SetPos(Pos)
|
|
Engine:Spawn()
|
|
Engine:SetPlayer(Owner)
|
|
Engine.Owner = Owner
|
|
Engine.Id = EID
|
|
|
|
Engine.Model = Lookup.model
|
|
Engine.Weight = Lookup.weight
|
|
|
|
Engine.ModTable = Lookup.modtable
|
|
Engine.ModTable[1] = Data1
|
|
Engine.ModTable[2] = Data2
|
|
|
|
--Set Mods Table (Used for Duplicator)
|
|
Engine.Mod1 = Data1
|
|
Engine.Mod2 = Data2
|
|
|
|
if(tonumber(Data1) != nil and tonumber(Data1) >= 10) then Engine.IdleRPM = tonumber(Data1) else Engine.IdleRPM = 10 end
|
|
if(tonumber(Data2) != nil and tonumber(Data2) >= 0.001) then Engine.FlywheelMass = tonumber(Data2) else Engine.FlywheelMass = 0.001 end
|
|
|
|
Engine.Inertia = Engine.FlywheelMass*(3.1416)^2
|
|
|
|
--Create Normal Vars
|
|
Engine.FlywheelMassNormal = Engine.FlywheelMass
|
|
Engine.IdleRPMNormal = Engine.IdleRPM
|
|
|
|
Engine.FlyRPM = 0
|
|
Engine:SetModel( Engine.Model )
|
|
Engine.RPM = {}
|
|
|
|
Engine:PhysicsInit( SOLID_VPHYSICS )
|
|
Engine:SetMoveType( MOVETYPE_VPHYSICS )
|
|
Engine:SetSolid( SOLID_VPHYSICS )
|
|
|
|
Engine.Out = Engine:WorldToLocal(Engine:GetPos())
|
|
|
|
local phys = Engine:GetPhysicsObject()
|
|
if IsValid( phys ) then
|
|
phys:SetMass( Engine.Weight )
|
|
end
|
|
|
|
Engine:SetNWString( "WireName", Lookup.name )
|
|
------ GUI ---------
|
|
Engine:UpdateOverlayText()
|
|
|
|
Owner:AddCount("_acf_engine_flywheel", Engine)
|
|
Owner:AddCleanup( "acfcustom", Engine )
|
|
|
|
Engine:ACFInit()
|
|
|
|
return Engine
|
|
end
|
|
list.Set( "ACFCvars", "acf_engine_flywheel", {"id", "data1", "data2"} )
|
|
duplicator.RegisterEntityClass("acf_engine_flywheel", MakeACF_EngineFly, "Pos", "Angle", "Id", "Mod1", "Mod2")
|
|
|
|
--###################################################
|
|
--##### UPDATE ENGINE #####
|
|
--###################################################
|
|
|
|
function ENT:Update( ArgsTable )
|
|
-- That table is the player data, as sorted in the ACFCvars above, with player who shot,
|
|
-- and pos and angle of the tool trace inserted at the start
|
|
|
|
if ArgsTable[1] ~= self.Owner then -- Argtable[1] is the player that shot the tool
|
|
return false, "You don't own that engine!"
|
|
end
|
|
|
|
local Id = ArgsTable[4] -- Argtable[4] is the engine ID
|
|
local Lookup = list.Get("ACFCUSTOMEnts").MobilityCustom[Id]
|
|
|
|
if Lookup.model ~= self.Model then
|
|
return false, "The flywheel must have the same model!"
|
|
end
|
|
|
|
self.Id = Id
|
|
self.Weight = Lookup.weight
|
|
|
|
self.ModTable[1] = ArgsTable[5]
|
|
self.ModTable[2] = ArgsTable[6]
|
|
|
|
--Set Mods Table (Used for Duplicator)
|
|
self.Mod1 = ArgsTable[5]
|
|
self.Mod2 = ArgsTable[6]
|
|
|
|
if(tonumber(ArgsTable[5]) != nil and tonumber(ArgsTable[5]) >= 10) then self.IdleRPM = tonumber(ArgsTable[5]) else self.IdleRPM = 10 end
|
|
if(tonumber(ArgsTable[6]) != nil and tonumber(ArgsTable[6]) >= 0.001) then self.FlywheelMass = tonumber(ArgsTable[6]) else self.FlywheelMass = 0.001 end
|
|
|
|
self.Inertia = self.FlywheelMass*(3.1416)^2
|
|
|
|
--Create Normal Vars
|
|
self.FlywheelMassNormal = self.FlywheelMass
|
|
self.IdleRPMNormal = self.IdleRPM
|
|
|
|
self:SetModel( self.Model )
|
|
self:SetSolid( SOLID_VPHYSICS )
|
|
self.Out = self:WorldToLocal(self:GetPos())
|
|
|
|
local phys = self:GetPhysicsObject()
|
|
if IsValid( phys ) then
|
|
phys:SetMass( self.Weight )
|
|
end
|
|
|
|
self:SetNWString( "WireName", Lookup.name )
|
|
------ GUI ---------
|
|
self:UpdateOverlayText()
|
|
|
|
self:ACFInit()
|
|
|
|
return true, "Flywheel updated successfully!"
|
|
end
|
|
|
|
--###################################################
|
|
--##### FUNCTIONS #####
|
|
--###################################################
|
|
|
|
function ENT:UpdateOverlayText()
|
|
local text = "Idle: " .. math.Round(self.IdleRPM) .. " RPM\n"
|
|
text = text .. "FlywheelMass: " .. math.Round(self.FlywheelMass,3) .. " Kg\n"
|
|
text = text .. "Inertia: " .. math.Round(self.Inertia,3) .. "\n"
|
|
text = text .. "RPM: " .. math.Round(self.FlyRPM) .. " RPM\n"
|
|
text = text .. "Weight: " .. math.Round(self.Weight) .. "Kg"
|
|
|
|
self:SetOverlayText( text )
|
|
end
|
|
|
|
--###################################################
|
|
--##### TRIGGER INPUTS #####
|
|
--###################################################
|
|
|
|
function ENT:TriggerInput( iname, value )
|
|
if (iname == "Throttle") then
|
|
if (value > 0 and self.Throttle != value) then
|
|
self.Throttle = value
|
|
if self.Throttle > 1 then self.Throttle = 1 end
|
|
elseif (value <= 0 and self.Throttle != 0) then
|
|
self.Throttle = 0
|
|
end
|
|
elseif (iname == "RPM") then
|
|
if (value > 0 and self.FlyRPM != value) then
|
|
self.FlyRPM = value
|
|
elseif (value <= 0 and self.FlyRPM != 0) then
|
|
self.FlyRPM = 0
|
|
end
|
|
elseif (iname == "IdleRPM") then
|
|
if (value > 0 and self.IdleRPM != value) then
|
|
self.IdleRPM = value
|
|
elseif (value <= 0 and self.IdleRPM != self.IdleRPMNormal) then
|
|
self.IdleRPM = self.IdleRPMNormal
|
|
end
|
|
elseif (iname == "FlywheelMass") then
|
|
if (value > 0 and self.FlywheelMass != value) then
|
|
self.FlywheelMass = value
|
|
self.Inertia = self.FlywheelMass*(3.1416)^2
|
|
elseif (value <= 0 and self.FlywheelMass != self.FlywheelMassNormal) then
|
|
self.FlywheelMass = self.FlywheelMassNormal
|
|
self.Inertia = self.FlywheelMass*(3.1416)^2
|
|
end
|
|
end
|
|
end
|
|
|
|
function ENT:Think()
|
|
local Time = CurTime()
|
|
|
|
if self.Legal then
|
|
self:CalcRPM()
|
|
end
|
|
|
|
if self.LastCheck < CurTime() then
|
|
self:CheckRopes()
|
|
self:CalcMassRatio()
|
|
self.Legal = self:CheckLegal()
|
|
|
|
self.LastCheck = Time + math.Rand(5, 10)
|
|
end
|
|
|
|
self.LastThink = Time
|
|
self:NextThink( Time )
|
|
return true
|
|
end
|
|
|
|
function ENT:CheckLegal()
|
|
--make sure it's not invisible to traces
|
|
if not self:IsSolid() then return false end
|
|
-- make sure weight is not below stock
|
|
if self:GetPhysicsObject():GetMass() < self.Weight then return false end
|
|
|
|
local rootparent = self:GetParent()
|
|
|
|
-- if it's not parented we're fine
|
|
if not IsValid( rootparent ) then return true end
|
|
--find the root parent
|
|
local depth = 0
|
|
while rootparent:GetParent():IsValid() and depth<5 do
|
|
depth = depth + 1
|
|
rootparent = rootparent:GetParent()
|
|
end
|
|
|
|
--if there's still more parents, it's not legal
|
|
if rootparent:GetParent():IsValid() then return false end
|
|
--make sure it's welded to root parent
|
|
for k, v in pairs( constraint.FindConstraints( self, "Weld" ) ) do
|
|
if v.Ent1 == rootparent or v.Ent2 == rootparent then return true end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function ENT:CalcMassRatio()
|
|
local Mass = 0
|
|
local PhysMass = 0
|
|
|
|
-- get the shit that is physically attached to the vehicle
|
|
local PhysEnts = ACF_GetAllPhysicalConstraints( self )
|
|
|
|
-- add any parented but not constrained props you sneaky bastards
|
|
local AllEnts = table.Copy( PhysEnts )
|
|
for k, v in pairs( PhysEnts ) do
|
|
table.Merge( AllEnts, ACF_GetAllChildren( v ) )
|
|
end
|
|
|
|
for k, v in pairs( AllEnts ) do
|
|
if not IsValid( v ) then continue end
|
|
|
|
local phys = v:GetPhysicsObject()
|
|
if not IsValid( phys ) then continue end
|
|
|
|
Mass = Mass + phys:GetMass()
|
|
|
|
if PhysEnts[ v ] then
|
|
PhysMass = PhysMass + phys:GetMass()
|
|
end
|
|
end
|
|
|
|
self.MassRatio = PhysMass / Mass
|
|
|
|
Wire_TriggerOutput( self, "Mass", math.Round( Mass, 2 ) )
|
|
Wire_TriggerOutput( self, "Physical Mass", math.Round( PhysMass, 2 ) )
|
|
end
|
|
|
|
function ENT:ACFInit()
|
|
self:CalcMassRatio()
|
|
|
|
self.LastThink = CurTime()
|
|
self.FlyRPM = 0
|
|
end
|
|
|
|
function ENT:CalcRPM()
|
|
local DeltaTime = CurTime() - self.LastThink
|
|
|
|
--Set FlyRPM by object rotation
|
|
/*local phys = self:GetPhysicsObject()
|
|
if phys:IsValid() then
|
|
self.FlyRPM = math.floor(((phys:GetAngleVelocity().z)/10)*1.6)
|
|
end
|
|
|
|
--Reset Negative RPM
|
|
self.inverted = false
|
|
if self.FlyRPM < 0 then
|
|
self.FlyRPM = -self.FlyRPM
|
|
self.inverted = true
|
|
end*/
|
|
|
|
--Set TorqueDiff
|
|
local TorqueDiff = math.max( self.FlyRPM - self.IdleRPM, 0 ) * self.Inertia
|
|
--Reset TorqueDiff for Manual Gearbox
|
|
if (self.ManualGearbox) then
|
|
TorqueDiff = math.max(self.FlyRPM + self.IdleRPM, 0) * self.Inertia
|
|
end
|
|
|
|
-- The gearboxes don't think on their own, it's the engine that calls them, to ensure consistent execution order
|
|
local Boxes = table.Count( self.GearLink )
|
|
local TotalReqTq = 0
|
|
local TotalReqTq2 = 0 --Used for Manual Gearbox (Aka Engine Back-Force)
|
|
|
|
-- Get the requirements for torque for the gearboxes (Max clutch rating minus any wheels currently spinning faster than the Flywheel)
|
|
for Key, Link in pairs( self.GearLink ) do
|
|
if not Link.Ent.Legal then continue end
|
|
-- Set accelerating engine gearbox Back-Force
|
|
Link.ReqTq = Link.Ent:Calc( self.FlyRPM, self.Inertia )
|
|
TotalReqTq = TotalReqTq + Link.ReqTq
|
|
if self.ManualGearbox then
|
|
-- Set deccelerating engine gearbox Back-Force (Manual Gearbox)
|
|
Link.ReqTq2 = Link.Ent:Calc2( self.FlyRPM, self.Inertia )
|
|
TotalReqTq2 = TotalReqTq2 + Link.ReqTq2
|
|
end
|
|
end
|
|
|
|
-- Split the torque fairly between the gearboxes who need it
|
|
for Key, Link in pairs( self.GearLink ) do
|
|
if not Link.Ent.Legal then continue end
|
|
-- Calculate the ratio of total requested torque versus what's avaliable
|
|
local AvailRatio = math.min( TorqueDiff / TotalReqTq / Boxes, 1 )
|
|
self.GearboxCurrentGear = Link.Ent.Gear
|
|
-- Reset AvailRatio (Manual Gearbox)
|
|
if (self.ManualGearbox and self.Throttle == 0 and self.GearboxCurrentGear != 0 and self.FlyRPM > self.IdleRPM) then
|
|
AvailRatio = 0
|
|
end
|
|
-- Set the Torque On Gearboxes
|
|
Link.Ent:Act( Link.ReqTq * AvailRatio * self.MassRatio, DeltaTime, self.MassRatio )
|
|
end
|
|
|
|
-- Remove RPM with Gearbox Velocity (Engine Back Force)
|
|
//self:ApplyTorque(math.min(TorqueDiff, TotalReqTq)/self.Inertia, true, DeltaTime)
|
|
Wire_TriggerOutput(self, "Back Force", -(math.min(TorqueDiff, TotalReqTq)/self.Inertia))
|
|
|
|
-- Add RPM (Manual Gearbox)(Engine Back Force)
|
|
if self.ManualGearbox then
|
|
if self.Throttle == 0 and self.GearboxCurrentGear != 0 then
|
|
//self:ApplyTorque(TotalReqTq2/self.Inertia, false, DeltaTime)
|
|
Wire_TriggerOutput(self, "Back Force", (TotalReqTq2/self.Inertia))
|
|
end
|
|
end
|
|
|
|
self:UpdateOverlayText()
|
|
Wire_TriggerOutput(self, "RPM", self.FlyRPM)
|
|
|
|
return
|
|
end
|
|
|
|
/*function ENT:ApplyTorque(Torque, Removal, DeltaTime)
|
|
local Phys = self:GetPhysicsObject()
|
|
local Force = self:GetForward() * Torque - self:GetForward()
|
|
if Removal then
|
|
if self.inverted then Phys:ApplyForceOffset( Force * -39.37 * DeltaTime, self:GetPos() + self:GetRight() * 39.37 )
|
|
else Phys:ApplyForceOffset( Force * 39.37 * DeltaTime, self:GetPos() + self:GetRight() * -39.37 ) end
|
|
else
|
|
if self.inverted then Phys:ApplyForceOffset( Force * 39.37 * DeltaTime, self:GetPos() + self:GetRight() * 39.37 )
|
|
else Phys:ApplyForceOffset( Force * -39.37 * DeltaTime, self:GetPos() + self:GetRight() * -39.37 ) end
|
|
end
|
|
end*/
|
|
|
|
function ENT:CheckRopes()
|
|
for Key, Link in pairs( self.GearLink ) do
|
|
local Ent = Link.Ent
|
|
|
|
local OutPos = self:LocalToWorld( self.Out )
|
|
local InPos = Ent:LocalToWorld( Ent.In )
|
|
|
|
-- make sure it is not stretched too far
|
|
if OutPos:Distance( InPos ) > Link.RopeLen * 1.5 then
|
|
self:Unlink( Ent )
|
|
end
|
|
|
|
-- make sure the angle is not excessive
|
|
local Direction = -self:GetUp()
|
|
|
|
local DrvAngle = ( OutPos - InPos ):GetNormalized():DotProduct( Direction )
|
|
if DrvAngle < 0.7 then
|
|
self:Unlink( Ent )
|
|
end
|
|
end
|
|
end
|
|
|
|
function ENT:Link( Target )
|
|
--Allowable Target
|
|
if not IsValid( Target ) or not table.HasValue( { "acf_gearbox", "acf_gearbox_cvt", "acf_gearbox_auto", "acf_gearbox_air", "acf_gearbox_manual" }, Target:GetClass() ) then
|
|
return false, "Can only link to gearboxes!"
|
|
end
|
|
|
|
-- Check if target is already linked
|
|
for Key, Link in pairs( self.GearLink ) do
|
|
if Link.Ent == Target then
|
|
return false, "That is already linked to this engine!"
|
|
end
|
|
end
|
|
|
|
-- make sure the angle is not excessive
|
|
local InPos = Target:LocalToWorld( Target.In )
|
|
local OutPos = self:LocalToWorld( self.Out )
|
|
|
|
local Direction = -self:GetUp()
|
|
|
|
local DrvAngle = ( OutPos - InPos ):GetNormalized():DotProduct( Direction )
|
|
if DrvAngle < 0.7 then
|
|
return false, "Cannot link due to excessive driveshaft angle!"
|
|
end
|
|
|
|
local Rope = nil
|
|
if self.Owner:GetInfoNum( "ACF_MobilityRopeLinks", 1) == 1 then
|
|
Rope = constraint.CreateKeyframeRope( OutPos, 1, "cable/cable2", nil, self, self.Out, 0, Target, Target.In, 0 )
|
|
end
|
|
|
|
local Link = {
|
|
Ent = Target,
|
|
Rope = Rope,
|
|
RopeLen = ( OutPos - InPos ):Length(),
|
|
ReqTq = 0,
|
|
ReqTq2 = 0 --used for manual gearbox, engine back-force torque
|
|
}
|
|
|
|
table.insert( self.GearLink, Link )
|
|
table.insert( Target.Master, self )
|
|
|
|
//Perform Manual Gearbox Checkup
|
|
if Target.isManual then
|
|
self.ManualGearbox = true
|
|
end
|
|
|
|
return true, "Link successful!"
|
|
end
|
|
|
|
function ENT:Unlink( Target )
|
|
--unlink gearboxes
|
|
for Key, Link in pairs( self.GearLink ) do
|
|
if Link.Ent == Target then
|
|
-- Remove any old physical ropes leftover from dupes
|
|
for Key, Rope in pairs( constraint.FindConstraints( Link.Ent, "Rope" ) ) do
|
|
if Rope.Ent1 == self or Rope.Ent2 == self then
|
|
Rope.Constraint:Remove()
|
|
end
|
|
end
|
|
|
|
if IsValid( Link.Rope ) then
|
|
Link.Rope:Remove()
|
|
end
|
|
|
|
table.remove( self.GearLink,Key )
|
|
|
|
-- Make Sure Manual Gearbox are disabled
|
|
self.ManualGearbox = false
|
|
|
|
return true, "Unlink successful!"
|
|
end
|
|
end
|
|
return false, "That gearbox is not linked to this engine!"
|
|
end
|
|
|
|
function ENT:PreEntityCopy()
|
|
//Link Saving
|
|
local info = {}
|
|
local entids = {}
|
|
for Key, Link in pairs( self.GearLink ) do --First clean the table of any invalid entities
|
|
if not IsValid( Link.Ent ) then
|
|
table.remove( self.GearLink, Key )
|
|
end
|
|
end
|
|
for Key, Link in pairs( self.GearLink ) do --Then save it
|
|
table.insert( entids, Link.Ent:EntIndex() )
|
|
end
|
|
|
|
info.entities = entids
|
|
if info.entities then
|
|
duplicator.StoreEntityModifier( self, "GearLink", info )
|
|
end
|
|
|
|
//Wire dupe info
|
|
self.BaseClass.PreEntityCopy( self )
|
|
end
|
|
|
|
function ENT:PostEntityPaste( Player, Ent, CreatedEntities )
|
|
//Link Pasting
|
|
if (Ent.EntityMods) and (Ent.EntityMods.GearLink) and (Ent.EntityMods.GearLink.entities) then
|
|
local GearLink = Ent.EntityMods.GearLink
|
|
if GearLink.entities and table.Count(GearLink.entities) > 0 then
|
|
timer.Simple( 0, function() -- this timer is a workaround for an ad2/makespherical issue https://github.com/nrlulz/ACF/issues/14#issuecomment-22844064
|
|
for _,ID in pairs(GearLink.entities) do
|
|
local Linked = CreatedEntities[ ID ]
|
|
if IsValid( Linked ) then
|
|
self:Link( Linked )
|
|
end
|
|
end
|
|
end )
|
|
end
|
|
Ent.EntityMods.GearLink = nil
|
|
end
|
|
|
|
//Wire dupe info
|
|
self.BaseClass.PostEntityPaste( self, Player, Ent, CreatedEntities )
|
|
end
|
|
|
|
function ENT:OnRemove()
|
|
if self.Sound then
|
|
self.Sound:Stop()
|
|
end
|
|
end
|