冒险岛大地图展示信息相关

来源

主要对worldmapinfo.cpp做了相关修改

cpp 复制代码
 
 
#include <algorithm>
#include <climits>
#include <cstdio>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
 
#include "getmapbyid.h"
#include "getmobinfobyid.h"
#include "getnpcbyid.h"
#include "hookt.h"
#include "wvs/msghandler.h"
#include "wvs/tooltip.h"
#include "wvs/util.h"
#include "ztl/ztl.h"
 
ZALLOC_GLOBAL
ZALLOCEX(ZAllocAnonSelector, 0x00BF0B00)
ZALLOCEX(ZAllocStrSelector<char>, 0x00BF0A90)
ZALLOCEX(ZAllocStrSelector<wchar_t>, 0x00BF0BA8)
namespace {
 
    constexpr DWORD kAddrWorldMapOnMouseMove = 0x009EE2B3;
    constexpr DWORD kAddrWorldMapOnDestroy = 0x009EB94A;
    constexpr DWORD kAddrGetField = 0x00437A0C;
    constexpr DWORD kAddrToolTipCtor = 0x008E49B5;
 
    constexpr int kWorldMapSpotsPtrDw = 366;
    constexpr int kWorldMapSpotStrideDwords = 17;
    constexpr int kWorldMapSpotXDw = 0;
    constexpr int kWorldMapSpotYDw = 1;
    constexpr int kWorldMapCanvasOriginX = 13;
    constexpr int kWorldMapCanvasOriginY = 35;
    constexpr int kWorldMapSpotFieldArrayDw = 11;
    constexpr int kWorldMapHitRadiusPx = 6;
    constexpr int kToolTipMinWidth = 220;
    constexpr int kToolTipMaxWidth = 320;
    constexpr int kToolTipWidthPadding = 28;
    constexpr int kToolTipScreenMargin = 8;
    constexpr int kToolTipAnchorYOffset = 18;
    constexpr int kToolTipTitleBarHeight = 18;
    constexpr int kToolTipTitleTop = 8;
    constexpr int kToolTipContentLeft = 12;
    constexpr int kToolTipContentTop = 32;
    constexpr int kToolTipLineHeight = 24; //+10
    constexpr int kToolTipSectionGap = 6;
    constexpr int kToolTipItemIndent = 6;
    constexpr int kMobIconWidth = 20;
    constexpr int kMobIconHeight = 20;
    constexpr int kMobIconGap = 6;
 
 
    constexpr unsigned long kToolTipFillColor = 0xFF141A24;
    constexpr unsigned long kToolTipTitleFillColor = 0xFF423010;
    constexpr unsigned long kToolTipSeparatorColor = 0x70E0C070;
    constexpr unsigned long kToolTipTitleColor = 0xFFFFD45A;
    constexpr unsigned long kToolTipMapColor = 0xFF6EDB7E;
    constexpr unsigned long kToolTipMonsterHeaderColor = 0xFFFF6A6A;
    constexpr unsigned long kToolTipNpcHeaderColor = 0xFF6FAFFF;
    constexpr unsigned long kToolTipBodyColor = 0xFFFFFFFF;
    constexpr unsigned long kToolTipShadowColor = 0xFF000000;
    constexpr unsigned long kToolTipFontSize = 12;
    constexpr int kMaxTooltipEntriesPerSection = 10;
    const std::string kUnknownText = "未知";
    const std::string kTitleText = "地图信息";
 
    using WorldMapOnMouseMove = int(__thiscall*)(void*, int, int);
    using WorldMapOnDestroy = void(__thiscall*)(void*);
    using GetField = void* (__cdecl*)();
 
    auto CWorldMapDlg__OnMouseMove_Orig =
        reinterpret_cast<WorldMapOnMouseMove>(kAddrWorldMapOnMouseMove);
    auto CWorldMapDlg__OnDestroy_Orig =
        reinterpret_cast<WorldMapOnDestroy>(kAddrWorldMapOnDestroy);
    GetField g_GetField = nullptr;
 
    alignas(8) unsigned char g_toolTipBuffer[0x600];
    bool g_toolTipInitialized = false;
 
