mirror of
https://github.com/IkarusHD/ICF-3.git
synced 2025-03-04 03:03:12 -05:00
Merge pull request #426 from marchc1/baseplate-ents
Add baseplate entities
This commit is contained in:
commit
387945561c
87
lua/acf/compatibility/baseplate_convert_sv.lua
Normal file
87
lua/acf/compatibility/baseplate_convert_sv.lua
Normal file
@ -0,0 +1,87 @@
|
||||
local ACF = ACF
|
||||
|
||||
local RecursiveEntityRemove
|
||||
function RecursiveEntityRemove(ent, track)
|
||||
track = track or {}
|
||||
if track[ent] == true then return end
|
||||
local constrained = constraint.GetAllConstrainedEntities(ent)
|
||||
ent:Remove()
|
||||
track[ent] = true
|
||||
for k, _ in pairs(constrained) do
|
||||
if k ~= ent then RecursiveEntityRemove(k, track) end
|
||||
end
|
||||
end
|
||||
|
||||
local bpConvertibleModelPaths = {
|
||||
{
|
||||
startWith = "models/sprops/rectangles",
|
||||
addAngles = Angle(0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function ACF.ConvertEntityToBaseplate(Player, Target)
|
||||
if not AdvDupe2 then return false, "Advanced Duplicator 2 is not installed" end
|
||||
|
||||
if not IsValid(Target) then return false, "Invalid target" end
|
||||
|
||||
local Owner = Target:CPPIGetOwner()
|
||||
if not IsValid(Owner) or Owner ~= Player then return false, "You do not own this entity" end
|
||||
|
||||
local PhysObj = Target:GetPhysicsObject()
|
||||
if not IsValid(PhysObj) then return false, "Entity is not physical" end
|
||||
|
||||
if Target:GetClass() ~= "prop_physics" then return false, "Entity must be typeof 'prop_physics'" end
|
||||
|
||||
local foundTranslation
|
||||
local targetModel = Target:GetModel()
|
||||
|
||||
for _, v in ipairs(bpConvertibleModelPaths) do
|
||||
if string.StartsWith(targetModel, v.startWith) then
|
||||
foundTranslation = v
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not foundTranslation then return false, "Incompatible model '" .. targetModel .. "'" end
|
||||
|
||||
local AMi, AMa = PhysObj:GetAABB()
|
||||
local BoxSize = AMa - AMi
|
||||
|
||||
-- Duplicate the entire thing
|
||||
local Entities, Constraints = AdvDupe2.duplicator.Copy(Player, Target, {}, {}, vector_origin)
|
||||
|
||||
-- Find the baseplate
|
||||
local Baseplate = Entities[Target:EntIndex()]
|
||||
|
||||
-- Setup the dupe table to convert it to a baseplate
|
||||
local w, l, t = BoxSize.x, BoxSize.y, BoxSize.z
|
||||
Baseplate.Class = "acf_baseplate"
|
||||
Baseplate.Length = w
|
||||
Baseplate.Width = l
|
||||
Baseplate.Thickness = t
|
||||
Baseplate.PhysicsObjects[0].Angle = Baseplate.PhysicsObjects[0].Angle + foundTranslation.addAngles
|
||||
|
||||
-- Delete everything now
|
||||
for k, _ in pairs(Entities) do
|
||||
local e = Entity(k)
|
||||
if IsValid(e) then e:Remove() end
|
||||
end
|
||||
|
||||
-- Paste the stuff back to the dupe
|
||||
local Ents = AdvDupe2.duplicator.Paste(Owner, Entities, Constraints, vector_origin, angle_zero, vector_origin, true)
|
||||
-- Try to find the baseplate
|
||||
local NewBaseplate
|
||||
for _, v in pairs(Ents) do
|
||||
if v:GetClass() == "acf_baseplate" and v:GetPos() == Baseplate.Pos then
|
||||
NewBaseplate = v
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
undo.Create("acf_baseplate")
|
||||
undo.AddEntity(NewBaseplate)
|
||||
undo.SetPlayer(Player)
|
||||
undo.Finish()
|
||||
|
||||
return true, NewBaseplate
|
||||
end
|
@ -307,7 +307,7 @@ do -- ASSUMING DIRECT CONTROL
|
||||
local Ent = self:GetEntity()
|
||||
|
||||
-- Required due for AD2 support, if this isn't present then entities will never get set to their required weight on dupe paste
|
||||
if Ent.IsACFEntity then Contraption.SetMass(Ent, Ent.ACF.Mass) return end
|
||||
if Ent.IsACFEntity and not Ent.ACF_UserWeighable then Contraption.SetMass(Ent, Ent.ACF.Mass) return end
|
||||
|
||||
if Ent.ACF_OnMassChange then
|
||||
Ent:ACF_OnMassChange(self:GetMass(), Mass)
|
||||
|
@ -20,9 +20,10 @@ local function GetEntityTable(Class)
|
||||
|
||||
if not Data then
|
||||
Data = {
|
||||
Lookup = {},
|
||||
Count = 0,
|
||||
List = {},
|
||||
Lookup = {},
|
||||
Count = 0,
|
||||
List = {},
|
||||
Restrictions = {}
|
||||
}
|
||||
|
||||
Entries[Class] = Data
|
||||
@ -55,11 +56,188 @@ local function AddArguments(Entity, Arguments)
|
||||
return List
|
||||
end
|
||||
|
||||
local ArgumentTypes = {}
|
||||
|
||||
local function AddArgumentRestrictions(Entity, ArgumentRestrictions)
|
||||
local Restrictions = Entity.Restrictions
|
||||
|
||||
for k, v in pairs(ArgumentRestrictions) do
|
||||
if not v.Type then error("Argument '" .. tostring(k or "<NIL>") .. "' didn't have a Type!") end
|
||||
if not isstring(v.Type) then error("Argument '" .. tostring(k or "<NIL>") .. "' has a non-string Type! (" .. tostring(v.Type) .. ")") end
|
||||
if not ArgumentTypes[v.Type] then error("Argument '" .. tostring(k or "<NIL>") .. "' has a non-registered Type! (" .. tostring(v.Type) .. ")") end
|
||||
|
||||
Restrictions[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Adds an argument type and verifier to the ArgumentTypes dictionary.
|
||||
--- @param Type string The type of data
|
||||
--- @param Verifier function The verification function. Arguments are: Value:any, Restrictions:table. Must return a Value of the same type and NOT nil!
|
||||
function Entities.AddArgumentType(Type, Verifier)
|
||||
if ArgumentTypes[Type] then return end
|
||||
|
||||
ArgumentTypes[Type] = Verifier
|
||||
end
|
||||
|
||||
Entities.AddArgumentType("Number", function(Value, Specs)
|
||||
if not isnumber(Value) then Value = ACF.CheckNumber(Value, Specs.Default or 0) end
|
||||
|
||||
if Specs.Decimals then Value = math.Round(Value, Specs.Decimals) end
|
||||
if Specs.Min then Value = math.max(Value, Specs.Min) end
|
||||
if Specs.Max then Value = math.min(Value, Specs.Max) end
|
||||
|
||||
return Value
|
||||
end)
|
||||
|
||||
--- Adds extra arguments to a class which has been created via Entities.AutoRegister() (or Entities.Register() with no arguments)
|
||||
--- @param Class string A class previously registered as an entity class
|
||||
--- @param DataKeys table A key-value table, where key is the name of the data and value defines the type and restrictions of the data.
|
||||
function Entities.AddStrictArguments(Class, DataKeys)
|
||||
if not isstring(Class) then return end
|
||||
|
||||
local Entity = GetEntityTable(Class)
|
||||
local Arguments = table.GetKeys(DataKeys)
|
||||
local List = AddArguments(Entity, Arguments)
|
||||
AddArgumentRestrictions(Entity, DataKeys)
|
||||
return List
|
||||
end
|
||||
|
||||
-- Automatically registers an entity. This MUST be the last line in entity/init.lua for everything to work properly
|
||||
-- Can be passed with an ENT table if you have some weird usecase, but auto defaults to _G.ENT
|
||||
--- @param ENT table A scripted entity class definition (see https://wiki.facepunch.com/gmod/Structures/ENT)
|
||||
function Entities.AutoRegister(ENT)
|
||||
if ENT == nil then ENT = _G.ENT end
|
||||
if not ENT then error("Called Entities.AutoRegister(), but no entity was in the process of being created.") end
|
||||
|
||||
local Class = string.Split(ENT.Folder, "/"); Class = Class[#Class]
|
||||
ENT.ACF_Class = Class
|
||||
|
||||
local Entity = GetEntityTable(Class)
|
||||
local ArgsList = Entities.AddStrictArguments(Class, ENT.ACF_DataKeys or {})
|
||||
|
||||
if CLIENT then return end
|
||||
|
||||
if isnumber(ENT.ACF_Limit) then
|
||||
CreateConVar(
|
||||
"sbox_max_" .. Class,
|
||||
ENT.ACF_Limit,
|
||||
FCVAR_ARCHIVE + FCVAR_NOTIFY,
|
||||
"Maximum amount of " .. (ENT.PluralName or (Class .. " entities")) .. " a player can create."
|
||||
)
|
||||
end
|
||||
|
||||
-- Verification function
|
||||
local function VerifyClientData(ClientData)
|
||||
local Entity = GetEntityTable(Class)
|
||||
local List = Entity.List
|
||||
local Restrictions = Entity.Restrictions
|
||||
|
||||
for _, argName in ipairs(List) do
|
||||
if Restrictions[argName] then
|
||||
local RestrictionSpecs = Restrictions[argName]
|
||||
if not ArgumentTypes[RestrictionSpecs.Type] then error("No verification function for type '" .. tostring(RestrictionSpecs.Type or "<NIL>") .. "'") end
|
||||
ClientData[argName] = ArgumentTypes[RestrictionSpecs.Type](ClientData[argName], RestrictionSpecs)
|
||||
end
|
||||
end
|
||||
|
||||
if ENT.ACF_OnVerifyClientData then ENT.ACF_OnVerifyClientData(ClientData) end
|
||||
end
|
||||
|
||||
local function UpdateEntityData(self, ClientData)
|
||||
local Entity = GetEntityTable(Class)
|
||||
local List = Entity.List
|
||||
|
||||
if self.ACF_PreUpdateEntityData then self:ACF_PreUpdateEntityData(ClientData) end
|
||||
self.ACF = self.ACF or {}
|
||||
for _, v in ipairs(List) do
|
||||
self[v] = ClientData[v]
|
||||
end
|
||||
|
||||
if self.ACF_PostUpdateEntityData then self:ACF_PostUpdateEntityData(ClientData) end
|
||||
|
||||
ACF.Activate(self, true)
|
||||
end
|
||||
|
||||
function ENT:Update(ClientData)
|
||||
VerifyClientData(ClientData)
|
||||
|
||||
hook.Run("ACF_OnEntityLast", Class, self)
|
||||
|
||||
ACF.SaveEntity(self)
|
||||
UpdateEntityData(self, ClientData)
|
||||
ACF.RestoreEntity(self)
|
||||
|
||||
hook.Run("ACF_OnEntityUpdate", Class, self, ClientData)
|
||||
if self.UpdateOverlay then self:UpdateOverlay(true) end
|
||||
net.Start("ACF_UpdateEntity")
|
||||
net.WriteEntity(self)
|
||||
net.Broadcast()
|
||||
|
||||
return true, (self.PrintName or Class) .. " updated successfully!"
|
||||
end
|
||||
|
||||
local ACF_Limit = ENT.ACF_Limit
|
||||
function Entity.Spawn(Player, Pos, Angle, ClientData)
|
||||
if ACF_Limit then
|
||||
if isfunction(ACF_Limit) then
|
||||
if not ACF_Limit() then return end
|
||||
elseif isnumber(ACF_Limit) then
|
||||
if not Player:CheckLimit("_" .. Class) then return false end
|
||||
end
|
||||
end
|
||||
|
||||
local CanSpawn = hook.Run("ACF_PreEntitySpawn", Class, Player, ClientData)
|
||||
if CanSpawn == false then return false end
|
||||
|
||||
local New = ents.Create(Class)
|
||||
if not IsValid(New) then return end
|
||||
|
||||
VerifyClientData(ClientData)
|
||||
|
||||
New:SetPos(Pos)
|
||||
New:SetAngles(Angle)
|
||||
if New.ACF_PreSpawn then
|
||||
New:ACF_PreSpawn(Player, Pos, Angle, ClientData)
|
||||
end
|
||||
|
||||
New:SetPlayer(Player)
|
||||
New:Spawn()
|
||||
Player:AddCount("_" .. Class, New)
|
||||
Player:AddCleanup("_" .. Class, New)
|
||||
New.Owner = Player -- MUST be stored on ent for PP
|
||||
New.DataStore = Entities.GetArguments(Class)
|
||||
|
||||
hook.Run("ACF_OnEntitySpawn", Class, New, ClientData)
|
||||
|
||||
if New.ACF_PostSpawn then
|
||||
New:ACF_PostSpawn(Player, Pos, Angle, ClientData)
|
||||
end
|
||||
|
||||
New:ACF_UpdateEntityData(ClientData)
|
||||
if New.UpdateOverlay then New:UpdateOverlay(true) end
|
||||
ACF.CheckLegal(New)
|
||||
|
||||
return New
|
||||
end
|
||||
|
||||
ENT.ACF_VerifyClientData = VerifyClientData
|
||||
ENT.ACF_UpdateEntityData = UpdateEntityData
|
||||
|
||||
duplicator.RegisterEntityClass(Class, Entity.Spawn, "Pos", "Angle", "Data", unpack(ArgsList))
|
||||
end
|
||||
|
||||
--- Registers a class as a spawnable entity class
|
||||
--- @param Class string The class to register
|
||||
--- @param Function fun(Player:entity, Pos:vector, Ang:angle, Data:table):Entity A function defining how to spawn your class (This should be your MakeACF_<something> function)
|
||||
--- @param ... any #A vararg of arguments to attach to the entity
|
||||
function Entities.Register(Class, Function, ...)
|
||||
if Class == nil and Function == nil then
|
||||
-- Calling Entities.Register with no arguments performs an automatic registration
|
||||
Entities.AutoRegister(ENT)
|
||||
return
|
||||
end
|
||||
|
||||
if not isstring(Class) then return end
|
||||
if not isfunction(Function) then return end
|
||||
|
||||
|
71
lua/acf/menu/items_cl/baseplates.lua
Normal file
71
lua/acf/menu/items_cl/baseplates.lua
Normal file
@ -0,0 +1,71 @@
|
||||
local ACF = ACF
|
||||
|
||||
local gridMaterial = CreateMaterial("acf_bp_vis_grid1", "VertexLitGeneric", {
|
||||
["$basetexture"] = "hunter/myplastic",
|
||||
["$model"] = 1,
|
||||
["$translucent"] = 1,
|
||||
["$vertexalpha"] = 1,
|
||||
["$vertexcolor"] = 1
|
||||
})
|
||||
|
||||
local function CreateMenu(Menu)
|
||||
ACF.SetToolMode("acf_menu", "Spawner", "Baseplate")
|
||||
ACF.SetClientData("PrimaryClass", "acf_baseplate")
|
||||
ACF.SetClientData("SecondaryClass", "N/A")
|
||||
|
||||
Menu:AddTitle("Baseplate Settings")
|
||||
|
||||
Menu:AddLabel("The root entity of all ACF contraptions.")
|
||||
local BaseplateBase = Menu:AddCollapsible("Baseplate Information")
|
||||
local SizeX = BaseplateBase:AddSlider("Plate Width (gmu)", 36, 96, 2)
|
||||
local SizeY = BaseplateBase:AddSlider("Plate Length (gmu)", 36, 420, 2)
|
||||
local SizeZ = BaseplateBase:AddSlider("Plate Thickness (gmu)", 0.5, 3, 2)
|
||||
|
||||
Menu:AddLabel("Comparing the current dimensions with a 105mm Howitzer:")
|
||||
local Vis = Menu:AddModelPreview("models/howitzer/howitzer_105mm.mdl", true)
|
||||
Vis:SetSize(30, 300)
|
||||
function Vis:PreDrawModel(_)
|
||||
local w, h, t = SizeX:GetValue(), SizeY:GetValue(), SizeZ:GetValue()
|
||||
self.CamDistance = math.max(w, h, 60) * 1
|
||||
|
||||
render.SetMaterial(gridMaterial)
|
||||
render.DrawBox(vector_origin, angle_zero, Vector(-h / 2, -w / 2, -t / 2), Vector(h / 2, w / 2, t / 2), color_white)
|
||||
end
|
||||
|
||||
SizeX:SetClientData("Width", "OnValueChanged")
|
||||
SizeX:DefineSetter(function(Panel, _, _, Value)
|
||||
local X = math.Round(Value, 2)
|
||||
|
||||
Panel:SetValue(X)
|
||||
|
||||
return X
|
||||
end)
|
||||
|
||||
SizeY:SetClientData("Length", "OnValueChanged")
|
||||
SizeY:DefineSetter(function(Panel, _, _, Value)
|
||||
local Y = math.Round(Value, 2)
|
||||
|
||||
Panel:SetValue(Y)
|
||||
|
||||
return Y
|
||||
end)
|
||||
|
||||
SizeZ:SetClientData("Thickness", "OnValueChanged")
|
||||
SizeZ:DefineSetter(function(Panel, _, _, Value)
|
||||
local Z = math.Round(Value, 2)
|
||||
|
||||
Panel:SetValue(Z)
|
||||
|
||||
return Z
|
||||
end)
|
||||
|
||||
Menu:AddLabel("You can right click on an entity to replace an existing entity with an ACF Baseplate. " ..
|
||||
"This will, to the best of its abilities (given you're using a cubical prop, with the long side facing forwards, ex. a SProps plate), replace the entity you're looking at with " ..
|
||||
"a new ACF baseplate.\n\nIt works by taking an Advanced Duplicator 2 copy of the entire contraption from the target entity, replacing the target entity " ..
|
||||
"in the dupe's class to acf_baseplate, setting the size based off the physical size of the target entity, then removing all entities and re-pasting the dupe. " ..
|
||||
"\n\nYou will need to manually re-copy the contraption with the Adv. Dupe 2 tool before using it again, but after that, everything should be converted. This is " ..
|
||||
"an experimental tool, so if something breaks with an ordinary setup, report it at https://github.com/ACF-Team/ACF-3/issues."
|
||||
)
|
||||
end
|
||||
|
||||
ACF.AddMenuItem(0, "Entities", "Baseplates", "shape_square", CreateMenu)
|
@ -4,7 +4,7 @@ local Turrets = ACF.Classes.Turrets
|
||||
local function CreateMenu(Menu)
|
||||
local Entries = Turrets.GetEntries()
|
||||
|
||||
ACF.SetToolMode("acf_menu", "Spawner", "Component")
|
||||
ACF.SetToolMode("acf_menu", "Spawner", "Turret")
|
||||
|
||||
ACF.SetClientData("PrimaryClass", "N/A")
|
||||
ACF.SetClientData("SecondaryClass", "N/A")
|
||||
@ -29,6 +29,7 @@ local function CreateMenu(Menu)
|
||||
|
||||
ClassDesc:SetText(Data.Description or "No description provided.")
|
||||
|
||||
ACF.SetToolMode("acf_menu", "Spawner", Data.ID)
|
||||
ACF.LoadSortedList(ComponentClass, Data.Items, "Name")
|
||||
end
|
||||
|
||||
|
@ -219,7 +219,8 @@ do -- Generic Spawner/Linker operation creator
|
||||
--- @param Name string The name of the link type performed by the toolgun (e.g. Weapon, Engine, etc.)
|
||||
--- @param Primary string The type of the entity to be spawned on left click (purely aesthetical)
|
||||
--- @param Secondary string | nil The type of entity to be spawned on shift + right click (purely aesthetical)
|
||||
function ACF.CreateMenuOperation(Name, Primary, Secondary)
|
||||
--- @param OnRightClick table | nil If provided, a table with a Text and Func parameter for when right clicking
|
||||
function ACF.CreateMenuOperation(Name, Primary, Secondary, OnRightClick)
|
||||
if not isstring(Name) then return end
|
||||
if not isstring(Primary) then return end
|
||||
|
||||
@ -229,12 +230,12 @@ do -- Generic Spawner/Linker operation creator
|
||||
-- These basically setup the tool information display you see on the top left of your screen
|
||||
ACF.RegisterOperation("acf_menu", "Spawner", Name, {
|
||||
OnLeftClick = SpawnEntity,
|
||||
OnRightClick = function(Tool, Trace)
|
||||
OnRightClick = OnRightClick and OnRightClick.Func or function(Tool, Trace)
|
||||
local Entity = Trace.Entity
|
||||
|
||||
-- The call to SelectEntity will switch the mode to the linker
|
||||
return SelectEntity(Entity, Name, Tool)
|
||||
end,
|
||||
end
|
||||
})
|
||||
|
||||
ACF.RegisterToolInfo("acf_menu", "Spawner", Name, {
|
||||
@ -252,7 +253,7 @@ do -- Generic Spawner/Linker operation creator
|
||||
|
||||
ACF.RegisterToolInfo("acf_menu", "Spawner", Name, {
|
||||
name = "right",
|
||||
text = "Select the entity you want to link or unlink.",
|
||||
text = OnRightClick and OnRightClick.Text or "Select the entity you want to link or unlink."
|
||||
})
|
||||
end
|
||||
|
||||
@ -326,4 +327,20 @@ ACF.CreateMenuOperation("Missile", "rack", "ammo crate")
|
||||
ACF.CreateMenuOperation("Engine", "engine", "fuel tank")
|
||||
ACF.CreateMenuOperation("Component", "component")
|
||||
ACF.CreateMenuOperation("Gearbox", "gearbox")
|
||||
ACF.CreateMenuOperation("Sensor", "sensor")
|
||||
ACF.CreateMenuOperation("Sensor", "sensor")
|
||||
|
||||
ACF.CreateMenuOperation("1-Turret", "turret")
|
||||
ACF.CreateMenuOperation("2-Motor", "turret motor")
|
||||
ACF.CreateMenuOperation("3-Gyro", "turret gyroscope")
|
||||
ACF.CreateMenuOperation("4-Computer", "turret computer")
|
||||
|
||||
ACF.CreateMenuOperation("Baseplate", "baseplate", nil, {
|
||||
Text = "Attempts to convert the target entity into a baseplate.",
|
||||
Func = function(Tool, Trace)
|
||||
if CLIENT then return end
|
||||
local success, msg = ACF.ConvertEntityToBaseplate(Tool.SWEP:GetOwner(), Trace.Entity)
|
||||
if not success then
|
||||
ACF.SendNotify(Tool:GetOwner(), err, "[ACF] Could not convert baseplate: " .. msg)
|
||||
end
|
||||
end
|
||||
})
|
@ -170,6 +170,8 @@ DefineScannerType("primitive_airfoil", "Primitive Airfoil", Color(
|
||||
drawMesh = true
|
||||
})
|
||||
|
||||
DefineScannerType("acf_baseplate", "ACF Baseplate", Color(255, 65, 160), "ABP", {drawBounds = true, drawMarker = true})
|
||||
|
||||
local function NetStart(n)
|
||||
net_Start("ACF_Scanning_NetworkPacket")
|
||||
net_WriteString(n)
|
||||
@ -1344,9 +1346,11 @@ if CLIENT then
|
||||
end
|
||||
|
||||
for _, ent in ipairs(baseplates) do
|
||||
drawEntityNoOutline(ent, baseplateC.colorEntityInside)
|
||||
drawPhysMesh(ent, baseplateC.color)
|
||||
drawBounds(ent, baseplateC)
|
||||
if ent:GetClass() ~= "acf_baseplate" then
|
||||
drawEntityNoOutline(ent, baseplateC.colorEntityInside)
|
||||
drawPhysMesh(ent, baseplateC.color)
|
||||
drawBounds(ent, baseplateC)
|
||||
end
|
||||
VisualizeClips(ent)
|
||||
end
|
||||
render.DepthRange(0, 1)
|
||||
@ -1529,7 +1533,7 @@ if CLIENT then
|
||||
end
|
||||
|
||||
for _, ent in ipairs(baseplates) do
|
||||
if IsValid(ent) then
|
||||
if IsValid(ent) and ent:GetClass() ~= "acf_baseplate" then
|
||||
local pXY = ent:GetPos():ToScreen()
|
||||
local pX, pY = pXY.x, pXY.y
|
||||
DrawMarker(baseplateC, pX, pY, ent)
|
||||
|
52
lua/entities/acf_baseplate/cl_init.lua
Normal file
52
lua/entities/acf_baseplate/cl_init.lua
Normal file
@ -0,0 +1,52 @@
|
||||
include("shared.lua")
|
||||
|
||||
function ENT:Update() end
|
||||
|
||||
local HideInfo = ACF.HideInfoBubble
|
||||
|
||||
local ColorBlack = Color(0, 0, 0)
|
||||
local ColorRed = Color(255, 50, 40)
|
||||
local ColorGreen = Color(40, 255, 50)
|
||||
|
||||
function ENT:DrawGizmos()
|
||||
cam.IgnoreZ(true)
|
||||
|
||||
local Pos = self:GetPos()
|
||||
local Size = self.Size
|
||||
|
||||
render.SetColorMaterial()
|
||||
render.DrawBeam(Pos, self:LocalToWorld(Vector(Size.x / 2, 0, 0)), 2, 0, 1, ColorBlack)
|
||||
render.DrawBeam(Pos, self:LocalToWorld(Vector(Size.x / 2, 0, 0)), 1, 0, 1, ColorRed)
|
||||
render.DrawBeam(Pos, self:LocalToWorld(Vector(0, -Size.y / 2, 0)), 2, 0, 1, ColorBlack)
|
||||
render.DrawBeam(Pos, self:LocalToWorld(Vector(0, -Size.y / 2, 0)), 1, 0, 1, ColorGreen)
|
||||
|
||||
cam.IgnoreZ(false)
|
||||
end
|
||||
|
||||
function ENT:Draw()
|
||||
-- Partial from base_wire_entity, need the tooltip but without the model drawing since we're drawing our own
|
||||
local LocalPlayer = LocalPlayer()
|
||||
local Weapon = LocalPlayer:GetActiveWeapon()
|
||||
local LookedAt = self:BeingLookedAtByLocalPlayer()
|
||||
|
||||
if LookedAt then
|
||||
self:DrawEntityOutline()
|
||||
end
|
||||
|
||||
self:DrawModel()
|
||||
|
||||
if not LookedAt then return end
|
||||
if HideInfo() then return end
|
||||
|
||||
self:AddWorldTip()
|
||||
|
||||
if LocalPlayer:InVehicle() then return end
|
||||
if not IsValid(Weapon) then return end
|
||||
|
||||
local class = Weapon:GetClass()
|
||||
if class ~= "weapon_physgun" and (class ~= "gmod_tool" or Weapon.current_mode ~= "acf_menu") then return end
|
||||
|
||||
self:DrawGizmos()
|
||||
end
|
||||
|
||||
ACF.Classes.Entities.Register()
|
39
lua/entities/acf_baseplate/init.lua
Normal file
39
lua/entities/acf_baseplate/init.lua
Normal file
@ -0,0 +1,39 @@
|
||||
AddCSLuaFile("shared.lua")
|
||||
AddCSLuaFile("cl_init.lua")
|
||||
include("shared.lua")
|
||||
|
||||
local ACF = ACF
|
||||
local Classes = ACF.Classes
|
||||
local Entities = Classes.Entities
|
||||
|
||||
ENT.ACF_Limit = 16
|
||||
ENT.ACF_UserWeighable = true
|
||||
|
||||
function ENT.ACF_OnVerifyClientData(ClientData)
|
||||
ClientData.Size = Vector(ClientData.Length, ClientData.Width, ClientData.Thickness)
|
||||
end
|
||||
|
||||
function ENT:ACF_PostUpdateEntityData(ClientData)
|
||||
self:SetSize(ClientData.Size)
|
||||
end
|
||||
|
||||
function ENT:ACF_PreSpawn(_, _, _, _)
|
||||
self:SetScaledModel("models/holograms/cube.mdl")
|
||||
self:SetMaterial("hunter/myplastic")
|
||||
end
|
||||
|
||||
function ENT:ACF_PostSpawn(_, _, _, ClientData)
|
||||
local EntMods = ClientData.EntityMods
|
||||
if EntMods and EntMods.mass then
|
||||
ACF.Contraption.SetMass(self, self.ACF.Mass or 1)
|
||||
else
|
||||
ACF.Contraption.SetMass(self, 1000)
|
||||
end
|
||||
end
|
||||
|
||||
local Text = "Baseplate Size: %.1f x %.1f x %.1f"
|
||||
function ENT:UpdateOverlayText()
|
||||
return Text:format(self.Size[1], self.Size[2], self.Size[3])
|
||||
end
|
||||
|
||||
Entities.Register()
|
13
lua/entities/acf_baseplate/shared.lua
Normal file
13
lua/entities/acf_baseplate/shared.lua
Normal file
@ -0,0 +1,13 @@
|
||||
DEFINE_BASECLASS "acf_base_scalable"
|
||||
|
||||
ENT.PrintName = "ACF Baseplate"
|
||||
ENT.WireDebugName = "ACF Baseplate"
|
||||
ENT.PluralName = "ACF Baseplates"
|
||||
ENT.IsACFEntity = true
|
||||
ENT.IsACFBaseplate = true
|
||||
|
||||
ENT.ACF_DataKeys = {
|
||||
["Width"] = {Type = "Number", Min = 36, Max = 96, Default = 36, Decimals = 2},
|
||||
["Length"] = {Type = "Number", Min = 36, Max = 480, Default = 36, Decimals = 2},
|
||||
["Thickness"] = {Type = "Number", Min = 0.5, Max = 3, Default = 3, Decimals = 2}
|
||||
}
|
Loading…
Reference in New Issue
Block a user