16 | function Tedder.initSpecialization() |
17 | g_workAreaTypeManager:addWorkAreaType("tedder", false) |
18 | |
19 | local schema = Vehicle.xmlSchema |
20 | schema:setXMLSpecializationType("Tedder") |
21 | |
22 | schema:register(XMLValueType.STRING, "vehicle.tedder#fillTypeConverter", "Fill type converter name") |
23 | EffectManager.registerEffectXMLPaths(schema, "vehicle.tedder.effects.effect(?)") |
24 | schema:register(XMLValueType.INT, "vehicle.tedder.effects.effect(?)#workAreaIndex", "Work area index", 1) |
25 | |
26 | AnimationManager.registerAnimationNodesXMLPaths(schema, "vehicle.tedder.animationNodes") |
27 | |
28 | schema:register(XMLValueType.INT, WorkArea.WORK_AREA_XML_KEY .. ".tedder#dropWindrowWorkAreaIndex", "Drop work area index", 1) |
29 | schema:register(XMLValueType.INT, WorkArea.WORK_AREA_XML_CONFIG_KEY .. ".tedder#dropWindrowWorkAreaIndex", "Drop work area index", 1) |
30 | |
31 | schema:setXMLSpecializationType() |
32 | end |
278 | function Tedder:loadWorkAreaFromXML(superFunc, workArea, xmlFile, key) |
279 | local retValue = superFunc(self, workArea, xmlFile, key) |
280 | |
281 | if workArea.type == WorkAreaType.DEFAULT then |
282 | workArea.type = WorkAreaType.TEDDER |
283 | end |
284 | |
285 | if workArea.type == WorkAreaType.TEDDER then |
286 | workArea.dropWindrowWorkAreaIndex = xmlFile:getValue(key .. ".tedder#dropWindrowWorkAreaIndex", 1) |
287 | workArea.litersToDrop = 0 |
288 | workArea.lastPickupLiters = 0 |
289 | workArea.lastDropFillType = FillType.UNKNOWN |
290 | workArea.lastDroppedLiters = 0 |
291 | workArea.tedderParticlesActive = false |
292 | workArea.tedderParticlesActiveSent = false |
293 | |
294 | local spec = self.spec_tedder |
295 | if spec.tedderWorkAreaFillTypes == nil then |
296 | spec.tedderWorkAreaFillTypes = {} |
297 | end |
298 | table.insert(spec.tedderWorkAreaFillTypes, FruitType.UNKNOWN) |
299 | workArea.tedderWorkAreaIndex = #spec.tedderWorkAreaFillTypes |
300 | end |
301 | |
302 | return retValue |
303 | end |
77 | function Tedder:onLoad(savegame) |
78 | local spec = self.spec_tedder |
79 | |
80 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.turnedOnRotationNodes.turnedOnRotationNode#type", "vehicle.tedder.animationNodes.animationNode", "tedder") --FS17 to FS19 |
81 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.tedder.sounds", "vehicle.turnOnVehicle.sounds.work") --FS17 to FS19 |
82 | |
83 | spec.fillTypeConverters = {} |
84 | spec.fillTypeConvertersReverse = {} |
85 | local converter = self.xmlFile:getValue("vehicle.tedder#fillTypeConverter") |
86 | if converter ~= nil then |
87 | local data = g_fillTypeManager:getConverterDataByName(converter) |
88 | if data ~= nil then |
89 | for input, converted in pairs(data) do |
90 | spec.fillTypeConverters[input] = converted |
91 | |
92 | if spec.fillTypeConvertersReverse[converted.targetFillTypeIndex] == nil then |
93 | spec.fillTypeConvertersReverse[converted.targetFillTypeIndex] = {} |
94 | end |
95 | table.insert(spec.fillTypeConvertersReverse[converted.targetFillTypeIndex], input) |
96 | end |
97 | end |
98 | else |
99 | print(string.format("Warning: Missing fill type converter in '%s'", self.configFileName)) |
100 | end |
101 | |
102 | if self.isClient then |
103 | spec.animationNodes = g_animationManager:loadAnimations(self.xmlFile, "vehicle.tedder.animationNodes", self.components, self, self.i3dMappings) |
104 | |
105 | spec.effects = {} |
106 | spec.workAreaToEffects = {} |
107 | local i = 0 |
108 | while true do |
109 | local key = string.format("vehicle.tedder.effects.effect(%d)", i) |
110 | if not self.xmlFile:hasProperty(key) then |
111 | break |
112 | end |
113 | local effects = g_effectManager:loadEffect(self.xmlFile, key, self.components, self, self.i3dMappings) |
114 | if effects ~= nil then |
115 | local effect = {} |
116 | effect.effects = effects |
117 | effect.workAreaIndex = self.xmlFile:getValue(key .. "#workAreaIndex", 1) |
118 | effect.activeTime = -1 |
119 | effect.activeTimeDuration = 250 |
120 | effect.isActive = false |
121 | effect.isActiveSent = false |
122 | table.insert(spec.effects, effect) |
123 | end |
124 | i = i + 1 |
125 | end |
126 | end |
127 | |
128 | spec.lastDroppedLiters = 0 |
129 | |
130 | spec.stoneLastState = 0 |
131 | spec.stoneWearMultiplierData = g_currentMission.stoneSystem:getWearMultiplierByType("TEDDER") |
132 | |
133 | spec.fillTypesDirtyFlag = self:getNextDirtyFlag() |
134 | spec.effectDirtyFlag = self:getNextDirtyFlag() |
135 | |
136 | if self.addAIDensityHeightTypeRequirement ~= nil then |
137 | self:addAIDensityHeightTypeRequirement(FillType.GRASS_WINDROW) |
138 | end |
139 | end |
143 | function Tedder:onPostLoad(savegame) |
144 | local spec = self.spec_tedder |
145 | for i=#spec.effects, 1, -1 do |
146 | local effect = spec.effects[i] |
147 | local workArea = self:getWorkAreaByIndex(effect.workAreaIndex) |
148 | if workArea ~= nil then |
149 | effect.tedderWorkAreaFillTypeIndex = workArea.tedderWorkAreaIndex |
150 | |
151 | if spec.workAreaToEffects[workArea.index] == nil then |
152 | spec.workAreaToEffects[workArea.index] = {} |
153 | end |
154 | table.insert(spec.workAreaToEffects[workArea.index], effect) |
155 | else |
156 | Logging.xmlWarning(self.xmlFile, "Invalid workAreaIndex '%d' for effect 'vehicle.tedder.effects.effect(%d)'!", effect.workAreaIndex, i) |
157 | table.insert(spec.effects, i) |
158 | end |
159 | end |
160 | |
161 | if not self.isServer then |
162 | SpecializationUtil.removeEventListener(self, "onUpdateTick", Tedder) |
163 | end |
164 | end |
213 | function Tedder:onReadUpdateStream(streamId, timestamp, connection) |
214 | if connection:getIsServer() then |
215 | local spec = self.spec_tedder |
216 | |
217 | if streamReadBool(streamId) then |
218 | for index, _ in ipairs(spec.tedderWorkAreaFillTypes) do |
219 | local fillType = streamReadUIntN(streamId, FillTypeManager.SEND_NUM_BITS) |
220 | spec.tedderWorkAreaFillTypes[index] = fillType |
221 | end |
222 | end |
223 | |
224 | if streamReadBool(streamId) then |
225 | for _, effect in ipairs(spec.effects) do |
226 | if streamReadBool(streamId) then |
227 | local fillType = spec.tedderWorkAreaFillTypes[effect.tedderWorkAreaFillTypeIndex] |
228 | g_effectManager:setFillType(effect.effects, fillType) |
229 | g_effectManager:startEffects(effect.effects) |
230 | else |
231 | g_effectManager:stopEffects(effect.effects) |
232 | end |
233 | end |
234 | end |
235 | end |
236 | end |
240 | function Tedder:onWriteUpdateStream(streamId, connection, dirtyMask) |
241 | if not connection:getIsServer() then |
242 | local spec = self.spec_tedder |
243 | |
244 | if streamWriteBool(streamId, bitAND(dirtyMask, spec.fillTypesDirtyFlag) ~= 0) then |
245 | for _, fillTypeIndex in ipairs(spec.tedderWorkAreaFillTypes) do |
246 | streamWriteUIntN(streamId, fillTypeIndex, FillTypeManager.SEND_NUM_BITS) |
247 | end |
248 | end |
249 | |
250 | if streamWriteBool(streamId, bitAND(dirtyMask, spec.effectDirtyFlag) ~= 0) then |
251 | for _, effect in ipairs(spec.effects) do |
252 | streamWriteBool(streamId, effect.isActiveSent) |
253 | end |
254 | end |
255 | end |
256 | end |
444 | function Tedder:processDropArea(dropArea, fillType, litersToDrop) |
445 | local lsx, lsy, lsz, lex, ley, lez, lineRadius = DensityMapHeightUtil.getLineByArea(dropArea.start, dropArea.width, dropArea.height, true) |
446 | local dropped, lineOffset = DensityMapHeightUtil.tipToGroundAroundLine(self, litersToDrop, fillType, lsx, lsy, lsz, lex, ley, lez, lineRadius, nil, dropArea.lineOffset, false, nil, false) |
447 | dropArea.lineOffset = lineOffset |
448 | |
449 | return dropped |
450 | end |
353 | function Tedder:processTedderArea(workArea, dt) |
354 | local spec = self.spec_tedder |
355 | local workAreaSpec = self.spec_workArea |
356 | |
357 | local sx, sy, sz = getWorldTranslation(workArea.start) |
358 | local wx, wy, wz = getWorldTranslation(workArea.width) |
359 | local hx, hy, hz = getWorldTranslation(workArea.height) |
360 | |
361 | -- pick up |
362 | local lsx, lsy, lsz, lex, ley, lez, lineRadius = DensityMapHeightUtil.getLineByAreaDimensions(sx, sy, sz, wx, wy, wz, hx, hy, hz, true) |
363 | |
364 | for targetFillType, inputFillTypes in pairs(spec.fillTypeConvertersReverse) do |
365 | local pickedUpLiters = 0 |
366 | for _, inputFillType in ipairs(inputFillTypes) do |
367 | pickedUpLiters = pickedUpLiters + DensityMapHeightUtil.tipToGroundAroundLine(self, -math.huge, inputFillType, lsx, lsy, lsz, lex, ley, lez, lineRadius, nil, nil, false, nil) |
368 | end |
369 | |
370 | if pickedUpLiters == 0 and workArea.lastDropFillType ~= FillType.UNKNOWN then |
371 | targetFillType = workArea.lastDropFillType |
372 | end |
373 | |
374 | workArea.lastPickupLiters = -pickedUpLiters |
375 | workArea.litersToDrop = workArea.litersToDrop + workArea.lastPickupLiters |
376 | |
377 | -- drop |
378 | local dropArea = workAreaSpec.workAreas[workArea.dropWindrowWorkAreaIndex] |
379 | if dropArea ~= nil and workArea.litersToDrop > 0 then |
380 | local dropped = self:processDropArea(dropArea, targetFillType, workArea.litersToDrop) |
381 | |
382 | workArea.lastDropFillType = targetFillType |
383 | workArea.lastDroppedLiters = dropped |
384 | spec.lastDroppedLiters = spec.lastDroppedLiters + dropped |
385 | workArea.litersToDrop = workArea.litersToDrop - dropped |
386 | |
387 | if self.isServer then |
388 | --particles |
389 | local lastSpeed = self:getLastSpeed(true) |
390 | if dropped > 0 and lastSpeed > 0.5 then |
391 | local changedFillType = false |
392 | if spec.tedderWorkAreaFillTypes[workArea.tedderWorkAreaIndex] ~= targetFillType then |
393 | spec.tedderWorkAreaFillTypes[workArea.tedderWorkAreaIndex] = targetFillType |
394 | self:raiseDirtyFlags(spec.fillTypesDirtyFlag) |
395 | changedFillType = true |
396 | end |
397 | |
398 | local effects = spec.workAreaToEffects[workArea.index] |
399 | if effects ~= nil then |
400 | for _, effect in ipairs(effects) do |
401 | effect.activeTime = g_currentMission.time + effect.activeTimeDuration |
402 | |
403 | -- sync mp |
404 | if not effect.isActiveSent then |
405 | effect.isActiveSent = true |
406 | self:raiseDirtyFlags(spec.effectDirtyFlag) |
407 | end |
408 | |
409 | if changedFillType then |
410 | g_effectManager:setFillType(effect.effects, targetFillType) |
411 | end |
412 | |
413 | -- enable effect |
414 | if not effect.isActive then |
415 | g_effectManager:setFillType(effect.effects, targetFillType) |
416 | g_effectManager:startEffects(effect.effects) |
417 | end |
418 | |
419 | g_effectManager:setDensity(effect.effects, math.max(lastSpeed / self:getSpeedLimit(), 0.6)) |
420 | |
421 | effect.isActive = true |
422 | end |
423 | end |
424 | end |
425 | end |
426 | end |
427 | end |
428 | |
429 | if self:getLastSpeed() > 0.5 then |
430 | spec.stoneLastState = FSDensityMapUtil.getStoneArea(sx, sz, wx, wz, hx, hz) |
431 | else |
432 | spec.stoneLastState = 0 |
433 | end |
434 | |
435 | --calculating area by area width multiplied by last moved distance (not 100% accuracy in corners) |
436 | local areaWidth = MathUtil.vector3Length(lsx-lex, lsy-ley, lsz-lez) |
437 | local area = areaWidth * self.lastMovedDistance |
438 | |
439 | return area, area |
440 | end |
59 | function Tedder.registerEventListeners(vehicleType) |
60 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", Tedder) |
61 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", Tedder) |
62 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", Tedder) |
63 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Tedder) |
64 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Tedder) |
65 | SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Tedder) |
66 | SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Tedder) |
67 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Tedder) |
68 | SpecializationUtil.registerEventListener(vehicleType, "onStartWorkAreaProcessing", Tedder) |
69 | SpecializationUtil.registerEventListener(vehicleType, "onEndWorkAreaProcessing", Tedder) |
70 | SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", Tedder) |
71 | SpecializationUtil.registerEventListener(vehicleType, "onTurnedOn", Tedder) |
72 | SpecializationUtil.registerEventListener(vehicleType, "onTurnedOff", Tedder) |
73 | end |