    enum class ToolTipLineKind {
        MapName,
        MonstersHeader,
        MonsterItem,
        NpcsHeader,
        NpcItem,
        Spacer,
    };
 
    struct SpotInfo {
        int index = -1;
        int mapId = 0;
        int anchorX = 0;
        int anchorY = 0;
    };
 
    struct MapToolTipData {
        int mapId = 0;
        std::string mapName;
        std::vector<std::string> monsterLines;
        std::vector<std::string> npcLines;
    };
 
    struct StyledToolTipLine {
        std::string text;
        ToolTipLineKind kind = ToolTipLineKind::MapName;
    };
 
    struct ToolTipFonts {
        IWzFontPtr title;
        IWzFontPtr mapName;
        IWzFontPtr monstersHeader;
        IWzFontPtr npcsHeader;
        IWzFontPtr body;
        IWzFontPtr shadow;
        bool attempted = false;
    };
 
    std::unordered_map<int, MapToolTipData> g_mapCache;
    ToolTipFonts g_toolTipFonts;
 
    int* ReadIntPtrDw(const void* base, int dwordIndex) {
        return *reinterpret_cast<int* const*>(
            reinterpret_cast<const char*>(base) + dwordIndex * 4);
    }
 
    bool CreateBoldFont(IWzFontPtr& outFont, unsigned long color, unsigned long size = kToolTipFontSize) {
        if (outFont) {
            return true;
        }
 
        PcCreateObject<IWzFontPtr>(L"Canvas#Font", outFont, nullptr);
        if (!outFont) {
            return false;
        }
 
        Ztl_variant_t style = L"B";
        auto createFont =
            reinterpret_cast<HRESULT(__thiscall*)(IWzFont*, Ztl_bstr_t, unsigned long, unsigned long, const Ztl_variant_t&)>(
                0x0046341A);
 
        return SUCCEEDED(createFont(outFont, Ztl_bstr_t(L"Dotum"), size, color, style));
    }
 
    bool EnsureToolTipFonts() {
        if (g_toolTipFonts.attempted) {
            return g_toolTipFonts.title && g_toolTipFonts.mapName && g_toolTipFonts.monstersHeader &&
                g_toolTipFonts.npcsHeader && g_toolTipFonts.body && g_toolTipFonts.shadow;
        }
 
        g_toolTipFonts.attempted = true;
 
        try {
            CreateBoldFont(g_toolTipFonts.title, kToolTipTitleColor);
            CreateBoldFont(g_toolTipFonts.mapName, kToolTipMapColor);
            CreateBoldFont(g_toolTipFonts.monstersHeader, kToolTipMonsterHeaderColor);
            CreateBoldFont(g_toolTipFonts.npcsHeader, kToolTipNpcHeaderColor);
            CreateBoldFont(g_toolTipFonts.body, kToolTipBodyColor);
            CreateBoldFont(g_toolTipFonts.shadow, kToolTipShadowColor);
        }
        catch (...) {
        }
 
        return g_toolTipFonts.title && g_toolTipFonts.mapName && g_toolTipFonts.monstersHeader &&
            g_toolTipFonts.npcsHeader && g_toolTipFonts.body && g_toolTipFonts.shadow;
    }
 
    IWzFont* GetFontForLine(const StyledToolTipLine& line) {
        switch (line.kind) {
        case ToolTipLineKind::MapName:
            return g_toolTipFonts.mapName;
        case ToolTipLineKind::MonstersHeader:
            return g_toolTipFonts.monstersHeader;
        case ToolTipLineKind::NpcsHeader:
            return g_toolTipFonts.npcsHeader;
        case ToolTipLineKind::MonsterItem:
        case ToolTipLineKind::NpcItem:
            return g_toolTipFonts.body;
        case ToolTipLineKind::Spacer:
            return nullptr;
        }
        return g_toolTipFonts.body;
    }
 
    int CalcTextWidth(IWzFont* font, const std::string& text) {
        if (!font || text.empty()) {
            return 0;
        }
 
        try {
            return static_cast<int>(font->CalcTextWidth(text.c_str()));
        }
        catch (...) {
            return static_cast<int>(text.size()) * 7;
        }
    }
 
