From eb7b7a94e464294445a11e240e3e14128c615c9b Mon Sep 17 00:00:00 2001 From: MarcelineVQ Date: Sun, 8 Mar 2026 13:37:44 -0700 Subject: [PATCH] Clean up minimap icons: remove WMO filtering, cache active tracking, namespace Lua API Remove unused indoor/outdoor WMO filtering and gray blip tinting. Cache active tracking state in a bool refreshed on config change instead of scanning all entries per-object per-frame. Move SetObjectTypeBlip into WeirdUtils table to keep it out of the global namespace. Add minimap tracking section to DLL_README. --- DLL_README.md | 16 ++++++ src/main.zig | 6 ++- src/minimapicons/addon/MinimapIcons.lua | 10 ++-- src/minimapicons/minimapicons.zig | 72 +++++++------------------ 4 files changed, 46 insertions(+), 58 deletions(-) diff --git a/DLL_README.md b/DLL_README.md index ce437d9..93c056d 100644 --- a/DLL_README.md +++ b/DLL_README.md @@ -107,6 +107,22 @@ No configuration needed, install and forget. --- +### Utility Minimap Trackings + +Adds custom minimap icons for NPC types (vendors, trainers, innkeepers, etc.) and game objects (mailboxes). +Replaces the native tracking dropdown with a combined menu showing both spell tracking and NPC category tracking. + +- Click the minimap tracking icon to open the dropdown +- Check/uncheck NPC categories to toggle their minimap icons +- Spell tracking (Hunter tracking, Find Herbs, etc.) remains available alongside NPC tracking + +Supports many NPC types such as Auctioneer, Banker, Flightmaster, Repair, Reagents, Poisons, and more +Supported game objects: Mailbox, Brainwasher + +**DLL:** `minimapicons.dll` + +--- + ### Log Sessions Organizes the combat, raw combat, and chat logs into per-character directories with timestamped filenames: diff --git a/src/main.zig b/src/main.zig index 4159a67..d61cced 100644 --- a/src/main.zig +++ b/src/main.zig @@ -94,7 +94,11 @@ fn registerLuaFunctions() void { registerFunction("GetCursorScale", @intFromPtr(&bigcursor.luaGetCursorScale)); } if (build_opts.minimapicons and minimapicons.isActive()) { - registerFunction("SetObjectTypeBlip", @intFromPtr(&minimapicons.luaSetObjectTypeBlip)); + const lib = [_]lua.LuaReg{ + .{ .name = "SetObjectTypeBlip", .func = @intFromPtr(&minimapicons.luaSetObjectTypeBlip) }, + .{ .name = null, .func = 0 }, + }; + lua.openlib(lua.getContext(), "WeirdUtils", &lib, 0); } if (build_opts.worldmarkers and markers.isActive()) { // User-facing functions stay global diff --git a/src/minimapicons/addon/MinimapIcons.lua b/src/minimapicons/addon/MinimapIcons.lua index e1677a7..ab618b4 100644 --- a/src/minimapicons/addon/MinimapIcons.lua +++ b/src/minimapicons/addon/MinimapIcons.lua @@ -79,7 +79,7 @@ local AMMO_FILTERS = { } -- NPC tracking categories (icons embedded from Wrath client) --- trackingType maps to DLL SetObjectTypeBlip() type names. +-- trackingType maps to DLL WeirdUtils.SetObjectTypeBlip() type names. -- Filter: pipe-delimited exact subname match (DLL matches ANY segment as substring). -- Filtered entries take priority over unfiltered catch-all entries with the same flag. -- "dynamic" filter is resolved at runtime via getFilter(). @@ -87,7 +87,7 @@ local NPC_CATEGORIES = { { name = "Auctioneer", trackingType = "auctioneer", icon = "Interface\\Minimap\\Tracking\\Auctioneer" }, { name = "Banker", trackingType = "banker", icon = "Interface\\Minimap\\Tracking\\Banker" }, { name = "Battle Master", trackingType = "battlemaster", icon = "Interface\\Minimap\\Tracking\\BattleMaster" }, - { name = "Brainwasher", trackingType = "brainwasher", icon = "Interface\\Minimap\\Tracking\\Brainwasher", scale = 1.7, default = 1 }, + { name = "Brainwasher", trackingType = "brainwasher", icon = "Interface\\Minimap\\Tracking\\Brainwasher", scale = 1.8, default = 1 }, { name = "Class Trainer", trackingType = "trainer", icon = "Interface\\Minimap\\Tracking\\Class", getFilter = function() return (UnitClass("player")) end }, { name = "Flight Master", trackingType = "flightmaster", icon = "Interface\\Minimap\\Tracking\\FlightMaster" }, @@ -119,14 +119,14 @@ local activeNpcCategories = {} -- name -> 1/nil, loaded from WeirdUtils_MinimapI -- (e.g. Class Trainer filtering by player class) create filtered entries -- that take priority over unfiltered catch-all entries. local function updateDllTracking() - if not SetObjectTypeBlip then return end + if not WeirdUtils.SetObjectTypeBlip then return end for _, cat in ipairs(NPC_CATEGORIES) do local inc = cat.getFilter and cat.getFilter() or nil local exc = cat.getExclude and cat.getExclude() or nil if activeNpcCategories[cat.name] then - SetObjectTypeBlip(cat.trackingType, cat.icon, cat.scale or 1.5, inc, exc) + WeirdUtils.SetObjectTypeBlip(cat.trackingType, cat.icon, cat.scale or 1.5, inc, exc) else - SetObjectTypeBlip(cat.trackingType, nil, nil, inc, exc) + WeirdUtils.SetObjectTypeBlip(cat.trackingType, nil, nil, inc, exc) end end end diff --git a/src/minimapicons/minimapicons.zig b/src/minimapicons/minimapicons.zig index 3647aa1..cd35b15 100644 --- a/src/minimapicons/minimapicons.zig +++ b/src/minimapicons/minimapicons.zig @@ -43,7 +43,6 @@ const ADDR = struct { const GxPrimDrawElements: usize = 0x58A2E0; const GxPrimUnlockVertexPtrs: usize = 0x58A340; const GetObjectByGUID: usize = 0x464870; - const QueryMapObjIDs: usize = 0x670540; const CGxTexFlagsInit: usize = 0x58A980; const CStatusDestructor: usize = 0x419E30; @@ -58,7 +57,6 @@ const ADDR = struct { const OBJ_VTABLE: usize = 0x00; const OBJ_DATA: usize = 0x08; // m_data — update fields descriptor (starts at field 0) const OBJ_TYPE: usize = 0x14; // m_objectType - const OBJ_WORLD_DATA: usize = 0xE0; // m_worldData (CWorld*) const OBJ_CREATURE_CACHE: usize = 0xB30; // ptr to creature cache entry // Creature cache entry offsets (name[0..3] at +0x00..+0x0C, subname at +0x10) @@ -70,8 +68,6 @@ const ADDR = struct { const DESC_GO_TYPE: usize = 0x15 * 4; // GAMEOBJECT_TYPE_ID // MINIMAPINFO struct offsets - const MI_WMO_ID: usize = 0x04; - const MI_MAP_OBJ_ID: usize = 0x08; const MI_POS: usize = 0x0C; // C3Vector const MI_RADIUS: usize = 0x18; const MI_LAYOUT_SCALE: usize = 0x1C; @@ -116,7 +112,6 @@ const Blip = struct { const TrackedBlip = struct { pos: C2Vector, blip: Blip, - gray: bool, }; const allocator = std.heap.page_allocator; @@ -263,6 +258,7 @@ var g_go_tracking: [MAX_GO_ENTRIES]GoTypeEntry = .{GoTypeEntry{}} ** MAX_GO_ENTR var g_go_id_tracking: [MAX_GO_ID_ENTRIES]GoEntryEntry = .{GoEntryEntry{}} ** MAX_GO_ID_ENTRIES; var g_blips: [MAX_BLIPS]TrackedBlip = undefined; var g_blip_count: u32 = 0; +var g_has_active_tracking: bool = false; var g_tex_cache: [MAX_TEXTURES]TextureEntry = .{TextureEntry{}} ** MAX_TEXTURES; var g_tex_cache_count: u32 = 0; var g_default_tex_flags: u32 = 0; @@ -388,26 +384,6 @@ fn getObjectPosition(obj: u32) ?C3Vector { return pos; } -fn queryMapObjIDs(world_data: u32) struct { wmo_id: u32, map_obj_id: u32 } { - if (!isValidPtr(world_data)) return .{ .wmo_id = 0, .map_obj_id = 0 }; - var wmo_id: u32 = 0; - var map_obj_id: u32 = 0; - var group_num: u32 = 0; - // __fastcall(world_ECX, &wmoID_EDX, &mapObjID, &groupNum) — RET 0x8 - asm volatile ( - \\push %[group] - \\push %[map] - \\call *%[func] - : - : [_] "{ecx}" (world_data), - [_] "{edx}" (@intFromPtr(&wmo_id)), - [map] "r" (@intFromPtr(&map_obj_id)), - [group] "r" (@intFromPtr(&group_num)), - [func] "r" (@as(u32, ADDR.QueryMapObjIDs)), - : .{ .eax = true, .memory = true, .cc = true }); - return .{ .wmo_id = wmo_id, .map_obj_id = map_obj_id }; -} - // ============================================================================= // Minimap coordinate conversion // ============================================================================= @@ -553,13 +529,10 @@ fn initTexFlags() u32 { // Blip drawing (port of VanillaHelpers DrawMinimapTexture) // ============================================================================= -fn drawMinimapTexture(texture: u32, pos: C2Vector, scale: f32, gray: bool) void { +fn drawMinimapTexture(texture: u32, pos: C2Vector, scale: f32) void { if (texture == 0) return; - const color: CImVector = if (gray) - .{ .b = 0xB0, .g = 0xB0, .r = 0xB0, .a = 0xFF } - else - .{ .b = 0xFF, .g = 0xFF, .r = 0xFF, .a = 0xFF }; + const color: CImVector = .{ .b = 0xFF, .g = 0xFF, .r = 0xFF, .a = 0xFF }; // Scale static blip vertex template by blip scale and offset by minimap position var vertices: [4]C3Vector = undefined; @@ -724,20 +697,6 @@ fn checkObject(info: u32, guid_lo: u32, guid_hi: u32) bool { fn trackObject(info: u32, obj: u32, blip: Blip) void { if (g_blip_count >= MAX_BLIPS) return; - // WMO indoor/outdoor filtering - const info_wmo = hook.readMem(u32, info + ADDR.MI_WMO_ID); - const info_map = hook.readMem(u32, info + ADDR.MI_MAP_OBJ_ID); - const world_data = hook.readMem(u32, obj + ADDR.OBJ_WORLD_DATA); - - var is_different_area = false; - if (isValidPtr(world_data)) { - const ids = queryMapObjIDs(world_data); - // Hide outside blips when player is inside a WMO (replicating original behavior) - if (info_wmo != 0 and (ids.wmo_id != info_wmo or ids.map_obj_id != info_map)) - return; - is_different_area = (ids.wmo_id != info_wmo); - } - const pos = getObjectPosition(obj) orelse return; const cur = C3Vector{ @@ -755,22 +714,30 @@ fn trackObject(info: u32, obj: u32, blip: Blip) void { g_blips[g_blip_count] = .{ .pos = minimap_pos, .blip = blip, - .gray = is_different_area, }; g_blip_count += 1; } -fn hasActiveTracking() bool { +fn refreshActiveTrackingCache() void { for (&g_flag_tracking) |*entry| { - if (entry.active) return true; + if (entry.active) { + g_has_active_tracking = true; + return; + } } for (&g_go_tracking) |*entry| { - if (entry.active) return true; + if (entry.active) { + g_has_active_tracking = true; + return; + } } for (&g_go_id_tracking) |*entry| { - if (entry.active) return true; + if (entry.active) { + g_has_active_tracking = true; + return; + } } - return false; + g_has_active_tracking = false; } // ============================================================================= @@ -784,7 +751,7 @@ const EnumProcFn = fn (u32, u32, u32, u32) callconv(fc) i32; var enum_proc_hook: hook.Detour(EnumProcFn) = .{}; fn objectEnumProcDetour(info: u32, _edx: u32, guid_lo: u32, guid_hi: u32) callconv(fc) i32 { - if (hasActiveTracking()) { + if (g_has_active_tracking) { if (checkObject(info, guid_lo, guid_hi)) { return 1; // Skip original — we draw our own icon } @@ -806,7 +773,6 @@ fn renderObjectBlipsDetour(thisptr: u32, _edx: u32, dn_info: u32) callconv(fc) v g_blips[i].blip.texture, g_blips[i].pos, g_blips[i].blip.scale, - g_blips[i].gray, ); } } @@ -839,6 +805,8 @@ fn luaStringToSlice(L: lua.State, idx: i32) ?[]const u8 { // SetObjectTypeBlip(typeName [, texturePath [, scale [, includeFilter [, excludeFilter]]]]) pub fn luaSetObjectTypeBlip(L: lua.State) callconv(fc) i32 { + defer refreshActiveTrackingCache(); + if (!lua.isstring(L, 1)) { lua.luaError(L, "Usage: SetObjectTypeBlip(type [, texture [, scale [, include [, exclude]]]])"); return 0;