297 lines
7.2 KiB
Lua
297 lines
7.2 KiB
Lua
--@include ./enums/powertrain_component.txt
|
|
--@include ./factories/powertrain_component.txt
|
|
--@include /koptilnya/libs/table.txt
|
|
--@include /koptilnya/libs/constants.txt
|
|
--@include /libs/task.txt
|
|
local Task = require('/libs/task.txt')
|
|
|
|
local PowertrainComponentFactory = require('./factories/powertrain_component.txt')
|
|
|
|
local POWERTRAIN_COMPONENT = require('./enums/powertrain_component.txt')
|
|
|
|
require('/koptilnya/libs/table.txt')
|
|
require('/koptilnya/libs/constants.txt')
|
|
|
|
---@class KPTLVehicle
|
|
---@field config { Name: string, Input?: string, Config?: table }[]
|
|
---@field components PowertrainComponent[]
|
|
---@field headComponents PowertrainComponent[]
|
|
---@field steer number
|
|
---@field brake number
|
|
---@field handbrake number
|
|
---@field playersConnectedToHUD Player[]
|
|
---@field base? Entity
|
|
---@field basePhysObject? PhysObj
|
|
---@field initialized boolean
|
|
---@field initializedPlayers Player[]
|
|
local Vehicle = class('Vehicle')
|
|
|
|
---@param config { [string]: any }
|
|
function Vehicle:initialize(config)
|
|
if not Vehicle:validateConfig(config) then
|
|
throw('Please check your vehicle configuration!')
|
|
end
|
|
|
|
self.initialized = false
|
|
|
|
self.config = config
|
|
self.components = {}
|
|
self.headComponents = {}
|
|
|
|
self.steer = 0
|
|
self.brake = 0
|
|
self.handbrake = 0
|
|
self.initializedPlayers = {}
|
|
self.playersConnectedToHUD = {}
|
|
|
|
if SERVER then
|
|
self.playersConnectedToHUD = find.allPlayers(function(ply)
|
|
return isValid(ply) and ply:isHUDActive()
|
|
end)
|
|
|
|
hook.add('Input', 'vehicle_wire_input', function(name, value)
|
|
self:handleWireInput(name, value)
|
|
end)
|
|
|
|
---@diagnostic disable-next-line: param-type-mismatch
|
|
hook.add('Tick', 'vehicle_update', function()
|
|
self:update()
|
|
end)
|
|
|
|
hook.add('HUDConnected', 'vehicle_hudconnected', function(ent, ply)
|
|
table.insert(self.playersConnectedToHUD, ply)
|
|
end)
|
|
|
|
hook.add('HUDDisconnected', 'vehicle_huddisconnected', function(ent, ply)
|
|
table.removeByValue(self.playersConnectedToHUD, ply)
|
|
end)
|
|
|
|
hook.add('PlayerDisconnected', 'vehicle_huddisconnected', function(ply)
|
|
table.removeByValue(self.playersConnectedToHUD, ply)
|
|
end)
|
|
|
|
hook.add('ClientInitialized', 'vehicle_clientinitialized', function(ply)
|
|
table.insert(self.initializedPlayers, ply)
|
|
|
|
if self.initialized then
|
|
net.start('VEHICLE_READY')
|
|
net.send(ply, true)
|
|
end
|
|
end)
|
|
else
|
|
--@include ./hud.txt
|
|
require('./hud.txt')
|
|
|
|
net.receive('VEHICLE_READY', function()
|
|
self:start()
|
|
end)
|
|
end
|
|
end
|
|
|
|
---@return nil
|
|
function Vehicle:start()
|
|
self:createComponents()
|
|
self:linkComponents()
|
|
|
|
if SERVER then
|
|
self:createIO()
|
|
end
|
|
|
|
self.initialized = true
|
|
|
|
if SERVER then
|
|
net.start('VEHICLE_READY')
|
|
net.send(self.initializedPlayers, true)
|
|
end
|
|
end
|
|
|
|
---@param config any
|
|
---@return boolean
|
|
function Vehicle:validateConfig(config)
|
|
return type(config) == 'table'
|
|
end
|
|
|
|
---@param name string
|
|
---@return PowertrainComponent
|
|
function Vehicle:getComponentByName(name)
|
|
return table.find(self.components, function(component)
|
|
return component.name == name
|
|
end)
|
|
end
|
|
|
|
-- ---@param type string
|
|
-- ---@return PowertrainComponent[]
|
|
-- function Vehicle:getComponentsByType(type)
|
|
-- return table.find(self.components, function(component)
|
|
-- return component.name == name
|
|
-- end)
|
|
-- end
|
|
|
|
---@return nil
|
|
function Vehicle:createComponents()
|
|
for _, componentConfig in pairs(self.config) do
|
|
local component = PowertrainComponentFactory:create(self, componentConfig.Type, componentConfig.Name, componentConfig.Config)
|
|
|
|
table.insert(self.components, component)
|
|
end
|
|
end
|
|
|
|
---@return nil
|
|
function Vehicle:linkComponents()
|
|
for _, componentConfig in pairs(self.config) do
|
|
local component = self:getComponentByName(componentConfig.Name)
|
|
|
|
if componentConfig.Input == nil then
|
|
table.insert(self.headComponents, component)
|
|
else
|
|
local inputComponent = self:getComponentByName(componentConfig.Input)
|
|
|
|
if inputComponent ~= nil then
|
|
inputComponent:linkComponent(component)
|
|
end
|
|
end
|
|
end
|
|
|
|
if SERVER then
|
|
print(Color(0, 255, 0), 'Powertrain tree:')
|
|
for _, component in pairs(self.headComponents) do
|
|
PrintTree(component)
|
|
print(' ')
|
|
end
|
|
end
|
|
end
|
|
|
|
---@return nil
|
|
function Vehicle:createIO()
|
|
local inputs = {
|
|
Base = 'entity',
|
|
Steer = 'number',
|
|
Brake = 'number',
|
|
Handbrake = 'number',
|
|
}
|
|
local outputs = {}
|
|
|
|
for _, component in ipairs(self.components) do
|
|
inputs = table.merge(inputs, component.wireInputs)
|
|
outputs = table.merge(outputs, component.wireOutputs)
|
|
end
|
|
|
|
wire.adjustPorts(inputs, outputs)
|
|
|
|
for _, component in ipairs(self.components) do
|
|
component:onWirePortsReady()
|
|
end
|
|
end
|
|
|
|
---@return nil
|
|
function Vehicle:handleWireInput(name, value)
|
|
if name == 'Base' then
|
|
self.base = value
|
|
self.basePhysObject = isValid(value) and value:getPhysicsObject() or nil
|
|
elseif name == 'Steer' then
|
|
self.steer = value
|
|
elseif name == 'Brake' then
|
|
self.brake = value
|
|
elseif name == 'Handbrake' then
|
|
self.handbrake = value
|
|
end
|
|
|
|
if not self.initialized and self.base ~= nil and self.basePhysObject ~= nil then
|
|
self:start()
|
|
end
|
|
end
|
|
|
|
---@return nil
|
|
function Vehicle:update()
|
|
if SERVER then
|
|
local base = wire.ports.Base
|
|
|
|
for _, component in pairs(self.headComponents) do
|
|
component:forwardStep(0, 0)
|
|
end
|
|
|
|
for _, component in pairs(self.components) do
|
|
component:updateWireOutputs()
|
|
end
|
|
|
|
-- net.start("CAR_SPEED")
|
|
-- net.writeUInt(base:getVelocity():getLength()* 1.905 / 100000 * 3600, 12)
|
|
-- net.send(self.playersConnectedToHUD, true)
|
|
end
|
|
|
|
--if isValid(base) and (self.clutch:getPress() == 1 or self.gearbox.ratio == 0) then
|
|
-- local tiltForce = self.engine.torque * (-1 + self.engine.masterThrottle * 2)
|
|
--
|
|
-- base:applyForceOffset(base:getUp() * tiltForce, base:getMassCenter() + base:getRight() * UNITS_PER_METER)
|
|
-- base:applyForceOffset(-base:getUp() * tiltForce, base:getMassCenter() - base:getRight() * UNITS_PER_METER)
|
|
--end
|
|
end
|
|
|
|
---@return nil
|
|
function PrintTree(root)
|
|
if root == nil then
|
|
return
|
|
end
|
|
|
|
local lines = { { Color(255, 255, 255), root.name } }
|
|
local subtreeLines = PrintSubtree(root, '')
|
|
for _, line in ipairs(subtreeLines) do
|
|
table.insert(lines, line)
|
|
end
|
|
|
|
for _, line in ipairs(lines) do
|
|
print(unpack(line))
|
|
end
|
|
end
|
|
|
|
---@return (string | Color)[]
|
|
function PrintSubtree(root, prefix)
|
|
if root == nil then
|
|
return {}
|
|
end
|
|
|
|
---@type (string | Color)[]
|
|
local lines = {}
|
|
|
|
local left = root.outputB
|
|
local right = root.output or root.outputA
|
|
|
|
local hasLeft = left ~= nil
|
|
local hasRight = right ~= nil
|
|
|
|
if not hasLeft and not hasRight then
|
|
return lines
|
|
end
|
|
|
|
if hasRight then
|
|
local printStrand = hasLeft and (right.output ~= nil or right.outputA ~= nil or right.outputB ~= nil)
|
|
local newPrefix = prefix .. (printStrand and '│ \t' or '\t\t')
|
|
|
|
table.insert(
|
|
lines,
|
|
{ Color(80, 80, 80), prefix .. (hasLeft and '├── ' or '└── '), Color(255, 255, 255), tostring(right.name) }
|
|
)
|
|
|
|
local rightLines = PrintSubtree(right, newPrefix)
|
|
for _, line in ipairs(rightLines) do
|
|
table.insert(lines, line)
|
|
end
|
|
end
|
|
|
|
if hasLeft then
|
|
table.insert(lines, { Color(80, 80, 80), (hasRight and prefix or '') .. '└── ', Color(255, 255, 255), tostring(left.name) })
|
|
|
|
local leftLines = PrintSubtree(left, prefix .. '\t\t')
|
|
for _, line in ipairs(leftLines) do
|
|
table.insert(lines, line)
|
|
end
|
|
end
|
|
|
|
return lines
|
|
end
|
|
|
|
return {
|
|
Vehicle,
|
|
POWERTRAIN_COMPONENT,
|
|
}
|