    std::string FitTextToWidth(IWzFont* font, const std::string& text, int maxWidth) {
        if (!font || text.empty() || maxWidth <= 0) {
            return text;
        }
 
        if (CalcTextWidth(font, text) <= maxWidth) {
            return text;
        }
 
        static const std::string ellipsis = "...";
        if (CalcTextWidth(font, ellipsis) > maxWidth) {
            return std::string();
        }
 
        for (int length = static_cast<int>(text.size()) - 1; length > 0; --length) {
            const std::string candidate = text.substr(0, static_cast<size_t>(length)) + ellipsis;
            if (CalcTextWidth(font, candidate) <= maxWidth) {
                return candidate;
            }
        }
 
        return ellipsis;
    }
 
    std::vector<std::string> BuildMapDisplayLines(const std::string& mapName) {
        std::string displayName = mapName.empty() || mapName == "Unknown" ? kUnknownText : mapName;
        const std::string separator = " - ";
        const size_t separatorPos = displayName.find(separator);
        if (separatorPos != std::string::npos) {
            const std::string streetName = displayName.substr(0, separatorPos);
            const std::string mapOnlyName = displayName.substr(separatorPos + separator.size());
            if (!mapOnlyName.empty() && !streetName.empty()) {
                return { streetName, mapOnlyName };
            }
        }
        return { displayName };
    }
 
    void DrawShadowText(IWzCanvas* canvas, int x, int y, const std::string& text, IWzFont* font) {
        if (!canvas || !font || text.empty() || !EnsureToolTipFonts()) {
            return;
        }
 
        canvas->DrawTextA(x + 1, y + 1, text.c_str(), g_toolTipFonts.shadow);
        canvas->DrawTextA(x, y, text.c_str(), font);
    }
 
    bool TryGetWorldMapAbsPosition(void* ecx, int& left, int& top) {
        try {
            auto* ui = reinterpret_cast<IUIMsgHandler*>(ecx);
            left = ui->GetAbsLeft();
            top = ui->GetAbsTop();
            return true;
        }
        catch (...) {
            left = 0;
            top = 0;
            return false;
        }
    }
 
    SpotInfo GetSpotAt(void* base, int rx, int ry) {
        SpotInfo out;
 
        int* spots = ReadIntPtrDw(base, kWorldMapSpotsPtrDw);
        if (!spots) {
            return out;
        }
 
        const int count = *(spots - 1);
        if (count <= 0) {
            return out;
        }
 
        const int canvasX = rx - kWorldMapCanvasOriginX;
        const int canvasY = ry - kWorldMapCanvasOriginY;
        const int hitRadiusSquared = kWorldMapHitRadiusPx * kWorldMapHitRadiusPx;
 
        int bestIndex = -1;
        int bestRadiusSquared = INT_MAX;
 
        for (int i = 0; i < count; ++i) {
            int* spot = spots + i * kWorldMapSpotStrideDwords;
            auto fieldArray = *reinterpret_cast<unsigned* const*>(spot + kWorldMapSpotFieldArrayDw);
            if (!fieldArray) {
                continue;
            }
 
            const unsigned fieldCount = *(fieldArray - 1);
            if (!fieldCount) {
                continue;
            }
 
            const int dx = spot[kWorldMapSpotXDw] - canvasX;
            const int dy = spot[kWorldMapSpotYDw] - canvasY;
            const int radiusSquared = dx * dx + dy * dy;
            if (radiusSquared <= hitRadiusSquared && radiusSquared < bestRadiusSquared) {
                bestRadiusSquared = radiusSquared;
                bestIndex = i;
            }
        }
 
        if (bestIndex < 0) {
            return out;
        }
 
        int* spot = spots + bestIndex * kWorldMapSpotStrideDwords;
        auto fieldArray = *reinterpret_cast<unsigned* const*>(spot + kWorldMapSpotFieldArrayDw);
 
        out.index = bestIndex;
        out.mapId = (fieldArray && *(fieldArray - 1) > 0) ? static_cast<int>(fieldArray[0]) : 0;
        out.anchorX = spot[kWorldMapSpotXDw] + kWorldMapCanvasOriginX;
        out.anchorY = spot[kWorldMapSpotYDw] + kWorldMapCanvasOriginY;
        return out;
    }
 
