336 | function PlaceableSilo:canBeSold(superFunc) |
337 | local spec = self.spec_silo |
338 | -- We do not support selling silos used by more than one farm because of the gameplay complexity. (What do to with contents, when to allow) |
339 | if spec.storagePerFarm then |
340 | return false, nil |
341 | end |
342 | |
343 | local warning = spec.sellWarningText .. "\n" |
344 | local totalFillLevel = 0 |
345 | |
346 | spec.totalFillTypeSellPrice = 0 |
347 | for fillTypeIndex, fillLevel in pairs(spec.storages[1].fillLevels) do |
348 | totalFillLevel = totalFillLevel + fillLevel |
349 | |
350 | if fillLevel > 0 then |
351 | local lowestSellPrice = math.huge |
352 | |
353 | for _, unloadingStation in pairs(g_currentMission.storageSystem:getUnloadingStations()) do |
354 | if unloadingStation.owningPlaceable ~= nil |
355 | and unloadingStation.isSellingPoint |
356 | and unloadingStation.acceptedFillTypes[fillTypeIndex] then |
357 | local price = unloadingStation:getEffectiveFillTypePrice(fillTypeIndex) |
358 | |
359 | if price > 0 then |
360 | lowestSellPrice = math.min(lowestSellPrice, price) |
361 | end |
362 | end |
363 | end |
364 | |
365 | if lowestSellPrice == math.huge then |
366 | lowestSellPrice = 0.5 |
367 | end |
368 | |
369 | local price = fillLevel * lowestSellPrice * PlaceableSilo.PRICE_SELL_FACTOR |
370 | local fillType = g_fillTypeManager:getFillTypeByIndex(fillTypeIndex) |
371 | warning = string.format("%s%s (%s) - %s: %s\n", warning, fillType.title, g_i18n:formatVolume(fillLevel), g_i18n:getText("ui_sellValue"), g_i18n:formatMoney(price, 0, true, true)) |
372 | spec.totalFillTypeSellPrice = spec.totalFillTypeSellPrice + price |
373 | end |
374 | end |
375 | |
376 | if totalFillLevel > 0 then |
377 | return true, warning |
378 | end |
379 | |
380 | return true, nil |
381 | end |
144 | function PlaceableSilo:onDelete() |
145 | local spec = self.spec_silo |
146 | |
147 | local storageSystem = g_currentMission.storageSystem |
148 | if spec.storages ~= nil then |
149 | for _, storage in ipairs(spec.storages) do |
150 | if spec.unloadingStation ~= nil then |
151 | storageSystem:removeStorageFromUnloadingStations(storage, {spec.unloadingStation}) |
152 | end |
153 | if spec.loadingStation ~= nil then |
154 | storageSystem:removeStorageFromLoadingStations(storage, {spec.loadingStation}) |
155 | end |
156 | |
157 | storage:removeFillLevelChangedListeners(spec.storageFilLLevelChangedCallback) |
158 | storageSystem:removeStorage(storage) |
159 | end |
160 | |
161 | -- delete storages later to avoid access to already deleted storages |
162 | for _, storage in ipairs(spec.storages) do |
163 | storage:delete() |
164 | end |
165 | end |
166 | |
167 | if spec.unloadingStation ~= nil then |
168 | storageSystem:removeUnloadingStation(spec.unloadingStation, self) |
169 | spec.unloadingStation:delete() |
170 | end |
171 | |
172 | if spec.loadingStation ~= nil then |
173 | if spec.loadingStation:getIsFillTypeSupported(FillType.LIQUIDMANURE) then |
174 | g_currentMission:removeLiquidManureLoadingStation(spec.loadingStation) |
175 | end |
176 | |
177 | storageSystem:removeLoadingStation(spec.loadingStation, self) |
178 | spec.loadingStation:delete() |
179 | end |
180 | |
181 | g_currentMission.activatableObjectsSystem:removeActivatable(spec.activatable) |
182 | |
183 | if spec.playerActionTrigger ~= nil then |
184 | removeTrigger(spec.playerActionTrigger) |
185 | end |
186 | end |
190 | function PlaceableSilo:onFinalizePlacement() |
191 | local spec = self.spec_silo |
192 | |
193 | local storageSystem = g_currentMission.storageSystem |
194 | |
195 | spec.unloadingStation:register(true) |
196 | storageSystem:addUnloadingStation(spec.unloadingStation, self) |
197 | |
198 | if spec.loadingStation ~= nil then |
199 | spec.loadingStation:register(true) |
200 | storageSystem:addLoadingStation(spec.loadingStation, self) |
201 | |
202 | if spec.loadingStation:getIsFillTypeSupported(FillType.LIQUIDMANURE) then |
203 | g_currentMission:addLiquidManureLoadingStation(spec.loadingStation) |
204 | end |
205 | end |
206 | |
207 | for _, storage in ipairs(spec.storages) do |
208 | if not spec.storagePerFarm then |
209 | storage:setOwnerFarmId(self:getOwnerFarmId(), true) |
210 | end |
211 | |
212 | storageSystem:addStorage(storage) |
213 | |
214 | storage:register(true) |
215 | |
216 | storageSystem:addStorageToUnloadingStation(storage, spec.unloadingStation) |
217 | storageSystem:addStorageToLoadingStation(storage, spec.loadingStation) |
218 | end |
219 | |
220 | -- now check if there are some storages in range which can also be used |
221 | local storagesInRange = storageSystem:getStorageExtensionsInRange(spec.unloadingStation, self:getOwnerFarmId()) |
222 | if storagesInRange ~= nil then |
223 | for _, storage in ipairs(storagesInRange) do |
224 | if spec.unloadingStation.targetStorages[storage] == nil then |
225 | storageSystem:addStorageToUnloadingStation(storage, spec.unloadingStation) |
226 | end |
227 | end |
228 | end |
229 | |
230 | storagesInRange = storageSystem:getStorageExtensionsInRange(spec.loadingStation, self:getOwnerFarmId()) |
231 | if storagesInRange ~= nil then |
232 | for _, storage in ipairs(storagesInRange) do |
233 | if spec.loadingStation.sourceStorages[storage] == nil then |
234 | storageSystem:addStorageToLoadingStation(storage, spec.loadingStation) |
235 | end |
236 | end |
237 | end |
238 | |
239 | if not spec.storagePerFarm then |
240 | -- For the very first silo on easy, fill it. The startSiloAmounts are only set on game creation. |
241 | -- In case of no silo on the map this code is used for the first silo placed in new savegame. |
242 | local num = 0 |
243 | for _, placeable in pairs(g_currentMission.placeables) do |
244 | if placeable:getOwnerFarmId() == self:getOwnerFarmId() and placeable.spec_silo ~= nil then |
245 | num = num + 1 |
246 | end |
247 | end |
248 | |
249 | if num == 1 and g_currentMission.missionInfo.difficulty == 1 and g_currentMission.missionInfo.startSiloAmounts ~= nil and not g_currentMission.missionInfo:getIsLoadedFromSavegame() and not g_currentMission.missionInfo.hasLoadedFirstFilledSilo then |
250 | -- This is to circumvent a cheat: load new easy game, sell silo, place silo (it is filled now), sell silo, etc. |
251 | -- No need to save it as this code does not run on savegames |
252 | g_currentMission.missionInfo.hasLoadedFirstFilledSilo = true |
253 | |
254 | for fillTypeName, amount in pairs(g_currentMission.missionInfo.startSiloAmounts) do |
255 | local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeName) |
256 | self:setAmount(fillTypeIndex, amount) |
257 | end |
258 | end |
259 | end |
260 | |
261 | if spec.playerActionTrigger ~= nil then |
262 | addTrigger(spec.playerActionTrigger, "onPlayerActionTriggerCallback", self) |
263 | end |
264 | |
265 | end |
87 | function PlaceableSilo:onLoad(savegame) |
88 | local spec = self.spec_silo |
89 | local xmlFile = self.xmlFile |
90 | |
91 | spec.playerActionTrigger = xmlFile:getValue("placeable.silo#playerActionTrigger", nil, self.components, self.i3dMappings) |
92 | |
93 | if spec.playerActionTrigger ~= nil then |
94 | spec.activatable = PlaceableSiloActivatable.new(self) |
95 | end |
96 | |
97 | spec.storagePerFarm = xmlFile:getValue("placeable.silo.storages#perFarm", false) |
98 | spec.foreignSilo = xmlFile:getValue("placeable.silo.storages#foreignSilo", spec.storagePerFarm) -- Shows as foreign silo in the menu |
99 | |
100 | spec.unloadingStation = UnloadingStation.new(self.isServer, self.isClient) |
101 | spec.unloadingStation:load(self.components, xmlFile, "placeable.silo.unloadingStation", self.customEnvironment, self.i3dMappings, self.components[1].node) |
102 | spec.unloadingStation.owningPlaceable = self |
103 | spec.unloadingStation.hasStoragePerFarm = spec.storagePerFarm |
104 | |
105 | spec.loadingStation = LoadingStation.new(self.isServer, self.isClient) |
106 | spec.loadingStation:load(self.components, xmlFile, "placeable.silo.loadingStation", self.customEnvironment, self.i3dMappings, self.components[1].node) |
107 | spec.loadingStation.owningPlaceable = self |
108 | spec.loadingStation.hasStoragePerFarm = spec.storagePerFarm |
109 | |
110 | spec.fillTypesAndLevelsAuxiliary = {} |
111 | spec.fillTypeToFillTypeStorageTable = {} |
112 | spec.infoTriggerFillTypesAndLevels = {} |
113 | |
114 | local numStorageSets = spec.storagePerFarm and FarmManager.MAX_NUM_FARMS or 1 |
115 | if not g_currentMission.missionDynamicInfo.isMultiplayer then |
116 | numStorageSets = 1 |
117 | end |
118 | |
119 | spec.storages = {} |
120 | local i = 0 |
121 | while true do |
122 | local storageKey = string.format("placeable.silo.storages.storage(%d)", i) |
123 | if not xmlFile:hasProperty(storageKey) then |
124 | break |
125 | end |
126 | |
127 | for j = 1, numStorageSets do |
128 | local storage = Storage.new(self.isServer, self.isClient) |
129 | if storage:load(self.components, xmlFile, storageKey, self.i3dMappings) then |
130 | storage.ownerFarmId = j |
131 | storage.foreignSilo = spec.foreignSilo -- Pass along for usage by prices menu |
132 | table.insert(spec.storages, storage) |
133 | end |
134 | end |
135 | |
136 | i = i + 1 |
137 | end |
138 | |
139 | spec.sellWarningText = g_i18n:convertText(xmlFile:getValue("placeable.silo#sellWarningText", "$l10n_info_siloExtensionNotEmpty")) |
140 | end |
269 | function PlaceableSilo:onReadStream(streamId, connection) |
270 | local spec = self.spec_silo |
271 | |
272 | local unloadingStationId = NetworkUtil.readNodeObjectId(streamId) |
273 | spec.unloadingStation:readStream(streamId, connection) |
274 | g_client:finishRegisterObject(spec.unloadingStation, unloadingStationId) |
275 | |
276 | local loadingStationId = NetworkUtil.readNodeObjectId(streamId) |
277 | spec.loadingStation:readStream(streamId, connection) |
278 | g_client:finishRegisterObject(spec.loadingStation, loadingStationId) |
279 | |
280 | for _, storage in ipairs(spec.storages) do |
281 | local storageId = NetworkUtil.readNodeObjectId(streamId) |
282 | storage:readStream(streamId, connection) |
283 | g_client:finishRegisterObject(storage, storageId) |
284 | end |
285 | end |
289 | function PlaceableSilo:onWriteStream(streamId, connection) |
290 | local spec = self.spec_silo |
291 | |
292 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(spec.unloadingStation)) |
293 | spec.unloadingStation:writeStream(streamId, connection) |
294 | g_server:registerObjectInStream(connection, spec.unloadingStation) |
295 | |
296 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(spec.loadingStation)) |
297 | spec.loadingStation:writeStream(streamId, connection) |
298 | g_server:registerObjectInStream(connection, spec.loadingStation) |
299 | |
300 | for _, storage in ipairs(spec.storages) do |
301 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(storage)) |
302 | storage:writeStream(streamId, connection) |
303 | g_server:registerObjectInStream(connection, storage) |
304 | end |
305 | end |
451 | function PlaceableSilo:refillAmount(fillTypeIndex, amount, price) |
452 | if fillTypeIndex == nil or amount == nil or price == nil then |
453 | return |
454 | end |
455 | |
456 | if not self.isServer then |
457 | g_client:getServerConnection():sendEvent(PlaceableSiloRefillEvent.new(self, fillTypeIndex, amount, price)) |
458 | return |
459 | end |
460 | |
461 | local spec = self.spec_silo |
462 | for _, storage in ipairs(spec.storages) do |
463 | local freeCapacity = storage:getFreeCapacity(fillTypeIndex) |
464 | if freeCapacity > 0 then |
465 | local moved = math.min(amount, freeCapacity) |
466 | local fillLevel = storage:getFillLevel(fillTypeIndex) |
467 | storage:setFillLevel(fillLevel + moved, fillTypeIndex) |
468 | |
469 | amount = amount - moved |
470 | end |
471 | |
472 | if amount <= 0.001 then |
473 | break |
474 | end |
475 | end |
476 | |
477 | if self.isServer then |
478 | g_currentMission:addMoney(-price, self:getOwnerFarmId(), MoneyType.BOUGHT_MATERIALS, true) |
479 | end |
480 | g_currentMission:showMoneyChange(MoneyType.BOUGHT_MATERIALS) |
481 | end |
56 | function PlaceableSilo.registerXMLPaths(schema, basePath) |
57 | schema:setXMLSpecializationType("Silo") |
58 | schema:register(XMLValueType.STRING, basePath .. ".silo#sellWarningText", "Sell warning text") |
59 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".silo#playerActionTrigger", "Trigger for player interaction") |
60 | schema:register(XMLValueType.BOOL, basePath .. ".silo.storages#perFarm", "Silo is per farm", false) |
61 | schema:register(XMLValueType.BOOL, basePath .. ".silo.storages#foreignSilo", "Shows as foreign silo in the menu", false) |
62 | UnloadingStation.registerXMLPaths(schema, basePath .. ".silo.unloadingStation") |
63 | LoadingStation.registerXMLPaths(schema, basePath .. ".silo.loadingStation") |
64 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".silo.storages.storage(?)#node", "Storage node") |
65 | Storage.registerXMLPaths(schema, basePath .. ".silo.storages.storage(?)") |
66 | schema:setXMLSpecializationType() |
67 | end |
528 | function PlaceableSilo:updateInfo(superFunc, infoTable) |
529 | superFunc(self, infoTable) |
530 | local spec = self.spec_silo |
531 | |
532 | -- collect all fillTypes and levels from storage |
533 | |
534 | local farmId = g_currentMission:getFarmId() |
535 | for fillType, fillLevel in pairs(spec.loadingStation:getAllFillLevels(farmId)) do |
536 | spec.fillTypesAndLevelsAuxiliary[fillType] = (spec.fillTypesAndLevelsAuxiliary[fillType] or 0) + fillLevel |
537 | end |
538 | |
539 | -- filter empty fillType, merge to index table for sorting |
540 | table.clear(spec.infoTriggerFillTypesAndLevels) |
541 | for fillType, fillLevel in pairs(spec.fillTypesAndLevelsAuxiliary) do |
542 | if fillLevel > 0.1 then |
543 | spec.fillTypeToFillTypeStorageTable[fillType] = spec.fillTypeToFillTypeStorageTable[fillType] or {fillType=fillType, fillLevel=fillLevel} |
544 | spec.fillTypeToFillTypeStorageTable[fillType].fillLevel = fillLevel |
545 | table.insert(spec.infoTriggerFillTypesAndLevels, spec.fillTypeToFillTypeStorageTable[fillType]) |
546 | end |
547 | end |
548 | |
549 | table.clear(spec.fillTypesAndLevelsAuxiliary) |
550 | |
551 | table.sort(spec.infoTriggerFillTypesAndLevels, function(a, b) return a.fillLevel > b.fillLevel end) |
552 | |
553 | local numEntries = math.min(#spec.infoTriggerFillTypesAndLevels, PlaceableSilo.INFO_TRIGGER_NUM_DISPLAYED_FILLTYPES) |
554 | if numEntries > 0 then |
555 | for i=1, numEntries do |
556 | local fillTypeAndLevel = spec.infoTriggerFillTypesAndLevels[i] |
557 | table.insert(infoTable, {title=g_fillTypeManager:getFillTypeTitleByIndex(fillTypeAndLevel.fillType), text=g_i18n:formatVolume(fillTypeAndLevel.fillLevel, 0)}) |
558 | end |
559 | else |
560 | table.insert(infoTable, {title=g_i18n:getText("infohud_siloEmpty"), text=""}) |
561 | end |
562 | end |