--@include ./powertrain_component.txt --@include /koptilnya/libs/constants.txt require('./powertrain_component.txt') require('/koptilnya/libs/constants.txt') Engine = class('Engine', PowertrainComponent) function Engine:initialize(config, clutch) PowertrainComponent.initialize(self, 'Engine') self.wireInputs = { Throttle = 'number' } self.wireOutputs = { Engine_RPM = 'number', Engine_Torque = 'number', Engine_MasterThrottle = '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 end function Engine:updateWireOutputs() wire.ports.Engine_RPM = self:getRPM() wire.ports.Engine_Torque = self.torque wire.ports.Engine_MasterThrottle = self.masterThrottle 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:integrateDownwards() 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 local finalTorque = generatedTorque + returnedTorque + reactTorque self.angularVelocity = self.angularVelocity + finalTorque / inertiaSum * TICK_INTERVAL self.angularVelocity = math.max(self.angularVelocity, 0) end