animalFood.animals.animal | for each animal type we define food groups and mixtures |
animalFood.animals.animal#type | the type of animal. It is defined in $dataS\character\xxxAnimals.xml (animalHusbandry.animals#type) |
animalFood.animals.animal#consumptionType | is how the food is consumed by the animals |
animalFood.animals.animal.foodGroups.foodGroup | is a list of fillType. It must be defined in $data\maps\mapXXX.xml |
animalFood.animals.animal.foodGroups.foodGroup#title | localized string for the group |
animalFood.animals.animal.foodGroups.foodGroup#productionWeight | how much production is impacted when this group of food is processed. Total of weight must be equal to 1.0. |
animalFood.foodMixtures | list of mixtures possible |
animalFood.foodMixtures.foodMixture#fillType | name of fillType for the mixture. It must be defined in $data\maps\mapXXX.xml |
animalFood.foodMixtures.foodMixture.ingredient | list of ingredients for the mixture. |
animalFood.foodMixtures.foodMixture.ingredient#fillTypes | list of filltypes for this ingredient |
animalFood.foodMixtures.foodMixture.ingredient#weight | percentage of a filltype that is contained in the mixture. Total of weights must be equal to 1.0. |
316 | function AnimalFoodManager:consumeFood(animalType, amountToConsume, fillLevels, consumedFood) |
317 | local production = 0.0 |
318 | local animalFoodGroups = self.foodGroups[animalType] |
319 | |
320 | -- print(string.format("-- [AnimalFoodManager:consumeFood] animalType(%s) animalFoodGroups(%s)", tostring(animalType), tostring(animalFoodGroups))) |
321 | if animalFoodGroups ~= nil then |
322 | -- print(string.format("-- [AnimalFoodManager:consumeFood] consumptionType(%d)", animalFoodGroups.consumptionType)) |
323 | if animalFoodGroups.consumptionType == AnimalFoodManager.FOOD_CONSUME_TYPE_SERIAL then |
324 | production = self:consumeFoodSerially(amountToConsume, animalFoodGroups.content, fillLevels, consumedFood) |
325 | elseif animalFoodGroups.consumptionType == AnimalFoodManager.FOOD_CONSUME_TYPE_PARALLEL then |
326 | production = self:consumeFoodParallelly(amountToConsume, animalFoodGroups.content, fillLevels, consumedFood) |
327 | end |
328 | end |
329 | return production |
330 | end |
379 | function AnimalFoodManager:consumeFoodGroup(foodGroup, amount, fillLevels, consumedFood) |
380 | for _, fillTypeIndex in pairs(foodGroup.fillTypes) do |
381 | if fillLevels[fillTypeIndex] ~= nil then |
382 | local currentFillLevel = fillLevels[fillTypeIndex] |
383 | local amountToConsume = -math.min(amount, currentFillLevel) |
384 | local deltaConsumed = self:changeFillLevels(amountToConsume, fillLevels, fillTypeIndex) |
385 | |
386 | -- print(string.format("-- [AnimalFoodManager:consumeFoodGroup] foodGroup(%s) amount(%.3f) amountToConsume(%.3f) deltaConsumed(%.3f) fillTypeIndex(%s/%d) newAmount(%.3f)", |
387 | -- foodGroup.title, |
388 | -- amount, |
389 | -- amountToConsume, |
390 | -- deltaConsumed, |
391 | -- g_fillTypeManager:getFillTypeNameByIndex(fillTypeIndex), fillTypeIndex, |
392 | -- math.max(amount + deltaConsumed, 0.0) |
393 | -- )) |
394 | amount = math.max(amount + deltaConsumed, 0.0) |
395 | consumedFood[fillTypeIndex] = -deltaConsumed |
396 | if amount == 0.0 then |
397 | -- print(string.format("-- [AnimalFoodManager:consumeFoodGroup][A] EXIT")) |
398 | return amount |
399 | end |
400 | end |
401 | end |
402 | -- print(string.format("-- [AnimalFoodManager:consumeFoodGroup][B] EXIT restAmount(%.3f) ", amount)) |
403 | return amount |
404 | end |
359 | function AnimalFoodManager:consumeFoodParallelly(amount, foodGroups, fillLevels, consumedFood) |
360 | local productionWeight = 0.0 |
361 | |
362 | for _, foodGroup in pairs(foodGroups) do |
363 | local totalFillLevelInGroup = self:getTotalFillLevelInGroup(foodGroup, fillLevels) |
364 | local foodGroupConsume = amount * foodGroup.eatWeight |
365 | local consumeFood = math.min(totalFillLevelInGroup, foodGroupConsume) |
366 | |
367 | local ret = self:consumeFoodGroup(foodGroup, consumeFood, fillLevels, consumedFood) |
368 | local foodFactor = (consumeFood-ret) / foodGroupConsume |
369 | |
370 | productionWeight = productionWeight + foodFactor * foodGroup.productionWeight |
371 | end |
372 | |
373 | return productionWeight |
374 | end |
334 | function AnimalFoodManager:consumeFoodSerially(amount, foodGroups, fillLevels, consumedFood) |
335 | local productionWeight = 0.0 |
336 | local totalAmountToConsume = amount |
337 | |
338 | -- print(string.format("-- [AnimalFoodManager:consumeFoodSerially] ENTER")) |
339 | for _, foodGroup in ipairs(foodGroups) do |
340 | local oldAmount = amount |
341 | amount = self:consumeFoodGroup(foodGroup, amount, fillLevels, consumedFood) |
342 | local deltaProdWeight = ((oldAmount - amount) / totalAmountToConsume) * foodGroup.productionWeight |
343 | -- print(string.format("-- [AnimalFoodManager:consumeFoodSerially] deltaProdWeight(%.3f) amount(%.3f) oldAmount(%.3f) totalAmountToConsume(%.3f) foodGroup.productionWeight(%.3f)", |
344 | -- deltaProdWeight, |
345 | -- amount, |
346 | -- oldAmount, |
347 | -- totalAmountToConsume, |
348 | -- foodGroup.productionWeight |
349 | -- )) |
350 | |
351 | productionWeight = productionWeight + deltaProdWeight |
352 | end |
353 | -- print(string.format("-- [AnimalFoodManager:consumeFoodSerially] EXIT: productionWeight(%.3f)", productionWeight)) |
354 | return productionWeight |
355 | end |
90 | function AnimalFoodManager:loadFoodGroups(xmlFile) |
91 | local i = 0 |
92 | |
93 | while true do |
94 | local animalKey = string.format("animalFood.animals.animalFoodGroups(%d)", i) |
95 | if not hasXMLProperty(xmlFile, animalKey) then |
96 | break |
97 | end |
98 | local animalType = getXMLString(xmlFile, animalKey.."#type") |
99 | if animalType ~= nil then |
100 | animalType = animalType:upper() |
101 | self.foodGroups[animalType] = {} |
102 | self.foodGroups[animalType].content = {} |
103 | self.foodGroups[animalType].consumptionType = AnimalFoodManager.FOOD_CONSUME_TYPE_SERIAL |
104 | |
105 | local foodProcessTypeString = getXMLString(xmlFile, animalKey .. "#consumptionType") |
106 | foodProcessTypeString = foodProcessTypeString:upper() |
107 | if foodProcessTypeString == "PARALLEL" then |
108 | self.foodGroups[animalType].consumptionType = AnimalFoodManager.FOOD_CONSUME_TYPE_PARALLEL |
109 | end |
110 | |
111 | local j = 0 |
112 | while true do |
113 | local groupKey = string.format("%s.foodGroup(%d)", animalKey, j) |
114 | if not hasXMLProperty(xmlFile, groupKey) then |
115 | break |
116 | end |
117 | |
118 | local foodGroup = {} |
119 | foodGroup.title = g_i18n:convertText(Utils.getNoNil(getXMLString(xmlFile, groupKey.."#title"), "")) |
120 | foodGroup.productionWeight = Utils.getNoNil(getXMLFloat(xmlFile, groupKey.."#productionWeight"), 0.0) |
121 | foodGroup.eatWeight = Utils.getNoNil(getXMLFloat(xmlFile, groupKey.."#eatWeight"), 1.0) |
122 | foodGroup.fillTypes = {} |
123 | local fillTypesStr = Utils.getNoNil(getXMLString(xmlFile, groupKey.."#fillTypes"), "") |
124 | local warning = string.format("Warning: FillType '%s' undefined for foodGroups of '%s'. Ignoring it!", tostring(fillTypeName), tostring(animalType)) |
125 | foodGroup.fillTypes = g_fillTypeManager:getFillTypesByNames(fillTypesStr, warning) |
126 | table.insert(self.foodGroups[animalType].content, foodGroup) |
127 | j = j + 1 |
128 | end |
129 | end |
130 | i = i + 1 |
131 | end |
132 | return true |
133 | end |
61 | function AnimalFoodManager:loadMapData(xmlFile, missionInfo) |
62 | AnimalFoodManager:superClass().loadMapData(self) |
63 | |
64 | local filename = Utils.getFilename(getXMLString(xmlFile, "map.husbandryFood#filename"), g_currentMission.baseDirectory) |
65 | if filename == nil or filename == "" then |
66 | print("Error: Could not load husbandry food configuration file '" .. tostring(filename) .. "'!") |
67 | return false |
68 | end |
69 | local foodGroupsLoaded = false |
70 | local mixturesLoaded = false |
71 | local foodGroupsNormalized = false |
72 | local mixturesNormalized = false |
73 | local animalFoodXmlFile = loadXMLFile("animalFood", filename) |
74 | |
75 | if animalFoodXmlFile ~= nil then |
76 | foodGroupsLoaded = self:loadFoodGroups(animalFoodXmlFile) |
77 | mixturesLoaded = self:loadMixtures(animalFoodXmlFile) |
78 | foodGroupsNormalized = self:normalizeFoodGroupWeights() |
79 | mixturesNormalized = self:normalizeMixtureWeights() |
80 | delete(animalFoodXmlFile) |
81 | end |
82 | |
83 | return foodGroupsLoaded and mixturesLoaded and foodGroupsNormalized and mixturesNormalized |
84 | end |
139 | function AnimalFoodManager:loadMixtures(xmlFile) |
140 | local i = 0 |
141 | |
142 | while true do |
143 | local mixtureKey = string.format("animalFood.foodMixtures.foodMixture(%d)", i) |
144 | if not hasXMLProperty(xmlFile, mixtureKey) then |
145 | break |
146 | end |
147 | |
148 | local mixtureFillTypeName = Utils.getNoNil(getXMLString(xmlFile, mixtureKey.."#fillType"), "") |
149 | local animalType = Utils.getNoNil(getXMLString(xmlFile, mixtureKey.."#type"), "") |
150 | if animalType ~= nil then |
151 | animalType = animalType:upper() |
152 | if self.animalFoodMixtures[animalType] == nil then |
153 | self.animalFoodMixtures[animalType] = {} |
154 | end |
155 | |
156 | local mixtureFillType = g_fillTypeManager:getFillTypeByName(mixtureFillTypeName) |
157 | if mixtureFillType ~= nil then |
158 | local fillType = mixtureFillType.index |
159 | table.insert(self.animalFoodMixtures[animalType], fillType) |
160 | self.foodMixtures[fillType] = {} |
161 | self.foodMixtures[fillType].ingredients = {} |
162 | |
163 | local j = 0 |
164 | while true do |
165 | local ingredientKey = string.format("%s.ingredient(%d)", mixtureKey, j) |
166 | if not hasXMLProperty(xmlFile, ingredientKey) then |
167 | break |
168 | end |
169 | |
170 | local ingredient = {} |
171 | local warning = string.format("Warning: FillType '%s' undefined for mixture of '%s'. Ignoring it!", tostring(fillTypeName), tostring(mixtureFillTypeName)) |
172 | local fillTypesStr = Utils.getNoNil(getXMLString(xmlFile, ingredientKey.."#fillTypes"), "") |
173 | |
174 | ingredient.fillTypes = g_fillTypeManager:getFillTypesByNames(fillTypesStr, warning) |
175 | ingredient.weight = Utils.getNoNil(getXMLFloat(xmlFile, ingredientKey.."#weight"), 0.0) |
176 | table.insert(self.foodMixtures[fillType].ingredients, ingredient) |
177 | j = j + 1 |
178 | end |
179 | else |
180 | print("Warning: FillType '"..tostring(fillTypeName).."' undefined for mixtures. Ignoring it!") |
181 | return false |
182 | end |
183 | end |
184 | i = i + 1 |
185 | end |
186 | return true |
187 | end |
192 | function AnimalFoodManager:normalizeFoodGroupWeights() |
193 | for _, animalFoodGroup in pairs(self.foodGroups) do |
194 | if animalFoodGroup.consumptionType == AnimalFoodManager.FOOD_CONSUME_TYPE_PARALLEL then |
195 | local sumWeigths = 0 |
196 | local eatWeights = 0 |
197 | for _, foodGroup in pairs(animalFoodGroup.content) do |
198 | sumWeigths = sumWeigths + foodGroup.productionWeight |
199 | eatWeights = eatWeights + foodGroup.eatWeight |
200 | end |
201 | |
202 | for _, foodGroup in pairs(animalFoodGroup.content) do |
203 | if sumWeigths > 0 then |
204 | foodGroup.productionWeight = foodGroup.productionWeight / sumWeigths |
205 | end |
206 | if eatWeights > 0 then |
207 | foodGroup.eatWeight = foodGroup.eatWeight / eatWeights |
208 | end |
209 | end |
210 | end |
211 | end |
212 | |
213 | return true |
214 | end |