Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 205 additions & 26 deletions modules/game_cyclopedia/tab/map/map.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,79 @@
local UI = nil
local virtualFloor = 7
local viewRadioGroup = nil
local loadedAssetsDir = nil
local loadedFloorSet = {} -- floors already indexed for loadedAssetsDir

-- Forward declaration — defined later in the file.
local refreshVirtualFloors

-- Surface View is only available for floors 0-7 (surface and above).
-- Underground floors (8-15) always use Map View.
local SEA_FLOOR = 7

local function ensureViewFloorsLoaded()
local assetsDir = string.format("/data/things/%d", g_game.getClientVersion())

-- Reset when switching game versions
if assetsDir ~= loadedAssetsDir then
g_satelliteMap.clear()
loadedFloorSet = {}
loadedAssetsDir = assetsDir
end

-- For surface floors: composite view needs [virtualFloor, SEA_FLOOR].
-- For underground: only the current floor is needed (static minimap, map view only).
local floorMax = (virtualFloor <= SEA_FLOOR) and SEA_FLOOR or virtualFloor

local minNeeded, maxNeeded
for f = virtualFloor, floorMax do
if not loadedFloorSet[f] then
if not minNeeded then
minNeeded = f
end
maxNeeded = f
end
end

if minNeeded then
g_satelliteMap.loadFloors(assetsDir, minNeeded, maxNeeded)
for f = minNeeded, maxNeeded do
loadedFloorSet[f] = true
end
end
end

local function isSurfaceFloor(floor)
return floor <= SEA_FLOOR
end

local function updateViewMode()
if not UI or not viewRadioGroup then
return
end

local minimapWidget = UI.MapBase.minimap
local viewBase = UI.InformationBase.InternalBase.DisplayBase.ViewBase1
local surfaceCheck = viewBase.SurfaceCheck
local separatorScroll = viewBase.SeparatorScroll
local separatorLabel = viewBase.SeparatorLabel

local canUseSurface = isSurfaceFloor(virtualFloor) and g_satelliteMap.hasChunksForView(virtualFloor)

-- Underground: force Map View and lock the Surface option.
-- selectWidget must come BEFORE setEnabled: UIRadioGroup calls setChecked(false)
-- on the deselected widget, which does not work correctly on a disabled widget.
if not canUseSurface then
viewRadioGroup:selectWidget(viewBase.MapCheck)
end
surfaceCheck:setEnabled(canUseSurface)

-- Level separator scroll is only meaningful in Surface View on floors above z7 (z < 7).
-- At z7 itself the composite has no lower floor to blend with, so the scroll is unused.
local inSurfaceMode = canUseSurface and surfaceCheck:isChecked() and virtualFloor < SEA_FLOOR
separatorScroll:setEnabled(inSurfaceMode)
separatorLabel:setEnabled(inSurfaceMode)
end

function showMap()
g_minimap.saveOtmm('/minimap.otmm')
Expand All @@ -9,7 +83,6 @@ function showMap()
onPositionChange = Cyclopedia.onUpdateCameraPosition
}):execute()

Cyclopedia.prevFloor = 7
Cyclopedia.loadMap()

controllerCyclopedia.ui.CharmsBase:setVisible(false)
Expand All @@ -24,6 +97,15 @@ function Cyclopedia.loadMap()
local clientVersion = g_game.getClientVersion()
local minimapWidget = UI.MapBase.minimap

-- Prime virtualFloor from current player position before any floor-dependent logic
local player = g_game.getLocalPlayer()
if player then
local pos = player:getPosition()
if pos then
virtualFloor = pos.z
end
end

g_minimap.clean()

local loaded = false
Expand All @@ -47,36 +129,105 @@ function Cyclopedia.loadMap()
print("Minimap couldn't be loaded, file missing?")
end

-- Preserve the zoom declared in the OTUI style — load() overwrites it with the
-- HUD minimap's saved settings zoom, which we don't want for Cyclopedia.
local initialZoom = minimapWidget:getZoom()
minimapWidget:load()
-- minimapWidget:hideFlags()
end
minimapWidget:setZoom(initialZoom)
minimapWidget:setUseStaticMinimap(true)

-- Load satellite chunks for the floors needed by the current view only.
-- Additional floors are indexed on demand when the user navigates deeper.
ensureViewFloorsLoaded()
local chunkCount = g_satelliteMap.hasChunksForView(virtualFloor) and 1 or 0

-- Views panel is always visible; satellite chunks determine whether Surface View is enabled.
local viewBase = UI.InformationBase.InternalBase.DisplayBase.ViewBase1
local surfaceCheck = viewBase.SurfaceCheck
local mapCheck = viewBase.MapCheck

-- Clean up previous radio group if the map tab was reopened
if viewRadioGroup then
viewRadioGroup:destroy()
viewRadioGroup = nil
end

viewRadioGroup = UIRadioGroup.create()
viewRadioGroup:addWidget(surfaceCheck)
viewRadioGroup:addWidget(mapCheck)

function Cyclopedia.CreateMarkItem(Data)
local MarkItem = g_ui.createWidget("MarkListItem", UI.InformationBase.InternalBase.DisplayBase.MarkList)
MarkItem:setIcon("/images/game/minimap/flag" .. Data.flagId)
viewRadioGroup.onSelectionChange = function(self, selected)
local inSurface = selected == surfaceCheck and isSurfaceFloor(virtualFloor) and
g_satelliteMap.hasChunksForView(virtualFloor)
minimapWidget:setSatelliteMode(inSurface)

