z_mirror/MouseOverConstruction

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

Caleb HeuerWIP7a81c3d

insertion
4.8 KiB167 linesraw
1--- @param box1 BoundingBox
2--- @param box2 BoundingBox
3local function bounding_boxes_equal(box1, box2)
4  return box1.left_top.x == box2.left_top.x
5    and box1.left_top.y == box2.left_top.y
6    and box1.right_bottom.x == box2.right_bottom.x
7    and box1.right_bottom.y == box2.right_bottom.y
8end
9
10--- @class common
11local common = {}
12
13--- Builds an entity over the top of an existing entity.
14--- @param player LuaPlayer
15--- @param entity LuaEntity
16--- @param new_prototype LuaEntityPrototype
17--- @param new_quality LuaQualityPrototype
18--- @return LuaEntity?
19function common.build(player, entity, new_prototype, new_quality)
20  local item_spec = common.get_item_to_place(player, new_prototype, new_quality)
21  if not item_spec then
22    return nil
23  end
24
25  local bounding_box = entity.bounding_box
26  local surface = entity.surface
27  local position = entity.position
28  local direction = entity.direction
29  local quality = entity.quality
30  local force = entity.force
31
32  local new_entity = player.surface.create_entity({
33    name = new_prototype.name,
34    position = entity.position,
35    direction = entity.direction,
36    quality = new_quality,
37    force = entity.force,
38    fast_replace = true,
39    player = player,
40    character = player.character,
41    raise_built = true,
42    create_build_effect_smoke = true,
43    spawn_decorations = true,
44    move_stuck_players = true,
45    item = item_spec.item,
46    register_plant = true,
47    --- Undergound belt
48    type = entity.type == "underground-belt" and entity.belt_to_ground_type or nil,
49  })
50  if not new_entity or not new_entity.valid then
51    -- Some mods will immediately replace the entity with a different one. Let's see if we can detect this.
52    for _, replaced_entity in
53      pairs(surface.find_entities_filtered({
54        direction = direction,
55        position = position,
56        quality = quality.name,
57        force = force,
58      }))
59    do
60      if
61        replaced_entity.type ~= "entity-ghost"
62        and replaced_entity.type ~= "tile-ghost"
63        and bounding_boxes_equal(replaced_entity.bounding_box, bounding_box)
64      then
65        new_entity = replaced_entity
66        break
67      else
68        return
69      end
70    end
71  end
72
73  item_spec.item.count = item_spec.item.count - item_spec.count
74
75  player.play_sound({
76    path = "entity-build/" .. new_entity.name,
77    position = new_entity.position,
78  })
79
80  return new_entity
81end
82
83--- @param player LuaPlayer
84--- @param entity LuaEntity
85--- @param new_prototype LuaEntityPrototype
86--- @return boolean
87function common.check_can_place_entity(player, entity, new_prototype)
88  -- TODO: Remote interface for mods to conditionally decide if placement is allowed.
89  if
90    not player.can_place_entity({
91      name = new_prototype.name,
92      position = entity.position,
93      direction = entity.direction,
94    })
95  then
96    storage.recheck_on_move[player.index] = true
97    return false
98  end
99  return true
100end
101
102--- @param item LuaItemStack
103--- @param name string
104--- @param quality LuaQualityPrototype
105--- @param min_count uint?
106--- @param max_count uint?
107local function does_item_match(item, name, quality, min_count, max_count)
108  min_count = min_count or 0
109  max_count = max_count or math.huge
110
111  return item.valid_for_read
112    and item.name == name
113    and item.quality == quality
114    and item.count >= min_count
115    and item.count <= max_count
116end
117
118--- Returns the first LuaItemStack with the given name and quality. If count is provided, the returned item stack will
119--- contain at least count items.
120--- @param player LuaPlayer
121--- @param name string
122--- @param quality LuaQualityPrototype
123--- @param min_count uint?
124--- @param max_count uint?
125--- @return fun(): LuaItemStack?
126function common.get_item(player, name, quality, min_count, max_count)
127  local cursor_stack_used = false
128  local cursor_stack = player.cursor_stack
129  local inventory = player.get_main_inventory()
130  local i = 0
131  return function()
132    if not cursor_stack_used then
133      cursor_stack_used = true
134      if cursor_stack and does_item_match(cursor_stack, name, quality, min_count, max_count) then
135        return cursor_stack
136      end
137    end
138
139    if not inventory then
140      return
141    end
142
143    while i <= #inventory do
144      i = i + 1
145      local stack = inventory[i]
146      if does_item_match(stack, name, quality, min_count, max_count) then
147        return stack
148      end
149    end
150  end
151end
152
153--- Returns the first LuaItemStack that can build the given entity and quality.
154--- @param player LuaPlayer
155--- @param prototype LuaEntityPrototype
156--- @param quality LuaQualityPrototype
157--- @return {item: LuaItemStack, count: uint}?
158function common.get_item_to_place(player, prototype, quality)
159  for _, item_to_place in pairs(prototype.items_to_place_this or {}) do
160    local item = common.get_item(player, item_to_place.name, quality, item_to_place.count)()
161    if item then
162      return { item = item, count = item_to_place.count }
163    end
164  end
165end
166
167return common