diff --git a/README.md b/README.md
index 8c207c7..1119fc4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
Не забудьте установить форматтер ```koihik.vscode-lua-format```
+
+Для WebStorm нужно поставить ```EmmyLua```
diff --git a/koptilnya/.idea/.gitignore b/koptilnya/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/koptilnya/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/koptilnya/.idea/koptilnya.iml b/koptilnya/.idea/koptilnya.iml
new file mode 100644
index 0000000..0c8867d
--- /dev/null
+++ b/koptilnya/.idea/koptilnya.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/koptilnya/.idea/modules.xml b/koptilnya/.idea/modules.xml
new file mode 100644
index 0000000..22e25db
--- /dev/null
+++ b/koptilnya/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/koptilnya/.idea/vcs.xml b/koptilnya/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/koptilnya/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/koptilnya/better_steering/config/sample.txt b/koptilnya/better_steering/config/sample.txt
new file mode 100644
index 0000000..f88cba4
--- /dev/null
+++ b/koptilnya/better_steering/config/sample.txt
@@ -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
+ }}
+})
diff --git a/koptilnya/better_steering/slave.txt b/koptilnya/better_steering/slave.txt
new file mode 100644
index 0000000..f219ebc
--- /dev/null
+++ b/koptilnya/better_steering/slave.txt
@@ -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
diff --git a/koptilnya/better_steering/steering.txt b/koptilnya/better_steering/steering.txt
new file mode 100644
index 0000000..fd682d6
--- /dev/null
+++ b/koptilnya/better_steering/steering.txt
@@ -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
diff --git a/koptilnya/better_steering/steering_axle.txt b/koptilnya/better_steering/steering_axle.txt
new file mode 100644
index 0000000..7f43432
--- /dev/null
+++ b/koptilnya/better_steering/steering_axle.txt
@@ -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
\ No newline at end of file
diff --git a/koptilnya/data/models/bmw_m4_g82.txt b/koptilnya/data/models/bmw_m4_g82.txt
new file mode 100644
index 0000000..77834e1
--- /dev/null
+++ b/koptilnya/data/models/bmw_m4_g82.txt
@@ -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
diff --git a/koptilnya/data/models/vw_golf_mk7_5.txt b/koptilnya/data/models/vw_golf_mk7_5.txt
new file mode 100644
index 0000000..5da5e2a
--- /dev/null
+++ b/koptilnya/data/models/vw_golf_mk7_5.txt
@@ -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
diff --git a/koptilnya/engine_remastered/configs/sx240.txt b/koptilnya/engine_remastered/configs/sx240.txt
index aaae9aa..1dbad92 100644
--- a/koptilnya/engine_remastered/configs/sx240.txt
+++ b/koptilnya/engine_remastered/configs/sx240.txt
@@ -26,7 +26,8 @@ Vehicle:new({
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},
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}}
})
diff --git a/koptilnya/engine_remastered/configs/sx240_stock.txt b/koptilnya/engine_remastered/configs/sx240_stock.txt
index 09408e5..e2ff6c2 100644
--- a/koptilnya/engine_remastered/configs/sx240_stock.txt
+++ b/koptilnya/engine_remastered/configs/sx240_stock.txt
@@ -24,7 +24,7 @@ Vehicle:new({
}
},
Clutch = {Stiffness = 20, Damping = 0.7, MaxTorque = 400},
- Gearbox = {Type = 'MANUAL', ShiftDuration = 0.2, ShiftSmoothness = 0.3, Ratios = {3.626, 2.200, 1.541, 1.213, 1.000, 0.767}, Reverse = 3.437},
+ Gearbox = {Type = 'MANUAL', ShiftDuration = 0.2, ShiftSmoothness = 0.3, Ratios = {3.626, 2.200, 1.541, 1.213, 1.000, 0.767}, Reverse = 3.437},
Axles = {{Power = 0.96, Coast = 0.96, Preload = 10, UsePowerBias = 10, ViscousCoeff = 0.96, Axle = Vector(1, 0, 0), DistributionCoeff = 1, FinalDrive = 3.392}},
Systems = {{Type = 'LAUNCH', Limit = 3500}, {Type = 'TRACTION', Limit = 1.5}}
})
diff --git a/koptilnya/grip_steering/main.txt b/koptilnya/grip_steering/main.txt
deleted file mode 100644
index e2bddf2..0000000
--- a/koptilnya/grip_steering/main.txt
+++ /dev/null
@@ -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)
diff --git a/koptilnya/grip_steering/slave.txt b/koptilnya/grip_steering/slave.txt
deleted file mode 100644
index 6497fff..0000000
--- a/koptilnya/grip_steering/slave.txt
+++ /dev/null
@@ -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
diff --git a/koptilnya/input_system/configs/sample.txt b/koptilnya/input_system/configs/sample.txt
index 4dba483..8fc6217 100644
--- a/koptilnya/input_system/configs/sample.txt
+++ b/koptilnya/input_system/configs/sample.txt
@@ -2,8 +2,8 @@ Axles = {
Horizontal = {
Negative = KEY.D,
Positive = KEY.A,
- Gravity = 0.1,
- Sensitivity = 0.2
+ Gravity = 1,
+ Sensitivity = 1
},
Throttle = {
Positive = KEY.W,
@@ -15,10 +15,15 @@ Axles = {
Gravity = 0.15,
Sensitivity = 0.25
},
+ Brakes = {
+ Positive = KEY.S,
+ Gravity = 0.15,
+ Sensitivity = 0.25
+ },
Shifter = {
Positive = MOUSE.LEFT,
Negative = MOUSE.RIGHT,
Gravity = 1,
Sensitivity = 1
- },
+ }
}
diff --git a/koptilnya/engine_remastered/wire_component.txt b/koptilnya/libs/wire_component.txt
similarity index 100%
rename from koptilnya/engine_remastered/wire_component.txt
rename to koptilnya/libs/wire_component.txt
diff --git a/koptilnya/suspension/main.txt b/koptilnya/suspension/main.txt
new file mode 100644
index 0000000..e7fde2f
--- /dev/null
+++ b/koptilnya/suspension/main.txt
@@ -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
\ No newline at end of file