LUADOC - Farming Simulator 22

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
4118function Cylindered.actionEventInput(self, actionName, inputValue, callbackState, isAnalog, isMouse)
4119 local spec = self.spec_cylindered
4120 local tool = spec.movingTools[callbackState]
4121
4122 if tool ~= nil then
4123 tool.lastInputTime = g_time
4124
4125 local move
4126 if tool.invertAxis then
4127 move = -inputValue
4128 else
4129 move = inputValue
4130 end
4131
4132 move = move * g_gameSettings:getValue(GameSettings.SETTING.VEHICLE_ARM_SENSITIVITY)
4133 if isMouse then
4134 -- revert dt scaling for mouse input
4135 move = move * 16.666 / g_currentDt * tool.mouseSpeedFactor
4136
4137 -- allow only the input of the highest mouse axis value
4138 -- 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
4139 if tool.moveLocked then
4140 if math.abs(inputValue) < 0.75 then
4141 if math.abs(move) > math.abs(tool.lockTool.move) * 2 then
4142 tool.moveLocked = false
4143 else
4144 move = 0
4145 end
4146 else
4147 tool.moveLocked = false
4148 end
4149 else
4150 local checkOtherTools = function(tools)
4151 for tool2Index, tool2 in ipairs(tools) do
4152 if tool2Index ~= callbackState then
4153 if tool2.move ~= nil and tool2.move ~= 0 then
4154 if math.abs(move) > math.abs(tool2.move) then
4155 tool2.move = 0
4156 tool2.moveToSend = 0
4157 tool2.moveLocked = true
4158 tool2.lockTool = tool
4159 else
4160 move = 0
4161 tool.moveLocked = true
4162 tool.lockTool = tool2
4163 end
4164 end
4165 end
4166 end
4167 end
4168
4169 checkOtherTools(spec.movingTools)
4170
4171 if self.getAttachedImplements ~= nil then
4172 for _, implement in pairs(self:getAttachedImplements()) do
4173 local vehicle = implement.object
4174 if vehicle.spec_cylindered ~= nil then
4175 checkOtherTools(vehicle.spec_cylindered.movingTools)
4176 end
4177 end
4178 end
4179 end
4180 end
4181
4182 if move ~= tool.move then
4183 tool.move = move
4184 end
4185
4186 if tool.move ~= tool.moveToSend then
4187 tool.moveToSend = tool.move
4188 self:raiseDirtyFlags(spec.cylinderedInputDirtyFlag)
4189 end
4190
4191 tool.smoothedMove = tool.smoothedMove * 0.9 + move * 0.1
4192 end
4193end

allowLoadMovingToolStates

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

checkMovingPartDirtyUpdateNode

Description
Definition
checkMovingPartDirtyUpdateNode()
Code
2789function Cylindered:checkMovingPartDirtyUpdateNode(node, movingPart)
2790end

getAdditionalSchemaText

Description
Definition
getAdditionalSchemaText()
Code
3178function Cylindered:getAdditionalSchemaText(superFunc)
3179 local t = superFunc(self)
3180 if self.isClient then
3181 if self:getIsActiveForInput(true) then
3182 local spec = self.spec_cylindered
3183 if #spec.controlGroupNames > 1 and spec.currentControlGroupIndex ~= 0 then
3184 t = tostring(spec.currentControlGroupIndex)
3185 end
3186 end
3187 end
3188
3189 return t
3190end

getConsumingLoad

Description
Definition
getConsumingLoad()
Code
3215function Cylindered:getConsumingLoad(superFunc)
3216 local value, count = superFunc(self)
3217
3218 local spec = self.spec_cylindered
3219 local loadPercentage = math.max(spec.powerConsumingTimer / spec.powerConsumingActiveTimeOffset, 0)
3220 return value + loadPercentage, count + 1
3221end

getDischargeNodeEmptyFactor

Description
Definition
getDischargeNodeEmptyFactor()
Code
3050function Cylindered:getDischargeNodeEmptyFactor(superFunc, dischargeNode)
3051 if dischargeNode.movingToolActivation == nil then
3052 return superFunc(self, dischargeNode)
3053 else
3054 local spec = self.spec_cylindered
3055 local movingToolActivation = dischargeNode.movingToolActivation
3056
3057 local currentSpeed = superFunc(self, dischargeNode)
3058
3059 local movingTool = spec.nodesToMovingTools[movingToolActivation.node]
3060 local state = MathUtil.clamp(Cylindered.getMovingToolState(self, movingTool), 0, 1)
3061 if movingToolActivation.isInverted then
3062 state = math.abs(state-1)
3063 end
3064
3065 state = math.max(state-movingToolActivation.openOffset, 0) / movingToolActivation.openOffsetInv
3066 local speedFactor = MathUtil.clamp(state/movingToolActivation.openFactor, 0, 1)
3067
3068 return currentSpeed * speedFactor
3069 end
3070end

getDoConsumePtoPower

Description
Returns if should consume pto power
Definition
getDoConsumePtoPower()
Return Values
booleanconsumeconsumePtoPower
Code
3209function Cylindered:getDoConsumePtoPower(superFunc)
3210 return superFunc(self) or self.spec_cylindered.powerConsumingTimer > 0
3211end

getIsDynamicMountGrabOpened

Description
Definition
getIsDynamicMountGrabOpened()
Code
3134function Cylindered:getIsDynamicMountGrabOpened(superFunc, grab)
3135 local isActive = superFunc(self, grab)
3136 if not isActive or grab.movingToolActivation == nil then
3137 return isActive
3138 end
3139
3140
3141 local spec = self.spec_cylindered
3142 local movingToolActivation = grab.movingToolActivation
3143 local movingTool = spec.nodesToMovingTools[movingToolActivation.node]
3144 local state = Cylindered.getMovingToolState(self, movingTool)
3145 if movingToolActivation.isInverted then
3146 state = math.abs(state-1)
3147 end
3148
3149 return state > movingToolActivation.openFactor
3150end

getIsMovingPartActive

Description
Definition
getIsMovingPartActive()
Code
2927function Cylindered:getIsMovingPartActive(movingPart)
2928 return movingPart.isActive
2929end

getIsMovingToolActive

Description
Definition
getIsMovingToolActive()
Code
2921function Cylindered:getIsMovingToolActive(movingTool)
2922 return movingTool.isActive
2923end

getMovingPartByNode

Description
Definition
getMovingPartByNode()
Code
2899function Cylindered:getMovingPartByNode(node)
2900 return self.spec_cylindered.nodesToMovingParts[node]
2901end

getMovingToolByNode

Description
Definition
getMovingToolByNode()
Code
2893function Cylindered:getMovingToolByNode(node)
2894 return self.spec_cylindered.nodesToMovingTools[node]
2895end

getMovingToolDashboardState

Description
Definition
getMovingToolDashboardState()
Code
4197function Cylindered.getMovingToolDashboardState(self, dashboard)
4198 local vehicle = self
4199
4200 if dashboard.attacherJointNode ~= nil then
4201 dashboard.attacherJointIndex = self:getAttacherJointIndexByNode(dashboard.attacherJointNode)
4202 dashboard.attacherJointNode = nil
4203 end
4204
4205 if dashboard.attacherJointIndex ~= nil then
4206 local implement = self:getImplementFromAttacherJointIndex(dashboard.attacherJointIndex)
4207 if implement ~= nil then
4208 vehicle = implement.object
4209 else
4210 vehicle = nil
4211 end
4212 end
4213
4214 if vehicle ~= nil then
4215 local spec = vehicle.spec_cylindered
4216 if spec ~= nil then
4217 for _, movingTool in ipairs(spec.movingTools) do
4218 if movingTool.axis == dashboard.axis then
4219 return (movingTool.smoothedMove + 1) / 2
4220 end
4221 end
4222 end
4223 end
4224
4225 return 0.5
4226end

getMovingToolMoveValue

Description
Definition
getMovingToolMoveValue()
Code
2933function Cylindered:getMovingToolMoveValue(movingTool)
2934 return movingTool.move + movingTool.externalMove
2935end

getMovingToolState

Description
Returns moving tool state
Definition
getMovingToolState(table tool)
Arguments
tabletooltool
Return Values
floatstatestate of moving tool [0..1]
Code
3504function Cylindered.getMovingToolState(self, tool)
3505 local state = 0
3506 if tool.rotMax ~= nil and tool.rotMin ~= nil then
3507 state = (tool.curRot[tool.rotationAxis]-tool.rotMin) / (tool.rotMax-tool.rotMin)
3508 else
3509 if tool.transMax ~= nil and tool.transMin ~= nil then
3510 state = (tool.curTrans[tool.translationAxis]-tool.transMin) / (tool.transMax-tool.transMin)
3511 end
3512 end
3513
3514 return state
3515end

getShovelNodeIsActive

Description
Definition
getShovelNodeIsActive()
Code
3094function Cylindered:getShovelNodeIsActive(superFunc, shovelNode)
3095 local isActive = superFunc(self, shovelNode)
3096 if not isActive or shovelNode.movingToolActivation == nil then
3097 return isActive
3098 end
3099
3100
3101 local spec = self.spec_cylindered
3102 local movingToolActivation = shovelNode.movingToolActivation
3103 local movingTool = spec.nodesToMovingTools[movingToolActivation.node]
3104 local state = Cylindered.getMovingToolState(self, movingTool)
3105 if movingToolActivation.isInverted then
3106 state = math.abs(state-1)
3107 end
3108
3109 return state > movingToolActivation.openFactor
3110end

getTranslatingPartByNode

Description
Definition
getTranslatingPartByNode()
Code
2905function Cylindered:getTranslatingPartByNode(node)
2906 local spec = self.spec_cylindered
2907 for i=1, #spec.movingParts do
2908 local part = spec.movingParts[i]
2909 if part.translatingParts ~= nil then
2910 for j=1, #part.translatingParts do
2911 if part.translatingParts[j].node == node then
2912 return part.translatingParts[j]
2913 end
2914 end
2915 end
2916 end
2917end

getWearMultiplier

Description
Returns current wear multiplier
Definition
getWearMultiplier()
Return Values
floatwearMultipliercurrent wear multiplier
Code
3195function Cylindered:getWearMultiplier(superFunc)
3196 local spec = self.spec_cylindered
3197 local multiplier = superFunc(self)
3198
3199 if spec.isHydraulicSamplePlaying then
3200 multiplier = multiplier + self:getWorkWearMultiplier()
3201 end
3202
3203 return multiplier
3204end

initSpecialization

