385 | function ConveyorBelt:getFillUnitAllowsFillType(superFunc, fillUnitIndex, fillType) |
386 | if not superFunc(self, fillUnitIndex, fillType) then |
387 | return false |
388 | end |
389 | |
390 | if self.getCurrentDischargeNode ~= nil then |
391 | local currentDischargeNode = self:getCurrentDischargeNode() |
392 | if currentDischargeNode.fillUnitIndex == fillUnitIndex then |
393 | local object, targetFillUnitIndex = currentDischargeNode.dischargeHitObject, currentDischargeNode.dischargeHitObjectUnitIndex |
394 | if object ~= nil and object.getFillUnitAllowsFillType ~= nil and targetFillUnitIndex ~= nil then |
395 | if currentDischargeNode.fillTypeConverter ~= nil then |
396 | local conversion = currentDischargeNode.fillTypeConverter[fillType] |
397 | if conversion ~= nil then |
398 | if object:getFillUnitAllowsFillType(targetFillUnitIndex, conversion.targetFillTypeIndex) then |
399 | return true |
400 | end |
401 | end |
402 | end |
403 | |
404 | return object:getFillUnitAllowsFillType(targetFillUnitIndex, fillType) |
405 | end |
406 | end |
407 | end |
408 | |
409 | return true |
410 | end |
414 | function ConveyorBelt:getFillUnitFreeCapacity(superFunc, fillUnitIndex, fillTypeIndex, farmId) |
415 | local freeCapacity = superFunc(self, fillUnitIndex, fillTypeIndex, farmId) |
416 | |
417 | if self.getCurrentDischargeNode ~= nil then |
418 | local currentDischargeNode = self:getCurrentDischargeNode() |
419 | if currentDischargeNode.fillUnitIndex == fillUnitIndex then |
420 | local object, targetFillUnitIndex = currentDischargeNode.dischargeHitObject, currentDischargeNode.dischargeHitObjectUnitIndex |
421 | if object ~= nil and object.getFillUnitFreeCapacity ~= nil and targetFillUnitIndex ~= nil then |
422 | return freeCapacity + object:getFillUnitFreeCapacity(targetFillUnitIndex, fillTypeIndex, farmId) |
423 | end |
424 | end |
425 | end |
426 | |
427 | return freeCapacity |
428 | end |
23 | function ConveyorBelt.initSpecialization() |
24 | local schema = Vehicle.xmlSchema |
25 | schema:setXMLSpecializationType("ConveyorBelt") |
26 | |
27 | AnimationManager.registerAnimationNodesXMLPaths(schema, "vehicle.conveyorBelt.animationNodes") |
28 | EffectManager.registerEffectXMLPaths(schema, "vehicle.conveyorBelt.effects") |
29 | |
30 | schema:register(XMLValueType.INT, "vehicle.conveyorBelt#dischargeNodeIndex", "Discharge node index", 1) |
31 | schema:register(XMLValueType.FLOAT, "vehicle.conveyorBelt#startPercentage", "Start unloading percentage", 0.9) |
32 | |
33 | schema:register(XMLValueType.NODE_INDEX, "vehicle.conveyorBelt.offset(?)#movingToolNode", "Moving tool node") |
34 | schema:register(XMLValueType.INT, "vehicle.conveyorBelt.offset(?).effect(?)#index", "Index of effect", 0) |
35 | schema:register(XMLValueType.FLOAT, "vehicle.conveyorBelt.offset(?).effect(?)#minOffset", "Min. offset", 0) |
36 | schema:register(XMLValueType.FLOAT, "vehicle.conveyorBelt.offset(?).effect(?)#maxOffset", "Max. offset", 1) |
37 | schema:register(XMLValueType.BOOL, "vehicle.conveyorBelt.offset(?).effect(?)#inverted", "Is inverted", false) |
38 | |
39 | SoundManager.registerSampleXMLPaths(schema, "vehicle.conveyorBelt.sounds", "belt") |
40 | |
41 | schema:setXMLSpecializationType() |
42 | end |
432 | function ConveyorBelt:onFillUnitFillLevelChanged(fillUnitIndex, fillLevelDelta, fillType, toolType, fillPositionData, appliedDelta) |
433 | local spec = self.spec_conveyorBelt |
434 | if spec.fillUnitIndex == fillUnitIndex then |
435 | local fillLevel = self:getFillUnitFillLevel(fillUnitIndex) |
436 | |
437 | if fillLevelDelta > 0 then |
438 | spec.morphStartPos = 0 |
439 | spec.morphEndPos = math.max(spec.morphEndPos, fillLevel / self:getFillUnitCapacity(fillUnitIndex)) |
440 | spec.isEffectDirty = true |
441 | end |
442 | |
443 | if fillLevelDelta ~= 0 then |
444 | spec.scrollUpdateTime = 100 |
445 | end |
446 | |
447 | if fillLevel == 0 then |
448 | g_effectManager:stopEffects(spec.effects) |
449 | spec.morphStartPos = 0 |
450 | spec.morphEndPos = 0 |
451 | spec.isEffectDirty = true |
452 | else |
453 | g_effectManager:setFillType(spec.effects, fillType) |
454 | g_effectManager:startEffects(spec.effects) |
455 | end |
456 | end |
457 | end |
77 | function ConveyorBelt:onLoad(savegame) |
78 | local spec = self.spec_conveyorBelt |
79 | |
80 | if self.isClient then |
81 | spec.animationNodes = g_animationManager:loadAnimations(self.xmlFile, "vehicle.conveyorBelt.animationNodes", self.components, self, self.i3dMappings) |
82 | |
83 | spec.samples = {} |
84 | spec.samples.belt = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.conveyorBelt.sounds", "belt", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
85 | end |
86 | |
87 | spec.effects = g_effectManager:loadEffect(self.xmlFile, "vehicle.conveyorBelt.effects", self.components, self, self.i3dMappings) |
88 | spec.currentDelay = 0 |
89 | table.sort(spec.effects, function(effect1, effect2) return effect1.startDelay < effect2.startDelay end) |
90 | |
91 | for _, effect in pairs(spec.effects) do |
92 | if effect.planeFadeTime ~= nil then |
93 | spec.currentDelay = spec.currentDelay + effect.planeFadeTime |
94 | end |
95 | if effect.setScrollUpdate ~= nil then |
96 | effect:setScrollUpdate(false) |
97 | end |
98 | end |
99 | spec.maxDelay = spec.currentDelay |
100 | |
101 | spec.morphStartPos = 0 |
102 | spec.morphEndPos = 0 |
103 | spec.isEffectDirty = true |
104 | spec.emptyFactor = 1 |
105 | spec.scrollUpdateTime = 0 |
106 | spec.lastScrollUpdate = false |
107 | |
108 | spec.dischargeNodeIndex = self.xmlFile:getValue("vehicle.conveyorBelt#dischargeNodeIndex", 1) |
109 | self:setCurrentDischargeNodeIndex(spec.dischargeNodeIndex) |
110 | local dischargeNode = self:getDischargeNodeByIndex(spec.dischargeNodeIndex) |
111 | local capacity = self:getFillUnitCapacity(dischargeNode.fillUnitIndex) |
112 | spec.fillUnitIndex = dischargeNode.fillUnitIndex |
113 | spec.startFillLevel = capacity * self.xmlFile:getValue("vehicle.conveyorBelt#startPercentage", 0.9) |
114 | |
115 | local i = 0 |
116 | while true do |
117 | local key = string.format("vehicle.conveyorBelt.offset(%d)", i) |
118 | if not self.xmlFile:hasProperty(key) then |
119 | break |
120 | end |
121 | |
122 | local movingToolNode = self.xmlFile:getValue(key.."#movingToolNode", nil, self.components, self.i3dMappings) |
123 | if movingToolNode ~= nil then |
124 | if spec.offsets == nil then |
125 | spec.offsets = {} |
126 | end |
127 | |
128 | local offset = {} |
129 | offset.lastState = 0 |
130 | offset.movingToolNode = movingToolNode |
131 | offset.effects = {} |
132 | local j = 0 |
133 | while true do |
134 | local effectKey = string.format(key..".effect(%d)", j) |
135 | if not self.xmlFile:hasProperty(effectKey) then |
136 | break |
137 | end |
138 | |
139 | local effectIndex = self.xmlFile:getValue(effectKey.."#index", 0) |
140 | local effect = spec.effects[effectIndex] |
141 | if effect ~= nil and effect.setOffset ~= nil then |
142 | local entry = {} |
143 | entry.effect = effect |
144 | entry.minValue = self.xmlFile:getValue(effectKey.."#minOffset", 0) * 1000 |
145 | entry.maxValue = self.xmlFile:getValue(effectKey.."#maxOffset", 1) * 1000 |
146 | entry.inverted = self.xmlFile:getValue(effectKey.."#inverted", false) |
147 | table.insert(offset.effects, entry) |
148 | else |
149 | Logging.xmlWarning(self.xmlFile, "Effect index '%d' not found at '%s'!", effectIndex, effectKey) |
150 | end |
151 | j = j + 1 |
152 | end |
153 | |
154 | table.insert(spec.offsets, offset) |
155 | else |
156 | Logging.xmlWarning(self.xmlFile, "Missing movingToolNode for conveyor offset '%s'!", key) |
157 | end |
158 | i = i + 1 |
159 | end |
160 | end |
461 | function ConveyorBelt:onMovingToolChanged(movingTool, speed, dt) |
462 | local spec = self.spec_conveyorBelt |
463 | if spec.offsets ~= nil then |
464 | local offset = spec.movingToolToOffset[movingTool] |
465 | |
466 | if offset ~= nil then |
467 | local state = Cylindered.getMovingToolState(self, movingTool) |
468 | if state ~= offset.lastState then |
469 | local updateDelay = false |
470 | |
471 | for _, entry in pairs(offset.effects) do |
472 | local effectState = state |
473 | if entry.inverted then |
474 | effectState = 1 - effectState |
475 | end |
476 | |
477 | entry.effect:setOffset(MathUtil.lerp(entry.minValue, entry.maxValue, effectState)) |
478 | updateDelay = true |
479 | spec.isEffectDirty = true |
480 | end |
481 | |
482 | if updateDelay then |
483 | spec.currentDelay = 0 |
484 | for _, effect in pairs(spec.effects) do |
485 | if effect.planeFadeTime ~= nil then |
486 | spec.currentDelay = spec.currentDelay + effect.planeFadeTime - effect.offset |
487 | end |
488 | end |
489 | end |
490 | |
491 | offset.lastState = state |
492 | end |
493 | end |
494 | end |
495 | end |
164 | function ConveyorBelt:onPostLoad(savegame) |
165 | local spec = self.spec_conveyorBelt |
166 | |
167 | -- we need to check movingTool in post load to avoid order dependencies |
168 | if spec.offsets ~= nil then |
169 | if self.getMovingToolByNode ~= nil then |
170 | spec.movingToolToOffset = {} |
171 | for i=#spec.offsets, 1, -1 do |
172 | local offset = spec.offsets[i] |
173 | local movingTool = self:getMovingToolByNode(offset.movingToolNode) |
174 | if movingTool ~= nil then |
175 | offset.movingTool = movingTool |
176 | spec.movingToolToOffset[movingTool] = offset |
177 | ConveyorBelt.onMovingToolChanged(self, movingTool, 0, 0) |
178 | else |
179 | Logging.xmlWarning(self.xmlFile, "No movingTool node '%s' defined for conveyor offset '%d'!", getName(offset.movingToolNode), i) |
180 | table.remove(spec.offsets, i) |
181 | end |
182 | end |
183 | |
184 | if #spec.offsets == 0 then |
185 | spec.offsets = nil |
186 | spec.movingToolToOffset = nil |
187 | end |
188 | else |
189 | Logging.xmlError(self.xmlFile, "'Cylindered' specialization is required to use conveyorBelt offsets!") |
190 | spec.offsets = nil |
191 | end |
192 | end |
193 | end |
209 | function ConveyorBelt:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
210 | local spec = self.spec_conveyorBelt |
211 | |
212 | local doScrollUpdate = spec.scrollUpdateTime > 0 |
213 | if doScrollUpdate ~= spec.lastScrollUpdate then |
214 | if self.isClient then |
215 | if doScrollUpdate then |
216 | g_animationManager:startAnimations(spec.animationNodes) |
217 | g_soundManager:playSample(spec.samples.belt) |
218 | else |
219 | g_animationManager:stopAnimations(spec.animationNodes) |
220 | g_soundManager:stopSample(spec.samples.belt) |
221 | end |
222 | |
223 | for _, effect in pairs(spec.effects) do |
224 | if effect.setScrollUpdate ~= nil then |
225 | effect:setScrollUpdate(doScrollUpdate) |
226 | end |
227 | end |
228 | end |
229 | |
230 | spec.lastScrollUpdate = doScrollUpdate |
231 | end |
232 | spec.scrollUpdateTime = math.max(spec.scrollUpdateTime - dt, 0) |
233 | |
234 | local isBeltActive = self:getDischargeState() ~= Dischargeable.DISCHARGE_STATE_OFF |
235 | if isBeltActive then |
236 | local fillLevel = self:getFillUnitFillLevel(spec.fillUnitIndex) |
237 | if fillLevel > 0.0001 then |
238 | local movedFactor = dt / spec.currentDelay |
239 | spec.morphStartPos = MathUtil.clamp(spec.morphStartPos + movedFactor, 0, 1) |
240 | spec.morphEndPos = MathUtil.clamp(spec.morphEndPos + movedFactor, 0, 1) |
241 | |
242 | -- we calculate the empty factor based on visual effect mesh and filllevel to get a smooth transition |
243 | local fillFactor = fillLevel/self:getFillUnitCapacity(spec.fillUnitIndex) |
244 | local visualFactor = spec.morphEndPos-spec.morphStartPos |
245 | |
246 | spec.emptyFactor = 1 |
247 | if visualFactor > fillFactor then |
248 | spec.emptyFactor = MathUtil.clamp(fillFactor/visualFactor, 0, 1) |
249 | else |
250 | local offset = fillFactor - visualFactor |
251 | spec.offset = offset |
252 | spec.morphStartPos = MathUtil.clamp(spec.morphStartPos - (offset/((1-spec.morphStartPos)*spec.currentDelay))*dt , 0, 1) |
253 | end |
254 | |
255 | spec.isEffectDirty = true |
256 | spec.scrollUpdateTime = dt * 3 |
257 | end |
258 | end |
259 | |
260 | if doScrollUpdate then |
261 | self:raiseActive() |
262 | end |
263 | |
264 | if self.isClient then |
265 | if spec.isEffectDirty then |
266 | for _, effect in pairs(spec.effects) do |
267 | if effect.setMorphPosition ~= nil then |
268 | local effectStart = effect.startDelay/spec.currentDelay |
269 | local effectEnd = (effect.startDelay+effect.planeFadeTime-effect.offset)/spec.currentDelay |
270 | local offsetFactor = effect.offset/effect.planeFadeTime |
271 | |
272 | local startMorphFactor = (spec.morphStartPos-effectStart)/(effectEnd-effectStart) |
273 | local startMorph = MathUtil.clamp(offsetFactor + startMorphFactor*(1-offsetFactor), offsetFactor, 1) |
274 | |
275 | local endMorphFactor = (spec.morphEndPos-effectStart)/(effectEnd-effectStart) |
276 | local endMorph = MathUtil.clamp(offsetFactor + endMorphFactor*(1-offsetFactor), offsetFactor, 1) |
277 | |
278 | --renderText(0.6, 0.8-i*0.015, 0.012, string.format("%d: effectStart %.4f effectEnd %.4f -> startMorph %.4f endMorph %.4f | offset %.4f | %.4f %.4f", i, effectStart, effectEnd, startMorph, endMorph, effect.offset/effect.planeFadeTime, (spec.morphStartPos-effectStart)/(effectEnd-effectStart), (spec.morphEndPos-effectStart)/(effectEnd-effectStart))) |
279 | effect:setMorphPosition(startMorph, endMorph) |
280 | end |
281 | end |
282 | spec.isEffectDirty = false |
283 | end |
284 | end |
285 | end |