546 | function Placeable:delete() |
547 | if self.isDeleted then |
548 | Logging.devError("Trying to delete a already deleted placeable") |
549 | printCallstack() |
550 | return |
551 | end |
552 | |
553 | g_messageCenter:unsubscribeAll(self) |
554 | |
555 | self.isDeleting = true |
556 | SpecializationUtil.raiseEvent(self, "onPreDelete") |
557 | |
558 | -- Remove from placeables to delete list, so that base mission doesn't try to double delete |
559 | g_currentMission:removePlaceableToDelete(self) |
560 | g_currentMission.placeableSystem:removePlaceable(self) |
561 | g_currentMission:removeOwnedItem(self) |
562 | |
563 | if self.sharedLoadRequestId ~= nil then |
564 | g_i3DManager:releaseSharedI3DFile(self.sharedLoadRequestId) |
565 | self.sharedLoadRequestId = nil |
566 | end |
567 | |
568 | for _, node in pairs(self.pickObjects) do |
569 | g_currentMission:removeNodeObject(node) |
570 | end |
571 | |
572 | SpecializationUtil.raiseEvent(self, "onDelete") |
573 | |
574 | if self.boughtWithFarmland and self.isServer then |
575 | g_farmlandManager:removeStateChangeListener(self) |
576 | end |
577 | |
578 | if self.rootNode ~= nil then |
579 | delete(self.rootNode) |
580 | self.rootNode = nil |
581 | end |
582 | |
583 | if self.xmlFile ~= nil then |
584 | self.xmlFile:delete() |
585 | self.xmlFile = nil |
586 | end |
587 | |
588 | self.isDeleting = false |
589 | self.isDeleted = true |
590 | |
591 | Placeable:superClass().delete(self) |
592 | end |
499 | function Placeable:finalizePlacement() |
500 | SpecializationUtil.raiseEvent(self, "onPreFinalizePlacement") |
501 | |
502 | self:addToPhysics() |
503 | |
504 | g_currentMission.placeableSystem:addPlaceable(self) |
505 | g_currentMission:addOwnedItem(self) |
506 | |
507 | self:collectPickObjects(self.rootNode) |
508 | for _, node in pairs(self.pickObjects) do |
509 | g_currentMission:addNodeObject(node, self) |
510 | end |
511 | |
512 | local x,_,z = getWorldTranslation(self.rootNode) |
513 | self.farmlandId = g_farmlandManager:getFarmlandIdAtWorldPosition(x, z) |
514 | |
515 | if self.boughtWithFarmland then |
516 | if self.isServer then |
517 | self:updateOwnership() |
518 | end |
519 | |
520 | g_farmlandManager:addStateChangeListener(self) |
521 | end |
522 | SpecializationUtil.raiseEvent(self, "onFinalizePlacement") |
523 | |
524 | SpecializationUtil.raiseEvent(self, "onPostFinalizePlacement") |
525 | |
526 | if self:getNeedWeatherChanged() then |
527 | g_messageCenter:subscribe(MessageType.WEATHER_CHANGED, self.weatherChanged, self) |
528 | end |
529 | if self:getNeedHourChanged() then |
530 | g_messageCenter:subscribe(MessageType.HOUR_CHANGED, self.hourChanged, self) |
531 | end |
532 | if self:getNeedMinuteChanged() then |
533 | g_messageCenter:subscribe(MessageType.MINUTE_CHANGED, self.minuteChanged, self) |
534 | end |
535 | if self:getNeedDayChanged() then |
536 | g_messageCenter:subscribe(MessageType.DAY_CHANGED, self.dayChanged, self) |
537 | end |
538 | g_messageCenter:subscribe(MessageType.PERIOD_CHANGED, self.periodChanged, self) |
539 | |
540 | g_messageCenter:publish(MessageType.FARM_PROPERTY_CHANGED, self:getOwnerFarmId()) |
541 | end |
133 | function Placeable.init() |
134 | local schema = Placeable.xmlSchema |
135 | |
136 | local basePath = "placeable" |
137 | schema:register(XMLValueType.STRING, basePath .. "#type", "Placeable type", nil, true) |
138 | schema:register(XMLValueType.STRING, basePath .. ".annotation", "Annotation", nil, false) |
139 | schema:register(XMLValueType.STRING, basePath .. ".base.filename", "Placeable i3d file", nil, true) |
140 | schema:register(XMLValueType.BOOL, basePath .. ".base.canBeRenamed", "Placeable can be renamed by player", false) |
141 | schema:register(XMLValueType.BOOL, basePath .. ".base.boughtWithFarmland", "Placeable is bough with farmland", false) |
142 | schema:register(XMLValueType.BOOL, basePath .. ".base.buysFarmland", "Placeable buys farmland it is placed on", false) |
143 | schema:register(XMLValueType.BOOL, basePath .. ".base.canBeDeleted", "Placeable can be deleted by the player, set to false if it should be set farm 0 on sell instead", true) |
144 | StoreManager.registerStoreDataXMLPaths(schema, basePath) |
145 | I3DUtil.registerI3dMappingXMLPaths(schema, basePath) |
146 | |
147 | |
148 | local savegameSchema = Placeable.xmlSchemaSavegame |
149 | local basePathSavegame = "placeables.placeable(?)" |
150 | savegameSchema:register(XMLValueType.BOOL, "placeables#loadAnyFarmInSingleplayer", "Load any farm in singleplayer. Causes any placeable with any farmId to be loaded.", false) |
151 | savegameSchema:register(XMLValueType.INT, "placeables#version", "Version of map placeables file") |
152 | savegameSchema:register(XMLValueType.STRING, basePathSavegame .. "#name", "Custom name set by player to be used instead of store item name") |
153 | savegameSchema:register(XMLValueType.STRING, basePathSavegame .. "#mapBoundId", "Map bound identifier (defines that a placeable is placed on a map directly, and with a unique ID)") |
154 | savegameSchema:register(XMLValueType.VECTOR_TRANS, basePathSavegame .. "#position", "Position") |
155 | savegameSchema:register(XMLValueType.VECTOR_ROT, basePathSavegame .. "#rotation", "Rotation") |
156 | savegameSchema:register(XMLValueType.STRING, basePathSavegame .. "#filename", "Path to xml filename") |
157 | savegameSchema:register(XMLValueType.FLOAT, basePathSavegame .. "#age", "Age of placeable in months.", 0) |
158 | savegameSchema:register(XMLValueType.FLOAT, basePathSavegame .. "#price", "Price of placeable") |
159 | savegameSchema:register(XMLValueType.INT, basePathSavegame .. "#farmId", "Owner farmland", 0) |
160 | savegameSchema:register(XMLValueType.INT, basePathSavegame .. "#id", "Save id") |
161 | savegameSchema:register(XMLValueType.BOOL, basePathSavegame .. "#defaultFarmProperty", "Is property of default farm. Causes object to be removed on non-starter games.", false) |
162 | savegameSchema:register(XMLValueType.STRING, basePathSavegame .. "#modName", "Name of mod") |
163 | savegameSchema:register(XMLValueType.INT, basePathSavegame .. "#sinceVersion", "Version of xml file when this placeable was added. Will cause placeable to appear on older, existing saves") |
164 | |
165 | for name, spec in pairs(g_placeableSpecializationManager:getSpecializations()) do |
166 | local classObj = ClassUtil.getClassObject(spec.className) |
167 | if classObj ~= nil then |
168 | if rawget(classObj, "registerXMLPaths") then |
169 | classObj.registerXMLPaths(schema, basePath) |
170 | end |
171 | |
172 | if rawget(classObj, "registerSavegameXMLPaths") then |
173 | classObj.registerSavegameXMLPaths(savegameSchema, basePathSavegame .. "." .. name) |
174 | end |
175 | end |
176 | end |
177 | |
178 | g_storeManager:addSpecType("placeableSlots", "shopListAttributeIconSlots", nil, Placeable.getSpecValueSlots, "placeable") |
179 | end |
226 | function Placeable:load(placeableData, asyncCallbackFunction, asyncCallbackObject, asyncCallbackArguments) |
227 | self.asyncData = {} |
228 | self.asyncData.callback = asyncCallbackFunction |
229 | self.asyncData.object = asyncCallbackObject |
230 | self.asyncData.arguments = asyncCallbackArguments |
231 | |
232 | if asyncCallbackFunction == nil then |
233 | self:onLoadingError("Missing asyncCallbackFunction. Placeable only supports async loading!") |
234 | return |
235 | end |
236 | |
237 | local modName, baseDirectory = Utils.getModNameAndBaseDirectory(placeableData.filename) |
238 | |
239 | self:setLoadingStep(Placeable.LOAD_STEP_PRE_LOAD) |
240 | |
241 | self.configFileName = placeableData.filename |
242 | |
243 | self.baseDirectory = baseDirectory |
244 | self.customEnvironment = modName |
245 | self.typeName = placeableData.typeName |
246 | |
247 | local typeDef = g_placeableTypeManager:getTypeByName(self.typeName) |
248 | if typeDef == nil then |
249 | self:onLoadingError("Unable to find placeable type '%s'", self.typeName) |
250 | return |
251 | end |
252 | |
253 | self.type = typeDef |
254 | self.specializations = typeDef.specializations |
255 | self.specializationNames = typeDef.specializationNames |
256 | self.specializationsByName = typeDef.specializationsByName |
257 | self.eventListeners = table.copy(typeDef.eventListeners, 2) |
258 | |
259 | self.xmlFile = XMLFile.load("placeableXml", placeableData.filename, Placeable.xmlSchema) |
260 | self.savegame = placeableData.savegame |
261 | |
262 | self.position = {x=placeableData.posX, y=placeableData.posY, z=placeableData.posZ} |
263 | self.rotation = {x=placeableData.rotX, y=placeableData.rotY, z=placeableData.rotZ} |
264 | |
265 | if placeableData.ownerFarmId ~= nil then |
266 | self:setOwnerFarmId(placeableData.ownerFarmId, true) |
267 | end |
268 | |
269 | self.storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
270 | if self.storeItem ~= nil then |
271 | self.brand = g_brandManager:getBrandByIndex(self.storeItem.brandIndex) |
272 | |
273 | if self.price == 0 or self.price == nil then |
274 | self.price = StoreItemUtil.getDefaultPrice(self.storeItem) |
275 | end |
276 | else |
277 | self:onLoadingError("Missing storeItem for placable '%s'", self.configFileName) |
278 | return |
279 | end |
280 | |
281 | -- pass function pointers from specializations to 'self' |
282 | for funcName, func in pairs(typeDef.functions) do |
283 | self[funcName] = func |
284 | end |
285 | |
286 | for i=1, #self.specializations do |
287 | local specEntryName = "spec_" .. self.specializationNames[i] |
288 | if self[specEntryName] ~= nil then |
289 | self:onLoadingError("The placeable specialization '%s' could not be added because variable '%s' already exists!", self.specializationNames[i], specEntryName) |
290 | return |
291 | end |
292 | |
293 | local env = setmetatable({}, { __index = self } ) |
294 | self[specEntryName] = env |
295 | end |
296 | |
297 | SpecializationUtil.raiseEvent(self, "onPreLoad", self.savegame) |
298 | if self.loadingState ~= Placeable.LOADING_STATE_OK then |
299 | self:onLoadingError("Placeable pre-loading failed!") |
300 | return |
301 | end |
302 | |
303 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "placeable.filename", "placeable.base.filename") |
304 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "placeable.dayNightObjects", "Visiblity Condition-Tab in GIANTS Editor / Exporter") |
305 | |
306 | self.i3dFilename = self.xmlFile:getValue("placeable.base.filename") |
307 | if self.i3dFilename == nil then |
308 | self:onLoadingError("Placeable filename missing!") |
309 | return |
310 | end |
311 | |
312 | self.canBeRenamed = self.xmlFile:getValue("placeable.base.canBeRenamed", false) |
313 | self.boughtWithFarmland = self.xmlFile:getValue("placeable.base.boughtWithFarmland", false) |
314 | self.buysFarmland = self.xmlFile:getValue("placeable.base.buysFarmland", false) |
315 | self.canBeDeleted = self.xmlFile:getValue("placeable.base.canBeDeleted", true) |
316 | |
317 | if self.storeItem.showInStore and not self.canBeDeleted then |
318 | self:onLoadingError('Store item can be manually placed (showInStore.showInStore) but not deleted by the player (base.canBeDeleted=false)! Only use this option for preplaced placeables') |
319 | return |
320 | end |
321 | |
322 | self:setLoadingStep(Placeable.LOAD_STEP_AWAIT_I3D) |
323 | |
324 | self.i3dFilename = Utils.getFilename(self.i3dFilename, baseDirectory) |
325 | self.sharedLoadRequestId = g_i3DManager:loadSharedI3DFileAsync(self.i3dFilename, true, false, self.loadI3dFinished, self, nil) |
326 | end |
330 | function Placeable:loadI3dFinished(i3dNode, failedReason, args) |
331 | self:setLoadingState(Placeable.LOADING_STATE_OK) |
332 | self:setLoadingStep(Placeable.LOAD_STEP_LOAD) |
333 | |
334 | self:removeFromPhysics() |
335 | |
336 | if i3dNode == 0 then |
337 | self:onLoadingError("Placeable i3d loading failed!!") |
338 | return |
339 | end |
340 | |
341 | self.rootNode = i3dNode |
342 | |
343 | link(getRootNode(), i3dNode) |
344 | |
345 | I3DUtil.loadI3DComponents(i3dNode, self.components) |
346 | |
347 | if #self.components == 0 then |
348 | self:onLoadingError("Unable to get placeable components") |
349 | return |
350 | end |
351 | |
352 | I3DUtil.loadI3DMapping(self.xmlFile, "placeable", self.components, self.i3dMappings) |
353 | |
354 | self:initPose() |
355 | |
356 | SpecializationUtil.raiseEvent(self, "onLoad", self.savegame) |
357 | if self.loadingState ~= Placeable.LOADING_STATE_OK then |
358 | self:onLoadingError("Placeable loading failed!") |
359 | return |
360 | end |
361 | |
362 | self:setLoadingStep(Placeable.LOAD_STEP_POST_LOAD) |
363 | |
364 | SpecializationUtil.raiseEvent(self, "onPostLoad", self.savegame) |
365 | if self.loadingState ~= Placeable.LOADING_STATE_OK then |
366 | self:onLoadingError("Placeable post-loading failed!") |
367 | return |
368 | end |
369 | |
370 | self:setVisibility(false) |
371 | |
372 | if #self.loadingTasks == 0 then |
373 | self:onFinishedLoading() |
374 | else |
375 | self.readyForFinishLoading = true |
376 | self:setLoadingStep(Placeable.LOAD_STEP_AWAIT_SUB_I3D) |
377 | end |
378 | end |
382 | function Placeable:onFinishedLoading() |
383 | self:setVisibility(true) |
384 | |
385 | if self.isServer and self.savegame ~= nil then |
386 | self.currentSavegameId = self.savegame.xmlFile:getValue(self.savegame.key .. "#id") |
387 | |
388 | self.isLoadingFromSavegameXML = true |
389 | for id, spec in pairs(self.specializations) do |
390 | local name = self.specializationNames[id] |
391 | |
392 | if spec.loadFromXMLFile ~= nil then |
393 | spec.loadFromXMLFile(self, self.savegame.xmlFile, self.savegame.key.."."..name, self.savegame.reset) |
394 | end |
395 | end |
396 | self.isLoadingFromSavegameXML = false |
397 | |
398 | self.mapBoundId = self.savegame.xmlFile:getValue(self.savegame.key .. "#mapBoundId", self.mapBoundId) |
399 | |
400 | self.name = self.savegame.xmlFile:getValue(self.savegame.key .. "#name") |
401 | |
402 | self.age = self.savegame.xmlFile:getValue(self.savegame.key.."#age", 0) |
403 | self.price = self.savegame.xmlFile:getValue(self.savegame.key.."#price", self.price) |
404 | |
405 | if not self.savegame.ignoreFarmId then |
406 | -- Use a call so any sub-objects of the placeable can be updated |
407 | self:setOwnerFarmId(self.savegame.xmlFile:getValue(self.savegame.key .. "#farmId", AccessHandler.EVERYONE), true) |
408 | end |
409 | |
410 | self.isLoadedFromSavegame = true |
411 | end |
412 | |
413 | self:setLoadingStep(Placeable.LOAD_STEP_FINISHED) |
414 | SpecializationUtil.raiseEvent(self, "onLoadFinished", self.savegame) |
415 | |
416 | if self.isLoadedFromSavegame then |
417 | self:finalizePlacement() |
418 | end |
419 | |
420 | -- if we are the server or in single player we don't need to be synchonized |
421 | if self.isServer then |
422 | self:setLoadingStep(Placeable.LOAD_STEP_SYNCHRONIZED) |
423 | end |
424 | |
425 | self.finishedLoading = true |
426 | |
427 | self:raiseLoadingCallback() |
428 | |
429 | self.savegame = nil |
430 | end |
662 | function Placeable:postReadStream(streamId, connection) |
663 | self:finalizePlacement() |
664 | |
665 | if Placeable.DEBUG_NETWORK then |
666 | print("-------------------------------------------------------------") |
667 | print(self.configFileName) |
668 | for _, spec in ipairs(self.eventListeners["onReadStream"]) do |
669 | local className = ClassUtil.getClassName(spec) |
670 | local startBits = streamGetReadOffset(streamId) |
671 | spec["onReadStream"](self, streamId, connection) |
672 | print(" "..tostring(className).." read " .. streamGetReadOffset(streamId)-startBits .. " bits") |
673 | end |
674 | else |
675 | SpecializationUtil.raiseEvent(self, "onReadStream", streamId, connection) |
676 | end |
677 | |
678 | if streamReadBool(streamId) then |
679 | self:setName(streamReadString(streamId), true) |
680 | end |
681 | |
682 | self:setLoadingStep(Placeable.LOAD_STEP_SYNCHRONIZED) |
683 | |
684 | self:raiseActive() |
685 | end |
691 | function Placeable:postWriteStream(streamId, connection) |
692 | if Placeable.DEBUG_NETWORK then |
693 | print("-------------------------------------------------------------") |
694 | print(self.configFileName) |
695 | for _, spec in ipairs(self.eventListeners["onWriteStream"]) do |
696 | local className = ClassUtil.getClassName(spec) |
697 | local startBits = streamGetWriteOffset(streamId) |
698 | spec["onWriteStream"](self, streamId, connection) |
699 | print(" "..tostring(className).." Wrote " .. streamGetWriteOffset(streamId)-startBits .. " bits") |
700 | end |
701 | else |
702 | SpecializationUtil.raiseEvent(self, "onWriteStream", streamId, connection) |
703 | end |
704 | |
705 | if streamWriteBool(streamId, self.name ~= nil) then |
706 | streamWriteString(streamId, self.name) |
707 | end |
708 | end |
598 | function Placeable:readStream(streamId, connection, objectId) |
599 | Placeable:superClass().readStream(self, streamId, connection, objectId) |
600 | |
601 | local configFileName = NetworkUtil.convertFromNetworkFilename(streamReadString(streamId)) |
602 | local typeName = streamReadString(streamId) |
603 | |
604 | if configFileName ~= nil then |
605 | local data = {} |
606 | data.filename = configFileName |
607 | data.typeName = typeName |
608 | data.posX = streamReadFloat32(streamId) |
609 | data.posY = streamReadFloat32(streamId) |
610 | data.posZ = streamReadFloat32(streamId) |
611 | data.rotX = NetworkUtil.readCompressedAngle(streamId) |
612 | data.rotY = NetworkUtil.readCompressedAngle(streamId) |
613 | data.rotZ = NetworkUtil.readCompressedAngle(streamId) |
614 | data.initRandom = false |
615 | |
616 | local isNew = self.configFileName == nil |
617 | |
618 | local placeable = self |
619 | local asyncCallbackFunction = function(_, p, loadingState, args) |
620 | if loadingState == Placeable.LOADING_STATE_OK then |
621 | g_client:onObjectFinishedAsyncLoading(placeable) |
622 | else |
623 | Logging.error("Failed to load placeable on client") |
624 | if p ~= nil then |
625 | p:delete() |
626 | end |
627 | printCallstack() |
628 | return |
629 | end |
630 | end |
631 | |
632 | if isNew then |
633 | self:load(data, asyncCallbackFunction) |
634 | end |
635 | end |
636 | end |
63 | function Placeable.registerEvents(placeableType) |
64 | SpecializationUtil.registerEvent(placeableType, "onPreLoad") |
65 | SpecializationUtil.registerEvent(placeableType, "onLoad") |
66 | SpecializationUtil.registerEvent(placeableType, "onPostLoad") |
67 | SpecializationUtil.registerEvent(placeableType, "onLoadFinished") |
68 | SpecializationUtil.registerEvent(placeableType, "onPreDelete") |
69 | SpecializationUtil.registerEvent(placeableType, "onDelete") |
70 | SpecializationUtil.registerEvent(placeableType, "onSave") |
71 | SpecializationUtil.registerEvent(placeableType, "onReadStream") |
72 | SpecializationUtil.registerEvent(placeableType, "onWriteStream") |
73 | SpecializationUtil.registerEvent(placeableType, "onReadUpdateStream") |
74 | SpecializationUtil.registerEvent(placeableType, "onWriteUpdateStream") |
75 | SpecializationUtil.registerEvent(placeableType, "onPreFinalizePlacement") |
76 | SpecializationUtil.registerEvent(placeableType, "onFinalizePlacement") |
77 | SpecializationUtil.registerEvent(placeableType, "onPostFinalizePlacement") |
78 | SpecializationUtil.registerEvent(placeableType, "onUpdate") |
79 | SpecializationUtil.registerEvent(placeableType, "onUpdateTick") |
80 | SpecializationUtil.registerEvent(placeableType, "onDraw") |
81 | SpecializationUtil.registerEvent(placeableType, "onHourChanged") |
82 | SpecializationUtil.registerEvent(placeableType, "onMinuteChanged") |
83 | SpecializationUtil.registerEvent(placeableType, "onDayChanged") |
84 | SpecializationUtil.registerEvent(placeableType, "onPeriodChanged") |
85 | SpecializationUtil.registerEvent(placeableType, "onWeatherChanged") |
86 | SpecializationUtil.registerEvent(placeableType, "onFarmlandStateChanged") |
87 | SpecializationUtil.registerEvent(placeableType, "onBuy") |
88 | SpecializationUtil.registerEvent(placeableType, "onSell") |
89 | SpecializationUtil.registerEvent(placeableType, "onOwnerChanged") |
90 | end |
94 | function Placeable.registerFunctions(placeableType) |
95 | SpecializationUtil.registerFunction(placeableType, "setOwnerFarmId", Placeable.setOwnerFarmId) |
96 | SpecializationUtil.registerFunction(placeableType, "setLoadingStep", Placeable.setLoadingStep) |
97 | SpecializationUtil.registerFunction(placeableType, "setLoadingState", Placeable.setLoadingState) |
98 | SpecializationUtil.registerFunction(placeableType, "addToPhysics", Placeable.addToPhysics) |
99 | SpecializationUtil.registerFunction(placeableType, "removeFromPhysics", Placeable.removeFromPhysics) |
100 | SpecializationUtil.registerFunction(placeableType, "raiseLoadingCallback", Placeable.raiseLoadingCallback) |
101 | SpecializationUtil.registerFunction(placeableType, "collectPickObjects", Placeable.collectPickObjects) |
102 | SpecializationUtil.registerFunction(placeableType, "getNeedWeatherChanged", Placeable.getNeedWeatherChanged) |
103 | SpecializationUtil.registerFunction(placeableType, "getNeedHourChanged", Placeable.getNeedHourChanged) |
104 | SpecializationUtil.registerFunction(placeableType, "getNeedMinuteChanged", Placeable.getNeedMinuteChanged) |
105 | SpecializationUtil.registerFunction(placeableType, "getNeedDayChanged", Placeable.getNeedDayChanged) |
106 | SpecializationUtil.registerFunction(placeableType, "initPose", Placeable.initPose) |
107 | SpecializationUtil.registerFunction(placeableType, "getName", Placeable.getName) |
108 | SpecializationUtil.registerFunction(placeableType, "getImageFilename", Placeable.getImageFilename) |
109 | SpecializationUtil.registerFunction(placeableType, "getCanBeRenamedByFarm", Placeable.getCanBeRenamedByFarm) |
110 | SpecializationUtil.registerFunction(placeableType, "setName", Placeable.setName) |
111 | SpecializationUtil.registerFunction(placeableType, "getPrice", Placeable.getPrice) |
112 | SpecializationUtil.registerFunction(placeableType, "canBuy", Placeable.canBuy) |
113 | SpecializationUtil.registerFunction(placeableType, "getCanBePlacedAt", Placeable.getCanBePlacedAt) |
114 | SpecializationUtil.registerFunction(placeableType, "canBeSold", Placeable.canBeSold) |
115 | SpecializationUtil.registerFunction(placeableType, "isMapBound", Placeable.isMapBound) |
116 | SpecializationUtil.registerFunction(placeableType, "getDestructionMethod", Placeable.getDestructionMethod) |
117 | SpecializationUtil.registerFunction(placeableType, "previewNodeDestructionNodes", Placeable.previewNodeDestructionNodes) |
118 | SpecializationUtil.registerFunction(placeableType, "performNodeDestruction", Placeable.performNodeDestruction) |
119 | SpecializationUtil.registerFunction(placeableType, "updateOwnership", Placeable.updateOwnership) |
120 | SpecializationUtil.registerFunction(placeableType, "setOverlayColor", Placeable.setOverlayColor) |
121 | SpecializationUtil.registerFunction(placeableType, "setOverlayColorNodes", Placeable.setOverlayColorNodes) |
122 | SpecializationUtil.registerFunction(placeableType, "getDailyUpkeep", Placeable.getDailyUpkeep) |
123 | SpecializationUtil.registerFunction(placeableType, "getSellPrice", Placeable.getSellPrice) |
124 | SpecializationUtil.registerFunction(placeableType, "setPreviewPosition", Placeable.setPreviewPosition) |
125 | SpecializationUtil.registerFunction(placeableType, "setPropertyState", Placeable.setPropertyState) |
126 | SpecializationUtil.registerFunction(placeableType, "getPropertyState", Placeable.getPropertyState) |
127 | SpecializationUtil.registerFunction(placeableType, "setVisibility", Placeable.setVisibility) |
128 | SpecializationUtil.registerFunction(placeableType, "getIsSynchronized", Placeable.getIsSynchronized) |
129 | end |
755 | function Placeable:saveToXMLFile(xmlFile, key, usedModNames) |
756 | local x,y,z = getTranslation(self.rootNode) |
757 | local xRot,yRot,zRot = getRotation(self.rootNode) |
758 | |
759 | xmlFile:setValue(key.."#id", self.currentSavegameId) |
760 | xmlFile:setValue(key.."#filename", HTMLUtil.encodeToHTML(NetworkUtil.convertToNetworkFilename(self.configFileName))) |
761 | xmlFile:setValue(key.."#position", x, y, z) |
762 | xmlFile:setValue(key.."#rotation", xRot, yRot, zRot) |
763 | xmlFile:setValue(key.."#age", self.age) |
764 | xmlFile:setValue(key.."#price", self.price) |
765 | xmlFile:setValue(key.."#farmId", self:getOwnerFarmId() or 1) |
766 | |
767 | -- custom name set by player |
768 | if self.canBeRenamed and self.name ~= nil and self.name:trim() ~= "" then |
769 | xmlFile:setValue(key.."#name", self.name) |
770 | end |
771 | |
772 | if self.mapBoundId ~= nil then |
773 | xmlFile:setValue(key.."#mapBoundId", self.mapBoundId) |
774 | end |
775 | |
776 | for id, spec in pairs(self.specializations) do |
777 | local name = self.specializationNames[id] |
778 | |
779 | if spec.saveToXMLFile ~= nil then |
780 | spec.saveToXMLFile(self, xmlFile, key.."."..name, usedModNames) |
781 | end |
782 | end |
783 | end |
642 | function Placeable:writeStream(streamId, connection) |
643 | Placeable:superClass().writeStream(self, streamId, connection) |
644 | |
645 | streamWriteString(streamId, NetworkUtil.convertToNetworkFilename(self.configFileName)) |
646 | streamWriteString(streamId, self.typeName) |
647 | |
648 | local x,y,z = getTranslation(self.rootNode) |
649 | local x_rot,y_rot,z_rot = getRotation(self.rootNode) |
650 | streamWriteFloat32(streamId, x) |
651 | streamWriteFloat32(streamId, y) |
652 | streamWriteFloat32(streamId, z) |
653 | NetworkUtil.writeCompressedAngle(streamId, x_rot) |
654 | NetworkUtil.writeCompressedAngle(streamId, y_rot) |
655 | NetworkUtil.writeCompressedAngle(streamId, z_rot) |
656 | end |
735 | function Placeable:writeUpdateStream(streamId, connection, dirtyMask) |
736 | if Placeable.DEBUG_NETWORK_UPDATE then |
737 | print("-------------------------------------------------------------") |
738 | print(self.configFileName) |
739 | for _, spec in ipairs(self.eventListeners["onWriteUpdateStream"]) do |
740 | local className = ClassUtil.getClassName(spec) |
741 | local startBits = streamGetWriteOffset(streamId) |
742 | spec["onWriteUpdateStream"](self, streamId, connection, dirtyMask) |
743 | print(" "..tostring(className).." Wrote " .. streamGetWriteOffset(streamId)-startBits .. " bits") |
744 | end |
745 | else |
746 | SpecializationUtil.raiseEvent(self, "onWriteUpdateStream", streamId, connection, dirtyMask) |
747 | end |
748 | end |