Description
Definition
initSpecialization()
Code
57function Cylindered.initSpecialization()
58 local schema = Vehicle.xmlSchema
59
60 schema:setXMLSpecializationType("Cylindered")
61
62 SoundManager.registerSampleXMLPaths(schema, "vehicle.cylindered.sounds", "hydraulic")
63 schema:register(XMLValueType.TIME, "vehicle.cylindered.movingTools#powerConsumingActiveTimeOffset", "Power consumer deactivation delay. After the moving tool has not been moved this long it will no longer consume power.", 5)
64
65 SoundManager.registerSampleXMLPaths(schema, "vehicle.cylindered.sounds", "actionSound(?)")
66 schema:register(XMLValueType.STRING, "vehicle.cylindered.sounds.actionSound(?)#actionNames", "Target actions on given nodes")
67 schema:register(XMLValueType.STRING, "vehicle.cylindered.sounds.actionSound(?)#nodes", "Nodes that can activate this sound on given action events")
68 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.sounds.actionSound(?).pitch#dropOffFactor", "Factor that is applied to pitch while drop off time is active", 1)
69 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.sounds.actionSound(?).pitch#dropOffTime", "After this time the sound will be deactivated", 0)
70
71 local partKey = Cylindered.MOVING_PART_XML_KEY
72
73 schema:register(XMLValueType.NODE_INDEX, partKey .. "#node", "Node")
74 schema:register(XMLValueType.NODE_INDEX, partKey .. "#referenceFrame", "Reference frame")
75 schema:register(XMLValueType.NODE_INDEX, partKey .. "#referencePoint", "Reference point")
76
77 schema:register(XMLValueType.BOOL, partKey .. "#invertZ", "Invert Z axis", false)
78 schema:register(XMLValueType.BOOL, partKey .. "#scaleZ", "Allow Z axis scaling", false)
79 schema:register(XMLValueType.INT, partKey .. "#limitedAxis", "Limited axis")
80 schema:register(XMLValueType.BOOL, partKey .. "#isActiveDirty", "Part is permanently updated", false)
81 schema:register(XMLValueType.BOOL, partKey .. "#playSound", "Play hydraulic sound", false)
82 schema:register(XMLValueType.BOOL, partKey .. "#moveToReferenceFrame", "Move to reference frame", false)
83 schema:register(XMLValueType.BOOL, partKey .. "#doLineAlignment", "Do line alignment (line as ref point)", false)
84 schema:register(XMLValueType.BOOL, partKey .. "#doInversedLineAlignment", "Do inversed line alignment (line inside part and fixed ref point)", false)
85 schema:register(XMLValueType.FLOAT, partKey .. ".orientationLine#partLength", "Part length (Distance from part to line)", 0.5)
86 schema:register(XMLValueType.NODE_INDEX, partKey .. ".orientationLine#partLengthNode", "Node to measure the part length dynamically")
87 schema:register(XMLValueType.NODE_INDEX, partKey .. ".orientationLine.lineNode(?)#node", "Line node")
88
89 schema:register(XMLValueType.BOOL, partKey .. "#doDirectionAlignment", "Do direction alignment", true)
90 schema:register(XMLValueType.BOOL, partKey .. "#doRotationAlignment", "Do rotation alignment", false)
91 schema:register(XMLValueType.FLOAT, partKey .. "#rotMultiplier", "Rotation multiplier for rotation alignment", 0)
92
93 schema:register(XMLValueType.ANGLE, partKey .. "#minRot", "Min. rotation for limited axis")
94 schema:register(XMLValueType.ANGLE, partKey .. "#maxRot", "Max. rotation for limited axis")
95
96 schema:register(XMLValueType.BOOL, partKey .. "#alignToWorldY", "Align part to world Y axis", false)
97 schema:register(XMLValueType.NODE_INDEX, partKey .. "#localReferencePoint", "Local reference point")
98 schema:register(XMLValueType.NODE_INDEX, partKey .. "#referenceDistancePoint", "Z translation will be used as reference distance")
99 schema:register(XMLValueType.FLOAT, partKey .. "#localReferenceDistance", "Predefined reference distance", "calculated automatically")
100 schema:register(XMLValueType.BOOL, partKey .. "#updateLocalReferenceDistance", "Update distance to local reference point", false)
101 schema:register(XMLValueType.BOOL, partKey .. "#dynamicLocalReferenceDistance", "Local reference distance will be calculated based on the initial distance and the localReferencePoint direction", false)
102 schema:register(XMLValueType.BOOL, partKey .. "#localReferenceTranslate", "Translate to local reference node", false)
103 schema:register(XMLValueType.FLOAT, partKey .. "#referenceDistanceThreshold", "Distance threshold to update moving part while isActiveDirty", 0)
104 schema:register(XMLValueType.BOOL, partKey .. "#useLocalOffset", "Use local offset", false)
105 schema:register(XMLValueType.FLOAT, partKey .. "#directionThreshold", "Direction threshold to update part if vehicle is inactive", 0.0001)
106 schema:register(XMLValueType.FLOAT, partKey .. "#directionThresholdActive", "Direction threshold to update part if vehicle is inactive", 0.0001)
107 schema:register(XMLValueType.STRING, partKey .. "#maxUpdateDistance", "Max. distance to vehicle root while isActiveDirty is set ('-' means unlimited)")
108
109 schema:register(XMLValueType.BOOL, partKey .. "#smoothedDirectionScale", "If moving part is deactivated e.g. due to folding limits the direction is slowly interpolated back to the start direction depending on #smoothedDirectionTime", false)
110 schema:register(XMLValueType.TIME, partKey .. "#smoothedDirectionTime", "Defines how low it takes until the part is back in original direction (sec.)", 2)
111
112 schema:register(XMLValueType.BOOL, partKey .. "#debug", "Enables debug rendering for this part", false)
113
114 schema:register(XMLValueType.NODE_INDEX, partKey .. ".dependentPart(?)#node", "Dependent part")
115 schema:register(XMLValueType.STRING, partKey .. ".dependentPart(?)#maxUpdateDistance", "Max. distance to vehicle root to update dependent part ('-' means unlimited)", "-")
116
117 schema:register(XMLValueType.BOOL, partKey .. "#divideTranslatingDistance", "If true all translating parts will move at the same time. If false they start to move in the order from the xml", true)
118
119 schema:register(XMLValueType.NODE_INDEX, partKey .. ".translatingPart(?)#node", "Translating part")
120 schema:register(XMLValueType.FLOAT, partKey .. ".translatingPart(?)#referenceDistance", "Reference distance")
121 schema:register(XMLValueType.FLOAT, partKey .. ".translatingPart(?)#minZTrans", "Min. Z Translation")
122 schema:register(XMLValueType.FLOAT, partKey .. ".translatingPart(?)#maxZTrans", "Max. Z Translation")
123 schema:register(XMLValueType.BOOL, partKey .. ".translatingPart(?)#divideTranslatingDistance", "Define individual division per translating part. E.g. one part is extending without division and two other parts extend afterwards at the same speed.", "movingPart#divideTranslatingDistance")
124
125 schema:register(XMLValueType.VECTOR_N, partKey .. "#wheelIndices", "List of wheel indices to update")
126 schema:register(XMLValueType.STRING, partKey .. "#wheelNodes", "List of wheel nodes to update")
127 schema:register(XMLValueType.BOOL, partKey .. ".inputAttacherJoint#value", "Update input attacher joint")
128 schema:register(XMLValueType.VECTOR_N, partKey .. ".attacherJoint#jointIndices", "List of attacher joints to update")
129
130 Cylindered.registerDependentComponentJointXMLPaths(schema, partKey)
131 Cylindered.registerCopyLocalDirectionXMLPaths(schema, partKey)
132 Cylindered.registerDependentAnimationXMLPaths(schema, partKey)
133
134 local toolKey = Cylindered.MOVING_TOOL_XML_KEY
135
136 schema:register(XMLValueType.NODE_INDEX, toolKey .. "#node", "Node")
137 schema:register(XMLValueType.BOOL, toolKey .. "#isEasyControlTarget", "Is easy control target", false)
138
139 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#rotSpeed", "Rotation speed")
140 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#rotAcceleration", "Rotation acceleration")
141 schema:register(XMLValueType.INT, toolKey .. ".rotation#rotationAxis", "Rotation axis", 1)
142 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#rotMax", "Max. rotation")
143 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#rotMin", "Min. rotation")
144 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#startRot", "Start rotation")
145 schema:register(XMLValueType.BOOL, toolKey .. ".rotation#syncMaxRotLimits", "Synchronize max. rotation limits", false)
146 schema:register(XMLValueType.BOOL, toolKey .. ".rotation#syncMinRotLimits", "Synchronize min. rotation limits", false)
147 schema:register(XMLValueType.INT, toolKey .. ".rotation#rotSendNumBits", "Number of bits to synchronize", 8)
148 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#attachRotMax", "Max. rotation value set during attach")
149 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#attachRotMin", "Min. rotation value set during attach")
150 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#detachingRotMaxLimit", "Max. rotation to detach vehicle")
151 schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#detachingRotMinLimit", "Min. rotation to detach vehicle")
152
153 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#transSpeed", "Translation speed")
154 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#transAcceleration", "Translation acceleration")
155 schema:register(XMLValueType.INT, toolKey .. ".translation#translationAxis", "Translation axis")
156 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#transMax", "Max. translation")
157 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#transMin", "Min. translation")
158 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#startTrans", "Start translation")
159 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#attachTransMax", "Max. translation value set during attach")
160 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#attachTransMin", "Min. translation value set during attach")
161 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#detachingTransMaxLimit", "Max. translation to detach vehicle")
162 schema:register(XMLValueType.FLOAT, toolKey .. ".translation#detachingTransMinLimit", "Min. translation to detach vehicle")
163
164 schema:register(XMLValueType.BOOL, toolKey .. "#playSound", "Play sound", false)
165
166 schema:register(XMLValueType.STRING, toolKey .. ".animation#animName", "Animation name")
167 schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animSpeed", "Animation speed")
168 schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animAcceleration", "Animation acceleration")
169 schema:register(XMLValueType.INT, toolKey .. ".animation#animSendNumBits", "Number of bits to synchronize", 8)
170 schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animMaxTime", "Animation max. time", 1)
171 schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animMinTime", "Animation min. time", 0)
172 schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animStartTime", "Animation start time")
173
174 schema:register(XMLValueType.STRING, toolKey .. ".controls#iconName", "Icon identifier")
175 schema:register(XMLValueType.INT, toolKey .. ".controls#groupIndex", "Control group index", 0)
176 schema:register(XMLValueType.STRING, toolKey .. ".controls#axis", "Input action name")
177 schema:register(XMLValueType.BOOL, toolKey .. ".controls#invertAxis", "Invert input axis", false)
178 schema:register(XMLValueType.FLOAT, toolKey .. ".controls#mouseSpeedFactor", "Mouse speed factor", 1)
179 schema:register(XMLValueType.BOOL, toolKey .. "#allowSaving", "Allow saving", true)
180
181 schema:register(XMLValueType.BOOL, toolKey .. "#isIntitialDirty", "Is initial dirty", true)
182 schema:register(XMLValueType.NODE_INDEX, toolKey .. "#delayedNode", "Delayed node")
183 schema:register(XMLValueType.INT, toolKey .. "#delayedFrames", "Delayed frames", 3)
184
185 schema:register(XMLValueType.BOOL, toolKey .. "#isConsumingPower", "While tool is moving the power consumer is set active", false)
186
187 schema:register(XMLValueType.NODE_INDEX, toolKey .. ".dependentPart(?)#node", "Dependent part")
188 schema:register(XMLValueType.STRING, toolKey .. ".dependentPart(?)#maxUpdateDistance", "Max. distance to vehicle root to update dependent part ('-' means unlimited)", "-")
189
190 schema:register(XMLValueType.VECTOR_N, toolKey .. "#wheelIndices", "List of wheel indices to update")
191 schema:register(XMLValueType.STRING, toolKey .. "#wheelNodes", "List of wheel nodes to update")
192 schema:register(XMLValueType.BOOL, toolKey .. ".inputAttacherJoint#value", "Update input attacher joint")
193 schema:register(XMLValueType.VECTOR_N, toolKey .. ".attacherJoint#jointIndices", "List of attacher joints to update")
194
195 schema:register(XMLValueType.INT, toolKey .. "#fillUnitIndex", "Fill unit index")
196 schema:register(XMLValueType.FLOAT, toolKey .. "#minFillLevel", "Min. fill level")
197 schema:register(XMLValueType.FLOAT, toolKey .. "#maxFillLevel", "Max. fill level")
198
199 schema:register(XMLValueType.FLOAT, toolKey .. "#foldMinLimit", "Min. fold time", 0)
200 schema:register(XMLValueType.FLOAT, toolKey .. "#foldMaxLimit", "Max. fold time", 1)
201
202 Cylindered.registerDependentComponentJointXMLPaths(schema, toolKey)
203 Cylindered.registerDependentAnimationXMLPaths(schema, toolKey)
204
205 schema:register(XMLValueType.NODE_INDEX, toolKey .. ".dependentMovingTool(?)#node", "Dependent part")
206 schema:register(XMLValueType.FLOAT, toolKey .. ".dependentMovingTool(?)#speedScale", "Speed scale")
207 schema:register(XMLValueType.BOOL, toolKey .. ".dependentMovingTool(?)#requiresMovement", "Requires movement", false)
208 schema:register(XMLValueType.ANGLE, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#rotation", "Rotation")
209 schema:register(XMLValueType.ANGLE, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#rotMin", "Min. rotation")
210 schema:register(XMLValueType.ANGLE, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#rotMax", "Max. rotation")
211 schema:register(XMLValueType.FLOAT, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#transMin", "Min. translation")
212 schema:register(XMLValueType.FLOAT, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#transMax", "Max. translation")
213 schema:register(XMLValueType.VECTOR_2, toolKey .. ".dependentMovingTool(?)#minTransLimits", "Min. translation limits")
214 schema:register(XMLValueType.VECTOR_2, toolKey .. ".dependentMovingTool(?)#maxTransLimits", "Max. translation limits")
215 schema:register(XMLValueType.VECTOR_ROT_2, toolKey .. ".dependentMovingTool(?)#minRotLimits", "Min. rotation limits")
216 schema:register(XMLValueType.VECTOR_ROT_2, toolKey .. ".dependentMovingTool(?)#maxRotLimits", "Max. rotation limits")
217
218 schema:register(XMLValueType.L10N_STRING, "vehicle.cylindered.movingTools.controlGroups.controlGroup(?)#name", "Control group name")
219
220 schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl#rootNode", "Root node")
221 schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl#node", "Node")
222 schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl#targetNodeZ", "Z target node")
223 schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl#refNode", "Reference node")
224 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl#maxTotalDistance", "Max. total distance the arms can move from rootNode", "automatically calculated")
225 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.targetMovement#speed", "Target node move speed", 1)
226 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.targetMovement#acceleration", "Target node move acceleration", 50)
227 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#minMoveRatio", "Min. ratio between translation and rotation movement [0: only rotation, 1: only translation]", 0.2)
228 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#maxMoveRatio", "Max. ratio between translation and rotation movement [0: only rotation, 1: only translation]", 0.8)
229 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#moveRatioMinDir", "Defines direction value when the translation parts start to move", 0.0)
230 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#moveRatioMaxDir", "Defines direction value when the rotation parts stop to move", 1.0)
231 schema:register(XMLValueType.BOOL, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#allowNegativeTrans", "Allow translation movement if translation parts are pointing towards the root node", false)
232 schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#minNegativeTrans", "Min. translation percentage when moving the translation parts into negative direction while they are pointing towards the root node", 0)
233 schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes.zTranslationNode(?)#node", "Z translation node")
234 schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl.xRotationNodes.xRotationNode1#node", "X translation node")
235 schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl.xRotationNodes.xRotationNode2#node", "X translation node")
236
237 Dashboard.registerDashboardXMLPaths(schema, "vehicle.cylindered.dashboards", "movingTool")
238 schema:register(XMLValueType.STRING, "vehicle.cylindered.dashboards.dashboard(?)#axis", "Moving tool input action name")
239 schema:register(XMLValueType.INT, "vehicle.cylindered.dashboards.dashboard(?)#attacherJointIndex", "Index of attacher joint that has to be connected")
240 schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.dashboards.dashboard(?)#attacherJointNode", "Node of attacher joint that has to be connected")
241
242 ObjectChangeUtil.addAdditionalObjectChangeXMLPaths(schema, function(_schema, key)
243 _schema:register(XMLValueType.ANGLE, key .. "#movingToolRotMaxActive", "Moving tool max. rotation if object change active")
244 _schema:register(XMLValueType.ANGLE, key .. "#movingToolRotMaxInactive", "Moving tool max. rotation if object change inactive")
245 _schema:register(XMLValueType.ANGLE, key .. "#movingToolRotMinActive", "Moving tool min. rotation if object change active")
246 _schema:register(XMLValueType.ANGLE, key .. "#movingToolRotMinInactive", "Moving tool min. rotation if object change inactive")
247
248 _schema:register(XMLValueType.FLOAT, key .. "#movingToolTransMaxActive", "Moving tool max. translation if object change active")
249 _schema:register(XMLValueType.FLOAT, key .. "#movingToolTransMaxInactive", "Moving tool max. translation if object change inactive")
250 _schema:register(XMLValueType.FLOAT, key .. "#movingToolTransMinActive", "Moving tool min. translation if object change active")
251 _schema:register(XMLValueType.FLOAT, key .. "#movingToolTransMinInactive", "Moving tool min. translation if object change inactive")
252
253 _schema:register(XMLValueType.BOOL, key .. "#movingPartUpdateActive", "moving part active state if object change active")
254 _schema:register(XMLValueType.BOOL, key .. "#movingPartUpdateInactive", "moving part active state if object change inactive")
255 end)
256
257 schema:register(XMLValueType.NODE_INDEX, Dischargeable.DISCHARGE_NODE_XML_PATH .. ".movingToolActivation#node", "Moving tool node")
258 schema:register(XMLValueType.BOOL, Dischargeable.DISCHARGE_NODE_XML_PATH .. ".movingToolActivation#isInverted", "Activation is inverted", false)
259 schema:register(XMLValueType.FLOAT, Dischargeable.DISCHARGE_NODE_XML_PATH .. ".movingToolActivation#openFactor", "Open factor", 1)
260 schema:register(XMLValueType.FLOAT, Dischargeable.DISCHARGE_NODE_XML_PATH .. ".movingToolActivation#openOffset", "Open offset", 0)
261
262 schema:register(XMLValueType.NODE_INDEX, Dischargeable.DISCHARGE_NODE_CONFIG_XML_PATH .. ".movingToolActivation#node", "Moving tool node")
263 schema:register(XMLValueType.BOOL, Dischargeable.DISCHARGE_NODE_CONFIG_XML_PATH .. ".movingToolActivation#isInverted", "Activation is inverted", false)
264 schema:register(XMLValueType.FLOAT, Dischargeable.DISCHARGE_NODE_CONFIG_XML_PATH .. ".movingToolActivation#openFactor", "Open factor", 1)
265 schema:register(XMLValueType.FLOAT, Dischargeable.DISCHARGE_NODE_CONFIG_XML_PATH .. ".movingToolActivation#openOffset", "Open offset", 0)
266
267 schema:register(XMLValueType.NODE_INDEX, Shovel.SHOVEL_NODE_XML_KEY .. ".movingToolActivation#node", "Moving tool node")
268 schema:register(XMLValueType.BOOL, Shovel.SHOVEL_NODE_XML_KEY .. ".movingToolActivation#isInverted", "Activation is inverted", false)
269 schema:register(XMLValueType.FLOAT, Shovel.SHOVEL_NODE_XML_KEY .. ".movingToolActivation#openFactor", "Open factor", 1)
270
271 schema:register(XMLValueType.NODE_INDEX, DynamicMountAttacher.DYNAMIC_MOUNT_GRAB_XML_PATH .. ".movingToolActivation#node", "Moving tool node")
272 schema:register(XMLValueType.BOOL, DynamicMountAttacher.DYNAMIC_MOUNT_GRAB_XML_PATH .. ".movingToolActivation#isInverted", "Activation is inverted", false)
273 schema:register(XMLValueType.FLOAT, DynamicMountAttacher.DYNAMIC_MOUNT_GRAB_XML_PATH .. ".movingToolActivation#openFactor", "Open factor", 1)
274
275 schema:setXMLSpecializationType()
276
277 local schemaSavegame = Vehicle.xmlSchemaSavegame
278 schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?).cylindered.movingTool(?)#translation", "Current translation value")
279 schemaSavegame:register(XMLValueType.ANGLE, "vehicles.vehicle(?).cylindered.movingTool(?)#rotation", "Current rotation in rad")
280 schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?).cylindered.movingTool(?)#animationTime", "Current animation time")
281end

isDetachAllowed

Description
Returns true if detach is allowed
Definition
isDetachAllowed()
Return Values
booleandetachAlloweddetach is allowed
Code
2940function Cylindered:isDetachAllowed(superFunc)
2941 local spec = self.spec_cylindered
2942 if spec.detachLockNodes ~= nil then
2943 for entry, data in pairs(spec.detachLockNodes) do
2944 local node = entry.node
2945 local rot = {getRotation(node)}
2946
2947 if data.detachingRotMinLimit ~= nil and rot[entry.rotationAxis] < data.detachingRotMinLimit then
2948 return false, nil
2949 end
2950 if data.detachingRotMaxLimit ~= nil and rot[entry.rotationAxis] > data.detachingRotMaxLimit then
2951 return false, nil
2952 end
2953
2954 local trans = {getTranslation(node)}
2955 if data.detachingTransMinLimit ~= nil and trans[entry.translationAxis] < data.detachingTransMinLimit then
2956 return false, nil
2957 end
2958 if data.detachingTransMaxLimit ~= nil and trans[entry.translationAxis] > data.detachingTransMaxLimit then
2959 return false, nil
2960 end
2961 end
2962 end
2963
2964 return superFunc(self)
2965end

limitInterpolator

Description
Definition
limitInterpolator()
Code
4052function Cylindered.limitInterpolator(first, second, alpha)
4053 local oneMinusAlpha = 1-alpha
4054
4055 local rotMin = nil
4056 local rotMax = nil
4057 local transMin = nil
4058 local transMax = nil
4059
4060 if first.rotMin ~= nil and second.rotMin ~= nil then
4061 rotMin = first.rotMin*alpha + second.rotMin*oneMinusAlpha
4062 end
4063 if first.rotMax ~= nil and second.rotMax ~= nil then
4064 rotMax = first.rotMax*alpha + second.rotMax*oneMinusAlpha
4065 end
4066 if first.transMin ~= nil and second.transMin ~= nil then
4067 transMin = first.minTrans*alpha + second.transMin*oneMinusAlpha
4068 end
4069 if first.transMax ~= nil and second.transMax ~= nil then
4070 transMax = first.transMax*alpha + second.transMax*oneMinusAlpha
4071 end
4072
4073 return rotMin, rotMax, transMin, transMax
4074end

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
2744function Cylindered:loadCopyLocalDirectionParts(xmlFile, baseName, entry)
2745 entry.copyLocalDirectionParts = {}
2746 local j = 0
2747 while true do
2748 local refBaseName = baseName..string.format(".copyLocalDirectionPart(%d)", j)
2749 if not xmlFile:hasProperty(refBaseName) then
2750 break
2751 end
2752
2753 XMLUtil.checkDeprecatedXMLElements(xmlFile, refBaseName.."#index", refBaseName.."#node") --FS15 to FS17
2754
2755 local node = xmlFile:getValue(refBaseName.."#node", nil, self.components, self.i3dMappings)
2756 if node ~= nil then
2757 local copyLocalDirectionPart = {}
2758 copyLocalDirectionPart.node = node
2759 copyLocalDirectionPart.dirScale = xmlFile:getValue(refBaseName.."#dirScale", nil, true)
2760 copyLocalDirectionPart.upScale = xmlFile:getValue(refBaseName.."#upScale", nil, true)
2761
2762 self:loadDependentComponentJoints(xmlFile, refBaseName, copyLocalDirectionPart)
2763
2764 table.insert(entry.copyLocalDirectionParts, copyLocalDirectionPart)
2765 end
2766 j = j + 1
2767 end
2768end

loadDependentAnimations

Description
Definition
loadDependentAnimations()
Code
2696function Cylindered:loadDependentAnimations(xmlFile, baseName, entry)
2697 entry.dependentAnimations = {}
2698
2699 local i = 0
2700 while true do
2701 local baseKey = string.format("%s.dependentAnimation(%d)", baseName, i)
2702 if not xmlFile:hasProperty(baseKey) then
2703 break
2704 end
2705
2706 local animationName = xmlFile:getValue(baseKey.."#name")
2707 if animationName ~= nil then
2708 local dependentAnimation = {}
2709 dependentAnimation.name = animationName
2710 dependentAnimation.lastPos = 0
2711
2712 dependentAnimation.translationAxis = xmlFile:getValue(baseKey.."#translationAxis")
2713 dependentAnimation.rotationAxis = xmlFile:getValue(baseKey.."#rotationAxis")
2714
2715 dependentAnimation.node = entry.node
2716 local useTranslatingPartIndex = xmlFile:getValue(baseKey.."#useTranslatingPartIndex")
2717 if useTranslatingPartIndex ~= nil then
2718 if entry.translatingParts[useTranslatingPartIndex] ~= nil then
2719 dependentAnimation.node = entry.translatingParts[useTranslatingPartIndex].node
2720 end
2721 end
2722
2723 dependentAnimation.minValue = xmlFile:getValue(baseKey.."#minValue")
2724 dependentAnimation.maxValue = xmlFile:getValue(baseKey.."#maxValue")
2725 if dependentAnimation.rotationAxis ~= nil then
2726 dependentAnimation.minValue = MathUtil.degToRad(dependentAnimation.minValue)
2727 dependentAnimation.maxValue = MathUtil.degToRad(dependentAnimation.maxValue)
2728 end
2729
2730 dependentAnimation.invert = xmlFile:getValue(baseKey.."#invert", false)
2731
2732 table.insert(entry.dependentAnimations, dependentAnimation)
2733 end
2734
2735 i = i + 1
2736 end
2737end

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
2580function Cylindered:loadDependentAttacherJoints(xmlFile, baseName, entry)
2581 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName.."#jointIndices", baseName..".attacherJoint#jointIndices") --FS15 to FS17
2582
2583 local indices = xmlFile:getValue(baseName.. ".attacherJoint#jointIndices", nil, true)
2584 if indices ~= nil then
2585 entry.attacherJoints = {}
2586
2587 local availableAttacherJoints
2588 if self.getAttacherJoints ~= nil then
2589 availableAttacherJoints = self:getAttacherJoints()
2590 end
2591 if availableAttacherJoints ~= nil then
2592 for i=1, #indices do
2593 if availableAttacherJoints[indices[i]] ~= nil then
2594 table.insert(entry.attacherJoints, availableAttacherJoints[indices[i]])
2595 else
2596 Logging.xmlWarning(xmlFile, "Invalid attacher joint index '%s' for '%s'!", indices[i], baseName)
2597 end
2598 end
2599 end
2600 end
2601
2602 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName.."#inputAttacherJoint", baseName..".inputAttacherJoint#value") --FS15 to FS17
2603
2604 entry.inputAttacherJoint = xmlFile:getValue(baseName.. ".inputAttacherJoint#value", false)
2605end

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
2529function Cylindered:loadDependentComponentJoints(xmlFile, baseName, entry)
2530 if not self.isServer then
2531 return
2532 end
2533
2534 entry.componentJoints = {}
2535
2536 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName.."#componentJointIndex", baseName..".componentJoint#index") --FS15 to FS17
2537 XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName.."#anchorActor", baseName..".componentJoint#anchorActor") --FS15 to FS17
2538
2539 local i = 0
2540 while true do
2541 local key = baseName .. string.format(".componentJoint(%d)", i)
2542 if not xmlFile:hasProperty(key) then
2543 break
2544 end
2545 local index = xmlFile:getValue(key .. "#index")
2546 if index ~= nil and self.componentJoints[index] ~= nil then
2547 local anchorActor = xmlFile:getValue(key.."#anchorActor", 0)
2548
2549 local componentJoint = self.componentJoints[index]
2550
2551 local jointEntry = {}
2552 jointEntry.componentJoint = componentJoint
2553 jointEntry.anchorActor = anchorActor
2554 jointEntry.index = index
2555
2556 local jointNode = componentJoint.jointNode
2557 if jointEntry.anchorActor == 1 then
2558 jointNode = componentJoint.jointNodeActor1
2559 end
2560
2561 local node = self.components[componentJoint.componentIndices[2]].node
2562 jointEntry.x, jointEntry.y, jointEntry.z = localToLocal(node, jointNode, 0,0,0)
2563 jointEntry.upX, jointEntry.upY, jointEntry.upZ = localDirectionToLocal(node, jointNode, 0,1,0)
2564 jointEntry.dirX, jointEntry.dirY, jointEntry.dirZ = localDirectionToLocal(node, jointNode, 0,0,1)
2565
2566 table.insert(entry.componentJoints, jointEntry)
2567 else
2568 Logging.xmlWarning(xmlFile, "Invalid index for '%s'", key)
2569 end
2570
2571 i = i + 1
2572 end
2573end

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
2404function Cylindered:loadDependentMovingTools(xmlFile, baseName, entry)
2405 entry.dependentMovingTools = {}
2406
2407 local j = 0
2408 while true do
2409 local refBaseName = baseName..string.format(".dependentMovingTool(%d)", j)
2410 if not xmlFile:hasProperty(refBaseName) then
2411 break
2412 end
2413
2414 XMLUtil.checkDeprecatedXMLElements(xmlFile, refBaseName.."#index", refBaseName.."#index") --FS17 to FS19
2415
2416 local node = xmlFile:getValue(refBaseName.."#node", nil, self.components, self.i3dMappings)
2417 local speedScale = xmlFile:getValue(refBaseName.."#speedScale")
2418 local requiresMovement = xmlFile:getValue(refBaseName.."#requiresMovement", false)
2419
2420 local rotationBasedLimits = AnimCurve.new(Cylindered.limitInterpolator)
2421 local found = false
2422 local i = 0
2423 while true do
2424 local key = string.format("%s.limit(%d)", refBaseName..".rotationBasedLimits", i)
2425 if not xmlFile:hasProperty(key) then
2426 break
2427 end
2428
2429 local keyFrame = self:loadRotationBasedLimits(xmlFile, key, entry)
2430 if keyFrame ~= nil then
2431 rotationBasedLimits:addKeyframe(keyFrame)
2432 found = true
2433 end
2434 i = i + 1
2435 end
2436 if not found then
2437 rotationBasedLimits = nil
2438 end
2439
2440 local minTransLimits = xmlFile:getValue(refBaseName.."#minTransLimits", nil, true)
2441 local maxTransLimits = xmlFile:getValue(refBaseName.."#maxTransLimits", nil, true)
2442 local minRotLimits = xmlFile:getValue(refBaseName.."#minRotLimits", nil, true)
2443 local maxRotLimits = xmlFile:getValue(refBaseName.."#maxRotLimits", nil, true)
2444 if node ~= nil and (rotationBasedLimits ~= nil or speedScale ~= nil or minTransLimits ~= nil or maxTransLimits ~= nil or minRotLimits ~= nil or maxRotLimits ~= nil) then
2445 local dependentTool = {}
2446 dependentTool.node = node
2447 dependentTool.rotationBasedLimits = rotationBasedLimits
2448 dependentTool.speedScale = speedScale
2449 dependentTool.requiresMovement = requiresMovement
2450 dependentTool.minTransLimits = minTransLimits
2451 dependentTool.maxTransLimits = maxTransLimits
2452 dependentTool.minRotLimits = minRotLimits
2453 dependentTool.maxRotLimits = maxRotLimits
2454 table.insert(entry.dependentMovingTools, dependentTool)
2455 end
2456
2457 j = j + 1
2458 end
2459end

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
2466function Cylindered:loadDependentParts(xmlFile, baseName, entry)
2467 entry.dependentPartData = {}
2468
2469 xmlFile:iterate(baseName .. ".dependentPart", function(_, key)
2470 XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#index", key.."#node") --FS17 to FS19
2471
2472 local dependentPart = {}
2473 dependentPart.node = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings)
2474 if dependentPart.node ~= nil then
2475 dependentPart.maxUpdateDistance = xmlFile:getValue(key.."#maxUpdateDistance", "-")
2476 if dependentPart.maxUpdateDistance == "-" then
2477 dependentPart.maxUpdateDistance = math.huge
2478 else
2479 dependentPart.maxUpdateDistance = tonumber(dependentPart.maxUpdateDistance)
2480 end
2481
2482 dependentPart.part = nil
2483
2484 table.insert(entry.dependentPartData, dependentPart)
2485 end
2486 end)
2487end

loadDependentTranslatingParts

Description
Load translating parts
Definition
loadDependentTranslatingParts(integer xmlFile, string baseName, table entry)
Arguments
integerxmlFileid of xml object
stringbaseNamebase name
tableentryentry to add
Code
2647function Cylindered:loadDependentTranslatingParts(xmlFile, baseName, entry)
2648 entry.translatingParts = {}
2649 if entry.referencePoint ~= nil then
2650 entry.divideTranslatingDistance = xmlFile:getValue(baseName .. "#divideTranslatingDistance", true)
2651 entry.translatingPartsDivider = 0
2652
2653 local j = 0
2654 while true do
2655 local refBaseName = baseName..string.format(".translatingPart(%d)", j)
2656 if not xmlFile:hasProperty(refBaseName) then
2657 break
2658 end
2659
2660 XMLUtil.checkDeprecatedXMLElements(xmlFile, refBaseName.."#index", refBaseName.."#node") --FS15 to FS17
2661
2662 local node = xmlFile:getValue(refBaseName.."#node", nil, self.components, self.i3dMappings)
2663 if node ~= nil then
2664 local transEntry = {}
2665 transEntry.node = node
2666 local x, y, z = getTranslation(node)
2667 transEntry.startPos = {x, y, z}
2668 transEntry.lastZ = z
2669 local _, _, refZ = worldToLocal(node, getWorldTranslation(entry.referencePoint))
2670 transEntry.referenceDistance = xmlFile:getValue(refBaseName.."#referenceDistance", refZ)
2671
2672 transEntry.minZTrans = xmlFile:getValue(refBaseName.."#minZTrans")
2673 transEntry.maxZTrans = xmlFile:getValue(refBaseName.."#maxZTrans")
2674
2675 transEntry.divideTranslatingDistance = xmlFile:getValue(refBaseName .. "#divideTranslatingDistance", entry.divideTranslatingDistance)
2676 if transEntry.divideTranslatingDistance then
2677 entry.translatingPartsDivider = entry.translatingPartsDivider + 1
2678 end
2679
2680 table.insert(entry.translatingParts, transEntry)
2681 end
2682
2683 j = j + 1
2684 end
2685 end
2686end

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
2612function Cylindered:loadDependentWheels(xmlFile, baseName, entry)
2613 if SpecializationUtil.hasSpecialization(Wheels, self.specializations) then
2614 local indices = xmlFile:getValue(baseName.. "#wheelIndices", nil, true)
2615 if indices ~= nil then
2616 entry.wheels = {}
2617 for _,wheelIndex in pairs(indices) do
2618 local wheel = self:getWheelFromWheelIndex(wheelIndex)
2619 if wheel ~= nil then
2620 table.insert(entry.wheels, wheel)
2621 else
2622 Logging.xmlWarning(xmlFile, "Invalid wheelIndex '%s' for '%s'!", wheelIndex, baseName)
2623 end
2624 end
2625 end
2626
2627 local wheelNodesStr = xmlFile:getValue(baseName.. "#wheelNodes")
2628 if wheelNodesStr ~= nil and wheelNodesStr ~= "" then
2629 local wheelNodes = wheelNodesStr:split(" ")
2630 for i=1, #wheelNodes do
2631 local wheel = self:getWheelByWheelNode(wheelNodes[i])
2632 if wheel ~= nil then
2633 table.insert(entry.wheels, wheel)
2634 else
2635 Logging.xmlWarning(xmlFile, "Invalid wheelNode '%s' for '%s'!", wheelNodes[i], baseName)
2636 end
2637 end
2638 end
2639 end
2640end

loadDischargeNode

Description
Definition
loadDischargeNode()
Code
3029function Cylindered:loadDischargeNode(superFunc, xmlFile, key, entry)
3030 if not superFunc(self, xmlFile, key, entry) then
3031 return false
3032 end
3033
3034 local baseKey = key .. ".movingToolActivation"
3035
3036 if xmlFile:hasProperty(baseKey) then
3037 entry.movingToolActivation = {}
3038 entry.movingToolActivation.node = xmlFile:getValue(baseKey.."#node", nil, self.components, self.i3dMappings)
3039 entry.movingToolActivation.isInverted = xmlFile:getValue(baseKey.."#isInverted", false)
3040 entry.movingToolActivation.openFactor = xmlFile:getValue(baseKey.."#openFactor", 1)
3041 entry.movingToolActivation.openOffset = xmlFile:getValue(baseKey.."#openOffset", 0)
3042 entry.movingToolActivation.openOffsetInv = 1-entry.movingToolActivation.openOffset
3043 end
3044
3045 return true
3046end

loadDynamicMountGrabFromXML

Description
Definition
loadDynamicMountGrabFromXML()
Code
3114function Cylindered:loadDynamicMountGrabFromXML(superFunc, xmlFile, key, entry)
3115 if not superFunc(self, xmlFile, key, entry) then
3116 return false
3117 end
3118
3119 local baseKey = key .. ".movingToolActivation"
3120 if not xmlFile:hasProperty(baseKey) then
3121 return true
3122 end
3123
3124 entry.movingToolActivation = {}
3125 entry.movingToolActivation.node = xmlFile:getValue(baseKey.."#node", nil, self.components, self.i3dMappings)
3126 entry.movingToolActivation.isInverted = xmlFile:getValue(baseKey.."#isInverted", false)
3127 entry.movingToolActivation.openFactor = xmlFile:getValue(baseKey.."#openFactor", 1)
3128
3129 return true
3130end

loadEasyArmControlFromXML

