Fixed some stuff, procrastinating the real problem
All checks were successful
Build Dusk / build-linux (push) Successful in 47s
Build Dusk / build-psp (push) Successful in 1m6s

This commit is contained in:
2025-11-16 16:18:01 -06:00
parent 9a59c22288
commit c874e6c197
14 changed files with 161 additions and 154 deletions

View File

@@ -5,16 +5,7 @@ import json
from assetstool.args import args
from assetstool.assetcache import assetCache, assetGetCache
from assetstool.assethelpers import getAssetRelativePath
from dusk.defs import defs
CHUNK_WIDTH = int(defs.get('CHUNK_WIDTH'))
CHUNK_HEIGHT = int(defs.get('CHUNK_HEIGHT'))
CHUNK_DEPTH = int(defs.get('CHUNK_DEPTH'))
CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH
TILE_WIDTH = float(defs.get('TILE_WIDTH'))
TILE_HEIGHT = float(defs.get('TILE_HEIGHT'))
TILE_DEPTH = float(defs.get('TILE_DEPTH'))
from editortool.map.mapdefs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_TILE_COUNT
def processTile(tileIndex, x=0, y=0, z=0, chunkX=0, chunkY=0, chunkZ=0):
vertices = []

View File

@@ -1,36 +0,0 @@
import json
class ChunkData:
def __init__(self):
self.current = {}
self.original = {}
self.filename = None
def load(self, filename):
try:
with open(filename, 'r') as f:
self.current = json.load(f)
self.filename = filename
self.original = json.loads(json.dumps(self.current)) # Deep copy
except Exception as e:
raise RuntimeError(f"Failed to load chunk file: {e}")
def save(self, filename=None):
fname = filename if filename else self.filename
if not fname:
raise ValueError("No filename specified for saving chunk.")
try:
with open(fname, 'w') as f:
json.dump(self.current, f, indent=2)
self.original = json.loads(json.dumps(self.current)) # Deep copy
self.filename = fname
except Exception as e:
raise RuntimeError(f"Failed to save chunk file: {e}")
def new(self):
self.current = {}
self.original = {}
self.filename = None
def isDirty(self):
return json.dumps(self.current, sort_keys=True) != json.dumps(self.original, sort_keys=True)

View File

