--luacheck: ignore --tetris by mewmew --18x10 gb local event = require 'utils.event' local bricks = require 'maps.tetris.bricks' local connect_belts = require 'utils.functions.connect_belts' local playfield_left_top = { x = -17, y = -18 } local playfield_width = 12 local playfield_height = 24 local playfield_area = { ['left_top'] = { x = playfield_left_top.x, y = playfield_left_top.y }, ['right_bottom'] = { x = playfield_left_top.x + playfield_width, y = playfield_left_top.y + playfield_height } } local playfield_width = math.abs(playfield_area.left_top.x - playfield_area.right_bottom.x) local playfield_height = math.abs(playfield_area.left_top.y - playfield_area.right_bottom.y) local move_translations = { ['iron-plate'] = { -1, 0 }, ['copper-plate'] = { 1, 0 } } local function coord_string(x, y) str = tostring(x) .. '_' str = str .. tostring(y) return str end local function draw_active_bricks(surface) if not storage.active_brick then return end if not storage.active_brick.entities then storage.active_brick.entities = {} end for k, e in pairs(storage.active_brick.entities) do if e.valid then storage.tetris_active_grid[coord_string(math.floor(e.position.x), math.floor(e.position.y))] = false end storage.active_brick.entities[k] = nil e.destroy() end for _, p in pairs(storage.active_brick.positions) do local e = surface.create_entity({ name = storage.active_brick.entity_name, position = p, create_build_effect_smoke = false }) --if bricks[storage.active_brick.type].fluid then -- e.fluidbox[1] = {name = bricks[storage.active_brick.type].fluid, amount = 100} --end storage.tetris_active_grid[coord_string(math.floor(e.position.x), math.floor(e.position.y))] = true storage.active_brick.entities[#storage.active_brick.entities + 1] = e end if storage.active_brick.entity_type == 'transport-belt' then connect_belts(storage.active_brick.entities) end end local function set_collision_grid(surface) for x = 0, playfield_width - 1, 1 do for y = 0, playfield_height - 1, 1 do local position = { x = playfield_area.left_top.x + x, y = playfield_area.left_top.y + y } storage.tetris_grid[coord_string(math.floor(position.x), math.floor(position.y))] = true end end local entities = surface.find_entities_filtered({ area = playfield_area, force = 'enemy' }) for _, e in pairs(entities) do if not storage.tetris_active_grid[coord_string(math.floor(e.position.x), math.floor(e.position.y))] then storage.tetris_grid[coord_string(math.floor(e.position.x), math.floor(e.position.y))] = false else --game.print(e.position) end end end local function rotate_brick(surface) if not storage.active_brick then return end local center_pos = storage.active_brick.positions[1] local vectors = bricks[storage.active_brick.type].vectors local new_direction = storage.active_brick.direction + 1 if new_direction > 4 then new_direction = 1 end local new_vectors = vectors[new_direction] for k, p in pairs(storage.active_brick.positions) do if not storage.tetris_grid[coord_string(math.floor(center_pos.x + new_vectors[k][1]), math.floor(center_pos.y + new_vectors[k][2]))] then return end end for k, p in pairs(storage.active_brick.positions) do storage.active_brick.positions[k] = { x = center_pos.x + new_vectors[k][1], y = center_pos.y + new_vectors[k][2] } end storage.active_brick.direction = new_direction end local function set_hotbar() for _, player in pairs(game.connected_players) do player.set_quick_bar_slot(1, 'iron-plate') player.set_quick_bar_slot(2, 'copper-plate') player.set_quick_bar_slot(9, 'iron-gear-wheel') player.set_quick_bar_slot(10, 'processing-unit') end end local function set_inventory() for _, player in pairs(game.connected_players) do for _, item in pairs({ 'iron-plate', 'copper-plate', 'iron-gear-wheel', 'processing-unit' }) do if player.get_main_inventory().get_item_count(item) == 0 then player.insert({ name = item, count = 1 }) end end end end local function draw_playfield(surface) for x = -1, playfield_width, 1 do for y = -1, playfield_height, 1 do local position = { x = playfield_area.left_top.x + x, y = playfield_area.left_top.y + y } --surface.set_tiles({{name = "deepwater", position = position}}) end end for x = 0, playfield_width - 1, 1 do for y = 0, playfield_height - 1, 1 do local position = { x = playfield_area.left_top.x + x, y = playfield_area.left_top.y + y } storage.tetris_grid[coord_string(math.floor(position.x), math.floor(position.y))] = true surface.set_tiles({ { name = 'tutorial-grid', position = position } }) end end end local function draw_score(surface) local score = 'Score: ' .. storage.score local high_score = 'Highscore: ' .. storage.high_score local level = 'Level: ' .. storage.level local cleared_lines = 'Lines: ' .. storage.cleared_lines if not storage.tetris_score_rendering then storage.tetris_score_rendering = {} rendering.draw_text { text = '#######################', surface = surface, target = { playfield_area.right_bottom.x + 1, playfield_area.left_top.y + 0.5 }, color = { r = 0.2, g = 0.0, b = 0.5 }, scale = 1, font = 'heading-1', --alignment = "center", scale_with_zoom = false } storage.tetris_score_rendering.score = rendering.draw_text { text = score, surface = surface, target = { playfield_area.right_bottom.x + 1, playfield_area.left_top.y + 1 }, color = { r = 0.98, g = 0.66, b = 0.22 }, scale = 2, font = 'heading-1', --alignment = "center", scale_with_zoom = false } rendering.draw_text { text = '#######################', surface = surface, target = { playfield_area.right_bottom.x + 1, playfield_area.left_top.y + 2.5 }, color = { r = 0.2, g = 0.0, b = 0.5 }, scale = 1, font = 'heading-1', --alignment = "center", scale_with_zoom = false } storage.tetris_score_rendering.high_score = rendering.draw_text { text = high_score, surface = surface, target = { playfield_area.right_bottom.x + 1, playfield_area.left_top.y + 3.3 }, color = { r = 0.98, g = 0.66, b = 0.22 }, scale = 1.15, font = 'heading-1', --alignment = "center", scale_with_zoom = false } storage.tetris_score_rendering.level = rendering.draw_text { text = level, surface = surface, target = { playfield_area.right_bottom.x + 1, playfield_area.left_top.y + 4.3 }, color = { r = 0.98, g = 0.66, b = 0.22 }, scale = 1.15, font = 'heading-1', --alignment = "center", scale_with_zoom = false } storage.tetris_score_rendering.cleared_lines = rendering.draw_text { text = cleared_lines, surface = surface, target = { playfield_area.right_bottom.x + 1, playfield_area.left_top.y + 5.3 }, color = { r = 0.98, g = 0.66, b = 0.22 }, scale = 1.15, font = 'heading-1', --alignment = "center", scale_with_zoom = false } end rendering.set_text(storage.tetris_score_rendering.score, score) rendering.set_text(storage.tetris_score_rendering.high_score, high_score) rendering.set_text(storage.tetris_score_rendering.level, level) rendering.set_text(storage.tetris_score_rendering.cleared_lines, cleared_lines) end local function add_score_points(amount) storage.score = storage.score + amount if storage.score > storage.high_score then storage.high_score = storage.score end end local function move_lines_down(surface, y) local entities = surface.find_entities_filtered({ area = { { playfield_area.left_top.x, playfield_area.left_top.y }, { playfield_area.left_top.x + playfield_width + 1, playfield_area.left_top.y + y + 1 } }, force = 'enemy' }) for _, e in pairs(entities) do if e.valid then e.clone { position = { e.position.x, e.position.y + 1 }, surface = surface, force = 'enemy' } e.destroy() end end end local function tetris(surface) local c = 0 local entity_lines_to_kill = {} for y = 0, playfield_height, 1 do local entities = surface.find_entities_filtered( { area = { { playfield_area.left_top.x, playfield_area.left_top.y + y }, { playfield_area.left_top.x + playfield_width + 1, playfield_area.left_top.y + y + 1 } }, force = 'enemy' } ) if #entities == playfield_width then entity_lines_to_kill[#entity_lines_to_kill + 1] = { entities = entities, y = y } c = c + 1 end end if c < 1 then return end storage.cleared_lines = storage.cleared_lines + c local name = 'explosion' if c > 2 then name = 'uranium-cannon-shell-explosion' end if c >= 4 then name = 'ground-explosion' end local score_y = false for _, line in pairs(entity_lines_to_kill) do for _, entity in pairs(line.entities) do if math.random(1, 2) == 1 then surface.create_entity({ name = name, position = entity.position, force = 'neutral' }) end entity.destroy() end move_lines_down(surface, line.y) if not score_y then score_y = line.y end end set_collision_grid(surface) local score_gain = (c * 100 * c) add_score_points(score_gain) rendering.draw_text { text = '+' .. score_gain, surface = surface, target = { playfield_area.left_top.x + playfield_width * 0.5, playfield_area.left_top.y + score_y }, color = { r = 0.22, g = 0.77, b = 0.22 }, scale = c, font = 'heading-1', time_to_live = 180, alignment = 'center', scale_with_zoom = false } end local function reset_score() storage.level = 0 storage.cleared_lines = 0 storage.score = 0 end local function reset_play_area(surface) local entities = surface.find_entities_filtered({ area = playfield_area, force = 'enemy' }) for _, e in pairs(entities) do if math.random(1, 6) == 1 then e.surface.create_entity({ name = 'big-artillery-explosion', position = e.position, force = 'neutral' }) end e.destroy() end set_collision_grid(surface) storage.last_reset = game.tick + 300 end local function set_difficulty() storage.move_down_delay = game.tick + (64 - (storage.level * 2)) local level = math.floor(storage.cleared_lines * 0.15) + 1 if storage.level == level then return end storage.level = level end local function new_brick(surface) if storage.active_brick then return end local r = math.random(1, 7) if math.random(1, 16) == 1 then r = math.random(8, #bricks) end --local r = 12 local brick = bricks[r] local x_modifier = -1 + math.random(0, 2) local spawn_position = { x = playfield_area.left_top.x + playfield_width * 0.5 + x_modifier, y = playfield_area.left_top.y + brick.spawn_y_modifier - 1 } if not storage.tetris_grid[coord_string(math.floor(spawn_position.x), math.floor(spawn_position.y))] then reset_play_area(surface) reset_score() set_difficulty() draw_score(surface) end if game.tick < storage.last_reset then rendering.draw_text { text = 'Round begins in.. ' .. math.abs(math.floor((game.tick - storage.last_reset) / 60)), surface = surface, target = { playfield_area.left_top.x + playfield_width * 0.5, playfield_area.left_top.y + playfield_height * 0.5 }, color = { r = 0.98, g = 0.66, b = 0.22 }, scale = 3, font = 'heading-1', time_to_live = 60, alignment = 'center', scale_with_zoom = false } return end storage.active_brick = {} storage.active_brick.direction = 1 storage.active_brick.type = r storage.active_brick.positions = {} storage.active_brick.entity_name = brick.entity_name storage.active_brick.entity_type = brick.entity_type for k, v in pairs(brick.vectors[1]) do storage.active_brick.positions[k] = { x = spawn_position.x + v[1], y = spawn_position.y + v[2] } end end local function move_down(surface) if not storage.active_brick then return end if storage.move_down_delay > game.tick then return end for k, p in pairs(storage.active_brick.positions) do if not storage.tetris_grid[coord_string(math.floor(storage.active_brick.positions[k].x), math.floor(storage.active_brick.positions[k].y + 1))] then for k, p in pairs(storage.active_brick.positions) do storage.tetris_grid[coord_string(math.floor(p.x), math.floor(p.y))] = false storage.tetris_active_grid[coord_string(math.floor(p.x), math.floor(p.y))] = false end storage.active_brick = nil tetris(surface) add_score_points(5) draw_score(surface) new_brick(surface) return end end for k, p in pairs(storage.active_brick.positions) do storage.active_brick.positions[k] = { x = storage.active_brick.positions[k].x, y = storage.active_brick.positions[k].y + 1 } end set_difficulty() return true end local function move(surface, item) if not storage.active_brick then return end if item == 'iron-gear-wheel' then rotate_brick(surface) return end if item == 'processing-unit' then for c = 1, 4, 1 do storage.move_down_delay = 0 local b = move_down(surface) draw_active_bricks(surface) if not b then return end end end if not move_translations[item] then return end for k, p in pairs(storage.active_brick.positions) do if not storage.tetris_grid[coord_string(math.floor(storage.active_brick.positions[k].x + move_translations[item][1]), math.floor(storage.active_brick.positions[k].y + move_translations[item][2]))] then return end end for k, p in pairs(storage.active_brick.positions) do storage.active_brick.positions[k] = { x = storage.active_brick.positions[k].x + move_translations[item][1], y = storage.active_brick.positions[k].y + move_translations[item][2] } end end local function on_player_cursor_stack_changed(event) local player = game.players[event.player_index] --game.print(game.tick) -- game.print(player.cursor_stack) if not player.cursor_stack then return end if not player.cursor_stack.valid_for_read then return end if not player.cursor_stack.name then return end local item = player.cursor_stack.name move(player.surface, item) player.cursor_stack.clear() player.surface.spill_item_stack(player.position, { name = item, count = 1 }, true) if item == 'iron-plate' then player.surface.create_entity({ name = 'flying-text', position = player.position, text = '<', color = player.color }) end if item == 'copper-plate' then player.surface.create_entity({ name = 'flying-text', position = player.position, text = '>', color = player.color }) end if item == 'processing-unit' then player.surface.create_entity({ name = 'flying-text', position = player.position, text = '▼', color = player.color }) end if item == 'iron-gear-wheel' then player.surface.create_entity({ name = 'flying-text', position = player.position, text = '8', color = player.color }) end end local function on_player_joined_game(event) local player = game.players[event.player_index] set_hotbar() set_inventory() player.surface.daytime = 0.22 player.surface.freeze_daytime = 1 player.print('Use 1,2, 9, 0 on your keyboard to control the bricks.', { r = 0.98, g = 0.66, b = 0.22 }) end local function on_chunk_generated(event) local surface = event.surface for _, e in pairs(surface.find_entities_filtered({ area = event.area, force = { 'neutral', 'enemy' } })) do e.destroy() end for _, t in pairs(surface.find_tiles_filtered({ area = event.area })) do --surface.set_tiles({{name = "tutorial-grid", position = t.position}}) if t.position.y < 4 and t.position.y > -4 and t.position.x < 4 and t.position.x > -4 then surface.set_tiles({ { name = 'sand-1', position = t.position } }) else surface.set_tiles({ { name = 'out-of-map', position = t.position } }) end end surface.destroy_decoratives { area = event.area } if event.area.left_top.x == 128 and event.area.left_top.y == 128 then draw_playfield(surface) end end local function on_init(event) storage.tetris_grid = {} storage.tetris_active_grid = {} storage.high_score = 0 storage.last_reset = 120 storage.cleared_lines = 0 storage.level = 0 end local function tick() local surface = game.surfaces[1] if game.tick % 4 == 0 then draw_active_bricks(surface) move_down(surface) end if game.tick % 60 == 0 then new_brick(surface) end end event.on_nth_tick(4, tick) event.on_init(on_init) event.add(defines.events.on_player_cursor_stack_changed, on_player_cursor_stack_changed) event.add(defines.events.on_player_joined_game, on_player_joined_game) event.add(defines.events.on_chunk_generated, on_chunk_generated)