Description
Definition
loadEasyArmControlFromXML()
Code
1395function Cylindered:loadEasyArmControlFromXML(xmlFile, key, easyArmControl)
1396 XMLUtil.checkDeprecatedXMLElements(xmlFile, key..".xRotationNodes#maxDistance") --FS19 to FS22
1397 XMLUtil.checkDeprecatedXMLElements(xmlFile, key..".xRotationNodes#transRotRatio") --FS19 to FS22
1398
1399 easyArmControl.rootNode = xmlFile:getValue(key.."#rootNode", nil, self.components, self.i3dMappings)
1400 easyArmControl.targetNodeY = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings)
1401 easyArmControl.targetNodeZ = xmlFile:getValue(key.."#targetNodeZ", easyArmControl.targetNodeY, self.components, self.i3dMappings)
1402 easyArmControl.state = false
1403
1404 if easyArmControl.targetNodeZ ~= nil and easyArmControl.targetNodeY ~= nil then
1405 local targetYTool = self:getMovingToolByNode(easyArmControl.targetNodeY)
1406 local targetZTool = self:getMovingToolByNode(easyArmControl.targetNodeZ)
1407 if targetYTool ~= nil and targetZTool ~= nil then
1408 easyArmControl.targetNode = easyArmControl.targetNodeZ
1409 if getParent(easyArmControl.targetNodeY) == easyArmControl.targetNodeZ then
1410 easyArmControl.targetNode = easyArmControl.targetNodeY
1411 end
1412 easyArmControl.targetRefNode = xmlFile:getValue(key.."#refNode", nil, self.components, self.i3dMappings)
1413 easyArmControl.lastValidPositionY = {getTranslation(easyArmControl.targetNodeY)}
1414 easyArmControl.lastValidPositionZ = {getTranslation(easyArmControl.targetNodeZ)}
1415
1416 easyArmControl.moveSpeed = xmlFile:getValue(key..".targetMovement#speed", 1) / 1000
1417 easyArmControl.moveAcceleration = xmlFile:getValue(key..".targetMovement#acceleration", 50) / (1000 * 1000)
1418 easyArmControl.lastSpeedY = 0
1419 easyArmControl.lastSpeedZ = 0
1420
1421 easyArmControl.minTransMoveRatio = xmlFile:getValue(key..".zTranslationNodes#minMoveRatio", 0.2)
1422 easyArmControl.maxTransMoveRatio = xmlFile:getValue(key..".zTranslationNodes#maxMoveRatio", 0.8)
1423 easyArmControl.transMoveRatioMinDir = xmlFile:getValue(key..".zTranslationNodes#moveRatioMinDir", 0)
1424 easyArmControl.transMoveRatioMaxDir = xmlFile:getValue(key..".zTranslationNodes#moveRatioMaxDir", 1)
1425 easyArmControl.allowNegativeTrans = xmlFile:getValue(key..".zTranslationNodes#allowNegativeTrans", false)
1426 easyArmControl.minNegativeTrans = xmlFile:getValue(key..".zTranslationNodes#minNegativeTrans", 0)
1427
1428 easyArmControl.zTranslationNodes = {}
1429 local maxTrans = 0
1430 xmlFile:iterate(key..".zTranslationNodes.zTranslationNode", function (_, transKey)
1431 local node = xmlFile:getValue(transKey.."#node", nil, self.components, self.i3dMappings)
1432 if node ~= nil then
1433 local movingTool = self:getMovingToolByNode(node)
1434 if movingTool ~= nil then
1435 local maxDistance = math.abs(movingTool.transMin - movingTool.transMax)
1436 maxTrans = maxTrans + maxDistance
1437 movingTool.easyArmControlActive = false
1438 table.insert(easyArmControl.zTranslationNodes, {node=node, movingTool=movingTool, maxDistance=maxDistance, transFactor=0, startTranslation={getTranslation(node)}})
1439 end
1440 end
1441 end)
1442
1443 for _, translationNode in ipairs(easyArmControl.zTranslationNodes) do
1444 translationNode.transFactor = translationNode.maxDistance / maxTrans
1445 end
1446
1447 easyArmControl.xRotationNodes = {}
1448 for i=1, 2 do
1449 local xRotKey = string.format("%s.xRotationNodes.xRotationNode%d", key, i)
1450 if not xmlFile:hasProperty(xRotKey) then
1451 Logging.xmlWarning(xmlFile, "Missing second xRotation node for easy control!")
1452 return false
1453 end
1454
1455 XMLUtil.checkDeprecatedXMLElements(xmlFile, xRotKey.."#refNode") --FS19 to FS22
1456
1457 local node = xmlFile:getValue(xRotKey.."#node", nil, self.components, self.i3dMappings)
1458 if node ~= nil then
1459 local movingTool = self:getMovingToolByNode(node)
1460 if movingTool ~= nil then
1461 movingTool.easyArmControlActive = false
1462 table.insert(easyArmControl.xRotationNodes, {node=node, movingTool=movingTool, startRotation={getRotation(node)}})
1463 end
1464 end
1465 end
1466
1467 if easyArmControl.targetRefNode ~= nil then
1468 local xOffset, yOffset, _ = localToLocal(easyArmControl.targetRefNode, easyArmControl.xRotationNodes[2].node, 0, 0, 0)
1469 if math.abs(xOffset) > 0.0001 or math.abs(yOffset) > 0.0001 then
1470 Logging.xmlWarning(xmlFile, "Invalid position of '%s'. Offset to second xRotation node is not 0 on X or Y axis (x: %f y: %f)", key.."#refNode", xOffset, yOffset)
1471 return false
1472 end
1473 end
1474
1475 local xOffset, yOffset, _ = localToLocal(easyArmControl.xRotationNodes[2].node, easyArmControl.xRotationNodes[1].node, 0, 0, 0)
1476 if math.abs(xOffset) > 0.0001 or math.abs(yOffset) > 0.0001 then
1477 Logging.xmlWarning(xmlFile, "Invalid position of xRotationNode2. Offset to second xRotationNode1 is not 0 on X or Y axis (x: %f y: %f)", xOffset, yOffset)
1478 return false
1479 end
1480
1481 local rootOffset = calcDistanceFrom(easyArmControl.rootNode, easyArmControl.xRotationNodes[1].node)
1482 if rootOffset > 0.05 then
1483 Logging.xmlWarning(xmlFile, "Distance between easyArmControl rootNode and xRotationNode1 is to big (%.2f). They should be at the same position.", rootOffset)
1484 return false
1485 end
1486
1487 easyArmControl.maxTotalDistance = xmlFile:getValue(key.."#maxTotalDistance")
1488 if easyArmControl.maxTotalDistance == nil then
1489 -- move nodes to the max length state
1490 for i=1, #easyArmControl.xRotationNodes do
1491 local xRotationNode = easyArmControl.xRotationNodes[i]
1492 local curRot = {getRotation(xRotationNode.node)}
1493 curRot[xRotationNode.movingTool.rotationAxis] = xRotationNode.movingTool.rotMin
1494 setRotation(xRotationNode.node, curRot[1], curRot[2], curRot[3])
1495 end
1496
1497 for i=1, #easyArmControl.zTranslationNodes do
1498 local zTranslationNode = easyArmControl.zTranslationNodes[i]
1499 local curTrans = {getTranslation(zTranslationNode.node)}
1500 curTrans[zTranslationNode.movingTool.translationAxis] = zTranslationNode.movingTool.transMax
1501 setTranslation(zTranslationNode.node, curTrans[1], curTrans[2], curTrans[3])
1502 end
1503
1504 -- calculate max distances
1505 easyArmControl.maxTotalDistance = calcDistanceFrom(easyArmControl.rootNode, easyArmControl.targetRefNode)
1506 easyArmControl.maxTransDistance = calcDistanceFrom(easyArmControl.xRotationNodes[#easyArmControl.xRotationNodes].node, easyArmControl.targetRefNode)
1507
1508 -- reset nodes again to previous states
1509 for i=1, #easyArmControl.xRotationNodes do
1510 local xRotationNode = easyArmControl.xRotationNodes[i]
1511 setRotation(xRotationNode.node, xRotationNode.startRotation[1], xRotationNode.startRotation[2], xRotationNode.startRotation[3])
1512 end
1513
1514 for i=1, #easyArmControl.zTranslationNodes do
1515 local zTranslationNode = easyArmControl.zTranslationNodes[i]
1516 setTranslation(zTranslationNode.node, zTranslationNode.startTranslation[1], zTranslationNode.startTranslation[2], zTranslationNode.startTranslation[3])
1517 end
1518 end
1519 else
1520 Logging.xmlError(xmlFile, "Missing moving tools for easy control targets!")
1521 return false
1522 end
1523 else
1524 Logging.xmlError(xmlFile, "Missing easy control targets!")
1525 return false
1526 end
1527
1528 return true
1529end

loadExtraDependentParts

Description
Definition
loadExtraDependentParts()
Code
2690function Cylindered:loadExtraDependentParts(xmlFile, baseName, entry)
2691 return true
2692end

loadMovingPartFromXML

Description
Definition
loadMovingPartFromXML()
Code
2079function Cylindered:loadMovingPartFromXML(xmlFile, key, entry)
2080 XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#index", key.."#node") --FS17 to FS19
2081
2082 local node = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings)
2083 local referenceFrame = xmlFile:getValue(key.."#referenceFrame", nil, self.components, self.i3dMappings)
2084 if node ~= nil and referenceFrame ~= nil then
2085 entry.referencePoint = xmlFile:getValue(key.."#referencePoint", nil, self.components, self.i3dMappings)
2086 entry.node = node
2087 entry.parent = getParent(node)
2088 entry.referenceFrame = referenceFrame
2089 entry.invertZ = xmlFile:getValue(key.."#invertZ", false)
2090 entry.scaleZ = xmlFile:getValue(key.."#scaleZ", false)
2091 entry.limitedAxis = xmlFile:getValue(key.."#limitedAxis")
2092 entry.isActiveDirty = xmlFile:getValue(key.."#isActiveDirty", false)
2093 entry.playSound = xmlFile:getValue(key.."#playSound", false)
2094
2095 entry.moveToReferenceFrame = xmlFile:getValue(key.."#moveToReferenceFrame", false)
2096 if entry.moveToReferenceFrame then
2097 local x,y,z = worldToLocal(referenceFrame, getWorldTranslation(node))
2098 entry.referenceFrameOffset = {x,y,z}
2099 end
2100
2101 if entry.referenceFrame == entry.node then
2102 Logging.xmlWarning(xmlFile, "Reference frame equals moving part node. This can lead to bad behaviours! Node '%s' in '%s'.", getName(entry.node), key)
2103 end
2104
2105 entry.doLineAlignment = xmlFile:getValue(key.."#doLineAlignment", false)
2106 entry.doInversedLineAlignment = xmlFile:getValue(key.."#doInversedLineAlignment", false)
2107 entry.partLength = xmlFile:getValue(key..".orientationLine#partLength", 0.5)
2108 entry.partLengthNode = xmlFile:getValue(key..".orientationLine#partLengthNode", nil, self.components, self.i3dMappings)
2109 entry.orientationLineNodes = {}
2110 local i = 0
2111 while true do
2112 local pointKey = string.format("%s.orientationLine.lineNode(%d)", key, i)
2113 if not xmlFile:hasProperty(pointKey) then
2114 break
2115 end
2116
2117 local lineNode = xmlFile:getValue(pointKey.."#node", nil, self.components, self.i3dMappings)
2118 table.insert(entry.orientationLineNodes, lineNode)
2119
2120 i = i + 1
2121 end
2122
2123 entry.doDirectionAlignment = xmlFile:getValue(key.."#doDirectionAlignment", true)
2124 entry.doRotationAlignment = xmlFile:getValue(key.."#doRotationAlignment", false)
2125 entry.rotMultiplier = xmlFile:getValue(key.."#rotMultiplier", 0)
2126
2127 local minRot = xmlFile:getValue(key.."#minRot")
2128 local maxRot = xmlFile:getValue(key.."#maxRot")
2129 if minRot ~= nil and maxRot ~= nil then
2130 if entry.limitedAxis ~= nil then
2131 entry.minRot = MathUtil.getValidLimit(minRot)
2132 entry.maxRot = MathUtil.getValidLimit(maxRot)
2133 else
2134 print("Warning: minRot/maxRot requires the use of limitedAxis in '"..self.configFileName.."'")
2135 end
2136 end
2137 entry.alignToWorldY = xmlFile:getValue(key.."#alignToWorldY", false)
2138
2139 if entry.referencePoint ~= nil then
2140 local localReferencePoint = xmlFile:getValue(key.."#localReferencePoint", nil, self.components, self.i3dMappings)
2141 local refX, refY, refZ = worldToLocal(node, getWorldTranslation(entry.referencePoint))
2142 if localReferencePoint ~= nil then
2143 local x,y,z = worldToLocal(node, getWorldTranslation(localReferencePoint))
2144
2145 entry.referenceDistance = MathUtil.vector3Length(refX-x, refY-y, refZ-z)
2146 entry.lastReferenceDistance = entry.referenceDistance
2147 entry.localReferencePoint = {x, y, z}
2148
2149 local side = y*(refZ-z) - z*(refY-y)
2150 entry.localReferenceAngleSide = side
2151 entry.localReferencePointNode = localReferencePoint
2152 entry.updateLocalReferenceDistance = xmlFile:getValue(key.."#updateLocalReferenceDistance", false)
2153 entry.localReferenceTranslate = xmlFile:getValue(key.."#localReferenceTranslate", false)
2154 if entry.localReferenceTranslate then
2155 entry.localReferenceTranslation = { getTranslation(entry.node) }
2156 end
2157
2158 entry.dynamicLocalReferenceDistance = xmlFile:getValue(key.."#dynamicLocalReferenceDistance", false)
2159 else
2160 entry.referenceDistance = 0
2161 entry.localReferencePoint = {refX, refY, refZ}
2162 end
2163 entry.referenceDistanceThreshold = xmlFile:getValue(key.."#referenceDistanceThreshold", 0)
2164
2165 entry.useLocalOffset = xmlFile:getValue(key.."#useLocalOffset", false)
2166
2167 entry.referenceDistancePoint = xmlFile:getValue(key.."#referenceDistancePoint", nil, self.components, self.i3dMappings)
2168
2169 entry.localReferenceDistance = xmlFile:getValue(key.."#localReferenceDistance", MathUtil.vector2Length(entry.localReferencePoint[2], entry.localReferencePoint[3]))
2170
2171 self:loadDependentTranslatingParts(xmlFile, key, entry)
2172 end
2173
2174 -- direction threshold for updateing the moving tools of the vehicle is entered/active
2175 entry.directionThreshold = xmlFile:getValue(key.."#directionThreshold", 0.0001)
2176 entry.directionThresholdActive = xmlFile:getValue(key.."#directionThresholdActive", 0.00001)
2177 entry.maxUpdateDistance = xmlFile:getValue(key.."#maxUpdateDistance", "-")
2178 if entry.maxUpdateDistance == "-" then
2179 entry.maxUpdateDistance = math.huge
2180 else
2181 entry.maxUpdateDistance = tonumber(entry.maxUpdateDistance)
2182 end
2183
2184 if entry.isActiveDirty and (xmlFile:getString(key.."#maxUpdateDistance") == nil or entry.maxUpdateDistance == nil) then
2185 Logging.xmlWarning(xmlFile, 'No max. update distance set for isActiveDirty moving part (%s)! Use #maxUpdateDistance attribute.', key)
2186 end
2187
2188 entry.smoothedDirectionScale = self.xmlFile:getValue(key.."#smoothedDirectionScale", false)
2189 entry.smoothedDirectionTime = 1 / (self.xmlFile:getValue(key.."#smoothedDirectionTime", 2))
2190 entry.smoothedDirectionScaleAlpha = nil
2191
2192 if entry.smoothedDirectionScale then
2193 entry.initialDirection = {localDirectionToLocal(entry.node, getParent(entry.node), 0, 0, 1)}
2194 end
2195
2196 entry.debug = xmlFile:getValue(key.."#debug", false)
2197 if entry.debug then
2198 Logging.xmlWarning(xmlFile, "MovingPart debug enabled for moving part '%s'", key)
2199 end
2200
2201 entry.lastDirection = {0, 0, 0}
2202 entry.lastUpVector = {0, 0, 0}
2203
2204 entry.isDirty = false
2205 entry.isPart = true
2206 entry.isActive = true
2207
2208 return true
2209 end
2210
2211 return false
2212end

loadMovingToolFromXML

Description
Definition
loadMovingToolFromXML()
Code
2216function Cylindered:loadMovingToolFromXML(xmlFile, key, entry)
2217 local spec = self.spec_cylindered
2218
2219 XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#index", key.."#node") --FS17 to FS19
2220
2221 local node = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings)
2222 if node ~= nil then
2223 entry.node = node
2224
2225 entry.externalMove = 0
2226 entry.easyArmControlActive = true
2227 entry.isEasyControlTarget = xmlFile:getValue(key.."#isEasyControlTarget", false)
2228
2229 entry.networkInterpolators = {}
2230
2231 -- rotation
2232 XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#rotSpeed", key..".rotation#rotSpeed") --FS15 to FS17
2233
2234 local rotSpeed = xmlFile:getValue(key..".rotation#rotSpeed")
2235 if rotSpeed ~= nil then
2236 entry.rotSpeed = rotSpeed / 1000
2237 end
2238 local rotAcceleration = xmlFile:getValue(key..".rotation#rotAcceleration")
2239 if rotAcceleration ~= nil then
2240 entry.rotAcceleration = rotAcceleration / (1000*1000)
2241 end
2242 entry.lastRotSpeed = 0
2243 entry.rotMax = xmlFile:getValue(key..".rotation#rotMax")
2244 entry.rotMin = xmlFile:getValue(key..".rotation#rotMin")
2245 entry.syncMaxRotLimits = xmlFile:getValue(key..".rotation#syncMaxRotLimits", false)
2246 entry.syncMinRotLimits = xmlFile:getValue(key..".rotation#syncMinRotLimits", false)
2247 entry.rotSendNumBits = xmlFile:getValue(key..".rotation#rotSendNumBits", 8)
2248 entry.attachRotMax = xmlFile:getValue(key..".rotation#attachRotMax")
2249 entry.attachRotMin = xmlFile:getValue(key..".rotation#attachRotMin")
2250
2251 -- translation
2252 XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#transSpeed", key..".rotation#transSpeed") --FS15 to FS17
2253
2254 local transSpeed = xmlFile:getValue(key..".translation#transSpeed")
2255 if transSpeed ~= nil then
2256 entry.transSpeed = transSpeed/1000
2257 end
2258 local transAcceleration = xmlFile:getValue(key..".translation#transAcceleration")
2259 if transAcceleration ~= nil then
2260 entry.transAcceleration = transAcceleration/(1000*1000)
2261 end
2262 entry.lastTransSpeed = 0
2263 entry.transMax = xmlFile:getValue(key..".translation#transMax")
2264 entry.transMin = xmlFile:getValue(key..".translation#transMin")
2265 entry.attachTransMax = xmlFile:getValue(key..".translation#attachTransMax")
2266 entry.attachTransMin = xmlFile:getValue(key..".translation#attachTransMin")
2267 entry.playSound = xmlFile:getValue(key.."#playSound", false)
2268
2269 entry.isConsumingPower = xmlFile:getValue(key.."#isConsumingPower", false)
2270
2271 -- animation
2272 if SpecializationUtil.hasSpecialization(AnimatedVehicle, self.specializations) then
2273 local animSpeed = xmlFile:getValue(key..".animation#animSpeed")
2274 if animSpeed ~= nil then
2275 entry.animSpeed = animSpeed / 1000
2276 end
2277 local animAcceleration = xmlFile:getValue(key..".animation#animAcceleration")
2278 if animAcceleration ~= nil then
2279 entry.animAcceleration = animAcceleration / (1000*1000)
2280 end
2281 entry.curAnimTime = 0
2282 entry.lastAnimSpeed = 0
2283 entry.animName = xmlFile:getValue(key..".animation#animName")
2284 entry.animSendNumBits = xmlFile:getValue(key..".animation#animSendNumBits", 8)
2285 entry.animMaxTime = math.min(xmlFile:getValue(key..".animation#animMaxTime", 1.0), 1.0)
2286 entry.animMinTime = math.max(xmlFile:getValue(key..".animation#animMinTime", 0.0), 0.0)
2287
2288 entry.animStartTime = xmlFile:getValue(key..".animation#animStartTime")
2289 if entry.animStartTime ~= nil then
2290 entry.curAnimTime = entry.animStartTime
2291 end
2292
2293 entry.networkInterpolators.animation = InterpolatorValue.new(entry.curAnimTime)
2294 entry.networkInterpolators.animation:setMinMax(0, 1)
2295 end
2296
2297 XMLUtil.checkDeprecatedXMLElements(xmlFile, key..".controls#iconFilename", key..".controls#iconName") --FS17 to FS19
2298
2299 local iconName = xmlFile:getValue(key .. ".controls#iconName")
2300 if iconName ~= nil then
2301 if InputHelpElement.AXIS_ICON[iconName] == nil then
2302 -- add the mod name as a prefix to match axis icon loading name collision avoidance
2303 iconName = (self.customEnvironment or "") .. iconName
2304 end
2305
2306 entry.axisActionIcon = iconName
2307 end
2308
2309 entry.controlGroupIndex = xmlFile:getValue(key .. ".controls#groupIndex", 0)
2310 if entry.controlGroupIndex ~= 0 then
2311 if spec.controlGroupNames[entry.controlGroupIndex] ~= nil then
2312 table.addElement(spec.controlGroups, entry.controlGroupIndex)
2313 else
2314 Logging.xmlWarning(xmlFile, "ControlGroup '%d' not defined for '%s'!", entry.controlGroupIndex, key)
2315 end
2316 end
2317
2318 entry.axis = xmlFile:getValue(key..".controls#axis")
2319 if entry.axis ~= nil then
2320 entry.axisActionIndex = InputAction[entry.axis]
2321 end
2322 entry.invertAxis = xmlFile:getValue(key..".controls#invertAxis", false)
2323 entry.mouseSpeedFactor = xmlFile:getValue(key..".controls#mouseSpeedFactor", 1.0)
2324
2325 if (entry.rotSpeed ~= nil or entry.transSpeed ~= nil or entry.animSpeed ~= nil) then
2326 entry.dirtyFlag = self:getNextDirtyFlag()
2327 entry.saving = xmlFile:getValue(key.."#allowSaving", true)
2328 end
2329
2330 entry.isDirty = false
2331 entry.isIntitialDirty = xmlFile:getValue(key.."#isIntitialDirty", true)
2332
2333 entry.rotationAxis = xmlFile:getValue(key..".rotation#rotationAxis", 1)
2334 entry.translationAxis = xmlFile:getValue(key..".translation#translationAxis", 3)
2335
2336 local detachingRotMaxLimit = xmlFile:getValue(key..".rotation#detachingRotMaxLimit")
2337 local detachingRotMinLimit = xmlFile:getValue(key..".rotation#detachingRotMinLimit")
2338 local detachingTransMaxLimit = xmlFile:getValue(key..".translation#detachingTransMaxLimit")
2339 local detachingTransMinLimit = xmlFile:getValue(key..".translation#detachingTransMinLimit")
2340 if detachingRotMaxLimit ~= nil or detachingRotMinLimit ~= nil or detachingTransMaxLimit ~= nil or detachingTransMinLimit ~= nil then
2341 if spec.detachLockNodes == nil then
2342 spec.detachLockNodes = {}
2343 end
2344
2345 local detachLock = {}
2346 detachLock.detachingRotMaxLimit = detachingRotMaxLimit
2347 detachLock.detachingRotMinLimit = detachingRotMinLimit
2348 detachLock.detachingTransMinLimit = detachingTransMinLimit
2349 detachLock.detachingTransMaxLimit = detachingTransMaxLimit
2350
2351 spec.detachLockNodes[entry] = detachLock
2352 end
2353
2354
2355 local rx,ry,rz = getRotation(node)
2356 entry.curRot = {rx,ry,rz}
2357 local x,y,z = getTranslation(node)
2358 entry.curTrans = {x,y,z}
2359
2360 entry.startRot = xmlFile:getValue(key..".rotation#startRot")
2361 entry.startTrans = xmlFile:getValue(key..".translation#startTrans")
2362
2363 entry.move = 0
2364 entry.moveToSend = 0
2365
2366 entry.smoothedMove = 0
2367 entry.lastInputTime = 0
2368
2369 -- delayed node
2370 XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#delayedIndex", key.."#delayedNode") --FS17 to FS19
2371
2372 entry.delayedNode = xmlFile:getValue(key.."#delayedNode", nil, self.components, self.i3dMappings)
2373 if entry.delayedNode ~= nil then
2374 entry.delayedFrames = xmlFile:getValue(key.."#delayedFrames", 3)
2375
2376 entry.currentDelayedData = {rot = {rx, ry, rz}, trans = {x, y, z}}
2377 entry.delayedHistroyData = {}
2378 for i=1, entry.delayedFrames do
2379 entry.delayedHistroyData[i] = {rot = {rx, ry, rz}, trans = {x, y, z}}
2380 end
2381
2382 entry.delayedHistoryIndex = 0
2383 end
2384
2385 entry.networkInterpolators.translation = InterpolatorValue.new(entry.curTrans[entry.translationAxis])
2386 entry.networkInterpolators.translation:setMinMax(entry.transMin, entry.transMax)
2387 entry.networkInterpolators.rotation = InterpolatorAngle.new(entry.curRot[entry.rotationAxis])
2388 entry.networkInterpolators.rotation:setMinMax(entry.rotMin, entry.rotMax)
2389 entry.networkTimeInterpolator = InterpolationTime.new(1.2)
2390
2391 entry.isTool = true
2392
2393 return true
2394 end
2395
2396 return false
2397end

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
2973function Cylindered:loadObjectChangeValuesFromXML(superFunc, xmlFile, key, node, object)
2974 superFunc(self, xmlFile, key, node, object)
2975
2976 local spec = self.spec_cylindered
2977
2978 if spec.nodesToMovingTools ~= nil and spec.nodesToMovingTools[node] ~= nil then
2979 local movingTool = spec.nodesToMovingTools[node]
2980 object.movingToolRotMaxActive = xmlFile:getValue(key.."#movingToolRotMaxActive", movingTool.rotMax)
2981 object.movingToolRotMaxInactive = xmlFile:getValue(key.."#movingToolRotMaxInactive", movingTool.rotMax)
2982 object.movingToolRotMinActive = xmlFile:getValue(key.."#movingToolRotMinActive", movingTool.rotMin)
2983 object.movingToolRotMinInactive = xmlFile:getValue(key.."#movingToolRotMinInactive", movingTool.rotMin)
2984
2985 object.movingToolTransMaxActive = xmlFile:getValue(key.."#movingToolTransMaxActive", movingTool.transMax)
2986 object.movingToolTransMaxInactive = xmlFile:getValue(key.."#movingToolTransMaxInactive", movingTool.transMax)
2987 object.movingToolTransMinActive = xmlFile:getValue(key.."#movingToolTransMinActive", movingTool.transMin)
2988 object.movingToolTransMinInactive = xmlFile:getValue(key.."#movingToolTransMinInactive", movingTool.transMin)
2989 end
2990
2991 ObjectChangeUtil.loadValueType(object.values, xmlFile, key, "movingPartUpdate", nil,
2992 function(state)
2993 if self.getMovingPartByNode ~= nil then
2994 local movingPart = self:getMovingPartByNode(node)
2995 if movingPart ~= nil then
2996 movingPart.isActive = state
2997 end
2998 end
2999 end, false)
3000end