-- Sync separator scroll enabled state (only for floors above z7)
local separatorScroll = viewBase.SeparatorScroll
local separatorLabel = viewBase.SeparatorLabel
local canUseSeparator = inSurface and virtualFloor < SEA_FLOOR
separatorScroll:setEnabled(canUseSeparator)
separatorLabel:setEnabled(canUseSeparator)
end

-- Default: Surface View when satellite data exists and on a surface floor, else Map View
viewRadioGroup:selectWidget((chunkCount > 0 and isSurfaceFloor(virtualFloor)) and surfaceCheck or mapCheck)

-- Hook separator scroll: adjusts floor compositing opacity (1.0 = all visible, 0.0 = target only)
local separatorScroll = viewBase.SeparatorScroll
separatorScroll.onValueChange = function(self, value)
minimapWidget:setFloorSeparatorOpacity(value / 100.0)
end
-- Reset opacity on (re-)open
minimapWidget:setFloorSeparatorOpacity(separatorScroll:getValue() / 100.0)

updateViewMode()

-- Sync flags from main minimap's live table (avoids stale g_settings on first open).
local mainMinimap = modules.game_minimap and modules.game_minimap.getMiniMapUi and
modules.game_minimap.getMiniMapUi()
if mainMinimap then
for _, flag in pairs(mainMinimap.flags) do
if not minimapWidget:getFlag(flag.pos) then
minimapWidget:addFlag(flag.pos, flag.icon, flag.description)
end
end
end

-- Apply initial filter state: hide flags whose filter checkbox is unchecked.
local markList = UI.InformationBase.InternalBase.DisplayBase.MarkList
for _, flag in pairs(minimapWidget.flags) do
local btn = markList:getChildById(tostring(flag.icon))
flag:setVisible(btn ~= nil and btn:isChecked())
end

refreshVirtualFloors()
end

function Cyclopedia.toggleMapFlag(widget, checked)
-- UI.MapBase.minimap:filterFlag(widget:getId(), checked)
local flagType = tonumber(widget:getId())
if not flagType then
return
end
local minimapWidget = UI.MapBase.minimap
for _, flag in pairs(minimapWidget.flags) do
if flag.icon == flagType then
flag:setVisible(checked)
end
end
end

function Cyclopedia.showAllFlags(checked)
local size = UI.InformationBase.InternalBase.DisplayBase.MarkList:getChildCount()
if checked then
for i = 0, size do
local flag = UI.InformationBase.InternalBase.DisplayBase.MarkList[i]
if flag then
flag:setChecked(true)
end
end
else
for i = 0, size do
local flag = UI.InformationBase.InternalBase.DisplayBase.MarkList[i]
if flag then
flag:setChecked(false)
end
local list = UI.InformationBase.InternalBase.DisplayBase.MarkList
for i = 0, list:getChildCount() - 1 do
local btn = list:getChildByIndex(i)
if btn then
btn:setChecked(checked)
end
end
local minimapWidget = UI.MapBase.minimap
for _, flag in pairs(minimapWidget.flags) do
flag:setVisible(checked)
end
end

function Cyclopedia.moveMap(widget)
Expand Down Expand Up @@ -127,6 +278,26 @@ function ConvertLayer(Value)
end
end

refreshVirtualFloors = function()
local mark = UI.InformationBase.InternalBase.NavigationBase.layersMark
mark:setMarginTop(((virtualFloor + 1) * 4) - 3)
UI.InformationBase.InternalBase.NavigationBase.automapLayers:setImageClip((virtualFloor * 14) .. ' 0 14 67')

-- Update player marker (cross) visibility on the minimap
local cross = UI.MapBase.minimap.cross
if cross then
if virtualFloor > SEA_FLOOR then
cross:setVisible(false)
elseif virtualFloor < SEA_FLOOR then
cross:setVisible(true)
cross:setIconColor("#ffffff80")
else
cross:setVisible(true)
cross:setIconColor("#ffffffff")
end
end
end

function Cyclopedia.onUpdateCameraPosition()
local player = g_game.getLocalPlayer()
if not player then
Expand All @@ -147,7 +318,16 @@ function Cyclopedia.onUpdateCameraPosition()
minimapWidget:setCrossPosition(player:getPosition(), true)
end

local prevFloor = virtualFloor
virtualFloor = pos.z

-- When the player changes floor, sync the visual floor indicator and
-- satellite/map view toggle (e.g. walking underground disables Surface View).
if virtualFloor ~= prevFloor then
ensureViewFloorsLoaded()
refreshVirtualFloors()
updateViewMode()
end
end

function Cyclopedia.onClickRoseButton(dir)
Expand Down Expand Up @@ -178,19 +358,16 @@ function Cyclopedia.setZooom(zoom)
end
end

local function refreshVirtualFloors()
UI.InformationBase.InternalBase.NavigationBase.layersMark:setMarginTop(((virtualFloor + 1) * 4) - 3)
UI.InformationBase.InternalBase.NavigationBase.automapLayers:setImageClip((virtualFloor * 14) .. ' 0 14 67')
end

function Cyclopedia.downLayer()
if virtualFloor == 15 then
return
end

UI.MapBase.minimap:floorDown(1)
virtualFloor = virtualFloor + 1
ensureViewFloorsLoaded()
refreshVirtualFloors()
updateViewMode()
end

function Cyclopedia.upLayer()
Expand All @@ -200,5 +377,7 @@ function Cyclopedia.upLayer()

UI.MapBase.minimap:floorUp(1)
virtualFloor = virtualFloor - 1
ensureViewFloorsLoaded()
refreshVirtualFloors()
updateViewMode()
end
Loading