////////////////////////////////////////////////////////////////////// // This file is part of Remere's Map Editor ////////////////////////////////////////////////////////////////////// // Remere's Map Editor is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Remere's Map Editor is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ////////////////////////////////////////////////////////////////////// #include "main.h" #ifdef __APPLE__ #include #else #include #endif #include "editor.h" #include "gui.h" #include "sprites.h" #include "map_drawer.h" #include "map_display.h" #include "copybuffer.h" #include "live_socket.h" #include "graphics.h" #include "doodad_brush.h" #include "creature_brush.h" #include "house_exit_brush.h" #include "house_brush.h" #include "spawn_brush.h" #include "wall_brush.h" #include "carpet_brush.h" #include "raw_brush.h" #include "table_brush.h" #include "waypoint_brush.h" #include "light_drawer.h" DrawingOptions::DrawingOptions() { SetDefault(); } void DrawingOptions::SetDefault() { transparent_floors = false; transparent_items = false; show_ingame_box = false; show_lights = false; ingame = false; dragging = false; show_grid = 0; show_all_floors = true; show_creatures = true; show_spawns = true; show_houses = true; show_shade = true; show_special_tiles = true; show_items = true; highlight_items = false; show_blocking = false; show_tooltips = false; show_as_minimap = false; show_only_colors = false; show_only_modified = false; show_preview = false; show_hooks = false; show_pickupables = false; show_moveables = false; hide_items_when_zoomed = true; } void DrawingOptions::SetIngame() { transparent_floors = false; transparent_items = false; show_ingame_box = false; show_lights = false; ingame = true; dragging = false; show_grid = 0; show_all_floors = true; show_creatures = true; show_spawns = false; show_houses = false; show_shade = false; show_special_tiles = false; show_items = true; highlight_items = false; show_blocking = false; show_tooltips = false; show_as_minimap = false; show_only_colors = false; show_only_modified = false; show_preview = false; show_hooks = false; show_pickupables = false; show_moveables = false; hide_items_when_zoomed = false; } bool DrawingOptions::isOnlyColors() const noexcept { return show_as_minimap || show_only_colors; } bool DrawingOptions::isTileIndicators() const noexcept { if(isOnlyColors()) return false; return show_pickupables || show_moveables || show_houses || show_spawns; } bool DrawingOptions::isTooltips() const noexcept { return show_tooltips && !isOnlyColors(); } bool DrawingOptions::isDrawLight() const noexcept { return show_ingame_box && show_lights; } MapDrawer::MapDrawer(MapCanvas* canvas) : canvas(canvas), editor(canvas->editor) { light_drawer = std::make_shared(); } MapDrawer::~MapDrawer() { Release(); } void MapDrawer::SetupVars() { canvas->MouseToMap(&mouse_map_x, &mouse_map_y); canvas->GetViewBox(&view_scroll_x, &view_scroll_y, &screensize_x, &screensize_y); dragging = canvas->dragging; dragging_draw = canvas->dragging_draw; zoom = static_cast(canvas->GetZoom()); tile_size = int(rme::TileSize / zoom); // after zoom floor = canvas->GetFloor(); if(options.show_all_floors) { if(floor < 8) start_z = rme::MapGroundLayer; else start_z = std::min(rme::MapMaxLayer, floor + 2); } else start_z = floor; end_z = floor; superend_z = (floor > rme::MapGroundLayer ? 8 : 0); start_x = view_scroll_x / rme::TileSize; start_y = view_scroll_y / rme::TileSize; if(floor > rme::MapGroundLayer) { start_x -= 2; start_y -= 2; } end_x = start_x + screensize_x / tile_size + 2; end_y = start_y + screensize_y / tile_size + 2; } void MapDrawer::SetupGL() { glViewport(0, 0, screensize_x, screensize_y); // Enable 2D mode int vPort[4]; glGetIntegerv(GL_VIEWPORT, vPort); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, vPort[2]*zoom, vPort[3]*zoom, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTranslatef(0.375f, 0.375f, 0.0f); } void MapDrawer::Release() { for(auto it = tooltips.begin(); it != tooltips.end(); ++it) { delete *it; } tooltips.clear(); if(light_drawer) { light_drawer->clear(); } // Disable 2D mode glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } void MapDrawer::Draw() { DrawBackground(); DrawMap(); DrawDraggingShadow(); DrawHigherFloors(); if(options.dragging) DrawSelectionBox(); DrawLiveCursors(); DrawBrush(); if(options.show_grid && zoom <= 10.f) DrawGrid(); if(options.show_ingame_box) DrawIngameBox(); if(options.isTooltips()) DrawTooltips(); } void MapDrawer::DrawBackground() { // Black Background glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); //glAlphaFunc(GL_GEQUAL, 0.9f); //glEnable(GL_ALPHA_TEST); } inline int getFloorAdjustment(int floor) { if(floor > rme::MapGroundLayer) // Underground return 0; // No adjustment else return rme::TileSize * (rme::MapGroundLayer - floor); } void MapDrawer::DrawShade(int map_z) { if(map_z == end_z && start_z != end_z) { bool only_colors = options.isOnlyColors(); if(!only_colors) glDisable(GL_TEXTURE_2D); float x = screensize_x * zoom; float y = screensize_y * zoom; glColor4ub(0, 0, 0, 128); glBegin(GL_QUADS); glVertex2f(0, y); glVertex2f(x, y); glVertex2f(x,0); glVertex2f(0,0); glEnd(); if(!only_colors) glEnable(GL_TEXTURE_2D); } } void MapDrawer::DrawMap() { int center_x = start_x + int(screensize_x * zoom / 64); int center_y = start_y + int(screensize_y * zoom / 64); int offset_y = 2; int box_start_map_x = center_x - view_scroll_x; int box_start_map_y = center_y - view_scroll_x + offset_y; int box_end_map_x = center_x + rme::ClientMapWidth; int box_end_map_y = center_y + rme::ClientMapHeight + offset_y; bool live_client = editor.IsLiveClient(); Brush* brush = g_gui.GetCurrentBrush(); // The current house we're drawing current_house_id = 0; if(brush) { if(brush->isHouse()) current_house_id = brush->asHouse()->getHouseID(); else if(brush->isHouseExit()) current_house_id = brush->asHouseExit()->getHouseID(); } bool only_colors = options.isOnlyColors(); bool tile_indicators = options.isTileIndicators(); for(int map_z = start_z; map_z >= superend_z; map_z--) { if(options.show_shade) { DrawShade(map_z); } if(map_z >= end_z) { if(!only_colors) glEnable(GL_TEXTURE_2D); int nd_start_x = start_x & ~3; int nd_start_y = start_y & ~3; int nd_end_x = (end_x & ~3) + 4; int nd_end_y = (end_y & ~3) + 4; for(int nd_map_x = nd_start_x; nd_map_x <= nd_end_x; nd_map_x += 4) { for(int nd_map_y = nd_start_y; nd_map_y <= nd_end_y; nd_map_y += 4) { QTreeNode* nd = editor.getMap().getLeaf(nd_map_x, nd_map_y); if(!nd) { if(!live_client) continue; nd = editor.getMap().createLeaf(nd_map_x, nd_map_y); nd->setVisible(false, false); } if(!live_client || nd->isVisible(map_z > rme::MapGroundLayer)) { for(int map_x = 0; map_x < 4; ++map_x) { for(int map_y = 0; map_y < 4; ++map_y) { TileLocation* location = nd->getTile(map_x, map_y, map_z); DrawTile(location); if(location && options.isDrawLight()) { auto& position = location->getPosition(); if(position.x >= box_start_map_x && position.x <= box_end_map_x && position.y >= box_start_map_y && position.y <= box_end_map_y) { AddLight(location); } } } } if(tile_indicators) { for(int map_x = 0; map_x < 4; ++map_x) { for(int map_y = 0; map_y < 4; ++map_y) { DrawTileIndicators(nd->getTile(map_x, map_y, map_z)); } } } } else { if(!nd->isRequested(map_z > rme::MapGroundLayer)) { // Request the node editor.QueryNode(nd_map_x, nd_map_y, map_z > rme::MapGroundLayer); nd->setRequested(map_z > rme::MapGroundLayer, true); } int cy = (nd_map_y) * rme::TileSize - view_scroll_y - getFloorAdjustment(floor); int cx = (nd_map_x) * rme::TileSize - view_scroll_x - getFloorAdjustment(floor); glColor4ub(255, 0, 255, 128); glBegin(GL_QUADS); glVertex2f(cx, cy + rme::TileSize * 4); glVertex2f(cx + rme::TileSize * 4, cy + rme::TileSize * 4); glVertex2f(cx + rme::TileSize * 4, cy); glVertex2f(cx, cy); glEnd(); } } } if(!only_colors) glDisable(GL_TEXTURE_2D); DrawPositionIndicator(map_z); } // Draws the doodad preview or the paste preview (or import preview) DrawSecondaryMap(map_z); --start_x; --start_y; ++end_x; ++end_y; } if(!only_colors) glEnable(GL_TEXTURE_2D); } void MapDrawer::DrawSecondaryMap(int map_z) { if(options.ingame) return; BaseMap* secondary_map = g_gui.secondary_map; if(!secondary_map) return; Position normal_pos; Position to_pos(mouse_map_x, mouse_map_y, floor); if(canvas->isPasting()) { normal_pos = editor.copybuffer.getPosition(); } else { Brush* brush = g_gui.GetCurrentBrush(); if(brush && brush->isDoodad()) { normal_pos = Position(0x8000, 0x8000, 0x8); } } glEnable(GL_TEXTURE_2D); for(int map_x = start_x; map_x <= end_x; map_x++) { for(int map_y = start_y; map_y <= end_y; map_y++) { Position final_pos(map_x, map_y, map_z); Position pos = normal_pos + final_pos - to_pos; if(pos.z < 0 || pos.z >= rme::MapLayers) { continue; } Tile* tile = secondary_map->getTile(pos); if(!tile) continue; int draw_x, draw_y; getDrawPosition(final_pos, draw_x, draw_y); // Draw ground uint8_t r = 160, g = 160, b = 160; if(tile->ground) { if(options.show_blocking && tile->isBlocking()) { g = g/3*2; b = b/3*2; } if(options.show_houses && tile->isHouseTile()) { if(tile->getHouseID() == current_house_id) { r /= 2; } else { r /= 2; g /= 2; } } else if(options.show_special_tiles && tile->isPZ()) { r /= 2; b /= 2; } if(options.show_special_tiles && tile->getMapFlags() & TILESTATE_PVPZONE) { r = r/3*2; b = r/3*2; } if(options.show_special_tiles && tile->getMapFlags() & TILESTATE_NOLOGOUT) { b /= 2; } if(options.show_special_tiles && tile->getMapFlags() & TILESTATE_NOPVP) { g /= 2; } BlitItem(draw_x, draw_y, tile, tile->ground, true, r, g, b, 160); } bool hidden = options.hide_items_when_zoomed && zoom > 10.f; // Draw items if(!hidden && !tile->items.empty()) { for(const Item* item : tile->items) { if(item->isBorder()) { BlitItem(draw_x, draw_y, tile, item, true, 160, r, g, b); } else { BlitItem(draw_x, draw_y, tile, item, true, 160, 160, 160, 160); } } } // Draw creature if(!hidden && options.show_creatures && tile->creature) { BlitCreature(draw_x, draw_y, tile->creature); } } } glDisable(GL_TEXTURE_2D); } void MapDrawer::DrawIngameBox() { int center_x = start_x + int(screensize_x * zoom / 64); int center_y = start_y + int(screensize_y * zoom / 64); int offset_y = 2; int box_start_map_x = center_x; int box_start_map_y = center_y + offset_y; int box_end_map_x = center_x + rme::ClientMapWidth; int box_end_map_y = center_y + rme::ClientMapHeight + offset_y; int box_start_x = box_start_map_x * rme::TileSize - view_scroll_x; int box_start_y = box_start_map_y * rme::TileSize - view_scroll_y; int box_end_x = box_end_map_x * rme::TileSize - view_scroll_x; int box_end_y = box_end_map_y * rme::TileSize - view_scroll_y; if(options.isDrawLight()) { light_drawer->draw(box_start_map_x, box_start_map_y, view_scroll_x, view_scroll_y); } static wxColor side_color(0, 0, 0, 200); glDisable(GL_TEXTURE_2D); // left side if(box_start_map_x >= start_x) { drawFilledRect(0, 0, box_start_x, screensize_y * zoom, side_color); } // right side if(box_end_map_x < end_x) { drawFilledRect(box_end_x, 0, screensize_x * zoom, screensize_y * zoom, side_color); } // top side if(box_start_map_y >= start_y) { drawFilledRect(box_start_x, 0, box_end_x-box_start_x, box_start_y, side_color); } // bottom side if(box_end_map_y < end_y) { drawFilledRect(box_start_x, box_end_y, box_end_x-box_start_x, screensize_y * zoom, side_color); } // hidden tiles drawRect(box_start_x, box_start_y, box_end_x-box_start_x, box_end_y-box_start_y, *wxRED); // visible tiles box_start_x += rme::TileSize; box_start_y += rme::TileSize; box_end_x -= 2 * rme::TileSize; box_end_y -= 2 * rme::TileSize; drawRect(box_start_x, box_start_y, box_end_x-box_start_x, box_end_y-box_start_y, *wxGREEN); // player position box_start_x += ((rme::ClientMapWidth/2)-2) * rme::TileSize; box_start_y += ((rme::ClientMapHeight/2)-2) * rme::TileSize; box_end_x = box_start_x + rme::TileSize; box_end_y = box_start_y + rme::TileSize; drawRect(box_start_x, box_start_y, box_end_x-box_start_x, box_end_y-box_start_y, *wxGREEN); glEnable(GL_TEXTURE_2D); } void MapDrawer::DrawGrid() { glDisable(GL_TEXTURE_2D); glColor4ub(255, 255, 255, 128); glBegin(GL_LINES); for(int y = start_y; y < end_y; ++y) { int py = y * rme::TileSize - view_scroll_y; glVertex2f(start_x * rme::TileSize - view_scroll_x, py); glVertex2f(end_x * rme::TileSize - view_scroll_x, py); } for(int x = start_x; x < end_x; ++x) { int px = x * rme::TileSize - view_scroll_x; glVertex2f(px, start_y * rme::TileSize - view_scroll_y); glVertex2f(px, end_y * rme::TileSize - view_scroll_y); } glEnd(); glEnable(GL_TEXTURE_2D); } void MapDrawer::DrawDraggingShadow() { if(!dragging || options.ingame || editor.getSelection().isBusy()) return; glEnable(GL_TEXTURE_2D); for(Tile* tile : editor.getSelection()) { int move_z = canvas->drag_start_z - floor; int move_x = canvas->drag_start_x - mouse_map_x; int move_y = canvas->drag_start_y - mouse_map_y; if(move_x == 0 && move_y == 0 && move_z == 0) continue; const Position& position = tile->getPosition(); int pos_z = position.z - move_z; if(pos_z < 0 || pos_z >= rme::MapLayers) { continue; } int pos_x = position.x - move_x; int pos_y = position.y - move_y; // On screen and dragging? if(pos_x+2 > start_x && pos_x < end_x && pos_y+2 > start_y && pos_y < end_y) { Position pos(pos_x, pos_y, pos_z); int draw_x, draw_y; getDrawPosition(pos, draw_x, draw_y); ItemVector items = tile->getSelectedItems(); Tile* dest_tile = editor.getMap().getTile(pos); for(Item* item : items) { if(dest_tile) BlitItem(draw_x, draw_y, dest_tile, item, true, 160,160,160,160); else BlitItem(draw_x, draw_y, pos, item, true, 160,160,160,160); } if(options.show_creatures && tile->creature && tile->creature->isSelected()) BlitCreature(draw_x, draw_y, tile->creature); if(tile->spawn && tile->spawn->isSelected()) DrawIndicator(draw_x, draw_y, EDITOR_SPRITE_SPAWNS, 160, 160, 160, 160); } } glDisable(GL_TEXTURE_2D); } void MapDrawer::DrawHigherFloors() { if(!options.transparent_floors || floor == 0 || floor == 8) return; glEnable(GL_TEXTURE_2D); int map_z = floor - 1; for(int map_x = start_x; map_x <= end_x; map_x++) { for(int map_y = start_y; map_y <= end_y; map_y++) { Tile* tile = editor.getMap().getTile(map_x, map_y, map_z); if(!tile) continue; int draw_x, draw_y; getDrawPosition(tile->getPosition(), draw_x, draw_y); if(tile->ground) { if(tile->isPZ()) { BlitItem(draw_x, draw_y, tile, tile->ground, false, 128,255,128, 96); } else { BlitItem(draw_x, draw_y, tile, tile->ground, false, 255,255,255, 96); } } bool hidden = options.hide_items_when_zoomed && zoom > 10.f; if(!hidden && !tile->items.empty()) { for(const Item* item : tile->items) BlitItem(draw_x, draw_y, tile, item, false, 255,255,255, 96); } } } glDisable(GL_TEXTURE_2D); } void MapDrawer::DrawSelectionBox() { if (options.ingame) { return; } // Draw bounding box int last_click_rx = canvas->last_click_abs_x - view_scroll_x; int last_click_ry = canvas->last_click_abs_y - view_scroll_y; double cursor_rx = canvas->cursor_x * zoom; double cursor_ry = canvas->cursor_y * zoom; static double lines[4][4]; lines[0][0] = last_click_rx; lines[0][1] = last_click_ry; lines[0][2] = cursor_rx; lines[0][3] = last_click_ry; lines[1][0] = cursor_rx; lines[1][1] = last_click_ry; lines[1][2] = cursor_rx; lines[1][3] = cursor_ry; lines[2][0] = cursor_rx; lines[2][1] = cursor_ry; lines[2][2] = last_click_rx; lines[2][3] = cursor_ry; lines[3][0] = last_click_rx; lines[3][1] = cursor_ry; lines[3][2] = last_click_rx; lines[3][3] = last_click_ry; glDisable(GL_TEXTURE_2D); glEnable(GL_LINE_STIPPLE); glLineStipple(2, 0xAAAA); glLineWidth(1.0); glColor4f(1.0,1.0,1.0,1.0); glBegin(GL_LINES); for(int i = 0; i < 4; i++) { glVertex2f(lines[i][0], lines[i][1]); glVertex2f(lines[i][2], lines[i][3]); } glEnd(); glDisable(GL_LINE_STIPPLE); glEnable(GL_TEXTURE_2D); } void MapDrawer::DrawLiveCursors() { if(options.ingame || !editor.IsLive()) return; LiveSocket& live = editor.GetLive(); for(LiveCursor& cursor : live.getCursorList()) { if(cursor.pos.z <= rme::MapGroundLayer && floor > rme::MapGroundLayer) { continue; } if(cursor.pos.z > rme::MapGroundLayer && floor <= 8) { continue; } if(cursor.pos.z < floor) { cursor.color = wxColor( cursor.color.Red(), cursor.color.Green(), cursor.color.Blue(), std::max(cursor.color.Alpha() / 2, 64) ); } int offset; if(cursor.pos.z <= rme::MapGroundLayer) offset = (rme::MapGroundLayer - cursor.pos.z) * rme::TileSize; else offset = rme::TileSize * (floor - cursor.pos.z); float draw_x = ((cursor.pos.x * rme::TileSize) - view_scroll_x) - offset; float draw_y = ((cursor.pos.y * rme::TileSize) - view_scroll_y) - offset; glColor(cursor.color); glBegin(GL_QUADS); glVertex2f(draw_x, draw_y); glVertex2f(draw_x + rme::TileSize, draw_y); glVertex2f(draw_x + rme::TileSize, draw_y + rme::TileSize); glVertex2f(draw_x, draw_y + rme::TileSize); glEnd(); } } void MapDrawer::DrawBrush() { if(options.ingame || !g_gui.IsDrawingMode() || !g_gui.GetCurrentBrush()) { return; } Brush* brush = g_gui.GetCurrentBrush(); BrushColor brushColor = COLOR_BLANK; if(brush->isTerrain() || brush->isTable() || brush->isCarpet()) brushColor = COLOR_BRUSH; else if(brush->isHouse()) brushColor = COLOR_HOUSE_BRUSH; else if(brush->isFlag()) brushColor = COLOR_FLAG_BRUSH; else if(brush->isSpawn()) brushColor = COLOR_SPAWN_BRUSH; else if(brush->isEraser()) brushColor = COLOR_ERASER; int adjustment = getFloorAdjustment(floor); if(dragging_draw) { ASSERT(brush->canDrag()); if(brush->isWall()) { int last_click_start_map_x = std::min(canvas->last_click_map_x, mouse_map_x); int last_click_start_map_y = std::min(canvas->last_click_map_y, mouse_map_y); int last_click_end_map_x = std::max(canvas->last_click_map_x, mouse_map_x)+1; int last_click_end_map_y = std::max(canvas->last_click_map_y, mouse_map_y)+1; int last_click_start_sx = last_click_start_map_x * rme::TileSize - view_scroll_x - adjustment; int last_click_start_sy = last_click_start_map_y * rme::TileSize - view_scroll_y - adjustment; int last_click_end_sx = last_click_end_map_x * rme::TileSize - view_scroll_x - adjustment; int last_click_end_sy = last_click_end_map_y * rme::TileSize - view_scroll_y - adjustment; int delta_x = last_click_end_sx - last_click_start_sx; int delta_y = last_click_end_sy - last_click_start_sy; glColor(brushColor); glBegin(GL_QUADS); { glVertex2f(last_click_start_sx, last_click_start_sy + rme::TileSize); glVertex2f(last_click_end_sx, last_click_start_sy + rme::TileSize); glVertex2f(last_click_end_sx, last_click_start_sy); glVertex2f(last_click_start_sx, last_click_start_sy); } if(delta_y > rme::TileSize) { glVertex2f(last_click_start_sx, last_click_end_sy - rme::TileSize); glVertex2f(last_click_start_sx + rme::TileSize, last_click_end_sy - rme::TileSize); glVertex2f(last_click_start_sx + rme::TileSize, last_click_start_sy + rme::TileSize); glVertex2f(last_click_start_sx, last_click_start_sy + rme::TileSize); } if(delta_x > rme::TileSize && delta_y > rme::TileSize) { glVertex2f(last_click_end_sx - rme::TileSize, last_click_start_sy + rme::TileSize); glVertex2f(last_click_end_sx, last_click_start_sy + rme::TileSize); glVertex2f(last_click_end_sx, last_click_end_sy - rme::TileSize); glVertex2f(last_click_end_sx - rme::TileSize, last_click_end_sy - rme::TileSize); } if(delta_y > rme::TileSize) { glVertex2f(last_click_start_sx, last_click_end_sy - rme::TileSize); glVertex2f(last_click_end_sx, last_click_end_sy - rme::TileSize); glVertex2f(last_click_end_sx, last_click_end_sy); glVertex2f(last_click_start_sx, last_click_end_sy); } glEnd(); } else { if(brush->isRaw()) glEnable(GL_TEXTURE_2D); if(g_gui.GetBrushShape() == BRUSHSHAPE_SQUARE || brush->isSpawn() /* Spawn brush is always square */) { if(brush->isRaw() || brush->isOptionalBorder()) { int start_x, end_x; int start_y, end_y; if(mouse_map_x < canvas->last_click_map_x) { start_x = mouse_map_x; end_x = canvas->last_click_map_x; } else { start_x = canvas->last_click_map_x; end_x = mouse_map_x; } if(mouse_map_y < canvas->last_click_map_y) { start_y = mouse_map_y; end_y = canvas->last_click_map_y; } else { start_y = canvas->last_click_map_y; end_y = mouse_map_y; } RAWBrush* raw_brush = nullptr; if(brush->isRaw()) raw_brush = brush->asRaw(); for(int y = start_y; y <= end_y; y++) { int cy = y * rme::TileSize - view_scroll_y - adjustment; for(int x = start_x; x <= end_x; x++) { int cx = x * rme::TileSize - view_scroll_x - adjustment; if(brush->isOptionalBorder()) glColorCheck(brush, Position(x, y, floor)); else BlitSpriteType(cx, cy, raw_brush->getItemType()->sprite, 160, 160, 160, 160); } } } else { int last_click_start_map_x = std::min(canvas->last_click_map_x, mouse_map_x); int last_click_start_map_y = std::min(canvas->last_click_map_y, mouse_map_y); int last_click_end_map_x = std::max(canvas->last_click_map_x, mouse_map_x)+1; int last_click_end_map_y = std::max(canvas->last_click_map_y, mouse_map_y)+1; int last_click_start_sx = last_click_start_map_x * rme::TileSize - view_scroll_x - adjustment; int last_click_start_sy = last_click_start_map_y * rme::TileSize - view_scroll_y - adjustment; int last_click_end_sx = last_click_end_map_x * rme::TileSize - view_scroll_x - adjustment; int last_click_end_sy = last_click_end_map_y * rme::TileSize - view_scroll_y - adjustment; glColor(brushColor); glBegin(GL_QUADS); glVertex2f(last_click_start_sx, last_click_start_sy); glVertex2f(last_click_end_sx, last_click_start_sy); glVertex2f(last_click_end_sx, last_click_end_sy); glVertex2f(last_click_start_sx, last_click_end_sy); glEnd(); } } else if(g_gui.GetBrushShape() == BRUSHSHAPE_CIRCLE) { // Calculate drawing offsets int start_x, end_x; int start_y, end_y; int width = std::max( std::abs(std::max(mouse_map_y, canvas->last_click_map_y) - std::min(mouse_map_y, canvas->last_click_map_y)), std::abs(std::max(mouse_map_x, canvas->last_click_map_x) - std::min(mouse_map_x, canvas->last_click_map_x)) ); if(mouse_map_x < canvas->last_click_map_x) { start_x = canvas->last_click_map_x - width; end_x = canvas->last_click_map_x; } else { start_x = canvas->last_click_map_x; end_x = canvas->last_click_map_x + width; } if(mouse_map_y < canvas->last_click_map_y) { start_y = canvas->last_click_map_y - width; end_y = canvas->last_click_map_y; } else { start_y = canvas->last_click_map_y; end_y = canvas->last_click_map_y + width; } int center_x = start_x + (end_x - start_x) / 2; int center_y = start_y + (end_y - start_y) / 2; float radii = width / 2.0f + 0.005f; RAWBrush* raw_brush = nullptr; if(brush->isRaw()) raw_brush = brush->asRaw(); for(int y = start_y-1; y <= end_y+1; y++) { int cy = y * rme::TileSize - view_scroll_y - adjustment; float dy = center_y - y; for(int x = start_x-1; x <= end_x+1; x++) { int cx = x * rme::TileSize - view_scroll_x - adjustment; float dx = center_x - x; //printf("%f;%f\n", dx, dy); float distance = sqrt(dx*dx + dy*dy); if(distance < radii) { if(brush->isRaw()) { BlitSpriteType(cx, cy, raw_brush->getItemType()->sprite, 160, 160, 160, 160); } else { glColor(brushColor); glBegin(GL_QUADS); glVertex2f(cx, cy + rme::TileSize); glVertex2f(cx + rme::TileSize, cy + rme::TileSize); glVertex2f(cx + rme::TileSize, cy); glVertex2f(cx, cy); glEnd(); } } } } } if(brush->isRaw()) glDisable(GL_TEXTURE_2D); } } else { if(brush->isWall()) { int start_map_x = mouse_map_x - g_gui.GetBrushSize(); int start_map_y = mouse_map_y - g_gui.GetBrushSize(); int end_map_x = mouse_map_x + g_gui.GetBrushSize() + 1; int end_map_y = mouse_map_y + g_gui.GetBrushSize() + 1; int start_sx = start_map_x * rme::TileSize - view_scroll_x - adjustment; int start_sy = start_map_y * rme::TileSize - view_scroll_y - adjustment; int end_sx = end_map_x * rme::TileSize - view_scroll_x - adjustment; int end_sy = end_map_y * rme::TileSize - view_scroll_y - adjustment; int delta_x = end_sx - start_sx; int delta_y = end_sy - start_sy; glColor(brushColor); glBegin(GL_QUADS); { glVertex2f(start_sx, start_sy + rme::TileSize); glVertex2f(end_sx, start_sy + rme::TileSize); glVertex2f(end_sx, start_sy); glVertex2f(start_sx, start_sy); } if(delta_y > rme::TileSize) { glVertex2f(start_sx, end_sy - rme::TileSize); glVertex2f(start_sx + rme::TileSize, end_sy - rme::TileSize); glVertex2f(start_sx + rme::TileSize, start_sy + rme::TileSize); glVertex2f(start_sx, start_sy + rme::TileSize); } if(delta_x > rme::TileSize && delta_y > rme::TileSize) { glVertex2f(end_sx - rme::TileSize, start_sy + rme::TileSize); glVertex2f(end_sx, start_sy + rme::TileSize); glVertex2f(end_sx, end_sy - rme::TileSize); glVertex2f(end_sx - rme::TileSize, end_sy - rme::TileSize); } if(delta_y > rme::TileSize) { glVertex2f(start_sx, end_sy - rme::TileSize); glVertex2f(end_sx, end_sy - rme::TileSize); glVertex2f(end_sx, end_sy); glVertex2f(start_sx, end_sy); } glEnd(); } else if(brush->isDoor()) { int cx = (mouse_map_x) * rme::TileSize - view_scroll_x - adjustment; int cy = (mouse_map_y) * rme::TileSize - view_scroll_y - adjustment; glColorCheck(brush, Position(mouse_map_x, mouse_map_y, floor)); glBegin(GL_QUADS); glVertex2f(cx, cy + rme::TileSize); glVertex2f(cx + rme::TileSize, cy + rme::TileSize); glVertex2f(cx + rme::TileSize, cy); glVertex2f(cx, cy); glEnd(); } else if(brush->isCreature()) { glEnable(GL_TEXTURE_2D); int cy = (mouse_map_y) * rme::TileSize - view_scroll_y - adjustment; int cx = (mouse_map_x) * rme::TileSize - view_scroll_x - adjustment; CreatureBrush* creature_brush = brush->asCreature(); if(creature_brush->canDraw(&editor.getMap(), Position(mouse_map_x, mouse_map_y, floor))) BlitCreature(cx, cy, creature_brush->getType()->outfit, SOUTH, 255, 255, 255, 160); else BlitCreature(cx, cy, creature_brush->getType()->outfit, SOUTH, 255, 64, 64, 160); glDisable(GL_TEXTURE_2D); } else if(!brush->isDoodad()) { RAWBrush* raw_brush = nullptr; if(brush->isRaw()) { // Textured brush glEnable(GL_TEXTURE_2D); raw_brush = brush->asRaw(); } else { glDisable(GL_TEXTURE_2D); } for(int y = -g_gui.GetBrushSize()-1; y <= g_gui.GetBrushSize()+1; y++) { int cy = (mouse_map_y + y) * rme::TileSize - view_scroll_y - adjustment; for(int x = -g_gui.GetBrushSize()-1; x <= g_gui.GetBrushSize()+1; x++) { int cx = (mouse_map_x + x) * rme::TileSize - view_scroll_x - adjustment; if(g_gui.GetBrushShape() == BRUSHSHAPE_SQUARE) { if(x >= -g_gui.GetBrushSize() && x <= g_gui.GetBrushSize() && y >= -g_gui.GetBrushSize() && y <= g_gui.GetBrushSize()) { if(brush->isRaw()) { BlitSpriteType(cx, cy, raw_brush->getItemType()->sprite, 160, 160, 160, 160); } else { if(brush->isWaypoint()) { uint8_t r, g, b; getColor(brush, Position(mouse_map_x + x, mouse_map_y + y, floor), r, g, b); DrawBrushIndicator(cx, cy, brush, r, g, b); } else { if(brush->isHouseExit() || brush->isOptionalBorder()) glColorCheck(brush, Position(mouse_map_x + x, mouse_map_y + y, floor)); else glColor(brushColor); glBegin(GL_QUADS); glVertex2f(cx, cy + rme::TileSize); glVertex2f(cx + rme::TileSize, cy + rme::TileSize); glVertex2f(cx + rme::TileSize, cy); glVertex2f(cx, cy); glEnd(); } } } } else if(g_gui.GetBrushShape() == BRUSHSHAPE_CIRCLE) { double distance = sqrt(double(x*x) + double(y*y)); if(distance < g_gui.GetBrushSize()+0.005) { if(brush->isRaw()) { BlitSpriteType(cx, cy, raw_brush->getItemType()->sprite, 160, 160, 160, 160); } else { if(brush->isWaypoint()) { uint8_t r, g, b; getColor(brush, Position(mouse_map_x + x, mouse_map_y + y, floor), r, g, b); DrawBrushIndicator(cx, cy, brush, r, g, b); } else { if(brush->isHouseExit() || brush->isOptionalBorder()) glColorCheck(brush, Position(mouse_map_x + x, mouse_map_y + y, floor)); else glColor(brushColor); glBegin(GL_QUADS); glVertex2f(cx, cy + rme::TileSize); glVertex2f(cx + rme::TileSize, cy + rme::TileSize); glVertex2f(cx + rme::TileSize, cy); glVertex2f(cx, cy); glEnd(); } } } } } } if(brush->isRaw()) { // Textured brush glDisable(GL_TEXTURE_2D); } else { glEnable(GL_TEXTURE_2D); } } } } void MapDrawer::BlitItem(int& draw_x, int& draw_y, const Tile* tile, const Item* item, bool ephemeral, int red, int green, int blue, int alpha) { const ItemType& type = g_items.getItemType(item->getID()); if(type.id == 0) { glDisable(GL_TEXTURE_2D); glBlitSquare(draw_x, draw_y, *wxRED); glEnable(GL_TEXTURE_2D); return; } if(!options.ingame && !ephemeral && item->isSelected()) { red /= 2; blue /= 2; green /= 2; } // Ugly hacks. :) if(type.id == 459 && !options.ingame) { glDisable(GL_TEXTURE_2D); glBlitSquare(draw_x, draw_y, red, green, 0, alpha/3*2); glEnable(GL_TEXTURE_2D); return; } else if(type.id == 460 && !options.ingame) { glDisable(GL_TEXTURE_2D); glBlitSquare(draw_x, draw_y, red, 0, 0, alpha/3*2); glEnable(GL_TEXTURE_2D); return; } if(type.isMetaItem()) return; if(!ephemeral && type.pickupable && !options.show_items) return; GameSprite* sprite = type.sprite; if(!sprite) return; int screenx = draw_x - sprite->getDrawOffset().x; int screeny = draw_y - sprite->getDrawOffset().y; const Position& pos = tile->getPosition(); // Set the newd drawing height accordingly draw_x -= sprite->getDrawHeight(); draw_y -= sprite->getDrawHeight(); int subtype = -1; int pattern_x = pos.x % sprite->pattern_x; int pattern_y = pos.y % sprite->pattern_y; int pattern_z = pos.z % sprite->pattern_z; if(type.isSplash() || type.isFluidContainer()) { subtype = item->getSubtype(); } else if(type.isHangable) { if(tile->hasProperty(HOOK_SOUTH)) { pattern_x = 1; } else if(tile->hasProperty(HOOK_EAST)) { pattern_x = 2; } else { pattern_x = 0; } } else if(type.stackable) { if(item->getSubtype() <= 1) subtype = 0; else if(item->getSubtype() <= 2) subtype = 1; else if(item->getSubtype() <= 3) subtype = 2; else if(item->getSubtype() <= 4) subtype = 3; else if(item->getSubtype() < 10) subtype = 4; else if(item->getSubtype() < 25) subtype = 5; else if(item->getSubtype() < 50) subtype = 6; else subtype = 7; } if(!ephemeral && options.transparent_items && (!type.isGroundTile() || sprite->width > 1 || sprite->height > 1) && !type.isSplash() && (!type.isBorder || sprite->width > 1 || sprite->height > 1) ) { alpha /= 2; } int frame = item->getFrame(); for(int cx = 0; cx != sprite->width; cx++) { for(int cy = 0; cy != sprite->height; cy++) { for(int cf = 0; cf != sprite->layers; cf++) { int texnum = sprite->getHardwareID(cx,cy,cf, subtype, pattern_x, pattern_y, pattern_z, frame ); glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); } } } if(options.show_hooks && (type.hookSouth || type.hookEast)) DrawHookIndicator(draw_x, draw_y, type); } void MapDrawer::BlitItem(int& draw_x, int& draw_y, const Position& pos, const Item* item, bool ephemeral, int red, int green, int blue, int alpha) { const ItemType& type = g_items.getItemType(item->getID()); if(type.id == 0) return; if(!options.ingame && !ephemeral && item->isSelected()) { red /= 2; blue /= 2; green /= 2; } if(type.id == 459 && !options.ingame) { // Ugly hack yes? glDisable(GL_TEXTURE_2D); glBlitSquare(draw_x, draw_y, red, green, 0, alpha/3*2); glEnable(GL_TEXTURE_2D); return; } else if(type.id == 460 && !options.ingame) { // Ugly hack yes? glDisable(GL_TEXTURE_2D); glBlitSquare(draw_x, draw_y, red, 0, 0, alpha/3*2); glEnable(GL_TEXTURE_2D); return; } if(type.isMetaItem()) return; if(!ephemeral && type.pickupable && options.show_items) return; GameSprite* sprite = type.sprite; if(!sprite) return; int screenx = draw_x - sprite->getDrawOffset().x; int screeny = draw_y - sprite->getDrawOffset().y; // Set the newd drawing height accordingly draw_x -= sprite->getDrawHeight(); draw_y -= sprite->getDrawHeight(); int subtype = -1; int pattern_x = pos.x % sprite->pattern_x; int pattern_y = pos.y % sprite->pattern_y; int pattern_z = pos.z % sprite->pattern_z; if(type.isSplash() || type.isFluidContainer()) { subtype = item->getSubtype(); } else if(type.isHangable) { pattern_x = 0; /* if(tile->hasProperty(HOOK_SOUTH)) { pattern_x = 2; } else if(tile->hasProperty(HOOK_EAST)) { pattern_x = 1; } else { pattern_x = -0; } */ } else if(type.stackable) { if(item->getSubtype() <= 1) subtype = 0; else if(item->getSubtype() <= 2) subtype = 1; else if(item->getSubtype() <= 3) subtype = 2; else if(item->getSubtype() <= 4) subtype = 3; else if(item->getSubtype() < 10) subtype = 4; else if(item->getSubtype() < 25) subtype = 5; else if(item->getSubtype() < 50) subtype = 6; else subtype = 7; } if(!ephemeral && options.transparent_items && (!type.isGroundTile() || sprite->width > 1 || sprite->height > 1) && !type.isSplash() && (!type.isBorder || sprite->width > 1 || sprite->height > 1) ) { alpha /= 2; } int frame = item->getFrame(); for(int cx = 0; cx != sprite->width; ++cx) { for(int cy = 0; cy != sprite->height; ++cy) { for(int cf = 0; cf != sprite->layers; ++cf) { int texnum = sprite->getHardwareID(cx,cy,cf, subtype, pattern_x, pattern_y, pattern_z, frame ); glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); } } } if(options.show_hooks && (type.hookSouth || type.hookEast) && zoom <= 3.0) DrawHookIndicator(draw_x, draw_y, type); } void MapDrawer::BlitSpriteType(int screenx, int screeny, uint32_t spriteid, int red, int green, int blue, int alpha) { const ItemType& type = g_items.getItemType(spriteid); if(type.id == 0) return; GameSprite* sprite = type.sprite; if(!sprite) return; screenx -= sprite->getDrawOffset().x; screeny -= sprite->getDrawOffset().y; int frame = 0; for(int cx = 0; cx != sprite->width; ++cx) { for(int cy = 0; cy != sprite->height; ++cy) { for(int cf = 0; cf != sprite->layers; ++cf) { int texnum = sprite->getHardwareID(cx,cy,cf,-1,0,0,0, frame); glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); } } } } void MapDrawer::BlitSpriteType(int screenx, int screeny, GameSprite* sprite, int red, int green, int blue, int alpha) { if(!sprite) return; screenx -= sprite->getDrawOffset().x; screeny -= sprite->getDrawOffset().y; int frame = 0; for(int cx = 0; cx != sprite->width; ++cx) { for(int cy = 0; cy != sprite->height; ++cy) { for(int cf = 0; cf != sprite->layers; ++cf) { int texnum = sprite->getHardwareID(cx,cy,cf,-1,0,0,0, frame); glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); } } } } void MapDrawer::BlitCreature(int screenx, int screeny, const Outfit& outfit, Direction dir, int red, int green, int blue, int alpha) { if(outfit.lookItem != 0) { const ItemType& type = g_items.getItemType(outfit.lookItem); BlitSpriteType(screenx, screeny, type.sprite, red, green, blue, alpha); } else { GameSprite* sprite = g_gui.gfx.getCreatureSprite(outfit.lookType); if(!sprite || outfit.lookType == 0) { return; } // mount and addon drawing thanks to otc code int pattern_z = 0; if(outfit.lookMount != 0) { if(GameSprite* mountSpr = g_gui.gfx.getCreatureSprite(outfit.lookMount)) { for(int cx = 0; cx != mountSpr->width; ++cx) { for(int cy = 0; cy != mountSpr->height; ++cy) { int texnum = mountSpr->getHardwareID(cx, cy, 0, 0, (int)dir, 0, 0, 0); glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); } } pattern_z = std::min(1, sprite->pattern_z - 1); } } int frame = 0; // pattern_y => creature addon for(int pattern_y = 0; pattern_y < sprite->pattern_y; pattern_y++) { // continue if we dont have this addon if(pattern_y > 0 && !(outfit.lookAddon & (1 << (pattern_y - 1)))) continue; for(int cx = 0; cx != sprite->width; ++cx) { for(int cy = 0; cy != sprite->height; ++cy) { int texnum = sprite->getHardwareID(cx, cy, (int)dir, pattern_y, pattern_z, outfit, frame); glBlitTexture(screenx - cx * rme::TileSize, screeny - cy * rme::TileSize, texnum, red, green, blue, alpha); } } } } } void MapDrawer::BlitCreature(int screenx, int screeny, const Creature* creature, int red, int green, int blue, int alpha) { if(!options.ingame && creature->isSelected()) { red /= 2; green /= 2; blue /= 2; } BlitCreature(screenx, screeny, creature->getLookType(), creature->getDirection(), red, green, blue, alpha); } void MapDrawer::WriteTooltip(const Item* item, std::ostringstream& stream) { if(!item) return; const uint16_t id = item->getID(); if(id < 100) return; const uint16_t unique = item->getUniqueID(); const uint16_t action = item->getActionID(); const std::string& text = item->getText(); if(unique == 0 && action == 0 && text.empty()) return; if(stream.tellp() > 0) stream << "\n"; stream << "id: " << id << "\n"; if(action > 0) stream << "aid: " << action << "\n"; if(unique > 0) stream << "uid: " << unique << "\n"; if(!text.empty()) stream << "text: " << text << "\n"; } void MapDrawer::WriteTooltip(const Waypoint* waypoint, std::ostringstream& stream) { if(stream.tellp() > 0) stream << "\n"; stream << "wp: " << waypoint->name << "\n"; } void MapDrawer::DrawTile(TileLocation* location) { if(!location) return; Tile* tile = location->get(); if(!tile) return; if(options.show_only_modified && !tile->isModified()) return; const Position& position = location->getPosition(); bool show_tooltips = options.isTooltips(); if(show_tooltips && location->getWaypointCount() > 0) { Waypoint* waypoint = canvas->editor.getMap().waypoints.getWaypoint(position); if(waypoint) WriteTooltip(waypoint, tooltip); } bool only_colors = options.isOnlyColors(); int draw_x, draw_y; getDrawPosition(position, draw_x, draw_y); uint8_t r = 255,g = 255,b = 255; if(only_colors || tile->hasGround()) { if(!options.show_as_minimap) { bool showspecial = options.show_only_colors || options.show_special_tiles; if(options.show_blocking && tile->isBlocking() && tile->size() > 0) { g = g / 3 * 2; b = b / 3 * 2; } int item_count = tile->items.size(); if(options.highlight_items && item_count > 0 && !tile->items.back()->isBorder()) { static const float factor[5] = { 0.75f, 0.6f, 0.48f, 0.40f, 0.33f }; int idx = (item_count < 5 ? item_count : 5) - 1; g = int(g * factor[idx]); r = int(r * factor[idx]); } if(options.show_spawns && location->getSpawnCount() > 0) { float f = 1.0f; for(uint32_t i = 0; i < location->getSpawnCount(); ++i) { f *= 0.7f; } g = uint8_t(g * f); b = uint8_t(b * f); } if(options.show_houses && tile->isHouseTile()) { if((int)tile->getHouseID() == current_house_id) { r /= 2; } else { r /= 2; g /= 2; } } else if(showspecial && tile->isPZ()) { r /= 2; b /= 2; } if(showspecial && tile->getMapFlags() & TILESTATE_PVPZONE) { g = r / 4; b = b / 3 * 2; } if(showspecial && tile->getMapFlags() & TILESTATE_NOLOGOUT) { b /= 2; } if(showspecial && tile->getMapFlags() & TILESTATE_NOPVP) { g /= 2; } } if(only_colors) { glDisable(GL_TEXTURE_2D); if(options.show_as_minimap) { wxColor color = colorFromEightBit(tile->getMiniMapColor()); glBlitSquare(draw_x, draw_y, color); } else if(r != 255 || g != 255 || b != 255) { glBlitSquare(draw_x, draw_y, r, g, b, 128); } glEnable(GL_TEXTURE_2D); } else { if(options.show_preview && zoom <= 2.0) tile->ground->animate(); BlitItem(draw_x, draw_y, tile, tile->ground, false, r, g, b); } if(show_tooltips && position.z == floor) WriteTooltip(tile->ground, tooltip); } bool hidden = only_colors || (options.hide_items_when_zoomed && zoom > 10.f); if(!hidden && !tile->items.empty()) { for(Item* item : tile->items) { if(show_tooltips && position.z == floor) WriteTooltip(item, tooltip); if(options.show_preview && zoom <= 2.0) item->animate(); if(item->isBorder()) { BlitItem(draw_x, draw_y, tile, item, false, r, g, b); } else { BlitItem(draw_x, draw_y, tile, item); } } } if(!hidden && options.show_creatures && tile->creature) { BlitCreature(draw_x, draw_y, tile->creature); } if(show_tooltips) { if(location->getWaypointCount() > 0) MakeTooltip(draw_x, draw_y, tooltip.str(), 0, 255, 0); else MakeTooltip(draw_x, draw_y, tooltip.str()); tooltip.str(""); } } void MapDrawer::DrawBrushIndicator(int x, int y, Brush* brush, uint8_t r, uint8_t g, uint8_t b) { x += (rme::TileSize / 2); y += (rme::TileSize / 2); // 7----0----1 // | | // 6--5 3--2 // \/ // 4 static int vertexes[9][2] = { {-15, -20}, // 0 { 15, -20}, // 1 { 15, -5}, // 2 { 5, -5}, // 3 { 0, 0}, // 4 {-5, -5}, // 5 {-15, -5}, // 6 {-15, -20}, // 7 {-15, -20}, // 0 }; // circle glBegin(GL_TRIANGLE_FAN); glColor4ub(0x00, 0x00, 0x00, 0x50); glVertex2i(x, y); for(int i = 0; i <= 30; i++) { float angle = i * 2.0f * rme::PI / 30; glVertex2f(cos(angle) * (rme::TileSize / 2) + x, sin(angle) * (rme::TileSize / 2) + y); } glEnd(); // background glColor4ub(r, g, b, 0xB4); glBegin(GL_POLYGON); for(int i = 0; i < 8; ++i) glVertex2i(vertexes[i][0] + x, vertexes[i][1] + y); glEnd(); // borders glColor4ub(0x00, 0x00, 0x00, 0xB4); glLineWidth(1.0); glBegin(GL_LINES); for(int i = 0; i < 8; ++i) { glVertex2i(vertexes[i][0] + x, vertexes[i][1] + y); glVertex2i(vertexes[i + 1][0] + x, vertexes[i + 1][1] + y); } glEnd(); } void MapDrawer::DrawHookIndicator(int x, int y, const ItemType& type) { glDisable(GL_TEXTURE_2D); glColor4ub(uint8_t(0), uint8_t(0), uint8_t(255), uint8_t(200)); glBegin(GL_QUADS); if(type.hookSouth) { x -= 10; y += 10; glVertex2f(x, y); glVertex2f(x + 10, y); glVertex2f(x + 20, y + 10); glVertex2f(x + 10, y + 10); } else if(type.hookEast) { x += 10; y -= 10; glVertex2f(x, y); glVertex2f(x + 10, y + 10); glVertex2f(x + 10, y + 20); glVertex2f(x, y + 10); } glEnd(); glEnable(GL_TEXTURE_2D); } void MapDrawer::DrawTileIndicators(TileLocation* location) { if(!location) return; Tile* tile = location->get(); if(!tile) return; int x, y; getDrawPosition(location->getPosition(), x, y); if(zoom < 10.0 && (options.show_pickupables || options.show_moveables)) { uint8_t red = 0xFF, green = 0xFF, blue = 0xFF; if(tile->isHouseTile()) { green = 0x00; blue = 0x00; } for(const Item* item : tile->items) { const ItemType& type = g_items.getItemType(item->getID()); if((type.pickupable && options.show_pickupables) || (type.moveable && options.show_moveables)) { if(type.pickupable && options.show_pickupables && type.moveable && options.show_moveables) DrawIndicator(x, y, EDITOR_SPRITE_PICKUPABLE_MOVEABLE_ITEM, red, green, blue); else if(type.pickupable && options.show_pickupables) DrawIndicator(x, y, EDITOR_SPRITE_PICKUPABLE_ITEM, red, green, blue); else if(type.moveable && options.show_moveables) DrawIndicator(x, y, EDITOR_SPRITE_MOVEABLE_ITEM, red, green, blue); } } } if(options.show_houses && tile->isHouseExit()) { if(tile->hasHouseExit(current_house_id)) { DrawIndicator(x, y, EDITOR_SPRITE_HOUSE_EXIT); } else { DrawIndicator(x, y, EDITOR_SPRITE_HOUSE_EXIT, 64, 64, 255, 128); } } if(options.show_spawns && tile->spawn) { if(tile->spawn->isSelected()) { DrawIndicator(x, y, EDITOR_SPRITE_SPAWNS, 128, 128, 128); } else { DrawIndicator(x, y, EDITOR_SPRITE_SPAWNS); } } } void MapDrawer::DrawIndicator(int x, int y, int indicator, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { GameSprite* sprite = g_gui.gfx.getEditorSprite(indicator); if(sprite == nullptr) return; int textureId = sprite->getHardwareID(0,0,0,-1,0,0,0,0); glBlitTexture(x, y, textureId, r, g, b, a, true); } void MapDrawer::DrawPositionIndicator(int z) { if(z != pos_indicator.z || pos_indicator.x < start_x || pos_indicator.x > end_x || pos_indicator.y < start_y || pos_indicator.y > end_y) { return; } constexpr int duration = 3000; const long time = pos_indicator_timer.Time(); if(time >= duration) return; int x, y; getDrawPosition(pos_indicator, x, y); int size = static_cast(rme::TileSize * (0.3f + std::abs(500 - time % 1000) / 1000.f)); int offset = (rme::TileSize - size) / 2; glDisable(GL_TEXTURE_2D); drawRect(x + offset + 2, y + offset + 2, size - 4, size - 4, *wxWHITE, 2); drawRect(x + offset + 1, y + offset + 1, size - 2, size - 2, *wxBLACK, 2); glEnable(GL_TEXTURE_2D); } void MapDrawer::DrawTooltips() { if(!options.show_tooltips || tooltips.empty()) return; glDisable(GL_TEXTURE_2D); for(MapTooltip* tooltip : tooltips) { const char* text = tooltip->text.c_str(); float line_width = 0.0f; float width = 2.0f; float height = 14.0f; int char_count = 0; int line_char_count = 0; for(const char* c = text; *c != '\0'; c++) { if(*c == '\n' || (line_char_count >= MapTooltip::MAX_CHARS_PER_LINE && *c == ' ')) { height += 14.0f; line_width = 0.0f; line_char_count = 0; } else { line_width += glutBitmapWidth(GLUT_BITMAP_HELVETICA_12, *c); } width = std::max(width, line_width); char_count++; line_char_count++; if(tooltip->ellipsis && char_count > (MapTooltip::MAX_CHARS + 3)) break; } float scale = zoom < 1.0f ? zoom : 1.0f; width = (width + 8.0f) * scale; height = (height + 4.0f) * scale; float x = tooltip->x + (rme::TileSize / 2.0f); float y = tooltip->y + ((rme::TileSize / 2.0f) * scale); float center = width / 2.0f; float space = (7.0f * scale); float startx = x - center; float endx = x + center; float starty = y - (height + space); float endy = y - space; // 7----0----1 // | | // 6--5 3--2 // \/ // 4 float vertexes[9][2] = { {x, starty}, // 0 {endx, starty}, // 1 {endx, endy}, // 2 {x + space, endy}, // 3 {x, y}, // 4 {x - space, endy}, // 5 {startx, endy}, // 6 {startx, starty}, // 7 {x, starty}, // 0 }; // background glColor4ub(tooltip->r, tooltip->g, tooltip->b, 255); glBegin(GL_POLYGON); for(int i = 0; i < 8; ++i) glVertex2f(vertexes[i][0], vertexes[i][1]); glEnd(); // borders glColor4ub(0, 0, 0, 255); glLineWidth(1.0); glBegin(GL_LINES); for(int i = 0; i < 8; ++i) { glVertex2f(vertexes[i][0], vertexes[i][1]); glVertex2f(vertexes[i + 1][0], vertexes[i + 1][1]); } glEnd(); // text if(zoom <= 1.0) { startx += (3.0f * scale); starty += (14.0f * scale); glColor4ub(0, 0, 0, 255); glRasterPos2f(startx, starty); char_count = 0; line_char_count = 0; for(const char* c = text; *c != '\0'; c++) { if(*c == '\n' || (line_char_count >= MapTooltip::MAX_CHARS_PER_LINE && *c == ' ')) { starty += (14.0f * scale); glRasterPos2f(startx, starty); line_char_count = 0; } char_count++; line_char_count++; if(tooltip->ellipsis && char_count >= MapTooltip::MAX_CHARS) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, '.'); if(char_count >= (MapTooltip::MAX_CHARS + 2)) break; } else if(!iscntrl(*c)) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *c); } } } } glEnable(GL_TEXTURE_2D); } void MapDrawer::MakeTooltip(int screenx, int screeny, const std::string& text, uint8_t r, uint8_t g, uint8_t b) { if(text.empty()) return; MapTooltip *tooltip = new MapTooltip(screenx, screeny, text, r, g, b); tooltip->checkLineEnding(); tooltips.push_back(tooltip); } void MapDrawer::AddLight(TileLocation* location) { if(!options.isDrawLight() || !location) { return; } auto tile = location->get(); if(!tile) { return; } auto& position = location->getPosition(); if(tile->ground) { if (tile->ground->hasLight()) { light_drawer->addLight(position.x, position.y, tile->ground->getLight()); } } bool hidden = options.hide_items_when_zoomed && zoom > 10.f; if(!hidden && !tile->items.empty()) { for(auto item : tile->items) { if(item->hasLight()) { light_drawer->addLight(position.x, position.y, item->getLight()); } } } } void MapDrawer::getColor(Brush* brush, const Position& position, uint8_t &r, uint8_t &g, uint8_t &b) { if(brush->canDraw(&editor.getMap(), position)) { if(brush->isWaypoint()) { r = 0x00; g = 0xff, b = 0x00; } else { r = 0x00; g = 0x00, b = 0xff; } } else { r = 0xff; g = 0x00, b = 0x00; } } void MapDrawer::TakeScreenshot(uint8_t* screenshot_buffer) { glFinish(); // Wait for the operation to finish glPixelStorei(GL_PACK_ALIGNMENT, 1); // 1 byte alignment for(int i = 0; i < screensize_y; ++i) glReadPixels(0, screensize_y - i, screensize_x, 1, GL_RGB, GL_UNSIGNED_BYTE, (GLubyte*)(screenshot_buffer) + 3*screensize_x*i); } void MapDrawer::ShowPositionIndicator(const Position& position) { pos_indicator = position; pos_indicator_timer.Start(); } void MapDrawer::glBlitTexture(int x, int y, int textureId, int red, int green, int blue, int alpha, bool adjustZoom) { if(textureId <= 0) return; glBindTexture(GL_TEXTURE_2D, textureId); glColor4ub(uint8_t(red), uint8_t(green), uint8_t(blue), uint8_t(alpha)); glBegin(GL_QUADS); if(adjustZoom) { float size = rme::TileSize; if(zoom < 1.0f) { float offset = 10 / (10 * zoom); size = std::max(16, rme::TileSize * zoom); x += offset; y += offset; } else if(zoom > 1.f) { float offset = (10 * zoom); size = rme::TileSize + offset; x -= offset; y -= offset; } glTexCoord2f(0.f, 0.f); glVertex2f(x, y); glTexCoord2f(1.f, 0.f); glVertex2f(x + size, y); glTexCoord2f(1.f, 1.f); glVertex2f(x + size, y + size); glTexCoord2f(0.f, 1.f); glVertex2f(x, y + size); } else { glTexCoord2f(0.f, 0.f); glVertex2f(x, y); glTexCoord2f(1.f, 0.f); glVertex2f(x + rme::TileSize, y); glTexCoord2f(1.f, 1.f); glVertex2f(x + rme::TileSize, y + rme::TileSize); glTexCoord2f(0.f, 1.f); glVertex2f(x, y + rme::TileSize); } glEnd(); } void MapDrawer::glBlitSquare(int x, int y, int red, int green, int blue, int alpha) { glColor4ub(uint8_t(red), uint8_t(green), uint8_t(blue), uint8_t(alpha)); glBegin(GL_QUADS); glVertex2f(x, y); glVertex2f(x + rme::TileSize, y); glVertex2f(x + rme::TileSize, y + rme::TileSize); glVertex2f(x, y + rme::TileSize); glEnd(); } void MapDrawer::glBlitSquare(int x, int y, const wxColor& color) { glColor4ub(color.Red(), color.Green(), color.Blue(), color.Alpha()); glBegin(GL_QUADS); glVertex2f(x, y); glVertex2f(x + rme::TileSize, y); glVertex2f(x + rme::TileSize, y + rme::TileSize); glVertex2f(x, y + rme::TileSize); glEnd(); } void MapDrawer::glColor(const wxColor& color) { glColor4ub(color.Red(), color.Green(), color.Blue(), color.Alpha()); } void MapDrawer::glColor(MapDrawer::BrushColor color) { switch(color) { case COLOR_BRUSH: glColor4ub( g_settings.getInteger(Config::CURSOR_RED), g_settings.getInteger(Config::CURSOR_GREEN), g_settings.getInteger(Config::CURSOR_BLUE), g_settings.getInteger(Config::CURSOR_ALPHA) ); break; case COLOR_FLAG_BRUSH: case COLOR_HOUSE_BRUSH: glColor4ub( g_settings.getInteger(Config::CURSOR_ALT_RED), g_settings.getInteger(Config::CURSOR_ALT_GREEN), g_settings.getInteger(Config::CURSOR_ALT_BLUE), g_settings.getInteger(Config::CURSOR_ALT_ALPHA) ); break; case COLOR_SPAWN_BRUSH: glColor4ub(166, 0, 0, 128); break; case COLOR_ERASER: glColor4ub(166, 0, 0, 128); break; case COLOR_VALID: glColor4ub(0, 166, 0, 128); break; case COLOR_INVALID: glColor4ub(166, 0, 0, 128); break; default: glColor4ub(255, 255, 255, 128); break; } } void MapDrawer::glColorCheck(Brush* brush, const Position& pos) { if(brush->canDraw(&editor.getMap(), pos)) glColor(COLOR_VALID); else glColor(COLOR_INVALID); } void MapDrawer::drawRect(int x, int y, int w, int h, const wxColor& color, int width) { glLineWidth(width); glColor4ub(color.Red(), color.Green(), color.Blue(), color.Alpha()); glBegin(GL_LINE_STRIP); glVertex2f(x, y); glVertex2f(x + w, y); glVertex2f(x + w, y + h); glVertex2f(x, y + h); glVertex2f(x, y); glEnd(); } void MapDrawer::drawFilledRect(int x, int y, int w, int h, const wxColor& color) { glColor4ub(color.Red(), color.Green(), color.Blue(), color.Alpha()); glBegin(GL_QUADS); glVertex2f(x, y); glVertex2f(x + w, y); glVertex2f(x + w, y + h); glVertex2f(x, y + h); glEnd(); } void MapDrawer::getDrawPosition(const Position& position, int& x, int& y) { int offset; if(position.z <= rme::MapGroundLayer) offset = (rme::MapGroundLayer - position.z) * rme::TileSize; else offset = rme::TileSize * (floor - position.z); x = ((position.x * rme::TileSize) - view_scroll_x) - offset; y = ((position.y * rme::TileSize) - view_scroll_y) - offset; }