127 | function Rideable:onLoad(savegame) |
128 | local spec = self.spec_rideable |
129 | |
130 | -- Overwrite the Vehicle high precision setting. Otherwise the precision might lead to large jitter in speed in multiplayer |
131 | self.highPrecisionPositionSynchronization = true |
132 | |
133 | self.isVehicleSaved = false |
134 | spec.currentDirtScale = 0 |
135 | spec.abandonTimerDuration = g_gameSettings:getValue("horseAbandonTimerDuration") |
136 | spec.abandonTimer = spec.abandonTimerDuration |
137 | spec.fadeDuration = 400 |
138 | spec.isRideableRemoved = false |
139 | spec.justSpawned = true |
140 | spec.meshNode = nil |
141 | spec.hairNode = nil |
142 | -- Animation |
143 | spec.animationNode = nil |
144 | spec.charsetId = nil |
145 | spec.animationPlayer = 0 |
146 | spec.animationParameters = {} |
147 | spec.animationParameters.forwardVelocity = {id=1, value=0.0, type=1} |
148 | spec.animationParameters.verticalVelocity = {id=2, value=0.0, type=1} |
149 | spec.animationParameters.yawVelocity = {id=3, value=0.0, type=1} |
150 | spec.animationParameters.absForwardVelocity = {id=4, value=0.0, type=1} |
151 | spec.animationParameters.onGround = {id=5, value=false, type=0} |
152 | spec.animationParameters.inWater = {id=6, value=false, type=0} |
153 | spec.animationParameters.closeToGround = {id=7, value=false, type=0} |
154 | spec.animationParameters.leftRightWeight = {id=8, value=0.0, type=1} |
155 | spec.animationParameters.absYawVelocity = {id=9, value=0.0, type=1} |
156 | spec.animationParameters.halted = {id=10, value=false, type=0} |
157 | spec.animationParameters.smoothedForwardVelocity = {id=11, value=0.0, type=1} |
158 | spec.animationParameters.absSmoothedForwardVelocity = {id=12, value=0.0, type=1} |
159 | |
160 | -- InputAction |
161 | spec.acceletateEventId = "" |
162 | spec.brakeEventId = "" |
163 | spec.steerEventId = "" |
164 | spec.jumpEventId = "" |
165 | |
166 | -- movements |
167 | spec.currentTurnAngle = 0 |
168 | spec.currentTurnSpeed = 0.0 |
169 | spec.currentSpeed = 0.0 |
170 | spec.currentSpeedY = 0.0 |
171 | spec.isInWater = false |
172 | spec.cctMoveQueue = {} |
173 | spec.currentCCTPosX = 0.0 |
174 | spec.currentCCTPosY = 0.0 |
175 | spec.currentCCTPosZ = 0.0 |
176 | spec.lastCCTPosX = 0.0 |
177 | spec.lastCCTPosY = 0.0 |
178 | spec.lastCCTPosZ = 0.0 |
179 | spec.topSpeeds = {} |
180 | spec.topSpeeds[Rideable.GAITTYPES.BACKWARDS] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#speedBackwards"), -1.0) |
181 | spec.topSpeeds[Rideable.GAITTYPES.STILL] = 0.0 |
182 | spec.topSpeeds[Rideable.GAITTYPES.WALK] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#speedWalk"), 2.5) |
183 | spec.topSpeeds[Rideable.GAITTYPES.CANTER] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#speedCanter"), 3.5) |
184 | spec.topSpeeds[Rideable.GAITTYPES.TROT] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#speedTrot"), 5.0) |
185 | spec.topSpeeds[Rideable.GAITTYPES.GALLOP] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#speedGallop"), 10.0) |
186 | spec.minTurnRadius = {} |
187 | spec.minTurnRadius[Rideable.GAITTYPES.BACKWARDS] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#minTurnRadiusBackwards"), 1.0) |
188 | spec.minTurnRadius[Rideable.GAITTYPES.STILL] = 1.0 |
189 | spec.minTurnRadius[Rideable.GAITTYPES.WALK] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#minTurnRadiusWalk"), 1.0) |
190 | spec.minTurnRadius[Rideable.GAITTYPES.CANTER] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#minTurnRadiusCanter"), 2.5) |
191 | spec.minTurnRadius[Rideable.GAITTYPES.TROT] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#minTurnRadiusTrot"), 5.0) |
192 | spec.minTurnRadius[Rideable.GAITTYPES.GALLOP] = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#minTurnRadiusGallop"), 10.0) |
193 | spec.groundRaycastResult = {} |
194 | spec.groundRaycastResult.y = 0.0 |
195 | spec.groundRaycastResult.object = nil |
196 | spec.groundRaycastResult.distance = 0.0 |
197 | spec.haltTimer = 0.0 |
198 | spec.smoothedLeftRightWeight = 0.0 |
199 | spec.interpolationDt = 16 |
200 | |
201 | -- interpolation |
202 | -- spec.interpolationTime = InterpolationTime:new(1.0) |
203 | -- spec.interpolatorPosition = InterpolatorPosition:new(0.0, 0.0, 0.0) |
204 | -- spec.interpolatorQuaternion = InterpolatorQuaternion:new(0.0, 0.0, 0.0, 1.0) -- only used on server side for rotation of camera |
205 | -- spec.interpolatorOnGround = InterpolatorValue:new(0.0) |
206 | |
207 | -- steer |
208 | spec.maxAcceleration = 5 -- m/s^2 |
209 | spec.maxDeceleration = 10 -- m/s^2 |
210 | spec.gravity = -18.8 |
211 | |
212 | -- ground orientation |
213 | spec.frontCheckDistance = 0.0 |
214 | spec.backCheckDistance = 0.0 |
215 | spec.isOnGround = true |
216 | spec.isCloseToGround = true |
217 | |
218 | assert(spec.topSpeeds[Rideable.GAITTYPES.MIN] < spec.topSpeeds[Rideable.GAITTYPES.MAX]) |
219 | spec.maxTurnSpeed = math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#turnSpeed"), 45.0)) -- xml: deg/s, script: rad/s |
220 | spec.jumpHeight = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable#jumpHeight"), 2.0) |
221 | |
222 | local function loadHoof(target, index, key) |
223 | local hoof = {} |
224 | hoof.node = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key.."#node"), self.i3dMappings) |
225 | -- hoof.psNode = createTransformGroup("psLinkNode") |
226 | -- link(hoof.node, hoof.psNode) |
227 | hoof.onGround = false |
228 | hoof.psSlow = {} |
229 | ParticleUtil.loadParticleSystem(self.xmlFile, hoof.psSlow, key..".particleSystemSlow", getRootNode(), false, nil, self.baseDirectory) |
230 | hoof.psFast = {} |
231 | ParticleUtil.loadParticleSystem(self.xmlFile, hoof.psFast, key..".particleSystemFast", getRootNode(), false, nil, self.baseDirectory) |
232 | target[index] = hoof |
233 | end |
234 | |
235 | -- Hooves |
236 | spec.hooves = {} |
237 | loadHoof(spec.hooves, Rideable.HOOVES.FRONT_LEFT, "vehicle.rideable.modelInfo.hoofFrontLeft") |
238 | loadHoof(spec.hooves, Rideable.HOOVES.FRONT_RIGHT, "vehicle.rideable.modelInfo.hoofFrontRight") |
239 | loadHoof(spec.hooves, Rideable.HOOVES.BACK_LEFT, "vehicle.rideable.modelInfo.hoofBackLeft") |
240 | loadHoof(spec.hooves, Rideable.HOOVES.BACK_RIGHT, "vehicle.rideable.modelInfo.hoofBackRight") |
241 | |
242 | spec.frontCheckDistance = self:calculateLegsDistance(spec.hooves[Rideable.HOOVES.FRONT_LEFT].node, spec.hooves[Rideable.HOOVES.FRONT_RIGHT].node) |
243 | spec.backCheckDistance = self:calculateLegsDistance(spec.hooves[Rideable.HOOVES.BACK_LEFT].node, spec.hooves[Rideable.HOOVES.BACK_RIGHT].node) |
244 | |
245 | spec.animationNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.rideable.modelInfo#animationNode"), self.i3dMappings) |
246 | spec.meshNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.rideable.modelInfo#meshNode"), self.i3dMappings) |
247 | spec.hairNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.rideable.modelInfo#hairNode"), self.i3dMappings) |
248 | spec.equipmentNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.rideable.modelInfo#equipmentNode"), self.i3dMappings) |
249 | spec.reinsNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.rideable.modelInfo#reinsNode"), self.i3dMappings) |
250 | spec.leftReinNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.rideable.modelInfo#reinLeftNode"), self.i3dMappings) |
251 | spec.rightReinNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.rideable.modelInfo#reinRightNode"), self.i3dMappings) |
252 | spec.leftReinParentNode = getParent(spec.leftReinNode) |
253 | spec.rightReinParentNode = getParent(spec.rightReinNode) |
254 | |
255 | -- animation |
256 | if spec.animationNode ~= nil then |
257 | spec.charsetId = getAnimCharacterSet(spec.animationNode) |
258 | spec.animationPlayer = createConditionalAnimation() |
259 | for key, parameter in pairs(spec.animationParameters) do |
260 | conditionalAnimationRegisterParameter(spec.animationPlayer, parameter.id, parameter.type, key) |
261 | end |
262 | initConditionalAnimation(spec.animationPlayer, spec.charsetId, self.configFileName, "vehicle.conditionalAnimation") |
263 | setConditionalAnimationSpecificParameterIds(spec.animationPlayer, spec.animationParameters.absForwardVelocity.id, spec.animationParameters.absYawVelocity.id) |
264 | end |
265 | |
266 | -- Sounds |
267 | spec.surfaceSounds = {} |
268 | spec.surfaceIdToSound = {} |
269 | spec.surfaceNameToSound = {} |
270 | spec.currentSurfaceSound = nil |
271 | for _, surfaceSound in pairs(g_currentMission.surfaceSounds) do |
272 | if surfaceSound.type == "hoofstep" and surfaceSound.sample ~= nil then |
273 | local sample = g_soundManager:cloneSample(surfaceSound.sample, self.components[1].node, self) |
274 | sample.sampleName = surfaceSound.name |
275 | |
276 | table.insert(spec.surfaceSounds, sample) |
277 | spec.surfaceIdToSound[surfaceSound.materialId] = sample |
278 | spec.surfaceNameToSound[surfaceSound.name] = sample |
279 | end |
280 | end |
281 | spec.horseStopSound = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.rideable.sounds", "halt", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
282 | spec.horseBreathSoundsNoEffort = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.rideable.sounds", "breathingNoEffort", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
283 | spec.horseBreathSoundsEffort = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.rideable.sounds", "breathingEffort", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
284 | spec.horseBreathIntervalNoEffort = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable.sounds#breathIntervalNoEffort"), 1.0) * 1000.0 |
285 | spec.horseBreathIntervalEffort = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable.sounds#breathIntervalEffort"), 1.0) * 1000.0 |
286 | spec.horseBreathMinIntervalIdle = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable.sounds#minBreathIntervalIdle"), 1.0) * 1000.0 |
287 | spec.horseBreathMaxIntervalIdle = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.rideable.sounds#maxBreathIntervalIdle"), 1.0) * 1000.0 |
288 | spec.currentBreathTimer = 0.0 |
289 | |
290 | -- attributes set by action events |
291 | spec.inputValues = {} |
292 | spec.inputValues.axisSteer = 0.0 |
293 | spec.inputValues.axisSteerSend = 0.0 |
294 | spec.inputValues.currentGait = Rideable.GAITTYPES.STILL |
295 | self:resetInputs() |
296 | |
297 | spec.interpolatorIsOnGround = InterpolatorValue:new(0.0) |
298 | if self.isServer then |
299 | spec.interpolatorTurnAngle = InterpolatorAngle:new(0.0) |
300 | end |
301 | |
302 | if self.isServer then |
303 | self.networkTimeInterpolator.maxInterpolationAlpha = 1.2 |
304 | end |
305 | |
306 | -- Network |
307 | spec.dirtyFlag = self:getNextDirtyFlag() |
308 | end |
961 | function Rideable:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) |
962 | if self.isClient then |
963 | local spec = self.spec_rideable |
964 | self:clearActionEventsTable(spec.actionEvents) |
965 | |
966 | if isActiveForInputIgnoreSelection then |
967 | local actionEventId |
968 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.AXIS_ACCELERATE_VEHICLE, self, Rideable.actionEventAccelerate, false, true, false, true, nil) |
969 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_HIGH) |
970 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
971 | spec.acceletateEventId = actionEventId |
972 | |
973 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.AXIS_BRAKE_VEHICLE, self, Rideable.actionEventBrake, false, true, false, true, nil) |
974 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_HIGH) |
975 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
976 | spec.brakeEventId = actionEventId |
977 | |
978 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.AXIS_MOVE_SIDE_VEHICLE, self, Rideable.actionEventSteer, false, false, true, true, nil) |
979 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
980 | spec.steerEventId = actionEventId |
981 | |
982 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.JUMP, self, Rideable.actionEventJump, false, true, false, true, nil) |
983 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_LOW) |
984 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
985 | spec.jumpEventId = actionEventId |
986 | end |
987 | end |
988 | end |
547 | function Rideable:onUpdateInterpolation(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
548 | local spec = self.spec_rideable |
549 | |
550 | if self.isServer then |
551 | if not self:getIsControlled() then |
552 | spec.inputValues.currentGait = Rideable.GAITTYPES.STILL |
553 | end |
554 | -- for key, moveInfo in pairs(spec.cctMoveQueue) do |
555 | -- 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)) |
556 | -- end |
557 | |
558 | local interpolationDt = dt |
559 | local oldestMoveInfo = spec.cctMoveQueue[1] |
560 | if oldestMoveInfo ~= nil and getIsPhysicsUpdateIndexSimulated(oldestMoveInfo.physicsIndex) then |
561 | interpolationDt = oldestMoveInfo.dt |
562 | end |
563 | spec.interpolationDt = interpolationDt |
564 | |
565 | self:testCCTMove(interpolationDt) |
566 | self:updateKinematic(dt) |
567 | |
568 | if self:getIsEntered() then |
569 | self:resetInputs() |
570 | end |
571 | |
572 | local component = self.components[1] |
573 | local x,y,z = self:getCCTWorldTranslation() |
574 | component.networkInterpolators.position:setTargetPosition(x,y,z) |
575 | spec.interpolatorTurnAngle:setTargetAngle(spec.currentTurnAngle) |
576 | spec.interpolatorIsOnGround:setTargetValue(self:getIsCCTOnGround() and 1.0 or 0.0) |
577 | |
578 | -- use 75 or if dt > 75 then dt + 20 |
579 | local phaseDuration = interpolationDt + 30 |
580 | |
581 | |
582 | self.networkTimeInterpolator:startNewPhase(phaseDuration) |
583 | self.networkTimeInterpolator:update(interpolationDt) |
584 | |
585 | -- 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 |
586 | -- local deltamag = math.sqrt(deltax*deltax+deltay*deltay+deltaz*deltaz) |
587 | -- 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)) |
588 | |
589 | local x, y, z = component.networkInterpolators.position:getInterpolatedValues(self.networkTimeInterpolator.interpolationAlpha) |
590 | setTranslation(self.rootNode, x, y, z) |
591 | |
592 | local turnAngle = spec.interpolatorTurnAngle:getInterpolatedValue(self.networkTimeInterpolator.interpolationAlpha) |
593 | local dirX, dirY, dirZ = localDirectionToWorld(self.rootNode, 0.0, 0.0, 1.0) |
594 | dirX, dirZ = math.sin(turnAngle), math.cos(turnAngle) |
595 | |
596 | -- rescale direction to length, keeping the original y (but keep it to some reasonable value) |
597 | local scale = math.sqrt(1-math.min(dirY*dirY, 0.9)) |
598 | dirX = dirX * scale |
599 | dirZ = dirZ * scale |
600 | setDirection(self.rootNode, dirX, dirY, dirZ, 0,1,0) |
601 | |
602 | if self.networkTimeInterpolator.isDirty then |
603 | self:raiseActive() |
604 | end |
605 | end |
606 | |
607 | local isOnGroundFloat = spec.interpolatorIsOnGround:getInterpolatedValue(self.networkTimeInterpolator:getAlpha()) |
608 | spec.isOnGround = isOnGroundFloat > 0.9 |
609 | spec.isCloseToGround = false |
610 | |
611 | if spec.isOnGround and (math.abs(spec.currentSpeed) > 0.001 or math.abs(spec.currentTurnSpeed) > 0.001) then |
612 | -- orientation from ground |
613 | local posX, posY, posZ = getWorldTranslation(self.rootNode) |
614 | local dirX, dirY, dirZ = localDirectionToWorld(self.rootNode, 0.0, 0.0, 1.0) |
615 | local fx, fy, fz = posX + dirX * spec.frontCheckDistance, posY + dirY * spec.frontCheckDistance, posZ + dirZ * spec.frontCheckDistance |
616 | spec.groundRaycastResult.y = fy + Rideable.GROUND_RAYCAST_OFFSET - Rideable.GROUND_RAYCAST_MAXDISTANCE |
617 | raycastClosest(fx, fy + Rideable.GROUND_RAYCAST_OFFSET, fz, 0.0, -1.0, 0.0, "groundRaycastCallback", Rideable.GROUND_RAYCAST_MAXDISTANCE, self, Rideable.GROUND_RAYCAST_COLLISIONMASK) |
618 | fy = spec.groundRaycastResult.y |
619 | local bx, by, bz = posX + dirX * spec.backCheckDistance, posY + dirY * spec.backCheckDistance, posZ + dirZ * spec.backCheckDistance |
620 | spec.groundRaycastResult.y = by + Rideable.GROUND_RAYCAST_OFFSET - Rideable.GROUND_RAYCAST_MAXDISTANCE |
621 | raycastClosest(bx, by + Rideable.GROUND_RAYCAST_OFFSET, bz, 0.0, -1.0, 0.0, "groundRaycastCallback", Rideable.GROUND_RAYCAST_MAXDISTANCE, self, Rideable.GROUND_RAYCAST_COLLISIONMASK) |
622 | by = spec.groundRaycastResult.y |
623 | local dx, dy, dz = fx - bx, fy - by, fz - bz |
624 | setDirection(self.rootNode, dx, dy, dz, 0, 1, 0) |
625 | else |
626 | local posX, posY, posZ = getWorldTranslation(self.rootNode) |
627 | spec.groundRaycastResult.distance = Rideable.GROUND_RAYCAST_MAXDISTANCE |
628 | raycastClosest(posX, posY, posZ, 0.0, -1.0, 0.0, "groundRaycastCallback", Rideable.GROUND_RAYCAST_MAXDISTANCE, self, Rideable.GROUND_RAYCAST_COLLISIONMASK) |
629 | spec.isCloseToGround = spec.groundRaycastResult.distance < 1.25 |
630 | end |
631 | end |
60 | function Rideable.registerFunctions(vehicleType) |
61 | SpecializationUtil.registerFunction(vehicleType, "jump", Rideable.jump) |
62 | SpecializationUtil.registerFunction(vehicleType, "resetInputs", Rideable.resetInputs) |
63 | SpecializationUtil.registerFunction(vehicleType, "updateKinematic", Rideable.updateKinematic) |
64 | SpecializationUtil.registerFunction(vehicleType, "testCCTMove", Rideable.testCCTMove) |
65 | SpecializationUtil.registerFunction(vehicleType, "updateAnimation", Rideable.updateAnimation) |
66 | SpecializationUtil.registerFunction(vehicleType, "updateSound", Rideable.updateSound) |
67 | SpecializationUtil.registerFunction(vehicleType, "updateFitness", Rideable.updateFitness) |
68 | SpecializationUtil.registerFunction(vehicleType, "updateDirt", Rideable.updateDirt) |
69 | SpecializationUtil.registerFunction(vehicleType, "calculateLegsDistance", Rideable.calculateLegsDistance) |
70 | SpecializationUtil.registerFunction(vehicleType, "setWorldPositionQuat", Rideable.setWorldPositionQuat) |
71 | SpecializationUtil.registerFunction(vehicleType, "setShaderParameter", Rideable.setShaderParameter) |
72 | SpecializationUtil.registerFunction(vehicleType, "getShaderParameter", Rideable.getShaderParameter) |
73 | SpecializationUtil.registerFunction(vehicleType, "updateFootsteps", Rideable.updateFootsteps) |
74 | SpecializationUtil.registerFunction(vehicleType, "getPosition", Rideable.getPosition) |
75 | SpecializationUtil.registerFunction(vehicleType, "getRotation", Rideable.getRotation) |
76 | SpecializationUtil.registerFunction(vehicleType, "setDirtScale", Rideable.setDirtScale) |
77 | SpecializationUtil.registerFunction(vehicleType, "getDirtScale", Rideable.getDirtScale) |
78 | SpecializationUtil.registerFunction(vehicleType, "setFitnessChangedCallback", Rideable.setFitnessChangedCallback) |
79 | SpecializationUtil.registerFunction(vehicleType, "setDirtChangedCallback", Rideable.setDirtChangedCallback) |
80 | SpecializationUtil.registerFunction(vehicleType, "isOnHusbandyGround", Rideable.isOnHusbandyGround) |
81 | SpecializationUtil.registerFunction(vehicleType, "setEquipmentVisibility", Rideable.setEquipmentVisibility) |
82 | SpecializationUtil.registerFunction(vehicleType, "abandonCheck", Rideable.abandonCheck) |
83 | SpecializationUtil.registerFunction(vehicleType, "getHoofSurfaceSound", Rideable.getHoofSurfaceSound) |
84 | SpecializationUtil.registerFunction(vehicleType, "removeRideable", Rideable.removeRideable) |
85 | SpecializationUtil.registerFunction(vehicleType, "setAnimal", Rideable.setAnimal) |
86 | SpecializationUtil.registerFunction(vehicleType, "groundRaycastCallback", Rideable.groundRaycastCallback) |
87 | SpecializationUtil.registerFunction(vehicleType, "unlinkReins", Rideable.unlinkReins) |
88 | SpecializationUtil.registerFunction(vehicleType, "updateInputText", Rideable.updateInputText) |
89 | SpecializationUtil.registerFunction(vehicleType, "setPlayerToEnter", Rideable.setPlayerToEnter) |
90 | SpecializationUtil.registerFunction(vehicleType, "endFade", Rideable.endFade) |
91 | end |
784 | function Rideable:updateAnimation(dt) |
785 | local spec = self.spec_rideable |
786 | local params = spec.animationParameters |
787 | local speed = self.lastSignedSpeedReal * 1000.0 |
788 | local smoothedSpeed = self.lastSignedSpeed * 1000.0 |
789 | speed = MathUtil.clamp(speed, spec.topSpeeds[Rideable.GAITTYPES.BACKWARDS], spec.topSpeeds[Rideable.GAITTYPES.MAX]) |
790 | smoothedSpeed = MathUtil.clamp(smoothedSpeed, spec.topSpeeds[Rideable.GAITTYPES.BACKWARDS], spec.topSpeeds[Rideable.GAITTYPES.MAX]) |
791 | |
792 | local turnSpeed |
793 | if self.isServer then |
794 | turnSpeed = (spec.interpolatorTurnAngle.targetValue - spec.interpolatorTurnAngle.lastValue) / (self.networkTimeInterpolator.interpolationDuration * 0.001) |
795 | else |
796 | local interpQuat = self.components[1].networkInterpolators.quaternion |
797 | local lastDirX, lastDirY, lastDirZ = mathQuaternionRotateVector(interpQuat.lastQuaternionX, interpQuat.lastQuaternionY, interpQuat.lastQuaternionZ, interpQuat.lastQuaternionW, 0,0,1) |
798 | local targetDirX, targetDirY, targetDirZ = mathQuaternionRotateVector(interpQuat.targetQuaternionX, interpQuat.targetQuaternionY, interpQuat.targetQuaternionZ, interpQuat.targetQuaternionW, 0,0,1) |
799 | local lastTurnAngle = MathUtil.getYRotationFromDirection(lastDirX, lastDirZ) |
800 | local targetTurnAngle = MathUtil.getYRotationFromDirection(targetDirX, targetDirZ) |
801 | local turnAngleDiff = targetTurnAngle - lastTurnAngle |
802 | -- normalize to -180,180deg |
803 | if turnAngleDiff > math.pi then |
804 | turnAngleDiff = turnAngleDiff - 2*math.pi |
805 | elseif turnAngleDiff < -math.pi then |
806 | turnAngleDiff = turnAngleDiff + 2*math.pi |
807 | end |
808 | turnSpeed = turnAngleDiff / (self.networkTimeInterpolator.interpolationDuration * 0.001) |
809 | end |
810 | |
811 | local interpPos = self.components[1].networkInterpolators.position |
812 | local speedY = (interpPos.targetPositionY - interpPos.lastPositionY) / (self.networkTimeInterpolator.interpolationDuration * 0.001) |
813 | |
814 | |
815 | local leftRightWeight = 0 |
816 | if math.abs(speed) > 0.01 then |
817 | local closestGait = Rideable.GAITTYPES.STILL |
818 | local closestDiff = math.huge |
819 | for i=1,Rideable.GAITTYPES.MAX do |
820 | local diff = math.abs(speed - spec.topSpeeds[i]) |
821 | if diff < closestDiff then |
822 | closestGait = i |
823 | closestDiff = diff |
824 | end |
825 | end |
826 | local minTurnRadius = spec.minTurnRadius[closestGait] |
827 | leftRightWeight = minTurnRadius * turnSpeed / speed |
828 | else |
829 | leftRightWeight = turnSpeed / spec.maxTurnSpeed |
830 | end |
831 | if leftRightWeight < spec.smoothedLeftRightWeight then |
832 | spec.smoothedLeftRightWeight = math.max(leftRightWeight, spec.smoothedLeftRightWeight-1/500*dt, -1) |
833 | else |
834 | spec.smoothedLeftRightWeight = math.min(leftRightWeight, spec.smoothedLeftRightWeight+1/500*dt, 1) |
835 | end |
836 | |
837 | -- 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)) |
838 | |
839 | params.forwardVelocity.value = speed |
840 | params.absForwardVelocity.value = math.abs(speed) |
841 | params.verticalVelocity.value = speedY |
842 | params.yawVelocity.value = turnSpeed |
843 | params.absYawVelocity.value = math.abs(turnSpeed) |
844 | params.leftRightWeight.value = spec.smoothedLeftRightWeight |
845 | params.onGround.value = spec.isOnGround or spec.justSpawned |
846 | params.closeToGround.value = spec.isCloseToGround |
847 | params.inWater.value = spec.isInWater |
848 | params.halted.value = spec.haltTimer > 0 |
849 | params.smoothedForwardVelocity.value = smoothedSpeed |
850 | params.absSmoothedForwardVelocity.value = math.abs(smoothedSpeed) |
851 | |
852 | -- horse animation |
853 | if spec.animationPlayer ~= 0 then |
854 | for _, parameter in pairs(params) do |
855 | if parameter.type == 0 then |
856 | setConditionalAnimationBoolValue(spec.animationPlayer, parameter.id, parameter.value) |
857 | elseif parameter.type == 1 then |
858 | setConditionalAnimationFloatValue(spec.animationPlayer, parameter.id, parameter.value) |
859 | end |
860 | end |
861 | updateConditionalAnimation(spec.animationPlayer, dt) |
862 | -- local x,y,z = getWorldTranslation(self.rootNode) |
863 | -- conditionalAnimationDebugDraw(spec.animationPlayer, x,y,z) |
864 | end |
865 | local isEntered = self.getIsEntered ~= nil and self:getIsEntered() |
866 | local isControlled = self.getIsControlled ~= nil and self:getIsControlled() |
867 | |
868 | if isEntered or isControlled then |
869 | -- rider animation |
870 | local character = self:getVehicleCharacter() |
871 | if character ~= nil and character.animationCharsetId ~= 0 and character.animationPlayer ~= nil then |
872 | for _, parameter in pairs(params) do |
873 | if parameter.type == 0 then |
874 | setConditionalAnimationBoolValue(character.animationPlayer, parameter.id, parameter.value) |
875 | elseif parameter.type == 1 then |
876 | setConditionalAnimationFloatValue(character.animationPlayer, parameter.id, parameter.value) |
877 | end |
878 | end |
879 | updateConditionalAnimation(character.animationPlayer, dt) |
880 | -- local x,y,z = getWorldTranslation(self.rootNode) |
881 | -- conditionalAnimationDebugDraw(character.animationPlayer, x,y,z) |
882 | end |
883 | end |
884 | self:updateFootsteps(dt, math.abs(speed)) |
885 | end |
1124 | function Rideable:updateFootsteps(dt, speed) |
1125 | local spec = self.spec_rideable |
1126 | local epsilon = 0.001 |
1127 | |
1128 | if speed > epsilon then |
1129 | for k, hoofInfo in pairs(spec.hooves) do |
1130 | local posX, posY, posZ = getWorldTranslation(hoofInfo.node) |
1131 | spec.groundRaycastResult.object = 0 |
1132 | spec.groundRaycastResult.y = posY - 1 |
1133 | raycastClosest(posX, posY + Rideable.GROUND_RAYCAST_OFFSET, posZ, 0.0, -1.0, 0.0, "groundRaycastCallback", Rideable.GROUND_RAYCAST_MAXDISTANCE, self, Rideable.GROUND_RAYCAST_COLLISIONMASK) |
1134 | |
1135 | local hitTerrain = spec.groundRaycastResult.object == g_currentMission.terrainRootNode |
1136 | local terrainY = spec.groundRaycastResult.y |
1137 | local onGround = ((posY - terrainY) < 0.05) |
1138 | |
1139 | -- DebugUtil.drawDebugNode(hoofInfo.node, string.format("[%s] (%.6f/%.6f|%s)", getName(hoofInfo.node), posY, terrainY, tostring(((posY - terrainY) < 0.05)))) |
1140 | if onGround and not hoofInfo.onGround then |
1141 | local r, g, b, _, _ = getTerrainAttributesAtWorldPos(g_currentMission.terrainRootNode, posX, posY, posZ, true, true, true, true, false) |
1142 | hoofInfo.onGround = true |
1143 | -- particles |
1144 | if spec.inputValues.currentGait < Rideable.GAITTYPES.CANTER then |
1145 | ParticleUtil.resetNumOfEmittedParticles(hoofInfo.psSlow) |
1146 | ParticleUtil.setEmittingState(hoofInfo.psSlow, true) |
1147 | setTranslation(hoofInfo.psSlow.emitterShape, posX, terrainY, posZ) |
1148 | setShaderParameter(hoofInfo.psSlow.shape, "psColor", r, g, b, 1, false) |
1149 | else |
1150 | ParticleUtil.resetNumOfEmittedParticles(hoofInfo.psFast) |
1151 | ParticleUtil.setEmittingState(hoofInfo.psFast, true) |
1152 | setTranslation(hoofInfo.psFast.emitterShape, posX, terrainY, posZ) |
1153 | setShaderParameter(hoofInfo.psFast.shape, "psColor", r, g, b, 1, false) |
1154 | end |
1155 | |
1156 | local sample = self:getHoofSurfaceSound(posX, posY, posZ, hitTerrain) |
1157 | if sample ~= nil then |
1158 | hoofInfo.sampleDebug = string.format("%s - %s", sample.sampleName, sample.filename) |
1159 | g_soundManager:playSample(sample) |
1160 | end |
1161 | |
1162 | elseif not onGround and hoofInfo.onGround then |
1163 | hoofInfo.onGround = false |
1164 | ParticleUtil.setEmittingState(hoofInfo.psSlow, false) |
1165 | ParticleUtil.setEmittingState(hoofInfo.psFast, false) |
1166 | end |
1167 | end |
1168 | end |
1169 | end |
1414 | function Rideable:updateInputText() |
1415 | local spec = self.spec_rideable |
1416 | |
1417 | if spec.inputValues.currentGait == Rideable.GAITTYPES.BACKWARDS then |
1418 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_stop")) |
1419 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1420 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1421 | |
1422 | g_inputBinding:setActionEventActive(spec.brakeEventId, false) |
1423 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, false) |
1424 | |
1425 | g_inputBinding:setActionEventActive(spec.jumpEventId, false) |
1426 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, false) |
1427 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.STILL then |
1428 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_walk")) |
1429 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1430 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1431 | |
1432 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_walkBackwards")) |
1433 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1434 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1435 | |
1436 | g_inputBinding:setActionEventActive(spec.jumpEventId, false) |
1437 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, false) |
1438 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.WALK then |
1439 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_trot")) |
1440 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1441 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1442 | |
1443 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_stop")) |
1444 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1445 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1446 | |
1447 | g_inputBinding:setActionEventActive(spec.jumpEventId, false) |
1448 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, false) |
1449 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.TROT then |
1450 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_canter")) |
1451 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1452 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1453 | |
1454 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_walk")) |
1455 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1456 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1457 | |
1458 | g_inputBinding:setActionEventActive(spec.jumpEventId, false) |
1459 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, false) |
1460 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.CANTER then |
1461 | g_inputBinding:setActionEventText(spec.acceletateEventId, g_i18n:getText("action_gallop")) |
1462 | g_inputBinding:setActionEventActive(spec.acceletateEventId, true) |
1463 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, true) |
1464 | |
1465 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_trot")) |
1466 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1467 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1468 | |
1469 | g_inputBinding:setActionEventText(spec.jumpEventId, g_i18n:getText("input_JUMP")) |
1470 | g_inputBinding:setActionEventActive(spec.jumpEventId, true) |
1471 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, true) |
1472 | elseif spec.inputValues.currentGait == Rideable.GAITTYPES.GALLOP then |
1473 | g_inputBinding:setActionEventActive(spec.acceletateEventId, false) |
1474 | g_inputBinding:setActionEventTextVisibility(spec.acceletateEventId, false) |
1475 | |
1476 | g_inputBinding:setActionEventText(spec.brakeEventId, g_i18n:getText("action_canter")) |
1477 | g_inputBinding:setActionEventActive(spec.brakeEventId, true) |
1478 | g_inputBinding:setActionEventTextVisibility(spec.brakeEventId, true) |
1479 | |
1480 | g_inputBinding:setActionEventText(spec.jumpEventId, g_i18n:getText("input_JUMP")) |
1481 | g_inputBinding:setActionEventActive(spec.jumpEventId, true) |
1482 | g_inputBinding:setActionEventTextVisibility(spec.jumpEventId, true) |
1483 | end |
1484 | end |
697 | function Rideable:updateKinematic(dt) |
698 | local spec = self.spec_rideable |
699 | local dtInSec = dt*0.001 |
700 | |
701 | -- Update movement in current direction |
702 | local desiredSpeed = spec.topSpeeds[spec.inputValues.currentGait] |
703 | local maxSpeedChange = spec.maxAcceleration |
704 | if desiredSpeed == 0.0 then |
705 | maxSpeedChange = spec.maxDeceleration |
706 | end |
707 | maxSpeedChange = maxSpeedChange * dtInSec |
708 | if not spec.isOnGround then |
709 | -- reduce acceleration when in the air |
710 | maxSpeedChange = maxSpeedChange * 0.2 |
711 | end |
712 | |
713 | local speedChange = (desiredSpeed - spec.currentSpeed) |
714 | speedChange = MathUtil.clamp(speedChange, -maxSpeedChange, maxSpeedChange) |
715 | |
716 | --local movement = (spec.currentSpeed + 0.5 * speedChange) * dtInSec |
717 | if spec.haltTimer <= 0.0 then |
718 | spec.currentSpeed = spec.currentSpeed + speedChange |
719 | else |
720 | spec.currentSpeed = 0.0 |
721 | end |
722 | |
723 | local movement = spec.currentSpeed * dtInSec |
724 | |
725 | -- Update gravity / vertical movement |
726 | local gravitySpeedChange = spec.gravity * dtInSec |
727 | spec.currentSpeedY = spec.currentSpeedY + gravitySpeedChange |
728 | local movementY = spec.currentSpeedY * dtInSec |
729 | |
730 | -- Update rotation |
731 | local slowestSpeed = spec.topSpeeds[Rideable.GAITTYPES.WALK] |
732 | local fastestSpeed = spec.topSpeeds[Rideable.GAITTYPES.MAX] |
733 | |
734 | 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) |
735 | maxTurnSpeedChange = maxTurnSpeedChange * dtInSec |
736 | if not spec.isOnGround then |
737 | -- reduce turn acceleration when in the air |
738 | maxTurnSpeedChange = maxTurnSpeedChange * 0.25 |
739 | end |
740 | |
741 | if self.isServer then |
742 | if not self:getIsEntered() and not self:getIsControlled() and spec.inputValues.axisSteer ~= 0.0 then |
743 | spec.inputValues.axisSteer = 0.0 |
744 | end |
745 | end |
746 | |
747 | local desiredTurnSpeed = spec.maxTurnSpeed * spec.inputValues.axisSteer |
748 | local turnSpeedChange = (desiredTurnSpeed - spec.currentTurnSpeed) |
749 | turnSpeedChange = MathUtil.clamp(turnSpeedChange, -maxTurnSpeedChange, maxTurnSpeedChange) |
750 | spec.currentTurnSpeed = spec.currentTurnSpeed + turnSpeedChange |
751 | spec.currentTurnAngle = spec.currentTurnAngle + spec.currentTurnSpeed * dtInSec * (movement >= 0 and 1 or -1) |
752 | |
753 | local movementX, movementZ = math.sin(spec.currentTurnAngle) * movement, math.cos(spec.currentTurnAngle) * movement |
754 | -- print(string.format("-- [Rideable:updateKinematic][%d]\t%.6f\t%.6f\t%.6f\t%d", g_updateLoopIndex, dt, spec.currentSpeed, movement, getPhysicsUpdateIndex())) |
755 | self:moveCCT(movementX, movementY, movementZ, true) |
756 | |
757 | table.insert(spec.cctMoveQueue, {physicsIndex = getPhysicsUpdateIndex(), moveX = movementX, moveY = movementY, moveZ = movementZ, dt = dt}) |
758 | end |