    void* GetFieldContext() {
        if (!g_GetField) {
            g_GetField = reinterpret_cast<GetField>(kAddrGetField);
        }
 
        return g_GetField ? g_GetField() : nullptr;
    }
 
    void EnsureToolTip() {
        if (g_toolTipInitialized) {
            return;
        }
 
        reinterpret_cast<void(__thiscall*)(void*)>(kAddrToolTipCtor)(g_toolTipBuffer);
        g_toolTipInitialized = true;
    }
 
    void ToolTipClear() {
        if (g_toolTipInitialized) {
            reinterpret_cast<CUIToolTip*>(g_toolTipBuffer)->ClearToolTip();
        }
    }
 
    std::string BuildCountHeader(size_t count, const char* singular, const char* plural) {
        return std::string(count == 1 ? singular : plural) + " (" + std::to_string(count) + "):";
    }
 
    std::vector<StyledToolTipLine> BuildStyledLines(const MapToolTipData& data, int toolTipWidth) {
        std::vector<StyledToolTipLine> lines;
        const int headerWidth = (std::max)(40, toolTipWidth - (kToolTipContentLeft * 2));
        const int bodyWidth = headerWidth;
 
        for (const auto& mapLine : BuildMapDisplayLines(data.mapName)) {
            lines.push_back({ FitTextToWidth(g_toolTipFonts.mapName, mapLine, headerWidth), ToolTipLineKind::MapName });
        }
 
        if (!data.monsterLines.empty()) {
            lines.push_back({ "", ToolTipLineKind::Spacer });
            const std::string monstersHeader = BuildCountHeader(data.monsterLines.size(), "Monstro", "Monstros");
            lines.push_back({ FitTextToWidth(g_toolTipFonts.monstersHeader, monstersHeader, headerWidth), ToolTipLineKind::MonstersHeader });
            for (const auto& entry : data.monsterLines) {
                lines.push_back({ FitTextToWidth(g_toolTipFonts.body, entry, bodyWidth), ToolTipLineKind::MonsterItem });
            }
        }
 
        if (!data.npcLines.empty()) {
            lines.push_back({ "", ToolTipLineKind::Spacer });
            const std::string npcsHeader = BuildCountHeader(data.npcLines.size(), "NPC", "NPCs");
            lines.push_back({ FitTextToWidth(g_toolTipFonts.npcsHeader, npcsHeader, headerWidth), ToolTipLineKind::NpcsHeader });
            for (const auto& entry : data.npcLines) {
                lines.push_back({ FitTextToWidth(g_toolTipFonts.body, entry, bodyWidth), ToolTipLineKind::NpcItem });
            }
        }
 
        return lines;
    }
 
    std::string BuildPlainDescription(const std::vector<StyledToolTipLine>& lines) {
        std::string desc;
 
        for (size_t i = 0; i < lines.size(); ++i) {
            if (i > 0) {
                desc += '\n';
            }
            desc += lines[i].text;
        }
 
        return desc;
    }
 
    int ComputeToolTipWidth(const MapToolTipData& data) {
        if (!EnsureToolTipFonts()) {
            return 260;
        }
 
        int width = CalcTextWidth(g_toolTipFonts.title, kTitleText);
        for (const auto& mapLine : BuildMapDisplayLines(data.mapName)) {
            width = (std::max)(width, CalcTextWidth(g_toolTipFonts.mapName, mapLine));
        }
 
        if (!data.monsterLines.empty()) {
            width = (std::max)(width, CalcTextWidth(g_toolTipFonts.monstersHeader, BuildCountHeader(data.monsterLines.size(), "Monstro", "Monstros")));
        }
        if (!data.npcLines.empty()) {
            width = (std::max)(width, CalcTextWidth(g_toolTipFonts.npcsHeader, BuildCountHeader(data.npcLines.size(), "NPC", "NPCs")));
        }
 
        for (const auto& monster : data.monsterLines) {
            width = (std::max)(width, CalcTextWidth(g_toolTipFonts.body, monster));
        }
        for (const auto& npc : data.npcLines) {
            width = (std::max)(width, CalcTextWidth(g_toolTipFonts.body, npc));
        }
 
        width += kToolTipWidthPadding;
        return (std::clamp)(width, kToolTipMinWidth, kToolTipMaxWidth);
    }
 