@@ -2,14 +2,14 @@ import math
import time
from OpenGL.GL import *
from OpenGL.GLU import *
from dusk.defs import defs
from editortool.map.map import map
from editortool.map.mapdefs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH, RPG_CAMERA_PIXELS_PER_UNIT, RPG_CAMERA_Z_OFFSET, RPG_CAMERA_FOV
class Camera:
def __init__(self):
self.pixelsPerUnit = float(defs.get('RPG_CAMERA_PIXELS_PER_UNIT'))
self.yOffset = float(defs.get('RPG_CAMERA_Z_OFFSET'))
self.fov = float(defs.get('RPG_CAMERA_FOV'))
def __init__(self, parent):
self.parent = parent
self.pixelsPerUnit = RPG_CAMERA_PIXELS_PER_UNIT
self.yOffset = RPG_CAMERA_Z_OFFSET
self.fov = RPG_CAMERA_FOV
self.scale = 8.0
self.lastTime = time.time()
self.lookAtTarget = [0.0, 0.0, 0.0]
@@ -25,13 +25,12 @@ class Camera:
(self.pixelsPerUnit * self.scale) * math.tan(math.radians(self.fov / 2.0))
)
lookAt = [
map.position[0] * float(defs.get('TILE_WIDTH')),
map.position[1] * float(defs.get('TILE_HEIGHT')),
map.position[2] * float(defs.get('TILE_DEPTH')),
self.parent.parent.map.position[0] * TILE_WIDTH,
self.parent.parent.map.position[1] * TILE_HEIGHT,
self.parent.parent.map.position[2] * TILE_DEPTH,
]
aspectRatio = vw / vh
# Ease the lookAt target
for i in range(3):
self.lookAtTarget[i] += (lookAt[i] - self.lookAtTarget[i]) * ease
@@ -39,13 +38,14 @@ class Camera:
# Camera position is now based on the eased lookAtTarget
cameraPosition = (
self.lookAtTarget[0],
self.lookAtTarget[1] - self.yOffset,
self.lookAtTarget[1] + self.yOffset,
self.lookAtTarget[2] + z
)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(self.fov, aspectRatio, 0.1, 1000.0)
glScalef(1.0, -1.0, 1.0) # Flip the projection matrix upside down
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(

View File

@@ -1,13 +1,14 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QGridLayout, QTreeWidget, QTreeWidgetItem
from editortool.map.map import map
class ChunkPanel(QWidget):
def __init__(self, parent=None):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
layout = QVBoxLayout(self)
self.chunkInfoLabel = QLabel("Tile Information")
layout.addWidget(self.chunkInfoLabel)
# Nav buttons
grid = QGridLayout()
self.btnUp = QPushButton("U")
self.btnN = QPushButton("N")
@@ -16,7 +17,6 @@ class ChunkPanel(QWidget):
self.btnS = QPushButton("S")
self.btnE = QPushButton("E")
# Arrange buttons: U N D on top row, W S E on bottom row
grid.addWidget(self.btnUp, 0, 0)
grid.addWidget(self.btnN, 0, 1)
grid.addWidget(self.btnDown, 0, 2)
@@ -38,17 +38,10 @@ class ChunkPanel(QWidget):
self.tree.expandAll() # Expand by default, remove if you want collapsed
layout.addWidget(self.tree, 1) # Give tree stretch factor so it expands
self.btnN.clicked.connect(lambda: map.moveRelative(0, 1, 0))
self.btnS.clicked.connect(lambda: map.moveRelative(0, -1, 0))
self.btnE.clicked.connect(lambda: map.moveRelative(1, 0, 0))
self.btnW.clicked.connect(lambda: map.moveRelative(-1, 0, 0))
self.btnUp.clicked.connect(lambda: map.moveRelative(0, 0, 1))
self.btnDown.clicked.connect(lambda: map.moveRelative(0, 0, -1))
# Subscribe to parent's fileSaving event
if parent is not None and hasattr(parent, 'fileSaving'):
parent.fileSaving.subscribe(self.onFileSaving)
def onFileSaving(self, objOut):
# Inject 'name' into the object
objOut['name'] = 'ChunkPanel'
# Event subscriptions
self.btnN.clicked.connect(lambda: self.parent.map.moveRelative(0, -1, 0))
self.btnS.clicked.connect(lambda: self.parent.map.moveRelative(0, 1, 0))
self.btnE.clicked.connect(lambda: self.parent.map.moveRelative(1, 0, 0))
self.btnW.clicked.connect(lambda: self.parent.map.moveRelative(-1, 0, 0))
self.btnUp.clicked.connect(lambda: self.parent.map.moveRelative(0, 0, 1))
self.btnDown.clicked.connect(lambda: self.parent.map.moveRelative(0, 0, -1))

View File

@@ -2,18 +2,20 @@ from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QOpenGLWidget
from OpenGL.GL import *
from OpenGL.GLU import *
from editortool.map.selectbox import drawSelectBox
from editortool.map.selectbox import SelectBox
from editortool.map.camera import Camera
from editortool.map.grid import Grid
class GLWidget(QOpenGLWidget):
def __init__(self, parent=None):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.timer = QTimer(self)
self.timer.timeout.connect(self.update)
self.timer.start(16) # ~60 FPS
self.camera = Camera()
self.camera = Camera(self)
self.grid = Grid()
self.selectBox = SelectBox(self)
def initializeGL(self):
glClearColor(0.392, 0.584, 0.929, 1.0)
@@ -37,4 +39,4 @@ class GLWidget(QOpenGLWidget):
self.camera.setup(w, h)
self.grid.draw()
drawSelectBox()
self.selectBox.draw()

View File

@@ -1,10 +1,10 @@
from OpenGL.GL import *
from dusk.defs import defs
from editortool.map.mapdefs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
class Grid:
def __init__(self, lines=1000):
self.cellWidth = float(defs.get('TILE_WIDTH'))
self.cellHeight = float(defs.get('TILE_HEIGHT'))
self.cellWidth = TILE_WIDTH
self.cellHeight = TILE_HEIGHT
self.lines = lines
self.enabled = True

View File

@@ -2,10 +2,10 @@ from dusk.event import Event
from editortool.map.mapdata import MAP_WIDTH, MAP_HEIGHT, MAP_DEPTH
class Map:
def __init__(self):
def __init__(self, parent):
self.parent = parent
self.position = [0, 0, 0] # x, y, z
self.positionEvent = Event()
self.chunks = []
def moveTo(self, x, y, z):
self.position = [x, y, z]
@@ -17,6 +17,3 @@ class Map:
self.position[1] + y,
self.position[2] + z
)
# Singleton instance
map = Map()

