605 lines
15 KiB
Plaintext
605 lines
15 KiB
Plaintext
--@name Torque editor
|
|
--@author Opti1337
|
|
--@shared
|
|
--@include /koptilnya/libs/render.txt
|
|
--@include /koptilnya/gui/render_devices/hud.txt
|
|
--@include /koptilnya/gui/gui.txt
|
|
--@include /koptilnya/gui/elements/panel.txt
|
|
--@include /koptilnya/gui/elements/label.txt
|
|
--@include /koptilnya/gui/elements/shape.txt
|
|
--@include /koptilnya/gui/elements/button.txt
|
|
--@include /koptilnya/gui/segoe_mdl2_assets_icons.txt
|
|
local points = {
|
|
Vector(0, 0),
|
|
Vector(100, 0)
|
|
}
|
|
local zoom = 1
|
|
local zoomBase = 100
|
|
local scroll = 0
|
|
|
|
if SERVER then
|
|
local user
|
|
|
|
wire.adjustPorts({Seat = "ENTITY"}, {})
|
|
|
|
local function setUser(ply)
|
|
user = ply
|
|
|
|
net.start("setUser")
|
|
net.writeEntity(ply)
|
|
net.send()
|
|
end
|
|
|
|
local function resetUser()
|
|
user = nil
|
|
|
|
net.start("resetUser")
|
|
net.send()
|
|
end
|
|
|
|
hook.add("ClientInitialized", "_ClientInitialized", function(ply)
|
|
net.start("init")
|
|
net.writeFloat(zoom)
|
|
net.writeFloat(scroll)
|
|
net.writeTable(points)
|
|
net.send(ply)
|
|
end)
|
|
|
|
net.receive("settings", function(len, ply)
|
|
zoom = net.readFloat()
|
|
scroll = net.readFloat()
|
|
|
|
net.start("settings")
|
|
net.writeFloat(zoom)
|
|
net.writeFloat(scroll)
|
|
net.send(find.allPlayers(function(p)
|
|
return p ~= ply
|
|
end))
|
|
end)
|
|
|
|
net.receive("points", function(len, ply)
|
|
points = net.readTable()
|
|
|
|
net.start("points")
|
|
net.writeTable(points)
|
|
net.send(find.allPlayers(function(p)
|
|
return p ~= ply
|
|
end))
|
|
end)
|
|
|
|
hook.add("PlayerEnteredVehicle", "_PlayerEnteredVehicle", function(ply, veh)
|
|
if veh == wire.ports.Seat then
|
|
setUser(ply)
|
|
|
|
enableHud(ply, true)
|
|
end
|
|
end)
|
|
|
|
hook.add("PlayerLeaveVehicle", "_PlayerLeaveVehicle", function(ply, veh)
|
|
if veh == wire.ports.Seat then
|
|
resetUser()
|
|
|
|
enableHud(ply, false)
|
|
end
|
|
end)
|
|
|
|
return
|
|
end
|
|
|
|
require("/koptilnya/libs/render.txt")
|
|
require("/koptilnya/gui/render_devices/hud.txt")
|
|
require("/koptilnya/gui/gui.txt")
|
|
require("/koptilnya/gui/elements/panel.txt")
|
|
require("/koptilnya/gui/elements/label.txt")
|
|
require("/koptilnya/gui/elements/shape.txt")
|
|
require("/koptilnya/gui/elements/button.txt")
|
|
|
|
local segoeIcons = require("/koptilnya/gui/segoe_mdl2_assets_icons.txt")
|
|
|
|
local labelsFont = render.createFont("Roboto", 16, 400, true)
|
|
local numbersFont = render.createFont("Roboto Mono", 20)
|
|
local scr = {w = 682, h = 512}
|
|
local padding = 61
|
|
local workspace = {x = padding, y = 0, w = scr.w - padding, h = scr.h - padding}
|
|
local gridSize = Vector(100, 1)
|
|
local zoomSteps = {0.05, 0.1, 0.25, 0.5, 1, 2, 4}
|
|
|
|
-------------------------------------------------------
|
|
|
|
setupPermissionRequest({"file.write", "file.read", "input"}, "", false)
|
|
|
|
local cellSize = Vector((workspace.w - 1) / gridSize.x, workspace.h / gridSize.y)
|
|
|
|
local crosshair
|
|
local user
|
|
|
|
local function sendSettings()
|
|
net.start("settings")
|
|
net.writeFloat(zoom)
|
|
net.writeFloat(scroll)
|
|
net.send()
|
|
end
|
|
|
|
local function sendPoints()
|
|
net.start("points")
|
|
net.writeTable(points)
|
|
net.send()
|
|
end
|
|
|
|
local function cursorIntersectWorkspace(x, y)
|
|
x = x >= workspace.x and x < workspace.x + workspace.w
|
|
y = y >= workspace.y and y <= workspace.y + workspace.h
|
|
|
|
return x and y
|
|
end
|
|
|
|
local function fromWorkspacePos(x, y)
|
|
return workspace.x + x, workspace.y + workspace.h - 1 - y
|
|
end
|
|
|
|
local function toWorkspacePos(x, y)
|
|
return x - workspace.x, workspace.y + workspace.h - 1 - y
|
|
end
|
|
|
|
local function getNearestGridPoint(x, y)
|
|
x, y = toWorkspacePos(x, y)
|
|
|
|
x = math.clamp(x, 0, workspace.w - 1)
|
|
y = math.clamp(y, 0, workspace.h - 1)
|
|
|
|
x = math.round(x / cellSize.x) * cellSize.x
|
|
y = math.round(y / cellSize.y) * cellSize.y
|
|
|
|
x, y = fromWorkspacePos(x, y)
|
|
|
|
return x, y
|
|
end
|
|
|
|
local function zoomIn()
|
|
local zoomId = table.keyFromValue(zoomSteps, zoom)
|
|
|
|
local nextZoom
|
|
if zoomId < #zoomSteps then
|
|
nextZoom = zoomSteps[zoomId + 1]
|
|
zoom = nextZoom
|
|
|
|
sendSettings()
|
|
end
|
|
|
|
return nextZoom
|
|
end
|
|
|
|
local function zoomOut()
|
|
local zoomId = table.keyFromValue(zoomSteps, zoom)
|
|
|
|
local prevZoom
|
|
if zoomId > 1 then
|
|
prevZoom = zoomSteps[zoomId - 1]
|
|
zoom = prevZoom
|
|
|
|
sendSettings()
|
|
end
|
|
|
|
return prevZoom
|
|
end
|
|
|
|
local function scrollUp()
|
|
scroll = scroll + 1
|
|
|
|
sendSettings()
|
|
end
|
|
|
|
local function scrollDown()
|
|
scroll = math.max(scroll - 1, 0)
|
|
|
|
sendSettings()
|
|
end
|
|
|
|
local function addPoint()
|
|
if crosshair ~= nil then
|
|
local x, y = toWorkspacePos(crosshair.x, crosshair.y)
|
|
|
|
x = math.round(x / cellSize.x)
|
|
y = y / (workspace.h - 1) * (zoomBase / zoom) + (zoomBase / zoom / 10 * scroll)
|
|
|
|
--- GOVNO ----------------------------
|
|
|
|
local replaceId
|
|
for i, point in ipairs(points) do
|
|
if point.x == x then
|
|
replaceId = i
|
|
|
|
break
|
|
end
|
|
end
|
|
|
|
if replaceId ~= nil then
|
|
points[replaceId] = Vector(x, y)
|
|
else
|
|
table.insert(points, Vector(x, y))
|
|
end
|
|
|
|
table.sortByMember(points, "x", true)
|
|
|
|
sendPoints()
|
|
|
|
--- KONEC GOVNA ----------------------
|
|
end
|
|
end
|
|
|
|
local function removePoint()
|
|
if crosshair ~= nil then
|
|
local x, y = toWorkspacePos(crosshair.x, crosshair.y)
|
|
|
|
x = math.round(x / cellSize.x)
|
|
y = y / (workspace.h - 1) * (zoomBase / zoom) + (zoomBase / zoom / 10 * scroll)
|
|
|
|
--- GOVNO ----------------------------
|
|
|
|
local replaceId
|
|
for i, point in ipairs(points) do
|
|
if point.x == x then
|
|
replaceId = i
|
|
|
|
break
|
|
end
|
|
end
|
|
|
|
if replaceId ~= nil then
|
|
table.remove(points, replaceId)
|
|
end
|
|
|
|
table.sortByMember(points, "x", true)
|
|
|
|
sendPoints()
|
|
|
|
--- KONEC GOVNA ----------------------
|
|
end
|
|
end
|
|
|
|
local function getYAt(t)
|
|
if #points == 0 then
|
|
return 0
|
|
end
|
|
|
|
if t == 0 then
|
|
return points[1].y
|
|
elseif t == 100 then
|
|
return points[#points].y
|
|
else
|
|
local segment
|
|
|
|
local prevPoint
|
|
for k, point in pairs(points) do
|
|
if t <= point.x then
|
|
segment = {prevPoint, point}
|
|
|
|
break
|
|
end
|
|
|
|
prevPoint = point
|
|
end
|
|
|
|
return math.lerp((t - segment[1].x) / (segment[2].x - segment[1].x), segment[1].y, segment[2].y)
|
|
end
|
|
end
|
|
|
|
local function exportPoints(asE2Array)
|
|
if not hasPermission("file.write") then
|
|
return
|
|
end
|
|
|
|
asE2Array = asE2Array or false
|
|
|
|
local result = ""
|
|
local prefix = asE2Array and "array(" or "{"
|
|
local suffix = asE2Array and ")" or "}"
|
|
local vecSyntax = asE2Array and "vec2" or "Vector"
|
|
|
|
for k, v in pairs(points) do
|
|
result = result .. "\t" .. vecSyntax .. "(" .. (v.x / 100) .. ", " .. v.y .. ")"
|
|
|
|
if k ~= #points then
|
|
result = result .. ",\n"
|
|
end
|
|
end
|
|
|
|
result = string.format("%s\n%s\n%s", prefix, result, suffix)
|
|
|
|
file.write("torque_export_result.txt", result)
|
|
end
|
|
|
|
local function exportTorqueMap()
|
|
if not hasPermission("file.write") then
|
|
return
|
|
end
|
|
|
|
local result = ""
|
|
|
|
for i = 0, 100 do
|
|
result = result .. "\t" .. getYAt(i)
|
|
|
|
if i ~= 100 then
|
|
result = result .. ",\n"
|
|
end
|
|
end
|
|
|
|
result = "array(\n" .. result
|
|
result = result .. "\n)"
|
|
|
|
file.write("torque_export_result.txt", result)
|
|
end
|
|
|
|
local renderDevice = RenderDeviceHUD:new()
|
|
local scrW, scrH = 1920, 1080
|
|
|
|
local gui = GUI:new(renderDevice)
|
|
local w, h = 190, 226
|
|
local panel = EPanel:new()
|
|
panel:setTitle("Torque Editor")
|
|
panel:setDraggable(false)
|
|
panel:setMinimizable(false)
|
|
panel:setCloseable(false)
|
|
panel:setPos(scrW - w - 20, scrH - h - 20)
|
|
panel:setSize(w, h)
|
|
gui:add(panel)
|
|
|
|
local importButton = EButton:new()
|
|
importButton:setPos(11, 43)
|
|
importButton:setSize(w - 22, 24)
|
|
importButton:setText("Import")
|
|
importButton.onMousePressed = function(_, x, y, key, keyName)
|
|
if keyName == "MOUSE1" then
|
|
end
|
|
end
|
|
panel:addChild(importButton)
|
|
|
|
local dividerShape = EShape:new()
|
|
dividerShape:setPos(11, 82)
|
|
dividerShape:setSize(w - 22, 1)
|
|
dividerShape:setColor(Color(60, 60, 60))
|
|
panel:addChild(dividerShape)
|
|
|
|
local exportLabel = ELabel:new()
|
|
exportLabel:setPos(11, 97)
|
|
exportLabel:setFont(GUI.fonts.mainBold)
|
|
exportLabel:setText("Export as")
|
|
panel:addChild(exportLabel)
|
|
|
|
local pointsArrayLabel = ELabel:new()
|
|
pointsArrayLabel:setPos(11, 126)
|
|
pointsArrayLabel:setText("Points array")
|
|
pointsArrayLabel:setColorScheme(Color(200, 200, 200))
|
|
panel:addChild(pointsArrayLabel)
|
|
|
|
local exportE2ArrayButton = EButton:new()
|
|
exportE2ArrayButton:setPos(w - 91, 123)
|
|
exportE2ArrayButton:setText("E2")
|
|
exportE2ArrayButton:setSize(35, 24)
|
|
exportE2ArrayButton.onMousePressed = function(_, x, y, key, keyName)
|
|
if keyName == "MOUSE1" then
|
|
exportPoints(true)
|
|
end
|
|
end
|
|
panel:addChild(exportE2ArrayButton)
|
|
|
|
local exportSFArrayButton = EButton:new()
|
|
exportSFArrayButton:setPos(w - 46, 123)
|
|
exportSFArrayButton:setText("SF")
|
|
exportSFArrayButton:setSize(35, 24)
|
|
exportSFArrayButton.onMousePressed = function(_, x, y, key, keyName)
|
|
if keyName == "MOUSE1" then
|
|
exportPoints()
|
|
end
|
|
end
|
|
panel:addChild(exportSFArrayButton)
|
|
|
|
local exportMapButton = EButton:new()
|
|
exportMapButton:setPos(11, 157)
|
|
exportMapButton:setText("Torque map")
|
|
exportMapButton:setSize(w - 22, 24)
|
|
exportMapButton.onMousePressed = function(_, x, y, key, keyName)
|
|
if keyName == "MOUSE1" then
|
|
exportTorqueMap()
|
|
end
|
|
end
|
|
panel:addChild(exportMapButton)
|
|
|
|
local pointsCountLabel = ELabel:new()
|
|
pointsCountLabel:setPos(11, 200)
|
|
pointsCountLabel:setText("Points count: " .. #points)
|
|
panel:addChild(pointsCountLabel)
|
|
|
|
hook.add("render", "_render", function()
|
|
local cursorX, cursorY
|
|
|
|
if isValid(user) then
|
|
cursorX, cursorY = render.cursorPos(user)
|
|
end
|
|
|
|
local x, y
|
|
|
|
--- Background
|
|
x, y = fromWorkspacePos(0, workspace.h - 1)
|
|
|
|
render.setColor(Color(15, 15, 15))
|
|
render.drawRectFast(x, y, workspace.w, workspace.h)
|
|
|
|
render.setColor(Color(40, 40, 40))
|
|
render.setFont(labelsFont)
|
|
|
|
--- Throttle axis label
|
|
x, y = fromWorkspacePos(30, 31)
|
|
|
|
render.drawRectFast(x, y, 20, 1)
|
|
render.drawSimpleText(x + 22, y - 8, "RPM")
|
|
|
|
--- Torque axis label
|
|
x, y = fromWorkspacePos(30, 50)
|
|
|
|
render.drawRectFast(x, y, 1, 20)
|
|
render.drawRotatedSimpleText(x - 8, y - 2, "Torque", -90)
|
|
|
|
render.setColor(Color(255, 255, 255))
|
|
|
|
--- Throttle axis
|
|
x, y = fromWorkspacePos(0, 0)
|
|
|
|
render.drawRectFast(x, y, workspace.w, 1)
|
|
|
|
--- Torque axis
|
|
x, y = fromWorkspacePos(0, workspace.h - 1)
|
|
|
|
render.drawRectFast(x, y, 1, workspace.h)
|
|
|
|
--- Axes lines & numbers
|
|
for i = 1, 10 do
|
|
|
|
--- Throttle axis
|
|
local segmentSize = (workspace.w - 1) / 10
|
|
|
|
x, y = fromWorkspacePos(segmentSize * i, 0)
|
|
|
|
local length = 20
|
|
local text = tostring(math.remap(i, 0, 10, 1000, 7000)) -- tostring(i * 10)
|
|
|
|
render.drawRect(x, y - 20, 1, 20)
|
|
render.drawRect(x - segmentSize / 2, y - 10, 1, 10)
|
|
|
|
render.setFont(numbersFont)
|
|
render.drawSimpleText(x - (9 * #text), y, text)
|
|
|
|
--- Torque axis
|
|
segmentSize = (workspace.h - 1) / 10
|
|
x, y = fromWorkspacePos(0, segmentSize * i)
|
|
text = tostring(zoomBase / zoom / 10 * (i + scroll))
|
|
|
|
render.drawRect(x, y, 20, 1)
|
|
render.drawRect(x, y + segmentSize / 2, 10, 1)
|
|
|
|
render.setFont(numbersFont)
|
|
render.drawSimpleText(x - (10 * #text), y - 2, text)
|
|
end
|
|
|
|
--- Zero
|
|
x, y = fromWorkspacePos(-40, 0)
|
|
|
|
render.setFont(numbersFont)
|
|
render.drawSimpleText(x, y, "1000")
|
|
|
|
if cursorX ~= nil and cursorIntersectWorkspace(cursorX, cursorY) then
|
|
x, y = fromWorkspacePos(0, workspace.h - 1)
|
|
cursorX = getNearestGridPoint(cursorX, cursorY)
|
|
cursorY = math.clamp(cursorY, workspace.y, workspace.y + workspace.h - 1)
|
|
|
|
crosshair = {x = cursorX, y = cursorY}
|
|
|
|
render.setColor(Color(60, 60, 60, 200))
|
|
render.drawRect(x, cursorY, workspace.w, 1)
|
|
render.drawRect(cursorX, y, 1, workspace.h)
|
|
else
|
|
crosshair = nil
|
|
end
|
|
|
|
render.setColor(Color(255, 150, 50))
|
|
|
|
local prevPointData
|
|
for _, point in pairs(points) do
|
|
x, y = fromWorkspacePos(cellSize.x * point.x, (workspace.h - 1) / (zoomBase / zoom) * point.y - ((workspace.h - 1) / 10 * scroll))
|
|
|
|
if prevPointData ~= nil then
|
|
render.drawLine(prevPointData.x, prevPointData.y, x, y)
|
|
end
|
|
|
|
render.drawRect(x - 1, y - 1, 3, 3)
|
|
|
|
prevPointData = {point = point, x = x, y = y}
|
|
end
|
|
|
|
local t = (math.cos(timer.curtime() * 0.5) + 1) / 2 * 100
|
|
|
|
x, y = fromWorkspacePos(cellSize.x * t, (workspace.h - 1) / (zoomBase / zoom) * getYAt(t) - ((workspace.h - 1) / 10 * scroll))
|
|
|
|
render.setColor(Color(0, 0, 255, 180))
|
|
render.drawRect(x - 3, y - 3, 7, 7)
|
|
|
|
render.drawRect(x, workspace.y, 1, workspace.h)
|
|
end)
|
|
|
|
net.receive("setUser", function()
|
|
net.readEntity(function(ent)
|
|
user = ent
|
|
end)
|
|
end)
|
|
|
|
net.receive("resetUser", function()
|
|
user = nil
|
|
end)
|
|
|
|
net.receive("init", function()
|
|
zoom = net.readFloat()
|
|
scroll = net.readFloat()
|
|
points = net.readTable()
|
|
end)
|
|
|
|
net.receive("settings", function()
|
|
zoom = math.round(net.readFloat() * 100) / 100
|
|
scroll = math.round(net.readFloat() * 100) / 100
|
|
|
|
|
|
end)
|
|
|
|
net.receive("points", function()
|
|
points = net.readTable()
|
|
|
|
pointsCountLabel:setText("Points count: " .. #points)
|
|
end)
|
|
|
|
hook.add("hudconnected", "_hudconnected", function()
|
|
-- renderDevice:setPlayer()
|
|
if not hasPermission("file.write") or not hasPermission("file.read") then
|
|
sendPermissionRequest()
|
|
end
|
|
end)
|
|
|
|
hook.add("huddisconnected", "_huddisconnected", function()
|
|
end)
|
|
|
|
hook.add("inputPressed", "_inputPressed", function(button)
|
|
if not hasPermission("input") then
|
|
return
|
|
end
|
|
|
|
if player() == user then
|
|
local keyName = input.getKeyName(button)
|
|
|
|
if keyName == "t" then
|
|
input.enableCursor(!input.getCursorVisible())
|
|
elseif keyName == "MOUSE1" then
|
|
if not input.getCursorVisible() then
|
|
addPoint()
|
|
|
|
pointsCountLabel:setText("Points count: " .. #points)
|
|
end
|
|
elseif keyName == "MOUSE2" then
|
|
if not input.getCursorVisible() then
|
|
removePoint()
|
|
|
|
pointsCountLabel:setText("Points count: " .. #points)
|
|
end
|
|
elseif keyName == "MWHEELUP" then
|
|
if input.isShiftDown() then
|
|
zoomIn()
|
|
else
|
|
scrollUp()
|
|
end
|
|
elseif keyName == "MWHEELDOWN" then
|
|
if input.isShiftDown() then
|
|
zoomOut()
|
|
else
|
|
scrollDown()
|
|
end
|
|
end
|
|
end
|
|
end)
|