z_mirror/PipeVisualizer

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

Caleb HeuerFix warningseb997d6

main
7.6 KiB242 linesraw
1local flib_bounding_box = require("__flib__.bounding-box")
2local flib_math = require("__flib__.math")
3local flib_position = require("__flib__.position")
4local flib_queue = require("__flib__.queue")
5
6--- @alias RenderObjectID uint64
7
8local function reset()
9  rendering.clear(script.mod_name)
10  --- @type Queue<RenderObjectID>
11  global.render_objects = flib_queue.new()
12end
13
14--- @param id RenderObjectID
15local function clear_sprite(id)
16  if not rendering.is_valid(id) then
17    return
18  end
19  rendering.set_visible(id, false)
20  flib_queue.push_back(global.render_objects, id)
21end
22
23--- @param args LuaRendering.draw_sprite_param
24--- @return RenderObjectID
25local function draw_sprite(args)
26  --- @type RenderObjectID?
27  local id
28  repeat
29    id = flib_queue.pop_front(global.render_objects)
30  until not id or rendering.is_valid(id) and rendering.get_surface(id).index == args.surface
31
32  if not id then
33    return rendering.draw_sprite(args)
34  end
35
36  rendering.set_sprite(id, args.sprite)
37  rendering.set_color(id, args.tint)
38  rendering.set_x_scale(id, args.x_scale or 1)
39  rendering.set_y_scale(id, args.y_scale or 1)
40  rendering.set_render_layer(id, args.render_layer)
41  rendering.set_orientation(id, args.orientation or 0)
42  rendering.set_target(id, args.target)
43  rendering.set_players(id, args.players)
44  rendering.set_visible(id, true)
45
46  return id
47end
48
49local layers = {
50  arrow = "195",
51  line = "194",
52  underground = "193",
53  entity = "192",
54}
55
56local pipe_types = {
57  ["infinity-pipe"] = true,
58  ["pipe-to-ground"] = true,
59  ["pipe"] = true,
60}
61
62local encoded_directions = {
63  [defines.direction.north] = 1,
64  [defines.direction.east] = 2,
65  [defines.direction.south] = 4,
66  [defines.direction.west] = 8,
67}
68
69--- @type Color
70local default_color = { r = 0.4, g = 0.4, b = 0.4 }
71--- @type FluidSystemData
72local default_fluid_system = { color = default_color, from_hover = false, order = flib_math.max_uint }
73
74local renderer = {}
75
76--- @param it Iterator
77--- @param entity_data EntityData
78function renderer.draw(it, entity_data)
79  local is_complex_type = not pipe_types[entity_data.entity.type]
80  if is_complex_type then
81    local box = flib_bounding_box.resize(entity_data.entity.selection_box, -0.1)
82    entity_data.shape = draw_sprite({
83      sprite = "pv-entity-box",
84      tint = default_color,
85      x_scale = flib_bounding_box.width(box),
86      y_scale = flib_bounding_box.height(box),
87      render_layer = layers.entity,
88      target = flib_bounding_box.center(box),
89      surface = entity_data.entity.surface_index,
90      players = { it.player_index },
91    })
92  else
93    local box = flib_bounding_box.ceil(entity_data.entity.selection_box)
94    entity_data.shape = draw_sprite({
95      sprite = "pv-pipe-connections-0",
96      tint = default_color,
97      x_scale = flib_bounding_box.width(box),
98      y_scale = flib_bounding_box.height(box),
99      render_layer = layers.line,
100      target = entity_data.entity.position,
101      surface = entity_data.entity.surface_index,
102      players = { it.player_index },
103    })
104  end
105  local shape_fluid_system = default_fluid_system
106  local encoded_connections = 0
107  for fluid_system_id, connections in pairs(entity_data.connections) do
108    local fluid_system_data = it.systems[fluid_system_id]
109    if not fluid_system_data then
110      goto continue
111    end
112    if fluid_system_data.order < shape_fluid_system.order then
113      shape_fluid_system = fluid_system_data
114    end
115    local objects = entity_data.connection_objects[fluid_system_id]
116    if not objects then
117      objects = {}
118      entity_data.connection_objects[fluid_system_id] = objects
119    end
120    for _, connection in pairs(connections) do
121      if not connection.target then
122        goto inner_continue
123      end
124
125      local direction = connection.direction
126      if is_complex_type then
127        if connection.flow_direction == "input" then
128          direction = (direction + 4) % 8 -- Opposite
129        end
130        local sprite = "pv-fluid-arrow-" .. connection.flow_direction
131        if connection.flow_direction ~= "input-output" and not pipe_types[connection.target_owner.type] then
132          sprite = "pv-fluid-arrow"
133        end
134        objects[#objects + 1] = draw_sprite({
135          sprite = sprite,
136          tint = fluid_system_data.color,
137          render_layer = layers.arrow,
138          orientation = direction / 8,
139          target = connection.shape_position,
140          surface = entity_data.entity.surface_index,
141          players = { it.player_index },
142        })
143      else
144        encoded_connections = bit32.bor(encoded_connections, encoded_directions[direction])
145      end
146
147      if connection.connection_type == "underground" then
148        local target_data = it.entities[
149          connection.target_owner.unit_number --[[@as uint]]
150        ]
151        if not target_data then
152          goto inner_continue
153        end
154        local target_fluid_system_connections = target_data.connections[fluid_system_id]
155        if not target_fluid_system_connections then
156          goto inner_continue
157        end
158        local target_connection_data = target_fluid_system_connections[connection.target_pipe_connection_index]
159
160        local target_position = target_connection_data.position
161        local distance = flib_position.distance(connection.position, target_connection_data.position)
162        for i = 1, distance - 1 do
163          local target = flib_position.lerp(connection.position, target_position, i / distance)
164          objects[#objects + 1] = draw_sprite({
165            sprite = "pv-underground-connection",
166            tint = fluid_system_data.color,
167            render_layer = layers.underground,
168            orientation = direction / 8,
169            target = target,
170            surface = entity_data.entity.surface_index,
171            players = { it.player_index },
172          })
173        end
174      end
175
176      ::inner_continue::
177    end
178
179    ::continue::
180  end
181  if entity_data.shape then
182    rendering.set_color(entity_data.shape, shape_fluid_system.color)
183    if encoded_connections > 0 then
184      rendering.set_sprite(entity_data.shape, "pv-pipe-connections-" .. encoded_connections)
185    end
186  end
187end
188
189--- @param entity_data EntityData
190function renderer.clear(entity_data)
191  clear_sprite(entity_data.shape)
192  entity_data.shape = nil
193  for _, objects in pairs(entity_data.connection_objects) do
194    for _, id in pairs(objects) do
195      clear_sprite(id)
196    end
197  end
198  entity_data.connection_objects = {}
199end
200
201--- @param iterator Iterator
202--- @param entity_data EntityData
203--- @param fluid_system_id FluidSystemID
204--- @return boolean should_remove
205function renderer.clear_system(iterator, entity_data, fluid_system_id)
206  local objects = entity_data.connection_objects[fluid_system_id]
207  if objects then
208    for _, id in pairs(objects) do
209      clear_sprite(id)
210    end
211    entity_data.connection_objects[fluid_system_id] = nil
212  end
213  local should_remove = not next(entity_data.connection_objects)
214  if should_remove then
215    clear_sprite(entity_data.shape)
216    entity_data.shape = nil
217  else
218    renderer.update_shape_color(iterator, entity_data)
219  end
220  return should_remove
221end
222
223--- @param iterator Iterator
224--- @param entity_data EntityData
225function renderer.update_shape_color(iterator, entity_data)
226  --- @type FluidSystemData
227  local shape_fluid_system = { color = default_color, from_hover = false, order = flib_math.max_uint }
228  for fluid_system_id in pairs(entity_data.connection_objects) do
229    local fluid_system_data = iterator.systems[fluid_system_id]
230    if fluid_system_data and fluid_system_data.order < shape_fluid_system.order then
231      shape_fluid_system = fluid_system_data
232    end
233  end
234  rendering.set_color(entity_data.shape, shape_fluid_system.color)
235end
236
237renderer.on_init = reset
238renderer.on_configuration_changed = reset
239
240renderer.reset = reset
241
242return renderer