29 | function CrabSteering:load(savegame) |
30 | self.setCrabSteering = SpecializationUtil.callSpecializationsFunction("setCrabSteering"); |
31 | |
32 | self.updateSteeringAngle = CrabSteering.updateSteeringAngle; |
33 | self.updateArticulatedAxisRotation = CrabSteering.updateArticulatedAxisRotation |
34 | |
35 | self.crabSteering = {}; |
36 | self.crabSteering.state = 1; |
37 | self.crabSteering.stateMax = -1; |
38 | |
39 | self.crabSteering.distFromCompJointToCenterOfBackWheels = getXMLFloat(self.xmlFile, "vehicle.crabSteering#distFromCompJointToCenterOfBackWheels"); |
40 | self.crabSteering.aiSteeringModeIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.crabSteering#aiSteeringModeIndex"), 1); |
41 | self.crabSteering.toggleSpeedFactor = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.crabSteering#toggleSpeedFactor"), 1); |
42 | |
43 | self.crabSteeringCheckedInputs = {}; |
44 | table.insert(self.crabSteeringCheckedInputs, InputBinding.TOGGLE_CRABSTEERING); |
45 | |
46 | local baseKey = "vehicle.crabSteering"; |
47 | |
48 | self.crabSteering.steeringModes = {}; |
49 | local i=0; |
50 | while true do |
51 | local key = string.format("%s.steeringMode(%d)", baseKey, i); |
52 | if not hasXMLProperty(self.xmlFile, key) then |
53 | break; |
54 | end |
55 | local entry = {}; |
56 | entry.name = g_i18n:getText( getXMLString(self.xmlFile, key .. "#name") ); |
57 | |
58 | local inputBindingName = getXMLString(self.xmlFile, key .. "#inputBindingName"); |
59 | if inputBindingName ~= nil then |
60 | if InputBinding[inputBindingName] ~= nil then |
61 | entry.inputBinding = InputBinding[inputBindingName]; |
62 | end |
63 | end |
64 | |
65 | entry.wheels = {}; |
66 | local j=0; |
67 | while true do |
68 | local wheelKey = string.format("%s.wheel(%d)", key, j); |
69 | if not hasXMLProperty(self.xmlFile, wheelKey) then |
70 | break; |
71 | end |
72 | local wheelEntry = {}; |
73 | wheelEntry.wheelIndex = getXMLInt(self.xmlFile, wheelKey .. "#index"); |
74 | wheelEntry.offset = math.rad( Utils.getNoNil(getXMLFloat(self.xmlFile, wheelKey .. "#offset"), 0) ); |
75 | wheelEntry.locked = Utils.getNoNil(getXMLBool(self.xmlFile, wheelKey .. "#locked"), false); |
76 | |
77 | if self.wheels[wheelEntry.wheelIndex] ~= nil then |
78 | self.wheels[wheelEntry.wheelIndex].steeringOffset = 0; |
79 | |
80 | self.wheels[wheelEntry.wheelIndex].rotSpeedBackUp = self.wheels[wheelEntry.wheelIndex].rotSpeed; |
81 | else |
82 | print("Error: CrabSteering, the wheelIndex '"..tostring(wheelEntry.wheelIndex).."' is not valid!"); |
83 | end |
84 | |
85 | table.insert(entry.wheels, wheelEntry); |
86 | j=j+1; |
87 | end |
88 | |
89 | if self.articulatedAxis ~= nil then |
90 | self.articulatedAxis.currentOffset = 0; |
91 | self.articulatedAxis.rotSpeedBackUp = self.articulatedAxis.rotSpeed; |
92 | entry.articulatedAxis = {}; |
93 | entry.articulatedAxis.offset = math.rad( Utils.getNoNil(getXMLFloat(self.xmlFile, key .. ".articulatedAxis(0)#offset"), 0) ); |
94 | entry.articulatedAxis.locked = Utils.getNoNil(getXMLBool(self.xmlFile, key .. ".articulatedAxis(0)#locked"), false); |
95 | local vector = { Utils.getVectorFromString(getXMLString(self.xmlFile, key .. ".articulatedAxis(0)#wheelIndices")) }; |
96 | if vector[1] ~= nil then |
97 | entry.articulatedAxis.wheelIndices = vector; |
98 | end |
99 | end |
100 | |
101 | entry.animations = {}; |
102 | local j=0; |
103 | while true do |
104 | local animKey = string.format("%s.animation(%d)", key, j); |
105 | if not hasXMLProperty(self.xmlFile, animKey) then |
106 | break; |
107 | end |
108 | local animName = getXMLString(self.xmlFile, animKey .. "#name"); |
109 | local animSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, animKey .. "#speed"), 1.0); |
110 | local stopTime = getXMLFloat(self.xmlFile, animKey .. "#stopTime"); |
111 | if animName ~= nil and self.animations[animName] ~= nil then |
112 | table.insert(entry.animations, {animName=animName, animSpeed=animSpeed, stopTime=stopTime}); |
113 | end |
114 | j=j+1; |
115 | end |
116 | |
117 | table.insert(self.crabSteering.steeringModes, entry); |
118 | i=i+1; |
119 | end |
120 | self.crabSteering.stateMax = table.getn(self.crabSteering.steeringModes); |
121 | if self.crabSteering.stateMax > ((2^CrabSteering.STEERING_SEND_NUM_BITS) - 1) then |
122 | print("Error: CrabSteering only supports "..((2^CrabSteering.STEERING_SEND_NUM_BITS) - 1).." steering modes!"); |
123 | end |
124 | |
125 | if self.crabSteering.stateMax > 0 then |
126 | self:setCrabSteering(1, true); |
127 | end |
128 | end |
243 | function CrabSteering:setCrabSteering(state, noEventSend) |
244 | if noEventSend == nil or noEventSend == false then |
245 | if g_server ~= nil then |
246 | g_server:broadcastEvent(SetCrabSteeringEvent:new(self, state), nil, nil, self); |
247 | else |
248 | g_client:getServerConnection():sendEvent(SetCrabSteeringEvent:new(self, state)); |
249 | end |
250 | end |
251 | |
252 | if state ~= self.crabSteering.state then |
253 | local currentMode = self.crabSteering.steeringModes[self.crabSteering.state]; |
254 | if currentMode.animations ~= nil then |
255 | for _,anim in pairs(currentMode.animations) do |
256 | local curTime = self:getAnimationTime(anim.animName); |
257 | if anim.stopTime == nil then |
258 | self:playAnimation(anim.animName, -anim.animSpeed, curTime, noEventSend); |
259 | end |
260 | end |
261 | end |
262 | local newMode = self.crabSteering.steeringModes[state]; |
263 | if newMode.animations ~= nil then |
264 | for _,anim in pairs(newMode.animations) do |
265 | local curTime = self:getAnimationTime(anim.animName); |
266 | if anim.stopTime ~= nil then |
267 | self:setAnimationStopTime(anim.animName, anim.stopTime); |
268 | local speed = 1.0; |
269 | if curTime > anim.stopTime then |
270 | speed = -1.0; |
271 | end |
272 | self:playAnimation(anim.animName, speed, curTime, noEventSend); |
273 | else |
274 | self:playAnimation(anim.animName, anim.animSpeed, curTime, noEventSend); |
275 | end |
276 | end |
277 | end |
278 | end |
279 | |
280 | self.crabSteering.state = state; |
281 | end |
302 | function CrabSteering:updateSteeringAngle(wheel, dt, steeringAngle) |
303 | if self.crabSteering.stateMax == 0 then |
304 | return steeringAngle; |
305 | end |
306 | |
307 | local currentMode = self.crabSteering.steeringModes[self.crabSteering.state]; |
308 | if currentMode.wheels == nil or table.getn(currentMode.wheels) == 0 then |
309 | return steeringAngle; |
310 | end |
311 | |
312 | for k,wheelProperties in pairs(currentMode.wheels) do |
313 | if self.wheels[wheelProperties.wheelIndex] == wheel then --and (wheel.rotSpeed ~= 0 or wheel.rotSpeedBackUp ~= nil) then |
314 | |
315 | local rotScale = math.min(1.0/(self.lastSpeed*self.speedRotScale+self.speedRotScaleOffset), 1); |
316 | local delta = dt*0.001*self.autoRotateBackSpeed*rotScale * self.crabSteering.toggleSpeedFactor; |
317 | |
318 | if wheel.steeringOffset < wheelProperties.offset then |
319 | wheel.steeringOffset = math.min(wheelProperties.offset, wheel.steeringOffset + delta); |
320 | elseif wheel.steeringOffset > wheelProperties.offset then |
321 | wheel.steeringOffset = math.max(wheelProperties.offset, wheel.steeringOffset - delta); |
322 | end |
323 | |
324 | |
325 | if not wheelProperties.locked then |
326 | |
327 | local rotSpeed = 0; |
328 | |
329 | if self.rotatedTime > 0 then |
330 | rotSpeed = (wheel.rotMax - wheel.steeringOffset) / self.wheelSteeringDuration; |
331 | if wheel.rotSpeedBackUp < 0 then |
332 | rotSpeed = (wheel.rotMin - wheel.steeringOffset) / self.wheelSteeringDuration; |
333 | end |
334 | else |
335 | rotSpeed = -(wheel.rotMin - wheel.steeringOffset) / self.wheelSteeringDuration; |
336 | if wheel.rotSpeedBackUp < 0 then |
337 | rotSpeed = -(wheel.rotMax - wheel.steeringOffset) / self.wheelSteeringDuration; |
338 | end |
339 | end |
340 | |
341 | if wheel.rotSpeed < wheel.rotSpeedBackUp then |
342 | wheel.rotSpeed = math.min(wheel.rotSpeedBackUp, wheel.rotSpeed + delta); |
343 | elseif wheel.rotSpeed > wheel.rotSpeedBackUp then |
344 | wheel.rotSpeed = math.max(wheel.rotSpeedBackUp, wheel.rotSpeed - delta); |
345 | end |
346 | local f = wheel.rotSpeed / wheel.rotSpeedBackUp; |
347 | |
348 | steeringAngle = wheel.steeringOffset + (self.rotatedTime * f * rotSpeed); |
349 | |
350 | else |
351 | |
352 | if wheel.steeringAngle > wheel.steeringOffset or steeringAngle > wheel.steeringOffset then |
353 | steeringAngle = math.max(wheel.steeringOffset, math.min(wheel.steeringAngle, steeringAngle) - delta); |
354 | elseif wheel.steeringAngle < wheel.steeringOffset or steeringAngle < wheel.steeringOffset then |
355 | steeringAngle = math.min(wheel.steeringOffset, math.max(wheel.steeringAngle, steeringAngle) + delta); |
356 | end |
357 | |
358 | if steeringAngle == wheel.steeringOffset then |
359 | wheel.rotSpeed = 0; |
360 | else |
361 | if wheel.rotSpeed < 0 then |
362 | wheel.rotSpeed = math.min(0, wheel.rotSpeed + delta); |
363 | elseif wheel.rotSpeed > 0 then |
364 | wheel.rotSpeed = math.max(0, wheel.rotSpeed - delta); |
365 | end |
366 | end |
367 | |
368 | end |
369 | |
370 | steeringAngle = Utils.clamp(steeringAngle, wheel.rotMin, wheel.rotMax); |
371 | |
372 | break; |
373 | end |
374 | end |
375 | |
376 | return steeringAngle; |
377 | end |
384 | function CrabSteering:updateArticulatedAxisRotation(steeringAngle, dt) |
385 | if self.crabSteering.stateMax == 0 then |
386 | return steeringAngle; |
387 | end |
388 | |
389 | if not self.isServer then |
390 | return self.articulatedAxis.curRot; |
391 | end |
392 | |
393 | local currentMode = self.crabSteering.steeringModes[self.crabSteering.state]; |
394 | if currentMode.articulatedAxis == nil then |
395 | return steeringAngle; |
396 | end |
397 | |
398 | -- |
399 | local rotScale = math.min(1.0/(self.lastSpeed*self.speedRotScale+self.speedRotScaleOffset), 1); |
400 | local delta = dt*0.001*self.autoRotateBackSpeed*rotScale * self.crabSteering.toggleSpeedFactor; |
401 | |
402 | if self.articulatedAxis.currentOffset < currentMode.articulatedAxis.offset then |
403 | self.articulatedAxis.currentOffset = math.min(currentMode.articulatedAxis.offset, self.articulatedAxis.currentOffset + delta); |
404 | elseif self.articulatedAxis.currentOffset > currentMode.articulatedAxis.offset then |
405 | self.articulatedAxis.currentOffset = math.max(currentMode.articulatedAxis.offset, self.articulatedAxis.currentOffset - delta); |
406 | end |
407 | |
408 | -- adjust rotSpeed |
409 | if currentMode.articulatedAxis.locked then |
410 | if self.articulatedAxis.rotSpeed > 0 then |
411 | self.articulatedAxis.rotSpeed = math.max(0, self.articulatedAxis.rotSpeed - delta); |
412 | elseif self.articulatedAxis.rotSpeed < 0 then |
413 | self.articulatedAxis.rotSpeed = math.min(0, self.articulatedAxis.rotSpeed + delta); |
414 | end |
415 | else |
416 | if self.articulatedAxis.rotSpeed > self.articulatedAxis.rotSpeedBackUp then |
417 | self.articulatedAxis.rotSpeed = math.max(self.articulatedAxis.rotSpeedBackUp, self.articulatedAxis.rotSpeed - delta); |
418 | elseif self.articulatedAxis.rotSpeed < self.articulatedAxis.rotSpeedBackUp then |
419 | self.articulatedAxis.rotSpeed = math.min(self.articulatedAxis.rotSpeedBackUp, self.articulatedAxis.rotSpeed + delta); |
420 | end |
421 | end |
422 | |
423 | |
424 | local rotSpeed = 0; |
425 | if (self.rotatedTime) * (self.articulatedAxis.rotSpeedBackUp) > 0 then |
426 | rotSpeed = (self.articulatedAxis.rotMax - self.articulatedAxis.currentOffset) / self.wheelSteeringDuration; |
427 | else |
428 | rotSpeed = (self.articulatedAxis.rotMin - self.articulatedAxis.currentOffset) / self.wheelSteeringDuration; |
429 | end |
430 | |
431 | local f = math.abs(self.articulatedAxis.rotSpeed) / math.abs(self.articulatedAxis.rotSpeedBackUp); |
432 | rotSpeed = rotSpeed * f; |
433 | |
434 | steeringAngle = self.articulatedAxis.currentOffset + (math.abs(self.rotatedTime) * rotSpeed); |
435 | |
436 | |
437 | |
438 | -- optimize rotation if cutter is lowered |
439 | local adjustedRotation = false; |
440 | local cutterObject = next(self.attachedCutters); |
441 | if cutterObject ~= nil then |
442 | adjustedRotation = cutterObject:isLowered(false); |
443 | end |
444 | |
445 | if adjustedRotation and currentMode.articulatedAxis.wheelIndices ~= nil and self.crabSteering.distFromCompJointToCenterOfBackWheels ~= nil then |
446 | local curRot = Utils.sign(self.articulatedAxis.rotSpeedBackUp) * self.articulatedAxis.curRot; |
447 | |
448 | local alpha = 0; |
449 | local count = 0; |
450 | for _,wheelIndex in pairs(currentMode.articulatedAxis.wheelIndices) do |
451 | alpha = alpha + self.wheels[wheelIndex].steeringAngle; |
452 | count = count + 1; |
453 | end; |
454 | alpha = alpha / count; |
455 | alpha = alpha - curRot; |
456 | |
457 | local v = 0; |
458 | local count = 0; |
459 | for _,wheelIndex in pairs(currentMode.articulatedAxis.wheelIndices) do |
460 | local wheel = self.wheels[wheelIndex]; |
461 | local axleSpeed = getWheelShapeAxleSpeed(wheel.node, wheel.wheelShape); -- rad/sec |
462 | if wheel.hasGroundContact then |
463 | local longSlip, latSlip = getWheelShapeSlip(wheel.node, wheel.wheelShape); |
464 | local fac = 1.0 - math.min(1.0, longSlip); |
465 | v = v + fac * axleSpeed * wheel.radius; |
466 | count = count + 1; |
467 | end |
468 | end; |
469 | v = v / count; |
470 | local h = v * 0.001 * dt; |
471 | local g = math.sin(alpha) * h; |
472 | local a = math.cos(alpha) * h; |
473 | local ls = self.crabSteering.distFromCompJointToCenterOfBackWheels; |
474 | local beta = math.atan2(g, ls - a); |
475 | |
476 | steeringAngle = Utils.sign(self.articulatedAxis.rotSpeedBackUp) * (curRot + beta); |
477 | |
478 | end |
479 | |
480 | steeringAngle = math.max(self.articulatedAxis.rotMin, math.min(self.articulatedAxis.rotMax, steeringAngle)); |
481 | |
482 | return steeringAngle; |
483 | end |