   /* void DrawStyledToolTip(CUIToolTip* toolTip, const std::vector<StyledToolTipLine>& lines) {
        if (!toolTip || !toolTip->m_pLayer || !EnsureToolTipFonts()) {
            return;
        }
 
        IWzCanvasPtr canvas = toolTip->m_pLayer->canvas[0];
        if (!canvas) {
            return;
        }
 
        const int width = toolTip->m_nWidth;
        const int height = toolTip->m_nHeight;
        if (width <= 0 || height <= 0) {
            return;
        }
 
        canvas->DrawRectangle(3, 3, (std::max)(0, width - 6), (std::max)(0, height - 6), kToolTipFillColor);
        canvas->DrawRectangle(5, 5, (std::max)(0, width - 10), kToolTipTitleBarHeight, kToolTipTitleFillColor);
        canvas->DrawRectangle(9, 27, (std::max)(0, width - 18), 1, kToolTipSeparatorColor);
 
        std::string title = FitTextToWidth(g_toolTipFonts.title, kTitleText, (std::max)(40, width - 20));
        const int titleWidth = CalcTextWidth(g_toolTipFonts.title, title);
        const int titleX = (std::max)(6, (width - titleWidth) / 2);
        DrawShadowText(canvas, titleX, kToolTipTitleTop, title, g_toolTipFonts.title);
 
        int y = kToolTipContentTop;
        for (const auto& line : lines) {
            if (line.kind == ToolTipLineKind::Spacer) {
                y += kToolTipSectionGap;
                continue;
            }
 
            IWzFont* font = GetFontForLine(line);
            if (!font) {
                continue;
            }
 
            int x = kToolTipContentLeft;
            if (line.kind == ToolTipLineKind::MapName) {
                const int lineWidth = CalcTextWidth(font, line.text);
                x = (std::max)(kToolTipContentLeft, (width - lineWidth) / 2);
            }
 
            DrawShadowText(canvas, x, y, line.text, font);
            y += kToolTipLineHeight;
        }
    }*/
 
