60 | function Rideable.initSpecialization() |
61 | local schema = Vehicle.xmlSchema |
62 | schema:setXMLSpecializationType("Rideable") |
63 | |
64 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#speedBackwards", "Backward speed", -1) |
65 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#speedWalk", "Walk speed", 2.5) |
66 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#speedCanter", "Canter speed", 3.5) |
67 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#speedTrot", "Trot speed", 5.0) |
68 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#speedGallop", "Gallop speed", 10.0) |
69 | |
70 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#minTurnRadiusBackwards", "Min turning radius backward", 1) |
71 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#minTurnRadiusWalk", "Min turning radius walk", 1) |
72 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#minTurnRadiusCanter", "Min turning radius canter", 2.5) |
73 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#minTurnRadiusTrot", "Min turning radius trot", 5) |
74 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#minTurnRadiusGallop", "Min turning radius gallop", 10) |
75 | |
76 | schema:register(XMLValueType.ANGLE, "vehicle.rideable#turnSpeed", "Turn speed (deg/s)", 45) |
77 | schema:register(XMLValueType.FLOAT, "vehicle.rideable#jumpHeight", "Jump height", 2) |
78 | |
79 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable#proxy", "Proxy node") |
80 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofFrontLeft#node", "Hoof node") |
81 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofFrontLeft.particleSystemSlow#node", "Slow step particle emitterShape") |
82 | schema:register(XMLValueType.STRING, "vehicle.rideable.modelInfo.hoofFrontLeft.particleSystemSlow#particleType", "Slow step particle type") |
83 | ParticleUtil.registerParticleCopyXMLPaths(schema, "vehicle.rideable.modelInfo.hoofFrontLeft.particleSystemSlow") |
84 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofFrontLeft.particleSystemFast#node", "Fast step particle emitterShape") |
85 | schema:register(XMLValueType.STRING, "vehicle.rideable.modelInfo.hoofFrontLeft.particleSystemFast#particleType", "Fast step particle type") |
86 | ParticleUtil.registerParticleCopyXMLPaths(schema, "vehicle.rideable.modelInfo.hoofFrontLeft.particleSystemFast") |
87 | |
88 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofFrontRight#node", "Hoof node") |
89 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofFrontRight.particleSystemSlow#node", "Slow step particle emitterShape") |
90 | schema:register(XMLValueType.STRING, "vehicle.rideable.modelInfo.hoofFrontRight.particleSystemSlow#particleType", "Slow step particle type") |
91 | ParticleUtil.registerParticleCopyXMLPaths(schema, "vehicle.rideable.modelInfo.hoofFrontRight.particleSystemSlow") |
92 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofFrontRight.particleSystemFast#node", "Fast step particle emitterShape") |
93 | schema:register(XMLValueType.STRING, "vehicle.rideable.modelInfo.hoofFrontRight.particleSystemFast#particleType", "Fast step particle type") |
94 | ParticleUtil.registerParticleCopyXMLPaths(schema, "vehicle.rideable.modelInfo.hoofFrontRight.particleSystemFast") |
95 | |
96 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofBackLeft#node", "Hoof node") |
97 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofBackLeft.particleSystemSlow#node", "Slow step particle emitterShape") |
98 | schema:register(XMLValueType.STRING, "vehicle.rideable.modelInfo.hoofBackLeft.particleSystemSlow#particleType", "Slow step particle type") |
99 | ParticleUtil.registerParticleCopyXMLPaths(schema, "vehicle.rideable.modelInfo.hoofBackLeft.particleSystemSlow") |
100 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofBackLeft.particleSystemFast#node", "Fast step particle emitterShape") |
101 | schema:register(XMLValueType.STRING, "vehicle.rideable.modelInfo.hoofBackLeft.particleSystemFast#particleType", "Fast step particle type") |
102 | ParticleUtil.registerParticleCopyXMLPaths(schema, "vehicle.rideable.modelInfo.hoofBackLeft.particleSystemFast") |
103 | |
104 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofBackRight#node", "Hoof node") |
105 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofBackRight.particleSystemSlow#node", "Slow step particle emitterShape") |
106 | schema:register(XMLValueType.STRING, "vehicle.rideable.modelInfo.hoofBackRight.particleSystemSlow#particleType", "Slow step particle type") |
107 | ParticleUtil.registerParticleCopyXMLPaths(schema, "vehicle.rideable.modelInfo.hoofBackRight.particleSystemSlow") |
108 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo.hoofBackRight.particleSystemFast#node", "Fast step particle emitterShape") |
109 | schema:register(XMLValueType.STRING, "vehicle.rideable.modelInfo.hoofBackRight.particleSystemFast#particleType", "Fast step particle type") |
110 | ParticleUtil.registerParticleCopyXMLPaths(schema, "vehicle.rideable.modelInfo.hoofBackRight.particleSystemFast") |
111 | |
112 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo#animationNode", "Animation node") |
113 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo#meshNode", "Mesh node") |
114 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo#equipmentNode", "Equipment node") |
115 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo#reinsNode", "Reins node") |
116 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo#reinLeftNode", "Rein left node") |
117 | schema:register(XMLValueType.NODE_INDEX, "vehicle.rideable.modelInfo#reinRightNode", "Rein right node") |
118 | |
119 | schema:register(XMLValueType.FLOAT, "vehicle.rideable.sounds#breathIntervalNoEffort", "Breath interval no effort", 1) |
120 | schema:register(XMLValueType.FLOAT, "vehicle.rideable.sounds#breathIntervalEffort", "Breath interval effort", 1) |
121 | schema:register(XMLValueType.FLOAT, "vehicle.rideable.sounds#minBreathIntervalIdle", "Min. breath interval idle", 1) |
122 | schema:register(XMLValueType.FLOAT, "vehicle.rideable.sounds#maxBreathIntervalIdle", "Max. breath interval idle", 1) |
123 | |
124 | SoundManager.registerSampleXMLPaths(schema, "vehicle.rideable.sounds", "halt") |
125 | SoundManager.registerSampleXMLPaths(schema, "vehicle.rideable.sounds", "breathingNoEffort") |
126 | SoundManager.registerSampleXMLPaths(schema, "vehicle.rideable.sounds", "breathingEffort") |
127 | |
128 | -- values loaded only by engine, registration just for documentation/validation purposes |
129 | local registerConditionalAnimation = function(xmlKey) |
130 | schema:register(XMLValueType.STRING, xmlKey .. ".item(?)#id", "") |
131 | schema:register(XMLValueType.FLOAT, xmlKey .. ".item(?)#entryTransitionDuration", "") |
132 | schema:register(XMLValueType.FLOAT, xmlKey .. ".item(?)#exitTransitionDuration", "") |
133 | |
134 | schema:register(XMLValueType.STRING, xmlKey .. ".item(?).clips#speedScaleType", "") |
135 | schema:register(XMLValueType.FLOAT, xmlKey .. ".item(?).clips#speedScaleParameter", "") |
136 | schema:register(XMLValueType.BOOL, xmlKey .. ".item(?).clips#blended", "") |
137 | schema:register(XMLValueType.STRING, xmlKey .. ".item(?).clips#blendingParameter", "") |
138 | schema:register(XMLValueType.STRING, xmlKey .. ".item(?).clips#blendingParameterType", "") |
139 | schema:register(XMLValueType.STRING, xmlKey .. ".item(?).clips.clip(?)#clipName", "") |
140 | schema:register(XMLValueType.STRING, xmlKey .. ".item(?).clips.clip(?)#id", "") |
141 | schema:register(XMLValueType.FLOAT, xmlKey .. ".item(?).clips.clip(?)#blendingThreshold", "") |
142 | |
143 | schema:register(XMLValueType.STRING, xmlKey .. ".item(?).conditions.conditionGroup(?).condition(?)#parameter", "") |
144 | schema:register(XMLValueType.BOOL, xmlKey .. ".item(?).conditions.conditionGroup(?).condition(?)#equal", "") |
145 | schema:register(XMLValueType.STRING, xmlKey .. ".item(?).conditions.conditionGroup(?).condition(?)#between", "") |
146 | schema:register(XMLValueType.FLOAT, xmlKey .. ".item(?).conditions.conditionGroup(?).condition(?)#greater", "") |
147 | schema:register(XMLValueType.FLOAT, xmlKey .. ".item(?).conditions.conditionGroup(?).condition(?)#lower", "") |
148 | end |
149 | |
150 | registerConditionalAnimation('vehicle.conditionalAnimation') |
151 | registerConditionalAnimation('vehicle.riderConditionalAnimation') |
152 | |
153 | schema:setXMLSpecializationType() |
154 | |
155 | local savegameSchema = Vehicle.xmlSchemaSavegame |
156 | savegameSchema:register(XMLValueType.STRING, "vehicles.vehicle(?).rideable#animalType", "Animal type name") |
157 | end |
233 | function Rideable:onLoad(savegame) |
234 | local spec = self.spec_rideable |
235 | |
236 | -- Overwrite the Vehicle high precision setting. Otherwise the precision might lead to large jitter in speed in multiplayer |
237 | self.highPrecisionPositionSynchronization = true |
238 | |
239 | spec.leaveTimer = 15000 |
240 | spec.currentDirtScale = 0 |
241 | spec.abandonTimerDuration = g_gameSettings:getValue("horseAbandonTimerDuration") |
242 | spec.abandonTimer = spec.abandonTimerDuration |
243 | spec.fadeDuration = 400 |
244 | spec.isRideableRemoved = false |
245 | spec.justSpawned = true |
246 | spec.meshNode = nil |
247 | -- Animation |
248 | spec.animationNode = nil |
249 | spec.charsetId = nil |
250 | spec.animationPlayer = nil |
251 | spec.animationParameters = {} |
252 | spec.animationParameters.forwardVelocity = {id=1, value=0.0, type=1} |
253 | spec.animationParameters.verticalVelocity = {id=2, value=0.0, type=1} |
254 | spec.animationParameters.yawVelocity = {id=3, value=0.0, type=1} |
255 | spec.animationParameters.absForwardVelocity = {id=4, value=0.0, type=1} |
256 | spec.animationParameters.onGround = {id=5, value=false, type=0} |
257 | spec.animationParameters.inWater = {id=6, value=false, type=0} |
258 | spec.animationParameters.closeToGround = {id=7, value=false, type=0} |
259 | spec.animationParameters.leftRightWeight = {id=8, value=0.0, type=1} |
260 | spec.animationParameters.absYawVelocity = {id=9, value=0.0, type=1} |
261 | spec.animationParameters.halted = {id=10, value=false, type=0} |
262 | spec.animationParameters.smoothedForwardVelocity = {id=11, value=0.0, type=1} |
263 | spec.animationParameters.absSmoothedForwardVelocity = {id=12, value=0.0, type=1} |
264 | |
265 | -- InputAction |
266 | spec.acceletateEventId = "" |
267 | spec.brakeEventId = "" |
268 | spec.steerEventId = "" |
269 | spec.jumpEventId = "" |
270 | |
271 | -- movements |
272 | spec.currentTurnAngle = 0 |
273 | spec.currentTurnSpeed = 0.0 |
274 | spec.currentSpeed = 0.0 |
275 | spec.currentSpeedY = 0.0 |
276 | spec.cctMoveQueue = {} |
277 | spec.currentCCTPosX = 0.0 |
278 | spec.currentCCTPosY = 0.0 |
279 | spec.currentCCTPosZ = 0.0 |
280 | spec.lastCCTPosX = 0.0 |
281 | spec.lastCCTPosY = 0.0 |
282 | spec.lastCCTPosZ = 0.0 |
283 | spec.topSpeeds = {} |
284 | spec.topSpeeds[Rideable.GAITTYPES.BACKWARDS] = self.xmlFile:getValue("vehicle.rideable#speedBackwards", -1.0) |
285 | spec.topSpeeds[Rideable.GAITTYPES.STILL] = 0.0 |
286 | spec.topSpeeds[Rideable.GAITTYPES.WALK] = self.xmlFile:getValue("vehicle.rideable#speedWalk", 2.5) |
287 | spec.topSpeeds[Rideable.GAITTYPES.CANTER] = self.xmlFile:getValue("vehicle.rideable#speedCanter", 3.5) |
288 | spec.topSpeeds[Rideable.GAITTYPES.TROT] = self.xmlFile:getValue("vehicle.rideable#speedTrot", 5.0) |
289 | spec.topSpeeds[Rideable.GAITTYPES.GALLOP] = self.xmlFile:getValue("vehicle.rideable#speedGallop", 10.0) |
290 | spec.minTurnRadius = {} |
291 | spec.minTurnRadius[Rideable.GAITTYPES.BACKWARDS] = self.xmlFile:getValue("vehicle.rideable#minTurnRadiusBackwards", 1.0) |
292 | spec.minTurnRadius[Rideable.GAITTYPES.STILL] = 1.0 |
293 | spec.minTurnRadius[Rideable.GAITTYPES.WALK] = self.xmlFile:getValue("vehicle.rideable#minTurnRadiusWalk", 1.0) |
294 | spec.minTurnRadius[Rideable.GAITTYPES.CANTER] = self.xmlFile:getValue("vehicle.rideable#minTurnRadiusCanter", 2.5) |
295 | spec.minTurnRadius[Rideable.GAITTYPES.TROT] = self.xmlFile:getValue("vehicle.rideable#minTurnRadiusTrot", 5.0) |
296 | spec.minTurnRadius[Rideable.GAITTYPES.GALLOP] = self.xmlFile:getValue("vehicle.rideable#minTurnRadiusGallop", 10.0) |
297 | spec.groundRaycastResult = {} |
298 | spec.groundRaycastResult.y = 0.0 |
299 | spec.groundRaycastResult.object = nil |
300 | spec.groundRaycastResult.distance = 0.0 |
301 | spec.haltTimer = 0.0 |
302 | spec.smoothedLeftRightWeight = 0.0 |
303 | spec.interpolationDt = 16 |
304 | spec.ridingTimer = 0 |
305 | spec.doHusbandryCheck = 0 |
306 | |
307 | spec.proxy = self.xmlFile:getValue("vehicle.rideable#proxy", nil, self.components, self.i3dMappings) |
308 | if spec.proxy ~= nil then |
309 | setRigidBodyType(spec.proxy, RigidBodyType.NONE) |
310 | end |
311 | spec.collisionMask = getCollisionMask(self.components[1].node) |
312 | |
313 | -- interpolation |
314 | -- spec.interpolationTime = InterpolationTime.new(1.0) |
315 | -- spec.interpolatorPosition = InterpolatorPosition.new(0.0, 0.0, 0.0) |
316 | -- spec.interpolatorQuaternion = InterpolatorQuaternion.new(0.0, 0.0, 0.0, 1.0) -- only used on server side for rotation of camera |
317 | -- spec.interpolatorOnGround = InterpolatorValue.new(0.0) |
318 | |
319 | -- steer |
320 | spec.maxAcceleration = 5 -- m/s^2 |
321 | spec.maxDeceleration = 10 -- m/s^2 |
322 | spec.gravity = -18.8 |
323 | |
324 | -- ground orientation |
325 | spec.frontCheckDistance = 0.0 |
326 | spec.backCheckDistance = 0.0 |
327 | spec.isOnGround = true |
328 | spec.isCloseToGround = true |
329 | |
330 | assert(spec.topSpeeds[Rideable.GAITTYPES.MIN] < spec.topSpeeds[Rideable.GAITTYPES.MAX]) |
331 | spec.maxTurnSpeed = self.xmlFile:getValue("vehicle.rideable#turnSpeed", 45.0) -- xml: deg/s, script: rad/s |
332 | spec.jumpHeight = self.xmlFile:getValue("vehicle.rideable#jumpHeight", 2.0) |
333 | |
334 | local function loadHoof(target, index, key) |
335 | local hoof = {} |
336 | hoof.node = self.xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings) |
337 | hoof.onGround = false |
338 | |
339 | local nodeSlow = self.xmlFile:getValue(key..".particleSystemSlow#node", nil, self.components, self.i3dMappings) |
340 | local particleType = self.xmlFile:getValue(key .. ".particleSystemSlow#particleType") |
341 | if particleType == nil then |
342 | Logging.xmlWarning(self.xmlFile, "Missing horse step slow particleType in '%s'", key .. ".particleSystemSlow") |
343 | return false |
344 | end |
345 | local particleSystem = g_particleSystemManager:getParticleSystem(particleType) |
346 | if particleSystem ~= nil then |
347 | hoof.psSlow = ParticleUtil.copyParticleSystem(self.xmlFile, key .. ".particleSystemSlow", particleSystem, nodeSlow) |
348 | end |
349 | |
350 | local nodeFast = self.xmlFile:getValue(key..".particleSystemFast#node", nil, self.components, self.i3dMappings) |
351 | local particleTypeFast = self.xmlFile:getValue(key .. ".particleSystemFast#particleType") |
352 | if particleTypeFast == nil then |
353 | Logging.xmlWarning(self.xmlFile, "Missing horse step fast particleType in '%s'", key .. ".particleSystemFast") |
354 | return false |
355 | end |
356 | local particleSystemFast = g_particleSystemManager:getParticleSystem(particleTypeFast) |
357 | if particleSystemFast ~= nil then |
358 | hoof.psFast = ParticleUtil.copyParticleSystem(self.xmlFile, key .. ".particleSystemFast", particleSystemFast, nodeFast) |
359 | end |
360 | |
361 | target[index] = hoof |
362 | end |
363 | |
364 | -- Hooves |
365 | spec.hooves = {} |
366 | loadHoof(spec.hooves, Rideable.HOOVES.FRONT_LEFT, "vehicle.rideable.modelInfo.hoofFrontLeft") |
367 | loadHoof(spec.hooves, Rideable.HOOVES.FRONT_RIGHT, "vehicle.rideable.modelInfo.hoofFrontRight") |
368 | loadHoof(spec.hooves, Rideable.HOOVES.BACK_LEFT, "vehicle.rideable.modelInfo.hoofBackLeft") |
369 | loadHoof(spec.hooves, Rideable.HOOVES.BACK_RIGHT, "vehicle.rideable.modelInfo.hoofBackRight") |
370 | |
371 | for _, hoove in pairs(spec.hooves) do |
372 | link(getRootNode(), hoove.psSlow.emitterShape) |
373 | link(getRootNode(), hoove.psFast.emitterShape) |
374 | end |
375 | |
376 | spec.frontCheckDistance = self:calculateLegsDistance(spec.hooves[Rideable.HOOVES.FRONT_LEFT].node, spec.hooves[Rideable.HOOVES.FRONT_RIGHT].node) |
377 | spec.backCheckDistance = self:calculateLegsDistance(spec.hooves[Rideable.HOOVES.BACK_LEFT].node, spec.hooves[Rideable.HOOVES.BACK_RIGHT].node) |
378 | |
379 | spec.animationNode = self.xmlFile:getValue("vehicle.rideable.modelInfo#animationNode", nil, self.components, self.i3dMappings) |
380 | spec.meshNode = self.xmlFile:getValue("vehicle.rideable.modelInfo#meshNode", nil, self.components, self.i3dMappings) |
381 | spec.equipmentNode = self.xmlFile:getValue("vehicle.rideable.modelInfo#equipmentNode", nil, self.components, self.i3dMappings) |
382 | spec.reinsNode = self.xmlFile:getValue("vehicle.rideable.modelInfo#reinsNode", nil, self.components, self.i3dMappings) |
383 | spec.leftReinNode = self.xmlFile:getValue("vehicle.rideable.modelInfo#reinLeftNode", nil, self.components, self.i3dMappings) |
384 | spec.rightReinNode = self.xmlFile:getValue("vehicle.rideable.modelInfo#reinRightNode", nil, self.components, self.i3dMappings) |
385 | spec.leftReinParentNode = getParent(spec.leftReinNode) |
386 | spec.rightReinParentNode = getParent(spec.rightReinNode) |
387 | |
388 | -- animation |
389 | if spec.animationNode ~= nil then |
390 | spec.charsetId = getAnimCharacterSet(spec.animationNode) |
391 | local animationPlayer = createConditionalAnimation() |
392 | if animationPlayer ~= 0 then |
393 | spec.animationPlayer = animationPlayer |
394 | for key, parameter in pairs(spec.animationParameters) do |
395 | conditionalAnimationRegisterParameter(spec.animationPlayer, parameter.id, parameter.type, key) |
396 | end |
397 | initConditionalAnimation(spec.animationPlayer, spec.charsetId, self.configFileName, "vehicle.conditionalAnimation") |
398 | setConditionalAnimationSpecificParameterIds(spec.animationPlayer, spec.animationParameters.absForwardVelocity.id, spec.animationParameters.absYawVelocity.id) |
399 | end |
400 | end |
401 | |
402 | -- Sounds |
403 | spec.surfaceSounds = {} |
404 | spec.surfaceIdToSound = {} |
405 | spec.surfaceNameToSound = {} |
406 | spec.currentSurfaceSound = nil |
407 | for _, surfaceSound in pairs(g_currentMission.surfaceSounds) do |
408 | if surfaceSound.type == "hoofstep" and surfaceSound.sample ~= nil then |
409 | local sample = g_soundManager:cloneSample(surfaceSound.sample, self.components[1].node, self) |
410 | sample.sampleName = surfaceSound.name |
411 | |
412 | table.insert(spec.surfaceSounds, sample) |
413 | spec.surfaceIdToSound[surfaceSound.materialId] = sample |
414 | spec.surfaceNameToSound[surfaceSound.name] = sample |
415 | end |
416 | end |
417 | spec.horseStopSound = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.rideable.sounds", "halt", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
418 | spec.horseBreathSoundsNoEffort = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.rideable.sounds", "breathingNoEffort", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
419 | spec.horseBreathSoundsEffort = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.rideable.sounds", "breathingEffort", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
420 | spec.horseBreathIntervalNoEffort = self.xmlFile:getValue("vehicle.rideable.sounds#breathIntervalNoEffort", 1.0) * 1000.0 |
421 | spec.horseBreathIntervalEffort = self.xmlFile:getValue("vehicle.rideable.sounds#breathIntervalEffort", 1.0) * 1000.0 |
422 | spec.horseBreathMinIntervalIdle = self.xmlFile:getValue("vehicle.rideable.sounds#minBreathIntervalIdle", 1.0) * 1000.0 |
423 | spec.horseBreathMaxIntervalIdle = self.xmlFile:getValue("vehicle.rideable.sounds#maxBreathIntervalIdle", 1.0) * 1000.0 |
424 | spec.currentBreathTimer = 0.0 |
425 | |
426 | -- attributes set by action events |
427 | spec.inputValues = {} |
428 | spec.inputValues.axisSteer = 0.0 |
429 | spec.inputValues.axisSteerSend = 0.0 |
430 | spec.inputValues.currentGait = Rideable.GAITTYPES.STILL |
431 | self:resetInputs() |
432 | |
433 | spec.interpolatorIsOnGround = InterpolatorValue.new(0.0) |
434 | if self.isServer then |
435 | spec.interpolatorTurnAngle = InterpolatorAngle.new(0.0) |
436 | self.networkTimeInterpolator.maxInterpolationAlpha = 1.2 |
437 | end |
438 | |
439 | -- Network |
440 | spec.dirtyFlag = self:getNextDirtyFlag() |
441 | |
442 | if savegame ~= nil then |
443 | local xmlFile = savegame.xmlFile |
444 | local key = savegame.key .. ".rideable" |
445 | |
446 | local subTypeName = xmlFile:getString(key .. "#subType", "HORSE_GRAY") |
447 | local subType = g_currentMission.animalSystem:getSubTypeByName(subTypeName) |
448 | if subType ~= nil then |
449 | local cluster = g_currentMission.animalSystem:createClusterFromSubTypeIndex(subType.subTypeIndex) |
450 | cluster:loadFromXMLFile(xmlFile, key .. ".animal") |
451 | self:setCluster(cluster) |
452 | else |
453 | Logging.xmlError(self.xmlFile, "No animal sub type found!", spec.fillUnitIndex) |
454 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_ERROR) |
455 | return |
456 | end |
457 | end |
458 | |
459 | g_currentMission.husbandrySystem:addRideable(self) |
460 | end |
801 | function Rideable:onUpdateInterpolation(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
802 | local spec = self.spec_rideable |
803 | |
804 | if self.isServer then |
805 | if not self:getIsControlled() then |
806 | self:setCurrentGait(Rideable.GAITTYPES.STILL) |
807 | end |
808 | -- for key, moveInfo in pairs(spec.cctMoveQueue) do |
809 | -- print(string.format("-- [Rideable:onUpdateInterpolation][A][%d]\t%d\t%d\t%d\t%s\t%.6f", g_updateLoopIndex, getPhysicsUpdateIndex(), key, moveInfo.physicsIndex, tostring(getIsPhysicsUpdateIndexSimulated(moveInfo.physicsIndex)), moveInfo.dt)) |
810 | -- end |
811 | |
812 | local interpolationDt = dt |
813 | local oldestMoveInfo = spec.cctMoveQueue[1] |
814 | if oldestMoveInfo ~= nil and getIsPhysicsUpdateIndexSimulated(oldestMoveInfo.physicsIndex) then |
815 | interpolationDt = oldestMoveInfo.dt |
816 | end |
817 | spec.interpolationDt = interpolationDt |
818 | |
819 | self:testCCTMove(interpolationDt) |
820 | self:updateKinematic(dt) |
821 | |
822 | if self:getIsEntered() then |
823 | self:resetInputs() |
824 | end |
825 | |
826 | local component = self.components[1] |
827 | local x,y,z = self:getCCTWorldTranslation() |
828 | component.networkInterpolators.position:setTargetPosition(x,y,z) |
829 | spec.interpolatorTurnAngle:setTargetAngle(spec.currentTurnAngle) |
830 | spec.interpolatorIsOnGround:setTargetValue(self:getIsCCTOnGround() and 1.0 or 0.0) |
831 | |
832 | -- use 75 or if dt > 75 then dt + 20 |
833 | local phaseDuration = interpolationDt + 30 |
834 | |
835 | |
836 | self.networkTimeInterpolator:startNewPhase(phaseDuration) |
837 | self.networkTimeInterpolator:update(interpolationDt) |
838 | |
839 | -- local deltax, deltay, deltaz = component.networkInterpolators.position.targetPositionX - component.networkInterpolators.position.lastPositionX, component.networkInterpolators.position.targetPositionY - component.networkInterpolators.position.lastPositionY, component.networkInterpolators.position.targetPositionZ - component.networkInterpolators.position.lastPositionZ |
840 | -- local deltamag = math.sqrt(deltax*deltax+deltay*deltay+deltaz*deltaz) |
841 | -- print(string.format("-- [Rideable:onUpdateInterpolation][B][%d]\t%.6f\t%.6f\t%d\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f", g_updateLoopIndex, dt, interpolationDt, getPhysicsUpdateIndex(), self.networkTimeInterpolator.interpolationAlpha, self.networkTimeInterpolator.interpolationDuration, component.networkInterpolators.position.targetPositionX, component.networkInterpolators.position.targetPositionY, component.networkInterpolators.position.targetPositionZ, component.networkInterpolators.position.lastPositionX, component.networkInterpolators.position.lastPositionY, component.networkInterpolators.position.lastPositionZ, deltax, deltay, deltaz, deltamag)) |
842 | |
843 | x, y, z = component.networkInterpolators.position:getInterpolatedValues(self.networkTimeInterpolator.interpolationAlpha) |
844 | setTranslation(self.rootNode, x, y, z) |
845 | |
846 | local turnAngle = spec.interpolatorTurnAngle:getInterpolatedValue(self.networkTimeInterpolator.interpolationAlpha) |
847 | local _, dirY, _ = localDirectionToWorld(self.rootNode, 0.0, 0.0, 1.0) |
848 | local dirX, dirZ = math.sin(turnAngle), math.cos(turnAngle) |
849 | |
850 | -- rescale direction to length, keeping the original y (but keep it to some reasonable value) |
851 | local scale = math.sqrt(1-math.min(dirY*dirY, 0.9)) |
852 | dirX = dirX * scale |
853 | dirZ = dirZ * scale |
854 | setDirection(self.rootNode, dirX, dirY, dirZ, 0,1,0) |
855 | end |
856 | |
857 | if not self:getIsEntered() then |
858 | if spec.leaveTimer > 0 then |
859 | spec.leaveTimer = spec.leaveTimer - dt |
860 | self:raiseActive() |
861 | end |
862 | end |
863 | |
864 | local isOnGroundFloat = spec.interpolatorIsOnGround:getInterpolatedValue(self.networkTimeInterpolator:getAlpha()) |
865 | spec.isOnGround = isOnGroundFloat > 0.9 |
866 | spec.isCloseToGround = false |
867 | |
868 | if spec.isOnGround and (math.abs(spec.currentSpeed) > 0.001 or math.abs(spec.currentTurnSpeed) > 0.001) then |
869 | -- orientation from ground |
870 | local posX, posY, posZ = getWorldTranslation(self.rootNode) |
871 | local dirX, dirY, dirZ = localDirectionToWorld(self.rootNode, 0.0, 0.0, 1.0) |
872 | local fx, fy, fz = posX + dirX * spec.frontCheckDistance, posY + dirY * spec.frontCheckDistance, posZ + dirZ * spec.frontCheckDistance |
873 | spec.groundRaycastResult.y = fy + Rideable.GROUND_RAYCAST_OFFSET - Rideable.GROUND_RAYCAST_MAXDISTANCE |
874 | raycastClosest(fx, fy + Rideable.GROUND_RAYCAST_OFFSET, fz, 0.0, -1.0, 0.0, "groundRaycastCallback", Rideable.GROUND_RAYCAST_MAXDISTANCE, self, Rideable.GROUND_RAYCAST_COLLISIONMASK) |
875 | fy = spec.groundRaycastResult.y |
876 | local bx, by, bz = posX + dirX * spec.backCheckDistance, posY + dirY * spec.backCheckDistance, posZ + dirZ * spec.backCheckDistance |
877 | spec.groundRaycastResult.y = by + Rideable.GROUND_RAYCAST_OFFSET - Rideable.GROUND_RAYCAST_MAXDISTANCE |
878 | raycastClosest(bx, by + Rideable.GROUND_RAYCAST_OFFSET, bz, 0.0, -1.0, 0.0, "groundRaycastCallback", Rideable.GROUND_RAYCAST_MAXDISTANCE, self, Rideable.GROUND_RAYCAST_COLLISIONMASK) |
879 | by = spec.groundRaycastResult.y |
880 | local dx, dy, dz = fx - bx, fy - by, fz - bz |
881 | setDirection(self.rootNode, dx, dy, dz, 0, 1, 0) |
882 | else |
883 | local posX, posY, posZ = getWorldTranslation(self.rootNode) |
884 | spec.groundRaycastResult.distance = Rideable.GROUND_RAYCAST_MAXDISTANCE |
885 | raycastClosest(posX, posY, posZ, 0.0, -1.0, 0.0, "groundRaycastCallback", Rideable.GROUND_RAYCAST_MAXDISTANCE, self, Rideable.GROUND_RAYCAST_COLLISIONMASK) |
886 | spec.isCloseToGround = spec.groundRaycastResult.distance < 1.25 |
887 | end |
888 | end |
1094 | function Rideable:updateAnimation(dt) |
1095 | local spec = self.spec_rideable |
1096 | local params = spec.animationParameters |
1097 | local speed = self.lastSignedSpeedReal * 1000.0 |
1098 | local smoothedSpeed = self.lastSignedSpeed * 1000.0 |
1099 | speed = MathUtil.clamp(speed, spec.topSpeeds[Rideable.GAITTYPES.BACKWARDS], spec.topSpeeds[Rideable.GAITTYPES.MAX]) |
1100 | smoothedSpeed = MathUtil.clamp(smoothedSpeed, spec.topSpeeds[Rideable.GAITTYPES.BACKWARDS], spec.topSpeeds[Rideable.GAITTYPES.MAX]) |
1101 | |
1102 | local turnSpeed |
1103 | if self.isServer then |
1104 | turnSpeed = (spec.interpolatorTurnAngle.targetValue - spec.interpolatorTurnAngle.lastValue) / (self.networkTimeInterpolator.interpolationDuration * 0.001) |
1105 | else |
1106 | local interpQuat = self.components[1].networkInterpolators.quaternion |
1107 | local lastDirX, _, lastDirZ = mathQuaternionRotateVector(interpQuat.lastQuaternionX, interpQuat.lastQuaternionY, interpQuat.lastQuaternionZ, interpQuat.lastQuaternionW, 0,0,1) |
1108 | local targetDirX, _, targetDirZ = mathQuaternionRotateVector(interpQuat.targetQuaternionX, interpQuat.targetQuaternionY, interpQuat.targetQuaternionZ, interpQuat.targetQuaternionW, 0,0,1) |
1109 | local lastTurnAngle = MathUtil.getYRotationFromDirection(lastDirX, lastDirZ) |
1110 | local targetTurnAngle = MathUtil.getYRotationFromDirection(targetDirX, targetDirZ) |
1111 | local turnAngleDiff = targetTurnAngle - lastTurnAngle |
1112 | -- normalize to -180,180deg |
1113 | if turnAngleDiff > math.pi then |
1114 | turnAngleDiff = turnAngleDiff - 2*math.pi |
1115 | elseif turnAngleDiff < -math.pi then |
1116 | turnAngleDiff = turnAngleDiff + 2*math.pi |
1117 | end |
1118 | turnSpeed = turnAngleDiff / (self.networkTimeInterpolator.interpolationDuration * 0.001) |
1119 | end |
1120 | |
1121 | local interpPos = self.components[1].networkInterpolators.position |
1122 | local speedY = (interpPos.targetPositionY - interpPos.lastPositionY) / (self.networkTimeInterpolator.interpolationDuration * 0.001) |
1123 | |
1124 | |
1125 | local leftRightWeight |
1126 | if math.abs(speed) > 0.01 then |
1127 | local closestGait = Rideable.GAITTYPES.STILL |
1128 | local closestDiff = math.huge |
1129 | for i=1,Rideable.GAITTYPES.MAX do |
1130 | local diff = math.abs(speed - spec.topSpeeds[i]) |
1131 | if diff < closestDiff then |
1132 | closestGait = i |
1133 | closestDiff = diff |
1134 | end |
1135 | end |
1136 | local minTurnRadius = spec.minTurnRadius[closestGait] |
1137 | leftRightWeight = minTurnRadius * turnSpeed / speed |
1138 | else |
1139 | leftRightWeight = turnSpeed / spec.maxTurnSpeed |
1140 | end |
1141 | if leftRightWeight < spec.smoothedLeftRightWeight then |
1142 | spec.smoothedLeftRightWeight = math.max(leftRightWeight, spec.smoothedLeftRightWeight-1/500*dt, -1) |
1143 | else |
1144 | spec.smoothedLeftRightWeight = math.min(leftRightWeight, spec.smoothedLeftRightWeight+1/500*dt, 1) |
1145 | end |
1146 | |
1147 | -- print(string.format("-- [Rideable:updateAnimation][%d][%s]\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f", g_updateLoopIndex, tostring(self), dt, leftRightWeight, spec.smoothedLeftRightWeight, MathUtil.clamp(leftRightWeight, -1.0, 1.0), turnSpeed / spec.maxTurnSpeed, MathUtil.clamp(turnSpeed / spec.maxTurnSpeed, -1.0, 1.0), turnSpeed, speed, self.movingDirection, self.lastSpeedAcceleration * 1000 * 1000)) |
1148 | |
1149 | params.forwardVelocity.value = speed |
1150 | params.absForwardVelocity.value = math.abs(speed) |
1151 | params.verticalVelocity.value = speedY |
1152 | params.yawVelocity.value = turnSpeed |
1153 | params.absYawVelocity.value = math.abs(turnSpeed) |
1154 | params.leftRightWeight.value = spec.smoothedLeftRightWeight |
1155 | params.onGround.value = spec.isOnGround-- or spec.justSpawned |
1156 | params.closeToGround.value = spec.isCloseToGround |
1157 | params.inWater.value = self.isInWater |
1158 | params.halted.value = spec.haltTimer > 0 |
1159 | params.smoothedForwardVelocity.value = smoothedSpeed |
1160 | params.absSmoothedForwardVelocity.value = math.abs(smoothedSpeed) |
1161 | |
1162 | -- horse animation |
1163 | if spec.animationPlayer ~= nil then |
1164 | for _, parameter in pairs(params) do |
1165 | if parameter.type == 0 then |
1166 | setConditionalAnimationBoolValue(spec.animationPlayer, parameter.id, parameter.value) |
1167 | elseif parameter.type == 1 then |
1168 | setConditionalAnimationFloatValue(spec.animationPlayer, parameter.id, parameter.value) |
1169 | end |
1170 | end |
1171 | updateConditionalAnimation(spec.animationPlayer, dt) |
1172 | -- local x,y,z = getWorldTranslation(self.rootNode) |
1173 | -- conditionalAnimationDebugDraw(spec.animationPlayer, x,y,z) |
1174 | end |
1175 | local isEntered = self.getIsEntered ~= nil and self:getIsEntered() |
1176 | local isControlled = self.getIsControlled ~= nil and self:getIsControlled() |
1177 | |
1178 | if isEntered or isControlled then |
1179 | -- rider animation |
1180 | local character = self:getVehicleCharacter() |
1181 | if character ~= nil and character.animationCharsetId ~= nil and character.animationPlayer ~= nil then |
1182 | for _, parameter in pairs(params) do |
1183 | if parameter.type == 0 then |
1184 | setConditionalAnimationBoolValue(character.animationPlayer, parameter.id, parameter.value) |
1185 | elseif parameter.type == 1 then |
1186 | setConditionalAnimationFloatValue(character.animationPlayer, parameter.id, parameter.value) |
1187 | end |
1188 | end |
1189 | updateConditionalAnimation(character.animationPlayer, dt) |
1190 | -- local x,y,z = getWorldTranslation(self.rootNode) |
1191 | -- conditionalAnimationDebugDraw(character.animationPlayer, x,y,z) |
1192 | end |
1193 | end |
1194 | self:updateFootsteps(dt, math.abs(speed)) |
1195 | end |
1435 | function Rideable:updateFootsteps(dt, speed) |
1436 | local spec = self.spec_rideable |
1437 | local epsilon = 0.001 |
1438 | |
1439 | if speed > epsilon then |
1440 | local dirX, _, dirZ = localDirectionToWorld(self.rootNode, 0, 0, 1) |
1441 | local rotY = MathUtil.getYRotationFromDirection(dirX, dirZ) |
1442 | for k, hoofInfo in pairs(spec.hooves) do |
1443 | local posX, posY, posZ = getWorldTranslation(hoofInfo.node) |
1444 | spec.groundRaycastResult.object = 0 |
1445 | spec.groundRaycastResult.y = posY - 1 |
1446 | raycastClosest(posX, posY + Rideable.GROUND_RAYCAST_OFFSET, posZ, 0.0, -1.0, 0.0, "groundRaycastCallback", Rideable.GROUND_RAYCAST_MAXDISTANCE, self, Rideable.GROUND_RAYCAST_COLLISIONMASK) |
1447 | |
1448 | local hitTerrain = spec.groundRaycastResult.object == g_currentMission.terrainRootNode |
1449 | local terrainY = spec.groundRaycastResult.y |
1450 | local onGround = ((posY - terrainY) < 0.05) |
1451 | |
1452 | -- DebugUtil.drawDebugNode(hoofInfo.node, string.format("[%s] (%.6f/%.6f|%s)", getName(hoofInfo.node), posY, terrainY, tostring(((posY - terrainY) < 0.05)))) |
1453 | if onGround and not hoofInfo.onGround then |
1454 | local r, g, b, _, _ = getTerrainAttributesAtWorldPos(g_currentMission.terrainRootNode, posX, posY, posZ, true, true, true, true, false) |
1455 | hoofInfo.onGround = true |
1456 | -- particles |
1457 | if spec.inputValues.currentGait < Rideable.GAITTYPES.CANTER then |
1458 | if hoofInfo.psSlow.emitterShape ~= nil then |
1459 | ParticleUtil.resetNumOfEmittedParticles(hoofInfo.psSlow) |
1460 | ParticleUtil.setEmittingState(hoofInfo.psSlow, true) |
1461 | setShaderParameter(hoofInfo.psSlow.shape, "psColor", r, g, b, 1, false) |
1462 | |
1463 | -- emittershapes are linked to world rootnode |
1464 | setWorldTranslation(hoofInfo.psSlow.emitterShape, posX, terrainY, posZ) |
1465 | setWorldRotation(hoofInfo.psSlow.emitterShape, 0, rotY, 0) |
1466 | end |
1467 | else |
1468 | if hoofInfo.psFast.emitterShape ~= nil then |
1469 | ParticleUtil.resetNumOfEmittedParticles(hoofInfo.psFast) |
1470 | ParticleUtil.setEmittingState(hoofInfo.psFast, true) |
1471 | setShaderParameter(hoofInfo.psFast.shape, "psColor", r, g, b, 1, false) |
1472 | |
1473 | -- emittershapes are linked to world rootnode |
1474 | setWorldTranslation(hoofInfo.psFast.emitterShape, posX, terrainY, posZ) |
1475 | setWorldRotation(hoofInfo.psSlow.emitterShape, 0, rotY, 0) |
1476 | end |
1477 | end |
1478 | |
1479 | local sample = self:getHoofSurfaceSound(posX, posY, posZ, hitTerrain) |
1480 | if sample ~= nil then |
1481 | hoofInfo.sampleDebug = string.format("%s - %s", sample.sampleName, sample.filename) |
1482 | g_soundManager:playSample(sample) |
1483 | end |
1484 | |
1485 | elseif not onGround and hoofInfo.onGround then |
1486 | hoofInfo.onGround = false |
1487 | if hoofInfo.psSlow.emitterShape ~= nil then |
1488 | ParticleUtil.setEmittingState(hoofInfo.psSlow, false) |
1489 | end |
1490 | if hoofInfo.psFast.emitterShape ~= nil then |
1491 | ParticleUtil.setEmittingState(hoofInfo.psFast, false) |
1492 | end |
1493 | end |
1494 | end |
1495 | end |
1496 | end |
1668 | function Rideable:updateInputText() |
1669 | local spec = self.spec_rideable |
1670 | |
1671 | if spec.inputValues.currentGait == Rideable.GAITTYPES.BACKWARDS then |
1672 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_stop")) |
1673 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1674 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1675 | |
1676 | g_inputBinding:setActionEventActive(spec.brakeEventId, false) |
1677 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, false) |
1678 | |
1679 | g_inputBinding:setActionEventActive(spec.jumpEventId, false) |
1680 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, false) |
1681 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.STILL then |
1682 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_walk")) |
1683 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1684 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1685 | |
1686 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_walkBackwards")) |
1687 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1688 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1689 | |
1690 | g_inputBinding:setActionEventActive(spec.jumpEventId, false) |
1691 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, false) |
1692 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.WALK then |
1693 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_trot")) |
1694 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1695 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1696 | |
1697 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_stop")) |
1698 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1699 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1700 | |
1701 | g_inputBinding:setActionEventActive(spec.jumpEventId, false) |
1702 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, false) |
1703 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.TROT then |
1704 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_canter")) |
1705 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1706 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1707 | |
1708 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_walk")) |
1709 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1710 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1711 | |
1712 | g_inputBinding:setActionEventActive(spec.jumpEventId, false) |
1713 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, false) |
1714 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.CANTER then |
1715 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_gallop")) |
1716 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1717 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1718 | |
1719 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_trot")) |
1720 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1721 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1722 | |
1723 | g_inputBinding:setActionEventText(spec.jumpEventId, g_i18n:getText("input_JUMP")) |
1724 | g_inputBinding:setActionEventActive(spec.jumpEventId, true) |
1725 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, true) |
1726 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.GALLOP then |
1727 | g_inputBinding:setActionEventActive(spec.acceletateEventId, false) |
1728 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, false) |
1729 | |
1730 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_canter")) |
1731 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1732 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1733 | |
1734 | g_inputBinding:setActionEventText(spec.jumpEventId, g_i18n:getText("input_JUMP")) |
1735 | g_inputBinding:setActionEventActive(spec.jumpEventId, true) |
1736 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, true) |
1737 | end |
1738 | end |
1007 | function Rideable:updateKinematic(dt) |
1008 | local spec = self.spec_rideable |
1009 | local dtInSec = dt*0.001 |
1010 | |
1011 | -- Update movement in current direction |
1012 | local desiredSpeed = spec.topSpeeds[spec.inputValues.currentGait] |
1013 | local maxSpeedChange = spec.maxAcceleration |
1014 | if desiredSpeed == 0.0 then |
1015 | maxSpeedChange = spec.maxDeceleration |
1016 | end |
1017 | maxSpeedChange = maxSpeedChange * dtInSec |
1018 | if not spec.isOnGround then |
1019 | -- reduce acceleration when in the air |
1020 | maxSpeedChange = maxSpeedChange * 0.2 |
1021 | end |
1022 | |
1023 | local speedChange = (desiredSpeed - spec.currentSpeed) |
1024 | speedChange = MathUtil.clamp(speedChange, -maxSpeedChange, maxSpeedChange) |
1025 | |
1026 | --local movement = (spec.currentSpeed + 0.5 * speedChange) * dtInSec |
1027 | if spec.haltTimer <= 0.0 then |
1028 | spec.currentSpeed = spec.currentSpeed + speedChange |
1029 | else |
1030 | spec.currentSpeed = 0.0 |
1031 | end |
1032 | |
1033 | local movement = spec.currentSpeed * dtInSec |
1034 | |
1035 | -- Update gravity / vertical movement |
1036 | local gravitySpeedChange = spec.gravity * dtInSec |
1037 | spec.currentSpeedY = spec.currentSpeedY + gravitySpeedChange |
1038 | local movementY = spec.currentSpeedY * dtInSec |
1039 | |
1040 | -- Update rotation |
1041 | local slowestSpeed = spec.topSpeeds[Rideable.GAITTYPES.WALK] |
1042 | local fastestSpeed = spec.topSpeeds[Rideable.GAITTYPES.MAX] |
1043 | |
1044 | local maxTurnSpeedChange = (MathUtil.clamp((fastestSpeed - spec.currentSpeed) / (fastestSpeed - slowestSpeed), 0, 1) * 0.4 + 0.8) -- Use smaller changes when walking slowly (between 0.8 and 1.2 rad/s^2) |
1045 | maxTurnSpeedChange = maxTurnSpeedChange * dtInSec |
1046 | if not spec.isOnGround then |
1047 | -- reduce turn acceleration when in the air |
1048 | maxTurnSpeedChange = maxTurnSpeedChange * 0.25 |
1049 | end |
1050 | |
1051 | if self.isServer then |
1052 | if not self:getIsEntered() and not self:getIsControlled() and spec.inputValues.axisSteer ~= 0.0 then |
1053 | spec.inputValues.axisSteer = 0.0 |
1054 | end |
1055 | end |
1056 | |
1057 | local desiredTurnSpeed = spec.maxTurnSpeed * spec.inputValues.axisSteer |
1058 | local turnSpeedChange = (desiredTurnSpeed - spec.currentTurnSpeed) |
1059 | turnSpeedChange = MathUtil.clamp(turnSpeedChange, -maxTurnSpeedChange, maxTurnSpeedChange) |
1060 | spec.currentTurnSpeed = spec.currentTurnSpeed + turnSpeedChange |
1061 | spec.currentTurnAngle = spec.currentTurnAngle + spec.currentTurnSpeed * dtInSec * (movement >= 0 and 1 or -1) |
1062 | |
1063 | local movementX, movementZ = math.sin(spec.currentTurnAngle) * movement, math.cos(spec.currentTurnAngle) * movement |
1064 | -- print(string.format("-- [Rideable:updateKinematic][%d]\t%.6f\t%.6f\t%.6f\t%d", g_updateLoopIndex, dt, spec.currentSpeed, movement, getPhysicsUpdateIndex())) |
1065 | self:moveCCT(movementX, movementY, movementZ, true) |
1066 | |
1067 | table.insert(spec.cctMoveQueue, {physicsIndex = getPhysicsUpdateIndex(), moveX = movementX, moveY = movementY, moveZ = movementZ, dt = dt}) |
1068 | end |