loadRotationBasedLimits

Description
Definition
loadRotationBasedLimits()
Code
2772function Cylindered:loadRotationBasedLimits(xmlFile, key, tool)
2773 local rotation = xmlFile:getValue(key.."#rotation")
2774 local rotMin = xmlFile:getValue(key.."#rotMin")
2775 local rotMax = xmlFile:getValue(key.."#rotMax")
2776 local transMin = xmlFile:getValue(key.."#transMin")
2777 local transMax = xmlFile:getValue(key.."#transMax")
2778
2779 if rotation ~= nil and (rotMin ~= nil or rotMax ~= nil or transMin ~= nil or transMax ~= nil) then
2780 local time = (rotation-tool.rotMin) / (tool.rotMax-tool.rotMin)
2781 return {rotMin=rotMin, rotMax=rotMax, transMin=transMin, transMax=transMax, time=time}
2782 end
2783
2784 return nil
2785end

loadShovelNode

Description
Definition
loadShovelNode()
Code
3074function Cylindered:loadShovelNode(superFunc, xmlFile, key, entry)
3075 if not superFunc(self, xmlFile, key, entry) then
3076 return false
3077 end
3078
3079 local baseKey = key .. ".movingToolActivation"
3080 if not xmlFile:hasProperty(baseKey) then
3081 return true
3082 end
3083
3084 entry.movingToolActivation = {}
3085 entry.movingToolActivation.node = xmlFile:getValue(baseKey.."#node", nil, self.components, self.i3dMappings)
3086 entry.movingToolActivation.isInverted = xmlFile:getValue(baseKey.."#isInverted", false)
3087 entry.movingToolActivation.openFactor = xmlFile:getValue(baseKey.."#openFactor", 1)
3088
3089 return true
3090end

movingToolDashboardAttributes

Description
Definition
movingToolDashboardAttributes()
Code
4230function Cylindered.movingToolDashboardAttributes(self, xmlFile, key, dashboard)
4231 dashboard.axis = xmlFile:getValue(key.."#axis")
4232
4233 if dashboard.axis == nil then
4234 Logging.xmlWarning(xmlFile, "Misssing axis attribute for dashboard '%s'", key)
4235 return false
4236 end
4237
4238 dashboard.attacherJointIndex = xmlFile:getValue(key.."#attacherJointIndex")
4239 dashboard.attacherJointNode = xmlFile:getValue(key.."#attacherJointNode", nil, self.components, self.i3dMappings)
4240
4241 return true
4242end

onAnimationPartChanged

Description
Called when animation part has changed
Definition
onAnimationPartChanged()
Code
3327function Cylindered:onAnimationPartChanged(node)
3328 self:setMovingToolDirty(node)
3329end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
3317function Cylindered:onDeactivate()
3318 if self.isClient then
3319 local spec = self.spec_cylindered
3320 g_soundManager:stopSample(spec.samples.hydraulic)
3321 spec.isHydraulicSamplePlaying = false
3322 end
3323end

onDelete

Description
Called on deleting
Definition
onDelete()
Code
900function Cylindered:onDelete()
901 local spec = self.spec_cylindered
902
903 g_soundManager:deleteSamples(spec.samples)
904 g_soundManager:deleteSamples(spec.actionSamples)
905
906 if spec.movingTools ~= nil then
907 for _,movingTool in pairs(spec.movingTools) do
908 if movingTool.icon ~= nil then
909 movingTool.icon:delete()
910 movingTool.icon = nil
911 end
912 end
913 end
914end

onDraw

Description
Definition
onDraw()
Code
2066function Cylindered:onDraw(isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
2067 local spec = self.spec_cylindered
2068 if #spec.controlGroupNames > 1 then
2069 if isActiveForInputIgnoreSelection then
2070 if spec.currentControlGroupIndex ~= 0 then
2071 g_currentMission:addExtraPrintText(string.format(g_i18n:getText("action_selectedControlGroup"), spec.controlGroupNames[spec.currentControlGroupIndex], spec.currentControlGroupIndex))
2072 end
2073 end
2074 end
2075end

onLoad

Description
Called on loading
Definition
onLoad(table savegame)
Arguments
tablesavegamesavegame
Code
405function Cylindered:onLoad(savegame)
406 local spec = self.spec_cylindered
407
408 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.movingParts", "vehicle.cylindered.movingParts") --FS17 to FS19
409 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.movingTools", "vehicle.cylindered.movingTools") --FS17 to FS19
410 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.cylinderedHydraulicSound", "vehicle.cylindered.sounds.hydraulic") --FS17 to FS19
411
412 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.cylindered.movingParts#isActiveDirtyTimeOffset") --FS19 to FS22
413
414 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.cylindered.movingParts.sounds", "vehicle.cylindered.sounds") --FS19 to FS22
415
416 spec.activeDirtyMovingParts = {}
417
418 local referenceNodes = {}
419 spec.nodesToMovingParts = {}
420 spec.movingParts = {}
421 self.anyMovingPartsDirty = false
422 spec.detachLockNodes = nil
423 local i = 0
424 while true do
425 local partKey = string.format("vehicle.cylindered.movingParts.movingPart(%d)", i)
426 if not self.xmlFile:hasProperty(partKey) then
427 break
428 end
429
430 local entry = {}
431 if self:loadMovingPartFromXML(self.xmlFile, partKey, entry) then
432 if referenceNodes[entry.node] == nil then
433 referenceNodes[entry.node] = {}
434 end
435 if spec.nodesToMovingParts[entry.node] == nil then
436 table.insert(referenceNodes[entry.node], entry)
437
438 self:loadDependentParts(self.xmlFile, partKey, entry)
439 self:loadDependentComponentJoints(self.xmlFile, partKey, entry)
440 self:loadCopyLocalDirectionParts(self.xmlFile, partKey, entry)
441 self:loadExtraDependentParts(self.xmlFile, partKey, entry)
442 self:loadDependentAnimations(self.xmlFile, partKey, entry)
443
444 entry.key = partKey
445 table.insert(spec.movingParts, entry)
446
447 if entry.isActiveDirty then
448 table.insert(spec.activeDirtyMovingParts, entry)
449 end
450
451 spec.nodesToMovingParts[entry.node] = entry
452 else
453 Logging.xmlWarning(self.xmlFile, "Moving part with node '%s' already exists!", getName(entry.node))
454 end
455 end
456
457 i = i + 1
458 end
459
460 if Cylindered.DIRTY_COLLISION_UPDATE_CHECK then
461 -- collect all active dirty moving parts which have directionThreshold not changed from xml
462 -- and check if any of these has collisions as children. If yes, print warning
463 local function collectDependentParts(part, target)
464 table.insert(target, part)
465
466 if part.dependentPartNodes ~= nil then
467 for l=1, #part.dependentPartNodes do
468 local dependentMovingPart = spec.nodesToMovingParts[part.dependentPartNodes[l]]
469 if dependentMovingPart ~= nil then
470 table.insert(target, dependentMovingPart)
471
472 collectDependentParts(dependentMovingPart, target)
473 end
474 end
475 end
476 end
477
478 local function subCollisionErrorFunction(collisionNode, xmlFile, key)
479 if getHasClassId(collisionNode, ClassIds.SHAPE) then
480 Logging.xmlError(xmlFile, "Found collision '%s' as child of isActiveDirty movingPart '%s'. This can cause the vehicle to never sleep!", getName(collisionNode), key)
481 end
482 end
483
484 spec.realActiveDirtyParts = {}
485 for j=1, #spec.movingParts do
486 local movingPart = spec.movingParts[j]
487 if movingPart.isActiveDirty and movingPart.directionThreshold == 0.0001 then
488 collectDependentParts(movingPart, spec.realActiveDirtyParts)
489 end
490 end
491
492 for j=1, #spec.realActiveDirtyParts do
493 local part = spec.realActiveDirtyParts[j]
494 I3DUtil.checkForChildCollisions(part.node, subCollisionErrorFunction, self.xmlFile, getName(part.node))
495 end
496 end
497
498 spec.powerConsumingActiveTimeOffset = self.xmlFile:getValue("vehicle.cylindered.movingTools#powerConsumingActiveTimeOffset", 5)
499 spec.powerConsumingTimer = -1
500
501 -- find dependencies
502 for _, part in pairs(spec.movingParts) do
503 self:resolveDependentPartData(part.dependentPartData, referenceNodes)
504 end
505
506 local function addMovingPart(part, newTable, allowDependentParts)
507 for _, addedPart in ipairs(newTable) do
508 if addedPart == part then
509 return
510 end
511 end
512
513 if part.isDependentPart == true then
514 if allowDependentParts ~= true then
515 return
516 end
517 end
518
519 table.insert(newTable, part)
520
521 for _, depPart in pairs(part.dependentPartData) do
522 addMovingPart(depPart.part, newTable, true)
523 end
524 end
525
526 local newParts = {}
527 for _, part in ipairs(spec.movingParts) do
528 addMovingPart(part, newParts)
529 end
530 spec.movingParts = newParts
531
532 spec.controlGroups = {}
533 spec.controlGroupMapping = {}
534 spec.currentControlGroupIndex = 1
535 spec.controlGroupNames = {}
536 i = 0
537 while true do
538 local groupKey = string.format("vehicle.cylindered.movingTools.controlGroups.controlGroup(%d)", i)
539 if not self.xmlFile:hasProperty(groupKey) then
540 break
541 end
542
543 local name = self.xmlFile:getValue(groupKey.."#name", "", self.customEnvironment, false)
544 if name ~= nil then
545 table.insert(spec.controlGroupNames, name)
546 end
547
548 i = i + 1
549 end
550
551 spec.nodesToMovingTools = {}
552 spec.movingTools = {}
553 i = 0
554 while true do
555 local toolKey = string.format("vehicle.cylindered.movingTools.movingTool(%d)", i)
556 if not self.xmlFile:hasProperty(toolKey) then
557 break
558 end
559
560 local entry = {}
561 if self:loadMovingToolFromXML(self.xmlFile, toolKey, entry) then
562 if referenceNodes[entry.node] == nil then
563 referenceNodes[entry.node] = {}
564 end
565
566 if spec.nodesToMovingTools[entry.node] == nil then
567 table.insert(referenceNodes[entry.node], entry)
568
569 self:loadDependentMovingTools(self.xmlFile, toolKey, entry)
570 self:loadDependentParts(self.xmlFile, toolKey, entry)
571 self:loadDependentComponentJoints(self.xmlFile, toolKey, entry)
572 self:loadExtraDependentParts(self.xmlFile, toolKey, entry)
573 self:loadDependentAnimations(self.xmlFile, toolKey, entry)
574
575 entry.isActive = true
576 entry.key = toolKey
577 table.insert(spec.movingTools, entry)
578 spec.nodesToMovingTools[entry.node] = entry
579 else
580 Logging.xmlWarning(self.xmlFile, "Moving tool with node '%s' already exists!", getName(entry.node))
581 end
582 end
583 i = i + 1
584 end
585
586 local function sort(a, b)
587 return a < b
588 end
589 table.sort(spec.controlGroups, sort)
590
591 for _, groupIndex in ipairs(spec.controlGroups) do
592 local subSelectionIndex = self:addSubselection(groupIndex)
593 spec.controlGroupMapping[subSelectionIndex] = groupIndex
594 end
595
596 for _, part in pairs(spec.movingTools) do
597 self:resolveDependentPartData(part.dependentPartData, referenceNodes)
598
599 for j=#part.dependentMovingTools, 1, -1 do
600 local dependentTool = part.dependentMovingTools[j]
601 local tool = spec.nodesToMovingTools[dependentTool.node]
602 if tool ~= nil then
603 dependentTool.movingTool = tool
604 else
605 Logging.xmlWarning(self.xmlFile, "Dependent moving tool '%s' not defined. Ignoring it!", getName(dependentTool.node))
606 table.remove(part.dependentMovingTools, j)
607 end
608 end
609 end
610
611
612 local easyArmControlKey = "vehicle.cylindered.movingTools.easyArmControl"
613 if self.xmlFile:hasProperty(easyArmControlKey) then
614 local easyArmControl = {}
615 if self:loadEasyArmControlFromXML(self.xmlFile, easyArmControlKey, easyArmControl) then
616 spec.easyArmControl = easyArmControl
617 end
618 end
619
620 spec.samples = {}
621 spec.actionSamples = {}
622 if self.isClient then
623 spec.samples.hydraulic = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.cylindered.sounds", "hydraulic", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
624 spec.isHydraulicSamplePlaying = false
625
626 spec.nodesToSamples = {}
627 spec.activeSamples = {}
628 spec.endingSamples = {}
629 spec.endingSamplesBySample = {}
630 spec.startingSamples = {}
631 spec.startingSamplesBySample = {}
632
633 i = 0
634 while true do
635 local actionKey = string.format("actionSound(%d)", i)
636 local baseKey = "vehicle.cylindered.sounds." .. actionKey
637 if not self.xmlFile:hasProperty(baseKey) then
638 break
639 end
640
641 local sample = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.cylindered.sounds", actionKey, self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
642
643 if sample ~= nil then
644 local actionNamesStr = self.xmlFile:getValue(baseKey .. "#actionNames")
645 local actionNames = string.split(actionNamesStr:trim(), " ")
646
647 local nodesStr = self.xmlFile:getValue(baseKey .. "#nodes")
648 local nodes = string.split(nodesStr, " ")
649
650 for l=1, #nodes do
651 nodes[l] = I3DUtil.indexToObject(self.components, nodes[l], self.i3dMappings)
652 end
653
654 for j=1, #actionNames do
655 local actionName = actionNames[j]
656 actionName = "SOUND_ACTION_" .. actionName:upper()
657 local action = Cylindered[actionName]
658 if action ~= nil then
659 for l=1, #nodes do
660 local node = nodes[l]
661 if node ~= nil then
662 if spec.nodesToSamples[node] == nil then
663 spec.nodesToSamples[node] = {}
664 end
665
666 if spec.nodesToSamples[node][action] == nil then
667 spec.nodesToSamples[node][action] = {}
668 end
669
670 local part = self:getMovingPartByNode(node) or self:getTranslatingPartByNode(node) or self:getMovingToolByNode(node)
671 if part ~= nil then
672 part.samplesByAction = spec.nodesToSamples[node]
673 else
674 Logging.xmlWarning(self.xmlFile, "Unable to find movingPart or translatingPart for node '%s'", getName(node))
675 end
676
677 table.insert(spec.nodesToSamples[node][action], sample)
678 end
679 end
680 else
681 Logging.xmlWarning(self.xmlFile, "Unable to find sound action '%s' for sound '%s'", actionName, baseKey)
682 end
683 end
684
685 sample.dropOffFactor = self.xmlFile:getValue(baseKey .. ".pitch#dropOffFactor", 1)
686 sample.dropOffTime = self.xmlFile:getValue(baseKey .. ".pitch#dropOffTime", 0) * 1000
687
688 sample.actionNames = actionNames
689 sample.nodes = nodes
690
691 table.insert(spec.actionSamples, sample)
692 end
693
694 i = i + 1
695 end
696 end
697
698 if self.loadDashboardsFromXML ~= nil then
699 local dashboardData = {valueTypeToLoad = "movingTool",
700 valueObject = self,
701 valueFunc = Cylindered.getMovingToolDashboardState,
702 minFunc = 0,
703 maxFunc = 1,
704 additionalAttributesFunc = Cylindered.movingToolDashboardAttributes,
705 idleValue = 0.5}
706
707 self:loadDashboardsFromXML(self.xmlFile, "vehicle.cylindered.dashboards", dashboardData)
708 end
709
710 spec.cylinderedDirtyFlag = self:getNextDirtyFlag()
711 spec.cylinderedInputDirtyFlag = self:getNextDirtyFlag()
712
713 self:registerVehicleSetting(GameSettings.SETTING.EASY_ARM_CONTROL, true)
714
715 spec.isLoading = true
716end

onLoadFinished

Description
Called after loading
Definition
onLoadFinished(table savegame)
Arguments
tablesavegamesavegame
Code
886function Cylindered:onLoadFinished(savegame)
887 local spec = self.spec_cylindered
888 spec.isLoading = false
889
890 for i=1, #spec.movingTools do
891 local tool = spec.movingTools[i]
892 if tool.delayedHistoryIndex ~= nil and tool.delayedHistoryIndex > 0 then
893 self:updateDelayedTool(tool, true)
894 end
895 end
896end

onMovingPartSoundEvent

Description
Definition
onMovingPartSoundEvent()
Code
1820function Cylindered:onMovingPartSoundEvent(part, action, type)
1821 if part.samplesByAction ~= nil then
1822 local samples = part.samplesByAction[action]
1823 if samples ~= nil then
1824 for i=1, #samples do
1825 local sample = samples[i]
1826
1827 if type == Cylindered.SOUND_TYPE_EVENT then
1828 if sample.loops == 0 then
1829 sample.loops = 1
1830 end
1831
1832 g_soundManager:playSample(sample)
1833 elseif type == Cylindered.SOUND_TYPE_CONTINUES then
1834 if not g_soundManager:getIsSamplePlaying(sample) then
1835 g_soundManager:playSample(sample)
1836
1837 sample.lastActivationTime = g_time
1838 sample.lastActivationPart = part
1839
1840 local spec = self.spec_cylindered
1841 table.insert(spec.activeSamples, sample)
1842 else
1843 if sample.lastActivationPart == part then
1844 sample.lastActivationTime = g_time
1845 end
1846 end
1847 elseif type == Cylindered.SOUND_TYPE_ENDING then
1848 local spec = self.spec_cylindered
1849 if spec.endingSamplesBySample[sample] == nil then
1850 sample.lastActivationTime = g_time
1851
1852 table.insert(spec.endingSamples, sample)
1853 spec.endingSamplesBySample[sample] = sample
1854 else
1855 sample.lastActivationTime = g_time
1856 end
1857 elseif type == Cylindered.SOUND_TYPE_STARTING then
1858 local spec = self.spec_cylindered
1859 if spec.startingSamplesBySample[sample] == nil then
1860 if sample.loops == 0 then
1861 sample.loops = 1
1862 end
1863
1864 g_soundManager:playSample(sample)
1865
1866 sample.lastActivationTime = g_time
1867
1868 table.insert(spec.startingSamples, sample)
1869 spec.startingSamplesBySample[sample] = sample
1870 else
1871 sample.lastActivationTime = g_time
1872 end
1873 end
1874 end
1875 end
1876 end
1877end

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
3251function Cylindered:onPostAttach(attacherVehicle, inputJointDescIndex, jointDescIndex)
3252 local spec = self.spec_cylindered
3253
3254 for _, tool in ipairs(spec.movingTools) do
3255 local changed = false
3256 if tool.transSpeed ~= nil then
3257 local trans = tool.curTrans[tool.translationAxis]
3258
3259 local changedTrans = false
3260 if tool.attachTransMax ~= nil and trans > tool.attachTransMax then
3261 trans = tool.attachTransMax
3262 changedTrans = true
3263 elseif tool.attachTransMin ~= nil and trans < tool.attachTransMin then
3264 trans = tool.attachTransMin
3265 changedTrans = true
3266 end
3267 if changedTrans then
3268 tool.curTrans[tool.translationAxis] = trans
3269 setTranslation(tool.node, unpack(tool.curTrans))
3270 changed = true
3271 end
3272 end
3273 if tool.rotSpeed ~= nil then
3274 local rot = tool.curRot[tool.rotationAxis]
3275
3276 local changedRot = false
3277 if tool.attachRotMax ~= nil and rot > tool.attachRotMax then
3278 rot = tool.attachRotMax
3279 changedRot = true
3280 elseif tool.attachRotMin ~= nil and rot < tool.attachRotMin then
3281 rot = tool.attachRotMin
3282 changedRot = true
3283 end
3284 if changedRot then
3285 tool.curRot[tool.rotationAxis] = rot
3286 setRotation(tool.node, unpack(tool.curRot))
3287 changed = true
3288 end
3289 end
3290 if changed then
3291 Cylindered.setDirty(self, tool)
3292 end
3293 end
3294end

onPostLoad

Description
Called after loading
Definition
onPostLoad(table savegame)
Arguments
tablesavegamesavegame
Code
721function Cylindered:onPostLoad(savegame)
722 local spec = self.spec_cylindered
723
724 for _, tool in pairs(spec.movingTools) do
725 if self:getIsMovingToolActive(tool) then
726 if tool.startRot ~= nil then
727 tool.curRot[tool.rotationAxis] = tool.startRot
728 setRotation(tool.node, unpack(tool.curRot))
729 end
730 if tool.startTrans ~= nil then
731 tool.curTrans[tool.translationAxis] = tool.startTrans
732 setTranslation(tool.node, unpack(tool.curTrans))
733 end
734
735 if tool.animStartTime ~= nil then
736 self:setAnimationTime(tool.animName, tool.animStartTime, nil, false)
737 end
738
739 if tool.delayedNode ~= nil then
740 self:setDelayedData(tool, true)
741 end
742
743 if tool.isIntitialDirty then
744 Cylindered.setDirty(self, tool)
745 end
746 end
747 end
748
749 for _, part in pairs(spec.movingParts) do
750 self:loadDependentAttacherJoints(self.xmlFile, part.key, part)
751 self:loadDependentWheels(self.xmlFile, part.key, part)
752 end
753
754 for _, tool in pairs(spec.movingTools) do
755 self:loadDependentAttacherJoints(self.xmlFile, tool.key, tool)
756 self:loadDependentWheels(self.xmlFile, tool.key, tool)
757 end
758
759 if self:allowLoadMovingToolStates() then
760 if savegame ~= nil and not savegame.resetVehicles then
761 local i = 0
762 for _, tool in ipairs(spec.movingTools) do
763 if tool.saving then
764 if self:getIsMovingToolActive(tool) then
765 local toolKey = string.format("%s.cylindered.movingTool(%d)", savegame.key, i)
766 local changed = false
767 if tool.transSpeed ~= nil then
768 local newTrans = savegame.xmlFile:getValue(toolKey.."#translation")
769 if newTrans ~= nil then
770 if tool.transMax ~= nil then
771 newTrans = math.min(newTrans, tool.transMax)
772 end
773 if tool.transMin ~= nil then
774 newTrans = math.max(newTrans, tool.transMin)
775 end
776 end
777 if newTrans ~= nil and math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then
778 tool.curTrans = {getTranslation(tool.node)}
779 tool.curTrans[tool.translationAxis] = newTrans
780 setTranslation(tool.node, unpack(tool.curTrans))
781 changed = true
782 end
783 end
784 if tool.rotSpeed ~= nil then
785 local newRot = savegame.xmlFile:getValue(toolKey.."#rotation")
786 if newRot ~= nil then
787 if tool.rotMax ~= nil then
788 newRot = math.min(newRot, tool.rotMax)
789 end
790 if tool.rotMin ~= nil then
791 newRot = math.max(newRot, tool.rotMin)
792 end
793 end
794 if newRot ~= nil and math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then
795 tool.curRot = {getRotation(tool.node)}
796 tool.curRot[tool.rotationAxis] = newRot
797 setRotation(tool.node, unpack(tool.curRot))
798 changed = true
799 end
800 end
801 if tool.animSpeed ~= nil then
802 local animTime = savegame.xmlFile:getValue(toolKey.."#animationTime")
803 if animTime ~= nil then
804 if tool.animMinTime ~= nil then
805 animTime = math.max(animTime, tool.animMinTime)
806 end
807 if tool.animMaxTime ~= nil then
808 animTime = math.min(animTime, tool.animMaxTime)
809 end
810
811 tool.curAnimTime = animTime
812 self:setAnimationTime(tool.animName, animTime, true, false)
813 end
814 end
815 if changed then
816 Cylindered.setDirty(self, tool)
817 end
818
819 if tool.delayedNode ~= nil then
820 self:setDelayedData(tool, true)
821 end
822 end
823 i = i + 1
824 end
825
826 for _, dependentTool in pairs(tool.dependentMovingTools) do
827 Cylindered.updateRotationBasedLimits(self, tool, dependentTool)
828 end
829 end
830 end
831 end
832
833 self:updateEasyControl(9999, true)
834 self:updateCylinderedInitial(false)
835
836 local hasTools, hasParts = #spec.movingTools > 0, #spec.movingParts > 0
837 if not hasTools then
838 SpecializationUtil.removeEventListener(self, "onReadStream", Cylindered)
839 SpecializationUtil.removeEventListener(self, "onWriteStream", Cylindered)
840 SpecializationUtil.removeEventListener(self, "onReadUpdateStream", Cylindered)
841 SpecializationUtil.removeEventListener(self, "onWriteUpdateStream", Cylindered)
842 SpecializationUtil.removeEventListener(self, "onUpdate", Cylindered)
843
844 if not hasParts then
845 SpecializationUtil.removeEventListener(self, "onUpdateTick", Cylindered)
846 SpecializationUtil.removeEventListener(self, "onPostUpdate", Cylindered)
847 SpecializationUtil.removeEventListener(self, "onPostUpdateTick", Cylindered)
848 end
849 end
850
851 if not self.isClient or not hasTools then
852 SpecializationUtil.removeEventListener(self, "onDraw", Cylindered)
853 SpecializationUtil.removeEventListener(self, "onRegisterActionEvents", Cylindered)
854 end
855
856 -- check if functional nodes are included in moving parts with limited update distance
857 if g_isDevelopmentVersion then
858 local function checkPart(part)
859 I3DUtil.interateRecursively(part.node, function(child, depth)
860 self:checkMovingPartDirtyUpdateNode(child, part)
861 end, math.huge)
862
863 if part.dependentPartData ~= nil then
864 for _, data in pairs(part.dependentPartData) do
865 if data.part ~= nil then
866 checkPart(data.part)
867 end
868 end
869 end
870 end
871
872 for j=1, #spec.movingParts do
873 local movingPart = spec.movingParts[j]
874 if movingPart.isActiveDirty then
875 if movingPart.maxUpdateDistance ~= math.huge then
876 checkPart(movingPart)
877 end
878 end
879 end
880 end
881end

onPostUpdate

Description
Definition
onPostUpdate()
Code
1986function Cylindered:onPostUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1987 local spec = self.spec_cylindered
1988 for _, part in pairs(spec.activeDirtyMovingParts) do
1989 if self.currentUpdateDistance < part.maxUpdateDistance then
1990 Cylindered.setDirty(self, part)
1991 end
1992 end
1993
1994 self:updateDirtyMovingParts(dt, true)
1995end

onPostUpdateTick

Description
Definition
onPostUpdateTick()
Code
1999function Cylindered:onPostUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
2000 -- update the moving parts that have been set dirty in the updateTick, so they won't be one frame delayed
2001 self:updateDirtyMovingParts(dt, false)
2002end

onReadStream

Description
Called on client side on join
Definition
onReadStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
945function Cylindered:onReadStream(streamId, connection)
946 local spec = self.spec_cylindered
947
948 if connection:getIsServer() then
949 if streamReadBool(streamId) then
950 for i=1, #spec.movingTools do
951 local tool = spec.movingTools[i]
952 if tool.dirtyFlag ~= nil then
953 tool.networkTimeInterpolator:reset()
954 if tool.transSpeed ~= nil then
955 local newTrans = streamReadFloat32(streamId)
956 tool.curTrans[tool.translationAxis] = newTrans
957 setTranslation(tool.node, unpack(tool.curTrans))
958 tool.networkInterpolators.translation:setValue(tool.curTrans[tool.translationAxis])
959 end
960 if tool.rotSpeed ~= nil then
961 local newRot = streamReadFloat32(streamId)
962 tool.curRot[tool.rotationAxis] = newRot
963 setRotation(tool.node, unpack(tool.curRot))
964 tool.networkInterpolators.rotation:setAngle(newRot)
965 end
966 if tool.animSpeed ~= nil then
967 local newAnimTime = streamReadFloat32(streamId)
968 tool.curAnimTime = newAnimTime
969 self:setAnimationTime(tool.animName, tool.curAnimTime, nil, false)
970 tool.networkInterpolators.animation:setValue(newAnimTime)
971 end
972 if tool.delayedNode ~= nil then
973 self:setDelayedData(tool, true)
974 end
975 Cylindered.setDirty(self, tool)
976 end
977 end
978 end
979 end
980end

onReadUpdateStream

Description
Called on on update
Definition
onReadUpdateStream(integer streamId, integer timestamp, table connection)
Arguments
integerstreamIdstream ID
integertimestamptimestamp
tableconnectionconnection
Code
1014function Cylindered:onReadUpdateStream(streamId, timestamp, connection)
1015 local spec = self.spec_cylindered
1016
1017 -- if server, read input from client
1018 if not connection:getIsServer() then
1019 if streamReadBool(streamId) then
1020 for _, tool in ipairs(spec.movingTools) do
1021 if tool.axisActionIndex ~= nil then
1022 tool.move = (streamReadUIntN(streamId, 12) / 4095 * 2 - 1) * 5
1023 if math.abs(tool.move) < 0.01 then
1024 tool.move = 0
1025 end
1026 end
1027 end
1028 end
1029 else
1030 -- if client, read updated attributes
1031 if streamReadBool(streamId) then
1032 for _, tool in ipairs(spec.movingTools) do
1033 if tool.dirtyFlag ~= nil then
1034 if streamReadBool(streamId) then
1035 tool.networkTimeInterpolator:startNewPhaseNetwork()
1036
1037 if tool.transSpeed ~= nil then
1038 local newTrans = streamReadFloat32(streamId)
1039 if math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then
1040 tool.networkInterpolators.translation:setTargetValue(newTrans)
1041 end
1042 end
1043 if tool.rotSpeed ~= nil then
1044 local newRot
1045 if tool.rotMin == nil or tool.rotMax == nil then
1046 newRot = NetworkUtil.readCompressedAngle(streamId)
1047 else
1048 if tool.syncMinRotLimits then
1049 tool.rotMin = streamReadFloat32(streamId)
1050 end
1051 if tool.syncMaxRotLimits then
1052 tool.rotMax = streamReadFloat32(streamId)
1053 end
1054
1055 tool.networkInterpolators.rotation:setMinMax(tool.rotMin, tool.rotMax)
1056 newRot = NetworkUtil.readCompressedRange(streamId, tool.rotMin, tool.rotMax, tool.rotSendNumBits)
1057 end
1058 if math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then
1059 tool.networkInterpolators.rotation:setTargetAngle(newRot)
1060 end
1061 end
1062 if tool.animSpeed ~= nil then
1063 local newAnimTime = NetworkUtil.readCompressedRange(streamId, tool.animMinTime, tool.animMaxTime, tool.animSendNumBits)
1064 if math.abs(newAnimTime - tool.curAnimTime) > 0.0001 then
1065 tool.networkInterpolators.animation:setTargetValue(newAnimTime)
1066 end
1067 end
1068 end
1069 end
1070 end
1071 end
1072 end
1073end

onRegisterActionEvents

Description
Definition
onRegisterActionEvents()
Code
3225function Cylindered:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
3226 if self.isClient then
3227 local spec = self.spec_cylindered
3228 self:clearActionEventsTable(spec.actionEvents)
3229
3230 -- no check for selection since movingTools can be controlled globally
3231 -- sub selection moving tools always require the selection of the sub index
3232 if isActiveForInputIgnoreSelection then
3233 for i=1, #spec.movingTools do
3234 local movingTool = spec.movingTools[i]
3235 local isSelectedGroup = movingTool.controlGroupIndex == 0 or movingTool.controlGroupIndex == spec.currentControlGroupIndex
3236 local canBeControlled = (not g_gameSettings:getValue("easyArmControl") and not movingTool.isEasyControlTarget) or movingTool.easyArmControlActive
3237 if movingTool.axisActionIndex ~= nil and isSelectedGroup and canBeControlled then
3238 local _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, movingTool.axisActionIndex, self, Cylindered.actionEventInput, false, false, true, true, i, movingTool.axisActionIcon)
3239 g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL)
3240 end
3241 end
3242 end
3243 end
3244end

