LUADOC - Farming Simulator 19

Script v1.7.1.0

Engine v1.7.1.0

Foundation Reference

Cylindered

Description
Specialization for vehicles with dependent movable parts (e.g. cylinders)
Functions

actionEventInput

Description
Definition
actionEventInput()
Code
2962function Cylindered.actionEventInput(self, actionName, inputValue, callbackState, isAnalog, isMouse)
2963 local spec = self.spec_cylindered
2964 local tool = spec.movingTools[callbackState]
2965
2966 if tool ~= nil then
2967 local move
2968 if tool.invertAxis then
2969 move = -inputValue
2970 else
2971 move = inputValue
2972 end
2973
2974 move = move * g_gameSettings:getValue(GameSettings.SETTING.VEHICLE_ARM_SENSITIVITY)
2975 if isMouse then
2976 -- revert dt scaling for mouse input
2977 move = move * 16.666 / g_currentDt * tool.mouseSpeedFactor;
2978
2979 -- allow only the input of the highest mouse axis value
2980 -- lock the move of the lower mouse axis value until it is higher than 0.75 or the value is higher than the doubled value of the other tool
2981 if tool.moveLocked then
2982 if math.abs(inputValue) < 0.75 then
2983 if math.abs(move) > math.abs(tool.lockTool.move) * 2 then
2984 tool.moveLocked = false
2985 else
2986 move = 0
2987 end
2988 else
2989 tool.moveLocked = false
2990 end
2991 else
2992 local checkOtherTools = function(tools)
2993 for tool2Index, tool2 in ipairs(tools) do
2994 if tool2Index ~= callbackState then
2995 if tool2.move ~= nil and tool2.move ~= 0 then
2996 if math.abs(move) > math.abs(tool2.move) then
2997 tool2.move = 0
2998 tool2.moveToSend = 0
2999 tool2.moveLocked = true
3000 tool2.lockTool = tool
3001 else
3002 move = 0
3003 tool.moveLocked = true
3004 tool.lockTool = tool2
3005 end
3006 end
3007 end
3008 end
3009 end
3010
3011 checkOtherTools(spec.movingTools)
3012
3013 if self.getAttachedImplements ~= nil then
3014 for _, implement in pairs(self:getAttachedImplements()) do
3015 local vehicle = implement.object
3016 if vehicle.spec_cylindered ~= nil then
3017 checkOtherTools(vehicle.spec_cylindered.movingTools)
3018 end
3019 end
3020 end
3021 end
3022 end
3023
3024 if move ~= tool.move then
3025 tool.move = move
3026 end
3027
3028 if tool.move ~= tool.moveToSend then
3029 tool.moveToSend = tool.move
3030 self:raiseDirtyFlags(spec.cylinderedInputDirtyFlag)
3031 end
3032 end
3033end

allowLoadMovingToolStates

Description
Returns if loading of moving tool stats from savegame is allowed
Definition
allowLoadMovingToolStates()
Return Values
booleanisAllowedis allowed
Code
2012function Cylindered:allowLoadMovingToolStates(superFunc)
2013 return true
2014end

getAdditionalSchemaText

Description
Definition
getAdditionalSchemaText()
Code
2256function Cylindered:getAdditionalSchemaText(superFunc)
2257 local t = superFunc(self)
2258 if self.isClient then
2259 if self:getIsActiveForInput(true) then
2260 local spec = self.spec_cylindered
2261 if #spec.controlGroupNames > 1 then
2262 if t ~= nil then
2263 t = t .. " "
2264 end
2265
2266 t = tostring(spec.currentControlGroupIndex)
2267 end
2268 end
2269 end
2270
2271 return t
2272end

getDischargeNodeEmptyFactor

Description
Definition
getDischargeNodeEmptyFactor()
Code
2128function Cylindered:getDischargeNodeEmptyFactor(superFunc, dischargeNode)
2129 if dischargeNode.movingToolActivation == nil then
2130 return superFunc(self, dischargeNode)
2131 else
2132 local spec = self.spec_cylindered
2133 local movingToolActivation = dischargeNode.movingToolActivation
2134
2135 local currentSpeed = superFunc(self, dischargeNode)
2136
2137 local movingTool = spec.nodesToMovingTools[movingToolActivation.node]
2138 local state = Cylindered.getMovingToolState(self, movingTool)
2139 if movingToolActivation.isInverted then
2140 state = math.abs(state-1)
2141 end
2142
2143 state = math.max(state-movingToolActivation.openOffset, 0) / movingToolActivation.openOffsetInv
2144 local speedFactor = MathUtil.clamp(state/movingToolActivation.openFactor, 0, 1)
2145
2146 return currentSpeed * speedFactor
2147 end
2148end

getIsDynamicMountGrabOpened

Description
Definition
getIsDynamicMountGrabOpened()
Code
2212function Cylindered:getIsDynamicMountGrabOpened(superFunc, grab)
2213 local isActive = superFunc(self, grab)
2214 if not isActive or grab.movingToolActivation == nil then
2215 return isActive
2216 end
2217
2218
2219 local spec = self.spec_cylindered
2220 local movingToolActivation = grab.movingToolActivation
2221 local movingTool = spec.nodesToMovingTools[movingToolActivation.node]
2222 local state = Cylindered.getMovingToolState(self, movingTool)
2223 if movingToolActivation.isInverted then
2224 state = math.abs(state-1)
2225 end
2226
2227 return state > movingToolActivation.openFactor
2228end

getIsMovingToolActive

Description
Definition
getIsMovingToolActive()
Code
2030function Cylindered:getIsMovingToolActive(movingTool)
2031 return movingTool.isActive
2032end

getMovingPartByNode

Description
Definition
getMovingPartByNode()
Code
2024function Cylindered:getMovingPartByNode(node)
2025 return self.spec_cylindered.nodesToMovingParts[node]
2026end

getMovingToolByNode

Description
Definition
getMovingToolByNode()
Code
2018function Cylindered:getMovingToolByNode(node)
2019 return self.spec_cylindered.nodesToMovingTools[node]
2020end

getMovingToolDashboardState

Description
Definition
getMovingToolDashboardState()
Code
3037function Cylindered.getMovingToolDashboardState(self, dashboard)
3038 local vehicle = self
3039 if dashboard.attacherJointIndex ~= nil then
3040 local implement = self:getImplementFromAttacherJointIndex(dashboard.attacherJointIndex)
3041 if implement ~= nil then
3042 vehicle = implement.object
3043 else
3044 vehicle = nil
3045 end
3046 end
3047
3048 if vehicle ~= nil then
3049 local spec = vehicle.spec_cylindered
3050 if spec ~= nil then
3051 for _, movingTool in ipairs(spec.movingTools) do
3052 if movingTool.axis == dashboard.axis then
3053 return (movingTool.move + 1) / 2
3054 end
3055 end
3056 end
3057 end
3058
3059 return 0.5
3060end

getMovingToolState

Description
Returns moving tool state
Definition
getMovingToolState(table tool)
Arguments
tabletooltool
Return Values
floatstatestate of moving tool [0..1]
Code
2511function Cylindered.getMovingToolState(self, tool)
2512 local state = 0
2513 if tool.rotMax ~= nil and tool.rotMin ~= nil then
2514 state = (tool.curRot[tool.rotationAxis]-tool.rotMin) / (tool.rotMax-tool.rotMin)
2515 else
2516 if tool.transMax ~= nil and tool.transMin ~= nil then
2517 state = (tool.curTrans[tool.translationAxis]-tool.transMin) / (tool.transMax-tool.transMin)
2518 end
2519 end
2520
2521 return state
2522end

getShovelNodeIsActive

Description
Definition
getShovelNodeIsActive()
Code
2172function Cylindered:getShovelNodeIsActive(superFunc, shovelNode)
2173 local isActive = superFunc(self, shovelNode)
2174 if not isActive or shovelNode.movingToolActivation == nil then
2175 return isActive
2176 end
2177
2178
2179 local spec = self.spec_cylindered
2180 local movingToolActivation = shovelNode.movingToolActivation
2181 local movingTool = spec.nodesToMovingTools[movingToolActivation.node]
2182 local state = Cylindered.getMovingToolState(self, movingTool)
2183 if movingToolActivation.isInverted then
2184 state = math.abs(state-1)
2185 end
2186
2187 return state > movingToolActivation.openFactor
2188end

getWearMultiplier

Description
Returns current wear multiplier
Definition
getWearMultiplier()
Return Values
floatwearMultipliercurrent wear multiplier
Code
2277function Cylindered:getWearMultiplier(superFunc)
2278 local spec = self.spec_cylindered
2279 local multiplier = superFunc(self)
2280
2281 if spec.isHydraulicSamplePlaying then
2282 multiplier = multiplier + self:getWorkWearMultiplier()
2283 end
2284
2285 return multiplier
2286end

isDetachAllowed

Description
Returns true if detach is allowed
Definition
isDetachAllowed()
Return Values
booleandetachAlloweddetach is allowed
Code
2037function Cylindered:isDetachAllowed(superFunc)
2038 local spec = self.spec_cylindered
2039 if spec.detachLockNodes ~= nil then
2040 for entry, data in pairs(spec.detachLockNodes) do
2041 local node = entry.node
2042 local rot = {getRotation(node)}
2043
2044 if data.detachingRotMinLimit ~= nil and rot[entry.rotationAxis] < data.detachingRotMinLimit then
2045 return false, nil
2046 end
2047 if data.detachingRotMaxLimit ~= nil and rot[entry.rotationAxis] > data.detachingRotMaxLimit then
2048 return false, nil
2049 end
2050
2051 local trans = {getTranslation(node)}
2052 if data.detachingTransMinLimit ~= nil and trans[entry.translationAxis] < data.detachingTransMinLimit then
2053 return false, nil
2054 end
2055 if data.detachingTransMaxLimit ~= nil and trans[entry.translationAxis] > data.detachingTransMaxLimit then
2056 return false, nil
2057 end
2058 end
2059 end
2060
2061 return superFunc(self)
2062end

limitInterpolator

Description
Definition
limitInterpolator()
Code
2896function Cylindered.limitInterpolator(first, second, alpha)
2897 local oneMinusAlpha = 1-alpha
2898
2899 local rotMin = nil
2900 local rotMax = nil
2901 local transMin = nil
2902 local transMax = nil
2903
2904 if first.rotMin ~= nil and second.rotMin ~= nil then
2905 rotMin = first.rotMin*alpha + second.rotMin*oneMinusAlpha
2906 end
2907 if first.rotMax ~= nil and second.rotMax ~= nil then
2908 rotMax = first.rotMax*alpha + second.rotMax*oneMinusAlpha
2909 end
2910 if first.transMin ~= nil and second.transMin ~= nil then
2911 transMin = first.minTrans*alpha + second.transMin*oneMinusAlpha
2912 end
2913 if first.transMax ~= nil and second.transMax ~= nil then
2914 transMax = first.transMax*alpha + second.transMax*oneMinusAlpha
2915 end
2916
2917 return rotMin, rotMax, transMin, transMax
2918end

loadCopyLocalDirectionParts

Description
Load copy local direction parts from xml
Definition
loadCopyLocalDirectionParts(integer xmlFile, string baseName, table entry)
Arguments
integerxmlFileid of xml object
stringbaseNamebase name
tableentryentry to add
Code
1900function Cylindered:loadCopyLocalDirectionParts(xmlFile, baseName, entry)
1901 entry.copyLocalDirectionParts = {}
1902 local j = 0
1903 while true do
1904 local refBaseName = baseName..string.format(".copyLocalDirectionPart(%d)", j)
1905 if not hasXMLProperty(xmlFile, refBaseName) then
1906 break
1907 end
1908
1909 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, refBaseName.."#index", refBaseName.."#node") --FS15 to FS17
1910
1911 local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#node"), self.i3dMappings)
1912 if node ~= nil then
1913 local copyLocalDirectionPart = {}
1914 copyLocalDirectionPart.node = node
1915 copyLocalDirectionPart.dirScale = StringUtil.getVectorNFromString(getXMLString(xmlFile, refBaseName.."#dirScale"), 3)
1916 copyLocalDirectionPart.upScale = StringUtil.getVectorNFromString(getXMLString(xmlFile, refBaseName.."#upScale"), 3)
1917
1918 self:loadDependentComponentJoints(xmlFile, refBaseName, copyLocalDirectionPart)
1919
1920 table.insert(entry.copyLocalDirectionParts, copyLocalDirectionPart)
1921 end
1922 j = j + 1
1923 end
1924end

loadDependentAnimations

Description
Definition
loadDependentAnimations()
Code
1852function Cylindered:loadDependentAnimations(xmlFile, baseName, entry)
1853 entry.dependentAnimations = {}
1854
1855 local i = 0
1856 while true do
1857 local baseKey = string.format("%s.dependentAnimation(%d)", baseName, i)
1858 if not hasXMLProperty(xmlFile, baseKey) then
1859 break
1860 end
1861
1862 local animationName = getXMLString(self.xmlFile, baseKey.."#name")
1863 if animationName ~= nil then
1864 local dependentAnimation = {}
1865 dependentAnimation.name = animationName
1866 dependentAnimation.lastPos = 0
1867
1868 dependentAnimation.translationAxis = getXMLInt(self.xmlFile, baseKey.."#translationAxis")
1869 dependentAnimation.rotationAxis = getXMLInt(self.xmlFile, baseKey.."#rotationAxis")
1870
1871 dependentAnimation.node = entry.node
1872 local useTranslatingPartIndex = getXMLInt(self.xmlFile, baseKey.."#useTranslatingPartIndex")
1873 if useTranslatingPartIndex ~= nil then
1874 if entry.translatingParts[useTranslatingPartIndex] ~= nil then
1875 dependentAnimation.node = entry.translatingParts[useTranslatingPartIndex].node
1876 end
1877 end
1878
1879 dependentAnimation.minValue = getXMLFloat(self.xmlFile, baseKey.."#minValue")
1880 dependentAnimation.maxValue = getXMLFloat(self.xmlFile, baseKey.."#maxValue")
1881 if dependentAnimation.rotationAxis ~= nil then
1882 dependentAnimation.minValue = MathUtil.degToRad(dependentAnimation.minValue)
1883 dependentAnimation.maxValue = MathUtil.degToRad(dependentAnimation.maxValue)
1884 end
1885
1886 dependentAnimation.invert = Utils.getNoNil(getXMLBool(self.xmlFile, baseKey.."#invert"), false)
1887
1888 table.insert(entry.dependentAnimations, dependentAnimation)
1889 end
1890
1891 i = i + 1
1892 end
1893end

loadDependentAttacherJoints

Description
Load attacher joints from xml
Definition
loadDependentAttacherJoints(integer xmlFile, string baseName, table entry)
Arguments
integerxmlFileid of xml object
stringbaseNamebase name
tableentryentry to add
Code
1760function Cylindered:loadDependentAttacherJoints(xmlFile, baseName, entry)
1761 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName.."#jointIndices", baseName..".attacherJoint#jointIndices") --FS15 to FS17
1762
1763 local indices = StringUtil.getVectorNFromString(getXMLString(xmlFile, baseName.. ".attacherJoint#jointIndices"))
1764 if indices ~= nil then
1765 entry.attacherJoints = {}
1766
1767 local availableAttacherJoints
1768 if self.getAttacherJoints ~= nil then
1769 availableAttacherJoints = self:getAttacherJoints()
1770 end
1771 if availableAttacherJoints ~= nil then
1772 for i=1, table.getn(indices) do
1773 if availableAttacherJoints[indices[i]] ~= nil then
1774 table.insert(entry.attacherJoints, availableAttacherJoints[indices[i]])
1775 end
1776 end
1777 end
1778 end
1779
1780 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName.."#inputAttacherJoint", baseName..".inputAttacherJoint#value") --FS15 to FS17
1781
1782 entry.inputAttacherJoint = Utils.getNoNil(getXMLBool(xmlFile, baseName.. ".inputAttacherJoint#value"), false)
1783end

loadDependentComponentJoints

