Files
wnsrc/lua/pac3/editor/client/panels/extra_properties.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

768 lines
18 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local L = pace.LanguageString
local function populate_part_menu(menu, part, func)
if part:HasChildren() then
local menu, pnl = menu:AddSubMenu(pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", part:GetName(), part:GetPrintUniqueID()) or part:GetName(), function()
func(part)
end)
pnl:SetImage(part.Icon)
for key, part in ipairs(part:GetChildren()) do
populate_part_menu(menu, part, func)
end
else
menu:AddOption(pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", part:GetName(), part:GetPrintUniqueID()) or part:GetName(), function()
func(part)
end):SetImage(part.Icon)
end
end
local function get_friendly_name(ent)
if not IsValid(ent) then return "NULL" end
local name = ent.GetName and ent:GetName()
if not name or name == "" then
name = ent:GetClass()
end
if ent:EntIndex() == -1 then
if name == "10C_BaseFlex" then
return "csentity - " .. ent:GetModel()
end
return name
end
if ent == pac.LocalPlayer then
return name
end
return ent:EntIndex() .. " - " .. name
end
do -- bone
local PANEL = {}
PANEL.ClassName = "properties_bone"
PANEL.Base = "pace_properties_base_type"
function PANEL:MoreOptionsLeftClick()
if not pace.current_part:IsValid() or not pace.current_part:GetParentOwner():IsValid() then return end
pace.SelectBone(pace.current_part:GetParentOwner(), function(data)
if not self:IsValid() then return end
self:SetValue(L(data.friendly))
self.OnValueChanged(data.friendly)
end, pace.current_part.ClassName == "bone" or pace.current_part.ClassName == "timeline_dummy_bone")
end
function PANEL:MoreOptionsRightClick()
local bones = pac.GetModelBones(pace.current_part:GetParentOwner())
local menu = DermaMenu()
menu:MakePopup()
local list = {}
for k,v in pairs(bones) do
table.insert(list, v.friendly)
end
pace.CreateSearchList(
self,
self.CurrentKey,
L"bones",
function(list)
list:AddColumn(L"name")
end,
function()
return list
end,
function()
return pace.current_part:GetBone()
end,
function(list, key, val)
return list:AddLine(val)
end
)
end
pace.RegisterPanel(PANEL)
end
do -- part
local PANEL = {}
PANEL.ClassName = "properties_part"
PANEL.Base = "pace_properties_base_type"
function PANEL:EncodeEdit(uid)
local part = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), uid)
if part:IsValid() then
return part:GetName()
end
return ""
end
function PANEL:DecodeEdit(name)
if name:Trim() ~= "" then
local part = pac.FindPartByName(pac.Hash(pac.LocalPlayer), name, pace.current_part)
if part:IsValid() then
return part:GetUniqueID()
end
end
return ""
end
function PANEL:OnValueSet(val)
if not IsValid(self.part) then return end
local part = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), val)
if IsValid(self.Icon) then self.Icon:Remove() end
if not part:IsValid() then
if self.CurrentKey == "TargetEntityUID" then
local owner = pace.current_part:GetOwner()
self:SetText(" " .. get_friendly_name(owner))
local pnl = vgui.Create("DImage", self)
pnl:SetImage(pace.GroupsIcons.entity)
self.Icon = pnl
end
return
end
if self.CurrentKey == "TargetEntityUID" then
if part.Owner:IsValid() then
local owner = part:GetOwner()
self:SetText(" " .. part:GetName())
else
local owner = part:GetOwner()
self:SetText(" " .. get_friendly_name(owner))
end
local pnl = vgui.Create("DImage", self)
pnl:SetImage(pace.GroupsIcons.entity)
self.Icon = pnl
return
end
self:SetText(" " .. (pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", part:GetName(), part:GetPrintUniqueID()) or part:GetName()))
if
GetConVar("pac_editor_model_icons"):GetBool() and
part.is_model_part and
part.GetModel and
part:GetOwner():IsValid() and
part.ClassName ~= "entity2" and
part.ClassName ~= "weapon" -- todo: is_model_part is true, class inheritance issues?
then
local pnl = vgui.Create("SpawnIcon", self)
pnl:SetModel(part:GetOwner():GetModel() or "")
self.Icon = pnl
elseif isstring(part.Icon) then
local pnl = vgui.Create("DImage", self)
pnl:SetImage(part.Icon)
self.Icon = pnl
end
end
function PANEL:PerformLayout()
if not IsValid(self.Icon) then return end
self:SetTextInset(11, 0)
self.Icon:SetPos(4,0)
surface.SetFont(pace.CurrentFont)
local w,h = surface.GetTextSize(".")
h = h / 1.5
self.Icon:SetSize(h, h)
self.Icon:CenterVertical()
end
function PANEL:MoreOptionsLeftClick()
pace.SelectPart(pac.GetLocalParts(), function(part)
if not self:IsValid() then return end
self:SetValue(part:GetUniqueID())
self.OnValueChanged(part)
end)
end
function PANEL:MoreOptionsRightClick(key)
local menu = DermaMenu()
menu:MakePopup()
for _, part in pairs(pac.GetLocalParts()) do
if not part:HasParent() and part:GetShowInEditor() then
populate_part_menu(menu, part, function(part)
if not self:IsValid() then return end
self:SetValue(part:GetUniqueID())
self.OnValueChanged(part)
end)
end
end
if key ~= "ParentUID" then
menu:AddOption("none", function()
self:SetValue("")
self.OnValueChanged("")
end):SetImage(pace.MiscIcons.clear)
end
pace.FixMenu(menu)
end
pace.RegisterPanel(PANEL)
end
do -- owner
local PANEL = {}
PANEL.ClassName = "properties_ownername"
PANEL.Base = "pace_properties_base_type"
function PANEL:MoreOptionsLeftClick()
pace.SelectEntity(function(ent)
if not self:IsValid() then return end
pace.current_part:SetOwnerName(ent:EntIndex())
local name = pace.current_part:GetOwnerName()
self.OnValueChanged(name)
self:SetValue(L(name))
end)
end
function PANEL:MoreOptionsRightClick()
local menu = DermaMenu()
menu:MakePopup()
for key, name in pairs(pac.OwnerNames) do
menu:AddOption(name, function() pace.current_part:SetOwnerName(name) self.OnValueChanged(name) end)
end
local entities = menu:AddSubMenu(L"entities", function() end)
entities.GetDeleteSelf = function() return false end
for _, ent in pairs(ents.GetAll()) do
if ent:EntIndex() > 0 then
entities:AddOption(get_friendly_name(ent), function()
pace.current_part:SetOwnerName(ent:EntIndex())
self.OnValueChanged(ent:EntIndex())
end)
end
end
pace.FixMenu(menu)
end
pace.RegisterPanel(PANEL)
end
do -- sequence list
local PANEL = {}
PANEL.ClassName = "properties_sequence"
PANEL.Base = "pace_properties_base_type"
function PANEL:MoreOptionsLeftClick()
pace.CreateSearchList(
self,
self.CurrentKey,
L"animations",
function(list)
list:AddColumn(L"id"):SetFixedWidth(25)
list:AddColumn(L"name")
end,
function()
return pace.current_part:GetSequenceList()
end,
function()
return pace.current_part.SequenceName or pace.current_part.GestureName
end,
function(list, key, val)
return list:AddLine(key, val)
end
)
end
pace.RegisterPanel(PANEL)
end
do -- model
local PANEL = {}
PANEL.ClassName = "properties_model"
PANEL.Base = "pace_properties_base_type"
function PANEL:MoreOptionsRightClick()
pace.SafeRemoveSpecialPanel()
g_SpawnMenu:Open()
end
function PANEL:MoreOptionsLeftClick(key)
pace.close_spawn_menu = true
pace.SafeRemoveSpecialPanel()
local part = pace.current_part
pace.AssetBrowser(function(path)
if not part:IsValid() then return end
-- because we refresh the properties
if IsValid(self) and self.OnValueChanged then
self.OnValueChanged(path)
end
if pace.current_part.SetMaterials then
local model = pace.current_part:GetModel()
local part = pace.current_part
if part.pace_last_model and part.pace_last_model ~= model then
part:SetMaterials("")
end
part.pace_last_model = model
end
pace.PopulateProperties(pace.current_part)
for k,v in ipairs(pace.properties.List) do
if v.panel and v.panel.part == part and v.key == key then
self = v.panel
break
end
end
end, "models")
pac.AddHook("Think", "pace_close_browser", function()
if part ~= pace.current_part then
pac.RemoveHook("Think", "pace_close_browser")
pace.model_browser:SetVisible(false)
end
end)
end
pace.RegisterPanel(PANEL)
end
do -- materials and textures
local PANEL_MATERIAL = {}
PANEL_MATERIAL.ClassName = "properties_material"
PANEL_MATERIAL.Base = "pace_properties_base_type"
function PANEL_MATERIAL:MoreOptionsLeftClick(key)
pace.AssetBrowser(function(path)
if not self:IsValid() then return end
path = path:match("materials/(.+)%.vmt") or "error"
self:SetValue(path)
self.OnValueChanged(path)
end, "materials", key)
end
function PANEL_MATERIAL:MoreOptionsRightClick()
pace.SafeRemoveSpecialPanel()
local pnl = pace.CreatePanel("mat_browser")
pace.ShowSpecial(pnl, self, 300)
function pnl.MaterialSelected(_, path)
self:SetValue(path)
self.OnValueChanged(path)
end
pace.ActiveSpecialPanel = pnl
end
local PANEL = {}
local pace_material_display
PANEL.ClassName = "properties_textures"
PANEL.Base = "pace_properties_base_type"
function PANEL:MoreOptionsLeftClick()
pace.AssetBrowser(function(path)
if not self:IsValid() then return end
path = path:match("materials/(.+)%.vtf") or "error"
self:SetValue(path)
self.OnValueChanged(path)
end, "textures")
end
function PANEL:MoreOptionsRightClick()
pace.SafeRemoveSpecialPanel()
local pnl = pace.CreatePanel("mat_browser")
pace.ShowSpecial(pnl, self, 300)
function pnl.MaterialSelected(_, path)
self:SetValue(path)
self.OnValueChanged(path)
end
pace.ActiveSpecialPanel = pnl
end
function PANEL:HUDPaint()
if IsValid(self.editing) then return self:MustHideTexture() end
-- Near Button?
-- local w, h = self:GetSize()
-- local x, y = self:LocalToScreen(w, 0)
-- Near cursor
local W, H = ScrW(), ScrH()
local x, y = input.GetCursorPos()
local w, h = 256, 256
x = x + 12
y = y + 4
if x + w > W then
x = x - w - 24
end
if y + h > H then
y = y - h - 8
end
surface.SetDrawColor(255, 255, 255, 255)
surface.SetAlphaMultiplier(1)
surface.SetMaterial(pace_material_display)
surface.DrawTexturedRect(x, y, w, h)
end
PANEL_MATERIAL.HUDPaint = PANEL.HUDPaint
function PANEL:MustShowTexture()
if self.isShownTexture then return end
if not pace_material_display then
pace_material_display = CreateMaterial('pace_material_display', "UnlitGeneric", {})
end
if pace.current_part[self.CurrentKey] then
if pace.current_part[self.CurrentKey] == "" then
pace_material_display:SetTexture("$basetexture", "models/debug/debugwhite")
elseif not string.find(pace.current_part[self.CurrentKey], '^https?://') then
pace_material_display:SetTexture("$basetexture", pace.current_part[self.CurrentKey])
else
local function callback(mat, tex)
if not tex then return end
pace_material_display:SetTexture("$basetexture", tex)
end
pac.urltex.GetMaterialFromURL(pace.current_part[self.CurrentKey], callback, false, 'UnlitGeneric')
end
end
local id = tostring(self)
pac.AddHook("PostRenderVGUI", id, function()
if self:IsValid() then
self:HUDPaint()
else
pac.RemoveHook("PostRenderVGUI", id)
end
end)
self.isShownTexture = true
end
PANEL_MATERIAL.MustShowTexture = PANEL.MustShowTexture
function PANEL:MustHideTexture()
if not self.isShownTexture then return end
self.isShownTexture = false
pac.RemoveHook('PostRenderVGUI', tostring(self))
end
PANEL_MATERIAL.MustHideTexture = PANEL.MustHideTexture
function PANEL:ThinkTextureDisplay()
if self.preTextureThink then self:preTextureThink() end
if not IsValid(self.textureButton) or IsValid(self.editing) then return end
local rTime = RealTime()
self.lastHovered = self.lastHovered or rTime
if not self.textureButton:IsHovered() and not self:IsHovered() then
self.lastHovered = rTime
end
if self.lastHovered + 0.5 < rTime then
self:MustShowTexture()
else
self:MustHideTexture()
end
end
PANEL_MATERIAL.ThinkTextureDisplay = PANEL.ThinkTextureDisplay
function PANEL:OnMoreOptionsLeftClickButton(btn)
self.preTextureThink = self.Think
self.Think = self.ThinkTextureDisplay
self.textureButton = btn
end
PANEL_MATERIAL.OnMoreOptionsLeftClickButton = PANEL.OnMoreOptionsLeftClickButton
pace.RegisterPanel(PANEL)
pace.RegisterPanel(PANEL_MATERIAL)
end
do -- sound
local PANEL = {}
PANEL.ClassName = "properties_sound"
PANEL.Base = "pace_properties_base_type"
function PANEL:MoreOptionsLeftClick()
pace.AssetBrowser(function(path)
if not self:IsValid() then return end
self:SetValue(path)
self.OnValueChanged(path)
if pace.current_part:IsValid() then
pace.current_part:OnShow()
end
end, "sound")
end
pace.RegisterPanel(PANEL)
end
do -- script
local PANEL = {}
PANEL.ClassName = "properties_code"
PANEL.Base = "pace_properties_base_type"
function PANEL:MoreOptionsLeftClick()
pace.SafeRemoveSpecialPanel()
local frame = vgui.Create("DFrame")
frame:SetTitle(L"script")
pace.ShowSpecial(frame, self, 512)
frame:SetSizable(true)
local editor = vgui.Create("pace_luapad", frame)
editor:Dock(FILL)
editor:SetText(pace.current_part:GetCode())
editor.OnTextChanged = function(self)
pace.current_part:SetCode(self:GetValue())
end
editor.last_error = ""
function editor:CheckGlobal(str)
local part = pace.current_part
if not part:IsValid() then frame:Remove() return end
return part:ShouldHighlight(str)
end
function editor:Think()
local part = pace.current_part
if not part:IsValid() then frame:Remove() return end
local title = L"script editor"
if part.Error then
title = part.Error
local line = tonumber(title:match("SCRIPT_ENV:(%d-):"))
if line then
title = title:match("SCRIPT_ENV:(.+)")
if self.last_error ~= title then
editor:SetScrollPosition(line)
editor:SetErrorLine(line)
self.last_error = title
end
end
else
editor:SetErrorLine(nil)
if part.script_printing then
title = part.script_printing
part.script_printing = nil
end
end
frame:SetTitle(title)
end
pace.ActiveSpecialPanel = frame
end
pace.RegisterPanel(PANEL)
end
do -- hull
local PANEL = {}
PANEL.ClassName = "properties_hull"
PANEL.Base = "pace_properties_number"
function PANEL:OnValueSet()
local function stop()
RunConsoleCommand("-duck")
hook.Remove("PostDrawOpaqueRenderables", "pace_draw_hull")
end
local time = os.clock() + 3
hook.Add("PostDrawOpaqueRenderables", "pace_draw_hull", function()
if not pace.current_part:IsValid() then stop() return end
if pace.current_part.ClassName ~= "entity2" then stop() return end
local ent = pace.current_part:GetOwner()
if not ent.GetHull then stop() return end
if not ent.GetHullDuck then stop() return end
local min, max = ent:GetHull()
if self.udata and self.udata.crouch then
min, max = ent:GetHullDuck()
RunConsoleCommand("+duck")
end
min = min * ent:GetModelScale()
max = max * ent:GetModelScale()
render.DrawWireframeBox( ent:GetPos(), Angle(0), min, max, Color(255, 204, 51, 255), true )
if time < os.clock() then
stop()
end
end)
end
pace.RegisterPanel(PANEL)
end
do -- event ranger
local PANEL = {}
PANEL.ClassName = "properties_ranger"
PANEL.Base = "pace_properties_number"
function PANEL:OnValueSet()
local function stop()
hook.Remove("PostDrawOpaqueRenderables", "pace_draw_ranger")
end
local last_part = pace.current_part
hook.Add("PostDrawOpaqueRenderables", "pace_draw_ranger", function()
local part = pace.current_part
if not part:IsValid() then stop() return end
if part ~= last_part then stop() return end
if part.ClassName ~= "event" then stop() return end
if part:GetEvent() ~= "ranger" then stop() return end
local distance = part:GetProperty("distance")
local compare = part:GetProperty("compare")
local trigger = part.event_triggered
local parent = part:GetParent()
if not parent:IsValid() or not parent.GetWorldPosition then stop() return end
local startpos = parent:GetWorldPosition()
local endpos
local color
if self.udata then
if self.udata.ranger_property == "distance" then
endpos = startpos + parent:GetWorldAngles():Forward() * distance
color = Color(255,255,255)
elseif self.udata.ranger_property == "compare" then
endpos = startpos + parent:GetWorldAngles():Forward() * compare
color = Color(10,255,10)
end
render.DrawLine( startpos, endpos, trigger and Color(255,0,0) or color)
end
end)
end
pace.RegisterPanel(PANEL)
end
do -- event is_touching
local PANEL = {}
PANEL.ClassName = "properties_is_touching"
PANEL.Base = "pace_properties_number"
function PANEL:OnValueSet()
local function stop()
hook.Remove("PostDrawOpaqueRenderables", "pace_draw_is_touching")
end
local last_part = pace.current_part
hook.Add("PostDrawOpaqueRenderables", "pace_draw_is_touching", function()
local part = pace.current_part
if part ~= last_part then stop() return end
if not part:IsValid() then stop() return end
if part.ClassName ~= "event" then stop() return end
if part:GetEvent() ~= "is_touching" then stop() return end
local extra_radius = part:GetProperty("extra_radius") or 0
local ent
if part.RootOwner then
ent = part:GetRootPart():GetOwner()
else
ent = part:GetOwner()
end
if not IsValid(ent) then stop() return end
local radius = ent:BoundingRadius()
if radius == 0 and IsValid(ent.pac_projectile) then
radius = ent.pac_projectile:GetRadius()
end
radius = math.max(radius + extra_radius + 1, 1)
local mins = Vector(-1,-1,-1)
local maxs = Vector(1,1,1)
local startpos = ent:WorldSpaceCenter()
mins = mins * radius
maxs = maxs * radius
local tr = util.TraceHull( {
start = startpos,
endpos = startpos,
maxs = maxs,
mins = mins,
filter = ent
} )
if self.udata then
render.DrawWireframeBox( startpos, Angle( 0, 0, 0 ), mins, maxs, tr.Hit and Color(255,0,0) or Color(255,255,255), true )
end
end)
end
pace.RegisterPanel(PANEL)
end