z_mirror/MouseOverConstruction

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

Caleb HeuerWIP7a81c3d

insertion
5.7 KiB203 linesraw
1local common = require("scripts.common")
2
3--- @generic T
4--- @param array T[]
5--- @param pred fun(elem: T): boolean
6local function remove_if(array, pred)
7  local to_remove = {}
8  for i = 1, #array do
9    if pred(array[i]) then
10      to_remove[#to_remove + 1] = i
11    end
12  end
13  -- Remove from back to front to keep the indices valid
14  for i = #to_remove, 1, -1 do
15    table.remove(array, to_remove[i])
16  end
17end
18
19--- @param base LocalisedString
20--- @param child LocalisedString
21local function append_localised_string(base, child)
22  --- @cast base -?
23
24  local len = #base
25
26  if len < 18 then
27    base[len + 1] = child
28    return
29  end
30
31  if len == 18 then
32    base[19] = { "" }
33  end
34
35  append_localised_string(base[19], child)
36end
37
38--- @alias Deltas table<string, {name: string, quality: string, count: integer}>
39
40--- @param deltas Deltas
41--- @param name string
42--- @param quality string
43--- @param count integer
44local function add_delta(deltas, name, quality, count)
45  local key = name .. "/" .. quality
46  local delta = deltas[key]
47  if not delta then
48    delta = { name = name, quality = quality, count = 0 }
49    deltas[key] = delta
50  end
51  delta.count = delta.count + count
52end
53
54--- @param player LuaPlayer
55--- @param entity LuaEntity
56return function(player, entity)
57  if not player.mod_settings["moc-enable-insertion"].value then
58    return
59  end
60
61  if entity.type == "entity-ghost" then
62    return
63  end
64
65  local proxy = entity.item_request_proxy
66  if not proxy then
67    return
68  end
69
70  --- @type Deltas
71  local deltas = {}
72
73  local entity_insert_plan = proxy.insert_plan
74  remove_if(entity_insert_plan, function(insert_plan)
75    -- TODO: Equipment grids
76    local inventories = insert_plan.items.in_inventory
77    if not inventories then
78      return false
79    end
80
81    -- Despite the docs, these reads are not LuaObjects because insert_plan is a PropertyTree...
82    local name = insert_plan.id.name --[[@as string]]
83    local quality_name = insert_plan.id.quality or "normal" --[[@as string]]
84    local quality = prototypes.quality[quality_name]
85
86    remove_if(inventories, function(inventory_position)
87      local inventory = entity.get_inventory(inventory_position.inventory)
88      if not inventory then
89        return false
90      end
91
92      if not inventory_position.count then
93        inventory_position.count = 1
94      end
95
96      while inventory_position.count > 0 do
97        local item = common.get_item(player, name, quality)()
98        if not item then
99          return false
100        end
101
102        -- For some ungodly reason, stack is zero indexed!
103        local destination_stack = inventory[inventory_position.stack + 1]
104        local to_transfer = math.min(inventory_position.count, item.count)
105        assert(destination_stack.transfer_stack(item, to_transfer), "Inventory transfer failed")
106        inventory_position.count = inventory_position.count - to_transfer
107        -- Negate because the deltas are relative to the player inventory.
108        add_delta(deltas, name, quality_name, -to_transfer)
109      end
110
111      return inventory_position.count == 0
112    end)
113
114    return not next(inventories)
115  end)
116  proxy.insert_plan = entity_insert_plan
117
118  local entity_removal_plan = proxy.removal_plan
119  remove_if(entity_removal_plan, function(removal_plan)
120    -- TODO: Equipment grids
121    local inventories = removal_plan.items.in_inventory
122    if not inventories then
123      return false
124    end
125
126    -- Despite the docs, these reads are not LuaObjects because removal_plan is a PropertyTree...
127    local name = removal_plan.id.name --[[@as string]]
128    local item_prototype = prototypes.item[name]
129    local quality_name = removal_plan.id.quality or "normal" --[[@as string]]
130    local quality = prototypes.quality[quality_name]
131
132    remove_if(inventories, function(inventory_position)
133      local inventory = entity.get_inventory(inventory_position.inventory)
134      if not inventory then
135        return false
136      end
137
138      local count_to_remove = inventory_position.count or 1
139
140      -- For some ungodly reason, stack is zero indexed!
141      local source_stack = inventory[inventory_position.stack + 1]
142      -- This probably can't ever happen.
143      if source_stack.count < count_to_remove then
144        return false
145      end
146
147      local destination_stack_iterator = common.get_item(player, name, quality)
148      repeat
149        local destination_stack = destination_stack_iterator()
150        if not destination_stack then
151          return false
152        end
153
154        local to_transfer = math.min(inventory_position.count, destination_stack.count)
155        assert(destination_stack.transfer_stack(destination_stack, to_transfer), "Inventory transfer failed")
156        inventory_position.count = inventory_position.count - to_transfer
157        -- Negate because the deltas are relative to the player inventory.
158        add_delta(deltas, name, quality_name, -to_transfer)
159      until count_to_remove <= 0 or not destination_stack
160
161      return inventory_position.count == 0
162    end)
163
164    return not next(inventories)
165  end)
166  proxy.removal_plan = entity_removal_plan
167
168  table.sort(deltas, function(a, b)
169    return a.count > b.count
170  end)
171
172  --- @type LocalisedString
173  local output = { "" }
174
175  for _, delta in pairs(deltas) do
176    if delta.count == 0 then
177      goto continue
178    end
179
180    player.play_sound({ path = "item-drop/" .. delta.name })
181    local delta_string = tostring(delta.count)
182    if delta.count > 0 then
183      delta_string = "+" .. delta_string
184    end
185    append_localised_string(output, {
186      "",
187      delta_string,
188      " ",
189      "[item=" .. delta.name .. "] ",
190      prototypes.item[delta.name].localised_name,
191      "\n",
192    })
193
194    ::continue::
195  end
196
197  player.create_local_flying_text({
198    position = entity.position,
199    text = output,
200  })
201
202  return true
203end