about summary refs log tree commit diff stats
path: root/data/maps/the_graveyard/metadata.txtpb
blob: 0bac22260ad66c60c2a81ba1ebd0abd4ea4f796d (plain) (blame)
1
2
3
4
5
6
display_name: "The Graveyard"
# These really shouldn't be shuffled because it would make Black Ending trivial.
excluded_nodes: "Components/Paintings/grave"
excluded_nodes: "Components/Paintings/grave2"
# I'll be real, I have no idea what this is.
excluded_nodes: "Panels/panel_4"
padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#include "area_popup.h"

#include <wx/dcbuffer.h>

#include <algorithm>

#include "ap_state.h"
#include "game_data.h"
#include "global.h"
#include "icons.h"
#include "tracker_config.h"
#include "tracker_panel.h"
#include "tracker_state.h"

AreaPopup::AreaPopup(wxWindow* parent, int area_id)
    : wxScrolledCanvas(parent, wxID_ANY), area_id_(area_id) {
  SetBackgroundStyle(wxBG_STYLE_PAINT);

  LoadIcons();

  // TODO: This is slow on high-DPI screens.
  SetScrollRate(5, 5);

  SetBackgroundColour(*wxBLACK);
  Hide();

  Bind(wxEVT_PAINT, &AreaPopup::OnPaint, this);
  Bind(wxEVT_DPI_CHANGED, &AreaPopup::OnDPIChanged, this);

  UpdateIndicators();
}

