Script v1_7_1_0
- AI
- Animals
- Collections
- Contracts
- Debug
- Economy
- Elements
- EnvironmentalScore
- Errors
- Events
- GUI
- Handtools
- Hud
- I3d
- Input
- Jobs
- Maps
- Materials
- Misc
- Objects
- Parameters
- Placeables
- Placement
- Player
- Shop
- Sounds
- Specialization
- Specializations
- StateMachine
- Statistics
- Tasks
- Triggers
- Utils
- Vehicles
Engine v1_7_1_0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- general
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- NoteNode
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- VoiceChat
- XML
Foundation Reference
Vehicle
DescriptionThis class handles all basic functionality of a vehicle - loading of i3d - syncing of components - handling of specializationsParent
ObjectFunctions
- actionEventToggleSelection
- actionEventToggleSelectionReverse
- activate
- addChildVehicles
- addNodeObjectMapping
- addSubselection
- addToPhysics
- addVehicleToAIImplementList
- calculateDailyUpkeep
- calculateSellPrice
- createComponentJoint
- createMapHotspot
- dayChanged
- deactivate
- delete
- deleteMapHotspot
- doCheckSpeedLimit
- doCollisionMaskCheck
- draw
- drawUIInfo
- findRootVehicle
- getActionControllerDirection
- getAdditionalComponentMass
- getAdditionalSchemaText
- getAreControlledActionsAllowed
- getAreControlledActionsAvailable
- getAvailableComponentMass
- getBlockSelection
- getBrand
- getCanBePickedUp
- getCanBeReset
- getCanBeSelected
- getCanByMounted
- getCanToggleSelectable
- getChildVehicles
- getComponentMass
- getDailyUpkeep
- getDeactivateOnLeave
- getDefaultMass
- getDistanceToNode
- getFillLevelInformation
- getFullName
- getImageFilename
- getInteractionHelp
- getIsActive
- getIsActiveForInput
- getIsActiveForSound
- getIsAIActive
- getIsAutomaticShiftingAllowed
- getIsInUse
- getIsLowered
- getIsMapHotspotVisible
- getIsOnField
- getIsOperating
- getIsPowered
- getIsReadyForAutomatedTrainTravel
- getIsSelected
- getIsSynchronized
- getIsVehicleNode
- getLastSpeed
- getLimitedVehicleYPosition
- getMapHotspot
- getMaxComponentMassReached
- getName
- getOperatingTime
- getOverallCenterOfMass
- getOwner
- getParentComponent
- getPrice
- getPropertyState
- getRawSpeedLimit
- getReloadXML
- getRepaintPrice
- getRepairPrice
- getRootVehicle
- getSelectedObject
- getSelectedVehicle
- getSellPrice
- getSpecConfigValuesWeight
- getSpecValueAdditionalWeight
- getSpecValueCombinations
- getSpecValueDailyUpkeep
- getSpecValueOperatingTime
- getSpecValueSlots
- getSpecValueSpeedLimit
- getSpecValueWeight
- getSpecValueWorkingWidth
- getSpecValueWorkingWidthConfig
- getSpeedLimit
- getTotalMass
- getUseTurnedOnSchema
- getVehicleDamage
- getVehicleWorldDirection
- getVehicleWorldXRot
- getWorkLoad
- hasInputConflictWithSelection
- init
- interact
- load
- loadComponentFromXML
- loadComponentJointFromXML
- loadFinished
- loadObjectChangeValuesFromXML
- loadSchemaOverlay
- loadSpecValueAdditionalWeight
- loadSpecValueCombinations
- loadSpecValueSpeedLimit
- loadSpecValueWeight
- loadSpecValueWorkingWidth
- loadSpecValueWorkingWidthConfig
- loadSubSharedI3DFile
- new
- onVehicleWakeUpCallback
- onWaterRaycastCallback
- periodChanged
- playControlledActions
- postInit
- postReadStream
- postWriteStream
- raiseStateChange
- readStream
- readUpdateStream
- registerActionEvents
- registerEvents
- registerFunctions
- registerInteractionFlag
- registers
- registerSelectableObjects
- registerStateChange
- removeActionEvent
- removeActionEvents
- removeFromPhysics
- removeNodeObjectMapping
- requestActionEventUpdate
- saveStatsToXMLFile
- saveToXMLFile
- selectVehicle
- setAbsolutePosition
- setBroken
- setComponentJointFrame
- setComponentJointRotLimit
- setComponentJointTransLimit
- setLoadingState
- setLoadingStep
- setMassDirty
- setObjectChangeValues
- setOperatingTime
- setOwnerFarmId
- setRelativePosition
- setSelectedObject
- setSelectedVehicle
- setVisibility
- setWorldPosition
- setWorldPositionQuaternion
- showInfo
- tryFinishLoading
- unselectVehicle
- update
- updateActionEvents
- updateEnd
- updateMapHotspot
- updateMass
- updateSelectableObjects
- updateTick
- updateVehicleChain
- updateVehicleSpeed
- updateWaterInfo
- wakeUp
- writeStream
- writeUpdateStream
actionEventToggleSelection
DescriptionDefinitionactionEventToggleSelection()Code
3984 | function Vehicle.actionEventToggleSelection(self, actionName, inputValue, callbackState, isAnalog) |
3985 | local currentSelection = self.currentSelection |
3986 | local currentObject = currentSelection.object |
3987 | local currentObjectIndex = currentSelection.index |
3988 | local currentSubObjectIndex = currentSelection.subIndex |
3989 | |
3990 | local numSubSelections = 0 |
3991 | if currentObject ~= nil then |
3992 | numSubSelections = #currentObject.subSelections |
3993 | end |
3994 | |
3995 | local newSelectedSubObjectIndex = currentSubObjectIndex + 1 |
3996 | local newSelectedObjectIndex = currentObjectIndex |
3997 | local newSelectedObject = currentObject |
3998 | |
3999 | if newSelectedSubObjectIndex > numSubSelections then |
4000 | newSelectedSubObjectIndex = 1 |
4001 | newSelectedObjectIndex = currentObjectIndex + 1 |
4002 | |
4003 | if newSelectedObjectIndex > #self.selectableObjects then |
4004 | newSelectedObjectIndex = 1 |
4005 | end |
4006 | newSelectedObject = self.selectableObjects[newSelectedObjectIndex] |
4007 | end |
4008 | |
4009 | if currentObject ~= newSelectedObject or currentObjectIndex ~= newSelectedObjectIndex or currentSubObjectIndex ~= newSelectedSubObjectIndex then |
4010 | -- event |
4011 | self:setSelectedObject(newSelectedObject, newSelectedSubObjectIndex) |
4012 | end |
4013 | end |
actionEventToggleSelectionReverse
DescriptionDefinitionactionEventToggleSelectionReverse()Code
4017 | function Vehicle.actionEventToggleSelectionReverse(self, actionName, inputValue, callbackState, isAnalog) |
4018 | local currentSelection = self.currentSelection |
4019 | local currentObject = currentSelection.object |
4020 | local currentObjectIndex = currentSelection.index |
4021 | local currentSubObjectIndex = currentSelection.subIndex |
4022 | |
4023 | local newSelectedSubObjectIndex = currentSubObjectIndex - 1 |
4024 | local newSelectedObjectIndex = currentObjectIndex |
4025 | local newSelectedObject = currentObject |
4026 | |
4027 | if newSelectedSubObjectIndex < 1 then |
4028 | newSelectedSubObjectIndex = 1 |
4029 | newSelectedObjectIndex = currentObjectIndex - 1 |
4030 | |
4031 | if newSelectedObjectIndex < 1 then |
4032 | newSelectedObjectIndex = #self.selectableObjects |
4033 | end |
4034 | |
4035 | newSelectedObject = self.selectableObjects[newSelectedObjectIndex] |
4036 | if newSelectedObject ~= nil then |
4037 | newSelectedSubObjectIndex = #newSelectedObject.subSelections |
4038 | end |
4039 | end |
4040 | |
4041 | if currentObject ~= newSelectedObject or currentObjectIndex ~= newSelectedObjectIndex or currentSubObjectIndex ~= newSelectedSubObjectIndex then |
4042 | -- event |
4043 | self:setSelectedObject(newSelectedObject, newSelectedSubObjectIndex) |
4044 | end |
4045 | end |
activate
DescriptionCalled on activateDefinition
activate()Code
3092 | function Vehicle:activate() |
3093 | SpecializationUtil.raiseEvent(self, "onActivate") |
3094 | end |
addChildVehicles
DescriptionDefinitionaddChildVehicles()Code
2766 | function Vehicle:addChildVehicles(vehicles) |
2767 | table.insert(vehicles, self) |
2768 | end |
addNodeObjectMapping
DescriptionAdd component nodes to listDefinition
addNodeObjectMapping(table list)Arguments
table | list | list |
2074 | function Vehicle:addNodeObjectMapping(list) |
2075 | for _,v in pairs(self.components) do |
2076 | list[v.node] = self |
2077 | end |
2078 | end |
addSubselection
DescriptionDefinitionaddSubselection()Code
2723 | function Vehicle:addSubselection(subSelection) |
2724 | table.insert(self.selectionObject.subSelections, subSelection) |
2725 | return #self.selectionObject.subSelections |
2726 | end |
addToPhysics
DescriptionAdd vehicle to physicsDefinition
addToPhysics()Return Values
boolean | success | success |
2094 | function Vehicle:addToPhysics() |
2095 | |
2096 | if not self.isAddedToPhysics then |
2097 | local lastMotorizedNode = nil |
2098 | for _, component in pairs(self.components) do |
2099 | addToPhysics(component.node) |
2100 | if component.motorized then |
2101 | if lastMotorizedNode ~= nil then |
2102 | if self.isServer then |
2103 | addVehicleLink(lastMotorizedNode, component.node) |
2104 | end |
2105 | end |
2106 | lastMotorizedNode = component.node |
2107 | end |
2108 | end |
2109 | |
2110 | self.isAddedToPhysics = true |
2111 | |
2112 | if self.isServer then |
2113 | for _, jointDesc in pairs(self.componentJoints) do |
2114 | self:createComponentJoint(self.components[jointDesc.componentIndices[1]], self.components[jointDesc.componentIndices[2]], jointDesc) |
2115 | end |
2116 | |
2117 | -- if rootnode is sleeping all other components are sleeping as well |
2118 | addWakeUpReport(self.rootNode, "onVehicleWakeUpCallback", self) |
2119 | end |
2120 | |
2121 | for _, collisionPair in pairs(self.collisionPairs) do |
2122 | setPairCollision(collisionPair.component1.node, collisionPair.component2.node, collisionPair.enabled) |
2123 | end |
2124 | |
2125 | self:setMassDirty() |
2126 | end |
2127 | |
2128 | return true |
2129 | end |
addVehicleToAIImplementList
DescriptionDefinitionaddVehicleToAIImplementList()Code
3633 | function Vehicle:addVehicleToAIImplementList(list) |
3634 | end |
calculateDailyUpkeep
DescriptionDefinitioncalculateDailyUpkeep()Code
3755 | function Vehicle.calculateDailyUpkeep(storeItem, age, operatingTime, configurations) |
3756 | local multiplier = 1 |
3757 | if storeItem.lifetime ~= nil and storeItem.lifetime ~= 0 then |
3758 | local ageMultiplier = 0.3 * math.min(age / storeItem.lifetime, 1) |
3759 | operatingTime = operatingTime / (1000*60*60) |
3760 | local operatingTimeMultiplier = 0.7 * math.min(operatingTime / (storeItem.lifetime*EconomyManager.LIFETIME_OPERATINGTIME_RATIO), 1) |
3761 | multiplier = 1 + EconomyManager.MAX_DAILYUPKEEP_MULTIPLIER * (ageMultiplier+operatingTimeMultiplier) |
3762 | end |
3763 | |
3764 | return StoreItemUtil.getDailyUpkeep(storeItem, configurations) * multiplier |
3765 | end |
calculateSellPrice
DescriptionCalculate price of vehicle given a bunch of parametersDefinition
calculateSellPrice()Code
2285 | function Vehicle.calculateSellPrice(storeItem, age, operatingTime, price, repairPrice, repaintPrice) |
2286 | local operatingTimeHours = operatingTime / (60 * 60 * 1000) |
2287 | local maxVehicleAge = storeItem.lifetime |
2288 | local ageInYears = age / Environment.PERIODS_IN_YEAR |
2289 | |
2290 | StoreItemUtil.loadSpecsFromXML(storeItem) |
2291 | |
2292 | -- Motorized vehicles depreciate differently |
2293 | local motorizedFactor = 1.0 |
2294 | if storeItem.specs.power == nil then |
2295 | motorizedFactor = 1.3 |
2296 | end |
2297 | |
2298 | local operatingTimeFactor = 1 - operatingTimeHours ^ motorizedFactor / maxVehicleAge |
2299 | local ageFactor = math.min(-0.1 * math.log(ageInYears) + 0.75, 0.8) |
2300 | |
2301 | return math.max(price * operatingTimeFactor * ageFactor - repairPrice - repaintPrice, price * 0.03) |
2302 | end |
createComponentJoint
DescriptionCreate component joint between two componentsDefinition
createComponentJoint(table component1, table component2, table jointDesc)Arguments
table | component1 | component 1 |
table | component2 | component 2 |
table | jointDesc | joint desc |
boolean | success | success |
3433 | function Vehicle:createComponentJoint(component1, component2, jointDesc) |
3434 | if component1 == nil or component2 == nil or jointDesc == nil then |
3435 | Logging.xmlWarning(self.xmlFile, "Could not create component joint. No component1, component2 or jointDesc given!") |
3436 | return false |
3437 | end |
3438 | |
3439 | local constr = JointConstructor.new() |
3440 | constr:setActors(component1.node, component2.node) |
3441 | |
3442 | local localPoses1 = jointDesc.jointLocalPoses[1] |
3443 | local localPoses2 = jointDesc.jointLocalPoses[2] |
3444 | constr:setJointLocalPositions(localPoses1.trans[1], localPoses1.trans[2], localPoses1.trans[3], localPoses2.trans[1], localPoses2.trans[2], localPoses2.trans[3]) |
3445 | constr:setJointLocalRotations(localPoses1.rot[1], localPoses1.rot[2], localPoses1.rot[3], localPoses2.rot[1], localPoses2.rot[2], localPoses2.rot[3]) |
3446 | --constr:setJointTransforms(jointDesc.jointNode, jointDesc.jointNodeActor1) |
3447 | |
3448 | constr:setRotationLimitSpring(jointDesc.rotLimitSpring[1], jointDesc.rotLimitDamping[1], jointDesc.rotLimitSpring[2], jointDesc.rotLimitDamping[2], jointDesc.rotLimitSpring[3], jointDesc.rotLimitDamping[3]) |
3449 | constr:setTranslationLimitSpring(jointDesc.transLimitSpring[1], jointDesc.transLimitDamping[1], jointDesc.transLimitSpring[2], jointDesc.transLimitDamping[2], jointDesc.transLimitSpring[3], jointDesc.transLimitDamping[3]) |
3450 | constr:setZRotationXOffset(jointDesc.zRotationXOffset) |
3451 | for i=1, 3 do |
3452 | if jointDesc.rotLimit[i] >= jointDesc.rotMinLimit[i] then |
3453 | constr:setRotationLimit(i-1, jointDesc.rotMinLimit[i], jointDesc.rotLimit[i]) |
3454 | end |
3455 | |
3456 | if jointDesc.transLimit[i] >= jointDesc.transMinLimit[i] then |
3457 | constr:setTranslationLimit(i-1, true, jointDesc.transMinLimit[i], jointDesc.transLimit[i]) |
3458 | else |
3459 | constr:setTranslationLimit(i-1, false, 0, 0) |
3460 | end |
3461 | end |
3462 | |
3463 | constr:setRotationLimitForceLimit(jointDesc.rotLimitForceLimit[1], jointDesc.rotLimitForceLimit[2], jointDesc.rotLimitForceLimit[3]) |
3464 | constr:setTranslationLimitForceLimit(jointDesc.transLimitForceLimit[1], jointDesc.transLimitForceLimit[2], jointDesc.transLimitForceLimit[3]) |
3465 | |
3466 | if jointDesc.isBreakable then |
3467 | constr:setBreakable(jointDesc.breakForce, jointDesc.breakTorque) |
3468 | end |
3469 | constr:setEnableCollision(jointDesc.enableCollision) |
3470 | |
3471 | for i=1,3 do |
3472 | if jointDesc.maxRotDriveForce[i] > 0.0001 and (jointDesc.rotDriveVelocity[i] ~= nil or jointDesc.rotDriveRotation[i] ~= nil) then |
3473 | local pos = Utils.getNoNil(jointDesc.rotDriveRotation[i], 0) |
3474 | local vel = Utils.getNoNil(jointDesc.rotDriveVelocity[i], 0) |
3475 | constr:setAngularDrive(i-1, jointDesc.rotDriveRotation[i] ~= nil, jointDesc.rotDriveVelocity[i] ~= nil, jointDesc.rotDriveSpring[i], jointDesc.rotDriveDamping[i], jointDesc.maxRotDriveForce[i], pos, vel) |
3476 | end |
3477 | if jointDesc.maxTransDriveForce[i] > 0.0001 and (jointDesc.transDriveVelocity[i] ~= nil or jointDesc.transDrivePosition[i] ~= nil) then |
3478 | local pos = Utils.getNoNil(jointDesc.transDrivePosition[i], 0) |
3479 | local vel = Utils.getNoNil(jointDesc.transDriveVelocity[i], 0) |
3480 | constr:setLinearDrive(i-1, jointDesc.transDrivePosition[i] ~= nil, jointDesc.transDriveVelocity[i] ~= nil, jointDesc.transDriveSpring[i], jointDesc.transDriveDamping[i], jointDesc.maxTransDriveForce[i], pos, vel) |
3481 | end |
3482 | end |
3483 | |
3484 | jointDesc.jointIndex = constr:finalize() |
3485 | |
3486 | return true |
3487 | end |
createMapHotspot
DescriptionDefinitioncreateMapHotspot()Code
3901 | function Vehicle:createMapHotspot() |
3902 | local mapHotspot = VehicleHotspot.new() |
3903 | mapHotspot:setVehicle(self) |
3904 | mapHotspot:setVehicleType(self.mapHotspotType) |
3905 | mapHotspot:setHasRotation(self.mapHotspotHasDirection) |
3906 | mapHotspot:setOwnerFarmId(self:getOwnerFarmId()) |
3907 | self.mapHotspot = mapHotspot |
3908 | |
3909 | g_currentMission:addMapHotspot(mapHotspot) |
3910 | end |
dayChanged
DescriptionDefinitiondayChanged()Code
3565 | function Vehicle:dayChanged() |
3566 | end |
deactivate
DescriptionCalled on deactivateDefinition
deactivate()Code
3098 | function Vehicle:deactivate() |
3099 | SpecializationUtil.raiseEvent(self, "onDeactivate") |
3100 | end |
delete
DescriptionDefinitiondelete()Code
1262 | function Vehicle:delete() |
1263 | if self.isDeleted then |
1264 | Logging.devError("Trying to delete a already deleted vehicle") |
1265 | printCallstack() |
1266 | return |
1267 | end |
1268 | |
1269 | VehicleDebug.delete(self) |
1270 | |
1271 | if self.tourId ~= nil then |
1272 | if g_currentMission ~= nil then |
1273 | g_currentMission.guidedTour:removeVehicle(self.tourId) |
1274 | end |
1275 | end |
1276 | |
1277 | self.isDeleting = true |
1278 | g_messageCenter:unsubscribeAll(self) |
1279 | |
1280 | self:deleteMapHotspot() |
1281 | |
1282 | local rootVehicle = self.rootVehicle |
1283 | if rootVehicle ~= nil and rootVehicle:getIsAIActive() then |
1284 | rootVehicle:stopCurrentAIJob(AIMessageErrorVehicleDeleted.new()) |
1285 | end |
1286 | |
1287 | g_inputBinding:beginActionEventsModification(Vehicle.INPUT_CONTEXT_NAME) |
1288 | self:removeActionEvents() |
1289 | g_inputBinding:endActionEventsModification() |
1290 | |
1291 | SpecializationUtil.raiseEvent(self, "onPreDelete") |
1292 | SpecializationUtil.raiseEvent(self, "onDelete") |
1293 | |
1294 | if self.isServer and self.componentJoints ~= nil then |
1295 | for _,v in pairs(self.componentJoints) do |
1296 | if v.jointIndex ~= 0 then |
1297 | removeJoint(v.jointIndex) |
1298 | end |
1299 | end |
1300 | |
1301 | removeWakeUpReport(self.rootNode) |
1302 | end |
1303 | |
1304 | self:removeNodeObjectMapping(g_currentMission.nodeToObject) |
1305 | |
1306 | if self.components ~= nil then |
1307 | for _,v in pairs(self.components) do |
1308 | delete(v.node) |
1309 | end |
1310 | end |
1311 | |
1312 | if self.sharedLoadRequestId ~= nil then |
1313 | g_i3DManager:releaseSharedI3DFile(self.sharedLoadRequestId) |
1314 | self.sharedLoadRequestId = nil |
1315 | end |
1316 | |
1317 | self.xmlFile:delete() |
1318 | |
1319 | if self.externalSoundsFile ~= nil then |
1320 | self.externalSoundsFile:delete() |
1321 | end |
1322 | |
1323 | self.isDeleting = false |
1324 | self.isDeleted = true |
1325 | |
1326 | Vehicle:superClass().delete(self) |
1327 | |
1328 | if self.currentSavegameId ~= nil then |
1329 | g_currentMission.savegameIdToVehicle[self.currentSavegameId] = nil |
1330 | end |
1331 | end |
deleteMapHotspot
DescriptionDefinitiondeleteMapHotspot()Code
3914 | function Vehicle:deleteMapHotspot() |
3915 | if self.mapHotspot ~= nil then |
3916 | g_currentMission:removeMapHotspot(self.mapHotspot) |
3917 | self.mapHotspot:delete() |
3918 | self.mapHotspot = nil |
3919 | end |
3920 | end |
doCheckSpeedLimit
DescriptionDefinitiondoCheckSpeedLimit()Code
3582 | function Vehicle:doCheckSpeedLimit() |
3583 | return false |
3584 | end |
doCollisionMaskCheck
DescriptionDefinitiondoCollisionMaskCheck()Code
3653 | function Vehicle:doCollisionMaskCheck(targetCollisionMask, path, node, str) |
3654 | local ignoreCheck = false |
3655 | if path ~= nil then |
3656 | ignoreCheck = self.xmlFile:getValue(path, false) |
3657 | end |
3658 | |
3659 | if not ignoreCheck then |
3660 | local hasMask = false |
3661 | if node == nil then |
3662 | for _, component in ipairs(self.components) do |
3663 | hasMask = hasMask or bitAND(getCollisionMask(component.node), targetCollisionMask) == targetCollisionMask |
3664 | end |
3665 | else |
3666 | hasMask = hasMask or bitAND(getCollisionMask(node), targetCollisionMask) == targetCollisionMask |
3667 | end |
3668 | |
3669 | if not hasMask then |
3670 | printCallstack() |
3671 | Logging.xmlWarning(self.xmlFile, "%s has wrong collision mask! Following bit(s) need to be set '%s' or use '%s'", str or self.typeName, MathUtil.numberToSetBitsStr(targetCollisionMask), path) |
3672 | return false |
3673 | end |
3674 | end |
3675 | |
3676 | return true |
3677 | end |
draw
DescriptionDefinitiondraw()Code
1987 | function Vehicle:draw(subDraw) |
1988 | if self:getIsSynchronized() then |
1989 | local rootVehicle = self.rootVehicle |
1990 | local selectedVehicle = self:getSelectedVehicle() |
1991 | if not subDraw then |
1992 | -- draw is only called on the 'entered' vehicle |
1993 | -- so if the 'entered' vehicle is not the root vehicle or not the selected vehicle we call draw on them |
1994 | if self ~= rootVehicle and selectedVehicle ~= rootVehicle then |
1995 | rootVehicle:draw(true) |
1996 | end |
1997 | |
1998 | if selectedVehicle ~= nil and self ~= selectedVehicle and selectedVehicle ~= rootVehicle then |
1999 | selectedVehicle:draw(true) |
2000 | end |
2001 | end |
2002 | |
2003 | if selectedVehicle == self or rootVehicle == self then |
2004 | local isActiveForInput = self:getIsActiveForInput() |
2005 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
2006 | SpecializationUtil.raiseEvent(self, "onDraw", isActiveForInput, isActiveForInputIgnoreSelection, true) |
2007 | end |
2008 | |
2009 | -- DebugUtil.drawDebugNode(self.rootNode, "farm: " .. tostring(self:getOwnerFarmId()) .. ", controller: " .. tostring(self:getActiveFarm())) |
2010 | |
2011 | -- if self == self.rootVehicle and self:getIsActiveForInput() then |
2012 | -- renderText(0.5, 0.95, 0.012, tostring(self:getSelectedVehicle())) |
2013 | -- for k, object in ipairs(self.selectableObjects) do |
2014 | -- renderText(0.5, 0.9-k*0.015, 0.012, tostring(object.vehicle) .. " " .. tostring(object.vehicle.configFileName) .. ": " .. #object.subSelections .. " " .. tostring(object.vehicle:getIsSelected())) |
2015 | -- end |
2016 | -- end |
2017 | |
2018 | VehicleDebug.drawDebug(self) |
2019 | |
2020 | if self.showTailwaterDepthWarning then |
2021 | g_currentMission:showBlinkingWarning(g_i18n:getText("warning_dontDriveIntoWater"), 2000) |
2022 | end |
2023 | end |
2024 | end |
drawUIInfo
DescriptionDraw UI infoDefinition
drawUIInfo()Code
2028 | function Vehicle:drawUIInfo() |
2029 | if self:getIsSynchronized() then |
2030 | SpecializationUtil.raiseEvent(self, "onDrawUIInfo") |
2031 | |
2032 | if g_showVehicleDistance then |
2033 | local dist = calcDistanceFrom(self.rootNode, getCamera()) |
2034 | if dist <= 350 then |
2035 | local x, y, z = getWorldTranslation(self.rootNode) |
2036 | Utils.renderTextAtWorldPosition(x, y+1, z, string.format("%.0f", dist), getCorrectTextSize(0.02), 0) |
2037 | end |
2038 | end |
2039 | end |
2040 | end |
findRootVehicle
DescriptionDefinitionfindRootVehicle()Code
2754 | function Vehicle:findRootVehicle() |
2755 | return self |
2756 | end |
getActionControllerDirection
DescriptionDefinitiongetActionControllerDirection()Code
3891 | function Vehicle:getActionControllerDirection() |
3892 | if self.actionController ~= nil then |
3893 | return self.actionController:getActionControllerDirection() |
3894 | end |
3895 | |
3896 | return 1 |
3897 | end |
getAdditionalComponentMass
DescriptionDefinitiongetAdditionalComponentMass()Code
3000 | function Vehicle:getAdditionalComponentMass(component) |
3001 | return 0 |
3002 | end |
getAdditionalSchemaText
DescriptionDefinitiongetAdditionalSchemaText()Code
3553 | function Vehicle:getAdditionalSchemaText() |
3554 | return nil |
3555 | end |
getAreControlledActionsAllowed
DescriptionReturns if controlled actions are allowedDefinition
getAreControlledActionsAllowed()Return Values
boolean | allow | allow controlled actions |
string | warning | not allowed warning |
3877 | function Vehicle:getAreControlledActionsAllowed() |
3878 | return not self:getIsAIActive(), "" |
3879 | end |
getAreControlledActionsAvailable
DescriptionDefinitiongetAreControlledActionsAvailable()Code
3861 | function Vehicle:getAreControlledActionsAvailable() |
3862 | if self:getIsAIActive() then |
3863 | return false |
3864 | end |
3865 | |
3866 | if self.actionController ~= nil then |
3867 | return self.actionController:getAreControlledActionsAvailable() |
3868 | end |
3869 | |
3870 | return false |
3871 | end |
getAvailableComponentMass
DescriptionDefinitiongetAvailableComponentMass()Code
2994 | function Vehicle:getAvailableComponentMass() |
2995 | return math.max((self.maxComponentMass - self.precalculatedMass) - self.serverMass, 0) |
2996 | end |
getBlockSelection
DescriptionDefinitiongetBlockSelection()Code
2736 | function Vehicle:getBlockSelection() |
2737 | return false |
2738 | end |
getBrand
DescriptionDefinitiongetBrand()Code
3801 | function Vehicle:getBrand() |
3802 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
3803 | if storeItem ~= nil and storeItem.configurations ~= nil then |
3804 | for configName, _ in pairs(storeItem.configurations) do |
3805 | local configId = self.configurations[configName] |
3806 | local config = storeItem.configurations[configName][configId] |
3807 | if config.vehicleBrand ~= nil then |
3808 | return config.vehicleBrand |
3809 | end |
3810 | end |
3811 | end |
3812 | |
3813 | return storeItem.brandIndex |
3814 | end |
getCanBePickedUp
DescriptionDefinitiongetCanBePickedUp()Code
3837 | function Vehicle:getCanBePickedUp(byPlayer) |
3838 | return self.supportsPickUp and g_currentMission.accessHandler:canPlayerAccess(self, byPlayer) |
3839 | end |
getCanBeReset
DescriptionDefinitiongetCanBeReset()Code
3843 | function Vehicle:getCanBeReset() |
3844 | return self.canBeReset |
3845 | end |
getCanBeSelected
DescriptionDefinitiongetCanBeSelected()Code
2730 | function Vehicle:getCanBeSelected() |
2731 | return VehicleDebug.state ~= 0 -- allow selection while any debug modes is active to debug any vehicle |
2732 | end |
getCanByMounted
DescriptionDefinitiongetCanByMounted()Code
3741 | function Vehicle:getCanByMounted() |
3742 | return entityExists(self.components[1].node) |
3743 | end |
getCanToggleSelectable
DescriptionDefinitiongetCanToggleSelectable()Code
2742 | function Vehicle:getCanToggleSelectable() |
2743 | return false |
2744 | end |
getChildVehicles
DescriptionDefinitiongetChildVehicles()Code
2760 | function Vehicle:getChildVehicles() |
2761 | return self.childVehicles |
2762 | end |
getComponentMass
DescriptionReturns the mass of a componentDefinition
getComponentMass(table component)Arguments
table | component | component |
float | mass | mass |
3022 | function Vehicle:getComponentMass(component) |
3023 | if component ~= nil then |
3024 | return component.mass |
3025 | end |
3026 | |
3027 | return 0 |
3028 | end |
getDailyUpkeep
DescriptionGet daily up keepDefinition
getDailyUpkeep()Return Values
float | dailyUpkeep | daily up keep |
3748 | function Vehicle:getDailyUpkeep() |
3749 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
3750 | return Vehicle.calculateDailyUpkeep(storeItem, self.age, self.operatingTime, self.configurations) |
3751 | end |
getDeactivateOnLeave
DescriptionDefinitiongetDeactivateOnLeave()Code
2356 | function Vehicle:getDeactivateOnLeave() |
2357 | return true |
2358 | end |
getDefaultMass
DescriptionDefinitiongetDefaultMass()Code
3032 | function Vehicle:getDefaultMass() |
3033 | local mass = 0 |
3034 | |
3035 | for _, component in ipairs(self.components) do |
3036 | mass = mass + (component.defaultMass or 0) |
3037 | end |
3038 | |
3039 | return mass |
3040 | end |
getDistanceToNode
DescriptionDefinitiongetDistanceToNode()Code
3607 | function Vehicle:getDistanceToNode(node) |
3608 | self.interactionFlag = Vehicle.INTERACTION_FLAG_NONE |
3609 | return math.huge |
3610 | end |
getFillLevelInformation
DescriptionGet fill level informationDefinition
getFillLevelInformation(table fillLevelInformations)Arguments
table | fillLevelInformations | fill level informations |
3087 | function Vehicle:getFillLevelInformation(display) |
3088 | end |
getFullName
DescriptionDefinitiongetFullName()Code
3786 | function Vehicle:getFullName() |
3787 | local name = self:getName() |
3788 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
3789 | if storeItem ~= nil then |
3790 | local brand = g_brandManager:getBrandByIndex(self:getBrand()) |
3791 | if brand ~= nil then |
3792 | name = brand.title .. " " .. name |
3793 | end |
3794 | end |
3795 | |
3796 | return name |
3797 | end |
getImageFilename
DescriptionDefinitiongetImageFilename()Code
3818 | function Vehicle:getImageFilename() |
3819 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
3820 | if storeItem ~= nil and storeItem.configurations ~= nil then |
3821 | for configName, _ in pairs(storeItem.configurations) do |
3822 | local configId = self.configurations[configName] |
3823 | local config = storeItem.configurations[configName][configId] |
3824 | if config == nil then |
3825 | Logging.xmlWarning(self.xmlFile, "Vehicle has an invalid configuration value %s for %s", configId, configName) |
3826 | elseif config.vehicleIcon ~= nil and config.vehicleIcon ~= "" then |
3827 | return config.vehicleIcon |
3828 | end |
3829 | end |
3830 | end |
3831 | |
3832 | return storeItem.imageFilename |
3833 | end |
getInteractionHelp
DescriptionDefinitiongetInteractionHelp()Code
3601 | function Vehicle:getInteractionHelp() |
3602 | return "" |
3603 | end |
getIsActive
DescriptionDefinitiongetIsActive()Code
2400 | function Vehicle:getIsActive() |
2401 | if self.isBroken then |
2402 | return false |
2403 | end |
2404 | |
2405 | if self.forceIsActive then |
2406 | return true |
2407 | end |
2408 | |
2409 | return false |
2410 | end |
getIsActiveForInput
DescriptionDefinitiongetIsActiveForInput()Code
2414 | function Vehicle:getIsActiveForInput(ignoreSelection, activeForAI) |
2415 | if not self.allowsInput then |
2416 | return false |
2417 | end |
2418 | |
2419 | if not g_currentMission.isRunning then |
2420 | return false |
2421 | end |
2422 | |
2423 | if (activeForAI == nil or not activeForAI) and self:getIsAIActive() then |
2424 | return false |
2425 | end |
2426 | |
2427 | if not ignoreSelection or ignoreSelection == nil then |
2428 | local rootVehicle = self.rootVehicle |
2429 | if rootVehicle ~= nil then |
2430 | local selectedObject = rootVehicle:getSelectedVehicle() |
2431 | if self ~= selectedObject then |
2432 | return false |
2433 | end |
2434 | else |
2435 | return false |
2436 | end |
2437 | end |
2438 | |
2439 | local rootAttacherVehicle = self.rootVehicle |
2440 | if rootAttacherVehicle ~= self then |
2441 | if not rootAttacherVehicle:getIsActiveForInput(true, activeForAI) then |
2442 | return false |
2443 | end |
2444 | else |
2445 | -- if it is the root vehicle and it is not enterable we check for a attacherVehicle |
2446 | if self.getIsEntered == nil and self.getAttacherVehicle ~= nil and self:getAttacherVehicle() == nil then |
2447 | return false |
2448 | end |
2449 | end |
2450 | |
2451 | return true |
2452 | end |
getIsActiveForSound
DescriptionDefinitiongetIsActiveForSound()Code
2456 | function Vehicle:getIsActiveForSound() |
2457 | print("Warning: Vehicle:getIsActiveForSound() is deprecated") |
2458 | return false |
2459 | end |
getIsAIActive
DescriptionDefinitiongetIsAIActive()Code
3614 | function Vehicle:getIsAIActive() |
3615 | if self.getAttacherVehicle ~= nil then |
3616 | local attacherVehicle = self:getAttacherVehicle() |
3617 | if attacherVehicle ~= nil then |
3618 | return attacherVehicle:getIsAIActive() |
3619 | end |
3620 | end |
3621 | |
3622 | return false |
3623 | end |
getIsAutomaticShiftingAllowed
DescriptionDefinitiongetIsAutomaticShiftingAllowed()Code
3687 | function Vehicle:getIsAutomaticShiftingAllowed() |
3688 | return true |
3689 | end |
getIsInUse
DescriptionDefinitiongetIsInUse()Code
3849 | function Vehicle:getIsInUse(connection) |
3850 | return false |
3851 | end |
getIsLowered
DescriptionDefinitiongetIsLowered()Code
2463 | function Vehicle:getIsLowered(defaultIsLowered) |
2464 | return false |
2465 | end |
getIsMapHotspotVisible
DescriptionDefinitiongetIsMapHotspotVisible()Code
3938 | function Vehicle:getIsMapHotspotVisible() |
3939 | return true |
3940 | end |
getIsOnField
DescriptionReturns true if vehicle is on a fieldDefinition
getIsOnField()Return Values
boolean | isOnField | is on field |
2307 | function Vehicle:getIsOnField() |
2308 | for _,component in pairs(self.components) do |
2309 | local wx, wy, wz = localToWorld(component.node, getCenterOfMass(component.node)) |
2310 | |
2311 | local h = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz) |
2312 | if h-1 > wy then -- 1m threshold since ground tools are working slightly under the ground |
2313 | break |
2314 | end |
2315 | |
2316 | local isOnField, _ = FSDensityMapUtil.getFieldDataAtWorldPosition(wx, wy, wz) |
2317 | if isOnField then |
2318 | return true |
2319 | end |
2320 | end |
2321 | |
2322 | return false |
2323 | end |
getIsOperating
DescriptionReturns true if is operatingDefinition
getIsOperating()Return Values
boolean | isOperating | is operating |
2394 | function Vehicle:getIsOperating() |
2395 | return false |
2396 | end |
getIsPowered
DescriptionDefinitiongetIsPowered()Code
3627 | function Vehicle:getIsPowered() |
3628 | return true |
3629 | end |
getIsReadyForAutomatedTrainTravel
DescriptionDefinitiongetIsReadyForAutomatedTrainTravel()Code
3681 | function Vehicle:getIsReadyForAutomatedTrainTravel() |
3682 | return true |
3683 | end |
getIsSelected
DescriptionDefinitiongetIsSelected()Code
2903 | function Vehicle:getIsSelected() |
2904 | return self.selectionObject.isSelected |
2905 | end |
getIsSynchronized
DescriptionDefinitiongetIsSynchronized()Code
2362 | function Vehicle:getIsSynchronized() |
2363 | return self.loadingStep == Vehicle.LOAD_STEP_SYNCHRONIZED |
2364 | end |
getIsVehicleNode
DescriptionReturns true if node is from vehicleDefinition
getIsVehicleNode(Integer nodeId)Arguments
Integer | nodeId | node id |
boolean | isFromVehicle | is from vehicle |
2387 | function Vehicle:getIsVehicleNode(nodeId) |
2388 | return self.vehicleNodes[nodeId] ~= nil |
2389 | end |
getLastSpeed
DescriptionReturns last speed in kphDefinition
getLastSpeed(boolean useAttacherVehicleSpeed)Arguments
boolean | useAttacherVehicleSpeed | use speed of attacher vehicle |
float | lastSpeed | last speed |
2343 | function Vehicle:getLastSpeed(useAttacherVehicleSpeed) |
2344 | if useAttacherVehicleSpeed then |
2345 | if self.attacherVehicle ~= nil then |
2346 | return self.attacherVehicle:getLastSpeed(true) |
2347 | end |
2348 | end |
2349 | |
2350 | return self.lastSpeed * 3600 |
2351 | end |
getLimitedVehicleYPosition
DescriptionDefinitiongetLimitedVehicleYPosition()Code
2197 | function Vehicle:getLimitedVehicleYPosition(position) |
2198 | if position.posY == nil then |
2199 | -- vehicle position based on yOffset |
2200 | local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, position.posX, 300, position.posZ) |
2201 | return terrainHeight + Utils.getNoNil(position.yOffset, 0) |
2202 | end |
2203 | |
2204 | return position.posY |
2205 | end |
getMapHotspot
DescriptionDefinitiongetMapHotspot()Code
3924 | function Vehicle:getMapHotspot() |
3925 | return self.mapHotspot |
3926 | end |
getMaxComponentMassReached
DescriptionDefinitiongetMaxComponentMassReached()Code
2988 | function Vehicle:getMaxComponentMassReached() |
2989 | return (self.serverMass + 0.00001) >= (self.maxComponentMass - self.precalculatedMass) |
2990 | end |
getName
DescriptionDefinitiongetName()Code
3769 | function Vehicle:getName() |
3770 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
3771 | if storeItem ~= nil and storeItem.configurations ~= nil then |
3772 | for configName, _ in pairs(storeItem.configurations) do |
3773 | local configId = self.configurations[configName] |
3774 | local config = storeItem.configurations[configName][configId] |
3775 | if config.vehicleName ~= nil and config.vehicleName ~= "" then |
3776 | return config.vehicleName |
3777 | end |
3778 | end |
3779 | end |
3780 | |
3781 | return storeItem.name |
3782 | end |
getOperatingTime
DescriptionDefinitiongetOperatingTime()Code
3647 | function Vehicle:getOperatingTime() |
3648 | return self.operatingTime |
3649 | end |
getOverallCenterOfMass
DescriptionReturns overall center of mass of vehicleDefinition
getOverallCenterOfMass()Return Values
float | x | x |
float | y | y |
float | z | z |
3047 | function Vehicle:getOverallCenterOfMass() |
3048 | local cx, cy, cz = 0, 0, 0 |
3049 | local totalMass = self:getTotalMass(true) |
3050 | local numComponents = #self.components |
3051 | for i=1, numComponents do |
3052 | local component = self.components[i] |
3053 | local ccx, ccy, ccz = localToWorld(component.node, getCenterOfMass(component.node)) |
3054 | local percentage = self:getComponentMass(component) / totalMass |
3055 | |
3056 | cx, cy, cz = cx + ccx * percentage, cy + ccy * percentage, cz + ccz * percentage |
3057 | end |
3058 | |
3059 | return cx, cy, cz |
3060 | end |
getOwner
DescriptionGet owner of vehicleDefinition
getOwner()Return Values
table | owner | owner |
2369 | function Vehicle:getOwner() |
2370 | if self.owner ~= nil then |
2371 | return self.owner |
2372 | end |
2373 | |
2374 | return nil |
2375 | end |
getParentComponent
DescriptionGet parent component of nodeDefinition
getParentComponent(Integer node)Arguments
Integer | node | id of node |
Integer | parentComponent | id of parent component node |
2329 | function Vehicle:getParentComponent(node) |
2330 | while node ~= 0 do |
2331 | if self:getIsVehicleNode(node) then |
2332 | return node |
2333 | end |
2334 | node = getParent(node) |
2335 | end |
2336 | return 0 |
2337 | end |
getPrice
DescriptionReturns priceDefinition
getPrice(float price)Arguments
float | price | price |
2271 | function Vehicle:getPrice() |
2272 | return self.price |
2273 | end |
getPropertyState
DescriptionDefinitiongetPropertyState()Code
3855 | function Vehicle:getPropertyState() |
3856 | return self.propertyState |
3857 | end |
getRawSpeedLimit
DescriptionGet raw speed limit of vehicleDefinition
getRawSpeedLimit()Return Values
float | speedLimit | speed limit |
3729 | function Vehicle:getRawSpeedLimit() |
3730 | return self.speedLimit |
3731 | end |
getReloadXML
DescriptionGet reload xmlDefinition
getReloadXML(table vehicle)Arguments
table | vehicle | vehicle |
string | xml | xml |
4051 | function Vehicle.getReloadXML(vehicle) |
4052 | local vehicleXMLFile = XMLFile.create("vehicleXMLFile", "", "vehicles", Vehicle.xmlSchemaSavegame) |
4053 | if vehicleXMLFile ~= nil then |
4054 | local key = string.format("vehicles.vehicle(%d)", 0) |
4055 | |
4056 | vehicleXMLFile:setValue(key.."#id", 1) |
4057 | vehicleXMLFile:setValue(key.."#filename", HTMLUtil.encodeToHTML(NetworkUtil.convertToNetworkFilename(vehicle.configFileName))) |
4058 | vehicle.currentSavegameId = 1 |
4059 | vehicle:saveToXMLFile(vehicleXMLFile, key, {}) |
4060 | |
4061 | return vehicleXMLFile |
4062 | end |
4063 | |
4064 | return nil |
4065 | end |
getRepaintPrice
DescriptionDefinitiongetRepaintPrice()Code
2522 | function Vehicle:getRepaintPrice() |
2523 | return 0 |
2524 | end |
getRepairPrice
DescriptionDefinitiongetRepairPrice()Code
2516 | function Vehicle:getRepairPrice() |
2517 | return 0 |
2518 | end |
getRootVehicle
DescriptionDefinitiongetRootVehicle()Code
2748 | function Vehicle:getRootVehicle() |
2749 | return self.rootVehicle |
2750 | end |
getSelectedObject
DescriptionDefinitiongetSelectedObject()Code
2909 | function Vehicle:getSelectedObject() |
2910 | local rootVehicle = self.rootVehicle |
2911 | if rootVehicle == self then |
2912 | return self.currentSelection.object |
2913 | end |
2914 | |
2915 | return rootVehicle:getSelectedObject() |
2916 | end |
getSelectedVehicle
DescriptionDefinitiongetSelectedVehicle()Code
2920 | function Vehicle:getSelectedVehicle() |
2921 | local selectedObject = self:getSelectedObject() |
2922 | if selectedObject ~= nil then |
2923 | return selectedObject.vehicle |
2924 | end |
2925 | return nil |
2926 | end |
getSellPrice
DescriptionGet sell priceDefinition
getSellPrice()Return Values
float | sellPrice | sell price |
2278 | function Vehicle:getSellPrice() |
2279 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
2280 | return Vehicle.calculateSellPrice(storeItem, self.age, self.operatingTime, self:getPrice(), self:getRepairPrice(), self:getRepaintPrice()) |
2281 | end |
getSpecConfigValuesWeight
DescriptionDefinitiongetSpecConfigValuesWeight()Code
4275 | function Vehicle.getSpecConfigValuesWeight(storeItem, realItem, configurations, saleItem, returnValues, returnRange) |
4276 | if storeItem.specs.weight ~= nil then |
4277 | if storeItem.specs.weight.storeDataConfigs ~= nil then |
4278 | return storeItem.specs.weight.storeDataConfigs |
4279 | end |
4280 | end |
4281 | end |
getSpecValueAdditionalWeight
DescriptionDefinitiongetSpecValueAdditionalWeight()Code
4331 | function Vehicle.getSpecValueAdditionalWeight(storeItem, realItem, configurations, saleItem, returnValues, returnRange) |
4332 | if not g_currentMission.missionInfo.trailerFillLimit then |
4333 | return nil |
4334 | end |
4335 | |
4336 | if storeItem.specs.additionalWeight ~= nil then |
4337 | local baseWeight = Vehicle.getSpecValueWeight(storeItem, realItem, configurations, saleItem, true) |
4338 | if baseWeight ~= nil then |
4339 | local additionalWeight = storeItem.specs.additionalWeight - baseWeight |
4340 | |
4341 | if returnValues then |
4342 | return additionalWeight |
4343 | else |
4344 | return g_i18n:formatMass(additionalWeight) |
4345 | end |
4346 | end |
4347 | end |
4348 | |
4349 | return nil |
4350 | end |
getSpecValueCombinations
DescriptionDefinitiongetSpecValueCombinations()Code
4384 | function Vehicle.getSpecValueCombinations(storeItem, realItem) |
4385 | return storeItem.specs.combinations |
4386 | end |
getSpecValueDailyUpkeep
DescriptionDefinitiongetSpecValueDailyUpkeep()Code
4083 | function Vehicle.getSpecValueDailyUpkeep(storeItem, realItem, _, saleItem) |
4084 | local dailyUpkeep = storeItem.dailyUpkeep |
4085 | if realItem ~= nil and realItem.getDailyUpkeep ~= nil then |
4086 | dailyUpkeep = realItem:getDailyUpkeep() |
4087 | elseif saleItem ~= nil then |
4088 | dailyUpkeep = 0--Vehicle.calculateDailyUpkeep(storeItem, saleItem.age, saleItem.operatingTime, {}) |
4089 | end |
4090 | |
4091 | -- Hide when no upkeep |
4092 | if dailyUpkeep == 0 then |
4093 | return nil |
4094 | end |
4095 | |
4096 | return string.format(g_i18n:getText("shop_maintenanceValue"), g_i18n:formatMoney(dailyUpkeep, 2)) |
4097 | end |
getSpecValueOperatingTime
DescriptionDefinitiongetSpecValueOperatingTime()Code
4101 | function Vehicle.getSpecValueOperatingTime(storeItem, realItem, _, saleItem) |
4102 | local operatingTime |
4103 | |
4104 | if realItem ~= nil and realItem.operatingTime ~= nil then |
4105 | operatingTime = realItem.operatingTime |
4106 | elseif saleItem ~= nil then |
4107 | operatingTime = saleItem.operatingTime |
4108 | else |
4109 | return nil |
4110 | end |
4111 | |
4112 | local minutes = operatingTime / (1000 * 60) |
4113 | local hours = math.floor(minutes / 60) |
4114 | minutes = math.floor((minutes - hours * 60) / 6) |
4115 | |
4116 | return string.format(g_i18n:getText("shop_operatingTime"), hours, minutes) |
4117 | end |
getSpecValueSlots
DescriptionDefinitiongetSpecValueSlots()Code
4390 | function Vehicle.getSpecValueSlots(storeItem, realItem, configurations, saleItem, returnValues, returnRange) |
4391 | local numOwned = g_currentMission:getNumOfItems(storeItem) |
4392 | local valueText = "" |
4393 | if realItem ~= nil then |
4394 | local sellSlotUsage = g_currentMission.slotSystem:getStoreItemSlotUsage(storeItem, numOwned == 1) |
4395 | if sellSlotUsage ~= 0 then |
4396 | valueText = "+"..sellSlotUsage |
4397 | end |
4398 | else |
4399 | local buySlotUsage = g_currentMission.slotSystem:getStoreItemSlotUsage(storeItem, numOwned == 0) |
4400 | if storeItem.bundleInfo ~= nil then |
4401 | buySlotUsage = 0 |
4402 | for i=1, #storeItem.bundleInfo.bundleItems do |
4403 | local bundleInfo = storeItem.bundleInfo.bundleItems[i] |
4404 | local numBundleItemOwned = g_currentMission:getNumOfItems(bundleInfo.item) |
4405 | local usage = g_currentMission.slotSystem:getStoreItemSlotUsage(bundleInfo.item, numBundleItemOwned == 0) |
4406 | buySlotUsage = buySlotUsage + usage |
4407 | end |
4408 | end |
4409 | |
4410 | if buySlotUsage ~= 0 then |
4411 | valueText = "-"..buySlotUsage |
4412 | end |
4413 | end |
4414 | |
4415 | if valueText ~= "" then |
4416 | return valueText |
4417 | else |
4418 | return nil |
4419 | end |
4420 | end |
getSpecValueSpeedLimit
DescriptionDefinitiongetSpecValueSpeedLimit()Code
4222 | function Vehicle.getSpecValueSpeedLimit(storeItem, realItem) |
4223 | if storeItem.specs.speedLimit ~= nil then |
4224 | return string.format(g_i18n:getText("shop_maxSpeed"), string.format("%1d", g_i18n:getSpeed(storeItem.specs.speedLimit)), g_i18n:getSpeedMeasuringUnit()) |
4225 | end |
4226 | return nil |
4227 | end |
getSpecValueWeight
DescriptionDefinitiongetSpecValueWeight()Code
4284 | function Vehicle.getSpecValueWeight(storeItem, realItem, configurations, saleItem, returnValues, returnRange) |
4285 | if storeItem.specs.weight ~= nil then |
4286 | local vehicleMass, vehicleMassMax |
4287 | if realItem ~= nil then |
4288 | realItem:updateMass() |
4289 | vehicleMass = realItem:getTotalMass(true) |
4290 | else |
4291 | if storeItem.specs.weight.storeDataMin ~= nil then |
4292 | vehicleMass = (storeItem.specs.weight.storeDataMin or 0) / 1000 |
4293 | vehicleMassMax = (storeItem.specs.weight.storeDataMax or 0) / 1000 |
4294 | elseif storeItem.specs.weight.componentMass ~= nil then |
4295 | vehicleMass = storeItem.specs.weight.componentMass + (storeItem.specs.weight.wheelMassDefaultConfig or 0) |
4296 | vehicleMass = vehicleMass + FillUnit.getSpecValueStartFillUnitMassByMassData(storeItem.specs.weight.fillUnitMassData) |
4297 | end |
4298 | end |
4299 | |
4300 | if vehicleMass ~= nil and vehicleMass ~= 0 then |
4301 | if returnValues then |
4302 | if returnRange then |
4303 | return vehicleMass, vehicleMassMax |
4304 | else |
4305 | return vehicleMass |
4306 | end |
4307 | else |
4308 | if vehicleMassMax ~= nil and vehicleMassMax ~= 0 then |
4309 | return g_i18n:formatMass(vehicleMass, vehicleMassMax) |
4310 | else |
4311 | return g_i18n:formatMass(vehicleMass) |
4312 | end |
4313 | end |
4314 | end |
4315 | end |
4316 | |
4317 | return nil |
4318 | end |
getSpecValueWorkingWidth
DescriptionDefinitiongetSpecValueWorkingWidth()Code
4127 | function Vehicle.getSpecValueWorkingWidth(storeItem, realItem) |
4128 | if storeItem.specs.workingWidth ~= nil then |
4129 | return string.format(g_i18n:getText("shop_workingWidthValue"), g_i18n:formatNumber(storeItem.specs.workingWidth, 1, true)) |
4130 | end |
4131 | return nil |
4132 | end |
getSpecValueWorkingWidthConfig
DescriptionDefinitiongetSpecValueWorkingWidthConfig()Code
4175 | function Vehicle.getSpecValueWorkingWidthConfig(storeItem, realItem, configurations, saleItem, returnValues, returnRange) |
4176 | if storeItem.specs.workingWidthConfig ~= nil then |
4177 | local minWorkingWidth = 0 |
4178 | local workingWidth = 0 |
4179 | if configurations ~= nil and (realItem ~= nil or saleItem ~= nil) then |
4180 | for configName, config in pairs(configurations) do |
4181 | local width = storeItem.specs.workingWidthConfig[configName][config] |
4182 | if width ~= nil then |
4183 | workingWidth = width |
4184 | end |
4185 | end |
4186 | else |
4187 | local minWidth = math.huge |
4188 | local maxWidth = 0 |
4189 | for configName, configs in pairs(storeItem.specs.workingWidthConfig) do |
4190 | for _, width in pairs(configs) do |
4191 | minWidth = math.min(minWidth, width) |
4192 | maxWidth = math.max(maxWidth, width) |
4193 | end |
4194 | end |
4195 | |
4196 | minWorkingWidth = minWidth |
4197 | workingWidth = maxWidth |
4198 | end |
4199 | |
4200 | if not returnValues then |
4201 | if minWorkingWidth ~= 0 and minWorkingWidth ~= math.huge and minWorkingWidth ~= workingWidth then |
4202 | return string.format(g_i18n:getText("shop_workingWidthValue"), string.format("%s-%s", g_i18n:formatNumber(minWorkingWidth, 1, true), g_i18n:formatNumber(workingWidth, 1, true))) |
4203 | else |
4204 | return string.format(g_i18n:getText("shop_workingWidthValue"), g_i18n:formatNumber(workingWidth, 1, true)) |
4205 | end |
4206 | else |
4207 | return workingWidth |
4208 | end |
4209 | end |
4210 | |
4211 | return nil |
4212 | end |
getSpeedLimit
DescriptionGet speed limitDefinition
getSpeedLimit(boolean onlyIfWorking)Arguments
boolean | onlyIfWorking | only if working |
float | limit | limit |
boolean | doCheckSpeedLimit | do check speed limit |
3696 | function Vehicle:getSpeedLimit(onlyIfWorking) |
3697 | local limit = math.huge |
3698 | local doCheckSpeedLimit = self:doCheckSpeedLimit() |
3699 | if onlyIfWorking == nil or (onlyIfWorking and doCheckSpeedLimit) then |
3700 | limit = self:getRawSpeedLimit() |
3701 | |
3702 | local damage = self:getVehicleDamage() |
3703 | if damage > 0 then |
3704 | limit = limit * (1 - damage * Vehicle.DAMAGED_SPEEDLIMIT_REDUCTION) |
3705 | end |
3706 | end |
3707 | |
3708 | local attachedImplements |
3709 | if self.getAttachedImplements ~= nil then |
3710 | attachedImplements = self:getAttachedImplements() |
3711 | end |
3712 | if attachedImplements ~= nil then |
3713 | for _, implement in pairs(attachedImplements) do |
3714 | if implement.object ~= nil then |
3715 | local speed, implementDoCheckSpeedLimit = implement.object:getSpeedLimit(onlyIfWorking) |
3716 | if onlyIfWorking == nil or (onlyIfWorking and implementDoCheckSpeedLimit) then |
3717 | limit = math.min(limit, speed) |
3718 | end |
3719 | doCheckSpeedLimit = doCheckSpeedLimit or implementDoCheckSpeedLimit |
3720 | end |
3721 | end |
3722 | end |
3723 | return limit, doCheckSpeedLimit |
3724 | end |
getTotalMass
DescriptionReturns total mass of vehicle (optional including attached vehicles)Definition
getTotalMass(boolean onlyGivenVehicle)Arguments
boolean | onlyGivenVehicle | use only the given vehicle, if false or nil it includes all attachables |
float | totalMass | total mass |
3008 | function Vehicle:getTotalMass(onlyGivenVehicle) |
3009 | local mass = 0 |
3010 | |
3011 | for _, component in ipairs(self.components) do |
3012 | mass = mass + self:getComponentMass(component) |
3013 | end |
3014 | |
3015 | return mass |
3016 | end |
getUseTurnedOnSchema
DescriptionDefinitiongetUseTurnedOnSchema()Code
3559 | function Vehicle:getUseTurnedOnSchema() |
3560 | return false |
3561 | end |
getVehicleDamage
DescriptionDefinitiongetVehicleDamage()Code
2510 | function Vehicle:getVehicleDamage() |
2511 | return 0 |
2512 | end |
getVehicleWorldDirection
DescriptionReturns the world space direction of the vehicleDefinition
getVehicleWorldDirection()Return Values
float | x | x |
float | y | y |
float | z | z |
3080 | function Vehicle:getVehicleWorldDirection() |
3081 | return localDirectionToWorld(self.components[1].node, 0, 0, 1) |
3082 | end |
getVehicleWorldXRot
DescriptionReturns the world space x rotation in radDefinition
getVehicleWorldXRot()Return Values
float | rotation | rotation |
3065 | function Vehicle:getVehicleWorldXRot() |
3066 | local _, y, _ = localDirectionToWorld(self.components[1].node, 0, 0, 1) |
3067 | local slopeAngle = math.pi * 0.5 - math.atan(1/y) |
3068 | if slopeAngle > math.pi * 0.5 then |
3069 | slopeAngle = slopeAngle - math.pi |
3070 | end |
3071 | |
3072 | return slopeAngle |
3073 | end |
getWorkLoad
DescriptionDefinitiongetWorkLoad()Code
3589 | function Vehicle:getWorkLoad() |
3590 | return 0, 0 |
3591 | end |
hasInputConflictWithSelection
DescriptionDefinitionhasInputConflictWithSelection()Code
2930 | function Vehicle:hasInputConflictWithSelection(inputs) |
2931 | printCallstack() |
2932 | Logging.xmlWarning(self.xmlFile, "Vehicle:hasInputConflictWithSelection() is deprecated!") |
2933 | return false |
2934 | end |
init
DescriptionDefinitioninit()Code
258 | function Vehicle.init() |
259 | g_configurationManager:addConfigurationType("baseColor", g_i18n:getText("configuration_baseColor"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_COLOR) |
260 | g_configurationManager:addConfigurationType("design", g_i18n:getText("configuration_design"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_MULTIOPTION) |
261 | g_configurationManager:addConfigurationType("designColor", g_i18n:getText("configuration_designColor"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_COLOR) |
262 | g_configurationManager:addConfigurationType("vehicleType", g_i18n:getText("configuration_design"), nil, nil, ConfigurationUtil.getStoreAdditionalConfigData, nil, ConfigurationUtil.SELECTOR_MULTIOPTION) |
263 | |
264 | for i=2, 8 do |
265 | g_configurationManager:addConfigurationType(string.format("design%d", i), g_i18n:getText("configuration_design"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_MULTIOPTION) |
266 | end |
267 | |
268 | g_storeManager:addSpecType("age", "shopListAttributeIconLifeTime", nil, Vehicle.getSpecValueAge, "vehicle") |
269 | g_storeManager:addSpecType("operatingTime", "shopListAttributeIconOperatingHours", nil, Vehicle.getSpecValueOperatingTime, "vehicle") |
270 | g_storeManager:addSpecType("dailyUpkeep", "shopListAttributeIconMaintenanceCosts", nil, Vehicle.getSpecValueDailyUpkeep, "vehicle") |
271 | g_storeManager:addSpecType("workingWidth", "shopListAttributeIconWorkingWidth", Vehicle.loadSpecValueWorkingWidth, Vehicle.getSpecValueWorkingWidth, "vehicle") |
272 | g_storeManager:addSpecType("workingWidthConfig", "shopListAttributeIconWorkingWidth", Vehicle.loadSpecValueWorkingWidthConfig, Vehicle.getSpecValueWorkingWidthConfig, "vehicle") |
273 | g_storeManager:addSpecType("speedLimit", "shopListAttributeIconWorkSpeed", Vehicle.loadSpecValueSpeedLimit, Vehicle.getSpecValueSpeedLimit, "vehicle") |
274 | g_storeManager:addSpecType("weight", "shopListAttributeIconWeight", Vehicle.loadSpecValueWeight, Vehicle.getSpecValueWeight, "vehicle", nil, Vehicle.getSpecConfigValuesWeight) |
275 | g_storeManager:addSpecType("additionalWeight", "shopListAttributeIconAdditionalWeight", Vehicle.loadSpecValueAdditionalWeight, Vehicle.getSpecValueAdditionalWeight, "vehicle") |
276 | g_storeManager:addSpecType("combinations", nil, Vehicle.loadSpecValueCombinations, Vehicle.getSpecValueCombinations, "vehicle") |
277 | g_storeManager:addSpecType("slots", "shopListAttributeIconSlots", nil, Vehicle.getSpecValueSlots, "vehicle") |
278 | |
279 | Vehicle.xmlSchema = XMLSchema.new("vehicle") |
280 | Vehicle.xmlSchemaSounds = XMLSchema.new("vehicle_sounds") |
281 | Vehicle.xmlSchemaSounds:setRootNodeName("sounds") |
282 | Vehicle.xmlSchema:addSubSchema(Vehicle.xmlSchemaSounds, "sounds") |
283 | |
284 | Vehicle.xmlSchemaSavegame = XMLSchema.new("savegame_vehicles") |
285 | |
286 | Vehicle.registers() |
287 | end |
interact
DescriptionDefinitioninteract()Code
3596 | function Vehicle:interact() |
3597 | end |
load
DescriptionDefinitionload()Code
525 | function Vehicle:load(vehicleData, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments) |
526 | if asyncCallbackFunction == nil then |
527 | Logging.xmlWarning(self.xmlFile, "Missing async callback function for '%s'", vehicleData.filename) |
528 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_ERROR) |
529 | printCallstack() |
530 | return self.loadingState |
531 | end |
532 | |
533 | local modName, baseDirectory = Utils.getModNameAndBaseDirectory(vehicleData.filename) |
534 | |
535 | self:setLoadingStep(Vehicle.LOAD_STEP_PRE_LOAD) |
536 | |
537 | self.configFileName = vehicleData.filename |
538 | self.configFileNameClean = Utils.getFilenameInfo(vehicleData.filename, true) |
539 | self.baseDirectory = baseDirectory |
540 | self.customEnvironment = modName |
541 | self.typeName = vehicleData.typeName |
542 | self.isVehicleSaved = Utils.getNoNil(vehicleData.isVehicleSaved, true) |
543 | self.configurations = Utils.getNoNil(vehicleData.configurations, {}) |
544 | self.boughtConfigurations = Utils.getNoNil(vehicleData.boughtConfigurations, {}) |
545 | |
546 | local typeDef = g_vehicleTypeManager:getTypeByName(self.typeName) |
547 | if typeDef == nil then |
548 | Logging.xmlWarning(self.xmlFile, "Unable to find vehicleType '%s'", self.typeName) |
549 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_ERROR) |
550 | return self.loadingState |
551 | end |
552 | |
553 | self.type = typeDef |
554 | self.specializations = typeDef.specializations |
555 | self.specializationNames = typeDef.specializationNames |
556 | self.specializationsByName = typeDef.specializationsByName |
557 | self.eventListeners = table.copy(typeDef.eventListeners, 2) |
558 | self.actionEvents = {} |
559 | self.xmlFile = XMLFile.load("vehicleXml", vehicleData.filename, Vehicle.xmlSchema) |
560 | self.savegame = vehicleData.savegame |
561 | self.isAddedToPhysics = false |
562 | |
563 | self.additionalLoadParameters = vehicleData.additionalLoadParameters |
564 | |
565 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
566 | if storeItem ~= nil then |
567 | self.brand = g_brandManager:getBrandByIndex(storeItem.brandIndex) |
568 | self.lifetime = storeItem.lifetime |
569 | end |
570 | |
571 | self.externalSoundsFilename = self.xmlFile:getValue("vehicle.base.sounds#filename") |
572 | if self.externalSoundsFilename ~= nil then |
573 | self.externalSoundsFilename = Utils.getFilename(self.externalSoundsFilename, self.baseDirectory) |
574 | self.externalSoundsFile = XMLFile.load("TempExternalSounds", self.externalSoundsFilename, Vehicle.xmlSchemaSounds) |
575 | end |
576 | |
577 | self.soundVolumeFactor = self.xmlFile:getValue("vehicle.base.sounds#volumeFactor") |
578 | |
579 | -- pass function pointers from specializations to 'self' |
580 | for funcName, func in pairs(typeDef.functions) do |
581 | self[funcName] = func |
582 | end |
583 | |
584 | local data = {} |
585 | data.position = {posX=vehicleData.posX, posY=vehicleData.posY, posZ=vehicleData.posZ, yOffset=vehicleData.yOffset, isAbsolute=vehicleData.isAbsolute} |
586 | data.rotation = {rotX=vehicleData.rotX, rotY=vehicleData.rotY, rotZ=vehicleData.rotZ} |
587 | data.isVehicleSaved = vehicleData.isVehicleSaved |
588 | data.propertyState = vehicleData.propertyState |
589 | data.ownerFarmId = vehicleData.ownerFarmId |
590 | data.price = vehicleData.price |
591 | data.savegame = vehicleData.savegame |
592 | data.asyncCallbackFunction = asyncCallbackFunction |
593 | data.asyncCallbackObject = asyncCallbackObject |
594 | data.asyncCallbackArguments = asyncCallbackArguments |
595 | data.componentPositions = vehicleData.componentPositions |
596 | |
597 | -- check if one of the configurations is not set - e.g. if new configurations are available but not in savegame |
598 | local item = g_storeManager:getItemByXMLFilename(self.configFileName) |
599 | if item ~= nil and item.configurations ~= nil then |
600 | -- check if the loaded configurations do match the configuration sets |
601 | -- if not we apply the set which has the most common configurations |
602 | -- e.g. if a new configuration was added to the configuration set we make sure we don't break old savegames |
603 | if item.configurationSets ~= nil and #item.configurationSets > 0 then |
604 | if not StoreItemUtil.getConfigurationsMatchConfigSets(self.configurations, item.configurationSets) then |
605 | local closestSet, closestSetMatches = StoreItemUtil.getClosestConfigurationSet(self.configurations, item.configurationSets) |
606 | if closestSet ~= nil then |
607 | for configName, index in pairs(closestSet.configurations) do |
608 | self.configurations[configName] = index |
609 | end |
610 | |
611 | Logging.xmlInfo(self.xmlFile, "Savegame configurations to not match the configuration sets! Apply closest configuration set '%s' with %d matching configurations.", closestSet.name, closestSetMatches) |
612 | end |
613 | end |
614 | end |
615 | |
616 | for configName, _ in pairs(item.configurations) do |
617 | local defaultConfigId = StoreItemUtil.getDefaultConfigId(item, configName) |
618 | if self.configurations[configName] == nil then |
619 | ConfigurationUtil.setConfiguration(self, configName, defaultConfigId) |
620 | end |
621 | -- base configuration is always included |
622 | ConfigurationUtil.addBoughtConfiguration(self, configName, defaultConfigId) |
623 | end |
624 | -- check if currently used configurations are still available |
625 | for configName, value in pairs(self.configurations) do |
626 | if item.configurations[configName] == nil then |
627 | Logging.xmlWarning(self.xmlFile, "Configurations are not present anymore. Ignoring this configuration (%s)!", configName) |
628 | self.configurations[configName] = nil |
629 | self.boughtConfigurations[configName] = nil |
630 | else |
631 | local defaultConfigId = StoreItemUtil.getDefaultConfigId(item, configName) |
632 | if #item.configurations[configName] < value then |
633 | Logging.xmlWarning(self.xmlFile, "Configuration with index '%d' is not present anymore. Using default configuration instead!", value) |
634 | |
635 | if self.boughtConfigurations[configName] ~= nil then |
636 | self.boughtConfigurations[configName][value] = nil |
637 | if next(self.boughtConfigurations[configName]) == nil then |
638 | self.boughtConfigurations[configName] = nil |
639 | end |
640 | end |
641 | ConfigurationUtil.setConfiguration(self, configName, defaultConfigId) |
642 | else |
643 | ConfigurationUtil.addBoughtConfiguration(self, configName, value) |
644 | end |
645 | end |
646 | end |
647 | end |
648 | |
649 | for i=1, #self.specializations do |
650 | local specEntryName = "spec_" .. self.specializationNames[i] |
651 | if self[specEntryName] ~= nil then |
652 | Logging.xmlError(self.xmlFile, "The vehicle specialization '%s' could not be added because variable '%s' already exists!", self.specializationNames[i], specEntryName) |
653 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_ERROR) |
654 | end |
655 | |
656 | local env = {} |
657 | setmetatable(env, { __index = self } ) |
658 | env.actionEvents = {} |
659 | self[specEntryName] = env |
660 | end |
661 | |
662 | SpecializationUtil.raiseEvent(self, "onPreLoad", vehicleData.savegame) |
663 | if self.loadingState ~= VehicleLoadingUtil.VEHICLE_LOAD_OK then |
664 | Logging.xmlError(self.xmlFile, "Vehicle pre-loading failed!") |
665 | self.xmlFile:delete() |
666 | asyncCallbackFunction(asyncCallbackObject, nil, self.loadingState, asyncCallbackArguments) |
667 | return |
668 | end |
669 | |
670 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.filename", "vehicle.base.filename") --FS17 to FS19 |
671 | |
672 | self.i3dFilename = Utils.getFilename(self.xmlFile:getValue("vehicle.base.filename"), baseDirectory) |
673 | |
674 | self:setLoadingStep(Vehicle.LOAD_STEP_AWAIT_I3D) |
675 | |
676 | self.sharedLoadRequestId = g_i3DManager:loadSharedI3DFileAsync(self.i3dFilename, true, false, self.loadFinished, self, data) |
677 | end |
loadComponentFromXML
DescriptionLoad component from xmlDefinition
loadComponentFromXML(table component, integer xmlFile, string key, table rootPosition, Integer i)Arguments
table | component | component |
integer | xmlFile | id of xml object |
string | key | key |
table | rootPosition | root position (x, y, z) |
Integer | i | component index |
boolean | success | success |
3179 | function Vehicle:loadComponentFromXML(component, xmlFile, key, rootPosition, i) |
3180 | if not self.isServer then |
3181 | if getRigidBodyType(component.node) == RigidBodyType.DYNAMIC then |
3182 | setRigidBodyType(component.node, RigidBodyType.KINEMATIC) |
3183 | end |
3184 | end |
3185 | link(getRootNode(), component.node) |
3186 | if i == 1 then |
3187 | rootPosition[1], rootPosition[2], rootPosition[3] = getTranslation(component.node) |
3188 | if rootPosition[2] ~= 0 then |
3189 | Logging.xmlWarning(self.xmlFile, "Y-Translation of component 1 (node 0>) has to be 0. Current value is: %.5f", rootPosition[2]) |
3190 | end |
3191 | end |
3192 | |
3193 | if getRigidBodyType(component.node) == RigidBodyType.STATIC then |
3194 | component.isStatic = true |
3195 | elseif getRigidBodyType(component.node) == RigidBodyType.KINEMATIC then |
3196 | component.isKinematic = true |
3197 | elseif getRigidBodyType(component.node) == RigidBodyType.DYNAMIC then |
3198 | component.isDynamic = true |
3199 | end |
3200 | |
3201 | if not CollisionFlag.getHasFlagSet(component.node, CollisionFlag.VEHICLE) then |
3202 | Logging.xmlWarning(self.xmlFile, "Missing collision mask bit '%d'. Please add this bit to component node '%s'", CollisionFlag.getBit(CollisionFlag.VEHICLE), getName(component.node)) |
3203 | end |
3204 | |
3205 | if not CollisionFlag.getHasFlagSet(component.node, CollisionFlag.TRIGGER_VEHICLE) then |
3206 | Logging.xmlWarning(self.xmlFile, "Missing collision mask bit '%d'. Please add this bit to component node '%s'", CollisionFlag.getBit(CollisionFlag.TRIGGER_VEHICLE), getName(component.node)) |
3207 | end |
3208 | |
3209 | if not getVisibility(component.node) then |
3210 | Logging.xmlDevWarning(self.xmlFile, "Found hidden component '%s' in i3d file. Components are not allowed to be hidden!", getName(component.node)) |
3211 | setVisibility(component.node, true) |
3212 | end |
3213 | |
3214 | -- the position of the first component is the zero |
3215 | translate(component.node, -rootPosition[1], -rootPosition[2], -rootPosition[3]) |
3216 | local x,y,z = getTranslation(component.node) |
3217 | local rx,ry,rz = getRotation(component.node) |
3218 | component.originalTranslation = {x,y,z} |
3219 | component.originalRotation = {rx,ry,rz} |
3220 | |
3221 | component.sentTranslation = {x,y,z} |
3222 | component.sentRotation = {rx,ry,rz} |
3223 | |
3224 | component.defaultMass = nil |
3225 | component.mass = nil |
3226 | |
3227 | local mass = xmlFile:getValue(key.."#mass") |
3228 | if mass ~= nil then |
3229 | if mass < 10 then |
3230 | Logging.xmlDevWarning(self.xmlFile, "Mass is lower than 10kg for '%s'. Mass unit is kilogramms. Is this correct?", key) |
3231 | end |
3232 | if component.isDynamic then |
3233 | setMass(component.node, mass/1000) |
3234 | end |
3235 | |
3236 | component.defaultMass = mass/1000 |
3237 | component.mass = component.defaultMass |
3238 | component.lastMass = component.mass |
3239 | else |
3240 | Logging.xmlWarning(self.xmlFile, "Missing 'mass' for '%s'. Using default mass 500kg instead!", key) |
3241 | component.defaultMass = 0.5 |
3242 | component.mass = 0.5 |
3243 | component.lastMass = component.mass |
3244 | end |
3245 | |
3246 | local comX, comY, comZ = xmlFile:getValue(key .. "#centerOfMass") |
3247 | if comX ~= nil then |
3248 | setCenterOfMass(component.node, comX, comY, comZ) |
3249 | end |
3250 | local count = xmlFile:getValue(key .. "#solverIterationCount") |
3251 | if count ~= nil then |
3252 | setSolverIterationCount(component.node, count) |
3253 | component.solverIterationCount = count |
3254 | end |
3255 | component.motorized = xmlFile:getValue(key .. "#motorized") -- Note: motorized is nil if not set in the xml, and can be set by the wheels |
3256 | self.vehicleNodes[component.node] = {component=component} |
3257 | local clipDistance = getClipDistance(component.node) |
3258 | if clipDistance >= 1000000 and getVisibility(component.node) then |
3259 | local defaultClipdistance = 300 |
3260 | Logging.xmlWarning(self.xmlFile, "No clipdistance is set to component node '%s' (%s>). Set default clipdistance '%d'", getName(component.node), i-1, defaultClipdistance) |
3261 | setClipDistance(component.node, defaultClipdistance) |
3262 | end |
3263 | |
3264 | component.collideWithAttachables = xmlFile:getValue(key.."#collideWithAttachables", false) |
3265 | |
3266 | if getRigidBodyType(component.node) ~= RigidBodyType.NONE then |
3267 | if getLinearDamping(component.node) > 0.01 then |
3268 | Logging.xmlDevWarning(self.xmlFile, "Non-zero linear damping (%.4f) for component node '%s' (%s>). Is this correct?", getLinearDamping(component.node), getName(component.node), i-1) |
3269 | elseif getAngularDamping(component.node) > 0.05 then |
3270 | Logging.xmlDevWarning(self.xmlFile, "Large angular damping (%.4f) for component node '%s' (%s>). Is this correct?", getAngularDamping(component.node), getName(component.node), i-1) |
3271 | elseif getAngularDamping(component.node) < 0.0001 then |
3272 | Logging.xmlDevWarning(self.xmlFile, "Zero damping for component node '%s' (%s>). Is this correct?", getName(component.node), i-1) |
3273 | end |
3274 | end |
3275 | |
3276 | local name = getName(component.node) |
3277 | if not name:endsWith("component"..i) then |
3278 | Logging.xmlDevWarning(self.xmlFile, "Name of component '%d' ('%s') does not correpond with the component naming convention! (vehicleName_componentName_component%d)", i, name, i) |
3279 | end |
3280 | |
3281 | return true |
3282 | end |
loadComponentJointFromXML
DescriptionLoad component joints from xmlDefinition
loadComponentJointFromXML(table jointDesc, integer xmlFile, string key, Integer componentJointI, Integer jointNode, Integer index1, Integer index2)Arguments
table | jointDesc | joint desc |
integer | xmlFile | id of xml object |
string | key | key |
Integer | componentJointI | component joint index |
Integer | jointNode | id of joint node |
Integer | index1 | index of component 1 |
Integer | index2 | index of component 2 |
boolean | success | success |
3294 | function Vehicle:loadComponentJointFromXML(jointDesc, xmlFile, key, componentJointI, jointNode, index1, index2) |
3295 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, key .. "#indexActor1", key .. "#nodeActor1") --FS17 to FS19 |
3296 | |
3297 | jointDesc.componentIndices = {index1, index2} |
3298 | jointDesc.jointNode = jointNode |
3299 | jointDesc.jointNodeActor1 = xmlFile:getValue(key.."#nodeActor1", jointNode, self.components, self.i3dMappings) |
3300 | if self.isServer then |
3301 | if self.components[index1] == nil or self.components[index2] == nil then |
3302 | Logging.xmlWarning(self.xmlFile, "Invalid component indices (component1: %d, component2: %d) for component joint %d. Indices start with 1!", index1, index2, componentJointI) |
3303 | return false |
3304 | end |
3305 | |
3306 | local x, y, z = xmlFile:getValue( key.."#rotLimit") |
3307 | local rotLimits = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) } |
3308 | x, y, z = xmlFile:getValue( key.."#transLimit") |
3309 | local transLimits = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
3310 | jointDesc.rotLimit = rotLimits |
3311 | jointDesc.transLimit = transLimits |
3312 | |
3313 | x, y, z = xmlFile:getValue( key.."#rotMinLimit") |
3314 | local rotMinLimits = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } |
3315 | |
3316 | x, y, z = xmlFile:getValue( key.."#transMinLimit") |
3317 | local transMinLimits = { x,y,z } |
3318 | |
3319 | for i=1,3 do |
3320 | if rotMinLimits[i] == nil then |
3321 | if rotLimits[i] >= 0 then |
3322 | rotMinLimits[i] = -rotLimits[i] |
3323 | else |
3324 | rotMinLimits[i] = rotLimits[i]+1 |
3325 | end |
3326 | end |
3327 | if transMinLimits[i] == nil then |
3328 | if transLimits[i] >= 0 then |
3329 | transMinLimits[i] = -transLimits[i] |
3330 | else |
3331 | transMinLimits[i] = transLimits[i]+1 |
3332 | end |
3333 | end |
3334 | end |
3335 | |
3336 | jointDesc.jointLocalPoses = {} |
3337 | local trans = {localToLocal(jointDesc.jointNode, self.components[index1].node, 0, 0, 0)} |
3338 | local rot = {localRotationToLocal(jointDesc.jointNode, self.components[index1].node, 0, 0, 0)} |
3339 | jointDesc.jointLocalPoses[1] = {trans=trans, rot=rot} |
3340 | |
3341 | trans = {localToLocal(jointDesc.jointNodeActor1, self.components[index2].node, 0, 0, 0)} |
3342 | rot = {localRotationToLocal(jointDesc.jointNodeActor1, self.components[index2].node, 0, 0, 0)} |
3343 | jointDesc.jointLocalPoses[2] = {trans=trans, rot=rot} |
3344 | |
3345 | jointDesc.rotMinLimit = rotMinLimits |
3346 | jointDesc.transMinLimit = transMinLimits |
3347 | |
3348 | x, y, z = xmlFile:getValue( key.."#rotLimitSpring") |
3349 | local rotLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
3350 | x, y, z = xmlFile:getValue( key.."#rotLimitDamping") |
3351 | local rotLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
3352 | jointDesc.rotLimitSpring = rotLimitSpring |
3353 | jointDesc.rotLimitDamping = rotLimitDamping |
3354 | |
3355 | x, y, z = xmlFile:getValue( key.."#rotLimitForceLimit") |
3356 | local rotLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) } |
3357 | x, y, z = xmlFile:getValue( key.."#transLimitForceLimit") |
3358 | local transLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) } |
3359 | jointDesc.rotLimitForceLimit = rotLimitForceLimit |
3360 | jointDesc.transLimitForceLimit = transLimitForceLimit |
3361 | |
3362 | x, y, z = xmlFile:getValue( key.."#transLimitSpring") |
3363 | local transLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
3364 | x, y, z = xmlFile:getValue( key.."#transLimitDamping") |
3365 | local transLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
3366 | jointDesc.transLimitSpring = transLimitSpring |
3367 | jointDesc.transLimitDamping = transLimitDamping |
3368 | |
3369 | jointDesc.zRotationXOffset = 0 |
3370 | local zRotationNode = xmlFile:getValue(key.."#zRotationNode", nil, self.components, self.i3dMappings) |
3371 | if zRotationNode ~= nil then |
3372 | local _ |
3373 | jointDesc.zRotationXOffset,_,_ = localToLocal(zRotationNode, jointNode, 0,0,0) |
3374 | end |
3375 | |
3376 | jointDesc.isBreakable = xmlFile:getValue(key.."#breakable", false) |
3377 | if jointDesc.isBreakable then |
3378 | jointDesc.breakForce = xmlFile:getValue(key.."#breakForce", 10) |
3379 | jointDesc.breakTorque = xmlFile:getValue(key.."#breakTorque", 10) |
3380 | end |
3381 | jointDesc.enableCollision = xmlFile:getValue(key.."#enableCollision", false) |
3382 | |
3383 | -- Rotational drive |
3384 | x, y, z = xmlFile:getValue( key.."#maxRotDriveForce") |
3385 | local maxRotDriveForce = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
3386 | x, y, z = xmlFile:getValue( key.."#rotDriveVelocity") |
3387 | local rotDriveVelocity = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } -- convert from deg/s to rad/s |
3388 | x, y, z = xmlFile:getValue( key.."#rotDriveRotation") |
3389 | local rotDriveRotation = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } -- convert from deg to rad |
3390 | x, y, z = xmlFile:getValue( key.."#rotDriveSpring") |
3391 | local rotDriveSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
3392 | x, y, z = xmlFile:getValue( key.."#rotDriveDamping") |
3393 | local rotDriveDamping = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
3394 | |
3395 | jointDesc.rotDriveVelocity = rotDriveVelocity |
3396 | jointDesc.rotDriveRotation = rotDriveRotation |
3397 | jointDesc.rotDriveSpring = rotDriveSpring |
3398 | jointDesc.rotDriveDamping = rotDriveDamping |
3399 | jointDesc.maxRotDriveForce = maxRotDriveForce |
3400 | |
3401 | -- Translational drive |
3402 | x, y, z = xmlFile:getValue( key.."#maxTransDriveForce") |
3403 | local maxTransDriveForce = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
3404 | x, y, z = xmlFile:getValue( key.."#transDriveVelocity") |
3405 | local transDriveVelocity = { x,y,z } |
3406 | x, y, z = xmlFile:getValue( key.."#transDrivePosition") |
3407 | local transDrivePosition = { x,y,z } |
3408 | x, y, z = xmlFile:getValue( key.."#transDriveSpring") |
3409 | local transDriveSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
3410 | x, y, z = xmlFile:getValue( key.."#transDriveDamping") |
3411 | local transDriveDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
3412 | |
3413 | jointDesc.transDriveVelocity = transDriveVelocity |
3414 | jointDesc.transDrivePosition = transDrivePosition |
3415 | jointDesc.transDriveSpring = transDriveSpring |
3416 | jointDesc.transDriveDamping = transDriveDamping |
3417 | jointDesc.maxTransDriveForce = maxTransDriveForce |
3418 | |
3419 | jointDesc.initComponentPosition = xmlFile:getValue(key.."#initComponentPosition", true) |
3420 | |
3421 | jointDesc.jointIndex = 0 |
3422 | end |
3423 | |
3424 | return true |
3425 | end |
loadFinished
DescriptionDefinitionloadFinished()Code
681 | function Vehicle:loadFinished(i3dNode, failedReason, arguments, i3dLoadingId) |
682 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_OK) |
683 | self:setLoadingStep(Vehicle.LOAD_STEP_LOAD) |
684 | |
685 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.forcedMapHotspotType", "vehicle.base.mapHotspot#type") --FS17 to FS19 |
686 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.base.forcedMapHotspotType", "vehicle.base.mapHotspot#type") --FS17 to FS19 |
687 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.speedLimit#value", "vehicle.base.speedLimit#value") --FS17 to FS19 |
688 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.steeringAxleNode#index", "vehicle.base.steeringAxle#node") --FS17 to FS19 |
689 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.size#width", "vehicle.base.size#width") --FS17 to FS19 |
690 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.size#length", "vehicle.base.size#length") --FS17 to FS19 |
691 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.size#widthOffset", "vehicle.base.size#widthOffset") --FS17 to FS19 |
692 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.size#lengthOffset", "vehicle.base.size#lengthOffset") --FS17 to FS19 |
693 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.typeDesc", "vehicle.base.typeDesc") --FS17 to FS19 |
694 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.components", "vehicle.base.components") --FS17 to FS19 |
695 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.components.component", "vehicle.base.components.component") --FS17 to FS19 |
696 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.base.components.component1", "vehicle.base.components.component") --FS17 to FS19 |
697 | |
698 | local position = arguments.position |
699 | local rotation = arguments.rotation |
700 | local propertyState = arguments.propertyState |
701 | local ownerFarmId = arguments.ownerFarmId |
702 | local price = arguments.price |
703 | local savegame = arguments.savegame |
704 | local asyncCallbackFunction = arguments.asyncCallbackFunction |
705 | local asyncCallbackObject = arguments.asyncCallbackObject |
706 | local asyncCallbackArguments = arguments.asyncCallbackArguments |
707 | local componentPositions = arguments.componentPositions |
708 | |
709 | if i3dNode == 0 then |
710 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_ERROR) |
711 | Logging.xmlError(self.xmlFile, "Vehicle i3d loading failed!") |
712 | asyncCallbackFunction(asyncCallbackObject, self, self.loadingState, asyncCallbackArguments) |
713 | return |
714 | end |
715 | |
716 | if savegame ~= nil then |
717 | local i = 0 |
718 | while true do |
719 | local key = string.format(savegame.key..".boughtConfiguration(%d)", i) |
720 | if not savegame.xmlFile:hasProperty(key) then |
721 | break |
722 | end |
723 | local name = savegame.xmlFile:getValue(key.."#name") |
724 | local id = savegame.xmlFile:getValue(key.."#id") |
725 | ConfigurationUtil.addBoughtConfiguration(self, name, ConfigurationUtil.getConfigIdBySaveId(self.configFileName, name, id)) |
726 | i = i + 1 |
727 | end |
728 | |
729 | self.tourId = nil |
730 | local tourId = savegame.xmlFile:getValue(savegame.key.."#tourId") |
731 | if tourId ~= nil then |
732 | self.tourId = tourId |
733 | if g_currentMission ~= nil then |
734 | g_currentMission.guidedTour:addVehicle(self, self.tourId) |
735 | end |
736 | end |
737 | end |
738 | |
739 | self.age = 0 |
740 | self.propertyState = propertyState |
741 | self:setOwnerFarmId(ownerFarmId, true) |
742 | |
743 | if savegame ~= nil then |
744 | -- Load this early: it used by the vehicle load functions already |
745 | local farmId = savegame.xmlFile:getValue(savegame.key .. "#farmId", AccessHandler.EVERYONE) |
746 | if g_farmManager.spFarmWasMerged and farmId ~= AccessHandler.EVERYONE then |
747 | farmId = FarmManager.SINGLEPLAYER_FARM_ID |
748 | end |
749 | self:setOwnerFarmId(farmId, true) |
750 | end |
751 | |
752 | self.price = price |
753 | if self.price == 0 or self.price == nil then |
754 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
755 | self.price = StoreItemUtil.getDefaultPrice(storeItem, self.configurations) |
756 | end |
757 | self.typeDesc = self.xmlFile:getValue("vehicle.base.typeDesc", "TypeDescription", self.customEnvironment, true) |
758 | self.synchronizePosition = self.xmlFile:getValue("vehicle.base.synchronizePosition", true) |
759 | self.highPrecisionPositionSynchronization = false |
760 | self.supportsPickUp = self.xmlFile:getValue("vehicle.base.supportsPickUp", self.isPallet) |
761 | self.canBeReset = self.xmlFile:getValue("vehicle.base.canBeReset", true) |
762 | self.showInVehicleMenu = self.xmlFile:getValue("vehicle.base.showInVehicleMenu", true) |
763 | |
764 | |
765 | self.rootNode = getChildAt(i3dNode, 0) |
766 | self.serverMass = 0 |
767 | self.precalculatedMass = 0 -- mass of vehicle that is not included in serverMass (e.g. wheels) |
768 | self.isMassDirty = false |
769 | |
770 | self.currentUpdateDistance = 0 |
771 | self.lastDistanceToCamera = 0 |
772 | self.lodDistanceCoeff = getLODDistanceCoeff() |
773 | |
774 | self.components = {} |
775 | self.vehicleNodes = {} |
776 | |
777 | local realNumComponents = getNumOfChildren(i3dNode) |
778 | local rootPosition = {0,0,0} |
779 | local i = 1 |
780 | |
781 | local numComponents = self.xmlFile:getValue("vehicle.base.components#numComponents", realNumComponents) |
782 | self.maxComponentMass = self.xmlFile:getValue("vehicle.base.components#maxMass", math.huge) / 1000 |
783 | |
784 | while true do |
785 | local namei = string.format("vehicle.base.components.component(%d)", i - 1) |
786 | if not self.xmlFile:hasProperty(namei) then |
787 | break |
788 | end |
789 | if i > numComponents then |
790 | Logging.xmlWarning(self.xmlFile, "Invalid components count. I3D file has '%d' components, but tried to load component no. '%d'!", numComponents, i+1) |
791 | break |
792 | end |
793 | |
794 | local component = {node = getChildAt(i3dNode, 0)} |
795 | |
796 | if self:loadComponentFromXML(component, self.xmlFile, namei, rootPosition, i) then |
797 | local x,y,z = getWorldTranslation(component.node) |
798 | local qx,qy,qz,qw = getWorldQuaternion(component.node) |
799 | component.networkInterpolators = {} |
800 | component.networkInterpolators.position = InterpolatorPosition.new(x, y, z) |
801 | component.networkInterpolators.quaternion = InterpolatorQuaternion.new(qx, qy, qz, qw) |
802 | table.insert(self.components, component) |
803 | end |
804 | i = i + 1 |
805 | end |
806 | delete(i3dNode) |
807 | |
808 | self.numComponents = #self.components |
809 | if numComponents ~= self.numComponents then |
810 | Logging.xmlWarning(self.xmlFile, "I3D file offers '%d' objects, but '%d' components have been loaded!", numComponents, self.numComponents) |
811 | end |
812 | |
813 | if self.numComponents == 0 then |
814 | Logging.xmlWarning(self.xmlFile, "No components defined for vehicle!") |
815 | |
816 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_ERROR) |
817 | asyncCallbackFunction(asyncCallbackObject, self, self.loadingState, asyncCallbackArguments) |
818 | return |
819 | end |
820 | |
821 | self.defaultMass = 0 |
822 | for j=1, #self.components do |
823 | self.defaultMass = self.defaultMass + self.components[j].defaultMass |
824 | end |
825 | |
826 | -- load i3d mappings |
827 | self.i3dMappings = {} |
828 | I3DUtil.loadI3DMapping(self.xmlFile, "vehicle", self.components, self.i3dMappings, realNumComponents) |
829 | |
830 | -- need to be defined in vehicle because all vehicles can define a steering axle ref node |
831 | self.steeringAxleNode = self.xmlFile:getValue("vehicle.base.steeringAxle#node", nil, self.components, self.i3dMappings) |
832 | if self.steeringAxleNode == nil then |
833 | self.steeringAxleNode = self.components[1].node |
834 | end |
835 | |
836 | self:loadSchemaOverlay(self.xmlFile) |
837 | |
838 | -- load component joints |
839 | self.componentJoints = {} |
840 | |
841 | local componentJointI = 0 |
842 | while true do |
843 | local key = string.format("vehicle.base.components.joint(%d)", componentJointI) |
844 | local index1 = self.xmlFile:getValue(key.."#component1") |
845 | local index2 = self.xmlFile:getValue(key.."#component2") |
846 | |
847 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, key .. "#index", key .. "#node") --FS17 to FS19 |
848 | |
849 | if index1 == nil or index2 == nil then |
850 | break |
851 | end |
852 | |
853 | local jointNode = self.xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings) |
854 | if jointNode ~= nil and jointNode ~= 0 then |
855 | local jointDesc = {} |
856 | if self:loadComponentJointFromXML(jointDesc, self.xmlFile, key, componentJointI, jointNode, index1, index2) then |
857 | table.insert(self.componentJoints, jointDesc) |
858 | jointDesc.index = #self.componentJoints |
859 | end |
860 | end |
861 | componentJointI = componentJointI +1 |
862 | end |
863 | |
864 | local collisionPairI = 0 |
865 | self.collisionPairs = {} |
866 | while true do |
867 | local key = string.format("vehicle.base.components.collisionPair(%d)", collisionPairI) |
868 | if not self.xmlFile:hasProperty(key) then |
869 | break |
870 | end |
871 | local enabled = self.xmlFile:getValue(key.."#enabled") |
872 | local index1 = self.xmlFile:getValue(key.."#component1") |
873 | local index2 = self.xmlFile:getValue(key.."#component2") |
874 | if index1 ~= nil and index2 ~= nil and enabled ~= nil then |
875 | local component1 = self.components[index1] |
876 | local component2 = self.components[index2] |
877 | if component1 ~= nil and component2 ~= nil then |
878 | if not enabled then |
879 | table.insert(self.collisionPairs, {component1=component1, component2=component2, enabled=enabled}) |
880 | end |
881 | else |
882 | Logging.xmlWarning(self.xmlFile, "Failed to load collision pair '%s'. Unknown component indices. Indices start with 1.", key) |
883 | end |
884 | end |
885 | collisionPairI = collisionPairI +1 |
886 | end |
887 | |
888 | self.supportsRadio = self.xmlFile:getValue("vehicle.base.supportsRadio", true) |
889 | self.allowsInput = self.xmlFile:getValue("vehicle.base.input#allowed", true) |
890 | self.size = StoreItemUtil.getSizeValuesFromXML(self.xmlFile, "vehicle", 0, self.configurations) |
891 | self.yRotationOffset = self.xmlFile:getValue("vehicle.base.size#yRotation", 0.0) |
892 | self.showTailwaterDepthWarning = false |
893 | self.thresholdTailwaterDepthWarning = self.xmlFile:getValue("vehicle.base.tailwaterDepth#warning", 1.0) |
894 | self.thresholdTailwaterDepth = self.xmlFile:getValue("vehicle.base.tailwaterDepth#threshold", 2.5) |
895 | self.networkTimeInterpolator = InterpolationTime.new(1.2) |
896 | self.movingDirection = 0 |
897 | self.requiredDriveMode = 1 |
898 | self.rotatedTime = 0 |
899 | self.isBroken = false |
900 | self.forceIsActive = false |
901 | self.finishedFirstUpdate = false |
902 | self.lastPosition = nil |
903 | self.lastSpeed = 0 |
904 | self.lastSpeedReal = 0 |
905 | self.lastSignedSpeed = 0 |
906 | self.lastSignedSpeedReal = 0 |
907 | self.lastMovedDistance = 0 |
908 | self.lastSpeedAcceleration = 0 |
909 | self.lastMoveTime = -10000 |
910 | self.operatingTime = 0 |
911 | self.isSelectable = true |
912 | |
913 | self.isInWater = false |
914 | self.isInShallowWater = false |
915 | self.isInMediumWater = false |
916 | self.waterY = -200 |
917 | self.tailwaterDepth = -200 |
918 | self.waterCheckPosition = {0, 0, 0} |
919 | |
920 | self.selectionObjects = {} |
921 | self.currentSelection = {object=nil, index=0, subIndex=1} |
922 | self.selectionObject = {index=0, isSelected=false, vehicle=self, subSelections={}} |
923 | |
924 | self.childVehicles = {self} -- table including all attached children and the vehicle itself |
925 | self.rootVehicle = self |
926 | |
927 | self.registeredActionEvents = {} |
928 | self.actionEventUpdateRequested = false |
929 | self.vehicleDirtyFlag = self:getNextDirtyFlag() |
930 | |
931 | if g_currentMission ~= nil and g_currentMission.environment ~= nil then |
932 | g_messageCenter:subscribe(MessageType.DAY_CHANGED, self.dayChanged, self) |
933 | g_messageCenter:subscribe(MessageType.PERIOD_CHANGED, self.periodChanged, self) |
934 | end |
935 | |
936 | self.mapHotspotAvailable = self.xmlFile:getValue("vehicle.base.mapHotspot#available", true) |
937 | if self.mapHotspotAvailable then |
938 | local hotspotType = self.xmlFile:getValue("vehicle.base.mapHotspot#type", "OTHER") |
939 | self.mapHotspotType = VehicleHotspot.getTypeByName(hotspotType) or VehicleHotspot.TYPE.OTHER |
940 | self.mapHotspotHasDirection = self.xmlFile:getValue("vehicle.base.mapHotspot#hasDirection", true) |
941 | end |
942 | |
943 | local speedLimit = math.huge |
944 | for i=1, #self.specializations do |
945 | if self.specializations[i].getDefaultSpeedLimit ~= nil then |
946 | local limit = self.specializations[i].getDefaultSpeedLimit(self) |
947 | speedLimit = math.min(limit, speedLimit) |
948 | end |
949 | end |
950 | |
951 | self.checkSpeedLimit = speedLimit == math.huge |
952 | self.speedLimit = self.xmlFile:getValue("vehicle.base.speedLimit#value", speedLimit) |
953 | |
954 | -- |
955 | local objectChanges = {} |
956 | ObjectChangeUtil.loadObjectChangeFromXML(self.xmlFile, "vehicle.base.objectChanges", objectChanges, self.components, self) |
957 | ObjectChangeUtil.setObjectChanges(objectChanges, true) |
958 | |
959 | if self.configurations["vehicleType"] ~= nil then |
960 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.vehicleTypeConfigurations.vehicleTypeConfiguration", self.configurations["vehicleType"], self.components, self) |
961 | end |
962 | |
963 | local preLoadDesignConfigurations = self.xmlFile:getValue("vehicle.designConfigurations#preLoad", false) |
964 | if preLoadDesignConfigurations then |
965 | if self.configurations["design"] ~= nil then |
966 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.designConfigurations.designConfiguration", self.configurations["design"], self.components, self) |
967 | end |
968 | end |
969 | |
970 | |
971 | SpecializationUtil.raiseEvent(self, "onLoad", savegame) |
972 | if self.loadingState ~= VehicleLoadingUtil.VEHICLE_LOAD_OK then |
973 | Logging.xmlError(self.xmlFile, "Vehicle loading failed!") |
974 | asyncCallbackFunction(asyncCallbackObject, self, self.loadingState, asyncCallbackArguments) |
975 | return |
976 | end |
977 | |
978 | if self.actionController ~= nil then |
979 | self.actionController:load(savegame) |
980 | end |
981 | |
982 | SpecializationUtil.raiseEvent(self, "onRootVehicleChanged", self) |
983 | |
984 | -- apply material from all configurations |
985 | for configName, configId in pairs(self.configurations) do |
986 | ConfigurationUtil.applyConfigMaterials(self, self.xmlFile, configName, configId) |
987 | end |
988 | |
989 | -- apply design |
990 | if not preLoadDesignConfigurations then |
991 | if self.configurations["design"] ~= nil then |
992 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.designConfigurations.designConfiguration", self.configurations["design"], self.components, self) |
993 | end |
994 | end |
995 | |
996 | for j=2, 8 do |
997 | local name = string.format("design%d", j) |
998 | if self.configurations[name] ~= nil then |
999 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, string.format("vehicle.%sConfigurations.%sConfiguration", name, name), self.configurations[name], self.components, self) |
1000 | end |
1001 | end |
1002 | |
1003 | -- do coloring |
1004 | if self.configurations["baseColor"] ~= nil then |
1005 | ConfigurationUtil.setColor(self, self.xmlFile, "baseColor", self.configurations["baseColor"]) |
1006 | end |
1007 | |
1008 | -- do coloring |
1009 | if self.configurations["designColor"] ~= nil then |
1010 | ConfigurationUtil.setColor(self, self.xmlFile, "designColor", self.configurations["designColor"]) |
1011 | end |
1012 | |
1013 | -- move all components that are joint to other components to the joint node, so all specializations can move them in there postLoad |
1014 | if self.isServer then |
1015 | for _, jointDesc in pairs(self.componentJoints) do |
1016 | if jointDesc.initComponentPosition then |
1017 | local component2 = self.components[jointDesc.componentIndices[2]].node |
1018 | local jointNode = jointDesc.jointNode |
1019 | |
1020 | if self:getParentComponent(jointNode) == component2 then |
1021 | jointNode = jointDesc.jointNodeActor1 |
1022 | end |
1023 | |
1024 | if self:getParentComponent(jointNode) ~= component2 then |
1025 | setTranslation(component2, localToLocal(component2, jointNode, 0, 0, 0)) |
1026 | setRotation(component2, localRotationToLocal(component2, jointNode, 0, 0, 0)) |
1027 | link(jointNode, component2) |
1028 | end |
1029 | end |
1030 | end |
1031 | end |
1032 | |
1033 | self:setLoadingStep(Vehicle.LOAD_STEP_POST_LOAD) |
1034 | |
1035 | SpecializationUtil.raiseEvent(self, "onPostLoad", savegame) |
1036 | if self.loadingState ~= VehicleLoadingUtil.VEHICLE_LOAD_OK then |
1037 | Logging.xmlError(self.xmlFile, "Vehicle post-loading failed!") |
1038 | asyncCallbackFunction(asyncCallbackObject, self, self.loadingState, asyncCallbackArguments) |
1039 | return |
1040 | end |
1041 | |
1042 | -- move all components that are joint to other components back to the world, so the changes from the specs post load is applied to the world trans/rot |
1043 | if self.isServer then |
1044 | for _, jointDesc in pairs(self.componentJoints) do |
1045 | if jointDesc.initComponentPosition then |
1046 | local component2 = self.components[jointDesc.componentIndices[2]] |
1047 | local jointNode = jointDesc.jointNode |
1048 | |
1049 | if self:getParentComponent(jointNode) == component2.node then |
1050 | jointNode = jointDesc.jointNodeActor1 |
1051 | end |
1052 | |
1053 | if self:getParentComponent(jointNode) ~= component2.node then |
1054 | if getParent(component2.node) == jointNode then |
1055 | local ox, oy, oz = 0, 0, 0 |
1056 | if jointDesc.jointNodeActor1 ~= jointDesc.jointNode then |
1057 | local x1, y1, z1 = localToLocal(jointDesc.jointNode, component2.node, 0, 0, 0) |
1058 | local x2, y2, z2 = localToLocal(jointDesc.jointNodeActor1, component2.node, 0, 0, 0) |
1059 | ox, oy, oz = x1-x2, y1-y2, z1-z2 |
1060 | end |
1061 | |
1062 | local x, y, z = localToWorld(component2.node, ox, oy, oz) |
1063 | local rx, ry, rz = localRotationToWorld(component2.node, 0, 0, 0) |
1064 | |
1065 | link(getRootNode(), component2.node) |
1066 | setWorldTranslation(component2.node, x, y, z) |
1067 | setWorldRotation(component2.node, rx, ry, rz) |
1068 | |
1069 | component2.originalTranslation = {x, y, z} |
1070 | component2.originalRotation = {rx, ry, rz} |
1071 | |
1072 | component2.sentTranslation = {x, y, z} |
1073 | component2.sentRotation = {rx, ry, rz} |
1074 | end |
1075 | end |
1076 | end |
1077 | end |
1078 | |
1079 | for _, jointDesc in pairs(self.componentJoints) do |
1080 | self:setComponentJointFrame(jointDesc, 0) |
1081 | self:setComponentJointFrame(jointDesc, 1) |
1082 | end |
1083 | end |
1084 | |
1085 | if savegame ~= nil then |
1086 | self.currentSavegameId = savegame.xmlFile:getValue(savegame.key .. "#id") |
1087 | self.age = savegame.xmlFile:getValue(savegame.key.."#age", 0) |
1088 | self.price = savegame.xmlFile:getValue(savegame.key.."#price", self.price) |
1089 | self.propertyState = savegame.xmlFile:getValue(savegame.key.."#propertyState", self.propertyState) |
1090 | self.activeMissionId = savegame.xmlFile:getValue(savegame.key .. "#activeMissionId") |
1091 | |
1092 | local operatingTime = savegame.xmlFile:getValue(savegame.key .. "#operatingTime", self.operatingTime) * 1000 |
1093 | self:setOperatingTime(operatingTime, true) |
1094 | local findPlace = savegame.resetVehicles and not savegame.keepPosition |
1095 | if not findPlace then |
1096 | local isAbsolute = savegame.xmlFile:getValue(savegame.key.."#isAbsolute", false) |
1097 | if isAbsolute then |
1098 | local componentPosition = {} |
1099 | local i = 0 |
1100 | while true do |
1101 | local componentKey = string.format(savegame.key..".component(%d)", i) |
1102 | if not savegame.xmlFile:hasProperty(componentKey) then |
1103 | break |
1104 | end |
1105 | local componentIndex = savegame.xmlFile:getValue(componentKey.."#index") |
1106 | local x, y, z = savegame.xmlFile:getValue(componentKey.."#position") |
1107 | local xRot, yRot, zRot = savegame.xmlFile:getValue(componentKey.."#rotation") |
1108 | if x == nil or y == nil or z == nil or xRot == nil or yRot == nil or zRot == nil then |
1109 | findPlace = true |
1110 | break |
1111 | end |
1112 | if componentPosition[componentIndex] ~= nil then |
1113 | Logging.xmlWarning(savegame.xmlFile, "Duplicate component index '%s' in '%s' (%s)!", componentIndex, savegame.key, self.xmlFile.filename) |
1114 | else |
1115 | componentPosition[componentIndex] = {x=x, y=y, z=z, xRot=xRot, yRot=yRot, zRot=zRot} |
1116 | end |
1117 | i = i + 1 |
1118 | end |
1119 | local numSavegameComponents = table.size(componentPosition) |
1120 | if numSavegameComponents == #self.components then |
1121 | for j=1, #self.components do |
1122 | local p = componentPosition[j] |
1123 | self:setWorldPosition(p.x, p.y, p.z, p.xRot, p.yRot, p.zRot, j, true) |
1124 | end |
1125 | elseif numSavegameComponents >= 1 then |
1126 | local p = componentPosition[1] |
1127 | self:setAbsolutePosition(p.x, p.y, p.z, p.xRot, p.yRot, p.zRot) |
1128 | Logging.xmlWarning(savegame.xmlFile, "Invalid component count found in savegame for '%s'. Reset component positions.", self.xmlFile.filename) |
1129 | else |
1130 | findPlace = true |
1131 | Logging.xmlWarning(savegame.xmlFile, "No component positions found in savegame for '%s'!", self.xmlFile.filename) |
1132 | end |
1133 | else |
1134 | local yOffset = savegame.xmlFile:getValue(savegame.key.."#yOffset") |
1135 | local xPosition = savegame.xmlFile:getValue(savegame.key.."#xPosition") |
1136 | local zPosition = savegame.xmlFile:getValue(savegame.key.."#zPosition") |
1137 | local yRotation = savegame.xmlFile:getValue(savegame.key.."#yRotation") |
1138 | if yOffset == nil or xPosition == nil or zPosition == nil or yRotation == nil then |
1139 | findPlace = true |
1140 | else |
1141 | self:setRelativePosition(xPosition, yOffset, zPosition, math.rad(yRotation)) |
1142 | end |
1143 | end |
1144 | end |
1145 | if findPlace then |
1146 | if savegame.resetVehicles and not savegame.keepPosition then |
1147 | local x, _, z, place, width, offset = PlacementUtil.getPlace(g_currentMission:getResetPlaces(), self.size, g_currentMission.usedLoadPlaces, true, false, true) |
1148 | if x ~= nil then |
1149 | local yRot = MathUtil.getYRotationFromDirection(place.dirPerpX, place.dirPerpZ) |
1150 | PlacementUtil.markPlaceUsed(g_currentMission.usedLoadPlaces, place, width) |
1151 | self:setRelativePosition(x, offset, z, yRot) |
1152 | else |
1153 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_NO_SPACE) |
1154 | asyncCallbackFunction(asyncCallbackObject, self, self.loadingState, asyncCallbackArguments) |
1155 | return |
1156 | end |
1157 | else |
1158 | self:setLoadingState(VehicleLoadingUtil.VEHICLE_LOAD_DELAYED) |
1159 | end |
1160 | end |
1161 | else |
1162 | self:setAbsolutePosition(position.posX, self:getLimitedVehicleYPosition(position), position.posZ, rotation.rotX, rotation.rotY, rotation.rotZ, componentPositions) |
1163 | end |
1164 | |
1165 | self:updateSelectableObjects() |
1166 | self:setSelectedVehicle(self, nil, true) |
1167 | |
1168 | if self.rootVehicle == self then |
1169 | if savegame ~= nil then |
1170 | self.loadedSelectedObjectIndex = savegame.xmlFile:getValue(savegame.key.."#selectedObjectIndex") |
1171 | self.loadedSubSelectedObjectIndex = savegame.xmlFile:getValue(savegame.key.."#subSelectedObjectIndex") |
1172 | end |
1173 | end |
1174 | |
1175 | if componentPositions ~= nil and savegame == nil then |
1176 | self:setAbsolutePosition(position.posX, self:getLimitedVehicleYPosition(position), position.posZ, rotation.rotX, rotation.rotY, rotation.rotZ, componentPositions) |
1177 | end |
1178 | |
1179 | SpecializationUtil.raiseEvent(self, "onPreLoadFinished", self.savegame) |
1180 | |
1181 | self:addNodeObjectMapping(g_currentMission.nodeToObject) |
1182 | |
1183 | if not self.subLoadingTasksFinished then |
1184 | self:setLoadingStep(Vehicle.LOAD_STEP_AWAIT_SUB_I3D) |
1185 | end |
1186 | |
1187 | self.syncVehicleLoadingFinished = true |
1188 | |
1189 | self.subLoadingTasksFinishedAsyncData = {asyncCallbackFunction=asyncCallbackFunction, |
1190 | asyncCallbackObject=asyncCallbackObject, |
1191 | asyncCallbackArguments=asyncCallbackArguments} |
1192 | -- hide vehicle and remove from physics as long as the sub tasks are not finished |
1193 | self:setVisibility(false) |
1194 | |
1195 | self:tryFinishLoading() |
1196 | end |
loadObjectChangeValuesFromXML
DescriptionLoad object change from xmlDefinition
loadObjectChangeValuesFromXML(integer xmlFile, string key, integer node, table object)Arguments
integer | xmlFile | id of xml object |
string | key | key |
integer | node | node id |
table | object | object |
3956 | function Vehicle:loadObjectChangeValuesFromXML(xmlFile, key, node, object) |
3957 | end |
loadSchemaOverlay
DescriptionLoad schema overlay data from xml file The HUD draws the schema from this information and handles all required visual components.Definition
loadSchemaOverlay(integer xmlFile)Arguments
integer | xmlFile | id of xml object |
3507 | function Vehicle:loadSchemaOverlay(xmlFile) |
3508 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#file") --FS17 to FS19 |
3509 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#width") --FS17 to FS19 |
3510 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#height") --FS17 to FS19 |
3511 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#invisibleBorderRight", "vehicle.base.schemaOverlay#invisibleBorderRight") --FS17 to FS19 |
3512 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#invisibleBorderLeft", "vehicle.base.schemaOverlay#invisibleBorderLeft") --FS17 to FS19 |
3513 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#attacherJointPosition", "vehicle.base.schemaOverlay#attacherJointPosition") --FS17 to FS19 |
3514 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#basePosition", "vehicle.base.schemaOverlay#basePosition") --FS17 to FS19 |
3515 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#fileSelected") --FS17 to FS19 |
3516 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#fileTurnedOn") --FS17 to FS19 |
3517 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.schemaOverlay#fileSelectedTurnedOn") --FS17 to FS19 |
3518 | |
3519 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.base.schemaOverlay.default#name", "vehicle.base.schemaOverlay#name") --FS19 to FS22 |
3520 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.base.schemaOverlay.turnedOn#name") --FS19 to FS22 |
3521 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.base.schemaOverlay.selected#name") --FS19 to FS22 |
3522 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.base.schemaOverlay.turnedOnSelected#name") --FS19 to FS22 |
3523 | |
3524 | if xmlFile:hasProperty("vehicle.base.schemaOverlay") then |
3525 | XMLUtil.checkDeprecatedXMLElements(xmlFile, "vehicle.schemaOverlay.attacherJoint", "vehicle.attacherJoints.attacherJoint.schema") -- FS17 |
3526 | |
3527 | local x, y = xmlFile:getValue("vehicle.base.schemaOverlay#attacherJointPosition") |
3528 | local baseX, baseY = xmlFile:getValue("vehicle.base.schemaOverlay#basePosition") |
3529 | |
3530 | if baseX == nil then |
3531 | baseX = x |
3532 | end |
3533 | |
3534 | if baseY == nil then |
3535 | baseY = y |
3536 | end |
3537 | |
3538 | local schemaName = xmlFile:getValue("vehicle.base.schemaOverlay#name", "") |
3539 | |
3540 | local modPrefix = self.customEnvironment or "" |
3541 | schemaName = Vehicle.prefixSchemaOverlayName(schemaName, modPrefix) |
3542 | |
3543 | self.schemaOverlay = VehicleSchemaOverlayData.new( |
3544 | baseX, baseY, |
3545 | schemaName, |
3546 | xmlFile:getValue("vehicle.base.schemaOverlay#invisibleBorderRight"), |
3547 | xmlFile:getValue("vehicle.base.schemaOverlay#invisibleBorderLeft")) |
3548 | end |
3549 | end |
loadSpecValueAdditionalWeight
DescriptionDefinitionloadSpecValueAdditionalWeight()Code
4322 | function Vehicle.loadSpecValueAdditionalWeight(xmlFile, customEnvironment, baseDir) |
4323 | local maxWeight = xmlFile:getValue("vehicle.base.components#maxMass") |
4324 | if maxWeight ~= nil then |
4325 | return maxWeight / 1000 |
4326 | end |
4327 | end |
loadSpecValueCombinations
DescriptionDefinitionloadSpecValueCombinations()Code
4354 | function Vehicle.loadSpecValueCombinations(xmlFile, customEnvironment, baseDirectory) |
4355 | local combinations = {} |
4356 | |
4357 | xmlFile:iterate("vehicle.storeData.specs.combination", function(index, key) |
4358 | local combinationData = {} |
4359 | local xmlFilename = xmlFile:getValue(key .. "#xmlFilename") |
4360 | if xmlFilename ~= nil then |
4361 | combinationData.xmlFilename = Utils.getFilename(xmlFilename) |
4362 | combinationData.customXMLFilename = Utils.getFilename(xmlFilename, baseDirectory) |
4363 | end |
4364 | |
4365 | local filterCategoryStr = xmlFile:getValue(key .. "#filterCategory") |
4366 | if filterCategoryStr ~= nil then |
4367 | combinationData.filterCategories = filterCategoryStr:split(" ") |
4368 | end |
4369 | |
4370 | combinationData.filterSpec = xmlFile:getValue(key .. "#filterSpec") |
4371 | combinationData.filterSpecMin = xmlFile:getValue(key .. "#filterSpecMin", 0) |
4372 | combinationData.filterSpecMax = xmlFile:getValue(key .. "#filterSpecMax", 1) |
4373 | |
4374 | if combinationData.xmlFilename ~= nil or combinationData.filterCategories ~= nil or combinationData.filterSpec then |
4375 | table.insert(combinations, combinationData) |
4376 | end |
4377 | end) |
4378 | |
4379 | return combinations |
4380 | end |
loadSpecValueSpeedLimit
DescriptionDefinitionloadSpecValueSpeedLimit()Code
4216 | function Vehicle.loadSpecValueSpeedLimit(xmlFile, customEnvironment, baseDir) |
4217 | return xmlFile:getValue("vehicle.base.speedLimit#value") |
4218 | end |
loadSpecValueWeight
DescriptionDefinitionloadSpecValueWeight()Code
4231 | function Vehicle.loadSpecValueWeight(xmlFile, customEnvironment, baseDir) |
4232 | local massData = {} |
4233 | massData.componentMass = 0 |
4234 | xmlFile:iterate("vehicle.base.components.component", function(index, key) |
4235 | local mass = xmlFile:getValue(key .. "#mass", 0) / 1000 |
4236 | |
4237 | massData.componentMass = massData.componentMass + mass |
4238 | end) |
4239 | |
4240 | massData.fillUnitMassData = FillUnit.loadSpecValueFillUnitMassData(xmlFile, customEnvironment, baseDir) |
4241 | massData.wheelMassDefaultConfig = Wheels.loadSpecValueWheelWeight(xmlFile, customEnvironment, baseDir) |
4242 | |
4243 | local configMin, configMax |
4244 | massData.storeDataConfigs = {} |
4245 | xmlFile:iterate("vehicle.storeData.specs.weight.config", function(index, key) |
4246 | local config = {} |
4247 | config.name = xmlFile:getValue(key .. "#name") |
4248 | if config.name ~= nil then |
4249 | config.index = xmlFile:getValue(key .. "#index", 1) |
4250 | config.value = xmlFile:getValue(key .. "#value", 0) / 1000 |
4251 | |
4252 | configMin = math.min(configMin or math.huge, config.value * 1000) |
4253 | configMax = math.max(configMax or -math.huge, config.value * 1000) |
4254 | |
4255 | table.insert(massData.storeDataConfigs, config) |
4256 | end |
4257 | end) |
4258 | |
4259 | if #massData.storeDataConfigs == 0 then |
4260 | massData.storeDataConfigs = nil |
4261 | end |
4262 | |
4263 | if not xmlFile:getValue("vehicle.storeData.specs.weight#ignore", false) then |
4264 | massData.storeDataMin = xmlFile:getValue("vehicle.storeData.specs.weight#minValue", configMin) |
4265 | massData.storeDataMax = xmlFile:getValue("vehicle.storeData.specs.weight#maxValue", configMax) |
4266 | |
4267 | if massData.componentMass > 0 or massData.storeDataMin ~= nil then |
4268 | return massData |
4269 | end |
4270 | end |
4271 | end |
loadSpecValueWorkingWidth
DescriptionDefinitionloadSpecValueWorkingWidth()Code
4121 | function Vehicle.loadSpecValueWorkingWidth(xmlFile, customEnvironment, baseDir) |
4122 | return xmlFile:getValue("vehicle.storeData.specs.workingWidth") |
4123 | end |
loadSpecValueWorkingWidthConfig
DescriptionDefinitionloadSpecValueWorkingWidthConfig()Code
4136 | function Vehicle.loadSpecValueWorkingWidthConfig(xmlFile, customEnvironment, baseDir) |
4137 | local workingWidths = {} |
4138 | local isValid = false |
4139 | |
4140 | for name, id in pairs(g_configurationManager:getConfigurations()) do |
4141 | local specializationKey = g_configurationManager:getConfigurationAttribute(name, "xmlKey") |
4142 | if specializationKey ~= nil then |
4143 | specializationKey = "." .. specializationKey |
4144 | else |
4145 | specializationKey = "" |
4146 | end |
4147 | |
4148 | local configrationsKey = string.format("vehicle%s.%sConfigurations", specializationKey, name) |
4149 | |
4150 | workingWidths[name] = {} |
4151 | |
4152 | local i = 0 |
4153 | while true do |
4154 | local baseKey = string.format("%s.%sConfiguration(%d)", configrationsKey, name, i) |
4155 | if not xmlFile:hasProperty(baseKey) then |
4156 | break |
4157 | end |
4158 | |
4159 | workingWidths[name][i+1] = xmlFile:getValue(baseKey.."#workingWidth") |
4160 | if workingWidths[name][i+1] ~= nil then |
4161 | isValid = true |
4162 | end |
4163 | |
4164 | i = i + 1 |
4165 | end |
4166 | end |
4167 | |
4168 | if isValid then |
4169 | return workingWidths |
4170 | end |
4171 | end |
loadSubSharedI3DFile
DescriptionDefinitionloadSubSharedI3DFile()Code
1200 | function Vehicle:loadSubSharedI3DFile(filename, callOnCreate, addToPhysics, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments) |
1201 | if asyncCallbackFunction ~= nil and not self.syncVehicleLoadingFinished then |
1202 | local targetAsyncCallbackFunction = asyncCallbackFunction |
1203 | asyncCallbackFunction = function(target, i3dNode, ...) |
1204 | self.numPendingSubLoadingTasks = self.numPendingSubLoadingTasks - 1 |
1205 | self.subLoadingTasksFinished = self.numPendingSubLoadingTasks == 0 |
1206 | |
1207 | if self.isDeleted or self.isDeleting then |
1208 | delete(i3dNode) |
1209 | targetAsyncCallbackFunction(target, 0, ...) |
1210 | return |
1211 | else |
1212 | targetAsyncCallbackFunction(target, i3dNode, ...) |
1213 | |
1214 | if self.syncVehicleLoadingFinished then |
1215 | self:tryFinishLoading() |
1216 | end |
1217 | end |
1218 | end |
1219 | |
1220 | self.subLoadingTasksFinished = false |
1221 | self.numPendingSubLoadingTasks = self.numPendingSubLoadingTasks + 1 |
1222 | end |
1223 | |
1224 | local sharedLoadRequestId = g_i3DManager:loadSharedI3DFileAsync(filename, callOnCreate, addToPhysics, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments) |
1225 | |
1226 | return sharedLoadRequestId |
1227 | end |
new
DescriptionDefinitionnew()Code
498 | function Vehicle.new(isServer, isClient, customMt) |
499 | |
500 | local self = Object.new(isServer, isClient, customMt or Vehicle_mt) |
501 | |
502 | self.finishedLoading = false |
503 | self.isAddedToMission = false |
504 | self.isDeleted = false |
505 | self.updateLoopIndex = -1 |
506 | self.sharedLoadRequestId = nil |
507 | self.loadingState = VehicleLoadingUtil.VEHICLE_LOAD_OK |
508 | self.loadingStep = Vehicle.LOAD_STEP_CREATED |
509 | |
510 | self.syncVehicleLoadingFinished = false |
511 | self.subLoadingTasksFinished = true |
512 | self.numPendingSubLoadingTasks = 0 |
513 | |
514 | self.synchronizedConnections = {} -- start update stream if connection to client was synchronized |
515 | |
516 | self.actionController = VehicleActionController.new(self) |
517 | |
518 | self.tireTrackSystem = g_currentMission.tireTrackSystem |
519 | |
520 | return self |
521 | end |
onVehicleWakeUpCallback
DescriptionDefinitiononVehicleWakeUpCallback()Code
3735 | function Vehicle:onVehicleWakeUpCallback(id) |
3736 | self:raiseActive() |
3737 | end |
onWaterRaycastCallback
DescriptionDefinitiononWaterRaycastCallback()Code
2480 | function Vehicle:onWaterRaycastCallback(waterY, args) |
2481 | local y = self.waterCheckPosition[2] |
2482 | waterY = waterY or -2000 |
2483 | self.waterY = waterY |
2484 | self.isInWater = waterY > y |
2485 | self.isInShallowWater = false |
2486 | self.isInMediumWater = false |
2487 | if self.isInWater then |
2488 | local x, z = self.waterCheckPosition[1], self.waterCheckPosition[3] |
2489 | local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z) |
2490 | local waterDepth = math.max(0, waterY-terrainHeight) |
2491 | self.isInShallowWater = waterDepth <= 0.5 |
2492 | self.isInMediumWater = not self.isInShallowWater |
2493 | end |
2494 | self.tailwaterDepth = math.max(0, waterY-y) |
2495 | end |
periodChanged
DescriptionCalled if period changedDefinition
periodChanged()Code
3570 | function Vehicle:periodChanged() |
3571 | self.age = self.age + 1 |
3572 | end |
playControlledActions
DescriptionDefinitionplayControlledActions()Code
3883 | function Vehicle:playControlledActions() |
3884 | if self.actionController ~= nil then |
3885 | self.actionController:playControlledActions() |
3886 | end |
3887 | end |
postInit
DescriptionDefinitionpostInit()Code
291 | function Vehicle.postInit() |
292 | local schema = Vehicle.xmlSchema |
293 | for name, _ in pairs(g_configurationManager:getConfigurations()) do |
294 | local specializationKey = g_configurationManager:getConfigurationAttribute(name, "xmlKey") |
295 | if specializationKey ~= nil then |
296 | specializationKey = "." .. specializationKey |
297 | else |
298 | specializationKey = "" |
299 | end |
300 | |
301 | local configrationsKey = string.format("vehicle%s.%sConfigurations", specializationKey, name) |
302 | local configrationKey = string.format("%s.%sConfiguration(?)", configrationsKey, name) |
303 | |
304 | schema:setXMLSharedRegistration("configSize", configrationKey) |
305 | schema:register(XMLValueType.FLOAT, configrationKey .. ".size#width", "occupied width of the vehicle when loaded in this configuration") |
306 | schema:register(XMLValueType.FLOAT, configrationKey .. ".size#length", "occupied length of the vehicle when loaded in this configuration") |
307 | schema:register(XMLValueType.FLOAT, configrationKey .. ".size#height", "occupied height of the vehicle when loaded in this configuration") |
308 | schema:register(XMLValueType.FLOAT, configrationKey .. ".size#widthOffset", "width offset") |
309 | schema:register(XMLValueType.FLOAT, configrationKey .. ".size#lengthOffset", "length offset") |
310 | schema:register(XMLValueType.FLOAT, configrationKey .. ".size#heightOffset", "height offset") |
311 | schema:setXMLSharedRegistration() |
312 | |
313 | schema:register(XMLValueType.L10N_STRING, configrationsKey .. "#title", "Configration title to display in shop") |
314 | |
315 | schema:register(XMLValueType.L10N_STRING, configrationKey .. "#name", "Configuration name") |
316 | schema:register(XMLValueType.STRING, configrationKey .. "#params", "Extra paramters to insert in #name text") |
317 | schema:register(XMLValueType.L10N_STRING, configrationKey .. "#desc", "Configuration description") |
318 | schema:register(XMLValueType.FLOAT, configrationKey .. "#price", "Price of configuration", 0) |
319 | schema:register(XMLValueType.FLOAT, configrationKey .. "#dailyUpkeep", "Daily up keep with this configration", 0) |
320 | schema:register(XMLValueType.BOOL, configrationKey .. "#isDefault", "Is selected by default in shop config screen", false) |
321 | schema:register(XMLValueType.BOOL, configrationKey .. "#isSelectable", "Configuration can be selected in the shop", true) |
322 | schema:register(XMLValueType.STRING, configrationKey .. "#saveId", "Custom save id", "Number of configuration") |
323 | |
324 | schema:register(XMLValueType.STRING, configrationKey .. "#displayBrand", "If defined a brand icon is displayed in the shop config screen") |
325 | |
326 | schema:register(XMLValueType.STRING, configrationKey .. "#vehicleBrand", "Custom brand to display after bought with this configuration") |
327 | schema:register(XMLValueType.STRING, configrationKey .. "#vehicleName", "Custom vehicle name to display after bought with this configuration") |
328 | schema:register(XMLValueType.STRING, configrationKey .. "#vehicleIcon", "Custom icon to display after bought with this configuration") |
329 | |
330 | schema:register(XMLValueType.FLOAT, configrationKey .. "#workingWidth", "Work width to display in shop while config is active") |
331 | |
332 | ConfigurationUtil.registerMaterialConfigurationXMLPaths(schema, configrationKey) |
333 | end |
334 | end |
postReadStream
DescriptionCalled on client side on join when the vehicle was fully loadedDefinition
postReadStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
1528 | function Vehicle:postReadStream(streamId, connection) |
1529 | -- remove from physics to set static components correctly (normally they should not be added yet) |
1530 | self:removeFromPhysics() |
1531 | |
1532 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
1533 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
1534 | for i=1, #self.components do |
1535 | local component = self.components[i] |
1536 | local x = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
1537 | local y = NetworkUtil.readCompressedWorldPosition(streamId, paramsY) |
1538 | local z = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
1539 | local x_rot = NetworkUtil.readCompressedAngle(streamId) |
1540 | local y_rot = NetworkUtil.readCompressedAngle(streamId) |
1541 | local z_rot = NetworkUtil.readCompressedAngle(streamId) |
1542 | |
1543 | local qx,qy,qz,qw = mathEulerToQuaternion(x_rot,y_rot,z_rot) |
1544 | self:setWorldPositionQuaternion(x,y,z, qx,qy,qz,qw, i, true) |
1545 | |
1546 | component.networkInterpolators.position:setPosition(x,y,z) |
1547 | component.networkInterpolators.quaternion:setQuaternion(qx,qy,qz,qw) |
1548 | end |
1549 | self.networkTimeInterpolator:reset() |
1550 | |
1551 | -- add to physics and make visible |
1552 | self:setVisibility(true) |
1553 | self:addToPhysics() |
1554 | |
1555 | self.serverMass = streamReadFloat32(streamId) |
1556 | self.age = streamReadUInt16(streamId) |
1557 | self:setOperatingTime(streamReadFloat32(streamId), true) |
1558 | self.price = streamReadInt32(streamId) |
1559 | |
1560 | if Vehicle.DEBUG_NETWORK_READ_WRITE then |
1561 | print("-------------------------------------------------------------") |
1562 | print(self.configFileName) |
1563 | for _, spec in ipairs(self.eventListeners["onReadStream"]) do |
1564 | local className = ClassUtil.getClassName(spec) |
1565 | local startBits = streamGetReadOffset(streamId) |
1566 | spec["onReadStream"](self, streamId, connection) |
1567 | print(" "..tostring(className).." read " .. streamGetReadOffset(streamId)-startBits .. " bits") |
1568 | end |
1569 | else |
1570 | SpecializationUtil.raiseEvent(self, "onReadStream", streamId, connection) |
1571 | end |
1572 | |
1573 | self:setLoadingStep(Vehicle.LOAD_STEP_SYNCHRONIZED) |
1574 | end |
postWriteStream
DescriptionCalled on server side when vehicle is fully loaded on client sideDefinition
postWriteStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
1624 | function Vehicle:postWriteStream(streamId, connection) |
1625 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
1626 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
1627 | for i=1, #self.components do |
1628 | local component = self.components[i] |
1629 | local x,y,z = getWorldTranslation(component.node) |
1630 | local x_rot,y_rot,z_rot = getWorldRotation(component.node) |
1631 | NetworkUtil.writeCompressedWorldPosition(streamId, x, paramsXZ) |
1632 | NetworkUtil.writeCompressedWorldPosition(streamId, y, paramsY) |
1633 | NetworkUtil.writeCompressedWorldPosition(streamId, z, paramsXZ) |
1634 | NetworkUtil.writeCompressedAngle(streamId, x_rot) |
1635 | NetworkUtil.writeCompressedAngle(streamId, y_rot) |
1636 | NetworkUtil.writeCompressedAngle(streamId, z_rot) |
1637 | end |
1638 | |
1639 | streamWriteFloat32(streamId, self.serverMass) |
1640 | streamWriteUInt16(streamId, self.age) |
1641 | streamWriteFloat32(streamId, self.operatingTime) |
1642 | streamWriteInt32(streamId, self.price) |
1643 | |
1644 | if Vehicle.DEBUG_NETWORK_READ_WRITE then |
1645 | print("-------------------------------------------------------------") |
1646 | print(self.configFileName) |
1647 | for _, spec in ipairs(self.eventListeners["onWriteStream"]) do |
1648 | local className = ClassUtil.getClassName(spec) |
1649 | local startBits = streamGetWriteOffset(streamId) |
1650 | spec["onWriteStream"](self, streamId, connection) |
1651 | print(" "..tostring(className).." Wrote " .. streamGetWriteOffset(streamId)-startBits .. " bits") |
1652 | end |
1653 | else |
1654 | SpecializationUtil.raiseEvent(self, "onWriteStream", streamId, connection) |
1655 | end |
1656 | end |
raiseStateChange
DescriptionDefinitionraiseStateChange()Code
3576 | function Vehicle:raiseStateChange(state, ...) |
3577 | SpecializationUtil.raiseEvent(self, "onStateChange", state, ...) |
3578 | end |
readStream
DescriptionCalled on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
1453 | function Vehicle:readStream(streamId, connection, objectId) |
1454 | Vehicle:superClass().readStream(self, streamId, connection, objectId) |
1455 | |
1456 | local configFile = NetworkUtil.convertFromNetworkFilename(streamReadString(streamId)) |
1457 | local typeName = streamReadString(streamId) |
1458 | |
1459 | local configurations = {} |
1460 | local numConfigs = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
1461 | for _=1, numConfigs do |
1462 | local configNameId = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
1463 | local configId = streamReadUInt16(streamId) |
1464 | |
1465 | local configName = g_configurationManager:getConfigurationNameByIndex(configNameId+1) |
1466 | if configName ~= nil then |
1467 | configurations[configName] = configId+1 |
1468 | end |
1469 | end |
1470 | |
1471 | local boughtConfigurations = {} |
1472 | numConfigs = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
1473 | for _=1, numConfigs do |
1474 | local configNameId = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
1475 | local configName = g_configurationManager:getConfigurationNameByIndex(configNameId+1) |
1476 | boughtConfigurations[configName] = {} |
1477 | local numBoughtConfigIds = streamReadUInt16(streamId) |
1478 | for _=1, numBoughtConfigIds do |
1479 | local boughtConfigId = streamReadUInt16(streamId) |
1480 | boughtConfigurations[configName][boughtConfigId + 1] = true |
1481 | end |
1482 | end |
1483 | |
1484 | self.propertyState = streamReadUIntN(streamId, 2) |
1485 | |
1486 | if self.configFileName == nil then |
1487 | local vehicleData = {} |
1488 | vehicleData.filename = configFile |
1489 | vehicleData.isAbsolute = false |
1490 | vehicleData.typeName = typeName |
1491 | vehicleData.posX = 0 |
1492 | vehicleData.posY = nil |
1493 | vehicleData.posZ = 0 |
1494 | vehicleData.yOffset = 0 |
1495 | vehicleData.rotX = 0 |
1496 | vehicleData.rotY = 0 |
1497 | vehicleData.rotZ = 0 |
1498 | vehicleData.isVehicleSaved = true |
1499 | vehicleData.price = 0 |
1500 | vehicleData.propertyState = self.propertyState |
1501 | -- assign parent class Object's ownerFarmId field here to ensure ownership synchronization in MP: |
1502 | vehicleData.ownerFarmId = self.ownerFarmId |
1503 | vehicleData.isLeased = 0 |
1504 | vehicleData.configurations = configurations |
1505 | vehicleData.boughtConfigurations = boughtConfigurations |
1506 | |
1507 | local vehicle = self |
1508 | local asyncCallbackFunction = function(_, v, loadingState, args) |
1509 | if loadingState == VehicleLoadingUtil.VEHICLE_LOAD_OK then |
1510 | g_client:onObjectFinishedAsyncLoading(vehicle) |
1511 | else |
1512 | Logging.error("Failed to load vehicle on client") |
1513 | if v ~= nil then |
1514 | v:delete() |
1515 | end |
1516 | printCallstack() |
1517 | return |
1518 | end |
1519 | end |
1520 | self:load(vehicleData, asyncCallbackFunction, self) |
1521 | end |
1522 | end |
readUpdateStream
DescriptionCalled on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
integer | streamId | stream ID |
integer | timestamp | timestamp |
table | connection | connection |
1663 | function Vehicle:readUpdateStream(streamId, timestamp, connection) |
1664 | if connection.isServer then |
1665 | local hasUpdate = streamReadBool(streamId) |
1666 | if hasUpdate then |
1667 | self.networkTimeInterpolator:startNewPhaseNetwork() |
1668 | |
1669 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
1670 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
1671 | for i=1, #self.components do |
1672 | local component = self.components[i] |
1673 | if not component.isStatic then |
1674 | local x = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
1675 | local y = NetworkUtil.readCompressedWorldPosition(streamId, paramsY) |
1676 | local z = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
1677 | local x_rot = NetworkUtil.readCompressedAngle(streamId) |
1678 | local y_rot = NetworkUtil.readCompressedAngle(streamId) |
1679 | local z_rot = NetworkUtil.readCompressedAngle(streamId) |
1680 | local qx,qy,qz,qw = mathEulerToQuaternion(x_rot,y_rot,z_rot) |
1681 | |
1682 | component.networkInterpolators.position:setTargetPosition(x,y,z) |
1683 | component.networkInterpolators.quaternion:setTargetQuaternion(qx,qy,qz,qw) |
1684 | end |
1685 | end |
1686 | SpecializationUtil.raiseEvent(self, "onReadPositionUpdateStream", streamId, connection) |
1687 | end |
1688 | end |
1689 | |
1690 | if Vehicle.DEBUG_NETWORK_READ_WRITE_UPDATE then |
1691 | print("-------------------------------------------------------------") |
1692 | print(self.configFileName) |
1693 | for _, spec in ipairs(self.eventListeners["onReadUpdateStream"]) do |
1694 | local className = ClassUtil.getClassName(spec) |
1695 | local startBits = streamGetReadOffset(streamId) |
1696 | spec["onReadUpdateStream"](self, streamId, timestamp, connection) |
1697 | print(" "..tostring(className).." read " .. streamGetReadOffset(streamId)-startBits .. " bits") |
1698 | end |
1699 | else |
1700 | SpecializationUtil.raiseEvent(self, "onReadUpdateStream", streamId, timestamp, connection) |
1701 | end |
1702 | end |
registerActionEvents
DescriptionDefinitionregisterActionEvents()Code
2556 | function Vehicle:registerActionEvents(excludedVehicle) |
2557 | if not g_gui:getIsGuiVisible() and not g_currentMission.isPlayerFrozen and excludedVehicle ~= self then |
2558 | self.actionEventUpdateRequested = false |
2559 | |
2560 | local isActiveForInput = self:getIsActiveForInput() |
2561 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
2562 | |
2563 | if isActiveForInput then |
2564 | -- reset the action binding enabled state for bindings previously disabled during conflict resolution: |
2565 | g_inputBinding:resetActiveActionBindings() |
2566 | end |
2567 | |
2568 | -- safety: set the input registration context without changing the actual input context in case we're currently |
2569 | -- not in the vehicle context (e.g. due to network events) |
2570 | g_inputBinding:beginActionEventsModification(Vehicle.INPUT_CONTEXT_NAME) |
2571 | |
2572 | SpecializationUtil.raiseEvent(self, "onRegisterActionEvents", isActiveForInput, isActiveForInputIgnoreSelection) |
2573 | |
2574 | self:clearActionEventsTable(self.actionEvents) |
2575 | if self:getCanToggleSelectable() then |
2576 | local numSelectableObjects = 0 |
2577 | |
2578 | for _, object in ipairs(self.selectableObjects) do |
2579 | numSelectableObjects = numSelectableObjects + 1 + #object.subSelections |
2580 | end |
2581 | |
2582 | if numSelectableObjects > 1 then |
2583 | local _, actionEventId = self:addActionEvent(self.actionEvents, InputAction.SWITCH_IMPLEMENT, self, Vehicle.actionEventToggleSelection, false, true, false, true, nil) |
2584 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_LOW) |
2585 | |
2586 | _, actionEventId = self:addActionEvent(self.actionEvents, InputAction.SWITCH_IMPLEMENT_BACK, self, Vehicle.actionEventToggleSelectionReverse, false, true, false, true, nil) |
2587 | g_inputBinding:setActionEventTextVisibility(actionEventId, false) |
2588 | end |
2589 | end |
2590 | |
2591 | VehicleDebug.registerActionEvents(self) |
2592 | |
2593 | if Platform.gameplay.automaticVehicleControl then |
2594 | if self.actionController ~= nil then |
2595 | if self:getIsActiveForInput(true) then |
2596 | if self == self.rootVehicle then |
2597 | self.actionController:registerActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) |
2598 | end |
2599 | end |
2600 | end |
2601 | end |
2602 | |
2603 | g_inputBinding:endActionEventsModification() |
2604 | end |
2605 | end |
registerEvents
DescriptionDefinitionregisterEvents()Code
93 | function Vehicle.registerEvents(vehicleType) |
94 | SpecializationUtil.registerEvent(vehicleType, "onPreLoad") |
95 | SpecializationUtil.registerEvent(vehicleType, "onLoad") |
96 | SpecializationUtil.registerEvent(vehicleType, "onPostLoad") |
97 | SpecializationUtil.registerEvent(vehicleType, "onPreLoadFinished") |
98 | SpecializationUtil.registerEvent(vehicleType, "onLoadFinished") |
99 | SpecializationUtil.registerEvent(vehicleType, "onPreDelete") |
100 | SpecializationUtil.registerEvent(vehicleType, "onDelete") |
101 | SpecializationUtil.registerEvent(vehicleType, "onSave") |
102 | SpecializationUtil.registerEvent(vehicleType, "onReadStream") |
103 | SpecializationUtil.registerEvent(vehicleType, "onWriteStream") |
104 | SpecializationUtil.registerEvent(vehicleType, "onReadUpdateStream") |
105 | SpecializationUtil.registerEvent(vehicleType, "onWriteUpdateStream") |
106 | SpecializationUtil.registerEvent(vehicleType, "onReadPositionUpdateStream") |
107 | SpecializationUtil.registerEvent(vehicleType, "onWritePositionUpdateStream") |
108 | SpecializationUtil.registerEvent(vehicleType, "onPreUpdate") |
109 | SpecializationUtil.registerEvent(vehicleType, "onUpdate") |
110 | SpecializationUtil.registerEvent(vehicleType, "onUpdateInterpolation") |
111 | SpecializationUtil.registerEvent(vehicleType, "onUpdateDebug") |
112 | SpecializationUtil.registerEvent(vehicleType, "onPostUpdate") |
113 | SpecializationUtil.registerEvent(vehicleType, "onUpdateTick") |
114 | SpecializationUtil.registerEvent(vehicleType, "onPostUpdateTick") |
115 | SpecializationUtil.registerEvent(vehicleType, "onUpdateEnd") |
116 | SpecializationUtil.registerEvent(vehicleType, "onDraw") |
117 | SpecializationUtil.registerEvent(vehicleType, "onDrawUIInfo") |
118 | SpecializationUtil.registerEvent(vehicleType, "onActivate") |
119 | SpecializationUtil.registerEvent(vehicleType, "onDeactivate") |
120 | SpecializationUtil.registerEvent(vehicleType, "onStateChange") |
121 | SpecializationUtil.registerEvent(vehicleType, "onRegisterActionEvents") |
122 | SpecializationUtil.registerEvent(vehicleType, "onRootVehicleChanged") |
123 | SpecializationUtil.registerEvent(vehicleType, "onSelect") |
124 | SpecializationUtil.registerEvent(vehicleType, "onUnselect") |
125 | SpecializationUtil.registerEvent(vehicleType, "onSetBroken") |
126 | end |
registerFunctions
DescriptionDefinitionregisterFunctions()Code
130 | function Vehicle.registerFunctions(vehicleType) |
131 | SpecializationUtil.registerFunction(vehicleType, "setOwnerFarmId", Vehicle.setOwnerFarmId) |
132 | SpecializationUtil.registerFunction(vehicleType, "loadSubSharedI3DFile", Vehicle.loadSubSharedI3DFile) |
133 | SpecializationUtil.registerFunction(vehicleType, "drawUIInfo", Vehicle.drawUIInfo) |
134 | SpecializationUtil.registerFunction(vehicleType, "raiseActive", Vehicle.raiseActive) |
135 | SpecializationUtil.registerFunction(vehicleType, "setLoadingState", Vehicle.setLoadingState) |
136 | SpecializationUtil.registerFunction(vehicleType, "setLoadingStep", Vehicle.setLoadingStep) |
137 | SpecializationUtil.registerFunction(vehicleType, "addNodeObjectMapping", Vehicle.addNodeObjectMapping) |
138 | SpecializationUtil.registerFunction(vehicleType, "removeNodeObjectMapping", Vehicle.removeNodeObjectMapping) |
139 | SpecializationUtil.registerFunction(vehicleType, "addToPhysics", Vehicle.addToPhysics) |
140 | SpecializationUtil.registerFunction(vehicleType, "removeFromPhysics", Vehicle.removeFromPhysics) |
141 | SpecializationUtil.registerFunction(vehicleType, "setVisibility", Vehicle.setVisibility) |
142 | SpecializationUtil.registerFunction(vehicleType, "setRelativePosition", Vehicle.setRelativePosition) |
143 | SpecializationUtil.registerFunction(vehicleType, "setAbsolutePosition", Vehicle.setAbsolutePosition) |
144 | SpecializationUtil.registerFunction(vehicleType, "getLimitedVehicleYPosition", Vehicle.getLimitedVehicleYPosition) |
145 | SpecializationUtil.registerFunction(vehicleType, "setWorldPosition", Vehicle.setWorldPosition) |
146 | SpecializationUtil.registerFunction(vehicleType, "setWorldPositionQuaternion", Vehicle.setWorldPositionQuaternion) |
147 | SpecializationUtil.registerFunction(vehicleType, "updateVehicleSpeed", Vehicle.updateVehicleSpeed) |
148 | SpecializationUtil.registerFunction(vehicleType, "getUpdatePriority", Vehicle.getUpdatePriority) |
149 | SpecializationUtil.registerFunction(vehicleType, "getPrice", Vehicle.getPrice) |
150 | SpecializationUtil.registerFunction(vehicleType, "getSellPrice", Vehicle.getSellPrice) |
151 | SpecializationUtil.registerFunction(vehicleType, "getDailyUpkeep", Vehicle.getDailyUpkeep) |
152 | SpecializationUtil.registerFunction(vehicleType, "getIsOnField", Vehicle.getIsOnField) |
153 | SpecializationUtil.registerFunction(vehicleType, "getParentComponent", Vehicle.getParentComponent) |
154 | SpecializationUtil.registerFunction(vehicleType, "getLastSpeed", Vehicle.getLastSpeed) |
155 | SpecializationUtil.registerFunction(vehicleType, "getDeactivateOnLeave", Vehicle.getDeactivateOnLeave) |
156 | SpecializationUtil.registerFunction(vehicleType, "getOwner", Vehicle.getOwner) |
157 | SpecializationUtil.registerFunction(vehicleType, "getIsVehicleNode", Vehicle.getIsVehicleNode) |
158 | SpecializationUtil.registerFunction(vehicleType, "getIsOperating", Vehicle.getIsOperating) |
159 | SpecializationUtil.registerFunction(vehicleType, "getIsActive", Vehicle.getIsActive) |
160 | SpecializationUtil.registerFunction(vehicleType, "getIsActiveForInput", Vehicle.getIsActiveForInput) |
161 | SpecializationUtil.registerFunction(vehicleType, "getIsActiveForSound", Vehicle.getIsActiveForSound) |
162 | SpecializationUtil.registerFunction(vehicleType, "getIsLowered", Vehicle.getIsLowered) |
163 | SpecializationUtil.registerFunction(vehicleType, "updateWaterInfo", Vehicle.updateWaterInfo) |
164 | SpecializationUtil.registerFunction(vehicleType, "onWaterRaycastCallback", Vehicle.onWaterRaycastCallback) |
165 | SpecializationUtil.registerFunction(vehicleType, "setBroken", Vehicle.setBroken) |
166 | SpecializationUtil.registerFunction(vehicleType, "getVehicleDamage", Vehicle.getVehicleDamage) |
167 | SpecializationUtil.registerFunction(vehicleType, "getRepairPrice", Vehicle.getRepairPrice) |
168 | SpecializationUtil.registerFunction(vehicleType, "getRepaintPrice", Vehicle.getRepaintPrice) |
169 | SpecializationUtil.registerFunction(vehicleType, "setMassDirty", Vehicle.setMassDirty) |
170 | SpecializationUtil.registerFunction(vehicleType, "updateMass", Vehicle.updateMass) |
171 | SpecializationUtil.registerFunction(vehicleType, "getMaxComponentMassReached", Vehicle.getMaxComponentMassReached) |
172 | SpecializationUtil.registerFunction(vehicleType, "getAdditionalComponentMass", Vehicle.getAdditionalComponentMass) |
173 | SpecializationUtil.registerFunction(vehicleType, "getTotalMass", Vehicle.getTotalMass) |
174 | SpecializationUtil.registerFunction(vehicleType, "getComponentMass", Vehicle.getComponentMass) |
175 | SpecializationUtil.registerFunction(vehicleType, "getDefaultMass", Vehicle.getDefaultMass) |
176 | SpecializationUtil.registerFunction(vehicleType, "getOverallCenterOfMass", Vehicle.getOverallCenterOfMass) |
177 | SpecializationUtil.registerFunction(vehicleType, "getVehicleWorldXRot", Vehicle.getVehicleWorldXRot) |
178 | SpecializationUtil.registerFunction(vehicleType, "getVehicleWorldDirection", Vehicle.getVehicleWorldDirection) |
179 | SpecializationUtil.registerFunction(vehicleType, "getFillLevelInformation", Vehicle.getFillLevelInformation) |
180 | SpecializationUtil.registerFunction(vehicleType, "activate", Vehicle.activate) |
181 | SpecializationUtil.registerFunction(vehicleType, "deactivate", Vehicle.deactivate) |
182 | SpecializationUtil.registerFunction(vehicleType, "setComponentJointFrame", Vehicle.setComponentJointFrame) |
183 | SpecializationUtil.registerFunction(vehicleType, "setComponentJointRotLimit", Vehicle.setComponentJointRotLimit) |
184 | SpecializationUtil.registerFunction(vehicleType, "setComponentJointTransLimit", Vehicle.setComponentJointTransLimit) |
185 | SpecializationUtil.registerFunction(vehicleType, "loadComponentFromXML", Vehicle.loadComponentFromXML) |
186 | SpecializationUtil.registerFunction(vehicleType, "loadComponentJointFromXML", Vehicle.loadComponentJointFromXML) |
187 | SpecializationUtil.registerFunction(vehicleType, "createComponentJoint", Vehicle.createComponentJoint) |
188 | SpecializationUtil.registerFunction(vehicleType, "loadSchemaOverlay", Vehicle.loadSchemaOverlay) |
189 | SpecializationUtil.registerFunction(vehicleType, "getAdditionalSchemaText", Vehicle.getAdditionalSchemaText) |
190 | SpecializationUtil.registerFunction(vehicleType, "getUseTurnedOnSchema", Vehicle.getUseTurnedOnSchema) |
191 | SpecializationUtil.registerFunction(vehicleType, "dayChanged", Vehicle.dayChanged) |
192 | SpecializationUtil.registerFunction(vehicleType, "periodChanged", Vehicle.periodChanged) |
193 | SpecializationUtil.registerFunction(vehicleType, "raiseStateChange", Vehicle.raiseStateChange) |
194 | SpecializationUtil.registerFunction(vehicleType, "doCheckSpeedLimit", Vehicle.doCheckSpeedLimit) |
195 | SpecializationUtil.registerFunction(vehicleType, "interact", Vehicle.interact) |
196 | SpecializationUtil.registerFunction(vehicleType, "getInteractionHelp", Vehicle.getInteractionHelp) |
197 | SpecializationUtil.registerFunction(vehicleType, "getDistanceToNode", Vehicle.getDistanceToNode) |
198 | SpecializationUtil.registerFunction(vehicleType, "getIsAIActive", Vehicle.getIsAIActive) |
199 | SpecializationUtil.registerFunction(vehicleType, "getIsPowered", Vehicle.getIsPowered) |
200 | SpecializationUtil.registerFunction(vehicleType, "addVehicleToAIImplementList", Vehicle.addVehicleToAIImplementList) |
201 | SpecializationUtil.registerFunction(vehicleType, "setOperatingTime", Vehicle.setOperatingTime) |
202 | |
203 | SpecializationUtil.registerFunction(vehicleType, "requestActionEventUpdate", Vehicle.requestActionEventUpdate) |
204 | SpecializationUtil.registerFunction(vehicleType, "removeActionEvents", Vehicle.removeActionEvents) |
205 | SpecializationUtil.registerFunction(vehicleType, "updateActionEvents", Vehicle.updateActionEvents) |
206 | SpecializationUtil.registerFunction(vehicleType, "registerActionEvents", Vehicle.registerActionEvents) |
207 | SpecializationUtil.registerFunction(vehicleType, "addActionEvent", Vehicle.addActionEvent) |
208 | |
209 | SpecializationUtil.registerFunction(vehicleType, "updateSelectableObjects", Vehicle.updateSelectableObjects) |
210 | SpecializationUtil.registerFunction(vehicleType, "registerSelectableObjects", Vehicle.registerSelectableObjects) |
211 | SpecializationUtil.registerFunction(vehicleType, "addSubselection", Vehicle.addSubselection) |
212 | SpecializationUtil.registerFunction(vehicleType, "getRootVehicle", Vehicle.getRootVehicle) |
213 | SpecializationUtil.registerFunction(vehicleType, "findRootVehicle", Vehicle.findRootVehicle) |
214 | SpecializationUtil.registerFunction(vehicleType, "getChildVehicles", Vehicle.getChildVehicles) |
215 | SpecializationUtil.registerFunction(vehicleType, "addChildVehicles", Vehicle.addChildVehicles) |
216 | SpecializationUtil.registerFunction(vehicleType, "updateVehicleChain", Vehicle.updateVehicleChain) |
217 | SpecializationUtil.registerFunction(vehicleType, "getCanBeSelected", Vehicle.getCanBeSelected) |
218 | SpecializationUtil.registerFunction(vehicleType, "getBlockSelection", Vehicle.getBlockSelection) |
219 | SpecializationUtil.registerFunction(vehicleType, "getCanToggleSelectable", Vehicle.getCanToggleSelectable) |
220 | SpecializationUtil.registerFunction(vehicleType, "unselectVehicle", Vehicle.unselectVehicle) |
221 | SpecializationUtil.registerFunction(vehicleType, "selectVehicle", Vehicle.selectVehicle) |
222 | SpecializationUtil.registerFunction(vehicleType, "getIsSelected", Vehicle.getIsSelected) |
223 | SpecializationUtil.registerFunction(vehicleType, "getSelectedObject", Vehicle.getSelectedObject) |
224 | SpecializationUtil.registerFunction(vehicleType, "getSelectedVehicle", Vehicle.getSelectedVehicle) |
225 | SpecializationUtil.registerFunction(vehicleType, "setSelectedVehicle", Vehicle.setSelectedVehicle) |
226 | SpecializationUtil.registerFunction(vehicleType, "setSelectedObject", Vehicle.setSelectedObject) |
227 | SpecializationUtil.registerFunction(vehicleType, "getIsReadyForAutomatedTrainTravel", Vehicle.getIsReadyForAutomatedTrainTravel) |
228 | SpecializationUtil.registerFunction(vehicleType, "getIsAutomaticShiftingAllowed", Vehicle.getIsAutomaticShiftingAllowed) |
229 | SpecializationUtil.registerFunction(vehicleType, "getSpeedLimit", Vehicle.getSpeedLimit) |
230 | SpecializationUtil.registerFunction(vehicleType, "getRawSpeedLimit", Vehicle.getRawSpeedLimit) |
231 | SpecializationUtil.registerFunction(vehicleType, "getActiveFarm", Vehicle.getActiveFarm) |
232 | SpecializationUtil.registerFunction(vehicleType, "onVehicleWakeUpCallback", Vehicle.onVehicleWakeUpCallback) |
233 | SpecializationUtil.registerFunction(vehicleType, "getCanByMounted", Vehicle.getCanByMounted) |
234 | SpecializationUtil.registerFunction(vehicleType, "getName", Vehicle.getName) |
235 | SpecializationUtil.registerFunction(vehicleType, "getFullName", Vehicle.getFullName) |
236 | SpecializationUtil.registerFunction(vehicleType, "getBrand", Vehicle.getBrand) |
237 | SpecializationUtil.registerFunction(vehicleType, "getImageFilename", Vehicle.getImageFilename) |
238 | SpecializationUtil.registerFunction(vehicleType, "getCanBePickedUp", Vehicle.getCanBePickedUp) |
239 | SpecializationUtil.registerFunction(vehicleType, "getCanBeReset", Vehicle.getCanBeReset) |
240 | SpecializationUtil.registerFunction(vehicleType, "getIsInUse", Vehicle.getIsInUse) |
241 | SpecializationUtil.registerFunction(vehicleType, "getPropertyState", Vehicle.getPropertyState) |
242 | SpecializationUtil.registerFunction(vehicleType, "getAreControlledActionsAllowed", Vehicle.getAreControlledActionsAllowed) |
243 | SpecializationUtil.registerFunction(vehicleType, "getAreControlledActionsAvailable", Vehicle.getAreControlledActionsAvailable) |
244 | SpecializationUtil.registerFunction(vehicleType, "playControlledActions", Vehicle.playControlledActions) |
245 | SpecializationUtil.registerFunction(vehicleType, "getActionControllerDirection", Vehicle.getActionControllerDirection) |
246 | SpecializationUtil.registerFunction(vehicleType, "createMapHotspot", Vehicle.createMapHotspot) |
247 | SpecializationUtil.registerFunction(vehicleType, "getMapHotspot", Vehicle.getMapHotspot) |
248 | SpecializationUtil.registerFunction(vehicleType, "updateMapHotspot", Vehicle.updateMapHotspot) |
249 | SpecializationUtil.registerFunction(vehicleType, "getIsMapHotspotVisible", Vehicle.getIsMapHotspotVisible) |
250 | SpecializationUtil.registerFunction(vehicleType, "showInfo", Vehicle.showInfo) |
251 | SpecializationUtil.registerFunction(vehicleType, "loadObjectChangeValuesFromXML", Vehicle.loadObjectChangeValuesFromXML) |
252 | SpecializationUtil.registerFunction(vehicleType, "setObjectChangeValues", Vehicle.setObjectChangeValues) |
253 | SpecializationUtil.registerFunction(vehicleType, "getIsSynchronized", Vehicle.getIsSynchronized) |
254 | end |
registerInteractionFlag
DescriptionRegister interaction flagDefinition
registerInteractionFlag(string name)Arguments
string | name | name of flag |
71 | function Vehicle.registerInteractionFlag(name) |
72 | local key = "INTERACTION_FLAG_"..string.upper(name) |
73 | if Vehicle[key] == nil then |
74 | Vehicle.NUM_INTERACTION_FLAGS = Vehicle.NUM_INTERACTION_FLAGS + 1 |
75 | Vehicle[key] = Vehicle.NUM_INTERACTION_FLAGS |
76 | end |
77 | end |
registers
DescriptionDefinitionregisters()Code
338 | function Vehicle.registers() |
339 | local schema = Vehicle.xmlSchema |
340 | local schemaSavegame = Vehicle.xmlSchemaSavegame |
341 | |
342 | schema:register(XMLValueType.STRING, "vehicle#type", "Vehicle type") |
343 | schema:register(XMLValueType.STRING, "vehicle.annotation", "Annotation", nil, true) |
344 | |
345 | StoreManager.registerStoreDataXMLPaths(schema, "vehicle") |
346 | |
347 | schema:register(XMLValueType.STRING, "vehicle.storeData.specs.workingWidth", "Working width to display in shop") |
348 | schema:register(XMLValueType.STRING, "vehicle.storeData.specs.combination(?)#xmlFilename", "Combination to display in shop") |
349 | schema:register(XMLValueType.STRING, "vehicle.storeData.specs.combination(?)#filterCategory", "Filter in this category") |
350 | schema:register(XMLValueType.STRING, "vehicle.storeData.specs.combination(?)#filterSpec", "Filter for this spec type") |
351 | schema:register(XMLValueType.FLOAT, "vehicle.storeData.specs.combination(?)#filterSpecMin", "Filter spec type in this range (min.)") |
352 | schema:register(XMLValueType.FLOAT, "vehicle.storeData.specs.combination(?)#filterSpecMax", "Filter spec type in this range (max.)") |
353 | |
354 | schema:register(XMLValueType.BOOL, "vehicle.storeData.specs.weight#ignore", "Hide vehicle weight in shop", false) |
355 | schema:register(XMLValueType.FLOAT, "vehicle.storeData.specs.weight#minValue", "Min. weight to display in shop") |
356 | schema:register(XMLValueType.FLOAT, "vehicle.storeData.specs.weight#maxValue", "Max. weight to display in shop") |
357 | schema:register(XMLValueType.STRING, "vehicle.storeData.specs.weight.config(?)#name", "Name of configuration") |
358 | schema:register(XMLValueType.INT, "vehicle.storeData.specs.weight.config(?)#index", "Index of selected configuration") |
359 | schema:register(XMLValueType.FLOAT, "vehicle.storeData.specs.weight.config(?)#value", "Weight value which can be reached with this configuration") |
360 | |
361 | schema:register(XMLValueType.STRING, "vehicle.base.filename", "Path to i3d filename", nil) |
362 | schema:register(XMLValueType.L10N_STRING, "vehicle.base.typeDesc", "Type description", nil) |
363 | schema:register(XMLValueType.BOOL, "vehicle.base.synchronizePosition", "Vehicle position synchronized", true) |
364 | schema:register(XMLValueType.BOOL, "vehicle.base.supportsPickUp", "Vehicle can be picked up by hand", "true if vehicle is a pallet, false otherwise") |
365 | schema:register(XMLValueType.BOOL, "vehicle.base.canBeReset", "Vehicle can be reset to shop", true) |
366 | schema:register(XMLValueType.BOOL, "vehicle.base.showInVehicleMenu", "Vehicle shows in vehicle menu", true) |
367 | schema:register(XMLValueType.BOOL, "vehicle.base.supportsRadio", "Vehicle supported radio", true) |
368 | schema:register(XMLValueType.BOOL, "vehicle.base.input#allowed", "Vehicle allows key input", true) |
369 | schema:register(XMLValueType.FLOAT, "vehicle.base.tailwaterDepth#warning", "Tailwater depth warning is shown from this water depth", 1) |
370 | schema:register(XMLValueType.FLOAT, "vehicle.base.tailwaterDepth#threshold", "Vehicle is broken after this water depth", 2.5) |
371 | schema:register(XMLValueType.STRING, "vehicle.base.mapHotspot#type", "Map hotspot type") |
372 | schema:register(XMLValueType.BOOL, "vehicle.base.mapHotspot#hasDirection", "Map hotspot has direction") |
373 | schema:register(XMLValueType.BOOL, "vehicle.base.mapHotspot#available", "Map hotspot is available", true) |
374 | schema:register(XMLValueType.FLOAT, "vehicle.base.speedLimit#value", "Speed limit") |
375 | schema:register(XMLValueType.FLOAT, "vehicle.base.size#width", "Occupied width of the vehicle when loaded", nil, true) |
376 | schema:register(XMLValueType.FLOAT, "vehicle.base.size#length", "Occupied length of the vehicle when loaded", nil, true) |
377 | schema:register(XMLValueType.FLOAT, "vehicle.base.size#height", "Occupied height of the vehicle when loaded") |
378 | schema:register(XMLValueType.FLOAT, "vehicle.base.size#widthOffset", "Width offset") |
379 | schema:register(XMLValueType.FLOAT, "vehicle.base.size#lengthOffset", "Width offset") |
380 | schema:register(XMLValueType.FLOAT, "vehicle.base.size#heightOffset", "Height offset") |
381 | schema:register(XMLValueType.ANGLE, "vehicle.base.size#yRotation", "Y Rotation offset in i3d (Needs to be set to the vehicle's rotation in the i3d file and is e.g. used to check ai working direction)", 0) |
382 | schema:register(XMLValueType.NODE_INDEX, "vehicle.base.steeringAxle#node", "Steering axle node used to calculate the steering angle of attachments") |
383 | schema:register(XMLValueType.STRING, "vehicle.base.sounds#filename", "Path to external sound files") |
384 | schema:register(XMLValueType.FLOAT, "vehicle.base.sounds#volumeFactor", "This factor will be applied to all sounds of this vehicle") |
385 | |
386 | I3DUtil.registerI3dMappingXMLPaths(schema, "vehicle") |
387 | |
388 | schema:register(XMLValueType.INT, "vehicle.base.components#numComponents", "Number of components loaded from i3d", "number of components the i3d contains") |
389 | schema:register(XMLValueType.FLOAT, "vehicle.base.components#maxMass", "Max. overall mass the vehicle can have", "unlimited") |
390 | schema:register(XMLValueType.FLOAT, "vehicle.base.components.component(?)#mass", "Mass of component", "Mass of component in i3d") |
391 | schema:register(XMLValueType.VECTOR_TRANS, "vehicle.base.components.component(?)#centerOfMass", "Center of mass", "Center of mass in i3d") |
392 | schema:register(XMLValueType.INT, "vehicle.base.components.component(?)#solverIterationCount", "Solver iterations count") |
393 | schema:register(XMLValueType.BOOL, "vehicle.base.components.component(?)#motorized", "Is motorized component", "set by motorized specialization") |
394 | schema:register(XMLValueType.BOOL, "vehicle.base.components.component(?)#collideWithAttachables", "Collides with attachables", false) |
395 | |
396 | schema:register(XMLValueType.INT, "vehicle.base.components.joint(?)#component1", "First component of the joint") |
397 | schema:register(XMLValueType.INT, "vehicle.base.components.joint(?)#component2", "Second component of the joint") |
398 | schema:register(XMLValueType.NODE_INDEX, "vehicle.base.components.joint(?)#node", "Joint node") |
399 | schema:register(XMLValueType.NODE_INDEX, "vehicle.base.components.joint(?)#nodeActor1", "Actor node of second component", "Joint node") |
400 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotLimit", "Rotation limit", "0 0 0") |
401 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transLimit", "Translation limit", "0 0 0") |
402 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotMinLimit", "Min rotation limit", "inversed rotation limit") |
403 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transMinLimit", "Min translation limit", "inversed translation limit") |
404 | |
405 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotLimitSpring", "Rotation spring limit", "0 0 0") |
406 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotLimitDamping", "Rotation damping limit", "1 1 1") |
407 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotLimitForceLimit", "Rotation limit force limit (-1 = infinite)", "-1 -1 -1") |
408 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transLimitForceLimit", "Translation limit force limit (-1 = infinite)", "-1 -1 -1") |
409 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transLimitSpring", "Translation spring limit", "0 0 0") |
410 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transLimitDamping", "Translation damping limit", "1 1 1") |
411 | |
412 | schema:register(XMLValueType.NODE_INDEX, "vehicle.base.components.joint(?)#zRotationNode", "Position of joints z rotation") |
413 | schema:register(XMLValueType.BOOL, "vehicle.base.components.joint(?)#breakable", "Joint is breakable", false) |
414 | schema:register(XMLValueType.FLOAT, "vehicle.base.components.joint(?)#breakForce", "Joint force until it breaks", 10) |
415 | schema:register(XMLValueType.FLOAT, "vehicle.base.components.joint(?)#breakTorque", "Joint torque until it breaks", 10) |
416 | schema:register(XMLValueType.BOOL, "vehicle.base.components.joint(?)#enableCollision", "Enable collision between both components", false) |
417 | |
418 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#maxRotDriveForce", "Max rotational drive force", "0 0 0") |
419 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotDriveVelocity", "Rotational drive velocity") |
420 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotDriveRotation", "Rotational drive rotation") |
421 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotDriveSpring", "Rotational drive spring", "0 0 0") |
422 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#rotDriveDamping", "Rotational drive damping", "0 0 0") |
423 | |
424 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transDriveVelocity", "Translational drive velocity") |
425 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transDrivePosition", "Translational drive position") |
426 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transDriveSpring", "Translational drive spring", "0 0 0") |
427 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#transDriveDamping", "Translational drive damping", "1 1 1") |
428 | schema:register(XMLValueType.VECTOR_3, "vehicle.base.components.joint(?)#maxTransDriveForce", "Max translational drive force", "0 0 0") |
429 | |
430 | schema:register(XMLValueType.BOOL, "vehicle.base.components.joint(?)#initComponentPosition", "Defines if the component is translated and rotated during loading based on joint movement", true) |
431 | |
432 | schema:register(XMLValueType.BOOL, "vehicle.base.components.collisionPair(?)#enabled", "Collision between components enabled") |
433 | schema:register(XMLValueType.INT, "vehicle.base.components.collisionPair(?)#component1", "Index of first component") |
434 | schema:register(XMLValueType.INT, "vehicle.base.components.collisionPair(?)#component2", "Index of second component") |
435 | |
436 | ObjectChangeUtil.registerObjectChangesXMLPaths(schema, "vehicle.base") |
437 | |
438 | schema:register(XMLValueType.VECTOR_2, "vehicle.base.schemaOverlay#attacherJointPosition", "Position of attacher joint") |
439 | schema:register(XMLValueType.VECTOR_2, "vehicle.base.schemaOverlay#basePosition", "Position of vehicle") |
440 | schema:register(XMLValueType.STRING, "vehicle.base.schemaOverlay#name", "Name of schema overlay") |
441 | schema:register(XMLValueType.FLOAT, "vehicle.base.schemaOverlay#invisibleBorderRight", "Size of invisible border on the right") |
442 | schema:register(XMLValueType.FLOAT, "vehicle.base.schemaOverlay#invisibleBorderLeft", "Size of invisible border on the left") |
443 | |
444 | ConfigurationUtil.registerColorConfigurationXMLPaths(schema, "baseColor") |
445 | ConfigurationUtil.registerColorConfigurationXMLPaths(schema, "designColor") |
446 | ConfigurationUtil.registerColorConfigurationXMLPaths(schema, "design") |
447 | |
448 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.designConfigurations.designConfiguration(?)") |
449 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.vehicleTypeConfigurations.vehicleTypeConfiguration(?)") |
450 | schema:register(XMLValueType.STRING, "vehicle.vehicleTypeConfigurations.vehicleTypeConfiguration(?)#vehicleType", "Vehicle type for configuration") |
451 | |
452 | for i=2, 8 do |
453 | ConfigurationUtil.registerColorConfigurationXMLPaths(schema, string.format("design%d", i)) |
454 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, string.format("vehicle.design%dConfigurations.design%dConfiguration(?)", i, i)) |
455 | end |
456 | |
457 | schema:register(XMLValueType.BOOL, "vehicle.designConfigurations#preLoad", "Defines if the design configurations are applied before the execution of load or after. Can help if the configurations manipulate the wheel positions for example.", false) |
458 | |
459 | StoreItemUtil.registerConfigurationSetXMLPaths(schema, "vehicle") |
460 | |
461 | schemaSavegame:register(XMLValueType.BOOL, "vehicles#loadAnyFarmInSingleplayer", "Load any farm in singleplayer", false) |
462 | schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?)#filename", "XML filename") |
463 | schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?)#modName", "Vehicle mod name") |
464 | schemaSavegame:register(XMLValueType.BOOL, "vehicles.vehicle(?)#defaultFarmProperty", "Property of default farm", false) |
465 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?)#id", "Vehicle id") |
466 | schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?)#tourId", "Tour id") |
467 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?)#farmId", "Farm id") |
468 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?)#age", "Age in number of months") |
469 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?)#price", "Price") |
470 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?)#propertyState", "Property state") |
471 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?)#activeMissionId", "Active mission id") |
472 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?)#operatingTime", "Operating time") |
473 | schemaSavegame:register(XMLValueType.BOOL, "vehicles.vehicle(?)#isAbsolute", "Position is Absolute") |
474 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?)#yOffset", "Y Offset") |
475 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?)#xPosition", "X Position") |
476 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?)#zPosition", "Z Position") |
477 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?)#yRotation", "Y Rotation") |
478 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?)#selectedObjectIndex", "Selected object index") |
479 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?)#subSelectedObjectIndex", "Sub selected object index") |
480 | |
481 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).component(?)#index", "Component index") |
482 | schemaSavegame:register(XMLValueType.VECTOR_TRANS, "vehicles.vehicle(?).component(?)#position", "Component position") |
483 | schemaSavegame:register(XMLValueType.VECTOR_ROT, "vehicles.vehicle(?).component(?)#rotation", "Component rotation") |
484 | |
485 | schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?).boughtConfiguration(?)#name", "Configuration name") |
486 | schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?).boughtConfiguration(?)#id", "Configuration save id") |
487 | |
488 | schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?).configuration(?)#name", "Configuration name") |
489 | schemaSavegame:register(XMLValueType.STRING, "vehicles.vehicle(?).configuration(?)#id", "Configuration save id") |
490 | |
491 | VehicleActionController.registerXMLPaths(schemaSavegame, "vehicles.vehicle(?).actionController") |
492 | |
493 | schemaSavegame:register(XMLValueType.INT, "vehicles.attachments(?)#rootVehicleId", "Id of root vehicle") |
494 | end |
registerSelectableObjects
DescriptionDefinitionregisterSelectableObjects()Code
2714 | function Vehicle:registerSelectableObjects(selectableObjects) |
2715 | if self:getCanBeSelected() and not self:getBlockSelection() then |
2716 | table.insert(selectableObjects, self.selectionObject) |
2717 | self.selectionObject.index = #selectableObjects |
2718 | end |
2719 | end |
registerStateChange
DescriptionDefinitionregisterStateChange()Code
81 | function Vehicle.registerStateChange(name) |
82 | local key = "STATE_CHANGE_"..string.upper(name) |
83 | if Vehicle[key] == nil then |
84 | Vehicle.NUM_STATE_CHANGES = Vehicle.NUM_STATE_CHANGES + 1 |
85 | Vehicle[key] = Vehicle.NUM_STATE_CHANGES |
86 | end |
87 | end |
removeActionEvent
DescriptionDefinitionremoveActionEvent()Code
2690 | function Vehicle:removeActionEvent(actionEventsTable, inputAction) |
2691 | if actionEventsTable[inputAction] ~= nil then |
2692 | g_inputBinding:removeActionEvent(actionEventsTable[inputAction].actionEventId) |
2693 | actionEventsTable[inputAction] = nil |
2694 | end |
2695 | end |
removeActionEvents
DescriptionDefinitionremoveActionEvents()Code
2543 | function Vehicle:removeActionEvents() |
2544 | g_inputBinding:removeActionEventsByTarget(self) |
2545 | end |
removeFromPhysics
DescriptionRemove vehicle from physicsDefinition
removeFromPhysics()Code
2133 | function Vehicle:removeFromPhysics() |
2134 | for _, component in pairs(self.components) do |
2135 | removeFromPhysics(component.node) |
2136 | end |
2137 | -- invalidate wheel shapes and component joints (removing the components removes the wheels and joints too) |
2138 | if self.isServer then |
2139 | for _, jointDesc in pairs(self.componentJoints) do |
2140 | jointDesc.jointIndex = 0 |
2141 | end |
2142 | removeWakeUpReport(self.rootNode) |
2143 | end |
2144 | self.isAddedToPhysics = false |
2145 | |
2146 | return true |
2147 | end |
removeNodeObjectMapping
DescriptionRemove component nodes from listDefinition
removeNodeObjectMapping(table list)Arguments
table | list | list |
2083 | function Vehicle:removeNodeObjectMapping(list) |
2084 | if self.components ~= nil then |
2085 | for _,v in pairs(self.components) do |
2086 | list[v.node] = nil |
2087 | end |
2088 | end |
2089 | end |
requestActionEventUpdate
DescriptionDefinitionrequestActionEventUpdate()Code
2528 | function Vehicle:requestActionEventUpdate() |
2529 | -- pass request to rootVehicle |
2530 | local vehicle = self.rootVehicle |
2531 | if vehicle == self then |
2532 | self.actionEventUpdateRequested = true |
2533 | else |
2534 | vehicle:requestActionEventUpdate() |
2535 | end |
2536 | |
2537 | -- remove all actionEvents |
2538 | vehicle:removeActionEvents() |
2539 | end |
saveStatsToXMLFile
DescriptionGet xml states attributesDefinition
saveStatsToXMLFile()Return Values
string | attributes | attributes |
1412 | function Vehicle:saveStatsToXMLFile(xmlFile, key) |
1413 | local isTabbable = self.getIsTabbable == nil or self:getIsTabbable() |
1414 | if self.isDeleted or not self.isVehicleSaved or not isTabbable then |
1415 | return false |
1416 | end |
1417 | local name = "Unknown" |
1418 | local categoryName = "unknown" |
1419 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
1420 | if storeItem ~= nil then |
1421 | if storeItem.name ~= nil then |
1422 | name = tostring(storeItem.name) |
1423 | end |
1424 | if storeItem.categoryName ~= nil and storeItem.categoryName ~= "" then |
1425 | categoryName = tostring(storeItem.categoryName) |
1426 | end |
1427 | end |
1428 | |
1429 | setXMLString(xmlFile, key.."#name", HTMLUtil.encodeToHTML(name)) |
1430 | setXMLString(xmlFile, key.."#category", HTMLUtil.encodeToHTML(categoryName)) |
1431 | setXMLString(xmlFile, key.."#type", HTMLUtil.encodeToHTML(tostring(self.typeName))) |
1432 | |
1433 | if self.components[1] ~= nil and self.components[1].node ~= 0 then |
1434 | local x,y,z = getWorldTranslation(self.components[1].node) |
1435 | setXMLFloat(xmlFile, key.."#x", x) |
1436 | setXMLFloat(xmlFile, key.."#y", y) |
1437 | setXMLFloat(xmlFile, key.."#z", z) |
1438 | end |
1439 | |
1440 | for id, spec in pairs(self.specializations) do |
1441 | if spec.saveStatsToXMLFile ~= nil then |
1442 | spec.saveStatsToXMLFile(self, xmlFile, key) |
1443 | end |
1444 | end |
1445 | |
1446 | return true |
1447 | end |
saveToXMLFile
DescriptionDefinitionsaveToXMLFile()Code
1335 | function Vehicle:saveToXMLFile(xmlFile, key, usedModNames) |
1336 | xmlFile:setValue(key.."#id", self.currentSavegameId) |
1337 | xmlFile:setValue(key.."#isAbsolute", true) |
1338 | xmlFile:setValue(key.."#age", self.age) |
1339 | xmlFile:setValue(key.."#price", self.price) |
1340 | xmlFile:setValue(key.."#farmId", self:getOwnerFarmId()) |
1341 | xmlFile:setValue(key.."#propertyState", self.propertyState) |
1342 | xmlFile:setValue(key.."#operatingTime", self.operatingTime / 1000) |
1343 | |
1344 | if self.activeMissionId ~= nil then |
1345 | xmlFile:setValue(key.."#activeMissionId", self.activeMissionId) |
1346 | end |
1347 | |
1348 | if self.tourId ~= nil then |
1349 | xmlFile:setValue(key.."#tourId", self.tourId) |
1350 | end |
1351 | |
1352 | if self.rootVehicle == self then |
1353 | xmlFile:setValue(key.."#selectedObjectIndex", self.currentSelection.index) |
1354 | if self.currentSelection.subIndex ~= nil then |
1355 | xmlFile:setValue(key.."#subSelectedObjectIndex", self.currentSelection.subIndex) |
1356 | end |
1357 | end |
1358 | |
1359 | if not self.isBroken then |
1360 | for k, component in ipairs(self.components) do |
1361 | local compKey = string.format("%s.component(%d)", key, k-1) |
1362 | local node = component.node |
1363 | local x,y,z = getWorldTranslation(node) |
1364 | local xRot,yRot,zRot = getWorldRotation(node) |
1365 | |
1366 | xmlFile:setValue(compKey.."#index", k) |
1367 | xmlFile:setValue(compKey.."#position", x, y, z) |
1368 | xmlFile:setValue(compKey.."#rotation", xRot, yRot, zRot) |
1369 | end |
1370 | end |
1371 | |
1372 | local configIndex = 0 |
1373 | for configName, configId in pairs(self.configurations) do |
1374 | local saveId = ConfigurationUtil.getSaveIdByConfigId(self.configFileName, configName, configId) |
1375 | if saveId ~= nil then |
1376 | local configKey = string.format("%s.configuration(%d)", key, configIndex) |
1377 | xmlFile:setValue(configKey.."#name", configName) |
1378 | xmlFile:setValue(configKey.."#id", saveId) |
1379 | configIndex = configIndex + 1 |
1380 | end |
1381 | end |
1382 | |
1383 | configIndex = 0 |
1384 | for configName, configIds in pairs(self.boughtConfigurations) do |
1385 | for configId,_ in pairs(configIds) do |
1386 | local saveId = ConfigurationUtil.getSaveIdByConfigId(self.configFileName, configName, configId) |
1387 | if saveId ~= nil then |
1388 | local configKey = string.format("%s.boughtConfiguration(%d)", key, configIndex) |
1389 | xmlFile:setValue(configKey.."#name", configName) |
1390 | xmlFile:setValue(configKey.."#id", saveId) |
1391 | configIndex = configIndex + 1 |
1392 | end |
1393 | end |
1394 | end |
1395 | |
1396 | for id, spec in pairs(self.specializations) do |
1397 | local name = self.specializationNames[id] |
1398 | |
1399 | if spec.saveToXMLFile ~= nil then |
1400 | spec.saveToXMLFile(self, xmlFile, key.."."..name, usedModNames) |
1401 | end |
1402 | end |
1403 | |
1404 | if self.actionController ~= nil then |
1405 | self.actionController:saveToXMLFile(xmlFile, key..".actionController", usedModNames) |
1406 | end |
1407 | end |
selectVehicle
DescriptionDefinitionselectVehicle()Code
2810 | function Vehicle:selectVehicle(subSelectionIndex, ignoreActionEventUpdate) |
2811 | self.selectionObject.isSelected = true |
2812 | SpecializationUtil.raiseEvent(self, "onSelect", subSelectionIndex) |
2813 | |
2814 | if ignoreActionEventUpdate == nil or not ignoreActionEventUpdate then |
2815 | self:requestActionEventUpdate() |
2816 | end |
2817 | end |
setAbsolutePosition
DescriptionDefinitionsetAbsolutePosition()Code
2173 | function Vehicle:setAbsolutePosition(positionX, positionY, positionZ, xRot, yRot, zRot, componentPositions) |
2174 | local tempRootNode = createTransformGroup("tempRootNode") |
2175 | setTranslation(tempRootNode, positionX, positionY, positionZ) |
2176 | setRotation(tempRootNode, xRot, yRot, zRot) |
2177 | |
2178 | -- now move the objects to the scene root node |
2179 | for i, component in pairs(self.components) do |
2180 | local x,y,z = localToWorld(tempRootNode, unpack(component.originalTranslation)) |
2181 | local rx,ry,rz = localRotationToWorld(tempRootNode, unpack(component.originalRotation)) |
2182 | |
2183 | if componentPositions ~= nil and #componentPositions == #self.components then |
2184 | x,y,z = unpack(componentPositions[i][1]) |
2185 | rx,ry,rz = unpack(componentPositions[i][2]) |
2186 | end |
2187 | |
2188 | self:setWorldPosition(x, y, z, rx, ry, rz, i, true) |
2189 | end |
2190 | delete(tempRootNode) |
2191 | |
2192 | self.networkTimeInterpolator:reset() |
2193 | end |
setBroken
DescriptionDefinitionsetBroken()Code
2499 | function Vehicle:setBroken() |
2500 | if self.isServer and not self.isBroken then |
2501 | g_server:broadcastEvent(VehicleBrokenEvent.new(self), nil, nil, self) |
2502 | end |
2503 | |
2504 | self.isBroken = true |
2505 | SpecializationUtil.raiseEvent(self, "onSetBroken") |
2506 | end |
setComponentJointFrame
DescriptionSet component joint frameDefinition
setComponentJointFrame(Integer jointDesc, Integer anchorActor)Arguments
Integer | jointDesc | joint desc index |
Integer | anchorActor | anchor actor |
3107 | function Vehicle:setComponentJointFrame(jointDesc, anchorActor) |
3108 | if anchorActor == 0 then |
3109 | local localPoses = jointDesc.jointLocalPoses[1] |
3110 | localPoses.trans[1], localPoses.trans[2], localPoses.trans[3] = localToLocal(jointDesc.jointNode, self.components[jointDesc.componentIndices[1]].node, 0, 0, 0) |
3111 | localPoses.rot[1], localPoses.rot[2], localPoses.rot[3] = localRotationToLocal(jointDesc.jointNode, self.components[jointDesc.componentIndices[1]].node, 0, 0, 0) |
3112 | else |
3113 | local localPoses = jointDesc.jointLocalPoses[2] |
3114 | localPoses.trans[1], localPoses.trans[2], localPoses.trans[3] = localToLocal(jointDesc.jointNodeActor1, self.components[jointDesc.componentIndices[2]].node, 0, 0, 0) |
3115 | localPoses.rot[1], localPoses.rot[2], localPoses.rot[3] = localRotationToLocal(jointDesc.jointNodeActor1, self.components[jointDesc.componentIndices[2]].node, 0, 0, 0) |
3116 | end |
3117 | |
3118 | local jointNode = jointDesc.jointNode |
3119 | if anchorActor == 1 then |
3120 | jointNode = jointDesc.jointNodeActor1 |
3121 | end |
3122 | |
3123 | if jointDesc.jointIndex ~= 0 then |
3124 | setJointFrame(jointDesc.jointIndex, anchorActor, jointNode) |
3125 | end |
3126 | end |
setComponentJointRotLimit
DescriptionSet component joint rot limitDefinition
setComponentJointRotLimit(Integer componentJoint, Integer axis, float minLimit, float maxLimit)Arguments
Integer | componentJoint | index of component joint |
Integer | axis | axis |
float | minLimit | min limit |
float | maxLimit | max limit |
3135 | function Vehicle:setComponentJointRotLimit(componentJoint, axis, minLimit, maxLimit) |
3136 | if self.isServer then |
3137 | componentJoint.rotLimit[axis] = maxLimit |
3138 | componentJoint.rotMinLimit[axis] = minLimit |
3139 | |
3140 | if componentJoint.jointIndex ~= 0 then |
3141 | if minLimit <= maxLimit then |
3142 | setJointRotationLimit(componentJoint.jointIndex, axis-1, true, minLimit, maxLimit) |
3143 | else |
3144 | setJointRotationLimit(componentJoint.jointIndex, axis-1, false, 0, 0) |
3145 | end |
3146 | end |
3147 | end |
3148 | end |
setComponentJointTransLimit
DescriptionSet component joint trans limitDefinition
setComponentJointTransLimit(Integer componentJoint, Integer axis, float minLimit, float maxLimit)Arguments
Integer | componentJoint | index of component joint |
Integer | axis | axis |
float | minLimit | min limit |
float | maxLimit | max limit |
3156 | function Vehicle:setComponentJointTransLimit(componentJoint, axis, minLimit, maxLimit) |
3157 | if self.isServer then |
3158 | componentJoint.transLimit[axis] = maxLimit |
3159 | componentJoint.transMinLimit[axis] = minLimit |
3160 | |
3161 | if componentJoint.jointIndex ~= 0 then |
3162 | if minLimit <= maxLimit then |
3163 | setJointTranslationLimit(componentJoint.jointIndex, axis-1, true, minLimit, maxLimit) |
3164 | else |
3165 | setJointTranslationLimit(componentJoint.jointIndex, axis-1, false, 0, 0) |
3166 | end |
3167 | end |
3168 | end |
3169 | end |
setLoadingState
DescriptionDefinitionsetLoadingState()Code
2044 | function Vehicle:setLoadingState(loadingState) |
2045 | if loadingState == VehicleLoadingUtil.VEHICLE_LOAD_OK or loadingState == VehicleLoadingUtil.VEHICLE_LOAD_ERROR or loadingState == VehicleLoadingUtil.VEHICLE_LOAD_DELAYED or loadingState == VehicleLoadingUtil.VEHICLE_LOAD_NO_SPACE then |
2046 | self.loadingState = loadingState |
2047 | else |
2048 | printCallstack() |
2049 | Logging.error("Invalid loading state '%s'!", loadingState) |
2050 | end |
2051 | end |
setLoadingStep
DescriptionDefinitionsetLoadingStep()Code
2055 | function Vehicle:setLoadingStep(loadingStep) |
2056 | if loadingStep == Vehicle.LOAD_STEP_CREATED |
2057 | or loadingStep == Vehicle.LOAD_STEP_PRE_LOAD |
2058 | or loadingStep == Vehicle.LOAD_STEP_AWAIT_I3D |
2059 | or loadingStep == Vehicle.LOAD_STEP_LOAD |
2060 | or loadingStep == Vehicle.LOAD_STEP_POST_LOAD |
2061 | or loadingStep == Vehicle.LOAD_STEP_AWAIT_SUB_I3D |
2062 | or loadingStep == Vehicle.LOAD_STEP_FINISHED |
2063 | or loadingStep == Vehicle.LOAD_STEP_SYNCHRONIZED then |
2064 | self.loadingStep = loadingStep |
2065 | else |
2066 | printCallstack() |
2067 | Logging.error("Invalid loading step '%s'!", loadingStep) |
2068 | end |
2069 | end |
setMassDirty
DescriptionDefinitionsetMassDirty()Code
2938 | function Vehicle:setMassDirty() |
2939 | self.isMassDirty = true |
2940 | end |
setObjectChangeValues
DescriptionSets object change valuesDefinition
setObjectChangeValues(table object, boolean isActive)Arguments
table | object | object |
boolean | isActive | is active |
3963 | function Vehicle:setObjectChangeValues(object, isActive) |
3964 | end |
setOperatingTime
DescriptionDefinitionsetOperatingTime()Code
3638 | function Vehicle:setOperatingTime(operatingTime, isLoading) |
3639 | if not isLoading and self.propertyState == Vehicle.PROPERTY_STATE_LEASED and g_currentMission ~= nil and g_currentMission.economyManager ~= nil and math.floor(operatingTime/(1000*60*60)) > math.floor(self.operatingTime/(1000*60*60)) then |
3640 | g_currentMission.economyManager:vehicleOperatingHourChanged(self) |
3641 | end |
3642 | self.operatingTime = math.max(Utils.getNoNil(operatingTime, 0), 0) |
3643 | end |
setOwnerFarmId
DescriptionDefinitionsetOwnerFarmId()Code
3974 | function Vehicle:setOwnerFarmId(farmId, noEventSend) |
3975 | Vehicle:superClass().setOwnerFarmId(self, farmId, noEventSend) |
3976 | |
3977 | if self.mapHotspot ~= nil then |
3978 | self.mapHotspot:setOwnerFarmId(farmId) |
3979 | end |
3980 | end |
setRelativePosition
DescriptionSet relative position of vehicleDefinition
setRelativePosition(float positionX, float offsetY, float positionZ, float yRot)Arguments
float | positionX | x position |
float | offsetY | y offset |
float | positionZ | z position |
float | yRot | y rotation |
2164 | function Vehicle:setRelativePosition(positionX, offsetY, positionZ, yRot) |
2165 | -- position the vehicle |
2166 | local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, positionX, 300, positionZ) |
2167 | |
2168 | self:setAbsolutePosition(positionX, terrainHeight+offsetY, positionZ, 0, yRot, 0) |
2169 | end |
setSelectedObject
DescriptionDefinitionsetSelectedObject()Code
2844 | function Vehicle:setSelectedObject(object, subSelectionIndex, ignoreActionEventUpdate) |
2845 | local currentSelection = self.currentSelection |
2846 | |
2847 | if object == nil then |
2848 | object = self:getSelectedObject() |
2849 | end |
2850 | |
2851 | local found = false |
2852 | for _, o in ipairs(self.selectableObjects) do |
2853 | if o == object then |
2854 | found = true |
2855 | end |
2856 | end |
2857 | |
2858 | if found then |
2859 | -- if object was found unselect all other vehicles |
2860 | for _, o in ipairs(self.selectableObjects) do |
2861 | if o ~= object and o.vehicle:getIsSelected() then |
2862 | o.vehicle:unselectVehicle() |
2863 | end |
2864 | end |
2865 | |
2866 | if object ~= currentSelection.object or subSelectionIndex ~= currentSelection.subIndex then |
2867 | currentSelection.object = object |
2868 | currentSelection.index = object.index |
2869 | if subSelectionIndex ~= nil then |
2870 | currentSelection.subIndex = subSelectionIndex |
2871 | end |
2872 | if currentSelection.subIndex > #object.subSelections then |
2873 | currentSelection.subIndex = 1 |
2874 | end |
2875 | |
2876 | currentSelection.object.vehicle:selectVehicle(currentSelection.subIndex, ignoreActionEventUpdate) |
2877 | |
2878 | return true |
2879 | end |
2880 | else |
2881 | object = self:getSelectedObject() |
2882 | |
2883 | found = false |
2884 | for _, o in ipairs(self.selectableObjects) do |
2885 | if o == object then |
2886 | found = true |
2887 | end |
2888 | end |
2889 | |
2890 | -- if the object to select could not be found and the object that was selected is not available anymore we clear the selection |
2891 | if not found then |
2892 | currentSelection.object = nil |
2893 | currentSelection.index = 1 |
2894 | currentSelection.subIndex = 1 |
2895 | end |
2896 | end |
2897 | |
2898 | return false |
2899 | end |
setSelectedVehicle
DescriptionDefinitionsetSelectedVehicle()Code
2821 | function Vehicle:setSelectedVehicle(vehicle, subSelectionIndex, ignoreActionEventUpdate) |
2822 | local object = nil |
2823 | |
2824 | -- if vehicle could not be selected we select the next possible vehicle |
2825 | if vehicle == nil or not vehicle:getCanBeSelected() or self:getBlockSelection() then |
2826 | vehicle = nil |
2827 | for _, o in ipairs(self.selectableObjects) do |
2828 | if o.vehicle:getCanBeSelected() and not o.vehicle:getBlockSelection() then |
2829 | vehicle = o.vehicle |
2830 | break |
2831 | end |
2832 | end |
2833 | end |
2834 | |
2835 | if vehicle ~= nil then |
2836 | object = vehicle.selectionObject |
2837 | end |
2838 | |
2839 | return self:setSelectedObject(object, subSelectionIndex, ignoreActionEventUpdate) |
2840 | end |
setVisibility
DescriptionSets visibility of a vehicleDefinition
setVisibility(boolean state)Arguments
boolean | state | visibility state |
2152 | function Vehicle:setVisibility(state) |
2153 | for _, component in pairs(self.components) do |
2154 | setVisibility(component.node, state) |
2155 | end |
2156 | end |
setWorldPosition
DescriptionSet world position and rotation of componentDefinition
setWorldPosition(float x, float y, float z, float xRot, float yRot, float zRot, Integer i, boolean changeInterp)Arguments
float | x | x position |
float | y | y position |
float | z | z position |
float | xRot | x rotation |
float | yRot | y rotation |
float | zRot | z rotation |
Integer | i | index if component |
boolean | changeInterp | change interpolation |
2217 | function Vehicle:setWorldPosition(x,y,z, xRot,yRot,zRot, i, changeInterp) |
2218 | local component = self.components[i] |
2219 | setWorldTranslation(component.node, x,y,z) |
2220 | setWorldRotation(component.node, xRot,yRot,zRot) |
2221 | if changeInterp then |
2222 | local qx, qy, qz, qw = mathEulerToQuaternion(xRot,yRot,zRot) |
2223 | component.networkInterpolators.quaternion:setQuaternion(qx, qy, qz, qw) |
2224 | component.networkInterpolators.position:setPosition(x,y,z) |
2225 | end |
2226 | end |
setWorldPositionQuaternion
DescriptionSet world position and quaternion rotation of componentDefinition
setWorldPositionQuaternion(float x, float y, float z, float qx, float qy, float qz, float qw, Integer i, boolean changeInterp)Arguments
float | x | x position |
float | y | y position |
float | z | z position |
float | qx | x rotation |
float | qy | y rotation |
float | qz | z rotation |
float | qw | w rotation |
Integer | i | index if component |
boolean | changeInterp | change interpolation |
2239 | function Vehicle:setWorldPositionQuaternion(x,y,z, qx,qy,qz,qw, i, changeInterp) |
2240 | local component = self.components[i] |
2241 | setWorldTranslation(component.node, x,y,z) |
2242 | setWorldQuaternion(component.node, qx,qy,qz,qw) |
2243 | if changeInterp then |
2244 | component.networkInterpolators.quaternion:setQuaternion(qx, qy, qz, qw) |
2245 | component.networkInterpolators.position:setPosition(x,y,z) |
2246 | end |
2247 | end |
showInfo
DescriptionDefinitionshowInfo()Code
3944 | function Vehicle:showInfo(box) |
3945 | if self.isBroken then |
3946 | box:addLine(g_i18n:getText("infohud_vehicleBrokenNeedToReset"), nil, true) |
3947 | end |
3948 | end |
tryFinishLoading
DescriptionDefinitiontryFinishLoading()Code
1231 | function Vehicle:tryFinishLoading() |
1232 | if self.subLoadingTasksFinished then |
1233 | if self.isServer then |
1234 | -- Only servers add to physics and make visible now. Clients will do this later in postReadStream |
1235 | self:setVisibility(true) |
1236 | self:addToPhysics() |
1237 | end |
1238 | |
1239 | self:setLoadingStep(Vehicle.LOAD_STEP_FINISHED) |
1240 | SpecializationUtil.raiseEvent(self, "onLoadFinished", self.savegame) |
1241 | |
1242 | -- if we are the server or in single player we don't need to be synchonized |
1243 | if self.isServer then |
1244 | self:setLoadingStep(Vehicle.LOAD_STEP_SYNCHRONIZED) |
1245 | end |
1246 | |
1247 | if g_currentMission ~= nil and self.propertyState ~= Vehicle.PROPERTY_STATE_SHOP_CONFIG and self.mapHotspotAvailable then |
1248 | self:createMapHotspot() |
1249 | end |
1250 | |
1251 | self.finishedLoading = true |
1252 | |
1253 | local asyncData = self.subLoadingTasksFinishedAsyncData |
1254 | asyncData.asyncCallbackFunction(asyncData.asyncCallbackObject, self, self.loadingState, asyncData.asyncCallbackArguments) |
1255 | |
1256 | self.savegame = nil |
1257 | end |
1258 | end |
unselectVehicle
DescriptionDefinitionunselectVehicle()Code
2801 | function Vehicle:unselectVehicle() |
2802 | self.selectionObject.isSelected = false |
2803 | SpecializationUtil.raiseEvent(self, "onUnselect") |
2804 | |
2805 | self:requestActionEventUpdate() |
2806 | end |
update
DescriptionDefinitionupdate()Code
1830 | function Vehicle:update(dt) |
1831 | -- states |
1832 | self.isActive = self:getIsActive() |
1833 | local isActiveForInput = self:getIsActiveForInput() |
1834 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
1835 | local isSelected = self:getIsSelected() |
1836 | self.lastDistanceToCamera = calcDistanceFrom(self.rootNode, getCamera()) |
1837 | self.currentUpdateDistance = self.lastDistanceToCamera / self.lodDistanceCoeff |
1838 | self.isActiveForInputIgnoreSelectionIgnoreAI = self:getIsActiveForInput(true, true) |
1839 | self.isActiveForLocalSound = self.isActiveForInputIgnoreSelectionIgnoreAI |
1840 | |
1841 | self.updateLoopIndex = g_updateLoopIndex |
1842 | |
1843 | SpecializationUtil.raiseEvent(self, "onPreUpdate", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1844 | |
1845 | -- interpolation of position |
1846 | if not self.isServer and self.synchronizePosition then |
1847 | self.networkTimeInterpolator:update(dt) |
1848 | local interpolationAlpha = self.networkTimeInterpolator:getAlpha() |
1849 | for i, component in pairs(self.components) do |
1850 | if not component.isStatic then |
1851 | local posX, posY, posZ = component.networkInterpolators.position:getInterpolatedValues(interpolationAlpha) |
1852 | local quatX, quatY, quatZ, quatW = component.networkInterpolators.quaternion:getInterpolatedValues(interpolationAlpha) |
1853 | self:setWorldPositionQuaternion(posX, posY, posZ, quatX, quatY, quatZ, quatW, i, false) |
1854 | end |
1855 | end |
1856 | |
1857 | if self.networkTimeInterpolator:isInterpolating() then |
1858 | self:raiseActive() |
1859 | end |
1860 | end |
1861 | SpecializationUtil.raiseEvent(self, "onUpdateInterpolation", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1862 | |
1863 | self:updateVehicleSpeed(dt) |
1864 | |
1865 | if self.actionEventUpdateRequested then |
1866 | self:updateActionEvents() |
1867 | end |
1868 | |
1869 | if Platform.gameplay.automaticVehicleControl then |
1870 | if self.actionController ~= nil then |
1871 | self.actionController:update(dt) |
1872 | self.actionController:updateForAI(dt) |
1873 | end |
1874 | else |
1875 | if self.actionController ~= nil then |
1876 | self.actionController:updateForAI(dt) |
1877 | end |
1878 | end |
1879 | |
1880 | SpecializationUtil.raiseEvent(self, "onUpdate", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1881 | if Vehicle.debuggingActive then |
1882 | SpecializationUtil.raiseEvent(self, "onUpdateDebug", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1883 | end |
1884 | SpecializationUtil.raiseEvent(self, "onPostUpdate", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1885 | |
1886 | if self.finishedFirstUpdate and self.isMassDirty then |
1887 | self.isMassDirty = false |
1888 | self:updateMass() |
1889 | end |
1890 | |
1891 | self.finishedFirstUpdate = true |
1892 | |
1893 | if self.isServer then |
1894 | if not getIsSleeping(self.rootNode) then |
1895 | self:raiseActive() |
1896 | end |
1897 | end |
1898 | |
1899 | VehicleDebug.updateDebug(self, dt) |
1900 | |
1901 | self:updateMapHotspot() |
1902 | end |
updateActionEvents
DescriptionDefinitionupdateActionEvents()Code
2549 | function Vehicle:updateActionEvents() |
2550 | local rootVehicle = self.rootVehicle |
2551 | rootVehicle:registerActionEvents() |
2552 | end |
updateEnd
DescriptionDefinitionupdateEnd()Code
1977 | function Vehicle:updateEnd(dt) |
1978 | local isActiveForInput = self:getIsActiveForInput() |
1979 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
1980 | local isSelected = self:getIsSelected() |
1981 | |
1982 | SpecializationUtil.raiseEvent(self, "onUpdateEnd", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1983 | end |
updateMapHotspot
DescriptionDefinitionupdateMapHotspot()Code
3930 | function Vehicle:updateMapHotspot() |
3931 | if self.mapHotspot ~= nil then |
3932 | self.mapHotspot:setVisible(self:getIsMapHotspotVisible()) |
3933 | end |
3934 | end |
updateMass
DescriptionDefinitionupdateMass()Code
2944 | function Vehicle:updateMass() |
2945 | self.serverMass = 0 |
2946 | |
2947 | for _, component in ipairs(self.components) do |
2948 | if component.defaultMass == nil then |
2949 | if component.isDynamic then |
2950 | component.defaultMass = getMass(component.node) |
2951 | else |
2952 | component.defaultMass = 1 |
2953 | end |
2954 | component.mass = component.defaultMass |
2955 | end |
2956 | |
2957 | local mass = self:getAdditionalComponentMass(component) |
2958 | component.mass = component.defaultMass + mass |
2959 | self.serverMass = self.serverMass + component.mass |
2960 | end |
2961 | |
2962 | local realTotalMass = 0 |
2963 | for _, component in ipairs(self.components) do |
2964 | realTotalMass = realTotalMass + self:getComponentMass(component) |
2965 | end |
2966 | |
2967 | self.precalculatedMass = realTotalMass - self.serverMass |
2968 | |
2969 | for _, component in ipairs(self.components) do |
2970 | local maxFactor = self.serverMass / (self.maxComponentMass - self.precalculatedMass) |
2971 | |
2972 | if maxFactor > 1 then |
2973 | component.mass = component.mass / maxFactor |
2974 | end |
2975 | |
2976 | -- only update physically mass if difference to last mass is greater 20kg |
2977 | if self.isServer and component.isDynamic and math.abs(component.lastMass-component.mass) > 0.02 then |
2978 | setMass(component.node, component.mass) |
2979 | component.lastMass = component.mass |
2980 | end |
2981 | end |
2982 | |
2983 | self.serverMass = math.min(self.serverMass, self.maxComponentMass - self.precalculatedMass) |
2984 | end |
updateSelectableObjects
DescriptionDefinitionupdateSelectableObjects()Code
2705 | function Vehicle:updateSelectableObjects() |
2706 | self.selectableObjects = {} |
2707 | if self == self.rootVehicle then |
2708 | self:registerSelectableObjects(self.selectableObjects) |
2709 | end |
2710 | end |
updateTick
DescriptionupdateTickDefinition
updateTick(float dt)Arguments
float | dt | time since last call in ms |
1907 | function Vehicle:updateTick(dt) |
1908 | local isActiveForInput = self:getIsActiveForInput() |
1909 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
1910 | local isSelected = self:getIsSelected() |
1911 | |
1912 | self.wasTooFast = false |
1913 | |
1914 | if self.isActive then |
1915 | self:updateWaterInfo() |
1916 | end |
1917 | |
1918 | if self.isServer then |
1919 | if self.synchronizePosition then |
1920 | local hasOwner = self:getOwner() ~= nil |
1921 | for i=1, #self.components do |
1922 | local component = self.components[i] |
1923 | if not component.isStatic then |
1924 | local x,y,z = getWorldTranslation(component.node) |
1925 | local x_rot,y_rot,z_rot=getWorldRotation(component.node) |
1926 | local sentTranslation = component.sentTranslation |
1927 | local sentRotation = component.sentRotation |
1928 | if hasOwner or |
1929 | math.abs(x-sentTranslation[1]) > 0.005 or |
1930 | math.abs(y-sentTranslation[2]) > 0.005 or |
1931 | math.abs(z-sentTranslation[3]) > 0.005 or |
1932 | math.abs(x_rot-sentRotation[1]) > 0.1 or |
1933 | math.abs(y_rot-sentRotation[2]) > 0.1 or |
1934 | math.abs(z_rot-sentRotation[3]) > 0.1 |
1935 | then |
1936 | self:raiseDirtyFlags(self.vehicleDirtyFlag) |
1937 | sentTranslation[1] = x |
1938 | sentTranslation[2] = y |
1939 | sentTranslation[3] = z |
1940 | sentRotation[1] = x_rot |
1941 | sentRotation[2] = y_rot |
1942 | sentRotation[3] = z_rot |
1943 | |
1944 | self.lastMoveTime = g_currentMission.time |
1945 | end |
1946 | end |
1947 | end |
1948 | end |
1949 | |
1950 | -- is the vehicle sunken in the water? |
1951 | self.showTailwaterDepthWarning = false |
1952 | if not self.isBroken and not g_gui:getIsGuiVisible() then |
1953 | if self.tailwaterDepth > self.thresholdTailwaterDepthWarning then |
1954 | self.showTailwaterDepthWarning = true |
1955 | if self.tailwaterDepth > self.thresholdTailwaterDepth then |
1956 | self:setBroken() |
1957 | end |
1958 | end |
1959 | end |
1960 | |
1961 | local rootAttacherVehicle = self.rootVehicle |
1962 | if rootAttacherVehicle ~= nil and rootAttacherVehicle ~= self then |
1963 | rootAttacherVehicle.showTailwaterDepthWarning = rootAttacherVehicle.showTailwaterDepthWarning or self.showTailwaterDepthWarning |
1964 | end |
1965 | end |
1966 | |
1967 | if self:getIsOperating() then |
1968 | self:setOperatingTime(self.operatingTime + dt) |
1969 | end |
1970 | |
1971 | SpecializationUtil.raiseEvent(self, "onUpdateTick", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1972 | SpecializationUtil.raiseEvent(self, "onPostUpdateTick", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1973 | end |
updateVehicleChain
DescriptionDefinitionupdateVehicleChain()Code
2772 | function Vehicle:updateVehicleChain(secondCall) |
2773 | local rootVehicle = self:findRootVehicle() |
2774 | if rootVehicle ~= self.rootVehicle then |
2775 | self.rootVehicle = rootVehicle |
2776 | SpecializationUtil.raiseEvent(self, "onRootVehicleChanged", rootVehicle) |
2777 | end |
2778 | |
2779 | if rootVehicle ~= self and not secondCall then |
2780 | rootVehicle:updateVehicleChain() |
2781 | return |
2782 | end |
2783 | |
2784 | for i=#self.childVehicles, 1, -1 do |
2785 | self.childVehicles[i] = nil |
2786 | end |
2787 | |
2788 | self:addChildVehicles(self.childVehicles) |
2789 | |
2790 | if rootVehicle == self then |
2791 | for i=1, #self.childVehicles do |
2792 | if self.childVehicles[i] ~= rootVehicle then |
2793 | self.childVehicles[i]:updateVehicleChain(true) |
2794 | end |
2795 | end |
2796 | end |
2797 | end |
updateVehicleSpeed
DescriptionDefinitionupdateVehicleSpeed()Code
1748 | function Vehicle:updateVehicleSpeed(dt) |
1749 | -- On servers, the physics state is only change |
1750 | if self.finishedFirstUpdate and not self.components[1].isStatic then |
1751 | local speedReal = 0 |
1752 | local movedDistance = 0 |
1753 | local movingDirection = 0 |
1754 | local signedSpeedReal = 0 |
1755 | |
1756 | if not self.isServer or self.components[1].isKinematic then |
1757 | if not self.isServer and self.synchronizePosition then |
1758 | -- Client code can use the interpolation information that is driving the position change |
1759 | local interpPos = self.components[1].networkInterpolators.position |
1760 | local dx, dy, dz = 0, 0, 0 |
1761 | if self.networkTimeInterpolator:isInterpolating() then |
1762 | dx, dy, dz = worldDirectionToLocal(self.components[1].node, interpPos.targetPositionX-interpPos.lastPositionX, interpPos.targetPositionY-interpPos.lastPositionY, interpPos.targetPositionZ-interpPos.lastPositionZ) |
1763 | end |
1764 | |
1765 | if dz > 0.001 then |
1766 | movingDirection = 1 |
1767 | elseif dz < -0.001 then |
1768 | movingDirection = -1 |
1769 | end |
1770 | speedReal = MathUtil.vector3Length(dx, dy, dz) / self.networkTimeInterpolator.interpolationDuration |
1771 | signedSpeedReal = speedReal * (dz >= 0 and 1 or -1) |
1772 | movedDistance = speedReal * dt |
1773 | else |
1774 | -- Client or kinematic code can't use physics velocity, so calculate based on the position change |
1775 | local x,y,z = getWorldTranslation(self.components[1].node) |
1776 | if self.lastPosition == nil then |
1777 | self.lastPosition = {x,y,z} |
1778 | end |
1779 | local dx, dy, dz = worldDirectionToLocal(self.components[1].node, x-self.lastPosition[1], y-self.lastPosition[2], z-self.lastPosition[3]) |
1780 | self.lastPosition[1], self.lastPosition[2], self.lastPosition[3] = x, y, z |
1781 | if dz > 0.001 then |
1782 | movingDirection = 1 |
1783 | elseif dz < -0.001 then |
1784 | movingDirection = -1 |
1785 | end |
1786 | movedDistance = MathUtil.vector3Length(dx, dy, dz) |
1787 | speedReal = movedDistance / dt |
1788 | signedSpeedReal = speedReal * (dz >= 0 and 1 or -1) |
1789 | end |
1790 | elseif self.components[1].isDynamic then |
1791 | -- Dynamic objects on server use velocity provided by the physics engine |
1792 | local vx, vy, vz = getLocalLinearVelocity(self.components[1].node) |
1793 | speedReal = MathUtil.vector3Length(vx, vy, vz)*0.001 |
1794 | movedDistance = speedReal*g_physicsDt |
1795 | signedSpeedReal = speedReal * (vz >= 0 and 1 or -1) |
1796 | if vz > 0.001 then |
1797 | movingDirection = 1 |
1798 | elseif vz < -0.001 then |
1799 | movingDirection = -1 |
1800 | end |
1801 | end |
1802 | |
1803 | if self.isServer then |
1804 | -- On the server, the velocity only changes when a physics simulation step is performed (thus only update the acceleration when something was simulated) |
1805 | if g_physicsDtNonInterpolated > 0 then |
1806 | self.lastSpeedAcceleration = (speedReal*movingDirection - self.lastSpeedReal*self.movingDirection) / g_physicsDtNonInterpolated |
1807 | end |
1808 | else |
1809 | -- On the client, the position is driven by the interpolation (updated with dt) |
1810 | self.lastSpeedAcceleration = (speedReal*movingDirection - self.lastSpeedReal*self.movingDirection) / dt |
1811 | end |
1812 | |
1813 | -- Update smooth values (use less smoothing on the server) |
1814 | if self.isServer then |
1815 | self.lastSpeed = self.lastSpeed*0.5 + speedReal*0.5 |
1816 | self.lastSignedSpeed = self.lastSignedSpeed*0.5 + signedSpeedReal*0.5 |
1817 | else |
1818 | self.lastSpeed = self.lastSpeed*0.9 + speedReal*0.1 |
1819 | self.lastSignedSpeed = self.lastSignedSpeed*0.9 + signedSpeedReal*0.1 |
1820 | end |
1821 | self.lastSpeedReal = speedReal |
1822 | self.lastSignedSpeedReal = signedSpeedReal |
1823 | self.movingDirection = movingDirection |
1824 | self.lastMovedDistance = movedDistance |
1825 | end |
1826 | end |
updateWaterInfo
DescriptionDefinitionupdateWaterInfo()Code
2469 | function Vehicle:updateWaterInfo() |
2470 | -- water info is always one frame delayed for current position (async) |
2471 | local x, y, z = getWorldTranslation(self.rootNode) |
2472 | self.waterCheckPosition[1] = x |
2473 | self.waterCheckPosition[2] = y |
2474 | self.waterCheckPosition[3] = z |
2475 | g_currentMission.environmentAreaSystem:getWaterYAtWorldPositionAsync(x, y, z, self.onWaterRaycastCallback, self, nil) |
2476 | end |
wakeUp
DescriptionShortly wake up the vehicle physicsDefinition
wakeUp()Code
3968 | function Vehicle:wakeUp() |
3969 | I3DUtil.wakeUpObject(self.components[1].node) |
3970 | end |
writeStream
DescriptionCalled on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
1580 | function Vehicle:writeStream(streamId, connection) |
1581 | Vehicle:superClass().writeStream(self, streamId, connection) |
1582 | |
1583 | streamWriteString(streamId, NetworkUtil.convertToNetworkFilename(self.configFileName)) |
1584 | streamWriteString(streamId, self.typeName) |
1585 | |
1586 | local numConfigs = 0 |
1587 | for _,_ in pairs(self.configurations) do |
1588 | numConfigs = numConfigs + 1 |
1589 | end |
1590 | |
1591 | streamWriteUIntN(streamId, numConfigs, ConfigurationUtil.SEND_NUM_BITS) |
1592 | for configName, configId in pairs(self.configurations) do |
1593 | local configNameId = g_configurationManager:getConfigurationIndexByName(configName) |
1594 | streamWriteUIntN(streamId, configNameId-1, ConfigurationUtil.SEND_NUM_BITS) |
1595 | streamWriteUInt16(streamId, configId-1) |
1596 | end |
1597 | |
1598 | local numBoughtConfigs = 0 |
1599 | for _,_ in pairs(self.boughtConfigurations) do |
1600 | numBoughtConfigs = numBoughtConfigs + 1 |
1601 | end |
1602 | |
1603 | streamWriteUIntN(streamId, numBoughtConfigs, ConfigurationUtil.SEND_NUM_BITS) |
1604 | for configName, configIds in pairs(self.boughtConfigurations) do |
1605 | local numBoughtConfigIds = 0 |
1606 | for _,_ in pairs(configIds) do |
1607 | numBoughtConfigIds = numBoughtConfigIds + 1 |
1608 | end |
1609 | local configNameId = g_configurationManager:getConfigurationIndexByName(configName) |
1610 | streamWriteUIntN(streamId, configNameId-1, ConfigurationUtil.SEND_NUM_BITS) |
1611 | streamWriteUInt16(streamId, numBoughtConfigIds) |
1612 | for id, _ in pairs(configIds) do |
1613 | streamWriteUInt16(streamId, id-1) |
1614 | end |
1615 | end |
1616 | |
1617 | streamWriteUIntN(streamId, self.propertyState, 2) |
1618 | end |
writeUpdateStream
DescriptionCalled on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
integer | streamId | stream ID |
table | connection | connection |
integer | dirtyMask | dirty mask |
1709 | function Vehicle:writeUpdateStream(streamId, connection, dirtyMask) |
1710 | if not connection.isServer then |
1711 | if streamWriteBool(streamId, bitAND(dirtyMask, self.vehicleDirtyFlag) ~= 0) then |
1712 | |
1713 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
1714 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
1715 | for i=1, #self.components do |
1716 | local component = self.components[i] |
1717 | if not component.isStatic then |
1718 | local x,y,z = getWorldTranslation(component.node) |
1719 | local x_rot,y_rot,z_rot = getWorldRotation(component.node) |
1720 | NetworkUtil.writeCompressedWorldPosition(streamId, x, paramsXZ) |
1721 | NetworkUtil.writeCompressedWorldPosition(streamId, y, paramsY) |
1722 | NetworkUtil.writeCompressedWorldPosition(streamId, z, paramsXZ) |
1723 | NetworkUtil.writeCompressedAngle(streamId, x_rot) |
1724 | NetworkUtil.writeCompressedAngle(streamId, y_rot) |
1725 | NetworkUtil.writeCompressedAngle(streamId, z_rot) |
1726 | end |
1727 | end |
1728 | SpecializationUtil.raiseEvent(self, "onWritePositionUpdateStream", streamId, connection, dirtyMask) |
1729 | end |
1730 | end |
1731 | |
1732 | if Vehicle.DEBUG_NETWORK_READ_WRITE_UPDATE then |
1733 | print("-------------------------------------------------------------") |
1734 | print(self.configFileName) |
1735 | for _, spec in ipairs(self.eventListeners["onWriteUpdateStream"]) do |
1736 | local className = ClassUtil.getClassName(spec) |
1737 | local startBits = streamGetWriteOffset(streamId) |
1738 | spec["onWriteUpdateStream"](self, streamId, connection, dirtyMask) |
1739 | print(" "..tostring(className).." Wrote " .. streamGetWriteOffset(streamId)-startBits .. " bits") |
1740 | end |
1741 | else |
1742 | SpecializationUtil.raiseEvent(self, "onWriteUpdateStream", streamId, connection, dirtyMask) |
1743 | end |
1744 | end |