diff --git a/control.lua b/control.lua index c6f1c1c..4e258d7 100644 --- a/control.lua +++ b/control.lua @@ -67,6 +67,7 @@ script.on_event(defines.events.on_gui_click, TLBE.GUI.onClick) script.on_event(defines.events.on_gui_selection_state_changed, TLBE.GUI.onSelected) script.on_event(defines.events.on_gui_text_changed, TLBE.GUI.onTextChanged) script.on_event(defines.events.on_gui_checked_state_changed, TLBE.GUI.onStateChanged) +script.on_event(defines.events.on_gui_confirmed, TLBE.GUI.onGuiConfirmed) script.on_event(defines.events.on_runtime_mod_setting_changed, TLBE.Config.reload) script.on_event(defines.events.on_player_created, on_player_created) script.on_event(defines.events.on_player_joined_game, on_player_created) diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index 9e9d718..49006ef 100644 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -14,6 +14,7 @@ tracker-area=Area Tracker tracker-base=Base Tracker tracker-player=Player Tracker tracker-rocket=Rocket Tracker +tracker-cityBlock=City Block Tracker [gui] button-base-recalculate=Recalculate base size @@ -37,6 +38,12 @@ label-type=Type: label-until-build=Follow until build label-zoom=Zoom: label-transitionperiod=Transition period: +label-cityblock-size=Block size: +label-cityblock-offset=Offset: +label-cityblock-block=Block: +label-cityblock-currentblock=Current: +label-cityblock-blockScale=Scale: +label-cityblock-centerOnPlayer=Select block: label-transition-speedgain=Transition speed gain: item-add-tracker= item-new-tracker= @@ -66,6 +73,18 @@ tracker-area-player=Use player coordinates to fill in this corner. tracker-smooth=Enable/Disable smooth behavior of the camera when this tracker is active. tracker-until-build=Follow the player until an entity is build. tracker-wrong-surface=Tracker is disabled because tracker surface differs from camera surface. +tracker-cityblock-size=The size of a single city block in tiles. +tracker-cityblock-size-x=The width of a single city block in tiles. +tracker-cityblock-size-y=The height of a single city block in tiles. +tracker-cityblock-offset=The number of tiles to shift the city blocks grid. +tracker-cityblock-offset-x=The number of tiles right to shift the city blocks grid. +tracker-cityblock-offset-y=The number of tiles down to shift the city blocks grid. +tracker-cityblock-currentblock=The city block to focus on, counting in whole blocks. +tracker-cityblock-currentblock-x=The position of the current city block, counting blocks rightwards. +tracker-cityblock-currentblock-y=The position of the current city block, counting blocks downwards. +tracker-cityblock-blockScale=How many city blocks to zoom out, centered on the current block. +tracker-cityblock-blockScale-value=A zoom of 3 will capture the current block and all of the neighbours. to set. +tracker-cityblock-player=Use player coordinates to select the current city block. [controls] tlbe-main-window-toggle=Open/Close camera settings diff --git a/prototypes/signal.lua b/prototypes/signal.lua index e610097..3a7170b 100644 --- a/prototypes/signal.lua +++ b/prototypes/signal.lua @@ -103,7 +103,6 @@ data:extend({ } }, icon_size = 32, - tint = { 0, 1, 0 }, icon_mipmaps = 1, subgroup = "tlbe-virtual-signal", order = "g" diff --git a/scripts/gui.lua b/scripts/gui.lua index bd20317..287d455 100644 --- a/scripts/gui.lua +++ b/scripts/gui.lua @@ -1,11 +1,12 @@ local GUI = { - allTrackers = { "area", "base", "player", "rocket" }, - allTrackersLabels = { { "tracker-area" }, { "tracker-base" }, { "tracker-player" }, { "tracker-rocket" } }, + allTrackers = { "area", "base", "cityblock", "player", "rocket" }, + allTrackersLabels = { { "tracker-area" }, { "tracker-base" }, { "tracker-cityBlock" }, { "tracker-player" }, { "tracker-rocket" } }, allTrackersLabelsMap = { area = { "tracker-area" }, base = { "tracker-base" }, + cityBlock = { "tracker-cityBlock" }, player = { "tracker-player" }, - rocket = { "tracker-rocket" } + rocket = { "tracker-rocket" }, } } @@ -212,6 +213,12 @@ function GUI.onClick(event) GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) end + elseif event.element.name == "tlbe-tracker-cityblock-player" then + local selectedTracker = playerSettings.trackers[playerSettings.guiPersist.selectedTracker] + Tracker.focusCityBlock(selectedTracker, player.position) + + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) + GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) else local _, index _, _, index = event.element.name:find("^camera_tracker_(%d+)$") @@ -504,6 +511,8 @@ end function GUI.onTextChanged(event) ---@type playerSettings local playerSettings = global.playerSettings[event.player_index] + local selectedTracker = playerSettings.trackers[playerSettings.guiPersist.selectedTracker] + if event.element.name == "camera-name" then Camera.setName(playerSettings.cameras[playerSettings.guiPersist.selectedCamera], event.element.text) GUI.updateCameraList(playerSettings.gui, playerSettings.guiPersist, playerSettings.cameras) @@ -555,7 +564,6 @@ function GUI.onTextChanged(event) elseif event.element.name == "tlbe-tracker-top" then local value = tonumber(event.element.text) if value ~= nil then - local selectedTracker = playerSettings.trackers[playerSettings.guiPersist.selectedTracker] -- Note that game origin is top-left, so top is min and bottom is max selectedTracker.minPos.y = value Tracker.areaUpdateCenterAndSize(selectedTracker) @@ -566,7 +574,6 @@ function GUI.onTextChanged(event) elseif event.element.name == "tlbe-tracker-bottom" then local value = tonumber(event.element.text) if value ~= nil then - local selectedTracker = playerSettings.trackers[playerSettings.guiPersist.selectedTracker] -- Note that game origin is top-left, so top is min and bottom is max selectedTracker.maxPos.y = value Tracker.areaUpdateCenterAndSize(selectedTracker) @@ -577,7 +584,6 @@ function GUI.onTextChanged(event) elseif event.element.name == "tlbe-tracker-left" then local value = tonumber(event.element.text) if value ~= nil then - local selectedTracker = playerSettings.trackers[playerSettings.guiPersist.selectedTracker] selectedTracker.minPos.x = value Tracker.areaUpdateCenterAndSize(selectedTracker) @@ -587,10 +593,78 @@ function GUI.onTextChanged(event) elseif event.element.name == "tlbe-tracker-right" then local value = tonumber(event.element.text) if value ~= nil then - local selectedTracker = playerSettings.trackers[playerSettings.guiPersist.selectedTracker] selectedTracker.maxPos.x = value Tracker.areaUpdateCenterAndSize(selectedTracker) + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) + GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) + end + elseif event.element.name == "tlbe-tracker-cityblock-size-x" and event.element.text ~= nil then + local value = tonumber(event.element.text) + if value ~= nil and value >= 1 then + selectedTracker.cityBlock.blockSize.x = math.floor(value) + Tracker.recalculateCityBlock(selectedTracker) + + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) + GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) + end + elseif event.element.name == "tlbe-tracker-cityblock-size-y" and event.element.text ~= nil then + local value = tonumber(event.element.text) + if value ~= nil and value >= 1 then + selectedTracker.cityBlock.blockSize.y = math.floor(value) + Tracker.recalculateCityBlock(selectedTracker) + + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) + GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) + end + elseif event.element.name == "tlbe-tracker-cityblock-offset-x" and event.element.text ~= nil then + local value = tonumber(event.element.text) + if value ~= nil then + selectedTracker.cityBlock.blockOffset.x = math.floor(value) + Tracker.recalculateCityBlock(selectedTracker) + + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) + GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) + end + elseif event.element.name == "tlbe-tracker-cityblock-offset-y" and event.element.text ~= nil then + local value = tonumber(event.element.text) + if value ~= nil then + selectedTracker.cityBlock.blockOffset.y = math.floor(value) + Tracker.recalculateCityBlock(selectedTracker) + + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) + GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) + end + elseif event.element.name == "tlbe-tracker-cityblock-currentblock-x" and event.element.text ~= nil then + local value = tonumber(event.element.text) + if value ~= nil then + selectedTracker.cityBlock.currentBlock.x = math.floor(value) + Tracker.recalculateCityBlock(selectedTracker) + + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) + GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) + end + elseif event.element.name == "tlbe-tracker-cityblock-currentblock-y" and event.element.text ~= nil then + local value = tonumber(event.element.text) + if value ~= nil then + selectedTracker.cityBlock.currentBlock.y = math.floor(value) + Tracker.recalculateCityBlock(selectedTracker) + + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) + GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) + end + end +end + +function GUI.onGuiConfirmed(event) + local playerSettings = global.playerSettings[event.player_index] + if event.element.name == "tlbe-tracker-cityblock-blockScale-value" and event.element.text ~= nil then + local selectedTracker = playerSettings.trackers[playerSettings.guiPersist.selectedTracker] + local value = tonumber(event.element.text) + if value ~= nil then + selectedTracker.cityBlock.blockScale = math.floor(value * 100)/100 + Tracker.recalculateCityBlock(selectedTracker) + GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) GUI.updateTrackerInfo(playerSettings.gui.trackerInfo, selectedTracker) end @@ -1426,6 +1500,110 @@ function GUI.createTrackerConfigAndInfo(trackerInfo, tracker) tooltip = { "tooltip.tracker-until-build" }, state = tracker.untilBuild } + elseif tracker.type == "cityblock" then + trackerInfo.add { + type = "label", + caption = { "gui.label-cityblock-size" }, + tooltip = { "tooltip.tracker-cityblock-size" }, + style = "description_property_name_label" + } + local sizeFlow = trackerInfo.add { type = "flow", name = "cityblock-size" } + sizeFlow.add { + type = "textfield", + name = "tlbe-tracker-cityblock-size-x", + style = "tlbe_config_half_width_textfield", + tooltip = { "tooltip.tracker-cityblock-size-x" }, + numeric = true, + allow_negative = true + } + sizeFlow.add { type = "label", caption = "/", style = "tlbe_config_half_width_label" } + sizeFlow.add { + type = "textfield", + name = "tlbe-tracker-cityblock-size-y", + style = "tlbe_config_half_width_textfield", + tooltip = { "tooltip.tracker-cityblock-size-y" }, + numeric = true, + allow_negative = true + } + + trackerInfo.add { + type = "label", + caption = { "gui.label-cityblock-offset" }, + tooltip = { "tooltip.tracker-cityblock-offset" }, + style = "description_property_name_label" + } + local offsetFlow = trackerInfo.add { type = "flow", name = "cityblock-offset" } + offsetFlow.add { + type = "textfield", + name = "tlbe-tracker-cityblock-offset-x", + style = "tlbe_config_half_width_textfield", + tooltip = { "tooltip.tracker-cityblock-offset-x" }, + numeric = true, + allow_negative = true + } + offsetFlow.add { type = "label", caption = "/", style = "tlbe_config_half_width_label" } + offsetFlow.add { + type = "textfield", + name = "tlbe-tracker-cityblock-offset-y", + style = "tlbe_config_half_width_textfield", + tooltip = { "tooltip.tracker-cityblock-offset-y" }, + numeric = true, + allow_negative = true + } + + trackerInfo.add { + type = "label", + caption = { "gui.label-cityblock-currentblock" }, + tooltip = { "tooltip.tracker-cityblock-currentblock" }, + style = "description_property_name_label" + } + local blockFlow = trackerInfo.add { type = "flow", name = "cityblock-block" } + blockFlow.add { + type = "textfield", + name = "tlbe-tracker-cityblock-currentblock-x", + style = "tlbe_config_half_width_textfield", + tooltip = { "tooltip.tracker-cityblock-currentblock-x" }, + numeric = true, + allow_negative = true + } + blockFlow.add { type = "label", caption = "/", style = "tlbe_config_half_width_label" } + blockFlow.add { + type = "textfield", + name = "tlbe-tracker-cityblock-currentblock-y", + style = "tlbe_config_half_width_textfield", + tooltip = { "tooltip.tracker-cityblock-currentblock-y" }, + numeric = true, + allow_negative = true + } + + trackerInfo.add { + type = "label", + caption = { "gui.label-cityblock-blockScale" }, + tooltip = { "tooltip.tracker-cityblock-blockScale" }, + style = "description_property_name_label" + } + trackerInfo.add { + type = "textfield", + name = "tlbe-tracker-cityblock-blockScale-value", + style = "tlbe_config_half_width_textfield", + tooltip = { "tooltip.tracker-cityblock-blockScale-value" }, + numeric = true, + allow_decimal = true, + allow_negative = false + } + + trackerInfo.add { + type = "label", + caption = { "gui.label-cityblock-centerOnPlayer" }, + style = "description_property_name_label" + } + trackerInfo.add { + type = "sprite-button", + name = "tlbe-tracker-cityblock-player", + tooltip = { "tooltip.tracker-cityblock-player" }, + sprite = "utility/show_player_names_in_map_view_black", + style = "tlbe_config_button" + } end end @@ -1447,6 +1625,8 @@ function GUI.createTrackerConfigAndInfo(trackerInfo, tracker) GUI.updateTrackerInfo(trackerInfo, tracker) end +---@param trackerInfo any +---@param tracker Tracker.tracker function GUI.updateTrackerConfig(trackerInfo, tracker) if tracker == nil then trackerInfo["tracker-name"].enabled = false @@ -1488,6 +1668,25 @@ function GUI.updateTrackerConfig(trackerInfo, tracker) blFlow["tlbe-tracker-left"].style = style elseif tracker.type == "player" then trackerInfo["tracker-untilbuild"].state = tracker.untilBuild + elseif tracker.type == "cityblock" then + local cityBlock = tracker.cityBlock + if cityBlock == nil then + return + end + + local sizeFlow = trackerInfo["cityblock-size"] + sizeFlow["tlbe-tracker-cityblock-size-x"].text = string.format("%d", cityBlock.blockSize.x) + sizeFlow["tlbe-tracker-cityblock-size-y"].text = string.format("%d", cityBlock.blockSize.y) + + local offsetFlow = trackerInfo["cityblock-offset"] + offsetFlow["tlbe-tracker-cityblock-offset-x"].text = string.format("%d", cityBlock.blockOffset.x) + offsetFlow["tlbe-tracker-cityblock-offset-y"].text = string.format("%d", cityBlock.blockOffset.y) + + local blockFlow = trackerInfo["cityblock-block"] + blockFlow["tlbe-tracker-cityblock-currentblock-x"].text = string.format("%d", cityBlock.currentBlock.x) + blockFlow["tlbe-tracker-cityblock-currentblock-y"].text = string.format("%d", cityBlock.currentBlock.y) + + trackerInfo["tlbe-tracker-cityblock-blockScale-value"].text = string.format("%g", cityBlock.blockScale) end end end diff --git a/scripts/tracker.lua b/scripts/tracker.lua index f4b4e15..9550848 100644 --- a/scripts/tracker.lua +++ b/scripts/tracker.lua @@ -17,6 +17,25 @@ local Tracker = {} --- @field size MapPosition.0 Size of the tracker area (Calculated from minPos and maxPos) --- @field minPos MapPosition.0 Bottom/Left of tracker area --- @field maxPos MapPosition.0 TopRight of tracker area +--- @field cityBlock Tracker.cityBlock? City block vital statistics, used when type="cityblock" + +--- @class Tracker.cityBlock +--- @field blockSize TilePosition The size of a single city block +--- @field blockOffset TilePosition The offset where the "first" city block begins +--- @field currentBlock TilePosition An abuse of the TilePosition type to number the blocks (1,4 is one block over and 4 blocks up) +--- @field blockScale number How many blocks to hold in view (1=1 block, 1.5=1.5 blocks etc.) +Tracker.cityBlock = {} + +---@return Tracker.cityBlock +function Tracker.cityBlock:new() + local cityBlock = {} + + cityBlock.blockSize = { x=32, y=32 } + cityBlock.blockOffset = { x=0, y=0 } + cityBlock.currentBlock = { x=0, y=0 } + cityBlock.blockScale = 1.5 + return cityBlock +end --- Create and setup a new tracker --- @param trackerType string Type of the new tracker @@ -55,11 +74,80 @@ function Tracker.newTracker(trackerType, trackerList) newTracker.userCanEnable = false newTracker.enabled = false newTracker.realtimeCamera = true + elseif trackerType == "cityblock" then + -- cityblock-specific data + newTracker.cityBlock = Tracker.cityBlock:new() + Tracker.recalculateCityBlock(newTracker) end return newTracker end +---find the city block containing the position and relocate to that one +---@param tracker Tracker.tracker +---@param pos MapPosition.0|MapPosition.1 +function Tracker.focusCityBlock(tracker, pos) + local cityBlock = tracker.cityBlock + if cityBlock == nil then + return + end + + cityBlock.currentBlock = { + x = math.floor((pos.x - cityBlock.blockOffset.x) / cityBlock.blockSize.x), + y = math.floor((pos.y - cityBlock.blockOffset.y) / cityBlock.blockSize.y), + } + + Tracker.recalculateCityBlock(tracker) +end + +--- recalculates the tracker vital stats from the city block vital stats +---@param tracker Tracker.tracker +function Tracker.recalculateCityBlock(tracker) + if tracker.type ~= "cityblock" then + return + end + + local cityBlock = tracker.cityBlock + if cityBlock == nil then + return + end + + + local width = cityBlock.blockSize.x + local height = cityBlock.blockSize.y + local widthDiam = width * cityBlock.blockScale + local heightDiam = height * cityBlock.blockScale + local widthRad = widthDiam / 2 + local heightRad = heightDiam / 2 + + tracker.centerPos = { + x = cityBlock.blockOffset.x + cityBlock.currentBlock.x * width + width / 2, + y = cityBlock.blockOffset.y + cityBlock.currentBlock.y * height + height / 2 + } + + tracker.size = { x = widthDiam, y = heightDiam } + + local minPosX = tracker.centerPos.x - widthRad + local minPosY = tracker.centerPos.y - heightRad + local maxPosX = tracker.centerPos.x + widthRad + local maxPosY = tracker.centerPos.y + heightRad + + if tracker.minPos == nil or tracker.maxPos == nil or minPosX ~= tracker.minPos.x or minPosY ~= tracker.minPos.y or maxPosX ~= tracker.maxPos.x or maxPosY ~= tracker.maxPos.y then + tracker.minPos = { + x = minPosX, + y = minPosY + } + + tracker.maxPos = { + x = tracker.centerPos.x + widthRad, + y = tracker.centerPos.y + heightRad + } + + Tracker.changed(tracker) + end +end + + -- Update tracker state (if needed) --- @param tracker Tracker.tracker --- @param player LuaPlayer diff --git a/tests/test-suite.lua b/tests/test-suite.lua index 42013fe..2053db4 100644 --- a/tests/test-suite.lua +++ b/tests/test-suite.lua @@ -11,6 +11,7 @@ require("tracker") require("tracker-base") require("tracker-player") require("tracker-rocket") +require("tracker-cityblock") -- Integration tests require("camera") diff --git a/tests/tracker-cityblock.lua b/tests/tracker-cityblock.lua new file mode 100644 index 0000000..2a724b9 --- /dev/null +++ b/tests/tracker-cityblock.lua @@ -0,0 +1,75 @@ +package.path = package.path .. ";../?.lua" +local TLBE = { + Config = require("scripts.config"), + Tracker = require("scripts.tracker") +} + +local lu = require("luaunit") + +TestTrackerCityBlock = {} + +function TestTrackerCityBlock:Setup() + -- mock Factorio provided globals + global = {} + game = { + surfaces = { { name = "nauvis" }, { name = "other-surface" } } + } + + -- mock TLBE tables + global.playerSettings = { + TLBE.Config.newPlayerSettings({ position = { x = 0, y = 0 } }) + } + + -- Create city block tracker + self.cityblockTracker = TLBE.Tracker.newTracker("cityblock", global.playerSettings[1].trackers) + for k, v in pairs( + { + changeId = 1 + } + ) do + self.cityblockTracker[k] = v + end +end + +function TestTrackerCityBlock:Focus() + -- chunk-aligned city block is chunk-aligned + self.cityblockTracker.cityBlock.blockSize.x = 32 + self.cityblockTracker.cityBlock.blockSize.y = 32 + + + TLBE.Tracker.focusCityBlock(self.cityblockTracker, {x=0,y=0} ) + lu.assertEquals(self.cityblockTracker.cityBlock.currentBlock, {x=0,y=0}, "focus on tile zero should be in the block zero") + lu.assertEquals(self.cityblockTracker.centerPos, {x=16,y=16}, "focus on tile zero should center on the zero chunk") + + TLBE.Tracker.focusCityBlock(self.cityblockTracker, {x=14,y=19} ) + lu.assertEquals(self.cityblockTracker.cityBlock.currentBlock, {x=0,y=0}, "focus on a tile within zero chunk should be in the block zero") + lu.assertEquals(self.cityblockTracker.centerPos, {x=16,y=16}, "focus on a tile within zero chunk should center on the zero chunk") + + TLBE.Tracker.focusCityBlock(self.cityblockTracker, {x=32,y=32} ) + lu.assertEquals(self.cityblockTracker.cityBlock.currentBlock, {x=0,y=0}, "focus on tile {32,32} should be in the block {1,1}") + lu.assertEquals(self.cityblockTracker.centerPos, {x=32+16,y=32+16}, "focus on tile {32,32} should center on the {1,1} chunk") +end + +function TestTrackerCityBlock:scale() + -- zooming doesn't recenter, but does affect width and height + self.cityblockTracker.cityBlock.blockSize.x = 32 + self.cityblockTracker.cityBlock.blockSize.y = 32 + self.cityblockTracker.cityBlock.currentBlock.x = 0 + self.cityblockTracker.cityBlock.currentBlock.y = 0 + TLBE.Tracker.recalculateCityBlock(self.cityblockTracker) + local oldCenterPos = { + x=self.cityblockTracker.centerPos.x, + y=self.cityblockTracker.centerPos.y + } + + + self.cityblockTracker.cityBlock.blockScale = 1 + TLBE.Tracker.recalculateCityBlock(self.cityblockTracker) + lu.assertEquals(self.cityblockTracker.size, {x=32,y=32}, "A blockScale of 1 should give a size matching the block size") + lu.assertEquals(self.cityblockTracker.centerPos, oldCenterPos, "Rescaling doesn't recenter") + + self.cityblockTracker.cityBlock.blockScale = 2 + TLBE.Tracker.recalculateCityBlock(self.cityblockTracker) + lu.assertEquals(self.cityblockTracker.size, {x=64,y=64}, "A blockScale of 2 should give a size twice the block size") + lu.assertEquals(self.cityblockTracker.centerPos, oldCenterPos, "Rescaling doesn't recenter") +end \ No newline at end of file