diff --git a/Source/automap.cpp b/Source/automap.cpp index eabf18b717b..0ee349f02e7 100644 --- a/Source/automap.cpp +++ b/Source/automap.cpp @@ -15,7 +15,9 @@ #include "engine/render/automap_render.hpp" #include "levels/gendung.h" #include "levels/setmaps.h" +#include "missiles.h" #include "player.h" +#include "towners.h" #include "utils/language.h" #include "utils/stdcompat/algorithm.hpp" #include "utils/ui_fwd.h" @@ -39,6 +41,20 @@ enum MapColors : uint8_t { MapColorsDim = (PAL16_YELLOW + 8), /** color for items on automap */ MapColorsItem = (PAL8_BLUE + 1), + /** color for objects on automap */ + MapColorsObject = (PAL8_ORANGE + 1), + /** color for Town Portal on automap */ + MapColorsPortal = (PAL8_BLUE + 1), + /** color for Red Portal on automap */ + MapColorsRedPortal = (PAL8_RED + 1), + /** color for towners on automap */ + MapColorsTowner = (PAL16_GRAY + 15), + /** color for golems on automap */ + MapColorsGolem = (PAL16_GRAY + 4), + /** color for berserked monster on automap */ + MapColorsBerserk = (PAL8_YELLOW + 4), + /** color for berserked monster on automap */ + MapColorsDead = (PAL8_RED + 2), }; struct AutomapTile { @@ -102,7 +118,7 @@ struct AutomapTile { */ std::array AutomapTypeTiles; -void DrawDiamond(const Surface &out, Point center, uint8_t color) +void DrawMapDiamond(const Surface &out, Point center, uint8_t color) { const Point left { center.x - AmLine(16), center.y }; const Point top { center.x, center.y - AmLine(8) }; @@ -114,16 +130,32 @@ void DrawDiamond(const Surface &out, Point center, uint8_t color) DrawMapLineNE(out, bottom, AmLine(8), color); } +void DrawMapCross(const Surface &out, Point center, uint8_t color) +{ + DrawMapLineNE(out, { center.x, center.y - AmLine(8) }, AmLine(8), color); + DrawMapLineNE(out, { center.x - AmLine(32), center.y + AmLine(8) }, AmLine(8), color); + DrawMapLineNE(out, { center.x - AmLine(32), center.y - AmLine(8) }, AmLine(8), color); + DrawMapLineSE(out, { center.x + AmLine(16), center.y - AmLine(16) }, AmLine(8), color); + DrawMapLineSE(out, { center.x - AmLine(32), center.y + AmLine(8) }, AmLine(8), color); + DrawMapLineSE(out, { center.x - AmLine(32), center.y - AmLine(8) }, AmLine(8), color); + DrawMapLineSE(out, { center.x - AmLine(16), center.y - AmLine(16) }, AmLine(8), color); + DrawMapLineSE(out, { center.x, center.y + AmLine(8) }, AmLine(8), color); + DrawMapLineSE(out, { center.x + AmLine(16), center.y }, AmLine(8), color); + DrawMapLineNE(out, { center.x - AmLine(16), center.y + AmLine(16) }, AmLine(8), color); + DrawMapLineNE(out, { center.x + AmLine(16), center.y }, AmLine(8), color); + DrawMapLineNE(out, { center.x + AmLine(16), center.y + AmLine(16) }, AmLine(8), color); +} + void DrawMapVerticalDoor(const Surface &out, Point center, uint8_t colorBright, uint8_t colorDim) { if (leveltype != DTYPE_CATACOMBS) { DrawMapLineNE(out, { center.x + AmLine(8), center.y - AmLine(4) }, AmLine(4), colorDim); DrawMapLineNE(out, { center.x - AmLine(16), center.y + AmLine(8) }, AmLine(4), colorDim); - DrawDiamond(out, center, colorBright); + DrawMapDiamond(out, center, colorBright); } else { DrawMapLineNE(out, { center.x - AmLine(8), center.y + AmLine(4) }, AmLine(8), colorDim); DrawMapLineNE(out, { center.x - AmLine(16), center.y + AmLine(8) }, AmLine(4), colorDim); - DrawDiamond(out, { center.x + AmLine(16), center.y - AmLine(8) }, colorBright); + DrawMapDiamond(out, { center.x + AmLine(16), center.y - AmLine(8) }, colorBright); } } @@ -132,226 +164,326 @@ void DrawMapHorizontalDoor(const Surface &out, Point center, uint8_t colorBright if (leveltype != DTYPE_CATACOMBS) { DrawMapLineSE(out, { center.x - AmLine(16), center.y - AmLine(8) }, AmLine(4), colorDim); DrawMapLineSE(out, { center.x + AmLine(8), center.y + AmLine(4) }, AmLine(4), colorDim); - DrawDiamond(out, center, colorBright); + DrawMapDiamond(out, center, colorBright); } else { DrawMapLineSE(out, { center.x - AmLine(8), center.y - AmLine(4) }, AmLine(8), colorDim); DrawMapLineSE(out, { center.x + AmLine(8), center.y + AmLine(4) }, AmLine(4), colorDim); - DrawDiamond(out, { center.x - AmLine(16), center.y - AmLine(8) }, colorBright); + DrawMapDiamond(out, { center.x - AmLine(16), center.y - AmLine(8) }, colorBright); } } void DrawDirt(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x + AmLine(8) - AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8) - AmLine(32), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(16), center.y }, color); - out.SetPixel({ center.x - AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x, center.y - AmLine(8) }, color); - out.SetPixel(center, color); - out.SetPixel({ center.x, center.y + AmLine(8) }, color); - out.SetPixel({ center.x, center.y + AmLine(16) }, color); + SetMapPixel(out, { center.x, center.y - AmLine(8) }, color); + SetMapPixel(out, center, color); + SetMapPixel(out, { center.x, center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x, center.y + AmLine(16) }, color); - out.SetPixel({ center.x + AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(16), center.y }, color); - out.SetPixel({ center.x + AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); } void DrawBridge(const Surface &out, Point center, uint8_t color) { - out.SetPixel(center, color); + SetMapPixel(out, center, color); - out.SetPixel({ center.x + AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(16), center.y }, color); - out.SetPixel({ center.x + AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); } void DrawRiverRightIn(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x - AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel(center, color); - out.SetPixel({ center.x, center.y + AmLine(8) }, color); - out.SetPixel({ center.x, center.y + AmLine(16) }, color); + SetMapPixel(out, center, color); + SetMapPixel(out, { center.x, center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x, center.y + AmLine(16) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(16), center.y }, color); - out.SetPixel({ center.x + AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); } void DrawRiverCornerSouth(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x, center.y + AmLine(16) }, color); + SetMapPixel(out, { center.x, center.y + AmLine(16) }, color); } void DrawRiverCornerNorth(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x - AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x, center.y - AmLine(8) }, color); - out.SetPixel({ center.x + AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x, center.y - AmLine(8) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y - AmLine(4) }, color); } void DrawRiverLeftOut(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x + AmLine(8) - AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8) - AmLine(32), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(16), center.y }, color); - out.SetPixel({ center.x - AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel(center, color); - out.SetPixel({ center.x, center.y + AmLine(8) }, color); + SetMapPixel(out, center, color); + SetMapPixel(out, { center.x, center.y + AmLine(8) }, color); - out.SetPixel({ center.x + AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(16), center.y }, color); - out.SetPixel({ center.x + AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); } void DrawRiverLeftIn(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x - AmLine(16), center.y }, color); - out.SetPixel({ center.x - AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x, center.y - AmLine(8) }, color); - out.SetPixel(center, color); - out.SetPixel({ center.x, center.y + AmLine(8) }, color); - out.SetPixel({ center.x, center.y + AmLine(16) }, color); + SetMapPixel(out, { center.x, center.y - AmLine(8) }, color); + SetMapPixel(out, center, color); + SetMapPixel(out, { center.x, center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x, center.y + AmLine(16) }, color); - out.SetPixel({ center.x + AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); } void DrawRiverCornerWest(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x + AmLine(8) - AmLine(32), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(8) - AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y }, color); } void DrawRiverCornerEast(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x + AmLine(16), center.y }, color); - out.SetPixel({ center.x + AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); } void DrawRiverRightOut(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel(center, color); - out.SetPixel({ center.x, center.y + AmLine(8) }, color); - out.SetPixel({ center.x, center.y + AmLine(16) }, color); + SetMapPixel(out, center, color); + SetMapPixel(out, { center.x, center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x, center.y + AmLine(16) }, color); - out.SetPixel({ center.x + AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(16), center.y }, color); - out.SetPixel({ center.x + AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); } void DrawRiver(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x - AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel(center, color); - out.SetPixel({ center.x, center.y + AmLine(8) }, color); - out.SetPixel({ center.x, center.y + AmLine(16) }, color); + SetMapPixel(out, center, color); + SetMapPixel(out, { center.x, center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x, center.y + AmLine(16) }, color); - out.SetPixel({ center.x + AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(16), center.y }, color); - out.SetPixel({ center.x + AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); } void DrawRiverForkIn(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x - AmLine(16), center.y }, color); - out.SetPixel({ center.x - AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x, center.y - AmLine(8) }, color); - out.SetPixel(center, color); - out.SetPixel({ center.x, center.y + AmLine(8) }, color); - out.SetPixel({ center.x, center.y + AmLine(16) }, color); + SetMapPixel(out, { center.x, center.y - AmLine(8) }, color); + SetMapPixel(out, center, color); + SetMapPixel(out, { center.x, center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x, center.y + AmLine(16) }, color); - out.SetPixel({ center.x + AmLine(8), center.y - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y - AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x + AmLine(16), center.y }, color); - out.SetPixel({ center.x + AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y }, color); + SetMapPixel(out, { center.x + AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8) + AmLine(32), center.y + AmLine(4) }, color); } void DrawRiverForkOut(const Surface &out, Point center, uint8_t color) { - out.SetPixel({ center.x + AmLine(8) - AmLine(32), center.y + AmLine(4) }, color); + SetMapPixel(out, { center.x + AmLine(8) - AmLine(32), center.y + AmLine(4) }, color); - out.SetPixel({ center.x - AmLine(16), center.y }, color); - out.SetPixel({ center.x - AmLine(16), center.y + AmLine(8) }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y }, color); + SetMapPixel(out, { center.x - AmLine(16), center.y + AmLine(8) }, color); - out.SetPixel({ center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); + SetMapPixel(out, { center.x - AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); - out.SetPixel({ center.x, center.y + AmLine(16) }, color); + SetMapPixel(out, { center.x, center.y + AmLine(16) }, color); + + SetMapPixel(out, { center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); +} - out.SetPixel({ center.x + AmLine(8), center.y + AmLine(16) - AmLine(4) }, color); +enum class StairsType : uint8_t { + Invalid, + DownRight, + DownLeft, + UpRight, + UpLeft, +}; + +StairsType GetStairsType(uint8_t tileId, uint8_t dlvl) +{ + if (dlvl == 0) + return StairsType::DownRight; + // clang-format off + switch (tileId) { + // Church + case 57: + return StairsType::DownRight; + case 66: + return StairsType::UpRight; + // Catacombs + case 78: + return StairsType::DownRight; + case 77: + case 160: + return StairsType::UpRight; // Shortcut + // Caves + case 47: + return StairsType::DownLeft; + case 48: + return StairsType::DownRight; + case 51: + case 153: + return StairsType::UpRight; // Shortcut + // Hell + case 43: + return StairsType::DownLeft; + case 33: + case 131: + return StairsType::UpRight; // Shortcut + // Hive + case 16: + return StairsType::DownLeft; + case 17: + case 21: + return StairsType::UpRight; + // Crypt + case 46: + return StairsType::DownRight; + case 56: + case 64: + return StairsType::UpRight; + default: + return StairsType::Invalid; + } + // clang-format on } -void DrawStairs(const Surface &out, Point center, uint8_t color) +void DrawStairs(const Surface &out, Point center, uint8_t color, StairsType type) { constexpr int NumStairSteps = 4; - const Displacement offset = { -AmLine(8), AmLine(4) }; - Point p = { center.x - AmLine(8), center.y - AmLine(8) - AmLine(4) }; + + Displacement stairsOffset = { 0, 0 }; + + if (type == StairsType::DownRight) { + stairsOffset = Displacement { AmLine(24), AmLine(12) }; + } else if (type == StairsType::DownLeft) { + stairsOffset = Displacement { -AmLine(24), AmLine(12) }; + } + + int lineLength = 16; + + if (IsAnyOf(type, StairsType::DownRight, StairsType::DownLeft)) + lineLength = 4; + + Displacement offset = { 0, 0 }; + Point p; + + if (IsAnyOf(type, StairsType::DownRight, StairsType::UpRight)) { + offset = { -AmLine(8), AmLine(4) }; + p = { center.x - AmLine(8) + stairsOffset.deltaX, center.y - AmLine(8) - AmLine(4) + stairsOffset.deltaY }; + } else if (IsAnyOf(type, StairsType::DownLeft, StairsType::UpLeft)) { + offset = { AmLine(8), AmLine(4) }; + p = { center.x + AmLine(8) + stairsOffset.deltaX, center.y - AmLine(8) - AmLine(4) + stairsOffset.deltaY }; + } + for (int i = 0; i < NumStairSteps; ++i) { - DrawMapLineSE(out, p, AmLine(16), color); + if (IsAnyOf(type, StairsType::DownRight, StairsType::UpRight)) + DrawMapLineSE(out, p, AmLine(lineLength), color); + else if (IsAnyOf(type, StairsType::DownLeft, StairsType::UpLeft)) + DrawMapLineSW(out, p, AmLine(lineLength), color); + + if (i != NumStairSteps - 1) { + if (type == StairsType::DownRight) + DrawMapLineSW(out, p + Displacement { 1, 0 }, AmLine(4), color); + else if (type == StairsType::DownLeft) + DrawMapLineSE(out, p + Displacement { -1, 0 }, AmLine(4), color); + } + p += offset; + + if (type == StairsType::DownRight) { + p -= Displacement { AmLine(8), AmLine(4) }; + } else if (type == StairsType::DownLeft) { + p -= Displacement { -AmLine(8), AmLine(4) }; + } + + if (IsAnyOf(type, StairsType::DownRight, StairsType::DownLeft)) + lineLength += 4; } } @@ -369,9 +501,9 @@ void DrawHorizontal(const Surface &out, Point center, AutomapTile tile, uint8_t } if (tile.HasFlag(AutomapTile::Flags::HorizontalGrate)) { DrawMapLineSE(out, { center.x + AmLine(16), center.y - AmLine(8) }, AmLine(8), colorDim); - DrawDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); + DrawMapDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); } else if (tile.HasFlag(AutomapTile::Flags::HorizontalArch)) { - DrawDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); + DrawMapDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); } } @@ -389,9 +521,9 @@ void DrawVertical(const Surface &out, Point center, AutomapTile tile, uint8_t co } if (tile.HasFlag(AutomapTile::Flags::VerticalGrate)) { // right-facing half-wall DrawMapLineNE(out, { center.x - AmLine(32), center.y }, AmLine(8), colorDim); - DrawDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); + DrawMapDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); } else if (tile.HasFlag(AutomapTile::Flags::VerticalArch)) { // window or passable column - DrawDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); + DrawMapDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); } } @@ -514,12 +646,20 @@ void DrawAutomapTile(const Surface &out, Point center, Point map) } if (tile.HasFlag(AutomapTile::Flags::Stairs)) { - DrawStairs(out, center, colorBright); + DrawStairs(out, center, colorBright, GetStairsType(dungeon[map.x][map.y], currlevel)); + } + + if (currlevel == Quests[Q_BETRAYER]._qlevel && map == Quests[Q_BETRAYER].position.worldToMega() + Displacement { 1, 1 }) { + int pentaColor = (Quests[Q_BETRAYER]._qactive == QUEST_DONE) ? PAL8_RED + 2 : PAL16_YELLOW + 8; + DrawMapEllipse(out, center + Displacement { 0, 1 }, AmLine(64), 0); // shadow + DrawMapStar(out, center + Displacement { 0, 1 }, AmLine(64), 0); // shadow + DrawMapEllipse(out, center, AmLine(64), pentaColor); + DrawMapStar(out, center, AmLine(64), pentaColor); } switch (tile.type) { case AutomapTile::Types::Diamond: // stand-alone column or other unpassable object - DrawDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); + DrawMapDiamond(out, { center.x, center.y - AmLine(8) }, colorDim); break; case AutomapTile::Types::Vertical: case AutomapTile::Types::FenceVertical: @@ -593,10 +733,44 @@ void DrawAutomapTile(const Surface &out, Point center, Point map) } } +Point GetAutomapScreen(const int i, const int j, const Displacement &myPlayerOffset) +{ + int px = i - 2 * AutomapOffset.deltaX - ViewPosition.x; + int py = j - 2 * AutomapOffset.deltaY - ViewPosition.y; + + Point screenOffset { + gnScreenWidth / 2, + (gnScreenHeight - GetMainPanel().size.height) / 2 + }; + + if (AutomapMini) { + screenOffset = { + AutomapMiniRect.position.x + AutomapMiniRect.size.width / 2, + AutomapMiniRect.position.y + AutomapMiniRect.size.height / 2 + }; + } + + Point screen = { + (myPlayerOffset.deltaX * AutoMapScale / 100 / 2) + (px - py) * AmLine(16) + screenOffset.x, + (myPlayerOffset.deltaY * AutoMapScale / 100 / 2) + (px + py) * AmLine(8) + screenOffset.y + }; + + if (CanPanelsCoverView()) { + if (IsRightPanelOpen()) + screen.x -= 160; + if (IsLeftPanelOpen()) + screen.x += 160; + } + screen.y -= AmLine(8); + + return screen; +} + void SearchAutomapItem(const Surface &out, const Displacement &myPlayerOffset, int searchRadius, tl::function_ref highlightTile) { const Player &player = *MyPlayer; Point tile = player.position.tile; + if (player._pmode == PM_WALK_SIDEWAYS) { tile = player.position.future; if (player._pdir == Direction::West) @@ -616,49 +790,91 @@ void SearchAutomapItem(const Surface &out, const Displacement &myPlayerOffset, i if (!highlightTile({ i, j })) continue; - int px = i - 2 * AutomapOffset.deltaX - ViewPosition.x; - int py = j - 2 * AutomapOffset.deltaY - ViewPosition.y; + DrawMapDiamond(out, GetAutomapScreen(i, j, myPlayerOffset), MapColorsItem); + } + } +} - Point screen = { - (myPlayerOffset.deltaX * AutoMapScale / 100 / 2) + (px - py) * AmLine(16) + gnScreenWidth / 2, - (myPlayerOffset.deltaY * AutoMapScale / 100 / 2) + (px + py) * AmLine(8) + (gnScreenHeight - GetMainPanel().size.height) / 2 - }; +void DrawAutomapObject(const Surface &out, const Displacement &myPlayerOffset, tl::function_ref highlightTile) +{ + for (int i = 0; i < MAXDUNX; i++) { + for (int j = 0; j < MAXDUNY; j++) { + MapExplorationType explorationType = static_cast(AutomapView[clamp(((i - 16) / 2), 0, DMAXX - 1)][clamp(((j - 16) / 2), 0, DMAXY - 1)]); - if (CanPanelsCoverView()) { - if (IsRightPanelOpen()) - screen.x -= 160; - if (IsLeftPanelOpen()) - screen.x += 160; - } - screen.y -= AmLine(8); - DrawDiamond(out, screen, MapColorsItem); + if (!highlightTile({ i, j }) || explorationType == MAP_EXP_NONE || !IsAnyOf(ObjectAtPosition({ i, j })._otype, OBJ_BLINDBOOK, OBJ_BLOODBOOK, OBJ_BOOK2R, OBJ_CRUX1, OBJ_CRUX2, OBJ_CRUX3, OBJ_L5BOOKS, OBJ_L5LEVER, OBJ_LAZSTAND, OBJ_LEVER, OBJ_MCIRCLE1, OBJ_MCIRCLE2, OBJ_MUSHPATCH, OBJ_PEDESTAL, OBJ_SIGNCHEST, OBJ_SLAINHERO, OBJ_STAND, OBJ_STORYBOOK, OBJ_SWITCHSKL, OBJ_WARARMOR, OBJ_WARWEAP)) + continue; + + DrawMapDiamond(out, GetAutomapScreen(i, j, myPlayerOffset), MapColorsObject); } } } -/** - * @brief Renders an arrow on the automap, centered on and facing the direction of the player. - */ -void DrawAutomapPlr(const Surface &out, const Displacement &myPlayerOffset, int playerId) +void DrawAutomapMissile(const Surface &out, const Displacement &myPlayerOffset) { - int playerColor = MapColorsPlayer + (8 * playerId) % 128; + for (int i = 0; i < MAXDUNX; i++) { + for (int j = 0; j < MAXDUNY; j++) { + MapExplorationType explorationType = static_cast(AutomapView[clamp(((i - 16) / 2), 0, DMAXX - 1)][clamp(((j - 16) / 2), 0, DMAXY - 1)]); + Missile *portal = nullptr; + + for (auto &m : Missiles) { + if (IsAnyOf(m._mitype, MissileID::TownPortal, MissileID::RedPortal) && m.position.tile == Point { i, j }) { + portal = &m; + } + } + if (portal == nullptr || (explorationType == MAP_EXP_NONE && portal->_mitype == MissileID::RedPortal)) + continue; - Player &player = Players[playerId]; - Point tile = player.position.tile; - if (player._pmode == PM_WALK_SIDEWAYS) { - tile = player.position.future; + DrawMapEllipse(out, GetAutomapScreen(i, j, myPlayerOffset), AmLine(16), portal->_mitype == MissileID::TownPortal ? MapColorsPortal : MapColorsRedPortal); + } } +} + +Displacement GetAutomapWalkingOffset(const Player &player) +{ + Displacement offset = {}; + if (player.isWalking()) + offset = GetOffsetForWalking(player.AnimInfo, player._pdir); + return offset; +} + +Displacement GetAutomapWalkingOffset(const Monster *monster) +{ + Displacement offset = {}; + + if (monster->isWalking()) + offset = GetOffsetForWalking(monster->animInfo, monster->direction); + return offset; +} + +Displacement GetAutomapWalkingOffset(const Towner &towner) +{ + return {}; +} + +template +void DrawAutomapArrow(const Surface &out, const EntityType &entity, const Displacement &myPlayerOffset, const Point &tile, const Direction &dir, const int col, const int deadCol, const bool entityIsAlive) +{ int px = tile.x - 2 * AutomapOffset.deltaX - ViewPosition.x; int py = tile.y - 2 * AutomapOffset.deltaY - ViewPosition.y; - Displacement playerOffset = {}; - if (player.isWalking()) - playerOffset = GetOffsetForWalking(player.AnimInfo, player._pdir); + Displacement offset = GetAutomapWalkingOffset(entity); + + Point screen { + gnScreenWidth / 2, + (gnScreenHeight - GetMainPanel().size.height) / 2 + }; + + if (AutomapMini) { + screen = { + AutomapMiniRect.position.x + AutomapMiniRect.size.width / 2, + AutomapMiniRect.position.y + AutomapMiniRect.size.height / 2 + }; + } Point base = { - ((playerOffset.deltaX + myPlayerOffset.deltaX) * AutoMapScale / 100 / 2) + (px - py) * AmLine(16) + gnScreenWidth / 2, - ((playerOffset.deltaY + myPlayerOffset.deltaY) * AutoMapScale / 100 / 2) + (px + py) * AmLine(8) + (gnScreenHeight - GetMainPanel().size.height) / 2 + ((offset.deltaX + myPlayerOffset.deltaX) * AutoMapScale / 100 / 2) + (px - py) * AmLine(16) + screen.x, + ((offset.deltaY + myPlayerOffset.deltaY) * AutoMapScale / 100 / 2) + (px + py) * AmLine(8) + screen.y }; if (CanPanelsCoverView()) { @@ -669,60 +885,150 @@ void DrawAutomapPlr(const Surface &out, const Displacement &myPlayerOffset, int } base.y -= AmLine(16); - switch (player._pdir) { - case Direction::North: { - const Point point { base.x, base.y - AmLine(16) }; - DrawVerticalLine(out, point, AmLine(16), playerColor); - DrawMapLineSteepNE(out, { point.x - AmLine(4), point.y + 2 * AmLine(4) }, AmLine(4), playerColor); - DrawMapLineSteepNW(out, { point.x + AmLine(4), point.y + 2 * AmLine(4) }, AmLine(4), playerColor); - } break; - case Direction::NorthEast: { - const Point point { base.x + AmLine(16), base.y - AmLine(8) }; - DrawHorizontalLine(out, { point.x - AmLine(8), point.y }, AmLine(8), playerColor); - DrawMapLineNE(out, { point.x - 2 * AmLine(8), point.y + AmLine(8) }, AmLine(8), playerColor); - DrawMapLineSteepSW(out, point, AmLine(4), playerColor); - } break; - case Direction::East: { - const Point point { base.x + AmLine(16), base.y }; - DrawMapLineNW(out, point, AmLine(4), playerColor); - DrawHorizontalLine(out, { point.x - AmLine(16), point.y }, AmLine(16), playerColor); - DrawMapLineSW(out, point, AmLine(4), playerColor); - } break; - case Direction::SouthEast: { - const Point point { base.x + AmLine(16), base.y + AmLine(8) }; - DrawMapLineSteepNW(out, point, AmLine(4), playerColor); - DrawMapLineSE(out, { point.x - 2 * AmLine(8), point.y - AmLine(8) }, AmLine(8), playerColor); - DrawHorizontalLine(out, { point.x - (AmLine(8) + 1), point.y }, AmLine(8) + 1, playerColor); - } break; - case Direction::South: { - const Point point { base.x, base.y + AmLine(16) }; - DrawVerticalLine(out, { point.x, point.y - AmLine(16) }, AmLine(16), playerColor); - DrawMapLineSteepSW(out, { point.x + AmLine(4), point.y - 2 * AmLine(4) }, AmLine(4), playerColor); - DrawMapLineSteepSE(out, { point.x - AmLine(4), point.y - 2 * AmLine(4) }, AmLine(4), playerColor); - } break; - case Direction::SouthWest: { - const Point point { base.x - AmLine(16), base.y + AmLine(8) }; - DrawMapLineSteepNE(out, point, AmLine(4), playerColor); - DrawMapLineSW(out, { point.x + 2 * AmLine(8), point.y - AmLine(8) }, AmLine(8), playerColor); - DrawHorizontalLine(out, point, AmLine(8) + 1, playerColor); - } break; - case Direction::West: { - const Point point { base.x - AmLine(16), base.y }; - DrawMapLineNE(out, point, AmLine(4), playerColor); - DrawHorizontalLine(out, point, AmLine(16) + 1, playerColor); - DrawMapLineSE(out, point, AmLine(4), playerColor); - } break; - case Direction::NorthWest: { - const Point point { base.x - AmLine(16), base.y - AmLine(8) }; - DrawMapLineNW(out, { point.x + 2 * AmLine(8), point.y + AmLine(8) }, AmLine(8), playerColor); - DrawHorizontalLine(out, point, AmLine(8) + 1, playerColor); - DrawMapLineSteepSE(out, point, AmLine(4), playerColor); - } break; - case Direction::NoDirection: - break; + if (entityIsAlive) { + switch (dir) { + case Direction::North: { + const Point point { base.x, base.y - AmLine(16) }; + DrawVerticalLine(out, point, AmLine(16), col); + DrawMapLineSteepNE(out, { point.x - AmLine(4), point.y + 2 * AmLine(4) }, AmLine(4), col); + DrawMapLineSteepNW(out, { point.x + AmLine(4), point.y + 2 * AmLine(4) }, AmLine(4), col); + } break; + case Direction::NorthEast: { + const Point point { base.x + AmLine(16), base.y - AmLine(8) }; + DrawHorizontalLine(out, { point.x - AmLine(8), point.y }, AmLine(8), col); + DrawMapLineNE(out, { point.x - 2 * AmLine(8), point.y + AmLine(8) }, AmLine(8), col); + DrawMapLineSteepSW(out, point, AmLine(4), col); + } break; + case Direction::East: { + const Point point { base.x + AmLine(16), base.y }; + DrawMapLineNW(out, point, AmLine(4), col); + DrawHorizontalLine(out, { point.x - AmLine(16), point.y }, AmLine(16), col); + DrawMapLineSW(out, point, AmLine(4), col); + } break; + case Direction::SouthEast: { + const Point point { base.x + AmLine(16), base.y + AmLine(8) }; + DrawMapLineSteepNW(out, point, AmLine(4), col); + DrawMapLineSE(out, { point.x - 2 * AmLine(8), point.y - AmLine(8) }, AmLine(8), col); + DrawHorizontalLine(out, { point.x - (AmLine(8) + 1), point.y }, AmLine(8) + 1, col); + } break; + case Direction::South: { + const Point point { base.x, base.y + AmLine(16) }; + DrawVerticalLine(out, { point.x, point.y - AmLine(16) }, AmLine(16), col); + DrawMapLineSteepSW(out, { point.x + AmLine(4), point.y - 2 * AmLine(4) }, AmLine(4), col); + DrawMapLineSteepSE(out, { point.x - AmLine(4), point.y - 2 * AmLine(4) }, AmLine(4), col); + } break; + case Direction::SouthWest: { + const Point point { base.x - AmLine(16), base.y + AmLine(8) }; + DrawMapLineSteepNE(out, point, AmLine(4), col); + DrawMapLineSW(out, { point.x + 2 * AmLine(8), point.y - AmLine(8) }, AmLine(8), col); + DrawHorizontalLine(out, point, AmLine(8) + 1, col); + } break; + case Direction::West: { + const Point point { base.x - AmLine(16), base.y }; + DrawMapLineNE(out, point, AmLine(4), col); + DrawHorizontalLine(out, point, AmLine(16) + 1, col); + DrawMapLineSE(out, point, AmLine(4), col); + } break; + case Direction::NorthWest: { + const Point point { base.x - AmLine(16), base.y - AmLine(8) }; + DrawMapLineNW(out, { point.x + 2 * AmLine(8), point.y + AmLine(8) }, AmLine(8), col); + DrawHorizontalLine(out, point, AmLine(8) + 1, col); + DrawMapLineSteepSE(out, point, AmLine(4), col); + } break; + case Direction::NoDirection: + break; + } + } else { + const Point point { base.x, base.y }; + DrawMapLineNE(out, { point.x - AmLine(8), point.y }, AmLine(8), deadCol); + DrawMapLineNW(out, { point.x + AmLine(8), point.y }, AmLine(8), deadCol); } } +/** + * @brief Renders an arrow on the automap, centered on and facing the direction of the towner. + */ +void DrawAutomapTowner(const Surface &out, const Displacement &myPlayerOffset) +{ + for (auto &towner : Towners) { + bool townerAlive = towner._tAnimLen != 1; + Point tile = towner.position; + + Direction townerDir; + switch (towner._ttype) { + case TOWN_BMAID: + case TOWN_DEADGUY: + case TOWN_DRUNK: + case TOWN_HEALER: + townerDir = Direction::SouthEast; + break; + case TOWN_PEGBOY: + case TOWN_STORY: + case TOWN_WITCH: + townerDir = Direction::South; + break; + case TOWN_FARMER: + case TOWN_GIRL: + case TOWN_SMITH: + case TOWN_TAVERN: + townerDir = Direction::SouthWest; + break; + default: + townerDir = Direction::NoDirection; + break; + } + + DrawAutomapArrow(out, towner, myPlayerOffset, tile, townerDir, MapColorsTowner, MapColorsDead, townerAlive); + } +} + +/** + * @brief Renders an arrow on the automap, centered on and facing the direction of the minion. + */ +void DrawAutomapMinion(const Surface &out, const Displacement &myPlayerOffset) +{ + for (int i = 0; i < MAXDUNX; i++) { + for (int j = 0; j < MAXDUNY; j++) { + MapExplorationType explorationType = static_cast(AutomapView[clamp(((i - 16) / 2), 0, DMAXX - 1)][clamp(((j - 16) / 2), 0, DMAXY - 1)]); + auto *monster = FindMonsterAtPosition({ i, j }); + if (monster == nullptr) + continue; + if ((monster->flags & (MFLAG_BERSERK | MFLAG_GOLEM)) == 0) + continue; + + int monsterColor = (monster->type().type == MT_GOLEM) ? MapColorsGolem : MapColorsBerserk; + bool monsterAlive = monster->hitPoints > 0; + Point tile = monster->position.tile; + + if (monster->position.tile == GolemHoldingCell) + return; + + if (monster->mode == MonsterMode::MoveSideways) { + tile = monster->position.future; + } + + DrawAutomapArrow(out, monster, myPlayerOffset, tile, monster->direction, monsterColor, monsterColor, monsterAlive); + } + } +} + +/** + * @brief Renders an arrow on the automap, centered on and facing the direction of the player. + */ +void DrawAutomapPlr(const Surface &out, const Displacement &myPlayerOffset, int playerId) +{ + Player &player = Players[playerId]; + bool plrAlive = player._pHitPoints > 0; + int playerColor = MapColorsPlayer + (8 * playerId) % 128; + Point tile = player.position.tile; + + if (player._pmode == PM_WALK_SIDEWAYS) { + tile = player.position.future; + } + + DrawAutomapArrow(out, player, myPlayerOffset, tile, player._pdir, playerColor, MapColorsDead, plrAlive); +} + /** * @brief Renders game info, such as the name of the current level, and in multi player the name of the game and the game password. */ @@ -816,14 +1122,23 @@ std::unique_ptr LoadAutomapData(size_t &tileCount) } // namespace bool AutomapActive; +bool AutomapMini; +bool AutomapTransparent; uint8_t AutomapView[DMAXX][DMAXY]; int AutoMapScale; Displacement AutomapOffset; +Rectangle AutomapMiniRect {}; void InitAutomapOnce() { AutomapActive = false; + AutomapMini = false; + AutomapTransparent = true; AutoMapScale = 50; + int minimapWidth = gnScreenWidth / 4; + Size minimapSize { minimapWidth, minimapWidth / 2 }; + int minimapPadding = 8; + AutomapMiniRect = Rectangle { { gnScreenWidth - minimapPadding - minimapSize.width, minimapPadding }, minimapSize }; } void InitAutomap() @@ -845,6 +1160,23 @@ void StartAutomap() { AutomapOffset = { 0, 0 }; AutomapActive = true; + AutomapTransparent = true; + AutoMapScale = 50; +} + +void StartMinimap() +{ + AutomapOffset = { 0, 0 }; + AutomapMini = true; + AutomapTransparent = false; + if (gnScreenHeight >= 0 && gnScreenHeight < 960) + AutoMapScale = 25; + else if (gnScreenHeight >= 960 && gnScreenHeight < 1440) + AutoMapScale = 50; + else if (gnScreenHeight >= 1440 && gnScreenHeight < 1920) + AutoMapScale = 75; + else + AutoMapScale = 100; } void AutomapUp() @@ -873,23 +1205,24 @@ void AutomapRight() void AutomapZoomIn() { - if (AutoMapScale >= 200) + if (AutoMapScale >= 400) return; - AutoMapScale += 5; + AutoMapScale += 25; } void AutomapZoomOut() { - if (AutoMapScale <= 50) + if (AutoMapScale <= 25) return; - AutoMapScale -= 5; + AutoMapScale -= 25; } void DrawAutomap(const Surface &out) { Automap = { (ViewPosition.x - 8) / 2, (ViewPosition.y - 8) / 2 }; + if (leveltype != DTYPE_TOWN) { Automap += { -4, -4 }; } @@ -924,6 +1257,19 @@ void DrawAutomap(const Surface &out) gnScreenWidth / 2, (gnScreenHeight - GetMainPanel().size.height) / 2 }; + + if (AutomapMini) { + screen = { + AutomapMiniRect.position.x + AutomapMiniRect.size.width / 2, + AutomapMiniRect.position.y + AutomapMiniRect.size.height / 2 + }; + DrawHalfTransparentRectTo(out, AutomapMiniRect.position.x, AutomapMiniRect.position.y, AutomapMiniRect.size.width, AutomapMiniRect.size.height); + DrawHorizontalLine(out, AutomapMiniRect.position + Displacement { -1, -1 }, AutomapMiniRect.size.width + 2, MapColorsDim); + DrawHorizontalLine(out, AutomapMiniRect.position + Displacement { -1, AutomapMiniRect.size.height }, AutomapMiniRect.size.width + 2, MapColorsDim); + DrawVerticalLine(out, AutomapMiniRect.position + Displacement { -1, 0 }, AutomapMiniRect.size.height, MapColorsDim); + DrawVerticalLine(out, AutomapMiniRect.position + Displacement { AutomapMiniRect.size.width, 0 }, AutomapMiniRect.size.height, MapColorsDim); + } + if ((cells & 1) != 0) { screen.x -= AmLine(64) * ((cells - 1) / 2); screen.y -= AmLine(32) * ((cells + 1) / 2); @@ -973,6 +1319,21 @@ void DrawAutomap(const Surface &out) if (leveltype == DTYPE_CAVES) myPlayerOffset.deltaY += TILE_HEIGHT; + // Draw Objects + DrawAutomapObject(out, myPlayerOffset, [](Point position) { return dObject[position.x][position.y] != 0; }); + + // Draw Missiles + DrawAutomapMissile(out, myPlayerOffset); + + if (leveltype == DTYPE_TOWN) { + // Draw Towners + DrawAutomapTowner(out, myPlayerOffset); + } else { + // Draw Minions + DrawAutomapMinion(out, myPlayerOffset); + } + + // Draw Players for (size_t playerId = 0; playerId < Players.size(); playerId++) { Player &player = Players[playerId]; if (player.isOnActiveLevel() && player.plractive && !player._pLvlChanging && (&player == MyPlayer || player.friendlyMode)) { diff --git a/Source/automap.h b/Source/automap.h index b2b1cb23d37..f8f171c1af9 100644 --- a/Source/automap.h +++ b/Source/automap.h @@ -30,16 +30,21 @@ enum MapExplorationType : uint8_t { /** Specifies whether the automap is enabled. */ extern DVL_API_FOR_TEST bool AutomapActive; +/** Specifies whether the automap is in minimap mode. */ +extern DVL_API_FOR_TEST bool AutomapMini; +/** Specifies whether the automap is transparent. */ +extern DVL_API_FOR_TEST bool AutomapTransparent; /** Tracks the explored areas of the map. */ extern uint8_t AutomapView[DMAXX][DMAXY]; /** Specifies the scale of the automap. */ extern DVL_API_FOR_TEST int AutoMapScale; extern DVL_API_FOR_TEST Displacement AutomapOffset; +extern Rectangle AutomapMiniRect; inline int AmLine(int x) { - assert(x >= 4 && x <= 64); - assert((x & (x - 1)) == 0); + assert(x >= 2 && x <= 64); + //assert((x & (x - 1)) == 0); return AutoMapScale * x / 100; } @@ -58,6 +63,11 @@ void InitAutomap(); */ void StartAutomap(); +/** + * @brief Displays the minimap. + */ +void StartMinimap(); + /** * @brief Scrolls the automap upwards. */ diff --git a/Source/control.cpp b/Source/control.cpp index 9a40e7f3a61..542a0978565 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -991,10 +991,14 @@ void control_check_btn_press() void DoAutoMap() { - if (!AutomapActive) + if (!AutomapActive) { StartAutomap(); - else + } else if (!AutomapMini) { + StartMinimap(); + } else { AutomapActive = false; + AutomapMini = false; + } } void CheckPanelInfo() diff --git a/Source/engine.cpp b/Source/engine.cpp index 909733cd80e..7d4b504c250 100644 --- a/Source/engine.cpp +++ b/Source/engine.cpp @@ -163,6 +163,15 @@ void DrawHalfTransparentRectTo(const Surface &out, int sx, int sy, int width, in DrawHalfTransparentBlendedRectTo(out, sx, sy, width, height); } +void SetHalfTransparentPixel(const Surface &out, Point position, std::uint8_t col) +{ + if (out.InBounds(position)) { + uint8_t *pix = out.at(position.x, position.y); + const std::array &lookupTable = paletteTransparencyLookup[col]; + *pix = lookupTable[*pix]; + } +} + void UnsafeDrawBorder2px(const Surface &out, Rectangle rect, uint8_t color) { const size_t width = rect.size.width; diff --git a/Source/engine.h b/Source/engine.h index 6750e6c62ca..08ff4bd200c 100644 --- a/Source/engine.h +++ b/Source/engine.h @@ -117,6 +117,7 @@ void UnsafeDrawVerticalLine(const Surface &out, Point from, int height, std::uin * @param height Rectangle height */ void DrawHalfTransparentRectTo(const Surface &out, int sx, int sy, int width, int height); +void SetHalfTransparentPixel(const Surface &out, Point position, std::uint8_t col); /** * Draws a 2px inset border. diff --git a/Source/engine/render/automap_render.cpp b/Source/engine/render/automap_render.cpp index 9dc5de0484a..4c176033ab9 100644 --- a/Source/engine/render/automap_render.cpp +++ b/Source/engine/render/automap_render.cpp @@ -4,6 +4,8 @@ * Line drawing routines for the automap. */ #include "engine/render/automap_render.hpp" +#include "automap.h" +#include "engine.h" #include @@ -24,36 +26,74 @@ template void DrawMapLine(const Surface &out, Point from, int height, std::uint8_t colorIndex) { while (height-- > 0) { - out.SetPixel({ from.x, from.y + 1 }, 0); - out.SetPixel(from, colorIndex); + SetMapPixel(out, { from.x, from.y + 1 }, 0); + SetMapPixel(out, from, colorIndex); from.x += static_cast(DirX); - out.SetPixel({ from.x, from.y + 1 }, 0); - out.SetPixel(from, colorIndex); + SetMapPixel(out, { from.x, from.y + 1 }, 0); + SetMapPixel(out, from, colorIndex); from.x += static_cast(DirX); from.y += static_cast(DirY); } - out.SetPixel({ from.x, from.y + 1 }, 0); - out.SetPixel(from, colorIndex); + SetMapPixel(out, { from.x, from.y + 1 }, 0); + SetMapPixel(out, from, colorIndex); } template void DrawMapLineSteep(const Surface &out, Point from, int width, std::uint8_t colorIndex) { while (width-- > 0) { - out.SetPixel({ from.x, from.y + 1 }, 0); - out.SetPixel(from, colorIndex); + SetMapPixel(out, { from.x, from.y + 1 }, 0); + SetMapPixel(out, from, colorIndex); from.y += static_cast(DirY); - out.SetPixel({ from.x, from.y + 1 }, 0); - out.SetPixel(from, colorIndex); + SetMapPixel(out, { from.x, from.y + 1 }, 0); + SetMapPixel(out, from, colorIndex); from.y += static_cast(DirY); from.x += static_cast(DirX); } - out.SetPixel({ from.x, from.y + 1 }, 0); - out.SetPixel(from, colorIndex); + SetMapPixel(out, { from.x, from.y + 1 }, 0); + SetMapPixel(out, from, colorIndex); +} + +void DrawMapFreeLine(const Surface &out, Point from, Point to, uint8_t colorIndex) +{ + int dx = std::abs(to.x - from.x); + int dy = std::abs(to.y - from.y); + int sx = from.x < to.x ? 1 : -1; + int sy = from.y < to.y ? 1 : -1; + int err = dx - dy; + + while (true) { + SetMapPixel(out, from, colorIndex); + + if (from.x == to.x && from.y == to.y) { + break; + } + + int e2 = 2 * err; + if (e2 > -dy) { + err -= dy; + from.x += sx; + } + if (e2 < dx) { + err += dx; + from.y += sy; + } + } } } // namespace +void SetMapPixel(const Surface &out, Point point, uint8_t color) +{ + if (AutomapMini && !AutomapMiniRect.contains(point)) + return; + if (!AutomapTransparent) { + out.SetPixel(point, color); + } else { + SetHalfTransparentPixel(out, point, color); + } +} + void DrawMapLineNE(const Surface &out, Point from, int height, std::uint8_t colorIndex) { DrawMapLine(out, from, height, colorIndex); @@ -94,4 +134,78 @@ void DrawMapLineSteepSW(const Surface &out, Point from, int width, std::uint8_t DrawMapLineSteep(out, from, width, colorIndex); } +void DrawMapEllipse(const Surface &out, Point from, int radius, uint8_t colorIndex) +{ + from.y -= AmLine(8); + + const int a = radius; + const int b = radius / 2; + + int x = 0; + int y = b; + + // Initial point + SetMapPixel(out, { from.x, from.y + b }, colorIndex); + SetMapPixel(out, { from.x, from.y - b }, colorIndex); + + // Initialize the parameters + int p1 = (b * b) - (a * a * b) + (a * a) / 4; + + // Region 1 + while ((b * b * x) < (a * a * y)) { + x++; + if (p1 < 0) { + p1 += (2 * b * b * x) + (b * b); + } else { + y--; + p1 += (2 * b * b * x) - (2 * a * a * y) + (b * b); + } + + SetMapPixel(out, { from.x + x, from.y + y }, colorIndex); + SetMapPixel(out, { from.x - x, from.y + y }, colorIndex); + SetMapPixel(out, { from.x + x, from.y - y }, colorIndex); + SetMapPixel(out, { from.x - x, from.y - y }, colorIndex); + } + + // Initialize the second parameter for Region 2 + int p2 = (b * b * ((x + 1) * (x + 1))) + (a * a * ((y - 1) * (y - 1))) - (a * a * b * b); + + // Region 2 + while (y > 0) { + y--; + if (p2 > 0) { + p2 += (-2 * a * a * y) + (a * a); + } else { + x++; + p2 += (2 * b * b * x) - (2 * a * a * y) + (a * a); + } + + SetMapPixel(out, { from.x + x, from.y + y }, colorIndex); + SetMapPixel(out, { from.x - x, from.y + y }, colorIndex); + SetMapPixel(out, { from.x + x, from.y - y }, colorIndex); + SetMapPixel(out, { from.x - x, from.y - y }, colorIndex); + } +} + +void DrawMapStar(const Surface &out, Point from, int radius, uint8_t color) +{ + from.y -= AmLine(8); + + const int scaleFactor = 128; + Point anchors[5]; + + anchors[0] = { from.x - (121 * radius / scaleFactor), from.y + (19 * radius / scaleFactor) }; // Left Point + anchors[1] = { from.x + (121 * radius / scaleFactor), from.y + (19 * radius / scaleFactor) }; // Right Point + anchors[2] = { from.x, from.y + (64 * radius / scaleFactor) }; // Bottom Point + anchors[3] = { from.x - (75 * radius / scaleFactor), from.y - (51 * radius / scaleFactor) }; // Top Left Point + anchors[4] = { from.x + (75 * radius / scaleFactor), from.y - (51 * radius / scaleFactor) }; // Top Right Point + + // Draw lines between the anchors to form a star + DrawMapFreeLine(out, anchors[3], anchors[1], color); // Connect Top Left -> Right + DrawMapFreeLine(out, anchors[1], anchors[0], color); // Connect Right -> Left + DrawMapFreeLine(out, anchors[0], anchors[4], color); // Connect Left -> Top Right + DrawMapFreeLine(out, anchors[4], anchors[2], color); // Connect Top Right -> Bottom + DrawMapFreeLine(out, anchors[2], anchors[3], color); // Connect Bottom -> Top Left +} + } // namespace devilution diff --git a/Source/engine/render/automap_render.hpp b/Source/engine/render/automap_render.hpp index df342eac78e..1b295a233d0 100644 --- a/Source/engine/render/automap_render.hpp +++ b/Source/engine/render/automap_render.hpp @@ -17,6 +17,7 @@ namespace devilution { +void SetMapPixel(const Surface &out, Point point, uint8_t color); /** * @brief Draw a line in the target buffer from the given point towards north east at an `atan(1/2)` angle. * @@ -88,5 +89,7 @@ void DrawMapLineSteepNW(const Surface &out, Point from, int width, std::uint8_t * The end point is at `{ from.x - (width + 1), from.y + 2 * width }`. */ void DrawMapLineSteepSW(const Surface &out, Point from, int width, std::uint8_t colorIndex); +void DrawMapEllipse(const Surface &out, Point from, int radius, uint8_t colorIndex); +void DrawMapStar(const Surface &out, Point from, int radius, uint8_t colorIndex); } // namespace devilution