Description
Load component joints from xml
Definition
loadDependentComponentJoints(integer xmlFile, string baseName, table entry)
Arguments
integerxmlFileid of xml object
stringbaseNamebase name
tableentryentry to add
Code
1709function Cylindered:loadDependentComponentJoints(xmlFile, baseName, entry)
1710 if not self.isServer then
1711 return
1712 end
1713
1714 entry.componentJoints = {}
1715
1716 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName.."#componentJointIndex", baseName..".componentJoint#index") --FS15 to FS17
1717 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName.."#anchorActor", baseName..".componentJoint#anchorActor") --FS15 to FS17
1718
1719 local i = 0
1720 while true do
1721 local key = baseName .. string.format(".componentJoint(%d)", i)
1722 if not hasXMLProperty(xmlFile, key) then
1723 break
1724 end
1725 local index = getXMLInt(xmlFile, key .. "#index")
1726 if index ~= nil and self.componentJoints[index] ~= nil then
1727 local anchorActor = Utils.getNoNil(getXMLInt(xmlFile, key.."#anchorActor"), 0)
1728
1729 local componentJoint = self.componentJoints[index]
1730
1731 local jointEntry = {}
1732 jointEntry.componentJoint = componentJoint
1733 jointEntry.anchorActor = anchorActor
1734 jointEntry.index = index
1735
1736 local jointNode = componentJoint.jointNode
1737 if jointEntry.anchorActor == 1 then
1738 jointNode = componentJoint.jointNodeActor1
1739 end
1740
1741 local node = self.components[componentJoint.componentIndices[2]].node
1742 jointEntry.x, jointEntry.y, jointEntry.z = localToLocal(node, jointNode, 0,0,0)
1743 jointEntry.upX, jointEntry.upY, jointEntry.upZ = localDirectionToLocal(node, jointNode, 0,1,0)
1744 jointEntry.dirX, jointEntry.dirY, jointEntry.dirZ = localDirectionToLocal(node, jointNode, 0,0,1)
1745
1746 table.insert(entry.componentJoints, jointEntry)
1747 else
1748 g_logManager:xmlWarning(self.configFileName, "Invalid index for '%s'", key)
1749 end
1750
1751 i = i + 1
1752 end
1753end

loadDependentMovingTools

Description
Load dependent moving tools from xml
Definition
loadDependentMovingTools(integer xmlFile, string baseName, table entry)
Arguments
integerxmlFileid of xml object
stringbaseNamebase name
tableentryentry to add
Code
1621function Cylindered:loadDependentMovingTools(xmlFile, baseName, entry)
1622 entry.dependentMovingTools = {}
1623
1624 local j = 0
1625 while true do
1626 local refBaseName = baseName..string.format(".dependentMovingTool(%d)", j)
1627 if not hasXMLProperty(xmlFile, refBaseName) then
1628 break
1629 end
1630
1631 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, refBaseName.."#index", refBaseName.."#index") --FS17 to FS19
1632
1633 local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#node"), self.i3dMappings)
1634 local speedScale = getXMLFloat(xmlFile, refBaseName.."#speedScale")
1635 local requiresMovement = Utils.getNoNil(getXMLBool(xmlFile, refBaseName.."#requiresMovement"), false)
1636
1637 local rotationBasedLimits = AnimCurve:new(Cylindered.limitInterpolator)
1638 local found = false
1639 local i = 0
1640 while true do
1641 local key = string.format("%s.limit(%d)", refBaseName..".rotationBasedLimits", i)
1642 if not hasXMLProperty(xmlFile, key) then
1643 break
1644 end
1645
1646 local keyFrame = self:loadRotationBasedLimits(xmlFile, key, entry)
1647 if keyFrame ~= nil then
1648 rotationBasedLimits:addKeyframe(keyFrame)
1649 found = true
1650 end
1651 i = i + 1
1652 end
1653 if not found then
1654 rotationBasedLimits = nil
1655 end
1656
1657 local minTransLimits = getXMLString(xmlFile, refBaseName.."#minTransLimits")
1658 local maxTransLimits = getXMLString(xmlFile, refBaseName.."#maxTransLimits")
1659 local minRotLimits = getXMLString(xmlFile, refBaseName.."#minRotLimits")
1660 local maxRotLimits = getXMLString(xmlFile, refBaseName.."#maxRotLimits")
1661 if node ~= nil and (rotationBasedLimits ~= nil or speedScale ~= nil or minTransLimits ~= nil or maxTransLimits ~= nil or minRotLimits ~= nil or maxRotLimits ~= nil) then
1662 local dependentTool = {}
1663 dependentTool.node = node
1664 dependentTool.rotationBasedLimits = rotationBasedLimits
1665 dependentTool.speedScale = speedScale
1666 dependentTool.requiresMovement = requiresMovement
1667 dependentTool.minTransLimits = StringUtil.getVectorNFromString(minTransLimits, 2)
1668 dependentTool.maxTransLimits = StringUtil.getVectorNFromString(maxTransLimits, 2)
1669 dependentTool.minRotLimits = StringUtil.getRadiansFromString(minRotLimits, 2)
1670 dependentTool.maxRotLimits = StringUtil.getRadiansFromString(maxRotLimits, 2)
1671 table.insert(entry.dependentMovingTools, dependentTool)
1672 end
1673
1674 j = j + 1
1675 end
1676end

loadDependentParts

Description
Load dependent parts from xml
Definition
loadDependentParts(integer xmlFile, string baseName, table entry)
Arguments
integerxmlFileid of xml object
stringbaseNamebase name
tableentryentry to add
Code
1683function Cylindered:loadDependentParts(xmlFile, baseName, entry)
1684 entry.dependentPartNodes = {}
1685
1686 local j = 0
1687 while true do
1688 local refBaseName = baseName..string.format(".dependentPart(%d)", j)
1689 if not hasXMLProperty(xmlFile, refBaseName) then
1690 break
1691 end
1692
1693 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, refBaseName.."#index", refBaseName.."#index") --FS17 to FS19
1694
1695 local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#node"), self.i3dMappings)
1696 if node ~= nil then
1697 table.insert(entry.dependentPartNodes, node)
1698 end
1699
1700 j = j + 1
1701 end
1702end

loadDependentTranslatingParts

Description
Load translating parts
Definition
loadDependentTranslatingParts(integer xmlFile, string baseName, table entry)
Arguments
integerxmlFileid of xml object
stringbaseNamebase name
tableentryentry to add
Code
1812function Cylindered:loadDependentTranslatingParts(xmlFile, baseName, entry)
1813 entry.translatingParts = {}
1814 if entry.referencePoint ~= nil then
1815 local j = 0
1816 while true do
1817 local refBaseName = baseName..string.format(".translatingPart(%d)", j)
1818 if not hasXMLProperty(xmlFile, refBaseName) then
1819 break
1820 end
1821
1822 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, refBaseName.."#index", refBaseName.."#node") --FS15 to FS17
1823
1824 local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#node"), self.i3dMappings)
1825 if node ~= nil then
1826 local transEntry = {}
1827 transEntry.node = node
1828 local x, y, z = getTranslation(node)
1829 transEntry.startPos = {x, y, z}
1830 local _, _, refZ = worldToLocal(node, getWorldTranslation(entry.referencePoint))
1831 transEntry.referenceDistance = refZ
1832 transEntry.referenceDistancePoint = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#referenceDistancePoint"), self.i3dMappings)
1833
1834 transEntry.minZTrans = getXMLFloat(xmlFile, refBaseName.."#minZTrans")
1835 transEntry.maxZTrans = getXMLFloat(xmlFile, refBaseName.."#maxZTrans")
1836
1837 table.insert(entry.translatingParts, transEntry)
1838 end
1839 j = j + 1
1840 end
1841 end
1842end

loadDependentWheels

Description
Load wheels from xml
Definition
loadDependentWheels(integer xmlFile, string baseName, table entry)
Arguments
integerxmlFileid of xml object
stringbaseNamebase name
tableentryentry to add
Code
1790function Cylindered:loadDependentWheels(xmlFile, baseName, entry)
1791 if SpecializationUtil.hasSpecialization(Wheels, self.specializations) then
1792 local indices = StringUtil.getVectorNFromString(getXMLString(xmlFile, baseName.. "#wheelIndices"))
1793 if indices ~= nil then
1794 entry.wheels = {}
1795 for _,wheelIndex in pairs(indices) do
1796 local wheel = self:getWheelFromWheelIndex(wheelIndex)
1797 if wheel ~= nil then
1798 table.insert(entry.wheels, wheel)
1799 else
1800 g_logManager:xmlWarning(self.configFileName, "Invalid wheelIndex '%s' for '%s'!", wheelIndex, baseName)
1801 end
1802 end
1803 end
1804 end
1805end

loadDischargeNode

Description
Definition
loadDischargeNode()
Code
2107function Cylindered:loadDischargeNode(superFunc, xmlFile, key, entry)
2108 if not superFunc(self, xmlFile, key, entry) then
2109 return false
2110 end
2111
2112 local baseKey = key .. ".movingToolActivation"
2113
2114 if hasXMLProperty(xmlFile, baseKey) then
2115 entry.movingToolActivation = {}
2116 entry.movingToolActivation.node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey.."#node"), self.i3dMappings)
2117 entry.movingToolActivation.isInverted = Utils.getNoNil(getXMLBool(xmlFile, baseKey.."#isInverted"), false)
2118 entry.movingToolActivation.openFactor = Utils.getNoNil(getXMLFloat(xmlFile, baseKey.."#openFactor"), 1)
2119 entry.movingToolActivation.openOffset = Utils.getNoNil(getXMLFloat(xmlFile, baseKey.."#openOffset"), 0)
2120 entry.movingToolActivation.openOffsetInv = 1-entry.movingToolActivation.openOffset
2121 end
2122
2123 return true
2124end

loadDynamicMountGrabFromXML

Description
Definition
loadDynamicMountGrabFromXML()
Code
2192function Cylindered:loadDynamicMountGrabFromXML(superFunc, xmlFile, key, entry)
2193 if not superFunc(self, xmlFile, key, entry) then
2194 return false
2195 end
2196
2197 local baseKey = key .. ".movingToolActivation"
2198 if not hasXMLProperty(xmlFile, baseKey) then
2199 return true
2200 end
2201
2202 entry.movingToolActivation = {}
2203 entry.movingToolActivation.node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey.."#node"), self.i3dMappings)
2204 entry.movingToolActivation.isInverted = Utils.getNoNil(getXMLBool(xmlFile, baseKey.."#isInverted"), false)
2205 entry.movingToolActivation.openFactor = Utils.getNoNil(getXMLFloat(xmlFile, baseKey.."#openFactor"), 1)
2206
2207 return true
2208end

loadExtraDependentParts

Description
Definition
loadExtraDependentParts()
Code
1846function Cylindered:loadExtraDependentParts(xmlFile, baseName, entry)
1847 return true
1848end

loadMovingPartFromXML

Description
Definition
loadMovingPartFromXML()
Code
1321function Cylindered:loadMovingPartFromXML(xmlFile, key, entry)
1322 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key.."#index", key.."#node") --FS17 to FS19
1323
1324 local node = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key.."#node"), self.i3dMappings)
1325 local referenceFrame = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key.."#referenceFrame"), self.i3dMappings)
1326 if node ~= nil and referenceFrame ~= nil then
1327 entry.referencePoint = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key.."#referencePoint"), self.i3dMappings)
1328 entry.node = node
1329 entry.referenceFrame = referenceFrame
1330 entry.invertZ = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#invertZ"), false)
1331 entry.scaleZ = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#scaleZ"), false)
1332 entry.limitedAxis = getXMLInt(self.xmlFile, key.."#limitedAxis")
1333 entry.isActiveDirty = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#isActiveDirty"), false)
1334 entry.playSound = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#playSound"), false)
1335
1336 entry.moveToReferenceFrame = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#moveToReferenceFrame"), false)
1337 if entry.moveToReferenceFrame then
1338 local x,y,z = worldToLocal(referenceFrame, getWorldTranslation(node))
1339 entry.referenceFrameOffset = {x,y,z}
1340 end
1341
1342 entry.doLineAlignment = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#doLineAlignment"), false)
1343 entry.partLength = Utils.getNoNil(getXMLFloat(self.xmlFile, key..".orientationLine#partLength"), 0.5)
1344 entry.orientationLineNodes = {}
1345 local i = 0
1346 while true do
1347 local pointKey = string.format("%s.orientationLine.lineNode(%d)", key, i)
1348 if not hasXMLProperty(xmlFile, pointKey) then
1349 break
1350 end
1351
1352 local lineNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, pointKey.."#node"), self.i3dMappings)
1353 table.insert(entry.orientationLineNodes, lineNode)
1354
1355 i = i + 1
1356 end
1357
1358 entry.doDirectionAlignment = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#doDirectionAlignment"), true)
1359 entry.doRotationAlignment = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#doRotationAlignment"), false)
1360 entry.rotMultiplier = Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#rotMultiplier"), 0)
1361
1362 local minRot = getXMLFloat(self.xmlFile, key.."#minRot")
1363 local maxRot = getXMLFloat(self.xmlFile, key.."#maxRot")
1364 if minRot ~= nil and maxRot ~= nil then
1365 if entry.limitedAxis ~= nil then
1366 entry.minRot = MathUtil.getValidLimit(math.rad(minRot))
1367 entry.maxRot = MathUtil.getValidLimit(math.rad(maxRot))
1368 else
1369 print("Warning: minRot/maxRot requires the use of limitedAxis in '"..self.configFileName.."'")
1370 end
1371 end
1372 entry.alignToWorldY = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#alignToWorldY"), false)
1373
1374 if entry.referencePoint ~= nil then
1375 local localReferencePoint = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key.."#localReferencePoint"), self.i3dMappings)
1376 local refX, refY, refZ = worldToLocal(node, getWorldTranslation(entry.referencePoint))
1377 if localReferencePoint ~= nil then
1378 local x,y,z = worldToLocal(node, getWorldTranslation(localReferencePoint))
1379
1380 entry.referenceDistance = MathUtil.vector3Length(refX-x, refY-y, refZ-z)
1381 entry.lastReferenceDistance = entry.referenceDistance
1382 entry.localReferencePoint = {x, y, z}
1383
1384 local side = y*(refZ-z) - z*(refY-y)
1385 entry.localReferenceAngleSide = side
1386 entry.localReferencePointNode = localReferencePoint
1387 entry.updateLocalReferenceDistance = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#updateLocalReferenceDistance"), false)
1388 entry.localReferenceTranslate = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#localReferenceTranslate"), false)
1389 if entry.localReferenceTranslate then
1390 entry.localReferenceTranslation = { getTranslation(entry.node) }
1391 end
1392 else
1393 entry.referenceDistance = 0
1394 entry.localReferencePoint = {refX, refY, refZ}
1395 end
1396 entry.referenceDistanceThreshold = Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#referenceDistanceThreshold"), 0)
1397
1398 entry.useLocalOffset = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#useLocalOffset"), false)
1399
1400 entry.localReferenceDistance = MathUtil.vector2Length(entry.localReferencePoint[2], entry.localReferencePoint[3])
1401
1402 self:loadDependentTranslatingParts(self.xmlFile, key, entry)
1403 end
1404
1405 -- direction threshold for updateing the moving tools of the vehicle is entered/active
1406 entry.directionThreshold = Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#directionThreshold"), 0.0001)
1407 entry.lastDirection = {0, 0, 0}
1408 entry.lastUpVector = {0, 0, 0}
1409
1410 entry.isDirty = false
1411 entry.isPart = true
1412
1413 return true
1414 end
1415
1416 return false
1417end

loadMovingToolFromXML