View File

@@ -4,13 +4,11 @@ from PyQt5.QtWidgets import QFileDialog, QMessageBox
from PyQt5.QtCore import QTimer
import os
from editortool.map.chunkdata import ChunkData
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')
MAP_WIDTH = 5
MAP_HEIGHT = 5
MAP_DEPTH = 3
MAP_CHUNK_COUNT = MAP_WIDTH * MAP_HEIGHT * MAP_DEPTH
class MapData:
def __init__(self):
@@ -72,6 +70,7 @@ class MapData:
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):
@@ -85,6 +84,7 @@ class MapData:
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):

View File

@@ -0,0 +1,20 @@
from dusk.defs import defs
CHUNK_WIDTH = int(defs.get('CHUNK_WIDTH'))
CHUNK_HEIGHT = int(defs.get('CHUNK_HEIGHT'))
CHUNK_DEPTH = int(defs.get('CHUNK_DEPTH'))
CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH
TILE_WIDTH = float(defs.get('TILE_WIDTH'))
TILE_HEIGHT = float(defs.get('TILE_HEIGHT'))
TILE_DEPTH = float(defs.get('TILE_DEPTH'))
RPG_CAMERA_PIXELS_PER_UNIT = float(defs.get('RPG_CAMERA_PIXELS_PER_UNIT'))
RPG_CAMERA_Z_OFFSET = float(defs.get('RPG_CAMERA_Z_OFFSET'))
RPG_CAMERA_FOV = float(defs.get('RPG_CAMERA_FOV'))
MAP_WIDTH = 5
MAP_HEIGHT = 5
MAP_DEPTH = 3
MAP_CHUNK_COUNT = MAP_WIDTH * MAP_HEIGHT * MAP_DEPTH

View File

@@ -1,15 +1,9 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton, QHBoxLayout, QMessageBox
from editortool.map.map import map
from dusk.defs import defs
chunkWidth = int(defs.get('CHUNK_WIDTH'))
chunkHeight = int(defs.get('CHUNK_HEIGHT'))
chunkDepth = int(defs.get('CHUNK_DEPTH'))
from editortool.map.mapdefs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH
class MapInfoPanel(QWidget):
def __init__(self, parent=None):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
# Components
@@ -36,9 +30,9 @@ class MapInfoPanel(QWidget):
tilePositionRow.addWidget(self.tileGo)
layout.addLayout(tilePositionRow)
self.chunkPosLabel = QLabel("Chunk Position: 0, 0, 0")
self.chunkPosLabel = QLabel()
layout.addWidget(self.chunkPosLabel)
self.chunkLabel = QLabel("Chunk: 0, 0, 0")
self.chunkLabel = QLabel()
layout.addWidget(self.chunkLabel)
layout.addStretch()
@@ -49,13 +43,12 @@ class MapInfoPanel(QWidget):
self.tileXInput.returnPressed.connect(self.goToPosition)
self.tileYInput.returnPressed.connect(self.goToPosition)
self.tileZInput.returnPressed.connect(self.goToPosition)
map.positionEvent.sub(self.updatePositionLabels)
parent.mapData.onMapData.sub(self.onMapData)
self.parent.map.positionEvent.sub(self.updatePositionLabels)
self.parent.mapData.onMapData.sub(self.onMapData)
self.mapTitleInput.textChanged.connect(self.onMapNameChange)
# Initial label setting
self.updatePositionLabels(map.position)
self.updatePositionLabels(self.parent.map.position)
def goToPosition(self):
try:
@@ -70,11 +63,20 @@ class MapInfoPanel(QWidget):
self.tileXInput.setText(str(pos[0]))
self.tileYInput.setText(str(pos[1]))
self.tileZInput.setText(str(pos[2]))
self.chunkPosLabel.setText(f"Chunk Position: {pos[0] % chunkWidth}, {pos[1] % chunkHeight}, {pos[2] % chunkDepth}")
self.chunkLabel.setText(f"Chunk: {pos[0] // chunkWidth}, {pos[1] // chunkHeight}, {pos[2] // chunkDepth}")
chunkTileX = pos[0] % CHUNK_WIDTH
chunkTileY = pos[1] % CHUNK_HEIGHT
chunkTileZ = pos[2] % CHUNK_DEPTH
chunkTileIndex = chunkTileX + chunkTileY * CHUNK_WIDTH + chunkTileZ * CHUNK_WIDTH * CHUNK_HEIGHT
self.chunkPosLabel.setText(f"Chunk Position: {chunkTileX}, {chunkTileY}, {chunkTileZ} ({chunkTileIndex})")
chunkX = pos[0] // CHUNK_WIDTH
chunkY = pos[1] // CHUNK_HEIGHT
chunkZ = pos[2] // CHUNK_DEPTH
self.chunkLabel.setText(f"Chunk: {chunkX}, {chunkY}, {chunkZ}")
def onMapData(self, data):
self.updatePositionLabels(map.position)
self.updatePositionLabels(self.parent.map.position)
self.mapTitleInput.setText(data.get("mapName", ""))
def onMapNameChange(self, text):

