Script v1.7.1.0
- AI
- Animals
- Contracts
- Debug
- Economy
- Effects
- Events
- Farms
- GUI
- Handtools
- I3d
- Materials
- Misc
- Objects
- Placeables
- Player
- Shop
- Sounds
- Specializations
- Triggers
- Utils
- Vehicles
- AIVehicleUtil
- ConfigurationManager
- ConfigurationUtil
- SpecializationManager
- SpecializationUtil
- Vehicle
- VehicleCamera
- VehicleCharacter
- VehicleHudUtils
- VehicleMotor
- VehiclePlacementCallback
- VehicleTypeManager
- WheelsUtil
- WorkAreaTypeManager
- Weather
Engine v1.7.1.0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- XML
- general
Foundation Reference
Vehicle
DescriptionThis class handles all basic functionality of a vehicle - loading of i3d - syncing of components - handling of specializationsParent
ObjectFunctions
- actionEventToggleSelection
- activate
- addNodeObjectMapping
- addSubselection
- addToPhysics
- addToTotalVehicleList
- addVehicleToAIImplementList
- createComponentJoint
- dayChanged
- deactivate
- delete
- doCheckSpeedLimit
- doCollisionMaskCheck
- draw
- drawUIInfo
- getAdditionalComponentMass
- getAdditionalSchemaText
- getCanBePickedUp
- getCanBeReset
- getCanBeSelected
- getCanByMounted
- getCanToggleSelectable
- getChildVehicles
- getDailyUpkeep
- getDeactivateOnLeave
- getDistanceToNode
- getFillLevelInformation
- getFullName
- getInteractionHelp
- getIsActive
- getIsActiveForInput
- getIsActiveForSound
- getIsAIActive
- getIsInUse
- getIsLowered
- getIsOnField
- getIsOperating
- getIsReadyForAutomatedTrainTravel
- getIsSelected
- getIsVehicleNode
- getLastSpeed
- getLimitedVehicleYPosition
- getName
- getOperatingTime
- getOwner
- getParentComponent
- getPrice
- getPropertyState
- getReloadXML
- getRepairPrice
- getRootVehicle
- getSelectedObject
- getSelectedVehicle
- getSellPrice
- getSpecValueCombinations
- getSpecValueDailyUpkeep
- getSpecValueOperatingTime
- getSpecValueSlots
- getSpecValueSpeedLimit
- getSpecValueWorkingWidth
- getSpeedLimit
- getTailwaterDepth
- getTotalMass
- getVehicleDamage
- getWorkLoad
- hasInputConflictWithSelection
- init
- interact
- load
- loadComponentFromXML
- loadComponentJointFromXML
- loadFinished
- loadSchemaOverlay
- loadSpecValueCombinations
- loadSpecValueSpeedLimit
- loadSpecValueWorkingWidth
- new
- onVehicleWakeUpCallback
- raiseStateChange
- readStream
- readUpdateStream
- registerActionEvents
- registerEvents
- registerFunctions
- registerInteractionFlag
- registerSelectableObjects
- registerStateChange
- removeActionEvent
- removeActionEvents
- removeFromPhysics
- removeNodeObjectMapping
- requestActionEventUpdate
- saveStatsToXMLFile
- saveToXMLFile
- selectVehicle
- setAbsolutePosition
- setBroken
- setComponentJointFrame
- setComponentJointRotLimit
- setComponentJointTransLimit
- setLoadingState
- setMassDirty
- setOperatingTime
- setRelativePosition
- setSelectedObject
- setSelectedVehicle
- setWorldPosition
- setWorldPositionQuaternion
- unselectVehicle
- update
- updateActionEvents
- updateEnd
- updateMass
- updateSelectableObjects
- updateTick
- updateVehicleSpeed
- writeStream
- writeUpdateStream
actionEventToggleSelection
DescriptionDefinitionactionEventToggleSelection()Code
2951 | function Vehicle.actionEventToggleSelection(self, actionName, inputValue, callbackState, isAnalog) |
2952 | local currentSelection = self.currentSelection |
2953 | local currentObject = currentSelection.object |
2954 | local currentObjectIndex = currentSelection.index |
2955 | local currentSubObjectIndex = currentSelection.subIndex |
2956 | |
2957 | local numSubSelections = 0 |
2958 | if currentObject ~= nil then |
2959 | numSubSelections = #currentObject.subSelections |
2960 | end |
2961 | |
2962 | local newSelectedSubObjectIndex = currentSubObjectIndex + 1 |
2963 | local newSelectedObjectIndex = currentObjectIndex |
2964 | local newSelectedObject = currentObject |
2965 | |
2966 | if newSelectedSubObjectIndex > numSubSelections then |
2967 | newSelectedSubObjectIndex = 1 |
2968 | newSelectedObjectIndex = currentObjectIndex + 1 |
2969 | |
2970 | if newSelectedObjectIndex > #self.selectableObjects then |
2971 | newSelectedObjectIndex = 1 |
2972 | end |
2973 | newSelectedObject = self.selectableObjects[newSelectedObjectIndex] |
2974 | end |
2975 | |
2976 | if currentObject ~= newSelectedObject or currentObjectIndex ~= newSelectedObjectIndex or currentSubObjectIndex ~= newSelectedSubObjectIndex then |
2977 | -- event |
2978 | self:setSelectedObject(newSelectedObject, newSelectedSubObjectIndex) |
2979 | end |
2980 | end |
activate
DescriptionCalled on activateDefinition
activate()Code
2257 | function Vehicle:activate() |
2258 | SpecializationUtil.raiseEvent(self, "onActivate") |
2259 | end |
addNodeObjectMapping
DescriptionAdd component nodes to listDefinition
addNodeObjectMapping(table list)Arguments
table | list | list |
1462 | function Vehicle:addNodeObjectMapping(list) |
1463 | for _,v in pairs(self.components) do |
1464 | list[v.node] = self |
1465 | end |
1466 | end |
addSubselection
DescriptionDefinitionaddSubselection()Code
2028 | function Vehicle:addSubselection(subSelection) |
2029 | table.insert(self.selectionObject.subSelections, subSelection) |
2030 | return #self.selectionObject.subSelections |
2031 | end |
addToPhysics
DescriptionAdd vehicle to physicsDefinition
addToPhysics()Return Values
boolean | success | success |
1480 | function Vehicle:addToPhysics() |
1481 | |
1482 | if not self.isAddedToPhysics then |
1483 | local lastMotorizedNode = nil |
1484 | for _, component in pairs(self.components) do |
1485 | addToPhysics(component.node) |
1486 | if component.motorized then |
1487 | if lastMotorizedNode ~= nil then |
1488 | if self.isServer then |
1489 | addVehicleLink(lastMotorizedNode, component.node) |
1490 | end |
1491 | end |
1492 | lastMotorizedNode = component.node |
1493 | end |
1494 | end |
1495 | |
1496 | self.isAddedToPhysics = true |
1497 | |
1498 | if self.isServer then |
1499 | for _, jointDesc in pairs(self.componentJoints) do |
1500 | self:createComponentJoint(self.components[jointDesc.componentIndices[1]], self.components[jointDesc.componentIndices[2]], jointDesc) |
1501 | end |
1502 | |
1503 | -- if rootnode is sleeping all other components are sleeping as well |
1504 | addWakeUpReport(self.rootNode, "onVehicleWakeUpCallback", self) |
1505 | end |
1506 | |
1507 | for _, collisionPair in pairs(self.collisionPairs) do |
1508 | setPairCollision(collisionPair.component1.node, collisionPair.component2.node, collisionPair.enabled) |
1509 | end |
1510 | |
1511 | self:setMassDirty() |
1512 | end |
1513 | |
1514 | return true |
1515 | end |
addToTotalVehicleList
DescriptionDefinitionaddToTotalVehicleList()Code
2783 | function Vehicle:addToTotalVehicleList(list) |
2784 | list[self] = self |
2785 | end |
addVehicleToAIImplementList
DescriptionDefinitionaddVehicleToAIImplementList()Code
2778 | function Vehicle:addVehicleToAIImplementList(list) |
2779 | 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 |
2591 | function Vehicle:createComponentJoint(component1, component2, jointDesc) |
2592 | if component1 == nil or component2 == nil or jointDesc == nil then |
2593 | g_logManager:xmlWarning(self.configFileName, "Could not create component joint. No component1, component2 or jointDesc given!") |
2594 | return false |
2595 | end |
2596 | |
2597 | local constr = JointConstructor:new() |
2598 | constr:setActors(component1.node, component2.node) |
2599 | |
2600 | local localPoses1 = jointDesc.jointLocalPoses[1] |
2601 | local localPoses2 = jointDesc.jointLocalPoses[2] |
2602 | constr:setJointLocalPositions(localPoses1.trans[1], localPoses1.trans[2], localPoses1.trans[3], localPoses2.trans[1], localPoses2.trans[2], localPoses2.trans[3]) |
2603 | constr:setJointLocalRotations(localPoses1.rot[1], localPoses1.rot[2], localPoses1.rot[3], localPoses2.rot[1], localPoses2.rot[2], localPoses2.rot[3]) |
2604 | --constr:setJointTransforms(jointDesc.jointNode, jointDesc.jointNodeActor1) |
2605 | |
2606 | constr:setRotationLimitSpring(jointDesc.rotLimitSpring[1], jointDesc.rotLimitDamping[1], jointDesc.rotLimitSpring[2], jointDesc.rotLimitDamping[2], jointDesc.rotLimitSpring[3], jointDesc.rotLimitDamping[3]) |
2607 | constr:setTranslationLimitSpring(jointDesc.transLimitSpring[1], jointDesc.transLimitDamping[1], jointDesc.transLimitSpring[2], jointDesc.transLimitDamping[2], jointDesc.transLimitSpring[3], jointDesc.transLimitDamping[3]) |
2608 | constr:setZRotationXOffset(jointDesc.zRotationXOffset) |
2609 | for i=1, 3 do |
2610 | if jointDesc.rotLimit[i] >= jointDesc.rotMinLimit[i] then |
2611 | constr:setRotationLimit(i-1, jointDesc.rotMinLimit[i], jointDesc.rotLimit[i]) |
2612 | end |
2613 | |
2614 | if jointDesc.transLimit[i] >= jointDesc.transMinLimit[i] then |
2615 | constr:setTranslationLimit(i-1, true, jointDesc.transMinLimit[i], jointDesc.transLimit[i]) |
2616 | else |
2617 | constr:setTranslationLimit(i-1, false, 0, 0) |
2618 | end |
2619 | end |
2620 | |
2621 | constr:setRotationLimitForceLimit(jointDesc.rotLimitForceLimit[1], jointDesc.rotLimitForceLimit[2], jointDesc.rotLimitForceLimit[3]) |
2622 | constr:setTranslationLimitForceLimit(jointDesc.transLimitForceLimit[1], jointDesc.transLimitForceLimit[2], jointDesc.transLimitForceLimit[3]) |
2623 | |
2624 | if jointDesc.isBreakable then |
2625 | constr:setBreakable(jointDesc.breakForce, jointDesc.breakTorque) |
2626 | end |
2627 | constr:setEnableCollision(jointDesc.enableCollision) |
2628 | |
2629 | for i=1,3 do |
2630 | if jointDesc.maxRotDriveForce[i] > 0.0001 and (jointDesc.rotDriveVelocity[i] ~= nil or jointDesc.rotDriveRotation[i] ~= nil) then |
2631 | local pos = Utils.getNoNil(jointDesc.rotDriveRotation[i], 0) |
2632 | local vel = Utils.getNoNil(jointDesc.rotDriveVelocity[i], 0) |
2633 | constr:setAngularDrive(i-1, jointDesc.rotDriveRotation[i] ~= nil, jointDesc.rotDriveVelocity[i] ~= nil, jointDesc.rotDriveSpring[i], jointDesc.rotDriveDamping[i], jointDesc.maxRotDriveForce[i], pos, vel) |
2634 | end |
2635 | if jointDesc.maxTransDriveForce[i] > 0.0001 and (jointDesc.transDriveVelocity[i] ~= nil or jointDesc.transDrivePosition[i] ~= nil) then |
2636 | local pos = Utils.getNoNil(jointDesc.transDrivePosition[i], 0) |
2637 | local vel = Utils.getNoNil(jointDesc.transDriveVelocity[i], 0) |
2638 | constr:setLinearDrive(i-1, jointDesc.transDrivePosition[i] ~= nil, jointDesc.transDriveVelocity[i] ~= nil, jointDesc.transDriveSpring[i], jointDesc.transDriveDamping[i], jointDesc.maxTransDriveForce[i], pos, vel) |
2639 | end |
2640 | end |
2641 | |
2642 | jointDesc.jointIndex = constr:finalize() |
2643 | |
2644 | return true |
2645 | end |
dayChanged
DescriptionCalled if day changedDefinition
dayChanged()Code
2721 | function Vehicle:dayChanged() |
2722 | self.age = self.age + 1 |
2723 | end |
deactivate
DescriptionCalled on deactivateDefinition
deactivate()Code
2263 | function Vehicle:deactivate() |
2264 | SpecializationUtil.raiseEvent(self, "onDeactivate") |
2265 | end |
delete
DescriptionDefinitiondelete()Code
801 | function Vehicle:delete() |
802 | g_messageCenter:unsubscribeAll(self) |
803 | |
804 | if g_currentMission ~= nil and g_currentMission.environment ~= nil then |
805 | g_currentMission.environment:removeDayChangeListener(self) |
806 | end |
807 | |
808 | local rootVehicle = self:getRootVehicle() |
809 | if rootVehicle:getIsAIActive() then |
810 | rootVehicle:stopAIVehicle(AIVehicle.STOP_REASON_REGULAR) |
811 | end |
812 | |
813 | g_inputBinding:beginActionEventsModification(Vehicle.INPUT_CONTEXT_NAME) |
814 | self:removeActionEvents() |
815 | g_inputBinding:endActionEventsModification() |
816 | |
817 | SpecializationUtil.raiseEvent(self, "onPreDelete") |
818 | SpecializationUtil.raiseEvent(self, "onDelete") |
819 | |
820 | if self.isServer then |
821 | for _,v in pairs(self.componentJoints) do |
822 | if v.jointIndex ~= 0 then |
823 | removeJoint(v.jointIndex) |
824 | end |
825 | end |
826 | |
827 | removeWakeUpReport(self.rootNode) |
828 | end |
829 | |
830 | for _,v in pairs(self.components) do |
831 | delete(v.node) |
832 | end |
833 | |
834 | g_i3DManager:releaseSharedI3DFile(self.i3dFilename, self.baseDirectory, true) |
835 | |
836 | delete(self.xmlFile) |
837 | |
838 | self.isDeleted = true |
839 | |
840 | Vehicle:superClass().delete(self) |
841 | end |
doCheckSpeedLimit
DescriptionDefinitiondoCheckSpeedLimit()Code
2733 | function Vehicle:doCheckSpeedLimit() |
2734 | return false |
2735 | end |
doCollisionMaskCheck
DescriptionDefinitiondoCollisionMaskCheck()Code
2804 | function Vehicle:doCollisionMaskCheck(targetCollisionMask, path, node, str) |
2805 | local ignoreCheck = false |
2806 | if path ~= nil then |
2807 | ignoreCheck = Utils.getNoNil(getXMLBool(self.xmlFile, path), false) |
2808 | end |
2809 | |
2810 | if not ignoreCheck then |
2811 | local hasMask = false |
2812 | if node == nil then |
2813 | for _, component in ipairs(self.components) do |
2814 | hasMask = hasMask or bitAND(getCollisionMask(component.node), targetCollisionMask) == targetCollisionMask |
2815 | end |
2816 | else |
2817 | hasMask = hasMask or bitAND(getCollisionMask(node), targetCollisionMask) == targetCollisionMask |
2818 | end |
2819 | |
2820 | if not hasMask then |
2821 | g_logManager:xmlWarning(self.configFileName, "%s has wrong collision mask! Following bit(s) need to be set '%s' or use '%s'", str or self.typeName, MathUtil.numberToSetBitsStr(targetCollisionMask), path) |
2822 | return false |
2823 | end |
2824 | end |
2825 | |
2826 | return true |
2827 | end |
draw
DescriptionDefinitiondraw()Code
1412 | function Vehicle:draw() |
1413 | -- draw root attacher vehicle and the selected implement |
1414 | if self:getIsSelected() or self:getRootVehicle() == self then |
1415 | local isActiveForInput = self:getIsActiveForInput() |
1416 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
1417 | SpecializationUtil.raiseEvent(self, "onDraw", isActiveForInput, isActiveForInputIgnoreSelection, true) |
1418 | end |
1419 | |
1420 | -- DebugUtil.drawDebugNode(self.rootNode, "farm: " .. tostring(self:getOwnerFarmId()) .. ", controller: " .. tostring(self:getActiveFarm())) |
1421 | |
1422 | -- if self == self:getRootVehicle() and self:getIsActiveForInput() then |
1423 | -- renderText(0.5, 0.95, 0.012, tostring(self:getSelectedVehicle())) |
1424 | -- for k, object in ipairs(self.selectableObjects) do |
1425 | -- renderText(0.5, 0.9-k*0.015, 0.012, tostring(object.vehicle) .. " " .. tostring(object.vehicle.configFileName) .. ": " .. #object.subSelections .. " " .. tostring(object.vehicle:getIsSelected())) |
1426 | -- end |
1427 | -- end |
1428 | |
1429 | VehicleDebug.drawDebug(self) |
1430 | |
1431 | if self.showTailwaterDepthWarning then |
1432 | g_currentMission:showBlinkingWarning(g_i18n:getText("warning_dontDriveIntoWater"), 2000) |
1433 | end |
1434 | end |
drawUIInfo
DescriptionDraw UI infoDefinition
drawUIInfo()Code
1439 | function Vehicle:drawUIInfo() |
1440 | if g_showVehicleDistance then |
1441 | local dist = calcDistanceFrom(self.rootNode, getCamera()) |
1442 | if dist <= 350 then |
1443 | Utils.renderTextAtWorldPosition(x,y+1,z, string.format("%.0f", dist), getCorrectTextSize(0.02), 0) |
1444 | end |
1445 | end |
1446 | end |
getAdditionalComponentMass
DescriptionDefinitiongetAdditionalComponentMass()Code
2227 | function Vehicle:getAdditionalComponentMass(component) |
2228 | return 0 |
2229 | end |
getAdditionalSchemaText
DescriptionDefinitiongetAdditionalSchemaText()Code
2715 | function Vehicle:getAdditionalSchemaText() |
2716 | return nil |
2717 | end |
getCanBePickedUp
DescriptionDefinitiongetCanBePickedUp()Code
2927 | function Vehicle:getCanBePickedUp(byPlayer) |
2928 | return self.supportsPickUp and self:getTotalMass() <= Player.MAX_PICKABLE_OBJECT_MASS and g_currentMission.accessHandler:canPlayerAccess(self, byPlayer) |
2929 | end |
getCanBeReset
DescriptionDefinitiongetCanBeReset()Code
2933 | function Vehicle:getCanBeReset() |
2934 | return self.canBeReset |
2935 | end |
getCanBeSelected
DescriptionDefinitiongetCanBeSelected()Code
2035 | function Vehicle:getCanBeSelected() |
2036 | return VehicleDebug.state ~= 0 -- allow selection while any debug modes is active to debug any vehicle |
2037 | end |
getCanByMounted
DescriptionDefinitiongetCanByMounted()Code
2878 | function Vehicle:getCanByMounted() |
2879 | return entityExists(self.components[1].node) |
2880 | end |
getCanToggleSelectable
DescriptionDefinitiongetCanToggleSelectable()Code
2041 | function Vehicle:getCanToggleSelectable() |
2042 | return false |
2043 | end |
getChildVehicles
DescriptionDefinitiongetChildVehicles()Code
2053 | function Vehicle:getChildVehicles(vehicles) |
2054 | table.insert(vehicles, self) |
2055 | end |
getDailyUpkeep
DescriptionGet daily up keepDefinition
getDailyUpkeep()Return Values
float | dailyUpkeep | daily up keep |
2885 | function Vehicle:getDailyUpkeep() |
2886 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
2887 | |
2888 | local multiplier = 1 |
2889 | if storeItem.lifetime ~= nil and storeItem.lifetime ~= 0 then |
2890 | local ageMultiplier = 0.3 * math.min(self.age/storeItem.lifetime, 1) |
2891 | local operatingTime = self.operatingTime / (1000*60*60) |
2892 | local operatingTimeMultiplier = 0.7 * math.min(operatingTime / (storeItem.lifetime*EconomyManager.LIFETIME_OPERATINGTIME_RATIO), 1) |
2893 | multiplier = 1 + EconomyManager.MAX_DAILYUPKEEP_MULTIPLIER * (ageMultiplier+operatingTimeMultiplier) |
2894 | end |
2895 | |
2896 | return StoreItemUtil.getDailyUpkeep(storeItem, self.configurations) * multiplier |
2897 | end |
getDeactivateOnLeave
DescriptionDefinitiongetDeactivateOnLeave()Code
1724 | function Vehicle:getDeactivateOnLeave() |
1725 | return true |
1726 | end |
getDistanceToNode
DescriptionDefinitiongetDistanceToNode()Code
2758 | function Vehicle:getDistanceToNode(node) |
2759 | self.interactionFlag = Vehicle.INTERACTION_FLAG_NONE |
2760 | return math.huge |
2761 | end |
getFillLevelInformation
DescriptionGet fill level informationDefinition
getFillLevelInformation(table fillLevelInformations)Arguments
table | fillLevelInformations | fill level informations |
2252 | function Vehicle:getFillLevelInformation(fillLevelInformations) |
2253 | end |
getFullName
DescriptionDefinitiongetFullName()Code
2908 | function Vehicle:getFullName() |
2909 | local name = self:getName() |
2910 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
2911 | if storeItem ~= nil then |
2912 | local brand = g_brandManager:getBrandByIndex(storeItem.brandIndex) |
2913 | if brand ~= nil then |
2914 | name = brand.title .. " " .. name |
2915 | end |
2916 | end |
2917 | |
2918 | if self:getIsAIActive() then |
2919 | name = name .. " (" .. g_i18n:getText("ui_helper") .. " " .. self:getCurrentHelper().name .. ")" |
2920 | end |
2921 | |
2922 | return name |
2923 | end |
getInteractionHelp
DescriptionDefinitiongetInteractionHelp()Code
2752 | function Vehicle:getInteractionHelp() |
2753 | return "" |
2754 | end |
getIsActive
DescriptionDefinitiongetIsActive()Code
1762 | function Vehicle:getIsActive() |
1763 | if self.isBroken then |
1764 | return false |
1765 | end |
1766 | |
1767 | if self.forceIsActive then |
1768 | return true |
1769 | end |
1770 | |
1771 | return false |
1772 | end |
getIsActiveForInput
DescriptionDefinitiongetIsActiveForInput()Code
1776 | function Vehicle:getIsActiveForInput(ignoreSelection, activeForAI) |
1777 | if not self.allowsInput then |
1778 | return false |
1779 | end |
1780 | |
1781 | if not g_currentMission.isRunning then |
1782 | return false |
1783 | end |
1784 | |
1785 | if (activeForAI == nil or not activeForAI) and self:getIsAIActive() then |
1786 | return false |
1787 | end |
1788 | |
1789 | if not ignoreSelection or ignoreSelection == nil then |
1790 | local rootVehicle = self:getRootVehicle() |
1791 | if rootVehicle ~= nil then |
1792 | local selectedObject = rootVehicle:getSelectedVehicle() |
1793 | if self ~= selectedObject then |
1794 | return false |
1795 | end |
1796 | else |
1797 | return false |
1798 | end |
1799 | end |
1800 | |
1801 | local rootAttacherVehicle = self:getRootVehicle() |
1802 | if rootAttacherVehicle ~= self then |
1803 | if not rootAttacherVehicle:getIsActiveForInput(true, activeForAI) then |
1804 | return false |
1805 | end |
1806 | else |
1807 | -- if it is the root vehicle and it is not enterable we check for a attacherVehicle |
1808 | if self.getIsEntered == nil and self.getAttacherVehicle ~= nil and self:getAttacherVehicle() == nil then |
1809 | return false |
1810 | end |
1811 | end |
1812 | |
1813 | return true |
1814 | end |
getIsActiveForSound
DescriptionDefinitiongetIsActiveForSound()Code
1818 | function Vehicle:getIsActiveForSound() |
1819 | print("Warning: Vehicle:getIsActiveForSound() is deprecated") |
1820 | return false |
1821 | end |
getIsAIActive
DescriptionDefinitiongetIsAIActive()Code
2765 | function Vehicle:getIsAIActive() |
2766 | if self.getAttacherVehicle ~= nil then |
2767 | local attacherVehicle = self:getAttacherVehicle() |
2768 | if attacherVehicle ~= nil then |
2769 | return attacherVehicle:getIsAIActive() |
2770 | end |
2771 | end |
2772 | |
2773 | return false |
2774 | end |
getIsInUse
DescriptionDefinitiongetIsInUse()Code
2939 | function Vehicle:getIsInUse(connection) |
2940 | return false |
2941 | end |
getIsLowered
DescriptionDefinitiongetIsLowered()Code
1825 | function Vehicle:getIsLowered(defaultIsLowered) |
1826 | return false |
1827 | end |
getIsOnField
DescriptionReturns true if vehicle is on a fieldDefinition
getIsOnField()Return Values
boolean | isOnField | is on field |
1673 | function Vehicle:getIsOnField() |
1674 | local densityBits = 0 |
1675 | for _,component in pairs(self.components) do |
1676 | local wx, wy, wz = localToWorld(component.node, getCenterOfMass(component.node)) |
1677 | |
1678 | local h = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz) |
1679 | if h-1 > wy then -- 1m threshold since ground tools are working slightly under the ground |
1680 | break |
1681 | end |
1682 | |
1683 | local bits = getDensityAtWorldPos(g_currentMission.terrainDetailId, wx, wy, wz) |
1684 | densityBits = bitOR(densityBits, bits) |
1685 | if densityBits ~= 0 then |
1686 | return true |
1687 | end |
1688 | end |
1689 | |
1690 | return false |
1691 | end |
getIsOperating
DescriptionReturns true if is operatingDefinition
getIsOperating()Return Values
boolean | isOperating | is operating |
1756 | function Vehicle:getIsOperating() |
1757 | return false |
1758 | end |
getIsReadyForAutomatedTrainTravel
DescriptionDefinitiongetIsReadyForAutomatedTrainTravel()Code
2831 | function Vehicle:getIsReadyForAutomatedTrainTravel() |
2832 | return true |
2833 | end |
getIsSelected
DescriptionDefinitiongetIsSelected()Code
2161 | function Vehicle:getIsSelected() |
2162 | return self.selectionObject.isSelected |
2163 | end |
getIsVehicleNode
DescriptionReturns true if node is from vehicleDefinition
getIsVehicleNode(Integer nodeId)Arguments
Integer | nodeId | node id |
boolean | isFromVehicle | is from vehicle |
1749 | function Vehicle:getIsVehicleNode(nodeId) |
1750 | return self.vehicleNodes[nodeId] ~= nil |
1751 | end |
getLastSpeed
DescriptionReturns last speed in kphDefinition
getLastSpeed(boolean useAttacherVehicleSpeed)Arguments
boolean | useAttacherVehicleSpeed | use speed of attacher vehicle |
float | lastSpeed | last speed |
1711 | function Vehicle:getLastSpeed(useAttacherVehicleSpeed) |
1712 | if useAttacherVehicleSpeed then |
1713 | if self.attacherVehicle ~= nil then |
1714 | return self.attacherVehicle:getLastSpeed(true) |
1715 | end |
1716 | end |
1717 | |
1718 | return self.lastSpeed * 3600 |
1719 | end |
getLimitedVehicleYPosition
DescriptionDefinitiongetLimitedVehicleYPosition()Code
1574 | function Vehicle:getLimitedVehicleYPosition(position) |
1575 | if position.posY == nil then |
1576 | -- vehicle position based on yOffset |
1577 | local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, position.posX, 300, position.posZ) |
1578 | return terrainHeight + Utils.getNoNil(position.yOffset, 0) |
1579 | end |
1580 | |
1581 | return position.posY |
1582 | end |
getName
DescriptionDefinitiongetName()Code
2901 | function Vehicle:getName() |
2902 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
2903 | return storeItem.name |
2904 | end |
getOperatingTime
DescriptionDefinitiongetOperatingTime()Code
2798 | function Vehicle:getOperatingTime() |
2799 | return self.operatingTime |
2800 | end |
getOwner
DescriptionGet owner of vehicleDefinition
getOwner()Return Values
table | owner | owner |
1731 | function Vehicle:getOwner() |
1732 | if self.owner ~= nil then |
1733 | return self.owner |
1734 | end |
1735 | |
1736 | return nil |
1737 | end |
getParentComponent
DescriptionGet parent component of nodeDefinition
getParentComponent(Integer node)Arguments
Integer | node | id of node |
Integer | parentComponent | id of parent component node |
1697 | function Vehicle:getParentComponent(node) |
1698 | while node ~= 0 do |
1699 | if self:getIsVehicleNode(node) then |
1700 | return node |
1701 | end |
1702 | node = getParent(node) |
1703 | end |
1704 | return 0 |
1705 | end |
getPrice
DescriptionReturns priceDefinition
getPrice(float price)Arguments
float | price | price |
1648 | function Vehicle:getPrice() |
1649 | return self.price |
1650 | end |
getPropertyState
DescriptionDefinitiongetPropertyState()Code
2945 | function Vehicle:getPropertyState() |
2946 | return self.propertyState |
2947 | end |
getReloadXML
DescriptionGet reload xmlDefinition
getReloadXML(table vehicle)Arguments
table | vehicle | vehicle |
string | xml | xml |
2987 | function Vehicle.getReloadXML(vehicle) |
2988 | |
2989 | local vehicleXMLFile = createXMLFile("vehicleXMLFile", "", "vehicles") |
2990 | if vehicleXMLFile ~= nil then |
2991 | local key = string.format("vehicles.vehicle(%d)", 0) |
2992 | |
2993 | setXMLInt(vehicleXMLFile, key.."#id", 1) |
2994 | setXMLString(vehicleXMLFile, key.."#filename", HTMLUtil.encodeToHTML(NetworkUtil.convertToNetworkFilename(vehicle.configFileName))) |
2995 | |
2996 | vehicle:saveToXMLFile(vehicleXMLFile, key, {}) |
2997 | |
2998 | return vehicleXMLFile |
2999 | end |
3000 | |
3001 | return nil |
3002 | end |
getRepairPrice
DescriptionDefinitiongetRepairPrice()Code
1859 | function Vehicle:getRepairPrice(atSellingPoint) |
1860 | return 0 |
1861 | end |
getRootVehicle
DescriptionDefinitiongetRootVehicle()Code
2047 | function Vehicle:getRootVehicle() |
2048 | return self |
2049 | end |
getSelectedObject
DescriptionDefinitiongetSelectedObject()Code
2167 | function Vehicle:getSelectedObject() |
2168 | local rootVehicle = self:getRootVehicle() |
2169 | if rootVehicle == self then |
2170 | return self.currentSelection.object |
2171 | end |
2172 | |
2173 | return rootVehicle:getSelectedObject() |
2174 | end |
getSelectedVehicle
DescriptionDefinitiongetSelectedVehicle()Code
2178 | function Vehicle:getSelectedVehicle() |
2179 | local selectedObject = self:getSelectedObject() |
2180 | if selectedObject ~= nil then |
2181 | return selectedObject.vehicle |
2182 | end |
2183 | return nil |
2184 | end |
getSellPrice
DescriptionGet sell priceDefinition
getSellPrice()Return Values
float | sellPrice | sell price |
1655 | function Vehicle:getSellPrice() |
1656 | local priceMultiplier = 0.75 |
1657 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
1658 | local maxVehicleAge = storeItem.lifetime |
1659 | |
1660 | if maxVehicleAge ~= nil and maxVehicleAge ~= 0 then |
1661 | local ageMultiplier = 0.5 * math.min(self.age/maxVehicleAge, 1) |
1662 | local operatingTime = self.operatingTime / (1000*60*60) |
1663 | local operatingTimeMultiplier = 0.5 * math.min(operatingTime / (maxVehicleAge*EconomyManager.LIFETIME_OPERATINGTIME_RATIO), 1) |
1664 | priceMultiplier = priceMultiplier * math.exp(-3.5 * (ageMultiplier+operatingTimeMultiplier)) |
1665 | end |
1666 | |
1667 | return math.max(math.floor(self:getPrice() * math.max(priceMultiplier, 0.05)) - self:getRepairPrice(true), 0) |
1668 | end |
getSpecValueCombinations
DescriptionDefinitiongetSpecValueCombinations()Code
3081 | function Vehicle.getSpecValueCombinations(storeItem, realItem) |
3082 | return storeItem.specs.combination |
3083 | end |
getSpecValueDailyUpkeep
DescriptionDefinitiongetSpecValueDailyUpkeep()Code
3017 | function Vehicle.getSpecValueDailyUpkeep(storeItem, realItem) |
3018 | local dailyUpkeep = storeItem.dailyUpkeep |
3019 | if realItem ~= nil and realItem.getDailyUpkeep ~= nil then |
3020 | dailyUpkeep = realItem:getDailyUpkeep() |
3021 | end |
3022 | |
3023 | -- Hide when no upkeep |
3024 | if dailyUpkeep == 0 then |
3025 | return nil |
3026 | end |
3027 | |
3028 | return string.format(g_i18n:getText("shop_maintenanceValue"), g_i18n:formatMoney(dailyUpkeep, 2)) |
3029 | end |
getSpecValueOperatingTime
DescriptionDefinitiongetSpecValueOperatingTime()Code
3033 | function Vehicle.getSpecValueOperatingTime(storeItem, realItem) |
3034 | if realItem ~= nil and realItem.operatingTime ~= nil then |
3035 | local minutes = realItem.operatingTime / (1000 * 60) |
3036 | local hours = math.floor(minutes / 60) |
3037 | minutes = math.floor((minutes - hours * 60) / 6) |
3038 | return string.format(g_i18n:getText("shop_operatingTime"), hours, minutes) |
3039 | end |
3040 | return nil |
3041 | end |
getSpecValueSlots
DescriptionDefinitiongetSpecValueSlots()Code
3087 | function Vehicle.getSpecValueSlots(storeItem, realItem, isGarage) |
3088 | local numOwned = g_currentMission:getNumOfItems(storeItem) |
3089 | local valueText = "" |
3090 | if isGarage then |
3091 | local sellSlotUsage = g_currentMission:getStoreItemSlotUsage(storeItem, numOwned == 1) |
3092 | if sellSlotUsage ~= 0 then |
3093 | valueText = "+"..sellSlotUsage |
3094 | end |
3095 | else |
3096 | local buySlotUsage = g_currentMission:getStoreItemSlotUsage(storeItem, numOwned == 0) |
3097 | if buySlotUsage ~= 0 then |
3098 | valueText = "-"..buySlotUsage |
3099 | end |
3100 | end |
3101 | |
3102 | if valueText ~= "" then |
3103 | return valueText |
3104 | else |
3105 | return nil |
3106 | end |
3107 | end |
getSpecValueSpeedLimit
DescriptionDefinitiongetSpecValueSpeedLimit()Code
3066 | function Vehicle.getSpecValueSpeedLimit(storeItem, realItem) |
3067 | if storeItem.specs.speedLimit ~= nil then |
3068 | return string.format(g_i18n:getText("shop_maxSpeed"), string.format("%1d", g_i18n:getSpeed(storeItem.specs.speedLimit)), g_i18n:getSpeedMeasuringUnit()) |
3069 | end |
3070 | return nil |
3071 | end |
getSpecValueWorkingWidth
DescriptionDefinitiongetSpecValueWorkingWidth()Code
3051 | function Vehicle.getSpecValueWorkingWidth(storeItem, realItem) |
3052 | if storeItem.specs.workingWidth ~= nil then |
3053 | return string.format(g_i18n:getText("shop_workingWidthValue"), g_i18n:formatNumber(storeItem.specs.workingWidth, 1, true)) |
3054 | end |
3055 | return nil |
3056 | end |
getSpeedLimit
DescriptionGet speed limitDefinition
getSpeedLimit(boolean onlyIfWorking)Arguments
boolean | onlyIfWorking | only if working |
float | limit | limit |
boolean | doCheckSpeedLimit | do check speed limit |
2840 | function Vehicle:getSpeedLimit(onlyIfWorking) |
2841 | local limit = math.huge |
2842 | local doCheckSpeedLimit = self:doCheckSpeedLimit() |
2843 | if onlyIfWorking == nil or (onlyIfWorking and doCheckSpeedLimit) then |
2844 | limit = self.speedLimit |
2845 | |
2846 | local damage = self:getVehicleDamage() |
2847 | if damage > 0 then |
2848 | limit = limit * (1 - damage * Vehicle.DAMAGED_SPEEDLIMIT_REDUCTION) |
2849 | end |
2850 | end |
2851 | |
2852 | local attachedImplements |
2853 | if self.getAttachedImplements ~= nil then |
2854 | attachedImplements = self:getAttachedImplements() |
2855 | end |
2856 | if attachedImplements ~= nil then |
2857 | for _, implement in pairs(attachedImplements) do |
2858 | if implement.object ~= nil then |
2859 | local speed, implementDoCheckSpeedLimit = implement.object:getSpeedLimit(onlyIfWorking) |
2860 | if onlyIfWorking == nil or (onlyIfWorking and implementDoCheckSpeedLimit) then |
2861 | limit = math.min(limit, speed) |
2862 | end |
2863 | doCheckSpeedLimit = doCheckSpeedLimit or implementDoCheckSpeedLimit |
2864 | end |
2865 | end |
2866 | end |
2867 | return limit, doCheckSpeedLimit |
2868 | end |
getTailwaterDepth
DescriptionDefinitiongetTailwaterDepth()Code
1831 | function Vehicle:getTailwaterDepth() |
1832 | local tailwaterDepth = 0 |
1833 | for _,component in pairs(self.components) do |
1834 | local _,yt,_ = getWorldTranslation(component.node) |
1835 | tailwaterDepth = math.max(0, g_currentMission.waterY - yt) |
1836 | end |
1837 | return tailwaterDepth |
1838 | 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 |
2235 | function Vehicle:getTotalMass(onlyGivenVehicle) |
2236 | if self.isServer then |
2237 | local mass = 0 |
2238 | |
2239 | for _, component in ipairs(self.components) do |
2240 | mass = mass + component.mass |
2241 | end |
2242 | |
2243 | return mass |
2244 | end |
2245 | |
2246 | return 0 |
2247 | end |
getVehicleDamage
DescriptionDefinitiongetVehicleDamage()Code
1853 | function Vehicle:getVehicleDamage() |
1854 | return 0 |
1855 | end |
getWorkLoad
DescriptionDefinitiongetWorkLoad()Code
2740 | function Vehicle:getWorkLoad() |
2741 | return 0, 0 |
2742 | end |
hasInputConflictWithSelection
DescriptionDefinitionhasInputConflictWithSelection()Code
2188 | function Vehicle:hasInputConflictWithSelection(inputs) |
2189 | printCallstack() |
2190 | g_logManager:xmlWarning(self.configFileName, "Vehicle:hasInputConflictWithSelection() is deprecated!") |
2191 | return false |
2192 | end |
init
DescriptionDefinitioninit()Code
192 | function Vehicle.init() |
193 | g_configurationManager:addConfigurationType("baseColor", g_i18n:getText("configuration_baseColor"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_COLOR) |
194 | g_configurationManager:addConfigurationType("design", g_i18n:getText("configuration_design"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_MULTIOPTION) |
195 | g_configurationManager:addConfigurationType("designColor", g_i18n:getText("configuration_designColor"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_COLOR) |
196 | g_configurationManager:addConfigurationType("vehicleType", g_i18n:getText("configuration_design"), nil, nil, ConfigurationUtil.getStoreAddtionalConfigData, nil, ConfigurationUtil.SELECTOR_MULTIOPTION) |
197 | |
198 | g_storeManager:addSpecType("age", "shopListAttributeIconLifeTime", nil, Vehicle.getSpecValueAge) |
199 | g_storeManager:addSpecType("operatingTime", "shopListAttributeIconOperatingHours", nil, Vehicle.getSpecValueOperatingTime) |
200 | g_storeManager:addSpecType("dailyUpkeep", "shopListAttributeIconMaintenanceCosts", nil, Vehicle.getSpecValueDailyUpkeep) |
201 | g_storeManager:addSpecType("workingWidth", "shopListAttributeIconWorkingWidth", Vehicle.loadSpecValueWorkingWidth, Vehicle.getSpecValueWorkingWidth) |
202 | g_storeManager:addSpecType("speedLimit", "shopListAttributeIconWorkSpeed", Vehicle.loadSpecValueSpeedLimit, Vehicle.getSpecValueSpeedLimit) |
203 | g_storeManager:addSpecType("combination", "shopListAttributeIconCombinations", Vehicle.loadSpecValueCombinations, Vehicle.getSpecValueCombinations) |
204 | g_storeManager:addSpecType("slots", "shopListAttributeIconSlots", nil, Vehicle.getSpecValueSlots) |
205 | end |
interact
DescriptionDefinitioninteract()Code
2747 | function Vehicle:interact() |
2748 | end |
load
DescriptionDefinitionload()Code
223 | function Vehicle:load(vehicleData, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments) |
224 | local modName, baseDirectory = Utils.getModNameAndBaseDirectory(vehicleData.filename) |
225 | |
226 | self.configFileName = vehicleData.filename |
227 | self.baseDirectory = baseDirectory |
228 | self.customEnvironment = modName |
229 | self.typeName = vehicleData.typeName |
230 | self.isVehicleSaved = Utils.getNoNil(vehicleData.isVehicleSaved, true) |
231 | self.configurations = Utils.getNoNil(vehicleData.configurations, {}) |
232 | self.boughtConfigurations = Utils.getNoNil(vehicleData.boughtConfigurations, {}) |
233 | |
234 | local typeDef = g_vehicleTypeManager:getVehicleTypeByName(self.typeName) |
235 | if typeDef == nil then |
236 | g_logManager:xmlWarning(self.configFileName, "Unable to find vehicleType '%s'", self.typeName) |
237 | self:setLoadingState(BaseMission.VEHICLE_LOAD_ERROR) |
238 | return self.loadingState |
239 | end |
240 | |
241 | self.vehicleType = typeDef |
242 | self.specializations = typeDef.specializations |
243 | self.specializationNames = typeDef.specializationNames |
244 | self.specializationsByName = typeDef.specializationsByName |
245 | self.eventListeners = typeDef.eventListeners |
246 | self.actionEvents = {} |
247 | self.xmlFile = loadXMLFile("TempConfig", vehicleData.filename) |
248 | self.isAddedToPhysics = false |
249 | |
250 | -- pass function pointers from specializations to 'self' |
251 | for funcName, func in pairs(typeDef.functions) do |
252 | self[funcName] = func |
253 | end |
254 | |
255 | local data = {} |
256 | data[1] = {posX=vehicleData.posX, posY=vehicleData.posY, posZ=vehicleData.posZ, yOffset=vehicleData.yOffset, isAbsolute=vehicleData.isAbsolute} |
257 | data[2] = {rotX=vehicleData.rotX, rotY=vehicleData.rotY, rotZ=vehicleData.rotZ} |
258 | data[3] = vehicleData.isVehicleSaved |
259 | data[4] = vehicleData.propertyState |
260 | data[5] = vehicleData.ownerFarmId |
261 | data[6] = vehicleData.price |
262 | data[7] = vehicleData.savegame |
263 | data[8] = asyncCallbackFunction |
264 | data[9] = asyncCallbackObject |
265 | data[10] = asyncCallbackArguments |
266 | data[11] = vehicleData.componentPositions |
267 | |
268 | -- check if one of the configurations is not set - e.g. if new configurations are available but not in savegame |
269 | local item = g_storeManager:getItemByXMLFilename(self.configFileName) |
270 | if item ~= nil and item.configurations ~= nil then |
271 | for configName, _ in pairs(item.configurations) do |
272 | local defaultConfigId = StoreItemUtil.getDefaultConfigId(item, configName) |
273 | if self.configurations[configName] == nil then |
274 | ConfigurationUtil.setConfiguration(self, configName, defaultConfigId) |
275 | end |
276 | -- base configuration is always included |
277 | ConfigurationUtil.addBoughtConfiguration(self, configName, defaultConfigId) |
278 | end |
279 | -- check if currently used configurations are still available |
280 | for configName, value in pairs(self.configurations) do |
281 | if item.configurations[configName] == nil then |
282 | g_logManager:xmlWarning(self.configFileName, "Configurations are not present anymore. Ignoring this configuration (%s)!", configName) |
283 | self.configurations[configName] = nil |
284 | self.boughtConfigurations[configName] = nil |
285 | else |
286 | local defaultConfigId = StoreItemUtil.getDefaultConfigId(item, configName) |
287 | if #item.configurations[configName] < value then |
288 | g_logManager:xmlWarning(self.configFileName, "Configuration with index '%d' is not present anymore. Using default configuration instead!", value) |
289 | |
290 | if self.boughtConfigurations[configName] ~= nil then |
291 | self.boughtConfigurations[configName][value] = nil |
292 | if next(self.boughtConfigurations[configName]) == nil then |
293 | self.boughtConfigurations[configName] = nil |
294 | end |
295 | end |
296 | ConfigurationUtil.setConfiguration(self, configName, defaultConfigId) |
297 | else |
298 | ConfigurationUtil.addBoughtConfiguration(self, configName, value) |
299 | end |
300 | end |
301 | end |
302 | end |
303 | |
304 | for i=1, table.getn(self.specializations) do |
305 | local specEntryName = "spec_" .. self.specializationNames[i] |
306 | if self[specEntryName] ~= nil then |
307 | g_logManager:xmlError(self.configFileName, "The vehicle specialization '%s' could not be added because variable '%s' already exists!", self.specializationNames[i], specEntryName) |
308 | self:setLoadingState(BaseMission.VEHICLE_LOAD_ERROR) |
309 | end |
310 | |
311 | local env = {} |
312 | setmetatable(env, { __index = self } ) |
313 | env.actionEvents = {} |
314 | self[specEntryName] = env |
315 | end |
316 | |
317 | SpecializationUtil.raiseEvent(self, "onPreLoad", vehicleData.savegame) |
318 | if self.loadingState ~= BaseMission.VEHICLE_LOAD_OK then |
319 | g_logManager:xmlError(self.configFileName, "Vehicle pre-loading failed!") |
320 | if asyncCallbackFunction ~= nil then |
321 | asyncCallbackFunction(asyncCallbackObject, nil, self.loadingState, asyncCallbackArguments) |
322 | end |
323 | |
324 | return self.loadingState |
325 | end |
326 | |
327 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.filename", "vehicle.base.filename") --FS17 to FS19 |
328 | |
329 | self.i3dFilename = getXMLString(self.xmlFile, "vehicle.base.filename") |
330 | |
331 | if asyncCallbackFunction ~= nil then |
332 | g_i3DManager:loadSharedI3DFile(self.i3dFilename, baseDirectory, true, false, true, self.loadFinished, self, data) |
333 | else |
334 | local i3dNode = g_i3DManager:loadSharedI3DFile(self.i3dFilename, baseDirectory, true, false, true) |
335 | return self:loadFinished(i3dNode, data) |
336 | end |
337 | 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 |
2344 | function Vehicle:loadComponentFromXML(component, xmlFile, key, rootPosition, i) |
2345 | if not self.isServer then |
2346 | if getRigidBodyType(component.node) == "Dynamic" then |
2347 | setRigidBodyType(component.node, "Kinematic") |
2348 | end |
2349 | end |
2350 | link(getRootNode(), component.node) |
2351 | if i == 1 then |
2352 | rootPosition[1], rootPosition[2], rootPosition[3] = getTranslation(component.node) |
2353 | if rootPosition[2] ~= 0 then |
2354 | g_logManager:xmlWarning(self.configFileName, "Y-Translation of component 1 (node 0>) has to be 0. Current value is: %.5f", rootPosition[2]) |
2355 | end |
2356 | end |
2357 | |
2358 | if getRigidBodyType(component.node) == "Static" then |
2359 | component.isStatic = true |
2360 | elseif getRigidBodyType(component.node) == "Kinematic" then |
2361 | component.isKinematic = true |
2362 | elseif getRigidBodyType(component.node) == "Dynamic" then |
2363 | component.isDynamic = true |
2364 | end |
2365 | |
2366 | -- the position of the first component is the zero |
2367 | translate(component.node, -rootPosition[1], -rootPosition[2], -rootPosition[3]) |
2368 | local x,y,z = getTranslation(component.node) |
2369 | local rx,ry,rz = getRotation(component.node) |
2370 | component.originalTranslation = {x,y,z} |
2371 | component.originalRotation = {rx,ry,rz} |
2372 | |
2373 | component.sentTranslation = {x,y,z} |
2374 | component.sentRotation = {rx,ry,rz} |
2375 | |
2376 | component.defaultMass = nil |
2377 | component.mass = nil |
2378 | |
2379 | local mass = getXMLFloat(xmlFile, key.."#mass") |
2380 | if mass ~= nil then |
2381 | if mass < 10 then |
2382 | g_logManager:xmlDevWarning(self.configFileName, "Mass is lower than 10kg for '%s'. Mass unit is kilogramms. Is this correct?", key) |
2383 | end |
2384 | if component.isDynamic then |
2385 | setMass(component.node, mass/1000) |
2386 | end |
2387 | |
2388 | component.defaultMass = mass/1000 |
2389 | component.mass = component.defaultMass |
2390 | component.lastMass = component.mass |
2391 | else |
2392 | g_logManager:xmlWarning(self.configFileName, "Missing 'mass' for '%s'. Using default mass 500kg instead!", key) |
2393 | component.defaultMass = 0.5 |
2394 | component.mass = 0.5 |
2395 | component.lastMass = component.mass |
2396 | end |
2397 | |
2398 | local comStr = getXMLString(xmlFile, key .. "#centerOfMass"); |
2399 | if comStr ~= nil then |
2400 | local com = StringUtil.getVectorNFromString(comStr, 3) |
2401 | if com ~= nil then |
2402 | setCenterOfMass(component.node, com[1], com[2], com[3]) |
2403 | else |
2404 | g_logManager:xmlWarning(self.configFileName, "Invalid center of mass given for '%s'. Ignoring this definition", key) |
2405 | end |
2406 | end |
2407 | local count = getXMLInt(xmlFile, key .. "#solverIterationCount") |
2408 | if count ~= nil then |
2409 | setSolverIterationCount(component.node, count) |
2410 | component.solverIterationCount = count |
2411 | end |
2412 | component.motorized = getXMLBool(xmlFile, key .. "#motorized") -- Note: motorized is nil if not set in the xml, and can be set by the wheels |
2413 | self.vehicleNodes[component.node] = {component=component} |
2414 | local clipDistance = getClipDistance(component.node) |
2415 | if clipDistance >= 1000000 and getVisibility(component.node) then |
2416 | local defaultClipdistance = 300 |
2417 | g_logManager:xmlWarning(self.configFileName, "No clipdistance is set to component node '%s' (%s>). Set default clipdistance '%d'", getName(component.node), i-1, defaultClipdistance) |
2418 | setClipDistance(component.node, defaultClipdistance) |
2419 | end |
2420 | |
2421 | component.collideWithAttachables = Utils.getNoNil(getXMLBool(xmlFile, key.."#collideWithAttachables"), false) |
2422 | |
2423 | if getRigidBodyType(component.node) ~= "NoRigidBody" then |
2424 | if getLinearDamping(component.node) > 0.01 then |
2425 | g_logManager:xmlDevWarning(self.configFileName, "Non-zero linear damping (%.4f) for component node '%s' (%s>). Is this correct?", getLinearDamping(component.node), getName(component.node), i-1) |
2426 | elseif getAngularDamping(component.node) > 0.05 then |
2427 | g_logManager:xmlDevWarning(self.configFileName, "Large angular damping (%.4f) for component node '%s' (%s>). Is this correct?", getAngularDamping(component.node), getName(component.node), i-1) |
2428 | elseif getAngularDamping(component.node) < 0.0001 then |
2429 | g_logManager:xmlDevWarning(self.configFileName, "Zero damping for component node '%s' (%s>). Is this correct?", getName(component.node), i-1) |
2430 | end |
2431 | end |
2432 | |
2433 | local name = getName(component.node) |
2434 | if not StringUtil.endsWith(name, "component"..i) then |
2435 | g_logManager:xmlDevWarning(self.configFileName, "Name of component '%d' ('%s') does not correpond with the component naming convention! (vehicleName_componentName_component%d)", i, name, i) |
2436 | end |
2437 | |
2438 | return true |
2439 | 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 |
2451 | function Vehicle:loadComponentJointFromXML(jointDesc, xmlFile, key, componentJointI, jointNode, index1, index2) |
2452 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, key .. "#indexActor1", key .. "#nodeActor1") --FS17 to FS19 |
2453 | |
2454 | jointDesc.componentIndices = {index1, index2} |
2455 | jointDesc.jointNode = jointNode |
2456 | jointDesc.jointNodeActor1 = Utils.getNoNil(I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#nodeActor1"), self.i3dMappings), jointNode) |
2457 | if self.isServer then |
2458 | if self.components[index1] == nil or self.components[index2] == nil then |
2459 | g_logManager:xmlWarning(self.configFileName, "Invalid component indices (component1: %d, component2: %d) for component joint %d. Indices start with 1!", index1, index2, componentJointI) |
2460 | return false |
2461 | end |
2462 | |
2463 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotLimit")) |
2464 | local rotLimits = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) } |
2465 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transLimit")) |
2466 | local transLimits = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
2467 | jointDesc.rotLimit = rotLimits |
2468 | jointDesc.transLimit = transLimits |
2469 | |
2470 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotMinLimit")) |
2471 | local rotMinLimits = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } |
2472 | |
2473 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transMinLimit")) |
2474 | local transMinLimits = { x,y,z } |
2475 | |
2476 | for i=1,3 do |
2477 | if rotMinLimits[i] == nil then |
2478 | if rotLimits[i] >= 0 then |
2479 | rotMinLimits[i] = -rotLimits[i] |
2480 | else |
2481 | rotMinLimits[i] = rotLimits[i]+1 |
2482 | end |
2483 | end |
2484 | if transMinLimits[i] == nil then |
2485 | if transLimits[i] >= 0 then |
2486 | transMinLimits[i] = -transLimits[i] |
2487 | else |
2488 | transMinLimits[i] = transLimits[i]+1 |
2489 | end |
2490 | end |
2491 | end |
2492 | |
2493 | jointDesc.jointLocalPoses = {} |
2494 | local trans = {localToLocal(jointDesc.jointNode, self.components[index1].node, 0, 0, 0)} |
2495 | local rot = {localRotationToLocal(jointDesc.jointNode, self.components[index1].node, 0, 0, 0)} |
2496 | jointDesc.jointLocalPoses[1] = {trans=trans, rot=rot} |
2497 | |
2498 | local trans = {localToLocal(jointDesc.jointNodeActor1, self.components[index2].node, 0, 0, 0)} |
2499 | local rot = {localRotationToLocal(jointDesc.jointNodeActor1, self.components[index2].node, 0, 0, 0)} |
2500 | jointDesc.jointLocalPoses[2] = {trans=trans, rot=rot} |
2501 | |
2502 | jointDesc.rotMinLimit = rotMinLimits |
2503 | jointDesc.transMinLimit = transMinLimits |
2504 | |
2505 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotLimitSpring")) |
2506 | local rotLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
2507 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotLimitDamping")) |
2508 | local rotLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
2509 | jointDesc.rotLimitSpring = rotLimitSpring |
2510 | jointDesc.rotLimitDamping = rotLimitDamping |
2511 | |
2512 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotLimitForceLimit")) |
2513 | local rotLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) } |
2514 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transLimitForceLimit")) |
2515 | local transLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) } |
2516 | jointDesc.rotLimitForceLimit = rotLimitForceLimit |
2517 | jointDesc.transLimitForceLimit = transLimitForceLimit |
2518 | |
2519 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transLimitSpring")) |
2520 | local transLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
2521 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transLimitDamping")) |
2522 | local transLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
2523 | jointDesc.transLimitSpring = transLimitSpring |
2524 | jointDesc.transLimitDamping = transLimitDamping |
2525 | |
2526 | jointDesc.zRotationXOffset = 0 |
2527 | local zRotationNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#zRotationNode"), self.i3dMappings) |
2528 | if zRotationNode ~= nil then |
2529 | jointDesc.zRotationXOffset,_,_ = localToLocal(zRotationNode, jointNode, 0,0,0) |
2530 | end |
2531 | |
2532 | jointDesc.isBreakable = Utils.getNoNil(getXMLBool(xmlFile, key.."#breakable"), false) |
2533 | if jointDesc.isBreakable then |
2534 | jointDesc.breakForce = Utils.getNoNil(getXMLFloat(xmlFile, key.."#breakForce"), 10) |
2535 | jointDesc.breakTorque = Utils.getNoNil(getXMLFloat(xmlFile, key.."#breakTorque"), 10) |
2536 | end |
2537 | jointDesc.enableCollision = Utils.getNoNil(getXMLBool(xmlFile, key.."#enableCollision"), false) |
2538 | |
2539 | -- Rotational drive |
2540 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#maxRotDriveForce")) |
2541 | local maxRotDriveForce = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
2542 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotDriveVelocity")) |
2543 | local rotDriveVelocity = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } -- convert from deg/s to rad/s |
2544 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotDriveRotation")) |
2545 | local rotDriveRotation = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } -- convert from deg to rad |
2546 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotDriveSpring")) |
2547 | local rotDriveSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
2548 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotDriveDamping")) |
2549 | local rotDriveDamping = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
2550 | |
2551 | jointDesc.rotDriveVelocity = rotDriveVelocity |
2552 | jointDesc.rotDriveRotation = rotDriveRotation |
2553 | jointDesc.rotDriveSpring = rotDriveSpring |
2554 | jointDesc.rotDriveDamping = rotDriveDamping |
2555 | jointDesc.maxRotDriveForce = maxRotDriveForce |
2556 | |
2557 | -- Translational drive |
2558 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transDriveVelocity")) |
2559 | local transDriveVelocity = { x,y,z } |
2560 | |
2561 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transDrivePosition")) |
2562 | local transDrivePosition = { x,y,z } |
2563 | |
2564 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transDriveSpring")) |
2565 | local transDriveSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
2566 | |
2567 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transDriveDamping")) |
2568 | local transDriveDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
2569 | |
2570 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#maxTransDriveForce")) |
2571 | local maxTransDriveForce = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
2572 | |
2573 | jointDesc.transDriveVelocity = transDriveVelocity |
2574 | jointDesc.transDrivePosition = transDrivePosition |
2575 | jointDesc.transDriveSpring = transDriveSpring |
2576 | jointDesc.transDriveDamping = transDriveDamping |
2577 | jointDesc.maxTransDriveForce = maxTransDriveForce |
2578 | |
2579 | jointDesc.jointIndex = 0 |
2580 | end |
2581 | |
2582 | return true |
2583 | end |
loadFinished
DescriptionDefinitionloadFinished()Code
341 | function Vehicle:loadFinished(i3dNode, arguments) |
342 | self:setLoadingState(BaseMission.VEHICLE_LOAD_OK) |
343 | |
344 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.forcedMapHotspotType", "vehicle.base.mapHotspot#type") --FS17 to FS19 |
345 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.speedLimit#value", "vehicle.base.speedLimit#value") --FS17 to FS19 |
346 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.steeringAxleNode#index", "vehicle.base.steeringAxle#node") --FS17 to FS19 |
347 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.size#width", "vehicle.base.size#width") --FS17 to FS19 |
348 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.size#length", "vehicle.base.size#length") --FS17 to FS19 |
349 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.size#widthOffset", "vehicle.base.size#widthOffset") --FS17 to FS19 |
350 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.size#lengthOffset", "vehicle.base.size#lengthOffset") --FS17 to FS19 |
351 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.typeDesc", "vehicle.base.typeDesc") --FS17 to FS19 |
352 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.components", "vehicle.base.components") --FS17 to FS19 |
353 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.components.component", "vehicle.base.components.component") --FS17 to FS19 |
354 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.base.components.component1", "vehicle.base.components.component") --FS17 to FS19 |
355 | |
356 | local position, rotation, isSave, propertyState, ownerFarmId, price, savegame, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments, componentPositions = unpack(arguments) |
357 | |
358 | if i3dNode == 0 then |
359 | self:setLoadingState(BaseMission.VEHICLE_LOAD_ERROR) |
360 | if asyncCallbackFunction ~= nil then |
361 | asyncCallbackFunction(asyncCallbackObject, nil, self.loadingState, asyncCallbackArguments) |
362 | end |
363 | return self.loadingState |
364 | end |
365 | |
366 | if savegame ~= nil then |
367 | local i = 0 |
368 | while true do |
369 | local key = string.format(savegame.key..".boughtConfiguration(%d)", i) |
370 | if not hasXMLProperty(savegame.xmlFile, key) then |
371 | break |
372 | end |
373 | local name = getXMLString(savegame.xmlFile, key.."#name") |
374 | local id = getXMLInt(savegame.xmlFile, key.."#id") |
375 | ConfigurationUtil.addBoughtConfiguration(self, name, id) |
376 | i = i + 1 |
377 | end |
378 | |
379 | self.tourId = nil |
380 | local tourId = getXMLString(savegame.xmlFile, savegame.key.."#tourId") |
381 | if tourId ~= nil then |
382 | self.tourId = tourId |
383 | if g_currentMission ~= nil then |
384 | g_currentMission.tourVehicles[self.tourId] = self |
385 | end |
386 | end |
387 | end |
388 | |
389 | self.age = 0 |
390 | self.propertyState = propertyState |
391 | self:setOwnerFarmId(ownerFarmId, true) |
392 | |
393 | if savegame ~= nil then |
394 | -- Load this early: it used by the vehicle load functions already |
395 | local farmId = Utils.getNoNil(getXMLInt(savegame.xmlFile, savegame.key .. "#farmId"), AccessHandler.EVERYONE) |
396 | if g_farmManager.spFarmWasMerged and farmId ~= AccessHandler.EVERYONE then |
397 | farmId = FarmManager.SINGLEPLAYER_FARM_ID |
398 | end |
399 | self:setOwnerFarmId(farmId, true) |
400 | end |
401 | |
402 | self.price = price |
403 | if self.price == 0 or self.price == nil then |
404 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
405 | self.price = StoreItemUtil.getDefaultPrice(storeItem, self.configurations) |
406 | end |
407 | self.typeDesc = XMLUtil.getXMLI18NValue(self.xmlFile, "vehicle.base.typeDesc", getXMLString, "", "TypeDescription", self.customEnvironment, true) |
408 | self.synchronizePosition = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.base.synchronizePosition"), true) |
409 | self.highPrecisionPositionSynchronization = false |
410 | self.supportsPickUp = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.base.supportsPickUp"), true) |
411 | self.canBeReset = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.base.canBeReset"), true) |
412 | |
413 | self.rootNode = getChildAt(i3dNode, 0) |
414 | self.serverMass = 0 |
415 | self.isMassDirty = false |
416 | |
417 | |
418 | self.components = {} |
419 | self.vehicleNodes = {} |
420 | |
421 | local numComponents = getNumOfChildren(i3dNode) |
422 | local rootPosition = {0,0,0} |
423 | local i = 1 |
424 | |
425 | numComponents = getXMLInt(self.xmlFile, "vehicle.base.components#numComponents") or numComponents |
426 | |
427 | while true do |
428 | local namei = string.format("vehicle.base.components.component(%d)", i - 1) |
429 | if not hasXMLProperty(self.xmlFile, namei) then |
430 | break |
431 | end |
432 | if i > numComponents then |
433 | g_logManager:xmlWarning(self.configFileName, "Invalid components count. I3D file has '%d' components, but tried to load component no. '%d'!", numComponents, i+1) |
434 | break |
435 | end |
436 | |
437 | local component = {node = getChildAt(i3dNode, 0)} |
438 | |
439 | if self:loadComponentFromXML(component, self.xmlFile, namei, rootPosition, i) then |
440 | local x,y,z = getWorldTranslation(component.node) |
441 | local qx,qy,qz,qw = getWorldQuaternion(component.node) |
442 | component.networkInterpolators = {} |
443 | component.networkInterpolators.position = InterpolatorPosition:new(x, y, z) |
444 | component.networkInterpolators.quaternion = InterpolatorQuaternion:new(qx, qy, qz, qw) |
445 | table.insert(self.components, component) |
446 | end |
447 | i = i + 1 |
448 | end |
449 | delete(i3dNode) |
450 | |
451 | self.numComponents = table.getn(self.components) |
452 | if numComponents ~= self.numComponents then |
453 | g_logManager:xmlWarning(self.configFileName, "I3D file offers '%d' objects, but '%d' components have been loaded!", numComponents, self.numComponents) |
454 | end |
455 | |
456 | if self.numComponents == 0 then |
457 | g_logManager:xmlWarning(self.configFileName, "No components defined for vehicle!") |
458 | |
459 | self:setLoadingState(BaseMission.VEHICLE_LOAD_ERROR) |
460 | if asyncCallbackFunction ~= nil then |
461 | asyncCallbackFunction(asyncCallbackObject, nil, self.loadingState, asyncCallbackArguments) |
462 | end |
463 | return self.loadingState |
464 | end |
465 | |
466 | -- load i3d mappings |
467 | self.i3dMappings = {} |
468 | local i = 0 |
469 | while true do |
470 | local key = string.format("vehicle.i3dMappings.i3dMapping(%d)", i) |
471 | if not hasXMLProperty(self.xmlFile, key) then |
472 | break |
473 | end |
474 | local id = getXMLString(self.xmlFile, key.."#id") |
475 | local node = getXMLString(self.xmlFile, key.."#node") |
476 | if id ~= nil and node ~= nil then |
477 | self.i3dMappings[id] = node |
478 | end |
479 | i = i + 1 |
480 | end |
481 | |
482 | -- need to be defined in vehicle because all vehicles can define a steering axle ref node |
483 | self.steeringAxleNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.base.steeringAxle#node"), self.i3dMappings) |
484 | if self.steeringAxleNode == nil then |
485 | self.steeringAxleNode = self.components[1].node |
486 | end |
487 | |
488 | self:loadSchemaOverlay(self.xmlFile) |
489 | |
490 | -- load component joints |
491 | self.componentJoints = {} |
492 | |
493 | local componentJointI = 0 |
494 | while true do |
495 | local key = string.format("vehicle.base.components.joint(%d)", componentJointI) |
496 | local index1 = getXMLInt(self.xmlFile, key.."#component1") |
497 | local index2 = getXMLInt(self.xmlFile, key.."#component2") |
498 | |
499 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, key .. "#index", key .. "#node") --FS17 to FS19 |
500 | |
501 | local jointIndexStr = getXMLString(self.xmlFile, key.."#node") |
502 | if index1 == nil or index2 == nil or jointIndexStr == nil then |
503 | break |
504 | end |
505 | local jointNode = I3DUtil.indexToObject(self.components, jointIndexStr, self.i3dMappings) |
506 | if jointNode ~= nil and jointNode ~= 0 then |
507 | local jointDesc = {} |
508 | if self:loadComponentJointFromXML(jointDesc, self.xmlFile, key, componentJointI, jointNode, index1, index2) then |
509 | table.insert(self.componentJoints, jointDesc) |
510 | end |
511 | end |
512 | componentJointI = componentJointI +1 |
513 | end |
514 | |
515 | local collisionPairI = 0 |
516 | self.collisionPairs = {} |
517 | while true do |
518 | local key = string.format("vehicle.base.components.collisionPair(%d)", collisionPairI) |
519 | if not hasXMLProperty(self.xmlFile, key) then |
520 | break |
521 | end |
522 | local enabled = getXMLBool(self.xmlFile, key.."#enabled") |
523 | local index1 = getXMLInt(self.xmlFile, key.."#component1") |
524 | local index2 = getXMLInt(self.xmlFile, key.."#component2") |
525 | if index1 ~= nil and index2 ~= nil and enabled ~= nil then |
526 | local component1 = self.components[index1] |
527 | local component2 = self.components[index2] |
528 | if component1 ~= nil and component2 ~= nil then |
529 | if not enabled then |
530 | table.insert(self.collisionPairs, {component1=component1, component2=component2, enabled=enabled}) |
531 | end |
532 | else |
533 | g_logManager:xmlWarning(self.configFileName, "Failed to load collision pair '%s'. Unknown component indices. Indices start with 1.", key) |
534 | end |
535 | end |
536 | collisionPairI = collisionPairI +1 |
537 | end |
538 | |
539 | self.supportsRadio = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.base.supportsRadio"), true) |
540 | self.allowsInput = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.base.input#allowed"), true) |
541 | self.sizeWidth, self.sizeLength, self.widthOffset, self.lengthOffset = StoreItemUtil.getSizeValuesFromXML(self.xmlFile, "vehicle", 0, self.configurations) |
542 | self.showTailwaterDepthWarning = false |
543 | self.thresholdTailwaterDepthWarning = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.base.tailwaterDepth#warning"), 1.0) |
544 | self.thresholdTailwaterDepth = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.base.tailwaterDepth#threshold"), 2.5) |
545 | self.networkTimeInterpolator = InterpolationTime:new(1.2) |
546 | self.movingDirection = 0 |
547 | self.requiredDriveMode = 1 |
548 | self.rotatedTime = 0 |
549 | self.isBroken = false |
550 | self.forceIsActive = false |
551 | self.operatingTime = 0 |
552 | self.firstTimeRun = false |
553 | self.lastPosition = nil |
554 | self.lastSpeed = 0 |
555 | self.lastSpeedReal = 0 |
556 | self.lastSignedSpeed = 0 |
557 | self.lastSignedSpeedReal = 0 |
558 | self.lastMovedDistance = 0 |
559 | self.lastSpeedAcceleration = 0 |
560 | self.lastMoveTime = -10000 |
561 | self.operatingTime = 0 |
562 | self.isSelectable = true |
563 | |
564 | |
565 | self.selectionObjects = {} |
566 | self.currentSelection = {object=nil, index=0, subIndex=1} |
567 | self.selectionObject = {index=0, isSelected=false, vehicle=self, subSelections={}} |
568 | |
569 | self.registeredActionEvents = {} |
570 | self.actionEventUpdateRequested = false |
571 | self.vehicleDirtyFlag = self:getNextDirtyFlag() |
572 | |
573 | if g_currentMission ~= nil and g_currentMission.environment ~= nil then |
574 | g_currentMission.environment:addDayChangeListener(self) |
575 | end |
576 | |
577 | -- load optional forcedMapHotspotType |
578 | self.forcedMapHotspotType = nil |
579 | local forcedMapHotspotType = getXMLString(self.xmlFile, "vehicle.base.mapHotspot#type") |
580 | if forcedMapHotspotType ~= nil then |
581 | if forcedMapHotspotType == "Tool" then |
582 | self.forcedMapHotspotType = MapHotspot.CATEGORY_VEHICLE_TOOL |
583 | elseif forcedMapHotspotType == "Trailer" then |
584 | self.forcedMapHotspotType = MapHotspot.CATEGORY_VEHICLE_TRAILER |
585 | elseif forcedMapHotspotType == "Combine" then |
586 | self.forcedMapHotspotType = MapHotspot.CATEGORY_VEHICLE_COMBINE |
587 | elseif forcedMapHotspotType == "Steerable" then |
588 | self.forcedMapHotspotType = MapHotspot.CATEGORY_VEHICLE_STEERABLE |
589 | else |
590 | g_logManager:xmlWarning(self.configFileName, "Unsupported forcedMapHotspotType '%s'!", forcedMapHotspotType) |
591 | end |
592 | end |
593 | |
594 | local speedLimit = math.huge |
595 | for i=1, table.getn(self.specializations) do |
596 | if self.specializations[i].getDefaultSpeedLimit ~= nil then |
597 | local limit = self.specializations[i].getDefaultSpeedLimit(self) |
598 | speedLimit = math.min(limit, speedLimit) |
599 | end |
600 | end |
601 | |
602 | self.checkSpeedLimit = speedLimit == math.huge |
603 | self.speedLimit = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.base.speedLimit#value"), speedLimit) |
604 | |
605 | -- |
606 | local objectChanges = {} |
607 | ObjectChangeUtil.loadObjectChangeFromXML(self.xmlFile, "vehicle.base.objectChanges", objectChanges, self.components, self) |
608 | ObjectChangeUtil.setObjectChanges(objectChanges, true) |
609 | |
610 | if self.configurations["vehicleType"] ~= nil then |
611 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.vehicleTypeConfigurations.vehicleTypeConfiguration", self.configurations["vehicleType"], self.components, self) |
612 | end |
613 | |
614 | SpecializationUtil.raiseEvent(self, "onLoad", savegame) |
615 | if self.loadingState ~= BaseMission.VEHICLE_LOAD_OK then |
616 | g_logManager:xmlError(self.configFileName, "Vehicle loading failed!") |
617 | if asyncCallbackFunction ~= nil then |
618 | asyncCallbackFunction(asyncCallbackObject, nil, self.loadingState, asyncCallbackArguments) |
619 | end |
620 | |
621 | return self.loadingState |
622 | end |
623 | |
624 | -- apply design |
625 | if self.configurations["design"] ~= nil then |
626 | ConfigurationUtil.applyDesign(self, self.xmlFile, self.configurations["design"]) |
627 | end |
628 | |
629 | -- do coloring |
630 | if self.configurations["baseColor"] ~= nil then |
631 | ConfigurationUtil.setColor(self, self.xmlFile, "baseColor", self.configurations["baseColor"]) |
632 | end |
633 | |
634 | -- do coloring |
635 | if self.configurations["designColor"] ~= nil then |
636 | ConfigurationUtil.setColor(self, self.xmlFile, "designColor", self.configurations["designColor"]) |
637 | end |
638 | |
639 | |
640 | -- move all components that are joint to other components to the joint node, so all specializations can move them in there postLoad |
641 | if self.isServer then |
642 | for _, jointDesc in pairs(self.componentJoints) do |
643 | local component2 = self.components[jointDesc.componentIndices[2]].node |
644 | local jointNode = jointDesc.jointNode |
645 | |
646 | if self:getParentComponent(jointNode) == component2 then |
647 | jointNode = jointDesc.jointNodeActor1 |
648 | end |
649 | |
650 | if self:getParentComponent(jointNode) ~= component2 then |
651 | setTranslation(component2, localToLocal(component2, jointNode, 0, 0, 0)) |
652 | setRotation(component2, localRotationToLocal(component2, jointNode, 0, 0, 0)) |
653 | link(jointNode, component2) |
654 | end |
655 | end |
656 | end |
657 | |
658 | SpecializationUtil.raiseEvent(self, "onPostLoad", savegame) |
659 | if self.loadingState ~= BaseMission.VEHICLE_LOAD_OK then |
660 | g_logManager:xmlError(self.configFileName, "Vehicle post-loading failed!") |
661 | if asyncCallbackFunction ~= nil then |
662 | asyncCallbackFunction(asyncCallbackObject, nil, self.loadingState, asyncCallbackArguments) |
663 | end |
664 | |
665 | return self.loadingState |
666 | end |
667 | |
668 | -- 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 |
669 | if self.isServer then |
670 | for _, jointDesc in pairs(self.componentJoints) do |
671 | local component2 = self.components[jointDesc.componentIndices[2]] |
672 | local jointNode = jointDesc.jointNode |
673 | |
674 | if self:getParentComponent(jointNode) == component2.node then |
675 | jointNode = jointDesc.jointNodeActor1 |
676 | end |
677 | |
678 | if self:getParentComponent(jointNode) ~= component2.node then |
679 | local ox, oy, oz = 0, 0, 0 |
680 | if jointDesc.jointNodeActor1 ~= jointDesc.jointNode then |
681 | local x1, y1, z1 = localToLocal(jointDesc.jointNode, component2.node, 0, 0, 0) |
682 | local x2, y2, z2 = localToLocal(jointDesc.jointNodeActor1, component2.node, 0, 0, 0) |
683 | ox, oy, oz = x1-x2, y1-y2, z1-z2 |
684 | end |
685 | |
686 | local x, y, z = localToWorld(component2.node, ox, oy, oz) |
687 | local rx, ry, rz = localRotationToWorld(component2.node, 0, 0, 0) |
688 | |
689 | link(getRootNode(), component2.node) |
690 | setWorldTranslation(component2.node, x, y, z) |
691 | setWorldRotation(component2.node, rx, ry, rz) |
692 | |
693 | component2.originalTranslation = {x, y, z} |
694 | component2.originalRotation = {rx, ry, rz} |
695 | |
696 | component2.sentTranslation = {x, y, z} |
697 | component2.sentRotation = {rx, ry, rz} |
698 | end |
699 | end |
700 | |
701 | for _, jointDesc in pairs(self.componentJoints) do |
702 | self:setComponentJointFrame(jointDesc, 0) |
703 | self:setComponentJointFrame(jointDesc, 1) |
704 | end |
705 | end |
706 | |
707 | if savegame ~= nil then |
708 | self.age = Utils.getNoNil(getXMLFloat(savegame.xmlFile, savegame.key.."#age"), 0) |
709 | self.price = Utils.getNoNil(getXMLInt(savegame.xmlFile, savegame.key.."#price"), self.price) |
710 | self.propertyState = Utils.getNoNil(getXMLInt(savegame.xmlFile, savegame.key.."#propertyState"), self.propertyState) |
711 | self.activeMissionId = getXMLInt(savegame.xmlFile, savegame.key .. "#activeMissionId") |
712 | |
713 | local operatingTime = Utils.getNoNil(getXMLFloat(savegame.xmlFile, savegame.key .. "#operatingTime"), self.operatingTime) * 1000 |
714 | self:setOperatingTime(operatingTime, true) |
715 | local findPlace = savegame.resetVehicles and not savegame.keepPosition |
716 | if not findPlace then |
717 | local isAbsolute = Utils.getNoNil(getXMLBool(savegame.xmlFile, savegame.key.."#isAbsolute"), false) |
718 | if isAbsolute then |
719 | local componentPosition = {} |
720 | local i = 1 |
721 | while true do |
722 | local componentKey = string.format(savegame.key..".component%d", i) |
723 | if not hasXMLProperty(savegame.xmlFile, componentKey) then |
724 | break |
725 | end |
726 | local x,y,z = StringUtil.getVectorFromString(getXMLString(savegame.xmlFile, componentKey.."#position")) |
727 | local xRot,yRot,zRot = StringUtil.getVectorFromString(getXMLString(savegame.xmlFile, componentKey.."#rotation")) |
728 | if x == nil or y == nil or z == nil or xRot == nil or yRot == nil or zRot == nil then |
729 | findPlace = true |
730 | break |
731 | end |
732 | xRot = math.rad(xRot) |
733 | yRot = math.rad(yRot) |
734 | zRot = math.rad(zRot) |
735 | table.insert(componentPosition, {x=x, y=y, z=z, xRot=xRot, yRot=yRot, zRot=zRot}) |
736 | i = i + 1 |
737 | end |
738 | if #componentPosition == #self.components then |
739 | for i=1, #self.components do |
740 | local p = componentPosition[i] |
741 | self:setWorldPosition(p.x,p.y,p.z, p.xRot,p.yRot,p.zRot, i, true) |
742 | end |
743 | else |
744 | findPlace = true |
745 | g_logManager:xmlWarning(self.configFileName, "Invalid savegame component count. Ignoring savegame position!") |
746 | end |
747 | else |
748 | local yOffset = getXMLFloat(savegame.xmlFile, savegame.key.."#yOffset") |
749 | local xPosition = getXMLFloat(savegame.xmlFile, savegame.key.."#xPosition") |
750 | local zPosition = getXMLFloat(savegame.xmlFile, savegame.key.."#zPosition") |
751 | local yRotation = getXMLFloat(savegame.xmlFile, savegame.key.."#yRotation") |
752 | if yOffset == nil or xPosition == nil or zPosition == nil or yRotation == nil then |
753 | findPlace = true |
754 | else |
755 | self:setRelativePosition(xPosition, yOffset, zPosition, math.rad(yRotation)) |
756 | end |
757 | end |
758 | end |
759 | if findPlace then |
760 | if savegame.resetVehicles and not savegame.keepPosition then |
761 | local x, _, z, place, width, offset = PlacementUtil.getPlace(g_currentMission:getResetPlaces(), self.sizeWidth, self.sizeLength, self.widthOffset, self.lengthOffset, g_currentMission.usedLoadPlaces, true, false, true) |
762 | if x ~= nil then |
763 | local yRot = MathUtil.getYRotationFromDirection(place.dirPerpX, place.dirPerpZ) |
764 | PlacementUtil.markPlaceUsed(g_currentMission.usedLoadPlaces, place, width) |
765 | self:setRelativePosition(x, offset, z, yRot) |
766 | else |
767 | self:setLoadingState(BaseMission.VEHICLE_LOAD_NO_SPACE) |
768 | if asyncCallbackFunction ~= nil then |
769 | asyncCallbackFunction(asyncCallbackObject, nil, self.loadingState, asyncCallbackArguments) |
770 | end |
771 | return self.loadingState |
772 | end |
773 | else |
774 | self:setLoadingState(BaseMission.VEHICLE_LOAD_DELAYED) |
775 | end |
776 | end |
777 | else |
778 | self:setAbsolutePosition(position.posX, self:getLimitedVehicleYPosition(position), position.posZ, rotation.rotX, rotation.rotY, rotation.rotZ, componentPositions) |
779 | end |
780 | |
781 | self:addToPhysics() |
782 | |
783 | self:updateSelectableObjects() |
784 | self:setSelectedVehicle(self, nil, true) |
785 | |
786 | SpecializationUtil.raiseEvent(self, "onLoadFinished", savegame) |
787 | |
788 | if componentPositions ~= nil and savegame == nil then |
789 | self:setAbsolutePosition(position.posX, self:getLimitedVehicleYPosition(position), position.posZ, rotation.rotX, rotation.rotY, rotation.rotZ, componentPositions) |
790 | end |
791 | |
792 | if asyncCallbackFunction ~= nil then |
793 | asyncCallbackFunction(asyncCallbackObject, self, self.loadingState, asyncCallbackArguments) |
794 | else |
795 | return self.loadingState |
796 | end |
797 | 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 |
2665 | function Vehicle:loadSchemaOverlay(xmlFile) |
2666 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#file") --FS17 to FS19 |
2667 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#width") --FS17 to FS19 |
2668 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#height") --FS17 to FS19 |
2669 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#invisibleBorderRight", "vehicle.base.schemaOverlay#invisibleBorderRight") --FS17 to FS19 |
2670 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#invisibleBorderLeft", "vehicle.base.schemaOverlay#invisibleBorderLeft") --FS17 to FS19 |
2671 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#attacherJointPosition", "vehicle.base.schemaOverlay#attacherJointPosition") --FS17 to FS19 |
2672 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#basePosition", "vehicle.base.schemaOverlay#basePosition") --FS17 to FS19 |
2673 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#fileSelected") --FS17 to FS19 |
2674 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#fileTurnedOn") --FS17 to FS19 |
2675 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#fileSelectedTurnedOn") --FS17 to FS19 |
2676 | |
2677 | if hasXMLProperty(xmlFile, "vehicle.base.schemaOverlay") then |
2678 | XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, "vehicle.schemaOverlay.attacherJoint", "vehicle.attacherJoints.attacherJoint.schema") -- FS17 |
2679 | |
2680 | local x, y = StringUtil.getVectorFromString(getXMLString(xmlFile, "vehicle.base.schemaOverlay#attacherJointPosition")) |
2681 | local baseX, baseY = StringUtil.getVectorFromString(getXMLString(xmlFile, "vehicle.base.schemaOverlay#basePosition")) |
2682 | |
2683 | if baseX == nil then |
2684 | baseX = x |
2685 | end |
2686 | |
2687 | if baseY == nil then |
2688 | baseY = y |
2689 | end |
2690 | |
2691 | local schemaNameDefault = getXMLString(xmlFile, "vehicle.base.schemaOverlay.default#name") or "" |
2692 | local schemaNameTurnedOn = getXMLString(xmlFile, "vehicle.base.schemaOverlay.turnedOn#name") or "" |
2693 | local schemaNameSelected = getXMLString(xmlFile, "vehicle.base.schemaOverlay.selected#name") or "" |
2694 | local schemaNameSelectedTurnedOn = getXMLString(xmlFile, "vehicle.base.schemaOverlay.turnedOnSelected#name") or "" |
2695 | |
2696 | local modPrefix = self.customEnvironment or "" |
2697 | schemaNameDefault = Vehicle.prefixSchemaOverlayName(schemaNameDefault, modPrefix) |
2698 | schemaNameTurnedOn = Vehicle.prefixSchemaOverlayName(schemaNameTurnedOn, modPrefix) |
2699 | schemaNameSelected = Vehicle.prefixSchemaOverlayName(schemaNameSelected, modPrefix) |
2700 | schemaNameSelectedTurnedOn = Vehicle.prefixSchemaOverlayName(schemaNameSelectedTurnedOn, modPrefix) |
2701 | |
2702 | self.schemaOverlay = VehicleSchemaOverlayData.new( |
2703 | baseX, baseY, |
2704 | schemaNameDefault, |
2705 | schemaNameTurnedOn, |
2706 | schemaNameSelected, |
2707 | schemaNameSelectedTurnedOn, |
2708 | getXMLFloat(xmlFile, "vehicle.base.schemaOverlay#invisibleBorderRight"), |
2709 | getXMLFloat(xmlFile, "vehicle.base.schemaOverlay#invisibleBorderLeft")) |
2710 | end |
2711 | end |
loadSpecValueCombinations
DescriptionDefinitionloadSpecValueCombinations()Code
3075 | function Vehicle.loadSpecValueCombinations(xmlFile, customEnvironment) |
3076 | return XMLUtil.getXMLI18NValue(xmlFile, "vehicle.storeData.specs", getXMLString, "combination", nil, customEnvironment, false) |
3077 | end |
loadSpecValueSpeedLimit
DescriptionDefinitionloadSpecValueSpeedLimit()Code
3060 | function Vehicle.loadSpecValueSpeedLimit(xmlFile, customEnvironment) |
3061 | return getXMLString(xmlFile, "vehicle.base.speedLimit#value") |
3062 | end |
loadSpecValueWorkingWidth
DescriptionDefinitionloadSpecValueWorkingWidth()Code
3045 | function Vehicle.loadSpecValueWorkingWidth(xmlFile, customEnvironment) |
3046 | return getXMLString(xmlFile, "vehicle.storeData.specs.workingWidth") |
3047 | end |
new
DescriptionDefinitionnew()Code
209 | function Vehicle:new(isServer, isClient, customMt) |
210 | |
211 | local self = Object:new(isServer, isClient, customMt or Vehicle_mt) |
212 | |
213 | self.isAddedToMission = false |
214 | self.isDeleted = false |
215 | self.updateLoopIndex = -1 |
216 | self.loadingState = BaseMission.VEHICLE_LOAD_OK |
217 | |
218 | return self |
219 | end |
onVehicleWakeUpCallback
DescriptionDefinitiononVehicleWakeUpCallback()Code
2872 | function Vehicle:onVehicleWakeUpCallback(id) |
2873 | self:raiseActive() |
2874 | end |
raiseStateChange
DescriptionDefinitionraiseStateChange()Code
2727 | function Vehicle:raiseStateChange(state, data) |
2728 | SpecializationUtil.raiseEvent(self, "onStateChange", state, data) |
2729 | end |
readStream
DescriptionCalled on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
946 | function Vehicle:readStream(streamId, connection) |
947 | Vehicle:superClass().readStream(self, streamId) |
948 | local configFile = NetworkUtil.convertFromNetworkFilename(streamReadString(streamId)) |
949 | local typeName = streamReadString(streamId) |
950 | |
951 | local configurations = {} |
952 | local numConfigs = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
953 | for i=1, numConfigs do |
954 | local configNameId = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
955 | local configId = streamReadUInt16(streamId) |
956 | |
957 | local configName = g_configurationManager:getConfigurationNameByIndex(configNameId+1) |
958 | if configName ~= nil then |
959 | configurations[configName] = configId+1 |
960 | end |
961 | end |
962 | |
963 | local boughtConfigurations = {} |
964 | local numConfigs = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
965 | for i=1, numConfigs do |
966 | local configNameId = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
967 | local configName = g_configurationManager:getConfigurationNameByIndex(configNameId+1) |
968 | boughtConfigurations[configName] = {} |
969 | local numBoughtConfigIds = streamReadUInt16(streamId) |
970 | for j=1, numBoughtConfigIds do |
971 | local boughtConfigId = streamReadUInt16(streamId) |
972 | boughtConfigurations[configName][boughtConfigId + 1] = true |
973 | end |
974 | end |
975 | |
976 | if self.configFileName == nil then |
977 | local vehicleData = {} |
978 | vehicleData.filename = configFile |
979 | vehicleData.isAbsolute = false |
980 | vehicleData.typeName = typeName |
981 | vehicleData.posX = 0 |
982 | vehicleData.posY = nil |
983 | vehicleData.posZ = 0 |
984 | vehicleData.yOffset = 0 |
985 | vehicleData.rotX = 0 |
986 | vehicleData.rotY = 0 |
987 | vehicleData.rotZ = 0 |
988 | vehicleData.isVehicleSaved = true |
989 | vehicleData.price = 0 |
990 | vehicleData.propertyState = Vehicle.PROPERTY_STATE_NONE |
991 | -- assign parent class Object's ownerFarmId field here to ensure ownership synchronization in MP: |
992 | vehicleData.ownerFarmId = self.ownerFarmId |
993 | vehicleData.isLeased = 0 |
994 | vehicleData.configurations = configurations |
995 | vehicleData.boughtConfigurations = boughtConfigurations |
996 | self:load(vehicleData) |
997 | end |
998 | |
999 | -- remove from physics to set static components correctly |
1000 | self:removeFromPhysics() |
1001 | |
1002 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
1003 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
1004 | for i=1, table.getn(self.components) do |
1005 | local component = self.components[i] |
1006 | local x = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
1007 | local y = NetworkUtil.readCompressedWorldPosition(streamId, paramsY) |
1008 | local z = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
1009 | local x_rot = NetworkUtil.readCompressedAngle(streamId) |
1010 | local y_rot = NetworkUtil.readCompressedAngle(streamId) |
1011 | local z_rot = NetworkUtil.readCompressedAngle(streamId) |
1012 | |
1013 | local qx,qy,qz,qw = mathEulerToQuaternion(x_rot,y_rot,z_rot) |
1014 | self:setWorldPositionQuaternion(x,y,z, qx,qy,qz,qw, i, true) |
1015 | |
1016 | component.networkInterpolators.position:setPosition(x,y,z) |
1017 | component.networkInterpolators.quaternion:setQuaternion(qx,qy,qz,qw) |
1018 | end |
1019 | self.networkTimeInterpolator:reset() |
1020 | |
1021 | -- add to physics again |
1022 | self:addToPhysics() |
1023 | |
1024 | self.serverMass = streamReadFloat32(streamId) |
1025 | self.age = streamReadUInt16(streamId) |
1026 | self:setOperatingTime(streamReadFloat32(streamId), true) |
1027 | self.price = streamReadInt32(streamId) |
1028 | self.propertyState = streamReadUIntN(streamId, 2) |
1029 | |
1030 | SpecializationUtil.raiseEvent(self, "onReadStream", streamId, connection) |
1031 | 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 |
1101 | function Vehicle:readUpdateStream(streamId, timestamp, connection) |
1102 | if connection.isServer then |
1103 | local hasUpdate = streamReadBool(streamId) |
1104 | if hasUpdate then |
1105 | self.networkTimeInterpolator:startNewPhaseNetwork() |
1106 | |
1107 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
1108 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
1109 | for i=1, table.getn(self.components) do |
1110 | local component = self.components[i] |
1111 | if not component.isStatic then |
1112 | local x = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
1113 | local y = NetworkUtil.readCompressedWorldPosition(streamId, paramsY) |
1114 | local z = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
1115 | local x_rot = NetworkUtil.readCompressedAngle(streamId) |
1116 | local y_rot = NetworkUtil.readCompressedAngle(streamId) |
1117 | local z_rot = NetworkUtil.readCompressedAngle(streamId) |
1118 | local qx,qy,qz,qw = mathEulerToQuaternion(x_rot,y_rot,z_rot) |
1119 | |
1120 | component.networkInterpolators.position:setTargetPosition(x,y,z) |
1121 | component.networkInterpolators.quaternion:setTargetQuaternion(qx,qy,qz,qw) |
1122 | end |
1123 | end |
1124 | SpecializationUtil.raiseEvent(self, "onReadPositionUpdateStream", streamId, connection) |
1125 | end |
1126 | end |
1127 | |
1128 | if Vehicle.debugNetworkUpdate then |
1129 | print("-------------------------------------------------------------") |
1130 | print(self.configFileName) |
1131 | for _, spec in ipairs(self.eventListeners["readUpdateStream"]) do |
1132 | local className = ClassUtil.getClassName(spec) |
1133 | local startBits = streamGetReadOffset(streamId) |
1134 | spec["readUpdateStream"](self, streamId, timestamp, connection) |
1135 | print(" "..tostring(className).." read " .. streamGetReadOffset(streamId)-startBits .. " bits") |
1136 | end |
1137 | else |
1138 | SpecializationUtil.raiseEvent(self, "onReadUpdateStream", streamId, timestamp, connection) |
1139 | end |
1140 | end |
registerActionEvents
DescriptionDefinitionregisterActionEvents()Code
1893 | function Vehicle:registerActionEvents(excludedVehicle) |
1894 | if not g_gui:getIsGuiVisible() and not g_currentMission.isPlayerFrozen and excludedVehicle ~= self then |
1895 | self.actionEventUpdateRequested = false |
1896 | |
1897 | local isActiveForInput = self:getIsActiveForInput() |
1898 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
1899 | |
1900 | if isActiveForInput then |
1901 | -- reset the action binding enabled state for bindings previously disabled during conflict resolution: |
1902 | g_inputBinding:resetActiveActionBindings() |
1903 | end |
1904 | |
1905 | -- safety: set the input registration context without changing the actual input context in case we're currently |
1906 | -- not in the vehicle context (e.g. due to network events) |
1907 | g_inputBinding:beginActionEventsModification(Vehicle.INPUT_CONTEXT_NAME) |
1908 | |
1909 | SpecializationUtil.raiseEvent(self, "onRegisterActionEvents", isActiveForInput, isActiveForInputIgnoreSelection) |
1910 | |
1911 | self:clearActionEventsTable(self.actionEvents) |
1912 | if self:getCanToggleSelectable() then |
1913 | local numSelectableObjects = 0 |
1914 | |
1915 | for _, object in ipairs(self.selectableObjects) do |
1916 | numSelectableObjects = numSelectableObjects + 1 + #object.subSelections |
1917 | end |
1918 | |
1919 | if numSelectableObjects > 1 then |
1920 | local _, actionEventId = self:addActionEvent(self.actionEvents, InputAction.SWITCH_IMPLEMENT, self, Vehicle.actionEventToggleSelection, false, true, false, true, nil) |
1921 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_LOW) |
1922 | end |
1923 | end |
1924 | |
1925 | g_inputBinding:endActionEventsModification() |
1926 | end |
1927 | end |
registerEvents
DescriptionDefinitionregisterEvents()Code
66 | function Vehicle.registerEvents(vehicleType) |
67 | SpecializationUtil.registerEvent(vehicleType, "onPreLoad") |
68 | SpecializationUtil.registerEvent(vehicleType, "onLoad") |
69 | SpecializationUtil.registerEvent(vehicleType, "onPostLoad") |
70 | SpecializationUtil.registerEvent(vehicleType, "onLoadFinished") |
71 | SpecializationUtil.registerEvent(vehicleType, "onPreDelete") |
72 | SpecializationUtil.registerEvent(vehicleType, "onDelete") |
73 | SpecializationUtil.registerEvent(vehicleType, "onSave") |
74 | SpecializationUtil.registerEvent(vehicleType, "onReadStream") |
75 | SpecializationUtil.registerEvent(vehicleType, "onWriteStream") |
76 | SpecializationUtil.registerEvent(vehicleType, "onReadUpdateStream") |
77 | SpecializationUtil.registerEvent(vehicleType, "onWriteUpdateStream") |
78 | SpecializationUtil.registerEvent(vehicleType, "onReadPositionUpdateStream") |
79 | SpecializationUtil.registerEvent(vehicleType, "onWritePositionUpdateStream") |
80 | SpecializationUtil.registerEvent(vehicleType, "onPreUpdate") |
81 | SpecializationUtil.registerEvent(vehicleType, "onUpdate") |
82 | SpecializationUtil.registerEvent(vehicleType, "onUpdateInterpolation") |
83 | SpecializationUtil.registerEvent(vehicleType, "onUpdateDebug") |
84 | SpecializationUtil.registerEvent(vehicleType, "onPostUpdate") |
85 | SpecializationUtil.registerEvent(vehicleType, "onUpdateTick") |
86 | SpecializationUtil.registerEvent(vehicleType, "onPostUpdateTick") |
87 | SpecializationUtil.registerEvent(vehicleType, "onUpdateEnd") |
88 | SpecializationUtil.registerEvent(vehicleType, "onDraw") |
89 | SpecializationUtil.registerEvent(vehicleType, "onActivate") |
90 | SpecializationUtil.registerEvent(vehicleType, "onDeactivate") |
91 | SpecializationUtil.registerEvent(vehicleType, "onStateChange") |
92 | SpecializationUtil.registerEvent(vehicleType, "onRegisterActionEvents") |
93 | SpecializationUtil.registerEvent(vehicleType, "onSelect") |
94 | SpecializationUtil.registerEvent(vehicleType, "onUnselect") |
95 | SpecializationUtil.registerEvent(vehicleType, "onSetBroken") |
96 | end |
registerFunctions
DescriptionDefinitionregisterFunctions()Code
100 | function Vehicle.registerFunctions(vehicleType) |
101 | SpecializationUtil.registerFunction(vehicleType, "drawUIInfo", Vehicle.drawUIInfo) |
102 | SpecializationUtil.registerFunction(vehicleType, "raiseActive", Vehicle.raiseActive) |
103 | SpecializationUtil.registerFunction(vehicleType, "setLoadingState", Vehicle.setLoadingState) |
104 | SpecializationUtil.registerFunction(vehicleType, "addNodeObjectMapping", Vehicle.addNodeObjectMapping) |
105 | SpecializationUtil.registerFunction(vehicleType, "removeNodeObjectMapping", Vehicle.removeNodeObjectMapping) |
106 | SpecializationUtil.registerFunction(vehicleType, "addToPhysics", Vehicle.addToPhysics) |
107 | SpecializationUtil.registerFunction(vehicleType, "removeFromPhysics", Vehicle.removeFromPhysics) |
108 | SpecializationUtil.registerFunction(vehicleType, "setRelativePosition", Vehicle.setRelativePosition) |
109 | SpecializationUtil.registerFunction(vehicleType, "setAbsolutePosition", Vehicle.setAbsolutePosition) |
110 | SpecializationUtil.registerFunction(vehicleType, "getLimitedVehicleYPosition", Vehicle.getLimitedVehicleYPosition) |
111 | SpecializationUtil.registerFunction(vehicleType, "setWorldPosition", Vehicle.setWorldPosition) |
112 | SpecializationUtil.registerFunction(vehicleType, "setWorldPositionQuaternion", Vehicle.setWorldPositionQuaternion) |
113 | SpecializationUtil.registerFunction(vehicleType, "updateVehicleSpeed", Vehicle.updateVehicleSpeed) |
114 | SpecializationUtil.registerFunction(vehicleType, "getUpdatePriority", Vehicle.getUpdatePriority) |
115 | SpecializationUtil.registerFunction(vehicleType, "getPrice", Vehicle.getPrice) |
116 | SpecializationUtil.registerFunction(vehicleType, "getSellPrice", Vehicle.getSellPrice) |
117 | SpecializationUtil.registerFunction(vehicleType, "getDailyUpkeep", Vehicle.getDailyUpkeep) |
118 | SpecializationUtil.registerFunction(vehicleType, "getIsOnField", Vehicle.getIsOnField) |
119 | SpecializationUtil.registerFunction(vehicleType, "getParentComponent", Vehicle.getParentComponent) |
120 | SpecializationUtil.registerFunction(vehicleType, "getLastSpeed", Vehicle.getLastSpeed) |
121 | SpecializationUtil.registerFunction(vehicleType, "getDeactivateOnLeave", Vehicle.getDeactivateOnLeave) |
122 | SpecializationUtil.registerFunction(vehicleType, "getOwner", Vehicle.getOwner) |
123 | SpecializationUtil.registerFunction(vehicleType, "getIsVehicleNode", Vehicle.getIsVehicleNode) |
124 | SpecializationUtil.registerFunction(vehicleType, "getIsOperating", Vehicle.getIsOperating) |
125 | SpecializationUtil.registerFunction(vehicleType, "getIsActive", Vehicle.getIsActive) |
126 | SpecializationUtil.registerFunction(vehicleType, "getIsActiveForInput", Vehicle.getIsActiveForInput) |
127 | SpecializationUtil.registerFunction(vehicleType, "getIsActiveForSound", Vehicle.getIsActiveForSound) |
128 | SpecializationUtil.registerFunction(vehicleType, "getIsLowered", Vehicle.getIsLowered) |
129 | SpecializationUtil.registerFunction(vehicleType, "getTailwaterDepth", Vehicle.getTailwaterDepth) |
130 | SpecializationUtil.registerFunction(vehicleType, "setBroken", Vehicle.setBroken) |
131 | SpecializationUtil.registerFunction(vehicleType, "getVehicleDamage", Vehicle.getVehicleDamage) |
132 | SpecializationUtil.registerFunction(vehicleType, "getRepairPrice", Vehicle.getRepairPrice) |
133 | SpecializationUtil.registerFunction(vehicleType, "setMassDirty", Vehicle.setMassDirty) |
134 | SpecializationUtil.registerFunction(vehicleType, "updateMass", Vehicle.updateMass) |
135 | SpecializationUtil.registerFunction(vehicleType, "getAdditionalComponentMass", Vehicle.getAdditionalComponentMass) |
136 | SpecializationUtil.registerFunction(vehicleType, "getTotalMass", Vehicle.getTotalMass) |
137 | SpecializationUtil.registerFunction(vehicleType, "getFillLevelInformation", Vehicle.getFillLevelInformation) |
138 | SpecializationUtil.registerFunction(vehicleType, "activate", Vehicle.activate) |
139 | SpecializationUtil.registerFunction(vehicleType, "deactivate", Vehicle.deactivate) |
140 | SpecializationUtil.registerFunction(vehicleType, "setComponentJointFrame", Vehicle.setComponentJointFrame) |
141 | SpecializationUtil.registerFunction(vehicleType, "setComponentJointRotLimit", Vehicle.setComponentJointRotLimit) |
142 | SpecializationUtil.registerFunction(vehicleType, "setComponentJointTransLimit", Vehicle.setComponentJointTransLimit) |
143 | SpecializationUtil.registerFunction(vehicleType, "loadComponentFromXML", Vehicle.loadComponentFromXML) |
144 | SpecializationUtil.registerFunction(vehicleType, "loadComponentJointFromXML", Vehicle.loadComponentJointFromXML) |
145 | SpecializationUtil.registerFunction(vehicleType, "createComponentJoint", Vehicle.createComponentJoint) |
146 | SpecializationUtil.registerFunction(vehicleType, "loadSchemaOverlay", Vehicle.loadSchemaOverlay) |
147 | SpecializationUtil.registerFunction(vehicleType, "getAdditionalSchemaText", Vehicle.getAdditionalSchemaText) |
148 | SpecializationUtil.registerFunction(vehicleType, "dayChanged", Vehicle.dayChanged) |
149 | SpecializationUtil.registerFunction(vehicleType, "raiseStateChange", Vehicle.raiseStateChange) |
150 | SpecializationUtil.registerFunction(vehicleType, "doCheckSpeedLimit", Vehicle.doCheckSpeedLimit) |
151 | SpecializationUtil.registerFunction(vehicleType, "interact", Vehicle.interact) |
152 | SpecializationUtil.registerFunction(vehicleType, "getInteractionHelp", Vehicle.getInteractionHelp) |
153 | SpecializationUtil.registerFunction(vehicleType, "getDistanceToNode", Vehicle.getDistanceToNode) |
154 | SpecializationUtil.registerFunction(vehicleType, "getIsAIActive", Vehicle.getIsAIActive) |
155 | SpecializationUtil.registerFunction(vehicleType, "addVehicleToAIImplementList", Vehicle.addVehicleToAIImplementList) |
156 | SpecializationUtil.registerFunction(vehicleType, "addToTotalVehicleList", Vehicle.addToTotalVehicleList) |
157 | SpecializationUtil.registerFunction(vehicleType, "setOperatingTime", Vehicle.setOperatingTime) |
158 | |
159 | SpecializationUtil.registerFunction(vehicleType, "requestActionEventUpdate", Vehicle.requestActionEventUpdate) |
160 | SpecializationUtil.registerFunction(vehicleType, "removeActionEvents", Vehicle.removeActionEvents) |
161 | SpecializationUtil.registerFunction(vehicleType, "updateActionEvents", Vehicle.updateActionEvents) |
162 | SpecializationUtil.registerFunction(vehicleType, "registerActionEvents", Vehicle.registerActionEvents) |
163 | |
164 | SpecializationUtil.registerFunction(vehicleType, "updateSelectableObjects", Vehicle.updateSelectableObjects) |
165 | SpecializationUtil.registerFunction(vehicleType, "registerSelectableObjects", Vehicle.registerSelectableObjects) |
166 | SpecializationUtil.registerFunction(vehicleType, "addSubselection", Vehicle.addSubselection) |
167 | SpecializationUtil.registerFunction(vehicleType, "getRootVehicle", Vehicle.getRootVehicle) |
168 | SpecializationUtil.registerFunction(vehicleType, "getChildVehicles", Vehicle.getChildVehicles) |
169 | SpecializationUtil.registerFunction(vehicleType, "getCanBeSelected", Vehicle.getCanBeSelected) |
170 | SpecializationUtil.registerFunction(vehicleType, "getCanToggleSelectable", Vehicle.getCanToggleSelectable) |
171 | SpecializationUtil.registerFunction(vehicleType, "unselectVehicle", Vehicle.unselectVehicle) |
172 | SpecializationUtil.registerFunction(vehicleType, "selectVehicle", Vehicle.selectVehicle) |
173 | SpecializationUtil.registerFunction(vehicleType, "getIsSelected", Vehicle.getIsSelected) |
174 | SpecializationUtil.registerFunction(vehicleType, "getSelectedObject", Vehicle.getSelectedObject) |
175 | SpecializationUtil.registerFunction(vehicleType, "getSelectedVehicle", Vehicle.getSelectedVehicle) |
176 | SpecializationUtil.registerFunction(vehicleType, "setSelectedVehicle", Vehicle.setSelectedVehicle) |
177 | SpecializationUtil.registerFunction(vehicleType, "setSelectedObject", Vehicle.setSelectedObject) |
178 | SpecializationUtil.registerFunction(vehicleType, "getIsReadyForAutomatedTrainTravel", Vehicle.getIsReadyForAutomatedTrainTravel) |
179 | SpecializationUtil.registerFunction(vehicleType, "getActiveFarm", Vehicle.getActiveFarm) |
180 | SpecializationUtil.registerFunction(vehicleType, "onVehicleWakeUpCallback", Vehicle.onVehicleWakeUpCallback) |
181 | SpecializationUtil.registerFunction(vehicleType, "getCanByMounted", Vehicle.getCanByMounted) |
182 | SpecializationUtil.registerFunction(vehicleType, "getName", Vehicle.getName) |
183 | SpecializationUtil.registerFunction(vehicleType, "getFullName", Vehicle.getFullName) |
184 | SpecializationUtil.registerFunction(vehicleType, "getCanBePickedUp", Vehicle.getCanBePickedUp) |
185 | SpecializationUtil.registerFunction(vehicleType, "getCanBeReset", Vehicle.getCanBeReset) |
186 | SpecializationUtil.registerFunction(vehicleType, "getIsInUse", Vehicle.getIsInUse) |
187 | SpecializationUtil.registerFunction(vehicleType, "getPropertyState", Vehicle.getPropertyState) |
188 | end |
registerInteractionFlag
DescriptionRegister interaction flagDefinition
registerInteractionFlag(string name)Arguments
string | name | name of flag |
46 | function Vehicle.registerInteractionFlag(name) |
47 | local key = "INTERACTION_FLAG_"..string.upper(name) |
48 | if Vehicle[key] == nil then |
49 | Vehicle.NUM_INTERACTION_FLAGS = Vehicle.NUM_INTERACTION_FLAGS + 1 |
50 | Vehicle[key] = Vehicle.NUM_INTERACTION_FLAGS |
51 | end |
52 | end |
registerSelectableObjects
DescriptionDefinitionregisterSelectableObjects()Code
2019 | function Vehicle:registerSelectableObjects(selectableObjects) |
2020 | if self:getCanBeSelected() then |
2021 | table.insert(selectableObjects, self.selectionObject) |
2022 | self.selectionObject.index = #selectableObjects |
2023 | end |
2024 | end |
registerStateChange
DescriptionDefinitionregisterStateChange()Code
56 | function Vehicle.registerStateChange(name) |
57 | local key = "STATE_CHANGE_"..string.upper(name) |
58 | if Vehicle[key] == nil then |
59 | Vehicle.NUM_STATE_CHANGES = Vehicle.NUM_STATE_CHANGES + 1 |
60 | Vehicle[key] = Vehicle.NUM_STATE_CHANGES |
61 | end |
62 | end |
removeActionEvent
DescriptionDefinitionremoveActionEvent()Code
1995 | function Vehicle:removeActionEvent(actionEventsTable, inputAction) |
1996 | if actionEventsTable[inputAction] ~= nil then |
1997 | g_inputBinding:removeActionEvent(actionEventsTable[inputAction].actionEventId) |
1998 | actionEventsTable[inputAction] = nil |
1999 | end |
2000 | end |
removeActionEvents
DescriptionDefinitionremoveActionEvents()Code
1880 | function Vehicle:removeActionEvents() |
1881 | g_inputBinding:removeActionEventsByTarget(self) |
1882 | end |
removeFromPhysics
DescriptionRemove vehicle from physicsDefinition
removeFromPhysics()Code
1519 | function Vehicle:removeFromPhysics() |
1520 | for _, component in pairs(self.components) do |
1521 | removeFromPhysics(component.node) |
1522 | end |
1523 | -- invalidate wheel shapes and component joints (removing the components removes the wheels and joints too) |
1524 | if self.isServer then |
1525 | for _, jointDesc in pairs(self.componentJoints) do |
1526 | jointDesc.jointIndex = 0 |
1527 | end |
1528 | removeWakeUpReport(self.rootNode) |
1529 | end |
1530 | self.isAddedToPhysics = false |
1531 | |
1532 | return true |
1533 | end |
removeNodeObjectMapping
DescriptionRemove component nodes from listDefinition
removeNodeObjectMapping(table list)Arguments
table | list | list |
1471 | function Vehicle:removeNodeObjectMapping(list) |
1472 | for _,v in pairs(self.components) do |
1473 | list[v.node] = nil |
1474 | end |
1475 | end |
requestActionEventUpdate
DescriptionDefinitionrequestActionEventUpdate()Code
1865 | function Vehicle:requestActionEventUpdate() |
1866 | -- pass request to rootVehicle |
1867 | local vehicle = self:getRootVehicle() |
1868 | if vehicle == self then |
1869 | self.actionEventUpdateRequested = true |
1870 | else |
1871 | vehicle:requestActionEventUpdate() |
1872 | end |
1873 | |
1874 | -- remove all actionEvents |
1875 | vehicle:removeActionEvents() |
1876 | end |
saveStatsToXMLFile
DescriptionGet xml states attributesDefinition
saveStatsToXMLFile()Return Values
string | attributes | attributes |
903 | function Vehicle:saveStatsToXMLFile(xmlFile, key) |
904 | local isTabbable = self.getIsTabbable == nil or self:getIsTabbable() |
905 | if self.isDeleted or not self.isVehicleSaved or not isTabbable then |
906 | return false |
907 | end |
908 | local name = "Unknown" |
909 | local categoryName = "unknown" |
910 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
911 | if storeItem ~= nil then |
912 | if storeItem.name ~= nil then |
913 | name = tostring(storeItem.name) |
914 | end |
915 | if storeItem.categoryName ~= nil and storeItem.categoryName ~= "" then |
916 | categoryName = tostring(storeItem.categoryName) |
917 | end |
918 | end |
919 | |
920 | setXMLString(xmlFile, key.."#name", HTMLUtil.encodeToHTML(name)) |
921 | setXMLString(xmlFile, key.."#category", HTMLUtil.encodeToHTML(categoryName)) |
922 | setXMLString(xmlFile, key.."#type", HTMLUtil.encodeToHTML(tostring(self.typeName))) |
923 | |
924 | if self.components[1] ~= nil and self.components[1].node ~= 0 then |
925 | local x,y,z = getWorldTranslation(self.components[1].node) |
926 | setXMLFloat(xmlFile, key.."#x", x) |
927 | setXMLFloat(xmlFile, key.."#y", y) |
928 | setXMLFloat(xmlFile, key.."#z", z) |
929 | end |
930 | |
931 | for id, spec in pairs(self.specializations) do |
932 | local name = self.specializationNames[id] |
933 | |
934 | if spec.saveStatsToXMLFile ~= nil then |
935 | spec.saveStatsToXMLFile(self, xmlFile, key) |
936 | end |
937 | end |
938 | |
939 | return true |
940 | end |
saveToXMLFile
DescriptionDefinitionsaveToXMLFile()Code
845 | function Vehicle:saveToXMLFile(xmlFile, key, usedModNames) |
846 | setXMLBool(xmlFile, key.."#isAbsolute", true) |
847 | setXMLFloat(xmlFile, key.."#age", self.age) |
848 | setXMLFloat(xmlFile, key.."#price", self.price) |
849 | setXMLInt(xmlFile, key.."#farmId", self:getOwnerFarmId()) |
850 | setXMLInt(xmlFile, key.."#propertyState", self.propertyState) |
851 | setXMLFloat(xmlFile, key.."#operatingTime", self.operatingTime / 1000) |
852 | |
853 | if self.activeMissionId ~= nil then |
854 | setXMLInt(xmlFile, key.."#activeMissionId", self.activeMissionId) |
855 | end |
856 | |
857 | if self.tourId ~= nil then |
858 | setXMLString(xmlFile, key.."#tourId", self.tourId) |
859 | end |
860 | |
861 | if not self.isBroken then |
862 | for k, component in ipairs(self.components) do |
863 | local compKey = string.format("%s.component%d", key, k) |
864 | local node = component.node |
865 | local x,y,z = getWorldTranslation(node) |
866 | local xRot,yRot,zRot = getWorldRotation(node) |
867 | |
868 | setXMLString(xmlFile, compKey.."#position", string.format("%.4f %.4f %.4f", x, y, z)) |
869 | setXMLString(xmlFile, compKey.."#rotation", string.format("%.4f %.4f %.4f", math.deg(xRot), math.deg(yRot), math.deg(zRot))) |
870 | end |
871 | end |
872 | |
873 | local configIndex = 0 |
874 | for configName, configId in pairs(self.configurations) do |
875 | local configKey = string.format("%s.configuration(%d)", key, configIndex) |
876 | setXMLString(xmlFile, configKey.."#name", configName) |
877 | setXMLInt(xmlFile, configKey.."#id", configId) |
878 | configIndex = configIndex + 1 |
879 | end |
880 | |
881 | configIndex = 0 |
882 | for configName, configIds in pairs(self.boughtConfigurations) do |
883 | for configId,_ in pairs(configIds) do |
884 | local configKey = string.format("%s.boughtConfiguration(%d)", key, configIndex) |
885 | setXMLString(xmlFile, configKey.."#name", configName) |
886 | setXMLInt(xmlFile, configKey.."#id", configId) |
887 | configIndex = configIndex + 1 |
888 | end |
889 | end |
890 | |
891 | for id, spec in pairs(self.specializations) do |
892 | local name = self.specializationNames[id] |
893 | |
894 | if spec.saveToXMLFile ~= nil then |
895 | spec.saveToXMLFile(self, xmlFile, key.."."..name, usedModNames) |
896 | end |
897 | end |
898 | end |
selectVehicle
DescriptionDefinitionselectVehicle()Code
2068 | function Vehicle:selectVehicle(subSelectionIndex, ignoreActionEventUpdate) |
2069 | self.selectionObject.isSelected = true |
2070 | SpecializationUtil.raiseEvent(self, "onSelect", subSelectionIndex) |
2071 | |
2072 | if ignoreActionEventUpdate == nil or not ignoreActionEventUpdate then |
2073 | self:requestActionEventUpdate() |
2074 | end |
2075 | end |
setAbsolutePosition
DescriptionDefinitionsetAbsolutePosition()Code
1550 | function Vehicle:setAbsolutePosition(positionX, positionY, positionZ, xRot, yRot, zRot, componentPositions) |
1551 | local tempRootNode = createTransformGroup("tempRootNode") |
1552 | setTranslation(tempRootNode, positionX, positionY, positionZ) |
1553 | setRotation(tempRootNode, xRot, yRot, zRot) |
1554 | |
1555 | -- now move the objects to the scene root node |
1556 | for i, component in pairs(self.components) do |
1557 | local x,y,z = localToWorld(tempRootNode, unpack(component.originalTranslation)) |
1558 | local rx,ry,rz = localRotationToWorld(tempRootNode, unpack(component.originalRotation)) |
1559 | |
1560 | if componentPositions ~= nil and #componentPositions == #self.components then |
1561 | x,y,z = unpack(componentPositions[i][1]) |
1562 | rx,ry,rz = unpack(componentPositions[i][2]) |
1563 | end |
1564 | |
1565 | self:setWorldPosition(x, y, z, rx, ry, rz, i, true) |
1566 | end |
1567 | delete(tempRootNode) |
1568 | |
1569 | self.networkTimeInterpolator:reset() |
1570 | end |
setBroken
DescriptionDefinitionsetBroken()Code
1842 | function Vehicle:setBroken() |
1843 | if self.isServer and not self.isBroken then |
1844 | g_server:broadcastEvent(VehicleBrokenEvent:new(self), nil, nil, self) |
1845 | end |
1846 | |
1847 | self.isBroken = true |
1848 | SpecializationUtil.raiseEvent(self, "onSetBroken") |
1849 | end |
setComponentJointFrame
DescriptionSet component joint frameDefinition
setComponentJointFrame(Integer jointDesc, Integer anchorActor)Arguments
Integer | jointDesc | joint desc index |
Integer | anchorActor | anchor actor |
2272 | function Vehicle:setComponentJointFrame(jointDesc, anchorActor) |
2273 | if anchorActor == 0 then |
2274 | local localPoses = jointDesc.jointLocalPoses[1] |
2275 | localPoses.trans[1], localPoses.trans[2], localPoses.trans[3] = localToLocal(jointDesc.jointNode, self.components[jointDesc.componentIndices[1]].node, 0, 0, 0) |
2276 | localPoses.rot[1], localPoses.rot[2], localPoses.rot[3] = localRotationToLocal(jointDesc.jointNode, self.components[jointDesc.componentIndices[1]].node, 0, 0, 0) |
2277 | else |
2278 | local localPoses = jointDesc.jointLocalPoses[2] |
2279 | localPoses.trans[1], localPoses.trans[2], localPoses.trans[3] = localToLocal(jointDesc.jointNodeActor1, self.components[jointDesc.componentIndices[2]].node, 0, 0, 0) |
2280 | localPoses.rot[1], localPoses.rot[2], localPoses.rot[3] = localRotationToLocal(jointDesc.jointNodeActor1, self.components[jointDesc.componentIndices[2]].node, 0, 0, 0) |
2281 | end |
2282 | |
2283 | local jointNode = jointDesc.jointNode |
2284 | if anchorActor == 1 then |
2285 | jointNode = jointDesc.jointNodeActor1 |
2286 | end |
2287 | |
2288 | if jointDesc.jointIndex ~= 0 then |
2289 | setJointFrame(jointDesc.jointIndex, anchorActor, jointNode) |
2290 | end |
2291 | 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 |
2300 | function Vehicle:setComponentJointRotLimit(componentJoint, axis, minLimit, maxLimit) |
2301 | if self.isServer then |
2302 | componentJoint.rotLimit[axis] = maxLimit |
2303 | componentJoint.rotMinLimit[axis] = minLimit |
2304 | |
2305 | if componentJoint.jointIndex ~= 0 then |
2306 | if minLimit <= maxLimit then |
2307 | setJointRotationLimit(componentJoint.jointIndex, axis-1, true, minLimit, maxLimit) |
2308 | else |
2309 | setJointRotationLimit(componentJoint.jointIndex, axis-1, false, 0, 0) |
2310 | end |
2311 | end |
2312 | end |
2313 | 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 |
2321 | function Vehicle:setComponentJointTransLimit(componentJoint, axis, minLimit, maxLimit) |
2322 | if self.isServer then |
2323 | componentJoint.transLimit[axis] = maxLimit |
2324 | componentJoint.transMinLimit[axis] = minLimit |
2325 | |
2326 | if componentJoint.jointIndex ~= 0 then |
2327 | if minLimit <= maxLimit then |
2328 | setJointTranslationLimit(componentJoint.jointIndex, axis-1, true, minLimit, maxLimit) |
2329 | else |
2330 | setJointTranslationLimit(componentJoint.jointIndex, axis-1, false, 0, 0) |
2331 | end |
2332 | end |
2333 | end |
2334 | end |
setLoadingState
DescriptionDefinitionsetLoadingState()Code
1450 | function Vehicle:setLoadingState(loadingState) |
1451 | if loadingState == BaseMission.VEHICLE_LOAD_OK or loadingState == BaseMission.VEHICLE_LOAD_ERROR or loadingState == BaseMission.VEHICLE_LOAD_DELAYED or loadingState == BaseMission.VEHICLE_LOAD_NO_SPACE then |
1452 | self.loadingState = loadingState |
1453 | else |
1454 | printCallstack() |
1455 | g_logManager:xmlError(self.configFileName, "Invalid loading state!") |
1456 | end |
1457 | end |
setMassDirty
DescriptionDefinitionsetMassDirty()Code
2196 | function Vehicle:setMassDirty() |
2197 | self.isMassDirty = true |
2198 | end |
setOperatingTime
DescriptionDefinitionsetOperatingTime()Code
2789 | function Vehicle:setOperatingTime(operatingTime, isLoading) |
2790 | 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 |
2791 | g_currentMission.economyManager:vehicleOperatingHourChanged(self) |
2792 | end |
2793 | self.operatingTime = math.max(Utils.getNoNil(operatingTime, 0), 0) |
2794 | 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 |
1541 | function Vehicle:setRelativePosition(positionX, offsetY, positionZ, yRot) |
1542 | -- position the vehicle |
1543 | local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, positionX, 300, positionZ) |
1544 | |
1545 | self:setAbsolutePosition(positionX, terrainHeight+offsetY, positionZ, 0, yRot, 0) |
1546 | end |
setSelectedObject
DescriptionDefinitionsetSelectedObject()Code
2102 | function Vehicle:setSelectedObject(object, subSelectionIndex, ignoreActionEventUpdate) |
2103 | local currentSelection = self.currentSelection |
2104 | |
2105 | if object == nil then |
2106 | object = self:getSelectedObject() |
2107 | end |
2108 | |
2109 | local found = false |
2110 | for _, o in ipairs(self.selectableObjects) do |
2111 | if o == object then |
2112 | found = true |
2113 | end |
2114 | end |
2115 | |
2116 | if found then |
2117 | -- if object was found unselect all other vehicles |
2118 | for _, o in ipairs(self.selectableObjects) do |
2119 | if o ~= object and o.vehicle:getIsSelected() then |
2120 | o.vehicle:unselectVehicle() |
2121 | end |
2122 | end |
2123 | |
2124 | if object ~= currentSelection.object or subSelectionIndex ~= currentSelection.subIndex then |
2125 | currentSelection.object = object |
2126 | currentSelection.index = object.index |
2127 | if subSelectionIndex ~= nil then |
2128 | currentSelection.subIndex = subSelectionIndex |
2129 | end |
2130 | if currentSelection.subIndex > #object.subSelections then |
2131 | currentSelection.subIndex = 1 |
2132 | end |
2133 | |
2134 | currentSelection.object.vehicle:selectVehicle(currentSelection.subIndex, ignoreActionEventUpdate) |
2135 | |
2136 | return true |
2137 | end |
2138 | else |
2139 | object = self:getSelectedObject() |
2140 | |
2141 | found = false |
2142 | for _, o in ipairs(self.selectableObjects) do |
2143 | if o == object then |
2144 | found = true |
2145 | end |
2146 | end |
2147 | |
2148 | -- if the object to select could not be found and the object that was selected is not available anymore we clear the selection |
2149 | if not found then |
2150 | currentSelection.object = nil |
2151 | currentSelection.index = 1 |
2152 | currentSelection.subIndex = 1 |
2153 | end |
2154 | end |
2155 | |
2156 | return false |
2157 | end |
setSelectedVehicle
DescriptionDefinitionsetSelectedVehicle()Code
2079 | function Vehicle:setSelectedVehicle(vehicle, subSelectionIndex, ignoreActionEventUpdate) |
2080 | local object = nil |
2081 | |
2082 | -- if vehicle could not be selected we select the next possible vehicle |
2083 | if vehicle == nil or not vehicle:getCanBeSelected() then |
2084 | vehicle = nil |
2085 | for _, o in ipairs(self.selectableObjects) do |
2086 | if o.vehicle:getCanBeSelected() then |
2087 | vehicle = o.vehicle |
2088 | break |
2089 | end |
2090 | end |
2091 | end |
2092 | |
2093 | if vehicle ~= nil then |
2094 | object = vehicle.selectionObject |
2095 | end |
2096 | |
2097 | return self:setSelectedObject(object, subSelectionIndex, ignoreActionEventUpdate) |
2098 | 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 |
1594 | function Vehicle:setWorldPosition(x,y,z, xRot,yRot,zRot, i, changeInterp) |
1595 | local component = self.components[i] |
1596 | setWorldTranslation(component.node, x,y,z) |
1597 | setWorldRotation(component.node, xRot,yRot,zRot) |
1598 | if changeInterp then |
1599 | local qx, qy, qz, qw = mathEulerToQuaternion(xRot,yRot,zRot) |
1600 | component.networkInterpolators.quaternion:setQuaternion(qx, qy, qz, qw) |
1601 | component.networkInterpolators.position:setPosition(x,y,z) |
1602 | end |
1603 | 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 |
1616 | function Vehicle:setWorldPositionQuaternion(x,y,z, qx,qy,qz,qw, i, changeInterp) |
1617 | local component = self.components[i] |
1618 | setWorldTranslation(component.node, x,y,z) |
1619 | setWorldQuaternion(component.node, qx,qy,qz,qw) |
1620 | if changeInterp then |
1621 | component.networkInterpolators.quaternion:setQuaternion(qx, qy, qz, qw) |
1622 | component.networkInterpolators.position:setPosition(x,y,z) |
1623 | end |
1624 | end |
unselectVehicle
DescriptionDefinitionunselectVehicle()Code
2059 | function Vehicle:unselectVehicle() |
2060 | self.selectionObject.isSelected = false |
2061 | SpecializationUtil.raiseEvent(self, "onUnselect") |
2062 | |
2063 | self:requestActionEventUpdate() |
2064 | end |
update
DescriptionDefinitionupdate()Code
1268 | function Vehicle:update(dt) |
1269 | -- states |
1270 | local isActive = self:getIsActive() |
1271 | self.isActive = isActive |
1272 | local isActiveForInput = self:getIsActiveForInput() |
1273 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
1274 | --local isActiveForInput = isActive and not g_gui:getIsGuiVisible() and not g_currentMission.isPlayerFrozen |
1275 | local isSelected = self:getIsSelected() |
1276 | |
1277 | self.updateLoopIndex = g_updateLoopIndex |
1278 | |
1279 | SpecializationUtil.raiseEvent(self, "onPreUpdate", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1280 | |
1281 | -- interpolation of position |
1282 | if not self.isServer and self.synchronizePosition then |
1283 | self.networkTimeInterpolator:update(dt) |
1284 | local interpolationAlpha = self.networkTimeInterpolator:getAlpha() |
1285 | for i, component in pairs(self.components) do |
1286 | if not component.isStatic then |
1287 | local posX, posY, posZ = component.networkInterpolators.position:getInterpolatedValues(interpolationAlpha) |
1288 | local quatX, quatY, quatZ, quatW = component.networkInterpolators.quaternion:getInterpolatedValues(interpolationAlpha) |
1289 | self:setWorldPositionQuaternion(posX, posY, posZ, quatX, quatY, quatZ, quatW, i, false) |
1290 | end |
1291 | end |
1292 | |
1293 | if self.networkTimeInterpolator:isInterpolating() then |
1294 | self:raiseActive() |
1295 | end |
1296 | end |
1297 | SpecializationUtil.raiseEvent(self, "onUpdateInterpolation", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1298 | |
1299 | self:updateVehicleSpeed(dt) |
1300 | |
1301 | if self.actionEventUpdateRequested then |
1302 | self:updateActionEvents() |
1303 | end |
1304 | |
1305 | SpecializationUtil.raiseEvent(self, "onUpdate", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1306 | if Vehicle.debuggingActive then |
1307 | SpecializationUtil.raiseEvent(self, "onUpdateDebug", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1308 | end |
1309 | SpecializationUtil.raiseEvent(self, "onPostUpdate", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1310 | |
1311 | if self.vehicleCharacter ~= nil then |
1312 | self.vehicleCharacter:setDirty(true) |
1313 | end |
1314 | |
1315 | if self.firstTimeRun and self.isMassDirty then |
1316 | self.isMassDirty = false |
1317 | self:updateMass() |
1318 | end |
1319 | |
1320 | self.firstTimeRun = true |
1321 | |
1322 | if self.isServer then |
1323 | if not getIsSleeping(self.rootNode) then |
1324 | self:raiseActive() |
1325 | end |
1326 | end |
1327 | |
1328 | VehicleDebug.updateDebug(self) |
1329 | end |
updateActionEvents
DescriptionDefinitionupdateActionEvents()Code
1886 | function Vehicle:updateActionEvents() |
1887 | local rootVehicle = self:getRootVehicle() |
1888 | rootVehicle:registerActionEvents() |
1889 | end |
updateEnd
DescriptionDefinitionupdateEnd()Code
1402 | function Vehicle:updateEnd(dt) |
1403 | local isActiveForInput = self:getIsActiveForInput() |
1404 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
1405 | local isSelected = self:getIsSelected() |
1406 | |
1407 | SpecializationUtil.raiseEvent(self, "onUpdateEnd", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1408 | end |
updateMass
DescriptionDefinitionupdateMass()Code
2202 | function Vehicle:updateMass() |
2203 | self.serverMass = 0 |
2204 | |
2205 | for _, component in ipairs(self.components) do |
2206 | if component.defaultMass == nil then |
2207 | if component.isDynamic then |
2208 | component.defaultMass = getMass(component.node) |
2209 | end |
2210 | component.mass = component.defaultMass |
2211 | end |
2212 | |
2213 | local mass = self:getAdditionalComponentMass(component) |
2214 | component.mass = component.defaultMass + mass |
2215 | |
2216 | -- only update physically mass if difference to last mass is greater 20kg |
2217 | if self.isServer and component.isDynamic and math.abs(component.lastMass-component.mass) > 0.02 then |
2218 | setMass(component.node, component.mass) |
2219 | component.lastMass = component.mass |
2220 | end |
2221 | self.serverMass = self.serverMass + component.mass |
2222 | end |
2223 | end |
updateSelectableObjects
DescriptionDefinitionupdateSelectableObjects()Code
2010 | function Vehicle:updateSelectableObjects() |
2011 | self.selectableObjects = {} |
2012 | if self == self:getRootVehicle() then |
2013 | self:registerSelectableObjects(self.selectableObjects) |
2014 | end |
2015 | end |
updateTick
DescriptionupdateTickDefinition
updateTick(float dt)Arguments
float | dt | time since last call in ms |
1334 | function Vehicle:updateTick(dt) |
1335 | |
1336 | local isActive = self:getIsActive() |
1337 | local isActiveForInput = self:getIsActiveForInput() |
1338 | local isActiveForInputIgnoreSelection = self:getIsActiveForInput(true) |
1339 | local isSelected = self:getIsSelected() |
1340 | |
1341 | self.wasTooFast = false |
1342 | if self.isServer then |
1343 | if self.synchronizePosition then |
1344 | local hasOwner = self:getOwner() ~= nil |
1345 | for i=1, table.getn(self.components) do |
1346 | local component = self.components[i] |
1347 | if not component.isStatic then |
1348 | local x,y,z = getWorldTranslation(component.node) |
1349 | local x_rot,y_rot,z_rot=getWorldRotation(component.node) |
1350 | local sentTranslation = component.sentTranslation |
1351 | local sentRotation = component.sentRotation |
1352 | if hasOwner or |
1353 | math.abs(x-sentTranslation[1]) > 0.005 or |
1354 | math.abs(y-sentTranslation[2]) > 0.005 or |
1355 | math.abs(z-sentTranslation[3]) > 0.005 or |
1356 | math.abs(x_rot-sentRotation[1]) > 0.1 or |
1357 | math.abs(y_rot-sentRotation[2]) > 0.1 or |
1358 | math.abs(z_rot-sentRotation[3]) > 0.1 |
1359 | then |
1360 | self:raiseDirtyFlags(self.vehicleDirtyFlag) |
1361 | sentTranslation[1] = x |
1362 | sentTranslation[2] = y |
1363 | sentTranslation[3] = z |
1364 | sentRotation[1] = x_rot |
1365 | sentRotation[2] = y_rot |
1366 | sentRotation[3] = z_rot |
1367 | |
1368 | self.lastMoveTime = g_currentMission.time |
1369 | end |
1370 | end |
1371 | end |
1372 | end |
1373 | |
1374 | -- is the vehicle sunken in the water? |
1375 | self.showTailwaterDepthWarning = false |
1376 | if not self.isBroken and not g_gui:getIsGuiVisible() then |
1377 | local tailwaterDepth = self:getTailwaterDepth() |
1378 | if tailwaterDepth > self.thresholdTailwaterDepthWarning then |
1379 | self.showTailwaterDepthWarning = true |
1380 | if tailwaterDepth > self.thresholdTailwaterDepth then |
1381 | self:setBroken() |
1382 | end |
1383 | end |
1384 | end |
1385 | |
1386 | local rootAttacherVehicle = self:getRootVehicle() |
1387 | if rootAttacherVehicle ~= nil and rootAttacherVehicle ~= self then |
1388 | rootAttacherVehicle.showTailwaterDepthWarning = rootAttacherVehicle.showTailwaterDepthWarning or self.showTailwaterDepthWarning |
1389 | end |
1390 | end |
1391 | |
1392 | if self:getIsOperating() then |
1393 | self:setOperatingTime(self.operatingTime + dt) |
1394 | end |
1395 | |
1396 | SpecializationUtil.raiseEvent(self, "onUpdateTick", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1397 | SpecializationUtil.raiseEvent(self, "onPostUpdateTick", dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1398 | end |
updateVehicleSpeed
DescriptionDefinitionupdateVehicleSpeed()Code
1186 | function Vehicle:updateVehicleSpeed(dt) |
1187 | -- On servers, the physics state is only change |
1188 | if self.firstTimeRun and not self.components[1].isStatic then |
1189 | local speedReal = 0 |
1190 | local movedDistance = 0 |
1191 | local movingDirection = 0 |
1192 | local signedSpeedReal = 0 |
1193 | if not self.isServer or self.components[1].isKinematic then |
1194 | if not self.isServer and self.synchronizePosition then |
1195 | -- Client code can use the interpolation information that is driving the position change |
1196 | local interpPos = self.components[1].networkInterpolators.position |
1197 | local dx, dy, dz = 0, 0, 0 |
1198 | if self.networkTimeInterpolator:isInterpolating() then |
1199 | dx, dy, dz = worldDirectionToLocal(self.components[1].node, interpPos.targetPositionX-interpPos.lastPositionX, interpPos.targetPositionY-interpPos.lastPositionY, interpPos.targetPositionZ-interpPos.lastPositionZ) |
1200 | end |
1201 | |
1202 | if dz > 0.001 then |
1203 | movingDirection = 1 |
1204 | elseif dz < -0.001 then |
1205 | movingDirection = -1 |
1206 | end |
1207 | speedReal = MathUtil.vector3Length(dx, dy, dz) / self.networkTimeInterpolator.interpolationDuration |
1208 | signedSpeedReal = speedReal * (dz >= 0 and 1 or -1) |
1209 | movedDistance = speedReal * dt |
1210 | else |
1211 | -- Client or kinematic code can't use physics velocity, so calculate based on the position change |
1212 | local x,y,z = getWorldTranslation(self.components[1].node) |
1213 | if self.lastPosition == nil then |
1214 | self.lastPosition = {x,y,z} |
1215 | end |
1216 | local dx, dy, dz = worldDirectionToLocal(self.components[1].node, x-self.lastPosition[1], y-self.lastPosition[2], z-self.lastPosition[3]) |
1217 | self.lastPosition[1], self.lastPosition[2], self.lastPosition[3] = x, y, z |
1218 | if dz > 0.001 then |
1219 | movingDirection = 1 |
1220 | elseif dz < -0.001 then |
1221 | movingDirection = -1 |
1222 | end |
1223 | movedDistance = MathUtil.vector3Length(dx, dy, dz) |
1224 | speedReal = movedDistance / dt |
1225 | signedSpeedReal = speedReal * (dz >= 0 and 1 or -1) |
1226 | end |
1227 | elseif self.components[1].isDynamic then |
1228 | -- Dynamic objects on server use velocity provided by the physics engine |
1229 | local vx, vy, vz = getLocalLinearVelocity(self.components[1].node) |
1230 | speedReal = MathUtil.vector3Length(vx, vy, vz)*0.001 |
1231 | movedDistance = speedReal*g_physicsDt |
1232 | signedSpeedReal = speedReal * (vz >= 0 and 1 or -1) |
1233 | if vz > 0.001 then |
1234 | movingDirection = 1 |
1235 | elseif vz < -0.001 then |
1236 | movingDirection = -1 |
1237 | end |
1238 | end |
1239 | |
1240 | if self.isServer then |
1241 | -- On the server, the velocity only changes when a physics simulation step is performed (thus only update the acceleration when something was simulated) |
1242 | if g_physicsDtNonInterpolated > 0 then |
1243 | self.lastSpeedAcceleration = (speedReal*movingDirection - self.lastSpeedReal*self.movingDirection) / g_physicsDtNonInterpolated |
1244 | end |
1245 | else |
1246 | -- On the client, the position is driven by the interpolation (updated with dt) |
1247 | self.lastSpeedAcceleration = (speedReal*movingDirection - self.lastSpeedReal*self.movingDirection) / dt |
1248 | end |
1249 | |
1250 | -- Update smooth values (use less smoothing on the server) |
1251 | if self.isServer then |
1252 | self.lastSpeed = self.lastSpeed*0.5 + speedReal*0.5 |
1253 | self.lastSignedSpeed = self.lastSignedSpeed*0.5 + signedSpeedReal*0.5 |
1254 | else |
1255 | self.lastSpeed = self.lastSpeed*0.9 + speedReal*0.1 |
1256 | self.lastSignedSpeed = self.lastSignedSpeed*0.9 + signedSpeedReal*0.1 |
1257 | end |
1258 | |
1259 | self.lastSpeedReal = speedReal |
1260 | self.lastSignedSpeedReal = signedSpeedReal |
1261 | self.movingDirection = movingDirection |
1262 | self.lastMovedDistance = movedDistance |
1263 | end |
1264 | end |
writeStream
DescriptionCalled on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
integer | streamId | stream ID |
table | connection | connection |
1037 | function Vehicle:writeStream(streamId, connection) |
1038 | Vehicle:superClass().writeStream(self, streamId) |
1039 | streamWriteString(streamId, NetworkUtil.convertToNetworkFilename(self.configFileName)) |
1040 | streamWriteString(streamId, self.typeName) |
1041 | |
1042 | local numConfigs = 0 |
1043 | for _,_ in pairs(self.configurations) do |
1044 | numConfigs = numConfigs + 1 |
1045 | end |
1046 | |
1047 | streamWriteUIntN(streamId, numConfigs, ConfigurationUtil.SEND_NUM_BITS) |
1048 | for configName, configId in pairs(self.configurations) do |
1049 | local configNameId = g_configurationManager:getConfigurationIndexByName(configName) |
1050 | streamWriteUIntN(streamId, configNameId-1, ConfigurationUtil.SEND_NUM_BITS) |
1051 | streamWriteUInt16(streamId, configId-1) |
1052 | end |
1053 | |
1054 | local numBoughtConfigs = 0 |
1055 | for _,_ in pairs(self.boughtConfigurations) do |
1056 | numBoughtConfigs = numBoughtConfigs + 1 |
1057 | end |
1058 | |
1059 | streamWriteUIntN(streamId, numBoughtConfigs, ConfigurationUtil.SEND_NUM_BITS) |
1060 | for configName, configIds in pairs(self.boughtConfigurations) do |
1061 | local numBoughtConfigIds = 0 |
1062 | for _,_ in pairs(configIds) do |
1063 | numBoughtConfigIds = numBoughtConfigIds + 1 |
1064 | end |
1065 | local configNameId = g_configurationManager:getConfigurationIndexByName(configName) |
1066 | streamWriteUIntN(streamId, configNameId-1, ConfigurationUtil.SEND_NUM_BITS) |
1067 | streamWriteUInt16(streamId, numBoughtConfigIds) |
1068 | for id, _ in pairs(configIds) do |
1069 | streamWriteUInt16(streamId, id-1) |
1070 | end |
1071 | end |
1072 | |
1073 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
1074 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
1075 | for i=1, table.getn(self.components) do |
1076 | local component = self.components[i] |
1077 | local x,y,z = getWorldTranslation(component.node) |
1078 | local x_rot,y_rot,z_rot = getWorldRotation(component.node) |
1079 | NetworkUtil.writeCompressedWorldPosition(streamId, x, paramsXZ) |
1080 | NetworkUtil.writeCompressedWorldPosition(streamId, y, paramsY) |
1081 | NetworkUtil.writeCompressedWorldPosition(streamId, z, paramsXZ) |
1082 | NetworkUtil.writeCompressedAngle(streamId, x_rot) |
1083 | NetworkUtil.writeCompressedAngle(streamId, y_rot) |
1084 | NetworkUtil.writeCompressedAngle(streamId, z_rot) |
1085 | end |
1086 | |
1087 | streamWriteFloat32(streamId, self.serverMass) |
1088 | streamWriteUInt16(streamId, self.age) |
1089 | streamWriteFloat32(streamId, self.operatingTime) |
1090 | streamWriteInt32(streamId, self.price) |
1091 | streamWriteUIntN(streamId, self.propertyState, 2) |
1092 | |
1093 | SpecializationUtil.raiseEvent(self, "onWriteStream", streamId, connection) |
1094 | 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 |
1147 | function Vehicle:writeUpdateStream(streamId, connection, dirtyMask) |
1148 | if not connection.isServer then |
1149 | if streamWriteBool(streamId, bitAND(dirtyMask, self.vehicleDirtyFlag) ~= 0) then |
1150 | |
1151 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
1152 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
1153 | for i=1, table.getn(self.components) do |
1154 | local component = self.components[i] |
1155 | if not component.isStatic then |
1156 | local x,y,z = getWorldTranslation(component.node) |
1157 | local x_rot,y_rot,z_rot = getWorldRotation(component.node) |
1158 | NetworkUtil.writeCompressedWorldPosition(streamId, x, paramsXZ) |
1159 | NetworkUtil.writeCompressedWorldPosition(streamId, y, paramsY) |
1160 | NetworkUtil.writeCompressedWorldPosition(streamId, z, paramsXZ) |
1161 | NetworkUtil.writeCompressedAngle(streamId, x_rot) |
1162 | NetworkUtil.writeCompressedAngle(streamId, y_rot) |
1163 | NetworkUtil.writeCompressedAngle(streamId, z_rot) |
1164 | end |
1165 | end |
1166 | SpecializationUtil.raiseEvent(self, "onWritePositionUpdateStream", streamId, connection, dirtyMask) |
1167 | end |
1168 | end |
1169 | |
1170 | if Vehicle.debugNetworkUpdate then |
1171 | print("-------------------------------------------------------------") |
1172 | print(self.configFileName) |
1173 | for _, spec in ipairs(self.eventListeners["writeUpdateStream"]) do |
1174 | local className = ClassUtil.getClassName(spec) |
1175 | local startBits = streamGetWriteOffset(streamId) |
1176 | spec["writeUpdateStream"](self, streamId, connection, dirtyMask) |
1177 | print(" "..tostring(className).." Wrote " .. streamGetWriteOffset(streamId)-startBits .. " bits") |
1178 | end |
1179 | else |
1180 | SpecializationUtil.raiseEvent(self, "onWriteUpdateStream", streamId, connection, dirtyMask) |
1181 | end |
1182 | end |