Description
Definition
loadMovingToolFromXML()
Code
1421function Cylindered:loadMovingToolFromXML(xmlFile, key, entry)
1422 local spec = self.spec_cylindered
1423
1424 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key.."#index", key.."#node") --FS17 to FS19
1425
1426 local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#node"), self.i3dMappings)
1427 if node ~= nil then
1428 entry.node = node
1429
1430 entry.externalMove = 0
1431 entry.easyArmControlActive = true
1432 entry.isEasyControlTarget = Utils.getNoNil(getXMLBool(xmlFile, key.."#isEasyControlTarget"), false)
1433
1434 entry.networkInterpolators = {}
1435
1436 -- rotation
1437 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key.."#rotSpeed", key..".rotation#rotSpeed") --FS15 to FS17
1438
1439 local rotSpeed = getXMLFloat(xmlFile, key..".rotation#rotSpeed")
1440 if rotSpeed ~= nil then
1441 entry.rotSpeed = math.rad(rotSpeed)/1000
1442 end
1443 local rotAcceleration = getXMLFloat(xmlFile, key..".rotation#rotAcceleration")
1444 if rotAcceleration ~= nil then
1445 entry.rotAcceleration = math.rad(rotAcceleration)/(1000*1000)
1446 end
1447 entry.lastRotSpeed = 0
1448 local rotMax = getXMLFloat(xmlFile, key..".rotation#rotMax")
1449 if rotMax ~= nil then
1450 entry.rotMax = math.rad(rotMax)
1451 end
1452 local rotMin = getXMLFloat(xmlFile, key..".rotation#rotMin")
1453 if rotMin ~= nil then
1454 entry.rotMin = math.rad(rotMin)
1455 end
1456 entry.syncMaxRotLimits = Utils.getNoNil(getXMLBool(xmlFile, key..".rotation#syncMaxRotLimits"), false)
1457 entry.syncMinRotLimits = Utils.getNoNil(getXMLBool(xmlFile, key..".rotation#syncMinRotLimits"), false)
1458 entry.rotSendNumBits = Utils.getNoNil(getXMLInt(xmlFile, key..".rotation#rotSendNumBits"), 8)
1459 local attachRotMax = getXMLFloat(xmlFile, key..".rotation#attachRotMax")
1460 if attachRotMax ~= nil then
1461 entry.attachRotMax = math.rad(attachRotMax)
1462 end
1463 local attachRotMin = getXMLFloat(xmlFile, key..".rotation#attachRotMin")
1464 if attachRotMin ~= nil then
1465 entry.attachRotMin = math.rad(attachRotMin)
1466 end
1467
1468 -- translation
1469 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key.."#transSpeed", key..".rotation#transSpeed") --FS15 to FS17
1470
1471 local transSpeed = getXMLFloat(xmlFile, key..".translation#transSpeed")
1472 if transSpeed ~= nil then
1473 entry.transSpeed = transSpeed/1000
1474 end
1475 local transAcceleration = getXMLFloat(xmlFile, key..".translation#transAcceleration")
1476 if transAcceleration ~= nil then
1477 entry.transAcceleration = transAcceleration/(1000*1000)
1478 end
1479 entry.lastTransSpeed = 0
1480 entry.transMax = getXMLFloat(xmlFile, key..".translation#transMax")
1481 entry.transMin = getXMLFloat(xmlFile, key..".translation#transMin")
1482 entry.attachTransMax = getXMLFloat(xmlFile, key..".translation#attachTransMax")
1483 entry.attachTransMin = getXMLFloat(xmlFile, key..".translation#attachTransMin")
1484 entry.playSound = Utils.getNoNil(getXMLBool(xmlFile, key.."#playSound"), false)
1485
1486 -- animation
1487 if SpecializationUtil.hasSpecialization(AnimatedVehicle, self.specializations) then
1488 local animSpeed = getXMLFloat(xmlFile, key..".animation#animSpeed")
1489 if animSpeed ~= nil then
1490 entry.animSpeed = animSpeed / 1000
1491 end
1492 local animAcceleration = getXMLFloat(xmlFile, key..".animation#animAcceleration")
1493 if animAcceleration ~= nil then
1494 entry.animAcceleration = animAcceleration / (1000*1000)
1495 end
1496 entry.curAnimTime = 0
1497 entry.lastAnimSpeed = 0
1498 entry.animName = getXMLString(xmlFile, key..".animation#animName")
1499 entry.animSendNumBits = Utils.getNoNil(getXMLInt(xmlFile, key..".animation#animSendNumBits"), 8)
1500 entry.animMaxTime = math.min(Utils.getNoNil(getXMLFloat(xmlFile, key..".animation#animMaxTime"), 1.0), 1.0)
1501 entry.animMinTime = math.max(Utils.getNoNil(getXMLFloat(xmlFile, key..".animation#animMinTime"), 0.0), 0.0)
1502
1503 local animStartTime = getXMLFloat(xmlFile, key..".animation#animStartTime")
1504 if animStartTime ~= nil then
1505 entry.curAnimTime = animStartTime
1506 self:setAnimationTime(entry.animName, animStartTime)
1507 end
1508
1509 entry.networkInterpolators.animation = InterpolatorValue:new(entry.curAnimTime)
1510 entry.networkInterpolators.animation:setMinMax(0, 1)
1511 end
1512
1513 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key..".controls#iconFilename", key..".controls#iconName") --FS17 to FS19
1514
1515 local iconName = getXMLString(xmlFile, key .. ".controls#iconName")
1516 if iconName ~= nil then
1517 if InputHelpElement.AXIS_ICON[iconName] == nil then
1518 -- add the mod name as a prefix to match axis icon loading name collision avoidance
1519 iconName = (self.customEnvironment or "") .. iconName
1520 end
1521
1522 entry.axisActionIcon = iconName
1523 end
1524
1525 entry.controlGroupIndex = getXMLInt(xmlFile, key .. ".controls#groupIndex") or 0
1526 if entry.controlGroupIndex ~= 0 then
1527 if spec.controlGroupNames[entry.controlGroupIndex] ~= nil then
1528 ListUtil.addElementToList(spec.controlGroups, entry.controlGroupIndex)
1529 else
1530 g_logManager:xmlWarning(self.configFileName, "ControlGroup '%d' not defined for '%s'!", entry.controlGroupIndex, key)
1531 end
1532 end
1533
1534 entry.axis = getXMLString(xmlFile, key..".controls#axis")
1535 if entry.axis ~= nil then
1536 entry.axisActionIndex = InputAction[entry.axis]
1537 end
1538 entry.invertAxis = Utils.getNoNil(getXMLBool(xmlFile, key..".controls#invertAxis"), false)
1539 entry.mouseSpeedFactor = Utils.getNoNil(getXMLFloat(xmlFile, key..".controls#mouseSpeedFactor"), 1.0)
1540
1541 if (entry.rotSpeed ~= nil or entry.transSpeed ~= nil or entry.animSpeed ~= nil) then
1542 entry.dirtyFlag = self:getNextDirtyFlag()
1543 entry.saving = Utils.getNoNil(getXMLBool(xmlFile, key.."#allowSaving"), true)
1544 end
1545
1546 entry.isDirty = false
1547 entry.isIntitialDirty = Utils.getNoNil(getXMLBool(xmlFile, key.."#isIntitialDirty"), true)
1548
1549 entry.rotationAxis = Utils.getNoNil(getXMLInt(xmlFile, key..".rotation#rotationAxis"), 1)
1550 entry.translationAxis = Utils.getNoNil(getXMLInt(xmlFile, key..".translation#translationAxis"), 3)
1551
1552 local detachingRotMaxLimit = getXMLFloat(xmlFile, key..".rotation#detachingRotMaxLimit")
1553 local detachingRotMinLimit = getXMLFloat(xmlFile, key..".rotation#detachingRotMinLimit")
1554 local detachingTransMaxLimit = getXMLFloat(xmlFile, key..".translation#detachingTransMaxLimit")
1555 local detachingTransMinLimit = getXMLFloat(xmlFile, key..".translation#detachingTransMinLimit")
1556 if detachingRotMaxLimit ~= nil or detachingRotMinLimit ~= nil or detachingTransMaxLimit ~= nil or detachingTransMinLimit ~= nil then
1557 if spec.detachLockNodes == nil then
1558 spec.detachLockNodes = {}
1559 end
1560
1561 local detachLock = {}
1562 if detachingRotMaxLimit ~= nil then
1563 detachLock.detachingRotMaxLimit = math.rad(detachingRotMaxLimit)
1564 end
1565 if detachingRotMinLimit ~= nil then
1566 detachLock.detachingRotMinLimit = math.rad(detachingRotMinLimit)
1567 end
1568 detachLock.detachingTransMinLimit = detachingTransMinLimit
1569 detachLock.detachingTransMaxLimit = detachingTransMaxLimit
1570
1571 spec.detachLockNodes[entry] = detachLock
1572 end
1573
1574
1575 local rx,ry,rz = getRotation(node)
1576 entry.curRot = {rx,ry,rz}
1577 local x,y,z = getTranslation(node)
1578 entry.curTrans = {x,y,z}
1579
1580 entry.startRot = getXMLFloat(xmlFile, key..".rotation#startRot")
1581 if entry.startRot ~= nil then
1582 entry.startRot = math.rad(entry.startRot)
1583 end
1584 entry.startTrans = getXMLFloat(xmlFile, key..".translation#startTrans")
1585
1586 entry.move = 0
1587 entry.moveToSend = 0
1588
1589 -- delayed node
1590 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key.."#delayedIndex", key.."#delayedNode") --FS17 to FS19
1591
1592 entry.delayedNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#delayedNode"), self.i3dMappings)
1593 if entry.delayedNode ~= nil then
1594 entry.currentDelayedData = {rot = {rx, ry, rz}, trans = {x, y, z}}
1595 entry.delayedHistroyData = {}
1596 entry.delayedHistroyData[1] = {rot = {rx, ry, rz}, trans = {x, y, z}}
1597 entry.delayedHistroyData[2] = {rot = {rx, ry, rz}, trans = {x, y, z}}
1598 entry.delayedHistroyData[3] = {rot = {rx, ry, rz}, trans = {x, y, z}}
1599 entry.delayedHistoryIndex = 0
1600 end
1601
1602 entry.networkInterpolators.translation = InterpolatorValue:new(entry.curTrans[entry.translationAxis])
1603 entry.networkInterpolators.translation:setMinMax(entry.transMin, entry.transMax)
1604 entry.networkInterpolators.rotation = InterpolatorAngle:new(entry.curRot[entry.rotationAxis])
1605 entry.networkInterpolators.rotation:setMinMax(entry.rotMin, entry.rotMax)
1606 entry.networkTimeInterpolator = InterpolationTime:new(1.2)
1607
1608 entry.isTool = true
1609
1610 return true
1611 end
1612
1613 return false
1614end

loadObjectChangeValuesFromXML

Description
Load object change from xml
Definition
loadObjectChangeValuesFromXML(integer xmlFile, string key, integer node, table object)
Arguments
integerxmlFileid of xml object
stringkeykey
integernodenode id
tableobjectobject
Code
2070function Cylindered:loadObjectChangeValuesFromXML(superFunc, xmlFile, key, node, object)
2071 superFunc(self, xmlFile, key, node, object)
2072
2073 local spec = self.spec_cylindered
2074
2075 if spec.nodesToMovingTools ~= nil and spec.nodesToMovingTools[node] ~= nil then
2076 local movingTool = spec.nodesToMovingTools[node]
2077 object.movingToolRotMaxActive = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#movingToolRotMaxActive"), movingTool.rotMax)
2078 object.movingToolRotMaxInactive = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#movingToolRotMaxInactive"), movingTool.rotMax)
2079 object.movingToolRotMinActive = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#movingToolRotMinActive"), movingTool.rotMin)
2080 object.movingToolRotMinInactive = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#movingToolRotMinInactive"), movingTool.rotMin)
2081 end
2082end

loadRotationBasedLimits

Description
Definition
loadRotationBasedLimits()
Code
1928function Cylindered:loadRotationBasedLimits(xmlFile, key, tool)
1929 local rotation = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#rotation"), nil)
1930 local rotMin = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#rotMin"), nil)
1931 local rotMax = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#rotMax"), nil)
1932 local transMin = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#transMin"), nil)
1933 local transMax = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#transMax"), nil)
1934
1935 if rotation ~= nil and (rotMin ~= nil or rotMax ~= nil or transMin ~= nil or transMax ~= nil) then
1936 local time = (rotation-tool.rotMin) / (tool.rotMax-tool.rotMin)
1937 return {rotMin=rotMin, rotMax=rotMax, transMin=transMin, transMax=transMax, time=time}
1938 end
1939
1940 return nil
1941end

loadShovelNode

Description
Definition
loadShovelNode()
Code
2152function Cylindered:loadShovelNode(superFunc, xmlFile, key, entry)
2153 if not superFunc(self, xmlFile, key, entry) then
2154 return false
2155 end
2156
2157 local baseKey = key .. ".movingToolActivation"
2158 if not hasXMLProperty(xmlFile, baseKey) then
2159 return true
2160 end
2161
2162 entry.movingToolActivation = {}
2163 entry.movingToolActivation.node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, baseKey.."#node"), self.i3dMappings)
2164 entry.movingToolActivation.isInverted = Utils.getNoNil(getXMLBool(xmlFile, baseKey.."#isInverted"), false)
2165 entry.movingToolActivation.openFactor = Utils.getNoNil(getXMLFloat(xmlFile, baseKey.."#openFactor"), 1)
2166
2167 return true
2168end

movingToolDashboardAttributes

Description
Definition
movingToolDashboardAttributes()
Code
3064function Cylindered.movingToolDashboardAttributes(self, xmlFile, key, dashboard)
3065 dashboard.axis = getXMLString(xmlFile, key.."#axis")
3066
3067 if dashboard.axis == nil then
3068 g_logManager:xmlWarning(self.configFileName, "Misssing axis attribute for dashboard '%s'", key)
3069 return false
3070 end
3071
3072 dashboard.attacherJointIndex = getXMLInt(xmlFile, key.."#attacherJointIndex")
3073
3074 return true
3075end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
2382function Cylindered:onDeactivate()
2383 if self.isClient then
2384 local spec = self.spec_cylindered
2385 g_soundManager:stopSample(spec.samples.hydraulic)
2386 spec.isHydraulicSamplePlaying = false
2387 end
2388end

onDelete

Description
Called on deleting
Definition
onDelete()
Code
516function Cylindered:onDelete()
517 local spec = self.spec_cylindered
518
519 if self.isClient then
520 g_soundManager:deleteSamples(spec.samples)
521 end
522
523 for _,movingTool in pairs(spec.movingTools) do
524 if movingTool.icon ~= nil then
525 movingTool.icon:delete()
526 movingTool.icon = nil
527 end
528 end
529end

onDraw

