Aleksei-bird 7c9c708c92 Первый фикс
Пачки некоторых позиций увеличены
2024-03-01 20:54:33 +03:00

399 lines
11 KiB
Lua

local common = require("layouts.common")
local simple = require("layouts.simple")
local mpp_util = require("mpp_util")
local builder = require("builder")
local floor, ceil = math.floor, math.ceil
local min, max = math.min, math.max
local EAST, NORTH, SOUTH, WEST = mpp_util.directions()
---@class SuperCompactLayout : SimpleLayout
local layout = table.deepcopy(simple)
layout.name = "super_compact"
layout.translation = {"mpp.settings_layout_choice_super_compact"}
layout.restrictions.miner_near_radius = {1, 1}
layout.restrictions.miner_far_radius = {1, 10e3}
layout.restrictions.uses_underground_belts = true
layout.restrictions.pole_omittable = true
layout.restrictions.pole_width = {1, 1}
layout.restrictions.pole_length = {5, 10e3}
layout.restrictions.pole_supply_area = {2.5, 10e3}
layout.restrictions.coverage_tuning = true
layout.restrictions.lamp_available = false
layout.restrictions.module_available = true
layout.restrictions.pipe_available = false
---@class SuperCompactState : SimpleState
---@field miner_bounds any
-- Validate the selection
---@param self SuperCompactLayout
---@param state SimpleState
function layout:validate(state)
local c = state.coords
if (state.direction_choice == "west" or state.direction_choice == "east") then
if c.h < 3 then
return nil, {"mpp.msg_miner_err_1_w", 3}
end
else
if c.w < 3 then
return nil, {"mpp.msg_miner_err_1_h", 3}
end
end
return true
end
---@param self SuperCompactLayout
---@param state SimpleState
---@return PlacementAttempt
function layout:_placement_attempt(state, shift_x, shift_y)
local grid = state.grid
local size, near, fullsize = state.miner.size, state.miner.near, state.miner.full_size
local neighbor_sum = 0
local far_neighbor_sum = 0
local miners, postponed = {}, {}
local simple_density = 0
local real_density = 0
local leech_sum = 0
local empty_space = 0
local lane_layout = {}
--@param tile GridTile
--local function heuristic(tile) return tile.neighbor_count > 2 end
local heuristic = self:_get_miner_placement_heuristic(state)
local function miner_stagger(start_x, start_y, direction, stagger_step, mark_lane)
local miner_index = 1
for y = 1 + shift_y + start_y, state.coords.th + 2, size * 3 + 1 do
if mark_lane then lane_layout[#lane_layout+1] = {y=y} end
for x = 1 + shift_x + start_x, state.coords.tw + 2, size * 2 do
local tile = grid:get_tile(x, y)
local center = grid:get_tile(x+near, y+near) --[[@as GridTile]]
local miner = {
tile = tile,
center = center,
direction = direction,
stagger = stagger_step,
line = miner_index,
}
if center.far_neighbor_count > 0 then
if heuristic(center) then
miners[#miners+1] = miner
neighbor_sum = neighbor_sum + center.neighbor_count
far_neighbor_sum = far_neighbor_sum + center.far_neighbor_count
empty_space = empty_space + (size^2) - center.neighbor_count
simple_density = simple_density + center.neighbor_count / (size ^ 2)
real_density = real_density + center.far_neighbor_count / (fullsize ^ 2)
leech_sum = leech_sum + max(0, center.far_neighbor_count - center.neighbor_count)
else
postponed[#postponed+1] = miner
end
end
end
miner_index = miner_index + 1
end
end
miner_stagger(0, -2, "south", 1)
miner_stagger(3, 0, "east", 1, true)
miner_stagger(0, 2, "north", 1)
-- the redundant calculation makes it easier to find the stagger offset
miner_stagger(0+size, -2+size+2, "south", 2)
miner_stagger(3-size, 0+size+2, "east", 2, true)
miner_stagger(0+size, 2+size+2, "north", 2)
local result = {
sx=shift_x, sy=shift_y,
miners = miners,
miner_count=#miners,
lane_layout=lane_layout,
postponed = postponed,
neighbor_sum = neighbor_sum,
far_neighbor_sum = far_neighbor_sum,
leech_sum=leech_sum,
simple_density = simple_density,
real_density = real_density,
empty_space=empty_space,
unconsumed_count = 0,
postponed_count = 0,
}
common.process_postponed(state, result, miners, postponed)
return result
end
---@param self SimpleLayout
---@param state SimpleState
---@return CallbackState
function layout:prepare_miner_layout(state)
local grid = state.grid
local builder_miners = {}
state.builder_miners = builder_miners
for _, miner in ipairs(state.best_attempt.miners) do
local center = miner.center
grid:build_miner(center.x, center.y)
-- used for deconstruction, not ghost placement
builder_miners[#builder_miners+1] = {
thing="miner",
extent_=state.miner.size,
grid_x = miner.center.x,
grid_y = miner.center.y,
padding_pre = state.miner.near,
padding_post = state.miner.near,
}
--[[ debug visualisation - miner placement
local color = {1, 0, 0}
if miner.direction == "west" then
color = {0, 1, 0}
elseif miner.direction == "north" then
color = {0, 0, 1}
end
local rect_color = miner.stagger == 1 and {1, 1, 1} or {0, 0, 0}
local off = state.miner.size / 2 - 0.1
local tx, ty = coord_revert[DIR](center.x, center.y, c.tw, c.th)
rendering.draw_rectangle{
surface = state.surface,
filled = false,
--color = miner.postponed and {1, 0, 0} or {0, 1, 0},
color = rect_color,
width = 3,
--target = {c.x1 + x, c.y1 + y},
left_top = {c.gx+tx-off, c.gy + ty - off},
right_bottom = {c.gx+tx+off, c.gy + ty + off},
}
rendering.draw_text{
surface=state.surface,
color=color,
text=miner_dir,
target=mpp_revert(c.gx, c.gy, DIR, center.x, center.y, c.tw, c.th),
vertical_alignment = "top",
alignment = "center",
}
rendering.draw_text{
surface=state.surface,
color={1, 1, 1},
text=miner.line * 2 + miner.stagger - 2,
target={c.gx + tx, c.gy + ty},
vertical_alignment = "bottom",
alignment = "center",
}
--]]
end
return "prepare_belt_layout"
end
---@param self SuperCompactLayout
---@param state SimpleState
function layout:prepare_belt_layout(state)
local m = state.miner
local g = state.grid
local attempt = state.best_attempt
local underground_belt = game.entity_prototypes[state.belt_choice].related_underground_belt.name
local power_poles = {}
state.builder_power_poles = power_poles
---@type table<number, MinerPlacement[]>
local belt_lanes = {}
local miner_lane_number = 0 -- highest index of a lane, because using # won't do the job if a lane is missing
local builder_belts = {}
state.builder_belts = builder_belts
local function que_entity(t) builder_belts[#builder_belts+1] = t end
state.belt_count = 0
for _, miner in ipairs(attempt.miners) do
local index = miner.line * 2 + miner.stagger - 2
miner_lane_number = max(miner_lane_number, index)
if not belt_lanes[index] then belt_lanes[index] = {} end
local line = belt_lanes[index]
line._index = index
if miner.center.x > (line.last_x or 0) then
line.last_x = miner.center.x
line.last_miner = miner
end
line[#line+1] = miner
end
local temp_belts = {}
for k, v in pairs(belt_lanes) do temp_belts[#temp_belts+1] = v end
table.sort(temp_belts, function(a, b) return a._index < b._index end)
state.belts = temp_belts
local shift_x, shift_y = state.best_attempt.sx, state.best_attempt.sy
local function place_belts(start_x, end_x, y)
local belt_start, belt_end = 1 + shift_x + start_x, end_x
if start_x == 0 then
-- straight runoff
for sx = 0, 2 do
que_entity{
name=state.belt_choice,
thing="belt",
grid_x=belt_start-sx,
grid_y=y,
direction=WEST,
}
end
else
-- underground exit
que_entity{
name=underground_belt,
type="output",
thing="belt",
grid_x=shift_x-1,
grid_y=y,
direction=WEST,
}
que_entity{
name=underground_belt,
type="input",
thing="belt",
grid_x=shift_x+m.size+1,
grid_y=y,
direction=WEST,
}
local miner = g:get_tile(shift_x+m.size, y)
if miner and miner.built_on == "miner" then
power_poles[#power_poles+1] = {
name=state.pole_choice,
thing="pole",
grid_x = shift_x,
grid_y = y,
}
end
end
for x = belt_start, end_x, m.size * 2 do
local miner1 = g:get_tile(x, y-1) --[[@as GridTile]]
local miner2 = g:get_tile(x, y+1) --[[@as GridTile]]
local miner3 = g:get_tile(x+3, y) --[[@as GridTile]]
local built = miner1.built_on == "miner" or miner2.built_on == "miner"
local capped = miner3.built_on == "miner"
local pole_built = built or capped
local last = x + m.size * 2 > end_x
if last and not capped then
-- last passtrough and no trailing miner
que_entity{
name=state.belt_choice,
thing="belt",
grid_x=x+1,
grid_y=y,
direction=WEST,
}
elseif capped or built then
que_entity{
name=underground_belt,
type="output",
thing="belt",
grid_x=x+1,
grid_y=y,
direction=WEST,
}
que_entity{
name=underground_belt,
type="input",
thing="belt",
grid_x=x+m.size*2,
grid_y=y,
direction=WEST,
}
else
for sx = 1, 6 do
que_entity{
name=state.belt_choice,
thing="belt",
grid_x=x+sx,
grid_y=y,
direction=WEST,
}
end
end
if last and capped then belt_end = x+6 end
if pole_built then
power_poles[#power_poles+1] = {
name=state.pole_choice,
thing="pole",
grid_x = x + 2,
grid_y = y,
}
end
end
return belt_start, belt_end
end
local stagger_shift = 1
for i = 1, miner_lane_number do
local belt = belt_lanes[i]
if belt and belt.last_x then
local y = m.size + shift_y - 1 + (m.size + 2) * (i-1)
local x_start =stagger_shift % 2 == 0 and 3 or 0
local bx1, bx2 = place_belts(x_start, belt.last_x, y)
belt.x1, belt.x2, belt.y = bx1-3, bx2, y
state.belt_count = state.belt_count + 1
local lane1, lane2 = {}, {}
for _, miner in ipairs(belt) do
if miner.direction == "north" then
lane2[#lane2+1] = miner
else
lane1[#lane1+1] = miner
end
end
if #lane1 > 0 then belt.lane1 = lane1 end
if #lane2 > 0 then belt.lane2 = lane2 end
end
stagger_shift = stagger_shift + 1
end
return "expensive_deconstruct"
end
---@param self SuperCompactLayout
---@param state SimpleState
---@return CallbackState
function layout:placement_miners(state)
local create_entity = builder.create_entity_builder(state)
local grid = state.grid
local module_inv_size = state.miner.module_inventory_size --[[@as uint]]
for _, miner in ipairs(state.best_attempt.miners) do
local center = miner.center
grid:build_miner(center.x, center.y)
local ghost = create_entity{
name = state.miner_choice,
grid_x = center.x,
grid_y = center.y,
direction = defines.direction[miner.direction],
}
if state.module_choice ~= "none" then
ghost.item_requests = {[state.module_choice] = module_inv_size}
end
end
return "placement_belts"
end
return layout