AddCSLuaFile() DEFINE_BASECLASS( "base_wire_entity" ) ENT.PrintName = "ACF Engine Maker" ENT.WireDebugName = "ACF Engine Maker" if CLIENT then local ACFCUSTOM_EngineMakerInfoWhileSeated = CreateClientConVar("ACFCUSTOM_EngineMakerInfoWhileSeated", 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_EngineMakerInfoWhileSeated"):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 ACFEngineMakerGUICreate( 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:CPanelText("Desc", "Desc : "..Table.desc) for ID,Value in pairs(acfmenupanelcustom.ModData[Table.id]["ModTable"]) do if ID == 1 then ACF_ModingEngine() end end acfmenupanelcustom.CustomDisplay:PerformLayout() end function ACF_ModingEngine( ) --Set vars local NameLoad = "No Name" local SoundLoad = "No Sound" local ModelLoad = "models/engines/inline4s.mdl" local FuelTypeLoad = "Petrol" local EngineTypeLoad = "GenericPetrol" local TorqueLoad = 1 local IdleLoad = 1 local PeakMinLoad = 1 local PeakMaxLoad = 1 local LimitRpmLoad = 1 local FlywheelLoad = 1 local WeightLoad = 1 local EngineSizeText = 1 local EngineTypeText = 1 local IselectText = "false" local IsTransText = "false" local FlywheelOverNumber = 1 if not file.Exists("acf/lastengine.txt", "DATA") then local txt = NameLoad..","..SoundLoad..","..ModelLoad..","..FuelTypeLoad..","..EngineTypeLoad..","..TorqueLoad.."," txt = txt ..IdleLoad..","..PeakMinLoad..","..PeakMaxLoad..","..LimitRpmLoad..","..FlywheelLoad..","..WeightLoad.."," txt = txt ..EngineSizeText..","..EngineTypeText..","..IselectText..","..IsTransText..","..FlywheelOverNumber file.CreateDir("acf") file.CreateDir("acf/custom.engines") file.Write("acf/lastengine.txt", txt) acfmenupanelcustom.CustomDisplay:PerformLayout() end --Start code local wide = acfmenupanelcustom.CustomDisplay:GetWide() Open = vgui.Create("DButton") Open:SetText("Open Engine Menu") Open:SetTextColor(Color(ACFC.R,ACFC.G,ACFC.B,255)) Open:SetWide(wide) Open:SetTall(30) Open:SetVisible(true) Open.DoClick = function() RunConsoleCommand("acf_enginestart_open") end acfmenupanelcustom.CustomDisplay:AddItem(Open) DisplayModel = vgui.Create( "DModelPanel", acfmenupanelcustom.CustomDisplay ) DisplayModel:SetModel( ModelLoad ) DisplayModel:SetCamPos( Vector( 250, 500, 250 ) ) DisplayModel:SetLookAt( Vector( 0, 0, 0 ) ) DisplayModel:SetFOV( 20 ) DisplayModel:SetSize(acfmenupanelcustom:GetWide(),acfmenupanelcustom:GetWide()) DisplayModel.LayoutEntity = function( panel, entity ) end acfmenupanelcustom.CustomDisplay:AddItem( DisplayModel ) acfmenupanelcustom:CPanelText("EngName", "Engine Name : "..NameLoad) acfmenupanelcustom:CPanelText("EngSound", "Sound : "..SoundLoad) acfmenupanelcustom:CPanelText("EngModel", "Model : "..ModelLoad) acfmenupanelcustom:CPanelText("EngFuel", "Fuel Type : "..FuelTypeLoad) acfmenupanelcustom:CPanelText("EngType", "Engine Type : "..EngineTypeLoad) acfmenupanelcustom:CPanelText("EngTorque", "Torque : "..TorqueLoad) acfmenupanelcustom:CPanelText("EngIdle", "Idle Rpm : "..IdleLoad) acfmenupanelcustom:CPanelText("EngPeakMin", "Peak Min Rpm : "..PeakMinLoad) acfmenupanelcustom:CPanelText("EngPeakMax", "Peak Max Rpm : "..PeakMaxLoad) acfmenupanelcustom:CPanelText("EngLimit", "Limit Rpm : "..LimitRpmLoad) acfmenupanelcustom:CPanelText("EngFly", "Flywheel Mass : "..FlywheelLoad) acfmenupanelcustom:CPanelText("EngWeight", "Weight : "..WeightLoad) Help = vgui.Create("DButton") Help:SetToolTip("1.Create/Load a engine in the Engine menu\n2.Update the text by selecting again Engine Maker\n3.Spawn the Engine !") Help:SetTextColor(Color(0,150,0,255)) Help:SetWide(wide/2) Help:SetTall(30) Help:SetVisible(true) Help:SetText("Help") acfmenupanelcustom.CustomDisplay:AddItem(Help) --Reload Last Engine if file.Exists("acf/lastengine.txt", "DATA") then local LastEngineText = file.Read("acf/lastengine.txt") local EngT = {} for w in string.gmatch(LastEngineText, "([^,]+)") do table.insert(EngT, w) end RunConsoleCommand( "acfcustom_data1", EngT[1] ) RunConsoleCommand( "acfcustom_data2", EngT[2] ) RunConsoleCommand( "acfcustom_data3", EngT[3] ) RunConsoleCommand( "acfcustom_data4", EngT[4] ) RunConsoleCommand( "acfcustom_data5", EngT[5] ) RunConsoleCommand( "acfcustom_data6", EngT[6] ) RunConsoleCommand( "acfcustom_data7", EngT[7] ) RunConsoleCommand( "acfcustom_data8", EngT[8] ) RunConsoleCommand( "acfcustom_data9", EngT[9] ) RunConsoleCommand( "acfcustom_data10", EngT[10] ) RunConsoleCommand( "acfcustom_data11", EngT[11] ) RunConsoleCommand( "acfcustom_data12", EngT[12] ) RunConsoleCommand( "acfcustom_data13", EngT[15] ) RunConsoleCommand( "acfcustom_data14", EngT[16] ) RunConsoleCommand( "acfcustom_data15", EngT[17] ) acfmenupanelcustom:CPanelText("EngName", "Engine Name : "..EngT[1]) acfmenupanelcustom:CPanelText("EngSound", "Sound : "..EngT[2]) acfmenupanelcustom:CPanelText("EngModel", "Model : "..EngT[3]) acfmenupanelcustom:CPanelText("EngFuel", "Fuel Type : "..EngT[4]) acfmenupanelcustom:CPanelText("EngType", "Engine Type : "..EngT[5]) acfmenupanelcustom:CPanelText("EngTorque", "Torque : "..EngT[6]) acfmenupanelcustom:CPanelText("EngIdle", "Idle Rpm : "..EngT[7]) acfmenupanelcustom:CPanelText("EngPeakMin", "Peak Min Rpm : "..EngT[8]) acfmenupanelcustom:CPanelText("EngPeakMax", "Peak Max Rpm : "..EngT[9]) acfmenupanelcustom:CPanelText("EngLimit", "Limit Rpm : "..EngT[10]) acfmenupanelcustom:CPanelText("EngFly", "Flywheel Mass : "..EngT[11]) acfmenupanelcustom:CPanelText("EngWeight", "Weight : "..EngT[12]) DisplayModel:SetModel( EngT[3] ) acfmenupanelcustom.CustomDisplay:PerformLayout() end end return end --################################################### --##### END CL_INIT ##### --################################################### function ENT:Initialize() self.Throttle = 0 self.Active = false self.IsMaster = true self.GearLink = {} -- a "Link" has these components: Ent, Rope, RopeLen, ReqTq self.FuelLink = {} self.LastCheck = 0 self.LastThink = 0 self.MassRatio = 1 self.FuelTank = 0 self.Legal = true self.CanUpdate = true self.RequiresFuel = false self.Fuelusing = 0 -- Rev Limiter Vars self.CutMode = 0 self.CutRpm = 0 self.DisableCut = 0 self.DisableCutFinal = 0 -- Table Vars self.ExtraLink = {} self.RadiatorLink = {} -- Extra Vars self.PeakTorqueExtra = 0 self.RPMExtra = 0 self.ExtraUsing = 0 self.PeakTorqueHealth = 0 -- Manual Gearbox Vars self.ManualGearbox = false self.GearboxCurrentGear = 0 self.Outputs = WireLib.CreateSpecialOutputs( self, { "RPM", "Torque", "Power", "Fuel Use", "Entity", "Mass", "Physical Mass", "Running" }, { "NORMAL","NORMAL","NORMAL", "NORMAL", "ENTITY", "NORMAL", "NORMAL", "NORMAL" } ) Wire_TriggerOutput( self, "Entity", self ) self.WireDebugName = "ACF Engine Maker" end --################################################### --##### MAKE ENGINE ##### --################################################### function MakeACF_EngineMaker(Owner, Pos, Angle, Id, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, Data13, Data14, Data15) if not Owner:CheckLimit("_acf_maker") then return false end local Engine = ents.Create( "acf_engine_maker" ) if not IsValid( Engine ) then return false end local EID local List = list.Get("ACFCUSTOMEnts") if List["MobilityCustom"][Id] then EID = Id else EID = "Maker" end local Lookup = List.MobilityCustom[EID] Engine:SetAngles(Angle) Engine:SetPos(Pos) Engine:Spawn() Engine:SetPlayer(Owner) Engine.Owner = Owner Engine.Id = EID Engine.SoundPitch = Lookup.pitch or 1 Engine.SpecialHealth = true Engine.SpecialDamage = true Engine.TorqueMult = 1 Engine.FuelTank = 0 Engine.ModTable = Lookup.modtable Engine.ModTable[1] = Data1 Engine.ModTable[2] = Data2 Engine.ModTable[3] = Data3 Engine.ModTable[4] = Data4 Engine.ModTable[5] = Data5 Engine.ModTable[6] = Data6 Engine.ModTable[7] = Data7 Engine.ModTable[8] = Data8 Engine.ModTable[9] = Data9 Engine.ModTable[10] = Data10 Engine.ModTable[11] = Data11 Engine.ModTable[12] = Data12 Engine.ModTable[13] = Data13 Engine.ModTable[14] = Data14 Engine.ModTable[15] = Data15 --Set Mods Table (Used for Duplicator) Engine.Mod1 = Data1 Engine.Mod2 = Data2 Engine.Mod3 = Data3 Engine.Mod4 = Data4 Engine.Mod5 = Data5 Engine.Mod6 = Data6 Engine.Mod7 = Data7 Engine.Mod8 = Data8 Engine.Mod9 = Data9 Engine.Mod10 = Data10 Engine.Mod11 = Data11 Engine.Mod12 = Data12 Engine.Mod13 = Data13 Engine.Mod14 = Data14 Engine.Mod15 = Data15 --Set Strings Values if tostring(Data1) != nil then Engine.EngineName = tostring(Data1) else Engine.EngineName = "No Name" end if tostring(Data2) != nil then Engine.SoundPath = tostring(Data2) else Engine.SoundPath = "" end if string.find(tostring(Data3),".mdl") then Engine.Model = tostring(Data3) else Engine.Model = "models/engines/inline4s.mdl" end if tostring(Data4) != nil then Engine.FuelType = tostring(Data4) else Engine.FuelType = "Petrol" end if tostring(Data5) != nil then Engine.EngineType = tostring(Data5) else Engine.EngineType = "GenericPetrol" end if(tonumber(Data6) != nil and tonumber(Data6) >= 1) then Engine.PeakTorque = tonumber(Data6) else Engine.PeakTorque = 1 end if(tonumber(Data7) != nil and tonumber(Data7) >= 1) then Engine.IdleRPM = tonumber(Data7) else Engine.IdleRPM = 1 end if(tonumber(Data8) != nil and tonumber(Data8) >= 1) then Engine.PeakMinRPM = tonumber(Data8) else Engine.PeakMinRPM = 1 end if(tonumber(Data9) != nil and tonumber(Data10) != nil and Data9 <= Data10 and tonumber(Data9) >= 1 ) then Engine.PeakMaxRPM = tonumber(Data9) elseif(tonumber(Data9) != nil and tonumber(Data10) != nil and Data9 > Data10) then Engine.PeakMaxRPM = tonumber(Data10) else Engine.PeakMaxRPM = 1 end if(tonumber(Data10) != nil and tonumber(Data10) >= 100) then Engine.LimitRPM = tonumber(Data10) else Engine.LimitRPM = 100 end if(tonumber(Data11) != nil and tonumber(Data11) >= 0.001) then Engine.FlywheelMassValue = tonumber(Data11) else Engine.FlywheelMassValue = 0.001 end if(tonumber(Data12) != nil and tonumber(Data12) >= 1) then Engine.Weight = tonumber(Data12) else Engine.Weight = 1 end if tonumber(Data13) != nil then Engine.iselec = tobool(Data13) else Engine.iselec = false end if tonumber(Data14) != nil then Engine.IsTrans = tobool(Data14) else Engine.IsTrans = false end if tonumber(Data15) != nil then Engine.FlywheelOverride = tonumber(Data15) else Engine.FlywheelOverride = 1200 end --Set Original Values Engine.CutValue = Engine.LimitRPM / 20 Engine.CutRpm = Engine.LimitRPM - 100 Engine.Inertia = Engine.FlywheelMassValue*(3.1416)^2 --Set Custom Values Engine:FirstLoadCustom() --Reset Engine Type (PulseJet Fix) Engine.EngineTypeFinal = Engine.EngineType if Engine.EngineType == "Pulsejet" then Engine.EngineType = "Turbine" end --Creating Wire Inputs local Inputs = {"Active", "Throttle"} if GetConVarNumber("sbox_max_acf_modding") > 0 then if string.find(Engine.Model, "/pulsejet") then --Create inputs for PulseJet table.insert(Inputs, "TqAdd") else if Engine.EngineTypeFinal == "Turbine" or Engine.EngineTypeFinal == "Electric" then --Create inputs for Electric&Turbine table.insert(Inputs, "TqAdd") table.insert(Inputs, "RpmAdd") table.insert(Inputs, "FlywheelMass") table.insert(Inputs, "Override") else --Create inputs others engines table.insert(Inputs, "TqAdd") table.insert(Inputs, "RpmAdd") table.insert(Inputs, "FlywheelMass") table.insert(Inputs, "Idle") table.insert(Inputs, "Disable Cutoff") end end end Engine.Inputs = Wire_CreateInputs( Engine, Inputs ) Engine.TorqueScale = ACF.TorqueScale[Engine.EngineType] --calculate boosted peak kw if Engine.EngineType == "Turbine" or Engine.EngineType == "Electric" then Engine.DisableCut = 1 Engine.peakkw = ( Engine.PeakTorque * ( 1 + Engine.PeakMaxRPM / Engine.LimitRPM )) * Engine.LimitRPM / (4*9548.8) --adjust torque to 1 rpm maximum, assuming a linear decrease from a max @ 1 rpm to min @ limiter Engine.PeakKwRPM = math.floor(Engine.LimitRPM / 2) else Engine.peakkw = Engine.PeakTorque * Engine.PeakMaxRPM / 9548.8 Engine.PeakKwRPM = Engine.PeakMaxRPM end --calculate base fuel usage if Engine.EngineType == "Electric" then Engine.FuelUse = ACF.ElecRate / (ACF.Efficiency[Engine.EngineType] * 60 * 60) --elecs use current power output, not max else Engine.FuelUse = ACF.TorqueBoost * ACF.FuelRate * ACF.Efficiency[Engine.EngineType] * Engine.peakkw / (60 * 60) end Engine.FlyRPM = 0 Engine:SetModel( Engine.Model ) Engine.Sound = nil Engine.RPM = {} Engine:PhysicsInit( SOLID_VPHYSICS ) Engine:SetMoveType( MOVETYPE_VPHYSICS ) Engine:SetSolid( SOLID_VPHYSICS ) Engine.Out = Engine:WorldToLocal(Engine:GetAttachment(Engine:LookupAttachment( "driveshaft" )).Pos) 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_maker", Engine) Owner:AddCleanup( "acfcustom", Engine ) ACF_Activate( Engine, 0 ) return Engine end list.Set( "ACFCvars", "acf_engine_maker", {"id", "data1", "data2", "data3", "data4", "data5", "data6", "data7", "data8", "data9", "data10", "data11", "data12", "data13", "data14", "data15"} ) duplicator.RegisterEntityClass("acf_engine_maker", MakeACF_EngineMaker, "Pos", "Angle", "Id", "Mod1", "Mod2", "Mod3", "Mod4", "Mod5", "Mod6", "Mod7", "Mod8", "Mod9", "Mod10", "Mod11", "Mod12", "Mod13") --################################################### --##### 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 -- 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 self.Active then return false, "Turn off the engine before updating it!" end 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 ArgsTable[7] ~= self.Model then return false, "The new engine must have the same model!" end local Feedback = "" if Lookup.fuel != self.FuelType then Feedback = " Fuel type changed, fuel tanks unlinked." for Key,Value in pairs(self.FuelLink) do table.remove(self.FuelLink,Key) --need to remove from tank master? end end if self.Id != Id then self.Id = Id self.SoundPitch = Lookup.pitch or 1 self.SpecialHealth = true self.SpecialDamage = true self.TorqueMult = self.TorqueMult or 1 self.FuelTank = 0 end self.ModTable[1] = ArgsTable[5] self.ModTable[2] = ArgsTable[6] self.ModTable[3] = ArgsTable[7] self.ModTable[4] = ArgsTable[8] self.ModTable[5] = ArgsTable[9] self.ModTable[6] = ArgsTable[10] self.ModTable[7] = ArgsTable[11] self.ModTable[8] = ArgsTable[12] self.ModTable[9] = ArgsTable[13] self.ModTable[10] = ArgsTable[14] self.ModTable[11] = ArgsTable[15] self.ModTable[12] = ArgsTable[16] self.ModTable[13] = ArgsTable[17] self.ModTable[14] = ArgsTable[18] self.ModTable[15] = ArgsTable[19] --Set Mods Table (Used for Duplicator) self.Mod1 = ArgsTable[5] self.Mod2 = ArgsTable[6] self.Mod3 = ArgsTable[7] self.Mod4 = ArgsTable[8] self.Mod5 = ArgsTable[9] self.Mod6 = ArgsTable[10] self.Mod7 = ArgsTable[11] self.Mod8 = ArgsTable[12] self.Mod9 = ArgsTable[13] self.Mod10 = ArgsTable[14] self.Mod11 = ArgsTable[15] self.Mod12 = ArgsTable[16] self.Mod13 = ArgsTable[17] self.Mod14 = ArgsTable[18] self.Mod15 = ArgsTable[19] --Set String Values if tostring(ArgsTable[5]) != nil then self.EngineName = tostring(ArgsTable[5]) else self.EngineName = "No Name" end if tostring(ArgsTable[6]) != nil then self.SoundPath = tostring(ArgsTable[6]) else self.SoundPath = "" end if string.find(tostring(ArgsTable[7]),".mdl") then self.Model = tostring(ArgsTable[7]) else self.Model = "models/engines/v8s.mdl" end if tostring(ArgsTable[8]) != nil then self.FuelType = tostring(ArgsTable[8]) else self.FuelType = "Petrol" end if tostring(ArgsTable[9]) != nil then self.EngineType = tostring(ArgsTable[9]) else self.EngineType = "GenericPetrol" end if(tonumber(ArgsTable[10]) != nil and tonumber(ArgsTable[10]) >= 1) then self.PeakTorque = tonumber(ArgsTable[10]) else self.PeakTorque = 1 end if(tonumber(ArgsTable[11]) != nil and tonumber(ArgsTable[11]) >= 1) then self.IdleRPM = tonumber(ArgsTable[11]) else self.IdleRPM = 1 end if(tonumber(ArgsTable[12]) != nil and tonumber(ArgsTable[12]) >= 1) then self.PeakMinRPM = tonumber(ArgsTable[12]) else self.PeakMinRPM = 1 end if(tonumber(ArgsTable[13]) != nil and tonumber(ArgsTable[14]) != nil and ArgsTable[13] <= ArgsTable[14] and tonumber(ArgsTable[13]) >= 1 ) then self.PeakMaxRPM = tonumber(ArgsTable[13]) elseif(tonumber(ArgsTable[13]) != nil and tonumber(ArgsTable[14]) != nil and ArgsTable[13] > ArgsTable[14]) then self.PeakMaxRPM = tonumber(ArgsTable[14]) else self.PeakMaxRPM = 1 end if(tonumber(ArgsTable[14]) != nil and tonumber(ArgsTable[14]) >= 100) then self.LimitRPM = tonumber(ArgsTable[14]) else self.LimitRPM = 100 end if(tonumber(ArgsTable[15]) != nil and tonumber(ArgsTable[15]) >= 0.001) then self.FlywheelMassValue = tonumber(ArgsTable[15]) else self.FlywheelMassValue = 0.001 end if(tonumber(ArgsTable[16]) != nil and tonumber(ArgsTable[16]) >= 1) then self.Weight = tonumber(ArgsTable[16]) else self.Weight = 1 end if tonumber(ArgsTable[17]) != nil then self.iselec = tobool(ArgsTable[17]) else self.iselec = false end if tonumber(ArgsTable[18]) != nil then self.IsTrans = tobool(ArgsTable[18]) else self.IsTrans = false end if tonumber(ArgsTable[19]) != nil then self.FlywheelOverride = tonumber(ArgsTable[19]) else self.FlywheelOverride = 1200 end --Set Original Values self.CutValue = self.LimitRPM / 20 self.CutRpm = self.LimitRPM - 100 self.Inertia = self.FlywheelMassValue*(3.1416)^2 --Set Custom Values self:FirstLoadCustom() --Reset Engine Type (PulseJet Fix) self.EngineTypeFinal = self.EngineType if self.EngineType == "Pulsejet" then self.EngineType = "Turbine" end --Creating Wire Inputs local Inputs = {"Active", "Throttle"} if GetConVarNumber("sbox_max_acf_modding") > 0 then if string.find(self.Model, "/pulsejet") then --Create inputs for PulseJet table.insert(Inputs, "TqAdd") else if self.EngineTypeFinal == "Turbine" or self.EngineTypeFinal == "Electric" then --Create inputs for Electric&Turbine table.insert(Inputs, "TqAdd") table.insert(Inputs, "RpmAdd") table.insert(Inputs, "FlywheelMass") table.insert(Inputs, "Override") else --Create inputs others engines table.insert(Inputs, "TqAdd") table.insert(Inputs, "RpmAdd") table.insert(Inputs, "FlywheelMass") table.insert(Inputs, "Idle") table.insert(Inputs, "Disable Cutoff") end end end self.Inputs = Wire_CreateInputs( self, Inputs ) --------------------- self.TorqueScale = ACF.TorqueScale[self.EngineType] --calculate boosted peak kw if self.EngineType == "Turbine" or self.EngineType == "Electric" then self.DisableCut = 1 self.peakkw = ( self.PeakTorque * ( 1 + self.PeakMaxRPM / self.LimitRPM )) * self.LimitRPM / (4*9548.8) --adjust torque to 1 rpm maximum, assuming a linear decrease from a max @ 1 rpm to min @ limiter self.PeakKwRPM = math.floor(self.LimitRPM / 2) else self.peakkw = self.PeakTorque * self.PeakMaxRPM / 9548.8 self.PeakKwRPM = self.PeakMaxRPM end --calculate base fuel usage if self.EngineType == "Electric" then self.FuelUse = ACF.ElecRate / (ACF.Efficiency[self.EngineType] * 60 * 60) --elecs use current power output, not max else self.FuelUse = ACF.TorqueBoost * ACF.FuelRate * ACF.Efficiency[self.EngineType] * self.peakkw / (60 * 60) end self:SetModel( self.Model ) self:SetSolid( SOLID_VPHYSICS ) self.Out = self:WorldToLocal(self:GetAttachment(self:LookupAttachment( "driveshaft" )).Pos) local phys = self:GetPhysicsObject() if IsValid( phys ) then phys:SetMass( self.Weight ) end self:SetNWString( "WireName", self.EngineName ) ------ GUI --------- self:UpdateOverlayText() ACF_Activate( self, 1 ) return true, "Engine updated successfully!"..Feedback end --################################################### --##### FUNCTIONS ##### --################################################### function ENT:FirstLoadCustom( ) self.PeakTorqueNormal = self.PeakTorque self.PeakMaxRPMNormal = self.PeakMaxRPM self.LimitRPMNormal = self.LimitRPM self.FlywheelMassNormal = self.FlywheelMassValue self.IdleRPMNormal = self.IdleRPM self.FlywheelOverrideNormal = self.FlywheelOverride self.CutValue = self.LimitRPM / 20 self.CutRpm = self.LimitRPM - 100 self.PeakTorqueAdd = self.PeakTorque end function ENT:UpdateOverlayText() local pbmin local pbmax if (self.iselec == true )then --elecs and turbs get peak power in middle of rpm range pbmin = self.IdleRPM pbmax = math.floor(self.LimitRPM / 2) else pbmin = self.PeakMinRPM pbmax = self.PeakMaxRPM end local SpecialBoost = self.RequiresFuel and ACF.TorqueBoost or 1 self.PowerGUI = self.peakkw*SpecialBoost self.TorqueGUI = (self.PeakTorqueAdd+self.PeakTorqueExtra-self.PeakTorqueHealth)*SpecialBoost local text = "" .. self.EngineName.."\n" text = text .. "Power: " .. math.Round(self.PowerGUI) .. " kW / " .. math.Round(self.PowerGUI * 1.34) .. " hp\n" text = text .. "Torque: " .. math.Round(self.TorqueGUI) .. " Nm / " .. math.Round(self.TorqueGUI * 0.73) .. " ft-lb\n" text = text .. "Powerband: " .. pbmin .. " - " .. pbmax .. " RPM\n" if self.EngineType == "Turbine" or self.EngineType == "Electric" then text = text .. "Override: " .. math.Round(self.FlywheelOverride) .. " RPM\n" else text = text .. "Idle: " .. math.Round(self.IdleRPM) .. " RPM\n" end text = text .. "Redline: " .. math.Round((self.LimitRPM+self.RPMExtra)) .. " RPM\n" text = text .. "FlywheelMass: " .. math.Round(self.FlywheelMassValue,3) .. " Kg\n" text = text .. "EngineType: " .. self.EngineTypeFinal .. "\n" //text = text .. "Rpm: " .. math.Round(self.FlyRPM) .. " RPM\n" //if self.RequiresFuel then text = text .. "Consumption: " .. math.Round(self.Fuelusing,3) .. " liters/min\n" end text = text .. "Weight: " .. math.Round(self.Weight) .. "Kg" self:SetOverlayText( text ) end function ENT:UpdateEngineConsumption() --reload peak and torque if self.EngineType == "Turbine" or self.EngineType == "Electric" then self.peakkw = (self.PeakTorque+self.PeakTorqueExtra-self.PeakTorqueHealth) * (self.LimitRPM+self.RPMExtra) / (4 * 9548.8) self.PeakKwRPM = math.floor((self.LimitRPM+self.RPMExtra) / 2) else self.peakkw = (self.PeakTorque+self.PeakTorqueExtra-self.PeakTorqueHealth) * (self.PeakMaxRPM+self.RPMExtra) / 9548.8 self.PeakKwRPM = (self.PeakMaxRPM+self.RPMExtra) end if self.EngineType == "Electric" then self.FuelUse = ACF.ElecRate / (ACF.Efficiency[self.EngineType] * 60 * 60) --elecs use current power output, not max else self.FuelUse = ACF.TorqueBoost * ACF.FuelRate * ACF.Efficiency[self.EngineType] * self.peakkw / (60 * 60) end --update gui final self:UpdateOverlayText() end --################################################### --##### TRIGGER INPUTS ##### --################################################### function ENT:TriggerInput( iname, value ) if (iname == "Throttle") then self.Throttle = math.Clamp(value,0,100)/100 elseif (iname == "Active") then if (value > 0 and not self.Active) then --make sure we have fuel local HasFuel if not self.RequiresFuel then HasFuel = true else for _,fueltank in pairs(self.FuelLink) do if fueltank.Fuel > 0 and fueltank.Active then HasFuel = true break end end end if HasFuel then self.RPM = {} self.RPM[1] = self.IdleRPM self.Active = true self.Active2 = true Wire_TriggerOutput( self, "Running", 1 ) self.Sound = CreateSound(self, self.SoundPath) self.Sound:PlayEx(0.5,100) self:ACFInit() end elseif (value > 0) then local HasFuel if not self.RequiresFuel then HasFuel = true else for _,fueltank in pairs(self.FuelLink) do if fueltank.Fuel <= 0 or not fueltank.Active then HasFuel = false break end end end if not HasFuel then self:TriggerInput( "Active" , 0 ) self.Active = false Wire_TriggerOutput( self, "RPM", 0 ) Wire_TriggerOutput( self, "Torque", 0 ) Wire_TriggerOutput( self, "Power", 0 ) Wire_TriggerOutput( self, "Fuel Use", 0 ) Wire_TriggerOutput( self, "Running", 0 ) end elseif (value <= 0 and self.Active) then self.Active = false Wire_TriggerOutput( self, "RPM", 0 ) Wire_TriggerOutput( self, "Torque", 0 ) Wire_TriggerOutput( self, "Power", 0 ) Wire_TriggerOutput( self, "Fuel Use", 0 ) Wire_TriggerOutput( self, "Running", 0 ) end elseif (iname == "TqAdd") then if (self.PeakTorqueAdd != self.PeakTorqueNormal+value) then self.PeakTorqueAdd = self.PeakTorqueNormal+value self:UpdateEngineConsumption() end elseif (iname == "RpmAdd") then if (self.PeakMaxRPM != self.PeakMaxRPMNormal+value) then self.PeakMaxRPM = self.PeakMaxRPMNormal+value self.LimitRPM = self.LimitRPMNormal+value self.CutValue = self.LimitRPM / 20 self.CutRpm = self.LimitRPM - 100 self:UpdateEngineConsumption() end elseif (iname == "FlywheelMass") then if (value > 0 and self.FlywheelMassValue != value) then self.FlywheelMassValue = value self.Inertia = self.FlywheelMassValue*(3.1416)^2 self:UpdateOverlayText() elseif (value <= 0 and self.FlywheelMassValue != self.FlywheelMassNormal) then self.FlywheelMassValue = self.FlywheelMassNormal self.Inertia = self.FlywheelMassValue*(3.1416)^2 self:UpdateOverlayText() end elseif (iname == "Override") then if (value > 0 and self.FlywheelOverride != value) then self.FlywheelOverride = value self:UpdateOverlayText() elseif (value <= 0 and self.FlywheelOverride != self.FlywheelOverrideNormal) then self.FlywheelOverride = self.FlywheelOverrideNormal self:UpdateOverlayText() end elseif (iname == "Idle") then if (value > 0 and self.IdleRPM != value) then self.IdleRPM = value self:UpdateOverlayText() elseif (value <= 0 and self.IdleRPM != self.IdleRPMNormal) then self.IdleRPM = self.IdleRPMNormal self:UpdateOverlayText() end elseif (iname == "Disable Cutoff") then if (value > 0 and self.DisableCut != 1) then self.DisableCut = 1 elseif (value <= 0 and self.DisableCut != 0) then self.DisableCut = 0 end end end function ENT:ACF_Activate() --Density of steel = 7.8g cm3 so 7.8kg for a 1mx1m plate 1m thick local Entity = self Entity.ACF = Entity.ACF or {} local Count local PhysObj = Entity:GetPhysicsObject() if PhysObj:GetMesh() then Count = #PhysObj:GetMesh() end if PhysObj:IsValid() and Count and Count>100 then if not Entity.ACF.Aera then Entity.ACF.Aera = (PhysObj:GetSurfaceArea() * 6.45) * 0.52505066107 end --if not Entity.ACF.Volume then -- Entity.ACF.Volume = (PhysObj:GetVolume() * 16.38) --end else local Size = Entity.OBBMaxs(Entity) - Entity.OBBMins(Entity) if not Entity.ACF.Aera then Entity.ACF.Aera = ((Size.x * Size.y)+(Size.x * Size.z)+(Size.y * Size.z)) * 6.45 end --if not Entity.ACF.Volume then -- Entity.ACF.Volume = Size.x * Size.y * Size.z * 16.38 --end end Entity.ACF.Ductility = Entity.ACF.Ductility or 0 --local Area = (Entity.ACF.Aera+Entity.ACF.Aera*math.Clamp(Entity.ACF.Ductility,-0.8,0.8)) local Area = (Entity.ACF.Aera) --local Armour = (Entity:GetPhysicsObject():GetMass()*1000 / Area / 0.78) / (1 + math.Clamp(Entity.ACF.Ductility, -0.8, 0.8))^(1/2) --So we get the equivalent thickness of that prop in mm if all it's weight was a steel plate local Armour = (Entity:GetPhysicsObject():GetMass()*1000 / Area / 0.78) --local Health = (Area/ACF.Threshold) * (1 + math.Clamp(Entity.ACF.Ductility, -0.8, 0.8)) --Setting the threshold of the prop aera gone local Health = (Area/ACF.Threshold) local Percent = 1 if Recalc and Entity.ACF.Health and Entity.ACF.MaxHealth then Percent = Entity.ACF.Health/Entity.ACF.MaxHealth end Entity.ACF.Health = Health * Percent * ACF.EngineHPMult[self.EngineType] Entity.ACF.MaxHealth = Health * ACF.EngineHPMult[self.EngineType] Entity.ACF.Armour = Armour * (0.5 + Percent/2) Entity.ACF.MaxArmour = Armour * ACF.ArmorMod Entity.ACF.Type = nil Entity.ACF.Mass = PhysObj:GetMass() --Entity.ACF.Density = (PhysObj:GetMass()*1000)/Entity.ACF.Volume Entity.ACF.Type = "Prop" --print(Entity.ACF.Health) end function ENT:ACF_OnDamage( Entity, Energy, FrAera, Angle, Inflictor, Bone, Type ) --This function needs to return HitRes local Mul = ((Type == "HEAT" and ACF.HEATMulEngine) or 1) --Heat penetrators deal bonus damage to engines local HitRes = ACF_PropDamage( Entity, Energy, FrAera * Mul, Angle, Inflictor ) --Calling the standard damage prop function return HitRes --This function needs to return HitRes end function ENT:Think() local Time = CurTime() if self.Active2 then if self.Legal then self:CalcRPM() end if self.LastCheck < CurTime() then self:CheckRopes() self:CheckFuel() self:CheckExtra() self:CheckRadiator() self:CalcMassRatio() self.Legal = self:CheckLegal() self.LastCheck = Time + math.Rand(5, 10) end else --SET VALUES FOR RADIATORS for Key, Radiator in pairs(self.RadiatorLink) do if IsValid( Radiator ) then if not Radiator.Legal then continue end Radiator:GetRPM(0, self.LimitRPM, self.Active2) end end end self:SetRadsInfos() --Set Manual Gearbox Infos (Stall Engine) if (self.ManualGearbox) then self:SetManualInfos() 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.Torque = self.PeakTorqueAdd+self.PeakTorqueExtra-self.PeakTorqueHealth self.FlyRPM = self.IdleRPM * 1.5 end function ENT:CalcRPM() local DeltaTime = CurTime() - self.LastThink -- local AutoClutch = math.min(math.max(self.FlyRPM-self.IdleRPM,0)/(self.IdleRPM+self.LimitRPM/10),1) --local ClutchRatio = math.min(Clutch/math.max(TorqueDiff,0.05),1) --find next active tank with fuel local Tank = nil local boost = 1 local MaxTanks = #self.FuelLink for i = 1, MaxTanks do Tank = self.FuelLink[self.FuelTank+1] self.FuelTank = (self.FuelTank + 1) % MaxTanks if IsValid(Tank) and Tank.Fuel > 0 and Tank.Active then break --return Tank end Tank = nil i = i + 1 end --calculate fuel usage if Tank then local Consumption if self.FuelType == "Electric" then Consumption = (self.Torque * self.FlyRPM / 9548.8) * self.FuelUse * DeltaTime else local Load = 0.3 + self.Throttle * 0.7 Consumption = Load * self.FuelUse * (self.FlyRPM / self.PeakKwRPM) * DeltaTime / ACF.FuelDensity[Tank.FuelType] end Tank.Fuel = math.max(Tank.Fuel - Consumption,0) boost = ACF.TorqueBoost Wire_TriggerOutput(self, "Fuel Use", math.Round(60*Consumption/DeltaTime,3)) self.Fuelusing = math.Round((60*Consumption/DeltaTime),3) elseif self.RequiresFuel then self.Active = false self:TriggerInput( "Active", 0 ) --shut off if no fuel and requires it Wire_TriggerOutput( self, "Running", 0 ) //return 0 else Wire_TriggerOutput(self, "Fuel Use", 0) end --adjusting performance based on damage self.TorqueMult = math.Clamp(((1 - self.TorqueScale) / (0.5)) * ((self.ACF.Health/self.ACF.MaxHealth) - 1) + 1, self.TorqueScale, 1) self.PeakTorque = (self.PeakTorqueAdd+self.PeakTorqueExtra-self.PeakTorqueHealth) * self.TorqueMult local Drag local TorqueDiff --Custom Drag Value for Engines Types local DragType = self.PeakMaxRPM+self.RPMExtra if self.iselec == true then DragType = self.FlywheelOverride end if (self.Active and self.CutMode == 0) then --Set Torque & Drag self.Torque = boost * self.Throttle * math.max( self.PeakTorque * math.min( self.FlyRPM / self.PeakMinRPM , ((self.LimitRPM+self.RPMExtra) - self.FlyRPM) / ((self.LimitRPM+self.RPMExtra) - (self.PeakMaxRPM+self.RPMExtra)), 1 ), 0 ) Drag = self.PeakTorque * (math.max( self.FlyRPM - self.IdleRPM, 0) / DragType) * (1 - self.Throttle) / self.Inertia --Set FlyRPM & TorqueDiff self.FlyRPM = math.max( self.FlyRPM + self.Torque / self.Inertia - Drag, 1 ) TorqueDiff = math.max( self.FlyRPM - self.IdleRPM, 0 ) * self.Inertia --Reset TorqueDiff for Manual Gearbox if (self.ManualGearbox) then local MaxTorque = math.max(self.LimitRPM - self.IdleRPM, 0) * self.Inertia local AddedTorque = math.max(self.FlyRPM + self.IdleRPM, 0) * self.Inertia local MaxAddedTorque = math.max(self.LimitRPM + self.IdleRPM, 0) * self.Inertia TorqueDiff = (AddedTorque * MaxTorque) / MaxAddedTorque end else --Set Torque & Drag self.Torque = 0 Drag = 10 * (math.max( self.FlyRPM, 0) / DragType) * 1 / (0.001*(3.1416)^2) --Set RPM & TorqueDiff self.FlyRPM = math.max( self.FlyRPM + self.Torque / self.Inertia - Drag, 1 ) TorqueDiff = 0 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.FlyRPM = self.FlyRPM - math.min( TorqueDiff, TotalReqTq ) / self.Inertia -- Add RPM (Manual Gearbox)(Engine Back Force) if (self.ManualGearbox and self.Active) then if self.Throttle == 0 and self.GearboxCurrentGear != 0 then --Remove Rev Limiter while decompressing self.DisableCutFinal = 1 self.FlyRPM = self.FlyRPM + (TotalReqTq2 / self.Inertia) else --Enable Rev Limiter self.DisableCutFinal = self.DisableCut end else --Enable Rev Limiter self.DisableCutFinal = self.DisableCut end if( self.DisableCutFinal == 0 ) then -- Cut the Engine (Rev Limiter) if( self.FlyRPM >= self.CutRpm and self.CutMode == 0 ) then self.CutMode = 1 if self.Sound then self.Sound:Stop() end self.Sound = nil local MakeSound = math.random(1,4) if MakeSound <= 1 and self.Sound2 and self.Sound2:IsPlaying() then self.Sound2:Stop() end if MakeSound <= 1 then self.Sound2 = CreateSound(self, "ambient/explosions/explode_4.wav") self.Sound2:PlayEx(0.7,100) end end -- Enable back Engine (Rev Limiter) if( self.FlyRPM <= self.CutRpm - self.CutValue and self.CutMode == 1 ) then self.CutMode = 0 self.Sound = CreateSound(self, self.SoundPath) self.Sound:PlayEx(0.5,100) end elseif( self.DisableCutFinal == 1 ) then -- Fix Sound Bug once its cutted if( self.CutMode == 1 ) then self.Sound = CreateSound(self, self.SoundPath) self.Sound:PlayEx(0.5,100) end -- No Rev Limiting self.CutMode = 0 end --Update Gui //self:UpdateOverlayText() -- Kill the Engine Off under 100 RPM if( self.FlyRPM <= 100 and self.Active == false ) then self.Active2 = false Wire_TriggerOutput( self, "Running", 0 ) self.FlyRPM = 0 if self.Sound then self.Sound:Stop() end self.Sound = nil end --Update extras values for Key, Extra in pairs(self.ExtraLink) do if IsValid(Extra) then if self.PeakTorqueExtra != (Extra.TorqueAdd or 0) or self.RPMExtra != (Extra.RPMAdd or 0) then self:AddExtraValue() end if Extra.GetRpm == true then if not Extra.Legal then continue end local PeakTorqueSend = (self.PeakTorqueAdd-self.PeakTorqueHealth)*self.TorqueMult Extra:GetRPM(self.FlyRPM, self.LimitRPM, self.Weight, self.Throttle, self.EngineType, PeakTorqueSend, self.IdleRPM) end end end --Update radiator values for Key, Radiator in pairs(self.RadiatorLink) do if IsValid( Radiator ) then if not Radiator.Legal then continue end Radiator:GetRPM(self.FlyRPM, self.LimitRPM, self.Active2) end end -- Then we calc a smoothed RPM value for the sound effects table.remove( self.RPM, 10 ) table.insert( self.RPM, 1, self.FlyRPM ) local SmoothRPM = 0 for Key, RPM in pairs( self.RPM ) do SmoothRPM = SmoothRPM + (RPM or 0) end SmoothRPM = SmoothRPM / 10 local Power = self.Torque * SmoothRPM / 9548.8 Wire_TriggerOutput(self, "Torque", math.floor(self.Torque)) Wire_TriggerOutput(self, "Power", math.floor(Power)) Wire_TriggerOutput(self, "RPM", self.FlyRPM) if self.Sound then self.Sound:ChangePitch( math.min( 20 + (SmoothRPM * self.SoundPitch) / 50, 255 ), 0 ) self.Sound:ChangeVolume( 0.25 + (0.1 + 0.9 * ((SmoothRPM / (self.LimitRPM+self.RPMExtra)) ^ 1.5)) * self.Throttle / 1.5, 0 ) end return RPM 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 if self.IsTrans then Direction = -self:GetRight() else Direction = self:GetForward() end local DrvAngle = ( OutPos - InPos ):GetNormalized():DotProduct( Direction ) if DrvAngle < 0.7 then self:Unlink( Ent ) end end end --unlink fuel tanks out of range function ENT:CheckFuel() for _,tank in pairs(self.FuelLink) do if self:GetPos():Distance(tank:GetPos()) > 512 then self:Unlink( tank ) soundstr = "physics/metal/metal_box_impact_bullet" .. tostring(math.random(1, 3)) .. ".wav" self:EmitSound(soundstr,500,100) 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", "acf_fueltank", "acf_chips", "acf_nos", "acf_rads", "acf_turbo", "acf_supercharger" }, Target:GetClass() ) then return false, "Can only link to gearboxes, fuel tanks or engine extras!" end --Fuel Tank Linking if Target:GetClass() == "acf_fueltank" then return self:LinkFuel( Target ) end --Extra Linking if Target:GetClass() == "acf_chips" or Target:GetClass() == "acf_nos" or Target:GetClass() == "acf_turbo" or Target:GetClass() == "acf_supercharger" then if self.ExtraUsing == 1 then --Not link severals Extra Object return false, "You CAN'T use more than one Extra!" else return self:LinkExtra( Target ) end end --Radiators Linking if Target:GetClass() == "acf_rads" then return self:LinkRadiator( Target ) 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 if self.IsTrans then Direction = -self:GetRight() else Direction = self:GetForward() end 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 fuel tank if Target:GetClass() == "acf_fueltank" then return self:UnlinkFuel( Target ) end --unlink extra if Target:GetClass() == "acf_chips" or Target:GetClass() == "acf_nos" or Target:GetClass() == "acf_turbo" or Target:GetClass() == "acf_supercharger" then return self:UnlinkExtra( Target ) end --unlink radiator if Target:GetClass() == "acf_rads" then return self:UnlinkRadiator( Target ) end --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 --LINKING FUEL TANK function ENT:LinkFuel( Target ) if not (self.FuelType == "Multifuel" and not (Target.FuelType == "Electric")) then if self.FuelType ~= Target.FuelType then return false, "Cannot link because fuel type is incompatible." end end if Target.NoLinks then return false, "This fuel tank doesn\'t allow linking." end for Key,Value in pairs(self.FuelLink) do if Value == Target then return false, "That fuel tank is already linked to this engine!" end end if self:GetPos():Distance( Target:GetPos() ) > 512 then return false, "Fuel tank is too far away." end table.insert( self.FuelLink, Target ) table.insert( Target.Master, self ) self.RequiresFuel = true self:UpdateOverlayText() return true, "Link successful! Now Require Fuel" end --UNLINK FUEL TANK function ENT:UnlinkFuel( Target ) for Key, Value in pairs( self.FuelLink ) do if Value == Target then self.RequiresFuel = false self:UpdateOverlayText() table.remove( self.FuelLink, Key ) return true, "Unlink successful! Now NOT Require Fuel" end end return false, "That fuel tank is not linked to this engine!" end --LINKING EXTRA function ENT:LinkExtra( Target ) for Key,Value in pairs(self.ExtraLink) do if Value == Target then return false, "It's already linked to this engine!" end end if self:GetPos():Distance( Target:GetPos() ) > 512 then return false, "The Extra is too far away." end table.insert( self.ExtraLink, Target ) table.insert( Target.Master, self ) self:AddExtraValue( ) --Add Values self.ExtraUsing = 1 --Set Using Extra to 1 return true, "Link successful!" end --UNLINK EXTRA function ENT:UnlinkExtra( Target ) for Key, Value in pairs( self.ExtraLink ) do if Value == Target then self:RemoveExtraValue( ) --Remove values self.ExtraUsing = 0 --Set Using Extra to 0 table.remove( self.ExtraLink, Key ) return true, "Unlink successful!" end end return false, "That's not linked to this engine!" end --UNLINK EXTRA out of range function ENT:CheckExtra() for _,extra in pairs(self.ExtraLink) do if self:GetPos():Distance(extra:GetPos()) > 512 then self:Unlink( extra ) self:RemoveExtraValue( ) --Remove values self.ExtraUsing = 0 --Set Using Extra to 0 soundstr = "physics/metal/metal_box_impact_bullet" .. tostring(math.random(1, 3)) .. ".wav" self:EmitSound(soundstr,500,100) end end end --ADD EXTRA SETTINGS FUNCTION function ENT:AddExtraValue( ) for Key, Extra in pairs(self.ExtraLink) do if IsValid( Extra ) then self.PeakTorqueExtra = (Extra.TorqueAdd or 0) self.RPMExtra = (Extra.RPMAdd or 0) self:UpdateEngineConsumption() end end end --REMOVE EXTRA SETTINGS FUNCTION function ENT:RemoveExtraValue( ) self.PeakTorqueExtra = 0 self.RPMExtra = 0 self:UpdateEngineConsumption() end --LINKING RADIATOR function ENT:LinkRadiator( Target ) for Key,Value in pairs(self.RadiatorLink) do if Value == Target then return false, "It's already linked to this engine!" end end if self:GetPos():Distance( Target:GetPos() ) > 512 then return false, "The Radiator is too far away." end table.insert( self.RadiatorLink, Target ) table.insert( Target.Master, self ) return true, "Link successful!" end --UNLINK RADIATOR function ENT:UnlinkRadiator( Target ) for Key, Value in pairs( self.RadiatorLink ) do if Value == Target then self.PeakTorqueHealth = 0 self:UpdateEngineConsumption() table.remove( self.RadiatorLink, Key ) return true, "Unlink successful!" end end return false, "That's not linked to this engine!" end --UNLINK Radiator out of range function ENT:CheckRadiator() for _,radiator in pairs(self.RadiatorLink) do if self:GetPos():Distance(radiator:GetPos()) > 512 then self:Unlink( radiator ) soundstr = "physics/metal/metal_box_impact_bullet" .. tostring(math.random(1, 3)) .. ".wav" self:EmitSound(soundstr,500,100) end end end --SET Radiator Functions function ENT:SetRadsInfos() local Blowed = false for Key, Radiator in pairs(self.RadiatorLink) do if IsValid( Radiator ) then local TorqueBand = (self.PeakTorqueAdd+self.PeakTorqueExtra)/2 self.PeakTorqueHealth = math.abs(((TorqueBand*(Radiator.HealthVal-100))/-100)) if Radiator.HealthVal <= 0 then Blowed = true end self:UpdateEngineConsumption() end end if Blowed then self.Active = false self:TriggerInput( "Active" , 0 ) end end --SET Manual Gearbox Functions (Kill Engine) function ENT:SetManualInfos() local Staled = false if (self.FlyRPM < (self.IdleRPM / 2)) then Staled = true end if Staled then self.Active = false self:TriggerInput( "Active" , 0 ) Wire_TriggerOutput( self, "Running", 0 ) end 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 --fuel tank link saving local fuel_info = {} local fuel_entids = {} for Key, Value in pairs(self.FuelLink) do --First clean the table of any invalid entities if not Value:IsValid() then table.remove(self.FuelLink, Value) end end for Key, Value in pairs(self.FuelLink) do --Then save it table.insert(fuel_entids, Value:EntIndex()) end fuel_info.entities = fuel_entids if fuel_info.entities then duplicator.StoreEntityModifier( self, "FuelLink", fuel_info ) end --extra link saving local extra_info, extra_entids = {} for Key, Value in pairs(self.ExtraLink) do if not Value:IsValid() then table.remove(self.ExtraLink, Value) end end for Key, Value in pairs(self.ExtraLink) do table.insert(extra_entids, Value:EntIndex()) end extra_info.entities = extra_entids if extra_info.entities then duplicator.StoreEntityModifier( self, "ExtraLink", extra_info ) end --radiator link saving local rads_info, rads_entids = {} for Key, Value in pairs(self.RadiatorLink) do if not Value:IsValid() then table.remove(self.RadiatorLink, Value) end end for Key, Value in pairs(self.RadiatorLink) do table.insert(rads_entids, Value:EntIndex()) end rads_info.entities = rads_entids if rads_info.entities then duplicator.StoreEntityModifier( self, "RadiatorLink", rads_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 --fuel tank link Pasting if (Ent.EntityMods) and (Ent.EntityMods.FuelLink) and (Ent.EntityMods.FuelLink.entities) then local FuelLink = Ent.EntityMods.FuelLink if FuelLink.entities and table.Count(FuelLink.entities) > 0 then for _,ID in pairs(FuelLink.entities) do local Linked = CreatedEntities[ ID ] if IsValid( Linked ) then self.RequiresFuel = true self:UpdateOverlayText() self:Link( Linked ) end end end Ent.EntityMods.FuelLink = nil end --Extra link Pasting if (Ent.EntityMods) and (Ent.EntityMods.ExtraLink) and (Ent.EntityMods.ExtraLink.entities) then local ExtraLink = Ent.EntityMods.ExtraLink if ExtraLink.entities and table.Count(ExtraLink.entities) > 0 then for _,ID in pairs(ExtraLink.entities) do local Linked = CreatedEntities[ ID ] if IsValid( Linked ) then self:Link( Linked ) end end end Ent.EntityMods.ExtraLink = nil end --Radiator link Pasting if (Ent.EntityMods) and (Ent.EntityMods.RadiatorLink) and (Ent.EntityMods.RadiatorLink.entities) then local RadiatorLink = Ent.EntityMods.RadiatorLink if RadiatorLink.entities and table.Count(RadiatorLink.entities) > 0 then for _,ID in pairs(RadiatorLink.entities) do local Linked = CreatedEntities[ ID ] if IsValid( Linked ) then self:Link( Linked ) end end end Ent.EntityMods.RadiatorLink = 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