diff --git a/lua/acf/entities/turrets/turrets.lua b/lua/acf/entities/turrets/turrets.lua index 141a1323f..86980f6a9 100644 --- a/lua/acf/entities/turrets/turrets.lua +++ b/lua/acf/entities/turrets/turrets.lua @@ -14,6 +14,16 @@ local InchToMm = ACF.InchToMm -- Bunched all of the definitions together due to some loading issue do -- Turret drives + local function ClampAngle(A,Amin,Amax) + local p,y,r + + if A.p < Amin.p then p = Amin.p elseif A.p > Amax.p then p = Amax.p else p = A.p end + if A.y < Amin.y then y = Amin.y elseif A.y > Amax.y then y = Amax.y else y = A.y end + if A.r < Amin.r then r = Amin.r elseif A.r > Amax.r then r = Amax.r else r = A.r end + + return Angle(p,y,r) + end + Turrets.Register("1-Turret",{ Name = "Turrets", Description = "The turret drives themselves.\nThese have a fallback handcrank that is used automatically if no motor is available.", @@ -25,7 +35,7 @@ do -- Turret drives Text = "Maximum number of ACF turrets a player can create." }, GetMass = function(Data, Size) - return math.Round(math.max(Data.Mass * (Size / Data.Size.Base),5), 1) + return math.Round(math.max(Data.Mass * (Size / Data.Size.Base),5) ^ 1.5, 1) end, GetTeethCount = function(Data, Size) local SizePerc = (Size - Data.Size.Min) / (Data.Size.Max - Data.Size.Min) @@ -126,15 +136,15 @@ do -- Turret drives Turrets.RegisterItem("Turret-H","1-Turret",{ Name = "Horizontal Turret", Description = "The large stable base of a turret.", - Model = "models/props_phx/construct/metal_plate_curve360.mdl", + Model = "models/acf/core/t_ring.mdl", ModelSmall = "models/holograms/hq_cylinder.mdl", -- To be used for diameters < 12u, for RWS or other small turrets - Mass = 200, -- At default size, this is the mass of the turret ring. Will scale up/down with diameter difference + Mass = 25, -- At default size, this is the mass of the turret ring. Will scale up/down with diameter difference Size = { Base = 60, -- The default size for the menu Min = 2, -- To accomodate itty bitty RWS turrets Max = 512, -- To accomodate ship turrets - Ratio = 0.05 -- Height modifier for total size + Ratio = 0.1 -- Height modifier for total size }, Teeth = { -- Used to give a final teeth count with size @@ -142,13 +152,8 @@ do -- Turret drives Max = 3072 }, - MassLimit = { - Min = 50, -- Max amount of mass this component can support at minimum size - Max = 80000 -- Max amount of mass th is component can support at maximum size - }, - Armor = { - Min = 5, + Min = 15, Max = 175 }, @@ -156,7 +161,35 @@ do -- Turret drives local Count = #List List[Count + 1] = "Bearing (Local degrees from home angle)" - end + end, + + SlewFuncs = { + GetStab = function(Turret) + local AngDiff = Turret.Rotator:WorldToLocalAngles(Turret.LastRotatorAngle) + + return (Turret.Stabilized and Turret.Active) and (AngDiff.yaw * Turret.StabilizeAmount) or 0 + end, + + GetTargetBearing = function(Turret,StabAmt) + local Rotator = Turret.Rotator + + if Turret.HasArc then + if Turret.Manual then + return Rotator:WorldToLocalAngles(Turret:LocalToWorldAngles(Angle(0, math.Clamp(-Turret.DesiredDeg,Turret.MinDeg,Turret.MaxDeg), 0))).yaw + else + local LocalDesiredAngle = ClampAngle(Turret:WorldToLocalAngles(Turret.DesiredAngle) - Angle(0,StabAmt,0),Angle(0,-Turret.MaxDeg,0),Angle(0,-Turret.MinDeg,0)) + + return Rotator:WorldToLocalAngles(Turret:LocalToWorldAngles(LocalDesiredAngle)).yaw + end + else + return Turret.Manual and (Rotator:WorldToLocalAngles(Turret:LocalToWorldAngles(Angle(0, -Turret.DesiredDeg, 0))).yaw) or (Rotator:WorldToLocalAngles(Turret.DesiredAngle).yaw - StabAmt) + end + end, + + SetRotatorAngle = function(Turret) + Turret.Rotator:SetAngles(Turret:LocalToWorldAngles(Angle(0, Turret.CurrentAngle, 0))) + end + } }) end @@ -164,36 +197,59 @@ do -- Turret drives Turrets.RegisterItem("Turret-V","1-Turret",{ Name = "Vertical Turret", Description = "The smaller part of a turret, usually has the weapon directly attached to it.\nCan be naturally stabilized up to 25% if there is no motor attached, but the mass must be balanced.", - Model = "models/holograms/hq_cylinder.mdl", - Mass = 100, -- At default size, this is the mass of the turret ring. Will scale up/down with diameter difference + Model = "models/acf/core/t_trun.mdl", + Mass = 25, -- At default size, this is the mass of the turret ring. Will scale up/down with diameter difference Size = { Base = 12, -- The default size for the menu - Min = 1, -- To accomodate itty bitty RWS turrets - Max = 36, -- To accomodate ship turrets - Ratio = 1.5 -- Height modifier for total size + Min = 4, -- To accomodate itty bitty RWS turrets + Max = 48, -- To accomodate ship turrets + Ratio = 1 -- Height modifier for total size }, Teeth = { -- Used to give a final teeth count with size Min = 8, - Max = 288 - }, - - MassLimit = { - Min = 20, -- Max amount of mass this component can support at minimum size - Max = 40000 -- Max amount of mass th is component can support at maximum size + Max = 384 }, Armor = { Min = 5, - Max = 175 + Max = 305 }, SetupInputs = function(_,List) local Count = #List List[Count + 1] = "Elevation (Local degrees from home angle)" - end + end, + + SlewFuncs = { + GetStab = function(Turret) + local AngDiff = Turret.Rotator:WorldToLocalAngles(Turret.LastRotatorAngle) + + return (Turret.Stabilized and Turret.Active) and (AngDiff.pitch * Turret.StabilizeAmount) or 0 + end, + + GetTargetBearing = function(Turret,StabAmt) + local Rotator = Turret.Rotator + + if Turret.HasArc then + if Turret.Manual then + return Rotator:WorldToLocalAngles(Turret:LocalToWorldAngles(Angle(math.Clamp(-Turret.DesiredDeg,Turret.MinDeg,Turret.MaxDeg), 0, 0))).pitch + else + local LocalDesiredAngle = ClampAngle(Turret:WorldToLocalAngles(Turret.DesiredAngle) - Angle(StabAmt,0,0),Angle(-Turret.MaxDeg,0,0),Angle(-Turret.MinDeg,0,0)) + + return Rotator:WorldToLocalAngles(Turret:LocalToWorldAngles(LocalDesiredAngle)).pitch + end + else + return Turret.Manual and (Rotator:WorldToLocalAngles(Turret:LocalToWorldAngles(Angle(-Turret.DesiredDeg, 0, 0))).pitch) or (Rotator:WorldToLocalAngles(Turret.DesiredAngle).pitch - StabAmt) + end + end, + + SetRotatorAngle = function(Turret) + Turret.Rotator:SetAngles(Turret:LocalToWorldAngles(Angle(Turret.CurrentAngle, 0, 0))) + end + } }) end end @@ -245,7 +301,7 @@ do -- Turret motors Base = 12, Min = 8, - Max = 24, + Max = 32, }, Torque = { @@ -276,7 +332,7 @@ do -- Turret motors Base = 12, Min = 8, - Max = 24, + Max = 32, }, Torque = { diff --git a/lua/entities/acf_turret/cl_init.lua b/lua/entities/acf_turret/cl_init.lua index ac33ba3ea..49f9c927f 100644 --- a/lua/entities/acf_turret/cl_init.lua +++ b/lua/entities/acf_turret/cl_init.lua @@ -24,6 +24,7 @@ do -- NET SURFER Entity.MinDeg = Data.MinDeg Entity.MaxDeg = Data.MaxDeg Entity.CoMDist = Data.CoMDist + Entity.Type = Data.Type Entity.HasData = true Entity.Age = Clock.CurTime + 5 @@ -42,6 +43,66 @@ do -- NET SURFER end end +do -- Turret drive drawing + local DrawDist = 1024 ^ 2 + local HideInfo = ACF.HideInfoBubble + + local function CSModel(Ent) + if not IsValid(Ent) then return end + + if IsValid(Ent.CSModel) then + if Ent.CSModel:GetModel() ~= Ent:GetModel() then Ent.CSModel:Remove() return end + + return Ent.CSModel + end + + if not Ent.Matrix then return end + + local CSModel = ClientsideModel(Ent:GetModel()) + CSModel:SetParent(Ent) + CSModel:SetPos(Ent:GetPos()) + CSModel:SetAngles(Ent:GetAngles()) + CSModel:SetMaterial(Ent:GetMaterial()) + CSModel:SetColor(Ent:GetColor()) + + CSModel.Material = Ent:GetMaterial() + CSModel.Matrix = Ent.Matrix + CSModel:EnableMatrix("RenderMultiply", CSModel.Matrix) + + Ent.CSModel = CSModel + + return Ent.CSModel + end + + function ENT:Draw() + + -- Partial from base_wire_entity, need the tooltip but without the model drawing since we're drawing our own + local looked_at = self:BeingLookedAtByLocalPlayer() + + if looked_at then + self:DrawEntityOutline() + if not HideInfo() then self:AddWorldTip() end + end + + local Rotator = self:GetNWEntity("ACF.Rotator") + + if (not IsValid(Rotator)) or ((EyePos()):DistToSqr(self:GetPos()) > DrawDist) then self:DrawModel() return end + + local CSM = CSModel(self) + if not IsValid(CSM) then self:DrawModel() return end + + if CSM.Material ~= self:GetMaterial() then CSM:Remove() return end + if CSM:GetColor() ~= self:GetColor() then CSM:Remove() return end + if CSM.Matrix ~= self.Matrix then CSM:Remove() return end + + CSM:SetAngles(Rotator:GetAngles()) + end + + function ENT:OnRemove() + if IsValid(self.CSModel) then self.CSModel:Remove() end + end +end + do -- Overlay local red = Color(255,0,0) local green = Color(0,255,0) @@ -61,13 +122,28 @@ do -- Overlay local X = self:OBBMaxs().x local Pos = self:LocalToWorld(self:OBBCenter()) + if self.Type == "Turret-V" then + UX = self:GetRight() * 0.5 + end + render.DrawLine(Pos + UX,Pos + (self:GetForward() * X) + UX,orange,true) if IsValid(self.Rotator) then render.DrawLine(Pos,Pos + self.Rotator:GetForward() * X,green,true) end local LocPos = self:WorldToLocal(Trace.HitPos) + local AimAng = 0 + local CurAng = 0 local LocDir = Vector(LocPos.x,LocPos.y,0):GetNormalized() + if self.Type == "Turret-V" then + LocDir = Vector(LocPos.x,0,LocPos.z):GetNormalized() + AimAng = -math.Round(self:WorldToLocalAngles(self:LocalToWorldAngles(LocDir:Angle())).pitch,2) + CurAng = -math.Round(self:WorldToLocalAngles(self.Rotator:GetAngles()).pitch,2) + else + AimAng = -math.Round(self:WorldToLocalAngles(self:LocalToWorldAngles(LocDir:Angle())).yaw,2) + CurAng = -math.Round(self:WorldToLocalAngles(self.Rotator:GetAngles()).yaw,2) + end + render.DrawLine(Pos - UX,self:LocalToWorld(self:OBBCenter() + LocDir * X * 2) - UX,magenta,true) render.DrawLine(self:LocalToWorld(self:OBBCenter()),self.Rotator:LocalToWorld(self.LocalCoM),red,true) @@ -78,9 +154,16 @@ do -- Overlay if not ((self.MinDeg == -180) and (self.MaxDeg == 180)) then local MinDir = Vector(X,0,0) - MinDir:Rotate(Angle(0,-self.MinDeg,0)) local MaxDir = Vector(X,0,0) - MaxDir:Rotate(Angle(0,-self.MaxDeg,0)) + + if self.Type == "Turret-V" then + MinDir:Rotate(Angle(-self.MinDeg,0,0)) + MaxDir:Rotate(Angle(-self.MaxDeg,0,0)) + else + MinDir:Rotate(Angle(0,-self.MinDeg,0)) + MaxDir:Rotate(Angle(0,-self.MaxDeg,0)) + end + render.DrawLine(Pos - UX,self:LocalToWorld(self:OBBCenter() + MinDir) - UX,red,true) render.DrawLine(Pos - UX,self:LocalToWorld(self:OBBCenter() + MaxDir) - UX,green,true) end @@ -93,8 +176,8 @@ do -- Overlay cam.Start2D() draw.SimpleTextOutlined("Zero","DermaDefault",HomePos.x,HomePos.y,orange,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER,1,color_black) - draw.SimpleTextOutlined("Current: " .. -math.Round(self:WorldToLocalAngles(self.Rotator:GetAngles()).yaw,2),"DermaDefault",CurPos.x,CurPos.y,green,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER,1,color_black) - draw.SimpleTextOutlined("Aim: " .. -math.Round(self:WorldToLocalAngles(self:LocalToWorldAngles(LocDir:Angle())).yaw,2),"DermaDefault",AimPos.x,AimPos.y,magenta,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER,1,color_black) + draw.SimpleTextOutlined("Current: " .. CurAng,"DermaDefault",CurPos.x,CurPos.y,green,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER,1,color_black) + draw.SimpleTextOutlined("Aim: " .. AimAng,"DermaDefault",AimPos.x,AimPos.y,magenta,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER,1,color_black) draw.SimpleTextOutlined("Mass: " .. self.Mass .. "kg","DermaDefault",CoMPos.x,CoMPos.y,color_white,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER,1,color_black) draw.SimpleTextOutlined("Lateral Distance: " .. self.CoMDist .. "u","DermaDefault",CoMPos.x,CoMPos.y + 16,color_white,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER,1,color_black) diff --git a/lua/entities/acf_turret/init.lua b/lua/entities/acf_turret/init.lua index 664f99342..3d5da9dbc 100644 --- a/lua/entities/acf_turret/init.lua +++ b/lua/entities/acf_turret/init.lua @@ -6,6 +6,7 @@ include("shared.lua") -- Local Vars local ACF = ACF + local Contraption = ACF.Contraption local Classes = ACF.Classes local Utilities = ACF.Utilities @@ -60,7 +61,7 @@ do -- Spawn and Update funcs ------------------ local function GetMass(Turret,Data) - return math.Round(math.max(Turret.Mass * (Data.RingSize / Turret.Size.Base),5), 1) + return math.Round(math.max(Turret.Mass * (Data.RingSize / Turret.Size.Base),5) ^ 1.5, 1) end local function UpdateTurret(Entity, Data, Class, Turret) @@ -75,7 +76,11 @@ do -- Spawn and Update funcs local RingHeight = Class.GetRingHeight({Type = Data.Turret, Ratio = Turret.Size.Ratio}, Size) - Entity:SetSize(Vector(Size,Size,RingHeight)) + if Data.Turret == "Turret-H" then + Entity:SetSize(Vector(Size,Size,RingHeight)) + else + Entity:SetScale(Size / 20) + end Entity.ACF.Model = Model Entity.Name = math.Round(Size,2) .. "\" " .. Turret.Name @@ -94,8 +99,11 @@ do -- Spawn and Update funcs LocalCoM = Vector() } + -- Type-specific functions that differ between horizontal and vertical turret components + Entity.SlewFuncs = Turret.SlewFuncs + Entity.DesiredAngle = Entity.DesiredAngle or Angle(0,0,0) - Entity.CurrentAngle = Entity.CurrentAngle or Angle(0,0,0) + Entity.CurrentAngle = Entity.CurrentAngle or 0 -- This is TRUE whenever the last used angle input is Elevation/Bearing -- Otherwise this is FALSE and will attempt to rotate to the Angle input @@ -193,7 +201,8 @@ do -- Spawn and Update funcs Mass = math.Round(Entity.TurretData.TotalMass,1), MinDeg = Entity.MinDeg, MaxDeg = Entity.MaxDeg, - CoMDist = math.Round(CoM:Length2D(),2) + CoMDist = math.Round(CoM:Length2D(),2), + Type = Entity.Turret } local DataString = util.TableToJSON(Data) @@ -256,6 +265,8 @@ do -- Spawn and Update funcs Entity.HandGear = Class.HandGear Entity.Disconnect = false + Entity:SetNWEntity("ACF.Rotator", Rotator) + Rotator:SetPos(Entity:GetPos()) Rotator:SetAngles(Entity:GetAngles()) Rotator:SetParent(Entity) @@ -263,6 +274,7 @@ do -- Spawn and Update funcs Rotator:Spawn() Entity.Rotator = Rotator + Rotator.Turret = Entity UpdateTurret(Entity, Data, Class, Turret) @@ -342,12 +354,39 @@ do -- Spawn and Update funcs if Parent == NULL then continue end -- somehow this shit is still a problem List[V] = V - if V:GetClass() ~= FilterClass then GetFilteredChildren(V, List) end + + if V:GetClass() == FilterClass then continue end + + GetFilteredChildren(V, List, FilterClass) end return List end + local function Proxy_ACF_OnParent(self, _, _) + if (not IsValid(self.ACF_TurretAncestor)) or (not Contraption.HasAncestor(self,self.ACF_TurretAncestor)) then self.ACF_OnParented = nil self.ACF_TurretAncestor = nil return end + + self.ACF_TurretAncestor:UpdateTurretMass(false) + end + + local function Proxy_ACF_OnMassChange(self) + if (not IsValid(self.ACF_TurretAncestor)) or (not Contraption.HasAncestor(self,self.ACF_TurretAncestor)) then self.ACF_OnMassChange = nil self.ACF_TurretAncestor = nil return end + + self.ACF_TurretAncestor:UpdateTurretMass(false) + end + + local function ParentLink(Turret, Entity, Connect) + if Connect then + Entity.ACF_OnParented = Proxy_ACF_OnParent + Entity.ACF_OnMassChange = Proxy_ACF_OnMassChange + Entity.ACF_TurretAncestor = Turret + else + Entity.ACF_OnParented = nil + Entity.ACF_OnMassChange = nil + Entity.ACF_TurretAncestor = nil + end + end + local function BuildWatchlist(Entity) -- Potentially hot and heavy, should only be triggered after a (maximum) delay to catch large changes and not every single new entity if not IsValid(Entity) then return end @@ -366,12 +405,19 @@ do -- Spawn and Update funcs for k in pairs(ChildList) do local Class = k:GetClass() + k.ACF_TurretAncestor = nil if Class == "acf_turret" then Entity.SubTurrets[k] = true + + k.ACF_TurretAncestor = Entity elseif DynamicMassTypes[Class] then Entity.DynamicEntities[k] = true + + ParentLink(Entity,k,true) else if not IsValid(k) then continue end + ParentLink(Entity,k,true) + local PO = k:GetPhysicsObject() if not IsValid(PO) then continue end @@ -389,9 +435,6 @@ do -- Spawn and Update funcs end Entity.StaticCoM = CoM - - debugoverlay.Line(Entity:GetPos(),Entity:LocalToWorld(Entity.StaticCoM),5,Color(194,55,0),true) - debugoverlay.Cross(Entity:LocalToWorld(Entity.StaticCoM),3,5,Color(194,55,0),true) end local function GetDynamicMass(Entity) -- Returns mass center (local to rotator) and amount from all "dynamic" entities, should be triggered after a resettable delay (only delayable by so long) in order to reduce spammed calls @@ -422,9 +465,6 @@ do -- Spawn and Update funcs Entity.DynamicCoM = CoM - debugoverlay.Line(Entity.Rotator:GetPos(),Entity.Rotator:LocalToWorld(Entity.DynamicCoM),5,Color(3,0,194),true) - debugoverlay.Cross(Entity.Rotator:LocalToWorld(Entity.DynamicCoM),3,5,Color(3,0,194),true) - return CoM, Mass end @@ -454,9 +494,6 @@ do -- Spawn and Update funcs Entity.SubTurretCoM = CoM - debugoverlay.Line(Entity.Rotator:GetPos(),Entity.Rotator:LocalToWorld(Entity.SubTurretCoM),5,Color(0,211,81),true) - debugoverlay.Cross(Entity.Rotator:LocalToWorld(Entity.SubTurretCoM),3,5,Color(0,211,81),true) - return CoM, Mass end @@ -550,9 +587,6 @@ do -- Spawn and Update funcs self:CheckCoM(Force) self:UpdateOverlay() - - debugoverlay.Line(self:GetPos(),self:LocalToWorld(self.TurretData.LocalCoM),5,Color(134,134,134),true) - debugoverlay.Cross(self:LocalToWorld(self.TurretData.LocalCoM),3,5,Color(134,134,134),true) end) end end @@ -708,20 +742,11 @@ do -- Metamethods end do -- Think - local function ClampAngle(A,Amin,Amax) - local p,y,r - - if A.p < Amin.p then p = Amin.p elseif A.p > Amax.p then p = Amax.p else p = A.p end - if A.y < Amin.y then y = Amin.y elseif A.y > Amax.y then y = Amax.y else y = A.y end - if A.r < Amin.r then r = Amin.r elseif A.r > Amax.r then r = Amax.r else r = A.r end - - return Angle(p,y,r) - end - function ENT:SetSoundState(State) if State ~= self.SoundPlaying then if State == true then Sounds.CreateAdjustableSound(self,self.SoundPath,0,0) + self.CurrentSound = self.SoundPath else Sounds.SendAdjustableSound(self,true) end @@ -730,16 +755,54 @@ do -- Metamethods self.SoundPlaying = State end + function ENT:InputDirection(Direction) + if self.Disabled then return end + + self.Manual = true + self.UseVector = false + + if isnumber(Direction) then + self.DesiredDeg = Direction + return + end + + self.Manual = false + + if isangle(Direction) then + Direction:Normalize() + self.DesiredAng = Direction + + return + end + if isvector(Direction) then + self.UseVector = true + self.DesiredVector = Direction + + return + end + end + function ENT:Think() -- The meat and POE-TAE-TOES of the turret working + if self.Disabled then + self:SetSoundState(false) + self:NextThink(Clock.CurTime + 0.1) + + return true + end + self:CheckCoM(false) local Tick = Clock.DeltaTime local Rotator = self.Rotator + if not IsValid(Rotator) then self:Remove() return end + local Scale = self.DamageScale * Tick local SlewMax = self.MaxSlewRate * Scale local SlewAccel = self.SlewAccel * Scale local MaxImpulse = math.min(SlewMax, SlewAccel) + local AngleChange = self.CurrentAngle + -- Something or another has caused the turret to be unable to rotate, so don't waste the extra processing time if MaxImpulse == 0 then self.LastRotatorAngle = Rotator:GetAngles() @@ -754,21 +817,9 @@ do -- Metamethods if self.UseVector and (self.Manual == false) then self.DesiredAngle = (self.DesiredVector - Rotator:GetPos()):GetNormalized():Angle() end - local AngDiff = Rotator:WorldToLocalAngles(self.LastRotatorAngle) --+ Angle(0,-self.SlewRate / 2,0) - local StabAmt = math.Clamp((self.Stabilized and self.Active) and (AngDiff.yaw * self.StabilizeAmount) or 0,-SlewMax,SlewMax) - - local TargetBearing = 0 - if self.HasArc then - if self.Manual then - TargetBearing = Rotator:WorldToLocalAngles(self:LocalToWorldAngles(Angle(0, math.Clamp(-self.DesiredDeg,self.MinDeg,self.MaxDeg), 0))).yaw - else - local LocalDesiredAngle = ClampAngle(self:WorldToLocalAngles(self.DesiredAngle) - Angle(0,StabAmt,0),Angle(0,-self.MaxDeg,0),Angle(0,-self.MinDeg,0)) + local StabAmt = math.Clamp(self.SlewFuncs.GetStab(self), -SlewMax, SlewMax) - TargetBearing = Rotator:WorldToLocalAngles(self:LocalToWorldAngles(LocalDesiredAngle)).yaw - end - else - TargetBearing = self.Manual and (Rotator:WorldToLocalAngles(self:LocalToWorldAngles(Angle(0, -self.DesiredDeg, 0))).yaw) or (Rotator:WorldToLocalAngles(self.DesiredAngle).yaw - StabAmt) - end + local TargetBearing = self.SlewFuncs.GetTargetBearing(self,StabAmt) local sign = TargetBearing < 0 and -1 or 1 local Dist = math.abs(TargetBearing) @@ -780,25 +831,25 @@ do -- Metamethods if self.SlewRate ~= 0 and (Dist <= math.abs(FinalAccel)) and (self.SlewRate <= FinalAccel) then self.SlewRate = 0 - self.CurrentAngle = self.CurrentAngle + Angle(0, TargetBearing / 2, 0) + self.CurrentAngle = self.CurrentAngle + TargetBearing / 2 end elseif not self.Active and self.SlewRate ~= 0 then self.SlewRate = self.SlewRate - (math.min(SlewAccel, math.abs(self.SlewRate)) * (self.SlewRate >= 0 and 1 or -1)) end - self.CurrentAngle = self.CurrentAngle + Angle(0, math.Clamp(self.SlewRate + StabAmt,-SlewMax,SlewMax), 0) + self.CurrentAngle = self.CurrentAngle + math.Clamp(self.SlewRate + StabAmt,-SlewMax,SlewMax) if self.HasArc then - self.CurrentAngle = Angle(0,math.Clamp(self.CurrentAngle.yaw,-self.MaxDeg,-self.MinDeg),0) + self.CurrentAngle = math.Clamp(self.CurrentAngle,-self.MaxDeg,-self.MinDeg) end - self.CurrentAngle:Normalize() + self.CurrentAngle = math.NormalizeAngle(self.CurrentAngle) - WireLib.TriggerOutput(self, "Degrees", -self.CurrentAngle.yaw) + WireLib.TriggerOutput(self, "Degrees", -self.CurrentAngle) - Rotator:SetAngles(self:LocalToWorldAngles(self.CurrentAngle)) + self.SlewFuncs.SetRotatorAngle(self) - local MotorSpeed = math.Clamp(math.abs(self.SlewRate + StabAmt),0,SlewMax) / Tick + local MotorSpeed = math.Clamp(math.abs(self.CurrentAngle - AngleChange),0,SlewMax) / Tick local MotorSpeedPerc = MotorSpeed / self.MotorMaxSpeed if MotorSpeedPerc > 0.1 and (self.SoundPlaying == false) then @@ -807,9 +858,13 @@ do -- Metamethods self:SetSoundState(false) end - if self.SoundPlaying == true then Sounds.SendAdjustableSound(self,false, 70 + math.ceil(MotorSpeedPerc * 30), 0.1 + (self.EffortScale * 0.9)) end - - debugoverlay.Line(Rotator:GetPos(), Rotator:GetPos() + Rotator:GetForward() * 16384, 0.05, Color(255,0,0), false) + if self.SoundPlaying == true then + if self.SoundPath ~= (self.CurrentSound or "") then -- should only get set off if the motor is enabled/disabled while the sound is playing + self:SetSoundState(false) + else + Sounds.SendAdjustableSound(self,false, 70 + math.ceil(MotorSpeedPerc * 30), 0.1 + (self.EffortScale * 0.9)) + end + end self.LastRotatorAngle = Rotator:GetAngles() @@ -820,32 +875,33 @@ do -- Metamethods do -- Input/Outputs/Eventually linking ACF.AddInputAction("acf_turret", "Active", function(Entity,Value) + if Entity.Disabled then return end + Entity.Active = tobool(Value) end) ACF.AddInputAction("acf_turret", "Angle", function(Entity,Value) local Ang = isangle(Value) and Value or Angle(0,0,0) - Entity.Manual = false - Entity.DesiredAngle = Ang + + Entity:InputDirection(Ang) end) ACF.AddInputAction("acf_turret", "Vector", function(Entity,Value) local Pos = isvector(Value) and Value or Vector(0,0,0) - Entity.Manual = false - Entity.UseVector = true - Entity.DesiredVector = Pos + + Entity:InputDirection(Pos) end) ACF.AddInputAction("acf_turret", "Bearing", function(Entity,Value) -- Only on horizontal drives if not isnumber(Value) then return end - Entity.Manual = true - Entity.DesiredDeg = Value + + Entity:InputDirection(Value) end) ACF.AddInputAction("acf_turret", "Elevation", function(Entity,Value) -- Only on vertical drives if not isnumber(Value) then return end - Entity.Manual = true - Entity.DesiredDeg = Value + + Entity:InputDirection(Value) end) end @@ -929,85 +985,15 @@ do -- Metamethods self:UpdateOverlay() end - local function ProxyACF_OnParented(self,Entity,Connected) - if not IsValid(Entity) then return end - if Entity:GetClass() ~= "acf_turret" then - if not (IsValid(self.ACF_TurretAncestor) or (Contraption.HasAncestor(self,self.ACF_TurretAncestor))) then - self.ACF_OnParented = nil - self.ACF_OnMassChange = nil - self.ACF_TurretAncestor = nil - return - end - - self.ACF_TurretAncestor:UpdateTurretMass() - - if Connected == true then - Entity.ACF_OnParented = self.ACF_OnParented - Entity.ACF_OnMassChange = self.ACF_OnMassChange - Entity.ACF_TurretAncestor = self.ACF_TurretAncestor - - for k in pairs(Entity:GetChildren()) do - if k:GetClass() == "acf_turret" then continue end - if not IsValid(k) then continue end - - ProxyACF_OnParented(Entity,k,true) - end - else - Entity.ACF_OnParented = nil - Entity.ACF_OnMassChange = nil - Entity.ACF_TurretAncestor = nil - end - end - end - - function ENT:ACF_OnParented(Entity, Connected) -- Potentially called many times a second, so we won't force mass to update + function ENT:ACF_OnParented(Entity, _) -- Potentially called many times a second, so we won't force mass to update if Entity:GetClass() == "acf_turret_rotator" then return end - self:UpdateTurretMass() - - if Entity:GetClass() == "acf_turret" then - if self:GetClass() == "acf_turret" then - if Connected == true then - Entity.ACF_TurretAncestor = self - self:UpdateTurretMass() - else - Entity.ACF_TurretAncestor = nil - if IsValid(Entity) then self:UpdateTurretMass() end - end - end - return - elseif IsValid(self.ACF_TurretAncestor) then - self.ACF_TurretAncestor:UpdateTurretMass() - end + self:UpdateTurretMass(false) -- Should only be called when parenting, checks the position of the motor relative to the ring -- Shooouuld be using ACF_OnParented as it was made with this in mind, but turret entities will overwrite it with the above function to ensure everything is captured if Entity:GetClass() == "acf_turret_motor" then Entity:ValidatePlacement() end if IsValid(self.Motor) then self.Motor:ValidatePlacement() end - - if Connected then - Entity.ACF_TurretAncestor = self - - Entity.ACF_OnMassChange = function(self) - if not IsValid(self.ACF_TurretAncestor) then self.ACF_OnMassChange = nil return end - if not IsValid(Entity) then return end - - self.ACF_TurretAncestor:UpdateTurretMass() - end - - Entity.ACF_OnParented = ProxyACF_OnParented - - for k in pairs(Entity:GetChildren()) do - if k:GetClass() == "acf_turret" then continue end - if not IsValid(k) then continue end - - ProxyACF_OnParented(Entity,k,true) - end - else - Entity.ACF_OnMassChange = nil - Entity.ACF_OnParented = nil - Entity.ACF_TurretAncestor = nil - end end function ENT:OnRemove() diff --git a/lua/entities/acf_turret_gyro/init.lua b/lua/entities/acf_turret_gyro/init.lua index 1a944a69b..1cd6978d3 100644 --- a/lua/entities/acf_turret_gyro/init.lua +++ b/lua/entities/acf_turret_gyro/init.lua @@ -6,6 +6,7 @@ include("shared.lua") -- Local Vars local ACF = ACF +local Damage = ACF.Damage local Classes = ACF.Classes local Utilities = ACF.Utilities local HookRun = hook.Run @@ -223,12 +224,8 @@ do -- Metamethods and other important stuff self:UpdateOverlay() end - function ENT:ACF_OnDamage(DmgResult, DmgInfo) - local HitRes = Damage.doPropDamage(self, DmgResult, DmgInfo) - + function ENT:ACF_PostDamage() self.DamageScale = math.max((self.ACF.Health / (self.ACF.MaxHealth * 0.75)) - 0.25 / 0.75,0) - - return HitRes end function ENT:ACF_OnRepaired() -- Normally has OldArmor, OldHealth, Armor, and Health passed @@ -249,6 +246,8 @@ do -- Metamethods and other important stuff end function ENT:IsActive() + if self.Disabled then return false end + if not (IsValid(self.Turret) or IsValid(self["Turret-H"]) or IsValid(self["Turret-V"])) then self:SetActive(false,"") return false end if (self.ACF.Health / self.ACF.MaxHealth) <= 0.25 then diff --git a/lua/entities/acf_turret_motor/init.lua b/lua/entities/acf_turret_motor/init.lua index 40e729183..2bb1d527f 100644 --- a/lua/entities/acf_turret_motor/init.lua +++ b/lua/entities/acf_turret_motor/init.lua @@ -6,6 +6,7 @@ include("shared.lua") -- Local Vars local ACF = ACF +local Damage = ACF.Damage local Classes = ACF.Classes local Utilities = ACF.Utilities local HookRun = hook.Run @@ -219,16 +220,16 @@ do -- Metamethods and other important stuff self:UpdateOverlay() end - function ENT:ACF_OnDamage(DmgResult, DmgInfo) - local HitRes = Damage.doPropDamage(self, DmgResult, DmgInfo) - + function ENT:ACF_PostDamage() self.DamageScale = math.max((self.ACF.Health / (self.ACF.MaxHealth * 0.75)) - 0.25 / 0.75,0) - return HitRes + if self.Turret then self.Turret:UpdateTurretSlew() end end function ENT:ACF_OnRepaired() -- Normally has OldArmor, OldHealth, Armor, and Health passed self.DamageScale = math.max((self.ACF.Health / (self.ACF.MaxHealth * 0.75)) - 0.25 / 0.75,0) + + if self.Turret then self.Turret:UpdateTurretSlew() end end function ENT:ACF_Activate(Recalc) @@ -254,8 +255,6 @@ do -- Metamethods and other important stuff self.Active = Active self.InactiveReason = Reason - if IsValid(self.Turret) then self.Turret:UpdateTurretSlew() end - self:UpdateOverlay(true) end @@ -295,9 +294,10 @@ do -- Metamethods and other important stuff end function ENT:IsActive() + if self.Disabled then return false end if self.ValidPlacement == false then return false end - if (self.ACF.Health / self.ACF.MaxHealth) <= 0.5 then + if (self.ACF.Health / self.ACF.MaxHealth) <= 0.25 then self:SetActive(false,"Too damaged!") return false end @@ -307,7 +307,7 @@ do -- Metamethods and other important stuff end function ENT:GetInfo() - return {Teeth = self.Teeth, Speed = self.Speed, Torque = self.Torque, Efficiency = self.Efficiency, Accel = self.Accel} + return {Teeth = self.Teeth, Speed = self.Speed, Torque = self.Torque * self.DamageScale, Efficiency = self.Efficiency, Accel = self.Accel} end end end \ No newline at end of file diff --git a/lua/entities/acf_turret_rotator/init.lua b/lua/entities/acf_turret_rotator/init.lua index 10a3411cf..37d9b7cdd 100644 --- a/lua/entities/acf_turret_rotator/init.lua +++ b/lua/entities/acf_turret_rotator/init.lua @@ -1,17 +1,21 @@ include("shared.lua") -ACF.Contraption.AddParentDetour("acf_turret_rotator", "turret") - -function ENT:Initialize(turret) - self.turret = turret or self:GetParent() -end +ACF.Contraption.AddParentDetour("acf_turret_rotator", "Turret") +-- One can't exist without the other function ENT:OnRemove() - if IsValid(self.turret) then - self.turret:Remove() + if IsValid(self.Turret) then + self.Turret:Remove() end end +-- This shouldn't be called usually due to parent detouring, but in the offchance that this is ever directly unparented from the turret ring, destroy it and the turret entity since it is no longer a valid turret +function ENT:ACF_OnParented(Entity, Connected) + if not IsValid(Entity) then return end + + if Connected == false and Entity == self.Turret then self:Remove() end +end + function ENT:UpdateTransmitState() return TRANSMIT_PVS end \ No newline at end of file diff --git a/models/acf/core/t_ring.dx80.vtx b/models/acf/core/t_ring.dx80.vtx new file mode 100644 index 000000000..4cd890359 Binary files /dev/null and b/models/acf/core/t_ring.dx80.vtx differ diff --git a/models/acf/core/t_ring.dx90.vtx b/models/acf/core/t_ring.dx90.vtx new file mode 100644 index 000000000..726750b47 Binary files /dev/null and b/models/acf/core/t_ring.dx90.vtx differ diff --git a/models/acf/core/t_ring.mdl b/models/acf/core/t_ring.mdl new file mode 100644 index 000000000..aac20e600 Binary files /dev/null and b/models/acf/core/t_ring.mdl differ diff --git a/models/acf/core/t_ring.phy b/models/acf/core/t_ring.phy new file mode 100644 index 000000000..0c67aa3f7 Binary files /dev/null and b/models/acf/core/t_ring.phy differ diff --git a/models/acf/core/t_ring.sw.vtx b/models/acf/core/t_ring.sw.vtx new file mode 100644 index 000000000..446d750f2 Binary files /dev/null and b/models/acf/core/t_ring.sw.vtx differ diff --git a/models/acf/core/t_ring.vvd b/models/acf/core/t_ring.vvd new file mode 100644 index 000000000..b2a75330b Binary files /dev/null and b/models/acf/core/t_ring.vvd differ diff --git a/models/acf/core/t_trun.dx80.vtx b/models/acf/core/t_trun.dx80.vtx new file mode 100644 index 000000000..757262dcf Binary files /dev/null and b/models/acf/core/t_trun.dx80.vtx differ diff --git a/models/acf/core/t_trun.dx90.vtx b/models/acf/core/t_trun.dx90.vtx new file mode 100644 index 000000000..755dc1453 Binary files /dev/null and b/models/acf/core/t_trun.dx90.vtx differ diff --git a/models/acf/core/t_trun.mdl b/models/acf/core/t_trun.mdl new file mode 100644 index 000000000..94f99f7f9 Binary files /dev/null and b/models/acf/core/t_trun.mdl differ diff --git a/models/acf/core/t_trun.phy b/models/acf/core/t_trun.phy new file mode 100644 index 000000000..279059d64 Binary files /dev/null and b/models/acf/core/t_trun.phy differ diff --git a/models/acf/core/t_trun.sw.vtx b/models/acf/core/t_trun.sw.vtx new file mode 100644 index 000000000..5bbf1e06a Binary files /dev/null and b/models/acf/core/t_trun.sw.vtx differ diff --git a/models/acf/core/t_trun.vvd b/models/acf/core/t_trun.vvd new file mode 100644 index 000000000..3f9888b66 Binary files /dev/null and b/models/acf/core/t_trun.vvd differ