View File

@@ -1,46 +1,44 @@
import OpenGL.GL as gl
from dusk.defs import defs
import colorsys
from editortool.map.map import map
from editortool.map.mapdefs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
hue = [0.0] # Mutable container for static hue
class SelectBox:
def __init__(self, parent):
self.parent = parent
self.hue = 0.0
def drawSelectBox():
w = float(defs.get('TILE_WIDTH'))
h = float(defs.get('TILE_HEIGHT'))
d = float(defs.get('TILE_DEPTH'))
position = [
map.position[0] * w - (w / 2.0),
map.position[1] * h - (h / 2.0),
map.position[2] * d - (d / 2.0)
]
def draw(self):
position = [
self.parent.parent.map.position[0] * TILE_WIDTH - (TILE_WIDTH / 2.0),
self.parent.parent.map.position[1] * TILE_HEIGHT - (TILE_HEIGHT / 2.0),
self.parent.parent.map.position[2] * TILE_DEPTH - (TILE_DEPTH / 2.0)
]
# Define the 8 vertices of the cube with w=h=d=1
vertices = [
(position[0], position[1], position[2]), # 0: min corner
(position[0]+w, position[1], position[2]), # 1
(position[0]+w, position[1]+h, position[2]), # 2
(position[0], position[1]+h, position[2]), # 3
(position[0], position[1], position[2]+d), # 4
(position[0]+w, position[1], position[2]+d), # 5
(position[0]+w, position[1]+h, position[2]+d), # 6
(position[0], position[1]+h, position[2]+d) # 7
]
# List of edges as pairs of vertex indices
edges = [
(0, 1), (1, 2), (2, 3), (3, 0), # bottom face
(4, 5), (5, 6), (6, 7), (7, 4), # top face
(4, 5), (5, 6), (6, 7), (7, 4), # top face
(0, 4), (1, 5), (2, 6), (3, 7) # vertical edges
]
vertices = [
(position[0], position[1], position[2]),
(position[0]+TILE_WIDTH, position[1], position[2]),
(position[0]+TILE_WIDTH, position[1]+TILE_HEIGHT, position[2]),
(position[0], position[1]+TILE_HEIGHT, position[2]),
(position[0], position[1], position[2]+TILE_DEPTH),
(position[0]+TILE_WIDTH, position[1], position[2]+TILE_DEPTH),
(position[0]+TILE_WIDTH, position[1]+TILE_HEIGHT, position[2]+TILE_DEPTH),
(position[0], position[1]+TILE_HEIGHT, position[2]+TILE_DEPTH)
]
edges = [
(0, 1), (1, 2), (2, 3), (3, 0), # bottom face
(4, 5), (5, 6), (6, 7), (7, 4), # top face
(4, 5), (5, 6), (6, 7), (7, 4), # top face
(0, 4), (1, 5), (2, 6), (3, 7) # vertical edges
]
# Cycle hue
hue[0] = (hue[0] + 0.01) % 1.0
r, g, b = colorsys.hsv_to_rgb(hue[0], 1.0, 1.0)
gl.glColor3f(r, g, b)
gl.glLineWidth(2.0)
gl.glBegin(gl.GL_LINES)
for edge in edges:
for vertex in edge:
# Cycle hue
self.hue = (self.hue + 0.01) % 1.0
r, g, b = colorsys.hsv_to_rgb(self.hue, 1.0, 1.0)
gl.glColor3f(r, g, b)
gl.glLineWidth(2.0)
gl.glBegin(gl.GL_LINES)
for edge in edges:
for vertex in edge:
gl.glVertex3f(*vertices[vertex])
gl.glEnd()
gl.glEnd()