    void DrawStyledToolTip(CUIToolTip* toolTip, const std::vector<StyledToolTipLine>& lines)
    {
        if (!toolTip || !toolTip->m_pLayer || !EnsureToolTipFonts()) {
            return;
        }
 
        IWzCanvasPtr canvas = toolTip->m_pLayer->canvas[0];
        if (!canvas) {
            return;
        }
 
        const int width = toolTip->m_nWidth;
        const int height = toolTip->m_nHeight;
        if (width <= 0 || height <= 0) {
            return;
        }
 
        canvas->DrawRectangle(3, 3, (std::max)(0, width - 6), (std::max)(0, height - 6), kToolTipFillColor);
        canvas->DrawRectangle(5, 5, (std::max)(0, width - 10), kToolTipTitleBarHeight, kToolTipTitleFillColor);
        canvas->DrawRectangle(9, 27, (std::max)(0, width - 18), 1, kToolTipSeparatorColor);
 
        std::string title = FitTextToWidth(g_toolTipFonts.title, kTitleText, (std::max)(40, width - 20));
        const int titleWidth = CalcTextWidth(g_toolTipFonts.title, title);
        const int titleX = (std::max)(6, (width - titleWidth) / 2);
 
        DrawShadowText(canvas, titleX, kToolTipTitleTop, title, g_toolTipFonts.title);
 
        int y = kToolTipContentTop;
 
        for (const auto& line : lines) {
            if (line.kind == ToolTipLineKind::Spacer) {
                y += kToolTipSectionGap;
                continue;
            }
 
            IWzFont* font = GetFontForLine(line);
            if (!font) {
                continue;
            }
 
            int x = kToolTipContentLeft;
            std::string drawText = line.text;
 
            int mobId = 0;
            const std::string prefix = "[MOBICON:";
 
            if (drawText.rfind(prefix, 0) == 0) {
                size_t endPos = drawText.find("]");
                if (endPos != std::string::npos) {
                    mobId = std::atoi(
                        drawText.substr(prefix.size(), endPos - prefix.size()).c_str()
                    );
 
                    if (endPos + 2 <= drawText.size()) {
                        drawText = drawText.substr(endPos + 2);
                    }
                    else {
                        drawText.clear();
                    }
                }
            }
 
            if (line.kind == ToolTipLineKind::MapName) {
                const int lineWidth = CalcTextWidth(font, drawText);
                x = (std::max)(kToolTipContentLeft, (width - lineWidth) / 2);
            }
 
            if (mobId > 0) {
                IWzCanvasPtr icon = GetMobIconById(mobId);
 
                if (icon) {
                    try {
                        canvas->CopyEx(
                            x,
                            y -4,
                            icon.GetInterfacePtr(),
                            CA_OVERWRITE,
                            kMobIconWidth,
                            kMobIconHeight,
                            0,
                            0,
                            static_cast<int>(icon->Getwidth()),
                            static_cast<int>(icon->Getheight()),
                            vtEmpty
                        );
                    }
                    catch (...) {
                    }
 
                    x += kMobIconWidth + kMobIconGap;
                }
            }
 
            // 画文字
            DrawShadowText(canvas, x, y, drawText, font);
 
            //  行高统一控制(关键)
            y += kToolTipLineHeight;
        }
    }
    void ToolTipShow(int screenLeft, int screenTop, const MapToolTipData& data)
    {
        EnsureToolTip();
 
        auto* toolTip = reinterpret_cast<CUIToolTip*>(g_toolTipBuffer);
        toolTip->ClearToolTip();
 
        const int width = ComputeToolTipWidth(data);
        const auto lines = BuildStyledLines(data, width);
 
        std::string descText = BuildPlainDescription(lines);
 
        // 关键:因为我们自定义绘制的行高比原 tooltip 默认行高大,
        // 所以这里补充空行,让 SetToolTip_String2 创建更高的背景。
        int extraHeight = 0;
        for (const auto& line : lines) {
            if (line.kind == ToolTipLineKind::Spacer)
                continue;
 
            // 怪物行现在带 icon,行高更高
            if (line.text.rfind("[MOBICON:", 0) == 0) {
                extraHeight += 10;
            }
        }
 
        // 底部额外留白
        extraHeight += 16;
 
        const int defaultLineHeight = 14;
        int extraLines = (extraHeight + defaultLineHeight - 1) / defaultLineHeight;
 
        for (int i = 0; i < extraLines; ++i) {
            descText += "\n ";
        }
 
        ZXString<char> title(kTitleText.c_str());
        ZXString<char> desc(descText.c_str());
 
        toolTip->SetToolTip_String2(
            screenLeft,
            screenTop,
            title,
            desc,
            0,
            0,
            0,
            width,
            1,
            0
        );
 
        DrawStyledToolTip(toolTip, lines);
    }
 
