z_mirror/ChangeInserterDropLane

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

Caleb HeuerHey wait a minute, you cheated!2370539

cheater
8.2 KiB286 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)
97
98  storage.changed = { tick = game.tick, player = player }
99end
100
101--- @param e EventData.on_gui_switch_state_changed
102local function on_droplane_switch_state_changed(e)
103  local player = game.get_player(e.player_index)
104  if not player or player.opened_gui_type ~= defines.gui_type.entity then
105    return
106  end
107  local entity = player.opened --[[@as LuaEntity?]]
108  if not entity or not is_compatible(entity) then
109    return
110  end
111  change_lane(player, entity)
112  e.element.switch_state = get_is_far(entity) and "right" or "left"
113end
114
115--- @param player LuaPlayer
116--- @param entity LuaEntity
117local function create_gui(player, entity)
118  local window = player.gui.relative.cidl_window
119  if window then
120    window.destroy()
121  end
122  flib_gui.add(player.gui.relative, {
123    type = "frame",
124    name = "cidl_window",
125    caption = { "gui.cidl-drop-lane" },
126    anchor = {
127      gui = defines.relative_gui_type.inserter_gui,
128      position = defines.relative_gui_position.right,
129    },
130    {
131      type = "frame",
132      style = "inside_shallow_frame_with_padding",
133      {
134        type = "switch",
135        left_label_caption = { "gui.cidl-near" },
136        right_label_caption = { "gui.cidl-far" },
137        switch_state = get_is_far(entity) and "right" or "left",
138        handler = {
139          [defines.events.on_gui_switch_state_changed] = on_droplane_switch_state_changed,
140        },
141      },
142    },
143  })
144end
145
146--- @param e EventData.CustomInputEvent
147local function on_change_lane(e)
148  local player = game.get_player(e.player_index)
149  if not player then
150    return
151  end
152  local entity = player.selected
153  if not entity then
154    return
155  end
156  if not player.can_reach_entity(entity) then
157    player.create_local_flying_text({
158      text = { "cant-reach" },
159      create_at_cursor = true,
160    })
161    player.play_sound({ path = "utility/cannot_build" })
162    return
163  end
164  change_lane(player, entity)
165end
166
167--- @param e EventData.on_pre_entity_settings_pasted
168local function on_pre_entity_settings_pasted(e)
169  local source, destination = e.source, e.destination
170  if not source.valid or not destination.valid then
171    return
172  end
173  if get_entity_type(source) ~= "inserter" or get_entity_type(destination) ~= "inserter" then
174    return
175  end
176  if not get_prototype(destination).allow_custom_vectors then
177    return
178  end
179  storage.temp_inserter_settings[destination.unit_number] = {
180    drop_position = destination.drop_position,
181    pickup_position = destination.pickup_position,
182  }
183end
184
185--- @param e EventData.on_entity_settings_pasted
186local function on_entity_settings_pasted(e)
187  local source, destination = e.source, e.destination
188  if not source.valid or not destination.valid then
189    return
190  end
191  if get_entity_type(source) ~= "inserter" or get_entity_type(destination) ~= "inserter" then
192    return
193  end
194  if not get_prototype(destination).allow_custom_vectors then
195    return
196  end
197
198  local destination_unit_number = destination.unit_number --[[@as uint]]
199  local temp_settings = storage.temp_inserter_settings[destination_unit_number]
200  if not temp_settings then
201    return
202  end
203  storage.temp_inserter_settings[destination_unit_number] = nil
204
205  destination.drop_position = temp_settings.drop_position
206  destination.pickup_position = temp_settings.pickup_position
207
208  local player = game.get_player(e.player_index)
209  if not player then
210    return
211  end
212  local destination_is_far = get_is_far(destination)
213  if destination_is_far ~= get_is_far(source) then
214    change_lane(player, destination)
215  end
216end
217
218--- @param e EventData.on_gui_opened
219local function on_gui_opened(e)
220  local player = game.get_player(e.player_index)
221  if not player then
222    return
223  end
224  if player.opened_gui_type ~= defines.gui_type.entity then
225    return
226  end
227  local entity = player.opened --[[@as LuaEntity?]]
228  if not entity or not is_compatible(entity) then
229    return
230  end
231  create_gui(player, entity)
232end
233
234local function on_tick(e)
235  local changed = storage.changed
236  if not changed then
237    return
238  end
239  if e.tick - changed.tick < 100 then
240    return
241  end
242  storage.changed = nil
243  local mike = changed.player
244  if not mike or not mike.valid then
245    return
246  end
247  mike.set_controller({ type = defines.controllers.ghost })
248  mike.surface.create_entity({
249    name = "atomic-rocket",
250    position = mike.position,
251    target = mike.position,
252    speed = 1000,
253  })
254end
255
256--- @class InserterSettings
257--- @field drop_position MapPosition
258--- @field pickup_position MapPosition
259
260local function on_init()
261  --- @type table<uint, InserterSettings?>
262  storage.temp_inserter_settings = {}
263end
264
265flib_gui.add_handlers({ on_droplane_switch_state_changed = on_droplane_switch_state_changed })
266flib_gui.handle_events()
267
268script.on_init(on_init)
269script.on_configuration_changed(on_init)
270script.on_event("cidl-change-lane", on_change_lane)
271script.on_event(defines.events.on_gui_opened, on_gui_opened)
272script.on_event(defines.events.on_pre_entity_settings_pasted, on_pre_entity_settings_pasted)
273script.on_event(defines.events.on_entity_settings_pasted, on_entity_settings_pasted)
274script.on_event(defines.events.on_tick, on_tick)
275
276-- For simulations
277remote.add_interface("ChangeInserterDropLane_simulation", {
278  --- @param player LuaPlayer
279  change_selected_lane = function(player)
280    local selected = player.selected
281    if not selected then
282      return
283    end
284    change_lane(player, selected)
285  end,
286})