2025-05-16 07:11:48 +06:00

233 lines
5.7 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 { [string]: any }
---@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
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.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)
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
print('VEHICLE READY')
if SERVER then
net.start('VEHICLE_READY')
net.send(find.allPlayers(), 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
for _, component in pairs(self.headComponents) do
self:printComponentTree(component)
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)
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
---@param component PowertrainComponent
---@param result? string
---@return nil
function Vehicle:printComponentTree(component, result)
-- result = result or component.name
-- if component.output then
-- result = result .. component.name
-- return self:printComponentTree(component.output, result .. ' -> ')
-- else
-- print(result)
-- end
end
return {
Vehicle,
POWERTRAIN_COMPONENT,
}