Description
Definition
onDraw()
Code
1310function Cylindered:onDraw(isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1311 local spec = self.spec_cylindered
1312 if #spec.controlGroupNames > 1 then
1313 if isActiveForInputIgnoreSelection then
1314 g_currentMission:addExtraPrintText(string.format(g_i18n:getText("action_selectedControlGroup"), spec.controlGroupNames[spec.currentControlGroupIndex], spec.currentControlGroupIndex))
1315 end
1316 end
1317end

onLoad

Description
Called on loading
Definition
onLoad(table savegame)
Arguments
tablesavegamesavegame
Code
101function Cylindered:onLoad(savegame)
102 local spec = self.spec_cylindered
103
104 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.movingParts", "vehicle.cylindered.movingParts") --FS17 to FS19
105 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.movingTools", "vehicle.cylindered.movingTools") --FS17 to FS19
106 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.cylinderedHydraulicSound", "vehicle.cylindered.sounds.hydraulic") --FS17 to FS19
107
108 spec.samples = {}
109 if self.isClient then
110 spec.samples.hydraulic = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.cylindered.sounds", "hydraulic", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
111 spec.isHydraulicSamplePlaying = false
112 end
113
114 spec.activeDirtyMovingParts = {}
115
116 local referenceNodes = {}
117 spec.nodesToMovingParts = {}
118 spec.movingParts = {}
119 self.anyMovingPartsDirty = false
120 spec.detachLockNodes = nil
121 local i = 0
122 while true do
123 local partKey = string.format("vehicle.cylindered.movingParts.movingPart(%d)", i)
124 if not hasXMLProperty(self.xmlFile, partKey) then
125 break
126 end
127
128 local entry = {}
129 if self:loadMovingPartFromXML(self.xmlFile, partKey, entry) then
130 if referenceNodes[entry.node] == nil then
131 referenceNodes[entry.node] = {}
132 end
133 if spec.nodesToMovingParts[entry.node] == nil then
134 table.insert(referenceNodes[entry.node], entry)
135
136 self:loadDependentParts(self.xmlFile, partKey, entry)
137 self:loadDependentComponentJoints(self.xmlFile, partKey, entry)
138 self:loadCopyLocalDirectionParts(self.xmlFile, partKey, entry)
139 self:loadExtraDependentParts(self.xmlFile, partKey, entry)
140 self:loadDependentAnimations(self.xmlFile, partKey, entry)
141
142 entry.key = partKey
143 table.insert(spec.movingParts, entry)
144
145 if entry.isActiveDirty then
146 table.insert(spec.activeDirtyMovingParts, entry)
147 end
148
149 spec.nodesToMovingParts[entry.node] = entry
150 else
151 g_logManager:xmlWarning(self.configFileName, "Moving part with node '%s' already exists!", getName(entry.node))
152 end
153 end
154
155 i = i + 1
156 end
157
158 spec.isActiveDirtyTimeOffset = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.cylindered.movingParts#isActiveDirtyTimeOffset"), 0) * 1000
159 spec.isActiveDirtyTime = g_currentMission.time
160
161 -- find dependencies
162 for _, part in pairs(spec.movingParts) do
163 part.dependentParts = {}
164 for _, ref in pairs(part.dependentPartNodes) do
165 if referenceNodes[ref] ~= nil then
166 for _, p in pairs(referenceNodes[ref]) do
167 part.dependentParts[p] = p
168 p.isDependentPart = true
169 end
170 end
171 end
172 end
173
174 local function addMovingPart(part, newTable, allowDependentParts)
175 for _, addedPart in ipairs(newTable) do
176 if addedPart == part then
177 return
178 end
179 end
180
181 if part.isDependentPart == true then
182 if allowDependentParts ~= true then
183 return
184 end
185 end
186
187 table.insert(newTable, part)
188
189 for _, depPart in pairs(part.dependentParts) do
190 addMovingPart(depPart, newTable, true)
191 end
192 end
193
194 local newParts = {}
195 for _, part in ipairs(spec.movingParts) do
196 addMovingPart(part, newParts)
197 end
198 spec.movingParts = newParts
199
200 spec.controlGroups = {}
201 spec.controlGroupMapping = {}
202 spec.currentControlGroupIndex = 1
203 spec.controlGroupNames = {}
204 local i = 0
205 while true do
206 local groupKey = string.format("vehicle.cylindered.movingTools.controlGroups.controlGroup(%d)", i)
207 if not hasXMLProperty(self.xmlFile, groupKey) then
208 break
209 end
210
211 local name = getXMLString(self.xmlFile, groupKey.."#name")
212 if name ~= nil then
213 table.insert(spec.controlGroupNames, g_i18n:convertText(name, self.customEnvironment))
214 end
215
216 i = i + 1
217 end
218
219 spec.nodesToMovingTools = {}
220 spec.movingTools = {}
221 local i = 0
222 while true do
223 local toolKey = string.format("vehicle.cylindered.movingTools.movingTool(%d)", i)
224 if not hasXMLProperty(self.xmlFile, toolKey) then
225 break
226 end
227
228 local entry = {}
229 if self:loadMovingToolFromXML(self.xmlFile, toolKey, entry) then
230 if referenceNodes[entry.node] == nil then
231 referenceNodes[entry.node] = {}
232 end
233
234 if spec.nodesToMovingTools[entry.node] == nil then
235 table.insert(referenceNodes[entry.node], entry)
236
237 self:loadDependentMovingTools(self.xmlFile, toolKey, entry)
238 self:loadDependentParts(self.xmlFile, toolKey, entry)
239 self:loadDependentComponentJoints(self.xmlFile, toolKey, entry)
240 self:loadExtraDependentParts(self.xmlFile, toolKey, entry)
241 self:loadDependentAnimations(self.xmlFile, toolKey, entry)
242
243 entry.isActive = true
244 entry.key = toolKey
245 table.insert(spec.movingTools, entry)
246 spec.nodesToMovingTools[entry.node] = entry
247 else
248 g_logManager:xmlWarning(self.configFileName, "Moving tool with node '%s' already exists!", getName(entry.node))
249 end
250 end
251 i = i + 1
252 end
253
254 for _, groupIndex in ipairs(spec.controlGroups) do
255 local subSelectionIndex = self:addSubselection(groupIndex)
256 spec.controlGroupMapping[subSelectionIndex] = groupIndex
257 end
258
259 for _, part in pairs(spec.movingTools) do
260 part.dependentParts = {}
261 for _, ref in pairs(part.dependentPartNodes) do
262 if referenceNodes[ref] ~= nil then
263 for _, p in pairs(referenceNodes[ref]) do
264 part.dependentParts[p] = p
265 end
266 end
267 end
268 for i=#part.dependentMovingTools, 1, -1 do
269 local dependentTool = part.dependentMovingTools[i]
270 local tool = spec.nodesToMovingTools[dependentTool.node]
271 if tool ~= nil then
272 dependentTool.movingTool = tool
273 else
274 g_logManager:xmlWarning(self.configFileName, "Dependent moving tool '%s' not defined. Ignoring it!", getName(dependentTool.node))
275 table.remove(part.dependentMovingTools, i)
276 end
277 end
278 end
279
280
281 local simpleKey = "vehicle.cylindered.movingTools.easyArmControl"
282 if hasXMLProperty(self.xmlFile, simpleKey) then
283 spec.easyArmControl = {}
284 spec.easyArmControl.rootNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, simpleKey.."#rootNode"), self.i3dMappings)
285 spec.easyArmControl.targetNodeY = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, simpleKey.."#node"), self.i3dMappings)
286 spec.easyArmControl.targetNodeZ = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, simpleKey.."#targetNodeZ"), self.i3dMappings) or spec.easyArmControl.targetNodeY
287
288 if spec.easyArmControl.targetNodeZ ~= nil and spec.easyArmControl.targetNodeY ~= nil then
289 local targetYTool = self:getMovingToolByNode(spec.easyArmControl.targetNodeY)
290 local targetZTool = self:getMovingToolByNode(spec.easyArmControl.targetNodeZ)
291 if targetYTool ~= nil and targetZTool ~= nil then
292 spec.easyArmControl.targetNode = spec.easyArmControl.targetNodeZ
293 if getParent(spec.easyArmControl.targetNodeY) == spec.easyArmControl.targetNodeZ then
294 spec.easyArmControl.targetNode = spec.easyArmControl.targetNodeY
295 end
296 spec.easyArmControl.targetRefNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, simpleKey.."#refNode"), self.i3dMappings)
297 spec.easyArmControl.lastValidPositionY = {getTranslation(spec.easyArmControl.targetNodeY)}
298 spec.easyArmControl.lastValidPositionZ = {getTranslation(spec.easyArmControl.targetNodeZ)}
299 spec.easyArmControl.xRotationMaxDistance = getXMLFloat(self.xmlFile, simpleKey..".xRotationNodes#maxDistance") or 0
300 spec.easyArmControl.xRotationNodes = {}
301 spec.easyArmControl.zTranslationNodes = {}
302
303 i = 0
304 local maxTrans = 0
305 while true do
306 local transKey = string.format("%s.zTranslationNodes.zTranslationNode(%d)", simpleKey, i)
307 if not hasXMLProperty(self.xmlFile, transKey) then
308 break
309 end
310
311 local node = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, transKey.."#node"), self.i3dMappings)
312 if node ~= nil then
313 local movingTool = self:getMovingToolByNode(node)
314 if movingTool ~= nil then
315 local maxDistance = math.abs(movingTool.transMin - movingTool.transMax)
316 maxTrans = maxTrans + maxDistance
317 movingTool.easyArmControlActive = false
318 table.insert(spec.easyArmControl.zTranslationNodes, {node=node, movingTool=movingTool, maxDistance=maxDistance, transFactor=0})
319 end
320 end
321
322 i = i + 1
323 end
324
325 for _, translationNode in ipairs(spec.easyArmControl.zTranslationNodes) do
326 translationNode.transFactor = translationNode.maxDistance / maxTrans
327 end
328
329 for i=1, 2 do
330 local xRotKey = string.format("%s.xRotationNodes.xRotationNode%d", simpleKey, i)
331 if not hasXMLProperty(self.xmlFile, xRotKey) then
332 g_logManager:xmlWarning(self.configFileName, "Missing second xRotation node for easy control!")
333 spec.easyArmControl = nil
334 break
335 end
336
337 local node = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, xRotKey.."#node"), self.i3dMappings)
338 local refNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, xRotKey.."#refNode"), self.i3dMappings)
339 if node ~= nil and refNode ~= nil then
340 local movingTool = self:getMovingToolByNode(node)
341 if movingTool ~= nil then
342 movingTool.easyArmControlActive = false
343 table.insert(spec.easyArmControl.xRotationNodes, {node=node, refNode=refNode, movingTool=movingTool})
344 end
345 end
346 end
347 else
348 g_logManager:xmlError(self.configFileName, "Missing moving tools for easy control targets!")
349 spec.easyArmControl = nil
350 end
351 else
352 g_logManager:xmlError(self.configFileName, "Missing easy control targets!")
353 spec.easyArmControl = nil
354 end
355 end
356
357 if self.loadDashboardsFromXML ~= nil then
358 local dashboardData = {valueTypeToLoad = "movingTool",
359 valueObject = self,
360 valueFunc = Cylindered.getMovingToolDashboardState,
361 minFunc = 0,
362 maxFunc = 1,
363 additionalAttributesFunc = Cylindered.movingToolDashboardAttributes,
364 idleValue = 0.5}
365
366 self:loadDashboardsFromXML(self.xmlFile, "vehicle.cylindered.dashboards", dashboardData)
367 end
368
369 if self.isClient and g_isDevelopmentVersion then
370 if (#spec.movingParts > 0 or #spec.movingTools > 0) and spec.samples.hydraulic == nil then
371 g_logManager:xmlDevWarning(self.configFileName, "Missing cylindered hydraulic sound")
372 end
373 end
374
375 spec.cylinderedDirtyFlag = self:getNextDirtyFlag()
376 spec.cylinderedInputDirtyFlag = self:getNextDirtyFlag()
377
378 spec.isLoading = true
379end

onLoadFinished

Description
Called after loading
Definition
onLoadFinished(table savegame)
Arguments
tablesavegamesavegame
Code
502function Cylindered:onLoadFinished(savegame)
503 local spec = self.spec_cylindered
504 spec.isLoading = false
505
506 for i=1, table.getn(spec.movingTools) do
507 local tool = spec.movingTools[i]
508 if tool.delayedHistoryIndex ~= nil and tool.delayedHistoryIndex > 0 then
509 self:updateDelayedTool(tool, true)
510 end
511 end
512end

onPostAttach

Description
Called if vehicle gets attached
Definition
onPostAttach(table attacherVehicle, integer inputJointDescIndex, integer jointDescIndex)
Arguments
tableattacherVehicleattacher vehicle
integerinputJointDescIndexindex of input attacher joint
integerjointDescIndexindex of attacher joint it gets attached to
Code
2316function Cylindered:onPostAttach(attacherVehicle, inputJointDescIndex, jointDescIndex)
2317 local spec = self.spec_cylindered
2318
2319 for _, tool in ipairs(spec.movingTools) do
2320 local changed = false
2321 if tool.transSpeed ~= nil then
2322 local trans = tool.curTrans[tool.translationAxis]
2323
2324 local changedTrans = false
2325 if tool.attachTransMax ~= nil and trans > tool.attachTransMax then
2326 trans = tool.attachTransMax
2327 changedTrans = true
2328 elseif tool.attachTransMin ~= nil and trans < tool.attachTransMin then
2329 trans = tool.attachTransMin
2330 changedTrans = true
2331 end
2332 if changedTrans then
2333 tool.curTrans[tool.translationAxis] = trans
2334 setTranslation(tool.node, unpack(tool.curTrans))
2335 changed = true
2336 end
2337 end
2338 if tool.rotSpeed ~= nil then
2339 local rot = tool.curRot[tool.rotationAxis]
2340
2341 local changedRot = false
2342 if tool.attachRotMax ~= nil and rot > tool.attachRotMax then
2343 rot = tool.attachRotMax
2344 changedRot = true
2345 elseif tool.attachRotMin ~= nil and rot < tool.attachRotMin then
2346 rot = tool.attachRotMin
2347 changedRot = true
2348 end
2349 if changedRot then
2350 tool.curRot[tool.rotationAxis] = rot
2351 setRotation(tool.node, unpack(tool.curRot))
2352 changed = true
2353 end
2354 end
2355 if changed then
2356 Cylindered.setDirty(self, tool)
2357 end
2358 end
2359end

onPostLoad

Description
Called after loading
Definition
onPostLoad(table savegame)
Arguments
tablesavegamesavegame
Code
384function Cylindered:onPostLoad(savegame)
385 local spec = self.spec_cylindered
386
387 for _, tool in pairs(spec.movingTools) do
388 if self:getIsMovingToolActive(tool) then
389 if tool.startRot ~= nil then
390 tool.curRot[tool.rotationAxis] = tool.startRot
391 setRotation(tool.node, unpack(tool.curRot))
392 end
393 if tool.startTrans ~= nil then
394 tool.curTrans[tool.translationAxis] = tool.startTrans
395 setTranslation(tool.node, unpack(tool.curTrans))
396 end
397
398 if tool.delayedNode ~= nil then
399 self:setDelayedData(tool, true)
400 end
401
402 if tool.isIntitialDirty then
403 Cylindered.setDirty(self, tool)
404 end
405 end
406 end
407
408 for _, part in pairs(spec.movingParts) do
409 self:loadDependentAttacherJoints(self.xmlFile, part.key, part)
410 self:loadDependentWheels(self.xmlFile, part.key, part)
411 end
412
413 for _, tool in pairs(spec.movingTools) do
414 self:loadDependentAttacherJoints(self.xmlFile, tool.key, tool)
415 self:loadDependentWheels(self.xmlFile, tool.key, tool)
416 end
417
418 if self:allowLoadMovingToolStates() then
419 if savegame ~= nil and not savegame.resetVehicles then
420 local i = 0
421 for _, tool in ipairs(spec.movingTools) do
422 if tool.saving then
423 if self:getIsMovingToolActive(tool) then
424 local toolKey = string.format("%s.cylindered.movingTool(%d)", savegame.key, i)
425 local changed = false
426 if tool.transSpeed ~= nil then
427 local newTrans = getXMLFloat(savegame.xmlFile, toolKey.."#translation")
428 if newTrans ~= nil then
429 if tool.transMax ~= nil then
430 newTrans = math.min(newTrans, tool.transMax)
431 end
432 if tool.transMin ~= nil then
433 newTrans = math.max(newTrans, tool.transMin)
434 end
435 end
436 if newTrans ~= nil and math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then
437 tool.curTrans = {getTranslation(tool.node)}
438 tool.curTrans[tool.translationAxis] = newTrans
439 setTranslation(tool.node, unpack(tool.curTrans))
440 changed = true
441 end
442 end
443 if tool.rotSpeed ~= nil then
444 local newRot = getXMLFloat(savegame.xmlFile, toolKey.."#rotation")
445 if newRot ~= nil then
446 if tool.rotMax ~= nil then
447 newRot = math.min(newRot, tool.rotMax)
448 end
449 if tool.rotMin ~= nil then
450 newRot = math.max(newRot, tool.rotMin)
451 end
452 end
453 if newRot ~= nil and math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then
454 tool.curRot = {getRotation(tool.node)}
455 tool.curRot[tool.rotationAxis] = newRot
456 setRotation(tool.node, unpack(tool.curRot))
457 changed = true
458 end
459 end
460 if tool.animSpeed ~= nil then
461 local animTime = getXMLFloat(savegame.xmlFile, toolKey.."#animationTime")
462 if animTime ~= nil then
463 if tool.animMinTime ~= nil then
464 animTime = math.max(animTime, tool.animMinTime)
465 end
466 if tool.animMaxTime ~= nil then
467 animTime = math.min(animTime, tool.animMaxTime)
468 end
469
470 tool.curAnimTime = animTime
471 self:setAnimationTime(tool.animName, animTime, true)
472 end
473 end
474 if changed then
475 Cylindered.setDirty(self, tool)
476 end
477
478 if tool.delayedNode ~= nil then
479 self:setDelayedData(tool, true)
480 end
481 end
482 i = i + 1
483 end
484
485 for _, dependentTool in pairs(tool.dependentMovingTools) do
486 Cylindered.updateRotationBasedLimits(self, tool, dependentTool)
487 end
488 end
489 end
490 end
491
492 self:updateEasyControl(9999, true)
493 self:updateCylinderedInitial(false)
494
495 -- force update after loading, e.g. savegame
496 spec.isActiveDirtyTime = g_currentMission.time + math.max(spec.isActiveDirtyTimeOffset, 1000)
497end

onPostUpdate

Description
Definition
onPostUpdate()
Code
1247function Cylindered:onPostUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1248 local spec = self.spec_cylindered
1249
1250 spec.isActiveDirtyTime = g_currentMission.time + spec.isActiveDirtyTimeOffset
1251
1252 if spec.isActiveDirtyTime >= g_currentMission.time then
1253 for _, part in pairs(spec.activeDirtyMovingParts) do
1254 Cylindered.setDirty(self, part)
1255 end
1256 end
1257
1258 for _, tool in pairs(spec.movingTools) do
1259 if tool.isDirty then
1260 if tool.playSound then
1261 spec.movingToolNeedsSound = true
1262 end
1263 if self.isServer then
1264 -- update component joint
1265 Cylindered.updateComponentJoints(self, tool, false)
1266 end
1267 self:updateExtraDependentParts(tool, dt)
1268 self:updateDependentAnimations(tool, dt)
1269 tool.isDirty = false
1270 end
1271 end
1272
1273 if self.anyMovingPartsDirty then
1274 for i, part in ipairs(spec.movingParts) do
1275 if part.isDirty then
1276 Cylindered.updateMovingPart(self, part, false)
1277 self:updateExtraDependentParts(part, dt)
1278 self:updateDependentAnimations(part, dt)
1279 if part.playSound then
1280 spec.cylinderedHydraulicSoundPartNumber = i
1281 spec.movingPartNeedsSound = true
1282 end
1283 else
1284 if spec.isClient and spec.cylinderedHydraulicSoundPartNumber == i then
1285 spec.movingPartNeedsSound = false
1286 end
1287 end
1288 end
1289 self.anyMovingPartsDirty = false
1290 end
1291
1292 if self.isClient then
1293 if spec.movingToolNeedsSound or spec.movingPartNeedsSound then
1294 if not spec.isHydraulicSamplePlaying then
1295 g_soundManager:playSample(spec.samples.hydraulic)
1296 spec.isHydraulicSamplePlaying = true
1297 end
1298 self:raiseActive()
1299 else
1300 if spec.isHydraulicSamplePlaying then
1301 g_soundManager:stopSample(spec.samples.hydraulic)
1302 spec.isHydraulicSamplePlaying = false
1303 end
1304 end
1305 end
1306end

onReadStream

Description
Called on client side on join
Definition
onReadStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
560function Cylindered:onReadStream(streamId, connection)
561 local spec = self.spec_cylindered
562
563 if streamReadBool(streamId) then
564 if connection:getIsServer() then
565 for i=1, table.getn(spec.movingTools) do
566 local tool = spec.movingTools[i]
567 if tool.dirtyFlag ~= nil then
568 tool.networkTimeInterpolator:reset()
569 if tool.transSpeed ~= nil then
570 local newTrans = streamReadFloat32(streamId)
571 tool.curTrans[tool.translationAxis] = newTrans
572 setTranslation(tool.node, unpack(tool.curTrans))
573 tool.networkInterpolators.translation:setValue(tool.curTrans[tool.translationAxis])
574 end
575 if tool.rotSpeed ~= nil then
576 local newRot = streamReadFloat32(streamId)
577 tool.curRot[tool.rotationAxis] = newRot
578 setRotation(tool.node, unpack(tool.curRot))
579 tool.networkInterpolators.rotation:setAngle(newRot)
580 end
581 if tool.animSpeed ~= nil then
582 local newAnimTime = streamReadFloat32(streamId)
583 tool.curAnimTime = newAnimTime
584 self:setAnimationTime(tool.animName, tool.curAnimTime)
585 tool.networkInterpolators.animation:setValue(newAnimTime)
586 end
587 if tool.delayedNode ~= nil then
588 self:setDelayedData(tool, true)
589 end
590 Cylindered.setDirty(self, tool)
591 end
592 end
593 end
594 end
595end

onReadUpdateStream

Description
Called on on update
Definition
onReadUpdateStream(integer streamId, integer timestamp, table connection)
Arguments
integerstreamIdstream ID
integertimestamptimestamp
tableconnectionconnection
Code
629function Cylindered:onReadUpdateStream(streamId, timestamp, connection)
630 local spec = self.spec_cylindered
631
632 -- if server, read input from client
633 if not connection:getIsServer() then
634 if streamReadBool(streamId) then
635 for _, tool in ipairs(spec.movingTools) do
636 if tool.axisActionIndex ~= nil then
637 tool.move = (streamReadUIntN(streamId, 12) / 4095 * 2 - 1) * 5
638 if math.abs(tool.move) < 0.01 then
639 tool.move = 0
640 end
641 end
642 end
643 end
644 else
645 -- if client, read updated attributes
646 if streamReadBool(streamId) then
647 for _, tool in ipairs(spec.movingTools) do
648 if tool.dirtyFlag ~= nil then
649 if streamReadBool(streamId) then
650 tool.networkTimeInterpolator:startNewPhaseNetwork()
651
652 if tool.transSpeed ~= nil then
653 local newTrans = streamReadFloat32(streamId)
654 if math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then
655 tool.networkInterpolators.translation:setTargetValue(newTrans)
656 end
657 end
658 if tool.rotSpeed ~= nil then
659 local newRot
660 if tool.rotMin == nil or tool.rotMax == nil then
661 newRot = NetworkUtil.readCompressedAngle(streamId)
662 else
663 if tool.syncMinRotLimits then
664 tool.rotMin = streamReadFloat32(streamId)
665 end
666 if tool.syncMaxRotLimits then
667 tool.rotMax = streamReadFloat32(streamId)
668 end
669
670 tool.networkInterpolators.rotation:setMinMax(tool.rotMin, tool.rotMax)
671 newRot = NetworkUtil.readCompressedRange(streamId, tool.rotMin, tool.rotMax, tool.rotSendNumBits)
672 end
673 if math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then
674 tool.networkInterpolators.rotation:setTargetAngle(newRot)
675 end
676 end
677 if tool.animSpeed ~= nil then
678 local newAnimTime = NetworkUtil.readCompressedRange(streamId, tool.animMinTime, tool.animMaxTime, tool.animSendNumBits)
679 if math.abs(newAnimTime - tool.curAnimTime) > 0.0001 then
680 tool.networkInterpolators.animation:setTargetValue(newAnimTime)
681 end
682 end
683 end
684 end
685 end
686 end
687 end
688end

onRegisterActionEvents

Description
Definition
onRegisterActionEvents()
Code
2290function Cylindered:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
2291 if self.isClient then
2292 local spec = self.spec_cylindered
2293 self:clearActionEventsTable(spec.actionEvents)
2294
2295 -- no check for selection since movingTools can be controlled globally
2296 -- sub selection moving tools always require the selection of the sub index
2297 if isActiveForInputIgnoreSelection then
2298 for i=1, table.getn(spec.movingTools) do
2299 local movingTool = spec.movingTools[i]
2300 local isSelectedGroup = movingTool.controlGroupIndex == 0 or movingTool.controlGroupIndex == spec.currentControlGroupIndex
2301 local canBeControlled = (not g_gameSettings:getValue("easyArmControl") and not movingTool.isEasyControlTarget) or movingTool.easyArmControlActive
2302 if movingTool.axisActionIndex ~= nil and isSelectedGroup and canBeControlled then
2303 local _, actionEventId = self:addActionEvent(spec.actionEvents, movingTool.axisActionIndex, self, Cylindered.actionEventInput, false, false, true, true, i, movingTool.axisActionIcon)
2304 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL)
2305 end
2306 end
2307 end
2308 end
2309end

