185 | function PlaceableGreenhouse:loadPlantFromXml(xmlFilename) |
186 | local plant = { |
187 | i3dFilename = "", |
188 | i3dNode = nil, |
189 | stages = { -- stagse are stored as child indices of meshes -> allows working on cloned nodes without managing node ids |
190 | growing = {}, -- growing stages from small to big |
191 | first = nil, |
192 | last = nil, |
193 | withered = nil, |
194 | } |
195 | } |
196 | |
197 | local plantXmlFile = XMLFile.load("plantXml", xmlFilename, PlaceableGreenhouse.plantXmlSchema) |
198 | if plantXmlFile ~= nil then |
199 | local i3dFilename = plantXmlFile:getValue("greenhousePlant.i3dFilename") |
200 | if i3dFilename ~= nil then |
201 | plant.i3dFilename = Utils.getFilename(i3dFilename, self.baseDirectory) |
202 | |
203 | local loadingTask = self:createLoadingTask() |
204 | |
205 | local arguments = { |
206 | plant = plant, |
207 | plantXmlFile = plantXmlFile, |
208 | loadingTask = loadingTask |
209 | } |
210 | plant.sharedLoadRequestId = g_i3DManager:loadSharedI3DFileAsync(plant.i3dFilename, false, false, self.plantI3DLoadedCallback, self, arguments) |
211 | end |
212 | end |
213 | |
214 | return plant |
215 | end |
256 | function PlaceableGreenhouse:onDelete() |
257 | local spec = self.spec_greenhouse |
258 | |
259 | if spec.growthTimer ~= nil then |
260 | spec.growthTimer:delete() |
261 | end |
262 | if spec.wateringTimer ~= nil then |
263 | spec.wateringTimer:delete() |
264 | end |
265 | |
266 | if spec.filltypeIdToPlant ~= nil then |
267 | for _, plant in pairs(spec.filltypeIdToPlant) do |
268 | if plant.sharedLoadRequestId ~= nil then |
269 | g_i3DManager:releaseSharedI3DFile(plant.sharedLoadRequestId) |
270 | end |
271 | |
272 | if plant.i3dNode ~= nil then |
273 | delete(plant.i3dNode) |
274 | plant.i3dNode = nil |
275 | end |
276 | end |
277 | end |
278 | |
279 | g_effectManager:deleteEffects(spec.effects) |
280 | g_soundManager:deleteSamples(spec.samples) |
281 | end |
94 | function PlaceableGreenhouse:onLoad(savegame) |
95 | local spec = self.spec_greenhouse |
96 | local xmlFile = self.xmlFile |
97 | local key = "placeable.greenhouse" |
98 | |
99 | spec.filltypeIdToPlant = {} |
100 | spec.plantPlaces = {} |
101 | |
102 | spec.activeFilltypes = {} |
103 | spec.hasWater = true |
104 | |
105 | xmlFile:iterate(key .. ".plants.plant", function(index, plantKey) |
106 | local fillTypeName = xmlFile:getValue(plantKey .. "#fillType") |
107 | local fillType = g_fillTypeManager:getFillTypeIndexByName(fillTypeName) |
108 | if fillType ~= nil then |
109 | local plantXmlFilename = xmlFile:getValue(plantKey .. "#xmlFilename") |
110 | if plantXmlFilename ~= nil then |
111 | plantXmlFilename = Utils.getFilename(plantXmlFilename, self.baseDirectory) |
112 | local plant = self:loadPlantFromXml(plantXmlFilename) |
113 | if plant ~= nil then |
114 | spec.filltypeIdToPlant[fillType] = plant |
115 | end |
116 | end |
117 | else |
118 | Logging.xmlWarning(xmlFile, "Unknown fillType '%s' for plant '%s'", fillTypeName, plantKey) |
119 | end |
120 | end) |
121 | |
122 | xmlFile:iterate(key .. ".plantSpaces.space", function(index, plantSpaceKey) |
123 | local plantPlaceNode = self.xmlFile:getValue(plantSpaceKey .. "#node", nil, self.components, self.i3dMappings) |
124 | local useRandomYRot = self.xmlFile:getValue(plantSpaceKey .. "#useRandomYRot", true) |
125 | if plantPlaceNode ~= nil then |
126 | self:addPlantPlace(plantPlaceNode, useRandomYRot) |
127 | end |
128 | end) |
129 | |
130 | xmlFile:iterate(key .. ".plantSpaces.spacesParent", function(index, plantParentKey) |
131 | local parentNode = self.xmlFile:getValue(plantParentKey .. "#node", nil, self.components, self.i3dMappings) |
132 | local useRandomYRot = self.xmlFile:getValue(plantParentKey .. "#useRandomYRot", true) |
133 | local numChildren = getNumOfChildren(parentNode) |
134 | if numChildren > 0 then |
135 | for i=0, numChildren-1 do |
136 | self:addPlantPlace(getChildAt(parentNode, i), useRandomYRot) |
137 | end |
138 | else |
139 | Logging.xmlWarning(xmlFile, "No i3d child nodes for '%s'", plantParentKey) |
140 | end |
141 | end) |
142 | |
143 | if self.isClient then |
144 | spec.samples = {} |
145 | spec.samples.watering = g_soundManager:loadSampleFromXML(xmlFile, key .. ".sounds", "watering", self.baseDirectory, self.components, 1, AudioGroup.ENVIRONMENT, self.i3dMappings, nil) |
146 | |
147 | spec.effects = g_effectManager:loadEffect(xmlFile, key .. ".effectNodes", self.components, self, self.i3dMappings) |
148 | end |
149 | |
150 | spec.growthTimer = Timer.new(PlaceableGreenhouse.getRandomGrowthInterval()) |
151 | spec.growthTimer:setFinishCallback(function(timerInstance) |
152 | timerInstance:setDuration(PlaceableGreenhouse.getRandomGrowthInterval()) |
153 | self:updatePlantsStage() -- restarts timer if reasonable |
154 | end) |
155 | |
156 | spec.wateringTimer = Timer.new(PlaceableGreenhouse.getRandomWateringInterval()) |
157 | spec.wateringTimer:setFinishCallback(function(timerInstance) |
158 | if spec.hasWater then |
159 | g_effectManager:startEffects(spec.effects) |
160 | g_soundManager:playSample(spec.samples.watering) |
161 | |
162 | Timer.createOneshot(PlaceableGreenhouse.WATERING_DURATION_SECONDS * 1000, function() |
163 | g_effectManager:stopEffects(spec.effects) |
164 | g_soundManager:stopSample(spec.samples.watering) |
165 | end) |
166 | timerInstance:start() -- restart for next watering |
167 | end |
168 | end) |
169 | end |
219 | function PlaceableGreenhouse:plantI3DLoadedCallback(i3dNode, failedReason, args) |
220 | local plant = args.plant |
221 | local plantXmlFile = args.plantXmlFile |
222 | local loadingTask = args.loadingTask |
223 | |
224 | if i3dNode ~= 0 then |
225 | local components = {} |
226 | local i3dMappings = {} |
227 | I3DUtil.loadI3DComponents(i3dNode, components) |
228 | I3DUtil.loadI3DMapping(plantXmlFile, i3dNode, components, i3dMappings) |
229 | |
230 | plant.i3dNode = i3dNode |
231 | |
232 | plantXmlFile:iterate("greenhousePlant.stages.growing", function(index, key) |
233 | local growingNode = plantXmlFile:getValue(key .. "#node", nil, i3dNode) |
234 | if growingNode ~= nil then |
235 | local childIndex = getChildIndex(growingNode) |
236 | table.insert(plant.stages.growing, childIndex) |
237 | end |
238 | end) |
239 | |
240 | plant.stages.first = plant.stages.growing[1] |
241 | plant.stages.last = plant.stages.growing[#plant.stages.growing] |
242 | |
243 | local witheredNode = plantXmlFile:getValue("greenhousePlant.stages.withered#node", nil, plant.i3dNode) |
244 | if witheredNode ~= nil then |
245 | plant.stages.withered = getChildIndex(witheredNode) |
246 | end |
247 | end |
248 | |
249 | plantXmlFile:delete() |
250 | |
251 | self:finishLoadingTask(loadingTask) |
252 | end |
77 | function PlaceableGreenhouse.registerXMLPaths(schema, basePath) |
78 | schema:setXMLSpecializationType("Greenhouse") |
79 | schema:register(XMLValueType.STRING, basePath .. ".greenhouse.plants.plant(?)#fillType", "FillType of plant") |
80 | schema:register(XMLValueType.STRING, basePath .. ".greenhouse.plants.plant(?)#xmlFilename", "xml file of greenhouse plant") |
81 | |
82 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".greenhouse.plantSpaces.space(?)#node", "node where plant is placed") |
83 | schema:register(XMLValueType.BOOL, basePath .. ".greenhouse.plantSpaces.space(?)#useRandomYRot", "node is randomly rotated on the y axis", true) |
84 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".greenhouse.plantSpaces.spacesParent(?)#node", "parent node of nodes where plants are placed") |
85 | schema:register(XMLValueType.BOOL, basePath .. ".greenhouse.plantSpaces.spacesParent(?)#useRandomYRot", "node is randomly rotated on the y axis", true) |
86 | |
87 | SoundManager.registerSampleXMLPaths(schema, basePath .. ".greenhouse.sounds", "watering") |
88 | EffectManager.registerEffectXMLPaths(schema, basePath .. ".greenhouse.effectNodes") |
89 | schema:setXMLSpecializationType() |
90 | end |
366 | function PlaceableGreenhouse:setPlantAtPlace(fillType, plantPlace) |
367 | local spec = self.spec_greenhouse |
368 | |
369 | -- only update if required fillType differs from current one |
370 | if plantPlace.fillType ~= fillType then |
371 | if plantPlace.fillType ~= nil then |
372 | -- remove existing stages from other plant |
373 | for i=getNumOfChildren(plantPlace.node)-1, 0, -1 do |
374 | local plantStage = getChildAt(plantPlace.node, i) |
375 | delete(plantStage) |
376 | end |
377 | plantPlace.fillType = nil |
378 | end |
379 | |
380 | local plant = spec.filltypeIdToPlant[fillType] |
381 | if plant ~= nil then |
382 | local plantClone = clone(getChildAt(plant.i3dNode, 0), false, false, false) |
383 | |
384 | for n=getNumOfChildren(plantClone)-1, 0, -1 do |
385 | local plantStage = getChildAt(plantClone, n) |
386 | link(plantPlace.node, plantStage, 0) |
387 | end |
388 | plantPlace.fillType = fillType |
389 | plantPlace.stage = nil |
390 | |
391 | delete(plantClone) |
392 | end |
393 | end |
394 | end |
398 | function PlaceableGreenhouse:updatePlantsStage() |
399 | local spec = self.spec_greenhouse |
400 | |
401 | if table.size(spec.activeFilltypes) == 0 then |
402 | return |
403 | end |
404 | |
405 | if not spec.hasWater then |
406 | spec.growthTimer:stop() |
407 | else |
408 | spec.growthTimer:start() |
409 | end |
410 | |
411 | for i=1, #spec.plantPlaces do |
412 | local plantPlace = spec.plantPlaces[i] |
413 | |
414 | local plant = spec.filltypeIdToPlant[plantPlace.fillType] |
415 | |
416 | local newStage |
417 | if not spec.hasWater then |
418 | newStage = plant.stages.withered |
419 | else |
420 | newStage = (plantPlace.stage and (plantPlace.stage + 1)) or plant.stages.first |
421 | if newStage > plant.stages.last then |
422 | newStage = plant.stages.first |
423 | end |
424 | end |
425 | |
426 | if plantPlace.stage ~= newStage then |
427 | for n=0, getNumOfChildren(plantPlace.node)-1 do |
428 | local plantStage = getChildAt(plantPlace.node, n) |
429 | setVisibility(plantStage, n == newStage) |
430 | end |
431 | plantPlace.stage = newStage |
432 | end |
433 | end |
434 | end |