    MapToolTipData GetMapToolTipData(int mapId) {
        const auto cached = g_mapCache.find(mapId);
        if (cached != g_mapCache.end()) {
            return cached->second;
        }
 
        MapToolTipData result;
        result.mapId = mapId;
        result.mapName = GetMapById(mapId);
 
        try {
            wchar_t path[128];
            swprintf(path, _countof(path), L"Map/Map/Map%d/%08d.img", mapId / 100000000, mapId);
 
            IWzPropertyPtr pMap = get_rm()->GetObjectA(path).GetUnknown();
            if (!pMap) {
                g_mapCache[mapId] = result;
                return result;
            }
 
            IWzPropertyPtr pLife = pMap->item[L"life"].GetUnknown();
            if (!pLife) {
                g_mapCache[mapId] = result;
                return result;
            }
 
            std::unordered_set<int> seenMobs;
            std::unordered_set<int> seenNpcs;
 
            const int total = pLife->Getcount();
            for (int i = 0; i < total; ++i) {
                wchar_t indexText[16];
                _itow_s(i, indexText, 10);
 
                IWzPropertyPtr pEntry = get_unknown(pLife->item[indexText]);
                if (!pEntry) {
                    continue;
                }
 
                const int id = get_int32(pEntry->item[L"id"], 0);
                Ztl_variant_t vType = pEntry->item[L"type"];
                if (vType.vt != VT_BSTR) {
                    continue;
                }
 
                const std::wstring type = static_cast<const wchar_t*>(_bstr_t(vType));
              /*  if (type == L"m") {
                    if (id > 0 && seenMobs.insert(id).second) {
                        const int level = GetMobLevelById(id);
                        std::string line = "[等级  " + std::to_string(level) + "] " + GetMobNameById(id);
                        result.monsterLines.push_back(std::move(line));
                    }
                }*/
                if (type == L"m") {
                    if (id > 0 && seenMobs.insert(id).second) {
                        const int level = GetMobLevelById(id);
 
                        std::string line =
                            "[MOBICON:" + std::to_string(id) + "] " +
                            "[等级 " + std::to_string(level) + "] " +
                            GetMobNameById(id);
 
                        result.monsterLines.push_back(std::move(line));
                    }
                }
 
                else if (type == L"n") {
                    if (id > 0 && seenNpcs.insert(id).second) {
                        result.npcLines.push_back(GetNpcById(id));
                    }
                }
            }
 
            if (result.monsterLines.size() > kMaxTooltipEntriesPerSection) {
                result.monsterLines.resize(kMaxTooltipEntriesPerSection);
            }
            if (result.npcLines.size() > kMaxTooltipEntriesPerSection) {
                result.npcLines.resize(kMaxTooltipEntriesPerSection);
            }
 
            g_mapCache[mapId] = result;
            return result;
        }
        catch (...) {
            g_mapCache[mapId] = result;
            return result;
        }
    }
 
    int __fastcall CWorldMapDlg__OnMouseMove_hook(void* ecx, void*, int x, int y) {
        if (!ecx) {
            ToolTipClear();
            return 0;
        }
 
        if (!GetFieldContext()) {
            ToolTipClear();
            return CWorldMapDlg__OnMouseMove_Orig(ecx, x, y);
        }
 
        void* base = reinterpret_cast<char*>(ecx) - 4;
        const SpotInfo spot = GetSpotAt(base, x, y);
 
        if (spot.index >= 0 && spot.mapId > 0) {
            int absLeft = 0;
            int absTop = 0;
            TryGetWorldMapAbsPosition(ecx, absLeft, absTop);
 
            try {
                reinterpret_cast<IUIMsgHandler*>(ecx)->ClearToolTip();
            }
            catch (...) {
            }
 
            const MapToolTipData data = GetMapToolTipData(spot.mapId);
            const int toolTipWidth = ComputeToolTipWidth(data);
            const int desiredLeft = absLeft + spot.anchorX - (toolTipWidth / 2);
            const int screenLeft = (std::clamp)(
                desiredLeft,
                kToolTipScreenMargin,
                (std::max)(kToolTipScreenMargin, get_screen_width() - toolTipWidth - kToolTipScreenMargin));
            const int screenTop = (std::max)(kToolTipScreenMargin, absTop + spot.anchorY + kToolTipAnchorYOffset);
 
            ToolTipShow(screenLeft, screenTop, data);
            return 0;
        }
 
        ToolTipClear();
        const int result = CWorldMapDlg__OnMouseMove_Orig(ecx, x, y);
 
        try {
            reinterpret_cast<IUIMsgHandler*>(ecx)->ClearToolTip();
        }
        catch (...) {
        }
 
        return result;
    }
 
    void __fastcall CWorldMapDlg__OnDestroy_hook(void* ecx, void*) {
        ToolTipClear();
        CWorldMapDlg__OnDestroy_Orig(ecx);
    }
 
} // namespace
 
void AttachMapInfoToolTip() {
    ATTACH_HOOK(CWorldMapDlg__OnMouseMove_Orig, CWorldMapDlg__OnMouseMove_hook);
    ATTACH_HOOK(CWorldMapDlg__OnDestroy_Orig, CWorldMapDlg__OnDestroy_hook);
}
相关推荐
萧曵 丶4 天前
如何增加一个npc的脚本
gms·冒险岛·079