z_mirror/QuickbarTemplates

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

Caleb HeuerSimplifications and formattingaef62a9

trunk
7.8 KiB272 linesraw
1--- @param player LuaPlayer
2local function create_gui(player)
3  local window = player.gui.screen.add({ type = "frame", name = "qt_window", style = "slot_window_frame" })
4  local inner_panel = window.add({ type = "frame", name = "qt_inner_panel", style = "shortcut_bar_inner_panel" })
5  local export_button = inner_panel.add({
6    type = "sprite-button",
7    name = "qt_export_button",
8    style = "shortcut_bar_button_blue",
9    sprite = "qt-export-blueprint-white",
10    tooltip = { "qt-gui.export" },
11  })
12  export_button.style.padding = 4
13  local import_button = inner_panel.add({
14    type = "sprite-button",
15    name = "qt_import_button",
16    style = "shortcut_bar_button_blue",
17    sprite = "qt-import-blueprint-white",
18    tooltip = { "qt-gui.import" },
19  })
20  import_button.style.padding = 4
21  window.visible = false
22  return { window = window, export_button = export_button, import_button = import_button }
23end
24
25--- @param player LuaPlayer
26local function setup_player(player)
27  local data = create_gui(player)
28  storage.players[player.index] = data
29end
30
31--- @param player LuaPlayer
32--- @param window LuaGuiElement
33local function set_gui_location(player, window)
34  local resolution = player.display_resolution
35  local scale = player.display_scale
36  window.location = {
37    x = (resolution.width / 2) - ((56 + 258) * scale),
38    y = (resolution.height - (56 * scale)),
39  }
40end
41
42--- @param position MapPosition
43--- @param zero_position MapPosition
44--- @return integer
45local function position_to_index(position, zero_position)
46  return math.floor(position.x - zero_position.x) + math.floor(zero_position.y - position.y) * 10 + 1
47end
48
49--- @param entities BlueprintEntity[]
50local function get_zero_position(entities)
51  local result = { x = entities[1].position.x, y = entities[1].position.y }
52  for i = 2, 100 do
53    local position = entities[i].position
54    if result.x > position.x then
55      result.x = position.x
56    end
57    if result.y < position.y then
58      result.y = position.y
59    end
60  end
61  return result
62end
63
64local ignored_item_names = {
65  ["blueprint"] = true,
66  ["blueprint-book"] = true,
67  ["upgrade-planner"] = true,
68  ["deconstruction-planner"] = true,
69}
70
71--- @param player LuaPlayer
72local function export_quickbar(player)
73  local entities = {}
74  local pos = { x = -4, y = 4 }
75  for i = 1, 100 do
76    entities[i] = {
77      entity_number = i,
78      name = "constant-combinator",
79      position = { x = pos.x, y = pos.y },
80    }
81
82    local item = player.get_quick_bar_slot(i)
83    if item and not ignored_item_names[item.name] then
84      entities[i].control_behavior = {
85        filters = {
86          {
87            count = 1,
88            index = 1,
89            signal = {
90              type = "item",
91              name = item.name,
92              quality = item.quality,
93            },
94          },
95        },
96      }
97    end
98
99    pos.x = pos.x + 1
100    if pos.x == 6 then
101      pos.x = -4
102      pos.y = pos.y - 1
103    end
104  end
105  return entities
106end
107
108--- @param player LuaPlayer
109--- @param entities BlueprintEntity[]
110--- @param ignore_empty boolean
111--- @return boolean # If the transfer was successful.
112local function import_quickbar(player, entities, ignore_empty)
113  if #entities ~= 100 then
114    return false
115  end
116
117  --- @type ({name: string, quality: string}?)[]
118  local filters = {}
119  local zero_position = get_zero_position(entities)
120  for i = 1, 100 do
121    local entity = entities[i]
122    if not entity or entity.name ~= "constant-combinator" then
123      return false
124    end
125    local filter_index = position_to_index(entity.position, zero_position)
126    local cb = entity.control_behavior
127    if not cb then
128      goto continue
129    end
130    local sections_outer = cb.sections --- @diagnostic disable-line:undefined-field
131    if not sections_outer then
132      goto continue
133    end
134    local sections_inner = sections_outer.sections
135    if not sections_inner then
136      goto continue
137    end
138    local first_section = sections_inner[1]
139    if not first_section then
140      goto continue
141    end
142    local section_filters = first_section.filters
143    if not section_filters then
144      goto continue
145    end
146    local first_filter = section_filters[1]
147    if not first_filter then
148      goto continue
149    end
150    filters[filter_index] = { name = first_filter.name, quality = first_filter.quality }
151    ::continue::
152  end
153
154  for i = 1, 100 do
155    local filter = filters[i]
156    if not ignore_empty or filter then
157      player.set_quick_bar_slot(i, filter)
158    end
159  end
160
161  return true
162end
163
164--- @param player LuaPlayer
165--- @return BlueprintEntity[]?
166local function get_blueprint_entities(player)
167  if not player.is_cursor_blueprint() then
168    return
169  end
170  local cursor_stack = player.cursor_stack
171  if cursor_stack and cursor_stack.valid_for_read and cursor_stack.type == "blueprint" then
172    return cursor_stack.get_blueprint_entities()
173  end
174  local cursor_record = player.cursor_record
175  if cursor_record and cursor_record.type == "blueprint" then
176    return cursor_record.get_blueprint_entities()
177  end
178end
179
180-- EVENT HANDLERS
181
182script.on_init(function()
183  storage.players = {}
184  for _, player in pairs(game.players) do
185    setup_player(player)
186  end
187end)
188
189script.on_configuration_changed(function()
190  for i, player_table in pairs(storage.players) do
191    player_table.window.destroy()
192    local player = game.get_player(i)
193    if player then
194      storage.players[i] = create_gui(player)
195    else
196      storage.players[i] = nil
197    end
198  end
199end)
200
201script.on_event(defines.events.on_player_created, function(e)
202  local player = game.players[e.player_index]
203  setup_player(player)
204
205  local template = player.mod_settings["qt-default-template"].value --[[@as string]]
206  if template == "" then
207    return
208  end
209
210  local temp_inventory = game.create_inventory(1)
211  temp_inventory.insert({ name = "blueprint" })
212  local blueprint = temp_inventory[1]
213
214  if blueprint.import_stack(template) == 0 then
215    local entities = blueprint.get_blueprint_entities()
216    if not entities or not import_quickbar(player, entities, false) then
217      player.print({ "qt-message.invalid-default-blueprint" })
218    end
219  else
220    player.print({ "qt-message.invalid-default-blueprint" })
221  end
222
223  temp_inventory.destroy()
224end)
225
226--- @param e EventData.on_player_cursor_stack_changed
227local function on_cursor_stack_changed(e)
228  local player = game.players[e.player_index]
229  local gui = storage.players[e.player_index]
230  if not player.is_cursor_blueprint() then
231    gui.window.visible = false
232    return
233  end
234  set_gui_location(player, gui.window)
235  local entities = get_blueprint_entities(player)
236  if entities then
237    gui.export_button.visible = false
238    gui.import_button.visible = true
239  else
240    gui.export_button.visible = true
241    gui.import_button.visible = false
242  end
243  gui.window.visible = true
244end
245script.on_event(defines.events.on_player_cursor_stack_changed, on_cursor_stack_changed)
246
247script.on_event(
248  { defines.events.on_player_display_resolution_changed, defines.events.on_player_display_scale_changed },
249  --- @param e EventData.on_player_display_resolution_changed|EventData.on_player_display_scale_changed
250  function(e)
251    set_gui_location(game.players[e.player_index], storage.players[e.player_index].window)
252  end
253)
254
255script.on_event(defines.events.on_gui_click, function(e)
256  if e.element.name == "qt_export_button" then
257    local player = game.players[e.player_index]
258    local stack = player.cursor_stack
259    if stack and stack.valid_for_read and stack.name == "blueprint" then
260      stack.set_blueprint_entities(export_quickbar(player))
261      on_cursor_stack_changed(e) --- @diagnostic disable-line:param-type-mismatch
262    end
263  elseif e.element.name == "qt_import_button" then
264    local player = game.players[e.player_index]
265    local entities = get_blueprint_entities(player)
266    if entities then
267      if not import_quickbar(player, entities, e.shift) then
268        player.print({ "qt-message.invalid-blueprint" })
269      end
270    end
271  end
272end)