Добавлены все обновления от сообщества, вплоть до #148
This commit is contained in:
24
mining-patch-planner/mpp/blacklist.lua
Normal file
24
mining-patch-planner/mpp/blacklist.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
local b = {}
|
||||
|
||||
b["robotMiningSite-extra"] = true
|
||||
b["robotMiningSite-large"] = true
|
||||
b["robotMiningSite-new"] = true
|
||||
b["invisible-logistic-chest-storage"] = true
|
||||
b["robot-chest-provider"] = true
|
||||
b["robot-chest-requester"] = true
|
||||
b["construction-recaller"] = true
|
||||
b["logistic-recaller"] = true
|
||||
|
||||
b["se-space-elevator"] = true
|
||||
b["se-space-elevator-energy-interface"] = true
|
||||
b["se-space-elevator-energy-pole"] = true
|
||||
|
||||
b["se-spaceship-clamp"] = true
|
||||
b["se-spaceship-clamp-place"] = true
|
||||
b["se-spaceship-clamp-power-pole-external-east"] = true
|
||||
b["se-spaceship-clamp-power-pole-external-west"] = true
|
||||
b["se-spaceship-clamp-power-pole-internal"] = true
|
||||
|
||||
b["kr-quarry-drill"] = true
|
||||
|
||||
return b
|
||||
171
mining-patch-planner/mpp/blueprintmeta.lua
Normal file
171
mining-patch-planner/mpp/blueprintmeta.lua
Normal file
@@ -0,0 +1,171 @@
|
||||
local EAST = defines.direction.east
|
||||
local NORTH = defines.direction.north
|
||||
local SOUTH = defines.direction.south
|
||||
local WEST = defines.direction.west
|
||||
|
||||
---@class EvaluatedBlueprint
|
||||
---@field valid boolean Are all blueprint entities valid
|
||||
---@field w number
|
||||
---@field h number
|
||||
---@field tw number Runtime transposed width
|
||||
---@field th number Runtime transposed height
|
||||
---@field ox number Start offset x
|
||||
---@field oy number Start offset y
|
||||
---@field entities BlueprintEntityEx[] All entities in the blueprint
|
||||
---@field entity_names table<string, number>
|
||||
---@field miner_name string Mining drill name
|
||||
local bp_meta = {}
|
||||
bp_meta.__index = bp_meta
|
||||
|
||||
---@class BlueprintEntityEx : BlueprintEntity
|
||||
---@field capstone_x boolean
|
||||
---@field capstone_y boolean
|
||||
|
||||
---Blueprint analysis data
|
||||
---@param bp LuaItemStack
|
||||
---@return EvaluatedBlueprint
|
||||
function bp_meta:new(bp)
|
||||
---@type EvaluatedBlueprint
|
||||
local new = setmetatable({}, self)
|
||||
|
||||
new.valid = true
|
||||
new.w, new.h = bp.blueprint_snap_to_grid.x, bp.blueprint_snap_to_grid.y
|
||||
new.ox, new.oy = 0, 0
|
||||
if bp.blueprint_position_relative_to_grid then
|
||||
new.ox = bp.blueprint_position_relative_to_grid.x
|
||||
new.oy = bp.blueprint_position_relative_to_grid.y
|
||||
end
|
||||
new.entities = bp.get_blueprint_entities() or {}
|
||||
new.entity_names = bp.cost_to_build
|
||||
|
||||
new:evaluate_tiling()
|
||||
new:evaluate_miners()
|
||||
|
||||
return new
|
||||
end
|
||||
|
||||
---Marks capstone BlueprintEntities
|
||||
function bp_meta:evaluate_tiling()
|
||||
local sw, sh = self.w, self.h
|
||||
local buckets_x, buckets_y = {}, {}
|
||||
|
||||
for i, ent in pairs(self.entities) do
|
||||
local x, y = ent.position.x, ent.position.y
|
||||
ent.direction = ent.direction or NORTH
|
||||
if not buckets_x[x] then buckets_x[x] = {} end
|
||||
table.insert(buckets_x[x], ent)
|
||||
if not buckets_y[y] then buckets_y[y] = {} end
|
||||
table.insert(buckets_y[y], ent)
|
||||
|
||||
end
|
||||
|
||||
for _, bucket in pairs(buckets_y) do
|
||||
for i = 1, #bucket-1 do
|
||||
local e1 = bucket[i] ---@type BlueprintEntityEx
|
||||
local e1x = e1.position.x
|
||||
for j = 2, #bucket do
|
||||
local e2 = bucket[j] ---@type BlueprintEntityEx
|
||||
local e2x = e2.position.x
|
||||
if e1x + sw == e2x or e1x - sw == e2x then
|
||||
e2.capstone_x = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, bucket in pairs(buckets_x) do
|
||||
for i = 1, #bucket-1 do
|
||||
local e1 = bucket[i] ---@type BlueprintEntityEx
|
||||
local e1y = e1.position.y
|
||||
for j = 2, #bucket do
|
||||
local e2 = bucket[j] ---@type BlueprintEntityEx
|
||||
local e2y = e2.position.y
|
||||
if e1y + sh == e2y or e1y - sh == e2y then
|
||||
e2.capstone_y = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function bp_meta:evaluate_miners()
|
||||
for _, ent in pairs(self.entities) do
|
||||
local name = ent.name
|
||||
if game.entity_prototypes[name].type == "mining-drill" then
|
||||
--local proto = game.entity_prototypes[name]
|
||||
self.miner_name = name
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@return BlueprintEntityEx[]
|
||||
function bp_meta:get_mining_drills()
|
||||
local miner_name = self.miner_name
|
||||
local mining_drills = {}
|
||||
for _, ent in pairs(self.entities) do
|
||||
if ent.name == miner_name then
|
||||
mining_drills[#mining_drills+1] = ent
|
||||
end
|
||||
end
|
||||
return mining_drills
|
||||
end
|
||||
|
||||
function bp_meta:check_valid()
|
||||
for k, v in pairs(self.entity_names) do
|
||||
if not game.entity_prototypes[k] and not game.item_prototypes[k] then
|
||||
self.valid = false
|
||||
return false
|
||||
end
|
||||
end
|
||||
self.valid = true
|
||||
return true
|
||||
end
|
||||
|
||||
---Returns resource categories for a blueprint
|
||||
---@return table<string, boolean>
|
||||
function bp_meta:get_resource_categories()
|
||||
local categories = {}
|
||||
local proto = game.entity_prototypes[self.miner_name]
|
||||
if proto.resource_categories then
|
||||
for cat, bool in pairs(proto.resource_categories) do
|
||||
categories[cat] = bool
|
||||
end
|
||||
end
|
||||
return categories
|
||||
end
|
||||
|
||||
---@return table<string, GridBuilding>
|
||||
function bp_meta:get_entity_categories()
|
||||
---@type table<string, GridBuilding>
|
||||
local categories = {
|
||||
["mining-drill"] = "miner",
|
||||
["beacon"] = "beacon",
|
||||
["transport-belt"] = "belt",
|
||||
["underground-belt"] = "belt",
|
||||
["splitter"] = "belt",
|
||||
["inserter"] = "inserter",
|
||||
["container"] = "container",
|
||||
["logistic-container"] = "container",
|
||||
["loader"] = "inserter",
|
||||
["loader-1x1"] = "inserter",
|
||||
["electric-pole"] = "pole",
|
||||
}
|
||||
|
||||
---@type table<string, GridBuilding>
|
||||
local category_map = {}
|
||||
|
||||
for _, ent in pairs(self.entities) do
|
||||
local name = ent.name
|
||||
local category = category_map[name]
|
||||
if not category then
|
||||
local ent_type = game.entity_prototypes[name].type
|
||||
category = categories[ent_type] or "other"
|
||||
category_map[name] = category
|
||||
end
|
||||
end
|
||||
|
||||
return category_map
|
||||
end
|
||||
|
||||
return bp_meta
|
||||
65
mining-patch-planner/mpp/builder.lua
Normal file
65
mining-patch-planner/mpp/builder.lua
Normal file
@@ -0,0 +1,65 @@
|
||||
local mpp_util = require("mpp.mpp_util")
|
||||
local coord_revert_world = mpp_util.revert_world
|
||||
local builder = {}
|
||||
|
||||
---@class GhostSpecification : LuaSurface.create_entity_param.entity_ghost
|
||||
---@field grid_x number Grid x coordinate
|
||||
---@field grid_y number Grid x coordinate
|
||||
---@field radius number? Object radius or default to 0.5 if nil
|
||||
---@field extent_w number? Object extent from origin, converted from radius if nil
|
||||
---@field extent_h number? Object extent from origin, converted from radius if nil
|
||||
---@field thing GridBuilding Enum for the grid
|
||||
---@field items table<string, number>? Item requests
|
||||
|
||||
---@class PowerPoleGhostSpecification : GhostSpecification
|
||||
---@field no_light boolean
|
||||
---@field ix number
|
||||
---@field iy number
|
||||
|
||||
--- Builder for a convenience function that automatically translates
|
||||
--- internal grid state for a surface.create_entity call
|
||||
---@param state State
|
||||
---@return fun(ghost: GhostSpecification, check_allowed: boolean?): LuaEntity?
|
||||
function builder.create_entity_builder(state)
|
||||
local c = state.coords
|
||||
local grid = state.grid
|
||||
local DIR = state.direction_choice
|
||||
local surface = state.surface
|
||||
local gx, gy, tw, th = c.gx, c.gy, c.tw, c.th
|
||||
local direction_conv = mpp_util.bp_direction[state.direction_choice]
|
||||
local collected_ghosts = state._collected_ghosts
|
||||
local is_space = state.is_space
|
||||
|
||||
return function(ghost, check_allowed)
|
||||
ghost.raise_built = true
|
||||
ghost.player = state.player
|
||||
ghost.force = state.player.force
|
||||
ghost.inner_name=ghost.name
|
||||
ghost.name="entity-ghost"
|
||||
ghost.position=coord_revert_world(gx, gy, DIR, ghost.grid_x, ghost.grid_y, tw, th)
|
||||
ghost.direction=direction_conv[ghost.direction or defines.direction.north]
|
||||
|
||||
--local can_place = surface.can_place_entity(ghost)
|
||||
if check_allowed and not surface.can_place_entity{
|
||||
name = ghost.inner_name,
|
||||
-- name = "entity-ghost",
|
||||
-- inner_name = ghost.inner_name,
|
||||
force = state.player.force,
|
||||
position = ghost.position,
|
||||
direction = ghost.direction,
|
||||
build_check_type = defines.build_check_type.blueprint_ghost,
|
||||
forced = true,
|
||||
} then
|
||||
return
|
||||
end
|
||||
|
||||
local result = surface.create_entity(ghost)
|
||||
if result then
|
||||
grid:build_specification(ghost)
|
||||
collected_ghosts[#collected_ghosts+1] = result
|
||||
end
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
return builder
|
||||
176
mining-patch-planner/mpp/color.lua
Normal file
176
mining-patch-planner/mpp/color.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
local sin, cos, pi = math.sin, math.cos, math.pi
|
||||
local min, max = math.min, math.max
|
||||
local function clamp(x, a, b)
|
||||
return max(min(x, b or 1), a or 0)
|
||||
end
|
||||
|
||||
local color = {}
|
||||
|
||||
-- OKLAB, OKHSV conversion functions yoinked from Björn Ottosson
|
||||
-- https://bottosson.github.io/posts/colorpicker/
|
||||
|
||||
function color.oklab_to_linear_srgb(L, a, b)
|
||||
|
||||
local l_ = L + 0.3963377774 * a + 0.2158037573 * b;
|
||||
local m_ = L - 0.1055613458 * a - 0.0638541728 * b;
|
||||
local s_ = L - 0.0894841775 * a - 1.2914855480 * b;
|
||||
|
||||
local l = l_*l_*l_;
|
||||
local m = m_*m_*m_;
|
||||
local s = s_*s_*s_;
|
||||
|
||||
return
|
||||
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
|
||||
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
|
||||
-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s
|
||||
end
|
||||
|
||||
-- Finds the maximum saturation possible for a given hue that fits in sRGB
|
||||
-- Saturation here is defined as S = C/L
|
||||
-- a and b must be normalized so a^2 + b^2 == 1
|
||||
local function compute_max_saturation(a, b)
|
||||
-- Max saturation will be when one of r, g or b goes below zero.
|
||||
|
||||
-- Select different coefficients depending on which component goes below zero first
|
||||
local k0, k1, k2, k3, k4, wl, wm, ws;
|
||||
|
||||
if -1.88170328 * a - 0.80936493 * b > 1 then
|
||||
-- Red component
|
||||
k0 = 1.19086277; k1 = 1.76576728; k2 = 0.59662641; k3 = 0.75515197; k4 = 0.56771245;
|
||||
wl = 4.0767416621; wm = -3.3077115913; ws = 0.2309699292;
|
||||
elseif 1.81444104 * a - 1.19445276 * b > 1 then
|
||||
-- Green component
|
||||
k0 = 0.73956515; k1 = -0.45954404; k2 = 0.08285427; k3 = 0.12541070; k4 = 0.14503204;
|
||||
wl = -1.2684380046; wm = 2.6097574011; ws = -0.3413193965;
|
||||
else
|
||||
-- Blue component
|
||||
k0 = 1.35733652; k1 = -0.00915799; k2 = -1.15130210; k3 = -0.50559606; k4 = 0.00692167;
|
||||
wl = -0.0041960863; wm = -0.7034186147; ws = 1.7076147010;
|
||||
end
|
||||
|
||||
-- Approximate max saturation using a polynomial:
|
||||
local S = k0 + k1 * a + k2 * b + k3 * a * a + k4 * a * b;
|
||||
|
||||
-- Do one step Halley's method to get closer
|
||||
-- this gives an error less than 10e6, except for some blue hues where the dS/dh is close to infinite
|
||||
-- this should be sufficient for most applications, otherwise do two/three steps
|
||||
|
||||
local k_l = 0.3963377774 * a + 0.2158037573 * b;
|
||||
local k_m = -0.1055613458 * a - 0.0638541728 * b;
|
||||
local k_s = -0.0894841775 * a - 1.2914855480 * b;
|
||||
|
||||
do
|
||||
local l_ = 1 + S * k_l;
|
||||
local m_ = 1 + S * k_m;
|
||||
local s_ = 1 + S * k_s;
|
||||
|
||||
local l = l_ * l_ * l_;
|
||||
local m = m_ * m_ * m_;
|
||||
local s = s_ * s_ * s_;
|
||||
|
||||
local l_dS = 3 * k_l * l_ * l_;
|
||||
local m_dS = 3 * k_m * m_ * m_;
|
||||
local s_dS = 3 * k_s * s_ * s_;
|
||||
|
||||
local l_dS2 = 6 * k_l * k_l * l_;
|
||||
local m_dS2 = 6 * k_m * k_m * m_;
|
||||
local s_dS2 = 6 * k_s * k_s * s_;
|
||||
|
||||
local f = wl * l + wm * m + ws * s;
|
||||
local f1 = wl * l_dS + wm * m_dS + ws * s_dS;
|
||||
local f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2;
|
||||
|
||||
S = S - f * f1 / (f1*f1 - 0.5 * f * f2);
|
||||
end
|
||||
|
||||
return S
|
||||
end
|
||||
|
||||
function find_cusp(a, b)
|
||||
-- First, find the maximum saturation (saturation S = C/L)
|
||||
local S_cusp = compute_max_saturation(a, b);
|
||||
|
||||
-- Convert to linear sRGB to find the first point where at least one of r,g or b >= 1:
|
||||
local rgb_at_max = {color.oklab_to_linear_srgb(1, S_cusp * a, S_cusp * b)}
|
||||
local L_cusp = (1 / max(max(rgb_at_max[1], rgb_at_max[2]), rgb_at_max[3])) ^ (1/3)
|
||||
local C_cusp = L_cusp * S_cusp;
|
||||
|
||||
return L_cusp , C_cusp
|
||||
end
|
||||
|
||||
local function get_ST_max(a_, b_, cusp)
|
||||
if not cusp then
|
||||
cusp = {find_cusp(a_, b_)}
|
||||
end
|
||||
local L = cusp[1];
|
||||
local C = cusp[2];
|
||||
return C/L, C/(1-L)
|
||||
end
|
||||
|
||||
local function toe_inv(x)
|
||||
local k_1 = 0.206
|
||||
local k_2 = 0.03
|
||||
local k_3 = (1+k_1)/(1+k_2)
|
||||
return (x*x + k_1*x)/(k_3*(x+k_2))
|
||||
end
|
||||
|
||||
local function srgb_transfer_function(value)
|
||||
-- if value <= 0.04045 then
|
||||
-- return value / 12.92
|
||||
-- end
|
||||
-- return ((value + 0.055) / 1.055) ^ 2.4;
|
||||
return .0031308 >= value and (12.92 * value) or (1.055 * math.pow(value, .4166666666666667) - .055)
|
||||
end
|
||||
|
||||
function color.linear_srgb_to_rgb(sr, sg, sb)
|
||||
return
|
||||
srgb_transfer_function(sr),
|
||||
srgb_transfer_function(sg),
|
||||
srgb_transfer_function(sb)
|
||||
end
|
||||
|
||||
function color.okhsv_to_srgb(h, s, v)
|
||||
local a_ = cos(2*pi*h)
|
||||
local b_ = sin(2*pi*h)
|
||||
|
||||
local ST_max = {get_ST_max(a_,b_)};
|
||||
local S_max = ST_max[1];
|
||||
local S_0 = 0.5;
|
||||
local T = ST_max[2]
|
||||
local k = 1 - S_0/S_max;
|
||||
|
||||
local L_v = 1 - s*S_0/(S_0+T - T*k*s)
|
||||
local C_v = s*T*S_0/(S_0+T-T*k*s)
|
||||
|
||||
local L = v*L_v;
|
||||
local C = v*C_v;
|
||||
|
||||
local L_vt = toe_inv(L_v);
|
||||
local C_vt = C_v * L_vt/L_v;
|
||||
|
||||
local L_new = toe_inv(L); -- * L_v/L_vt;
|
||||
C = C * L_new/L;
|
||||
L = L_new;
|
||||
|
||||
local rgb_scale = {color.oklab_to_linear_srgb(L_vt,a_*C_vt,b_*C_vt)};
|
||||
local scale_L = (1/(max(rgb_scale[1],rgb_scale[2],rgb_scale[3],0))) ^ (1/3)
|
||||
|
||||
L = L * scale_L;
|
||||
C = C * scale_L;
|
||||
|
||||
--local rgb = color.oklab_to_linear_srgb(L, C*a_, C*b_);
|
||||
-- apply srgb transfer function
|
||||
|
||||
return color.oklab_to_linear_srgb(L, C*a_, C*b_)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function color.hue_sequence(n)
|
||||
local r,g,b = color.okhsv_to_srgb((n * math.phi) % 1, 1, 1)
|
||||
return {clamp(r), clamp(g), clamp(b)}
|
||||
--return {r, g, b}
|
||||
end
|
||||
|
||||
|
||||
return color
|
||||
131
mining-patch-planner/mpp/compatibility.lua
Normal file
131
mining-patch-planner/mpp/compatibility.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
local enum = require("mpp.enums")
|
||||
|
||||
local compatibility = {}
|
||||
|
||||
--[[----------------------------------------------------------------------------
|
||||
Space Exploration
|
||||
----------------------------------------------------------------------------]]--
|
||||
|
||||
local space_exploration_active = nil
|
||||
---@return boolean
|
||||
compatibility.is_space_exploration_active = function()
|
||||
if space_exploration_active == nil then
|
||||
space_exploration_active = game.active_mods["space-exploration"] and true or false
|
||||
end
|
||||
return space_exploration_active
|
||||
end
|
||||
|
||||
--- @class SERemoteViewToggledEventData: EventData
|
||||
--- @field player_index uint
|
||||
|
||||
-- Thanks Raiguard
|
||||
compatibility.get_se_events = function()
|
||||
local se, events = remote.interfaces["space-exploration"], {}
|
||||
if not se then return events end
|
||||
|
||||
if se.get_on_remote_view_started_event then
|
||||
events["get_on_remote_view_started_event"] = remote.call("space-exploration", "get_on_remote_view_started_event")
|
||||
end
|
||||
if se.get_on_remote_view_stopped_event then
|
||||
events["get_on_remote_view_stopped_event"] = remote.call("space-exploration", "get_on_remote_view_stopped_event")
|
||||
end
|
||||
return events
|
||||
end
|
||||
|
||||
local memoize_space_surfaces = {}
|
||||
|
||||
--- Wrapper for Space Exploration get_zone_is_space remote interface calls
|
||||
---@param surface_identification SurfaceIdentification
|
||||
---@return boolean
|
||||
compatibility.is_space = function(surface_identification)
|
||||
local surface_index = surface_identification
|
||||
if type(surface_identification) == "string" then
|
||||
surface_identification = game.get_surface(surface_identification).index
|
||||
elseif type(surface_identification) == "userdata" then
|
||||
---@cast surface_identification LuaSurface
|
||||
surface_identification = surface_identification.index
|
||||
end
|
||||
|
||||
local memoized = memoize_space_surfaces[surface_index]
|
||||
if memoized ~= nil then return memoized end
|
||||
|
||||
if game.active_mods["space-exploration"] then
|
||||
local zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = surface_index} --[[@as table]]) --[[@as LuaSurface?]]
|
||||
if not zone then
|
||||
memoize_space_surfaces[surface_index] = false
|
||||
return false
|
||||
end
|
||||
local result = remote.call("space-exploration", "get_zone_is_space", {zone_index = zone.index} --[[@as table]]) --[[@as boolean]]
|
||||
memoize_space_surfaces[surface_index] = result
|
||||
return result
|
||||
end
|
||||
memoize_space_surfaces[surface_index] = false
|
||||
return false
|
||||
end
|
||||
|
||||
---@type string
|
||||
local space_collision_mask_name = nil
|
||||
|
||||
---@type table<string, boolean>
|
||||
local memoize_space_buildable = {}
|
||||
|
||||
function compatibility.is_buildable_in_space(name)
|
||||
local buildable_status = memoize_space_buildable[name]
|
||||
if buildable_status ~= nil then
|
||||
return buildable_status
|
||||
end
|
||||
|
||||
-- if something goes wrong, give up, allow to build
|
||||
if space_collision_mask_name == nil then
|
||||
local scaffold_tile = game.tile_prototypes["se-space-platform-scaffold"]
|
||||
if not scaffold_tile then
|
||||
memoize_space_buildable[name] = true
|
||||
return true
|
||||
end
|
||||
space_collision_mask_name = next(scaffold_tile.collision_mask)
|
||||
if space_collision_mask_name == nil then
|
||||
memoize_space_buildable[name] = true
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local entity_proto = game.entity_prototypes[name]
|
||||
local allowed = not entity_proto.collision_mask[space_collision_mask_name]
|
||||
|
||||
memoize_space_buildable[name] = allowed
|
||||
|
||||
return allowed
|
||||
end
|
||||
|
||||
|
||||
--- Return true to skip non space item
|
||||
---@param is_space boolean
|
||||
---@param protype LuaEntityPrototype
|
||||
---@return boolean
|
||||
compatibility.guess_space_item = function(is_space, protype)
|
||||
if not is_space then return false end
|
||||
return string.match(protype.name, "^se%-")
|
||||
end
|
||||
|
||||
--[[----------------------------------------------------------------------------
|
||||
Pyanodons
|
||||
----------------------------------------------------------------------------]]--
|
||||
|
||||
local pyanodons_active = nil
|
||||
---@return boolean
|
||||
compatibility.is_pyanodons_active = function()
|
||||
if pyanodons_active == nil then
|
||||
local active_mods = game.active_mods
|
||||
-- pyanodons_active = game.active_mods["space-exploration"] and true or false
|
||||
for k, v in ipairs{"pyrawores", "pycoalprocessing", "pyalienlife"} do
|
||||
if active_mods[v] then
|
||||
pyanodons_active = true
|
||||
break
|
||||
end
|
||||
end
|
||||
pyanodons_active = false
|
||||
end
|
||||
return pyanodons_active
|
||||
end
|
||||
|
||||
return compatibility
|
||||
143
mining-patch-planner/mpp/drawing.lua
Normal file
143
mining-patch-planner/mpp/drawing.lua
Normal file
@@ -0,0 +1,143 @@
|
||||
-- debug rendering library
|
||||
local table_insert = table.insert
|
||||
|
||||
---@class DebugRendering
|
||||
---@field _state State
|
||||
---@field _enabled boolean Does drawing do anything
|
||||
---@field _register boolean Add to render objects to be removed
|
||||
local drawing_meta = {}
|
||||
drawing_meta.__index = drawing_meta
|
||||
|
||||
function drawing_meta:draw_circle(t)
|
||||
if not self._enabled then return end
|
||||
local state = self._state --[[@as State]]
|
||||
|
||||
local C = state.coords
|
||||
local x, y
|
||||
if t.integer then
|
||||
x, y = C.ix1, C.iy1
|
||||
else
|
||||
x, y = C.gx, C.gy
|
||||
end
|
||||
local tx, ty = t.x, t.y
|
||||
|
||||
t.surface = state.surface
|
||||
t.players = {state.player}
|
||||
t.width = t.width or 3
|
||||
t.color = t.color or {1, 1, 1}
|
||||
t.radius = t.radius or 0.5
|
||||
t.target = { x + tx, y + ty }
|
||||
local id = rendering.draw_circle(t)
|
||||
if self._register then
|
||||
table_insert(state._render_objects, id)
|
||||
end
|
||||
end
|
||||
|
||||
function drawing_meta:draw_line(t)
|
||||
if not self._enabled then return end
|
||||
local state = self._state --[[@as State]]
|
||||
|
||||
local C = state.coords
|
||||
local x, y
|
||||
if t.integer then
|
||||
x, y = C.ix1, C.iy1
|
||||
else
|
||||
x, y = C.gx, C.gy
|
||||
end
|
||||
|
||||
local tx, ty = t.x or t.x1, t.y or t.y1
|
||||
local target1 = {x + tx, y + ty}
|
||||
local target2
|
||||
if t.x2 and t.y2 then
|
||||
target2 = {x + t.x2, y + t.y2}
|
||||
elseif t.w and t.h then
|
||||
target2 = {x + tx + (t.w or 0), y + ty + (t.h or 0)}
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
t.surface = state.surface
|
||||
t.players = {state.player}
|
||||
t.width = t.width or 3
|
||||
t.color = t.color or {1, 1, 1}
|
||||
t.from = t.from or target1
|
||||
t.to = t.to or target2
|
||||
local id = rendering.draw_line(t)
|
||||
if self._register then
|
||||
table_insert(state._render_objects, id)
|
||||
end
|
||||
end
|
||||
|
||||
function drawing_meta:draw_rectangle(t)
|
||||
if not self._enabled then return end
|
||||
|
||||
local state = self._state --[[@as State]]
|
||||
|
||||
local C = state.coords
|
||||
local x, y
|
||||
if t.integer then
|
||||
x, y = C.ix1, C.iy1
|
||||
else
|
||||
x, y = C.gx, C.gy
|
||||
end
|
||||
|
||||
local tx, ty = t.x or t.x1, t.y or t.y1
|
||||
local target1 = {x + tx, y + ty}
|
||||
local target2
|
||||
if t.x2 and t.y2 then
|
||||
target2 = {x + t.x2, y + t.y2}
|
||||
elseif t.w and t.h then
|
||||
target2 = {x + tx + (t.w or 0), y + ty + (t.h or 0)}
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
t.surface = state.surface
|
||||
t.players = {state.player}
|
||||
t.width = t.width or 3
|
||||
t.color = t.color or {1, 1, 1}
|
||||
t.left_top = t.left_top or target1
|
||||
t.right_bottom = t.right_bottom or target2
|
||||
local id = rendering.draw_rectangle(t)
|
||||
if self._register then
|
||||
table_insert(state._render_objects, id)
|
||||
end
|
||||
end
|
||||
|
||||
function drawing_meta:draw_text(t)
|
||||
if not self._enabled then return end
|
||||
|
||||
local state = self._state --[[@as State]]
|
||||
|
||||
local C = state.coords
|
||||
local x, y
|
||||
if t.integer then
|
||||
x, y = C.ix1, C.iy1
|
||||
else
|
||||
x, y = C.gx, C.gy
|
||||
end
|
||||
local tx, ty = t.x, t.y
|
||||
|
||||
t.surface = state.surface
|
||||
t.players = {state.player}
|
||||
t.width = t.width or 3
|
||||
t.color = t.color or {1, 1, 1}
|
||||
t.target = { x + tx, y + ty }
|
||||
t.scale = t.scale or 1
|
||||
t.alignment = t.alignment or "center"
|
||||
t.vertical_alignment = t.vertical_alignment or "middle"
|
||||
|
||||
local id = rendering.draw_text(t)
|
||||
if self._register then
|
||||
table_insert(state._render_objects, id)
|
||||
end
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param state State
|
||||
---@param enabled boolean Enable drawing
|
||||
local function drawing(state, enabled, register)
|
||||
return setmetatable({_state=state, _enabled=(not not enabled), _register=(not not register)}, drawing_meta)
|
||||
end
|
||||
|
||||
return drawing
|
||||
134
mining-patch-planner/mpp/enums.lua
Normal file
134
mining-patch-planner/mpp/enums.lua
Normal file
@@ -0,0 +1,134 @@
|
||||
local enums = {}
|
||||
|
||||
---@type LuaEntityPrototype[]
|
||||
local cached_miners = {}
|
||||
---@type table<string, boolean>
|
||||
local cached_resource_categories = {}
|
||||
|
||||
local invalid_resource = { --fluid or otherwise
|
||||
["se-core-mining"] = true,
|
||||
}
|
||||
local miner_blacklist = {
|
||||
["se-core-miner-drill"] = true
|
||||
}
|
||||
|
||||
function enums.get_default_miner()
|
||||
if game.active_mods["nullius"] then
|
||||
return "nullius-medium-miner-1"
|
||||
end
|
||||
return "electric-mining-drill"
|
||||
end
|
||||
|
||||
---Get mining drills and resource categories
|
||||
---@return LuaEntityPrototype[], table
|
||||
function enums.get_available_miners()
|
||||
enums.get_available_miners = function() return cached_miners, cached_resource_categories end
|
||||
|
||||
local all_miners = game.get_filtered_entity_prototypes{{filter="type", type="mining-drill"}}
|
||||
---@type table<string, LuaEntityPrototype>
|
||||
--local all_fluids = game.get_filtered_item_prototypes({filter="type", type="
|
||||
local all_resources = game.get_filtered_entity_prototypes{{filter="type", type="resource"}}
|
||||
---@type table<string, LuaResourceCategoryPrototype>
|
||||
|
||||
for name, proto in pairs(all_resources) do
|
||||
---@cast proto LuaEntityPrototype
|
||||
local mineable_properties = proto.mineable_properties
|
||||
|
||||
if mineable_properties.products then
|
||||
for _, product in ipairs(mineable_properties.products) do
|
||||
if product.type == "fluid" then
|
||||
invalid_resource[name] = true
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
invalid_resource[name] = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
for miner_name, miner_proto in pairs(all_miners) do
|
||||
if miner_blacklist[miner_name] then goto continue_miner end
|
||||
if miner_proto.resource_categories then
|
||||
for resource_cat, bool in pairs(miner_proto.resource_categories) do
|
||||
if invalid_resource[resource_cat] then
|
||||
miner_blacklist[miner_name] = true
|
||||
end
|
||||
end
|
||||
else
|
||||
miner_blacklist[miner_name] = true
|
||||
goto continue_miner
|
||||
end
|
||||
|
||||
---@cast miner_proto LuaEntityPrototype
|
||||
local fluidboxes = miner_proto.fluidbox_prototypes
|
||||
for _, fluidbox in pairs(fluidboxes) do
|
||||
---@cast fluidbox LuaFluidBoxPrototype
|
||||
if fluidbox.production_type == "output" then
|
||||
miner_blacklist[miner_name] = true
|
||||
end
|
||||
end
|
||||
::continue_miner::
|
||||
end
|
||||
|
||||
if game.active_mods["Cursed-FMD"] then
|
||||
local mangled_categories = {}
|
||||
local miners = {}
|
||||
for name, proto in pairs(all_miners) do
|
||||
if string.find(name, ";") then -- Cursed-FMD hack
|
||||
for resource_name, _ in pairs(proto.resource_categories) do
|
||||
if not invalid_resource[resource_name] and not string.find(resource_name, "core-fragment") then
|
||||
mangled_categories[resource_name] = true
|
||||
end
|
||||
end
|
||||
else
|
||||
if proto.flags and proto.flags.hidden then goto continue_miner end
|
||||
if miner_blacklist[name] then goto continue_miner end
|
||||
|
||||
for resource_name, _ in pairs(proto.resource_categories) do
|
||||
if not invalid_resource[resource_name] and not string.find(resource_name, "core-fragment") then
|
||||
miners[name] = proto
|
||||
end
|
||||
end
|
||||
end
|
||||
::continue_miner::
|
||||
end
|
||||
cached_miners = miners
|
||||
cached_resource_categories = mangled_categories
|
||||
else
|
||||
local miners = {}
|
||||
local resource_categories = {
|
||||
["basic-solid"] = true,
|
||||
["hard-resource"] = true,
|
||||
}
|
||||
for name, proto in pairs(all_miners) do
|
||||
if proto.flags and proto.flags.hidden then goto continue_miner end
|
||||
if miner_blacklist[name] then goto continue_miner end
|
||||
--if not proto.resource_categories["basic-solid"] then goto continue_miner end
|
||||
for resource_category, bool in pairs(proto.resource_categories) do
|
||||
resource_categories[resource_category] = bool
|
||||
end
|
||||
|
||||
miners[name] = proto
|
||||
|
||||
::continue_miner::
|
||||
end
|
||||
|
||||
cached_miners = miners
|
||||
cached_resource_categories = resource_categories
|
||||
--[[ {
|
||||
["basic-solid"] = true,
|
||||
["hard-resource"] = true,
|
||||
}]]
|
||||
end
|
||||
return enums.get_available_miners()
|
||||
end
|
||||
|
||||
enums.space_surfaces = {
|
||||
["asteroid-belt"] = true,
|
||||
["asteroid-field"] = true,
|
||||
["orbit"] = true,
|
||||
["anomaly"] = true,
|
||||
}
|
||||
|
||||
return enums
|
||||
45
mining-patch-planner/mpp/global_extends.lua
Normal file
45
mining-patch-planner/mpp/global_extends.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
local table_insert = table.insert
|
||||
|
||||
List = require("mpp.list")
|
||||
|
||||
math.phi = 1.618033988749894
|
||||
|
||||
---Filters out a list
|
||||
---@param t any
|
||||
---@param func any
|
||||
function table.filter(t, func)
|
||||
local new = {}
|
||||
for k, v in ipairs(t) do
|
||||
if func(v) then new[#new+1] = v end
|
||||
end
|
||||
return new
|
||||
end
|
||||
|
||||
function table.map(t, func)
|
||||
local new = {}
|
||||
for k, v in pairs(t) do
|
||||
new[k] = func(v)
|
||||
end
|
||||
return new
|
||||
end
|
||||
|
||||
function table.mapkey(t, func)
|
||||
local new = {}
|
||||
for k, v in pairs(t) do
|
||||
new[func(v)] = v
|
||||
end
|
||||
return new
|
||||
end
|
||||
|
||||
---Appends a list to the target table
|
||||
---@param target table
|
||||
---@param other any[]
|
||||
function table.append(target, other)
|
||||
for _, value in pairs(other) do
|
||||
table_insert(target, value)
|
||||
end
|
||||
end
|
||||
|
||||
function math.divmod(a, b)
|
||||
return math.floor(a / b), a % b
|
||||
end
|
||||
385
mining-patch-planner/mpp/grid_mt.lua
Normal file
385
mining-patch-planner/mpp/grid_mt.lua
Normal file
@@ -0,0 +1,385 @@
|
||||
local floor, ceil, min, max = math.floor, math.ceil, math.min, math.max
|
||||
|
||||
local mpp_util = require("mpp.mpp_util")
|
||||
|
||||
---@class GridRow: table<number, GridTile>
|
||||
---@field [number] GridTile
|
||||
|
||||
---@class Grid
|
||||
---@field [number] GridRow
|
||||
local grid_mt = {}
|
||||
grid_mt.__index = grid_mt
|
||||
|
||||
---Coordinate aggregate of the resource patch
|
||||
---@class Coords
|
||||
---@field x1 double Top left corner of the patch
|
||||
---@field y1 double Top left corner of the patch
|
||||
---@field x2 double Bottom right corner of the patch
|
||||
---@field y2 double Bottom right corner of the patch
|
||||
---@field ix1 number Integer top left corner of the patch
|
||||
---@field iy1 number Integer top left corner of the patch
|
||||
---@field ix2 number Integer bottom right corner
|
||||
---@field iy2 number Integer bottom right corner
|
||||
---@field w integer Width of the patch
|
||||
---@field h integer Height of the patch
|
||||
---@field tw integer Width Rotation invariant width
|
||||
---@field th integer Height Rotation invariant height
|
||||
---@field gx double x1 but -1 for grid rendering
|
||||
---@field gy double y1 but -1 for grid rendering
|
||||
---@field extent_x1 number Internal grid dimensions
|
||||
---@field extent_y1 number Internal grid dimensions
|
||||
---@field extent_x2 number Internal grid dimensions
|
||||
---@field extent_y2 number Internal grid dimensions
|
||||
|
||||
---@alias GridBuilding
|
||||
---| nil
|
||||
---| "miner"
|
||||
---| "pole"
|
||||
---| "beacon"
|
||||
---| "belt"
|
||||
---| "inserter"
|
||||
---| "container"
|
||||
---| "lamp"
|
||||
---| "other"
|
||||
|
||||
local need_electricity = {
|
||||
miner = true,
|
||||
beacon = true,
|
||||
inserter = true
|
||||
}
|
||||
|
||||
---@class GridTile
|
||||
---@field amount number Amount of resource on tile
|
||||
---@field neighbor_amount number Total resource sum of neighbors (performance killer?)
|
||||
---@field neighbors_inner number Physical drill coverage
|
||||
---@field neighbors_outer number Drill radius coverage
|
||||
---@field x integer
|
||||
---@field y integer
|
||||
---@field gx double actual coordinate in surface
|
||||
---@field gy double actual coordinate in surface
|
||||
---@field built_on GridBuilding Is tile occupied by a building entity
|
||||
---@field consumed boolean Is a miner consuming this tile
|
||||
|
||||
---@class BlueprintGridTile : GridTile
|
||||
---@field neighbor_counts table<number, number>
|
||||
---@field neighbors_inner nil
|
||||
---@field neighbors_outer nil
|
||||
|
||||
---comment
|
||||
---@param x integer Grid coordinate
|
||||
---@param y integer Grid coordinate
|
||||
---@return GridTile|nil
|
||||
function grid_mt:get_tile(x, y)
|
||||
local row = self[y]
|
||||
if row then return row[x] end
|
||||
end
|
||||
|
||||
---Convolves resource count for a grid cell
|
||||
---For usage in blueprint layouts
|
||||
---@param ox any
|
||||
---@param oy any
|
||||
---@param size any
|
||||
function grid_mt:convolve(ox, oy, size)
|
||||
local nx1, nx2 = ox, ox+size - 1
|
||||
local ny1, ny2 = oy, oy+size - 1
|
||||
|
||||
for y = ny1, ny2 do
|
||||
---@type table<number, BlueprintGridTile>
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = nx1, nx2 do
|
||||
local tile = row[x]
|
||||
if tile == nil then goto continue_column end
|
||||
local counts = tile.neighbor_counts
|
||||
counts[size] = counts[size] + 1
|
||||
::continue_column::
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
end
|
||||
|
||||
---Convolves resource count for a grid cell
|
||||
---@param ox any
|
||||
---@param oy any
|
||||
---@param size any
|
||||
function grid_mt:convolve_inner(ox, oy, size)
|
||||
local nx1, nx2 = ox, ox+size - 1
|
||||
local ny1, ny2 = oy, oy+size - 1
|
||||
|
||||
for y = ny1, ny2 do
|
||||
---@type table<number, GridTile>
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = nx1, nx2 do
|
||||
local tile = row[x]
|
||||
if tile == nil then goto continue_column end
|
||||
tile.neighbors_inner = tile.neighbors_inner + 1
|
||||
::continue_column::
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
end
|
||||
|
||||
---Convolves resource count for a grid cell
|
||||
---@param ox any
|
||||
---@param oy any
|
||||
---@param size any
|
||||
function grid_mt:convolve_outer(ox, oy, size, amount)
|
||||
local nx1, nx2 = ox, ox+size - 1
|
||||
local ny1, ny2 = oy, oy+size - 1
|
||||
|
||||
for y = ny1, ny2 do
|
||||
---@type table<number, GridTile>
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = nx1, nx2 do
|
||||
local tile = row[x]
|
||||
if tile == nil then goto continue_column end
|
||||
tile.neighbors_outer = tile.neighbors_outer + 1
|
||||
tile.neighbor_amount = tile.neighbor_amount + amount
|
||||
::continue_column::
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
end
|
||||
|
||||
---@param ox number
|
||||
---@param oy number
|
||||
---@param drill MinerStruct
|
||||
function grid_mt:convolve_miner(ox, oy, drill)
|
||||
local x1, y1 = ox-drill.extent_positive, ox-drill.extent_positive
|
||||
local x2, y2 = x1+drill.area, y1+drill.area
|
||||
local ix1, iy1 = ox-drill.size+1, oy-drill.size+1
|
||||
local ix2, iy2 = x1+drill.size, y1+drill.size
|
||||
|
||||
for y = y1, y2 do
|
||||
local row = self[y]
|
||||
if row then
|
||||
for x = x1, x2 do
|
||||
---@type GridTile
|
||||
local tile = row[x]
|
||||
if tile then
|
||||
tile.neighbors_outer = tile.neighbors_outer + 1
|
||||
if ix1 < x and x < ix2 and iy1 < y and y < iy2 then
|
||||
tile.neighbors_outer = tile.neighbors_outer + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Marks tiles (with resources) as consumed by a mining drill
|
||||
---@param ox integer
|
||||
---@param oy integer
|
||||
function grid_mt:consume(ox, oy, size)
|
||||
local nx1, nx2 = ox, ox+size - 1
|
||||
local ny1, ny2 = oy, oy+size - 1
|
||||
|
||||
for y = ny1, ny2 do
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = nx1, nx2 do
|
||||
local tile = row[x]
|
||||
if tile and tile.amount > 0 then
|
||||
tile.consumed = true
|
||||
end
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
end
|
||||
|
||||
---@param cx number
|
||||
---@param cy number
|
||||
---@param w number
|
||||
---@param evenw boolean
|
||||
---@param evenh boolean
|
||||
---@deprecated
|
||||
function grid_mt:consume_custom(cx, cy, w, evenw, evenh)
|
||||
local ox, oy = evenw and 1 or 0, evenh and 1 or 0
|
||||
local x1, x2 = cx+ox-w, cx+w
|
||||
for y = cy+oy-w, cy+w do
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = x1, x2 do
|
||||
local tile = row[x]
|
||||
if tile and tile.amount then
|
||||
tile.consumed = true
|
||||
end
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
end
|
||||
|
||||
---Marks tiles as consumed by a miner
|
||||
---@param tiles GridTile[]
|
||||
function grid_mt:clear_consumed(tiles)
|
||||
for _, tile in pairs(tiles) do
|
||||
---@cast tile GridTile
|
||||
tile.consumed = false
|
||||
end
|
||||
end
|
||||
|
||||
---Builder function
|
||||
---@param cx number x coord
|
||||
---@param cy number y coord
|
||||
---@param size_w number
|
||||
---@param thing GridBuilding Type of building
|
||||
function grid_mt:build_thing(cx, cy, thing, size_w, size_h)
|
||||
size_h = size_h or size_w
|
||||
for y = cy, cy + size_h do
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = cx, cx + size_w do
|
||||
local tile = row[x]
|
||||
if tile then
|
||||
tile.built_on = thing
|
||||
end
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
end
|
||||
|
||||
function grid_mt:build_thing_simple(cx, cy, thing)
|
||||
local row = self[cy]
|
||||
if row then
|
||||
local tile = row[cx]
|
||||
if tile then
|
||||
tile.built_on = thing
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param t GhostSpecification
|
||||
function grid_mt:build_specification(t)
|
||||
local cx, cy = t.grid_x, t.grid_y
|
||||
local left, right = t.padding_pre, t.padding_post
|
||||
local thing = t.thing
|
||||
|
||||
if left == nil and right == nil then
|
||||
local row = self[cy]
|
||||
if row then
|
||||
local tile = row[cx]
|
||||
if tile then
|
||||
tile.built_on = thing
|
||||
end
|
||||
end
|
||||
else
|
||||
left, right = left or 0, right or 0
|
||||
local x1, x2 = cx-left, cx+right
|
||||
for y = cy-left, cy+right do
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = x1, x2 do
|
||||
local tile = row[x]
|
||||
if tile then
|
||||
tile.built_on = thing
|
||||
end
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Finds if an entity type is built near
|
||||
---@param cx number x coord
|
||||
---@param cy number y coord
|
||||
---@param thing GridBuilding Type of building
|
||||
---@return boolean
|
||||
function grid_mt:find_thing(cx, cy, thing, size)
|
||||
local x1, x2 = cx, cx + size
|
||||
for y = cy, cy+size do
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = x1, x2 do
|
||||
local tile = row[x]
|
||||
if tile and tile.built_on == thing then
|
||||
return true
|
||||
end
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---Finds if an entity type is built near
|
||||
---@param cx number x coord
|
||||
---@param cy number y coord
|
||||
---@param things table<string, true> Types of entities
|
||||
---@param r number Radius
|
||||
---@param even boolean Is even width building
|
||||
---@return boolean
|
||||
function grid_mt:find_thing_in(cx, cy, things, r, even)
|
||||
things = mpp_util.list_to_keys(things)
|
||||
local o = even and 1 or 0
|
||||
for y = cy+o-r, cy+r do
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = cx+o-r, cx+r do
|
||||
local tile = row[x]
|
||||
if tile and things[tile.built_on] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function grid_mt:build_miner(cx, cy, size)
|
||||
self:build_thing(cx, cy, "miner", size, size)
|
||||
end
|
||||
|
||||
function grid_mt:get_unconsumed(ox, oy, size)
|
||||
local nx1, nx2 = ox, ox+size - 1
|
||||
local ny1, ny2 = oy, oy+size - 1
|
||||
local count = 0
|
||||
|
||||
for y = ny1, ny2 do
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = nx1, nx2 do
|
||||
local tile = row[x]
|
||||
if tile and tile.amount > 0 and not tile.consumed then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
---@param mx number
|
||||
---@param my number
|
||||
---@param pole number|PoleStruct
|
||||
---@return boolean
|
||||
function grid_mt:needs_power(mx, my, pole)
|
||||
local nx1, nx2, ny1, ny2
|
||||
if type(pole) == "table" then
|
||||
local size = pole.size
|
||||
local extent = ceil((pole.supply_width-size) / 2)
|
||||
nx1, nx2 = mx - extent, mx + extent
|
||||
ny1, ny2 = my - extent, my + extent
|
||||
else
|
||||
nx1, nx2 = mx, mx+pole-1
|
||||
ny1, ny2 = my, my+pole-1
|
||||
end
|
||||
|
||||
for y = ny1, ny2 do
|
||||
local row = self[y]
|
||||
if row == nil then goto continue_row end
|
||||
for x = nx1, nx2 do
|
||||
---@type GridTile
|
||||
local tile = row[x]
|
||||
if tile and need_electricity[tile.built_on] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
::continue_row::
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return grid_mt
|
||||
34
mining-patch-planner/mpp/list.lua
Normal file
34
mining-patch-planner/mpp/list.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
local table_insert = table.insert
|
||||
|
||||
---@class List
|
||||
local list_mt = {}
|
||||
list_mt.__index = list_mt
|
||||
|
||||
function list_mt:push(value)
|
||||
table_insert(self, value)
|
||||
return self
|
||||
end
|
||||
|
||||
function list_mt:unshift(value)
|
||||
table_insert(self, 1, value)
|
||||
return self
|
||||
end
|
||||
|
||||
function list_mt:append(...)
|
||||
for _, value in pairs({...}) do
|
||||
table_insert(self, value)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function list_mt:contitional_append(check, ...)
|
||||
if not check then return self end
|
||||
for _, value in pairs({...}) do
|
||||
table_insert(self, value)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
return function(t)
|
||||
return setmetatable(t or {}, list_mt)
|
||||
end
|
||||
765
mining-patch-planner/mpp/mpp_util.lua
Normal file
765
mining-patch-planner/mpp/mpp_util.lua
Normal file
@@ -0,0 +1,765 @@
|
||||
local enums = require("mpp.enums")
|
||||
local blacklist = require("mpp.blacklist")
|
||||
local floor, ceil = math.floor, math.ceil
|
||||
local min, max = math.min, math.max
|
||||
|
||||
local EAST = defines.direction.east
|
||||
local NORTH = defines.direction.north
|
||||
local SOUTH = defines.direction.south
|
||||
local WEST = defines.direction.west
|
||||
|
||||
---@alias DirectionString
|
||||
---| "west"
|
||||
---| "east"
|
||||
---| "south"
|
||||
---| "north"
|
||||
|
||||
local mpp_util = {}
|
||||
|
||||
---@alias CoordinateConverterFunction fun(number, number, number, number): number, number
|
||||
|
||||
---@type table<DirectionString, CoordinateConverterFunction>
|
||||
local coord_convert = {
|
||||
west = function(x, y, w, h) return x, y end,
|
||||
east = function(x, y, w, h) return w-x, h-y end,
|
||||
south = function(x, y, w, h) return h-y, x end,
|
||||
north = function(x, y, w, h) return y, w-x end,
|
||||
}
|
||||
mpp_util.coord_convert = coord_convert
|
||||
|
||||
---@type table<DirectionString, CoordinateConverterFunction>
|
||||
local coord_revert = {
|
||||
west = coord_convert.west,
|
||||
east = coord_convert.east,
|
||||
north = coord_convert.south,
|
||||
south = coord_convert.north,
|
||||
}
|
||||
mpp_util.coord_revert = coord_revert
|
||||
|
||||
mpp_util.miner_direction = {west="south",east="north",north="west",south="east"}
|
||||
mpp_util.belt_direction = {west="north", east="south", north="east", south="west"}
|
||||
mpp_util.opposite = {west="east",east="west",north="south",south="north"}
|
||||
|
||||
do
|
||||
-- switch to (direction + EAST) % ROTATION
|
||||
local d = defines.direction
|
||||
mpp_util.bp_direction = {
|
||||
west = {
|
||||
[d.north] = d.north,
|
||||
[d.east] = d.east,
|
||||
[d.south] = d.south,
|
||||
[d.west] = d.west,
|
||||
},
|
||||
north = {
|
||||
[d.north] = d.east,
|
||||
[d.east] = d.south,
|
||||
[d.south] = d.west,
|
||||
[d.west] = d.north,
|
||||
},
|
||||
east = {
|
||||
[d.north] = d.south,
|
||||
[d.east] = d.west,
|
||||
[d.south] = d.north,
|
||||
[d.west] = d.east,
|
||||
},
|
||||
south = {
|
||||
[d.north] = d.west,
|
||||
[d.east] = d.north,
|
||||
[d.south] = d.east,
|
||||
[d.west] = d.south,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
---@type table<defines.direction, MapPosition.0>
|
||||
local direction_coord = {
|
||||
[NORTH] = {x=0, y=-1},
|
||||
[WEST] = {x=-1, y=0},
|
||||
[SOUTH] = {x=0, y=1},
|
||||
[EAST] = {x=1, y=0},
|
||||
}
|
||||
mpp_util.direction_coord = direction_coord
|
||||
|
||||
---@class EntityStruct
|
||||
---@field name string
|
||||
---@field type string
|
||||
---@field w number Collision width
|
||||
---@field h number Collision height
|
||||
---@field x number x origin
|
||||
---@field y number y origin
|
||||
---@field size number?
|
||||
---@field extent_w number Half of width
|
||||
---@field extent_h number Half of height
|
||||
|
||||
---@type table<string, EntityStruct>
|
||||
local entity_cache = {}
|
||||
|
||||
---@param entity_name string
|
||||
---@return EntityStruct
|
||||
function mpp_util.entity_struct(entity_name)
|
||||
local cached = entity_cache[entity_name]
|
||||
if cached then return cached end
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
local struct = {} --[[@as EntityStruct]]
|
||||
local proto = game.entity_prototypes[entity_name]
|
||||
|
||||
local cbox = proto.collision_box
|
||||
local cbox_tl, cbox_br = cbox.left_top, cbox.right_bottom
|
||||
local cw, ch = cbox_br.x - cbox_tl.x, cbox_br.y - cbox_tl.y
|
||||
struct.name = entity_name
|
||||
struct.type = proto.type
|
||||
struct.w, struct.h = ceil(cw), ceil(ch)
|
||||
struct.size = max(struct.w, struct.h)
|
||||
struct.x = struct.w / 2 - 0.5
|
||||
struct.y = struct.h / 2 - 0.5
|
||||
struct.extent_w, struct.extent_h = struct.w / 2, struct.h / 2
|
||||
|
||||
entity_cache[entity_name] = struct
|
||||
return struct
|
||||
end
|
||||
|
||||
---@param struct EntityStruct
|
||||
---@param direction defines.direction
|
||||
---@return number, number, number, number
|
||||
function mpp_util.rotate_struct(struct, direction)
|
||||
if direction == NORTH or direction == SOUTH then
|
||||
return struct.x, struct.y, struct.w, struct.h
|
||||
end
|
||||
return struct.y, struct.x, struct.h, struct.w
|
||||
end
|
||||
|
||||
---A mining drill's origin (0, 0) is the top left corner
|
||||
---The spawn location is (x, y), rotations need to rotate around
|
||||
---@class MinerStruct : EntityStruct
|
||||
---@field size number Physical miner size
|
||||
---@field size_sq number Size squared
|
||||
---@field symmetric boolean
|
||||
---@field parity (-1|0) Parity offset for even sized drills, -1 when odd
|
||||
---@field resource_categories table<string, boolean>
|
||||
---@field radius float Mining area reach
|
||||
---@field area number Full coverage span of the miner
|
||||
---@field area_sq number Squared area
|
||||
---@field outer_span number Lenght between physical size and end of radius
|
||||
---@field module_inventory_size number
|
||||
---@field middle number "Center" x position
|
||||
---@field drop_pos MapPosition Raw drop position
|
||||
---@field out_x integer Resource drop position x
|
||||
---@field out_y integer Resource drop position y
|
||||
---@field extent_negative number
|
||||
---@field extent_positive number
|
||||
---@field supports_fluids boolean
|
||||
---@field skip_outer boolean Skip outer area calculations
|
||||
---@field pipe_left number Y height on left side
|
||||
---@field pipe_right number Y height on right side
|
||||
---@field output_rotated table<defines.direction, MapPosition> Rotated output positions in reference to (0, 0) origin
|
||||
---@field power_source_tooltip (string|table)?
|
||||
|
||||
---@type table<string, MinerStruct>
|
||||
local miner_struct_cache = {}
|
||||
|
||||
---Calculates values for drill sizes and extents
|
||||
---@param mining_drill_name string
|
||||
---@return MinerStruct
|
||||
function mpp_util.miner_struct(mining_drill_name)
|
||||
local cached = miner_struct_cache[mining_drill_name]
|
||||
if cached then return cached end
|
||||
|
||||
local miner_proto = game.entity_prototypes[mining_drill_name]
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
local miner = mpp_util.entity_struct(mining_drill_name) --[[@as MinerStruct]]
|
||||
if miner.w ~= miner.h then
|
||||
-- we have a problem ?
|
||||
end
|
||||
miner.size_sq = miner.size ^ 2
|
||||
miner.symmetric = miner.size % 2 == 1
|
||||
miner.parity = miner.size % 2 - 1
|
||||
miner.radius = miner_proto.mining_drill_radius
|
||||
miner.area = ceil(miner_proto.mining_drill_radius * 2)
|
||||
miner.area_sq = miner.area ^ 2
|
||||
miner.outer_span = floor((miner.area - miner.size) / 2)
|
||||
miner.resource_categories = miner_proto.resource_categories
|
||||
miner.name = miner_proto.name
|
||||
miner.module_inventory_size = miner_proto.module_inventory_size
|
||||
miner.extent_negative = floor(miner.size * 0.5) - floor(miner_proto.mining_drill_radius) + miner.parity
|
||||
miner.extent_positive = miner.extent_negative + miner.area - 1
|
||||
miner.middle = floor(miner.size / 2) + miner.parity
|
||||
|
||||
local nauvis = game.get_surface("nauvis") --[[@as LuaSurface]]
|
||||
|
||||
local dummy = nauvis.create_entity{
|
||||
name = mining_drill_name,
|
||||
position = {miner.x, miner.y},
|
||||
}
|
||||
|
||||
if dummy then
|
||||
miner.drop_pos = dummy.drop_position
|
||||
miner.out_x = floor(dummy.drop_position.x)
|
||||
miner.out_y = floor(dummy.drop_position.y)
|
||||
dummy.destroy()
|
||||
else
|
||||
-- hardcoded fallback
|
||||
local dx, dy = floor(miner.size / 2) + miner.parity, -1
|
||||
miner.drop_pos = { dx+.5, -0.296875, x = dx+.5, y = -0.296875 }
|
||||
miner.out_x = dx
|
||||
miner.out_y = dy
|
||||
end
|
||||
|
||||
local output_rotated = {
|
||||
[defines.direction.north] = {miner.out_x, miner.out_y},
|
||||
[defines.direction.south] = {miner.size - miner.out_x - 1, miner.size },
|
||||
[defines.direction.west] = {miner.size, miner.out_x},
|
||||
[defines.direction.east] = {-1, miner.size - miner.out_x -1},
|
||||
}
|
||||
output_rotated[NORTH].x = output_rotated[NORTH][1]
|
||||
output_rotated[NORTH].y = output_rotated[NORTH][2]
|
||||
output_rotated[SOUTH].x = output_rotated[SOUTH][1]
|
||||
output_rotated[SOUTH].y = output_rotated[SOUTH][2]
|
||||
output_rotated[WEST].x = output_rotated[WEST][1]
|
||||
output_rotated[WEST].y = output_rotated[WEST][2]
|
||||
output_rotated[EAST].x = output_rotated[EAST][1]
|
||||
output_rotated[EAST].y = output_rotated[EAST][2]
|
||||
|
||||
miner.output_rotated = output_rotated
|
||||
|
||||
--pipe height stuff
|
||||
if miner_proto.fluidbox_prototypes and #miner_proto.fluidbox_prototypes > 0 then
|
||||
local connections = miner_proto.fluidbox_prototypes[1].pipe_connections
|
||||
|
||||
for _, conn in pairs(connections) do
|
||||
---@cast conn FluidBoxConnection
|
||||
-- pray a mod that does weird stuff with pipe connections doesn't appear
|
||||
end
|
||||
|
||||
miner.pipe_left = floor(miner.size / 2) + miner.parity
|
||||
miner.pipe_right = floor(miner.size / 2) + miner.parity
|
||||
miner.supports_fluids = true
|
||||
else
|
||||
miner.supports_fluids = false
|
||||
end
|
||||
|
||||
-- If larger than a large mining drill
|
||||
miner.skip_outer = miner.size > 7 or miner.area > 13
|
||||
|
||||
if miner_proto.electric_energy_source_prototype then
|
||||
miner.power_source_tooltip = {
|
||||
"", " [img=tooltip-category-electricity] ",
|
||||
{"tooltip-category.consumes"}, " ", {"tooltip-category.electricity"},
|
||||
}
|
||||
elseif miner_proto.burner_prototype then
|
||||
local burner = miner_proto.burner_prototype --[[@as LuaBurnerPrototype]]
|
||||
if burner.fuel_categories["nuclear"] then
|
||||
miner.power_source_tooltip = {
|
||||
"", "[img=tooltip-category-nuclear]",
|
||||
{"tooltip-category.consumes"}, " ", {"fuel-category-name.nuclear"},
|
||||
}
|
||||
else
|
||||
miner.power_source_tooltip = {
|
||||
"", "[img=tooltip-category-chemical]",
|
||||
{"tooltip-category.consumes"}, " ", {"fuel-category-name.chemical"},
|
||||
}
|
||||
end
|
||||
elseif miner_proto.fluid_energy_source_prototype then
|
||||
miner.power_source_tooltip = {
|
||||
"", "[img=tooltip-category-water]",
|
||||
{"tooltip-category.consumes"}, " ", {"tooltip-category.fluid"},
|
||||
}
|
||||
end
|
||||
|
||||
return miner
|
||||
end
|
||||
|
||||
---@class PoleStruct : EntityStruct
|
||||
---@field name string
|
||||
---@field place boolean Flag if poles are to be actually placed
|
||||
---@field size number
|
||||
---@field radius number Power supply reach
|
||||
---@field supply_width number Full width of supply reach
|
||||
---@field wire number Max wire distance
|
||||
---@field supply_area_distance number
|
||||
---@field extent_negative number Negative extent of the supply reach
|
||||
|
||||
---@type table<string, PoleStruct>
|
||||
local pole_struct_cache = {}
|
||||
|
||||
---@param pole_name string
|
||||
---@return PoleStruct
|
||||
function mpp_util.pole_struct(pole_name)
|
||||
local cached_struct = pole_struct_cache[pole_name]
|
||||
if cached_struct then return cached_struct end
|
||||
|
||||
local pole_proto = game.entity_prototypes[pole_name]
|
||||
if pole_proto then
|
||||
local pole = mpp_util.entity_struct(pole_name) --[[@as PoleStruct]]
|
||||
|
||||
local radius = pole_proto.supply_area_distance --[[@as number]]
|
||||
pole.supply_area_distance = radius
|
||||
pole.supply_width = floor(radius * 2)
|
||||
pole.radius = pole.supply_width / 2
|
||||
pole.wire = pole_proto.max_wire_distance
|
||||
|
||||
-- local distance = beacon_proto.supply_area_distance
|
||||
-- beacon.area = beacon.size + distance * 2
|
||||
-- beacon.extent_negative = -distance
|
||||
|
||||
local extent = (pole.supply_width - pole.size) / 2
|
||||
|
||||
pole.extent_negative = -extent
|
||||
|
||||
pole_struct_cache[pole_name] = pole
|
||||
return pole
|
||||
end
|
||||
return {
|
||||
place = false, -- nonexistent pole, use fallbacks and don't place
|
||||
size = 1,
|
||||
supply_width = 7,
|
||||
radius = 3.5,
|
||||
wire = 9,
|
||||
}
|
||||
end
|
||||
|
||||
---@class BeaconStruct : EntityStruct
|
||||
---@field extent_negative number
|
||||
---@field area number
|
||||
|
||||
local beacon_cache = {}
|
||||
|
||||
---@param beacon_name string
|
||||
---@return BeaconStruct
|
||||
function mpp_util.beacon_struct(beacon_name)
|
||||
local cached_struct = beacon_cache[beacon_name]
|
||||
if cached_struct then return cached_struct end
|
||||
|
||||
local beacon_proto = game.entity_prototypes[beacon_name]
|
||||
local beacon = mpp_util.entity_struct(beacon_name) --[[@as BeaconStruct]]
|
||||
|
||||
local distance = beacon_proto.supply_area_distance
|
||||
beacon.area = beacon.size + distance * 2
|
||||
beacon.extent_negative = -distance
|
||||
|
||||
beacon_cache[beacon_name] = beacon
|
||||
return beacon
|
||||
end
|
||||
|
||||
|
||||
local hardcoded_pipes = {}
|
||||
|
||||
---@param pipe_name string Name of the normal pipe
|
||||
---@return string|nil, LuaEntityPrototype|nil
|
||||
function mpp_util.find_underground_pipe(pipe_name)
|
||||
if hardcoded_pipes[pipe_name] then
|
||||
return hardcoded_pipes[pipe_name], game.entity_prototypes[hardcoded_pipes[pipe_name]]
|
||||
end
|
||||
local ground_name = pipe_name.."-to-ground"
|
||||
local ground_proto = game.entity_prototypes[ground_name]
|
||||
if ground_proto then
|
||||
return ground_name, ground_proto
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
function mpp_util.revert(gx, gy, direction, x, y, w, h)
|
||||
local tx, ty = coord_revert[direction](x-.5, y-.5, w, h)
|
||||
return {gx + tx+.5, gy + ty + .5}
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param gx any
|
||||
---@param gy any
|
||||
---@param direction any
|
||||
---@param x any
|
||||
---@param y any
|
||||
---@param w any
|
||||
---@param h any
|
||||
---@return unknown
|
||||
---@return unknown
|
||||
function mpp_util.revert_ex(gx, gy, direction, x, y, w, h)
|
||||
local tx, ty = coord_revert[direction](x-.5, y-.5, w, h)
|
||||
return gx + tx+.5, gy + ty + .5
|
||||
end
|
||||
|
||||
function mpp_util.revert_world(gx, gy, direction, x, y, w, h)
|
||||
local tx, ty = coord_revert[direction](x-.5, y-.5, w, h)
|
||||
return {gx + tx, gy + ty}
|
||||
end
|
||||
|
||||
---@class BeltStruct
|
||||
---@field name string
|
||||
---@field related_underground_belt string?
|
||||
---@field underground_reach number?
|
||||
|
||||
---@type table<string, BeltStruct>
|
||||
local belt_struct_cache = {}
|
||||
|
||||
function mpp_util.belt_struct(belt_name)
|
||||
local cached = belt_struct_cache[belt_name]
|
||||
if cached then return cached end
|
||||
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
local belt = {} --[[@as BeltStruct]]
|
||||
local belt_proto = game.entity_prototypes[belt_name]
|
||||
|
||||
belt.name = belt_name
|
||||
|
||||
local related = belt_proto.related_underground_belt
|
||||
if related then
|
||||
belt.related_underground_belt = related.name
|
||||
belt.underground_reach = related.max_underground_distance
|
||||
else
|
||||
local match_attempts = {
|
||||
["transport"] = "underground",
|
||||
["belt"] = "underground-belt",
|
||||
}
|
||||
for pattern, replacement in pairs(match_attempts) do
|
||||
local new_name = string.gsub(belt_name, pattern, replacement)
|
||||
if new_name == belt_name then goto continue end
|
||||
related = game.entity_prototypes[new_name]
|
||||
if related then
|
||||
belt.related_underground_belt = new_name
|
||||
belt.underground_reach = related.max_underground_distance
|
||||
break
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
belt_struct_cache[belt_name] = belt
|
||||
return belt
|
||||
end
|
||||
|
||||
---@class InserterStruct : EntityStruct
|
||||
---@field pickup_rotated table<defines.direction, MapPosition.0>
|
||||
---@field drop_rotated table<defines.direction, MapPosition.0>
|
||||
|
||||
---@type table<string, InserterStruct>
|
||||
local inserter_struct_cache = {}
|
||||
|
||||
function mpp_util.inserter_struct(inserter_name)
|
||||
local cached = inserter_struct_cache[inserter_name]
|
||||
if cached then return cached end
|
||||
|
||||
local inserter_proto = game.entity_prototypes[inserter_name]
|
||||
local inserter = mpp_util.entity_struct(inserter_name) --[[@as InserterStruct]]
|
||||
|
||||
local function rotations(_x, _y)
|
||||
_x, _y = floor(_x), floor(_y)
|
||||
return {
|
||||
[NORTH] = { x = _x, y = _y},
|
||||
[EAST] = { x = -_y, y = -_x},
|
||||
[SOUTH] = { x = -_x, y = -_y},
|
||||
[WEST] = { x = _y, y = _x},
|
||||
}
|
||||
end
|
||||
|
||||
local pickup_position = inserter_proto.inserter_pickup_position --[[@as MapPosition.1]]
|
||||
local drop_position = inserter_proto.inserter_drop_position --[[@as MapPosition.1]]
|
||||
inserter.pickup_rotated = rotations(pickup_position[1], pickup_position[2])
|
||||
inserter.drop_rotated = rotations(drop_position[1], drop_position[2])
|
||||
|
||||
inserter_struct_cache[inserter_name] = inserter
|
||||
return inserter
|
||||
end
|
||||
|
||||
---Calculates needed power pole count
|
||||
---@param state SimpleState
|
||||
function mpp_util.calculate_pole_coverage(state, miner_count, lane_count)
|
||||
local cov = {}
|
||||
local m = mpp_util.miner_struct(state.miner_choice)
|
||||
local p = mpp_util.pole_struct(state.pole_choice)
|
||||
|
||||
-- Shift subtract
|
||||
local covered_miners = ceil(p.supply_width / m.size)
|
||||
local miner_step = covered_miners * m.size
|
||||
|
||||
-- Special handling to shift back small radius power poles so they don't poke out
|
||||
local capable_span = false
|
||||
if floor(p.wire) >= miner_step and m.size ~= p.supply_width then
|
||||
capable_span = true
|
||||
else
|
||||
miner_step = floor(p.wire)
|
||||
end
|
||||
cov.capable_span = capable_span
|
||||
|
||||
local pole_start = m.middle
|
||||
if capable_span then
|
||||
if covered_miners % 2 == 0 then
|
||||
pole_start = m.size-1
|
||||
elseif miner_count % covered_miners == 0 then
|
||||
pole_start = pole_start + m.size
|
||||
end
|
||||
end
|
||||
|
||||
cov.pole_start = pole_start
|
||||
cov.pole_step = miner_step
|
||||
cov.full_miner_width = miner_count * m.size
|
||||
|
||||
cov.lane_start = 0
|
||||
cov.lane_step = m.size * 2 + 2
|
||||
local lane_pairs = floor(lane_count / 2)
|
||||
local lane_coverage = ceil((p.radius-1) / (m.size + 0.5))
|
||||
if lane_coverage > 1 then
|
||||
cov.lane_start = (ceil(lane_pairs / 2) % 2 == 0 and 1 or 0) * (m.size * 2 + 2)
|
||||
cov.lane_step = lane_coverage * (m.size * 2 + 2)
|
||||
end
|
||||
|
||||
cov.lamp_alter = miner_step < 9 and true or false
|
||||
|
||||
return cov
|
||||
end
|
||||
|
||||
---Calculates the spacing for belt interleaved power poles
|
||||
---@param state State
|
||||
---@param miner_count number
|
||||
---@param lane_count number
|
||||
---@param force_capable (number|true)?
|
||||
function mpp_util.calculate_pole_spacing(state, miner_count, lane_count, force_capable)
|
||||
local cov = {}
|
||||
local m = mpp_util.miner_struct(state.miner_choice)
|
||||
local p = mpp_util.pole_struct(state.pole_choice)
|
||||
|
||||
-- Shift subtract
|
||||
local covered_miners = ceil(p.supply_width / m.size)
|
||||
local miner_step = covered_miners * m.size
|
||||
if force_capable then
|
||||
miner_step = force_capable == true and miner_step or force_capable --[[@as number]]
|
||||
force_capable = miner_step
|
||||
end
|
||||
|
||||
-- Special handling to shift back small radius power poles so they don't poke out
|
||||
local capable_span = false
|
||||
|
||||
if floor(p.wire) >= miner_step then
|
||||
capable_span = true
|
||||
elseif force_capable and force_capable > 0 then
|
||||
return mpp_util.calculate_pole_spacing(
|
||||
state, miner_count, lane_count, miner_step - m.size
|
||||
)
|
||||
else
|
||||
miner_step = floor(p.wire)
|
||||
end
|
||||
cov.capable_span = capable_span
|
||||
|
||||
local pole_start = m.size-1
|
||||
if capable_span then
|
||||
if covered_miners % 2 == 0 then
|
||||
pole_start = m.size-1
|
||||
elseif miner_count % covered_miners == 0 and miner_step ~= m.size then
|
||||
pole_start = pole_start + m.size
|
||||
end
|
||||
end
|
||||
|
||||
cov.pole_start = pole_start
|
||||
cov.pole_step = miner_step
|
||||
cov.full_miner_width = miner_count * m.size
|
||||
|
||||
cov.lane_start = 0
|
||||
cov.lane_step = m.size * 2 + 2
|
||||
local lane_pairs = floor(lane_count / 2)
|
||||
local lane_coverage = ceil((p.radius-1) / (m.size + 0.5))
|
||||
if lane_coverage > 1 then
|
||||
cov.lane_start = (ceil(lane_pairs / 2) % 2 == 0 and 1 or 0) * (m.size * 2 + 2)
|
||||
cov.lane_step = lane_coverage * (m.size * 2 + 2)
|
||||
end
|
||||
|
||||
return cov
|
||||
end
|
||||
|
||||
---@param t table
|
||||
---@param func function
|
||||
---@return true | nil
|
||||
function mpp_util.table_find(t, func)
|
||||
for k, v in pairs(t) do
|
||||
if func(v) then return true end
|
||||
end
|
||||
end
|
||||
|
||||
---@param t table
|
||||
---@param m LuaObject
|
||||
function mpp_util.table_mapping(t, m)
|
||||
for k, v in pairs(t) do
|
||||
if k == m then return v end
|
||||
end
|
||||
end
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@param blueprint LuaItemStack
|
||||
function mpp_util.validate_blueprint(player, blueprint)
|
||||
if not blueprint.blueprint_snap_to_grid then
|
||||
player.print({"", "[color=red]", {"mpp.msg_blueprint_undefined_grid"}, "[/color]"}, {sound_path="utility/cannot_build"})
|
||||
return false
|
||||
end
|
||||
|
||||
local miners, _ = enums.get_available_miners()
|
||||
local cost = blueprint.cost_to_build
|
||||
local drills = {}
|
||||
for name, drill in pairs(miners) do
|
||||
if cost[name] then
|
||||
drills[#drills+1] = drill.localised_name
|
||||
end
|
||||
end
|
||||
|
||||
if #drills > 1 then
|
||||
local msg = {"", "[color=red]", {"mpp.msg_blueprint_different_miners"}, "[/color]" }
|
||||
for _, name in pairs(drills) do
|
||||
msg[#msg+1] = "\n"
|
||||
msg[#msg+1] = name
|
||||
end
|
||||
player.print(msg, {sound_path="utility/cannot_build"})
|
||||
return false
|
||||
elseif #drills == 0 then
|
||||
player.print({"", "[color=red]", {"mpp.msg_blueprint_no_miner"}, "[/color]"}, {sound_path="utility/cannot_build"})
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function mpp_util.keys_to_set(...)
|
||||
local set, temp = {}, {}
|
||||
for _, t in pairs{...} do
|
||||
for k, _ in pairs(t) do
|
||||
temp[k] = true
|
||||
end
|
||||
end
|
||||
for k, _ in pairs(temp) do
|
||||
set[#set+1] = k
|
||||
end
|
||||
table.sort(set)
|
||||
return set
|
||||
end
|
||||
|
||||
function mpp_util.list_to_keys(t)
|
||||
local temp = {}
|
||||
for _, k in ipairs(t) do
|
||||
temp[k] = true
|
||||
end
|
||||
return temp
|
||||
end
|
||||
|
||||
---@param bp LuaItemStack
|
||||
function mpp_util.blueprint_label(bp)
|
||||
local label = bp.label
|
||||
if label then
|
||||
if #label > 30 then
|
||||
return string.sub(label, 0, 28) .. "...", label
|
||||
end
|
||||
return label
|
||||
else
|
||||
return {"", {"gui-blueprint.unnamed-blueprint"}, " ", bp.item_number}
|
||||
end
|
||||
end
|
||||
|
||||
---@class CollisionBoxProperties
|
||||
---@field w number
|
||||
---@field h number
|
||||
---@field near number
|
||||
---@field [1] boolean
|
||||
---@field [2] boolean
|
||||
|
||||
-- LuaEntityPrototype#tile_height was added in 1.1.64, I'm developing on 1.1.61
|
||||
local even_width_memoize = {}
|
||||
---Gets properties of entity collision box
|
||||
---@param name string
|
||||
---@return CollisionBoxProperties
|
||||
function mpp_util.entity_even_width(name)
|
||||
local check = even_width_memoize[name]
|
||||
if check then return check end
|
||||
local proto = game.entity_prototypes[name]
|
||||
local cbox = proto.collision_box
|
||||
local cbox_tl, cbox_br = cbox.left_top, cbox.right_bottom
|
||||
local cw, ch = cbox_br.x - cbox_tl.x, cbox_br.y - cbox_tl.y
|
||||
local w, h = ceil(cw), ceil(ch)
|
||||
local res = {w % 2 ~= 1, h % 2 ~= 1, w=w, h=h, near=floor(w/2)}
|
||||
even_width_memoize[name] = res
|
||||
return res
|
||||
end
|
||||
|
||||
--- local EAST, NORTH, SOUTH, WEST, ROTATION = mpp_util.directions()
|
||||
function mpp_util.directions()
|
||||
return
|
||||
defines.direction.east,
|
||||
defines.direction.north,
|
||||
defines.direction.south,
|
||||
defines.direction.west,
|
||||
table_size(defines.direction)
|
||||
end
|
||||
|
||||
---@param player_index uint
|
||||
---@return uint
|
||||
function mpp_util.get_display_duration(player_index)
|
||||
return settings.get_player_settings(player_index)["mpp-lane-filling-info-duration"].value * 60 --[[@as uint]]
|
||||
end
|
||||
|
||||
---@param player_index uint
|
||||
---@return boolean
|
||||
function mpp_util.get_dump_state(player_index)
|
||||
return settings.get_player_settings(player_index)["mpp-dump-heuristics-data"].value --[[@as boolean]]
|
||||
end
|
||||
|
||||
function mpp_util.wrap_tooltip(...)
|
||||
return select(1, ...) and {"", " ", ...} or nil
|
||||
end
|
||||
|
||||
function mpp_util.tooltip_entity_not_available(check, arg)
|
||||
if check then
|
||||
return mpp_util.wrap_tooltip(arg, "\n[color=red]", {"mpp.label_not_available"}, "[/color]")
|
||||
end
|
||||
return mpp_util.wrap_tooltip(arg)
|
||||
end
|
||||
|
||||
---@param c1 Coords
|
||||
---@param c2 Coords
|
||||
function mpp_util.coords_overlap(c1, c2)
|
||||
local x = (c1.ix1 <= c2.ix1 and c2.ix1 <= c1.ix2) or (c1.ix1 <= c2.ix2 and c2.ix2 <= c1.ix2) or
|
||||
(c2.ix1 <= c1.ix1 and c1.ix1 <= c2.ix2) or (c2.ix1 <= c1.ix2 and c1.ix2 <= c2.ix2)
|
||||
local y = (c1.iy1 <= c2.iy1 and c2.iy1 <= c1.iy2) or (c1.iy1 <= c2.iy2 and c2.iy2 <= c1.iy2) or
|
||||
(c2.iy1 <= c1.iy1 and c1.iy1 <= c2.iy2) or (c2.iy1 <= c1.iy2 and c1.iy2 <= c2.iy2)
|
||||
return x and y
|
||||
end
|
||||
|
||||
---Checks if thing (entity) should never appear as a choice
|
||||
---@param thing LuaEntityPrototype|MinerStruct
|
||||
---@return boolean|nil
|
||||
function mpp_util.check_filtered(thing)
|
||||
return
|
||||
blacklist[thing.name]
|
||||
or (thing.flags and thing.flags.hidden)
|
||||
end
|
||||
|
||||
---@param player_data any
|
||||
---@param category MppSettingSections
|
||||
---@param name string
|
||||
function mpp_util.set_entity_hidden(player_data, category, name, value)
|
||||
player_data.filtered_entities[category..":"..name] = value
|
||||
end
|
||||
|
||||
function mpp_util.get_entity_hidden(player_data, category, name)
|
||||
return player_data.filtered_entities[category..":"..name]
|
||||
end
|
||||
|
||||
---Checks if a player has hidden the entity choice
|
||||
---@param player_data any
|
||||
---@param category MppSettingSections
|
||||
---@param thing MinerStruct|LuaEntityPrototype
|
||||
---@return false
|
||||
function mpp_util.check_entity_hidden(player_data, category, thing)
|
||||
return (not player_data.entity_filtering_mode and player_data.filtered_entities[category..":"..thing.name])
|
||||
end
|
||||
|
||||
---@param player_data PlayerData
|
||||
function mpp_util.update_undo_button(player_data)
|
||||
|
||||
local enabled = false
|
||||
local undo_button = player_data.gui.undo_button
|
||||
local last_state = player_data.last_state
|
||||
|
||||
if last_state then
|
||||
local duration = mpp_util.get_display_duration(last_state.player.index)
|
||||
enabled = enabled or (last_state and last_state._collected_ghosts and #last_state._collected_ghosts > 0 and game.tick < player_data.tick_expires)
|
||||
end
|
||||
|
||||
undo_button.enabled = enabled
|
||||
undo_button.sprite = enabled and "mpp_undo_enabled" or "mpp_undo_disabled"
|
||||
undo_button.tooltip = mpp_util.wrap_tooltip(enabled and {"controls.undo"} or {"", {"controls.undo"}," (", {"gui.not-available"}, ")"})
|
||||
end
|
||||
|
||||
return mpp_util
|
||||
236
mining-patch-planner/mpp/pole_grid_mt.lua
Normal file
236
mining-patch-planner/mpp/pole_grid_mt.lua
Normal file
@@ -0,0 +1,236 @@
|
||||
local table_insert = table.insert
|
||||
local min, max = math.min, math.max
|
||||
|
||||
-- broke: implement power pole connection calculation yourself
|
||||
-- woke: place ghosts to make factorio calculate the connections
|
||||
|
||||
---@class PowerPoleGrid
|
||||
---@field [number] table<number, GridPole>
|
||||
local pole_grid_mt = {}
|
||||
pole_grid_mt.__index = pole_grid_mt
|
||||
|
||||
---@class GridPole
|
||||
---@field ix number Position in the pole grid
|
||||
---@field iy number Position in the pole grid
|
||||
---@field grid_x number Position in the full grid
|
||||
---@field grid_y number Position in the full grid
|
||||
---@field built boolean? Does the pole need to be built
|
||||
---@field entity LuaEntity? Pole ghost LuaEntity
|
||||
---@field has_consumers boolean Does pole cover any powered items
|
||||
---@field backtracked boolean
|
||||
---@field connections table<GridPole, true>
|
||||
---@field set_id number?
|
||||
---@field no_light boolean?
|
||||
|
||||
function pole_grid_mt.new()
|
||||
local new = {
|
||||
_max_x = 1,
|
||||
_max_y = 1,
|
||||
}
|
||||
return setmetatable(new, pole_grid_mt)
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param p GridPole
|
||||
function pole_grid_mt:set_pole(x, y, p)
|
||||
if p.connections == nil then p.connections = {} end
|
||||
if not self[y] then self[y] = {} end
|
||||
self._max_x = max(self._max_x, x)
|
||||
self._max_y = max(self._max_y, y)
|
||||
self[y][x] = p
|
||||
end
|
||||
|
||||
---@param p GridPole
|
||||
function pole_grid_mt:add_pole(p)
|
||||
self:set_pole(p.ix, p.iy, p)
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@return GridPole | nil
|
||||
function pole_grid_mt:get_pole(x, y)
|
||||
if self[y] then return self[y][x] end
|
||||
end
|
||||
|
||||
---@param p1 GridPole
|
||||
---@param p2 GridPole
|
||||
---@param struct PoleStruct
|
||||
---@return boolean
|
||||
function pole_grid_mt:pole_reaches(p1, p2, struct)
|
||||
local x, y = p1.grid_x - p2.grid_x, p1.grid_y - p2.grid_y
|
||||
return (x * x + y * y) ^ 0.5 <= (struct.wire)
|
||||
end
|
||||
|
||||
---@param P PoleStruct
|
||||
---@return table<number, table<GridPole, true>>
|
||||
function pole_grid_mt:find_connectivity(P)
|
||||
-- this went off the rails
|
||||
|
||||
local all_poles = {}
|
||||
---@type table<GridPole, true>
|
||||
local not_visited = {}
|
||||
|
||||
-- Make connections
|
||||
for y1 = 1, #self do
|
||||
local row = self[y1]
|
||||
for x1 = 1, #row do
|
||||
local pole = row[x1]
|
||||
if pole == nil then goto continue end
|
||||
|
||||
table.insert(all_poles, pole)
|
||||
|
||||
local right = self:get_pole(x1+1, y1)
|
||||
local bottom = self:get_pole(x1, y1+1)
|
||||
|
||||
if right and self:pole_reaches(pole, right, P) then
|
||||
pole.connections[right], right.connections[pole] = true, true
|
||||
end
|
||||
|
||||
if bottom and self:pole_reaches(pole, bottom, P) then
|
||||
pole.connections[bottom], bottom.connections[pole] = true, true
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
-- Process network connection sets
|
||||
local unconnected = {}
|
||||
---@type number, table<GridPole, true>
|
||||
local set_id, current_set = 1, {}
|
||||
---@type PoleConnectivity
|
||||
local sets = {[0]=unconnected, [1]=current_set}
|
||||
for _, v in pairs(all_poles) do not_visited[v] = true end
|
||||
|
||||
---@param pole GridPole
|
||||
---@return number?
|
||||
local function is_continuation(pole)
|
||||
for other, _ in pairs(pole.connections) do
|
||||
if other.set_id then
|
||||
return other.set_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param start_pole GridPole
|
||||
local function iterate_connections(start_pole)
|
||||
|
||||
local continuation = is_continuation(start_pole)
|
||||
if not continuation and table_size(current_set) > 0 then
|
||||
if sets[set_id] == nil then
|
||||
sets[set_id] = current_set
|
||||
end
|
||||
current_set, set_id = {}, set_id + 1
|
||||
end
|
||||
|
||||
---@param pole GridPole
|
||||
---@param depth_remaining number
|
||||
local function recurse_pole(pole, depth_remaining)
|
||||
not_visited[start_pole] = nil
|
||||
|
||||
if not pole.has_consumers then
|
||||
unconnected[pole] = true
|
||||
return
|
||||
end
|
||||
|
||||
if pole.set_id and pole.set_id < set_id then
|
||||
--Encountered a different set, merge to it
|
||||
local target_set_id = pole.set_id
|
||||
local target_set = sets[target_set_id]
|
||||
for current_pole, v in pairs(current_set) do
|
||||
current_set[current_pole], target_set[current_pole] = nil, true
|
||||
current_pole.set_id = target_set_id
|
||||
end
|
||||
set_id = pole.set_id
|
||||
current_set = target_set
|
||||
else
|
||||
pole.set_id = set_id
|
||||
end
|
||||
current_set[pole] = true
|
||||
|
||||
for other, _ in pairs(pole.connections) do
|
||||
local other_not_visited = not_visited[other]
|
||||
|
||||
if other_not_visited and depth_remaining > 0 then
|
||||
recurse_pole(other, depth_remaining-1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
recurse_pole(start_pole, 5)
|
||||
end
|
||||
|
||||
local remaining = next(not_visited)
|
||||
while remaining do
|
||||
iterate_connections(remaining)
|
||||
remaining = next(not_visited)
|
||||
end
|
||||
sets[set_id] = current_set
|
||||
|
||||
return sets
|
||||
end
|
||||
|
||||
---List of sets
|
||||
---set at index 0 are unconnected power poles
|
||||
---@alias PoleConnectivity table<number, table<GridPole, true>>
|
||||
|
||||
---@param connectivity PoleConnectivity
|
||||
---@return GridPole[]
|
||||
function pole_grid_mt:ensure_connectivity(connectivity)
|
||||
---@type GridPole[]
|
||||
local connected = {}
|
||||
local unconnecteds_set, main_set = connectivity[0], connectivity[1]
|
||||
|
||||
-- connect trivial power poles
|
||||
for connector_pole in pairs(unconnecteds_set) do
|
||||
--Set id is key and value is merge marker
|
||||
local different_sets = {} --[[@as table<number, boolean>]]
|
||||
for connection in pairs(connector_pole.connections) do
|
||||
if connection.set_id ~= nil then
|
||||
different_sets[connection.set_id] = true
|
||||
end
|
||||
end
|
||||
if table_size(different_sets) < 2 then goto skip_pole end
|
||||
|
||||
local target_set_id = different_sets[1] and 1 or next(different_sets)
|
||||
local target_set = connectivity[target_set_id]
|
||||
|
||||
for source_set_id in pairs(different_sets) do
|
||||
if source_set_id == target_set_id then goto skip_merge_set end
|
||||
local source_set = connectivity[source_set_id]
|
||||
|
||||
if different_sets[source_set_id] == true then
|
||||
for source_pole in pairs(source_set) do
|
||||
target_set[source_pole], source_set[source_pole] = true, nil
|
||||
source_pole.set_id = target_set_id
|
||||
different_sets[source_set_id] = false
|
||||
end
|
||||
end
|
||||
::skip_merge_set::
|
||||
end
|
||||
|
||||
connector_pole.built = true
|
||||
connector_pole.set_id = target_set_id
|
||||
target_set[connector_pole] = true
|
||||
unconnecteds_set[connector_pole] = nil
|
||||
|
||||
::skip_pole::
|
||||
end
|
||||
|
||||
---- append the main set to output list
|
||||
--for pole in pairs(main_set) do table_insert(connected, pole) end
|
||||
|
||||
for set_id, pole_set in pairs(connectivity) do
|
||||
if set_id == 0 then goto continue_set end
|
||||
--if set_id == 1 then goto continue_set end
|
||||
|
||||
for pole in pairs(pole_set) do table_insert(connected, pole) end
|
||||
|
||||
::continue_set::
|
||||
end
|
||||
|
||||
return connected
|
||||
end
|
||||
|
||||
return pole_grid_mt
|
||||
788
mining-patch-planner/mpp/render_util.lua
Normal file
788
mining-patch-planner/mpp/render_util.lua
Normal file
@@ -0,0 +1,788 @@
|
||||
local mpp_util = require("mpp.mpp_util")
|
||||
local color = require("mpp.color")
|
||||
|
||||
local floor, ceil = math.floor, math.ceil
|
||||
local min, max = math.min, math.max
|
||||
local EAST, NORTH, SOUTH, WEST = mpp_util.directions()
|
||||
|
||||
local render_util = {}
|
||||
|
||||
---@class RendererParams
|
||||
---@field origin MapPosition?
|
||||
---@field target MapPosition?
|
||||
---@field x number?
|
||||
---@field y number?
|
||||
---@field w number?
|
||||
---@field h number?
|
||||
---@field r number?
|
||||
---@field color Color?
|
||||
---@field width number?
|
||||
---@field c Color?
|
||||
---@field left_top MapPosition?
|
||||
---@field right_bottom MapPosition?
|
||||
|
||||
---this went off the rails
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
---@return MppRendering
|
||||
function render_util.renderer(event)
|
||||
|
||||
---@param t RendererParams
|
||||
local function parametrizer(t, overlay)
|
||||
|
||||
for k, v in pairs(overlay or {}) do t[k] = v end
|
||||
if t.x and t.y then t.origin = {t.x, t.y} end
|
||||
local target = t.origin or t.left_top --[[@as MapPosition]]
|
||||
local left_top, right_bottom = t.left_top or t.origin or target, t.right_bottom or t.origin
|
||||
|
||||
if t.origin and t.w or t.h then
|
||||
t.w, t.h = t.w or t.h, t.h or t.w
|
||||
right_bottom = {(target[1] or target.x) + t.w, (target[2] or target.y) + t.h}
|
||||
elseif t.r then
|
||||
local r = t.r
|
||||
local ox, oy = target[1] or target.x, target[2] or target.y
|
||||
left_top = {ox-r, oy-r}
|
||||
right_bottom = {ox+r, oy+r}
|
||||
end
|
||||
|
||||
local new = {
|
||||
surface = event.surface,
|
||||
players = {event.player_index},
|
||||
filled = false,
|
||||
radius = t.r or 1,
|
||||
color = t.c or t.color or {1, 1, 1},
|
||||
left_top = left_top,
|
||||
right_bottom = right_bottom,
|
||||
target = target, -- circles
|
||||
from = left_top,
|
||||
to = right_bottom, -- lines
|
||||
width = 1,
|
||||
}
|
||||
for k, v in pairs(t) do new[k]=v end
|
||||
for _, v in ipairs{"x", "y", "h", "w", "r", "origin"} do new[v]=nil end
|
||||
return new
|
||||
end
|
||||
|
||||
local meta_renderer_meta = {}
|
||||
meta_renderer_meta.__index = function(self, k)
|
||||
return function(t, t2)
|
||||
return {
|
||||
rendering[k](
|
||||
parametrizer(t, t2)
|
||||
)
|
||||
}
|
||||
end end
|
||||
local rendering = setmetatable({}, meta_renderer_meta)
|
||||
|
||||
---@class MppRendering
|
||||
local rendering_extension = {}
|
||||
|
||||
---Draws an x between left_top and right_bottom
|
||||
---@param params RendererParams
|
||||
function rendering_extension.draw_cross(params)
|
||||
rendering.draw_line(params)
|
||||
rendering.draw_line({
|
||||
width = params.width,
|
||||
color = params.color,
|
||||
left_top={
|
||||
params.right_bottom[1],
|
||||
params.left_top[2]
|
||||
},
|
||||
right_bottom={
|
||||
params.left_top[1],
|
||||
params.right_bottom[2],
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
function rendering_extension.draw_rectangle_dashed(params)
|
||||
rendering.draw_line(params, {
|
||||
from={params.left_top[1], params.left_top[2]},
|
||||
to={params.right_bottom[1], params.left_top[2]},
|
||||
dash_offset = 0.0,
|
||||
})
|
||||
rendering.draw_line(params, {
|
||||
from={params.left_top[1], params.right_bottom[2]},
|
||||
to={params.right_bottom[1], params.right_bottom[2]},
|
||||
dash_offset = 0.5,
|
||||
})
|
||||
rendering.draw_line(params, {
|
||||
from={params.right_bottom[1], params.left_top[2]},
|
||||
to={params.right_bottom[1], params.right_bottom[2]},
|
||||
dash_offset = 0.0,
|
||||
})
|
||||
rendering.draw_line(params, {
|
||||
from={params.left_top[1], params.left_top[2]},
|
||||
to={params.left_top[1], params.right_bottom[2]},
|
||||
dash_offset = 0.5,
|
||||
})
|
||||
end
|
||||
|
||||
local meta = {}
|
||||
function meta:__index(k)
|
||||
return function(t, t2)
|
||||
if rendering_extension[k] then
|
||||
rendering_extension[k](parametrizer(t, t2))
|
||||
else
|
||||
rendering[k](parametrizer(t, t2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable({}, meta)
|
||||
end
|
||||
|
||||
function render_util.draw_clear_rendering(player_data, event)
|
||||
rendering.clear("mining-patch-planner")
|
||||
end
|
||||
|
||||
---Draws the properties of a mining drill
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_drill_struct(player_data, event)
|
||||
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
local x, y = fx1 + 0.5, fy1 + 0.5
|
||||
local fx2, fy2 = event.area.right_bottom.x, event.area.right_bottom.y
|
||||
fx2, fy2 = ceil(fx2), ceil(fy2)
|
||||
|
||||
--renderer.draw_cross{x=fx1, y=fy1, w=fx2-fx1, h=fy2-fy1}
|
||||
--renderer.draw_cross{x=fx1, y=fy1, w=2}
|
||||
|
||||
local drill = mpp_util.miner_struct(player_data.choices.miner_choice)
|
||||
|
||||
renderer.draw_circle{
|
||||
x = fx1 + drill.drop_pos.x,
|
||||
y = fy1 + drill.drop_pos.y,
|
||||
c = {0, 1, 0},
|
||||
r = 0.2,
|
||||
}
|
||||
|
||||
-- drop pos
|
||||
renderer.draw_cross{
|
||||
x = fx1 + 0.5 + drill.out_x,
|
||||
y = fy1 + 0.5 + drill.out_y,
|
||||
r = 0.3,
|
||||
}
|
||||
|
||||
for _, pos in pairs(drill.output_rotated) do
|
||||
renderer.draw_cross{
|
||||
x = fx1 + 0.5 + pos[1],
|
||||
y = fy1 + 0.5 + pos[2],
|
||||
r = 0.15,
|
||||
width = 3,
|
||||
c={0, 0, 0, .5},
|
||||
}
|
||||
end
|
||||
|
||||
renderer.draw_line{
|
||||
from={x + drill.x, y},
|
||||
to={x + drill.x, y + 2},
|
||||
width = 2, color={0.5, 0.5, 0.5}
|
||||
}
|
||||
renderer.draw_line{
|
||||
from={x + drill.x, y},
|
||||
to={x + drill.x-.5, y + .65},
|
||||
width = 2, color={0.5, 0.5, 0.5}
|
||||
}
|
||||
renderer.draw_line{
|
||||
from={x + drill.x, y},
|
||||
to={x + drill.x+.5, y + .65},
|
||||
width = 2, color={0.5, 0.5, 0.5}
|
||||
}
|
||||
|
||||
|
||||
-- drill origin
|
||||
renderer.draw_circle{
|
||||
x = fx1 + 0.5,
|
||||
y = fy1 + 0.5,
|
||||
width = 2,
|
||||
r = 0.4,
|
||||
}
|
||||
|
||||
renderer.draw_text{
|
||||
target={fx1 + .5, fy1 + .5},
|
||||
text = "(0, 0)",
|
||||
alignment = "center",
|
||||
vertical_alignment="middle",
|
||||
scale = 0.6,
|
||||
}
|
||||
|
||||
-- negative extent - cyan
|
||||
renderer.draw_cross{
|
||||
x = fx1 +.5 + drill.extent_negative,
|
||||
y = fy1 +.5 + drill.extent_negative,
|
||||
r = 0.25,
|
||||
c = {0, 0.8, 0.8},
|
||||
}
|
||||
|
||||
-- positive extent - purple
|
||||
renderer.draw_cross{
|
||||
x = fx1 +.5 + drill.extent_positive,
|
||||
y = fy1 +.5 + drill.extent_positive,
|
||||
r = 0.25,
|
||||
c = {1, 0, 1},
|
||||
}
|
||||
|
||||
renderer.draw_rectangle{
|
||||
x=fx1,
|
||||
y=fy1,
|
||||
w=drill.size,
|
||||
h=drill.size,
|
||||
width=3,
|
||||
gap_length=0.5,
|
||||
dash_length=0.5,
|
||||
}
|
||||
|
||||
renderer.draw_rectangle_dashed{
|
||||
x=fx1 + drill.extent_negative,
|
||||
y=fy1 + drill.extent_negative,
|
||||
w=drill.area,
|
||||
h=drill.area,
|
||||
c={0.5, 0.5, 0.5},
|
||||
width=5,
|
||||
gap_length=0.5,
|
||||
dash_length=0.5,
|
||||
}
|
||||
|
||||
if drill.supports_fluids then
|
||||
-- pipe connections
|
||||
renderer.draw_line{
|
||||
width=4, color = {0, .7, 1},
|
||||
from={fx1-.3, y+drill.pipe_left-.5},
|
||||
to={fx1-.3, y+drill.pipe_left+.5},
|
||||
}
|
||||
renderer.draw_line{
|
||||
width=4, color = {.7, .7, 0},
|
||||
from={fx1+drill.size+.3, y+drill.pipe_left-.5},
|
||||
to={fx1+drill.size+.3, y+drill.pipe_left+.5},
|
||||
}
|
||||
end
|
||||
|
||||
renderer.draw_text{
|
||||
target={fx1 + drill.extent_negative, fy1 + drill.extent_negative-1.5},
|
||||
text = string.format("skip_outer: %s", drill.skip_outer),
|
||||
alignment = "left",
|
||||
vertical_alignment="middle",
|
||||
}
|
||||
|
||||
renderer.draw_circle{x = fx1, y = fy1, r = 0.1}
|
||||
--renderer.draw_circle{ x = fx2, y = fy2, r = 0.15, color={1, 0, 0} }
|
||||
end
|
||||
|
||||
---Preview the pole coverage
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_pole_layout(player_data, event)
|
||||
rendering.clear("mining-patch-planner")
|
||||
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
|
||||
--renderer.draw_cross{x=fx1, y=fy1, w=fx2-fx1, h=fy2-fy1}
|
||||
--renderer.draw_cross{x=fx1, y=fy1, w=2}
|
||||
|
||||
local drill = mpp_util.miner_struct(player_data.choices.miner_choice)
|
||||
local pole = mpp_util.pole_struct(player_data.choices.pole_choice)
|
||||
|
||||
local function draw_lane(x, y, count)
|
||||
for i = 0, count-1 do
|
||||
renderer.draw_rectangle{
|
||||
x = x + drill.size * i + 0.15 , y = y+.15,
|
||||
w = drill.size-.3, h=1-.3,
|
||||
color = i % 2 == 0 and {143/255, 86/255, 59/255} or {223/255, 113/255, 38/255},
|
||||
width=2,
|
||||
}
|
||||
end
|
||||
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local coverage = mpp_util.calculate_pole_coverage(player_data.choices, count, 1)
|
||||
|
||||
renderer.draw_circle{
|
||||
x=x+.5, y=y-0.5, radius = .25, color={0.7, 0.7, 0.7},
|
||||
}
|
||||
for i = coverage.pole_start, coverage.full_miner_width, coverage.pole_step do
|
||||
renderer.draw_circle{
|
||||
x = x + i + .5,
|
||||
y = y - .5,
|
||||
radius = 0.3, width=2,
|
||||
color = {0, 1, 1},
|
||||
}
|
||||
renderer.draw_line{
|
||||
x = x + i +.5 - pole.supply_width / 2+.2,
|
||||
y = y - .2,
|
||||
h = 0,
|
||||
w = pole.supply_width-.4,
|
||||
color = {0, 1, 1},
|
||||
width = 2,
|
||||
}
|
||||
renderer.draw_line{
|
||||
x = x + i +.5 - pole.supply_width / 2 + .2,
|
||||
y = y - .7,
|
||||
h = .5,
|
||||
w = 0,
|
||||
color = {0, 1, 1},
|
||||
width = 2,
|
||||
}
|
||||
renderer.draw_line{
|
||||
x = x + i +.5 + pole.supply_width / 2 - .2,
|
||||
y = y - .7,
|
||||
h = .5,
|
||||
w = 0,
|
||||
color = {0, 1, 1},
|
||||
width = 2,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, 10 do
|
||||
draw_lane(fx1, fy1+(i-1)*3, i)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---Preview the pole coverage
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_pole_layout_compact(player_data, event)
|
||||
rendering.clear("mining-patch-planner")
|
||||
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
|
||||
--renderer.draw_cross{x=fx1, y=fy1, w=fx2-fx1, h=fy2-fy1}
|
||||
--renderer.draw_cross{x=fx1, y=fy1, w=2}
|
||||
|
||||
local drill = mpp_util.miner_struct(player_data.choices.miner_choice)
|
||||
local pole = mpp_util.pole_struct(player_data.choices.pole_choice)
|
||||
|
||||
local function draw_lane(x, y, count)
|
||||
for i = 0, count-1 do
|
||||
renderer.draw_rectangle{
|
||||
x = x + drill.size * i + 0.15 , y = y+.15,
|
||||
w = drill.size-.3, h=1-.3,
|
||||
color = i % 2 == 0 and {143/255, 86/255, 59/255} or {223/255, 113/255, 38/255},
|
||||
width=2,
|
||||
}
|
||||
end
|
||||
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local coverage = mpp_util.calculate_pole_spacing(player_data.choices, count, 1)
|
||||
|
||||
renderer.draw_circle{
|
||||
x=x+.5, y=y-0.5, radius = .25, color={0.7, 0.7, 0.7},
|
||||
}
|
||||
for i = coverage.pole_start, coverage.full_miner_width, coverage.pole_step do
|
||||
renderer.draw_circle{
|
||||
x = x + i + .5,
|
||||
y = y - .5,
|
||||
radius = 0.3, width=2,
|
||||
color = {0, 1, 1},
|
||||
}
|
||||
renderer.draw_line{
|
||||
x = x + i +.5 - pole.supply_width / 2+.2,
|
||||
y = y - .2,
|
||||
h = 0,
|
||||
w = pole.supply_width-.4,
|
||||
color = {0, 1, 1},
|
||||
width = 2,
|
||||
}
|
||||
renderer.draw_line{
|
||||
x = x + i +.5 - pole.supply_width / 2 + .2,
|
||||
y = y - .7,
|
||||
h = .5,
|
||||
w = 0,
|
||||
color = {0, 1, 1},
|
||||
width = 2,
|
||||
}
|
||||
renderer.draw_line{
|
||||
x = x + i +.5 + pole.supply_width / 2 - .2,
|
||||
y = y - .7,
|
||||
h = .5,
|
||||
w = 0,
|
||||
color = {0, 1, 1},
|
||||
width = 2,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, 10 do
|
||||
draw_lane(fx1, fy1+(i-1)*3, i)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---Displays the labels of built things on the grid
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_built_things(player_data, event)
|
||||
rendering.clear("mining-patch-planner")
|
||||
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local state = player_data.last_state
|
||||
|
||||
if not state then return end
|
||||
|
||||
local C = state.coords
|
||||
local G = state.grid
|
||||
|
||||
for _, row in pairs(G) do
|
||||
for _, tile in pairs(row) do
|
||||
---@cast tile GridTile
|
||||
local thing = tile.built_on
|
||||
if thing then
|
||||
-- renderer.draw_circle{
|
||||
-- x = C.gx + tile.x, y = C.gy + tile.y,
|
||||
-- w = 1,
|
||||
-- color = {0, 0.5, 0, 0.1},
|
||||
-- r = 0.5,
|
||||
-- }
|
||||
renderer.draw_rectangle{
|
||||
x = C.ix1 + tile.x -.9, y = C.iy1 + tile.y -.9,
|
||||
w = .8,
|
||||
color = {0, 0.2, 0, 0.1},
|
||||
}
|
||||
renderer.draw_text{
|
||||
x = C.gx + tile.x, y = C.gy + tile.y - .3,
|
||||
alignment = "center",
|
||||
vertical_alignment = "top",
|
||||
--vertical_alignment = tile.x % 2 == 1 and "top" or "bottom",
|
||||
text = thing,
|
||||
scale = 0.6,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_drill_convolution(player_data, event)
|
||||
rendering.clear("mining-patch-planner")
|
||||
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
|
||||
local state = player_data.last_state
|
||||
if not state then return end
|
||||
|
||||
local C = state.coords
|
||||
local grid = state.grid
|
||||
|
||||
for _, row in pairs(grid) do
|
||||
for _, tile in pairs(row) do
|
||||
---@cast tile GridTile
|
||||
--local c1, c2 = tile.neighbor_counts[m_size], tile.neighbor_counts[m_area]
|
||||
local c1, c2 = tile.neighbors_inner, tile.neighbors_outer
|
||||
if c1 == 0 and c2 == 0 then goto continue end
|
||||
|
||||
rendering.draw_circle{
|
||||
surface = state.surface, filled=false, color = {0.3, 0.3, 1},
|
||||
width=1, radius = 0.5,
|
||||
target={C.gx + tile.x, C.gy + tile.y},
|
||||
}
|
||||
local stagger = (.5 - (tile.x % 2)) * .25
|
||||
local col = c1 == 0 and {0.3, 0.3, 0.3} or {0.6, 0.6, 0.6}
|
||||
rendering.draw_text{
|
||||
surface = state.surface, filled = false, color = col,
|
||||
target={C.gx + tile.x, C.gy + tile.y + stagger},
|
||||
text = string.format("%i,%i", c1, c2),
|
||||
alignment = "center",
|
||||
vertical_alignment="middle",
|
||||
}
|
||||
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_power_grid(player_data, event)
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
|
||||
local state = player_data.last_state
|
||||
if not state then return end
|
||||
---@cast state SimpleState
|
||||
|
||||
local C = state.coords
|
||||
local grid = state.grid
|
||||
|
||||
local connectivity = state.power_connectivity
|
||||
if not connectivity then
|
||||
game.print("No connectivity exists")
|
||||
return
|
||||
end
|
||||
local rendered = {}
|
||||
|
||||
for set_id, set in pairs(connectivity) do
|
||||
local set_color = color.hue_sequence(set_id)
|
||||
if set_id == 0 then set_color = {1, 1, 1} end
|
||||
for pole, _ in pairs(set) do
|
||||
---@cast pole GridPole
|
||||
-- if rendered[pole] then goto continue end
|
||||
-- rendered[pole] = true
|
||||
local pole_color = set_color
|
||||
-- if not pole.backtracked and not pole.has_consumers then
|
||||
-- pole_color = {0, 0, 0}
|
||||
-- end
|
||||
|
||||
renderer.draw_circle{
|
||||
surface = state.surface,
|
||||
filled = not pole.backtracked,
|
||||
color = pole_color,
|
||||
width = 5,
|
||||
target = {C.gx + pole.grid_x, C.gy + pole.grid_y},
|
||||
radius = 0.65,
|
||||
}
|
||||
renderer.draw_text{
|
||||
surface = state.surface,
|
||||
target={C.gx + pole.grid_x, C.gy + pole.grid_y},
|
||||
text = set_id,
|
||||
alignment = "center",
|
||||
vertical_alignment="middle",
|
||||
scale = 2,
|
||||
}
|
||||
renderer.draw_text{
|
||||
surface = state.surface,
|
||||
target={C.gx + pole.grid_x, C.gy + pole.grid_y-1.25},
|
||||
text = ("%i,%i"):format(pole.ix, pole.iy),
|
||||
alignment = "center",
|
||||
vertical_alignment="middle",
|
||||
scale = 2,
|
||||
}
|
||||
::continue::
|
||||
end
|
||||
end --]]
|
||||
|
||||
end
|
||||
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_centricity(player_data, event)
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
|
||||
local state = player_data.last_state
|
||||
if not state then return end
|
||||
|
||||
local C = state.coords
|
||||
local grid = state.grid
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_blueprint_data(player_data, event)
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
local x, y = fx1 + 0.5, fy1 + 0.5
|
||||
|
||||
local id = player_data.choices.blueprint_choice and player_data.choices.blueprint_choice.item_number
|
||||
if not id then return end
|
||||
local bp = player_data.blueprints.cache[id]
|
||||
if not bp then return end
|
||||
|
||||
renderer.draw_line{x = fx1, y = fy1-1, w = 0, h = 2, width = 7, color={0, 0, 0}}
|
||||
renderer.draw_line{x = fx1-1, y = fy1, w = 2, h = 0, width = 7, color={0, 0, 0}}
|
||||
|
||||
renderer.draw_rectangle{
|
||||
x = fx1,
|
||||
y = fy1,
|
||||
w = bp.w,
|
||||
h = bp.h,
|
||||
}
|
||||
|
||||
for _, ent in pairs(bp.entities) do
|
||||
local struct = mpp_util.entity_struct(ent.name)
|
||||
local clr = {1, 1, 1}
|
||||
if ent.capstone_x and ent.capstone_y then
|
||||
clr = {0, 1, 1}
|
||||
elseif ent.capstone_x then
|
||||
clr = {0, 1, 0}
|
||||
elseif ent.capstone_y then
|
||||
clr = {0, 0, 1}
|
||||
end
|
||||
renderer.draw_circle{
|
||||
x = fx1 + ent.position.x,
|
||||
y = fy1 + ent.position.y,
|
||||
r = struct.size / 2,
|
||||
color = clr,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_deconstruct_preview(player_data, event)
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
|
||||
local state = player_data.last_state
|
||||
if not state then return end
|
||||
|
||||
local c = state.coords
|
||||
local grid = state.grid
|
||||
|
||||
local layout = algorithm.layouts[state.layout_choice]
|
||||
if not layout._get_deconstruction_objects then return end
|
||||
local objects = layout:_get_deconstruction_objects(state)
|
||||
|
||||
local DIR = state.direction_choice
|
||||
|
||||
for _, t in pairs(objects) do
|
||||
for _, object in ipairs(t) do
|
||||
---@cast object GhostSpecification
|
||||
local extent_w = object.extent_w or object.radius or 0.5
|
||||
local extent_h = object.extent_h or extent_w
|
||||
|
||||
local x1, y1 = object.grid_x-extent_w, object.grid_y-extent_h
|
||||
local x2, y2 = object.grid_x+extent_w, object.grid_y+extent_h
|
||||
|
||||
x1, y1 = mpp_util.revert_ex(c.gx, c.gy, DIR, x1, y1, c.tw, c.th)
|
||||
x2, y2 = mpp_util.revert_ex(c.gx, c.gy, DIR, x2, y2, c.tw, c.th)
|
||||
|
||||
rendering.draw_rectangle{
|
||||
surface=state.surface,
|
||||
players={state.player},
|
||||
filled=false,
|
||||
width=3,
|
||||
color={1, 0, 0},
|
||||
left_top={x1+.1,y1+.1},
|
||||
right_bottom={x2-.1,y2-.1},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---@param player_data PlayerData
|
||||
---@param event EventData.on_player_reverse_selected_area
|
||||
function render_util.draw_can_place_entity(player_data, event)
|
||||
local renderer = render_util.renderer(event)
|
||||
|
||||
local fx1, fy1 = event.area.left_top.x, event.area.left_top.y
|
||||
fx1, fy1 = floor(fx1), floor(fy1)
|
||||
local x, y = fx1 + 0.5, fy1 + 0.5
|
||||
|
||||
local id = player_data.choices.blueprint_choice and player_data.choices.blueprint_choice.item_number
|
||||
if not id then return end
|
||||
local bp = player_data.blueprints.cache[id]
|
||||
if not bp then return end
|
||||
|
||||
renderer.draw_line{x = fx1, y = fy1-1, w = 0, h = 1, width = 7, color={0.3, 0.3, 0.3}}
|
||||
renderer.draw_line{x = fx1-1, y = fy1, w = 1, h = 0, width = 7, color={0.3, 0.3, 0.3}}
|
||||
|
||||
local build_check_type = defines.build_check_type
|
||||
local can_forced = {
|
||||
[{false, false}] = 0,
|
||||
[{true, false}] = 1,
|
||||
[{false, true}] = 2,
|
||||
[{true, true}] = 3,
|
||||
}
|
||||
|
||||
for check_type, i1 in pairs(build_check_type) do
|
||||
|
||||
for forced_ghost, i2 in pairs(can_forced) do
|
||||
|
||||
local forced, ghost = forced_ghost[1], forced_ghost[2]
|
||||
|
||||
local nx = x + (bp.w + 3) * i1
|
||||
local ny = y + (bp.h + 3) * i2
|
||||
|
||||
renderer.draw_rectangle{
|
||||
x = nx-.5,
|
||||
y = ny-.5,
|
||||
w = bp.w,
|
||||
h = bp.h,
|
||||
color = {0.2, 0.2, .7},
|
||||
}
|
||||
|
||||
-- renderer.draw_text{
|
||||
-- x = nx+(bp.w-1)/2, y = ny-.5,
|
||||
-- text = ("%i,%i"):format(i1, i2),
|
||||
-- alignment = "center",
|
||||
-- vertical_alignment="bottom",
|
||||
-- }
|
||||
|
||||
renderer.draw_text{
|
||||
x = nx+(bp.w-1)/2, y = ny-1.5,
|
||||
text = ("%s"):format(check_type),
|
||||
alignment = "center",
|
||||
vertical_alignment="bottom",
|
||||
}
|
||||
renderer.draw_text{
|
||||
x = nx+(bp.w-1)/2, y = ny-1,
|
||||
text = ("forced: %s"):format(forced),
|
||||
alignment = "center",
|
||||
vertical_alignment="bottom",
|
||||
}
|
||||
renderer.draw_text{
|
||||
x = nx+(bp.w-1)/2, y = ny-.5,
|
||||
text = ("ghost: %s"):format(ghost),
|
||||
alignment = "center",
|
||||
vertical_alignment="bottom",
|
||||
}
|
||||
|
||||
for _, ent in pairs(bp.entities) do
|
||||
local ex = nx + ent.position.x - .5
|
||||
local ey = ny + ent.position.y - .5
|
||||
local t = {
|
||||
name = ent.name,
|
||||
position = {ex, ey},
|
||||
direction = ent.direction,
|
||||
build_check_type = defines.build_check_type[check_type],
|
||||
force = game.get_player(event.player_index).force,
|
||||
forced = forced,
|
||||
}
|
||||
if ghost then
|
||||
t.name, t.inner_name = "entity-ghost", t.name
|
||||
end
|
||||
|
||||
local can_place = event.surface.can_place_entity(t)
|
||||
|
||||
renderer.draw_circle{x = ex, y = ey, r = 0.5, width = 3,
|
||||
color = can_place and {0.1, .9, .1} or {0.9, .1, .1},
|
||||
}
|
||||
|
||||
if can_place then
|
||||
if not ghost then
|
||||
t.name, t.inner_name = "entity-ghost", t.name
|
||||
end
|
||||
event.surface.create_entity(t)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
return render_util
|
||||
25
mining-patch-planner/mpp/structs.lua
Normal file
25
mining-patch-planner/mpp/structs.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
local set_mt = {}
|
||||
set_mt.__index = set_mt
|
||||
|
||||
function set_mt.new(t)
|
||||
local new = {}
|
||||
for k, v in pairs(t or {}) do
|
||||
new[v] = true
|
||||
end
|
||||
return setmetatable(new, set_mt)
|
||||
end
|
||||
|
||||
local list_mt = {}
|
||||
list_mt.__index = list_mt
|
||||
|
||||
function list_mt.new(t)
|
||||
local new = {}
|
||||
for k, v in pairs(t or {}) do
|
||||
|
||||
end
|
||||
return setmetatable(new, list_mt)
|
||||
end
|
||||
|
||||
|
||||
return set_mt, list_mt
|
||||
Reference in New Issue
Block a user