2021-12-21 15:35:16 -08:00
|
|
|
|
local S = minetest.get_translator("lzr_levels")
|
|
|
|
|
|
2021-12-21 10:56:56 -08:00
|
|
|
|
lzr_levels = {}
|
|
|
|
|
|
2022-03-04 11:30:18 -08:00
|
|
|
|
local CEILING_NODE = "lzr_core:wood"
|
|
|
|
|
local WALL_NODE = "lzr_core:wood"
|
2022-01-08 18:04:13 -08:00
|
|
|
|
local WINDOW_NODE = "lzr_decor:woodframed_glass"
|
2022-02-11 03:57:55 -08:00
|
|
|
|
local FLOOR_NODE = "lzr_core:wood"
|
2021-12-21 10:56:56 -08:00
|
|
|
|
local WINDOW_HEIGHT = 3
|
|
|
|
|
local WINDOW_DIST = 3
|
|
|
|
|
|
2021-12-28 14:43:01 -08:00
|
|
|
|
local current_level = nil
|
|
|
|
|
|
2022-01-09 08:43:59 -08:00
|
|
|
|
local level_data = {}
|
2022-01-30 10:45:42 -08:00
|
|
|
|
lzr_levels.LAST_LEVEL = 0
|
2022-01-09 08:43:59 -08:00
|
|
|
|
|
2022-03-02 13:04:21 -08:00
|
|
|
|
local level_size = vector.copy(lzr_globals.DEFAULT_LEVEL_SIZE)
|
2022-03-02 07:40:13 -08:00
|
|
|
|
|
|
|
|
|
lzr_levels.get_level_size = function()
|
|
|
|
|
return level_size
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local set_level_size = function(new_size)
|
2022-03-02 13:04:21 -08:00
|
|
|
|
level_size = vector.copy(new_size)
|
2022-03-02 07:40:13 -08:00
|
|
|
|
minetest.log("verbose", "[lzr_levels] Level size set to: "..minetest.pos_to_string(new_size))
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-04 13:23:07 -08:00
|
|
|
|
local get_max_treasures = function()
|
|
|
|
|
if current_level then
|
|
|
|
|
return level_data[current_level].treasures
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-01-16 07:58:35 -08:00
|
|
|
|
-- Mod storage for game progress
|
|
|
|
|
local mod_storage = minetest.get_mod_storage()
|
|
|
|
|
|
2022-02-03 09:32:43 -08:00
|
|
|
|
local flat_index_to_pos = function(index, size)
|
|
|
|
|
local d = index-1
|
|
|
|
|
local x = d % size.x
|
|
|
|
|
local y = math.floor((d / size.x) % size.y)
|
|
|
|
|
local z = math.floor((d / (size.x*size.y)) % size.z)
|
|
|
|
|
return vector.new(x,y,z)
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-06 07:24:20 -08:00
|
|
|
|
--[[ Read the level schematics to find out some metadata about them
|
|
|
|
|
and count the number of levels. A CSV file is used for metadata.
|
|
|
|
|
Syntax of level_data.cvs:
|
|
|
|
|
<File name>, <Title>, <Border nodes>, <Ambience>
|
|
|
|
|
|
|
|
|
|
Border nodes is a list of nodenames for the level border, separated by the pipe symbol (“|”), in this order:
|
|
|
|
|
wall, window, floor, ceiling
|
|
|
|
|
wall is mandatory, the rest is optional (will default to the wall node)
|
|
|
|
|
Ambience is an ambience ID for the background noise (see lzr_ambience).
|
|
|
|
|
]]
|
2022-01-09 08:43:59 -08:00
|
|
|
|
local analyze_levels = function()
|
2022-01-30 10:45:42 -08:00
|
|
|
|
local level_list_path = minetest.get_modpath("lzr_levels").."/data/level_data.csv"
|
|
|
|
|
local level_list_file = io.open(level_list_path, "r")
|
|
|
|
|
assert(level_list_file, "Could not load level_data.csv")
|
2022-03-02 13:04:21 -08:00
|
|
|
|
|
2022-01-30 10:45:42 -08:00
|
|
|
|
for line in level_list_file:lines() do
|
|
|
|
|
local matches = string.split(line, ",")
|
|
|
|
|
assert(matches ~= nil, "Malformed level_data.csv")
|
|
|
|
|
local filename = matches[1]
|
2022-02-10 18:50:48 -08:00
|
|
|
|
local lname = matches[2]
|
2022-03-04 11:30:18 -08:00
|
|
|
|
local nodes = matches[3]
|
2022-03-06 07:24:20 -08:00
|
|
|
|
local ambience = matches[4]
|
2022-03-04 11:30:18 -08:00
|
|
|
|
local node_matches = string.split(nodes, "|")
|
|
|
|
|
local node_wall = node_matches[1]
|
|
|
|
|
local node_window = node_matches[2] or node_wall
|
|
|
|
|
local node_floor = node_matches[3] or node_wall
|
|
|
|
|
local node_ceiling = node_matches[4] or node_wall
|
2022-03-06 07:24:20 -08:00
|
|
|
|
table.insert(level_data, {filename=filename, name=lname, node_wall=node_wall, node_window=node_window, node_floor=node_floor, node_ceiling=node_ceiling, ambience=ambience})
|
2022-01-30 10:45:42 -08:00
|
|
|
|
end
|
|
|
|
|
lzr_levels.LAST_LEVEL = #level_data
|
|
|
|
|
|
2022-01-09 08:43:59 -08:00
|
|
|
|
-- Mark levels that contain at least 1 rotatable block
|
2022-01-30 10:45:42 -08:00
|
|
|
|
for l=1, #level_data do
|
|
|
|
|
local filename = level_data[l].filename
|
|
|
|
|
local filepath = minetest.get_modpath("lzr_levels").."/schematics/"..filename
|
|
|
|
|
local schem = minetest.read_schematic(filepath, {write_yslice_prob="none"})
|
|
|
|
|
assert(schem, "Could not load level file: "..filename)
|
|
|
|
|
level_data[l].contains_rotatable_block = false
|
2022-03-04 13:23:07 -08:00
|
|
|
|
level_data[l].treasures = 0
|
2022-01-30 10:45:42 -08:00
|
|
|
|
level_data[l].size = schem.size
|
2022-02-03 09:32:43 -08:00
|
|
|
|
local size = level_data[l].size
|
2022-01-09 08:43:59 -08:00
|
|
|
|
for d=1, #schem.data do
|
|
|
|
|
local nodename = schem.data[d].name
|
|
|
|
|
local is_rotatable = minetest.get_item_group(nodename, "rotatable") == 1
|
2022-03-04 13:23:07 -08:00
|
|
|
|
local treasure = minetest.get_item_group(nodename, "chest_closed") > 0
|
2022-01-09 08:43:59 -08:00
|
|
|
|
if is_rotatable then
|
|
|
|
|
level_data[l].contains_rotatable_block = true
|
2022-02-03 09:32:43 -08:00
|
|
|
|
end
|
2022-03-04 13:23:07 -08:00
|
|
|
|
if treasure then
|
|
|
|
|
level_data[l].treasures = level_data[l].treasures + 1
|
|
|
|
|
end
|
2022-02-03 09:32:43 -08:00
|
|
|
|
if nodename == "lzr_teleporter:teleporter_off" then
|
|
|
|
|
local start = flat_index_to_pos(d, size)
|
|
|
|
|
start = vector.add(start, vector.new(0, 0.5, 0))
|
|
|
|
|
level_data[l].start_pos = start
|
2022-01-09 08:43:59 -08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-02 13:04:21 -08:00
|
|
|
|
-- Set the basic nodes of the room
|
|
|
|
|
local set_room_nodes = function(param)
|
2021-12-21 10:56:56 -08:00
|
|
|
|
local pos = param.pos
|
|
|
|
|
local psize = param.size
|
|
|
|
|
local posses_border = {}
|
|
|
|
|
local posses_window = {}
|
2022-02-11 03:57:55 -08:00
|
|
|
|
local posses_floor = {}
|
2022-03-04 11:30:18 -08:00
|
|
|
|
local posses_ceiling = {}
|
2021-12-21 10:56:56 -08:00
|
|
|
|
local size = vector.add(psize, {x=1,y=1,z=1})
|
2022-03-02 07:40:13 -08:00
|
|
|
|
set_level_size(psize)
|
2021-12-21 10:56:56 -08:00
|
|
|
|
for x=0,size.x do
|
|
|
|
|
for z=0,size.z do
|
|
|
|
|
for y=0,size.y do
|
|
|
|
|
local offset = {x=x-1, y=y-1, z=z-1}
|
2022-02-10 09:28:56 -08:00
|
|
|
|
if not ((x >= 1 and x < size.x) and
|
2021-12-21 10:56:56 -08:00
|
|
|
|
(y >= 1 and y < size.y) and
|
2022-02-10 09:28:56 -08:00
|
|
|
|
(z >= 1 and z < size.z)) then
|
|
|
|
|
if y == WINDOW_HEIGHT and ((x >= 1 and x < size.x and x % WINDOW_DIST == 0) or (z >= 1 and z < size.z and z % WINDOW_DIST == 0)) then
|
|
|
|
|
table.insert(posses_window, vector.add(pos, offset))
|
|
|
|
|
else
|
2022-02-11 03:57:55 -08:00
|
|
|
|
if y == 0 then
|
|
|
|
|
table.insert(posses_floor, vector.add(pos, offset))
|
2022-03-04 11:30:18 -08:00
|
|
|
|
elseif y == size.y then
|
|
|
|
|
table.insert(posses_ceiling, vector.add(pos, offset))
|
2022-02-11 03:57:55 -08:00
|
|
|
|
else
|
|
|
|
|
table.insert(posses_border, vector.add(pos, offset))
|
|
|
|
|
end
|
2022-02-10 09:28:56 -08:00
|
|
|
|
end
|
2021-12-21 10:56:56 -08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-03-04 11:30:18 -08:00
|
|
|
|
minetest.bulk_set_node(posses_floor, {name=param.nodes.node_floor})
|
|
|
|
|
minetest.bulk_set_node(posses_border, {name=param.nodes.node_wall})
|
|
|
|
|
minetest.bulk_set_node(posses_window, {name=param.nodes.node_window})
|
|
|
|
|
minetest.bulk_set_node(posses_ceiling, {name=param.nodes.node_ceiling})
|
2021-12-21 10:56:56 -08:00
|
|
|
|
end
|
|
|
|
|
|
2022-02-10 12:13:57 -08:00
|
|
|
|
local get_singleplayer = function()
|
|
|
|
|
return minetest.get_player_by_name("singleplayer")
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-21 10:56:56 -08:00
|
|
|
|
local emerge_callback = function(blockpos, action, calls_remaining, param)
|
|
|
|
|
minetest.log("verbose", "[lzr_levels] emerge_callback() ...")
|
|
|
|
|
if action == minetest.EMERGE_ERRORED then
|
|
|
|
|
minetest.log("error", "[lzr_levels] Room emerging error.")
|
|
|
|
|
elseif action == minetest.EMERGE_CANCELLED then
|
|
|
|
|
minetest.log("error", "[lzr_levels] Room emerging cancelled.")
|
|
|
|
|
elseif calls_remaining == 0 and (action == minetest.EMERGE_GENERATED or action == minetest.EMERGE_FROM_DISK or action == minetest.EMERGE_FROM_MEMORY) then
|
2022-02-10 09:28:56 -08:00
|
|
|
|
lzr_levels.clear_playfield(param.size)
|
2022-03-02 13:04:21 -08:00
|
|
|
|
set_room_nodes(param)
|
|
|
|
|
local level_ok = false
|
2022-02-05 05:08:39 -08:00
|
|
|
|
if param.level then
|
2022-03-02 13:04:21 -08:00
|
|
|
|
level_ok = lzr_levels.build_level(param.level)
|
|
|
|
|
elseif param.schematic then
|
|
|
|
|
level_ok = lzr_levels.build_level_raw(param.schematic)
|
|
|
|
|
else
|
|
|
|
|
minetest.log("action", "[lzr_levels] Empty room emerge callback done")
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not level_ok then
|
|
|
|
|
minetest.log("error", "[lzr_levels] Room emerge callback done with error")
|
|
|
|
|
else
|
|
|
|
|
local player = get_singleplayer()
|
|
|
|
|
if player then
|
|
|
|
|
if param.spawn_pos then
|
|
|
|
|
player:set_pos(param.spawn_pos)
|
|
|
|
|
end
|
|
|
|
|
if param.yaw then
|
|
|
|
|
player:set_look_horizontal(param.yaw)
|
|
|
|
|
player:set_look_vertical(0)
|
2022-02-10 15:04:51 -08:00
|
|
|
|
end
|
2022-03-04 13:23:07 -08:00
|
|
|
|
local gs = lzr_gamestate.get_state()
|
|
|
|
|
if gs == lzr_gamestate.LEVEL then
|
|
|
|
|
lzr_gui.update_treasure_status(player, 0, get_max_treasures())
|
|
|
|
|
end
|
|
|
|
|
if param.level then
|
|
|
|
|
lzr_messages.show_message(player, lzr_levels.get_level_name(param.level), 3)
|
|
|
|
|
minetest.sound_play({name = "lzr_levels_level_enter", gain = 1}, {to_player=player:get_player_name()}, true)
|
|
|
|
|
end
|
2022-03-02 13:04:21 -08:00
|
|
|
|
end
|
|
|
|
|
minetest.log("action", "[lzr_levels] Room emerge callback done")
|
2022-01-11 09:05:24 -08:00
|
|
|
|
end
|
2021-12-21 10:56:56 -08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-02 13:04:21 -08:00
|
|
|
|
local prepare_room = function(room_data)
|
|
|
|
|
minetest.emerge_area(room_data.pos, vector.add(room_data.pos, room_data.size), emerge_callback, room_data)
|
2021-12-21 10:56:56 -08:00
|
|
|
|
end
|
|
|
|
|
|
2022-02-10 09:28:56 -08:00
|
|
|
|
function lzr_levels.clear_playfield(room_size)
|
|
|
|
|
local posses_air = {}
|
|
|
|
|
local posses_water = {}
|
|
|
|
|
local size = lzr_globals.PLAYFIELD_SIZE
|
2022-02-11 03:53:28 -08:00
|
|
|
|
for z=0, size.z do
|
|
|
|
|
for y=0, size.y do
|
|
|
|
|
for x=0, size.x do
|
2022-02-10 09:28:56 -08:00
|
|
|
|
local pos = vector.new(x,y,z)
|
2022-02-11 03:53:28 -08:00
|
|
|
|
pos = vector.add(pos, lzr_globals.PLAYFIELD_START)
|
2022-02-11 04:01:11 -08:00
|
|
|
|
if pos.y <= lzr_globals.WATER_LEVEL and (x > room_size.x or y > room_size.y or z > room_size.z) then
|
2022-02-11 03:53:28 -08:00
|
|
|
|
table.insert(posses_water, pos)
|
2022-02-10 09:28:56 -08:00
|
|
|
|
else
|
2022-02-11 03:53:28 -08:00
|
|
|
|
table.insert(posses_air, pos)
|
2022-02-10 09:28:56 -08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
minetest.bulk_set_node(posses_water, {name="lzr_core:water_source"})
|
|
|
|
|
minetest.bulk_set_node(posses_air, {name="air"})
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-02 13:04:21 -08:00
|
|
|
|
-- room_data:
|
|
|
|
|
-- - pos: Room pos
|
|
|
|
|
-- - size: Room size vector
|
|
|
|
|
-- - spawn_pos: Relative player spawn position (optional)
|
|
|
|
|
-- - yaw: Initial player yaw (optional)
|
|
|
|
|
-- Either one of these (or none of them for empty room):
|
|
|
|
|
-- - level: level ID (for builtin level)
|
|
|
|
|
-- - schematic: Path to schematic
|
2022-03-04 11:30:18 -08:00
|
|
|
|
-- - nodes: Table containing:
|
|
|
|
|
-- - node_floor, node_ceiling, node_wall, node_window
|
2022-03-02 13:04:21 -08:00
|
|
|
|
function lzr_levels.build_room(room_data)
|
|
|
|
|
prepare_room(room_data)
|
2021-12-21 15:17:36 -08:00
|
|
|
|
end
|
|
|
|
|
|
2022-02-10 12:13:57 -08:00
|
|
|
|
function lzr_levels.prepare_and_build_level(level, spawn_pos, yaw)
|
2022-03-04 11:30:18 -08:00
|
|
|
|
local nodes = {
|
|
|
|
|
node_floor = level_data[level].node_floor,
|
|
|
|
|
node_wall = level_data[level].node_wall,
|
|
|
|
|
node_ceiling = level_data[level].node_ceiling,
|
|
|
|
|
node_window = level_data[level].node_window,
|
|
|
|
|
}
|
|
|
|
|
lzr_levels.build_room({pos=lzr_globals.LEVEL_POS, size=level_data[level].size, level=level, spawn_pos=spawn_pos, yaw=yaw, nodes=nodes})
|
2021-12-21 15:17:36 -08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-02 13:04:21 -08:00
|
|
|
|
function lzr_levels.prepare_and_build_custom_level(schematic, spawn_pos, yaw)
|
2022-03-04 11:30:18 -08:00
|
|
|
|
local nodes = {
|
|
|
|
|
node_floor = FLOOR_NODE,
|
|
|
|
|
node_wall = WALL_NODE,
|
|
|
|
|
node_ceiling = CEILING_NODE,
|
|
|
|
|
node_window = WINDOW_NODE,
|
|
|
|
|
}
|
|
|
|
|
lzr_levels.build_room({pos=lzr_globals.LEVEL_POS, size=schematic.size, schematic=schematic, spawn_pos=spawn_pos, yaw=yaw, nodes=nodes})
|
2022-03-02 13:04:21 -08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function lzr_levels.build_level_raw(schematic_specifier)
|
|
|
|
|
local schem = minetest.place_schematic(lzr_globals.LEVEL_POS, schematic_specifier, "0", {}, true, "")
|
2022-01-11 09:05:24 -08:00
|
|
|
|
if schem then
|
2022-01-11 09:22:42 -08:00
|
|
|
|
-- Propagate lasers and check for insta-win
|
2022-01-11 09:05:24 -08:00
|
|
|
|
lzr_laser.full_laser_update(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END)
|
2022-01-11 09:22:42 -08:00
|
|
|
|
local done = lzr_laser.check_level_won()
|
2022-02-10 09:31:08 -08:00
|
|
|
|
if done and lzr_gamestate.get_state() ~= lzr_gamestate.EDITOR then
|
2022-01-11 09:22:42 -08:00
|
|
|
|
lzr_levels.level_complete()
|
|
|
|
|
end
|
2022-01-11 09:05:24 -08:00
|
|
|
|
else
|
2022-03-02 13:04:21 -08:00
|
|
|
|
minetest.log("error", "[lzr_levels] lzr_levels.build_level_raw failed to build level")
|
2022-01-11 09:05:24 -08:00
|
|
|
|
end
|
|
|
|
|
return schem
|
2021-12-21 10:56:56 -08:00
|
|
|
|
end
|
2021-12-21 15:35:16 -08:00
|
|
|
|
|
2022-03-02 13:04:21 -08:00
|
|
|
|
function lzr_levels.build_level(level)
|
|
|
|
|
local filepath = minetest.get_modpath("lzr_levels").."/schematics/"..level_data[level].filename
|
|
|
|
|
local schem = lzr_levels.build_level_raw(filepath)
|
|
|
|
|
return schem
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-25 18:37:04 -08:00
|
|
|
|
local function clear_inventory(player)
|
2021-12-21 16:20:29 -08:00
|
|
|
|
local inv = player:get_inventory()
|
|
|
|
|
for i=1,inv:get_size("main") do
|
|
|
|
|
inv:set_stack("main", i, "")
|
|
|
|
|
end
|
2021-12-25 18:37:04 -08:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-09 08:43:59 -08:00
|
|
|
|
local function reset_inventory(player, needs_rotate)
|
2021-12-25 18:37:04 -08:00
|
|
|
|
clear_inventory(player)
|
2022-01-09 08:43:59 -08:00
|
|
|
|
if needs_rotate then
|
|
|
|
|
local inv = player:get_inventory()
|
|
|
|
|
inv:add_item("main", "screwdriver2:screwdriver")
|
|
|
|
|
end
|
2021-12-23 13:43:26 -08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function lzr_levels.start_level(level)
|
|
|
|
|
current_level = level
|
|
|
|
|
local player = get_singleplayer()
|
2022-02-03 09:46:58 -08:00
|
|
|
|
local start_pos -- player start position, relative to level
|
|
|
|
|
local size = level_data[level].size
|
2022-02-03 09:32:43 -08:00
|
|
|
|
if level_data[level].start_pos then
|
2022-02-03 09:46:58 -08:00
|
|
|
|
start_pos = level_data[level].start_pos
|
2022-02-03 09:32:43 -08:00
|
|
|
|
else
|
2022-02-03 09:46:58 -08:00
|
|
|
|
-- Fallback start pos
|
|
|
|
|
start_pos = vector.new(math.floor(size.x/2), -0.5, math.floor(size.z/2))
|
2022-02-03 09:32:43 -08:00
|
|
|
|
end
|
2022-02-03 09:46:58 -08:00
|
|
|
|
local spawn_pos = vector.add(lzr_globals.LEVEL_POS, start_pos) -- absolute spawn position
|
|
|
|
|
local yaw = 0
|
|
|
|
|
if start_pos.z > size.z/2 then
|
|
|
|
|
yaw = yaw + math.pi
|
|
|
|
|
end
|
2022-02-10 12:13:57 -08:00
|
|
|
|
lzr_levels.prepare_and_build_level(level, spawn_pos, yaw)
|
2022-01-09 08:43:59 -08:00
|
|
|
|
local needs_rotate = level_data[current_level].contains_rotatable_block
|
|
|
|
|
reset_inventory(player, needs_rotate)
|
2022-01-12 18:24:29 -08:00
|
|
|
|
if lzr_gamestate.get_state() ~= lzr_gamestate.EDITOR then
|
|
|
|
|
lzr_gamestate.set_state(lzr_gamestate.LEVEL)
|
|
|
|
|
end
|
2022-03-06 07:24:20 -08:00
|
|
|
|
lzr_ambience.set_ambience(level_data[level].ambience)
|
2022-01-20 05:37:09 -08:00
|
|
|
|
minetest.log("action", "[lzr_levels] Starting level "..level)
|
2022-01-09 06:21:47 -08:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-16 07:58:35 -08:00
|
|
|
|
function lzr_levels.clear_level_progress()
|
|
|
|
|
mod_storage:set_string("lzr_levels:levels", "")
|
2022-01-20 05:37:09 -08:00
|
|
|
|
minetest.log("action", "[lzr_levels] Level progress was cleared")
|
2022-01-16 07:58:35 -08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function lzr_levels.mark_level_as_complete(level)
|
|
|
|
|
local levels = minetest.deserialize(mod_storage:get_string("lzr_levels:levels"))
|
|
|
|
|
if not levels then
|
|
|
|
|
levels = {}
|
|
|
|
|
end
|
|
|
|
|
levels[level] = true
|
|
|
|
|
mod_storage:set_string("lzr_levels:levels", minetest.serialize(levels))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function lzr_levels.get_completed_levels()
|
|
|
|
|
local levels = minetest.deserialize(mod_storage:get_string("lzr_levels:levels"))
|
|
|
|
|
if not levels then
|
|
|
|
|
levels = {}
|
|
|
|
|
end
|
|
|
|
|
return levels
|
|
|
|
|
end
|
|
|
|
|
|
2022-01-09 06:21:47 -08:00
|
|
|
|
function lzr_levels.level_complete()
|
|
|
|
|
if lzr_gamestate.get_state() == lzr_gamestate.LEVEL_COMPLETE then
|
|
|
|
|
return false
|
|
|
|
|
end
|
2022-01-16 07:58:35 -08:00
|
|
|
|
lzr_levels.mark_level_as_complete(current_level)
|
2022-03-03 16:34:39 -08:00
|
|
|
|
|
|
|
|
|
-- Trigger chest treasure particle animation
|
|
|
|
|
local open_chests = minetest.find_nodes_in_area(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END, {"group:chest_open"})
|
|
|
|
|
for c=1, #open_chests do
|
|
|
|
|
local pos = open_chests[c]
|
|
|
|
|
local node = minetest.get_node(pos)
|
|
|
|
|
local def = minetest.registered_nodes[node.name]
|
|
|
|
|
if def._lzr_send_treasure then
|
|
|
|
|
def._lzr_send_treasure(pos, node)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-01-09 06:21:47 -08:00
|
|
|
|
local player = get_singleplayer()
|
2022-02-10 18:50:48 -08:00
|
|
|
|
lzr_messages.show_message(player, S("@1 complete!", lzr_levels.get_level_name(current_level)), 3)
|
2022-01-20 05:37:09 -08:00
|
|
|
|
minetest.log("action", "[lzr_levels] Level "..current_level.." completed")
|
2022-01-09 06:21:47 -08:00
|
|
|
|
minetest.sound_play({name = "lzr_levels_level_complete", gain = 1}, {to_player=player:get_player_name()}, true)
|
|
|
|
|
lzr_gamestate.set_state(lzr_gamestate.LEVEL_COMPLETE)
|
2022-01-09 08:00:52 -08:00
|
|
|
|
minetest.after(3, function(completed_level)
|
|
|
|
|
if lzr_gamestate.get_state() == lzr_gamestate.LEVEL_COMPLETE and current_level == completed_level then
|
2022-01-09 06:21:47 -08:00
|
|
|
|
lzr_levels.next_level()
|
|
|
|
|
end
|
2022-01-09 08:00:52 -08:00
|
|
|
|
end, current_level)
|
2021-12-23 13:43:26 -08:00
|
|
|
|
end
|
|
|
|
|
|
2021-12-21 15:35:16 -08:00
|
|
|
|
function lzr_levels.next_level()
|
2021-12-23 13:43:26 -08:00
|
|
|
|
local player = get_singleplayer()
|
2021-12-21 15:35:16 -08:00
|
|
|
|
current_level = current_level + 1
|
2022-01-12 18:48:13 -08:00
|
|
|
|
if current_level > lzr_levels.LAST_LEVEL then
|
2021-12-23 14:06:10 -08:00
|
|
|
|
lzr_messages.show_message(player, S("Final level completed!"), 5)
|
2022-02-10 12:31:04 -08:00
|
|
|
|
lzr_levels.leave_level()
|
2021-12-21 15:35:16 -08:00
|
|
|
|
else
|
2021-12-23 13:43:26 -08:00
|
|
|
|
lzr_levels.start_level(current_level)
|
2021-12-21 15:35:16 -08:00
|
|
|
|
end
|
|
|
|
|
end
|
2021-12-23 13:43:26 -08:00
|
|
|
|
|
2021-12-25 18:37:04 -08:00
|
|
|
|
function lzr_levels.leave_level()
|
|
|
|
|
local player = get_singleplayer()
|
|
|
|
|
current_level = nil
|
|
|
|
|
clear_inventory(player)
|
|
|
|
|
player:set_pos(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PLAYER_SPAWN_OFFSET))
|
2022-01-09 11:23:51 -08:00
|
|
|
|
player:set_look_horizontal(0)
|
2022-02-10 18:32:30 -08:00
|
|
|
|
player:set_look_vertical(0)
|
2022-01-08 18:53:55 -08:00
|
|
|
|
lzr_gamestate.set_state(lzr_gamestate.MENU)
|
2021-12-25 18:37:04 -08:00
|
|
|
|
end
|
|
|
|
|
|
2022-02-10 18:18:10 -08:00
|
|
|
|
function lzr_levels.get_current_level()
|
|
|
|
|
return current_level
|
|
|
|
|
end
|
|
|
|
|
|
2022-02-10 18:50:48 -08:00
|
|
|
|
function lzr_levels.get_level_name(level)
|
|
|
|
|
local name = level_data[level].name
|
|
|
|
|
if name then
|
|
|
|
|
return level_data[level].name
|
|
|
|
|
else
|
|
|
|
|
return S("Level @1", level)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-02-10 18:18:10 -08:00
|
|
|
|
function lzr_levels.restart_level()
|
|
|
|
|
local state = lzr_gamestate.get_state()
|
|
|
|
|
if state == lzr_gamestate.LEVEL or state == lzr_gamestate.EDITOR then
|
|
|
|
|
lzr_levels.start_level(current_level)
|
|
|
|
|
return true
|
|
|
|
|
else
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-04 13:23:07 -08:00
|
|
|
|
-- To be called when a treasure has been found (only in game mode LEVEL!)
|
|
|
|
|
function lzr_levels.found_treasure()
|
|
|
|
|
local treasures = lzr_laser.count_found_treasures(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END)
|
|
|
|
|
local player = get_singleplayer()
|
|
|
|
|
lzr_gui.update_treasure_status(player, treasures, get_max_treasures())
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-23 13:43:26 -08:00
|
|
|
|
minetest.register_chatcommand("restart", {
|
|
|
|
|
privs = {},
|
|
|
|
|
params = "",
|
|
|
|
|
description = S("Restart current level"),
|
|
|
|
|
func = function(name, param)
|
2022-01-20 05:30:34 -08:00
|
|
|
|
local state = lzr_gamestate.get_state()
|
|
|
|
|
if state == lzr_gamestate.LEVEL or state == lzr_gamestate.EDITOR then
|
2022-02-10 18:18:10 -08:00
|
|
|
|
lzr_levels.restart_level()
|
2021-12-25 18:37:04 -08:00
|
|
|
|
return true
|
2022-01-20 05:30:34 -08:00
|
|
|
|
elseif state == lzr_gamestate.LEVEL_COMPLETE then
|
|
|
|
|
return false, S("Can’t restart level right now.")
|
2021-12-25 18:37:04 -08:00
|
|
|
|
else
|
|
|
|
|
return false, S("Not playing in a level!")
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
minetest.register_chatcommand("leave", {
|
|
|
|
|
privs = {},
|
|
|
|
|
params = "",
|
|
|
|
|
description = S("Leave current level"),
|
|
|
|
|
func = function(name, param)
|
2022-01-09 08:00:52 -08:00
|
|
|
|
if lzr_gamestate.get_state() == lzr_gamestate.LEVEL or lzr_gamestate.get_state() == lzr_gamestate.LEVEL_COMPLETE then
|
2021-12-25 18:37:04 -08:00
|
|
|
|
lzr_levels.leave_level(current_level)
|
|
|
|
|
return true
|
|
|
|
|
else
|
|
|
|
|
return false, S("Not playing in a level!")
|
|
|
|
|
end
|
2021-12-23 13:43:26 -08:00
|
|
|
|
end,
|
|
|
|
|
})
|
2022-01-09 08:43:59 -08:00
|
|
|
|
|
2022-01-16 07:58:35 -08:00
|
|
|
|
minetest.register_chatcommand("reset_progress", {
|
|
|
|
|
privs = {},
|
|
|
|
|
params = "yes",
|
|
|
|
|
description = S("Reset level progress"),
|
|
|
|
|
func = function(name, param)
|
|
|
|
|
if param == "yes" then
|
|
|
|
|
lzr_levels.clear_level_progress()
|
|
|
|
|
return true, S("Level progress resetted.")
|
|
|
|
|
else
|
|
|
|
|
return false, S("To reset level progress, use “/reset_progress yes”")
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-01-12 16:40:16 -08:00
|
|
|
|
lzr_gamestate.register_on_enter_state(function(state)
|
|
|
|
|
if state == lzr_gamestate.LEVEL then
|
|
|
|
|
local player = minetest.get_player_by_name("singleplayer")
|
|
|
|
|
lzr_player.set_play_inventory(player)
|
|
|
|
|
lzr_gui.set_play_gui(player)
|
|
|
|
|
lzr_laser.full_laser_update(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
2022-01-09 08:43:59 -08:00
|
|
|
|
|
|
|
|
|
analyze_levels()
|