Merge remote-tracking branch 'origin/master'
# Conflicts: # koptilnya/engine_remastered/clutch.txt # koptilnya/engine_remastered/differential.txt # koptilnya/engine_remastered/engine.txt # koptilnya/engine_remastered/gearboxes/base.txt # koptilnya/engine_remastered/novojiloff_diff.txt
This commit is contained in:
commit
1cc473d6ac
@ -1 +1,3 @@
|
|||||||
Не забудьте установить форматтер ```koihik.vscode-lua-format```
|
Не забудьте установить форматтер ```koihik.vscode-lua-format```
|
||||||
|
<br>
|
||||||
|
Для WebStorm нужно поставить ```EmmyLua```
|
||||||
|
|||||||
5
koptilnya/.idea/.gitignore
generated
vendored
Normal file
5
koptilnya/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
12
koptilnya/.idea/koptilnya.iml
generated
Normal file
12
koptilnya/.idea/koptilnya.iml
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
8
koptilnya/.idea/modules.xml
generated
Normal file
8
koptilnya/.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/koptilnya.iml" filepath="$PROJECT_DIR$/.idea/koptilnya.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
koptilnya/.idea/vcs.xml
generated
Normal file
6
koptilnya/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
28
koptilnya/better_steering/config/sample.txt
Normal file
28
koptilnya/better_steering/config/sample.txt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
-- @name Better steering sample config
|
||||||
|
-- @author Koptilnya1337
|
||||||
|
-- @server
|
||||||
|
-- @include /koptilnya/better_steering/steering.txt
|
||||||
|
require('/koptilnya/better_steering/steering.txt')
|
||||||
|
|
||||||
|
Steering:new({
|
||||||
|
Sensitivity = 0.04,
|
||||||
|
PowerSteer = 0.5,
|
||||||
|
Axles = {{
|
||||||
|
Lock = 30,
|
||||||
|
Camber = -2,
|
||||||
|
Caster = 8,
|
||||||
|
Ackermann = 15.43,
|
||||||
|
Toe = 0,
|
||||||
|
LeftOffset = 180,
|
||||||
|
RightOffset = 180
|
||||||
|
}, {
|
||||||
|
Lock = 0,
|
||||||
|
Camber = -2,
|
||||||
|
Caster = 0,
|
||||||
|
Ackermann = 0,
|
||||||
|
Toe = 0,
|
||||||
|
LeftOffset = 180,
|
||||||
|
RightOffset = 180,
|
||||||
|
Direction = -1
|
||||||
|
}}
|
||||||
|
})
|
||||||
105
koptilnya/better_steering/slave.txt
Normal file
105
koptilnya/better_steering/slave.txt
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
-- @include /koptilnya/libs/wire_component.txt
|
||||||
|
-- @include /koptilnya/libs/constants.txt
|
||||||
|
-- @include /koptilnya/libs/entity.txt
|
||||||
|
require('/koptilnya/libs/wire_component.txt')
|
||||||
|
require('/koptilnya/libs/constants.txt')
|
||||||
|
require('/koptilnya/libs/entity.txt')
|
||||||
|
|
||||||
|
Slave = class('Slave', WireComponent)
|
||||||
|
|
||||||
|
function Slave:initialize(options)
|
||||||
|
options = options or {}
|
||||||
|
|
||||||
|
self.lock = options.Lock or 0
|
||||||
|
self.camber = options.Camber or 0
|
||||||
|
self.caster = options.Caster or 0
|
||||||
|
self.ackermann = options.Ackermann or 0
|
||||||
|
self.toe = options.Toe or 0
|
||||||
|
self.offset = options.Offset or 0
|
||||||
|
self.isRight = options.IsRight or false
|
||||||
|
self.direction = options.Direction or 1
|
||||||
|
|
||||||
|
self.order = options.Order or 0
|
||||||
|
|
||||||
|
self.entity = options.Slave
|
||||||
|
self.wheel = options.Wheel
|
||||||
|
self.base = options.Base or nil
|
||||||
|
|
||||||
|
self.steer = 0
|
||||||
|
self.steeringAngle = 0
|
||||||
|
self.lateralVel = 0
|
||||||
|
self.baseLngVel = 0
|
||||||
|
self.lateralForce = 0
|
||||||
|
self.selfAligningTorque = 0
|
||||||
|
self.correctingAngle = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function Slave:getOutputs()
|
||||||
|
local outputs = {}
|
||||||
|
|
||||||
|
outputs['SL_' .. self.order .. '_LateralForce'] = 'number'
|
||||||
|
outputs['SL_' .. self.order .. '_SelfAligningTq'] = 'number'
|
||||||
|
outputs['SL_' .. self.order .. '_CorrectingAngle'] = 'number'
|
||||||
|
outputs['W_' .. self.order .. '_LateralVel'] = 'number'
|
||||||
|
outputs['SL_' .. self.order .. '_SteeringAngle'] = 'number'
|
||||||
|
outputs['SL_' .. self.order .. '_BaseLngVel'] = 'number'
|
||||||
|
|
||||||
|
return outputs
|
||||||
|
end
|
||||||
|
|
||||||
|
function Slave:updateOutputs()
|
||||||
|
wire.ports['SL_' .. self.order .. '_LateralForce'] = self.lateralForce
|
||||||
|
wire.ports['SL_' .. self.order .. '_SelfAligningTq'] = self.selfAligningTorque
|
||||||
|
wire.ports['SL_' .. self.order .. '_CorrectingAngle'] = self.correctingAngle
|
||||||
|
wire.ports['W_' .. self.order .. '_LateralVel'] = self.lateralVel
|
||||||
|
wire.ports['SL_' .. self.order .. '_SteeringAngle'] = self.steeringAngle
|
||||||
|
wire.ports['SL_' .. self.order .. '_BaseLngVel'] = self.baseLngVel
|
||||||
|
end
|
||||||
|
|
||||||
|
function Slave:update()
|
||||||
|
if isValid(self.entity) and isValid(self.base) and isValid(self.wheel) then
|
||||||
|
if not self.entity:isFrozen() and not self.entity:isPlayerHolding() then
|
||||||
|
self.entity:setFrozen(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local wheelMass = self.wheel:getMass()
|
||||||
|
local wheelInertia = self.wheel:getInertia():getLength()
|
||||||
|
local wheelRadius = self.wheel:getModelRadius() / UNITS_PER_METER
|
||||||
|
local wheelFriction = self.wheel:getFriction()
|
||||||
|
local wheelLocalVel = getLocalVelocity(self.wheel)
|
||||||
|
local baseLocalVel = getLocalVelocity(self.base)
|
||||||
|
local lateralVel = wheelLocalVel[2]
|
||||||
|
local baseLngVel = baseLocalVel[1]
|
||||||
|
|
||||||
|
local camber = self.isRight and -self.camber or self.camber
|
||||||
|
local steerRatio = self.lock ~= 0 and self.steeringAngle / self.lock or 0
|
||||||
|
local caster = steerRatio * self.caster
|
||||||
|
local toe = self.isRight and self.toe or -self.toe
|
||||||
|
local ackermann = 0
|
||||||
|
|
||||||
|
if steerRatio > 0 then
|
||||||
|
ackermann = self.isRight and -steerRatio * self.ackermann or steerRatio * self.ackermann
|
||||||
|
elseif steerRatio < 0 then
|
||||||
|
ackermann = self.isRight and steerRatio * self.ackermann or -steerRatio * self.ackermann
|
||||||
|
end
|
||||||
|
|
||||||
|
self.lateralForce = wheelFriction * wheelMass * lateralVel / UNITS_PER_METER
|
||||||
|
self.selfAligningTorque = self.lateralForce * wheelRadius
|
||||||
|
|
||||||
|
self.correctingAngle = self.selfAligningTorque / wheelInertia * math.sin(math.rad(math.abs(self.caster * 2))) *
|
||||||
|
math.sign(baseLngVel) * (self.isRight and -1 or 1)
|
||||||
|
|
||||||
|
local inc = self.steer * self.lock * self.direction * math.cos(math.rad(math.abs(self.caster * 2)))
|
||||||
|
self.steeringAngle = math.clamp(self.steeringAngle + inc + self.correctingAngle, -self.lock, self.lock)
|
||||||
|
|
||||||
|
-- Dividing ackermann by 2 because ackermann angle is difference between wheels angle
|
||||||
|
-- So if we don't divide it by 2, the result angle will be 2 times more than expected
|
||||||
|
local angle = Angle(0, self.offset + self.steeringAngle + toe + ackermann / 2, camber + caster)
|
||||||
|
local localizedAngle = self.base:localToWorldAngles(angle)
|
||||||
|
|
||||||
|
self.entity:setAngles(localizedAngle)
|
||||||
|
|
||||||
|
self.lateralVel = lateralVel
|
||||||
|
self.baseLngVel = baseLngVel
|
||||||
|
end
|
||||||
|
end
|
||||||
79
koptilnya/better_steering/steering.txt
Normal file
79
koptilnya/better_steering/steering.txt
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
-- @include /koptilnya/libs/wire_component.txt
|
||||||
|
-- @include ./steering_axle.txt
|
||||||
|
-- @include /koptilnya/libs/table.txt
|
||||||
|
require('/koptilnya/libs/wire_component.txt')
|
||||||
|
require('./steering_axle.txt')
|
||||||
|
require('/koptilnya/libs/table.txt')
|
||||||
|
|
||||||
|
Steering = class('Steering', WireComponent)
|
||||||
|
|
||||||
|
function Steering:initialize(options)
|
||||||
|
options = options or {}
|
||||||
|
self.axles = {}
|
||||||
|
self.steer = 0
|
||||||
|
self.sensitivity = options.Sensitivity or 1
|
||||||
|
|
||||||
|
for key, axle in ipairs(options.Axles) do
|
||||||
|
axle.Order = key
|
||||||
|
axle.Base = wire.ports.Base
|
||||||
|
table.insert(self.axles, SteeringAxle:new(axle))
|
||||||
|
end
|
||||||
|
|
||||||
|
self:_adjustPorts()
|
||||||
|
|
||||||
|
hook.add('tick', 'update', function()
|
||||||
|
self:update()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Steering:_adjustPorts()
|
||||||
|
local inputs = {}
|
||||||
|
local outputs = {}
|
||||||
|
|
||||||
|
inputs = table.merge(inputs, self:getInputs())
|
||||||
|
outputs = table.merge(outputs, self:getOutputs())
|
||||||
|
|
||||||
|
for _, axle in ipairs(self.axles) do
|
||||||
|
inputs = table.merge(inputs, axle:getInputs())
|
||||||
|
outputs = table.merge(outputs, axle:getOutputs())
|
||||||
|
end
|
||||||
|
|
||||||
|
wire.adjustPorts(inputs, outputs)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Steering:getInputs()
|
||||||
|
return {
|
||||||
|
TargetSteer = 'number',
|
||||||
|
Base = 'entity'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Steering:getOutputs()
|
||||||
|
return {
|
||||||
|
Steer = 'number'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Steering:updateOutputs()
|
||||||
|
-- local totalSteeringAngle = table.reduce(self.slaves, function(steer, slave)
|
||||||
|
-- return steer + slave.direction * slave.steeringAngle / (slave.lock ~= 0 and slave.lock or 1)
|
||||||
|
-- end, 0)
|
||||||
|
|
||||||
|
-- local steerableSlaves = table.filter(self.slaves, function(slave)
|
||||||
|
-- return slave.lock ~= 0
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- wire.ports.Steer = totalSteeringAngle / #steerableSlaves
|
||||||
|
end
|
||||||
|
|
||||||
|
function Steering:update()
|
||||||
|
self.steer = wire.ports.TargetSteer * self.sensitivity
|
||||||
|
|
||||||
|
self:updateOutputs()
|
||||||
|
|
||||||
|
for _, axle in ipairs(self.axles) do
|
||||||
|
axle.steer = self.steer
|
||||||
|
axle:update()
|
||||||
|
axle:updateOutputs()
|
||||||
|
end
|
||||||
|
end
|
||||||
69
koptilnya/better_steering/steering_axle.txt
Normal file
69
koptilnya/better_steering/steering_axle.txt
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
-- @include /koptilnya/libs/wire_component.txt
|
||||||
|
-- @include /koptilnya/libs/constants.txt
|
||||||
|
-- @include /koptilnya/libs/entity.txt
|
||||||
|
-- @include ./slave.txt
|
||||||
|
require('/koptilnya/libs/wire_component.txt')
|
||||||
|
require('/koptilnya/libs/constants.txt')
|
||||||
|
require('/koptilnya/libs/entity.txt')
|
||||||
|
require('./slave.txt')
|
||||||
|
|
||||||
|
SteeringAxle = class('SteeringAxle', WireComponent)
|
||||||
|
|
||||||
|
function SteeringAxle:createSlave(options, isRight)
|
||||||
|
local config = {
|
||||||
|
Lock = options.Lock,
|
||||||
|
Camber = options.Camber,
|
||||||
|
Caster = options.Caster,
|
||||||
|
Ackermann = options.Ackermann,
|
||||||
|
Toe = options.Toe,
|
||||||
|
Direction = options.Direction,
|
||||||
|
IsRight = isRight,
|
||||||
|
Slave = wire.ports[self._prefix .. (isRight and '_RightSlave' or '_LeftSlave')],
|
||||||
|
Wheel = wire.ports[self._prefix .. (isRight and '_RightWheel' or '_LeftWheel')],
|
||||||
|
Offset = isRight and options.RightOffset or options.LeftOffset,
|
||||||
|
Prefix = self._prefix .. (isRight and '_RightWheel' or '_LeftWheel'),
|
||||||
|
Base = wire.ports.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
return Slave:new(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SteeringAxle:initialize(options)
|
||||||
|
options = options or {}
|
||||||
|
|
||||||
|
self.steer = 0
|
||||||
|
self._prefix = 'Axle' .. options.Order
|
||||||
|
|
||||||
|
self.leftSlave = self:createSlave(options, false)
|
||||||
|
self.rightSlave = self:createSlave(options, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SteeringAxle:getInputs()
|
||||||
|
local inputs = {}
|
||||||
|
|
||||||
|
inputs[self._prefix .. '_LeftSlave'] = 'entity'
|
||||||
|
inputs[self._prefix .. '_RightSlave'] = 'entity'
|
||||||
|
inputs[self._prefix .. '_LeftWheel'] = 'entity'
|
||||||
|
inputs[self._prefix .. '_RightWheel'] = 'entity'
|
||||||
|
|
||||||
|
return inputs
|
||||||
|
end
|
||||||
|
|
||||||
|
function SteeringAxle:getOutputs()
|
||||||
|
local outputs = {}
|
||||||
|
|
||||||
|
table.merge(outputs, self.leftSlave:getOutputs())
|
||||||
|
table.merge(outputs, self.rightSlave:getOutputs())
|
||||||
|
|
||||||
|
return outputs
|
||||||
|
end
|
||||||
|
|
||||||
|
function SteeringAxle:updateOutputs()
|
||||||
|
self.leftSlave:updateOutputs()
|
||||||
|
self.rightSlave:updateOutputs()
|
||||||
|
end
|
||||||
|
|
||||||
|
function SteeringAxle:update()
|
||||||
|
self.leftSlave:update()
|
||||||
|
self.rightSlave:update()
|
||||||
|
end
|
||||||
45
koptilnya/data/models/bmw_m4_g82.txt
Normal file
45
koptilnya/data/models/bmw_m4_g82.txt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
-- @shared
|
||||||
|
-- @name BMW M4 G82 Competition
|
||||||
|
-- @author Koptilnya1337
|
||||||
|
-- @include /koptilnya/mesh_loader/builder.txt
|
||||||
|
require("/koptilnya/mesh_loader/builder.txt")
|
||||||
|
|
||||||
|
DEBUG_MODE = true
|
||||||
|
|
||||||
|
local LINK = "https://drive.google.com/u/0/uc?id=1yxjPtVhoMROwSB56tfL_dbBPrW1Yjr9h&export=download"
|
||||||
|
local SCALE = Vector(1)
|
||||||
|
|
||||||
|
local Materials =
|
||||||
|
{
|
||||||
|
Body = "phoenix_storms/fender_white",
|
||||||
|
Carbon = "sprops/textures/sprops_cfiber2"
|
||||||
|
}
|
||||||
|
|
||||||
|
local Colors =
|
||||||
|
{
|
||||||
|
Body = Color(115,218,201),
|
||||||
|
Carbon = Color(255,255,255)
|
||||||
|
}
|
||||||
|
|
||||||
|
local builder = {}
|
||||||
|
|
||||||
|
if SERVER then
|
||||||
|
builder = MeshBuilder:new(LINK)
|
||||||
|
|
||||||
|
local this = chip()
|
||||||
|
|
||||||
|
-- Body group
|
||||||
|
builder:build("Body", Vector(0), Angle(0), SCALE, Colors.Body, Materials.Body, this, this)
|
||||||
|
builder:build("LeftDoor", Vector(0), Angle(0), SCALE, Colors.Body, Materials.Body, this, this)
|
||||||
|
builder:build("RightDoor", Vector(0), Angle(0), SCALE, Colors.Body, Materials.Body, this, this)
|
||||||
|
builder:build("Bonnet", Vector(0), Angle(0), SCALE, Colors.Body, Materials.Body, this, this)
|
||||||
|
builder:build("Trunk", Vector(0), Angle(0), SCALE, Colors.Body, Materials.Body, this, this)
|
||||||
|
builder:build("Roof", Vector(0), Angle(0), SCALE, Colors.Carbon, Materials.Carbon, this, this)
|
||||||
|
|
||||||
|
local result = builder:getResult()
|
||||||
|
else
|
||||||
|
PERMA.onPermissionsGained = function()
|
||||||
|
builder = MeshBuilder:new(LINK)
|
||||||
|
end
|
||||||
|
PERMA.build()
|
||||||
|
end
|
||||||
151
koptilnya/data/models/vw_golf_mk7_5.txt
Normal file
151
koptilnya/data/models/vw_golf_mk7_5.txt
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
-- @shared
|
||||||
|
-- @name VW Golf Mk 7.5
|
||||||
|
-- @author Koptilnya1337
|
||||||
|
-- @include /koptilnya/mesh_loader/builder.txt
|
||||||
|
require("/koptilnya/mesh_loader/builder.txt")
|
||||||
|
|
||||||
|
DEBUG_MODE = true
|
||||||
|
|
||||||
|
local LINK = "https://drive.google.com/u/0/uc?id=1FNn5_6PMmurvraVqlJNNV3WXTMnozMJM&export=download"
|
||||||
|
local SCALE = Vector(1)
|
||||||
|
|
||||||
|
local Materials = {
|
||||||
|
{
|
||||||
|
Match = "Shadow",
|
||||||
|
Material = "",
|
||||||
|
Color = Color(20,20,20)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = "BumpersVents",
|
||||||
|
Material = "models/debug/debugwhite",
|
||||||
|
Color = Color(20,20,20)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = { "Body", ".*Body*" },
|
||||||
|
Material = "phoenix_storms/fender_white",
|
||||||
|
Color = Color(230,229,231)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = "Badges",
|
||||||
|
Material = "debug/env_cubemap_model",
|
||||||
|
Color = Color(255,255,255)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = ".*Lights_Glass*",
|
||||||
|
Material = "phoenix_storms/mrref2",
|
||||||
|
Color = Color(255,255,255,120)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = { ".*Lights_Base*", "RearLights_Chrome" },
|
||||||
|
Material = "models/debug/debugwhite",
|
||||||
|
Color = Color(20,20,20)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = { "WindowsRubber", "WindshieldOutline" },
|
||||||
|
Material = "phoenix_storms/mrref2",
|
||||||
|
Color = Color(25,25,25)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = ".*Seat.*",
|
||||||
|
Material = "models/debug/debugwhite",
|
||||||
|
Color = Color(60,60,60)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = "Interior_DoorMaps",
|
||||||
|
Material = "models/debug/debugwhite",
|
||||||
|
Color = Color(40,40,40)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match = "Interior_Plastic",
|
||||||
|
Material = "models/debug/debugwhite",
|
||||||
|
Color = Color(50,50,50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local Bodyparts = {
|
||||||
|
"Body",
|
||||||
|
"Bonnet_Body",
|
||||||
|
"DoorHandles",
|
||||||
|
"BumpersVents",
|
||||||
|
"ExhaustPipes",
|
||||||
|
"ExhaustPipes",
|
||||||
|
"FrontBumper_Body",
|
||||||
|
"FrontLeftDoor_Body",
|
||||||
|
"FrontQuaterpanels_Body",
|
||||||
|
"FrontRightDoor_Body",
|
||||||
|
"FrontRightDoor_Body",
|
||||||
|
"Mirrors_Plastic",
|
||||||
|
"RearBumper_Body",
|
||||||
|
"RearLeftDoor_Body",
|
||||||
|
"RearRightDoor_Body",
|
||||||
|
"RearRightDoor_Body",
|
||||||
|
"Shadow",
|
||||||
|
"Sidepanels_Body",
|
||||||
|
"Sideskirts_Body",
|
||||||
|
"Trunk_Body",
|
||||||
|
}
|
||||||
|
|
||||||
|
local Details = {
|
||||||
|
"Badges",
|
||||||
|
}
|
||||||
|
|
||||||
|
local FrontLights = {
|
||||||
|
"FrontLights_Base",
|
||||||
|
-- "FrontLights_Glass",
|
||||||
|
}
|
||||||
|
|
||||||
|
local RearLights = {
|
||||||
|
"RearLights",
|
||||||
|
"RearLights_Base",
|
||||||
|
"RearLights_Chrome",
|
||||||
|
-- "RearLights_Glass",
|
||||||
|
}
|
||||||
|
|
||||||
|
local Windows = {
|
||||||
|
"WindowsRubber",
|
||||||
|
"WindshieldOutline",
|
||||||
|
}
|
||||||
|
|
||||||
|
local Interior = {
|
||||||
|
"FrontLeftSeat",
|
||||||
|
"FrontRightSeat",
|
||||||
|
"RearSeats",
|
||||||
|
-- "Interior_Decals",
|
||||||
|
"Interior_DoorMaps",
|
||||||
|
"Interior_Plastic",
|
||||||
|
"SoundDynamics",
|
||||||
|
"SteeringWheel",
|
||||||
|
"SteeringWheel_Badge",
|
||||||
|
-- "SteeringWheel_Decals",
|
||||||
|
"SteeringWheel_Plastic",
|
||||||
|
}
|
||||||
|
|
||||||
|
local Meshes = {}
|
||||||
|
|
||||||
|
table.add(Meshes, Bodyparts)
|
||||||
|
table.add(Meshes, Details)
|
||||||
|
table.add(Meshes, FrontLights)
|
||||||
|
table.add(Meshes, RearLights)
|
||||||
|
table.add(Meshes, Windows)
|
||||||
|
table.add(Meshes, Interior)
|
||||||
|
|
||||||
|
local builder = {}
|
||||||
|
|
||||||
|
if SERVER then
|
||||||
|
local this = chip()
|
||||||
|
|
||||||
|
builder = MeshBuilder:new(LINK)
|
||||||
|
:useGlobalScale(SCALE)
|
||||||
|
:useGlobalParent(this)
|
||||||
|
:useGlobalRelative(this)
|
||||||
|
|
||||||
|
builder:buildAll(Meshes)
|
||||||
|
builder:getResult()
|
||||||
|
|
||||||
|
else
|
||||||
|
PERMA.onPermissionsGained = function()
|
||||||
|
builder = MeshBuilder:new(LINK)
|
||||||
|
builder:useMaterialPipeline(Materials)
|
||||||
|
end
|
||||||
|
PERMA.build()
|
||||||
|
end
|
||||||
@ -26,7 +26,8 @@ Vehicle:new({
|
|||||||
Clutch = {Stiffness = 20, Damping = 0.5, MaxTorque = 400},
|
Clutch = {Stiffness = 20, Damping = 0.5, MaxTorque = 400},
|
||||||
Gearbox = {Type = 'MANUAL', ShiftDuration = 0.2, ShiftSmoothness = 0.3, Ratios = {3.321, 1.902, 1.308, 1, 0.838}, Reverse = 3.382},
|
Gearbox = {Type = 'MANUAL', ShiftDuration = 0.2, ShiftSmoothness = 0.3, Ratios = {3.321, 1.902, 1.308, 1, 0.838}, Reverse = 3.382},
|
||||||
Axles = {
|
Axles = {
|
||||||
{Power = 0.96, Coast = 0.96, Preload = 10, UsePowerBias = 10, ViscousCoeff = 0.96, Axle = Vector(1, 0, 0), DistributionCoeff = 1, FinalDrive = 3.9}
|
{Power = 0.96, Coast = 0.96, Preload = 10, UsePowerBias = 10, ViscousCoeff = 0.96, Axle = Vector(1, 0, 0), DistributionCoeff = 0.8, FinalDrive = 3.9},
|
||||||
|
{Power = 0.96, Coast = 0.96, Preload = 10, UsePowerBias = 10, ViscousCoeff = 0.96, Axle = Vector(1, 0, 0), DistributionCoeff = 1.2, FinalDrive = 3.9},
|
||||||
},
|
},
|
||||||
Systems = {{Type = 'LAUNCH', Limit = 3500}, {Type = 'TRACTION', Limit = 1.5}}
|
Systems = {{Type = 'LAUNCH', Limit = 3500}, {Type = 'TRACTION', Limit = 1.5}}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,188 +0,0 @@
|
|||||||
-- @name Grip Steering
|
|
||||||
-- @author DarkSupah
|
|
||||||
-- @server
|
|
||||||
-- @include ./slave.txt
|
|
||||||
Steering = class('Steering')
|
|
||||||
|
|
||||||
require('./slave.txt')
|
|
||||||
|
|
||||||
function inrange(val, min, max)
|
|
||||||
return val >= min and val <= max
|
|
||||||
end
|
|
||||||
|
|
||||||
function isNaN(val)
|
|
||||||
return val ~= val
|
|
||||||
end
|
|
||||||
|
|
||||||
function vectorIsNaN(vec)
|
|
||||||
return isNaN(vec[1]) or isNaN(vec[2]) or isNaN(vec[3])
|
|
||||||
end
|
|
||||||
|
|
||||||
function getLocalVelocity(ent)
|
|
||||||
return ent:worldToLocal((ent:getVelocity() + ent:getPos()))
|
|
||||||
end
|
|
||||||
|
|
||||||
local options = {
|
|
||||||
Camber = -2,
|
|
||||||
Caster = 5,
|
|
||||||
Ackermann = 15,
|
|
||||||
Lock = 45,
|
|
||||||
|
|
||||||
ZeroThreshold = 2,
|
|
||||||
|
|
||||||
StartSpeedThreshold = 40,
|
|
||||||
EndSpeedThreshold = 150,
|
|
||||||
SpeedCoeff = 0.2,
|
|
||||||
|
|
||||||
StartCorrectionThreshold = 5,
|
|
||||||
EndCorrectionThreshold = 40,
|
|
||||||
CorrectionOnCoeff = 0.6,
|
|
||||||
CorrectionOffCoeff = 1,
|
|
||||||
CorrectionOnLerp = 0.3,
|
|
||||||
CorrectionOffLerp = 0.6
|
|
||||||
}
|
|
||||||
|
|
||||||
function Steering:_adjustPorts()
|
|
||||||
local inputs = {
|
|
||||||
Axles = 'table',
|
|
||||||
Base = 'entity',
|
|
||||||
LeftSlave = 'entity',
|
|
||||||
RightSlave = 'entity'
|
|
||||||
}
|
|
||||||
|
|
||||||
local outputs = {
|
|
||||||
SteerAngle = 'number',
|
|
||||||
TargetAngle = 'number',
|
|
||||||
CorrectionAngle = 'number'
|
|
||||||
}
|
|
||||||
|
|
||||||
wire.adjustPorts(inputs, outputs)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Steering:_createSlaves()
|
|
||||||
local entities = {wire.ports.LeftSlave, wire.ports.RightSlave}
|
|
||||||
local slaves = {}
|
|
||||||
|
|
||||||
for k, v in pairs(entities) do
|
|
||||||
local slave = Slave:new({
|
|
||||||
Entity = v,
|
|
||||||
IsLeft = k % 2 == 1,
|
|
||||||
Offset = 180,
|
|
||||||
Base = wire.ports.Base,
|
|
||||||
Camber = self.slavesConfig.Camber,
|
|
||||||
Caster = self.slavesConfig.Caster,
|
|
||||||
Ackermann = self.slavesConfig.Ackermann
|
|
||||||
})
|
|
||||||
|
|
||||||
table.insert(self.slaves, slave)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Steering:_updateOutputs()
|
|
||||||
wire.ports.TargetAngle = self.targetAngle
|
|
||||||
wire.ports.SteerAngle = self.angle
|
|
||||||
wire.ports.CorrectionAngle = self.correction
|
|
||||||
end
|
|
||||||
|
|
||||||
function Steering:_getCorrectionGradient(speed)
|
|
||||||
return math.clamp(math.remap(speed, self.startCorrectionThreshold, self.endCorrectionThreshold, 0, 1), 0, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Steering:getBaseSpeed()
|
|
||||||
local MPH = self.base:getVelocity():getLength() * 3600 / 63360
|
|
||||||
local KPH = MPH * 1.609
|
|
||||||
|
|
||||||
return KPH
|
|
||||||
end
|
|
||||||
|
|
||||||
function Steering:_getSpeedCorrectionGradient(speed)
|
|
||||||
return math.clamp(math.remap(speed, self.startSpeedThreshold, self.endSpeedThreshold, 0, 1), 0, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Steering:_getCorrectionAngle()
|
|
||||||
local localVel = getLocalVelocity(self.base)
|
|
||||||
local forwardVel = (localVel * Vector(1, 0, 0)):getNormalized()
|
|
||||||
local planarVec = (localVel * Vector(1, 1, 0)):getNormalized()
|
|
||||||
|
|
||||||
forwardVel = vectorIsNaN(forwardVel) and Vector(0, 0, 0) or forwardVel
|
|
||||||
planarVec = vectorIsNaN(planarVec) and Vector(0, 0, 0) or planarVec
|
|
||||||
|
|
||||||
local cross = forwardVel:cross(planarVec)
|
|
||||||
|
|
||||||
return math.deg(math.acos(forwardVel:dot(planarVec))) * math.sign(localVel[2])
|
|
||||||
end
|
|
||||||
|
|
||||||
function Steering:initialize(options)
|
|
||||||
self:_adjustPorts()
|
|
||||||
|
|
||||||
options = options or {}
|
|
||||||
|
|
||||||
self.base = wire.ports.Base
|
|
||||||
|
|
||||||
self.slavesConfig = {
|
|
||||||
Camber = options.Camber,
|
|
||||||
Caster = options.Caster,
|
|
||||||
Ackermann = options.Ackermann
|
|
||||||
}
|
|
||||||
|
|
||||||
self.lock = options.Lock
|
|
||||||
|
|
||||||
self.targetAngle = 0
|
|
||||||
self.angle = 0
|
|
||||||
|
|
||||||
self.zeroThreshold = options.ZeroThreshold
|
|
||||||
|
|
||||||
self.speedCoeff = options.SpeedCoeff
|
|
||||||
self.startSpeedThreshold = options.StartSpeedThreshold
|
|
||||||
self.endSpeedThreshold = options.EndSpeedThreshold
|
|
||||||
|
|
||||||
self.startCorrectionThreshold = options.StartCorrectionThreshold
|
|
||||||
self.endCorrectionThreshold = options.EndCorrectionThreshold
|
|
||||||
self.correctionOnCoeff = options.CorrectionOnCoeff
|
|
||||||
self.correctionOffCoeff = options.CorrectionOffCoeff
|
|
||||||
|
|
||||||
self.correctionOnLerp = options.CorrectionOnLerp
|
|
||||||
self.correctionOffLerp = options.CorrectionOffLerp
|
|
||||||
|
|
||||||
self.correction = 0
|
|
||||||
|
|
||||||
self.slaves = {}
|
|
||||||
|
|
||||||
self:_createSlaves()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Steering:update()
|
|
||||||
local horizontal = wire.ports.Axles.Horizontal.Value
|
|
||||||
local horizontalTarget = wire.ports.Axles.Horizontal.Target
|
|
||||||
|
|
||||||
local baseSpeedKMH = self:getBaseSpeed()
|
|
||||||
local angleCorrection = self:_getCorrectionGradient(baseSpeedKMH)
|
|
||||||
local speedCorrection = self.angle * self:_getSpeedCorrectionGradient(baseSpeedKMH) * self.speedCoeff
|
|
||||||
|
|
||||||
self.targetAngle = horizontal * self.lock
|
|
||||||
|
|
||||||
local correctionCoeff = horizontalTarget ~= 0 and self.correctionOnCoeff or self.correctionOffCoeff
|
|
||||||
local correctionLerpCoeff = horizontalTarget ~= 0 and self.correctionOnLerp or self.correctionOffLerp
|
|
||||||
local correction = self:_getCorrectionAngle() * angleCorrection * correctionCoeff
|
|
||||||
self.correction = math.lerp(correctionLerpCoeff, self.correction, isNaN(correction) and 0 or correction)
|
|
||||||
|
|
||||||
local possibleAngle = math.clamp(horizontal * self.lock + speedCorrection + self.correction, -self.lock, self.lock)
|
|
||||||
|
|
||||||
if inrange(possibleAngle, -self.zeroThreshold, self.zeroThreshold) then
|
|
||||||
self.angle = 0
|
|
||||||
else
|
|
||||||
self.angle = possibleAngle
|
|
||||||
end
|
|
||||||
|
|
||||||
for k, v in pairs(self.slaves) do
|
|
||||||
v:rotate(self.angle)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:_updateOutputs()
|
|
||||||
end
|
|
||||||
|
|
||||||
local steering = Steering:new(options)
|
|
||||||
|
|
||||||
hook.add('think', 'SteeringUpdate', function()
|
|
||||||
steering:update()
|
|
||||||
end)
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
Slave = class('Slave')
|
|
||||||
|
|
||||||
function Slave:initialize(options)
|
|
||||||
options = options or {}
|
|
||||||
|
|
||||||
self.entity = options.Entity
|
|
||||||
self.base = options.Base
|
|
||||||
|
|
||||||
self.camber = options.Camber
|
|
||||||
self.caster = options.Caster
|
|
||||||
self.ackermann = options.Ackermann
|
|
||||||
self.isLeft = options.IsLeft
|
|
||||||
self.offset = options.Offset
|
|
||||||
end
|
|
||||||
|
|
||||||
function Slave:rotate(ang)
|
|
||||||
if self.entity:isFrozen() == false then
|
|
||||||
self.entity:setFrozen(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ackermann = math.sin(math.rad(ang)) ^ 2 * self.ackermann * (self.isLeft and -1 or 1)
|
|
||||||
local yaw = self.offset + ang + ackermann
|
|
||||||
local roll = (self.isLeft and 1 or -1) * self.camber - ang / 270 * self.caster
|
|
||||||
|
|
||||||
local angle = Angle(0, yaw, roll)
|
|
||||||
local transformedAngle = self.base:localToWorldAngles(angle)
|
|
||||||
|
|
||||||
self.entity:setAngles(transformedAngle)
|
|
||||||
end
|
|
||||||
@ -2,8 +2,8 @@ Axles = {
|
|||||||
Horizontal = {
|
Horizontal = {
|
||||||
Negative = KEY.D,
|
Negative = KEY.D,
|
||||||
Positive = KEY.A,
|
Positive = KEY.A,
|
||||||
Gravity = 0.1,
|
Gravity = 1,
|
||||||
Sensitivity = 0.2
|
Sensitivity = 1
|
||||||
},
|
},
|
||||||
Throttle = {
|
Throttle = {
|
||||||
Positive = KEY.W,
|
Positive = KEY.W,
|
||||||
@ -15,10 +15,15 @@ Axles = {
|
|||||||
Gravity = 0.15,
|
Gravity = 0.15,
|
||||||
Sensitivity = 0.25
|
Sensitivity = 0.25
|
||||||
},
|
},
|
||||||
|
Brakes = {
|
||||||
|
Positive = KEY.S,
|
||||||
|
Gravity = 0.15,
|
||||||
|
Sensitivity = 0.25
|
||||||
|
},
|
||||||
Shifter = {
|
Shifter = {
|
||||||
Positive = MOUSE.LEFT,
|
Positive = MOUSE.LEFT,
|
||||||
Negative = MOUSE.RIGHT,
|
Negative = MOUSE.RIGHT,
|
||||||
Gravity = 1,
|
Gravity = 1,
|
||||||
Sensitivity = 1
|
Sensitivity = 1
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
100
koptilnya/suspension/main.txt
Normal file
100
koptilnya/suspension/main.txt
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
--@name apply force suspension
|
||||||
|
--@author Koptilnya1337
|
||||||
|
--@shared
|
||||||
|
|
||||||
|
local _restLength = 26.0
|
||||||
|
local _springTravel = 20.0
|
||||||
|
local _springStiffness = 45000
|
||||||
|
local _compressionDamper = 250000 -- 45000
|
||||||
|
local _reboundDamper = 150000 -- 196000
|
||||||
|
local _maxForce = 800000
|
||||||
|
|
||||||
|
local _minLength = 0
|
||||||
|
local _maxLength = 0
|
||||||
|
local _lastLength = 0
|
||||||
|
local _springLength = 0
|
||||||
|
local _springVelocity = 0
|
||||||
|
local _springForce = 0
|
||||||
|
local _damperForce = 0
|
||||||
|
|
||||||
|
if SERVER then
|
||||||
|
wire.adjustInputs(
|
||||||
|
{
|
||||||
|
'Wheel1',
|
||||||
|
'Wheel2',
|
||||||
|
'Wheel3',
|
||||||
|
'Wheel4',
|
||||||
|
'Strut1',
|
||||||
|
'Strut2',
|
||||||
|
'Strut3',
|
||||||
|
'Strut4',
|
||||||
|
'Base'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'entity',
|
||||||
|
'entity',
|
||||||
|
'entity',
|
||||||
|
'entity',
|
||||||
|
'entity',
|
||||||
|
'entity',
|
||||||
|
'entity',
|
||||||
|
'entity',
|
||||||
|
'entity'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
local wheel1 = wire.ports.Wheel1
|
||||||
|
local wheel2 = wire.ports.Wheel2
|
||||||
|
local wheel3 = wire.ports.Wheel3
|
||||||
|
local wheel4 = wire.ports.Wheel4
|
||||||
|
|
||||||
|
local strut1 = wire.ports.Strut1
|
||||||
|
local strut2 = wire.ports.Strut2
|
||||||
|
local strut3 = wire.ports.Strut3
|
||||||
|
local strut4 = wire.ports.Strut4
|
||||||
|
|
||||||
|
local wheels = { wheel1, wheel2, wheel3, wheel4 }
|
||||||
|
local struts = { strut1, strut2, strut3, strut4 }
|
||||||
|
|
||||||
|
local base = wire.ports.Base
|
||||||
|
|
||||||
|
function handleWheel(base, wheel, strut)
|
||||||
|
local lastLength = 0
|
||||||
|
|
||||||
|
return function ()
|
||||||
|
if not base:isValid() then return end
|
||||||
|
if not wheel:isValid() then return end
|
||||||
|
if not strut:isValid() then return end
|
||||||
|
|
||||||
|
local distance = strut:getPos():getDistance(wheel:getPos()) - _restLength
|
||||||
|
local strutToWheelDirection = (strut:getPos() - wheel:getPos()):setX(0):setY(0):getNormalized()
|
||||||
|
local springVelocity = (distance - lastLength)
|
||||||
|
|
||||||
|
local usedDamper = springVelocity > 0 and _reboundDamper or _compressionDamper
|
||||||
|
local spring = distance * _springStiffness
|
||||||
|
local dampener = springVelocity * usedDamper
|
||||||
|
|
||||||
|
local force = math.clamp((spring + dampener) * game.getTickInterval(), -_maxForce, _maxForce)
|
||||||
|
local direction = (strut:getPos() - wheel:getPos()):getNormalized()
|
||||||
|
local baseDirection = direction * Vector(0, 0, math.clamp(base:getUp().z, 0, 1))
|
||||||
|
|
||||||
|
lastLength = distance
|
||||||
|
|
||||||
|
base:applyForceOffset(-force * baseDirection, wheel:getPos())
|
||||||
|
wheel:applyForceCenter(force * direction)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local wheelHandlers = {}
|
||||||
|
|
||||||
|
for idx, wheel in ipairs(wheels) do
|
||||||
|
table.insert(wheelHandlers, handleWheel(base, wheel, struts[idx]))
|
||||||
|
end
|
||||||
|
|
||||||
|
hook.add('tick', 'runtime', function()
|
||||||
|
for _, handler in ipairs(wheelHandlers) do
|
||||||
|
handler()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
end
|
||||||
Loading…
x
Reference in New Issue
Block a user