826 | function AIDrivable:consoleCommandMove(distance) |
827 | local vehicles = {} |
828 | local attachedVehicles = self:getChildVehicles() |
829 | for _, vehicle in ipairs(attachedVehicles) do |
830 | vehicle:removeFromPhysics() |
831 | table.insert(vehicles, vehicle) |
832 | end |
833 | |
834 | local aiRootNode = self:getAIRootNode() |
835 | local dirX, dirY, dirZ = localDirectionToWorld(aiRootNode, 1, 0, 0) |
836 | local moveX, moveY, moveZ = dirX*distance, dirY*distance, dirZ*distance |
837 | local currentTurnRadius = self:getTurningRadiusByRotTime(self.rotatedTime) |
838 | |
839 | local gizmo = DebugGizmo.new() |
840 | local x, y, z = localToWorld(aiRootNode, currentTurnRadius, 0.05, 0) |
841 | gizmo:createWithWorldPosAndDir(x, y, z, 0, 0, 1, 0, 1, 0, "", false, nil) |
842 | g_debugManager:addPermanentElement(gizmo) |
843 | |
844 | currentTurnRadius = -currentTurnRadius + distance |
845 | self.rotatedTime = self:getSteeringRotTimeByCurvature(1 / currentTurnRadius) |
846 | |
847 | if self.rotatedTime < 0 then |
848 | self.spec_wheels.axisSide = self.rotatedTime / -self.maxRotTime / self:getSteeringDirection() |
849 | else |
850 | self.spec_wheels.axisSide = self.rotatedTime / self.minRotTime / self:getSteeringDirection() |
851 | end |
852 | |
853 | for _, vehicle in ipairs(vehicles) do |
854 | for _, component in ipairs(vehicle.components) do |
855 | x, y, z = getWorldTranslation(component.node) |
856 | setWorldTranslation(component.node, x + moveX, y + moveY, z + moveZ) |
857 | end |
858 | end |
859 | |
860 | for _, vehicle in ipairs(vehicles) do |
861 | vehicle:addToPhysics() |
862 | end |
863 | end |
300 | function AIDrivable:createAgent(helperIndex) |
301 | if self.isServer then |
302 | local spec = self.spec_aiDrivable |
303 | |
304 | -- first update the ai attachments which may have influence on the agent size |
305 | self:updateAIAgentAttachments() |
306 | local trailerData = spec.attachmentsTrailerOffsetData |
307 | |
308 | local navigationMapId = g_currentMission.aiSystem:getNavigationMap() |
309 | local agent = spec.agentInfo |
310 | local width, length, lengthOffset, frontOffset = self:getAIAgentSize() |
311 | local maxBrakeAcceleration = self:getAIAgentMaxBrakeAcceleration() |
312 | local maxCentripedalAcceleration = agent.maxCentripedalAcceleration |
313 | local minTurningRadius = self:getAITurningRadius(agent.maxTurningRadius or self.maxTurningRadius) |
314 | local minLandingTurningRadius = minTurningRadius -- turning radius limit used when approaching the goal |
315 | local allowBackwards = self:getAIAllowsBackwards() |
316 | |
317 | spec.agentId = createVehicleNavigationAgent(navigationMapId, minTurningRadius, minLandingTurningRadius, allowBackwards, width, length, lengthOffset, frontOffset, maxBrakeAcceleration, maxCentripedalAcceleration, trailerData) |
318 | |
319 | self:setAIVehicleObstacleStateDirty() |
320 | |
321 | if g_currentMission.aiSystem.debugEnabled then |
322 | if spec.debugVehicle ~= nil then |
323 | spec.debugVehicle:delete() |
324 | end |
325 | spec.debugVehicle = AIDebugVehicle.new(self, {math.random(), math.random(), math.random()}) |
326 | |
327 | if spec.debugDump ~= nil then |
328 | spec.debugDump:delete() |
329 | end |
330 | |
331 | spec.debugDump = AIDebugDump.new(self, spec.agentId) |
332 | spec.debugDump:startRecording(minTurningRadius, allowBackwards, width, length, lengthOffset, frontOffset, maxBrakeAcceleration, maxCentripedalAcceleration) |
333 | end |
334 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
335 | enableVehicleNavigationAgentDebugRendering(spec.agentId, true) |
336 | end |
337 | end |
338 | end |
487 | function AIDrivable:drawDebugAIAgent() |
488 | local spec = self.spec_aiDrivable |
489 | local aiRootNode = self:getAIRootNode() |
490 | local groundOffset = 0.05 |
491 | |
492 | local width, length, lengthOffset, frontOffset, height = self:getAIAgentSize() |
493 | spec.debugSizeBox:createWithNode(aiRootNode, width*0.5, height*0.5, length*0.5, 0, height*0.5, lengthOffset) |
494 | spec.debugSizeBox:draw() |
495 | |
496 | local fx, _, fz = localToWorld(aiRootNode, 0, 0, frontOffset) |
497 | local dirX, dirY, dirZ = localDirectionToWorld(aiRootNode, 0, 0, 1) |
498 | local upX, upY, upZ = localDirectionToWorld(aiRootNode, 0, 1, 0) |
499 | local fy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, fx, 0, fz) + 0.05 |
500 | spec.debugFrontMarker:createWithWorldPosAndDir(fx, fy, fz, dirX, dirY, dirZ, upX, upY, upZ, "FrontMarker", false, nil, 3) |
501 | spec.debugFrontMarker:draw() |
502 | |
503 | local x, y, z = getWorldTranslation(aiRootNode) |
504 | if spec.isRunning then |
505 | local text |
506 | if spec.useManualDriving then |
507 | text = string.format("Distance: %.2f", spec.distanceToTarget) |
508 | else |
509 | text = AIDrivable.STATES[spec.lastState] |
510 | end |
511 | |
512 | Utils.renderTextAtWorldPosition(x, y + 4, z, text , 0.015, 0, {1, 1, 1, 1}) |
513 | end |
514 | |
515 | if spec.debugVehicle ~= nil then |
516 | spec.debugVehicle:draw(y + 0.1) |
517 | end |
518 | |
519 | local sx, _, sz = localToWorld(aiRootNode, 0, 0.2, 0) |
520 | local lx, _, lz = localToWorld(aiRootNode, 20, 0.2, 0) |
521 | local rx, _, rz = localToWorld(aiRootNode, -20, 0.2, 0) |
522 | |
523 | local sy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0, sz) + groundOffset |
524 | local ly = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, lx, 0, lz) + groundOffset |
525 | local ry = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rx, 0, rz) + groundOffset |
526 | |
527 | drawDebugLine(sx, sy, sz, 1, 0, 0, lx, ly, lz, 1, 0, 0) |
528 | drawDebugLine(sx, sy, sz, 0, 1, 0, rx, ry, rz, 0, 1, 0) |
529 | |
530 | local dirX1, dirZ1 = MathUtil.vector2Normalize(lx-sx, lz-sz) |
531 | local maxTurningRadius = self.maxTurningRadius |
532 | local currentTurnRadius = self:getTurningRadiusByRotTime(self.rotatedTime) |
533 | local minRadius = self:getAITurningRadius(self.maxTurningRadius) |
534 | |
535 | local revTime = self:getSteeringRotTimeByCurvature(1 / (currentTurnRadius * (self.rotatedTime >= 0 and 1 or -1))) |
536 | |
537 | local debugString = string.format("ReferenceRadius: %.3fm\nMinRadius: %.3fm\nCalc Radius: %.3f\nRotatedTime: %.3f\nRevTime: %.3f", currentTurnRadius, maxTurningRadius, minRadius, self.rotatedTime, revTime) |
538 | Utils.renderTextAtWorldPosition(sx, sy + 5, sz, debugString, getCorrectTextSize(0.012), 0) |
539 | |
540 | local wheelSpec = self.spec_wheels |
541 | for _, wheel in ipairs(wheelSpec.wheels) do |
542 | local wsx, _, wsz = localToWorld(wheel.driveNode, 0, 0, 0) |
543 | local wlx, _, wlz = localToWorld(wheel.driveNode, 20, 0, 0) |
544 | local wrx, _, wrz = localToWorld(wheel.driveNode, -20, 0, 0) |
545 | local wsy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wsx, 0, wsz) + groundOffset |
546 | local wly = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wlx, 0, wlz) + groundOffset |
547 | local wry = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wrx, 0, wrz) + groundOffset |
548 | drawDebugLine(wsx, wsy, wsz, 1, 0, 0, wlx, wly, wlz, 1, 0, 0) |
549 | drawDebugLine(wsx, wsy, wsz, 0, 1, 0, wrx, wry, wrz, 0, 1, 0) |
550 | |
551 | -- Utils.renderTextAtWorldPosition(wsx, wsy+5, wsz, string.format("RotMin/Max: %.3f / %.3f\nSpeed: %.3f/%.3f", math.deg(wheel.rotMin), math.deg(wheel.rotMax), wheel.rotSpeed, wheel.rotSpeedNeg), getCorrectTextSize(0.012), 0) |
552 | end |
553 | |
554 | if wheelSpec.steeringCenterNode ~= nil then |
555 | DebugUtil.drawDebugNode(wheelSpec.steeringCenterNode, "SCN", false, nil) |
556 | end |
557 | |
558 | local sign = MathUtil.sign(self.rotatedTime) |
559 | local cx, cz = sx + sign*dirX1*currentTurnRadius, sz + sign*dirZ1*currentTurnRadius |
560 | DebugUtil.drawDebugGizmoAtWorldPos(cx, sy, cz, dirX1, 0, dirZ1, 0, 1, 0, "X", false, nil, 3) |
561 | end |
565 | function AIDrivable:loadAgentInfoFromXML(xmlFile, agent) |
566 | local baseSizeKey = "vehicle.ai.agent" |
567 | agent.width = xmlFile:getValue(baseSizeKey .. "#width", Vehicle.DEFAULT_SIZE.width) |
568 | agent.length = xmlFile:getValue(baseSizeKey .. "#length", Vehicle.DEFAULT_SIZE.length) |
569 | agent.height = xmlFile:getValue(baseSizeKey .. "#height", Vehicle.DEFAULT_SIZE.height) |
570 | agent.lengthOffset = xmlFile:getValue(baseSizeKey .. "#lengthOffset", Vehicle.DEFAULT_SIZE.lengthOffset) |
571 | agent.frontOffset = xmlFile:getValue(baseSizeKey .. "#frontOffset", 3) |
572 | agent.maxBrakeAcceleration = xmlFile:getValue(baseSizeKey .. "#maxBrakeAcceleration", 5) |
573 | agent.maxCentripedalAcceleration = xmlFile:getValue(baseSizeKey .. "#maxCentripedalAcceleration", 1) |
574 | agent.maxTurningRadius = xmlFile:getValue(baseSizeKey .. "#maxTurningRadius") |
575 | |
576 | -- check configurations for changed agent values |
577 | for name, id in pairs(self.configurations) do |
578 | local specializationKey = g_configurationManager:getConfigurationAttribute(name, "xmlKey") |
579 | if specializationKey ~= nil then |
580 | specializationKey = "." .. specializationKey |
581 | else |
582 | specializationKey = "" |
583 | end |
584 | local key = string.format("vehicle%s.%sConfigurations.%sConfiguration(%d).aiAgent", specializationKey, name, name , id - 1) |
585 | agent.width = math.max(agent.width, xmlFile:getValue(key .. "#width", 0)) |
586 | agent.length = math.max(agent.length, xmlFile:getValue(key .. "#length", 0)) |
587 | agent.height = math.max(agent.height, xmlFile:getValue(key .. "#height", 0)) |
588 | agent.lengthOffset = xmlFile:getValue(key .. "#lengthOffset", agent.lengthOffset) |
589 | agent.frontOffset = xmlFile:getValue(key .. "#frontOffset", agent.frontOffset) |
590 | agent.maxBrakeAcceleration = math.min(xmlFile:getValue(key .. "#maxBrakeAcceleration", agent.maxBrakeAcceleration)) |
591 | agent.maxCentripedalAcceleration = math.min(xmlFile:getValue(key .. "#maxCentripedalAcceleration", agent.maxCentripedalAcceleration)) |
592 | agent.maxTurningRadius = xmlFile:getValue(key .. "#maxTurningRadius", agent.maxTurningRadius) |
593 | end |
594 | end |
202 | function AIDrivable:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
203 | local spec = self.spec_aiDrivable |
204 | |
205 | if self.isServer then |
206 | if spec.isRunning then |
207 | |
208 | -- if vehicle was blocked in the last frame we want to keep the block state as long as last speed < 4kmh |
209 | -- to avoid issues with set block or unblock each frame |
210 | local isStillBlocked = spec.lastIsBlocked and self:getLastSpeed() < 5 |
211 | -- check if vehicle is currently blocked |
212 | local isCurrentlyBlocked = math.abs(spec.lastMaxSpeed) > 0 and self:getLastSpeed() < 1 |
213 | |
214 | if isCurrentlyBlocked or isStillBlocked then |
215 | spec.stuckTime = spec.stuckTime + dt |
216 | else |
217 | spec.stuckTime = 0 |
218 | end |
219 | |
220 | local isBlocked = spec.stuckTime > 5000 |
221 | |
222 | local aiRootNode = self:getAIRootNode() |
223 | local x, y, z = getWorldTranslation(aiRootNode) |
224 | spec.distanceToTarget = MathUtil.vector2Length(x - spec.targetX, z - spec.targetZ) |
225 | local lastSpeed = self.lastSpeedReal * self.movingDirection * 1000 |
226 | |
227 | local maxSpeed = math.min(spec.maxSpeed, self:getCruiseControlSpeed()) |
228 | |
229 | if spec.useManualDriving then |
230 | local tx, _, tz = worldToLocal(aiRootNode, spec.targetX, spec.targetY, spec.targetZ) |
231 | |
232 | AIVehicleUtil.driveToPoint(self, dt, 1, true, true, tx, tz, maxSpeed, false) |
233 | |
234 | spec.lastMaxSpeed = maxSpeed |
235 | |
236 | if spec.distanceToTarget < 0.5 then |
237 | self:reachedAITarget() |
238 | end |
239 | else |
240 | local dirX, dirY, dirZ = localDirectionToWorld(aiRootNode, 0, 0, 1) |
241 | |
242 | self:updateAIAgentPoseData() |
243 | |
244 | local curvature, maxSpeedCurvature, status = getVehicleNavigationAgentNextCurvature(spec.agentId, spec.poseData, lastSpeed) |
245 | |
246 | if spec.debugDump ~= nil then |
247 | spec.debugDump:addData(dt, x, y, z, dirX, dirY, dirZ, lastSpeed, curvature, maxSpeed, status) |
248 | end |
249 | |
250 | -- update driving |
251 | if status == AgentState.DRIVING then |
252 | maxSpeed = math.min(maxSpeedCurvature * 3.6, maxSpeed) |
253 | AIVehicleUtil.driveAlongCurvature(self, dt, curvature, maxSpeed, 1) |
254 | if maxSpeed == 0 then |
255 | isBlocked = false |
256 | end |
257 | elseif status == AgentState.PLANNING then |
258 | isBlocked = false |
259 | self:brake(1) |
260 | elseif status == AgentState.BLOCKED then |
261 | isBlocked = true |
262 | self:brake(1) |
263 | elseif status == AgentState.TARGET_REACHED then |
264 | isBlocked = false |
265 | self:reachedAITarget() |
266 | elseif status == AgentState.NOT_REACHABLE then |
267 | isBlocked = false |
268 | self:stopCurrentAIJob(AIMessageErrorNotReachable.new()) |
269 | end |
270 | |
271 | spec.lastState = status |
272 | spec.lastMaxSpeed = maxSpeed |
273 | end |
274 | |
275 | if spec.debugVehicle ~= nil then |
276 | spec.debugVehicle:update(dt) |
277 | end |
278 | |
279 | if isBlocked and not spec.lastIsBlocked then |
280 | g_server:broadcastEvent(AIVehicleIsBlockedEvent.new(self, true), true, nil, self) |
281 | elseif not isBlocked and spec.lastIsBlocked then |
282 | g_server:broadcastEvent(AIVehicleIsBlockedEvent.new(self, false), true, nil, self) |
283 | end |
284 | |
285 | spec.lastIsBlocked = isBlocked |
286 | |
287 | SpecializationUtil.raiseEvent(self, "onAIDriveableActive") |
288 | end |
289 | |
290 | if spec.vehicleObstacleId ~= nil then |
291 | local speed = self.lastSpeedReal * 1000 |
292 | local poses = self:getAIAgentPoses(speed) |
293 | g_currentMission.aiSystem:setVehiclObstaclePose(spec.vehicleObstacleId, speed, poses) |
294 | end |
295 | end |
296 | end |
49 | function AIDrivable.postInitSpecialization() |
50 | local schema = Vehicle.xmlSchema |
51 | for name, _ in pairs(g_configurationManager:getConfigurations()) do |
52 | local specializationKey = g_configurationManager:getConfigurationAttribute(name, "xmlKey") |
53 | if specializationKey ~= nil then |
54 | specializationKey = "." .. specializationKey |
55 | else |
56 | specializationKey = "" |
57 | end |
58 | |
59 | local configrationsKey = string.format("vehicle%s.%sConfigurations", specializationKey, name) |
60 | local configrationKey = string.format("%s.%sConfiguration(?)", configrationsKey, name) |
61 | |
62 | schema:setXMLSharedRegistration("configAIAgent", configrationKey) |
63 | schema:register(XMLValueType.FLOAT, configrationKey .. ".aiAgent#width", "ai width of the vehicle when loaded in this configuration") |
64 | schema:register(XMLValueType.FLOAT, configrationKey .. ".aiAgent#length", "ai length of the vehicle when loaded in this configuration") |
65 | schema:register(XMLValueType.FLOAT, configrationKey .. ".aiAgent#height", "ai height of the vehicle when loaded in this configuration") |
66 | schema:register(XMLValueType.FLOAT, configrationKey .. ".aiAgent#lengthOffset", "length offset") |
67 | schema:register(XMLValueType.FLOAT, configrationKey .. ".aiAgent#frontOffset", "front offset") |
68 | schema:register(XMLValueType.FLOAT, configrationKey .. ".aiAgent#maxBrakeAcceleration", "AI vehicle max brake acceleration") |
69 | schema:register(XMLValueType.FLOAT, configrationKey .. ".aiAgent#maxCentripedalAcceleration", "AI vehicle max centripedal acceleration") |
70 | schema:register(XMLValueType.FLOAT, configrationKey .. ".aiAgent#maxTurningRadius", "Max. turning radius (overwrites value detected from ackermann steering)") |
71 | schema:setXMLSharedRegistration() |
72 | end |
73 | end |
87 | function AIDrivable.registerFunctions(vehicleType) |
88 | SpecializationUtil.registerFunction(vehicleType, "consoleCommandSetTurnRadius", AIDrivable.consoleCommandSetTurnRadius) |
89 | SpecializationUtil.registerFunction(vehicleType, "consoleCommandMove", AIDrivable.consoleCommandMove) |
90 | SpecializationUtil.registerFunction(vehicleType, "consoleCommandClearPath", AIDrivable.consoleCommandClearPath) |
91 | SpecializationUtil.registerFunction(vehicleType, "createAgent", AIDrivable.createAgent) |
92 | SpecializationUtil.registerFunction(vehicleType, "deleteAgent", AIDrivable.deleteAgent) |
93 | SpecializationUtil.registerFunction(vehicleType, "setAITarget", AIDrivable.setAITarget) |
94 | SpecializationUtil.registerFunction(vehicleType, "unsetAITarget", AIDrivable.unsetAITarget) |
95 | SpecializationUtil.registerFunction(vehicleType, "reachedAITarget", AIDrivable.reachedAITarget) |
96 | SpecializationUtil.registerFunction(vehicleType, "getAIRootNode", AIDrivable.getAIRootNode) |
97 | SpecializationUtil.registerFunction(vehicleType, "getAIAllowsBackwards", AIDrivable.getAIAllowsBackwards) |
98 | SpecializationUtil.registerFunction(vehicleType, "drawDebugAIAgent", AIDrivable.drawDebugAIAgent) |
99 | SpecializationUtil.registerFunction(vehicleType, "loadAgentInfoFromXML", AIDrivable.loadAgentInfoFromXML) |
100 | SpecializationUtil.registerFunction(vehicleType, "getAIAgentSize", AIDrivable.getAIAgentSize) |
101 | SpecializationUtil.registerFunction(vehicleType, "getAIAgentMaxBrakeAcceleration", AIDrivable.getAIAgentMaxBrakeAcceleration) |
102 | SpecializationUtil.registerFunction(vehicleType, "updateAIAgentAttachments", AIDrivable.updateAIAgentAttachments) |
103 | SpecializationUtil.registerFunction(vehicleType, "addAIAgentAttachment", AIDrivable.addAIAgentAttachment) |
104 | SpecializationUtil.registerFunction(vehicleType, "startNewAIAgentAttachmentChain", AIDrivable.startNewAIAgentAttachmentChain) |
105 | SpecializationUtil.registerFunction(vehicleType, "updateAIAgentAttachmentOffsetData", AIDrivable.updateAIAgentAttachmentOffsetData) |
106 | SpecializationUtil.registerFunction(vehicleType, "updateAIAgentPoseData", AIDrivable.updateAIAgentPoseData) |
107 | SpecializationUtil.registerFunction(vehicleType, "prepareForAIDriving", AIDrivable.prepareForAIDriving) |
108 | SpecializationUtil.registerFunction(vehicleType, "getAITurningRadius", AIDrivable.getAITurningRadius) |
109 | end |
363 | function AIDrivable:setAITarget(task, x, y, z, dirX, dirY, dirZ, maxSpeed, useManualDriving) |
364 | local spec = self.spec_aiDrivable |
365 | |
366 | local aiRootNode = self:getAIRootNode() |
367 | local cx, cy, cz = getWorldTranslation(aiRootNode) |
368 | local cDirX, cDirY, cDirZ = localDirectionToWorld(aiRootNode, 0, 0, 1) |
369 | |
370 | spec.useManualDriving = Utils.getNoNil(useManualDriving, false) |
371 | |
372 | spec.isRunning = true |
373 | spec.task = task |
374 | spec.maxSpeed = maxSpeed or math.huge |
375 | spec.targetX, spec.targetY, spec.targetZ = x, y, z |
376 | spec.targetDirX, spec.targetDirY, spec.targetDirZ = dirX or 0, dirY, dirZ or 0 |
377 | spec.distanceToTarget = MathUtil.vector2Length(cx-x, cz-z) |
378 | |
379 | if not spec.useManualDriving and self.isServer then |
380 | setVehicleNavigationAgentTarget(spec.agentId, x, y, z, dirX, dirY, dirZ) |
381 | end |
382 | |
383 | if spec.debugVehicle ~= nil then |
384 | spec.debugVehicle:setTarget(x, y, z, dirX, dirY, dirZ) |
385 | end |
386 | |
387 | if spec.debugDump ~= nil then |
388 | spec.debugDump:setTarget(x, y, z, dirX, dirY, dirZ, cx, cy, cz, cDirX, cDirY, cDirZ, spec.maxSpeed) |
389 | end |
390 | |
391 | SpecializationUtil.raiseEvent(self, "onAIDriveableStart") |
392 | end |
670 | function AIDrivable:updateAIAgentAttachmentOffsetData() |
671 | local spec = self.spec_aiDrivable |
672 | local _, agentLength, lengthOffset, _ = self:getAIAgentSize() |
673 | local numTrailers = 0 |
674 | for ci=1, #spec.attachmentChains do |
675 | local chainAttachments = spec.attachmentChains[ci] |
676 | local isDynamicChain = true |
677 | local isStaticChain = true |
678 | |
679 | local parentSteeringCenterNode = self.components[1].node |
680 | local parentSteeringCenterOffsetZ = lengthOffset |
681 | for i=1, #chainAttachments do |
682 | local agentAttachment = chainAttachments[i] |
683 | if agentAttachment.rotCenterNode ~= nil and isDynamicChain then |
684 | if numTrailers < AIDrivable.TRAILER_LIMIT then |
685 | local jointNode = agentAttachment.jointNode or agentAttachment.jointNodeDynamic |
686 | local attacherVehicleJointNode = agentAttachment.attacherVehicleJointNode or jointNode |
687 | if attacherVehicleJointNode ~= nil and jointNode ~= nil then |
688 | local x1, _, z1 = getWorldTranslation(attacherVehicleJointNode) |
689 | local x2, _, z2 = localToWorld(parentSteeringCenterNode, 0, 0, -parentSteeringCenterOffsetZ) |
690 | local tractorHitchOffset = -MathUtil.vector2Length(x1-x2, z1-z2) |
691 | |
692 | x1, _, z1 = getWorldTranslation(jointNode) |
693 | x2, _, z2 = getWorldTranslation(agentAttachment.rotCenterNode) |
694 | local trailerHitchOffset = MathUtil.vector2Length(x1-x2, z1-z2) |
695 | |
696 | -- TODO: still wip until fixed in the engine |
697 | local centerOffset = agentAttachment.lengthOffset + (agentLength * 0.5) - (agentAttachment.length * 0.5) |
698 | local hasCollision = 0-- agentAttachment.hasCollision and 1 or 0 |
699 | |
700 | table.insert(spec.attachmentsTrailerOffsetData, tractorHitchOffset) |
701 | table.insert(spec.attachmentsTrailerOffsetData, trailerHitchOffset) |
702 | table.insert(spec.attachmentsTrailerOffsetData, centerOffset) |
703 | table.insert(spec.attachmentsTrailerOffsetData, hasCollision) |
704 | |
705 | parentSteeringCenterNode = agentAttachment.rotCenterNode |
706 | parentSteeringCenterOffsetZ = 0 |
707 | numTrailers = numTrailers + 1 |
708 | end |
709 | isStaticChain = false |
710 | end |
711 | elseif isStaticChain then |
712 | if numTrailers > 0 then |
713 | isDynamicChain = false |
714 | end |
715 | |
716 | if agentAttachment.rotCenterNode == nil then |
717 | local aiRootNode = self:getAIRootNode() |
718 | local _, _, z1 = localToLocal(agentAttachment.rootNode, aiRootNode, 0, 0, agentAttachment.length * 0.5) |
719 | local _, _, z2 = localToLocal(agentAttachment.rootNode, aiRootNode, 0, 0, -agentAttachment.length * 0.5) |
720 | |
721 | local minZ = -spec.agentInfo.length * 0.5 + spec.agentInfo.lengthOffset |
722 | local maxZ = spec.agentInfo.length * 0.5 + spec.agentInfo.lengthOffset |
723 | local zDiffNeg = math.min(0, z1 - minZ, z2 - minZ) |
724 | local zDiffPos = math.max(0, z1 - maxZ, z2 - maxZ) |
725 | |
726 | spec.attachmentsMaxLengthOffsetPos = math.max(spec.attachmentsMaxLengthOffsetPos, zDiffPos) |
727 | spec.attachmentsMaxLengthOffsetNeg = math.min(spec.attachmentsMaxLengthOffsetNeg, zDiffNeg) |
728 | end |
729 | end |
730 | end |
731 | end |
732 | end |