onSelect

Description
Definition
onSelect()
Code
2363function Cylindered:onSelect(subSelectionIndex)
2364 local spec = self.spec_cylindered
2365 local controlGroupIndex = spec.controlGroupMapping[subSelectionIndex]
2366 if controlGroupIndex ~= nil then
2367 spec.currentControlGroupIndex = controlGroupIndex
2368 else
2369 spec.currentControlGroupIndex = 0
2370 end
2371end

onUnselect

Description
Definition
onUnselect()
Code
2375function Cylindered:onUnselect()
2376 local spec = self.spec_cylindered
2377 spec.currentControlGroupIndex = 0
2378end

onUpdate

Description
Called on update
Definition
onUpdate(float dt, boolean isActive, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActivetrue if vehicle is active
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
747function Cylindered:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
748 local spec = self.spec_cylindered
749
750 spec.movingToolNeedsSound = false
751 spec.movingPartNeedsSound = false
752
753 self:updateEasyControl(dt)
754
755 if self.isServer then
756 for i=1,table.getn(spec.movingTools) do
757 local tool = spec.movingTools[i]
758
759 local rotSpeed = 0
760 local transSpeed = 0
761 local animSpeed = 0
762
763 local move = tool.move + tool.externalMove
764 if math.abs(move) > 0 then
765 tool.externalMove = 0
766
767 if tool.rotSpeed ~= nil then
768 rotSpeed = move*tool.rotSpeed
769 if tool.rotAcceleration ~= nil and math.abs(rotSpeed - tool.lastRotSpeed) >= tool.rotAcceleration*dt then
770 if rotSpeed > tool.lastRotSpeed then
771 rotSpeed = tool.lastRotSpeed + tool.rotAcceleration*dt
772 else
773 rotSpeed = tool.lastRotSpeed - tool.rotAcceleration*dt
774 end
775 end
776 end
777 if tool.transSpeed ~= nil then
778 transSpeed = move*tool.transSpeed
779 if tool.transAcceleration ~= nil and math.abs(transSpeed - tool.lastTransSpeed) >= tool.transAcceleration*dt then
780 if transSpeed > tool.lastTransSpeed then
781 transSpeed = tool.lastTransSpeed + tool.transAcceleration*dt
782 else
783 transSpeed = tool.lastTransSpeed - tool.transAcceleration*dt
784 end
785 end
786 end
787 if tool.animSpeed ~= nil then
788 animSpeed = move*tool.animSpeed
789 if tool.animAcceleration ~= nil and math.abs(animSpeed - tool.lastAnimSpeed) >= tool.animAcceleration*dt then
790 if animSpeed > tool.lastAnimSpeed then
791 animSpeed = tool.lastAnimSpeed + tool.animAcceleration*dt
792 else
793 animSpeed = tool.lastAnimSpeed - tool.animAcceleration*dt
794 end
795 end
796 end
797 else
798 -- decelerate
799 if tool.rotAcceleration ~= nil then
800 if tool.lastRotSpeed < 0 then
801 rotSpeed = math.min(tool.lastRotSpeed + tool.rotAcceleration*dt, 0)
802 else
803 rotSpeed = math.max(tool.lastRotSpeed - tool.rotAcceleration*dt, 0)
804 end
805 end
806 if tool.transAcceleration ~= nil then
807 if tool.lastTransSpeed < 0 then
808 transSpeed = math.min(tool.lastTransSpeed + tool.transAcceleration*dt, 0)
809 else
810 transSpeed = math.max(tool.lastTransSpeed - tool.transAcceleration*dt, 0)
811 end
812 end
813 if tool.animAcceleration ~= nil then
814 if tool.lastAnimSpeed < 0 then
815 animSpeed = math.min(tool.lastAnimSpeed + tool.animAcceleration*dt, 0)
816 else
817 animSpeed = math.max(tool.lastAnimSpeed - tool.animAcceleration*dt, 0)
818 end
819 end
820 end
821
822 local changed = false
823 if rotSpeed ~= nil and rotSpeed ~= 0 then
824 changed = changed or Cylindered.setToolRotation(self, tool, rotSpeed, dt)
825 else
826 tool.lastRotSpeed = 0
827 end
828 if transSpeed ~= nil and transSpeed ~= 0 then
829 changed = changed or Cylindered.setToolTranslation(self, tool, transSpeed, dt)
830 else
831 tool.lastTransSpeed = 0
832 end
833 if animSpeed ~= nil and animSpeed ~= 0 then
834 changed = changed or Cylindered.setToolAnimation(self, tool, animSpeed, dt)
835 else
836 tool.lastAnimSpeed = 0
837 end
838
839 for _, dependentTool in pairs(tool.dependentMovingTools) do
840 if dependentTool.speedScale ~= nil then
841 local isAllowed = true
842 if dependentTool.requiresMovement then
843 if not changed then
844 isAllowed = false
845 end
846 end
847
848 if isAllowed then
849 dependentTool.movingTool.externalMove = dependentTool.speedScale * tool.move
850 end
851 end
852
853 Cylindered.updateRotationBasedLimits(self, tool, dependentTool)
854
855 self:updateDependentToolLimits(tool, dependentTool)
856 end
857
858 if changed then
859 if tool.playSound then
860 spec.movingToolNeedsSound = true
861 end
862 Cylindered.setDirty(self, tool)
863 tool.networkPositionIsDirty = true
864 self:raiseDirtyFlags(tool.dirtyFlag)
865 self:raiseDirtyFlags(spec.cylinderedDirtyFlag)
866
867 -- keep moving tool at least 2 frames in a row network dirty, so the client will always recieve and set the final position of the tool
868 tool.networkDirtyNextFrame = true
869 else
870 if tool.networkDirtyNextFrame then
871 self:raiseDirtyFlags(tool.dirtyFlag)
872 self:raiseDirtyFlags(spec.cylinderedDirtyFlag)
873 tool.networkDirtyNextFrame = nil
874 end
875 end
876 end
877 else
878 -- client side
879 for i=1,table.getn(spec.movingTools) do
880 local tool = spec.movingTools[i]
881
882 tool.networkTimeInterpolator:update(dt)
883 local interpolationAlpha = tool.networkTimeInterpolator:getAlpha()
884 local changed = false
885
886 if self:getIsMovingToolActive(tool) then
887 if tool.rotSpeed ~= nil then
888 local newRot = tool.networkInterpolators.rotation:getInterpolatedValue(interpolationAlpha)
889 if math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then
890 changed = true
891 tool.curRot[tool.rotationAxis] = newRot
892 setRotation(tool.node, tool.curRot[1], tool.curRot[2], tool.curRot[3])
893 end
894 end
895
896 if tool.transSpeed ~= nil then
897 local newTrans = tool.networkInterpolators.translation:getInterpolatedValue(interpolationAlpha)
898 if math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then
899 changed = true
900 tool.curTrans[tool.translationAxis] = newTrans
901 setTranslation(tool.node, tool.curTrans[1], tool.curTrans[2], tool.curTrans[3])
902 end
903 end
904
905 if tool.animSpeed ~= nil then
906 local newAnimTime = tool.networkInterpolators.animation:getInterpolatedValue(interpolationAlpha)
907 if math.abs(newAnimTime - tool.curAnimTime) > 0.0001 then
908 changed = true
909 tool.curAnimTime = newAnimTime
910 self:setAnimationTime(tool.animName, newAnimTime)
911 end
912 end
913
914 if changed then
915 Cylindered.setDirty(self, tool)
916 end
917 end
918
919 for _, dependentTool in pairs(tool.dependentMovingTools) do
920 if not dependentTool.movingTool.syncMinRotLimits or not dependentTool.movingTool.syncMaxRotLimits then
921 self:updateDependentToolLimits(tool, dependentTool)
922 end
923 end
924
925 if tool.networkTimeInterpolator:isInterpolating() then
926 self:raiseActive()
927 end
928 end
929 end
930
931 for i=1, table.getn(spec.movingTools) do
932 local tool = spec.movingTools[i]
933 if tool.delayedHistoryIndex ~= nil and tool.delayedHistoryIndex > 0 then
934 self:updateDelayedTool(tool)
935 end
936 end
937end

onUpdateTick

Description
Definition
onUpdateTick()
Code
1228function Cylindered:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1229 if self.isClient then
1230 local spec = self.spec_cylindered
1231 for _,movingTool in pairs(spec.movingTools) do
1232 if movingTool.axisActionIndex ~= nil then
1233 -- check only movingTools from selected control group since the other movingTools action events are not registered
1234 if spec.currentControlGroupIndex == movingTool.controlGroupIndex then
1235 local actionEvent = spec.actionEvents[movingTool.axisActionIndex]
1236 if actionEvent ~= nil then
1237 g_inputBinding:setActionEventActive(actionEvent.actionEventId, self:getIsMovingToolActive(movingTool))
1238 end
1239 end
1240 end
1241 end
1242 end
1243end

onWriteStream

Description
Called on server side on join
Definition
onWriteStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
601function Cylindered:onWriteStream(streamId, connection)
602 local spec = self.spec_cylindered
603
604 if streamWriteBool(streamId, self:allowLoadMovingToolStates()) then
605 if not connection:getIsServer() then
606 for i=1, table.getn(spec.movingTools) do
607 local tool = spec.movingTools[i]
608 if tool.dirtyFlag ~= nil then
609 if tool.transSpeed ~= nil then
610 streamWriteFloat32(streamId, tool.curTrans[tool.translationAxis])
611 end
612 if tool.rotSpeed ~= nil then
613 streamWriteFloat32(streamId, tool.curRot[tool.rotationAxis])
614 end
615 if tool.animSpeed ~= nil then
616 streamWriteFloat32(streamId, tool.curAnimTime)
617 end
618 end
619 end
620 end
621 end
622end

onWriteUpdateStream

Description
Called on on update
Definition
onWriteUpdateStream(integer streamId, table connection, integer dirtyMask)
Arguments
integerstreamIdstream ID
tableconnectionconnection
integerdirtyMaskdirty mask
Code
695function Cylindered:onWriteUpdateStream(streamId, connection, dirtyMask)
696 local spec = self.spec_cylindered
697
698 -- if client, send input to server
699 if connection:getIsServer() then
700 if streamWriteBool(streamId, bitAND(dirtyMask, spec.cylinderedInputDirtyFlag) ~= 0) then
701 for _, tool in ipairs(spec.movingTools) do
702 if tool.axisActionIndex ~= nil then
703 local value = (MathUtil.clamp(tool.moveToSend / 5, -1, 1) + 1) / 2 * 4095
704 streamWriteUIntN(streamId, value, 12)
705 end
706 end
707 end
708 else
709 -- if server, send updated attributes
710 if streamWriteBool(streamId, bitAND(dirtyMask, spec.cylinderedDirtyFlag) ~= 0) then
711 for _, tool in ipairs(spec.movingTools) do
712 if tool.dirtyFlag ~= nil then
713 if streamWriteBool(streamId, bitAND(dirtyMask, tool.dirtyFlag) ~= 0 and self:getIsMovingToolActive(tool)) then
714 if tool.transSpeed ~= nil then
715 streamWriteFloat32(streamId, tool.curTrans[tool.translationAxis])
716 end
717 if tool.rotSpeed ~= nil then
718 local rot = tool.curRot[tool.rotationAxis]
719 if tool.rotMin == nil or tool.rotMax == nil then
720 NetworkUtil.writeCompressedAngle(streamId, rot)
721 else
722 if tool.syncMinRotLimits then
723 streamWriteFloat32(streamId, tool.rotMin)
724 end
725 if tool.syncMaxRotLimits then
726 streamWriteFloat32(streamId, tool.rotMax)
727 end
728 NetworkUtil.writeCompressedRange(streamId, rot, tool.rotMin, tool.rotMax, tool.rotSendNumBits)
729 end
730 end
731 if tool.animSpeed ~= nil then
732 NetworkUtil.writeCompressedRange(streamId, tool.curAnimTime, tool.animMinTime, tool.animMaxTime, tool.animSendNumBits)
733 end
734 end
735 end
736 end
737 end
738 end
739end

prerequisitesPresent

Description
Checks if all prerequisite specializations are loaded
Definition
prerequisitesPresent(table specializations)
Arguments
tablespecializationsspecializations
Return Values
booleanhasPrerequisitetrue if all prerequisite specializations are loaded
Code
19function Cylindered.prerequisitesPresent(specializations)
20 return true
21end

registerEventListeners

Description
Definition
registerEventListeners()
Code
78function Cylindered.registerEventListeners(vehicleType)
79 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Cylindered)
80 SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", Cylindered)
81 SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", Cylindered)
82 SpecializationUtil.registerEventListener(vehicleType, "onDelete", Cylindered)
83 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Cylindered)
84 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Cylindered)
85 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Cylindered)
86 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Cylindered)
87 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Cylindered)
88 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Cylindered)
89 SpecializationUtil.registerEventListener(vehicleType, "onPostUpdate", Cylindered)
90 SpecializationUtil.registerEventListener(vehicleType, "onDraw", Cylindered)
91 SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", Cylindered)
92 SpecializationUtil.registerEventListener(vehicleType, "onPostAttach", Cylindered)
93 SpecializationUtil.registerEventListener(vehicleType, "onSelect", Cylindered)
94 SpecializationUtil.registerEventListener(vehicleType, "onUnselect", Cylindered)
95 SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", Cylindered)
96end

