2024-12-24 14:56:57 +01:00

1063 lines
36 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local S = minetest.get_translator("lzr_menu")
local FS = function(...) return minetest.formspec_escape(S(...)) end
local SPEAKER_POS = vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_SPEAKER_OFFSET)
local COLOR_PICKER_MODIFIERS = { -32, -8, -1, 1, 8, 32 }
local MENU_LASER_ALPHA = "BF"
lzr_menu = {}
lzr_menu.SHIP_SIZE = nil
local registered_on_ship_builts = {}
-- Register callback function that is called when the ship has been built
-- and is ready for play. Will be called only once for the current game session.
function lzr_menu.register_on_ship_built(func)
table.insert(registered_on_ship_builts, func)
end
local registered_on_ship_rebuilts = {}
-- Register callback function that is called when the ship has been built
-- OR rebuilt (after an update).
function lzr_menu.register_on_ship_rebuilt(func)
table.insert(registered_on_ship_rebuilts, func)
end
local registered_on_player_ship_enters = {}
-- Register callback function that is called when the player enters the ship.
function lzr_menu.register_on_player_ship_enter(func)
table.insert(registered_on_player_ship_enters, func)
end
local place_gold_stash = function(gold_blocks)
if gold_blocks <= 0 then
return
end
local min_pos = vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_STASH_OFFSET)
local max_pos = vector.add(min_pos, lzr_globals.MENU_SHIP_STASH_SIZE)
local pos = table.copy(min_pos)
local posses = {}
for g=0, gold_blocks-1 do
if pos.z > max_pos.z then
break
end
local gpos = table.copy(pos)
if g % 4 == 1 then
gpos.x = gpos.x + 1
elseif g % 4 == 2 then
gpos.z = gpos.z + 1
elseif g % 4 == 3 then
gpos.x = gpos.x + 1
gpos.z = gpos.z + 1
pos.y = pos.y + 1
if pos.y >= max_pos.y then
pos.y = min_pos.y
pos.x = pos.x + 3
if pos.x > max_pos.x then
pos.x = min_pos.x
pos.z = pos.z + 3
end
end
end
table.insert(posses, gpos)
end
minetest.bulk_set_node(posses, {name="lzr_treasure:gold_block"})
minetest.log("action", "[lzr_menu] Placed "..gold_blocks.." gold block(s) in ship treasure stash")
end
local ship_emerged = false
local gold_stashed = 0
local build_ship = function()
minetest.place_schematic(lzr_globals.MENU_SHIP_POS, minetest.get_modpath("lzr_menu").."/schematics/lzr_menu_ship.mts", "0", {}, true, "")
minetest.set_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_STARTBOOK_OFFSET), {name="lzr_menu:level_starter", param2=0})
minetest.set_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_CUSTOMBOOK_OFFSET), {name="lzr_menu:custom_level_starter", param2=0})
minetest.set_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_EDITOR_OFFSET), {name="lzr_menu:editor_starter", param2=1})
minetest.set_node(SPEAKER_POS, {name="lzr_menu:speaker", param2=2})
minetest.set_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_TELEVISION_OFFSET), {name="lzr_menu:television", param2=2})
minetest.set_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_HOWTO_BOOKSHELF_OFFSET), {name="lzr_menu:bookshelf_info", param2=1})
local core_level_data = lzr_levels.get_level_pack("__core")
if lzr_levels.are_all_levels_completed(core_level_data) then
lzr_menu.place_painting("perfect_plunderer")
end
-- Call 'rebuilt' callbacks
for f=1, #registered_on_ship_rebuilts do
registered_on_ship_rebuilts[f]()
end
end
lzr_menu.update_treasure_stash = function(gold_blocks)
if gold_stashed == gold_blocks then
return
end
if ship_emerged then
if gold_stashed > gold_blocks then
build_ship()
end
place_gold_stash(gold_blocks)
gold_stashed = gold_blocks
else
gold_stashed = gold_blocks
end
end
lzr_levels.register_on_collected_treasure(function(treasures)
lzr_menu.update_treasure_stash(treasures)
end)
local emerge_callback = function(blockpos, action, calls_remaining, param)
minetest.log("verbose", "[lzr_menu] emerge_callback() ...")
if action == minetest.EMERGE_ERRORED then
minetest.log("error", "[lzr_menu] Emerging error.")
elseif action == minetest.EMERGE_CANCELLED then
minetest.log("error", "[lzr_menu] Emerging cancelled.")
elseif calls_remaining == 0 and (action == minetest.EMERGE_GENERATED or action == minetest.EMERGE_FROM_DISK or action == minetest.EMERGE_FROM_MEMORY) then
ship_emerged = true
build_ship()
minetest.log("action", "[lzr_menu] Ship emerged and built.")
place_gold_stash(gold_stashed)
-- Now that the ship has *actually* been built,
-- setting the player pos should guarantee the player
-- stands on solid ground
local player = minetest.get_player_by_name("singleplayer")
lzr_menu.teleport_player_to_ship(player, "captain")
-- Call 'built' callbacks
for f=1, #registered_on_ship_builts do
registered_on_ship_builts[f]()
end
-- Call 'built' callbacks
for f=1, #registered_on_ship_builts do
registered_on_ship_builts[f]()
end
end
end
local SPEAKER_NOTE_INTERVAL = 1.0
local emerge_ship = function(pos)
local ship_schem = minetest.read_schematic(minetest.get_modpath("lzr_menu").."/schematics/lzr_menu_ship.mts", {write_yslice_prob="none"})
if not ship_schem then
minetest.log("error", "[lzr_menu] Could not read ship schematic!")
return
end
lzr_menu.SHIP_SIZE = ship_schem.size
minetest.emerge_area(pos, vector.add(pos, lzr_menu.SHIP_SIZE), emerge_callback)
end
minetest.register_on_joinplayer(function(player)
emerge_ship(lzr_globals.MENU_SHIP_POS)
-- NOTE: This teleport happens BEFORE the ship was actually built, so the
-- player may fall for a moment. This is thus not reliable
-- and is only made in prepatation for the future ship spawn.
lzr_menu.teleport_player_to_ship(player, "captain")
player:set_look_horizontal(0)
player:set_look_vertical(0)
local inv = player:get_inventory()
for i=1,inv:get_size("main") do
inv:set_stack("main", i, "")
end
end)
local on_punch_start_core = function(self, node, puncher)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
lzr_level_select.open_dialog(puncher, "core")
end
local on_rightclick_start_core = function(self, node, clicker)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
lzr_level_select.open_dialog(clicker, "core")
end
local on_punch_start_custom = function(self, node, puncher)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
lzr_level_select.open_dialog(puncher, "custom")
end
local on_rightclick_start_custom = function(self, node, clicker)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
lzr_level_select.open_dialog(clicker, "custom")
end
-- A node that starts level selection
minetest.register_node("lzr_menu:level_starter", {
--~ A node that starts level selection
description = S("Level Starter"),
-- symbolized by a map
tiles = { "lzr_menu_map.png", "blank.png" },
paramtype = "light",
paramtype2 = "4dir",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, -0.5, 0.5, -63/128, 0.5 },
}
},
use_texture_alpha = "clip",
walkable = false,
sunlight_propagates = true,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Start playing"))
end,
on_punch = on_punch_start_core,
on_rightclick = on_rightclick_start_core,
groups = { snappy = 3, not_in_creative_inventory = 1, },
})
-- A node that starts custom level selection
minetest.register_node("lzr_menu:custom_level_starter", {
--~ A node that starts custom level selection
description = S("Custom Level Starter"),
-- symbolized by a map
tiles = { "lzr_menu_map_custom.png", "blank.png" },
paramtype = "light",
paramtype2 = "4dir",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, -0.5, 0.5, -63/128, 0.5 },
}
},
use_texture_alpha = "clip",
walkable = false,
sunlight_propagates = true,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Play custom levels"))
end,
on_punch = on_punch_start_custom,
on_rightclick = on_rightclick_start_custom,
groups = { snappy = 3, not_in_creative_inventory = 1, },
})
local on_punch_editor = function(self, node, puncher)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
lzr_editor.enter_editor(puncher)
end
local on_rightclick_editor = function(self, node, clicker)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
lzr_editor.enter_editor(clicker)
end
minetest.register_node("lzr_menu:editor_starter", {
description = S("Level Editor Starter"),
-- The level editor starter is represented by a saw
tiles = { "lzr_menu_saw.png", "blank.png" },
paramtype = "light",
paramtype2 = "4dir",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, -0.5, 0.5, -63/128, 0.5 },
}
},
use_texture_alpha = "clip",
walkable = false,
sunlight_propagates = true,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Enter the level editor"))
end,
on_punch = on_punch_editor,
on_rightclick = on_rightclick_editor,
groups = { breakable = 1, rotatable = 1, not_in_creative_inventory = 1, },
})
local use_speaker = function(speaker_pos, user)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
local state = lzr_ambience.toggle_ambience_by_player(user, true)
if state then
minetest.sound_play({name="lzr_menu_speaker_turn_on", gain=0.4}, {pos=speaker_pos}, true)
else
minetest.sound_play({name="lzr_menu_speaker_turn_off", gain=0.4}, {pos=speaker_pos}, true)
end
end
local update_speaker_infotext = function(pos)
if minetest.get_node(pos).name ~= "lzr_menu:speaker" then
return
end
local meta = minetest.get_meta(pos)
local infotext = S("Toggle music") .. "\n\n"
local active = lzr_ambience.is_active()
local ambience_state = meta:get_int("ambience_state")
-- Update infotext only if neccessary
if active and ambience_state ~= 1 then
infotext = infotext .. S("(Music is enabled)")
meta:set_int("ambience_state", 1)
meta:set_string("infotext", infotext)
elseif not active and ambience_state ~= 2 then
infotext = infotext .. S("(Music is disabled)")
meta:set_int("ambience_state", 2)
meta:set_string("infotext", infotext)
end
end
local on_punch_speaker = function(pos, node, puncher)
use_speaker(pos, puncher)
update_speaker_infotext(pos)
end
local on_rightclick_speaker = function(pos, node, clicker)
use_speaker(pos, clicker)
update_speaker_infotext(pos)
end
minetest.register_node("lzr_menu:speaker", {
--~ Loudspeaker
description = S("Speaker"),
tiles = {
"lzr_menu_speaker_top.png",
"lzr_menu_speaker_side.png",
"lzr_menu_speaker_side.png",
"lzr_menu_speaker_side.png",
"lzr_menu_speaker_back.png",
"lzr_menu_speaker_front.png",
},
paramtype2 = "4dir",
groups = { breakable = 1, rotatable = 1, not_in_creative_inventory = 1},
sounds = lzr_sounds.node_sound_wood_defaults(),
is_ground_content = false,
on_punch = on_punch_speaker,
on_rightclick = on_rightclick_speaker,
on_construct = function(pos)
update_speaker_infotext(pos)
local timer = minetest.get_node_timer(pos)
timer:start(SPEAKER_NOTE_INTERVAL)
end,
on_timer = function(pos)
if lzr_ambience.is_active() then
local node = minetest.get_node(pos)
local dir = minetest.fourdir_to_dir(node.param2)
local notes = {
"lzr_menu_speaker_note_quarter.png",
"lzr_menu_speaker_note_eighth.png",
"lzr_menu_speaker_note_eighth_2.png",
}
local note = notes[math.random(1, #notes)]
local hue = math.random(-12, 12) * 15
note = note .. "^[hsl:"..hue..":0:0"
local notepos = vector.add(pos, vector.multiply(dir, -0.65))
notepos = vector.offset(notepos, 0, -2/16, 0)
minetest.add_particlespawner({
amount = 1,
time = SPEAKER_NOTE_INTERVAL / 2,
pos = notepos,
texture = {
name = note,
alpha_tween = { start = 0.6, 1, 0 },
},
size = 3,
exptime = 1,
vel = {
min = vector.new(0, 0.2, 0),
max = vector.new(0, 0.6, 0),
},
})
end
update_speaker_infotext(pos)
local timer = minetest.get_node_timer(pos)
timer:start(SPEAKER_NOTE_INTERVAL)
end,
})
-- Instantly update the ship speaker infotext when ambience status
-- was changed (could be by chat command)
lzr_ambience.register_on_ambience_change(function(state)
update_speaker_infotext(SPEAKER_POS)
end)
local graphics_settings_initial = {
opaque_lasers = minetest.settings:get_bool("lzr_opaque_lasers", false),
patterned_lasers = minetest.settings:get_bool("lzr_patterned_lasers", false),
}
for c=1, #lzr_globals.COLOR_NAMES do
local colorname = lzr_globals.COLOR_NAMES[c]
local setting = minetest.settings:get("lzr_laser_color_"..colorname)
if not setting or setting == "" then
setting = lzr_laser.DEFAULT_LASER_COLORS[c]
end
graphics_settings_initial["color_"..colorname] = setting
end
local graphics_settings_state = table.copy(graphics_settings_initial)
local color_descriptions = {
-- [laser color] = { <color adjective>, <laser color button tooltip>, <long description> }
[lzr_globals.COLOR_RED] = { S("red"), S("“red” lasers"), S("Adjust the replacement color for “red” lasers") },
[lzr_globals.COLOR_GREEN] = { S("green"), S("“green” lasers"), S("Adjust the replacement color for “green” lasers") },
[lzr_globals.COLOR_BLUE] = { S("blue"), S("“blue” lasers"), S("Adjust the replacement color for “blue” lasers") },
[lzr_globals.COLOR_YELLOW] = { S("yellow"), S("“yellow” lasers"), S("Adjust the replacement color for “yellow” lasers") },
[lzr_globals.COLOR_CYAN] = { S("cyan"), S("“cyan” lasers"), S("Adjust the replacement color for “cyan” lasers") },
[lzr_globals.COLOR_MAGENTA] = { S("magenta"), S("“magenta” lasers"), S("Adjust the replacement color for “magenta” lasers") },
[lzr_globals.COLOR_WHITE] = { S("white"), S("“white” lasers"), S("Adjust the replacement color for “white” lasers") },
}
local open_color_picker = function(player, colorname)
local r = graphics_settings_state["picked_color_red"]
local g = graphics_settings_state["picked_color_green"]
local b = graphics_settings_state["picked_color_blue"]
local colorcode = lzr_globals.COLOR_NAMES_SWAPPED[colorname]
-- If missing or invalid setting, fallback to default
if not r then
if colorcode then
local lhexcode = lzr_laser.DEFAULT_LASER_COLORS[colorcode]
if lhexcode then
r, g, b = lzr_util.hexcode_to_rgb(lhexcode)
end
end
if not r then
minetest.log("error", "[lzr_menu] open_color_picker could not pick default color for: "..colorname.."!")
return
end
end
local hexcode = lzr_util.rgb_to_hexcode(r,g,b)
local channels = {
{ "red", S("Red"), r },
{ "green", S("Green"), g },
{ "blue", S("Blue"), b },
}
local lalpha
if graphics_settings_state.opaque_lasers then
lalpha = "FF"
else
lalpha = MENU_LASER_ALPHA
end
local color_adj = color_descriptions[colorcode][1]
local form = "" ..
"formspec_version[7]size[9,7.1]"..
"image[8.3,0.3;0.4,0.4;lzr_menu_tooltip_icon.png]"..
"tooltip[8.3,0.3;0.4,0.4;"..
minetest.formspec_escape(
S("Here you can redefine the color @1 for lasers.", color_adj).."\n"..
S("The game will pretend this is @1, even if the actual color is different.", color_adj)
).."]"..
"label[0.5,0.5;"..minetest.formspec_escape(color_descriptions[colorcode][3]).."]"..
"box[1.0,1.2;7,0.2;#"..hexcode..lalpha.."]"..
"container[0.5,2.0]"
for c=1, #channels do
local id = channels[c][1]
local desc = channels[c][2]
local defval = channels[c][3]
local y = c - 1
-- get modifier
local function gm(index)
local txt
if COLOR_PICKER_MODIFIERS[index] < 0 then
--~ Subtract color value in custom laser color menu
return FS("@1", math.abs(COLOR_PICKER_MODIFIERS[index]))
else
--~ Add color value in custom laser color menu
return FS("+@1", COLOR_PICKER_MODIFIERS[index])
end
end
form = form .. "label[0.39,"..(y+0.3)..";"..minetest.formspec_escape(desc).."]" ..
"button[2,"..y..";0.7,0.5;channel_"..id.."_mmm;"..gm(1).."]" ..
"button[2.7,"..y..";0.7,0.5;channel_"..id.."_mm;"..gm(2).."]"..
"button[3.4,"..y..";0.7,0.5;channel_"..id.."_m;"..gm(3).."]"..
--~ Color value number in custom laser color menu. @1 current value, @2 maximum possible value
"label[4.4,"..(y+0.3)..";"..FS("@1/@2", defval, 255).."]"..
"button[5.6,"..y..";0.7,0.5;channel_"..id.."_p;"..gm(4).."]"..
"button[6.3,"..y..";0.7,0.5;channel_"..id.."_pp;"..gm(5).."]"..
"button[7.0,"..y..";0.7,0.5;channel_"..id.."_ppp;"..gm(6).."]"
local chancode
if id == "red" then
chancode = string.format("#%02x0000", defval)
elseif id == "green" then
chancode = string.format("#00%02x00", defval)
elseif id == "blue" then
chancode = string.format("#0000%02x", defval)
end
form = form .. "box[0.0,"..(y+0.15)..";0.3,0.3;"..chancode..lalpha.."]"
end
form = form .. "container_end[]"
form = form .. "button[0.5,4.9;2,0.5;default;"..FS("Default").."]"..
"tooltip[default;"..FS("Pick the default color").."]"
form = form .. "button[0.7,6;3.5,0.8;ok;"..FS("OK").."]"..
"button[4.75,6;3.5,0.8;cancel;"..FS("Cancel").."]"
minetest.show_formspec(player:get_player_name(), "lzr_menu:color_picker", form)
end
local open_graphics_settings = function(player)
graphics_settings_state.picking_color = nil
local put_asterisk = function(x, y)
return "label["..x..","..y..";"..minetest.formspec_escape("(*)").."]" ..
"tooltip["..(x-0.08)..","..(y-0.2)..";0.4,0.4;"..S("This setting will take effect after a restart.").."]"
end
local form = "" ..
-- Header
"formspec_version[7]size[12,6.1]"..
-- Opaque lasers
"container[-0.6,0]" ..
"checkbox[2.3,1;opaque_lasers;"..FS("Opaque lasers")..";"..tostring(graphics_settings_state.opaque_lasers).."]"
form = form .. "box[1,0.5;1,1;#0000003f]"
if graphics_settings_state.opaque_lasers then
form = form .. "image[1.1,0.6;0.8,0.8;lzr_menu_settings_opaque_lasers_on.png]"
else
form = form .. "image[1.1,0.6;0.8,0.8;lzr_menu_settings_opaque_lasers_off.png]"
end
form = form .. "tooltip[opaque_lasers;"..FS("If enabled, lasers arent translucent. Can improve performance.").."]"
if graphics_settings_state.opaque_lasers ~= graphics_settings_initial.opaque_lasers then
form = form .. put_asterisk(6, 1)
end
-- Patterned lasers
form = form .. "box[1,1.6;1,1;#0000003f]"
if graphics_settings_state.patterned_lasers then
form = form .. "image[1.1,1.7.1;0.8,0.8;lzr_menu_settings_patterned_lasers_on.png]"
else
form = form .. "image[1.1,1.7.1;0.8,0.8;lzr_menu_settings_patterned_lasers_off.png]"
end
form = form .. "checkbox[2.3,2.1;patterned_lasers;"..FS("Draw patterns on lasers")..";"..tostring(graphics_settings_state.patterned_lasers).."]" ..
"tooltip[patterned_lasers;"..FS("Special patterns will appear on the lasers, one for each color. Helps to distinguish lasers without relying on color alone.").."]"
if graphics_settings_state.patterned_lasers ~= graphics_settings_initial.patterned_lasers then
form = form .. put_asterisk(6, 2.1)
end
form = form .. "container_end[]"
local get_color_setting_texture = function(colorname)
local hexcode = graphics_settings_state["color_"..colorname]
local lalpha
if graphics_settings_state.opaque_lasers then
lalpha = "FF"
else
lalpha = MENU_LASER_ALPHA
end
if type(hexcode) == "string" and string.len(hexcode) == 6 then
return "[fill:16x16:#"..hexcode..lalpha.."^[mask:lzr_menu_color_select_button_mask.png"
elseif hexcode == "" or hexcode == nil then
local colorcode = lzr_globals.COLOR_NAMES_SWAPPED[colorname]
hexcode = lzr_laser.DEFAULT_LASER_COLORS[colorcode]
-- This is an error
if not hexcode then
minetest.log("error", "[lzr_menu] get_color_setting_texture: Could not get default color for: "..colorname)
return "blank.png"
end
return "[fill:16x16:#"..hexcode..lalpha.."^[mask:lzr_menu_color_select_button_mask.png"
else
-- Invalid color in settings
return "blank.png"
end
end
local are_colors_modified = function(colornames)
for c=1, #colornames do
local colorname = colornames[c]
if graphics_settings_initial["color_"..colorname] ~= graphics_settings_state["color_"..colorname] then
return true
end
end
end
-- Laser colors
form = form .. "container[6.5,0.6]"
form = form .. "box[-0.2,-0.2;5.4,3;#00000040]"
form = form .. "label[0,0;"..FS("Laser colors:").."]"
local ui_colors = { "red", "green", "blue", "yellow", "magenta", "cyan", "white" }
if are_colors_modified(ui_colors) then
form = form .. put_asterisk(4.5, 0)
end
local ibx = 0
local iby = 0
local max_x = 0
local max_y = 0
for c=1, #ui_colors do
local col = ui_colors[c]
local x = 0 + ibx*1.7
local y = 0.3 + iby
max_x = math.max(x, max_x)
max_y = math.max(y, max_y)
form = form .. "image_button["..x..","..y..";1.6,0.5;"..minetest.formspec_escape(get_color_setting_texture(col))..";color_"..col..";]" ..
"tooltip[color_"..col..";"..minetest.formspec_escape(color_descriptions[lzr_globals.COLOR_NAMES_SWAPPED[col]][2]).."]"
ibx = ibx + 1
if ibx >= 3 then
ibx = 0
iby = iby + 0.6
end
end
form = form .. "button[0,"..(max_y+0.7)..";3.2,0.5;color_reset;"..FS("Reset").."]" ..
"tooltip[color_reset;"..FS("Reset all laser colors").."]" ..
"container_end[]"
-- Buttons
form = form .. "button[2.5,4.7;3.1,0.8;apply;"..FS("Apply").."]"..
"button_exit[6.5,4.7;3.1,0.8;cancel;"..FS("Cancel").."]"
minetest.show_formspec(player:get_player_name(), "lzr_menu:graphics_settings", form)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
local pname = player:get_player_name()
if formname == "lzr_menu:graphics_settings" then
if fields.opaque_lasers == "true" then
graphics_settings_state.opaque_lasers = true
open_graphics_settings(player)
elseif fields.opaque_lasers == "false" then
graphics_settings_state.opaque_lasers = false
open_graphics_settings(player)
end
if fields.patterned_lasers == "true" then
graphics_settings_state.patterned_lasers = true
open_graphics_settings(player)
elseif fields.patterned_lasers == "false" then
graphics_settings_state.patterned_lasers = false
open_graphics_settings(player)
end
for c=1, #lzr_globals.COLOR_NAMES do
local color = lzr_globals.COLOR_NAMES[c]
if fields["color_"..color] then
graphics_settings_state.picking_color = color
local r, g, b = lzr_util.hexcode_to_rgb(graphics_settings_state["color_"..color])
graphics_settings_state["picked_color_red"] = r
graphics_settings_state["picked_color_green"] = g
graphics_settings_state["picked_color_blue"] = b
open_color_picker(player, color)
return
end
end
if fields.color_reset then
for c=1, #lzr_globals.COLOR_NAMES do
local colorname = lzr_globals.COLOR_NAMES[c]
graphics_settings_state["color_"..colorname] = lzr_laser.DEFAULT_LASER_COLORS[c]
end
open_graphics_settings(player)
return
end
if fields.apply then
-- Check if any setting has changed
local changed = false
for k, v in pairs(graphics_settings_initial) do
if v ~= graphics_settings_state[k] then
changed = true
break
end
end
if not changed then
-- Nothing has changed: Nothing to do.
minetest.close_formspec(pname, "lzr_menu:graphics_settings")
return
end
minetest.settings:set_bool("lzr_opaque_lasers", graphics_settings_state.opaque_lasers)
minetest.settings:set_bool("lzr_patterned_lasers", graphics_settings_state.patterned_lasers)
for c=1, #lzr_globals.COLOR_NAMES do
local colorname = lzr_globals.COLOR_NAMES[c]
local cvalue = graphics_settings_state["color_"..colorname]
if not cvalue then
cvalue = ""
end
minetest.settings:set("lzr_laser_color_"..colorname, cvalue)
end
local form = "formspec_version[7]size[10,4]"..
"textarea[1,0.8;8,1.5;;;"..FS("The game needs to be restarted for the new graphics settings to take effect.").."]"..
"button_exit[1,2.5;3,0.8;restart;"..FS("Restart").."]"..
"button_exit[5.5,2.5;3,0.8;cancel;"..FS("Keep playing").."]"
minetest.show_formspec(pname, "lzr_menu:confirm_restart", form)
elseif fields.quit or fields.cancel then
graphics_settings_state = {
opaque_lasers = minetest.settings:get_bool("lzr_opaque_lasers", false),
patterned_lasers = minetest.settings:get_bool("lzr_patterned_lasers", false),
}
for c=1, #lzr_globals.COLOR_NAMES do
local colorname = lzr_globals.COLOR_NAMES[c]
graphics_settings_state["color_"..colorname] = minetest.settings:get("lzr_laser_color_"..colorname)
end
end
elseif formname == "lzr_menu:confirm_restart" then
if fields.restart then
minetest.disconnect_player(pname, S("Youve quit the game. Start the game again for the settings to take effect."))
end
elseif formname == "lzr_menu:color_picker" then
local channels = { "red", "green", "blue" }
local modifiers = { "mmm", "mm", "m", "p", "pp", "ppp" }
for c=1, #channels do
for m=1, #modifiers do
local channel = channels[c]
local modifier = modifiers[m]
if fields["channel_"..channel.."_"..modifier] then
local val = graphics_settings_state["picked_color_"..channel]
if val then
val = val + COLOR_PICKER_MODIFIERS[m]
val = math.floor(math.min(255, math.max(0, val)))
else
val = 255
end
graphics_settings_state["picked_color_"..channel] = val
open_color_picker(player, graphics_settings_state.picking_color)
end
end
end
if fields.default then
local colorname = graphics_settings_state.picking_color
if colorname then
local colorcode = lzr_globals.COLOR_NAMES_SWAPPED[colorname]
local default_color_hex = lzr_laser.DEFAULT_LASER_COLORS[colorcode]
local r,g,b = lzr_util.hexcode_to_rgb(default_color_hex)
graphics_settings_state["picked_color_red"] = r
graphics_settings_state["picked_color_green"] = g
graphics_settings_state["picked_color_blue"] = b
open_color_picker(player, colorname)
end
end
if fields.ok then
local r = graphics_settings_state["picked_color_red"]
local g = graphics_settings_state["picked_color_green"]
local b = graphics_settings_state["picked_color_blue"]
if r and g and b then
r = math.floor(math.min(255, math.max(0, r)))
g = math.floor(math.min(255, math.max(0, g)))
b = math.floor(math.min(255, math.max(0, b)))
local hex = lzr_util.rgb_to_hexcode(r, g, b)
if graphics_settings_state.picking_color and hex then
graphics_settings_state["color_"..graphics_settings_state.picking_color] = hex
end
end
open_graphics_settings(player)
elseif fields.cancel or fields.quit then
open_graphics_settings(player)
end
end
end)
local function on_rightclick_television(_, _, player)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
open_graphics_settings(player)
end
local function on_punch_television(_, _, player)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
open_graphics_settings(player)
end
-- Television to update graphics settings
minetest.register_node("lzr_menu:television", {
description = S("Television"),
tiles = {
"xdecor_television_left.png^[transformR90",
"xdecor_television_left.png^[transformR270",
"xdecor_television_left.png",
"xdecor_television_left.png^[transformR180",
"xdecor_television_back.png",
{ name = "xdecor_television_front_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 1 } },
},
paramtype2 = "4dir",
groups = { breakable = 1, rotatable = 1, not_in_creative_inventory = 1},
sounds = lzr_sounds.node_sound_metal_defaults(),
is_ground_content = false,
on_punch = on_punch_television,
on_rightclick = on_rightclick_television,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Change graphics settings"))
end,
})
minetest.register_node("lzr_menu:bookshelf_info", {
description = S("Info Book Bookshelf"),
paramtype2 = "4dir",
tiles = {
{name="default_wood.png", align_style="world"},
{name="default_wood.png", align_style="world"},
{name="default_wood.png", align_style="world"},
{name="default_wood.png", align_style="world"},
"default_wood.png^lzr_decor_bookshelf.png",
},
sounds = lzr_sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Read about how to play the game"))
end,
on_punch = function(self, node, puncher)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
lzr_infobooks.open_bookshelf(puncher:get_player_name(), "how_to_play")
end,
on_rightclick = function(self, node, clicker)
if lzr_gamestate.get_state() ~= lzr_gamestate.MENU then
return
end
lzr_infobooks.open_bookshelf(clicker:get_player_name(), "how_to_play")
end,
groups = { not_in_creative_inventory = 1 },
})
-- This painting symbolizes completion of the game
minetest.register_node("lzr_menu:painting_perfect_plunderer", {
description = S("Painting: Perfect Plunderer"),
drawtype = "nodebox",
paramtype2 = "wallmounted",
paramtype = "light",
sunlight_propagates = true,
walkable = false,
node_box = {
type = "wallmounted",
wall_top = {-0.4375, 0.4375, -0.3725, 0.4375, 0.5, 0.3725},
wall_bottom = {-0.4375, -0.5, -0.3725, 0.4375, -0.4375, 0.3725},
wall_side = {-0.5, -0.3725, -0.4375, -0.4375, 0.3725, 0.4375},
},
tiles = {"lzr_menu_painting_perfect_plunderer.png", "lzr_menu_painting_back.png"},
use_texture_alpha = "clip",
groups = { breakable = 1, not_in_creative_inventory = 1 },
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Found every treasure of the known world"))
end,
})
-- This painting symbolizes finding all hidden parrots
minetest.register_node("lzr_menu:painting_parrot_finder", {
description = S("Painting: Parrot Finder"),
drawtype = "nodebox",
paramtype2 = "wallmounted",
paramtype = "light",
sunlight_propagates = true,
walkable = false,
node_box = {
type = "wallmounted",
wall_top = {-0.4375, 0.4375, -0.3725, 0.4375, 0.5, 0.3725},
wall_bottom = {-0.4375, -0.5, -0.3725, 0.4375, -0.4375, 0.3725},
wall_side = {-0.5, -0.3725, -0.4375, -0.4375, 0.3725, 0.4375},
},
tiles = {"lzr_menu_painting_parrot_finder.png", "lzr_menu_painting_back.png"},
use_texture_alpha = "clip",
groups = { breakable = 1, not_in_creative_inventory = 1 },
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Found every hidden parrot"))
end,
})
function lzr_menu.spawn_hidden_parrot(parrot_name)
local offset = lzr_globals.MENU_SHIP_HIDDEN_PARROT_OFFSETS[parrot_name]
if not offset then
minetest.log("error", "[lzr_menu] Unknown hidden parrot ship spawn offset for parrot '"..tostring(parrot_name).."'!")
return
end
local pos = vector.add(lzr_globals.MENU_SHIP_POS, offset)
-- Check if this parrot is already spawned at this pos
local objs = minetest.get_objects_inside_radius(pos, 0.5)
for o=1, #objs do
local obj = objs[o]
local ent = obj:get_luaentity()
if ent and ent.name == "lzr_parrot_npc:hidden_parrot" and ent._hidden_id == parrot_name then
-- No spawn if parrot is already there
return
end
end
-- Spawn parrot, then check for success
local obj = minetest.add_entity(pos, "lzr_parrot_npc:hidden_parrot")
if not obj then
minetest.log("error", "[lzr_menu] Failed to spawn in-ship hidden parrot '"..tostring(parrot_name).."'!")
return
end
local ent = obj:get_luaentity()
if not ent then
minetest.log("error", "[lzr_menu] Failed to get luaentity of in-ship hidden parrot '"..tostring(parrot_name).."'!")
return
end
-- Ship parrots look randomly
local yaw = (math.random(0,359) / 360) * (math.pi*2)
obj:set_yaw(yaw)
-- Assign parrot name
ent:_init(parrot_name)
end
function lzr_menu.teleport_player_to_ship(player, location)
if location == "captain" then
player:set_pos(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PLAYER_SPAWN_OFFSET))
player:set_look_horizontal(0)
player:set_look_vertical(0)
elseif location == "skulls" then
player:set_pos(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PLAYER_RESPAWN_OFFSET))
player:set_look_horizontal(0)
player:set_look_vertical(0)
elseif location == "victory" then
player:set_pos(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PLAYER_WINSPAWN_OFFSET))
player:set_look_horizontal(math.pi)
player:set_look_vertical(0)
else
minetest.log("error", "[lzr_menu] Tried to teleport player to invalid ship location: "..tostring(location))
return
end
for f=1, #registered_on_player_ship_enters do
registered_on_player_ship_enters[f](player, location)
end
end
function lzr_menu.remove_hidden_parrots()
if not lzr_menu.SHIP_SIZE then
return
end
local max_pos = vector.add(lzr_globals.MENU_SHIP_POS, lzr_menu.SHIP_SIZE)
local objs = minetest.get_objects_in_area(lzr_globals.MENU_SHIP_POS, max_pos)
for o=1, #objs do
local obj = objs[o]
local ent = obj:get_luaentity()
if ent and ent.name == "lzr_parrot_npc:hidden_parrot" then
obj:remove()
end
end
end
function lzr_menu.place_painting(painting)
if painting == "perfect_plunderer" then
minetest.set_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PAINTING_PERFECT_PLUNDERER_OFFSET), {name="lzr_menu:painting_perfect_plunderer", param2=4})
minetest.log("action", "[lzr_menu] Added painting 'perfect_plunderer'")
elseif painting == "parrot_finder" then
minetest.set_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PAINTING_PARROT_FINDER_OFFSET), {name="lzr_menu:painting_parrot_finder", param2=4})
minetest.log("action", "[lzr_menu] Added painting 'parrot_finder'")
end
end
function lzr_menu.remove_painting(painting)
if painting == "perfect_plunderer" then
minetest.remove_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PAINTING_PERFECT_PLUNDERER_OFFSET))
minetest.log("action", "[lzr_menu] Removed painting 'perfect_plunderer'")
elseif painting == "parrot_finder" then
minetest.remove_node(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PAINTING_PARROT_FINDER_OFFSET))
minetest.log("action", "[lzr_menu] Removed painting 'perfect_plunderer'")
end
end
lzr_gamestate.register_on_enter_state(function(state)
if state == lzr_gamestate.MENU then
local player = minetest.get_player_by_name("singleplayer")
lzr_player.set_menu_inventory(player)
lzr_gui.set_menu_gui(player)
lzr_ambience.set_ambience("village")
lzr_sky.set_sky("bright_blue")
lzr_weather.set_weather("clear")
lzr_triggers.reset_triggers()
end
end)
local timer = 0
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer < 1.5 then
return
end
timer = 0
if lzr_gamestate.get_state ()~= lzr_gamestate.MENU then
return
end
local player = minetest.get_player_by_name("singleplayer")
if not player then
return
end
local pos = player:get_pos()
-- Render menu markers if player is inside captain's cabin
local in_cabin = false
-- Main area
if vector.in_area(pos,
vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_CAPTAIN_CABIN_OFFSET_MIN),
vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_CAPTAIN_CABIN_OFFSET_MAX)) then
in_cabin = true
-- Exclusion zones
for i=1, #lzr_globals.MENU_SHIP_CAPTAIN_CABIN_EXCLUSIONS do
if vector.in_area(pos,
vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_CAPTAIN_CABIN_EXCLUSIONS[i][1]),
vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_CAPTAIN_CABIN_EXCLUSIONS[i][2])) then
in_cabin = false
break
end
end
end
if in_cabin then
lzr_gui.show_menu_markers(player)
else
lzr_gui.hide_menu_markers(player)
end
end)
-- Force-load the player spawn position
local function forceload()
local ok = minetest.forceload_block(vector.add(lzr_globals.MENU_SHIP_POS, lzr_globals.MENU_SHIP_PLAYER_SPAWN_OFFSET), true)
if not ok then
minetest.log("warning", "[lzr_menu] Could not forceload menu ship")
else
minetest.log("action", "[lzr_menu] Menu ship successfully forceloaded")
end
end
forceload()
minetest.register_entity("lzr_menu:arrow", {
initial_properties = {
visual = "mesh",
mesh = "lzr_menu_arrow.gltf",
visual_size = { x=5, y=5, z=5 },
textures = {
"lzr_menu_arrow.png",
},
use_texture_alpha = true,
static_save = false,
physical = false,
collide_with_objects = false,
selectionbox = {
-0.25, -0.25, -0.25, 0.25, 0.5, 0.25,
},
pointable = false,
automatic_rotate = -0.5,
},
on_activate = function(self)
minetest.log("error", "set_anim")
self.object:set_animation({x=0,y=48}, 0.5, 0, true)
end,
})
minetest.register_alias("lzr_menu:editorbook", "lzr_menu:editor_starter")
minetest.register_alias("lzr_menu:startbook", "lzr_menu:level_starter")
minetest.register_alias("lzr_menu:startbook_custom", "lzr_menu:custom_level_starter")