mirror of
https://github.com/Refactorio/RedMew.git
synced 2025-03-05 15:05:57 +02:00
Add Tetris (#605)
Somehow nailed everything on the first try. What a pro.
This commit is contained in:
parent
f09ca708ee
commit
7fa83bae6c
@ -57,8 +57,8 @@ global.config = {
|
||||
paint = {
|
||||
enabled = true,
|
||||
},
|
||||
-- adds a fish market
|
||||
fish_market = {
|
||||
-- adds a market
|
||||
market = {
|
||||
enabled = true,
|
||||
currency = currency,
|
||||
},
|
||||
|
@ -41,8 +41,8 @@ end
|
||||
if config.donator_messages.enabled then
|
||||
require 'features.donator_messages'
|
||||
end
|
||||
if config.fish_market.enabled then
|
||||
require 'features.fish_market'
|
||||
if config.market.enabled then
|
||||
require 'features.market'
|
||||
end
|
||||
if config.nuke_control.enabled then
|
||||
require 'features.nuke_control'
|
||||
|
@ -763,7 +763,7 @@ Gui.allow_player_to_toggle_top_element_visibility(main_button_name)
|
||||
local Public = {}
|
||||
|
||||
function Public.show_info(player)
|
||||
toggle(player)
|
||||
toggle({player = player})
|
||||
end
|
||||
|
||||
function Public.get_map_name()
|
||||
|
@ -129,10 +129,6 @@ local function toggle(event)
|
||||
direction = 'vertical',
|
||||
caption = 'Paint Brush'
|
||||
}
|
||||
main_frame.add {
|
||||
type = 'label',
|
||||
caption = 'Choose a replacement tile for Refined hazard concrete'
|
||||
}
|
||||
|
||||
local tooltip = global.paint_brushes_by_player[event.player_index] or ''
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
local Module = {}
|
||||
|
||||
local Event = require 'utils.event'
|
||||
local Token = require 'utils.token'
|
||||
local Gui = require 'utils.gui'
|
||||
@ -19,6 +21,12 @@ Global.register(
|
||||
local chest_gui_frame_name = Gui.uid_name()
|
||||
local chest_content_table_name = Gui.uid_name()
|
||||
|
||||
function Module.create_chest(surface, position, storage)
|
||||
local entity = surface.create_entity{name = 'infinity-chest', position = position, force = 'player'}
|
||||
chests[entity.unit_number] = {entity = entity, storage = storage}
|
||||
return entity
|
||||
end
|
||||
|
||||
local function built_entity(event)
|
||||
local entity = event.created_entity
|
||||
if not entity or not entity.valid or entity.name ~= 'infinity-chest' then
|
||||
@ -251,3 +259,5 @@ Gui.on_custom_close(
|
||||
|
||||
local market_items = require 'resources.market_items'
|
||||
table.insert(market_items, {price = 100, name = 'infinity-chest', description = 'Stores unlimited quantity of items for up to 48 different item types'})
|
||||
return Module
|
||||
|
||||
|
@ -11,7 +11,7 @@ local pairs = pairs
|
||||
local random = math.random
|
||||
local format = string.format
|
||||
local get_random = table.get_random
|
||||
local currency = global.config.fish_market.currency
|
||||
local currency = global.config.market.currency
|
||||
|
||||
local running_speed_boost_messages = {
|
||||
'%s found the lost Dragon Scroll and got a lv.1 speed boost!',
|
||||
|
@ -50,9 +50,9 @@ local function player_created(event)
|
||||
p(get_random_weighted(random_messages))
|
||||
end
|
||||
|
||||
if config.show_info_at_start then
|
||||
if config.show_info_at_start and not _DEBUG then
|
||||
if Info ~= nil then
|
||||
Info.show_info({player = player})
|
||||
Info.show_info(player)
|
||||
end
|
||||
end
|
||||
|
||||
|
437
map_gen/combined/tetris/control.lua
Normal file
437
map_gen/combined/tetris/control.lua
Normal file
@ -0,0 +1,437 @@
|
||||
local Event = require 'utils.event'
|
||||
local Token = require 'utils.token'
|
||||
local Task = require 'utils.task'
|
||||
local Global = require 'utils.global'
|
||||
local Game = require 'utils.game'
|
||||
local Debug = require 'utils.debug'
|
||||
local Map = require 'map_gen.combined.tetris.shape'
|
||||
local Tetrimino = require 'map_gen.combined.tetris.tetrimino'(Map)
|
||||
local View = require 'map_gen.combined.tetris.view'
|
||||
local InfinityChest = require 'features.infinite_storage_chest'
|
||||
local states = require 'map_gen.combined.tetris.states'
|
||||
local StateMachine = require 'utils.state_machine'
|
||||
|
||||
local tetriminos = {}
|
||||
local primitives = {
|
||||
tetri_spawn_y_position = -160,
|
||||
winner_option_index = 0,
|
||||
state = states.voting,
|
||||
next_vote_finished = 305,
|
||||
points = 0,
|
||||
down_substate = 0
|
||||
}
|
||||
local player_votes = {}
|
||||
local options = {
|
||||
{
|
||||
button = View.button_enum.ccw_button,
|
||||
action_func_name = 'rotate',
|
||||
args = {false},
|
||||
transition = states.moving
|
||||
},
|
||||
{
|
||||
button = View.button_enum.noop_button,
|
||||
action_func_name = 'noop',
|
||||
args = {},
|
||||
transition = states.moving
|
||||
},
|
||||
{
|
||||
button = View.button_enum.cw_button,
|
||||
action_func_name = 'rotate',
|
||||
args = {true},
|
||||
transition = states.moving
|
||||
},
|
||||
{
|
||||
button = View.button_enum.left_button,
|
||||
action_func_name = 'move',
|
||||
args = {-1, 0},
|
||||
transition = states.moving
|
||||
},
|
||||
{
|
||||
button = View.button_enum.down_button,
|
||||
transition = states.down
|
||||
},
|
||||
{
|
||||
button = View.button_enum.right_button,
|
||||
action_func_name = 'move',
|
||||
args = {1, 0},
|
||||
transition = states.moving
|
||||
},
|
||||
{
|
||||
button = View.button_enum.pause_button,
|
||||
action_func_name = 'noop',
|
||||
args = {},
|
||||
transition = states.pause
|
||||
}
|
||||
}
|
||||
|
||||
local machine = StateMachine.new(states.voting)
|
||||
|
||||
local player_zoom = {}
|
||||
local player_force = nil
|
||||
local nauvis = nil
|
||||
Global.register(
|
||||
{
|
||||
tetriminos = tetriminos,
|
||||
primitives = primitives,
|
||||
player_votes = player_votes,
|
||||
player_zoom = player_zoom,
|
||||
machine = machine
|
||||
},
|
||||
function(tbl)
|
||||
tetriminos = tbl.tetriminos
|
||||
primitives = tbl.primitives
|
||||
player_votes = tbl.player_votes
|
||||
player_zoom = tbl.player_zoom
|
||||
machine = tbl.machine
|
||||
end
|
||||
)
|
||||
|
||||
local point_table = {1, 3, 5, 9}
|
||||
local tetris_tick_duration = 61
|
||||
global.vote_delay = 10
|
||||
|
||||
local function calculate_winner()
|
||||
if StateMachine.in_state(machine, states.down) then --TODO: Fix
|
||||
return --Halt vote if in down mode
|
||||
end
|
||||
Debug.print('calculating winner')
|
||||
local vote_sum = {0, 0, 0, 0, 0, 0, 0}
|
||||
for _, vote in pairs(player_votes) do
|
||||
vote_sum[vote] = vote_sum[vote] + 1
|
||||
end
|
||||
|
||||
local winners = {}
|
||||
local max = math.max(vote_sum[1], vote_sum[2], vote_sum[3], vote_sum[4], vote_sum[5], vote_sum[6], vote_sum[7])
|
||||
for candidate, n_votes in pairs(vote_sum) do
|
||||
if max == n_votes then
|
||||
table.insert(winners, candidate)
|
||||
end
|
||||
View.set_vote_number(options[candidate].button, n_votes)
|
||||
end
|
||||
local winner_option_index = 0
|
||||
if max > 0 then
|
||||
winner_option_index = winners[math.random(#winners)]
|
||||
end
|
||||
primitives.winner_option_index = winner_option_index
|
||||
if _DEBUG and (winner_option_index > 0) then
|
||||
Debug.print('Calculated winner: ' .. View.pretty_names[options[winner_option_index].button])
|
||||
end
|
||||
end
|
||||
|
||||
local function player_vote(player, option_index)
|
||||
local old_vote = player_votes[player.index]
|
||||
if old_vote == option_index then
|
||||
return
|
||||
end
|
||||
local vote_button = nil
|
||||
local old_vote_button = nil
|
||||
|
||||
if option_index then
|
||||
vote_button = options[option_index].button
|
||||
end
|
||||
|
||||
if old_vote then
|
||||
old_vote_button = options[old_vote].button
|
||||
end
|
||||
|
||||
player_votes[player.index] = option_index
|
||||
|
||||
if _DEBUG then
|
||||
Debug.print(string.format('%s voted for %s', player.name, View.pretty_names[vote_button]))
|
||||
end
|
||||
StateMachine.transition(machine, states.voting)
|
||||
|
||||
calculate_winner()
|
||||
|
||||
View.set_player_vote(player, vote_button, old_vote_button)
|
||||
end
|
||||
|
||||
for option_index, option in pairs(options) do
|
||||
View.bind_button(
|
||||
option.button,
|
||||
function(player)
|
||||
player_vote(player, option_index)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
local function spawn_new_tetrimino()
|
||||
table.insert(tetriminos, Tetrimino.new(nauvis, {x = 0, y = primitives.tetri_spawn_y_position}))
|
||||
end
|
||||
|
||||
local function collect_full_row_resources(tetri)
|
||||
local active_qchunks = Tetrimino.active_qchunks(tetri)
|
||||
local storage = {}
|
||||
|
||||
local full_rows = {}
|
||||
local rows = {}
|
||||
local position = tetri.position
|
||||
local tetri_y = position.y
|
||||
local surface = tetri.surface
|
||||
local get_tile = surface.get_tile
|
||||
local find_entities_filtered = surface.find_entities_filtered
|
||||
for _, qchunk in pairs(active_qchunks) do
|
||||
local q_y = qchunk.y
|
||||
if not rows[q_y] then
|
||||
rows[q_y] = true
|
||||
local y = tetri_y + 16 * q_y - 14
|
||||
local row_full = true
|
||||
for x = -178, 178, 16 do
|
||||
local tile = get_tile(x, y)
|
||||
if tile.valid and tile.name == 'water' then
|
||||
row_full = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if row_full then
|
||||
table.insert(full_rows, q_y)
|
||||
for _, patch in pairs(find_entities_filtered {type = 'resource', area = {{-178, y}, {162, y + 12}}}) do
|
||||
local subtotal = storage[patch.name] or 0
|
||||
storage[patch.name] = subtotal + patch.amount
|
||||
patch.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #full_rows > 0 then
|
||||
local points = point_table[#full_rows]
|
||||
for resource, amount in pairs(storage) do
|
||||
storage[resource] = amount * points
|
||||
if resource == 'crude-oil' then
|
||||
storage[resource] = nil
|
||||
if #full_rows == 1 then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local x = position.x + active_qchunks[1].x * 16 - 9
|
||||
local y = tetri_y + active_qchunks[1].y * 16 - 9
|
||||
local chest = InfinityChest.create_chest(tetri.surface, {x, y}, storage)
|
||||
chest.minable = false
|
||||
chest.destructible = false
|
||||
|
||||
primitives.points = primitives.points + points * 100
|
||||
View.set_points(primitives.points)
|
||||
end
|
||||
end
|
||||
|
||||
local function tetrimino_finished(tetri)
|
||||
local final_y_position = tetri.position.y
|
||||
if final_y_position < (primitives.tetri_spawn_y_position + 352) then
|
||||
primitives.tetri_spawn_y_position = final_y_position - 256
|
||||
player_force.chart(tetri.surface, {{-192, final_y_position - 352}, {160, final_y_position - 176}})
|
||||
end
|
||||
|
||||
StateMachine.transition(machine, states.voting)
|
||||
|
||||
collect_full_row_resources(tetri)
|
||||
|
||||
spawn_new_tetrimino()
|
||||
end
|
||||
|
||||
local chart_area =
|
||||
Token.register(
|
||||
function(data)
|
||||
data.force.chart(data.surface, data.area)
|
||||
end
|
||||
)
|
||||
|
||||
local switch_state =
|
||||
Token.register(
|
||||
function(data)
|
||||
StateMachine.transition(machine, data.state)
|
||||
end
|
||||
)
|
||||
|
||||
local move_down =
|
||||
Token.register(
|
||||
function()
|
||||
for key, tetri in pairs(tetriminos) do
|
||||
if not Tetrimino.move(tetri, 0, 1) then
|
||||
tetrimino_finished(tetri) --If collided with ground fire finished event
|
||||
tetriminos[key] = nil
|
||||
end
|
||||
|
||||
local pos = tetri.position
|
||||
Task.set_timeout_in_ticks(
|
||||
10,
|
||||
chart_area,
|
||||
{
|
||||
force = player_force,
|
||||
surface = nauvis,
|
||||
area = {
|
||||
{pos.x - 32, pos.y - 32},
|
||||
{pos.x + 64, pos.y + 64}
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
local function execute_vote_tick()
|
||||
if game.tick < primitives.next_vote_finished then
|
||||
return
|
||||
end
|
||||
|
||||
local winner = options[primitives.winner_option_index]
|
||||
if winner then
|
||||
StateMachine.transition(machine, winner.transition)
|
||||
View.set_last_move(winner.button)
|
||||
else
|
||||
View.set_last_move(nil)
|
||||
StateMachine.transition(machine, states.moving)
|
||||
end
|
||||
|
||||
primitives.winner_option_index = 0
|
||||
for player_index, _ in pairs(player_votes) do -- reset poll
|
||||
player_votes[player_index] = nil
|
||||
end
|
||||
View.reset_poll_buttons()
|
||||
end
|
||||
|
||||
local function execute_winner_action()
|
||||
for key, tetri in pairs(tetriminos) do --Execute voted action
|
||||
local winner = options[primitives.winner_option_index]
|
||||
--Execute voted action
|
||||
if winner then
|
||||
local action = Tetrimino[winner.action_func_name]
|
||||
if action then
|
||||
action(tetri, winner.args[1], winner.args[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Task.set_timeout_in_ticks(16, move_down)
|
||||
Task.set_timeout_in_ticks(26, switch_state, {state = states.voting})
|
||||
end
|
||||
|
||||
local spawn_new_tetrimino_token = Token.register(spawn_new_tetrimino)
|
||||
Event.on_init(
|
||||
function()
|
||||
player_force = game.forces.player
|
||||
nauvis = game.surfaces.nauvis
|
||||
player_force.chart(nauvis, {{-192, -432}, {160, 0}})
|
||||
Task.set_timeout_in_ticks(30 * tetris_tick_duration - 15, spawn_new_tetrimino_token)
|
||||
View.enable_vote_buttons(true)
|
||||
end
|
||||
)
|
||||
|
||||
Event.add(
|
||||
defines.events.on_tick,
|
||||
function()
|
||||
if StateMachine.in_state(machine, states.voting) then
|
||||
local progress = (primitives.next_vote_finished - game.tick + 1) / global.vote_delay / tetris_tick_duration
|
||||
if progress >= 0 and progress <= 1 then
|
||||
View.set_progress(progress)
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
local function execute_down_tick()
|
||||
local down_state = primitives.down_substate
|
||||
|
||||
if down_state > 3 then
|
||||
primitives.down_substate = 0
|
||||
StateMachine.transition(machine, states.voting)
|
||||
return
|
||||
end
|
||||
|
||||
primitives.down_substate = down_state + 1
|
||||
|
||||
Task.set_timeout_in_ticks(16, move_down)
|
||||
end
|
||||
|
||||
StateMachine.register_state_tick_callback(machine, states.voting, execute_vote_tick)
|
||||
|
||||
StateMachine.register_state_tick_callback(machine, states.down, execute_down_tick)
|
||||
|
||||
StateMachine.register_transition_callback(
|
||||
machine,
|
||||
states.voting,
|
||||
states.pause,
|
||||
function()
|
||||
View.enable_vote_buttons(true)
|
||||
game.print('Pausing...')
|
||||
end
|
||||
)
|
||||
|
||||
StateMachine.register_transition_callback(
|
||||
machine,
|
||||
states.pause,
|
||||
states.voting,
|
||||
function()
|
||||
primitives.next_vote_finished = global.vote_delay * tetris_tick_duration + game.tick
|
||||
game.print('Resuming...')
|
||||
end
|
||||
)
|
||||
|
||||
StateMachine.register_transition_callback(
|
||||
machine,
|
||||
states.moving,
|
||||
states.voting,
|
||||
function()
|
||||
View.enable_vote_buttons(true)
|
||||
end
|
||||
)
|
||||
|
||||
StateMachine.register_transition_callback(
|
||||
machine,
|
||||
states.down,
|
||||
states.voting,
|
||||
function()
|
||||
View.enable_vote_buttons(true)
|
||||
end
|
||||
)
|
||||
|
||||
StateMachine.register_transition_callback(
|
||||
machine,
|
||||
states.voting,
|
||||
states.down,
|
||||
function()
|
||||
primitives.next_vote_finished = (3 + global.vote_delay) * tetris_tick_duration + game.tick
|
||||
View.enable_vote_buttons(false)
|
||||
end
|
||||
)
|
||||
|
||||
StateMachine.register_transition_callback(
|
||||
machine,
|
||||
states.voting,
|
||||
states.moving,
|
||||
function()
|
||||
View.enable_vote_buttons(false)
|
||||
primitives.next_vote_finished = global.vote_delay * tetris_tick_duration + game.tick
|
||||
execute_winner_action()
|
||||
end
|
||||
)
|
||||
|
||||
Event.on_nth_tick(
|
||||
tetris_tick_duration,
|
||||
function()
|
||||
StateMachine.machine_tick(machine)
|
||||
end
|
||||
)
|
||||
|
||||
Event.add(
|
||||
defines.events.on_player_left_game,
|
||||
function(event)
|
||||
player_votes[event.player_index] = nil
|
||||
end
|
||||
)
|
||||
|
||||
Event.add(
|
||||
defines.events.on_player_created,
|
||||
function(event)
|
||||
local player = Game.get_player_by_index(event.player_index)
|
||||
|
||||
local position = player.surface.find_non_colliding_position('player', {8, 8}, 3, 1)
|
||||
if position then
|
||||
player.teleport(position)
|
||||
end
|
||||
end
|
||||
)
|
||||
return Map.get_map()
|
423
map_gen/combined/tetris/shape.lua
Normal file
423
map_gen/combined/tetris/shape.lua
Normal file
@ -0,0 +1,423 @@
|
||||
-- Map by grilledham & Jayefuu
|
||||
|
||||
-- Set scenario generation cliffs to none.
|
||||
-- Load blueprint from scenarios\RedMew\map_gen\data\presets\tetris\
|
||||
-- Obtain items using silent commands from scenarios\RedMew\map_gen\data\presets\tetris\
|
||||
-- Place the blueprint on the island south of spawn
|
||||
-- Teleport to centre of island and run the second command in tetris_theme_items_command.txt
|
||||
-- Excellent tetris themed music generated from midi files, credit to mgabor of miditorio.com
|
||||
|
||||
local b = require 'map_gen.shared.builders'
|
||||
local math = require 'utils.math'
|
||||
local degrees = math.rad
|
||||
local ore_seed1 = 7000
|
||||
local ore_seed2 = ore_seed1 * 2
|
||||
local noise = require 'map_gen.shared.perlin_noise'.noise
|
||||
local abs = math.abs
|
||||
|
||||
require 'utils.table'
|
||||
|
||||
local Random = require 'map_gen.shared.random'
|
||||
local random = Random.new(ore_seed1, ore_seed2)
|
||||
local math_random = math.random
|
||||
|
||||
local enable_sand_border = false
|
||||
|
||||
local function value(base, mult, pow)
|
||||
return function(x, y)
|
||||
local d_sq = x * x + y * y
|
||||
return base + mult * d_sq ^ (pow / 2) -- d ^ pow
|
||||
end
|
||||
end
|
||||
|
||||
-- Removes vanilla resources when called
|
||||
local function no_resources(_, _, world, tile)
|
||||
for _, e in ipairs(
|
||||
world.surface.find_entities_filtered(
|
||||
{type = 'resource', area = {{world.x, world.y}, {world.x + 1, world.y + 1}}}
|
||||
)
|
||||
) do
|
||||
e.destroy()
|
||||
end
|
||||
return tile
|
||||
end
|
||||
|
||||
local m_t_width = 12 -- map size in number of tiles
|
||||
local t_width = 16 -- tile width
|
||||
local t_h_width = t_width / 2
|
||||
|
||||
-- https://wiki.factorio.com/Data.raw#tile for the tile types you can send to this function
|
||||
local function two_tone_square(inner, outer) -- r_tile is a bool flag to show if it should have chance of resources on it
|
||||
local outer_tile = b.any {b.rectangle(t_width, t_width)}
|
||||
outer_tile = b.change_tile(outer_tile, true, outer)
|
||||
local inner_tile = b.any {b.rectangle(t_width - 2, t_width - 2)}
|
||||
inner_tile = b.change_tile(inner_tile, true, inner)
|
||||
local land_tile = b.any {inner_tile, outer_tile}
|
||||
|
||||
return land_tile
|
||||
end
|
||||
|
||||
local tet_bounds = b.rectangle(t_width * 4)
|
||||
tet_bounds = b.translate(tet_bounds, t_width, t_width)
|
||||
local function tetrify(pattern, block)
|
||||
for r = 1, 4 do
|
||||
local row = pattern[r]
|
||||
for c = 1, 4 do
|
||||
if row[c] == 1 then
|
||||
row[c] = block
|
||||
else
|
||||
row[c] = b.empty_shape()
|
||||
end
|
||||
end
|
||||
end
|
||||
local grid = b.grid_pattern(pattern, 4, 4, t_width, t_width)
|
||||
grid = b.translate(grid, -t_width / 2, -t_width / 2)
|
||||
grid = b.choose(tet_bounds, grid, b.empty_shape)
|
||||
grid = b.translate(grid, -t_width, -t_width)
|
||||
return grid
|
||||
end
|
||||
|
||||
local tet_O =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
two_tone_square('dirt-7', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_I =
|
||||
tetrify(
|
||||
{
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0}
|
||||
},
|
||||
two_tone_square('grass-2', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_J =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 1, 1, 0}
|
||||
},
|
||||
two_tone_square('grass-1', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_L =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 1, 0}
|
||||
},
|
||||
two_tone_square('dirt-4', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_S =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{1, 1, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
two_tone_square('grass-4', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_Z =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{1, 1, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
two_tone_square('grass-3', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_T =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{1, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
two_tone_square('red-desert-2', 'sand-1')
|
||||
)
|
||||
|
||||
local tetriminos = {tet_I, tet_O, tet_T, tet_S, tet_Z, tet_J, tet_L}
|
||||
local tetriminos_count = #tetriminos
|
||||
|
||||
local quarter = math.tau / 4
|
||||
|
||||
local p_cols = 1 --m_t_width / 4
|
||||
local p_rows = 50
|
||||
local pattern = {}
|
||||
|
||||
for _ = 1, p_rows do
|
||||
local row = {}
|
||||
table.insert(pattern, row)
|
||||
for _ = 1, p_cols do
|
||||
local i = random:next_int(1, tetriminos_count * 1.5)
|
||||
local shape = tetriminos[i] or b.empty_shape
|
||||
|
||||
local angle = random:next_int(0, 3) * quarter
|
||||
shape = b.rotate(shape, angle)
|
||||
|
||||
local x_offset = random:next_int(-10, 8) * t_width
|
||||
shape = b.translate(shape, x_offset, 0)
|
||||
|
||||
table.insert(row, shape)
|
||||
end
|
||||
end
|
||||
|
||||
local ore_shape = b.rectangle(t_width * 0.8)
|
||||
local oil_shape = b.throttle_world_xy(ore_shape, 1, 4, 1, 4)
|
||||
|
||||
local ores = {
|
||||
{b.resource(ore_shape, 'iron-ore', value(50, 0.225, 1.15)), 10},
|
||||
{b.resource(ore_shape, 'copper-ore', value(40, 0.225, 1.15)), 6},
|
||||
{b.resource(ore_shape, 'stone', value(70, 0.12, 1.075)), 3},
|
||||
{b.resource(ore_shape, 'coal', value(40, 0.24, 1.075)), 5},
|
||||
{b.resource(b.scale(ore_shape, 0.5), 'uranium-ore', value(60, 0.09, 1.05)), 2},
|
||||
{b.resource(oil_shape, 'crude-oil', value(120000, 50, 1.15)), 1},
|
||||
{b.empty_shape, 100}
|
||||
}
|
||||
|
||||
local total_weights = {}
|
||||
local t = 0
|
||||
for _, v in pairs(ores) do
|
||||
t = t + v[2]
|
||||
table.insert(total_weights, t)
|
||||
end
|
||||
|
||||
p_cols = 50
|
||||
p_rows = 50
|
||||
|
||||
pattern = {}
|
||||
|
||||
for _ = 1, p_rows do
|
||||
local row = {}
|
||||
table.insert(pattern, row)
|
||||
for _ = 1, p_cols do
|
||||
local i = random:next_int(1, t)
|
||||
|
||||
local index = table.binary_search(total_weights, i)
|
||||
if (index < 0) then
|
||||
index = bit32.bnot(index)
|
||||
end
|
||||
|
||||
local shape = ores[index][1]
|
||||
table.insert(row, shape)
|
||||
end
|
||||
end
|
||||
|
||||
local worm_names = {
|
||||
'small-worm-turret',
|
||||
'medium-worm-turret',
|
||||
'big-worm-turret'
|
||||
}
|
||||
|
||||
local max_worm_chance = 1 / 128
|
||||
local max_spawner_chance = 1/64
|
||||
local worm_chance_factor = 1 / (192 * 512)
|
||||
|
||||
local function worms(position)
|
||||
local d = abs(position.y)
|
||||
local worm_chance = d - 300
|
||||
|
||||
if worm_chance > 0 then
|
||||
worm_chance = worm_chance * worm_chance_factor
|
||||
worm_chance = math.min(worm_chance, max_worm_chance)
|
||||
|
||||
if math_random() < worm_chance then
|
||||
if d < 600 then
|
||||
return {name = 'small-worm-turret', position = position}
|
||||
else
|
||||
local max_lvl
|
||||
local min_lvl
|
||||
if d < 800 then
|
||||
max_lvl = 2
|
||||
min_lvl = 1
|
||||
else
|
||||
max_lvl = 3
|
||||
min_lvl = 2
|
||||
end
|
||||
local lvl = math_random() ^ (512 / d) * max_lvl
|
||||
lvl = math.ceil(lvl)
|
||||
lvl = math.clamp(lvl, min_lvl, 3)
|
||||
return {name = worm_names[lvl], position = position}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local spawner_names = {
|
||||
'spitter-spawner',
|
||||
'biter-spawner'
|
||||
}
|
||||
|
||||
local function spawners(position)
|
||||
|
||||
local spawner_chance = abs(position.y) - 600
|
||||
|
||||
if spawner_chance > 0 then
|
||||
spawner_chance = spawner_chance * worm_chance_factor
|
||||
spawner_chance = math.min(spawner_chance, max_spawner_chance)
|
||||
|
||||
if math_random() < spawner_chance then
|
||||
return {name = spawner_names[math_random(1,2)], position = position}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Starting area
|
||||
local start_patch = b.rectangle(t_width * 0.8)
|
||||
local start_iron_patch =
|
||||
b.resource(
|
||||
b.translate(start_patch, -t_width/2, -t_width/2),
|
||||
'iron-ore',
|
||||
function()
|
||||
return 1500
|
||||
end
|
||||
)
|
||||
local start_copper_patch =
|
||||
b.resource(
|
||||
b.translate(start_patch, t_width/2, -t_width/2),
|
||||
'copper-ore',
|
||||
function()
|
||||
return 1200
|
||||
end
|
||||
)
|
||||
local start_stone_patch =
|
||||
b.resource(
|
||||
b.translate(start_patch, t_width/2, t_width/2),
|
||||
'stone',
|
||||
function()
|
||||
return 900
|
||||
end
|
||||
)
|
||||
local start_coal_patch =
|
||||
b.resource(
|
||||
b.translate(start_patch, -t_width/2, t_width/2),
|
||||
'coal',
|
||||
function()
|
||||
return 1350
|
||||
end
|
||||
)
|
||||
local start_resources = b.any({start_iron_patch, start_copper_patch, start_stone_patch, start_coal_patch})
|
||||
local tet_O_start = b.apply_entity(tet_O, start_resources)
|
||||
|
||||
local starting_area = b.any{
|
||||
b.translate(tet_I,t_width,-t_width*2),
|
||||
b.translate(tet_O_start,t_width*2,-t_width),
|
||||
b.translate(tet_T,-t_width,-t_width),
|
||||
b.translate(tet_Z,-t_width*6,-t_width),
|
||||
b.translate(tet_L,-t_width*8,-t_width*2)
|
||||
}
|
||||
|
||||
ores = b.grid_pattern_overlap(pattern, p_cols, p_rows, t_width, t_width)
|
||||
ores = b.translate(ores, t_h_width, t_h_width)
|
||||
|
||||
local water_tile = two_tone_square('water', 'deepwater')
|
||||
local half_sea_width = m_t_width * t_width - t_width
|
||||
local function sea_bounds(x, y)
|
||||
return x > -half_sea_width and x < half_sea_width and y < 0
|
||||
end
|
||||
|
||||
local sea = b.single_grid_pattern(water_tile, t_width, t_width)
|
||||
sea = b.translate(sea, t_h_width, -t_h_width)
|
||||
sea = b.choose(sea_bounds, sea, b.empty_shape)
|
||||
|
||||
local map = b.choose(sea_bounds, starting_area, b.empty_shape)
|
||||
map = b.if_else(map, sea)
|
||||
|
||||
local half_border_width = half_sea_width + t_width
|
||||
local function border_bounds(x, y)
|
||||
return x > -half_border_width and x < half_border_width and y < t_width
|
||||
end
|
||||
|
||||
border_bounds = b.subtract(border_bounds, sea_bounds)
|
||||
local border = b.change_tile(border_bounds, true, 'sand-1')
|
||||
if enable_sand_border then
|
||||
map = b.add(map, border)
|
||||
end
|
||||
local music_island = b.translate(b.rotate(tet_I,degrees(90)),0, 2*t_width)
|
||||
map = b.add(map,music_island)
|
||||
map = b.translate(map, 0, -t_width / 2 + 24)
|
||||
|
||||
map = b.apply_effect(map, no_resources)
|
||||
|
||||
local bounds = t_width * 2
|
||||
|
||||
local Module = {}
|
||||
|
||||
local bounds_size = t_width * 4
|
||||
|
||||
function Module.spawn_tetri(surface, pos, number)
|
||||
local tiles = {}
|
||||
local shape = tetriminos[number]
|
||||
|
||||
local offset = math_random(1,1000) * bounds_size
|
||||
|
||||
local create_entity = surface.create_entity
|
||||
|
||||
local tree = 'tree-0' .. math_random(1,9)
|
||||
|
||||
for x = -bounds, bounds do
|
||||
for y = -bounds, bounds do
|
||||
local x2, y2 = x + 0.5, y + 0.5
|
||||
local name = shape(x2, y2)
|
||||
if name then
|
||||
local position = {x = pos.x + x, y = pos.y + y}
|
||||
table.insert(tiles, {name = name, position = position})
|
||||
|
||||
if math_random() > 0.8 and (noise(0.02 * x, 0.02 * y,0)) > 0.3 then
|
||||
create_entity {name = tree, position = position}
|
||||
else
|
||||
local n = math_random(1, 599)
|
||||
if n > 590 then
|
||||
create_entity{name = 'tree-0' .. n % 10, position = position}
|
||||
end
|
||||
end
|
||||
|
||||
local ore = ores(x2, y2 - offset, position)
|
||||
if ore then
|
||||
ore.position = position
|
||||
ore.enable_tree_removal = false
|
||||
create_entity(ore)
|
||||
end
|
||||
|
||||
local worm = worms(position)
|
||||
if worm then
|
||||
create_entity(worm)
|
||||
else
|
||||
local spawner = spawners(position)
|
||||
if spawner then
|
||||
create_entity(spawner)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
surface.set_tiles(tiles)
|
||||
end
|
||||
|
||||
Module.disable = function()
|
||||
tetriminos = {}
|
||||
end
|
||||
|
||||
Module.get_map = function()
|
||||
return map
|
||||
end
|
||||
|
||||
return Module
|
6
map_gen/combined/tetris/states.lua
Normal file
6
map_gen/combined/tetris/states.lua
Normal file
@ -0,0 +1,6 @@
|
||||
return {
|
||||
voting = 1,
|
||||
moving = 2,
|
||||
down = 3,
|
||||
pause = 4
|
||||
}
|
387
map_gen/combined/tetris/tetrimino.lua
Normal file
387
map_gen/combined/tetris/tetrimino.lua
Normal file
@ -0,0 +1,387 @@
|
||||
--- @class This module provides a class that provides Tetrimino object functionallity
|
||||
local Tetrimino = {}
|
||||
|
||||
--- @type LuaTetrimino
|
||||
-- @field number number identifies the kind of Tetrimino
|
||||
-- @field collision_box LuaTetriminoCollisionBox @see LuaTetriminoCollisionBox
|
||||
-- @field position LuaPosition The current position of top left corner of the collision box of the tetrimino.
|
||||
-- Only x/y divisible by 16 are legal coordinates.
|
||||
|
||||
--- @type LuaTetriminoCollisionBox represents the collision box of a tetrimino. Contains a 4x4 2d array.
|
||||
-- @description If a cell is 1 the quad chunk represented by the array position is occupied. 0 if not
|
||||
-- @field 1 table of numbers first row
|
||||
-- @field 2 table of numbers second row
|
||||
-- @field 3 table of numbers thrid row
|
||||
-- @field 4 table of numbers forth row
|
||||
|
||||
local Token = require 'utils.token'
|
||||
local Task = require 'utils.task'
|
||||
local Queue = require 'utils.queue'
|
||||
local Global = require 'utils.global'
|
||||
local Game = require 'utils.game'
|
||||
|
||||
local insert = table.insert
|
||||
|
||||
--- Holds collision boxes for all tetriminos.
|
||||
-- The index of this table is what defines the tetriminos kind.
|
||||
-- IE: tetrimino with 1 is the "I" tetrimino
|
||||
-- @table containing LuaTetriminoCollisionBox
|
||||
local collision_boxes = {
|
||||
{
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0}
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{1, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{1, 1, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{1, 1, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 1, 1, 0}
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 1, 0}
|
||||
}
|
||||
}
|
||||
|
||||
local worker = nil
|
||||
local move_queue = Queue.new()
|
||||
local Map = nil
|
||||
local sequence = {1, 2, 3, 4, 5, 6, 7, _head = 8}
|
||||
|
||||
Global.register(
|
||||
{
|
||||
move_queue = move_queue,
|
||||
sequence = sequence
|
||||
},
|
||||
function(tbl)
|
||||
move_queue = tbl.move_queue
|
||||
sequence = tbl.sequence
|
||||
end
|
||||
)
|
||||
|
||||
--- Does this tetrimino collide with anything but itself with a new collision box(rotation) and an x/y offset
|
||||
-- @param self LuaTetrimino (See @LuaTetrimino) The tetrimino that may collide
|
||||
-- @param collision_box LuaTetriminoCollisionBox (See @LuaTetriminoCollisionBox) The suggested new collision box of self.
|
||||
-- @param x_steps number either -1, 0 or 1. Represent going left by 16 tiles, not moving in x direction or going right by 16 tiles
|
||||
-- @param y_steps number either -1, 0 or 1. Represent going up by 16 tiles, not moving in y direction or going down by 16 tiles
|
||||
-- @return boolean
|
||||
local function collides(self, collision_box, x_steps, y_steps)
|
||||
local old_collision_box = self.collision_box
|
||||
local position = self.position
|
||||
local surface = self.surface
|
||||
local c_x = position.x
|
||||
local c_y = position.y
|
||||
for y = 1, 4 do
|
||||
for x = 1, 4 do
|
||||
local bit = collision_box[y][x]
|
||||
if bit == 1 then
|
||||
local y_offset = y + y_steps
|
||||
local x_offset = x + x_steps
|
||||
if
|
||||
y_offset < 1 or --Cant collide with itself, so continue checking for collision
|
||||
y_offset > 4 or
|
||||
x_offset < 1 or
|
||||
x_offset > 4 or
|
||||
old_collision_box[y_offset][x_offset] == 0
|
||||
then --check for collision if not colliding with old self
|
||||
local x_target = x_offset * 16 + c_x + 2 - 16
|
||||
local y_target = y_offset * 16 + c_y + 2 - 16
|
||||
local tile = surface.get_tile {x_target, y_target}
|
||||
if not tile.valid or tile.name ~= 'water' then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Replaces a quad chunk with water and destroys all entities on it
|
||||
-- @param surface LuaSurface
|
||||
-- @param x number top left x position of the quad chunk
|
||||
-- @param y number top left y position of the quad chunk
|
||||
local function erase_qchunk(surface, x, y)
|
||||
local new_tiles = {}
|
||||
for c_x = x + 1, x + 14 do
|
||||
for c_y = y + 1, y + 14 do
|
||||
insert(new_tiles, {name = 'water', position = {c_x, c_y}})
|
||||
end
|
||||
end
|
||||
local y_plus15 = y + 15
|
||||
for c_x = x, x + 15 do
|
||||
insert(new_tiles, {name = 'deepwater', position = {c_x, y}})
|
||||
insert(new_tiles, {name = 'deepwater', position = {c_x, y_plus15}})
|
||||
end
|
||||
|
||||
local x_plus15 = x + 15
|
||||
for c_y = y, y + 14 do
|
||||
insert(new_tiles, {name = 'deepwater', position = {x, c_y}})
|
||||
insert(new_tiles, {name = 'deepwater', position = {x_plus15, c_y}})
|
||||
end
|
||||
surface.set_tiles(new_tiles)
|
||||
end
|
||||
|
||||
--- Moves a quad chunk (16x16 tiles)
|
||||
-- @param surface LuaSurface
|
||||
-- @param x number x position of the quad chunk
|
||||
-- @param y number y position of the quad chunk
|
||||
-- @param x_offset number tiles to move in x direction
|
||||
-- @param y_offset number tiles to move in y direction
|
||||
local function move_qchunk(surface, x, y, x_offset, y_offset)
|
||||
local entities = surface.find_entities_filtered {area = {{x, y}, {x + 15, y + 15}}}
|
||||
local old_tiles = surface.find_tiles_filtered {area = {{x, y}, {x + 16, y + 16}}}
|
||||
local new_tiles = {}
|
||||
local player_positions = {}
|
||||
for index, tile in pairs(old_tiles) do
|
||||
local old_pos = tile.position
|
||||
new_tiles[index] = {name = tile.name, position = {x = old_pos.x + x_offset, y = old_pos.y + y_offset}}
|
||||
end
|
||||
|
||||
for index, entity in pairs(entities) do
|
||||
local old_pos = entity.position
|
||||
|
||||
local success,
|
||||
e =
|
||||
pcall(
|
||||
function()
|
||||
entity.teleport {old_pos.x + x_offset, old_pos.y + y_offset}
|
||||
end
|
||||
)
|
||||
if not success then --I will remove this after the beta :)
|
||||
game.print('PLEASE TELL VALANSCH OR WE WILL ALL DIE: ')
|
||||
game.print(entity.name)
|
||||
game.print(entity.type)
|
||||
|
||||
log(entity.name)
|
||||
log(entity.type)
|
||||
log('error in create entity ' .. tostring(e))
|
||||
end
|
||||
end
|
||||
surface.set_tiles(new_tiles)
|
||||
erase_qchunk(surface, x, y)
|
||||
for player_index, position in pairs(player_positions) do
|
||||
Game.get_player_by_index(player_index).teleport(position)
|
||||
end
|
||||
end
|
||||
|
||||
--- Moves the tetrimino in a supplied direction
|
||||
-- @param self LuaTetrimino
|
||||
-- @param x_direction number (-1, 0 or 1)
|
||||
-- @param y_direction number (-1, 0 or 1)
|
||||
-- @return boolean success
|
||||
function Tetrimino.move(self, x_direction, y_direction)
|
||||
local surface = self.surface
|
||||
local position = self.position
|
||||
local collision_box = self.collision_box
|
||||
if collides(self, collision_box, x_direction, y_direction) then
|
||||
return false
|
||||
end
|
||||
local tetri_x = position.x
|
||||
local tetri_y = position.y
|
||||
if y_direction == 1 then
|
||||
for y = 4, 1, -1 do
|
||||
for x = 1, 4 do
|
||||
if collision_box[y] and collision_box[y][x] == 1 then
|
||||
Queue.push(move_queue, {surface = surface, x = tetri_x + (x - 1) * 16, y = tetri_y + (y - 1) * 16, x_offset = 0, y_offset = 16})
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif x_direction ~= 0 then --east or west
|
||||
for x = 2.5 + 1.5 * x_direction, 2.5 - 1.5 * x_direction, -x_direction do --go from 1 to 4 or from 4 to 1
|
||||
for y = 4, 1, -1 do
|
||||
if collision_box[y] and collision_box[y][x] == 1 then
|
||||
Queue.push(
|
||||
move_queue,
|
||||
{surface = surface, x = tetri_x + (x - 1) * 16, y = tetri_y + (y - 1) * 16, x_offset = x_direction * 16, y_offset = 0}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
position.y = tetri_y + 16 * y_direction
|
||||
position.x = tetri_x + 16 * x_direction
|
||||
Task.set_timeout_in_ticks(1, worker)
|
||||
return true
|
||||
end
|
||||
|
||||
--- Do nothing. Literally.
|
||||
function Tetrimino.noop()
|
||||
end
|
||||
|
||||
--- Returns a rotated version of a supplied collision box by 90° in mathematically positive direction
|
||||
-- @param collision_box LuaTetriminoCollisionBox
|
||||
-- @param[opt=false] reverse boolean rotate in mathematically negative direction?
|
||||
-- @treturn LuaTetriminoCollisionBox the rotated collision box
|
||||
local function rotate_collision_box(collision_box, reverse)
|
||||
local new_collision_box = {{}, {}, {}, {}}
|
||||
local transformation = {{}, {}, {}, {}}
|
||||
if reverse then
|
||||
for y = 1, 4 do
|
||||
for x = 1, 4 do
|
||||
new_collision_box[y][x] = collision_box[5 - x][y]
|
||||
transformation[5 - x][y] = {x = x, y = y}
|
||||
end
|
||||
end
|
||||
else
|
||||
for y = 1, 4 do
|
||||
for x = 1, 4 do
|
||||
new_collision_box[y][x] = collision_box[x][5 - y]
|
||||
transformation[x][5 - y] = {x = x, y = y}
|
||||
end
|
||||
end
|
||||
end
|
||||
return new_collision_box, transformation
|
||||
end
|
||||
|
||||
--- Returns the collision box positions of the occupied qchunks in the tetris collision box
|
||||
-- @param self LuaTetrimino
|
||||
-- @return table of LuaPosition
|
||||
function Tetrimino.active_qchunks(self)
|
||||
local collision_box = self.collision_box
|
||||
local result = {nil, nil, nil, nil}
|
||||
for x = 1, 4 do
|
||||
for y = 1, 4 do
|
||||
if collision_box[y][x] == 1 then
|
||||
insert(result, {x = x, y = y})
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--- Rotates a tetrimino, if it doesnt collide
|
||||
-- @param self LuaTetrimino
|
||||
-- @param[opt=false] reverse boolean rotate in mathmatically negative direction?
|
||||
-- @return boolean success
|
||||
function Tetrimino.rotate(self, reverse)
|
||||
local new_collision_box, transformation = rotate_collision_box(self.collision_box, reverse)
|
||||
if collides(self, new_collision_box, 0, 0) then
|
||||
return false
|
||||
end
|
||||
|
||||
if self.number == 2 then
|
||||
game.print("You are a smart motherfucker, that's right.")
|
||||
end
|
||||
|
||||
local old_collision_box = self.collision_box
|
||||
local surface = self.surface
|
||||
local find_tiles_filtered = surface.find_tiles_filtered
|
||||
local find_entities_filtered = surface.find_entities_filtered
|
||||
local tetri_x = self.position.x
|
||||
local tetri_y = self.position.y
|
||||
local insert = insert -- luacheck: ignore 431 (intentional upvalue shadow)
|
||||
local tiles = {}
|
||||
local entities = {}
|
||||
for x = 1, 4 do
|
||||
for y = 1, 4 do
|
||||
local target = transformation[y][x]
|
||||
if
|
||||
(target.x ~= x or target.y ~= y) and --Do not rotate identity
|
||||
old_collision_box[y][x] == 1
|
||||
then --check for existence
|
||||
local top_left_x = tetri_x + x * 16 - 16
|
||||
local top_left_y = tetri_y + y * 16 - 16
|
||||
|
||||
for _, tile in pairs(find_tiles_filtered {area = {{top_left_x, top_left_y}, {tetri_x + x * 16, tetri_y + y * 16}}}) do
|
||||
insert(tiles, {name = tile.name, position = {tile.position.x + (target.x - x) * 16, tile.position.y + (target.y - y) * 16}})
|
||||
end
|
||||
|
||||
for _, entity in pairs(find_entities_filtered {area = {{top_left_x, top_left_y}, {tetri_x + x * 16 - 1, tetri_y + y * 16 - 1}}}) do
|
||||
entity.teleport {entity.position.x + (target.x - x) * 16, entity.position.y + (target.y - y) * 16}
|
||||
end
|
||||
if new_collision_box[y][x] == 0 then
|
||||
erase_qchunk(surface, top_left_x, top_left_y)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, e in pairs(entities) do
|
||||
surface.create_entity(e)
|
||||
end
|
||||
|
||||
surface.set_tiles(tiles)
|
||||
self.collision_box = new_collision_box
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function get_next_tetri_number()
|
||||
local head = sequence._head
|
||||
if head > 7 then
|
||||
table.shuffle_table(sequence)
|
||||
head = 1
|
||||
sequence._head = 0
|
||||
end
|
||||
sequence._head = head + 1
|
||||
return sequence[head]
|
||||
end
|
||||
|
||||
--- Constructs a new tetri and places it on the map
|
||||
-- @param surface LuaSurface the surface the tetri will be placed on
|
||||
-- @param position LuaPosition the position the tetri will be placed at. Legal values for x and y must be divisable by 16
|
||||
-- @return LuaTetrimino @see LuaTetrimino
|
||||
function Tetrimino.new(surface, position)
|
||||
local number = get_next_tetri_number()
|
||||
local self = {}
|
||||
self.number = number
|
||||
self.position = {x = position.x - 32, y = position.y - 32}
|
||||
self.surface = surface
|
||||
self.collision_box = collision_boxes[number]
|
||||
|
||||
Map.spawn_tetri(surface, position, number)
|
||||
return self
|
||||
end
|
||||
|
||||
--Works on one quad chunk in the move_queue
|
||||
worker =
|
||||
Token.register(
|
||||
function()
|
||||
local quad = Queue.pop(move_queue)
|
||||
if quad then
|
||||
Task.set_timeout_in_ticks(1, worker)
|
||||
local surface = quad.surface
|
||||
local x = quad.x
|
||||
local y = quad.y
|
||||
local x_offset = quad.x_offset
|
||||
local y_offset = quad.y_offset
|
||||
move_qchunk(surface, x, y, x_offset, y_offset)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
--- This module requires a map module as input.
|
||||
--- @param map_input table containing the function field spawn_tetri(surface, position, number)
|
||||
return function(map_input)
|
||||
Map = map_input
|
||||
return Tetrimino
|
||||
end
|
318
map_gen/combined/tetris/view.lua
Normal file
318
map_gen/combined/tetris/view.lua
Normal file
@ -0,0 +1,318 @@
|
||||
local Module = {}
|
||||
|
||||
local Gui = require 'utils.gui'
|
||||
local Event = require 'utils.event'
|
||||
local Game = require 'utils.game'
|
||||
local Global = require 'utils.global'
|
||||
|
||||
local main_button_name = Gui.uid_name()
|
||||
local main_frame_name = Gui.uid_name()
|
||||
|
||||
local uids = {
|
||||
['ccw_button'] = Gui.uid_name(),
|
||||
['noop_button'] = Gui.uid_name(),
|
||||
['cw_button'] = Gui.uid_name(),
|
||||
['left_button'] = Gui.uid_name(),
|
||||
['down_button'] = Gui.uid_name(),
|
||||
['right_button'] = Gui.uid_name(),
|
||||
['pause_button'] = Gui.uid_name(),
|
||||
['points_label'] = Gui.uid_name()
|
||||
}
|
||||
|
||||
local button_pretty_names = {
|
||||
[uids.ccw_button] = 'Rotate counter clockwise',
|
||||
[uids.noop_button] = 'Do nothing',
|
||||
[uids.cw_button] = 'Rotate clockwise',
|
||||
[uids.left_button] = 'Move left',
|
||||
[uids.down_button] = 'Move down',
|
||||
[uids.right_button] = 'Move right',
|
||||
[uids.pause_button] = 'Pause'
|
||||
}
|
||||
|
||||
local sprites = {
|
||||
[uids.ccw_button] = 'utility/reset',
|
||||
[uids.noop_button] = 'utility/clear',
|
||||
[uids.cw_button] = 'utility/reset',
|
||||
[uids.left_button] = 'utility/left_arrow',
|
||||
[uids.down_button] = 'utility/speed_down',
|
||||
[uids.right_button] = 'utility/right_arrow',
|
||||
[uids.pause_button] = 'utility/pause'
|
||||
}
|
||||
|
||||
Module.button_enum = uids
|
||||
Module.pretty_names = button_pretty_names
|
||||
|
||||
local primitives = {
|
||||
buttons_enabled = false,
|
||||
points = 0,
|
||||
progress = 0,
|
||||
last_move = nil
|
||||
}
|
||||
local vote_players = {
|
||||
[uids.ccw_button] = {},
|
||||
[uids.noop_button] = {},
|
||||
[uids.cw_button] = {},
|
||||
[uids.left_button] = {},
|
||||
[uids.down_button] = {},
|
||||
[uids.right_button] = {},
|
||||
[uids.pause_button] = {}
|
||||
}
|
||||
local vote_numbers = {
|
||||
[uids.ccw_button] = 0,
|
||||
[uids.noop_button] = 0,
|
||||
[uids.cw_button] = 0,
|
||||
[uids.left_button] = 0,
|
||||
[uids.down_button] = 0,
|
||||
[uids.right_button] = 0,
|
||||
[uids.pause_button] = 0
|
||||
}
|
||||
Global.register(
|
||||
{
|
||||
primitives = primitives,
|
||||
vote_players = vote_players,
|
||||
vote_numbers = vote_numbers
|
||||
},
|
||||
function(tbl)
|
||||
primitives = tbl.primitives
|
||||
vote_players = tbl.vote_players
|
||||
vote_numbers = tbl.vote_numbers
|
||||
end
|
||||
)
|
||||
|
||||
local function button_tooltip(button_id)
|
||||
local tooltip = ''
|
||||
local non_zero = false
|
||||
local players = vote_players[button_id]
|
||||
if not players then
|
||||
return button_pretty_names[button_id]
|
||||
end
|
||||
for _, p_name in pairs(vote_players[button_id]) do
|
||||
non_zero = true
|
||||
tooltip = string.format('%s, %s', p_name, tooltip) --If you have a better solution please tell me. Lol.
|
||||
end
|
||||
if non_zero then
|
||||
return string.format('%s: %s', button_pretty_names[button_id], tooltip:sub(1, -3))
|
||||
end
|
||||
return button_pretty_names[button_id]
|
||||
end
|
||||
|
||||
local function button_enabled(button_id, player_id)
|
||||
return (not vote_players[button_id]) or primitives.buttons_enabled and (not vote_players[button_id][player_id])
|
||||
end
|
||||
|
||||
local function add_sprite_button(element, player, name)
|
||||
return element.add {
|
||||
type = 'sprite-button',
|
||||
name = name,
|
||||
enabled = button_enabled(name, player.index),
|
||||
tooltip = button_tooltip(name),
|
||||
number = vote_numbers[name],
|
||||
sprite = sprites[name]
|
||||
}
|
||||
end
|
||||
|
||||
local function toggle(player)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
local left = player.gui.left
|
||||
local main_frame = left[main_frame_name]
|
||||
|
||||
if main_frame and main_frame.valid then
|
||||
Gui.destroy(main_frame)
|
||||
else
|
||||
main_frame =
|
||||
left.add {
|
||||
type = 'frame',
|
||||
name = main_frame_name,
|
||||
direction = 'vertical',
|
||||
caption = 'Tetris'
|
||||
}
|
||||
main_frame.style.width = 250
|
||||
main_frame.add {
|
||||
type = 'label',
|
||||
caption = 'Vote on the next move!'
|
||||
}
|
||||
|
||||
local upper_b_f = main_frame.add {type = 'flow', direction = 'horizontal'}
|
||||
local lower_b_f = main_frame.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local vote_buttons = {
|
||||
[uids.ccw_button] = add_sprite_button(upper_b_f, player, uids.ccw_button),
|
||||
[uids.noop_button] = add_sprite_button(upper_b_f, player, uids.noop_button),
|
||||
[uids.cw_button] = add_sprite_button(upper_b_f, player, uids.cw_button),
|
||||
[uids.left_button] = add_sprite_button(lower_b_f, player, uids.left_button),
|
||||
[uids.down_button] = add_sprite_button(lower_b_f, player, uids.down_button),
|
||||
[uids.right_button] = add_sprite_button(lower_b_f, player, uids.right_button),
|
||||
[uids.pause_button] = add_sprite_button(main_frame, player, uids.pause_button)
|
||||
}
|
||||
|
||||
local progress_bar = main_frame.add {type = 'progressbar', value = primitives.progress}
|
||||
|
||||
local points_f = main_frame.add {type = 'flow', direction = 'horizontal'}
|
||||
points_f.add {
|
||||
type = 'label',
|
||||
caption = 'Points: '
|
||||
}
|
||||
|
||||
local points =
|
||||
points_f.add {
|
||||
type = 'label',
|
||||
caption = primitives.points
|
||||
}
|
||||
main_frame.add {
|
||||
type = 'label',
|
||||
caption = 'Last move:'
|
||||
}
|
||||
|
||||
local last_move_tooltip = 'Do nothing'
|
||||
local last_move_sprite = nil
|
||||
local last_move_button = primitives.last_move
|
||||
if last_move_button then
|
||||
last_move_tooltip = button_pretty_names[last_move_button]
|
||||
last_move_sprite = sprites[last_move_button]
|
||||
end
|
||||
main_frame.add {
|
||||
type = 'sprite-button',
|
||||
enabled = false,
|
||||
tooltip = last_move_tooltip,
|
||||
sprite = last_move_sprite
|
||||
}
|
||||
|
||||
local data = {
|
||||
vote_buttons = vote_buttons,
|
||||
points = points,
|
||||
progress_bar = progress_bar
|
||||
}
|
||||
Gui.set_data(main_frame, data)
|
||||
end
|
||||
end
|
||||
|
||||
local function player_joined(event)
|
||||
local player = Game.get_player_by_index(event.player_index)
|
||||
|
||||
if player.gui.top[main_button_name] ~= nil then
|
||||
return
|
||||
end
|
||||
|
||||
player.gui.top.add {name = main_button_name, type = 'sprite-button', sprite = 'utility/force_editor_icon'}
|
||||
toggle(Game.get_player_by_index(event.player_index))
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
main_button_name,
|
||||
function(event)
|
||||
toggle(event.player)
|
||||
end
|
||||
)
|
||||
|
||||
function Module.bind_button(button_uid, handler)
|
||||
Gui.on_click(
|
||||
button_uid,
|
||||
function(event)
|
||||
handler(event.player)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
--- Sets the total game points to the given number
|
||||
-- @param points number
|
||||
function Module.set_points(points)
|
||||
primitives.points = points
|
||||
for _, player in pairs(game.players) do
|
||||
local mf = player.gui.left[main_frame_name]
|
||||
if mf then
|
||||
local data = Gui.get_data(mf)
|
||||
if data then
|
||||
data['points'].caption = points
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets the number displayed next to a button
|
||||
-- @param button_id string the buttons uid
|
||||
-- @param number number then number that will be displayed
|
||||
function Module.set_vote_number(button_id, number)
|
||||
vote_numbers[button_id] = number
|
||||
end
|
||||
|
||||
--- Adds a players name to the tooltip of a button and (if applicable) removes the name from another button
|
||||
-- @description Also disabled the selected button an (if applicable) enables the second
|
||||
-- @param player LuaPlayer
|
||||
-- @param vote_button_id string the uid of the button that the players name will be added to
|
||||
-- @param[opt] old_vote_button_id string the uid of the button that the players name will be removed from
|
||||
function Module.set_player_vote(player, vote_button_id, old_vote_button_id)
|
||||
local mf = player.gui.left[main_frame_name]
|
||||
if mf then
|
||||
local vote_buttons = Gui.get_data(mf).vote_buttons
|
||||
if vote_button_id then
|
||||
vote_buttons[vote_button_id].enabled = false
|
||||
vote_players[vote_button_id][player.index] = player.name
|
||||
end
|
||||
|
||||
if old_vote_button_id then
|
||||
vote_buttons[old_vote_button_id].enabled = true
|
||||
vote_players[old_vote_button_id][player.index] = nil
|
||||
end
|
||||
end
|
||||
for _, p in pairs(game.players) do
|
||||
toggle(p)
|
||||
toggle(p)
|
||||
end
|
||||
end
|
||||
|
||||
--- enables or disables the vote buttons
|
||||
-- @param enable boolean true if the vote buttons should be enabled, false if not
|
||||
function Module.enable_vote_buttons(enable)
|
||||
primitives.buttons_enabled = enable
|
||||
for _, player in pairs(game.players) do
|
||||
local mf = player.gui.left[main_frame_name]
|
||||
if mf then
|
||||
local data = Gui.get_data(mf)
|
||||
if data then
|
||||
local buttons = data.vote_buttons
|
||||
for _, button in pairs(buttons) do
|
||||
button.enabled = button_enabled(button.name, player.index)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets the last move
|
||||
-- @param button_id string the button id of the last move
|
||||
function Module.set_last_move(button_id)
|
||||
primitives.last_move = button_id
|
||||
end
|
||||
|
||||
--- Resets all poll buttons back to default
|
||||
function Module.reset_poll_buttons()
|
||||
for key, _ in pairs(vote_players) do
|
||||
vote_players[key] = {}
|
||||
vote_numbers[key] = 0
|
||||
end
|
||||
for _, player in pairs(game.players) do
|
||||
toggle(player)
|
||||
toggle(player)
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets progressbar denoting the time left until the current vote is finished
|
||||
-- @param progress number between 0 and 1
|
||||
function Module.set_progress(progress)
|
||||
primitives.progress = progress
|
||||
for _, player in pairs(game.players) do
|
||||
local mf = player.gui.left[main_frame_name]
|
||||
if mf then
|
||||
local data = Gui.get_data(mf)
|
||||
if data then
|
||||
data.progress_bar.value = progress
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, player_joined)
|
||||
|
||||
return Module
|
@ -1,364 +1 @@
|
||||
-- Map by grilledham & Jayefuu
|
||||
|
||||
-- Set scenario generation cliffs to none.
|
||||
-- Load blueprint from scenarios\RedMew\map_gen\data\presets\tetris\
|
||||
-- Obtain items using silent commands from scenarios\RedMew\map_gen\data\presets\tetris\
|
||||
-- Place the blueprint on the island south of spawn
|
||||
-- Teleport to centre of island and run the second command in tetris_theme_items_command.txt
|
||||
-- Excellent tetris themed music generated from midi files, credit to mgabor of miditorio.com
|
||||
|
||||
local b = require 'map_gen.shared.builders'
|
||||
local math = require 'utils.math'
|
||||
local degrees = math.rad
|
||||
local ore_seed1 = 7000
|
||||
local ore_seed2 = ore_seed1 * 2
|
||||
|
||||
local Random = require 'map_gen.shared.random'
|
||||
local random = Random.new(ore_seed1, ore_seed2)
|
||||
|
||||
local function value(base, mult, pow)
|
||||
return function(x, y)
|
||||
local d_sq = x * x + y * y
|
||||
return base + mult * d_sq ^ (pow / 2) -- d ^ pow
|
||||
end
|
||||
end
|
||||
|
||||
-- Removes vanilla resources when called
|
||||
local function no_resources(x, y, world, tile)
|
||||
for _, e in ipairs(
|
||||
world.surface.find_entities_filtered(
|
||||
{type = 'resource', area = {{world.x, world.y}, {world.x + 1, world.y + 1}}}
|
||||
)
|
||||
) do
|
||||
e.destroy()
|
||||
end
|
||||
return tile
|
||||
end
|
||||
|
||||
local names = {
|
||||
'biter-spawner',
|
||||
'spitter-spawner'
|
||||
}
|
||||
|
||||
-- removes spawners when called
|
||||
local function no_spawners(x, y, world, tile)
|
||||
for _, e in ipairs(
|
||||
world.surface.find_entities_filtered(
|
||||
{force = 'enemy', name = names, position = {world.x, world.y}}
|
||||
)
|
||||
) do
|
||||
e.destroy()
|
||||
end
|
||||
return tile
|
||||
end
|
||||
|
||||
local m_t_width = 12 -- map size in number of tiles
|
||||
local t_width = 16 -- tile width
|
||||
local t_h_width = t_width / 2
|
||||
|
||||
-- https://wiki.factorio.com/Data.raw#tile for the tile types you can send to this function
|
||||
local function two_tone_square(inner, outer) -- r_tile is a bool flag to show if it should have chance of resources on it
|
||||
local outer_tile = b.any {b.rectangle(t_width, t_width)}
|
||||
outer_tile = b.change_tile(outer_tile, true, outer)
|
||||
local inner_tile = b.any {b.rectangle(t_width - 2, t_width - 2)}
|
||||
inner_tile = b.change_tile(inner_tile, true, inner)
|
||||
local land_tile = b.any {inner_tile, outer_tile}
|
||||
|
||||
return land_tile
|
||||
end
|
||||
|
||||
local tet_bounds = b.rectangle(t_width * 4)
|
||||
tet_bounds = b.translate(tet_bounds, t_width, t_width)
|
||||
local function tetrify(pattern, block)
|
||||
for r = 1, 4 do
|
||||
local row = pattern[r]
|
||||
for c = 1, 4 do
|
||||
if row[c] == 1 then
|
||||
row[c] = block
|
||||
else
|
||||
row[c] = b.empty_shape()
|
||||
end
|
||||
end
|
||||
end
|
||||
local grid = b.grid_pattern(pattern, 4, 4, t_width, t_width)
|
||||
grid = b.translate(grid, -t_width / 2, -t_width / 2)
|
||||
grid = b.choose(tet_bounds, grid, b.empty_shape)
|
||||
grid = b.translate(grid, -t_width, -t_width)
|
||||
--grid = b.translate(grid, -t_width, t_width * 2)
|
||||
return grid
|
||||
end
|
||||
|
||||
local tet_O =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
two_tone_square('dirt-7', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_I =
|
||||
tetrify(
|
||||
{
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0}
|
||||
},
|
||||
two_tone_square('grass-2', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_J =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 1, 1, 0}
|
||||
},
|
||||
two_tone_square('grass-1', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_L =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 1, 1, 0}
|
||||
},
|
||||
two_tone_square('dirt-4', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_S =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{1, 1, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
two_tone_square('grass-4', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_Z =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{1, 1, 0, 0},
|
||||
{0, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
two_tone_square('grass-3', 'sand-1')
|
||||
)
|
||||
|
||||
local tet_T =
|
||||
tetrify(
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{1, 1, 1, 0},
|
||||
{0, 0, 0, 0}
|
||||
},
|
||||
two_tone_square('red-desert-2', 'sand-1')
|
||||
)
|
||||
|
||||
local tetriminos = {tet_I, tet_O, tet_T, tet_S, tet_Z, tet_J, tet_L}
|
||||
local tetriminos_count = #tetriminos
|
||||
|
||||
local quarter = math.tau / 4
|
||||
|
||||
local p_cols = 1 --m_t_width / 4
|
||||
local p_rows = 50
|
||||
local pattern = {}
|
||||
|
||||
for _ = 1, p_rows do
|
||||
local row = {}
|
||||
table.insert(pattern, row)
|
||||
for _ = 1, p_cols do
|
||||
--m_t_width
|
||||
--t_width
|
||||
-- map_width = m_t_width*t_width
|
||||
local i = random:next_int(1, tetriminos_count * 1.5)
|
||||
local shape = tetriminos[i] or b.empty_shape
|
||||
|
||||
local angle = random:next_int(0, 3) * quarter
|
||||
shape = b.rotate(shape, angle)
|
||||
|
||||
--local y_offset = random:next_int(-2, 2) * t_width
|
||||
local x_offset = random:next_int(-10, 8) * t_width
|
||||
shape = b.translate(shape, x_offset, 0)
|
||||
|
||||
table.insert(row, shape)
|
||||
end
|
||||
end
|
||||
|
||||
local tetriminos_shape = b.grid_pattern(pattern, p_cols, p_rows, t_width * 24, t_width * 4)
|
||||
tetriminos_shape = b.translate(tetriminos_shape, t_width, -t_width)
|
||||
|
||||
local ore_shape = b.rectangle(t_width * 0.8)
|
||||
local oil_shape = b.throttle_world_xy(ore_shape, 1, 4, 1, 4)
|
||||
|
||||
local ores = {
|
||||
{b.resource(ore_shape, 'iron-ore', value(250, 0.75, 1.15)), 10},
|
||||
{b.resource(ore_shape, 'copper-ore', value(200, 0.75, 1.15)), 6},
|
||||
{b.resource(ore_shape, 'stone', value(350, 0.4, 1.075)), 3},
|
||||
{b.resource(ore_shape, 'coal', value(200, 0.8, 1.075)), 5},
|
||||
{b.resource(b.scale(ore_shape, 0.5), 'uranium-ore', value(300, 0.3, 1.05)), 2},
|
||||
{b.resource(oil_shape, 'crude-oil', value(120000, 50, 1.15)), 1},
|
||||
{b.empty_shape, 100}
|
||||
}
|
||||
|
||||
local total_weights = {}
|
||||
local t = 0
|
||||
for _, v in pairs(ores) do
|
||||
t = t + v[2]
|
||||
table.insert(total_weights, t)
|
||||
end
|
||||
|
||||
p_cols = 50
|
||||
p_rows = 50
|
||||
|
||||
pattern = {}
|
||||
|
||||
for _ = 1, p_rows do
|
||||
local row = {}
|
||||
table.insert(pattern, row)
|
||||
for _ = 1, p_cols do
|
||||
local i = random:next_int(1, t)
|
||||
|
||||
local index = table.binary_search(total_weights, i)
|
||||
if (index < 0) then
|
||||
index = bit32.bnot(index)
|
||||
end
|
||||
|
||||
local shape = ores[index][1]
|
||||
table.insert(row, shape)
|
||||
end
|
||||
end
|
||||
|
||||
local worm_names = {
|
||||
'small-worm-turret',
|
||||
'medium-worm-turret',
|
||||
'big-worm-turret'
|
||||
}
|
||||
|
||||
local max_worm_chance = 1 / 128
|
||||
local worm_chance_factor = 1 / (192 * 512)
|
||||
|
||||
local function worms(_, _, world)
|
||||
local wx, wy = world.x, world.y
|
||||
local d = math.sqrt(wx * wx + wy * wy)
|
||||
|
||||
local worm_chance = d - 128
|
||||
|
||||
if worm_chance > 0 then
|
||||
worm_chance = worm_chance * worm_chance_factor
|
||||
worm_chance = math.min(worm_chance, max_worm_chance)
|
||||
|
||||
if math.random() < worm_chance then
|
||||
if d < 256 then
|
||||
return {name = 'small-worm-turret'}
|
||||
else
|
||||
local max_lvl
|
||||
local min_lvl
|
||||
if d < 512 then
|
||||
max_lvl = 2
|
||||
min_lvl = 1
|
||||
else
|
||||
max_lvl = 3
|
||||
min_lvl = 2
|
||||
end
|
||||
local lvl = math.random() ^ (512 / d) * max_lvl
|
||||
lvl = math.ceil(lvl)
|
||||
lvl = math.clamp(lvl, min_lvl, 3)
|
||||
return {name = worm_names[lvl]}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Starting area
|
||||
local start_patch = b.rectangle(t_width * 0.8)
|
||||
local start_iron_patch =
|
||||
b.resource(
|
||||
b.translate(start_patch, -t_width/2, -t_width/2),
|
||||
'iron-ore',
|
||||
function()
|
||||
return 1500
|
||||
end
|
||||
)
|
||||
local start_copper_patch =
|
||||
b.resource(
|
||||
b.translate(start_patch, t_width/2, -t_width/2),
|
||||
'copper-ore',
|
||||
function()
|
||||
return 1200
|
||||
end
|
||||
)
|
||||
local start_stone_patch =
|
||||
b.resource(
|
||||
b.translate(start_patch, t_width/2, t_width/2),
|
||||
'stone',
|
||||
function()
|
||||
return 900
|
||||
end
|
||||
)
|
||||
local start_coal_patch =
|
||||
b.resource(
|
||||
b.translate(start_patch, -t_width/2, t_width/2),
|
||||
'coal',
|
||||
function()
|
||||
return 1350
|
||||
end
|
||||
)
|
||||
local start_resources = b.any({start_iron_patch, start_copper_patch, start_stone_patch, start_coal_patch})
|
||||
local tet_O_start = b.apply_entity(tet_O, start_resources)
|
||||
|
||||
local starting_area = b.any{
|
||||
b.translate(tet_I,t_width,-t_width*2),
|
||||
b.translate(tet_O_start,t_width*2,-t_width),
|
||||
b.translate(tet_T,-t_width,-t_width),
|
||||
b.translate(tet_Z,-t_width*6,-t_width),
|
||||
b.translate(tet_L,-t_width*8,-t_width*2)
|
||||
}
|
||||
|
||||
tetriminos_shape = b.any{tetriminos_shape, starting_area}
|
||||
ores = b.grid_pattern_overlap(pattern, p_cols, p_rows, t_width, t_width)
|
||||
ores = b.translate(ores, t_h_width, t_h_width)
|
||||
|
||||
tetriminos_shape = b.apply_entity(tetriminos_shape, ores) -- add ores to tetriminoes
|
||||
tetriminos_shape = b.apply_effect(tetriminos_shape, no_spawners) -- remove spawners to help pathing
|
||||
tetriminos_shape = b.apply_entity(tetriminos_shape, worms) -- add worms
|
||||
|
||||
local water_tile = two_tone_square('water', 'deepwater')
|
||||
local half_sea_width = m_t_width * t_width - t_width
|
||||
local function sea_bounds(x, y)
|
||||
return x > -half_sea_width and x < half_sea_width and y < 0
|
||||
end
|
||||
|
||||
local sea = b.single_grid_pattern(water_tile, t_width, t_width)
|
||||
sea = b.translate(sea, t_h_width, -t_h_width)
|
||||
sea = b.choose(sea_bounds, sea, b.empty_shape)
|
||||
|
||||
local map = b.choose(sea_bounds, tetriminos_shape, b.empty_shape)
|
||||
map = b.if_else(map, sea)
|
||||
|
||||
local half_border_width = half_sea_width + t_width
|
||||
local function border_bounds(x, y)
|
||||
return x > -half_border_width and x < half_border_width and y < t_width
|
||||
end
|
||||
|
||||
border_bounds = b.subtract(border_bounds, sea_bounds)
|
||||
local border = b.change_tile(border_bounds, true, 'sand-1')
|
||||
map = b.add(map, border)
|
||||
local music_island = b.translate(b.rotate(tet_I,degrees(90)),0, 2*t_width)
|
||||
map = b.add(map,music_island)
|
||||
map = b.translate(map, 0, -t_width / 2)
|
||||
|
||||
|
||||
map = b.apply_effect(map, no_resources)
|
||||
|
||||
return map
|
||||
return require 'map_gen.combined.tetris.control'
|
||||
|
@ -25,8 +25,9 @@ function Queue.pop(queue)
|
||||
local element = queue[index]
|
||||
queue[index] = nil
|
||||
|
||||
queue._tail = index - 1
|
||||
|
||||
if element then
|
||||
queue._tail = index - 1
|
||||
end
|
||||
return element
|
||||
end
|
||||
|
||||
|
118
utils/state_machine.lua
Normal file
118
utils/state_machine.lua
Normal file
@ -0,0 +1,118 @@
|
||||
--- This module provides a classical mealy/moore state machine.
|
||||
-- Each machine in constructed by calling new()
|
||||
-- States and Transitions are lazily added to the machine as transition handlers and state tick handlers are registered.
|
||||
-- However the state machine must be fully defined after init is done. Dynamic machine changes are currently unsupported
|
||||
-- An example usage can be found here: map_gen\combined\tetris\control.lua
|
||||
|
||||
local Module = {}
|
||||
|
||||
local Debug = require 'utils.debug'
|
||||
|
||||
local in_state_callbacks = {}
|
||||
local transaction_callbacks = {}
|
||||
local max_stack_depth = 20
|
||||
local machine_count = 0
|
||||
|
||||
--- Transitions the supplied machine into a given state and executes all transaction_callbacks
|
||||
-- @param self StateMachine
|
||||
-- @param new_state number/string The new state to transition to
|
||||
function Module.transition(self, new_state)
|
||||
Debug.print(string.format('Transitioning from state %d to state %d.', self.state, new_state))
|
||||
local old_state = self.state
|
||||
|
||||
local stack_depth = self.stack_depth
|
||||
self.stack_depth = stack_depth + 1
|
||||
if stack_depth > max_stack_depth then
|
||||
if _DEBUG then
|
||||
error('[WARNING] Stack overflow at:' .. debug.traceback())
|
||||
else
|
||||
log('[WARNING] Stack overflow at:' .. debug.traceback())
|
||||
end
|
||||
end
|
||||
|
||||
local exit_callbacks = transaction_callbacks[self.id][old_state]
|
||||
if exit_callbacks then
|
||||
local entry_callbacks = exit_callbacks[new_state]
|
||||
if entry_callbacks then
|
||||
for i = 1, #entry_callbacks do
|
||||
local callback = entry_callbacks[i]
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.state = new_state
|
||||
end
|
||||
|
||||
--- Is this machine in this state?
|
||||
-- @param self StateMachine
|
||||
-- @param state number/string
|
||||
-- @return boolean
|
||||
function Module.in_state(self, state)
|
||||
return self.state == state
|
||||
end
|
||||
|
||||
--- Invoke a machine tick. Will execute all in_state_callbacks of the given machine
|
||||
-- @param self StateMachine the machine, whose handlers will be invoked
|
||||
function Module.machine_tick(self)
|
||||
local callbacks = in_state_callbacks[self.id][self.state]
|
||||
if callbacks then
|
||||
for i=1, #callbacks do
|
||||
local callback = callbacks[i]
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
end
|
||||
self.stack_depth = 0
|
||||
end
|
||||
|
||||
--- Register a handler that will be invoked by StateMachine.machine_tick
|
||||
-- You may register multiple handlers for the same transition
|
||||
-- NOTICE: This function will invoke an error if called after init. Dynamic machine changes are currently unsupported
|
||||
-- @param self StateMachine the machine
|
||||
-- @param state number/string The state, that the machine will be in, when callback is invoked
|
||||
-- @param callback function
|
||||
function Module.register_state_tick_callback(self, state, callback)
|
||||
if game then
|
||||
error('StateMachine.register_state_tick_callback after on_init() is unsupported due to desyncs.', 2)
|
||||
end
|
||||
in_state_callbacks[self.id][state] = in_state_callbacks[self.id][state] or {}
|
||||
table.insert(in_state_callbacks[self.id][state], callback)
|
||||
end
|
||||
|
||||
--- Register a handler that will be invoked by StateMachine.transition
|
||||
-- You may register multiple handlers for the same transition
|
||||
-- NOTICE: This function will invoke an error if called after init. Dynamic machine changes are currently unsupported
|
||||
-- @param self StateMachine the machine
|
||||
-- @param state number/string exiting state
|
||||
-- @param state number/string entering state
|
||||
-- @param callback function
|
||||
function Module.register_transition_callback(self, old, new, callback)
|
||||
if game then
|
||||
error('StateMachine.register_transition after on_init() is unsupported due to desyncs.', 2)
|
||||
end
|
||||
transaction_callbacks[self.id][old] = transaction_callbacks[self.id][old] or {}
|
||||
transaction_callbacks[self.id][old][new] = transaction_callbacks[self.id][old][new] or {}
|
||||
table.insert(transaction_callbacks[self.id][old][new], callback)
|
||||
end
|
||||
|
||||
--- Constructs a new state machine
|
||||
-- @param init_state number/string The starting state of the machine
|
||||
-- @return StateMachine The constructed state machine object
|
||||
function Module.new(init_state)
|
||||
if game then
|
||||
error('StateMachine.register_transition after on_init() is unsupported due to desyncs.', 2)
|
||||
end
|
||||
machine_count = machine_count + 1
|
||||
in_state_callbacks[machine_count] = {}
|
||||
transaction_callbacks[machine_count] = {}
|
||||
return {
|
||||
state = init_state,
|
||||
stack_depth = 0,
|
||||
id = machine_count,
|
||||
}
|
||||
end
|
||||
|
||||
return Module
|
Loading…
x
Reference in New Issue
Block a user