From 967121a34bbc60e6b46c7ec470b151f668ef1fef Mon Sep 17 00:00:00 2001 From: sapier Date: Sun, 23 Jun 2013 18:30:21 +0200 Subject: Replace C++ mainmenu by formspec powered one --- builtin/gamemgr.lua | 309 +++++ builtin/mainmenu.lua | 1190 +++++++++++++++++++ builtin/mainmenu_helper.lua | 105 ++ builtin/modmgr.lua | 881 +++++++++++++++ builtin/modstore.lua | 275 +++++ doc/lua_api.txt | 58 + doc/menu_lua_api.txt | 163 +++ minetest.conf.example | 6 + src/CMakeLists.txt | 11 +- src/convert_json.cpp | 367 ++++++ src/convert_json.h | 34 + src/defaultsettings.cpp | 5 + src/filesys.cpp | 267 ++++- src/filesys.h | 33 +- src/game.cpp | 27 - src/guiConfigureWorld.cpp | 693 ------------ src/guiConfigureWorld.h | 107 -- src/guiConfirmMenu.cpp | 204 ---- src/guiConfirmMenu.h | 56 - src/guiCreateWorld.cpp | 280 ----- src/guiCreateWorld.h | 64 -- src/guiEngine.cpp | 570 ++++++++++ src/guiEngine.h | 260 +++++ src/guiFileSelectMenu.cpp | 133 +++ src/guiFileSelectMenu.h | 80 ++ src/guiFormSpecMenu.cpp | 2072 ++++++++++++++++++++++++++-------- src/guiFormSpecMenu.h | 129 ++- src/guiLuaApi.cpp | 1067 +++++++++++++++++ src/guiLuaApi.h | 183 +++ src/guiMainMenu.cpp | 1521 ------------------------- src/guiMainMenu.h | 111 +- src/main.cpp | 461 +------- src/map.cpp | 13 +- src/mods.cpp | 28 + src/mods.h | 68 ++ src/serverlist.cpp | 60 +- src/test.cpp | 208 ++++ textures/base/pack/no_screenshot.png | Bin 0 -> 2446 bytes 38 files changed, 8104 insertions(+), 3995 deletions(-) create mode 100644 builtin/gamemgr.lua create mode 100644 builtin/mainmenu.lua create mode 100644 builtin/mainmenu_helper.lua create mode 100644 builtin/modmgr.lua create mode 100644 builtin/modstore.lua create mode 100644 doc/menu_lua_api.txt create mode 100644 src/convert_json.cpp create mode 100644 src/convert_json.h delete mode 100644 src/guiConfigureWorld.cpp delete mode 100644 src/guiConfigureWorld.h delete mode 100644 src/guiConfirmMenu.cpp delete mode 100644 src/guiConfirmMenu.h delete mode 100644 src/guiCreateWorld.cpp delete mode 100644 src/guiCreateWorld.h create mode 100644 src/guiEngine.cpp create mode 100644 src/guiEngine.h create mode 100644 src/guiFileSelectMenu.cpp create mode 100644 src/guiFileSelectMenu.h create mode 100644 src/guiLuaApi.cpp create mode 100644 src/guiLuaApi.h delete mode 100644 src/guiMainMenu.cpp create mode 100644 textures/base/pack/no_screenshot.png diff --git a/builtin/gamemgr.lua b/builtin/gamemgr.lua new file mode 100644 index 000000000..bbff51305 --- /dev/null +++ b/builtin/gamemgr.lua @@ -0,0 +1,309 @@ +--Minetest +--Copyright (C) 2013 sapier +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +gamemgr = {} + +-------------------------------------------------------------------------------- +function gamemgr.dialog_new_game() + local retval = + "label[2,2;Game Name]".. + "field[4.5,2.4;6,0.5;te_game_name;;]" .. + "button[5,4.2;2.6,0.5;new_game_confirm;Create]" .. + "button[7.5,4.2;2.8,0.5;new_game_cancel;Cancel]" + + return retval +end + +-------------------------------------------------------------------------------- +function gamemgr.handle_games_buttons(fields) + if fields["gamelist"] ~= nil then + local event = explode_textlist_event(fields["gamelist"]) + gamemgr.selected_game = event.index + end + + if fields["btn_game_mgr_edit_game"] ~= nil then + return { + is_dialog = true, + show_buttons = false, + current_tab = "dialog_edit_game" + } + end + + if fields["btn_game_mgr_new_game"] ~= nil then + return { + is_dialog = true, + show_buttons = false, + current_tab = "dialog_new_game" + } + end + + return nil +end + +-------------------------------------------------------------------------------- +function gamemgr.handle_new_game_buttons(fields) + + if fields["new_game_confirm"] and + fields["te_game_name"] ~= nil and + fields["te_game_name"] ~= "" then + local gamepath = engine.get_gamepath() + + if gamepath ~= nil and + gamepath ~= "" then + local gamefolder = cleanup_path(fields["te_game_name"]) + + --TODO check for already existing first + engine.create_dir(gamepath .. DIR_DELIM .. gamefolder) + engine.create_dir(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "mods") + engine.create_dir(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "menu") + + local gameconf = + io.open(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "game.conf","w") + + if gameconf then + gameconf:write("name = " .. fields["te_game_name"]) + gameconf:close() + end + end + end + + return { + is_dialog = false, + show_buttons = true, + current_tab = engine.setting_get("main_menu_tab") + } +end + +-------------------------------------------------------------------------------- +function gamemgr.handle_edit_game_buttons(fields) + local current_game = gamemgr.get_game(gamemgr.selected_game) + + if fields["btn_close_edit_game"] ~= nil or + current_game == nil then + return { + is_dialog = false, + show_buttons = true, + current_tab = engine.setting_get("main_menu_tab") + } + end + + if fields["btn_remove_mod_from_game"] ~= nil then + gamemgr.delete_mod(current_game,engine.get_textlist_index("mods_current")) + end + + if fields["btn_add_mod_to_game"] ~= nil then + local modindex = engine.get_textlist_index("mods_available") + + if modindex > 0 and + modindex <= #modmgr.global_mods then + + local sourcepath = + engine.get_modpath() .. DIR_DELIM .. modmgr.global_mods[modindex] + + gamemgr.add_mod(current_game,sourcepath) + end + end + + return nil +end + +-------------------------------------------------------------------------------- +function gamemgr.add_mod(gamespec,sourcepath) + if gamespec.gamemods_path ~= nil and + gamespec.gamemods_path ~= "" then + + local modname = get_last_folder(sourcepath) + + engine.copy_dir(sourcepath,gamespec.gamemods_path .. DIR_DELIM .. modname); + end +end + +-------------------------------------------------------------------------------- +function gamemgr.delete_mod(gamespec,modindex) + if gamespec.gamemods_path ~= nil and + gamespec.gamemods_path ~= "" then + local game_mods = {} + get_mods(gamespec.gamemods_path,game_mods) + + if modindex > 0 and + #game_mods >= modindex then + + local modname = game_mods[modindex] + + if modname:find("") ~= nil then + modname = modname:sub(0,modname:find("<") -2) + end + + local modpath = gamespec.gamemods_path .. DIR_DELIM .. modname + if modpath:sub(0,gamespec.gamemods_path:len()) == gamespec.gamemods_path then + engine.delete_dir(modpath) + end + end + end +end + +-------------------------------------------------------------------------------- +function gamemgr.get_game_mods(gamespec) + + local retval = "" + + if gamespec.gamemods_path ~= nil and + gamespec.gamemods_path ~= "" then + local game_mods = {} + get_mods(gamespec.gamemods_path,game_mods) + + for i=1,#game_mods,1 do + if retval ~= "" then + retval = retval.."," + end + retval = retval .. game_mods[i] + end + end + return retval +end + +-------------------------------------------------------------------------------- +function gamemgr.gettab(name) + local retval = "" + + if name == "dialog_edit_game" then + retval = retval .. gamemgr.dialog_edit_game() + end + + if name == "dialog_new_game" then + retval = retval .. gamemgr.dialog_new_game() + end + + if name == "game_mgr" then + retval = retval .. gamemgr.tab() + end + + return retval +end + +-------------------------------------------------------------------------------- +function gamemgr.tab() + if gamemgr.selected_game == nil then + gamemgr.selected_game = 1 + end + + local retval = + "vertlabel[0,-0.25;GAMES]" .. + "label[1,-0.25;Games:]" .. + "textlist[1,0.25;4.5,4.4;gamelist;" .. + gamemgr.gamelist() .. + ";" .. gamemgr.selected_game .. "]" + + local current_game = gamemgr.get_game(gamemgr.selected_game) + + if current_game ~= nil then + if current_game.menuicon_path ~= nil and + current_game.menuicon_path ~= "" then + retval = retval .. + "image[5.8,-0.25;2,2;" .. current_game.menuicon_path .. "]" + end + + retval = retval .. + "field[8,-0.25;6,2;;" .. current_game.name .. ";]".. + "label[6,1.4;Mods:]" .. + "button[9.7,1.5;2,0.2;btn_game_mgr_edit_game;edit game]" .. + "textlist[6,2;5.5,3.3;game_mgr_modlist;" + .. gamemgr.get_game_mods(current_game) ..";0]" .. + "button[1,4.75;3.2,0.5;btn_game_mgr_new_game;new game]" + end + return retval +end + +-------------------------------------------------------------------------------- +function gamemgr.dialog_edit_game() + local current_game = gamemgr.get_game(gamemgr.selected_game) + if current_game ~= nil then + local retval = + "vertlabel[0,-0.25;EDIT GAME]" .. + "label[0,-0.25;" .. current_game.name .. "]" .. + "button[11.55,-0.2;0.75,0.5;btn_close_edit_game;x]" + + if current_game.menuicon_path ~= nil and + current_game.menuicon_path ~= "" then + retval = retval .. + "image[5.25,0;2,2;" .. current_game.menuicon_path .. "]" + end + + retval = retval .. + "textlist[0.5,0.5;4.5,4.3;mods_current;" + .. gamemgr.get_game_mods(current_game) ..";0]" + + + retval = retval .. + "textlist[7,0.5;4.5,4.3;mods_available;" + .. modmgr.get_mods_list() .. ";0]" + + retval = retval .. + "button[0.55,4.95;4.7,0.5;btn_remove_mod_from_game;Remove selected mod]" + + retval = retval .. + "button[7.05,4.95;4.7,0.5;btn_add_mod_to_game;<<-- Add mod]" + + return retval + end +end + +-------------------------------------------------------------------------------- +function gamemgr.handle_buttons(tab,fields) + local retval = nil + + if tab == "dialog_edit_game" then + retval = gamemgr.handle_edit_game_buttons(fields) + end + + if tab == "dialog_new_game" then + retval = gamemgr.handle_new_game_buttons(fields) + end + + if tab == "game_mgr" then + retval = gamemgr.handle_games_buttons(fields) + end + + return retval +end + +-------------------------------------------------------------------------------- +function gamemgr.get_game(index) + if index > 0 and index <= #gamemgr.games then + return gamemgr.games[index] + end + + return nil +end + +-------------------------------------------------------------------------------- +function gamemgr.update_gamelist() + gamemgr.games = engine.get_games() +end + +-------------------------------------------------------------------------------- +function gamemgr.gamelist() + local retval = "" + if #gamemgr.games > 0 then + retval = retval .. gamemgr.games[1].id + + for i=2,#gamemgr.games,1 do + retval = retval .. "," .. gamemgr.games[i].name + end + end + return retval +end diff --git a/builtin/mainmenu.lua b/builtin/mainmenu.lua new file mode 100644 index 000000000..ef0ba7226 --- /dev/null +++ b/builtin/mainmenu.lua @@ -0,0 +1,1190 @@ +local scriptpath = engine.get_scriptdir() + +dofile(scriptpath .. DIR_DELIM .. "modmgr.lua") +dofile(scriptpath .. DIR_DELIM .. "modstore.lua") +dofile(scriptpath .. DIR_DELIM .. "gamemgr.lua") + +local menu = {} +local tabbuilder = {} +local menubar = {} + +-------------------------------------------------------------------------------- +function render_favourite(spec) + local text = "" + + if spec.name ~= nil then + text = text .. spec.name:trim() + + if spec.description ~= nil then + --TODO make sure there's no invalid chat in spec.description + text = text .. " (" .. fs_escape_string(spec.description) .. ")" + end + else + if spec.address ~= nil then + text = text .. spec.address:trim() + end + end + + local details = "" + if spec.password == true then + details = " *" + else + details = " " + end + + if spec.creative then + details = details .. "C" + else + details = details .. " " + end + + if spec.damage then + details = details .. "D" + else + details = details .. " " + end + + if spec.pvp then + details = details .. "P" + else + details = details .. " " + end + + text = text .. ":" .. spec.port:trim() + + return text +end + +-------------------------------------------------------------------------------- +os.tempfolder = function() + local filetocheck = os.tmpname() + os.remove(filetocheck) + + local randname = "MTTempModFolder_" .. math.random(0,10000) + if DIR_DELIM == "\\" then + local tempfolder = os.getenv("TEMP") + return tempfolder .. filetocheck + else + local backstring = filetocheck:reverse() + return filetocheck:sub(0,filetocheck:len()-backstring:find(DIR_DELIM)+1) ..randname + end + +end + +-------------------------------------------------------------------------------- +function cleanup_path(temppath) + + local parts = temppath:split("-") + temppath = "" + for i=1,#parts,1 do + if temppath ~= "" then + temppath = temppath .. "_" + end + temppath = temppath .. parts[i] + end + + parts = temppath:split(".") + temppath = "" + for i=1,#parts,1 do + if temppath ~= "" then + temppath = temppath .. "_" + end + temppath = temppath .. parts[i] + end + + parts = temppath:split("'") + temppath = "" + for i=1,#parts,1 do + if temppath ~= "" then + temppath = temppath .. "" + end + temppath = temppath .. parts[i] + end + + parts = temppath:split(" ") + temppath = "" + for i=1,#parts,1 do + if temppath ~= "" then + temppath = temppath + end + temppath = temppath .. parts[i] + end + + return temppath +end + +-------------------------------------------------------------------------------- +function menu.update_gametype() + if (menu.game_last_check == nil or + menu.game_last_check ~= menu.last_game) and + tabbuilder.current_tab == "singleplayer" then + + local gamedetails = menu.lastgame() + engine.set_topleft_text(gamedetails.name) + + --background + local path_background_texture = gamedetails.path .. DIR_DELIM .."menu" .. + DIR_DELIM .. "background.png" + if engine.set_background("background",path_background_texture) then + engine.set_clouds(false) + else + engine.set_clouds(true) + end + + --overlay + local path_overlay_texture = gamedetails.path .. DIR_DELIM .."menu" .. + DIR_DELIM .. "overlay.png" + engine.set_background("overlay",path_overlay_texture) + + --header + local path_overlay_texture = gamedetails.path .. DIR_DELIM .."menu" .. + DIR_DELIM .. "header.png" + engine.set_background("header",path_overlay_texture) + + --footer + local path_overlay_texture = gamedetails.path .. DIR_DELIM .."menu" .. + DIR_DELIM .. "footer.png" + engine.set_background("footer",path_overlay_texture) + + menu.game_last_check = menu.last_game + else + if menu.game_last_check ~= menu.last_game then + menu.game_last_check = menu.last_game + menu.reset_gametype() + end + end +end + +-------------------------------------------------------------------------------- +function menu.reset_gametype() + menu.game_last_check = nil + engine.set_clouds(true) + engine.set_background("background","") + engine.set_background("overlay",menu.basetexturedir .. "menu_overlay.png") + engine.set_background("header",menu.basetexturedir .. "menu_header.png") + engine.set_background("footer",menu.basetexturedir .. "menu_footer.png") + engine.set_topleft_text("") +end + +-------------------------------------------------------------------------------- +function get_last_folder(text,count) + local parts = text:split(DIR_DELIM) + + if count == nil then + return parts[#parts] + end + + local retval = "" + for i=1,count,1 do + retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM + end + + return retval +end + +-------------------------------------------------------------------------------- +function init_globals() + --init gamedata + gamedata.worldindex = 0 +end + +-------------------------------------------------------------------------------- +function identify_filetype(name) + + if name:sub(-3):lower() == "zip" then + return { + name = name, + type = "zip" + } + end + + if name:sub(-6):lower() == "tar.gz" or + name:sub(-3):lower() == "tgz"then + return { + name = name, + type = "tgz" + } + end + + if name:sub(-6):lower() == "tar.bz2" then + return { + name = name, + type = "tbz" + } + end + + if name:sub(-2):lower() == "7z" then + return { + name = name, + type = "7z" + } + end + + return { + name = name, + type = "ukn" + } +end + +-------------------------------------------------------------------------------- +function update_menu() + + local formspec = "size[12,5.2]" + + -- handle errors + if gamedata.errormessage ~= nil then + formspec = formspec .. + "field[1,2;10,2;;ERROR: " .. + gamedata.errormessage .. + ";]".. + "button[4.5,4.2;3,0.5;btn_error_confirm;Ok]" + else + formspec = formspec .. tabbuilder.gettab() + end + + engine.update_formspec(formspec) +end + +-------------------------------------------------------------------------------- +function menu.filtered_game_list() + local retval = "" + + local current_game = menu.lastgame() + + for i=1,#menu.worldlist,1 do + if menu.worldlist[i].gameid == current_game.id then + if retval ~= "" then + retval = retval .."," + end + + retval = retval .. menu.worldlist[i].name .. + " [[" .. menu.worldlist[i].gameid .. "]]" + end + end + + return retval +end + +-------------------------------------------------------------------------------- +function menu.filtered_game_list_raw() + local retval = {} + + local current_game = menu.lastgame() + + for i=1,#menu.worldlist,1 do + if menu.worldlist[i].gameid == current_game.id then + table.insert(retval,menu.worldlist[i]) + end + end + + return retval +end + +-------------------------------------------------------------------------------- +function menu.filtered_index_to_plain(filtered_index) + + local current_game = menu.lastgame() + + local temp_idx = 0 + + for i=1,#menu.worldlist,1 do + if menu.worldlist[i].gameid == current_game.id then + temp_idx = temp_idx +1 + end + + if temp_idx == filtered_index then + return i + end + end + return -1 +end + +-------------------------------------------------------------------------------- +function menu.init() + --init menu data + gamemgr.update_gamelist() + + menu.worldlist = engine.get_worlds() + + menu.last_world = tonumber(engine.setting_get("main_menu_last_world_idx")) + menu.last_game = tonumber(engine.setting_get("main_menu_last_game_idx")) + + if type(menu.last_world) ~= "number" then + menu.last_world = 1 + end + + if type(menu.last_game) ~= "number" then + menu.last_game = 1 + end + + if engine.setting_getbool("public_serverlist") then + menu.favorites = engine.get_favorites("online") + else + menu.favorites = engine.get_favorites("local") + end + + + menu.basetexturedir = engine.get_gamepath() .. DIR_DELIM .. ".." .. + DIR_DELIM .. "textures" .. DIR_DELIM .. "base" .. + DIR_DELIM .. "pack" .. DIR_DELIM +end + +-------------------------------------------------------------------------------- +function menu.lastgame() + if menu.last_game > 0 and menu.last_game <= #gamemgr.games then + return gamemgr.games[menu.last_game] + end + + if #gamemgr.games >= 1 then + menu.last_game = 1 + return gamemgr.games[menu.last_game] + end + + --error case!! + return nil +end + +-------------------------------------------------------------------------------- +function menu.lastworld() + if menu.last_world ~= nil and + menu.last_world > 0 and + menu.last_world <= #menu.worldlist then + return menu.worldlist[menu.last_world] + end + + if #menu.worldlist >= 1 then + menu.last_world = 1 + return menu.worldlist[menu.last_world] + end + + --error case!! + return nil +end + +-------------------------------------------------------------------------------- +function menu.update_last_game(world_idx) + if gamedata.selected_world <= #menu.worldlist then + local world = menu.worldlist[gamedata.selected_world] + + for i=1,#gamemgr.games,1 do + if gamemgr.games[i].id == world.gameid then + menu.last_game = i + engine.setting_set("main_menu_last_game_idx",menu.last_game) + break + end + end + end +end + +-------------------------------------------------------------------------------- +function menubar.handle_buttons(fields) + for i=1,#menubar.buttons,1 do + if fields[menubar.buttons[i].btn_name] ~= nil then + menu.last_game = menubar.buttons[i].index + engine.setting_set("main_menu_last_game_idx",menu.last_game) + menu.update_gametype() + end + end +end + +-------------------------------------------------------------------------------- +function menubar.refresh() + menubar.formspec = "box[-2,7.625;15.75,1.75;BLK]" + menubar.buttons = {} + + local button_base = -1.8 + + local maxbuttons = #gamemgr.games + + if maxbuttons > 12 then + maxbuttons = 12 + end + + for i=1,maxbuttons,1 do + + local btn_name = "menubar_btn_" .. gamemgr.games[i].id + local buttonpos = button_base + (i-1) * 1.3 + if gamemgr.games[i].menuicon_path ~= nil and + gamemgr.games[i].menuicon_path ~= "" then + + menubar.formspec = menubar.formspec .. + "image_button[" .. buttonpos .. ",7.9;1.3,1.3;" .. + gamemgr.games[i].menuicon_path .. ";" .. btn_name .. ";;true;false]" + else + + local part1 = gamemgr.games[i].id:sub(1,5) + local part2 = gamemgr.games[i].id:sub(6,10) + local part3 = gamemgr.games[i].id:sub(11) + + local text = part1 .. "\n" .. part2 + if part3 ~= nil and + part3 ~= "" then + text = text .. "\n" .. part3 + end + menubar.formspec = menubar.formspec .. + "image_button[" .. buttonpos .. ",7.9;1.3,1.3;;" ..btn_name .. + ";" .. text .. ";true;true]" + end + + local toadd = { + btn_name = btn_name, + index = i, + } + + table.insert(menubar.buttons,toadd) + end +end + +-------------------------------------------------------------------------------- +function tabbuilder.dialog_create_world() + local retval = + "label[2,0;World name]".. + "label[2,1;Mapgen]".. + "field[4.5,0.4;6,0.5;te_world_name;;]" .. + "label[2,2;Game]".. + "button[5,4.5;2.6,0.5;world_create_confirm;Create]" .. + "button[7.5,4.5;2.8,0.5;world_create_cancel;Cancel]" .. + "dropdown[4.2,1;6.3;dd_mapgen;v6,v7,indev,singlenode,math;1]" .. --TODO read from minetest + "textlist[4.2,1.9;5.8,2.3;games;" .. + gamemgr.gamelist() .. + ";" .. menu.last_game .. ";true]" + + return retval +end + +-------------------------------------------------------------------------------- +function tabbuilder.dialog_delete_world() + return "label[2,2;Delete World \"" .. menu.lastworld().name .. "\"?]".. + "button[3.5,4.2;2.6,0.5;world_delete_confirm;Yes]" .. + "button[6,4.2;2.8,0.5;world_delete_cancel;No]" +end + +-------------------------------------------------------------------------------- +function tabbuilder.gettab() + local retval = "" + + if tabbuilder.show_buttons then + retval = retval .. tabbuilder.tab_header() + end + + if tabbuilder.current_tab == "singleplayer" then + retval = retval .. tabbuilder.tab_singleplayer() + end + + if tabbuilder.current_tab == "multiplayer" then + retval = retval .. tabbuilder.tab_multiplayer() + end + + if tabbuilder.current_tab == "server" then + retval = retval .. tabbuilder.tab_server() + end + + if tabbuilder.current_tab == "settings" then + retval = retval .. tabbuilder.tab_settings() + end + + if tabbuilder.current_tab == "credits" then + retval = retval .. tabbuilder.tab_credits() + end + + if tabbuilder.current_tab == "dialog_create_world" then + retval = retval .. tabbuilder.dialog_create_world() + end + + if tabbuilder.current_tab == "dialog_delete_world" then + retval = retval .. tabbuilder.dialog_delete_world() + end + + retval = retval .. modmgr.gettab(tabbuilder.current_tab) + retval = retval .. gamemgr.gettab(tabbuilder.current_tab) + retval = retval .. modstore.gettab(tabbuilder.current_tab) + + return retval +end + +-------------------------------------------------------------------------------- +function tabbuilder.handle_create_world_buttons(fields) + + if fields["world_create_confirm"] then + + local worldname = fields["te_world_name"] + local gameindex = engine.get_textlist_index("games") + + if gameindex > 0 and + worldname ~= "" then + engine.setting_set("mg_name",fields["dd_mapgen"]) + local message = engine.create_world(worldname,gameindex) + + menu.last_game = gameindex + engine.setting_set("main_menu_last_game_idx",gameindex) + + if message ~= nil then + gamedata.errormessage = message + else + menu.worldlist = engine.get_worlds() + + local worldlist = menu.worldlist + + if tabbuilder.current_tab == "singleplayer" then + worldlist = menu.filtered_game_list_raw() + end + + local index = 0 + + for i=1,#worldlist,1 do + if worldlist[i].name == worldname then + index = i + print("found new world index: " .. index) + break + end + end + + if tabbuilder.current_tab == "singleplayer" then + engine.setting_set("main_menu_singleplayer_world_idx",index) + else + menu.last_world = index + end + end + else + gamedata.errormessage = "No worldname given or no game selected" + end + end + + if fields["games"] then + tabbuilder.skipformupdate = true + return + end + + tabbuilder.is_dialog = false + tabbuilder.show_buttons = true + tabbuilder.current_tab = engine.setting_get("main_menu_tab") +end + +-------------------------------------------------------------------------------- +function tabbuilder.handle_delete_world_buttons(fields) + + if fields["world_delete_confirm"] then + if menu.last_world > 0 and + menu.last_world < #menu.worldlist then + engine.delete_world(menu.last_world) + menu.worldlist = engine.get_worlds() + menu.last_world = 1 + end + end + + tabbuilder.is_dialog = false + tabbuilder.show_buttons = true + tabbuilder.current_tab = engine.setting_get("main_menu_tab") +end + +-------------------------------------------------------------------------------- +function tabbuilder.handle_multiplayer_buttons(fields) + if fields["favourites"] ~= nil then + local event = explode_textlist_event(fields["favourites"]) + if event.typ == "DCL" then + gamedata.address = menu.favorites[event.index].name + if gamedata.address == nil then + gamedata.address = menu.favorites[event.index].address + end + gamedata.port = menu.favorites[event.index].port + gamedata.playername = fields["te_name"] + gamedata.password = fields["te_pwd"] + gamedata.selected_world = 0 + + if gamedata.address ~= nil and + gamedata.port ~= nil then + + engine.start() + end + end + + if event.typ == "CHG" then + local address = menu.favorites[event.index].name + if address == nil then + address = menu.favorites[event.index].address + end + local port = menu.favorites[event.index].port + + if address ~= nil and + port ~= nil then + engine.setting_set("address",address) + engine.setting_set("port",port) + end + end + return + end + + if fields["cb_public_serverlist"] ~= nil then + engine.setting_setbool("public_serverlist", + tabbuilder.tobool(fields["cb_public_serverlist"])) + + if engine.setting_getbool("public_serverlist") then + menu.favorites = engine.get_favorites("online") + else + menu.favorites = engine.get_favorites("local") + end + end + + if fields["btn_delete_favorite"] ~= nil then + local current_favourite = engine.get_textlist_index("favourites") + engine.delete_favorite(current_favourite) + menu.favorites = engine.get_favorites() + + engine.setting_set("address","") + engine.setting_get("port","") + + return + end + + if fields["btn_mp_connect"] ~= nil then + gamedata.playername = fields["te_name"] + gamedata.password = fields["te_pwd"] + gamedata.address = fields["te_address"] + gamedata.port = fields["te_port"] + gamedata.selected_world = 0 + + engine.start() + return + end +end + +-------------------------------------------------------------------------------- +function tabbuilder.handle_server_buttons(fields) + + local world_doubleclick = false + + if fields["worlds"] ~= nil then + local event = explode_textlist_event(fields["worlds"]) + + if event.typ == "DBL" then + world_doubleclick = true + end + end + + if fields["cb_creative_mode"] then + engine.setting_setbool("creative_mode",tabbuilder.tobool(fields["cb_creative_mode"])) + end + + if fields["cb_enable_damage"] then + engine.setting_setbool("enable_damage",tabbuilder.tobool(fields["cb_enable_damage"])) + end + + if fields["start_server"] ~= nil or + world_doubleclick then + local selected = engine.get_textlist_index("srv_worlds") + if selected > 0 then + gamedata.playername = fields["te_playername"] + gamedata.password = fields["te_pwd"] + gamedata.address = "" + gamedata.port = fields["te_serverport"] + gamedata.selected_world = selected + + engine.setting_set("main_menu_tab",tabbuilder.current_tab) + engine.setting_set("main_menu_last_world_idx",gamedata.selected_world) + + menu.update_last_game(gamedata.selected_world) + engine.start() + end + end + + if fields["world_create"] ~= nil then + tabbuilder.current_tab = "dialog_create_world" + tabbuilder.is_dialog = true + tabbuilder.show_buttons = false + end + + if fields["world_delete"] ~= nil then + local selected = engine.get_textlist_index("srv_worlds") + if selected > 0 then + menu.last_world = engine.get_textlist_index("worlds") + if menu.lastworld() ~= nil and + menu.lastworld().name ~= nil and + menu.lastworld().name ~= "" then + tabbuilder.current_tab = "dialog_delete_world" + tabbuilder.is_dialog = true + tabbuilder.show_buttons = false + else + menu.last_world = 0 + end + end + end + + if fields["world_configure"] ~= nil then + selected = engine.get_textlist_index("srv_worlds") + if selected > 0 then + modmgr.world_config_selected_world = selected + if modmgr.init_worldconfig() then + tabbuilder.current_tab = "dialog_configure_world" + tabbuilder.is_dialog = true + tabbuilder.show_buttons = false + end + end + end +end + +-------------------------------------------------------------------------------- +function tabbuilder.tobool(text) + if text == "true" then + return true + else + return false + end +end + +-------------------------------------------------------------------------------- +function tabbuilder.handle_settings_buttons(fields) + if fields["cb_fancy_trees"] then + engine.setting_setbool("new_style_leaves",tabbuilder.tobool(fields["cb_fancy_trees"])) + end + + if fields["cb_smooth_lighting"] then + engine.setting_setbool("smooth_lighting",tabbuilder.tobool(fields["cb_smooth_lighting"])) + end + if fields["cb_3d_clouds"] then + engine.setting_setbool("enable_3d_clouds",tabbuilder.tobool(fields["cb_3d_clouds"])) + end + if fields["cb_opaque_water"] then + engine.setting_setbool("opaque_water",tabbuilder.tobool(fields["cb_opaque_water"])) + end + + if fields["cb_mipmapping"] then + engine.setting_setbool("mip_map",tabbuilder.tobool(fields["cb_mipmapping"])) + end + if fields["cb_anisotrophic"] then + engine.setting_setbool("anisotropic_filter",tabbuilder.tobool(fields["cb_anisotrophic"])) + end + if fields["cb_bilinear"] then + engine.setting_setbool("bilinear_filter",tabbuilder.tobool(fields["cb_bilinear"])) + end + if fields["cb_trilinear"] then + engine.setting_setbool("trilinear_filter",tabbuilder.tobool(fields["cb_trilinear"])) + end + + if fields["cb_shaders"] then + engine.setting_setbool("enable_shaders",tabbuilder.tobool(fields["cb_shaders"])) + end + if fields["cb_pre_ivis"] then + engine.setting_setbool("preload_item_visuals",tabbuilder.tobool(fields["cb_pre_ivis"])) + end + if fields["cb_particles"] then + engine.setting_setbool("enable_particles",tabbuilder.tobool(fields["cb_particles"])) + end + if fields["cb_finite_liquid"] then + engine.setting_setbool("liquid_finite",tabbuilder.tobool(fields["cb_finite_liquid"])) + end + + if fields["btn_change_keys"] ~= nil then + engine.show_keys_menu() + end +end + +-------------------------------------------------------------------------------- +function tabbuilder.handle_singleplayer_buttons(fields) + + local world_doubleclick = false + + if fields["sp_worlds"] ~= nil then + local event = explode_textlist_event(fields["sp_worlds"]) + + if event.typ == "DCL" then + world_doubleclick = true + end + end + + if fields["cb_creative_mode"] then + engine.setting_setbool("creative_mode",tabbuilder.tobool(fields["cb_creative_mode"])) + end + + if fields["cb_enable_damage"] then + engine.setting_setbool("enable_damage",tabbuilder.tobool(fields["cb_enable_damage"])) + end + + if fields["play"] ~= nil or + world_doubleclick then + local selected = engine.get_textlist_index("sp_worlds") + if selected > 0 then + gamedata.selected_world = menu.filtered_index_to_plain(selected) + gamedata.singleplayer = true + + engine.setting_set("main_menu_tab",tabbuilder.current_tab) + engine.setting_set("main_menu_singleplayer_world_idx",selected) + + menu.update_last_game(gamedata.selected_world) + + engine.start() + end + end + + if fields["world_create"] ~= nil then + tabbuilder.current_tab = "dialog_create_world" + tabbuilder.is_dialog = true + tabbuilder.show_buttons = false + end + + if fields["world_delete"] ~= nil then + local selected = engine.get_textlist_index("sp_worlds") + if selected > 0 then + menu.last_world = menu.filtered_index_to_plain(selected) + if menu.lastworld() ~= nil and + menu.lastworld().name ~= nil and + menu.lastworld().name ~= "" then + tabbuilder.current_tab = "dialog_delete_world" + tabbuilder.is_dialog = true + tabbuilder.show_buttons = false + else + menu.last_world = 0 + end + end + end + + if fields["world_configure"] ~= nil then + selected = engine.get_textlist_index("sp_worlds") + if selected > 0 then + modmgr.world_config_selected_world = menu.filtered_index_to_plain(selected) + if modmgr.init_worldconfig() then + tabbuilder.current_tab = "dialog_configure_world" + tabbuilder.is_dialog = true + tabbuilder.show_buttons = false + end + end + end +end + +-------------------------------------------------------------------------------- +function tabbuilder.tab_header() + + if tabbuilder.last_tab_index == nil then + tabbuilder.last_tab_index = 1 + end + + local toadd = "" + + for i=1,#tabbuilder.current_buttons,1 do + + if toadd ~= "" then + toadd = toadd .. "," + end + + toadd = toadd .. tabbuilder.current_buttons[i].caption + end + return "tabheader[-0.3,-0.99;main_tab;" .. toadd ..";" .. tabbuilder.last_tab_index .. ";true;false]" +end + +-------------------------------------------------------------------------------- +function tabbuilder.handle_tab_buttons(fields) + + if fields["main_tab"] then + local index = tonumber(fields["main_tab"]) + tabbuilder.last_tab_index = index + tabbuilder.current_tab = tabbuilder.current_buttons[index].name + + engine.setting_set("main_menu_tab",tabbuilder.current_tab) + end + + --handle tab changes + if tabbuilder.current_tab ~= tabbuilder.old_tab then + if tabbuilder.current_tab ~= "singleplayer" then + menu.reset_gametype() + end + end + + if tabbuilder.current_tab == "singleplayer" then + menu.update_gametype() + end + + tabbuilder.old_tab = tabbuilder.current_tab +end + +-------------------------------------------------------------------------------- +function tabbuilder.init() + tabbuilder.current_tab = engine.setting_get("main_menu_tab") + + if tabbuilder.current_tab == nil or + tabbuilder.current_tab == "" then + tabbuilder.current_tab = "singleplayer" + engine.setting_set("main_menu_tab",tabbuilder.current_tab) + end + + + --initialize tab buttons + tabbuilder.last_tab = nil + tabbuilder.show_buttons = true + + tabbuilder.current_buttons = {} + table.insert(tabbuilder.current_buttons,{name="singleplayer", caption="Singleplayer"}) + table.insert(tabbuilder.current_buttons,{name="multiplayer", caption="Client"}) + table.insert(tabbuilder.current_buttons,{name="server", caption="Server"}) + table.insert(tabbuilder.current_buttons,{name="settings", caption="Settings"}) + + if engine.setting_getbool("main_menu_game_mgr") then + table.insert(tabbuilder.current_buttons,{name="game_mgr", caption="Games"}) + end + + if engine.setting_getbool("main_menu_mod_mgr") then + table.insert(tabbuilder.current_buttons,{name="mod_mgr", caption="Mods"}) + end + table.insert(tabbuilder.current_buttons,{name="credits", caption="Credits"}) + + + for i=1,#tabbuilder.current_buttons,1 do + if tabbuilder.current_buttons[i].name == tabbuilder.current_tab then + tabbuilder.last_tab_index = i + end + end + + menu.update_gametype() +end + +-------------------------------------------------------------------------------- +function tabbuilder.tab_multiplayer() + local retval = + "vertlabel[0,-0.25;CLIENT]" .. + "label[1,-0.25;Favorites:]".. + "label[1,4.25;Address/Port]".. + "label[9,0;Name/Password]" .. + "field[1.25,5.25;5.5,0.5;te_address;;" ..engine.setting_get("address") .."]" .. + "field[6.75,5.25;2.25,0.5;te_port;;" ..engine.setting_get("port") .."]" .. + "button[6.45,3.95;2.25,0.5;btn_delete_favorite;Delete]" .. + "button[9,4.95;2.5,0.5;btn_mp_connect;Connect]" .. + "field[9.25,1;2.5,0.5;te_name;;" ..engine.setting_get("name") .."]" .. + "pwdfield[9.25,1.75;2.5,0.5;te_pwd;]" .. + "checkbox[1,3.6;cb_public_serverlist;Public Serverlist;" .. + dump(engine.setting_getbool("public_serverlist")) .. "]" .. + "textlist[1,0.35;7.5,3.35;favourites;" + + if #menu.favorites > 0 then + retval = retval .. render_favourite(menu.favorites[1]) + + for i=2,#menu.favorites,1 do + retval = retval .. "," .. render_favourite(menu.favorites[i]) + end + end + + retval = retval .. ";1]" + + return retval +end + +-------------------------------------------------------------------------------- +function tabbuilder.tab_server() + local retval = + "button[4,4.15;2.6,0.5;world_delete;Delete]" .. + "button[6.5,4.15;2.8,0.5;world_create;New]" .. + "button[9.2,4.15;2.55,0.5;world_configure;Configure]" .. + "button[8.5,4.9;3.25,0.5;start_server;Start Game]" .. + "label[4,-0.25;Select World:]".. + "vertlabel[0,-0.25;START SERVER]" .. + "checkbox[0.5,0.25;cb_creative_mode;Creative Mode;" .. + dump(engine.setting_getbool("creative_mode")) .. "]".. + "checkbox[0.5,0.7;cb_enable_damage;Enable Damage;" .. + dump(engine.setting_getbool("enable_damage")) .. "]".. + "field[0.8,2.2;3,0.5;te_playername;Name;" .. + engine.setting_get("name") .. "]" .. + "pwdfield[0.8,3.2;3,0.5;te_passwd;Password]" .. + "field[0.8,5.2;3,0.5;te_serverport;Server Port;30000]" .. + "textlist[4,0.25;7.5,3.7;srv_worlds;" + + if #menu.worldlist > 0 then + retval = retval .. menu.worldlist[1].name .. + " [[" .. menu.worldlist[1].gameid .. "]]" + + for i=2,#menu.worldlist,1 do + retval = retval .. "," .. menu.worldlist[i].name .. + " [[" .. menu.worldlist[i].gameid .. "]]" + end + end + + retval = retval .. ";" .. menu.last_world .. "]" + + return retval +end + +-------------------------------------------------------------------------------- +function tabbuilder.tab_settings() + return "vertlabel[0,0;SETTINGS]" .. + "checkbox[1,0.75;cb_fancy_trees;Fancy trees;" .. dump(engine.setting_getbool("new_style_leaves")) .. "]".. + "checkbox[1,1.25;cb_smooth_lighting;Smooth Lighting;".. dump(engine.setting_getbool("smooth_lighting")) .. "]".. + "checkbox[1,1.75;cb_3d_clouds;3D Clouds;" .. dump(engine.setting_getbool("enable_3d_clouds")) .. "]".. + "checkbox[1,2.25;cb_opaque_water;Opaque Water;" .. dump(engine.setting_getbool("opaque_water")) .. "]".. + + "checkbox[4,0.75;cb_mipmapping;Mip-Mapping;" .. dump(engine.setting_getbool("mip_map")) .. "]".. + "checkbox[4,1.25;cb_anisotrophic;Anisotropic Filtering;".. dump(engine.setting_getbool("anisotropic_filter")) .. "]".. + "checkbox[4,1.75;cb_bilinear;Bi-Linear Filtering;" .. dump(engine.setting_getbool("bilinear_filter")) .. "]".. + "checkbox[4,2.25;cb_trilinear;Tri-Linear Filtering;" .. dump(engine.setting_getbool("trilinear_filter")) .. "]".. + + "checkbox[7.5,0.75;cb_shaders;Shaders;" .. dump(engine.setting_getbool("enable_shaders")) .. "]".. + "checkbox[7.5,1.25;cb_pre_ivis;Preload item visuals;".. dump(engine.setting_getbool("preload_item_visuals")) .. "]".. + "checkbox[7.5,1.75;cb_particles;Enable Particles;" .. dump(engine.setting_getbool("enable_particles")) .. "]".. + "checkbox[7.5,2.25;cb_finite_liquid;Finite Liquid;" .. dump(engine.setting_getbool("liquid_finite")) .. "]".. + + "button[1,3.75;2.25,0.5;btn_change_keys;Change keys]" +end + +-------------------------------------------------------------------------------- +function tabbuilder.tab_singleplayer() + local index = engine.setting_get("main_menu_singleplayer_world_idx") + + if index == nil then + index = 0 + end + + return "button[4,4.15;2.6,0.5;world_delete;Delete]" .. + "button[6.5,4.15;2.8,0.5;world_create;New]" .. + "button[9.2,4.15;2.55,0.5;world_configure;Configure]" .. + "button[8.5,4.95;3.25,0.5;play;Play]" .. + "label[4,-0.25;Select World:]".. + "vertlabel[0,-0.25;SINGLE PLAYER]" .. + "checkbox[0.5,0.25;cb_creative_mode;Creative Mode;" .. + dump(engine.setting_getbool("creative_mode")) .. "]".. + "checkbox[0.5,0.7;cb_enable_damage;Enable Damage;" .. + dump(engine.setting_getbool("enable_damage")) .. "]".. + "textlist[4,0.25;7.5,3.7;sp_worlds;" .. + menu.filtered_game_list() .. + ";" .. index .. "]" .. + menubar.formspec +end + +-------------------------------------------------------------------------------- +function tabbuilder.tab_credits() + return "vertlabel[0,-0.5;CREDITS]" .. + "label[0.5,3;Minetest " .. engine.get_version() .. "]" .. + "label[0.5,3.3;http://minetest.net]" .. + "image[0.5,1;" .. menu.basetexturedir .. "logo.png]" .. + "textlist[3.5,-0.25;8.5,5.8;list_credits;" .. + "#YLWCore Developers," .. + "Perttu Ahola (celeron55) ,".. + "Ryan Kwolek (kwolekr) ,".. + "PilzAdam ," .. + "IIya Zhuravlev (thexyz) ,".. + "Lisa Milne (darkrose) ,".. + "Maciej Kasatkin (RealBadAngel) ,".. + "proller ,".. + "sfan5 ,".. + "kahrl ,".. + ",".. + "#YLWActive Contributors," .. + "sapier,".. + "Vanessa Ezekowitz (VanessaE) ,".. + "Jurgen Doser (doserj) ,".. + "Jeija ,".. + "MirceaKitsune ,".. + "ShadowNinja".. + "dannydark ".. + "0gb.us <0gb.us@0gb.us>,".. + "," .. + "#YLWPrevious Contributors," .. + "Guiseppe Bilotta (Oblomov) ,".. + "Jonathan Neuschafer ,".. + "Nils Dagsson Moskopp (erlehmann) ,".. + "Constantin Wenger (SpeedProg) ,".. + "matttpt ,".. + "JacobF ,".. + ";0;true]" +end + +-------------------------------------------------------------------------------- +function tabbuilder.checkretval(retval) + + if retval ~= nil then + if retval.current_tab ~= nil then + tabbuilder.current_tab = retval.current_tab + end + + if retval.is_dialog ~= nil then + tabbuilder.is_dialog = retval.is_dialog + end + + if retval.show_buttons ~= nil then + tabbuilder.show_buttons = retval.show_buttons + end + + if retval.skipformupdate ~= nil then + tabbuilder.skipformupdate = retval.skipformupdate + end + end +end + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-- initialize callbacks +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +engine.button_handler = function(fields) + --print("Buttonhandler: tab: " .. tabbuilder.current_tab .. " fields: " .. dump(fields)) + + if fields["btn_error_confirm"] then + gamedata.errormessage = nil + end + + local retval = modmgr.handle_buttons(tabbuilder.current_tab,fields) + tabbuilder.checkretval(retval) + + retval = gamemgr.handle_buttons(tabbuilder.current_tab,fields) + tabbuilder.checkretval(retval) + + retval = modstore.handle_buttons(tabbuilder.current_tab,fields) + tabbuilder.checkretval(retval) + + if tabbuilder.current_tab == "dialog_create_world" then + tabbuilder.handle_create_world_buttons(fields) + end + + if tabbuilder.current_tab == "dialog_delete_world" then + tabbuilder.handle_delete_world_buttons(fields) + end + + if tabbuilder.current_tab == "singleplayer" then + tabbuilder.handle_singleplayer_buttons(fields) + end + + if tabbuilder.current_tab == "multiplayer" then + tabbuilder.handle_multiplayer_buttons(fields) + end + + if tabbuilder.current_tab == "settings" then + tabbuilder.handle_settings_buttons(fields) + end + + if tabbuilder.current_tab == "server" then + tabbuilder.handle_server_buttons(fields) + end + + --tab buttons + tabbuilder.handle_tab_buttons(fields) + + --menubar buttons + menubar.handle_buttons(fields) + + if not tabbuilder.skipformupdate then + --update menu + update_menu() + else + tabbuilder.skipformupdate = false + end +end + +-------------------------------------------------------------------------------- +engine.event_handler = function(event) + if event == "MenuQuit" then + if tabbuilder.is_dialog then + tabbuilder.is_dialog = false + tabbuilder.show_buttons = true + tabbuilder.current_tab = engine.setting_get("main_menu_tab") + update_menu() + else + engine.close() + end + end +end + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-- menu startup +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +init_globals() +menu.init() +tabbuilder.init() +menubar.refresh() +modstore.init() +update_menu() diff --git a/builtin/mainmenu_helper.lua b/builtin/mainmenu_helper.lua new file mode 100644 index 000000000..f5a470b72 --- /dev/null +++ b/builtin/mainmenu_helper.lua @@ -0,0 +1,105 @@ +-------------------------------------------------------------------------------- +function dump(o, dumped) + dumped = dumped or {} + if type(o) == "number" then + return tostring(o) + elseif type(o) == "string" then + return string.format("%q", o) + elseif type(o) == "table" then + if dumped[o] then + return "" + end + dumped[o] = true + local t = {} + for k,v in pairs(o) do + t[#t+1] = "" .. k .. " = " .. dump(v, dumped) + end + return "{" .. table.concat(t, ", ") .. "}" + elseif type(o) == "boolean" then + return tostring(o) + elseif type(o) == "function" then + return "" + elseif type(o) == "userdata" then + return "" + elseif type(o) == "nil" then + return "nil" + else + error("cannot dump a " .. type(o)) + return nil + end +end + +-------------------------------------------------------------------------------- +function string:split(sep) + local sep, fields = sep or ",", {} + local pattern = string.format("([^%s]+)", sep) + self:gsub(pattern, function(c) fields[#fields+1] = c end) + return fields +end + +-------------------------------------------------------------------------------- +function string:trim() + return (self:gsub("^%s*(.-)%s*$", "%1")) +end + +-------------------------------------------------------------------------------- +engine.get_game = function(index) + local games = game.get_games() + + if index > 0 and index <= #games then + return games[index] + end + + return nil +end + +-------------------------------------------------------------------------------- +function fs_escape_string(text) + if text ~= nil then + while (text:find("\r\n") ~= nil) do + local newtext = text:sub(1,text:find("\r\n")-1) + newtext = newtext .. " " .. text:sub(text:find("\r\n")+3) + + text = newtext + end + + while (text:find("\n") ~= nil) do + local newtext = text:sub(1,text:find("\n")-1) + newtext = newtext .. " " .. text:sub(text:find("\n")+1) + + text = newtext + end + + while (text:find("\r") ~= nil) do + local newtext = text:sub(1,text:find("\r")-1) + newtext = newtext .. " " .. text:sub(text:find("\r")+1) + + text = newtext + end + + text = text:gsub("%[","%[%[") + text = text:gsub("]","]]") + text = text:gsub(";"," ") + end + return text +end + +-------------------------------------------------------------------------------- +function explode_textlist_event(text) + + local retval = {} + retval.typ = "INV" + + local parts = text:split(":") + + if #parts == 2 then + retval.typ = parts[1]:trim() + retval.index= tonumber(parts[2]:trim()) + + if type(retval.index) ~= "number" then + retval.typ = "INV" + end + end + + return retval +end diff --git a/builtin/modmgr.lua b/builtin/modmgr.lua new file mode 100644 index 000000000..1cb4b3922 --- /dev/null +++ b/builtin/modmgr.lua @@ -0,0 +1,881 @@ +--Minetest +--Copyright (C) 2013 sapier +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +-------------------------------------------------------------------------------- +function get_mods(path,retval,basefolder) + + local mods = engine.get_dirlist(path,true) + + for i=1,#mods,1 do + local filename = path .. DIR_DELIM .. mods[i] .. DIR_DELIM .. "modpack.txt" + local modpackfile,error = io.open(filename,"r") + + local name = mods[i] + if basefolder ~= nil and + basefolder ~= "" then + name = basefolder .. DIR_DELIM .. mods[i] + end + + if modpackfile ~= nil then + modpackfile:close() + table.insert(retval,name .. " ") + get_mods(path .. DIR_DELIM .. name,retval,name) + else + + table.insert(retval,name) + end + end +end + +--modmanager implementation +modmgr = {} + +-------------------------------------------------------------------------------- +function modmgr.extract(modfile) + if modfile.type == "zip" then + local tempfolder = os.tempfolder() + + if tempfolder ~= nil and + tempfodler ~= "" then + engine.create_dir(tempfolder) + engine.extract_zip(modfile.name,tempfolder) + return tempfolder + end + end +end + +------------------------------------------------------------------------------- +function modmgr.getbasefolder(temppath) + + if temppath == nil then + return { + type = "invalid", + path = "" + } + end + + local testfile = io.open(temppath .. DIR_DELIM .. "init.lua","r") + if testfile ~= nil then + testfile:close() + return { + type="mod", + path=temppath + } + end + + testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r") + if testfile ~= nil then + testfile:close() + return { + type="modpack", + path=temppath + } + end + + local subdirs = engine.get_dirlist(temppath,true) + + --only single mod or modpack allowed + if #subdirs ~= 1 then + return { + type = "invalid", + path = "" + } + end + + testfile = + io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r") + if testfile ~= nil then + testfile:close() + return { + type="mod", + path= temppath .. DIR_DELIM .. subdirs[1] + } + end + + testfile = + io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r") + if testfile ~= nil then + testfile:close() + return { + type="modpack", + path=temppath .. DIR_DELIM .. subdirs[1] + } + end + + return { + type = "invalid", + path = "" + } +end + +-------------------------------------------------------------------------------- +function modmgr.isValidModname(modpath) + if modpath:find("-") ~= nil then + return false + end + + return true +end + +-------------------------------------------------------------------------------- +function modmgr.parse_register_line(line) + local pos1 = line:find("\"") + local pos2 = nil + if pos1 ~= nil then + pos2 = line:find("\"",pos1+1) + end + + if pos1 ~= nil and pos2 ~= nil then + local item = line:sub(pos1+1,pos2-1) + + if item ~= nil and + item ~= "" then + local pos3 = item:find(":") + + if pos3 ~= nil then + return item:sub(1,pos3-1) + end + end + end + return nil +end + +-------------------------------------------------------------------------------- +function modmgr.parse_dofile_line(modpath,line) + local pos1 = line:find("\"") + local pos2 = nil + if pos1 ~= nil then + pos2 = line:find("\"",pos1+1) + end + + if pos1 ~= nil and pos2 ~= nil then + local filename = line:sub(pos1+1,pos2-1) + + if filename ~= nil and + filename ~= "" and + filename:find(".lua") then + return modmgr.identify_modname(modpath,filename) + end + end + return nil +end + +-------------------------------------------------------------------------------- +function modmgr.update_global_mods() + local modpath = engine.get_modpath() + modmgr.global_mods = {} + if modpath ~= nil and + modpath ~= "" then + get_mods(modpath,modmgr.global_mods) + end +end + +-------------------------------------------------------------------------------- +function modmgr.get_mods_list() + local toadd = "" + + modmgr.update_global_mods() + + if modmgr.global_mods ~= nil then + for i=1,#modmgr.global_mods,1 do + if toadd ~= "" then + toadd = toadd.."," + end + toadd = toadd .. modmgr.global_mods[i] + end + end + + return toadd +end + +-------------------------------------------------------------------------------- +function modmgr.mod_exists(basename) + modmgr.update_global_mods() + + if modmgr.global_mods ~= nil then + for i=1,#modmgr.global_mods,1 do + if modmgr.global_mods[i] == basename then + return true + end + end + end + + return false +end + +-------------------------------------------------------------------------------- +function modmgr.identify_modname(modpath,filename) + local testfile = io.open(modpath .. DIR_DELIM .. filename,"r") + if testfile ~= nil then + local line = testfile:read() + + while line~= nil do + local modname = nil + + if line:find("minetest.register_tool") then + modname = modmgr.parse_register_line(line) + end + + if line:find("minetest.register_craftitem") then + modname = modmgr.parse_register_line(line) + end + + + if line:find("minetest.register_node") then + modname = modmgr.parse_register_line(line) + end + + if line:find("dofile") then + modname = modmgr.parse_dofile_line(modpath,line) + end + + if modname ~= nil then + testfile:close() + return modname + end + + line = testfile:read() + end + testfile:close() + end + + return nil +end + +-------------------------------------------------------------------------------- +function modmgr.tab() + if modmgr.selected_mod == nil then + modmgr.selected_mod = 1 + end + + local retval = + "vertlabel[0,-0.25;MODS]" .. + "label[0.8,-0.25;Installed Mods:]" .. + "textlist[0.75,0.25;4.5,4.3;modlist;" .. + modmgr.get_mods_list() .. + ";" .. modmgr.selected_mod .. "]" + + retval = retval .. + "button[1,4.85;2,0.5;btn_mod_mgr_install_local;Install]" .. + "button[3,4.85;2,0.5;btn_mod_mgr_download;Download]" + + if #modmgr.global_mods >= modmgr.selected_mod and + modmgr.global_mods[modmgr.selected_mod]:find("") then + retval = retval .. "button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;Rename]" + end + + if #modmgr.global_mods >= modmgr.selected_mod then + local modpath = engine.get_modpath() + --show dependencys + if modmgr.global_mods[modmgr.selected_mod]:find("") == nil then + retval = retval .. + "label[6,1.9;Depends:]" .. + "textlist[6,2.4;5.7,2;deplist;" + + toadd = modmgr.get_dependencys(modpath .. DIR_DELIM .. + modmgr.global_mods[modmgr.selected_mod]) + + retval = retval .. toadd .. ";0;true,false]" + + --TODO read modinfo + end + --show delete button + retval = retval .. "button[8,4.85;2,0.5;btn_mod_mgr_delete_mod;Delete]" + end + return retval +end + +-------------------------------------------------------------------------------- +function modmgr.dialog_rename_modpack() + + local modname = modmgr.global_mods[modmgr.selected_mod] + modname = modname:sub(0,modname:find("<") -2) + + local retval = + "label[1.75,1;Rename Modpack:]".. + "field[4.5,1.4;6,0.5;te_modpack_name;;" .. + modname .. + "]" .. + "button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;Accept]" .. + "button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;Cancel]" + + return retval +end + +-------------------------------------------------------------------------------- +function modmgr.precheck() + if modmgr.global_mods == nil then + modmgr.update_global_mods() + end + + if modmgr.world_config_selected_world == nil then + modmgr.world_config_selected_world = 1 + end + + if modmgr.world_config_selected_mod == nil then + modmgr.world_config_selected_mod = 1 + end + + if modmgr.hide_gamemods == nil then + modmgr.hide_gamemods = true + end +end + +-------------------------------------------------------------------------------- +function modmgr.get_worldmod_idx() + if not modmgr.hide_gamemods then + return modmgr.world_config_selected_mod - #modmgr.worldconfig.game_mods + else + return modmgr.world_config_selected_mod + end +end + +-------------------------------------------------------------------------------- +function modmgr.is_gamemod() + if not modmgr.hide_gamemods then + if modmgr.world_config_selected_mod <= #modmgr.worldconfig.game_mods then + return true + else + return false + end + else + return false + end +end + +-------------------------------------------------------------------------------- +function modmgr.render_worldmodlist() + local retval = "" + + for i=1,#modmgr.global_mods,1 do + local parts = modmgr.global_mods[i]:split(DIR_DELIM) + local shortname = parts[#parts] + if modmgr.worldconfig.global_mods[shortname] then + retval = retval .. "#GRN" .. modmgr.global_mods[i] .. "," + else + retval = retval .. modmgr.global_mods[i] .. "," + end + end + + return retval +end + +-------------------------------------------------------------------------------- +function modmgr.render_gamemodlist() + local retval = "" + for i=1,#modmgr.worldconfig.game_mods,1 do + retval = retval .. + "#BLU" .. modmgr.worldconfig.game_mods[i] .. "," + end + + return retval +end + +-------------------------------------------------------------------------------- +function modmgr.dialog_configure_world() + modmgr.precheck() + + local modpack_selected = false + local gamemod_selected = modmgr.is_gamemod() + local modname = "" + local modfolder = "" + local shortname = "" + + if not gamemod_selected then + local worldmodidx = modmgr.get_worldmod_idx() + modname = modmgr.global_mods[worldmodidx] + + if modname:find("") ~= nil then + modname = modname:sub(0,modname:find("<") -2) + modpack_selected = true + end + + local parts = modmgr.global_mods[worldmodidx]:split(DIR_DELIM) + shortname = parts[#parts] + + modfolder = engine.get_modpath() .. DIR_DELIM .. modname + end + + local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] + + local retval = + "size[11,6.5]" .. + "label[1.5,-0.25;World: " .. worldspec.name .. "]" + + if modmgr.hide_gamemods then + retval = retval .. "checkbox[5.5,6.15;cb_hide_gamemods;Hide Game;true]" + else + retval = retval .. "checkbox[5.5,6.15;cb_hide_gamemods;Hide Game;false]" + end + retval = retval .. + "button[9.25,6.35;2,0.5;btn_config_world_save;Save]" .. + "button[7.4,6.35;2,0.5;btn_config_world_cancel;Cancel]" .. + "textlist[5.5,-0.25;5.5,6.5;world_config_modlist;" + + + if not modmgr.hide_gamemods then + retval = retval .. modmgr.render_gamemodlist() + end + + retval = retval .. modmgr.render_worldmodlist() + + retval = retval .. ";" .. modmgr.world_config_selected_mod .."]" + + if not gamemod_selected then + retval = retval .. + "label[0,0.45;Mod:]" .. + "label[0.75,0.45;" .. modname .. "]" .. + "label[0,1.5;depends on:]" .. + "textlist[0,2;5,2;world_config_depends;" .. + modmgr.get_dependencys(modfolder) .. ";0]" .. + "label[0,4;depends on:]" .. + "textlist[0,4.5;5,2;world_config_is_required;;0]" + + if modpack_selected then + retval = retval .. + "button[-0.05,1.05;2,0.5;btn_cfgw_enable_all;Enable All]" .. + "button[3.25,1.05;2,0.5;btn_cfgw_disable_all;Disable All]" + else + retval = retval .. + "checkbox[0,0.8;cb_mod_enabled;enabled;" + + if modmgr.worldconfig.global_mods[shortname] then + print("checkbox " .. shortname .. " enabled") + retval = retval .. "true" + else + print("checkbox " .. shortname .. " disabled") + retval = retval .. "false" + end + + retval = retval .. "]" + end + end + + return retval +end + +-------------------------------------------------------------------------------- +function modmgr.handle_buttons(tab,fields) + + local retval = nil + + if tab == "mod_mgr" then + retval = modmgr.handle_modmgr_buttons(fields) + end + + if tab == "dialog_rename_modpack" then + retval = modmgr.handle_rename_modpack_buttons(fields) + end + + if tab == "dialog_delete_mod" then + retval = modmgr.handle_delete_mod_buttons(fields) + end + + if tab == "dialog_configure_world" then + retval = modmgr.handle_configure_world_buttons(fields) + end + + return retval +end + +-------------------------------------------------------------------------------- +function modmgr.get_dependencys(modfolder) + local filename = modfolder .. + DIR_DELIM .. "depends.txt" + + local dependencyfile = io.open(filename,"r") + + local toadd = "" + if dependencyfile then + local dependency = dependencyfile:read("*l") + while dependency do + if toadd ~= "" then + toadd = toadd .. "," + end + toadd = toadd .. dependency + dependency = dependencyfile:read() + end + dependencyfile:close() + else + print(filename .. " not found") + end + + return toadd +end + + +-------------------------------------------------------------------------------- +function modmgr.get_worldconfig(worldpath) + local filename = worldpath .. + DIR_DELIM .. "world.mt" + + local worldfile = io.open(filename,"r") + + local worldconfig = {} + worldconfig.global_mods = {} + worldconfig.game_mods = {} + + if worldfile then + local dependency = worldfile:read("*l") + while dependency do + local parts = dependency:split("=") + + local key = parts[1]:trim() + + if key == "gameid" then + worldconfig.id = parts[2]:trim() + else + local key = parts[1]:trim():sub(10) + if parts[2]:trim() == "true" then + print("found enabled mod: >" .. key .. "<") + worldconfig.global_mods[key] = true + else + print("found disabled mod: >" .. key .. "<") + worldconfig.global_mods[key] = false + end + end + dependency = worldfile:read("*l") + end + worldfile:close() + else + print(filename .. " not found") + end + + --read gamemods + local gamemodpath = engine.get_gamepath() .. DIR_DELIM .. worldconfig.id .. DIR_DELIM .. "mods" + + print("reading game mods from: " .. dump(gamemodpath)) + get_mods(gamemodpath,worldconfig.game_mods) + + return worldconfig +end +-------------------------------------------------------------------------------- +function modmgr.handle_modmgr_buttons(fields) + local retval = { + tab = nil, + is_dialog = nil, + show_buttons = nil, + } + + if fields["modlist"] ~= nil then + local event = explode_textlist_event(fields["modlist"]) + modmgr.selected_mod = event.index + end + + if fields["btn_mod_mgr_install_local"] ~= nil then + engine.show_file_open_dialog("mod_mgt_open_dlg","Select Mod File:") + end + + if fields["btn_mod_mgr_download"] ~= nil then + retval.current_tab = "dialog_modstore_unsorted" + retval.is_dialog = true + retval.show_buttons = false + return retval + end + + if fields["btn_mod_mgr_rename_modpack"] ~= nil then + retval.current_tab = "dialog_rename_modpack" + retval.is_dialog = true + retval.show_buttons = false + return retval + end + + if fields["btn_mod_mgr_delete_mod"] ~= nil then + retval.current_tab = "dialog_delete_mod" + retval.is_dialog = true + retval.show_buttons = false + return retval + end + + if fields["mod_mgt_open_dlg_accepted"] ~= nil and + fields["mod_mgt_open_dlg_accepted"] ~= "" then + modmgr.installmod(fields["mod_mgt_open_dlg_accepted"],nil) + end + + return nil; +end + +-------------------------------------------------------------------------------- +function modmgr.installmod(modfilename,basename) + local modfile = identify_filetype(modfilename) + + local modpath = modmgr.extract(modfile) + + if modpath == nil then + gamedata.errormessage = "Install Mod: file: " .. modfile.name .. + "\nInstall Mod: unsupported filetype \"" .. modfile.type .. "\"" + return + end + + + local basefolder = modmgr.getbasefolder(modpath) + + if basefolder.type == "modpack" then + local clean_path = nil + + if basename ~= nil then + clean_path = "mp_" .. basename + end + + if clean_path == nil then + clean_path = get_last_folder(cleanup_path(basefolder.path)) + end + + if clean_path ~= nil then + local targetpath = engine.get_modpath() .. DIR_DELIM .. clean_path + engine.copy_dir(basefolder.path,targetpath) + else + gamedata.errormessage = "Install Mod: unable to find suitable foldername for modpack " + .. modfilename + end + end + + if basefolder.type == "mod" then + local targetfolder = basename + + if targetfolder == nil then + targetfolder = modmgr.identify_modname(basefolder.path,"init.lua") + end + + --if heuristic failed try to use current foldername + if targetfolder == nil then + targetfolder = get_last_folder(basefolder.path) + end + + if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then + local targetpath = engine.get_modpath() .. DIR_DELIM .. targetfolder + engine.copy_dir(basefolder.path,targetpath) + else + gamedata.errormessage = "Install Mod: unable to find real modname for: " + .. modfilename + end + end + + engine.delete_dir(modpath) +end + +-------------------------------------------------------------------------------- +function modmgr.handle_rename_modpack_buttons(fields) + local oldname = modmgr.global_mods[modmgr.selected_mod] + oldname = oldname:sub(0,oldname:find("<") -2) + + if fields["dlg_rename_modpack_confirm"] ~= nil then + local oldpath = engine.get_modpath() .. DIR_DELIM .. oldname + local targetpath = engine.get_modpath() .. DIR_DELIM .. fields["te_modpack_name"] + engine.copy_dir(oldpath,targetpath,false) + end + + return { + is_dialog = false, + show_buttons = true, + current_tab = engine.setting_get("main_menu_tab") + } +end +-------------------------------------------------------------------------------- +function modmgr.handle_configure_world_buttons(fields) + if fields["world_config_modlist"] ~= nil then + local event = explode_textlist_event(fields["world_config_modlist"]) + modmgr.world_config_selected_mod = event.index + end + + if fields["cb_mod_enabled"] ~= nil then + local index = modmgr.get_worldmod_idx() + local modname = modmgr.global_mods[index] + + local parts = modmgr.global_mods[index]:split(DIR_DELIM) + local shortname = parts[#parts] + + if fields["cb_mod_enabled"] == "true" then + modmgr.worldconfig.global_mods[shortname] = true + else + modmgr.worldconfig.global_mods[shortname] = false + end + end + + if fields["cb_hide_gamemods"] ~= nil then + if fields["cb_hide_gamemods"] == "true" then + modmgr.hide_gamemods = true + else + modmgr.hide_gamemods = false + end + end + + if fields["btn_config_world_save"] then + local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] + + local filename = worldspec.path .. + DIR_DELIM .. "world.mt" + + local worldfile = io.open(filename,"w") + + if worldfile then + worldfile:write("gameid = " .. modmgr.worldconfig.id .. "\n") + for key,value in pairs(modmgr.worldconfig.global_mods) do + if value then + worldfile:write("load_mod_" .. key .. " = true" .. "\n") + else + worldfile:write("load_mod_" .. key .. " = false" .. "\n") + end + end + + worldfile:close() + end + + modmgr.worldconfig = nil + + return { + is_dialog = false, + show_buttons = true, + current_tab = engine.setting_get("main_menu_tab") + } + end + + if fields["btn_config_world_cancel"] then + + modmgr.worldconfig = nil + + return { + is_dialog = false, + show_buttons = true, + current_tab = engine.setting_get("main_menu_tab") + } + end + + if fields["btn_cfgw_enable_all"] then + local worldmodidx = modmgr.get_worldmod_idx() + modname = modmgr.global_mods[worldmodidx] + + modname = modname:sub(0,modname:find("<") -2) + + for i=1,#modmgr.global_mods,1 do + + if modmgr.global_mods[i]:find("") == nil then + local modpackpart = modmgr.global_mods[i]:sub(0,modname:len()) + + if modpackpart == modname then + local parts = modmgr.global_mods[i]:split(DIR_DELIM) + local shortname = parts[#parts] + modmgr.worldconfig.global_mods[shortname] = true + end + end + end + end + + if fields["btn_cfgw_disable_all"] then + local worldmodidx = modmgr.get_worldmod_idx() + modname = modmgr.global_mods[worldmodidx] + + modname = modname:sub(0,modname:find("<") -2) + + for i=1,#modmgr.global_mods,1 do + local modpackpart = modmgr.global_mods[i]:sub(0,modname:len()) + + if modpackpart == modname then + local parts = modmgr.global_mods[i]:split(DIR_DELIM) + local shortname = parts[#parts] + modmgr.worldconfig.global_mods[shortname] = nil + end + end + end + + return nil +end +-------------------------------------------------------------------------------- +function modmgr.handle_delete_mod_buttons(fields) + local modname = modmgr.global_mods[modmgr.selected_mod] + + if modname:find("") ~= nil then + modname = modname:sub(0,modname:find("<") -2) + end + + if fields["dlg_delete_mod_confirm"] ~= nil then + local oldpath = engine.get_modpath() .. DIR_DELIM .. modname + + if oldpath ~= nil and + oldpath ~= "" and + oldpath ~= engine.get_modpath() then + engine.delete_dir(oldpath) + end + end + + return { + is_dialog = false, + show_buttons = true, + current_tab = engine.setting_get("main_menu_tab") + } +end + +-------------------------------------------------------------------------------- +function modmgr.dialog_delete_mod() + + local modname = modmgr.global_mods[modmgr.selected_mod] + + if modname:find("") ~= nil then + modname = modname:sub(0,modname:find("<") -2) + end + + local retval = + "field[1.75,1;10,3;;Are you sure you want to delete ".. modname .. "?;]".. + "button[4,4.2;1,0.5;dlg_delete_mod_confirm;Yes]" .. + "button[6.5,4.2;3,0.5;dlg_delete_mod_cancel;No of course not!]" + + return retval +end + +-------------------------------------------------------------------------------- +function modmgr.init_worldconfig() + + local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] + + if worldspec ~= nil then + --read worldconfig + modmgr.worldconfig = modmgr.get_worldconfig(worldspec.path) + + if modmgr.worldconfig.id == nil or + modmgr.worldconfig.id == "" then + modmgr.worldconfig = nil + return false + end + + return true + end + + return false +end + +-------------------------------------------------------------------------------- +function modmgr.gettab(name) + local retval = "" + + if name == "mod_mgr" then + retval = retval .. modmgr.tab() + end + + if name == "dialog_rename_modpack" then + retval = retval .. modmgr.dialog_rename_modpack() + end + + if name == "dialog_delete_mod" then + retval = retval .. modmgr.dialog_delete_mod() + end + + if name == "dialog_configure_world" then + retval = retval .. modmgr.dialog_configure_world() + end + + return retval +end diff --git a/builtin/modstore.lua b/builtin/modstore.lua new file mode 100644 index 000000000..73afc3899 --- /dev/null +++ b/builtin/modstore.lua @@ -0,0 +1,275 @@ +--Minetest +--Copyright (C) 2013 sapier +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +-------------------------------------------------------------------------------- + +--modstore implementation +modstore = {} + +-------------------------------------------------------------------------------- +function modstore.init() + modstore.tabnames = {} + + table.insert(modstore.tabnames,"dialog_modstore_unsorted") + table.insert(modstore.tabnames,"dialog_modstore_search") + + modstore.modsperpage = 5 + + modstore.basetexturedir = engine.get_gamepath() .. DIR_DELIM .. ".." .. + DIR_DELIM .. "textures" .. DIR_DELIM .. "base" .. + DIR_DELIM .. "pack" .. DIR_DELIM + modstore.update_modlist() + + modstore.current_list = nil + + modstore.details_cache = {} +end +-------------------------------------------------------------------------------- +function modstore.nametoindex(name) + + for i=1,#modstore.tabnames,1 do + if modstore.tabnames[i] == name then + return i + end + end + + return 1 +end + +-------------------------------------------------------------------------------- +function modstore.gettab(tabname) + local retval = "" + + local is_modstore_tab = false + + if tabname == "dialog_modstore_unsorted" then + retval = modstore.getmodlist(modstore.modlist_unsorted) + is_modstore_tab = true + end + + if tabname == "dialog_modstore_search" then + + + is_modstore_tab = true + end + + if is_modstore_tab then + return modstore.tabheader(tabname) .. retval + end + + if tabname == "modstore_mod_installed" then + return "size[6,2]label[0.25,0.25;Mod: " .. modstore.lastmodtitle .. + " installed successfully]" .. + "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;ok]" + end + + return "" +end + +-------------------------------------------------------------------------------- +function modstore.tabheader(tabname) + local retval = "size[12,9.25]" + retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" .. + "Unsorted,Search;" .. + modstore.nametoindex(tabname) .. ";true;false]" + + return retval +end + +-------------------------------------------------------------------------------- +function modstore.handle_buttons(current_tab,fields) + + modstore.lastmodtitle = "" + + if fields["modstore_tab"] then + local index = tonumber(fields["modstore_tab"]) + + if index > 0 and + index <= #modstore.tabnames then + return { + current_tab = modstore.tabnames[index], + is_dialog = true, + show_buttons = false + } + end + + modstore.modlist_page = 0 + end + + if fields["btn_modstore_page_up"] then + if modstore.current_list ~= nil and modstore.current_list.page > 0 then + modstore.current_list.page = modstore.current_list.page - 1 + end + end + + if fields["btn_modstore_page_down"] then + if modstore.current_list ~= nil and + modstore.current_list.page #list.data) then + endmod = #list.data + end + + for i=(list.page * modstore.modsperpage) +1, endmod, 1 do + --getmoddetails + local details = modstore.get_details(list.data[i].id) + + if details ~= nil then + local screenshot_ypos = (i-1 - (list.page * modstore.modsperpage))*1.9 +0.2 + + retval = retval .. "box[0," .. screenshot_ypos .. ";11.4,1.75;WHT]" + + --screenshot + if details.screenshot_url ~= nil and + details.screenshot_url ~= "" then + if list.data[i].texturename == nil then + print("downloading screenshot: " .. details.screenshot_url) + local filename = os.tempfolder() + + if engine.download_file(details.screenshot_url,filename) then + list.data[i].texturename = filename + end + end + end + + if list.data[i].texturename == nil then + list.data[i].texturename = modstore.basetexturedir .. "no_screenshot.png" + end + + retval = retval .. "image[0,".. screenshot_ypos .. ";3,2;" .. + list.data[i].texturename .. "]" + + --title + author + retval = retval .."label[2.75," .. screenshot_ypos .. ";" .. + fs_escape_string(details.title) .. " (" .. details.author .. ")]" + + --description + local descriptiony = screenshot_ypos + 0.5 + retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.6;;" .. + fs_escape_string(details.description) .. ";]" + --rating + local ratingy = screenshot_ypos + 0.6 + retval = retval .."label[10.1," .. ratingy .. ";Rating: " .. details.rating .."]" + + --install button + local buttony = screenshot_ypos + 1.2 + local buttonnumber = (i - (list.page * modstore.modsperpage)) + retval = retval .."button[9.6," .. buttony .. ";2,0.5;btn_install_mod_" .. buttonnumber .. ";" + + if modmgr.mod_exists(details.basename) then + retval = retval .. "re-Install]" + else + retval = retval .. "Install]" + end + end + end + + modstore.current_list = list + + return retval +end + +-------------------------------------------------------------------------------- +function modstore.get_details(modid) + + if modstore.details_cache[modid] ~= nil then + return modstore.details_cache[modid] + end + + local retval = engine.get_modstore_details(tostring(modid)) + modstore.details_cache[modid] = retval + return retval +end \ No newline at end of file diff --git a/doc/lua_api.txt b/doc/lua_api.txt index cd0824eb3..ebba40fbf 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -883,6 +883,15 @@ background[,;,;] ^ Position and size units are inventory slots ^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px +pwdfield[,;,;;