onSelect

Description
Definition
onSelect()
Code
3298function Cylindered:onSelect(subSelectionIndex)
3299 local spec = self.spec_cylindered
3300 local controlGroupIndex = spec.controlGroupMapping[subSelectionIndex]
3301 if controlGroupIndex ~= nil then
3302 spec.currentControlGroupIndex = controlGroupIndex
3303 else
3304 spec.currentControlGroupIndex = 0
3305 end
3306end

onUnselect

Description
Definition
onUnselect()
Code
3310function Cylindered:onUnselect()
3311 local spec = self.spec_cylindered
3312 spec.currentControlGroupIndex = 0
3313end

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
1132function Cylindered:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1133 local spec = self.spec_cylindered
1134
1135 spec.movingToolNeedsSound = false
1136 spec.movingPartNeedsSound = false
1137
1138 self:updateEasyControl(dt)
1139
1140 if self.isServer then
1141 for i=1, #spec.movingTools do
1142 local tool = spec.movingTools[i]
1143
1144 local rotSpeed = 0
1145 local transSpeed = 0
1146 local animSpeed = 0
1147
1148 local move = self:getMovingToolMoveValue(tool)
1149 if math.abs(move) > 0 then
1150 tool.externalMove = 0
1151
1152 if tool.rotSpeed ~= nil then
1153 rotSpeed = move*tool.rotSpeed
1154 if tool.rotAcceleration ~= nil and math.abs(rotSpeed - tool.lastRotSpeed) >= tool.rotAcceleration*dt then
1155 if rotSpeed > tool.lastRotSpeed then
1156 rotSpeed = tool.lastRotSpeed + tool.rotAcceleration*dt
1157 else
1158 rotSpeed = tool.lastRotSpeed - tool.rotAcceleration*dt
1159 end
1160 end
1161 end
1162 if tool.transSpeed ~= nil then
1163 transSpeed = move*tool.transSpeed
1164 if tool.transAcceleration ~= nil and math.abs(transSpeed - tool.lastTransSpeed) >= tool.transAcceleration*dt then
1165 if transSpeed > tool.lastTransSpeed then
1166 transSpeed = tool.lastTransSpeed + tool.transAcceleration*dt
1167 else
1168 transSpeed = tool.lastTransSpeed - tool.transAcceleration*dt
1169 end
1170 end
1171 end
1172 if tool.animSpeed ~= nil then
1173 animSpeed = move*tool.animSpeed
1174 if tool.animAcceleration ~= nil and math.abs(animSpeed - tool.lastAnimSpeed) >= tool.animAcceleration*dt then
1175 if animSpeed > tool.lastAnimSpeed then
1176 animSpeed = tool.lastAnimSpeed + tool.animAcceleration*dt
1177 else
1178 animSpeed = tool.lastAnimSpeed - tool.animAcceleration*dt
1179 end
1180 end
1181 end
1182 else
1183 -- decelerate
1184 if tool.rotAcceleration ~= nil then
1185 if tool.lastRotSpeed < 0 then
1186 rotSpeed = math.min(tool.lastRotSpeed + tool.rotAcceleration*dt, 0)
1187 else
1188 rotSpeed = math.max(tool.lastRotSpeed - tool.rotAcceleration*dt, 0)
1189 end
1190 end
1191 if tool.transAcceleration ~= nil then
1192 if tool.lastTransSpeed < 0 then
1193 transSpeed = math.min(tool.lastTransSpeed + tool.transAcceleration*dt, 0)
1194 else
1195 transSpeed = math.max(tool.lastTransSpeed - tool.transAcceleration*dt, 0)
1196 end
1197 end
1198 if tool.animAcceleration ~= nil then
1199 if tool.lastAnimSpeed < 0 then
1200 animSpeed = math.min(tool.lastAnimSpeed + tool.animAcceleration*dt, 0)
1201 else
1202 animSpeed = math.max(tool.lastAnimSpeed - tool.animAcceleration*dt, 0)
1203 end
1204 end
1205 end
1206
1207 local changed = false
1208 if rotSpeed ~= nil and rotSpeed ~= 0 then
1209 changed = changed or Cylindered.setToolRotation(self, tool, rotSpeed, dt)
1210 else
1211 tool.lastRotSpeed = 0
1212 end
1213 if transSpeed ~= nil and transSpeed ~= 0 then
1214 changed = changed or Cylindered.setToolTranslation(self, tool, transSpeed, dt)
1215 else
1216 tool.lastTransSpeed = 0
1217 end
1218 if animSpeed ~= nil and animSpeed ~= 0 then
1219 changed = changed or Cylindered.setToolAnimation(self, tool, animSpeed, dt)
1220 else
1221 tool.lastAnimSpeed = 0
1222 end
1223
1224 for _, dependentTool in pairs(tool.dependentMovingTools) do
1225 if dependentTool.speedScale ~= nil then
1226 local isAllowed = true
1227 if dependentTool.requiresMovement then
1228 if not changed then
1229 isAllowed = false
1230 end
1231 end
1232
1233 if isAllowed then
1234 dependentTool.movingTool.externalMove = dependentTool.speedScale * tool.move
1235 end
1236 end
1237
1238 Cylindered.updateRotationBasedLimits(self, tool, dependentTool)
1239
1240 self:updateDependentToolLimits(tool, dependentTool)
1241 end
1242
1243 if changed then
1244 if tool.playSound then
1245 spec.movingToolNeedsSound = true
1246 end
1247 Cylindered.setDirty(self, tool)
1248 tool.networkPositionIsDirty = true
1249 self:raiseDirtyFlags(tool.dirtyFlag)
1250 self:raiseDirtyFlags(spec.cylinderedDirtyFlag)
1251
1252 -- 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
1253 tool.networkDirtyNextFrame = true
1254
1255 if tool.isConsumingPower then
1256 spec.powerConsumingTimer = spec.powerConsumingActiveTimeOffset
1257 end
1258 else
1259 if tool.networkDirtyNextFrame then
1260 self:raiseDirtyFlags(tool.dirtyFlag)
1261 self:raiseDirtyFlags(spec.cylinderedDirtyFlag)
1262 tool.networkDirtyNextFrame = nil
1263 end
1264 end
1265 end
1266 else
1267 -- client side
1268 for i=1, #spec.movingTools do
1269 local tool = spec.movingTools[i]
1270
1271 tool.networkTimeInterpolator:update(dt)
1272 local interpolationAlpha = tool.networkTimeInterpolator:getAlpha()
1273 local changed = false
1274
1275 if self:getIsMovingToolActive(tool) then
1276 if tool.rotSpeed ~= nil then
1277 local newRot = tool.networkInterpolators.rotation:getInterpolatedValue(interpolationAlpha)
1278 if math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then
1279 changed = true
1280 tool.curRot[tool.rotationAxis] = newRot
1281 setRotation(tool.node, tool.curRot[1], tool.curRot[2], tool.curRot[3])
1282 end
1283 end
1284
1285 if tool.transSpeed ~= nil then
1286 local newTrans = tool.networkInterpolators.translation:getInterpolatedValue(interpolationAlpha)
1287 if math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then
1288 changed = true
1289 tool.curTrans[tool.translationAxis] = newTrans
1290 setTranslation(tool.node, tool.curTrans[1], tool.curTrans[2], tool.curTrans[3])
1291 end
1292 end
1293
1294 if tool.animSpeed ~= nil then
1295 local newAnimTime = tool.networkInterpolators.animation:getInterpolatedValue(interpolationAlpha)
1296 if math.abs(newAnimTime - tool.curAnimTime) > 0.0001 then
1297 changed = true
1298 tool.curAnimTime = newAnimTime
1299 self:setAnimationTime(tool.animName, newAnimTime, nil, true)
1300 end
1301 end
1302
1303 if changed then
1304 Cylindered.setDirty(self, tool)
1305 end
1306 end
1307
1308 for _, dependentTool in pairs(tool.dependentMovingTools) do
1309 if not dependentTool.movingTool.syncMinRotLimits or not dependentTool.movingTool.syncMaxRotLimits then
1310 self:updateDependentToolLimits(tool, dependentTool)
1311 end
1312 end
1313
1314 if tool.networkTimeInterpolator:isInterpolating() then
1315 self:raiseActive()
1316 end
1317 end
1318 end
1319
1320 for i=1, #spec.movingTools do
1321 local tool = spec.movingTools[i]
1322 if tool.delayedHistoryIndex ~= nil and tool.delayedHistoryIndex > 0 then
1323 self:updateDelayedTool(tool)
1324 end
1325
1326 if tool.smoothedMove ~= 0 then
1327 if tool.lastInputTime + 50 < g_time then
1328 tool.smoothedMove = 0
1329 end
1330 end
1331 end
1332
1333 if spec.powerConsumingTimer > 0 then
1334 spec.powerConsumingTimer = spec.powerConsumingTimer - dt
1335 end
1336
1337 if next(spec.activeSamples) ~= nil then
1338 self:raiseActive()
1339 end
1340end

onUpdateEnd

Description
Definition
onUpdateEnd()
Code
1973function Cylindered:onUpdateEnd(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1974 -- force update of all moving parts independent of camera distance right before vehicles starts to sleep
1975 -- so if we get into the moving part update distance agan we are already in the right state without waking up the vehicle
1976 local spec = self.spec_cylindered
1977 for _, part in pairs(spec.activeDirtyMovingParts) do
1978 Cylindered.setDirty(self, part)
1979 end
1980
1981 self:updateDirtyMovingParts(dt, true)
1982end

onUpdateTick

Description
Definition
onUpdateTick()
Code
1916function Cylindered:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1917 if self.isClient then
1918 local spec = self.spec_cylindered
1919 for _,movingTool in pairs(spec.movingTools) do
1920 if movingTool.axisActionIndex ~= nil then
1921 -- check only movingTools from selected control group since the other movingTools action events are not registered
1922 if spec.currentControlGroupIndex == movingTool.controlGroupIndex then
1923 local actionEvent = spec.actionEvents[movingTool.axisActionIndex]
1924 if actionEvent ~= nil then
1925 g_inputBinding:setActionEventActive(actionEvent.actionEventId, self:getIsMovingToolActive(movingTool))
1926 end
1927 end
1928 end
1929 end
1930
1931 for i=#spec.activeSamples, 1, -1 do
1932 local sample = spec.activeSamples[i]
1933 if sample.lastActivationTime + dt * 3 < g_time then
1934 if sample.lastActivationTime + dt * 3 + sample.dropOffTime >= g_time then
1935 if not sample.dropOffActive then
1936 sample.dropOffActive = true
1937 g_soundManager:setSamplePitchOffset(sample, g_soundManager:getCurrentSamplePitch(sample) * (sample.dropOffFactor - 1))
1938 end
1939 else
1940 sample.dropOffActive = false
1941 g_soundManager:setSamplePitchOffset(sample, 0)
1942 g_soundManager:stopSample(sample)
1943 table.remove(spec.activeSamples, i)
1944 end
1945 end
1946 end
1947
1948 for i=#spec.endingSamples, 1, -1 do
1949 local sample = spec.endingSamples[i]
1950 if sample.lastActivationTime + dt < g_time then
1951 if sample.loops == 0 then
1952 sample.loops = 1
1953 end
1954
1955 g_soundManager:playSample(sample)
1956 table.remove(spec.endingSamples, i)
1957 spec.endingSamplesBySample[sample] = nil
1958 end
1959 end
1960
1961 for i=#spec.startingSamples, 1, -1 do
1962 local sample = spec.startingSamples[i]
1963 if sample.lastActivationTime + dt < g_time then
1964 table.remove(spec.startingSamples, i)
1965 spec.startingSamplesBySample[sample] = nil
1966 end
1967 end
1968 end
1969end

onVehicleSettingChanged

Description
Called when vehicle settings change
Definition
onVehicleSettingChanged()
Code
3333function Cylindered:onVehicleSettingChanged(gameSettingId, state)
3334 if gameSettingId == GameSettings.SETTING.EASY_ARM_CONTROL then
3335 self:setIsEasyControlActive(state)
3336 end
3337end

onWriteStream

Description
Called on server side on join
Definition
onWriteStream(integer streamId, integer connection)
Arguments
integerstreamIdstreamId
integerconnectionconnection
Code
986function Cylindered:onWriteStream(streamId, connection)
987 local spec = self.spec_cylindered
988
989 if not connection:getIsServer() then
990 if streamWriteBool(streamId, self:allowLoadMovingToolStates()) then
991 for i=1, #spec.movingTools do
992 local tool = spec.movingTools[i]
993 if tool.dirtyFlag ~= nil then
994 if tool.transSpeed ~= nil then
995 streamWriteFloat32(streamId, tool.curTrans[tool.translationAxis])
996 end
997 if tool.rotSpeed ~= nil then
998 streamWriteFloat32(streamId, tool.curRot[tool.rotationAxis])
999 end
1000 if tool.animSpeed ~= nil then
1001 streamWriteFloat32(streamId, tool.curAnimTime)
1002 end
1003 end
1004 end
1005 end
1006 end
1007end

onWriteUpdateStream

Description
Called on on update
Definition
onWriteUpdateStream(integer streamId, table connection, integer dirtyMask)
Arguments
integerstreamIdstream ID
tableconnectionconnection
integerdirtyMaskdirty mask
Code
1080function Cylindered:onWriteUpdateStream(streamId, connection, dirtyMask)
1081 local spec = self.spec_cylindered
1082
1083 -- if client, send input to server
1084 if connection:getIsServer() then
1085 if streamWriteBool(streamId, bitAND(dirtyMask, spec.cylinderedInputDirtyFlag) ~= 0) then
1086 for _, tool in ipairs(spec.movingTools) do
1087 if tool.axisActionIndex ~= nil then
1088 local value = (MathUtil.clamp(tool.moveToSend / 5, -1, 1) + 1) / 2 * 4095
1089 streamWriteUIntN(streamId, value, 12)
1090 end
1091 end
1092 end
1093 else
1094 -- if server, send updated attributes
1095 if streamWriteBool(streamId, bitAND(dirtyMask, spec.cylinderedDirtyFlag) ~= 0) then
1096 for _, tool in ipairs(spec.movingTools) do
1097 if tool.dirtyFlag ~= nil then
1098 if streamWriteBool(streamId, bitAND(dirtyMask, tool.dirtyFlag) ~= 0 and self:getIsMovingToolActive(tool)) then
1099 if tool.transSpeed ~= nil then
1100 streamWriteFloat32(streamId, tool.curTrans[tool.translationAxis])
1101 end
1102 if tool.rotSpeed ~= nil then
1103 local rot = tool.curRot[tool.rotationAxis]
1104 if tool.rotMin == nil or tool.rotMax == nil then
1105 NetworkUtil.writeCompressedAngle(streamId, rot)
1106 else
1107 if tool.syncMinRotLimits then
1108 streamWriteFloat32(streamId, tool.rotMin)
1109 end
1110 if tool.syncMaxRotLimits then
1111 streamWriteFloat32(streamId, tool.rotMax)
1112 end
1113 NetworkUtil.writeCompressedRange(streamId, rot, tool.rotMin, tool.rotMax, tool.rotSendNumBits)
1114 end
1115 end
1116 if tool.animSpeed ~= nil then
1117 NetworkUtil.writeCompressedRange(streamId, tool.curAnimTime, tool.animMinTime, tool.animMaxTime, tool.animSendNumBits)
1118 end
1119 end
1120 end
1121 end
1122 end
1123 end
1124end

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
51function Cylindered.prerequisitesPresent(specializations)
52 return SpecializationUtil.hasSpecialization(VehicleSettings, specializations)
53end

registerCopyLocalDirectionXMLPaths

Description
Definition
registerCopyLocalDirectionXMLPaths()
Code
292function Cylindered.registerCopyLocalDirectionXMLPaths(schema, basePath)
293 schema:register(XMLValueType.NODE_INDEX, basePath .. ".copyLocalDirectionPart(?)#node", "Copy local direction part")
294 schema:register(XMLValueType.VECTOR_3, basePath .. ".copyLocalDirectionPart(?)#dirScale", "Direction scale")
295 schema:register(XMLValueType.VECTOR_3, basePath .. ".copyLocalDirectionPart(?)#upScale", "Up vector scale")
296
297 Cylindered.registerDependentComponentJointXMLPaths(schema, basePath .. ".copyLocalDirectionPart(?)")
298end

registerDependentAnimationXMLPaths

Description
Definition
registerDependentAnimationXMLPaths()
Code
302function Cylindered.registerDependentAnimationXMLPaths(schema, basePath)
303 schema:register(XMLValueType.STRING, basePath .. ".dependentAnimation(?)#name", "Dependent animation name")
304 schema:register(XMLValueType.INT, basePath .. ".dependentAnimation(?)#translationAxis", "Translation axis")
305 schema:register(XMLValueType.INT, basePath .. ".dependentAnimation(?)#rotationAxis", "Rotation axis")
306 schema:register(XMLValueType.INT, basePath .. ".dependentAnimation(?)#useTranslatingPartIndex", "Use translation part index")
307 schema:register(XMLValueType.FLOAT, basePath .. ".dependentAnimation(?)#minValue", "Min. reference value")
308 schema:register(XMLValueType.FLOAT, basePath .. ".dependentAnimation(?)#maxValue", "Max. reference value")
309 schema:register(XMLValueType.BOOL, basePath .. ".dependentAnimation(?)#invert", "Invert reference value", false)
310end

registerDependentComponentJointXMLPaths

Description
Definition
registerDependentComponentJointXMLPaths()
Code
285function Cylindered.registerDependentComponentJointXMLPaths(schema, basePath)
286 schema:register(XMLValueType.INT, basePath .. ".componentJoint(?)#index", "Dependent component joint index")
287 schema:register(XMLValueType.INT, basePath .. ".componentJoint(?)#anchorActor", "Dependent component anchor actor")
288end

registerEventListeners

Description
Definition
registerEventListeners()
Code
378function Cylindered.registerEventListeners(vehicleType)
379 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Cylindered)
380 SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", Cylindered)
381 SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", Cylindered)
382 SpecializationUtil.registerEventListener(vehicleType, "onDelete", Cylindered)
383 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Cylindered)
384 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Cylindered)
385 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Cylindered)
386 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Cylindered)
387 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Cylindered)
388 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Cylindered)
389 SpecializationUtil.registerEventListener(vehicleType, "onUpdateEnd", Cylindered)
390 SpecializationUtil.registerEventListener(vehicleType, "onPostUpdate", Cylindered)
391 SpecializationUtil.registerEventListener(vehicleType, "onPostUpdateTick", Cylindered)
392 SpecializationUtil.registerEventListener(vehicleType, "onDraw", Cylindered)
393 SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", Cylindered)
394 SpecializationUtil.registerEventListener(vehicleType, "onPostAttach", Cylindered)
395 SpecializationUtil.registerEventListener(vehicleType, "onSelect", Cylindered)
396 SpecializationUtil.registerEventListener(vehicleType, "onUnselect", Cylindered)
397 SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", Cylindered)
398 SpecializationUtil.registerEventListener(vehicleType, "onAnimationPartChanged", Cylindered)
399 SpecializationUtil.registerEventListener(vehicleType, "onVehicleSettingChanged", Cylindered)
400end

