ACF_CustomMod/lua/entities/acf_gearbox_air.lua

534 lines
15 KiB
Lua

AddCSLuaFile()
DEFINE_BASECLASS( "base_wire_entity" )
ENT.PrintName = "ACF Gearbox AIR"
ENT.WireDebugName = "ACF Gearbox AIR"
if CLIENT then
local ACF_GearboxAirInfoWhileSeated = CreateClientConVar("ACF_GearboxAirInfoWhileSeated", 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("ACF_GearboxAirInfoWhileSeated"):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 ACFGearboxAirGUICreate( Table )
acfmenupanelcustom:CPanelText("Name", Table.name)
acfmenupanelcustom.CData.DisplayModel = vgui.Create( "DModelPanel", acfmenupanelcustom.CustomDisplay )
acfmenupanelcustom.CData.DisplayModel:SetModel( Table.model )
acfmenupanelcustom.CData.DisplayModel:SetCamPos( Vector( 250, 500, 250 ) )
acfmenupanelcustom.CData.DisplayModel:SetLookAt( Vector( 0, 0, 0 ) )
acfmenupanelcustom.CData.DisplayModel:SetFOV( 20 )
acfmenupanelcustom.CData.DisplayModel:SetSize(acfmenupanelcustom:GetWide(),acfmenupanelcustom:GetWide())
acfmenupanelcustom.CData.DisplayModel.LayoutEntity = function( panel, entity ) end
acfmenupanelcustom.CustomDisplay:AddItem( acfmenupanelcustom.CData.DisplayModel )
acfmenupanelcustom:CPanelText("Desc", "Desc : "..Table.desc)
acfmenupanelcustom:CPanelText("MaxTq", "Max Torque Rating : "..(Table.maxtq).."n-m / "..math.Round(Table.maxtq*0.73).."ft-lb\nWeight : "..(Table.weight).." kg")
acfmenupanelcustom.CustomDisplay:PerformLayout()
maxtorque = Table.maxtq
end
return
end
function ENT:Initialize()
self.IsGeartrain = true
self.Master = {}
self.IsMaster = true
self.WheelLink = {} -- a "Link" has these components: Ent, Side, Axis, Rope, RopeLen, Output, ReqTq, Vel
self.TotalReqTq = 0
self.RClutch = 0
self.LClutch = 0
self.LBrake = 0
self.RBrake = 0
self.Gear = 0
self.GearRatio = 1
self.PropellerRpm = 0
self.LegalThink = 0
self.RPM = {}
self.CurRPM = 0
self.CanUpdate = true
self.LastActive = 0
self.Legal = true
self.Inputs = Wire_CreateInputs( self, {"Clutch", "Brake"} )
self.Outputs = WireLib.CreateSpecialOutputs( self, { "Entity" }, { "ENTITY" , "NORMAL" } )
Wire_TriggerOutput( self, "Entity", self )
end
function MakeACF_GearboxAIR(Owner, Pos, Angle, Id)
if not Owner:CheckLimit("_acf_misc") then return false end
local GearboxAIR = ents.Create("acf_gearbox_air")
local List = list.Get("ACFCUSTOMEnts")
local Classes = list.Get("ACFClasses")
if not IsValid( GearboxAIR ) then return false end
GearboxAIR:SetAngles(Angle)
GearboxAIR:SetPos(Pos)
GearboxAIR:Spawn()
GearboxAIR:SetPlayer(Owner)
GearboxAIR.Owner = Owner
GearboxAIR.Id = Id
GearboxAIR.Model = List.MobilityCustom[Id].model
GearboxAIR.Mass = List.MobilityCustom[Id].weight
GearboxAIR.MaxTorque = List.MobilityCustom[Id].maxtq
GearboxAIR:SetModel( GearboxAIR.Model )
GearboxAIR.LClutch = GearboxAIR.MaxTorque
GearboxAIR.RClutch = GearboxAIR.MaxTorque
GearboxAIR:PhysicsInit( SOLID_VPHYSICS )
GearboxAIR:SetMoveType( MOVETYPE_VPHYSICS )
GearboxAIR:SetSolid( SOLID_VPHYSICS )
local phys = GearboxAIR:GetPhysicsObject()
if IsValid( phys ) then
phys:SetMass( GearboxAIR.Mass )
end
GearboxAIR.In = GearboxAIR:WorldToLocal(GearboxAIR:GetAttachment(GearboxAIR:LookupAttachment( "input" )).Pos)
GearboxAIR.OutL = GearboxAIR:WorldToLocal(GearboxAIR:GetAttachment(GearboxAIR:LookupAttachment( "driveshaftL" )).Pos)
GearboxAIR.OutR = GearboxAIR:WorldToLocal(GearboxAIR:GetAttachment(GearboxAIR:LookupAttachment( "driveshaftR" )).Pos)
Owner:AddCount("_acf_gearboxair", GearboxAIR)
Owner:AddCleanup( "acfcustom", GearboxAIR )
GearboxAIR:SetBodygroup(1, 0)
GearboxAIR:SetNWString( "WireName", List.MobilityCustom[Id].name )
GearboxAIR:UpdateOverlayText()
--GearboxAIR:SetModelScaling()
return GearboxAIR
end
list.Set( "ACFCvars", "acf_gearbox_air" , {"id"} )
duplicator.RegisterEntityClass("acf_gearbox_air", MakeACF_GearboxAIR, "Pos", "Angle", "Id" )
function ENT:Update( ArgsTable )
-- That table is the player data, as sorted in the ACFCvars above, with player who shot,
-- and pos and angle of the tool trace inserted at the start
if ArgsTable[1] ~= self.Owner then -- Argtable[1] is the player that shot the tool
return false, "You don't own that gearbox!"
end
local Id = ArgsTable[4] -- Argtable[4] is the engine ID
local List = list.Get("ACFCUSTOMEnts")
if List.Mobility[Id].model ~= self.Model then
return false, "The new gearbox must have the same model!"
end
if self.Id != Id then
self.Id = Id
self.Mass = List.MobilityCustom[Id].weight
self.MaxTorque = List.MobilityCustom[Id].maxtq
local phys = self:GetPhysicsObject()
if IsValid( phys ) then
phys:SetMass( self.Mass )
end
end
self:SetBodygroup(1, 0)
self:SetNWString( "WireName", List.MobilityCustom[Id].name )
self:UpdateOverlayText()
return true, "AIR Gearbox updated successfully!"
end
function ENT:UpdateOverlayText()
local text = ""
text = text .. "Propeller RPM: " .. math.Round(self.PropellerRpm,0) .. " Rpm\n"
text = text .. "Torque Rating: " .. self.MaxTorque .. " Nm / " .. math.Round( self.MaxTorque * 0.73 ) .. " ft-lb"
self:SetOverlayText( text )
end
-- prevent people from changing bodygroup
function ENT:CanProperty( ply, property )
return property ~= "bodygroups"
end
function ENT:TriggerInput( iname, value )
if ( iname == "Clutch" ) then
self.LClutch = math.Clamp(1-value,0,1)*self.MaxTorque
self.RClutch = math.Clamp(1-value,0,1)*self.MaxTorque
elseif ( iname == "Brake" ) then
self.LBrake = math.Clamp(value,0,100)
self.RBrake = math.Clamp(value,0,100)
end
end
function ENT:Think()
local Time = CurTime()
if self.LastActive + 2 > Time then
self:CheckRopes()
end
self.Legal = self:CheckLegal()
self:NextThink( Time + math.random( 5, 10 ) )
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.Mass then return false end
self.RootParent = nil
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
--if it's welded, make sure it's welded to root parent
if IsValid( constraint.FindConstraintEntity( self, "Weld" ) ) then
for k, v in pairs( constraint.FindConstraints( self, "Weld" ) ) do
if v.Ent1 == rootparent or v.Ent2 == rootparent then return true end
end
else
--if it's parented and not welded, check that it's allowed for this gearbox type
if self.Parentable then
self.RootParent = rootparent
return true
end
end
return false
end
function ENT:CheckRopes()
for Key, Link in pairs( self.WheelLink ) do
local Ent = Link.Ent
local OutPos = self:LocalToWorld( Link.Output )
local InPos = Ent:GetPos()
if Ent.IsGeartrain then
InPos = Ent:LocalToWorld( Ent.In )
end
-- 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 DrvAngle = ( OutPos - InPos ):GetNormalized():DotProduct( ( self:GetRight() * Link.Output.y ):GetNormalized() )
if DrvAngle < 0.7 then
self:Unlink( Ent )
end
end
end
-- Check if every entity we are linked to still actually exists
-- and remove any links that are invalid.
function ENT:CheckEnts()
for Key, Link in pairs( self.WheelLink ) do
if not IsValid( Link.Ent ) then
table.remove( self.WheelLink, Key )
continue end
local Phys = Link.Ent:GetPhysicsObject()
if not IsValid( Phys ) then
Link.Ent:Remove()
table.remove( self.WheelLink, Key )
end
end
end
function ENT:Calc( InputRPM, InputInertia, GetRatio )
if self.LastActive == CurTime() then
return math.min( self.TotalReqTq, self.MaxTorque )
end
self:CheckEnts()
local BoxPhys = self:GetPhysicsObject()
local SelfWorld = self:LocalToWorld( BoxPhys:GetAngleVelocity() ) - self:GetPos()
self.TotalReqTq = 0
for Key, Link in pairs( self.WheelLink ) do
if not IsValid( Link.Ent ) then
table.remove( self.WheelLink, Key )
continue end
local Clutch = 0
if Link.Side == 0 then
Clutch = self.LClutch
elseif Link.Side == 1 then
Clutch = self.RClutch
end
Link.ReqTq = 0
--Set Rpm, Convert Speed to Rpm
local RPM = 0
if GetRatio >= 0 then RPM = self:GetVelocity():Length()*1.5
else RPM = self:GetVelocity():Length()*-1.5 end
if self.GearRatio ~= 0 and ( ( InputRPM > 0 and RPM < InputRPM ) or ( InputRPM < 0 and RPM > InputRPM ) ) then
Link.ReqTq = math.min( Clutch, ( InputRPM - RPM ) * InputInertia )
end
self.PropellerRpm = RPM
self:UpdateOverlayText()
self.TotalReqTq = self.TotalReqTq + math.abs( Link.ReqTq )
end
return math.min( self.TotalReqTq, self.MaxTorque )
end
function ENT:Act( Torque, DeltaTime, MassRatio )
local ReactTq = 0
-- Calculate the ratio of total requested torque versus what's avaliable, and then multiply it but the current gearratio
local AvailTq = 0
if Torque ~= 0 then
AvailTq = math.min( math.abs( Torque ) / self.TotalReqTq, 1 ) / self.GearRatio * -( -Torque / math.abs( Torque ) )
end
for Key, Link in pairs( self.WheelLink ) do
local Brake = 0
if Link.Side == 0 then
Brake = self.LBrake
elseif Link.Side == 1 then
Brake = self.RBrake
end
if Link.Ent.IsGeartrain then
Link.Ent:Act( Link.ReqTq * AvailTq, DeltaTime, MassRatio )
else
self:ActWheel( Link, Link.ReqTq * AvailTq, Brake, DeltaTime )
ReactTq = ReactTq + Link.ReqTq * AvailTq
end
end
local BoxPhys
if IsValid( self.RootParent ) then
BoxPhys = self.RootParent:GetPhysicsObject()
else
BoxPhys = self:GetPhysicsObject()
end
if IsValid( BoxPhys ) and ReactTq ~= 0 then
local Force = self:GetForward() * ReactTq * MassRatio - self:GetForward()
BoxPhys:ApplyForceOffset( Force * 39.37 * DeltaTime, self:GetPos() + self:GetUp() * -39.37 )
BoxPhys:ApplyForceOffset( Force * -39.37 * DeltaTime, self:GetPos() + self:GetUp() * 39.37 )
end
self.LastActive = CurTime()
end
function ENT:ActWheel( Link, Torque, Brake, DeltaTime )
local Phys = Link.Ent:GetPhysicsObject()
local Pos = Link.Ent:GetPos()
local TorqueAxis = Link.Ent:LocalToWorld( Link.Axis ) - Pos
local Cross = TorqueAxis:Cross( Vector( TorqueAxis.y, TorqueAxis.z, TorqueAxis.x ) )
local TorqueVec = TorqueAxis:Cross( Cross ):GetNormalized()
/*local BrakeMult = 0
if Brake > 0 then
BrakeMult = Link.Vel * Phys:GetInertia() * Brake / 10
end*/
--Get Rpm
local BoxPhys = self:GetPhysicsObject()
local SelfWorld = self:LocalToWorld( BoxPhys:GetAngleVelocity() ) - self:GetPos()
local Wheel = Link.Ent
local WheelPhys = Wheel:GetPhysicsObject()
local VelDiff = ( Wheel:LocalToWorld( WheelPhys:GetAngleVelocity() ) - Wheel:GetPos() ) - SelfWorld
local BaseRPM = VelDiff:Dot( Wheel:LocalToWorld( Link.Axis ) - Wheel:GetPos() )
local RealRpm = BaseRPM/-6
--set force1
local Force = 0
if RealRpm <= self:GetVelocity():Length()/2.5 then
Force = 5
elseif RealRpm > self:GetVelocity():Length()/2.5 then
Force = -5
end
--set force2
local Force2 = Torque*3
/*if Brake == 0 then
Force2 = Torque*3
else
Force2 = 0
end*/
Phys:AddAngleVelocity(Vector(0,0,Force*-39.37*DeltaTime))
Phys:ApplyForceCenter( Link.Ent:GetUp()*Force2 )
end
function ENT:Link( Target )
if not IsValid( Target ) or not table.HasValue( { "prop_physics" }, Target:GetClass() ) then
return false, "Can only link props, like propeller!"
end
-- Check if target is already linked
for Key, Link in pairs( self.WheelLink ) do
if Link.Ent == Target then
return false, "That is already linked to this gearbox!"
end
end
-- make sure the angle is not excessive
local InPos = Vector( 0, 0, 0 )
local InPosWorld = Target:LocalToWorld( InPos )
local OutPos = self.OutR
local Side = 1
if self:WorldToLocal( InPosWorld ).y < 0 then
OutPos = self.OutL
Side = 0
end
local OutPosWorld = self:LocalToWorld( OutPos )
local DrvAngle = ( OutPosWorld - InPosWorld ):GetNormalized():DotProduct( ( self:GetRight() * OutPos.y ):GetNormalized() )
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( OutPosWorld, 1, "cable/cable2", nil, self, OutPos, 0, Target, InPos, 0 )
end
local Link = {
Ent = Target,
Side = Side,
Axis = Target:WorldToLocal( self:GetRight() + InPosWorld ),
Rope = Rope,
RopeLen = ( OutPosWorld - InPosWorld ):Length(),
Output = OutPos,
ReqTq = 0,
Vel = 0
}
table.insert( self.WheelLink, Link )
return true, "Link successful!"
end
function ENT:Unlink( Target )
for Key, Link in pairs( self.WheelLink ) 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.WheelLink, Key )
return true, "Unlink successful!"
end
end
return false, "That entity is not linked to this gearbox!"
end
function ENT:PreEntityCopy()
-- Link Saving
local info = {}
local entids = {}
-- Clean the table of any invalid entities
for Key, Link in pairs( self.WheelLink ) do
if not IsValid( Link.Ent ) then
table.remove( self.WheelLink, Key )
end
end
-- Then save it
for Key, Link in pairs( self.WheelLink ) do
table.insert( entids, Link.Ent:EntIndex() )
end
info.entities = entids
if info.entities then
duplicator.StoreEntityModifier( self, "WheelLink", info )
end
//Wire dupe info
self.BaseClass.PreEntityCopy( self )
end
function ENT:PostEntityPaste( Player, Ent, CreatedEntities )
-- Link Pasting
if Ent.EntityMods and Ent.EntityMods.WheelLink and Ent.EntityMods.WheelLink.entities then
local WheelLink = Ent.EntityMods.WheelLink
if WheelLink.entities and table.Count( WheelLink.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( WheelLink.entities ) do
local Linked = CreatedEntities[ ID ]
if IsValid( Linked ) then
self:Link( Linked )
end
end
end )
end
Ent.EntityMods.WheelLink = nil
end
//Wire dupe info
self.BaseClass.PostEntityPaste( self, Player, Ent, CreatedEntities )
end
function ENT:OnRemove()
for Key,Value in pairs(self.Master) do --Let's unlink ourselves from the engines properly
if IsValid( self.Master[Key] ) then
self.Master[Key]:Unlink( self )
end
end
end