registerEvents

Description
Definition
registerEvents()
Code
25function Cylindered.registerEvents(vehicleType)
26 SpecializationUtil.registerEvent(vehicleType, "onMovingToolChanged")
27end

registerFunctions

Description
Definition
registerFunctions()
Code
31function Cylindered.registerFunctions(vehicleType)
32 SpecializationUtil.registerFunction(vehicleType, "loadMovingPartFromXML", Cylindered.loadMovingPartFromXML)
33 SpecializationUtil.registerFunction(vehicleType, "loadMovingToolFromXML", Cylindered.loadMovingToolFromXML)
34 SpecializationUtil.registerFunction(vehicleType, "loadDependentMovingTools", Cylindered.loadDependentMovingTools)
35 SpecializationUtil.registerFunction(vehicleType, "loadDependentParts", Cylindered.loadDependentParts)
36 SpecializationUtil.registerFunction(vehicleType, "loadDependentComponentJoints", Cylindered.loadDependentComponentJoints)
37 SpecializationUtil.registerFunction(vehicleType, "loadDependentAttacherJoints", Cylindered.loadDependentAttacherJoints)
38 SpecializationUtil.registerFunction(vehicleType, "loadDependentWheels", Cylindered.loadDependentWheels)
39 SpecializationUtil.registerFunction(vehicleType, "loadDependentTranslatingParts", Cylindered.loadDependentTranslatingParts)
40 SpecializationUtil.registerFunction(vehicleType, "loadExtraDependentParts", Cylindered.loadExtraDependentParts)
41 SpecializationUtil.registerFunction(vehicleType, "loadDependentAnimations", Cylindered.loadDependentAnimations)
42 SpecializationUtil.registerFunction(vehicleType, "loadCopyLocalDirectionParts", Cylindered.loadCopyLocalDirectionParts)
43 SpecializationUtil.registerFunction(vehicleType, "loadRotationBasedLimits", Cylindered.loadRotationBasedLimits)
44 SpecializationUtil.registerFunction(vehicleType, "setMovingToolDirty", Cylindered.setMovingToolDirty)
45 SpecializationUtil.registerFunction(vehicleType, "updateCylinderedInitial", Cylindered.updateCylinderedInitial)
46 SpecializationUtil.registerFunction(vehicleType, "allowLoadMovingToolStates", Cylindered.allowLoadMovingToolStates)
47 SpecializationUtil.registerFunction(vehicleType, "getMovingToolByNode", Cylindered.getMovingToolByNode)
48 SpecializationUtil.registerFunction(vehicleType, "getMovingPartByNode", Cylindered.getMovingPartByNode)
49 SpecializationUtil.registerFunction(vehicleType, "getIsMovingToolActive", Cylindered.getIsMovingToolActive)
50 SpecializationUtil.registerFunction(vehicleType, "setDelayedData", Cylindered.setDelayedData)
51 SpecializationUtil.registerFunction(vehicleType, "updateDelayedTool", Cylindered.updateDelayedTool)
52 SpecializationUtil.registerFunction(vehicleType, "updateEasyControl", Cylindered.updateEasyControl)
53 SpecializationUtil.registerFunction(vehicleType, "setIsEasyControlActive", Cylindered.setIsEasyControlActive)
54 SpecializationUtil.registerFunction(vehicleType, "updateExtraDependentParts", Cylindered.updateExtraDependentParts)
55 SpecializationUtil.registerFunction(vehicleType, "updateDependentAnimations", Cylindered.updateDependentAnimations)
56 SpecializationUtil.registerFunction(vehicleType, "updateDependentToolLimits", Cylindered.updateDependentToolLimits)
57end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
61function Cylindered.registerOverwrittenFunctions(vehicleType)
62 SpecializationUtil.registerOverwrittenFunction(vehicleType, "isDetachAllowed", Cylindered.isDetachAllowed)
63 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadObjectChangeValuesFromXML", Cylindered.loadObjectChangeValuesFromXML)
64 SpecializationUtil.registerOverwrittenFunction(vehicleType, "setObjectChangeValues", Cylindered.setObjectChangeValues)
65 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadDischargeNode", Cylindered.loadDischargeNode)
66 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDischargeNodeEmptyFactor", Cylindered.getDischargeNodeEmptyFactor)
67 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadShovelNode", Cylindered.loadShovelNode)
68 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getShovelNodeIsActive", Cylindered.getShovelNodeIsActive)
69 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadDynamicMountGrabFromXML", Cylindered.loadDynamicMountGrabFromXML)
70 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsDynamicMountGrabOpened", Cylindered.getIsDynamicMountGrabOpened)
71 SpecializationUtil.registerOverwrittenFunction(vehicleType, "setComponentJointFrame", Cylindered.setComponentJointFrame)
72 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAdditionalSchemaText", Cylindered.getAdditionalSchemaText)
73 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getWearMultiplier", Cylindered.getWearMultiplier)
74end

saveToXMLFile

Description
Definition
saveToXMLFile()
Code
533function Cylindered:saveToXMLFile(xmlFile, key, usedModNames)
534 local spec = self.spec_cylindered
535
536 local index = 0
537 for _, tool in ipairs(spec.movingTools) do
538 if tool.saving then
539 local toolKey = string.format("%s.movingTool(%d)", key, index)
540
541 if tool.transSpeed ~= nil then
542 setXMLFloat(xmlFile, toolKey.."#translation", tool.curTrans[tool.translationAxis])
543 end
544 if tool.rotSpeed ~= nil then
545 setXMLFloat(xmlFile, toolKey.."#rotation", tool.curRot[tool.rotationAxis])
546 end
547 if tool.animSpeed ~= nil then
548 setXMLFloat(xmlFile, toolKey.."#animationTime", tool.curAnimTime)
549 end
550
551 index = index + 1
552 end
553 end
554end

setComponentJointFrame

Description
Definition
setComponentJointFrame()
Code
2232function Cylindered:setComponentJointFrame(superFunc, jointDesc, anchorActor)
2233 superFunc(self, jointDesc, anchorActor)
2234
2235 -- update the joint to component offset
2236 local spec = self.spec_cylindered
2237 for _, movingTool in ipairs(spec.movingTools) do
2238 for _, componentJoint in ipairs(movingTool.componentJoints) do
2239 local componentJointDesc = self.componentJoints[componentJoint.index]
2240
2241 local jointNode = componentJointDesc.jointNode
2242 if componentJoint.anchorActor == 1 then
2243 jointNode = componentJointDesc.jointNodeActor1
2244 end
2245
2246 local node = self.components[componentJointDesc.componentIndices[2]].node
2247 componentJoint.x, componentJoint.y, componentJoint.z = localToLocal(node, jointNode, 0,0,0)
2248 componentJoint.upX, componentJoint.upY, componentJoint.upZ = localDirectionToLocal(node, jointNode, 0,1,0)
2249 componentJoint.dirX, componentJoint.dirY, componentJoint.dirZ = localDirectionToLocal(node, jointNode, 0,0,1)
2250 end
2251 end
2252end

setDelayedData

Description
Definition
setDelayedData()
Code
942function Cylindered:setDelayedData(tool, immediate)
943 local x, y, z = getTranslation(tool.node)
944 local rx, ry, rz = getRotation(tool.node)
945
946 tool.delayedHistroyData[3] = {rot = {rx, ry, rz}, trans = {x, y, z}}
947 if immediate then
948 tool.delayedHistroyData[2] = tool.delayedHistroyData[3]
949 tool.delayedHistroyData[1] = tool.delayedHistroyData[2]
950 end
951
952 tool.delayedHistoryIndex = 3
953end

setDirty

Description
Set dirty
Definition
setDirty(table part)
Arguments
tablepartpart to set dirty
Code
2527function Cylindered.setDirty(self, part)
2528 if not part.isDirty or self.spec_cylindered.isLoading then -- during loading we always allow setting dirty since we do not reset it
2529 part.isDirty = true
2530 self.anyMovingPartsDirty = true
2531
2532 if part.delayedNode ~= nil then
2533 self:setDelayedData(part)
2534 end
2535
2536 -- on moving tools we update the wheels and attacher joints on dirty since the are updated from external influences (e.g. animations)
2537 -- on moving parts the part is updated first and then the wheels and attacher joints are updated
2538 if part.isTool then
2539 Cylindered.updateAttacherJoints(self, part)
2540 Cylindered.updateWheels(self, part)
2541 end
2542
2543 for _, v in pairs(part.dependentParts) do
2544 Cylindered.setDirty(self, v)
2545 end
2546 end
2547end

setIsEasyControlActive

Description
Definition
setIsEasyControlActive()
Code
1126function Cylindered:setIsEasyControlActive(state, noEventSend)
1127 if self.isServer then
1128 local spec = self.spec_cylindered
1129 local easyArmControl = spec.easyArmControl
1130 if easyArmControl ~= nil then
1131 local targetYTool = self:getMovingToolByNode(easyArmControl.targetNodeY)
1132 local targetZTool = self:getMovingToolByNode(easyArmControl.targetNodeZ)
1133
1134 if state then
1135 local _, y, z = localToLocal(easyArmControl.targetRefNode, getParent(easyArmControl.targetNodeY), 0, 0, 0)
1136 local _, oldY, _ = getTranslation(easyArmControl.targetNodeY)
1137 if Cylindered.setToolTranslation(self, targetYTool, nil, 0, y-oldY) then
1138 Cylindered.setDirty(self, targetYTool)
1139
1140 self:raiseDirtyFlags(targetYTool.dirtyFlag)
1141 end
1142
1143 _, _, z = localToLocal(easyArmControl.targetRefNode, getParent(easyArmControl.targetNodeZ), 0, 0, 0)
1144 local _, _, oldZ = getTranslation(easyArmControl.targetNodeZ)
1145 if Cylindered.setToolTranslation(self, targetZTool, nil, 0, z-oldZ) then
1146 Cylindered.setDirty(self, targetZTool)
1147
1148 self:raiseDirtyFlags(targetZTool.dirtyFlag)
1149 end
1150
1151 self:raiseDirtyFlags(spec.cylinderedDirtyFlag)
1152 end
1153 end
1154 end
1155
1156 CylinderedEasyControlChangeEvent.sendEvent(self, state, noEventSend)
1157end

setMovingToolDirty

Description
Set moving tool dirty
Definition
setMovingToolDirty(integer node)
Arguments
integernodenode id
Code
1946function Cylindered:setMovingToolDirty(node)
1947 local spec = self.spec_cylindered
1948
1949 local tool = spec.nodesToMovingTools[node]
1950 if tool ~= nil then
1951 local x,y,z = getRotation(tool.node)
1952 tool.curRot[1] = x
1953 tool.curRot[2] = y
1954 tool.curRot[3] = z
1955 local x,y,z = getTranslation(tool.node)
1956 tool.curTrans[1] = x
1957 tool.curTrans[2] = y
1958 tool.curTrans[3] = z
1959 Cylindered.setDirty(self, tool)
1960
1961 if not self.isServer and self.isClient then
1962 tool.networkInterpolators.translation:setValue(tool.curTrans[tool.translationAxis])
1963 tool.networkInterpolators.rotation:setAngle(tool.curRot[tool.rotationAxis])
1964 end
1965 end
1966end

setObjectChangeValues

Description
Sets object change values
Definition
setObjectChangeValues(table object, boolean isActive)
Arguments
tableobjectobject
booleanisActiveis active
Code
2088function Cylindered:setObjectChangeValues(superFunc, object, isActive)
2089 superFunc(self, object, isActive)
2090
2091 local spec = self.spec_cylindered
2092
2093 if spec.nodesToMovingTools ~= nil and spec.nodesToMovingTools[object.node] ~= nil then
2094 local movingTool = spec.nodesToMovingTools[object.node]
2095 if isActive then
2096 movingTool.rotMax = object.movingToolRotMaxActive
2097 movingTool.rotMin = object.movingToolRotMinActive
2098 else
2099 movingTool.rotMax = object.movingToolRotMaxInactive
2100 movingTool.rotMin = object.movingToolRotMinInactive
2101 end
2102 end
2103end

setToolAnimation

Description
Set tool animation
Definition
setToolAnimation(table tool, float animSpeed, float dt)
Arguments
tabletooltool
floatanimSpeedanimation speed
floatdttime since last call in ms
Return Values
booleanchangedanimation changed
Code
2483function Cylindered.setToolAnimation(self, tool, animSpeed, dt)
2484 local lastAnimTime = self:getAnimationTime(tool.animName)
2485 local newAnimTime = lastAnimTime+animSpeed*dt
2486
2487 if tool.animMaxTime ~= nil then
2488 newAnimTime = math.min(newAnimTime, tool.animMaxTime)
2489 end
2490 if tool.animMinTime ~= nil then
2491 newAnimTime = math.max(newAnimTime, tool.animMinTime)
2492 end
2493 local diff = newAnimTime - lastAnimTime
2494 if dt ~= 0 then
2495 tool.lastAnimSpeed = diff / dt
2496 end
2497 if math.abs(diff) > 0.0001 then
2498 tool.curAnimTime = newAnimTime
2499 self:setAnimationTime(tool.animName, newAnimTime)
2500 SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, animSpeed, dt)
2501 return true
2502 end
2503
2504 return false
2505end

setToolRotation

