27 | function Cover.initSpecialization() |
28 | g_configurationManager:addConfigurationType("cover", g_i18n:getText("configuration_cover"), "cover", nil, nil, nil, ConfigurationUtil.SELECTOR_MULTIOPTION) |
29 | |
30 | local schema = Vehicle.xmlSchema |
31 | schema:setXMLSpecializationType("Cover") |
32 | |
33 | schema:register(XMLValueType.STRING, Cover.COVER_XML_KEY .. "#openAnimation", "Open animation name") |
34 | schema:register(XMLValueType.FLOAT, Cover.COVER_XML_KEY .. "#openAnimationStopTime", "Open animation stop time") |
35 | schema:register(XMLValueType.FLOAT, Cover.COVER_XML_KEY .. "#openAnimationStartTime", "Open animation start time") |
36 | schema:register(XMLValueType.STRING, Cover.COVER_XML_KEY .. "#closeAnimation", "Close animation name") |
37 | schema:register(XMLValueType.FLOAT, Cover.COVER_XML_KEY .. "#closeAnimationStopTime", "Close animation stop time") |
38 | schema:register(XMLValueType.BOOL, Cover.COVER_XML_KEY .. "#openOnBuy", "Open after buying", false) |
39 | schema:register(XMLValueType.BOOL, Cover.COVER_XML_KEY .. "#forceOpenOnTip", "Open while tipping", true) |
40 | schema:register(XMLValueType.BOOL, Cover.COVER_XML_KEY .. "#autoReactToTrigger", "Automatically open in triggers", true) |
41 | schema:register(XMLValueType.VECTOR_N, Cover.COVER_XML_KEY .. "#fillUnitIndices", "Fill unit indices to cover") |
42 | schema:register(XMLValueType.STRING, Cover.COVER_XML_KEY .. "#blockedToolTypes", "List with blocked tool types", "dischargeable bale trigger pallet") |
43 | |
44 | schema:register(XMLValueType.BOOL, "vehicle.cover.coverConfigurations.coverConfiguration(?)#closeCoverIfNotAllowed", "Close cover if not allowed to open it", false) |
45 | schema:register(XMLValueType.BOOL, "vehicle.cover.coverConfigurations.coverConfiguration(?)#openCoverWhileTipping", "Open cover while tipping", false) |
46 | ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.cover.coverConfigurations.coverConfiguration(?)") |
47 | |
48 | schema:register(XMLValueType.INT, "vehicle.pipe#coverMinState", "Min. cover state to allow pipe state change", 0) |
49 | schema:register(XMLValueType.INT, "vehicle.pipe#coverMaxState", "Max. cover state to allow pipe state change", "Max. cover state") |
50 | |
51 | schema:setXMLSpecializationType() |
52 | |
53 | local schemaSavegame = Vehicle.xmlSchemaSavegame |
54 | schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).cover#state", "Current cover state") |
55 | end |
265 | function Cover:loadCoverFromXML(xmlFile, key, cover) |
266 | cover.openAnimation = xmlFile:getValue(key.."#openAnimation") |
267 | cover.openAnimationStartTime = xmlFile:getValue(key.."#openAnimationStartTime") |
268 | cover.openAnimationStopTime = xmlFile:getValue(key.."#openAnimationStopTime") |
269 | |
270 | if cover.openAnimation == nil then |
271 | Logging.xmlWarning(self.xmlFile, "Missing 'openAnimation' for cover '%s'!", key) |
272 | return false |
273 | end |
274 | |
275 | cover.closeAnimation = xmlFile:getValue(key.."#closeAnimation") |
276 | cover.closeAnimationStopTime = xmlFile:getValue(key.."#closeAnimationStopTime") |
277 | |
278 | cover.startOpenState = xmlFile:getValue(key.."#openOnBuy", false) |
279 | cover.forceOpenOnTip = xmlFile:getValue(key.."#forceOpenOnTip", true) |
280 | cover.autoReactToTrigger = xmlFile:getValue(key.."#autoReactToTrigger", true) |
281 | |
282 | cover.fillUnitIndices = xmlFile:getValue(key.."#fillUnitIndices", nil, true) |
283 | if cover.fillUnitIndices == nil then |
284 | Logging.xmlWarning(self.xmlFile, "Missing 'fillUnitIndices' for cover '%s'!", key) |
285 | return false |
286 | end |
287 | |
288 | cover.blockedToolTypes = {} |
289 | |
290 | local strBlockedToolTypes = xmlFile:getValue(key.."#blockedToolTypes", "dischargeable bale trigger pallet") |
291 | strBlockedToolTypes = strBlockedToolTypes:trim():split(" ") |
292 | for _, toolType in ipairs(strBlockedToolTypes) do |
293 | local index = g_toolTypeManager:getToolTypeIndexByName(toolType) |
294 | if index ~= ToolType.UNDEFINED then |
295 | cover.blockedToolTypes[index] = true |
296 | end |
297 | end |
298 | |
299 | return true |
300 | end |
520 | function Cover:onFillUnitTriggerChanged(fillTrigger, fillTypeIndex, fillUnitIndex, numTriggers) |
521 | local spec = self.spec_cover |
522 | local covers = spec.fillUnitIndexToCovers[fillUnitIndex] |
523 | if covers ~= nil then |
524 | local isDifferentState = true |
525 | for _, cover in pairs(covers) do |
526 | isDifferentState = isDifferentState and spec.state ~= cover.index |
527 | end |
528 | |
529 | local isStateChangedAllowed = self:getIsNextCoverStateAllowed(covers[1].index) |
530 | if covers[1].autoReactToTrigger and isDifferentState and isStateChangedAllowed then |
531 | self:setCoverState(covers[1].index, true) |
532 | spec.isStateSetAutomatically = true |
533 | end |
534 | end |
535 | end |
96 | function Cover:onLoad(savegame) |
97 | local spec = self.spec_cover |
98 | |
99 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.cover#animationName", "vehicle.cover.coverConfigurations.coverConfiguration.cover#openAnimation") --FS17 to FS19 |
100 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.foldable.foldingParts#closeCoverOnFold", "vehicle.cover.coverConfigurations.coverConfiguration.cover#closeCoverIfNotAllowed") --FS17 to FS19 |
101 | |
102 | local coverConfigurationId = Utils.getNoNil(self.configurations["cover"], 1) |
103 | local configKey = string.format("vehicle.cover.coverConfigurations.coverConfiguration(%d)", coverConfigurationId -1) |
104 | ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.cover.coverConfigurations.coverConfiguration", coverConfigurationId , self.components, self) |
105 | |
106 | spec.state = 0 |
107 | spec.runningAnimations = {} |
108 | spec.covers = {} |
109 | spec.fillUnitIndexToCovers = {} |
110 | spec.isStateSetAutomatically = false |
111 | local i = 0 |
112 | while true do |
113 | local key = string.format("%s.cover(%d)", configKey, i) |
114 | if not self.xmlFile:hasProperty(key) then |
115 | break |
116 | end |
117 | |
118 | local cover = {} |
119 | if self:loadCoverFromXML(self.xmlFile, key, cover) then |
120 | for j=#cover.fillUnitIndices, 1, -1 do |
121 | local index = cover.fillUnitIndices[j] |
122 | |
123 | if spec.fillUnitIndexToCovers[index] == nil then |
124 | spec.fillUnitIndexToCovers[index] = {cover} |
125 | else |
126 | table.insert(spec.fillUnitIndexToCovers[index], cover) |
127 | end |
128 | end |
129 | |
130 | table.insert(spec.covers, cover) |
131 | cover.index = #spec.covers |
132 | end |
133 | |
134 | i = i + 1 |
135 | end |
136 | |
137 | spec.closeCoverIfNotAllowed = self.xmlFile:getValue(configKey.."#closeCoverIfNotAllowed", false) |
138 | spec.openCoverWhileTipping = self.xmlFile:getValue(configKey.."#openCoverWhileTipping", false) |
139 | |
140 | spec.hasCovers = #spec.covers > 0 |
141 | spec.isDirty = false |
142 | |
143 | if not spec.hasCovers then |
144 | SpecializationUtil.removeEventListener(self, "onReadStream", Cover) |
145 | SpecializationUtil.removeEventListener(self, "onWriteStream", Cover) |
146 | SpecializationUtil.removeEventListener(self, "onUpdate", Cover) |
147 | SpecializationUtil.removeEventListener(self, "onRegisterActionEvents", Cover) |
148 | SpecializationUtil.removeEventListener(self, "onStartTipping", Cover) |
149 | SpecializationUtil.removeEventListener(self, "onFillUnitTriggerChanged", Cover) |
150 | SpecializationUtil.removeEventListener(self, "onRemovedFillUnitTrigger", Cover) |
151 | end |
152 | end |
156 | function Cover:onPostLoad(savegame) |
157 | local spec = self.spec_cover |
158 | if spec.hasCovers then |
159 | local state = 0 |
160 | if savegame ~= nil then |
161 | state = savegame.xmlFile:getValue(savegame.key..".cover#state", state) |
162 | else |
163 | for i=1, #spec.covers do |
164 | local cover = spec.covers[i] |
165 | if cover.startOpenState then |
166 | state = i |
167 | end |
168 | end |
169 | end |
170 | |
171 | if state == 0 then |
172 | -- set last state to max cover state to run the close animation of the last state. So we make sure the cover animations are at the currect time |
173 | spec.state = #spec.covers |
174 | end |
175 | |
176 | self:setCoverState(state, true) |
177 | for i=#spec.runningAnimations, 1, -1 do |
178 | local animation = spec.runningAnimations[i] |
179 | AnimatedVehicle.updateAnimationByName(self, animation.name, 9999999, true) |
180 | table.remove(spec.runningAnimations, i) |
181 | end |
182 | spec.isDirty = false |
183 | end |
184 | end |
198 | function Cover:onReadStream(streamId, connection) |
199 | if connection:getIsServer() then |
200 | local spec = self.spec_cover |
201 | local state = streamReadUIntN(streamId, Cover.SEND_NUM_BITS) |
202 | self:setCoverState(state, true) |
203 | |
204 | for i=#spec.runningAnimations, 1, -1 do |
205 | local animation = spec.runningAnimations[i] |
206 | AnimatedVehicle.updateAnimationByName(self, animation.name, 9999999, true) |
207 | table.remove(spec.runningAnimations, i) |
208 | end |
209 | spec.isDirty = false |
210 | end |
211 | end |
436 | function Cover:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) |
437 | if self.isClient then |
438 | local spec = self.spec_cover |
439 | self:clearActionEventsTable(spec.actionEvents) |
440 | |
441 | if isActiveForInputIgnoreSelection then |
442 | local state, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.TOGGLE_COVER, self, Cover.actionEventToggleCover, false, true, false, true, nil, nil, true, true) |
443 | if not state then |
444 | local _ |
445 | _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.IMPLEMENT_EXTRA4, self, Cover.actionEventToggleCover, false, true, false, true, nil, nil, true, true) |
446 | end |
447 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL) |
448 | Cover.updateActionText(self) |
449 | end |
450 | end |
451 | end |
223 | function Cover:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
224 | local spec = self.spec_cover |
225 | if spec.isDirty then |
226 | local animation = spec.runningAnimations[1] |
227 | if animation ~= nil then |
228 | local nextAnim = spec.runningAnimations[2] |
229 | -- if next animation is same as current animation, we stop the current animation and play the next |
230 | if nextAnim ~= nil and nextAnim.name == animation.name then |
231 | table.remove(spec.runningAnimations, 1) |
232 | self:stopAnimation(animation.name, true) |
233 | self:playCoverAnimation(nextAnim) |
234 | end |
235 | |
236 | -- check if current animation is playing. if not play next |
237 | if not self:getIsAnimationPlaying(animation.name) then |
238 | table.remove(spec.runningAnimations, 1) |
239 | local nextAnimation = spec.runningAnimations[1] |
240 | if nextAnimation ~= nil then |
241 | self:playCoverAnimation(nextAnimation) |
242 | else |
243 | spec.isDirty = false |
244 | end |
245 | end |
246 | end |
247 | end |
248 | |
249 | if spec.closeCoverIfNotAllowed then |
250 | if spec.state ~= 0 then |
251 | local newState = spec.state + 1 |
252 | if newState > #spec.covers then |
253 | newState = 0 |
254 | end |
255 | |
256 | if not self:getIsNextCoverStateAllowed(newState) then |
257 | self:setCoverState(0, true) |
258 | end |
259 | end |
260 | end |
261 | end |
347 | function Cover:playCoverAnimation(animation) |
348 | if animation.startTime ~= nil then |
349 | self:setAnimationTime(animation.name, animation.startTime, true) |
350 | end |
351 | |
352 | local dir = MathUtil.sign(animation.stopTime - self:getAnimationTime(animation.name)) |
353 | self:setAnimationStopTime(animation.name, animation.stopTime) |
354 | self:playAnimation(animation.name, dir, animation.startTime or self:getAnimationTime(animation.name), true) |
355 | end |
81 | function Cover.registerEventListeners(vehicleType) |
82 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", Cover) |
83 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", Cover) |
84 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Cover) |
85 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Cover) |
86 | SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Cover) |
87 | SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", Cover) |
88 | SpecializationUtil.registerEventListener(vehicleType, "onStartTipping", Cover) |
89 | SpecializationUtil.registerEventListener(vehicleType, "onOpenBackDoor", Cover) |
90 | SpecializationUtil.registerEventListener(vehicleType, "onFillUnitTriggerChanged", Cover) |
91 | SpecializationUtil.registerEventListener(vehicleType, "onRemovedFillUnitTrigger", Cover) |
92 | end |
59 | function Cover.registerFunctions(vehicleType) |
60 | SpecializationUtil.registerFunction(vehicleType, "loadCoverFromXML", Cover.loadCoverFromXML) |
61 | SpecializationUtil.registerFunction(vehicleType, "getIsNextCoverStateAllowed", Cover.getIsNextCoverStateAllowed) |
62 | SpecializationUtil.registerFunction(vehicleType, "setCoverState", Cover.setCoverState) |
63 | SpecializationUtil.registerFunction(vehicleType, "playCoverAnimation", Cover.playCoverAnimation) |
64 | SpecializationUtil.registerFunction(vehicleType, "getCoverByFillUnitIndex", Cover.getCoverByFillUnitIndex) |
65 | end |
69 | function Cover.registerOverwrittenFunctions(vehicleType) |
70 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getFillUnitSupportsToolType", Cover.getFillUnitSupportsToolType) |
71 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanBeSelected", Cover.getCanBeSelected) |
72 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadPipeNodes", Cover.loadPipeNodes) |
73 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsPipeStateChangeAllowed", Cover.getIsPipeStateChangeAllowed) |
74 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "aiPrepareLoading", Cover.aiPrepareLoading) |
75 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "aiFinishLoading", Cover.aiFinishLoading) |
76 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "finishedAIDischarge", Cover.finishedAIDischarge) |
77 | end |
304 | function Cover:setCoverState(state, noEventSend) |
305 | local spec = self.spec_cover |
306 | |
307 | if spec.hasCovers and state >= 0 and state <= #spec.covers and spec.state ~= state then |
308 | SetCoverStateEvent.sendEvent(self, state, noEventSend) |
309 | |
310 | local startAnim = #spec.runningAnimations == 0 |
311 | |
312 | -- check if we need to close previous cover |
313 | if spec.state > 0 then |
314 | local cover = spec.covers[spec.state] |
315 | local animation = cover.closeAnimation |
316 | local stopTime = cover.closeAnimationStopTime or 1 |
317 | |
318 | if animation == nil then |
319 | animation = cover.openAnimation |
320 | stopTime = cover.openAnimationStopTime or 0 |
321 | end |
322 | |
323 | if self:getAnimationExists(animation) then |
324 | table.insert(spec.runningAnimations, {name=animation, stopTime=stopTime}) |
325 | end |
326 | end |
327 | |
328 | -- open next cover if state is not 'All-closed' (0) |
329 | if state > 0 then |
330 | local cover = spec.covers[state] |
331 | table.insert(spec.runningAnimations, {name=cover.openAnimation, startTime=cover.openAnimationStartTime, stopTime=cover.openAnimationStopTime or 1}) |
332 | end |
333 | |
334 | spec.state = state |
335 | spec.isDirty = #spec.runningAnimations > 0 |
336 | |
337 | if startAnim and #spec.runningAnimations > 0 then |
338 | self:playCoverAnimation(spec.runningAnimations[1]) |
339 | end |
340 | |
341 | Cover.updateActionText(self) |
342 | end |
343 | end |
552 | function Cover.updateActionText(self) |
553 | local spec = self.spec_cover |
554 | if next(spec.actionEvents) ~= nil then |
555 | local actionEvent = spec.actionEvents[next(spec.actionEvents)] |
556 | if actionEvent ~= nil then |
557 | local text = g_i18n:getText("action_nextCover") |
558 | if spec.state == #spec.covers then |
559 | text = g_i18n:getText("action_closeCover") |
560 | elseif spec.state == 0 then |
561 | text = g_i18n:getText("action_openCover") |
562 | end |
563 | |
564 | g_inputBinding:setActionEventText(actionEvent.actionEventId, text) |
565 | end |
566 | end |
567 | end |