import json from dusk.event import Event from PyQt5.QtWidgets import QFileDialog, QMessageBox from PyQt5.QtCore import QTimer import os from editortool.map.chunk import Chunk from editortool.map.mapdefs import MAP_WIDTH, MAP_HEIGHT, MAP_DEPTH import traceback MAP_DEFAULT_PATH = os.path.join(os.path.dirname(__file__), '../../../assets/map/') EDITOR_CONFIG_PATH = os.path.join(os.path.dirname(__file__), '.editor') class Map: def __init__(self, parent): self.parent = parent self.data = {} self.dataOriginal = {} self.position = [0, 0, 0] # x, y, z self.chunks = {} self.onMapData = Event() self.onPositionChange = Event() self.mapFileName = None self.lastFile = None index = 0 for x in range(MAP_WIDTH): for y in range(MAP_HEIGHT): for z in range(MAP_DEPTH): self.chunks[index] = Chunk(self, x, y, z) index += 1 QTimer.singleShot(16, self.loadLastFile) def loadLastFile(self): if not os.path.exists(EDITOR_CONFIG_PATH): return try: with open(EDITOR_CONFIG_PATH, 'r') as f: config = json.load(f) lastFile = config.get('lastFile') lastPosition = config.get('lastPosition') if lastFile and os.path.exists(lastFile): self.load(lastFile) if lastPosition and isinstance(lastPosition, list) and len(lastPosition) == 3: self.moveTo(*lastPosition) except Exception: traceback.print_exc() def updateEditorConfig(self): print("Updating editor config...") try: mapFileName = self.getMapFilename() config = { 'lastFile': mapFileName if mapFileName else "", 'lastPosition': self.position } config_dir = os.path.dirname(EDITOR_CONFIG_PATH) if not os.path.exists(config_dir): os.makedirs(config_dir, exist_ok=True) with open(EDITOR_CONFIG_PATH, 'w') as f: json.dump(config, f, indent=2) except Exception: traceback.print_exc() def newFile(self): self.data = {} self.dataOriginal = {} self.mapFileName = None self.lastFile = None for chunk in self.chunks.values(): chunk.new() self.moveTo(0, 0, 0) self.onMapData.invoke(self.data) self.updateEditorConfig() def save(self, fname=None): if not self.getMapFilename() and fname is None: filePath, _ = QFileDialog.getSaveFileName(None, "Save Map File", MAP_DEFAULT_PATH, "Map Files (*.json)") if not filePath: return self.mapFileName = filePath if fname: self.mapFileName = fname try: with open(self.getMapFilename(), 'w') as f: json.dump(self.data, f, indent=2) self.dataOriginal = json.loads(json.dumps(self.data)) # Deep copy for chunk in self.chunks.values(): chunk.save() self.updateEditorConfig() except Exception as e: traceback.print_exc() QMessageBox.critical(None, "Save Error", f"Failed to save map file:\n{e}") def load(self, fileName): try: with open(fileName, 'r') as f: self.data = json.load(f) self.mapFileName = fileName self.dataOriginal = json.loads(json.dumps(self.data)) # Deep copy for chunk in self.chunks.values(): chunk.load() self.onMapData.invoke(self.data) self.updateEditorConfig() except Exception as e: traceback.print_exc() QMessageBox.critical(None, "Load Error", f"Failed to load map file:\n{e}") def isMapFileDirty(self): return json.dumps(self.data, sort_keys=True) != json.dumps(self.dataOriginal, sort_keys=True) def isDirty(self): return self.isMapFileDirty() or self.anyChunksDirty() def getMapFilename(self): return self.mapFileName if self.mapFileName and os.path.exists(self.mapFileName) else None def getMapDirectory(self): fname = self.getMapFilename() if not fname or not fname.endswith('.json'): return None return fname[:-5] # Remove '.json' extension def anyChunksDirty(self): for chunk in self.chunks.values(): if chunk.isDirty(): return True return False def moveTo(self, x, y, z): self.position = [x, y, z] self.onPositionChange.invoke(self.position) self.updateEditorConfig() def moveRelative(self, x, y, z): self.moveTo( self.position[0] + x, self.position[1] + y, self.position[2] + z )