ACF_CustomMod/lua/entities/acf_enginemaker.lua

1490 lines
52 KiB
Lua

AddCSLuaFile()
DEFINE_BASECLASS( "base_wire_entity" )
ENT.PrintName = "ACF Engine Maker"
ENT.WireDebugName = "ACF Engine Maker"
if CLIENT then
function ACFEngineMakerGUICreate( Table )
if not acfmenupanel.ModData then
acfmenupanel.ModData = {}
end
if not acfmenupanel.ModData[Table.id] then
acfmenupanel.ModData[Table.id] = {}
acfmenupanel.ModData[Table.id]["ModTable"] = Table.modtable
end
acfmenupanel:CPanelText("Name", Table.name)
acfmenupanel:CPanelText("Desc", "Desc : "..Table.desc)
for ID,Value in pairs(acfmenupanel.ModData[Table.id]["ModTable"]) do
if ID == 1 then
ACF_ModingEngine()
end
end
acfmenupanel.CustomDisplay:PerformLayout()
end
function ACF_ModingEngine( )
--############
--loading
local Redcolor = 0
local Greencolor = 0
local Bluecolor = 0
if file.Exists("acf/menucolor.txt", "DATA") then
local MenuColor = file.Read("acf/menucolor.txt")
local MenuColorTable = {}
for w in string.gmatch(MenuColor, "([^,]+)") do
table.insert(MenuColorTable, w)
end
Redcolor = tonumber(MenuColorTable[1])
Greencolor = tonumber(MenuColorTable[2])
Bluecolor = tonumber(MenuColorTable[3])
local RedString = tostring(Redcolor)
local GreenString = tostring(Greencolor)
local BlueString = tostring(Bluecolor)
else
Redcolor = 0
Greencolor = 0
Bluecolor = 200
end
--First Load
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)
acfmenupanel.CustomDisplay:PerformLayout()
end
--###########################################
local wide = acfmenupanel.CustomDisplay:GetWide()
Open = vgui.Create("DButton")
Open:SetText("Open Engine Menu")
Open:SetTextColor(Color(Redcolor,Greencolor,Bluecolor,255))
Open:SetWide(wide)
Open:SetTall(30)
Open:SetVisible(true)
Open.DoClick = function()
RunConsoleCommand("acf_enginestart_browser_open")
end
acfmenupanel.CustomDisplay:AddItem(Open)
DisplayModel = vgui.Create( "DModelPanel", acfmenupanel.CustomDisplay )
DisplayModel:SetModel( ModelLoad )
DisplayModel:SetCamPos( Vector( 250, 500, 250 ) )
DisplayModel:SetLookAt( Vector( 0, 0, 0 ) )
DisplayModel:SetFOV( 20 )
DisplayModel:SetSize(acfmenupanel:GetWide(),acfmenupanel:GetWide())
DisplayModel.LayoutEntity = function( panel, entity ) end
acfmenupanel.CustomDisplay:AddItem( DisplayModel )
acfmenupanel:CPanelText("EngName", "Engine Name : "..NameLoad)
acfmenupanel:CPanelText("EngSound", "Sound : "..SoundLoad)
acfmenupanel:CPanelText("EngModel", "Model : "..ModelLoad)
acfmenupanel:CPanelText("EngFuel", "Fuel Type : "..FuelTypeLoad)
acfmenupanel:CPanelText("EngType", "Engine Type : "..EngineTypeLoad)
acfmenupanel:CPanelText("EngTorque", "Torque : "..TorqueLoad)
acfmenupanel:CPanelText("EngIdle", "Idle Rpm : "..IdleLoad)
acfmenupanel:CPanelText("EngPeakMin", "Peak Min Rpm : "..PeakMinLoad)
acfmenupanel:CPanelText("EngPeakMax", "Peak Max Rpm : "..PeakMaxLoad)
acfmenupanel:CPanelText("EngLimit", "Limit Rpm : "..LimitRpmLoad)
acfmenupanel:CPanelText("EngFly", "Flywheel Mass : "..FlywheelLoad)
acfmenupanel: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")
acfmenupanel.CustomDisplay:AddItem(Help)
--Reload Last Engine
if file.Exists("acf/lastengine.txt", "DATA") then
local LastEngineText = file.Read("acf/lastengine.txt")
local LastEngineTable = {}
for w in string.gmatch(LastEngineText, "([^,]+)") do
table.insert(LastEngineTable, w)
end
NameLoad = tostring(LastEngineTable[1])
SoundLoad = tostring(LastEngineTable[2])
ModelLoad = tostring(LastEngineTable[3])
FuelTypeLoad = tostring(LastEngineTable[4])
EngineTypeLoad = tostring(LastEngineTable[5])
TorqueLoad = tonumber(LastEngineTable[6])
IdleLoad = tonumber(LastEngineTable[7])
PeakMinLoad = tonumber(LastEngineTable[8])
PeakMaxLoad = tonumber(LastEngineTable[9])
LimitRpmLoad = tonumber(LastEngineTable[10])
FlywheelLoad = tonumber(LastEngineTable[11])
WeightLoad = tonumber(LastEngineTable[12])
IselectText = tostring(LastEngineTable[15])
IsTransText = tostring(LastEngineTable[16])
FlywheelOverNumber = tonumber(LastEngineTable[17])
RunConsoleCommand( "acfmenu_data1", NameLoad )
RunConsoleCommand( "acfmenu_data2", SoundLoad )
RunConsoleCommand( "acfmenu_data3", ModelLoad )
RunConsoleCommand( "acfmenu_data4", FuelTypeLoad )
RunConsoleCommand( "acfmenu_data5", EngineTypeLoad )
RunConsoleCommand( "acfmenu_data6", TorqueLoad )
RunConsoleCommand( "acfmenu_data7", IdleLoad )
RunConsoleCommand( "acfmenu_data8", PeakMinLoad )
RunConsoleCommand( "acfmenu_data9", PeakMaxLoad )
RunConsoleCommand( "acfmenu_data10", LimitRpmLoad )
RunConsoleCommand( "acfmenu_data11", FlywheelLoad )
RunConsoleCommand( "acfmenu_data12", WeightLoad )
RunConsoleCommand( "acfmenu_data13", IselectText )
RunConsoleCommand( "acfmenu_data14", IsTransText )
RunConsoleCommand( "acfmenu_data15", FlywheelOverNumber )
--###
acfmenupanel:CPanelText("EngName", "Engine Name : "..NameLoad)
acfmenupanel:CPanelText("EngSound", "Sound : "..SoundLoad)
acfmenupanel:CPanelText("EngModel", "Model : "..ModelLoad)
acfmenupanel:CPanelText("EngFuel", "Fuel Type : "..FuelTypeLoad)
acfmenupanel:CPanelText("EngType", "Engine Type : "..EngineTypeLoad)
acfmenupanel:CPanelText("EngTorque", "Torque : "..TorqueLoad)
acfmenupanel:CPanelText("EngIdle", "Idle Rpm : "..IdleLoad)
acfmenupanel:CPanelText("EngPeakMin", "Peak Min Rpm : "..PeakMinLoad)
acfmenupanel:CPanelText("EngPeakMax", "Peak Max Rpm : "..PeakMaxLoad)
acfmenupanel:CPanelText("EngLimit", "Limit Rpm : "..LimitRpmLoad)
acfmenupanel:CPanelText("EngFly", "Flywheel Mass : "..FlywheelLoad)
acfmenupanel:CPanelText("EngWeight", "Weight : "..WeightLoad)
DisplayModel:SetModel( ModelLoad )
acfmenupanel.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.Legal = true
self.CanUpdate = true
self.RequiresFuel = 0
--####################
self.TqAdd = 0
self.MaxRpmAdd = 0
self.LimitRpmAdd = 0
self.FlywheelMass = 0
self.Override = 0
self.WeightKg = 0
self.idle = 0
self.CutMode = 0
self.CutValue = 0
self.CutRpm = 0
self.DisableAutoClutch = 0
self.Fuelusing = 0
self.DisableCut = 0
self.ExtraLink = {}
self.Master = {}
self.PeakTorqueExtra = 0
self.PeakMaxRPMExtra = 0
self.LimitRPMExtra = 0
self.ExtraUsing = 0
self.PeakTorqueHealth = 0
--#####################
self.Outputs = WireLib.CreateSpecialOutputs( self, { "RPM", "Torque", "Power", "Fuel Use", "Entity", "Mass", "Physical Mass" }, { "NORMAL","NORMAL","NORMAL", "NORMAL", "ENTITY", "NORMAL", "NORMAL" } )
Wire_TriggerOutput( self, "Entity", self )
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 EngineMaker = ents.Create( "acf_enginemaker" )
if not IsValid( EngineMaker ) then return false end
local EID
local List = list.Get("ACFEnts")
if List["Mobility"][Id] then EID = Id else EID = "Maker" end
local Lookup = List.Mobility[EID]
EngineMaker:SetAngles(Angle)
EngineMaker:SetPos(Pos)
EngineMaker:Spawn()
EngineMaker:SetPlayer(Owner)
EngineMaker.Owner = Owner
EngineMaker.Id = EID
EngineMaker.elecpower = Lookup.elecpower
EngineMaker.SoundPitch = Lookup.pitch or 1
EngineMaker.SpecialHealth = true
EngineMaker.SpecialDamage = true
EngineMaker.TorqueMult = 1
EngineMaker.ModTable = Lookup.modtable
EngineMaker.ModTable[1] = Data1
EngineMaker.ModTable[2] = Data2
EngineMaker.ModTable[3] = Data3
EngineMaker.ModTable[4] = Data4
EngineMaker.ModTable[5] = Data5
EngineMaker.ModTable[6] = Data6
EngineMaker.ModTable[7] = Data7
EngineMaker.ModTable[8] = Data8
EngineMaker.ModTable[9] = Data9
EngineMaker.ModTable[10] = Data10
EngineMaker.ModTable[11] = Data11
EngineMaker.ModTable[12] = Data12
EngineMaker.ModTable[13] = Data13
EngineMaker.ModTable[14] = Data14
EngineMaker.ModTable[15] = Data15
EngineMaker.Mod1 = Data1
EngineMaker.Mod2 = Data2
EngineMaker.Mod3 = Data3
EngineMaker.Mod4 = Data4
EngineMaker.Mod5 = Data5
EngineMaker.Mod6 = Data6
EngineMaker.Mod7 = Data7
EngineMaker.Mod8 = Data8
EngineMaker.Mod9 = Data9
EngineMaker.Mod10 = Data10
EngineMaker.Mod11 = Data11
EngineMaker.Mod12 = Data12
EngineMaker.Mod13 = Data13
EngineMaker.Mod14 = Data14
EngineMaker.Mod15 = Data15
--Set Strings Values
if tostring(EngineMaker.Mod1) != nil then EngineMaker.EngineName = tostring(EngineMaker.Mod1) else EngineMaker.EngineName = "No Name" end
if tostring(EngineMaker.Mod2) != nil then EngineMaker.SoundPath = tostring(EngineMaker.Mod2) else EngineMaker.SoundPath = "" end
if string.find(tostring(EngineMaker.Mod3),".mdl") then EngineMaker.Model = tostring(EngineMaker.Mod3) else EngineMaker.Model = "models/engines/inline4s.mdl" end
if tostring(EngineMaker.Mod4) != nil then EngineMaker.FuelType = tostring(EngineMaker.Mod4) else EngineMaker.FuelType = "Petrol" end
if tostring(EngineMaker.Mod5) != nil then EngineMaker.EngineType = tostring(EngineMaker.Mod5) else EngineMaker.EngineType = "GenericPetrol" end
--Set Torque
if(tonumber(EngineMaker.Mod6) >= 1) then EngineMaker.PeakTorque = tonumber(EngineMaker.Mod6)
elseif(tonumber(Data6) < 1 or tonumber(Data6) == nil) then EngineMaker.PeakTorque = 1 end
--Set Idle
if(tonumber(EngineMaker.Mod7) >= 1) then EngineMaker.IdleRPM = tonumber(EngineMaker.Mod7)
elseif(tonumber(EngineMaker.Mod7) < 1 or tonumber(EngineMaker.Mod7) == nil) then EngineMaker.IdleRPM = 1 end
--Set PeakMin
if(tonumber(EngineMaker.Mod8) >= 1) then EngineMaker.PeakMinRPM = tonumber(EngineMaker.Mod8)
elseif(tonumber(EngineMaker.Mod8) < 1 or tonumber(EngineMaker.Mod8) == nil) then EngineMaker.PeakMinRPM = 1 end
--Set PeakMax
if(EngineMaker.Mod9 <= EngineMaker.Mod10 and tonumber(EngineMaker.Mod9) >= 1 ) then EngineMaker.PeakMaxRPM = tonumber(EngineMaker.Mod9)
elseif(EngineMaker.Mod9 > EngineMaker.Mod10 ) then EngineMaker.PeakMaxRPM = tonumber(EngineMaker.Mod10)
elseif(tonumber(EngineMaker.Mod9) < 1 or tonumber(EngineMaker.Mod9) == nil) then EngineMaker.PeakMaxRPM = 1 end
--Set Limit
if(tonumber(EngineMaker.Mod10) >= 100) then EngineMaker.LimitRPM = tonumber(EngineMaker.Mod10)
elseif(tonumber(EngineMaker.Mod10) < 100 or tonumber(EngineMaker.Mod10) == nil) then EngineMaker.LimitRPM = 100 end
--Set Flywheel
if(tonumber(EngineMaker.Mod11) >= 0.001) then EngineMaker.FlywheelMassValue = tonumber(EngineMaker.Mod11)
elseif(tonumber(EngineMaker.Mod11) < 0.001 or tonumber(EngineMaker.Mod11) == nil) then EngineMaker.FlywheelMassValue = 0.001 end
--Set Weight
if(tonumber(EngineMaker.Mod12) >= 1) then EngineMaker.Weight = tonumber(EngineMaker.Mod12)
elseif(tonumber(EngineMaker.Mod12) < 1 or tonumber(EngineMaker.Mod12) == nil ) then EngineMaker.Weight = 1 end
--Set Electric/turbine stuff
if tobool(EngineMaker.Mod13) != nil then EngineMaker.iselec = tobool(EngineMaker.Mod13) else EngineMaker.iselec = false end
if tobool(EngineMaker.Mod14) != nil then EngineMaker.IsTrans = tobool(EngineMaker.Mod14) else EngineMaker.IsTrans = false end
if tonumber(EngineMaker.Mod15) != nil then EngineMaker.FlywheelOverride = tonumber(EngineMaker.Mod15) else EngineMaker.FlywheelOverride = 1200 end
--Set Original Values
EngineMaker.PeakTorqueHeld = EngineMaker.PeakTorque
EngineMaker.CutValue = EngineMaker.LimitRPM / 40
EngineMaker.CutRpm = EngineMaker.LimitRPM - 100
EngineMaker.Inertia = EngineMaker.FlywheelMassValue*(3.1416)^2
--Set Custom Values
EngineMaker.FlywheelMass3 = EngineMaker.FlywheelMassValue
EngineMaker.PeakTorqueLoad = EngineMaker.PeakTorque
EngineMaker.PeakTorqueAdd = EngineMaker.PeakTorque
EngineMaker.Idling = EngineMaker.IdleRPM
EngineMaker.PeakMaxRPM2 = EngineMaker.PeakMaxRPM
EngineMaker.LimitRPM2 = EngineMaker.LimitRPM
--calculate boosted peak kw
if EngineMaker.EngineType == "GenericDiesel" then
EngineMaker.TorqueScale = ACF.DieselTorqueScale
else
EngineMaker.TorqueScale = ACF.TorqueScale
end
if EngineMaker.EngineType == "Turbine" or EngineMaker.EngineType == "Electric" then
EngineMaker.DisableCut = 1
EngineMaker.peakkw = EngineMaker.PeakTorque * EngineMaker.LimitRPM / (4 * 9548.8)
EngineMaker.PeakKwRPM = math.floor(EngineMaker.LimitRPM / 2)
else
EngineMaker.peakkw = EngineMaker.PeakTorque * EngineMaker.PeakMaxRPM / 9548.8
EngineMaker.PeakKwRPM = EngineMaker.PeakMaxRPM
end
--calculate base fuel usage
if EngineMaker.EngineType == "Electric" then
EngineMaker.FuelUse = ACF.ElecRate / (ACF.Efficiency[EngineMaker.EngineType] * 60 * 60) --elecs use current power output, not max
else
EngineMaker.FuelUse = ACF.TorqueBoost * ACF.FuelRate * ACF.Efficiency[EngineMaker.EngineType] * EngineMaker.peakkw / (60 * 60)
end
--Creating Wire Inputs
EngineMaker.CustomLimit = GetConVarNumber("sbox_max_acf_modding")
local Inputs = {"Active", "Throttle"}
if EngineMaker.CustomLimit > 0 then
if EngineMaker.EngineType == "Turbine" or EngineMaker.EngineType == "Electric" then --Create inputs for Electric&Turbine
table.insert(Inputs, "TqAdd")
table.insert(Inputs, "LimitRpmAdd")
table.insert(Inputs, "FlywheelMass")
table.insert(Inputs, "Override")
table.insert(Inputs, "Gearbox RPM")
else --Create inputs others engines
table.insert(Inputs, "TqAdd")
table.insert(Inputs, "MaxRpmAdd")
table.insert(Inputs, "LimitRpmAdd")
table.insert(Inputs, "FlywheelMass")
table.insert(Inputs, "Idle")
table.insert(Inputs, "Disable Cutoff")
table.insert(Inputs, "Gearbox RPM")
end
end
EngineMaker.Inputs = Wire_CreateInputs( EngineMaker, Inputs )
EngineMaker.FlyRPM = 0
EngineMaker:SetModel( EngineMaker.Model )
EngineMaker.Sound = nil
EngineMaker.RPM = {}
EngineMaker:PhysicsInit( SOLID_VPHYSICS )
EngineMaker:SetMoveType( MOVETYPE_VPHYSICS )
EngineMaker:SetSolid( SOLID_VPHYSICS )
EngineMaker.Out = EngineMaker:WorldToLocal(EngineMaker:GetAttachment(EngineMaker:LookupAttachment( "driveshaft" )).Pos)
local phys = EngineMaker:GetPhysicsObject()
if IsValid( phys ) then
phys:SetMass( EngineMaker.Weight )
end
EngineMaker:SetNetworkedString( "WireName", EngineMaker.EngineName )
------ GUI ---------
EngineMaker.FlywheelMassGUI = EngineMaker.FlywheelMassValue
EngineMaker:UpdateOverlayText()
Owner:AddCount("_acf_enginemaker", EngineMaker)
Owner:AddCleanup( "acfmenu", EngineMaker )
ACF_Activate( EngineMaker, 0 )
return EngineMaker
end
list.Set( "ACFCvars", "acf_enginemaker", {"id", "data1", "data2", "data3", "data4", "data5", "data6", "data7", "data8", "data9", "data10", "data11", "data12", "data13", "data14", "data15"} )
duplicator.RegisterEntityClass("acf_enginemaker", 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("ACFEnts").Mobility[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.elecpower = Lookup.elecpower -- how much power does it output
self.SoundPitch = Lookup.pitch or 1
self.SpecialHealth = true
self.SpecialDamage = true
self.TorqueMult = self.TorqueMult or 1
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]
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(self.Mod1) != nil then self.EngineName = tostring(self.Mod1) else self.EngineName = "No Name" end
if tostring(self.Mod2) != nil then self.SoundPath = tostring(self.Mod2) else self.SoundPath = "" end
if string.find(tostring(self.Mod3),".mdl") then self.Model = tostring(self.Mod3) else self.Model = "models/engines/v8s.mdl" end
if tostring(self.Mod4) != nil then self.FuelType = tostring(self.Mod4) else self.FuelType = "Petrol" end
if tostring(self.Mod5) != nil then self.EngineType = tostring(self.Mod5) else self.EngineType = "GenericPetrol" end
--Set Torque
if(tonumber(self.Mod6) >= 1) then self.PeakTorque = tonumber(self.Mod6)
elseif(tonumber(self.Mod6) < 1 or tonumber(self.Mod6) == nil) then self.PeakTorque = 1 end
--Set Idle
if(tonumber(self.Mod7) >= 1) then self.IdleRPM = tonumber(self.Mod7)
elseif(tonumber(self.Mod7) < 1 or tonumber(self.Mod7) == nil) then self.IdleRPM = 1 end
--Set PeakMin
if(tonumber(self.Mod8) >= 1) then self.PeakMinRPM = tonumber(self.Mod8)
elseif(tonumber(self.Mod8) < 1 or tonumber(self.Mod8) == nil) then self.PeakMinRPM = 1 end
--Set PeakMax
if(self.Mod9 <= self.Mod10 and tonumber(self.Mod9) >= 1 ) then self.PeakMaxRPM = tonumber(self.Mod9)
elseif(self.Mod9 > self.Mod10 ) then self.PeakMaxRPM = tonumber(self.Mod10)
elseif(tonumber(self.Mod9) < 1 or tonumber(self.Mod9) == nil) then self.PeakMaxRPM = 1 end
--Set Limit
if(tonumber(self.Mod10) >= 100) then self.LimitRPM = tonumber(self.Mod10)
elseif(tonumber(self.Mod10) < 100 or tonumber(self.Mod10) == nil) then self.LimitRPM = 100 end
--Set Flywheel
if(tonumber(self.Mod11) >= 0.001) then self.FlywheelMassValue = tonumber(self.Mod11)
elseif(tonumber(self.Mod11) < 0.001 or tonumber(self.Mod11) == nil) then self.FlywheelMassValue = 0.001 end
--Set Weight
if(tonumber(self.Mod12) >= 1) then self.Weight = tonumber(self.Mod12)
elseif(tonumber(self.Mod12) < 1 or tonumber(self.Mod12) == nil) then self.Weight = 1 end
--Set Electric/Turbine Stuff
if tobool(self.Mod13) != nil then self.iselec = tobool(self.Mod13) else self.iselec = false end
if tobool(self.Mod14) != nil then self.IsTrans = tobool(self.Mod14) else self.IsTrans = false end
if tonumber(self.Mod15) != nil then self.FlywheelOverride = tonumber(self.Mod15) else self.FlywheelOverride = 1200 end
--Set Original Values
self.PeakTorqueHeld = self.PeakTorque
self.CutValue = self.LimitRPM / 40
self.CutRpm = self.LimitRPM - 100
self.Inertia = self.FlywheelMass3*(3.1416)^2
--Set Custom Values
self.FlywheelMass3 = self.FlywheelMassValue
self.PeakTorqueLoad = self.PeakTorque
self.PeakTorqueAdd = self.PeakTorque
self.Idling = self.IdleRPM
self.PeakMaxRPM2 = self.PeakMaxRPM
self.LimitRPM2 = self.LimitRPM
if self.EngineType == "GenericDiesel" then
self.TorqueScale = ACF.DieselTorqueScale
else
self.TorqueScale = ACF.TorqueScale
end
--calculate boosted peak kw
if self.EngineType == "Turbine" or self.EngineType == "Electric" then
self.DisableCut = 1
self.peakkw = self.PeakTorque * self.LimitRPM / (4 * 9548.8)
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
--Creating Wire Inputs
self.CustomLimit = GetConVarNumber("sbox_max_acf_modding")
local Inputs = {"Active", "Throttle"}
if self.CustomLimit > 0 then
if self.EngineType == "Turbine" or self.EngineType == "Electric" then --Create inputs for Electric&Turbine
table.insert(Inputs, "TqAdd")
table.insert(Inputs, "LimitRpmAdd")
table.insert(Inputs, "FlywheelMass")
table.insert(Inputs, "Override")
table.insert(Inputs, "Gearbox RPM")
else --Create inputs others engines
table.insert(Inputs, "TqAdd")
table.insert(Inputs, "MaxRpmAdd")
table.insert(Inputs, "LimitRpmAdd")
table.insert(Inputs, "FlywheelMass")
table.insert(Inputs, "Idle")
table.insert(Inputs, "Disable Cutoff")
table.insert(Inputs, "Gearbox RPM")
end
end
self.Inputs = Wire_CreateInputs( self, Inputs )
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:SetNetworkedString( "WireName", self.EngineName )
------ GUI ---------
self.FlywheelMassGUI = self.FlywheelMassValue
self:UpdateOverlayText()
ACF_Activate( self, 1 )
return true, "Engine updated successfully!"..Feedback
end
--###################################################
--##### FUNCTIONS #####
--###################################################
function ENT:UpdateOverlayText()
--Better values for Power Gui and Torque Gui
if self.RequiresFuel == 1 then
self.PowerGUI = self.peakkw*ACF.TorqueBoost
self.TorqueGUI = (self.PeakTorqueAdd+self.PeakTorqueExtra)*ACF.TorqueBoost
else
self.PowerGUI = self.peakkw
self.TorqueGUI = (self.PeakTorqueAdd+self.PeakTorqueExtra)
end
local 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"
if self.EngineType == "Turbine" or self.EngineType == "Electric" then --Set Gui on electric&turbine
text = text .. "Override: " .. math.Round(self.FlywheelOverride) .. " RPM\n"
text = text .. "Redline: " .. math.Round((self.LimitRPM+self.LimitRPMExtra)) .. " RPM\n"
text = text .. "FlywheelMass: " .. math.Round(self.FlywheelMassGUI,3) .. " Kg\n"
text = text .. "Rpm: " .. math.Round(self.FlyRPM) .. " RPM\n"
text = text .. "Consumption: " .. math.Round(self.Fuelusing,3) .. " liters/min\n"
text = text .. "Weight: " .. math.Round(self.Weight) .. "Kg\n"
else --Set Gui on Others
text = text .. "Powerband: " .. math.Round(self.PeakMinRPM) .. " - " .. math.Round((self.PeakMaxRPM+self.PeakMaxRPMExtra)) .. " RPM\n"
text = text .. "Redline: " .. math.Round((self.LimitRPM+self.LimitRPMExtra)) .. " RPM\n"
text = text .. "FlywheelMass: " .. math.Round(self.FlywheelMassGUI,3) .. " Kg\n"
text = text .. "Rpm: " .. math.Round(self.FlyRPM) .. " RPM\n"
text = text .. "Consumption: " .. math.Round(self.Fuelusing,3) .. " liters/min\n"
text = text .. "Idle: " .. math.Round(self.IdleRPM) .. " RPM\n"
text = text .. "Weight: " .. math.Round(self.Weight) .. "Kg\n"
end
self:SetOverlayText( text )
end
function ENT:UpdateEngineConsumption()
if self.EngineType == "Turbine" or self.EngineType == "Electric" then
self.peakkw = (self.PeakTorqueAdd+self.PeakTorqueExtra) * (self.LimitRPM+self.LimitRPMExtra) / (4 * 9548.8)
self.PeakKwRPM = math.floor((self.LimitRPM+self.LimitRPMExtra) / 2)
else
self.peakkw = (self.PeakTorqueAdd+self.PeakTorqueExtra) * (self.PeakMaxRPM+self.PeakMaxRPMExtra) / 9548.8
self.PeakKwRPM = (self.PeakMaxRPM+self.PeakMaxRPMExtra)
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
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 self.RequiresFuel == 0 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
self.Sound = CreateSound(self, self.SoundPath)
self.Sound:PlayEx(0.5,100)
self:ACFInit()
end
elseif (value > 0) then
local HasFuel
if self.RequiresFuel == 0 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 )
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 )
end
--##########################################
elseif (iname == "TqAdd") then
if (value ~= 0 ) then
self.PeakTorqueAdd = self.PeakTorqueLoad+value
self:UpdateEngineConsumption()
elseif (value == 0 ) then
self.PeakTorqueAdd = self.PeakTorqueLoad
self:UpdateEngineConsumption()
end
elseif (iname == "MaxRpmAdd") then
if (value ~= 0 ) then
if( self.PeakMaxRPM2+value <= self.LimitRPM ) then
self.PeakMaxRPM = self.PeakMaxRPM2+value
elseif( self.PeakMaxRPM2+value > self.LimitRPM ) then
self.PeakMaxRPM = self.LimitRPM
end
self:UpdateEngineConsumption()
elseif (value == 0 ) then
self.PeakMaxRPM = self.PeakMaxRPM2
self:UpdateEngineConsumption()
end
elseif (iname == "LimitRpmAdd") then
if (value ~= 0 ) then
self.LimitRPM = self.LimitRPM2+value
self.CutValue = self.LimitRPM / 40
self.CutRpm = self.LimitRPM - 100
self:UpdateEngineConsumption()
elseif (value == 0 ) then
self.LimitRPM = self.LimitRPM2
self.CutValue = self.LimitRPM / 40
self.CutRpm = self.LimitRPM - 100
self:UpdateEngineConsumption()
end
elseif (iname == "FlywheelMass") then
if (value > 0 ) then
self.FlywheelMassValue = value
self.FlywheelMassGUI = self.FlywheelMassValue
self:UpdateOverlayText()
elseif (value <= 0 ) then
self.FlywheelMassValue = self.FlywheelMass3
self.FlywheelMassGUI = self.FlywheelMassValue
self:UpdateOverlayText()
end
elseif (iname == "Override") then
if (value > 0 ) then
self.FlywheelOverride = value
self:UpdateOverlayText()
elseif (value <= 0 ) then
self.FlywheelOverride = self.FlywheelOverride2
self:UpdateOverlayText()
end
elseif (iname == "Idle") then
if (value > 0 ) then
self.IdleRPM = value
self:UpdateOverlayText()
elseif (value <= 0 ) then
self.IdleRPM = self.Idling
self:UpdateOverlayText()
end
elseif (iname == "Disable Cutoff") then
if (value > 0 ) then
self.DisableCut = 1
elseif (value <= 0 ) then
self.DisableCut = 0
end
--Disabling AutoClutch on Engine while Moving
elseif (iname == "Gearbox RPM") then
if ((value*0.8) > self.IdleRPM and self.Throttle == 0) then
self.DisableAutoClutch = 1
self.GearboxRpm = value
elseif (value <= self.IdleRPM or self.Throttle > 0 and self.DisableAutoClutch == 1) then
self.DisableAutoClutch = 0
self.GearboxRpm = 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
if self.EngineType == "GenericDiesel" then
Entity.ACF.Health = Health * Percent * ACF.DieselEngineHPMult
Entity.ACF.MaxHealth = Health * ACF.DieselEngineHPMult
else
Entity.ACF.Health = Health * Percent * ACF.EngineHPMult
Entity.ACF.MaxHealth = Health * ACF.EngineHPMult
end
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 6.6) or 1) --Heat penetrators deal bonus damage to engines, roughly half an AP round
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:CalcMassRatio()
self.Legal = self:CheckLegal()
self.LastCheck = Time + math.Rand(5, 10)
end
end
self.LastThink = Time
self:NextThink( Time )
return true
end
function ENT:CheckLegal()
-- make sure weight is not below stock
if self:GetPhysicsObject():GetMass() < self.Weight then return false end
-- if it's not parented we're fine
if not IsValid( self:GetParent() ) then return true end
-- but not if it's parented to a parented prop
if IsValid( self:GetParent():GetParent() ) then return false end
-- parenting is only legal if it's also welded
for k, v in pairs( constraint.FindConstraints( self, "Weld" ) ) do
if v.Ent1 == self:GetParent() or v.Ent2 == self:GetParent() 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.FlyRPM = self.IdleRPM * 1.5
end
function ENT:CalcRPM()
local DeltaTime = CurTime() - self.LastThink
--find first active tank with fuel
local Tank = nil
local boost = 1
for _,fueltank in pairs(self.FuelLink) do
if fueltank.Fuel > 0 and fueltank.Active then Tank = fueltank break end
if fueltank.Fuel <= 0 or not fueltank.Active then Tank = false break end
end
if (not Tank) and self.RequiresFuel == 1 then --make sure we've got a tank with fuel if needed
self:TriggerInput( "Active" , 0 )
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)
else
Wire_TriggerOutput(self, "Fuel Use", 0)
end
--Function Calling
self:UpdateOverlayText() --Update GUI
for Key, Extra in pairs(self.ExtraLink) do --Update Power with Extra Link
if IsValid( Extra ) then
self:AddExtraValue( )
end
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.TorqueMult
-- Calculate the current torque from flywheel RPM
self.Torque = boost * self.Throttle * math.max( self.PeakTorque * math.min( self.FlyRPM / self.PeakMinRPM, (self.LimitRPM - self.FlyRPM) / (self.LimitRPM - self.PeakMaxRPM), 1 ), 0 )
self.Inertia = self.FlywheelMassValue*(3.1416)^2
local Drag
local TorqueDiff
if self.Active then
if( self.CutMode == 0 ) then
self.Torque = boost * self.Throttle * math.max( self.PeakTorque * math.min( self.FlyRPM / self.PeakMinRPM , ((self.LimitRPM+self.LimitRPMExtra) - self.FlyRPM) / ((self.LimitRPM+self.LimitRPMExtra) - (self.PeakMaxRPM+self.PeakMaxRPMExtra)), 1 ), 0 )
if self.iselec == true then
Drag = self.PeakTorque * (math.max( self.FlyRPM - self.IdleRPM, 0) / self.FlywheelOverride) * (1 - self.Throttle) / self.Inertia
else
Drag = self.PeakTorque * (math.max( self.FlyRPM - self.IdleRPM, 0) / (self.PeakMaxRPM+self.PeakMaxRPMExtra)) * ( 1 - self.Throttle) / self.Inertia
end
elseif( self.CutMode == 1 ) then
self.Torque = boost * 0 * math.max( self.PeakTorque * math.min( self.FlyRPM / self.PeakMinRPM , ((self.LimitRPM+self.LimitRPMExtra) - self.FlyRPM) / ((self.LimitRPM+self.LimitRPMExtra) - (self.PeakMaxRPM+self.PeakMaxRPMExtra)), 1 ), 0 )
if self.iselec == true then
Drag = self.PeakTorque * (math.max( self.FlyRPM - self.IdleRPM, 0) / self.FlywheelOverride) * (1 - 0) / self.Inertia
else
Drag = self.PeakTorque * (math.max( self.FlyRPM - self.IdleRPM, 0) / (self.PeakMaxRPM+self.PeakMaxRPMExtra)) * ( 1 - 0) / self.Inertia
end
end
-- Let's accelerate the flywheel based on that torque
self.FlyRPM = math.max( self.FlyRPM + self.Torque / self.Inertia - Drag, 1 )
if self.DisableAutoClutch == 0 then
-- This is the presently avaliable torque from the engine
TorqueDiff = math.max( self.FlyRPM - self.IdleRPM, 0 ) * self.Inertia
elseif self.DisableAutoClutch == 1 then
TorqueDiff = 0
end
end
if( self.Active == false ) then
self.Torque = boost * 0 * math.max( self.PeakTorque * math.min( self.FlyRPM / self.PeakMinRPM , ((self.LimitRPM+self.LimitRPMExtra) - self.FlyRPM) / ((self.LimitRPM+self.LimitRPMExtra) - (self.PeakMaxRPM+self.PeakMaxRPMExtra)), 1 ), 0 )
if self.iselec == true then
Drag = self.PeakTorque * (math.max( self.FlyRPM - 0, 0) / self.FlywheelOverride) * (1 - 0) / self.Inertia
else
Drag = self.PeakTorque * (math.max( self.FlyRPM - 0, 0) / (self.PeakMaxRPM+self.PeakMaxRPMExtra)) * ( 1 - 0) / self.Inertia
end
-- Let's accelerate the flywheel based on that torque
self.FlyRPM = math.max( self.FlyRPM + self.Torque / self.Inertia - Drag, 1 )
-- This is the presently avaliable torque from the engine
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
-- 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
Link.ReqTq = Link.Ent:Calc( self.FlyRPM, self.Inertia )
TotalReqTq = TotalReqTq + Link.ReqTq
end
-- Calculate the ratio of total requested torque versus what's avaliable
local AvailRatio = math.min( TorqueDiff / TotalReqTq / Boxes, 1 )
-- 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
Link.Ent:Act( Link.ReqTq * AvailRatio * self.MassRatio, DeltaTime )
end
if self.DisableAutoClutch == 0 then
self.FlyRPM = self.FlyRPM - math.min( TorqueDiff, TotalReqTq ) / self.Inertia
elseif self.DisableAutoClutch == 1 then
self.FlyRPM = self.GearboxRpm*0.8
end
--#######################################
if( self.DisableCut == 0 ) then
if( self.FlyRPM >= self.CutRpm and self.CutMode == 0 and self.DisableAutoClutch == 0 ) then
self.CutMode = 1
if self.Sound then
self.Sound:Stop()
end
self.Sound = nil
self.Sound2 = CreateSound(self, "acf_other/penetratingshots/00000293.wav")
self.Sound2:PlayEx(0.5,100)
end
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)
if self.Sound2 then
self.Sound2:Stop()
end
end
elseif( self.DisableCut == 1 ) then
self.CutMode = 0
end
if( self.FlyRPM <= 50 and self.Active == false ) then
self.Active2 = false
self.FlyRPM = 0
if self.Sound then
self.Sound:Stop()
end
self.Sound = nil
self:UpdateOverlayText()
end
--SET RPM FOR EXTRA
for Key, Extra in pairs(self.ExtraLink) do
if IsValid( Extra ) then
if Extra.KickRpmNumber > 0 then --Send Rpm for vtec
if not Extra.Legal then continue end
Extra:GetRPM( self.FlyRPM )
end
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.LimitRPMExtra)) ^ 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
-- make sure the rope is still there
if not IsValid( Link.Rope ) then
self:Unlink( Ent )
continue end
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 )
continue 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 Target:GetClass() ~= "acf_gearbox" and Target:GetClass() ~= "acf_fueltank" and Target:GetClass() ~= "acf_gearboxcvt" and Target:GetClass() ~= "acf_chips" and Target:GetClass() ~= "acf_nos" and Target:GetClass() ~= "acf_gearboxauto" then
if not IsValid( Target ) or not table.HasValue( { "acf_gearbox", "acf_gearboxcvt", "acf_gearboxauto", "acf_fueltank", "acf_chips", "acf_nos" }, Target:GetClass() ) then
return false, "Can only link to gearboxes, fuel tanks or engine extras!"
end
if Target:GetClass() == "acf_fueltank" then
return self:LinkFuel( Target )
end
--Link Extra Object
if self.ExtraUsing == 1 then --Not link severals Extra Object
return false, "You CAN'T use more that one Extra!"
end
if Target:GetClass() == "acf_chips" or Target:GetClass() == "acf_nos" then
if self.EngineType == "Turbine" or self.EngineType == "Electric" then
return false, "You CAN'T link Extra to this engine type!"
else
return self:LinkExtra( Target )
end
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 = constraint.CreateKeyframeRope( OutPos, 1, "cable/cable2", nil, self, self.Out, 0, Target, Target.In, 0 )
local Link = {
Ent = Target,
Rope = Rope,
RopeLen = ( OutPos - InPos ):Length(),
ReqTq = 0
}
table.insert( self.GearLink, Link )
table.insert( Target.Master, self )
return true, "Link successful!"
end
function ENT:Unlink( Target )
if Target:GetClass() == "acf_fueltank" then
return self:UnlinkFuel( Target )
end
--unlink extra
if Target:GetClass() == "acf_chips" or Target:GetClass() == "acf_nos" then
return self:UnlinkExtra( Target )
end
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 )
return true, "Unlink successful!"
end
end
return false, "That gearbox is not linked to this engine!"
end
function ENT:LinkFuel( Target )
if not (self.FuelType == "Any" 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 = 1
self:UpdateOverlayText()
return true, "Link successful! Now Require Fuel"
end
function ENT:UnlinkFuel( Target )
for Key, Value in pairs( self.FuelLink ) do
if Value == Target then
self.RequiresFuel = 0
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.PeakMaxRPMExtra = (Extra.MaxRPMAdd or 0)
self.LimitRPMExtra = (Extra.LimitRPMAdd or 0)
self:UpdateEngineConsumption()
end
end
end
--REMOVE EXTRA SETTINGS FUNCTION
function ENT:RemoveExtraValue( )
for Key, Extra in pairs(self.ExtraLink) do
if IsValid( Extra ) then
self.PeakTorqueExtra = 0
self.PeakMaxRPMExtra = 0
self.LimitRPMExtra = 0
self:UpdateEngineConsumption()
end
end
end
--SET THE ENGINE BLOWED (RADIATOR)
function ENT:SetBlow()
self.Active = false
self:TriggerInput( "Active" , 0 )
end
--SET ENGINE TORQUE WITH HIS HEALTH (RADIATOR)
function ENT:SetEngineHealth(RadiatorHealth)
local TorqueBand = (self.PeakTorqueAdd+self.PeakTorqueExtra)/2
if RadiatorHealth > 0 then
self.PeakTorqueHealth = math.abs(((TorqueBand*(RadiatorHealth-100))/-100))
end
self:UpdateEngineConsumption()
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 = {}
local extra_entids = {}
for Key, Value in pairs(self.ExtraLink) do --First clean the table of any invalid entities
if not Value:IsValid() then
table.remove(self.ExtraLink, Value)
end
end
for Key, Value in pairs(self.ExtraLink) do --Then save it
table.insert(extra_entids, Value:EntIndex())
end
extra_info.entities = extra_entids
if extra_info.entities then
duplicator.StoreEntityModifier( self, "ExtraLink", extra_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 = 1
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
//Wire dupe info
self.BaseClass.PostEntityPaste( self, Player, Ent, CreatedEntities )
end
function ENT:OnRemove()
if self.Sound then
self.Sound:Stop()
end
end