diff --git a/README.md b/README.md index e764dd2..441c37e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Download from releases: Output: `build/distributions/KorgePlugin.zip` -### Testing +### Executing from Source ``` ./gradlew runIde @@ -26,4 +26,10 @@ Output: `build/distributions/KorgePlugin.zip` This should launch a new intelliJ Community Edition with the KorGE plugin. You can open this project with IDEA and launch the `runIde` task via debugger -to be able to debug the plugin. \ No newline at end of file +to be able to debug the plugin. + +### Trying the Tiled Editor + +``` +./gradlew runDebugTilemap +``` diff --git a/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/MyTileMapEditorFrame.kt b/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/MyTileMapEditorFrame.kt index c9693ae..e028d54 100644 --- a/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/MyTileMapEditorFrame.kt +++ b/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/MyTileMapEditorFrame.kt @@ -1,14 +1,15 @@ package com.soywiz.korge.intellij.editor.tile +import com.intellij.ui.components.* import com.intellij.uiDesigner.core.* import com.soywiz.korge.tiled.* import com.soywiz.korim.awt.* import com.soywiz.korim.bitmap.* +import com.soywiz.korim.color.* import com.soywiz.korio.file.std.* -import com.soywiz.korma.geom.* import kotlinx.coroutines.* import java.awt.* -import java.awt.image.* +import java.awt.event.* import javax.swing.* class MyTileMapEditorPanel(val tmx: TiledMap) : JPanel(BorderLayout()) { @@ -16,105 +17,153 @@ class MyTileMapEditorPanel(val tmx: TiledMap) : JPanel(BorderLayout()) { val maxTileGid = tmx.tilesets.map { it.firstgid + it.tileset.textures.size }.max() ?: 0 - data class TileInfo(val image: Bitmap32, val awt: BufferedImage, val area: RectangleInt) { - val miniImage = awt.getSubimage(area.x, area.y, area.width, area.height) - val miniBmp32 = image.copySliceWithSize(area.x, area.y, area.width, area.height) - val miniSlice = miniBmp32.sliceWithSize(0, 0, area.width, area.height) + data class TileInfo(val bmp32: Bitmap32) { + val miniSlice = bmp32.slice() } - private val emptyImage = Bitmaps.transparent.bmp - private val emptyImageAwt = emptyImage.toAwt() - val dummyTile = TileInfo(emptyImage, emptyImageAwt, Bitmaps.transparent.bounds) - val tiles: Array = Array(maxTileGid) { dummyTile }.also { tiles -> + val tiles = Array(maxTileGid) { null }.also { tiles -> for (tileset in tmx.tilesets) { - val tex = tileset.tileset.base.toBMP32() - val tex2 = tex.toAwt() for (tileIdx in tileset.tileset.textures.indices) { val tile = tileset.tileset.textures[tileIdx] if (tile != null) { - tiles[tileset.firstgid + tileIdx] = TileInfo(tex, tex2, (tile as BitmapSlice<*>).bounds) + val miniSlice = (tile as BitmapSlice<*>).extract().toBMP32() + tiles[tileset.firstgid + tileIdx] = when { + miniSlice.allFixed { it.a == 0 } -> null // Transparent + else -> TileInfo(miniSlice) + } } } } } val realPanel = tileMapEditor.contentPanel + var scale = 2.0 + set(value) { + field = value + mapComponent.updateSize() + mapComponent.revalidate() + mapComponentScroll.revalidate() + mapComponentScroll.repaint() + } - init { - add(realPanel, BorderLayout.CENTER) + inner class MapComponent : JComponent() { + init { + updateSize() + addMouseMotionListener(object : MouseMotionAdapter() { + override fun mouseDragged(e: MouseEvent) { + println("mouseDragged: $e") + onPressMouse(e.point) + } + }) + addMouseListener(object : MouseAdapter() { + override fun mousePressed(e: MouseEvent) { + println("mousePressed: $e") + if (e.button == MouseEvent.BUTTON1) { + onPressMouse(e.point) + } else { + onRightPressMouse(e.point) + } + } + }) + } - tileMapEditor.mapPanel.add(JScrollPane(object : JComponent() { - init { - this.preferredSize = Dimension(tmx.pixelWidth, tmx.pixelHeight) - } + var currentTileSelected = 1 - override fun paintComponent(g: Graphics) { - val g = (g as Graphics2D) + fun onPressMouse(point: Point) { + val tileIndex = getTileIndex(point) + tmx.patternLayers[0].map[tileIndex.x, tileIndex.y] = RGBA(currentTileSelected) + repaint() + //println(tileIndex) + } - val TILE_WIDTH = tmx.tilewidth - val TILE_HEIGHT = tmx.tileheight + fun onRightPressMouse(point: Point) { + val tileIndex = getTileIndex(point) + currentTileSelected = tmx.patternLayers[0].map[tileIndex.x, tileIndex.y].value + } - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - val clipBounds = g.clipBounds - val displayTilesX = (clipBounds.width / TILE_WIDTH) + 2 - val displayTilesY = (clipBounds.height / TILE_HEIGHT) + 2 - val temp = Bitmap32(displayTilesX * TILE_WIDTH, displayTilesY * TILE_HEIGHT) + fun updateSize() { + this.preferredSize = Dimension((tmx.pixelWidth * scale).toInt(), (tmx.pixelHeight * scale).toInt()) + } - val offsetX = clipBounds.x / TILE_WIDTH - val offsetY = clipBounds.y / TILE_HEIGHT + fun getTileIndex(coords: Point): Point = Point((coords.x / tmx.tilewidth / scale).toInt(), (coords.y / tmx.tileheight / scale).toInt()) - for (layer in tmx.allLayers) { - when (layer) { - is TiledMap.Layer.Patterns -> { - for (x in 0 until displayTilesX) { - for (y in 0 until displayTilesY) { - val rx = x + offsetX - val ry = y + offsetY + override fun paintComponent(g: Graphics) { + val g = (g as Graphics2D) - if (rx < 0 || rx >= layer.map.width) continue - if (ry < 0 || ry >= layer.map.height) continue + val TILE_WIDTH = tmx.tilewidth + val TILE_HEIGHT = tmx.tileheight - val tileIdx = layer.map[rx, ry].value - val tile = tiles.getOrElse(tileIdx) { dummyTile } - val px0 = x * TILE_WIDTH - val py0 = y * TILE_HEIGHT - val px = rx * TILE_WIDTH - val py = ry * TILE_HEIGHT + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) + val clipBounds = g.clipBounds + val displayTilesX = ((clipBounds.width / TILE_WIDTH / scale) + 3).toInt() + val displayTilesY = ((clipBounds.height / TILE_HEIGHT / scale) + 3).toInt() + val temp = Bitmap32((displayTilesX * TILE_WIDTH), (displayTilesY * TILE_HEIGHT)) - temp._draw(tile.miniSlice, px0, py0, mix = true) - //temp.draw(tile.miniBmp32, px0, py0) - //g.drawImage(tile.miniImage, px, py, null) - /* - g.drawImage( - tile.image, - px, py, px + TILE_WIDTH, py + TILE_HEIGHT, - tile.area.left, tile.area.top, tile.area.right, tile.area.bottom, - null - ) - */ - //g.color = Color.RED - //g.drawRect(px, py, TILE_WIDTH, TILE_HEIGHT) + val offsetX = (clipBounds.x / TILE_WIDTH / scale).toInt() + val offsetY = (clipBounds.y / TILE_HEIGHT / scale).toInt() + + for (layer in tmx.allLayers) { + when (layer) { + is TiledMap.Layer.Patterns -> { + for (x in 0 until displayTilesX) { + for (y in 0 until displayTilesY) { + val rx = x + offsetX + val ry = y + offsetY + + if (rx < 0 || rx >= layer.map.width) continue + if (ry < 0 || ry >= layer.map.height) continue + + val tileIdx = layer.map[rx, ry].value + val tile = tiles.getOrNull(tileIdx) + if (tile != null) { + temp._draw(tile.miniSlice, x * TILE_WIDTH, y * TILE_HEIGHT, mix = true) } } } } } - - g.drawImage(temp.toAwt(), offsetX * TILE_WIDTH, offsetY * TILE_HEIGHT, null) - - for (x in 0 until displayTilesX) { - for (y in 0 until displayTilesY) { - val rx = x + offsetX - val ry = y + offsetY - val px = rx * TILE_WIDTH - val py = ry * TILE_HEIGHT - g.color = Color.BLACK - g.drawRect(px, py, TILE_WIDTH, TILE_HEIGHT) - } - } } - }), GridConstraints().also { it.fill = GridConstraints.FILL_BOTH }) + //val oldTransform = g.transform + g.translate(offsetX * TILE_WIDTH * scale, offsetY * TILE_HEIGHT * scale) + g.scale(scale, scale) + g.drawImage(temp.toAwt(), 0, 0, null) + + //g.transform = oldTransform + + g.stroke = BasicStroke((1f / scale).toFloat()) + g.color = Color.BLACK + //g.translate(offsetX * TILE_WIDTH * scale, offsetY * TILE_HEIGHT * scale) + for (y in 0 until displayTilesY) g.drawLine(0, y * TILE_HEIGHT, displayTilesX * TILE_WIDTH, y * TILE_HEIGHT) + for (x in 0 until displayTilesX) g.drawLine(x * TILE_WIDTH, 0, x * TILE_WIDTH, displayTilesY * TILE_HEIGHT) + } + } + + val mapComponent = MapComponent() + val mapComponentScroll = JBScrollPane(mapComponent).also { scroll -> + //scroll.verticalScrollBar.unitIncrement = 16 + } + + fun updatedSize() { + tileMapEditor.leftSplitPane.dividerLocation = 200 + tileMapEditor.rightSplitPane.dividerLocation = tileMapEditor.rightSplitPane.width - 200 + } + + init { + + add(realPanel, BorderLayout.CENTER) + + tileMapEditor.mapPanel.add(mapComponentScroll, GridConstraints().also { it.fill = GridConstraints.FILL_BOTH }) + + tileMapEditor.zoomInButton.addActionListener { scale *= 1.5 } + tileMapEditor.zoomOutButton.addActionListener { scale /= 1.5 } + + updatedSize() + addComponentListener(object : ComponentAdapter() { + override fun componentResized(e: ComponentEvent) { + updatedSize() + } + }) } } @@ -135,3 +184,6 @@ class MyTileMapEditorFrame(val tmx: TiledMap) : JFrame() { } } } + +inline fun Bitmap32.anyFixed(callback: (RGBA) -> Boolean): Boolean = (0 until area).any { callback(data[it]) } +inline fun Bitmap32.allFixed(callback: (RGBA) -> Boolean): Boolean = (0 until area).all { callback(data[it]) } diff --git a/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/TileMapEditor.form b/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/TileMapEditor.form index 4ddcd22..00ce940 100644 --- a/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/TileMapEditor.form +++ b/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/TileMapEditor.form @@ -53,6 +53,18 @@ + + + + + + + + + + + + @@ -60,7 +72,7 @@ - + @@ -121,7 +133,7 @@ - + diff --git a/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/TileMapEditor.java b/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/TileMapEditor.java index e1daaed..1679ce5 100644 --- a/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/TileMapEditor.java +++ b/src/main/kotlin/com/soywiz/korge/intellij/editor/tile/TileMapEditor.java @@ -18,4 +18,8 @@ public class TileMapEditor { public JPanel mapPanel; public JTabbedPane tabbedPane4; public JTabbedPane tabbedPane5; + public JButton zoomInButton; + public JButton zoomOutButton; + public JSplitPane rightSplitPane; + public JSplitPane leftSplitPane; }