From 99e9eaf556fc37fbb6254bc545668caf610fe1e2 Mon Sep 17 00:00:00 2001 From: Vitroze Date: Mon, 26 Jan 2026 19:42:49 +0100 Subject: [PATCH 1/3] Addition of a trailer attachment and detachment system --- README.md | 5 + lua/autorun/sh_glide.lua | 3 + lua/entities/base_glide/init.lua | 99 +++++++++++++++++++ lua/entities/base_glide/sv_input.lua | 2 + lua/entities/base_glide_trailer.lua | 42 ++++++++ lua/glide/sh_input.lua | 1 + .../localization/en/glide_vehicles.properties | 3 + .../localization/fr/glide_vehicles.properties | 3 + 8 files changed, 158 insertions(+) diff --git a/README.md b/README.md index 1f5862a2..f0e7f52f 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,11 @@ A pack of content and entity classes to add cars, motorcycles, planes, helicopte | `glide_global_damage_multiplier` `` | Multiplier for damage taken from any source (except from collisions) | `glide_player_collision_damage_multiplier` `` | Damage multiplier for players inside vehicles taking damage from hard collisions. +### Trailer +| Command | Description +| ------- | ----------- +| `glide_trailer_attach_percentage_wheel` `` | Percentage of wheels required on trailer to attach it to a vehicle. Default : 0.6 + ### Sandbox limits | Command | Description diff --git a/lua/autorun/sh_glide.lua b/lua/autorun/sh_glide.lua index abe63b0e..8c28de6c 100644 --- a/lua/autorun/sh_glide.lua +++ b/lua/autorun/sh_glide.lua @@ -183,6 +183,9 @@ if SERVER then CreateConVar( "glide_world_physics_damage_multiplier", "1", FCVAR_ARCHIVE + FCVAR_NOTIFY, "Damage multiplier taken by Glide vehicles after colliding against the world.", 0, 10 ) CreateConVar( "glide_global_damage_multiplier", "1", FCVAR_ARCHIVE + FCVAR_NOTIFY, "Multiplier for damage taken from any source (except collisions).", 0, 10 ) CreateConVar( "glide_player_collision_damage_multiplier", "0.5", FCVAR_ARCHIVE + FCVAR_NOTIFY, "Damage multiplier for players inside vehicles taking damage from hard collisions.", 0, 2 ) + + -- Trailer convars + CreateConVar( "glide_trailer_attach_percentage_wheel", "0.6", FCVAR_ARCHIVE + FCVAR_NOTIFY + FCVAR_REPLICATED, "Percentage of wheels required on trailer to attach it to a vehicle.", 0, 1 ) end -- Toggles diff --git a/lua/entities/base_glide/init.lua b/lua/entities/base_glide/init.lua index 2e84005a..fd3fceac 100644 --- a/lua/entities/base_glide/init.lua +++ b/lua/entities/base_glide/init.lua @@ -841,3 +841,102 @@ function ENT:GetSpawnColor() local color = colors[math.random( #colors )] return Color( color.r, color.g, color.b ) end + +function ENT:AttachTrailer( trailer ) + if not IsValid( trailer ) then return end + + trailer:AttachToVehicle( self ) + self.AttachedTrailer = trailer + + Glide.SendNotification( self:GetAllPlayers(), { + text = "#glide.notify.vehicle_attach", + icon = "materials/glide/icons/trailer.png", + sound = "buttons/button15.wav", + immediate = true + } ) +end + +function ENT:DetachTrailer() + if not IsValid( self.AttachedTrailer ) then return end + + self.AttachedTrailer:DetachFromVehicle( self ) + self.AttachedTrailer = nil + + Glide.SendNotification( self:GetAllPlayers(), { + text = "#glide.notify.vehicle_detach", + icon = "materials/glide/icons/trailer.png", + sound = "buttons/button15.wav", + immediate = true + } ) +end + +local percentageWheel = GetConVar( "glide_trailer_attach_percentage_wheel" ):GetFloat() -- At least 60% of wheels must be on the trailer +local minWheels = { + ["base_glide_heli"] = 0, + ["base_glide_boat"] = 0, + ["base_glide_bike"] = 2, + ["base_glide_motorcycle"] = 2, + ["base_glide_plane"] = 2, + ["base_glide_car"] = 4, + ["base_glide_tank"] = 4, +} + +function ENT:AttachVehicle() + if self.cooldownAttachTrailer and CurTime() < self.cooldownAttachTrailer then return end + self.cooldownAttachTrailer = CurTime() + 0.5 + + if self:GetSpeed() > 5 then return end + + local minWheel = minWheels[self.Base] + if not minWheel then return end + + if IsValid( self.AttachedTrailer ) then + self:DetachTrailer() + else + local totalWheels = table.Count( self.wheels ) or 0 + if minWheel == 0 or totalWheels == 0 then + + local posVehicle = self:GetPos() + local Trace = util.TraceLine( { + start = posVehicle, + endpos = posVehicle - Vector( 0, 0, 200 ), + filter = function( ent ) + return ent:GetClass() ~= "glide_wheel" and ent ~= self + end + } ) + + local trailerEntity = Trace.Entity + if IsValid( trailerEntity ) and trailerEntity.Base == "base_glide_trailer" then + self:AttachTrailer( trailerEntity ) + end + else + totalWheels = math.max( totalWheels, minWheel ) + + local wheelCount = 0 + local trailerEntity = nil + for _, wheel in Glide.EntityPairs( self.wheels ) do + if not IsValid( wheel ) then continue end + + local posWheel = wheel:GetPos() + local Trace = util.TraceLine( { + start = posWheel, + endpos = posWheel - Vector( 0, 0, 200 ), + filter = function( ent ) + return ent:GetClass() ~= "glide_wheel" and ent ~= self + end + } ) + + local trailer = Trace.Entity + if IsValid( trailer ) and trailer.Base == "base_glide_trailer" and ( not IsValid( trailerEntity ) or trailerEntity == trailer ) then + wheelCount = wheelCount + 1 + + trailerEntity = Trace.Entity + end + end + + if math.ceil( wheelCount / totalWheels ) >= percentageWheel then + self:AttachTrailer( trailerEntity ) + end + end + end +end \ No newline at end of file diff --git a/lua/entities/base_glide/sv_input.lua b/lua/entities/base_glide/sv_input.lua index c4219e8a..a74b9353 100644 --- a/lua/entities/base_glide/sv_input.lua +++ b/lua/entities/base_glide/sv_input.lua @@ -174,6 +174,8 @@ function ENT:SetInputBool( seatIndex, action, pressed ) icon = "materials/glide/icons/" .. ( self.inputThrottleModifierToggle and "play_next" or "fast_forward" ) .. ".png", immediate = true } ) + elseif action == "attach_vehicle" then + self:AttachVehicle() end if action == "toggle_engine" then diff --git a/lua/entities/base_glide_trailer.lua b/lua/entities/base_glide_trailer.lua index 37453261..f15b9e1e 100644 --- a/lua/entities/base_glide_trailer.lua +++ b/lua/entities/base_glide_trailer.lua @@ -105,3 +105,45 @@ function ENT:OnPostThink( _dt, selfTbl ) self:SetTurnSignalState( attachedVehicle:GetTurnSignalState() ) self:SetIsVehicleReversing( attachedVehicle:IsReversing() ) end + +function ENT:AttachToVehicle( vehicle ) + if not IsValid( vehicle ) then return end + + constraint.Weld( self, vehicle, 0, 0, 0, true ) + constraint.NoCollide( self, vehicle, 0, 0 ) + self.listAttachedVehicle = self.listAttachedVehicle or {} + self.listAttachedVehicle[vehicle] = true + + vehicle:TurnOff() + vehicle:EnableEngine( false ) + vehicle:CallOnRemove( "Glide_TrailerDetach_" .. tostring( self:EntIndex() ), function() + if IsValid( self ) then + self:DetachFromVehicle( vehicle ) + end + end ) +end + +function ENT:DetachFromVehicle( vehicle ) + if not IsValid( vehicle ) then return end + + constraint.RemoveConstraints( vehicle, "Weld" ) + constraint.RemoveConstraints( vehicle, "NoCollide" ) + if self.listAttachedVehicle then + self.listAttachedVehicle[vehicle] = nil + end + + vehicle.AttachedTrailer = nil + vehicle:TurnOff() + vehicle:EnableEngine( true ) + vehicle:RemoveCallOnRemove( "Glide_TrailerDetach_" .. tostring( self:EntIndex() ) ) +end + +function ENT:GetAttachedVehicles() + return self.listAttachedVehicle or {} +end + +function ENT:OnRemove() + for vehicle, _ in pairs( self:GetAttachedVehicles() ) do + self:DetachFromVehicle( vehicle ) + end +end \ No newline at end of file diff --git a/lua/glide/sh_input.lua b/lua/glide/sh_input.lua index aedad742..f8f97acd 100644 --- a/lua/glide/sh_input.lua +++ b/lua/glide/sh_input.lua @@ -78,6 +78,7 @@ Glide.AddInputAction( "general_controls", "switch_weapon", KEY_R ) Glide.AddInputAction( "general_controls", "toggle_engine", KEY_I ) Glide.AddInputAction( "general_controls", "headlights", KEY_H ) Glide.AddInputAction( "general_controls", "free_look", KEY_LALT ) +Glide.AddInputAction( "general_controls", "attach_vehicle", KEY_M ) --[[ Inputs that only apply to land vehicle types diff --git a/resource/localization/en/glide_vehicles.properties b/resource/localization/en/glide_vehicles.properties index 6fe55693..e50d90ab 100644 --- a/resource/localization/en/glide_vehicles.properties +++ b/resource/localization/en/glide_vehicles.properties @@ -101,6 +101,7 @@ glide.input.signal_right=Right turn signal glide.input.aircraft_controls=Aircraft controls glide.input.countermeasures=Countermeasures glide.input.free_look=Free look (Flying with mouse only) +glide.input.attach_vehicle=Attach/Detach vehicle to trailer glide.input.landing_gear=Landing gear glide.input.pitch_up=Pitch up glide.input.pitch_down=Pitch down @@ -204,6 +205,8 @@ glide.notify.tip.countermeasures=Press %s to deploy countermeasures. glide.notify.tip.trailer_attached=Trailer attached. Press %s to release it. glide.notify.tip.trailer_detached=Trailer detached. glide.notify.tip.vtol_toggle=Hold %s to toggle the "Vertical Take-Off and Landing" mode. +glide.notify.vehicle_attach=The vehicle has been attached to the trailer. +glide.notify.vehicle_detach=The vehicle has been detached from the trailer. # HUD diff --git a/resource/localization/fr/glide_vehicles.properties b/resource/localization/fr/glide_vehicles.properties index 8b0b7615..fd1943d2 100644 --- a/resource/localization/fr/glide_vehicles.properties +++ b/resource/localization/fr/glide_vehicles.properties @@ -101,6 +101,7 @@ glide.input.signal_right=Clignotant droit glide.input.aircraft_controls=Contrôles des aéronefs glide.input.countermeasures=Contre-mesures glide.input.free_look=Regard libre (Vol uniquement avec la souris) +glide.input.attach_vehicle=Attacher/Détacher un véhicule a une remorque glide.input.landing_gear=Train d'atterrissage glide.input.pitch_up=Monter (Tangage) glide.input.pitch_down=Descendre (Tangage) @@ -205,6 +206,8 @@ glide.notify.tip.countermeasures=Appuyez sur %s pour déployer les contre-mesure glide.notify.tip.trailer_attached=Remorque attachée. Appuyez sur %s pour la libérer. glide.notify.tip.trailer_detached=Remorque détachée. glide.notify.tip.vtol_toggle=Maintenez %s pour basculer le mode « Décollage et atterrissage verticaux ». +glide.notify.vehicle_attach=Le véhicule a été attaché à la remorque. +glide.notify.vehicle_detach=Le véhicule a été détaché de la remorque. # HUD From 3c24062e8614c65221d8cbd2cafeaec9268bf866 Mon Sep 17 00:00:00 2001 From: Vitroze Date: Wed, 28 Jan 2026 15:30:06 +0100 Subject: [PATCH 2/3] Removes the effect of wheels on the attached vehicle when the truck turns --- lua/entities/base_glide/shared.lua | 1 + lua/entities/base_glide_trailer.lua | 2 ++ lua/entities/glide_wheel/cl_init.lua | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lua/entities/base_glide/shared.lua b/lua/entities/base_glide/shared.lua index 4428570c..a87a5e2e 100644 --- a/lua/entities/base_glide/shared.lua +++ b/lua/entities/base_glide/shared.lua @@ -44,6 +44,7 @@ function ENT:SetupDataTables() self:NetworkVar( "Int", "EngineState" ) self:NetworkVar( "Bool", "IsEngineOnFire" ) self:NetworkVar( "Bool", "IsLocked" ) + self:NetworkVar( "Bool", "IsAttachedToTrailer" ) self:NetworkVar( "Int", "LockOnState" ) self:NetworkVar( "Entity", "LockOnTarget" ) diff --git a/lua/entities/base_glide_trailer.lua b/lua/entities/base_glide_trailer.lua index f15b9e1e..f6a6966e 100644 --- a/lua/entities/base_glide_trailer.lua +++ b/lua/entities/base_glide_trailer.lua @@ -116,6 +116,7 @@ function ENT:AttachToVehicle( vehicle ) vehicle:TurnOff() vehicle:EnableEngine( false ) + vehicle:SetIsAttachedToTrailer( true ) vehicle:CallOnRemove( "Glide_TrailerDetach_" .. tostring( self:EntIndex() ), function() if IsValid( self ) then self:DetachFromVehicle( vehicle ) @@ -136,6 +137,7 @@ function ENT:DetachFromVehicle( vehicle ) vehicle:TurnOff() vehicle:EnableEngine( true ) vehicle:RemoveCallOnRemove( "Glide_TrailerDetach_" .. tostring( self:EntIndex() ) ) + vehicle:SetIsAttachedToTrailer( false ) end function ENT:GetAttachedVehicles() diff --git a/lua/entities/glide_wheel/cl_init.lua b/lua/entities/glide_wheel/cl_init.lua index c20fdc1e..1e30593d 100644 --- a/lua/entities/glide_wheel/cl_init.lua +++ b/lua/entities/glide_wheel/cl_init.lua @@ -214,7 +214,7 @@ function ENT:Think() rollFactor = rollFactor + fastFactor end - if rollFactor > 0.1 and selfTbl.enableParticles then + if rollFactor > 0.1 and selfTbl.enableParticles and not parent:GetIsAttachedToTrailer() then rollFactor = Clamp( rollFactor, 0, 0.5 ) local eff = EffectData() @@ -226,7 +226,7 @@ function ENT:Think() Effect( "glide_tire_roll", eff ) end - if forwardSlipFactor > 0.2 and selfTbl.enableParticles then + if forwardSlipFactor > 0.2 and selfTbl.enableParticles and not parent:GetIsAttachedToTrailer() then forwardSlipFactor = Clamp( forwardSlipFactor, 0, 1 ) local eff = EffectData() From ad02ab36651b5b0c2a7ab5acfa91bde56ed49232 Mon Sep 17 00:00:00 2001 From: Vitroze Date: Wed, 28 Jan 2026 15:54:09 +0100 Subject: [PATCH 3/3] Addition of verification and micro-optimization --- lua/entities/base_glide/init.lua | 33 +++++++++++++++++----------- lua/entities/base_glide/sv_input.lua | 2 +- lua/entities/base_glide_trailer.lua | 13 +++++------ 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lua/entities/base_glide/init.lua b/lua/entities/base_glide/init.lua index fd3fceac..a009d38d 100644 --- a/lua/entities/base_glide/init.lua +++ b/lua/entities/base_glide/init.lua @@ -848,7 +848,10 @@ function ENT:AttachTrailer( trailer ) trailer:AttachToVehicle( self ) self.AttachedTrailer = trailer - Glide.SendNotification( self:GetAllPlayers(), { + local driver = self:GetDriver() + if not IsValid( driver ) then return end + + Glide.SendNotification( driver, { text = "#glide.notify.vehicle_attach", icon = "materials/glide/icons/trailer.png", sound = "buttons/button15.wav", @@ -862,7 +865,10 @@ function ENT:DetachTrailer() self.AttachedTrailer:DetachFromVehicle( self ) self.AttachedTrailer = nil - Glide.SendNotification( self:GetAllPlayers(), { + local driver = self:GetDriver() + if not IsValid( driver ) then return end + + Glide.SendNotification( driver, { text = "#glide.notify.vehicle_detach", icon = "materials/glide/icons/trailer.png", sound = "buttons/button15.wav", @@ -877,11 +883,17 @@ local minWheels = { ["base_glide_bike"] = 2, ["base_glide_motorcycle"] = 2, ["base_glide_plane"] = 2, - ["base_glide_car"] = 4, + ["base_glide_car"] = 3, ["base_glide_tank"] = 4, } -function ENT:AttachVehicle() +local OFFSET_WHEEL = Vector( 0, 0, 200 ) + +local function canFilter( vehicle, ent ) + return ent:GetClass() ~= "glide_wheel" and ent ~= vehicle +end + +function ENT:ToggleAttachDetachTrailer() if self.cooldownAttachTrailer and CurTime() < self.cooldownAttachTrailer then return end self.cooldownAttachTrailer = CurTime() + 0.5 @@ -895,14 +907,11 @@ function ENT:AttachVehicle() else local totalWheels = table.Count( self.wheels ) or 0 if minWheel == 0 or totalWheels == 0 then - local posVehicle = self:GetPos() local Trace = util.TraceLine( { start = posVehicle, - endpos = posVehicle - Vector( 0, 0, 200 ), - filter = function( ent ) - return ent:GetClass() ~= "glide_wheel" and ent ~= self - end + endpos = posVehicle - OFFSET_WHEEL, + filter = function( ent ) return canFilter( self, ent ) end } ) local trailerEntity = Trace.Entity @@ -920,10 +929,8 @@ function ENT:AttachVehicle() local posWheel = wheel:GetPos() local Trace = util.TraceLine( { start = posWheel, - endpos = posWheel - Vector( 0, 0, 200 ), - filter = function( ent ) - return ent:GetClass() ~= "glide_wheel" and ent ~= self - end + endpos = posWheel - OFFSET_WHEEL, + filter = function( ent ) return canFilter( self, ent ) end } ) local trailer = Trace.Entity diff --git a/lua/entities/base_glide/sv_input.lua b/lua/entities/base_glide/sv_input.lua index a74b9353..818cddc3 100644 --- a/lua/entities/base_glide/sv_input.lua +++ b/lua/entities/base_glide/sv_input.lua @@ -175,7 +175,7 @@ function ENT:SetInputBool( seatIndex, action, pressed ) immediate = true } ) elseif action == "attach_vehicle" then - self:AttachVehicle() + self:ToggleAttachDetachTrailer() end if action == "toggle_engine" then diff --git a/lua/entities/base_glide_trailer.lua b/lua/entities/base_glide_trailer.lua index f6a6966e..379a8e8d 100644 --- a/lua/entities/base_glide_trailer.lua +++ b/lua/entities/base_glide_trailer.lua @@ -114,13 +114,11 @@ function ENT:AttachToVehicle( vehicle ) self.listAttachedVehicle = self.listAttachedVehicle or {} self.listAttachedVehicle[vehicle] = true - vehicle:TurnOff() vehicle:EnableEngine( false ) vehicle:SetIsAttachedToTrailer( true ) - vehicle:CallOnRemove( "Glide_TrailerDetach_" .. tostring( self:EntIndex() ), function() - if IsValid( self ) then - self:DetachFromVehicle( vehicle ) - end + vehicle:CallOnRemove( ( "Glide_TrailerDetach_%s" ):format( self:EntIndex() ), function() + if not IsValid( self ) then return end + self:DetachFromVehicle( vehicle ) end ) end @@ -129,14 +127,13 @@ function ENT:DetachFromVehicle( vehicle ) constraint.RemoveConstraints( vehicle, "Weld" ) constraint.RemoveConstraints( vehicle, "NoCollide" ) + if self.listAttachedVehicle then self.listAttachedVehicle[vehicle] = nil end - vehicle.AttachedTrailer = nil - vehicle:TurnOff() vehicle:EnableEngine( true ) - vehicle:RemoveCallOnRemove( "Glide_TrailerDetach_" .. tostring( self:EntIndex() ) ) + vehicle:RemoveCallOnRemove( ( "Glide_TrailerDetach_%s" ):format( self:EntIndex() ) ) vehicle:SetIsAttachedToTrailer( false ) end