registerEvents

Description
Definition
registerEvents()
Code
314function Cylindered.registerEvents(vehicleType)
315 SpecializationUtil.registerEvent(vehicleType, "onMovingToolChanged")
316end

registerFunctions

Description
Definition
registerFunctions()
Code
320function Cylindered.registerFunctions(vehicleType)
321 SpecializationUtil.registerFunction(vehicleType, "loadMovingPartFromXML", Cylindered.loadMovingPartFromXML)
322 SpecializationUtil.registerFunction(vehicleType, "loadMovingToolFromXML", Cylindered.loadMovingToolFromXML)
323 SpecializationUtil.registerFunction(vehicleType, "loadDependentMovingTools", Cylindered.loadDependentMovingTools)
324 SpecializationUtil.registerFunction(vehicleType, "loadEasyArmControlFromXML", Cylindered.loadEasyArmControlFromXML)
325 SpecializationUtil.registerFunction(vehicleType, "loadDependentParts", Cylindered.loadDependentParts)
326 SpecializationUtil.registerFunction(vehicleType, "resolveDependentPartData", Cylindered.resolveDependentPartData)
327 SpecializationUtil.registerFunction(vehicleType, "loadDependentComponentJoints", Cylindered.loadDependentComponentJoints)
328 SpecializationUtil.registerFunction(vehicleType, "loadDependentAttacherJoints", Cylindered.loadDependentAttacherJoints)
329 SpecializationUtil.registerFunction(vehicleType, "loadDependentWheels", Cylindered.loadDependentWheels)
330 SpecializationUtil.registerFunction(vehicleType, "loadDependentTranslatingParts", Cylindered.loadDependentTranslatingParts)
331 SpecializationUtil.registerFunction(vehicleType, "loadExtraDependentParts", Cylindered.loadExtraDependentParts)
332 SpecializationUtil.registerFunction(vehicleType, "loadDependentAnimations", Cylindered.loadDependentAnimations)
333 SpecializationUtil.registerFunction(vehicleType, "loadCopyLocalDirectionParts", Cylindered.loadCopyLocalDirectionParts)
334 SpecializationUtil.registerFunction(vehicleType, "loadRotationBasedLimits", Cylindered.loadRotationBasedLimits)
335 SpecializationUtil.registerFunction(vehicleType, "checkMovingPartDirtyUpdateNode", Cylindered.checkMovingPartDirtyUpdateNode)
336 SpecializationUtil.registerFunction(vehicleType, "updateDirtyMovingParts", Cylindered.updateDirtyMovingParts)
337 SpecializationUtil.registerFunction(vehicleType, "setMovingToolDirty", Cylindered.setMovingToolDirty)
338 SpecializationUtil.registerFunction(vehicleType, "updateCylinderedInitial", Cylindered.updateCylinderedInitial)
339 SpecializationUtil.registerFunction(vehicleType, "allowLoadMovingToolStates", Cylindered.allowLoadMovingToolStates)
340 SpecializationUtil.registerFunction(vehicleType, "getMovingToolByNode", Cylindered.getMovingToolByNode)
341 SpecializationUtil.registerFunction(vehicleType, "getMovingPartByNode", Cylindered.getMovingPartByNode)
342 SpecializationUtil.registerFunction(vehicleType, "getTranslatingPartByNode", Cylindered.getTranslatingPartByNode)
343 SpecializationUtil.registerFunction(vehicleType, "getIsMovingToolActive", Cylindered.getIsMovingToolActive)
344 SpecializationUtil.registerFunction(vehicleType, "getIsMovingPartActive", Cylindered.getIsMovingPartActive)
345 SpecializationUtil.registerFunction(vehicleType, "getMovingToolMoveValue", Cylindered.getMovingToolMoveValue)
346 SpecializationUtil.registerFunction(vehicleType, "setDelayedData", Cylindered.setDelayedData)
347 SpecializationUtil.registerFunction(vehicleType, "updateDelayedTool", Cylindered.updateDelayedTool)
348 SpecializationUtil.registerFunction(vehicleType, "updateEasyControl", Cylindered.updateEasyControl)
349 SpecializationUtil.registerFunction(vehicleType, "setIsEasyControlActive", Cylindered.setIsEasyControlActive)
350 SpecializationUtil.registerFunction(vehicleType, "updateExtraDependentParts", Cylindered.updateExtraDependentParts)
351 SpecializationUtil.registerFunction(vehicleType, "updateDependentAnimations", Cylindered.updateDependentAnimations)
352 SpecializationUtil.registerFunction(vehicleType, "updateDependentToolLimits", Cylindered.updateDependentToolLimits)
353 SpecializationUtil.registerFunction(vehicleType, "onMovingPartSoundEvent", Cylindered.onMovingPartSoundEvent)
354 SpecializationUtil.registerFunction(vehicleType, "updateMovingToolSoundEvents", Cylindered.updateMovingToolSoundEvents)
355end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
359function Cylindered.registerOverwrittenFunctions(vehicleType)
360 SpecializationUtil.registerOverwrittenFunction(vehicleType, "isDetachAllowed", Cylindered.isDetachAllowed)
361 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadObjectChangeValuesFromXML", Cylindered.loadObjectChangeValuesFromXML)
362 SpecializationUtil.registerOverwrittenFunction(vehicleType, "setObjectChangeValues", Cylindered.setObjectChangeValues)
363 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadDischargeNode", Cylindered.loadDischargeNode)
364 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDischargeNodeEmptyFactor", Cylindered.getDischargeNodeEmptyFactor)
365 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadShovelNode", Cylindered.loadShovelNode)
366 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getShovelNodeIsActive", Cylindered.getShovelNodeIsActive)
367 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadDynamicMountGrabFromXML", Cylindered.loadDynamicMountGrabFromXML)
368 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsDynamicMountGrabOpened", Cylindered.getIsDynamicMountGrabOpened)
369 SpecializationUtil.registerOverwrittenFunction(vehicleType, "setComponentJointFrame", Cylindered.setComponentJointFrame)
370 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAdditionalSchemaText", Cylindered.getAdditionalSchemaText)
371 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getWearMultiplier", Cylindered.getWearMultiplier)
372 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDoConsumePtoPower", Cylindered.getDoConsumePtoPower)
373 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getConsumingLoad", Cylindered.getConsumingLoad)
374end

resolveDependentPartData

Description
Resolve loaded dependent part data into real moving parts and tools
Definition
resolveDependentPartData(table dependentPartData, table referenceNodes)
Arguments
tabledependentPartDatadependentPartData
tablereferenceNodesreferenceNodes
Code
2493function Cylindered:resolveDependentPartData(dependentPartData, referenceNodes)
2494 for _, dependentPart in pairs(dependentPartData) do
2495 if dependentPart.part == nil then
2496 if referenceNodes[dependentPart.node] ~= nil then
2497 for j=1, #referenceNodes[dependentPart.node] do
2498 local depPart = referenceNodes[dependentPart.node][j]
2499 if j == 1 then
2500 dependentPart.part = depPart
2501 depPart.isDependentPart = true
2502 else
2503 table.insert(dependentPartData, {
2504 node = dependentPart.node,
2505 maxUpdateDistance = dependentPart.maxUpdateDistance,
2506 part = depPart,
2507 })
2508
2509 depPart.isDependentPart = true
2510 end
2511 end
2512 end
2513 end
2514 end
2515
2516 for j=#dependentPartData, 1, -1 do
2517 local data = dependentPartData[j]
2518 if data.part == nil then
2519 table.remove(dependentPartData, j)
2520 end
2521 end
2522end

saveToXMLFile

Description
Definition
saveToXMLFile()
Code
918function Cylindered:saveToXMLFile(xmlFile, key, usedModNames)
919 local spec = self.spec_cylindered
920
921 local index = 0
922 for _, tool in ipairs(spec.movingTools) do
923 if tool.saving then
924 local toolKey = string.format("%s.movingTool(%d)", key, index)
925
926 if tool.transSpeed ~= nil then
927 xmlFile:setValue(toolKey.."#translation", tool.curTrans[tool.translationAxis])
928 end
929 if tool.rotSpeed ~= nil then
930 xmlFile:setValue(toolKey.."#rotation", tool.curRot[tool.rotationAxis])
931 end
932 if tool.animSpeed ~= nil then
933 xmlFile:setValue(toolKey.."#animationTime", tool.curAnimTime)
934 end
935
936 index = index + 1
937 end
938 end
939end

setAbsoluteToolRotation

Description
Set absolute tool rotation
Definition
setAbsoluteToolRotation(table tool, float rotation, bool updateDelayedNodes)
Arguments
tabletooltool
floatrotationrotation
boolupdateDelayedNodesupdate delayed nodes
Code
3450function Cylindered.setAbsoluteToolRotation(self, tool, rotation, updateDelayedNodes)
3451 tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node)
3452 local oldRot = tool.curRot[tool.rotationAxis]
3453 if Cylindered.setToolRotation(self, tool, nil, 0, rotation - oldRot) then
3454 Cylindered.setDirty(self, tool)
3455
3456 if updateDelayedNodes ~= nil and updateDelayedNodes then
3457 self:updateDelayedTool(tool)
3458 end
3459
3460 self:raiseDirtyFlags(tool.dirtyFlag)
3461 self:raiseDirtyFlags(self.spec_cylindered.cylinderedDirtyFlag)
3462 end
3463end

setAbsoluteToolTranslation

Description
Set absolute tool translation
Definition
setAbsoluteToolTranslation(table tool, float translation)
Arguments
tabletooltool
floattranslationtranslation
Code
3382function Cylindered.setAbsoluteToolTranslation(self, tool, translation)
3383 tool.curTrans[1], tool.curTrans[2], tool.curTrans[3] = getTranslation(tool.node)
3384 local oldTrans = tool.curTrans[tool.translationAxis]
3385 if Cylindered.setToolTranslation(self, tool, nil, 0, translation - oldTrans) then
3386 Cylindered.setDirty(self, tool)
3387
3388 self:raiseDirtyFlags(tool.dirtyFlag)
3389 self:raiseDirtyFlags(self.spec_cylindered.cylinderedDirtyFlag)
3390 end
3391end

setComponentJointFrame

Description
Definition
setComponentJointFrame()
Code
3154function Cylindered:setComponentJointFrame(superFunc, jointDesc, anchorActor)
3155 superFunc(self, jointDesc, anchorActor)
3156
3157 -- update the joint to component offset
3158 local spec = self.spec_cylindered
3159 for _, movingTool in ipairs(spec.movingTools) do
3160 for _, componentJoint in ipairs(movingTool.componentJoints) do
3161 local componentJointDesc = self.componentJoints[componentJoint.index]
3162
3163 local jointNode = componentJointDesc.jointNode
3164 if componentJoint.anchorActor == 1 then
3165 jointNode = componentJointDesc.jointNodeActor1
3166 end
3167
3168 local node = self.components[componentJointDesc.componentIndices[2]].node
3169 componentJoint.x, componentJoint.y, componentJoint.z = localToLocal(node, jointNode, 0,0,0)
3170 componentJoint.upX, componentJoint.upY, componentJoint.upZ = localDirectionToLocal(node, jointNode, 0,1,0)
3171 componentJoint.dirX, componentJoint.dirY, componentJoint.dirZ = localDirectionToLocal(node, jointNode, 0,0,1)
3172 end
3173 end
3174end

setDelayedData

Description
Definition
setDelayedData()
Code
1345function Cylindered:setDelayedData(tool, immediate)
1346 local x, y, z = getTranslation(tool.node)
1347 local rx, ry, rz = getRotation(tool.node)
1348
1349 tool.delayedHistroyData[tool.delayedFrames] = {rot = {rx, ry, rz}, trans = {x, y, z}}
1350 if immediate then
1351 for i=1, tool.delayedFrames - 1 do
1352 tool.delayedHistroyData[i] = tool.delayedHistroyData[tool.delayedFrames]
1353 end
1354 end
1355
1356 tool.delayedHistoryIndex = tool.delayedFrames
1357end

setDirty

Description
Set dirty
Definition
setDirty(table part)
Arguments
tablepartpart to set dirty
Code
3520function Cylindered.setDirty(self, part)
3521 if not part.isDirty or self.spec_cylindered.isLoading then -- during loading we always allow setting dirty since we do not reset it
3522 part.isDirty = true
3523 self.anyMovingPartsDirty = true
3524
3525 if part.delayedNode ~= nil then
3526 self:setDelayedData(part)
3527 end
3528
3529 -- on moving tools we update the wheels and attacher joints on dirty since the are updated from external influences (e.g. animations)
3530 -- on moving parts the part is updated first and then the wheels and attacher joints are updated
3531 if part.isTool then
3532 Cylindered.updateAttacherJoints(self, part)
3533 Cylindered.updateWheels(self, part)
3534 end
3535
3536 for _, data in pairs(part.dependentPartData) do
3537 if self.currentUpdateDistance < data.maxUpdateDistance then
3538 Cylindered.setDirty(self, data.part)
3539 end
3540 end
3541 end
3542end

setIsEasyControlActive

Description
Definition
setIsEasyControlActive()
Code
1711function Cylindered:setIsEasyControlActive(state)
1712 local spec = self.spec_cylindered
1713 if self.isServer then
1714 local easyArmControl = spec.easyArmControl
1715 if easyArmControl ~= nil then
1716 local targetYTool = self:getMovingToolByNode(easyArmControl.targetNodeY)
1717 local targetZTool = self:getMovingToolByNode(easyArmControl.targetNodeZ)
1718
1719 if state then
1720 local origin = getParent(easyArmControl.targetNodeY)
1721 if origin == easyArmControl.targetNodeZ then
1722 origin = getParent(easyArmControl.targetNodeZ)
1723 end
1724
1725 local _, y, _ = localToLocal(easyArmControl.targetRefNode, origin, 0, 0, 0)
1726 local _, oldY, _ = getTranslation(easyArmControl.targetNodeY)
1727 if Cylindered.setToolTranslation(self, targetYTool, nil, 0, y-oldY) then
1728 Cylindered.setDirty(self, targetYTool)
1729 end
1730
1731 local z
1732 _, _, z = localToLocal(easyArmControl.targetRefNode, origin, 0, 0, 0)
1733 local _, _, oldZ = getTranslation(easyArmControl.targetNodeZ)
1734 if Cylindered.setToolTranslation(self, targetZTool, nil, 0, z-oldZ) then
1735 Cylindered.setDirty(self, targetZTool)
1736 end
1737
1738 easyArmControl.lastValidPositionY[1], easyArmControl.lastValidPositionY[2], easyArmControl.lastValidPositionY[3] = getTranslation(easyArmControl.targetNodeY)
1739 easyArmControl.lastValidPositionZ[1], easyArmControl.lastValidPositionZ[2], easyArmControl.lastValidPositionZ[3] = getTranslation(easyArmControl.targetNodeZ)
1740
1741 self:raiseDirtyFlags(spec.cylinderedDirtyFlag)
1742 end
1743
1744 easyArmControl.state = state
1745 end
1746 end
1747
1748 self:requestActionEventUpdate()
1749end

setMovingToolDirty

Description
Set moving tool dirty
Definition
setMovingToolDirty(integer node, bool forceUpdate, float dt)
Arguments
integernodenode id
boolforceUpdateforce immediate update of moving tool and dependent parts
floatdttime since last call (only if forceUpdate is set)
Code
2797function Cylindered:setMovingToolDirty(node, forceUpdate, dt)
2798 local spec = self.spec_cylindered
2799
2800 local tool = spec.nodesToMovingTools[node]
2801 if tool ~= nil then
2802 -- update curTrans and curRot values + moving tool action sounds
2803 if tool.transSpeed ~= nil then
2804 local oldTrans = tool.curTrans[tool.translationAxis]
2805 tool.curTrans[1], tool.curTrans[2], tool.curTrans[3] = getTranslation(tool.node)
2806 local newTrans = tool.curTrans[tool.translationAxis]
2807
2808 local diff = newTrans - oldTrans
2809 if math.abs(diff) > 0.0001 then
2810 self:updateMovingToolSoundEvents(tool, diff > 0, math.abs(newTrans-(tool.transMax or math.huge)) < 0.0001 or math.abs(newTrans-(tool.transMin or math.huge)) < 0.0001, math.abs(oldTrans-(tool.transMax or math.huge)) < 0.0001 or math.abs(oldTrans-(tool.transMin or math.huge)) < 0.0001)
2811 end
2812 end
2813
2814 if tool.rotSpeed ~= nil then
2815 local oldRot = tool.curRot[tool.rotationAxis]
2816 tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node)
2817 local newRot = tool.curRot[tool.rotationAxis]
2818
2819 local diff = newRot - oldRot
2820 if math.abs(diff) > 0.0001 then
2821 self:updateMovingToolSoundEvents(tool, diff > 0, math.abs(newRot-(tool.rotMax or math.huge)) < 0.0001 or math.abs(newRot-(tool.rotMin or math.huge)) < 0.0001, math.abs(oldRot-(tool.rotMax or math.huge)) < 0.0001 or math.abs(oldRot-(tool.rotMin or math.huge)) < 0.0001)
2822 end
2823 end
2824
2825 Cylindered.setDirty(self, tool)
2826
2827 if not self.isServer and self.isClient then
2828 tool.networkInterpolators.translation:setValue(tool.curTrans[tool.translationAxis])
2829 tool.networkInterpolators.rotation:setAngle(tool.curRot[tool.rotationAxis])
2830 end
2831
2832 if forceUpdate or (self.finishedFirstUpdate and not self.isActive) then
2833 self:updateDirtyMovingParts(dt or g_currentDt, true)
2834 end
2835 end
2836end

setObjectChangeValues

Description
Sets object change values
Definition
setObjectChangeValues(table object, boolean isActive)
Arguments
tableobjectobject
booleanisActiveis active
Code
3006function Cylindered:setObjectChangeValues(superFunc, object, isActive)
3007 superFunc(self, object, isActive)
3008
3009 local spec = self.spec_cylindered
3010
3011 if spec.nodesToMovingTools ~= nil and spec.nodesToMovingTools[object.node] ~= nil then
3012 local movingTool = spec.nodesToMovingTools[object.node]
3013 if isActive then
3014 movingTool.rotMax = object.movingToolRotMaxActive
3015 movingTool.rotMin = object.movingToolRotMinActive
3016 movingTool.transMax = object.movingToolTransMaxActive
3017 movingTool.transMin = object.movingToolTransMinActive
3018 else
3019 movingTool.rotMax = object.movingToolRotMaxInactive
3020 movingTool.rotMin = object.movingToolRotMinInactive
3021 movingTool.transMax = object.movingToolTransMaxInactive
3022 movingTool.transMin = object.movingToolTransMinInactive
3023 end
3024 end
3025end

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
3471function Cylindered.setToolAnimation(self, tool, animSpeed, dt)
3472 tool.curAnimTime = self:getAnimationTime(tool.animName)
3473
3474 local newAnimTime = tool.curAnimTime+animSpeed*dt
3475 local oldAnimTime = tool.curAnimTime
3476
3477 if tool.animMaxTime ~= nil then
3478 newAnimTime = math.min(newAnimTime, tool.animMaxTime)
3479 end
3480 if tool.animMinTime ~= nil then
3481 newAnimTime = math.max(newAnimTime, tool.animMinTime)
3482 end
3483 local diff = newAnimTime - tool.curAnimTime
3484 if dt ~= 0 then
3485 tool.lastAnimSpeed = diff / dt
3486 end
3487 if math.abs(diff) > 0.0001 then
3488 tool.curAnimTime = newAnimTime
3489 self:setAnimationTime(tool.animName, newAnimTime, nil, true)
3490
3491 self:updateMovingToolSoundEvents(tool, diff > 0, newAnimTime == tool.animMaxTime or newAnimTime == tool.animMinTime or newAnimTime == 0 or newAnimTime == 1, oldAnimTime == tool.animMaxTime or oldAnimTime == tool.animMinTime or oldAnimTime == 0 or oldAnimTime == 1)
3492
3493 SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, animSpeed, dt)
3494 return true
3495 end
3496
3497 return false
3498end

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
3400function Cylindered.setToolRotation(self, tool, rotSpeed, dt, delta)
3401 tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node)
3402 local newRot = tool.curRot[tool.rotationAxis]
3403 local oldRot = newRot
3404 if rotSpeed ~= nil then
3405 newRot = newRot + rotSpeed*dt
3406 else
3407 newRot = newRot + delta
3408 end
3409 if tool.rotMax ~= nil then
3410 newRot = math.min(newRot, tool.rotMax)
3411 end
3412 if tool.rotMin ~= nil then
3413 newRot = math.max(newRot, tool.rotMin)
3414 end
3415 local diff = newRot - tool.curRot[tool.rotationAxis]
3416 if rotSpeed ~= nil then
3417 if dt ~= 0 then
3418 tool.lastRotSpeed = diff/dt
3419 end
3420 end
3421
3422 if math.abs(diff) > 0.0001 then
3423 -- wrap if not limited
3424 if tool.rotMin == nil and tool.rotMax == nil then
3425 if newRot > 2*math.pi then
3426 newRot = newRot - 2*math.pi
3427 end
3428 if newRot < 0 then
3429 newRot = newRot + 2*math.pi
3430 end
3431 end
3432 tool.curRot[tool.rotationAxis] = newRot
3433 setRotation(tool.node, tool.curRot[1], tool.curRot[2], tool.curRot[3])
3434
3435 self:updateMovingToolSoundEvents(tool, diff > 0, newRot == tool.rotMax or newRot == tool.rotMin, oldRot == tool.rotMax or oldRot == tool.rotMin)
3436
3437 SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, rotSpeed, dt)
3438
3439 return true
3440 end
3441
3442 return false
3443end

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
3345function Cylindered.setToolTranslation(self, tool, transSpeed, dt, delta)
3346 tool.curTrans[1], tool.curTrans[2], tool.curTrans[3] = getTranslation(tool.node)
3347 local newTrans = tool.curTrans[tool.translationAxis]
3348 local oldTrans = newTrans
3349 if transSpeed ~= nil then
3350 newTrans = newTrans + transSpeed*dt
3351 else
3352 newTrans = newTrans + delta
3353 end
3354 if tool.transMax ~= nil then
3355 newTrans = math.min(newTrans, tool.transMax)
3356 end
3357 if tool.transMin ~= nil then
3358 newTrans = math.max(newTrans, tool.transMin)
3359 end
3360 local diff = newTrans - oldTrans
3361 if dt ~= 0 then
3362 tool.lastTransSpeed = diff/dt
3363 end
3364 if math.abs(diff) > 0.0001 then
3365 tool.curTrans[tool.translationAxis] = newTrans
3366 setTranslation(tool.node, tool.curTrans[1], tool.curTrans[2], tool.curTrans[3])
3367
3368 self:updateMovingToolSoundEvents(tool, diff > 0, newTrans == tool.transMax or newTrans == tool.transMin, oldTrans == tool.transMax or oldTrans == tool.transMin)
3369
3370 SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, transSpeed, dt)
3371
3372 return true
3373 end
3374
3375 return false
3376end

