Initial commit

This commit is contained in:
2026-03-15 14:54:49 +03:00
commit 64f8029c06
4027 changed files with 254888 additions and 0 deletions

533
lua/autorun/base_npcs.lua Normal file
View File

@@ -0,0 +1,533 @@
-- Don't try to edit this file if you're trying to add new NPCs.
-- Just make a new file and copy the format below.
local function AddNPC( t, class )
if ( !t.Name ) then t.Name = "#" .. ( class or t.Class ) end
t.Author = "VALVe"
list.Set( "NPC", class or t.Class, t )
end
local Category = "#spawnmenu.category.humans_resistance"
AddNPC( {
Class = "npc_alyx",
Category = Category,
Weapons = { "weapon_alyxgun", "weapon_smg1", "weapon_shotgun" },
KeyValues = { SquadName = "resistance" }
} )
AddNPC( {
Class = "npc_barney",
Category = Category,
Weapons = { "weapon_smg1", "weapon_shotgun", "weapon_ar2" },
KeyValues = { SquadName = "resistance" }
} )
AddNPC( {
Class = "npc_breen",
Category = Category,
SpawnFlags = 131072, -- SF_BREEN_GMOD_SPAWNMENU, makes him be a combine for NPC relationships
Weapons = { "" }
} )
AddNPC( {
Class = "npc_dog",
Category = Category
} )
AddNPC( {
Class = "npc_eli",
Category = Category,
Weapons = { "" }
} )
AddNPC( {
Class = "npc_gman",
Category = Category
} )
-- Did you know that this MAN can shoot annabelle like he's been doing it his whole life?
AddNPC( {
Class = "npc_kleiner",
Category = Category,
Weapons = { "" }
} )
AddNPC( {
Class = "npc_mossman",
Category = Category,
Weapons = { "" }
} )
-- I don't trust these Vorts, but I'll let em stay in this category until they mess up
AddNPC( {
Class = "npc_vortigaunt",
Category = Category,
KeyValues = { SquadName = "resistance" }
} )
AddNPC( {
Name = "#npc_vortigaunt_slave",
Class = "npc_vortigaunt",
Category = Category,
Model = "models/vortigaunt_slave.mdl"
}, "VortigauntSlave" )
AddNPC( {
Class = "npc_citizen",
Category = Category,
KeyValues = { citizentype = CT_DOWNTRODDEN, SquadName = "resistance" },
Weapons = { "" } -- Tells the spawnmenu that this NPC can use weapons, but doesn't have any default ones
} )
AddNPC( {
Name = "#npc_citizen_rebel",
Class = "npc_citizen",
Category = Category,
SpawnFlags = SF_CITIZEN_RANDOM_HEAD,
KeyValues = { citizentype = CT_REBEL, SquadName = "resistance" },
Weapons = { "weapon_pistol", "weapon_smg1", "weapon_ar2", "weapon_shotgun", "weapon_rpg" }
}, "Rebel" )
AddNPC( {
Class = "npc_citizen",
Category = Category,
Model = "models/odessa.mdl",
KeyValues = { citizentype = CT_UNIQUE, SquadName = "resistance" },
Weapons = { "" }
}, "npc_odessa" )
AddNPC( {
Name = "#npc_citizen_medic",
Class = "npc_citizen",
Category = Category,
SpawnFlags = SERVER and bit.bor( SF_NPC_DROP_HEALTHKIT, SF_CITIZEN_MEDIC ) or nil,
KeyValues = { citizentype = CT_REBEL, SquadName = "resistance" },
Weapons = { "weapon_pistol", "weapon_smg1", "weapon_ar2" }
}, "Medic" )
AddNPC( {
Name = "#npc_citizen_refugee",
Class = "npc_citizen",
Category = Category,
KeyValues = { citizentype = CT_REFUGEE, SquadName = "resistance" },
Weapons = { "weapon_pistol", "weapon_smg1" }
}, "Refugee" )
AddNPC( {
Name = "#npc_vortigaunt_uriah",
Class = "npc_vortigaunt",
Category = Category,
Model = "models/vortigaunt_doctor.mdl",
KeyValues = { SquadName = "resistance" }
}, "VortigauntUriah" )
AddNPC( {
Class = "npc_magnusson",
Category = Category,
Weapons = { "" }
} )
if ( IsMounted( "lostcoast" ) ) then
AddNPC( {
Class = "npc_fisherman",
Category = Category,
Weapons = { "weapon_oldmanharpoon" }
} ) -- Has no death sequence/ragdoll
end
AddNPC( {
Class = "npc_turret_floor",
Category = Category,
OnFloor = true,
TotalSpawnFlags = SF_FLOOR_TURRET_CITIZEN,
Rotate = Angle( 0, 180, 0 ),
Offset = 2,
KeyValues = { SquadName = "resistance" }
}, "npc_turret_floor_resistance" )
AddNPC( {
Class = "npc_rollermine",
Category = Category,
Offset = 20,
KeyValues = { SquadName = "resistance" },
SpawnFlags = 262144, -- SF_ROLLERMINE_HACKED
NoDrop = true
}, "npc_rollermine_hacked" )
Category = "#spawnmenu.category.zombies_aliens"
AddNPC( {
Class = "npc_zombie",
Category = Category,
KeyValues = { SquadName = "zombies" }
} )
AddNPC( {
Class = "npc_zombie_torso",
Category = Category,
KeyValues = { SquadName = "zombies" }
} )
AddNPC( {
Class = "npc_poisonzombie",
Category = Category,
KeyValues = { SquadName = "poison" }
} )
AddNPC( {
Class = "npc_antlion",
Category = Category,
KeyValues = { SquadName = "antlions" }
} )
AddNPC( {
Class = "npc_antlionguard",
Category = Category,
KeyValues = { SquadName = "antlions" }
} )
AddNPC( {
Class = "npc_barnacle",
Category = Category,
OnCeiling = true,
Offset = 2
} )
AddNPC( {
Class = "npc_fastzombie",
Category = Category,
KeyValues = { SquadName = "zombies" }
} )
AddNPC( {
Class = "npc_headcrab",
Category = Category,
KeyValues = { SquadName = "zombies" }
} )
AddNPC( {
Class = "npc_headcrab_black",
Category = Category,
KeyValues = { SquadName = "poison" }
} )
AddNPC( {
Class = "npc_headcrab_fast",
Category = Category,
KeyValues = { SquadName = "zombies" }
} )
AddNPC( {
Class = "npc_fastzombie_torso",
Category = Category,
KeyValues = { SquadName = "zombies" }
} )
AddNPC( {
Class = "npc_zombine",
Category = Category,
KeyValues = { SquadName = "zombies" }
} )
AddNPC( {
Class = "npc_antlionguard",
Category = Category,
KeyValues = { cavernbreed = 1, incavern = 1, SquadName = "antlions" },
Material = "Models/antlion_guard/antlionGuard2"
}, "npc_antlionguardian" )
AddNPC( {
Class = "npc_antlion_grub",
Category = Category,
NoDrop = true,
Offset = 1
} )
AddNPC( {
Class = "npc_antlion_worker",
Category = Category,
KeyValues = { SquadName = "antlions" }
} )
Category = "#spawnmenu.category.animals"
AddNPC( {
Class = "npc_monk",
Category = Category,
Weapons = { "weapon_annabelle" }
} )
AddNPC( {
Class = "npc_crow",
Category = Category,
NoDrop = true
} )
AddNPC( {
Class = "npc_pigeon",
Category = Category,
NoDrop = true
} )
AddNPC( {
Class = "npc_seagull",
Category = Category,
NoDrop = true
} )
Category = "#spawnmenu.category.combine"
AddNPC( {
Class = "npc_metropolice",
Category = Category,
Weapons = { "weapon_stunstick", "weapon_pistol", "weapon_smg1" },
SpawnFlags = SF_NPC_DROP_HEALTHKIT,
KeyValues = { SquadName = "overwatch" }
} )
AddNPC( {
Class = "npc_rollermine",
Category = Category,
Offset = 20,
KeyValues = { SquadName = "overwatch" },
NoDrop = true
} )
-- It is still considered an enemy by friendly NPCs (so that it chases them)
AddNPC( {
Class = "npc_rollermine",
Category = Category,
Offset = 20,
KeyValues = { SquadName = "overwatch" },
SpawnFlags = 65536, -- SF_ROLLERMINE_FRIENDLY
NoDrop = true
}, "npc_rollermine_friendly" )
AddNPC( {
Class = "npc_turret_floor",
Category = Category,
OnFloor = true,
TotalSpawnFlags = 0,
Rotate = Angle( 0, 180, 0 ),
Offset = 2,
KeyValues = { SquadName = "overwatch" }
} )
AddNPC( {
Class = "npc_combine_s",
Category = Category,
Model = "models/combine_soldier.mdl",
Skin = 0,
Weapons = { "weapon_smg1", "weapon_ar2" },
KeyValues = { SquadName = "overwatch", Numgrenades = 5 }
} )
AddNPC( {
Name = "#npc_combine_s_shotgun",
Class = "npc_combine_s",
Category = Category,
Model = "models/combine_soldier.mdl",
Skin = 1,
Weapons = { "weapon_shotgun" },
KeyValues = { SquadName = "overwatch", Numgrenades = 5 }
}, "ShotgunSoldier" )
AddNPC( {
Name = "#npc_combine_s_prison",
Class = "npc_combine_s",
Category = Category,
Model = "models/combine_soldier_prisonguard.mdl",
Skin = 0,
Weapons = { "weapon_smg1", "weapon_ar2" },
KeyValues = { SquadName = "novaprospekt", Numgrenades = 5 }
}, "CombinePrison" )
AddNPC( {
Name = "#npc_combine_s_prison_shotgun",
Class = "npc_combine_s",
Category = Category,
Model = "models/combine_soldier_prisonguard.mdl",
Skin = 1,
Weapons = { "weapon_shotgun" },
KeyValues = { SquadName = "novaprospekt", Numgrenades = 5 }
}, "PrisonShotgunner" )
AddNPC( {
Name = "#npc_combine_s_elite",
Class = "npc_combine_s",
Category = Category,
Model = "models/combine_super_soldier.mdl",
Skin = 0,
Weapons = { "weapon_ar2" },
KeyValues = { Numgrenades = 10, SquadName = "overwatch" },
SpawnFlags = SF_NPC_NO_PLAYER_PUSHAWAY
}, "CombineElite" )
AddNPC( {
Class = "npc_cscanner",
Category = Category,
Offset = 20,
KeyValues = { SquadName = "overwatch", SpotlightLength = 500, SpotlightWidth = 100 },
NoDrop = true
} )
AddNPC( {
Class = "npc_clawscanner",
Category = Category,
Offset = 20,
KeyValues = { SquadName = "overwatch", SpotlightLength = 500, SpotlightWidth = 100 },
NoDrop = true
} )
AddNPC( {
Class = "npc_combinegunship",
Category = Category,
Offset = 300,
KeyValues = { SquadName = "overwatch" },
NoDrop = true
} )
AddNPC( {
Class = "npc_combinedropship",
Category = Category,
Offset = 300,
KeyValues = { SquadName = "overwatch" },
NoDrop = true
} )
AddNPC( {
Class = "npc_helicopter",
Category = Category,
Offset = 300,
Health = 600,
KeyValues = { SquadName = "overwatch" },
NoDrop = true
} )
AddNPC( {
Class = "npc_combine_camera",
Category = Category,
OnCeiling = true,
Offset = 2,
KeyValues = { SquadName = "overwatch" },
NoDrop = true
} )
AddNPC( {
Class = "npc_turret_ceiling",
Category = Category,
SpawnFlags = 32, -- SF_NPC_TURRET_AUTOACTIVATE
OnCeiling = true,
Offset = 0,
KeyValues = { SquadName = "overwatch" }
} )
AddNPC( {
Class = "npc_strider",
Category = Category,
Offset = 100,
KeyValues = { SquadName = "overwatch" }
} )
AddNPC( {
Class = "npc_stalker",
Category = Category,
KeyValues = { SquadName = "npc_stalker_squad" },
Offset = 10
} )
AddNPC( {
Class = "npc_manhack",
Category = Category,
KeyValues = { SquadName = "overwatch" },
NoDrop = true
} )
-- This is meant for NPC reskins, so humanoid NPC reskins don't sound like combine.
-- This is also just for fun, and exists here to let people know that the option exists and how to use it.
AddNPC( {
Class = "npc_citizen",
Category = Category,
KeyValues = { citizentype = CT_REBEL, SquadName = "overwatch", Hostile = "1" },
Weapons = { "weapon_pistol", "weapon_smg1", "weapon_ar2", "weapon_shotgun", "weapon_rpg" }
}, "npc_citizen_rebel_enemy" )
AddNPC( {
Class = "npc_hunter",
Category = Category,
KeyValues = { SquadName = "overwatch" }
} )
if ( IsMounted( "hl1" ) or IsMounted( "hl1mp" ) ) then
Category = "Half-Life: Source"
AddNPC( { Class = "monster_alien_grunt", Category = Category } )
AddNPC( { Class = "monster_nihilanth", Category = Category, Offset = 1200, SpawnFlags = 262144, NoDrop = true } )
AddNPC( { Class = "monster_tentacle", Category = Category } )
AddNPC( { Class = "monster_alien_slave", Category = Category } )
AddNPC( { Class = "monster_bigmomma", Category = Category } )
AddNPC( { Class = "monster_bullchicken", Category = Category } )
AddNPC( { Class = "monster_gargantua", Category = Category } )
AddNPC( { Class = "monster_human_assassin", Category = Category } )
AddNPC( { Class = "monster_babycrab", Category = Category } )
AddNPC( { Class = "monster_human_grunt", Category = Category } )
AddNPC( { Class = "monster_cockroach", Category = Category } )
AddNPC( { Class = "monster_houndeye", Category = Category } )
AddNPC( { Class = "monster_scientist", Category = Category, KeyValues = { body = "-1" } } )
AddNPC( { Class = "monster_snark", Category = Category, Offset = 6, NoDrop = true } )
AddNPC( { Class = "monster_zombie", Category = Category } )
AddNPC( { Class = "monster_headcrab", Category = Category } )
AddNPC( { Class = "monster_alien_controller", Category = Category, NoDrop = true } )
AddNPC( { Class = "monster_barney", Category = Category } )
-- Hack to have it not invert angles again
local turretOnDupe = function( npc, data ) npc:SetKeyValue( "spawnflags", bit.bor( npc.SpawnFlags, 2048 ) ) end
local turretOnCeiling = function( npc ) npc:SetKeyValue( "orientation", 1 ) end
AddNPC( { Class = "monster_turret", Category = Category, Offset = 0, OnCeiling = turretOnCeiling, OnFloor = true, SpawnFlags = 32, OnDuplicated = turretOnDupe } )
AddNPC( { Class = "monster_miniturret", Category = Category, Offset = 0, OnCeiling = turretOnCeiling, OnFloor = true, SpawnFlags = 32, OnDuplicated = turretOnDupe } )
AddNPC( { Class = "monster_sentry", Category = Category, Offset = 0, OnFloor = true, SpawnFlags = 32 } )
end
if ( IsMounted( "portal" ) ) then
Category = "Portal"
AddNPC( {
Class = "npc_portal_turret_floor",
Category = Category,
OnFloor = true,
Rotate = Angle( 0, 180, 0 ),
Offset = 2,
TotalSpawnFlags = 0,
} )
AddNPC( {
Class = "npc_rocket_turret",
Category = Category,
OnFloor = true,
SpawnFlags = 2, --SF_ROCKET_TURRET_SPAWNMENU, makes it target NPCs
Offset = 0,
Rotate = Angle( 0, 180, 0 ),
} )
AddNPC( {
Class = "npc_security_camera",
Category = Category,
Offset = -1,
SpawnFlags = 32, --SF_SECURITY_CAMERA_AUTOACTIVATE
SnapToNormal = true,
NoDrop = true
} )
end

View File

@@ -0,0 +1,269 @@
-- Don't try to edit this file if you're trying to add new vehicles
-- Just make a new file and copy the format below.
local function AddVehicle( t, class )
list.Set( "Vehicles", class, t )
end
local Category = "Half-Life 2"
AddVehicle( {
-- Required information
Name = "#spawnmenu.vehicle.jeep",
Model = "models/buggy.mdl",
Class = "prop_vehicle_jeep_old",
Category = Category,
-- Optional information
Author = "VALVe",
Information = "The regular old jeep",
KeyValues = {
vehiclescript = "scripts/vehicles/jeep_test.txt"
}
}, "Jeep" )
AddVehicle( {
Name = "#spawnmenu.vehicle.airboat",
Model = "models/airboat.mdl",
Class = "prop_vehicle_airboat",
Category = Category,
Author = "VALVe",
Information = "Airboat from Half-Life 2",
KeyValues = {
vehiclescript = "scripts/vehicles/airboat.txt"
}
}, "Airboat" )
AddVehicle( {
Name = "#spawnmenu.vehicle.prisoner_pod",
Model = "models/vehicles/prisoner_pod_inner.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "VALVe",
Information = "The prisoner pod",
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
}
}, "Pod" )
AddVehicle( {
Name = "#spawnmenu.vehicle.jalopy",
Model = "models/vehicle.mdl",
Class = "prop_vehicle_jeep",
Category = Category,
Author = "VALVe",
Information = "The muscle car from Episode 2",
KeyValues = {
vehiclescript = "scripts/vehicles/jalopy.txt"
}
}, "Jalopy" )
Category = "#spawnmenu.category.chairs"
local function HandleRollercoasterAnimation( vehicle, player )
return player:SelectWeightedSequence( ACT_GMOD_SIT_ROLLERCOASTER )
end
AddVehicle( {
Name = "#spawnmenu.chair.wooden",
Model = "models/nova/chair_wood01.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "VALVe",
Information = "A wooden chair",
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandleRollercoasterAnimation,
}
}, "Chair_Wood" )
AddVehicle( {
Name = "#spawnmenu.chair.plastic",
Model = "models/nova/chair_plastic01.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "VALVe",
Information = "A plastic chair",
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandleRollercoasterAnimation,
}
}, "Chair_Plastic" )
AddVehicle( {
Name = "#spawnmenu.chair.office",
Model = "models/nova/chair_office01.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "VALVe",
Information = "A small office chair",
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandleRollercoasterAnimation,
}
}, "Chair_Office1" )
AddVehicle( {
Name = "#spawnmenu.chair.office_big",
Model = "models/nova/chair_office02.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "VALVe",
Information = "A big office chair",
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandleRollercoasterAnimation,
}
}, "Chair_Office2" )
AddVehicle( {
Name = "#spawnmenu.seat.jeep",
Model = "models/nova/jeep_seat.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "VALVe",
Information = "A seat from VALVe's Jeep",
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandleRollercoasterAnimation,
}
}, "Seat_Jeep" )
AddVehicle( {
Name = "#spawnmenu.seat.airboat",
Model = "models/nova/airboat_seat.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "VALVe",
Information = "A seat from VALVe's Airboat",
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandleRollercoasterAnimation,
}
}, "Seat_Airboat" )
AddVehicle( {
Name = "#spawnmenu.seat.jalopy",
Model = "models/nova/jalopy_seat.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "VALVe",
Information = "A seat from VALVe's Jalopy",
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandleRollercoasterAnimation,
}
}, "Seat_Jalopy" )
-- PhoeniX-Storms Vehicles
local function HandlePHXSeatAnimation( vehicle, player )
return player:SelectWeightedSequence( ACT_HL2MP_SIT )
end
local function HandlePHXVehicleAnimation( vehicle, ply )
return ply:SelectWeightedSequence( ACT_DRIVE_JEEP )
end
local function HandlePHXAirboatAnimation( vehicle, ply )
return ply:SelectWeightedSequence( ACT_DRIVE_AIRBOAT )
end
AddVehicle( {
Name = "#spawnmenu.seat.simple_sit",
Model = "models/props_phx/carseat2.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "PhoeniX-Storms",
Information = "PHX Airboat Seat with Sitting Animation",
Offset = 16,
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandlePHXSeatAnimation,
}
}, "phx_seat" )
AddVehicle( {
Name = "#spawnmenu.seat.simple_jeep",
Model = "models/props_phx/carseat3.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "PhoeniX-Storms",
Information = "PHX Airboat Seat with Jeep animations",
Offset = 16,
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandlePHXVehicleAnimation,
}
}, "phx_seat2" )
AddVehicle( {
Name = "#spawnmenu.seat.simple_airboat",
Model = "models/props_phx/carseat2.mdl",
Class = "prop_vehicle_prisoner_pod",
Category = Category,
Author = "PhoeniX-Storms",
Information = "PHX Airboat Seat with Airboat animations",
Offset = 16,
KeyValues = {
vehiclescript = "scripts/vehicles/prisoner_pod.txt",
limitview = "0"
},
Members = {
HandleAnimation = HandlePHXAirboatAnimation,
}
}, "phx_seat3" )

View File

@@ -0,0 +1,121 @@
if ( !engine.IsPlayingDemo() ) then return end
local VideoSettings = engine.VideoSettings()
if ( !VideoSettings ) then return end
PrintTable( VideoSettings )
local SmoothedAng = nil
local SmoothedFOV = nil
local SmoothedPos = nil
local AutoFocusPoint = nil
hook.Add( "Initialize", "DemoRenderInit", function()
if ( VideoSettings.frameblend < 2 ) then
RunConsoleCommand( "pp_fb", "0" )
else
RunConsoleCommand( "pp_fb", "1" )
RunConsoleCommand( "pp_fb_frames", VideoSettings.frameblend )
RunConsoleCommand( "pp_fb_shutter", VideoSettings.fbshutter )
end
end )
hook.Add( "RenderScene", "RenderForDemo", function( ViewOrigin, ViewAngles, ViewFOV )
if ( !engine.IsPlayingDemo() ) then return false end
render.Clear( 0, 0, 0, 255, true, true )
local FramesPerFrame = 1
if ( frame_blend.IsActive() ) then
FramesPerFrame = frame_blend.RenderableFrames()
frame_blend.AddFrame()
if ( frame_blend.ShouldSkipFrame() ) then
frame_blend.DrawPreview()
return true
end
end
if ( !SmoothedAng ) then SmoothedAng = ViewAngles * 1 end
if ( !SmoothedFOV ) then SmoothedFOV = ViewFOV end
if ( !SmoothedPos ) then SmoothedPos = ViewOrigin * 1 end
if ( !AutoFocusPoint ) then AutoFocusPoint = SmoothedPos * 1 end
if ( VideoSettings.viewsmooth > 0 ) then
SmoothedAng = LerpAngle( ( 1 - VideoSettings.viewsmooth ) / FramesPerFrame, SmoothedAng, ViewAngles )
SmoothedFOV = Lerp( ( 1 - VideoSettings.viewsmooth ) / FramesPerFrame, SmoothedFOV, ViewFOV )
else
SmoothedAng = ViewAngles * 1
SmoothedFOV = ViewFOV
end
if ( VideoSettings.possmooth > 0 ) then
SmoothedPos = LerpVector( ( 1 - VideoSettings.possmooth ) / FramesPerFrame, SmoothedPos, ViewOrigin )
else
SmoothedPos = ViewOrigin * 1
end
local view = {
x = 0,
y = 0,
w = math.Round( VideoSettings.width ),
h = math.Round( VideoSettings.height ),
angles = SmoothedAng,
origin = SmoothedPos,
fov = SmoothedFOV,
drawhud = false,
drawviewmodel = true,
dopostprocess = true,
drawmonitors = true
}
if ( VideoSettings.dofsteps && VideoSettings.dofpasses ) then
local trace = util.TraceHull( {
start = view.origin,
endpos = view.origin + ( view.angles:Forward() * 8000 ),
mins = Vector( -2, -2, -2 ),
maxs = Vector( 2, 2, 2 ),
filter = { GetViewEntity() }
} )
local focuspeed = math.Clamp( ( VideoSettings.doffocusspeed / FramesPerFrame ) * 0.2, 0, 1 )
AutoFocusPoint = LerpVector( focuspeed, AutoFocusPoint, trace.HitPos )
local UsableFocusPoint = view.origin + view.angles:Forward() * AutoFocusPoint:Distance( view.origin )
RenderDoF( view.origin, view.angles, UsableFocusPoint, VideoSettings.dofsize * 0.3, VideoSettings.dofsteps, VideoSettings.dofpasses, false, table.Copy( view ) )
else
render.RenderView( view )
end
-- TODO: IF RENDER HUD
render.RenderHUD( 0, 0, view.w, view.h )
local ShouldRecordThisFrme = frame_blend.IsLastFrame()
if ( frame_blend.IsActive() ) then
frame_blend.BlendFrame()
frame_blend.DrawPreview()
end
if ( ShouldRecordThisFrme ) then
menu.RecordFrame()
end
return true
end )

View File

@@ -0,0 +1,30 @@
concommand.Add( "gm_demo", function( ply, cmd, arg )
if ( engine.IsRecordingDemo() ) then
RunConsoleCommand( "stop" )
return
end
local dynamic_name = game.GetMap() .. " " .. util.DateStamp()
RunConsoleCommand( "record", "demos/" .. dynamic_name .. ".dem" )
RunConsoleCommand( "record_screenshot", dynamic_name )
end, nil, "Start or stop recording a demo.", FCVAR_DONTRECORD )
local matRecording = nil
local drawicon = CreateConVar( "gm_demo_icon", 1, FCVAR_ARCHIVE + FCVAR_DONTRECORD, "If set to 1, display a 'RECORDING' icon during gm_demo." )
hook.Add( "HUDPaint", "DrawRecordingIcon", function()
if ( !engine.IsRecordingDemo() || !drawicon:GetBool() ) then return end
if ( !matRecording ) then
matRecording = Material( "gmod/recording.png" )
end
surface.SetDrawColor( 255, 255, 255, 255 )
surface.SetMaterial( matRecording )
surface.DrawTexturedRect( ScrW() - 512, 0, 512, 256 )
end )

View File

@@ -0,0 +1,159 @@
-- Makes stuff easier to parse visually
local offColor = Color( 220, 220, 220 )
local function FindInTable( tab, find, parents, depth )
depth = depth or 0
parents = parents or ""
if ( !istable( tab ) ) then return end
if ( depth > 3 ) then return end
depth = depth + 1
for k, v in pairs ( tab ) do
if ( isstring( k ) ) then
if ( k and k:lower():find( find:lower() ) ) then
local info = isfunction( v ) and debug.getinfo( v ) or { source = "" }
if ( info.source:len() != 0 ) then info.source = " " .. info.source end
Msg( " ", parents, k, string.rep( " ", math.Clamp( 32 - ( parents:len() + k:len() ), 1, 32 ) ) )
MsgC( offColor, "(", type( v ), " - ", v, info.source, ")\n" )
end
-- Recurse
if ( istable( v ) and
k != "_R" and
k != "_E" and
k != "_G" and
k != "_M" and
k != "_LOADED" and
k != "__index" ) then
local NewParents = parents .. k .. "."
FindInTable( v, find, NewParents, depth )
end
end
end
end
local function FindInHooks( base, name )
for b, t in pairs( hook.GetTable() ) do
local head = true
if ( istable( t ) and b:lower():find( base:lower() ) ) then
for n, f in pairs( t ) do
local nameStr = tostring( n )
if ( !name or nameStr:lower():find( tostring( name ):lower() ) ) then
if ( head ) then Msg( "\n ", b, " hooks:\n" ) head = false end
local info = isfunction( f ) and debug.getinfo( f, "S" ) or { source = "" }
if ( info.source:len() != 0 ) then info.source = " " .. info.source end
Msg( "\t", nameStr, string.rep( " ", math.Clamp( 32 - nameStr:len(), 1, 32 ) ) )
MsgC( offColor, "(", tostring( f ), info.source, ")\n" )
end
end
end
end
end
local function UTIL_IsCommandIssuedByServerAdmin( ply )
if ( game.SinglePlayer() ) then return true end -- Singleplayer
if ( !IsValid( ply ) ) then return SERVER end -- Dedicated server console
return ply:IsListenServerHost() -- Only if we are a listen server host
end
--[[---------------------------------------------------------
Name: Find
-----------------------------------------------------------]]
local function Find( ply, command, arguments )
if ( !UTIL_IsCommandIssuedByServerAdmin( ply ) ) then return end
if ( !arguments[1] ) then
if ( command:StartsWith( "lua_findhooks" ) ) then
MsgN( "Usage: lua_findhooks <event name> [hook identifier]" )
return
end
MsgN( "Usage: lua_find <text>" )
return
end
if ( command:StartsWith( "lua_findhooks" ) ) then
Msg( "Finding '", arguments[1], "' hooks ",
( arguments[2] and "with name '" .. arguments[2] .. "' " or "" ),
( SERVER and "SERVERSIDE" or "CLIENTSIDE" ), ":\n"
)
FindInHooks( arguments[1], arguments[2] )
else
Msg( "Finding '", arguments[1], "' ", ( SERVER and "SERVERSIDE" or "CLIENTSIDE" ), ":\n" )
FindInTable( _G, arguments[1] )
--FindInTable( debug.getregistry(), arguments[1] )
end
Msg( "\n" )
if ( SERVER and IsValid( ply ) and ply:IsPlayer() and ply:IsListenServerHost() ) then
RunConsoleCommand( command .. "_cl", arguments[1], arguments[2] )
end
end
if ( SERVER ) then
concommand.Add( "lua_find", Find, nil, "Find any variable by name on the server.", FCVAR_DONTRECORD )
concommand.Add( "lua_findhooks", Find, nil, "Find hooks by event name and hook identifier on the server.", FCVAR_DONTRECORD )
else
concommand.Add( "lua_find_cl", Find, nil, "Find any variable by name on the client.", FCVAR_DONTRECORD )
concommand.Add( "lua_findhooks_cl", Find, nil, "Find hooks by event name and hook identifier on the client.", FCVAR_DONTRECORD )
end
if ( SERVER ) then
--[[---------------------------------------------------------
What am I looking at?
-----------------------------------------------------------]]
concommand.Add( "trace", function( ply )
if ( !IsValid( ply ) || ( !game.SinglePlayer() && !ply:IsListenServerHost() ) ) then return end
local tr = util.TraceLine( {
start = ply:EyePos(),
endpos = ply:EyePos() + ply:GetAimVector() * 30000,
filter = ply,
//mask = MASK_OPAQUE_AND_NPCS,
} )
PrintTable( tr )
print( "Dist: ", ( tr.HitPos - tr.StartPos ):Length() )
if ( IsValid( tr.Entity ) ) then print( "Model: " .. tr.Entity:GetModel() ) end
-- Print out the clientside class name
ply:SendLua( [[print(Entity(]] .. ply:EntIndex() .. [[):GetEyeTrace().Entity)]] )
end )
end

209
lua/autorun/game_hl2.lua Normal file
View File

@@ -0,0 +1,209 @@
local Category = ""
-- This is basically dupliacator.GenericDuplicatorFunction, but calls the relevant hooks
-- Move this all to commands.lua?
local function ADD_ITEM_DUPEFUNC( ply, data )
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnSENT", ply, data.Class ) ) then return NULL end
local ent = ents.Create( data.Class )
if ( !IsValid( ent ) ) then return NULL end -- Must've hit edict limit
-- Remove certain fields we do not want dupes to manipulate
data.Model = nil
-- Restore the keyvalues
local entTable = list.GetEntry( "SpawnableEntities", data.EntityName )
if ( entTable && entTable.ClassName == data.Class && entTable.KeyValues ) then
for k, v in pairs( entTable.KeyValues ) do
ent:SetKeyValue( k, v )
end
end
duplicator.DoGeneric( ent, data )
ent:Spawn()
--duplicator.DoGenericPhysics( ent, ply, data )
ent:Activate()
ent.EntityName = data.EntityName
-- For hacked combine mines, they reset their skin
if ( data.Skin ) then ent:SetSkin( data.Skin ) end
if ( IsValid( ply ) ) then
ent:SetCreator( ply )
gamemode.Call( "PlayerSpawnedSENT", ply, ent )
end
return ent
end
local function ADD_WEAPON_DUPEFUNC( ply, data )
if ( IsValid( ply ) && !gamemode.Call( "PlayerSpawnSWEP", ply, data.Class, list.GetEntry( "Weapon", data.Class ) ) ) then return NULL end
local ent = ents.Create( data.Class )
if ( !IsValid( ent ) ) then return NULL end -- Must've hit edict limit
-- Remove certain fields we do not want dupes to manipulate
data.Model = nil
duplicator.DoGeneric( ent, data )
ent:Spawn()
--duplicator.DoGenericPhysics( ent, ply, data )
ent:Activate()
ent.EntityName = data.EntityName
if ( IsValid( ply ) ) then
ent:SetCreator( ply )
gamemode.Call( "PlayerSpawnedSWEP", ply, ent )
end
return ent
end
local function ADD_ITEM( class, offset, extras, classOverride )
local base = { PrintName = "#" .. ( classOverride or class ), ClassName = class, Category = Category, NormalOffset = offset or 32, DropToFloor = true, Author = "VALVe" }
list.Set( "SpawnableEntities", classOverride or class, table.Merge( base, extras or {} ) )
duplicator.RegisterEntityClass( class, ADD_ITEM_DUPEFUNC, "Data" )
end
local function ADD_WEAPON( class )
list.Set( "Weapon", class, { ClassName = class, PrintName = "#" .. ( class ), Category = Category, Author = "VALVe", Spawnable = true } )
duplicator.RegisterEntityClass( class, ADD_WEAPON_DUPEFUNC, "Data" )
end
local function ADD_NPC_WEAPON( class )
list.Add( "NPCUsableWeapons", { class = class, title = "#" .. class, category = Category } )
end
Category = "Half-Life 2"
-- Ammo
ADD_ITEM( "item_ammo_ar2", -8 )
ADD_ITEM( "item_ammo_ar2_large", -8 )
ADD_ITEM( "item_ammo_pistol", -4 )
ADD_ITEM( "item_ammo_pistol_large", -4 )
ADD_ITEM( "item_ammo_357", -4 )
ADD_ITEM( "item_ammo_357_large", -4 )
ADD_ITEM( "item_ammo_smg1", -2 )
ADD_ITEM( "item_ammo_smg1_large", -2 )
ADD_ITEM( "item_ammo_smg1_grenade", -10 )
ADD_ITEM( "item_ammo_crossbow", -10 )
ADD_ITEM( "item_box_buckshot", -10 )
ADD_ITEM( "item_ammo_ar2_altfire", -2 )
ADD_ITEM( "item_rpg_round", -10 )
-- Dynamic materials; gives player what he needs most (health, shotgun ammo, suit energy, etc)
-- ADD_ITEM( "item_dynamic_resupply" )
-- Items
ADD_ITEM( "item_battery", -4 )
ADD_ITEM( "item_healthkit", -8 )
ADD_ITEM( "item_healthvial", -4 )
ADD_ITEM( "item_suitcharger" )
ADD_ITEM( "item_healthcharger" )
ADD_ITEM( "item_suit", 0 )
ADD_ITEM( "prop_thumper" )
ADD_ITEM( "combine_mine", -8 )
ADD_ITEM( "combine_mine", -8, { KeyValues = { Modification = 1 } }, "combine_mine_resistance" )
ADD_ITEM( "npc_grenade_frag", -8 )
ADD_ITEM( "grenade_helicopter", 4 )
ADD_ITEM( "weapon_striderbuster" )
-- Weapons
ADD_WEAPON( "weapon_physcannon" )
ADD_WEAPON( "weapon_stunstick" )
ADD_WEAPON( "weapon_frag" )
ADD_WEAPON( "weapon_crossbow" )
ADD_WEAPON( "weapon_bugbait" )
ADD_WEAPON( "weapon_rpg" )
ADD_WEAPON( "weapon_crowbar" )
ADD_WEAPON( "weapon_shotgun" )
ADD_WEAPON( "weapon_pistol" )
ADD_WEAPON( "weapon_slam" )
ADD_WEAPON( "weapon_smg1" )
ADD_WEAPON( "weapon_ar2" )
ADD_WEAPON( "weapon_357" )
--ADD_WEAPON( "weapon_alyxgun" )
--ADD_WEAPON( "weapon_annabelle" )
-- NPC Weapons
ADD_NPC_WEAPON( "weapon_pistol" )
ADD_NPC_WEAPON( "weapon_357" )
ADD_NPC_WEAPON( "weapon_smg1" )
ADD_NPC_WEAPON( "weapon_shotgun" )
ADD_NPC_WEAPON( "weapon_ar2" )
ADD_NPC_WEAPON( "weapon_rpg" )
ADD_NPC_WEAPON( "weapon_alyxgun" )
ADD_NPC_WEAPON( "weapon_annabelle" )
ADD_NPC_WEAPON( "weapon_crossbow" )
ADD_NPC_WEAPON( "weapon_stunstick" )
ADD_NPC_WEAPON( "weapon_crowbar" )
if ( IsMounted( "hl1" ) or IsMounted( "hl1mp" ) ) then
Category = "Half-Life: Source"
ADD_WEAPON( "weapon_snark" )
ADD_WEAPON( "weapon_handgrenade" )
ADD_WEAPON( "weapon_mp5_hl1" )
ADD_WEAPON( "weapon_hornetgun" )
ADD_WEAPON( "weapon_satchel" )
ADD_WEAPON( "weapon_tripmine" )
ADD_WEAPON( "weapon_crossbow_hl1" )
ADD_WEAPON( "weapon_357_hl1" )
ADD_WEAPON( "weapon_rpg_hl1" )
ADD_WEAPON( "weapon_shotgun_hl1" )
ADD_WEAPON( "weapon_glock_hl1" )
ADD_WEAPON( "weapon_gauss" )
ADD_WEAPON( "weapon_egon" )
ADD_WEAPON( "weapon_crowbar_hl1" )
ADD_ITEM( "ammo_crossbow", 0 )
ADD_ITEM( "ammo_gaussclip", 0 )
ADD_ITEM( "ammo_glockclip", 0 )
ADD_ITEM( "ammo_mp5clip", 0 )
ADD_ITEM( "ammo_9mmbox", 0, { Information = "Gives ammo for the MP5 and Glock." } )
ADD_ITEM( "ammo_mp5grenades", 0 )
ADD_ITEM( "ammo_357", 0 )
ADD_ITEM( "ammo_rpgclip", 0 )
ADD_ITEM( "ammo_buckshot", 0 )
-- Can't be physgunned
--ADD_ITEM( "xen_plantlight", -16 )
ADD_NPC_WEAPON( "weapon_357_hl1" )
ADD_NPC_WEAPON( "weapon_mp5_hl1" )
ADD_NPC_WEAPON( "weapon_glock_hl1" )
ADD_NPC_WEAPON( "weapon_shotgun_hl1" )
end
if ( IsMounted( "portal" ) ) then
Category = "Portal"
ADD_ITEM( "prop_glados_core", 32, { KeyValues = { CoreType = 0, DelayBetweenLines = 0.4 }, PrintName = "#prop_glados_core_curiosity" } )
ADD_ITEM( "prop_glados_core", 32, { KeyValues = { CoreType = 1, DelayBetweenLines = 0.1 } }, "prop_glados_core_anger" )
ADD_ITEM( "prop_glados_core", 32, { KeyValues = { CoreType = 2, DelayBetweenLines = 0.1 } }, "prop_glados_core_crazy" )
ADD_ITEM( "prop_glados_core", 32, { KeyValues = { CoreType = 3 } }, "prop_glados_core_morality" )
end
Category = "#spawnmenu.category.other"
ADD_WEAPON( "weapon_physgun" )

127
lua/autorun/menubar.lua Normal file
View File

@@ -0,0 +1,127 @@
AddCSLuaFile()
if ( SERVER ) then return end
local checkList = {}
checkList[ "cheat" ] = { tooltip = "#menubar.cheatstip", func = function() return GetConVarNumber( "sv_cheats" ) != 0 end }
checkList[ "cheatSP" ] = { tooltip = "#menubar.cheatstip", func = function() return GetConVarNumber( "sv_cheats" ) != 0 or game.SinglePlayer() end }
checkList[ "host" ] = { tooltip = "#menubar.host_only", func = function() return IsValid( LocalPlayer() ) and LocalPlayer():IsListenServerHost() end }
local function AddCVar( s, checkStr, ... )
local cvar = s:AddCVar( ... )
cvar.OldThink = cvar.Think
cvar.Think = function( se )
se:OldThink()
local checks = string.Split( checkStr, " " )
for k, v in ipairs( checks ) do
if ( checkList[ v ] and !checkList[ v ].func() ) then
se:SetEnabled( false )
se:SetTooltip( checkList[ v ].tooltip )
return
end
end
se:SetEnabled( true )
se:SetTooltip()
end
end
local function AddHostCVar( s, ... ) AddCVar( s, "host", ... ) end
local function AddCheatCVar( s, ... ) AddCVar( s, "cheat", ... ) end
local function AddCheatOrSPCVar( s, ... ) AddCVar( s, "cheatSP host", ... ) end
-- Display options
hook.Add( "PopulateMenuBar", "DisplayOptions_MenuBar", function( menubar )
local m = menubar:AddOrGetMenu( "#menubar.drawing" )
m:AddCVar( "#menubar.drawing.physgun_beam", "physgun_drawbeams", "1", "0" )
m:AddCVar( "#menubar.drawing.physgun_halo", "physgun_halo", "1", "0" )
m:AddCVar( "#menubar.drawing.freeze", "effects_freeze", "1", "0" )
m:AddCVar( "#menubar.drawing.unfreeze", "effects_unfreeze", "1", "0" )
m:AddSpacer()
m:AddCVar( "#menubar.drawing.hud", "cl_drawhud", "1", "0" )
m:AddCVar( "#menubar.drawing.toolhelp", "gmod_drawhelp", "1", "0" )
m:AddCVar( "#menubar.drawing.toolui", "gmod_drawtooleffects", "1", "0" )
m:AddCVar( "#menubar.drawing.world_tooltips", "cl_drawworldtooltips", "1", "0" )
m:AddCVar( "#menubar.drawing.spawn_effect", "cl_drawspawneffect", "1", "0" )
m:AddCVar( "#menubar.drawing.effect_rings", "cl_draweffectrings", "1", "0" )
m:AddCVar( "#menubar.drawing.cameras", "cl_drawcameras", "1", "0" )
m:AddCVar( "#menubar.drawing.thrusters", "cl_drawthrusterseffects", "1", "0" )
m:AddSpacer()
m:AddCVar( "#menubar.drawing.shadows", "r_shadows", "1", "0" )
m:AddCVar( "#menubar.drawing.detailprops", "r_drawdetailprops", "1", "0" )
m:AddSpacer()
m:AddCVar( "#menubar.drawing.showfps", "cl_showfps", "1", "0" )
AddCheatOrSPCVar( m, "#menubar.drawing.minecraftify", "mat_showlowresimage", "1", "0", function() timer.Simple( 0.1, function() RunConsoleCommand( "mat_reloadallmaterials" ) end ) end )
AddCheatCVar( m, "#menubar.drawing.wireframe", "mat_wireframe", "1", "0" )
m:AddSpacer()
m:AddCVar( "#menubar.drawing.hints", "cl_showhints", "1", "0" )
end )
-- AI Options
hook.Add( "PopulateMenuBar", "NPCOptions_MenuBar", function( menubar )
local m = menubar:AddOrGetMenu( "#menubar.npcs" )
AddHostCVar( m, "#menubar.npcs.disableai", "ai_disabled", "1", "0" )
AddHostCVar( m, "#menubar.npcs.ignoreplayers", "ai_ignoreplayers", "1", "0" )
AddHostCVar( m, "#menubar.npcs.keepcorpses", "ai_serverragdolls", "1", "0" )
AddHostCVar( m, "#menubar.npcs.autoplayersquad", "npc_citizen_auto_player_squad", "1", "0" )
local wpns = m:AddSubMenu( "#menubar.npcs.weapon" )
wpns:SetDeleteSelf( false )
wpns:AddCVar( "#menubar.npcs.defaultweapon", "gmod_npcweapon", "" )
wpns:AddCVar( "#menubar.npcs.noweapon", "gmod_npcweapon", "none" )
wpns:AddSpacer()
local groupedWeps = {}
for _, v in pairs( list.Get( "NPCUsableWeapons" ) ) do
local cat = ( v.category or "" ):lower()
groupedWeps[ cat ] = groupedWeps[ cat ] or {}
groupedWeps[ cat ][ v.class ] = language.GetPhrase( v.title )
end
for group, items in SortedPairs( groupedWeps ) do
wpns:AddSpacer()
for class, title in SortedPairsByValue( items ) do
wpns:AddCVar( title, "gmod_npcweapon", class )
end
end
end )
-- Server options
hook.Add( "PopulateMenuBar", "MenuBar_ServerOptions", function( menubar )
local m = menubar:AddOrGetMenu( "#menubar.server" )
AddHostCVar( m, "#utilities.allowcslua", "sv_allowcslua", "1", "0" )
AddHostCVar( m, "#utilities.falldamage", "mp_falldamage", "1", "0" )
AddHostCVar( m, "#utilities.gmod_suit", "gmod_suit", "1", "0" )
AddCheatOrSPCVar( m, "#physcannon_mega_enabled", "physcannon_mega_enabled", "1", "0" )
m:AddSpacer()
AddHostCVar( m, "#enable_weapons", "sbox_weapons", "1", "0" )
AddHostCVar( m, "#allow_god_mode", "sbox_godmode", "1", "0" )
m:AddSpacer()
AddHostCVar( m, "#players_damage_players", "sbox_playershurtplayers", "1", "0" )
AddHostCVar( m, "#allow_noclip", "sbox_noclip", "1", "0" )
AddHostCVar( m, "#bone_manipulate_npcs", "sbox_bonemanip_npc", "1", "0" )
AddHostCVar( m, "#bone_manipulate_players", "sbox_bonemanip_player", "1", "0" )
AddHostCVar( m, "#bone_manipulate_others", "sbox_bonemanip_misc", "1", "0" )
end )

View File

@@ -0,0 +1,16 @@
include( "properties/bone_manipulate.lua" )
include( "properties/remove.lua" )
include( "properties/statue.lua" )
include( "properties/keep_upright.lua" )
include( "properties/persist.lua" )
include( "properties/drive.lua" )
include( "properties/ignite.lua" )
include( "properties/collisions.lua" )
include( "properties/gravity.lua" )
include( "properties/npc_scale.lua" )
include( "properties/editentity.lua" )
include( "properties/kinect_controller.lua" )
include( "properties/bodygroups.lua" )
include( "properties/skin.lua" )

View File

@@ -0,0 +1,128 @@
AddCSLuaFile()
properties.Add( "bodygroups", {
MenuLabel = "#bodygroups",
Order = 600,
MenuIcon = "icon16/link_edit.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:IsPlayer() ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "bodygroups", ent ) ) then return false end
if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end -- If our ent has an attached entity, we want to use and modify its bodygroups instead
--
-- Get a list of bodygroups
--
local options = ent:GetBodyGroups()
if ( !options ) then return false end
--
-- If a bodygroup has more than one state - then we can configure it
--
for k, v in pairs( options ) do
if ( v.num > 1 ) then return true end
end
return false
end,
MenuOpen = function( self, option, ent, tr )
local target = IsValid( ent.AttachedEntity ) and ent.AttachedEntity or ent
--
-- Get a list of bodygroups
--
local options = target:GetBodyGroups()
--
-- Add a submenu to our automatically created menu option
--
local submenu = option:AddSubMenu()
--
-- For each body group - add a menu or checkbox
--
for k, v in pairs( options ) do
if ( v.num <= 1 ) then continue end
--
-- If there's only 2 options, add it as a checkbox instead of a submenu
--
if ( v.num == 2 ) then
local current = target:GetBodygroup( v.id )
local opposite = 1
if ( current == opposite ) then opposite = 0 end
local opt = submenu:AddOption( string.NiceName( v.name ) )
opt:SetChecked( current == 1 )
opt:SetIsCheckable( true )
opt.OnChecked = function( s, checked ) self:SetBodyGroup( ent, v.id, checked and 1 or 0 ) end
--
-- More than 2 options we add our own submenu
--
else
local groups = submenu:AddSubMenu( string.NiceName( v.name ) )
for i = 1, v.num do
local modelname = "Model #" .. i
if ( v.submodels and v.submodels[ i - 1 ] != "" ) then modelname = v.submodels[ i - 1 ] end
modelname = string.Trim( modelname, "." )
modelname = string.Trim( modelname, "/" )
modelname = string.Trim( modelname, "\\" )
modelname = string.StripExtension( modelname )
modelname = string.GetFileFromFilename( modelname )
local opt = groups:AddOption( string.NiceName( modelname ) )
opt:SetRadio( true )
opt:SetChecked( target:GetBodygroup( v.id ) == i - 1 )
opt:SetIsCheckable( true )
opt.OnChecked = function( s, checked ) if ( checked ) then self:SetBodyGroup( ent, v.id, i - 1 ) end end
end
end
end
end,
Action = function( self, ent )
-- Nothing - we use SetBodyGroup below
end,
SetBodyGroup = function( self, ent, body, id )
self:MsgStart()
net.WriteEntity( ent )
net.WriteUInt( body, 8 )
net.WriteUInt( id, 8 )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
local body = net.ReadUInt( 8 )
local id = net.ReadUInt( 8 )
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent = IsValid( ent.AttachedEntity ) and ent.AttachedEntity or ent
ent:SetBodygroup( body, id )
end
} )

View File

@@ -0,0 +1,226 @@
AddCSLuaFile()
properties.Add( "bone_manipulate", {
MenuLabel = "#edit_bones",
Order = 500,
MenuIcon = "icon16/vector.png",
Filter = function( self, ent, ply )
if ( !gamemode.Call( "CanProperty", ply, "bonemanipulate", ent ) ) then return false end
if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end -- If our ent has an attached entity, we want to use and modify its bones instead
local bonecount = ent:GetBoneCount()
if ( bonecount <= 1 ) then return false end
return ents.FindByClassAndParent( "widget_bones", ent ) == nil
end,
Action = function( self, ent )
if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent.widget = ents.Create( "widget_bones" )
ent.widget:Setup( ent )
ent.widget:Spawn()
ent.widget.LastBonePress = 0
ent.widget.BonePressCount = 0
-- What happens when we click on a bone?
ent.widget.OnBoneClick = function( w, boneid, pl )
-- If we have an old axis, remove it
if ( IsValid( w.axis ) ) then w.axis:Remove() end
-- We clicked on the same bone
if ( w.LastBonePress == boneid ) then
w.BonePressCount = w.BonePressCount + 1
if ( w.BonePressCount >= 3 ) then w.BonePressCount = 0 end
-- We clicked on a new bone!
else
w.BonePressCount = 0
w.LastBonePress = boneid
end
local EntityCycle = { "widget_bonemanip_move", "widget_bonemanip_rotate", "widget_bonemanip_scale" }
w.axis = ents.Create( EntityCycle[ w.BonePressCount + 1 ] )
w.axis:Setup( ent, boneid, w.BonePressCount == 1 )
w.axis:Spawn()
w.axis:SetPriority( 0.5 )
w:DeleteOnRemove( w.axis )
end
end
} )
properties.Add( "bone_manipulate_end", {
MenuLabel = "#stop_editing_bones",
Order = 500,
MenuIcon = "icon16/vector_delete.png",
Filter = function( self, ent )
if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end -- If our ent has an attached entity, we want to use and modify its bones instead
return ents.FindByClassAndParent( "widget_bones", ent ) != nil
end,
Action = function( self, ent )
if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !IsValid( ent.widget ) ) then return end
ent.widget:Remove()
end
} )
local widget_bonemanip_move = {
Base = "widget_axis",
OnArrowDragged = function( self, num, dist, pl, mv )
-- Prediction doesn't work properly yet.. because of the confusion with the bone moving, and the parenting, Agh.
if ( CLIENT ) then return end
local ent = self:GetParent()
if ( !IsValid( ent ) ) then return end
local bone = self:GetParentAttachment()
if ( bone <= 0 ) then return end
local v = Vector( 0, 0, 0 )
if ( num == 1 ) then v.x = dist end
if ( num == 2 ) then v.y = dist end
if ( num == 3 ) then v.z = dist end
ent:ManipulateBonePosition( bone, ent:GetManipulateBonePosition( bone ) + v )
end,
--
-- Although we use the position from our bone, we want to use the angles from the
-- parent bone - because that's the direction our bone goes
--
CalcAbsolutePosition = function( self, v, a )
local ent = self:GetParent()
if ( !IsValid( ent ) ) then return end
local bone = ent:GetBoneParent( self:GetParentAttachment() )
if ( bone <= 0 ) then return end
local _, ang = ent:GetBonePosition( bone )
local pos, _ = ent:GetBonePosition( self:GetParentAttachment() )
return pos, ang
end
}
scripted_ents.Register( widget_bonemanip_move, "widget_bonemanip_move" )
local widget_bonemanip_rotate = {
Base = "widget_axis",
OnArrowDragged = function( self, num, dist, pl, mv )
-- Prediction doesn't work properly yet.. because of the confusion with the bone moving, and the parenting, Agh.
if ( CLIENT ) then return end
local ent = self:GetParent()
if ( !IsValid( ent ) ) then return end
local bone = self:GetParentAttachment()
if ( bone <= 0 ) then return end
local v = Angle( 0, 0, 0 )
if ( num == 2 ) then v.x = dist end
if ( num == 3 ) then v.y = dist end
if ( num == 1 ) then v.z = dist end
ent:ManipulateBoneAngles( bone, ent:GetManipulateBoneAngles( bone ) + v )
end
}
scripted_ents.Register( widget_bonemanip_rotate, "widget_bonemanip_rotate" )
local widget_bonemanip_scale = {
Base = "widget_axis",
IsScaleArrow = true,
OnArrowDragged = function( self, num, dist, pl, mv )
-- Prediction doesn't work properly yet.. because of the confusion with the bone moving, and the parenting, Agh.
if ( CLIENT ) then return end
local ent = self:GetParent()
if ( !IsValid( ent ) ) then return end
local bone = self:GetParentAttachment()
if ( bone <= 0 ) then return end
local v = Vector( 0, 0, 0 )
if ( num == 1 ) then v.x = dist end
if ( num == 2 ) then v.y = dist end
if ( num == 3 ) then v.z = dist end
ent:ManipulateBoneScale( bone, ent:GetManipulateBoneScale( bone ) + v * 0.1 )
ent:ManipulateBoneScale( ent:GetBoneParent( bone ), ent:GetManipulateBoneScale( ent:GetBoneParent( bone ) ) + v )
end,
--
-- Although we use the position from our bone, we want to use the angles from the
-- parent bone - because that's the direction our bone goes
--
CalcAbsolutePosition = function( self, v, a )
local ent = self:GetParent()
if ( !IsValid( ent ) ) then return end
local bone = self:GetParentAttachment()
if ( bone <= 0 ) then return end
local pbone = ent:GetBoneParent( bone )
if ( pbone <= 0 ) then return end
local pos, ang = ent:GetBonePosition( bone )
local pos2, _ = ent:GetBonePosition( pbone )
return pos + ( pos2 - pos ) * 0.5, ang
end
}
scripted_ents.Register( widget_bonemanip_scale, "widget_bonemanip_scale" )

View File

@@ -0,0 +1,73 @@
AddCSLuaFile()
properties.Add( "collision_off", {
MenuLabel = "#collision_off",
Order = 1500,
MenuIcon = "icon16/collision_off.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:IsPlayer() ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "collision", ent ) ) then return false end
if ( ent:GetCollisionGroup() == COLLISION_GROUP_WORLD ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent:SetCollisionGroup( COLLISION_GROUP_WORLD )
end
} )
properties.Add( "collision_on", {
MenuLabel = "#collision_on",
Order = 1500,
MenuIcon = "icon16/collision_on.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:IsPlayer() ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "collision", ent ) ) then return false end
return ent:GetCollisionGroup() == COLLISION_GROUP_WORLD
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent:SetCollisionGroup( COLLISION_GROUP_NONE )
end
} )

View File

@@ -0,0 +1,52 @@
AddCSLuaFile()
properties.Add( "drive", {
MenuLabel = "#drive",
Order = 1100,
MenuIcon = "icon16/joystick.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) || !IsValid( ply ) ) then return false end
if ( ent:IsPlayer() || IsValid( ply:GetVehicle() ) ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "drive", ent ) ) then return false end
if ( !gamemode.Call( "CanDrive", ply, ent ) ) then return false end
-- We cannot drive these, maybe this should have a custom GetEntityDriveMode?
if ( ent:GetClass() == "prop_vehicle_jeep" || ent:GetClass() == "prop_vehicle_jeep_old" ) then return false end
-- Make sure nobody else is driving this or we can get into really invalid states
for id, pl in player.Iterator() do
if ( pl:GetDrivingEntity() == ent ) then return false end
end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
local drivemode = "drive_sandbox"
if ( ent.GetEntityDriveMode ) then
drivemode = ent:GetEntityDriveMode( ply )
end
drive.PlayerStartDriving( ply, ent, drivemode )
end
} )

View File

@@ -0,0 +1,48 @@
AddCSLuaFile()
properties.Add( "editentity", {
MenuLabel = "#entedit",
Order = 90001,
PrependSpacer = true,
MenuIcon = "icon16/pencil.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( !ent.Editable ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "editentity", ent ) ) then return false end
return true
end,
Action = function( self, ent )
local printName = ent.PrintName
if ( !isstring( printName ) ) then
printName = ent:GetClass()
end
printName = language.GetPhrase( printName )
printName = string.format( "%s [%d]", printName, ent:EntIndex() )
local window = g_ContextMenu:Add( "DFrame" )
window:SetSize( 320, 400 )
window:SetTitle( printName )
window:Center()
window:SetSizable( true )
local control = window:Add( "DEntityProperties" )
control:SetEntity( ent )
control:Dock( FILL )
control.OnEntityLost = function()
window:Remove()
end
end
} )

View File

@@ -0,0 +1,82 @@
AddCSLuaFile()
-- The following is for the server's eyes only
local GravityDuplicator
if ( SERVER ) then
function GravityDuplicator( ply, ent, data )
if ( !data || !data.enabled ) then
duplicator.ClearEntityModifier( ent, "gravity_property" )
return
end
-- Simply restore the value whenever we are duplicated
-- We don't need to reapply EnableGravity because duplicator already does it for us
ent:SetNWBool( "gravity_disabled", data.enabled )
duplicator.StoreEntityModifier( ent, "gravity_property", data )
end
duplicator.RegisterEntityModifier( "gravity_property", GravityDuplicator )
end
properties.Add( "gravity", {
MenuLabel = "#gravity",
Type = "toggle",
Order = 1001,
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "gravity", ent ) ) then return false end
if ( ent:GetClass() == "prop_physics" ) then return true end
if ( ent:GetClass() == "prop_ragdoll" ) then return true end
return false
end,
Checked = function( self, ent, ply )
return ent:GetNWBool( "gravity_disabled" ) == false
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
local bones = ent:GetPhysicsObjectCount()
local b = ent:GetNWBool( "gravity_disabled" )
for i = 0, bones - 1 do
local phys = ent:GetPhysicsObjectNum( i )
if ( IsValid( phys ) ) then
phys:EnableGravity( b )
phys:Wake()
end
end
ent:SetNWBool( "gravity_disabled", b == false )
GravityDuplicator( ply, ent, { enabled = ent:GetNWBool( "gravity_disabled" ) } )
end
} )

View File

@@ -0,0 +1,88 @@
AddCSLuaFile()
local function CanEntityBeSetOnFire( ent )
local class = ent:GetClass()
-- func_pushable, func_breakable & func_physbox cannot be ignited
if ( class == "item_item_crate" ) then return true end
if ( class == "simple_physics_prop" ) then return true end
if ( class:match( "prop_physics*" ) ) then return true end
if ( class:match( "prop_ragdoll*" ) ) then return true end
if ( ent:IsNPC() ) then return true end
return false
end
properties.Add( "ignite", {
MenuLabel = "#ignite",
Order = 999,
MenuIcon = "icon16/fire.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:IsPlayer() ) then return false end
if ( !CanEntityBeSetOnFire( ent ) ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "ignite", ent ) ) then return false end
return !ent:IsOnFire()
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent:Ignite( 360 )
end
} )
properties.Add( "extinguish", {
MenuLabel = "#extinguish",
Order = 999,
MenuIcon = "icon16/water.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:IsPlayer() ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "extinguish", ent ) ) then return false end
return ent:IsOnFire()
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent:Extinguish()
end
} )

View File

@@ -0,0 +1,97 @@
AddCSLuaFile()
properties.Add( "keepupright", {
MenuLabel = "#keepupright",
Order = 900,
MenuIcon = "icon16/arrow_up.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:GetClass() != "prop_physics" ) then return false end
if ( ent:GetNWBool( "IsUpright" ) ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "keepupright", ent ) ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !IsValid( ply ) ) then return end
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( ent:GetClass() != "prop_physics" ) then return end
if ( ent:GetNWBool( "IsUpright" ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
local Phys = ent:GetPhysicsObjectNum( 0 )
if ( !IsValid( Phys ) ) then return end
local constraint = constraint.Keepupright( ent, Phys:GetAngles(), 0, 999999 )
-- I feel like this is not stable enough
-- This cannot be implemented without a custom constraint.Keepupright function or modification for proper duplicator support.
--print( constraint:GetSaveTable().m_worldGoalAxis )
--constraint:SetSaveValue( "m_localTestAxis", constraint:GetSaveTable().m_worldGoalAxis ) --ent:GetAngles():Up() )
--constraint:SetSaveValue( "m_worldGoalAxis", Vector( 0, 0, 1 ) )
--constraint:SetSaveValue( "m_bDampAllRotation", true )
if ( constraint ) then
ply:AddCleanup( "constraints", constraint )
ent:SetNWBool( "IsUpright", true )
end
end
} )
properties.Add( "keepupright_stop", {
MenuLabel = "#keepupright_stop",
Order = 900,
MenuIcon = "icon16/arrow_rotate_clockwise.png",
Filter = function( self, ent )
if ( !IsValid( ent ) ) then return false end
if ( ent:GetClass() != "prop_physics" ) then return false end
if ( !ent:GetNWBool( "IsUpright" ) ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !IsValid( ply ) ) then return end
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( ent:GetClass() != "prop_physics" ) then return end
if ( !ent:GetNWBool( "IsUpright" ) ) then return end
constraint.RemoveConstraints( ent, "Keepupright" )
ent:SetNWBool( "IsUpright", false )
end
} )

View File

@@ -0,0 +1,81 @@
AddCSLuaFile()
if ( SERVER ) then
CreateConVar( "sensor_debugragdoll", "0", FCVAR_NOTIFY )
CreateConVar( "sensor_stretchragdoll", "0", FCVAR_NOTIFY )
end
local playerTimeouts = {}
properties.Add( "motioncontrol_ragdoll", {
MenuLabel = "#control_with_motion_sensor",
Order = 2500,
MenuIcon = "icon16/controller.png",
Filter = function( self, ent, ply )
if ( CLIENT && !motionsensor ) then return false end
if ( CLIENT && !motionsensor.IsAvailable() ) then return false end
if ( !ent:IsRagdoll() ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "motioncontrol_ragdoll", ent ) ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
--
-- Start up the kinect controller. This will freeze the game for a second.
--
if ( !motionsensor.IsActive() ) then
motionsensor.Start()
end
end,
Receive = function( self, length, player )
local ent = net.ReadEntity()
if ( !self:Filter( ent, player ) ) then return end
-- Do not spam please!
local timeout = playerTimeouts[ player ]
if ( timeout && timeout.time > CurTime() ) then
if ( !timeout.sentMessage ) then
ServerLog( "Player " .. tostring( player ) .. " tried to use 'motioncontrol_ragdoll' property too rapidly!\n" )
player:PrintMessage( HUD_PRINTTALK, "Please wait at least 0.2 seconds before trying to control another ragdoll." )
timeout.sentMessage = true
end
return
end
-- Only 1 controller per ragdoll please!
if ( IsValid( ent.MotionSensorController ) ) then
ent.MotionSensorController:Remove()
end
local ragdoll_motion = ents.Create( "ragdoll_motion" )
ragdoll_motion:SetPos( player:EyePos() + player:EyeAngles():Forward() * 10 )
ragdoll_motion:SetAngles( Angle( 0, player:EyeAngles().yaw, 0 ) )
ragdoll_motion:SetRagdoll( ent )
ragdoll_motion:SetController( player )
ragdoll_motion:Spawn()
undo.Create( "ragdoll_motion" )
undo.AddEntity( ragdoll_motion )
undo.SetPlayer( player )
undo.Finish()
playerTimeouts[ player ] = { time = CurTime() + 0.2, sentMessage = false }
end
} )

View File

@@ -0,0 +1,72 @@
AddCSLuaFile()
properties.Add( "npc_bigger", {
MenuLabel = "#biggify",
Order = 1799,
MenuIcon = "icon16/magnifier_zoom_in.png",
Filter = function( self, ent, ply )
if ( !gamemode.Call( "CanProperty", ply, "npc_bigger", ent ) ) then return false end
if ( !IsValid( ent ) ) then return false end
if ( !ent:IsNPC() ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent:SetModelScale( ent:GetModelScale() * 1.25, 1 )
end
} )
properties.Add( "npc_smaller", {
MenuLabel = "#smallify",
Order = 1800,
MenuIcon = "icon16/magifier_zoom_out.png",
Filter = function( self, ent, ply )
if ( !gamemode.Call( "CanProperty", ply, "npc_smaller", ent ) ) then return false end
if ( !IsValid( ent ) ) then return false end
if ( !ent:IsNPC() ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent:SetModelScale( ent:GetModelScale() * 0.8, 1 )
end
} )

View File

@@ -0,0 +1,79 @@
AddCSLuaFile()
properties.Add( "persist", {
MenuLabel = "#makepersistent",
Order = 400,
MenuIcon = "icon16/link.png",
Filter = function( self, ent, ply )
if ( ent:IsPlayer() ) then return false end
if ( GetConVarString( "sbox_persist" ):Trim() == "" ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "persist", ent ) ) then return false end
return !ent:GetPersistent()
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
-- TODO: Start some kind of animation, take 5 seconds to make something persistent
ent:SetPersistent( true )
--ent:EnableMotion( false )
end
} )
properties.Add( "persist_end", {
MenuLabel = "#stoppersisting",
Order = 400,
MenuIcon = "icon16/link_break.png",
Filter = function( self, ent, ply )
if ( ent:IsPlayer() ) then return false end
if ( GetConVarString( "sbox_persist" ):Trim() == "" ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "persist", ent ) ) then return false end
return ent:GetPersistent()
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
-- TODO: Start some kind of animation, take 5 seconds to make something persistent
ent:SetPersistent( false )
end
} )

View File

@@ -0,0 +1,57 @@
AddCSLuaFile()
properties.Add( "remove", {
MenuLabel = "#remove",
Order = 1000,
MenuIcon = "icon16/delete.png",
Filter = function( self, ent, ply )
if ( !gamemode.Call( "CanProperty", ply, "remover", ent ) ) then return false end
if ( !IsValid( ent ) ) then return false end
if ( ent:IsPlayer() ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
if ( !IsValid( ply ) ) then return end
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
-- Don't allow removal of players or objects that cannot be physically targeted by properties
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
-- Remove all constraints (this stops ropes from hanging around)
constraint.RemoveAll( ent )
-- Remove it properly in 1 second
timer.Simple( 1, function() if ( IsValid( ent ) ) then ent:Remove() end end )
-- Make it non solid
ent:SetNotSolid( true )
ent:SetMoveType( MOVETYPE_NONE )
ent:SetNoDraw( true )
-- Send Effect
local ed = EffectData()
ed:SetEntity( ent )
util.Effect( "entity_remove", ed, true, true )
ply:SendLua( "achievements.Remover()" )
end
} )

View File

@@ -0,0 +1,75 @@
AddCSLuaFile()
properties.Add( "skin", {
MenuLabel = "#skin",
Order = 601,
MenuIcon = "icon16/picture_edit.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:IsPlayer() ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "skin", ent ) ) then return false end
if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end -- If our ent has an attached entity, we want to modify its skin instead
if ( !ent:SkinCount() ) then return false end
return ent:SkinCount() > 1
end,
MenuOpen = function( self, option, ent, tr )
--
-- Add a submenu to our automatically created menu option
--
local submenu = option:AddSubMenu()
--
-- Create a check item for each skin
--
local target = IsValid( ent.AttachedEntity ) and ent.AttachedEntity or ent
local num = target:SkinCount()
for i = 0, num - 1 do
local opt = submenu:AddOption( "Skin " .. i )
opt:SetRadio( true )
opt:SetChecked( target:GetSkin() == i )
opt:SetIsCheckable( true )
opt.OnChecked = function( s, checked ) if ( checked ) then self:SetSkin( ent, i ) end end
end
end,
Action = function( self, ent )
-- Nothing - we use SetSkin below
end,
SetSkin = function( self, ent, id )
self:MsgStart()
net.WriteEntity( ent )
net.WriteUInt( id, 8 )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
local skinid = net.ReadUInt( 8 )
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( !self:Filter( ent, ply ) ) then return end
ent = IsValid( ent.AttachedEntity ) and ent.AttachedEntity or ent
ent:SetSkin( skinid )
end
} )

View File

@@ -0,0 +1,194 @@
AddCSLuaFile()
-- The following is for the server's eyes only
local StatueDuplicator
if ( SERVER ) then
function StatueDuplicator( ply, ent, data )
if ( !data ) then
duplicator.ClearEntityModifier( ent, "statue_property" )
return
end
-- We have been pasted from duplicator, restore the necessary variables for the unstatue to work
if ( ent.StatueInfo == nil ) then
-- Ew. Have to wait a frame for the constraints to get pasted
timer.Simple( 0, function()
if ( !IsValid( ent ) ) then return end
local bones = ent:GetPhysicsObjectCount()
if ( bones < 2 ) then return end
ent:SetNWBool( "IsStatue", true )
ent.StatueInfo = {}
local con = constraint.FindConstraints( ent, "Weld" )
for id, t in pairs( con ) do
if ( t.Ent1 != t.Ent2 || t.Ent1 != ent || t.Bone1 != 0 ) then continue end
ent.StatueInfo[ t.Bone2 ] = t.Constraint
end
local numC = table.Count( ent.StatueInfo )
if ( numC < 1 --[[or numC != bones - 1]] ) then duplicator.ClearEntityModifier( ent, "statue_property" ) end
end )
end
duplicator.StoreEntityModifier( ent, "statue_property", data )
end
duplicator.RegisterEntityModifier( "statue_property", StatueDuplicator )
end
local playerTimeouts = {}
properties.Add( "statue", {
MenuLabel = "#makestatue",
Order = 1501,
MenuIcon = "icon16/lock.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:GetClass() != "prop_ragdoll" ) then return false end
if ( ent:GetNWBool( "IsStatue" ) ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "statue", ent ) ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !IsValid( ply ) ) then return end
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( ent:GetClass() != "prop_ragdoll" ) then return end
if ( !self:Filter( ent, ply ) ) then return end
-- Do not spam please!
local timeout = playerTimeouts[ ply ]
if ( timeout && timeout.time > CurTime() ) then
if ( !timeout.sentMessage ) then
ServerLog( "Player " .. tostring( ply ) .. " tried to use 'statue' property too rapidly!\n" )
ply:PrintMessage( HUD_PRINTTALK, "Please wait at least 0.2 seconds before trying to make another ragdoll a statue." )
timeout.sentMessage = true
end
return
end
local bones = ent:GetPhysicsObjectCount()
if ( bones < 2 ) then return end
if ( ent.StatueInfo ) then return end
ent.StatueInfo = {}
undo.Create( "Statue" )
for bone = 1, bones - 1 do
local constr = constraint.Weld( ent, ent, 0, bone, 0 )
if ( constr ) then
ent.StatueInfo[ bone ] = constr
ply:AddCleanup( "constraints", constr )
undo.AddEntity( constr )
end
local effectdata = EffectData()
effectdata:SetOrigin( ent:GetPhysicsObjectNum( bone ):GetPos() )
effectdata:SetScale( 1 )
effectdata:SetMagnitude( 1 )
util.Effect( "GlassImpact", effectdata, true, true )
end
ent:SetNWBool( "IsStatue", true )
undo.AddFunction( function()
if ( !IsValid( ent ) ) then return false end
ent:SetNWBool( "IsStatue", false )
ent.StatueInfo = nil
StatueDuplicator( ply, ent, nil )
end )
undo.SetPlayer( ply )
undo.SetCustomUndoText( "Undone #makestatue" )
undo.Finish( "#makestatue" )
StatueDuplicator( ply, ent, {} )
playerTimeouts[ ply ] = { time = CurTime() + 0.2, sentMessage = false }
end
} )
properties.Add( "statue_stop", {
MenuLabel = "#unstatue",
Order = 1501,
MenuIcon = "icon16/lock_open.png",
Filter = function( self, ent, ply )
if ( !IsValid( ent ) ) then return false end
if ( ent:GetClass() != "prop_ragdoll" ) then return false end
if ( !ent:GetNWBool( "IsStatue" ) ) then return false end
if ( !gamemode.Call( "CanProperty", ply, "unstatue", ent ) ) then return false end
return true
end,
Action = function( self, ent )
self:MsgStart()
net.WriteEntity( ent )
self:MsgEnd()
end,
Receive = function( self, length, ply )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !IsValid( ply ) ) then return end
if ( !properties.CanBeTargeted( ent, ply ) ) then return end
if ( ent:GetClass() != "prop_ragdoll" ) then return end
if ( !self:Filter( ent, ply ) ) then return end
local bones = ent:GetPhysicsObjectCount()
if ( bones < 2 ) then return end
if ( !ent.StatueInfo ) then return end
for k, v in pairs( ent.StatueInfo ) do
if ( IsValid( v ) ) then
v:Remove()
end
end
ent:SetNWBool( "IsStatue", false )
ent.StatueInfo = nil
StatueDuplicator( ply, ent, nil )
end
} )

View File

@@ -0,0 +1,35 @@
--[[---------------------------------------------------------
Name: KickId2
Desc: Allows admins to use the kickid2 command to kick people.
-----------------------------------------------------------]]
local function KickId( player, command, arguments )
if ( !player:IsAdmin() ) then return end
local id = arguments[1]
local reason = arguments[2] or "Kicked"
RunConsoleCommand( "kickid", id, Format( "%s (%s)", reason, player:Nick() ) )
end
concommand.Add( "kickid2", KickId, nil, "Alias of kickid, do not use.", { FCVAR_DONTRECORD } )
--[[---------------------------------------------------------
Name: BanId2
Desc: Allows admins to use the banid2 command to ban people.
-----------------------------------------------------------]]
local function BanID( player, command, arguments )
if ( !player:IsAdmin() ) then return end
local length = arguments[1]
local id = arguments[2]
RunConsoleCommand( "banid", length, id )
end
concommand.Add( "banid2", BanID, nil, "Alias of banid, do not use.", { FCVAR_DONTRECORD } )

View File

@@ -0,0 +1,119 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
local SPNE = 1
local TRSO = 2
local RSLD = 3
local LSLD = 4
local LARM = 5
local LWST = 6
local LHND = 7
local RARM = 8
local RWST = 9
local RHND = 10
local RTHY = 11
local RCLF = 12
local LTHY = 13
local LCLF = 14
local HEAD = 15
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 1.0
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.7 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.5 )
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * 0.2 )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * 0.2 )
-- sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * 0.3 )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[TRSO] = SENSORBONE.SPINE,
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[SPNE] = SENSORBONE.SPINE,
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = LTHY, to = RTHY, up = "hips_fwd" },
[SPNE] = { from_sensor = SENSORBONE.HEAD, to_sensor = SENSORBONE.SPINE, up = "chest_rgt" },
[TRSO] = { from_sensor = SENSORBONE.HEAD, to_sensor = SENSORBONE.SPINE, up = "chest_rgt" },
[HEAD] = { from_sensor = SENSORBONE.HEAD, to_sensor = SENSORBONE.SHOULDER, up = "chest_lft" },
[RSLD] = { from = RSLD, to = LSLD, up = "chest_bck" },
[LSLD] = { from = LSLD, to = RSLD, up = "chest_fwd" },
[RARM] = { from = RARM, to = RSLD, up = "chest_up" },
[LARM] = { from = LARM, to = LSLD, up = "chest_dn" },
[RWST] = { from = RHND, to = RARM, up = "chest_up" },
[LWST] = { from = LHND, to = LARM, up = "chest_dn" },
[RTHY] = { from = RCLF, to = RTHY, up_up = SPNE },
[RCLF] = { from_sensor = SENSORBONE.ANKLE_RIGHT, to_sensor = SENSORBONE.KNEE_RIGHT, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up_up = SPNE },
[LCLF] = { from_sensor = SENSORBONE.ANKLE_LEFT, to_sensor = SENSORBONE.KNEE_LEFT, up_up = LTHY },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_lft = RARM },
[LHND] = { from_sensor = SENSORBONE.HAND_LEFT, to_sensor = SENSORBONE.WRIST_LEFT, up_rgt = LARM },
[LWST] = { from = LHND, to = LARM, up = "chest_dn" },
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
pos[SPNE] = LerpVector( 0.45, pos[SPNE], pos[HEAD] )
pos[RWST] = pos[RARM]
pos[LWST] = pos[LARM]
end,
-- We're used as a default - no need to return true to anything here.
IsApplicable = function( self, ent )
local mdl = ent:GetModel()
if ( mdl:EndsWith( "models/player/ct_gign.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/ct_sas.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/ct_urban.mdl" ) ) then return true end
if ( mdl:EndsWith( "models//player/ct_gsg9.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/t_guerilla.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/t_leet.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/t_phoenix.mdl" ) ) then return true end
if ( mdl:EndsWith( "models//player/t_arctic.mdl" ) ) then return true end
return false
end,
}
list.Set( "SkeletonConvertor", "CounterStrikeSource", Builder )

View File

@@ -0,0 +1,104 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
local LTHY = 1
local SPNE = 2
local RSLD = 3
local RARM = 4
local LSLD = 5
local LARM = 6
local LHND = 7
local HEAD = 8
local RHND = 9
local RTHY = 10
local RCLF = 11
local LCLF = 12
local RFOT = 13
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 0.7
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.7 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.5 )
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * 0.3 )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * 0.3 )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.HIP_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 0.2 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 0.2 )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[HEAD] = SENSORBONE.HEAD,
[SPNE] = SENSORBONE.SPINE,
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = LTHY, to = RTHY, up = "hips_fwd" },
[SPNE] = { from = HEAD, to = SPNE, up = "chest_rgt" },
[HEAD] = { from_sensor = SENSORBONE.HEAD, to_sensor = SENSORBONE.SPINE, up = "chest_lft" },
[RSLD] = { from = RARM, to = RSLD, up = "chest_up" },
[RARM] = { from = RHND, to = RARM, up_up = RSLD },
[LSLD] = { from = LARM, to = LSLD, up = "chest_dn" },
[LARM] = { from = LHND, to = LARM, up_up = LSLD },
[RTHY] = { from = RCLF, to = RTHY, up_up = SPNE },
[RCLF] = { from = RFOT, to = RCLF, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up_up = SPNE },
[LCLF] = { from_sensor = SENSORBONE.ANKLE_LEFT, to = LCLF, up_up = LTHY },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_lft = RARM },
[LHND] = { from_sensor = SENSORBONE.HAND_LEFT, to_sensor = SENSORBONE.WRIST_LEFT, up_rgt = LARM }
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
pos[SPNE] = LerpVector( 0.4, pos[SPNE], pos[HEAD] )
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
ang[RFOT] = ang[RCLF]:Right():AngleEx( ang[RCLF]:Up() ) + Angle( 20, 0, 0 )
end,
-- Should this entity use this builder?
IsApplicable = function( self, ent )
return ent:GetModel():EndsWith( "models/eli.mdl" )
end,
}
list.Set( "SkeletonConvertor", "Eli", Builder )

View File

@@ -0,0 +1,163 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
-- coord
local RTHY = 2
local RCLF = 3
local RFOT = 4
local LTHY = 5
local LCLF = 6
local LFOT = 7
local SPNE = 8
local RSLD = 9
local RARM = 10
local LSLD = 11
local LARM = 12
local LHND = 13
local NECK = 14
local HEAD = 15
local RHND = 16
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 0.6
local acrossshoulders = ( sensor[SENSORBONE.SHOULDER_RIGHT] - sensor[SENSORBONE.SHOULDER_LEFT] ):GetNormal() * 0.08
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.7 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.9 )
local acrosships = ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.HIP_RIGHT] ):GetNormal() * 0.06
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * -0.1 + acrosships )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * -0.1 + acrosships * -1 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.KNEE_LEFT]-sensor[SENSORBONE.HIP_LEFT] ) * 0.0 + acrosships )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.KNEE_RIGHT] - sensor[SENSORBONE.HIP_RIGHT] ) * 0.0 - acrosships )
sensor[SENSORBONE.FOOT_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 0.2 + acrosships )
sensor[SENSORBONE.FOOT_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 0.2 - acrosships )
sensor[SENSORBONE.ANKLE_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 0.2 + acrosships )
sensor[SENSORBONE.ANKLE_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 0.2 - acrosships )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[LFOT] = SENSORBONE.ANKLE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[NECK] = SENSORBONE.HEAD,
[SPNE] = { type = "lerp", value = 0.8, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE }
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = PLVS, to = SPNE, up = "hips_back" },
[SPNE] = { from = PLVS, to = SPNE, up = "chest_bck" },
[HEAD] = { from = SPNE, to = HEAD, up = "head_back" },
[RSLD] = { from = RARM, to = RSLD, up_rgt = SPNE },
[RARM] = { from = RHND, to = RARM, up_rgt = RSLD },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_fwd = RARM },
[LSLD] = { from = LARM, to = LSLD, up_lft = SPNE },
[LARM] = { from = LHND, to = LARM, up_up = LSLD },
[LHND] = { from_sensor = SENSORBONE.WRIST_LEFT, to_sensor = SENSORBONE.HAND_LEFT, up_bck = LARM },
[RTHY] = { from = RCLF, to = RTHY, up_dn = PLVS },
[RCLF] = { from = RFOT, to = RCLF, up_up = RTHY },
[RFOT] = { from = RCLF, to = RFOT, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up_dn = PLVS },
[LCLF] = { from = LFOT, to = LCLF, up_up = LTHY },
[LFOT] = { from = LFOT, to = LCLF, up_up = LTHY },
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
--
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
--
ang[RFOT]:RotateAroundAxis( ang[RFOT]:Up(), -90 )
ang[RFOT]:RotateAroundAxis( ang[RFOT]:Forward(), 180 )
ang[LFOT]:RotateAroundAxis( ang[LFOT]:Up(), 90 )
ang[LFOT]:RotateAroundAxis( ang[LFOT]:Forward(), -45 )
ang[RFOT]:RotateAroundAxis( ang[LFOT]:Forward(), -45 )
ang[PLVS]:RotateAroundAxis( ang[PLVS]:Up(), -90 )
ang[SPNE]:RotateAroundAxis( ang[SPNE]:Up(), -90 )
ang[HEAD]:RotateAroundAxis( ang[HEAD]:Up(), -90 )
ang[NECK] = ang[HEAD]
pos[1] = pos[PLVS]
ang[1] = ang[PLVS] * -1
ang[1]:RotateAroundAxis( ang[1]:Right(), 90 )
pos[17] = pos[PLVS]
ang[17] = ang[PLVS] * -1
ang[17]:RotateAroundAxis( ang[1]:Right(), 90 )
--
-- AGH HANDS
--
ang[LHND] = ang[LARM] * 1
ang[LHND]:RotateAroundAxis( ang[LHND]:Up(), 90 )
ang[RHND] = ang[RARM] * 1
ang[RHND]:RotateAroundAxis( ang[RHND]:Up(), -90 )
ang[RHND]:RotateAroundAxis( ang[RHND]:Right(), 180 )
end,
IsApplicable = function( self, ent )
local mdl = ent:GetModel()
if ( mdl:EndsWith( "models/player/engineer.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/hwm/engineer.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/engineer/bot_engineer.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/demo_engineer/bot_demo_engineer.mdl" ) ) then return true end
return false
end,
}
list.Set( "SkeletonConvertor", "TF2_engineer", Builder )

View File

@@ -0,0 +1,147 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
local RTHY = 1
local RCLF = 2
local LTHY = 3
local LCLF = 4
local LFOT = 5
local SPNE = 6
local SPN2 = 7
local RSLD = 8
local LSLD = 9
local LARM = 10
local LHND = 11
local RARM = 12
local RHND = 13
local HEAD = 14
local RFOT = 15
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 1.2
local acrossshoulders = ( sensor[SENSORBONE.SHOULDER_RIGHT] - sensor[SENSORBONE.SHOULDER_LEFT] ):GetNormal() * 0.08
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.6 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.9 )
local acrosships = ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.HIP_RIGHT] ):GetNormal() * 0.06
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * -0.1 + acrosships )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * -0.1 + acrosships * -1 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.KNEE_LEFT]-sensor[SENSORBONE.HIP_LEFT] ) * 0.3 + acrosships )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.KNEE_RIGHT] - sensor[SENSORBONE.HIP_RIGHT] ) * 0.3 - acrosships )
sensor[SENSORBONE.FOOT_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 1.2 + acrosships )
sensor[SENSORBONE.FOOT_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 1.2 - acrosships )
sensor[SENSORBONE.ANKLE_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 1.2 + acrosships )
sensor[SENSORBONE.ANKLE_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 1.2 - acrosships )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[LFOT] = SENSORBONE.ANKLE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[SPNE] = { type = "lerp", value = 0.8, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE },
[SPN2] = { type = "lerp", value = 0.8, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE }
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = PLVS, to = SPNE, up = "hips_back" },
[SPNE] = { from = PLVS, to = SPNE, up = "chest_bck" },
[SPN2] = { from = PLVS, to = SPNE, up = "chest_bck" },
[HEAD] = { from = SPNE, to = HEAD, up = "head_back" },
[RSLD] = { from = RARM, to = RSLD, up_rgt = SPNE },
[RARM] = { from = RHND, to = RARM, up_rgt = RSLD },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_fwd = RARM },
[LSLD] = { from = LARM, to = LSLD, up_lft = SPNE },
[LARM] = { from = LHND, to = LARM, up_up = LSLD },
[LHND] = { from_sensor = SENSORBONE.WRIST_LEFT, to_sensor = SENSORBONE.HAND_LEFT, up_bck = LARM },
[RTHY] = { from = RCLF, to = RTHY, up = "right" },
[RCLF] = { from = RFOT, to = RCLF, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up = "forward" },
[LCLF] = { from = LFOT, to = LCLF, up_up = LTHY },
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
--
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
--
ang[RFOT] = ang[RCLF]:Right():AngleEx( ang[RCLF]:Up() ) + Angle( 0, 180, -40 )
ang[LFOT] = ang[LCLF]:Right():AngleEx( ang[LCLF]:Up() ) + Angle( 0, -90, 130 )
ang[PLVS]:RotateAroundAxis( ang[PLVS]:Up(), -90 )
ang[SPNE]:RotateAroundAxis( ang[SPNE]:Up(), -90 )
ang[SPN2]:RotateAroundAxis( ang[SPNE]:Up(), -90 )
ang[HEAD]:RotateAroundAxis( ang[HEAD]:Up(), -90 )
--
-- AGH HANDS
--
ang[LHND] = ang[LARM] * 1
ang[LHND]:RotateAroundAxis( ang[LHND]:Up(), 90 )
ang[RHND] = ang[RARM] * 1
ang[RHND]:RotateAroundAxis( ang[RHND]:Up(), -90 )
ang[RHND]:RotateAroundAxis( ang[RHND]:Right(), 180 )
end,
IsApplicable = function( self, ent )
local mdl = ent:GetModel()
if ( mdl:EndsWith( "models/player/heavy.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/hwm/heavy.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/heavy/bot_heavy.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/heavy_boss/bot_heavy_boss.mdl" ) ) then return true end
return false
end,
}
list.Set( "SkeletonConvertor", "TF2_heavy", Builder )

View File

@@ -0,0 +1,143 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
-- Coat 1-9
local RTHY = 10
local RCLF = 11
-- Coat 12
local LTHY = 13
local LCLF = 14
local LFOT = 15
local SPNE = 16
local RSLD = 17
local RARM = 18
local LSLD = 19
local LARM = 20
local HEAD = 21
local RHND = 22
local RFOT = 23
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 0.6
local acrossshoulders = ( sensor[SENSORBONE.SHOULDER_RIGHT] - sensor[SENSORBONE.SHOULDER_LEFT] ):GetNormal() * 0.08
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.6 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.9 )
local acrosships = ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.HIP_RIGHT] ):GetNormal() * 0.06
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * -0.1 + acrosships )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * -0.1 + acrosships * -1 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.KNEE_LEFT]-sensor[SENSORBONE.HIP_LEFT] ) * 0.1 + acrosships )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.KNEE_RIGHT] - sensor[SENSORBONE.HIP_RIGHT] ) * 0.1 - acrosships )
sensor[SENSORBONE.FOOT_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 1.0 + acrosships )
sensor[SENSORBONE.FOOT_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 1.0 - acrosships )
sensor[SENSORBONE.ANKLE_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 1.0 + acrosships )
sensor[SENSORBONE.ANKLE_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 1.0 - acrosships )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[LFOT] = SENSORBONE.ANKLE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[SPNE] = { type = "lerp", value = 0.8, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE }
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = PLVS, to = SPNE, up = "hips_back" },
[SPNE] = { from = PLVS, to = SPNE, up = "chest_bck" },
[HEAD] = { from = SPNE, to = HEAD, up = "head_back" },
[RSLD] = { from = RARM, to = RSLD, up_rgt = SPNE },
[RARM] = { from = RHND, to = RARM, up_rgt = RSLD },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_fwd = RARM },
[LSLD] = { from = LARM, to = LSLD, up_lft = SPNE },
[LARM] = { from_sensor = SENSORBONE.HAND_LEFT, to_sensor = SENSORBONE.ELBOW_LEFT, up_up = LSLD },
[RTHY] = { from = RCLF, to = RTHY, up = "right" },
[RCLF] = { from = RFOT, to = RCLF, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up = "forward" },
[LCLF] = { from = LFOT, to = LCLF, up_up = LTHY },
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
--
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
--
ang[RFOT] = ang[RCLF]:Right():AngleEx( ang[RCLF]:Up() ) + Angle( 0, 180, -40 )
ang[LFOT] = ang[LCLF]:Right():AngleEx( ang[LCLF]:Up() ) + Angle( 0, -90, 130 )
ang[PLVS]:RotateAroundAxis( ang[PLVS]:Up(), -90 )
ang[SPNE]:RotateAroundAxis( ang[SPNE]:Up(), -90 )
ang[HEAD]:RotateAroundAxis( ang[HEAD]:Up(), -90 )
--
-- AGH HANDS
--
ang[RHND] = ang[RARM] * 1
ang[RHND]:RotateAroundAxis( ang[RHND]:Up(), -90 )
ang[RHND]:RotateAroundAxis( ang[RHND]:Right(), 180 )
end,
IsApplicable = function( self, ent )
local mdl = ent:GetModel()
if ( mdl:EndsWith( "models/player/medic.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/hwm/medic.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/medic/bot_medic.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/medic_boss/bot_medic_boss.mdl" ) ) then return true end
return false
end,
}
list.Set( "SkeletonConvertor", "TF2_medic", Builder )

View File

@@ -0,0 +1,147 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
local RTHY = 1
local RCLF = 2
local LTHY = 3
local LCLF = 4
local LFOT = 5
local SPNE = 6
local RSLD = 7
local RARM = 8
local LSLD = 9
local LARM = 10
local LHND = 11
local HEAD = 12
local RHND = 13
local RFOT = 14
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 0.6
local acrossshoulders = ( sensor[SENSORBONE.SHOULDER_RIGHT] - sensor[SENSORBONE.SHOULDER_LEFT] ):GetNormal() * 0.08
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.7 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.9 )
local acrosships = ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.HIP_RIGHT] ):GetNormal() * 0.06
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * -0.1 + acrosships )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * -0.1 + acrosships * -1 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.KNEE_LEFT]-sensor[SENSORBONE.HIP_LEFT] ) * 0.3 + acrosships )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.KNEE_RIGHT] - sensor[SENSORBONE.HIP_RIGHT] ) * 0.3 - acrosships )
sensor[SENSORBONE.FOOT_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 1.2 + acrosships )
sensor[SENSORBONE.FOOT_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 1.2 - acrosships )
sensor[SENSORBONE.ANKLE_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 1.2 + acrosships )
sensor[SENSORBONE.ANKLE_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 1.2 - acrosships )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[LFOT] = SENSORBONE.ANKLE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[SPNE] = { type = "lerp", value = 0.8, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE }
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = PLVS, to = SPNE, up = "hips_back" },
[SPNE] = { from = PLVS, to = SPNE, up = "chest_bck" },
[HEAD] = { from = SPNE, to = HEAD, up = "head_back" },
[RSLD] = { from = RARM, to = RSLD, up_rgt = SPNE },
[RARM] = { from = RHND, to = RARM, up_rgt = RSLD },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_fwd = RARM },
[LSLD] = { from = LARM, to = LSLD, up_lft = SPNE },
[LARM] = { from = LHND, to = LARM, up_up = LSLD },
[LHND] = { from_sensor = SENSORBONE.WRIST_LEFT, to_sensor = SENSORBONE.HAND_LEFT, up_bck = LARM },
[RTHY] = { from = RCLF, to = RTHY, up = "right" },
[RCLF] = { from = RFOT, to = RCLF, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up = "forward" },
[LCLF] = { from = LFOT, to = LCLF, up_up = LTHY },
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
--
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
--
ang[RFOT] = ang[RCLF]:Right():AngleEx( ang[RCLF]:Up() ) + Angle( 0, 180, -40 )
ang[LFOT] = ang[LCLF]:Right():AngleEx( ang[LCLF]:Up() ) + Angle( 0, -90, 130 )
ang[PLVS]:RotateAroundAxis( ang[PLVS]:Up(), -90 )
ang[SPNE]:RotateAroundAxis( ang[SPNE]:Up(), -90 )
ang[HEAD]:RotateAroundAxis( ang[HEAD]:Up(), -90 )
--
-- AGH HANDS
--
ang[LHND] = ang[LARM] * 1
ang[LHND]:RotateAroundAxis( ang[LHND]:Up(), 90 )
ang[RHND] = ang[RARM] * 1
ang[RHND]:RotateAroundAxis( ang[RHND]:Up(), -90 )
ang[RHND]:RotateAroundAxis( ang[RHND]:Right(), 180 )
end,
IsApplicable = function( self, ent )
local mdl = ent:GetModel();
if ( mdl:EndsWith( "models/player/pyro.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/hwm/pyro.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/pyro/bot_pyro.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/demo.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/hwm/demo.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/pyro/bot_demo.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/pyro_boss/bot_pyro_boss.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/demo_boss/bot_demo_boss.mdl" ) ) then return true end
return false
end,
}
list.Set( "SkeletonConvertor", "TF2_pyro", Builder )

View File

@@ -0,0 +1,150 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
local RTHY = 1
local RCLF = 2
local LTHY = 3
local LCLF = 4
local LFOT = 5
local SPNE = 6
local RSLD = 7
local RARM = 8
local LSLD = 9
local LARM = 10
local LHND = 11
local NECK = 12
local HEAD = 13
local DGTG = 14
local RHND = 15
local RFOT = 16
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 0.5
local acrossshoulders = ( sensor[SENSORBONE.SHOULDER_RIGHT] - sensor[SENSORBONE.SHOULDER_LEFT] ):GetNormal() * 0.08
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.7 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.9 )
local acrosships = ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.HIP_RIGHT] ):GetNormal() * 0.06
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * -0.1 + acrosships )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * -0.1 + acrosships * -1 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.KNEE_LEFT]-sensor[SENSORBONE.HIP_LEFT] ) * 0.3 + acrosships )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.KNEE_RIGHT] - sensor[SENSORBONE.HIP_RIGHT] ) * 0.3 - acrosships )
sensor[SENSORBONE.FOOT_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 1.2 + acrosships )
sensor[SENSORBONE.FOOT_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 1.2 - acrosships )
sensor[SENSORBONE.ANKLE_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 1.2 + acrosships )
sensor[SENSORBONE.ANKLE_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 1.2 - acrosships )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
--[TRSO] = { type = "lerp", value = 0.2, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE },
[NECK] = { type = "lerp", value = 0.5, from = SENSORBONE.SHOULDER, to = SENSORBONE.HEAD },
[DGTG] = { type = "lerp", value = 0.2, from = SENSORBONE.SHOULDER, to = SENSORBONE.HIP },
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[LFOT] = SENSORBONE.ANKLE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[SPNE] = { type = "lerp", value = 0.8, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE }
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = PLVS, to = SPNE, up = "hips_back" },
[SPNE] = { from = PLVS, to = SPNE, up = "chest_bck" },
[HEAD] = { from = NECK, to = HEAD, up = "head_back" },
[NECK] = { from = SPNE, to = NECK, up = "head_back" },
[DGTG] = { from = NECK, to = DGTG, up = "chest_up" },
[RSLD] = { from = RARM, to = RSLD, up_rgt = SPNE },
[RARM] = { from = RHND, to = RARM, up_rgt = RSLD },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_fwd = RARM },
[LSLD] = { from = LARM, to = LSLD, up_dn = SPNE },
[LARM] = { from = LHND, to = LARM, up_up = LSLD },
[LHND] = { from_sensor = SENSORBONE.WRIST_LEFT, to_sensor = SENSORBONE.HAND_LEFT, up_bck = LARM },
[RTHY] = { from = RCLF, to = RTHY, up = "right" },
[RCLF] = { from = RFOT, to = RCLF, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up = "forward" },
[LCLF] = { from = LFOT, to = LCLF, up_up = LTHY },
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
--
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
--
ang[RFOT] = ang[RCLF]:Right():AngleEx( ang[RCLF]:Up() ) + Angle( 0, 180, -40 )
ang[LFOT] = ang[LCLF]:Right():AngleEx( ang[LCLF]:Up() ) + Angle( 0, -90, 130 )
ang[PLVS]:RotateAroundAxis( ang[PLVS]:Up(), -90 )
ang[SPNE]:RotateAroundAxis( ang[SPNE]:Up(), -90 )
ang[NECK]:RotateAroundAxis( ang[NECK]:Up(), -90 )
ang[HEAD]:RotateAroundAxis( ang[HEAD]:Up(), -90 )
--
-- AGH HANDS
--
ang[LHND] = ang[LARM] * 1
ang[LHND]:RotateAroundAxis( ang[LHND]:Up(), 90 )
ang[RHND] = ang[RARM] * 1
ang[RHND]:RotateAroundAxis( ang[RHND]:Up(), -90 )
ang[RHND]:RotateAroundAxis( ang[RHND]:Right(), 180 )
end,
IsApplicable = function( self, ent )
local mdl = ent:GetModel()
if ( mdl:EndsWith( "models/player/scout.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/hwm/scout.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/scout/bot_scout.mdl" ) ) then return true end
return false
end,
}
list.Set( "SkeletonConvertor", "TF2_scout", Builder )

View File

@@ -0,0 +1,159 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
local RTHY = 1
local RCLF = 2
local RFOT = 3
local LTHY = 4
local LCLF = 5
local LFOT = 6
local SPNE = 7
local RSLD = 8
local RARM = 9
local LSLD = 10
local LARM = 11
local LHND = 12
local NECK = 13
local HEAD = 14
local RHND = 15
-- pouch
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 0.8
local acrossshoulders = ( sensor[SENSORBONE.SHOULDER_RIGHT] - sensor[SENSORBONE.SHOULDER_LEFT] ):GetNormal() * 0.08
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.7 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.9 )
local acrosships = ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.HIP_RIGHT] ):GetNormal() * 0.06
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * -0.1 + acrosships )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * -0.1 + acrosships * -1 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.KNEE_LEFT]-sensor[SENSORBONE.HIP_LEFT] ) * 0.3 + acrosships )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.KNEE_RIGHT] - sensor[SENSORBONE.HIP_RIGHT] ) * 0.3 - acrosships )
sensor[SENSORBONE.FOOT_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 0.6 + acrosships )
sensor[SENSORBONE.FOOT_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 0.6 - acrosships )
sensor[SENSORBONE.ANKLE_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 0.6 + acrosships )
sensor[SENSORBONE.ANKLE_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 0.6 - acrosships )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[LFOT] = SENSORBONE.ANKLE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[NECK] = SENSORBONE.HEAD,
[SPNE] = { type = "lerp", value = 0.8, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE }
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = PLVS, to = SPNE, up = "hips_back" },
[SPNE] = { from = PLVS, to = SPNE, up = "chest_bck" },
[HEAD] = { from = SPNE, to = HEAD, up = "head_back" },
[RSLD] = { from = RARM, to = RSLD, up_rgt = SPNE },
[RARM] = { from = RHND, to = RARM, up_rgt = RSLD },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_fwd = RARM },
[LSLD] = { from = LARM, to = LSLD, up_lft = SPNE },
[LARM] = { from = LHND, to = LARM, up_up = LSLD },
[LHND] = { from_sensor = SENSORBONE.WRIST_LEFT, to_sensor = SENSORBONE.HAND_LEFT, up_bck = LARM },
[RTHY] = { from = RCLF, to = RTHY, up_dn = PLVS },
[RCLF] = { from = RFOT, to = RCLF, up_lft = RTHY },
[RFOT] = { from = RCLF, to = RFOT, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up_dn = PLVS },
[LCLF] = { from = LFOT, to = LCLF, up_up = LTHY },
[LFOT] = { from = LFOT, to = LCLF, up_up = LTHY },
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
--
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
--
ang[RFOT]:RotateAroundAxis( ang[RFOT]:Up(), -90 )
ang[RFOT]:RotateAroundAxis( ang[RFOT]:Forward(), 180 )
ang[LFOT]:RotateAroundAxis( ang[LFOT]:Up(), 90 )
ang[LFOT]:RotateAroundAxis( ang[LFOT]:Forward(), -45 )
ang[RFOT]:RotateAroundAxis( ang[LFOT]:Forward(), -45 )
ang[PLVS]:RotateAroundAxis( ang[PLVS]:Up(), -90 )
ang[SPNE]:RotateAroundAxis( ang[SPNE]:Up(), -90 )
ang[HEAD]:RotateAroundAxis( ang[HEAD]:Up(), -90 )
ang[NECK] = ang[HEAD]
pos[16] = pos[PLVS]
ang[16] = ang[PLVS] * 1
ang[16]:RotateAroundAxis( ang[1]:Right(), 180 )
--
-- AGH HANDS
--
ang[LHND] = ang[LARM] * 1
ang[LHND]:RotateAroundAxis( ang[LHND]:Up(), 90 )
ang[RHND] = ang[RARM] * 1
ang[RHND]:RotateAroundAxis( ang[RHND]:Up(), -90 )
ang[RHND]:RotateAroundAxis( ang[RHND]:Right(), 180 )
end,
IsApplicable = function( self, ent )
local mdl = ent:GetModel()
if ( mdl:EndsWith( "models/player/sniper.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/hwm/sniper.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/sniper/bot_sniper.mdl" ) ) then return true end
return false
end,
}
list.Set( "SkeletonConvertor", "TF2_sniper", Builder )

View File

@@ -0,0 +1,154 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
local RTHY = 1
local RCLF = 2
local LTHY = 3
local LCLF = 4
local LFOT = 5
local SPNE = 6
local TRSO = 7
local RSLD = 8
local LSLD = 9
local LARM = 10
local LHND = 11
local RARM = 12
local NECK = 13
local RHND = 14
local HEAD = 15
local RFOT = 16
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 0.8
local acrossshoulders = ( sensor[SENSORBONE.SHOULDER_RIGHT] - sensor[SENSORBONE.SHOULDER_LEFT] ):GetNormal() * 0.08
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.7 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch - acrossshoulders )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch + acrossshoulders )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.8 )
local acrosships = ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.HIP_RIGHT] ):GetNormal() * 0.08
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * -0.1 + acrosships )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * -0.1 + acrosships * -1 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.KNEE_LEFT]-sensor[SENSORBONE.HIP_LEFT] ) * 0.3 + acrosships )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.KNEE_RIGHT] - sensor[SENSORBONE.HIP_RIGHT] ) * 0.3 - acrosships )
sensor[SENSORBONE.ANKLE_LEFT]:Add( ( sensor[SENSORBONE.ANKLE_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 0.8 + acrosships )
sensor[SENSORBONE.ANKLE_RIGHT]:Add( ( sensor[SENSORBONE.ANKLE_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 0.8 - acrosships )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[TRSO] = { type = "lerp", value = 0.2, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE },
[NECK] = { type = "lerp", value = 0.3, from = SENSORBONE.SHOULDER, to = SENSORBONE.HEAD },
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[LFOT] = SENSORBONE.ANKLE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[SPNE] = { type = "lerp", value = 0.8, from = SENSORBONE.SHOULDER, to = SENSORBONE.SPINE }
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = PLVS, to = SPNE, up = "hips_back" },
[SPNE] = { from = SPNE, to = TRSO, up = "chest_bck" },
[TRSO] = { from = TRSO, to = NECK, up = "head_back" },
[HEAD] = { from = NECK, to = HEAD, up = "head_back" },
[NECK] = { from = TRSO, to = NECK, up = "head_back" },
[RSLD] = { from = RARM, to = RSLD, up_rgt = TRSO },
[RARM] = { from = RHND, to = RARM, up_up = RSLD },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_dn = RARM },
[LSLD] = { from = LARM, to = LSLD, up_lft = TRSO },
[LARM] = { from = LHND, to = LARM, up_up = LSLD },
[LHND] = { from_sensor = SENSORBONE.WRIST_LEFT, to_sensor = SENSORBONE.HAND_LEFT, up_up = LARM },
[RTHY] = { from = RCLF, to = RTHY, up = "right" },
[RCLF] = { from = RFOT, to = RCLF, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up = "forward" },
[LCLF] = { from = LFOT, to = LCLF, up_up = LTHY },
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
--
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
--
ang[RFOT] = ang[RCLF]:Right():AngleEx( ang[RCLF]:Up() ) + Angle( 0, 90, -70 )
ang[LFOT] = ang[LCLF]:Right():AngleEx( ang[LCLF]:Up() ) + Angle( 0, -90, 110 )
--
-- TODO: Get the hands working.
--
--ang[RHND] = ( ang[RARM]:Up() ):AngleEx( ang[RARM]:Right() * -1 )
--ang[LHND] = ( ang[LARM]:Up() ):AngleEx( ang[LARM]:Right() )
--
-- Maya uses Y up for some bones. Because life isn't hard enough already.
--
ang[PLVS]:RotateAroundAxis( ang[PLVS]:Up(), -90 )
ang[SPNE]:RotateAroundAxis( ang[SPNE]:Up(), -90 )
ang[TRSO]:RotateAroundAxis( ang[TRSO]:Up(), -90 )
ang[NECK]:RotateAroundAxis( ang[NECK]:Up(), -90 )
ang[HEAD]:RotateAroundAxis( ang[HEAD]:Up(), -90 )
ang[LHND]:RotateAroundAxis( ang[LHND]:Up(), -90 )
ang[RHND]:RotateAroundAxis( ang[RHND]:Up(), -90 )
--ang[LHND]:RotateAroundAxis( ang[LHND]:Right(), 180 )
end,
IsApplicable = function( self, ent )
local mdl = ent:GetModel()
if ( mdl:EndsWith( "models/player/hwm/soldier.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/soldier.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/spy.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/player/hwm/spy.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/soldier/bot_soldier.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/spy/bot_spy.mdl" ) ) then return true end
if ( mdl:EndsWith( "models/bots/soldier_boss/bot_soldier_boss.mdl" ) ) then return true end
return false
end,
}
list.Set( "SkeletonConvertor", "TF2_soldier_spy", Builder )

View File

@@ -0,0 +1,104 @@
--
-- These are the physics bone numbers
--
local PLVS = 0
local SPNE = 1
local RSLD = 2
local LSLD = 3
local LARM = 4
local LHND = 5
local RARM = 6
local RHND = 7
local RTHY = 8
local RCLF = 9
local HEAD = 10
local LTHY = 11
local LCLF = 12
local LFOT = 13
local RFOT = 14
local Builder =
{
PrePosition = function( self, sensor )
local spinestretch = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.SPINE] ) * 0.7
sensor[SENSORBONE.SHOULDER]:Add( spinestretch * 0.7 )
sensor[SENSORBONE.SHOULDER_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.SHOULDER_LEFT]:Add( spinestretch )
sensor[SENSORBONE.ELBOW_LEFT]:Add( spinestretch )
sensor[SENSORBONE.ELBOW_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.WRIST_LEFT]:Add( spinestretch )
sensor[SENSORBONE.WRIST_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.HAND_LEFT]:Add( spinestretch )
sensor[SENSORBONE.HAND_RIGHT]:Add( spinestretch )
sensor[SENSORBONE.HEAD]:Add( spinestretch * 0.5 )
sensor[SENSORBONE.HIP_LEFT]:Add( spinestretch * 0.3 )
sensor[SENSORBONE.HIP_RIGHT]:Add( spinestretch * 0.3 )
sensor[SENSORBONE.KNEE_RIGHT]:Add( ( sensor[SENSORBONE.HIP_RIGHT] - sensor[SENSORBONE.KNEE_RIGHT] ) * 0.2 )
sensor[SENSORBONE.KNEE_LEFT]:Add( ( sensor[SENSORBONE.HIP_LEFT] - sensor[SENSORBONE.KNEE_LEFT] ) * 0.2 )
end,
--
-- Which on the sensor should we use for which ones on our model
--
PositionTable =
{
[PLVS] = SENSORBONE.HIP,
[RSLD] = SENSORBONE.SHOULDER_RIGHT,
[LSLD] = SENSORBONE.SHOULDER_LEFT,
[LARM] = SENSORBONE.ELBOW_LEFT,
[LHND] = SENSORBONE.WRIST_LEFT,
[RARM] = SENSORBONE.ELBOW_RIGHT,
[RHND] = SENSORBONE.WRIST_RIGHT,
[LTHY] = SENSORBONE.HIP_LEFT,
[RTHY] = SENSORBONE.HIP_RIGHT,
[RCLF] = SENSORBONE.KNEE_RIGHT,
[LCLF] = SENSORBONE.KNEE_LEFT,
[RFOT] = SENSORBONE.ANKLE_RIGHT,
[LFOT] = SENSORBONE.ANKLE_LEFT,
[HEAD] = SENSORBONE.HEAD,
[SPNE] = SENSORBONE.SPINE,
},
--
-- Which bones should we use to determine our bone angles
--
AnglesTable =
{
[PLVS] = { from = LTHY, to = RTHY, up = "hips_fwd" },
[SPNE] = { from_sensor = SENSORBONE.HEAD, to_sensor = SENSORBONE.SPINE, up = "chest_rgt" },
[HEAD] = { from_sensor = SENSORBONE.HEAD, to_sensor = SENSORBONE.SHOULDER, up = "chest_lft" },
[RSLD] = { from = RARM, to = RSLD, up = "chest_up" },
[LSLD] = { from = LARM, to = LSLD, up = "chest_dn" },
[RARM] = { from = RHND, to = RARM, up_up = RSLD },
[LARM] = { from = LHND, to = LARM, up_up = LSLD },
[RTHY] = { from = RCLF, to = RTHY, up_up = SPNE },
[RCLF] = { from = RFOT, to = RCLF, up_up = RTHY },
[LTHY] = { from = LCLF, to = LTHY, up_up = SPNE },
[LCLF] = { from = LFOT, to = LCLF, up_up = LTHY },
[RHND] = { from_sensor = SENSORBONE.HAND_RIGHT, to_sensor = SENSORBONE.WRIST_RIGHT, up_lft = RARM },
[LHND] = { from_sensor = SENSORBONE.HAND_LEFT, to_sensor = SENSORBONE.WRIST_LEFT, up_rgt = LARM }
},
--
-- Any polishing that can't be done with the above tables
--
Complete = function( self, player, sensor, rotation, pos, ang )
pos[SPNE] = LerpVector( 0.45, pos[SPNE], pos[HEAD] )
-- Feet are insanely spazzy, so we lock the feet to the angle of the calf
ang[LFOT] = ang[LCLF]:Right():AngleEx( ang[LCLF]:Up() ) + Angle( 20, 0, 0 )
ang[RFOT] = ang[RCLF]:Right():AngleEx( ang[RCLF]:Up() ) + Angle( 20, 0, 0 )
end,
-- We're used as a default - no need to return true to anything here.
IsApplicable = function( self, ent ) return false end,
}
list.Set( "SkeletonConvertor", "ValveBiped", Builder )

View File

@@ -0,0 +1,11 @@
resource.AddWorkshop("2486994157") -- Mac Quests
resource.AddWorkshop("2973776853") -- QWB
resource.AddWorkshop("1912375404") -- Map
resource.AddWorkshop("2844924488") -- Senwai
resource.AddWorkshop("3626176147") -- Event content
for i,addon in pairs( engine.GetAddons() ) do
if addon.mounted then
resource.AddWorkshop(addon.wsid)
--print("\t[+] "..addon.wsid..": "..addon.title)
end
end

View File

@@ -0,0 +1,269 @@
--
-- The server only runs this file so it can send it to the client
--
if ( SERVER ) then AddCSLuaFile( "utilities_menu.lua" ) return end
local function Undo( pnl )
-- This is added by the undo module dynamically
end
local function User_Cleanup( pnl )
-- This is added by the cleanup module dynamically
end
local function LoadInConvarDefaults( cvars )
for k, v in pairs( cvars ) do
local convar = GetConVar( k )
if ( convar and convar:GetDefault():len() != 0 ) then
cvars[ k ] = convar:GetDefault()
end
end
end
local function ServerSettings( pnl )
pnl:Help( "#utilities.serversettings" )
local ConVarsDefault = {
hostname = "My Garry's Mod Server",
-- sv_password = "", -- Can't be read by addons/servers
sv_kickerrornum = "0",
sv_allowcslua = "0",
sv_sticktoground = "1",
sv_playerpickupallowed = "1",
mp_falldamage = "0",
gmod_suit = "0",
gmod_maxammo = "9999",
sv_gravity = "600",
sv_friction = "8",
phys_timescale = "1.00",
gmod_physiterations = "4",
sv_defaultdeployspeed = "4.00",
sv_noclipspeed = "5",
g_ragdoll_maxcount = "32",
sv_timeout = "65"
}
LoadInConvarDefaults( ConVarsDefault )
pnl:ToolPresets( "util_server", ConVarsDefault )
pnl:TextEntry( "#utilities.hostname", "hostname" )
pnl:TextEntry( "#utilities.password", "sv_password" )
pnl:CheckBox( "#utilities.kickerrornum", "sv_kickerrornum" )
pnl:CheckBox( "#utilities.allowcslua", "sv_allowcslua" )
pnl:CheckBox( "#utilities.sticktoground", "sv_sticktoground" )
pnl:ControlHelp( "#utilities.sticktoground.help" ):DockMargin( 32, 4, 32, 8 ) -- 4 extra on top
pnl:CheckBox( "#utilities.epickupallowed", "sv_playerpickupallowed" )
pnl:CheckBox( "#utilities.falldamage", "mp_falldamage" )
pnl:CheckBox( "#utilities.gmod_suit", "gmod_suit" )
-- Fun convars
pnl:NumSlider( "#utilities.gravity", "sv_gravity", -500, 1000, 0 )
pnl:NumSlider( "#utilities.friction", "sv_friction", 0, 16, 0 )
pnl:NumSlider( "#utilities.timescale", "phys_timescale", 0, 2 )
pnl:NumSlider( "#utilities.deployspeed", "sv_defaultdeployspeed", 0.1, 10 )
pnl:NumSlider( "#utilities.noclipspeed", "sv_noclipspeed", 1, 10, 0 ) -- TODO: Switch this and friction back to Float once the sliders dont reset the convar from 8 to 8.00
pnl:NumSlider( "#utilities.maxammo", "gmod_maxammo", 0, 9999, 0 )
pnl:ControlHelp( "#utilities.maxammo.help" )
pnl:NumSlider( "#utilities.max_ragdolls", "g_ragdoll_maxcount", 0, 128, 0 )
-- Technical convars
pnl:NumSlider( "#utilities.iterations", "gmod_physiterations", 1, 10, 0 )
pnl:NumSlider( "#utilities.sv_timeout", "sv_timeout", 60, 300, 0 )
end
local function SandboxClientSettings( pnl )
pnl:Help( "#utilities.sandboxsettings_cl" )
local ConVarsDefault = {
sbox_search_maxresults = "1024",
cl_drawhud = "1",
gmod_drawhelp = "1",
gmod_drawtooleffects = "1",
cl_drawworldtooltips = "1",
cl_drawspawneffect = "1",
cl_draweffectrings = "1",
cl_drawcameras = "1",
cl_drawthrusterseffects = "1",
cl_showhints = "1",
spawnmenu_toggle = "0",
}
LoadInConvarDefaults( ConVarsDefault )
pnl:ToolPresets( "util_sandbox_cl", ConVarsDefault )
pnl:NumSlider( "#utilities.max_results", "sbox_search_maxresults", 1024, 8192, 0 )
pnl:ControlHelp( "#utilities.max_results.help" )
pnl:CheckBox( "#menubar.drawing.hud", "cl_drawhud" )
pnl:CheckBox( "#menubar.drawing.toolhelp", "gmod_drawhelp" )
pnl:CheckBox( "#menubar.drawing.toolui", "gmod_drawtooleffects" )
pnl:CheckBox( "#menubar.drawing.world_tooltips", "cl_drawworldtooltips" )
pnl:CheckBox( "#menubar.drawing.spawn_effect", "cl_drawspawneffect" )
pnl:CheckBox( "#menubar.drawing.effect_rings", "cl_draweffectrings" )
pnl:CheckBox( "#menubar.drawing.cameras", "cl_drawcameras" )
pnl:CheckBox( "#menubar.drawing.thrusters", "cl_drawthrusterseffects" )
pnl:CheckBox( "#menubar.drawing.hints", "cl_showhints" )
pnl:CheckBox( "#utilities.spawnmenu_toggle", "spawnmenu_toggle" )
pnl:ControlHelp( "#utilities.spawnmenu_toggle.help" ):DockMargin( 32, 4, 32, 8 ) -- 4 extra on top
end
local function SandboxSettings( pnl )
pnl:Help( "#utilities.sandboxsettings" )
local ConVarsDefault = {
sbox_persist = "",
sbox_noclip = "1",
sbox_weapons = "1",
sbox_godmode = "0",
sbox_playershurtplayers = "1",
physgun_limited = "0",
physgun_maxrange = "4096",
sbox_bonemanip_npc = "1",
sbox_bonemanip_player = "0",
sbox_bonemanip_misc = "0"
}
LoadInConvarDefaults( ConVarsDefault )
local ConVarsLimits = {}
for id, str in pairs( cleanup.GetTable() ) do
local cvar = GetConVar( "sbox_max" .. str )
if ( !cvar ) then continue end
ConVarsDefault[ "sbox_max" .. str ] = cvar:GetDefault()
table.insert( ConVarsLimits, {
command = "sbox_max" .. str,
default = cvar:GetDefault(),
label = language.GetPhrase( "max_" .. str ),
min = 0,
max = math.max( 200, cvar:GetDefault() * 1.4 )
} )
end
pnl:ToolPresets( "util_sandbox", ConVarsDefault )
pnl:TextEntry( "#persistent_mode", "sbox_persist" )
pnl:ControlHelp( "#persistent_mode.help" ):DockMargin( 16, 4, 16, 8 )
pnl:CheckBox( "#enable_weapons", "sbox_weapons" )
pnl:CheckBox( "#allow_god_mode", "sbox_godmode" )
pnl:ControlHelp( "#utilities.mp_only" ):DockMargin( 16, 16, 16, 4 )
pnl:CheckBox( "#players_damage_players", "sbox_playershurtplayers" )
pnl:CheckBox( "#allow_noclip", "sbox_noclip" )
pnl:CheckBox( "#bone_manipulate_npcs", "sbox_bonemanip_npc" )
pnl:CheckBox( "#bone_manipulate_players", "sbox_bonemanip_player" )
pnl:CheckBox( "#bone_manipulate_others", "sbox_bonemanip_misc" )
for id, t in SortedPairsByMemberValue( ConVarsLimits, "label" ) do
pnl:NumSlider( t.label, t.command, t.min, t.max, 0 ):SetHeight( 16 ) -- This makes the controls all bunched up like how we want
end
end
local function PhysgunSettings( pnl )
pnl:Help( "#utilities.physgunsettings" )
local ConVarsDefault = {
physgun_halo = "1",
physgun_drawbeams = "1",
effects_freeze = "1",
effects_unfreeze = "1",
gm_snapgrid = "0",
gm_snapangles = "45",
physgun_rotation_sensitivity = "0.05",
physgun_wheelspeed = "10"
}
LoadInConvarDefaults( ConVarsDefault )
pnl:ToolPresets( "util_physgun", ConVarsDefault )
pnl:CheckBox( "#utilities.physgun_halo", "physgun_halo" )
pnl:CheckBox( "#utilities.physgun_drawbeams", "physgun_drawbeams" )
pnl:CheckBox( "#menubar.drawing.freeze", "effects_freeze" )
pnl:CheckBox( "#menubar.drawing.unfreeze", "effects_unfreeze" )
pnl:NumSlider( "#utilities.gm_snapgrid", "gm_snapgrid", 0, 128, 0 )
pnl:NumSlider( "#utilities.gm_snapangles", "gm_snapangles", 5, 90, 0 )
pnl:NumSlider( "#utilities.physgun_rotation_sensitivity", "physgun_rotation_sensitivity", 0.01, 1, 2 )
pnl:NumSlider( "#utilities.physgun_wheelspeed", "physgun_wheelspeed", 0, 50, 0 )
end
local function PhysgunSVSettings( pnl )
pnl:Help( "#utilities.physgunsvsettings" )
local ConVarsDefault = {
physgun_limited = "0",
physgun_maxrange = "4096",
physgun_teleportDistance = "0",
physgun_maxSpeed = "5000",
physgun_maxAngular = "5000",
physgun_timeToArrive = "0.05",
physgun_timeToArriveRagdoll = "0.1"
}
LoadInConvarDefaults( ConVarsDefault )
pnl:ToolPresets( "util_physgun_sv", ConVarsDefault )
pnl:CheckBox( "#utilities.physgun_limited", "physgun_limited" )
pnl:ControlHelp( "#utilities.physgun_limited.help" ):DockMargin( 32, 4, 32, 8 ) -- 4 extra on top
pnl:NumSlider( "#utilities.physgun_maxrange", "physgun_maxrange", 128, 8192, 0 )
pnl:NumSlider( "#utilities.physgun_tpdist", "physgun_teleportdistance", 0, 10000, 0 )
pnl:NumSlider( "#utilities.physgun_maxspeed", "physgun_maxspeed", 0, 10000, 0 )
pnl:NumSlider( "#utilities.physgun_maxangular", "physgun_maxangular", 0, 10000, 0 )
pnl:NumSlider( "#utilities.physgun_timetoarrive", "physgun_timetoarrive", 0, 2, 2 )
pnl:NumSlider( "#utilities.physgun_timetoarriveragdoll", "physgun_timetoarriveragdoll", 0, 2, 2 )
pnl:ControlHelp( "#utilities.physgun_timetoarriveragdoll.help" )
end
local function PlayerOptions( pnl )
pnl:Help( "#smwidget.playermodel_title" )
pnl:Button( "#smwidget.playermodel_title", "open_playermodel_selector" )
end
-- Tool Menu
hook.Add( "PopulateToolMenu", "PopulateUtilityMenus", function()
spawnmenu.AddToolMenuOption( "Utilities", "User", "User_Cleanup", "#spawnmenu.utilities.cleanup", "", "", User_Cleanup )
spawnmenu.AddToolMenuOption( "Utilities", "User", "Undo", "#spawnmenu.utilities.undo", "", "", Undo )
spawnmenu.AddToolMenuOption( "Utilities", "User", "PhysgunSettings", "#spawnmenu.utilities.physgunsettings", "", "", PhysgunSettings )
spawnmenu.AddToolMenuOption( "Utilities", "User", "SandboxClientSettings", "#spawnmenu.utilities.sandbox_settings", "", "", SandboxClientSettings )
spawnmenu.AddToolMenuOption( "Utilities", "User", "PlayerModelSelector", "#smwidget.playermodel_title", "", "", PlayerOptions )
spawnmenu.AddToolMenuOption( "Utilities", "Admin", "Admin_Cleanup", "#spawnmenu.utilities.cleanup", "", "", User_Cleanup )
spawnmenu.AddToolMenuOption( "Utilities", "Admin", "ServerSettings", "#spawnmenu.utilities.server_settings", "", "", ServerSettings )
spawnmenu.AddToolMenuOption( "Utilities", "Admin", "SandboxSettings", "#spawnmenu.utilities.sandbox_settings", "", "", SandboxSettings )
spawnmenu.AddToolMenuOption( "Utilities", "Admin", "PhysgunSVSettings", "#spawnmenu.utilities.physgunsettings", "", "", PhysgunSVSettings )
end )
-- Categories
hook.Add( "AddToolMenuCategories", "CreateUtilitiesCategories", function()
spawnmenu.AddToolCategory( "Utilities", "User", "#spawnmenu.utilities.user" )
spawnmenu.AddToolCategory( "Utilities", "Admin", "#spawnmenu.utilities.admin" )
end )

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lua/bin/libmysql.dll Normal file

Binary file not shown.

245
lua/derma/derma.lua Normal file
View File

@@ -0,0 +1,245 @@
local table = table
local vgui = vgui
local Msg = Msg
local setmetatable = setmetatable
local _G = _G
local gamemode = gamemode
local pairs = pairs
local isfunction = isfunction
module( "derma" )
Controls = {}
SkinList = {}
local DefaultSkin = {}
local SkinMetaTable = {}
local iSkinChangeIndex = 1
SkinMetaTable.__index = function ( self, key )
return DefaultSkin[ key ]
end
local function FindPanelsByClass( SeekingClass )
local outtbl = {}
--
-- Going through the registry is a hacky way to do this.
-- We're only doing it this way because it doesn't matter if it's a
-- bit slow - because this function is only used when reloading.
--
local tbl = vgui.GetAll()
for k, v in pairs( tbl ) do
if ( v.ClassName && v.ClassName == SeekingClass ) then
table.insert( outtbl, v )
end
end
return outtbl
end
--
-- Find all the panels that use this class and
-- if allowed replace the functions with the new ones.
--
local function ReloadClass( classname )
local ctrl = vgui.GetControlTable( classname )
if ( !ctrl ) then return end
local tbl = FindPanelsByClass( classname )
for k, v in pairs ( tbl ) do
if ( !v.AllowAutoRefresh ) then continue end
if ( v.PreAutoRefresh ) then
v:PreAutoRefresh()
end
for name, func in pairs( ctrl ) do
if ( !isfunction( func ) ) then continue end
v[ name ] = func
end
if ( v.PostAutoRefresh ) then
v:PostAutoRefresh()
end
end
end
--[[---------------------------------------------------------
GetControlList
-----------------------------------------------------------]]
function GetControlList()
return Controls
end
--[[---------------------------------------------------------
DefineControl
-----------------------------------------------------------]]
function DefineControl( strName, strDescription, strTable, strBase )
local bReloading = Controls[ strName ] != nil
-- Add Derma table to PANEL table.
strTable.Derma = {
ClassName = strName,
Description = strDescription,
BaseClass = strBase
}
-- Register control with VGUI
vgui.Register( strName, strTable, strBase )
-- Store control
Controls[ strName ] = strTable.Derma
-- Store as a global so controls can 'baseclass' easier
-- TODO: STOP THIS
_G[ strName ] = strTable
if ( bReloading ) then
Msg( "Reloaded Control: ", strName, "\n" )
ReloadClass( strName )
end
return strTable
end
--[[---------------------------------------------------------
DefineSkin
-----------------------------------------------------------]]
function DefineSkin( strName, strDescription, strTable )
strTable.Name = strName
strTable.Description = strDescription
strTable.Base = strTable.Base or "Default"
if ( strName != "Default" ) then
setmetatable( strTable, SkinMetaTable )
else
DefaultSkin = strTable
end
SkinList[ strName ] = strTable
-- Make all panels update their skin
RefreshSkins()
end
--[[---------------------------------------------------------
GetSkin - Returns current skin for panel
-----------------------------------------------------------]]
function GetSkinTable()
return table.Copy( SkinList )
end
--[[---------------------------------------------------------
Returns 'Default' Skin
-----------------------------------------------------------]]
function GetDefaultSkin()
local skin = nil
-- Check gamemode skin preference
if ( gamemode ) then
local skinname = gamemode.Call( "ForceDermaSkin" )
if ( skinname ) then skin = GetNamedSkin( skinname ) end
end
-- default
if ( !skin ) then skin = DefaultSkin end
return skin
end
--[[---------------------------------------------------------
Returns 'Named' Skin
-----------------------------------------------------------]]
function GetNamedSkin( name )
return SkinList[ name ]
end
--[[---------------------------------------------------------
SkinHook( strType, strName, panel )
-----------------------------------------------------------]]
function SkinHook( strType, strName, panel, w, h )
local Skin = panel:GetSkin()
if ( !Skin ) then return end
local func = Skin[ strType .. strName ]
if ( !func ) then return end
return func( Skin, panel, w, h )
end
--[[---------------------------------------------------------
SkinTexture( strName, panel, default )
-----------------------------------------------------------]]
function SkinTexture( strName, panel, default )
local Skin = panel:GetSkin()
if ( !Skin ) then return default end
local Textures = Skin.tex
if ( !Textures ) then return default end
return Textures[ strName ] or default
end
--[[---------------------------------------------------------
Color( strName, panel, default )
-----------------------------------------------------------]]
function Color( strName, panel, default )
local Skin = panel:GetSkin()
if ( !Skin ) then return default end
return Skin[ strName ] or default
end
--[[---------------------------------------------------------
SkinChangeIndex
-----------------------------------------------------------]]
function SkinChangeIndex()
return iSkinChangeIndex
end
--[[---------------------------------------------------------
RefreshSkins - clears all cache'd panels (so they will reassess which skin they should be using)
-----------------------------------------------------------]]
function RefreshSkins()
iSkinChangeIndex = iSkinChangeIndex + 1
end

View File

@@ -0,0 +1,70 @@
local DermaAnimation = {}
DermaAnimation.__index = DermaAnimation
function DermaAnimation:Run()
if ( !self.Running ) then return end
local time = SysTime()
local delta = ( time - self.StartTime ) / self.Length
delta = delta ^ ( 1.0 - ( delta - 0.5 ) )
-- If we have ended we run once more with the Finished member set
-- This allows us to clean up in the same function..
if ( time > self.EndTime ) then
self.Finished = true
self.Running = nil
delta = 1
end
self.Func( self.Panel, self, delta, self.Data )
self.Started = nil
end
function DermaAnimation:Start( Length, Data )
if ( self.Length == 0 ) then return end
self.Running = true
self.Started = true
self.Finished = nil
self.Length = Length
self.StartTime = SysTime()
self.EndTime = SysTime() + Length
self.Data = Data
end
function DermaAnimation:Stop()
if ( !self.Running ) then return end
self.Finished = true
self.Running = nil
end
function DermaAnimation:Active()
return self.Running
end
function Derma_Anim( name, panel, func )
local anim = {}
anim.Name = name
anim.Panel = panel
anim.Func = func
setmetatable( anim, DermaAnimation )
return anim
end

View File

@@ -0,0 +1,58 @@
local PANEL = {}
function PANEL:Init()
self:SetTitle( "Derma Initiative Control Test" )
self.ContentPanel = vgui.Create( "DPropertySheet", self )
self.ContentPanel:Dock( FILL )
self:InvalidateLayout( true )
local w, h = self:GetSize()
local Controls = table.Copy( derma.GetControlList() )
for key, ctrl in SortedPairs( Controls ) do
local Ctrls = _G[ key ]
if ( Ctrls && Ctrls.GenerateExample ) then
Ctrls:GenerateExample( key, self.ContentPanel, w, h )
end
end
self:SetSize( 600, 450 )
end
function PANEL:SwitchTo( name )
self.ContentPanel:SwitchToName( name )
end
local vguiExampleWindow = vgui.RegisterTable( PANEL, "DFrame" )
--
-- This is all to open the actual window via concommand
--
local DermaExample = nil
local DermaControlsSuffix = ""
if ( MENU_DLL ) then -- Not all controls are available in menu state
DermaControlsSuffix = "_menu"
end
concommand.Add( "derma_controls" .. DermaControlsSuffix, function( player, command, arguments, args )
if ( IsValid( DermaExample ) ) then
DermaExample:Remove()
return end
DermaExample = vgui.CreateFromTable( vguiExampleWindow )
DermaExample:SwitchTo( args )
DermaExample:MakePopup()
DermaExample:Center()
end, nil, "", { FCVAR_DONTRECORD } )

229
lua/derma/derma_gwen.lua Normal file
View File

@@ -0,0 +1,229 @@
local meta = FindMetaTable( "Panel" )
GWEN = {}
function GWEN.CreateTextureBorder( _xo, _yo, _wo, _ho, l, t, r, b, material_override )
local mat = SKIN && SKIN.GwenTexture || material_override
if ( material_override && !material_override:IsError() ) then mat = material_override end
return function( x, y, w, h, col )
local tex = mat:GetTexture( "$basetexture" )
local texW, texH = tex:Width(), tex:Height()
local _x = _xo / texW
local _y = _yo / texH
local _w = _wo / texW
local _h = _ho / texH
surface.SetMaterial( mat )
if ( col ) then
surface.SetDrawColor( col.r, col.g, col.b, col.a )
else
surface.SetDrawColor( 255, 255, 255, 255 )
end
local halfW, halfH = w / 2, h / 2
local left = math.min( l, math.ceil( halfW ) )
local right = math.min( r, math.floor( halfW ) )
local top = math.min( t, math.ceil( halfH ) )
local bottom = math.min( b, math.floor( halfH ) )
local _l = left / texW
local _t = top / texH
local _r = right / texW
local _b = bottom / texH
-- top
surface.DrawTexturedRectUV( x, y, left, top, _x, _y, _x + _l, _y + _t )
surface.DrawTexturedRectUV( x + left, y, w - left - right, top, _x + _l, _y, _x + _w - _r, _y + _t )
surface.DrawTexturedRectUV( x + w - right, y, right, top, _x + _w - _r, _y, _x + _w, _y + _t )
-- middle
surface.DrawTexturedRectUV( x, y + top, left, h - top - bottom, _x, _y + _t, _x + _l, _y + _h - _b )
surface.DrawTexturedRectUV( x + left, y + top, w - left - right, h - top - bottom, _x + _l, _y + _t, _x + _w - _r, _y + _h - _b )
surface.DrawTexturedRectUV( x + w - right, y + top, right, h - top - bottom, _x + _w - _r, _y + _t, _x + _w, _y + _h - _b )
-- bottom
surface.DrawTexturedRectUV( x, y + h - bottom, left, bottom, _x, _y + _h - _b, _x + _l, _y + _h )
surface.DrawTexturedRectUV( x + left, y + h - bottom, w - left - right, bottom, _x + _l, _y + _h - _b, _x + _w - _r, _y + _h )
surface.DrawTexturedRectUV( x + w - right, y + h - bottom, right, bottom, _x + _w - _r, _y + _h - _b, _x + _w, _y + _h )
end
end
function GWEN.CreateTextureNormal( _xo, _yo, _wo, _ho, material_override )
local mat = SKIN && SKIN.GwenTexture || material_override
if ( material_override && !material_override:IsError() ) then mat = material_override end
return function( x, y, w, h, col )
local tex = mat:GetTexture( "$basetexture" )
local texW, texH = tex:Width(), tex:Height()
local _x = _xo / texW
local _y = _yo / texH
local _w = _wo / texW
local _h = _ho / texH
surface.SetMaterial( mat )
if ( col ) then
surface.SetDrawColor( col.r, col.g, col.b, col.a )
else
surface.SetDrawColor( 255, 255, 255, 255 )
end
surface.DrawTexturedRectUV( x, y, w, h, _x, _y, _x + _w, _y + _h )
end
end
function GWEN.CreateTextureCentered( _xo, _yo, _wo, _ho, material_override )
local mat = SKIN && SKIN.GwenTexture || material_override
if ( material_override && !material_override:IsError() ) then mat = material_override end
return function( x, y, w, h, col )
local tex = mat:GetTexture( "$basetexture" )
local texW, texH = tex:Width(), tex:Height()
local _x = _xo / texW
local _y = _yo / texH
local _w = _wo / texW
local _h = _ho / texH
x = x + ( w - _wo ) * 0.5
y = y + ( h - _ho ) * 0.5
w = _wo
h = _ho
surface.SetMaterial( mat )
if ( col ) then
surface.SetDrawColor( col.r, col.g, col.b, col.a )
else
surface.SetDrawColor( 255, 255, 255, 255 )
end
surface.DrawTexturedRectUV( x, y, w, h, _x, _y, _x + _w, _y + _h )
end
end
function GWEN.TextureColor( x, y, material_override )
local mat = SKIN && SKIN.GwenTexture || material_override
if ( material_override && !material_override:IsError() ) then mat = material_override end
return mat:GetColor( x, y )
end
--
-- Loads a .gwen file (created by GWEN Designer)
-- TODO: Cache - but check for file changes?
--
function meta:LoadGWENFile( filename, path )
local contents = file.Read( filename, path || "GAME" )
if ( !contents ) then return end
self:LoadGWENString( contents )
end
--
-- Load from String
--
function meta:LoadGWENString( str )
local tbl = util.JSONToTable( str )
if ( !tbl ) then return end
if ( !tbl.Controls ) then return end
self:ApplyGWEN( tbl.Controls )
end
--
-- Convert GWEN types into Derma Types
--
local GwenTypes = {
Base = "Panel",
Button = "DButton",
Label = "DLabel",
TextBox = "DTextEntry",
TextBoxMultiline = "DTextEntry",
ComboBox = "DComboBox",
HorizontalSlider = "Slider",
ImagePanel = "DImage",
CheckBoxWithLabel = "DCheckBoxLabel"
}
--
-- Apply a GWEN table to the control (should contain Properties and (optionally) Children subtables)
--
function meta:ApplyGWEN( tbl )
if ( tbl.Type == "TextBoxMultiline" ) then self:SetMultiline( true ) end
for k, v in pairs( tbl.Properties ) do
if ( self[ "GWEN_Set" .. k ] ) then
self[ "GWEN_Set" .. k ]( self, v )
end
end
if ( !tbl.Children ) then return end
for k, v in pairs( tbl.Children ) do
local type = GwenTypes[ v.Type ]
if ( type ) then
local pnl = self:Add( type )
pnl:ApplyGWEN( v )
else
MsgN( "Warning: No GWEN Panel Type ", v.Type )
end
end
end
--
-- SET properties
--
function meta:GWEN_SetPosition( tbl ) self:SetPos( tbl.x, tbl.y ) end
function meta:GWEN_SetSize( tbl ) self:SetSize( tbl.w, tbl.h ) end
function meta:GWEN_SetText( tbl ) self:SetText( tbl ) end
function meta:GWEN_SetControlName( tbl ) self:SetName( tbl ) end
function meta:GWEN_SetMargin( tbl ) self:DockMargin( tbl.left, tbl.top, tbl.right, tbl.bottom ) end
function meta:GWEN_SetMin( min ) self:SetMin( tonumber( min ) ) end
function meta:GWEN_SetMax( max ) self:SetMax( tonumber( max ) ) end
function meta:GWEN_SetHorizontalAlign( txt )
if ( txt == "Right" ) then self:SetContentAlignment( 6 ) end
if ( txt == "Center" ) then self:SetContentAlignment( 5 ) end
if ( txt == "Left" ) then self:SetContentAlignment( 4 ) end
end
function meta:GWEN_SetDock( tbl )
if ( tbl == "Right" ) then self:Dock( RIGHT ) end
if ( tbl == "Left" ) then self:Dock( LEFT ) end
if ( tbl == "Bottom" ) then self:Dock( BOTTOM ) end
if ( tbl == "Top" ) then self:Dock( TOP ) end
if ( tbl == "Fill" ) then self:Dock( FILL ) end
end
function meta:GWEN_SetCheckboxText( tbl ) self:SetText( tbl ) end

59
lua/derma/derma_menus.lua Normal file
View File

@@ -0,0 +1,59 @@
local tblOpenMenus = {}
function RegisterDermaMenuForClose( dmenu )
table.insert( tblOpenMenus, dmenu )
end
function DermaMenu( parentmenu, parent )
if ( !parentmenu ) then CloseDermaMenus() end
local dmenu = vgui.Create( "DMenu", parent )
return dmenu
end
function CloseDermaMenus()
for k, dmenu in pairs( tblOpenMenus ) do
if ( IsValid( dmenu ) ) then
dmenu:SetVisible( false )
if ( dmenu:GetDeleteSelf() ) then
dmenu:Remove()
end
end
end
tblOpenMenus = {}
hook.Run( "CloseDermaMenus" )
end
--
-- Here we detect when the mouse is pressed on another panel
-- This allows us to close the menus.
--
local function DermaDetectMenuFocus( panel, mousecode )
if ( IsValid( panel ) ) then
if ( panel.m_bIsMenuComponent ) then return end
-- Is the parent a menu?
return DermaDetectMenuFocus( panel:GetParent(), mousecode )
end
CloseDermaMenus()
end
hook.Add( "VGUIMousePressed", "DermaDetectMenuFocus", DermaDetectMenuFocus )

264
lua/derma/derma_utils.lua Normal file
View File

@@ -0,0 +1,264 @@
local matBlurScreen = Material( "pp/blurscreen" )
--[[
This is designed for Paint functions..
--]]
function Derma_DrawBackgroundBlur( panel, starttime )
local Fraction = 1
if ( starttime ) then
Fraction = math.Clamp( ( SysTime() - starttime ) / 1, 0, 1 )
end
local x, y = panel:LocalToScreen( 0, 0 )
local wasEnabled = DisableClipping( true )
-- Menu cannot do blur
if ( !MENU_DLL ) then
surface.SetMaterial( matBlurScreen )
surface.SetDrawColor( 255, 255, 255, 255 )
for i = 0.33, 1, 0.33 do
matBlurScreen:SetFloat( "$blur", Fraction * 5 * i )
matBlurScreen:Recompute()
if ( render ) then render.UpdateScreenEffectTexture() end -- Todo: Make this available to menu Lua
surface.DrawTexturedRect( x * -1, y * -1, ScrW(), ScrH() )
end
end
surface.SetDrawColor( 10, 10, 10, 200 * Fraction )
surface.DrawRect( x * -1, y * -1, ScrW(), ScrH() )
DisableClipping( wasEnabled )
end
--[[
Display a simple message box.
Derma_Message( "Hey Some Text Here!!!", "Message Title (Optional)", "Button Text (Optional)" )
--]]
function Derma_Message( strText, strTitle, strButtonText )
local Window = vgui.Create( "DFrame" )
Window:SetTitle( strTitle or "Message" )
Window:SetDraggable( false )
Window:ShowCloseButton( false )
Window:SetBackgroundBlur( true )
Window:SetDrawOnTop( true )
local InnerPanel = vgui.Create( "Panel", Window )
local Text = vgui.Create( "DLabel", InnerPanel )
Text:SetText( strText or "Message Text" )
Text:SizeToContents()
Text:SetContentAlignment( 5 )
Text:SetTextColor( color_white )
local ButtonPanel = vgui.Create( "DPanel", Window )
ButtonPanel:SetTall( 30 )
ButtonPanel:SetPaintBackground( false )
local Button = vgui.Create( "DButton", ButtonPanel )
Button:SetText( strButtonText or "OK" )
Button:SizeToContents()
Button:SetTall( 20 )
Button:SetWide( Button:GetWide() + 20 )
Button:SetPos( 5, 5 )
Button.DoClick = function() Window:Close() end
ButtonPanel:SetWide( Button:GetWide() + 10 )
local w, h = Text:GetSize()
Window:SetSize( w + 50, h + 25 + 45 + 10 )
Window:Center()
InnerPanel:StretchToParent( 5, 25, 5, 45 )
Text:StretchToParent( 5, 5, 5, 5 )
ButtonPanel:CenterHorizontal()
ButtonPanel:AlignBottom( 8 )
Window:MakePopup()
Window:DoModal()
return Window
end
--[[
Ask a question with multiple answers..
Derma_Query( "Would you like me to punch you right in the face?", "Question!",
"Yesss", function() MsgN( "Pressed YES!") end,
"Nope!", function() MsgN( "Pressed Nope!") end,
"Cancel", function() MsgN( "Cancelled!") end )
--]]
function Derma_Query( strText, strTitle, ... )
local Window = vgui.Create( "DFrame" )
Window:SetTitle( strTitle or "Message Title (First Parameter)" )
Window:SetDraggable( false )
Window:ShowCloseButton( false )
Window:SetBackgroundBlur( true )
Window:SetDrawOnTop( true )
local InnerPanel = vgui.Create( "DPanel", Window )
InnerPanel:SetPaintBackground( false )
local Text = vgui.Create( "DLabel", InnerPanel )
Text:SetText( strText or "Message Text (Second Parameter)" )
Text:SizeToContents()
Text:SetContentAlignment( 5 )
Text:SetTextColor( color_white )
local ButtonPanel = vgui.Create( "DPanel", Window )
ButtonPanel:SetTall( 30 )
ButtonPanel:SetPaintBackground( false )
-- Loop through all the options and create buttons for them.
local NumOptions = 0
local x = 5
for k = 1, 8, 2 do
local txt = select( k, ... )
if ( txt == nil ) then break end
local Func = select( k + 1, ... ) or function() end
local Button = vgui.Create( "DButton", ButtonPanel )
Button:SetText( txt )
Button:SizeToContents()
Button:SetTall( 20 )
Button:SetWide( Button:GetWide() + 20 )
Button.DoClick = function() Window:Close() Func() end
Button:SetPos( x, 5 )
x = x + Button:GetWide() + 5
ButtonPanel:SetWide( x )
NumOptions = NumOptions + 1
end
local w, h = Text:GetSize()
w = math.max( w, ButtonPanel:GetWide() )
Window:SetSize( w + 50, h + 25 + 45 + 10 )
Window:Center()
InnerPanel:StretchToParent( 5, 25, 5, 45 )
Text:StretchToParent( 5, 5, 5, 5 )
ButtonPanel:CenterHorizontal()
ButtonPanel:AlignBottom( 8 )
Window:MakePopup()
Window:DoModal()
if ( NumOptions == 0 ) then
Window:Close()
Error( "Derma_Query: Created Query with no Options!?" )
return nil
end
return Window
end
--[[
Request a string from the user
Derma_StringRequest( "Question",
"What Is Your Favourite Color?",
"Type your answer here!",
function( strTextOut ) Derma_Message( "Your Favourite Color Is: " .. strTextOut ) end,
function( strTextOut ) Derma_Message( "You pressed Cancel!" ) end,
"Okey Dokey",
"Cancel" )
--]]
function Derma_StringRequest( strTitle, strText, strDefaultText, fnEnter, fnCancel, strButtonText, strButtonCancelText )
local Window = vgui.Create( "DFrame" )
Window:SetTitle( strTitle or "Message Title (First Parameter)" )
Window:SetDraggable( false )
Window:ShowCloseButton( false )
Window:SetBackgroundBlur( true )
Window:SetDrawOnTop( true )
local InnerPanel = vgui.Create( "DPanel", Window )
InnerPanel:SetPaintBackground( false )
local Text = vgui.Create( "DLabel", InnerPanel )
Text:SetText( strText or "Message Text (Second Parameter)" )
Text:SizeToContents()
Text:SetContentAlignment( 5 )
Text:SetTextColor( color_white )
local TextEntry = vgui.Create( "DTextEntry", InnerPanel )
TextEntry:SetText( strDefaultText or "" )
TextEntry.OnEnter = function() Window:Close() fnEnter( TextEntry:GetValue() ) end
local ButtonPanel = vgui.Create( "DPanel", Window )
ButtonPanel:SetTall( 30 )
ButtonPanel:SetPaintBackground( false )
local Button = vgui.Create( "DButton", ButtonPanel )
Button:SetText( strButtonText or "OK" )
Button:SizeToContents()
Button:SetTall( 20 )
Button:SetWide( Button:GetWide() + 20 )
Button:SetPos( 5, 5 )
Button.DoClick = function() Window:Close() fnEnter( TextEntry:GetValue() ) end
local ButtonCancel = vgui.Create( "DButton", ButtonPanel )
ButtonCancel:SetText( strButtonCancelText or "Cancel" )
ButtonCancel:SizeToContents()
ButtonCancel:SetTall( 20 )
ButtonCancel:SetWide( Button:GetWide() + 20 )
ButtonCancel:SetPos( 5, 5 )
ButtonCancel.DoClick = function() Window:Close() if ( fnCancel ) then fnCancel( TextEntry:GetValue() ) end end
ButtonCancel:MoveRightOf( Button, 5 )
ButtonPanel:SetWide( Button:GetWide() + 5 + ButtonCancel:GetWide() + 10 )
local w, h = Text:GetSize()
w = math.max( w, 400 )
Window:SetSize( w + 50, h + 25 + 75 + 10 )
Window:Center()
InnerPanel:StretchToParent( 5, 25, 5, 45 )
Text:StretchToParent( 5, 5, 5, 35 )
TextEntry:StretchToParent( 5, nil, 5, nil )
TextEntry:AlignBottom( 5 )
TextEntry:RequestFocus()
TextEntry:SelectAllText( true )
ButtonPanel:CenterHorizontal()
ButtonPanel:AlignBottom( 8 )
Window:MakePopup()
Window:DoModal()
return Window
end

121
lua/derma/init.lua Normal file
View File

@@ -0,0 +1,121 @@
--
-- The default font used by everything Derma
--
if ( system.IsLinux() ) then
surface.CreateFont( "DermaDefault", {
font = "DejaVu Sans",
size = 14,
weight = 500,
extended = true
} )
surface.CreateFont( "DermaDefaultBold", {
font = "DejaVu Sans",
size = 14,
weight = 800,
extended = true
} )
else
surface.CreateFont( "DermaDefault", {
font = "Tahoma",
size = 13,
weight = 500,
extended = true
} )
surface.CreateFont( "DermaDefaultBold", {
font = "Tahoma",
size = 13,
weight = 800,
extended = true
} )
end
surface.CreateFont( "DermaLarge", {
font = "Roboto",
size = 32,
weight = 500,
extended = true
} )
include( "derma.lua" )
include( "derma_example.lua" )
include( "derma_menus.lua" )
include( "derma_animation.lua" )
include( "derma_utils.lua" )
include( "derma_gwen.lua" )
function Derma_Hook( panel, functionname, hookname, typename )
panel[ functionname ] = function ( self, a, b, c, d )
return derma.SkinHook( hookname, typename, self, a, b, c, d )
end
end
--[[
ConVar Functions
To associate controls with convars. The controls automatically
update from the value of the control, and automatically update
the value of the convar from the control.
Controls must:
Call ConVarStringThink or ConVarNumberThink from the
Think function to get any changes from the ConVars.
Have SetValue( value ) implemented, to receive the
value.
--]]
function Derma_Install_Convar_Functions( PANEL )
function PANEL:SetConVar( strConVar )
self.m_strConVar = strConVar
end
function PANEL:ConVarChanged( strNewValue )
if ( !self.m_strConVar || #self.m_strConVar < 2 ) then return end
RunConsoleCommand( self.m_strConVar, tostring( strNewValue ) )
end
-- Todo: Think only every 0.1 seconds?
function PANEL:ConVarStringThink()
if ( !self.m_strConVar || #self.m_strConVar < 2 ) then return end
local strValue = GetConVarString( self.m_strConVar )
if ( self.m_strConVarValue == strValue ) then return end
self.m_strConVarValue = strValue
self:SetValue( self.m_strConVarValue )
end
function PANEL:ConVarNumberThink()
if ( !self.m_strConVar || #self.m_strConVar < 2 ) then return end
local numValue = GetConVarNumber( self.m_strConVar )
-- In case the convar is a "nan"
if ( numValue != numValue ) then return end
if ( self.m_strConVarValue == numValue ) then return end
self.m_strConVarValue = numValue
self:SetValue( self.m_strConVarValue )
end
end

83
lua/drive/drive_base.lua Normal file
View File

@@ -0,0 +1,83 @@
AddCSLuaFile()
drive.Register( "drive_base",
{
--
-- You should override these :)
-- see drive_noclip.lua for help
--
Init = function( self, cmd ) end,
SetupControls = function( self, cmd ) end,
StartMove = function( self, mv, cmd ) end,
Move = function( self, mv ) end,
FinishMove = function( self, mv ) end,
CalcView = function( self, view ) end,
--
-- Utility methods
--
--
-- Call this in your drive method at
-- any point to stop driving.
--
Stop = function( self )
self.StopDriving = true
end,
--
-- A generic thirdperson view
--
-- > view - the view passed into CalcView
-- > dist - the ideal distance from the center
-- > hullsize - the size of the hull to trace so we don't go through walls (0 for no trace)
-- > entityfilter - usually the self.Entity - so our trace doesn't hit the entity in question
--
CalcView_ThirdPerson = function( self, view, dist, hullsize, entityfilter )
--
-- > Get the current position (teh center of teh entity)
-- > Move the view backwards the size of the entity
--
local neworigin = view.origin - self.Player:EyeAngles():Forward() * dist
if ( hullsize && hullsize > 0 ) then
--
-- > Trace a hull (cube) from the old eye position to the new
--
local tr = util.TraceHull( {
start = view.origin,
endpos = neworigin,
mins = Vector( hullsize, hullsize, hullsize ) * -1,
maxs = Vector( hullsize, hullsize, hullsize ),
filter = entityfilter
} )
--
-- > If we hit something then stop there
-- [ stops the camera going through walls ]
--
if ( tr.Hit ) then
neworigin = tr.HitPos
end
end
--
-- Set our calculated origin
--
view.origin = neworigin
--
-- Set the angles to our view angles (not the entities eye angles)
--
view.angles = self.Player:EyeAngles()
end
} )

126
lua/drive/drive_noclip.lua Normal file
View File

@@ -0,0 +1,126 @@
AddCSLuaFile()
--
-- Note that this just controls what BaseClass provides.
-- If you're changing the base scroll to the bottom of the file too.
--
DEFINE_BASECLASS( "drive_base" )
drive.Register( "drive_noclip",
{
--
-- Called before each move. You should use your entity and cmd to
-- fill mv with information you need for your move.
--
StartMove = function( self, mv, cmd )
--
-- Use (E) was pressed - stop it.
--
if ( mv:KeyReleased( IN_USE ) ) then
self:Stop()
end
--
-- Update move position and velocity from our entity
--
mv:SetOrigin( self.Entity:GetNetworkOrigin() )
mv:SetVelocity( self.Entity:GetAbsVelocity() )
end,
--
-- Runs the actual move. On the client when there's
-- prediction errors this can be run multiple times.
-- You should try to only change mv.
--
Move = function( self, mv )
--
-- Set up a speed, go faster if shift is held down
--
local speed = 1
if ( mv:KeyDown( IN_SPEED ) ) then speed = 3 end
if ( mv:KeyDown( IN_DUCK ) ) then speed = 0.1 end
-- Simulate noclip's action when holding space
if ( mv:KeyDown( IN_JUMP ) ) then mv:SetUpSpeed( 10000 ) end
-- Doesn't work correctly
--[[if ( mv:KeyDown( IN_RELOAD ) ) then
local ang = mv:GetMoveAngles()
ang.r = 0
mv:SetMoveAngles( ang )
end]]
--
-- Get information from the movedata
--
local ang = mv:GetMoveAngles()
local pos = mv:GetOrigin()
local vel = mv:GetVelocity()
--
-- Calculate our velocity
--
local accel = speed * FrameTime() * 0.3
vel = vel + ang:Forward() * mv:GetForwardSpeed() * accel
vel = vel + ang:Right() * mv:GetSideSpeed() * accel
vel = vel + ang:Up() * mv:GetUpSpeed() * accel
local maxSpeed = 400 * speed
if ( vel:Length() > maxSpeed ) then
vel = vel:GetNormalized() * maxSpeed
end
--
-- Apply friction when we are not trying to move
--
if ( math.abs( mv:GetForwardSpeed() ) + math.abs( mv:GetSideSpeed() ) + math.abs( mv:GetUpSpeed() ) < 0.1 ) then
vel = vel * 0.86
end
--
-- Add the velocity to the position (this is the movement)
--
pos = pos + vel * FrameTime()
--
-- We don't set the newly calculated values on the entity itself
-- we instead store them in the movedata. These get applied in FinishMove.
--
mv:SetVelocity( vel )
mv:SetOrigin( pos )
end,
--
-- The move is finished. Use mv to set the new positions
-- on your entities/players.
--
FinishMove = function( self, mv )
--
-- Update our entity!
--
self.Entity:SetNetworkOrigin( mv:GetOrigin() )
self.Entity:SetAbsVelocity( mv:GetVelocity() )
self.Entity:SetAngles( mv:GetMoveAngles() )
--
-- If we have a physics object update that too. But only on the server.
--
if ( SERVER && IsValid( self.Entity:GetPhysicsObject() ) ) then
self.Entity:GetPhysicsObject():EnableMotion( true )
self.Entity:GetPhysicsObject():SetPos( mv:GetOrigin() )
self.Entity:GetPhysicsObject():Wake()
self.Entity:GetPhysicsObject():EnableMotion( false )
end
end
}, "drive_base" )

213
lua/drive/drive_sandbox.lua Normal file
View File

@@ -0,0 +1,213 @@
AddCSLuaFile()
--
--
-- This is the default drive type for when you right click -> drive in sandbox
--
--
DEFINE_BASECLASS( "drive_base" )
drive.Register( "drive_sandbox",
{
--
-- Called on creation
--
Init = function( self )
self.CameraDist = 4
self.CameraDistVel = 0.1
end,
--
-- Calculates the view when driving the entity
--
CalcView = function( self, view )
--
-- Use the utility method on drive_base.lua to give us a 3rd person view
--
local idealdist = math.max( 10, self.Entity:BoundingRadius() ) * self.CameraDist
self:CalcView_ThirdPerson( view, idealdist, 2, { self.Entity } )
view.angles.roll = 0
end,
SetupControls = function( self, cmd )
--
-- If we're holding the reload key down then freeze the view angles
--
if ( cmd:KeyDown( IN_RELOAD ) ) then
self.CameraForceViewAngles = self.CameraForceViewAngles or cmd:GetViewAngles()
cmd:SetViewAngles( self.CameraForceViewAngles )
else
self.CameraForceViewAngles = nil
end
--
-- Zoom out when we use the mouse wheel (this is completely clientside, so it's ok to use a lua var!!)
--
self.CameraDistVel = self.CameraDistVel + cmd:GetMouseWheel() * -0.5
self.CameraDist = self.CameraDist + self.CameraDistVel * FrameTime()
self.CameraDist = math.Clamp( self.CameraDist, 2, 20 )
self.CameraDistVel = math.Approach( self.CameraDistVel, 0, self.CameraDistVel * FrameTime() * 2 )
end,
--
-- Called before each move. You should use your entity and cmd to
-- fill mv with information you need for your move.
--
StartMove = function( self, mv, cmd )
--
-- Set the observer mode to chase so that the entity is drawn
--
self.Player:SetObserverMode( OBS_MODE_CHASE )
--
-- Use (E) was pressed - stop it.
--
if ( mv:KeyReleased( IN_USE ) ) then
self:Stop()
end
--
-- Update move position and velocity from our entity
--
mv:SetOrigin( self.Entity:GetNetworkOrigin() )
mv:SetVelocity( self.Entity:GetAbsVelocity() )
mv:SetMoveAngles( mv:GetAngles() ) -- Always move relative to the player's eyes
local entity_angle = mv:GetAngles()
entity_angle.roll = self.Entity:GetAngles().roll
--
-- Right mouse button is down, don't change the angle of the object
--
if ( mv:KeyDown( IN_ATTACK2 ) or mv:KeyReleased( IN_ATTACK2 ) ) then
entity_angle = self.Entity:GetAngles()
end
--
-- If reload is down then spin the object around
--
if ( mv:KeyDown( IN_RELOAD ) ) then
entity_angle.roll = entity_angle.roll + cmd:GetMouseX() * 0.01
end
--
-- Right mouse button was released
--
if ( mv:KeyReleased( IN_ATTACK2 ) ) then
self.Player:SetEyeAngles( self.Entity:GetAngles() )
end
mv:SetAngles( entity_angle )
end,
--
-- Runs the actual move. On the client when there's
-- prediction errors this can be run multiple times.
-- You should try to only change mv.
--
Move = function( self, mv )
--
-- Set up a speed, go faster if shift is held down
--
local speed = 0.0005 * FrameTime()
if ( mv:KeyDown( IN_SPEED ) ) then speed = 0.005 * FrameTime() end
if ( mv:KeyDown( IN_DUCK ) ) then speed = 0.00005 * FrameTime() end
-- Simulate noclip's action when holding space
if ( mv:KeyDown( IN_JUMP ) ) then mv:SetUpSpeed( 10000 ) end
--
-- Get information from the movedata
--
local ang = mv:GetMoveAngles()
local pos = mv:GetOrigin()
local vel = mv:GetVelocity()
-- Cancel out the roll
ang.roll = 0
--
-- Add velocities. This can seem complicated. On the first line
-- we're basically saying get the forward vector, then multiply it
-- by our forward speed (which will be > 0 if we're holding W, < 0 if we're
-- holding S and 0 if we're holding neither) - and add that to velocity.
-- We do that for right and up too, which gives us our free movement.
--
vel = vel + ang:Forward() * mv:GetForwardSpeed() * speed
vel = vel + ang:Right() * mv:GetSideSpeed() * speed
vel = vel + ang:Up() * mv:GetUpSpeed() * speed
--
-- We don't want our velocity to get out of hand so we apply
-- a little bit of air resistance. If no keys are down we apply
-- more resistance so we slow down more.
--
if ( math.abs( mv:GetForwardSpeed() ) + math.abs( mv:GetSideSpeed() ) + math.abs( mv:GetUpSpeed() ) < 0.1 ) then
vel = vel * 0.90
else
vel = vel * 0.99
end
--
-- Add the velocity to the position (this is the movement)
--
pos = pos + vel
--
-- We don't set the newly calculated values on the entity itself
-- we instead store them in the movedata. These get applied in FinishMove.
--
mv:SetVelocity( vel )
mv:SetOrigin( pos )
end,
--
-- The move is finished. Use mv to set the new positions
-- on your entities/players.
--
FinishMove = function( self, mv )
--
-- Update our entity!
--
self.Entity:SetNetworkOrigin( mv:GetOrigin() )
self.Entity:SetAbsVelocity( mv:GetVelocity() )
self.Entity:SetAngles( mv:GetAngles() )
--
-- If we have a physics object update that too. But only on the server.
--
if ( SERVER && IsValid( self.Entity:GetPhysicsObject() ) ) then
self.Entity:GetPhysicsObject():EnableMotion( true )
self.Entity:GetPhysicsObject():SetPos( mv:GetOrigin() )
self.Entity:GetPhysicsObject():Wake()
self.Entity:GetPhysicsObject():EnableMotion( false )
end
end
}, "drive_base" )

188
lua/entities/sent_ball.lua Normal file
View File

@@ -0,0 +1,188 @@
AddCSLuaFile()
DEFINE_BASECLASS( "base_anim" )
ENT.PrintName = "#sent_ball"
ENT.Author = "Garry Newman"
ENT.Information = "An edible bouncy ball. Press USE on the bouncy ball to eat it."
ENT.Category = "#spawnmenu.category.fun_games"
ENT.Editable = true
ENT.Spawnable = true
ENT.AdminOnly = false
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.MinSize = 4
ENT.MaxSize = 128
function ENT:SetupDataTables()
self:NetworkVar( "Float", 0, "BallSize", { KeyName = "ballsize", Edit = { type = "Float", min = self.MinSize, max = self.MaxSize, order = 1 } } )
self:NetworkVar( "Vector", 0, "BallColor", { KeyName = "ballcolor", Edit = { type = "VectorColor", order = 2 } } )
if ( SERVER ) then
self:NetworkVarNotify( "BallSize", self.OnBallSizeChanged )
end
end
-- This is the spawn function. It's called when a client calls the entity to be spawned.
-- If you want to make your SENT spawnable you need one of these functions to properly create the entity
--
-- ply is the name of the player that is spawning it
-- tr is the trace from the player's eyes
--
function ENT:SpawnFunction( ply, tr, ClassName )
if ( !tr.Hit ) then return end
local size = math.random( 16, 48 )
local SpawnPos = tr.HitPos + tr.HitNormal * size
-- Make sure the spawn position is not out of bounds
local oobTr = util.TraceLine( {
start = tr.HitPos,
endpos = SpawnPos,
mask = MASK_SOLID_BRUSHONLY
} )
if ( oobTr.Hit ) then
SpawnPos = oobTr.HitPos + oobTr.HitNormal * ( tr.HitPos:Distance( oobTr.HitPos ) / 2 )
end
local ent = ents.Create( ClassName )
ent:SetPos( SpawnPos )
ent:SetBallSize( size )
ent:Spawn()
ent:Activate()
return ent
end
function ENT:Initialize()
-- We do NOT want to execute anything below in this FUNCTION on CLIENT
if ( CLIENT ) then return end
-- Use the helibomb model just for the shadow (because it's about the same size)
self:SetModel( "models/Combine_Helicopter/helicopter_bomb01.mdl" )
-- We will put this here just in case, even though it should be called from OnBallSizeChanged in any case
self:RebuildPhysics()
-- Set the size if it wasn't set already..
if ( self:GetBallSize() == 0 ) then self:SetBallSize( 32 ) end
-- Select a random color for the ball, if one wasn't set.
if ( !self:GetBallColor():IsZero() ) then return end
self:SetBallColor( table.Random( {
Vector( 1, 0.3, 0.3 ),
Vector( 0.3, 1, 0.3 ),
Vector( 1, 1, 0.3 ),
Vector( 0.2, 0.3, 1 ),
} ) )
end
function ENT:RebuildPhysics( value )
-- This is necessary so that the vphysics.dll will not crash when attaching constraints to the new PhysObj after old one was destroyed
-- TODO: Somehow figure out why it happens and/or move this code/fix to the constraint library
self.ConstraintSystem = nil
local size = math.Clamp( value or self:GetBallSize(), self.MinSize, self.MaxSize ) / 2.1
self:PhysicsInitSphere( size, "metal_bouncy" )
self:SetCollisionBounds( Vector( -size, -size, -size ), Vector( size, size, size ) )
self:PhysWake()
end
if ( SERVER ) then
function ENT:OnBallSizeChanged( varname, oldvalue, newvalue )
-- Do not rebuild if the size wasn't changed
if ( oldvalue == newvalue ) then return end
self:RebuildPhysics( newvalue )
end
function ENT:KeyValue( key, val )
if ( key == "ball_size" ) then self:SetBallSize( val ) end
if ( key == "rendercolor" ) then self:SetBallColor( Vector( val ) / 255 ) end
end
end
local BounceSound = Sound( "garrysmod/balloon_pop_cute.wav" )
function ENT:PhysicsCollide( data, physobj )
-- Play sound on bounce
if ( data.Speed > 60 && data.DeltaTime > 0.2 ) then
local pitch = 32 + 128 - math.Clamp( self:GetBallSize(), self.MinSize, self.MaxSize )
sound.Play( BounceSound, self:GetPos(), 75, math.random( pitch - 10, pitch + 10 ), math.Clamp( data.Speed / 150, 0, 1 ) )
end
-- Bounce like a crazy bitch
local LastSpeed = math.max( data.OurOldVelocity:Length(), data.Speed )
local NewVelocity = physobj:GetVelocity()
NewVelocity:Normalize()
LastSpeed = math.max( NewVelocity:Length(), LastSpeed )
local TargetVelocity = NewVelocity * LastSpeed * 0.9
physobj:SetVelocity( TargetVelocity )
end
function ENT:OnTakeDamage( dmginfo )
-- React physically when shot/getting blown
self:TakePhysicsDamage( dmginfo )
end
function ENT:Use( activator, caller )
self:Remove()
if ( activator:IsPlayer() ) then
-- Give the collecting player some free health
local health = activator:Health()
activator:SetHealth( health + 5 )
activator:SendLua( "achievements.EatBall()" )
end
end
if ( SERVER ) then return end -- We do NOT want to execute anything below in this FILE on SERVER
local matBall = Material( "sprites/sent_ball" )
function ENT:Draw()
render.SetMaterial( matBall )
local pos = self:GetPos()
local lcolor = render.ComputeLighting( pos, Vector( 0, 0, 1 ) )
local c = self:GetBallColor()
lcolor.x = c.r * ( math.Clamp( lcolor.x, 0, 1 ) + 0.5 ) * 255
lcolor.y = c.g * ( math.Clamp( lcolor.y, 0, 1 ) + 0.5 ) * 255
lcolor.z = c.b * ( math.Clamp( lcolor.z, 0, 1 ) + 0.5 ) * 255
local size = math.Clamp( self:GetBallSize(), self.MinSize, self.MaxSize )
render.DrawSprite( pos, size, size, Color( lcolor.x, lcolor.y, lcolor.z, 255 ) )
end

View File

@@ -0,0 +1,114 @@
AddCSLuaFile()
DEFINE_BASECLASS( "widget_base" )
local matArrow = Material( "widgets/arrow.png", "nocull alphatest smooth mips" )
local matScale = Material( "widgets/scale.png", "nocull alphatest smooth mips" )
function ENT:SetupDataTables()
BaseClass.SetupDataTables( self )
self:NetworkVar( "Bool", 2, "IsScaleArrow" )
if ( SERVER ) then
self:SetIsScaleArrow( false )
end
end
--
-- Set our dimensions etc
--
function ENT:Initialize()
BaseClass.Initialize( self )
if ( SERVER ) then
self:SetSize( 16 )
end
end
function ENT:PressedShouldDraw( widget ) return widget == self end
function ENT:OverlayRender()
local fwd = self:GetAngles():Forward()
local size = self:GetSize() * 0.5
local c = self:GetColor()
if ( !self:IsHovered() && !self:IsPressed() ) then
c.r = c.r * 0.5
c.g = c.g * 0.5
c.b = c.b * 0.5
end
if ( self:GetIsScaleArrow() ) then
render.SetMaterial( matScale )
else
render.SetMaterial( matArrow )
end
local pos = self:GetPos()
render.DepthRange( 0, 0.01 )
render.DrawBeam( pos, pos + fwd * size, 2, 1, 0, Color( c.r, c.g, c.b, c.a * 0.1 ) )
render.DepthRange( 0, 1 * self:GetPriority() )
render.DrawBeam( pos, pos + fwd * size, 2, 1, 0, c )
render.DepthRange( 0, 1 )
end
function ENT:TestCollision( startpos, delta, isbox, extents )
if ( isbox ) then return end
if ( !widgets.Tracing ) then return end
local size = self:GetSize() * 0.5
local mins = Vector( 0, -1, -1 )
local maxs = Vector( size, 1, 1 )
local hit, norm, fraction = util.IntersectRayWithOBB( startpos, delta, self:GetPos(), self:GetAngles(), mins, maxs )
if ( !hit ) then return end
--debugoverlay.BoxAngles( self:GetPos(), mins, maxs, self:GetAngles(), 0.1, Color( 0, 0, 255, 64 ) )
return {
HitPos = hit,
Fraction = fraction * self:GetPriority()
}
end
function ENT:DragThink( pl, mv, dist )
local d = dist.x * -1
self:ArrowDragged( pl, mv, d )
end
function ENT:ArrowDragged( pl, mv, dist )
end
function ENT:GetGrabPos( Pos, Forward )
local fwd = Forward
local eye = Pos
local arrowdir = self:GetAngles():Forward()
local planepos = self:GetPos()
local planenrm = ( eye - planepos ):GetNormalized()
local hitpos = util.IntersectRayWithPlane( eye, fwd, planepos, planenrm )
if ( !hitpos ) then return end
-- Get nearest point along the arrow where we touched it
local fdist, vpos, falong = util.DistanceToLine( planepos - arrowdir * 1024, planepos + arrowdir * 1024, hitpos )
return vpos
end

View File

@@ -0,0 +1,121 @@
AddCSLuaFile()
--
-- widget_axis_arrow
--
DEFINE_BASECLASS( "widget_arrow" )
local widget_axis_arrow = { Base = "widget_arrow" }
function widget_axis_arrow:SetupDataTables()
BaseClass.SetupDataTables( self )
self:NetworkVar( "Int", 0, "AxisIndex" )
end
function widget_axis_arrow:ArrowDragged( pl, mv, dist )
self:GetParent():OnArrowDragged( self:GetAxisIndex(), dist, pl, mv )
end
scripted_ents.Register( widget_axis_arrow, "widget_axis_arrow" )
--
-- widget_axis_disc
--
DEFINE_BASECLASS( "widget_disc" )
local widget_axis_disc = { Base = "widget_disc" }
function widget_axis_disc:SetupDataTables()
BaseClass.SetupDataTables( self )
self:NetworkVar( "Int", 0, "AxisIndex" )
end
function widget_axis_disc:ArrowDragged( pl, mv, dist )
self:GetParent():OnArrowDragged( self:GetAxisIndex(), dist, pl, mv )
end
scripted_ents.Register( widget_axis_disc, "widget_axis_disc" )
DEFINE_BASECLASS( "widget_base" )
--
-- Set our dimensions etc
--
function ENT:Initialize()
BaseClass.Initialize( self )
self:SetCollisionBounds( Vector( -1, -1, -1 ), Vector( 1, 1, 1 ) )
self:SetSolid( SOLID_NONE )
end
function ENT:Setup( ent, boneid, rotate )
self:FollowBone( ent, boneid )
self:SetLocalPos( vector_origin )
self:SetLocalAngles( angle_zero )
local EntName = "widget_axis_arrow"
if ( rotate ) then EntName = "widget_axis_disc" end
self.ArrowX = ents.Create( EntName )
self.ArrowX:SetParent( self )
self.ArrowX:SetColor( Color( 255, 0, 0, 255 ) )
self.ArrowX:Spawn()
self.ArrowX:SetLocalPos( vector_origin )
self.ArrowX:SetLocalAngles( Vector( 1, 0, 0 ):Angle() )
self.ArrowX:SetAxisIndex( 1 )
self.ArrowY = ents.Create( EntName )
self.ArrowY:SetParent( self )
self.ArrowY:SetColor( Color( 0, 230, 50, 255 ) )
self.ArrowY:Spawn()
self.ArrowY:SetLocalPos( vector_origin )
self.ArrowY:SetLocalAngles( Vector( 0, 1, 0 ):Angle() )
self.ArrowY:SetAxisIndex( 2 )
self.ArrowZ = ents.Create( EntName )
self.ArrowZ:SetParent( self )
self.ArrowZ:SetColor( Color( 50, 100, 255, 255 ) )
self.ArrowZ:Spawn()
self.ArrowZ:SetLocalPos( vector_origin )
self.ArrowZ:SetLocalAngles( Vector( 0, 0, 1 ):Angle() )
self.ArrowZ:SetAxisIndex( 3 )
if ( self.IsScaleArrow && EntName == "widget_axis_arrow" ) then
if ( IsValid( self.ArrowX ) ) then self.ArrowX:SetIsScaleArrow( true ) end
if ( IsValid( self.ArrowY ) ) then self.ArrowY:SetIsScaleArrow( true ) end
if ( IsValid( self.ArrowZ ) ) then self.ArrowZ:SetIsScaleArrow( true ) end
end
end
function ENT:SetPriority( x )
if ( IsValid( self.ArrowX ) ) then self.ArrowX:SetPriority( x ) end
if ( IsValid( self.ArrowY ) ) then self.ArrowY:SetPriority( x ) end
if ( IsValid( self.ArrowZ ) ) then self.ArrowZ:SetPriority( x ) end
end
function ENT:Draw()
end
function ENT:OverlayRender()
end
function ENT:OnArrowDragged( num, dist, pl, mv )
end

View File

@@ -0,0 +1,238 @@
AddCSLuaFile()
DEFINE_BASECLASS( "base_anim" )
ENT.Spawnable = false
ENT.AdminOnly = false
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.Widget = true
-- This appears to be unused in the base game, but leaving it in case someones does
ENT.Materials = {}
function ENT:SetupDataTables()
self:NetworkVar( "Float", 0, "SizeVar" ) -- Size (bounds)
self:NetworkVar( "Float", 1, "Priority" ) -- Priority above other widgets (clicks, visually)
end
--
-- Unfilled, for override. Self explanatory.
-- Called on both client and server in multiplayer
-- Only on the server in singleplayer.
--
function ENT:OnClick( ply ) end
function ENT:OnRightClick( ply ) end
function ENT:PressedThink( pl, mv ) end
function ENT:PressedShouldDraw( widget ) return true end
function ENT:PressStart( pl, mv ) end
function ENT:PressEnd( pl, mv ) end
function ENT:DragThink( pl, mv, dist ) end
--
-- Set our dimensions etc
--
function ENT:Initialize()
self:SetCollisionGroup( COLLISION_GROUP_WORLD )
self:DrawShadow( false )
self:EnableCustomCollisions()
self.Color = Color( 0, 255, 255 )
self.Color_Hover = color_white
if ( SERVER ) then
self:SetSize( 4 )
self:SetPriority( 1 )
end
end
function ENT:GetSize()
return self:GetSizeVar()
end
function ENT:SetSize( size )
if ( self:GetSize() == size ) then return end
local sizeVector = Vector( size, size, size )
self:SetSizeVar( size )
self:SetSolid( SOLID_BBOX )
self:SetCollisionBounds( sizeVector * -0.5, sizeVector * 0.5 )
end
function ENT:GetGrabPos( Pos, Forward )
local fwd = Forward
local eye = Pos
local planepos = self:GetPos()
local planenrm = ( eye - planepos ):GetNormalized()
return util.IntersectRayWithPlane( eye, fwd, planepos, planenrm )
end
function ENT:PressedThinkInternal( ply, mv )
local eyePos = ply:EyePos()
local aimVector = ply:GetAimVector()
--
-- TODO: We should find out why this happens instead of just preventing it!
--
if ( !istable( ply.WidgetMove ) ) then
ply.WidgetMove = {}
ply.WidgetMove.EyePos = eyePos
ply.WidgetMove.EyeVec = aimVector
end
local widgetMove = ply.WidgetMove
local OldPos = self:GetGrabPos( widgetMove.EyePos, widgetMove.EyeVec )
local NewPos = self:GetGrabPos( eyePos, aimVector )
if ( NewPos && OldPos ) then
local dist = self:WorldToLocal( OldPos ) - self:WorldToLocal( NewPos )
local len = dist:LengthSqr()
local minLen = 0.01 * 0.01
local maxLen = 512 * 512
if ( len > minLen && len < maxLen ) then
self:DragThink( ply, mv, dist )
end
end
self:PressedThink( ply, mv )
-- Store the (new) old eye positions
widgetMove.EyePos = eyePos
widgetMove.EyeVec = aimVector
end
--
-- Called by widget's Tick hook when a mouse button was
-- pressed while hovering over this widget
--
function ENT:OnPress( ply, iButton, mv )
if ( self.Pressed ) then return end
ply:SetPressedWidget( self )
ply.WidgetMove = {}
ply.WidgetMove.EyePos = ply:EyePos()
ply.WidgetMove.EyeVec = ply:GetAimVector()
self:PressStart( ply, mv )
end
--
-- Called by widget's Tick hook when a mouse button was
-- released while this widget is pressed
--
function ENT:OnRelease( ply, iButton, mv )
ply:SetPressedWidget( NULL )
ply.WidgetMove = nil
self:PressEnd( ply, mv )
--
-- The player has to click and release on the widget
-- or we assume they clicked and changed their mind
-- so dragged off.. like people do sometimes.
--
if ( ply:GetHoveredWidget() != self ) then return end
--
-- Left Mouse
--
if ( iButton == 1 ) then
self:OnClick( ply )
end
--
-- Right Mouse
--
if ( iButton == 1 ) then
self:OnRightClick( ply )
end
end
function ENT:IsHovered()
return LocalPlayer():GetHoveredWidget() == self
end
function ENT:SomethingHovered()
return IsValid( LocalPlayer():GetHoveredWidget() )
end
function ENT:IsPressed()
return LocalPlayer():GetPressedWidget() == self
end
function ENT:Draw()
widgets.RenderMe( self )
end
local colDefault = Color( 0, 0, 50, 255 )
local colHovered = Color( 20, 50, 100, 255 )
local colPressed = Color( 180, 180, 50, 255 )
local colHoveredAndPressed = Color( 255, 255, 100, 255 )
function ENT:OverlayRender()
local col = colDefault
local ply = LocalPlayer()
local hovered = ply:GetHoveredWidget()
if ( hovered == self ) then
col = colHovered
end
if ( self:IsPressed() ) then
col = colPressed
if ( hovered == ply:GetPressedWidget() ) then
col = colHoveredAndPressed
end
end
local size = self:GetSize()
local vSize = Vector( size, size, size )
local pos = self:GetPos()
local angles = self:GetAngles()
render.SetColorMaterialIgnoreZ()
render.DrawBox( pos, angles, -vSize, vSize, ColorAlpha( col, 0.8 ) )
render.SetColorMaterial()
render.DrawBox( pos, angles, -vSize, vSize, col )
end
function ENT:TestCollision( startpos, delta, isbox, extents )
if ( isbox ) then return end
if ( !widgets.Tracing ) then return end
-- TODO. Actually trace against our cube!
return {
HitPos = self:GetPos(),
Fraction = 0.5 * self:GetPriority()
}
end

View File

@@ -0,0 +1,176 @@
AddCSLuaFile()
local matBone = Material( "widgets/bone.png", "smooth" )
local matBoneSmall = Material( "widgets/bone_small.png", "smooth" )
local c = Color( 255, 255, 255, 255 )
local widget_bone = {
Base = "widget_base",
a = 255,
GetParentPos = function( self )
local p = self:GetParent()
if ( !IsValid( p ) ) then return end
local bp = p:GetBoneParent( self:GetParentAttachment() )
if ( bp <= 0 ) then return end
return p:GetBonePosition( bp )
end,
OverlayRender = function( self )
local pp = self:GetParentPos()
if ( !pp ) then return end
local len = self:GetSize() / 2
local w = len * 0.2
if ( len > 10 ) then
render.SetMaterial( matBone )
else
render.SetMaterial( matBoneSmall )
end
local hovered = LocalPlayer():GetHoveredWidget()
if ( IsValid( hovered ) && hovered != self ) then
self.a = math.Approach( self.a, 20, FrameTime() * 255 * 10 )
else
self.a = math.Approach( self.a, 255, FrameTime() * 255 * 10 )
end
local pos = self:GetPos()
cam.IgnoreZ( true )
c.a = self.a * 0.5
render.DrawBeam( pos, pp, w, 0, 1, c )
cam.IgnoreZ( false )
c.a = self.a
render.DrawBeam( pos, pp, w, 0, 1, c )
end,
TestCollision = function( self, startpos, delta, isbox, extents )
if ( isbox ) then return end
if ( !widgets.Tracing ) then return end
local pp = self:GetParentPos()
if ( !pp ) then return end
local pos = self:GetPos()
local fwd = ( pp - pos ):GetNormal()
local ang = fwd:Angle()
local len = self:GetSize() / 2
local w = len * 0.2
local mins = Vector( 0, w * -0.5, w * -0.5 )
local maxs = Vector( len, w * 0.5, w * 0.5 )
local hit, norm, fraction = util.IntersectRayWithOBB( startpos, delta, pos, ang, mins, maxs )
if ( !hit ) then return end
--debugoverlay.BoxAngles( self:GetPos(), mins, maxs, ang, 0.1, Color( 0, 255, 0, 64 ) )
return {
HitPos = hit,
Fraction = fraction * self:GetPriority()
}
end,
Think = function( self )
if ( !SERVER ) then return end
local parent = self:GetParent()
if ( !IsValid( parent ) ) then return end
--
-- Because the bone length changes (with the manipulator)
-- we need to update the bones pretty regularly
--
local size = parent:BoneLength( self:GetParentAttachment() ) * 2
size = math.ceil( size )
self:SetSize( size )
end,
CalcAbsolutePosition = function( self, v, a )
local ent = self:GetParent()
if ( !IsValid( ent ) ) then return end
local bone = self:GetParentAttachment()
if ( bone <= 0 ) then return end
return ent:GetBonePosition( bone )
end
}
scripted_ents.Register( widget_bone, "widget_bone" )
DEFINE_BASECLASS( "widget_base" )
function ENT:SetupDataTables()
self:NetworkVar( "Entity", 0, "Target" )
self.BaseClass.SetupDataTables( self )
end
--
-- Set our dimensions etc
--
function ENT:Initialize()
self.BaseClass.Initialize( self )
self:SetCollisionBounds( Vector( -1, -1, -1 ), Vector( 1, 1, 1 ) )
self:SetSolid( SOLID_NONE )
end
function ENT:Setup( ent )
self:SetTarget( ent )
self:SetParent( ent )
self:SetLocalPos( vector_origin )
for k = 0, ent:GetBoneCount() - 1 do
if ( ent:GetBoneParent( k ) <= 0 ) then continue end
if ( !ent:BoneHasFlag( k, BONE_USED_BY_VERTEX_LOD0 ) ) then continue end
local btn = ents.Create( "widget_bone" )
btn:FollowBone( ent, k )
btn:SetLocalPos( vector_origin )
btn:SetLocalAngles( angle_zero )
btn:Spawn()
btn:SetSize( ent:BoneLength( k ) * 2 )
btn.OnClick = function( x, ply )
self:OnBoneClick( k, ply )
end
self:DeleteOnRemove( btn )
end
end
function ENT:OnBoneClick( boneid, ply )
end
function ENT:OverlayRender()
end

View File

@@ -0,0 +1,111 @@
AddCSLuaFile()
local matDisc = Material( "widgets/disc.png", "nocull alphatest smooth mips" )
local matDiscAlpha = Material( "widgets/disc.png", "nocull smooth mips" )
DEFINE_BASECLASS( "widget_arrow" )
--
-- Set our dimensions etc
--
function ENT:Initialize()
BaseClass.Initialize( self )
if ( SERVER ) then
self:SetSize( 16 )
end
end
function ENT:PressedShouldDraw( widget ) return widget == self end
function ENT:OverlayRender()
local angles = self:GetAngles()
local fwd = angles:Forward()
local size = self:GetSize()
local c = self:GetColor()
if ( !self:IsHovered() && !self:IsPressed() ) then
c.r = c.r * 0.5
c.g = c.g * 0.5
c.b = c.b * 0.5
end
local pos = self:GetPos()
local ang = angles.roll - 90
render.DepthRange( 0, 0.01 )
render.SetMaterial( matDiscAlpha )
render.DrawQuadEasy( pos, fwd, size, size, Color( c.r, c.g, c.b, c.a * 0.2 ), ang )
render.DepthRange( 0, 1 )
render.SetMaterial( matDisc )
render.DrawQuadEasy( pos, fwd, size, size, c, ang )
render.DepthRange( 0, 1 )
end
function ENT:TestCollision( startpos, delta, isbox, extents )
if ( isbox ) then return end
if ( !widgets.Tracing ) then return end
local fwd = self:GetAngles():Forward()
local size = self:GetSize() * 0.5
local pos = self:GetPos()
local hitpos = util.IntersectRayWithPlane( startpos, delta:GetNormalized(), pos, fwd )
if ( !hitpos ) then return end
local dist = pos:DistToSqr( hitpos )
size = size * size
if ( dist > size ) then return end
if ( dist < size * 0.9 ) then return end
--debugoverlay.Cross( hitpos, 0.5, 60 )
local fraction = ( hitpos - startpos ):Length() / delta:Length()
return {
HitPos = hitpos,
Fraction = fraction * self:GetPriority()
}
end
function ENT:DragThink( pl, mv, dist )
local d = dist.x * -1
self:ArrowDragged( pl, mv, d )
end
function ENT:ArrowDragged( pl, mv, dist )
end
function ENT:GetGrabPos( Pos, Forward )
local fwd = Forward
local eye = Pos
local arrowdir = self:GetAngles():Forward()
local planepos = self:GetPos()
--local planenrm = ( eye - planepos ):GetNormal()
local hitpos = util.IntersectRayWithPlane( eye, fwd, planepos, arrowdir )
if ( !hitpos ) then return end
-- The whole circle should be 360
hitpos = self:WorldToLocal( hitpos )
hitpos:Normalize()
local angle = math.atan2( hitpos.y, hitpos.z ) + math.pi
angle = math.deg( angle ) * -1
return arrowdir * angle
end

View File

@@ -0,0 +1,18 @@
local meta = FindMetaTable( "Angle" )
-- Nothing in here, still leaving this file here just in case
--[[---------------------------------------------------------
Angle Snap to nearest interval of degrees
-----------------------------------------------------------]]
function meta:SnapTo( component, degrees )
if ( degrees == 0 ) then ErrorNoHalt( "The snap degrees must be non-zero.\n" ); return self; end
if ( !self[ component ] ) then ErrorNoHalt( "You must choose a valid component of Angle( p || pitch, y || yaw, r || roll ) to snap such as Angle( 80, 40, 30 ):SnapTo( \"p\", 90 ):SnapTo( \"y\", 45 ):SnapTo( \"r\", 40 ); and yes, you can keep adding snaps.\n" ); return self; end
self[ component ] = math.Round( self[ component ] / degrees ) * degrees
self[ component ] = math.NormalizeAngle( self[ component ] )
return self
end

View File

@@ -0,0 +1,17 @@
if ( SERVER ) then return end
local meta = FindMetaTable( "Entity" )
if ( !meta ) then return end
--
-- You can set the render angles and render origin on
-- any entity. It will then render the entity at that
-- origin using those angles.
--
-- Set them to nil if you don't want to override anything.
--
AccessorFunc( meta, "m_RenderAngles", "RenderAngles" )
AccessorFunc( meta, "m_RenderOrigin", "RenderOrigin" )

View File

@@ -0,0 +1,14 @@
if ( SERVER ) then return end
function ScreenScale( width )
return width * ( ScrW() / 640.0 )
end
function ScreenScaleH( height )
return height * ( ScrH() / 480.0 )
end
SScale = ScreenScale

View File

@@ -0,0 +1,621 @@
include ( "panel/animation.lua" )
include ( "panel/dragdrop.lua" )
include ( "panel/selections.lua" )
include ( "panel/scriptedpanels.lua" )
local meta = FindMetaTable( "Panel" )
AccessorFunc( meta, "m_strCookieName", "CookieName" )
meta.SetFGColorEx = meta.SetFGColor
meta.SetBGColorEx = meta.SetBGColor
--[[---------------------------------------------------------
Name: SetFGColor
Desc: Override to make it possible to pass Color's
-----------------------------------------------------------]]
function meta:SetFGColor( r, g, b, a )
if ( istable( r ) ) then
return self:SetFGColorEx( r.r, r.g, r.b, r.a )
end
return self:SetFGColorEx( r, g, b, a )
end
--[[---------------------------------------------------------
Name: SetBGColor
Desc: Override to make it possible to pass Color's
-----------------------------------------------------------]]
function meta:SetBGColor( r, g, b, a )
if ( istable( r ) ) then
return self:SetBGColorEx( r.r, r.g, r.b, r.a )
end
return self:SetBGColorEx( r, g, b, a )
end
--[[---------------------------------------------------------
Name: SetHeight
-----------------------------------------------------------]]
function meta:SetHeight( h )
self:SetSize( self:GetWide(), h )
end
meta.SetTall = meta.SetHeight
--[[---------------------------------------------------------
Name: SetHeight
-----------------------------------------------------------]]
function meta:SetWidth( w )
self:SetSize( w, self:GetTall() )
end
meta.SetWide = meta.SetWidth
--[[---------------------------------------------------------
Name: Set/GetX/Y
-----------------------------------------------------------]]
function meta:GetX()
local x, y = self:GetPos()
return x
end
function meta:GetY()
local x, y = self:GetPos()
return y
end
function meta:SetX( x )
self:SetPos( x, self:GetY() )
end
function meta:SetY( y )
self:SetPos( self:GetX(), y )
end
--[[---------------------------------------------------------
Name: StretchToParent (borders)
-----------------------------------------------------------]]
function meta:StretchToParent( l, u, r, d )
local w, h = self:GetParent():GetSize()
if ( l != nil ) then
self.x = l
end
if ( u != nil ) then
self.y = u
end
if ( r != nil ) then
self:SetWide( w - self.x - r )
end
if ( d != nil ) then
self:SetTall( h - self.y - d )
end
--self:SetPos( l, u )
--self:SetSize( w - (r + l), h - (d + u) )
end
--[[---------------------------------------------------------
Name: CopyHeight
-----------------------------------------------------------]]
function meta:CopyHeight( pnl )
self:SetTall( pnl:GetTall() )
end
--[[---------------------------------------------------------
Name: CopyWidth
-----------------------------------------------------------]]
function meta:CopyWidth( pnl )
self:SetWide( pnl:GetWide() )
end
--[[---------------------------------------------------------
Name: CopyPos
-----------------------------------------------------------]]
function meta:CopyPos( pnl )
self:SetPos( pnl:GetPos() )
end
--[[---------------------------------------------------------
Name: Align with the edge of the parent
-----------------------------------------------------------]]
function meta:AlignBottom( m ) self:SetPos( self.x, self:GetParent():GetTall() - self:GetTall() - ( m or 0 ) ) end
function meta:AlignRight( m ) self:SetPos( self:GetParent():GetWide() - self:GetWide() - ( m or 0 ), self.y ) end
function meta:AlignTop( m ) self:SetPos( self.x, m or 0 ) end
function meta:AlignLeft( m ) self:SetPos( m or 0, self.y ) end
--[[---------------------------------------------------------
Name: Move relative to another panel
-----------------------------------------------------------]]
function meta:MoveAbove( pnl, m ) self:SetPos( self.x, pnl.y - self:GetTall() - ( m or 0 ) ) end
function meta:MoveBelow( pnl, m ) self:SetPos( self.x, pnl.y + pnl:GetTall() + ( m or 0 ) ) end
function meta:MoveRightOf( pnl, m ) self:SetPos( pnl.x + pnl:GetWide() + ( m or 0 ), self.y ) end
function meta:MoveLeftOf( pnl, m ) self:SetPos( pnl.x - self:GetWide() - ( m or 0 ), self.y ) end
--[[---------------------------------------------------------
Name: StretchRightTo
-----------------------------------------------------------]]
function meta:StretchRightTo( pnl, m ) self:SetWide( pnl.x - self.x - ( m or 0 ) ) end
function meta:StretchBottomTo( pnl, m ) self:SetTall( pnl.y - self.y - ( m or 0 ) ) end
--[[---------------------------------------------------------
Name: CenterVertical
-----------------------------------------------------------]]
function meta:CenterVertical( fraction )
self:SetY( self:GetParent():GetTall() * ( fraction or 0.5 ) - self:GetTall() * 0.5 )
end
--[[---------------------------------------------------------
Name: CenterHorizontal
-----------------------------------------------------------]]
function meta:CenterHorizontal( fraction )
self:SetX( self:GetParent():GetWide() * ( fraction or 0.5 ) - self:GetWide() * 0.5 )
end
--[[---------------------------------------------------------
Name: CenterHorizontal
-----------------------------------------------------------]]
function meta:Center()
self:CenterVertical()
self:CenterHorizontal()
end
--[[---------------------------------------------------------
Name: CopyBounds
-----------------------------------------------------------]]
function meta:CopyBounds( pnl )
local x, y, w, h = pnl:GetBounds()
self:SetPos( x, y )
self:SetSize( w, h )
end
--[[---------------------------------------------------------
Name: GetCookieNumber
-----------------------------------------------------------]]
function meta:SetCookieName( cookiename )
self.m_strCookieName = cookiename
-- If we have a loadcookies function, call it.
if ( self.LoadCookies ) then
self:LoadCookies()
self:InvalidateLayout()
end
end
--[[---------------------------------------------------------
Name: GetCookieNumber
-----------------------------------------------------------]]
function meta:GetCookieNumber( cookiename, default )
local name = self:GetCookieName()
if ( !name ) then return default end
return cookie.GetNumber( name .. "." .. cookiename, default )
end
--[[---------------------------------------------------------
Name: GetCookie
-----------------------------------------------------------]]
function meta:GetCookie( cookiename, default )
local name = self:GetCookieName()
if ( !name ) then return default end
return cookie.GetString( name .. "." .. cookiename, default )
end
--[[---------------------------------------------------------
Name: SetCookie
-----------------------------------------------------------]]
function meta:SetCookie( cookiename, value )
local name = self:GetCookieName()
if ( !name ) then return end
return cookie.Set( name .. "." .. cookiename, value )
end
--[[---------------------------------------------------------
Name: DeleteCookie
-----------------------------------------------------------]]
function meta:DeleteCookie( cookiename )
local name = self:GetCookieName()
if ( !name ) then return end
return cookie.Delete( name .. "." .. cookiename )
end
--[[---------------------------------------------------------
Name: InvalidateParent
-----------------------------------------------------------]]
function meta:InvalidateParent( layoutnow )
local parent = self:GetParent()
if ( !parent ) then return end
if ( self.LayingOutParent ) then return end
self.LayingOutParent = true
parent:InvalidateLayout( layoutnow )
self.LayingOutParent = false
end
--[[---------------------------------------------------------
Name: PositionLabel
-----------------------------------------------------------]]
function meta:PositionLabel( labelWidth, x, y, lbl, ctrl )
lbl:SetWide( labelWidth )
lbl:SetPos( x, y )
ctrl.y = y
ctrl:MoveRightOf( lbl, 0 )
return y + math.max( lbl:GetTall(), ctrl:GetTall() )
end
--[[---------------------------------------------------------
Name: GetTooltip
-----------------------------------------------------------]]
function meta:GetTooltip()
return self.strTooltipText
end
--[[---------------------------------------------------------
Name: GetTooltipPanel
-----------------------------------------------------------]]
function meta:GetTooltipPanel()
return self.pnlTooltipPanel
end
--[[---------------------------------------------------------
Name: GetTooltipDelay
-----------------------------------------------------------]]
function meta:GetTooltipDelay()
return self.numTooltipDelay
end
--[[---------------------------------------------------------
Name: SetTooltip
-----------------------------------------------------------]]
function meta:SetTooltip( tooltip )
self.strTooltipText = tooltip
end
meta.SetToolTip = meta.SetTooltip
--[[---------------------------------------------------------
Name: SetTooltipPanel
-----------------------------------------------------------]]
function meta:SetTooltipPanel( panel )
self.pnlTooltipPanel = panel
if ( IsValid( panel ) ) then panel:SetVisible( false ) end
end
meta.SetToolTipPanel = meta.SetTooltipPanel
-- Override which panel will be created instead of DTooltip
function meta:SetTooltipPanelOverride( panel )
self.pnlTooltipPanelOverride = panel
end
--[[---------------------------------------------------------
Name: SetTooltipDelay
-----------------------------------------------------------]]
function meta:SetTooltipDelay( delay )
self.numTooltipDelay = delay
end
--[[---------------------------------------------------------
Name: SizeToContentsY (Only works on Labels)
-----------------------------------------------------------]]
function meta:SizeToContentsY( addval )
local w, h = self:GetContentSize()
if ( !w || !h ) then return end
self:SetTall( h + ( addval or 0 ) )
end
--[[---------------------------------------------------------
Name: SizeToContentsX (Only works on Labels)
-----------------------------------------------------------]]
function meta:SizeToContentsX( addval )
local w, h = self:GetContentSize()
if ( !w || !h ) then return end
self:SetWide( w + ( addval or 0 ) )
end
-- Make sure all children update their skin, if SOMEHOW they cached their skin before the parent
local function InvalidateSkinRecurse( self )
for id, pnl in pairs( self:GetChildren() ) do
InvalidateSkinRecurse( pnl )
pnl.m_iSkinIndex = nil
end
end
--[[---------------------------------------------------------
Name: SetSkin
-----------------------------------------------------------]]
function meta:SetSkin( strSkin )
if ( self.m_ForceSkinName == strSkin ) then return end
self.m_ForceSkinName = strSkin
self.m_iSkinIndex = nil
InvalidateSkinRecurse( self )
end
--[[---------------------------------------------------------
Name: GetSkin
-----------------------------------------------------------]]
function meta:GetSkin()
local skin = nil
if ( derma.SkinChangeIndex() == self.m_iSkinIndex ) then
skin = self.m_Skin
if ( skin ) then return skin end
end
-- We have a default skin
if ( !skin && self.m_ForceSkinName ) then
skin = derma.GetNamedSkin( self.m_ForceSkinName )
end
-- No skin, inherit from parent
local parent = self:GetParent()
if ( !skin && IsValid( parent ) ) then
skin = parent:GetSkin()
end
-- Parent had no skin, use default
if ( !skin ) then
skin = derma.GetDefaultSkin()
end
-- Save skin details on us so we don't have to keep looking up
self.m_Skin = skin
self.m_iSkinIndex = derma.SkinChangeIndex()
self:InvalidateLayout( false )
return skin
end
--[[---------------------------------------------------------
Name: ToggleVisible
-----------------------------------------------------------]]
function meta:ToggleVisible()
self:SetVisible( !self:IsVisible() )
end
function meta:Distance( pnl )
if ( !IsValid( pnl ) ) then return 0 end
return self:DistanceFrom( pnl.x + pnl:GetWide() * 0.5, pnl.y + pnl:GetTall() * 0.5 )
end
function meta:DistanceFrom( x, y )
local x = self.x + self:GetWide() * 0.5 - x
local y = self.y + self:GetTall() * 0.5 - y
return math.sqrt( x * x + y * y )
end
--[[---------------------------------------------------------
Name: Retusn the child position on this panel. Even if its parented to children of children.
-----------------------------------------------------------]]
function meta:GetChildPosition( pnl )
local x = 0
local y = 0
while ( IsValid( pnl ) && pnl != self ) do
x = x + pnl.x
y = y + pnl.y
pnl = pnl:GetParent()
end
return x, y
end
--[[---------------------------------------------------------
Name: Returns true if the panel is valid. This does not
check the type. If the passed object is anything other
than a panel or nil, this will error. (speed)
-----------------------------------------------------------]]
function ValidPanel( pnl )
if ( !pnl ) then return false end
return pnl:IsValid()
end
function meta:InvalidateChildren( bRecurse )
for k, v in ipairs( self:GetChildren() ) do
if ( bRecurse ) then
v:InvalidateChildren( true )
else
v:InvalidateLayout( true )
end
end
self:InvalidateLayout( true )
end
function meta:IsOurChild( child )
if ( !IsValid( child ) ) then return false end
return child:HasParent( self )
end
function meta:CopyBase( pnl )
self:CopyBounds( pnl )
self:Dock( pnl:GetDock() )
// TODO. More.
end
function meta:Add( pnl )
if ( isstring( pnl ) ) then
local pnl = vgui.Create( pnl, self )
return pnl
end
if ( istable( pnl ) ) then
local pnl = vgui.CreateFromTable( pnl, self )
return pnl
end
pnl:SetParent( self )
return pnl
end
function meta:GetClosestChild( x, y )
local distance = 9999
local closest = nil
for k, v in ipairs( self:GetChildren() ) do
local dist = v:DistanceFrom( x, y )
if ( dist < distance ) then
distance = dist
closest = v
end
end
return closest, distance
end
function meta:LocalCursorPos()
return self:ScreenToLocal( gui.MouseX(), gui.MouseY() )
end
function meta:MoveToAfter( pnl )
local children = self:GetParent():GetChildren()
-- remove us from the table
table.RemoveByValue( children, self )
-- find the key, where we want to be
local key = table.KeyFromValue( children, pnl )
if ( key ) then
-- insert us where we wanna be
table.insert( children, key + 1, self )
else
return false
end
for k, v in ipairs( children ) do
v:SetZPos( k )
end
end
function meta:MoveToBefore( pnl )
local children = self:GetParent():GetChildren()
-- remove us from the table
table.RemoveByValue( children, self )
-- find the key, where we want to be
local key = table.KeyFromValue( children, pnl )
if ( key ) then
-- insert us where we wanna be
table.insert( children, key, self )
else
return false
end
for k, v in ipairs( children ) do
v:SetZPos( k )
end
end
function meta:Clear()
for k, panel in ipairs( self:GetChildren() ) do
panel:Remove()
end
end
function meta:IsHovered()
return vgui.GetHoveredPanel() == self
end
function meta:Show()
self:SetVisible( true )
end
function meta:Hide()
self:SetVisible( false )
end
function meta:IsChildHovered( bImmediate )
local Hovered = vgui.GetHoveredPanel()
if ( !IsValid( Hovered ) ) then return false end
if ( Hovered == self ) then return false end
-- Check immediate child only (with support for old depth parameter)
if ( bImmediate == true or bImmediate == 1 ) then return Hovered:GetParent() == self end
return Hovered:HasParent( self )
end

View File

@@ -0,0 +1,365 @@
if ( SERVER ) then return end
local meta = FindMetaTable( "Panel" )
--[[---------------------------------------------------------
Name: SetTerm
Desc: Kill the panel at this time
-----------------------------------------------------------]]
function meta:SetTerm( term )
self.Term = SysTime() + term
self:SetAnimationEnabled( true )
end
--[[---------------------------------------------------------
Name: AnimationThinkInternal
-----------------------------------------------------------]]
function meta:AnimationThinkInternal()
local systime = SysTime()
if ( self.Term && self.Term <= systime ) then self:Remove() return end
if ( !self.m_AnimList ) then return end -- This can happen if we only have term
for k, anim in pairs( self.m_AnimList ) do
if ( systime >= anim.StartTime ) then
local Fraction = math.TimeFraction( anim.StartTime, anim.EndTime, systime )
Fraction = math.Clamp( Fraction, 0, 1 )
if ( anim.Think ) then
local Frac = Fraction ^ anim.Ease
-- Ease of -1 == ease in out
if ( anim.Ease < 0 ) then
Frac = Fraction ^ ( 1.0 - ( ( Fraction - 0.5 ) ) )
elseif ( anim.Ease > 0 && anim.Ease < 1 ) then
Frac = 1 - ( ( 1 - Fraction ) ^ ( 1 / anim.Ease ) )
end
anim:Think( self, Frac )
end
if ( Fraction == 1 ) then
if ( anim.OnEnd ) then anim:OnEnd( self ) end
self.m_AnimList[k] = nil
end
end
end
end
--[[---------------------------------------------------------
Name: SetAnimationEnabled
Desc: Enables animations on a panel
-----------------------------------------------------------]]
function meta:SetAnimationEnabled( b )
if ( !b ) then
self.AnimationThink = nil
return
end
if ( self.AnimationThink ) then return end
self.AnimationThink = self.AnimationThinkInternal
end
function meta:Stop()
self.m_AnimList = {}
end
function meta:Queue()
self.m_AnimQueue = true
end
function meta:AnimTail()
local last = SysTime()
for k, anim in pairs( self.m_AnimList ) do
last = math.max( last, anim.EndTime )
end
return last
end
--[[---------------------------------------------------------
Name: NewAnimation
Desc: Creates a new animation
-----------------------------------------------------------]]
function meta:NewAnimation( length, delay, ease, callback )
if ( delay == nil ) then delay = 0 end
if ( ease == nil ) then ease = -1 end
if ( self.m_AnimQueue ) then
delay = delay + self:AnimTail()
self.m_AnimQueue = false
else
delay = delay + SysTime()
end
local anim = {
EndTime = delay + length,
StartTime = delay,
Ease = ease,
OnEnd = callback
}
self:SetAnimationEnabled( true )
if ( self.m_AnimList == nil ) then self.m_AnimList = {} end
table.insert( self.m_AnimList, anim )
return anim
end
local function MoveThink( anim, panel, fraction )
if ( !anim.StartPos ) then anim.StartPos = Vector( panel.x, panel.y, 0 ) end
local pos = LerpVector( fraction, anim.StartPos, anim.Pos )
panel:SetPos( pos.x, pos.y )
end
--[[---------------------------------------------------------
Name: MoveTo
-----------------------------------------------------------]]
function meta:MoveTo( x, y, length, delay, ease, callback )
if ( self.x == x && self.y == y ) then return end
local anim = self:NewAnimation( length, delay, ease, callback )
anim.Pos = Vector( x, y, 0 )
anim.Think = MoveThink
end
local function SizeThink( anim, panel, fraction )
if ( !anim.StartSize ) then local w, h = panel:GetSize() anim.StartSize = Vector( w, h, 0 ) end
local size = LerpVector( fraction, anim.StartSize, anim.Size )
if ( anim.SizeX && anim.SizeY ) then
panel:SetSize( size.x, size.y )
elseif ( anim.SizeX ) then
panel:SetWide( size.x )
else
panel:SetTall( size.y )
end
if ( panel:GetDock() > 0 ) then
panel:InvalidateParent()
end
end
--[[---------------------------------------------------------
Name: SizeTo
-----------------------------------------------------------]]
function meta:SizeTo( w, h, length, delay, ease, callback )
local anim = self:NewAnimation( length, delay, ease, callback )
anim.SizeX = w != -1
anim.SizeY = h != -1
if ( !anim.SizeX ) then w = self:GetWide() end
if ( !anim.SizeY ) then h = self:GetTall() end
anim.Size = Vector( w, h, 0 )
anim.Think = SizeThink
return anim
end
--[[---------------------------------------------------------
Name: SlideUp
-----------------------------------------------------------]]
function meta:SlideUp( length )
local height = self:GetTall()
local anim = self:SizeTo( -1, 0, length )
anim.OnEnd = function()
self:SetVisible( false )
self:SetTall( height )
end
end
--[[---------------------------------------------------------
Name: SlideDown
-----------------------------------------------------------]]
function meta:SlideDown( length )
local height = self:GetTall()
self:SetVisible( true )
self:SetTall( 0 )
local anim = self:SizeTo( -1, height, length )
end
local function ColorThink( anim, panel, fraction )
if ( !anim.StartColor ) then anim.StartColor = panel:GetColor() end
panel:SetColor( Color( Lerp( fraction, anim.StartColor.r, anim.Color.r ),
Lerp( fraction, anim.StartColor.g, anim.Color.g ),
Lerp( fraction, anim.StartColor.b, anim.Color.b ),
Lerp( fraction, anim.StartColor.a, anim.Color.a ) ) )
end
--[[---------------------------------------------------------
Name: ColorTo
-----------------------------------------------------------]]
function meta:ColorTo( col, length, delay, callback )
-- We can only use this on specific panel types!
if ( !self.SetColor ) then return end
if ( !self.GetColor ) then return end
local anim = self:NewAnimation( length, delay, nil, callback )
anim.Color = col
anim.Think = ColorThink
end
local function AlphaThink( anim, panel, fraction )
if ( !anim.StartAlpha ) then anim.StartAlpha = panel:GetAlpha() end
panel:SetAlpha( Lerp( fraction, anim.StartAlpha, anim.Alpha ) )
end
--[[---------------------------------------------------------
Name: AlphaTo
-----------------------------------------------------------]]
function meta:AlphaTo( alpha, length, delay, callback )
local anim = self:NewAnimation( length, delay, nil, callback )
anim.Alpha = alpha
anim.Think = AlphaThink
end
local function MoveByThink( anim, panel, fraction )
if ( !anim.StartPos ) then
anim.StartPos = Vector( panel.x, panel.y, 0 )
anim.Pos = anim.StartPos + anim.Pos
end
local pos = LerpVector( fraction, anim.StartPos, anim.Pos )
panel:SetPos( pos.x, pos.y )
end
--[[---------------------------------------------------------
Name: MoveBy
-----------------------------------------------------------]]
function meta:MoveBy( x, y, length, delay, ease, callback )
local anim = self:NewAnimation( length, delay, ease, callback )
anim.Pos = Vector( x, y, 0 )
anim.Think = MoveByThink
end
-- This code is bad and will run forever, never reaching the targetpos.
local function LerpPositions( anim, panel )
if ( !panel.TargetPos ) then return end
local Speed = FrameTime() * 100 * anim.Speed
local Pos = Vector( panel.x, panel.y, 0 )
local Distance = panel.TargetPos - Pos
local Length = Distance:Length()
if ( anim.UseGravity && Length > 1 ) then
Speed = Speed * ( Length * 0.1 )
else
Speed = Speed * 10
end
if ( Length < Speed ) then
panel:SetPosReal( panel.TargetPos.x, panel.TargetPos.y )
panel.TargetPos = nil
return
end
Distance:Normalize()
Distance = Pos + ( Distance * Speed )
panel:SetPosReal( Distance.x, Distance.y )
end
local function NewSetPos( self, x, y )
self.TargetPos = Vector( x, y )
end
local function NewGetPos( self )
return self.TargetPos.x, self.TargetPos.y
end
--[[---------------------------------------------------------
Name: LerpPositions
-----------------------------------------------------------]]
function meta:LerpPositions( speed, usegravity )
if ( self.SetPosReal ) then return end
NewSetPos( self, self:GetPos() )
self.SetPosReal = self.SetPos
self.SetPos = NewSetPos
self.GetPosReal = self.GetPos
self.GetPos = NewGetPos
self.LerpAnim = self:NewAnimation( 86400 )
self.LerpAnim.Speed = speed
self.LerpAnim.UseGravity = usegravity
self.LerpAnim.Think = LerpPositions
end
--
-- DisableLerp
--
function meta:DisableLerp()
self.LerpAnim = nil
self.SetPos = self.SetPosReal
self.GetPos = self.GetPosReal
end

View File

@@ -0,0 +1,585 @@
if ( SERVER ) then return end
dragndrop = {}
function dragndrop.Clear()
dragndrop.m_Receiver = nil
dragndrop.m_ReceiverSlot = nil
dragndrop.m_HoverStart = nil
dragndrop.m_MouseCode = 0
dragndrop.m_DragWatch = nil
dragndrop.m_MouseX = 0
dragndrop.m_MouseY = 0
dragndrop.m_DraggingMain = nil
dragndrop.m_Dragging = nil
dragndrop.m_DropMenu = nil
end
function dragndrop.IsDragging()
if ( dragndrop.m_Dragging != nil ) then return true end
return false
end
function dragndrop.HandleDroppedInGame()
local panel = vgui.GetHoveredPanel()
if ( !IsValid( panel ) ) then return end
if ( panel:GetClassName() != "CGModBase" ) then return end
end
function dragndrop.Drop()
if ( dragndrop.HandleDroppedInGame() ) then
dragndrop.StopDragging()
return
end
-- Show the menu
if ( dragndrop.m_MouseCode == MOUSE_RIGHT && dragndrop.m_ReceiverSlot && dragndrop.m_ReceiverSlot.Menu ) then
local x, y = dragndrop.m_Receiver:LocalCursorPos()
local menu = DermaMenu()
menu.OnRemove = function( m ) -- If user clicks outside of the menu - drop the dragging
dragndrop.StopDragging()
end
for k, v in pairs( dragndrop.m_ReceiverSlot.Menu ) do
local opt = menu:AddOption( v, function()
dragndrop.CallReceiverFunction( true, k, x, y )
dragndrop.StopDragging()
end )
-- HACK: This is lame, but there's no other way that I can see to get icons for these
if ( k == "move" ) then opt:SetIcon( "icon16/arrow_turn_right.png" ) end
if ( k == "copy" ) then opt:SetIcon( "icon16/arrow_branch.png" ) end
end
menu:Open()
dragndrop.m_DropMenu = menu
return
end
dragndrop.CallReceiverFunction( true, nil, nil, nil )
dragndrop.StopDragging()
end
function dragndrop.StartDragging()
if ( !dragndrop.m_DragWatch:IsSelected() ) then
dragndrop.m_Dragging = { dragndrop.m_DragWatch }
else
local canvas = dragndrop.m_DragWatch:GetSelectionCanvas()
dragndrop.m_Dragging = {}
for k, v in pairs( canvas:GetSelectedChildren() ) do
if ( !v.m_DragSlot ) then continue end
table.insert( dragndrop.m_Dragging, v )
end
end
for k, v in pairs( dragndrop.m_Dragging ) do
if ( !IsValid( v ) ) then continue end
v:OnStartDragging()
end
dragndrop.m_DraggingMain = dragndrop.m_DragWatch
dragndrop.m_DraggingMain:MouseCapture( true )
dragndrop.m_DragWatch = nil
end
function dragndrop.StopDragging()
if ( IsValid( dragndrop.m_Receiver ) ) then
dragndrop.m_Receiver:DragHoverEnd()
dragndrop.m_Receiver = nil
end
for k, v in pairs( dragndrop.m_Dragging or {} ) do
if ( !IsValid( v ) ) then continue end
v:OnStopDragging()
end
dragndrop.Clear()
end
function dragndrop.UpdateReceiver()
local hovered = vgui.GetHoveredPanel()
local receiver = nil
local receiverslot = nil
if ( IsValid( hovered ) ) then
receiver, receiverslot = hovered:GetValidReceiverSlot()
end
if ( IsValid( dragndrop.m_Receiver ) ) then
if ( receiver == dragndrop.m_Receiver ) then return end
dragndrop.m_Receiver:DragHoverEnd()
end
if ( !IsValid( receiver ) ) then
dragndrop.m_Receiver = nil
dragndrop.m_ReceiverSlot = nil
end
dragndrop.m_Receiver = receiver
dragndrop.m_ReceiverSlot = receiverslot
end
--
-- Return all the dragged panels that match this name
--
function dragndrop.GetDroppable( name )
if ( !name ) then return dragndrop.m_Dragging end
if ( !dragndrop.m_Dragging ) then return end
local t = {}
for id, pnl in pairs( dragndrop.m_Dragging ) do
if ( pnl.m_DragSlot && pnl.m_DragSlot[ name ] ) then table.insert( t, pnl ) end
end
return t
end
function dragndrop.CallReceiverFunction( bDoDrop, command, mx, my )
if ( !dragndrop.m_ReceiverSlot ) then return end
if ( !IsValid( dragndrop.m_Receiver ) ) then return end
local x, y = dragndrop.m_Receiver:LocalCursorPos()
if ( mx ) then x = mx end
if ( my ) then y = my end
if ( dragndrop.m_ReceiverSlot.Func ) then
local droppable = dragndrop.GetDroppable( dragndrop.m_ReceiverSlot.Name )
dragndrop.m_ReceiverSlot.Func( dragndrop.m_Receiver, droppable, bDoDrop, command, x, y )
end
end
function dragndrop.Think()
if ( IsValid( dragndrop.m_DropMenu ) ) then return end
--
-- We're dragging but no mouse buttons are down..
-- So force the drop whereever it is!
--
--[[if ( dragndrop.m_Dragging != nil && !input.IsMouseDown( MOUSE_LEFT ) && !input.IsMouseDown( MOUSE_RIGHT ) ) then
dragndrop.m_Dragging:DragMouseRelease( dragndrop.m_MouseCode )
return
end]]
--
-- We're holding down a panel, watch for start of dragging
--
if ( IsValid( dragndrop.m_DragWatch ) ) then
local dist = math.abs( dragndrop.m_MouseX - gui.MouseX() ) + math.abs( dragndrop.m_MouseY - gui.MouseY() )
if ( dist > 20 ) then
dragndrop.StartDragging()
return
end
end
if ( dragndrop.m_Dragging != nil ) then
dragndrop.HoverThink()
dragndrop.UpdateReceiver()
if ( IsValid( dragndrop.m_Receiver ) ) then
dragndrop.CallReceiverFunction( false )
end
end
end
hook.Add( "DrawOverlay", "DragNDropPaint", function()
if ( dragndrop.m_Dragging == nil ) then return end
if ( dragndrop.m_DraggingMain == nil ) then return end
if ( IsValid( dragndrop.m_DropMenu ) ) then return end
local hold_offset_x = 65535
local hold_offset_y = 65535
-- Find the top, left most panel
for k, v in pairs( dragndrop.m_Dragging ) do
if ( !IsValid( v ) ) then continue end
hold_offset_x = math.min( hold_offset_x, v.x )
hold_offset_y = math.min( hold_offset_y, v.y )
end
local wasEnabled = DisableClipping( true )
local Alpha = 0.7
if ( IsValid( dragndrop.m_Hovered ) ) then Alpha = 0.8 end
surface.SetAlphaMultiplier( Alpha )
local ox = gui.MouseX() - hold_offset_x + 8
local oy = gui.MouseY() - hold_offset_y + 8
for k, v in pairs( dragndrop.m_Dragging ) do
if ( !IsValid( v ) ) then continue end
local dist = 512 - v:Distance( dragndrop.m_DraggingMain )
if ( dist < 0 ) then continue end
dist = dist / 512
surface.SetAlphaMultiplier( Alpha * dist )
v.PaintingDragging = true
v:PaintAt( ox + v.x - v:GetWide() / 2, oy + v.y - v:GetTall() / 2 ) -- fill the gap between the top left corner and the mouse position
v.PaintingDragging = nil
end
surface.SetAlphaMultiplier( 1.0 )
DisableClipping( wasEnabled )
end )
hook.Add( "Think", "DragNDropThink", dragndrop.Think )
--
--
--
-- Panel Drag n Drop Extensions
--
--
--
local meta = FindMetaTable( "Panel" )
--
-- Make this panel droppable
--
function meta:Droppable( name )
self.m_DragSlot = self.m_DragSlot or {}
self.m_DragSlot[ name ] = {}
return self.m_DragSlot[ name ]
end
--
-- Make this pannel a drop target
--
function meta:Receiver( name, func, menu )
self.m_ReceiverSlot = self.m_ReceiverSlot or {}
self.m_ReceiverSlot[ name ] = {}
self.m_ReceiverSlot[ name ].Name = name
self.m_ReceiverSlot[ name ].Func = func
self.m_ReceiverSlot[ name ].Menu = menu
end
--
-- Drag parent means that when we start to
-- drag this panel, we'll really start
-- dragging the defined parent
--
function meta:SetDragParent( parent )
self.m_pDragParent = parent
end
function meta:GetValidReceiverSlot()
if ( self.m_ReceiverSlot ) then
-- Find matching slot..
for k, v in pairs( self.m_ReceiverSlot ) do
if ( !dragndrop.m_DraggingMain.m_DragSlot ) then continue end
local slot = dragndrop.m_DraggingMain.m_DragSlot[ k ]
if ( !slot ) then continue end
return self, v
end
end
if ( !IsValid( self:GetParent() ) ) then
return false
end
return self:GetParent():GetValidReceiverSlot()
end
function meta:IsDraggable()
return self.m_DragSlot != nil
end
function meta:IsDragging()
if ( !self.m_DragSlot ) then return false end
return self.Dragging
end
function meta:DroppedOn( pnl )
-- For override.
end
function meta:OnDrop()
-- We're being dropped on something
-- we can create a new panel here and return it, so that instead of
-- dropping us - it drops the new panel instead! We remain where we are!
-- By default we return ourself
return self
end
function meta:OnStartDragging()
self.Dragging = true
self:InvalidateLayout()
if ( self:IsSelectable() ) then
local canvas = self:GetSelectionCanvas()
if ( IsValid( canvas ) && !self:IsSelected() ) then
canvas:UnselectAll()
end
end
end
function meta:OnStopDragging()
self.Dragging = false
end
function meta:DragMousePress( mcode )
if ( IsValid( dragndrop.m_DropMenu ) ) then return end
if ( dragndrop.IsDragging() ) then dragndrop.StopDragging() return end
if ( IsValid( self.m_pDragParent ) and self.m_pDragParent ~= self ) then
return self.m_pDragParent:DragMousePress( mcode )
end
if ( !self.m_DragSlot ) then return end
dragndrop.Clear()
dragndrop.m_MouseCode = mcode
dragndrop.m_DragWatch = self
dragndrop.m_MouseX = gui.MouseX()
dragndrop.m_MouseY = gui.MouseY()
end
function meta:DragClick( mcode )
self:MouseCapture( true )
-- Clicking one mouse button while dragging with another!
-- Return true to stop us clicking and selecting stuff below..
return true
end
function meta:DragMouseRelease( mcode )
if ( IsValid( dragndrop.m_DropMenu ) ) then return end
-- This wasn't the button we clicked with - so don't release drag
if ( dragndrop.IsDragging() && dragndrop.m_MouseCode != mcode ) then
return self:DragClick( mcode )
end
if ( !dragndrop.IsDragging() ) then
dragndrop.Clear()
return false
end
dragndrop.Drop()
-- Todo.. we should only do this if we enabled it!
if ( gui.EnableScreenClicker ) then
gui.EnableScreenClicker( false )
end
self:MouseCapture( false )
return true
end
function meta:SetDropTarget( x, y, w, h )
if ( !self.m_bDrawingPaintOver ) then
self.m_OldPaintOver = self.PaintOver
self.m_bDrawingPaintOver = true
end
self.PaintOver = function()
if ( self.m_OldPaintOver ) then
self:m_OldPaintOver()
end
self:DrawDragHover( x, y, w, h )
end
end
--
-- Drag Hover
--
-- These functions are used for things like trees
-- So that when you hover over the tree while dragging something
-- it will open up the tree. This works regardless of whether the
-- is droppable or not.
--
-- Implement DragHoverClick in your panel class to get this functionality
--
function meta:DragHover( HoverTime )
--
-- Call DragHoverClick if we've been hovering for 0.1 seconds..
--
if ( HoverTime < 0.1 ) then dragndrop.m_bHoverClick = false end
if ( HoverTime > 0.1 && !dragndrop.m_bHoverClick ) then
self:DragHoverClick( HoverTime )
dragndrop.m_bHoverClick = true
end
end
function meta:DrawDragHover( x, y, w, h )
DisableClipping( true )
surface.SetDrawColor( 255, 0, 255, 100 )
surface.DrawRect( x, y, w, h )
surface.SetDrawColor( 255, 220, 255, 230 )
surface.DrawOutlinedRect( x, y, w, h )
surface.SetDrawColor( 255, 100, 255, 50 )
surface.DrawOutlinedRect( x - 1, y - 1, w + 2, h + 2 )
DisableClipping( false )
end
function meta:DragHoverEnd()
if ( !self.m_bDrawingPaintOver ) then return end
self.PaintOver = self.m_OldPaintOver
self.m_bDrawingPaintOver = false
end
function meta:DragHoverClick( HoverTime )
end
--
--
-- This is called to open stuff when you're hovering over it.
--
--
local LastHoverThink = nil
local LastHoverChangeTime = 0
local LastX = 0
local LastY = 0
function dragndrop.HoverThink()
local hovered = vgui.GetHoveredPanel()
local x = gui.MouseX()
local y = gui.MouseY()
-- Hovering a different panel
if ( LastHoverThink != hovered or x != LastX or y != LastY ) then
LastHoverChangeTime = SysTime()
LastHoverThink = hovered
end
-- Hovered panel might do stuff when we're hovering it
-- so give it a chance to do that now.
if ( IsValid( LastHoverThink ) ) then
LastX = x
LastY = y
LastHoverThink:DragHover( SysTime() - LastHoverChangeTime )
end
end

View File

@@ -0,0 +1,129 @@
if ( SERVER ) then return end
local PanelFactory = {}
local panel_metatable = FindMetaTable( "Panel" )
baseclass.Set( "Panel", panel_metatable )
baseclass.Set( "Label", panel_metatable )
baseclass.Set( "EditablePanel", panel_metatable )
-- Keep the old function
vgui.CreateX = vgui.Create
function vgui.GetControlTable( classname )
return PanelFactory[ classname ]
end
function vgui.Exists( classname )
return PanelFactory[ classname ] != nil
end
function vgui.Create( classname, parent, name )
-- Is this a user-created panel?
if ( PanelFactory[ classname ] ) then
local metatable = PanelFactory[ classname ]
local panel = vgui.Create( metatable.Base, parent, name or classname )
if ( !panel ) then
Error( "Tried to create panel with invalid base '" .. metatable.Base .. "'\n" );
end
table.Merge( panel:GetTable(), metatable )
panel.BaseClass = PanelFactory[ metatable.Base ]
panel.ClassName = classname
-- Call the Init function if we have it
if ( panel.Init ) then
panel:Init()
end
panel:Prepare()
return panel
end
return vgui.CreateX( classname, parent, name or classname )
end
function vgui.CreateFromTable( metatable, parent, name )
if ( !istable( metatable ) ) then return nil end
local panel = vgui.Create( metatable.Base, parent, name )
table.Merge( panel:GetTable(), metatable )
panel.BaseClass = PanelFactory[ metatable.Base ]
-- Call the Init function if we have it
if ( panel.Init ) then
panel:Init()
end
panel:Prepare()
return panel
end
function vgui.Register( classname, mtable, base )
-- Remove the global
PANEL = nil
-- Default base is Panel
mtable.Base = base or "Panel"
mtable.Init = mtable.Init or function() end
PanelFactory[ classname ] = mtable
baseclass.Set( classname, mtable )
local mt = {}
mt.__index = function( t, k )
if ( PanelFactory[ mtable.Base ] && PanelFactory[ mtable.Base ][k] ) then return PanelFactory[ mtable.Base ][k] end
return panel_metatable[k]
end
setmetatable( mtable, mt )
return mtable
end
function vgui.RegisterTable( mtable, base )
-- Remove the global
PANEL = nil
mtable.Base = base or "Panel"
mtable.Init = mtable.Init or function() end
return mtable
end
function vgui.RegisterFile( filename )
local OldPanel = PANEL
PANEL = {}
-- The included file should fill the PANEL global.
include( filename )
local mtable = PANEL
PANEL = OldPanel
mtable.Base = mtable.Base or "Panel"
mtable.Init = mtable.Init or function() end
return mtable
end

View File

@@ -0,0 +1,249 @@
if ( SERVER ) then return end
local StartX = 0
local StartY = 0
local SelectionCanvas = nil
local meta = FindMetaTable( "Panel" )
function meta:SetSelectionCanvas( bSet )
self.m_bSelectionCanvas = bSet
self:SetMouseInputEnabled( true )
end
function meta:IsSelectionCanvas()
return self.m_bSelectionCanvas
end
function meta:SetSelectable( bSet )
self.m_bSelectable = bSet
end
function meta:ToggleSelection()
self:SetSelected( !self.m_bSelected )
end
function meta:UnselectAll()
self:SetSelected( false )
for k, v in ipairs( self:GetChildren() ) do
v:UnselectAll()
end
end
function meta:SetSelected( bSet )
if ( self.m_bSelected == bSet ) then return end
self.m_bSelected = bSet
if ( self.ApplySchemeSettings ) then
self:ApplySchemeSettings()
end
end
function meta:IsSelected( bSet )
if ( !self:IsSelectable() ) then return false end
return self.m_bSelected == true
end
function meta:IsSelectable()
return self.m_bSelectable == true
end
local function GetSelectionRect()
if ( !SelectionCanvas ) then
debug.Trace()
return
end
local CurX, CurY = SelectionCanvas:ScreenToLocal( gui.MouseX(), gui.MouseY() )
local x = math.min( CurX, StartX )
local y = math.min( CurY, StartY )
local w = math.abs( CurX - StartX )
local h = math.abs( CurY - StartY )
return x, y, w, h
end
function meta:DrawSelections()
if ( !self.m_bSelectable ) then return end
if ( !self.m_bSelected ) then return end
local w, h = self:GetSize()
surface.SetDrawColor( 255, 0, 255, 100 )
surface.DrawRect( 0, 0, w, h )
end
local function PaintSelectionBox( self )
if ( !IsValid( SelectionCanvas ) ) then return end
local x, y, w, h = GetSelectionRect()
surface.SetDrawColor( 255, 0, 255, 50 )
surface.DrawRect( x, y, w, h )
surface.SetDrawColor( 255, 200, 255, 200 )
surface.DrawOutlinedRect( x, y, w, h )
end
function meta:GetSelectionCanvas()
if ( !self.m_bSelectionCanvas ) then
local parent = self:GetParent()
if ( IsValid( parent ) ) then
return parent:GetSelectionCanvas()
end
return nil
end
return self
end
function meta:StartBoxSelection()
if ( !self.m_bSelectionCanvas ) then
local parent = self:GetParent()
if ( IsValid( parent ) ) then
return parent:StartBoxSelection()
end
return
end
self:MouseCapture( true )
if ( !input.IsShiftDown() && !input.IsControlDown() ) then
self:UnselectAll()
end
SelectionCanvas = self
StartX, StartY = self:ScreenToLocal( gui.MouseX(), gui.MouseY() )
self.PaintOver_Old = self.PaintOver
self.PaintOver = PaintSelectionBox
end
function meta:GetChildrenInRect( x, y, w, h )
local tab = {}
for k, v in ipairs( self:GetChildren() ) do
local vw, vh = v:GetSize()
if ( !self:IsVisible() ) then continue end
if ( x > v.x + vw ) then continue end
if ( y > v.y + vh ) then continue end
if ( v.x > x + w ) then continue end
if ( v.y > y + h ) then continue end
if ( v.m_bSelectable ) then
table.insert( tab, v )
end
table.Add( tab, v:GetChildrenInRect( x - v.x, y - v.y, w, h ) )
end
return tab
end
function meta:GetSelectedChildren()
local tab = {}
for k, v in ipairs( self:GetChildren() ) do
if ( v:IsSelected() ) then
table.insert( tab, v )
end
table.Add( tab, v:GetSelectedChildren() )
end
return tab
end
function meta:NumSelectedChildren()
local i = 0
for k, v in ipairs( self:GetChildren() ) do
if ( v:IsSelected() ) then
i = i + 1
end
end
return i
end
function meta:EndBoxSelection()
if ( SelectionCanvas != self ) then return false end
self:MouseCapture( false )
self.PaintOver = self.PaintOver_Old
self.PaintOver_Old = nil
for k, v in ipairs( self:GetChildrenInRect( GetSelectionRect() ) ) do
-- If player is holding shift, add new planels to existing selections, do not toggle
-- This mimics already familiar behavior of Windows Explorer, etc
if ( input.IsShiftDown() ) then
v:SetSelected( true )
else
v:ToggleSelection()
end
end
SelectionCanvas = nil
StartX, StartY = 0, 0
return true
end

View File

@@ -0,0 +1,76 @@
if ( SERVER ) then return end
local meta = FindMetaTable( "Player" )
local playerOptions = {}
local bindTranslation = {}
bindTranslation["slot1"] = 1
bindTranslation["slot2"] = 2
bindTranslation["slot3"] = 3
bindTranslation["slot4"] = 4
bindTranslation["slot5"] = 5
bindTranslation["slot6"] = 6
bindTranslation["slot7"] = 7
bindTranslation["slot8"] = 8
bindTranslation["slot9"] = 9
bindTranslation["slot0"] = 0
--[[---------------------------------------------------------
Name: PlayerOption
Params: <name> <timeout> <input function> <draw function>
Desc:
-----------------------------------------------------------]]
function meta:AddPlayerOption( name, timeout, in_func, draw_func )
local option = {}
option.timeout = timeout
option.in_func = in_func
option.draw_func = draw_func
if (timeout != -1) then
option.timeout = CurTime() + timeout
end
playerOptions[ name ] = option
end
local function hook_PlayerOptionInput( pl, bind, down )
if (!down || !bindTranslation[bind]) then return end
for k, v in pairs( playerOptions ) do
if ( v.timeout == -1 || v.timeout > CurTime() ) then
-- If the function returns true then remove this player option
if ( v.in_func( bindTranslation[bind] ) ) then
playerOptions[k] = nil
end
return true
else
playerOptions[k] = nil
end
end
end
hook.Add( "PlayerBindPress", "PlayerOptionInput", hook_PlayerOptionInput )
local function hook_PlayerOptionDraw()
for k, v in pairs( playerOptions ) do
if (v.draw_func) then v.draw_func() end
return
end
end
hook.Add( "HUDPaint", "PlayerOptionDraw", hook_PlayerOptionDraw )

View File

@@ -0,0 +1,196 @@
-- We don't want this to run in menu state, and render.GetAmbientLightColor doesn't exist in menu state
if ( !render || !render.GetAmbientLightColor ) then return end
--[[---------------------------------------------------------
Short aliases for stencil constants
-----------------------------------------------------------]]
STENCIL_NEVER = STENCILCOMPARISONFUNCTION_NEVER
STENCIL_LESS = STENCILCOMPARISONFUNCTION_LESS
STENCIL_EQUAL = STENCILCOMPARISONFUNCTION_EQUAL
STENCIL_LESSEQUAL = STENCILCOMPARISONFUNCTION_LESSEQUAL
STENCIL_GREATER = STENCILCOMPARISONFUNCTION_GREATER
STENCIL_NOTEQUAL = STENCILCOMPARISONFUNCTION_NOTEQUAL
STENCIL_GREATEREQUAL = STENCILCOMPARISONFUNCTION_GREATEREQUAL
STENCIL_ALWAYS = STENCILCOMPARISONFUNCTION_ALWAYS
STENCIL_KEEP = STENCILOPERATION_KEEP
STENCIL_ZERO = STENCILOPERATION_ZERO
STENCIL_REPLACE = STENCILOPERATION_REPLACE
STENCIL_INCRSAT = STENCILOPERATION_INCRSAT
STENCIL_DECRSAT = STENCILOPERATION_DECRSAT
STENCIL_INVERT = STENCILOPERATION_INVERT
STENCIL_INCR = STENCILOPERATION_INCR
STENCIL_DECR = STENCILOPERATION_DECR
--[[---------------------------------------------------------
Name: ClearRenderTarget
Params: <texture> <color>
Desc: Clear a render target
-----------------------------------------------------------]]
function render.ClearRenderTarget( rt, color )
render.PushRenderTarget( rt )
render.Clear( color.r, color.g, color.b, color.a )
render.PopRenderTarget()
end
--[[---------------------------------------------------------
Name: SupportsHDR
Params:
Desc: Return true if the client supports HDR
-----------------------------------------------------------]]
function render.SupportsHDR( )
if ( render.GetDXLevel() < 80 ) then return false end
return true
end
--[[---------------------------------------------------------
Name: CopyTexture
Params: <texture from> <texture to>
Desc: Copy the contents of one texture to another
-----------------------------------------------------------]]
function render.CopyTexture( from, to )
render.PushRenderTarget( from )
render.CopyRenderTargetToTexture( to )
render.PopRenderTarget()
end
local matColor = Material( "color" )
function render.SetColorMaterial()
render.SetMaterial( matColor )
end
local matColorIgnoreZ = Material( "color_ignorez" )
function render.SetColorMaterialIgnoreZ()
render.SetMaterial( matColorIgnoreZ )
end
local mat_BlurX = Material( "pp/blurx" )
local mat_BlurY = Material( "pp/blury" )
local tex_Bloom1 = render.GetBloomTex1()
function render.BlurRenderTarget( rt, sizex, sizey, passes )
mat_BlurX:SetTexture( "$basetexture", rt )
mat_BlurY:SetTexture( "$basetexture", tex_Bloom1 )
mat_BlurX:SetFloat( "$size", sizex )
mat_BlurY:SetFloat( "$size", sizey )
for i=1, passes+1 do
render.SetRenderTarget( tex_Bloom1 )
render.SetMaterial( mat_BlurX )
render.DrawScreenQuad()
render.SetRenderTarget( rt )
render.SetMaterial( mat_BlurY )
render.DrawScreenQuad()
end
end
local camera2DTable = { type = "2D" }
function cam.Start2D()
return cam.Start( camera2DTable )
end
function cam.Start3D( pos, ang, fov, x, y, w, h, znear, zfar )
local tab = {}
tab.type = "3D"
tab.origin = pos
tab.angles = ang
if ( fov != nil ) then tab.fov = fov end
if ( x != nil && y != nil && w != nil && h != nil ) then
tab.x = x
tab.y = y
tab.w = w
tab.h = h
tab.aspect = ( w / h )
end
if ( znear != nil && zfar != nil ) then
tab.znear = znear
tab.zfar = zfar
end
return cam.Start( tab )
end
local matFSB = Material( "pp/motionblur" )
function render.DrawTextureToScreen( tex )
matFSB:SetFloat( "$alpha", 1.0 )
matFSB:SetTexture( "$basetexture", tex )
render.SetMaterial( matFSB )
render.DrawScreenQuad()
end
function render.DrawTextureToScreenRect( tex, x, y, w, h )
matFSB:SetFloat( "$alpha", 1.0 )
matFSB:SetTexture( "$basetexture", tex )
render.SetMaterial( matFSB )
render.DrawScreenQuadEx( x, y, w, h )
end
--
-- This isn't very fast. If you're doing something every frame you should find a way to
-- cache a ClientsideModel and keep it around! This is fine for rendering to a render
-- target once - or something.
--
function render.Model( tbl, ent )
local inent = ent
if ( ent == nil ) then
ent = ClientsideModel( tbl.model or "error.mdl", RENDERGROUP_OTHER )
end
if ( !IsValid( ent ) ) then return end
ent:SetModel( tbl.model or "error.mdl" )
ent:SetNoDraw( true )
ent:SetPos( tbl.pos or vector_origin )
ent:SetAngles( tbl.angle or angle_zero )
ent:DrawModel()
--
-- If we created the model, then remove it!
--
if ( inent != ent ) then
ent:Remove()
end
end

View File

@@ -0,0 +1,26 @@
--
-- The client needs this file
--
AddCSLuaFile()
if ( !coroutine ) then return end
--
-- Name: coroutine.wait
-- Desc: Yield's the coroutine for so many seconds before returning.\n\nThis should only be called in a coroutine. This function uses CurTime() - not RealTime().
-- Arg1: number|seconds|The number of seconds to wait
-- Ret1:
--
function coroutine.wait( seconds )
local endtime = CurTime() + seconds
while ( true ) do
if ( endtime < CurTime() ) then return end
coroutine.yield()
end
end

View File

@@ -0,0 +1,51 @@
if ( !debug ) then return end
--[[---------------------------------------------------------
Name: Trace
Desc: Dumps a trace to the console..
Trace:
1: Line 21 "Trace" includes/extensions/debug.lua
2: Line 222 "WriteTable" includes/modules/saverestore.lua
3: Line 170 "WriteVar" includes/modules/saverestore.lua
4: Line 259 "WriteTable" includes/modules/saverestore.lua
5: Line 170 "WriteVar" includes/modules/saverestore.lua
6: Line 259 "WriteTable" includes/modules/saverestore.lua
7: Line 272 "Func" includes/extensions/entity_networkvars.lua
8: Line 396 "(null)" includes/modules/saverestore.lua
This trace shows that the function was called from the engine (line 8) in save restore.
Save restore then called something in entity_networkvars for some reason. Then
that function called WriteTable(6), which called other functions until it got to the trace
in 1 which was called by WriteTable in saverestore.lua
-----------------------------------------------------------]]
function debug.Trace()
local level = 1
Msg( "\nTrace:\n" )
while true do
local info = debug.getinfo( level, "Sln" )
if ( !info ) then break end
if ( info.what ) == "C" then
Msg( string.format( "\t%i: C function\t\"%s\"\n", level, info.name ) )
else
Msg( string.format( "\t%i: Line %d\t\"%s\"\t\t%s\n", level, info.currentline, info.name, info.short_src ) )
end
level = level + 1
end
Msg( "\n" )
end

View File

@@ -0,0 +1,712 @@
local meta = FindMetaTable( "Entity" )
-- Return if there's nothing to add on to
if ( !meta ) then return end
function meta:SetSpawnFlags( flags )
self:SetKeyValue( "spawnflags", flags )
end
function meta:AddSpawnFlags( flags )
self:SetKeyValue( "spawnflags", bit.bor( self:GetSpawnFlags(), flags ) )
end
function meta:RemoveSpawnFlags( flags )
self:SetKeyValue( "spawnflags", bit.band( self:GetSpawnFlags(), bit.bnot( flags ) ) )
end
function meta:GetShouldPlayPickupSound()
return self.m_bPlayPickupSound or false
end
function meta:SetShouldPlayPickupSound( bPlaySound )
self.m_bPlayPickupSound = tobool( bPlaySound ) or false
end
--
-- Entity index accessor. This used to be done in engine, but it's done in Lua now because it's faster
--
function meta:__index( key )
--
-- Search the metatable. We can do this without dipping into C, so we do it first.
--
local val = meta[ key ]
if ( val != nil ) then return val end
--
-- Search the entity table
--
local tab = meta.GetTable( self )
if ( tab ) then
local tabval = tab[ key ]
if ( tabval != nil ) then return tabval end
end
--
-- Legacy: sometimes use self.Owner to get the owner.. so lets carry on supporting that stupidness
-- This needs to be retired, just like self.Entity was.
--
if ( key == "Owner" ) then return meta.GetOwner( self ) end
return nil
end
--[[---------------------------------------------------------
Name: Short cut to add entities to the table
-----------------------------------------------------------]]
function meta:GetVar( name, default )
local Val = self:GetTable()[ name ]
if ( Val == nil ) then return default end
return Val
end
if ( SERVER ) then
function meta:SetCreator( ply --[[= NULL]] )
if ( ply == nil ) then
ply = NULL
elseif ( !isentity( ply ) ) then
error( "bad argument #1 to 'SetCreator' (Entity expected, got " .. type( ply ) .. ")", 2 )
end
self.m_PlayerCreator = ply
end
function meta:GetCreator()
return self.m_PlayerCreator or NULL
end
end
--[[---------------------------------------------------------
Name: Returns true if the entity has constraints attached to it
-----------------------------------------------------------]]
function meta:IsConstrained()
if ( CLIENT ) then return self:GetNWBool( "IsConstrained" ) end
local c = self:GetTable().Constraints
local bIsConstrained = false
if ( c ) then
for k, v in pairs( c ) do
if ( IsValid( v ) ) then bIsConstrained = true break end
c[ k ] = nil
end
end
self:SetNWBool( "IsConstrained", bIsConstrained )
return bIsConstrained
end
--[[---------------------------------------------------------
Name: Short cut to set tables on the entity table
-----------------------------------------------------------]]
function meta:SetVar( name, value )
self:GetTable()[ name ] = value
end
--[[---------------------------------------------------------
Name: CallOnRemove
Desc: Call this function when this entity dies.
Calls the function like Function( <entity>, <optional args> )
-----------------------------------------------------------]]
function meta:CallOnRemove( name, func, ... )
local mytable = self:GetTable()
mytable.OnDieFunctions = mytable.OnDieFunctions or {}
mytable.OnDieFunctions[ name ] = { Name = name, Function = func, Args = { ... } }
end
--[[---------------------------------------------------------
Name: RemoveCallOnRemove
Desc: Removes the named hook
-----------------------------------------------------------]]
function meta:RemoveCallOnRemove( name )
local mytable = self:GetTable()
mytable.OnDieFunctions = mytable.OnDieFunctions or {}
mytable.OnDieFunctions[ name ] = nil
end
--[[---------------------------------------------------------
Simple mechanism for calling the die functions.
-----------------------------------------------------------]]
local function DoDieFunction( ent )
if ( !ent or !ent.OnDieFunctions ) then return end
for k, v in pairs( ent.OnDieFunctions ) do
-- Functions aren't saved - so this could be nil if we loaded a game.
if ( v && v.Function ) then
v.Function( ent, unpack( v.Args ) )
end
end
end
hook.Add( "EntityRemoved", "DoDieFunction", DoDieFunction )
function meta:PhysWake()
local phys = self:GetPhysicsObject()
if ( !IsValid( phys ) ) then return end
phys:Wake()
end
-- This makes these a bit faster
function meta:GetColor()
return Color( self:GetColor4Part() )
end
function meta:SetColor( col )
-- Backwards compatibility
if ( !col ) then
return self:SetColor4Part( 255, 255, 255, 255 )
end
self:SetColor4Part( col.r, col.g, col.b, col.a )
end
function meta:GetChildBones( bone )
local bonecount = self:GetBoneCount()
if ( bonecount == 0 or bonecount < bone ) then return end
local bones = {}
for k = 0, bonecount - 1 do
if ( self:GetBoneParent( k ) != bone ) then continue end
table.insert( bones, k )
end
return bones
end
function DTVar_ReceiveProxyGL( ent, name, id, val )
if ( ent.CallDTVarProxies ) then
ent:CallDTVarProxies( name, id, val )
end
end
function meta:InstallDataTable()
self.dt = {}
local typetable = {}
local datatable = {}
local keytable = {}
local dtmeta = {}
local editing = {}
dtmeta.__index = function ( ent, key )
local dt = datatable[ key ]
if ( dt == nil ) then return end
return dt.GetFunc( self, dt.index, key )
end
dtmeta.__newindex = function( ent, key, value )
local dt = datatable[ key ]
if ( dt == nil ) then return end
dt.SetFunc( self, dt.index, value )
end
local function FindUnusedIndex( typename )
local tbl = typetable[ typename ]
if ( !tbl ) then return 0 end
for i = 0, 31 do
if ( !tbl[i] ) then return i end
end
end
self.IsDTVarSlotUsed = function( ent, typename, index )
local tbl = typetable[ typename ]
if ( !tbl or !tbl[index] ) then return false end
return true
end
self.DTVar = function( ent, typename, index, name )
if ( isstring( index ) && !name ) then
name = index
index = FindUnusedIndex( typename )
elseif ( !index && isstring( name ) ) then
index = FindUnusedIndex( typename )
end
local SetFunc = ent[ "SetDT" .. typename ]
local GetFunc = ent[ "GetDT" .. typename ]
if ( !SetFunc or !GetFunc ) then
MsgN( "Couldn't addvar ", name, " - type ", typename, " is invalid!" )
return
end
local data = {
index = index,
name = name,
SetFunc = SetFunc,
GetFunc = GetFunc,
typename = typename,
Notify = {}
}
typetable[ typename ] = typetable[ typename ] or {}
typetable[ typename ][ index ] = data
datatable[ name ] = data
return data
end
--
-- Access to the editing table
--
self.GetEditingData = function()
return editing
end
--
-- Adds an editable variable.
--
self.SetupEditing = function( ent, name, keyname, data )
if ( !data ) then return end
if ( !data.title ) then data.title = name end
editing[ keyname ] = data
end
self.SetupKeyValue = function( ent, keyname, kvtype, setfunc, getfunc, other_data )
keyname = keyname:lower()
keytable[ keyname ] = {
KeyName = keyname,
Set = setfunc,
Get = getfunc,
Type = kvtype
}
if ( other_data ) then
table.Merge( keytable[ keyname ], other_data )
end
end
local CallProxies = function( ent, tbl, name, oldval, newval )
for i = 1, #tbl do
tbl[ i ]( ent, name, oldval, newval )
end
end
self.CallDTVarProxies = function( ent, typename, index, newVal )
local t = typetable[ typename ] && typetable[ typename ][ index ] or nil
if ( t ) then
CallProxies( ent, t.Notify, t.name, t.GetFunc( ent, index ), newVal )
end
end
self.NetworkVar = function( ent, typename, index, name, other_data )
if ( isstring( index ) && ( istable( name ) or !name ) ) then
other_data = name
name = index
index = FindUnusedIndex( typename )
elseif ( !index && isstring( name ) ) then
index = FindUnusedIndex( typename )
end
local t = ent.DTVar( ent, typename, index, name )
-- Some addons call these on the entity table, and that used to work, so we keep that
ent[ "Set" .. name ] = function( selfent, value )
CallProxies( ent, t.Notify, name, t.GetFunc( ent, index ), value )
t.SetFunc( ent, index, value )
end
ent[ "Get" .. name ] = function( selfent )
return t.GetFunc( ent, index )
end
if ( !other_data ) then return end
-- This KeyName stuff is absolutely unnecessary, there's absolutely no reason for it to exist
-- But we cannot remove it now because dupes will break. It should've used the "name" variable
if ( other_data.KeyName ) then
ent:SetupKeyValue( other_data.KeyName, typename, ent[ "Set" .. name ], ent[ "Get" .. name ], other_data )
ent:SetupEditing( name, other_data.KeyName, other_data.Edit )
end
end
--
-- Add a function that gets called when the variable changes
-- Note: this doesn't work on the client yet - which drastically reduces its usefulness.
--
self.NetworkVarNotify = function( ent, name, func )
if ( !datatable[ name ] ) then error( "calling NetworkVarNotify on missing network var " .. name ) end
table.insert( datatable[ name ].Notify, func )
end
--
-- Create an accessor of an element. This is mainly so you can use spare
-- network vars (vectors, angles) to network single floats.
--
self.NetworkVarElement = function( ent, typename, index, element, name, other_data )
if ( isstring( index ) && isstring( element ) ) then
other_data = name
name = element
element = index
index = FindUnusedIndex( typename )
elseif ( !index && isstring( name ) ) then
index = FindUnusedIndex( typename )
end
local t = ent.DTVar( ent, typename, index, name )
t.element = element
ent[ "Set" .. name ] = function( selfent, value )
local old = t.GetFunc( selfent, index )
old[ element ] = value
t.SetFunc( selfent, index, old )
end
ent[ "Get" .. name ] = function( selfent )
return t.GetFunc( selfent, index )[ element ]
end
if ( !other_data ) then return end
-- This KeyName stuff is absolutely unnecessary, there's absolutely no reason for it to exist
-- But we cannot remove it now because dupes will break. It should've used the "name" variable
if ( other_data.KeyName ) then
ent:SetupKeyValue( other_data.KeyName, "float", ent[ "Set" .. name ], ent[ "Get" .. name ], other_data )
ent:SetupEditing( name, other_data.KeyName, other_data.Edit )
end
end
self.SetNetworkKeyValue = function( ent, key, value )
key = key:lower()
local k = keytable[ key ]
if ( !k ) then return end
local v = util.StringToType( value, k.Type )
if ( v == nil ) then return end
k.Set( ent, v )
return true
end
self.SetNetworkVarsFromMapInput = function( ent, name, data )
name = name:lower()
if ( !string.StartsWith( name, "set" ) ) then return end
name = string.sub( name, 4 )
if ( name == "" ) then return end
-- Only allow setting variables that were marked as editable!
local k = keytable[ name ]
if ( !k || !k.Edit ) then return end
local v = util.StringToType( data, k.Type )
if ( v == nil ) then return end
-- Special case for colors. FGD (maps) use color255 format, while colors in this system use unit vectors.
if ( k.Edit.type == "VectorColor" ) then
v = v / 255
end
k.Set( ent, v )
return true
end
self.GetNetworkKeyValue = function( ent, key )
key = key:lower()
local k = keytable[ key ]
if ( !k ) then return end
return k.Get( ent )
end
--
-- Called by the duplicator system to get the network vars
--
self.GetNetworkVars = function( ent )
local dt = {}
for k, v in pairs( datatable ) do
-- Don't try to save entities (yet?)
if ( v.typename == "Entity" ) then continue end
if ( v.element ) then
dt[ k ] = v.GetFunc( ent, v.index )[ v.element ]
else
dt[ k ] = v.GetFunc( ent, v.index )
end
end
--
-- If there's nothing in our table - then return nil.
--
if ( table.IsEmpty( dt ) ) then return nil end
return dt
end
--
-- Called by the duplicator system to restore from network vars
--
self.RestoreNetworkVars = function( ent, tab )
if ( !tab ) then return end
-- Loop this entities data table
for k, v in pairs( datatable ) do
-- If it contains this entry
if ( tab[ k ] == nil ) then continue end
-- Support old saves/dupes with incorrectly saved data
if ( v.element && ( isangle( tab[ k ] ) or isvector( tab[ k ] ) ) ) then
tab[ k ] = tab[ k ][ v.element ]
end
-- Set it.
if ( ent[ "Set" .. k ] ) then
ent[ "Set" .. k ]( ent, tab[ k ] )
else
v.SetFunc( ent, v.index, tab[ k ] )
end
end
end
setmetatable( self.dt, dtmeta )
--
-- In sandbox the client can edit certain values on certain entities
-- we implement this here incase any other gamemodes want to use it
-- although it is of course deactivated by default.
--
--
-- This function takes a keyname and a value - both strings.
--
--
-- Called serverside it will set the value.
--
self.EditValue = function( ent, variable, value )
if ( !isstring( variable ) ) then return end
if ( !isstring( value ) ) then return end
--
-- It can be called clientside to send a message to the server
-- to request a change of value.
--
if ( CLIENT ) then
net.Start( "editvariable" )
net.WriteEntity( ent )
net.WriteString( variable )
net.WriteString( value )
net.SendToServer()
end
--
-- Called serverside it simply changes the value
--
if ( SERVER ) then
ent:SetNetworkKeyValue( variable, value )
end
end
end
if ( SERVER ) then
util.AddNetworkString( "editvariable" )
net.Receive( "editvariable", function( len, client )
local ent = net.ReadEntity()
if ( !IsValid( ent ) ) then return end
if ( !isfunction( ent.GetEditingData ) ) then return end
if ( ent.AdminOnly && !( client:IsAdmin() or game.SinglePlayer() ) ) then return end
local key = net.ReadString()
-- Is this key in our edit table?
local editor = ent:GetEditingData()[ key ]
if ( !istable( editor ) ) then return end
local val = net.ReadString()
hook.Run( "VariableEdited", ent, client, key, val, editor )
end )
function meta:GetUnFreezable()
return self.m_bUnFreezable or false
end
function meta:SetUnFreezable( bFreeze )
self.m_bUnFreezable = tobool( bFreeze ) or false
end
end
--
-- Networked var proxies
--
function meta:SetNetworked2VarProxy( name, func )
if ( !self.NWVarProxies ) then
self.NWVarProxies = {}
end
self.NWVarProxies[ name ] = func
end
function meta:GetNetworked2VarProxy( name )
if ( self.NWVarProxies ) then
local func = self.NWVarProxies[ name ]
if ( isfunction( func ) ) then
return func
end
end
return nil
end
meta.SetNW2VarProxy = meta.SetNetworked2VarProxy
meta.GetNW2VarProxy = meta.GetNetworked2VarProxy
hook.Add( "EntityNetworkedVarChanged", "NetworkedVars", function( ent, name, oldValue, newValue )
if ( ent.NWVarProxies ) then
local func = ent.NWVarProxies[ name ]
if ( isfunction( func ) ) then
func( ent, name, oldValue, newValue )
end
end
end )
--
-- Vehicle Extensions
--
local vehicle = FindMetaTable( "Vehicle" )
--
-- We steal some DT slots by default for vehicles
-- to control the third person view. You should use
-- these functions if you want to play with them because
-- they might eventually be moved into the engine - so manually
-- editing the DT values will stop working.
--
function vehicle:SetVehicleClass( s )
self:SetDTString( 3, s )
end
function vehicle:GetVehicleClass()
return self:GetDTString( 3 )
end
function vehicle:SetThirdPersonMode( b )
self:SetDTBool( 3, b )
end
function vehicle:GetThirdPersonMode()
return self:GetDTBool( 3 )
end
function vehicle:SetCameraDistance( dist )
self:SetDTFloat( 3, dist )
end
function vehicle:GetCameraDistance()
return self:GetDTFloat( 3 )
end

View File

@@ -0,0 +1,39 @@
local inext = ipairs( {} )
local EntityCache = nil
function ents.Iterator()
if ( EntityCache == nil ) then EntityCache = ents.GetAll() end
return inext, EntityCache, 0
end
local PlayerCache = nil
function player.Iterator()
if ( PlayerCache == nil ) then PlayerCache = player.GetAll() end
return inext, PlayerCache, 0
end
-- Called by the engine just before OnEntityCreated & just after EntityRemoved hooks
function InvalidateInternalEntityCache( isPly )
EntityCache = nil
if ( isPly ) then PlayerCache = nil end
end
local function InvalidateInternalEntityCacheOld( ent )
InvalidateInternalEntityCache( ent:IsPlayer() )
end
-- These are for non-updated servers
-- TODO: Remove me after update is released (past June 2025)
hook.Add( "OnEntityCreated", "ents.Iterator", InvalidateInternalEntityCacheOld )
hook.Add( "EntityRemoved", "ents.Iterator", InvalidateInternalEntityCacheOld )

View File

@@ -0,0 +1,20 @@
function ents.FindByClassAndParent( classname, entity )
if ( !IsValid( entity ) ) then return end
local out = {}
for k, v in ipairs( ents.FindByClass( classname ) ) do
if ( v:GetParent() == entity ) then
table.insert( out, v )
end
end
if ( out[1] == nil ) then return end
return out
end

View File

@@ -0,0 +1,41 @@
function file.Read( filename, path )
if ( path == true ) then path = "GAME" end
if ( path == nil || path == false ) then path = "DATA" end
local f = file.Open( filename, "rb", path )
if ( !f ) then return nil end
local str = f:Read( f:Size() )
f:Close()
if ( !str ) then str = "" end
return str
end
function file.Write( filename, contents )
local f = file.Open( filename, "wb", "DATA" )
if ( !f ) then return false end
f:Write( contents )
f:Close()
return true
end
function file.Append( filename, contents )
local f = file.Open( filename, "ab", "DATA" )
if ( !f ) then return false end
f:Write( contents )
f:Close()
return true
end

View File

@@ -0,0 +1,54 @@
local AmmoTypes = {}
--
-- Called by modders to add a new ammo type.
-- Ammo types aren't something you can add on the fly. You have one
-- opportunity during loadtime. The ammo types should also be IDENTICAL on
-- server and client.
-- If they're not you will receive errors and maybe even crashes.
--
--
-- game.AddAmmoType(
-- {
-- name = "customammo",
-- dmgtype = DMG_BULLET,
-- tracer = TRACER_LINE_AND_WHIZ,
-- plydmg = 20,
-- npcdmg = 20,
-- force = 100,
-- minsplash = 10,
-- maxsplash = 100
-- })
--
function game.AddAmmoType( tbl )
if ( !isstring( tbl.name ) ) then
ErrorNoHalt( "bad argument #1 to 'AddAmmoType' ('name' key expected a string, got " .. type( tbl.name ) .. ")\n" )
return
end
local name = string.lower( tbl.name )
for id, ammo in ipairs( AmmoTypes ) do
if ( name == string.lower( ammo.name ) ) then
AmmoTypes[ id ] = tbl
return
end
end
table.insert( AmmoTypes, tbl )
end
--
-- Called by the engine to retrive the ammo types.
-- You should never have to call this manually.
--
function game.BuildAmmoTypes()
--
-- Sort the table by name here to assure that the ammo types
-- are inserted in the same order on both server and client
--
table.SortByMember( AmmoTypes, "name", true )
return AmmoTypes
end

View File

@@ -0,0 +1,269 @@
include( "math/ease.lua" )
math.tau = 2 * math.pi
--[[---------------------------------------------------------
Name: DistanceSqr( low, high )
Desc: Squared Distance between two 2d points, use this instead of math.Distance as it is more cpu efficient.
------------------------------------------------------------]]
function math.DistanceSqr( x1, y1, x2, y2 )
local xd = x2 - x1
local yd = y2 - y1
return xd * xd + yd * yd
end
--[[---------------------------------------------------------
Name: Distance( low, high )
Desc: Distance between two 2d points
------------------------------------------------------------]]
function math.Distance( x1, y1, x2, y2 )
local xd = x2 - x1
local yd = y2 - y1
return math.sqrt( xd * xd + yd * yd )
end
math.Dist = math.Distance -- Backwards compatibility
--[[---------------------------------------------------------
Name: BinToInt( bin )
Desc: Convert a binary string to an integer number
------------------------------------------------------------]]
function math.BinToInt( bin )
return tonumber( bin, 2 )
end
--[[---------------------------------------------------------
Name: IntToBin( int )
Desc: Convert an integer number to a binary string (the string len will be a multiple of three)
------------------------------------------------------------]]
local intbin = {
["0"] = "000", ["1"] = "001", ["2"] = "010", ["3"] = "011",
["4"] = "100", ["5"] = "101", ["6"] = "110", ["7"] = "111"
}
function math.IntToBin( int )
local str = string.gsub( string.format( "%o", int ), "(.)", function ( d ) return intbin[ d ] end )
return str
end
--[[---------------------------------------------------------
Name: Clamp( in, low, high )
Desc: Clamp value between 2 values
------------------------------------------------------------]]
function math.Clamp( _in, low, high )
return math.min( math.max( _in, low ), high )
end
--[[---------------------------------------------------------
Name: Rand( low, high )
Desc: Random number between low and high
-----------------------------------------------------------]]
function math.Rand( low, high )
return low + ( high - low ) * math.random()
end
math.Max = math.max
math.Min = math.min
--[[---------------------------------------------------------
Name: EaseInOut(fProgress, fEaseIn, fEaseOut)
Desc: Provided by garry from the facewound source and converted
to Lua by me :p
Usage: math.EaseInOut(0.1, 0.5, 0.5) - all parameters should be between 0 and 1
-----------------------------------------------------------]]
function math.EaseInOut( frac, easeIn, easeOut )
if ( frac == 0 or frac == 1 ) then return frac end
if ( easeIn == nil ) then easeIn = 0 end
if ( easeOut == nil ) then easeOut = 1 end
local fSumEase = easeIn + easeOut
if ( fSumEase == 0 ) then return frac end
if ( fSumEase > 1 ) then
easeIn = easeIn / fSumEase
easeOut = easeOut / fSumEase
end
local fProgressCalc = 1 / ( 2 - easeIn - easeOut )
if ( frac < easeIn ) then
return ( ( fProgressCalc / easeIn ) * frac * frac )
elseif ( frac < 1 - easeOut ) then
return ( fProgressCalc * ( 2 * frac - easeIn ) )
else
frac = 1 - frac
return ( 1 - ( fProgressCalc / easeOut ) * frac * frac )
end
end
function math.calcBSplineN( i, k, t, tInc )
local knot = ( i - 3 ) * tInc
if ( k <= 1 ) then
if ( knot <= t && t < knot + tInc ) then
return 1
end
return 0
end
local count = i + k - 4
local len = count * tInc
local knots = len - knot
local nknot = k - 1
local ret = 0
if ( knots > 0 ) then
ret = ( t - knot ) * math.calcBSplineN( i, nknot, t, tInc ) / knots
end
local sb = nknot * tInc
if ( sb > 0 ) then
ret = ret + ( len + tInc - t ) * math.calcBSplineN( i + 1, nknot, t, tInc ) / sb
end
return ret
end
function math.BSplinePoint( frac, points, frac_max )
local len = #points
local tInc = frac_max / ( len - 3 )
frac = frac + tInc
local ret = Vector()
for i = 1, len do
ret:Add( math.calcBSplineN( i, 4, frac, tInc ) * points[ i ] )
end
return ret
end
--[[---------------------------------------------------------
Cubic hermite spline
p0, p1 - points; m0, m1 - tangets; frac - fraction along the curve (0-1)
-----------------------------------------------------------]]
function math.CHSpline( frac, p0, m0, p1, m1 )
if ( frac >= 1 ) then return p1 end
if ( frac <= 0 ) then return p0 end
local t2 = frac * frac
local t3 = frac * t2
return p0 * ( 2 * t3 - 3 * t2 + 1 ) +
m0 * ( t3 - 2 * t2 + frac ) +
p1 * ( -2 * t3 + 3 * t2 ) +
m1 * ( t3 - t2 )
end
-- Round to the nearest integer
function math.Round( num, idp )
local mult = 10 ^ ( idp or 0 )
return math.floor( num * mult + 0.5 ) / mult
end
-- Rounds towards zero
function math.Truncate( num, idp )
local mult = 10 ^ ( idp or 0 )
return ( num < 0 and math.ceil or math.floor )( num * mult ) / mult
end
function math.Approach( cur, target, inc )
if ( cur < target ) then
return math.min( cur + math.abs( inc ), target )
end
if ( cur > target ) then
return math.max( cur - math.abs( inc ), target )
end
return target
end
function math.NormalizeAngle( a )
return ( a + 180 ) % 360 - 180
end
function math.AngleDifference( a, b )
local diff = math.NormalizeAngle( a - b )
if ( diff < 180 ) then
return diff
end
return diff - 360
end
function math.ApproachAngle( cur, target, inc )
return math.Approach( cur, cur + math.AngleDifference( target, cur ), inc )
end
function math.TimeFraction( Start, End, Current )
return ( Current - Start ) / ( End - Start )
end
function math.Remap( value, inMin, inMax, outMin, outMax )
return outMin + ( ( ( value - inMin ) / ( inMax - inMin ) ) * ( outMax - outMin ) )
end
-- Snaps the provided number to the nearest multiple
function math.SnapTo( num, multiple )
return math.floor( num / multiple + 0.5 ) * multiple
end
--[[---------------------------------------------------------
Name: CubicBezier( frac, p0, p1, p2, p3 )
Desc: Lerp point between points with cubic bezier
-----------------------------------------------------------]]
function math.CubicBezier( frac, p0, p1, p2, p3 )
local frac2 = frac * frac
local inv = 1 - frac
local inv2 = inv * inv
return inv2 * inv * p0 + 3 * inv2 * frac * p1 + 3 * inv * frac2 * p2 + frac2 * frac * p3
end
--[[---------------------------------------------------------
Name: QuadraticBezier( frac, p0, p1, p2 )
Desc: Lerp point between points with quadratic bezier
-----------------------------------------------------------]]
function math.QuadraticBezier( frac, p0, p1, p2 )
local frac2 = frac * frac
local inv = 1 - frac
local inv2 = inv * inv
return inv2 * p0 + 2 * inv * frac * p1 + frac2 * p2
end
--[[---------------------------------------------------------
Name: Factorial( num )
Desc: Calculate the factorial value of num
-----------------------------------------------------------]]
function math.Factorial( num )
if ( num < 0 ) then
return nil
elseif ( num < 2 ) then
return 1
end
local res = 1
for i = 2, num do
res = res * i
end
return res
end
--[[---------------------------------------------------------
Name: IsNearlyEqual( a, b, tolerance )
Desc: Checks if two floating point numbers are nearly equal
-----------------------------------------------------------]]
function math.IsNearlyEqual( a, b, tolerance )
if ( tolerance == nil ) then
tolerance = 1e-8
end
return math.abs( a - b ) <= tolerance
end

View File

@@ -0,0 +1,191 @@
module("math.ease", package.seeall)
--[[---------------------------------------------------------
Source code of functions
-----------------------------------------------------------]]
-- https://github.com/Facepunch/garrysmod/pull/1755
-- https://web.archive.org/web/20201212082306/https://easings.net/
-- https://web.archive.org/web/20201218211606/https://raw.githubusercontent.com/ai/easings.net/master/src/easings.yml
--[[---------------------------------------------------------
Easing constants
-----------------------------------------------------------]]
local c1 = 1.70158
local c3 = c1 + 1
local c2 = c1 * 1.525
local c4 = ( 2 * math.pi ) / 3
local c5 = ( 2 * math.pi ) / 4.5
local n1 = 7.5625
local d1 = 2.75
--[[---------------------------------------------------------
To reduce _G lookups, we'll localize some commonly
used functions in this extension
-----------------------------------------------------------]]
local pi = math.pi
local cos = math.cos
local sin = math.sin
local sqrt = math.sqrt
--[[---------------------------------------------------------
Easing functions
-----------------------------------------------------------]]
function InSine( x )
return 1 - cos( ( x * pi ) / 2 )
end
function OutSine( x )
return sin( ( x * pi ) / 2 )
end
function InOutSine( x )
return -( cos( pi * x ) - 1 ) / 2
end
function InQuad( x )
return x ^ 2
end
function OutQuad( x )
return 1 - ( 1 - x ) * ( 1 - x )
end
function InOutQuad( x )
return x < 0.5 && 2 * x ^ 2 || 1 - ( ( -2 * x + 2 ) ^ 2 ) / 2
end
function InCubic( x )
return x ^ 3
end
function OutCubic( x )
return 1 - ( ( 1 - x ) ^ 3 )
end
function InOutCubic( x )
return x < 0.5 && 4 * x ^ 3 || 1 - ( ( -2 * x + 2 ) ^ 3 ) / 2
end
function InQuart( x )
return x ^ 4
end
function OutQuart( x )
return 1 - ( ( 1 - x ) ^ 4 )
end
function InOutQuart( x )
return x < 0.5 && 8 * x ^ 4 || 1 - ( ( -2 * x + 2 ) ^ 4 ) / 2
end
function InQuint( x )
return x ^ 5
end
function OutQuint( x )
return 1 - ( ( 1 - x ) ^ 5 )
end
function InOutQuint( x )
return x < 0.5 && 16 * x ^ 5 || 1 - ( ( -2 * x + 2 ) ^ 5 ) / 2
end
function InExpo( x )
return x == 0 && 0 || ( 2 ^ ( 10 * x - 10 ) )
end
function OutExpo( x )
return x == 1 && 1 || 1 - ( 2 ^ ( -10 * x ) )
end
function InOutExpo( x )
return x == 0
&& 0
|| x == 1
&& 1
|| x < 0.5 && ( 2 ^ ( 20 * x - 10 ) ) / 2
|| ( 2 - ( 2 ^ ( -20 * x + 10 ) ) ) / 2
end
function InCirc( x )
return 1 - sqrt( 1 - ( x ^ 2 ) )
end
function OutCirc( x )
return sqrt( 1 - ( ( x - 1 ) ^ 2 ) )
end
function InOutCirc( x )
return x < 0.5
&& ( 1 - sqrt( 1 - ( ( 2 * x ) ^ 2 ) ) ) / 2
|| ( sqrt( 1 - ( ( -2 * x + 2 ) ^ 2 ) ) + 1 ) / 2
end
function InBack( x )
return c3 * x ^ 3 - c1 * x ^ 2
end
function OutBack( x )
return 1 + c3 * ( ( x - 1 ) ^ 3 ) + c1 * ( ( x - 1 ) ^ 2 )
end
function InOutBack( x )
return x < 0.5
&& ( ( ( 2 * x ) ^ 2 ) * ( ( c2 + 1 ) * 2 * x - c2 ) ) / 2
|| ( ( ( 2 * x - 2 ) ^ 2 ) * ( ( c2 + 1 ) * ( x * 2 - 2 ) + c2 ) + 2 ) / 2
end
function InElastic( x )
return x == 0
&& 0
|| x == 1
&& 1
|| -( 2 ^ ( 10 * x - 10 ) ) * sin( ( x * 10 - 10.75 ) * c4 )
end
function OutElastic( x )
return x == 0
&& 0
|| x == 1
&& 1
|| ( 2 ^ ( -10 * x ) ) * sin( ( x * 10 - 0.75 ) * c4 ) + 1
end
function InOutElastic( x )
return x == 0
&& 0
|| x == 1
&& 1
|| x < 0.5
&& -( ( 2 ^ ( 20 * x - 10 ) ) * sin( ( 20 * x - 11.125 ) * c5 ) ) / 2
|| ( ( 2 ^ ( -20 * x + 10 ) ) * sin( ( 20 * x - 11.125 ) * c5 ) ) / 2 + 1
end
function InBounce( x )
return 1 - OutBounce( 1 - x )
end
function OutBounce( x )
if ( x < 1 / d1 ) then
return n1 * x ^ 2
elseif ( x < 2 / d1 ) then
x = x - ( 1.5 / d1 )
return n1 * x ^ 2 + 0.75
elseif ( x < 2.5 / d1 ) then
x = x - ( 2.25 / d1 )
return n1 * x ^ 2 + 0.9375
else
x = x - ( 2.625 / d1 )
return n1 * x ^ 2 + 0.984375
end
end
function InOutBounce( x )
return x < 0.5
&& ( 1 - OutBounce( 1 - 2 * x ) ) / 2
|| ( 1 + OutBounce( 2 * x - 1 ) ) / 2
end

View File

@@ -0,0 +1,258 @@
--
-- Should never really happen.
--
motionsensor = motionsensor or {}
--
-- These bones are used to draw the debug
-- kinect skeleton. You just need to loop through
-- and draw a line between each of these bones.
--
motionsensor.DebugBones =
{
-- Torso
{ SENSORBONE.HEAD, SENSORBONE.SHOULDER },
{ SENSORBONE.SHOULDER, SENSORBONE.SHOULDER_LEFT },
{ SENSORBONE.SHOULDER, SENSORBONE.SHOULDER_RIGHT },
{ SENSORBONE.SHOULDER, SENSORBONE.SPINE },
{ SENSORBONE.SHOULDER, SENSORBONE.HIP },
{ SENSORBONE.HIP, SENSORBONE.HIP_LEFT },
{ SENSORBONE.HIP, SENSORBONE.HIP_RIGHT },
-- Left Arm
{ SENSORBONE.SHOULDER_LEFT, SENSORBONE.ELBOW_LEFT },
{ SENSORBONE.ELBOW_LEFT, SENSORBONE.WRIST_LEFT },
{ SENSORBONE.WRIST_LEFT, SENSORBONE.HAND_LEFT },
-- Right Arm
{ SENSORBONE.SHOULDER_RIGHT, SENSORBONE.ELBOW_RIGHT },
{ SENSORBONE.ELBOW_RIGHT, SENSORBONE.WRIST_RIGHT },
{ SENSORBONE.WRIST_RIGHT, SENSORBONE.HAND_RIGHT },
-- left leg
{ SENSORBONE.HIP_LEFT, SENSORBONE.KNEE_LEFT },
{ SENSORBONE.KNEE_LEFT, SENSORBONE.ANKLE_LEFT },
{ SENSORBONE.ANKLE_LEFT, SENSORBONE.FOOT_LEFT },
-- right leg
{ SENSORBONE.HIP_RIGHT, SENSORBONE.KNEE_RIGHT },
{ SENSORBONE.KNEE_RIGHT, SENSORBONE.ANKLE_RIGHT },
{ SENSORBONE.ANKLE_RIGHT, SENSORBONE.FOOT_RIGHT },
}
motionsensor.ChooseBuilderFromEntity = function( ent )
local builders = list.Get( "SkeletonConvertor" )
for k, v in pairs( builders ) do
if ( v:IsApplicable( ent ) ) then return k end
end
return "ValveBiped"
end
motionsensor.ProcessAngle = function( translator, sensor, pos, ang, special_vectors, boneid, v )
local a = nil
local b = nil
local up = special_vectors["up"]
--
-- Using a vector from another angle.
-- If the angle isn't processed yet return
-- we will be added to the list to process again.
--
if ( v.up_up ) then
if ( !ang[ v.up_up ] ) then return end
up = ang[ v.up_up ]:Up()
end
if ( v.up_dn ) then
if ( !ang[ v.up_dn ] ) then return end
up = ang[ v.up_dn ]:Up() * -1
end
if ( v.up_fwd ) then
if ( !ang[ v.up_fwd ] ) then return end
up = ang[ v.up_fwd ]:Forward()
end
if ( v.up_lft ) then
if ( !ang[ v.up_lft ] ) then return end
up = ang[ v.up_lft ]:Right() * -1
end
if ( v.up_rgt ) then
if ( !ang[ v.up_rgt ] ) then return end
up = ang[ v.up_rgt ]:Right()
end
--
-- From -> To vectors
--
if ( v.from_sensor ) then a = sensor[ v.from_sensor ] end
if ( v.to_sensor ) then b = sensor[ v.to_sensor ] end
if ( v.from ) then a = pos[ v.from ] end
if ( v.to ) then b = pos[ v.to ] end
--
-- We can offer special vectors to define 'up'
--
if ( isstring( v.up ) ) then
up = special_vectors[ v.up ]
end
if ( a == nil or b == nil or up == nil ) then return end
ang[ boneid ] = ( a - b ):GetNormal():AngleEx( up:GetNormal() );
if ( v.adjust ) then
ang[ boneid ] = ang[ boneid ] + v.adjust
end
return true
end
--
-- Processes the AnglesTable from a skeleton constructor
--
motionsensor.ProcessAnglesTable = function( translator, sensor, pos, rotation )
if ( !translator.AnglesTable ) then return {} end
local ang = {}
local special_vectors = {}
special_vectors["right"] = rotation:Right()
special_vectors["left"] = special_vectors["right"] * -1
special_vectors["up"] = rotation:Up()
special_vectors["down"] = special_vectors["up"] * -1
special_vectors["forward"] = special_vectors["down"]:Cross( special_vectors["right"] )
special_vectors["backward"] = special_vectors["forward"] * -1
special_vectors["hips_left"] = ( sensor[SENSORBONE.HIP_RIGHT] - sensor[SENSORBONE.HIP_LEFT] ):GetNormal()
special_vectors["hips_up"] = ( sensor[SENSORBONE.HIP] - sensor[SENSORBONE.SHOULDER] ):GetNormal()
special_vectors["hips_back"] = special_vectors["hips_up"]:Cross( special_vectors["hips_left"] )
special_vectors["hips_fwd"] = special_vectors["hips_back"] * -1
special_vectors["chest_lft"] = ( sensor[SENSORBONE.SHOULDER_RIGHT] - sensor[SENSORBONE.SHOULDER_LEFT] ):GetNormal()
special_vectors["chest_rgt"] = special_vectors["chest_lft"] * -1
special_vectors["chest_up"] = ( sensor[SENSORBONE.SPINE] - sensor[SENSORBONE.SHOULDER] ):GetNormal()
special_vectors["chest_dn"] = special_vectors["chest_up"] * -1
special_vectors["chest_bck"] = special_vectors["chest_up"]:Cross( special_vectors["chest_lft"] )
special_vectors["chest_fwd"] = special_vectors["chest_bck"] * -1
special_vectors["head_up"] = ( sensor[SENSORBONE.SHOULDER] - sensor[SENSORBONE.HEAD] ):GetNormal()
special_vectors["head_back"] = special_vectors["head_up"]:Cross( special_vectors["chest_lft"] )
local reprocess = {}
for k, v in pairs( translator.AnglesTable ) do
table.insert( reprocess, k )
end
for iPasses = 1, 5 do
local cur_process = reprocess
reprocess = {}
for k, v in pairs( cur_process ) do
if ( !motionsensor.ProcessAngle( translator, sensor, pos, ang, special_vectors, v, translator.AnglesTable[v] ) ) then
table.insert( reprocess, v )
end
end
if ( table.IsEmpty( reprocess ) ) then
--DebugInfo( 0, iPasses .. " Passes" )
return ang
end
end
--
-- If we got here then we're doing something wrong.
-- It should have out'ed before completing 5 passes.
--
DebugInfo( 0, "motionsensor.ProcessAnglesTable: 4+ passes!" )
return ang
end
--
-- Processes the PositionTable from a skeleton constructor
--
motionsensor.ProcessPositionTable = function( translator, sensor )
if ( !translator.PositionTable ) then return {} end
local pos = {}
for k, v in pairs( translator.PositionTable ) do
-- A number means get value straight from the sensor
if ( isnumber( v ) ) then pos[ k ] = sensor[ v ] end
if ( istable( v ) and v.type == "lerp" ) then
pos[ k ] = LerpVector( v.value, sensor[ v.from ], sensor[ v.to ] )
end
end
return pos
end
--
-- Called to build the skeleton
--
motionsensor.BuildSkeleton = function( translator, player, rotation )
--
-- The kinect animations are recorded on the skunt, so rotate towards the player
--
rotation:RotateAroundAxis( rotation:Up(), -90 )
--
-- Pre-get and rotate all the player positions
--
local sensor = {}
for i = 0, 19 do
sensor[ i ] = player:MotionSensorPos( i )
sensor[ i ]:Rotate( rotation )
end
if ( translator.PrePosition ) then
translator:PrePosition( sensor )
end
--
-- Fill out the position table..
--
local pos = motionsensor.ProcessPositionTable( translator, sensor )
--
-- Fill out the angles
--
local ang = motionsensor.ProcessAnglesTable( translator, sensor, pos, rotation )
--
-- Allow the bone builder to make any last minute changes
--
translator:Complete( player, sensor, rotation, pos, ang )
return pos, ang, sensor
end

View File

@@ -0,0 +1,238 @@
-- TODO: Hack. Move to where color is defined?
TYPE_COLOR = 255
net.Receivers = {}
--
-- Set up a function to receive network messages
--
function net.Receive( name, func )
net.Receivers[ name:lower() ] = func
end
--
-- A message has been received from the network..
--
function net.Incoming( len, client )
local i = net.ReadHeader()
local strName = util.NetworkIDToString( i )
if ( !strName ) then return end
local func = net.Receivers[ strName:lower() ]
if ( !func ) then return end
--
-- len includes the 16 bit int which told us the message name
--
len = len - 16
func( len, client )
end
--
-- Read/Write a boolean to the stream
--
net.WriteBool = net.WriteBit
function net.ReadBool()
return net.ReadBit() == 1
end
--
-- Read/Write an entity to the stream
--
function net.WriteEntity( ent )
if ( !IsValid( ent ) ) then
net.WriteUInt( 0, MAX_EDICT_BITS )
else
net.WriteUInt( ent:EntIndex(), MAX_EDICT_BITS )
end
end
function net.ReadEntity()
local i = net.ReadUInt( MAX_EDICT_BITS )
if ( !i ) then return end
return Entity( i )
end
--
-- Read/Write a player to the stream
--
-- TODO: Replace with MAX_PLAYER_BITS
local maxplayers_bits = math.ceil( math.log( 1 + game.MaxPlayers() ) / math.log( 2 ) )
function net.WritePlayer( ply )
net.WriteUInt( IsValid( ply ) and ply:IsPlayer() and ply:EntIndex() or 0, maxplayers_bits )
end
function net.ReadPlayer()
return Entity( net.ReadUInt( maxplayers_bits ) )
end
--
-- Read/Write a color to/from the stream
--
function net.WriteColor( col, writeAlpha )
if ( writeAlpha == nil ) then writeAlpha = true end
assert( IsColor( col ), "net.WriteColor: color expected, got ".. type( col ) )
local r, g, b, a = col:Unpack()
net.WriteUInt( r, 8 )
net.WriteUInt( g, 8 )
net.WriteUInt( b, 8 )
if ( writeAlpha ) then
net.WriteUInt( a, 8 )
end
end
function net.ReadColor( readAlpha )
if ( readAlpha == nil ) then readAlpha = true end
local r, g, b =
net.ReadUInt( 8 ),
net.ReadUInt( 8 ),
net.ReadUInt( 8 )
local a = 255
if ( readAlpha ) then a = net.ReadUInt( 8 ) end
return Color( r, g, b, a )
end
--
-- Write a whole table to the stream
-- This is less optimal than writing each
-- item individually and in a specific order
-- because it adds type information before each var
--
function net.WriteTable( tab, seq )
if ( seq ) then
local len = #tab
net.WriteUInt( len, 32 )
for i = 1, len do
net.WriteType( tab[ i ] )
end
else
for k, v in pairs( tab ) do
net.WriteType( k )
net.WriteType( v )
end
-- End of table
net.WriteType( nil )
end
end
function net.ReadTable( seq )
local tab = {}
if ( seq ) then
for i = 1, net.ReadUInt( 32 ) do
tab[ i ] = net.ReadType()
end
else
while true do
local k = net.ReadType()
if ( k == nil ) then break end
tab[ k ] = net.ReadType()
end
end
return tab
end
net.WriteVars =
{
[TYPE_NIL] = function ( t, v ) net.WriteUInt( t, 8 ) end,
[TYPE_STRING] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteString( v ) end,
[TYPE_NUMBER] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteDouble( v ) end,
[TYPE_TABLE] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteTable( v ) end,
[TYPE_BOOL] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteBool( v ) end,
[TYPE_ENTITY] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteEntity( v ) end,
[TYPE_VECTOR] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteVector( v ) end,
[TYPE_ANGLE] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteAngle( v ) end,
[TYPE_MATRIX] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteMatrix( v ) end,
[TYPE_COLOR] = function ( t, v ) net.WriteUInt( t, 8 ) net.WriteColor( v ) end,
}
function net.WriteType( v )
local typeid = nil
if IsColor( v ) then
typeid = TYPE_COLOR
else
typeid = TypeID( v )
end
local wv = net.WriteVars[ typeid ]
if ( wv ) then return wv( typeid, v ) end
error( "net.WriteType: Couldn't write " .. type( v ) .. " (type " .. typeid .. ")" )
end
net.ReadVars =
{
[TYPE_NIL] = function () return nil end,
[TYPE_STRING] = function () return net.ReadString() end,
[TYPE_NUMBER] = function () return net.ReadDouble() end,
[TYPE_TABLE] = function () return net.ReadTable() end,
[TYPE_BOOL] = function () return net.ReadBool() end,
[TYPE_ENTITY] = function () return net.ReadEntity() end,
[TYPE_VECTOR] = function () return net.ReadVector() end,
[TYPE_ANGLE] = function () return net.ReadAngle() end,
[TYPE_MATRIX] = function () return net.ReadMatrix() end,
[TYPE_COLOR] = function () return net.ReadColor() end,
}
function net.ReadType( typeid )
typeid = typeid or net.ReadUInt( 8 )
local rv = net.ReadVars[ typeid ]
if ( rv ) then return rv() end
error( "net.ReadType: Couldn't read type " .. typeid )
end

View File

@@ -0,0 +1,305 @@
local meta = FindMetaTable( "Player" )
local entity = FindMetaTable( "Entity" )
-- Return if there's nothing to add on to
if ( !meta ) then return end
--
-- Entity index accessor. This used to be done in engine, but it's done in Lua now because it's faster
--
function meta:__index( key )
--
-- Search the metatable. We can do this without dipping into C, so we do it first.
--
local val = meta[key]
if ( val ~= nil ) then return val end
--
-- Search the entity metatable
--
local entval = entity[key]
if ( entval ~= nil ) then return entval end
--
-- Search the entity table
--
local tab = entity.GetTable( self )
if ( tab ) then
return tab[ key ]
end
return nil
end
if ( !sql.TableExists( "playerpdata" ) ) then
sql.Query( "CREATE TABLE IF NOT EXISTS playerpdata ( infoid TEXT NOT NULL PRIMARY KEY, value TEXT );" )
end
--[[---------------------------------------------------------
Name: DebugInfo
Desc: Prints debug information for the player
( this is just an example )
-----------------------------------------------------------]]
function meta:DebugInfo()
Msg( "Name: " .. self:Name() .. "\n" )
Msg( "Pos: " .. tostring( self:GetPos() ) .. "\n" )
end
-- Helpful aliases
meta.GetName = meta.Nick
meta.Name = meta.Nick
--[[---------------------------------------------------------
Name: ConCommand
Desc: Overrides the default ConCommand function
-----------------------------------------------------------]]
if ( CLIENT ) then
local SendConCommand = meta.ConCommand
local CommandList = nil
function meta:ConCommand( command, bSkipQueue )
if ( bSkipQueue or IsConCommandBlocked( command ) ) then
SendConCommand( self, command )
else
CommandList = CommandList or {}
table.insert( CommandList, command )
end
end
local function SendQueuedConsoleCommands()
if ( !CommandList ) then return end
local BytesSent = 0
for k, v in pairs( CommandList ) do
SendConCommand( LocalPlayer(), v )
CommandList[ k ] = nil
-- Only send x bytes per tick
BytesSent = BytesSent + v:len()
if ( BytesSent > 128 ) then
break
end
end
-- Turn the table into a nil so we can return easy
if ( table.IsEmpty( CommandList ) ) then
CommandList = nil
end
end
hook.Add( "Tick", "SendQueuedConsoleCommands", SendQueuedConsoleCommands )
end
--[[---------------------------------------------------------
GetPData
Saves persist data for this player
-----------------------------------------------------------]]
function meta:GetPData( name, default )
-- First try looking up using the new key
local key = Format( "%s[%s]", self:SteamID64(), name )
local val = sql.QueryValue( "SELECT value FROM playerpdata WHERE infoid = " .. SQLStr( key ) .. " LIMIT 1" )
if ( val == nil ) then
-- Not found? Look using the old key
local oldkey = Format( "%s[%s]", self:UniqueID(), name )
val = sql.QueryValue( "SELECT value FROM playerpdata WHERE infoid = " .. SQLStr( oldkey ) .. " LIMIT 1" )
if ( val == nil ) then return default end
end
return val
end
--[[---------------------------------------------------------
SetPData
Set persistant data
-----------------------------------------------------------]]
function meta:SetPData( name, value )
-- Remove old value
local oldkey = Format( "%s[%s]", self:UniqueID(), name )
sql.Query( "DELETE FROM playerpdata WHERE infoid = " .. SQLStr( oldkey ) )
local key = Format( "%s[%s]", self:SteamID64(), name )
return sql.Query( "REPLACE INTO playerpdata ( infoid, value ) VALUES ( " .. SQLStr( key ) .. ", " .. SQLStr( value ) .. " )" ) ~= false
end
--[[---------------------------------------------------------
RemovePData
Remove persistant data
-----------------------------------------------------------]]
function meta:RemovePData( name )
-- First old key
local oldkey = Format( "%s[%s]", self:UniqueID(), name )
local removed = sql.Query( "DELETE FROM playerpdata WHERE infoid = " .. SQLStr( oldkey ) ) ~= false
-- Then new key
local key = Format( "%s[%s]", self:SteamID64(), name )
local removed2 = sql.Query( "DELETE FROM playerpdata WHERE infoid = " .. SQLStr( key ) ) ~= false
return removed or removed2
end
--
-- If they have their preferred default weapon then switch to it
--
function meta:SwitchToDefaultWeapon()
local weapon = self:GetInfo( "cl_defaultweapon" )
if ( self:HasWeapon( weapon ) ) then
self:SelectWeapon( weapon )
end
end
--
-- Can use flashlight?
--
function meta:AllowFlashlight( bAble ) self.m_bFlashlight = bAble end
function meta:CanUseFlashlight() return self.m_bFlashlight == true end
-- A function to set up player hands, so coders don't have to copy all the code everytime.
-- Call this in PlayerSpawn hook
function meta:SetupHands( spec_ply )
local oldhands = self:GetHands()
if ( IsValid( oldhands ) ) then
oldhands:Remove()
end
local hands = ents.Create( "gmod_hands" )
if ( IsValid( hands ) ) then
hands:DoSetup( self, spec_ply )
hands:Spawn()
end
end
--
-- Those functions have been removed from the engine since AddFlag and RemoveFlag
-- made them obsolete, but we'll keep a Lua version of them for backward compatibility
--
if ( SERVER ) then
--[[---------------------------------------------------------
Freeze
Freezes or unfreezes the player
-----------------------------------------------------------]]
function meta:Freeze( b )
if ( b ) then
self:AddFlags( FL_FROZEN )
else
self:RemoveFlags( FL_FROZEN )
end
end
--[[---------------------------------------------------------
GodEnable
Enables godmode on the player
-----------------------------------------------------------]]
function meta:GodEnable()
self:AddFlags( FL_GODMODE )
end
--[[---------------------------------------------------------
GodDisable
Disables godmode on the player
-----------------------------------------------------------]]
function meta:GodDisable()
self:RemoveFlags( FL_GODMODE )
end
end
--[[---------------------------------------------------------
IsFrozen
Returns true if the player is frozen
-----------------------------------------------------------]]
function meta:IsFrozen()
return self:IsFlagSet( FL_FROZEN )
end
--[[---------------------------------------------------------
HasGodMode
Returns true if the player is in godmode
-----------------------------------------------------------]]
function meta:HasGodMode()
return self:IsFlagSet( FL_GODMODE )
end
-- These are totally in the wrong place.
function player.GetByAccountID( ID )
for _, pl in player.Iterator() do
if ( pl:AccountID() == ID ) then
return pl
end
end
return false
end
function player.GetByUniqueID( ID )
for _, pl in player.Iterator() do
if ( pl:UniqueID() == ID ) then
return pl
end
end
return false
end
function player.GetBySteamID( ID )
ID = string.upper( ID )
for _, pl in player.Iterator() do
if ( pl:SteamID() == ID ) then
return pl
end
end
return false
end
function player.GetBySteamID64( ID )
for _, pl in player.Iterator() do
if ( pl:SteamID64() == ID ) then
return pl
end
end
return false
end

View File

@@ -0,0 +1,129 @@
local meta = FindMetaTable( "Player" )
if ( not meta ) then return end
--[[---------------------------------------------------------
Name: IsAdmin
Desc: Returns if a player is an admin.
-----------------------------------------------------------]]
function meta:IsAdmin()
if ( self:IsSuperAdmin() ) then return true end
if ( self:IsUserGroup( "admin" ) ) then return true end
return false
end
--[[---------------------------------------------------------
Name: IsSuperAdmin
Desc: Returns if a player is a superadmin.
-----------------------------------------------------------]]
function meta:IsSuperAdmin()
return self:IsUserGroup( "superadmin" )
end
--[[---------------------------------------------------------
Name: IsUserGroup
Desc: Returns if a player is in the specified usergroup.
-----------------------------------------------------------]]
function meta:IsUserGroup( name )
if ( not self:IsValid() ) then return false end
return self:GetUserGroup() == name
end
--[[---------------------------------------------------------
Name: GetUserGroup
Desc: Returns the player's usergroup.
-----------------------------------------------------------]]
function meta:GetUserGroup()
return self:GetNWString( "UserGroup", "user" )
end
--[[---------------------------------------------------------
This is the meat and spunk of the player auth system
-----------------------------------------------------------]]
if ( not SERVER ) then return end
--[[---------------------------------------------------------
Name: SetUserGroup
Desc: Sets the player's usergroup. ( Serverside Only )
-----------------------------------------------------------]]
function meta:SetUserGroup( name )
self:SetNWString( "UserGroup", name )
end
-- SteamIds table..
-- STEAM_0:1:7099:
-- name = garry
-- group = superadmin
local SteamIDs = {}
local function LoadUsersFile()
local txt = file.Read( "settings/users.txt", "MOD" )
if ( not txt ) then MsgN( "Failed to load settings/users.txt!" ) return end
-- Load the users file
local UsersKV = util.KeyValuesToTable( txt )
if ( not UsersKV ) then MsgN( "Failed to parse settings/users.txt!" ) return end
SteamIDs = {}
-- Extract the data into the SteamIDs table
for key, tab in pairs( UsersKV ) do
for name, steamid in pairs( tab ) do
SteamIDs[ steamid ] = {}
SteamIDs[ steamid ].name = name
SteamIDs[ steamid ].group = key
end
end
end
LoadUsersFile()
function util.GetUserGroups()
return SteamIDs
end
hook.Add( "PlayerInitialSpawn", "PlayerAuthSpawn", function( ply )
local steamid = ply:SteamID()
if ( game.SinglePlayer() or ply:IsListenServerHost() ) then
ply:SetUserGroup( "superadmin" )
return
end
if ( SteamIDs[ steamid ] == nil ) then
ply:SetUserGroup( "user" )
return
end
-- Admin SteamID need to be fully authenticated by Steam!
if ( ply.IsFullyAuthenticated and not ply:IsFullyAuthenticated() ) then
ply:ChatPrint( string.format( "Hey '%s' - Your SteamID wasn't fully authenticated, so your usergroup has not been set to '%s'.", SteamIDs[ steamid ].name, SteamIDs[ steamid ].group ) )
ply:ChatPrint( "Try restarting Steam." )
return
end
ply:SetUserGroup( SteamIDs[ steamid ].group )
ply:ChatPrint( string.format( "Hey '%s' - You're in the '%s' group on this server.", SteamIDs[ steamid ].name, SteamIDs[ steamid ].group ) )
end )

View File

@@ -0,0 +1,444 @@
local string = string
local math = math
--[[---------------------------------------------------------
Name: string.ToTable( string )
-----------------------------------------------------------]]
function string.ToTable( input )
local tbl = {}
-- For numbers, as some addons do this..
local str = tostring( input )
for i = 1, #str do
tbl[i] = string.sub( str, i, i )
end
return tbl
end
--[[---------------------------------------------------------
Name: string.JavascriptSafe( string )
Desc: Takes a string and escapes it for insertion in to a JavaScript string
-----------------------------------------------------------]]
local javascript_escape_replacements = {
["\\"] = "\\\\",
["\0"] = "\\x00" ,
["\b"] = "\\b" ,
["\t"] = "\\t" ,
["\n"] = "\\n" ,
["\v"] = "\\v" ,
["\f"] = "\\f" ,
["\r"] = "\\r" ,
["\""] = "\\\"",
["\'"] = "\\\'",
["`"] = "\\`",
["$"] = "\\$",
["{"] = "\\{",
["}"] = "\\}"
}
function string.JavascriptSafe( str )
str = string.gsub( str, ".", javascript_escape_replacements )
-- U+2028 and U+2029 are treated as line separators in JavaScript, handle separately as they aren't single-byte
str = string.gsub( str, "\226\128\168", "\\\226\128\168" )
str = string.gsub( str, "\226\128\169", "\\\226\128\169" )
return str
end
--[[---------------------------------------------------------
Name: string.PatternSafe( string )
Desc: Takes a string and escapes it for insertion in to a Lua pattern
-----------------------------------------------------------]]
local pattern_escape_replacements = {
["("] = "%(",
[")"] = "%)",
["."] = "%.",
["%"] = "%%",
["+"] = "%+",
["-"] = "%-",
["*"] = "%*",
["?"] = "%?",
["["] = "%[",
["]"] = "%]",
["^"] = "%^",
["$"] = "%$",
["\0"] = "%z"
}
function string.PatternSafe( str )
return ( string.gsub( str, ".", pattern_escape_replacements ) )
end
--[[---------------------------------------------------------
Name: explode(seperator ,string)
Desc: Takes a string and turns it into a table
Usage: string.explode( " ", "Seperate this string")
-----------------------------------------------------------]]
local totable = string.ToTable
local string_sub = string.sub
local string_find = string.find
local string_len = string.len
function string.Explode( separator, str, withpattern )
if ( separator == "" ) then return totable( str ) end
if ( withpattern == nil ) then withpattern = false end
local ret = {}
local current_pos = 1
for i = 1, string_len( str ) do
local start_pos, end_pos = string_find( str, separator, current_pos, not withpattern )
if ( not start_pos ) then break end
ret[ i ] = string_sub( str, current_pos, start_pos - 1 )
current_pos = end_pos + 1
end
ret[ #ret + 1 ] = string_sub( str, current_pos )
return ret
end
function string.Split( str, delimiter )
return string.Explode( delimiter, str )
end
--[[---------------------------------------------------------
Name: Implode( seperator, Table)
Desc: Takes a table and turns it into a string
Usage: string.Implode( " ", { "This", "Is", "A", "Table" } )
-----------------------------------------------------------]]
function string.Implode( seperator, Table ) return
table.concat( Table, seperator )
end
--[[---------------------------------------------------------
Name: GetExtensionFromFilename( path )
Desc: Returns extension from path
Usage: string.GetExtensionFromFilename("garrysmod/lua/modules/string.lua")
-----------------------------------------------------------]]
function string.GetExtensionFromFilename( path )
for i = #path, 1, -1 do
local c = string.sub( path, i, i )
if ( c == "/" or c == "\\" ) then return nil end
if ( c == "." ) then return string.sub( path, i + 1 ) end
end
return nil
end
--[[---------------------------------------------------------
Name: StripExtension( path )
-----------------------------------------------------------]]
function string.StripExtension( path )
for i = #path, 1, -1 do
local c = string.sub( path, i, i )
if ( c == "/" or c == "\\" ) then return path end
if ( c == "." ) then return string.sub( path, 1, i - 1 ) end
end
return path
end
--[[---------------------------------------------------------
Name: GetPathFromFilename( path )
Desc: Returns path from filepath
Usage: string.GetPathFromFilename("garrysmod/lua/modules/string.lua")
-----------------------------------------------------------]]
function string.GetPathFromFilename( path )
for i = #path, 1, -1 do
local c = string.sub( path, i, i )
if ( c == "/" or c == "\\" ) then return string.sub( path, 1, i ) end
end
return ""
end
--[[---------------------------------------------------------
Name: GetFileFromFilename( path )
Desc: Returns file with extension from path
Usage: string.GetFileFromFilename("garrysmod/lua/modules/string.lua")
-----------------------------------------------------------]]
function string.GetFileFromFilename( path )
for i = #path, 1, -1 do
local c = string.sub( path, i, i )
if ( c == "/" or c == "\\" ) then return string.sub( path, i + 1 ) end
end
return path
end
--[[-----------------------------------------------------------------
Name: FormattedTime( TimeInSeconds, Format )
Desc: Given a time in seconds, returns formatted time
If 'Format' is not specified the function returns a table
conatining values for hours, mins, secs, ms
Examples: string.FormattedTime( 123.456, "%02i:%02i:%02i") ==> "02:03:45"
string.FormattedTime( 123.456, "%02i:%02i") ==> "02:03"
string.FormattedTime( 123.456, "%2i:%02i") ==> " 2:03"
string.FormattedTime( 123.456 ) ==> { h = 0, m = 2, s = 3, ms = 45 }
-------------------------------------------------------------------]]
function string.FormattedTime( seconds, format )
if ( not seconds ) then seconds = 0 end
local hours = math.floor( seconds / 3600 )
local minutes = math.floor( ( seconds / 60 ) % 60 )
local millisecs = ( seconds - math.floor( seconds ) ) * 100
seconds = math.floor( seconds % 60 )
if ( format ) then
return string.format( format, minutes, seconds, millisecs )
else
return { h = hours, m = minutes, s = seconds, ms = millisecs }
end
end
--[[---------------------------------------------------------
Name: Old time functions
-----------------------------------------------------------]]
function string.ToMinutesSecondsMilliseconds( TimeInSeconds ) return string.FormattedTime( TimeInSeconds, "%02i:%02i:%02i" ) end
function string.ToMinutesSeconds( TimeInSeconds ) return string.FormattedTime( TimeInSeconds, "%02i:%02i" ) end
local function pluralizeString( str, quantity )
return str .. ( ( quantity ~= 1 ) and "s" or "" )
end
function string.NiceTime( seconds )
if ( seconds == nil ) then return "a few seconds" end
if ( seconds < 60 ) then
local t = math.floor( seconds )
return t .. pluralizeString( " second", t )
end
if ( seconds < 60 * 60 ) then
local t = math.floor( seconds / 60 )
return t .. pluralizeString( " minute", t )
end
if ( seconds < 60 * 60 * 24 ) then
local t = math.floor( seconds / (60 * 60) )
return t .. pluralizeString( " hour", t )
end
if ( seconds < 60 * 60 * 24 * 7 ) then
local t = math.floor( seconds / ( 60 * 60 * 24 ) )
return t .. pluralizeString( " day", t )
end
if ( seconds < 60 * 60 * 24 * 365 ) then
local t = math.floor( seconds / ( 60 * 60 * 24 * 7 ) )
return t .. pluralizeString( " week", t )
end
local t = math.floor( seconds / ( 60 * 60 * 24 * 365 ) )
return t .. pluralizeString( " year", t )
end
function string.Left( str, num ) return string.sub( str, 1, num ) end
function string.Right( str, num ) return string.sub( str, -num ) end
function string.Replace( str, tofind, toreplace )
local tbl = string.Explode( tofind, str )
if ( tbl[ 1 ] ) then return table.concat( tbl, toreplace ) end
return str
end
--[[---------------------------------------------------------
Name: Trim( s )
Desc: Removes leading and trailing spaces from a string.
Optionally pass char to trim that character from the ends instead of space
-----------------------------------------------------------]]
function string.Trim( s, char )
if ( char ) then char = string.PatternSafe( char ) else char = "%s" end
return string.match( s, "^" .. char .. "*(.-)" .. char .. "*$" ) or s
end
--[[---------------------------------------------------------
Name: TrimRight( s )
Desc: Removes trailing spaces from a string.
Optionally pass char to trim that character from the ends instead of space
-----------------------------------------------------------]]
function string.TrimRight( s, char )
if ( char ) then char = string.PatternSafe( char ) else char = "%s" end
return string.match( s, "^(.-)" .. char .. "*$" ) or s
end
--[[---------------------------------------------------------
Name: TrimLeft( s )
Desc: Removes leading spaces from a string.
Optionally pass char to trim that character from the ends instead of space
-----------------------------------------------------------]]
function string.TrimLeft( s, char )
if ( char ) then char = string.PatternSafe( char ) else char = "%s" end
return string.match( s, "^" .. char .. "*(.+)$" ) or s
end
function string.NiceSize( size )
size = tonumber( size )
if ( size <= 0 ) then return "0" end
if ( size < 1000 ) then return size .. " Bytes" end
if ( size < 1000 * 1000 ) then return math.Round( size / 1000, 2 ) .. " KB" end
if ( size < 1000 * 1000 * 1000 ) then return math.Round( size / ( 1000 * 1000 ), 2 ) .. " MB" end
return math.Round( size / ( 1000 * 1000 * 1000 ), 2 ) .. " GB"
end
-- Note: These use Lua index numbering, not what you'd expect
-- ie they start from 1, not 0.
function string.SetChar( s, k, v )
return string.sub( s, 0, k - 1 ) .. v .. string.sub( s, k + 1 )
end
function string.GetChar( s, k )
return string.sub( s, k, k )
end
local meta = getmetatable( "" )
function meta:__index( key )
local val = string[ key ]
if ( val ~= nil ) then
return val
elseif ( tonumber( key ) ) then
return string.sub( self, key, key )
end
end
function string.StartsWith( str, start )
return string.sub( str, 1, string.len( start ) ) == start
end
string.StartWith = string.StartsWith
function string.EndsWith( str, endStr )
return endStr == "" or string.sub( str, -string.len( endStr ) ) == endStr
end
function string.FromColor( color )
return Format( "%i %i %i %i", color.r, color.g, color.b, color.a )
end
function string.ToColor( str )
local r, g, b, a = string.match( str, "(%d+) (%d+) (%d+) (%d+)" )
return Color( tonumber( r ) or 255, tonumber( g ) or 255, tonumber( b ) or 255, tonumber( a ) or 255 )
end
function string.Comma( number, str )
if ( str ~= nil and not isstring( str ) ) then
error( "bad argument #2 to 'string.Comma' (string expected, got " .. type( str ) .. ")", 2 )
elseif ( str ~= nil and string.match( str, "%d" ) ~= nil ) then
error( "bad argument #2 to 'string.Comma' (non-numerical values expected, got " .. str .. ")", 2 )
end
local replace = str == nil and "%1,%2" or "%1" .. str .. "%2"
if ( isnumber( number ) ) then
number = string.format( "%f", number )
number = string.match( number, "^(.-)%.?0*$" ) -- Remove trailing zeros
end
local index = -1
while index ~= 0 do number, index = string.gsub( number, "^(-?%d+)(%d%d%d)", replace ) end
return number
end
function string.Interpolate( str, lookuptable )
return ( string.gsub( str, "{([_%a][_%w]*)}", lookuptable ) )
end
function string.CardinalToOrdinal( cardinal )
local basedigit = cardinal % 10
if ( basedigit == 1 ) then
if ( cardinal % 100 == 11 ) then
return cardinal .. "th"
end
return cardinal .. "st"
elseif ( basedigit == 2 ) then
if ( cardinal % 100 == 12 ) then
return cardinal .. "th"
end
return cardinal .. "nd"
elseif ( basedigit == 3 ) then
if ( cardinal % 100 == 13 ) then
return cardinal .. "th"
end
return cardinal .. "rd"
end
return cardinal .. "th"
end
function string.NiceName( name )
name = name:Replace( "_", " " )
-- Try to split text into words, where words would start with single uppercase character
local newParts = {}
for id, str in ipairs( string.Explode( " ", name ) ) do
local wordStart = 1
for i = 2, str:len() do
local c = str[ i ]
if ( c:upper() == c ) then
local toAdd = str:sub( wordStart, i - 1 )
if ( toAdd:upper() == toAdd ) then continue end
table.insert( newParts, toAdd )
wordStart = i
end
end
table.insert( newParts, str:sub( wordStart, str:len() ) )
end
-- Capitalize
--[[
for i, word in ipairs( newParts ) do
if ( #word == 1 ) then
newParts[i] = string.upper( word )
else
newParts[i] = string.upper( string.sub( word, 1, 1 ) ) .. string.sub( word, 2 )
end
end
return table.concat( newParts, " " )]]
local ret = table.concat( newParts, " " )
ret = string.upper( string.sub( ret, 1, 1 ) ) .. string.sub( ret, 2 )
return ret
end

View File

@@ -0,0 +1,806 @@
function table.Pack( ... )
return { ... }, select( "#", ... )
end
--[[---------------------------------------------------------
Name: Inherit( t, base )
Desc: Copies any missing data from base to t
-----------------------------------------------------------]]
function table.Inherit( t, base )
for k, v in pairs( base ) do
if ( t[ k ] == nil ) then t[ k ] = v end
end
t[ "BaseClass" ] = base
return t
end
--[[---------------------------------------------------------
Name: Copy(t, lookup_table)
Desc: Taken straight from http://lua-users.org/wiki/PitLibTablestuff
and modified to the new Lua 5.1 code by me.
Original function by PeterPrade!
-----------------------------------------------------------]]
function table.Copy( t, lookup_table )
if ( t == nil ) then return nil end
local copy = {}
setmetatable( copy, debug.getmetatable( t ) )
for i, v in pairs( t ) do
if ( !istable( v ) ) then
copy[ i ] = v
else
lookup_table = lookup_table or {}
lookup_table[ t ] = copy
if ( lookup_table[ v ] ) then
copy[ i ] = lookup_table[ v ] -- we already copied this table. reuse the copy.
else
copy[ i ] = table.Copy( v, lookup_table ) -- not yet copied. copy it.
end
end
end
return copy
end
--[[---------------------------------------------------------
Name: Empty( tab )
Desc: Empty a table
-----------------------------------------------------------]]
function table.Empty( tab )
for k, v in pairs( tab ) do
tab[ k ] = nil
end
end
--[[---------------------------------------------------------
Name: IsEmpty( tab )
Desc: Returns whether a table has iterable items in it, useful for non-sequential tables
-----------------------------------------------------------]]
function table.IsEmpty( tab )
return next( tab ) == nil
end
--[[---------------------------------------------------------
Name: CopyFromTo( FROM, TO )
Desc: Make TO exactly the same as FROM - but still the same table.
-----------------------------------------------------------]]
function table.CopyFromTo( from, to )
-- Erase values from table TO
table.Empty( to )
-- Copy values over
table.Merge( to, from )
end
--[[---------------------------------------------------------
Name: Merge
Desc: xx
-----------------------------------------------------------]]
function table.Merge( dest, source, forceOverride )
for k, v in pairs( source ) do
if ( !forceOverride and istable( v ) and istable( dest[ k ] ) ) then
-- don't overwrite one table with another
-- instead merge them recurisvely
table.Merge( dest[ k ], v )
else
dest[ k ] = v
end
end
return dest
end
--[[---------------------------------------------------------
Name: HasValue
Desc: Returns whether the value is in given table
-----------------------------------------------------------]]
function table.HasValue( t, val )
for k, v in pairs( t ) do
if ( v == val ) then return true end
end
return false
end
--[[---------------------------------------------------------
Name: table.Add( dest, source )
Desc: Unlike merge this adds the two tables together and discards keys.
-----------------------------------------------------------]]
function table.Add( dest, source )
-- The tables should be different otherwise this will just freeze the whole game
if ( dest == source ) then return dest end
-- At least one of them needs to be a table or this whole thing will fall on its ass
if ( !istable( source ) ) then return dest end
if ( !istable( dest ) ) then dest = {} end
for k, v in pairs( source ) do
table.insert( dest, v )
end
return dest
end
--[[---------------------------------------------------------
Name: table.SortDesc( table )
Desc: Like Lua's default sort, but descending
-----------------------------------------------------------]]
function table.SortDesc( t )
return table.sort( t, function( a, b ) return a > b end )
end
--[[---------------------------------------------------------
Name: table.SortByKey( table )
Desc: Returns a table sorted numerically by Key value
-----------------------------------------------------------]]
function table.SortByKey( t, desc )
local temp = {}
for key, _ in pairs( t ) do table.insert( temp, key ) end
if ( desc ) then
table.sort( temp, function( a, b ) return t[ a ] < t[ b ] end )
else
table.sort( temp, function( a, b ) return t[ a ] > t[ b ] end )
end
return temp
end
--[[---------------------------------------------------------
Name: table.Count( table )
Desc: Returns the number of keys in a table
-----------------------------------------------------------]]
function table.Count( t )
local i = 0
for k in pairs( t ) do i = i + 1 end
return i
end
--[[---------------------------------------------------------
Name: table.Random( table )
Desc: Return a random key
-----------------------------------------------------------]]
function table.Random( t )
local rk = math.random( 1, table.Count( t ) )
local i = 1
for k, v in pairs( t ) do
if ( i == rk ) then return v, k end
i = i + 1
end
end
--[[---------------------------------------------------------
Name: table.Shuffle( table )
Desc: Performs an inline Fisher-Yates shuffle on the table in O(n) time
-----------------------------------------------------------]]
function table.Shuffle( t )
local n = #t
for i = 1, n - 1 do
local j = math.random( i, n )
t[ i ], t[ j ] = t[ j ], t[ i ]
end
end
--[[----------------------------------------------------------------------
Name: table.IsSequential( table )
Desc: Returns true if the tables
keys are sequential
-------------------------------------------------------------------------]]
function table.IsSequential( t )
local i = 1
for key, value in pairs( t ) do
if ( t[ i ] == nil ) then return false end
i = i + 1
end
return true
end
--[[---------------------------------------------------------
Name: table.ToString( table,name,nice )
Desc: Convert a simple table to a string
table = the table you want to convert (table)
name = the name of the table (string)
nice = whether to add line breaks and indents (bool)
-----------------------------------------------------------]]
local function MakeTable( t, nice, indent, done )
local str = ""
done = done or {}
indent = indent or 0
local idt = ""
if ( nice ) then idt = string.rep( "\t", indent ) end
local nl, tab = "", ""
if ( nice ) then nl, tab = "\n", "\t" end
local sequential = table.IsSequential( t )
for key, value in pairs( t ) do
str = str .. idt .. tab .. tab
if !sequential then
if ( isnumber( key ) or isbool( key ) ) then
key = "[" .. tostring( key ) .. "]" .. tab .. "="
else
key = tostring( key ) .. tab .. "="
end
else
key = ""
end
if ( istable( value ) and !done[ value ] ) then
if ( IsColor( value ) ) then
done[ value ] = true
value = "Color(" .. value.r .. "," .. value.g .. "," .. value.b .. "," .. value.a .. ")"
str = str .. key .. tab .. value .. "," .. nl
else
done[ value ] = true
str = str .. key .. tab .. '{' .. nl .. MakeTable( value, nice, indent + 1, done )
str = str .. idt .. tab .. tab .. tab .. tab .. "}," .. nl
end
else
if ( isstring( value ) ) then
value = '"' .. tostring( value ) .. '"'
elseif ( isvector( value ) ) then
value = "Vector(" .. value.x .. "," .. value.y .. "," .. value.z .. ")"
elseif ( isangle( value ) ) then
value = "Angle(" .. value.pitch .. "," .. value.yaw .. "," .. value.roll .. ")"
else
value = tostring( value )
end
str = str .. key .. tab .. value .. "," .. nl
end
end
return str
end
function table.ToString( t, n, nice )
local nl, tab = "", ""
if ( nice ) then nl, tab = "\n", "\t" end
local str = ""
if ( n ) then str = n .. tab .. "=" .. tab end
return str .. "{" .. nl .. MakeTable( t, nice ) .. "}"
end
--[[---------------------------------------------------------
Name: table.Sanitise( table )
Desc: Converts a table containing vectors, angles, bools so it can be converted to and from keyvalues
-----------------------------------------------------------]]
function table.Sanitise( t, done )
done = done or {}
local tbl = {}
for k, v in pairs ( t ) do
if ( istable( v ) and !IsColor( v ) and !done[ v ] ) then
done[ v ] = true
tbl[ k ] = table.Sanitise( v, done )
else
if ( isvector( v ) ) then
local x, y, z = v.x, v.y, v.z
if y == 0 then y = nil end
if z == 0 then z = nil end
tbl[ k ] = { __type = "Vector", x = x, y = y, z = z }
elseif ( isangle( v ) ) then
local p, y, r = v.pitch, v.yaw, v.roll
if p == 0 then p = nil end
if y == 0 then y = nil end
if r == 0 then r = nil end
tbl[ k ] = { __type = "Angle", p = p, y = y, r = r }
elseif ( IsColor( v ) ) then
local r, g, b, a = v.r, v.g, v.b, v.a
if r == 255 then r = nil end
if g == 255 then g = nil end
if b == 255 then b = nil end
if a == 255 then a = nil end
tbl[ k ] = { __type = "Color", r = r, g = g, b = b, a = a }
elseif ( isbool( v ) ) then
tbl[ k ] = { __type = "Bool", tostring( v ) }
else
tbl[ k ] = tostring( v )
end
end
end
return tbl
end
--[[---------------------------------------------------------
Name: table.DeSanitise( table )
Desc: Converts a Sanitised table back
-----------------------------------------------------------]]
function table.DeSanitise( t, done )
done = done or {}
local tbl = {}
for k, v in pairs ( t ) do
if ( istable( v ) and !IsColor( v ) and !done[ v ] ) then
done[ v ] = true
if ( v.__type ) then
if ( v.__type == "Vector" ) then
tbl[ k ] = Vector( v.x or 0, v.y, v.z )
elseif ( v.__type == "Angle" ) then
tbl[ k ] = Angle( v.p or 0, v.y, v.r )
elseif ( v.__type == "Color" ) then
tbl[ k ] = Color( v.r or 255, v.g or 255, v.b or 255, v.a or 255 )
elseif ( v.__type == "Bool" ) then
tbl[ k ] = ( v[ 1 ] == "true" )
end
else
tbl[ k ] = table.DeSanitise( v, done )
end
else
tbl[ k ] = v
end
end
return tbl
end
function table.ForceInsert( t, v )
if ( t == nil ) then t = {} end
table.insert( t, v )
return t
end
--[[---------------------------------------------------------
Name: table.SortByMember( table )
Desc: Sorts table by named member
-----------------------------------------------------------]]
function table.SortByMember( tab, memberName, bAsc )
local TableMemberSort = function( a, b, MemberName, bReverse )
--
-- All this error checking kind of sucks, but really is needed
--
if ( !istable( a ) ) then return !bReverse end
if ( !istable( b ) ) then return bReverse end
if ( !a[ MemberName ] ) then return !bReverse end
if ( !b[ MemberName ] ) then return bReverse end
if ( isstring( a[ MemberName ] ) ) then
if ( bReverse ) then
return a[ MemberName ]:lower() < b[ MemberName ]:lower()
else
return a[ MemberName ]:lower() > b[ MemberName ]:lower()
end
end
if ( bReverse ) then
return a[ MemberName ] < b[ MemberName ]
else
return a[ MemberName ] > b[ MemberName ]
end
end
table.sort( tab, function( a, b ) return TableMemberSort( a, b, memberName, bAsc or false ) end )
end
--[[---------------------------------------------------------
Name: table.LowerKeyNames( table )
Desc: Lowercase the keynames of all tables
-----------------------------------------------------------]]
function table.LowerKeyNames( tab )
local OutTable = {}
for k, v in pairs( tab ) do
-- Recurse
if ( istable( v ) ) then
v = table.LowerKeyNames( v )
end
OutTable[ k ] = v
if ( isstring( k ) ) then
OutTable[ k ] = nil
OutTable[ string.lower( k ) ] = v
end
end
return OutTable
end
--[[---------------------------------------------------------
Name: table.CollapseKeyValue( table )
Desc: Collapses a table with keyvalue structure
-----------------------------------------------------------]]
function table.CollapseKeyValue( Table )
local OutTable = {}
for k, v in pairs( Table ) do
local Val = v.Value
if ( istable( Val ) ) then
Val = table.CollapseKeyValue( Val )
end
OutTable[ v.Key ] = Val
end
return OutTable
end
--[[---------------------------------------------------------
Name: table.ClearKeys( table, bSaveKey )
Desc: Clears the keys, converting to a numbered format
-----------------------------------------------------------]]
function table.ClearKeys( Table, bSaveKey )
local OutTable = {}
for k, v in pairs( Table ) do
if ( bSaveKey ) then
v.__key = k
end
table.insert( OutTable, v )
end
return OutTable
end
local function keyValuePairs( state )
state.Index = state.Index + 1
local keyValue = state.KeyValues[ state.Index ]
if ( !keyValue ) then return end
return keyValue.key, keyValue.val
end
local function toKeyValues( tbl )
local result = {}
for k, v in pairs( tbl ) do
table.insert( result, { key = k, val = v } )
end
return result
end
local function getKeys( tbl )
local keys, i = {}, 0
for k in pairs( tbl ) do
i = i + 1
keys[ i ] = k
end
return keys
end
--[[---------------------------------------------------------
A Pairs function
Sorted by TABLE KEY
-----------------------------------------------------------]]
function SortedPairs( pTable, Desc )
local keys = getKeys( pTable )
if ( Desc ) then
table.sort( keys, function( a, b )
return a > b
end )
else
table.sort( keys, function( a, b )
return a < b
end )
end
local i, key = 1, nil
return function()
key, i = keys[ i ], i + 1
return key, pTable[ key ]
end
end
--[[---------------------------------------------------------
A Pairs function
Sorted by VALUE
-----------------------------------------------------------]]
function SortedPairsByValue( pTable, Desc )
local sortedTbl = toKeyValues( pTable )
if ( Desc ) then
table.sort( sortedTbl, function( a, b ) return a.val > b.val end )
else
table.sort( sortedTbl, function( a, b ) return a.val < b.val end )
end
return keyValuePairs, { Index = 0, KeyValues = sortedTbl }
end
--[[---------------------------------------------------------
A Pairs function
Sorted by Member Value (All table entries must be a table!)
-----------------------------------------------------------]]
function SortedPairsByMemberValue( pTable, pValueName, Desc )
local sortedTbl = toKeyValues( pTable )
for k, v in pairs( sortedTbl ) do
v.member = v.val[ pValueName ]
end
table.SortByMember( sortedTbl, "member", !Desc )
return keyValuePairs, { Index = 0, KeyValues = sortedTbl }
end
--[[---------------------------------------------------------
A Pairs function
-----------------------------------------------------------]]
function RandomPairs( pTable, Desc )
local sortedTbl = toKeyValues( pTable )
for k, v in pairs( sortedTbl ) do
v.rand = math.random( 1, 1000000 )
end
-- descending/ascending for a random order, really?
if ( Desc ) then
table.sort( sortedTbl, function( a, b ) return a.rand > b.rand end )
else
table.sort( sortedTbl, function( a, b ) return a.rand < b.rand end )
end
return keyValuePairs, { Index = 0, KeyValues = sortedTbl }
end
--[[---------------------------------------------------------
GetFirstKey
-----------------------------------------------------------]]
function table.GetFirstKey( t )
local k, _ = next( t )
return k
end
function table.GetFirstValue( t )
local _, v = next( t )
return v
end
function table.GetLastKey( t )
local k, _ = next( t, table.Count( t ) - 1 )
return k
end
function table.GetLastValue( t )
local _, v = next( t, table.Count( t ) - 1 )
return v
end
function table.FindNext( tab, val )
local bfound = false
for k, v in pairs( tab ) do
if ( bfound ) then return v end
if ( val == v ) then bfound = true end
end
return table.GetFirstValue( tab )
end
function table.FindPrev( tab, val )
local last = table.GetLastValue( tab )
for k, v in pairs( tab ) do
if ( val == v ) then return last end
last = v
end
return last
end
function table.GetWinningKey( tab )
local highest = -math.huge
local winner = nil
for k, v in pairs( tab ) do
if ( v > highest ) then
winner = k
highest = v
end
end
return winner
end
function table.KeyFromValue( tbl, val )
for key, value in pairs( tbl ) do
if ( value == val ) then return key end
end
end
function table.RemoveByValue( tbl, val )
local key = table.KeyFromValue( tbl, val )
if ( !key ) then return false end
if ( isnumber( key ) ) then
table.remove( tbl, key )
else
tbl[ key ] = nil
end
return key
end
function table.KeysFromValue( tbl, val )
local res = {}
for key, value in pairs( tbl ) do
if ( value == val ) then res[ #res + 1 ] = key end
end
return res
end
function table.MemberValuesFromKey( tab, key )
local res = {}
for k, v in pairs( tab ) do
if ( istable( v ) and v[ key ] != nil ) then res[ #res + 1 ] = v[ key ] end
end
return res
end
function table.Reverse( tbl )
local len = #tbl
local ret = {}
for i = len, 1, -1 do
ret[ len - i + 1 ] = tbl[ i ]
end
return ret
end
function table.ForEach( tab, funcname )
for k, v in pairs( tab ) do
funcname( k, v )
end
end
function table.GetKeys( tab )
local keys = {}
local id = 1
for k, v in pairs( tab ) do
keys[ id ] = k
id = id + 1
end
return keys
end
function table.Flip( tab )
local res = {}
for k, v in pairs( tab ) do
res[ v ] = k
end
return res
end
-- Polyfill for table.move on 32-bit
-- Don't forget to remove this when it's no longer necessary
if ( !table.move ) then
function table.move( sourceTbl, from, to, dest, destTbl )
if ( !istable( sourceTbl ) ) then error( "bad argument #1 to 'move' (table expected, got " .. type( sourceTbl ) .. ")", 2 ) end
if ( !isnumber( from ) ) then error( "bad argument #2 to 'move' (number expected, got " .. type( from ) .. ")", 2 ) end
if ( !isnumber( to ) ) then error( "bad argument #3 to 'move' (number expected, got " .. type( to ) .. ")", 2 ) end
if ( !isnumber( dest ) ) then error( "bad argument #4 to 'move' (number expected, got " .. type( dest ) .. ")", 2 ) end
if ( destTbl != nil ) then
if ( !istable( destTbl ) ) then error( "bad argument #5 to 'move' (table expected, got " .. type( destTbl ) .. ")", 2 ) end
else
destTbl = sourceTbl
end
local buffer = { unpack( sourceTbl, from, to ) }
dest = math.floor( dest - 1 )
for i, v in ipairs( buffer ) do
destTbl[ dest + i ] = v
end
return destTbl
end
end

View File

@@ -0,0 +1,418 @@
-- Return if there's nothing to add on to
if ( !util ) then return end
if ( CLIENT ) then
include( "util/worldpicker.lua" )
end
--[[---------------------------------------------------------
Name: IsValidPhysicsObject
Params: <ent> <num>
Desc: Returns true if physics object is valid, false if not
-----------------------------------------------------------]]
function util.IsValidPhysicsObject( ent, num )
-- Make sure the entity is valid
if ( !ent or ( !ent:IsValid() and !ent:IsWorld() ) ) then return false end
-- This is to stop attaching to walking NPCs.
-- Although this is possible and `works', it can severly reduce the
-- performance of the server.. Plus they don't pay attention to constraints
-- anyway - so we're not really losing anything.
local MoveType = ent:GetMoveType()
if ( !ent:IsWorld() and MoveType != MOVETYPE_VPHYSICS and !( ent:GetModel() and ent:GetModel():StartsWith( "*" ) ) ) then return false end
local Phys = ent:GetPhysicsObjectNum( num )
return IsValid( Phys )
end
--[[---------------------------------------------------------
Name: GetPlayerTrace( ply, dir )
Desc: Returns a generic trace table for the player
(dir is optional, defaults to the player's aim)
-----------------------------------------------------------]]
function util.GetPlayerTrace( ply, dir )
dir = dir or ply:GetAimVector()
local trace = {}
trace.start = ply:EyePos()
trace.endpos = trace.start + ( dir * ( 4096 * 8 ) )
trace.filter = ply
return trace
end
--[[---------------------------------------------------------
Name: QuickTrace( origin, offset, filter )
Desc: Quick trace
-----------------------------------------------------------]]
function util.QuickTrace( origin, dir, filter )
local trace = {}
trace.start = origin
trace.endpos = origin + dir
trace.filter = filter
return util.TraceLine( trace )
end
--[[---------------------------------------------------------
Name: tobool( in )
Desc: Turn variable into bool
-----------------------------------------------------------]]
util.tobool = tobool
--[[---------------------------------------------------------
Name: LocalToWorld( ent, lpos, bone )
Desc: Convert the local position on an entity to world pos
-----------------------------------------------------------]]
function util.LocalToWorld( ent, lpos, bone )
bone = bone or 0
if ( ent:EntIndex() == 0 ) then
return lpos
else
if ( IsValid( ent:GetPhysicsObjectNum( bone ) ) ) then
return ent:GetPhysicsObjectNum( bone ):LocalToWorld( lpos )
else
return ent:LocalToWorld( lpos )
end
end
return nil
end
--[[---------------------------------------------------------
Returns year, month, day and hour, minute, second in a formatted string.
-----------------------------------------------------------]]
function util.DateStamp()
local t = os.date( '*t' )
return t.year .. "-" .. t.month .. "-" .. t.day .. " " .. Format( "%02i-%02i-%02i", t.hour, t.min, t.sec )
end
--[[---------------------------------------------------------
Convert a string to a certain type
-----------------------------------------------------------]]
function util.StringToType( str, typename )
typename = typename:lower()
if ( typename == "vector" ) then return Vector( str ) end
if ( typename == "angle" ) then return Angle( str ) end
if ( typename == "float" || typename == "number" ) then return tonumber( str ) end
if ( typename == "int" ) then local v = tonumber( str ) return v and math.Round( v ) or nil end
if ( typename == "bool" || typename == "boolean" ) then return tobool( str ) end
if ( typename == "string" ) then return tostring( str ) end
if ( typename == "entity" ) then return Entity( str ) end
MsgN( "util.StringToType: unknown type \"", typename, "\"!" )
end
--
-- Convert a type to a (nice, but still parsable) string
--
function util.TypeToString( v )
local iD = TypeID( v )
if ( iD == TYPE_VECTOR or iD == TYPE_ANGLE ) then
return string.format( "%.2f %.2f %.2f", v:Unpack() )
end
if ( iD == TYPE_NUMBER ) then
return util.NiceFloat( v )
end
return tostring( v )
end
--
-- Formats a float by stripping off extra 0's and .'s
--
-- 0.00 -> 0
-- 0.10 -> 0.1
-- 1.00 -> 1
-- 1.49 -> 1.49
-- 5.90 -> 5.9
--
function util.NiceFloat( f )
local str = string.format( "%f", f )
str = str:TrimRight( "0" )
str = str:TrimRight( "." )
return str
end
--
-- Timer
--
--
local T =
{
--
-- Resets the timer to nothing
--
Reset = function( self )
self.starttime = CurTime() - self.starttime
self.endtime = nil
end,
--
-- Starts the timer, call with end time
--
Start = function( self, time )
self.starttime = CurTime()
self.endtime = CurTime() + ( time or 0 )
end,
--
-- Returns true if the timer has been started
--
Started = function( self )
return self.endtime != nil
end,
--
-- Returns true if the time has elapsed
--
Elapsed = function( self )
return self.endtime == nil or self.endtime <= CurTime()
end,
--
-- Returns the amount of time that has passed since the Timer was started
--
GetElaspedTime = function( self )
return self:Started() and CurTime() - self.starttime or self.starttime
end
}
T.__index = T
--
-- Create a new timer object
--
function util.Timer( startdelay )
local t = {}
setmetatable( t, T )
t:Start( startdelay or 0 )
return t
end
local function PopStack( self, num )
if ( num == nil ) then
num = 1
elseif ( num < 0 ) then
error( string.format( "attempted to pop %d elements in stack, expected >= 0", num ), 3 )
else
num = math.floor( num )
end
local len = self[ 0 ]
if ( num > len ) then
error( string.format( "attempted to pop %u element%s in stack of length %u", num, num == 1 and "" or "s", len ), 3 )
end
return num, len
end
local STACK =
{
Push = function( self, obj )
local len = self[ 0 ] + 1
self[ len ] = obj
self[ 0 ] = len
end,
Pop = function( self, num )
local len
num, len = PopStack( self, num )
if ( num == 0 ) then
return nil
end
local newlen = len - num
self[ 0 ] = newlen
newlen = newlen + 1
local ret = self[ newlen ]
-- Pop up to the last element
for i = len, newlen, -1 do
self[ i ] = nil
end
return ret
end,
PopMulti = function( self, num )
local len
num, len = PopStack( self, num )
if ( num == 0 ) then
return {}
end
local newlen = len - num
self[ 0 ] = newlen
local ret = {}
local retpos = 0
-- Pop each element and add it to the table
-- Iterate in reverse since the stack is internally stored
-- with 1 being the bottom element and len being the top
-- But the return will have 1 as the top element
for i = len, newlen + 1, -1 do
retpos = retpos + 1
ret[ retpos ] = self[ i ]
self[ i ] = nil
end
return ret
end,
Top = function( self )
local len = self[ 0 ]
if ( len == 0 ) then
return nil
end
return self[ len ]
end,
Size = function( self )
return self[ 0 ]
end
}
STACK.__index = STACK
function util.Stack()
return setmetatable( { [ 0 ] = 0 }, STACK )
end
-- Helper for the following functions. This is not ideal but we cannot change this because it will break existing addons.
local function GetUniqueID( sid )
return util.CRC( "gm_" .. sid .. "_gm" )
end
--[[---------------------------------------------------------
Name: GetPData( steamid, name, default )
Desc: Gets the persistant data from a player by steamid
-----------------------------------------------------------]]
function util.GetPData( steamid, name, default )
-- First try looking up using the new key
local key = Format( "%s[%s]", util.SteamIDTo64( steamid ), name )
local val = sql.QueryValue( "SELECT value FROM playerpdata WHERE infoid = " .. SQLStr( key ) .. " LIMIT 1" )
if ( val == nil ) then
-- Not found? Look using the old key
local oldkey = Format( "%s[%s]", GetUniqueID( steamid ), name )
val = sql.QueryValue( "SELECT value FROM playerpdata WHERE infoid = " .. SQLStr( oldkey ) .. " LIMIT 1" )
if ( val == nil ) then return default end
end
return val
end
--[[---------------------------------------------------------
Name: SetPData( steamid, name, value )
Desc: Sets the persistant data of a player by steamid
-----------------------------------------------------------]]
function util.SetPData( steamid, name, value )
local key = Format( "%s[%s]", util.SteamIDTo64( steamid ), name )
sql.Query( "REPLACE INTO playerpdata ( infoid, value ) VALUES ( " .. SQLStr( key ) .. ", " .. SQLStr( value ) .. " )" )
end
--[[---------------------------------------------------------
Name: RemovePData( steamid, name )
Desc: Removes the persistant data from a player by steamid
-----------------------------------------------------------]]
function util.RemovePData( steamid, name )
-- First the old key
local oldkey = Format( "%s[%s]", GetUniqueID( steamid ), name )
sql.Query( "DELETE FROM playerpdata WHERE infoid = " .. SQLStr( oldkey ) )
-- Then the new key. util.SteamIDTo64 is not ideal, but nothing we can do about it now
local key = Format( "%s[%s]", util.SteamIDTo64( steamid ), name )
sql.Query( "DELETE FROM playerpdata WHERE infoid = " .. SQLStr( key ) )
end
--[[---------------------------------------------------------
Name: IsBinaryModuleInstalled( name )
Desc: Returns whether a binary module with the given name is present on disk
-----------------------------------------------------------]]
local suffix = ( { "osx64", "osx", "linux64", "linux", "win64", "win32" } )[
( system.IsWindows() and 4 or 0 )
+ ( system.IsLinux() and 2 or 0 )
+ ( jit.arch == "x86" and 1 or 0 )
+ 1
]
local fmt = "lua/bin/gm" .. ( ( CLIENT and !MENU_DLL ) and "cl" or "sv" ) .. "_%s_%s.dll"
function util.IsBinaryModuleInstalled( name )
if ( !isstring( name ) ) then
error( "bad argument #1 to 'IsBinaryModuleInstalled' (string expected, got " .. type( name ) .. ")", 2 )
elseif ( #name == 0 ) then
error( "bad argument #1 to 'IsBinaryModuleInstalled' (string cannot be empty)", 2 )
end
if ( file.Exists( string.format( fmt, name, suffix ), "MOD" ) ) then
return true
end
-- Edge case - on Linux 32-bit x86-64 branch, linux32 is also supported as a suffix
if ( jit.versionnum != 20004 and jit.arch == "x86" and system.IsLinux() ) then
return file.Exists( string.format( fmt, name, "linux32" ), "MOD" )
end
return false
end

View File

@@ -0,0 +1,56 @@
--
--
-- worldpicker is for picking an entity from the game while you have the GUI open.
-- Calling util.worldpicker.Start( func ) will hide all GUI and let you pick an entity from
-- the game world. Once selected it will call your passed function with the trace.
--
-- It's used in the icon editor
--
--
local bDoing = false
local fnAction = nil
util.worldpicker = {
--
-- Start world picking
--
Start = function( func )
bDoing = true
fnAction = func
gui.EnableScreenClicker( true )
end,
--
-- Finish world picking - you shouldn't have to call this (called from hook below)
--
Finish = function( tr )
bDoing = false
fnAction( tr )
gui.EnableScreenClicker( false )
end,
Active = function() return bDoing end
}
hook.Add( "VGUIMousePressAllowed", "WorldPickerMouseDisable", function( code )
if ( !bDoing ) then return end
local dir = gui.ScreenToVector( input.GetCursorPos() )
local tr = util.TraceLine( {
start = LocalPlayer():GetShootPos(),
endpos = LocalPlayer():GetShootPos() + dir * 32768,
filter = LocalPlayer()
} )
util.worldpicker.Finish( tr )
-- Don't register this click
return true
end )

View File

@@ -0,0 +1,12 @@
local meta = FindMetaTable( "Vector" )
-- Nothing in here, still leaving this file here just in case
--[[---------------------------------------------------------
Converts Vector To Color - alpha precision lost, must reset
-----------------------------------------------------------]]
function meta:ToColor( )
return Color( self.x * 255, self.y * 255, self.z * 255 )
end

View File

@@ -0,0 +1,45 @@
AddCSLuaFile()
local meta = FindMetaTable( "Weapon" )
local entity = FindMetaTable( "Entity" )
-- Return if there's nothing to add on to
if ( !meta ) then return end
--
-- Entity index accessor. This used to be done in engine, but it's done in Lua now because it's faster
--
function meta:__index( key )
--
-- Search the metatable. We can do this without dipping into C, so we do it first.
--
local val = meta[key]
if ( val != nil ) then return val end
--
-- Search the entity metatable
--
local val = entity[key]
if ( val != nil ) then return val end
--
-- Search the entity table
--
local tab = entity.GetTable( self )
if ( tab != nil ) then
local val = tab[ key ]
if ( val != nil ) then return val end
end
--
-- Legacy: sometimes use self.Owner to get the owner.. so lets carry on supporting that stupidness
-- This needs to be retired, just like self.Entity was.
--
if ( key == "Owner" ) then return entity.GetOwner( self ) end
return nil
end

115
lua/includes/gmsave.lua Normal file
View File

@@ -0,0 +1,115 @@
gmsave = {}
if ( CLIENT ) then return end
include( "gmsave/entity_filters.lua" )
include( "gmsave/player.lua" )
local g_WavSound = 1
function gmsave.LoadMap( strMapContents, ply, callback )
-- TODO: Do this in engine before sending it to this function.
-- Strip off any crap before the start char..
local startchar = string.find( strMapContents, '' )
if ( startchar != nil ) then
strMapContents = string.sub( strMapContents, startchar )
end
-- Strip off any crap after the end char..
strMapContents = strMapContents:reverse()
local startchar = string.find( strMapContents, '' )
if ( startchar != nil ) then
strMapContents = string.sub( strMapContents, startchar )
end
strMapContents = strMapContents:reverse()
-- END TODO
local tab = util.JSONToTable( strMapContents )
if ( !istable( tab ) ) then
-- Error loading save!
MsgN( "gm_load: Couldn't decode from json!" )
return false
end
-- Existing addons are forcing us to do this
-- The issue is that some addons overwrite game.CleanUpMap
-- Making the 3rd argument not exist
hook.Add( "PostCleanupMap", "GMod_Load_Save", function()
hook.Remove( "PostCleanupMap", "GMod_Load_Save" )
if ( IsValid( ply ) ) then
ply:SendLua( "hook.Run( \"OnSpawnMenuClose\" )" )
g_WavSound = g_WavSound + 1
if ( g_WavSound > 4 ) then g_WavSound = 1 end
ply:SendLua( string.format( "surface.PlaySound( \"garrysmod/save_load%d.wav\" )", g_WavSound ) )
gmsave.PlayerLoad( ply, tab.Player )
end
timer.Simple( 0.1, function()
DisablePropCreateEffect = true
duplicator.RemoveMapCreatedEntities()
duplicator.Paste( ply, tab.Entities, tab.Constraints )
DisablePropCreateEffect = nil
if ( IsValid( ply ) ) then
gmsave.PlayerLoad( ply, tab.Player )
end
-- Since this save system is inferior to Source's, we gotta make sure this entity is disabled on load of a save
-- On maps like Portal's testchmb_a_00.bsp this entity takes away player control and will not restore it
-- if the player is not in a very specific place.
timer.Simple( 1, function()
for id, ent in ipairs( ents.FindByClass( "point_viewcontrol" ) ) do
ent:Fire( "Disable" )
end
end )
if ( callback ) then callback() end
end )
end )
game.CleanUpMap( false, nil, function() end )
end
function gmsave.SaveMap( ply )
local Ents = ents.GetAll()
for k, v in ipairs( Ents ) do
if ( !gmsave.ShouldSaveEntity( v, v:GetSaveTable() ) || v:IsConstraint() ) then
Ents[ k ] = nil
end
end
-- This is to copy the constraints that are applied to the world only (ropes, etc)
-- It will not actually save and then try to restore the world entity, as that would cause issues
table.insert( Ents, game.GetWorld() )
local tab = duplicator.CopyEnts( Ents )
if ( !tab ) then return end
tab.Player = gmsave.PlayerSave( ply )
--
-- Try to figure out if any of the models/materials/etc came from some addon
--
duplicator.FigureOutRequiredAddons( tab )
return util.TableToJSON( tab )
end

View File

@@ -0,0 +1,84 @@
local ClassInfo = {}
ClassInfo[ "scene_manager" ] = { ['save'] = false }
ClassInfo[ "predicted_viewmodel" ] = { ['save'] = false }
ClassInfo[ "player" ] = { ['save'] = false }
ClassInfo[ "physgun_beam" ] = { ['save'] = false }
ClassInfo[ "worldspawn" ] = { ['save'] = false }
ClassInfo[ "player_manager" ] = { ['save'] = false }
ClassInfo[ "bodyque" ] = { ['save'] = false }
ClassInfo[ "info_player_start" ] = { ['save'] = false }
ClassInfo[ "ai_hint" ] = { ['save'] = false }
ClassInfo[ "ai_network" ] = { ['save'] = false }
ClassInfo[ "light" ] = { ['save'] = false }
ClassInfo[ "ai_network" ] = { ['save'] = false }
ClassInfo[ "env_tonemap_controller" ] = { ['save'] = false }
ClassInfo[ "path_corner" ] = { ['save'] = false }
ClassInfo[ "point_spotlight" ] = { ['save'] = false }
ClassInfo[ "func_brush" ] = { ['save'] = false }
ClassInfo[ "water_lod_control" ] = { ['save'] = false }
ClassInfo[ "spotlight_end" ] = { ['save'] = false }
ClassInfo[ "beam" ] = { ['save'] = false }
ClassInfo[ "lua_run" ] = { ['save'] = false }
ClassInfo[ "trigger_multiple" ] = { ['save'] = false }
ClassInfo[ "func_button" ] = { ['save'] = false }
ClassInfo[ "soundent" ] = { ['save'] = false }
ClassInfo[ "sky_camera" ] = { ['save'] = false }
ClassInfo[ "env_fog_controller" ] = { ['save'] = false }
ClassInfo[ "env_sun" ] = { ['save'] = false }
ClassInfo[ "phys_constraintsystem" ] = { ['save'] = false }
ClassInfo[ "keyframe_rope" ] = { ['save'] = false }
ClassInfo[ "logic_auto" ] = { ['save'] = false }
ClassInfo[ "physics_npc_solver" ] = { ['save'] = false }
ClassInfo[ "env_sun" ] = { ['save'] = false }
ClassInfo[ "env_wind" ] = { ['save'] = false }
ClassInfo[ "env_fog_controller" ] = { ['save'] = false }
ClassInfo[ "infodecal" ] = { ['save'] = false }
ClassInfo[ "info_projecteddecal" ] = { ['save'] = false }
ClassInfo[ "info_node" ] = { ['save'] = false }
ClassInfo[ "info_map_parameters" ] = { ['save'] = false }
ClassInfo[ "info_ladder" ] = { ['save'] = false }
ClassInfo[ "path_corner" ] = { ['save'] = false }
ClassInfo[ "point_viewcontrol" ] = { ['save'] = false }
ClassInfo[ "scene_manager" ] = { ['save'] = false }
ClassInfo[ "shadow_control" ] = { ['save'] = false }
ClassInfo[ "sky_camera" ] = { ['save'] = false }
ClassInfo[ "soundent" ] = { ['save'] = false }
function gmsave.ShouldSaveEntity( ent, t )
local info = ClassInfo[ t.classname ]
--
-- Filtered out - we don't want to save these entity types!
--
if ( info && info.save == false ) then return false end
--
-- Should we save the parent entity?
-- If not, don't save this!
--
local parent = ent:GetParent()
if ( IsValid( parent ) ) then
if ( !gmsave.ShouldSaveEntity( parent, parent:GetSaveTable() ) ) then return false end
end
--
-- If this is a weapon, and it has a valid owner.. don't save it!
--
if ( ent:IsWeapon() && IsValid( ent:GetOwner() ) ) then
return false
end
-- Do not save entities that do not want to be saved
if ( ent.DoNotDuplicate ) then
return false
end
--
-- Default action is to save..
--
return true
end

View File

@@ -0,0 +1,96 @@
--
-- Creates a save table for a single entity
--
function gmsave.PhysicsSave( ent )
if ( ent:GetPhysicsObjectCount() == 0 ) then return end
local tab = {}
for k = 0, ent:GetPhysicsObjectCount() - 1 do
local obj = ent:GetPhysicsObjectNum( k )
tab[ k ] = {}
tab[ k ].origin = tostring( obj:GetPos() )
tab[ k ].angles = tostring( obj:GetAngles() )
tab[ k ].mass = tostring( obj:GetMass() )
tab[ k ].material = tostring( obj:GetMaterial() )
if ( !obj:IsMotionEnabled() ) then tab[ k ].frozen = 1 end
if ( !obj:IsCollisionEnabled() ) then tab[ k ].nocollide = 1 end
if ( !obj:IsGravityEnabled() ) then tab[ k ].nogravity = 1 end
if ( !obj:IsDragEnabled() ) then tab[ k ].nodrag = 1 end
end
return tab
end
--
-- Creates a save table from a table of entities
--
function gmsave.PhysicsSaveList( ents )
local tabPhys = {}
for k, v in pairs( ents ) do
if ( !IsValid( v ) ) then continue end
tabPhys[ v.GMSaveName ] = gmsave.PhysicsSave( v )
if ( tabPhys[ v.GMSaveName ] ) then
tabPhys[ v.GMSaveName ].entity = v.GMSaveName
end
end
return tabPhys
end
--
-- Creates a single entity from a table
--
function gmsave.PhysicsLoad( t, ent )
for k = 0, ent:GetPhysicsObjectCount() - 1 do
local tab = t[ k ]
if ( !tab ) then continue end
local obj = ent:GetPhysicsObjectNum( k )
if ( !IsValid( obj ) ) then continue end
obj:SetPos( Vector( tab.origin ) )
obj:SetAngles( Angle( tab.angles ) )
obj:SetMass( tab.mass )
obj:SetMaterial( tab.material )
if ( tab.frozen ) then obj:EnableMotion( false ) end
if ( tab.nocollide ) then obj:EnableCollisions( false ) end
if ( tab.nogravity ) then obj:EnableGravity( false ) end
if ( tab.nodrag ) then obj:EnableDrag( false ) end
end
end
--
-- Restores multiple entitys from a table
--
function gmsave.PhysicsLoadFromTable( tab, ents )
if ( !tab ) then return end
for k, v in pairs( tab ) do
local ent = ents[ k ]
if ( !IsValid( ent ) ) then continue end
gmsave.PhysicsLoad( v, ent )
end
end

View File

@@ -0,0 +1,22 @@
function gmsave.PlayerSave( ent )
local tab = {}
tab.Origin = ent:GetPos()
tab.Angle = ent:GetAimVector():Angle()
tab.MoveType = ent:GetMoveType()
return tab
end
function gmsave.PlayerLoad( ent, tab )
if ( tab == nil ) then return end
if ( tab.Origin ) then ent:SetPos( tab.Origin ) end
if ( tab.Angle ) then ent:SetEyeAngles( tab.Angle ) end
if ( tab.MoveType ) then ent:SetMoveType( tab.MoveType ) end
end

View File

@@ -0,0 +1,57 @@
local g_Progress = nil
hook.Add( "SpawniconGenerated", "SpawniconGenerated", function( lastmodel, imagename, modelsleft )
if ( !IsValid( g_Progress ) ) then
g_Progress = vgui.Create( "DPanel" )
g_Progress:SetSize( 64 + 10, 64 + 10 + 20 )
g_Progress:SetBackgroundColor( Color( 0, 0, 0, 100 ) )
g_Progress:SetDrawOnTop( true )
g_Progress:DockPadding( 5, 0, 5, 5 )
g_Progress.Think = function()
if ( SysTime() - g_Progress.LastTouch < 3 ) then return end
g_Progress:Remove()
g_Progress.LastTouch = SysTime()
end
local label = g_Progress:Add( "DLabel" )
label:Dock( BOTTOM )
label:SetText( "remaining" )
label:SetTextColor( color_white )
label:SetExpensiveShadow( 1, Color( 0, 0, 0, 200 ) )
label:SetContentAlignment( 5 )
label:SetHeight( 14 )
label:SetFont( "DefaultSmall" )
g_Progress.Label = g_Progress:Add( "DLabel" )
g_Progress.Label:Dock( BOTTOM )
g_Progress.Label:SetTextColor( color_white )
g_Progress.Label:SetExpensiveShadow( 1, Color( 0, 0, 0, 200 ) )
g_Progress.Label:SetContentAlignment( 5 )
g_Progress.Label:SetFont( "DermaDefaultBold" )
g_Progress.Label:SetHeight( 14 )
g_Progress.icon = vgui.Create( "DImage", g_Progress )
g_Progress.icon:SetSize( 64, 64 )
g_Progress.icon:Dock( TOP )
end
g_Progress.LastTouch = SysTime()
imagename = imagename:Replace( "materials\\", "" )
imagename = imagename:Replace( "materials/", "" )
g_Progress.icon:SetImage( imagename )
g_Progress:AlignRight( 10 )
g_Progress:AlignBottom( 10 )
g_Progress.Label:SetText( modelsleft )
end )

124
lua/includes/init.lua Normal file
View File

@@ -0,0 +1,124 @@
--[[---------------------------------------------------------
Non-Module includes
-----------------------------------------------------------]]
include( "util.lua" ) -- Misc Utilities
include( "util/sql.lua" ) -- Includ sql here so it's available at loadtime to modules.
include( "extensions/net.lua" )
--[[---------------------------------------------------------
Shared Modules
-----------------------------------------------------------]]
require( "baseclass" )
require( "concommand" ) -- Console Commands
require( "saverestore" ) -- Save/Restore
require( "hook" ) -- Gamemode hooks
require( "gamemode" ) -- Gamemode manager
require( "weapons" ) -- SWEP manager
require( "scripted_ents" ) -- Scripted Entities
require( "player_manager" ) -- Player models/class manager
require( "numpad" )
require( "team" )
require( "undo" )
require( "cleanup" )
require( "duplicator" )
require( "constraint" )
require( "construct" )
require( "usermessage" )
require( "list" )
require( "cvars" )
require( "http" )
require( "properties" )
require( "widget" )
require( "cookie" )
require( "utf8" )
require( "drive" )
include( "drive/drive_base.lua" )
include( "drive/drive_noclip.lua" )
--[[---------------------------------------------------------
Serverside only modules
-----------------------------------------------------------]]
if ( SERVER ) then
require( "ai_task" )
require( "ai_schedule" )
end
--[[---------------------------------------------------------
Clientside only modules
-----------------------------------------------------------]]
if ( CLIENT ) then
require( "draw" ) -- 2D Draw library
require( "markup" ) -- Text markup library
require( "effects" )
require( "halo" )
require( "killicon" )
require( "spawnmenu" )
require( "controlpanel" )
require( "presets" )
require( "menubar" )
require( "matproxy" )
include( "util/model_database.lua" ) -- Store information on models as they're loaded
include( "util/vgui_showlayout.lua" ) -- VGUI Performance Debug
include( "util/tooltips.lua" )
include( "util/client.lua" )
include( "util/javascript_util.lua" )
include( "util/workshop_files.lua" )
include( "gui/icon_progress.lua" )
end
--[[---------------------------------------------------------
Shared modules
-----------------------------------------------------------]]
include( "gmsave.lua" )
--[[---------------------------------------------------------
Extensions
Load extensions that we specifically need for the menu,
to reduce the chances of loading something that might
cause errors.
-----------------------------------------------------------]]
include( "extensions/entity_iter.lua" )
include( "extensions/file.lua" )
include( "extensions/angle.lua" )
include( "extensions/debug.lua" )
include( "extensions/entity.lua" )
include( "extensions/ents.lua" )
include( "extensions/math.lua" )
include( "extensions/player.lua" )
include( "extensions/player_auth.lua" )
include( "extensions/string.lua" )
include( "extensions/table.lua" )
include( "extensions/util.lua" )
include( "extensions/vector.lua" )
include( "extensions/game.lua" )
include( "extensions/motionsensor.lua" )
include( "extensions/weapon.lua" )
include( "extensions/coroutine.lua" )
if ( CLIENT ) then
include( "extensions/client/entity.lua" )
include( "extensions/client/globals.lua" )
include( "extensions/client/panel.lua" )
include( "extensions/client/player.lua" )
include( "extensions/client/render.lua" )
require( "search" )
end

View File

@@ -0,0 +1,45 @@
--[[---------------------------------------------------------
Non-Module includes
-----------------------------------------------------------]]
include( "util.lua" )
include( "util/sql.lua" ) -- Include sql here so it's available at loadtime to modules.
--[[---------------------------------------------------------
Modules
-----------------------------------------------------------]]
require( "concommand" )
require( "list" )
require( "hook" )
require( "draw" )
require( "http" )
require( "cvars" )
require( "cookie" )
require( "baseclass" )
require( "utf8" )
require( "markup" )
--[[---------------------------------------------------------
Extensions
Load extensions that we specifically need for the menu,
to reduce the chances of loading something that might
cause errors.
-----------------------------------------------------------]]
include( "extensions/string.lua" )
include( "extensions/table.lua" )
include( "extensions/math.lua" )
include( "extensions/client/panel.lua" )
include( "extensions/util.lua" )
include( "extensions/file.lua" )
include( "extensions/debug.lua" )
include( "extensions/client/render.lua" )
include( "extensions/client/globals.lua" )
include( "util/vgui_showlayout.lua" )
include( "util/workshop_files.lua" )
include( "util/javascript_util.lua" )
include( "util/tooltips.lua" )
include( "menu/derma_icon_browser.lua" )

9
lua/includes/menu.lua Normal file
View File

@@ -0,0 +1,9 @@
--=============================================================================--
-- ___ ___ _ _ _ __ _ ___ ___ __ __
-- |_ _|| __| / \ | \_/ | / _| / \ | o \ o \\ V /
-- | | | _| | o || \_/ | ( |_n| o || / / \ /
-- |_| |___||_n_||_| |_| \__/|_n_||_|\\_|\\ |_| 2007
--
--=============================================================================--
include( "menu/menu.lua" )

View File

@@ -0,0 +1,98 @@
-- Serverside only.
if ( CLIENT ) then return end
local setmetatable = setmetatable
local tostring = tostring
local table = table
local ai_task = ai_task
module( "ai_schedule" )
-- Define the Schedule object
local Schedule = {}
Schedule.__index = Schedule
--[[---------------------------------------------------------
Name: Init
Sets the object up. Called automatically from ai_schedule.new.
-----------------------------------------------------------]]
function Schedule:Init( _debugname_ )
self.DebugName = tostring( _debugname_ )
self.Tasks = {}
self.TaskCount = 0
end
--[[---------------------------------------------------------
Adds an engine task to the schedule
schd:EngTask( "TASK_TARGET_PLAYER", 0 )
-----------------------------------------------------------]]
function Schedule:EngTask( _taskname_, _taskdata_ )
local NewTask = ai_task.New()
NewTask:InitEngine( _taskname_, _taskdata_ )
self.TaskCount = table.insert( self.Tasks, NewTask )
end
--[[---------------------------------------------------------
Adds Lua NPC task to the schedule
schdChase:AddTask( "LookForPlayer", "AnyDataYouWant" )
will call the functions
NPC:TaskStart_LookForPlayer( "AnyDataYouWant" ) - once on start
NPC:Task_LookForPlayer( "AnyDataYouWant" ) - every think until TaskComplete
-----------------------------------------------------------]]
function Schedule:AddTask( _functionname_, _data_ )
local NewTask = ai_task.New()
NewTask:InitFunctionName( "TaskStart_" .. _functionname_, "Task_" .. _functionname_, _data_ )
self.TaskCount = table.insert( self.Tasks, NewTask )
end
--[[---------------------------------------------------------
The same as above but you get to specify the exact
function name to call
-----------------------------------------------------------]]
function Schedule:AddTaskEx( _start, _run, _data_ )
local NewTask = ai_task.New()
NewTask:InitFunctionName( _start, _run, _data_ )
self.TaskCount = table.insert( self.Tasks, NewTask )
end
function Schedule:NumTasks()
return self.TaskCount
end
function Schedule:GetTask( num )
return self.Tasks[ num ]
end
--[[---------------------------------------------------------
Create a new empty task (this is ai_schedule.New )
-----------------------------------------------------------]]
function New( debugname )
local pNewSchedule = {}
setmetatable( pNewSchedule, Schedule )
pNewSchedule:Init( debugname )
return pNewSchedule
end

View File

@@ -0,0 +1,123 @@
-- Serverside only.
if ( CLIENT ) then return end
local setmetatable = setmetatable
--local table = table
local ai = ai
module( "ai_task" )
--[[---------------------------------------------------------
ENUMs for which kind of task it is.
-----------------------------------------------------------]]
local TYPE_ENGINE = 1
local TYPE_FNAME = 2
--[[---------------------------------------------------------
Keep track of created tasks
UNDONE: There's no need for this right now.
-----------------------------------------------------------]]
--local Task_Index = {}
--[[---------------------------------------------------------
Task metatable
-----------------------------------------------------------]]
local Task = {}
Task.__index = Task
function Task:Init()
self.Type = nil
end
--[[---------------------------------------------------------
Creates an engine based task
-----------------------------------------------------------]]
function Task:InitEngine( _taskname_, _taskdata_ )
self.TaskName = _taskname_
self.TaskID = nil
self.TaskData = _taskdata_
self.Type = TYPE_ENGINE
end
--[[---------------------------------------------------------
Creates an engine based task
-----------------------------------------------------------]]
function Task:InitFunctionName( _start, _end, _taskdata_ )
self.StartFunctionName = _start
self.FunctionName = _end
self.TaskData = _taskdata_
self.Type = TYPE_FNAME
end
function Task:IsEngineType()
return ( self.Type == TYPE_ENGINE )
end
function Task:IsFNameType()
return ( self.Type == TYPE_FNAME )
end
function Task:Start( npc )
if ( self:IsFNameType() ) then self:Start_FName( npc ) return end
if ( self:IsEngineType() ) then
if ( !self.TaskID ) then self.TaskID = ai.GetTaskID( self.TaskName ) end
npc:StartEngineTask( self.TaskID, self.TaskData )
end
end
--[[---------------------------------------------------------
Start_FName (called from Task:Start)
-----------------------------------------------------------]]
function Task:Start_FName( npc )
if ( !self.StartFunctionName ) then return end
--if ( !npc[ self.StartFunctionName ] ) then return end
-- Run the start function. Safely.
npc[ self.StartFunctionName ]( npc, self.TaskData )
end
function Task:Run( npc )
if ( self:IsFNameType() ) then self:Run_FName( npc ) return end
if ( self:IsEngineType() ) then
npc:RunEngineTask( self.TaskID, self.TaskData )
end
end
function Task:Run_FName( npc )
if ( !self.FunctionName ) then return end
--if (!npc[ self.StartFunctionName ]) then return end
-- Run the start function. Safely.
npc[ self.FunctionName ]( npc, self.TaskData )
end
--[[---------------------------------------------------------
Create a new empty task (this is ai_task.New )
-----------------------------------------------------------]]
function New()
local pNewTask = {}
setmetatable( pNewTask, Task )
pNewTask:Init()
--table.insert( Task_Index, pNewTask )
return pNewTask
end

View File

@@ -0,0 +1,58 @@
--
-- The baseclass module uses upvalues to give the impression of inheritence.
--
-- At the top of your class file add
--
-- DEFINE_BASECLASS( "base_class_name" )
--
-- Now the local variable BaseClass will be available.
-- ( in engine DEFINE_BASECLASS is replaced with "local BaseClass = baseclass.Get" )
--
-- Baseclasses are added using baseclass.Set - this is done automatically for:
--
-- > widgets
-- > panels
-- > drive modes
-- > entities
-- > weapons
--
-- Classes don't have to be created in any particular order. The system is
-- designed to work with whatever order the classes are defined.
--
-- The only caveat is that classnames must be unique.
-- eg Creating a panel and widget with the same name will cause problems.
--
module( "baseclass", package.seeall )
local BaseClassTable = {}
function Get( name )
if ( ENT ) then ENT.Base = name end
if ( SWEP ) then SWEP.Base = name end
BaseClassTable[name] = BaseClassTable[name] or {}
return BaseClassTable[name]
end
function Set( name, tab )
if ( !BaseClassTable[name] ) then
BaseClassTable[name] = tab
else
table.Merge( BaseClassTable[name], tab )
setmetatable( BaseClassTable[name], getmetatable(tab) )
end
BaseClassTable[name].ThisClass = name
end

View File

@@ -0,0 +1,241 @@
module( "cleanup", package.seeall )
local cleanup_types = {}
local function IsType( type )
for key, val in pairs( cleanup_types ) do
if ( val == type ) then return true end
end
return false
end
function Register( type )
if ( type == "all" ) then return end
for key, val in pairs( cleanup_types ) do
if val == type then return end
end
table.insert( cleanup_types, type )
end
function GetTable()
return cleanup_types
end
if ( SERVER ) then
local cleanup_list = {}
function GetList()
return cleanup_list
end
local function Save( save )
saverestore.WriteTable( cleanup_list, save )
end
local function Restore( restore )
cleanup_list = saverestore.ReadTable( restore )
end
saverestore.AddSaveHook( "CleanupTable", Save )
saverestore.AddRestoreHook( "CleanupTable", Restore )
function Add( pl, type, ent )
if ( !ent ) then return end
if ( !IsType( type ) ) then return end
local id = pl:UniqueID()
cleanup_list[ id ] = cleanup_list[ id ] or {}
cleanup_list[ id ][ type ] = cleanup_list[ id ][ type ] or {}
if ( !IsValid( ent ) ) then return end
table.insert( cleanup_list[ id ][ type ], ent )
end
function ReplaceEntity( from, to )
local ActionTaken = false
for _, PlayerTable in pairs( cleanup_list ) do
for _, TypeTable in pairs( PlayerTable ) do
for key, ent in pairs( TypeTable ) do
if ( ent == from ) then
TypeTable[ key ] = to
ActionTaken = true
end
end
end
end
return ActionTaken
end
function CC_Cleanup( pl, command, args )
if ( !IsValid( pl ) ) then return end
local id = pl:UniqueID()
if ( !cleanup_list[ id ] ) then return end
if ( !args[ 1 ] ) then
local count = 0
for key, val in pairs( cleanup_list[ id ] ) do
for _, ent in pairs( val ) do
if ( IsValid( ent ) ) then ent:Remove() end
count = count + 1
end
table.Empty( val )
end
-- Send tooltip command to client
if ( count > 0 ) then
pl:SendLua( "hook.Run('OnCleanup','all')" )
end
return
end
if ( !IsType( args[1] ) ) then return end
if ( !cleanup_list[id][ args[1] ] ) then return end
for key, ent in pairs( cleanup_list[id][ args[1] ] ) do
if ( IsValid( ent ) ) then ent:Remove() end
end
table.Empty( cleanup_list[id][ args[1] ] )
-- Send tooltip command to client
pl:SendLua( string.format( "hook.Run('OnCleanup',%q)", args[1] ) )
end
function CC_AdminCleanup( pl, command, args )
if ( IsValid( pl ) && !pl:IsAdmin() ) then return end
if ( !args[ 1 ] ) then
for key, ply in pairs( cleanup_list ) do
for _, type in pairs( ply ) do
for __, ent in pairs( type ) do
if ( IsValid( ent ) ) then ent:Remove() end
end
table.Empty( type )
end
end
game.CleanUpMap( false, nil, function()
-- Send tooltip command to client
if ( IsValid( pl ) ) then pl:SendLua( "hook.Run('OnCleanup','all')" ) end
end )
return
end
if ( !IsType( args[ 1 ] ) ) then return end
for key, ply in pairs( cleanup_list ) do
if ( ply[ args[ 1 ] ] != nil ) then
for id, ent in pairs( ply[ args[ 1 ] ] ) do
if ( IsValid( ent ) ) then ent:Remove() end
end
table.Empty( ply[ args[ 1 ] ] )
end
end
-- Send tooltip command to client
if ( IsValid( pl ) ) then pl:SendLua( string.format( "hook.Run('OnCleanup',%q)", args[1] ) ) end
end
concommand.Add( "gmod_cleanup", CC_Cleanup, nil, "", { FCVAR_DONTRECORD } )
concommand.Add( "gmod_admin_cleanup", CC_AdminCleanup, nil, "", { FCVAR_DONTRECORD } )
else
function UpdateUI()
local cleanup_types_s = {}
for id, val in pairs( cleanup_types ) do
cleanup_types_s[ language.GetPhrase( "Cleanup_" .. val ) ] = val
end
local Panel = controlpanel.Get( "User_Cleanup" )
if ( IsValid( Panel ) ) then
Panel:Clear()
Panel:Help( "#spawnmenu.utilities.cleanup.help" )
Panel:Button( "#spawnmenu.utilities.cleanup.all", "gmod_cleanup" )
for key, val in SortedPairs( cleanup_types_s ) do
Panel:Button( key, "gmod_cleanup", val )
end
end
local AdminPanel = controlpanel.Get( "Admin_Cleanup" )
if ( IsValid( AdminPanel ) ) then
AdminPanel:Clear()
AdminPanel:Help( "#spawnmenu.utilities.cleanup.help" )
AdminPanel:Button( "#spawnmenu.utilities.cleanup.all", "gmod_admin_cleanup" )
for key, val in SortedPairs( cleanup_types_s ) do
AdminPanel:Button( key, "gmod_admin_cleanup", val )
end
end
end
hook.Add( "PostReloadToolsMenu", "BuildCleanupUI", UpdateUI )
end

View File

@@ -0,0 +1,75 @@
local AddConsoleCommand = AddConsoleCommand
local string = string
local Msg = Msg
--[[---------------------------------------------------------
Name: concommand
Desc: A module to take care of the registration and calling
of Lua console commands.
-----------------------------------------------------------]]
module( "concommand" )
local CommandList = {}
local CompleteList = {}
--[[---------------------------------------------------------
Name: concommand.GetTable( )
Desc: Returns the table of console commands and auto complete
-----------------------------------------------------------]]
function GetTable()
return CommandList, CompleteList
end
--[[---------------------------------------------------------
Name: concommand.Add( name, func, completefunc )
Desc: Register a new console command
-----------------------------------------------------------]]
function Add( name, func, completefunc, help, flags )
local LowerName = string.lower( name )
CommandList[ LowerName ] = func
CompleteList[ LowerName ] = completefunc
AddConsoleCommand( name, help, flags )
end
--[[---------------------------------------------------------
Name: concommand.Remove( name )
Desc: Removes a console command
-----------------------------------------------------------]]
function Remove( name )
local LowerName = string.lower( name )
CommandList[ LowerName ] = nil
CompleteList[ LowerName ] = nil
end
--[[---------------------------------------------------------
Name: concommand.Run( )
Desc: Called by the engine when an unknown console command is run
-----------------------------------------------------------]]
function Run( player, command, arguments, argumentsStr )
local LowerCommand = string.lower( command )
if ( CommandList[ LowerCommand ] != nil ) then
CommandList[ LowerCommand ]( player, command, arguments, argumentsStr )
return true
end
Msg( "Unknown command: " .. command .. "\n" )
return false
end
--[[---------------------------------------------------------
Name: concommand.AutoComplete( )
Desc: Returns a table for the autocompletion
-----------------------------------------------------------]]
function AutoComplete( command, argumentsStr, arguments )
local LowerCommand = string.lower( command )
if ( CompleteList[ LowerCommand ] != nil ) then
return CompleteList[ LowerCommand ]( command, argumentsStr, arguments )
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
local ents = ents
local SERVER = SERVER
local duplicator = duplicator
local numpad = numpad
local Msg = Msg
local IsValid = IsValid
module( "construct" )
if SERVER then
function SetPhysProp( Player, Entity, BoneID, Bone, Data )
if ( !IsValid( Bone ) ) then
Bone = Entity:GetPhysicsObjectNum( BoneID )
if ( !IsValid( Bone ) ) then
Msg( "SetPhysProp: Error applying attributes to invalid physics object!\n" )
return
end
end
if ( Data.GravityToggle != nil ) then Bone:EnableGravity( Data.GravityToggle ) end
if ( Data.Material != nil ) then Bone:SetMaterial( Data.Material ) end
-- todo: Rename/Implement
--[[
if ( Data.motionb != nil ) then Bone:EnableMotion( Data.motionb ) end
if ( Data.mass != nil ) then Bone:SetMass( Data.mass ) end
if ( Data.dragb != nil ) then Bone:EnableDrag( Data.dragb ) end
if ( Data.drag != nil ) then Bone:SetDragCoefficient( Data.drag ) end
if ( Data.buoyancy != nil ) then Bone:SetBuoyancyRatio( Data.buoyancy ) end
if ( Data.rotdamping != nil ) then Bone:SetDamping( PhysBone:GetSpeedDamping(), Data.rotdamping ) end
if ( Data.speeddamping != nil ) then Bone:SetDamping( Data.speeddamping, PhysBone:GetRotDamping() ) end
--]]
-- HACK HACK
-- If we don't do this the prop will be motion enabled and will
-- slide through the world with no gravity.
if ( !Bone:IsMoveable() ) then
Bone:EnableMotion( true )
Bone:EnableMotion( false )
end
duplicator.StoreBoneModifier( Entity, BoneID, "physprops", Data )
end
duplicator.RegisterBoneModifier( "physprops", SetPhysProp )
local function MagnetOff( pl, magnet )
if ( !IsValid( magnet ) ) then return false end
if ( magnet:GetTable().toggle != 0 ) then return true end
magnet:Fire( "TurnOff", "" , 0 )
return true
end
local function MagnetOn( pl, magnet )
if ( !IsValid( magnet ) ) then return false end
if ( magnet:GetTable().toggle != 0 ) then
magnet:GetTable().toggle_state = !magnet:GetTable().toggle_state
if ( magnet:GetTable().toggle_state ) then
magnet:Fire( "TurnOn", "" , 0 )
else
magnet:Fire( "TurnOff", "" , 0 )
end
return true
end
magnet:Fire( "TurnOn", "" , 0 )
return true
end
numpad.Register( "MagnetOff", MagnetOff )
numpad.Register( "MagnetOn", MagnetOn )
function Magnet( pl, pos, angle, model, material, key, maxobjects, strength, nopull, allowrot, alwayson, toggle, Vel, aVel, frozen )
local magnet = ents.Create( "phys_magnet" )
magnet:SetPos( pos )
magnet:SetAngles( angle )
magnet:SetModel( model )
if ( material ) then magnet:SetMaterial( material ) end
local spawnflags = 4
if ( nopull && nopull > 0 ) then spawnflags = spawnflags - 4 end -- no pull required: remove the suck flag
if ( allowrot && allowrot > 0 ) then spawnflags = spawnflags + 8 end
if ( maxobjects ) then magnet:SetKeyValue( "maxobjects", maxobjects ) end
if ( strength ) then magnet:SetKeyValue( "forcelimit", strength ) end
magnet:SetKeyValue( "overridescript", "surfaceprop,metal")
magnet:SetKeyValue( "massScale", 0 )
magnet:Activate()
magnet:Spawn()
if ( IsValid( magnet:GetPhysicsObject() ) ) then
local Phys = magnet:GetPhysicsObject()
if ( Vel ) then Phys:SetVelocity( Vel ) end
if ( aVel ) then Phys:AddAngleVelocity( aVel ) end
Phys:EnableMotion( frozen != true )
end
if ( alwayson && alwayson > 0 ) then
magnet:Input( "TurnOn", nil, nil, nil )
else
magnet:Input( "TurnOff", nil, nil, nil )
end
local mtable = {
Model = model,
material = material,
key = key,
maxobjects = maxobjects,
strength = strength,
nopull = nopull,
allowrot = allowrot,
alwayson = alwayson,
toggle = toggle
}
magnet:SetTable( mtable )
if ( key ) then
numpad.OnDown( pl, key, "MagnetOn", magnet )
numpad.OnUp( pl, key, "MagnetOff", magnet )
end
return magnet
end
duplicator.RegisterEntityClass( "phys_magnet", Magnet, "Pos", "Ang", "Model", "material", "key", "maxobjects", "strength", "nopull", "allowrot", "alwayson", "toggle", "Vel", "aVel", "frozen" )
end

View File

@@ -0,0 +1,47 @@
local ControlPanels = {}
module( "controlpanel", package.seeall )
-- A hack for a very annoying race condition where spawnmenu_reload deletes the controlpanels on the next frame
-- But some panels are updated "this" frame after spawnmenu reloaded
local function ShouldReCreate( pnl )
if ( !IsValid( pnl ) || pnl:IsMarkedForDeletion() ) then return true end
local p = pnl
-- Can't use IsValid because it's false for marked for deletion panels
while ( IsValid( p ) && p:GetParent() != nil ) do
if ( p:GetParent():IsMarkedForDeletion() ) then return true end
p = p:GetParent()
end
return false
end
function Get( name )
if ( ShouldReCreate( ControlPanels[ name ] ) ) then
local cp = vgui.Create( "ControlPanel" )
if ( !cp ) then
debug.Trace()
Error( "controlpanel.Get() - Error creating a ControlPanel!\nYou're calling this function too early! Call it in a hook!\n" )
return nil
end
cp:SetVisible( false )
cp.Name = name
ControlPanels[ name ] = cp
end
return ControlPanels[ name ]
end
function Clear()
ControlPanels = {}
end

Some files were not shown because too many files have changed in this diff Show More