1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2026-04-26 21:02:59 +02:00
Files
Gerkiz 1e4bc9adfe Fix maps
Make maps compatible with 2.0
2026-02-26 18:56:59 +01:00

656 lines
21 KiB
Lua

--luacheck: ignore
--[[
It's a Minesweeper thingy - MewMew
Cell Values:
-- 1 to 8 = adjacent mines
-- 9 = empty cell with grid
-- 10 = mine
-- 11 = marked mine
]] --
if script.active_mods['space-age'] then
error('This map is incompatible with the Space Age mod. Disable Space Age to run this map.', 2)
end
require 'modules.satellite_score'
local Functions = require 'maps.minesweeper.functions'
local Map_score = require 'utils.gui.map_score'
local Map = require 'modules.map_info'
local Global = require 'utils.global'
local minesweeper = {}
Global.register(
minesweeper,
function (tbl)
minesweeper = tbl
end
)
local number_colors =
{
[1] = { 0, 0, 210 },
[2] = { 0, 100, 0 },
[3] = { 180, 0, 0 },
[4] = { 0, 0, 120 },
[5] = { 120, 0, 0 },
[6] = { 0, 110, 110 },
[7] = { 0, 0, 0 },
[8] = { 125, 125, 125 },
[11] = { 185, 0, 255 }
}
local rendering_tile_values =
{
['nuclear-ground'] = { offset = { 0.6, -0.2 }, zoom = 3, font = 'scenario-message-dialog' },
--['stone-path'] = {offset = {0.54, -0.27}, zoom = 3, font = 'default-large'},
['stone-path'] = { offset = { 0.52, -0.28 }, zoom = 3, font = 'default-large-bold' },
['concrete'] = { offset = { 0.52, -0.28 }, zoom = 3, font = 'default-large-bold' },
['hazard-concrete-left'] = { offset = { 0.52, -0.28 }, zoom = 3, font = 'default-large-bold' },
['hazard-concrete-right'] = { offset = { 0.52, -0.28 }, zoom = 3, font = 'default-large-bold' },
['refined-concrete'] = { offset = { 0.54, -0.26 }, zoom = 3, font = 'default-game' },
['refined-hazard-concrete-left'] = { offset = { 0.54, -0.26 }, zoom = 3, font = 'default-game' },
['refined-hazard-concrete-right'] = { offset = { 0.54, -0.26 }, zoom = 3, font = 'default-game' }
}
local chunk_divide_vectors = {}
for x = 0, 30, 2 do
for y = 0, 30, 2 do
table.insert(chunk_divide_vectors, { x, y })
end
end
local size_of_chunk_divide_vectors = #chunk_divide_vectors
local chunk_vectors = {}
for x = -32, 32, 32 do
for y = -32, 32, 32 do
table.insert(chunk_vectors, { x, y })
end
end
local cell_update_vectors = {}
for x = -2, 2, 2 do
for y = -2, 2, 2 do
table.insert(cell_update_vectors, { x, y })
end
end
local cell_adjacent_vectors = {}
for x = -2, 2, 2 do
for y = -2, 2, 2 do
if x == 0 and y == 0 then
else
table.insert(cell_adjacent_vectors, { x, y })
end
end
end
local solving_vector_tables = {}
local i = 1
for r = 3, 10, 1 do
solving_vector_tables[i] = {}
for x = r * -2, r * 2, 2 do
for y = r * -2, r * 2, 2 do
table.insert(solving_vector_tables[i], { x, y })
end
end
i = i + 1
end
local size_of_solving_vector_tables = #solving_vector_tables
local function update_rendering(cell, position)
local surface = game.surfaces[1]
local tile = surface.get_tile(position.x, position.y)
local tile_values = rendering_tile_values[tile.name]
if not tile_values then
tile_values = { offset = { 0.6, -0.2 }, zoom = 3, font = 'scenario-message-dialog' }
end
if cell[2] then
cell[2].destroy()
end
if cell[3] then
cell[3].destroy()
end
local cell_value = cell[1]
local color
if number_colors[cell_value] then
color = number_colors[cell_value]
else
color = { 125, 125, 125 }
end
local p = { position.x + tile_values.offset[1], position.y + tile_values.offset[2] }
local text = cell_value
if cell_value == 10 or cell_value == 9 then
text = ' '
end
if cell_value == 11 then
text = 'X'
end
cell[2] =
rendering.draw_text
{
text = text,
surface = surface,
target = p,
color = color,
scale = tile_values.zoom,
font = tile_values.font,
draw_on_ground = true,
scale_with_zoom = false,
only_in_alt_mode = false
}
if not tile.hidden_tile then
return
end
if tile.hidden_tile ~= 'nuclear-ground' then
return
end
cell[3] =
rendering.draw_rectangle
{
width = 2,
filled = false,
surface = surface,
left_top = position,
right_bottom = { position.x + 2, position.y + 2 },
color = { 0, 0, 0 },
draw_on_ground = true,
only_in_alt_mode = false
}
end
local function get_adjacent_mine_count(position)
local count = 0
for _, vector in pairs(cell_adjacent_vectors) do
local p = { x = position.x + vector[1], y = position.y + vector[2] }
local key = Functions.position_to_string(p)
local cell = minesweeper.cells[key]
if cell and cell[1] >= 10 then
count = count + 1
end
end
return count
end
local function kill_cell(position)
local key = Functions.position_to_string(position)
local cell = minesweeper.cells[key]
if not cell then
return
end
if cell[2] then
cell[2].destroy()
end
if cell[3] then
cell[3].destroy()
end
minesweeper.cells[key] = nil
end
local function visit_cell(position)
local score_change = 0
if not Functions.is_minefield_tile(position, true) then
return score_change
end
local key = Functions.position_to_string(position)
local cell = minesweeper.cells[key]
local cell_value_before_visit = false
if cell then
if cell[1] == 10 then
Functions.kaboom(position)
score_change = -8
cell[1] = -1
for _, vector in pairs(cell_update_vectors) do
local p = { x = position.x + vector[1], y = position.y + vector[2] }
key = Functions.position_to_string(p)
if minesweeper.cells[key] and minesweeper.cells[key][1] < 10 then
table.insert(minesweeper.visit_queue, { x = p.x, y = p.y })
end
end
return score_change
end
if cell[1] == 11 then
update_rendering(cell, position)
return score_change
end
cell_value_before_visit = cell[1]
end
if not cell then
minesweeper.cells[key] = {}
end
cell = minesweeper.cells[key]
cell[1] = get_adjacent_mine_count(position)
if cell[1] == 0 then
for _, vector in pairs(cell_adjacent_vectors) do
local adjacent_position = { x = position.x + vector[1], y = position.y + vector[2] }
if Functions.is_minefield_tile(adjacent_position, true) then
local adjacent_key = Functions.position_to_string(adjacent_position)
if not minesweeper.cells[adjacent_key] then
minesweeper.cells[adjacent_key] = {}
end
local adjacent_cell = minesweeper.cells[adjacent_key]
local mine_count = get_adjacent_mine_count(adjacent_position)
adjacent_cell[1] = mine_count
update_rendering(adjacent_cell, adjacent_position)
if mine_count == 0 then
table.insert(minesweeper.visit_queue, { x = adjacent_position.x, y = adjacent_position.y })
end
end
end
Functions.uncover_terrain(position)
kill_cell(position)
return score_change
end
if cell_value_before_visit and cell_value_before_visit ~= cell[1] then
for _, vector in pairs(cell_adjacent_vectors) do
local adjacent_position = { x = position.x + vector[1], y = position.y + vector[2] }
local adjacent_key = Functions.position_to_string(adjacent_position)
local adjacent_cell = minesweeper.cells[adjacent_key]
if adjacent_cell and adjacent_cell[1] < 9 then
table.insert(minesweeper.visit_queue, { x = adjacent_position.x, y = adjacent_position.y })
end
end
end
update_rendering(cell, position)
return score_change
end
local function get_solving_vectors(position)
local distance_to_center = math.sqrt(position.x ^ 2 + position.y ^ 2)
local key = math.floor(distance_to_center * 0.005) + 1
if key > size_of_solving_vector_tables then
key = size_of_solving_vector_tables
end
local solving_vectors = solving_vector_tables[key]
return solving_vectors
end
local function are_mines_marked_around_target(position)
local marked_positions = {}
for _, vector in pairs(get_solving_vectors(position)) do
local p = { x = position.x + vector[1], y = position.y + vector[2] }
local key = Functions.position_to_string(p)
local cell = minesweeper.cells[key]
if cell then
if cell[1] == 10 then
return
end
if cell[1] == 11 then
table.insert(marked_positions, p)
end
end
end
return marked_positions
end
local function solve_attempt(position)
local solved = false
for _, vector in pairs(get_solving_vectors(position)) do
local p = { x = position.x + vector[1], y = position.y + vector[2] }
local key = Functions.position_to_string(p)
local cell = minesweeper.cells[key]
if cell and cell[1] > 10 then
local marked_positions = are_mines_marked_around_target(p)
if marked_positions then
solved = true
for _, pp in pairs(marked_positions) do
minesweeper.cells[Functions.position_to_string(pp)][1] = -1
visit_cell(pp)
Functions.disarm_reward(pp)
end
end
end
end
return solved
end
local function mark_mine(entity, player)
local position = Functions.position_to_cell_position(entity.position)
local key = Functions.position_to_string(position)
local cell = minesweeper.cells[key]
local score_change = 0
--Success
if cell and cell[1] > 9 then
local surface = game.surfaces.nauvis
if cell[1] == 10 then
score_change = 1
end
surface.create_entity(
{
name = 'flying-text',
position = entity.position,
text = 'Mine marked.',
color = { r = 0.98, g = 0.66, b = 0.22 }
}
)
cell[1] = 11
update_rendering(cell, position)
entity.destroy()
local solved = solve_attempt(position)
if solved then
player.insert({ name = 'stone-furnace', count = 1 })
return score_change
end
local e = surface.create_entity({ name = 'item-on-ground', position = { position.x + 1, position.y + 1 }, stack = { name = 'stone-furnace', count = 1 } })
if e and e.valid then
e.to_be_looted = true
end
return score_change
end
--Trigger all adjacent mines when missplacing a disarming furnace.
for _, vector in pairs(cell_update_vectors) do
local p = { x = position.x + vector[1], y = position.y + vector[2] }
key = Functions.position_to_string(p)
if minesweeper.cells[key] and minesweeper.cells[key][1] == 10 then
Functions.kaboom(p)
score_change = score_change - 8
minesweeper.cells[key][1] = -1
solve_attempt(p)
table.insert(minesweeper.visit_queue, { x = p.x, y = p.y })
end
end
return score_change
end
local function add_mines_to_chunk(left_top, distance_to_center)
local base_mine_count = 40
local max_mine_count = 128
local mine_count = distance_to_center * 0.043 + base_mine_count
if mine_count > max_mine_count then
mine_count = max_mine_count
end
local shuffle_index = {}
for iv = 1, size_of_chunk_divide_vectors, 1 do
table.insert(shuffle_index, iv)
end
table.shuffle_table(shuffle_index)
-- place shuffled mines
if distance_to_center < 128 then
for iv = 1, mine_count, 1 do
local vector = chunk_divide_vectors[shuffle_index[iv]]
local position = { x = left_top.x + vector[1], y = left_top.y + vector[2] }
if not Functions.is_spawn(position) then
local key = Functions.position_to_string(position)
minesweeper.cells[key] = { 10 }
minesweeper.active_mines = minesweeper.active_mines + 1
end
end
else
for iv = 1, mine_count, 1 do
local vector = chunk_divide_vectors[shuffle_index[iv]]
local position = { x = left_top.x + vector[1], y = left_top.y + vector[2] }
local key = Functions.position_to_string(position)
minesweeper.cells[key] = { 10 }
minesweeper.active_mines = minesweeper.active_mines + 1
end
end
-- remove mines that would form a 3x3 block
for _, chunk_vector in pairs(chunk_vectors) do
local left_top_2 = { x = left_top.x + chunk_vector[1], y = left_top.y + chunk_vector[2] }
for _, vector in pairs(chunk_divide_vectors) do
local position = { x = left_top_2.x + vector[1], y = left_top_2.y + vector[2] }
local key = Functions.position_to_string(position)
local cell = minesweeper.cells[key]
if cell and cell[1] == 10 then
if get_adjacent_mine_count(position) == 8 then
--if cell[2] then rendering.destroy(cell[2]) end
minesweeper.cells[key] = nil
end
end
end
--[[
for _, vector in pairs(chunk_divide_vectors) do
local position = {x = left_top_2.x + vector[1], y = left_top_2.y + vector[2]}
local key = Functions.position_to_string(position)
local cell = minesweeper.cells[key]
if cell then update_rendering(cell, position) end
end
]]
end
end
local function on_chunk_generated(event)
local left_top = event.area.left_top
local surface = event.surface
if surface.index ~= 1 then
return
end
local distance_to_center = math.sqrt((left_top.x + 16) ^ 2 + (left_top.y + 16) ^ 2)
local tiles = {}
if distance_to_center < 128 then
for x = 0, 31, 1 do
for y = 0, 31, 1 do
local position = { x = left_top.x + x, y = left_top.y + y }
if Functions.is_spawn(position) then
table.insert(tiles, { name = Functions.get_terrain_tile(surface, position), position = position })
else
table.insert(tiles, { name = 'nuclear-ground', position = position })
end
end
end
else
for x = 0, 31, 1 do
for y = 0, 31, 1 do
local position = { x = left_top.x + x, y = left_top.y + y }
table.insert(tiles, { name = 'nuclear-ground', position = position })
--table.insert(tiles, {name = Functions.get_terrain_tile(surface, position), position = position})
end
end
end
surface.set_tiles(tiles, true)
--surface.clear() will cause to trigger on_chunk_generated twice
local key = Functions.position_to_string(left_top)
if minesweeper.chunks[key] then
return
end
minesweeper.chunks[key] = true
add_mines_to_chunk(left_top, distance_to_center)
end
local function on_player_changed_position(event)
local player = game.players[event.player_index]
if not Functions.is_minefield_tile(player.position) then
return
end
local cell_position = Functions.position_to_cell_position(player.position)
local score_change = visit_cell(cell_position)
if score_change < 0 then
solve_attempt(cell_position)
end
Map_score.set_score(player, Map_score.get_score(player) + score_change)
end
local function deny_building(event)
local entity = event.entity
if not entity.valid then
return
end
if not prototypes.item[entity.name] then
return
end
if not Functions.is_minefield_tile(entity.position, true) then
return
end
if event.player_index then
local player = game.players[event.player_index]
if entity.position.x % 2 == 1 and entity.position.y % 2 == 1 and entity.name == 'stone-furnace' then
local score_change = mark_mine(entity, player)
Map_score.set_score(player, Map_score.get_score(player) + score_change)
return
end
player.insert({ name = entity.name, count = 1 })
else
local inventory = event.robot.get_inventory(defines.inventory.robot_cargo)
inventory.insert({ name = entity.name, count = 1 })
end
entity.destroy()
end
local function on_built_entity(event)
deny_building(event)
end
local function on_robot_built_entity(event)
deny_building(event)
end
local function update_built_tiles(tiles)
for _, placed_tile in pairs(tiles) do
local cell_position = Functions.position_to_cell_position(placed_tile.position)
local key = Functions.position_to_string(cell_position)
local cell = minesweeper.cells[key]
if not cell and Functions.is_minefield_tile(placed_tile.position) then
minesweeper.cells[key] = { 9 }
end
cell = minesweeper.cells[key]
if cell then
update_rendering(cell, cell_position)
end
end
end
local function on_player_built_tile(event)
update_built_tiles(event.tiles)
end
local function on_robot_built_tile(event)
update_built_tiles(event.tiles)
end
local function on_player_mined_tile(event)
update_built_tiles(event.tiles)
end
local function on_robot_mined_tile(event)
update_built_tiles(event.tiles)
end
local function on_player_created(event)
local player = game.players[event.player_index]
player.insert({ name = 'stone-furnace', count = 1 })
end
local function on_player_respawned(event)
local player = game.players[event.player_index]
player.insert({ name = 'stone-furnace', count = 1 })
game.surfaces.nauvis.destroy_decoratives({ name = 'nuclear-ground-patch' })
end
local function on_entity_died(event)
local entity = event.entity
if not entity.valid then
return
end
if entity.force.index ~= 2 then
return
end
local force = event.force
if not force then
return
end
if force.name ~= 'minesweeper' then
return
end
local revived_entity = entity.clone({ position = entity.position })
revived_entity.health = entity.max_health
entity.destroy()
end
local function on_nth_tick()
for k, position in pairs(minesweeper.visit_queue) do
visit_cell(position)
table.remove(minesweeper.visit_queue, k)
break
end
end
local function on_init()
game.create_force('minesweeper')
storage.custom_highscore.description = 'Minesweep rank:'
local surface = game.surfaces[1]
local mgs = surface.map_gen_settings
mgs.water = 0
mgs.cliff_settings = { cliff_elevation_interval = 0, cliff_elevation_0 = 0 }
mgs.autoplace_controls =
{
['coal'] = { frequency = 0, size = 0, richness = 0 },
['stone'] = { frequency = 0, size = 0, richness = 0 },
['copper-ore'] = { frequency = 0, size = 0, richness = 0 },
['iron-ore'] = { frequency = 0, size = 0, richness = 0 },
['uranium-ore'] = { frequency = 0, size = 0, richness = 0 },
['crude-oil'] = { frequency = 0, size = 0, richness = 0 },
['trees'] = { frequency = 4, size = 0.5, richness = 0.1 }
}
surface.map_gen_settings = mgs
surface.clear(true)
minesweeper.chunks = {}
minesweeper.cells = {}
minesweeper.visit_queue = {}
minesweeper.player_data = {}
minesweeper.active_mines = 0
minesweeper.disarmed_mines = 0
minesweeper.triggered_mines = 0
local T = Map.get_map_information()
T.localised_category = 'minesweeper'
T.main_caption_color = { r = 255, g = 125, b = 55 }
T.sub_caption_color = { r = 0, g = 250, b = 150 }
end
local Event = require 'utils.event'
Event.on_init(on_init)
Event.on_nth_tick(2, on_nth_tick)
Event.add(defines.events.on_chunk_generated, on_chunk_generated)
Event.add(defines.events.on_player_changed_position, on_player_changed_position)
Event.add(defines.events.on_built_entity, on_built_entity)
Event.add(defines.events.on_entity_died, on_entity_died)
Event.add(defines.events.on_robot_built_entity, on_robot_built_entity)
Event.add(defines.events.on_player_created, on_player_created)
Event.add(defines.events.on_player_respawned, on_player_respawned)
Event.add(defines.events.on_robot_built_tile, on_robot_built_tile)
Event.add(defines.events.on_player_built_tile, on_player_built_tile)
Event.add(defines.events.on_robot_mined_tile, on_robot_mined_tile)
Event.add(defines.events.on_player_mined_tile, on_player_mined_tile)