z_mirror/PipeVisualizer

git clone https://git.raiguard.me/z_mirror/PipeVisualizer

Caleb HeuerStore pipe connections in global, split and clean up code9371b54

2.0
6.2 KiB240 linesraw
1local flib_bounding_box = require("__flib__/bounding-box")
2local flib_position = require("__flib__/position")
3
4local iterator = require("__PipeVisualizer__/scripts/iterator")
5
6-- In the source code, 200 is defined as the maximum viewable distance, but in reality it's around 220
7-- Map editor is 3x that, but we will ignore that for now
8-- Add five for a comfortable margin
9local max_overlay_size = 220 + 5
10
11--- @alias UnitNumber uint
12
13--- @class Overlay
14--- @field background RenderObjectID
15--- @field dimensions DisplayResolution
16--- @field last_position MapPosition?
17--- @field player LuaPlayer
18
19--- @param self Overlay
20--- @param position MapPosition
21local function get_areas(self, position)
22  local dimensions = self.dimensions
23  local last_position = self.last_position
24  if not last_position then
25    return { flib_bounding_box.from_dimensions(position, dimensions.width, dimensions.height) }
26  end
27  local position_x = position.x
28  local position_y = position.y
29  local radius_x = dimensions.width / 2
30  local radius_y = dimensions.height / 2
31
32  local delta = flib_position.sub(position, last_position)
33  --- @type BoundingBox[]
34  local areas = {}
35
36  if delta.x < 0 then
37    areas[#areas + 1] = {
38      left_top = {
39        x = position_x - radius_x,
40        y = position_y - radius_y,
41      },
42      right_bottom = {
43        x = last_position.x - radius_x,
44        y = position_y + radius_y,
45      },
46    }
47  elseif delta.x > 0 then
48    areas[#areas + 1] = {
49      left_top = {
50        x = last_position.x + radius_x,
51        y = position_y - radius_y,
52      },
53      right_bottom = {
54        x = position_x + radius_x,
55        y = position_y + radius_y,
56      },
57    }
58  end
59
60  if delta.y < 0 then
61    areas[#areas + 1] = {
62      left_top = {
63        x = position_x - radius_x,
64        y = position_y - radius_y,
65      },
66      right_bottom = {
67        x = position_x + radius_x,
68        y = last_position.y - radius_y,
69      },
70    }
71  elseif delta.y > 0 then
72    areas[#areas + 1] = {
73      left_top = {
74        x = position_x - radius_x,
75        y = last_position.y + radius_y,
76      },
77      right_bottom = {
78        x = position_x + radius_x,
79        y = position_y + radius_y,
80      },
81    }
82  end
83
84  return areas
85end
86
87--- @param player LuaPlayer
88--- @return DisplayResolution
89local function get_dimensions(player)
90  local resolution = player.display_resolution
91  local divisor = math.max(resolution.width, resolution.height) / max_overlay_size
92  resolution.width = resolution.width / divisor
93  resolution.height = resolution.height / divisor
94  return resolution
95end
96
97--- @param self Overlay
98--- @param area BoundingBox
99local function visualize_area(self, area)
100  local entities = self.player.surface.find_entities_filtered({
101    area = area,
102    force = self.player.force,
103    type = {
104      "assembling-machine",
105      "boiler",
106      "fluid-turret",
107      "furnace",
108      "generator",
109      "infinity-pipe",
110      "inserter",
111      "lab",
112      "loader",
113      "loader-1x1",
114      "mining-drill",
115      "offshore-pump",
116      "pipe",
117      "pipe-to-ground",
118      "pump",
119      "reactor",
120      "rocket-silo",
121      "storage-tank",
122    },
123  })
124  for _, entity in pairs(entities) do
125    iterator.request(entity, self.player.index, true)
126  end
127end
128
129--- @param self Overlay
130local function update_overlay(self)
131  local position = flib_position.floor(self.player.position)
132  if self.last_position and flib_position.eq(position, self.last_position) then
133    return
134  end
135  rendering.set_target(self.background, position)
136  rendering.set_x_scale(self.background, self.dimensions.width)
137  rendering.set_y_scale(self.background, self.dimensions.height)
138
139  local areas = get_areas(self, position)
140
141  self.last_position = position
142
143  for _, area in pairs(areas) do
144    visualize_area(self, area)
145  end
146end
147
148--- @param player LuaPlayer
149--- @return Overlay
150local function create_overlay(player)
151  local background = rendering.draw_sprite({
152    sprite = "pv-entity-box",
153    tint = {},
154    render_layer = "191",
155    target = player.position,
156    surface = player.surface,
157    players = { player },
158  })
159  --- @type Overlay
160  local self = {
161    background = background,
162    dimensions = get_dimensions(player),
163    player = player,
164  }
165  global.overlay[player.index] = self
166  return self
167end
168
169--- @param self Overlay
170local function destroy_overlay(self)
171  rendering.destroy(self.background)
172  iterator.clear_all(self.player.index)
173  global.overlay[self.player.index] = nil
174end
175
176--- @param e EventData.CustomInputEvent|EventData.on_lua_shortcut
177local function on_toggle_overlay(e)
178  if (e.input_name or e.prototype_name) ~= "pv-toggle-overlay" then
179    return
180  end
181  local player = game.get_player(e.player_index)
182  if not player then
183    return
184  end
185  local entity = player.selected
186  if entity and entity.prototype.belt_speed and script.active_mods["belt-visualizer"] then
187    return
188  end
189  local cursor_stack = player.cursor_stack
190  if cursor_stack and cursor_stack.valid_for_read and (cursor_stack.is_blueprint_book or cursor_stack.is_blueprint) then
191    return
192  end
193  local self = global.overlay[e.player_index]
194  if self then
195    destroy_overlay(self)
196  else
197    local overlay = create_overlay(player)
198    iterator.clear_all(e.player_index)
199    update_overlay(overlay)
200  end
201  player.set_shortcut_toggled("pv-toggle-overlay", global.overlay[e.player_index] ~= nil)
202end
203
204--- @param e EventData.on_player_changed_position
205local function on_player_changed_position(e)
206  local self = global.overlay[e.player_index]
207  if self then
208    update_overlay(self)
209  end
210end
211
212--- @param e EventData.on_player_display_resolution_changed
213local function on_player_display_resolution_changed(e)
214  if not global.overlay then
215    return
216  end
217  local self = global.overlay[e.player_index]
218  if not self then
219    return
220  end
221  self.dimensions = get_dimensions(self.player)
222  self.last_position = nil
223  update_overlay(self)
224end
225
226local overlay = {}
227
228function overlay.on_init()
229  --- @type table<uint, Overlay>
230  global.overlay = {}
231end
232
233overlay.events = {
234  [defines.events.on_lua_shortcut] = on_toggle_overlay,
235  [defines.events.on_player_changed_position] = on_player_changed_position,
236  [defines.events.on_player_display_resolution_changed] = on_player_display_resolution_changed,
237  ["pv-toggle-overlay"] = on_toggle_overlay,
238}
239
240return overlay