dobrograd-13-06-2022/garrysmod/addons/feature-cars/lua/car-dealer/vgui/vehviewer.lua

664 lines
19 KiB
Lua
Raw Normal View History

2023-11-16 15:01:19 +05:00
surface.CreateFont('car-dealer.view.title', {
font = 'Calibri',
size = 56,
weight = 500,
antialias = true,
extended = true,
})
surface.CreateFont('car-dealer.view.subtitle', {
font = 'Calibri',
size = 28,
weight = 500,
antialias = true,
extended = true,
})
surface.CreateFont('car-dealer.view.subtitle-plate', {
font = 'Plat Nomor',
size = 24,
weight = 500,
antialias = true,
extended = true,
})
local function getTrunkSize(simfphysID, ent)
local spData = list.Get('simfphys_vehicles')[simfphysID]
if not spData then return end
local trunkData = spData.Members.Trunk
if not trunkData then return end
for i = 2, #trunkData do
if not istable(trunkData[i]) then break end
local volume, bgID, bgVal = unpack(trunkData[i])
if ent:GetBodygroup(bgID) == bgVal then
return volume
end
end
return trunkData[1]
end
local PANEL = {}
function PANEL:Init()
self:Dock(FILL)
local mdl = self:Add 'cd_vehModel'
mdl:Dock(FILL)
mdl.canControl = true
mdl.camOffset = Vector(60, 0, 0)
mdl.camPos = Vector(300, 400, 75)
mdl.fovMultiplier = 0.9
self.mdl = mdl
local mats = cdData and cdData.default.mats or {}
for k, v in pairs(mats) do
self.mdl:SetSubMaterial(k-1, v)
end
local mdlButs = mdl:Add 'DPanel'
mdlButs:Dock(BOTTOM)
mdlButs:DockMargin(10, 0, 0, 10)
mdlButs:SetTall(32)
mdlButs:SetPaintBackground(false)
self.mdlButs = mdlButs
local title = self:Add 'DLabel'
title:SetFont('car-dealer.view.title')
title:SetText('Выбери машину внизу')
self.title = title
-- local tags = mdl:Add 'DPanel'
-- tags:SetPaintBackground(false)
-- tags:SetTall(16)
-- self.tags = tags
local health = self:Add 'DProgressLabel'
health:SetWide(200)
self.statusHealth = health
local fuel = self:Add 'DProgressLabel'
fuel:SetWide(200)
self.statusFuel = fuel
local subtitle = self:Add 'DLabel'
subtitle:SetFont('car-dealer.view.subtitle')
subtitle:SetText('')
self.subtitle = subtitle
local mass = self:Add 'DProgressLabel'
mass:SetWide(200)
mass.max = octolib.table.reduce(carDealer.vehicles, function(a, cdData)
return math.max(a, list.Get('simfphys_vehicles')[cdData.simfphysID].Members.Mass)
end, 0)
self.mass = mass
local grip = self:Add 'DProgressLabel'
grip:SetWide(200)
grip.max = octolib.table.reduce(carDealer.vehicles, function(a, cdData)
return math.max(a, list.Get('simfphys_vehicles')[cdData.simfphysID].Members.MaxGrip)
end, 0)
self.grip = grip
local torque = self:Add 'DProgressLabel'
torque:SetWide(200)
torque.max = octolib.table.reduce(carDealer.vehicles, function(a, cdData)
return math.max(a, list.Get('simfphys_vehicles')[cdData.simfphysID].Members.PeakTorque)
end, 0)
self.torque = torque
local steerSpeed = self:Add 'DProgressLabel'
steerSpeed:SetWide(200)
steerSpeed.max = octolib.table.reduce(carDealer.vehicles, function(a, cdData)
return math.max(a, list.Get('simfphys_vehicles')[cdData.simfphysID].Members.TurnSpeed)
end, 0)
self.steerSpeed = steerSpeed
local fuel = self:Add 'DProgressLabel'
fuel:SetWide(200)
fuel.max = octolib.table.reduce(carDealer.vehicles, function(a, cdData)
return math.max(a, list.Get('simfphys_vehicles')[cdData.simfphysID].Members.FuelTankSize)
end, 0)
self.fuel = fuel
local lv = self:Add 'DListView'
lv:SetWide(200)
lv:AddColumn('Параметр')
lv:AddColumn('Значение')
lv:SetHideHeaders(true)
lv:SetTall(200)
self.lv = lv
local buts = self:Add 'DPanel'
buts:SetPaintBackground(false)
buts:SetSize(200, 200)
self.buts = buts
end
function PANEL:PerformLayout()
local y = 10
local title = self.title
title:SizeToContents()
title:AlignRight(15)
title:AlignTop(10)
y = y + title:GetTall()
-- local tags = self.tags
-- tags:SetSize(100, 16)
-- tags:AlignTop(35)
-- tags:AlignRight(title:GetWide() + 30)
local subtitle = self.subtitle
if subtitle:GetText() ~= '' then
subtitle:SizeToContents()
subtitle:AlignRight(15)
subtitle:SetPos(subtitle:GetPos(), y - 5)
y = y + subtitle:GetTall() + 5
end
if self.vehData and self.vehData.id then
self.mass:SetVisible(false)
self.fuel:SetVisible(false)
self.torque:SetVisible(false)
self.grip:SetVisible(false)
self.steerSpeed:SetVisible(false)
self.lv:SetVisible(false)
local health = self.statusHealth
health:SetVisible(true)
health:AlignRight(15)
health:SetPos(health:GetPos(), y)
y = y + health:GetTall() + 2
local fuel = self.statusFuel
fuel:SetVisible(true)
fuel:AlignRight(15)
fuel:SetPos(fuel:GetPos(), y)
y = y + fuel:GetTall() + 15
else
self.statusHealth:SetVisible(false)
self.statusFuel:SetVisible(false)
local mass = self.mass
mass:SetVisible(true)
mass:AlignRight(15)
mass:SetPos(mass:GetPos(), y)
y = y + mass:GetTall() + 2
local fuel = self.fuel
fuel:SetVisible(true)
fuel:AlignRight(15)
fuel:SetPos(fuel:GetPos(), y)
y = y + fuel:GetTall() + 5
local torque = self.torque
torque:SetVisible(true)
torque:AlignRight(15)
torque:SetPos(torque:GetPos(), y)
y = y + torque:GetTall() + 2
local grip = self.grip
grip:SetVisible(true)
grip:AlignRight(15)
grip:SetPos(grip:GetPos(), y)
y = y + grip:GetTall() + 2
local steerSpeed = self.steerSpeed
steerSpeed:SetVisible(true)
steerSpeed:AlignRight(15)
steerSpeed:SetPos(steerSpeed:GetPos(), y)
y = y + steerSpeed:GetTall() + 2
local lv = self.lv
lv:SetVisible(true)
lv:AlignRight(15)
lv:SetPos(lv:GetPos(), y)
y = y + lv:GetTall() + 2
end
local buts = self.buts
buts:AlignRight(15)
buts:AlignBottom(15)
end
function PANEL:DelayedRefresh()
timer.Simple(0.5, function()
self:Refresh()
end)
end
function PANEL:SetVehicle(class, data)
data = data or {}
data.data = data.data or {}
local cdData = carDealer.vehicles[class]
if not cdData then return end
local catData = carDealer.categories[cdData.category]
if not catData then return end
self.class = class
self.vehData = data
local spData = list.Get('simfphys_vehicles')[cdData.simfphysID]
assert(spData ~= nil, 'Wrong simfphysID for ' .. class)
local m = spData.Members
local price = hook.Run('car-dealer.priceOverride', LocalPlayer(), class) or cdData.price
self.mdl:SetVehicle(class, data)
self.title:SetText(cdData.name)
self.subtitle:SetText(data.plate or DarkRP.formatMoney(price))
self.subtitle:SetFont(data.plate and 'car-dealer.view.subtitle-plate' or 'car-dealer.view.subtitle')
if data.id then
local status = data.data
self.statusHealth:SetFraction(status.health or 1)
self.statusHealth:SetText('Исправность: ' .. math.Round((status.health or 1) * 100, 0) .. '%')
local liters = math.Round((status.fuel or 1) * m.FuelTankSize, 1)
self.statusFuel:SetFraction(status.fuel or 1)
self.statusFuel:SetText('В баке: ' .. liters .. 'л')
end
self.mass:SetFraction(m.Mass / self.mass.max)
self.mass:SetText('Масса: ' .. math.floor(m.Mass * 0.75) .. 'кг')
self.fuel:SetFraction(m.FuelTankSize / self.fuel.max)
self.fuel:SetText('Объем бака: ' .. m.FuelTankSize .. 'л')
self.torque:SetFraction(m.PeakTorque / self.torque.max)
self.torque:SetText('Мощность двигателя')
self.grip:SetFraction(m.MaxGrip / self.grip.max)
self.grip:SetText('Сцепление с дорогой')
self.steerSpeed:SetFraction(m.TurnSpeed / self.steerSpeed.max)
self.steerSpeed:SetText('Скорость поворота')
local lv = self.lv
lv:Clear()
lv:AddLine('Тип топлива', m.FuelType == FUELTYPE_DIESEL and 'Дизель' or 'Бензин')
lv:AddLine('Привод', m.PowerBias > 0.5 and 'Задний' or m.PowerBias < -0.5 and 'Передний' or 'Полный')
lv:AddLine('Угол поворота', m.CustomSteerAngle .. '°')
lv:AddLine('Мест', #(m.PassengerSeats or {}) + 1)
local trunk = getTrunkSize(cdData.simfphysID, self.mdl.Entity)
local trunkLine = lv:AddLine('Багажник', trunk and (trunk .. 'л') or 'Нет')
local function setBodygroup(bgID, bgVal)
self.mdl.Entity:SetBodygroup(bgID, bgVal)
local trunk = getTrunkSize(cdData.simfphysID, self.mdl.Entity)
trunkLine:SetColumnText(2, trunk and (trunk .. 'л') or 'Нет')
end
self.mdlButs:Clear()
local lData = list.Get('simfphys_lights')[m.LightsTable or '']
if lData and lData.SubMaterials then
local mats = lData.SubMaterials
local headLightMode = 0
local lightMode = 'Base'
local function updateSubMats()
local t
if headLightMode == 0 then
t = mats.off[lightMode] or mats.off.Base
elseif headLightMode == 1 then
t = mats.on_lowbeam[lightMode] or mats.on_lowbeam.Base
elseif headLightMode == 2 then
t = mats.on_highbeam[lightMode] or mats.on_highbeam.Base
end
for k, v in pairs(t) do
self.mdl.Entity:SetSubMaterial(k, v)
end
end
local butLights = self.mdlButs:Add 'DImageButton'
butLights:Dock(LEFT)
butLights:DockMargin(0, 0, 5, 0)
butLights:SetWide(32)
butLights:SetIcon('octoteam/icons-car/light_close.png')
butLights:SetAlpha(100)
butLights:AddHint('Переключить фары')
local ppTarget = 0
function butLights.DoClick()
headLightMode = octolib.math.loop(headLightMode + 1, 0, 3)
if headLightMode == 0 then
butLights:SetIcon('octoteam/icons-car/light_close.png')
butLights:SetAlpha(100)
ppTarget = 0
elseif headLightMode == 1 then
butLights:SetIcon('octoteam/icons-car/light_close.png')
butLights:SetAlpha(255)
ppTarget = 1
elseif headLightMode == 2 then
butLights:SetIcon('octoteam/icons-car/light_far.png')
butLights:SetAlpha(255)
ppTarget = 1
end
updateSubMats()
end
if lData.PoseParameters then
local curState = 0
function butLights.Think()
curState = math.Approach(curState, ppTarget, FrameTime() * 2)
self.mdl.Entity:SetPoseParameter(lData.PoseParameters.name, curState)
end
end
local butBrake = self.mdlButs:Add 'DImageButton'
butBrake:Dock(LEFT)
butBrake:DockMargin(0, 0, 5, 0)
butBrake:SetWide(32)
butBrake:AddHint('Переключить тормоз')
butBrake:SetIcon('octoteam/icons-car/brake.png')
butBrake:SetAlpha(100)
butBrake.on = false
function butBrake.DoClick()
butBrake.on = not butBrake.on
butBrake:SetAlpha(butBrake.on and 255 or 100)
if butBrake.on then
if lightMode == 'Base' then
lightMode = 'Brake'
elseif lightMode == 'Reverse' then
lightMode = 'Brake_Reverse'
end
else
if lightMode == 'Brake' then
lightMode = 'Base'
elseif lightMode == 'Brake_Reverse' then
lightMode = 'Reverse'
end
end
updateSubMats()
end
local butReverse = self.mdlButs:Add 'DImageButton'
butReverse:Dock(LEFT)
butReverse:DockMargin(0, 0, 5, 0)
butReverse:SetWide(32)
butReverse:AddHint('Переключить реверс')
butReverse:SetIcon('octoteam/icons-car/left_3.png')
butReverse:SetAlpha(100)
butReverse.on = false
function butReverse.DoClick()
butReverse.on = not butReverse.on
butReverse:SetAlpha(butReverse.on and 255 or 100)
if butReverse.on then
if lightMode == 'Base' then
lightMode = 'Reverse'
elseif lightMode == 'Brake' then
lightMode = 'Brake_Reverse'
end
else
if lightMode == 'Reverse' then
lightMode = 'Base'
elseif lightMode == 'Brake_Reverse' then
lightMode = 'Brake'
end
end
updateSubMats()
end
if mats.turnsignals then
local butTurn = self.mdlButs:Add 'DImageButton'
butTurn:Dock(LEFT)
butTurn:DockMargin(0, 0, 5, 0)
butTurn:SetWide(32)
butTurn:AddHint('Переключить фары')
butTurn:SetIcon('octoteam/icons-car/alarm.png')
butTurn:SetAlpha(100)
butTurn.on = false
butTurn.curState = false
function butTurn.DoClick()
butTurn.on = not butTurn.on
butTurn:SetAlpha(butTurn.on and 255 or 100)
if not butTurn.on then
for k in pairs(mats.turnsignals.left) do self.mdl.Entity:SetSubMaterial(k, '') end
for k in pairs(mats.turnsignals.right) do self.mdl.Entity:SetSubMaterial(k, '') end
butTurn.curState = false
end
end
function butTurn.Think()
local ct = CurTime()
if not butTurn.on or ct < (butTurn.nextToggle or 0) then return end
butTurn.curState = not butTurn.curState
for k, v in pairs(mats.turnsignals.left) do self.mdl.Entity:SetSubMaterial(k, butTurn.curState and v or '') end
for k, v in pairs(mats.turnsignals.right) do self.mdl.Entity:SetSubMaterial(k, butTurn.curState and v or '') end
butTurn.nextToggle = ct + 0.5
end
end
end
-- self.tags:Clear()
-- local tags = table.Copy(cdData.tags or {})
-- hook.Run('car-dealer.populateTags', class, cdData, tags)
-- for _, tag in ipairs(tags) do
-- if not data.id or tag[3] then
-- local icon = self.tags:Add 'DImageButton'
-- icon:Dock(RIGHT)
-- icon:DockMargin(5, 0, 0, 0)
-- icon:SetWide(16)
-- icon:SetImage(tag[1])
-- icon:AddOctoHint(tag[2])
-- end
-- end
self:InvalidateLayout(true)
self.buts:Clear()
local ply = LocalPlayer()
local veh = carDealer.getCurVeh(ply)
local q = ply:GetLocalVar('car-dealer.queued')
if not data.id and not cdData.deposit then
local bgs = {}
local bgPrices = {}
local bBuy = self.buts:Add 'DButton'
bBuy:Dock(BOTTOM)
bBuy:DockMargin(0, 5, 0, 0)
bBuy:SetTall(25)
bBuy:SetIcon('icon16/money.png')
bBuy:SetText('Купить')
bBuy.DoClick = function()
local priceTotal = octolib.table.reduce(bgPrices, function(a, v) return a + v end, price)
local text = ('Ты уверен, что хочешь купить %s за %s?')
:format(cdData.name or 'Автомобиль', carDealer.formatMoney(priceTotal))
Derma_Query(text, 'Купить автомобиль', 'Да', function()
bBuy:SetEnabled(false)
netstream.Request('car-dealer.buy', class, bgs):Then(function(ok)
if not ok then return end
carDealer.menu.list:SwitchToName('Мой гараж')
end):Finally(function()
if IsValid(bBuy) then bBuy:SetEnabled(true) end
end)
end, 'Нет')
end
if cdData.bodygroups then
local bOptions = self.buts:Add 'DButton'
bOptions:Dock(BOTTOM)
bOptions:DockMargin(0, 5, 0, 0)
bOptions:SetTall(25)
bOptions:SetIcon('icon16/cog.png')
bOptions:SetText('Конфигурация')
bOptions.DoClick = function()
local m = DermaMenu()
for bgID, group in pairs(cdData.bodygroups) do
local sm = m:AddSubMenu(group.name)
for val, variant in pairs(group.variants) do
local bgVal = val - 1
local originalVal = cdData.default and cdData.default.bg and cdData.default.bg[bgID] or 0
local isOriginal = bgVal == originalVal
local variantName, variantPrice = unpack(variant)
local o = sm:AddOption(isOriginal and variantName or ('%s (+%s)'):format(variantName, carDealer.formatMoney(variantPrice)), function()
setBodygroup(bgID, bgVal)
bgs[bgID] = not isOriginal and bgVal or nil
bgPrices[bgID] = not isOriginal and variantPrice or nil
self.subtitle:SetText(carDealer.formatMoney(octolib.table.reduce(bgPrices, function(a, v) return a + v end, price)))
self:InvalidateLayout()
end)
if self.mdl.Entity:GetBodygroup(bgID) == bgVal then
o:SetIcon('icon16/tick.png')
end
end
end
m:Open()
end
end
elseif IsValid(veh) and veh:GetNetVar('cd.id') == data.id then
-- cdData.deposit or not ply:GetLocalVar('cd.cantHaveOwned')
local butDespawn = self.buts:Add 'DButton'
butDespawn:Dock(BOTTOM)
butDespawn:DockMargin(0, 5, 0, 0)
butDespawn:SetTall(25)
butDespawn:SetIcon('icon16/car.png')
butDespawn:SetText('Загнать')
butDespawn.DoClick = function()
butDespawn:SetEnabled(false)
netstream.Request('car-dealer.despawn'):Finally(function()
if IsValid(butDespawn) then butDespawn:SetEnabled(true) end
self:DelayedRefresh()
end)
end
elseif q then
if q[1] == data.id or q[1] == class then
local b2 = self.buts:Add 'DButton'
b2:Dock(BOTTOM)
b2:DockMargin(0, 5, 0, 0)
b2:SetTall(25)
b2:SetIcon('icon16/cancel.png')
b2:SetText('Отменить')
b2.DoClick = function()
b2:SetEnabled(false)
netstream.Request('car-dealer.despawn'):Finally(function()
if IsValid(b2) then b2:SetEnabled(true) end
self:DelayedRefresh()
end)
end
local ready = ply:GetLocalVar('car-dealer.queuedReady')
local b1 = self.buts:Add 'DButton'
b1:Dock(BOTTOM)
b1:DockMargin(0, 5, 0, 0)
b1:SetTall(25)
b1:SetIcon('icon16/accept.png')
b1:SetText(ready and 'Подтвердить' or 'В очереди (' .. q[2] .. ')...')
b1:SetEnabled(ready)
b1.DoClick = function()
b1:SetEnabled(false)
local function finally()
if IsValid(b1) then b1:SetEnabled(true) end
self:DelayedRefresh()
end
if q[1] == data.id then
netstream.Request('car-dealer.spawn', data.id):Finally(finally)
elseif q[1] == class then
netstream.Request('car-dealer.rent', class):Finally(finally)
end
end
if not ready then
hook.Add('octolib.netVarUpdate', 'car-dealer.queue', function(ent, var, val)
if ent ~= ply:EntIndex() or var ~= 'car-dealer.queued' then return end
if not IsValid(b1) or not val then return hook.Remove('octolib.netVarUpdate', 'car-dealer.queue') end
b1:SetText('В очереди (' .. val[2] .. ')...')
octolib.notify.show('hint', 'Обновлена очередь за автомобилем: ', tostring(val[2]))
end)
end
else
local b1 = self.buts:Add 'DButton'
b1:Dock(BOTTOM)
b1:DockMargin(0, 5, 0, 0)
b1:SetTall(25)
b1:SetIcon('icon16/accept.png')
b1:SetText('Ждем другое авто...')
b1:SetEnabled(false)
end
else
if data.id then
local b2 = self.buts:Add 'DButton'
b2:Dock(BOTTOM)
b2:DockMargin(0, 5, 0, 0)
b2:SetTall(25)
b2:SetIcon('icon16/delete.png')
b2:SetText('Продать')
b2:SetEnabled(carDealer.enabled)
b2.DoClick = function()
local mul = carDealer.sellPrice or 1
local text = ('Ты уверен, что хочешь продать %s за %s?')
:format(cdData.name or 'Автомобиль', carDealer.formatMoney(math.floor(mul * price)))
Derma_Query(text, 'Продать автомобиль', 'Да', function()
if IsValid(b2) then b2:SetEnabled(false) end
netstream.Request('car-dealer.sell', data.id):Finally(function()
if IsValid(b2) then b2:SetEnabled(true) end
self:DelayedRefresh()
end)
end, 'Нет')
end
local b1 = self.buts:Add 'DButton'
b1:Dock(BOTTOM)
b1:DockMargin(0, 5, 0, 0)
b1:SetTall(25)
b1:SetIcon('icon16/car.png')
b1:SetText('Пригнать')
b1.DoClick = function()
b1:SetEnabled(false)
netstream.Request('car-dealer.spawn', data.id):Finally(function()
if IsValid(b1) then b1:SetEnabled(true) end
self:DelayedRefresh()
end)
end
elseif cdData.deposit then
local bRent = self.buts:Add 'DButton'
bRent:Dock(BOTTOM)
bRent:DockMargin(0, 5, 0, 0)
bRent:SetTall(25)
bRent:SetIcon('icon16/money.png')
bRent:SetText('Арендовать')
bRent.DoClick = function()
local text = ('Ты уверен, что хочешь арендовать %s за %s?')
:format(cdData.name or 'Автомобиль', carDealer.formatMoney(price))
Derma_Query(text, 'Арендовать автомобиль', 'Да', function()
bRent:SetEnabled(false)
netstream.Request('car-dealer.rent', class):Then(function()
if IsValid(bRent) then bRent:SetEnabled(true) end
self:DelayedRefresh()
end)
end, 'Нет')
end
end
end
end
function PANEL:Refresh()
if self.vehData and self.vehData.id and self.vehData ~= carDealer.cache.owned[self.vehData.id] then
self.vehData = carDealer.cache.owned[self.vehData.id]
end
self:SetVehicle(self.class, self.vehData)
end
function PANEL:Paint()
-- no paint
end
vgui.Register('cd_vehViewer', PANEL, 'DPanel')