View File

@@ -4,7 +4,7 @@ class StatusBar(QStatusBar):
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
self.leftLabel = QLabel("Ready")
self.leftLabel = QLabel("")
self.rightLabel = QLabel("")
self.addWidget(self.leftLabel, 1)
self.addPermanentWidget(self.rightLabel)

View File

@@ -1,14 +1,14 @@
from OpenGL.GL import *
from dusk.defs import defs
from editortool.map.mapdefs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
class Tile:
def __init__(self):
self.tile_id = 0
def draw(self, x, y, z):
w = float(defs.get('TILE_WIDTH'))
h = float(defs.get('TILE_HEIGHT'))
d = float(defs.get('TILE_DEPTH'))
w = TILE_WIDTH
h = TILE_HEIGHT
d = TILE_DEPTH
x = x * w
y = y * h

View File

@@ -1,12 +1,14 @@
import os
from PyQt5.QtWidgets import QMainWindow, QWidget, QHBoxLayout, QMessageBox
from PyQt5.QtCore import Qt
from editortool.map.glwidget import GLWidget
from editortool.map.chunkpanel import ChunkPanel
from editortool.map.mapinfopanel import MapInfoPanel
from editortool.map.mapdata import MapData
from editortool.map.toolbar import MapToolbar
from editortool.map.statusbar import StatusBar
from editortool.map.map import Map
from editortool.map.mapdefs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
class MapWindow(QMainWindow):
def __init__(self):
@@ -14,6 +16,7 @@ class MapWindow(QMainWindow):
# Subclasses
self.mapData = MapData()
self.map = Map(self)
# Window setup
self.setWindowTitle("Dusk Map Editor")
@@ -28,7 +31,7 @@ class MapWindow(QMainWindow):
mainLayout = QHBoxLayout(central)
# Left panel (ChunkPanel)
self.chunkPanel = ChunkPanel()
self.chunkPanel = ChunkPanel(self)
leftWidget = QWidget()
leftWidget.setLayout(self.chunkPanel.layout())
leftWidget.setFixedWidth(200)
@@ -48,6 +51,14 @@ class MapWindow(QMainWindow):
self.statusBar = StatusBar(self)
self.setStatusBar(self.statusBar)
self.installEventFilter(self)
self._install_event_filter_recursive(self)
def _install_event_filter_recursive(self, widget):
for child in widget.findChildren(QWidget):
child.installEventFilter(self)
self._install_event_filter_recursive(child)
def generateData(self):
objOut = {}
self.fileSaving.invoke(objOut)
@@ -75,3 +86,32 @@ class MapWindow(QMainWindow):
event.ignore()
return
event.accept()
def eventFilter(self, obj, event):
if event.type() == event.KeyPress:
amtX, amtY, amtZ = 0, 0, 0
key = event.key()
if key == Qt.Key_Left:
amtX = -1
elif key == Qt.Key_Right:
amtX = 1
elif key == Qt.Key_Up:
amtY = -1
elif key == Qt.Key_Down:
amtY = 1
elif key == Qt.Key_PageUp:
amtZ = 1
elif key == Qt.Key_PageDown:
amtZ = -1
if event.modifiers() & Qt.ShiftModifier:
amtX *= int(TILE_WIDTH)
amtY *= int(TILE_HEIGHT)
amtZ *= int(TILE_DEPTH)
if amtX != 0 or amtY != 0 or amtZ != 0:
self.map.moveRelative(amtX, amtY, amtZ)
event.accept()
return True
return super().eventFilter(obj, event)