Description
Set tool rotation
Definition
setToolRotation(table tool, float rotSpeed, float dt, float delta)
Arguments
tabletooltool
floatrotSpeedrotation speed
floatdttime since last call in ms
floatdeltadelta rotation
Return Values
booleanchangedrotation changed
Code
2435function Cylindered.setToolRotation(self, tool, rotSpeed, dt, delta)
2436 local curRot = { getRotation(tool.node) }
2437 local newRot = curRot[tool.rotationAxis]
2438 if rotSpeed ~= nil then
2439 newRot = newRot + rotSpeed*dt
2440 else
2441 newRot = newRot + delta
2442 end
2443 if tool.rotMax ~= nil then
2444 newRot = math.min(newRot, tool.rotMax)
2445 end
2446 if tool.rotMin ~= nil then
2447 newRot = math.max(newRot, tool.rotMin)
2448 end
2449 local diff = newRot - curRot[tool.rotationAxis]
2450 if rotSpeed ~= nil then
2451 if dt ~= 0 then
2452 tool.lastRotSpeed = diff/dt
2453 end
2454 end
2455
2456 if math.abs(diff) > 0.0001 then
2457 -- wrap if not limited
2458 if tool.rotMin == nil and tool.rotMax == nil then
2459 if newRot > 2*math.pi then
2460 newRot = newRot - 2*math.pi
2461 end
2462 if newRot < 0 then
2463 newRot = newRot + 2*math.pi
2464 end
2465 end
2466 tool.curRot[tool.rotationAxis] = newRot
2467 setRotation(tool.node, unpack(tool.curRot))
2468
2469 SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, rotSpeed, dt)
2470
2471 return true
2472 end
2473
2474 return false
2475end

setToolTranslation

Description
Set tool translation
Definition
setToolTranslation(table tool, float transSpeed, float dt)
Arguments
tabletooltool
floattransSpeedtranslation speed
floatdttime since last call in ms
Return Values
booleanchangedtranslation changed
Code
2396function Cylindered.setToolTranslation(self, tool, transSpeed, dt, delta)
2397 local curTrans = { getTranslation(tool.node) }
2398 local newTrans = curTrans[tool.translationAxis]
2399 local oldTrans = newTrans
2400 if transSpeed ~= nil then
2401 newTrans = newTrans + transSpeed*dt
2402 else
2403 newTrans = newTrans + delta
2404 end
2405 if tool.transMax ~= nil then
2406 newTrans = math.min(newTrans, tool.transMax)
2407 end
2408 if tool.transMin ~= nil then
2409 newTrans = math.max(newTrans, tool.transMin)
2410 end
2411 local diff = newTrans - oldTrans
2412 if dt ~= 0 then
2413 tool.lastTransSpeed = diff/dt
2414 end
2415 if math.abs(diff) > 0.0001 then
2416 curTrans[tool.translationAxis] = newTrans
2417 tool.curTrans = curTrans
2418 setTranslation(tool.node, unpack(tool.curTrans))
2419
2420 SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, transSpeed, dt)
2421
2422 return true
2423 end
2424
2425 return false
2426end

updateAttacherJoints

Description
Update attacher joints
Definition
updateAttacherJoints(table entry)
Arguments
tableentryentry
Code
2860function Cylindered.updateAttacherJoints(self, entry)
2861 if self.isServer then
2862 if entry.attacherJoints ~= nil then
2863 for _,joint in ipairs(entry.attacherJoints) do
2864 if joint.jointIndex ~= 0 then
2865 setJointFrame(joint.jointIndex, 0, joint.jointTransform)
2866 end
2867 end
2868 end
2869
2870 if entry.inputAttacherJoint then
2871 if self.getAttacherVehicle ~= nil then
2872 local attacherVehicle = self:getAttacherVehicle()
2873 if attacherVehicle ~= nil then
2874
2875 local attacherJoints = attacherVehicle:getAttacherJoints()
2876 if attacherJoints ~= nil then
2877
2878 local jointDescIndex = attacherVehicle:getAttacherJointIndexFromObject(self)
2879 if jointDescIndex ~= nil then
2880 local jointDesc = attacherJoints[jointDescIndex]
2881
2882 local inputAttacherJoint = self:getActiveInputAttacherJoint()
2883 if inputAttacherJoint ~= nil then
2884 setJointFrame(jointDesc.jointIndex, 1, inputAttacherJoint.node )
2885 end
2886 end
2887 end
2888 end
2889 end
2890 end
2891 end
2892end

updateComponentJoints

Description
Update component joints
Definition
updateComponentJoints(table entry, boolean placeComponents)
Arguments
tableentryentry
booleanplaceComponentsplace components
Code
2831function Cylindered.updateComponentJoints(self, entry, placeComponents)
2832 if self.isServer then
2833 if entry.componentJoints ~= nil then
2834 for _,joint in ipairs(entry.componentJoints) do
2835 local componentJoint = joint.componentJoint
2836
2837 local jointNode = componentJoint.jointNode
2838 if joint.anchorActor == 1 then
2839 jointNode = componentJoint.jointNodeActor1
2840 end
2841
2842 if placeComponents then
2843 local node = self.components[componentJoint.componentIndices[2]].node
2844 local x,y,z = localToWorld(jointNode, joint.x, joint.y, joint.z)
2845 local upX,upY,upZ = localDirectionToWorld(jointNode, joint.upX,joint.upY,joint.upZ)
2846 local dirX,dirY,dirZ = localDirectionToWorld(jointNode, joint.dirX,joint.dirY,joint.dirZ)
2847 setWorldTranslation(node, x,y,z)
2848 I3DUtil.setWorldDirection(node, dirX,dirY,dirZ, upX,upY,upZ)
2849 end
2850
2851 self:setComponentJointFrame(componentJoint, joint.anchorActor)
2852 end
2853 end
2854 end
2855end

updateCylinderedInitial

Description
Initial update of cylindered
Definition
updateCylinderedInitial(boolean placeComponents)
Arguments
booleanplaceComponentsplace components
Code
1971function Cylindered:updateCylinderedInitial(placeComponents, keepDirty)
1972 if placeComponents == nil then
1973 placeComponents = true
1974 end
1975
1976 if keepDirty == nil then
1977 keepDirty = false
1978 end
1979
1980 local spec = self.spec_cylindered
1981
1982 for _, part in pairs(spec.activeDirtyMovingParts) do
1983 Cylindered.setDirty(self, part)
1984 end
1985
1986 for _, tool in ipairs(spec.movingTools) do
1987 if tool.isDirty then
1988 Cylindered.updateWheels(self, tool)
1989 if self.isServer then
1990 Cylindered.updateComponentJoints(self, tool, placeComponents)
1991 end
1992 tool.isDirty = false or keepDirty
1993 end
1994
1995 self:updateDependentAnimations(tool, 9999)
1996 end
1997
1998 for _, part in ipairs(spec.movingParts) do
1999 if part.isDirty then
2000 Cylindered.updateMovingPart(self, part, placeComponents)
2001 Cylindered.updateWheels(self, part)
2002 part.isDirty = false or keepDirty
2003 end
2004
2005 self:updateDependentAnimations(part, 9999)
2006 end
2007end

updateDelayedTool

Description
Definition
updateDelayedTool()
Code
957function Cylindered:updateDelayedTool(tool, forceLastPosition)
958 local spec = self.spec_cylindered
959
960 if forceLastPosition ~= nil and forceLastPosition then
961 tool.delayedHistroyData[2] = tool.delayedHistroyData[3]
962 tool.delayedHistroyData[1] = tool.delayedHistroyData[2]
963 end
964
965 local currentData = tool.delayedHistroyData[1]
966 tool.delayedHistroyData[1] = tool.delayedHistroyData[2]
967 tool.delayedHistroyData[2] = tool.delayedHistroyData[3]
968
969 setRotation(tool.delayedNode, unpack(currentData.rot))
970 setTranslation(tool.delayedNode, unpack(currentData.trans))
971
972-- local r, _, _ = getRotation(tool.node)
973-- log(string.format("%s: %.2f | %s: %.2f", getName(tool.node), math.deg(r), getName(tool.delayedNode), math.deg(currentData.rot[1])))
974
975 tool.delayedHistoryIndex = tool.delayedHistoryIndex - 1
976
977 local movingPart = spec.nodesToMovingParts[tool.delayedNode]
978 local movingTool = spec.nodesToMovingTools[tool.delayedNode]
979 if movingPart ~= nil then
980 Cylindered.setDirty(self, movingPart)
981 end
982 if spec.nodesToMovingTools[tool.delayedNode] ~= nil then
983 Cylindered.setDirty(self, movingTool)
984 end
985end

updateDependentAnimations

Description
Definition
updateDependentAnimations()
Code
1166function Cylindered:updateDependentAnimations(part, dt)
1167 if #part.dependentAnimations > 0 then
1168 for _, dependentAnimation in ipairs(part.dependentAnimations) do
1169 local pos = 0
1170 if dependentAnimation.translationAxis ~= nil then
1171 local retValues = {getTranslation(dependentAnimation.node)}
1172 pos = (retValues[dependentAnimation.translationAxis] - dependentAnimation.minValue) / (dependentAnimation.maxValue - dependentAnimation.minValue)
1173 end
1174
1175 if dependentAnimation.rotationAxis ~= nil then
1176 local retValues = {getRotation(dependentAnimation.node)}
1177 pos = (retValues[dependentAnimation.rotationAxis] - dependentAnimation.minValue) / (dependentAnimation.maxValue - dependentAnimation.minValue)
1178 end
1179
1180 pos = MathUtil.clamp(math.abs(pos), 0, 1)
1181 if dependentAnimation.invert then
1182 pos = 1-pos
1183 end
1184 dependentAnimation.lastPos = pos
1185
1186 self:setAnimationTime(dependentAnimation.name, pos, true)
1187 end
1188 end
1189end

updateDependentToolLimits

Description
Definition
updateDependentToolLimits()
Code
1193function Cylindered:updateDependentToolLimits(tool, dependentTool)
1194 if dependentTool.minTransLimits ~= nil or dependentTool.maxTransLimits ~= nil then
1195 local state = Cylindered.getMovingToolState(self, tool)
1196 if dependentTool.minTransLimits ~= nil then
1197 dependentTool.movingTool.transMin = MathUtil.lerp(dependentTool.minTransLimits[1], dependentTool.minTransLimits[2], 1-state)
1198 end
1199 if dependentTool.maxTransLimits ~= nil then
1200 dependentTool.movingTool.transMax = MathUtil.lerp(dependentTool.maxTransLimits[1], dependentTool.maxTransLimits[2], 1-state)
1201 end
1202 local transLimitChanged = Cylindered.setToolTranslation(self, dependentTool.movingTool, 0, 0)
1203 if transLimitChanged then
1204 Cylindered.setDirty(self, dependentTool.movingTool)
1205 end
1206 end
1207
1208 if dependentTool.minRotLimits ~= nil or dependentTool.maxRotLimits ~= nil then
1209 local state = Cylindered.getMovingToolState(self, tool)
1210 if dependentTool.minRotLimits ~= nil then
1211 dependentTool.movingTool.rotMin = MathUtil.lerp(dependentTool.minRotLimits[1], dependentTool.minRotLimits[2], 1-state)
1212 end
1213 if dependentTool.maxRotLimits ~= nil then
1214 dependentTool.movingTool.rotMax = MathUtil.lerp(dependentTool.maxRotLimits[1], dependentTool.maxRotLimits[2], 1-state)
1215 end
1216
1217 dependentTool.movingTool.networkInterpolators.rotation:setMinMax(dependentTool.movingTool.rotMin, dependentTool.movingTool.rotMax)
1218
1219 local rotLimitChanged = Cylindered.setToolRotation(self, dependentTool.movingTool, 0, 0)
1220 if rotLimitChanged then
1221 Cylindered.setDirty(self, dependentTool.movingTool)
1222 end
1223 end
1224end

updateEasyControl

Description
Definition
updateEasyControl()
Code
989function Cylindered:updateEasyControl(dt, updateDelayedNodes)
990 local spec = self.spec_cylindered
991 local easyArmControl = spec.easyArmControl
992 if easyArmControl ~= nil then
993 local targetYTool = self:getMovingToolByNode(easyArmControl.targetNodeY)
994 local targetZTool = self:getMovingToolByNode(easyArmControl.targetNodeZ)
995
996 local easyArmControlState = g_gameSettings:getValue("easyArmControl")
997 local easyArmControlsActive = true
998 easyArmControlsActive = easyArmControlsActive and self:getIsMovingToolActive(targetYTool)
999 easyArmControlsActive = easyArmControlsActive and self:getIsMovingToolActive(targetZTool)
1000
1001 local hasChanged = false
1002 if self.isClient then
1003 if spec.lastEasyArmControlState ~= easyArmControlState or spec.lastEasyArmControlsActive ~= easyArmControlsActive then
1004 spec.lastEasyArmControlState = easyArmControlState
1005 spec.lastEasyArmControlsActive = easyArmControlsActive
1006 self:requestActionEventUpdate()
1007
1008 local isActive = easyArmControlState and easyArmControlsActive
1009 self:setIsEasyControlActive(isActive)
1010 hasChanged = isActive
1011 end
1012 end
1013
1014 local tYx, tYy, tYz = getTranslation(easyArmControl.targetNodeY)
1015 local tZx, tZy, tZz = getTranslation(easyArmControl.targetNodeZ)
1016 if tYx+tYy+tYz ~= easyArmControl.oldTargetNodeYTrans or tZx+tZy+tZz ~= easyArmControl.oldTargetNodeZTrans then
1017 hasChanged = true
1018 end
1019
1020 if self.isServer and easyArmControlState and hasChanged and easyArmControlsActive then
1021 easyArmControl.oldTargetNodeYTrans = tYx+tYy+tYz
1022 easyArmControl.oldTargetNodeZTrans = tZx+tZy+tZz
1023
1024 local xRotNode1 = easyArmControl.xRotationNodes[1].refNode
1025 local xRotNode2 = easyArmControl.xRotationNodes[2].refNode
1026
1027 local x,y,z = localToLocal(easyArmControl.targetNode, getParent(xRotNode1), 0, 0, 0)
1028 local distance = MathUtil.vector3Length(x, y, z)
1029 local transDelta = distance - easyArmControl.xRotationMaxDistance
1030
1031 for i, translationNode in ipairs(easyArmControl.zTranslationNodes) do
1032 local tool = translationNode.movingTool
1033 local targetTrans = MathUtil.clamp(transDelta*translationNode.transFactor, tool.transMin, tool.transMax)
1034 local _, _, z = getTranslation(translationNode.node)
1035 local deltaTrans = targetTrans - z
1036
1037 if Cylindered.setToolTranslation(self, tool, nil, 0, deltaTrans) then
1038 Cylindered.setDirty(self, tool)
1039
1040 self:raiseDirtyFlags(tool.dirtyFlag)
1041 self:raiseDirtyFlags(spec.cylinderedDirtyFlag)
1042 end
1043 end
1044
1045 local _,_,node1Length = localToLocal(xRotNode2, xRotNode1, 0, 0, 0)
1046 local _, b, c = localToLocal(easyArmControl.targetRefNode, easyArmControl.xRotationNodes[2].node, 0, 0, 0)
1047 local node2Length = MathUtil.vector2Length(b, c)
1048 local _, ly, lz = localToLocal(easyArmControl.targetNode, getParent(xRotNode1), 0, 0, 0)
1049 local _, _, iy, iz = MathUtil.getCircleCircleIntersection(0,0, node1Length, ly, lz, node2Length)
1050 local isOutOfRange = true
1051
1052 if iy ~= nil and iz ~= nil then
1053 local node1Rotation = -math.atan2(iy, iz)
1054 local node1Tool = easyArmControl.xRotationNodes[1].movingTool
1055
1056 if node1Rotation >= node1Tool.rotMin and node1Rotation <= node1Tool.rotMax then
1057 local node2Rotation = math.pi - math.acos((node1Length*node1Length + node2Length*node2Length - distance*distance) / (2*node1Length*node2Length))
1058 local node2Tool = easyArmControl.xRotationNodes[2].movingTool
1059
1060 if node2Rotation >= node2Tool.rotMin and node2Rotation <= node2Tool.rotMax then
1061 setRotation(xRotNode1, node1Rotation, 0, 0)
1062 setRotation(xRotNode2, node2Rotation, 0, 0)
1063
1064 for i=1, 2 do
1065 local rotationNode = easyArmControl.xRotationNodes[i]
1066 local rx,_,_ = getRotation(rotationNode.refNode)
1067 local x,_,_ = getRotation(rotationNode.node)
1068
1069 if Cylindered.setToolRotation(self, rotationNode.movingTool, nil, 0, rx-x) then
1070 Cylindered.setDirty(self, rotationNode.movingTool)
1071
1072 if updateDelayedNodes ~= nil and updateDelayedNodes then
1073 self:updateDelayedTool(rotationNode.movingTool)
1074 end
1075
1076 self:raiseDirtyFlags(rotationNode.movingTool.dirtyFlag)
1077 self:raiseDirtyFlags(spec.cylinderedDirtyFlag)
1078 end
1079 end
1080
1081 x, y, z = getTranslation(easyArmControl.targetNodeY)
1082 easyArmControl.lastValidPositionY[1] = x
1083 easyArmControl.lastValidPositionY[2] = y
1084 easyArmControl.lastValidPositionY[3] = z
1085
1086 x, y, z = getTranslation(easyArmControl.targetNodeZ)
1087 easyArmControl.lastValidPositionZ[1] = x
1088 easyArmControl.lastValidPositionZ[2] = y
1089 easyArmControl.lastValidPositionZ[3] = z
1090
1091 isOutOfRange = false
1092 end
1093 end
1094 end
1095
1096 if isOutOfRange then
1097 setTranslation(easyArmControl.targetNodeY, unpack(easyArmControl.lastValidPositionY))
1098 setTranslation(easyArmControl.targetNodeZ, unpack(easyArmControl.lastValidPositionZ))
1099 end
1100 end
1101
1102 if VehicleDebug.state == VehicleDebug.DEBUG_ATTRIBUTES then
1103 local xRotNode1 = easyArmControl.xRotationNodes[1].refNode
1104 local xRotNode2 = easyArmControl.xRotationNodes[2].refNode
1105
1106 local _,_,node1Length = localToLocal(xRotNode2, xRotNode1, 0, 0, 0)
1107 local _, b, c = localToLocal(easyArmControl.targetRefNode, easyArmControl.xRotationNodes[2].node, 0, 0, 0)
1108 local node2Length = MathUtil.vector2Length(b, c)
1109
1110 local x1,y1,z1 = localToWorld(xRotNode1, 0, 0, 0)
1111 local x2,y2,z2 = localToWorld(xRotNode1, 0, 0, node1Length)
1112 drawDebugLine(x1,y1,z1, 0, 1, 0, x2,y2,z2, 0, 1, 0)
1113
1114 x1,y1,z1 = localToWorld(xRotNode2, 0, 0, 0)
1115 x2,y2,z2 = localToWorld(xRotNode2, 0, 0, node2Length)
1116 drawDebugLine(x1,y1,z1, 1, 0, 0, x2,y2,z2, 1, 0, 0)
1117
1118 DebugUtil.drawDebugNode(easyArmControl.targetNode, "scTarget")
1119 DebugUtil.drawDebugNode(easyArmControl.targetRefNode, "scTargetRef")
1120 end
1121 end
1122end

