--@include ./powertrain_component.txt --@include /koptilnya/libs/constants.txt local PowertrainComponent = require('./powertrain_component.txt') require('/koptilnya/libs/constants.txt') local Engine = class('Engine', PowertrainComponent) function Engine:initialize(vehicle, name, config) PowertrainComponent.initialize(self, vehicle, name, config) self.wireInputs = { Throttle = 'number' } self.wireOutputs = { Engine_RPM = 'number', Engine_Torque = 'number', Engine_MasterThrottle = 'number', Engine_ReactTorque = 'number', Engine_ReturnedTorque = 'number', Engine_FinalTorque = 'number' } self.idleRPM = config.IdleRPM or 900 self.maxRPM = config.MaxRPM or 7000 self.inertia = config.Inertia or 0.4 self.startFriction = config.StartFriction or -50 self.frictionCoeff = config.FrictionCoeff or 0.02 self.limiterDuration = config.LimiterDuration or 0.05 self.torqueMap = config.TorqueMap or {} self.rpmFrac = 0 self.friction = 0 self.masterThrottle = 0 self.limiterTimer = 0 self.reactTorque = 0 self.returnedTorque = 0 end function Engine:updateWireOutputs() wire.ports.Engine_RPM = self:getRPM() wire.ports.Engine_Torque = self.torque wire.ports.Engine_MasterThrottle = self.masterThrottle wire.ports.Engine_ReactTorque = self.reactTorque wire.ports.Engine_ReturnedTorque = self.returnedTorque wire.ports.Engine_FinalTorque = self.finalTorque end function Engine:getThrottle() return wire.ports.Throttle end function Engine:_generateTorque() local throttle = self:getThrottle() local rpm = self:getRPM() local rpmFrac = math.clamp((rpm - self.idleRPM) / (self.maxRPM - self.idleRPM), 0, 1) self.friction = self.startFriction - self:getRPM() * self.frictionCoeff local tqIdx = math.clamp(math.floor(rpmFrac * #self.torqueMap), 1, #self.torqueMap) local maxInitialTorque = self.torqueMap[tqIdx] - self.friction local idleFadeStart = math.clamp(math.remap(rpm, self.idleRPM - 300, self.idleRPM, 1, 0), 0, 1) local idleFadeEnd = math.clamp(math.remap(rpm, self.idleRPM, self.idleRPM + 600, 1, 0), 0, 1) local additionalEnergySupply = idleFadeEnd * (-self.friction / maxInitialTorque) + idleFadeStart if rpm > self.maxRPM then throttle = 0 self.limiterTimer = timer.systime() else throttle = timer.systime() >= self.limiterTimer + self.limiterDuration and throttle or 0 end self.masterThrottle = math.clamp(additionalEnergySupply + throttle, 0, 1) local realInitialTorque = maxInitialTorque * self.masterThrottle self.torque = realInitialTorque + self.friction return self.torque end function Engine:forwardStep() if self.output == nil then local generatedTorque = self:_generateTorque() self.angularVelocity = self.angularVelocity + generatedTorque / self.inertia * TICK_INTERVAL self.angularVelocity = math.max(self.angularVelocity, 0) end local outputInertia = self.output:queryInertia() local inertiaSum = self.inertia + outputInertia local outputW = self.output:queryAngularVelocity(self.angularVelocity) local targetW = self.inertia / inertiaSum * self.angularVelocity + outputInertia / inertiaSum * outputW local generatedTorque = self:_generateTorque() local reactTorque = (targetW - self.angularVelocity) * self.inertia / TICK_INTERVAL local returnedTorque = self.output:forwardStep(generatedTorque - reactTorque, 0) -- ??? 0 -> self.inertia self.reactTorque = reactTorque self.returnedTorque = returnedTorque local finalTorque = generatedTorque + reactTorque + returnedTorque self.finalTorque = finalTorque self.angularVelocity = self.angularVelocity + finalTorque / inertiaSum * TICK_INTERVAL self.angularVelocity = math.max(self.angularVelocity, 0) end return Engine