54 | function CrabSteering:onLoad(savegame) |
55 | local spec = self.spec_crabSteering |
56 | |
57 | spec.state = 1 |
58 | spec.stateMax = -1 |
59 | |
60 | spec.distFromCompJointToCenterOfBackWheels = getXMLFloat(self.xmlFile, "vehicle.crabSteering#distFromCompJointToCenterOfBackWheels") |
61 | spec.aiSteeringModeIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.crabSteering#aiSteeringModeIndex"), 1) |
62 | spec.toggleSpeedFactor = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.crabSteering#toggleSpeedFactor"), 1) |
63 | |
64 | spec.currentArticulatedAxisOffset = 0 |
65 | spec.articulatedAxisOffsetChanged = false |
66 | spec.articulatedAxisLastAngle = 0 |
67 | spec.articulatedAxisChangingTime = 0 |
68 | |
69 | local baseKey = "vehicle.crabSteering" |
70 | |
71 | spec.steeringModes = {} |
72 | local i = 0 |
73 | while true do |
74 | local key = string.format("%s.steeringMode(%d)", baseKey, i) |
75 | if not hasXMLProperty(self.xmlFile, key) then |
76 | break |
77 | end |
78 | |
79 | local entry = {} |
80 | entry.name = g_i18n:getText(getXMLString(self.xmlFile, key .. "#name"), self.customEnvironment) |
81 | |
82 | local inputBindingName = getXMLString(self.xmlFile, key .. "#inputBindingName") |
83 | if inputBindingName ~= nil then |
84 | if InputAction[inputBindingName] ~= nil then |
85 | entry.inputAction = InputAction[inputBindingName] |
86 | else |
87 | g_logManager:xmlWarning(self.configFileName, "Invalid inputBindingname '%s' for '%s'", tostring(inputBindingName), key) |
88 | end |
89 | end |
90 | |
91 | entry.wheels = {} |
92 | local j = 0 |
93 | while true do |
94 | local wheelKey = string.format("%s.wheel(%d)", key, j) |
95 | if not hasXMLProperty(self.xmlFile, wheelKey) then |
96 | break |
97 | end |
98 | local wheelEntry = {} |
99 | wheelEntry.wheelIndex = getXMLInt(self.xmlFile, wheelKey .. "#index") |
100 | wheelEntry.offset = math.rad( Utils.getNoNil(getXMLFloat(self.xmlFile, wheelKey .. "#offset"), 0) ) |
101 | wheelEntry.locked = Utils.getNoNil(getXMLBool(self.xmlFile, wheelKey .. "#locked"), false) |
102 | |
103 | local wheels = self:getWheels() |
104 | if wheels[wheelEntry.wheelIndex] ~= nil then |
105 | wheels[wheelEntry.wheelIndex].steeringOffset = 0 |
106 | |
107 | wheels[wheelEntry.wheelIndex].rotSpeedBackUp = wheels[wheelEntry.wheelIndex].rotSpeed |
108 | else |
109 | g_logManager:xmlError(self.configFileName, "Invalid wheelIndex '%s' for '%s'", tostring(wheelEntry.wheelIndex), wheelKey) |
110 | end |
111 | |
112 | table.insert(entry.wheels, wheelEntry) |
113 | j = j + 1 |
114 | end |
115 | |
116 | local specArticulatedAxis = self.spec_articulatedAxis |
117 | if specArticulatedAxis ~= nil and specArticulatedAxis.componentJoint ~= nil then |
118 | entry.articulatedAxis = {} |
119 | entry.articulatedAxis.rotSpeedBackUp = specArticulatedAxis.rotSpeed |
120 | entry.articulatedAxis.offset = math.rad( Utils.getNoNil(getXMLFloat(self.xmlFile, key .. ".articulatedAxis#offset"), 0) ) |
121 | entry.articulatedAxis.locked = Utils.getNoNil(getXMLBool(self.xmlFile, key .. ".articulatedAxis#locked"), false) |
122 | entry.articulatedAxis.wheelIndices = {StringUtil.getVectorFromString(getXMLString(self.xmlFile, key .. ".articulatedAxis#wheelIndices"))} |
123 | end |
124 | |
125 | entry.animations = {} |
126 | j = 0 |
127 | while true do |
128 | local animKey = string.format("%s.animation(%d)", key, j) |
129 | if not hasXMLProperty(self.xmlFile, animKey) then |
130 | break |
131 | end |
132 | local animName = getXMLString(self.xmlFile, animKey .. "#name") |
133 | local animSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, animKey .. "#speed"), 1.0) |
134 | local stopTime = getXMLFloat(self.xmlFile, animKey .. "#stopTime") |
135 | if animName ~= nil and self:getAnimationExists(animName) then |
136 | table.insert(entry.animations, {animName=animName, animSpeed=animSpeed, stopTime=stopTime}) |
137 | else |
138 | g_logManager:xmlWarning(self.configFileName, "Invalid animation '%s' for '%s'", tostring(animName), animKey) |
139 | end |
140 | j = j + 1 |
141 | end |
142 | |
143 | table.insert(spec.steeringModes, entry) |
144 | i = i + 1 |
145 | end |
146 | |
147 | spec.stateMax = table.getn(spec.steeringModes) |
148 | if spec.stateMax > ((2^CrabSteering.STEERING_SEND_NUM_BITS) - 1) then |
149 | g_logManager:xmlError(self.configFileName, "CrabSteering only supports %d steering modes!", (2^CrabSteering.STEERING_SEND_NUM_BITS) - 1) |
150 | end |
151 | |
152 | if spec.stateMax > 0 then |
153 | self:setCrabSteering(1, true) |
154 | end |
155 | end |
494 | function CrabSteering:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) |
495 | if self.isClient then |
496 | local spec = self.spec_crabSteering |
497 | if spec.stateMax > 0 then |
498 | |
499 | self:clearActionEventsTable(spec.actionEvents) |
500 | |
501 | if isActiveForInputIgnoreSelection then |
502 | local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.TOGGLE_CRABSTEERING, self, CrabSteering.actionEventToggleCrabSteeringModes, false, true, false, true, nil) |
503 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL) |
504 | |
505 | for _, mode in pairs(spec.steeringModes) do |
506 | if mode.inputAction ~= nil then |
507 | _, actionEventId = self:addActionEvent(spec.actionEvents, mode.inputAction, self, CrabSteering.actionEventSetCrabSteeringMode, false, true, false, true, nil) |
508 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
509 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL) |
510 | end |
511 | end |
512 | end |
513 | end |
514 | end |
515 | end |
233 | function CrabSteering:setCrabSteering(state, noEventSend) |
234 | local spec = self.spec_crabSteering |
235 | |
236 | if noEventSend == nil or noEventSend == false then |
237 | if g_server ~= nil then |
238 | g_server:broadcastEvent(SetCrabSteeringEvent:new(self, state), nil, nil, self) |
239 | else |
240 | g_client:getServerConnection():sendEvent(SetCrabSteeringEvent:new(self, state)) |
241 | end |
242 | end |
243 | |
244 | if state ~= spec.state then |
245 | local currentMode = spec.steeringModes[spec.state] |
246 | if currentMode.animations ~= nil then |
247 | for _,anim in pairs(currentMode.animations) do |
248 | local curTime = self:getAnimationTime(anim.animName) |
249 | if anim.stopTime == nil then |
250 | self:playAnimation(anim.animName, -anim.animSpeed, curTime, noEventSend) |
251 | end |
252 | end |
253 | end |
254 | local newMode = spec.steeringModes[state] |
255 | if newMode.animations ~= nil then |
256 | for _,anim in pairs(newMode.animations) do |
257 | local curTime = self:getAnimationTime(anim.animName) |
258 | if anim.stopTime ~= nil then |
259 | self:setAnimationStopTime(anim.animName, anim.stopTime) |
260 | local speed = 1.0 |
261 | if curTime > anim.stopTime then |
262 | speed = -1.0 |
263 | end |
264 | self:playAnimation(anim.animName, speed, curTime, noEventSend) |
265 | else |
266 | self:playAnimation(anim.animName, anim.animSpeed, curTime, noEventSend) |
267 | end |
268 | end |
269 | end |
270 | end |
271 | |
272 | spec.state = state |
273 | end |
364 | function CrabSteering:updateArticulatedAxisRotation(steeringAngle, dt) |
365 | local spec = self.spec_crabSteering |
366 | local specArticulatedAxis = self.spec_articulatedAxis |
367 | local specDriveable = self.spec_drivable |
368 | |
369 | if spec.stateMax == 0 then |
370 | return steeringAngle |
371 | end |
372 | |
373 | if not self.isServer then |
374 | return specArticulatedAxis.curRot |
375 | end |
376 | |
377 | local currentMode = spec.steeringModes[spec.state] |
378 | if currentMode.articulatedAxis == nil then |
379 | return steeringAngle |
380 | end |
381 | |
382 | -- |
383 | local rotScale = math.min(1.0/(self.lastSpeed*specDriveable.speedRotScale+specDriveable.speedRotScaleOffset), 1) |
384 | local delta = dt*0.001*self.autoRotateBackSpeed*rotScale * spec.toggleSpeedFactor |
385 | |
386 | if spec.currentArticulatedAxisOffset < currentMode.articulatedAxis.offset then |
387 | spec.currentArticulatedAxisOffset = math.min(currentMode.articulatedAxis.offset, spec.currentArticulatedAxisOffset + delta) |
388 | elseif spec.currentArticulatedAxisOffset > currentMode.articulatedAxis.offset then |
389 | spec.currentArticulatedAxisOffset = math.max(currentMode.articulatedAxis.offset, spec.currentArticulatedAxisOffset - delta) |
390 | end |
391 | |
392 | -- adjust rotSpeed |
393 | if currentMode.articulatedAxis.locked then |
394 | if specArticulatedAxis.rotSpeed > 0 then |
395 | specArticulatedAxis.rotSpeed = math.max(0, specArticulatedAxis.rotSpeed - delta) |
396 | elseif specArticulatedAxis.rotSpeed < 0 then |
397 | specArticulatedAxis.rotSpeed = math.min(0, specArticulatedAxis.rotSpeed + delta) |
398 | end |
399 | else |
400 | if specArticulatedAxis.rotSpeed > currentMode.articulatedAxis.rotSpeedBackUp then |
401 | specArticulatedAxis.rotSpeed = math.max(currentMode.articulatedAxis.rotSpeedBackUp, specArticulatedAxis.rotSpeed - delta) |
402 | elseif specArticulatedAxis.rotSpeed < currentMode.articulatedAxis.rotSpeedBackUp then |
403 | specArticulatedAxis.rotSpeed = math.min(currentMode.articulatedAxis.rotSpeedBackUp, specArticulatedAxis.rotSpeed + delta) |
404 | end |
405 | end |
406 | |
407 | local rotSpeed |
408 | if (self.rotatedTime) * (currentMode.articulatedAxis.rotSpeedBackUp) > 0 then |
409 | rotSpeed = (specArticulatedAxis.rotMax - spec.currentArticulatedAxisOffset) / self.wheelSteeringDuration |
410 | else |
411 | rotSpeed = (specArticulatedAxis.rotMin - spec.currentArticulatedAxisOffset) / self.wheelSteeringDuration |
412 | end |
413 | |
414 | local f = math.abs(specArticulatedAxis.rotSpeed) / math.abs(currentMode.articulatedAxis.rotSpeedBackUp) |
415 | rotSpeed = rotSpeed * f |
416 | |
417 | steeringAngle = spec.currentArticulatedAxisOffset + (math.abs(self.rotatedTime) * rotSpeed) |
418 | |
419 | -- change rotation just if wheels are moving (so you don't have to steer in the opposite direction while turning on crab steering) |
420 | if table.getn(currentMode.articulatedAxis.wheelIndices) > 0 and spec.distFromCompJointToCenterOfBackWheels ~= nil and self.movingDirection >= 0 then |
421 | local wheels = self:getWheels() |
422 | |
423 | local curRot = MathUtil.sign(currentMode.articulatedAxis.rotSpeedBackUp) * specArticulatedAxis.curRot |
424 | |
425 | local alpha = 0 |
426 | local count = 0 |
427 | for _,wheelIndex in pairs(currentMode.articulatedAxis.wheelIndices) do |
428 | alpha = alpha + wheels[wheelIndex].steeringAngle |
429 | count = count + 1 |
430 | end |
431 | alpha = alpha / count |
432 | alpha = alpha - curRot |
433 | |
434 | local v = 0 |
435 | count = 0 |
436 | for _,wheelIndex in pairs(currentMode.articulatedAxis.wheelIndices) do |
437 | local wheel = wheels[wheelIndex] |
438 | local axleSpeed = getWheelShapeAxleSpeed(wheel.node, wheel.wheelShape) -- rad/sec |
439 | if wheel.hasGroundContact then |
440 | local longSlip, _ = getWheelShapeSlip(wheel.node, wheel.wheelShape) |
441 | local fac = 1.0 - math.min(1.0, longSlip) |
442 | v = v + fac * axleSpeed * wheel.radius |
443 | count = count + 1 |
444 | end |
445 | end |
446 | v = v / count |
447 | local h = v * 0.001 * dt |
448 | local g = math.sin(alpha) * h |
449 | local a = math.cos(alpha) * h |
450 | local ls = spec.distFromCompJointToCenterOfBackWheels |
451 | local beta = math.atan2(g, ls - a) |
452 | |
453 | steeringAngle = MathUtil.sign(currentMode.articulatedAxis.rotSpeedBackUp) * (curRot + beta) |
454 | |
455 | spec.articulatedAxisOffsetChanged = true |
456 | spec.articulatedAxisLastAngle = steeringAngle |
457 | else |
458 | local changingTime = spec.articulatedAxisChangingTime |
459 | if spec.articulatedAxisOffsetChanged then |
460 | changingTime = 2500 |
461 | spec.articulatedAxisOffsetChanged = false |
462 | end |
463 | |
464 | --smooth blending if steering change is from crab to normal |
465 | if changingTime > 0 then |
466 | local pos = changingTime / 2500 |
467 | steeringAngle = steeringAngle * (1-pos) + spec.articulatedAxisLastAngle * pos |
468 | spec.articulatedAxisChangingTime = changingTime - dt |
469 | end |
470 | end |
471 | |
472 | steeringAngle = math.max(specArticulatedAxis.rotMin, math.min(specArticulatedAxis.rotMax, steeringAngle)) |
473 | |
474 | return steeringAngle |
475 | end |
281 | function CrabSteering:updateSteeringAngle(wheel, dt, steeringAngle) |
282 | local spec = self.spec_crabSteering |
283 | local specWheels = self.spec_wheels |
284 | local specDriveable = self.spec_drivable |
285 | |
286 | if spec.stateMax == 0 then |
287 | return steeringAngle |
288 | end |
289 | |
290 | local currentMode = spec.steeringModes[spec.state] |
291 | if currentMode.wheels == nil or table.getn(currentMode.wheels) == 0 then |
292 | return steeringAngle |
293 | end |
294 | |
295 | for _, wheelProperties in pairs(currentMode.wheels) do |
296 | if specWheels.wheels[wheelProperties.wheelIndex] == wheel then |
297 | local rotScale = math.min(1.0/(self.lastSpeed*specDriveable.speedRotScale+specDriveable.speedRotScaleOffset), 1) |
298 | local delta = dt*0.001*self.autoRotateBackSpeed*rotScale * spec.toggleSpeedFactor |
299 | |
300 | if wheel.steeringOffset < wheelProperties.offset then |
301 | wheel.steeringOffset = math.min(wheelProperties.offset, wheel.steeringOffset + delta) |
302 | elseif wheel.steeringOffset > wheelProperties.offset then |
303 | wheel.steeringOffset = math.max(wheelProperties.offset, wheel.steeringOffset - delta) |
304 | end |
305 | |
306 | if not wheelProperties.locked then |
307 | |
308 | local rotSpeed |
309 | if self.rotatedTime > 0 then |
310 | rotSpeed = (wheel.rotMax - wheel.steeringOffset) / self.wheelSteeringDuration |
311 | if wheel.rotSpeedBackUp < 0 then |
312 | rotSpeed = (wheel.rotMin - wheel.steeringOffset) / self.wheelSteeringDuration |
313 | end |
314 | else |
315 | rotSpeed = -(wheel.rotMin - wheel.steeringOffset) / self.wheelSteeringDuration |
316 | if wheel.rotSpeedBackUp < 0 then |
317 | rotSpeed = -(wheel.rotMax - wheel.steeringOffset) / self.wheelSteeringDuration |
318 | end |
319 | end |
320 | |
321 | if wheel.rotSpeed < wheel.rotSpeedBackUp then |
322 | wheel.rotSpeed = math.min(wheel.rotSpeedBackUp, wheel.rotSpeed + delta) |
323 | elseif wheel.rotSpeed > wheel.rotSpeedBackUp then |
324 | wheel.rotSpeed = math.max(wheel.rotSpeedBackUp, wheel.rotSpeed - delta) |
325 | end |
326 | local f = wheel.rotSpeed / wheel.rotSpeedBackUp |
327 | |
328 | steeringAngle = wheel.steeringOffset + (self.rotatedTime * f * rotSpeed) |
329 | |
330 | else |
331 | |
332 | if wheel.steeringAngle > wheel.steeringOffset or steeringAngle > wheel.steeringOffset then |
333 | steeringAngle = math.max(wheel.steeringOffset, math.min(wheel.steeringAngle, steeringAngle) - delta) |
334 | elseif wheel.steeringAngle < wheel.steeringOffset or steeringAngle < wheel.steeringOffset then |
335 | steeringAngle = math.min(wheel.steeringOffset, math.max(wheel.steeringAngle, steeringAngle) + delta) |
336 | end |
337 | |
338 | if steeringAngle == wheel.steeringOffset then |
339 | wheel.rotSpeed = 0 |
340 | else |
341 | if wheel.rotSpeed < 0 then |
342 | wheel.rotSpeed = math.min(0, wheel.rotSpeed + delta) |
343 | elseif wheel.rotSpeed > 0 then |
344 | wheel.rotSpeed = math.max(0, wheel.rotSpeed - delta) |
345 | end |
346 | end |
347 | |
348 | end |
349 | |
350 | steeringAngle = MathUtil.clamp(steeringAngle, wheel.rotMin, wheel.rotMax) |
351 | |
352 | break |
353 | end |
354 | end |
355 | |
356 | return steeringAngle |
357 | end |