Script v1_7_1_0
- AI
- Animals
- Collections
- Contracts
- Debug
- Economy
- Elements
- EnvironmentalScore
- Errors
- Events
- GUI
- Handtools
- Hud
- I3d
- Input
- Jobs
- Maps
- Materials
- Misc
- Objects
- Parameters
- Placeables
- Placement
- Player
- Shop
- Sounds
- Specialization
- Specializations
- AIConveyorBelt
- AIDrivable
- AIFieldWorker
- AIImplement
- AIJobVehicle
- AIVehicle
- AIVehicleObstacle
- AnimatedVehicle
- ArticulatedAxis
- Attachable
- AttacherJointControl
- AttacherJoints
- AutoLoader
- BaleGrab
- BaleLoader
- Baler
- BaleWrapper
- BaseMaterial
- BigBag
- BunkerSiloCompacter
- BunkerSiloInteractor
- CCTDrivable
- Combine
- ConnectionHoses
- ConveyorBelt
- Cover
- CrabSteering
- Crawlers
- CropSensor
- Cultivator
- Cutter
- Cylindered
- CylinderedFoldable
- Dashboard
- Dischargeable
- Drivable
- DynamicallyLoadedParts
- DynamicMountAttacher
- Enterable
- ExtendedAIVehicle
- ExtendedCombine
- ExtendedMotorized
- ExtendedMower
- ExtendedSowingMachine
- ExtendedSprayer
- ExtendedWearable
- FertilizingCultivator
- FertilizingSowingMachine
- FillTriggerVehicle
- FillUnit
- FillVolume
- Foldable
- FoliageBending
- ForageWagon
- FrontloaderAttacher
- FruitPreparer
- GroundAdjustedNodes
- GroundReference
- HeadlandAnimation
- Honk
- HookLiftContainer
- HookLiftTrailer
- IKChains
- InlineWrapper
- JigglingParts
- Leveler
- LicensePlates
- Lights
- LivestockTrailer
- Locomotive
- LogGrab
- ManureBarrel
- ManureSensor
- MixerWagon
- Motorized
- Mountable
- Mower
- Mulcher
- MultipleItemPurchase
- Pallet
- Pickup
- Pipe
- PlaceableAI
- PlaceableAnimatedObjects
- PlaceableBeehive
- PlaceableBeehivePalletSpa...
- PlaceableBunkerSilo
- PlaceableBuyingStation
- PlaceableCartridgePlayer
- PlaceableChargingStation
- PlaceableClearAreas
- PlaceableColorable
- PlaceableDeletedNodes
- PlaceableDoghouse
- PlaceableDynamicallyLoade...
- PlaceableFarmhouse
- PlaceableFence
- PlaceableFoliageAreas
- PlaceableGreenhouse
- PlaceableHighPressureWash...
- PlaceableHotspots
- PlaceableHusbandry
- PlaceableHusbandryAnimals
- PlaceableHusbandryFeeding...
- PlaceableHusbandryFence
- PlaceableHusbandryFood
- PlaceableHusbandryLiquidM...
- PlaceableHusbandryMilk
- PlaceableHusbandryPallets
- PlaceableHusbandryStraw
- PlaceableHusbandryWater
- PlaceableIncomePerHour
- PlaceableIndoorAreas
- PlaceableInfoTrigger
- PlaceableLeveling
- PlaceableLights
- PlaceableManureHeap
- PlaceablePlacement
- PlaceableProductionPoint
- PlaceableSellingStation
- PlaceableSilo
- PlaceableSiloExtension
- PlaceableSolarPanels
- PlaceableTipOcclusionArea...
- PlaceableTrainSystem
- PlaceableTriggerMarkers
- PlaceableVine
- PlaceableWardrobe
- PlaceableWeatherStation
- PlaceableWeighingStation
- PlaceableWindTurbine
- PlaceableWorkshop
- Plow
- PlowPacker
- PowerConsumer
- PowerTakeOffs
- PrecisionFarmingStatistic
- PushHandTool
- RandomlyMovingParts
- ReceivingHopper
- ReverseDriving
- Rideable
- RidgeMarker
- Roller
- Ropes
- RTKStation
- SaltSpreader
- SemiTrailerFront
- Shovel
- SlopeCompensation
- SmartAttach
- SoilSampler
- SowingMachine
- SpeedRotatingParts
- SplineVehicle
- Sprayer
- StonePicker
- StrawBlower
- StumpCutter
- SupportVehicle
- Suspensions
- Tedder
- TensionBeltObject
- TensionBelts
- TestAreas
- TipOccluder
- Trailer
- TreePlanter
- TreeSaplingPallet
- TreeSaw
- TurnOnVehicle
- VariableWorkWidth
- VehicleSettings
- VineCutter
- VineDetector
- VinePrepruner
- Washable
- WaterTrailer
- Wearable
- Weeder
- WeedSpotSpray
- Wheels
- WindBending
- Windrower
- Wipers
- WoodCrusher
- WoodHarvester
- WorkArea
- WorkMode
- WorkParticles
- StateMachine
- Statistics
- Tasks
- Triggers
- Utils
- Vehicles
Engine v1_7_1_0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- general
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- NoteNode
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- VoiceChat
- XML
Foundation Reference
RandomlyMovingParts
DescriptionSpecialization for making parts randomly move during work using rotation changesFunctions
- getIsRandomlyMovingPartActive
- initSpecialization
- loadRandomlyMovingPartFromXML
- onLoad
- onUpdate
- prerequisitesPresent
- registerEventListeners
- registerFunctions
- updateRandomlyMovingPart
- updateRotationTargetValues
getIsRandomlyMovingPartActive
DescriptionReturns if randomly moving part is activeDefinition
getIsRandomlyMovingPartActive(table part)Arguments
table | part | part to check |
boolean | isActive | is active |
294 | function RandomlyMovingParts:getIsRandomlyMovingPartActive(part) |
295 | local retValue = true |
296 | if part.groundReferenceNode ~= nil then |
297 | retValue = self:getIsGroundReferenceNodeActive(part.groundReferenceNode) |
298 | end |
299 | |
300 | return retValue |
301 | end |
initSpecialization
DescriptionDefinitioninitSpecialization()Code
26 | function RandomlyMovingParts.initSpecialization() |
27 | local schema = Vehicle.xmlSchema |
28 | schema:setXMLSpecializationType("RandomlyMovingParts") |
29 | |
30 | schema:register(XMLValueType.FLOAT, "vehicle.randomlyMovingParts#maxUpdateDistance", RandomlyMovingParts.DEFAULT_MAX_UPDATE_DISTANCE) |
31 | |
32 | schema:register(XMLValueType.NODE_INDEX, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#node", "Node") |
33 | schema:register(XMLValueType.INT, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#refNodeIndex", "Ground reference node index") |
34 | schema:register(XMLValueType.VECTOR_ROT_2, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#rotMean", "Rotation mean") |
35 | schema:register(XMLValueType.INT, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#rotAxis", "Rotation axis") |
36 | schema:register(XMLValueType.VECTOR_ROT_2, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#rotVariance", "Rotation variance") |
37 | schema:register(XMLValueType.VECTOR_2, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#rotTimeMean", "Rotation time mean") |
38 | schema:register(XMLValueType.VECTOR_2, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#rotTimeVariance", "Rotation time variance") |
39 | schema:register(XMLValueType.VECTOR_2, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#pauseMean", "Pause time variance") |
40 | schema:register(XMLValueType.VECTOR_2, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#pauseVariance", "Pause time variance") |
41 | schema:register(XMLValueType.BOOL, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. "#isSpeedDependent", "Is speed dependent") |
42 | |
43 | schema:register(XMLValueType.NODE_INDEX, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. ".node(?)#node", "Node to apply the same random angle") |
44 | schema:register(XMLValueType.INT, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. ".node(?)#rotAxis", "Rotation axis") |
45 | schema:register(XMLValueType.FLOAT, RandomlyMovingParts.RANDOMLY_MOVING_PART_XML_KEY .. ".node(?)#scale", "Rotation Scale") |
46 | |
47 | schema:setXMLSpecializationType() |
48 | end |
loadRandomlyMovingPartFromXML
DescriptionLoad randomly moving part from xmlDefinition
loadRandomlyMovingPartFromXML(table part, integer xmlFile, string key)Arguments
table | part | part |
integer | xmlFile | id of xml object |
string | key | key |
boolean | success |
115 | function RandomlyMovingParts:loadRandomlyMovingPartFromXML(part, xmlFile, key) |
116 | if not self.xmlFile:hasProperty(key) then |
117 | return false |
118 | end |
119 | |
120 | local isRotAxisValid = function(value) |
121 | return value ~= nil and value >= 1 and value <= 3 |
122 | end |
123 | |
124 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key .. "#index", key .. "#node") --FS17 to FS19 |
125 | |
126 | local node = xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings) |
127 | if node == nil then |
128 | Logging.xmlWarning(self.xmlFile, "Unknown node for randomlyMovingPart in '%s'", key) |
129 | return false |
130 | end |
131 | |
132 | part.node = node |
133 | |
134 | if self.getGroundReferenceNodeFromIndex ~= nil then |
135 | local refNodeIndex = xmlFile:getValue(key .. "#refNodeIndex") |
136 | if refNodeIndex ~= nil then |
137 | if refNodeIndex ~= 0 then |
138 | local groundReferenceNode = self:getGroundReferenceNodeFromIndex(refNodeIndex) |
139 | if groundReferenceNode ~= nil then |
140 | part.groundReferenceNode = groundReferenceNode |
141 | end |
142 | else |
143 | Logging.xmlWarning(self.xmlFile, "Unknown ground reference node in '%s'! Indices start with '0'", key.."#refNodeIndex") |
144 | end |
145 | end |
146 | end |
147 | |
148 | local rx, ry, rz = getRotation(part.node) |
149 | local rotMean = xmlFile:getValue(key .. "#rotMean", nil, true) |
150 | if rotMean then |
151 | part.rotOrig = {rx, ry, rz} |
152 | part.rotCur = {rx, ry, rz} |
153 | |
154 | part.rotAxis = xmlFile:getValue(key .. "#rotAxis") |
155 | if not isRotAxisValid(part.rotAxis) then |
156 | Logging.xmlWarning(xmlFile, "Invalid rot axis '%s' given for node '%s'. Only '1', '2' or '3' are allowed!", part.rotAxis, key) |
157 | return false |
158 | end |
159 | |
160 | part.rotMean = rotMean |
161 | part.rotVar = xmlFile:getValue(key .. "#rotVariance", nil, true) |
162 | |
163 | part.rotTimeMean = xmlFile:getValue(key .. "#rotTimeMean", nil, true) |
164 | part.rotTimeVar = xmlFile:getValue(key .. "#rotTimeVariance", nil, true) |
165 | |
166 | part.pauseMean = xmlFile:getValue(key .. "#pauseMean", nil, true) |
167 | part.pauseVar = xmlFile:getValue(key .. "#pauseVariance", nil, true) |
168 | |
169 | for i=1,2 do |
170 | part.rotTimeMean[i] = part.rotTimeMean[i] * 1000 |
171 | part.rotTimeVar[i] = part.rotTimeVar[i] * 1000 |
172 | part.pauseMean[i] = part.pauseMean[i] * 1000 |
173 | part.pauseVar[i] = part.pauseVar[i] * 1000 |
174 | end |
175 | |
176 | part.rotTarget = {} |
177 | part.rotSpeed = {} |
178 | part.pause = {} |
179 | |
180 | part.isSpeedDependent = xmlFile:getValue(key .. "#isSpeedDependent", false) |
181 | |
182 | self:updateRotationTargetValues(part) |
183 | end |
184 | |
185 | part.nodes = {} |
186 | xmlFile:iterate(key .. ".node", function(index, nodeKey) |
187 | local entry = {} |
188 | entry.node = xmlFile:getValue(nodeKey .. "#node", nil, self.components, self.i3dMappings) |
189 | if entry.node ~= nil then |
190 | entry.rotAxis = xmlFile:getValue(nodeKey .. "#rotAxis") |
191 | if entry.rotAxis ~= nil then |
192 | if not isRotAxisValid(entry.rotAxis) then |
193 | Logging.xmlWarning(xmlFile, "Invalid rot axis '%s' given for node '%s'. Only '1', '2' or '3' are allowed!", entry.rotAxis, nodeKey) |
194 | return |
195 | else |
196 | entry.currentRot = {getRotation(entry.node)} |
197 | end |
198 | end |
199 | |
200 | entry.scale = xmlFile:getValue(nodeKey .. "#scale", 1) |
201 | |
202 | table.insert(part.nodes, entry) |
203 | end |
204 | end) |
205 | |
206 | part.nextMoveTime = g_currentMission.time + part.pause[2] |
207 | part.curMoveDirection = 1 |
208 | part.isActive = true |
209 | |
210 | return true |
211 | end |
onLoad
DescriptionCalled on loadingDefinition
onLoad(table savegame)Arguments
table | savegame | savegame |
69 | function RandomlyMovingParts:onLoad(savegame) |
70 | local spec = self.spec_randomlyMovingParts |
71 | |
72 | spec.maxUpdateDistance = self.xmlFile:getValue("vehicle.randomlyMovingParts#maxUpdateDistance", RandomlyMovingParts.DEFAULT_MAX_UPDATE_DISTANCE) |
73 | |
74 | spec.nodes = {} |
75 | local i = 0 |
76 | while true do |
77 | local baseName = string.format("vehicle.randomlyMovingParts.randomlyMovingPart(%d)", i) |
78 | if not self.xmlFile:hasProperty(baseName) then |
79 | break |
80 | end |
81 | |
82 | local randomlyMovingPart = {} |
83 | if self:loadRandomlyMovingPartFromXML(randomlyMovingPart, self.xmlFile, baseName) then |
84 | table.insert(spec.nodes, randomlyMovingPart) |
85 | end |
86 | |
87 | i = i + 1 |
88 | end |
89 | |
90 | if not self.isClient or #spec.nodes == 0 then |
91 | SpecializationUtil.removeEventListener(self, "onUpdate", RandomlyMovingParts) |
92 | end |
93 | end |
onUpdate
DescriptionCalled on updateDefinition
onUpdate(float dt, boolean isActiveForInput, boolean isSelected)Arguments
float | dt | time since last call in ms |
boolean | isActiveForInput | true if vehicle is active for input |
boolean | isSelected | true if vehicle is selected |
100 | function RandomlyMovingParts:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
101 | local spec = self.spec_randomlyMovingParts |
102 | if self.currentUpdateDistance < spec.maxUpdateDistance then |
103 | for _, part in pairs(spec.nodes) do |
104 | self:updateRandomlyMovingPart(part, dt) |
105 | end |
106 | end |
107 | end |
prerequisitesPresent
DescriptionChecks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
table | specializations | specializations |
boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
20 | function RandomlyMovingParts.prerequisitesPresent(specializations) |
21 | return true |
22 | end |
registerEventListeners
DescriptionDefinitionregisterEventListeners()Code
61 | function RandomlyMovingParts.registerEventListeners(vehicleType) |
62 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", RandomlyMovingParts) |
63 | SpecializationUtil.registerEventListener(vehicleType, "onUpdate", RandomlyMovingParts) |
64 | end |
registerFunctions
DescriptionDefinitionregisterFunctions()Code
52 | function RandomlyMovingParts.registerFunctions(vehicleType) |
53 | SpecializationUtil.registerFunction(vehicleType, "loadRandomlyMovingPartFromXML", RandomlyMovingParts.loadRandomlyMovingPartFromXML) |
54 | SpecializationUtil.registerFunction(vehicleType, "updateRandomlyMovingPart", RandomlyMovingParts.updateRandomlyMovingPart) |
55 | SpecializationUtil.registerFunction(vehicleType, "updateRotationTargetValues", RandomlyMovingParts.updateRotationTargetValues) |
56 | SpecializationUtil.registerFunction(vehicleType, "getIsRandomlyMovingPartActive", RandomlyMovingParts.getIsRandomlyMovingPartActive) |
57 | end |
updateRandomlyMovingPart
DescriptionUpdate randomly moving partsDefinition
updateRandomlyMovingPart(table part, float dt)Arguments
table | part | part to update |
float | dt | time since last call in ms |
boolean | updated | part was updated |
218 | function RandomlyMovingParts:updateRandomlyMovingPart(part, dt) |
219 | if part.nextMoveTime < g_currentMission.time then |
220 | local speed = dt |
221 | if part.isSpeedDependent then |
222 | speed = speed * math.min(self:getLastSpeed() / self:getRawSpeedLimit(), 1) |
223 | end |
224 | |
225 | part.isActive = self:getIsRandomlyMovingPartActive(part) |
226 | if part.curMoveDirection > 0 then |
227 | if part.isActive then |
228 | part.rotCur[part.rotAxis] = math.min(part.rotTarget[1], part.rotCur[part.rotAxis] + (part.rotSpeed[1] * speed)) |
229 | if part.rotCur[part.rotAxis] == part.rotTarget[1] then |
230 | part.curMoveDirection = -1 |
231 | part.nextMoveTime = g_currentMission.time + part.pause[1] |
232 | end |
233 | end |
234 | else |
235 | part.rotCur[part.rotAxis] = math.max(part.rotTarget[2], part.rotCur[part.rotAxis] + (part.rotSpeed[2] * speed)) |
236 | if part.rotCur[part.rotAxis] == part.rotTarget[2] then |
237 | -- start next movement only if active |
238 | if part.isActive then |
239 | part.curMoveDirection = 1 |
240 | part.nextMoveTime = g_currentMission.time + part.pause[2] |
241 | self:updateRotationTargetValues(part) |
242 | end |
243 | end |
244 | end |
245 | |
246 | setRotation(part.node, part.rotCur[1], part.rotCur[2], part.rotCur[3]) |
247 | |
248 | for i=1, #part.nodes do |
249 | local nodeData = part.nodes[i] |
250 | if nodeData.rotAxis ~= nil then |
251 | nodeData.currentRot[nodeData.rotAxis] = part.rotCur[part.rotAxis] * nodeData.scale |
252 | setRotation(nodeData.node, nodeData.currentRot[1], nodeData.currentRot[2], nodeData.currentRot[3]) |
253 | else |
254 | setRotation(nodeData.node, part.rotCur[1] * nodeData.scale, part.rotCur[2] * nodeData.scale, part.rotCur[3] * nodeData.scale) |
255 | end |
256 | end |
257 | |
258 | if self.setMovingToolDirty ~= nil then |
259 | self:setMovingToolDirty(part.node) |
260 | end |
261 | |
262 | return true |
263 | else |
264 | return false |
265 | end |
266 | end |
updateRotationTargetValues
DescriptionUpdate rotation target valuesDefinition
updateRotationTargetValues(table part)Arguments
table | part | part to update |
271 | function RandomlyMovingParts:updateRotationTargetValues(part) |
272 | for i=1,2 do |
273 | part.rotTarget[i] = part.rotMean[i] + (part.rotVar[i] * (-0.5 + math.random())) |
274 | end |
275 | |
276 | for i=1,2 do |
277 | local rotTime = part.rotTimeMean[i] + (part.rotTimeVar[i] * (-0.5 + math.random())) |
278 | if i == 1 then |
279 | part.rotSpeed[i] = (part.rotTarget[1] - part.rotTarget[2]) / rotTime |
280 | else |
281 | part.rotSpeed[i] = (part.rotTarget[2] - part.rotTarget[1]) / rotTime |
282 | end |
283 | end |
284 | |
285 | for i=1,2 do |
286 | part.pause[i] = part.pauseMean[i] + (part.pauseVar[i] * (-0.5 + math.random())) |
287 | end |
288 | end |