updateExtraDependentParts

Description
Definition
updateExtraDependentParts()
Code
1161function Cylindered:updateExtraDependentParts(part, dt)
1162end

updateMovingPart

Description
Update moving part
Definition
updateMovingPart(table part, boolean placeComponents, boolean updateDependentParts)
Arguments
tablepartpart
booleanplaceComponentsplace components
booleanupdateDependentPartsupdate dependent parts
Code
2574function Cylindered.updateMovingPart(self, part, placeComponents, updateDependentParts)
2575
2576 -- the local reference point must be referenceDistance away from the referencePoint
2577 local refX,refY,refZ
2578 local dirX, dirY, dirZ = 0,0,0
2579 local changed = false
2580 if part.referencePoint ~= nil then
2581 if part.moveToReferenceFrame then
2582 local x,y,z = localToLocal(part.referenceFrame, getParent(part.node), part.referenceFrameOffset[1], part.referenceFrameOffset[2], part.referenceFrameOffset[3])
2583 setTranslation(part.node, x,y,z)
2584 changed = true
2585 end
2586 refX,refY,refZ = getWorldTranslation(part.referencePoint)
2587 if part.referenceDistance == 0 then
2588 if part.useLocalOffset then
2589 local lx, ly, lz = worldToLocal(part.node, refX, refY, refZ)
2590 dirX, dirY, dirZ = localDirectionToWorld(part.node, lx-part.localReferencePoint[1], ly-part.localReferencePoint[2], lz)
2591 else
2592 local x,y,z = getWorldTranslation(part.node)
2593 dirX, dirY, dirZ = refX - x, refY-y, refZ-z
2594 end
2595 else
2596 if part.updateLocalReferenceDistance then
2597 local _,y,z = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode))
2598 part.localReferenceDistance = MathUtil.vector2Length(y, z)
2599 end
2600 if part.referenceDistancePoint ~= nil then
2601 local _,_,z = worldToLocal(part.node, getWorldTranslation(part.referenceDistancePoint))
2602 part.referenceDistance = z
2603 end
2604
2605 if part.localReferenceTranslate then
2606 local _, ly, lz = worldToLocal(part.node, refX, refY, refZ)
2607
2608 -- calculate line-circle intersection
2609 if math.abs(ly) < part.referenceDistance then
2610 local dz = math.sqrt(part.referenceDistance*part.referenceDistance - ly*ly)
2611
2612 local z1 = (lz - dz) - part.localReferenceDistance
2613 local z2 = (lz + dz) - part.localReferenceDistance
2614 if math.abs(z2) < math.abs(z1) then
2615 z1 = z2
2616 end
2617 local parentNode = getParent(part.node)
2618 local tx,ty,tz = unpack(part.localReferenceTranslation)
2619 local _, _, coz = localToLocal(parentNode, part.node, tx,ty,tz)
2620 local ox,oy,oz = localDirectionToLocal(part.node, parentNode, 0,0,z1-coz)
2621 setTranslation(part.node, tx+ox,ty+oy,tz+oz)
2622 changed = true
2623 end
2624 else
2625
2626 local r1 = part.localReferenceDistance
2627 local r2 = part.referenceDistance
2628 local _, ly, lz = worldToLocal(part.node, refX, refY, refZ)
2629 --print("intersect: "..ly .. " "..lz)
2630 local ix, iy, i2x, i2y = MathUtil.getCircleCircleIntersection(0,0, r1, ly, lz, r2)
2631
2632 local allowUpdate = true
2633 if part.referenceDistanceThreshold > 0 then
2634 local lRefX, lRefY, lRefZ = worldToLocal(part.node, getWorldTranslation(part.referencePoint))
2635 local x,y,z = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode))
2636 local currentDistance = MathUtil.vector3Length(lRefX-x, lRefY-y, lRefZ-z)
2637
2638 if math.abs(currentDistance-part.referenceDistance) < part.referenceDistanceThreshold then
2639 allowUpdate = false
2640 end
2641 end
2642
2643 if allowUpdate and ix ~= nil then
2644 if i2x ~= nil then
2645 -- use the point which as the same angle side as the original configuration
2646 local side = ix*(lz-iy) - iy*(ly-ix)
2647 if (side < 0) ~= (part.localReferenceAngleSide < 0) then
2648 iy = i2y
2649 ix = i2x
2650 end
2651 end
2652 dirX, dirY, dirZ = localDirectionToWorld(part.node, 0, ix, iy)
2653 changed = true
2654 end
2655 end
2656 end
2657 else
2658 if part.alignToWorldY then
2659 dirX, dirY, dirZ = localDirectionToWorld(getRootNode(), 0, 1, 0)
2660 changed = true
2661 else
2662 dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, 0, 0, 1)
2663 changed = true
2664 end
2665 if part.moveToReferenceFrame then
2666 local x,y,z = localToLocal(part.referenceFrame, getParent(part.node), part.referenceFrameOffset[1], part.referenceFrameOffset[2], part.referenceFrameOffset[3])
2667 setTranslation(part.node, x,y,z)
2668 changed = true
2669 end
2670
2671 if part.doLineAlignment then
2672 local foundPoint = false
2673 for i=1, #part.orientationLineNodes-1 do
2674 local startNode = part.orientationLineNodes[i]
2675 local endNode = part.orientationLineNodes[i+1]
2676
2677 local _, sy, sz = localToLocal(startNode, part.referenceFrame, 0, 0, 0)
2678 local _, ey, ez = localToLocal(endNode, part.referenceFrame, 0, 0, 0)
2679 local _, cy, cz = localToLocal(part.node, part.referenceFrame, 0, 0, 0)
2680
2681 local hasIntersection, i1y, i1z, i2y, i2z = MathUtil.getCircleLineIntersection(cy, cz, part.partLength, sy, sz, ey, ez)
2682 if hasIntersection then
2683 local targetY, targetZ
2684 if not MathUtil.getIsOutOfBounds(i1y, sy, ey) and not MathUtil.getIsOutOfBounds(i1z, sz, ez) then
2685 targetY, targetZ = i1y, i1z
2686 foundPoint = true
2687 end
2688 if not MathUtil.getIsOutOfBounds(i2y, sy, ey) and not MathUtil.getIsOutOfBounds(i2z, sz, ez) then
2689 targetY, targetZ = i2y, i2z
2690 foundPoint = true
2691 end
2692
2693 if foundPoint and not MathUtil.isNan(targetY) and not MathUtil.isNan(targetZ) then
2694 dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, 0, targetY, targetZ)
2695 local upX, upY, upZ = localDirectionToWorld(part.referenceFrame, 0, 1, 0)
2696
2697 I3DUtil.setWorldDirection(part.node, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot)
2698 changed = true
2699 break
2700 end
2701 end
2702 end
2703 end
2704 end
2705
2706 -- if the vehicle is not active we use thresholds to update the moving parts
2707 if not self:getIsActive() then
2708 if part.directionThreshold ~= nil and part.directionThreshold > 0 then
2709 local lDirX, lDirY, lDirZ = worldDirectionToLocal(getParent(part.node), dirX, dirY, dirZ)
2710 local upX, upY, upZ = localDirectionToWorld(part.referenceFrame, 0, 1, 0)
2711 if math.abs(part.lastDirection[1] - lDirX) > part.directionThreshold or
2712 math.abs(part.lastDirection[2] - lDirY) > part.directionThreshold or
2713 math.abs(part.lastDirection[3] - lDirZ) > part.directionThreshold or
2714 math.abs(part.lastUpVector[1] - upX) > part.directionThreshold or
2715 math.abs(part.lastUpVector[2] - upY) > part.directionThreshold or
2716 math.abs(part.lastUpVector[3] - upZ) > part.directionThreshold then
2717 part.lastDirection = {lDirX, lDirY, lDirZ}
2718 part.lastUpVector = {upX, upY, upZ}
2719 else
2720 dirX, dirY, dirZ = 0, 0, 0
2721 changed = false
2722 end
2723 end
2724 end
2725
2726 if (dirX ~= 0 or dirY ~= 0 or dirZ ~= 0) and part.doDirectionAlignment then
2727 local upX, upY, upZ = localDirectionToWorld(part.referenceFrame, 0, 1, 0)
2728 if part.invertZ then
2729 dirX = -dirX
2730 dirY = -dirY
2731 dirZ = -dirZ
2732 end
2733
2734 I3DUtil.setWorldDirection(part.node, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot)
2735 changed = true
2736
2737 if part.scaleZ and part.localReferenceDistance ~= nil then
2738 local len = MathUtil.vector3Length(dirX, dirY, dirZ)
2739 setScale(part.node, 1, 1, len/part.localReferenceDistance)
2740 end
2741 end
2742 if part.doRotationAlignment then
2743 local x,y,z = getRotation(part.referenceFrame)
2744 setRotation(part.node, x*part.rotMultiplier, y*part.rotMultiplier, z*part.rotMultiplier)
2745 changed = true
2746 end
2747
2748 if part.referencePoint ~= nil then
2749 local numTranslatingParts = table.getn(part.translatingParts)
2750 if numTranslatingParts > 0 then
2751 local _, _, dist = worldToLocal(part.node, refX, refY, refZ)
2752 for i=1,numTranslatingParts do
2753 local translatingPart = part.translatingParts[i]
2754 local newZ = (dist - translatingPart.referenceDistance)/numTranslatingParts
2755
2756 if translatingPart.minZTrans ~= nil then
2757 newZ = math.max(translatingPart.minZTrans, newZ)
2758 end
2759
2760 if translatingPart.maxZTrans ~= nil then
2761 newZ = math.min(translatingPart.maxZTrans, newZ)
2762 end
2763
2764 local allowUpdate = true
2765 if part.referenceDistanceThreshold > 0 then
2766 local _, _, oldZ = getTranslation(translatingPart.node)
2767 if math.abs(oldZ-newZ) < part.referenceDistanceThreshold then
2768 allowUpdate = false
2769 end
2770 end
2771
2772 if allowUpdate then
2773 setTranslation(translatingPart.node, translatingPart.startPos[1], translatingPart.startPos[2], newZ)
2774 changed = true
2775 end
2776 end
2777 end
2778 end
2779
2780 if changed then
2781 if part.copyLocalDirectionParts ~= nil then
2782 for _,copyLocalDirectionPart in pairs(part.copyLocalDirectionParts) do
2783 local dx,dy,dz = localDirectionToWorld(part.node, 0,0,1)
2784 dx,dy,dz = worldDirectionToLocal(getParent(part.node), dx,dy,dz)
2785 dx = dx * copyLocalDirectionPart.dirScale[1]
2786 dy = dy * copyLocalDirectionPart.dirScale[2]
2787 dz = dz * copyLocalDirectionPart.dirScale[3]
2788
2789 local ux,uy,uz = localDirectionToWorld(part.node, 0,1,0)
2790 ux,uy,uz = worldDirectionToLocal(getParent(part.node), ux,uy,uz)
2791 ux = ux * copyLocalDirectionPart.upScale[1]
2792 uy = uy * copyLocalDirectionPart.upScale[2]
2793 uz = uz * copyLocalDirectionPart.upScale[3]
2794
2795 setDirection(copyLocalDirectionPart.node, dx,dy,dz, ux,uy,uz)
2796 changed = true
2797
2798 if self.isServer then
2799 Cylindered.updateComponentJoints(self, copyLocalDirectionPart, placeComponents)
2800 end
2801 end
2802 end
2803 end
2804
2805 -- update component joint
2806 if self.isServer then
2807 if changed then
2808 Cylindered.updateComponentJoints(self, part, placeComponents)
2809 Cylindered.updateAttacherJoints(self, part)
2810 Cylindered.updateWheels(self, part)
2811 end
2812 end
2813
2814 if changed then
2815 Cylindered.updateWheels(self, part)
2816 end
2817
2818 if updateDependentParts and part.dependentParts ~= nil then
2819 for _,part in pairs(part.dependentParts) do
2820 Cylindered.updateMovingPart(self, part, placeComponents, updateDependentParts)
2821 end
2822 end
2823
2824 part.isDirty = false
2825end

updateRotationBasedLimits

Description
Definition
updateRotationBasedLimits()
Code
2922function Cylindered.updateRotationBasedLimits(self, tool, dependentTool)
2923 if dependentTool.rotationBasedLimits ~= nil then
2924 local state = Cylindered.getMovingToolState(self, tool)
2925 if dependentTool.rotationBasedLimits ~= nil then
2926 local minRot, maxRot, minTrans, maxTrans = dependentTool.rotationBasedLimits:get(state)
2927
2928 if minRot ~= nil then
2929 dependentTool.movingTool.rotMin = minRot
2930 end
2931 if maxRot ~= nil then
2932 dependentTool.movingTool.rotMax = maxRot
2933 end
2934 if minTrans ~= nil then
2935 dependentTool.movingTool.transMin = minTrans
2936 end
2937 if maxTrans ~= nil then
2938 dependentTool.movingTool.transMax = maxTrans
2939 end
2940
2941 local isDirty = false
2942 if minRot ~= nil or maxRot ~= nil then
2943 isDirty = isDirty or Cylindered.setToolRotation(self, dependentTool.movingTool, 0, 0)
2944 end
2945 if minTrans ~= nil or maxTrans ~= nil then
2946 isDirty = isDirty or Cylindered.setToolTranslation(self, dependentTool.movingTool, 0, 0)
2947 end
2948
2949 if isDirty then
2950 Cylindered.setDirty(self, dependentTool.movingTool)
2951
2952 -- sync the new limited rotation values and also the new rotation limits
2953 self:raiseDirtyFlags(dependentTool.movingTool.dirtyFlag)
2954 self:raiseDirtyFlags(self.spec_cylindered.cylinderedDirtyFlag)
2955 end
2956 end
2957 end
2958end

updateWheels

Description
Update wheel of part
Definition
updateWheels(table part)
Arguments
tablepartpart
Code
2552function Cylindered.updateWheels(self, part)
2553 if part.wheels ~= nil then
2554 for _, wheel in pairs(part.wheels) do
2555 wheel.positionX, wheel.positionY, wheel.positionZ = localToLocal(getParent(wheel.repr), wheel.node, wheel.startPositionX-wheel.steeringCenterOffsetX, wheel.startPositionY-wheel.steeringCenterOffsetY, wheel.startPositionZ-wheel.steeringCenterOffsetZ)
2556 if wheel.useReprDirection then
2557 wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.repr, wheel.node, 0,-1,0)
2558 wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.repr, wheel.node, 1,0,0)
2559 elseif wheel.useDriveNodeDirection then
2560 wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 0,-1,0)
2561 wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 1,0,0)
2562 end
2563
2564 self:updateWheelBase(wheel)
2565 end
2566 end
2567end