updateAttacherJoints

Description
Update attacher joints
Definition
updateAttacherJoints(table entry)
Arguments
tableentryentry
Code
3998function Cylindered.updateAttacherJoints(self, entry)
3999 if self.isServer then
4000 if entry.attacherJoints ~= nil then
4001 for _,joint in ipairs(entry.attacherJoints) do
4002 if joint.jointIndex ~= 0 then
4003 setJointFrame(joint.jointIndex, 0, joint.jointTransform)
4004 end
4005 end
4006 end
4007
4008 if entry.inputAttacherJoint then
4009 if self.getAttacherVehicle ~= nil then
4010 local attacherVehicle = self:getAttacherVehicle()
4011 if attacherVehicle ~= nil then
4012
4013 local attacherJoints = attacherVehicle:getAttacherJoints()
4014 if attacherJoints ~= nil then
4015
4016 local jointDescIndex = attacherVehicle:getAttacherJointIndexFromObject(self)
4017 if jointDescIndex ~= nil then
4018 local jointDesc = attacherJoints[jointDescIndex]
4019
4020 local inputAttacherJoint = self:getActiveInputAttacherJoint()
4021 if inputAttacherJoint ~= nil then
4022 local xNew = jointDesc.jointOrigTrans[1] + jointDesc.jointPositionOffset[1]
4023 local yNew = jointDesc.jointOrigTrans[2] + jointDesc.jointPositionOffset[2]
4024 local zNew = jointDesc.jointOrigTrans[3] + jointDesc.jointPositionOffset[3]
4025
4026 -- transform offset position to world coord and to jointTransform coord to get position offset dependend on angle and position
4027 local ox, oy, oz = getTranslation(jointDesc.jointTransform)
4028 setTranslation(jointDesc.jointTransform, unpack(jointDesc.jointOrigTrans))
4029 local x, y, z = localToWorld(getParent(jointDesc.jointTransform), xNew, yNew, zNew)
4030 local x1, y1, z1 = worldToLocal(jointDesc.jointTransform, x, y, z)
4031 setTranslation(jointDesc.jointTransform, ox, oy, oz)
4032
4033 -- transform it to implement position and angle
4034 x, y, z = localToWorld(inputAttacherJoint.node, x1, y1, z1)
4035 local x2, y2, z2 = worldToLocal(getParent(inputAttacherJoint.node), x, y, z)
4036 setTranslation(inputAttacherJoint.node, x2, y2, z2)
4037
4038 setJointFrame(jointDesc.jointIndex, 1, inputAttacherJoint.node)
4039
4040 setTranslation(inputAttacherJoint.node, unpack(inputAttacherJoint.jointOrigTrans))
4041 end
4042 end
4043 end
4044 end
4045 end
4046 end
4047 end
4048end

updateComponentJoints

Description
Update component joints
Definition
updateComponentJoints(table entry, boolean placeComponents)
Arguments
tableentryentry
booleanplaceComponentsplace components
Code
3969function Cylindered.updateComponentJoints(self, entry, placeComponents)
3970 if self.isServer then
3971 if entry.componentJoints ~= nil then
3972 for _,joint in ipairs(entry.componentJoints) do
3973 local componentJoint = joint.componentJoint
3974
3975 local jointNode = componentJoint.jointNode
3976 if joint.anchorActor == 1 then
3977 jointNode = componentJoint.jointNodeActor1
3978 end
3979
3980 if placeComponents then
3981 local node = self.components[componentJoint.componentIndices[2]].node
3982 local x,y,z = localToWorld(jointNode, joint.x, joint.y, joint.z)
3983 local upX,upY,upZ = localDirectionToWorld(jointNode, joint.upX,joint.upY,joint.upZ)
3984 local dirX,dirY,dirZ = localDirectionToWorld(jointNode, joint.dirX,joint.dirY,joint.dirZ)
3985 setWorldTranslation(node, x,y,z)
3986 I3DUtil.setWorldDirection(node, dirX,dirY,dirZ, upX,upY,upZ)
3987 end
3988
3989 self:setComponentJointFrame(componentJoint, joint.anchorActor)
3990 end
3991 end
3992 end
3993end

updateCylinderedInitial

Description
Initial update of cylindered
Definition
updateCylinderedInitial(boolean placeComponents)
Arguments
booleanplaceComponentsplace components
Code
2841function Cylindered:updateCylinderedInitial(placeComponents, keepDirty)
2842 if placeComponents == nil then
2843 placeComponents = true
2844 end
2845
2846 if keepDirty == nil then
2847 keepDirty = false
2848 end
2849
2850 local spec = self.spec_cylindered
2851
2852 for _, part in pairs(spec.activeDirtyMovingParts) do
2853 Cylindered.setDirty(self, part)
2854 end
2855
2856 for _, tool in ipairs(spec.movingTools) do
2857 if tool.isDirty then
2858 Cylindered.updateWheels(self, tool)
2859 if self.isServer then
2860 Cylindered.updateComponentJoints(self, tool, placeComponents)
2861 end
2862 tool.isDirty = false or keepDirty
2863 end
2864
2865 self:updateExtraDependentParts(tool, 9999)
2866 self:updateDependentAnimations(tool, 9999)
2867 end
2868
2869 for _, part in ipairs(spec.movingParts) do
2870 local isActive = self:getIsMovingPartActive(part)
2871 if isActive or part.smoothedDirectionScale and part.smoothedDirectionScaleAlpha ~= 0 then
2872 if part.isDirty then
2873 Cylindered.updateMovingPart(self, part, placeComponents, nil, isActive, false)
2874 Cylindered.updateWheels(self, part)
2875 part.isDirty = false or keepDirty
2876 end
2877
2878 self:updateExtraDependentParts(part, 9999)
2879 self:updateDependentAnimations(part, 9999)
2880 end
2881 end
2882end

updateDelayedTool

Description
Definition
updateDelayedTool()
Code
1361function Cylindered:updateDelayedTool(tool, forceLastPosition)
1362 local spec = self.spec_cylindered
1363
1364 if forceLastPosition ~= nil and forceLastPosition then
1365 for i=1, tool.delayedFrames - 1 do
1366 tool.delayedHistroyData[i] = tool.delayedHistroyData[tool.delayedFrames]
1367 end
1368 end
1369
1370 local currentData = tool.delayedHistroyData[1]
1371 for i=1, tool.delayedFrames - 1 do
1372 tool.delayedHistroyData[i] = tool.delayedHistroyData[i + 1]
1373 end
1374
1375 setRotation(tool.delayedNode, unpack(currentData.rot))
1376 setTranslation(tool.delayedNode, unpack(currentData.trans))
1377
1378-- local r, _, _ = getRotation(tool.node)
1379-- log(string.format("%s: %.2f | %s: %.2f", getName(tool.node), math.deg(r), getName(tool.delayedNode), math.deg(currentData.rot[1])))
1380
1381 tool.delayedHistoryIndex = tool.delayedHistoryIndex - 1
1382
1383 local movingPart = spec.nodesToMovingParts[tool.delayedNode]
1384 local movingTool = spec.nodesToMovingTools[tool.delayedNode]
1385 if movingPart ~= nil then
1386 Cylindered.setDirty(self, movingPart)
1387 end
1388 if spec.nodesToMovingTools[tool.delayedNode] ~= nil then
1389 Cylindered.setDirty(self, movingTool)
1390 end
1391end

updateDependentAnimations

Description
Definition
updateDependentAnimations()
Code
1758function Cylindered:updateDependentAnimations(part, dt)
1759 if #part.dependentAnimations > 0 then
1760 for _, dependentAnimation in ipairs(part.dependentAnimations) do
1761 local pos = 0
1762 if dependentAnimation.translationAxis ~= nil then
1763 local retValues = {getTranslation(dependentAnimation.node)}
1764 pos = (retValues[dependentAnimation.translationAxis] - dependentAnimation.minValue) / (dependentAnimation.maxValue - dependentAnimation.minValue)
1765 end
1766
1767 if dependentAnimation.rotationAxis ~= nil then
1768 local retValues = {getRotation(dependentAnimation.node)}
1769 pos = (retValues[dependentAnimation.rotationAxis] - dependentAnimation.minValue) / (dependentAnimation.maxValue - dependentAnimation.minValue)
1770 end
1771
1772 pos = MathUtil.clamp(pos, 0, 1)
1773 if dependentAnimation.invert then
1774 pos = 1-pos
1775 end
1776 dependentAnimation.lastPos = pos
1777
1778 self:setAnimationTime(dependentAnimation.name, pos, true)
1779 end
1780 end
1781end

updateDependentToolLimits

Description
Definition
updateDependentToolLimits()
Code
1785function Cylindered:updateDependentToolLimits(tool, dependentTool)
1786 if dependentTool.minTransLimits ~= nil or dependentTool.maxTransLimits ~= nil then
1787 local state = Cylindered.getMovingToolState(self, tool)
1788 if dependentTool.minTransLimits ~= nil then
1789 dependentTool.movingTool.transMin = MathUtil.lerp(dependentTool.minTransLimits[1], dependentTool.minTransLimits[2], 1-state)
1790 end
1791 if dependentTool.maxTransLimits ~= nil then
1792 dependentTool.movingTool.transMax = MathUtil.lerp(dependentTool.maxTransLimits[1], dependentTool.maxTransLimits[2], 1-state)
1793 end
1794 local transLimitChanged = Cylindered.setToolTranslation(self, dependentTool.movingTool, 0, 0)
1795 if transLimitChanged then
1796 Cylindered.setDirty(self, dependentTool.movingTool)
1797 end
1798 end
1799
1800 if dependentTool.minRotLimits ~= nil or dependentTool.maxRotLimits ~= nil then
1801 local state = Cylindered.getMovingToolState(self, tool)
1802 if dependentTool.minRotLimits ~= nil then
1803 dependentTool.movingTool.rotMin = MathUtil.lerp(dependentTool.minRotLimits[1], dependentTool.minRotLimits[2], 1-state)
1804 end
1805 if dependentTool.maxRotLimits ~= nil then
1806 dependentTool.movingTool.rotMax = MathUtil.lerp(dependentTool.maxRotLimits[1], dependentTool.maxRotLimits[2], 1-state)
1807 end
1808
1809 dependentTool.movingTool.networkInterpolators.rotation:setMinMax(dependentTool.movingTool.rotMin, dependentTool.movingTool.rotMax)
1810
1811 local rotLimitChanged = Cylindered.setToolRotation(self, dependentTool.movingTool, 0, 0)
1812 if rotLimitChanged then
1813 Cylindered.setDirty(self, dependentTool.movingTool)
1814 end
1815 end
1816end

updateDirtyMovingParts

Description
Definition
updateDirtyMovingParts()
Code
2006function Cylindered:updateDirtyMovingParts(dt, updateSound)
2007 local spec = self.spec_cylindered
2008
2009 for _, tool in pairs(spec.movingTools) do
2010 if tool.isDirty then
2011 if tool.playSound then
2012 spec.movingToolNeedsSound = true
2013 end
2014 if self.isServer then
2015 -- update component joint
2016 Cylindered.updateComponentJoints(self, tool, false)
2017 end
2018 self:updateExtraDependentParts(tool, dt)
2019 self:updateDependentAnimations(tool, dt)
2020 tool.isDirty = false
2021 end
2022 end
2023
2024 if self.anyMovingPartsDirty then
2025 for i, part in ipairs(spec.movingParts) do
2026 if part.isDirty then
2027 local isActive = self:getIsMovingPartActive(part)
2028 if isActive or part.smoothedDirectionScale and part.smoothedDirectionScaleAlpha ~= 0 then
2029 Cylindered.updateMovingPart(self, part, false, nil, isActive)
2030 self:updateExtraDependentParts(part, dt)
2031 self:updateDependentAnimations(part, dt)
2032 if part.playSound then
2033 spec.cylinderedHydraulicSoundPartNumber = i
2034 spec.movingPartNeedsSound = true
2035 end
2036 end
2037 else
2038 if spec.isClient and spec.cylinderedHydraulicSoundPartNumber == i then
2039 spec.movingPartNeedsSound = false
2040 end
2041 end
2042 end
2043 self.anyMovingPartsDirty = false
2044 end
2045
2046 if updateSound then
2047 if self.isClient then
2048 if spec.movingToolNeedsSound or spec.movingPartNeedsSound then
2049 if not spec.isHydraulicSamplePlaying then
2050 g_soundManager:playSample(spec.samples.hydraulic)
2051 spec.isHydraulicSamplePlaying = true
2052 end
2053 self:raiseActive()
2054 else
2055 if spec.isHydraulicSamplePlaying then
2056 g_soundManager:stopSample(spec.samples.hydraulic)
2057 spec.isHydraulicSamplePlaying = false
2058 end
2059 end
2060 end
2061 end
2062end

updateEasyControl

Description
Definition
updateEasyControl()
Code
1533function Cylindered:updateEasyControl(dt, updateDelayedNodes)
1534 local spec = self.spec_cylindered
1535 local easyArmControl = spec.easyArmControl
1536 if easyArmControl ~= nil then
1537 local targetYTool = self:getMovingToolByNode(easyArmControl.targetNodeY)
1538 local targetZTool = self:getMovingToolByNode(easyArmControl.targetNodeZ)
1539
1540 local hasChanged = false
1541 if targetYTool.move ~= 0 or targetZTool.move ~= 0 then
1542 hasChanged = true
1543
1544 if (targetYTool.move ~= 0 and targetYTool.isConsumingPower) or (targetZTool.move ~= 0 and targetZTool.isConsumingPower) then
1545 spec.powerConsumingTimer = spec.powerConsumingActiveTimeOffset
1546 end
1547 end
1548
1549 if self.isServer and easyArmControl.state and hasChanged then
1550 local transSpeedY = targetYTool.move * easyArmControl.moveSpeed
1551 if easyArmControl.moveAcceleration ~= nil and math.abs(transSpeedY - easyArmControl.lastSpeedY) >= easyArmControl.moveAcceleration * dt then
1552 if transSpeedY > easyArmControl.lastSpeedY then
1553 transSpeedY = easyArmControl.lastSpeedY + easyArmControl.moveAcceleration * dt
1554 else
1555 transSpeedY = easyArmControl.lastSpeedY - easyArmControl.moveAcceleration * dt
1556 end
1557 end
1558
1559 local transSpeedZ = targetZTool.move * easyArmControl.moveSpeed
1560 if easyArmControl.moveAcceleration ~= nil and math.abs(transSpeedZ - easyArmControl.lastSpeedZ) >= easyArmControl.moveAcceleration * dt then
1561 if transSpeedZ > easyArmControl.lastSpeedZ then
1562 transSpeedZ = easyArmControl.lastSpeedZ + easyArmControl.moveAcceleration * dt
1563 else
1564 transSpeedZ = easyArmControl.lastSpeedZ - easyArmControl.moveAcceleration * dt
1565 end
1566 end
1567
1568 easyArmControl.lastSpeedY = transSpeedY
1569 local moveY = transSpeedY * dt
1570
1571 easyArmControl.lastSpeedZ = transSpeedZ
1572 local moveZ = transSpeedZ * dt
1573
1574 -- target world position
1575 local worldTargetDirX, worldTargetDirY, worldTargetDirZ = localDirectionToWorld(easyArmControl.rootNode, 0, moveY, moveZ)
1576 local worldTargetX, worldTargetY, worldTargetZ = getWorldTranslation(easyArmControl.targetRefNode)
1577 worldTargetX, worldTargetY, worldTargetZ = worldTargetX + worldTargetDirX, worldTargetY + worldTargetDirY, worldTargetZ + worldTargetDirZ
1578
1579 -- limit target position to max distance radius
1580 local locTargetX, locTargetY, locTargetZ = worldToLocal(easyArmControl.rootNode, worldTargetX, worldTargetY, worldTargetZ)
1581 local distanceToTarget = MathUtil.vector3Length(locTargetX, locTargetY, locTargetZ)
1582
1583 local targetExceedFactor = easyArmControl.maxTotalDistance / distanceToTarget
1584 if targetExceedFactor < 1 then
1585 locTargetX, locTargetY, locTargetZ = locTargetX * targetExceedFactor, locTargetY * targetExceedFactor, locTargetZ * targetExceedFactor
1586 worldTargetX, worldTargetY, worldTargetZ = localToWorld(easyArmControl.rootNode, locTargetX, locTargetY, locTargetZ)
1587
1588 distanceToTarget = easyArmControl.maxTotalDistance
1589 end
1590
1591 -- distance from arm1 to arm2 and arm2 to end node
1592 local circleDistance1 = MathUtil.vector3Length(localToLocal(easyArmControl.xRotationNodes[2].node, easyArmControl.xRotationNodes[1].node, 0, 0, 0))
1593 local _, _, circleDistance2 = localToLocal(easyArmControl.targetRefNode, easyArmControl.xRotationNodes[2].node, 0, 0, 0)
1594
1595 -- circle center positions
1596 local circle1X, circle1Y, circle1Z = localToLocal(easyArmControl.xRotationNodes[1].node, easyArmControl.rootNode, 0, 0, 0)
1597 local circle2X, circle2Y, circle2Z = worldToLocal(easyArmControl.rootNode, worldTargetX, worldTargetY, worldTargetZ)
1598
1599 --#debug local c1xw, c1yw, c1zw = localToWorld(easyArmControl.rootNode, circle1X, circle1Y, circle1Z)
1600 --#debug DebugUtil.drawDebugGizmoAtWorldPos(c1xw, c1yw, c1zw, 0, 0, 1, 0, 1, 0, "c1", false)
1601 --#debug local c2xw, c2yw, c2zw = localToWorld(easyArmControl.rootNode, circle2X, circle2Y, circle2Z)
1602 --#debug DebugUtil.drawDebugGizmoAtWorldPos(c2xw, c2yw, c2zw, 0, 0, 1, 0, 1, 0, "c2", false)
1603
1604 --#debug DebugUtil.drawDebugGizmoAtWorldPos(c2xw, c2yw - 0.5, c2zw, 0, 0, 1, 0, 1, 0, string.format("move: %.2f %.2f", moveY / dt * 1000, moveZ / dt * 1000), false)
1605
1606 local numZTranslationNodes = #easyArmControl.zTranslationNodes
1607 if numZTranslationNodes > 0 then
1608 -- get angle between input direction and translatio node direction
1609 local inputDirY, inputDirZ = MathUtil.vector2Normalize(math.abs(moveY), math.abs(moveZ))
1610 if moveY == 0 and moveZ == 0 then
1611 inputDirY, inputDirZ = 0, 0
1612 end
1613 local transDirX, transDirY, transDirZ = localDirectionToWorld(easyArmControl.zTranslationNodes[1].node, 0, 0, 1)
1614 transDirX, transDirY, transDirZ = worldDirectionToLocal(easyArmControl.rootNode, transDirX, transDirY, transDirZ)
1615
1616 local difference = math.acos(MathUtil.dotProduct(0, inputDirY, inputDirZ, 0, transDirY, transDirZ))
1617 if difference > (math.pi * 0.5) then
1618 difference = -difference + math.pi
1619 end
1620
1621 -- move translation parts
1622 local rotTransRatio = 1 - (difference / (math.pi * 0.5)) -- 1: only trans / 0: only rot
1623 rotTransRatio = (rotTransRatio - easyArmControl.transMoveRatioMinDir) / (easyArmControl.transMoveRatioMaxDir - easyArmControl.transMoveRatioMinDir)
1624 rotTransRatio = MathUtil.clamp(rotTransRatio, easyArmControl.minTransMoveRatio, easyArmControl.maxTransMoveRatio)
1625
1626 local _, _, targetZOffset = worldToLocal(easyArmControl.xRotationNodes[2].node, worldTargetX, worldTargetY, worldTargetZ)
1627 local zDifference = targetZOffset - circleDistance2
1628
1629 local minTransPct = 0
1630 if not easyArmControl.allowNegativeTrans then
1631 if transDirZ < 0 then
1632 -- move the translation parts to the min position
1633 if zDifference > 0 then
1634 rotTransRatio = -2
1635 end
1636
1637 minTransPct = easyArmControl.minNegativeTrans * -transDirZ
1638 end
1639 end
1640
1641 local transMove = zDifference * rotTransRatio
1642
1643 --#debug DebugUtil.drawDebugGizmoAtWorldPos(c2xw, c2yw - 1, c2zw, 0, 0, 1, 0, 1, 0, string.format("transDirZ %.2f\n rotTransRatio: %.2f\n transMove: %.3f\n zDifference: %.3f\n", transDirZ, rotTransRatio, transMove / dt * 1000, zDifference / dt * 1000), false)
1644
1645 for i=1, numZTranslationNodes do
1646 local zTranslationNode = easyArmControl.zTranslationNodes[i]
1647 local movingTool = zTranslationNode.movingTool
1648
1649 local delta = transMove / numZTranslationNodes
1650 local currentTrans = movingTool.curTrans[movingTool.translationAxis]
1651 local transMin = (movingTool.transMax - movingTool.transMin) * minTransPct + movingTool.transMin
1652 local newTrans = MathUtil.clamp(currentTrans + delta, transMin, movingTool.transMax)
1653 local newDelta = newTrans - currentTrans
1654
1655 Cylindered.setAbsoluteToolTranslation(self, movingTool, currentTrans + newDelta)
1656
1657 --#debug DebugUtil.drawDebugNode(zTranslationNode.node, "trans"..i, false)
1658 end
1659
1660 circleDistance2 = MathUtil.vector3Length(worldToLocal(easyArmControl.xRotationNodes[2].node, getWorldTranslation(easyArmControl.targetRefNode)))
1661 end
1662
1663 --#debug DebugUtil.drawDebugCircleAtNode(easyArmControl.rootNode, circleDistance1, 200, {1, 1, 1, 1}, true, {0, circle1Y, circle1Z})
1664 --#debug DebugUtil.drawDebugCircleAtNode(easyArmControl.rootNode, circleDistance2, 200, {1, 0, 1, 1}, true, {0, circle2Y, circle2Z})
1665
1666 -- calculate intersections
1667 local ix, iy, i2x, i2y = MathUtil.getCircleCircleIntersection(circle1Z, circle1Y, circleDistance1, circle2Z, circle2Y, circleDistance2)
1668 if ix ~= nil and iy ~= nil then
1669 local node1Tool = easyArmControl.xRotationNodes[1].movingTool
1670 local node2Tool = easyArmControl.xRotationNodes[2].movingTool
1671
1672 -- rotation node 1
1673 local node1Rotation = -math.atan2(iy, ix)
1674 local node1RotationClamped = MathUtil.clamp(node1Rotation, node1Tool.rotMin, node1Tool.rotMax)
1675 local node1Overrun = 0 -- node1Rotation - node1RotationClamped
1676
1677 Cylindered.setAbsoluteToolRotation(self, easyArmControl.xRotationNodes[1].movingTool, node1RotationClamped, updateDelayedNodes)
1678
1679 -- rotation node 2
1680 local node2Rotation = math.pi - math.acos((circleDistance1*circleDistance1 + circleDistance2*circleDistance2 - distanceToTarget*distanceToTarget) / (2*circleDistance1*circleDistance2))
1681 local node2RotationClamped = MathUtil.clamp(node2Rotation + node1Overrun, node2Tool.rotMin, node2Tool.rotMax)
1682 local node2Overrun = node2Rotation - node2RotationClamped
1683
1684 Cylindered.setAbsoluteToolRotation(self, easyArmControl.xRotationNodes[2].movingTool, node2RotationClamped, updateDelayedNodes)
1685
1686 -- rotation node 1
1687 node1RotationClamped = MathUtil.clamp(node1RotationClamped + node2Overrun * 0.5, node1Tool.rotMin, node1Tool.rotMax)
1688 Cylindered.setAbsoluteToolRotation(self, easyArmControl.xRotationNodes[1].movingTool, node1RotationClamped, updateDelayedNodes)
1689
1690 -- debug
1691 --#debug local dx0, dy0, dz0 = localToWorld(easyArmControl.rootNode, 0, 0, 0)
1692 --#debug local dx1, dy1, dz1 = localToWorld(easyArmControl.rootNode, 0, iy, ix)
1693 --#debug local dx2, dy2, dz2 = localToWorld(easyArmControl.rootNode, 0, i2y, i2x)
1694 --#debug DebugUtil.drawDebugGizmoAtWorldPos(dx1, dy1, dz1, 0, 0, 1, 0, 1, 0, "i1", false)
1695 --#debug DebugUtil.drawDebugGizmoAtWorldPos(dx2, dy2, dz2, 0, 0, 1, 0, 1, 0, "i2", false)
1696 --#debug drawDebugLine(dx0, dy0, dz0, 1, 0, 0, worldTargetX, worldTargetY, worldTargetZ, 1, 0, 0) -- root to target
1697 --#debug drawDebugLine(dx0, dy0, dz0, 1, 1, 0, dx1, dy1, dz1, 1, 1, 0) -- root to i1
1698 --#debug drawDebugLine(dx1, dy1, dz1, 1, 1, 0, worldTargetX, worldTargetY, worldTargetZ, 1, 1, 0) -- i1 to target
1699
1700 --#debug DebugUtil.drawDebugCircleAtNode(easyArmControl.rootNode, easyArmControl.maxTotalDistance, 200, {0, 1, 0, 1}, true)
1701 --#debug if easyArmControl.maxTransDistance ~= nil then
1702 --#debug DebugUtil.drawDebugCircleAtNode(easyArmControl.xRotationNodes[2].node, easyArmControl.maxTransDistance, 200, {0, 1, 0, 1}, true)
1703 --#debug end
1704 end
1705 end
1706 end
1707end

