This commit is contained in:
lifestorm
2024-08-04 22:55:00 +03:00
parent 0e770b2b49
commit 94063e4369
7342 changed files with 1718932 additions and 14 deletions

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View 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()

View 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',
}

View 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()

View 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

View 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

View 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

View 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

View 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

View 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)

View 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)

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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)

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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)

View 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

View 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)

View 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

View 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

View 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)

View 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

View 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

View 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

View 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)

View 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"] = "预设",
}

View 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"
}

View 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"] = "ライト",
}

View 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"] = "시작 튜토리얼(영어)",
}

View 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"] = "переключить т-позу",
}

View 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ı",
["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ı",
["load"] = "",
["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"] = "ı çı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ş",
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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)

View File

@@ -0,0 +1,123 @@
--[[
| 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 get_bans()
local str = file.Read("pac_bans.txt", "DATA")
local bans = {}
if str and str ~= "" then
bans = util.KeyValuesToTable(str)
end
do -- check if this needs to be rebuilt
local k,v = next(bans)
if isstring(v) then
local temp = {}
for k,v in pairs(bans) do
temp[util.CRC("gm_" .. v .. "_gm")] = {steamid = v, name = k}
end
bans = temp
end
end
return bans
end
function pace.Ban(ply)
ply:ConCommand("pac_clear_parts")
timer.Simple( 1, function() -- made it a timer because the ConCommand don't run fast enough. - Bizzclaw
net.Start("pac_submit_acknowledged")
net.WriteBool(false)
net.WriteString("You have been banned from using pac!")
net.Send(ply)
local bans = get_bans()
for key, data in pairs(bans) do
if ply:SteamID() == data.steamid then
bans[key] = nil
end
end
bans[ply:UniqueID()] = {steamid = ply:SteamID(), nick = ply:Nick()}
pace.Bans = bans
file.Write("pac_bans.txt", util.TableToKeyValues(bans), "DATA")
end)
end
function pace.Unban(ply)
net.Start("pac_submit_acknowledged")
net.WriteBool(true)
net.WriteString("You are now permitted to use pac!")
net.Send(ply)
local bans = get_bans()
for key, data in pairs(bans) do
if ply:SteamID() == data.steamid then
bans[key] = nil
end
end
pace.Bans = bans
file.Write("pac_bans.txt", util.TableToKeyValues(bans), "DATA")
end
local function GetPlayer(target)
for key, ply in pairs(player.GetAll()) do
if ply:SteamID() == target or ply:UniqueID() == target or ply:Nick():lower():find(target:lower()) then
return ply
end
end
end
concommand.Add("pac_ban", function(ply, cmd, args)
if not next(args) then
pac.Message("Please provide one of the following: SteamID, UniqueID or Nick.")
return
end
local target = GetPlayer(args[1])
if (not IsValid(ply) or ply:IsAdmin()) and target then
pace.Ban(target)
pac.Message(ply, " banned ", target, " from PAC.")
end
end)
concommand.Add("pac_unban", function(ply, cmd, args)
if not next(args) then
pac.Message("Please provide one of the following: SteamID, UniqueID or Nick.")
return
end
local target = GetPlayer(args[1])
if (not IsValid(ply) or ply:IsAdmin()) and target then
pace.Unban(target)
pac.Message(ply, " unbanned ", target, " from PAC.")
end
end)
function pace.IsBanned(ply)
if not ply or not ply:IsValid() then return false end
if not pace.Bans then
pace.Bans = get_bans()
end
return pace.Bans[ply:UniqueID()] ~= nil
end

View File

@@ -0,0 +1,105 @@
--[[
| 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 = pace or {}
-- for the default models
resource.AddWorkshop("104691717")
pace.luadata = include("pac3/libraries/luadata.lua")
pace.Parts = pace.Parts or {}
pace.Errors = {}
do
util.AddNetworkString("pac.TogglePartDrawing")
function pac.TogglePartDrawing(ent, b, who) --serverside interface to clientside function of the same name
net.Start("pac.TogglePartDrawing")
net.WriteEntity(ent)
net.WriteBit(b)
if not who then
net.Broadcast()
else
net.Send(who)
end
end
end
function pace.CanPlayerModify(ply, ent)
if not IsValid(ply) or not IsValid(ent) then
return false
end
if ply == ent then
return true
end
if game.SinglePlayer() then
return true
end
if ent.CPPICanTool and ent:CPPICanTool(ply, "paint") then
return true
end
if ent.CPPIGetOwner and ent:CPPIGetOwner() == ply then
return true
end
do
local tr = util.TraceLine({ start = ply:EyePos(), endpos = ent:WorldSpaceCenter(), filter = ply })
if tr.Entity == ent and hook.Run("CanTool", ply, tr, "paint") == true then
return true
end
end
return false
end
include("util.lua")
include("wear.lua")
include("wear_filter.lua")
include("bans.lua")
include("spawnmenu.lua")
include("show_outfit_on_use.lua")
do
util.AddNetworkString("pac_in_editor")
net.Receive("pac_in_editor", function(_, ply)
ply:SetNW2Bool("pac_in_editor", net.ReadBit() == 1)
end)
util.AddNetworkString("pac_in_editor_posang")
net.Receive("pac_in_editor_posang", function(_, ply)
if not ply.pac_last_editor_message then
ply.pac_last_editor_message = 0
end
if ply.pac_last_editor_message > CurTime() then return end
ply.pac_last_editor_message = CurTime() + 0.2
local pos = net.ReadVector()
local ang = net.ReadAngle()
local part_pos = net.ReadVector()
net.Start("pac_in_editor_posang", true)
net.WriteEntity(ply)
net.WriteVector(pos)
net.WriteAngle(ang)
net.WriteVector(part_pos)
net.SendPVS(ply:GetPos())
end)
end
CreateConVar("has_pac3_editor", "1", {FCVAR_NOTIFY})
resource.AddSingleFile("materials/icon64/pac3.png")

View File

@@ -0,0 +1,676 @@
--[[
| 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 {
[1076811763] = "is_dupe",
[1191326365] = "ClassName",
[1225748678] = "part",
[2068527459] = "self",
[2711073210] = "children",
[3479234172] = "owner",
[783959845] = "player_uid",
[100787502] = "fleshsubsurfacetexture",
[1020291948] = "Radius",
[102079488] = "LightWarpTexture",
[1021315175] = "StartAlpha",
[1022140656] = "flesheffectcenterradius1",
[1025052663] = "suppress_decals",
[1031186024] = "MaxPitch",
[1045412760] = "EmissiveBlendTint",
[1063510319] = "blendtintbybasealpha",
[1079863564] = "DrawWeapon",
[1081499344] = "CollideWithOwner",
[1083824756] = "FarZ",
[1090045300] = "emissiveblendstrength",
[1096474325] = "sheenmap",
[1099352106] = "BaseTextureScale",
[1100049831] = "MinimumRadius",
[1104142786] = "FollowPartUID",
[1110368737] = "DrawOrder",
[1113161297] = "PoseParameter",
[1116685109] = "FollowPart",
[114277299] = "PointC",
[114400132] = "Length",
[1154474433] = "ConstrainX",
[1162527309] = "ambientonly",
[1165467388] = "BloodColor",
[1177992464] = "detailscale",
[1196430393] = "Collide",
[1199526629] = "outline",
[1202631935] = "Velocity",
[1207855863] = "AmbientOcclusionTexture",
[1208336382] = "PlayerAngles",
[1212418031] = "bumptransformAngle",
[1215714457] = "FinLiftMode",
[1221564783] = "phongwarptexture",
[1222901307] = "InnerAngle",
[1224948332] = "FinCline",
[1228198681] = "compress",
[1237391457] = "flashlighttextureframe",
[1238953138] = "HidePhysgunBeam",
[1249613445] = "basetexturetransformPosition",
[1252061369] = "HorizontalFOV",
[125937960] = "Jump",
[1261109621] = "BumpMap",
[1261992767] = "ConstantVelocity",
[127573335] = "EmissiveBlendTexture",
[1280306369] = "envmapmode",
[1283871871] = "flesheffectcenterradius4",
[1285932729] = "distancealphafromdetail",
[1288785580] = "FleshInteriorTexture",
[1294740419] = "iris",
[1295479485] = "OverridePosition",
[1299096975] = "NoWorld",
[1299602945] = "cloakpassenabled",
[1306211566] = "Texture",
[1306225195] = "Width",
[1316359440] = "UseLua",
[133378496] = "outlinecolor",
[1350790561] = "ParticleAngleVelocity",
[1351414674] = "DetailBlendMode",
[1359751819] = "ParticleAngle",
[1366251103] = "DuckSpeed",
[136747755] = "multipass",
[13726709] = "nodiffusebumplighting",
[1380349261] = "Range",
[1381327793] = "Sphere",
[1381580842] = "ConstrainPitch",
[1385904101] = "AlignToSurface",
[1388988026] = "UserData",
[13906112] = "SphericalSize",
[1404127492] = "normalmap2",
[1408682351] = "ParentUID",
[1420980032] = "no_draw",
[1423777818] = "detailtint",
[1427089258] = "AimDir",
[1430631481] = "scaleoutlinesoftnessbasedonscreenres",
[1430956612] = "flat",
[1475057037] = "sheenmapmaskoffsety",
[1475513172] = "Size",
[1477136694] = "lightwarptexture",
[1486876184] = "TextureFrame",
[1511310547] = "BaseTexture",
[1527308877] = "vertexalphatest",
[1527723453] = "ambientoccltexture",
[1529787366] = "eyeballradius",
[153870497] = "FleshDebugForceFleshOn",
[1545096391] = "DrawShadow",
[1547209979] = "PhongTint",
[1553467512] = "RandomPitch",
[1563474897] = "AlphaTest",
[1570418372] = "AnimationRate",
[1572572351] = "FleshNormalTexture",
[1572922901] = "Color1",
[1579039006] = "refracttinttextureframe",
[1581292695] = "CellShade",
[1587990502] = "Offset",
[1589148299] = "Start",
[1595671388] = "Overlapping",
[1597486773] = "flashlighttexture",
[159830115] = "fleshglossbrightness",
[160765680] = "FilterFraction",
[1616823508] = "ActRangeAttack1",
[1635297824] = "selfillummask",
[1639766413] = "seamless_detail",
[1647506049] = "Selfillum",
[1657866020] = "URL",
[1667545098] = "TargetPartUID",
[1688468960] = "LifeTime",
[1702818780] = "Angles",
[1713137144] = "Spacing",
[1716930793] = "color",
[1747493843] = "halflambert",
[1748754976] = "Run",
[1756575192] = "NumberParticles",
[1756738751] = "OutlineAlpha",
[1760996810] = "BlendTintByBaseAlpha",
[1766098085] = "irisv",
[1767680427] = "FleshBorderSoftness",
[1779579145] = "nolod",
[1785337431] = "UseWeaponColor",
[1803606517] = "Phong",
[1808658128] = "glowstart",
[181643838] = "UniqueID",
[1822259635] = "EnvMapMode",
[1822771111] = "debug",
[1823988912] = "InputMultiplier",
[1835620746] = "edgesoftnessstart",
[1840448085] = "StickToSurface",
[1841733856] = "ExectueOnShow",
[1853286158] = "NoDraw",
[1855955664] = "Weapon",
[1857803419] = "nocull",
[1864389370] = "fresnelreflection",
[186789201] = "fleshbordertexture1d",
[1869147073] = "Orthographic",
[1870691467] = "FleshSubsurfaceTexture",
[1872249158] = "Follow",
[1875565421] = "ModelFallback",
[1878085130] = "FollowPartName",
[1886367387] = "UsePlayerColor",
[1889299241] = "BoneMerge",
[1889970156] = "Font",
[1892605006] = "EyeAnglesLerp",
[1899496415] = "ConstrainSphere",
[1908968229] = "PointB",
[1912561071] = "OwnerVelocityMultiplier",
[1914650335] = "VelocityToViewAngles",
[1916030699] = "SelfillumMask",
[1919609898] = "Attract",
[1931874443] = "ModelModifiers",
[1935388575] = "Delay",
[1949158587] = "AttractRadius",
[1951887127] = "StopRadius",
[1953937788] = "selfillumfresnelminmaxexp",
[1963527648] = "phongfresnelranges",
[1963915452] = "envmapcontrast",
[1969067402] = "Rimlight",
[198239607] = "edgesoftnessend",
[1986147915] = "MaxSpeedDamp",
[1988138791] = "Selfillum_Envmapmask_Alpha",
[1994059780] = "detailtexturetransformAngle",
[1996646023] = "PlayerOwner",
[2010458333] = "UseLegacyScale",
[2010856791] = "ResetVelocitiesOnHide",
[2014274444] = "VolumeLFOAmount",
[201810811] = "AddFrametimeLife",
[2022803527] = "bumptransformAngleCenter",
[2027003763] = "Collisions",
[2028067246] = "ColorTint_Tmp",
[2032645517] = "Arguments",
[2039881014] = "AttractMode",
[205696537] = "RimlightExponent",
[2065278286] = "Spread",
[2071291059] = "Override",
[2076886321] = "fleshbordersoftness",
[2084456166] = "fleshnormaltexture",
[2089246144] = "warpparam",
[2097184418] = "AffectChildrenOnly",
[2124224297] = "ModelIndex",
[2126483307] = "FOV",
[2130420070] = "phongtint",
[2131789398] = "OuterVolume",
[2132861502] = "StartLength",
[2133451414] = "Duration",
[2139599550] = "EndLength",
[2145292295] = "Rate",
[2146670156] = "alphatest",
[2149804402] = "detailblendfactor",
[2155233785] = "FleshScrollSpeed",
[2162269484] = "envmapNoHDR",
[2169069517] = "BlurSpacing",
[2172035691] = "use_in_fillrate_mode",
[2187726194] = "texture",
[2192070255] = "fleshinteriornoisetexture",
[2196266252] = "ConstrainRoll",
[219726482] = "basetexturetransformAngle",
[2198807142] = "LevelOfDetail",
[2208170016] = "IgnoreOwner",
[2213789594] = "JiggleAngle",
[2216901808] = "MuteSounds",
[2217606683] = "Air",
[2218327336] = "EnvMapMask",
[2219830704] = "SwimIdle",
[2240868719] = "Bounce",
[2241851621] = "StickEndSize",
[2243556946] = "CrouchIdle",
[2244483011] = "Material",
[2249424691] = "OuterAngle",
[2252036822] = "outlineend0",
[2267036619] = "FleshBorderWidth",
[226825662] = "AddOwnerSpeed",
[2268636547] = "Translucent",
[2269412568] = "normalmapalphaenvmapmask",
[2269939838] = "InvertFrames",
[2294442873] = "Outline",
[2296450635] = "LodOverride",
[229655782] = "normalmap",
[2299360628] = "Amount",
[2299685159] = "seamless_scale",
[2307504008] = "ColorTint_Base",
[230775901] = "Data",
[2307923604] = "TextureFilter",
[2314435604] = "sheenmapmaskdirection",
[2315684609] = "PingPongLoop",
[2320923108] = "StartColor",
[2325995266] = "Selfillum_EnvMapMask_Alpha",
[2332551640] = "MuteFootsteps",
[2343764982] = "Damping",
[2347308090] = "PhongExponent",
[2347713615] = "envmapsphere",
[2351691740] = "rimlight",
[2356986845] = "fleshcubetexture",
[2361627006] = "NormalMapAlphaEnvMapMask",
[2372710726] = "detailtexturetransformAngleCenter",
[23797187] = "DetailScale",
[2381693933] = "EndPointName",
[238564812] = "AlternativeRate",
[239372004] = "PitchLFOAmount",
[23966416] = "Loop",
[2403468563] = "DeathRagdollizeParent",
[2424072824] = "HideRagdollOnDeath",
[2427847608] = "Duplicate",
[2429118223] = "PlayCount",
[2434391136] = "Shadows",
[2442836657] = "PauseOnHide",
[245483896] = "Owner",
[2457472943] = "FleshBorderNoiseScale",
[245941972] = "AnimationType",
[2464347062] = "glowcolor",
[2467927080] = "PointDUID",
[2470426476] = "SecondsToArrive",
[2477553126] = "EmissiveBlendBaseTexture",
[2480406194] = "OutfitPartName",
[2483228587] = "StartSize",
[248425617] = "PointCUID",
[2492705487] = "AimPartUID",
[2494345416] = "vertexalpha",
[249530136] = "fadeoutonsilhouette",
[2497330934] = "Jiggle",
[2498046961] = "Sticky",
[2499943728] = "NoCulling",
[2503655295] = "PositionOffset",
[2504967853] = "ScaleChildren",
[2506526918] = "TextureStretch",
[2507411529] = "Bone",
[2510298171] = "Gravity",
[2511021039] = "sheenmapmaskframe",
[2511903858] = "BlendTintColorOverBase",
[2519455027] = "Brightness",
[2524680267] = "distancealpha",
[2527625975] = "RootOwner",
[2532097615] = "glowy",
[2540525009] = "Expression",
[2545356115] = "ReloadCrouch",
[2551912729] = "StopOtherAnimations",
[2553440258] = "MaxAngular",
[2560789589] = "Flex",
[2561355280] = "PointD",
[2568140703] = "String",
[2570476336] = "Min",
[2585628809] = "detailtexturetransformScale",
[2589223512] = "corneatexture",
[2610384354] = "phongexponenttexture",
[2610874064] = "PitchLFOTime",
[2612037735] = "EndAlpha",
[2612376555] = "refracttint",
[2612594937] = "Text",
[2618392030] = "fleshbordernoisescale",
[2619913426] = "AllowOggWhenMuted",
[2621544536] = "DefaultOnHide",
[2627599123] = "PhongFresnelRanges",
[2630596920] = "fleshsubsurfacetint",
[2639742899] = "AttackCrouchPrimaryfire",
[2645882096] = "ZeroEyePitch",
[2648461065] = "PointCName",
[2655015979] = "OwnerName",
[2655084767] = "invertphongmask",
[2655975514] = "FleshInteriorEnabled",
[2689828446] = "Ground",
[2693580309] = "NoModel",
[2696701625] = "PointBName",
[270362173] = "depthblend",
[2704002686] = "Doppler",
[2704213808] = "parallaxstrength",
[2711406660] = "Strain",
[2722139334] = "ignore_alpha_modulation",
[2722875168] = "Skin",
[2726990886] = "CloakFactor",
[2732657383] = "EmissiveBlendFlowTexture",
[2734613864] = "rimlightboost",
[2745527887] = "EndSize",
[275204260] = "DamageType",
[27535956] = "translucent",
[2759158581] = "SuppressFrames",
[2760075106] = "bumptransformScale",
[2760784560] = "Frequency",
[2764530714] = "PointAUID",
[2766825562] = "envmapmask",
[2770239416] = "DoubleFace",
[2772132969] = "Max",
[2773904166] = "SubMaterialId",
[2775049079] = "FallApartOnDeath",
[2777523306] = "phongboost",
[2783310154] = "flesheffectcenterradius2",
[2790218405] = "AmbientOcclusion",
[2790617357] = "AllowZVelocity",
[2799845852] = "basealphaenvmapmask",
[2811717613] = "Color",
[2818751691] = "fleshbordertint",
[2826786425] = "sheenmapmask",
[2837103662] = "RefractAmount",
[2845320476] = "MaterialOverride",
[2856449777] = "phong",
[2859513395] = "PlayOnFootstep",
[2864808173] = "ConstrainZ",
[2868056120] = "UseEndpointOffsets",
[2868953825] = "sheenpassenabled",
[2870201270] = "StandIdle",
[2873080202] = "BulletImpact",
[287381102] = "Alpha",
[2874029347] = "HideEntity",
[2875597873] = "Path",
[2880540790] = "SlotWeight",
[2897121259] = "WidthBendSize",
[2900648379] = "SizeY",
[2906761383] = "BaseTextureAngle",
[290789797] = "fleshinteriortexture",
[2912286735] = "StickEndAlpha",
[2918809477] = "refracttinttexture",
[29234718] = "AirFriction",
[2937253556] = "localrefractdepth",
[295005948] = "AimPartName",
[2961561047] = "PositionSpread",
[2974105667] = "outlinealpha",
[2982974660] = "Effect",
[2995653722] = "Resolution",
[2995890130] = "Interpolation",
[2999436126] = "SelfCollision",
[300286009] = "RollDelta",
[3013936484] = "sheenmapmaskscalex",
[3015686426] = "EyeTargetUID",
[3052944589] = "frame",
[3056118896] = "allowalphatocoverage",
[3056839399] = "MaxAirSpeed",
[3060607055] = "Darken",
[3060976628] = "PointBUID",
[3068149764] = "RandomRollSpeed",
[3073810188] = "Echo",
[3073827385] = "WidthBend",
[3076101386] = "opaquetexture",
[3078372176] = "phongalbedotint",
[3078601052] = "NoLighting",
[3091768899] = "EmissiveBlendScrollVector",
[3101336424] = "vertexcolormodulate",
[311612122] = "FireDelay",
[3123552686] = "GroundFriction",
[31331793] = "MaximumRadius",
[3149040884] = "DetailTint",
[3164026182] = "outlinestart1",
[3168501257] = "bumpmap",
[3168695210] = "AlternativeScaling",
[318312488] = "ReloadStand",
[3185819868] = "DieTime",
[3191261416] = "Volume",
[3195858990] = "glowend",
[3210380963] = "Position",
[3210927238] = "BlurX",
[3215971424] = "Sliding",
[3220792405] = "Pow",
[322269848] = "basetexture",
[3228764313] = "linearwrite",
[3250695889] = "StickStartAlpha",
[3252764057] = "phongexponent",
[3254932247] = "bumptransformPosition",
[325620535] = "CloakPassEnabled",
[3272832927] = "DampFactor",
[3278221977] = "color2",
[3280264308] = "TranslucentX",
[32833283] = "stretch",
[3284840275] = "fleshinteriorenabled",
[328655192] = "Noclip",
[3287782131] = "BlurFiltering",
[3288592966] = "3D",
[3290824561] = "EditorExpand",
[3291899302] = "znearer",
[3298964978] = "sheenmapmaskscaley",
[3301554095] = "Color2",
[3309610943] = "srgbtint",
[3323140843] = "PhongExponentTexture",
[3339354943] = "localrefract",
[3347082725] = "EnvMapContrast",
[3347359863] = "ParentName",
[3362097708] = "bumpcompress",
[3362122768] = "BlurY",
[3362723860] = "Fallback",
[3363468630] = "ResetOnHide",
[3369233247] = "SelfillumFresnlenMinMaxExp",
[3369354170] = "CenterAttraction",
[3373163503] = "FilterType",
[3380016481] = "LightBlend",
[3388936124] = "FinEfficiency",
[3396130544] = "VariableName",
[3399182291] = "WalkSpeed",
[3402293178] = "IsDisturbing",
[3404345144] = "emissiveblendtexture",
[3415213520] = "outlinestart0",
[3424774496] = "EyeAngles",
[3425825880] = "Mass",
[3428410754] = "FollowAnglesOnly",
[3446009287] = "AttackStandPrimaryfire",
[3460941471] = "Stretch",
[3467113162] = "masked",
[3471299058] = "Speed",
[3483905723] = "FleshBorderTint",
[3490224934] = "alphatestreference",
[3491011159] = "fleshscrollspeed",
[3493953945] = "RimlightBoost",
[3503728928] = "PhongAlbedoTint",
[3504355690] = "alpha",
[3529641707] = "FleshCubeTexture",
[3533715801] = "EnvMap",
[3538076124] = "flesheffectcenterradius3",
[35382841] = "cloakcolortint",
[3554452859] = "NoTextureFiltering",
[3555790875] = "VertexAlpha",
[3557570834] = "CrouchWalk",
[3558548301] = "decal",
[3559176154] = "ambientocclcolor",
[3562291122] = "ActLand",
[3571629350] = "FleshGlossBrightness",
[3572019749] = "forcerefract",
[3573885857] = "EmissiveBlendEnabled",
[3579762028] = "EnvMapTint",
[3580580754] = "RelativeBones",
[3582205125] = "RemoveOnCollide",
[3587230874] = "Maximum",
[3593577359] = "InverseKinematics",
[3608746186] = "FleshSubsurfaceTint",
[3609698214] = "Code",
[3611484261] = "fleshborderwidth",
[3613180849] = "StickStartSize",
[3616895705] = "model",
[3623967811] = "AirResistance",
[3624428921] = "IgnoreZ",
[36256171] = "emissiveblendflowtexture",
[3626359763] = "flashlightnolambert",
[3627612281] = "SlotName",
[3639218737] = "EchoDelay",
[364119328] = "EchoFeedback",
[3653155542] = "blendtintcoloroverbase",
[3653708971] = "fleshglobalopacity",
[365946439] = "MaxSpeed",
[3664816097] = "HideBullets",
[3675654537] = "MinPitch",
[3689107757] = "SizeX",
[3719121240] = "EnvMapMaskScale",
[3724789052] = "OutlineColor",
[3740738969] = "OutfitPartUID",
[374627805] = "Model",
[3751602517] = "corneabumpstrength",
[3756635115] = "envmapsaturation",
[375673178] = "Damage",
[3767786107] = "WeaponHoldType",
[3770904610] = "selfillum",
[3789529631] = "basetexturetransformScale",
[3790311129] = "glowx",
[3792418610] = "rimlightexponent",
[3803564364] = "bumpframe",
[382817369] = "PhongWarpTexture",
[3838112954] = "DistanceAlpha",
[3850296099] = "BaseTexturePosition",
[3862676233] = "additive",
[3864957405] = "Pitch",
[3872259023] = "UnlockPitch",
[3876283572] = "EyeTargetName",
[3877379177] = "PointAName",
[3886387638] = "Passes",
[3888625542] = "CrouchSpeed",
[3902550134] = "EyeTarget",
[3905018527] = "PointA",
[3911318520] = "JigglePosition",
[3915127474] = "Amplitude",
[3934040341] = "glow",
[3955665290] = "BlurLength",
[3955731083] = "Multiplier",
[3961566551] = "receiveflashlight",
[3964430251] = "DrawPlayerOnDeath",
[3978266206] = "fleshdebugforcefleshon",
[3980491407] = "envmap",
[3984299289] = "wireframe",
[3988566908] = "CloakColorTint",
[3988721371] = "VolumeLFOTime",
[3993258444] = "StickToGround",
[4005234647] = "bumpstretch",
[4006412890] = "DrawViewModel",
[4021008510] = "AffectChildren",
[4024739081] = "Bend",
[4025760937] = "UseParticleTracer",
[4031632671] = "irisu",
[4045073016] = "BaseTextureAngleCenter",
[4047268416] = "outlineend1",
[4050194015] = "SequenceName",
[4068251502] = "Style",
[407122208] = "envmapframe",
[4071891638] = "StickLifetime",
[4072346765] = "NearZ",
[4073739642] = "SpawnEntity",
[4074889273] = "Height",
[4078496411] = "TrailPath",
[408731570] = "AlternativeBones",
[410360270] = "emissiveblendenabled",
[410628504] = "LoadVmt",
[410702523] = "basemapalphaphongmask",
[4116696577] = "eyeorigin",
[4119253790] = "depthblendscale",
[4119609374] = "envmaptint",
[4120506060] = "OwnerCycle",
[4122548018] = "OwnerEntity",
[4122897393] = "Invert",
[4130807097] = "SelfillumFresnel",
[4132084891] = "RunSpeed",
[4135986248] = "InputDivider",
[4142526270] = "rimmask",
[4148491794] = "VelocityRoughness",
[4151342750] = "dudvmap",
[4151359135] = "ConstrainYaw",
[4164783995] = "entityorigin",
[4165134066] = "FleshGlobalOpacity",
[4179328573] = "sheenmaptint",
[4180692025] = "AimPart",
[4188075353] = "hdrcolorscale",
[419301429] = "irisframe",
[4195501556] = "SpritePath",
[4201426897] = "TargetPartName",
[4201588131] = "Event",
[4203405457] = "detailblendmode",
[4233617135] = "MaxGroundSpeed",
[4243931689] = "EndPoint",
[424808130] = "basetexturetransformAngleCenter",
[4248310283] = "EndPointUID",
[4262580536] = "Name",
[4263455099] = "VerticalFOV",
[4270648677] = "nofog",
[4290238655] = "separatedetailuvs",
[4294355676] = "FleshInteriorNoiseTexture",
[429871315] = "FixedSize",
[434541590] = "Materials",
[434707923] = "Input",
[436585760] = "intro",
[457408266] = "TextureScroll",
[461806449] = "BoneIndex",
[465977310] = "gammacolorread",
[47348911] = "PositionSpread2",
[485609692] = "Shape",
[491592698] = "LocalVelocity",
[491912776] = "RandomColor",
[497241101] = "OutfitPart",
[499682029] = "refractamount",
[51955875] = "MaxAngularDamp",
[525181279] = "Additive",
[52881986] = "SoundLevel",
[529048665] = "detailframe",
[536679825] = "dilation",
[545579439] = "FleshBorderTexture1D",
[552379675] = "sheenmapmaskoffsetx",
[554243480] = "HalfLambert",
[555957045] = "forcealphawrite",
[572178763] = "vertexcolor",
[5726647] = "BlendMode",
[584367692] = "no_debug_override",
[592602799] = "SelfillumTint",
[599400135] = "cloakfactor",
[601550850] = "HideMesh",
[605808423] = "TargetPart",
[606182201] = "seamless_base",
[609518133] = "glowalpha",
[615666844] = "raytracesphere",
[624765380] = "Axis",
[6377591] = "Weight",
[655049087] = "Sitting",
[665949449] = "softedges",
[678534180] = "BaseAlphaEnvMapMask",
[699038373] = "Detail",
[720518801] = "envmapcameraspace",
[722569406] = "PhongBoost",
[738174289] = "detailtexturetransformPosition",
[738569664] = "ReversePitch",
[741620047] = "scaleedgesoftnessbasedonscreenres",
[744834810] = "sheenindex",
[747255451] = "Class",
[748443611] = "TintColor",
[752581677] = "ignorez",
[753615260] = "bluramount",
[755431908] = "EndColor",
[755434297] = "albedo",
[763824768] = "Scale",
[765711723] = "Walk",
[76905957] = "ExectueOnWear",
[772177811] = "detail",
[774649796] = "RollAmount",
[777998942] = "Physical",
[780061838] = "Swim",
[78016324] = "StopOnHide",
[782008594] = "SprintSpeed",
[78341141] = "Hide",
[783638488] = "selfillumfresnel",
[785434071] = "Operator",
[799765487] = "selfillum_envmapmask_alpha",
[800117097] = "DetailBlendFactor",
[805081881] = "PointDName",
[807305828] = "selfillumtint",
[813694416] = "BonePower",
[81975224] = "LocalPlayerOnly",
[820252378] = "Box",
[841983231] = "emissiveblendtint",
[847148837] = "Lighting",
[847995095] = "MoveChildrenToOrigin",
[862209706] = "emissiveblendbasetexture",
[868798807] = "ConstrainY",
[869823595] = "Function",
[875804561] = "GestureName",
[881024350] = "JumpHeight",
[888504499] = "EnvMapSaturation",
[892946837] = "DoubleSided",
[908225605] = "BodyGroupName",
[912703237] = "noalphamod",
[916203062] = "Fullbright",
[945363216] = "DamageRadius",
[947603807] = "AmbientOcclusionColor",
[951154001] = "End",
[961539200] = "Sound",
[962069675] = "emissiveblendscrollvector",
[968192046] = "DrawManual",
[968774113] = "InvertHideMesh",
[975332729] = "Parent",
[985864094] = "AngleOffset",
}

View File

@@ -0,0 +1,11 @@
--[[
| 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/
--]]
CreateConVar("pac_onuse_only_force", "0", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED), "Forces the default for onuse to be ON on players that have not enabled the override.")

View File

@@ -0,0 +1,31 @@
--[[
| 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/
--]]
concommand.Add("pac_in_editor", function(ply, _, args)
ply:SetNWBool("in pac3 editor", tonumber(args[1]) == 1)
end)
function pace.SpawnPart(ply, model)
if pace.suppress_prop_spawn then return end
if model then
if IsValid(ply) and ply:GetNWBool("in pac3 editor") then
net.Start("pac_spawn_part")
net.WriteString(model)
net.Send(ply)
return false
end
end
end
pac.AddHook( "PlayerSpawnProp", "pac_PlayerSpawnProp", pace.SpawnPart)
pac.AddHook( "PlayerSpawnRagdoll", "pac_PlayerSpawnRagdoll", pace.SpawnPart)
pac.AddHook( "PlayerSpawnEffect", "pac_PlayerSpawnEffect", pace.SpawnPart)
util.AddNetworkString("pac_spawn_part")

View File

@@ -0,0 +1,68 @@
--[[
| 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.dprint(fmt, ...)
if pace.debug then
MsgN("\n")
MsgN(">>>PAC3>>>")
MsgN(fmt:format(...))
if pace.debug_trace then
MsgN("==TRACE==")
debug.Trace()
MsgN("==TRACE==")
end
MsgN("<<<PAC3<<<")
MsgN("\n")
end
end
function pace.CallHook(str, ...)
return hook.Call("pac_" .. str, GAMEMODE, ...)
end
local function wrap_err(ok,...)
if not ok then
ErrorNoHalt(tostring((...)) .. "\n")
end
return ...
end
function pace.PCallCriticalFunction(ply, func, ...)
if ply.pac_pcall_last_error and ply.pac_pcall_last_error + 1 > SysTime() then
local time = RealTime()
if not ply.pac_pcall_next_print or ply.pac_pcall_next_print < time then
pac.Message("cannot handle net message from ", ply, " because it errored less than 1 second ago")
ply.pac_pcall_next_print = time + 1
end
return false
end
local ok, msg = xpcall(func, function(msg)
debug.Trace()
end, ...)
if ok then
return ok, msg
end
pac.Message("net receive error from ", ply, ": ", msg)
ply.pac_pcall_last_error = SysTime()
return false
end
function pace.PCallNetReceive(receive, id, func)
receive(id, function(len, ply, ...)
pace.PCallCriticalFunction(ply, func, len, ply, ...)
end)
end

View File

@@ -0,0 +1,455 @@
--[[
| 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 next = next
local type = type
local istable = istable
local IsValid = IsValid
local tostring = tostring
local isfunction = isfunction
local ProtectedCall = ProtectedCall
pace.StreamQueue = pace.StreamQueue or {}
timer.Create("pac_check_stream_queue", 0.1, 0, function()
local item = table.remove(pace.StreamQueue)
if not item then return end
local data = item.data
local filter = item.filter
local callback = item.callback
local allowed, reason
local function submitPart()
allowed, reason = pace.SubmitPartNow(data, filter)
end
local success = ProtectedCall(submitPart)
if not isfunction(callback) then return end
if not success then
allowed = false
reason = "Unexpected Error"
end
ProtectedCall(function()
callback(allowed, reason)
end)
end)
local function make_copy(tbl, input)
if tbl.self.UniqueID then
tbl.self.UniqueID = pac.Hash(tbl.self.UniqueID .. input)
end
for _, val in pairs(tbl.children) do
make_copy(val, input)
end
end
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
pace.dupe_ents = pace.dupe_ents or {}
local uid2key = include("pac3/editor/server/legacy_network_dictionary_translate.lua")
local function translate_old_dupe(tableIn, target)
for key, value2 in pairs(tableIn) do
local value
if istable(value2) then
value = translate_old_dupe(value2, {})
else
value = value2
end
if isnumber(key) and key > 10000 then
local str = uid2key[key] or key
target[str] = value
else
target[key] = value
end
end
return target
end
duplicator.RegisterEntityModifier("pac_config", function(ply, ent, parts)
if parts.json then
parts = util.JSONToTable(parts.json)
parts = translate_old_dupe(parts, {})
end
local id = ent:EntIndex()
if parts.part then
parts = {[parts.part.self.UniqueID] = parts}
end
ent.pac_parts = parts
pace.dupe_ents[ent:EntIndex()] = {owner = ply, ent = ent}
-- give source engine time
timer.Simple(0, function()
for _, data in pairs(parts) do
if istable(data.part) then
make_copy(data.part, id)
data.part.self.Name = tostring(ent)
data.part.self.OwnerName = id
end
data.owner = ply
data.uid = pac.Hash(ply)
data.is_dupe = true
-- clientside sent variables cleanup for sanity
data.wear_filter = nil
data.partID = nil
data.totalParts = nil
data.transmissionID = nil
pace.SubmitPart(data)
end
end)
end)
function pace.SubmitPartNow(data, filter)
local part = data.part
local owner = data.owner
-- last arg "true" is pac3 only in case you need to do your checking differently from pac2
local allowed, reason = hook.Run("PrePACConfigApply", owner, data, true)
if allowed == false then return allowed, reason end
local uid = data.uid
if uid ~= false and pace.IsBanned(owner) then
return false, "you are banned from using pac"
end
if istable(part) then
local ent = Entity(tonumber(part.self.OwnerName) or -1)
if ent:IsValid() then
if not pace.CanPlayerModify(owner, ent) then
local entOwner = ent.CPPIGetOwner and ent:CPPIGetOwner()
entOwner = tostring(entOwner or "world")
return false, "you are not allowed to modify this entity: " .. tostring(ent) .. " owned by: " .. entOwner
else
if not data.is_dupe then
ent.pac_parts = ent.pac_parts or {}
ent.pac_parts[pac.Hash(owner)] = data
pace.dupe_ents[ent:EntIndex()] = {owner = owner, ent = ent}
duplicator.ClearEntityModifier(ent, "pac_config")
-- fresh table copy
duplicator.StoreEntityModifier(ent, "pac_config", {json = util.TableToJSON(table.Copy(ent.pac_parts))})
end
ent:CallOnRemove("pac_config", function(e)
if e.pac_parts then
for _, eData in pairs(e.pac_parts) do
if istable(eData.part) then
eData.part = eData.part.self.UniqueID
end
pace.RemovePart(eData)
end
end
end)
end
end
end
pace.Parts[uid] = pace.Parts[uid] or {}
if istable(part) then
pace.Parts[uid][part.self.UniqueID] = data
else
if part == "__ALL__" then
pace.Parts[uid] = {}
filter = true
for key, v in pairs(pace.dupe_ents) do
if v.owner:IsValid() and v.owner == owner then
if v.ent:IsValid() and v.ent.pac_parts then
v.ent.pac_parts = nil
duplicator.ClearEntityModifier(v.ent, "pac_config")
end
end
pace.dupe_ents[key] = nil
end
elseif part then
pace.Parts[uid][part] = nil
end
end
local players
if IsValid(data.temp_wear_filter) and type(data.temp_wear_filter) == "Player" then
players = {data.temp_wear_filter}
elseif istable(data.wear_filter) then
players = {}
for _, id in ipairs(data.wear_filter) do
local ply = pac.ReverseHash(id, "Player")
if ply:IsValid() then
table.insert(players, ply)
end
end
else
players = player.GetAll()
end
if filter == false then
filter = owner
elseif filter == true then
local tbl = {}
for _, v in pairs(players) do
if v ~= owner then
table.insert(tbl, v)
end
end
filter = tbl
end
if not data.server_only then
if owner:IsValid() then
data.player_uid = pac.Hash(owner)
end
players = filter or players
if istable(players) then
for key = #players, 1, -1 do
local ply = players[key]
if not ply.pac_requested_outfits and ply ~= owner then
table.remove(players, key)
end
end
if pace.GlobalBans and owner:IsValid() then
local owner_steamid = owner:SteamID()
for key, ply in pairs(players) do
local steamid = ply:SteamID()
for var in pairs(pace.GlobalBans) do
if var == steamid or istable(var) and (table.HasValue(var, steamid) or table.HasValue(var, util.CRC(ply:IPAddress():match("(.+):") or ""))) then
table.remove(players, key)
if owner_steamid == steamid then
pac.Message("Dropping data transfer request by '", ply:Nick(), "' due to a global PAC ban.")
return false, "You have been globally banned from using PAC. See global_bans.lua for more info."
end
end
end
end
end
elseif type(players) == "Player" and (not players.pac_requested_outfits and players ~= owner) then
data.transmissionID = nil
return true
end
if not players or istable(players) and not next(players) then return true end
-- Alternative transmission system
local ret = hook.Run("pac_SendData", players, data)
if ret == nil then
net.Start("pac_submit")
local bytes, err = net_write_table(data)
if not bytes then
local errStr = tostring(err)
ErrorNoHalt("[PAC3] Outfit broadcast failed for " .. tostring(owner) .. ": " .. errStr .. '\n')
if owner and owner:IsValid() then
owner:ChatPrint('[PAC3] ERROR: Could not broadcast your outfit: ' .. errStr)
end
else
net.Send(players)
end
end
if istable(part) then
pace.CallHook("OnWoreOutfit", owner, part)
end
end
-- nullify transmission ID
data.transmissionID = nil
return true
end
-- Inserts the given part into the StreamQueue
function pace.SubmitPart(data, filter, callback)
if istable(data.part) then
pac.dprint("queuing part %q from %s", data.part.self.Name, tostring(data.owner))
table.insert(pace.StreamQueue, {
data = data,
filter = filter,
callback = callback
})
return "queue"
end
return pace.SubmitPartNow(data, filter)
end
-- Inserts the given part into the StreamQueue, and notifies when it completes
function pace.SubmitPartNotify(data)
local part = data.part
local partName = part.self.Name or "no name"
pac.dprint("submitted outfit %q from %s with %i children to set on %s", partName, data.owner:GetName(), table.Count(part.children), part.self.OwnerName or "")
local function callback(allowed, reason)
if allowed == "queue" then return end
if not data.owner:IsPlayer() then return end
reason = reason or ""
if not reason and allowed and istable(part) then
reason = string.format("Your part %q has been applied", partName or "<unknown>")
end
net.Start("pac_submit_acknowledged")
net.WriteBool(allowed)
net.WriteString(reason)
net.WriteString(partName)
net.Send(data.owner)
hook.Run("PACSubmitAcknowledged", data.owner, not not allowed, reason, partName, data)
end
pace.SubmitPart(data, nil, callback)
end
function pace.RemovePart(data)
local part = data.part
local owner = data.owner
pac.dprint("%s is removed %q", owner and owner:IsValid() and owner:GetName(), part)
if part == "__ALL__" then
pace.CallHook("RemoveOutfit", owner)
end
pace.SubmitPart(data, data.filter)
end
function pace.HandleReceivedData(ply, data)
data.owner = ply
data.uid = pac.Hash(ply)
if data.wear_filter and #data.wear_filter > game.MaxPlayers() then
pac.Message("Player ", ply, " tried to submit extraordinary wear filter size of ", #data.wear_filter, ", dropping.")
data.wear_filter = nil
end
if istable(data.part) and data.part.self then
if istable(data.part.self) and not data.part.self.UniqueID then return end -- bogus data
pac.Message("Received pac group ", data.partID or 0 , "/", data.totalParts or 0, " from ", ply)
pace.SubmitPartNotify(data)
elseif isstring(data.part) then
local clearing = data.part == "__ALL__"
pac.Message("Clearing ", clearing and "Oufit" or "Part" , " from ", ply)
pace.RemovePart(data)
end
end
util.AddNetworkString("pac_submit")
local pac_submit_spam = CreateConVar('pac_submit_spam', '1', {FCVAR_NOTIFY, FCVAR_ARCHIVE}, 'Prevent users from spamming pac_submit')
local pac_submit_limit = CreateConVar('pac_submit_limit', '30', {FCVAR_NOTIFY, FCVAR_ARCHIVE}, 'pac_submit spam limit')
pace.PCallNetReceive(net.Receive, "pac_submit", function(len, ply)
if len < 64 then return end
if pac_submit_spam:GetBool() and not game.SinglePlayer() then
local allowed = pac.RatelimitPlayer( ply, "pac_submit", pac_submit_limit:GetInt(), 5, {"Player ", ply, " is spamming pac_submit!"} )
if not allowed then return end
end
if pac.CallHook("CanWearParts", ply) == false then
return
end
net.ReadStream(ply, function(data)
if not data then
pac.Message("message from ", ply, " timed out")
return
end
if not ply:IsValid() then
pac.Message("received message from ", ply, " but player is no longer valid!")
return
end
local buffer = pac.StringStream(data)
pace.HandleReceivedData(ply, buffer:readTable())
end)
end)
function pace.ClearOutfit(ply)
local uid = pac.Hash(ply)
pace.SubmitPart({part = "__ALL__", uid = pac.Hash(ply), owner = ply})
pace.CallHook("RemoveOutfit", ply)
end
function pace.RequestOutfits(ply)
if not ply:IsValid() then return end
if ply.pac_requested_outfits_time and ply.pac_requested_outfits_time > RealTime() then return end
ply.pac_requested_outfits_time = RealTime() + 30
ply.pac_requested_outfits = true
ply.pac_gonna_receive_outfits = true
pace.UpdateWearFilters()
timer.Simple(6, function()
if not IsValid(ply) then return end
ply.pac_gonna_receive_outfits = false
for id, outfits in pairs(pace.Parts) do
local owner = pac.ReverseHash(id, "Player")
if owner:IsValid() and owner:IsPlayer() and owner.GetPos and id ~= pac.Hash(ply) then
for key, outfit in pairs(outfits) do
if not outfit.wear_filter or table.HasValue(outfit.wear_filter, pac.Hash(ply)) then
pace.SubmitPart(outfit, ply)
end
end
end
end
end)
end
concommand.Add("pac_request_outfits", pace.RequestOutfits)

View File

@@ -0,0 +1,64 @@
--[[
| 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/
--]]
util.AddNetworkString('pac_submit_acknowledged')
util.AddNetworkString('pac_update_playerfilter')
local function find_outfits(ply)
for id, outfits in pairs(pace.Parts) do
local owner = pac.ReverseHash(id, "Player")
if owner:IsValid() then
if owner == ply then
return outfits
end
end
end
return {}
end
pace.PCallNetReceive(net.Receive, "pac_update_playerfilter", function(len, ply)
local sizeof = net.ReadUInt(8)
if sizeof > game.MaxPlayers() then
pac.Message("Player ", ply, " tried to submit extraordinary wear filter size of ", sizeof, ", dropping.")
return
end
local ids = {}
for i = 1, sizeof do
table.insert(ids, net.ReadString())
end
for _, outfit in pairs(find_outfits(ply)) do
if outfit.wear_filter then
for _, id in ipairs(ids) do
if not table.HasValue(outfit.wear_filter, id) then
local ply = pac.ReverseHash(id, "Player")
if ply:IsValid() then
if ply.pac_requested_outfits and not ply.pac_gonna_receive_outfits then
pace.SubmitPart(outfit, ply)
end
end
end
end
end
outfit.wear_filter = ids
end
end)
function pace.UpdateWearFilters()
net.Start('pac_update_playerfilter')
net.Broadcast()
end