Script v1.4.4.0
- Handtools
- Events
- Objects
- Placeables
- Triggers
- Utils
- Vehicles
- Specializations
- AIVehicle
- AnimatedVehicle
- ArticulatedAxis
- Attachable
- AttacherJointControl
- AttacherJoints
- BaleGrab
- BaleLoader
- Baler
- BaleWrapper
- BuiltInCutterTrailer
- BunkerSiloCompacter
- Combine
- ConveyorBelt
- Cover
- CrabSteering
- Crawler
- Cultivator
- Cutter
- Cylindered
- Drivable
- DynamicMountAttacher
- Fillable
- FillActivatable
- FillVolume
- Foldable
- ForageWagon
- FrontloaderAttacher
- FruitPreparer
- FuelTrailer
- Honk
- HookLiftContainer
- HookLiftTrailer
- Leveler
- Lights
- LivestockTrailer
- ManureBarrel
- ManureSpreader
- MixerWagon
- Motorized
- Mountable
- Mower
- NonTabbable
- Overloading
- Pickup
- Pipe
- Plough
- PowerConsumer
- RandomlyMovingParts
- ReceivingHopper
- ReverseDriving
- RidgeMarker
- Roller
- Ropes
- RotorSpreader
- SemiTrailerFront
- Shovel
- SowingMachine
- SpeedRotatingParts
- Sprayer
- Steerable
- StrawBlower
- StumpCutter
- Tedder
- TensionBelts
- Trailer
- TreePlanter
- TreePlanterActivatable
- TreeSaw
- TurnOnVehicle
- Washable
- WaterTrailer
- Weeder
- WheelRotations
- Windrower
- WoodCrusher
- WoodHarvester
- WorkArea
- WorkParticles
Engine v7.0.0.2
- General
- Entity
- Node
- Scenegraph
- Lighting
- Camera
- Shape
- Particle System
- Physics
- Spline
- Animation
- Overlays
- Sound
- Input
- XML
- Network
- Callbacks
- Text Rendering
- Terrain Detail
- Tire Track
- Editor
- Rendering
- String
- Math
- I3D
- Fillplanes
Foundation Reference
Shovel
DescriptionClass for all shovelsFunctions
- prerequisitesPresent
- preLoad
- load
- delete
- addNodeVehicleMapping
- removeNodeVehicleMapping
- readStream
- writeStream
- readUpdateStream
- writeUpdateStream
- update
- updateTick
- getAllowFillShovel
- getShovelEmptyingSpeed
- updateShovelParticleSystems
- setFillLevel
- setUnitFillLevel
- findTrailerRaycastCallback
- fillShovelFromTrigger
- loadAreaFromXML
- getIsAreaActive
- setChangedFillLevelCallback
prerequisitesPresent
DescriptionChecks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
table | specializations | specializations |
boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
17 | function Shovel.prerequisitesPresent(specializations) |
18 | return SpecializationUtil.hasSpecialization(Fillable, specializations); |
19 | end; |
preLoad
DescriptionCalled before loadingDefinition
preLoad(table savegame)Arguments
table | savegame | savegame |
24 | function Shovel:preLoad(savegame) |
25 | self.loadAreaFromXML = Shovel.loadAreaFromXML; |
26 | self.getIsAreaActive = Shovel.getIsAreaActive; |
27 | self.findTrailerRaycastCallback = Shovel.findTrailerRaycastCallback; |
28 | self.setFillLevel = Utils.overwrittenFunction(self.setFillLevel, Shovel.setFillLevel); |
29 | self.getAllowFillShovel = Shovel.getAllowFillShovel; |
30 | self.getShovelEmptyingSpeed = Shovel.getShovelEmptyingSpeed; |
31 | self.fillShovelFromGroundValue = Shovel.fillShovelFromGroundValue; |
32 | self.fillShovelFromTrigger = Shovel.fillShovelFromTrigger; |
33 | self.updateShovelParticleSystems = Shovel.updateShovelParticleSystems; |
34 | self.setChangedFillLevelCallback = Shovel.setChangedFillLevelCallback; |
35 | end; |
load
DescriptionCalled on loadingDefinition
load(table savegame)Arguments
table | savegame | savegame |
40 | function Shovel:load(savegame) |
41 | self.shovelTipReferenceNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.shovel#tipReferenceNode")); |
42 | self.shovelTipRaycastDistance = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#tipRaycastDistance"), 10); |
43 | self.shovelOpenNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.shovel#openNode")); |
44 | self.shovelOpenRotation = math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#openRotation"), 0)); |
45 | self.shovelOpenDirection = Utils.sign(Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.shovel#openDirection"), 1)); |
46 | self.shovelEmptyStartCosAngle = math.cos(math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#emptyStartAngle"), 110))); |
47 | self.shovelEmptyFullCosAngle = math.cos(math.rad(Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#emptyFullAngle"), 135))); |
48 | self.shovelEmptySpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#emptySpeed"), self:getCapacity()/2)*0.001; |
49 | self.allowFillFromShovelTrigger = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.shovel#allowFillFromShovelTrigger"), true); |
50 | |
51 | self.shovelShowFillTypeIcon = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.shovel#showFillTypeIcon"), true); |
52 | |
53 | -- Note: self.shovelFillLitersPerSecond has the wrong naming, it should be called self.shovelFillLitersPerMS) |
54 | self.shovelFillLitersPerSecond = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#fillLitersPerSecond"), 10000) * 0.001; |
55 | self.ignoreVehicleDirectionOnLoad = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.shovel#ignoreVehicleDirectionOnLoad"), false); |
56 | |
57 | |
58 | |
59 | self.pickUp = {}; |
60 | self.pickUp.node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.shovel#pickUpNode")); |
61 | self.pickUp.width = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#pickUpWidth"), 1.0); |
62 | self.pickUp.length = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#pickUpLength"), 0.5); |
63 | self.pickUp.yOffset = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#pickUpYOffset"), 0.0); |
64 | self.pickUp.pickUpRequiresMovement = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.shovel#pickUpRequiresMovement"), true); |
65 | self.pickUp.pickUpNeedsToBeTurnedOn = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.shovel#pickUpNeedsToBeTurnedOn"), false); |
66 | self.pickUp.pickUpNeedsActiveVehicle = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.shovel#pickUpNeedsActiveVehicle"), true); |
67 | |
68 | self.pickUp.remainingDelta = 0; |
69 | |
70 | local hasShovelNodes = false; |
71 | local shovelNodes = {}; |
72 | local i = 0; |
73 | while true do |
74 | local key = string.format("vehicle.shovel.node(%d)", i); |
75 | if not hasXMLProperty(self.xmlFile, key) then |
76 | break; |
77 | end; |
78 | hasShovelNodes = true; |
79 | local node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key.."#index")); |
80 | if node ~= nil then |
81 | shovelNodes[node] = true; |
82 | end; |
83 | i = i + 1; |
84 | end; |
85 | if hasShovelNodes then |
86 | self.shovelNodes = shovelNodes; |
87 | end; |
88 | |
89 | |
90 | if self.isClient then |
91 | for _, key in pairs({"empty", "fill"}) do |
92 | self[key.."EmitterShape"] = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle."..key.."ParticleSystems.emitterShape#node")); |
93 | self[key.."ReferenceParticleSystems"] = {}; |
94 | |
95 | if self[key.."EmitterShape"] ~= nil then |
96 | for _,fillUnit in pairs(self.fillUnits) do |
97 | for fillType,state in pairs(fillUnit.fillTypes) do |
98 | local particleSystems = MaterialUtil.getParticleSystem(fillType, "unloading") |
99 | if state and particleSystems ~= nil then |
100 | self[key.."ReferenceParticleSystems"][fillType] = ParticleUtil.copyParticleSystem(self.xmlFile, "vehicle."..key.."ParticleSystems.emitterShape", particleSystems, self[key.."EmitterShape"]) |
101 | end |
102 | end |
103 | end |
104 | end; |
105 | end; |
106 | |
107 | self.effectsTimeout = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#effectsTimeout"), 0.1)*1000; |
108 | self.fillEffectsTimeout = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.shovel#fillEffectsTimeout"), 0.1)*1000; |
109 | |
110 | self.shovelEmptyEffects = EffectManager:loadEffect(self.xmlFile, "vehicle.shovelEmptyEffect", self.components, self); |
111 | self.shovelEmptyEffectsRotateNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.shovelEmptyEffect#rotationNode")); |
112 | self.shovelEmptyEffectsRotateTimeOut = 0; |
113 | |
114 | self.shovelFillEffects = EffectManager:loadEffect(self.xmlFile, "vehicle.shovelFillEffect", self.components, self); |
115 | end; |
116 | |
117 | self.shovel = {}; |
118 | self.shovel.fillUnitIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.shovel#fillUnitIndex"), 1); |
119 | self.shovel.unloadInfoIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.shovel#unloadInfoIndex"), 1); |
120 | self.shovel.loadInfoIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.shovel#loadInfoIndex"), 1); |
121 | self.shovel.dischargeInfoIndex = Utils.getNoNil(getXMLInt(self.xmlFile, "vehicle.shovel#dischargeInfoIndex"), 1); |
122 | |
123 | local shovelTipTriggerId = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.shovelTipTrigger#index")); |
124 | self.shovel.allowPallets = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.shovelTipTrigger#allowPallets"), false); |
125 | |
126 | if shovelTipTriggerId ~= nil then |
127 | local shovelTipTrigger = TipTrigger:new(self.isServer, self.isClient); |
128 | if shovelTipTrigger:load(shovelTipTriggerId) then |
129 | shovelTipTrigger:addTipTriggerTarget(self); |
130 | self.shovelTipTrigger = shovelTipTrigger; |
131 | self.shovelTipTrigger:register(true); |
132 | else |
133 | shovelTipTrigger:delete(); |
134 | end |
135 | end |
136 | |
137 | self.accumulatedFillDelta = 0; |
138 | self.accumulatedEmptyDelta = 0; |
139 | |
140 | self.fillEffectActive = false; |
141 | self.sentFillEffectActive = false; |
142 | self.lastFillEffectActiveTime = 0; |
143 | |
144 | self.emptyEffectActive = false; |
145 | self.sentEmptyEffectActive = false; |
146 | self.lastEmptyEffectActiveTime = 0; |
147 | |
148 | self.curShovelTipRaycastDistance = 0; |
149 | self.sentCurShovelTipRaycastDistance = 0; |
150 | |
151 | |
152 | if self.shovelDirtyFlag == nil then |
153 | self.shovelDirtyFlag = self:getNextDirtyFlag(); |
154 | end |
155 | end; |
delete
DescriptionCalled on deletingDefinition
delete()Code
159 | function Shovel:delete() |
160 | if self.isClient then |
161 | EffectManager:deleteEffects(self.shovelFillEffects); |
162 | EffectManager:deleteEffects(self.shovelEmptyEffects); |
163 | ParticleUtil.deleteParticleSystems(self.emptyReferenceParticleSystems) |
164 | ParticleUtil.deleteParticleSystems(self.fillReferenceParticleSystems) |
165 | end; |
166 | if self.shovelTipTrigger ~= nil then |
167 | self.shovelTipTrigger:unregister(true); |
168 | self.shovelTipTrigger:delete(); |
169 | end |
170 | end; |
addNodeVehicleMapping
DescriptionAdd fillRootNode and exactFillRootNode to 'nodeToVehicle' mappingDefinition
addNodeVehicleMapping(table list)Arguments
table | list | list |
175 | function Shovel:addNodeVehicleMapping(list) |
176 | if self.shovelNodes ~= nil then |
177 | for node,_ in pairs(self.shovelNodes) do |
178 | list[node] = self; |
179 | end |
180 | end |
181 | end; |
removeNodeVehicleMapping
DescriptionRemove fillRootNode and exactFillRootNode from 'nodeToVehicle' mappingDefinition
removeNodeVehicleMapping(table list)Arguments
table | list | list |
186 | function Shovel:removeNodeVehicleMapping(list) |
187 | if self.shovelNodes ~= nil then |
188 | for node,_ in pairs(self.shovelNodes) do |
189 | list[node] = nil; |
190 | end |
191 | end |
192 | end; |
readStream
DescriptionCalled on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
integer | streamId | streamId |
integer | connection | connection |
198 | function Shovel:readStream(streamId, connection) |
199 | if connection:getIsServer() then |
200 | if streamReadBool(streamId) then |
201 | local shovelTipTriggerId = readNetworkNodeObjectId(streamId); |
202 | g_client:finishRegisterObject(self.shovelTipTrigger, shovelTipTriggerId); |
203 | end; |
204 | end; |
205 | end; |
writeStream
DescriptionCalled on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
integer | streamId | streamId |
integer | connection | connection |
211 | function Shovel:writeStream(streamId, connection) |
212 | if not connection:getIsServer() then |
213 | local hasShovelTipTrigger = self.shovelTipTrigger ~= nil; |
214 | streamWriteBool(streamId, hasShovelTipTrigger); |
215 | if hasShovelTipTrigger then |
216 | writeNetworkNodeObjectId(streamId, self.shovelTipTrigger.id); |
217 | end; |
218 | end; |
219 | end; |
readUpdateStream
DescriptionCalled on on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
integer | streamId | stream ID |
integer | timestamp | timestamp |
table | connection | connection |
226 | function Shovel:readUpdateStream(streamId, timestamp, connection) |
227 | if connection:getIsServer() then |
228 | if streamReadBool(streamId) then |
229 | self.fillEffectActive = streamReadBool(streamId); |
230 | self.emptyEffectActive = streamReadBool(streamId); |
231 | |
232 | self:updateShovelParticleSystems(self.emptyEffectActive, self.fillEffectActive); |
233 | |
234 | self.curShovelTipRaycastDistance = streamReadUIntN(streamId, 7)/128*10; |
235 | end |
236 | end |
237 | end; |
writeUpdateStream
DescriptionCalled on on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
integer | streamId | stream ID |
table | connection | connection |
integer | dirtyMask | dirty mask |
244 | function Shovel:writeUpdateStream(streamId, connection, dirtyMask) |
245 | if not connection:getIsServer() then |
246 | if streamWriteBool(streamId, bitAND(dirtyMask, self.shovelDirtyFlag) ~= 0) then |
247 | streamWriteBool(streamId, self.fillEffectActive); |
248 | streamWriteBool(streamId, self.emptyEffectActive); |
249 | |
250 | self.sentEmptyEffectActive = self.emptyEffectActive; |
251 | self.sentFillEffectActive = self.fillEffectActive; |
252 | |
253 | streamWriteUIntN(streamId, Utils.clamp(self.curShovelTipRaycastDistance/10, 0, 1)*128, 7); |
254 | self.sentCurShovelTipRaycastDistance = self.curShovelTipRaycastDistance; |
255 | end |
256 | end |
257 | end; |
update
DescriptionCalled on updateDefinition
update(float dt)Arguments
float | dt | time since last call in ms |
268 | function Shovel:update(dt) |
269 | if self:getIsActive() then |
270 | if self.shovelEmptyEffectsRotateNode ~= nil then |
271 | --if self.lastEmptyEffectActiveTime > 0 and self.lastEmptyEffectActiveTime + self.effectsTimeout < g_currentMission.time then |
272 | --self.shovelEmptyEffectsRotateTimeOut = self.shovelEmptyEffectsRotateTimeOut - dt; |
273 | --local dx,dy,dz = localDirectionToWorld(getParent(self.shovelEmptyEffectsRotateNode), 0,0,1); |
274 | --local alpha = math.acos(dy); |
275 | --setRotation(self.shovelEmptyEffectsRotateNode, -alpha+math.pi/2,0,0); |
276 | |
277 | local _,dy,dz = worldDirectionToLocal(getParent(self.shovelEmptyEffectsRotateNode), 0,1,0); |
278 | local alpha = math.atan2(dz, dy); |
279 | setRotation(self.shovelEmptyEffectsRotateNode, alpha,0,0); |
280 | --end |
281 | end |
282 | end; |
283 | end; |
updateTick
DescriptionCalled on update tickDefinition
updateTick(float dt)Arguments
float | dt | time since last call in ms |
288 | function Shovel:updateTick(dt) |
289 | if self.isServer then |
290 | -- |
291 | self.emptyEffectActive = false; |
292 | self.fillEffectActive = false; |
293 | |
294 | if (self:getIsActive() or not self.pickUp.pickUpNeedsActiveVehicle) then |
295 | -- calc movement of pickUpNode |
296 | local pickUpIsAllowed = true; |
297 | local pickUpSpeed = 0; |
298 | local pickUp = self.pickUp; |
299 | if pickUp.node ~= nil then |
300 | if Vehicle.debugRendering then |
301 | DebugUtil.drawDebugNode(pickUp.node); |
302 | end |
303 | |
304 | if self.pickUpRequiresMovement then |
305 | if pickUp.lastPos == nil then |
306 | pickUp.lastPos = {getWorldTranslation(pickUp.node)}; |
307 | end |
308 | local _,dy,dz = worldDirectionToLocal(getParent(pickUp.node), 0,1,0); |
309 | local alpha = math.atan2(dz, dy); |
310 | setRotation(pickUp.node, alpha, 0, 0); |
311 | |
312 | local x,y,z = worldToLocal(pickUp.node, pickUp.lastPos[1], pickUp.lastPos[2], pickUp.lastPos[3]); |
313 | pickUpIsAllowed = z < -0.01; |
314 | pickUpIsAllowed = pickUpIsAllowed and (alpha < math.pi/2) and (alpha > -math.pi/2); |
315 | |
316 | if pickUpIsAllowed then |
317 | pickUpSpeed = math.min(1, Utils.vector3Length(x,y,z) / (dt*0.001)); |
318 | end |
319 | |
320 | pickUp.lastPos[1], pickUp.lastPos[2], pickUp.lastPos[3] = getWorldTranslation(pickUp.node); |
321 | else |
322 | pickUpSpeed = 1; |
323 | end |
324 | |
325 | if self.pickUp.pickUpNeedsToBeTurnedOn and self.getIsTurnedOn ~= nil and not self:getIsTurnedOn() then |
326 | pickUpSpeed = 0; |
327 | end |
328 | |
329 | end |
330 | |
331 | -- check openNode |
332 | if pickUpIsAllowed then |
333 | if self.shovelOpenNode ~= nil then |
334 | local x,_,_ = getRotation(self.shovelOpenNode); |
335 | if self.shovelOpenDirection > 0 then |
336 | if x < self.shovelOpenRotation then |
337 | pickUpIsAllowed = false; |
338 | end; |
339 | else |
340 | if x > self.shovelOpenRotation then |
341 | pickUpIsAllowed = false; |
342 | end |
343 | end |
344 | end |
345 | end |
346 | |
347 | -- |
348 | local emptySpeed = self:getShovelEmptyingSpeed(); |
349 | local fillType = self:getUnitFillType(self.shovel.fillUnitIndex); |
350 | local fillLevel = self:getUnitFillLevel(self.shovel.fillUnitIndex); |
351 | if emptySpeed > 0 then |
352 | if fillLevel > 0 or self.accumulatedEmptyDelta > 0 then |
353 | if self.shovelTipReferenceNode ~= nil then |
354 | -- do raycast and empty.. |
355 | |
356 | self.trailerFound = nil; |
357 | self.objectFound = nil; |
358 | self.curShovelTipRaycastDistance = 0; |
359 | local x,y,z = getWorldTranslation(self.shovelTipReferenceNode); |
360 | raycastAll(x, y, z, 0, -1, 0, "findTrailerRaycastCallback", self.shovelTipRaycastDistance, self); |
361 | |
362 | if math.abs(self.sentCurShovelTipRaycastDistance-self.curShovelTipRaycastDistance) > 0.05 then |
363 | self:raiseDirtyFlags(self.shovelDirtyFlag); |
364 | end; |
365 | |
366 | local delta = math.min(fillLevel, emptySpeed*dt) + self.accumulatedEmptyDelta; |
367 | self.accumulatedEmptyDelta = 0; |
368 | if self.trailerFound ~= nil or self.objectFound ~= nil then |
369 | if self.trailerFound ~= nil and self.trailerFoundSupported then |
370 | self.trailerFound:resetFillLevelIfNeeded(fillType); |
371 | local oldFillLevel = self.trailerFound:getFillLevel(fillType); |
372 | |
373 | local dischargeInfos; |
374 | if self.fillVolumeDischargeInfos ~= nil and self.fillVolumeDischargeInfos[self.shovel.fillUnitIndex] ~= nil then |
375 | dischargeInfos = self.fillVolumeDischargeInfos[self.shovel.fillUnitIndex]; |
376 | for _,node in pairs(self.fillVolumeDischargeInfos[self.shovel.fillUnitIndex].nodes) do |
377 | local _,dy,_ = localDirectionToWorld(getParent(node.node), 0,0,1); |
378 | local alpha = math.acos(dy); |
379 | setRotation(node.node, -alpha+math.pi/2,0,0); |
380 | end |
381 | end |
382 | self.trailerFound:setFillLevel(oldFillLevel + delta, fillType, false, dischargeInfos); |
383 | delta = self.trailerFound:getFillLevel(fillType) - oldFillLevel; |
384 | elseif self.objectFound ~= nil and self.objectFoundSupported then |
385 | -- check if feedingThrough has enough space |
386 | local delta1 = self.objectFound:addShovelFillLevel(self, delta, fillType); |
387 | if delta1 ~= nil then |
388 | delta = delta1; |
389 | end; |
390 | else |
391 | if self.objectFound ~= nil and self.objectFound.getShovelNotAllowedText ~= nil then |
392 | local text = self.objectFound:getShovelNotAllowedText(self); |
393 | |
394 | if text ~= nil and text ~= "" then |
395 | self.tipErrorMessageTime = 3000; |
396 | self.tipErrorMessage = text; |
397 | end |
398 | end |
399 | -- do not empty shovel above unsupported trailer or shovel target |
400 | delta = 0; |
401 | end |
402 | else |
403 | -- tipAny |
404 | local minValidFillValue = TipUtil.getMinValidLiterValue(fillType); |
405 | if delta < minValidFillValue then |
406 | self.accumulatedEmptyDelta = self.accumulatedEmptyDelta + delta; |
407 | delta = 0; |
408 | if fillLevel < minValidFillValue then |
409 | self:setUnitFillLevel(self.shovel.fillUnitIndex, 0, FillUtil.FILLTYPE_UNKNOWN, true); |
410 | end |
411 | else |
412 | local sx,sy,sz; |
413 | local ex,ey,ez; |
414 | |
415 | local infos = self.fillVolumeDischargeInfos[self.shovel.fillUnitIndex]; |
416 | if infos ~= nil then |
417 | sx,sy,sz = localToWorld(infos.nodes[1].node, infos.nodes[1].width,0,0); |
418 | ex,ey,ez = localToWorld(infos.nodes[1].node, -infos.nodes[1].width,0,0); |
419 | else |
420 | local pickUp = self.pickUp; |
421 | sx,sy,sz = localToWorld(pickUp.node, pickUp.width, 0, 0); |
422 | ex,ey,ez = localToWorld(pickUp.node, -pickUp.width, 0, 0); |
423 | end |
424 | |
425 | local dropped, lineOffset = TipUtil.tipToGroundAroundLine(self, delta, fillType, sx,sy,sz, ex,ey,ez, 0, nil, self.shovel.lineOffsetDrop, true, nil); |
426 | self.shovel.lineOffsetDrop = lineOffset; |
427 | |
428 | if dropped > 0 then |
429 | if self.changedFillLevelCallback ~= nil then |
430 | if self.changedFillLevelCallbackTarget ~= nil then |
431 | self.changedFillLevelCallback(self.changedFillLevelCallbackTarget, self, dropped, fillType) |
432 | else |
433 | self.changedFillLevelCallback(self, dropped, fillType) |
434 | end |
435 | end |
436 | end |
437 | |
438 | delta = dropped; |
439 | end |
440 | end; |
441 | if delta ~= 0 then |
442 | self:setUnitFillLevel(self.shovel.fillUnitIndex, fillLevel - delta, fillType, false, self.fillVolumeUnloadInfos[self.shovel.unloadInfoIndex]); |
443 | self.emptyEffectActive = true; |
444 | end; |
445 | end; |
446 | end; |
447 | end |
448 | |
449 | -- always try to refill shovel |
450 | local fillLevel = self:getUnitFillLevel(self.shovel.fillUnitIndex); |
451 | local capacity = self:getUnitCapacity(self.shovel.fillUnitIndex); |
452 | |
453 | if self.pickUp.node ~= nil and pickUpIsAllowed and fillLevel < capacity then |
454 | local pickUp = self.pickUp; |
455 | |
456 | local length = pickUp.length; |
457 | local maxRadius = 1.75; |
458 | local zPos = math.min(pickUp.length/2, maxRadius); |
459 | |
460 | local foundFillType = false; |
461 | |
462 | while true do |
463 | local heapFillType = FillUtil.FILLTYPE_UNKNOWN; |
464 | |
465 | for i=1, 2 do |
466 | local width = i * pickUp.width / 2; |
467 | local x0,y0,z0 = localToWorld(pickUp.node, math.min(-width+zPos, 0), 0, zPos); |
468 | local x1,y1,z1 = localToWorld(pickUp.node, math.max(width-zPos, 0), 0, zPos); |
469 | |
470 | heapFillType = TipUtil.getFillTypeAtLine(x0,y0,z0, x1,y1,z1, math.min(length, maxRadius)); |
471 | if heapFillType ~= FillUtil.FILLTYPE_UNKNOWN then |
472 | break; |
473 | end; |
474 | end; |
475 | |
476 | if heapFillType ~= FillUtil.FILLTYPE_UNKNOWN and self:allowFillType(heapFillType) and self:allowUnitFillType(self.shovel.fillUnitIndex, heapFillType) then |
477 | local emptyFactor = 1.0 - (emptySpeed / self.shovelEmptySpeed); |
478 | local fillFactor = pickUpSpeed * emptyFactor; -- * 0.001 * dt; |
479 | |
480 | local pickUpDelta = math.min(emptyFactor*(capacity - fillLevel), self.shovelFillLitersPerSecond*dt + self.accumulatedFillDelta); |
481 | |
482 | pickUpDelta = pickUpDelta * math.min(1, fillFactor); |
483 | |
484 | if length > maxRadius * 2 then |
485 | pickUpDelta = pickUpDelta / math.floor(self.pickUp.length / (maxRadius * 2)); |
486 | end; |
487 | |
488 | |
489 | local sx,sy,sz = localToWorld(pickUp.node, -pickUp.width, pickUp.yOffset, zPos); |
490 | local ex,ey,ez = localToWorld(pickUp.node, pickUp.width, pickUp.yOffset, zPos); |
491 | |
492 | local sh = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx,sy,sz); |
493 | local eh = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, ex,ey,ez); |
494 | if sh < 0.1 then |
495 | sy = sh; |
496 | end |
497 | if eh < 0.1 then |
498 | ey = eh; |
499 | end |
500 | |
501 | local innerRadius = math.min(length, maxRadius); --0.05; |
502 | local radius = nil; --1.0; |
503 | |
504 | pickUp.remainingDelta = pickUp.remainingDelta + pickUpDelta; |
505 | if pickUp.remainingDelta > 20 then |
506 | local fillLevel = self:getUnitFillLevel(self.shovel.fillUnitIndex); |
507 | local capacity = self:getUnitCapacity(self.shovel.fillUnitIndex); |
508 | pickUpDelta = math.min(pickUp.remainingDelta, capacity-fillLevel); |
509 | pickUp.remainingDelta = pickUp.remainingDelta - pickUpDelta; |
510 | else |
511 | pickUpDelta = 0; |
512 | end; |
513 | |
514 | -- (vehicle, delta, fillType, sx,sy,sz, ex,ey,ez, innerRadius, radius, lineOffset, limitToLineHeight, occlusionAreas) |
515 | local fillDelta, lineOffset = TipUtil.tipToGroundAroundLine(self, -pickUpDelta, heapFillType, sx,sy,sz, ex,ey,ez, innerRadius, radius, self.shovel.lineOffsetPickUp, true, nil); |
516 | self.shovel.lineOffsetPickUp = lineOffset; |
517 | |
518 | self.accumulatedFillDelta = math.min(pickUpDelta + fillDelta, TipUtil.getMinValidLiterValue(heapFillType)); |
519 | |
520 | if fillDelta < 0 then |
521 | self:setUnitFillLevel(self.shovel.fillUnitIndex, fillLevel - fillDelta, heapFillType, false, self.fillVolumeLoadInfos[self.shovel.unloadInfoIndex]); |
522 | |
523 | self.fillEffectActive = true; |
524 | |
525 | if self.changedFillLevelCallback ~= nil then |
526 | if self.changedFillLevelCallbackTarget ~= nil then |
527 | self.changedFillLevelCallback(self.changedFillLevelCallbackTarget, self, fillDelta, heapFillType); |
528 | else |
529 | self.changedFillLevelCallback(self, fillDelta, heapFillType); |
530 | end |
531 | end |
532 | end |
533 | |
534 | if foundFillType then |
535 | break; |
536 | end; |
537 | foundFillType = true; |
538 | else |
539 | self.accumulatedFillDelta = 0; |
540 | end |
541 | |
542 | if length > maxRadius * 2 then |
543 | length = length - maxRadius * 2; |
544 | if length > maxRadius * 2 then |
545 | zPos = zPos + maxRadius * 2; |
546 | else |
547 | zPos = zPos + (pickUp.length-zPos+maxRadius)/2; |
548 | length = pickUp.length-zPos; |
549 | end; |
550 | else |
551 | break; |
552 | end; |
553 | end; |
554 | end |
555 | end; |
556 | |
557 | if self.fillEffectActive ~= self.sentFillEffectActive or self.emptyEffectActive ~= self.sentEmptyEffectActive then |
558 | self:raiseDirtyFlags(self.shovelDirtyFlag); |
559 | self:updateShovelParticleSystems(self.emptyEffectActive, self.fillEffectActive); |
560 | end; |
561 | |
562 | end; |
563 | |
564 | if self.isClient then |
565 | |
566 | if not self.fillEffectActive and self.lastFillEffectActiveTime > 0 and self.lastFillEffectActiveTime + self.effectsTimeout < g_currentMission.time then |
567 | self.lastFillEffectActiveTime = 0; |
568 | if self.lastFillPS ~= nil then |
569 | ParticleUtil.setEmittingState(self.lastFillPS, false); |
570 | end |
571 | if self.shovelFillEffects ~= nil then |
572 | EffectManager:stopEffects(self.shovelFillEffects); |
573 | end |
574 | end |
575 | |
576 | if not self.emptyEffectActive and self.lastEmptyEffectActiveTime > 0 and self.lastEmptyEffectActiveTime + self.fillEffectsTimeout < g_currentMission.time then |
577 | self.lastEmptyEffectActiveTime = 0; |
578 | if self.lastEmptyPS ~= nil then |
579 | ParticleUtil.setEmittingState(self.lastEmptyPS, false); |
580 | end |
581 | if self.shovelEmptyEffects ~= nil then |
582 | EffectManager:stopEffects(self.shovelEmptyEffects); |
583 | end |
584 | end |
585 | |
586 | if self.emptyEffectActive then |
587 | if self.shovelEmptyEffects ~= nil then |
588 | for _, effect in pairs(self.shovelEmptyEffects) do |
589 | effect:setUnloadingDistance(self.curShovelTipRaycastDistance); |
590 | end; |
591 | end |
592 | |
593 | if self.lastEmptyPS ~= nil then |
594 | self.curShovelTipRaycastDistance = self.curShovelTipRaycastDistance - 0.3; -- size of particles |
595 | |
596 | local dx,dy,dz = localDirectionToWorld(self.emptyEmitterShape, 0,0,1); |
597 | local factor = 1-(math.abs(dy)/1); |
598 | local maxFactor = 28*math.sqrt(self.curShovelTipRaycastDistance)*0.3162/100; |
599 | factor = 1 + factor * maxFactor; |
600 | |
601 | local lifespan = math.sqrt(self.curShovelTipRaycastDistance)*379.47; -- on gravity y factor of -1.5 |
602 | |
603 | ParticleUtil.setParticleLifespan(self.lastEmptyPS, lifespan/factor); |
604 | end |
605 | end; |
606 | end; |
607 | end; |
getAllowFillShovel
DescriptionReturns fill shovel is allowedDefinition
getAllowFillShovel(integer fillType)Arguments
integer | fillType | fill type |
boolean | allowed | fill shovel is allowed |
616 | function Shovel:getAllowFillShovel(fillType) |
617 | if self.shovelOpenNode ~= nil then |
618 | local x,_,_ = getRotation(self.shovelOpenNode); |
619 | if self.shovelOpenDirection > 0 then |
620 | if x < self.shovelOpenRotation then |
621 | return false; |
622 | end; |
623 | else |
624 | if x > self.shovelOpenRotation then |
625 | return false; |
626 | end; |
627 | end |
628 | end; |
629 | if self:getShovelEmptyingSpeed() > 0 then |
630 | return false; |
631 | end; |
632 | return self:allowFillType(fillType, false) |
633 | end; |
getShovelEmptyingSpeed
DescriptionReturns shovel emptying speed depending on the shovel world rotationDefinition
getShovelEmptyingSpeed()Return Values
float | emptyingSpeed | shovel emptying speed |
638 | function Shovel:getShovelEmptyingSpeed() |
639 | if self.shovelOpenNode ~= nil then |
640 | local x,_,_ = getRotation(self.shovelOpenNode); |
641 | if self.shovelOpenDirection > 0 then |
642 | if x < self.shovelOpenRotation then |
643 | return 0; |
644 | end; |
645 | else |
646 | if x > self.shovelOpenRotation then |
647 | return 0; |
648 | end; |
649 | end |
650 | end; |
651 | if self.shovelTipReferenceNode ~= nil then |
652 | local dx,dy,dz = localDirectionToWorld(self.shovelTipReferenceNode, 0,0,1); |
653 | -- more than 55deg rotated towards the ground |
654 | --if dy < -0.573576436351 then |
655 | if dy < self.shovelEmptyStartCosAngle then |
656 | local scale = math.min((self.shovelEmptyStartCosAngle-dy) / (self.shovelEmptyStartCosAngle - self.shovelEmptyFullCosAngle), 1); |
657 | return self.shovelEmptySpeed * scale; |
658 | end; |
659 | end; |
660 | return 0; |
661 | end; |
updateShovelParticleSystems
DescriptionUpdate shovel particle systemsDefinition
updateShovelParticleSystems(boolean emptyEffectActive, boolean fillEffectActive)Arguments
boolean | emptyEffectActive | empty effect is active |
boolean | fillEffectActive | fill effect is active |
667 | function Shovel:updateShovelParticleSystems(emptyEffectActive, fillEffectActive) |
668 | if self.isClient then |
669 | local fillType = self:getUnitFillType(self.shovel.fillUnitIndex); |
670 | |
671 | if emptyEffectActive then |
672 | self.lastEmptyEffectActiveTime = g_currentMission.time; |
673 | local ps = self.emptyReferenceParticleSystems[fillType]; |
674 | if self.emptyEmitterShape ~= nil and ps ~= nil then |
675 | ParticleUtil.resetNumOfEmittedParticles(ps); |
676 | ParticleUtil.setEmittingState(ps, true); |
677 | self.lastEmptyPS = ps; |
678 | elseif self.shovelEmptyEffects ~= nil then |
679 | EffectManager:setFillType(self.shovelEmptyEffects, fillType) |
680 | EffectManager:startEffects(self.shovelEmptyEffects); |
681 | end; |
682 | end |
683 | |
684 | if fillEffectActive then |
685 | self.lastFillEffectActiveTime = g_currentMission.time; |
686 | local ps = self.fillReferenceParticleSystems[fillType]; |
687 | if self.fillEmitterShape ~= nil and ps ~= nil then |
688 | ParticleUtil.resetNumOfEmittedParticles(ps); |
689 | ParticleUtil.setEmittingState(ps, true); |
690 | self.lastFillPS = ps; |
691 | elseif self.shovelFillEffects ~= nil then |
692 | EffectManager:setFillType(self.shovelFillEffects, fillType) |
693 | EffectManager:startEffects(self.shovelFillEffects); |
694 | end; |
695 | end |
696 | |
697 | end; |
698 | end |
setFillLevel
DescriptionSet fill level (filling all fill units that allow given fill type)Definition
setFillLevel(float filllevel, integer fillType, boolean force, table fillInfo)Arguments
float | filllevel | new fill level |
integer | fillType | fill type |
boolean | force | force action |
table | fillInfo | fill info for fill volume |
706 | function Shovel:setFillLevel(oldSetFillLevel, fillLevel, fillType, force, fillInfo) |
707 | if oldSetFillLevel ~= nil then |
708 | oldSetFillLevel(self, fillLevel, fillType, force, fillInfo); |
709 | end; |
710 | end; |
setUnitFillLevel
DescriptionSet unit fill levelDefinition
setUnitFillLevel(integer fillUnitIndex, float fillLevel, integer fillType, boolean force, table fillInfo)Arguments
integer | fillUnitIndex | index of fill unit |
float | fillLevel | new fill level |
integer | fillType | fill type |
boolean | force | force action |
table | fillInfo | fill info for fill volume |
719 | function Shovel:setUnitFillLevel(superFunc, fillUnitIndex, fillLevel, fillType, force, fillInfo) |
720 | local fillLevel = self.fillUnits[self.shovel.fillUnitIndex].fillLevel; |
721 | |
722 | superFunc(self, fillUnitIndex, fillLevel, fillType, force, fillInfo); |
723 | |
724 | -- adjust emit rate of empty PS |
725 | if self.lastEmptyPS ~= nil and fillUnitIndex == self.shovel.fillUnitIndex then |
726 | local delta = self.fillUnits[self.shovel.fillUnitIndex].fillLevel - fillLevel; |
727 | if delta < 0 then |
728 | local f = delta / self.emptySpeed; |
729 | ParticleUtil.setEmitCountScale(self.lastEmptyPS, f); |
730 | end |
731 | end |
732 | end |
findTrailerRaycastCallback
DescriptionRaycast callbackDefinition
findTrailerRaycastCallback(integer transformId, float x, float y, float z, float distance)Arguments
integer | transformId | id raycasted object |
float | x | x raycast position |
float | y | y raycast position |
float | z | z raycast position |
float | distance | distance to raycast position |
741 | function Shovel:findTrailerRaycastCallback(transformId, x, y, z, distance) |
742 | local fillType = self.fillUnits[self.shovel.fillUnitIndex].currentFillType; |
743 | |
744 | if self.trailerFound == nil then |
745 | self.curShovelTipRaycastDistance = distance; |
746 | end; |
747 | |
748 | local trailer = g_currentMission.objectToTrailer[transformId]; |
749 | if trailer ~= nil and trailer ~= self then |
750 | if trailer:allowFillType(fillType) and trailer.getAllowFillFromAir ~= nil and trailer:getAllowFillFromAir() then |
751 | self.trailerFound = trailer; |
752 | self.trailerFoundSupported = true; |
753 | else |
754 | if self.trailerFound == nil then |
755 | self.trailerFound = trailer; |
756 | self.trailerFoundSupported = false; |
757 | end |
758 | end |
759 | |
760 | --get distance to trailer ground, not to exactFillRootNode |
761 | if transformId ~= trailer.exactFillRootNode then |
762 | self.curShovelTipRaycastDistance = distance; |
763 | end; |
764 | end; |
765 | local object = g_currentMission:getNodeObject(transformId); |
766 | if object ~= nil then |
767 | -- fix for greenhouse placeable |
768 | if object.manurePlaneObject ~= nil and transformId == object.manurePlaneCollisionNode then |
769 | object = object.manurePlaneObject; |
770 | end |
771 | end; |
772 | if object ~= nil and object ~= self and object.addShovelFillLevel ~= nil and object.getAllowShovelFillType ~= nil then |
773 | if object:getAllowShovelFillType(fillType) then |
774 | self.objectFound = object; |
775 | self.objectFoundSupported = true; |
776 | return false; |
777 | else |
778 | if self.objectFound == nil then |
779 | self.objectFound = object; |
780 | self.objectFoundSupported = false; |
781 | end |
782 | end |
783 | end; |
784 | |
785 | return true; |
786 | end; |
fillShovelFromTrigger
DescriptionFill shovel from triggerDefinition
fillShovelFromTrigger(table shovelTrigger, float deltaFillLevel, integer fillType, float dt)Arguments
table | shovelTrigger | shovelTrigger |
float | deltaFillLevel | fill level to fill |
integer | fillType | fill type to fill |
float | dt | time since last call in ms |
float | realFillDelta | real fill delta |
795 | function Shovel:fillShovelFromTrigger(shovelTrigger, deltaFillLevel, fillType, dt) |
796 | if self:getAllowFillShovel(fillType) and (self.movingDirection == self.vehicleMovingDirection or self.ignoreVehicleDirectionOnLoad) then |
797 | local fillFactor = 1; |
798 | if not self.ignoreVehicleDirectionOnLoad then |
799 | fillFactor = math.min(math.abs(self:getLastSpeed()), 5)*0.1; -- <=5km/h = 50% speed |
800 | end; |
801 | deltaFillLevel = math.min(deltaFillLevel, self.shovelFillLitersPerSecond*dt*fillFactor); |
802 | self:resetFillLevelIfNeeded(fillType); |
803 | |
804 | local oldFillLevel = self:getFillLevel(fillType); |
805 | |
806 | local loadInfos = self.fillVolumeLoadInfos[1]; |
807 | if loadInfos == nil then |
808 | self:setFillLevel(oldFillLevel + deltaFillLevel, fillType); |
809 | else |
810 | self:setFillLevel(oldFillLevel + deltaFillLevel, fillType, false, loadInfos); |
811 | end; |
812 | |
813 | local finalDelta = self:getFillLevel(fillType) - oldFillLevel; |
814 | |
815 | return finalDelta; |
816 | end |
817 | return 0; |
818 | end; |
loadAreaFromXML
DescriptionLoad area from xmlDefinition
loadAreaFromXML(table area, integer xmlFile, string key)Arguments
table | area | area |
integer | xmlFile | id of xml object |
string | key | key |
boolean | areaLoaded | area successfully loaded |
826 | function Shovel:loadAreaFromXML(area, xmlFile, key) |
827 | local start = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#startIndex")); |
828 | local width = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#widthIndex")); |
829 | local height = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#heightIndex")); |
830 | |
831 | if start ~= nil and width ~= nil and height ~= nil then |
832 | area.start = start; |
833 | area.width = width; |
834 | area.height = height; |
835 | return true; |
836 | end; |
837 | return false; |
838 | end; |
getIsAreaActive
DescriptionReturns if area is activeDefinition
getIsAreaActive(table area)Arguments
table | area | area to check |
boolean | isActive | area is active |
844 | function Shovel:getIsAreaActive(area) |
845 | return true; |
846 | end; |
setChangedFillLevelCallback
DescriptionSet fill level changed callback (e.g. by bunker silo)Definition
setChangedFillLevelCallback(function callback, table callbackTarget)Arguments
function | callback | callback |
table | callbackTarget | callback target |
852 | function Shovel:setChangedFillLevelCallback(callback, callbackTarget) |
853 | self.changedFillLevelCallback = callback; |
854 | self.changedFillLevelCallbackTarget = callbackTarget; |
855 | end |