updateExtraDependentParts

Description
Definition
updateExtraDependentParts()
Code
1753function Cylindered:updateExtraDependentParts(part, dt)
1754end

updateMovingPart

Description
Update moving part
Definition
updateMovingPart(table part, boolean placeComponents, boolean updateDependentParts, boolean isActive)
Arguments
tablepartpart
booleanplaceComponentsplace components
booleanupdateDependentPartsupdate dependent parts
booleanisActivemoving part is active (default: true)
Code
3574function Cylindered.updateMovingPart(self, part, placeComponents, updateDependentParts, isActive, updateSounds)
3575
3576 -- the local reference point must be referenceDistance away from the referencePoint
3577 local refX,refY,refZ
3578 local dirX, dirY, dirZ = 0,0,0
3579 local changed, applyDirection = false, false
3580 if part.referencePoint ~= nil then
3581 if part.moveToReferenceFrame then
3582 local x,y,z = localToLocal(part.referenceFrame, getParent(part.node), part.referenceFrameOffset[1], part.referenceFrameOffset[2], part.referenceFrameOffset[3])
3583 setTranslation(part.node, x,y,z)
3584 changed = true
3585 end
3586 refX,refY,refZ = getWorldTranslation(part.referencePoint)
3587 if part.referenceDistance == 0 then
3588 if part.useLocalOffset then
3589 local lx, ly, lz = worldToLocal(part.node, refX, refY, refZ)
3590 dirX, dirY, dirZ = localDirectionToWorld(part.node, lx-part.localReferencePoint[1], ly-part.localReferencePoint[2], lz)
3591 else
3592 local x,y,z = getWorldTranslation(part.node)
3593 dirX, dirY, dirZ = refX - x, refY-y, refZ-z
3594 end
3595 else
3596 if part.updateLocalReferenceDistance then
3597 local _,y,z = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode))
3598 part.localReferenceDistance = MathUtil.vector2Length(y, z)
3599 end
3600 if part.referenceDistancePoint ~= nil then
3601 local _,_,z = worldToLocal(part.node, getWorldTranslation(part.referenceDistancePoint))
3602 part.referenceDistance = z
3603 end
3604
3605 if part.localReferenceTranslate then
3606 local _, ly, lz = worldToLocal(part.node, refX, refY, refZ)
3607
3608 -- calculate line-circle intersection
3609 if math.abs(ly) < part.referenceDistance then
3610 local dz = math.sqrt(part.referenceDistance*part.referenceDistance - ly*ly)
3611
3612 local z1 = (lz - dz) - part.localReferenceDistance
3613 local z2 = (lz + dz) - part.localReferenceDistance
3614 if math.abs(z2) < math.abs(z1) then
3615 z1 = z2
3616 end
3617 local parentNode = getParent(part.node)
3618 local tx,ty,tz = unpack(part.localReferenceTranslation)
3619 local _, _, coz = localToLocal(parentNode, part.node, tx,ty,tz)
3620 local ox,oy,oz = localDirectionToLocal(part.node, parentNode, 0,0,z1-coz)
3621 setTranslation(part.node, tx+ox,ty+oy,tz+oz)
3622 changed = true
3623 end
3624 else
3625 local r1 = part.localReferenceDistance
3626 local r2 = part.referenceDistance
3627
3628 if part.dynamicLocalReferenceDistance then
3629 local _, y1, z1 = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode))
3630 local _, y2, z2 = worldToLocal(part.node, localToWorld(part.localReferencePointNode, 0, 0, part.referenceDistance))
3631
3632 r2 = MathUtil.vector2Length(y1-y2, z1-z2)
3633 end
3634
3635 local _, ly, lz = worldToLocal(part.node, refX, refY, refZ)
3636 --print("intersect: "..ly .. " "..lz)
3637 local ix, iy, i2x, i2y = MathUtil.getCircleCircleIntersection(0,0, r1, ly, lz, r2)
3638
3639 local allowUpdate = true
3640 if part.referenceDistanceThreshold > 0 then
3641 local lRefX, lRefY, lRefZ = worldToLocal(part.node, getWorldTranslation(part.referencePoint))
3642 local x,y,z = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode))
3643 local currentDistance = MathUtil.vector3Length(lRefX-x, lRefY-y, lRefZ-z)
3644
3645 if math.abs(currentDistance-part.referenceDistance) < part.referenceDistanceThreshold then
3646 allowUpdate = false
3647 end
3648 end
3649
3650 if allowUpdate and ix ~= nil then
3651 if i2x ~= nil then
3652 -- use the point which as the same angle side as the original configuration
3653 local side = ix*(lz-iy) - iy*(ly-ix)
3654 if (side < 0) ~= (part.localReferenceAngleSide < 0) then
3655 iy = i2y
3656 ix = i2x
3657 end
3658 end
3659 dirX, dirY, dirZ = localDirectionToWorld(part.node, 0, ix, iy)
3660 changed = true
3661 end
3662 end
3663 end
3664
3665 if part.doInversedLineAlignment then
3666 if part.doInversedLineAlignmentRoot == nil then
3667 part.doInversedLineAlignmentRoot = createTransformGroup("inversedLineAlignmentRoot")
3668 link(getParent(part.node), part.doInversedLineAlignmentRoot, getChildIndex(part.node))
3669 setTranslation(part.doInversedLineAlignmentRoot, getTranslation(part.node))
3670 setRotation(part.doInversedLineAlignmentRoot, getRotation(part.node))
3671 link(part.doInversedLineAlignmentRoot, part.node)
3672 setTranslation(part.node, 0, 0, 0)
3673 setRotation(part.node, 0, 0, 0)
3674 end
3675
3676 for i=1, #part.orientationLineNodes-1 do
3677 local startNode = part.orientationLineNodes[i]
3678 local endNode = part.orientationLineNodes[i + 1]
3679
3680 local _, sy, sz = localToLocal(startNode, part.node, 0, 0, 0)
3681 local _, ey, ez = localToLocal(endNode, part.node, 0, 0, 0)
3682
3683 local minLength = MathUtil.vector2Length(sy, sz)
3684 local maxLength = MathUtil.vector2Length(ey, ez)
3685 local targetLength = calcDistanceFrom(part.referencePoint, part.doInversedLineAlignmentRoot)
3686
3687 if targetLength >= minLength and targetLength <= maxLength then
3688 local alpha = (targetLength - minLength) / (maxLength - minLength)
3689
3690 local ty = MathUtil.lerp(sy, ey, alpha)
3691 local tz = MathUtil.lerp(sz, ez, alpha)
3692
3693 -- alignment root always pointing to reference point
3694 -- actual moving part is adjusting to local offset to current target point on the line
3695
3696 local upX, upY, upZ = localDirectionToWorld(part.referenceFrame, 0, 1, 0)
3697
3698 dirX, dirY, dirZ = localDirectionToWorld(part.doInversedLineAlignmentRoot, 0, -ty, tz)
3699 I3DUtil.setWorldDirection(part.node, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot)
3700
3701 local x, y, z = getWorldTranslation(part.doInversedLineAlignmentRoot)
3702 dirX, dirY, dirZ = refX - x, refY - y, refZ - z
3703 I3DUtil.setWorldDirection(part.doInversedLineAlignmentRoot, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot)
3704
3705 changed = true
3706 break
3707 end
3708 end
3709 end
3710 else
3711 if part.alignToWorldY then
3712 dirX, dirY, dirZ = localDirectionToWorld(getRootNode(), 0, 1, 0)
3713
3714 local lDX, lDY, lDZ = worldDirectionToLocal(part.referenceFrame, dirX, dirY, dirZ)
3715 if lDZ < 0 then
3716 lDZ = -lDZ
3717 end
3718 dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, lDX, lDY, lDZ)
3719
3720 changed = true
3721 else
3722 dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, 0, 0, 1)
3723 changed = true
3724 end
3725 if part.moveToReferenceFrame then
3726 local x,y,z = localToLocal(part.referenceFrame, getParent(part.node), part.referenceFrameOffset[1], part.referenceFrameOffset[2], part.referenceFrameOffset[3])
3727 setTranslation(part.node, x,y,z)
3728 changed = true
3729 end
3730
3731 if part.doLineAlignment then
3732 local foundPoint = false
3733 for i=1, #part.orientationLineNodes-1 do
3734 local startNode = part.orientationLineNodes[i]
3735 local endNode = part.orientationLineNodes[i+1]
3736
3737 local _, sy, sz = localToLocal(startNode, part.referenceFrame, 0, 0, 0)
3738 local _, ey, ez = localToLocal(endNode, part.referenceFrame, 0, 0, 0)
3739 local _, cy, cz = localToLocal(part.node, part.referenceFrame, 0, 0, 0)
3740
3741 local partLength = part.partLength
3742 if part.partLengthNode ~= nil then
3743 partLength = calcDistanceFrom(part.node, part.partLengthNode)
3744 end
3745
3746 local hasIntersection, i1y, i1z, i2y, i2z = MathUtil.getCircleLineIntersection(cy, cz, partLength, sy, sz, ey, ez)
3747 if hasIntersection then
3748 local targetY, targetZ
3749 if not MathUtil.getIsOutOfBounds(i1y, sy, ey) and not MathUtil.getIsOutOfBounds(i1z, sz, ez) then
3750 targetY, targetZ = i1y, i1z
3751 foundPoint = true
3752 end
3753 if not MathUtil.getIsOutOfBounds(i2y, sy, ey) and not MathUtil.getIsOutOfBounds(i2z, sz, ez) then
3754 targetY, targetZ = i2y, i2z
3755 foundPoint = true
3756 end
3757
3758 if foundPoint and not MathUtil.isNan(targetY) and not MathUtil.isNan(targetZ) then
3759 dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, 0, targetY, targetZ)
3760
3761 changed = true
3762 applyDirection = true
3763 break
3764 end
3765 end
3766 end
3767 end
3768 end
3769
3770 if part.smoothedDirectionScale then
3771 if part.smoothedDirectionScaleAlpha == nil then
3772 part.smoothedDirectionScaleAlpha = isActive and 1 or 0
3773 end
3774
3775 local dt = g_currentDt or 9999
3776 if isActive then
3777 part.smoothedDirectionScaleAlpha = math.min(part.smoothedDirectionScaleAlpha + dt * part.smoothedDirectionTime, 1)
3778 else
3779 part.smoothedDirectionScaleAlpha = math.max(part.smoothedDirectionScaleAlpha - dt * part.smoothedDirectionTime, 0)
3780 end
3781
3782 local inDirX, inDirY, inDirZ = localDirectionToWorld(getParent(part.node), unpack(part.initialDirection))
3783 dirX, dirY, dirZ = MathUtil.vector3Lerp(inDirX, inDirY, inDirZ, dirX, dirY, dirZ, part.smoothedDirectionScaleAlpha)
3784 end
3785
3786 if (part.doDirectionAlignment or applyDirection) and (dirX ~= 0 or dirY ~= 0 or dirZ ~= 0) then
3787 local upX, upY, upZ = localDirectionToWorld(part.referenceFrame, 0, 1, 0)
3788 if part.invertZ then
3789 dirX = -dirX
3790 dirY = -dirY
3791 dirZ = -dirZ
3792 end
3793
3794 local directionThreshold = part.directionThresholdActive
3795 if not self:getIsActive() then
3796 if part.directionThreshold ~= nil and part.directionThreshold > 0 then
3797 directionThreshold = part.directionThreshold
3798 end
3799 end
3800
3801 local lDirX, lDirY, lDirZ = worldDirectionToLocal(part.parent, dirX, dirY, dirZ)
3802 local lastDirection, lastUpVector = part.lastDirection, part.lastUpVector
3803 if math.abs(lastDirection[1] - lDirX) > directionThreshold or
3804 math.abs(lastDirection[2] - lDirY) > directionThreshold or
3805 math.abs(lastDirection[3] - lDirZ) > directionThreshold or
3806 math.abs(lastUpVector[1] - upX) > directionThreshold or
3807 math.abs(lastUpVector[2] - upY) > directionThreshold or
3808 math.abs(lastUpVector[3] - upZ) > directionThreshold then
3809
3810 I3DUtil.setWorldDirection(part.node, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot)
3811
3812 if part.debug then
3813 local x, y, z = getWorldTranslation(part.node)
3814 local length, _ = 1
3815 if part.referencePoint ~= nil then
3816 _, _, length = worldToLocal(part.node, refX, refY, refZ)
3817 end
3818
3819 drawDebugLine(x, y, z, 1, 0, 0, x + dirX * length, y + dirY * length, z + dirZ * length, 0, 1, 0, true)
3820 end
3821
3822 lastDirection[1], lastDirection[2], lastDirection[3] = lDirX, lDirY, lDirZ
3823 lastUpVector[1], lastUpVector[2], lastUpVector[3] = upX, upY, upZ
3824
3825 changed = true
3826 else
3827 changed = false
3828 end
3829
3830 if part.scaleZ and part.localReferenceDistance ~= nil then
3831 local len = MathUtil.vector3Length(dirX, dirY, dirZ)
3832 setScale(part.node, 1, 1, len/part.localReferenceDistance)
3833
3834 if part.debug then
3835 DebugUtil.drawDebugNode(part.node, string.format("scale:%.2f", len/part.localReferenceDistance), false)
3836 end
3837 end
3838 end
3839
3840 if part.doRotationAlignment then
3841 local x,y,z = getRotation(part.referenceFrame)
3842 setRotation(part.node, x*part.rotMultiplier, y*part.rotMultiplier, z*part.rotMultiplier)
3843 changed = true
3844 end
3845
3846 if part.referencePoint ~= nil then
3847 local numTranslatingParts = #part.translatingParts
3848 local distanceDivider = math.max(part.translatingPartsDivider, 1)
3849 if numTranslatingParts > 0 then
3850 local _, _, dist = worldToLocal(part.node, refX, refY, refZ)
3851 for i=1,numTranslatingParts do
3852 local translatingPart = part.translatingParts[i]
3853 local newZ = (dist - translatingPart.referenceDistance) / (translatingPart.divideTranslatingDistance and distanceDivider or 1)
3854
3855 if translatingPart.minZTrans ~= nil then
3856 newZ = math.max(translatingPart.minZTrans, newZ)
3857 end
3858
3859 if translatingPart.maxZTrans ~= nil then
3860 newZ = math.min(translatingPart.maxZTrans, newZ)
3861 end
3862
3863 if not translatingPart.divideTranslatingDistance then
3864 dist = dist - (newZ - translatingPart.startPos[3])
3865 end
3866
3867 local allowUpdate = true
3868 if part.referenceDistanceThreshold > 0 then
3869 if math.abs(translatingPart.lastZ-newZ) < part.referenceDistanceThreshold then
3870 allowUpdate = false
3871 end
3872 end
3873
3874 if allowUpdate then
3875 if updateSounds ~= false then
3876 if part.samplesByAction ~= nil or translatingPart.samplesByAction ~= nil then
3877 if newZ ~= translatingPart.lastZ then
3878 if math.abs(translatingPart.lastZ - newZ) > 0.0001 then
3879 self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_END, Cylindered.SOUND_TYPE_ENDING)
3880 self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_END, Cylindered.SOUND_TYPE_ENDING)
3881
3882 self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_START, Cylindered.SOUND_TYPE_STARTING)
3883 self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_START, Cylindered.SOUND_TYPE_STARTING)
3884
3885 if newZ > translatingPart.lastZ + 0.0001 then
3886 self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_POS, Cylindered.SOUND_TYPE_CONTINUES)
3887 self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_POS, Cylindered.SOUND_TYPE_CONTINUES)
3888
3889 self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_END_POS, Cylindered.SOUND_TYPE_ENDING)
3890 self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_END_POS, Cylindered.SOUND_TYPE_ENDING)
3891
3892 self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_START_POS, Cylindered.SOUND_TYPE_STARTING)
3893 self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_START_POS, Cylindered.SOUND_TYPE_STARTING)
3894 elseif newZ < translatingPart.lastZ - 0.0001 then
3895 self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_NEG, Cylindered.SOUND_TYPE_CONTINUES)
3896 self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_NEG, Cylindered.SOUND_TYPE_CONTINUES)
3897
3898 self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_END_NEG, Cylindered.SOUND_TYPE_ENDING)
3899 self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_END_NEG, Cylindered.SOUND_TYPE_ENDING)
3900
3901 self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_START_NEG, Cylindered.SOUND_TYPE_STARTING)
3902 self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_START_NEG, Cylindered.SOUND_TYPE_STARTING)
3903 end
3904 end
3905 end
3906 end
3907 end
3908
3909 translatingPart.lastZ = newZ
3910 setTranslation(translatingPart.node, translatingPart.startPos[1], translatingPart.startPos[2], newZ)
3911 changed = true
3912 end
3913 end
3914 end
3915 end
3916
3917 if changed then
3918 if part.copyLocalDirectionParts ~= nil then
3919 for _,copyLocalDirectionPart in pairs(part.copyLocalDirectionParts) do
3920 local dx,dy,dz = localDirectionToWorld(part.node, 0,0,1)
3921 dx,dy,dz = worldDirectionToLocal(getParent(part.node), dx,dy,dz)
3922 dx = dx * copyLocalDirectionPart.dirScale[1]
3923 dy = dy * copyLocalDirectionPart.dirScale[2]
3924 dz = dz * copyLocalDirectionPart.dirScale[3]
3925
3926 local ux,uy,uz = localDirectionToWorld(part.node, 0,1,0)
3927 ux,uy,uz = worldDirectionToLocal(getParent(part.node), ux,uy,uz)
3928 ux = ux * copyLocalDirectionPart.upScale[1]
3929 uy = uy * copyLocalDirectionPart.upScale[2]
3930 uz = uz * copyLocalDirectionPart.upScale[3]
3931
3932 setDirection(copyLocalDirectionPart.node, dx,dy,dz, ux,uy,uz)
3933
3934 if self.isServer then
3935 Cylindered.updateComponentJoints(self, copyLocalDirectionPart, placeComponents)
3936 end
3937 end
3938 end
3939
3940 -- update component joint
3941 if self.isServer then
3942 Cylindered.updateComponentJoints(self, part, placeComponents)
3943 Cylindered.updateAttacherJoints(self, part)
3944 Cylindered.updateWheels(self, part)
3945 end
3946
3947 Cylindered.updateWheels(self, part)
3948 end
3949
3950 if updateDependentParts then
3951 for _, data in pairs(part.dependentPartData) do
3952 if self.currentUpdateDistance < data.maxUpdateDistance then
3953 local dependentPart = data.part
3954 local dependentIsActive = self:getIsMovingPartActive(dependentPart)
3955 if dependentIsActive or dependentPart.smoothedDirectionScale and dependentPart.smoothedDirectionScaleAlpha ~= 0 then
3956 Cylindered.updateMovingPart(self, dependentPart, placeComponents, updateDependentParts, dependentIsActive)
3957 end
3958 end
3959 end
3960 end
3961
3962 part.isDirty = false
3963end

updateMovingToolSoundEvents

Description
Definition
updateMovingToolSoundEvents()
Code
1881function Cylindered:updateMovingToolSoundEvents(tool, direction, hitLimit, wasAtLimit)
1882 if tool.samplesByAction ~= nil then
1883 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END, Cylindered.SOUND_TYPE_ENDING)
1884 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START, Cylindered.SOUND_TYPE_STARTING)
1885
1886 if direction then
1887 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_POS, Cylindered.SOUND_TYPE_CONTINUES)
1888 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END_POS, Cylindered.SOUND_TYPE_ENDING)
1889 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START_POS, Cylindered.SOUND_TYPE_STARTING)
1890
1891 if hitLimit then
1892 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END_POS_LIMIT, Cylindered.SOUND_TYPE_ENDING)
1893 end
1894
1895 if wasAtLimit then
1896 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START_POS_LIMIT, Cylindered.SOUND_TYPE_STARTING)
1897 end
1898 else
1899 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_NEG, Cylindered.SOUND_TYPE_CONTINUES)
1900 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END_NEG, Cylindered.SOUND_TYPE_ENDING)
1901 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START_NEG, Cylindered.SOUND_TYPE_STARTING)
1902
1903 if hitLimit then
1904 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END_NEG_LIMIT, Cylindered.SOUND_TYPE_ENDING)
1905 end
1906
1907 if wasAtLimit then
1908 self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START_NEG_LIMIT, Cylindered.SOUND_TYPE_STARTING)
1909 end
1910 end
1911 end
1912end

updateRotationBasedLimits

Description
Definition
updateRotationBasedLimits()
Code
4078function Cylindered.updateRotationBasedLimits(self, tool, dependentTool)
4079 if dependentTool.rotationBasedLimits ~= nil then
4080 local state = Cylindered.getMovingToolState(self, tool)
4081 if dependentTool.rotationBasedLimits ~= nil then
4082 local minRot, maxRot, minTrans, maxTrans = dependentTool.rotationBasedLimits:get(state)
4083
4084 if minRot ~= nil then
4085 dependentTool.movingTool.rotMin = minRot
4086 end
4087 if maxRot ~= nil then
4088 dependentTool.movingTool.rotMax = maxRot
4089 end
4090 if minTrans ~= nil then
4091 dependentTool.movingTool.transMin = minTrans
4092 end
4093 if maxTrans ~= nil then
4094 dependentTool.movingTool.transMax = maxTrans
4095 end
4096
4097 local isDirty = false
4098 if minRot ~= nil or maxRot ~= nil then
4099 isDirty = isDirty or Cylindered.setToolRotation(self, dependentTool.movingTool, 0, 0)
4100 end
4101 if minTrans ~= nil or maxTrans ~= nil then
4102 isDirty = isDirty or Cylindered.setToolTranslation(self, dependentTool.movingTool, 0, 0)
4103 end
4104
4105 if isDirty then
4106 Cylindered.setDirty(self, dependentTool.movingTool)
4107
4108 -- sync the new limited rotation values and also the new rotation limits
4109 self:raiseDirtyFlags(dependentTool.movingTool.dirtyFlag)
4110 self:raiseDirtyFlags(self.spec_cylindered.cylinderedDirtyFlag)
4111 end
4112 end
4113 end
4114end

updateWheels

Description
Update wheel of part
Definition
updateWheels(table part)
Arguments
tablepartpart
Code
3547function Cylindered.updateWheels(self, part)
3548 if part.wheels ~= nil then
3549 for _, wheel in pairs(part.wheels) do
3550 wheel.positionX, wheel.positionY, wheel.positionZ = localToLocal(getParent(wheel.repr), wheel.node, wheel.startPositionX-wheel.steeringCenterOffsetX, wheel.startPositionY-wheel.steeringCenterOffsetY, wheel.startPositionZ-wheel.steeringCenterOffsetZ)
3551 if wheel.useReprDirection then
3552 wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.repr, wheel.node, 0,-1,0)
3553 wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.repr, wheel.node, 1,0,0)
3554 elseif wheel.useDriveNodeDirection then
3555 wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 0,-1,0)
3556 wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 1,0,0)
3557 end
3558
3559 self:updateWheelBase(wheel)
3560
3561 for j=1, #wheel.wheelChocks do
3562 self:updateWheelChockPosition(wheel.wheelChocks[j])
3563 end
3564 end
3565 end
3566end