mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
578
lua/pac3/editor/client/about.lua
Normal file
578
lua/pac3/editor/client/about.lua
Normal file
@@ -0,0 +1,578 @@
|
||||
--[[
|
||||
| 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 DEMO = {}
|
||||
|
||||
DEMO.Title = "Sand"
|
||||
DEMO.Author = "Capsadmin"
|
||||
|
||||
local lines =
|
||||
{
|
||||
--surface.GetTextureID("sprites/laser"),
|
||||
--surface.GetTextureID("sprites/bluelaser"),
|
||||
surface.GetTextureID("effects/laser1"),
|
||||
surface.GetTextureID("trails/laser"),
|
||||
}
|
||||
|
||||
local sprites =
|
||||
{
|
||||
surface.GetTextureID("particle/fire"),
|
||||
}
|
||||
|
||||
local white = surface.GetTextureID("vgui/white")
|
||||
|
||||
function DEMO:DrawLineEx(x1,y1, x2,y2, w, skip_tex)
|
||||
w = w or 1
|
||||
if not skip_tex then surface.SetTexture(white) end
|
||||
|
||||
local dx,dy = x1-x2, y1-y2
|
||||
local ang = math.atan2(dx, dy)
|
||||
local dst = math.sqrt((dx * dx) + (dy * dy))
|
||||
|
||||
x1 = x1 - dx * 0.5
|
||||
y1 = y1 - dy * 0.5
|
||||
|
||||
surface.DrawTexturedRectRotated(x1, y1, w, dst, math.deg(ang))
|
||||
end
|
||||
|
||||
do
|
||||
local fonts = {}
|
||||
|
||||
local function create_fonts(font, size, weight, blursize)
|
||||
local main = "pretty_text_" .. size .. weight
|
||||
local blur = "pretty_text_blur_" .. size .. weight
|
||||
|
||||
surface.CreateFont(
|
||||
main,
|
||||
{
|
||||
font = font,
|
||||
size = size,
|
||||
weight = weight,
|
||||
antialias = true,
|
||||
additive = true,
|
||||
}
|
||||
)
|
||||
|
||||
surface.CreateFont(
|
||||
blur,
|
||||
{
|
||||
font = font,
|
||||
size = size,
|
||||
weight = weight,
|
||||
antialias = true,
|
||||
blursize = blursize,
|
||||
}
|
||||
)
|
||||
|
||||
return
|
||||
{
|
||||
main = main,
|
||||
blur = blur,
|
||||
}
|
||||
end
|
||||
|
||||
local def_color1 = Color(255, 255, 255, 255)
|
||||
local def_color2 = Color(0, 0, 0, 255)
|
||||
|
||||
local surface_SetFont = surface.SetFont
|
||||
local surface_SetTextColor = surface.SetTextColor
|
||||
local surface_SetTextPos = surface.SetTextPos
|
||||
local surface_DrawText = surface.DrawText
|
||||
local surface_GetTextSize = surface.GetTextSize
|
||||
|
||||
function DEMO:DrawPrettyText(text, x, y, font, size, weight, blursize, color1, color2, align_mult_x, align_mult_y)
|
||||
align_mult_x = align_mult_x or 0
|
||||
align_mult_y = align_mult_y or 0
|
||||
font = font or "Arial"
|
||||
size = size or 14
|
||||
weight = weight or 0
|
||||
blursize = blursize or 1
|
||||
color1 = color1 or def_color1
|
||||
color2 = color2 or def_color2
|
||||
|
||||
fonts[font] = fonts[font] or {}
|
||||
fonts[font][size] = fonts[font][size] or {}
|
||||
fonts[font][size][weight] = fonts[font][size][weight] or {}
|
||||
fonts[font][size][weight][blursize] = fonts[font][size][weight][blursize] or create_fonts(font, size, weight, blursize)
|
||||
|
||||
surface_SetFont(fonts[font][size][weight][blursize].blur)
|
||||
local w, h = surface_GetTextSize(text)
|
||||
surface_SetTextColor(color2)
|
||||
|
||||
align_mult_x = (w * align_mult_x)
|
||||
align_mult_y = (h * align_mult_y)
|
||||
|
||||
for i = 1, 5 do
|
||||
surface_SetTextPos(x - align_mult_x, y - align_mult_y) -- this resets for some reason after drawing
|
||||
surface_DrawText(text)
|
||||
end
|
||||
|
||||
surface_SetFont(fonts[font][size][weight][blursize].main)
|
||||
surface_SetTextColor(color1)
|
||||
|
||||
surface_SetTextPos(x - align_mult_x, y - align_mult_y)
|
||||
surface_DrawText(text)
|
||||
|
||||
return w, h
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function DEMO:OnStart(w, h)
|
||||
|
||||
input.SetCursorPos(w/2, h/2)
|
||||
|
||||
surface.SetDrawColor(0,0,0,255)
|
||||
surface.DrawRect(0,0,w,h)
|
||||
|
||||
self.first = true
|
||||
self.cam_pos = Vector(0, 0, 0)
|
||||
self.spos = Vector(w, h) / 2
|
||||
|
||||
self.max_size = 16
|
||||
|
||||
self.max_particles = 2000
|
||||
self.particles = {}
|
||||
self.base_color = math.random(360)
|
||||
end
|
||||
|
||||
function DEMO:CreateParticle(x, y, vx, vy, life, on_death)
|
||||
life = life or math.Rand(0.25, 2)
|
||||
|
||||
|
||||
local siz = math.Rand(0.5,self.max_size)
|
||||
table.insert(
|
||||
self.particles,
|
||||
{
|
||||
pos = {x = x, y = y},
|
||||
vel = {x = vx, y = vy},
|
||||
siz = siz,
|
||||
clr = HSVToColor(math.Rand(0, 60) + self.base_color, 1, 1),
|
||||
drag = 0.99 - (siz/150) ^ 3,
|
||||
tex_id1 = table.Random(lines),
|
||||
tex_id2 = table.Random(sprites),
|
||||
on_death = on_death,
|
||||
life = self.time + life,
|
||||
random = math.Rand(-1,1),
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function DEMO:PreUpdate(w, h, t, d)
|
||||
if input.IsKeyDown(KEY_W) then
|
||||
self.cam_pos.y = self.cam_pos.y + d
|
||||
elseif input.IsKeyDown(KEY_S) then
|
||||
self.cam_pos.y = self.cam_pos.y - d
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_A)then
|
||||
self.cam_pos.x = self.cam_pos.x - d
|
||||
elseif input.IsKeyDown(KEY_D) then
|
||||
self.cam_pos.x = self.cam_pos.x + d
|
||||
end
|
||||
|
||||
local mat = Matrix()
|
||||
|
||||
mat:Translate(Vector(w/2,h/2,0))
|
||||
|
||||
mat:Translate(Vector(self.cam_pos.x * 100, 0, 0))
|
||||
mat:Scale(Vector(1, 1, 1) * math.min(t ^ 4, 1))
|
||||
mat:Rotate(Angle(0, 0, 0))
|
||||
|
||||
mat:Translate(-Vector(w/2,h/2,0))
|
||||
|
||||
return mat
|
||||
end
|
||||
|
||||
local ext_vel_x = 0
|
||||
local ext_vel_y = 0
|
||||
local ext_vel_z = 0
|
||||
|
||||
local blur = Material("pp/blurscreen")
|
||||
|
||||
local function blur_screen(w, h, x, y)
|
||||
surface.SetMaterial(blur)
|
||||
surface.SetDrawColor(255, 50, 50, 2)
|
||||
|
||||
for i = 0, 10 do
|
||||
blur:SetFloat("$blur", i / 10)
|
||||
blur:Recompute()
|
||||
render.UpdateScreenEffectTexture()
|
||||
surface.DrawTexturedRect(x * math.random(-16, 16), y * math.random(-16, 16), w, h)
|
||||
end
|
||||
end
|
||||
|
||||
surface.CreateFont("pace_about_1", {font = "Roboto Bold", size = 512, weight = 800, additive = false, antialias = true})
|
||||
|
||||
local credits = {}
|
||||
local A = function(str, size, ...) table.insert(credits, {str, size or isstring(str) and 1 or nil, ...}) end
|
||||
|
||||
local cast = {
|
||||
"morshmellow",
|
||||
"immortalyes",
|
||||
"kilroy",
|
||||
"white queen",
|
||||
"dizrahk",
|
||||
"kerahk",
|
||||
"Nomad'Zorah vas Source",
|
||||
"Verbal Silence",
|
||||
"Madness",
|
||||
"Techbot",
|
||||
"Elmo",
|
||||
"Arctic",
|
||||
"krionikal",
|
||||
"Gm Matsilagi",
|
||||
"Daft Lad",
|
||||
"GigiSpahz",
|
||||
"Black Tea",
|
||||
"RocketMania",
|
||||
"ssogal",
|
||||
"Expresso",
|
||||
"Ryokon!",
|
||||
"Zeriga",
|
||||
"Aeo",
|
||||
"techbot",
|
||||
"midori",
|
||||
"sauer",
|
||||
"LilTrenya",
|
||||
"maarc",
|
||||
"dekota",
|
||||
"liltrenya",
|
||||
"nanori",
|
||||
"svetlana",
|
||||
"scud",
|
||||
}
|
||||
|
||||
local koreans = {
|
||||
"black tea",
|
||||
"ssogal",
|
||||
"girong",
|
||||
"scud",
|
||||
"명박오니●",
|
||||
"rocketmania",
|
||||
"maybe",
|
||||
"lac",
|
||||
"chupa",
|
||||
"momo",
|
||||
"천령씨",
|
||||
}
|
||||
|
||||
local japanese = {
|
||||
"kilroy",
|
||||
"bubu",
|
||||
"ゆっけりあーの",
|
||||
"yomofox",
|
||||
"zaguya",
|
||||
"acchan",
|
||||
"cabin mild",
|
||||
"enngawa",
|
||||
"freeman",
|
||||
"piichan",
|
||||
"fia",
|
||||
}
|
||||
|
||||
for _, text in RandomPairs(japanese) do
|
||||
A(text, 1, 0)
|
||||
end
|
||||
|
||||
A("00:06 - *DEAD* Bubu: おおおっきいいいいおおおおおおお", 2)
|
||||
|
||||
for _, text in RandomPairs(koreans) do
|
||||
A(text, 1, 0)
|
||||
end
|
||||
|
||||
A("ㅋㅋㅋㅋㅋㅋㅋㅋㅋ", 2)
|
||||
|
||||
for _, text in RandomPairs(cast) do
|
||||
A(text, 1, 0)
|
||||
end
|
||||
|
||||
A("makeup department", 2)
|
||||
|
||||
A(4)
|
||||
|
||||
A("black tea", 1)
|
||||
A("momo", 1.5)
|
||||
A("yomofox", 1)
|
||||
A("translations", 2)
|
||||
|
||||
A("your imagination")
|
||||
A("garry")
|
||||
A("puush")
|
||||
A("gdrive")
|
||||
A("dropbox")
|
||||
A("metastruct")
|
||||
A("Production Management", 2)
|
||||
|
||||
A("workshop")
|
||||
A("garrysmod.org")
|
||||
A("nexusmods")
|
||||
A("valve")
|
||||
A("Art Direction", 2)
|
||||
|
||||
A("Mark James")
|
||||
A("Editor Icons", 2)
|
||||
|
||||
A("Morten")
|
||||
A("HTML Department", 2)
|
||||
A(4)
|
||||
A("capsadmin", 1)
|
||||
A("written and managed by", 1)
|
||||
A("pac3", 4)
|
||||
|
||||
local start_height = 0
|
||||
local text_size = 32
|
||||
local text_spacing = 4
|
||||
|
||||
for k,v in pairs(credits) do
|
||||
if v[2] then
|
||||
v[1] = v[1]:upper()
|
||||
start_height = start_height + text_size + text_spacing
|
||||
end
|
||||
end
|
||||
|
||||
start_height = start_height * 1.75
|
||||
|
||||
function DEMO:DrawCredits(w, h, d, t, pos)
|
||||
local last_height = 0
|
||||
|
||||
for i, data in pairs(credits) do
|
||||
if not data[2] then
|
||||
last_height = last_height + data[1] * text_size + text_spacing
|
||||
else
|
||||
local w, h = self:DrawPrettyText(
|
||||
data[1],
|
||||
self.spos.x,
|
||||
-t * 30 + self.spos.y - last_height + start_height,
|
||||
|
||||
"Roboto-Black",
|
||||
text_size * data[2],
|
||||
|
||||
0,
|
||||
10,
|
||||
|
||||
Color(255, 255, 255, 200),
|
||||
Color(255, 100, 255, 50),
|
||||
|
||||
data[3] or 0.5,
|
||||
1
|
||||
)
|
||||
|
||||
last_height = last_height + h * data[2] + text_spacing
|
||||
end
|
||||
end
|
||||
|
||||
self.spos = self.spos + ((pos - self.spos) * d)
|
||||
end
|
||||
|
||||
function DEMO:DrawParticles(w, h, d, t, pos)
|
||||
d = d * 50
|
||||
|
||||
local mult = 0.00001
|
||||
|
||||
if input.IsMouseDown(MOUSE_RIGHT) then
|
||||
mult = 0.0001
|
||||
end
|
||||
|
||||
for i, part in pairs(self.particles) do
|
||||
-- random velocity for some variation
|
||||
part.vel.x = part.vel.x + ((pos.x - part.pos.x) * mult * part.siz) + math.Rand(-0.1,0.1)
|
||||
part.vel.y = part.vel.y + ((pos.y - part.pos.y) * mult * part.siz) + math.Rand(-0.1,0.1)
|
||||
|
||||
-- velocity
|
||||
part.pos.x = part.pos.x + (part.vel.x * d)
|
||||
part.pos.y = part.pos.y + (part.vel.y * d)
|
||||
|
||||
-- friction
|
||||
part.vel.x = part.vel.x * part.drag
|
||||
part.vel.y = part.vel.y * part.drag
|
||||
|
||||
-- collision with other particles (buggy)
|
||||
if part.pos.x - part.siz < 0 then
|
||||
part.pos.x = 0 + part.siz * 1
|
||||
part.vel.x = part.vel.x * -part.drag
|
||||
end
|
||||
|
||||
if part.pos.x + part.siz > w then
|
||||
part.pos.x = w - part.siz
|
||||
part.vel.x = part.vel.x * -part.drag
|
||||
end
|
||||
|
||||
if part.pos.y - part.siz < 0 then
|
||||
part.pos.y = 0 + part.siz * 1
|
||||
part.vel.y = part.vel.y * -part.drag
|
||||
end
|
||||
|
||||
if part.pos.y + part.siz > h then
|
||||
part.pos.y = h + part.siz * -1
|
||||
part.vel.y = part.vel.y * -part.drag
|
||||
end
|
||||
|
||||
local l = (part.vel.x * part.vel.y) + 5
|
||||
l = l * 0.75
|
||||
|
||||
local life_scale = math.min(part.life - t, 1) ^ 2
|
||||
local s = math.min(part.siz * l + 40, 100)
|
||||
|
||||
surface.SetTexture(part.tex_id2)
|
||||
|
||||
surface.SetDrawColor(part.clr.r, part.clr.g, part.clr.b, 255)
|
||||
self:DrawLineEx(
|
||||
part.pos.x,
|
||||
part.pos.y,
|
||||
part.pos.x - part.vel.x*l,
|
||||
part.pos.y - part.vel.y*l,
|
||||
|
||||
part.siz * life_scale, true
|
||||
)
|
||||
|
||||
s = s * life_scale
|
||||
|
||||
surface.SetDrawColor(part.clr.r*0.1*l, part.clr.g*0.1*l, part.clr.b*0.1*l, 255)
|
||||
surface.DrawTexturedRect(
|
||||
(part.pos.x - s * 0.5),
|
||||
(part.pos.y - s * 0.5),
|
||||
s,
|
||||
s
|
||||
)
|
||||
|
||||
if part.life < t and (not part.on_death or part:on_death() ~= false) then
|
||||
self.particles[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DEMO:DrawPostProcess(w, h, d, t, pos)
|
||||
local params = {}
|
||||
|
||||
params["$pp_colour_addr"] = 0
|
||||
params["$pp_colour_addg"] = 0
|
||||
params["$pp_colour_addb"] = 0
|
||||
params["$pp_colour_brightness"] = -0.1
|
||||
params["$pp_colour_contrast"] = 0.8
|
||||
params["$pp_colour_colour"] = math.sin(t) * 1 - 0.5
|
||||
params["$pp_colour_mulr"] = math.sin(t) / 3
|
||||
params["$pp_colour_mulg"] = math.cos(t) / 2
|
||||
params["$pp_colour_mulb"] = math.asin(t) / 2
|
||||
DrawColorModify(params)
|
||||
|
||||
local vel = ((self.last_pos or pos) - pos):Length() / 200
|
||||
|
||||
if vel > 1 then
|
||||
self.cursor = "arrow"
|
||||
else
|
||||
self.cursor = "none"
|
||||
end
|
||||
|
||||
vel = vel + 0.1
|
||||
|
||||
DrawSunbeams(0.5, vel, 0.05, self.spos.x / w, self.spos.y / h)
|
||||
blur_screen(w, h, self.spos.x / w, self.spos.y / h)
|
||||
end
|
||||
|
||||
local function ang_to_dir(ang, scale)
|
||||
ang = math.deg(ang)
|
||||
scale = scale or 1
|
||||
return math.sin(ang) * scale, math.cos(ang) * scale
|
||||
end
|
||||
|
||||
function DEMO:SpawnFireworks(x, y)
|
||||
local vx, vy = ang_to_dir(math.Rand(-45, 45), math.Rand(10, 20))
|
||||
self:CreateParticle(x, y, vx, vy, nil, function(part)
|
||||
for i = -90, 90 do
|
||||
self:CreateParticle(part.pos.x, part.pos.y, ang_to_dir(i * 2, math.Rand(1, 5) * math.Rand(1, 2)))
|
||||
end
|
||||
|
||||
self.base_color = self.base_color + math.Rand(30, 60)
|
||||
end)
|
||||
end
|
||||
|
||||
function DEMO:OnDraw(w, h, d, t, pos)
|
||||
|
||||
-- background
|
||||
surface.SetDrawColor(0, 0, 0, 20)
|
||||
surface.DrawRect(w*-1, h*-1, w*4, h*4)
|
||||
|
||||
if input.IsMouseDown(MOUSE_LEFT) then
|
||||
self:SpawnFireworks(input.GetCursorPos())
|
||||
end
|
||||
|
||||
if math.random() > 0.99 then
|
||||
self:SpawnFireworks(math.Rand(0, w), h - 20)
|
||||
end
|
||||
|
||||
self:DrawCredits(w, h, d, t, pos)
|
||||
self:DrawParticles(w, h, d, t, pos)
|
||||
self:DrawPostProcess(w, h, d, t, pos)
|
||||
|
||||
self.last_pos = pos
|
||||
end
|
||||
|
||||
function DEMO:OnUpate(w, h, d, t, pos, first)
|
||||
self.time = t
|
||||
|
||||
if first then
|
||||
local ok, err = pcall(self.OnStart, self, w, h)
|
||||
if not ok then return ok, err end
|
||||
end
|
||||
|
||||
local ok, mat = pcall(self.PreUpdate, self, w, h, t, d)
|
||||
|
||||
if not ok then return ok, mat end
|
||||
|
||||
cam.Start2D()
|
||||
if mat then cam.PushModelMatrix(mat) end
|
||||
local ok, err = pcall(self.OnDraw, self, w, h, d, t, pos)
|
||||
if mat then cam.PopModelMatrix() end
|
||||
cam.End2D()
|
||||
|
||||
return ok, err
|
||||
end
|
||||
|
||||
function pace.ShowAbout()
|
||||
|
||||
local pnl = vgui.Create("Panel")
|
||||
pnl:SetPos(0, 0)
|
||||
pnl:SetSize(ScrW(), ScrH())
|
||||
pnl:MakePopup()
|
||||
|
||||
local html = vgui.Create("DHTML", pnl)
|
||||
html:OpenURL("https://www.youtube.com/watch?v=Kvg7oTfGhYg")
|
||||
|
||||
local first = true
|
||||
local start_time = RealTime()
|
||||
|
||||
pac.AddHook("PreRender", "pace_about", function()
|
||||
|
||||
local w, h = ScrW(), ScrH()
|
||||
local t = RealTime() - start_time
|
||||
local d = FrameTime()
|
||||
|
||||
local ok, err = DEMO:OnUpate(w, h, d, t, Vector(input.GetCursorPos()), first)
|
||||
|
||||
if pnl.last_cursor ~= DEMO.cursor then
|
||||
pnl:SetCursor(DEMO.cursor or "arrow")
|
||||
pnl.last_cursor = DEMO.cursor
|
||||
end
|
||||
|
||||
first = false
|
||||
|
||||
local quit = input.IsKeyDown(KEY_SPACE) or input.IsKeyDown(KEY_ESCAPE) or not ok
|
||||
|
||||
if quit then
|
||||
if not ok then print(err) end
|
||||
pnl:Remove()
|
||||
pac.RemoveHook("PreRender", "pace_about")
|
||||
return
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
end
|
||||
1142
lua/pac3/editor/client/animation_timeline.lua
Normal file
1142
lua/pac3/editor/client/animation_timeline.lua
Normal file
File diff suppressed because it is too large
Load Diff
1524
lua/pac3/editor/client/asset_browser.lua
Normal file
1524
lua/pac3/editor/client/asset_browser.lua
Normal file
File diff suppressed because it is too large
Load Diff
7212
lua/pac3/editor/client/examples.lua
Normal file
7212
lua/pac3/editor/client/examples.lua
Normal file
File diff suppressed because one or more lines are too long
74
lua/pac3/editor/client/fonts.lua
Normal file
74
lua/pac3/editor/client/fonts.lua
Normal file
@@ -0,0 +1,74 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
pace.Fonts = {}
|
||||
|
||||
for i = 1, 5 do
|
||||
surface.CreateFont("pac_font_"..i,
|
||||
{
|
||||
font = "Arial",
|
||||
size = 11 + i,
|
||||
weight = 50,
|
||||
antialias = true,
|
||||
})
|
||||
|
||||
table.insert(pace.Fonts, "pac_font_"..i)
|
||||
end
|
||||
|
||||
for i = 1, 5 do
|
||||
surface.CreateFont("pac_font_bold"..i,
|
||||
{
|
||||
font = "Arial",
|
||||
size = 11 + i,
|
||||
weight = 800,
|
||||
antialias = true,
|
||||
})
|
||||
table.insert(pace.Fonts, "pac_font_bold"..i)
|
||||
end
|
||||
|
||||
table.insert(pace.Fonts, "DermaDefault")
|
||||
table.insert(pace.Fonts, "DermaDefaultBold")
|
||||
|
||||
local font_cvar = CreateClientConVar("pac_editor_font", pace.Fonts[1])
|
||||
|
||||
function pace.SetFont(fnt)
|
||||
pace.CurrentFont = fnt or font_cvar:GetString()
|
||||
|
||||
if not table.HasValue(pace.Fonts, pace.CurrentFont) then
|
||||
pace.CurrentFont = "DermaDefault"
|
||||
end
|
||||
|
||||
RunConsoleCommand("pac_editor_font", pace.CurrentFont)
|
||||
|
||||
if pace.Editor and pace.Editor:IsValid() then
|
||||
pace.CloseEditor()
|
||||
timer.Simple(0.1, function()
|
||||
pace.OpenEditor()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function pace.AddFontsToMenu(menu)
|
||||
local menu,pnl = menu:AddSubMenu(L"font")
|
||||
pnl:SetImage("icon16/text_bold.png")
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
|
||||
for key, val in pairs(pace.Fonts) do
|
||||
local pnl = menu:AddOption(L"The quick brown fox jumps over the lazy dog. (" ..val ..")", function()
|
||||
pace.SetFont(val)
|
||||
end)
|
||||
|
||||
pnl:SetFont(val)
|
||||
end
|
||||
end
|
||||
|
||||
pace.SetFont()
|
||||
49
lua/pac3/editor/client/icons.lua
Normal file
49
lua/pac3/editor/client/icons.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
pace.MiscIcons = {
|
||||
about = "icon16/star.png",
|
||||
appearance = "icon16/paintcan.png",
|
||||
autoload = "icon16/transmit_go.png",
|
||||
chat = "icon16/comment.png",
|
||||
clear = "icon16/cross.png",
|
||||
clone = "icon16/page_copy.png",
|
||||
copy = "icon16/page_white_text.png",
|
||||
edit = "icon16/table_edit.png",
|
||||
error = "icon16/exclamation.png",
|
||||
exit = "icon16/cancel.png",
|
||||
font = "icon16/text_smallcaps.png",
|
||||
help = "icon16/help.png",
|
||||
info = "icon16/information.png",
|
||||
language = "icon16/user_comment.png",
|
||||
load = "icon16/folder.png",
|
||||
new = "icon16/add.png",
|
||||
orientation = "icon16/shape_handles.png",
|
||||
outfit = "icon16/group.png",
|
||||
paste = "icon16/paste_plain.png",
|
||||
replace = "icon16/arrow_refresh.png",
|
||||
revert = "icon16/table_delete.png",
|
||||
save = "icon16/disk.png",
|
||||
uniqueid = "icon16/vcard.png",
|
||||
url = "icon16/server_go.png",
|
||||
warning = "icon16/error.png",
|
||||
wear = "icon16/transmit.png",
|
||||
}
|
||||
|
||||
pace.GroupsIcons = {
|
||||
effects = 'icon16/wand.png',
|
||||
model = 'icon16/shape_square.png',
|
||||
entity = 'icon16/brick.png',
|
||||
modifiers = 'icon16/disconnect.png',
|
||||
advanced = 'icon16/page_white_gear.png',
|
||||
experimental = 'icon16/bug.png',
|
||||
legacy = 'icon16/hourglass.png',
|
||||
}
|
||||
419
lua/pac3/editor/client/init.lua
Normal file
419
lua/pac3/editor/client/init.lua
Normal file
@@ -0,0 +1,419 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
include("autorun/pac_core_init.lua")
|
||||
|
||||
pace = pace or {}
|
||||
pace.luadata = include("pac3/libraries/luadata.lua")
|
||||
|
||||
include("language.lua")
|
||||
include("icons.lua")
|
||||
|
||||
include("util.lua")
|
||||
include("wear.lua")
|
||||
|
||||
include("select.lua")
|
||||
include("view.lua")
|
||||
include("parts.lua")
|
||||
include("saved_parts.lua")
|
||||
include("logic.lua")
|
||||
include("undo.lua")
|
||||
include("fonts.lua")
|
||||
include("settings.lua")
|
||||
include("shortcuts.lua")
|
||||
include("asset_browser.lua")
|
||||
include("menu_bar.lua")
|
||||
|
||||
include("mctrl.lua")
|
||||
|
||||
include("panels.lua")
|
||||
include("tools.lua")
|
||||
include("spawnmenu.lua")
|
||||
include("wiki.lua")
|
||||
include("examples.lua")
|
||||
include("about.lua")
|
||||
include("animation_timeline.lua")
|
||||
include("render_scores.lua")
|
||||
include("wires.lua")
|
||||
|
||||
include("wear_filter.lua")
|
||||
include("show_outfit_on_use.lua")
|
||||
|
||||
do
|
||||
local hue =
|
||||
{
|
||||
"red",
|
||||
"orange",
|
||||
"yellow",
|
||||
"green",
|
||||
"turquoise",
|
||||
"blue",
|
||||
"purple",
|
||||
"magenta",
|
||||
}
|
||||
|
||||
local sat =
|
||||
{
|
||||
"pale",
|
||||
"",
|
||||
"strong",
|
||||
}
|
||||
|
||||
local val =
|
||||
{
|
||||
"dark",
|
||||
"",
|
||||
"bright"
|
||||
}
|
||||
|
||||
function pace.HSVToNames(h,s,v)
|
||||
return
|
||||
hue[math.Round((1+(h/360)*#hue))] or hue[1],
|
||||
sat[math.ceil(s*#sat)] or sat[1],
|
||||
val[math.ceil(v*#val)] or val[1]
|
||||
end
|
||||
|
||||
function pace.ColorToNames(c)
|
||||
if c.r == 255 and c.g == 255 and c.b == 255 then return "white", "", "bright" end
|
||||
if c.r == 0 and c.g == 0 and c.b == 0 then return "black", "", "bright" end
|
||||
return pace.HSVToNames(ColorToHSV(Color(c.r, c.g, c.b)))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function pace.CallHook(str, ...)
|
||||
return hook.Call("pace_" .. str, GAMEMODE, ...)
|
||||
end
|
||||
|
||||
pace.ActivePanels = pace.ActivePanels or {}
|
||||
pace.Editor = NULL
|
||||
|
||||
local remember = CreateConVar("pac_editor_remember_position", "1", {FCVAR_ARCHIVE}, "Remember PAC3 editor position on screen")
|
||||
local positionMode = CreateConVar("pac_editor_position_mode", "0", {FCVAR_ARCHIVE}, "Editor position mode. 0 - Left, 1 - middle, 2 - Right. Has no effect if pac_editor_remember_position is true")
|
||||
local showCameras = CreateConVar("pac_show_cameras", "1", {FCVAR_ARCHIVE}, "Show the PAC cameras of players using the editor")
|
||||
local showInEditor = CreateConVar("pac_show_in_editor", "1", {FCVAR_ARCHIVE}, "Show the 'In PAC3 Editor' text above players using the editor")
|
||||
pace.pac_show_uniqueid = CreateConVar("pac_show_uniqueid", "0", {FCVAR_ARCHIVE}, "Show uniqueids of parts inside editor")
|
||||
|
||||
function pace.OpenEditor()
|
||||
pace.CloseEditor()
|
||||
|
||||
if hook.Run("PrePACEditorOpen", pac.LocalPlayer) == false then return end
|
||||
|
||||
pac.Enable()
|
||||
|
||||
pace.RefreshFiles()
|
||||
|
||||
pace.SetLanguage()
|
||||
|
||||
local editor = pace.CreatePanel("editor")
|
||||
editor:SetSize(240, ScrH())
|
||||
editor:MakePopup()
|
||||
--editor:SetPos(0, 0)
|
||||
editor.Close = function()
|
||||
--editor:OnRemove() -- ??? This is called by the engine
|
||||
--editor.__OnClosed = true
|
||||
pace.CloseEditor()
|
||||
end
|
||||
pace.Editor = editor
|
||||
pace.Active = true
|
||||
|
||||
if remember:GetBool() then
|
||||
local x = cookie.GetNumber("pac_editor_x", 0)
|
||||
|
||||
if x < 0 or x + 240 > ScrW() then
|
||||
x = 0
|
||||
end
|
||||
|
||||
editor:SetPos(x, 0)
|
||||
cookie.Set("pac_editor_x", tostring(x))
|
||||
else
|
||||
local mode = positionMode:GetInt()
|
||||
|
||||
if mode == 1 then
|
||||
editor:SetPos(ScrW() / 2 - 120, 0)
|
||||
elseif mode == 2 then
|
||||
editor:SetPos(ScrW() - 240, 0)
|
||||
else
|
||||
editor:SetPos(0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
if ctp and ctp.Disable then
|
||||
ctp:Disable()
|
||||
end
|
||||
|
||||
RunConsoleCommand("pac_enable", "1")
|
||||
RunConsoleCommand("pac_in_editor", "1")
|
||||
|
||||
pace.SetInPAC3Editor(true)
|
||||
|
||||
pace.DisableExternalHooks()
|
||||
vgui.GetWorldPanel():SetWorldClicker(false)
|
||||
|
||||
pace.Call("OpenEditor")
|
||||
end
|
||||
|
||||
function pace.CloseEditor()
|
||||
pace.RestoreExternalHooks()
|
||||
|
||||
if pace.Editor:IsValid() then
|
||||
local x = pace.Editor:GetPos()
|
||||
cookie.Set("pac_editor_x", tostring(x))
|
||||
--if not editor.__OnClosed then
|
||||
--pace.Editor:OnRemove() -- ??? This is called by the engine
|
||||
--end
|
||||
|
||||
pace.Editor:Remove()
|
||||
pace.Active = false
|
||||
pace.Focused = false
|
||||
pace.Call("CloseEditor")
|
||||
|
||||
if pace.timeline.IsActive() then
|
||||
pace.timeline.Close()
|
||||
end
|
||||
end
|
||||
|
||||
RunConsoleCommand("pac_in_editor", "0")
|
||||
pace.SetInPAC3Editor(false)
|
||||
end
|
||||
|
||||
function pace.HasFocus()
|
||||
return pace.Editor:IsValid() and pace.Editor:HasFocus()
|
||||
end
|
||||
|
||||
pac.AddHook("pac_Disable", "pac_editor_disable", function()
|
||||
pace.CloseEditor()
|
||||
end)
|
||||
|
||||
function pace.RefreshFiles()
|
||||
pace.CachedFiles = nil
|
||||
|
||||
if pace.Editor:IsValid() then
|
||||
pace.Editor:MakeBar()
|
||||
end
|
||||
|
||||
if pace.SpawnlistBrowser:IsValid() then
|
||||
pace.SpawnlistBrowser:PopulateFromClient()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function pace.Panic()
|
||||
pace.CloseEditor()
|
||||
|
||||
for key, pnl in pairs(pace.ActivePanels) do
|
||||
if pnl:IsValid() then
|
||||
pnl:Remove()
|
||||
table.remove(pace.ActivePanels, key)
|
||||
end
|
||||
end
|
||||
|
||||
pace.SafeRemoveSpecialPanel()
|
||||
|
||||
for i, ent in ipairs(ents.GetAll()) do
|
||||
if ent:IsValid() then
|
||||
ent.pac_onuse_only = nil
|
||||
ent.pac_onuse_only_check = nil
|
||||
hook.Remove('pace_OnUseOnlyUpdates', ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do -- forcing hooks
|
||||
pace.ExternalHooks =
|
||||
{
|
||||
"CalcView",
|
||||
"ShouldDrawLocalPlayer",
|
||||
}
|
||||
|
||||
function pace.DisableExternalHooks()
|
||||
if DLib or ULib then return end -- hook with priority
|
||||
|
||||
for _, event in pairs(pace.ExternalHooks) do
|
||||
local hooks = hook.GetTable()[event]
|
||||
|
||||
if hooks then
|
||||
pace.OldHooks = pace.OldHooks or {}
|
||||
pace.OldHooks[event] = pace.OldHooks[event] or {}
|
||||
pace.OldHooks[event] = table.Copy(hooks)
|
||||
|
||||
for name in pairs(hooks) do
|
||||
if isstring(name) and name:sub(1, 4) ~= "pace_" then
|
||||
hook.Remove(event, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.RestoreExternalHooks()
|
||||
if DLib or ULib then return end -- hook with priority
|
||||
|
||||
if pace.OldHooks then
|
||||
for event, hooks in pairs(pace.OldHooks) do
|
||||
for name, func in pairs(hooks) do
|
||||
if isstring(name) and name:sub(1, 4) ~= "pace_" then
|
||||
hook.Add(event, name, func)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pace.OldHooks = nil
|
||||
end
|
||||
end
|
||||
|
||||
function pace.IsActive()
|
||||
return pace.Active == true
|
||||
end
|
||||
|
||||
concommand.Add("pac_editor_panic", function()
|
||||
pace.Panic()
|
||||
timer.Simple(0.1, function() pace.OpenEditor() end)
|
||||
end)
|
||||
|
||||
concommand.Add("pac_editor", function(_, _, args)
|
||||
if args[1] == "toggle" then
|
||||
if pace.IsActive() then
|
||||
pace.CloseEditor()
|
||||
else
|
||||
pace.OpenEditor()
|
||||
end
|
||||
else
|
||||
pace.OpenEditor()
|
||||
end
|
||||
end)
|
||||
|
||||
concommand.Add("pac_reset_eye_angles", function() pace.ResetEyeAngles() end)
|
||||
concommand.Add("pac_toggle_tpose", function() pace.SetTPose(not pace.GetTPose()) end)
|
||||
|
||||
function pace.Call(str, ...)
|
||||
if pace["On" .. str] then
|
||||
if hook.Run("pace_On" .. str, ...) ~= false then
|
||||
return pace["On" .. str](...)
|
||||
end
|
||||
else
|
||||
ErrorNoHalt("missing function pace.On" .. str .. "!\n")
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
function pace.SetInPAC3Editor(b)
|
||||
net.Start("pac_in_editor")
|
||||
net.WriteBit(b)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
local up = Vector(0,0,10000)
|
||||
|
||||
hook.Add("HUDPaint", "pac_in_editor", function()
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply ~= pac.LocalPlayer and ply:GetNW2Bool("pac_in_editor") then
|
||||
|
||||
if showCameras:GetInt() == 1 then
|
||||
if ply.pac_editor_cam_pos then
|
||||
if not IsValid(ply.pac_editor_camera) then
|
||||
ply.pac_editor_camera = ClientsideModel("models/tools/camera/camera.mdl")
|
||||
ply.pac_editor_camera:SetModelScale(0.25,0)
|
||||
local ent = ply.pac_editor_camera
|
||||
ply:CallOnRemove("pac_editor_camera", function()
|
||||
SafeRemoveEntity(ent)
|
||||
end)
|
||||
end
|
||||
|
||||
local ent = ply.pac_editor_camera
|
||||
|
||||
local dt = math.Clamp(FrameTime() * 5, 0.0001, 0.5)
|
||||
|
||||
ent:SetPos(LerpVector(dt, ent:GetPos(), ply.pac_editor_cam_pos))
|
||||
ent:SetAngles(LerpAngle(dt, ent:GetAngles(), ply.pac_editor_cam_ang))
|
||||
|
||||
local pos_3d = ent:GetPos()
|
||||
local dist = pos_3d:Distance(EyePos())
|
||||
|
||||
if dist > 10 then
|
||||
local pos_2d = pos_3d:ToScreen()
|
||||
if pos_2d.visible then
|
||||
local alpha = math.Clamp(pos_3d:Distance(EyePos()) * -1 + 500, 0, 500)/500
|
||||
if alpha > 0 then
|
||||
draw.DrawText(ply:Nick() .. "'s PAC3 camera", "ChatFont", pos_2d.x, pos_2d.y, Color(255,255,255,alpha*255), 1)
|
||||
|
||||
if not ply.pac_editor_part_pos:IsZero() then
|
||||
surface.SetDrawColor(255, 255, 255, alpha*100)
|
||||
local endpos = ply.pac_editor_part_pos:ToScreen()
|
||||
if endpos.visible then
|
||||
surface.DrawLine(pos_2d.x, pos_2d.y, endpos.x, endpos.y)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if ply.pac_editor_camera then
|
||||
SafeRemoveEntity(ply.pac_editor_camera)
|
||||
ply.pac_editor_camera = nil
|
||||
end
|
||||
end
|
||||
|
||||
if showInEditor:GetInt() == 1 then
|
||||
local pos_3d = ply:NearestPoint(ply:EyePos() + up) + Vector(0,0,5)
|
||||
local alpha = math.Clamp(pos_3d:Distance(EyePos()) * -1 + 500, 0, 500)/500
|
||||
if alpha > 0 then
|
||||
local pos_2d = pos_3d:ToScreen()
|
||||
draw.DrawText("In PAC3 Editor", "ChatFont", pos_2d.x, pos_2d.y, Color(255,255,255,alpha*255), 1)
|
||||
end
|
||||
end
|
||||
else
|
||||
if ply.pac_editor_camera then
|
||||
SafeRemoveEntity(ply.pac_editor_camera)
|
||||
ply.pac_editor_camera = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
do
|
||||
local lastViewPos, lastViewAngle, lastTargetPos
|
||||
|
||||
timer.Create("pac_in_editor", 0.25, 0, function()
|
||||
if not pace.Active then return end
|
||||
if not pace.current_part:IsValid() then return end
|
||||
local pos, ang = pace.GetViewPos(), pace.GetViewAngles()
|
||||
local target_pos = pace.mctrl.GetWorldPosition()
|
||||
|
||||
if not target_pos then return end
|
||||
|
||||
if lastViewPos == pos and lastViewAngle == ang and lastTargetPos == target_pos then return end
|
||||
lastViewPos, lastViewAngle, lastTargetPos = pos, ang, target_pos
|
||||
|
||||
net.Start("pac_in_editor_posang", true)
|
||||
net.WriteVector(pos)
|
||||
net.WriteAngle(ang)
|
||||
net.WriteVector(target_pos)
|
||||
net.SendToServer()
|
||||
end)
|
||||
end
|
||||
|
||||
net.Receive("pac_in_editor_posang", function()
|
||||
local ply = net.ReadEntity()
|
||||
if not IsValid( ply ) then return end
|
||||
|
||||
local pos = net.ReadVector()
|
||||
local ang = net.ReadAngle()
|
||||
local part_pos = net.ReadVector()
|
||||
|
||||
ply.pac_editor_cam_pos = pos
|
||||
ply.pac_editor_cam_ang = ang
|
||||
ply.pac_editor_part_pos = part_pos
|
||||
end)
|
||||
end
|
||||
|
||||
pace.RegisterPanels()
|
||||
160
lua/pac3/editor/client/language.lua
Normal file
160
lua/pac3/editor/client/language.lua
Normal file
@@ -0,0 +1,160 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pace.KnownGUIStrings = pace.KnownGUIStrings or {}
|
||||
pace.CurrentTranslation = {}
|
||||
|
||||
local cvar = CreateClientConVar("pac_language", "english", true)
|
||||
|
||||
function pace.LanguageString(val)
|
||||
local key = val:Trim():lower()
|
||||
|
||||
pace.KnownGUIStrings[key] = val
|
||||
|
||||
return pace.CurrentTranslation[key] or val
|
||||
end
|
||||
|
||||
local L = pace.LanguageString
|
||||
|
||||
function pace.AddLanguagesToMenu(menu)
|
||||
local menu, pnl = menu:AddSubMenu(L"language")
|
||||
pnl:SetImage("icon16/world_edit.png")
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
menu:AddOption("english", function()
|
||||
pace.SetLanguage("english")
|
||||
end)
|
||||
|
||||
for key, val in pairs(file.Find("pac3/editor/client/translations/*", "LUA")) do
|
||||
val = val:gsub("%.lua", "")
|
||||
menu:AddOption(val, function()
|
||||
pace.SetLanguage(val)
|
||||
end)
|
||||
end
|
||||
|
||||
menu:AddSpacer()
|
||||
|
||||
menu:AddOption("edit", function() pace.ShowLanguageEditor() end)
|
||||
end
|
||||
|
||||
function pace.ShowLanguageEditor()
|
||||
local lang = cvar:GetString()
|
||||
|
||||
local frame = vgui.Create("DFrame")
|
||||
frame:SetSize(512, 512)
|
||||
frame:Center()
|
||||
frame:MakePopup()
|
||||
frame:SetTitle(L"translation editor")
|
||||
|
||||
local list = vgui.Create("DListView", frame)
|
||||
list:Dock(FILL)
|
||||
|
||||
list:AddColumn("english")
|
||||
list:AddColumn(lang)
|
||||
|
||||
local strings = {}
|
||||
|
||||
for k,v in pairs(pace.KnownGUIStrings) do
|
||||
strings[k] = v:Trim():lower()
|
||||
end
|
||||
table.Merge(strings, pace.CurrentTranslation)
|
||||
|
||||
for english, other in pairs(strings) do
|
||||
|
||||
local line = list:AddLine(english, other)
|
||||
line.OnRightClick = function()
|
||||
local menu = DermaMenu()
|
||||
menu:SetPos(input.GetCursorPos())
|
||||
menu:AddOption(L"edit", function()
|
||||
local window = Derma_StringRequest(
|
||||
L"translate",
|
||||
english,
|
||||
other,
|
||||
|
||||
function(new)
|
||||
pace.CurrentTranslation[english] = new
|
||||
line:SetValue(2, new)
|
||||
pace.SaveCurrentTranslation()
|
||||
end
|
||||
)
|
||||
for _, pnl in pairs(window:GetChildren()) do
|
||||
if pnl.ClassName == "DPanel" then
|
||||
for key, pnl in pairs(pnl:GetChildren()) do
|
||||
if pnl.ClassName == "DTextEntry" then
|
||||
pnl:SetAllowNonAsciiCharacters(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end):SetImage(pace.MiscIcons.edit)
|
||||
menu:AddOption(L"revert", function()
|
||||
local new = CompileFile("pac3/editor/client/translations/"..lang..".lua")()[english]
|
||||
pace.CurrentTranslation[english] = new
|
||||
line:SetValue(2, new or english)
|
||||
pace.SaveCurrentTranslation()
|
||||
end):SetImage(pace.MiscIcons.revert)
|
||||
|
||||
menu:MakePopup()
|
||||
end
|
||||
end
|
||||
|
||||
list:SizeToContents()
|
||||
end
|
||||
|
||||
function pace.SaveCurrentTranslation()
|
||||
local str = {}
|
||||
|
||||
table.insert(str, "return {")
|
||||
|
||||
for key, val in pairs(pace.CurrentTranslation) do
|
||||
table.insert(str, string.format("[%q] = %q,", key, val))
|
||||
end
|
||||
|
||||
table.insert(str, "}")
|
||||
|
||||
file.CreateDir("pac3_editor", "DATA")
|
||||
file.Write("pac3_editor/" .. cvar:GetString() .. ".txt", table.concat(str, "\n"), "DATA")
|
||||
end
|
||||
|
||||
function pace.GetOutputForTranslation()
|
||||
local str = ""
|
||||
|
||||
for key, val in pairs(pace.KnownGUIStrings) do
|
||||
str = str .. ("%s = %s\n"):format(key:gsub("(.)","_%1_"), val)
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
function pace.SetLanguage(lang)
|
||||
|
||||
lang = lang or cvar:GetString()
|
||||
RunConsoleCommand("pac_language", lang)
|
||||
|
||||
pace.CurrentTranslation = {}
|
||||
|
||||
if lang ~= "english" then
|
||||
if file.Exists("pac3_editor/" .. lang .. ".txt", "DATA") then
|
||||
table.Merge(pace.CurrentTranslation, CompileString(file.Read("pac3_editor/" .. lang .. ".txt", "DATA"), "pac3_lang")())
|
||||
elseif file.Exists("pac3/editor/client/translations/"..lang..".lua", "LUA") then
|
||||
table.Merge(pace.CurrentTranslation, CompileFile("pac3/editor/client/translations/"..lang..".lua")())
|
||||
else
|
||||
pac.Message(Color(255,0,0), "language " .. lang .. " does not exist, falling back to english")
|
||||
RunConsoleCommand("pac_language", "english")
|
||||
lang = "english"
|
||||
end
|
||||
end
|
||||
|
||||
if pace.Editor and pace.Editor:IsValid() then
|
||||
pace.CloseEditor()
|
||||
timer.Simple(0.1, function()
|
||||
pace.OpenEditor()
|
||||
end)
|
||||
end
|
||||
end
|
||||
103
lua/pac3/editor/client/logic.lua
Normal file
103
lua/pac3/editor/client/logic.lua
Normal file
@@ -0,0 +1,103 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pace.current_part = NULL
|
||||
pace.properties = NULL
|
||||
pace.tree = NULL
|
||||
|
||||
local L = pace.LanguageString
|
||||
local alreadyInCall
|
||||
|
||||
function pace.PopulateProperties(part)
|
||||
if pace.properties:IsValid() then
|
||||
pace.properties:Populate(part:GetProperties())
|
||||
for k,v in pairs(pace.extra_populates) do
|
||||
v.func(v.pnl)
|
||||
end
|
||||
pace.extra_populates = {}
|
||||
|
||||
pace.Editor:InvalidateLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function pace.OnDraw()
|
||||
if not pace.editing_viewmodel and not pace.editing_hands then
|
||||
pace.mctrl.HUDPaint()
|
||||
end
|
||||
end
|
||||
|
||||
local function post_draw_view_model()
|
||||
if alreadyInCall then return end
|
||||
|
||||
if pace.editing_viewmodel and not pace.editing_hands then
|
||||
cam.Start2D()
|
||||
|
||||
alreadyInCall = true
|
||||
pace.mctrl.HUDPaint()
|
||||
alreadyInCall = false
|
||||
|
||||
cam.End2D()
|
||||
end
|
||||
end
|
||||
|
||||
local function post_draw_player_hands()
|
||||
if alreadyInCall then return end
|
||||
|
||||
if not pace.editing_viewmodel and pace.editing_hands then
|
||||
cam.Start2D()
|
||||
|
||||
alreadyInCall = true
|
||||
pace.mctrl.HUDPaint()
|
||||
alreadyInCall = false
|
||||
|
||||
cam.End2D()
|
||||
end
|
||||
end
|
||||
|
||||
function pace.OnOpenEditor()
|
||||
alreadyInCall = false
|
||||
pace.SetViewPos(pac.LocalPlayer:EyePos())
|
||||
pace.SetViewAngles(pac.LocalPlayer:EyeAngles())
|
||||
pace.EnableView(true)
|
||||
|
||||
if table.Count(pac.GetLocalParts()) == 0 then
|
||||
pace.Call("CreatePart", "group", L"my outfit")
|
||||
end
|
||||
|
||||
pace.TrySelectPart()
|
||||
|
||||
pace.ResetView()
|
||||
|
||||
pac.AddHook("PostDrawPlayerHands", "pace_viewmodel_edit", post_draw_player_hands)
|
||||
pac.AddHook("PostDrawViewModel", "pace_viewmodel_edit", post_draw_view_model)
|
||||
end
|
||||
|
||||
function pace.OnCloseEditor()
|
||||
pace.EnableView(false)
|
||||
pace.StopSelect()
|
||||
pace.SafeRemoveSpecialPanel()
|
||||
|
||||
pac.RemoveHook("PostDrawViewModel", "pace_viewmodel_edit")
|
||||
pac.RemoveHook("PostDrawPlayerHands", "pace_viewmodel_edit")
|
||||
end
|
||||
|
||||
function pace.TrySelectPart()
|
||||
local part = select(2, next(pac.GetLocalParts()))
|
||||
|
||||
local found = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), pace.current_part_uid)
|
||||
|
||||
if found:IsValid() and found:GetPlayerOwner() == part:GetPlayerOwner() then
|
||||
part = found
|
||||
end
|
||||
|
||||
if part then
|
||||
pace.Call("PartSelected", part)
|
||||
end
|
||||
end
|
||||
568
lua/pac3/editor/client/mctrl.lua
Normal file
568
lua/pac3/editor/client/mctrl.lua
Normal file
@@ -0,0 +1,568 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pace.mctrl = {}
|
||||
local mctrl = pace.mctrl
|
||||
mctrl.grab_dist = 15
|
||||
mctrl.angle_pos = 0.5
|
||||
local cvar_pos_grid = CreateClientConVar("pac_grid_pos_size", "4")
|
||||
local cvar_ang_grid = CreateClientConVar("pac_grid_ang_size", "45")
|
||||
|
||||
--[[
|
||||
Give this function the coordinates of a pixel on your screen, and it will return a unit vector pointing
|
||||
in the direction that the camera would project that pixel in.
|
||||
|
||||
Useful for converting mouse positions to aim vectors for traces.
|
||||
|
||||
iScreenX is the x position of your cursor on the screen, in pixels.
|
||||
iScreenY is the y position of your cursor on the screen, in pixels.
|
||||
iScreenW is the width of the screen, in pixels.
|
||||
iScreenH is the height of the screen, in pixels.
|
||||
angCamRot is the angle your camera is at
|
||||
fFoV is the Field of View (FOV) of your camera in ___radians___
|
||||
Note: This must be nonzero or you will get a divide by zero error.
|
||||
]]
|
||||
local function LPCameraScreenToVector(iScreenX, iScreenY, iScreenW, iScreenH, angCamRot, fFoV)
|
||||
--This code works by basically treating the camera like a frustrum of a pyramid.
|
||||
--We slice this frustrum at a distance "d" from the camera, where the slice will be a rectangle whose width equals the "4:3" width corresponding to the given screen height.
|
||||
local d = 4 * iScreenH / (6 * math.tan(0.5 * fFoV))
|
||||
--Forward, right, and up vectors (need these to convert from local to world coordinates
|
||||
local vForward = angCamRot:Forward()
|
||||
local vRight = angCamRot:Right()
|
||||
local vUp = angCamRot:Up()
|
||||
--Then convert vec to proper world coordinates and return it
|
||||
|
||||
return (d * vForward + (iScreenX - 0.5 * iScreenW) * vRight + (0.5 * iScreenH - iScreenY) * vUp):GetNormalized()
|
||||
end
|
||||
|
||||
--[[
|
||||
Give this function a vector, pointing from the camera to a position in the world,
|
||||
and it will return the coordinates of a pixel on your screen - this is where the world position would be projected onto your screen.
|
||||
|
||||
Useful for finding where things in the world are on your screen (if they are at all).
|
||||
|
||||
vDir is a direction vector pointing from the camera to a position in the world
|
||||
iScreenW is the width of the screen, in pixels.
|
||||
iScreenH is the height of the screen, in pixels.
|
||||
angCamRot is the angle your camera is at
|
||||
fFoV is the Field of View (FOV) of your camera in ___radians___
|
||||
Note: This must be nonzero or you will get a divide by zero error.
|
||||
|
||||
Returns x, y, iVisibility.
|
||||
x and y are screen coordinates.
|
||||
iVisibility will be:
|
||||
1 if the point is visible
|
||||
0 if the point is in front of the camera, but is not visible
|
||||
-1 if the point is behind the camera
|
||||
]]
|
||||
local function VectorToLPCameraScreen(vDir, iScreenW, iScreenH, angCamRot, fFoV)
|
||||
--Same as we did above, we found distance the camera to a rectangular slice of the camera's frustrum, whose width equals the "4:3" width corresponding to the given screen height.
|
||||
local d = 4 * iScreenH / (6 * math.tan(0.5 * fFoV))
|
||||
local fdp = angCamRot:Forward():Dot(vDir)
|
||||
--fdp must be nonzero ( in other words, vDir must not be perpendicular to angCamRot:Forward() )
|
||||
--or we will get a divide by zero error when calculating vProj below.
|
||||
if fdp == 0 then return 0, 0, -1 end
|
||||
--Using linear projection, project this vector onto the plane of the slice
|
||||
local vProj = (d / fdp) * vDir
|
||||
--Dotting the projected vector onto the right and up vectors gives us screen positions relative to the center of the screen.
|
||||
--We add half-widths / half-heights to these coordinates to give us screen positions relative to the upper-left corner of the screen.
|
||||
--We have to subtract from the "up" instead of adding, since screen coordinates decrease as they go upwards.
|
||||
local x = 0.5 * iScreenW + angCamRot:Right():Dot(vProj)
|
||||
local y = 0.5 * iScreenH - angCamRot:Up():Dot(vProj)
|
||||
--Lastly we have to ensure these screen positions are actually on the screen.
|
||||
local iVisibility
|
||||
|
||||
--Simple check to see if the object is in front of the camera
|
||||
if fdp < 0 then
|
||||
iVisibility = -1
|
||||
elseif x < 0 or x > iScreenW or y < 0 or y > iScreenH then
|
||||
--We've already determined the object is in front of us, but it may be lurking just outside our field of vision.
|
||||
iVisibility = 0
|
||||
else
|
||||
iVisibility = 1
|
||||
end
|
||||
|
||||
return x, y, iVisibility
|
||||
end
|
||||
|
||||
local function LocalToWorldAngle(lang, wang)
|
||||
local lm = Matrix()
|
||||
lm:SetAngles(lang)
|
||||
local wm = Matrix()
|
||||
wm:SetAngles(wang)
|
||||
|
||||
return (wm * lm):GetAngles()
|
||||
end
|
||||
|
||||
local function WorldToLocalAngle(lang, wang)
|
||||
local lm = Matrix()
|
||||
lm:SetAngles(lang)
|
||||
local wm = Matrix()
|
||||
wm:SetAngles(wang)
|
||||
|
||||
return (wm:GetInverse() * lm):GetAngles()
|
||||
end
|
||||
|
||||
local function cursor_pos()
|
||||
local x, y = input.GetCursorPos()
|
||||
|
||||
if mctrl.grab and mctrl.grab.mouse_offset then
|
||||
x = x + mctrl.grab.mouse_offset.x
|
||||
y = y + mctrl.grab.mouse_offset.y
|
||||
end
|
||||
|
||||
return x, y
|
||||
end
|
||||
|
||||
-- pace
|
||||
do
|
||||
mctrl.target = NULL
|
||||
|
||||
function mctrl.SetTarget(part)
|
||||
part = part or NULL
|
||||
|
||||
if not part:IsValid() then
|
||||
mctrl.target = NULL
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if not part.GetDrawPosition then
|
||||
mctrl.target = NULL
|
||||
else
|
||||
mctrl.target = part
|
||||
end
|
||||
end
|
||||
|
||||
function mctrl.GetTarget()
|
||||
return mctrl.target:IsValid() and not mctrl.target:IsHidden() and mctrl.target or NULL
|
||||
end
|
||||
|
||||
function mctrl.GetAxes(ang)
|
||||
return ang:Forward(), ang:Right() * -1, ang:Up()
|
||||
end
|
||||
|
||||
function mctrl.GetWorldPosition()
|
||||
local part = mctrl.GetTarget()
|
||||
if not part:IsValid() then return end
|
||||
local m = part:GetWorldMatrixWithoutOffsets()
|
||||
|
||||
return m:GetTranslation(), m:GetAngles()
|
||||
end
|
||||
|
||||
function mctrl.GetWorldMatrix()
|
||||
local part = mctrl.GetTarget()
|
||||
if not part:IsValid() then return end
|
||||
|
||||
return part:GetWorldMatrixWithoutOffsets()
|
||||
end
|
||||
|
||||
function mctrl.WorldToLocalPosition(pos, ang)
|
||||
local part = mctrl.GetTarget()
|
||||
if not part:IsValid() then return end
|
||||
local wpos, wang = part:GetBonePosition()
|
||||
if wpos and wang then return WorldToLocal(pos, ang, wpos, wang) end
|
||||
end
|
||||
|
||||
function mctrl.GetCameraFOV()
|
||||
if pace.editing_viewmodel or pace.editing_hands then return pac.LocalPlayer:GetActiveWeapon().ViewModelFOV or 55 end
|
||||
|
||||
return pace.GetViewFOV()
|
||||
end
|
||||
|
||||
function mctrl.VecToScreen(vec)
|
||||
local x, y, vis = VectorToLPCameraScreen((vec - EyePos()):GetNormalized(), ScrW(), ScrH(), EyeAngles(), math.rad(mctrl.GetCameraFOV()))
|
||||
|
||||
return {
|
||||
x = x - 1,
|
||||
y = y - 1,
|
||||
visible = vis == 1
|
||||
}
|
||||
end
|
||||
|
||||
function mctrl.ScreenToVec(x, y)
|
||||
local vec = LPCameraScreenToVector(x, y, ScrW(), ScrH(), EyeAngles(), math.rad(mctrl.GetCameraFOV()))
|
||||
|
||||
return vec
|
||||
end
|
||||
|
||||
function mctrl.GetGizmoSize()
|
||||
local part = pace.current_part
|
||||
if pace.editing_viewmodel or pace.editing_hands then return 5 end
|
||||
|
||||
if part.ClassName == "clip" or part.ClassName == "clip2" then
|
||||
part = part.Parent
|
||||
end
|
||||
|
||||
if part.ClassName == "camera" then return 30 end
|
||||
if part.ClassName == "group" then return 45 end
|
||||
if not part:IsValid() or not part.GetWorldPosition then return 3 end
|
||||
local dist = (part:GetWorldMatrixWithoutOffsets():GetTranslation():Distance(pace.GetViewPos()) / 50)
|
||||
|
||||
if dist > 1 then
|
||||
dist = 1 / dist
|
||||
end
|
||||
|
||||
return 5 * math.rad(pace.GetViewFOV()) / dist
|
||||
end
|
||||
end
|
||||
|
||||
function mctrl.LinePlaneIntersection(pos, normal, x, y)
|
||||
local n = normal
|
||||
local lp = pace.GetViewPos() - pos
|
||||
local ln = mctrl.ScreenToVec(x, y)
|
||||
|
||||
return lp + ln * (-lp:Dot(n) / ln:Dot(n))
|
||||
end
|
||||
|
||||
local function dot2D(x1, y1, x2, y2)
|
||||
return x1 * x2 + y1 * y2
|
||||
end
|
||||
|
||||
function mctrl.PointToAxis(pos, axis)
|
||||
local x, y = cursor_pos()
|
||||
local origin = mctrl.VecToScreen(pos)
|
||||
local point = mctrl.VecToScreen(pos + axis * 10)
|
||||
local a = math.atan2(point.y - origin.y, point.x - origin.x)
|
||||
local d = dot2D(math.cos(a), math.sin(a), point.x - x, point.y - y)
|
||||
|
||||
return point.x + math.cos(a) * -d, point.y + math.sin(a) * -d
|
||||
end
|
||||
|
||||
function mctrl.CalculateMovement()
|
||||
local part = mctrl.GetTarget()
|
||||
if not part:IsValid() then return end
|
||||
local axis = mctrl.grab.axis
|
||||
local offset = mctrl.GetGizmoSize()
|
||||
local m = mctrl.grab.matrix --part:GetWorldMatrixWithoutOffsets()
|
||||
local pos = m:GetTranslation()
|
||||
local forward, right, up = m:GetForward(), -m:GetRight(), m:GetUp()
|
||||
local world_dir
|
||||
|
||||
if axis == "x" then
|
||||
local localpos = mctrl.LinePlaneIntersection(pos, right, mctrl.PointToAxis(pos, forward))
|
||||
world_dir = (localpos:Dot(forward) - offset) * forward
|
||||
elseif axis == "y" then
|
||||
local localpos = mctrl.LinePlaneIntersection(pos, forward, mctrl.PointToAxis(pos, right))
|
||||
world_dir = (localpos:Dot(right) - offset) * right
|
||||
elseif axis == "z" then
|
||||
local localpos = mctrl.LinePlaneIntersection(pos, forward, mctrl.PointToAxis(pos, up))
|
||||
world_dir = (localpos:Dot(up) - offset) * up
|
||||
elseif axis == "view" then
|
||||
world_dir = mctrl.LinePlaneIntersection(pos, pace.GetViewAngles():Forward(), cursor_pos())
|
||||
end
|
||||
|
||||
if world_dir then
|
||||
local m2 = Matrix()
|
||||
m2:SetTranslation(m:GetTranslation() + world_dir)
|
||||
local wm = mctrl.grab.bone_matrix:GetInverse() * m2
|
||||
local pos = wm:GetTranslation()
|
||||
|
||||
if input.IsKeyDown(KEY_LCONTROL) then
|
||||
local num = cvar_pos_grid:GetInt("pac_grid_pos_size")
|
||||
pos.x = math.Round(pos.x / num) * num
|
||||
pos.y = math.Round(pos.y / num) * num
|
||||
pos.z = math.Round(pos.z / num) * num
|
||||
end
|
||||
|
||||
pace.Call("VariableChanged", part, "Position", pos, 0.25)
|
||||
|
||||
timer.Create("pace_refresh_properties", 0.1, 1, function()
|
||||
pace.PopulateProperties(part)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function mctrl.CalculateRotation()
|
||||
local part = mctrl.GetTarget()
|
||||
if not part:IsValid() then return end
|
||||
local axis = mctrl.grab.axis
|
||||
local ang = mctrl.grab.matrix:GetAngles() --part:GetWorldMatrixWithoutOffsets():GetAngles()
|
||||
local world_angle
|
||||
|
||||
if axis == "x" then
|
||||
local plane_pos = util.IntersectRayWithPlane(EyePos(), mctrl.ScreenToVec(cursor_pos()), mctrl.grab.matrix:GetTranslation(), mctrl.grab.matrix:GetRight())
|
||||
if not plane_pos then return end
|
||||
local diff_angle = (plane_pos - mctrl.grab.matrix:GetTranslation()):Angle()
|
||||
local local_angle = WorldToLocalAngle(diff_angle, ang)
|
||||
local p = local_angle.p
|
||||
|
||||
if math.abs(local_angle.y) > 90 then
|
||||
p = -p + 180
|
||||
end
|
||||
|
||||
p = math.NormalizeAngle(p)
|
||||
world_angle = LocalToWorldAngle(Angle(p, 0, 0), ang)
|
||||
elseif axis == "y" then
|
||||
local plane_pos = util.IntersectRayWithPlane(EyePos(), mctrl.ScreenToVec(cursor_pos()), mctrl.grab.matrix:GetTranslation(), mctrl.grab.matrix:GetUp())
|
||||
if not plane_pos then return end
|
||||
local diff_angle = (plane_pos - mctrl.grab.matrix:GetTranslation()):Angle()
|
||||
local local_angle = WorldToLocalAngle(diff_angle, ang)
|
||||
world_angle = LocalToWorldAngle(Angle(0, local_angle.y - 90, 0), ang)
|
||||
elseif axis == "z" then
|
||||
local plane_pos = util.IntersectRayWithPlane(EyePos(), mctrl.ScreenToVec(cursor_pos()), mctrl.grab.matrix:GetTranslation(), mctrl.grab.matrix:GetForward())
|
||||
if not plane_pos then return end
|
||||
local diff_angle = (plane_pos - mctrl.grab.matrix:GetTranslation()):Angle()
|
||||
diff_angle:RotateAroundAxis(mctrl.grab.matrix:GetForward(), 90)
|
||||
local local_angle = WorldToLocalAngle(diff_angle, ang)
|
||||
local p = local_angle.p
|
||||
|
||||
if local_angle.y > 0 then
|
||||
p = -p + 180
|
||||
end
|
||||
|
||||
p = math.NormalizeAngle(p)
|
||||
world_angle = LocalToWorldAngle(Angle(0, 0, p), ang)
|
||||
end
|
||||
|
||||
if world_angle then
|
||||
local ang = WorldToLocalAngle(world_angle, mctrl.grab.bone_matrix:GetAngles())
|
||||
|
||||
if input.IsKeyDown(KEY_LCONTROL) then
|
||||
local num = cvar_ang_grid:GetInt("pac_grid_ang_size")
|
||||
ang.p = math.Round(ang.p / num) * num
|
||||
ang.y = math.Round(ang.y / num) * num
|
||||
ang.r = math.Round(ang.r / num) * num
|
||||
end
|
||||
|
||||
pace.Call("VariableChanged", part, "Angles", ang, 0.25)
|
||||
|
||||
timer.Create("pace_refresh_properties", 0.1, 1, function()
|
||||
pace.PopulateProperties(part)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
mctrl.grab = {
|
||||
mode = nil,
|
||||
axis = nil
|
||||
}
|
||||
|
||||
local GRAB_AND_CLONE = CreateClientConVar("pac_grab_clone", "1", true, false, "Holding shift when moving or rotating a part creates its clone")
|
||||
|
||||
function mctrl.GUIMousePressed(mc)
|
||||
if mc ~= MOUSE_LEFT then return end
|
||||
local target = mctrl.GetTarget()
|
||||
if not target:IsValid() then return end
|
||||
local x, y = input.GetCursorPos()
|
||||
local pos, ang = mctrl.GetWorldPosition()
|
||||
if not pos or not ang then return end
|
||||
local forward, right, up = mctrl.GetAxes(ang)
|
||||
local r = mctrl.GetGizmoSize()
|
||||
|
||||
-- Movement
|
||||
do
|
||||
local axis
|
||||
|
||||
for i, v in pairs({
|
||||
x = mctrl.VecToScreen(pos + forward * r),
|
||||
y = mctrl.VecToScreen(pos + right * r),
|
||||
z = mctrl.VecToScreen(pos + up * r),
|
||||
view = mctrl.VecToScreen(pos)
|
||||
}) do
|
||||
local d = math.sqrt((v.x - x) ^ 2 + (v.y - y) ^ 2)
|
||||
|
||||
if d <= mctrl.grab_dist then
|
||||
axis = {
|
||||
axis = i,
|
||||
pos = v
|
||||
}
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if axis then
|
||||
mctrl.grab = {}
|
||||
mctrl.grab.mode = "move"
|
||||
mctrl.grab.axis = axis.axis
|
||||
local x, y = input.GetCursorPos()
|
||||
|
||||
mctrl.grab.mouse_offset = {
|
||||
x = math.ceil(axis.pos.x - x),
|
||||
y = math.ceil(axis.pos.y - y),
|
||||
}
|
||||
|
||||
mctrl.grab.matrix = target:GetWorldMatrixWithoutOffsets() * Matrix()
|
||||
mctrl.grab.bone_matrix = target:GetBoneMatrix() * Matrix()
|
||||
|
||||
if GRAB_AND_CLONE:GetBool() and input.IsShiftDown() then
|
||||
local copy = target:Clone()
|
||||
copy:SetParent(copy:GetParent())
|
||||
end
|
||||
|
||||
pace.RecordUndoHistory()
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Rotation
|
||||
do
|
||||
local axis
|
||||
|
||||
for i, v in pairs({
|
||||
x = mctrl.VecToScreen(pos + forward * r * mctrl.angle_pos),
|
||||
y = mctrl.VecToScreen(pos + right * r * mctrl.angle_pos),
|
||||
z = mctrl.VecToScreen(pos + up * r * mctrl.angle_pos)
|
||||
}) do
|
||||
local d = math.sqrt((v.x - x) ^ 2 + (v.y - y) ^ 2)
|
||||
|
||||
if d <= mctrl.grab_dist then
|
||||
axis = {
|
||||
axis = i,
|
||||
pos = v
|
||||
}
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if axis then
|
||||
mctrl.grab = {}
|
||||
mctrl.grab.mode = "rotate"
|
||||
mctrl.grab.axis = axis.axis
|
||||
local x, y = input.GetCursorPos()
|
||||
|
||||
mctrl.grab.mouse_offset = {
|
||||
x = math.ceil(axis.pos.x - x) + 0.5,
|
||||
y = math.ceil(axis.pos.y - y) + 0.5,
|
||||
}
|
||||
|
||||
mctrl.grab.matrix = target:GetWorldMatrixWithoutOffsets() * Matrix()
|
||||
mctrl.grab.bone_matrix = target:GetBoneMatrix() * Matrix()
|
||||
mctrl.grab.dist = dist
|
||||
|
||||
if GRAB_AND_CLONE:GetBool() and input.IsShiftDown() then
|
||||
local copy = target:Clone()
|
||||
copy:SetParent(copy:GetParent())
|
||||
end
|
||||
|
||||
pace.RecordUndoHistory()
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mctrl.GUIMouseReleased(mc)
|
||||
if mc == MOUSE_LEFT then
|
||||
mctrl.grab = nil
|
||||
end
|
||||
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
local white = surface.GetTextureID("gui/center_gradient.vtf")
|
||||
|
||||
local function DrawLineEx(x1, y1, x2, y2, w, skip_tex)
|
||||
w = w or 1
|
||||
|
||||
if not skip_tex then
|
||||
surface.SetTexture(white)
|
||||
end
|
||||
|
||||
local dx, dy = x1 - x2, y1 - y2
|
||||
local ang = math.atan2(dx, dy)
|
||||
local dst = math.sqrt((dx * dx) + (dy * dy))
|
||||
x1 = x1 - dx * 0.5
|
||||
y1 = y1 - dy * 0.5
|
||||
surface.DrawTexturedRectRotated(x1, y1, w, dst, math.deg(ang))
|
||||
end
|
||||
|
||||
local function DrawLine(x, y, a, b)
|
||||
DrawLineEx(x, y, a, b, 3)
|
||||
end
|
||||
|
||||
local function DrawCircleEx(x, y, rad, res, ...)
|
||||
res = res or 16
|
||||
local spacing = (res / rad) - 0.1
|
||||
|
||||
for i = 0, res do
|
||||
local i1 = ((i + 0) / res) * math.pi * 2
|
||||
local i2 = ((i + 1 + spacing) / res) * math.pi * 2
|
||||
DrawLineEx(x + math.sin(i1) * rad, y + math.cos(i1) * rad, x + math.sin(i2) * rad, y + math.cos(i2) * rad, ...)
|
||||
end
|
||||
end
|
||||
|
||||
function mctrl.LineToBox(origin, point, siz)
|
||||
siz = siz or 7
|
||||
DrawLine(origin.x, origin.y, point.x, point.y)
|
||||
DrawCircleEx(point.x, point.y, siz, 32, 2)
|
||||
end
|
||||
|
||||
function mctrl.RotationLines(pos, dir, dir2, r)
|
||||
local pr = mctrl.VecToScreen(pos + dir * r * mctrl.angle_pos)
|
||||
local pra = mctrl.VecToScreen(pos + dir * r * (mctrl.angle_pos * 0.9) + dir2 * r * 0.08)
|
||||
local prb = mctrl.VecToScreen(pos + dir * r * (mctrl.angle_pos * 0.9) + dir2 * r * -0.08)
|
||||
DrawLine(pr.x, pr.y, pra.x, pra.y)
|
||||
DrawLine(pr.x, pr.y, prb.x, prb.y)
|
||||
end
|
||||
|
||||
function mctrl.HUDPaint()
|
||||
mctrl.LastThinkCall = FrameNumber()
|
||||
if pace.IsSelecting then return end
|
||||
local target = mctrl.GetTarget()
|
||||
if not target then return end
|
||||
local pos, ang = mctrl.GetWorldPosition()
|
||||
if not pos or not ang then return end
|
||||
local forward, right, up = mctrl.GetAxes(ang)
|
||||
local radius = mctrl.GetGizmoSize()
|
||||
local origin = mctrl.VecToScreen(pos)
|
||||
local forward_point = mctrl.VecToScreen(pos + forward * radius)
|
||||
local right_point = mctrl.VecToScreen(pos + right * radius)
|
||||
local up_point = mctrl.VecToScreen(pos + up * radius)
|
||||
|
||||
if origin.visible or forward_point.visible or right_point.visible or up_point.visible then
|
||||
if mctrl.grab and (mctrl.grab.axis == "x" or mctrl.grab.axis == "view") then
|
||||
surface.SetDrawColor(255, 200, 0, 255)
|
||||
else
|
||||
surface.SetDrawColor(255, 80, 80, 255)
|
||||
end
|
||||
|
||||
mctrl.LineToBox(origin, forward_point)
|
||||
mctrl.RotationLines(pos, forward, up, radius)
|
||||
|
||||
if mctrl.grab and (mctrl.grab.axis == "y" or mctrl.grab.axis == "view") then
|
||||
surface.SetDrawColor(255, 200, 0, 255)
|
||||
else
|
||||
surface.SetDrawColor(80, 255, 80, 255)
|
||||
end
|
||||
|
||||
mctrl.LineToBox(origin, right_point)
|
||||
mctrl.RotationLines(pos, right, forward, radius)
|
||||
|
||||
if mctrl.grab and (mctrl.grab.axis == "z" or mctrl.grab.axis == "view") then
|
||||
surface.SetDrawColor(255, 200, 0, 255)
|
||||
else
|
||||
surface.SetDrawColor(80, 80, 255, 255)
|
||||
end
|
||||
|
||||
mctrl.LineToBox(origin, up_point)
|
||||
mctrl.RotationLines(pos, up, right, radius)
|
||||
surface.SetDrawColor(255, 200, 0, 255)
|
||||
DrawCircleEx(origin.x, origin.y, 4, 32, 2)
|
||||
end
|
||||
end
|
||||
|
||||
function mctrl.Think()
|
||||
if pace.IsSelecting then return end
|
||||
if not mctrl.target:IsValid() then return end
|
||||
if not mctrl.grab then return end
|
||||
|
||||
if mctrl.grab.axis and mctrl.grab.mode == "move" then
|
||||
mctrl.CalculateMovement()
|
||||
elseif mctrl.grab.axis and mctrl.grab.mode == "rotate" then
|
||||
mctrl.CalculateRotation()
|
||||
end
|
||||
end
|
||||
|
||||
pac.AddHook("Think", "pace_mctrl_Think", mctrl.Think)
|
||||
pace.mctrl = mctrl
|
||||
190
lua/pac3/editor/client/menu_bar.lua
Normal file
190
lua/pac3/editor/client/menu_bar.lua
Normal file
@@ -0,0 +1,190 @@
|
||||
--[[
|
||||
| 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 add_expensive_submenu_load(pnl, callback)
|
||||
local old = pnl.OnCursorEntered
|
||||
pnl.OnCursorEntered = function(...)
|
||||
callback()
|
||||
pnl.OnCursorEntered = old
|
||||
return old(...)
|
||||
end
|
||||
end
|
||||
|
||||
local function populate_pac(menu)
|
||||
do
|
||||
local menu, icon = menu:AddSubMenu(L"save", function() pace.SaveParts() end)
|
||||
menu:SetDeleteSelf(false)
|
||||
icon:SetImage(pace.MiscIcons.save)
|
||||
add_expensive_submenu_load(icon, function() pace.AddSaveMenuToMenu(menu) end)
|
||||
end
|
||||
|
||||
do
|
||||
local menu, icon = menu:AddSubMenu(L"load", function() pace.LoadParts(nil, true) end)
|
||||
menu:SetDeleteSelf(false)
|
||||
icon:SetImage(pace.MiscIcons.load)
|
||||
add_expensive_submenu_load(icon, function() pace.AddSavedPartsToMenu(menu, true) end)
|
||||
end
|
||||
|
||||
do
|
||||
local menu, icon = menu:AddSubMenu(L"wear", function() pace.WearParts() end)
|
||||
menu:SetDeleteSelf(false)
|
||||
icon:SetImage(pace.MiscIcons.wear)
|
||||
|
||||
pace.PopulateWearMenu(menu)
|
||||
end
|
||||
|
||||
do
|
||||
menu:AddOption(L"request", function() RunConsoleCommand("pac_request_outfits") pac.Message('Requesting outfits.') end):SetImage(pace.MiscIcons.replace)
|
||||
end
|
||||
|
||||
do
|
||||
local menu, icon = menu:AddSubMenu(L"clear", function() end)
|
||||
icon:SetImage(pace.MiscIcons.clear)
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
menu:AddOption(L"OK", function() pace.ClearParts() end):SetImage(pace.MiscIcons.clear)
|
||||
end
|
||||
|
||||
menu:AddSpacer()
|
||||
|
||||
do
|
||||
local help, help_pnl = menu:AddSubMenu(L"help", function() pace.ShowWiki() end)
|
||||
help.GetDeleteSelf = function() return false end
|
||||
help_pnl:SetImage(pace.MiscIcons.help)
|
||||
|
||||
help:AddOption(
|
||||
L"Getting Started",
|
||||
function() pace.ShowWiki(pace.WikiURL .. "Beginners-FAQ") end
|
||||
):SetImage(pace.MiscIcons.info)
|
||||
|
||||
do
|
||||
local chat_pnl = help:AddOption(
|
||||
L"Discord / PAC3 Chat",
|
||||
function() gui.OpenURL("https://discord.gg/utpR3gJ") cookie.Set("pac3_discord_ad", 3) end
|
||||
) chat_pnl:SetImage(pace.MiscIcons.chat)
|
||||
|
||||
if cookie.GetNumber("pac3_discord_ad", 0) < 3 then
|
||||
help_pnl.PaintOver = function(_,w,h) surface.SetDrawColor(255,255,0,50 + math.sin(SysTime()*20)*20) surface.DrawRect(0,0,w,h) end
|
||||
chat_pnl.PaintOver = help_pnl.PaintOver
|
||||
cookie.Set("pac3_discord_ad", cookie.GetNumber("pac3_discord_ad", 0) + 1)
|
||||
end
|
||||
end
|
||||
|
||||
local version_string = _G.PAC_VERSION and PAC_VERSION()
|
||||
if version_string then
|
||||
local version, version_pnl = help:AddSubMenu(L"Version", function() pace.ShowWiki() end)
|
||||
version.GetDeleteSelf = function() return false end
|
||||
version_pnl:SetImage(pace.MiscIcons.info)
|
||||
|
||||
version:AddOption(version_string)
|
||||
end
|
||||
|
||||
help:AddOption(
|
||||
L"about",
|
||||
function() pace.ShowAbout() end
|
||||
):SetImage(pace.MiscIcons.about)
|
||||
end
|
||||
|
||||
do
|
||||
menu:AddOption(L"exit", function() pace.CloseEditor() end):SetImage(pace.MiscIcons.exit)
|
||||
end
|
||||
end
|
||||
|
||||
local function populate_view(menu)
|
||||
menu:AddOption(L"hide editor",
|
||||
function() pace.Call("ToggleFocus") chat.AddText("[PAC3] \"ctrl + e\" to get the editor back")
|
||||
end):SetImage("icon16/application_delete.png")
|
||||
|
||||
menu:AddCVar(L"camera follow: "..GetConVar("pac_camera_follow_entity"):GetInt(), "pac_camera_follow_entity", "1", "0"):SetImage("icon16/camera_go.png")
|
||||
menu:AddCVar(L"enable editor camera: "..GetConVar("pac_enable_editor_view"):GetInt(), "pac_enable_editor_view", "1", "0"):SetImage("icon16/camera.png")
|
||||
menu:AddOption(L"reset view position", function() pace.ResetView() end):SetImage("icon16/camera_link.png")
|
||||
menu:AddOption(L"reset zoom", function() pace.ResetZoom() end):SetImage("icon16/magnifier.png")
|
||||
end
|
||||
|
||||
local function populate_options(menu)
|
||||
menu:AddOption(L"settings", function() pace.OpenSettings() end)
|
||||
menu:AddCVar(L"inverse collapse/expand controls", "pac_reverse_collapse", "1", "0")
|
||||
menu:AddCVar(L"enable shift+move/rotate clone", "pac_grab_clone", "1", "0")
|
||||
menu:AddCVar(L"remember editor position", "pac_editor_remember_position", "1", "0")
|
||||
menu:AddCVar(L"show parts IDs", "pac_show_uniqueid", "1", "0")
|
||||
menu:AddSpacer()
|
||||
menu:AddOption(L"position grid size", function()
|
||||
Derma_StringRequest(L"position grid size", L"size in units:", GetConVarNumber("pac_grid_pos_size"), function(val)
|
||||
RunConsoleCommand("pac_grid_pos_size", val)
|
||||
end)
|
||||
end)
|
||||
menu:AddOption(L"angles grid size", function()
|
||||
Derma_StringRequest(L"angles grid size", L"size in degrees:", GetConVarNumber("pac_grid_ang_size"), function(val)
|
||||
RunConsoleCommand("pac_grid_ang_size", val)
|
||||
end)
|
||||
end)
|
||||
menu:AddCVar(L"render attachments as bones", "pac_render_attachments", "1", "0").DoClick = function() pace.ToggleRenderAttachments() end
|
||||
menu:AddSpacer()
|
||||
|
||||
menu:AddCVar(L"automatic property size", "pac_auto_size_properties", "1", "0")
|
||||
menu:AddCVar(L"enable language identifier in text fields", "pac_editor_languageid", "1", "0")
|
||||
pace.AddLanguagesToMenu(menu)
|
||||
pace.AddFontsToMenu(menu)
|
||||
|
||||
menu:AddSpacer()
|
||||
|
||||
local rendering, pnl = menu:AddSubMenu(L"rendering", function() end)
|
||||
rendering.GetDeleteSelf = function() return false end
|
||||
pnl:SetImage("icon16/camera_edit.png")
|
||||
rendering:AddCVar(L"no outfit reflections", "pac_optimization_render_once_per_frame", "1", "0")
|
||||
end
|
||||
|
||||
local function populate_player(menu)
|
||||
local pnl = menu:AddOption(L"t pose", function() pace.SetTPose(not pace.GetTPose()) end):SetImage("icon16/user_go.png")
|
||||
menu:AddOption(L"reset eye angles", function() pace.ResetEyeAngles() end):SetImage("icon16/user_delete.png")
|
||||
menu:AddOption(L"reset zoom", function() pace.ResetZoom() end):SetImage("icon16/magnifier.png")
|
||||
|
||||
-- this should be in pacx but it's kinda stupid to add a hook just to populate the player menu
|
||||
-- make it more generic
|
||||
if pacx and pacx.GetServerModifiers then
|
||||
local mods, pnl = menu:AddSubMenu(L"modifiers", function() end)
|
||||
pnl:SetImage("icon16/user_edit.png")
|
||||
mods.GetDeleteSelf = function() return false end
|
||||
for name in pairs(pacx.GetServerModifiers()) do
|
||||
mods:AddCVar(L(name), "pac_modifier_" .. name, "1", "0")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.OnMenuBarPopulate(bar)
|
||||
for k,v in pairs(bar.Menus) do
|
||||
v:Remove()
|
||||
end
|
||||
|
||||
populate_pac(bar:AddMenu("pac"))
|
||||
populate_view(bar:AddMenu(L"view"))
|
||||
populate_options(bar:AddMenu(L"options"))
|
||||
populate_player(bar:AddMenu(L"player"))
|
||||
pace.AddToolsToMenu(bar:AddMenu(L"tools"))
|
||||
|
||||
bar:RequestFocus(true)
|
||||
end
|
||||
|
||||
function pace.OnOpenMenu()
|
||||
local menu = DermaMenu()
|
||||
menu:SetPos(input.GetCursorPos())
|
||||
|
||||
populate_player(menu) menu:AddSpacer()
|
||||
populate_view(menu) menu:AddSpacer()
|
||||
populate_options(menu) menu:AddSpacer()
|
||||
populate_pac(menu) menu:AddSpacer()
|
||||
|
||||
local menu, pnl = menu:AddSubMenu(L"tools")
|
||||
pnl:SetImage("icon16/plugin.png")
|
||||
pace.AddToolsToMenu(menu)
|
||||
|
||||
menu:MakePopup()
|
||||
end
|
||||
60
lua/pac3/editor/client/panels.lua
Normal file
60
lua/pac3/editor/client/panels.lua
Normal file
@@ -0,0 +1,60 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
pace.RegisteredPanels = {}
|
||||
local TRACK_UNUSED_PANELS = false
|
||||
|
||||
function pace.RegisterPanel(PANEL)
|
||||
pace.RegisteredPanels[PANEL.ClassName] = PANEL
|
||||
vgui.Register("pace_" .. PANEL.ClassName, PANEL, PANEL.Base)
|
||||
end
|
||||
|
||||
function pace.PanelExists(class_name)
|
||||
return pace.GetRegisteredPanel(class_name) ~= nil
|
||||
end
|
||||
|
||||
function pace.GetRegisteredPanel(class_name)
|
||||
return pace.RegisteredPanels[class_name]
|
||||
end
|
||||
|
||||
function pace.CreatePanel(class_name, parent)
|
||||
local pnl = vgui.Create("pace_" .. class_name, parent)
|
||||
table.insert(pace.ActivePanels, pnl)
|
||||
|
||||
if TRACK_UNUSED_PANELS and class_name ~= 'editor' then
|
||||
local debugTrace = debug.traceback()
|
||||
|
||||
timer.Simple(0, function()
|
||||
if not IsValid(pnl) then return end
|
||||
local parent = pnl:GetParent()
|
||||
if not IsValid(parent) or parent:GetClassName() == 'CGModBase' then
|
||||
pac.Message('Panel was created without valid parent! ' .. class_name)
|
||||
pac.Message(debugTrace)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return pnl
|
||||
end
|
||||
|
||||
function pace.RegisterPanels()
|
||||
local files
|
||||
|
||||
if file.FindInLua then
|
||||
files = file.FindInLua("pac3/editor/client/panels/*.lua")
|
||||
else
|
||||
files = file.Find("pac3/editor/client/panels/*.lua", "LUA")
|
||||
end
|
||||
|
||||
for _, name in pairs(files) do
|
||||
include("pac3/editor/client/panels/" .. name)
|
||||
end
|
||||
end
|
||||
86
lua/pac3/editor/client/panels/browser.lua
Normal file
86
lua/pac3/editor/client/panels/browser.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
--[[
|
||||
| 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 PANEL = {}
|
||||
|
||||
PANEL.ClassName = "browser"
|
||||
PANEL.Base = "DListView"
|
||||
|
||||
PANEL.Dir = ""
|
||||
AccessorFunc(PANEL, "Dir", "Dir")
|
||||
|
||||
function PANEL:SetDir(str)
|
||||
self.Dir = str
|
||||
self:PopulateFromClient()
|
||||
end
|
||||
|
||||
function PANEL:Init()
|
||||
self:AddColumn(L"name")
|
||||
self:AddColumn(L"size")
|
||||
self:AddColumn(L"modified")
|
||||
self:PopulateFromClient()
|
||||
self:FixColumnsLayout()
|
||||
end
|
||||
|
||||
local function OnMousePressed(self, mcode)
|
||||
if mcode == MOUSE_RIGHT then
|
||||
self:GetListView():OnRowRightClick(self:GetID(), self)
|
||||
elseif mcode == MOUSE_LEFT then
|
||||
self:GetListView():OnClickLine(self, true)
|
||||
self:OnSelect()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:AddOutfits(folder, callback)
|
||||
for i, name in pairs(file.Find(folder.."*", "DATA")) do
|
||||
if name:find("%.txt") then
|
||||
local outfit = folder .. name
|
||||
if file.Exists(outfit, "DATA") then
|
||||
local filenode = self:AddLine(
|
||||
name:gsub("%.txt", ""),
|
||||
string.NiceSize(file.Size(outfit, "DATA")),
|
||||
os.date("%m/%d/%Y %H:%M", file.Time(outfit, "DATA"))
|
||||
)
|
||||
filenode.FileName = name
|
||||
filenode.OnSelect = callback
|
||||
filenode.OnMousePressed = OnMousePressed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PopulateFromClient()
|
||||
self:Clear()
|
||||
|
||||
self:AddOutfits("pac3/" .. self.Dir, function(node)
|
||||
pace.LoadParts(self.Dir .. node.FileName, true)
|
||||
pace.RefreshTree()
|
||||
end)
|
||||
end
|
||||
|
||||
function PANEL.OnRowRightClick(_self,id, self)
|
||||
local m=DermaMenu()
|
||||
m:AddOption(L"View",function()
|
||||
self:GetListView():OnClickLine(self, true)
|
||||
self:OnSelect()
|
||||
end)
|
||||
m:AddOption(L"wear on server",function()
|
||||
self:GetListView():OnClickLine(self, true)
|
||||
self:OnSelect()
|
||||
timer.Simple(0,function()
|
||||
RunConsoleCommand"pac_wear_parts"
|
||||
end)
|
||||
end)
|
||||
|
||||
m:Open()
|
||||
end
|
||||
|
||||
pace.RegisterPanel(PANEL)
|
||||
494
lua/pac3/editor/client/panels/editor.lua
Normal file
494
lua/pac3/editor/client/panels/editor.lua
Normal file
@@ -0,0 +1,494 @@
|
||||
--[[
|
||||
| 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 PANEL = {}
|
||||
|
||||
PANEL.ClassName = "editor"
|
||||
PANEL.Base = "DFrame"
|
||||
PANEL.menu_bar = NULL
|
||||
|
||||
PANEL.pac3_PanelsToRemove = {
|
||||
'btnMaxim', 'btnMinim'
|
||||
}
|
||||
|
||||
local BAR_SIZE = 17
|
||||
local RENDERSCORE_SIZE = 20
|
||||
|
||||
local use_tabs = CreateClientConVar("pac_property_tabs", 1, true)
|
||||
|
||||
local zoom_persistent = CreateClientConVar("pac_zoom_persistent", 0, true, false, 'Keep zoom between sessions.')
|
||||
local zoom_mousewheel = CreateClientConVar("pac_zoom_mousewheel", 0, true, false, 'Enable zooming with mouse wheel.')
|
||||
local zoom_smooth = CreateClientConVar("pac_zoom_smooth", 0, true, false, 'Enable smooth zooming.')
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetTitle("")
|
||||
self:SetSizable(true)
|
||||
--self:DockPadding(2, 23, 2, 2)
|
||||
|
||||
surface.SetFont(pace.CurrentFont)
|
||||
local _, h = surface.GetTextSize("|")
|
||||
RENDERSCORE_SIZE = h + 1
|
||||
|
||||
local div = vgui.Create("DVerticalDivider", self)
|
||||
|
||||
div:SetDividerHeight(RENDERSCORE_SIZE)
|
||||
div:Dock(FILL)
|
||||
div:SetTopMin(40)
|
||||
div:SetBottomMin(40)
|
||||
div:SetCookieName("pac3_editor")
|
||||
div:SetTopHeight(ScrH() / 1.4)
|
||||
div:LoadCookies()
|
||||
|
||||
self.div = div
|
||||
|
||||
self.treePanel = pace.CreatePanel("tree")
|
||||
self:SetTop(self.treePanel)
|
||||
|
||||
local pnl = pace.CreatePanel("properties", div)
|
||||
pace.properties = pnl
|
||||
|
||||
self.exit_button = vgui.Create("DButton")
|
||||
self.exit_button:SetText("")
|
||||
self.exit_button.DoClick = function() self:Close() end
|
||||
self.exit_button.Paint = function(self, w, h) derma.SkinHook("Paint", "WindowCloseButton", self, w, h) end
|
||||
self.exit_button:SetSize(31, 26)
|
||||
|
||||
self.zoomframe = vgui.Create( "DPanel" )
|
||||
self.zoomframe:SetSize( 180, 150 )
|
||||
|
||||
self.zoomsettings = vgui.Create("DPanel", self.zoomframe)
|
||||
self.zoomsettings:Dock(TOP)
|
||||
self.zoomsettings:DockPadding(4,0,4,4)
|
||||
|
||||
local SETTING_MARGIN_TOP = 6
|
||||
self.persistcheckbox = vgui.Create("DCheckBoxLabel", self.zoomsettings)
|
||||
self.persistcheckbox:SetText("Persistent camera FOV")
|
||||
self.persistcheckbox:Dock(TOP)
|
||||
self.persistcheckbox:SetDark(true)
|
||||
self.persistcheckbox:DockMargin(0,SETTING_MARGIN_TOP,0,0)
|
||||
self.persistcheckbox:SetConVar("pac_zoom_persistent")
|
||||
|
||||
self.persistlabel = vgui.Create("DLabel", self.zoomsettings)
|
||||
self.persistlabel:Dock(TOP)
|
||||
self.persistlabel:SetDark(true)
|
||||
self.persistlabel:SetText("Keep the zoom when reopening the editor.")
|
||||
self.persistlabel:SetWrap(true)
|
||||
self.persistlabel:SetAutoStretchVertical(true)
|
||||
|
||||
self.mwheelcheckbox = vgui.Create("DCheckBoxLabel", self.zoomsettings)
|
||||
self.mwheelcheckbox:SetText("Enable mouse wheel")
|
||||
self.mwheelcheckbox:Dock(TOP)
|
||||
self.mwheelcheckbox:SetDark(true)
|
||||
self.mwheelcheckbox:DockMargin(0,SETTING_MARGIN_TOP,0,0)
|
||||
self.mwheelcheckbox:SetConVar("pac_zoom_mousewheel")
|
||||
|
||||
self.mwheellabel = vgui.Create("DLabel", self.zoomsettings)
|
||||
self.mwheellabel:Dock(TOP)
|
||||
self.mwheellabel:SetDark(true)
|
||||
self.mwheellabel:SetText("Enable zooming with mouse wheel.\n+CTRL: Precise\n+SHIFT: Fast")
|
||||
self.mwheellabel:SetWrap(true)
|
||||
self.mwheellabel:SetAutoStretchVertical(true)
|
||||
|
||||
self.smoothcheckbox = vgui.Create("DCheckBoxLabel", self.zoomsettings)
|
||||
self.smoothcheckbox:SetText("Smooth zooming")
|
||||
self.smoothcheckbox:Dock(TOP)
|
||||
self.smoothcheckbox:SetDark(true)
|
||||
self.smoothcheckbox:DockMargin(0,SETTING_MARGIN_TOP,0,0)
|
||||
self.smoothcheckbox:SetConVar("pac_zoom_smooth")
|
||||
|
||||
self.smoothlabel = vgui.Create("DLabel", self.zoomsettings)
|
||||
self.smoothlabel:Dock(TOP)
|
||||
self.smoothlabel:SetDark(true)
|
||||
self.smoothlabel:SetText("Enable smooth zooming.")
|
||||
self.smoothlabel:SetWrap(true)
|
||||
self.smoothlabel:SetAutoStretchVertical(true)
|
||||
|
||||
self.sliderpanel = vgui.Create("DPanel", self.zoomframe)
|
||||
self.sliderpanel:SetSize(180, 20)
|
||||
self.sliderpanel:Dock(TOP)
|
||||
|
||||
self.zoomslider = vgui.Create("DNumSlider", self.sliderpanel)
|
||||
self.zoomslider:DockPadding(4,0,0,0)
|
||||
self.zoomslider:SetSize(200, 20)
|
||||
self.zoomslider:SetMin( 0 )
|
||||
self.zoomslider:SetMax( 100 )
|
||||
self.zoomslider:SetDecimals( 0 )
|
||||
self.zoomslider:SetText("Camera FOV")
|
||||
self.zoomslider:SetDark(true)
|
||||
self.zoomslider:SetDefaultValue( 75 )
|
||||
|
||||
if zoom_persistent:GetInt() == 1 then
|
||||
self.zoomslider:SetValue( pace.ViewFOV )
|
||||
else
|
||||
self.zoomslider:SetValue( 75 )
|
||||
end
|
||||
|
||||
self.btnClose.Paint = function() end
|
||||
|
||||
self:SetBottom(pnl)
|
||||
|
||||
self:SetCookieName("pac3_editor")
|
||||
self:SetPos(self:GetCookieNumber("x"), BAR_SIZE)
|
||||
|
||||
self:MakeBar()
|
||||
self.lastTopBarHover = 0
|
||||
self.rendertime_data = {}
|
||||
self.okay = true
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed()
|
||||
if self.m_bSizable and gui.MouseX() > ( self.x + self:GetWide() - 20 ) then
|
||||
self.Sizing = { gui.MouseX() - self:GetWide(), gui.MouseY() - self:GetTall() }
|
||||
self:MouseCapture( true )
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetDraggable() and gui.MouseY() < (self.y + 24) then
|
||||
self.Dragging = { gui.MouseX() - self.x, gui.MouseY() - self.y }
|
||||
self:MouseCapture( true )
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnMouseReleased(mc)
|
||||
if mc == MOUSE_RIGHT then
|
||||
self:Close()
|
||||
end
|
||||
|
||||
self.BaseClass.OnMouseReleased(self,mc)
|
||||
|
||||
end
|
||||
|
||||
function PANEL:MakeBar()
|
||||
if self.menu_bar:IsValid() then self.menu_bar:Remove() end
|
||||
|
||||
local bar = vgui.Create("DMenuBar", self)
|
||||
bar:SetSize(self:GetWide(), BAR_SIZE)
|
||||
pace.Call("MenuBarPopulate", bar)
|
||||
pace.MenuBar = bar
|
||||
|
||||
self.menu_bar = bar
|
||||
|
||||
self:DockMargin(2, 2, 2, 2)
|
||||
self:DockPadding(2, 2, 2, 2)
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
if self.menu_bar:IsValid() then
|
||||
self.menu_bar:Remove()
|
||||
end
|
||||
|
||||
if self.exit_button:IsValid() then
|
||||
self.exit_button:Remove()
|
||||
end
|
||||
|
||||
if self.zoomframe:IsValid() then
|
||||
self.zoomframe:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Think(...)
|
||||
if not self.okay then return end
|
||||
DFrame.Think(self, ...)
|
||||
|
||||
if self.Hovered and self.m_bSizable and gui.MouseX() > (self.x + self:GetWide() - 20) then
|
||||
self:SetCursor("sizewe")
|
||||
return
|
||||
end
|
||||
|
||||
for k,v in pairs(pac.GetRenderTimeInfo(pac.LocalPlayer)) do
|
||||
self.rendertime_data[k] = Lerp(0.03, self.rendertime_data[k] or 0, v)
|
||||
end
|
||||
|
||||
local bar = self.menu_bar
|
||||
|
||||
self:SetTall(ScrH())
|
||||
local w = math.max(self:GetWide(), 200)
|
||||
self:SetWide(w)
|
||||
self:SetPos(math.Clamp(self:GetPos(), 0, ScrW() - w), 0)
|
||||
|
||||
if x ~= self.last_x then
|
||||
self:SetCookie("x", x)
|
||||
self.last_x = x
|
||||
end
|
||||
|
||||
if self.exit_button:IsValid() then
|
||||
|
||||
if self:GetPos() + self:GetWide() / 2 < ScrW() / 2 then
|
||||
self.exit_button:SetPos(ScrW() - self.exit_button:GetWide() + 4, -4)
|
||||
else
|
||||
self.exit_button:SetPos(-4, -4)
|
||||
end
|
||||
end
|
||||
|
||||
if self.zoomframe:IsValid() then
|
||||
|
||||
self.zoomsettings:InvalidateLayout( true )
|
||||
self.zoomsettings:SizeToChildren( false, true )
|
||||
|
||||
self.zoomframe:InvalidateLayout( true )
|
||||
self.zoomframe:SizeToChildren( false, true )
|
||||
|
||||
if self:GetPos() + self:GetWide() / 2 < ScrW() / 2 then
|
||||
self.zoomframe:SetPos(ScrW() - self.zoomframe:GetWide(), ScrH() - self.zoomframe:GetTall())
|
||||
|
||||
else
|
||||
self.zoomframe:SetPos(0,ScrH() - self.zoomframe:GetTall())
|
||||
end
|
||||
|
||||
local x, y = self.zoomframe:GetPos()
|
||||
|
||||
if pace.timeline.IsActive() then
|
||||
self.zoomframe:SetPos(x,y-pace.timeline.frame:GetTall())
|
||||
end
|
||||
|
||||
if pace.zoom_reset then
|
||||
self.zoomslider:SetValue(75)
|
||||
pace.zoom_reset = nil
|
||||
end
|
||||
|
||||
if zoom_smooth:GetInt() == 1 then
|
||||
pace.SetZoom(self.zoomslider:GetValue(),true)
|
||||
else
|
||||
pace.SetZoom(self.zoomslider:GetValue(),false)
|
||||
end
|
||||
|
||||
local mx, my = input.GetCursorPos()
|
||||
local x, y = self.zoomframe:GetPos()
|
||||
local xs, xy = self.zoomframe:GetSize()
|
||||
|
||||
if mx > x and my > y and mx < x + xs and my < y + xy then
|
||||
self.zoomsettings:SetVisible(true)
|
||||
self.zoomsettings:RequestFocus()
|
||||
else
|
||||
self.zoomsettings:SetVisible(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local auto_size = CreateClientConVar("pac_auto_size_properties", 1, true)
|
||||
|
||||
function PANEL:PerformLayout()
|
||||
if not self.okay then return end
|
||||
|
||||
DFrame.PerformLayout(self)
|
||||
|
||||
for i, val in pairs(self.pac3_PanelsToRemove) do
|
||||
if IsValid(self[val]) then
|
||||
self[val].SetSize(self[val], 0, 0) -- Hacky
|
||||
end
|
||||
end
|
||||
|
||||
if self.old_part ~= pace.current_part then
|
||||
self.div:InvalidateLayout()
|
||||
self.bottom:PerformLayout()
|
||||
pace.properties:PerformLayout()
|
||||
self.old_part = pace.current_part
|
||||
|
||||
local sz = auto_size:GetInt()
|
||||
|
||||
if sz > 0 then
|
||||
local newh = sz > 0 and (ScrH() - math.min(pace.properties:GetHeight() + RENDERSCORE_SIZE + BAR_SIZE - 6, ScrH() / 1.5))
|
||||
|
||||
if sz >= 2 then
|
||||
local oldh = self.div:GetTopHeight()
|
||||
|
||||
if newh<oldh then
|
||||
self.div:SetTopHeight(newh)
|
||||
end
|
||||
elseif sz >= 1 then
|
||||
self.div:SetTopHeight(newh)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetTop(pnl)
|
||||
self.top = pnl
|
||||
self.div:SetTop(pnl)
|
||||
end
|
||||
|
||||
function PANEL:SetBottom(pnl)
|
||||
self.bottom = pnl
|
||||
self.div:SetBottom(pnl)
|
||||
end
|
||||
|
||||
pace.Focused = false
|
||||
|
||||
function pace.IsFocused()
|
||||
return pace.Focused
|
||||
end
|
||||
|
||||
local fade_time = 0.1
|
||||
|
||||
function pace.GainFocus(show_editor)
|
||||
local self = pace.Editor
|
||||
if self:IsValid() then
|
||||
if self.allowclick ~= false then
|
||||
self:MakePopup()
|
||||
pace.Focused = true
|
||||
|
||||
timer.Remove("pac_editor_visibility")
|
||||
|
||||
self:SetVisible(true)
|
||||
self.exit_button:SetVisible(true)
|
||||
self.zoomframe:SetVisible(true)
|
||||
|
||||
self:AlphaTo(255, fade_time, 0)
|
||||
self.exit_button:AlphaTo(255, fade_time, 0)
|
||||
self.zoomframe:AlphaTo(255, fade_time, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.KillFocus(show_editor)
|
||||
local self = pace.Editor
|
||||
if self:IsValid() then
|
||||
self:KillFocus()
|
||||
self:SetMouseInputEnabled(false)
|
||||
self:SetKeyBoardInputEnabled(false)
|
||||
gui.EnableScreenClicker(false)
|
||||
pace.Focused = false
|
||||
|
||||
if not show_editor then
|
||||
self:AlphaTo(0, fade_time, 0)
|
||||
self.exit_button:AlphaTo(0, fade_time, 0)
|
||||
self.zoomframe:AlphaTo(0, fade_time, 0)
|
||||
|
||||
timer.Create("pac_editor_visibility", fade_time, 1, function()
|
||||
self:SetVisible(false)
|
||||
self.exit_button:SetVisible(false)
|
||||
self.zoomframe:SetVisible(false)
|
||||
end)
|
||||
end
|
||||
|
||||
self.allowclick = false
|
||||
|
||||
timer.Simple(0.2, function()
|
||||
if self:IsValid() then
|
||||
self.allowclick = true
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local drawProfileInfos = 0
|
||||
local textCol, drawBox
|
||||
local boxW, boxH
|
||||
|
||||
local function drawTimeBox(text, time, x, y)
|
||||
local str = string.format("%s: %.3f ms", L(text), time)
|
||||
drawBox(x, y, boxW - 5, RENDERSCORE_SIZE - 1)
|
||||
|
||||
surface.SetTextPos(x + 5, y)
|
||||
surface.DrawText(str)
|
||||
return y + RENDERSCORE_SIZE
|
||||
end
|
||||
|
||||
local function PostRenderVGUI()
|
||||
|
||||
end
|
||||
|
||||
pac.AddHook('PostRenderVGUI', 'pac_DrawProfileInfos', PostRenderVGUI)
|
||||
|
||||
function PANEL:PaintOver(w, h)
|
||||
if not self.okay then return end
|
||||
textCol = self:GetSkin().Colours.Category.Line.Text
|
||||
local text = _G.PAC_VERSION and PAC_VERSION()
|
||||
if text then
|
||||
surface.SetFont("DermaDefault")
|
||||
local x, y = self:LocalToScreen()
|
||||
local w, h = surface.GetTextSize(text)
|
||||
x = x + self:GetWide() + 4
|
||||
y = y + self:GetTall() - 4 - h
|
||||
|
||||
local mx, my = gui.MousePos()
|
||||
local cx, cy = self:LocalToScreen(x, y)
|
||||
|
||||
local hovering = false
|
||||
DisableClipping(true)
|
||||
|
||||
if mx > x and mx < x + w and my > y and my < y + h then
|
||||
hovering = true
|
||||
text = "pac version: " .. text
|
||||
w, h = surface.GetTextSize(text)
|
||||
|
||||
surface.SetDrawColor(0,0,0,255)
|
||||
surface.DrawRect(x,y,w,h)
|
||||
end
|
||||
|
||||
surface.SetTextPos(x,y)
|
||||
surface.SetTextColor(255,255,255,hovering and 255 or 100)
|
||||
surface.DrawText(text)
|
||||
DisableClipping(false )
|
||||
end
|
||||
|
||||
local data = self.rendertime_data
|
||||
|
||||
local x = 2
|
||||
local y = 2
|
||||
y = y + self.menu_bar:GetTall()
|
||||
y = y + self.top:GetTall()
|
||||
boxW, boxH = w, h
|
||||
|
||||
surface.SetFont(pace.CurrentFont)
|
||||
|
||||
textCol = self:GetSkin().Colours.Category.Line.Text
|
||||
drawBox = self:GetSkin().tex.Menu_Strip
|
||||
surface.SetTextColor(textCol)
|
||||
cam.IgnoreZ(true)
|
||||
|
||||
local total = 0
|
||||
for k,v in pairs(data) do
|
||||
total = total + v
|
||||
end
|
||||
|
||||
local str = string.format("%s: %.3f ms", L("average render time"), total * 1000)
|
||||
drawBox(x, y, w - 5, RENDERSCORE_SIZE - 1)
|
||||
|
||||
local mx, my = input.GetCursorPos()
|
||||
local cx, cy = self:LocalToScreen(x, y)
|
||||
|
||||
if cx <= mx and cy <= my and mx <= cx + w - 5 and my <= cy + RENDERSCORE_SIZE - 1 and self:IsChildHovered() then
|
||||
surface.SetFont(pace.CurrentFont)
|
||||
surface.SetTextColor(textCol)
|
||||
|
||||
local x, y = input.GetCursorPos()
|
||||
x = x + 3
|
||||
y = y + 3
|
||||
|
||||
DisableClipping(true)
|
||||
for type, time in pairs(self.rendertime_data) do
|
||||
y = drawTimeBox(type, time * 1000, x, y)
|
||||
end
|
||||
DisableClipping(false)
|
||||
end
|
||||
|
||||
surface.SetTextPos(x + 5, y)
|
||||
surface.DrawText(str)
|
||||
cam.IgnoreZ(false)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w,h)
|
||||
if not self.okay then return end
|
||||
|
||||
|
||||
--surface.SetDrawColor(0, 0, 0, 255)
|
||||
--surface.DrawRect(0,0,w,h)
|
||||
-- there are some skins that have a transparent dframe
|
||||
-- so the categories that the properties draw will be transparent
|
||||
|
||||
self:GetSkin().tex.Tab_Control( 0, 0, w, h )
|
||||
|
||||
--DFrame.Paint(self, w,h)
|
||||
end
|
||||
|
||||
pace.RegisterPanel(PANEL)
|
||||
767
lua/pac3/editor/client/panels/extra_properties.lua
Normal file
767
lua/pac3/editor/client/panels/extra_properties.lua
Normal file
@@ -0,0 +1,767 @@
|
||||
--[[
|
||||
| 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
|
||||
110
lua/pac3/editor/client/panels/list.lua
Normal file
110
lua/pac3/editor/client/panels/list.lua
Normal file
@@ -0,0 +1,110 @@
|
||||
--[[
|
||||
| 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 function list(mid, name, store, populate)
|
||||
local list = vgui.Create("DListView", mid)
|
||||
list:AddColumn(name)
|
||||
|
||||
function list:Refresh()
|
||||
self:Clear()
|
||||
|
||||
self.data = populate()
|
||||
|
||||
for _, kv in ipairs(self.data) do
|
||||
self:AddLine(kv.name)
|
||||
end
|
||||
end
|
||||
|
||||
function list:Store()
|
||||
for _, line in ipairs(list:GetSelected()) do
|
||||
local i = line:GetID()
|
||||
local kv = self.data[i]
|
||||
if kv then
|
||||
store(kv)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function list:Populate()
|
||||
self:Clear()
|
||||
self.data = populate()
|
||||
end
|
||||
|
||||
list:Refresh()
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
return function(form, name, props)
|
||||
local pnl = vgui.Create("Panel", form)
|
||||
pnl:SetTall(200)
|
||||
|
||||
pnl.left = list(pnl, props.name_left, props.store_left, props.populate_left)
|
||||
pnl.right = list(pnl, props.name_right, props.store_right, props.populate_right)
|
||||
|
||||
local button = vgui.Create("DButton", pnl)
|
||||
button:SetText(props.empty_message)
|
||||
|
||||
local store = function() end
|
||||
local selected_side = pnl.left
|
||||
|
||||
pnl.left.OnRowSelected = function()
|
||||
pnl.right:ClearSelection()
|
||||
store = function() pnl.left:Store() end
|
||||
button:SetText("add to " .. name)
|
||||
end
|
||||
|
||||
pnl.right.OnRowSelected = function()
|
||||
pnl.left:ClearSelection()
|
||||
store = function() pnl.right:Store() end
|
||||
button:SetText("remove from " .. name)
|
||||
end
|
||||
|
||||
local function select()
|
||||
if #pnl.left:GetLines() == 0 then
|
||||
selected_side = pnl.right
|
||||
end
|
||||
|
||||
if #pnl.right:GetLines() == 0 then
|
||||
selected_side = pnl.left
|
||||
end
|
||||
|
||||
selected_side:SelectFirstItem()
|
||||
end
|
||||
|
||||
button.DoClick = function()
|
||||
store()
|
||||
|
||||
pnl.left:Refresh()
|
||||
pnl.right:Refresh()
|
||||
|
||||
select()
|
||||
end
|
||||
|
||||
select()
|
||||
|
||||
pnl.Think = function()
|
||||
local p = 5
|
||||
local w = pnl:GetWide() / 2
|
||||
local h = pnl:GetTall() - button:GetTall() - p
|
||||
|
||||
pnl.left:SetWide(w)
|
||||
pnl.left:SetTall(h)
|
||||
|
||||
pnl.right:SetWide(w + 1)
|
||||
pnl.right:SetTall(h)
|
||||
|
||||
pnl.right:MoveRightOf(pnl.left, -1)
|
||||
|
||||
button:MoveBelow(pnl.left, p)
|
||||
button:CopyWidth(pnl)
|
||||
end
|
||||
end
|
||||
1055
lua/pac3/editor/client/panels/lua_editor.lua
Normal file
1055
lua/pac3/editor/client/panels/lua_editor.lua
Normal file
File diff suppressed because it is too large
Load Diff
673
lua/pac3/editor/client/panels/mat_browser.lua
Normal file
673
lua/pac3/editor/client/panels/mat_browser.lua
Normal file
@@ -0,0 +1,673 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pace.Materials = {}
|
||||
|
||||
pace.Materials.materials =
|
||||
{
|
||||
"models/weapons/v_crowbar/crowbar_cyl",
|
||||
"models/weapons/v_crowbar/head_uvw",
|
||||
"models/weapons/v_bugbait/bugbait_sheet",
|
||||
"models/combine_advisor/arm",
|
||||
"models/combine_advisor/hose",
|
||||
"models/combine_advisor/face5",
|
||||
"models/combine_advisor/body9",
|
||||
"models/barnacle/barnacle_sheet",
|
||||
"models/headcrab_black/blackcrab_sheet",
|
||||
"models/headcrab/allinonebacup2",
|
||||
"models/headcrab_classic/headcrabsheet",
|
||||
"models/stalker/stalker_sheet",
|
||||
"models/zombie_poison/poisonzombie_sheet",
|
||||
"models/zombie_fast/fast_zombie_sheet",
|
||||
|
||||
"models/gunship/gunshipsheet",
|
||||
"models/shield_scanner/minelayer_sheet",
|
||||
"models/roller/rollermine_sheet",
|
||||
"models/dog/dog_sheet",
|
||||
"models/gibs/combine_helicopter_gibs/combine_helicopter01",
|
||||
"models/synth/mainbody",
|
||||
"models/combine_room/combine_monitor001",
|
||||
"models/items/combinerifle_ammo",
|
||||
"models/shadertest/shader2",
|
||||
"models/props_combine/combine_train001",
|
||||
"models/props_combine/combinethumper001",
|
||||
"models/props_combine/health_charger001",
|
||||
"models/props_combine/masterinterface01",
|
||||
"models/props_combine/introomarea_sheet",
|
||||
"models/props_combine/combine_bunker01",
|
||||
"models/props_combine/weaponstripper_sheet",
|
||||
"models/props_combine/tpcontroller_sheet",
|
||||
"models/props_combine/combine_tower01a",
|
||||
"models/combine_mine/combine_mine03",
|
||||
"models/magnusson_teleporter/magnusson_teleporter",
|
||||
"models/combine_strider/strider_brain",
|
||||
"models/combine_advisor_pod/combine_advisor_pod",
|
||||
"models/magnusson_device/magnusson_device_basecolor",
|
||||
|
||||
"models/antlion_grub/antlion_grub",
|
||||
"models/antlion/antlion_worker_wing",
|
||||
"models/antlion/antlion_worker",
|
||||
"models/ministrider/mini_skin_basecolor",
|
||||
"models/ministrider/mini_armor_basecolor",
|
||||
"models/spitball/spitball",
|
||||
|
||||
"models/mossman/mossman_hair",
|
||||
"models/alyx/hairbits",
|
||||
|
||||
"models/brokenglass/glassbroken_piece1",
|
||||
"models/props_halloween/flask",
|
||||
"models/props_halloween/flask_liquid",
|
||||
"models/props_halloween/flask_glass",
|
||||
"models/props_halloween/pumpkin",
|
||||
"models/props_mining/dustbowl_roof01",
|
||||
"models/props_mvm/mvm_museum_coal",
|
||||
"models/effects/partyhat",
|
||||
"models/props_gameplay/ball001",
|
||||
"models/props_gameplay/orange_cone001",
|
||||
|
||||
"models/player/items/soldier/dappertopper",
|
||||
"models/weapons/c_items/c_candy_cane_red",
|
||||
"models/props_halloween/halloween_blk",
|
||||
"models/props_halloween/halloween_demoeye_glass_2",
|
||||
"models/props_halloween/halloween_demoeye_glass",
|
||||
"models/weapons/c_items/c_saxxy",
|
||||
"models/props_halloween/scary_ghost",
|
||||
"models/weapons/c_items/c_urinejar_urine",
|
||||
"models/weapons/c_items/c_xms_cold_shoulder",
|
||||
"models/props_wasteland/rockcliff02c",
|
||||
"models/props_lakeside/flat_wall_02",
|
||||
"models/props_manor/table_01b",
|
||||
"models/props_manor/volume_light_01",
|
||||
"models/props_medieval/stone_base001",
|
||||
"models/props_medieval/fort_wall",
|
||||
"models/props_mining/quarryrock02",
|
||||
"models/props_nature/rock_worn001",
|
||||
"models/props_swamp/tallgrass_01",
|
||||
"models/props_swamp/shrub_03",
|
||||
"models/props_viaduct_event/fog_plane03",
|
||||
"models/weapons/v_baseball/baseball_sheet",
|
||||
"hunter/myplastic",
|
||||
"models/player/items/all_class/all_class_ring_diamond",
|
||||
|
||||
"models/effects/invulnfx_blue",
|
||||
"models/effects/invulnfx_red",
|
||||
"models/effects/goldenwrench",
|
||||
"models/effects/cappoint_beam_neutral",
|
||||
"models/effects/cappoint_beam_blue",
|
||||
"models/effects/cappoint_beam_red",
|
||||
"models/effects/medicyell",
|
||||
"models/effects/pyro/pilotlight",
|
||||
"models/effects/pyro/pilotlight_cloak",
|
||||
"models/effects/pyro/pilotlight_motion",
|
||||
"models/effects/invulnerability/invulnerability_red",
|
||||
"models/effects/invulnerability/invulnerability_blue",
|
||||
"models/effects/muzzleflash/blurmuzzle",
|
||||
"models/effects/muzzleflash/brightmuzzle",
|
||||
"models/lilchewchew/embers",
|
||||
"models/pl_hoodoo/metaldoor001",
|
||||
"models/thundermountain_fx/ibeam002_vert",
|
||||
"models/thundermountain_fx/wall025_vert",
|
||||
"models/thundermountain_fx/wood_beam03_vert",
|
||||
"models/thundermountain_fx/wood_bridge001_vert",
|
||||
"models/thundermountain_fx/wood_wall002_vert",
|
||||
|
||||
"models/shadertest/envball_1",
|
||||
"models/shadertest/envball_2",
|
||||
"models/shadertest/envball_3",
|
||||
"models/shadertest/envball_4",
|
||||
"models/shadertest/envball_5",
|
||||
"models/shadertest/envball_6",
|
||||
"models/shadertest/glassbrick",
|
||||
"models/shadertest/point_camera",
|
||||
"models/shadertest/shader1",
|
||||
"models/shadertest/shader1_dudv",
|
||||
"models/shadertest/shield",
|
||||
"models/shadertest/unlitgenericmodel",
|
||||
"models/shadertest/vertexlitalphatestedtexture",
|
||||
|
||||
"models/ihvtest/arm",
|
||||
"models/ihvtest/boot",
|
||||
"models/ihvtest/eyeball_l",
|
||||
|
||||
-- zpankr's material list
|
||||
-- http://www.garrysmod.org/downloads/?a=view&id=18470
|
||||
"models/wireframe",
|
||||
"debug/env_cubemap_model",
|
||||
"models/shadertest/shader3",
|
||||
"models/shadertest/shader4",
|
||||
"models/shadertest/shader5",
|
||||
"models/shiny",
|
||||
"models/debug/debugwhite",
|
||||
"Models/effects/comball_sphere",
|
||||
"Models/effects/comball_tape",
|
||||
"Models/effects/splodearc_sheet",
|
||||
"Models/effects/vol_light001",
|
||||
"models/props_combine/stasisshield_sheet",
|
||||
"models/props_combine/portalball001_sheet",
|
||||
"models/props_combine/com_shield001a",
|
||||
"models/props_c17/frostedglass_01a",
|
||||
"models/props_lab/Tank_Glass001",
|
||||
"models/props_combine/tprings_globe",
|
||||
"models/rendertarget",
|
||||
"models/screenspace",
|
||||
"brick/brick_model",
|
||||
"models/props_pipes/GutterMetal01a",
|
||||
"models/props_pipes/Pipesystem01a_skin3",
|
||||
"models/props_wasteland/wood_fence01a",
|
||||
"models/props_c17/FurnitureFabric003a",
|
||||
"models/props_c17/FurnitureMetal001a",
|
||||
"models/props_c17/paper01",
|
||||
"models/flesh",
|
||||
"models/airboat/airboat_blur02",
|
||||
"models/alyx/emptool_glow",
|
||||
"models/antlion/antlion_innards",
|
||||
"models/barnacle/roots",
|
||||
"models/combine_advisor/body9",
|
||||
"models/combine_advisor/mask",
|
||||
"models/combine_scanner/scanner_eye",
|
||||
"models/debug/debugwhite",
|
||||
"models/dog/eyeglass",
|
||||
"models/effects/comball_glow1",
|
||||
"models/effects/comball_glow2",
|
||||
"models/effects/portalrift_sheet",
|
||||
"models/effects/slimebubble_sheet",
|
||||
"models/effects/splode1_sheet",
|
||||
"models/effects/splodearc_sheet",
|
||||
"models/effects/splode_sheet",
|
||||
"models/effects/vol_light001",
|
||||
"models/gibs/woodgibs/woodgibs01",
|
||||
"models/gibs/woodgibs/woodgibs02",
|
||||
"models/gibs/woodgibs/woodgibs03",
|
||||
"models/gibs/metalgibs/metal_gibs",
|
||||
"models/items/boxsniperrounds",
|
||||
"models/player/player_chrome1",
|
||||
"models/props_animated_breakable/smokestack/brickwall002a",
|
||||
"models/props_building_details/courtyard_template001c_bars",
|
||||
"models/props_building_details/courtyard_template001c_bars",
|
||||
"models/props_buildings/destroyedbuilldingwall01a",
|
||||
"models/props_buildings/plasterwall021a",
|
||||
"models/props_c17/frostedglass_01a",
|
||||
"models/props_c17/furniturefabric001a",
|
||||
"models/props_c17/furniturefabric002a",
|
||||
"models/props_c17/furnituremetal001a",
|
||||
"models/props_c17/gate_door02a",
|
||||
"models/props_c17/metalladder001",
|
||||
"models/props_c17/metalladder002",
|
||||
"models/props_c17/metalladder003",
|
||||
"models/props_canal/canalmap_sheet",
|
||||
"models/props_canal/canal_bridge_railing_01a",
|
||||
"models/props_canal/canal_bridge_railing_01b",
|
||||
"models/props_canal/canal_bridge_railing_01c",
|
||||
"models/props_canal/coastmap_sheet",
|
||||
"models/props_canal/metalcrate001d",
|
||||
"models/props_canal/metalwall005b",
|
||||
"models/props_canal/rock_riverbed01a",
|
||||
"models/props_combine/citadel_cable",
|
||||
"models/props_combine/combine_interface_disp",
|
||||
"models/props_combine/combine_monitorbay_disp",
|
||||
"models/props_combine/com_shield001a",
|
||||
"models/props_combine/health_charger_glass",
|
||||
"models/props_combine/metal_combinebridge001",
|
||||
"models/props_combine/pipes01",
|
||||
"models/props_combine/pipes03",
|
||||
"models/props_combine/prtl_sky_sheet",
|
||||
"models/props_combine/stasisfield_beam",
|
||||
"models/props_debris/building_template010a",
|
||||
"models/props_debris/building_template022j",
|
||||
"models/props_debris/composite_debris",
|
||||
"models/props_debris/concretefloor013a",
|
||||
"models/props_debris/concretefloor020a",
|
||||
"models/props_debris/concretewall019a",
|
||||
"models/props_debris/metalwall001a",
|
||||
"models/props_debris/plasterceiling008a",
|
||||
"models/props_debris/plasterwall009d",
|
||||
"models/props_debris/plasterwall021a",
|
||||
"models/props_debris/plasterwall034a",
|
||||
"models/props_debris/plasterwall034d",
|
||||
"models/props_debris/plasterwall039c",
|
||||
"models/props_debris/plasterwall040c",
|
||||
"models/props_debris/tilefloor001c",
|
||||
"models/props_foliage/driftwood_01a",
|
||||
"models/props_foliage/oak_tree01",
|
||||
"models/props_interiors/metalfence007a",
|
||||
"models/props_junk/plasticcrate01a",
|
||||
"models/props_junk/plasticcrate01b",
|
||||
"models/props_junk/plasticcrate01c",
|
||||
"models/props_junk/plasticcrate01d",
|
||||
"models/props_junk/plasticcrate01e",
|
||||
"models/props_lab/cornerunit_cloud",
|
||||
"models/props_lab/door_klab01",
|
||||
"models/props_lab/security_screens",
|
||||
"models/props_lab/security_screens2",
|
||||
"models/props_lab/Tank_Glass001",
|
||||
"models/props_lab/warp_sheet",
|
||||
"models/props_lab/xencrystal_sheet",
|
||||
"models/props_pipes/destroyedpipes01a",
|
||||
"models/props_pipes/GutterMetal01a",
|
||||
"models/props_pipes/pipeset_metal02",
|
||||
"models/props_pipes/pipesystem01a_skin1",
|
||||
"models/props_pipes/pipesystem01a_skin2",
|
||||
"models/props_vents/borealis_vent001",
|
||||
"models/props_vents/borealis_vent001b",
|
||||
"models/props_vents/borealis_vent001c",
|
||||
"models/props_wasteland/concretefloor010a",
|
||||
"models/props_wasteland/concretewall064b",
|
||||
"models/props_wasteland/concretewall066a",
|
||||
"models/props_wasteland/dirtwall001a",
|
||||
"models/props_wasteland/metal_tram001a",
|
||||
"models/props_wasteland/quarryobjects01",
|
||||
"models/props_wasteland/rockcliff02a",
|
||||
"models/props_wasteland/rockcliff02b",
|
||||
"models/props_wasteland/rockcliff02c",
|
||||
"models/props_wasteland/rockcliff04a",
|
||||
"models/props_wasteland/rockgranite02a",
|
||||
"models/props_wasteland/tugboat01",
|
||||
"models/props_wasteland/tugboat02",
|
||||
"models/props_wasteland/wood_fence01a",
|
||||
"models/props_wasteland/wood_fence01a_skin2",
|
||||
"models/roller/rollermine_glow",
|
||||
"models/weapons/v_crossbow/rebar_glow",
|
||||
"models/weapons/v_crowbar/crowbar_cyl",
|
||||
"models/weapons/v_grenade/grenade body",
|
||||
"models/weapons/v_smg1/texture5",
|
||||
"models/weapons/w_smg1/smg_crosshair",
|
||||
"models/weapons/v_slam/new light2",
|
||||
"models/weapons/v_slam/new light1",
|
||||
"models/props/cs_assault/dollar",
|
||||
"models/props/cs_assault/fireescapefloor",
|
||||
"models/props/cs_assault/metal_stairs1",
|
||||
"models/props/cs_assault/moneywrap",
|
||||
"models/props/cs_assault/moneywrap02",
|
||||
"models/props/cs_assault/moneytop",
|
||||
"models/props/cs_assault/pylon",
|
||||
"models/props/CS_militia/boulder01",
|
||||
"models/props/CS_militia/milceil001",
|
||||
"models/props/CS_militia/militiarock",
|
||||
"models/props/CS_militia/militiarockb",
|
||||
"models/props/CS_militia/milwall006",
|
||||
"models/props/CS_militia/rocks01",
|
||||
"models/props/CS_militia/roofbeams01",
|
||||
"models/props/CS_militia/roofbeams02",
|
||||
"models/props/CS_militia/roofbeams03",
|
||||
"models/props/CS_militia/RoofEdges",
|
||||
"models/props/cs_office/clouds",
|
||||
"models/props/cs_office/file_cabinet2",
|
||||
"models/props/cs_office/file_cabinet3",
|
||||
"models/props/cs_office/screen",
|
||||
"models/props/cs_office/snowmana",
|
||||
"models/props/de_inferno/de_inferno_boulder_03",
|
||||
"models/props/de_inferno/infflra",
|
||||
"models/props/de_inferno/infflrd",
|
||||
"models/props/de_inferno/inftowertop",
|
||||
"models/props/de_inferno/offwndwb_break",
|
||||
"models/props/de_inferno/roofbits",
|
||||
"models/props/de_inferno/tileroof01",
|
||||
"models/props/de_inferno/woodfloor008a",
|
||||
"models/props/de_nuke/nukconcretewalla",
|
||||
"models/props/de_nuke/nukecardboard",
|
||||
"models/props/de_nuke/pipeset_metal",
|
||||
"models/shadertest/predator",
|
||||
}
|
||||
|
||||
pace.Materials.trails =
|
||||
{
|
||||
"trails/plasma",
|
||||
"trails/tube",
|
||||
"trails/electric",
|
||||
"trails/smoke",
|
||||
"trails/laser",
|
||||
"trails/physbeam",
|
||||
"trails/love",
|
||||
"trails/lol",
|
||||
"sprites/rollermine_shock_yellow",
|
||||
"sprites/yellowlaser1",
|
||||
"particle/beam_smoke_01",
|
||||
"particle/beam_smoke_02",
|
||||
"particle/bendibeam_nofog",
|
||||
"sprites/physbeam",
|
||||
"sprites/physbeama",
|
||||
"sprites/rollermine_shock",
|
||||
"sprites/hydraspinalcord",
|
||||
"particle/Particle_Square_Gradient",
|
||||
"particle/Particle_Square_Gradient_NoFog",
|
||||
}
|
||||
|
||||
pace.Materials.sprites =
|
||||
{
|
||||
"sprites/glow04_noz",
|
||||
"sprites/grip",
|
||||
"sprites/key_0",
|
||||
"sprites/key_1",
|
||||
"sprites/key_10",
|
||||
"sprites/key_11",
|
||||
"sprites/key_12",
|
||||
"sprites/key_13",
|
||||
"sprites/key_14",
|
||||
"sprites/key_15",
|
||||
"sprites/key_2",
|
||||
"sprites/key_3",
|
||||
"sprites/key_4",
|
||||
"sprites/key_5",
|
||||
"sprites/key_6",
|
||||
"sprites/key_7",
|
||||
"sprites/key_8",
|
||||
"sprites/key_9",
|
||||
"sprites/light_ignorez",
|
||||
"sprites/muzzleflash4",
|
||||
"sprites/orangecore1",
|
||||
"sprites/orangecore2",
|
||||
"sprites/orangeflare1",
|
||||
"sprites/physg_glow1",
|
||||
"sprites/physg_glow2",
|
||||
"sprites/physgbeamb",
|
||||
"sprites/redglow1",
|
||||
"sprites/animglow02",
|
||||
"sprites/ar2_muzzle1",
|
||||
"sprites/ar2_muzzle3",
|
||||
"sprites/ar2_muzzle4",
|
||||
"sprites/arrow",
|
||||
"sprites/blueglow2",
|
||||
"sprites/bluelaser1",
|
||||
"sprites/dot",
|
||||
"sprites/flamelet1",
|
||||
"sprites/flamelet2",
|
||||
"sprites/flamelet3",
|
||||
"sprites/flamelet4",
|
||||
"sprites/flamelet5",
|
||||
"sprites/heatwave",
|
||||
"sprites/heatwavedx70",
|
||||
"sprites/hydragutbeam",
|
||||
"sprites/hydragutbeamcap",
|
||||
"sprites/light_glow02_add",
|
||||
"sprites/light_glow02_add_noz",
|
||||
"sprites/plasmaember",
|
||||
"sprites/predator",
|
||||
"sprites/qi_center",
|
||||
"sprites/reticle",
|
||||
"sprites/reticle1",
|
||||
"sprites/reticle2",
|
||||
"sprites/rico1",
|
||||
"sprites/rico1_noz",
|
||||
"sprites/scanner",
|
||||
"sprites/scanner_bottom",
|
||||
"sprites/scanner_dots1",
|
||||
"sprites/scanner_dots2",
|
||||
"sprites/strider_blackball",
|
||||
"sprites/strider_bluebeam",
|
||||
"sprites/tp_beam001",
|
||||
"sprites/bucket_bat_blue",
|
||||
"sprites/bucket_bat_red",
|
||||
"sprites/bucket_bonesaw",
|
||||
"sprites/bucket_bottle_blue",
|
||||
"sprites/bucket_bottle_red",
|
||||
"sprites/bucket_fireaxe",
|
||||
"sprites/bucket_fists_blue",
|
||||
"sprites/bucket_fists_red",
|
||||
"sprites/bucket_flamethrower_blue",
|
||||
"sprites/bucket_flamethrower_red",
|
||||
"sprites/bucket_grenlaunch",
|
||||
"sprites/bucket_knife",
|
||||
"sprites/bucket_machete",
|
||||
"sprites/bucket_medigun_blue",
|
||||
"sprites/bucket_medigun_red",
|
||||
"sprites/bucket_minigun",
|
||||
"sprites/bucket_nailgun",
|
||||
"sprites/bucket_pda",
|
||||
"sprites/bucket_pda_build",
|
||||
"sprites/bucket_pda_destroy",
|
||||
"sprites/bucket_pipelaunch",
|
||||
"sprites/bucket_pistol",
|
||||
"sprites/bucket_revolver",
|
||||
"sprites/bucket_rl",
|
||||
"sprites/bucket_sapper",
|
||||
"sprites/bucket_scatgun",
|
||||
"sprites/bucket_shotgun",
|
||||
"sprites/bucket_shovel",
|
||||
"sprites/bucket_smg",
|
||||
"sprites/bucket_sniper",
|
||||
"sprites/bucket_syrgun_blue",
|
||||
"sprites/bucket_syrgun_red",
|
||||
"sprites/bucket_tranq",
|
||||
"sprites/bucket_wrench",
|
||||
"sprites/healbeam",
|
||||
"sprites/healbeam_blue",
|
||||
"sprites/healbeam_red",
|
||||
"sprites/640_pain_down",
|
||||
"sprites/640_pain_left",
|
||||
"sprites/640_pain_right",
|
||||
"sprites/640_pain_up",
|
||||
"sprites/bomb_carried",
|
||||
"sprites/bomb_carried_ring",
|
||||
"sprites/bomb_carried_ring_offscreen",
|
||||
"sprites/bomb_dropped",
|
||||
"sprites/bomb_dropped_ring",
|
||||
"sprites/bomb_planted",
|
||||
"sprites/bomb_planted_ring",
|
||||
"sprites/c4",
|
||||
"sprites/defuser",
|
||||
"sprites/hostage_following",
|
||||
"sprites/hostage_following_offscreen",
|
||||
"sprites/hostage_rescue",
|
||||
"sprites/numbers",
|
||||
"sprites/objective_rescue",
|
||||
"sprites/objective_site_a",
|
||||
"sprites/objective_site_b",
|
||||
"sprites/player_blue_dead",
|
||||
"sprites/player_blue_dead_offscreen",
|
||||
"sprites/player_blue_offscreen",
|
||||
"sprites/player_blue_self",
|
||||
"sprites/player_blue_small",
|
||||
"sprites/player_hostage_dead",
|
||||
"sprites/player_hostage_dead_offscreen",
|
||||
"sprites/player_hostage_offscreen",
|
||||
"sprites/player_hostage_small",
|
||||
"sprites/player_radio_ring",
|
||||
"sprites/player_radio_ring_offscreen",
|
||||
"sprites/player_red_dead",
|
||||
"sprites/player_red_dead_offscreen",
|
||||
"sprites/player_red_offscreen",
|
||||
"sprites/player_red_self",
|
||||
"sprites/player_red_small",
|
||||
"sprites/player_tick",
|
||||
"sprites/radar",
|
||||
"sprites/radar_trans",
|
||||
"sprites/radio",
|
||||
"sprites/scope_arc",
|
||||
"sprites/shopping_cart",
|
||||
"sprites/spectator_3rdcam",
|
||||
"sprites/spectator_eye",
|
||||
"sprites/spectator_freecam",
|
||||
"sprites/cloudglow1_nofog",
|
||||
"sprites/core_beam1",
|
||||
"sprites/bluelight",
|
||||
"sprites/grav_flare",
|
||||
"sprites/grav_light",
|
||||
"sprites/orangelight",
|
||||
"sprites/portalgun_effects",
|
||||
"sprites/sphere_silhouette",
|
||||
"sprites/redglow_mp1",
|
||||
"sprites/sent_ball",
|
||||
"particle/fire",
|
||||
"particle/particle_composite",
|
||||
"particle/Particle_Crescent",
|
||||
"particle/Particle_Crescent_Additive",
|
||||
"particle/Particle_Glow_02",
|
||||
"particle/Particle_Glow_03",
|
||||
"particle/Particle_Glow_03_Additive",
|
||||
"particle/Particle_Glow_04",
|
||||
"particle/Particle_Glow_04_Additive",
|
||||
"particle/Particle_Glow_05",
|
||||
"particle/particle_noisesphere",
|
||||
"particle/Particle_Ring_Blur",
|
||||
"particle/particle_ring_refract_01",
|
||||
"particle/Particle_Ring_Sharp",
|
||||
"particle/Particle_Ring_Sharp_Additive",
|
||||
"particle/Particle_Ring_Wave_8",
|
||||
"particle/Particle_Ring_Wave_8_15OB_NoFog",
|
||||
"particle/Particle_Ring_Wave_Additive",
|
||||
"particle/Particle_Ring_Wave_AddNoFog",
|
||||
"particle/particle_smokegrenade",
|
||||
"particle/particle_smokegrenade1",
|
||||
"particle/particle_sphere",
|
||||
"particle/rain",
|
||||
"particle/smokesprites_0001",
|
||||
"particle/smokesprites_0002",
|
||||
"particle/smokesprites_0003",
|
||||
"particle/smokesprites_0004",
|
||||
"particle/smokesprites_0005",
|
||||
"particle/smokesprites_0006",
|
||||
"particle/smokesprites_0007",
|
||||
"particle/smokesprites_0008",
|
||||
"particle/smokesprites_0009",
|
||||
"particle/smokesprites_0010",
|
||||
"particle/smokesprites_0011",
|
||||
"particle/smokesprites_0012",
|
||||
"particle/smokesprites_0013",
|
||||
"particle/smokesprites_0014",
|
||||
"particle/smokesprites_0015",
|
||||
"particle/smokesprites_0016",
|
||||
"particle/SmokeStack",
|
||||
"particle/snow",
|
||||
"particle/sparkles",
|
||||
"particle/warp1_warp",
|
||||
"particle/warp2_warp",
|
||||
"particle/warp3_warp_NoZ",
|
||||
"particle/warp4_warp",
|
||||
"particle/warp4_warp_NoZ",
|
||||
"particle/warp5_warp",
|
||||
"particle/warp_ripple",
|
||||
"particle/glow_haze_nofog",
|
||||
"particle/smoke_black_smokestack000",
|
||||
"particle/smoke_black_smokestack001",
|
||||
"particle/smoke_black_smokestack_all",
|
||||
"particle/smokestack_nofog",
|
||||
"particle/particle_rockettrail2",
|
||||
"particles/balloon_bit",
|
||||
"particles/fire1",
|
||||
"particles/fire_glow",
|
||||
"particles/flamelet1",
|
||||
"particles/flamelet2",
|
||||
"particles/flamelet3",
|
||||
"particles/flamelet4",
|
||||
"particles/flamelet5",
|
||||
"particles/smokey",
|
||||
}
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
PANEL.ClassName = "mat_browser_sheet"
|
||||
PANEL.Base = "DPanel"
|
||||
|
||||
AccessorFunc(PANEL, "m_Selected", "Selected")
|
||||
|
||||
function PANEL:Init()
|
||||
local list = vgui.Create("DPanelList", self)
|
||||
list:SetPadding(2)
|
||||
list:SetSpacing(2)
|
||||
list:EnableVerticalScrollbar(true)
|
||||
list:EnableHorizontal(true)
|
||||
list:Dock(FILL)
|
||||
self.MatList = list
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
surface.SetDrawColor(0,0,0, 255)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
local cache = {}
|
||||
local function IsError(path)
|
||||
if cache[path] then return true end
|
||||
if Material(path):IsError() then
|
||||
cache[path] = truer
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetMaterialList(tbl)
|
||||
self:SetSelected()
|
||||
|
||||
self.MaterialList = tbl
|
||||
|
||||
self.MatList:Clear(true)
|
||||
|
||||
for i, material in pairs(self.MaterialList) do
|
||||
-- if IsError(material) then continue end
|
||||
|
||||
local image = vgui.Create("DImageButton")
|
||||
image.m_Image.LoadMaterial = function(s) s:DoLoadMaterial() end
|
||||
image:SetOnViewMaterial(material, material)
|
||||
image:SetSize(64, 64)
|
||||
image.Value = material
|
||||
|
||||
self.MatList:AddItem(image)
|
||||
|
||||
image.DoClick = function(image) self:SetSelected(image.Value) end
|
||||
|
||||
if self:GetSelected() then
|
||||
image.PaintOver = HighlightedButtonPaint
|
||||
self:SetSelected(material)
|
||||
end
|
||||
end
|
||||
|
||||
self:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
pace.RegisterPanel(PANEL)
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
PANEL.Base = "DFrame"
|
||||
PANEL.ClassName = "mat_browser"
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetTitle("materials")
|
||||
|
||||
local list =
|
||||
{
|
||||
{key = "default", val = table.Merge(list.Get("OverrideMaterials"), pace.Materials.materials)},
|
||||
{key = "sprites", val = pace.Materials.sprites},
|
||||
{key = "trails", val = pace.Materials.trails},
|
||||
}
|
||||
|
||||
local sheet = vgui.Create("DPropertySheet", self)
|
||||
sheet:Dock(FILL)
|
||||
|
||||
for _, data in pairs(list) do
|
||||
local name, tbl = data.key, data.val
|
||||
local pnl = pace.CreatePanel("mat_browser_sheet", self)
|
||||
pnl:SetMaterialList(tbl)
|
||||
pnl.SetSelected = function(_, path) self:SetSelected(path) end
|
||||
sheet:AddSheet(name, pnl)
|
||||
end
|
||||
|
||||
local entry = vgui.Create("DTextEntry", self)
|
||||
entry.OnEnter = function() self:SetSelected(self.Entry:GetValue()) end
|
||||
entry:Dock(BOTTOM)
|
||||
entry:SetTall(20)
|
||||
self.Entry = entry
|
||||
|
||||
self:SetDrawOnTop(true)
|
||||
self:SetSize(300, 300)
|
||||
self:SetSizable(true)
|
||||
self:Center()
|
||||
end
|
||||
|
||||
function PANEL:SetSelected(value)
|
||||
self.Entry:SetText(value or "")
|
||||
|
||||
self.m_Selected = value
|
||||
|
||||
self:MaterialSelected(value)
|
||||
end
|
||||
|
||||
function PANEL:MaterialSelected(path) end
|
||||
|
||||
pace.RegisterPanel(PANEL)
|
||||
567
lua/pac3/editor/client/panels/pac_tree.lua
Normal file
567
lua/pac3/editor/client/panels/pac_tree.lua
Normal file
@@ -0,0 +1,567 @@
|
||||
--[[
|
||||
| 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 PANEL = vgui.Register("pac_dtree_node_button", {}, "DButton")
|
||||
pace.pac_dtree_node_button = PANEL
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetTextInset(32, 0)
|
||||
self:SetContentAlignment(4)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
derma.SkinHook("Paint", "TreeNodeButton", self, w, h)
|
||||
return false
|
||||
end
|
||||
|
||||
function PANEL:UpdateColours(skin)
|
||||
if self:IsSelected() then return self:SetTextStyleColor(skin.Colours.Tree.Selected) end
|
||||
if self.Hovered then return self:SetTextStyleColor(skin.Colours.Tree.Hover) end
|
||||
|
||||
return self:SetTextStyleColor(skin.Colours.Tree.Normal)
|
||||
end
|
||||
|
||||
local PANEL = vgui.Register("pac_dtree", {}, "DScrollPanel")
|
||||
pace.pac_dtree = PANEL
|
||||
|
||||
AccessorFunc(PANEL, "m_bShowIcons", "ShowIcons")
|
||||
AccessorFunc(PANEL, "m_iIndentSize", "IndentSize")
|
||||
AccessorFunc(PANEL, "m_iLineHeight", "LineHeight")
|
||||
AccessorFunc(PANEL, "m_pSelectedItem", "SelectedItem")
|
||||
AccessorFunc(PANEL, "m_bClickOnDragHover", "ClickOnDragHover")
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetShowIcons(true)
|
||||
self:SetIndentSize(14)
|
||||
self:SetLineHeight(17)
|
||||
|
||||
self.RootNode = self:GetCanvas():Add("pac_dtree_node")
|
||||
self.RootNode:SetRoot(self)
|
||||
self.RootNode:SetParentNode(self)
|
||||
self.RootNode:SetSize(5,5)
|
||||
self.RootNode:Dock(TOP)
|
||||
self.RootNode:SetText("")
|
||||
self.RootNode:SetExpanded(true, true)
|
||||
self.RootNode:DockMargin(0, 0, 0, 0)
|
||||
|
||||
self:SetPaintBackground(true)
|
||||
end
|
||||
|
||||
function PANEL:Root()
|
||||
return self.RootNode
|
||||
end
|
||||
|
||||
function PANEL:AddNode(strName, strIcon)
|
||||
return self.RootNode:AddNode(strName, strIcon)
|
||||
end
|
||||
|
||||
function PANEL:ChildExpanded(bExpand)
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:ShowIcons()
|
||||
return self.m_bShowIcons
|
||||
end
|
||||
|
||||
function PANEL:ExpandTo(bExpand)
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SetExpanded(bExpand)
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Clear()
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
derma.SkinHook("Paint", "Tree", self, w, h)
|
||||
return true
|
||||
end
|
||||
|
||||
function PANEL:DoClick(node)
|
||||
return false
|
||||
end
|
||||
|
||||
function PANEL:DoRightClick(node)
|
||||
return false
|
||||
end
|
||||
|
||||
function PANEL:SetSelectedItem(node)
|
||||
if IsValid(self.m_pSelectedItem) then
|
||||
self.m_pSelectedItem:SetSelected(false)
|
||||
self:OnNodeDeselected(self.m_pSelectedItem)
|
||||
end
|
||||
|
||||
self.m_pSelectedItem = node
|
||||
|
||||
if node then
|
||||
node:SetSelected(true)
|
||||
node:OnNodeSelected(node)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnNodeSelected(node)
|
||||
|
||||
end
|
||||
|
||||
function PANEL:OnNodeDeselected(node)
|
||||
|
||||
end
|
||||
|
||||
function PANEL:MoveChildTo(child, pos)
|
||||
self:InsertAtTop(child)
|
||||
end
|
||||
|
||||
function PANEL:LayoutTree()
|
||||
self:InvalidateChildren(true)
|
||||
end
|
||||
|
||||
local PANEL = vgui.Register("pac_dtree_node", {}, "DPanel")
|
||||
pace.pac_dtree_node = PANEL
|
||||
|
||||
AccessorFunc(PANEL, "m_pRoot", "Root")
|
||||
|
||||
AccessorFunc(PANEL, "m_pParentNode", "ParentNode")
|
||||
|
||||
AccessorFunc(PANEL, "m_strPathID", "PathID")
|
||||
AccessorFunc(PANEL, "m_strWildCard", "WildCard")
|
||||
AccessorFunc(PANEL, "m_bNeedsPopulating", "NeedsPopulating")
|
||||
|
||||
AccessorFunc(PANEL, "m_bDirty", "Dirty", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bNeedsChildSearch", "NeedsChildSearch", FORCE_BOOL)
|
||||
|
||||
AccessorFunc(PANEL, "m_bForceShowExpander", "ForceShowExpander", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bHideExpander", "HideExpander", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bDoubleClickToOpen", "DoubleClickToOpen", FORCE_BOOL)
|
||||
|
||||
AccessorFunc(PANEL, "m_bLastChild", "LastChild", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bDrawLines", "DrawLines", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_strDraggableName", "DraggableName")
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetDoubleClickToOpen(true)
|
||||
|
||||
self.Label = vgui.Create("pac_dtree_node_button", self)
|
||||
self.Label:SetDragParent(self)
|
||||
self.Label.DoClick = function() self:InternalDoClick() end
|
||||
self.Label.DoDoubleClick = function() self:InternalDoClick() end
|
||||
self.Label.DoRightClick = function() self:InternalDoRightClick() end
|
||||
self.Label.DragHover = function(s, t) self:DragHover(t) end
|
||||
|
||||
self.Expander = vgui.Create("DExpandButton", self)
|
||||
self.Expander.DoClick = function() self:SetExpanded( not self.m_bExpanded) end
|
||||
self.Expander:SetVisible(false)
|
||||
|
||||
self.Icon = vgui.Create("DImage", self)
|
||||
self.Icon:SetImage("icon16/folder.png")
|
||||
self.Icon:SizeToContents()
|
||||
|
||||
self.fLastClick = SysTime()
|
||||
|
||||
self:SetDrawLines(false)
|
||||
self:SetLastChild(false)
|
||||
end
|
||||
|
||||
function PANEL:SetRoot(root)
|
||||
self.m_pRoot = root
|
||||
|
||||
root.added_nodes = root.added_nodes or {}
|
||||
for i,v in ipairs(root.added_nodes) do
|
||||
if v == self then return end
|
||||
end
|
||||
table.insert(root.added_nodes, self)
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
local root = self:GetRoot()
|
||||
|
||||
if not IsValid(root) then return end
|
||||
|
||||
root.added_nodes = root.added_nodes or {}
|
||||
for i,v in ipairs(root.added_nodes) do
|
||||
if v == self then
|
||||
table.remove(root.added_nodes, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:IsRootNode()
|
||||
return self.m_pRoot == self.m_pParentNode
|
||||
end
|
||||
|
||||
function PANEL:InternalDoClick()
|
||||
self:GetRoot():SetSelectedItem(self)
|
||||
|
||||
if self:DoClick() then return end
|
||||
if self:GetRoot():DoClick(self) then return end
|
||||
|
||||
if not self.m_bDoubleClickToOpen or (SysTime() - self.fLastClick < 0.3) then
|
||||
self:SetExpanded( not self.m_bExpanded)
|
||||
end
|
||||
|
||||
self.fLastClick = SysTime()
|
||||
end
|
||||
|
||||
function PANEL:OnNodeSelected(node)
|
||||
local parent = self:GetParentNode()
|
||||
if IsValid(parent) and parent.OnNodeSelected then
|
||||
parent:OnNodeSelected(node)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:InternalDoRightClick()
|
||||
if self:DoRightClick() then return end
|
||||
if self:GetRoot():DoRightClick(self) then return end
|
||||
end
|
||||
|
||||
function PANEL:DoClick()
|
||||
return false
|
||||
end
|
||||
|
||||
function PANEL:DoRightClick()
|
||||
return false
|
||||
end
|
||||
|
||||
function PANEL:SetIcon(str)
|
||||
if not str then return end
|
||||
if str == "" then return end
|
||||
|
||||
self.Icon:SetImage(str)
|
||||
end
|
||||
|
||||
function PANEL:ShowIcons()
|
||||
return self:GetParentNode():ShowIcons()
|
||||
end
|
||||
|
||||
function PANEL:GetLineHeight()
|
||||
return self:GetParentNode():GetLineHeight()
|
||||
end
|
||||
|
||||
function PANEL:GetIndentSize()
|
||||
return self:GetParentNode():GetIndentSize()
|
||||
end
|
||||
|
||||
function PANEL:SetText(strName)
|
||||
self.Label:SetText(strName)
|
||||
end
|
||||
|
||||
function PANEL:ExpandRecurse(bExpand)
|
||||
self:SetExpanded(bExpand, true)
|
||||
|
||||
if not self.ChildNodes then return end
|
||||
|
||||
for k, Child in pairs(self.ChildNodes:GetItems()) do
|
||||
if Child.ExpandRecurse then
|
||||
Child:ExpandRecurse(bExpand)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:ExpandTo(bExpand)
|
||||
self:SetExpanded(bExpand, true)
|
||||
self:GetParentNode():ExpandTo(bExpand)
|
||||
end
|
||||
|
||||
|
||||
function PANEL:IsExpanded()
|
||||
local parent = self:GetParent():GetParent()
|
||||
|
||||
while IsValid(parent) and parent.m_bExpanded ~= nil do
|
||||
if parent.m_bExpanded == false then
|
||||
return false
|
||||
end
|
||||
parent = parent:GetParent():GetParent()
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function PANEL:SetExpanded(bExpand)
|
||||
|
||||
if self.m_pParentNode:IsValid() then
|
||||
self:GetParentNode():ChildExpanded(bExpand)
|
||||
end
|
||||
self.Expander:SetExpanded(bExpand)
|
||||
self.m_bExpanded = bExpand
|
||||
self:InvalidateLayout(true)
|
||||
|
||||
if not self.ChildNodes then return end
|
||||
|
||||
local StartTall = self:GetTall()
|
||||
|
||||
if self.ChildNodes then
|
||||
self.ChildNodes:SetVisible(bExpand)
|
||||
if bExpand then
|
||||
self.ChildNodes:InvalidateLayout(true)
|
||||
end
|
||||
end
|
||||
|
||||
self:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
function PANEL:ChildExpanded(bExpand)
|
||||
self.ChildNodes:InvalidateLayout(true)
|
||||
self:InvalidateLayout(true)
|
||||
self:GetParentNode():ChildExpanded(bExpand)
|
||||
end
|
||||
|
||||
function PANEL:Paint()
|
||||
end
|
||||
|
||||
function PANEL:HasChildren()
|
||||
if not IsValid(self.ChildNodes) then return false end
|
||||
return self.ChildNodes:HasChildren()
|
||||
end
|
||||
|
||||
|
||||
function PANEL:DoChildrenOrder()
|
||||
if not self.ChildNodes then return end
|
||||
|
||||
local last = table.Count(self.ChildNodes:GetChildren())
|
||||
for k, Child in pairs(self.ChildNodes:GetChildren()) do
|
||||
Child:SetLastChild(k == last)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PerformRootNodeLayout()
|
||||
self.Expander:SetVisible(false)
|
||||
self.Label:SetVisible(false)
|
||||
self.Icon:SetVisible(false)
|
||||
|
||||
if IsValid(self.ChildNodes) then
|
||||
self.ChildNodes:Dock(TOP)
|
||||
self:SetTall(self.ChildNodes:GetTall())
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout()
|
||||
if self:IsRootNode() then
|
||||
return self:PerformRootNodeLayout()
|
||||
end
|
||||
|
||||
local LineHeight = self:GetLineHeight()
|
||||
|
||||
if self.m_bHideExpander then
|
||||
self.Expander:SetPos(-11, 0)
|
||||
self.Expander:SetSize(15, 15)
|
||||
self.Expander:SetVisible(false)
|
||||
else
|
||||
self.Expander:SetPos(2, 0)
|
||||
self.Expander:SetSize(15, 15)
|
||||
self.Expander:SetVisible(self:HasChildren() or self:GetForceShowExpander())
|
||||
self.Expander:SetZPos(10)
|
||||
end
|
||||
|
||||
self.Label:StretchToParent(0, nil, 0, nil)
|
||||
self.Label:SetTall(LineHeight)
|
||||
|
||||
if self:ShowIcons() then
|
||||
self.Icon:SetVisible(true)
|
||||
self.Icon:SetPos(self.Expander.x + self.Expander:GetWide() + 4, (LineHeight - self.Icon:GetTall()) * 0.5)
|
||||
self.Label:SetTextInset(self.Icon.x + self.Icon:GetWide() + 4, 0)
|
||||
else
|
||||
self.Icon:SetVisible(false)
|
||||
self.Label:SetTextInset(self.Expander.x + self.Expander:GetWide() + 4, 0)
|
||||
end
|
||||
|
||||
if not self.ChildNodes or not self.ChildNodes:IsVisible() then
|
||||
self:SetTall(LineHeight)
|
||||
return end
|
||||
|
||||
self.ChildNodes:SizeToContents()
|
||||
self:SetTall(LineHeight + self.ChildNodes:GetTall())
|
||||
|
||||
self.ChildNodes:StretchToParent(LineHeight, LineHeight, 0, 0)
|
||||
|
||||
self:DoChildrenOrder()
|
||||
end
|
||||
|
||||
function PANEL:CreateChildNodes()
|
||||
if self.ChildNodes then return end
|
||||
|
||||
self.ChildNodes = vgui.Create("DListLayout", self)
|
||||
self.ChildNodes:SetDropPos("852")
|
||||
self.ChildNodes:SetVisible(self.m_bExpanded)
|
||||
self.ChildNodes.OnChildRemoved = function()
|
||||
self.ChildNodes:InvalidateLayout()
|
||||
end
|
||||
|
||||
self.ChildNodes.OnModified = function()
|
||||
self:OnModified()
|
||||
end
|
||||
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:AddPanel(pPanel)
|
||||
self:CreateChildNodes()
|
||||
|
||||
self.ChildNodes:Add(pPanel)
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:AddNode(strName, strIcon)
|
||||
self:CreateChildNodes()
|
||||
|
||||
local pNode = vgui.Create("pac_dtree_node", self)
|
||||
pNode:SetText(strName)
|
||||
pNode:SetParentNode(self)
|
||||
pNode:SetRoot(self:GetRoot())
|
||||
pNode:SetIcon(strIcon)
|
||||
pNode:SetDrawLines( not self:IsRootNode())
|
||||
|
||||
self:InstallDraggable(pNode)
|
||||
|
||||
self.ChildNodes:Add(pNode)
|
||||
self:InvalidateLayout()
|
||||
|
||||
return pNode
|
||||
end
|
||||
|
||||
function PANEL:InsertNode(pNode)
|
||||
self:CreateChildNodes()
|
||||
|
||||
pNode:SetParentNode(self)
|
||||
pNode:SetRoot(self:GetRoot())
|
||||
self:InstallDraggable(pNode)
|
||||
|
||||
self.ChildNodes:Add(pNode)
|
||||
self:InvalidateLayout()
|
||||
|
||||
return pNode
|
||||
end
|
||||
|
||||
function PANEL:InstallDraggable(pNode)
|
||||
local DragName = self:GetDraggableName()
|
||||
if not DragName then return end
|
||||
|
||||
-- Make this node draggable
|
||||
pNode:SetDraggableName(DragName)
|
||||
pNode:Droppable(DragName)
|
||||
|
||||
-- Allow item dropping onto us
|
||||
self.ChildNodes:MakeDroppable(DragName, true, true)
|
||||
end
|
||||
|
||||
function PANEL:DroppedOn(pnl)
|
||||
self:InsertNode(pnl)
|
||||
self:SetExpanded(true)
|
||||
end
|
||||
|
||||
function PANEL:SetSelected(b)
|
||||
self.Label:SetSelected(b)
|
||||
self.Label:InvalidateLayout()
|
||||
if self.OnSelected then
|
||||
self:OnSelected(b)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:DragHoverClick(HoverTime)
|
||||
if not self.m_bExpanded then
|
||||
self:SetExpanded(true)
|
||||
end
|
||||
|
||||
if self:GetRoot():GetClickOnDragHover() then
|
||||
self:InternalDoClick()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function PANEL:MoveToTop()
|
||||
local parent = self:GetParentNode()
|
||||
if not IsValid(parent) then return end
|
||||
|
||||
self:GetParentNode():MoveChildTo(self, 1)
|
||||
end
|
||||
|
||||
function PANEL:MoveChildTo(child)
|
||||
self.ChildNodes:InsertAtTop(child)
|
||||
end
|
||||
|
||||
function PANEL:GetText()
|
||||
return self.Label:GetText()
|
||||
end
|
||||
|
||||
function PANEL:GetIcon()
|
||||
return self.Icon:GetImage()
|
||||
end
|
||||
|
||||
function PANEL:CleanList()
|
||||
for k, panel in pairs(self.Items) do
|
||||
|
||||
if not IsValid(panel) or panel:GetParent() ~= self.pnlCanvas then
|
||||
self.Items[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Insert(pNode, pNodeNextTo, bBefore)
|
||||
pNode:SetParentNode(self)
|
||||
pNode:SetRoot(self:GetRoot())
|
||||
|
||||
self:CreateChildNodes()
|
||||
|
||||
if bBefore then
|
||||
self.ChildNodes:InsertBefore(pNodeNextTo, pNode)
|
||||
else
|
||||
self.ChildNodes:InsertAfter(pNodeNextTo, pNode)
|
||||
end
|
||||
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:LeaveTree(pnl)
|
||||
self.ChildNodes:RemoveItem(pnl, true)
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
|
||||
function PANEL:OnModified()
|
||||
end
|
||||
|
||||
function PANEL:GetChildNode(iNum)
|
||||
if not IsValid(self.ChildNodes) then return end
|
||||
return self.ChildNodes:GetChild(iNum)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
derma.SkinHook("Paint", "TreeNode", self, w, h)
|
||||
end
|
||||
|
||||
function PANEL:Copy()
|
||||
local copy = vgui.Create("pac_dtree_node", self:GetParent())
|
||||
copy:SetText(self:GetText())
|
||||
copy:SetIcon(self:GetIcon())
|
||||
copy:SetRoot(self:GetRoot())
|
||||
copy:SetParentNode(self:GetParentNode())
|
||||
|
||||
if self.ChildNodes then
|
||||
for k, v in pairs(self.ChildNodes:GetChildren()) do
|
||||
local childcopy = v:Copy()
|
||||
copy:InsertNode(childcopy)
|
||||
end
|
||||
end
|
||||
|
||||
self:SetupCopy(copy)
|
||||
|
||||
return copy
|
||||
end
|
||||
|
||||
function PANEL:SetupCopy(copy)
|
||||
|
||||
end
|
||||
|
||||
if Entity(1):IsPlayer() and not PAC_RESTART and not VLL2_FILEDEF then
|
||||
pace.OpenEditor()
|
||||
pace.RefreshTree(true)
|
||||
end
|
||||
1579
lua/pac3/editor/client/panels/properties.lua
Normal file
1579
lua/pac3/editor/client/panels/properties.lua
Normal file
File diff suppressed because it is too large
Load Diff
423
lua/pac3/editor/client/panels/scrollpanel_horizontal.lua
Normal file
423
lua/pac3/editor/client/panels/scrollpanel_horizontal.lua
Normal file
@@ -0,0 +1,423 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
do
|
||||
local PANEL = vgui.Register("pac_horizontal_scrollbar", {}, "Panel")
|
||||
|
||||
AccessorFunc( PANEL, "m_HideButtons", "HideButtons" )
|
||||
|
||||
function PANEL:Init()
|
||||
|
||||
self.Offset = 0
|
||||
self.Scroll = 0
|
||||
self.CanvasSize = 1
|
||||
self.BarSize = 1
|
||||
|
||||
self.btnLeft = vgui.Create( "DButton", self )
|
||||
self.btnLeft:SetText( "" )
|
||||
self.btnLeft.DoClick = function( self ) self:GetParent():AddScroll( -1 ) end
|
||||
self.btnLeft.Paint = function( panel, w, h ) derma.SkinHook( "Paint", "ButtonLeft", panel, w, h ) end
|
||||
|
||||
self.btnRight = vgui.Create( "DButton", self )
|
||||
self.btnRight:SetText( "" )
|
||||
self.btnRight.DoClick = function( self ) self:GetParent():AddScroll( 1 ) end
|
||||
self.btnRight.Paint = function( panel, w, h ) derma.SkinHook( "Paint", "ButtonRight", panel, w, h ) end
|
||||
|
||||
self.btnGrip = vgui.Create( "DScrollBarGrip", self )
|
||||
self.btnGrip.Paint = function(panel,w,h)
|
||||
local skin = panel:GetSkin()
|
||||
if ( panel:GetDisabled() ) then
|
||||
skin.tex.Scroller.ButtonH_Disabled( 0, 0, w, h )
|
||||
elseif ( panel.Depressed ) then
|
||||
return skin.tex.Scroller.ButtonH_Down( 0, 0, w, h )
|
||||
elseif ( panel.Hovered ) then
|
||||
return skin.tex.Scroller.ButtonH_Hover( 0, 0, w, h )
|
||||
else
|
||||
skin.tex.Scroller.ButtonH_Normal( 0, 0, w, h )
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
self:SetSize( 15, 15 )
|
||||
self:SetHideButtons( false )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SetEnabled( b )
|
||||
|
||||
if ( !b ) then
|
||||
|
||||
self.Offset = 0
|
||||
self:SetScroll( 0 )
|
||||
self.HasChanged = true
|
||||
|
||||
end
|
||||
|
||||
self:SetMouseInputEnabled( b )
|
||||
self:SetVisible( b )
|
||||
|
||||
-- We're probably changing the width of something in our parent
|
||||
-- by appearing or hiding, so tell them to re-do their layout.
|
||||
if ( self.Enabled != b ) then
|
||||
|
||||
self:GetParent():InvalidateLayout()
|
||||
|
||||
if ( self:GetParent().OnScrollbarAppear ) then
|
||||
self:GetParent():OnScrollbarAppear()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.Enabled = b
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Value()
|
||||
|
||||
return self.Pos
|
||||
|
||||
end
|
||||
|
||||
function PANEL:BarScale()
|
||||
|
||||
if ( self.BarSize == 0 ) then return 1 end
|
||||
|
||||
return self.BarSize / ( self.CanvasSize + self.BarSize )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SetUp( _barsize_, _canvassize_ )
|
||||
|
||||
self.BarSize = _barsize_
|
||||
self.CanvasSize = math.max( _canvassize_ - _barsize_, 1 )
|
||||
|
||||
self:SetEnabled( _canvassize_ > _barsize_ )
|
||||
|
||||
self:InvalidateLayout()
|
||||
|
||||
end
|
||||
|
||||
function PANEL:OnMouseWheeled( dlta )
|
||||
|
||||
if ( !self:IsVisible() ) then return false end
|
||||
|
||||
-- We return true if the scrollbar changed.
|
||||
-- If it didn't, we feed the mousehweeling to the parent panel
|
||||
|
||||
return self:AddScroll( dlta * -2 )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:AddScroll( dlta )
|
||||
|
||||
local OldScroll = self:GetScroll()
|
||||
|
||||
dlta = dlta * 25
|
||||
self:SetScroll( self:GetScroll() + dlta )
|
||||
|
||||
return OldScroll != self:GetScroll()
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SetScroll( scrll )
|
||||
|
||||
if ( !self.Enabled ) then self.Scroll = 0 return end
|
||||
|
||||
self.Scroll = math.Clamp( scrll, 0, self.CanvasSize )
|
||||
|
||||
self:InvalidateLayout()
|
||||
|
||||
-- If our parent has a OnVScroll function use that, if
|
||||
-- not then invalidate layout (which can be pretty slow)
|
||||
|
||||
local func = self:GetParent().OnVScroll
|
||||
if ( func ) then
|
||||
|
||||
func( self:GetParent(), self:GetOffset() )
|
||||
|
||||
else
|
||||
|
||||
self:GetParent():InvalidateLayout()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PANEL:AnimateTo( scrll, length, delay, ease )
|
||||
|
||||
local anim = self:NewAnimation( length, delay, ease )
|
||||
anim.StartPos = self.Scroll
|
||||
anim.TargetPos = scrll
|
||||
anim.Think = function( anim, pnl, fraction )
|
||||
|
||||
pnl:SetScroll( Lerp( fraction, anim.StartPos, anim.TargetPos ) )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PANEL:GetScroll()
|
||||
|
||||
if ( !self.Enabled ) then self.Scroll = 0 end
|
||||
return self.Scroll
|
||||
|
||||
end
|
||||
|
||||
function PANEL:GetOffset()
|
||||
|
||||
if ( !self.Enabled ) then return 0 end
|
||||
return self.Scroll * -1
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
end
|
||||
|
||||
function PANEL:Paint( w, h )
|
||||
|
||||
self:GetSkin().tex.Scroller.TrackH(0,0,w,h)
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed()
|
||||
|
||||
local x, y = self:CursorPos()
|
||||
|
||||
local PageSize = self.BarSize
|
||||
|
||||
if ( x > self.btnGrip.x ) then
|
||||
self:SetScroll( self:GetScroll() + PageSize )
|
||||
else
|
||||
self:SetScroll( self:GetScroll() - PageSize )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PANEL:OnMouseReleased()
|
||||
|
||||
self.Dragging = false
|
||||
self.DraggingCanvas = nil
|
||||
self:MouseCapture( false )
|
||||
|
||||
self.btnGrip.Depressed = false
|
||||
|
||||
end
|
||||
|
||||
function PANEL:OnCursorMoved( x, y )
|
||||
|
||||
if ( !self.Enabled ) then return end
|
||||
if ( !self.Dragging ) then return end
|
||||
|
||||
local x, y = self:ScreenToLocal( gui.MouseX(), 0 )
|
||||
|
||||
-- Uck.
|
||||
x = x - self.btnLeft:GetWide()
|
||||
x = x - self.HoldPos
|
||||
|
||||
local BtnHeight = self:GetTall()
|
||||
if ( self:GetHideButtons() ) then BtnHeight = 0 end
|
||||
|
||||
local TrackSize = self:GetWide() - BtnHeight * 2 - self.btnGrip:GetWide()
|
||||
|
||||
x = x / TrackSize
|
||||
|
||||
self:SetScroll( x * self.CanvasSize )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Grip()
|
||||
|
||||
if ( !self.Enabled ) then return end
|
||||
if ( self.BarSize == 0 ) then return end
|
||||
|
||||
self:MouseCapture( true )
|
||||
self.Dragging = true
|
||||
|
||||
local x, y = self.btnGrip:ScreenToLocal( gui.MouseX(), 0 )
|
||||
self.HoldPos = x
|
||||
|
||||
self.btnGrip.Depressed = true
|
||||
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout()
|
||||
|
||||
local Wide = self:GetTall()
|
||||
local BtnHeight = Wide
|
||||
if ( self:GetHideButtons() ) then BtnHeight = 0 end
|
||||
local Scroll = self:GetScroll() / self.CanvasSize
|
||||
local BarSize = math.max( self:BarScale() * ( self:GetWide() - ( BtnHeight * 2 ) ), 10 )
|
||||
local Track = self:GetWide() - ( BtnHeight * 2 ) - BarSize
|
||||
Track = Track + 1
|
||||
|
||||
Scroll = Scroll * Track
|
||||
|
||||
self.btnGrip:SetPos( BtnHeight + Scroll, 0 )
|
||||
self.btnGrip:SetSize( BarSize, Wide )
|
||||
|
||||
if ( BtnHeight > 0 ) then
|
||||
self.btnLeft:SetPos( 0, 0, Wide, Wide )
|
||||
self.btnLeft:SetSize( BtnHeight, Wide )
|
||||
|
||||
self.btnRight:SetPos( self:GetWide() - BtnHeight, 0 )
|
||||
self.btnRight:SetSize(BtnHeight, Wide )
|
||||
|
||||
self.btnLeft:SetVisible( true )
|
||||
self.btnRight:SetVisible( true )
|
||||
else
|
||||
self.btnLeft:SetVisible( false )
|
||||
self.btnRight:SetVisible( false )
|
||||
self.btnRight:SetSize( BtnHeight, Wide )
|
||||
self.btnLeft:SetSize( BtnHeight, Wide )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local PANEL = vgui.Register("pac_scrollpanel_horizontal", {}, "DPanel")
|
||||
|
||||
AccessorFunc( PANEL, "Padding", "Padding" )
|
||||
AccessorFunc( PANEL, "pnlCanvas", "Canvas" )
|
||||
|
||||
function PANEL:Init()
|
||||
|
||||
self.pnlCanvas = vgui.Create( "Panel", self )
|
||||
self.pnlCanvas.OnMousePressed = function( self, code ) self:GetParent():OnMousePressed( code ) end
|
||||
self.pnlCanvas:SetMouseInputEnabled( true )
|
||||
self.pnlCanvas.PerformLayout = function( pnl )
|
||||
|
||||
self:PerformLayout()
|
||||
self:InvalidateParent()
|
||||
|
||||
end
|
||||
|
||||
-- Create the scroll bar
|
||||
self.VBar = vgui.Create( "pac_horizontal_scrollbar", self )
|
||||
self.VBar:Dock( BOTTOM )
|
||||
|
||||
self:SetPadding( 0 )
|
||||
self:SetMouseInputEnabled( true )
|
||||
|
||||
-- This turns off the engine drawing
|
||||
self:SetPaintBackgroundEnabled( false )
|
||||
self:SetPaintBorderEnabled( false )
|
||||
self:SetPaintBackground( false )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:AddItem( pnl )
|
||||
|
||||
pnl:SetParent( self:GetCanvas() )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:OnChildAdded( child )
|
||||
|
||||
self:AddItem( child )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SizeToContents()
|
||||
|
||||
self:SetSize( self.pnlCanvas:GetSize() )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:GetVBar()
|
||||
|
||||
return self.VBar
|
||||
|
||||
end
|
||||
|
||||
function PANEL:GetCanvas()
|
||||
|
||||
return self.pnlCanvas
|
||||
|
||||
end
|
||||
|
||||
function PANEL:InnerWidth()
|
||||
|
||||
return self:GetCanvas():GetTall()
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Rebuild()
|
||||
|
||||
self:GetCanvas():SizeToChildren( true, false )
|
||||
|
||||
-- Although this behaviour isn't exactly implied, center vertically too
|
||||
if ( self.m_bNoSizing && self:GetCanvas():GetWide() < self:GetWide() ) then
|
||||
|
||||
self:GetCanvas():SetPos( ( self:GetWide() - self:GetCanvas():GetWide() ) * 0.5, 0 )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PANEL:OnMouseWheeled( dlta )
|
||||
|
||||
return self.VBar:OnMouseWheeled( dlta )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:OnVScroll( iOffset )
|
||||
|
||||
self.pnlCanvas:SetPos( iOffset, 0 )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:ScrollToChild( panel )
|
||||
|
||||
self:PerformLayout()
|
||||
|
||||
local x, y = self.pnlCanvas:GetChildPosition( panel )
|
||||
local w, h = panel:GetSize()
|
||||
|
||||
x = x + w * 0.5
|
||||
x = x - self:GetWide() * 0.5
|
||||
|
||||
self.VBar:AnimateTo( x, 0.5, 0, 0.5 )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout()
|
||||
|
||||
local Tall = self.pnlCanvas:GetWide()
|
||||
local Wide = self:GetWide()
|
||||
local YPos = 0
|
||||
|
||||
self:Rebuild()
|
||||
|
||||
self.VBar:SetUp( self:GetWide(), self.pnlCanvas:GetWide() )
|
||||
YPos = self.VBar:GetOffset()
|
||||
|
||||
if ( self.VBar.Enabled ) then Wide = Wide - self.VBar:GetWide() end
|
||||
|
||||
self.pnlCanvas:SetPos( YPos, 0 )
|
||||
self.pnlCanvas:SetWide( Wide )
|
||||
|
||||
self:Rebuild()
|
||||
|
||||
if ( Tall != self.pnlCanvas:GetWide() ) then
|
||||
self.VBar:SetScroll( self.VBar:GetScroll() ) -- Make sure we are not too far down!
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Clear()
|
||||
|
||||
return self.pnlCanvas:Clear()
|
||||
|
||||
end
|
||||
end
|
||||
593
lua/pac3/editor/client/panels/tree.lua
Normal file
593
lua/pac3/editor/client/panels/tree.lua
Normal file
@@ -0,0 +1,593 @@
|
||||
--[[
|
||||
| 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 PANEL = {}
|
||||
|
||||
PANEL.ClassName = "tree"
|
||||
PANEL.Base = "pac_dtree"
|
||||
|
||||
function PANEL:Init()
|
||||
pace.pac_dtree.Init(self)
|
||||
|
||||
self:SetLineHeight(18)
|
||||
self:SetIndentSize(10)
|
||||
|
||||
self.parts = {}
|
||||
|
||||
self:Populate()
|
||||
|
||||
pace.tree = self
|
||||
end
|
||||
|
||||
do
|
||||
local function get_added_nodes(self)
|
||||
local added_nodes = {}
|
||||
for i,v in ipairs(self.added_nodes) do
|
||||
if v.part and v:IsVisible() and v:IsExpanded() then
|
||||
table.insert(added_nodes, v)
|
||||
end
|
||||
end
|
||||
table.sort(added_nodes, function(a, b) return select(2, a:LocalToScreen()) < select(2, b:LocalToScreen()) end)
|
||||
return added_nodes
|
||||
end
|
||||
|
||||
local function scroll_to_node(self, node)
|
||||
timer.Simple(0.1, function()
|
||||
if not self:IsValid() then return end
|
||||
if not node:IsValid() then return end
|
||||
|
||||
local _, y = self:LocalToScreen()
|
||||
local h = self:GetTall()
|
||||
|
||||
local _, node_y = node:LocalToScreen()
|
||||
|
||||
if node_y > y + h or node_y < y then
|
||||
self:ScrollToChild(node)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local info_image = {
|
||||
pace.MiscIcons.info,
|
||||
pace.MiscIcons.warning,
|
||||
pace.MiscIcons.error,
|
||||
}
|
||||
|
||||
function PANEL:Think(...)
|
||||
if not pace.current_part:IsValid() then return end
|
||||
|
||||
if
|
||||
pace.current_part.pace_tree_node and
|
||||
pace.current_part.pace_tree_node:IsValid() and not
|
||||
(
|
||||
pace.BusyWithProperties:IsValid() or
|
||||
pace.ActiveSpecialPanel:IsValid() or
|
||||
pace.editing_viewmodel or
|
||||
pace.editing_hands or
|
||||
pace.properties.search:HasFocus()
|
||||
) and
|
||||
not gui.IsConsoleVisible()
|
||||
then
|
||||
if input.IsKeyDown(KEY_LEFT) then
|
||||
pace.Call("VariableChanged", pace.current_part, "EditorExpand", false)
|
||||
elseif input.IsKeyDown(KEY_RIGHT) then
|
||||
pace.Call("VariableChanged", pace.current_part, "EditorExpand", true)
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_UP) or input.IsKeyDown(KEY_PAGEUP) then
|
||||
local added_nodes = get_added_nodes(self)
|
||||
local offset = input.IsKeyDown(KEY_PAGEUP) and 10 or 1
|
||||
if not self.scrolled_up or self.scrolled_up < os.clock() then
|
||||
for i,v in ipairs(added_nodes) do
|
||||
if v == pace.current_part.pace_tree_node then
|
||||
local node = added_nodes[i - offset] or added_nodes[1]
|
||||
if node then
|
||||
node:DoClick()
|
||||
scroll_to_node(self, node)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.scrolled_up = self.scrolled_up or os.clock() + 0.4
|
||||
end
|
||||
else
|
||||
self.scrolled_up = nil
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_DOWN) or input.IsKeyDown(KEY_PAGEDOWN) then
|
||||
local added_nodes = get_added_nodes(self)
|
||||
local offset = input.IsKeyDown(KEY_PAGEDOWN) and 10 or 1
|
||||
if not self.scrolled_down or self.scrolled_down < os.clock() then
|
||||
for i,v in ipairs(added_nodes) do
|
||||
if v == pace.current_part.pace_tree_node then
|
||||
local node = added_nodes[i + offset] or added_nodes[#added_nodes]
|
||||
if node then
|
||||
node:DoClick()
|
||||
--scroll_to_node(self, node)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.scrolled_down = self.scrolled_down or os.clock() + 0.4
|
||||
end
|
||||
else
|
||||
self.scrolled_down = nil
|
||||
end
|
||||
end
|
||||
|
||||
for _, part in pairs(pac.GetLocalParts()) do
|
||||
local node = part.pace_tree_node
|
||||
if not node or not node:IsValid() then continue end
|
||||
|
||||
if node.add_button then
|
||||
node.add_button:SetVisible(false)
|
||||
end
|
||||
|
||||
if part.Info then
|
||||
local info = part.Info
|
||||
node.info:SetTooltip(info.message)
|
||||
node.info:SetImage(info_image[info.type])
|
||||
node.info:SetVisible(true)
|
||||
else
|
||||
node.info:SetVisible(false)
|
||||
end
|
||||
|
||||
if part.ClassName == "event" then
|
||||
if part.is_active then
|
||||
node.Icon:SetImage("icon16/clock_red.png")
|
||||
else
|
||||
node.Icon:SetImage(part.Icon)
|
||||
end
|
||||
end
|
||||
|
||||
if part.ClassName == "custom_animation" then
|
||||
local anim = part:GetLuaAnimation()
|
||||
if anim then
|
||||
node:SetText(part:GetName() .. " [" .. string.format("%.2f", anim.Frame + anim.FrameDelta) .. "]")
|
||||
end
|
||||
end
|
||||
|
||||
if (part.ClassName == "proxy" or part.ClassName == "event") and part.Name == "" then
|
||||
node:SetText(pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", part:GetName(), part:GetPrintUniqueID()) or part:GetName())
|
||||
end
|
||||
|
||||
if part:IsHiddenCached() then
|
||||
if not node.Icon.event_icon then
|
||||
local pnl = vgui.Create("DImage", node.Icon)
|
||||
pnl:SetImage("icon16/clock_red.png")
|
||||
pnl:SetSize(8, 8)
|
||||
pnl:SetPos(8, 8)
|
||||
pnl:SetVisible(false)
|
||||
node.Icon.event_icon = pnl
|
||||
end
|
||||
|
||||
node.Icon.event_icon:SetVisible(true)
|
||||
else
|
||||
if node.Icon.event_icon then
|
||||
node.Icon.event_icon:SetVisible(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local pnl = vgui.GetHoveredPanel() or NULL
|
||||
|
||||
if pnl:IsValid() then
|
||||
local pnl = pnl:GetParent()
|
||||
|
||||
if IsValid(pnl) and IsValid(pnl.part) then
|
||||
pace.Call("HoverPart", pnl.part)
|
||||
if pnl.add_button then
|
||||
pnl.add_button:SetVisible(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnMouseReleased(mc)
|
||||
if mc == MOUSE_RIGHT then
|
||||
pace.Call("PartMenu")
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetModel(path)
|
||||
if not file.Exists(path, "GAME") then
|
||||
path = player_manager.TranslatePlayerModel(path)
|
||||
if not file.Exists(path, "GAME") then
|
||||
print(path, "is invalid")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local pnl = vgui.Create("SpawnIcon", self)
|
||||
pnl:SetModel(path or "")
|
||||
pnl:SetSize(16, 16)
|
||||
|
||||
self.Icon:Remove()
|
||||
self.Icon = pnl
|
||||
|
||||
self.ModelPath = path
|
||||
end
|
||||
|
||||
function PANEL:GetModel()
|
||||
return self.ModelPath
|
||||
end
|
||||
|
||||
local function install_drag(node)
|
||||
node:SetDraggableName("pac3")
|
||||
|
||||
local old = node.OnDrop
|
||||
function node:OnDrop(child, ...)
|
||||
-- we're hovering on the label, not the actual node
|
||||
-- so get the parent node instead
|
||||
if not child.part then
|
||||
child = child:GetParent()
|
||||
end
|
||||
|
||||
if child.part and child.part:IsValid() then
|
||||
if self.part and self.part:IsValid() and self.part:GetParent() ~= child.part then
|
||||
pace.RecordUndoHistory()
|
||||
self.part:SetParent(child.part)
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
elseif self.part and self.part:IsValid() then
|
||||
if self.part.ClassName ~= "group" then
|
||||
pace.RecordUndoHistory()
|
||||
local group = pac.CreatePart("group", self.part:GetPlayerOwner())
|
||||
group:SetEditorExpand(true)
|
||||
self.part:SetParent(group)
|
||||
pace.RecordUndoHistory()
|
||||
pace.TrySelectPart()
|
||||
|
||||
else
|
||||
pace.RecordUndoHistory()
|
||||
self.part:SetParent()
|
||||
pace.RecordUndoHistory()
|
||||
pace.RefreshTree(true)
|
||||
end
|
||||
end
|
||||
|
||||
return old(self, child, ...)
|
||||
end
|
||||
|
||||
function node:DroppedOn(child)
|
||||
|
||||
if not child.part then
|
||||
child = child:GetParent()
|
||||
end
|
||||
|
||||
self:InsertNode(child)
|
||||
self:SetExpanded(true)
|
||||
|
||||
if child.part and child.part:IsValid() then
|
||||
if self.part and self.part:IsValid() and child.part:GetParent() ~= self.part then
|
||||
pace.RecordUndoHistory()
|
||||
child.part:SetParent(self.part)
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local function install_expand(node)
|
||||
local old = node.SetExpanded
|
||||
node.SetExpanded = function(self, b, ...)
|
||||
if self.part and self.part:IsValid() then
|
||||
self.part:SetEditorExpand(b)
|
||||
return old(self, b, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local old = node.Expander.OnMousePressed
|
||||
node.Expander.OnMousePressed = function(pnl, code, ...)
|
||||
old(pnl, code, ...)
|
||||
|
||||
if code == MOUSE_RIGHT then
|
||||
local menu = DermaMenu()
|
||||
menu:SetPos(input.GetCursorPos())
|
||||
menu:MakePopup()
|
||||
|
||||
menu:AddOption(L"collapse all", function()
|
||||
node.part:CallRecursive('SetEditorExpand', false)
|
||||
pace.RefreshTree(true)
|
||||
end):SetImage('icon16/arrow_in.png')
|
||||
|
||||
menu:AddOption(L"expand all", function()
|
||||
node.part:CallRecursive('SetEditorExpand', true)
|
||||
pace.RefreshTree(true)
|
||||
end):SetImage('icon16/arrow_down.png')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fix_folder_funcs = function(tbl)
|
||||
tbl.MakeFolder = function() end
|
||||
tbl.FilePopulateCallback = function() end
|
||||
tbl.FilePopulate = function() end
|
||||
tbl.PopulateChildren = function() end
|
||||
tbl.ChildExpanded = function() end
|
||||
tbl.PopulateChildrenAndSelf = function() end
|
||||
return tbl
|
||||
end
|
||||
|
||||
local function node_layout(self, ...)
|
||||
pace.pac_dtree_node.PerformLayout(self, ...)
|
||||
if self.Label then
|
||||
self.Label:SetFont(pace.CurrentFont)
|
||||
--self.Label:SetTextColor(derma.Color("text_dark", self, color_black))
|
||||
end
|
||||
|
||||
if self.add_button then
|
||||
local x = self.Label:GetPos() + self.Label:GetTextInset() + 4
|
||||
surface.SetFont(pace.CurrentFont)
|
||||
local w = surface.GetTextSize(self.Label:GetText())
|
||||
self.add_button:SetPos(x + w, (self.Label:GetTall() - self.add_button:GetTall()) / 2)
|
||||
end
|
||||
|
||||
if self.info then
|
||||
local is_adding = self.add_button:IsVisible()
|
||||
local x = self.Label:GetPos() + self.Label:GetTextInset() + (is_adding and self.add_button:GetWide() + 4 or 4)
|
||||
surface.SetFont(pace.CurrentFont)
|
||||
local w = surface.GetTextSize(self.Label:GetText())
|
||||
self.info:SetPos(x + w, (self.Label:GetTall() - self.info:GetTall()) / 2)
|
||||
end
|
||||
end
|
||||
|
||||
local function add_parts_menu(node)
|
||||
pace.Call("AddPartMenu", node.part)
|
||||
end
|
||||
|
||||
-- a hack, because creating a new node button will mess up the layout
|
||||
function PANEL:AddNode(...)
|
||||
|
||||
if self.RootNode then
|
||||
install_drag(self.RootNode)
|
||||
end
|
||||
|
||||
local node = fix_folder_funcs((self.RootNode and pace.pac_dtree.AddNode or pace.pac_dtree_node.AddNode)(self, ...))
|
||||
install_expand(node)
|
||||
install_drag(node)
|
||||
|
||||
local add_button = node:Add("DImageButton")
|
||||
add_button:SetImage(pace.MiscIcons.new)
|
||||
add_button:SetSize(16, 16)
|
||||
add_button:SetVisible(false)
|
||||
add_button.DoClick = function() add_parts_menu(node) pace.Call("PartSelected", node.part) end
|
||||
add_button.DoRightClick = function() node:DoRightClick() end
|
||||
node.add_button = add_button
|
||||
node.SetModel = self.SetModel
|
||||
node.GetModel = self.GetModel
|
||||
node.AddNode = PANEL.AddNode
|
||||
node.PerformLayout = node_layout
|
||||
|
||||
local info = node:Add("DImageButton")
|
||||
info:SetImage(pace.MiscIcons.info)
|
||||
info:SetSize(16, 16)
|
||||
info:SetVisible(false)
|
||||
info.DoClick = function() pace.MessagePrompt(info:GetTooltip()) end
|
||||
node.info = info
|
||||
|
||||
return node
|
||||
end
|
||||
|
||||
local enable_model_icons = CreateClientConVar("pac_editor_model_icons", "1")
|
||||
|
||||
function PANEL:PopulateParts(node, parts, children)
|
||||
fix_folder_funcs(node)
|
||||
|
||||
local temp = {}
|
||||
for k,v in pairs(parts) do
|
||||
table.insert(temp, v)
|
||||
end
|
||||
parts = temp
|
||||
|
||||
local tbl = {}
|
||||
|
||||
table.sort(parts, function(a,b)
|
||||
return a and b and a:GetName() < b:GetName()
|
||||
end)
|
||||
|
||||
for key, val in pairs(parts) do
|
||||
if not val:HasChildren() then
|
||||
table.insert(tbl, val)
|
||||
end
|
||||
end
|
||||
|
||||
for key, val in pairs(parts) do
|
||||
if val:HasChildren() then
|
||||
table.insert(tbl, val)
|
||||
end
|
||||
end
|
||||
|
||||
for key, part in pairs(tbl) do
|
||||
key = part.Id
|
||||
|
||||
if not part:GetShowInEditor() then goto CONTINUE end
|
||||
|
||||
if not part:HasParent() or children then
|
||||
local part_node
|
||||
|
||||
if IsValid(part.pace_tree_node) then
|
||||
part_node = part.pace_tree_node
|
||||
elseif IsValid(self.parts[key]) then
|
||||
part_node = self.parts[key]
|
||||
else
|
||||
part_node = node:AddNode(pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", part:GetName(), part:GetPrintUniqueID()) or part:GetName())
|
||||
end
|
||||
|
||||
fix_folder_funcs(part_node)
|
||||
|
||||
if part.Description then part_node:SetTooltip(L(part.Description)) end
|
||||
|
||||
part.pace_tree_node = part_node
|
||||
part_node.part = part
|
||||
|
||||
self.parts[key] = part_node
|
||||
|
||||
part_node.DoClick = function()
|
||||
if not part:IsValid() then return end
|
||||
pace.Call("PartSelected", part)
|
||||
|
||||
--part_node.add_button:SetVisible(true)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
part_node.DoRightClick = function()
|
||||
if not part:IsValid() then return end
|
||||
|
||||
pace.Call("PartMenu", part)
|
||||
pace.Call("PartSelected", part)
|
||||
part_node:InternalDoClick()
|
||||
return true
|
||||
end
|
||||
|
||||
if
|
||||
enable_model_icons:GetBool() and
|
||||
part.is_model_part and
|
||||
part.GetModel and
|
||||
part:GetOwner():IsValid()
|
||||
then
|
||||
part_node:SetModel(part:GetOwner():GetModel(), part.Icon)
|
||||
elseif isstring(part.Icon) then
|
||||
part_node.Icon:SetImage(part.Icon)
|
||||
end
|
||||
|
||||
self:PopulateParts(part_node, part:GetChildren(), true)
|
||||
|
||||
if part.newly_created then
|
||||
part_node:SetSelected(true)
|
||||
|
||||
local function expand(part)
|
||||
if part:HasParent() and part.Parent.pace_tree_node then
|
||||
part.Parent.pace_tree_node:SetExpanded(true)
|
||||
expand(part.Parent)
|
||||
end
|
||||
end
|
||||
|
||||
expand(part)
|
||||
|
||||
part_node:SetSelected(true)
|
||||
part.newly_created = nil
|
||||
else
|
||||
part_node:SetSelected(false)
|
||||
part_node:SetExpanded(part:GetEditorExpand())
|
||||
end
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SelectPart(part)
|
||||
for key, node in pairs(self.parts) do
|
||||
if not node.part or not node.part:IsValid() then
|
||||
node:Remove()
|
||||
self.parts[key] = nil
|
||||
else
|
||||
if node.part == part then
|
||||
node:SetSelected(true)
|
||||
else
|
||||
node:SetSelected(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:Populate(reset)
|
||||
|
||||
self:SetLineHeight(18)
|
||||
self:SetIndentSize(2)
|
||||
|
||||
for key, node in pairs(self.parts) do
|
||||
if reset or (not node.part or not node.part:IsValid()) then
|
||||
node:Remove()
|
||||
self.parts[key] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--[[self.m_pSelectedItem = nil
|
||||
|
||||
for key, node in pairs(self:GetItems()) do
|
||||
node:Remove()
|
||||
end]]
|
||||
|
||||
self:PopulateParts(self, pac.GetLocalParts())
|
||||
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
pace.RegisterPanel(PANEL)
|
||||
|
||||
local function remove_node(part)
|
||||
if not part:GetShowInEditor() then return end
|
||||
|
||||
if (part.pace_tree_node or NULL):IsValid() then
|
||||
part.pace_tree_node:SetForceShowExpander()
|
||||
part.pace_tree_node:GetRoot().m_pSelectedItem = nil
|
||||
part.pace_tree_node:Remove()
|
||||
pace.RefreshTree()
|
||||
end
|
||||
end
|
||||
|
||||
pac.AddHook("pac_OnPartRemove", "pace_remove_tree_nodes", remove_node)
|
||||
|
||||
|
||||
local last_refresh = 0
|
||||
local function refresh(part)
|
||||
if last_refresh > SysTime() then return end
|
||||
if not part:GetShowInEditor() then return end
|
||||
|
||||
|
||||
last_refresh = SysTime() + 0.1
|
||||
timer.Simple(0, function()
|
||||
if not part:IsValid() then return end
|
||||
if not part:GetShowInEditor() then return end
|
||||
|
||||
pace.RefreshTree(true)
|
||||
end)
|
||||
end
|
||||
|
||||
pac.AddHook("pac_OnWoreOutfit", "pace_refresh_tree_nodes", refresh)
|
||||
pac.AddHook("pac_OnPartParent", "pace_refresh_tree_nodes", refresh)
|
||||
pac.AddHook("pac_OnPartCreated", "pace_refresh_tree_nodes", refresh)
|
||||
|
||||
pac.AddHook("pace_OnVariableChanged", "pace_create_tree_nodes", function(part, key, val)
|
||||
if key == "EditorExpand" then
|
||||
local node = part.pace_tree_node
|
||||
if IsValid(node) then
|
||||
node:SetExpanded(val)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function pace.RefreshTree(reset)
|
||||
if pace.tree:IsValid() then
|
||||
timer.Create("pace_refresh_tree", 0.01, 1, function()
|
||||
if pace.tree:IsValid() then
|
||||
pace.tree:Populate(reset)
|
||||
pace.tree.RootNode:SetExpanded(true, true) -- why do I have to do this?
|
||||
|
||||
pace.TrySelectPart()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
if Entity(1):IsPlayer() and not PAC_RESTART and not VLL2_FILEDEF then
|
||||
pace.OpenEditor()
|
||||
pace.RefreshTree(true)
|
||||
end
|
||||
209
lua/pac3/editor/client/panels/web_browser.lua
Normal file
209
lua/pac3/editor/client/panels/web_browser.lua
Normal file
@@ -0,0 +1,209 @@
|
||||
--[[
|
||||
| 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 PANEL = {}
|
||||
|
||||
PANEL.Base = "DFrame"
|
||||
PANEL.ClassName = "web_browser"
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetTitle("Web Browser")
|
||||
self:SetDeleteOnClose(false)
|
||||
self:ShowCloseButton(true)
|
||||
self:SetDraggable(true)
|
||||
self:SetSizable(true)
|
||||
|
||||
local top = vgui.Create("EditablePanel", self)
|
||||
top:Dock(TOP)
|
||||
top:SetTall(24)
|
||||
self.top = top
|
||||
|
||||
local btn = vgui.Create("DButton", top)
|
||||
btn:SetText("Back")
|
||||
btn:SizeToContents()
|
||||
btn:SetWide(btn:GetWide()+8)
|
||||
btn:Dock(LEFT)
|
||||
btn.DoClick = function()
|
||||
self.browser:RunJavascript("history.back();")
|
||||
end
|
||||
|
||||
local btn = vgui.Create("DButton", top)
|
||||
btn:SetText("Forward")
|
||||
btn:SizeToContents()
|
||||
btn:SetWide(btn:GetWide()+8)
|
||||
btn:Dock(LEFT)
|
||||
btn.DoClick = function()
|
||||
self.browser:RunJavascript("history.forward();")
|
||||
end
|
||||
|
||||
local btn = vgui.Create("DButton", top)
|
||||
btn:SetText("Refresh")
|
||||
btn:SizeToContents()
|
||||
btn:SetWide(btn:GetWide() + 8)
|
||||
btn:Dock(LEFT)
|
||||
|
||||
btn.DoClick = function()
|
||||
self.browser:RunJavascript("location.reload(true);")
|
||||
end
|
||||
|
||||
btn.Paint = function(btn,w,h)
|
||||
DButton.Paint(btn,w,h)
|
||||
|
||||
if self.loaded then
|
||||
if self.browser:IsLoading() then
|
||||
self.loaded = false
|
||||
end
|
||||
|
||||
surface.SetDrawColor(100, 240, 50, 200)
|
||||
surface.DrawRect(1, 1, w-2, h-2)
|
||||
end
|
||||
|
||||
if self.browser:IsLoading() then
|
||||
surface.SetDrawColor(240 + math.sin(RealTime()*10)*15, 100, 50, 200)
|
||||
surface.DrawRect(1, 1, w-2, h-2)
|
||||
end
|
||||
end
|
||||
|
||||
local entry = vgui.Create("DTextEntry", top)
|
||||
self.entry = entry
|
||||
entry:Dock(FILL)
|
||||
entry:SetTall( 24)
|
||||
|
||||
entry.OnEnter = function(entry)
|
||||
local val = entry:GetText()
|
||||
local js,txt = val:match("javascript:(.+)")
|
||||
|
||||
if js and txt then
|
||||
self.browser:QueueJavascript(txt)
|
||||
return
|
||||
end
|
||||
|
||||
self:OpenURL(val)
|
||||
end
|
||||
|
||||
local browser = vgui.Create("DHTML", self)
|
||||
self.browser = browser
|
||||
browser:Dock(FILL)
|
||||
browser.Paint = function() end
|
||||
browser.OpeningURL = pac.Message
|
||||
browser.FinishedURL = pac.Message
|
||||
browser:AddFunction("gmod", "LoadedURL", function(url, title)
|
||||
self:LoadedURL(url,title)
|
||||
end)
|
||||
browser:AddFunction("gmod", "dbg", function(...)
|
||||
pac.Message('[Browser] ', ...)
|
||||
end)
|
||||
browser:AddFunction("gmod", "status", function(txt)
|
||||
self:StatusChanged(txt)
|
||||
end)
|
||||
browser.ActionSignal = function(...)
|
||||
pac.Message('[BrowserACT] ', ...)
|
||||
end
|
||||
|
||||
browser.OnKeyCodePressed = function(browser,code)
|
||||
if code == 96 then
|
||||
self.browser:RunJavascript[[location.reload(true);]]
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local status = vgui.Create("DLabel", self)
|
||||
self.status = status
|
||||
status:SetText""
|
||||
status:Dock(BOTTOM)
|
||||
end
|
||||
|
||||
function PANEL:StatusChanged(txt)
|
||||
if self.statustxt ~= txt then
|
||||
self.statustxt = txt
|
||||
self.status:SetText(txt or "")
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:LoadedURL(url,title)
|
||||
if self.entry:HasFocus() then return end
|
||||
self.entry:SetText(url)
|
||||
self.loaded = true
|
||||
self:SetTitle(title and title ~= "" and title or "Web browser")
|
||||
end
|
||||
|
||||
function PANEL:OpenURL(url)
|
||||
self.browser:OpenURL(url)
|
||||
self.entry:SetText(url)
|
||||
end
|
||||
|
||||
function PANEL:Think(w,h)
|
||||
self.BaseClass.Think(self,w,h)
|
||||
if input.IsKeyDown(KEY_ESCAPE) then
|
||||
self:Close()
|
||||
end
|
||||
|
||||
if not self.wasloading and self.browser:IsLoading() then
|
||||
self.wasloading = true
|
||||
end
|
||||
if self.wasloading and not self.browser:IsLoading() then
|
||||
self.wasloading = false
|
||||
self.browser:QueueJavascript[[gmod.LoadedURL(document.location.href,document.title); gmod.status(""); ]]
|
||||
self.browser:QueueJavascript[[function alert(str) { console.log("Alert: "+str); }]]
|
||||
self.browser:QueueJavascript[[if (!document.body.style.background) { document.body.style.background = 'white'; }; void 0;]]
|
||||
self.browser:QueueJavascript[[
|
||||
function getLink() {
|
||||
gmod.status(this.href || "-");
|
||||
}
|
||||
function clickLink() {
|
||||
if (this.href) {
|
||||
gmod.LoadedURL(this.href,"Loading...");
|
||||
}
|
||||
gmod.status("Loading...");
|
||||
}
|
||||
var links = document.getElementsByTagName("a");
|
||||
for (i = 0; i < links.length; i++) {
|
||||
links[i].addEventListener('mouseover',getLink,false)
|
||||
links[i].addEventListener('click',clickLink,false)
|
||||
}
|
||||
|
||||
]]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Show()
|
||||
if not self:IsVisible() then
|
||||
self:SetVisible(true)
|
||||
self:MakePopup()
|
||||
self:SetKeyboardInputEnabled(true)
|
||||
self:SetMouseInputEnabled(true)
|
||||
end
|
||||
|
||||
if ValidPanel(self.browser) then
|
||||
self.browser:RequestFocus()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Close()
|
||||
self:SetVisible(false)
|
||||
end
|
||||
|
||||
pace.wiki_panel = NULL
|
||||
|
||||
function pace.ShowWiki(url)
|
||||
if pace.wiki_panel:IsValid() then
|
||||
pace.wiki_panel:Remove()
|
||||
end
|
||||
|
||||
local pnl = pace.CreatePanel("web_browser")
|
||||
pnl:OpenURL(url or pace.WikiURL)
|
||||
pnl:SetSize(ScrW()*0.9, ScrH()*0.8)
|
||||
pnl:Center()
|
||||
pnl:MakePopup()
|
||||
pace.wiki_panel = pnl
|
||||
end
|
||||
|
||||
pace.RegisterPanel(PANEL)
|
||||
622
lua/pac3/editor/client/parts.lua
Normal file
622
lua/pac3/editor/client/parts.lua
Normal file
@@ -0,0 +1,622 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
-- load only when hovered above
|
||||
local function add_expensive_submenu_load(pnl, callback)
|
||||
local old = pnl.OnCursorEntered
|
||||
pnl.OnCursorEntered = function(...)
|
||||
callback()
|
||||
pnl.OnCursorEntered = old
|
||||
return old(...)
|
||||
end
|
||||
end
|
||||
|
||||
function pace.WearParts(temp_wear_filter)
|
||||
local allowed, reason = pac.CallHook("CanWearParts", pac.LocalPlayer)
|
||||
|
||||
if allowed == false then
|
||||
pac.Message(reason or "the server doesn't want you to wear parts for some reason")
|
||||
return
|
||||
end
|
||||
|
||||
return pace.WearOnServer(temp_wear_filter)
|
||||
end
|
||||
|
||||
function pace.OnCreatePart(class_name, name, mdl, no_parent)
|
||||
local part
|
||||
local parent = NULL
|
||||
|
||||
if no_parent then
|
||||
if class_name ~= "group" then
|
||||
local group
|
||||
local parts = pac.GetLocalParts()
|
||||
if table.Count(parts) == 1 then
|
||||
local test = select(2, next(parts))
|
||||
if test.ClassName == "group" then
|
||||
group = test
|
||||
end
|
||||
else
|
||||
group = pac.CreatePart("group")
|
||||
end
|
||||
part = pac.CreatePart(class_name)
|
||||
part:SetParent(group)
|
||||
parent = group
|
||||
else
|
||||
part = pac.CreatePart(class_name)
|
||||
end
|
||||
else
|
||||
if class_name ~= "group" and not next(pac.GetLocalParts()) then
|
||||
pace.Call("CreatePart", "group")
|
||||
end
|
||||
|
||||
part = pac.CreatePart(class_name)
|
||||
|
||||
parent = pace.current_part
|
||||
|
||||
if parent:IsValid() then
|
||||
part:SetParent(parent)
|
||||
elseif class_name ~= "group" then
|
||||
for _, v in pairs(pac.GetLocalParts()) do
|
||||
if v.ClassName == "group" then
|
||||
part:SetParent(v)
|
||||
parent = v
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if name then part:SetName(name) end
|
||||
|
||||
if part.SetModel then
|
||||
if mdl then
|
||||
part:SetModel(mdl)
|
||||
elseif class_name == "model" or class_name == "model2" then
|
||||
part:SetModel("models/pac/default.mdl")
|
||||
end
|
||||
end
|
||||
|
||||
local ply = pac.LocalPlayer
|
||||
|
||||
if part:GetPlayerOwner() == ply then
|
||||
pace.SetViewPart(part)
|
||||
end
|
||||
|
||||
if not input.IsControlDown() then
|
||||
pace.Call("PartSelected", part)
|
||||
end
|
||||
|
||||
part.newly_created = true
|
||||
|
||||
if parent.GetDrawPosition and parent:IsValid() and not parent:HasParent() and parent.OwnerName == "world" and part:GetPlayerOwner() == ply then
|
||||
local data = ply:GetEyeTrace()
|
||||
|
||||
if data.HitPos:Distance(ply:GetPos()) < 1000 then
|
||||
part:SetPosition(data.HitPos)
|
||||
else
|
||||
part:SetPosition(ply:GetPos())
|
||||
end
|
||||
end
|
||||
|
||||
pace.RefreshTree()
|
||||
|
||||
return part
|
||||
end
|
||||
|
||||
function pace.OnPartSelected(part, is_selecting)
|
||||
local parent = part:GetRootPart()
|
||||
|
||||
if parent:IsValid() and (parent.OwnerName == "viewmodel" or parent.OwnerName == "hands") then
|
||||
pace.editing_viewmodel = parent.OwnerName == "viewmodel"
|
||||
pace.editing_hands = parent.OwnerName == "hands"
|
||||
elseif pace.editing_viewmodel or pace.editing_hands then
|
||||
pace.editing_viewmodel = false
|
||||
pace.editing_hands = false
|
||||
end
|
||||
|
||||
pace.current_part = part
|
||||
pace.PopulateProperties(part)
|
||||
pace.mctrl.SetTarget(part)
|
||||
|
||||
pace.SetViewPart(part)
|
||||
|
||||
if pace.Editor:IsValid() then
|
||||
pace.Editor:InvalidateLayout()
|
||||
end
|
||||
|
||||
pace.SafeRemoveSpecialPanel()
|
||||
|
||||
if pace.tree:IsValid() then
|
||||
pace.tree:SelectPart(part)
|
||||
end
|
||||
|
||||
pace.current_part_uid = part.UniqueID
|
||||
|
||||
if not is_selecting then
|
||||
pace.StopSelect()
|
||||
end
|
||||
end
|
||||
|
||||
function pace.OnVariableChanged(obj, key, val, not_from_editor)
|
||||
local valType = type(val)
|
||||
if valType == 'Vector' then
|
||||
val = Vector(val)
|
||||
elseif valType == 'Angle' then
|
||||
val = Angle(val)
|
||||
end
|
||||
|
||||
if not not_from_editor then
|
||||
timer.Create("pace_backup", 1, 1, pace.Backup)
|
||||
|
||||
if not pace.undo_release_varchange then
|
||||
pace.RecordUndoHistory()
|
||||
pace.undo_release_varchange = true
|
||||
end
|
||||
|
||||
timer.Create("pace_undo", 0.25, 1, function()
|
||||
pace.undo_release_varchange = false
|
||||
pace.RecordUndoHistory()
|
||||
end)
|
||||
end
|
||||
|
||||
if key == "OwnerName" then
|
||||
local owner_name = obj:GetProperty(key)
|
||||
if val == "viewmodel" then
|
||||
pace.editing_viewmodel = true
|
||||
elseif val == "hands" then
|
||||
pace.editing_hands = true
|
||||
elseif owner_name == "hands" then
|
||||
pace.editing_hands = false
|
||||
elseif owner_name == "viewmodel" then
|
||||
pace.editing_viewmodel = false
|
||||
end
|
||||
end
|
||||
|
||||
obj:SetProperty(key, val)
|
||||
|
||||
local node = obj.pace_tree_node
|
||||
if IsValid(node) then
|
||||
if key == "Event" then
|
||||
pace.PopulateProperties(obj)
|
||||
elseif key == "Name" then
|
||||
if not obj:HasParent() then
|
||||
pace.RemovePartOnServer(obj:GetUniqueID(), true, true)
|
||||
end
|
||||
node:SetText(val)
|
||||
elseif key == "Model" and val and val ~= "" and isstring(val) then
|
||||
node:SetModel(val)
|
||||
elseif key == "Parent" then
|
||||
local tree = obj.pace_tree_node
|
||||
if IsValid(tree) then
|
||||
node:Remove()
|
||||
tree = tree:GetRoot()
|
||||
if tree:IsValid() then
|
||||
tree:SetSelectedItem(nil)
|
||||
pace.RefreshTree(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if obj.Name == "" then
|
||||
node:SetText(pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", obj:GetName(), obj:GetPrintUniqueID()) or obj:GetName())
|
||||
end
|
||||
end
|
||||
|
||||
if obj.ClassName:StartWith("sound", nil, true) then
|
||||
timer.Create("pace_preview_sound", 0.25, 1, function()
|
||||
obj:OnShow()
|
||||
end)
|
||||
end
|
||||
|
||||
timer.Simple(0, function()
|
||||
if not IsValid(obj) then return end
|
||||
|
||||
local prop_panel = obj.pace_properties and obj.pace_properties[key]
|
||||
|
||||
if IsValid(prop_panel) then
|
||||
local old = prop_panel.OnValueChanged
|
||||
prop_panel.OnValueChanged = function() end
|
||||
prop_panel:SetValue(val)
|
||||
prop_panel.OnValueChanged = old
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
pac.AddHook("pac_OnPartParent", "pace_parent", function(parent, child)
|
||||
pace.Call("VariableChanged", parent, "Parent", child, true)
|
||||
end)
|
||||
|
||||
function pace.GetRegisteredParts()
|
||||
local out = {}
|
||||
for class_name, PART in pairs(pac.GetRegisteredParts()) do
|
||||
local cond = not PART.ClassName:StartWith("base") and
|
||||
PART.show_in_editor ~= false and
|
||||
PART.is_deprecated ~= false
|
||||
|
||||
if cond then
|
||||
table.insert(out, PART)
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
do -- menu
|
||||
local trap
|
||||
function pace.AddRegisteredPartsToMenu(menu, parent)
|
||||
local partsToShow = {}
|
||||
local clicked = false
|
||||
|
||||
hook.Add('Think', menu, function()
|
||||
local ctrl = input.IsControlDown()
|
||||
|
||||
if clicked and not ctrl then
|
||||
menu:SetDeleteSelf(true)
|
||||
RegisterDermaMenuForClose(menu)
|
||||
CloseDermaMenus()
|
||||
return
|
||||
end
|
||||
|
||||
menu:SetDeleteSelf(not ctrl)
|
||||
end)
|
||||
|
||||
hook.Add('CloseDermaMenus', menu, function()
|
||||
clicked = true
|
||||
if input.IsControlDown() then
|
||||
menu:SetVisible(true)
|
||||
RegisterDermaMenuForClose(menu)
|
||||
end
|
||||
end)
|
||||
|
||||
local function add_part(menu, part)
|
||||
local newMenuEntry = menu:AddOption(L(part.FriendlyName or part.ClassName:Replace('_', ' ')), function()
|
||||
pace.RecordUndoHistory()
|
||||
pace.Call("CreatePart", part.ClassName, nil, nil, parent)
|
||||
pace.RecordUndoHistory()
|
||||
trap = true
|
||||
end)
|
||||
|
||||
if part.Icon then
|
||||
newMenuEntry:SetImage(part.Icon)
|
||||
|
||||
if part.Group == "legacy" then
|
||||
local mat = Material(pace.GroupsIcons.experimental)
|
||||
newMenuEntry.m_Image.PaintOver = function(_, w,h)
|
||||
surface.SetMaterial(mat)
|
||||
surface.DrawTexturedRect(2,6,13,13)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local sortedTree = {}
|
||||
|
||||
for _, part in pairs(pace.GetRegisteredParts()) do
|
||||
local group = part.Group or part.Groups or "other"
|
||||
|
||||
if isstring(group) then
|
||||
group = {group}
|
||||
end
|
||||
|
||||
for i, name in ipairs(group) do
|
||||
if not sortedTree[name] then
|
||||
sortedTree[name] = {}
|
||||
sortedTree[name].parts = {}
|
||||
sortedTree[name].icon = pace.GroupsIcons[name]
|
||||
sortedTree[name].name = L(name)
|
||||
end
|
||||
|
||||
partsToShow[part.ClassName] = nil
|
||||
|
||||
if name == part.ClassName or name == part.FriendlyName then
|
||||
sortedTree[name].group_class_name = part.ClassName
|
||||
else
|
||||
table.insert(sortedTree[name].parts, part)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local other = sortedTree.other
|
||||
sortedTree.other = nil
|
||||
|
||||
for group, groupData in pairs(sortedTree) do
|
||||
local sub, pnl = menu:AddSubMenu(groupData.name, function()
|
||||
if groupData.group_class_name then
|
||||
pace.RecordUndoHistory()
|
||||
pace.Call("CreatePart", groupData.group_class_name, nil, nil, parent)
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
end)
|
||||
|
||||
sub.GetDeleteSelf = function() return false end
|
||||
|
||||
if groupData.icon then
|
||||
pnl:SetImage(groupData.icon)
|
||||
end
|
||||
|
||||
trap = false
|
||||
table.sort(groupData.parts, function(a, b) return a.ClassName < b.ClassName end)
|
||||
for i, part in ipairs(groupData.parts) do
|
||||
add_part(sub, part)
|
||||
end
|
||||
|
||||
hook.Add('Think', sub, function()
|
||||
local ctrl = input.IsControlDown()
|
||||
|
||||
if clicked and not ctrl then
|
||||
sub:SetDeleteSelf(true)
|
||||
RegisterDermaMenuForClose(sub)
|
||||
CloseDermaMenus()
|
||||
return
|
||||
end
|
||||
|
||||
sub:SetDeleteSelf(not ctrl)
|
||||
end)
|
||||
|
||||
hook.Add('CloseDermaMenus', sub, function()
|
||||
if input.IsControlDown() and trap then
|
||||
trap = false
|
||||
sub:SetVisible(true)
|
||||
end
|
||||
|
||||
RegisterDermaMenuForClose(sub)
|
||||
end)
|
||||
end
|
||||
|
||||
for i,v in ipairs(other.parts) do
|
||||
add_part(menu, v)
|
||||
end
|
||||
|
||||
for class_name, part in pairs(partsToShow) do
|
||||
local newMenuEntry = menu:AddOption(L((part.FriendlyName or part.ClassName):Replace('_', ' ')), function()
|
||||
pace.RecordUndoHistory()
|
||||
pace.Call("CreatePart", class_name, nil, nil, parent)
|
||||
pace.RecordUndoHistory()
|
||||
end)
|
||||
|
||||
if part.Icon then
|
||||
newMenuEntry:SetImage(part.Icon)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.OnAddPartMenu(obj)
|
||||
local base = vgui.Create("EditablePanel")
|
||||
base:SetPos(input.GetCursorPos())
|
||||
base:SetSize(200, 300)
|
||||
base:MakePopup()
|
||||
|
||||
function base:OnRemove()
|
||||
pac.RemoveHook("VGUIMousePressed", "search_part_menu")
|
||||
end
|
||||
|
||||
local edit = base:Add("DTextEntry")
|
||||
edit:SetTall(20)
|
||||
edit:Dock(TOP)
|
||||
edit:RequestFocus()
|
||||
edit:SetUpdateOnType(true)
|
||||
|
||||
local result = base:Add("DPanel")
|
||||
result:Dock(FILL)
|
||||
|
||||
function edit:OnEnter()
|
||||
if result.found[1] then
|
||||
pace.RecordUndoHistory()
|
||||
pace.Call("CreatePart", result.found[1].ClassName)
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
base:Remove()
|
||||
end
|
||||
|
||||
edit.OnValueChange = function(_, str)
|
||||
result:Clear()
|
||||
result.found = {}
|
||||
|
||||
for _, part in ipairs(pace.GetRegisteredParts()) do
|
||||
if (part.FriendlyName or part.ClassName):find(str, nil, true) then
|
||||
table.insert(result.found, part)
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(result.found, function(a, b) return #a.ClassName < #b.ClassName end)
|
||||
|
||||
for _, part in ipairs(result.found) do
|
||||
local line = result:Add("DButton")
|
||||
line:SetText("")
|
||||
line:SetTall(20)
|
||||
line.DoClick = function()
|
||||
pace.RecordUndoHistory()
|
||||
pace.Call("CreatePart", part.ClassName)
|
||||
base:Remove()
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
local btn = line:Add("DImageButton")
|
||||
btn:SetSize(16, 16)
|
||||
btn:SetPos(4,0)
|
||||
btn:CenterVertical()
|
||||
btn:SetMouseInputEnabled(false)
|
||||
if part.Icon then
|
||||
btn:SetImage(part.Icon)
|
||||
|
||||
if part.Group == "legacy" then
|
||||
local mat = Material(pace.GroupsIcons.experimental)
|
||||
btn.m_Image.PaintOver = function(_, w,h)
|
||||
surface.SetMaterial(mat)
|
||||
surface.DrawTexturedRect(2,6,13,13)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local label = line:Add("DLabel")
|
||||
label:SetTextColor(label:GetSkin().Colours.Category.Line.Text)
|
||||
label:SetText(L((part.FriendlyName or part.ClassName):Replace('_', ' ')))
|
||||
label:SizeToContents()
|
||||
label:MoveRightOf(btn, 4)
|
||||
label:SetMouseInputEnabled(false)
|
||||
label:CenterVertical()
|
||||
|
||||
line:Dock(TOP)
|
||||
end
|
||||
|
||||
base:SetHeight(20 * #result.found + edit:GetTall())
|
||||
end
|
||||
|
||||
edit:OnValueChange("")
|
||||
|
||||
pac.AddHook("VGUIMousePressed", "search_part_menu", function(pnl, code)
|
||||
if code == MOUSE_LEFT or code == MOUSE_RIGHT then
|
||||
if not base:IsOurChild(pnl) then
|
||||
base:Remove()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function pace.Copy(obj)
|
||||
pace.Clipboard = obj:ToTable()
|
||||
end
|
||||
|
||||
function pace.Cut(obj)
|
||||
pace.RecordUndoHistory()
|
||||
pace.Copy(obj)
|
||||
obj:Remove()
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
function pace.Paste(obj)
|
||||
if not pace.Clipboard then return end
|
||||
pace.RecordUndoHistory()
|
||||
local newObj = pac.CreatePart(pace.Clipboard.self.ClassName)
|
||||
newObj:SetTable(pace.Clipboard, true)
|
||||
newObj:SetParent(obj)
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
function pace.PasteProperties(obj)
|
||||
if not pace.Clipboard then return end
|
||||
pace.RecordUndoHistory()
|
||||
local tbl = pace.Clipboard
|
||||
tbl.self.Name = nil
|
||||
tbl.self.ParentUID = nil
|
||||
tbl.self.UniqueID = nil
|
||||
tbl.children = {}
|
||||
obj:SetTable(tbl)
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
function pace.Clone(obj)
|
||||
pace.RecordUndoHistory()
|
||||
obj:Clone()
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
function pace.RemovePart(obj)
|
||||
pace.RecordUndoHistory()
|
||||
obj:Remove()
|
||||
pace.RecordUndoHistory()
|
||||
|
||||
pace.RefreshTree()
|
||||
|
||||
if not obj:HasParent() and obj.ClassName == "group" then
|
||||
pace.RemovePartOnServer(obj:GetUniqueID(), false, true)
|
||||
end
|
||||
end
|
||||
|
||||
function pace.OnPartMenu(obj)
|
||||
local menu = DermaMenu()
|
||||
menu:SetPos(input.GetCursorPos())
|
||||
|
||||
if obj then
|
||||
if not obj:HasParent() then
|
||||
menu:AddOption(L"wear", function() pace.SendPartToServer(obj) end):SetImage(pace.MiscIcons.wear)
|
||||
end
|
||||
|
||||
menu:AddOption(L"copy", function() pace.Copy(obj) end):SetImage(pace.MiscIcons.copy)
|
||||
menu:AddOption(L"paste", function() pace.Paste(obj) end):SetImage(pace.MiscIcons.paste)
|
||||
menu:AddOption(L"cut", function() pace.Cut(obj) end):SetImage('icon16/cut.png')
|
||||
menu:AddOption(L"paste properties", function() pace.PasteProperties(obj) end):SetImage(pace.MiscIcons.replace)
|
||||
menu:AddOption(L"clone", function() pace.Clone(obj) end):SetImage(pace.MiscIcons.clone)
|
||||
|
||||
menu:AddSpacer()
|
||||
end
|
||||
|
||||
pace.AddRegisteredPartsToMenu(menu, not obj)
|
||||
|
||||
menu:AddSpacer()
|
||||
|
||||
if obj then
|
||||
local save, pnl = menu:AddSubMenu(L"save", function() pace.SaveParts() end)
|
||||
pnl:SetImage(pace.MiscIcons.save)
|
||||
add_expensive_submenu_load(pnl, function() pace.AddSaveMenuToMenu(save, obj) end)
|
||||
end
|
||||
|
||||
local load, pnl = menu:AddSubMenu(L"load", function() pace.LoadParts() end)
|
||||
add_expensive_submenu_load(pnl, function() pace.AddSavedPartsToMenu(load, false, obj) end)
|
||||
|
||||
pnl:SetImage(pace.MiscIcons.load)
|
||||
|
||||
if obj then
|
||||
menu:AddSpacer()
|
||||
menu:AddOption(L"remove", function() pace.RemovePart(obj) end):SetImage(pace.MiscIcons.clear)
|
||||
end
|
||||
|
||||
menu:Open()
|
||||
menu:MakePopup()
|
||||
end
|
||||
|
||||
function pace.OnNewPartMenu()
|
||||
pace.current_part = NULL
|
||||
local menu = DermaMenu()
|
||||
menu:MakePopup()
|
||||
menu:SetPos(input.GetCursorPos())
|
||||
|
||||
pace.AddRegisteredPartsToMenu(menu)
|
||||
|
||||
menu:AddSpacer()
|
||||
|
||||
local load, pnl = menu:AddSubMenu(L"load", function() pace.LoadParts() end)
|
||||
pnl:SetImage(pace.MiscIcons.load)
|
||||
add_expensive_submenu_load(pnl, function() pace.AddSavedPartsToMenu(load, false, obj) end)
|
||||
|
||||
menu:AddOption(L"clear", function()
|
||||
pace.ClearParts()
|
||||
end):SetImage(pace.MiscIcons.clear)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
pac.haloex = include("pac3/libraries/haloex.lua")
|
||||
|
||||
function pace.OnHoverPart(self)
|
||||
local tbl = {}
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
table.insert(tbl, ent)
|
||||
end
|
||||
|
||||
for _, child in ipairs(self:GetChildrenList()) do
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() then
|
||||
table.insert(tbl, ent)
|
||||
end
|
||||
end
|
||||
|
||||
if #tbl > 0 then
|
||||
local pulse = math.sin(pac.RealTime * 20) * 0.5 + 0.5
|
||||
pulse = pulse * 255
|
||||
pac.haloex.Add(tbl, Color(pulse, pulse, pulse, 255), 1, 1, 1, true, true, 5, 1, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
83
lua/pac3/editor/client/render_scores.lua
Normal file
83
lua/pac3/editor/client/render_scores.lua
Normal file
@@ -0,0 +1,83 @@
|
||||
--[[
|
||||
| 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 enable = CreateClientConVar("pac_show_profiling_info", 1, true, true)
|
||||
local font = "pac_render_score"
|
||||
|
||||
surface.CreateFont(font, {font = "Arial", shadow = true, size = 14, antialias = false})
|
||||
|
||||
pace.RenderTimes = {} -- render times in seconds
|
||||
|
||||
|
||||
function pace.GetProfilingData(ent)
|
||||
local profile_data = false --pac.profile_info[ent]
|
||||
|
||||
if profile_data then
|
||||
local out = {events = {}}
|
||||
out.times_rendered = profile_data.times_ran
|
||||
|
||||
|
||||
for type, data in pairs(profile_data.types) do
|
||||
out.events[type] = {
|
||||
average_ms = data.total_render_time / out.times_rendered,
|
||||
}
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
end
|
||||
|
||||
timer.Create("pac_render_times", 0.1, 0, function()
|
||||
if not pac.IsEnabled() then return end
|
||||
|
||||
for key, ply in pairs(player.GetHumans()) do
|
||||
local data = pace.GetProfilingData(ply)
|
||||
|
||||
if data then
|
||||
local renderTime = 0
|
||||
|
||||
-- events are "opaque" and "translucent"
|
||||
-- WE DO NOT CALCULATE AN AVERAGE
|
||||
for k,v in pairs(data.events) do
|
||||
renderTime = renderTime + v.average_ms * 0.001
|
||||
end
|
||||
|
||||
pace.RenderTimes[ply:EntIndex()] = renderTime
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
pac.AddHook("HUDPaint", "pac_show_render_times", function()
|
||||
if not pace.IsActive() or not pace.IsFocused() or not enable:GetBool() then return end
|
||||
|
||||
for key, ply in pairs(player.GetHumans()) do
|
||||
if ply == pac.LocalPlayer then goto CONTINUE end
|
||||
|
||||
local pos = ply:EyePos()
|
||||
|
||||
if pos:Distance(pac.EyePos) < 100 then
|
||||
local pos = pos:ToScreen()
|
||||
|
||||
if pos.visible then
|
||||
|
||||
surface.SetFont(font)
|
||||
surface.SetTextColor(255, 255, 255, 255)
|
||||
|
||||
local renderTime = pace.RenderTimes[ply:EntIndex()]
|
||||
|
||||
if renderTime then
|
||||
surface.SetTextPos(pos.x, pos.y)
|
||||
surface.DrawText(string.format("average render time : %.3f ms", renderTime * 1000))
|
||||
end
|
||||
end
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
end)
|
||||
728
lua/pac3/editor/client/saved_parts.lua
Normal file
728
lua/pac3/editor/client/saved_parts.lua
Normal file
@@ -0,0 +1,728 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
-- load only when hovered above
|
||||
local function add_expensive_submenu_load(pnl, callback)
|
||||
local old = pnl.OnCursorEntered
|
||||
pnl.OnCursorEntered = function(...)
|
||||
callback()
|
||||
pnl.OnCursorEntered = old
|
||||
return old(...)
|
||||
end
|
||||
end
|
||||
|
||||
file.CreateDir("pac3")
|
||||
file.CreateDir("pac3/__backup/")
|
||||
file.CreateDir("pac3/__backup_save/")
|
||||
|
||||
function pace.SaveParts(name, prompt_name, override_part, overrideAsUsual)
|
||||
if not name or prompt_name then
|
||||
Derma_StringRequest(
|
||||
L"save parts",
|
||||
L"filename:",
|
||||
prompt_name or pace.LastSaveName or "autoload",
|
||||
|
||||
function(name)
|
||||
pace.LastSaveName = name
|
||||
pace.SaveParts(name, nil, override_part, overrideAsUsual)
|
||||
|
||||
pace.RefreshFiles()
|
||||
end
|
||||
)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
pac.dprint("saving parts %s", name)
|
||||
|
||||
local data = {}
|
||||
|
||||
if not overrideAsUsual then
|
||||
if pace.use_current_part_for_saveload and pace.current_part:IsValid() then
|
||||
override_part = pace.current_part
|
||||
end
|
||||
|
||||
if override_part then
|
||||
data = override_part:ToSaveTable()
|
||||
end
|
||||
elseif override_part then
|
||||
table.insert(data, override_part:ToSaveTable())
|
||||
override_part = nil
|
||||
end
|
||||
|
||||
if #data == 0 then
|
||||
for key, part in pairs(pac.GetLocalParts()) do
|
||||
if not part:HasParent() and part:GetShowInEditor() then
|
||||
table.insert(data, part:ToSaveTable())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
data = hook.Run("pac_pace.SaveParts", data) or data
|
||||
|
||||
if not override_part and #file.Find("pac3/sessions/*", "DATA") > 0 and not name:find("/") then
|
||||
pace.luadata.WriteFile("pac3/sessions/" .. name .. ".txt", data)
|
||||
else
|
||||
if file.Exists("pac3/" .. name .. ".txt", "DATA") then
|
||||
local date = os.date("%y-%m-%d-%H_%M_%S")
|
||||
local read = file.Read("pac3/" .. name .. ".txt", "DATA")
|
||||
file.Write("pac3/__backup_save/" .. name .. "_" .. date .. ".txt", read)
|
||||
|
||||
local files, folders = file.Find("pac3/__backup_save/*", "DATA")
|
||||
|
||||
if #files > 30 then
|
||||
local targetFiles = {}
|
||||
|
||||
for i, filename in ipairs(files) do
|
||||
local time = file.Time("pac3/__backup_save/" .. filename, "DATA")
|
||||
table.insert(targetFiles, {"pac3/__backup_save/" .. filename, time})
|
||||
end
|
||||
|
||||
table.sort(targetFiles, function(a, b)
|
||||
return a[2] > b[2]
|
||||
end)
|
||||
|
||||
for i = 31, #files do
|
||||
file.Delete(targetFiles[i][1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pace.luadata.WriteFile("pac3/" .. name .. ".txt", data)
|
||||
end
|
||||
|
||||
pace.Backup(data, name)
|
||||
end
|
||||
|
||||
local last_backup
|
||||
local maxBackups = CreateConVar("pac_backup_limit", "100", {FCVAR_ARCHIVE}, "Maximal amount of backups")
|
||||
|
||||
function pace.Backup(data, name)
|
||||
name = name or ""
|
||||
|
||||
if not data then
|
||||
data = {}
|
||||
for key, part in pairs(pac.GetLocalParts()) do
|
||||
if not part:HasParent() and part:GetShowInEditor() then
|
||||
table.insert(data, part:ToSaveTable())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #data > 0 then
|
||||
|
||||
local files, folders = file.Find("pac3/__backup/*", "DATA")
|
||||
|
||||
if #files > maxBackups:GetInt() then
|
||||
local temp = {}
|
||||
for key, name in pairs(files) do
|
||||
local time = file.Time("pac3/__backup/" .. name, "DATA")
|
||||
table.insert(temp, {path = "pac3/__backup/" .. name, time = time})
|
||||
end
|
||||
|
||||
table.sort(temp, function(a, b)
|
||||
return a.time > b.time
|
||||
end)
|
||||
|
||||
for i = maxBackups:GetInt() + 1, #files do
|
||||
file.Delete(temp[i].path, "DATA")
|
||||
end
|
||||
end
|
||||
|
||||
local date = os.date("%y-%m-%d-%H_%M_%S")
|
||||
local str = pace.luadata.Encode(data)
|
||||
|
||||
if str ~= last_backup then
|
||||
file.Write("pac3/__backup/" .. (name=="" and name or (name..'_')) .. date .. ".txt", str)
|
||||
last_backup = str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.LoadParts(name, clear, override_part)
|
||||
if not name then
|
||||
local frm = vgui.Create("DFrame")
|
||||
frm:SetTitle(L"parts")
|
||||
local pnl = pace.CreatePanel("browser", frm)
|
||||
|
||||
pnl.OnLoad = function(node)
|
||||
pace.LoadParts(node.FileName, clear, override_part)
|
||||
end
|
||||
|
||||
if #file.Find("pac3/sessions/*", "DATA") > 0 then
|
||||
pnl:SetDir("sessions/")
|
||||
else
|
||||
pnl:SetDir("")
|
||||
end
|
||||
|
||||
pnl:Dock(FILL)
|
||||
|
||||
frm:SetSize(300, 500)
|
||||
frm:MakePopup()
|
||||
frm:Center()
|
||||
|
||||
local btn = vgui.Create("DButton", frm)
|
||||
btn:Dock(BOTTOM)
|
||||
btn:SetText(L"load from url")
|
||||
btn.DoClick = function()
|
||||
Derma_StringRequest(
|
||||
L"load part",
|
||||
L"pastebin urls also work!",
|
||||
"",
|
||||
function(name)
|
||||
pace.LoadParts(name, clear, override_part)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
else
|
||||
if hook.Run("PrePACLoadOutfit", name) == false then
|
||||
return
|
||||
end
|
||||
|
||||
pac.dprint("loading Parts %s", name)
|
||||
|
||||
if name:find("https?://") then
|
||||
local function callback(str)
|
||||
if string.find( str, "<!DOCTYPE html>" ) then
|
||||
pace.MessagePrompt("Invalid URL, the website returned a HTML file. If you're using Github then use the RAW option.", "URL Failed", "OK")
|
||||
return
|
||||
end
|
||||
|
||||
local data, err = pace.luadata.Decode(str)
|
||||
if not data then
|
||||
local message = string.format("URL fail: %s : %s\n", name, err)
|
||||
pace.MessagePrompt(message, "URL Failed", "OK")
|
||||
return
|
||||
end
|
||||
|
||||
pace.LoadPartsFromTable(data, clear, override_part)
|
||||
end
|
||||
|
||||
pac.HTTPGet(name, callback, function(err)
|
||||
pace.MessagePrompt(err, "HTTP Request Failed for " .. name, "OK")
|
||||
end)
|
||||
else
|
||||
name = name:gsub("%.txt", "")
|
||||
|
||||
local data,err = pace.luadata.ReadFile("pac3/" .. name .. ".txt")
|
||||
|
||||
if name == "autoload" and (not data or not next(data)) then
|
||||
local err
|
||||
data,err = pace.luadata.ReadFile("pac3/sessions/" .. name .. ".txt",nil,true)
|
||||
if not data then
|
||||
if err then
|
||||
ErrorNoHalt(("Autoload failed: %s\n"):format(err))
|
||||
end
|
||||
return
|
||||
end
|
||||
elseif not data then
|
||||
ErrorNoHalt(("Decoding %s failed: %s\n"):format(name,err))
|
||||
return
|
||||
end
|
||||
|
||||
pace.LoadPartsFromTable(data, clear, override_part)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
concommand.Add('pac_load_url', function(ply, cmd, args)
|
||||
if not args[1] then return print('[PAC3] No URL specified') end
|
||||
local url = args[1]:Trim()
|
||||
if not url:find("https?://") then return print('[PAC3] Invalid URL specified') end
|
||||
pac.Message('Loading specified URL')
|
||||
if args[2] == nil then args[2] = '1' end
|
||||
pace.LoadParts(url, tobool(args[2]))
|
||||
end)
|
||||
|
||||
function pace.LoadPartsFromTable(data, clear, override_part)
|
||||
if pace.use_current_part_for_saveload and pace.current_part:IsValid() then
|
||||
override_part = pace.current_part
|
||||
end
|
||||
|
||||
if clear then
|
||||
pace.ClearParts()
|
||||
pace.ClearUndo()
|
||||
else
|
||||
--pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
local partsLoaded = {}
|
||||
|
||||
local copy_id = tostring(data)
|
||||
|
||||
if data.self then
|
||||
local part
|
||||
|
||||
if override_part then
|
||||
part = override_part
|
||||
part:SetTable(data)
|
||||
else
|
||||
part = override_part or pac.CreatePart(data.self.ClassName, nil, data, pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), data.self.UniqueID):IsValid() and copy_id)
|
||||
end
|
||||
|
||||
table.insert(partsLoaded, part)
|
||||
else
|
||||
data = pace.FixBadGrouping(data)
|
||||
data = pace.FixUniqueIDs(data)
|
||||
|
||||
for key, tbl in pairs(data) do
|
||||
local part = pac.CreatePart(tbl.self.ClassName, nil, tbl, pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), tbl.self.UniqueID):IsValid() and copy_id)
|
||||
table.insert(partsLoaded, part)
|
||||
end
|
||||
end
|
||||
|
||||
pace.RefreshTree(true)
|
||||
|
||||
for i, part in ipairs(partsLoaded) do
|
||||
part:CallRecursive('OnOutfitLoaded')
|
||||
part:CallRecursive('PostApplyFixes')
|
||||
end
|
||||
|
||||
pac.LocalPlayer.pac_fix_show_from_render = SysTime() + 1
|
||||
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
local function add_files(tbl, dir)
|
||||
local files, folders = file.Find("pac3/" .. dir .. "/*", "DATA")
|
||||
|
||||
if folders then
|
||||
for key, folder in pairs(folders) do
|
||||
if folder == "__backup" or folder == "objcache" or folder == "__animations" or folder == "__backup_save" then goto CONTINUE end
|
||||
tbl[folder] = {}
|
||||
add_files(tbl[folder], dir .. "/" .. folder)
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
if files then
|
||||
for i, name in pairs(files) do
|
||||
if name:find("%.txt") then
|
||||
local path = "pac3/" .. dir .. "/" .. name
|
||||
|
||||
if file.Exists(path, "DATA") then
|
||||
local data = {}
|
||||
data.Name = name:gsub("%.txt", "")
|
||||
data.FileName = name
|
||||
data.Size = string.NiceSize(file.Size(path, "DATA"))
|
||||
local time = file.Time(path, "DATA")
|
||||
data.LastModified = os.date("%m/%d/%Y %H:%M", time)
|
||||
data.Time = file.Time(path, "DATA")
|
||||
data.Path = path
|
||||
data.RelativePath = (dir .. "/" .. data.Name):sub(2)
|
||||
|
||||
local dat,err=pace.luadata.ReadFile(path)
|
||||
data.Content = dat
|
||||
|
||||
if dat then
|
||||
table.insert(tbl, data)
|
||||
else
|
||||
pac.dprint(("Decoding %s failed: %s\n"):format(path,err))
|
||||
chat.AddText(("Could not load: %s\n"):format(path))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(tbl, function(a,b)
|
||||
if a.Time and b.Time then
|
||||
return a.Name < b.Name
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
end
|
||||
|
||||
function pace.GetSavedParts(dir)
|
||||
if pace.CachedFiles then
|
||||
return pace.CachedFiles
|
||||
end
|
||||
|
||||
local out = {}
|
||||
|
||||
add_files(out, dir or "")
|
||||
|
||||
pace.CachedFiles = out
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
local function populate_part(menu, part, override_part, clear)
|
||||
local name = part.self.Name or ""
|
||||
|
||||
if name == "" then
|
||||
name = part.self.ClassName .. " (no name)"
|
||||
end
|
||||
|
||||
if #part.children > 0 then
|
||||
local menu, pnl = menu:AddSubMenu(name, function()
|
||||
pace.LoadPartsFromTable(part, nil, override_part)
|
||||
end)
|
||||
pnl:SetImage(part.self.Icon)
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
local old = menu.Open
|
||||
menu.Open = function(...)
|
||||
if not menu.pac_opened then
|
||||
for key, part in pairs(part.children) do
|
||||
populate_part(menu, part, override_part, clear)
|
||||
end
|
||||
menu.pac_opened = true
|
||||
end
|
||||
|
||||
return old(...)
|
||||
end
|
||||
else
|
||||
menu:AddOption(name, function()
|
||||
pace.LoadPartsFromTable(part, clear, override_part)
|
||||
end):SetImage(part.self.Icon)
|
||||
end
|
||||
end
|
||||
|
||||
local function populate_parts(menu, tbl, override_part, clear)
|
||||
for key, data in pairs(tbl) do
|
||||
if not data.Path then
|
||||
local menu, pnl = menu:AddSubMenu(key, function()end, data)
|
||||
pnl:SetImage(pace.MiscIcons.load)
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
local old = menu.Open
|
||||
menu.Open = function(...)
|
||||
if not menu.pac_opened then
|
||||
populate_parts(menu, data, override_part, clear)
|
||||
menu.pac_opened = true
|
||||
end
|
||||
|
||||
return old(...)
|
||||
end
|
||||
else
|
||||
local icon = pace.MiscIcons.outfit
|
||||
local parts = data.Content
|
||||
|
||||
if parts.self then
|
||||
icon = parts.self.Icon
|
||||
parts = {parts}
|
||||
end
|
||||
|
||||
local outfit, pnl = menu:AddSubMenu(data.Name, function()
|
||||
pace.LoadParts(data.RelativePath, clear, override_part)
|
||||
end)
|
||||
pnl:SetImage(icon)
|
||||
outfit.GetDeleteSelf = function() return false end
|
||||
|
||||
local old = outfit.Open
|
||||
outfit.Open = function(...)
|
||||
if not outfit.pac_opened then
|
||||
for key, part in pairs(parts) do
|
||||
populate_part(outfit, part, override_part, clear)
|
||||
end
|
||||
outfit.pac_opened = true
|
||||
end
|
||||
|
||||
return old(...)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.AddSavedPartsToMenu(menu, clear, override_part)
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
|
||||
menu:AddOption(L"load from url", function()
|
||||
Derma_StringRequest(
|
||||
L"load parts",
|
||||
L"Some indirect urls from on pastebin, dropbox, github, etc are handled automatically. Pasting the outfit's file contents into the input field will also work.",
|
||||
"",
|
||||
|
||||
function(name)
|
||||
pace.LoadParts(name, clear, override_part)
|
||||
end
|
||||
)
|
||||
end):SetImage(pace.MiscIcons.url)
|
||||
|
||||
menu:AddOption(L"load from clipboard", function()
|
||||
pace.MultilineStringRequest(
|
||||
L"load parts from clipboard",
|
||||
L"Paste the outfits content here.",
|
||||
"",
|
||||
|
||||
function(name)
|
||||
local data,err = pace.luadata.Decode(name)
|
||||
if data then
|
||||
pace.LoadPartsFromTable(data, clear, override_part)
|
||||
end
|
||||
end
|
||||
)
|
||||
end):SetImage(pace.MiscIcons.paste)
|
||||
|
||||
if not override_part and pace.example_outfits then
|
||||
local examples, pnl = menu:AddSubMenu(L"examples")
|
||||
pnl:SetImage(pace.MiscIcons.help)
|
||||
examples.GetDeleteSelf = function() return false end
|
||||
|
||||
local sorted = {}
|
||||
for k,v in pairs(pace.example_outfits) do sorted[#sorted + 1] = {k = k, v = v} end
|
||||
table.sort(sorted, function(a, b) return a.k < b.k end)
|
||||
|
||||
for _, data in pairs(sorted) do
|
||||
examples:AddOption(data.k, function() pace.LoadPartsFromTable(data.v) end)
|
||||
:SetImage(pace.MiscIcons.outfit)
|
||||
end
|
||||
end
|
||||
|
||||
menu:AddSpacer()
|
||||
|
||||
local tbl = pace.GetSavedParts()
|
||||
populate_parts(menu, tbl, override_part, clear)
|
||||
|
||||
menu:AddSpacer()
|
||||
|
||||
local backups, pnl = menu:AddSubMenu(L"backups")
|
||||
pnl:SetImage(pace.MiscIcons.clone)
|
||||
backups.GetDeleteSelf = function() return false end
|
||||
|
||||
add_expensive_submenu_load(pnl, function()
|
||||
local files = file.Find("pac3/__backup/*", "DATA")
|
||||
local files2 = {}
|
||||
|
||||
for i, filename in ipairs(files) do
|
||||
table.insert(files2, {filename, file.Time("pac3/__backup/" .. filename, "DATA")})
|
||||
end
|
||||
|
||||
table.sort(files2, function(a, b)
|
||||
return a[2] > b[2]
|
||||
end)
|
||||
|
||||
for _, data in pairs(files2) do
|
||||
local name = data[1]
|
||||
local full_path = "pac3/__backup/" .. name
|
||||
local friendly_name = os.date("%m/%d/%Y %H:%M:%S ", file.Time(full_path, "DATA")) .. string.NiceSize(file.Size(full_path, "DATA"))
|
||||
backups:AddOption(friendly_name, function() pace.LoadParts("__backup/" .. name, true) end)
|
||||
:SetImage(pace.MiscIcons.outfit)
|
||||
end
|
||||
end)
|
||||
|
||||
local backups, pnl = menu:AddSubMenu(L"outfit backups")
|
||||
pnl:SetImage(pace.MiscIcons.clone)
|
||||
backups.GetDeleteSelf = function() return false end
|
||||
|
||||
add_expensive_submenu_load(pnl, function()
|
||||
local files = file.Find("pac3/__backup_save/*", "DATA")
|
||||
local files2 = {}
|
||||
|
||||
for i, filename in ipairs(files) do
|
||||
table.insert(files2, {filename, file.Time("pac3/__backup_save/" .. filename, "DATA")})
|
||||
end
|
||||
|
||||
table.sort(files2, function(a, b)
|
||||
return a[2] > b[2]
|
||||
end)
|
||||
|
||||
for _, data in pairs(files2) do
|
||||
local name = data[1]
|
||||
local stamp = data[2]
|
||||
local nicename = name
|
||||
local date = os.date("_%y-%m-%d-%H_%M_%S", stamp)
|
||||
|
||||
if nicename:find(date, 1, true) then
|
||||
nicename = nicename:Replace(date, os.date(" %m/%d/%Y %H:%M:%S", stamp))
|
||||
end
|
||||
|
||||
backups:AddOption(nicename:Replace(".txt", "") .. " (" .. string.NiceSize(file.Size("pac3/__backup_save/" .. name, "DATA")) .. ")",
|
||||
function()
|
||||
pace.LoadParts("__backup_save/" .. name, true)
|
||||
end)
|
||||
:SetImage(pace.MiscIcons.outfit)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function populate_parts(menu, tbl, dir, override_part)
|
||||
dir = dir or ""
|
||||
menu:AddOption(L"new file", function() pace.SaveParts(nil, dir .. "/", override_part) end)
|
||||
:SetImage("icon16/page_add.png")
|
||||
|
||||
menu:AddOption(L"new directory", function()
|
||||
Derma_StringRequest(
|
||||
L"new directory",
|
||||
L"name:",
|
||||
"",
|
||||
|
||||
function(name)
|
||||
file.CreateDir("pac3/" .. dir .. "/" .. name)
|
||||
pace.RefreshFiles()
|
||||
end
|
||||
)
|
||||
end)
|
||||
:SetImage("icon16/folder_add.png")
|
||||
|
||||
menu:AddOption(L"to clipboard", function()
|
||||
local data = {}
|
||||
for key, part in pairs(pac.GetLocalParts()) do
|
||||
if not part:HasParent() and part:GetShowInEditor() then
|
||||
table.insert(data, part:ToSaveTable())
|
||||
end
|
||||
end
|
||||
SetClipboardText(pace.luadata.Encode(data):sub(1, -1))
|
||||
end)
|
||||
:SetImage(pace.MiscIcons.copy)
|
||||
|
||||
menu:AddSpacer()
|
||||
for key, data in pairs(tbl) do
|
||||
if not data.Path then
|
||||
local menu, pnl = menu:AddSubMenu(key, function()end, data)
|
||||
pnl:SetImage(pace.MiscIcons.load)
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
populate_parts(menu, data, dir .. "/" .. key, override_part)
|
||||
else
|
||||
local parts = data.Content
|
||||
|
||||
if parts[1] then
|
||||
local menu, pnl = menu:AddSubMenu(data.Name, function() pace.SaveParts(nil, data.RelativePath, override_part) end)
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
pnl:SetImage(pace.MiscIcons.outfit)
|
||||
|
||||
menu:AddOption(L"delete", function()
|
||||
file.Delete("pac3/" .. data.RelativePath .. ".txt", "DATA")
|
||||
pace.RefreshFiles()
|
||||
end):SetImage(pace.MiscIcons.clear)
|
||||
|
||||
pnl:SetImage(pace.MiscIcons.outfit)
|
||||
elseif parts.self then
|
||||
menu:AddOption(data.Name, function() pace.SaveParts(nil, data.RelativePath, override_part) end)
|
||||
:SetImage(parts.self.Icon)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if dir ~= "" then
|
||||
menu:AddSpacer()
|
||||
|
||||
menu:AddOption(L"delete directory", function()
|
||||
Derma_Query(
|
||||
L"Are you sure you want to delete data/pac3" .. dir .. "/* and all its files?\nThis cannot be undone!",
|
||||
L"delete directory",
|
||||
|
||||
L"yes", function()
|
||||
local function delete_directory(dir)
|
||||
local files, folders = file.Find(dir .. "*", "DATA")
|
||||
|
||||
for k,v in ipairs(files) do
|
||||
file.Delete(dir .. v)
|
||||
end
|
||||
|
||||
for k,v in ipairs(folders) do
|
||||
delete_directory(dir .. v .. "/")
|
||||
end
|
||||
|
||||
if file.Find(dir .. "*", "DATA")[1] then
|
||||
Derma_Message("Cannot remove the directory.\nMaybe it contains hidden files?", "unable to remove directory", L"ok")
|
||||
else
|
||||
file.Delete(dir)
|
||||
end
|
||||
end
|
||||
delete_directory("pac3/" .. dir .. "/")
|
||||
pace.RefreshFiles()
|
||||
end,
|
||||
|
||||
L"no", function()
|
||||
|
||||
end
|
||||
)
|
||||
end):SetImage("icon16/folder_delete.png")
|
||||
end
|
||||
end
|
||||
|
||||
function pace.AddSaveMenuToMenu(menu, override_part)
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
|
||||
if not override_part then
|
||||
menu:AddOption(L"auto load (your spawn outfit)", function()
|
||||
pace.SaveParts("autoload", nil, override_part)
|
||||
pace.RefreshFiles()
|
||||
end)
|
||||
:SetImage(pace.MiscIcons.autoload)
|
||||
menu:AddSpacer()
|
||||
end
|
||||
|
||||
local tbl = pace.GetSavedParts()
|
||||
populate_parts(menu, tbl, nil, override_part)
|
||||
end
|
||||
|
||||
-- this fixes parts that are using the same uniqueid as other parts because of some bugs in older versions
|
||||
function pace.FixUniqueIDs(data)
|
||||
local ids = {}
|
||||
|
||||
local function iterate(part)
|
||||
ids[part.self.UniqueID] = ids[part.self.UniqueID] or {}
|
||||
|
||||
table.insert(ids[part.self.UniqueID], part)
|
||||
|
||||
for key, part in pairs(part.children) do
|
||||
iterate(part)
|
||||
end
|
||||
end
|
||||
|
||||
for key, part in pairs(data) do
|
||||
iterate(part)
|
||||
end
|
||||
|
||||
for key, val in pairs(ids) do
|
||||
if #val > 1 then
|
||||
for key, part in pairs(val) do
|
||||
pac.dprint("Part (%s using model %s) named %q has %i other parts with the same unique id. Fixing!", part.self.ClassName, part.self.Name, part.self.Model or "", #val)
|
||||
part.self.UniqueID = pac.Hash()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
-- this is for fixing parts that are not in a group
|
||||
|
||||
function pace.FixBadGrouping(data)
|
||||
local parts = {}
|
||||
local other = {}
|
||||
|
||||
for key, part in pairs(data) do
|
||||
if part.self.ClassName ~= "group" then
|
||||
table.insert(parts, part)
|
||||
else
|
||||
table.insert(other, part)
|
||||
end
|
||||
end
|
||||
|
||||
if #parts > 0 then
|
||||
local out = {
|
||||
{
|
||||
["self"] = {
|
||||
["EditorExpand"] = true,
|
||||
["ClassName"] = "group",
|
||||
["UniqueID"] = pac.Hash(),
|
||||
["Name"] = "automatic group",
|
||||
},
|
||||
|
||||
["children"] = parts,
|
||||
},
|
||||
}
|
||||
|
||||
for k,v in pairs(other) do
|
||||
table.insert(out, v)
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
399
lua/pac3/editor/client/select.lua
Normal file
399
lua/pac3/editor/client/select.lua
Normal file
@@ -0,0 +1,399 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
pace.selectControl = {}
|
||||
local selectControl = pace.selectControl
|
||||
|
||||
function selectControl.VecToScreen(vec)
|
||||
return vec:ToScreen()
|
||||
end
|
||||
|
||||
function selectControl.GetMousePos()
|
||||
return input.GetCursorPos()
|
||||
end
|
||||
|
||||
function selectControl.GUIMousePressed(mcode) end
|
||||
function selectControl.GUIMouseReleased(mcode) end
|
||||
function selectControl.HUDPaint() end
|
||||
|
||||
local RENDER_ATTACHMENTS = CreateConVar('pac_render_attachments', '0', {FCVAR_ARCHIVE}, 'Render attachments when selecting bones')
|
||||
|
||||
function pace.ToggleRenderAttachments()
|
||||
RunConsoleCommand('pac_render_attachments', RENDER_ATTACHMENTS:GetBool() and '0' or '1')
|
||||
end
|
||||
|
||||
local font_name = "pac_select"
|
||||
local font_scale = 0.05
|
||||
|
||||
surface.CreateFont(
|
||||
font_name,
|
||||
{
|
||||
font = "DejaVu Sans",
|
||||
size = 500 * font_scale,
|
||||
weight = 800,
|
||||
antialias = true,
|
||||
additive = true,
|
||||
}
|
||||
)
|
||||
|
||||
local font_name_blur = font_name.."_blur"
|
||||
|
||||
surface.CreateFont(
|
||||
font_name_blur,
|
||||
{
|
||||
font = "DejaVu Sans",
|
||||
size = 500 * font_scale,
|
||||
weight = 800,
|
||||
antialias = true,
|
||||
additive = false,
|
||||
blursize = 3,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
local function draw_text(text, color, x, y)
|
||||
surface.SetFont(font_name_blur)
|
||||
surface.SetTextColor(color_black)
|
||||
|
||||
for i=1, 10 do
|
||||
surface.SetTextPos(x,y)
|
||||
surface.DrawText(text)
|
||||
end
|
||||
|
||||
surface.SetFont(font_name)
|
||||
surface.SetTextColor(color)
|
||||
surface.SetTextPos(x,y)
|
||||
surface.DrawText(text)
|
||||
end
|
||||
|
||||
local holding
|
||||
local area = 20
|
||||
local x, y = 0, 0
|
||||
local siz = 5
|
||||
local sizeSelected = 12
|
||||
local sizeHovered = 7
|
||||
local currentSizeSelected = 5
|
||||
|
||||
local hR = 83
|
||||
local hG = 167
|
||||
local hB = 213
|
||||
|
||||
local sR = 148
|
||||
local sG = 67
|
||||
local sB = 201
|
||||
|
||||
local white = surface.GetTextureID("gui/center_gradient.vtf")
|
||||
|
||||
function pace.DrawHUDText(x, y, text, lx, ly, mx, my, selected, line_color)
|
||||
mx = mx or gui.MouseX()
|
||||
my = my or gui.MouseY()
|
||||
|
||||
local color = selected and Color(128, 255, 128) or color_white
|
||||
|
||||
surface.SetDrawColor(line_color or color)
|
||||
|
||||
pace.util.DrawLine(
|
||||
Lerp(0.025, mx, x + lx),
|
||||
Lerp(0.025, my, y + ly),
|
||||
|
||||
Lerp(0.05, x + lx, mx),
|
||||
Lerp(0.05, y + ly, my),
|
||||
selected and 4 or 1
|
||||
)
|
||||
|
||||
surface.SetFont(font_name)
|
||||
|
||||
local w, h = surface.GetTextSize(text)
|
||||
draw_text(text, color, (x + lx) - w / 2, (y + ly) - h / 2)
|
||||
end
|
||||
|
||||
local function checkVisible(pos)
|
||||
return
|
||||
x > pos.x - area and x < pos.x + area and
|
||||
y > pos.y - area and y < pos.y + area
|
||||
end
|
||||
|
||||
local function DrawSelection(pos, r, g, b, sizeToUse)
|
||||
if not pos.visible then return false end
|
||||
surface.SetDrawColor(r, g, b, 255)
|
||||
surface.DrawOutlinedRect(pos.x - (sizeToUse * 0.5), pos.y - (sizeToUse * 0.5), sizeToUse, sizeToUse)
|
||||
surface.SetDrawColor(0, 0, 0, 255)
|
||||
surface.DrawOutlinedRect(pos.x - (sizeToUse * 0.5) - 1, pos.y - (sizeToUse * 0.5) - 1, sizeToUse + 2, sizeToUse + 2)
|
||||
|
||||
return checkVisible(pos)
|
||||
end
|
||||
|
||||
function pace.DrawSelection(pos)
|
||||
return DrawSelection(pos, 255, 255, 255, siz)
|
||||
end
|
||||
|
||||
function pace.DrawSelectionHovered(pos)
|
||||
return DrawSelection(pos, hR, hG, hB, sizeHovered)
|
||||
end
|
||||
|
||||
function pace.DrawSelectionSelected(pos)
|
||||
return DrawSelection(pos, sR, sG, sB, sizeSelected + math.sin(RealTime() * 4) * 3)
|
||||
end
|
||||
|
||||
local function get_friendly_name(ent)
|
||||
local name = ent.Nick and ent:Nick()
|
||||
if not name or name == "" then
|
||||
name = language.GetPhrase(ent:GetClass())
|
||||
end
|
||||
|
||||
return ent:EntIndex() .. " - " .. name
|
||||
end
|
||||
|
||||
function pace.StopSelect()
|
||||
pac.RemoveHook("GUIMouseReleased", "draw_select")
|
||||
pac.RemoveHook("GUIMousePressed", "draw_select")
|
||||
pac.RemoveHook("HUDPaint", "draw_select")
|
||||
function selectControl.GUIMousePressed(mcode) end
|
||||
function selectControl.GUIMouseReleased(mcode) end
|
||||
function selectControl.HUDPaint() end
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
pace.IsSelecting = false
|
||||
end)
|
||||
end
|
||||
|
||||
local function select_something(tblin, check, getpos, getfriendly, callback, selectCallback, poll)
|
||||
local data
|
||||
local selected = {}
|
||||
holding = nil
|
||||
|
||||
local function GUIMousePressed(mcode)
|
||||
if mcode == MOUSE_LEFT then
|
||||
if not selected then
|
||||
pace.StopSelect()
|
||||
end
|
||||
|
||||
holding = Vector(selectControl.GetMousePos())
|
||||
end
|
||||
end
|
||||
|
||||
local function GUIMouseReleased(mcode)
|
||||
if mcode == MOUSE_LEFT then
|
||||
if data then
|
||||
data.dist = nil
|
||||
callback(data)
|
||||
pace.StopSelect()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function HUDPaint()
|
||||
if poll and not poll() then pace.StopSelect() return end
|
||||
|
||||
surface.SetAlphaMultiplier(1)
|
||||
|
||||
x, y = selectControl.GetMousePos()
|
||||
|
||||
local tbl = {}
|
||||
|
||||
for key, value in pairs(tblin) do
|
||||
if check(key, value) then
|
||||
goto CONTINUE
|
||||
end
|
||||
|
||||
local pos = selectControl.VecToScreen(getpos(key, value))
|
||||
local friendly = getfriendly(key, value)
|
||||
|
||||
if checkVisible(pos) then
|
||||
table.insert(tbl, {pos = pos, friendly = friendly, dist = pace.util.FastDistance2D(pos.x, pos.y, x, y), key = key, value = value})
|
||||
else
|
||||
local hit = false
|
||||
if selected then
|
||||
for i, val in ipairs(selected) do
|
||||
if val.key == key and val.value == value then
|
||||
hit = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not hit then
|
||||
pace.DrawSelection(pos)
|
||||
end
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
|
||||
if tbl[1] then
|
||||
table.sort(tbl, function(a, b) return a.dist < b.dist end)
|
||||
|
||||
if not selected or not holding then
|
||||
selected = {}
|
||||
|
||||
local first = tbl[1]
|
||||
|
||||
for i, v in ipairs(tbl) do
|
||||
if math.Round(v.dist / 200) == math.Round(first.dist / 200) then
|
||||
table.insert(selected, v)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if #selected < 3 and first.dist < area / 4 then
|
||||
selected = {first}
|
||||
end
|
||||
end
|
||||
elseif not holding then
|
||||
selected = nil
|
||||
end
|
||||
|
||||
if selected then
|
||||
if #selected == 1 then
|
||||
local v = selected[1]
|
||||
pace.DrawSelectionSelected(v.pos)
|
||||
pace.DrawHUDText(v.pos.x, v.pos.y, L(v.friendly), 0, -30, v.pos.x, v.pos.y)
|
||||
data = v
|
||||
if selectCallback then selectCallback(v.key, v.value) end
|
||||
else
|
||||
table.sort(selected, function(a,b) return L(a.friendly) > L(b.friendly) end)
|
||||
|
||||
local found
|
||||
local rad = math.min(#selected * 30, 400)
|
||||
|
||||
for k, v in ipairs(selected) do
|
||||
local sx = math.sin((k / #selected) * math.pi * 2) * rad
|
||||
local sy = math.cos((k / #selected) * math.pi * 2) * rad
|
||||
|
||||
v.pos = selectControl.VecToScreen(getpos(v.key, v.value))
|
||||
|
||||
if holding and pace.util.FastDistance2D(v.pos.x + sx, v.pos.y + sy, x, y) < area then
|
||||
pace.DrawSelectionSelected(v.pos)
|
||||
pace.DrawHUDText(v.pos.x, v.pos.y, L(v.friendly), sx, sy, v.pos.x, v.pos.y, true)
|
||||
found = v
|
||||
if selectCallback then selectCallback(v.key, v.value) end
|
||||
else
|
||||
pace.DrawSelectionHovered(v.pos)
|
||||
pace.DrawHUDText(v.pos.x, v.pos.y, L(v.friendly), sx, sy, v.pos.x, v.pos.y, false, Color(255, 255, 255, 128))
|
||||
end
|
||||
end
|
||||
|
||||
data = found
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pace.IsSelecting = true
|
||||
|
||||
selectControl.HUDPaint = HUDPaint
|
||||
selectControl.GUIMousePressed = GUIMousePressed
|
||||
selectControl.GUIMouseReleased = GUIMouseReleased
|
||||
|
||||
pac.AddHook("GUIMousePressed", "draw_select", selectControl.GUIMousePressed)
|
||||
pac.AddHook("GUIMouseReleased", "draw_select", selectControl.GUIMouseReleased)
|
||||
pac.AddHook("HUDPaint", "draw_select", selectControl.HUDPaint)
|
||||
end
|
||||
|
||||
function pace.SelectBone(ent, callback, only_movable)
|
||||
if not ent or not ent:IsValid() then return end
|
||||
local tbl = table.Copy(pac.GetModelBones(ent))
|
||||
|
||||
if only_movable then
|
||||
local models = ent:GetModel() and util.GetModelMeshes(ent:GetModel())
|
||||
|
||||
if models then
|
||||
for k, v in pairs(tbl) do
|
||||
if not v.bone then
|
||||
tbl[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(tbl) do
|
||||
if v.is_special or not RENDER_ATTACHMENTS:GetBool() and v.is_attachment then
|
||||
tbl[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
select_something(
|
||||
tbl,
|
||||
|
||||
function() end,
|
||||
|
||||
function(k, v)
|
||||
return pac.GetBonePosAng(ent, k)
|
||||
end,
|
||||
|
||||
function(k, v)
|
||||
return k
|
||||
end,
|
||||
|
||||
callback,
|
||||
|
||||
function (key, val)
|
||||
if val.is_special or val.is_attachment then return end
|
||||
ent.pac_bones_select_target = val.i
|
||||
end,
|
||||
|
||||
function() return ent:IsValid() end
|
||||
)
|
||||
end
|
||||
|
||||
function pace.SelectPart(parts, callback)
|
||||
select_something(
|
||||
parts,
|
||||
|
||||
function(_, part)
|
||||
return part:IsHidden() and part:GetShowInEditor()
|
||||
end,
|
||||
|
||||
function(_, part)
|
||||
if part.GetDrawPosition then
|
||||
return part:GetDrawPosition()
|
||||
end
|
||||
|
||||
local owner = part:GetOwner()
|
||||
if owner:IsValid() then
|
||||
return owner:GetPos()
|
||||
end
|
||||
|
||||
owner = part:GetPlayerOwner()
|
||||
if owner:IsValid() then
|
||||
return owner:GetPos()
|
||||
end
|
||||
end,
|
||||
|
||||
function(_, part)
|
||||
return part:GetName()
|
||||
end,
|
||||
|
||||
function(data) return callback(data.value) end
|
||||
)
|
||||
end
|
||||
|
||||
function pace.SelectEntity(callback)
|
||||
select_something(
|
||||
ents.GetAll(),
|
||||
|
||||
function(_, ent)
|
||||
return
|
||||
not ent:IsValid() or
|
||||
ent:EntIndex() == -1
|
||||
end,
|
||||
|
||||
function(_, ent)
|
||||
return ent:EyePos()
|
||||
end,
|
||||
|
||||
function(_, ent)
|
||||
return get_friendly_name(ent)
|
||||
end,
|
||||
|
||||
function(data) return callback(data.value) end
|
||||
)
|
||||
end
|
||||
44
lua/pac3/editor/client/settings.lua
Normal file
44
lua/pac3/editor/client/settings.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
--[[
|
||||
| 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 PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
local pnl = vgui.Create("DPropertySheet", self)
|
||||
pnl:Dock(FILL)
|
||||
|
||||
local props = pace.FillWearSettings(pnl)
|
||||
|
||||
pnl:AddSheet("Wear / Ignore", props)
|
||||
self.sheet = pnl
|
||||
end
|
||||
|
||||
vgui.Register( "pace_settings", PANEL, "DPanel" )
|
||||
|
||||
function pace.OpenSettings()
|
||||
if IsValid(pace.settings_panel) then
|
||||
pace.settings_panel:Remove()
|
||||
end
|
||||
local pnl = vgui.Create("DFrame")
|
||||
pnl:SetTitle("pac settings")
|
||||
pace.settings_panel = pnl
|
||||
pnl:SetSize(600,600)
|
||||
pnl:MakePopup()
|
||||
pnl:Center()
|
||||
pnl:SetSizable(true)
|
||||
|
||||
local pnl = vgui.Create("pace_settings", pnl)
|
||||
pnl:Dock(FILL)
|
||||
end
|
||||
|
||||
concommand.Add("pace_settings", function()
|
||||
pace.OpenSettings()
|
||||
end)
|
||||
279
lua/pac3/editor/client/shortcuts.lua
Normal file
279
lua/pac3/editor/client/shortcuts.lua
Normal file
@@ -0,0 +1,279 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
function pace.OnShortcutSave()
|
||||
if not IsValid(pace.current_part) then return end
|
||||
|
||||
local part = pace.current_part:GetRootPart()
|
||||
surface.PlaySound("buttons/button9.wav")
|
||||
pace.SaveParts(nil, "part " .. (part:GetName() or "my outfit"), part, true)
|
||||
end
|
||||
|
||||
function pace.OnShortcutWear()
|
||||
if not IsValid(pace.current_part) then return end
|
||||
|
||||
local part = pace.current_part:GetRootPart()
|
||||
surface.PlaySound("buttons/button9.wav")
|
||||
pace.SendPartToServer(part)
|
||||
pace.FlashNotification('Wearing group: ' .. part:GetName())
|
||||
end
|
||||
|
||||
local last = 0
|
||||
|
||||
function pace.CheckShortcuts()
|
||||
if gui.IsConsoleVisible() then return end
|
||||
if not pace.Editor or not pace.Editor:IsValid() then return end
|
||||
if last > RealTime() or input.IsMouseDown(MOUSE_LEFT) then return end
|
||||
|
||||
if input.IsKeyDown(KEY_LALT) and input.IsKeyDown(KEY_E) then
|
||||
pace.Call("ToggleFocus", true)
|
||||
last = RealTime() + 0.2
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_LCONTROL) and input.IsKeyDown(KEY_E) then
|
||||
pace.Call("ToggleFocus")
|
||||
last = RealTime() + 0.2
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_LALT) and input.IsKeyDown(KEY_LCONTROL) and input.IsKeyDown(KEY_P) then
|
||||
RunConsoleCommand("pac_restart")
|
||||
end
|
||||
|
||||
-- Only if the editor is in the foreground
|
||||
if pace.IsFocused() then
|
||||
if input.IsKeyDown(KEY_LCONTROL) and input.IsKeyDown(KEY_S) then
|
||||
pace.Call("ShortcutSave")
|
||||
last = RealTime() + 0.2
|
||||
end
|
||||
|
||||
-- CTRL + (W)ear?
|
||||
if input.IsKeyDown(KEY_LCONTROL) and input.IsKeyDown(KEY_N) then
|
||||
pace.Call("ShortcutWear")
|
||||
last = RealTime() + 0.2
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_LCONTROL) and input.IsKeyDown(KEY_T) then
|
||||
pace.SetTPose(not pace.GetTPose())
|
||||
last = RealTime() + 0.2
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_LCONTROL) and input.IsKeyDown(KEY_F) then
|
||||
pace.properties.search:SetVisible(true)
|
||||
pace.properties.search:RequestFocus()
|
||||
pace.properties.search:SetEnabled(true)
|
||||
pace.property_searching = true
|
||||
|
||||
last = RealTime() + 0.2
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
pac.AddHook("Think", "pace_shortcuts", pace.CheckShortcuts)
|
||||
|
||||
do
|
||||
local hold = false
|
||||
local last = 0
|
||||
|
||||
local function thinkUndo()
|
||||
-- whooaaa
|
||||
-- if input.IsControlDown() and input.IsKeyDown(KEY_X) then
|
||||
-- pace.UndoPosition = math.Round((gui.MouseY() / ScrH()) * #pace.UndoHistory)
|
||||
-- pace.ApplyUndo()
|
||||
-- return
|
||||
-- end
|
||||
|
||||
if not input.IsKeyDown(KEY_Z) and not input.IsKeyDown(KEY_Y) then
|
||||
hold = false
|
||||
end
|
||||
|
||||
if hold then return end
|
||||
|
||||
if input.IsControlDown() and ((input.IsKeyDown(KEY_LSHIFT) and input.IsKeyDown(KEY_Z)) or input.IsKeyDown(KEY_Y)) then
|
||||
pace.Redo()
|
||||
hold = true
|
||||
elseif input.IsControlDown() and input.IsKeyDown(KEY_Z) then
|
||||
pace.Undo()
|
||||
hold = true
|
||||
end
|
||||
end
|
||||
|
||||
local hold = false
|
||||
|
||||
local function thinkCopy()
|
||||
if not input.IsKeyDown(KEY_C) then
|
||||
hold = false
|
||||
end
|
||||
|
||||
if hold or not (input.IsControlDown() and input.IsKeyDown(KEY_C)) then return end
|
||||
|
||||
-- copy
|
||||
hold = true
|
||||
local part = pace.current_part
|
||||
|
||||
if not part or not part:IsValid() then
|
||||
pace.FlashNotification('No part selected to copy')
|
||||
return
|
||||
end
|
||||
|
||||
pace.Copy(part)
|
||||
|
||||
surface.PlaySound("buttons/button9.wav")
|
||||
end
|
||||
|
||||
local hold = false
|
||||
|
||||
local function thinkCut()
|
||||
if not input.IsKeyDown(KEY_X) then
|
||||
hold = false
|
||||
end
|
||||
|
||||
if hold or not (input.IsControlDown() and input.IsKeyDown(KEY_X)) then return end
|
||||
|
||||
-- copy
|
||||
hold = true
|
||||
local part = pace.current_part
|
||||
|
||||
if not part or not part:IsValid() then
|
||||
pace.FlashNotification('No part selected to cut')
|
||||
return
|
||||
end
|
||||
|
||||
pace.Cut(part)
|
||||
|
||||
surface.PlaySound("buttons/button9.wav")
|
||||
end
|
||||
|
||||
local hold = false
|
||||
|
||||
local function thinkDelete()
|
||||
if not input.IsKeyDown(KEY_DELETE) then
|
||||
hold = false
|
||||
end
|
||||
|
||||
if hold or not input.IsKeyDown(KEY_DELETE) then return end
|
||||
|
||||
-- delete
|
||||
hold = true
|
||||
local part = pace.current_part
|
||||
|
||||
if not part or not part:IsValid() then
|
||||
pace.FlashNotification('No part to delete')
|
||||
return
|
||||
end
|
||||
|
||||
pace.RemovePart(part)
|
||||
|
||||
surface.PlaySound("buttons/button9.wav")
|
||||
end
|
||||
|
||||
local REVERSE_COLLAPSE_CONTROLS = CreateConVar('pac_reverse_collapse', '1', {FCVAR_ARCHIVE}, 'Reverse Collapse/Expand hotkeys')
|
||||
local hold = false
|
||||
|
||||
local function thinkExpandAll()
|
||||
if not input.IsKeyDown(KEY_LALT) and not input.IsKeyDown(KEY_RALT) and not input.IsKeyDown(KEY_0) then
|
||||
hold = false
|
||||
end
|
||||
|
||||
if hold or not input.IsShiftDown() or (not input.IsKeyDown(KEY_LALT) and not input.IsKeyDown(KEY_RALT)) or not input.IsKeyDown(KEY_0) then return end
|
||||
|
||||
-- expand all
|
||||
hold = true
|
||||
local part = pace.current_part
|
||||
|
||||
if not part or not part:IsValid() then
|
||||
pace.FlashNotification('No part to expand')
|
||||
return
|
||||
end
|
||||
|
||||
part:CallRecursive('SetEditorExpand', not REVERSE_COLLAPSE_CONTROLS:GetBool())
|
||||
|
||||
surface.PlaySound("buttons/button9.wav")
|
||||
pace.RefreshTree(true)
|
||||
end
|
||||
|
||||
local hold = false
|
||||
|
||||
local function thinkCollapseAll()
|
||||
if not input.IsKeyDown(KEY_LALT) and not input.IsKeyDown(KEY_RALT) and not input.IsKeyDown(KEY_0) then
|
||||
hold = false
|
||||
end
|
||||
|
||||
if hold or input.IsShiftDown() or (not input.IsKeyDown(KEY_LALT) and not input.IsKeyDown(KEY_RALT)) or not input.IsKeyDown(KEY_0) then return end
|
||||
|
||||
-- collapse all
|
||||
hold = true
|
||||
local part = pace.current_part
|
||||
|
||||
if not part or not part:IsValid() then
|
||||
pace.FlashNotification('No part to collapse')
|
||||
return
|
||||
end
|
||||
|
||||
part:CallRecursive('SetEditorExpand', REVERSE_COLLAPSE_CONTROLS:GetBool())
|
||||
|
||||
surface.PlaySound("buttons/button9.wav")
|
||||
pace.RefreshTree(true)
|
||||
end
|
||||
|
||||
local hold = false
|
||||
|
||||
local function thinkPaste()
|
||||
if not input.IsKeyDown(KEY_V) then
|
||||
hold = false
|
||||
end
|
||||
|
||||
if hold or not (input.IsControlDown() and input.IsKeyDown(KEY_V)) then return end
|
||||
|
||||
-- paste
|
||||
hold = true
|
||||
local part = pace.Clipboard
|
||||
|
||||
if not part then
|
||||
pace.FlashNotification('No part is stored in clipboard')
|
||||
return
|
||||
end
|
||||
|
||||
local findParent
|
||||
|
||||
if part == pace.current_part then
|
||||
findParent = part:GetParent()
|
||||
|
||||
if not findParent or not findParent:IsValid() then
|
||||
findParent = part
|
||||
end
|
||||
elseif pace.current_part and pace.current_part:IsValid() then
|
||||
findParent = pace.current_part
|
||||
else
|
||||
pace.RecordUndoHistory()
|
||||
findParent = pace.Call("CreatePart", "group", L"paste data")
|
||||
pace.RecordUndoHistory()
|
||||
end
|
||||
|
||||
pace.Paste(findParent)
|
||||
|
||||
surface.PlaySound("buttons/button9.wav")
|
||||
end
|
||||
|
||||
pac.AddHook("Think", "pace_keyboard_shortcuts", function()
|
||||
if not pace.IsActive() then return end
|
||||
if not pace.Focused then return end
|
||||
if IsValid(vgui.GetKeyboardFocus()) and vgui.GetKeyboardFocus():GetClassName():find('Text') then return end
|
||||
if gui.IsConsoleVisible() then return end
|
||||
thinkUndo()
|
||||
thinkCopy()
|
||||
thinkPaste()
|
||||
thinkCut()
|
||||
thinkDelete()
|
||||
thinkExpandAll()
|
||||
thinkCollapseAll()
|
||||
end)
|
||||
end
|
||||
196
lua/pac3/editor/client/show_outfit_on_use.lua
Normal file
196
lua/pac3/editor/client/show_outfit_on_use.lua
Normal file
@@ -0,0 +1,196 @@
|
||||
--[[
|
||||
| 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 MAX_DIST = 270
|
||||
local input_LookupBinding = input.LookupBinding
|
||||
local string_upper = string.upper
|
||||
|
||||
local pac_onuse_only = CreateClientConVar("pac_onuse_only", "0", true, false, 'Enable "on +use only" mode. Within this mode, outfits are not being actually "loaded" until you hover over player and press your use button')
|
||||
local pac_onuse_only_override = CreateClientConVar("pac_onuse_only_override", "0", true, false, "Ignore value of pac_onuse_only_force")
|
||||
local pac_onuse_only_force = CreateConVar("pac_onuse_only_force", "0", FCVAR_REPLICATED, "Sets pac_onuse_only for clients")
|
||||
|
||||
function pac.IsPacOnUseOnly()
|
||||
if pac_onuse_only_override:GetBool() then
|
||||
return pac_onuse_only:GetBool()
|
||||
end
|
||||
|
||||
return pac_onuse_only_force:GetBool() or pac_onuse_only:GetBool()
|
||||
end
|
||||
|
||||
local pac_IsPacOnUseOnly = pac.IsPacOnUseOnly
|
||||
|
||||
hook.Add("PlayerBindPress", "pac_onuse_only", function(ply, bind, isPressed)
|
||||
if bind ~= "use" and bind ~= "+use" then return end
|
||||
if bind ~= "+use" and isPressed then return end
|
||||
if not pac_IsPacOnUseOnly() then return end
|
||||
local eyes, aim = ply:EyePos(), ply:GetAimVector()
|
||||
|
||||
local tr = util.TraceLine({
|
||||
start = eyes,
|
||||
endpos = eyes + aim * MAX_DIST,
|
||||
filter = ply
|
||||
})
|
||||
|
||||
-- if not tr.Hit or not tr.Entity:IsValid() or not tr.Entity:IsPlayer() then return end
|
||||
if not tr.Hit or not tr.Entity:IsValid() then return end
|
||||
|
||||
local ply2 = tr.Entity
|
||||
if not ply2.pac_onuse_only or not ply2.pac_onuse_only_check then return end
|
||||
ply2.pac_onuse_only_check = false
|
||||
pac.ToggleIgnoreEntity(ply2, false, "pac_onuse_only")
|
||||
end)
|
||||
|
||||
do
|
||||
local lastDisplayLabel = 0
|
||||
|
||||
surface.CreateFont("pac_onuse_only_hint", {
|
||||
font = "Roboto",
|
||||
size = ScreenScale(16),
|
||||
weight = 600,
|
||||
})
|
||||
|
||||
hook.Add("HUDPaint", "pac_onuse_only", function()
|
||||
if not pac_IsPacOnUseOnly() then return end
|
||||
local ply = pac.LocalPlayer
|
||||
local eyes, aim = ply:EyePos(), ply:GetAimVector()
|
||||
|
||||
local tr = util.TraceLine({
|
||||
start = eyes,
|
||||
endpos = eyes + aim * MAX_DIST,
|
||||
filter = ply
|
||||
})
|
||||
|
||||
if tr.Hit and tr.Entity:IsValid() and tr.Entity.pac_onuse_only and tr.Entity.pac_onuse_only_check then
|
||||
lastDisplayLabel = RealTime() + 1
|
||||
end
|
||||
|
||||
if lastDisplayLabel < RealTime() then return end
|
||||
|
||||
local alpha = (lastDisplayLabel - RealTime()) / 2
|
||||
local key = string_upper( input_LookupBinding( "use" ) or "use" )
|
||||
local text = "Press " .. key .. " to reveal this persons PAC3 outfit"
|
||||
draw.DrawText(L(text), "pac_onuse_only_hint", ScrW() / 2, ScrH() * 0.3, Color(255, 255, 255, alpha * 255), TEXT_ALIGN_CENTER)
|
||||
end)
|
||||
end
|
||||
|
||||
function pace.OnUseOnlyUpdates(cvar, ...)
|
||||
hook.Call('pace_OnUseOnlyUpdates', nil, ...)
|
||||
end
|
||||
|
||||
cvars.AddChangeCallback("pac_onuse_only", pace.OnUseOnlyUpdates, "PAC3")
|
||||
|
||||
concommand.Add("pac_onuse_reset", function()
|
||||
for i, ent in ipairs(ents.GetAll()) do
|
||||
if ent.pac_onuse_only then
|
||||
ent.pac_onuse_only_check = true
|
||||
|
||||
if pac_IsPacOnUseOnly() then
|
||||
pac.ToggleIgnoreEntity(ent, ent.pac_onuse_only_check, 'pac_onuse_only')
|
||||
else
|
||||
pac.ToggleIgnoreEntity(ent, false, 'pac_onuse_only')
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local transmissions = {}
|
||||
|
||||
timer.Create('pac3_transmissions_ttl', 1, 0, function()
|
||||
local time = RealTime()
|
||||
|
||||
for transmissionID, data in pairs(transmissions) do
|
||||
if data.activity + 10 < time then
|
||||
transmissions[transmissionID] = nil
|
||||
pac.Message('Marking transmission session with id ', transmissionID, ' as dead. Received ', #data.list, ' out from ', data.total, ' parts.')
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function pace.HandleOnUseReceivedData(data)
|
||||
local validTransmission = isnumber(data.partID) and
|
||||
isnumber(data.totalParts) and isnumber(data.transmissionID)
|
||||
|
||||
if not data.owner.pac_onuse_only then
|
||||
data.owner.pac_onuse_only = true
|
||||
-- if TRUE - hide outfit
|
||||
data.owner.pac_onuse_only_check = true
|
||||
|
||||
if pac_IsPacOnUseOnly() then
|
||||
pac.ToggleIgnoreEntity(data.owner, data.owner.pac_onuse_only_check, 'pac_onuse_only')
|
||||
else
|
||||
pac.ToggleIgnoreEntity(data.owner, false, 'pac_onuse_only')
|
||||
end
|
||||
end
|
||||
|
||||
-- behaviour of this (if one of entities on this hook becomes invalid)
|
||||
-- is undefined if DLib is not installed, but anyway
|
||||
hook.Add('pace_OnUseOnlyUpdates', data.owner, function()
|
||||
if pac_IsPacOnUseOnly() then
|
||||
pac.ToggleIgnoreEntity(data.owner, data.owner.pac_onuse_only_check, 'pac_onuse_only')
|
||||
else
|
||||
pac.ToggleIgnoreEntity(data.owner, false, 'pac_onuse_only')
|
||||
end
|
||||
end)
|
||||
|
||||
if not validTransmission then
|
||||
local func = pace.HandleReceiveData(data)
|
||||
|
||||
local part_uid
|
||||
|
||||
if istable(data.part) and istable(data.part.self) then
|
||||
part_uid = data.part.self.UniqueID
|
||||
end
|
||||
|
||||
if isfunction(func) then
|
||||
pac.EntityIgnoreBound(data.owner, func, part_uid)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local trData = transmissions[data.transmissionID]
|
||||
|
||||
if not trData then
|
||||
trData = {
|
||||
id = data.transmissionID,
|
||||
total = data.totalParts,
|
||||
list = {},
|
||||
activity = RealTime()
|
||||
}
|
||||
|
||||
transmissions[data.transmissionID] = trData
|
||||
end
|
||||
|
||||
local transmissionID = data.transmissionID
|
||||
data.transmissionID = nil
|
||||
data.totalParts = nil
|
||||
data.partID = nil
|
||||
table.insert(trData.list, data)
|
||||
trData.activity = RealTime()
|
||||
|
||||
if #trData.list == trData.total then
|
||||
local funcs = {}
|
||||
|
||||
for i, part in ipairs(trData.list) do
|
||||
local func = pace.HandleReceiveData(part)
|
||||
|
||||
if isfunction(func) then
|
||||
table.insert(funcs, func)
|
||||
end
|
||||
end
|
||||
|
||||
for i, func in ipairs(funcs) do
|
||||
pac.EntityIgnoreBound(data.owner, func)
|
||||
end
|
||||
|
||||
transmissions[data.transmissionID or transmissionID] = nil
|
||||
end
|
||||
end
|
||||
170
lua/pac3/editor/client/spawnmenu.lua
Normal file
170
lua/pac3/editor/client/spawnmenu.lua
Normal file
@@ -0,0 +1,170 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
concommand.Add("pac_wear_parts", function(ply, _, _, file)
|
||||
if file then
|
||||
file = string.Trim(file)
|
||||
if file ~= "" then
|
||||
pace.LoadParts(string.Trim(string.Replace(file, "\"", "")), true)
|
||||
end
|
||||
end
|
||||
|
||||
pace.WearParts()
|
||||
end,
|
||||
function(cmd, args)
|
||||
-- Replace \ with /
|
||||
args = string.Trim(string.Replace(args, "\\", "/"))
|
||||
|
||||
-- Find path
|
||||
local path = ""
|
||||
local slashPos = string.find(args, "/[^/]*$")
|
||||
if slashPos then
|
||||
-- Set path to the directory without the file name
|
||||
path = string.sub(args, 1, slashPos)
|
||||
end
|
||||
|
||||
-- Find files and directories
|
||||
local files, dirs = file.Find("pac3/" .. args .. "*", "DATA")
|
||||
if not dirs then return end
|
||||
|
||||
-- Format directories
|
||||
for k, v in ipairs(dirs) do
|
||||
dirs[k] = v .. "/"
|
||||
end
|
||||
|
||||
-- Format results
|
||||
for k, v in ipairs(table.Add(dirs, files)) do
|
||||
dirs[k] = cmd .. " " .. path .. v
|
||||
end
|
||||
|
||||
return dirs
|
||||
end)
|
||||
|
||||
concommand.Add("pac_clear_parts", function()
|
||||
pace.ClearParts()
|
||||
end)
|
||||
|
||||
concommand.Add("pac_panic", function()
|
||||
pac.Panic()
|
||||
end)
|
||||
|
||||
net.Receive("pac_spawn_part", function()
|
||||
if not pace.current_part:IsValid() then return end
|
||||
|
||||
local mdl = net.ReadString()
|
||||
|
||||
if pace.close_spawn_menu then
|
||||
pace.RecordUndoHistory()
|
||||
pace.Call("VariableChanged", pace.current_part, "Model", mdl)
|
||||
|
||||
if g_SpawnMenu:IsVisible() then
|
||||
g_SpawnMenu:Close()
|
||||
end
|
||||
|
||||
pace.close_spawn_menu = false
|
||||
elseif pace.current_part.ClassName ~= "model" then
|
||||
local name = mdl:match(".+/(.+)%.mdl")
|
||||
|
||||
pace.RecordUndoHistory()
|
||||
pace.Call("CreatePart", "model2", name, mdl)
|
||||
else
|
||||
pace.RecordUndoHistory()
|
||||
pace.Call("VariableChanged", pace.current_part, "Model", mdl)
|
||||
end
|
||||
end)
|
||||
|
||||
pace.SpawnlistBrowser = NULL
|
||||
|
||||
function pace.ClientOptionsMenu(self)
|
||||
if not IsValid(self) then return end
|
||||
|
||||
self:Button(L"show editor", "pac_editor")
|
||||
self:CheckBox(L"enable", "pac_enable")
|
||||
self:Button(L"clear", "pac_clear_parts")
|
||||
self:Button(L"wear on server", "pac_wear_parts" )
|
||||
|
||||
local browser = self:AddControl("pace_browser", {})
|
||||
|
||||
browser.OnLoad = function(node)
|
||||
pace.LoadParts(node.FileName, true)
|
||||
end
|
||||
|
||||
if #file.Find("pac3/sessions/*", "DATA") > 0 then
|
||||
browser:SetDir("sessions/")
|
||||
else
|
||||
browser:SetDir("")
|
||||
end
|
||||
|
||||
browser:SetSize(400,480)
|
||||
|
||||
pace.SpawnlistBrowser = browser
|
||||
|
||||
self:Button(L"request outfits", "pac_request_outfits")
|
||||
end
|
||||
|
||||
function pace.ClientSettingsMenu(self)
|
||||
if not IsValid(self) then return end
|
||||
self:Help(L"Performance"):SetFont("DermaDefaultBold")
|
||||
self:CheckBox(L"Enable PAC", "pac_enable")
|
||||
self:NumSlider(L"Draw distance:", "pac_draw_distance", 0, 20000, 0)
|
||||
self:NumSlider(L"Max render time: ", "pac_max_render_time", 0, 100, 0)
|
||||
end
|
||||
|
||||
|
||||
local icon = "icon64/pac3.png"
|
||||
icon = file.Exists("materials/"..icon,'GAME') and icon or "icon64/playermodel.png"
|
||||
|
||||
list.Set(
|
||||
"DesktopWindows",
|
||||
"PACEditor",
|
||||
{
|
||||
title = "PAC Editor",
|
||||
icon = icon,
|
||||
width = 960,
|
||||
height = 700,
|
||||
onewindow = true,
|
||||
init = function(icn, pnl)
|
||||
pnl:Remove()
|
||||
RunConsoleCommand("pac_editor")
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
hook.Add("PopulateToolMenu", "pac_spawnmenu", function()
|
||||
spawnmenu.AddToolMenuOption(
|
||||
"Utilities",
|
||||
"PAC",
|
||||
"PAC3",
|
||||
L"PAC3",
|
||||
"",
|
||||
"",
|
||||
pace.ClientOptionsMenu,
|
||||
{
|
||||
SwitchConVar = "pac_enable",
|
||||
}
|
||||
)
|
||||
spawnmenu.AddToolMenuOption(
|
||||
"Utilities",
|
||||
"PAC",
|
||||
"PAC3S",
|
||||
L"Settings",
|
||||
"",
|
||||
"",
|
||||
pace.ClientSettingsMenu,
|
||||
{
|
||||
}
|
||||
)
|
||||
end)
|
||||
|
||||
if IsValid(g_ContextMenu) and CreateContextMenu then
|
||||
CreateContextMenu()
|
||||
end
|
||||
816
lua/pac3/editor/client/tools.lua
Normal file
816
lua/pac3/editor/client/tools.lua
Normal file
@@ -0,0 +1,816 @@
|
||||
--[[
|
||||
| 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
|
||||
pace.Tools = {}
|
||||
|
||||
function pace.AddToolsToMenu(menu)
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
for key, data in pairs(pace.Tools) do
|
||||
if #data.suboptions > 0 then
|
||||
local menu = menu:AddSubMenu(L(data.name))
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
for key, option in pairs(data.suboptions) do
|
||||
menu:AddOption(option, function()
|
||||
if pace.current_part:IsValid() then
|
||||
data.callback(pace.current_part, key)
|
||||
end
|
||||
end)
|
||||
end
|
||||
else
|
||||
menu:AddOption(L(data.name), function()
|
||||
if pace.current_part:IsValid() then
|
||||
data.callback(pace.current_part)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.AddTool(name, callback, ...)
|
||||
for i,v in pairs(pace.Tools) do
|
||||
if v.name == name then
|
||||
table.remove(pace.Tools, i)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(pace.Tools, {name = name, callback = callback, suboptions = {...}})
|
||||
end
|
||||
|
||||
|
||||
pace.AddTool(L"convert legacy parts to new parts", function(part, suboption)
|
||||
local class_translate = {
|
||||
model = "model2",
|
||||
material = "material_3d",
|
||||
entity = "entity2",
|
||||
bone = "bone3",
|
||||
bone2 = "bone3",
|
||||
light = "light2",
|
||||
trail = "trail2",
|
||||
clip = "clip2",
|
||||
}
|
||||
|
||||
local model_prop_translate = {
|
||||
Color = function(tbl, val) tbl.Color = Vector(val.r/255, val.g/255, val.b/255) end,
|
||||
DoubleFace = function(tbl, val) tbl.NoCulling = val end,
|
||||
Fullbright = function(tbl, val) tbl.NoLighting = val end,
|
||||
|
||||
TextureFilter = function(tbl, val) tbl.NoTextureFiltering = not val end,
|
||||
LodOverride = function(tbl, val) tbl.LevelOfDetail = val end,
|
||||
|
||||
}
|
||||
|
||||
local registered_parts = pac.GetRegisteredParts()
|
||||
local bones = {}
|
||||
|
||||
local material_translate = {}
|
||||
for old_key in pairs(registered_parts.material.ShaderParams) do
|
||||
|
||||
local new_key = old_key:lower()
|
||||
local new_val = registered_parts.material_3d[new_key]
|
||||
local old_val = registered_parts.material[old_key]
|
||||
|
||||
if new_val ~= nil and type(new_val) == type(old_val) then
|
||||
material_translate[old_key] = function(tbl, val)
|
||||
tbl[new_key] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local prop_translate = {
|
||||
model = model_prop_translate,
|
||||
entity = table.Merge(model_prop_translate, {
|
||||
HideEntity = function(tbl, val) tbl.NoDraw = val end
|
||||
}),
|
||||
material = material_translate,
|
||||
bone = {
|
||||
Bone = function(tbl, val)
|
||||
if bones[tbl.UniqueID] and not bones[tbl.UniqueID][val] then
|
||||
for k,v in pairs(bones[tbl.UniqueID]) do
|
||||
if v.bone == 0 then
|
||||
tbl.Bone = v.friendly
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tbl.Bone = val
|
||||
end,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
local function calc_manip_offset(ent, bone, pos, ang)
|
||||
if not ent:GetBoneMatrix(bone) then return end
|
||||
|
||||
local prev_pos = ent:GetManipulateBonePosition(bone)
|
||||
local prev_ang = ent:GetManipulateBoneAngles(bone)
|
||||
|
||||
ent:ManipulateBonePosition(bone, Vector())
|
||||
ent:ManipulateBoneAngles(bone, Angle())
|
||||
pac.SetupBones(ent)
|
||||
local pre = ent:GetBoneMatrix(bone)
|
||||
|
||||
ent:ManipulateBonePosition(bone, pos)
|
||||
ent:ManipulateBoneAngles(bone, ang)
|
||||
pac.SetupBones(ent)
|
||||
local post = ent:GetBoneMatrix(bone)
|
||||
|
||||
ent:ManipulateBonePosition(bone, prev_pos)
|
||||
ent:ManipulateBoneAngles(bone, prev_ang)
|
||||
|
||||
local m = pre:GetInverseTR() * post
|
||||
|
||||
return m:GetTranslation(), m:GetAngles()
|
||||
end
|
||||
|
||||
local saved = {}
|
||||
for _, part in pairs(pac.GetLocalParts()) do
|
||||
if part.ClassName == "bone" or part.ClassName == "bone2" then
|
||||
bones[part.UniqueID] = pac.GetAllBones(part:GetOwner())
|
||||
if part:GetModelBoneIndex() and not part:GetAlternativeBones() then
|
||||
local pos, ang = calc_manip_offset(part:GetOwner(), part:GetModelBoneIndex(), part:GetPosition(), part:GetAngles())
|
||||
part:SetPosition(pos)
|
||||
part:SetAngles(ang)
|
||||
end
|
||||
|
||||
end
|
||||
if not part:HasParent() then
|
||||
table.insert(saved, part:ToTable())
|
||||
end
|
||||
end
|
||||
|
||||
pace.ClearParts()
|
||||
|
||||
local done = {}
|
||||
|
||||
local function walk(tbl, parent)
|
||||
local new_classname = class_translate[tbl.self.ClassName]
|
||||
|
||||
if new_classname then
|
||||
local old_classname = tbl.self.ClassName
|
||||
tbl.self.ClassName = new_classname
|
||||
|
||||
if old_classname == "model" then
|
||||
if tbl.self.BlurLength and tbl.self.BlurLength > 0 then
|
||||
table.insert(tbl.children, {
|
||||
self = {
|
||||
ClassName = "motion_blur",
|
||||
UniqueID = pac.Hash(),
|
||||
BlurLength = tbl.self.BlurLength,
|
||||
BlurSpacing = tbl.self.BlurSpacing,
|
||||
},
|
||||
children = {},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if old_classname == "entity" then
|
||||
if tbl.self.Weapon == true and tbl.self.HideEntity then
|
||||
table.insert(parent.children, {
|
||||
self = {
|
||||
ClassName = "weapon",
|
||||
UniqueID = pac.Hash(),
|
||||
NoDraw = true
|
||||
},
|
||||
children = {},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for key in pairs(registered_parts[old_classname].StorableVars) do
|
||||
local value = tbl.self[key]
|
||||
if value == nil then
|
||||
value = registered_parts[old_classname][key]
|
||||
end
|
||||
|
||||
if prop_translate[old_classname] and prop_translate[old_classname][key] then
|
||||
tbl.self[key] = nil
|
||||
prop_translate[old_classname][key](tbl.self, value)
|
||||
if value ~= tbl.self[key] then
|
||||
pac.Message("translated property: ", key, " = ", value, " to ", tbl.self[key])
|
||||
end
|
||||
elseif not registered_parts[new_classname].StorableVars[key] then
|
||||
local msg = tbl.self.ClassName .. "." .. key
|
||||
if not done[msg] then
|
||||
pac.Message(Color(255,100,100), "cannot translate property ", msg)
|
||||
done[msg] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if tbl.self.ClassName == "proxy" and tbl.self.VariableName == "Color" then
|
||||
if isstring(tbl.self.Expression) and tbl.self.Expression ~= "" then
|
||||
local r,g,b = unpack(tbl.self.Expression:Split(","))
|
||||
r = tonumber(r)
|
||||
g = tonumber(g)
|
||||
b = tonumber(b)
|
||||
|
||||
if r and g and b then
|
||||
tbl.self.Expression = (r/255)..","..(g/255)..","..(b/255)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if tbl.self.Model and tbl.self.Model:find("://", nil, true) and not tbl.self.Model:find(".zip", nil, true) then
|
||||
tbl.self.ForceObjUrl = true
|
||||
end
|
||||
|
||||
if tbl.children then
|
||||
for _, child in ipairs(tbl.children) do
|
||||
walk(child, tbl)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, tbl in ipairs(saved) do
|
||||
walk(tbl)
|
||||
end
|
||||
|
||||
for _, tbl in ipairs(saved) do
|
||||
local part = pac.CreatePart(tbl.self.ClassName, pac.LocalPlayer, tbl)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
pace.AddTool(L"fix origin", function(part, suboption)
|
||||
if not part.GetEntity then return end
|
||||
|
||||
local ent = part:GetOwner()
|
||||
|
||||
part:SetPositionOffset(-ent:OBBCenter() * part.Scale * part.Size)
|
||||
end)
|
||||
|
||||
pace.AddTool(L"replace ogg with webaudio", function(part, suboption)
|
||||
for _, part in pairs(pac.GetLocalParts()) do
|
||||
if part.ClassName == "ogg" then
|
||||
local parent = part:GetParent()
|
||||
|
||||
local audio = pac.CreatePart("webaudio")
|
||||
audio:SetParent(parent)
|
||||
|
||||
audio:SetURL(part:GetURL())
|
||||
audio:SetVolume(part:GetVolume())
|
||||
audio:SetPitch(part:GetPitch())
|
||||
audio:SetStopOnHide(not part:GetStopOnHide())
|
||||
audio:SetPauseOnHide(part:GetPauseOnHide())
|
||||
|
||||
for k,v in ipairs(part:GetChildren()) do
|
||||
v:SetParent(audio)
|
||||
end
|
||||
|
||||
part:Remove()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
pace.AddTool(L"copy global id", function(obj)
|
||||
SetClipboardText("\"" .. obj.UniqueID .. "\"")
|
||||
end)
|
||||
|
||||
pace.AddTool(L"use legacy scale", function(part, suboption)
|
||||
for _, part in pairs(pac.GetLocalParts()) do
|
||||
if part.UseLegacyScale ~= nil then
|
||||
part:SetUseLegacyScale(suboption == 1)
|
||||
end
|
||||
end
|
||||
end, L"true", L"false")
|
||||
|
||||
pace.AddTool(L"scale this and children", function(part, suboption)
|
||||
Derma_StringRequest(L"scale", L"input the scale multiplier (does not work well with bones)", "1", function(scale)
|
||||
scale = tonumber(scale)
|
||||
|
||||
if scale and part:IsValid() then
|
||||
local function scale_parts(part, scale)
|
||||
if part.SetPosition then
|
||||
part:SetPosition(part:GetPosition() * scale)
|
||||
part:SetPositionOffset(part:GetPositionOffset() * scale)
|
||||
end
|
||||
|
||||
if part.SetSize then
|
||||
part:SetSize(part:GetSize() * scale)
|
||||
end
|
||||
|
||||
for _, part in ipairs(part:GetChildren()) do
|
||||
scale_parts(part, scale)
|
||||
end
|
||||
end
|
||||
|
||||
scale_parts(part, scale)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
pace.AddTool(L"free children from part" ,function(part, suboption)
|
||||
if part:IsValid() then
|
||||
local children = part.Children
|
||||
if #children == 0 then
|
||||
Derma_Message(L"this part has no children...", L"free children from part", "ok")
|
||||
return
|
||||
end
|
||||
|
||||
Derma_Query(L"this process cannot be undone, are you sure?", L"free children from part", L"yes", function()
|
||||
local grandparent = part:GetParent()
|
||||
if grandparent == NULL then
|
||||
grandparent = part:GetRootPart()
|
||||
end
|
||||
local parent = part
|
||||
for _, child in ipairs(children) do
|
||||
if child.BaseName ~= "base" and parent.BaseName ~= "base" then
|
||||
child:SetAngles(child.Angles + parent.Angles)
|
||||
child:SetPosition(child.Position + parent.Position)
|
||||
child:SetAngleOffset(child.AngleOffset + parent.AngleOffset)
|
||||
child:SetPositionOffset(child.PositionOffset + parent.PositionOffset)
|
||||
end
|
||||
child:SetParent(grandparent)
|
||||
end
|
||||
end, L"no", function() end)
|
||||
end
|
||||
end)
|
||||
|
||||
pace.AddTool(L"square model scales...", function(part, suboption)
|
||||
Derma_StringRequest(L"model", L"input the model name that should get squared", "default.mdl", function(model)
|
||||
for _, part in pairs(pac.GetLocalParts()) do
|
||||
if part:IsValid() and part.GetModel then
|
||||
local function square_scale(part)
|
||||
if part.SetSize then
|
||||
part:SetSize(part:GetSize() * part:GetSize())
|
||||
end
|
||||
|
||||
if part.SetScale then
|
||||
part:SetScale(part:GetScale() * part:GetScale())
|
||||
end
|
||||
end
|
||||
if string.find(part:GetModel(),model) then
|
||||
square_scale(part)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
pace.AddTool(L"show only with active weapon", function(part, suboption)
|
||||
local event = part:CreatePart("event")
|
||||
local owner = part:GetRootPart():GetOwner()
|
||||
if not owner.GetActiveWeapon or not owner:GetActiveWeapon():IsValid() then
|
||||
owner = pac.LocalPlayer
|
||||
end
|
||||
|
||||
local class_name = owner:GetActiveWeapon():GetClass()
|
||||
|
||||
event:SetEvent("weapon_class")
|
||||
event:SetOperator("equal")
|
||||
event:SetInvert(true)
|
||||
event:SetRootOwner(true)
|
||||
|
||||
event:ParseArguments(class_name, suboption == 1)
|
||||
|
||||
end, L"hide weapon", L"show weapon")
|
||||
|
||||
pace.AddTool(L"import editor tool from file...", function()
|
||||
local allowcslua = GetConVar("sv_allowcslua")
|
||||
if allowcslua:GetBool() then
|
||||
Derma_StringRequest(L"filename", L"relative to garrysmod/data/pac3_editor/tools/", "mytool.txt", function(toolfile)
|
||||
if file.Exists("pac3_editor/tools/" .. toolfile,"DATA") then
|
||||
local toolstr = file.Read("pac3_editor/tools/" .. toolfile,"DATA")
|
||||
local ctoolstr = [[pace.AddTool(L"]] .. toolfile .. [[", function(part, suboption) ]] .. toolstr .. " end)"
|
||||
RunStringEx(ctoolstr, "pac_editor_import_tool")
|
||||
pac.LocalPlayer:ConCommand("pac_editor") --close and reopen editor
|
||||
else
|
||||
Derma_Message("File " .. "garrysmod/data/pac3_editor/tools/" .. toolfile .. " not found.","Error: File Not Found","OK")
|
||||
end
|
||||
end)
|
||||
else
|
||||
Derma_Message("Importing pac editor tools is disallowed on this server.","Error: Clientside Lua Disabled","OK")
|
||||
end
|
||||
end)
|
||||
|
||||
pace.AddTool(L"import editor tool from url...", function()
|
||||
if GetConVar("sv_allowcslua"):GetBool() then
|
||||
Derma_StringRequest(L"URL", L"URL to PAC Editor tool txt file", "http://www.example.com/tool.txt", function(toolurl)
|
||||
local function ToolDLSuccess(body)
|
||||
local toolname = pac.PrettifyName(toolurl:match(".+/(.-)%."))
|
||||
local toolstr = body
|
||||
local ctoolstr = [[pace.AddTool(L"]] .. toolname .. [[", function(part, suboption)]] .. toolstr .. " end)"
|
||||
RunStringEx(ctoolstr, "pac_editor_import_tool")
|
||||
pac.LocalPlayer:ConCommand("pac_editor") --close and reopen editor
|
||||
end
|
||||
|
||||
pac.HTTPGet(toolurl,ToolDLSuccess,function(err)
|
||||
pace.MessagePrompt(err, "HTTP Request Failed for " .. toolurl, "OK")
|
||||
end)
|
||||
end)
|
||||
else
|
||||
Derma_Message("Importing pac editor tools is disallowed on this server.","Error: Clientside Lua Disabled","OK")
|
||||
end
|
||||
end)
|
||||
|
||||
local function round_pretty(val)
|
||||
return math.Round(val, 2)
|
||||
end
|
||||
|
||||
pace.AddTool(L"round numbers", function(part)
|
||||
local function ify_parts(part)
|
||||
for _, key in pairs(part:GetStorableVars()) do
|
||||
local val = part["Get" .. key](part)
|
||||
|
||||
if isnumber(val) then
|
||||
part["Set" .. key](part, round_pretty(val))
|
||||
elseif isvector(val) then
|
||||
part["Set" .. key](part, Vector(round_pretty(val.x), round_pretty(val.y), round_pretty(val.z)))
|
||||
elseif isangle(val) then
|
||||
part["Set" .. key](part, Angle(round_pretty(val.p), round_pretty(val.y), round_pretty(val.r)))
|
||||
end
|
||||
end
|
||||
|
||||
for _, part in ipairs(part:GetChildren()) do
|
||||
ify_parts(part)
|
||||
end
|
||||
end
|
||||
|
||||
ify_parts(part)
|
||||
end)
|
||||
|
||||
do
|
||||
|
||||
local function fix_name(str)
|
||||
str = str:lower()
|
||||
str = str:gsub("_", " ")
|
||||
return str
|
||||
end
|
||||
|
||||
local hue =
|
||||
{
|
||||
"red",
|
||||
"orange",
|
||||
"yellow",
|
||||
"green",
|
||||
"turquoise",
|
||||
"blue",
|
||||
"purple",
|
||||
"magenta",
|
||||
}
|
||||
|
||||
local sat =
|
||||
{
|
||||
"pale",
|
||||
"",
|
||||
"strong",
|
||||
}
|
||||
|
||||
local val =
|
||||
{
|
||||
"dark",
|
||||
"",
|
||||
"bright"
|
||||
}
|
||||
|
||||
local function HSVToNames(h,s,v)
|
||||
return
|
||||
hue[math.Round(1 + (h / 360) * #hue)] or hue[1],
|
||||
sat[math.ceil(s * #sat)] or sat[1],
|
||||
val[math.ceil(v * #val)] or val[1]
|
||||
end
|
||||
|
||||
local function ColorToNames(c)
|
||||
return HSVToNames(ColorToHSV(Color(c.r, c.g, c.b)))
|
||||
end
|
||||
|
||||
pace.AddTool(L"clear names", function(part, suboptions)
|
||||
for k,v in pairs(pac.GetLocalParts()) do
|
||||
v:SetName("")
|
||||
end
|
||||
pace.RefreshTree(true)
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
pace.AddTool(L"Convert group of models to Expression 2 holograms", function(part)
|
||||
|
||||
local str_ref =
|
||||
[[
|
||||
|
||||
I++, HN++, HT[HN,table] = table(I, Base, Base, 0, POSITION, ANGLES, SCALE, MODEL, MATERIAL, vec4(COLOR, ALPHA), SKIN)
|
||||
]]
|
||||
|
||||
local str_header =
|
||||
[[
|
||||
@name [NAME]
|
||||
#- Entity input directives
|
||||
@inputs [Base]:entity
|
||||
#- Spawning code directives
|
||||
@persist [HT CT]:table [SpawnStatus CoreStatus]:string [HN CN I SpawnCounter]
|
||||
@persist [ScaleFactor ToggleColMat ToggleShading] Indices
|
||||
@persist [DefaultColor DefaultScale]:vector
|
||||
#- Hologram index directives
|
||||
@persist
|
||||
|
||||
if (first() | dupefinished()) {
|
||||
Chip = entity(), Base = entity() #- Remove to use wired entity.
|
||||
|
||||
ScaleFactor = 1 #- Scale multiplier.
|
||||
ToggleColMat = 1 #- Toggle for materials and colours.
|
||||
ToggleShading = 0 #- Toggle for shading.
|
||||
Indices = 1
|
||||
|
||||
#- Data structure
|
||||
#- HN++, HT[HN, table] = table(Index, Local Entity (Entity:toWorld()), Parent Entity, ScaleType (Default 0), Pos, Ang, Scale, Model, Material, Color, Skin)
|
||||
#- CN++, CT[CN, table] = table(Index, Clip Index, Pos, Ang)
|
||||
|
||||
#- Editing holograms
|
||||
#- Scroll down to the bottom of the code to find where to insert your holo() code. In order to reference indexes
|
||||
#- add a ", I_HologramName"" to the end of that holograms data line with "HologramName" being of your choosing.
|
||||
#- Finally add this to a @persist directive eg "@persist [I_HologramName]", now you can address this in your holo() code.
|
||||
#- For example, "holoBodygroup(I_HologramName, 2, 3)" which would be put in the "InitPostSpawn" section.
|
||||
|
||||
#- Advanced functionality
|
||||
#- If you wish to take this system to the next level, you can. Instead of using multiple e2s for each "set" of holograms,
|
||||
#- instead save each set of hologram data to a new file inside a folder of your liking. You can now use the #include "" directive
|
||||
#- to bring that hologram data into a single e2 with this spawn code and compile multiple files into a single e2.
|
||||
#- This has many benefits such as 1 interval instead of many, auto updating due to the chip pulling saved data and increased
|
||||
#- organisation!
|
||||
|
||||
#- Your file hierarchy should look like this.
|
||||
#- /expression2/
|
||||
#- --> /yourfolder/
|
||||
#- --> /hologram_data.txt & hologram_spawner.txt
|
||||
|
||||
# # # # # # # # # HOLOGRAM DATA START # # # # # # # # #
|
||||
]]
|
||||
|
||||
local str_footer =
|
||||
[[
|
||||
|
||||
# # # # # # # # # HOLOGRAM DATA END # # # # # # # # #
|
||||
|
||||
#- Create a hologram from data array
|
||||
function table:holo() {
|
||||
local Index = This[1, number] * Indices
|
||||
if (This[2,entity]:isValid()) { Entity = This[2,entity] } else { Entity = holoEntity(This[2,number]) }
|
||||
if (This[3,entity]:isValid()) { Parent = This[3,entity] } else { Parent = holoEntity(This[3,number]) }
|
||||
local Rescale = (This[7, vector] / (This[4, number] ? 12 : 1)) * ScaleFactor
|
||||
|
||||
holoCreate(Index, Entity:toWorld(This[5, vector] * ScaleFactor), Rescale, Entity:toWorld(This[6, angle]), DefaultColor, This[8, string] ?: "cube")
|
||||
holoParent(Index, Parent)
|
||||
|
||||
if (ToggleColMat) {
|
||||
holoMaterial(Index, This[9, string])
|
||||
holoColor(Index, This[10, vector4])
|
||||
holoSkin(Index, This[11, number])
|
||||
}
|
||||
|
||||
if (ToggleShading) { holoDisableShading(Index, 1) }
|
||||
}
|
||||
|
||||
#- Clip a hologram from data array
|
||||
function table:clip() {
|
||||
holoClipEnabled(This[1, number] * Indices, This[2, number], 1)
|
||||
holoClip(This[1, number] * Indices, This[2, number], This[3, vector] * ScaleFactor, This[4, vector], 0)
|
||||
}
|
||||
|
||||
#- Load the contraption
|
||||
function loadContraption() {
|
||||
switch (SpawnStatus) {
|
||||
case "InitSpawn",
|
||||
if (clk("Start")) {
|
||||
SpawnStatus = "LoadHolograms"
|
||||
}
|
||||
Chip:soundPlay("Blip", 0, "@^garrysmod/content_downloaded.wav", 0.212)
|
||||
break
|
||||
|
||||
case "LoadHolograms",
|
||||
while (perf() & holoCanCreate() & SpawnCounter < HN) {
|
||||
SpawnCounter++
|
||||
HT[SpawnCounter, table]:holo()
|
||||
|
||||
if (SpawnCounter >= HN) {
|
||||
SpawnStatus = CN > 0 ? "LoadClips" : "PrintStatus"
|
||||
SpawnCounter = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case "LoadClips",
|
||||
while (perf() & SpawnCounter < CN) {
|
||||
SpawnCounter++
|
||||
CT[SpawnCounter, table]:clip()
|
||||
|
||||
if (SpawnCounter >= CN) {
|
||||
SpawnStatus = "PrintStatus"
|
||||
SpawnCounter = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case "PrintStatus",
|
||||
printColor( vec(222,37,188), "PAC to Holo: ", vec(255,255,255), "Loaded " + HN + " holograms and " + CN + " clips." )
|
||||
|
||||
HT:clear()
|
||||
CT:clear()
|
||||
|
||||
CoreStatus = "InitPostSpawn"
|
||||
SpawnStatus = ""
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
CoreStatus = "InitSpawn"
|
||||
SpawnStatus = "InitSpawn"
|
||||
|
||||
DefaultColor = vec(255, 255, 255)
|
||||
|
||||
runOnTick(1)
|
||||
timer("Start", 500)
|
||||
}
|
||||
|
||||
#- Credit to Shadowscion for the initial base hologram spawning code.
|
||||
|
||||
elseif (CoreStatus == "InitSpawn") {
|
||||
loadContraption()
|
||||
}
|
||||
elseif (CoreStatus == "InitPostSpawn") {
|
||||
#- This is your "if (first())" section of the code.
|
||||
|
||||
CoreStatus = "RunThisCode"
|
||||
}
|
||||
elseif (CoreStatus == "RunThisCode") {
|
||||
#- This is your "interval()" ran section of the code.
|
||||
|
||||
runOnTick(0)
|
||||
}]]
|
||||
|
||||
local function tovec(vec) return ("%s, %s, %s"):format(math.Round(vec.x, 4), math.Round(vec.y, 4), math.Round(vec.z, 4)) end
|
||||
local function toang(vec) return ("%s, %s, %s"):format(math.Round(vec.p, 4), math.Round(vec.y, 4), math.Round(vec.r, 4)) end
|
||||
|
||||
local function part_to_holo(part)
|
||||
local str_holo = str_ref
|
||||
|
||||
for CI, clip in ipairs(part:GetChildren()) do
|
||||
if not clip:IsHidden() then
|
||||
if clip.ClassName == "clip" then
|
||||
local pos, ang = clip.Position, clip:CalcAngles(clip.Angles)
|
||||
local normal = ang:Forward()
|
||||
str_holo = str_holo .. " CN++, CT[CN,table] = table(I, " .. CI .. ", vec(" .. tovec(pos + normal) .. "), vec(" .. tovec(normal) .. "))\n"
|
||||
elseif clip.ClassName == "clip2" then
|
||||
local pos, ang = clip.Position, clip:CalcAngles(clip.Angles)
|
||||
local normal = ang:Forward()
|
||||
str_holo = str_holo .. " CN++, CT[CN,table] = table(I, " .. CI .. ", vec(" .. tovec(pos) .. "), vec(" .. tovec(normal) .. "))\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local scale = part:GetSize() * part:GetScale()
|
||||
local pos, ang = part.Position, part.Angles
|
||||
if not part.PositionOffset:IsZero() or not part.AngleOffset:IsZero() then
|
||||
pos, ang = LocalToWorld(part.PositionOffset, part.AngleOffset, pos, ang)
|
||||
end
|
||||
|
||||
local holo = str_holo:gsub("[A-Z]+",{
|
||||
ALPHA = math.Round(part:GetAlpha() * 255, 4),
|
||||
COLOR = tovec((part.ProperColorRange and part:GetColor()*255) or part:GetColor()),
|
||||
SCALE = "vec(" .. tovec(Vector(scale.x, scale.y, scale.z)) .. ")",
|
||||
ANGLES = "ang(" .. toang(ang) .. ")",
|
||||
POSITION = "vec(" .. tovec(pos) .. ")",
|
||||
MATERIAL = ("%q"):format(part:GetMaterial()),
|
||||
MODEL = ("%q"):format(part:GetModel()),
|
||||
SKIN = part.GetSkin and part:GetSkin() or "0",
|
||||
PARENT = "entity()"
|
||||
})
|
||||
|
||||
return holo
|
||||
end
|
||||
|
||||
local function convert(part)
|
||||
local out = {string.Replace(str_header, "[NAME]", part:GetName() or "savedpacholos")}
|
||||
|
||||
local completed = {}
|
||||
local function recursiveConvert(parent)
|
||||
if completed[parent] then return end
|
||||
completed[parent] = true
|
||||
for key, part in ipairs(parent:GetChildren()) do
|
||||
if part.is_model_part and not part:IsHidden() then
|
||||
out[#out + 1] = part_to_holo(part)
|
||||
recursiveConvert(part)
|
||||
end
|
||||
end
|
||||
end
|
||||
recursiveConvert(part)
|
||||
|
||||
out[#out + 1] = str_footer
|
||||
|
||||
pac.LocalPlayer:ChatPrint("PAC --> Code saved in your Expression 2 folder under [expression2/pac/" .. part:GetName() .. ".txt" .. "].")
|
||||
|
||||
return table.concat(out)
|
||||
end
|
||||
|
||||
file.CreateDir("expression2/pac")
|
||||
file.Write("expression2/pac/" .. part:GetName() .. ".txt", convert(part))
|
||||
end)
|
||||
|
||||
pace.AddTool(L"record surrounding props to pac", function(part)
|
||||
local base = pac.CreatePart("group")
|
||||
base:SetName("recorded props")
|
||||
|
||||
local origin = base:CreatePart("model")
|
||||
origin:SetName("origin")
|
||||
origin:SetBone("none")
|
||||
origin:SetModel("models/dav0r/hoverball.mdl")
|
||||
|
||||
for key, ent in pairs(ents.FindInSphere(pac.EyePos, 1000)) do
|
||||
if
|
||||
not ent:IsPlayer() and
|
||||
not ent:IsNPC() and
|
||||
not ent:GetOwner():IsPlayer()
|
||||
then
|
||||
local mdl = origin:CreatePart("model")
|
||||
mdl:SetModel(ent:GetModel())
|
||||
|
||||
local lpos, lang = WorldToLocal(ent:GetPos(), ent:GetAngles(), pac.EyePos, pac.EyeAng)
|
||||
|
||||
mdl:SetMaterial(ent:GetMaterial())
|
||||
mdl:SetPosition(lpos)
|
||||
mdl:SetAngles(lang)
|
||||
local c = ent:GetColor()
|
||||
mdl:SetColor(Vector(c.r,c.g,c.b))
|
||||
mdl:SetAlpha(c.a / 255)
|
||||
mdl:SetName(ent:GetModel():match(".+/(.-)%.mdl"))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
pace.AddTool(L"populate with bones", function(part,suboption)
|
||||
local ent = part:GetOwner()
|
||||
local bones = pac.GetModelBones(ent)
|
||||
|
||||
for bone,tbl in pairs(bones) do
|
||||
if not tbl.is_special then
|
||||
local child = pac.CreatePart("bone3")
|
||||
child:SetParent(part)
|
||||
child:SetBone(bone)
|
||||
end
|
||||
end
|
||||
|
||||
pace.RefreshTree(true)
|
||||
end)
|
||||
|
||||
pace.AddTool(L"populate with dummy bones", function(part,suboption)
|
||||
local ent = part:GetOwner()
|
||||
local bones = pac.GetModelBones(ent)
|
||||
|
||||
for bone,tbl in pairs(bones) do
|
||||
if not tbl.is_special then
|
||||
local child = pac.CreatePart("model")
|
||||
child:SetParent(part)
|
||||
child:SetName(bone .. "_dummy")
|
||||
child:SetBone(bone)
|
||||
child:SetScale(Vector(0,0,0))
|
||||
end
|
||||
end
|
||||
|
||||
pace.RefreshTree(true)
|
||||
end)
|
||||
|
||||
pace.AddTool(L"print part info", function(part)
|
||||
PrintTable(part:ToTable())
|
||||
end)
|
||||
|
||||
pace.AddTool(L"dump player submaterials", function()
|
||||
local ply = pac.LocalPlayer
|
||||
for id,mat in pairs(ply:GetMaterials()) do
|
||||
chat.AddText(("%d %s"):format(id,tostring(mat)))
|
||||
end
|
||||
end)
|
||||
|
||||
pace.AddTool(L"stop all custom animations", function()
|
||||
pac.animations.StopAllEntityAnimations(pac.LocalPlayer)
|
||||
pac.animations.ResetEntityBoneMatrix(pac.LocalPlayer)
|
||||
end)
|
||||
|
||||
pace.AddTool(L"copy from faceposer tool", function(part, suboption)
|
||||
local group = pac.CreatePart("group")
|
||||
local ent = pac.LocalPlayer
|
||||
|
||||
for i = 0, ent:GetFlexNum() - 1 do
|
||||
local name = ent:GetFlexName(i)
|
||||
local fp_flex = GetConVar("faceposer_flex" .. i):GetFloat()
|
||||
local fp_scale = GetConVar("faceposer_scale"):GetFloat()
|
||||
local weight = fp_flex * fp_scale
|
||||
pac.Message(name, weight)
|
||||
if weight ~= 0 then
|
||||
local flex = group:CreatePart("flex")
|
||||
flex:SetFlex(name)
|
||||
flex:SetWeight(weight)
|
||||
end
|
||||
end
|
||||
end)
|
||||
402
lua/pac3/editor/client/translations/chinese.lua
Normal file
402
lua/pac3/editor/client/translations/chinese.lua
Normal file
@@ -0,0 +1,402 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
return {
|
||||
["legacy bone"] = "旧版本骨骼工具",
|
||||
["legacy clip"] = "旧版本裁剪工具",
|
||||
["legacy entity"] = "旧版本实体工具",
|
||||
["legacy light"] = "旧版本光源工具",
|
||||
["legacy model"] = "旧版本模型工具",
|
||||
["legacy ogg"] = "旧版本ogg音频工具",
|
||||
["legacy trail"] = "旧版本尾迹工具",
|
||||
["legacy trail"] = "旧版本尾迹工具",
|
||||
["legacy material"] = "旧版本材质工具",
|
||||
["legacy webaudio"] = "旧版本URL音频工具",
|
||||
["legacy experimental bone"] = "旧版本实验性骨骼工具",
|
||||
["legacy"] = "旧版本工具-如果不习惯新版可以使用",
|
||||
["target entiny"] = "目标实体",
|
||||
["parent"] = "父级",
|
||||
["strain"] = "张力",
|
||||
["sphere"] = "范围",
|
||||
["spread"] = "散布",
|
||||
["Right click to cancel"] = "右键关闭",
|
||||
["mass"] = "质量",
|
||||
["load vmt"] = "加载vmt文件",
|
||||
["material override"] = "材质覆盖",
|
||||
["no lod"] = "无多层次细节",
|
||||
["motion blur"] = "运动模糊",
|
||||
["collisions"] = "碰撞",
|
||||
["face poser"] = "面部工具",
|
||||
["load from clipboard"] = "在剪贴板导入",
|
||||
["to clipboard"] = "保存至剪贴板",
|
||||
["request"] = "刷新",
|
||||
["request"] = "刷新",
|
||||
["sprite path"] = "贴图",
|
||||
["right upperarm"] = "右上臂",
|
||||
["neck"] = "脖子",
|
||||
["size y"] = "Y轴坐标",
|
||||
["draw shadow"] = "绘画阴影",
|
||||
["copy"] = "复制",
|
||||
["start size"] = "初始大小",
|
||||
["clip"] = "裁剪工具",
|
||||
["size"] = "大小",
|
||||
["reset view"] = "重置视角",
|
||||
["right calf"] = "右腿",
|
||||
["effect"] = "特效",
|
||||
["max"] = "最大",
|
||||
["angles"] = "角度",
|
||||
["left foot"] = "左脚",
|
||||
["font"] = "字体",
|
||||
["wear"] = "穿戴",
|
||||
["load"] = "加载",
|
||||
["bodygroup state"] = "身体状态",
|
||||
["size x"] = "X轴坐标",
|
||||
["relative bones"] = "相对骨骼",
|
||||
["scale"] = "比例",
|
||||
["follow"] = "跟随",
|
||||
["outline color"] = "轮廓颜色",
|
||||
["left thigh"] = "左大腿",
|
||||
["bone"] = "骨骼",
|
||||
["invert"] = "反转",
|
||||
["double face"] = "双面",
|
||||
["clear"] = "清空衣架",
|
||||
["right forearm"] = "右前臂",
|
||||
["left hand"] = "右手",
|
||||
["left upperarm"] = "左上臂",
|
||||
["false"] = "否",
|
||||
["alpha"] = "透明度",
|
||||
["use lua"] = "使用Lua",
|
||||
["sprite"] = "精灵",
|
||||
["my outfit"] = "我的衣架",
|
||||
["right hand"] = "右手",
|
||||
["angle velocity"] = "角旋转速度",
|
||||
["right clavicle"] = "右锁骨",
|
||||
["outline"] = "概要",
|
||||
["string"] = "执行命令",
|
||||
["left clavicle"] = "左锁骨",
|
||||
["color"] = "颜色",
|
||||
["save"] = "保存",
|
||||
["parent name"] = "名称",
|
||||
["toggle t pose"] = "切换姿势",
|
||||
["outline alpha"] = "概要透明度",
|
||||
["remove"] = "删除",
|
||||
["spine 4"] = "脊柱4",
|
||||
["skin"] = "皮肤",
|
||||
["command"] = "命令行",
|
||||
["material"] = "材质",
|
||||
["sound"] = "音效",
|
||||
["fullbright"] = "最大亮度",
|
||||
["true"] = "开启",
|
||||
["camera FOV"] = "相机视野",
|
||||
["spine 2"] = "脊柱2",
|
||||
["bone merge"] = "合并骨骼",
|
||||
["animation"] = "动画",
|
||||
["spine 1"] = "脊柱1",
|
||||
["text"] = "文字",
|
||||
["rate"] = "速度",
|
||||
["operator"] = "查找",
|
||||
["settings"] = "过滤器设置",
|
||||
["wear filter"] = "谁可以看到你的PAC",
|
||||
["outfit filter"] = "你可以看到谁的PAC",
|
||||
["spine"] = "脊柱",
|
||||
["loop"] = "循环",
|
||||
["clone"] = "克隆",
|
||||
["position"] = "位置",
|
||||
["model"] = "模型",
|
||||
["sequence name"] = "选择动作",
|
||||
["left toe"] = "左脚趾",
|
||||
["pelvis"] = "骨盆",
|
||||
["pitch"] = "音高",
|
||||
["right toe"] = "右脚趾",
|
||||
["add parts to me!"] = "给我添加零件",
|
||||
["group"] = "组",
|
||||
["right foot"] = "右脚",
|
||||
["aim part name"] = "目标瞄准昵称",
|
||||
["left calf"] = "左小腿",
|
||||
["rotate origin"] = "旋转原点",
|
||||
["trail path"] = "尾迹材质",
|
||||
["reset eye angles"] = "重置眼睛角度",
|
||||
["volume"] = "音量",
|
||||
["entity"] = "实体",
|
||||
["modify"] = "修改",
|
||||
["draw weapon"] = "显示武器",
|
||||
["hide"] = "隐藏",
|
||||
["length"] = "长度",
|
||||
["offset"] = "抵销",
|
||||
["input multiplier"] = "输入倍数",
|
||||
["input divider"] = "输入分频",
|
||||
["owner name"] = "所有者名称",
|
||||
["head"] = "头部",
|
||||
["bodygroup"] = "身体",
|
||||
["brightness"] = "亮度",
|
||||
["eye angles"] = "跟随眼睛角度",
|
||||
["event"] = "事件",
|
||||
["trail"] = "尾迹",
|
||||
["arguments"] = "论点",
|
||||
["name"] = "名称",
|
||||
["right thigh"] = "右大腿",
|
||||
["ping pong loop"] = "播放后倒放后播放循环",
|
||||
["min"] = "最小",
|
||||
["left forearm"] = "左前臂",
|
||||
["light"] = "光源",
|
||||
["rendering"] = "渲染",
|
||||
["language"] = "语言",
|
||||
["exit"] = "退出",
|
||||
["help"] = "帮助",
|
||||
["modifiers"] = "装饰类",
|
||||
["t pose"] = "T字姿势",
|
||||
["hide editor"] = "隐藏编辑器",
|
||||
["camera follow"] = "相机跟随",
|
||||
["reset view position"] = "重置相机位置",
|
||||
["about"] = "关于PAC3",
|
||||
["advanced mode"] = "高级模式",
|
||||
["inverse collapse/expand controls"] = "反向折叠/展开控件",
|
||||
["enable shift+move/rotate clone"] = "启用shift+移动/旋转克隆",
|
||||
["view"] = "视图",
|
||||
["options"] = "选项",
|
||||
["player"] = "玩家",
|
||||
["tools"] = "工具",
|
||||
["remember editor position"] = "保留编辑位置",
|
||||
["position grid size"] = "位置网格大小",
|
||||
["angles grid size"] = "角度网格大小",
|
||||
["cut"] = "剪辑",
|
||||
["paste"] = "粘贴",
|
||||
["ogg"] = "音频",
|
||||
["paste properties"] = "粘贴属性",
|
||||
["fog"] = "雾",
|
||||
["render attachments as bones"] = "将附件渲染为骨骼",
|
||||
["automatic property size"] = "自动属性大小",
|
||||
["beam"] = "绳索工具",
|
||||
["halo"] = "光晕",
|
||||
["flex"] = "表情",
|
||||
["decal"] = "贴图",
|
||||
["proxy"] = "计算器",
|
||||
["experimental"] = "实验性工具",
|
||||
["advanced"] = "高级",
|
||||
["custom animation"] = "定制动画",
|
||||
["projectile"] = "弹丸",
|
||||
["camera"] = "相机",
|
||||
["gesture"] = "姿态",
|
||||
["holdtype"] = "holdtype工具",
|
||||
["poseparameter"] = "姿势参数",
|
||||
["submaterial"] = "子材质工具",
|
||||
["jiggle"] = "跟随工具",
|
||||
["physics"] = "物理效果",
|
||||
["effects"] = "特效",
|
||||
["particles"] = "粒子效果",
|
||||
["right click to add parts"] = "右键单击以添加物品",
|
||||
["shake"] = "震动效果",
|
||||
["sunbeams"] = "阳光效果",
|
||||
["webaudio"] = "外链音频",
|
||||
["woohoo"] = "马赛克",
|
||||
["load from url"] = "加载URL",
|
||||
["backups"] = "备份",
|
||||
["outfit backups"] = "衣架备份",
|
||||
["new file"] = "新文档",
|
||||
["new directory"] = "新目录",
|
||||
["average render time"] = "平均渲染时间",
|
||||
["generic"] = "通用",
|
||||
["duplicate"] = "重复",
|
||||
["is explicit"] = "是清晰的",
|
||||
["orientation"] = "取向",
|
||||
["appearance"] = "呈现",
|
||||
["draw order"] = "绘制顺序",
|
||||
["weapon"] = "武器",
|
||||
["material 3d"] = "3D材料",
|
||||
["material 2d"] = "2D材料",
|
||||
["player config"] = "玩家配置",
|
||||
["player movement"] = "玩家数据",
|
||||
["material refract"] = "易碎材料",
|
||||
["projected texture"] = "电灯光源",
|
||||
["material eye refract"] = "材料折射率",
|
||||
["no outfit reflections"] = "没有服装反射",
|
||||
["render objects outside visible fov"] = "在可见视野外渲染对象",
|
||||
["render projected textures (flashlight)"] = "渲染投影纹理(手电筒)",
|
||||
["examples"] = "样例",
|
||||
["auto load (your spawn outfit)"] = "自动保存(您的衣架)",
|
||||
["Camera FOV"] = "摄像机视野",
|
||||
["expression"]= "表达式",
|
||||
["additive"]= "叠加",
|
||||
["affect children"]= "作用在子级",
|
||||
["input"]= "输入",
|
||||
["pow"]= "功率",
|
||||
["player angles"]= "玩家角度",
|
||||
["axis"]= "轴",
|
||||
["variable name"]= "变量名",
|
||||
["angle offset"]= "角度",
|
||||
["position offset"]="位置",
|
||||
["alternative bones"]= "单个骨骼",
|
||||
["owner entity"]= "应用到所有者",
|
||||
["use player color"]= "使用玩家颜色",
|
||||
["use weapon color"]= "使用武器颜色",
|
||||
["blur length"]= "模糊长度",
|
||||
["blur spacing"]= "模糊留存时间",
|
||||
["blend mode"]= "混合模式",
|
||||
["no lighting"]= "没有阴影",
|
||||
["bone power"]= "骨骼力度",
|
||||
["animation type"]= "动画模式",
|
||||
["set start"]= "设置开始点",
|
||||
["reverse"]= "倒放",
|
||||
["duplicate to end"]= "复制到结尾",
|
||||
["no model"]= "不会照射到模型",
|
||||
["no world"]= "不会照射到世界",
|
||||
["mute footsteps"]= "隐藏脚步声",
|
||||
["mute sounds"]= "隐藏音效",
|
||||
["hide bullets"]= "隐藏子弹",
|
||||
["hide physgun beam"]= "隐藏物理枪光束",
|
||||
["blood color"]= "血液颜色",
|
||||
["noclip"]= "飞行模式",
|
||||
["gravity"]= "重力",
|
||||
["jump height"]= "跳跃力",
|
||||
["texture"]= "材质",
|
||||
["f o v"]= "视野",
|
||||
["end size"]= "结束大小",
|
||||
["start color"]= "开始颜色",
|
||||
["end color"]= "结束颜色",
|
||||
["start alpha"]= "开始透明度",
|
||||
["end alpha"]= "结束透明度",
|
||||
["no draw"]= "不显示",
|
||||
["class"]= "选择",
|
||||
["speed"]= "速度",
|
||||
["add owner speed"]= "算入所有者的速度",
|
||||
["damping"]= "抑制(阻尼)",
|
||||
["radius"]= "半径",
|
||||
["damage radius"]= "伤害半径",
|
||||
["attract mode"]= "吸引模式",
|
||||
["attract"]= "吸引",
|
||||
["physical"]= "物理效果",
|
||||
["life time"]= "残留时间",
|
||||
["sticky"]= "接地后固定",
|
||||
["bullet impact"]= "以子弹输出",
|
||||
["damage"]= "伤害",
|
||||
["damage type"]= "伤害类型",
|
||||
["outfit part name"]= "弹丸模型昵称",
|
||||
["weight"]= "幅度",
|
||||
["walk"]= "慢走",
|
||||
["crouch walk"]= "蹲下慢走",
|
||||
["reload stand"]= "换弹站立",
|
||||
["jump"]= "跳跃",
|
||||
["attack stand primaryfire"]= "站立攻击",
|
||||
["swim"]= "游泳",
|
||||
["attack crouch primaryfire"]= "蹲下攻击",
|
||||
["swim idle"]= "游泳站立",
|
||||
["reload crouch"]= "蹲下换弹",
|
||||
["crouch idle"]= "蹲下",
|
||||
["stand idle"]= "站立",
|
||||
["run"]= "跑步",
|
||||
["air"]= "在空中",
|
||||
["sitting"]= "坐下",
|
||||
["end point name"]= "结束点昵称",
|
||||
["frequency"]= "弯曲度",
|
||||
["width"]= "粗细度",
|
||||
["passes"]= "光晕大小",
|
||||
["blur x"]= "水平模糊",
|
||||
["blur y"]= "垂直模糊",
|
||||
["amount"]= "扩散量",
|
||||
["constant velocity"]= "偏移",
|
||||
["constrain x"]= "锁死X轴",
|
||||
["constrain y"]= "锁死Y轴",
|
||||
["constrain z"]= "锁死Z轴",
|
||||
["ground"]= "接地",
|
||||
["box"]= "盒子",
|
||||
["max speed"]= "最高速度",
|
||||
["play on footstep"]= "播放在脚步上",
|
||||
["fire delay"]= "频率",
|
||||
["number particles"]= "数量",
|
||||
["position spread"]= "扩散量",
|
||||
["die time"]= "消失时间",
|
||||
["start length"]= "开始长度",
|
||||
["end length"]= "结束长度",
|
||||
["lighting"]= "明亮",
|
||||
["darken"]= "变暗量",
|
||||
["hide entity"]= "隐藏实体",
|
||||
["start"]= "开始",
|
||||
["end"]= "结束",
|
||||
["eye target name"]= "眼睛看向的方向",
|
||||
["sub materials"]= "子贴图",
|
||||
["function"]= "函数",
|
||||
["hide mesh"]= "隐藏骨骼",
|
||||
["invert hide mesh"]= "反向隐藏骨骼",
|
||||
["run speed"]= "跑步速度",
|
||||
["walk speed"]= "慢走速度",
|
||||
["stretch"]= "伸展",
|
||||
["center attraction"]= "向中心吸引",
|
||||
["ignore z"]= "忽视阻挡物品",
|
||||
["weapon hold type"]= "拿起特定武器时使用holdtype",
|
||||
["invert frames"]= "倒放",
|
||||
["delay"]= "延迟发射",
|
||||
["collide with owner"]= "对所有者有效",
|
||||
["jiggle angle"]= "跟随角度",
|
||||
["jiggle position"]= "跟随位置",
|
||||
["draw view model"]= "调整第一人称视角",
|
||||
----tools------
|
||||
["fix origin"] = "固定原点",
|
||||
["replace ogg with webaudio"] = "将音乐替换为链接地址",
|
||||
["copy global id"] = "复制全局ID",
|
||||
["use legacy scale"] = "使用传统规模",
|
||||
["scale this and children"] = "缩放这个和子对象",
|
||||
["free children from part"] = "从一部分中释放子对象",
|
||||
["square model scales..."] = "方形模型比例...",
|
||||
["show only with active weapon"] = "仅显示有效武器",
|
||||
["import editor tool from file..."] = "从文件中导入编辑器工具...",
|
||||
["import editor tool from url..."] = "从链接中导入编辑器工具...",
|
||||
["round numbers"] = "整数",
|
||||
["clear names"] = "清除名字",
|
||||
["record surrounding props to pac"] = "将周围的道具记录到pac",
|
||||
["populate with bones"] = "人体骨骼显示",
|
||||
["populate with dummy bones"] = "人体软骨骼显示",
|
||||
["print part info"] = "打印部件信息",
|
||||
["dump player submaterials"] = "转储播放器子材料",
|
||||
["stop all custom animations"] = "停止所有自定义动画",
|
||||
["copy from faceposer tool"] = "复制脸部造型工具",
|
||||
["spawn as props"] = "作为PROP产生",
|
||||
["Convert group of models to Expression 2 holograms "] = "将组模型转换为E2 holo",
|
||||
----bones------
|
||||
["scale children"] = "同时缩放子级骨骼",
|
||||
["move children to origin"] = "子级骨骼移动到原点",
|
||||
["follow angles only"] = "仅跟随角度",
|
||||
["right finger 1"] = "食指",
|
||||
["right finger 3"] = "无名指",
|
||||
["right finger 4"] = "小指",
|
||||
["right finger 2"] = "中指",
|
||||
["left finger 1"] = "食指",
|
||||
["left finger 3"] = "无名指",
|
||||
["left finger 4"] = "小指",
|
||||
["left finger 2"] = "中指",
|
||||
-- Animation
|
||||
["sequence name"] = "序列名称",
|
||||
["owner cycle"] = "直接套用到所有者",
|
||||
["physgun"] = "物理枪",
|
||||
["zombie"] = "僵尸",
|
||||
["angry"] = "愤怒",
|
||||
["camera"] = "相机",
|
||||
["crossbow"] = "弩箭",
|
||||
["duel"] = "双持",
|
||||
["fist"] = "拳头",
|
||||
["grenade"] = "手榴弹",
|
||||
["knife"] = "刀",
|
||||
["magic"] = "魔法",
|
||||
["melee"] = "剑1",
|
||||
["melee2"] = "剑2",
|
||||
["meleeangry"] = "弯腰持剑",
|
||||
["normal"] = "普通",
|
||||
["passive"] = "被动",
|
||||
["pistol"] = "手枪",
|
||||
["revolver"] = "左轮",
|
||||
["rpg"] = "RPG",
|
||||
["scared"] = "惊慌的",
|
||||
["shotgut"] = "霰弹枪",
|
||||
["slam"] = "绊雷",
|
||||
["suitcase"] = "手提箱",
|
||||
["smg"] = "SMG",
|
||||
---face pose
|
||||
["face pose"] = "面部造型",
|
||||
["preset"] = "预设",
|
||||
}
|
||||
251
lua/pac3/editor/client/translations/french.lua
Normal file
251
lua/pac3/editor/client/translations/french.lua
Normal file
@@ -0,0 +1,251 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
return {
|
||||
["trail"] = "trainee",
|
||||
["texture filter"] = "filtre de texture",
|
||||
["show only with active weapon"] = "montrer seulement avec l'arme activee",
|
||||
["parent velocity length"] = "vitesse du parent",
|
||||
["operator"] = "operateur",
|
||||
["equal"] = "egal a",
|
||||
["owner velocity right"] = "vitesse droite du proprietaire",
|
||||
["owner velocity length"] = "vitesse du proprietaire",
|
||||
["timerx"] = "minuteurx",
|
||||
["shake"] = "secouer",
|
||||
["entity class"] = "classe de l'entite",
|
||||
["load from url"] = "charger a partir d'un lien",
|
||||
["owner velocity forward"] = "vitesse avant du proprietaire",
|
||||
["light"] = "lumiere",
|
||||
["event"] = "evenement",
|
||||
["follow part name"] = "nom de la partie a suivre",
|
||||
["is crouching"] = "est accroupi",
|
||||
["language"] = "langue",
|
||||
["lock 1"] = "verrouiller 1",
|
||||
["find"] = "trouver",
|
||||
["orientation"] = "orientation",
|
||||
["help"] = "aide",
|
||||
["play count"] = "nombre de lectures",
|
||||
["generic"] = "generique",
|
||||
["movement"] = "mouvement",
|
||||
["appearance"] = "apparence",
|
||||
["behavior"] = "comportement",
|
||||
["speed"] = "vitesse",
|
||||
["projectile"] = "tir de projectile",
|
||||
["life time"] = "duree de vie",
|
||||
["radius"] = "rayon",
|
||||
["damage radius"] = "rayon de degats",
|
||||
["aim dir"] = "pointer vers mouvement",
|
||||
["damage"] = "degats",
|
||||
["damage type"] = "type de degats",
|
||||
["delay"] = "delai",
|
||||
["mass"] = "masse",
|
||||
["attract"] = "force d'attraction",
|
||||
["attract mode"] = "mode d'attraction",
|
||||
["attract radius"] = "distance d'attraction",
|
||||
["outfit part name"] = "nom de piece pour visuel de l'obus",
|
||||
["physical"] = "physique",
|
||||
["collide with owner"] = "collision avec proprietaire",
|
||||
["remove on collide"] = "disparaitre apres collision",
|
||||
["bullet impact"] = "trou d'impact",
|
||||
["command"] = "commande",
|
||||
["events"] = "evenements",
|
||||
["weapon"] = "arme",
|
||||
["size"] = "taille",
|
||||
["clone"] = "cloner",
|
||||
["has weapon"] = "a une arme",
|
||||
["load parts"] = "charger les pieces",
|
||||
["load"] = "charger",
|
||||
["hide editor"] = "cacher l'editeur",
|
||||
["hide entity"] = "cacher l'entite",
|
||||
["health lost"] = "sante perdue",
|
||||
["name"] = "nom",
|
||||
["particles"] = "particules",
|
||||
["start size"] = "taille au debut",
|
||||
["end size"] = "taille a la fin",
|
||||
["start color"] = "couleur au debut",
|
||||
["end color"] = "couleur a la fin",
|
||||
["follow"] = "suivre",
|
||||
["additive"] = "additif",
|
||||
["fire delay"] = "delai de tir",
|
||||
["number particles"] = "nombre de particules",
|
||||
["position spread"] = "etalement",
|
||||
["position spread2"] = "etalement2",
|
||||
["die time"] = "duree de vie",
|
||||
["start length"] = "longueur initiale",
|
||||
["end length"] = "longueur finale",
|
||||
["particle angle"] = "angle des particules",
|
||||
["velocity"] = "vitesse",
|
||||
["spread"] = "precision",
|
||||
["air resistance"] = "resistance a l'air",
|
||||
["gravity"] = "gravite",
|
||||
["collide"] = "collisions",
|
||||
["is on ground"] = "est sur le sol",
|
||||
["loop"] = "boucle",
|
||||
["above"] = "dessus",
|
||||
["reset view"] = "reinitialiser la vue",
|
||||
["reset view position"] = "reinitialiser la position de vue",
|
||||
["reset eye angles"] = "reinitialiser les angles des yeux",
|
||||
["physics"] = "physiques",
|
||||
["draw weapon"] = "dessiner l'arme",
|
||||
["lock 0"] = "verrouiller 0",
|
||||
["true"] = "vrai",
|
||||
["position offset"] = "decalement de position",
|
||||
["weapon class"] = "classe de l'arme",
|
||||
["jiggle"] = "tremousser",
|
||||
["new file"] = "nouveau fichier",
|
||||
["advanced"] = "avance",
|
||||
["color"] = "couleur",
|
||||
["skirt"] = "jupe",
|
||||
["hide weapon"] = "cacher l'arme",
|
||||
["player"] = "joueur",
|
||||
["ammo"] = "munitions",
|
||||
["parent velocity forward"] = "vitesse avant du parent",
|
||||
["exit"] = "quitter",
|
||||
["alternative bones"] = "os alternatifs",
|
||||
["revert"] = "revenir",
|
||||
["modified"] = "modifie",
|
||||
["angle offset"] = "decalage d'angle",
|
||||
["sunbeams"] = "rayons de soleil",
|
||||
["remove"] = "enlever",
|
||||
["max pitch"] = "hauteur max",
|
||||
["volume"] = "volume",
|
||||
["ok"] = "ok",
|
||||
["ogg"] = "ogg",
|
||||
["outline"] = "contour",
|
||||
["invert"] = "inverser",
|
||||
["holdtype"] = "holdtype",
|
||||
["alternative scaling"] = "aggrandissement alternatif",
|
||||
["tint color"] = "couleur de teinte",
|
||||
["is in noclip"] = "est en noclip",
|
||||
["show editor"] = "montrer l'editeur",
|
||||
["play on footstep"] = "jouer a chaque pas",
|
||||
["parent velocity up"] = "vitesse haut du parent",
|
||||
["paste"] = "coller",
|
||||
["wear on server"] = "porter sur le serveur",
|
||||
["request outfits"] = "requete des outfits au serveur",
|
||||
["panic"] = "arreter PAC pour debugger",
|
||||
["wear parts"] = "porter les pieces",
|
||||
["toggle focus"] = "activer/desactiver le focus",
|
||||
["toggle basic mode"] = "activer/desactiver le mode basique",
|
||||
["scale"] = "echelle",
|
||||
["pitch"] = "tonalite du son",
|
||||
["aim part name"] = "piece a viser",
|
||||
["end part name"] = "piece de fin",
|
||||
["target part name"] = "piece ciblee",
|
||||
["draw order"] = "odre de dessin",
|
||||
["width"] = "largeur",
|
||||
["width bend"] = "largeur la courbe",
|
||||
["width bend size"] = "courbe de la largeur",
|
||||
["bend"] = "courbe",
|
||||
["frequency"] = "frequence",
|
||||
["texture stretch"] = "etirer texture",
|
||||
["texture scroll"] = "glissement de texture",
|
||||
["is under water"] = "est dans l'eau",
|
||||
["view"] = "vue",
|
||||
["clip"] = "clipper",
|
||||
["is voice chatting"] = "parle vocalement",
|
||||
["copy global id"] = "copier globalement les id",
|
||||
["copy"] = "copier",
|
||||
["equal or below"] = "egal ou inferieur a",
|
||||
["draw distance"] = "distance de dessin",
|
||||
["hide"] = "cacher",
|
||||
["position"] = "position",
|
||||
["duplicate"] = "dupliquer",
|
||||
["editor"] = "editeur",
|
||||
["timer"] = "minuteur",
|
||||
["is client"] = "est client",
|
||||
["operators"] = "operateurs",
|
||||
["edit"] = "editer",
|
||||
["modifiers"] = "modificateurs",
|
||||
["effects"] = "effets",
|
||||
["sound"] = "son",
|
||||
["owner armor"] = "armure du proprietaire",
|
||||
["sound level"] = "volume du son",
|
||||
["equal or above"] = "egal ou superieur a",
|
||||
["enable"] = "activer",
|
||||
["eye angles"] = "angles des yeux",
|
||||
["footstep"] = "a chaque pas",
|
||||
["fog"] = "brouillard",
|
||||
["health"] = "vie/sante",
|
||||
["false"] = "faux",
|
||||
["put parts in submenu"] = "pieces mises en sous-menu",
|
||||
["translation editor"] = "editeur de traduction",
|
||||
["min pitch"] = "hauteur min. du son",
|
||||
["material"] = "materiau",
|
||||
["filename:"] = "nom du fichier:",
|
||||
["group"] = "groupe",
|
||||
["filter type"] = "type de filtre",
|
||||
["find simple"] = "trouver simple",
|
||||
["fall apart on death"] = "effondrer quand mort",
|
||||
["font"] = "police",
|
||||
["fullbright"] = "illumination pleine",
|
||||
["brightness"] = "brillance",
|
||||
["getting started"] = "commencer",
|
||||
["affect children only"] = "affecter pieces contenues seulement",
|
||||
["draw in reflections"] = "dessiner en reflexions",
|
||||
["is flashlight on"] = "lampe est allumee",
|
||||
["owner entity"] = "entite du proprietaire",
|
||||
["lock 2"] = "verouiller 2",
|
||||
["save"] = "sauvegarder",
|
||||
["pause on hide"] = "pauser quand cachee",
|
||||
["tools"] = "outils",
|
||||
["maybe"] = "peut etre",
|
||||
["owner velocity up"] = "vitesse haut du proprietaire",
|
||||
["vehicle class"] = "classe du vehicule",
|
||||
["spawn as props"] = "Faire apparaitre comme props",
|
||||
["save parts"] = "sauvgarder parts",
|
||||
["outline alpha"] = "alpha du contour",
|
||||
["parent velocity right"] = "vitesse droite du parent",
|
||||
["show weapon"] = "montrer l'arme",
|
||||
["about"] = "a propos",
|
||||
["options"] = "options",
|
||||
["show deprecated features"] = "montrer caracteristiques obsoletes",
|
||||
["draw shadow"] = "dessiner ombres",
|
||||
["parent name"] = "nom du parent",
|
||||
["alpha"] = "alpha",
|
||||
["show in firstperson"] = "montrer en premiere personne",
|
||||
["examples"] = "exemples",
|
||||
["advanced mode"] = "mode avance",
|
||||
["stop on hide"] = "arreter quand cachee",
|
||||
["owner name"] = "nom du proprietaire",
|
||||
["overlapping"] = "chevauchement",
|
||||
["effect"] = "effets",
|
||||
["wear"] = "porter",
|
||||
["say"] = "dire",
|
||||
["sequence name"] = "nom de la sequence",
|
||||
["outline color"] = "couleur du contour",
|
||||
["other"] = "autres",
|
||||
["light blend"] = "melange de lumiere",
|
||||
["mute footsteps"] = "assourdir les pas",
|
||||
["move speed"] = "vitesse de mouvement",
|
||||
["t pose"] = "position t",
|
||||
["text"] = "texte",
|
||||
["model name"] = "nom du modele",
|
||||
["owner health"] = "sante du proprietaire",
|
||||
["toggle camera follow"] = "activer le suivi de la camera",
|
||||
["camera follow"] = "suivi de la camera",
|
||||
["time"] = "temps",
|
||||
["position grid size"] = "taille de la grille de position",
|
||||
["not equal"] = "pas egal a",
|
||||
["toggle t pose"] = "activer la position t",
|
||||
["scale this and children"] = "agrandir/rapetisser cette piece et ses contenus",
|
||||
["clear names"] = "effacer les noms",
|
||||
["gesture"] = "geste",
|
||||
["beam"] = "corde",
|
||||
["new directory"] = "nouveau dossier",
|
||||
["clear"] = "effacer",
|
||||
["rendering"] = "rendu",
|
||||
["no outfit reflections"] = "pas de reflets de pac",
|
||||
["render objects outside of fov"] = "dessiner objets hors du champ de vision",
|
||||
["render projected textures (flashlight)"] = "dessiner lampes",
|
||||
["is explicit"] = "est explicite",
|
||||
["use player color"] = "couleur du joueur",
|
||||
["use weapon color"] = "couleur du physgun"
|
||||
}
|
||||
115
lua/pac3/editor/client/translations/japanese.lua
Normal file
115
lua/pac3/editor/client/translations/japanese.lua
Normal file
@@ -0,0 +1,115 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
return {
|
||||
["sprite path"] = "sprite path",
|
||||
["right upperarm"] = "右 上腕",
|
||||
["neck"] = "首",
|
||||
["size y"] = "Y軸のサイズ",
|
||||
["draw shadow"] = "影を表示",
|
||||
["copy"] = "コピー",
|
||||
["start size"] = "開始 サイズ",
|
||||
["clip"] = "クリップ",
|
||||
["size"] = "サイズ",
|
||||
["reset view"] = "カメラをリセット",
|
||||
["right calf"] = "右 ふくらはぎ",
|
||||
["effect"] = "エフェクト",
|
||||
["max"] = "最大",
|
||||
["angles"] = "角度",
|
||||
["left foot"] = "左 足",
|
||||
["font"] = "フォント",
|
||||
["wear"] = "着用",
|
||||
["load"] = "読み込み",
|
||||
["bodygroup state"] = "ボディグループ ステータス",
|
||||
["size x"] = "X軸のサイズ",
|
||||
["relative bones"] = "relative bones",
|
||||
["scale"] = "スケール",
|
||||
["follow"] = "follow",
|
||||
["outline color"] = "アウトラインの色",
|
||||
["left thigh"] = "左 太もも",
|
||||
["bone"] = "骨",
|
||||
["invert"] = "反転",
|
||||
["double face"] = "両面",
|
||||
["clear"] = "全消去",
|
||||
["right forearm"] = "右 前腕",
|
||||
["left hand"] = "左 手",
|
||||
["left upperarm"] = "左 上腕",
|
||||
["false"] = "無効",
|
||||
["alpha"] = "透過度",
|
||||
["use lua"] = "Luaを使用",
|
||||
["sprite"] = "sprite",
|
||||
["my outfit"] = "自分の衣装",
|
||||
["right hand"] = "右 手",
|
||||
["angle velocity"] = "回転速度",
|
||||
["right clavicle"] = "右 鎖骨",
|
||||
["outline"] = "概要",
|
||||
["string"] = "文字列",
|
||||
["left clavicle"] = "左 鎖骨",
|
||||
["color"] = "色",
|
||||
["save"] = "保存",
|
||||
["parent name"] = "親の名前",
|
||||
["toggle t pose"] = "Tポーズのトグル",
|
||||
["outline alpha"] = "アウトラインの透過度",
|
||||
["remove"] = "削除",
|
||||
["spine 4"] = "脊椎 4",
|
||||
["skin"] = "スキン",
|
||||
["command"] = "コマンド",
|
||||
["material"] = "マテリアル",
|
||||
["sound"] = "音",
|
||||
["fullbright"] = "明るさの最大化",
|
||||
["true"] = "有効",
|
||||
["spine 2"] = "脊椎 2",
|
||||
["bone merge"] = "骨 統合",
|
||||
["animation"] = "アニメーション",
|
||||
["spine 1"] = "脊椎 1",
|
||||
["text"] = "テキスト",
|
||||
["rate"] = "速度",
|
||||
["operator"] = "operator",
|
||||
["spine"] = "脊椎",
|
||||
["loop"] = "繰り返し",
|
||||
["clone"] = "複製",
|
||||
["position"] = "位置",
|
||||
["model"] = "モデル",
|
||||
["sequence name"] = "シーケンス名",
|
||||
["left toe"] = "左 つま先",
|
||||
["pelvis"] = "骨盤",
|
||||
["pitch"] = "ピッチ",
|
||||
["right toe"] = "右 つま先",
|
||||
["right finger 2"] = "右 指 2",
|
||||
["add parts to me!"] = "add parts to me!",
|
||||
["group"] = "グループ",
|
||||
["right foot"] = "右 足",
|
||||
["aim part name"] = "aim part name",
|
||||
["left calf"] = "左 ふくらはぎ",
|
||||
["rotate origin"] = "回転の原点",
|
||||
["trail path"] = "trail path",
|
||||
["reset eye angles"] = "目の角度をリセット",
|
||||
["volume"] = "音量",
|
||||
["entity"] = "エンティティ",
|
||||
["modify"] = "編集",
|
||||
["draw weapon"] = "武器を表示",
|
||||
["hide"] = "隠す",
|
||||
["length"] = "長さ",
|
||||
["offset"] = "オフセット",
|
||||
["owner name"] = "所有者名",
|
||||
["head"] = "頭",
|
||||
["bodygroup"] = "ボディグループ",
|
||||
["brightness"] = "明るさ",
|
||||
["eye angles"] = "目の角度",
|
||||
["event"] = "イベント",
|
||||
["trail"] = "trail",
|
||||
["arguments"] = "引数",
|
||||
["name"] = "名前",
|
||||
["right thigh"] = "右 太もも",
|
||||
["ping pong loop"] = "ping pong loop",
|
||||
["min"] = "最小",
|
||||
["left forearm"] = "左 腕",
|
||||
["light"] = "ライト",
|
||||
}
|
||||
541
lua/pac3/editor/client/translations/korean.lua
Normal file
541
lua/pac3/editor/client/translations/korean.lua
Normal file
@@ -0,0 +1,541 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
return {
|
||||
["sprite path"] = "스프라이트 이미지",
|
||||
["right upperarm"] = "오른쪽 팔 위쪽",
|
||||
["neck"] = "목",
|
||||
["size y"] = "y축 크기",
|
||||
["draw shadow"] = "그림자 활성화",
|
||||
["copy"] = "복사",
|
||||
["start size"] = "시작 크기",
|
||||
["clip"] = "자르기",
|
||||
["size"] = "크기",
|
||||
["reset view"] = "시야 재설정",
|
||||
["right calf"] = "오른쪽 종아리",
|
||||
["effect"] = "이펙트",
|
||||
["max"] = "최대",
|
||||
["angles"] = "각도",
|
||||
["left foot"] = "왼발",
|
||||
["font"] = "글꼴",
|
||||
["wear"] = "착용",
|
||||
["load"] = "불러오기",
|
||||
["bodygroup state"] = "바디그룹 상태",
|
||||
["size x"] = "크기 x축",
|
||||
["relative bones"] = "상대적 뼈",
|
||||
["scale"] = "규모",
|
||||
["follow"] = "따라가기",
|
||||
["outline color"] = "테두리 색상",
|
||||
["left thigh"] = "왼쪽 허벅지",
|
||||
["bone"] = "뼈",
|
||||
["invert"] = "뒤집기",
|
||||
["double face"] = "두 얼굴",
|
||||
["clear"] = "지우기",
|
||||
["right forearm"] = "오른쪽 팔",
|
||||
["left hand"] = "왼손",
|
||||
["left upperarm"] = "왼쪽 팔 위쪽",
|
||||
["false"] = "비활성화",
|
||||
["alpha"] = "투명도",
|
||||
["use lua"] = "루아 사용",
|
||||
["sprite"] = "스프라이트",
|
||||
["my outfit"] = "내 의상",
|
||||
["right hand"] = "오른손",
|
||||
["angle velocity"] = "각도 회전",
|
||||
["right clavicle"] = "오른쪽 쇄골",
|
||||
["outline"] = "윤곽선",
|
||||
["string"] = "문자열",
|
||||
["left clavicle"] = "왼쪽 쇄골",
|
||||
["color"] = "색상",
|
||||
["save"] = "저장",
|
||||
["parent name"] = "상위 이름",
|
||||
["toggle t pose"] = "t포즈 고정",
|
||||
["outline alpha"] = "윤곽선 투명도",
|
||||
["remove"] = "지우기",
|
||||
["spine 4"] = "등 4",
|
||||
["skin"] = "스킨",
|
||||
["command"] = "명령어",
|
||||
["material"] = "재질",
|
||||
["sound"] = "소리",
|
||||
["fullb??right"] = "최대밝기",
|
||||
["true"] = "활성화",
|
||||
["spine 2"] = "등 2",
|
||||
["bone merge"] = "관절 병합",
|
||||
["animation"] = "애니메이션",
|
||||
["spine 1"] = "등 1",
|
||||
["text"] = "텍스트",
|
||||
["rate"] = "속도",
|
||||
["operator"] = "연산자",
|
||||
["spine"] = "등",
|
||||
["loop"] = "반복",
|
||||
["clone"] = "복제",
|
||||
["position"] = "위치",
|
||||
["model"] = "모델",
|
||||
["left toe"] = "왼쪽 발가락",
|
||||
["pelvis"] = "골반",
|
||||
["pitch"] = "간격",
|
||||
["right toe"] = "오른쪽 발가락",
|
||||
["right finger 2"] = "오른쪽 손가락 2",
|
||||
["add parts to me!"] = "나에게 파트를 추가하세요!",
|
||||
["group"] = "그룹",
|
||||
["right foot"] = "오른발",
|
||||
["aim part name"] = "에임 파트",
|
||||
["left calf"] = "왼쪽 종아리",
|
||||
["rotate origin"] = "회전 원점",
|
||||
["trail path"] = "트레일 경로",
|
||||
["reset eye angles"] = "눈 각도 재설정",
|
||||
["volume"] = "볼륨",
|
||||
["entity"] = "엔티티",
|
||||
["modify"] = "수정",
|
||||
["draw weapon"] = "무기 보기",
|
||||
["hide"] = "숨김",
|
||||
["length"] = "길이",
|
||||
["offset"] = "오프셋",
|
||||
["owner name"] = "소유자 이름",
|
||||
["head"] = "머리",
|
||||
["bodygroup"] = "바디그룹",
|
||||
["brightness"] = "밝기",
|
||||
["eye angles"] = "눈의 각도",
|
||||
["event"] = "이벤트",
|
||||
["trail"] = "트레일",
|
||||
["arguments"] = "인수",
|
||||
["name"] = "이름",
|
||||
["right thigh"] = "오른쪽 허벅지",
|
||||
["ping pong loop"] = "핑 퐁 루프",
|
||||
["min"] = "최소",
|
||||
["left forearm"] = "왼쪽 팔",
|
||||
["light"] = "빛",
|
||||
["bone merge alternative"] = "대체관절 병합",
|
||||
["origin fix"] = "프롭 고치기",
|
||||
["owner entity"] = "엔티티 소유자",
|
||||
["overall size"] = "전체관절 두깨",
|
||||
["fullbright"] = "최대광원",
|
||||
["follow part name"] = "따르는 파트 이름",
|
||||
["hide entity"] = "엔티티 숨기기",
|
||||
["save parts"] = "파트를 저장",
|
||||
["load parts"] = "파트를 불러오기",
|
||||
["toggle breathing"] = "호홉동작 고정",
|
||||
["parts"] = "파트를",
|
||||
["modified"] = "저장 날짜",
|
||||
["jiggle"] = "피직스파트 설정",
|
||||
["start color"] = "시작 색상",
|
||||
["end color"] = "끝나는 색상",
|
||||
["stretch"] = "늘리기",
|
||||
["spacing"] = "간격",
|
||||
["end size"] = "끝나는 크기",
|
||||
["min pitch"] = "최소 빠르기",
|
||||
["max pitch"] = "최대 빠르기",
|
||||
["show editor"] = "에디터 보이기",
|
||||
["wear on server"] = "착용 ",
|
||||
["clear"] = "지우기",
|
||||
["convert active pac2 outfit pac2"] = "의상 컨버팅",
|
||||
["draw distance"] = "의상 활성화 거리",
|
||||
["wear parts"] = "파트를 착용",
|
||||
["clear parts"] = "파트를 초기화",
|
||||
["toggle focus"] = "포커스 토글",
|
||||
["language"] = "언어 설정",
|
||||
["help"] = "도움말",
|
||||
["draw order"] = "렌더 순서",
|
||||
["root owner"] = "루트 오너",
|
||||
["halo"] = "빛나는 테두리",
|
||||
["particles"] = "파티클",
|
||||
["sunbeams"] = "햇살",
|
||||
["woohoo"] = "모자이크",
|
||||
["start alpha"] = "처음 알파값",
|
||||
["end alpha"] = "끝 알파값",
|
||||
["multiplier"] = "효과 배율",
|
||||
["darken"] = "어두움",
|
||||
|
||||
["function"] = "함수",
|
||||
["input"] = "입력",
|
||||
["weapon hold type"] = "무기 잡는 방식",
|
||||
["variable name"] = "변수 이름",
|
||||
["input multiplier"] = "효과 배율",
|
||||
["expression"] = "수식",
|
||||
["cell shade"] = "만화 테두리",
|
||||
["axis"] = "축",
|
||||
["additive"] = "첨가",
|
||||
["input divider"] = "인풋 분산",
|
||||
|
||||
-- momo
|
||||
--poseparameter
|
||||
["poseparameter"] = "자세한도",
|
||||
["pose parameter"] = "자세 한도",
|
||||
["range"] = "범위",
|
||||
|
||||
-- Animation
|
||||
["sequence name"] = "장면 이름",
|
||||
["owner cycle"] = "사용자에게 직접적용",
|
||||
["physgun"] = "게리건",
|
||||
["zombie"] = "좀비",
|
||||
["angry"] = "화남",
|
||||
["camera"] = "카메라",
|
||||
["crossbow"] = "크로스 보우",
|
||||
["duel"] = "듀얼 건",
|
||||
["fist"] = "주먹",
|
||||
["grenade"] = "폭탄",
|
||||
["knife"] = "나이프",
|
||||
["magic"] = "마법",
|
||||
["melee"] = "검1",
|
||||
["melee2"] = "검2",
|
||||
["meleeangry"] = "검 화남",
|
||||
["normal"] = "노멀",
|
||||
["passive"] = "패시브",
|
||||
["pistol"] = "피스톨",
|
||||
["revolver"] = "리볼버",
|
||||
["rpg"] = "RPG",
|
||||
["scared"] = "두려움",
|
||||
["shotgut"] = "샷건",
|
||||
["slam"] = "슬램",
|
||||
["suitcase"] = "슈트 케이스",
|
||||
["smg"] = "SMG",
|
||||
|
||||
-- Ogg
|
||||
["ogg"] = "노래 불러오기",
|
||||
["echo"] = "에코",
|
||||
["echo feedback"] = "에코 피드백",
|
||||
["echo delay"] = "에코 지연 속도",
|
||||
["play on footstep"] = "발소리로 변경",
|
||||
["play count"] = "재생 횟수",
|
||||
["filter type"] = "필터 종류",
|
||||
["filter fraction"] = "필터 부분",
|
||||
["doppler"] = "도플러 효과",
|
||||
["stop on hide"] = "멈추기",
|
||||
["pause on hide"] = "정지 시키기",
|
||||
["overlapping"] = "중복",
|
||||
|
||||
-- Proxy
|
||||
["proxy"] = "프록시",
|
||||
["pow"] = "pow값",
|
||||
["zero eye pitch"] = "제로 눈 피치",
|
||||
["reset velocities on hide"] = "숨김 속도 리셋",
|
||||
["velocity roughness"] = "거칠기 속도",
|
||||
|
||||
-- jiggle
|
||||
["constrain x"] = "X값 제한",
|
||||
["constrain y"] = "Y값 제한",
|
||||
["constrain z"] = "Z값 제한",
|
||||
["strain"] = "압력",
|
||||
["constant velocity"] = "정수 속도",
|
||||
["local velocity"] = "로컬 속도",
|
||||
["jiggle angle"] = "각도가 움직임",
|
||||
["jiggle position"] = "위치가 움직임",
|
||||
["stop radius"] = "정지반경",
|
||||
["ground"] = "땅바닥",
|
||||
["reset on hide"] = "숨김 리셋",
|
||||
["constrain pitch"] = "Pitch값 조정",
|
||||
["constrain yaw"] = "Yaw값 조정",
|
||||
["constrain roll"] = "Roll값 조정",
|
||||
|
||||
-- Physics
|
||||
["physics"] = "물리",
|
||||
["constrain sphere"] = "제한 영역",
|
||||
["radius"] = "반경",
|
||||
["box"] = "상자",
|
||||
["self collision"] = "자가 충돌",
|
||||
["collisions"] = "충돌",
|
||||
["mass"] = "질량",
|
||||
["seconds to arrive"] = "도착 시간",
|
||||
["max speed"] = "최대 속도",
|
||||
["max angular"] = "최대 각도",
|
||||
["max speed damp"] = "최대 제동속도",
|
||||
["max angular damp"] = "최대 제동각도",
|
||||
["damp factor"] = "제동 팩터",
|
||||
|
||||
-- Shake
|
||||
["shake"] = "흔들기",
|
||||
["frequency"] = "진동 빈도",
|
||||
["amplitude"] = "진폭",
|
||||
["duration"] = "지속 시간",
|
||||
|
||||
-- Beam
|
||||
["beam"] = "줄",
|
||||
["end point name"] = "끝 지점",
|
||||
["bend"] = "휘어짐",
|
||||
["resolution"] = "해상도",
|
||||
["width"] = "폭",
|
||||
["width bend"] = "폭 휘어짐",
|
||||
["width bend size"] = "폭 휘어짐 크기",
|
||||
["texture stretch"] = "텍스쳐 늘리기",
|
||||
["texture scroll"] = "텍스쳐 돌리기",
|
||||
|
||||
-- Fog
|
||||
["fog"] = "안개",
|
||||
["start"] = "시작",
|
||||
["end"] = "끝",
|
||||
["height"] = "높이",
|
||||
|
||||
-- Particle
|
||||
["velocity"] = "거리",
|
||||
["die time"] = "사라지는 시간",
|
||||
["fire delay"] = "발사 지연시간",
|
||||
["spread"] = "확산",
|
||||
["start length"] = "시작 길이",
|
||||
["end length"] = "끝 길이",
|
||||
["bounce"] = "튕김",
|
||||
["gravity"] = "중력",
|
||||
["random colour"] = "랜덤 색깔",
|
||||
["color1"] = "색1",
|
||||
["color2"] = "색2",
|
||||
["collide"] = "충돌",
|
||||
["lighting"] = "명암",
|
||||
["sliding"] = "슬라이딩",
|
||||
["number particles"] = "파티클 갯수",
|
||||
["air resistance"] = "기압 저항력",
|
||||
["3 d"] = "3D",
|
||||
["random roll speed"] = "랜덤 구르기 속도",
|
||||
["roll delta"] = "델타 구르기",
|
||||
["align to surface"] = "표면 조정",
|
||||
["stick to surface"] = "표면 붙이기",
|
||||
["double sided"] = "두개의 면을가진",
|
||||
["particle angle velocity"] = "파티클 각도 거리",
|
||||
["stick lifetime"] = "스틱 생명시간",
|
||||
["stick start size"] = "스틱 시작 크기",
|
||||
["stick end size"] = "스틱 끝 크기",
|
||||
["stick start alpha"] = "스틱 시작 알파",
|
||||
["stick end alpha"] = "스틱 끝 알파",
|
||||
["owner velocity multiplier"] = "사용자 거리 제곱",
|
||||
["draw manual"] = "수동 그리기",
|
||||
|
||||
-- born
|
||||
["scale children"] = "하위목록 크기도 조정",
|
||||
["alternative bones"] = "대채 관절",
|
||||
["move children to origin"] = "하위목록 원점으로 이동",
|
||||
["follow angles only"] = "각도만 따라가기",
|
||||
["chest"] = "가슴",
|
||||
["eyes"] = "눈",
|
||||
["mouth"] = "입",
|
||||
["forward"] = "",
|
||||
["right trapezius"] = "오른쪽 승모근",
|
||||
["right shoulder"] = "오른쪽 어꺠",
|
||||
["right bicep"] = "오른쪽 상완이두근",
|
||||
["right elbow"] = "오른쪽 팔꿈치",
|
||||
["right ulna"] = "오른쪽 자뼈",
|
||||
["right wrist"] = "오른쪽 손목",
|
||||
["left trapezius"] = "왼쪽 승모근",
|
||||
["left shoulder"] = "왼쪽 어꺠",
|
||||
["left bicep"] = "왼쪽 상완이두근",
|
||||
["left elbow"] = "왼쪽 팔꿈치",
|
||||
["left ulna"] = "왼쪽 자뼈",
|
||||
["left wrist"] = "왼쪽 손목",
|
||||
|
||||
-- projectile
|
||||
["projectile"] = "발사체",
|
||||
["damping"] = "감쇠",
|
||||
["sphere"] = "영역",
|
||||
["life time"] = "생명 시간",
|
||||
["aim dir"] = "에임 감독",
|
||||
["sticky"] = "달라붙음",
|
||||
["delay"] = "딜레이",
|
||||
["outfit part name"] = "내 의상 파트",
|
||||
["physical"] = "물리",
|
||||
["collide with owner"] = "사용자에 부딪힘",
|
||||
|
||||
-- entity
|
||||
["player angles"] = "플레이어 각도",
|
||||
["translucent"] = "반투명",
|
||||
["inverse kinematics"] = "역 운동학",
|
||||
["weapon"] = "무기",
|
||||
["mute footsteps"] = "발소리 끄기",
|
||||
["mute sounds"] = "소리 끄기",
|
||||
["allow ogg when muted"] = "소리 껐을때 ogg 불러오기",
|
||||
["hide bullets"] = "총알 숨기기",
|
||||
["draw player on death"] = "죽었을때 플레이어 표시",
|
||||
["use legacy scale"] = "오래된 크기조정 사용",
|
||||
["sprint speed"] = "전력질주 속도",
|
||||
["run speed"] = "달리기 속도",
|
||||
["walk speed"] = "걷기 속도",
|
||||
["crouch speed"] = "앉아서 이동 속도",
|
||||
["full apart on death"] = "사망시 전부착용",
|
||||
["death ragdollize parent"] = "사망시 전부 레그돌화",
|
||||
["hide ragdoll on death"] = "사망시 레그돌 숨기기",
|
||||
["animation rate"] = "애니메이션 속도",
|
||||
|
||||
|
||||
-- Model
|
||||
["position offset"] = "위치 기준변환",
|
||||
["angle offset"] = "각도 기준변환",
|
||||
["light blend"] = "밝기",
|
||||
["alternative scaling"] = "비율 조정",
|
||||
["texture filter"] = "텍스쳐 필터",
|
||||
["tint color"] = "염색",
|
||||
["angle part name"] = "앵글 파트이름",
|
||||
["angle part multiplier"] = "앵글 파트승수",
|
||||
["blur length"] = "블러 길이",
|
||||
["blur spacing"] = "블러 공간",
|
||||
["use player color"] = "플레이어색 사용",
|
||||
["use weapon color"] = "무기색 사용",
|
||||
|
||||
-- Event
|
||||
["affect children only"] = "하위목록에만 영향",
|
||||
["target part name"] = "목표 부위 이름",
|
||||
["is voice chatting"] = "음성 채팅사용중일때",
|
||||
["has weapon"] = "무기를 들고있을때",
|
||||
["timer"] = "타이머",
|
||||
["model name"] = "모델 이름",
|
||||
["is under water"] = "잠수중일때",
|
||||
["ammo"] = "탄약",
|
||||
["weapon class"] = "무기 종류",
|
||||
["vehicle class"] = "탑승물 종류",
|
||||
["owner health"] = "사용자의 체력",
|
||||
["gravitygun punt"] = "중력건을 사용",
|
||||
["say"] = "채팅중일때",
|
||||
["is crouching"] = "주저앉아있을때",
|
||||
["speed"] = "속도",
|
||||
["is flashlight on"] = "손전등 사용중일때",
|
||||
["is in noclip"] = "노클립 사용중일때",
|
||||
["is on ground"] = "지상일때",
|
||||
["owner armor"] = "사용자의 아머",
|
||||
["is client"] = "오직 클라이언트만",
|
||||
["equal"] = "같을때",
|
||||
["not equal"] = "같지 않을때",
|
||||
["above"] = "초과일때",
|
||||
["equal or above"] = "이상일때",
|
||||
["below"] = "미만일때",
|
||||
["equal or below"] = "이하일때",
|
||||
["maybe"] = "혹시나(Maybe)",
|
||||
["time"] = "시간",
|
||||
["advanced timer"] = "진보된 타이머",
|
||||
["animation event"] = "애니메이션 이벤트",
|
||||
["health lost"] = "HP 감소량",
|
||||
["holdtype"] = "무기잡는 방식",
|
||||
["fov"] = "시야",
|
||||
["entity class"] = "엔티티 클래스",
|
||||
["client spawned"] = "클라이언트 스폰",
|
||||
["owner velocity forward"] = "사용자 전진속도",
|
||||
["owner velocity length"] = "사용자 길이속도",
|
||||
["owner velocity right"] = "사용자 우향속도",
|
||||
["owner velocity up"] = "사용자 상승속도",
|
||||
["parent velocity forward"] = "상위 전진속도",
|
||||
["parent velocity length"] = "상위 길이속도",
|
||||
["parent velocity right"] = "상위 우향속도",
|
||||
["parent velocity up"] = "상위 상승속도",
|
||||
|
||||
-- holdtype
|
||||
["walk"] = "걷기",
|
||||
["crouch walk"] = "앉아서 걷기",
|
||||
["reload stand"] = "서서 재장전",
|
||||
["jump"] = "점프",
|
||||
["attack stand primaryfire"] = "서서 주사격무기 공격",
|
||||
["swim"] = "수영중",
|
||||
["attack crouch primaryfire"] = "앉아서 주사격무기 공격",
|
||||
["swim idle"] = "수영 자세",
|
||||
["reload crouch"] = "앉아서 재장전",
|
||||
["act range attack1"] = "어택1 범위",
|
||||
["crouch idle"] = "앉은 자세",
|
||||
["act land"] = "착륙",
|
||||
["stand idle"] = "서있는 자세",
|
||||
["run"] = "달리기",
|
||||
["fallback"] = "대비책",
|
||||
["noclip"] = "노클립",
|
||||
["air"] = "공중",
|
||||
["sitting"] = "(의자에)앉음",
|
||||
["alternative rate"] = "대안 속도",
|
||||
|
||||
-- outline
|
||||
["blur x"] = "x 블러",
|
||||
["blur y"] = "y 블러",
|
||||
["passes"] = "통과",
|
||||
["amount"] = "총량",
|
||||
["ignore z"] = "z각 무시",
|
||||
["spherical size"] = "구 크기",
|
||||
["shape"] = "날카로움",
|
||||
["affect children"] = "하위목록에 영향",
|
||||
|
||||
--material
|
||||
["alpha test"] = "알파 테스트",
|
||||
["translucent x"] = "X값 반투명",
|
||||
["blend tint by base alpha"] = "기존 알파값에의한 염색",
|
||||
["distance alpha"] = "거리에따른 알파값",
|
||||
["vertex alpha"] = "꼭지 알파값",
|
||||
|
||||
--webaudio
|
||||
["minimum radius"] = "최소 반경",
|
||||
["maximum radius"] = "최대 반경",
|
||||
["inner angle"] = "안쪽 앵글",
|
||||
["outer angle"] = "바깥쪽 앵글",
|
||||
["outer volume"] = "바깥쪽 볼륨",
|
||||
["random pitch"] = "랜덤 피치",
|
||||
|
||||
--body group
|
||||
["body group name"] = "바디 그룹 이름",
|
||||
["model index"] = "모델 인덱스",
|
||||
|
||||
--effects
|
||||
["use particle tracer"] = "파티클 트레이서 사용",
|
||||
["point a name"] = "포인트 A 이름",
|
||||
["point b name"] = "포인트 B 이름",
|
||||
["point c name"] = "포인트 C 이름",
|
||||
["point d name"] = "포인트 D 이름",
|
||||
|
||||
--flex
|
||||
["flex"] = "전선",
|
||||
|
||||
-- Pac3 menu
|
||||
["new file"] = "새 파일",
|
||||
["load from url"] = "URL에서 불러오기",
|
||||
["ok"] = "확인",
|
||||
["hide editor"] = "에디터 숨기기",
|
||||
["reset view position"] = "시점 초기화",
|
||||
["advanced mode"] = "고급 옵션 표기",
|
||||
["show only with active weapon"] = "사용중인 무기표시",
|
||||
["hide weapon"] = "무기 숨기기",
|
||||
["show weapon"] = "무기 보이기",
|
||||
["view"] = "보기",
|
||||
["options"] = "옵션",
|
||||
["player"] = "플레이어",
|
||||
["tools"] = "도구",
|
||||
["exit"] = "나가기",
|
||||
["toggle basic mode"] = "기본 모드 켜기",
|
||||
["disable input"] = "마우스 끄기",
|
||||
["toggle camera follow"] = "카메라를 따라오게",
|
||||
["camera follow"] = "카메라를 따라오게",
|
||||
["show deprecated features"] = "쓸모없는 옵션 표기",
|
||||
["rendering"] = "렌더링",
|
||||
["draw in reflections"] = "반사광 그리기",
|
||||
["t pose"] = "T 포즈",
|
||||
["modifiers"] = "모델 수정",
|
||||
["spawn as props"] = "프롭 소환하기",
|
||||
["scale this and children"] = "크기 키우기",
|
||||
["clear names"] = "이름 지우기",
|
||||
["position grid size"] = "위치 그리드 크기",
|
||||
["angles grid size"] = "각도 그리드 크기",
|
||||
["duplicate"] = "복사하기",
|
||||
["sound level"] = "사운드 레벨",
|
||||
["style"] = "스타일",
|
||||
["examples"] = "예제",
|
||||
["help i have ocd (rounding numbers)"] = "OCD를 가지게하기 (라운딩 넘버)",
|
||||
["copy global id"] = "글로벌 id 카피",
|
||||
["paste"] = "붙여넣기",
|
||||
["auto load (your spawn outfit)"] = "이 PAC를 스폰시 불러오기",
|
||||
["advanced"] = "전문기능",
|
||||
["effects"] = "이펙트",
|
||||
["webaudio"] = "웹오디오",
|
||||
["put parts in submenu"] = "부메뉴에 파트 넣기",
|
||||
["automatic property size"] = "자동으로 크기맞추기",
|
||||
["fix origin"] = "원점 수정",
|
||||
["replace ogg with webaudio"] = "ogg를 웹오디오로 교체",
|
||||
["free children from part"] = "자유로운 하위목록",
|
||||
["square model scales..."] = "모델 비율조정",
|
||||
["import editor tool from file..."] = "파일로부터 에디터 도구 가져오기",
|
||||
["import editor tool from url..."] = "url로부터 에디터 도구 가져오기",
|
||||
["round numbers"] = "라운드 넘버",
|
||||
["record surrounding props to pac"] = "pac에 주변프롭들 기록",
|
||||
["populate with bones"] = "뼈로 이동",
|
||||
["populate with dummy bones"] = "더미 뼈로 이동",
|
||||
["print part info"] = "파트 정보 출력",
|
||||
["dump player submaterials"] = "플레이어 부재질 덤핑",
|
||||
["stop all custom animations"] = "모든 커스텀애니메이션 정지",
|
||||
["copy from faceposer tool"] = "얼굴조정 툴에서 복사해오기",
|
||||
["script"] = "스크립트",
|
||||
["custom_animation"] = "커스텀 애니메이션",
|
||||
["about"] = "크레딧",
|
||||
["find"] = "값",
|
||||
["Getting Started"] = "시작 튜토리얼(영어)",
|
||||
}
|
||||
213
lua/pac3/editor/client/translations/russian.lua
Normal file
213
lua/pac3/editor/client/translations/russian.lua
Normal file
@@ -0,0 +1,213 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
return {
|
||||
["trail"] = "трейл",
|
||||
["show in firstperson"] = "показывать от 1-го лица",
|
||||
["show only with active weapon"] = "показывать только с активным оружием",
|
||||
["outfit backups"] = "копии костюма",
|
||||
["copy global id"] = "копировать глобальный id",
|
||||
["replace ogg with webaudio"] = "заменить ogg с помощью webaudio",
|
||||
["getting started"] = "ознакомление",
|
||||
["shake"] = "тряска",
|
||||
["entity class"] = "класс сущности",
|
||||
["copy"] = "копировать",
|
||||
["translate"] = "перевести",
|
||||
["bone"] = "кость",
|
||||
["light"] = "свет",
|
||||
["paste properties"] = "вставить свойства",
|
||||
["owner name"] = "имя владельца",
|
||||
["modifiers"] = "модификаторы",
|
||||
["input divider"] = "разделитель ввода",
|
||||
["fog"] = "туман",
|
||||
["help"] = "помощь",
|
||||
["save parts"] = "сохранить части",
|
||||
["speed"] = "скорость",
|
||||
["projectile"] = "снаряд",
|
||||
["command"] = "команда",
|
||||
["events"] = "события",
|
||||
["experimental"] = "экспериментальный",
|
||||
["custom animation"] = "пользовательская анимация",
|
||||
["weapon"] = "оружие",
|
||||
["clear names"] = "очистить названия",
|
||||
["fov"] = "поле зрения",
|
||||
["size"] = "размер",
|
||||
["clone"] = "клонировать",
|
||||
["has weapon"] = "имеет оружие",
|
||||
["load parts"] = "загрузить части",
|
||||
["reset view position"] = "сбросить позицию камеры",
|
||||
["hide editor"] = "спрятать редактор",
|
||||
["hide entity"] = "скрыть энтити",
|
||||
["function"] = "функция",
|
||||
["name"] = "имя",
|
||||
["brightness"] = "яркость",
|
||||
["particles"] = "частицы",
|
||||
["stop all custom animations"] = "остановить все пользовательские анимации",
|
||||
["is on ground"] = "на земле",
|
||||
["beam"] = "луч",
|
||||
["animation"] = "анимация",
|
||||
["arguments"] = "аргументы",
|
||||
["no outfit reflections"] = "не отражать костюм",
|
||||
["wear"] = "одеть",
|
||||
["reset eye angles"] = "сбросить направление глаз",
|
||||
["physics"] = "физика",
|
||||
["model"] = "модель",
|
||||
["draw weapon"] = "показывать оружие",
|
||||
["additive"] = "наложение",
|
||||
["axis"] = "ось",
|
||||
["show weapon"] = "показывать оружие",
|
||||
["weapon class"] = "класс оружия",
|
||||
["expression"] = "выражение",
|
||||
["auto load (your spawn outfit)"] = "автозагрузка (одежда при спавне)",
|
||||
["new file"] = "новый файл",
|
||||
["advanced"] = "продвинутый",
|
||||
["color"] = "цвет",
|
||||
["input multiplier"] = "множитель ввода",
|
||||
["backups"] = "резервные копии",
|
||||
["hide weapon"] = "спрятать оружие",
|
||||
["player"] = "игрок",
|
||||
["ammo"] = "патроны",
|
||||
["alpha"] = "альфа",
|
||||
["exit"] = "выход",
|
||||
["revert"] = "вернуть обратно",
|
||||
["modified"] = "изменено",
|
||||
["angle offset"] = "смещение угла",
|
||||
["entity"] = "сущность",
|
||||
["rendering"] = "отображение",
|
||||
["hide bullets"] = "скрыть пули",
|
||||
["sunbeams"] = "лучи солнца",
|
||||
["remove"] = "удалить",
|
||||
["ok"] = "окей",
|
||||
["animation rate"] = "темп анимации",
|
||||
["outline"] = "обводка",
|
||||
["max render time (in ms)"] = "порог времени прорисовки (в мс)",
|
||||
["halo"] = "свечение",
|
||||
["save"] = "сохранить",
|
||||
["run speed"] = "скорость бега",
|
||||
["orientation"] = "ориентировка",
|
||||
["material 2d"] = "2d материал",
|
||||
["root owner"] = "коренной владелец",
|
||||
["right click to add parts"] = "правая кнопка, чтобы добавить части",
|
||||
["tint color"] = "оттенок цвета",
|
||||
["editor"] = "редактор",
|
||||
["hide ragdoll on death"] = "скрыть рэгдолл при смерти",
|
||||
["cut"] = "вырезать",
|
||||
["show editor"] = "показать редактор",
|
||||
["offset"] = "смещение",
|
||||
["behavior"] = "поведение",
|
||||
["owner health"] = "здоровье владельца",
|
||||
["material 3d"] = "3d материал",
|
||||
["min"] = "mon",
|
||||
["camera follow"] = "слежение камерой",
|
||||
["paste"] = "вставить",
|
||||
["wear on server"] = "одеть на сервере",
|
||||
["wear parts"] = "одеть части",
|
||||
["true"] = "вкл.",
|
||||
["toggle focus"] = "переключить фокус",
|
||||
["texture filter"] = "фильтр текстур",
|
||||
["delete directory"] = "удалить папку",
|
||||
["is client"] = "является клиентом",
|
||||
["input"] = "ввод",
|
||||
["event"] = "событие",
|
||||
["false"] = "выкл",
|
||||
["discord / pac3 chat"] = "discord / чат pac3",
|
||||
["variable name"] = "имя переменной",
|
||||
["decal"] = "декаль",
|
||||
["is voice chatting"] = "говорит голосом",
|
||||
["effects"] = "эффекты",
|
||||
["language"] = "язык",
|
||||
["draw order"] = "порядок отрисовки",
|
||||
["mute footsteps"] = "заглушить звуки шагов",
|
||||
["view"] = "вид",
|
||||
["load from url"] = "загрузить по ссылке",
|
||||
["hide"] = "спрятать",
|
||||
["delete"] = "удалить",
|
||||
["my outfit"] = "мой костюм",
|
||||
["no texture filtering"] = "без фильтрации текстур",
|
||||
["gravitygun punt"] = "удар гравипушкой",
|
||||
["is crouching"] = "присел",
|
||||
["range"] = "дальность",
|
||||
["sprite"] = "спрайт",
|
||||
["say"] = "сказать",
|
||||
["timer"] = "таймер",
|
||||
["text"] = "текст",
|
||||
["pastebin urls also work!"] = "pastebin url так-же работают!",
|
||||
["resolution"] = "разрешение",
|
||||
["load"] = "загрузить",
|
||||
["parent name"] = "имя родителя",
|
||||
["sound"] = "звук",
|
||||
["walk speed"] = "скорость шага",
|
||||
["clear"] = "очистить",
|
||||
["is in noclip"] = "в noclip",
|
||||
["enable"] = "включить",
|
||||
["holdtype"] = "holdtype",
|
||||
["draw distance"] = "дальность прорисовки",
|
||||
["add parts to me!"] = "добавь частей ко мне!",
|
||||
["scale this and children"] = "масштабировать это и потомков",
|
||||
["name:"] = "назввание:",
|
||||
["font"] = "шрифт",
|
||||
["use player color"] = "использовать цвет игрока",
|
||||
["sequence name"] = "имя последовательности",
|
||||
["material"] = "материал",
|
||||
["is under water"] = "под водой",
|
||||
["group"] = "группа",
|
||||
["affect children only"] = "влиять только на потомков",
|
||||
["options"] = "опции",
|
||||
["mute sounds"] = "заглушить звуки",
|
||||
["sprint speed"] = "скорость спринта",
|
||||
["end alpha"] = "конечная альфа",
|
||||
["frequency"] = "частота",
|
||||
["script"] = "скрипт",
|
||||
["draw in reflections"] = "рисовать в отражениях",
|
||||
["new directory"] = "новая папка",
|
||||
["clip"] = "обрезка",
|
||||
["duplicate"] = "дублировать",
|
||||
["is flashlight on"] = "включен фонарик",
|
||||
["blur length"] = "длинна размытия",
|
||||
["tools"] = "инструменты",
|
||||
["toggle basic mode"] = "переключить базовый режим",
|
||||
["disable input"] = "отключить ввод",
|
||||
["random"] = "рандом",
|
||||
["position"] = "позиция",
|
||||
["use weapon color"] = "использовать цвет оружия",
|
||||
["angles"] = "углы",
|
||||
["help i have ocd (rounding numbers)"] = "помогите, у меня ОКР (округлить цифры)",
|
||||
["suppress frames"] = "игнорировать кадры",
|
||||
["about"] = "об авторах",
|
||||
["advanced mode"] = "продвинутый режим",
|
||||
["show deprecated features"] = "показать устаревшие возможности",
|
||||
["draw shadow"] = "показывать тень",
|
||||
["translation editor"] = "редактор перевода",
|
||||
["eye angles"] = "направление глаз",
|
||||
["position offset"] = "смещение позиции",
|
||||
["examples"] = "примеры",
|
||||
["toggle camera follow"] = "переключить следование камерой",
|
||||
["edit"] = "правка",
|
||||
["owner armor"] = "броня владельца",
|
||||
["end color"] = "конечный цвет",
|
||||
["effect"] = "эффект",
|
||||
["poseparameter"] = "параметрпозы",
|
||||
["max"] = "max",
|
||||
["double face"] = "двойное лицо",
|
||||
["outline color"] = "цвет обводки",
|
||||
["other"] = "другое",
|
||||
["outline alpha"] = "альфа обводки",
|
||||
["invert"] = "инверсия",
|
||||
["t pose"] = "т-поза",
|
||||
["proxy"] = "прокси",
|
||||
["operator"] = "оператор",
|
||||
["model name"] = "имя модели",
|
||||
["health lost"] = "потерянное здоровье",
|
||||
["size in units:"] = "размер в юнитах:",
|
||||
["end point name"] = "имя конечной точки",
|
||||
["width"] = "ширина",
|
||||
["translucent"] = "полупрозрачный",
|
||||
["toggle t pose"] = "переключить т-позу",
|
||||
}
|
||||
185
lua/pac3/editor/client/translations/turkish.lua
Normal file
185
lua/pac3/editor/client/translations/turkish.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
return {
|
||||
["trail"] = "iz",
|
||||
["texture filter"] = "doku filtresi",
|
||||
["show only with active weapon"] = "sadece aktif silah çıkmışken göster",
|
||||
["parent velocity length"] = "ebeveyn hız büyükluğü",
|
||||
["equal"] = "eşit",
|
||||
["owner velocity right"] = "sahip sağ hızı",
|
||||
["owner velocity length"] = "sahip hız büyüklüğü",
|
||||
["timerx"] = "zamanlayıcıX",
|
||||
["shake"] = "sallanma",
|
||||
["entity class"] = "entity adı",
|
||||
["load from url"] = "url'den parçaları aç",
|
||||
["owner velocity forward"] = "sahip hız ileri",
|
||||
["light"] = "ışık",
|
||||
["event"] = "olay",
|
||||
["follow part name"] = "takip edilecek parça adı",
|
||||
["is crouching"] = "eğiliyor",
|
||||
["language"] = "dil",
|
||||
["lock 1"] = "kilit 1",
|
||||
["find"] = "find (bul)",
|
||||
["orientation"] = "oryantasyon",
|
||||
["help"] = "yardım",
|
||||
["play count"] = "oynatma sayısı (0 sonsuz)",
|
||||
["generic"] = "generik",
|
||||
["speed"] = "hız",
|
||||
["projectile"] = "mermi (projectile)",
|
||||
["command"] = "komut",
|
||||
["events"] = "olaylar",
|
||||
["weapon"] = "silah",
|
||||
["size"] = "boyut",
|
||||
["clone"] = "klon",
|
||||
["has weapon"] = "silahı var",
|
||||
["load parts"] = "parçaları aç",
|
||||
["load"] = "aç",
|
||||
["hide editor"] = "editorü sakla",
|
||||
["hide entity"] = "entity gizle",
|
||||
["health lost"] = "can kayıp",
|
||||
["name"] = "isim",
|
||||
["particles"] = "parçacık",
|
||||
["is on ground"] = "yerde",
|
||||
["loop"] = "dongü",
|
||||
["above"] = "üzeri",
|
||||
["reset view"] = "görüşü sıfırla",
|
||||
["reset eye angles"] = "ğöz açısını sıfırla",
|
||||
["physics"] = "fizik",
|
||||
["draw weapon"] = "silahı göster",
|
||||
["lock 0"] = "kilit 0",
|
||||
["true"] = "gerçek ",
|
||||
["position offset"] = "pozisyon çıkıntısı",
|
||||
["weapon class"] = "silah tipi",
|
||||
["jiggle"] = "sallantı",
|
||||
["new file"] = "yeni dosya",
|
||||
["advanced"] = "gelişmiş",
|
||||
["color"] = "renk",
|
||||
["skirt"] = "etek",
|
||||
["hide weapon"] = "silahı gizle",
|
||||
["player"] = "oyuncu",
|
||||
["ammo"] = "cephane",
|
||||
["parent velocity forward"] = "ebeveyn hız ileri",
|
||||
["exit"] = "çık",
|
||||
["alternative bones"] = "alternatif kemikler",
|
||||
["revert"] = "geri dödür",
|
||||
["modified"] = "modifiye",
|
||||
["angle offset"] = "açı çıkıntısı",
|
||||
["sunbeams"] = "güneş ışını",
|
||||
["remove"] = "kaldır",
|
||||
["max pitch"] = "max eğim",
|
||||
["volume"] = "ses şiddeti",
|
||||
["ok"] = "tamam",
|
||||
["ogg"] = "ogg",
|
||||
["outline"] = "dışçizg",
|
||||
["invert"] = "çevir",
|
||||
["holdtype"] = "tutma türü",
|
||||
["alternative scaling"] = "alternatif büyütme",
|
||||
["tint color"] = "tint rengi",
|
||||
["is in noclip"] = "noclipde",
|
||||
["show editor"] = "editorü göster",
|
||||
["play on footstep"] = "ayak sesi olarak oynat",
|
||||
["parent velocity up"] = "ebeveyn yukarı hızı",
|
||||
["paste"] = "yapıştır",
|
||||
["wear on server"] = "parçaları sunucuda giy",
|
||||
["wear parts"] = "parçaları giy",
|
||||
["toggle focus"] = "focus geçiş",
|
||||
["toggle basic mode"] = "kolay moda geçis",
|
||||
["scale"] = "boyut",
|
||||
["pitch"] = "eğim",
|
||||
["aim part name"] = "hedef parça adı",
|
||||
["draw order"] = "görünum mesafesi",
|
||||
["is under water"] = "su altında",
|
||||
["view"] = "görüntüle",
|
||||
["clip"] = "kırpma",
|
||||
["is voice chatting"] = "mikrafon kullanıyor",
|
||||
["copy global id"] = "global id kopyala",
|
||||
["copy"] = "kopyala",
|
||||
["equal or below"] = "eşit yada altında",
|
||||
["draw distance"] = "görüş mesafesi",
|
||||
["hide"] = "sakla",
|
||||
["position"] = "pozisyon",
|
||||
["duplicate"] = "kopya yarat",
|
||||
["editor"] = "düzenleyici ",
|
||||
["timer"] = "zamanlayıcı",
|
||||
["is client"] = "client ",
|
||||
["operators"] = "operatörler",
|
||||
["edit"] = "duzenle",
|
||||
["modifiers"] = "modifiyeciler",
|
||||
["effects"] = "efektler",
|
||||
["sound"] = "ses",
|
||||
["owner armor"] = "sahip armoru",
|
||||
["sound level"] = "ses aşaması",
|
||||
["equal or above"] = "eşit yada üzerinde",
|
||||
["enable"] = "etkin hale getir",
|
||||
["eye angles"] = "göz açısı",
|
||||
["footstep"] = "ayakbasma",
|
||||
["fog"] = "sis",
|
||||
["health"] = "can",
|
||||
["false"] = "yanlış",
|
||||
["put parts in submenu"] = "parçaları submenüye koy",
|
||||
["translation editor"] = "dil editörü",
|
||||
["min pitch"] = "min eğim",
|
||||
["material"] = "doku (material)",
|
||||
["filename:"] = "dosya adı: ",
|
||||
["group"] = "gurup",
|
||||
["filter type"] = "filter type (filtre tipi)",
|
||||
["find simple"] = "find simple (kolay bul)",
|
||||
["fall apart on death"] = "olünce parçaları düşür",
|
||||
["font"] = "font",
|
||||
["fullbright"] = "fullparlak",
|
||||
["getting started"] = "başlarken",
|
||||
["affect children only"] = "sadece childreni etkile",
|
||||
["draw in reflections"] = "yansımalarda çiz",
|
||||
["is flashlight on"] = "ışığı açık",
|
||||
["owner entity"] = "sahip varlığı (entity)",
|
||||
["lock 2"] = "kilit 2",
|
||||
["save"] = "kaydet",
|
||||
["pause on hide"] = "saklamada durdur",
|
||||
["tools"] = "araçlar",
|
||||
["maybe"] = "belki",
|
||||
["owner velocity up"] = "sahip yukarı hızı",
|
||||
["vehicle class"] = "araç tipi",
|
||||
["spawn as props"] = "prop olarak meydana getir",
|
||||
["save parts"] = "parçaları kaydet",
|
||||
["outline alpha"] = "dışçizg alfası",
|
||||
["parent velocity right"] = "ebeveyn sağ hızı",
|
||||
["show weapon"] = "silahı göster",
|
||||
["about"] = "hakkında",
|
||||
["options"] = "ayarlar",
|
||||
["show deprecated features"] = "kaldırlılmış özellikleri göster",
|
||||
["draw shadow"] = "gölgeyi göster",
|
||||
["parent name"] = "ebeveyn adı",
|
||||
["alpha"] = "alfa",
|
||||
["show in firstperson"] = "birinci şahıs (firstperson) da göster",
|
||||
["examples"] = "örnekler",
|
||||
["advanced mode"] = "gelişmiş modu",
|
||||
["stop on hide"] = "saklayınca durdur",
|
||||
["owner name"] = "sahip adı",
|
||||
["overlapping"] = "çakışma",
|
||||
["effect"] = "efekt",
|
||||
["wear"] = "giy",
|
||||
["say"] = "söyle",
|
||||
["sequence name"] = "sekans adı ",
|
||||
["outline color"] = "dışçizgi rengi",
|
||||
["other"] = "başka",
|
||||
["light blend"] = "ışık blend",
|
||||
["mute footsteps"] = "ayak sesini kapat",
|
||||
["move speed"] = "ilerleme hızı",
|
||||
["t pose"] = "t pozu",
|
||||
["text"] = "yazı",
|
||||
["model name"] = "model adı",
|
||||
["owner health"] = "sahip canı",
|
||||
["toggle camera follow"] = "camera takibine geçiş",
|
||||
["time"] = "zaman",
|
||||
["position grid size"] = "pozisyon grid boyutu",
|
||||
["not equal"] = "eşit değil",
|
||||
["toggle t pose"] = "t posuna geçiş",
|
||||
}
|
||||
140
lua/pac3/editor/client/undo.lua
Normal file
140
lua/pac3/editor/client/undo.lua
Normal file
@@ -0,0 +1,140 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
function pace.ClearUndo()
|
||||
pace.UndoPosition = 1
|
||||
pace.UndoHistory = {}
|
||||
end
|
||||
|
||||
local function get_current_outfit()
|
||||
local data = {}
|
||||
|
||||
for key, part in pairs(pac.GetLocalParts()) do
|
||||
if not part:HasParent() and part:GetShowInEditor() then
|
||||
table.insert(data, part:ToUndoTable())
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
local function find_uid_part(part, findpart)
|
||||
for _, child in ipairs(part.children) do
|
||||
if child.self.UniqueID == findpart.self.UniqueID then
|
||||
return child
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function diff_remove(a, b, aparent, bparent)
|
||||
|
||||
for _, apart in ipairs(a.children) do
|
||||
local bpart = find_uid_part(b, apart)
|
||||
|
||||
if not bpart then
|
||||
local part = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), apart.self.UniqueID)
|
||||
local parent = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), apart.self.ParentUID)
|
||||
|
||||
if part:IsValid() then
|
||||
if part:GetParent() == parent then
|
||||
do
|
||||
local parent = part:GetParent()
|
||||
if parent:IsValid() then pace.Call("PartSelected", parent) end
|
||||
end
|
||||
part:Remove()
|
||||
end
|
||||
end
|
||||
else
|
||||
diff_remove(apart, bpart, a, b)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function diff_create(a, b, aparent, bparent)
|
||||
|
||||
for _, bpart in ipairs(b.children) do
|
||||
local apart = find_uid_part(a, bpart)
|
||||
|
||||
if apart then
|
||||
for key, aval in pairs(apart.self) do
|
||||
local bval = bpart.self[key]
|
||||
|
||||
if aval ~= bval then
|
||||
local part = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), bpart.self.UniqueID)
|
||||
local parent = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), apart.self.ParentUID)
|
||||
|
||||
if part:IsValid() and part:GetParent() == parent then
|
||||
if part["Set" .. key] then
|
||||
pace.Call("VariableChanged", part, key, bval, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
diff_create(apart, bpart, a, b)
|
||||
else
|
||||
local part = pac.CreatePart(bpart.self.ClassName)
|
||||
part:SetUndoTable(bpart)
|
||||
pace.Call("PartSelected", part)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.ApplyDifference(data)
|
||||
local A = get_current_outfit()
|
||||
local B = data
|
||||
|
||||
diff_remove({children = A}, {children = B})
|
||||
diff_create({children = A}, {children = B})
|
||||
end
|
||||
|
||||
pace.ClearUndo()
|
||||
|
||||
local last_json
|
||||
|
||||
function pace.RecordUndoHistory()
|
||||
local data = get_current_outfit()
|
||||
|
||||
local json = util.TableToJSON(data)
|
||||
if json == last_json then return end
|
||||
last_json = json
|
||||
|
||||
for i = pace.UndoPosition + 1, #pace.UndoHistory do
|
||||
table.remove(pace.UndoHistory)
|
||||
end
|
||||
|
||||
table.insert(pace.UndoHistory, data)
|
||||
pace.UndoPosition = #pace.UndoHistory
|
||||
end
|
||||
|
||||
function pace.Undo()
|
||||
pace.UndoPosition = math.Clamp(pace.UndoPosition - 1, 0, #pace.UndoHistory)
|
||||
local data = pace.UndoHistory[pace.UndoPosition]
|
||||
|
||||
if data then
|
||||
pace.ApplyDifference(data)
|
||||
pace.FlashNotification("Undo position: " .. pace.UndoPosition .. "/" .. #pace.UndoHistory)
|
||||
else
|
||||
pace.FlashNotification('Nothing to undo')
|
||||
end
|
||||
end
|
||||
|
||||
function pace.Redo()
|
||||
pace.UndoPosition = math.Clamp(pace.UndoPosition + 1, 1, #pace.UndoHistory + 1)
|
||||
local data = pace.UndoHistory[pace.UndoPosition]
|
||||
|
||||
if data then
|
||||
pace.ApplyDifference(data)
|
||||
pace.FlashNotification("Undo position: " .. pace.UndoPosition .. "/" .. #pace.UndoHistory)
|
||||
else
|
||||
pace.FlashNotification('Nothing to redo')
|
||||
end
|
||||
end
|
||||
240
lua/pac3/editor/client/util.lua
Normal file
240
lua/pac3/editor/client/util.lua
Normal file
@@ -0,0 +1,240 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pace.util = {}
|
||||
|
||||
local surface = surface
|
||||
local math = math
|
||||
|
||||
local white = surface.GetTextureID("gui/center_gradient.vtf")
|
||||
|
||||
function pace.util.DrawLine(x1, y1, x2, y2, w, skip_tex)
|
||||
w = w or 1
|
||||
if not skip_tex then surface.SetTexture(white) end
|
||||
|
||||
local dx,dy = x1-x2, y1 - y2
|
||||
local ang = math.atan2(dx, dy)
|
||||
local dst = math.sqrt((dx * dx) + (dy * dy))
|
||||
|
||||
x1 = x1 - dx * 0.5
|
||||
y1 = y1 - dy * 0.5
|
||||
|
||||
surface.DrawTexturedRectRotated(x1, y1, w, dst, math.deg(ang))
|
||||
end
|
||||
|
||||
function pace.util.FastDistance(x1, y1, z1, x2, y2, z2)
|
||||
return math.sqrt((x2 - x1) ^ 2 + (y2 - y1) ^ 2 + (z2 - z1) ^ 2)
|
||||
end
|
||||
|
||||
function pace.util.FastDistance2D(x1, y1, x2, y2)
|
||||
return math.sqrt((x2 - x1) ^ 2 + (y2 - y1) ^ 2)
|
||||
end
|
||||
|
||||
local function isUpperCase(charIn)
|
||||
return
|
||||
charIn == 'A' or
|
||||
charIn == 'B' or
|
||||
charIn == 'C' or
|
||||
charIn == 'D' or
|
||||
charIn == 'E' or
|
||||
charIn == 'F' or
|
||||
charIn == 'G' or
|
||||
charIn == 'H' or
|
||||
charIn == 'I' or
|
||||
charIn == 'J' or
|
||||
charIn == 'K' or
|
||||
charIn == 'L' or
|
||||
charIn == 'M' or
|
||||
charIn == 'N' or
|
||||
charIn == 'O' or
|
||||
charIn == 'P' or
|
||||
charIn == 'Q' or
|
||||
charIn == 'R' or
|
||||
charIn == 'S' or
|
||||
charIn == 'T' or
|
||||
charIn == 'U' or
|
||||
charIn == 'V' or
|
||||
charIn == 'W' or
|
||||
charIn == 'X' or
|
||||
charIn == 'Y' or
|
||||
charIn == 'Z'
|
||||
end
|
||||
|
||||
function pace.util.FriendlyName(strIn)
|
||||
local prevChar
|
||||
local outputTab = {}
|
||||
local iterableArray = string.Explode('', strIn)
|
||||
|
||||
for i, charIn in ipairs(iterableArray) do
|
||||
if not prevChar and not isUpperCase(charIn) or prevChar == ' ' and not isUpperCase(charIn) then
|
||||
prevChar = string.upper(charIn)
|
||||
table.insert(outputTab, prevChar)
|
||||
elseif charIn == '_' then
|
||||
iterableArray[i] = ' '
|
||||
prevChar = ' '
|
||||
table.insert(outputTab, ' ')
|
||||
elseif isUpperCase(charIn) then
|
||||
if prevChar == '_' and (not iterableArray[i + 1] or isUpperCase(iterableArray[i + 1])) then
|
||||
if charIn == 'L' then
|
||||
prevChar = ' '
|
||||
table.insert(outputTab, 'Left ')
|
||||
elseif charIn == 'R' then
|
||||
prevChar = ' '
|
||||
table.insert(outputTab, 'Right ')
|
||||
-- elseif charIn == 'O' then
|
||||
-- prevChar = ' '
|
||||
-- table.insert(outputTab, 'Open ') -- i guess?
|
||||
else
|
||||
prevChar = charIn
|
||||
table.insert(outputTab, charIn)
|
||||
end
|
||||
elseif not isUpperCase(prevChar) then
|
||||
prevChar = charIn
|
||||
table.insert(outputTab, ' ')
|
||||
table.insert(outputTab, charIn)
|
||||
else
|
||||
prevChar = charIn
|
||||
table.insert(outputTab, charIn)
|
||||
end
|
||||
else
|
||||
local condUpper =
|
||||
charIn == 'm' and iterableArray[i + 1] == 'p' and iterableArray[i - 1] == ' ' or
|
||||
charIn == 'p' and iterableArray[i - 1] == 'm' and iterableArray[i - 2] == ' ' or
|
||||
charIn == 'w' and (iterableArray[i - 1] == 'C' or iterableArray[i - 1] == 'c') or
|
||||
charIn == 'c' and iterableArray[i + 1] == 'w' or
|
||||
charIn == 'i' and (iterableArray[i - 1] == 'C' or iterableArray[i - 1] == 'c') or
|
||||
charIn == 'c' and iterableArray[i + 1] == 'i' or
|
||||
(charIn == 'x' or charIn == 'y' or charIn == 'z') and not iterableArray[i + 1] and iterableArray[i - 1] == ' '
|
||||
|
||||
if condUpper then
|
||||
prevChar = string.upper(charIn)
|
||||
table.insert(outputTab, prevChar)
|
||||
else
|
||||
prevChar = charIn
|
||||
table.insert(outputTab, charIn)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(outputTab, '')
|
||||
end
|
||||
|
||||
|
||||
function pace.MessagePrompt( 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 )
|
||||
Window:SetSizable(true)
|
||||
Window:SetTall(300)
|
||||
Window:SetWide(500)
|
||||
Window:Center()
|
||||
|
||||
Window.OnRemove = function()
|
||||
hook.Remove("Think", "pace_modal_escape")
|
||||
end
|
||||
|
||||
hook.Add("Think", "pace_modal_escape", function()
|
||||
if input.IsKeyDown(KEY_ESCAPE) then
|
||||
if Window:IsValid() then
|
||||
Window:Remove()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local DScrollPanel = vgui.Create( "DScrollPanel", Window )
|
||||
DScrollPanel:Dock( FILL )
|
||||
|
||||
local Text = DScrollPanel:Add("DLabel")
|
||||
Text:SetText( strText or "Message Text" )
|
||||
Text:SetTextColor( color_white )
|
||||
Text:Dock(FILL)
|
||||
Text:SetAutoStretchVertical(true)
|
||||
Text:SetWrap(true)
|
||||
|
||||
local Button = vgui.Create( "DButton", Window )
|
||||
Button:SetText( strButtonText or "OK" )
|
||||
Button:Dock(BOTTOM)
|
||||
Button:SetTall( 20 )
|
||||
Button:SetPos( 5, 5 )
|
||||
Button.DoClick = function() Window:Close() end
|
||||
|
||||
Window:MakePopup()
|
||||
Window:DoModal()
|
||||
|
||||
Window:PerformLayout()
|
||||
|
||||
return Window
|
||||
end
|
||||
|
||||
function pace.MultilineStringRequest( strTitle, strText, strDefaultText, fnEnter, fnCancel, strButtonText, strButtonCancelText )
|
||||
if IsValid(pace.last_modal) then pace.last_modal:Remove() end
|
||||
local Window = vgui.Create( "DFrame" )
|
||||
pace.last_modal = Window
|
||||
Window:SetTitle( strTitle or "Message Title (First Parameter)" )
|
||||
Window:SetDraggable( false )
|
||||
Window:ShowCloseButton( false )
|
||||
Window:SetBackgroundBlur( true )
|
||||
Window:SetDrawOnTop( true )
|
||||
Window:SetSize(400, 400)
|
||||
|
||||
local InnerPanel = vgui.Create( "DPanel", Window )
|
||||
InnerPanel:SetPaintBackground( false )
|
||||
InnerPanel:Dock( FILL )
|
||||
|
||||
local Text = vgui.Create( "DLabel", InnerPanel )
|
||||
Text:SetText( strText or "Message Text (Second Parameter)" )
|
||||
Text:SizeToContents()
|
||||
Text:SetContentAlignment( 5 )
|
||||
Text:SetTextColor( color_white )
|
||||
Text:Dock( TOP )
|
||||
|
||||
local TextEntry = vgui.Create( "DTextEntry", InnerPanel )
|
||||
TextEntry:SetText( strDefaultText or "" )
|
||||
TextEntry:SetMultiline(true)
|
||||
TextEntry:Dock(FILL)
|
||||
TextEntry:SetUpdateOnType(true)
|
||||
TextEntry.OnChange = function(self) self:SetText(self:GetValue():gsub("\t", " ")) end
|
||||
TextEntry.OnEnter = function() Window:Close() fnEnter( TextEntry:GetValue() ) end
|
||||
|
||||
local ButtonPanel = vgui.Create( "DPanel", Window )
|
||||
ButtonPanel:SetTall( 30 )
|
||||
ButtonPanel:SetPaintBackground( false )
|
||||
ButtonPanel:Dock(BOTTOM)
|
||||
|
||||
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 )
|
||||
|
||||
Window:MakePopup()
|
||||
Window:DoModal()
|
||||
Window:Center()
|
||||
|
||||
return Window
|
||||
|
||||
end
|
||||
592
lua/pac3/editor/client/view.lua
Normal file
592
lua/pac3/editor/client/view.lua
Normal file
@@ -0,0 +1,592 @@
|
||||
--[[
|
||||
| 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 acsfnc = function(key, def)
|
||||
pace["View" .. key] = def
|
||||
pace["SetView" .. key] = function(val) pace["View" .. key] = val end
|
||||
pace["GetView" .. key] = function() return pace["View" .. key] or def end
|
||||
end
|
||||
|
||||
acsfnc("Entity", NULL)
|
||||
acsfnc("Pos", Vector(5,5,5))
|
||||
acsfnc("Angles", Angle(0,0,0))
|
||||
acsfnc("FOV", 75)
|
||||
|
||||
function pace.GetViewEntity()
|
||||
return pace.ViewEntity:IsValid() and pace.ViewEntity or pac.LocalPlayer
|
||||
end
|
||||
|
||||
function pace.ResetView()
|
||||
if pace.Focused then
|
||||
local ent = pace.GetViewEntity()
|
||||
|
||||
if not ent:IsValid() then
|
||||
local _, part = next(pac.GetLocalParts())
|
||||
if part then
|
||||
ent = part:GetOwner()
|
||||
end
|
||||
end
|
||||
|
||||
if ent:IsValid() then
|
||||
local fwd = ent.EyeAngles and ent:EyeAngles() or ent:GetAngles()
|
||||
|
||||
-- Source Engine local angles fix
|
||||
if ent == pac.LocalPlayer and ent:GetVehicle():IsValid() then
|
||||
local ang = ent:GetVehicle():GetAngles()
|
||||
fwd = fwd + ang
|
||||
end
|
||||
|
||||
fwd = fwd:Forward()
|
||||
fwd.z = 0
|
||||
pace.ViewPos = ent:EyePos() + fwd * 128
|
||||
pace.ViewAngles = (ent:EyePos() - pace.ViewPos):Angle()
|
||||
pace.ViewAngles:Normalize()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.SetZoom(fov, smooth)
|
||||
if smooth then
|
||||
pace.ViewFOV = Lerp(FrameTime()*10, pace.ViewFOV, math.Clamp(fov,1,100))
|
||||
else
|
||||
pace.ViewFOV = math.Clamp(fov,1,100)
|
||||
end
|
||||
end
|
||||
|
||||
function pace.ResetZoom()
|
||||
pace.zoom_reset = 75
|
||||
end
|
||||
|
||||
local worldPanel = vgui.GetWorldPanel();
|
||||
function worldPanel.OnMouseWheeled( self, scrollDelta )
|
||||
if IsValid(pace.Editor) then
|
||||
local zoom_usewheel = GetConVar( "pac_zoom_mousewheel" )
|
||||
|
||||
if zoom_usewheel:GetInt() == 1 then
|
||||
local speed = 10
|
||||
|
||||
if input.IsKeyDown(KEY_LSHIFT) then
|
||||
speed = 50
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_LCONTROL) then
|
||||
speed = 1
|
||||
end
|
||||
|
||||
if vgui.GetHoveredPanel() == worldPanel then
|
||||
pace.Editor.zoomslider:SetValue(pace.ViewFOV - (scrollDelta * speed))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local held_ang = Angle(0,0,0)
|
||||
local held_mpos = Vector(0,0,0)
|
||||
local mcode, hoveredPanelCursor, isHoldingMovement
|
||||
|
||||
function pace.GUIMousePressed(mc)
|
||||
if pace.mctrl.GUIMousePressed(mc) then return end
|
||||
|
||||
if mc == MOUSE_LEFT and not pace.editing_viewmodel then
|
||||
held_ang = pace.ViewAngles * 1
|
||||
held_mpos = Vector(input.GetCursorPos())
|
||||
end
|
||||
|
||||
if mc == MOUSE_RIGHT then
|
||||
pace.Call("OpenMenu")
|
||||
end
|
||||
|
||||
hoveredPanelCursor = vgui.GetHoveredPanel()
|
||||
|
||||
if IsValid(hoveredPanelCursor) then
|
||||
hoveredPanelCursor:SetCursor('sizeall')
|
||||
end
|
||||
|
||||
mcode = mc
|
||||
isHoldingMovement = true
|
||||
end
|
||||
|
||||
function pace.GUIMouseReleased(mc)
|
||||
isHoldingMovement = false
|
||||
|
||||
if IsValid(hoveredPanelCursor) then
|
||||
hoveredPanelCursor:SetCursor('arrow')
|
||||
hoveredPanelCursor = nil
|
||||
end
|
||||
|
||||
if pace.mctrl.GUIMouseReleased(mc) then return end
|
||||
|
||||
if pace.editing_viewmodel or pace.editing_hands then return end
|
||||
|
||||
mcode = nil
|
||||
if not GetConVar("pac_enable_editor_view"):GetBool() then pace.EnableView(true)
|
||||
else
|
||||
pac.RemoveHook("CalcView", "camera_part")
|
||||
pac.AddHook("GUIMousePressed", "editor", pace.GUIMousePressed)
|
||||
pac.AddHook("GUIMouseReleased", "editor", pace.GUIMouseReleased)
|
||||
pac.AddHook("ShouldDrawLocalPlayer", "editor", pace.ShouldDrawLocalPlayer, DLib and -4 or ULib and -1 or nil)
|
||||
pac.AddHook("CalcView", "editor", pace.CalcView, DLib and -4 or ULib and -1 or nil)
|
||||
pac.AddHook("HUDPaint", "editor", pace.HUDPaint)
|
||||
pac.AddHook("HUDShouldDraw", "editor", pace.HUDShouldDraw)
|
||||
pac.AddHook("PostRenderVGUI", "editor", pace.PostRenderVGUI)
|
||||
end
|
||||
end
|
||||
|
||||
local function set_mouse_pos(x, y)
|
||||
input.SetCursorPos(x, y)
|
||||
held_ang = pace.ViewAngles * 1
|
||||
held_mpos = Vector(x, y)
|
||||
return held_mpos * 1
|
||||
end
|
||||
|
||||
local WORLD_ORIGIN = Vector(0, 0, 0)
|
||||
|
||||
local function CalcDrag()
|
||||
|
||||
if
|
||||
pace.BusyWithProperties:IsValid() or
|
||||
pace.ActiveSpecialPanel:IsValid() or
|
||||
pace.editing_viewmodel or
|
||||
pace.editing_hands or
|
||||
pace.properties.search:HasFocus()
|
||||
then return end
|
||||
|
||||
local focus = vgui.GetKeyboardFocus()
|
||||
if focus and focus:IsValid() and focus:GetName():lower():find('textentry') then return end
|
||||
|
||||
if not system.HasFocus() then
|
||||
held_mpos = Vector(input.GetCursorPos())
|
||||
end
|
||||
|
||||
local ftime = FrameTime() * 50
|
||||
local mult = 5
|
||||
|
||||
if input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL) then
|
||||
mult = 0.1
|
||||
end
|
||||
|
||||
local origin
|
||||
local part = pace.current_part or NULL
|
||||
|
||||
if not part:IsValid() then return end
|
||||
|
||||
local owner = part:GetRootPart():GetOwner()
|
||||
if not owner:IsValid() then
|
||||
owner = pac.LocalPlayer
|
||||
end
|
||||
|
||||
origin = owner:GetPos()
|
||||
if owner == pac.WorldEntity then
|
||||
if part:HasChildren() then
|
||||
for key, child in ipairs(part:GetChildren()) do
|
||||
if child.GetDrawPosition then
|
||||
part = child
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if part.GetDrawPosition then
|
||||
origin = part:GetDrawPosition()
|
||||
end
|
||||
|
||||
if not origin or origin == WORLD_ORIGIN then
|
||||
origin = pac.LocalPlayer:GetPos()
|
||||
end
|
||||
|
||||
mult = mult * math.min(origin:Distance(pace.ViewPos) / 200, 3)
|
||||
|
||||
if input.IsKeyDown(KEY_LSHIFT) then
|
||||
mult = mult + 5
|
||||
end
|
||||
|
||||
if not pace.IsSelecting then
|
||||
if mcode == MOUSE_LEFT then
|
||||
local mpos = Vector(input.GetCursorPos())
|
||||
|
||||
if mpos.x >= ScrW() - 1 then
|
||||
mpos = set_mouse_pos(1, gui.MouseY())
|
||||
elseif mpos.x < 1 then
|
||||
mpos = set_mouse_pos(ScrW() - 2, gui.MouseY())
|
||||
end
|
||||
|
||||
if mpos.y >= ScrH() - 1 then
|
||||
mpos = set_mouse_pos(gui.MouseX(), 1)
|
||||
elseif mpos.y < 1 then
|
||||
mpos = set_mouse_pos(gui.MouseX(), ScrH() - 2)
|
||||
end
|
||||
|
||||
local delta = (held_mpos - mpos) / 5 * math.rad(pace.ViewFOV)
|
||||
pace.ViewAngles.p = math.Clamp(held_ang.p - delta.y, -90, 90)
|
||||
pace.ViewAngles.y = held_ang.y + delta.x
|
||||
end
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_W) then
|
||||
pace.ViewPos = pace.ViewPos + pace.ViewAngles:Forward() * mult * ftime
|
||||
elseif input.IsKeyDown(KEY_S) then
|
||||
pace.ViewPos = pace.ViewPos - pace.ViewAngles:Forward() * mult * ftime
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_D) then
|
||||
pace.ViewPos = pace.ViewPos + pace.ViewAngles:Right() * mult * ftime
|
||||
elseif input.IsKeyDown(KEY_A) then
|
||||
pace.ViewPos = pace.ViewPos - pace.ViewAngles:Right() * mult * ftime
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_SPACE) then
|
||||
if not IsValid(pace.timeline.frame) then
|
||||
pace.ViewPos = pace.ViewPos + pace.ViewAngles:Up() * mult * ftime
|
||||
end
|
||||
end
|
||||
|
||||
--[[if input.IsKeyDown(KEY_LALT) then
|
||||
pace.ViewPos = pace.ViewPos + pace.ViewAngles:Up() * -mult * ftime
|
||||
end]]
|
||||
|
||||
|
||||
end
|
||||
|
||||
local follow_entity = CreateClientConVar("pac_camera_follow_entity", "0", true)
|
||||
local enable_editor_view = CreateClientConVar("pac_enable_editor_view", "1", true)
|
||||
local lastEntityPos
|
||||
|
||||
function pace.CalcView(ply, pos, ang, fov)
|
||||
if pace.editing_viewmodel or pace.editing_hands then
|
||||
pace.ViewPos = pos
|
||||
pace.ViewAngles = ang
|
||||
pace.ViewFOV = fov
|
||||
return end
|
||||
|
||||
if follow_entity:GetBool() then
|
||||
local ent = pace.GetViewEntity()
|
||||
local pos = ent:GetPos()
|
||||
lastEntityPos = lastEntityPos or pos
|
||||
pace.ViewPos = pace.ViewPos + pos - lastEntityPos
|
||||
lastEntityPos = pos
|
||||
else
|
||||
lastEntityPos = nil
|
||||
end
|
||||
|
||||
local pos, ang, fov = pac.CallHook("EditorCalcView", pace.ViewPos, pace.ViewAngles, pace.ViewFOV)
|
||||
|
||||
if pos then
|
||||
pace.ViewPos = pos
|
||||
end
|
||||
|
||||
if ang then
|
||||
pace.ViewAngles = ang
|
||||
end
|
||||
|
||||
if fov then
|
||||
pace.ViewFOV = fov
|
||||
end
|
||||
|
||||
return
|
||||
{
|
||||
origin = pace.ViewPos,
|
||||
angles = pace.ViewAngles,
|
||||
fov = pace.ViewFOV,
|
||||
}
|
||||
end
|
||||
|
||||
function pace.ShouldDrawLocalPlayer()
|
||||
if not pace.editing_viewmodel and not pace.editing_hands then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local notifText
|
||||
local notifDisplayTime, notifDisplayTimeFade = 0, 0
|
||||
|
||||
function pace.FlashNotification(text, timeToDisplay)
|
||||
timeToDisplay = timeToDisplay or math.Clamp(#text / 6, 1, 8)
|
||||
notifDisplayTime = RealTime() + timeToDisplay
|
||||
notifDisplayTimeFade = RealTime() + timeToDisplay * 1.1
|
||||
notifText = text
|
||||
end
|
||||
|
||||
function pace.PostRenderVGUI()
|
||||
if not pace.mctrl then return end
|
||||
|
||||
local time = RealTime()
|
||||
|
||||
if notifDisplayTimeFade > time then
|
||||
if notifDisplayTime > time then
|
||||
surface.SetTextColor(color_white)
|
||||
else
|
||||
surface.SetTextColor(255, 255, 255, 255 * (notifDisplayTimeFade - RealTime()) / (notifDisplayTimeFade - notifDisplayTime))
|
||||
end
|
||||
|
||||
surface.SetFont('Trebuchet18')
|
||||
local w = surface.GetTextSize(notifText)
|
||||
surface.SetTextPos(ScrW() / 2 - w / 2, 30)
|
||||
surface.DrawText(notifText)
|
||||
end
|
||||
|
||||
if not isHoldingMovement then return end
|
||||
|
||||
if pace.mctrl.LastThinkCall ~= FrameNumber() then
|
||||
surface.SetFont('Trebuchet18')
|
||||
surface.SetTextColor(color_white)
|
||||
local text = L'You are currently holding the camera, movement is disabled'
|
||||
local w = surface.GetTextSize(text)
|
||||
surface.SetTextPos(ScrW() / 2 - w / 2, 10)
|
||||
surface.DrawText(text)
|
||||
end
|
||||
end
|
||||
|
||||
function pace.EnableView(b)
|
||||
if b then
|
||||
pac.AddHook("GUIMousePressed", "editor", pace.GUIMousePressed)
|
||||
pac.AddHook("GUIMouseReleased", "editor", pace.GUIMouseReleased)
|
||||
pac.AddHook("ShouldDrawLocalPlayer", "editor", pace.ShouldDrawLocalPlayer, DLib and -4 or ULib and -1 or nil)
|
||||
pac.AddHook("CalcView", "editor", pace.CalcView, DLib and -4 or ULib and -1 or nil)
|
||||
pac.RemoveHook("CalcView", "camera_part")
|
||||
pac.AddHook("HUDPaint", "editor", pace.HUDPaint)
|
||||
pac.AddHook("HUDShouldDraw", "editor", pace.HUDShouldDraw)
|
||||
pac.AddHook("PostRenderVGUI", "editor", pace.PostRenderVGUI)
|
||||
pace.Focused = true
|
||||
pace.ResetView()
|
||||
else
|
||||
lastEntityPos = nil
|
||||
pac.RemoveHook("GUIMousePressed", "editor")
|
||||
pac.RemoveHook("GUIMouseReleased", "editor")
|
||||
pac.RemoveHook("ShouldDrawLocalPlayer", "editor")
|
||||
pac.RemoveHook("CalcView", "editor")
|
||||
pac.RemoveHook("CalcView", "camera_part")
|
||||
pac.RemoveHook("HUDPaint", "editor")
|
||||
pac.RemoveHook("HUDShouldDraw", "editor")
|
||||
pac.RemoveHook("PostRenderVGUI", "editor")
|
||||
pace.SetTPose(false)
|
||||
end
|
||||
|
||||
if not enable_editor_view:GetBool() then
|
||||
local ply = LocalPlayer()
|
||||
pac.RemoveHook("CalcView", "editor")
|
||||
pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz)
|
||||
for _, part in pairs(pac.GetLocalParts()) do
|
||||
if part:IsValid() and part.ClassName == "camera" then
|
||||
part:CalcShowHide()
|
||||
local temp = {}
|
||||
if not part:IsHidden() then
|
||||
pos, ang, fov, nearz, farz = part:CalcView(_,_,ply:EyeAngles())
|
||||
temp.origin = pos
|
||||
temp.angles = ang
|
||||
temp.fov = fov
|
||||
temp.znear = nearz
|
||||
temp.zfar = farz
|
||||
temp.drawviewer = not part.DrawViewModel
|
||||
return temp
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
--pac.RemoveHook("ShouldDrawLocalPlayer", "editor")
|
||||
end
|
||||
end
|
||||
|
||||
local function CalcAnimationFix(ent)
|
||||
if ent.SetEyeAngles then
|
||||
ent:SetEyeAngles(Angle(0,0,0))
|
||||
end
|
||||
end
|
||||
|
||||
local reset_pose_params =
|
||||
{
|
||||
"body_rot_z",
|
||||
"spine_rot_z",
|
||||
"head_rot_z",
|
||||
"head_rot_y",
|
||||
"head_rot_x",
|
||||
"walking",
|
||||
"running",
|
||||
"swimming",
|
||||
"rhand",
|
||||
"lhand",
|
||||
"rfoot",
|
||||
"lfoot",
|
||||
"move_yaw",
|
||||
"aim_yaw",
|
||||
"aim_pitch",
|
||||
"breathing",
|
||||
"vertical_velocity",
|
||||
"vehicle_steer",
|
||||
"body_yaw",
|
||||
"spine_yaw",
|
||||
"head_yaw",
|
||||
"head_pitch",
|
||||
"head_roll",
|
||||
}
|
||||
|
||||
function pace.GetTPose()
|
||||
return pace.tposed
|
||||
end
|
||||
|
||||
function pace.SetViewPart(part, reset_campos)
|
||||
pace.SetViewEntity(part:GetRootPart():GetOwner())
|
||||
|
||||
if reset_campos then
|
||||
pace.ResetView()
|
||||
end
|
||||
end
|
||||
|
||||
function pace.HUDPaint()
|
||||
if mcode and not input.IsMouseDown(mcode) then
|
||||
mcode = nil
|
||||
end
|
||||
|
||||
local ent = pace.GetViewEntity()
|
||||
|
||||
if pace.IsFocused() then
|
||||
CalcDrag()
|
||||
|
||||
if ent:IsValid() then
|
||||
pace.Call("Draw", ScrW(), ScrH())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.HUDShouldDraw(typ)
|
||||
if
|
||||
typ == "CHudEPOE" or
|
||||
(typ == "CHudCrosshair" and (pace.editing_viewmodel or pace.editing_hands))
|
||||
then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function pace.OnToggleFocus(show_editor)
|
||||
if pace.Focused then
|
||||
pace.KillFocus(show_editor)
|
||||
else
|
||||
pace.GainFocus(show_editor)
|
||||
end
|
||||
end
|
||||
|
||||
function pace.SetTPose(b)
|
||||
local ply = pac.LocalPlayer
|
||||
|
||||
if b then
|
||||
ply.pace_tpose_last_sequence = ply:GetSequence()
|
||||
ply.pace_tpose_last_layer_sequence = {}
|
||||
for i = 0, 16 do
|
||||
ply.pace_tpose_last_layer_sequence[i] = ply:GetLayerSequence(i)
|
||||
end
|
||||
|
||||
local function reset_angles(ply)
|
||||
local ang = ply:EyeAngles()
|
||||
ang.p = 0
|
||||
ply:SetEyeAngles(ang)
|
||||
ply:SetRenderAngles(ang)
|
||||
ply:SetAngles(ang)
|
||||
end
|
||||
|
||||
local function get_ref_anim(ply)
|
||||
local id = ply:LookupSequence("reference")
|
||||
local id2 = ply:LookupSequence("ragdoll")
|
||||
return id ~= -1 and id or id2 ~= -1 and id2 or 0
|
||||
end
|
||||
|
||||
pac.AddHook("PrePlayerDraw", "pace_tpose", function(ply)
|
||||
if ply ~= pac.LocalPlayer then return end
|
||||
|
||||
for i = 0, 16 do
|
||||
ply:SetLayerSequence(i, 0)
|
||||
end
|
||||
|
||||
ply:SetSequence(get_ref_anim(ply))
|
||||
reset_angles(ply)
|
||||
end)
|
||||
|
||||
pac.AddHook("UpdateAnimation", "pace_tpose", function()
|
||||
local ply = pac.LocalPlayer
|
||||
ply:ClearPoseParameters()
|
||||
reset_angles(ply)
|
||||
|
||||
for i = 0, ply:GetNumPoseParameters() - 1 do
|
||||
local name = ply:GetPoseParameterName(i)
|
||||
if name then
|
||||
ply:SetPoseParameter(name, 0)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
pac.AddHook("CalcMainActivity", "pace_tpose", function(ply)
|
||||
if ply == pac.LocalPlayer then
|
||||
for i = 0, 16 do
|
||||
ply:SetLayerSequence(i, 0)
|
||||
end
|
||||
|
||||
local act = get_ref_anim(ply)
|
||||
|
||||
return act, act
|
||||
end
|
||||
end)
|
||||
else
|
||||
pac.RemoveHook("PrePlayerDraw", "pace_tpose")
|
||||
pac.RemoveHook("UpdateAnimation", "pace_tpose")
|
||||
pac.RemoveHook("CalcMainActivity", "pace_tpose")
|
||||
|
||||
if ply.pace_tpose_last_sequence then
|
||||
ply:SetSequence(ply.pace_tpose_last_sequence)
|
||||
ply.pace_tpose_last_sequence = nil
|
||||
end
|
||||
|
||||
if ply.pace_tpose_last_layer_sequence then
|
||||
for i, seq in ipairs(ply.pace_tpose_last_layer_sequence) do
|
||||
ply:SetLayerSequence(i, seq)
|
||||
end
|
||||
|
||||
ply.pace_tpose_last_layer_sequence = nil
|
||||
end
|
||||
end
|
||||
|
||||
pace.tposed = b
|
||||
end
|
||||
|
||||
pace.SetTPose(pace.tposed)
|
||||
|
||||
function pace.ToggleCameraFollow()
|
||||
local c = GetConVar("pac_camera_follow_entity")
|
||||
RunConsoleCommand("pac_camera_follow_entity", c:GetBool() and "0" or "1")
|
||||
end
|
||||
|
||||
function pace.GetBreathing()
|
||||
return pace.breathing
|
||||
end
|
||||
function pace.ResetEyeAngles()
|
||||
local ent = pace.GetViewEntity()
|
||||
if ent:IsValid() then
|
||||
if ent:IsPlayer() then
|
||||
|
||||
RunConsoleCommand("+forward")
|
||||
timer.Simple(0, function()
|
||||
RunConsoleCommand("-forward")
|
||||
timer.Simple(0.1, function()
|
||||
RunConsoleCommand("+back")
|
||||
timer.Simple(0.015, function()
|
||||
RunConsoleCommand("-back")
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
ent:SetEyeAngles(Angle(0, 0, 0))
|
||||
else
|
||||
ent:SetAngles(Angle(0, 0, 0))
|
||||
end
|
||||
|
||||
pac.SetupBones(ent)
|
||||
end
|
||||
end
|
||||
344
lua/pac3/editor/client/wear.lua
Normal file
344
lua/pac3/editor/client/wear.lua
Normal file
@@ -0,0 +1,344 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
function pace.IsPartSendable(part)
|
||||
|
||||
if part:HasParent() then return false end
|
||||
if not part:GetShowInEditor() then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function pace.WearOnServer(filter)
|
||||
local toWear = {}
|
||||
|
||||
for key, part in pairs(pac.GetLocalParts()) do
|
||||
if pace.IsPartSendable(part) then
|
||||
table.insert(toWear, part)
|
||||
end
|
||||
end
|
||||
|
||||
local transmissionID = math.random(1, 0x7FFFFFFF)
|
||||
|
||||
for i, part in ipairs(toWear) do
|
||||
pace.SendPartToServer(part, {
|
||||
partID = i,
|
||||
totalParts = #toWear,
|
||||
transmissionID = transmissionID,
|
||||
temp_wear_filter = filter,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function pace.ClearParts()
|
||||
pace.ClearUndo()
|
||||
pac.RemoveAllParts(true, true)
|
||||
pace.RefreshTree()
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
if not pace.Editor:IsValid() then return end
|
||||
|
||||
if table.Count(pac.GetLocalParts()) == 0 then
|
||||
pace.Call("CreatePart", "group", L"my outfit")
|
||||
end
|
||||
|
||||
pace.TrySelectPart()
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
|
||||
do -- to server
|
||||
local function net_write_table(tbl)
|
||||
|
||||
local buffer = pac.StringStream()
|
||||
buffer:writeTable(tbl)
|
||||
|
||||
local data = buffer:getString()
|
||||
local ok, err = pcall(net.WriteStream, data)
|
||||
|
||||
if not ok then
|
||||
return ok, err
|
||||
end
|
||||
|
||||
return #data
|
||||
end
|
||||
|
||||
function pace.SendPartToServer(part, extra)
|
||||
local allowed, reason = pac.CallHook("CanWearParts", pac.LocalPlayer)
|
||||
|
||||
if allowed == false then
|
||||
pac.Message(reason or "the server doesn't want you to wear parts for some reason")
|
||||
return false
|
||||
end
|
||||
|
||||
if not pace.IsPartSendable(part) then return false end
|
||||
|
||||
local data = {part = part:ToTable()}
|
||||
|
||||
if extra then
|
||||
table.Merge(data, extra)
|
||||
end
|
||||
|
||||
data.owner = part:GetPlayerOwner()
|
||||
data.wear_filter = pace.CreateWearFilter()
|
||||
|
||||
net.Start("pac_submit")
|
||||
|
||||
local bytes, err = net_write_table(data)
|
||||
|
||||
if not bytes then
|
||||
pace.Notify(false, "unable to transfer data to server: " .. tostring(err or "too big"), pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", part:GetName(), part:GetPrintUniqueID()) or part:GetName())
|
||||
return false
|
||||
end
|
||||
|
||||
net.SendToServer()
|
||||
pac.Message(('Transmitting outfit %q to server (%s)'):format(part.Name or part.ClassName or '<unknown>', string.NiceSize(bytes)))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function pace.RemovePartOnServer(name, server_only, filter)
|
||||
local data = {part = name, server_only = server_only, filter = filter}
|
||||
|
||||
if name == "__ALL__" then
|
||||
pace.CallHook("RemoveOutfit", pac.LocalPlayer)
|
||||
end
|
||||
|
||||
net.Start("pac_submit")
|
||||
local ret,err = net_write_table(data)
|
||||
if ret == nil then
|
||||
pace.Notify(false, "unable to transfer data to server: "..tostring(err or "too big"), name)
|
||||
return false
|
||||
end
|
||||
net.SendToServer()
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
do -- from server
|
||||
function pace.WearPartFromServer(owner, part_data, data, doItNow)
|
||||
pac.dprint("received outfit %q from %s with %i number of children to set on %s", part_data.self.Name or "", tostring(owner), table.Count(part_data.children), part_data.self.OwnerName or "")
|
||||
|
||||
if pace.CallHook("WearPartFromServer", owner, part_data, data) == false then return end
|
||||
|
||||
local dupepart = pac.GetPartFromUniqueID(data.player_uid, part_data.self.UniqueID)
|
||||
|
||||
if dupepart:IsValid() then
|
||||
pac.dprint("removing part %q to be replaced with the part previously received", dupepart.Name)
|
||||
dupepart:Remove()
|
||||
end
|
||||
|
||||
local dupeEnt
|
||||
|
||||
-- safe guard
|
||||
if data.is_dupe then
|
||||
local id = tonumber(part_data.self.OwnerName)
|
||||
dupeEnt = Entity(id or -1)
|
||||
if not dupeEnt:IsValid() then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local function load_outfit()
|
||||
if dupeEnt and not dupeEnt:IsValid() then return end
|
||||
|
||||
dupepart = pac.GetPartFromUniqueID(data.player_uid, part_data.self.UniqueID)
|
||||
|
||||
if dupepart:IsValid() then
|
||||
pac.dprint("removing part %q to be replaced with the part previously received ON callback call", dupepart.Name)
|
||||
dupepart:Remove()
|
||||
end
|
||||
|
||||
owner.pac_render_time_exceeded = false
|
||||
|
||||
-- specify "level" as 1 so we can delay CalcShowHide recursive call until we are ready
|
||||
local part = pac.CreatePart(part_data.self.ClassName, owner, part_data, false, 1)
|
||||
|
||||
if data.is_dupe then
|
||||
part.dupe_remove = true
|
||||
end
|
||||
|
||||
if owner == pac.LocalPlayer then
|
||||
pace.CallHook("OnWoreOutfit", part)
|
||||
end
|
||||
|
||||
part:CallRecursive('OnWorn')
|
||||
part:CallRecursive('PostApplyFixes')
|
||||
|
||||
if part.UpdateOwnerName then
|
||||
part:UpdateOwnerName(true)
|
||||
part:CallRecursive("CalcShowHide", true)
|
||||
end
|
||||
|
||||
owner.pac_fix_show_from_render = SysTime() + 1
|
||||
end
|
||||
|
||||
if doItNow then
|
||||
load_outfit()
|
||||
end
|
||||
|
||||
return load_outfit
|
||||
end
|
||||
|
||||
function pace.RemovePartFromServer(owner, part_name, data)
|
||||
pac.dprint("%s removed %q", tostring(owner), part_name)
|
||||
|
||||
if part_name == "__ALL__" then
|
||||
pac.RemovePartsFromUniqueID(data.player_uid)
|
||||
|
||||
pace.CallHook("RemoveOutfit", owner)
|
||||
pac.CleanupEntityIgnoreBound(owner)
|
||||
else
|
||||
local part = pac.GetPartFromUniqueID(data.player_uid, part_name)
|
||||
|
||||
if part:IsValid() then
|
||||
part:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.HandleReceiveData(data, doitnow)
|
||||
local T = type(data.part)
|
||||
|
||||
if T == "table" then
|
||||
return pace.WearPartFromServer(data.owner, data.part, data, doitnow)
|
||||
elseif T == "string" then
|
||||
return pace.RemovePartFromServer(data.owner, data.part, data)
|
||||
else
|
||||
ErrorNoHalt("PAC: Unhandled "..T..'!?\n')
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("pac_submit", function()
|
||||
if not pac.IsEnabled() then return end
|
||||
|
||||
net.ReadStream(ply, function(data)
|
||||
if not data then
|
||||
pac.Message("message from server timed out")
|
||||
return
|
||||
end
|
||||
|
||||
local buffer = pac.StringStream(data)
|
||||
local data = buffer:readTable()
|
||||
|
||||
if type(data.owner) ~= "Player" or not data.owner:IsValid() then
|
||||
pac.Message("received message from server but owner is not valid!? typeof " .. type(data.owner) .. ' || ', data.owner)
|
||||
return
|
||||
end
|
||||
|
||||
if pac.IsPacOnUseOnly() and data.owner ~= pac.LocalPlayer then
|
||||
pace.HandleOnUseReceivedData(data)
|
||||
else
|
||||
pace.HandleReceiveData(data, true)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
function pace.Notify(allowed, reason, name)
|
||||
name = name or "???"
|
||||
|
||||
if allowed == true then
|
||||
pac.Message(string.format('Your part %q has been applied', name))
|
||||
else
|
||||
chat.AddText(Color(255, 255, 0), "[PAC3] ", Color(255, 0, 0), string.format('The server rejected applying your part (%q) - %s', name, reason))
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("pac_submit_acknowledged", function(umr)
|
||||
pace.Notify(net.ReadBool(), net.ReadString(), net.ReadString())
|
||||
end)
|
||||
|
||||
do
|
||||
local function LoadUpDefault()
|
||||
if next(pac.GetLocalParts()) then
|
||||
pac.Message("not wearing autoload outfit, already wearing something")
|
||||
elseif pace.IsActive() then
|
||||
pac.Message("not wearing autoload outfit, editor is open")
|
||||
else
|
||||
local autoload_file = "autoload"
|
||||
local autoload_result = hook.Run("PAC3Autoload", autoload_file)
|
||||
|
||||
if autoload_result ~= false then
|
||||
if isstring(autoload_result) then
|
||||
autoload_file = autoload_result
|
||||
end
|
||||
|
||||
pac.Message("Wearing " .. autoload_file .. "...")
|
||||
pace.LoadParts(autoload_file)
|
||||
pace.WearParts()
|
||||
end
|
||||
end
|
||||
|
||||
pac.RemoveHook("Think", "pac_request_outfits")
|
||||
pac.Message("Requesting outfits in 8 seconds...")
|
||||
|
||||
timer.Simple(8, function()
|
||||
pac.Message("Requesting outfits...")
|
||||
RunConsoleCommand("pac_request_outfits")
|
||||
end)
|
||||
end
|
||||
|
||||
local function Initialize()
|
||||
pac.RemoveHook("KeyRelease", "pac_request_outfits")
|
||||
|
||||
if not pac.LocalPlayer:IsValid() then
|
||||
return
|
||||
end
|
||||
|
||||
if not pac.IsEnabled() then
|
||||
pac.RemoveHook("Think", "pac_request_outfits")
|
||||
pace.NeverLoaded = true
|
||||
return
|
||||
end
|
||||
|
||||
LoadUpDefault()
|
||||
end
|
||||
|
||||
hook.Add("pac_Enable", "pac_LoadUpDefault", function()
|
||||
if not pace.NeverLoaded then return end
|
||||
pace.NeverLoaded = nil
|
||||
LoadUpDefault()
|
||||
end)
|
||||
|
||||
local frames = 0
|
||||
|
||||
pac.AddHook("Think", "pac_request_outfits", function()
|
||||
if RealFrameTime() > 0.2 then -- lag?
|
||||
return
|
||||
end
|
||||
|
||||
frames = frames + 1
|
||||
|
||||
if frames > 400 then
|
||||
if not xpcall(Initialize, ErrorNoHalt) then
|
||||
pac.RemoveHook("Think", "pac_request_outfits")
|
||||
pace.NeverLoaded = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
pac.AddHook("KeyRelease", "pac_request_outfits", function()
|
||||
local me = pac.LocalPlayer
|
||||
|
||||
if me:IsValid() and me:GetVelocity():Length() > 50 then
|
||||
frames = frames + 200
|
||||
|
||||
if frames > 400 then
|
||||
Initialize()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
402
lua/pac3/editor/client/wear_filter.lua
Normal file
402
lua/pac3/editor/client/wear_filter.lua
Normal file
@@ -0,0 +1,402 @@
|
||||
--[[
|
||||
| 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 list_form = include("panels/list.lua")
|
||||
local L = pace.LanguageString
|
||||
|
||||
local cache = {}
|
||||
|
||||
local function store_config(id, tbl)
|
||||
file.CreateDir("pac/config")
|
||||
file.Write("pac/config/"..id..".json", util.TableToJSON(tbl))
|
||||
cache[id] = tbl
|
||||
end
|
||||
|
||||
local function read_config(id)
|
||||
local tbl = util.JSONToTable(file.Read("pac/config/"..id..".json", "DATA") or "{}") or {}
|
||||
cache[id] = tbl
|
||||
return tbl
|
||||
end
|
||||
|
||||
local function get_config_value(id, key)
|
||||
return cache[id] and cache[id][key]
|
||||
end
|
||||
|
||||
local function jsonid(ply)
|
||||
return "_" .. pac.Hash(ply)
|
||||
end
|
||||
|
||||
local function update_ignore()
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
pac.ToggleIgnoreEntity(ply, pace.ShouldIgnorePlayer(ply), "wear_filter")
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("PlayerSpawn", "pace_outfit_ignore_update", function()
|
||||
update_ignore()
|
||||
end)
|
||||
|
||||
net.Receive("pac.TogglePartDrawing", function()
|
||||
local ent = net.ReadEntity()
|
||||
|
||||
if ent:IsValid() then
|
||||
local b = (net.ReadBit() == 1)
|
||||
pac.TogglePartDrawing(ent, b)
|
||||
end
|
||||
end)
|
||||
|
||||
-- ignore
|
||||
do
|
||||
function pac.ToggleIgnoreEntity(ent, status, strID)
|
||||
if status then
|
||||
return pac.IgnoreEntity(ent, strID)
|
||||
else
|
||||
return pac.UnIgnoreEntity(ent, strID)
|
||||
end
|
||||
end
|
||||
|
||||
function pac.IsEntityIgnored(ent)
|
||||
if pace.ShouldIgnorePlayer(ent) then
|
||||
return true
|
||||
end
|
||||
return ent.pac_ignored or false
|
||||
end
|
||||
|
||||
function pac.IsEntityIgnoredBy(ent, strID)
|
||||
return ent.pac_ignored_data and ent.pac_ignored_data[strID] or false
|
||||
end
|
||||
|
||||
function pac.IsEntityIgnoredOnlyBy(ent, strID)
|
||||
return ent.pac_ignored_data and ent.pac_ignored_data[strID] and table.Count(ent.pac_ignored_data) == 1 or false
|
||||
end
|
||||
|
||||
function pac.EntityIgnoreBound(ent, callback, index)
|
||||
assert(isfunction(callback), "isfunction(callback)")
|
||||
|
||||
if not pac.IsEntityIgnored(ent) then return callback(ent) end
|
||||
|
||||
ent.pac_ignored_callbacks = ent.pac_ignored_callbacks or {}
|
||||
|
||||
if index then
|
||||
for i, data in ipairs(ent.pac_ignored_callbacks) do
|
||||
if data.index == index then
|
||||
table.remove(ent.pac_ignored_callbacks, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(ent.pac_ignored_callbacks, {callback = callback, index = index})
|
||||
end
|
||||
|
||||
function pac.CleanupEntityIgnoreBound(ent)
|
||||
ent.pac_ignored_callbacks = nil
|
||||
end
|
||||
|
||||
function pac.IgnoreEntity(ent, strID)
|
||||
if ent == pac.LocalPlayer then return false end
|
||||
strID = strID or "generic"
|
||||
if ent.pac_ignored_data and ent.pac_ignored_data[strID] then return end
|
||||
ent.pac_ignored = ent.pac_ignored or false
|
||||
ent.pac_ignored_data = ent.pac_ignored_data or {}
|
||||
ent.pac_ignored_data[strID] = true
|
||||
local newStatus = true
|
||||
|
||||
if newStatus ~= ent.pac_ignored then
|
||||
ent.pac_ignored = newStatus
|
||||
pac.TogglePartDrawing(ent, not newStatus)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function pac.UnIgnoreEntity(ent, strID)
|
||||
if ent == pac.LocalPlayer then return false end
|
||||
strID = strID or "generic"
|
||||
if ent.pac_ignored_data and ent.pac_ignored_data[strID] == nil then return end
|
||||
ent.pac_ignored = ent.pac_ignored or false
|
||||
ent.pac_ignored_data = ent.pac_ignored_data or {}
|
||||
ent.pac_ignored_data[strID] = nil
|
||||
local newStatus = false
|
||||
|
||||
for _, v in pairs(ent.pac_ignored_data) do
|
||||
if v then
|
||||
newStatus = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if newStatus ~= ent.pac_ignored then
|
||||
ent.pac_ignored = newStatus
|
||||
|
||||
if not newStatus and ent.pac_ignored_callbacks then
|
||||
for i, data in ipairs(ent.pac_ignored_callbacks) do
|
||||
ProtectedCall(function()
|
||||
data.callback(ent)
|
||||
end)
|
||||
end
|
||||
|
||||
ent.pac_ignored_callbacks = nil
|
||||
end
|
||||
|
||||
pac.TogglePartDrawing(ent, not newStatus)
|
||||
end
|
||||
|
||||
return newStatus
|
||||
end
|
||||
end
|
||||
|
||||
CreateClientConVar("pace_wear_filter_mode", "disabled")
|
||||
CreateClientConVar("pace_outfit_filter_mode", "disabled")
|
||||
|
||||
function pace.ShouldIgnorePlayer(ply)
|
||||
local mode = GetConVar("pace_outfit_filter_mode"):GetString()
|
||||
|
||||
if mode == "steam_friends" then
|
||||
return ply:GetFriendStatus() ~= "friend"
|
||||
elseif mode == "whitelist" then
|
||||
return get_config_value("outfit_whitelist", jsonid(ply)) == nil
|
||||
elseif mode == "blacklist" then
|
||||
return get_config_value("outfit_blacklist", jsonid(ply)) ~= nil
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function pace.CreateWearFilter()
|
||||
local mode = GetConVar("pace_wear_filter_mode"):GetString()
|
||||
|
||||
local tbl = {}
|
||||
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
if ply == pac.LocalPlayer then continue end
|
||||
|
||||
if mode == "steam_friends" then
|
||||
if ply:GetFriendStatus() == "friend" then
|
||||
table.insert(tbl, pac.Hash(ply))
|
||||
end
|
||||
elseif mode == "whitelist" then
|
||||
if get_config_value("wear_whitelist", jsonid(ply)) ~= nil then
|
||||
table.insert(tbl, pac.Hash(ply))
|
||||
end
|
||||
elseif mode == "blacklist" then
|
||||
if get_config_value("wear_blacklist", jsonid(ply)) == nil then
|
||||
table.insert(tbl, pac.Hash(ply))
|
||||
end
|
||||
else
|
||||
table.insert(tbl, pac.Hash(ply))
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(tbl, pac.Hash(pac.LocalPlayer))
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
local function generic_form(help)
|
||||
local pnl = vgui.Create("DListLayout")
|
||||
|
||||
local label = pnl:Add("DLabel")
|
||||
label:DockMargin(0,5,0,5)
|
||||
label:SetWrap(true)
|
||||
label:SetDark(true)
|
||||
label:SetAutoStretchVertical(true)
|
||||
label:SetText(help)
|
||||
|
||||
return pnl
|
||||
end
|
||||
|
||||
local function player_list_form(name, id, help)
|
||||
local pnl = vgui.Create("DListLayout")
|
||||
|
||||
local label = pnl:Add("DLabel")
|
||||
label:DockMargin(0,5,0,5)
|
||||
label:SetWrap(true)
|
||||
label:SetDark(true)
|
||||
label:SetAutoStretchVertical(true)
|
||||
label:SetText(help)
|
||||
|
||||
list_form(pnl, name, {
|
||||
empty_message = L"No players online.",
|
||||
|
||||
name_left = "players",
|
||||
populate_left = function()
|
||||
local blacklist = read_config(id)
|
||||
|
||||
local tbl = {}
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
if ply == pac.LocalPlayer then continue end
|
||||
if not blacklist[jsonid(ply)] then
|
||||
table.insert(tbl, {
|
||||
name = ply:Nick(),
|
||||
value = ply,
|
||||
})
|
||||
end
|
||||
end
|
||||
return tbl
|
||||
end,
|
||||
store_left = function(kv)
|
||||
local tbl = read_config(id)
|
||||
tbl[jsonid(kv.value)] = kv.name
|
||||
store_config(id, tbl)
|
||||
|
||||
if id:StartWith("outfit") then
|
||||
update_ignore()
|
||||
end
|
||||
end,
|
||||
|
||||
name_right = name,
|
||||
populate_right = function()
|
||||
local tbl = {}
|
||||
for id, nick in pairs(read_config(id)) do
|
||||
local ply = pac.ReverseHash(id:sub(2), "Player")
|
||||
if ply == pac.LocalPlayer then continue end
|
||||
|
||||
if IsValid(ply) then
|
||||
table.insert(tbl, {
|
||||
name = ply:Nick(),
|
||||
value = id,
|
||||
})
|
||||
else
|
||||
table.insert(tbl, {
|
||||
name = nick .. " (offline)",
|
||||
value = id,
|
||||
})
|
||||
end
|
||||
end
|
||||
return tbl
|
||||
end,
|
||||
store_right = function(kv)
|
||||
local tbl = read_config(id)
|
||||
tbl[kv.value] = nil
|
||||
store_config(id, tbl)
|
||||
|
||||
if id:StartWith("outfit") then
|
||||
update_ignore()
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
return pnl
|
||||
end
|
||||
|
||||
do
|
||||
net.Receive("pac_update_playerfilter", function()
|
||||
local ids = pace.CreateWearFilter()
|
||||
net.Start("pac_update_playerfilter")
|
||||
net.WriteUInt(#ids, 8)
|
||||
|
||||
for _, val in ipairs(ids) do
|
||||
net.WriteString(val)
|
||||
end
|
||||
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
function pace.PopulateWearMenu(menu)
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
if ply == pac.LocalPlayer then continue end
|
||||
|
||||
local icon = menu:AddOption(L"wear only for " .. ply:Nick(), function()
|
||||
pace.WearParts(ply)
|
||||
end)
|
||||
icon:SetImage(pace.MiscIcons.wear)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pace.FillWearSettings(pnl)
|
||||
local list = vgui.Create("DCategoryList", pnl)
|
||||
list:Dock(FILL)
|
||||
|
||||
do
|
||||
local cat = list:Add(L"wear filter")
|
||||
cat.Header:SetSize(40,40)
|
||||
cat.Header:SetFont("DermaLarge")
|
||||
local list = vgui.Create("DListLayout")
|
||||
list:DockPadding(20,20,20,20)
|
||||
cat:SetContents(list)
|
||||
|
||||
local mode = vgui.Create("DComboBox", list)
|
||||
mode:SetSortItems(false)
|
||||
mode:AddChoice("disabled")
|
||||
mode:AddChoice("steam friends")
|
||||
mode:AddChoice("whitelist")
|
||||
mode:AddChoice("blacklist")
|
||||
|
||||
mode.OnSelect = function(_, _, value)
|
||||
if IsValid(mode.form) then
|
||||
mode.form:Remove()
|
||||
end
|
||||
|
||||
if value == "steam friends" then
|
||||
mode.form = generic_form(L"Only your steam friends can see your worn outfit.")
|
||||
elseif value == "whitelist" then
|
||||
mode.form = player_list_form(L"whitelist", "wear_whitelist", L"Only the players in the whitelist can see your worn outfit.")
|
||||
elseif value == "blacklist" then
|
||||
mode.form = player_list_form( L"blacklist", "wear_blacklist", L"The players in the blacklist cannot see your worn outfit.")
|
||||
elseif value == "disabled" then
|
||||
mode.form = generic_form(L"Everyone can see your worn outfit.")
|
||||
end
|
||||
|
||||
GetConVar("pace_wear_filter_mode"):SetString(value:gsub(" ", "_"))
|
||||
|
||||
mode.form:SetParent(list)
|
||||
end
|
||||
|
||||
local mode_str = GetConVar("pace_wear_filter_mode"):GetString():gsub("_", " ")
|
||||
mode:ChooseOption(mode_str)
|
||||
mode:OnSelect(nil, mode_str)
|
||||
end
|
||||
|
||||
do
|
||||
local cat = list:Add(L"outfit filter")
|
||||
cat.Header:SetSize(40,40)
|
||||
cat.Header:SetFont("DermaLarge")
|
||||
local list = vgui.Create("DListLayout")
|
||||
list:DockPadding(20,20,20,20)
|
||||
cat:SetContents(list)
|
||||
|
||||
local mode = vgui.Create("DComboBox", list)
|
||||
mode:SetSortItems(false)
|
||||
mode:AddChoice("disabled")
|
||||
mode:AddChoice("steam friends")
|
||||
mode:AddChoice("whitelist")
|
||||
mode:AddChoice("blacklist")
|
||||
|
||||
mode.OnSelect = function(_, _, value)
|
||||
if IsValid(mode.form) then
|
||||
mode.form:Remove()
|
||||
end
|
||||
|
||||
if value == "steam friends" then
|
||||
mode.form = generic_form(L"You will only see outfits from your steam friends.")
|
||||
elseif value == "whitelist" then
|
||||
mode.form = player_list_form(L"whitelist", "outfit_whitelist", L"You will only see outfits from the players in the whitelist.")
|
||||
elseif value == "blacklist" then
|
||||
mode.form = player_list_form(L"blacklist", "outfit_blacklist", L"You will see outfits from everyone except the players in the blacklist.")
|
||||
elseif value == "disabled" then
|
||||
mode.form = generic_form(L"You will see everyone's outfits.")
|
||||
end
|
||||
|
||||
GetConVar("pace_outfit_filter_mode"):SetString(value:gsub(" ", "_"))
|
||||
|
||||
mode.form:SetParent(list)
|
||||
|
||||
update_ignore()
|
||||
end
|
||||
|
||||
local mode_str = GetConVar("pace_outfit_filter_mode"):GetString():gsub("_", " ")
|
||||
mode:ChooseOption(mode_str)
|
||||
mode:OnSelect(nil, mode_str)
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
36
lua/pac3/editor/client/wiki.lua
Normal file
36
lua/pac3/editor/client/wiki.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pace.WikiURL = "https://github.com/capsadmin/pac3/wiki/"
|
||||
pace.wiki_cache = {}
|
||||
|
||||
function pace.GetPropertyDescription(part, field, callback)
|
||||
--[==[
|
||||
local function go(s)
|
||||
callback(s:match("<td>%s-"..field:gsub("%u", " %1"):lower().."%s-</td>.-</td><td> (.-)\n</td>") or "")
|
||||
end
|
||||
|
||||
if pace.wiki_cache[part] then
|
||||
go(pace.wiki_cache[part])
|
||||
end
|
||||
pac.HTTPGet(pace.WikiURL .. "/index.php/Part_"..part, function(s)
|
||||
pace.wiki_cache[part] = pace.wiki_cache[part] or s
|
||||
go(s)
|
||||
end, function(err) Derma_Message("HTTP Request Failed for " .. pace.WikiURL .. "/index.php/Part_"..part, err, "OK") end)
|
||||
]==]
|
||||
end
|
||||
|
||||
|
||||
function pace.ShowHelp(part)
|
||||
pace.ShowWiki(pace.WikiURL)
|
||||
--[[if part then
|
||||
pace.ShowWiki(pace.WikiURL .. "/index.php?title=Part_" .. part .. "&action=view")
|
||||
end]]
|
||||
end
|
||||
222
lua/pac3/editor/client/wires.lua
Normal file
222
lua/pac3/editor/client/wires.lua
Normal file
@@ -0,0 +1,222 @@
|
||||
--[[
|
||||
| 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 function CubicHermite(p0, p1, m0, m1, t)
|
||||
local tS = t*t;
|
||||
local tC = tS*t;
|
||||
|
||||
return (2*tC - 3*tS + 1)*p0 + (tC - 2*tS + t)*m0 + (-2*tC + 3*tS)*p1 + (tC - tS)*m1
|
||||
end
|
||||
|
||||
local vsamples = {}
|
||||
for i=1, 100 do vsamples[#vsamples+1] = {Vector(0,0,0), Color(0,0,0,0)} end
|
||||
|
||||
local math_sqrt = math.sqrt
|
||||
local math_abs = math.abs
|
||||
local math_max = math.max
|
||||
local math_min = math.min
|
||||
local math_cos = math.cos
|
||||
local math_pi = math.pi
|
||||
|
||||
local black = Color(0,0,0,100)
|
||||
local vector_add = Vector(0, 2.5, 0)
|
||||
|
||||
local mtVector = FindMetaTable("Vector")
|
||||
local mtColor = getmetatable(black)
|
||||
local render_startBeam = render.StartBeam
|
||||
local render_endBeam = render.EndBeam
|
||||
local render_addBeam = render.AddBeam
|
||||
local setVecUnpacked = mtVector.SetUnpacked
|
||||
local setColUnpacked = mtColor.SetUnpacked
|
||||
local colUnpack = mtColor.Unpack
|
||||
|
||||
local function DrawHermite(width, x0,y0,x1,y1,c0,c1,alpha,samples)
|
||||
|
||||
--[[if x0 < 0 and x1 < 0 then return end
|
||||
if x0 > w and x1 > w then return end
|
||||
if y0 < 0 and y1 < 0 then return end
|
||||
if y0 > h and y1 > h then return end]]
|
||||
|
||||
local r0,g0,b0,a0 = colUnpack(c0)
|
||||
local r1,g1,b1,a1 = colUnpack(c1)
|
||||
|
||||
alpha = alpha or 1
|
||||
|
||||
width = width or 5
|
||||
local samples = 20
|
||||
local positions = vsamples
|
||||
|
||||
samples = samples or 20
|
||||
|
||||
local dx = -(x1 - x0)
|
||||
local dy = (y1 - y0)
|
||||
|
||||
local d = math_sqrt(math_max(dx*dx, 8000), dy*dy) * 1.5
|
||||
d = math_max(d, math_abs(dy))
|
||||
d = math_min(d, 1000)
|
||||
d = d * 1.25 / (-dy/300)
|
||||
|
||||
setVecUnpacked(positions[1][1],x0,y0,0)
|
||||
positions[1][2] = c0
|
||||
|
||||
for i=1, samples do
|
||||
|
||||
local t = i/samples
|
||||
|
||||
t = 1 - (.5 + math_cos(t * math_pi) * .5)
|
||||
|
||||
local x = CubicHermite(x0, x1, dx >= 0 and d or -d, d, t)
|
||||
local y = CubicHermite(y0, y1, 0, 0, t)
|
||||
|
||||
setVecUnpacked(positions[i+1][1],x,y,0)
|
||||
setColUnpacked(positions[i+1][2],Lerp(t, r0, r1), Lerp(t, g0, g1), Lerp(t, b0, b1), a0 * alpha)
|
||||
end
|
||||
|
||||
render.PushFilterMag( TEXFILTER.LINEAR )
|
||||
render.PushFilterMin( TEXFILTER.LINEAR )
|
||||
|
||||
render_startBeam(samples + 1)
|
||||
|
||||
for i = 1, samples + 1 do
|
||||
render_addBeam(positions[i][1] + vector_add, width, 0.5, black)
|
||||
end
|
||||
|
||||
render_endBeam()
|
||||
|
||||
--render.SetMaterial(Material("cable/smoke.vmt"))
|
||||
render_startBeam(samples+1)
|
||||
|
||||
for i=1, samples+1 do
|
||||
local curr = positions[i][1]
|
||||
render_addBeam(curr, width, ((i/samples)*10000 - 0.5)%1, positions[i][2])
|
||||
end
|
||||
|
||||
render_endBeam()
|
||||
|
||||
render.PopFilterMag()
|
||||
render.PopFilterMin()
|
||||
end
|
||||
|
||||
local function draw_hermite(x,y, w,h, ...)
|
||||
local cam3d = {
|
||||
type = "3D",
|
||||
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = w,
|
||||
h = h,
|
||||
|
||||
znear = -10000,
|
||||
zfar = 10000,
|
||||
|
||||
origin = Vector(x,y,-1000),
|
||||
angles = Angle(-90,0,90),
|
||||
|
||||
ortho = {
|
||||
left = 0,
|
||||
right = w,
|
||||
|
||||
top = -h,
|
||||
bottom = 0
|
||||
}
|
||||
}
|
||||
|
||||
cam.Start(cam3d)
|
||||
DrawHermite(...)
|
||||
cam.End(cam3d)
|
||||
end
|
||||
--[[
|
||||
function PANEL:DrawHermite(...)
|
||||
local x, y = self:ScreenToLocal(0,0)
|
||||
local w, h = self:GetSize()
|
||||
draw_hermite(x,y, w,h)
|
||||
end
|
||||
]]
|
||||
|
||||
local last_part
|
||||
|
||||
hook.Add("PostRenderVGUI", "beams", function()
|
||||
if not pace.IsActive() then return end
|
||||
if not pace.IsFocused() then return end
|
||||
local part = pace.current_part
|
||||
if not part:IsValid() then return end
|
||||
local node = part.pace_tree_node
|
||||
|
||||
if part ~= last_part then
|
||||
part.cached_props = nil
|
||||
end
|
||||
part.cached_props = part.cached_props or part:GetProperties()
|
||||
local props = part.cached_props
|
||||
last_part = part
|
||||
|
||||
for _, info in ipairs(props) do
|
||||
if info.udata.part_key then
|
||||
--if info.udata.part_key == "Parent" then continue end
|
||||
|
||||
local from = part
|
||||
local to = part["Get" .. info.udata.part_key](part)
|
||||
|
||||
if not to:IsValid() then continue end
|
||||
|
||||
|
||||
local from_pnl = from.pace_properties and from.pace_properties[info.key] or NULL
|
||||
local to_pnl = to.pace_tree_node or NULL
|
||||
|
||||
if not from_pnl:IsValid() then continue end
|
||||
if not to_pnl:IsValid() then continue end
|
||||
|
||||
local params = {}
|
||||
|
||||
params["$basetexture"] = to.Icon or "gui/colors.png"
|
||||
params["$vertexcolor"] = 1
|
||||
params["$vertexalpha"] = 1
|
||||
params["$nocull"] = 1
|
||||
|
||||
local path = to_pnl:GetModel()
|
||||
if path then
|
||||
path = "spawnicons/" .. path:sub(1, -5) .. "_32"
|
||||
params["$basetexture"] = path
|
||||
end
|
||||
|
||||
|
||||
local mat = CreateMaterial("pac_wire_icon_" .. params["$basetexture"], "UnlitGeneric", params)
|
||||
|
||||
render.SetMaterial(mat)
|
||||
|
||||
local fx,fy = from_pnl:LocalToScreen(from_pnl:GetWide(), from_pnl:GetTall() / 2)
|
||||
|
||||
local tx,ty = to_pnl.Icon:LocalToScreen(0,to_pnl.Icon:GetTall() / 2)
|
||||
|
||||
do
|
||||
local x,y = pace.tree:LocalToScreen(0,0)
|
||||
local w,h = pace.tree:LocalToScreen(pace.tree:GetSize())
|
||||
|
||||
tx = math.Clamp(tx, x, w)
|
||||
ty = math.Clamp(ty, y, h)
|
||||
end
|
||||
|
||||
from_pnl.wire_smooth_hover = from_pnl.wire_smooth_hover or 0
|
||||
|
||||
if from_pnl:IsHovered() or (from.pace_tree_node and from.pace_tree_node:IsValid() and from.pace_tree_node.Label:IsHovered()) then
|
||||
from_pnl.wire_smooth_hover = from_pnl.wire_smooth_hover + (5 - from_pnl.wire_smooth_hover) * FrameTime() * 20
|
||||
else
|
||||
from_pnl.wire_smooth_hover = from_pnl.wire_smooth_hover + (0 - from_pnl.wire_smooth_hover) * FrameTime() * 20
|
||||
end
|
||||
|
||||
from_pnl.wire_smooth_hover = math.Clamp(from_pnl.wire_smooth_hover, 0, 5)
|
||||
|
||||
if from_pnl.wire_smooth_hover > 0.01 then
|
||||
draw_hermite(0,0,ScrW(),ScrH(), from_pnl.wire_smooth_hover, fx,fy, tx,ty, Color(255,255,255), Color(255,255,255, 255), 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
Reference in New Issue
Block a user