/** GDevelop - Tile Map Extension Copyright (c) 2014-2016 Victor Levasseur (victorlevasseur52@gmail.com) This project is released under the MIT License. */ #if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI) #include "TileMapPanel.h" #include #include #include #include "GDCore/IDE/wxTools/CommonBitmapProvider.h" #include "TileMapObjectEditorCommands.h" TileMapPanel::TileMapPanel(wxWindow* parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxScrolledWindow(parent, id, pos, size, style), m_tileToBeInserted(0), m_hideUpperLayers(false), m_tileset(NULL), m_tilemap(NULL), m_mapCurrentLayer(0), m_insertionMode(PencilMode), m_isDrawingRectangle(false), m_commandProcessor() { SetBackgroundStyle(wxBG_STYLE_PAINT); Connect(wxEVT_PAINT, wxPaintEventHandler(TileMapPanel::OnPaint), NULL, this); Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(TileMapPanel::OnMouseEvent), NULL, this); Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(TileMapPanel::OnMouseEvent), NULL, this); Connect(wxEVT_LEFT_UP, wxMouseEventHandler(TileMapPanel::OnMouseEvent), NULL, this); Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(TileMapPanel::OnMouseEvent), NULL, this); Connect(wxEVT_MOTION, wxMouseEventHandler(TileMapPanel::OnMouseEvent), NULL, this); } TileMapPanel::~TileMapPanel() { } void TileMapPanel::SetTileMap(TileMap *tilemap) { m_commandProcessor.ClearCommands(); m_tilemap = tilemap; Update(); } void TileMapPanel::SetTileSet(TileSet *tileset) { m_tileset = tileset; } void TileMapPanel::HideUpperLayers(bool enable) { m_hideUpperLayers = enable; Refresh(); } bool TileMapPanel::AreUpperLayersHidden() const { return m_hideUpperLayers; } void TileMapPanel::SetCurrentLayer(int currentLayer) { m_mapCurrentLayer = currentLayer; Refresh(); } void TileMapPanel::FillLayer(int layer, int tile) { if(!m_tilemap) return; for (unsigned int col = 0; col < m_tilemap->GetColumnsCount(); col++) { for (unsigned int row = 0; row < m_tilemap->GetRowsCount(); row++) { m_tilemap->SetTile(layer, col, row, tile); } } Refresh(); } void TileMapPanel::Update() { if (!m_tilemap || !m_tileset) return; SetScrollRate(1, 1); SetVirtualSize(m_tilemap->GetColumnsCount() * m_tileset->tileSize.x, m_tilemap->GetRowsCount() * m_tileset->tileSize.y); Refresh(); } void TileMapPanel::SetInsertionMode(InsertionMode newInsertionMode) { m_insertionMode = newInsertionMode; Update(); } void TileMapPanel::OnTileSetSelectionChanged(TileSelectionEvent &event) { m_tileToBeInserted = event.GetSelectedTile(); } void TileMapPanel::OnPaint(wxPaintEvent& event) { wxBitmap btmp(m_tileset->GetWxBitmap()); wxMemoryDC tilesetDC; tilesetDC.SelectObject(btmp); wxAutoBufferedPaintDC dc(this); DoPrepareDC(dc); wxPoint minPos = GetViewStart(); int width, height; GetClientSize(&width, &height); wxPoint maxPos = minPos + wxPoint(width, height); dc.SetBrush(gd::CommonBitmapProvider::Get()->transparentBg); dc.DrawRectangle(minPos.x, minPos.y, width, height); if(!m_tilemap || !m_tileset || m_tileset->IsDirty()) return; dc.SetPen(wxPen(wxColor(128, 128, 128, 255), 1)); //Determine the first and last columns and rows to draw int firstCol = std::max((int)(minPos.x / m_tileset->tileSize.x - 1), 0); int firstRow = std::max((int)(minPos.y / m_tileset->tileSize.y - 1), 0); int lastCol = std::min((int)(maxPos.x / m_tileset->tileSize.x + 1), m_tilemap->GetColumnsCount()); int lastRow = std::min((int)(maxPos.y / m_tileset->tileSize.y + 1), m_tilemap->GetRowsCount()); //Draw the tiles for(int layer = 0; m_hideUpperLayers ? layer <= GetCurrentLayer() : layer < 3; layer++) { for(int col = firstCol; col < lastCol; col++) { for(int row = firstRow; row < lastRow; row++) { if(m_tilemap->GetTile(layer, col, row) == -1) continue; dc.Blit(GetPositionOfTile(col, row).x, GetPositionOfTile(col, row).y, m_tileset->tileSize.x, m_tileset->tileSize.y, &tilesetDC, m_tileset->GetTileTextureCoords(m_tilemap->GetTile(layer, col, row)).topLeft.x, m_tileset->GetTileTextureCoords(m_tilemap->GetTile(layer, col, row)).topLeft.y, wxCOPY, true); } } } //Draw the rectangle (when the mode is RectangleMode) if(m_insertionMode == RectangleMode && m_isDrawingRectangle) { dc.SetBrush(wxBrush(wxColour(128, 128, 255, 128))); dc.SetPen(wxPen(wxColor(128, 128, 255, 255), 1)); wxPoint topLeftPos(GetPositionOfTile(std::min(m_beginCol, m_endCol), std::min(m_beginRow, m_endRow))); wxPoint bottomRightPos(GetPositionOfTile(std::max(m_beginCol + 1, m_endCol + 1), std::max(m_beginRow + 1, m_endRow + 1))); wxSize rectSize(bottomRightPos.x - topLeftPos.x, bottomRightPos.y - topLeftPos.y); dc.DrawRectangle(topLeftPos, rectSize); } dc.SetPen(wxPen(wxColor(128, 128, 128, 255), 1)); //Draw the grid for(int col = firstCol; col < lastCol; col++) { dc.DrawLine(col * m_tileset->tileSize.x, minPos.y, col * m_tileset->tileSize.x, maxPos.y); for(int row = firstRow; row < lastRow; row++) { dc.DrawLine(minPos.x, row * m_tileset->tileSize.y, maxPos.x, row * m_tileset->tileSize.y); } } //Draw a gray rectangle outside the map dc.SetBrush(wxColor(128, 128, 128, 255)); if(maxPos.x > (m_tilemap->GetColumnsCount() * m_tileset->tileSize.x)) { dc.DrawRectangle(m_tilemap->GetColumnsCount() * m_tileset->tileSize.x, minPos.y, maxPos.x - m_tilemap->GetColumnsCount() * m_tileset->tileSize.x, maxPos.y - minPos.y); } if(maxPos.y > (m_tilemap->GetRowsCount() * m_tileset->tileSize.y)) { dc.DrawRectangle(minPos.x, m_tilemap->GetRowsCount() * m_tileset->tileSize.y, maxPos.x - minPos.x, maxPos.y - m_tilemap->GetRowsCount() * m_tileset->tileSize.y); } } void TileMapPanel::OnMouseEvent(wxMouseEvent &event) { if(!m_tilemap || !m_tileset) return; //Get the current tile position (column and row) unsigned int currentColumn, currentRow; wxPoint mousePos = CalcUnscrolledPosition(event.GetPosition()); GetTileAt(mousePos, currentColumn, currentRow); if(currentColumn >= m_tilemap->GetColumnsCount() || currentRow >= m_tilemap->GetRowsCount()) return; //Stop if the position is out of range if(m_insertionMode == PencilMode) { if(event.GetEventType() == wxEVT_LEFT_DOWN || event.GetEventType() == wxEVT_RIGHT_DOWN || event.GetEventType() == wxEVT_MOTION) { if(event.LeftIsDown()) //Left mouse button pressed { //Add a tile to the current position (only if the tile has not been set before) if(m_tilemap->GetTile(m_mapCurrentLayer, currentColumn, currentRow) != m_tileToBeInserted) m_commandProcessor.Submit(new ChangeTileCommand(*m_tilemap, m_mapCurrentLayer, currentColumn, currentRow, m_tileToBeInserted)); Refresh(); } else if(event.RightIsDown()) { //Remove the tile m_commandProcessor.Submit(new ChangeTileCommand(*m_tilemap, m_mapCurrentLayer, currentColumn, currentRow, -1)); Refresh(); } } } else if(m_insertionMode == RectangleMode) { if(event.GetEventType() == wxEVT_LEFT_DOWN || event.GetEventType() == wxEVT_RIGHT_DOWN) { m_isDrawingRectangle = true; m_beginCol = m_endCol = currentColumn; m_beginRow = m_endRow = currentRow; Update(); } else if(event.GetEventType() == wxEVT_MOTION) { m_endCol = currentColumn; m_endRow = currentRow; Update(); } else if(event.GetEventType() == wxEVT_LEFT_UP) { m_endCol = currentColumn; m_endRow = currentRow; m_isDrawingRectangle = false; m_commandProcessor.Submit(new ChangeTileCommand(*m_tilemap, m_mapCurrentLayer, std::min(m_beginCol, m_endCol), std::min(m_beginRow, m_endRow), std::max(m_beginCol, m_endCol), std::max(m_beginRow, m_endRow), m_tileToBeInserted)); Update(); } else if(event.GetEventType() == wxEVT_RIGHT_UP) { m_endCol = currentColumn; m_endRow = currentRow; m_isDrawingRectangle = false; m_commandProcessor.Submit(new ChangeTileCommand(*m_tilemap, m_mapCurrentLayer, std::min(m_beginCol, m_endCol), std::min(m_beginRow, m_endRow), std::max(m_beginCol, m_endCol), std::max(m_beginRow, m_endRow), -1)); Update(); } } else if(m_insertionMode == FillMode) { if(event.GetEventType() == wxEVT_LEFT_DOWN) { m_commandProcessor.Submit(new FloodFillCommand(*m_tilemap, m_mapCurrentLayer, currentColumn, currentRow, m_tileToBeInserted)); Update(); } } } wxPoint TileMapPanel::GetPositionOfTile(int column, int row) { return wxPoint(column *(m_tileset->tileSize.x), row * (m_tileset->tileSize.y)); } void TileMapPanel::GetTileAt(wxPoint position, unsigned int &tileCol, unsigned int &tileRow) { tileCol = (int)(position.x / m_tileset->tileSize.x); tileRow = (int)(position.y / m_tileset->tileSize.y); } #endif