z_mirror/ChangeInserterDropLane

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

QuezlerPrevent toggling long inserter mode through ghosts (#14)e2c14e2

factorio-2.0
7.6 KiB261 linesraw
1if script.active_mods["bobinserters"] then
2  return
3end
4
5local flib_gui = require("__flib__.gui")
6local flib_math = require("__flib__.math")
7local flib_position = require("__flib__.position")
8
9local near_vectors = {
10  [defines.direction.north] = { 0.01, -0.2 },
11  [defines.direction.east] = { 0.2, 0.01 },
12  [defines.direction.south] = { -0.01, 0.2 },
13  [defines.direction.west] = { -0.2, -0.01 },
14}
15local far_vectors = {
16  [defines.direction.north] = { 0.0, 0.2 },
17  [defines.direction.east] = { -0.2, 0.0 },
18  [defines.direction.south] = { 0.0, -0.2 },
19  [defines.direction.west] = { 0.2, 0.0 },
20}
21
22--- @param entity LuaEntity
23local function get_prototype(entity)
24  return entity.type == "entity-ghost" and entity.ghost_prototype or entity.prototype
25end
26
27--- @param entity LuaEntity
28local function get_entity_type(entity)
29  return entity.type == "entity-ghost" and entity.ghost_type or entity.type
30end
31
32--- @param entity LuaEntity
33local function is_compatible(entity)
34  local prototype = get_prototype(entity)
35  return prototype.allow_custom_vectors and not string.find(prototype.name, "%-?miniloader%-inserter")
36end
37
38--- @param entity LuaEntity
39local function get_is_far(entity)
40  local drop_pos_vector = flib_position.sub(entity.drop_position, entity.position)
41  local vector_length = flib_math.sqrt(drop_pos_vector.x * drop_pos_vector.x + drop_pos_vector.y * drop_pos_vector.y)
42  return vector_length % 1 < 0.5, drop_pos_vector
43end
44
45--- @param entity LuaEntity
46--- @param is_far boolean
47local function change_mode_fx(entity, is_far)
48  for _, player in pairs(game.players) do
49    if player.surface == entity.surface then
50      player.create_local_flying_text({
51        text = is_far and { "message.cidl-drop-far" } or { "message.cidl-drop-near" },
52        color = { r = 1, g = 0.5, b = 0.25 },
53        position = entity.position,
54      })
55    end
56  end
57  -- Welding sound
58  game.play_sound({
59    path = "cidl-welding",
60    position = entity.position,
61    volume_modifier = 1.0,
62  })
63  -- Welding particle
64  entity.surface.create_particle({
65    name = "cidl-welding",
66    position = { entity.position.x, entity.position.y + 1 },
67    movement = { 0.0, -0.05 },
68    height = 1.0,
69    vertical_speed = 0.015,
70    frame_speed = 1,
71  })
72end
73
74--- @param player LuaPlayer
75--- @param entity LuaEntity
76local function change_lane(player, entity)
77  if not get_prototype(entity).allow_custom_vectors then
78    player.create_local_flying_text({
79      text = { "message.cidl-cannot-change-drop-lane" },
80      create_at_cursor = true,
81    })
82    player.play_sound({ path = "utility/cannot_build" })
83    return
84  end
85
86  -- Change lane
87  local is_far, drop_pos_vector = get_is_far(entity)
88  local vectors = is_far and near_vectors or far_vectors
89  local vector = vectors[entity.direction]
90  entity.drop_position = {
91    entity.position.x + flib_math.round(drop_pos_vector.x) + vector[1],
92    entity.position.y + flib_math.round(drop_pos_vector.y) + vector[2],
93  }
94
95  -- Special effects
96  change_mode_fx(entity, not is_far)
97end
98
99--- @param e EventData.on_gui_switch_state_changed
100local function on_droplane_switch_state_changed(e)
101  local player = game.get_player(e.player_index)
102  if not player or player.opened_gui_type ~= defines.gui_type.entity then
103    return
104  end
105  local entity = player.opened --[[@as LuaEntity?]]
106  if not entity or not is_compatible(entity) then
107    return
108  end
109  change_lane(player, entity)
110  e.element.switch_state = get_is_far(entity) and "right" or "left"
111end
112
113--- @param player LuaPlayer
114--- @param entity LuaEntity
115local function create_gui(player, entity)
116  local window = player.gui.relative.cidl_window
117  if window then
118    window.destroy()
119  end
120  flib_gui.add(player.gui.relative, {
121    type = "frame",
122    name = "cidl_window",
123    caption = { "gui.cidl-drop-lane" },
124    anchor = {
125      gui = defines.relative_gui_type.inserter_gui,
126      position = defines.relative_gui_position.right,
127    },
128    {
129      type = "frame",
130      style = "inside_shallow_frame_with_padding",
131      {
132        type = "switch",
133        left_label_caption = { "gui.cidl-near" },
134        right_label_caption = { "gui.cidl-far" },
135        switch_state = get_is_far(entity) and "right" or "left",
136        handler = {
137          [defines.events.on_gui_switch_state_changed] = on_droplane_switch_state_changed,
138        },
139      },
140    },
141  })
142end
143
144--- @param e EventData.CustomInputEvent
145local function on_change_lane(e)
146  local player = game.get_player(e.player_index)
147  if not player then
148    return
149  end
150  local entity = player.selected
151  if not entity then
152    return
153  end
154  if not player.can_reach_entity(entity) then
155    player.create_local_flying_text({
156      text = { "cant-reach" },
157      create_at_cursor = true,
158    })
159    player.play_sound({ path = "utility/cannot_build" })
160    return
161  end
162  change_lane(player, entity)
163end
164
165--- @param e EventData.on_pre_entity_settings_pasted
166local function on_pre_entity_settings_pasted(e)
167  local source, destination = e.source, e.destination
168  if not source.valid or not destination.valid then
169    return
170  end
171  if get_entity_type(source) ~= "inserter" or get_entity_type(destination) ~= "inserter" then
172    return
173  end
174  if not get_prototype(destination).allow_custom_vectors then
175    return
176  end
177  storage.temp_inserter_settings[destination.unit_number] = {
178    drop_position = destination.drop_position,
179    pickup_position = destination.pickup_position,
180  }
181end
182
183--- @param e EventData.on_entity_settings_pasted
184local function on_entity_settings_pasted(e)
185  local source, destination = e.source, e.destination
186  if not source.valid or not destination.valid then
187    return
188  end
189  if get_entity_type(source) ~= "inserter" or get_entity_type(destination) ~= "inserter" then
190    return
191  end
192  if not get_prototype(destination).allow_custom_vectors then
193    return
194  end
195
196  local destination_unit_number = destination.unit_number --[[@as uint]]
197  local temp_settings = storage.temp_inserter_settings[destination_unit_number]
198  if not temp_settings then
199    return
200  end
201  storage.temp_inserter_settings[destination_unit_number] = nil
202
203  destination.drop_position = temp_settings.drop_position
204  destination.pickup_position = temp_settings.pickup_position
205
206  local player = game.get_player(e.player_index)
207  if not player then
208    return
209  end
210  local destination_is_far = get_is_far(destination)
211  if destination_is_far ~= get_is_far(source) then
212    change_lane(player, destination)
213  end
214end
215
216--- @param e EventData.on_gui_opened
217local function on_gui_opened(e)
218  local player = game.get_player(e.player_index)
219  if not player then
220    return
221  end
222  if player.opened_gui_type ~= defines.gui_type.entity then
223    return
224  end
225  local entity = player.opened --[[@as LuaEntity?]]
226  if not entity or not is_compatible(entity) then
227    return
228  end
229  create_gui(player, entity)
230end
231
232--- @class InserterSettings
233--- @field drop_position MapPosition
234--- @field pickup_position MapPosition
235
236local function on_init()
237  --- @type table<uint, InserterSettings?>
238  storage.temp_inserter_settings = {}
239end
240
241flib_gui.add_handlers({ on_droplane_switch_state_changed = on_droplane_switch_state_changed })
242flib_gui.handle_events()
243
244script.on_init(on_init)
245script.on_configuration_changed(on_init)
246script.on_event("cidl-change-lane", on_change_lane)
247script.on_event(defines.events.on_gui_opened, on_gui_opened)
248script.on_event(defines.events.on_pre_entity_settings_pasted, on_pre_entity_settings_pasted)
249script.on_event(defines.events.on_entity_settings_pasted, on_entity_settings_pasted)
250
251-- For simulations
252remote.add_interface("ChangeInserterDropLane_simulation", {
253  --- @param player LuaPlayer
254  change_selected_lane = function(player)
255    local selected = player.selected
256    if not selected then
257      return
258    end
259    change_lane(player, selected)
260  end,
261})