#include "subway_map.h" #include "game_data.h" #include "global.h" #include "tracker_state.h" constexpr int AREA_ACTUAL_SIZE = 21; enum class ItemDrawType { kNone, kBox, kOwl }; SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) { map_image_ = wxImage(GetAbsolutePath("assets/subway.png").c_str(), wxBITMAP_TYPE_PNG); if (!map_image_.IsOk()) { return; } owl_image_ = wxImage(GetAbsolutePath("assets/owl.png").c_str(), wxBITMAP_TYPE_PNG); if (!owl_image_.IsOk()) { return; } tree_ = std::make_unique>( quadtree::Box{0, 0, static_cast(map_image_.GetWidth()), static_cast(map_image_.GetHeight())}); for (const SubwayItem &subway_item : GD_GetSubwayItems()) { tree_->add(subway_item.id); } Redraw(); Bind(wxEVT_PAINT, &SubwayMap::OnPaint, this); Bind(wxEVT_MOTION, &SubwayMap::OnMouseMove, this); } void SubwayMap::UpdateIndicators() { Redraw(); } void SubwayMap::OnPaint(wxPaintEvent &event) { if (GetSize() != rendered_.GetSize()) { Redraw(); } wxPaintDC dc(this); dc.DrawBitmap(rendered_, 0, 0); event.Skip(); } void SubwayMap::OnMouseMove(wxMouseEvent &event) { int mouse_x = std::clamp( (event.GetX() - render_x_) * map_image_.GetWidth() / render_width_, 0, map_image_.GetWidth() - 1); int mouse_y = std::clamp( (event.GetY() - render_y_) * map_image_.GetWidth() / render_width_, 0, map_image_.GetHeight() - 1); std::vector hovered = tree_->query( {static_cast(mouse_x), static_cast(mouse_y), 2, 2}); std::optional new_hovered_item; if (!hovered.empty()) { new_hovered_item = hovered[0]; } if (new_hovered_item != hovered_item_) { if (new_hovered_item) { wxLogVerbose("Hovered: %d", *new_hovered_item); } else { wxLogVerbose("Un-hovered: %d", *hovered_item_); } hovered_item_ = new_hovered_item; } event.Skip(); } void SubwayMap::Redraw() { wxSize panel_size = GetSize(); wxSize image_size = map_image_.GetSize(); render_x_ = 0; render_y_ = 0; render_width_ = panel_size.GetWidth(); render_height_ = panel_size.GetHeight(); if (image_size.GetWidth() * panel_size.GetHeight() > panel_size.GetWidth() * image_size.GetHeight()) { render_height_ = (panel_size.GetWidth() * image_size.GetHeight()) / image_size.GetWidth(); render_y_ = (panel_size.GetHeight() - render_height_) / 2; } else { render_width_ = (image_size.GetWidth() * panel_size.GetHeight()) / image_size.GetHeight(); render_x_ = (panel_size.GetWidth() - render_width_) / 2; } rendered_ = wxBitmap( map_image_ .Scale(render_width_, render_height_, wxIMAGE_QUALITY_BILINEAR) .Size(panel_size, {render_x_, render_y_}, 0, 0, 0)); wxMemoryDC dc; dc.SelectObject(rendered_); int real_area_size = render_width_ * AREA_ACTUAL_SIZE / image_size.GetWidth(); if (real_area_size == 0) { real_area_size = 1; } wxBitmap owl_bitmap = wxBitmap(owl_image_.Scale( real_area_size * 1.25, real_area_size * 1.25, wxIMAGE_QUALITY_BILINEAR)); for (const SubwayItem &subway_item : GD_GetSubwayItems()) { ItemDrawType draw_type = ItemDrawType::kNone; const wxBrush *brush_color = wxGREY_BRUSH; if (subway_item.door) { draw_type = ItemDrawType::kBox; if (IsDoorOpen(*subway_item.door)) { if (!subway_item.paintings.empty()) { draw_type = ItemDrawType::kOwl; } else { brush_color = wxGREEN_BRUSH; } } else { brush_color = wxRED_BRUSH; } } else if (!subway_item.paintings.empty()) { draw_type = ItemDrawType::kOwl; } int real_area_x = render_x_ + subway_item.x * render_width_ / image_size.GetWidth(); int real_area_y = render_y_ + subway_item.y * render_width_ / image_size.GetWidth(); if (draw_type == ItemDrawType::kBox) { dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 1)); dc.SetBrush(*brush_color); dc.DrawRectangle({real_area_x, real_area_y}, {real_area_size, real_area_size}); } else if (draw_type == ItemDrawType::kOwl) { dc.DrawBitmap(owl_bitmap, {real_area_x, real_area_y}); } } } quadtree::Box SubwayMap::GetItemBox::operator()(const int& id) const { const SubwayItem &subway_item = GD_GetSubwayItem(id); return {static_cast(subway_item.x), static_cast(subway_item.y), AREA_ACTUAL_SIZE, AREA_ACTUAL_SIZE}; }