void AreaPopup::UpdateIndicators() {
  const MapArea& map_area = GD_GetMapArea(area_id_);

  wxFont the_font = GetFont().Scale(GetDPIScaleFactor());

  // Start calculating extents.
  wxMemoryDC mem_dc;
  mem_dc.SetFont(the_font.Bold());
  wxSize header_extent = mem_dc.GetTextExtent(map_area.name);

  int acc_height = header_extent.GetHeight() + FromDIP(20);
  int col_width = 0;

  mem_dc.SetFont(the_font);

  TrackerPanel* tracker_panel = dynamic_cast<TrackerPanel*>(GetParent());

  std::vector<int> real_locations;

  for (int section_id = 0; section_id < map_area.locations.size();
       section_id++) {
    const Location& location = map_area.locations.at(section_id);
    if (IsLocationPostgame(location.ap_location_id)) {
      continue;
    }

    if (tracker_panel->IsPanelsMode()) {
      if (!location.single_panel) {
        continue;
      }
    } else {
      if (!AP_IsLocationVisible(location.classification) &&
          !(location.hunt && GetTrackerConfig().show_hunt_panels)) {
        continue;
      }
    }

    real_locations.push_back(section_id);

    wxSize item_extent = mem_dc.GetTextExtent(location.name);
    int item_height =
        std::max(FromDIP(32), item_extent.GetHeight()) + FromDIP(10);
    acc_height += item_height;

    if (item_extent.GetWidth() > col_width) {
      col_width = item_extent.GetWidth();
    }
  }

  if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) {
    for (int painting_id : map_area.paintings) {
      if (IsPaintingPostgame(painting_id)) {
        continue;
      }

      const PaintingExit& painting = GD_GetPaintingExit(painting_id);
      wxSize item_extent = mem_dc.GetTextExtent(painting.display_name);
      int item_height =
          std::max(FromDIP(32), item_extent.GetHeight()) + FromDIP(10);
      acc_height += item_height;

      if (item_extent.GetWidth() > col_width) {
        col_width = item_extent.GetWidth();
      }
    }
  }

  int item_width = col_width + FromDIP(10 + 32);
  int full_width = std::max(header_extent.GetWidth(), item_width) + FromDIP(20);

  Fit();
  SetVirtualSize(full_width, acc_height);

  rendered_ = wxBitmap(full_width, acc_height);
  mem_dc.SelectObject(rendered_);
  mem_dc.SetPen(*wxTRANSPARENT_PEN);
  mem_dc.SetBrush(*wxBLACK_BRUSH);
  mem_dc.DrawRectangle({0, 0}, {full_width, acc_height});

  mem_dc.SetFont(the_font.Bold());
  mem_dc.SetTextForeground(*wxWHITE);
  mem_dc.DrawText(map_area.name,
                  {(full_width - header_extent.GetWidth()) / 2, FromDIP(10)});

  int cur_height = header_extent.GetHeight() + FromDIP(20);

  mem_dc.SetFont(the_font);

  for (int section_id : real_locations) {
    const Location& location = map_area.locations.at(section_id);

    bool checked = false;
    if (IsLocationWinCondition(location)) {
      checked = AP_HasReachedGoal();
    } else if (tracker_panel->IsPanelsMode()) {
      const Panel& panel = GD_GetPanel(*location.single_panel);
      if (panel.non_counting) {
        checked = AP_HasCheckedGameLocation(location.ap_location_id);
      } else {
        checked = tracker_panel->GetSolvedPanels().contains(panel.nodepath);
      }
    } else {
      checked =
          AP_HasCheckedGameLocation(location.ap_location_id) ||
          (location.hunt && AP_HasCheckedHuntPanel(location.ap_location_id));
    }

    const wxBitmap* eye_ptr = checked ? checked_eye_ : unchecked_eye_;

    mem_dc.DrawBitmap(*eye_ptr, {FromDIP(10), cur_height});

    bool reachable = IsLocationReachable(location.ap_location_id);
    const wxColour* text_color = reachable ? wxWHITE : wxRED;
    mem_dc.SetTextForeground(*text_color);

    wxSize item_extent = mem_dc.GetTextExtent(location.name);
    mem_dc.DrawText(
        location.name,
                    {FromDIP(10 + 32 + 10),
         cur_height + (FromDIP(32) - mem_dc.GetFontMetrics().height) / 2});

    cur_height += FromDIP(10 + 32);
  }

  if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) {
    for (int painting_id : map_area.paintings) {
      if (IsPaintingPostgame(painting_id)) {
        continue;
      }

      const PaintingExit& painting = GD_GetPaintingExit(painting_id);

      bool reachable = IsPaintingReachable(painting_id);
      const wxColour* text_color = reachable ? wxWHITE : wxRED;
      mem_dc.SetTextForeground(*text_color);

      bool checked = reachable && AP_IsPaintingChecked(painting.internal_id);
      const wxBitmap* eye_ptr = checked ? checked_owl_ : unchecked_owl_;
      mem_dc.DrawBitmap(*eye_ptr, {FromDIP(10), cur_height});

      wxSize item_extent = mem_dc.GetTextExtent(painting.display_name);
      mem_dc.DrawText(painting.display_name,
                      {FromDIP(10 + 32 + 10),
           cur_height + (FromDIP(32) - mem_dc.GetFontMetrics().height) / 2});

      cur_height += FromDIP(10 + 32);
    }
  }
}

void AreaPopup::OnPaint(wxPaintEvent& event) {
  wxBufferedPaintDC dc(this);
  PrepareDC(dc);
  dc.DrawBitmap(rendered_, 0, 0);

  event.Skip();
}

void AreaPopup::OnDPIChanged(wxDPIChangedEvent& event) {
  LoadIcons();
  UpdateIndicators();

  event.Skip();
}

void AreaPopup::LoadIcons() {
  unchecked_eye_ = GetTheIconCache().GetIcon("assets/unchecked.png",
                                             FromDIP(wxSize{32, 32}));
  checked_eye_ =
      GetTheIconCache().GetIcon("assets/checked.png", FromDIP(wxSize{32, 32}));
  unchecked_owl_ =
      GetTheIconCache().GetIcon("assets/owl.png", FromDIP(wxSize{32, 32}));
  checked_owl_ = GetTheIconCache().GetIcon("assets/checked_owl.png",
                                           FromDIP(wxSize{32, 32}));
}